From dbb9c1d4f46d2e88840bb81de40adf07dbc65843 Mon Sep 17 00:00:00 2001 From: Thalix <67201730+thalix1337@users.noreply.github.com> Date: Mon, 29 Mar 2021 01:06:56 +0200 Subject: [PATCH 001/624] Minor ruletypes.h cleanup (#1306) Minor ruletypes.h cleanup --- common/ruletypes.h | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 31fb54fce..3ed1791e1 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -322,7 +322,6 @@ RULE_INT(Spells, BaseCritRatio, 100, "Base percentage bonus to damage on a succe RULE_INT(Spells, WizCritLevel, 12, "Level wizards first get spell crits") RULE_INT(Spells, WizCritChance, 7, "Wizards crit chance, on top of BaseCritChance") RULE_INT(Spells, WizCritRatio, 0, "Wizards crit bonus, on top of BaseCritRatio (should be 0 for Live-like)") -RULE_INT(Spells, ResistPerLevelDiff, 85, "Resist per level difference. 85=8.5") RULE_INT(Spells, TranslocateTimeLimit, 0, "If not zero, time in seconds to accept a Translocate") RULE_INT(Spells, SacrificeMinLevel, 46, "First level the spell Sacrifice will work on") RULE_INT(Spells, SacrificeMaxLevel, 69, "Last level the spell Sacrifice will work on") @@ -487,11 +486,11 @@ RULE_INT(Combat, ArcheryBonusChance, 50, "Chance for 50+ archery bonus damage if RULE_INT(Combat, BerserkerFrenzyStart, 35, "Percentage Health Points below which Warrior and Berserker start frenzy") RULE_INT(Combat, BerserkerFrenzyEnd, 45, "Percentage Health Points above which Warrior and Berserker end frenzy") RULE_BOOL(Combat, OneProcPerWeapon, true, "If enabled, One proc per weapon per round") -RULE_BOOL(Combat, ProjectileDmgOnImpact, true, "If enabled, projectiles (ie arrows) will hit on impact, instead of instantly") -RULE_BOOL(Combat, MeleePush, true, "Eenable melee push") +RULE_BOOL(Combat, ProjectileDmgOnImpact, true, "If enabled, projectiles (i.e. arrows) will hit on impact, instead of instantly") +RULE_BOOL(Combat, MeleePush, true, "Enable melee push") RULE_INT(Combat, MeleePushChance, 50, "NPC chance the target will be pushed. Made up, 100 actually isn't that bad") RULE_BOOL(Combat, UseLiveCombatRounds, true, "Turn this false if you don't want to worry about fixing up combat rounds for NPCs") -RULE_INT(Combat, NPCAssistCap, 5, "Maxiumium number of NPC that will assist another NPC at once") +RULE_INT(Combat, NPCAssistCap, 5, "Maximum number of NPC that will assist another NPC at once") RULE_INT(Combat, NPCAssistCapTimer, 6000, "Time a NPC will take to clear assist aggro cap space (milliseconds)") RULE_BOOL(Combat, UseRevampHandToHand, false, "Use h2h revamped dmg/delays I believe this was implemented during SoF") RULE_BOOL(Combat, ClassicMasterWu, false, "Classic Master Wu uses a random special, modern doesn't") @@ -522,7 +521,7 @@ RULE_BOOL(NPC, SmartLastFightingDelayMoving, true, "When true, mobs that started RULE_BOOL(NPC, ReturnNonQuestNoDropItems, false, "Returns NO DROP items on NPC that don't have an EVENT_TRADE sub in their script") RULE_INT(NPC, StartEnrageValue, 9, " Percentage HP that an NPC will begin to enrage") RULE_BOOL(NPC, LiveLikeEnrage, false, "If set to true then only player controlled pets will enrage") -RULE_BOOL(NPC, EnableMeritBasedFaction, false, "If set to true, faction will given in the same way as experience (solo/group/raid)") +RULE_BOOL(NPC, EnableMeritBasedFaction, false, "If set to true, faction will be given in the same way as experience (solo/group/raid)") RULE_INT(NPC, NPCToNPCAggroTimerMin, 500, "Minimum time span after which one NPC aggro another NPC (milliseconds)") RULE_INT(NPC, NPCToNPCAggroTimerMax, 6000, "Maximum time span after which one NPC aggro another NPC (milliseconds)") RULE_BOOL(NPC, UseClassAsLastName, true, "Uses class archetype as LastName for NPC with none") @@ -714,7 +713,7 @@ RULE_CATEGORY(QueryServ) RULE_BOOL(QueryServ, PlayerLogChat, false, "Log player chat") RULE_BOOL(QueryServ, PlayerLogTrades, false, "Log player trades") RULE_BOOL(QueryServ, PlayerDropItems, false, "Log player dropping items") -RULE_BOOL(QueryServ, PlayerLogHandins, false, "Log player handins") +RULE_BOOL(QueryServ, PlayerLogHandins, false, "Log player hand ins") RULE_BOOL(QueryServ, PlayerLogNPCKills, false, "Log player NPC kills") RULE_BOOL(QueryServ, PlayerLogDeletes, false, "Log player deletes") RULE_BOOL(QueryServ, PlayerLogMoves, false, "Log player moves") @@ -792,15 +791,15 @@ RULE_CATEGORY(Expedition) RULE_INT(Expedition, MinStatusToBypassPlayerCountRequirements, 80, "Minimum GM status to bypass minimum player requirements for Expedition creation") RULE_BOOL(Expedition, EmptyDzShutdownEnabled, true, "Enable early instance shutdown after last member of expedition removed") RULE_INT(Expedition, EmptyDzShutdownDelaySeconds, 1500, "Seconds to set dynamic zone instance expiration if early shutdown enabled") -RULE_INT(Expedition, WorldExpeditionProcessRateMS, 6000, "Timer interval (ms) that world checks expedition states") +RULE_INT(Expedition, WorldExpeditionProcessRateMS, 6000, "Timer interval (milliseconds) that world checks expedition states") RULE_BOOL(Expedition, AlwaysNotifyNewLeaderOnChange, false, "Always notify clients when made expedition leader. If false (live-like) new leaders are only notified when made leader via /dzmakeleader") RULE_REAL(Expedition, LockoutDurationMultiplier, 1.0, "Multiplies lockout duration by this value when new lockouts are added") -RULE_BOOL(Expedition, EnableInDynamicZoneStatus, false, "Enables the 'In Dynamic Zone' member status in expedition window. If false (live-like) players inside the dz will show as 'Online'") -RULE_INT(Expedition, ChooseLeaderCooldownTime, 2000, "Cooldown time (ms) between choosing a new leader for automatic leader changes") +RULE_BOOL(Expedition, EnableInDynamicZoneStatus, false, "Enables the 'In Dynamic Zone' member status in expedition window. If false (live-like) players inside the dynamic zone will show as 'Online'") +RULE_INT(Expedition, ChooseLeaderCooldownTime, 2000, "Cooldown time (milliseconds) between choosing a new leader for automatic leader changes") RULE_CATEGORY_END() RULE_CATEGORY(DynamicZone) -RULE_INT(DynamicZone, ClientRemovalDelayMS, 60000, "Delay (ms) until a client is teleported out of dynamic zone after being removed as member") +RULE_INT(DynamicZone, ClientRemovalDelayMS, 60000, "Delay (milliseconds) until a client is teleported out of dynamic zone after being removed as member") RULE_CATEGORY_END() #undef RULE_CATEGORY From d9e23a0303252aa15676cda4c86b880da17aa6fc Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Sun, 28 Mar 2021 19:14:36 -0400 Subject: [PATCH 002/624] [Expeditions] Decouple dz updates from expeditions (#1303) Use internal dz messages to process duration and location changes Add world DynamicZone class (later this will inherit from a base) Add FindDynamicZoneByID to get dz from zone and world system caches --- common/eq_constants.h | 10 ++ .../repositories/instance_list_repository.h | 9 ++ common/servertalk.h | 26 ++-- world/CMakeLists.txt | 2 + world/dynamic_zone.cpp | 103 ++++++++++++++++ world/dynamic_zone.h | 42 +++++++ world/expedition.cpp | 56 +-------- world/expedition.h | 18 +-- world/expedition_database.cpp | 31 +++-- world/expedition_database.h | 1 - world/expedition_message.cpp | 6 - world/expedition_state.cpp | 23 ++-- world/expedition_state.h | 2 +- world/zoneserver.cpp | 16 +-- zone/dynamic_zone.cpp | 108 ++++++++++++++-- zone/dynamic_zone.h | 20 +-- zone/expedition.cpp | 116 +----------------- zone/expedition.h | 11 +- zone/lua_expedition.cpp | 14 +-- zone/perl_expedition.cpp | 14 +-- zone/worldserver.cpp | 8 +- 21 files changed, 350 insertions(+), 286 deletions(-) create mode 100644 world/dynamic_zone.cpp create mode 100644 world/dynamic_zone.h diff --git a/common/eq_constants.h b/common/eq_constants.h index 1a0c517bd..cb042043f 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -464,4 +464,14 @@ namespace ZoneBlockedSpellTypes { const uint8 Region = 2; }; +enum class DynamicZoneType +{ + None = 0, + Expedition, + Tutorial, + Task, + Mission, // Shared Task + Quest +}; + #endif /*COMMON_EQ_CONSTANTS_H*/ diff --git a/common/repositories/instance_list_repository.h b/common/repositories/instance_list_repository.h index 312211295..6af82e302 100644 --- a/common/repositories/instance_list_repository.h +++ b/common/repositories/instance_list_repository.h @@ -65,6 +65,15 @@ public: // Custom extended repository methods here + static int UpdateDuration(Database& db, int instance_id, uint32_t new_duration) + { + auto results = db.QueryDatabase(fmt::format( + "UPDATE {} SET duration = {} WHERE {} = {};", + TableName(), new_duration, PrimaryKey(), instance_id + )); + + return (results.Success() ? results.RowsAffected() : 0); + } }; #endif //EQEMU_INSTANCE_LIST_REPOSITORY_H diff --git a/common/servertalk.h b/common/servertalk.h index 40be635ad..e86dd2c86 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -151,23 +151,23 @@ #define ServerOP_ExpeditionGetOnlineMembers 0x0407 #define ServerOP_ExpeditionDzAddPlayer 0x0408 #define ServerOP_ExpeditionDzMakeLeader 0x0409 -#define ServerOP_ExpeditionDzCompass 0x040a -#define ServerOP_ExpeditionDzSafeReturn 0x040b -#define ServerOP_ExpeditionDzZoneIn 0x040c #define ServerOP_ExpeditionCharacterLockout 0x040d #define ServerOP_ExpeditionSaveInvite 0x040e #define ServerOP_ExpeditionRequestInvite 0x040f #define ServerOP_ExpeditionReplayOnJoin 0x0410 #define ServerOP_ExpeditionLockState 0x0411 #define ServerOP_ExpeditionMembersRemoved 0x0412 -#define ServerOP_ExpeditionDzDuration 0x0413 #define ServerOP_ExpeditionLockoutDuration 0x0414 -#define ServerOP_ExpeditionSecondsRemaining 0x0415 #define ServerOP_ExpeditionExpireWarning 0x0416 #define ServerOP_ExpeditionChooseNewLeader 0x0417 #define ServerOP_DzCharacterChange 0x0450 #define ServerOP_DzRemoveAllCharacters 0x0451 +#define ServerOP_DzSetSecondsRemaining 0x0452 +#define ServerOP_DzDurationUpdate 0x0453 +#define ServerOP_DzSetCompass 0x0454 +#define ServerOP_DzSetSafeReturn 0x0455 +#define ServerOP_DzSetZoneIn 0x0456 #define ServerOP_LSInfo 0x1000 #define ServerOP_LSStatus 0x1001 @@ -2090,11 +2090,6 @@ struct ServerExpeditionCharacterID_Struct { uint32_t character_id; }; -struct ServerExpeditionUpdateDuration_Struct { - uint32_t expedition_id; - uint32_t new_duration_seconds; -}; - struct ServerExpeditionExpireWarning_Struct { uint32_t expedition_id; uint32_t minutes_remaining; @@ -2117,12 +2112,10 @@ struct ServerDzCommandMakeLeader_Struct { }; struct ServerDzLocation_Struct { - uint32 owner_id; // system associated with the dz (expedition, shared task, etc) - uint16 dz_zone_id; - uint16 dz_instance_id; + uint32 dz_id; uint32 sender_zone_id; uint16 sender_instance_id; - uint32 zone_id; // compass or safereturn zone id + uint32 zone_id; float y; float x; float z; @@ -2136,6 +2129,11 @@ struct ServerDzCharacter_Struct { uint32 character_id; }; +struct ServerDzSetDuration_Struct { + uint32 dz_id; + uint32 seconds; +}; + #pragma pack() #endif diff --git a/world/CMakeLists.txt b/world/CMakeLists.txt index 50210f2f3..183f4a27d 100644 --- a/world/CMakeLists.txt +++ b/world/CMakeLists.txt @@ -7,6 +7,7 @@ SET(world_sources cliententry.cpp clientlist.cpp console.cpp + dynamic_zone.cpp eql_config.cpp eqemu_api_world_data_service.cpp expedition.cpp @@ -41,6 +42,7 @@ SET(world_headers cliententry.h clientlist.h console.h + dynamic_zone.h eql_config.h eqemu_api_world_data_service.h expedition.h diff --git a/world/dynamic_zone.cpp b/world/dynamic_zone.cpp new file mode 100644 index 000000000..47e70d00a --- /dev/null +++ b/world/dynamic_zone.cpp @@ -0,0 +1,103 @@ +#include "dynamic_zone.h" +#include "expedition.h" +#include "expedition_state.h" +#include "worlddb.h" +#include "zonelist.h" +#include "zoneserver.h" +#include "../common/eqemu_logsys.h" +#include "../common/repositories/instance_list_repository.h" + +extern ZSList zoneserver_list; + +DynamicZone::DynamicZone( + uint32_t id, uint32_t zone_id, uint32_t instance_id, uint32_t zone_version, + uint32_t start_time, uint32_t duration, DynamicZoneType type +) : + m_id(id), + m_instance_id(instance_id), + m_zone_id(zone_id), + m_zone_version(zone_version), + m_start_time(std::chrono::system_clock::from_time_t(start_time)), + m_duration(duration), + m_type(type), + m_expire_time(m_start_time + m_duration) +{ +} + +DynamicZone* DynamicZone::FindDynamicZoneByID(uint32_t dz_id) +{ + auto expedition = expedition_state.GetExpeditionByDynamicZoneID(dz_id); + if (expedition) + { + return &expedition->GetDynamicZone(); + } + // todo: other system caches + return nullptr; +} + +void DynamicZone::SetSecondsRemaining(uint32_t seconds_remaining) +{ + auto now = std::chrono::system_clock::now(); + auto new_remaining = std::chrono::seconds(seconds_remaining); + + auto current_remaining = m_expire_time - now; + if (current_remaining > new_remaining) // reduce only + { + LogDynamicZonesDetail("Updating dynamic zone [{}] instance [{}] seconds remaining to [{}]s", + GetID(), GetInstanceID(), seconds_remaining); + + // preserve original start time and adjust duration instead + m_expire_time = now + new_remaining; + m_duration = std::chrono::duration_cast(m_expire_time - m_start_time); + + InstanceListRepository::UpdateDuration(database, + GetInstanceID(), static_cast(m_duration.count())); + + SendZonesDurationUpdate(); // update zone caches and actual instance's timer + } +} + +void DynamicZone::SendZonesDurationUpdate() +{ + constexpr uint32_t packsize = sizeof(ServerDzSetDuration_Struct); + auto pack = std::make_unique(ServerOP_DzDurationUpdate, packsize); + auto packbuf = reinterpret_cast(pack->pBuffer); + packbuf->dz_id = GetID(); + packbuf->seconds = static_cast(m_duration.count()); + zoneserver_list.SendPacket(pack.get()); +} + +void DynamicZone::HandleZoneMessage(ServerPacket* pack) +{ + switch (pack->opcode) + { + case ServerOP_DzSetCompass: + case ServerOP_DzSetSafeReturn: + case ServerOP_DzSetZoneIn: + { + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_DzCharacterChange: + case ServerOP_DzRemoveAllCharacters: + { + auto buf = reinterpret_cast(pack->pBuffer); + ZoneServer* instance_zs = zoneserver_list.FindByInstanceID(buf->instance_id); + if (instance_zs) + { + instance_zs->SendPacket(pack); + } + break; + } + case ServerOP_DzSetSecondsRemaining: + { + auto buf = reinterpret_cast(pack->pBuffer); + auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); + if (dz) + { + dz->SetSecondsRemaining(buf->seconds); + } + break; + } + }; +} diff --git a/world/dynamic_zone.h b/world/dynamic_zone.h new file mode 100644 index 000000000..c95512260 --- /dev/null +++ b/world/dynamic_zone.h @@ -0,0 +1,42 @@ +#ifndef WORLD_DYNAMIC_ZONE_H +#define WORLD_DYNAMIC_ZONE_H + +#include "../common/eq_constants.h" +#include + +class ServerPacket; + +class DynamicZone +{ +public: + DynamicZone() = default; + DynamicZone(uint32_t id, uint32_t zone_id, uint32_t instance_id, uint32_t zone_version, + uint32_t start_time, uint32_t duration, DynamicZoneType type); + + static DynamicZone* FindDynamicZoneByID(uint32_t dz_id); + static void HandleZoneMessage(ServerPacket* pack); + + uint32_t GetID() const { return m_id; } + uint16_t GetInstanceID() const { return static_cast(m_instance_id); } + uint16_t GetZoneID() const { return static_cast(m_zone_id); } + uint32_t GetZoneVersion() const { return m_zone_version; } + std::chrono::system_clock::duration GetRemainingDuration() const { + return m_expire_time - std::chrono::system_clock::now(); } + + bool IsExpired() const { return m_expire_time < std::chrono::system_clock::now(); } + void SetSecondsRemaining(uint32_t seconds_remaining); + +private: + void SendZonesDurationUpdate(); + + uint32_t m_id = 0; + uint32_t m_instance_id = 0; + uint32_t m_zone_id = 0; + uint32_t m_zone_version = 0; + DynamicZoneType m_type{ DynamicZoneType::None }; + std::chrono::seconds m_duration; + std::chrono::time_point m_start_time; + std::chrono::time_point m_expire_time; +}; + +#endif diff --git a/world/expedition.cpp b/world/expedition.cpp index c2562b5c2..d983a82d0 100644 --- a/world/expedition.cpp +++ b/world/expedition.cpp @@ -30,28 +30,19 @@ extern ClientList client_list; extern ZSList zoneserver_list; -Expedition::Expedition(uint32_t expedition_id, uint32_t dz_id, uint32_t dz_instance_id, - uint32_t dz_zone_id, uint32_t start_time, uint32_t duration, uint32_t leader_id +Expedition::Expedition(uint32_t expedition_id, const DynamicZone& dz, uint32_t leader_id ) : m_expedition_id(expedition_id), - m_dz_id(dz_id), - m_dz_instance_id(dz_instance_id), - m_dz_zone_id(dz_zone_id), - m_start_time(std::chrono::system_clock::from_time_t(start_time)), - m_duration(duration), + m_dynamic_zone(dz), m_leader_id(leader_id), m_choose_leader_cooldown_timer{ static_cast(RuleI(Expedition, ChooseLeaderCooldownTime)) } { - m_expire_time = m_start_time + m_duration; m_warning_cooldown_timer.Enable(); } void Expedition::AddMember(uint32_t character_id) { - auto it = std::find_if(m_member_ids.begin(), m_member_ids.end(), - [&](uint32_t member_id) { return member_id == character_id; }); - - if (it == m_member_ids.end()) + if (!HasMember(character_id)) { m_member_ids.emplace_back(character_id); } @@ -126,16 +117,6 @@ void Expedition::SendZonesExpeditionDeleted() zoneserver_list.SendPacket(pack.get()); } -void Expedition::SendZonesDurationUpdate() -{ - uint32_t packsize = sizeof(ServerExpeditionUpdateDuration_Struct); - auto pack = std::make_unique(ServerOP_ExpeditionDzDuration, packsize); - auto packbuf = reinterpret_cast(pack->pBuffer); - packbuf->expedition_id = GetID(); - packbuf->new_duration_seconds = static_cast(m_duration.count()); - zoneserver_list.SendPacket(pack.get()); -} - void Expedition::SendZonesExpireWarning(uint32_t minutes_remaining) { uint32_t pack_size = sizeof(ServerExpeditionExpireWarning_Struct); @@ -156,41 +137,12 @@ void Expedition::SendZonesLeaderChanged() zoneserver_list.SendPacket(pack.get()); } -void Expedition::UpdateDzSecondsRemaining(uint32_t seconds_remaining) -{ - auto now = std::chrono::system_clock::now(); - auto update_time = std::chrono::seconds(seconds_remaining); - - auto current_remaining = m_expire_time - now; - if (current_remaining > update_time) // reduce only - { - LogExpeditionsDetail( - "Updating expedition [{}] dz instance [{}] seconds remaining to [{}]s", - GetID(), GetInstanceID(), seconds_remaining - ); - - // preserve original start time and adjust duration instead - m_expire_time = now + update_time; - m_duration = std::chrono::duration_cast(m_expire_time - m_start_time); - - ExpeditionDatabase::UpdateDzDuration(GetInstanceID(), static_cast(m_duration.count())); - - // update zone level caches and update the actual dz instance's timer - SendZonesDurationUpdate(); - } -} - -std::chrono::system_clock::duration Expedition::GetRemainingDuration() const -{ - return m_expire_time - std::chrono::system_clock::now(); -} - void Expedition::CheckExpireWarning() { if (m_warning_cooldown_timer.Check(false)) { using namespace std::chrono_literals; - auto remaining = GetRemainingDuration(); + auto remaining = GetDynamicZone().GetRemainingDuration(); if ((remaining > 14min && remaining < 15min) || (remaining > 4min && remaining < 5min) || (remaining > 0min && remaining < 1min)) diff --git a/world/expedition.h b/world/expedition.h index d166419bb..952abc1a0 100644 --- a/world/expedition.h +++ b/world/expedition.h @@ -21,6 +21,7 @@ #ifndef WORLD_EXPEDITION_H #define WORLD_EXPEDITION_H +#include "dynamic_zone.h" #include "../common/timer.h" #include #include @@ -30,8 +31,7 @@ class Expedition { public: Expedition() = default; - Expedition(uint32_t expedition_id, uint32_t dz_id, uint32_t dz_instance_id, - uint32_t dz_zone_id, uint32_t expire_time, uint32_t duration, uint32_t leader_id); + Expedition(uint32_t expedition_id, const DynamicZone& dz, uint32_t leader_id); void AddMember(uint32_t character_id); void RemoveMember(uint32_t character_id); @@ -39,38 +39,28 @@ public: void CheckExpireWarning(); void CheckLeader(); void ChooseNewLeader(); + DynamicZone& GetDynamicZone() { return m_dynamic_zone; } uint32_t GetID() const { return m_expedition_id; } - uint16_t GetInstanceID() const { return static_cast(m_dz_instance_id); } - uint16_t GetZoneID() const { return static_cast(m_dz_zone_id); } bool HasMember(uint32_t character_id); bool IsEmpty() const { return m_member_ids.empty(); } - bool IsExpired() const { return m_expire_time < std::chrono::system_clock::now(); } bool IsPendingDelete() const { return m_pending_delete; } bool IsValid() const { return m_expedition_id != 0; } - void SendZonesDurationUpdate(); void SendZonesExpeditionDeleted(); void SendZonesExpireWarning(uint32_t minutes_remaining); bool SetNewLeader(uint32_t new_leader_id); void SetPendingDelete(bool pending) { m_pending_delete = pending; } - void UpdateDzSecondsRemaining(uint32_t seconds_remaining); - std::chrono::system_clock::duration GetRemainingDuration() const; private: void SendZonesLeaderChanged(); uint32_t m_expedition_id = 0; - uint32_t m_dz_id = 0; - uint32_t m_dz_instance_id = 0; - uint32_t m_dz_zone_id = 0; uint32_t m_leader_id = 0; bool m_pending_delete = false; bool m_choose_leader_needed = false; Timer m_choose_leader_cooldown_timer; Timer m_warning_cooldown_timer; + DynamicZone m_dynamic_zone; std::vector m_member_ids; - std::chrono::seconds m_duration; - std::chrono::time_point m_start_time; - std::chrono::time_point m_expire_time; }; #endif diff --git a/world/expedition_database.cpp b/world/expedition_database.cpp index f9df67918..2f54d7f3d 100644 --- a/world/expedition_database.cpp +++ b/world/expedition_database.cpp @@ -81,6 +81,7 @@ std::vector ExpeditionDatabase::LoadExpeditions(uint32_t select_expe expeditions.dynamic_zone_id, instance_list.id, instance_list.zone, + instance_list.version, instance_list.start_time, instance_list.duration, expeditions.leader_id, @@ -112,20 +113,26 @@ std::vector ExpeditionDatabase::LoadExpeditions(uint32_t select_expe if (last_expedition_id != expedition_id) { - expeditions.emplace_back( - static_cast(strtoul(row[0], nullptr, 10)), // expedition_id + DynamicZone dynamic_zone{ static_cast(strtoul(row[1], nullptr, 10)), // dz_id - static_cast(strtoul(row[2], nullptr, 10)), // dz_instance_id static_cast(strtoul(row[3], nullptr, 10)), // dz_zone_id - static_cast(strtoul(row[4], nullptr, 10)), // start_time - static_cast(strtoul(row[5], nullptr, 10)), // duration - static_cast(strtoul(row[6], nullptr, 10)) // leader_id + static_cast(strtoul(row[2], nullptr, 10)), // dz_instance_id + static_cast(strtoul(row[4], nullptr, 10)), // dz_zone_version + static_cast(strtoul(row[5], nullptr, 10)), // start_time + static_cast(strtoul(row[6], nullptr, 10)), // duration + DynamicZoneType::Expedition + }; + + expeditions.emplace_back( + expedition_id, + dynamic_zone, + static_cast(strtoul(row[7], nullptr, 10)) // leader_id ); } last_expedition_id = expedition_id; - uint32_t member_id = static_cast(strtoul(row[7], nullptr, 10)); + uint32_t member_id = static_cast(strtoul(row[8], nullptr, 10)); expeditions.back().AddMember(member_id); } } @@ -167,16 +174,6 @@ void ExpeditionDatabase::DeleteExpeditions(const std::vector& expediti } } -void ExpeditionDatabase::UpdateDzDuration(uint16_t instance_id, uint32_t new_duration) -{ - std::string query = fmt::format( - "UPDATE instance_list SET duration = {} WHERE id = {};", - new_duration, instance_id - ); - - database.QueryDatabase(query); -} - void ExpeditionDatabase::UpdateLeaderID(uint32_t expedition_id, uint32_t leader_id) { LogExpeditionsDetail("Updating leader [{}] for expedition [{}]", leader_id, expedition_id); diff --git a/world/expedition_database.h b/world/expedition_database.h index 16341210c..8342a292a 100644 --- a/world/expedition_database.h +++ b/world/expedition_database.h @@ -34,7 +34,6 @@ namespace ExpeditionDatabase void MoveMembersToSafeReturn(const std::vector& expedition_ids); void PurgeExpiredExpeditions(); void PurgeExpiredCharacterLockouts(); - void UpdateDzDuration(uint16_t instance_id, uint32_t new_duration); void UpdateLeaderID(uint32_t expedition_id, uint32_t leader_id); }; diff --git a/world/expedition_message.cpp b/world/expedition_message.cpp index 646e2272e..46ecfa9cc 100644 --- a/world/expedition_message.cpp +++ b/world/expedition_message.cpp @@ -104,12 +104,6 @@ void ExpeditionMessage::HandleZoneMessage(ServerPacket* pack) ExpeditionMessage::RequestInvite(pack); break; } - case ServerOP_ExpeditionSecondsRemaining: - { - auto buf = reinterpret_cast(pack->pBuffer); - expedition_state.SetSecondsRemaining(buf->expedition_id, buf->new_duration_seconds); - break; - } } } diff --git a/world/expedition_state.cpp b/world/expedition_state.cpp index 794cfeb36..c1cb0fc8c 100644 --- a/world/expedition_state.cpp +++ b/world/expedition_state.cpp @@ -38,6 +38,14 @@ Expedition* ExpeditionState::GetExpedition(uint32_t expedition_id) return (it != m_expeditions.end()) ? &(*it) : nullptr; } +Expedition* ExpeditionState::GetExpeditionByDynamicZoneID(uint32_t dz_id) +{ + auto it = std::find_if(m_expeditions.begin(), m_expeditions.end(), + [&](Expedition& expedition) { return expedition.GetDynamicZone().GetID() == dz_id; }); + + return (it != m_expeditions.end()) ? &(*it) : nullptr; +} + void ExpeditionState::LoadActiveExpeditions() { BenchTimer benchmark; @@ -98,15 +106,6 @@ void ExpeditionState::RemoveAllMembers(uint32_t expedition_id) } } -void ExpeditionState::SetSecondsRemaining(uint32_t expedition_id, uint32_t seconds_remaining) -{ - auto expedition = GetExpedition(expedition_id); - if (expedition) - { - expedition->UpdateDzSecondsRemaining(seconds_remaining); - } -} - void ExpeditionState::Process() { if (!m_process_throttle_timer.Check()) @@ -120,13 +119,13 @@ void ExpeditionState::Process() { bool is_deleted = false; - if (it->IsEmpty() || it->IsExpired()) + if (it->IsEmpty() || it->GetDynamicZone().IsExpired()) { // don't delete expedition until its dz instance is empty. this prevents // an exploit where all members leave expedition and complete an event // before being kicked from removal timer. the lockout could never be // applied because the zone expedition cache was already invalidated. - auto dz_zoneserver = zoneserver_list.FindByInstanceID(it->GetInstanceID()); + auto dz_zoneserver = zoneserver_list.FindByInstanceID(it->GetDynamicZone().GetInstanceID()); if (!dz_zoneserver || dz_zoneserver->NumPlayers() == 0) { LogExpeditions("Expedition [{}] expired or empty, notifying zones and deleting", it->GetID()); @@ -137,7 +136,7 @@ void ExpeditionState::Process() if (it->IsEmpty() && !it->IsPendingDelete() && RuleB(Expedition, EmptyDzShutdownEnabled)) { - it->UpdateDzSecondsRemaining(RuleI(Expedition, EmptyDzShutdownDelaySeconds)); + it->GetDynamicZone().SetSecondsRemaining(RuleI(Expedition, EmptyDzShutdownDelaySeconds)); } it->SetPendingDelete(true); diff --git a/world/expedition_state.h b/world/expedition_state.h index 9d54dcec9..58b733989 100644 --- a/world/expedition_state.h +++ b/world/expedition_state.h @@ -35,12 +35,12 @@ class ExpeditionState public: void AddExpedition(uint32_t expedition_id); Expedition* GetExpedition(uint32_t expedition_id); + Expedition* GetExpeditionByDynamicZoneID(uint32_t dz_id); void LoadActiveExpeditions(); void MemberChange(uint32_t expedition_id, uint32_t character_id, bool remove); void Process(); void RemoveAllMembers(uint32_t expedition_id); void RemoveExpedition(uint32_t expedition_id); - void SetSecondsRemaining(uint32_t expedition_id, uint32_t seconds_remaining); private: std::vector m_expeditions; diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 008c98073..3f0efbf19 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -36,6 +36,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "ucs.h" #include "queryserv.h" #include "world_store.h" +#include "dynamic_zone.h" #include "expedition_message.h" extern ClientList client_list; @@ -1367,9 +1368,6 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { case ServerOP_ExpeditionLockState: case ServerOP_ExpeditionMemberStatus: case ServerOP_ExpeditionReplayOnJoin: - case ServerOP_ExpeditionDzCompass: - case ServerOP_ExpeditionDzSafeReturn: - case ServerOP_ExpeditionDzZoneIn: case ServerOP_ExpeditionExpireWarning: { zoneserver_list.SendPacket(pack); @@ -1386,20 +1384,18 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { case ServerOP_ExpeditionCharacterLockout: case ServerOP_ExpeditionSaveInvite: case ServerOP_ExpeditionRequestInvite: - case ServerOP_ExpeditionSecondsRemaining: { ExpeditionMessage::HandleZoneMessage(pack); break; } case ServerOP_DzCharacterChange: case ServerOP_DzRemoveAllCharacters: + case ServerOP_DzSetSecondsRemaining: + case ServerOP_DzSetCompass: + case ServerOP_DzSetSafeReturn: + case ServerOP_DzSetZoneIn: { - auto buf = reinterpret_cast(pack->pBuffer); - ZoneServer* instance_zs = zoneserver_list.FindByInstanceID(buf->instance_id); - if (instance_zs) - { - instance_zs->SendPacket(pack); - } + DynamicZone::HandleZoneMessage(pack); break; } default: diff --git a/zone/dynamic_zone.cpp b/zone/dynamic_zone.cpp index 72b42abba..9358f15a2 100644 --- a/zone/dynamic_zone.cpp +++ b/zone/dynamic_zone.cpp @@ -20,6 +20,7 @@ #include "dynamic_zone.h" #include "client.h" +#include "expedition.h" #include "worldserver.h" #include "zonedb.h" #include "../common/eqemu_logsys.h" @@ -37,20 +38,27 @@ DynamicZone::DynamicZone( } DynamicZone::DynamicZone( - std::string zone_shortname, uint32_t version, uint32_t duration, DynamicZoneType type + std::string zone_name, uint32_t version, uint32_t duration, DynamicZoneType type ) : - m_version(version), - m_duration(duration), - m_type(type) + DynamicZone(ZoneID(zone_name), version, duration, type) { - m_zone_id = ZoneID(zone_shortname.c_str()); - if (!m_zone_id) { - LogDynamicZones("Failed to get zone id for zone [{}]", zone_shortname); + LogDynamicZones("Failed to get zone id for zone [{}]", zone_name); } } +DynamicZone* DynamicZone::FindDynamicZoneByID(uint32_t dz_id) +{ + auto expedition = Expedition::FindCachedExpeditionByDynamicZoneID(dz_id); + if (expedition) + { + return &expedition->GetDynamicZone(); + } + // todo: other system caches + return nullptr; +} + std::unordered_map DynamicZone::LoadMultipleDzFromDatabase( const std::vector& dynamic_zone_ids) { @@ -443,12 +451,23 @@ void DynamicZone::SetCompass(const DynamicZoneLocation& location, bool update_db { m_compass = location; + if (m_on_compass_change) + { + m_on_compass_change(); + } + if (update_db) { SaveCompassToDatabase(); + SendWorldSetLocation(ServerOP_DzSetCompass, location); } } +void DynamicZone::SetCompass(uint32_t zone_id, float x, float y, float z, bool update_db) +{ + SetCompass({ zone_id, x, y, z, 0.0f }, update_db); +} + void DynamicZone::SetSafeReturn(const DynamicZoneLocation& location, bool update_db) { m_safereturn = location; @@ -456,9 +475,15 @@ void DynamicZone::SetSafeReturn(const DynamicZoneLocation& location, bool update if (update_db) { SaveSafeReturnToDatabase(); + SendWorldSetLocation(ServerOP_DzSetSafeReturn, location); } } +void DynamicZone::SetSafeReturn(uint32_t zone_id, float x, float y, float z, float heading, bool update_db) +{ + SetSafeReturn({ zone_id, x, y, z, heading }, update_db); +} + void DynamicZone::SetZoneInLocation(const DynamicZoneLocation& location, bool update_db) { m_zonein = location; @@ -467,9 +492,15 @@ void DynamicZone::SetZoneInLocation(const DynamicZoneLocation& location, bool up if (update_db) { SaveZoneInLocationToDatabase(); + SendWorldSetLocation(ServerOP_DzSetZoneIn, location); } } +void DynamicZone::SetZoneInLocation(float x, float y, float z, float heading, bool update_db) +{ + SetZoneInLocation({ 0, x, y, z, heading }, update_db); +} + bool DynamicZone::IsCurrentZoneDzInstance() const { return (zone && zone->GetInstanceID() != 0 && zone->GetInstanceID() == GetInstanceID()); @@ -496,6 +527,17 @@ uint32_t DynamicZone::GetSecondsRemaining() const return 0; } +void DynamicZone::SetSecondsRemaining(uint32_t seconds_remaining) +{ + // async + constexpr uint32_t pack_size = sizeof(ServerDzSetDuration_Struct); + auto pack = std::make_unique(ServerOP_DzSetSecondsRemaining, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + buf->dz_id = GetID(); + buf->seconds = seconds_remaining; + worldserver.SendPacket(pack.get()); +} + void DynamicZone::SetUpdatedDuration(uint32_t new_duration) { // preserves original start time, just modifies duration and expire time @@ -511,6 +553,22 @@ void DynamicZone::SetUpdatedDuration(uint32_t new_duration) } } +void DynamicZone::SendWorldSetLocation(uint16_t server_opcode, const DynamicZoneLocation& location) +{ + uint32_t pack_size = sizeof(ServerDzLocation_Struct); + auto pack = std::make_unique(server_opcode, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + buf->dz_id = GetID(); + buf->sender_zone_id = zone ? zone->GetZoneID() : 0; + buf->sender_instance_id = zone ? zone->GetInstanceID() : 0; + buf->zone_id = location.zone_id; + buf->x = location.x; + buf->y = location.y; + buf->z = location.z; + buf->heading = location.heading; + worldserver.SendPacket(pack.get()); +} + void DynamicZone::HandleWorldMessage(ServerPacket* pack) { switch (pack->opcode) @@ -540,5 +598,41 @@ void DynamicZone::HandleWorldMessage(ServerPacket* pack) } break; } + case ServerOP_DzDurationUpdate: + { + auto buf = reinterpret_cast(pack->pBuffer); + auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); + if (dz) + { + dz->SetUpdatedDuration(buf->seconds); + } + break; + } + case ServerOP_DzSetCompass: + case ServerOP_DzSetSafeReturn: + case ServerOP_DzSetZoneIn: + { + auto buf = reinterpret_cast(pack->pBuffer); + if (zone && !zone->IsZone(buf->sender_zone_id, buf->sender_instance_id)) + { + auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); + if (dz) + { + if (pack->opcode == ServerOP_DzSetCompass) + { + dz->SetCompass(buf->zone_id, buf->x, buf->y, buf->z, false); + } + else if (pack->opcode == ServerOP_DzSetSafeReturn) + { + dz->SetSafeReturn(buf->zone_id, buf->x, buf->y, buf->z, buf->heading, false); + } + else if (pack->opcode == ServerOP_DzSetZoneIn) + { + dz->SetZoneInLocation(buf->x, buf->y, buf->z, buf->heading, false); + } + } + } + break; + } } } diff --git a/zone/dynamic_zone.h b/zone/dynamic_zone.h index 19d186bba..583dd6958 100644 --- a/zone/dynamic_zone.h +++ b/zone/dynamic_zone.h @@ -21,8 +21,10 @@ #ifndef DYNAMIC_ZONE_H #define DYNAMIC_ZONE_H +#include "../common/eq_constants.h" #include #include +#include #include #include #include @@ -30,16 +32,6 @@ class MySQLRequestRow; class ServerPacket; -enum class DynamicZoneType : uint8_t -{ - None = 0, - Expedition, - Tutorial, - Task, - Mission, // Shared Task - Quest -}; - struct DynamicZoneLocation { uint32_t zone_id = 0; @@ -62,6 +54,7 @@ public: DynamicZone(uint32_t dz_id) : m_id(dz_id) {} DynamicZone(DynamicZoneType type) : m_type(type) {} + static DynamicZone* FindDynamicZoneByID(uint32_t dz_id); static std::unordered_map LoadMultipleDzFromDatabase( const std::vector& dynamic_zone_ids); static void HandleWorldMessage(ServerPacket* pack); @@ -88,15 +81,20 @@ public: bool IsInstanceID(uint32_t instance_id) const; bool IsValid() const { return m_instance_id != 0; } bool IsSameDz(uint32_t zone_id, uint32_t instance_id) const; + void RegisterOnCompassChange(const std::function& on_change) { m_on_compass_change = on_change; } void RemoveAllCharacters(bool enable_removal_timers = true); void RemoveCharacter(uint32_t character_id); void SaveInstanceMembersToDatabase(const std::vector& character_ids); void SendInstanceCharacterChange(uint32_t character_id, bool removed); void SetCompass(const DynamicZoneLocation& location, bool update_db = false); + void SetCompass(uint32_t zone_id, float x, float y, float z, bool update_db = false); void SetLeaderName(const std::string& leader_name) { m_leader_name = leader_name; } void SetName(const std::string& name) { m_name = name; } void SetSafeReturn(const DynamicZoneLocation& location, bool update_db = false); + void SetSafeReturn(uint32_t zone_id, float x, float y, float z, float heading, bool update_db = false); + void SetSecondsRemaining(uint32_t seconds_remaining); void SetZoneInLocation(const DynamicZoneLocation& location, bool update_db = false); + void SetZoneInLocation(float x, float y, float z, float heading, bool update_db = false); void SetUpdatedDuration(uint32_t seconds); private: @@ -105,6 +103,7 @@ private: void SaveCompassToDatabase(); void SaveSafeReturnToDatabase(); void SaveZoneInLocationToDatabase(); + void SendWorldSetLocation(uint16_t server_opcode, const DynamicZoneLocation& location); uint32_t SaveToDatabase(); uint32_t m_id = 0; @@ -122,6 +121,7 @@ private: std::chrono::seconds m_duration; std::chrono::time_point m_start_time; std::chrono::time_point m_expire_time; + std::function m_on_compass_change; }; #endif diff --git a/zone/expedition.cpp b/zone/expedition.cpp index 96ff0d45f..5bfecb8e4 100644 --- a/zone/expedition.cpp +++ b/zone/expedition.cpp @@ -69,6 +69,7 @@ void Expedition::SetDynamicZone(DynamicZone&& dz) dz.SetLeaderName(GetLeaderName()); m_dynamiczone = std::move(dz); + m_dynamiczone.RegisterOnCompassChange([this]() { SendCompassUpdateToZoneMembers(); }); } Expedition* Expedition::TryCreate( @@ -1631,24 +1632,6 @@ void Expedition::SendWorldMemberStatus(uint32_t character_id, ExpeditionMemberSt worldserver.SendPacket(pack.get()); } -void Expedition::SendWorldDzLocationUpdate(uint16_t server_opcode, const DynamicZoneLocation& location) -{ - uint32_t pack_size = sizeof(ServerDzLocation_Struct); - auto pack = std::make_unique(server_opcode, pack_size); - auto buf = reinterpret_cast(pack->pBuffer); - buf->owner_id = GetID(); - buf->dz_zone_id = m_dynamiczone.GetZoneID(); - buf->dz_instance_id = m_dynamiczone.GetInstanceID(); - buf->sender_zone_id = zone ? zone->GetZoneID() : 0; - buf->sender_instance_id = zone ? zone->GetInstanceID() : 0; - buf->zone_id = location.zone_id; - buf->x = location.x; - buf->y = location.y; - buf->z = location.z; - buf->heading = location.heading; - worldserver.SendPacket(pack.get()); -} - void Expedition::SendWorldMemberSwapped( const std::string& remove_char_name, uint32_t remove_char_id, const std::string& add_char_name, uint32_t add_char_id) { @@ -1716,16 +1699,6 @@ void Expedition::SendWorldCharacterLockout( worldserver.SendPacket(pack.get()); } -void Expedition::SendWorldSetSecondsRemaining(uint32_t seconds_remaining) -{ - uint32_t pack_size = sizeof(ServerExpeditionUpdateDuration_Struct); - auto pack = std::make_unique(ServerOP_ExpeditionSecondsRemaining, pack_size); - auto buf = reinterpret_cast(pack->pBuffer); - buf->expedition_id = GetID(); - buf->new_duration_seconds = seconds_remaining; - worldserver.SendPacket(pack.get()); -} - void Expedition::AddLockoutByCharacterID( uint32_t character_id, const std::string& expedition_name, const std::string& event_name, uint32_t seconds, const std::string& uuid) @@ -2007,32 +1980,6 @@ void Expedition::HandleWorldMessage(ServerPacket* pack) } break; } - case ServerOP_ExpeditionDzCompass: - case ServerOP_ExpeditionDzSafeReturn: - case ServerOP_ExpeditionDzZoneIn: - { - auto buf = reinterpret_cast(pack->pBuffer); - if (zone && !zone->IsZone(buf->sender_zone_id, buf->sender_instance_id)) - { - auto expedition = Expedition::FindCachedExpeditionByID(buf->owner_id); - if (expedition) - { - if (pack->opcode == ServerOP_ExpeditionDzCompass) - { - expedition->SetDzCompass(buf->zone_id, buf->x, buf->y, buf->z, false); - } - else if (pack->opcode == ServerOP_ExpeditionDzSafeReturn) - { - expedition->SetDzSafeReturn(buf->zone_id, buf->x, buf->y, buf->z, buf->heading, false); - } - else if (pack->opcode == ServerOP_ExpeditionDzZoneIn) - { - expedition->SetDzZoneInLocation(buf->x, buf->y, buf->z, buf->heading, false); - } - } - } - break; - } case ServerOP_ExpeditionCharacterLockout: { auto buf = reinterpret_cast(pack->pBuffer); @@ -2056,16 +2003,6 @@ void Expedition::HandleWorldMessage(ServerPacket* pack) } break; } - case ServerOP_ExpeditionDzDuration: - { - auto buf = reinterpret_cast(pack->pBuffer); - auto expedition = Expedition::FindCachedExpeditionByID(buf->expedition_id); - if (expedition) - { - expedition->UpdateDzDuration(buf->new_duration_seconds); - } - break; - } case ServerOP_ExpeditionExpireWarning: { auto buf = reinterpret_cast(pack->pBuffer); @@ -2079,11 +2016,8 @@ void Expedition::HandleWorldMessage(ServerPacket* pack) } } -void Expedition::SetDzCompass(uint32_t zone_id, float x, float y, float z, bool update_db) +void Expedition::SendCompassUpdateToZoneMembers() { - DynamicZoneLocation location{ zone_id, x, y, z, 0.0f }; - m_dynamiczone.SetCompass(location, update_db); - for (const auto& member : m_members) { Client* member_client = entity_list.GetClientByCharID(member.char_id); @@ -2092,52 +2026,6 @@ void Expedition::SetDzCompass(uint32_t zone_id, float x, float y, float z, bool member_client->SendDzCompassUpdate(); } } - - if (update_db) - { - SendWorldDzLocationUpdate(ServerOP_ExpeditionDzCompass, location); - } -} - -void Expedition::SetDzCompass(const std::string& zone_name, float x, float y, float z, bool update_db) -{ - auto zone_id = ZoneID(zone_name.c_str()); - SetDzCompass(zone_id, x, y, z, update_db); -} - -void Expedition::SetDzSafeReturn(uint32_t zone_id, float x, float y, float z, float heading, bool update_db) -{ - DynamicZoneLocation location{ zone_id, x, y, z, heading }; - - m_dynamiczone.SetSafeReturn(location, update_db); - - if (update_db) - { - SendWorldDzLocationUpdate(ServerOP_ExpeditionDzSafeReturn, location); - } -} - -void Expedition::SetDzSafeReturn(const std::string& zone_name, float x, float y, float z, float heading, bool update_db) -{ - auto zone_id = ZoneID(zone_name.c_str()); - SetDzSafeReturn(zone_id, x, y, z, heading, update_db); -} - -void Expedition::SetDzSecondsRemaining(uint32_t seconds_remaining) -{ - SendWorldSetSecondsRemaining(seconds_remaining); // async -} - -void Expedition::SetDzZoneInLocation(float x, float y, float z, float heading, bool update_db) -{ - DynamicZoneLocation location{ 0, x, y, z, heading }; - - m_dynamiczone.SetZoneInLocation(location, update_db); - - if (update_db) - { - SendWorldDzLocationUpdate(ServerOP_ExpeditionDzZoneIn, location); - } } bool Expedition::CanClientLootCorpse(Client* client, uint32_t npc_type_id, uint32_t spawn_id) diff --git a/zone/expedition.h b/zone/expedition.h index 4b9b1e940..c78864c6b 100644 --- a/zone/expedition.h +++ b/zone/expedition.h @@ -161,13 +161,6 @@ public: void DzQuit(Client* requester); void DzKickPlayers(Client* requester); - void SetDzCompass(uint32_t zone_id, float x, float y, float z, bool update_db = false); - void SetDzCompass(const std::string& zone_name, float x, float y, float z, bool update_db = false); - void SetDzSafeReturn(uint32_t zone_id, float x, float y, float z, float heading, bool update_db = false); - void SetDzSafeReturn(const std::string& zone_name, float x, float y, float z, float heading, bool update_db = false); - void SetDzSecondsRemaining(uint32_t seconds_remaining); - void SetDzZoneInLocation(float x, float y, float z, float heading, bool update_db = false); - static const int32_t REPLAY_TIMER_ID; static const int32_t EVENT_TIMER_ID; @@ -197,7 +190,7 @@ private: void SendMembersExpireWarning(uint32_t minutes); void SendNewMemberAddedToZoneMembers(const std::string& added_name); void SendUpdatesToZoneMembers(bool clear = false, bool message_on_clear = true); - void SendWorldDzLocationUpdate(uint16_t server_opcode, const DynamicZoneLocation& location); + void SendCompassUpdateToZoneMembers(); void SendWorldExpeditionUpdate(uint16_t server_opcode); void SendWorldAddPlayerInvite(const std::string& inviter_name, const std::string& swap_remove_name, const std::string& add_name, bool pending = false); @@ -209,12 +202,10 @@ private: void SendWorldMemberStatus(uint32_t character_id, ExpeditionMemberStatus status); void SendWorldMemberSwapped(const std::string& remove_char_name, uint32_t remove_char_id, const std::string& add_char_name, uint32_t add_char_id); - void SendWorldSetSecondsRemaining(uint32_t seconds_remaining); void SendWorldSettingChanged(uint16_t server_opcode, bool setting_value); void SetDynamicZone(DynamicZone&& dz); void TryAddClient(Client* add_client, const std::string& inviter_name, const std::string& swap_remove_name, Client* leader_client = nullptr); - void UpdateDzDuration(uint32_t new_duration) { m_dynamiczone.SetUpdatedDuration(new_duration); } void UpdateMemberStatus(uint32_t update_character_id, ExpeditionMemberStatus status); ExpeditionMember GetMemberData(uint32_t character_id); diff --git a/zone/lua_expedition.cpp b/zone/lua_expedition.cpp index a7ac929c0..1bd872cc2 100644 --- a/zone/lua_expedition.cpp +++ b/zone/lua_expedition.cpp @@ -168,7 +168,7 @@ bool Lua_Expedition::IsLocked() { void Lua_Expedition::RemoveCompass() { Lua_Safe_Call_Void(); - self->SetDzCompass(0, 0, 0, 0, true); + self->GetDynamicZone().SetCompass(0, 0, 0, 0, true); } void Lua_Expedition::RemoveLockout(std::string event_name) { @@ -178,12 +178,12 @@ void Lua_Expedition::RemoveLockout(std::string event_name) { void Lua_Expedition::SetCompass(uint32_t zone_id, float x, float y, float z) { Lua_Safe_Call_Void(); - self->SetDzCompass(zone_id, x, y, z, true); + self->GetDynamicZone().SetCompass(zone_id, x, y, z, true); } void Lua_Expedition::SetCompass(std::string zone_name, float x, float y, float z) { Lua_Safe_Call_Void(); - self->SetDzCompass(zone_name, x, y, z, true); + self->GetDynamicZone().SetCompass(ZoneID(zone_name), x, y, z, true); } void Lua_Expedition::SetLocked(bool lock_expedition) { @@ -218,23 +218,23 @@ void Lua_Expedition::SetReplayLockoutOnMemberJoin(bool enable) { void Lua_Expedition::SetSafeReturn(uint32_t zone_id, float x, float y, float z, float heading) { Lua_Safe_Call_Void(); - self->SetDzSafeReturn(zone_id, x, y, z, heading, true); + self->GetDynamicZone().SetSafeReturn(zone_id, x, y, z, heading, true); } void Lua_Expedition::SetSafeReturn(std::string zone_name, float x, float y, float z, float heading) { Lua_Safe_Call_Void(); - self->SetDzSafeReturn(zone_name, x, y, z, heading, true); + self->GetDynamicZone().SetSafeReturn(ZoneID(zone_name), x, y, z, heading, true); } void Lua_Expedition::SetSecondsRemaining(uint32_t seconds_remaining) { Lua_Safe_Call_Void(); - self->SetDzSecondsRemaining(seconds_remaining); + self->GetDynamicZone().SetSecondsRemaining(seconds_remaining); } void Lua_Expedition::SetZoneInLocation(float x, float y, float z, float heading) { Lua_Safe_Call_Void(); - self->SetDzZoneInLocation(x, y, z, heading, true); + self->GetDynamicZone().SetZoneInLocation(x, y, z, heading, true); } void Lua_Expedition::UpdateLockoutDuration(std::string event_name, uint32_t duration) { diff --git a/zone/perl_expedition.cpp b/zone/perl_expedition.cpp index e7b477636..3095c3770 100644 --- a/zone/perl_expedition.cpp +++ b/zone/perl_expedition.cpp @@ -407,7 +407,7 @@ XS(XS_Expedition_RemoveCompass) { Expedition* THIS = nullptr; VALIDATE_THIS_IS_EXPEDITION; - THIS->SetDzCompass(0, 0, 0, 0, true); + THIS->GetDynamicZone().SetCompass(0, 0, 0, 0, true); XSRETURN_EMPTY; } @@ -446,12 +446,12 @@ XS(XS_Expedition_SetCompass) { if (SvTYPE(ST(1)) == SVt_PV) { std::string zone_name(SvPV_nolen(ST(1))); - THIS->SetDzCompass(zone_name, x, y, z, true); + THIS->GetDynamicZone().SetCompass(ZoneID(zone_name), x, y, z, true); } else if (SvTYPE(ST(1)) == SVt_IV) { uint32_t zone_id = static_cast(SvUV(ST(1))); - THIS->SetDzCompass(zone_id, x, y, z, true); + THIS->GetDynamicZone().SetCompass(zone_id, x, y, z, true); } else { @@ -556,12 +556,12 @@ XS(XS_Expedition_SetSafeReturn) { if (SvTYPE(ST(1)) == SVt_PV) { std::string zone_name(SvPV_nolen(ST(1))); - THIS->SetDzSafeReturn(zone_name, x, y, z, heading, true); + THIS->GetDynamicZone().SetSafeReturn(ZoneID(zone_name), x, y, z, heading, true); } else if (SvTYPE(ST(1)) == SVt_IV) { uint32_t zone_id = static_cast(SvUV(ST(1))); - THIS->SetDzSafeReturn(zone_id, x, y, z, heading, true); + THIS->GetDynamicZone().SetSafeReturn(zone_id, x, y, z, heading, true); } else { @@ -582,7 +582,7 @@ XS(XS_Expedition_SetSecondsRemaining) { VALIDATE_THIS_IS_EXPEDITION; uint32_t seconds_remaining = static_cast(SvUV(ST(1))); - THIS->SetDzSecondsRemaining(seconds_remaining); + THIS->GetDynamicZone().SetSecondsRemaining(seconds_remaining); XSRETURN_EMPTY; } @@ -602,7 +602,7 @@ XS(XS_Expedition_SetZoneInLocation) { float z = static_cast(SvNV(ST(3))); float heading = static_cast(SvNV(ST(4))); - THIS->SetDzZoneInLocation(x, y, z, heading, true); + THIS->GetDynamicZone().SetZoneInLocation(x, y, z, heading, true); XSRETURN_EMPTY; } diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index a838a75cb..34994d88a 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -2900,10 +2900,6 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) case ServerOP_ExpeditionGetOnlineMembers: case ServerOP_ExpeditionDzAddPlayer: case ServerOP_ExpeditionDzMakeLeader: - case ServerOP_ExpeditionDzCompass: - case ServerOP_ExpeditionDzSafeReturn: - case ServerOP_ExpeditionDzZoneIn: - case ServerOP_ExpeditionDzDuration: case ServerOP_ExpeditionCharacterLockout: case ServerOP_ExpeditionExpireWarning: { @@ -2912,6 +2908,10 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } case ServerOP_DzCharacterChange: case ServerOP_DzRemoveAllCharacters: + case ServerOP_DzDurationUpdate: + case ServerOP_DzSetCompass: + case ServerOP_DzSetSafeReturn: + case ServerOP_DzSetZoneIn: { DynamicZone::HandleWorldMessage(pack); break; From 74076078bb72f0232bb748525ba4e199d8c81998 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Sun, 28 Mar 2021 19:20:02 -0400 Subject: [PATCH 003/624] =?UTF-8?q?[Boats]=20Fix=20x/y=20offsets=20from=20?= =?UTF-8?q?client=20to=20reflect=20EQ=20x/y=20instead=20of=20boat=20headin?= =?UTF-8?q?g=E2=80=A6=20(#1296)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix x/y offsets from client to reflect EQ x/y instead of boat heading x/y * Use std version of math calls and reduce the # of calls * Remove errant instrumentation Co-authored-by: Noudess --- zone/client_packet.cpp | 61 +++++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index d6ef48140..a48925e5e 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -4411,27 +4411,27 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { PlayerPositionUpdateClient_Struct *ppu = (PlayerPositionUpdateClient_Struct *) app->pBuffer; - /* Boat handling */ - if (ppu->spawn_id != GetID()) { - /* If player is controlling boat */ - if (ppu->spawn_id && ppu->spawn_id == controlling_boat_id) { - Mob *boat = entity_list.GetMob(controlling_boat_id); - if (boat == 0) { - controlling_boat_id = 0; - return; - } + /* Non PC handling like boats and eye of zomm */ + if (ppu->spawn_id && ppu->spawn_id != GetID()) { + Mob *cmob = entity_list.GetMob(ppu->spawn_id); + if (!cmob) { + return; + } + + if (cmob->IsControllableBoat()) { + // Controllable boats auto boat_delta = glm::vec4(ppu->delta_x, ppu->delta_y, ppu->delta_z, EQ10toFloat(ppu->delta_heading)); - boat->SetDelta(boat_delta); + cmob->SetDelta(boat_delta); auto outapp = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); PlayerPositionUpdateServer_Struct *ppus = (PlayerPositionUpdateServer_Struct *) outapp->pBuffer; - boat->MakeSpawnUpdate(ppus); - entity_list.QueueCloseClients(boat, outapp, true, 300, this, false); + cmob->MakeSpawnUpdate(ppus); + entity_list.QueueCloseClients(cmob, outapp, true, 300, this, false); safe_delete(outapp); /* Update the boat's position on the server, without sending an update */ - boat->GMMove(ppu->x_pos, ppu->y_pos, ppu->z_pos, EQ12toFloat(ppu->heading), false); + cmob->GMMove(ppu->x_pos, ppu->y_pos, ppu->z_pos, EQ12toFloat(ppu->heading), false); return; } else { @@ -4439,16 +4439,13 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { // so that other clients see it. I could add a check here for eye of zomm // race, to limit this code, but this should handle any client controlled // mob that gets updates from OP_ClientUpdate - if (ppu->spawn_id == controlled_mob_id) { - Mob *cmob = entity_list.GetMob(ppu->spawn_id); - if (cmob != nullptr) { - cmob->SetPosition(ppu->x_pos, ppu->y_pos, ppu->z_pos); - cmob->SetHeading(EQ12toFloat(ppu->heading)); - mMovementManager->SendCommandToClients(cmob, 0.0, 0.0, 0.0, - 0.0, 0, ClientRangeAny, nullptr, this); - cmob->CastToNPC()->SaveGuardSpot(glm::vec4(ppu->x_pos, - ppu->y_pos, ppu->z_pos, EQ12toFloat(ppu->heading))); - } + if (!cmob->IsControllableBoat() && ppu->spawn_id == controlled_mob_id) { + cmob->SetPosition(ppu->x_pos, ppu->y_pos, ppu->z_pos); + cmob->SetHeading(EQ12toFloat(ppu->heading)); + mMovementManager->SendCommandToClients(cmob, 0.0, 0.0, 0.0, + 0.0, 0, ClientRangeAny, nullptr, this); + cmob->CastToNPC()->SaveGuardSpot(glm::vec4(ppu->x_pos, + ppu->y_pos, ppu->z_pos, EQ12toFloat(ppu->heading))); } } return; @@ -4476,9 +4473,23 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { LogError("Can't find boat for client position offset."); } else { - cx += boat->GetX(); - cy += boat->GetY(); + // Calculate angle from boat heading to EQ heading + double theta = std::fmod(((boat->GetHeading() * 360.0) / 512.0),360.0); + double thetar = (theta * M_PI) / 180.0; + + // Boat cx is inverted (positive to left) + // Boat cy is normal (positive toward heading) + double cosine = std::cos(thetar); + double sine = std::sin(thetar); + + double normalizedx, normalizedy; + normalizedx = cx * cosine - -cy * sine; + normalizedy = -cx * sine + cy * cosine; + + cx = boat->GetX() + normalizedx; + cy = boat->GetY() + normalizedy; cz += boat->GetZ(); + new_heading += boat->GetHeading(); } } From 410ba4b19ad9dc88e9a0043cf80023c1ea42c5cd Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 28 Mar 2021 19:37:21 -0400 Subject: [PATCH 004/624] [Rules] Cleanup all unused rules. (#1308) --- common/ruletypes.h | 53 ------------------------------------- zone/mod_functions.cpp | 2 +- zone/mod_functions_base.cpp | 2 +- 3 files changed, 2 insertions(+), 55 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 3ed1791e1..401e55099 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -70,7 +70,6 @@ RULE_INT(Character, HPRegenMultiplier, 100, "The hitpoint regeneration is multip RULE_INT(Character, ManaRegenMultiplier, 100, "The mana regeneration is multiplied by value/100 (up to the caps)") RULE_INT(Character, EnduranceRegenMultiplier, 100, "The endurance regeneration is multiplied by value/100 (up to the caps)") RULE_BOOL(Character, OldMinMana, false, "This is used for servers that want to follow older skill cap formulas so they can still have some regen w/o mediate") -RULE_INT(Character, ConsumptionMultiplier, 100, "Item's hunger restored = value x item's food level. 100=normal, 50=player eat 2x as fast, 200=player eat 2x as slow") RULE_BOOL(Character, HealOnLevel, false, "Setting whether a player should heal completely when leveling") RULE_BOOL(Character, FeignKillsPet, false, "Setting whether Feign Death kills the player pet") RULE_INT(Character, ItemManaRegenCap, 15, "Limit on mana regeneration granted by items") @@ -176,12 +175,10 @@ RULE_INT(Mercs, AggroRadius, 100, "Determines the distance from which a merc wil RULE_INT(Mercs, AggroRadiusPuller, 25, "Determines the distance from which a merc will aggro group member's target, if they have the group role of puller (also used to determine the distance at which a healer merc will begin healing a group member, if they have the group role of puller)") RULE_INT(Mercs, ResurrectRadius, 50, "Determines the distance from which a healer merc will attempt to resurrect a group member's corpse") RULE_INT(Mercs, ScaleRate, 100, "Merc scale factor") -RULE_BOOL(Mercs, MercsUsePathing, true, "Mercs will use node pathing when moving") RULE_BOOL(Mercs, AllowMercSuspendInCombat, true, "Allow merc suspend in combat") RULE_CATEGORY_END() RULE_CATEGORY(Guild) -RULE_INT(Guild, MaxMembers, 2048, "Maximum number of members a guild can have") RULE_BOOL(Guild, PlayerCreationAllowed, false, "Allow players to create a guild using the window in Underfoot+") RULE_INT(Guild, PlayerCreationLimit, 1, "Only allow use of the UF+ window if the account has < than this number of guild leaders on it") RULE_INT(Guild, PlayerCreationRequiredStatus, 0, "Required status to create a guild") @@ -230,7 +227,6 @@ RULE_INT(World, AddMaxClientsStatus, -1, "Accounts with status >= this rule will RULE_BOOL(World, MaxClientsSetByStatus, false, "If true, IP Limiting will be set to the status on the account as long as the status is > MaxClientsPerIP") RULE_BOOL(World, EnableIPExemptions, false, "If true, ip_exemptions table is used, if there is no entry for the IP it will default to RuleI(World, MaxClientsPerIP)") RULE_BOOL(World, ClearTempMerchantlist, true, "Clears temp merchant items when world boots") -RULE_BOOL(World, DeleteStaleCorpeBackups, true, "Deletes stale corpse backups older than 2 weeks") RULE_BOOL(World, GMAccountIPList, false, "Check IP list against GM accounts. This increases the security of GM accounts, e.g. if you only allow localhost '127.0.0.1' for GM accounts. Think carefully about what you enter!") RULE_INT(World, MinGMAntiHackStatus, 1, "Minimum status to check against AntiHack list") RULE_INT(World, SoFStartZoneID, -1, "Sets the Starting Zone for SoF Clients separate from Titanium Clients (-1 is disabled)") @@ -242,7 +238,6 @@ RULE_INT(World, PVPMinLevel, 0, "Minimum level to pvp") RULE_BOOL (World, IsGMPetitionWindowEnabled, false, "Setting whether the GM petition window is available") RULE_INT (World, FVNoDropFlag, 0, "Sets the Firiona Vie settings on the client, allowing trading of no-drop items. 1=for all players, 2=for GM only") RULE_BOOL (World, IPLimitDisconnectAll, false, "Disconnect all current clients by IP if they go over the IP limit. This should allow people to quickly reconnect in the case of dead sessions waiting to timeout") -RULE_BOOL(World, MaxClientsSimplifiedLogic, false, "New logic that only uses ExemptMaxClientsStatus and MaxClientsPerIP. Done on the loginserver. This mimics the P99-style special IP rules") RULE_INT (World, TellQueueSize, 20, "Maximum tell queue size") RULE_BOOL(World, StartZoneSameAsBindOnCreation, true, "Should the start zone always be the same location as your bind?") RULE_BOOL(World, EnforceCharacterLimitAtLogin, false, "Enforce the limit for characters that are online at login") @@ -253,16 +248,6 @@ RULE_CATEGORY(Zone) RULE_INT(Zone, ClientLinkdeadMS, 90000, "The time a client remains link dead on the server after a sudden disconnection (milliseconds)") RULE_INT(Zone, GraveyardTimeMS, 1200000, "Time until a player corpse is moved to a zone's graveyard, if one is specified for the zone (milliseconds)") RULE_BOOL(Zone, EnableShadowrest, 1, "Enables or disables the Shadowrest zone feature for player corpses. Default is turned on") -RULE_BOOL(Zone, UsePlayerCorpseBackups, true, "Keeps backups of player corpses") -RULE_INT(Zone, MQWarpExemptStatus, -1, "Required status level to exempt the MQWarpDetector. Set to -1 to disable this feature") -RULE_INT(Zone, MQZoneExemptStatus, -1, "Required status level to exempt the MQZoneDetector. Set to -1 to disable this feature") -RULE_INT(Zone, MQGateExemptStatus, -1, "Required status level to exempt the MQGateDetector. Set to -1 to disable this feature") -RULE_INT(Zone, MQGhostExemptStatus, -1, "Required status level to exempt the MGhostDetector. Set to -1 to disable this feature") -RULE_BOOL(Zone, EnableMQWarpDetector, true, "Enable the MQWarp Detector. Set to False to disable this feature") -RULE_BOOL(Zone, EnableMQZoneDetector, true, "Enable the MQZone Detector. Set to False to disable this feature") -RULE_BOOL(Zone, EnableMQGateDetector, true, "Enable the MQGate Detector. Set to False to disable this feature") -RULE_BOOL(Zone, EnableMQGhostDetector, true, "Enable the MQGhost Detector. Set to False to disable this feature") -RULE_REAL(Zone, MQWarpDetectionDistanceFactor, 9.0, "Distance factor for MQ-Warp detection. Clients move at 4.4 about if in a straight line but with movement and to acct for lag we raise it a bit") RULE_INT(Zone, AutoShutdownDelay, 5000, "How long a dynamic zone stays loaded while empty (milliseconds)") RULE_INT(Zone, PEQZoneReuseTime, 900, "Time between two uses of the #peqzone command (seconds)") RULE_INT(Zone, PEQZoneDebuff1, 4454, "First debuff casted by #peqzone Default is Cursed Keeper's Blight") @@ -292,7 +277,6 @@ RULE_INT(Map, FindBestZHeightAdjust, 1, "Adds this to the current Z before seeki RULE_CATEGORY_END() RULE_CATEGORY(Pathing) -RULE_BOOL(Pathing, Guard, true, "Enable pathing for mobs moving to their guard point") RULE_BOOL(Pathing, Find, true, "Enable pathing for FindPerson requests from the client") RULE_BOOL(Pathing, Fear, true, "Enable pathing for fear") RULE_REAL(Pathing, NavmeshStepSize, 100.0f, "Step size for the movement manager") @@ -302,9 +286,6 @@ RULE_CATEGORY_END() RULE_CATEGORY(Watermap) // enable these to use the water detection code. Requires Water Maps generated by awater utility -RULE_BOOL(Watermap, CheckWaypointsInWaterWhenLoading, false, "Does not apply BestZ as waypoints are loaded if they are in water") -RULE_BOOL(Watermap, CheckForWaterAtWaypoints, false, "Check if a mob has moved into/out of water when at waypoints and sets flymode") -RULE_BOOL(Watermap, CheckForWaterWhenMoving, false, "Checks if a mob has moved into/out of water each time it's loc is recalculated") RULE_BOOL(Watermap, CheckForWaterOnSendTo, false, "Checks if a mob has moved into/out of water on SendTo") RULE_BOOL(Watermap, CheckForWaterWhenFishing, false, "Only lets a player fish near water (if a water map exists for the zone)") RULE_REAL(Watermap, FishingRodLength, 30, "How far in front of player water must be for fishing to work") @@ -313,10 +294,6 @@ RULE_REAL(Watermap, FishingLineStepSize, 1, "Basic step size for fishing calc, t RULE_CATEGORY_END() RULE_CATEGORY(Spells) -RULE_REAL(Spells, ResistChance, 2.0, "chance to resist given no resists and same level") -RULE_REAL(Spells, ResistMod, 0.40, "Multiplier, chance to resist = this * ResistAmount") -RULE_REAL(Spells, PartialHitChance, 0.7, "The chance when a spell is resisted that it will partial hit") -RULE_REAL(Spells, PartialHitChanceFear, 0.25, "The chance when a fear spell is resisted that it will partial hit") RULE_INT(Spells, BaseCritChance, 0, "Base percentage chance that everyone has to crit a spell") RULE_INT(Spells, BaseCritRatio, 100, "Base percentage bonus to damage on a successful spell crit. 100=2xdamage") RULE_INT(Spells, WizCritLevel, 12, "Level wizards first get spell crits") @@ -395,7 +372,6 @@ RULE_CATEGORY(Combat) RULE_REAL(Combat, AERampageSafeZone, 0.018, "max hit ae ramp reduction range") RULE_INT(Combat, PetBaseCritChance, 0, "Pet base crit chance") RULE_INT(Combat, NPCBashKickLevel, 6, "The level that NPCcan KICK/BASH") -RULE_INT(Combat, NPCBashKickStunChance, 15, "Percent chance that a bash/kick will stun") RULE_INT(Combat, MeleeCritDifficulty, 8900, "Value against which is rolled to check if a melee crit is triggered. Lower is easier") RULE_INT(Combat, ArcheryCritDifficulty, 3400, "Value against which is rolled to check if an archery crit is triggered. Lower is easier") RULE_INT(Combat, ThrowingCritDifficulty, 1100, "Value against which is rolled to check if a throwing crit is triggered. Lower is easier") @@ -403,7 +379,6 @@ RULE_BOOL(Combat, NPCCanCrit, false, "Setting whether an NPC can land critical h RULE_BOOL(Combat, UseIntervalAC, true, "Switch whether bonuses, armour class, multipliers, classes and caps should be considered in the calculation of damage values") RULE_INT(Combat, PetAttackMagicLevel, 30, "Level at which pets can cause magic damage") RULE_BOOL(Combat, EnableFearPathing, true, "Setting whether to use pathing during fear") -RULE_REAL(Combat, FleeMultiplier, 2.0, "Determines how quickly a NPC will slow down while fleeing. Decrease multiplier to slow NPC down quicker") RULE_BOOL(Combat, FleeGray, true, "If true FleeGrayHPRatio will be used") RULE_INT(Combat, FleeGrayHPRatio, 50, "HP percentage when a Gray NPC begins to flee") RULE_INT(Combat, FleeGrayMaxLevel, 18, "NPC above this level won't do gray/green con flee") @@ -414,8 +389,6 @@ RULE_REAL(Combat, AvgProcsPerMinute, 2.0, "Average proc rate per minute") RULE_REAL(Combat, ProcPerMinDexContrib, 0.075, "Increases the probability of a proc increased by DEX by the value indicated") RULE_REAL(Combat, BaseProcChance, 0.035, "Base chance for procs") RULE_REAL(Combat, ProcDexDivideBy, 11000, "Divisor for the probability of a proc increased by dexterity") -RULE_BOOL(Combat, AdjustSpecialProcPerMinute, true, "Set PPM for special abilities like HeadShot (Live does this as of 4-14)") -RULE_REAL(Combat, AvgSpecialProcsPerMinute, 2.0, "Unclear what best value is atm") RULE_REAL(Combat, BaseHitChance, 69.0, "Base chance to hit") RULE_REAL(Combat, NPCBonusHitChance, 26.0, "Bonus chance to hit for NPC") RULE_REAL(Combat, HitFalloffMinor, 5.0, "Hit will fall off up to value over the initial level range (percent)") @@ -429,22 +402,12 @@ RULE_REAL(Combat, MinChancetoHit, 5.0, "Minimum percentage chance to hit with re RULE_REAL(Combat, MaxChancetoHit, 95.0, "Maximum percentage chance to hit with regular melee/ranged") RULE_INT(Combat, MinRangedAttackDist, 25, "Minimum Distance to use Ranged Attacks") RULE_BOOL(Combat, ArcheryBonusRequiresStationary, true, "does the 2x archery bonus chance require a stationary npc") -RULE_REAL(Combat, ArcheryBaseDamageBonus, 1, "Percentage modifier to base archery Damage 0.5=50% base damage, 1=100%,2=200%") RULE_REAL(Combat, ArcheryNPCMultiplier, 1.0, "Value is multiplied by the regular dmg to get the archery dmg") RULE_BOOL(Combat, AssistNoTargetSelf, true, "When assisting a target that does not have a target: true = target self, false = leave target as was before assist (false = live like)") RULE_INT(Combat, MaxRampageTargets, 3, "Maximum number of people hit with rampage") RULE_INT(Combat, DefaultRampageTargets, 1, "Default number of people to hit with rampage") RULE_BOOL(Combat, RampageHitsTarget, false, "Rampage will hit the target if it still has targets left") RULE_INT(Combat, MaxFlurryHits, 2, "Maximum number of extra hits from flurry") -RULE_INT(Combat, MonkDamageTableBonus, 5, "Percentage bonus monks get to their damage table calcs") -RULE_INT(Combat, FlyingKickBonus, 25, "Percentage Modifier that this skill gets to str and skill bonuses") -RULE_INT(Combat, DragonPunchBonus, 20, "Percentage Modifier that this skill gets to str and skill bonuses") -RULE_INT(Combat, EagleStrikeBonus, 15, "Percentage Modifier that this skill gets to str and skill bonuses") -RULE_INT(Combat, TigerClawBonus, 10, "Percentage Modifier that this skill gets to str and skill bonuses") -RULE_INT(Combat, RoundKickBonus, 5, "Percentage Modifier that this skill gets to str and skill bonuses") -RULE_INT(Combat, FrenzyBonus, 0, "Percentage Modifier to damage") -RULE_INT(Combat, BackstabBonus, 0, "Percentage Modifier to damage") -RULE_BOOL(Combat, ProcTargetOnly, true, "true = procs will only affect our target, false = procs will affect all of our targets") RULE_REAL(Combat, NPCACFactor, 2.25, "If UseIntervalAC is enabled, the armor class for NPC is divided by this value") RULE_INT(Combat, ClothACSoftcap, 75, "If OldACSoftcapRules is true: armorclass softcap for cloth armor") RULE_INT(Combat, LeatherACSoftcap, 100, "If OldACSoftcapRules is true: armorclass softcap for leather armor") @@ -479,10 +442,7 @@ RULE_BOOL (Combat,TauntOverLevel, 1, "Allows you to taunt NPC's over warriors le RULE_REAL (Combat,TauntSkillFalloff, 0.33, "For every taunt skill point that's not maxed you lose this percentage chance to taunt") RULE_BOOL (Combat,EXPFromDmgShield, false, "Determine if damage from a damage shield counts for experience gain") RULE_INT(Combat, MonkACBonusWeight, 15, "Usually, a monk under this weight threshold gets an AC bonus") -RULE_INT(Combat, ClientStunLevel, 55, "This is the level where client kicks and bashes can stun the target") RULE_INT(Combat, QuiverHasteCap, 1000, "Quiver haste cap 1000 on live for a while, currently 700 on live") -RULE_BOOL(Combat, UseArcheryBonusRoll, false, "Make the 51+ archery bonus require an actual roll") -RULE_INT(Combat, ArcheryBonusChance, 50, "Chance for 50+ archery bonus damage if Combat:UseArcheryBonusRoll is true") RULE_INT(Combat, BerserkerFrenzyStart, 35, "Percentage Health Points below which Warrior and Berserker start frenzy") RULE_INT(Combat, BerserkerFrenzyEnd, 45, "Percentage Health Points above which Warrior and Berserker end frenzy") RULE_BOOL(Combat, OneProcPerWeapon, true, "If enabled, One proc per weapon per round") @@ -542,7 +502,6 @@ RULE_INT(Aggro, MeleeRangeAggroMod, 10, "Aggro increase against targets in melee RULE_INT(Aggro, CurrentTargetAggroMod, 0, "Aggro increase against current target. 0% = prefer the current target to any other. Makes it harder for our NPC to switch targets") RULE_INT(Aggro, CriticallyWoundedAggroMod, 100, "Aggro increase against critical wounded targets") RULE_INT(Aggro, SpellAggroMod, 100, "Aggro increase for spells") -RULE_INT(Aggro, SongAggroMod, 33, "Aggro increase for songs") RULE_INT(Aggro, PetSpellAggroMod, 10, "Aggro increase for pet spells") RULE_REAL(Aggro, TunnelVisionAggroMod, 0.75, "People not currently the top hate generate this much hate on a Tunnel Vision mob") RULE_INT(Aggro, MaxScalingProcAggro, 400, "Set to -1 for no limit. Maximum amount of aggro that HP scaling SPA effect in a proc will add") @@ -574,7 +533,6 @@ RULE_INT(Range, SpellParticles, 135, "The packet range in which spell particles RULE_INT(Range, DamageMessages, 50, "The packet range in which damage messages are sent (non-crit)") RULE_INT(Range, SpellMessages, 75, "The packet range in which spell damage messages are sent") RULE_INT(Range, SongMessages, 75, "The packet range in which song messages are sent") -RULE_INT(Range, MobPositionUpdates, 600, "The packet range in which mob position updates are sent") RULE_INT(Range, ClientPositionUpdates, 300, "Distance in which the own changed position is communicated to other clients") RULE_INT(Range, CriticalDamage, 80, "The packet range in which critical hit messages are sent") RULE_INT(Range, MobCloseScanDistance, 600, "Close scan distance") @@ -594,14 +552,9 @@ RULE_INT(Bots, HealRotationMaxTargets, 12, "Maximum number of heal rotation targ RULE_REAL(Bots, ManaRegen, 2.0, "Adjust mana regen for bots, 1 is fast and higher numbers slow it down 3 is about the same as players") RULE_BOOL(Bots, PreferNoManaCommandSpells, true, "Give sorting priority to newer no-mana spells (i.e., 'Bind Affinity')") RULE_BOOL(Bots, QuestableSpawnLimit, false, "Optional quest method to manage bot spawn limits using the quest_globals name bot_spawn_limit, see: /bazaar/Aediles_Thrall.pl") -RULE_BOOL(Bots, QuestableSpells, false, "Anita Thrall's (Anita_Thrall.pl) Bot Spell Scriber quests") RULE_INT(Bots, SpawnLimit, 71, "Number of bots a character can have spawned at one time, You + 71 bots is a 12 group pseudo-raid") -RULE_BOOL(Bots, UpdatePositionWithTimer, false, "Sends a position update with every positive movement timer check") -RULE_BOOL(Bots, UsePathing, true, "Bots will use node pathing when moving") RULE_BOOL(Bots, BotGroupXP, false, "Determines whether client gets experience for bots outside their group") -RULE_BOOL(Bots, BotBardUseOutOfCombatSongs, true, "Determines whether bard bots use additional out of combat songs (optional script)") RULE_BOOL(Bots, BotLevelsWithOwner, false, "Auto-updates spawned bots as owner levels/de-levels (false is original behavior)") -RULE_BOOL(Bots, BotCharacterLevelEnabled, false, "Enables required level to spawn bots") RULE_INT(Bots, BotCharacterLevel, 0, "If level is greater that value player can spawn bots if BotCharacterLevelEnabled is true") RULE_INT(Bots, CasterStopMeleeLevel, 13, "Level at which caster bots stop melee attacks") RULE_INT(Bots, AllowedClasses, 0xFFFFFFFF, "Bitmask of allowed bot classes") @@ -681,12 +634,10 @@ RULE_INT(Adventure, ItemIDToEnablePorts, 41000, "ItemID to enable adventure port RULE_INT(Adventure, LDoNTrapDistanceUse, 625, "LDoN trap distance use") RULE_REAL(Adventure, LDoNBaseTrapDifficulty, 15.0, "LDoN base trap difficulty") RULE_REAL(Adventure, LDoNCriticalFailTrapThreshold, 10.0, "LDoN critical fail trap threshold") -RULE_INT(Adventure, LDoNAdventureExpireTime, 1800, "LDoN adventure expire time (seconds)") RULE_CATEGORY_END() RULE_CATEGORY(AA) RULE_INT(AA, ExpPerPoint, 23976503, "Amount of experience per AA. Is the same as the amount of experience to go from level 51 to level 52") -RULE_BOOL(AA, Stacking, true, "Allow AA that belong to the same group to stack on SOF+ clients") RULE_BOOL(AA, NormalizedAAEnabled, false, "TSS+ change to AA that normalizes AA experience to a fixed # of white con kills independent of level") RULE_INT(AA, NormalizedAANumberOfWhiteConPerAA, 25, "The number of white con kills per AA point") RULE_BOOL(AA, ModernAAScalingEnabled, false, "Are we linearly scaling AA experience based on total # of earned AA?") @@ -718,8 +669,6 @@ RULE_BOOL(QueryServ, PlayerLogNPCKills, false, "Log player NPC kills") RULE_BOOL(QueryServ, PlayerLogDeletes, false, "Log player deletes") RULE_BOOL(QueryServ, PlayerLogMoves, false, "Log player moves") RULE_BOOL(QueryServ, PlayerLogMerchantTransactions, false, "Log merchant transactions") -RULE_BOOL(QueryServ, PlayerLogPCCoordinates, false, "Log player coordinates with certain events") -RULE_BOOL(QueryServ, PlayerLogDropItem, false, "Log player drop item") RULE_BOOL(QueryServ, PlayerLogZone, false, "Log player zone events") RULE_BOOL(QueryServ, PlayerLogDeaths, false, "Log player deaths") RULE_BOOL(QueryServ, PlayerLogConnectDisconnect, false, "Logs player connect/disconnect state") @@ -727,11 +676,9 @@ RULE_BOOL(QueryServ, PlayerLogLevels, false, "Log player leveling/deleveling") RULE_BOOL(QueryServ, PlayerLogAARate, false, "Log player AA experience rates") RULE_BOOL(QueryServ, PlayerLogQGlobalUpdate, false, "Log player QGlobal updates") RULE_BOOL(QueryServ, PlayerLogTaskUpdates, false, "Log player Task updates") -RULE_BOOL(QueryServ, PlayerLogKeyringAddition, false, "Log player keyring additions") RULE_BOOL(QueryServ, PlayerLogAAPurchases, false, "Log player AA purchases") RULE_BOOL(QueryServ, PlayerLogTradeSkillEvents, false, "Log player tradeskill transactions") RULE_BOOL(QueryServ, PlayerLogIssuedCommandes, false, "Log player issued commands") -RULE_BOOL(QueryServ, PlayerLogMoneyTransactions, false, "Log player money transaction/splits") RULE_BOOL(QueryServ, PlayerLogAlternateCurrencyTransactions, false, "Log player alternate currency transactions") RULE_CATEGORY_END() diff --git a/zone/mod_functions.cpp b/zone/mod_functions.cpp index b0b335a4e..b4951f76d 100644 --- a/zone/mod_functions.cpp +++ b/zone/mod_functions.cpp @@ -151,7 +151,7 @@ int32 Mob::mod_monk_special_damage(int32 ndamage, EQ::skills::SkillType skill_ty //ndamage - Backstab damage as calculated by default formulas int32 Mob::mod_backstab_damage(int32 ndamage) { return(ndamage); } -//Chance for 50+ archery bonus damage if Combat:UseArcheryBonusRoll is true. Base is Combat:ArcheryBonusChance +//Chance for 50+ archery bonus damage if Combat:UseArcheryBonusRoll is true. int Mob::mod_archery_bonus_chance(int bonuschance, const EQ::ItemInstance* RangeWeapon) { return(bonuschance); } //Archery bonus damage diff --git a/zone/mod_functions_base.cpp b/zone/mod_functions_base.cpp index 477050534..5323ca32a 100644 --- a/zone/mod_functions_base.cpp +++ b/zone/mod_functions_base.cpp @@ -152,7 +152,7 @@ int32 Mob::mod_monk_special_damage(int32 ndamage, SkillType skill_type) { return //ndamage - Backstab damage as calculated by default formulas int32 Mob::mod_backstab_damage(int32 ndamage) { return(ndamage); } -//Chance for 50+ archery bonus damage if Combat:UseArcheryBonusRoll is true. Base is Combat:ArcheryBonusChance +//Chance for 50+ archery bonus damage if Combat:UseArcheryBonusRoll is true. int Mob::mod_archery_bonus_chance(int bonuschance, const ItemInst* RangeWeapon) { return(bonuschance); } //Archery bonus damage From ba64d6f4946de906d2c1ebeece10ed3ec18a8cd4 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 28 Mar 2021 20:04:34 -0500 Subject: [PATCH 005/624] [Installer] Swap unstable powershell download for Perl LWP::Simple call --- utils/scripts/eqemu_server.pl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl index 4c510ff9f..83688ad2a 100755 --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -705,7 +705,8 @@ sub get_windows_wget { if (!-d "bin") { mkdir("bin"); } - `powershell -Command "(New-Object Net.WebClient).DownloadFile('https://raw.githubusercontent.com/Akkadius/eqemu-install-v2/master/windows/wget.exe', 'bin\\wget.exe') "` + eval "use LWP::Simple qw(getstore);"; + getstore("https://raw.githubusercontent.com/Akkadius/eqemu-install-v2/master/windows/wget.exe", "bin\\wget.exe"); } } From 97c11a11995e53909a1cc3c9a3b5d161c25f80c1 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 28 Mar 2021 21:25:50 -0400 Subject: [PATCH 006/624] [Quest API] Add new zone name methods to Perl and Lua. (#1309) - Add quest::GetZoneShortName(zone_id) to Perl. - Add quest::GetZoneLongNameByID(zone_id) to Perl. - Add eq.get_zone_id_by_name(zone_name) to Lua. - Add eq.get_zone_short_name_by_id(zone_id) to Lua. - Add eq.get_zone_long_name_by_id(zone_id) to Lua. - Add eq.get_zone_long_name_by_name(zone_name) to Lua. --- zone/embparser_api.cpp | 32 ++++++++++++++++++++++++++++++++ zone/lua_general.cpp | 22 ++++++++++++++++++++++ zone/questmgr.cpp | 12 +++++++++--- zone/questmgr.h | 2 ++ 4 files changed, 65 insertions(+), 3 deletions(-) diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 2ffc56c23..1ed0df341 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -3821,6 +3821,36 @@ XS(XS__GetZoneLongName) { XSRETURN(1); } +XS(XS__GetZoneLongNameByID); +XS(XS__GetZoneLongNameByID) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::GetZoneLongNameByID(uint32 zone_id)"); + + dXSTARG; + uint32 zone_id = (uint32) SvUV(ST(0)); + std::string zone_long_name = quest_manager.GetZoneLongNameByID(zone_id); + sv_setpv(TARG, zone_long_name.c_str()); + XSprePUSH; + PUSHTARG; + XSRETURN(1); +} + +XS(XS__GetZoneShortName); +XS(XS__GetZoneShortName) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::GetZoneShortName(uint32 zone_id)"); + + dXSTARG; + uint32 zone_id = (uint32) SvUV(ST(0)); + std::string zone_short_name = quest_manager.GetZoneShortName(zone_id); + sv_setpv(TARG, zone_short_name.c_str()); + XSprePUSH; + PUSHTARG; + XSRETURN(1); +} + XS(XS__GetTimeSeconds); XS(XS__GetTimeSeconds) { dXSARGS; @@ -6490,6 +6520,8 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "GetTimeSeconds"), XS__GetTimeSeconds, file); newXS(strcpy(buf, "GetZoneID"), XS__GetZoneID, file); newXS(strcpy(buf, "GetZoneLongName"), XS__GetZoneLongName, file); + newXS(strcpy(buf, "GetZoneLongNameByID"), XS__GetZoneLongNameByID, file); + newXS(strcpy(buf, "GetZoneShortName"), XS__GetZoneShortName, file); newXS(strcpy(buf, "set_rule"), XS__set_rule, file); newXS(strcpy(buf, "get_rule"), XS__get_rule, file); newXS(strcpy(buf, "get_data"), XS__get_data, file); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index a4fd77449..668028eb8 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -1608,6 +1608,10 @@ int lua_get_zone_id() { return zone->GetZoneID(); } +int lua_get_zone_id_by_name(const char* zone_name) { + return ZoneID(zone_name); +} + const char *lua_get_zone_long_name() { if(!zone) return ""; @@ -1615,6 +1619,16 @@ const char *lua_get_zone_long_name() { return zone->GetLongName(); } +const char *lua_get_zone_long_name_by_name(const char* zone_name) { + return ZoneLongName( + ZoneID(zone_name) + ); +} + +const char *lua_get_zone_long_name_by_id(uint32 zone_id) { + return ZoneLongName(zone_id); +} + const char *lua_get_zone_short_name() { if(!zone) return ""; @@ -1622,6 +1636,10 @@ const char *lua_get_zone_short_name() { return zone->GetShortName(); } +const char *lua_get_zone_short_name_by_id(uint32 zone_id) { + return ZoneName(zone_id); +} + int lua_get_zone_instance_id() { if(!zone) return 0; @@ -2806,8 +2824,12 @@ luabind::scope lua_register_general() { luabind::def("zone_group", &lua_zone_group), luabind::def("zone_raid", &lua_zone_raid), luabind::def("get_zone_id", &lua_get_zone_id), + luabind::def("get_zone_id_by_name", &lua_get_zone_id_by_name), luabind::def("get_zone_long_name", &lua_get_zone_long_name), + luabind::def("get_zone_long_name_by_name", &lua_get_zone_long_name_by_name), + luabind::def("get_zone_long_name_by_id", &lua_get_zone_long_name_by_id), luabind::def("get_zone_short_name", &lua_get_zone_short_name), + luabind::def("get_zone_short_name_by_id", &lua_get_zone_short_name_by_id), luabind::def("get_zone_instance_id", &lua_get_zone_instance_id), luabind::def("get_zone_instance_version", &lua_get_zone_instance_version), luabind::def("get_zone_weather", &lua_get_zone_weather), diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 3ffc505dd..fc3e27239 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -3219,9 +3219,15 @@ int32 QuestManager::GetZoneID(const char *zone) { std::string QuestManager::GetZoneLongName(std::string zone_short_name) { - return zone_store.GetZoneLongName( - zone_store.GetZoneID(zone_short_name) - ); + return ZoneLongName(ZoneID(zone_short_name)); +} + +std::string QuestManager::GetZoneLongNameByID(uint32 zone_id) { + return ZoneLongName(zone_id); +} + +std::string QuestManager::GetZoneShortName(uint32 zone_id) { + return ZoneName(zone_id); } void QuestManager::CrossZoneAssignTaskByCharID(int character_id, uint32 task_id, bool enforce_level_requirement) { diff --git a/zone/questmgr.h b/zone/questmgr.h index b5d2adfe2..0bb765fed 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -285,6 +285,8 @@ public: uint16 CreateDoor( const char* model, float x, float y, float z, float heading, uint8 opentype, uint16 size); int32 GetZoneID(const char *zone); static std::string GetZoneLongName(std::string zone_short_name); + static std::string GetZoneLongNameByID(uint32 zone_id); + static std::string GetZoneShortName(uint32 zone_id); void CrossZoneAssignTaskByCharID(int character_id, uint32 task_id, bool enforce_level_requirement = false); void CrossZoneAssignTaskByGroupID(int group_id, uint32 task_id, bool enforce_level_requirement = false); void CrossZoneAssignTaskByRaidID(int raid_id, uint32 task_id, bool enforce_level_requirement = false); From f5cf566fcae1163ac1c85f6a7ad0bd484ec4448c Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Sun, 28 Mar 2021 21:43:09 -0400 Subject: [PATCH 007/624] [Expeditions] Let dz process its expired state (#1310) Move early empty shutdown and process rate rules to DynamicZone scope This decouples the expired status check from expeditions into an internal dz method that can be called by its owning system --- common/ruletypes.h | 6 +++--- world/dynamic_zone.cpp | 24 ++++++++++++++++++++++++ world/dynamic_zone.h | 10 ++++++++++ world/expedition.cpp | 18 ++++++++++++++++++ world/expedition.h | 4 +--- world/expedition_state.cpp | 35 +++-------------------------------- world/expedition_state.h | 2 +- 7 files changed, 60 insertions(+), 39 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 401e55099..4edda9277 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -736,9 +736,6 @@ RULE_CATEGORY_END() RULE_CATEGORY(Expedition) RULE_INT(Expedition, MinStatusToBypassPlayerCountRequirements, 80, "Minimum GM status to bypass minimum player requirements for Expedition creation") -RULE_BOOL(Expedition, EmptyDzShutdownEnabled, true, "Enable early instance shutdown after last member of expedition removed") -RULE_INT(Expedition, EmptyDzShutdownDelaySeconds, 1500, "Seconds to set dynamic zone instance expiration if early shutdown enabled") -RULE_INT(Expedition, WorldExpeditionProcessRateMS, 6000, "Timer interval (milliseconds) that world checks expedition states") RULE_BOOL(Expedition, AlwaysNotifyNewLeaderOnChange, false, "Always notify clients when made expedition leader. If false (live-like) new leaders are only notified when made leader via /dzmakeleader") RULE_REAL(Expedition, LockoutDurationMultiplier, 1.0, "Multiplies lockout duration by this value when new lockouts are added") RULE_BOOL(Expedition, EnableInDynamicZoneStatus, false, "Enables the 'In Dynamic Zone' member status in expedition window. If false (live-like) players inside the dynamic zone will show as 'Online'") @@ -747,6 +744,9 @@ RULE_CATEGORY_END() RULE_CATEGORY(DynamicZone) RULE_INT(DynamicZone, ClientRemovalDelayMS, 60000, "Delay (milliseconds) until a client is teleported out of dynamic zone after being removed as member") +RULE_BOOL(DynamicZone, EmptyShutdownEnabled, true, "Enable early instance shutdown for dynamic zones that have no members") +RULE_INT(DynamicZone, EmptyShutdownDelaySeconds, 1500, "Seconds to set dynamic zone instance expiration if early shutdown enabled") +RULE_INT(DynamicZone, WorldProcessRate, 6000, "Timer interval (milliseconds) that systems check their dynamic zone states") RULE_CATEGORY_END() #undef RULE_CATEGORY diff --git a/world/dynamic_zone.cpp b/world/dynamic_zone.cpp index 47e70d00a..252559c29 100644 --- a/world/dynamic_zone.cpp +++ b/world/dynamic_zone.cpp @@ -35,6 +35,30 @@ DynamicZone* DynamicZone::FindDynamicZoneByID(uint32_t dz_id) return nullptr; } +DynamicZoneStatus DynamicZone::Process(bool force_expire) +{ + DynamicZoneStatus status = DynamicZoneStatus::Normal; + + if (force_expire || IsExpired()) + { + status = DynamicZoneStatus::Expired; + + auto dz_zoneserver = zoneserver_list.FindByInstanceID(GetInstanceID()); + if (!dz_zoneserver || dz_zoneserver->NumPlayers() == 0) // no clients inside dz + { + status = DynamicZoneStatus::ExpiredEmpty; + + if (force_expire && !m_is_pending_early_shutdown && RuleB(DynamicZone, EmptyShutdownEnabled)) + { + SetSecondsRemaining(RuleI(DynamicZone, EmptyShutdownDelaySeconds)); + m_is_pending_early_shutdown = true; + } + } + } + + return status; +} + void DynamicZone::SetSecondsRemaining(uint32_t seconds_remaining) { auto now = std::chrono::system_clock::now(); diff --git a/world/dynamic_zone.h b/world/dynamic_zone.h index c95512260..84455d7eb 100644 --- a/world/dynamic_zone.h +++ b/world/dynamic_zone.h @@ -6,6 +6,14 @@ class ServerPacket; +enum class DynamicZoneStatus +{ + Unknown = 0, + Normal, + Expired, + ExpiredEmpty, +}; + class DynamicZone { public: @@ -23,6 +31,7 @@ public: std::chrono::system_clock::duration GetRemainingDuration() const { return m_expire_time - std::chrono::system_clock::now(); } + DynamicZoneStatus Process(bool force_expire); bool IsExpired() const { return m_expire_time < std::chrono::system_clock::now(); } void SetSecondsRemaining(uint32_t seconds_remaining); @@ -33,6 +42,7 @@ private: uint32_t m_instance_id = 0; uint32_t m_zone_id = 0; uint32_t m_zone_version = 0; + bool m_is_pending_early_shutdown = false; DynamicZoneType m_type{ DynamicZoneType::None }; std::chrono::seconds m_duration; std::chrono::time_point m_start_time; diff --git a/world/expedition.cpp b/world/expedition.cpp index d983a82d0..2937a20bc 100644 --- a/world/expedition.cpp +++ b/world/expedition.cpp @@ -161,3 +161,21 @@ void Expedition::CheckLeader() ChooseNewLeader(); } } + +bool Expedition::Process() +{ + // returns true if expedition needs to be deleted from world cache and db + // expedition is not deleted until its dz has no clients to prevent exploits + auto status = m_dynamic_zone.Process(IsEmpty()); // force expire if no members + if (status == DynamicZoneStatus::ExpiredEmpty) + { + LogExpeditions("Expedition [{}] expired or empty, notifying zones and deleting", GetID()); + SendZonesExpeditionDeleted(); + return true; + } + + CheckExpireWarning(); + CheckLeader(); + + return false; +} diff --git a/world/expedition.h b/world/expedition.h index 952abc1a0..2f48739df 100644 --- a/world/expedition.h +++ b/world/expedition.h @@ -43,19 +43,17 @@ public: uint32_t GetID() const { return m_expedition_id; } bool HasMember(uint32_t character_id); bool IsEmpty() const { return m_member_ids.empty(); } - bool IsPendingDelete() const { return m_pending_delete; } bool IsValid() const { return m_expedition_id != 0; } + bool Process(); void SendZonesExpeditionDeleted(); void SendZonesExpireWarning(uint32_t minutes_remaining); bool SetNewLeader(uint32_t new_leader_id); - void SetPendingDelete(bool pending) { m_pending_delete = pending; } private: void SendZonesLeaderChanged(); uint32_t m_expedition_id = 0; uint32_t m_leader_id = 0; - bool m_pending_delete = false; bool m_choose_leader_needed = false; Timer m_choose_leader_cooldown_timer; Timer m_warning_cooldown_timer; diff --git a/world/expedition_state.cpp b/world/expedition_state.cpp index c1cb0fc8c..815ef6c58 100644 --- a/world/expedition_state.cpp +++ b/world/expedition_state.cpp @@ -21,13 +21,9 @@ #include "expedition_state.h" #include "expedition.h" #include "expedition_database.h" -#include "zonelist.h" -#include "zoneserver.h" #include "../common/eqemu_logsys.h" #include -extern ZSList zoneserver_list; - ExpeditionState expedition_state; Expedition* ExpeditionState::GetExpedition(uint32_t expedition_id) @@ -117,36 +113,11 @@ void ExpeditionState::Process() for (auto it = m_expeditions.begin(); it != m_expeditions.end();) { - bool is_deleted = false; - - if (it->IsEmpty() || it->GetDynamicZone().IsExpired()) + bool is_deleted = it->Process(); + if (is_deleted) { - // don't delete expedition until its dz instance is empty. this prevents - // an exploit where all members leave expedition and complete an event - // before being kicked from removal timer. the lockout could never be - // applied because the zone expedition cache was already invalidated. - auto dz_zoneserver = zoneserver_list.FindByInstanceID(it->GetDynamicZone().GetInstanceID()); - if (!dz_zoneserver || dz_zoneserver->NumPlayers() == 0) - { - LogExpeditions("Expedition [{}] expired or empty, notifying zones and deleting", it->GetID()); - expedition_ids.emplace_back(it->GetID()); - it->SendZonesExpeditionDeleted(); - is_deleted = true; - } - - if (it->IsEmpty() && !it->IsPendingDelete() && RuleB(Expedition, EmptyDzShutdownEnabled)) - { - it->GetDynamicZone().SetSecondsRemaining(RuleI(Expedition, EmptyDzShutdownDelaySeconds)); - } - - it->SetPendingDelete(true); + expedition_ids.emplace_back(it->GetID()); } - else - { - it->CheckExpireWarning(); - it->CheckLeader(); - } - it = is_deleted ? m_expeditions.erase(it) : it + 1; } diff --git a/world/expedition_state.h b/world/expedition_state.h index 58b733989..d7fb27d09 100644 --- a/world/expedition_state.h +++ b/world/expedition_state.h @@ -44,7 +44,7 @@ public: private: std::vector m_expeditions; - Timer m_process_throttle_timer{static_cast(RuleI(Expedition, WorldExpeditionProcessRateMS))}; + Timer m_process_throttle_timer{static_cast(RuleI(DynamicZone, WorldProcessRate))}; }; #endif From 049fe55c7fa0438e370be0a8b5ada56cea7fe759 Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Mon, 29 Mar 2021 03:17:36 -0400 Subject: [PATCH 008/624] [Expeditions] Create common dz abstract class (#1312) This creates an abstract class in common so zone and world can share most of the implementation. World now has access to the same dz data and api as zone. Rename CharacterChange to AddRemoveCharacter for clarity Rename GetRemainingDuration to GetDurationRemaining for consistency Move dynamic zone queries to custom repository methods --- common/CMakeLists.txt | 2 + common/dynamic_zone_base.cpp | 289 ++++++++++ common/dynamic_zone_base.h | 112 ++++ .../repositories/dynamic_zones_repository.h | 235 ++++++++ common/servertalk.h | 2 +- world/dynamic_zone.cpp | 68 ++- world/dynamic_zone.h | 31 +- world/expedition.cpp | 2 +- world/zoneserver.cpp | 2 +- zone/client.cpp | 2 +- zone/command.cpp | 75 +-- zone/dynamic_zone.cpp | 512 ++---------------- zone/dynamic_zone.h | 97 +--- zone/expedition.cpp | 5 +- zone/expedition.h | 1 - zone/worldserver.cpp | 2 +- 16 files changed, 814 insertions(+), 623 deletions(-) create mode 100644 common/dynamic_zone_base.cpp create mode 100644 common/dynamic_zone_base.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 67cf4433c..d0611ae65 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -16,6 +16,7 @@ SET(common_sources database_instances.cpp dbcore.cpp deity.cpp + dynamic_zone_base.cpp emu_constants.cpp emu_limits.cpp emu_opcodes.cpp @@ -466,6 +467,7 @@ SET(common_headers database_schema.h dbcore.h deity.h + dynamic_zone_base.h emu_constants.h emu_limits.h emu_opcodes.h diff --git a/common/dynamic_zone_base.cpp b/common/dynamic_zone_base.cpp new file mode 100644 index 000000000..8e29722d3 --- /dev/null +++ b/common/dynamic_zone_base.cpp @@ -0,0 +1,289 @@ +#include "dynamic_zone_base.h" +#include "database.h" +#include "eqemu_logsys.h" +#include "repositories/instance_list_repository.h" +#include "repositories/instance_list_player_repository.h" +#include "servertalk.h" + +DynamicZoneBase::DynamicZoneBase(DynamicZonesRepository::DynamicZoneInstance&& entry) +{ + LoadRepositoryResult(std::move(entry)); +} + +uint32_t DynamicZoneBase::Create() +{ + if (m_id != 0) + { + return m_id; + } + + if (GetInstanceID() == 0) + { + CreateInstance(); + } + + m_id = SaveToDatabase(); + + return m_id; +} + +uint32_t DynamicZoneBase::CreateInstance() +{ + if (m_instance_id) + { + LogDynamicZones("CreateInstance failed, instance id [{}] already created", m_instance_id); + return 0; + } + + if (!m_zone_id) + { + LogDynamicZones("CreateInstance failed, invalid zone id [{}]", m_zone_id); + return 0; + } + + uint16_t unused_instance_id = 0; + if (!GetDatabase().GetUnusedInstanceID(unused_instance_id)) // todo: doesn't this race with insert? + { + LogDynamicZones("Failed to find unused instance id"); + return 0; + } + + m_start_time = std::chrono::system_clock::now(); + m_expire_time = m_start_time + m_duration; + + auto insert_instance = InstanceListRepository::NewEntity(); + insert_instance.id = unused_instance_id; + insert_instance.zone = m_zone_id; + insert_instance.version = m_zone_version; + insert_instance.start_time = static_cast(std::chrono::system_clock::to_time_t(m_start_time)); + insert_instance.duration = static_cast(m_duration.count()); + insert_instance.never_expires = m_never_expires; + + auto instance = InstanceListRepository::InsertOne(GetDatabase(), insert_instance); + if (instance.id == 0) + { + LogDynamicZones("Failed to create instance [{}] for zone [{}]", unused_instance_id, m_zone_id); + return 0; + } + + m_instance_id = instance.id; + + return m_instance_id; +} + +void DynamicZoneBase::LoadRepositoryResult(DynamicZonesRepository::DynamicZoneInstance&& dz_entry) +{ + m_id = dz_entry.id; + m_instance_id = dz_entry.instance_id; + m_type = static_cast(dz_entry.type); + m_compass.zone_id = dz_entry.compass_zone_id; + m_compass.x = dz_entry.compass_x; + m_compass.y = dz_entry.compass_y; + m_compass.z = dz_entry.compass_z; + m_safereturn.zone_id = dz_entry.safe_return_zone_id; + m_safereturn.x = dz_entry.safe_return_x; + m_safereturn.y = dz_entry.safe_return_y; + m_safereturn.z = dz_entry.safe_return_z; + m_safereturn.heading = dz_entry.safe_return_heading; + m_zonein.x = dz_entry.zone_in_x; + m_zonein.y = dz_entry.zone_in_y; + m_zonein.z = dz_entry.zone_in_z; + m_zonein.heading = dz_entry.zone_in_heading; + m_has_zonein = (dz_entry.has_zone_in != 0); + // instance_list portion + m_zone_id = dz_entry.zone; + m_zone_version = dz_entry.version; + m_start_time = std::chrono::system_clock::from_time_t(dz_entry.start_time); + m_duration = std::chrono::seconds(dz_entry.duration); + m_never_expires = (dz_entry.never_expires != 0); + m_expire_time = m_start_time + m_duration; +} + +uint32_t DynamicZoneBase::SaveToDatabase() +{ + LogDynamicZonesDetail("Saving dz instance [{}] to database", m_instance_id); + + if (m_instance_id != 0) + { + auto insert_dz = DynamicZonesRepository::NewEntity(); + insert_dz.instance_id = m_instance_id, + insert_dz.type = static_cast(m_type); + insert_dz.compass_zone_id = m_compass.zone_id; + insert_dz.compass_x = m_compass.x; + insert_dz.compass_y = m_compass.y; + insert_dz.compass_z = m_compass.z; + insert_dz.safe_return_zone_id = m_safereturn.zone_id; + insert_dz.safe_return_x = m_safereturn.x; + insert_dz.safe_return_y = m_safereturn.y; + insert_dz.safe_return_z = m_safereturn.z; + insert_dz.safe_return_heading = m_safereturn.heading; + insert_dz.zone_in_x = m_zonein.x; + insert_dz.zone_in_y = m_zonein.y; + insert_dz.zone_in_z = m_zonein.z; + insert_dz.zone_in_heading = m_zonein.heading; + insert_dz.has_zone_in = m_has_zonein; + + auto inserted_dz = DynamicZonesRepository::InsertOne(GetDatabase(), insert_dz); + return inserted_dz.id; + } + return 0; +} + +void DynamicZoneBase::AddCharacter(uint32_t character_id) +{ + GetDatabase().AddClientToInstance(m_instance_id, character_id); + SendInstanceAddRemoveCharacter(character_id, false); // stops client kick timer +} + +void DynamicZoneBase::RemoveCharacter(uint32_t character_id) +{ + GetDatabase().RemoveClientFromInstance(m_instance_id, character_id); + SendInstanceAddRemoveCharacter(character_id, true); // start client kick timer +} + +void DynamicZoneBase::RemoveAllCharacters(bool enable_removal_timers) +{ + if (GetInstanceID() == 0) + { + return; + } + + if (enable_removal_timers) + { + SendInstanceRemoveAllCharacters(); + } + + GetDatabase().RemoveClientsFromInstance(GetInstanceID()); +} + +void DynamicZoneBase::SaveInstanceMembersToDatabase(const std::vector& character_ids) +{ + LogDynamicZonesDetail("Saving [{}] members for instance [{}]", character_ids.size(), m_instance_id); + + std::vector insert_players; + + for (const auto& character_id : character_ids) + { + InstanceListPlayerRepository::InstanceListPlayer entry{}; + entry.id = static_cast(m_instance_id); + entry.charid = static_cast(character_id); + insert_players.emplace_back(entry); + } + + InstanceListPlayerRepository::InsertMany(GetDatabase(), insert_players); +} + +void DynamicZoneBase::SetCompass(const DynamicZoneLocation& location, bool update_db) +{ + ProcessCompassChange(location); + + if (update_db) + { + LogDynamicZonesDetail("Saving [{}] compass zone: [{}] xyz: ([{}], [{}], [{}])", + m_id, m_compass.zone_id, m_compass.x, m_compass.y, m_compass.z); + + DynamicZonesRepository::UpdateCompass(GetDatabase(), + m_id, m_compass.zone_id, m_compass.x, m_compass.y, m_compass.z); + + SendGlobalLocationChange(ServerOP_DzSetCompass, location); + } +} + +void DynamicZoneBase::SetCompass(uint32_t zone_id, float x, float y, float z, bool update_db) +{ + SetCompass({ zone_id, x, y, z, 0.0f }, update_db); +} + +void DynamicZoneBase::SetSafeReturn(const DynamicZoneLocation& location, bool update_db) +{ + m_safereturn = location; + + if (update_db) + { + LogDynamicZonesDetail("Saving [{}] safereturn zone: [{}] xyzh: ([{}], [{}], [{}], [{}])", + m_id, m_safereturn.zone_id, m_safereturn.x, m_safereturn.y, m_safereturn.z, m_safereturn.heading); + + DynamicZonesRepository::UpdateSafeReturn(GetDatabase(), m_id, m_safereturn.zone_id, + m_safereturn.x, m_safereturn.y, m_safereturn.z, m_safereturn.heading); + + SendGlobalLocationChange(ServerOP_DzSetSafeReturn, location); + } +} + +void DynamicZoneBase::SetSafeReturn(uint32_t zone_id, float x, float y, float z, float heading, bool update_db) +{ + SetSafeReturn({ zone_id, x, y, z, heading }, update_db); +} + +void DynamicZoneBase::SetZoneInLocation(const DynamicZoneLocation& location, bool update_db) +{ + m_zonein = location; + m_has_zonein = true; + + if (update_db) + { + LogDynamicZonesDetail("Saving [{}] zonein zone: [{}] xyzh: ([{}], [{}], [{}], [{}])", + m_id, m_zone_id, m_zonein.x, m_zonein.y, m_zonein.z, m_zonein.heading); + + DynamicZonesRepository::UpdateZoneIn(GetDatabase(), m_id, m_zone_id, + m_zonein.x, m_zonein.y, m_zonein.z, m_zonein.heading, m_has_zonein); + + SendGlobalLocationChange(ServerOP_DzSetZoneIn, location); + } +} + +void DynamicZoneBase::SetZoneInLocation(float x, float y, float z, float heading, bool update_db) +{ + SetZoneInLocation({ 0, x, y, z, heading }, update_db); +} + +uint32_t DynamicZoneBase::GetSecondsRemaining() const +{ + auto remaining = std::chrono::duration_cast(GetDurationRemaining()).count(); + return std::max(0, static_cast(remaining)); +} + +std::unique_ptr DynamicZoneBase::CreateServerAddRemoveCharacterPacket( + uint32_t character_id, bool removed) +{ + constexpr uint32_t pack_size = sizeof(ServerDzCharacter_Struct); + auto pack = std::make_unique(ServerOP_DzAddRemoveCharacter, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + buf->zone_id = GetZoneID(); + buf->instance_id = GetInstanceID(); + buf->remove = removed; + buf->character_id = character_id; + + return pack; +} + +std::unique_ptr DynamicZoneBase::CreateServerRemoveAllCharactersPacket() +{ + constexpr uint32_t pack_size = sizeof(ServerDzCharacter_Struct); + auto pack = std::make_unique(ServerOP_DzRemoveAllCharacters, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + buf->zone_id = GetZoneID(); + buf->instance_id = GetInstanceID(); + buf->remove = true; + buf->character_id = 0; + + return pack; +} + +std::unique_ptr DynamicZoneBase::CreateServerDzLocationPacket( + uint16_t server_opcode, const DynamicZoneLocation& location) +{ + constexpr uint32_t pack_size = sizeof(ServerDzLocation_Struct); + auto pack = std::make_unique(server_opcode, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + buf->dz_id = GetID(); + buf->sender_zone_id = GetCurrentZoneID(); + buf->sender_instance_id = GetCurrentInstanceID(); + buf->zone_id = location.zone_id; + buf->x = location.x; + buf->y = location.y; + buf->z = location.z; + buf->heading = location.heading; + + return pack; +} diff --git a/common/dynamic_zone_base.h b/common/dynamic_zone_base.h new file mode 100644 index 000000000..2c157b2b5 --- /dev/null +++ b/common/dynamic_zone_base.h @@ -0,0 +1,112 @@ +#ifndef COMMON_DYNAMIC_ZONE_BASE_H +#define COMMON_DYNAMIC_ZONE_BASE_H + +#include "eq_constants.h" +#include "repositories/dynamic_zones_repository.h" +#include +#include +#include +#include +#include +#include + +class Database; +class ServerPacket; + +struct DynamicZoneLocation +{ + uint32_t zone_id = 0; + float x = 0.0f; + float y = 0.0f; + float z = 0.0f; + float heading = 0.0f; + + DynamicZoneLocation() = default; + DynamicZoneLocation(uint32_t zone_id_, float x_, float y_, float z_, float heading_) + : zone_id(zone_id_), x(x_), y(y_), z(z_), heading(heading_) {} +}; + +class DynamicZoneBase +{ +public: + virtual ~DynamicZoneBase() = default; + DynamicZoneBase(const DynamicZoneBase&) = default; + DynamicZoneBase(DynamicZoneBase&&) = default; + DynamicZoneBase& operator=(const DynamicZoneBase&) = default; + DynamicZoneBase& operator=(DynamicZoneBase&&) = default; + DynamicZoneBase() = default; + DynamicZoneBase(uint32_t dz_id) : m_id(dz_id) {} + DynamicZoneBase(DynamicZoneType type) : m_type(type) {} + DynamicZoneBase(DynamicZonesRepository::DynamicZoneInstance&& entry); + + virtual void SetSecondsRemaining(uint32_t seconds_remaining) = 0; + + uint64_t GetExpireTime() const { return std::chrono::system_clock::to_time_t(m_expire_time); } + uint32_t GetID() const { return m_id; } + uint16_t GetInstanceID() const { return static_cast(m_instance_id); } + uint32_t GetSecondsRemaining() const; + uint16_t GetZoneID() const { return static_cast(m_zone_id); } + uint32_t GetZoneIndex() const { return (m_instance_id << 16) | (m_zone_id & 0xffff); } + uint32_t GetZoneVersion() const { return m_zone_version; } + DynamicZoneType GetType() const { return m_type; } + const std::string& GetLeaderName() const { return m_leader_name; } + const std::string& GetName() const { return m_name; } + const DynamicZoneLocation& GetCompassLocation() const { return m_compass; } + const DynamicZoneLocation& GetSafeReturnLocation() const { return m_safereturn; } + const DynamicZoneLocation& GetZoneInLocation() const { return m_zonein; } + std::chrono::system_clock::duration GetDurationRemaining() const { return m_expire_time - std::chrono::system_clock::now(); } + + void AddCharacter(uint32_t character_id); + uint32_t Create(); + bool HasZoneInLocation() const { return m_has_zonein; } + bool IsExpired() const { return m_expire_time < std::chrono::system_clock::now(); } + bool IsInstanceID(uint32_t instance_id) const { return (m_instance_id != 0 && m_instance_id == instance_id); } + bool IsValid() const { return m_instance_id != 0; } + bool IsSameDz(uint32_t zone_id, uint32_t instance_id) const { return zone_id == m_zone_id && instance_id == m_instance_id; } + void RemoveAllCharacters(bool enable_removal_timers = true); + void RemoveCharacter(uint32_t character_id); + void SaveInstanceMembersToDatabase(const std::vector& character_ids); + void SetCompass(const DynamicZoneLocation& location, bool update_db = false); + void SetCompass(uint32_t zone_id, float x, float y, float z, bool update_db = false); + void SetLeaderName(const std::string& leader_name) { m_leader_name = leader_name; } + void SetName(const std::string& name) { m_name = name; } + void SetSafeReturn(const DynamicZoneLocation& location, bool update_db = false); + void SetSafeReturn(uint32_t zone_id, float x, float y, float z, float heading, bool update_db = false); + void SetZoneInLocation(const DynamicZoneLocation& location, bool update_db = false); + void SetZoneInLocation(float x, float y, float z, float heading, bool update_db = false); + +protected: + virtual uint16_t GetCurrentInstanceID() { return 0; } + virtual uint16_t GetCurrentZoneID() { return 0; } + virtual Database& GetDatabase() = 0; + virtual void ProcessCompassChange(const DynamicZoneLocation& location) { m_compass = location; } + virtual void SendInstanceAddRemoveCharacter(uint32_t character_id, bool remove) = 0; + virtual void SendInstanceRemoveAllCharacters() = 0; + virtual void SendGlobalLocationChange(uint16_t server_opcode, const DynamicZoneLocation& location) = 0; + + uint32_t CreateInstance(); + void LoadRepositoryResult(DynamicZonesRepository::DynamicZoneInstance&& dz_entry); + uint32_t SaveToDatabase(); + + std::unique_ptr CreateServerAddRemoveCharacterPacket(uint32_t character_id, bool removed); + std::unique_ptr CreateServerRemoveAllCharactersPacket(); + std::unique_ptr CreateServerDzLocationPacket(uint16_t server_opcode, const DynamicZoneLocation& location); + + uint32_t m_id = 0; + uint32_t m_zone_id = 0; + uint32_t m_instance_id = 0; + uint32_t m_zone_version = 0; + bool m_never_expires = false; + bool m_has_zonein = false; + std::string m_name; + std::string m_leader_name; + DynamicZoneType m_type{ DynamicZoneType::None }; + DynamicZoneLocation m_compass; + DynamicZoneLocation m_safereturn; + DynamicZoneLocation m_zonein; + std::chrono::seconds m_duration; + std::chrono::time_point m_start_time; + std::chrono::time_point m_expire_time; +}; + +#endif diff --git a/common/repositories/dynamic_zones_repository.h b/common/repositories/dynamic_zones_repository.h index 03435457c..27fd406ca 100644 --- a/common/repositories/dynamic_zones_repository.h +++ b/common/repositories/dynamic_zones_repository.h @@ -65,6 +65,241 @@ public: // Custom extended repository methods here + struct DynamicZoneInstance + { + uint32_t id; + int instance_id; + int type; + int compass_zone_id; + float compass_x; + float compass_y; + float compass_z; + int safe_return_zone_id; + float safe_return_x; + float safe_return_y; + float safe_return_z; + float safe_return_heading; + float zone_in_x; + float zone_in_y; + float zone_in_z; + float zone_in_heading; + int has_zone_in; + int zone; + int version; + int is_global; + uint32_t start_time; + int duration; + int never_expires; + }; + + static std::string SelectDynamicZoneJoinInstance() + { + return std::string(SQL( + SELECT + dynamic_zones.id, + dynamic_zones.instance_id, + dynamic_zones.type, + dynamic_zones.compass_zone_id, + dynamic_zones.compass_x, + dynamic_zones.compass_y, + dynamic_zones.compass_z, + dynamic_zones.safe_return_zone_id, + dynamic_zones.safe_return_x, + dynamic_zones.safe_return_y, + dynamic_zones.safe_return_z, + dynamic_zones.safe_return_heading, + dynamic_zones.zone_in_x, + dynamic_zones.zone_in_y, + dynamic_zones.zone_in_z, + dynamic_zones.zone_in_heading, + dynamic_zones.has_zone_in, + instance_list.zone, + instance_list.version, + instance_list.is_global, + instance_list.start_time, + instance_list.duration, + instance_list.never_expires + FROM dynamic_zones + INNER JOIN instance_list ON dynamic_zones.instance_id = instance_list.id + )); + } + + static DynamicZoneInstance FillWithInstanceFromRow(MySQLRequestRow& row) + { + DynamicZoneInstance entry{}; + + int col = 0; + entry.id = strtoul(row[col++], nullptr, 10); + entry.instance_id = strtol(row[col++], nullptr, 10); + entry.type = strtol(row[col++], nullptr, 10); + entry.compass_zone_id = strtol(row[col++], nullptr, 10); + entry.compass_x = strtof(row[col++], nullptr); + entry.compass_y = strtof(row[col++], nullptr); + entry.compass_z = strtof(row[col++], nullptr); + entry.safe_return_zone_id = strtol(row[col++], nullptr, 10); + entry.safe_return_x = strtof(row[col++], nullptr); + entry.safe_return_y = strtof(row[col++], nullptr); + entry.safe_return_z = strtof(row[col++], nullptr); + entry.safe_return_heading = strtof(row[col++], nullptr); + entry.zone_in_x = strtof(row[col++], nullptr); + entry.zone_in_y = strtof(row[col++], nullptr); + entry.zone_in_z = strtof(row[col++], nullptr); + entry.zone_in_heading = strtof(row[col++], nullptr); + entry.has_zone_in = strtol(row[col++], nullptr, 10) != 0; + // from instance_list + entry.zone = strtol(row[col++], nullptr, 10); + entry.version = strtol(row[col++], nullptr, 10); + entry.is_global = strtol(row[col++], nullptr, 10); + entry.start_time = strtoul(row[col++], nullptr, 10); + entry.duration = strtol(row[col++], nullptr, 10); + entry.never_expires = strtol(row[col++], nullptr, 10); + + return entry; + } + + static std::vector GetWithInstance(Database& db, + const std::vector& dynamic_zone_ids) + { + if (dynamic_zone_ids.empty()) + { + return {}; + } + + std::vector all_entries; + + auto results = db.QueryDatabase(fmt::format( + "{} WHERE dynamic_zones.id IN ({}) ORDER BY dynamic_zones.id;", + SelectDynamicZoneJoinInstance(), + fmt::join(dynamic_zone_ids, ",") + )); + + if (results.Success()) + { + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) + { + DynamicZoneInstance entry = FillWithInstanceFromRow(row); + all_entries.emplace_back(std::move(entry)); + } + } + + return all_entries; + } + + static void UpdateCompass(Database& db, uint32_t dz_id, int zone_id, float x, float y, float z) + { + if (dz_id != 0) + { + std::string query = fmt::format(SQL( + UPDATE {} SET + compass_zone_id = {}, + compass_x = {}, + compass_y = {}, + compass_z = {} + WHERE {} = {}; + ), TableName(), zone_id, x, y, z, PrimaryKey(), dz_id); + + db.QueryDatabase(query); + } + } + + static void UpdateSafeReturn(Database& db, uint32_t dz_id, int zone_id, float x, float y, float z, float heading) + { + if (dz_id != 0) + { + std::string query = fmt::format(SQL( + UPDATE {} SET + safe_return_zone_id = {}, + safe_return_x = {}, + safe_return_y = {}, + safe_return_z = {}, + safe_return_heading = {} + WHERE {} = {}; + ), TableName(), zone_id, x, y, z, heading, PrimaryKey(), dz_id); + + db.QueryDatabase(query); + } + } + + static void UpdateZoneIn(Database& db, uint32_t dz_id, uint32_t zone_id, float x, float y, float z, float heading, bool has_zone_in) + { + if (dz_id != 0) + { + std::string query = fmt::format(SQL( + UPDATE {} SET + zone_in_x = {}, + zone_in_y = {}, + zone_in_z = {}, + zone_in_heading = {}, + has_zone_in = {} + WHERE {} = {}; + ), TableName(), x, y, z, heading, has_zone_in, PrimaryKey(), dz_id); + + db.QueryDatabase(query); + } + } + + struct DynamicZoneInstancePlayerCount + { + uint32_t id; + int type; + int instance; + int zone; + int version; + uint32_t start_time; + int duration; + int player_count; + }; + + static std::string SelectDynamicZoneInstancePlayerCount() + { + return std::string(SQL( + SELECT + dynamic_zones.id, + dynamic_zones.type, + instance_list.id, + instance_list.zone, + instance_list.version, + instance_list.start_time, + instance_list.duration, + COUNT(instance_list_player.id) member_count + FROM dynamic_zones + INNER JOIN instance_list ON dynamic_zones.instance_id = instance_list.id + LEFT JOIN instance_list_player ON instance_list.id = instance_list_player.id + GROUP BY instance_list.id + ORDER BY dynamic_zones.id; + )); + }; + + static std::vector AllDzInstancePlayerCounts(Database& db) + { + std::vector all_entries; + + auto results = db.QueryDatabase(SelectDynamicZoneInstancePlayerCount()); + if (results.Success()) + { + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) + { + DynamicZoneInstancePlayerCount entry{}; + + int col = 0; + entry.id = strtoul(row[col++], nullptr, 10); + entry.type = strtol(row[col++], nullptr, 10); + entry.instance = strtol(row[col++], nullptr, 10); + entry.zone = strtol(row[col++], nullptr, 10); + entry.version = strtol(row[col++], nullptr, 10); + entry.start_time = strtoul(row[col++], nullptr, 10); + entry.duration = strtol(row[col++], nullptr, 10); + entry.player_count = strtol(row[col++], nullptr, 10); + + all_entries.emplace_back(std::move(entry)); + } + } + return all_entries; + } }; #endif //EQEMU_DYNAMIC_ZONES_REPOSITORY_H diff --git a/common/servertalk.h b/common/servertalk.h index e86dd2c86..384bcc15b 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -161,7 +161,7 @@ #define ServerOP_ExpeditionExpireWarning 0x0416 #define ServerOP_ExpeditionChooseNewLeader 0x0417 -#define ServerOP_DzCharacterChange 0x0450 +#define ServerOP_DzAddRemoveCharacter 0x0450 #define ServerOP_DzRemoveAllCharacters 0x0451 #define ServerOP_DzSetSecondsRemaining 0x0452 #define ServerOP_DzDurationUpdate 0x0453 diff --git a/world/dynamic_zone.cpp b/world/dynamic_zone.cpp index 252559c29..a845c5b1c 100644 --- a/world/dynamic_zone.cpp +++ b/world/dynamic_zone.cpp @@ -12,16 +12,21 @@ extern ZSList zoneserver_list; DynamicZone::DynamicZone( uint32_t id, uint32_t zone_id, uint32_t instance_id, uint32_t zone_version, uint32_t start_time, uint32_t duration, DynamicZoneType type -) : - m_id(id), - m_instance_id(instance_id), - m_zone_id(zone_id), - m_zone_version(zone_version), - m_start_time(std::chrono::system_clock::from_time_t(start_time)), - m_duration(duration), - m_type(type), - m_expire_time(m_start_time + m_duration) +) { + m_id = id; + m_instance_id = instance_id; + m_zone_id = zone_id; + m_zone_version = zone_version; + m_start_time = std::chrono::system_clock::from_time_t(start_time); + m_duration = std::chrono::seconds(duration); + m_type = type; + m_expire_time = m_start_time + m_duration; +} + +Database& DynamicZone::GetDatabase() +{ + return database; } DynamicZone* DynamicZone::FindDynamicZoneByID(uint32_t dz_id) @@ -99,10 +104,27 @@ void DynamicZone::HandleZoneMessage(ServerPacket* pack) case ServerOP_DzSetSafeReturn: case ServerOP_DzSetZoneIn: { + auto buf = reinterpret_cast(pack->pBuffer); + auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); + if (dz) + { + if (pack->opcode == ServerOP_DzSetCompass) + { + dz->SetCompass(buf->zone_id, buf->x, buf->y, buf->z, false); + } + else if (pack->opcode == ServerOP_DzSetSafeReturn) + { + dz->SetSafeReturn(buf->zone_id, buf->x, buf->y, buf->z, buf->heading, false); + } + else if (pack->opcode == ServerOP_DzSetZoneIn) + { + dz->SetZoneInLocation(buf->x, buf->y, buf->z, buf->heading, false); + } + } zoneserver_list.SendPacket(pack); break; } - case ServerOP_DzCharacterChange: + case ServerOP_DzAddRemoveCharacter: case ServerOP_DzRemoveAllCharacters: { auto buf = reinterpret_cast(pack->pBuffer); @@ -125,3 +147,29 @@ void DynamicZone::HandleZoneMessage(ServerPacket* pack) } }; } + +void DynamicZone::SendInstanceAddRemoveCharacter(uint32_t character_id, bool remove) +{ + ZoneServer* instance_zs = zoneserver_list.FindByInstanceID(GetInstanceID()); + if (instance_zs) + { + auto pack = CreateServerAddRemoveCharacterPacket(character_id, remove); + instance_zs->SendPacket(pack.get()); + } +} + +void DynamicZone::SendInstanceRemoveAllCharacters() +{ + ZoneServer* instance_zs = zoneserver_list.FindByInstanceID(GetInstanceID()); + if (instance_zs) + { + auto pack = CreateServerRemoveAllCharactersPacket(); + instance_zs->SendPacket(pack.get()); + } +} + +void DynamicZone::SendGlobalLocationChange(uint16_t server_opcode, const DynamicZoneLocation& location) +{ + auto pack = CreateServerDzLocationPacket(server_opcode, location); + zoneserver_list.SendPacket(pack.get()); +} diff --git a/world/dynamic_zone.h b/world/dynamic_zone.h index 84455d7eb..a82ff9d0e 100644 --- a/world/dynamic_zone.h +++ b/world/dynamic_zone.h @@ -1,9 +1,9 @@ #ifndef WORLD_DYNAMIC_ZONE_H #define WORLD_DYNAMIC_ZONE_H -#include "../common/eq_constants.h" -#include +#include "../common/dynamic_zone_base.h" +class Database; class ServerPacket; enum class DynamicZoneStatus @@ -14,9 +14,11 @@ enum class DynamicZoneStatus ExpiredEmpty, }; -class DynamicZone +class DynamicZone : public DynamicZoneBase { public: + using DynamicZoneBase::DynamicZoneBase; // inherit base constructors + DynamicZone() = default; DynamicZone(uint32_t id, uint32_t zone_id, uint32_t instance_id, uint32_t zone_version, uint32_t start_time, uint32_t duration, DynamicZoneType type); @@ -24,29 +26,20 @@ public: static DynamicZone* FindDynamicZoneByID(uint32_t dz_id); static void HandleZoneMessage(ServerPacket* pack); - uint32_t GetID() const { return m_id; } - uint16_t GetInstanceID() const { return static_cast(m_instance_id); } - uint16_t GetZoneID() const { return static_cast(m_zone_id); } - uint32_t GetZoneVersion() const { return m_zone_version; } - std::chrono::system_clock::duration GetRemainingDuration() const { - return m_expire_time - std::chrono::system_clock::now(); } + void SetSecondsRemaining(uint32_t seconds_remaining) override; DynamicZoneStatus Process(bool force_expire); - bool IsExpired() const { return m_expire_time < std::chrono::system_clock::now(); } - void SetSecondsRemaining(uint32_t seconds_remaining); + +protected: + Database& GetDatabase() override; + void SendInstanceAddRemoveCharacter(uint32_t character_id, bool remove) override; + void SendInstanceRemoveAllCharacters() override; + void SendGlobalLocationChange(uint16_t server_opcode, const DynamicZoneLocation& location) override; private: void SendZonesDurationUpdate(); - uint32_t m_id = 0; - uint32_t m_instance_id = 0; - uint32_t m_zone_id = 0; - uint32_t m_zone_version = 0; bool m_is_pending_early_shutdown = false; - DynamicZoneType m_type{ DynamicZoneType::None }; - std::chrono::seconds m_duration; - std::chrono::time_point m_start_time; - std::chrono::time_point m_expire_time; }; #endif diff --git a/world/expedition.cpp b/world/expedition.cpp index 2937a20bc..a72275c94 100644 --- a/world/expedition.cpp +++ b/world/expedition.cpp @@ -142,7 +142,7 @@ void Expedition::CheckExpireWarning() if (m_warning_cooldown_timer.Check(false)) { using namespace std::chrono_literals; - auto remaining = GetDynamicZone().GetRemainingDuration(); + auto remaining = GetDynamicZone().GetDurationRemaining(); if ((remaining > 14min && remaining < 15min) || (remaining > 4min && remaining < 5min) || (remaining > 0min && remaining < 1min)) diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 3f0efbf19..9a319774a 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -1388,7 +1388,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { ExpeditionMessage::HandleZoneMessage(pack); break; } - case ServerOP_DzCharacterChange: + case ServerOP_DzAddRemoveCharacter: case ServerOP_DzRemoveAllCharacters: case ServerOP_DzSetSecondsRemaining: case ServerOP_DzSetCompass: diff --git a/zone/client.cpp b/zone/client.cpp index 96e18dcd9..d92129632 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -9595,7 +9595,7 @@ Expedition* Client::CreateExpedition( const std::string& zone_name, uint32 version, uint32 duration, const std::string& expedition_name, uint32 min_players, uint32 max_players, bool disable_messages) { - DynamicZone dz_instance{ zone_name, version, duration, DynamicZoneType::Expedition }; + DynamicZone dz_instance{ ZoneID(zone_name), version, duration, DynamicZoneType::Expedition }; ExpeditionRequest request{ expedition_name, min_players, max_players, disable_messages }; return Expedition::TryCreate(this, dz_instance, request); } diff --git a/zone/command.cpp b/zone/command.cpp index 62757f2b2..709bb92c4 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -57,6 +57,7 @@ #include "../common/eqemu_logsys.h" #include "../common/profanity_manager.h" #include "../common/net/eqstream.h" +#include "../common/repositories/dynamic_zones_repository.h" #include "data_bucket.h" #include "command.h" @@ -6960,58 +6961,36 @@ void command_dz(Client* c, const Seperator* sep) } else if (strcasecmp(sep->arg[1], "list") == 0) { - std::string query = SQL( - SELECT - dynamic_zones.id, - dynamic_zones.type, - instance_list.id, - instance_list.zone, - instance_list.version, - instance_list.start_time, - instance_list.duration, - COUNT(instance_list_player.id) member_count - FROM dynamic_zones - INNER JOIN instance_list ON dynamic_zones.instance_id = instance_list.id - LEFT JOIN instance_list_player ON instance_list.id = instance_list_player.id - GROUP BY instance_list.id - ORDER BY dynamic_zones.id; - ); + auto dz_list = DynamicZonesRepository::AllDzInstancePlayerCounts(database); + c->Message(Chat::White, fmt::format("Total Dynamic Zones: [{}]", dz_list.size()).c_str()); - auto results = database.QueryDatabase(query); - if (results.Success()) + auto now = std::chrono::system_clock::now(); + + for (const auto& dz : dz_list) { - c->Message(Chat::White, fmt::format("Total Dynamic Zones: [{}]", results.RowCount()).c_str()); - for (auto row = results.begin(); row != results.end(); ++row) + auto expire_time = std::chrono::system_clock::from_time_t(dz.start_time + dz.duration); + auto remaining = std::chrono::duration_cast(expire_time - now); + auto seconds = std::max(0, static_cast(remaining.count())); + bool is_expired = now > expire_time; + + if (!is_expired || strcasecmp(sep->arg[2], "all") == 0) { - auto start_time = strtoul(row[5], nullptr, 10); - auto duration = strtoul(row[6], nullptr, 10); - auto expire_time = std::chrono::system_clock::from_time_t(start_time + duration); + auto zone_saylink = is_expired ? "zone" : EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#zoneinstance {}", dz.instance), false, "zone"); - auto now = std::chrono::system_clock::now(); - auto remaining = std::chrono::duration_cast(expire_time - now); - auto seconds = std::max(0, static_cast(remaining.count())); - - bool is_expired = now > expire_time; - if (!is_expired || strcasecmp(sep->arg[2], "all") == 0) - { - uint32_t instance_id = strtoul(row[2], nullptr, 10); - auto zone_saylink = is_expired ? "zone" : EQ::SayLinkEngine::GenerateQuestSaylink( - fmt::format("#zoneinstance {}", instance_id), false, "zone"); - - c->Message(Chat::White, fmt::format( - "dz id: [{}] type: [{}] {}: [{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]", - strtoul(row[0], nullptr, 10), // dynamic_zone_id - strtoul(row[1], nullptr, 10), // dynamic_zone_type - zone_saylink, - strtoul(row[3], nullptr, 10), // instance_zone_id - instance_id, // instance_id - strtoul(row[4], nullptr, 10), // instance_zone_version - strtoul(row[7], nullptr, 10), // instance member_count - seconds / 3600, // hours - (seconds / 60) % 60, // minutes - seconds % 60 // seconds - ).c_str()); - } + c->Message(Chat::White, fmt::format( + "dz id: [{}] type: [{}] {}: [{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]", + dz.id, + dz.type, + zone_saylink, + dz.zone, + dz.instance, + dz.version, + dz.player_count, + seconds / 3600, // hours + (seconds / 60) % 60, // minutes + seconds % 60 // seconds + ).c_str()); } } } diff --git a/zone/dynamic_zone.cpp b/zone/dynamic_zone.cpp index 9358f15a2..9d9f35012 100644 --- a/zone/dynamic_zone.cpp +++ b/zone/dynamic_zone.cpp @@ -22,30 +22,35 @@ #include "client.h" #include "expedition.h" #include "worldserver.h" -#include "zonedb.h" #include "../common/eqemu_logsys.h" extern WorldServer worldserver; -DynamicZone::DynamicZone( - uint32_t zone_id, uint32_t version, uint32_t duration, DynamicZoneType type -) : - m_zone_id(zone_id), - m_version(version), - m_duration(duration), - m_type(type) -{ -} +// message string 8312 added in September 08 2020 Test patch (used by both dz and shared tasks) +const char* const CREATE_NOT_ALL_ADDED = "Not all players in your {} were added to the {}. The {} can take a maximum of {} players, and your {} has {}."; DynamicZone::DynamicZone( - std::string zone_name, uint32_t version, uint32_t duration, DynamicZoneType type -) : - DynamicZone(ZoneID(zone_name), version, duration, type) + uint32_t zone_id, uint32_t version, uint32_t duration, DynamicZoneType type) { - if (!m_zone_id) - { - LogDynamicZones("Failed to get zone id for zone [{}]", zone_name); - } + m_zone_id = zone_id; + m_zone_version = version; + m_duration = std::chrono::seconds(duration); + m_type = type; +} + +Database& DynamicZone::GetDatabase() +{ + return database; +} + +uint16_t DynamicZone::GetCurrentInstanceID() +{ + return zone ? static_cast(zone->GetInstanceID()) : 0; +} + +uint16_t DynamicZone::GetCurrentZoneID() +{ + return zone ? static_cast(zone->GetZoneID()) : 0; } DynamicZone* DynamicZone::FindDynamicZoneByID(uint32_t dz_id) @@ -64,366 +69,43 @@ std::unordered_map DynamicZone::LoadMultipleDzFromDatabas { LogDynamicZonesDetail("Loading dynamic zone data for [{}] instances", dynamic_zone_ids.size()); - std::string in_dynamic_zone_ids_query = fmt::format("{}", fmt::join(dynamic_zone_ids, ",")); - std::unordered_map dynamic_zones; - if (!in_dynamic_zone_ids_query.empty()) + auto entries = DynamicZonesRepository::GetWithInstance(database, dynamic_zone_ids); + for (auto& entry : entries) { - std::string query = fmt::format(SQL( - {} WHERE dynamic_zones.id IN ({}); - ), DynamicZoneSelectQuery(), in_dynamic_zone_ids_query); - - auto results = database.QueryDatabase(query); - if (results.Success()) - { - for (auto row = results.begin(); row != results.end(); ++row) - { - DynamicZone dz; - dz.LoadDatabaseResult(row); - dynamic_zones.emplace(dz.GetID(), dz); - } - } + dynamic_zones.emplace(entry.id, std::move(entry)); } return dynamic_zones; } -uint32_t DynamicZone::Create() +void DynamicZone::StartAllClientRemovalTimers() { - if (m_id != 0) + for (const auto& client_iter : entity_list.GetClientList()) { - return m_id; - } - - if (GetInstanceID() == 0) - { - CreateInstance(); - } - - m_id = SaveToDatabase(); - - return m_id; -} - -uint32_t DynamicZone::CreateInstance() -{ - if (m_instance_id) - { - LogDynamicZones("CreateInstance failed, instance id [{}] already created", m_instance_id); - return 0; - } - - if (!m_zone_id) - { - LogDynamicZones("CreateInstance failed, invalid zone id [{}]", m_zone_id); - return 0; - } - - uint16_t instance_id = 0; - if (!database.GetUnusedInstanceID(instance_id)) // todo: doesn't this race with insert? - { - LogDynamicZones("Failed to find unused instance id"); - return 0; - } - - m_start_time = std::chrono::system_clock::now(); - auto start_time = std::chrono::system_clock::to_time_t(m_start_time); - - std::string query = fmt::format(SQL( - INSERT INTO instance_list - (id, zone, version, start_time, duration) - VALUES - ({}, {}, {}, {}, {}) - ), instance_id, m_zone_id, m_version, start_time, m_duration.count()); - - auto results = database.QueryDatabase(query); - if (!results.Success()) - { - LogDynamicZones("Failed to create instance [{}] for Dynamic Zone [{}]", instance_id, m_zone_id); - return 0; - } - - m_instance_id = instance_id; - m_never_expires = false; - m_expire_time = m_start_time + m_duration; - - return m_instance_id; -} - -std::string DynamicZone::DynamicZoneSelectQuery() -{ - return std::string(SQL( - SELECT - instance_list.id, - instance_list.zone, - instance_list.version, - instance_list.start_time, - instance_list.duration, - instance_list.never_expires, - dynamic_zones.id, - dynamic_zones.type, - dynamic_zones.compass_zone_id, - dynamic_zones.compass_x, - dynamic_zones.compass_y, - dynamic_zones.compass_z, - dynamic_zones.safe_return_zone_id, - dynamic_zones.safe_return_x, - dynamic_zones.safe_return_y, - dynamic_zones.safe_return_z, - dynamic_zones.safe_return_heading, - dynamic_zones.zone_in_x, - dynamic_zones.zone_in_y, - dynamic_zones.zone_in_z, - dynamic_zones.zone_in_heading, - dynamic_zones.has_zone_in - FROM dynamic_zones - INNER JOIN instance_list ON dynamic_zones.instance_id = instance_list.id - )); -} - -void DynamicZone::LoadDatabaseResult(MySQLRequestRow& row) -{ - m_instance_id = strtoul(row[0], nullptr, 10); - m_zone_id = strtoul(row[1], nullptr, 10); - m_version = strtoul(row[2], nullptr, 10); - m_start_time = std::chrono::system_clock::from_time_t(strtoul(row[3], nullptr, 10)); - m_duration = std::chrono::seconds(strtoul(row[4], nullptr, 10)); - m_expire_time = m_start_time + m_duration; - m_never_expires = (strtoul(row[5], nullptr, 10) != 0); - m_id = strtoul(row[6], nullptr, 10); - m_type = static_cast(strtoul(row[7], nullptr, 10)); - m_compass.zone_id = strtoul(row[8], nullptr, 10); - m_compass.x = strtof(row[9], nullptr); - m_compass.y = strtof(row[10], nullptr); - m_compass.z = strtof(row[11], nullptr); - m_safereturn.zone_id = strtoul(row[12], nullptr, 10); - m_safereturn.x = strtof(row[13], nullptr); - m_safereturn.y = strtof(row[14], nullptr); - m_safereturn.z = strtof(row[15], nullptr); - m_safereturn.heading = strtof(row[16], nullptr); - m_zonein.x = strtof(row[17], nullptr); - m_zonein.y = strtof(row[18], nullptr); - m_zonein.z = strtof(row[19], nullptr); - m_zonein.heading = strtof(row[20], nullptr); - m_has_zonein = (strtoul(row[21], nullptr, 10) != 0); -} - -uint32_t DynamicZone::SaveToDatabase() -{ - LogDynamicZonesDetail("Saving dz instance [{}] to database", m_instance_id); - - if (m_instance_id != 0) - { - std::string query = fmt::format(SQL( - INSERT INTO dynamic_zones - ( - instance_id, - type, - compass_zone_id, - compass_x, - compass_y, - compass_z, - safe_return_zone_id, - safe_return_x, - safe_return_y, - safe_return_z, - safe_return_heading, - zone_in_x, - zone_in_y, - zone_in_z, - zone_in_heading, - has_zone_in - ) - VALUES - ({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}); - ), - m_instance_id, - static_cast(m_type), - m_compass.zone_id, - m_compass.x, - m_compass.y, - m_compass.z, - m_safereturn.zone_id, - m_safereturn.x, - m_safereturn.y, - m_safereturn.z, - m_safereturn.heading, - m_zonein.x, - m_zonein.y, - m_zonein.z, - m_zonein.heading, - m_has_zonein - ); - - auto results = database.QueryDatabase(query); - if (results.Success()) + if (client_iter.second) { - return results.LastInsertedID(); + client_iter.second->SetDzRemovalTimer(true); } } - return 0; } -void DynamicZone::SaveCompassToDatabase() +void DynamicZone::SendInstanceRemoveAllCharacters() { - LogDynamicZonesDetail( - "Instance [{}] saving compass zone: [{}] xyz: ([{}], [{}], [{}])", - m_instance_id, m_compass.zone_id, m_compass.x, m_compass.y, m_compass.z - ); - - if (m_instance_id != 0) + // just remove all clients in bulk instead of only characters assigned to the instance + if (IsCurrentZoneDzInstance()) { - std::string query = fmt::format(SQL( - UPDATE dynamic_zones SET - compass_zone_id = {}, - compass_x = {}, - compass_y = {}, - compass_z = {} - WHERE instance_id = {}; - ), - m_compass.zone_id, - m_compass.x, - m_compass.y, - m_compass.z, - m_instance_id - ); - - database.QueryDatabase(query); + DynamicZone::StartAllClientRemovalTimers(); + } + else if (GetInstanceID() != 0) + { + auto pack = CreateServerRemoveAllCharactersPacket(); + worldserver.SendPacket(pack.get()); } } -void DynamicZone::SaveSafeReturnToDatabase() -{ - LogDynamicZonesDetail( - "Instance [{}] saving safereturn zone: [{}] xyzh: ([{}], [{}], [{}], [{}])", - m_instance_id, m_safereturn.zone_id, m_safereturn.x, m_safereturn.y, m_safereturn.z, m_safereturn.heading - ); - - if (m_instance_id != 0) - { - std::string query = fmt::format(SQL( - UPDATE dynamic_zones SET - safe_return_zone_id = {}, - safe_return_x = {}, - safe_return_y = {}, - safe_return_z = {}, - safe_return_heading = {} - WHERE instance_id = {}; - ), - m_safereturn.zone_id, - m_safereturn.x, - m_safereturn.y, - m_safereturn.z, - m_safereturn.heading, - m_instance_id - ); - - database.QueryDatabase(query); - } -} - -void DynamicZone::SaveZoneInLocationToDatabase() -{ - LogDynamicZonesDetail( - "Instance [{}] saving zonein zone: [{}] xyzh: ([{}], [{}], [{}], [{}]) has: [{}]", - m_instance_id, m_zone_id, m_zonein.x, m_zonein.y, m_zonein.z, m_zonein.heading, m_has_zonein - ); - - if (m_instance_id != 0) - { - std::string query = fmt::format(SQL( - UPDATE dynamic_zones SET - zone_in_x = {}, - zone_in_y = {}, - zone_in_z = {}, - zone_in_heading = {}, - has_zone_in = {} - WHERE instance_id = {}; - ), - m_zonein.x, - m_zonein.y, - m_zonein.z, - m_zonein.heading, - m_has_zonein, - m_instance_id - ); - - database.QueryDatabase(query); - } -} - -void DynamicZone::AddCharacter(uint32_t character_id) -{ - database.AddClientToInstance(m_instance_id, character_id); - SendInstanceCharacterChange(character_id, false); // stops client kick timer -} - -void DynamicZone::RemoveCharacter(uint32_t character_id) -{ - database.RemoveClientFromInstance(m_instance_id, character_id); - SendInstanceCharacterChange(character_id, true); // start client kick timer -} - -void DynamicZone::RemoveAllCharacters(bool enable_removal_timers) -{ - if (GetInstanceID() == 0) - { - return; - } - - if (enable_removal_timers) - { - // just remove all clients in bulk instead of only characters assigned to the instance - if (IsCurrentZoneDzInstance()) - { - for (const auto& client_iter : entity_list.GetClientList()) - { - if (client_iter.second) - { - client_iter.second->SetDzRemovalTimer(true); - } - } - } - else if (GetInstanceID() != 0) - { - uint32_t packsize = sizeof(ServerDzCharacter_Struct); - auto pack = std::make_unique(ServerOP_DzRemoveAllCharacters, packsize); - auto packbuf = reinterpret_cast(pack->pBuffer); - packbuf->zone_id = GetZoneID(); - packbuf->instance_id = GetInstanceID(); - packbuf->remove = true; - packbuf->character_id = 0; - worldserver.SendPacket(pack.get()); - } - } - - database.RemoveClientsFromInstance(GetInstanceID()); -} - -void DynamicZone::SaveInstanceMembersToDatabase(const std::vector& character_ids) -{ - LogDynamicZonesDetail("Saving [{}] instance members to database", character_ids.size()); - - std::string insert_values; - for (const auto& character_id : character_ids) - { - fmt::format_to(std::back_inserter(insert_values), "({}, {}),", m_instance_id, character_id); - } - - if (!insert_values.empty()) - { - insert_values.pop_back(); // trailing comma - - std::string query = fmt::format(SQL( - REPLACE INTO instance_list_player (id, charid) VALUES {}; - ), insert_values); - - database.QueryDatabase(query); - } -} - -void DynamicZone::SendInstanceCharacterChange(uint32_t character_id, bool removed) +void DynamicZone::SendInstanceAddRemoveCharacter(uint32_t character_id, bool removed) { // if removing, sets removal timer on client inside the instance if (IsCurrentZoneDzInstance()) @@ -436,97 +118,16 @@ void DynamicZone::SendInstanceCharacterChange(uint32_t character_id, bool remove } else if (GetInstanceID() != 0) { - uint32_t packsize = sizeof(ServerDzCharacter_Struct); - auto pack = std::make_unique(ServerOP_DzCharacterChange, packsize); - auto packbuf = reinterpret_cast(pack->pBuffer); - packbuf->zone_id = GetZoneID(); - packbuf->instance_id = GetInstanceID(); - packbuf->remove = removed; - packbuf->character_id = character_id; + auto pack = CreateServerAddRemoveCharacterPacket(character_id, removed); worldserver.SendPacket(pack.get()); } } -void DynamicZone::SetCompass(const DynamicZoneLocation& location, bool update_db) -{ - m_compass = location; - - if (m_on_compass_change) - { - m_on_compass_change(); - } - - if (update_db) - { - SaveCompassToDatabase(); - SendWorldSetLocation(ServerOP_DzSetCompass, location); - } -} - -void DynamicZone::SetCompass(uint32_t zone_id, float x, float y, float z, bool update_db) -{ - SetCompass({ zone_id, x, y, z, 0.0f }, update_db); -} - -void DynamicZone::SetSafeReturn(const DynamicZoneLocation& location, bool update_db) -{ - m_safereturn = location; - - if (update_db) - { - SaveSafeReturnToDatabase(); - SendWorldSetLocation(ServerOP_DzSetSafeReturn, location); - } -} - -void DynamicZone::SetSafeReturn(uint32_t zone_id, float x, float y, float z, float heading, bool update_db) -{ - SetSafeReturn({ zone_id, x, y, z, heading }, update_db); -} - -void DynamicZone::SetZoneInLocation(const DynamicZoneLocation& location, bool update_db) -{ - m_zonein = location; - m_has_zonein = true; - - if (update_db) - { - SaveZoneInLocationToDatabase(); - SendWorldSetLocation(ServerOP_DzSetZoneIn, location); - } -} - -void DynamicZone::SetZoneInLocation(float x, float y, float z, float heading, bool update_db) -{ - SetZoneInLocation({ 0, x, y, z, heading }, update_db); -} - bool DynamicZone::IsCurrentZoneDzInstance() const { return (zone && zone->GetInstanceID() != 0 && zone->GetInstanceID() == GetInstanceID()); } -bool DynamicZone::IsInstanceID(uint32_t instance_id) const -{ - return (GetInstanceID() != 0 && GetInstanceID() == instance_id); -} - -bool DynamicZone::IsSameDz(uint32_t zone_id, uint32_t instance_id) const -{ - return zone_id == m_zone_id && instance_id == m_instance_id; -} - -uint32_t DynamicZone::GetSecondsRemaining() const -{ - auto now = std::chrono::system_clock::now(); - if (m_expire_time > now) - { - auto remaining = m_expire_time - now; - return static_cast(std::chrono::duration_cast(remaining).count()); - } - return 0; -} - void DynamicZone::SetSecondsRemaining(uint32_t seconds_remaining) { // async @@ -553,19 +154,9 @@ void DynamicZone::SetUpdatedDuration(uint32_t new_duration) } } -void DynamicZone::SendWorldSetLocation(uint16_t server_opcode, const DynamicZoneLocation& location) +void DynamicZone::SendGlobalLocationChange(uint16_t server_opcode, const DynamicZoneLocation& location) { - uint32_t pack_size = sizeof(ServerDzLocation_Struct); - auto pack = std::make_unique(server_opcode, pack_size); - auto buf = reinterpret_cast(pack->pBuffer); - buf->dz_id = GetID(); - buf->sender_zone_id = zone ? zone->GetZoneID() : 0; - buf->sender_instance_id = zone ? zone->GetInstanceID() : 0; - buf->zone_id = location.zone_id; - buf->x = location.x; - buf->y = location.y; - buf->z = location.z; - buf->heading = location.heading; + auto pack = CreateServerDzLocationPacket(server_opcode, location); worldserver.SendPacket(pack.get()); } @@ -573,7 +164,7 @@ void DynamicZone::HandleWorldMessage(ServerPacket* pack) { switch (pack->opcode) { - case ServerOP_DzCharacterChange: + case ServerOP_DzAddRemoveCharacter: { auto buf = reinterpret_cast(pack->pBuffer); Client* client = entity_list.GetClientByCharID(buf->character_id); @@ -588,13 +179,7 @@ void DynamicZone::HandleWorldMessage(ServerPacket* pack) auto buf = reinterpret_cast(pack->pBuffer); if (buf->remove) { - for (const auto& client_list_iter : entity_list.GetClientList()) - { - if (client_list_iter.second) - { - client_list_iter.second->SetDzRemovalTimer(true); - } - } + DynamicZone::StartAllClientRemovalTimers(); } break; } @@ -636,3 +221,12 @@ void DynamicZone::HandleWorldMessage(ServerPacket* pack) } } } + +void DynamicZone::ProcessCompassChange(const DynamicZoneLocation& location) +{ + DynamicZoneBase::ProcessCompassChange(location); + if (m_on_compass_change) + { + m_on_compass_change(); + } +} diff --git a/zone/dynamic_zone.h b/zone/dynamic_zone.h index 583dd6958..98949953d 100644 --- a/zone/dynamic_zone.h +++ b/zone/dynamic_zone.h @@ -21,106 +21,49 @@ #ifndef DYNAMIC_ZONE_H #define DYNAMIC_ZONE_H -#include "../common/eq_constants.h" -#include +#include "../common/dynamic_zone_base.h" #include #include #include #include #include -class MySQLRequestRow; +class Database; class ServerPacket; -struct DynamicZoneLocation -{ - uint32_t zone_id = 0; - float x = 0.0f; - float y = 0.0f; - float z = 0.0f; - float heading = 0.0f; +extern const char* const CREATE_NOT_ALL_ADDED; - DynamicZoneLocation() = default; - DynamicZoneLocation(uint32_t zone_id_, float x_, float y_, float z_, float heading_) - : zone_id(zone_id_), x(x_), y(y_), z(z_), heading(heading_) {} -}; - -class DynamicZone +class DynamicZone : public DynamicZoneBase { public: + using DynamicZoneBase::DynamicZoneBase; // inherit base constructors + DynamicZone() = default; DynamicZone(uint32_t zone_id, uint32_t version, uint32_t duration, DynamicZoneType type); - DynamicZone(std::string zone_shortname, uint32_t version, uint32_t duration, DynamicZoneType type); - DynamicZone(uint32_t dz_id) : m_id(dz_id) {} - DynamicZone(DynamicZoneType type) : m_type(type) {} static DynamicZone* FindDynamicZoneByID(uint32_t dz_id); static std::unordered_map LoadMultipleDzFromDatabase( const std::vector& dynamic_zone_ids); static void HandleWorldMessage(ServerPacket* pack); - uint64_t GetExpireTime() const { return std::chrono::system_clock::to_time_t(m_expire_time); } - uint32_t GetID() const { return m_id; } - uint16_t GetInstanceID() const { return static_cast(m_instance_id); } - uint32_t GetSecondsRemaining() const; - uint16_t GetZoneID() const { return static_cast(m_zone_id); } - uint32_t GetZoneIndex() const { return (m_instance_id << 16) | (m_zone_id & 0xffff); } - uint32_t GetZoneVersion() const { return m_version; } - const std::string& GetLeaderName() const { return m_leader_name; } - const std::string& GetName() const { return m_name; } - DynamicZoneType GetType() const { return m_type; } - DynamicZoneLocation GetCompassLocation() const { return m_compass; } - DynamicZoneLocation GetSafeReturnLocation() const { return m_safereturn; } - DynamicZoneLocation GetZoneInLocation() const { return m_zonein; } + void SetSecondsRemaining(uint32_t seconds_remaining) override; - void AddCharacter(uint32_t character_id); - uint32_t Create(); - uint32_t CreateInstance(); - bool HasZoneInLocation() const { return m_has_zonein; } - bool IsCurrentZoneDzInstance() const; - bool IsInstanceID(uint32_t instance_id) const; - bool IsValid() const { return m_instance_id != 0; } - bool IsSameDz(uint32_t zone_id, uint32_t instance_id) const; - void RegisterOnCompassChange(const std::function& on_change) { m_on_compass_change = on_change; } - void RemoveAllCharacters(bool enable_removal_timers = true); - void RemoveCharacter(uint32_t character_id); - void SaveInstanceMembersToDatabase(const std::vector& character_ids); - void SendInstanceCharacterChange(uint32_t character_id, bool removed); - void SetCompass(const DynamicZoneLocation& location, bool update_db = false); - void SetCompass(uint32_t zone_id, float x, float y, float z, bool update_db = false); - void SetLeaderName(const std::string& leader_name) { m_leader_name = leader_name; } - void SetName(const std::string& name) { m_name = name; } - void SetSafeReturn(const DynamicZoneLocation& location, bool update_db = false); - void SetSafeReturn(uint32_t zone_id, float x, float y, float z, float heading, bool update_db = false); - void SetSecondsRemaining(uint32_t seconds_remaining); - void SetZoneInLocation(const DynamicZoneLocation& location, bool update_db = false); - void SetZoneInLocation(float x, float y, float z, float heading, bool update_db = false); - void SetUpdatedDuration(uint32_t seconds); + bool IsCurrentZoneDzInstance() const; + void RegisterOnCompassChange(const std::function& on_change) { m_on_compass_change = on_change; } + void SetUpdatedDuration(uint32_t seconds); + +protected: + uint16_t GetCurrentInstanceID() override; + uint16_t GetCurrentZoneID() override; + Database& GetDatabase() override; + void ProcessCompassChange(const DynamicZoneLocation& location) override; + void SendInstanceAddRemoveCharacter(uint32_t character_id, bool remove) override; + void SendInstanceRemoveAllCharacters() override; + void SendGlobalLocationChange(uint16_t server_opcode, const DynamicZoneLocation& location) override; private: - static std::string DynamicZoneSelectQuery(); - void LoadDatabaseResult(MySQLRequestRow& row); - void SaveCompassToDatabase(); - void SaveSafeReturnToDatabase(); - void SaveZoneInLocationToDatabase(); - void SendWorldSetLocation(uint16_t server_opcode, const DynamicZoneLocation& location); - uint32_t SaveToDatabase(); + static void StartAllClientRemovalTimers(); - uint32_t m_id = 0; - uint32_t m_zone_id = 0; - uint32_t m_instance_id = 0; - uint32_t m_version = 0; - bool m_never_expires = false; - bool m_has_zonein = false; - std::string m_name; - std::string m_leader_name; - DynamicZoneType m_type{ DynamicZoneType::None }; - DynamicZoneLocation m_compass; - DynamicZoneLocation m_safereturn; - DynamicZoneLocation m_zonein; - std::chrono::seconds m_duration; - std::chrono::time_point m_start_time; - std::chrono::time_point m_expire_time; std::function m_on_compass_change; }; diff --git a/zone/expedition.cpp b/zone/expedition.cpp index 5bfecb8e4..27bb86079 100644 --- a/zone/expedition.cpp +++ b/zone/expedition.cpp @@ -39,9 +39,6 @@ const char* const EXPEDITION_OTHER_BELONGS = "{} attempted to create an expedi // lockout warnings were added to live in March 11 2020 patch const char* const DZADD_INVITE_WARNING = "Warning! You will be given replay timers for the following events if you enter %s:"; const char* const DZADD_INVITE_WARNING_TIMER = "%s - %sD:%sH:%sM"; -const char* const KICKPLAYERS_EVERYONE = "Everyone"; -// message string 8312 added in September 08 2020 Test patch (used by both dz and shared tasks) -const char* const CREATE_NOT_ALL_ADDED = "Not all players in your {} were added to the {}. The {} can take a maximum of {} players, and your {} has {}."; // various expeditions re-use these strings when locking constexpr char LOCK_CLOSE[] = "Your expedition is nearing its close. You cannot bring any additional people into your expedition at this time."; constexpr char LOCK_BEGIN[] = "The trial has begun. You cannot bring any additional people into your expedition at this time."; @@ -1107,7 +1104,7 @@ void Expedition::DzKickPlayers(Client* requester) } RemoveAllMembers(); - requester->MessageString(Chat::Red, EXPEDITION_REMOVED, KICKPLAYERS_EVERYONE, m_expedition_name.c_str()); + requester->MessageString(Chat::Red, EXPEDITION_REMOVED, "Everyone", m_expedition_name.c_str()); } void Expedition::SetLocked( diff --git a/zone/expedition.h b/zone/expedition.h index c78864c6b..5b2a40043 100644 --- a/zone/expedition.h +++ b/zone/expedition.h @@ -39,7 +39,6 @@ class ServerPacket; extern const char* const DZ_YOU_NOT_ASSIGNED; extern const char* const EXPEDITION_OTHER_BELONGS; -extern const char* const CREATE_NOT_ALL_ADDED; enum class ExpeditionMemberStatus : uint8_t { diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 34994d88a..1b4758ff2 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -2906,7 +2906,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) Expedition::HandleWorldMessage(pack); break; } - case ServerOP_DzCharacterChange: + case ServerOP_DzAddRemoveCharacter: case ServerOP_DzRemoveAllCharacters: case ServerOP_DzDurationUpdate: case ServerOP_DzSetCompass: From f51bc4daaf367bb0e2cbd21e6dc916d083d87b97 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 29 Mar 2021 03:18:03 -0400 Subject: [PATCH 009/624] [Fix] Clean up Filtered/MessageString functions (#1311) This solves the OOB issue pointed out in #1304 and cleans up the code a bit so it should be less error prone --- common/eq_packet_structs.h | 4 +- zone/client.cpp | 104 +++++++++++++------------------------ 2 files changed, 39 insertions(+), 69 deletions(-) diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 57d4c0795..401e62e59 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -3247,7 +3247,7 @@ struct TraderClick_Struct{ }; struct FormattedMessage_Struct{ - uint32 unknown0; + uint32 unknown0; // 1 means from world server uint32 string_id; uint32 type; char message[0]; @@ -3255,7 +3255,7 @@ struct FormattedMessage_Struct{ struct SimpleMessage_Struct{ uint32 string_id; uint32 color; - uint32 unknown8; + uint32 unknown8; // 1 means from world server }; struct GuildMemberUpdate_Struct { diff --git a/zone/client.cpp b/zone/client.cpp index d92129632..c34a314be 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -3161,53 +3161,37 @@ void Client::MessageString(uint32 type, uint32 string_id, const char* message1, if (GetFilter(FilterDamageShields) == FilterHide && type == Chat::DamageShield) return; - int i = 0, argcount = 0, length = 0; - char *bufptr = nullptr; - const char *message_arg[9] = {0}; + if (type == Chat::Emote) + type = 4; - if(type==Chat::Emote) - type=4; - - if(!message1) - { + if (!message1) { MessageString(type, string_id); // use the simple message instead return; } - message_arg[i++] = message1; - message_arg[i++] = message2; - message_arg[i++] = message3; - message_arg[i++] = message4; - message_arg[i++] = message5; - message_arg[i++] = message6; - message_arg[i++] = message7; - message_arg[i++] = message8; - message_arg[i++] = message9; + const char *message_arg[] = { + message1, message2, message3, message4, message5, + message6, message7, message8, message9 + }; - for(; message_arg[argcount]; ++argcount) - length += strlen(message_arg[argcount]) + 1; - - length += 1; - - auto outapp = new EQApplicationPacket(OP_FormattedMessage, sizeof(FormattedMessage_Struct) + length); - FormattedMessage_Struct *fm = (FormattedMessage_Struct *)outapp->pBuffer; - fm->string_id = string_id; - fm->type = type; - bufptr = fm->message; - for(i = 0; i < argcount; i++) - { - strcpy(bufptr, message_arg[i]); - bufptr += strlen(message_arg[i]) + 1; + SerializeBuffer buf(20); + buf.WriteInt32(0); // unknown + buf.WriteInt32(string_id); + buf.WriteInt32(type); + for (auto &m : message_arg) { + if (m == nullptr) + break; + buf.WriteString(m); } - // since we're moving the pointer the 0 offset is correct - bufptr[0] = '\0'; + buf.WriteInt8(0); // prevent oob in packet translation, maybe clean that up sometime - if(distance>0) - entity_list.QueueCloseClients(this,outapp,false,distance); + auto outapp = std::make_unique(OP_FormattedMessage, buf); + + if (distance > 0) + entity_list.QueueCloseClients(this, outapp.get(), false, distance); else - QueuePacket(outapp); - safe_delete(outapp); + QueuePacket(outapp.get()); } void Client::MessageString(const CZClientMessageString_Struct* msg) @@ -3297,10 +3281,6 @@ void Client::FilteredMessageString(Mob *sender, uint32 type, eqFilterType filter if (!FilteredMessageCheck(sender, filter)) return; - int i = 0, argcount = 0, length = 0; - char *bufptr = nullptr; - const char *message_arg[9] = {0}; - if (type == Chat::Emote) type = 4; @@ -3309,36 +3289,26 @@ void Client::FilteredMessageString(Mob *sender, uint32 type, eqFilterType filter return; } - message_arg[i++] = message1; - message_arg[i++] = message2; - message_arg[i++] = message3; - message_arg[i++] = message4; - message_arg[i++] = message5; - message_arg[i++] = message6; - message_arg[i++] = message7; - message_arg[i++] = message8; - message_arg[i++] = message9; + const char *message_arg[] = { + message1, message2, message3, message4, message5, + message6, message7, message8, message9 + }; - for (; message_arg[argcount]; ++argcount) - length += strlen(message_arg[argcount]) + 1; - - length += 1; - - auto outapp = new EQApplicationPacket(OP_FormattedMessage, sizeof(FormattedMessage_Struct) + length); - FormattedMessage_Struct *fm = (FormattedMessage_Struct *)outapp->pBuffer; - fm->string_id = string_id; - fm->type = type; - bufptr = fm->message; - for (i = 0; i < argcount; i++) { - strcpy(bufptr, message_arg[i]); - bufptr += strlen(message_arg[i]) + 1; + SerializeBuffer buf(20); + buf.WriteInt32(0); // unknown + buf.WriteInt32(string_id); + buf.WriteInt32(type); + for (auto &m : message_arg) { + if (m == nullptr) + break; + buf.WriteString(m); } - // since we're moving the pointer the 0 offset is correct - bufptr[0] = '\0'; + buf.WriteInt8(0); // prevent oob in packet translation, maybe clean that up sometime - QueuePacket(outapp); - safe_delete(outapp); + auto outapp = std::make_unique(OP_FormattedMessage, buf); + + QueuePacket(outapp.get()); } void Client::Tell_StringID(uint32 string_id, const char *who, const char *message) From 7aa5308f9c6c740293247a5efd17bcbfc95cc220 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Mon, 29 Mar 2021 02:52:57 -0500 Subject: [PATCH 010/624] [Scheduler] Event scheduler implementation (#1257) * Event scheduler implementation * Create 2021_02_17_server_scheduled_events.sql * Tweak * Remove unused event [skip ci] * Cleanup [skip ci] * PR adjustments * Database manifest --- common/CMakeLists.txt | 5 + common/content/world_content_service.cpp | 19 + common/content/world_content_service.h | 3 + common/cron/croncpp.h | 876 ++++++++++++++++++ common/database_schema.h | 2 +- common/eqemu_logsys.cpp | 7 +- common/eqemu_logsys.h | 2 + common/eqemu_logsys_log_aliases.h | 10 + common/platform.cpp | 10 +- .../base_server_scheduled_events_repository.h | 427 +++++++++ .../server_scheduled_events_repository.h | 70 ++ common/server_event_scheduler.cpp | 247 +++++ common/server_event_scheduler.h | 57 ++ common/servertalk.h | 5 +- common/version.h | 2 +- .../generators/repository-generator.pl | 2 +- utils/sql/db_update_manifest.txt | 1 + .../2021_02_17_server_scheduled_events.sql | 21 + world/CMakeLists.txt | 2 + world/main.cpp | 6 + world/world_event_scheduler.cpp | 64 ++ world/world_event_scheduler.h | 12 + zone/CMakeLists.txt | 2 + zone/main.cpp | 11 +- zone/worldserver.cpp | 20 +- zone/worldserver.h | 6 + zone/zone.cpp | 17 +- zone/zone.h | 3 +- zone/zone_event_scheduler.cpp | 165 ++++ zone/zone_event_scheduler.h | 14 + zone/zone_store.cpp | 15 +- 31 files changed, 2053 insertions(+), 50 deletions(-) create mode 100644 common/cron/croncpp.h create mode 100644 common/repositories/base/base_server_scheduled_events_repository.h create mode 100644 common/repositories/server_scheduled_events_repository.h create mode 100644 common/server_event_scheduler.cpp create mode 100644 common/server_event_scheduler.h create mode 100644 utils/sql/git/required/2021_02_17_server_scheduled_events.sql create mode 100644 world/world_event_scheduler.cpp create mode 100644 world/world_event_scheduler.h create mode 100644 zone/zone_event_scheduler.cpp create mode 100644 zone/zone_event_scheduler.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index d0611ae65..bb932490c 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -69,6 +69,7 @@ SET(common_sources rulesys.cpp say_link.cpp serialize_buffer.cpp + server_event_scheduler.cpp serverinfo.cpp shareddb.cpp skills.cpp @@ -252,6 +253,7 @@ SET(repositories repositories/base/base_rule_sets_repository.h repositories/base/base_rule_values_repository.h repositories/base/base_saylink_repository.h + repositories/base/base_server_scheduled_events_repository.h repositories/base/base_skill_caps_repository.h repositories/base/base_spawn2_repository.h repositories/base/base_spawnentry_repository.h @@ -415,6 +417,7 @@ SET(repositories repositories/rule_sets_repository.h repositories/rule_values_repository.h repositories/saylink_repository.h + repositories/server_scheduled_events_repository.h repositories/skill_caps_repository.h repositories/spawn2_repository.h repositories/spawnentry_repository.h @@ -461,6 +464,7 @@ SET(common_headers cli/argh.h cli/eqemu_command_handler.h cli/terminal_color.hpp + cron/croncpp.h database/database_dump_service.h data_verification.h database.h @@ -543,6 +547,7 @@ SET(common_headers say_link.h seperator.h serialize_buffer.h + server_event_scheduler.h serverinfo.h servertalk.h shareddb.h diff --git a/common/content/world_content_service.cpp b/common/content/world_content_service.cpp index 949ebcc0d..feb0f5b76 100644 --- a/common/content/world_content_service.cpp +++ b/common/content/world_content_service.cpp @@ -22,6 +22,7 @@ #include "../database.h" #include "../rulesys.h" #include "../eqemu_logsys.h" +#include "../repositories/content_flags_repository.h" WorldContentService::WorldContentService() @@ -99,3 +100,21 @@ bool WorldContentService::IsContentFlagEnabled(const std::string& content_flag) return false; } + +void WorldContentService::ReloadContentFlags(Database &db) +{ + std::vector set_content_flags; + auto content_flags = ContentFlagsRepository::GetWhere(db, "enabled = 1"); + + set_content_flags.reserve(content_flags.size()); + for (auto &flags: content_flags) { + set_content_flags.push_back(flags.flag_name); + } + + LogInfo( + "Enabled content flags [{}]", + implode(", ", set_content_flags) + ); + + SetContentFlags(set_content_flags); +} diff --git a/common/content/world_content_service.h b/common/content/world_content_service.h index 1edc91160..47220b349 100644 --- a/common/content/world_content_service.h +++ b/common/content/world_content_service.h @@ -24,6 +24,8 @@ #include #include +class Database; + namespace Expansion { static const int EXPANSION_ALL = -1; static const int EXPANSION_FILTER_MAX = 99; @@ -165,6 +167,7 @@ public: const std::vector &GetContentFlags() const; bool IsContentFlagEnabled(const std::string& content_flag); void SetContentFlags(std::vector content_flags); + void ReloadContentFlags(Database &db); void SetExpansionContext(); }; diff --git a/common/cron/croncpp.h b/common/cron/croncpp.h new file mode 100644 index 000000000..5a22a7f72 --- /dev/null +++ b/common/cron/croncpp.h @@ -0,0 +1,876 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#if __cplusplus > 201402L +#include +#define CRONCPP_IS_CPP17 +#endif + +namespace cron +{ +#ifdef CRONCPP_IS_CPP17 + #define HAS_STRING_VIEW + #define STRING_VIEW std::string_view + #define STRING_VIEW_NPOS std::string_view::npos + #define CONSTEXPTR constexpr +#else +#define STRING_VIEW std::string const & +#define STRING_VIEW_NPOS std::string::npos +#define CONSTEXPTR +#endif + + using cron_int = uint8_t; + + constexpr std::time_t INVALID_TIME = static_cast(-1); + + constexpr size_t INVALID_CRON_INDEX = static_cast(-1); + + class cronexpr; + + namespace detail + { + enum class cron_field + { + second, + minute, + hour_of_day, + day_of_week, + day_of_month, + month, + year + }; + + template + static bool find_next(cronexpr const & cex, + std::tm& date, + size_t const dot); + } + + struct bad_cronexpr : public std::runtime_error + { + public: + explicit bad_cronexpr(STRING_VIEW message) : + std::runtime_error(message.data()) + {} + }; + + + struct cron_standard_traits + { + static const cron_int CRON_MIN_SECONDS = 0; + static const cron_int CRON_MAX_SECONDS = 59; + + static const cron_int CRON_MIN_MINUTES = 0; + static const cron_int CRON_MAX_MINUTES = 59; + + static const cron_int CRON_MIN_HOURS = 0; + static const cron_int CRON_MAX_HOURS = 23; + + static const cron_int CRON_MIN_DAYS_OF_WEEK = 0; + static const cron_int CRON_MAX_DAYS_OF_WEEK = 6; + + static const cron_int CRON_MIN_DAYS_OF_MONTH = 1; + static const cron_int CRON_MAX_DAYS_OF_MONTH = 31; + + static const cron_int CRON_MIN_MONTHS = 1; + static const cron_int CRON_MAX_MONTHS = 12; + + static const cron_int CRON_MAX_YEARS_DIFF = 4; + +#ifdef CRONCPP_IS_CPP17 + static const inline std::vector DAYS = { "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" }; + static const inline std::vector MONTHS = { "NIL", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; +#else + static std::vector& DAYS() + { + static std::vector days = { "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" }; + return days; + } + + static std::vector& MONTHS() + { + static std::vector months = { "NIL", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; + return months; + } +#endif + }; + + struct cron_oracle_traits + { + static const cron_int CRON_MIN_SECONDS = 0; + static const cron_int CRON_MAX_SECONDS = 59; + + static const cron_int CRON_MIN_MINUTES = 0; + static const cron_int CRON_MAX_MINUTES = 59; + + static const cron_int CRON_MIN_HOURS = 0; + static const cron_int CRON_MAX_HOURS = 23; + + static const cron_int CRON_MIN_DAYS_OF_WEEK = 1; + static const cron_int CRON_MAX_DAYS_OF_WEEK = 7; + + static const cron_int CRON_MIN_DAYS_OF_MONTH = 1; + static const cron_int CRON_MAX_DAYS_OF_MONTH = 31; + + static const cron_int CRON_MIN_MONTHS = 0; + static const cron_int CRON_MAX_MONTHS = 11; + + static const cron_int CRON_MAX_YEARS_DIFF = 4; + +#ifdef CRONCPP_IS_CPP17 + static const inline std::vector DAYS = { "NIL", "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" }; + static const inline std::vector MONTHS = { "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; +#else + + static std::vector& DAYS() + { + static std::vector days = { "NIL", "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" }; + return days; + } + + static std::vector& MONTHS() + { + static std::vector months = { "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; + return months; + } +#endif + }; + + struct cron_quartz_traits + { + static const cron_int CRON_MIN_SECONDS = 0; + static const cron_int CRON_MAX_SECONDS = 59; + + static const cron_int CRON_MIN_MINUTES = 0; + static const cron_int CRON_MAX_MINUTES = 59; + + static const cron_int CRON_MIN_HOURS = 0; + static const cron_int CRON_MAX_HOURS = 23; + + static const cron_int CRON_MIN_DAYS_OF_WEEK = 1; + static const cron_int CRON_MAX_DAYS_OF_WEEK = 7; + + static const cron_int CRON_MIN_DAYS_OF_MONTH = 1; + static const cron_int CRON_MAX_DAYS_OF_MONTH = 31; + + static const cron_int CRON_MIN_MONTHS = 1; + static const cron_int CRON_MAX_MONTHS = 12; + + static const cron_int CRON_MAX_YEARS_DIFF = 4; + +#ifdef CRONCPP_IS_CPP17 + static const inline std::vector DAYS = { "NIL", "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" }; + static const inline std::vector MONTHS = { "NIL", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; +#else + static std::vector& DAYS() + { + static std::vector days = { "NIL", "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" }; + return days; + } + + static std::vector& MONTHS() + { + static std::vector months = { "NIL", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; + return months; + } +#endif + }; + + class cronexpr; + + template + static cronexpr make_cron(STRING_VIEW expr); + + class cronexpr + { + std::bitset<60> seconds; + std::bitset<60> minutes; + std::bitset<24> hours; + std::bitset<7> days_of_week; + std::bitset<31> days_of_month; + std::bitset<12> months; + + friend bool operator==(cronexpr const & e1, cronexpr const & e2); + friend bool operator!=(cronexpr const & e1, cronexpr const & e2); + + template + friend bool detail::find_next(cronexpr const & cex, + std::tm& date, + size_t const dot); + + friend std::string to_string(cronexpr const & cex); + + template + friend cronexpr make_cron(STRING_VIEW expr); + }; + + inline bool operator==(cronexpr const & e1, cronexpr const & e2) + { + return + e1.seconds == e2.seconds && + e1.minutes == e2.minutes && + e1.hours == e2.hours && + e1.days_of_week == e2.days_of_week && + e1.days_of_month == e2.days_of_month && + e1.months == e2.months; + } + + inline bool operator!=(cronexpr const & e1, cronexpr const & e2) + { + return !(e1 == e2); + } + + inline std::string to_string(cronexpr const & cex) + { + return + cex.seconds.to_string() + " " + + cex.minutes.to_string() + " " + + cex.hours.to_string() + " " + + cex.days_of_month.to_string() + " " + + cex.months.to_string() + " " + + cex.days_of_week.to_string(); + } + + namespace utils + { + inline std::time_t tm_to_time(std::tm& date) + { + return std::mktime(&date); + } + + inline std::tm* time_to_tm(std::time_t const * date, std::tm* const out) + { +#ifdef _WIN32 + errno_t err = localtime_s(out, date); + return 0 == err ? out : nullptr; +#else + return localtime_r(date, out); +#endif + } + + inline std::tm to_tm(STRING_VIEW time) + { + std::istringstream str(time.data()); + str.imbue(std::locale(setlocale(LC_ALL, nullptr))); + + std::tm result; + str >> std::get_time(&result, "%Y-%m-%d %H:%M:%S"); + if (str.fail()) throw std::runtime_error("Parsing date failed!"); + + result.tm_isdst = -1; // DST info not available + + return result; + } + + inline std::string to_string(std::tm const & tm) + { + std::ostringstream str; + str.imbue(std::locale(setlocale(LC_ALL, nullptr))); + str << std::put_time(&tm, "%Y-%m-%d %H:%M:%S"); + if (str.fail()) throw std::runtime_error("Writing date failed!"); + + return str.str(); + } + + inline std::string to_upper(std::string text) + { + std::transform(std::begin(text), std::end(text), + std::begin(text), static_cast(std::toupper)); + + return text; + } + + static std::vector split(STRING_VIEW text, char const delimiter) + { + std::vector tokens; + std::string token; + std::istringstream tokenStream(text.data()); + while (std::getline(tokenStream, token, delimiter)) + { + tokens.push_back(token); + } + return tokens; + } + + CONSTEXPTR inline bool contains(STRING_VIEW text, char const ch) noexcept + { + return STRING_VIEW_NPOS != text.find_first_of(ch); + } + } + + namespace detail + { + + inline cron_int to_cron_int(STRING_VIEW text) + { + try + { + return static_cast(std::stoul(text.data())); + } + catch (std::exception const & ex) + { + throw bad_cronexpr(ex.what()); + } + } + + static std::string replace_ordinals( + std::string text, + std::vector const & replacement) + { + for (size_t i = 0; i < replacement.size(); ++i) + { + auto pos = text.find(replacement[i]); + if (std::string::npos != pos) + text.replace(pos, 3 ,std::to_string(i)); + } + + return text; + } + + static std::pair make_range( + STRING_VIEW field, + cron_int const minval, + cron_int const maxval) + { + cron_int first = 0; + cron_int last = 0; + if (field.size() == 1 && field[0] == '*') + { + first = minval; + last = maxval; + } + else if (!utils::contains(field, '-')) + { + first = to_cron_int(field); + last = first; + } + else + { + auto parts = utils::split(field, '-'); + if (parts.size() != 2) + throw bad_cronexpr("Specified range requires two fields"); + + first = to_cron_int(parts[0]); + last = to_cron_int(parts[1]); + } + + if (first > maxval || last > maxval) + { + throw bad_cronexpr("Specified range exceeds maximum"); + } + if (first < minval || last < minval) + { + throw bad_cronexpr("Specified range is less than minimum"); + } + if (first > last) + { + throw bad_cronexpr("Specified range start exceeds range end"); + } + + return { first, last }; + } + + template + static void set_cron_field( + STRING_VIEW value, + std::bitset& target, + cron_int const minval, + cron_int const maxval) + { + if(value.length() > 0 && value[value.length()-1] == ',') + throw bad_cronexpr("Value cannot end with comma"); + + auto fields = utils::split(value, ','); + if (fields.empty()) + throw bad_cronexpr("Expression parsing error"); + + for (auto const & field : fields) + { + if (!utils::contains(field, '/')) + { +#ifdef CRONCPP_IS_CPP17 + auto[first, last] = detail::make_range(field, minval, maxval); +#else + auto range = detail::make_range(field, minval, maxval); + auto first = range.first; + auto last = range.second; +#endif + for (cron_int i = first - minval; i <= last - minval; ++i) + { + target.set(i); + } + } + else + { + auto parts = utils::split(field, '/'); + if (parts.size() != 2) + throw bad_cronexpr("Incrementer must have two fields"); + +#ifdef CRONCPP_IS_CPP17 + auto[first, last] = detail::make_range(parts[0], minval, maxval); +#else + auto range = detail::make_range(parts[0], minval, maxval); + auto first = range.first; + auto last = range.second; +#endif + + if (!utils::contains(parts[0], '-')) + { + last = maxval; + } + + auto delta = detail::to_cron_int(parts[1]); + if(delta <= 0) + throw bad_cronexpr("Incrementer must be a positive value"); + + for (cron_int i = first - minval; i <= last - minval; i += delta) + { + target.set(i); + } + } + } + } + + template + static void set_cron_days_of_week( + std::string value, + std::bitset<7>& target) + { + auto days = utils::to_upper(value); + auto days_replaced = detail::replace_ordinals( + days, +#ifdef CRONCPP_IS_CPP17 + Traits::DAYS +#else + Traits::DAYS() +#endif + ); + + if (days_replaced.size() == 1 && days_replaced[0] == '?') + days_replaced[0] = '*'; + + set_cron_field( + days_replaced, + target, + Traits::CRON_MIN_DAYS_OF_WEEK, + Traits::CRON_MAX_DAYS_OF_WEEK); + } + + template + static void set_cron_days_of_month( + std::string value, + std::bitset<31>& target) + { + if (value.size() == 1 && value[0] == '?') + value[0] = '*'; + + set_cron_field( + value, + target, + Traits::CRON_MIN_DAYS_OF_MONTH, + Traits::CRON_MAX_DAYS_OF_MONTH); + } + + template + static void set_cron_month( + std::string value, + std::bitset<12>& target) + { + auto month = utils::to_upper(value); + auto month_replaced = replace_ordinals( + month, +#ifdef CRONCPP_IS_CPP17 + Traits::MONTHS +#else + Traits::MONTHS() +#endif + ); + + set_cron_field( + month_replaced, + target, + Traits::CRON_MIN_MONTHS, + Traits::CRON_MAX_MONTHS); + } + + template + inline size_t next_set_bit( + std::bitset const & target, + size_t /*minimum*/, + size_t /*maximum*/, + size_t offset) + { + for (auto i = offset; i < N; ++i) + { + if (target.test(i)) return i; + } + + return INVALID_CRON_INDEX; + } + + inline void add_to_field( + std::tm& date, + cron_field const field, + int const val) + { + switch (field) + { + case cron_field::second: + date.tm_sec += val; + break; + case cron_field::minute: + date.tm_min += val; + break; + case cron_field::hour_of_day: + date.tm_hour += val; + break; + case cron_field::day_of_week: + case cron_field::day_of_month: + date.tm_mday += val; + break; + case cron_field::month: + date.tm_mon += val; + break; + case cron_field::year: + date.tm_year += val; + break; + } + + if (INVALID_TIME == utils::tm_to_time(date)) + throw bad_cronexpr("Invalid time expression"); + } + + inline void set_field( + std::tm& date, + cron_field const field, + int const val) + { + switch (field) + { + case cron_field::second: + date.tm_sec = val; + break; + case cron_field::minute: + date.tm_min = val; + break; + case cron_field::hour_of_day: + date.tm_hour = val; + break; + case cron_field::day_of_week: + date.tm_wday = val; + break; + case cron_field::day_of_month: + date.tm_mday = val; + break; + case cron_field::month: + date.tm_mon = val; + break; + case cron_field::year: + date.tm_year = val; + break; + } + + if (INVALID_TIME == utils::tm_to_time(date)) + throw bad_cronexpr("Invalid time expression"); + } + + inline void reset_field( + std::tm& date, + cron_field const field) + { + switch (field) + { + case cron_field::second: + date.tm_sec = 0; + break; + case cron_field::minute: + date.tm_min = 0; + break; + case cron_field::hour_of_day: + date.tm_hour = 0; + break; + case cron_field::day_of_week: + date.tm_wday = 0; + break; + case cron_field::day_of_month: + date.tm_mday = 1; + break; + case cron_field::month: + date.tm_mon = 0; + break; + case cron_field::year: + date.tm_year = 0; + break; + } + + if (INVALID_TIME == utils::tm_to_time(date)) + throw bad_cronexpr("Invalid time expression"); + } + + inline void reset_all_fields( + std::tm& date, + std::bitset<7> const & marked_fields) + { + for (size_t i = 0; i < marked_fields.size(); ++i) + { + if (marked_fields.test(i)) + reset_field(date, static_cast(i)); + } + } + + inline void mark_field( + std::bitset<7> & orders, + cron_field const field) + { + if (!orders.test(static_cast(field))) + orders.set(static_cast(field)); + } + + template + static size_t find_next( + std::bitset const & target, + std::tm& date, + unsigned int const minimum, + unsigned int const maximum, + unsigned int const value, + cron_field const field, + cron_field const next_field, + std::bitset<7> const & marked_fields) + { + auto next_value = next_set_bit(target, minimum, maximum, value); + if (INVALID_CRON_INDEX == next_value) + { + add_to_field(date, next_field, 1); + reset_field(date, field); + next_value = next_set_bit(target, minimum, maximum, 0); + } + + if (INVALID_CRON_INDEX == next_value || next_value != value) + { + set_field(date, field, static_cast(next_value)); + reset_all_fields(date, marked_fields); + } + + return next_value; + } + + template + static size_t find_next_day( + std::tm& date, + std::bitset<31> const & days_of_month, + size_t day_of_month, + std::bitset<7> const & days_of_week, + size_t day_of_week, + std::bitset<7> const & marked_fields) + { + unsigned int count = 0; + unsigned int maximum = 366; + while ( + (!days_of_month.test(day_of_month - Traits::CRON_MIN_DAYS_OF_MONTH) || + !days_of_week.test(day_of_week - Traits::CRON_MIN_DAYS_OF_WEEK)) + && count++ < maximum) + { + add_to_field(date, cron_field::day_of_month, 1); + + day_of_month = date.tm_mday; + day_of_week = date.tm_wday; + + reset_all_fields(date, marked_fields); + } + + return day_of_month; + } + + template + static bool find_next(cronexpr const & cex, + std::tm& date, + size_t const dot) + { + bool res = true; + + std::bitset<7> marked_fields{ 0 }; + std::bitset<7> empty_list{ 0 }; + + unsigned int second = date.tm_sec; + auto updated_second = find_next( + cex.seconds, + date, + Traits::CRON_MIN_SECONDS, + Traits::CRON_MAX_SECONDS, + second, + cron_field::second, + cron_field::minute, + empty_list); + + if (second == updated_second) + { + mark_field(marked_fields, cron_field::second); + } + + unsigned int minute = date.tm_min; + auto update_minute = find_next( + cex.minutes, + date, + Traits::CRON_MIN_MINUTES, + Traits::CRON_MAX_MINUTES, + minute, + cron_field::minute, + cron_field::hour_of_day, + marked_fields); + if (minute == update_minute) + { + mark_field(marked_fields, cron_field::minute); + } + else + { + res = find_next(cex, date, dot); + if (!res) return res; + } + + unsigned int hour = date.tm_hour; + auto updated_hour = find_next( + cex.hours, + date, + Traits::CRON_MIN_HOURS, + Traits::CRON_MAX_HOURS, + hour, + cron_field::hour_of_day, + cron_field::day_of_week, + marked_fields); + if (hour == updated_hour) + { + mark_field(marked_fields, cron_field::hour_of_day); + } + else + { + res = find_next(cex, date, dot); + if (!res) return res; + } + + unsigned int day_of_week = date.tm_wday; + unsigned int day_of_month = date.tm_mday; + auto updated_day_of_month = find_next_day( + date, + cex.days_of_month, + day_of_month, + cex.days_of_week, + day_of_week, + marked_fields); + if (day_of_month == updated_day_of_month) + { + mark_field(marked_fields, cron_field::day_of_month); + } + else + { + res = find_next(cex, date, dot); + if (!res) return res; + } + + unsigned int month = date.tm_mon; + auto updated_month = find_next( + cex.months, + date, + Traits::CRON_MIN_MONTHS, + Traits::CRON_MAX_MONTHS, + month, + cron_field::month, + cron_field::year, + marked_fields); + if (month != updated_month) + { + if (date.tm_year - dot > Traits::CRON_MAX_YEARS_DIFF) + return false; + + res = find_next(cex, date, dot); + if (!res) return res; + } + + return res; + } + } + + template + static cronexpr make_cron(STRING_VIEW expr) + { + cronexpr cex; + + if (expr.empty()) + throw bad_cronexpr("Invalid empty cron expression"); + + auto fields = utils::split(expr, ' '); + fields.erase( + std::remove_if(std::begin(fields), std::end(fields), + [](STRING_VIEW s) {return s.empty(); }), + std::end(fields)); + if (fields.size() != 6) + throw bad_cronexpr("cron expression must have six fields"); + + detail::set_cron_field(fields[0], cex.seconds, Traits::CRON_MIN_SECONDS, Traits::CRON_MAX_SECONDS); + detail::set_cron_field(fields[1], cex.minutes, Traits::CRON_MIN_MINUTES, Traits::CRON_MAX_MINUTES); + detail::set_cron_field(fields[2], cex.hours, Traits::CRON_MIN_HOURS, Traits::CRON_MAX_HOURS); + + detail::set_cron_days_of_week(fields[5], cex.days_of_week); + + detail::set_cron_days_of_month(fields[3], cex.days_of_month); + + detail::set_cron_month(fields[4], cex.months); + + return cex; + } + + template + static std::tm cron_next(cronexpr const & cex, std::tm date) + { + time_t original = utils::tm_to_time(date); + if (INVALID_TIME == original) return {}; + + if (!detail::find_next(cex, date, date.tm_year)) + return {}; + + time_t calculated = utils::tm_to_time(date); + if (INVALID_TIME == calculated) return {}; + + if (calculated == original) + { + add_to_field(date, detail::cron_field::second, 1); + if (!detail::find_next(cex, date, date.tm_year)) + return {}; + } + + return date; + } + + template + static std::time_t cron_next(cronexpr const & cex, std::time_t const & date) + { + std::tm val; + std::tm* dt = utils::time_to_tm(&date, &val); + if (dt == nullptr) return INVALID_TIME; + + time_t original = utils::tm_to_time(*dt); + if (INVALID_TIME == original) return INVALID_TIME; + + if(!detail::find_next(cex, *dt, dt->tm_year)) + return INVALID_TIME; + + time_t calculated = utils::tm_to_time(*dt); + if (INVALID_TIME == calculated) return calculated; + + if (calculated == original) + { + add_to_field(*dt, detail::cron_field::second, 1); + if(!detail::find_next(cex, *dt, dt->tm_year)) + return INVALID_TIME; + } + + return utils::tm_to_time(*dt); + } +} diff --git a/common/database_schema.h b/common/database_schema.h index 95a550d69..eb539cdb9 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -328,7 +328,7 @@ namespace DatabaseSchema { "reports", "respawn_times", "saylink", - + "server_scheduled_events", }; } diff --git a/common/eqemu_logsys.cpp b/common/eqemu_logsys.cpp index 368d8fbec..ad43ee61d 100644 --- a/common/eqemu_logsys.cpp +++ b/common/eqemu_logsys.cpp @@ -127,6 +127,7 @@ void EQEmuLogSys::LoadLogSettingsDefaults() log_settings[Logs::HotReload].log_to_gmsay = static_cast(Logs::General); log_settings[Logs::HotReload].log_to_console = static_cast(Logs::General); log_settings[Logs::Loot].log_to_gmsay = static_cast(Logs::General); + log_settings[Logs::Scheduler].log_to_console = static_cast(Logs::General); /** * RFC 5424 @@ -206,11 +207,7 @@ std::string EQEmuLogSys::FormatOutMessageString( const std::string &in_message ) { - std::string return_string; - - if (IsRfc5424LogCategory(log_category)) { - return_string = "[" + GetPlatformName() + "] "; - } + std::string return_string = "[" + GetPlatformName() + "] "; return return_string + "[" + Logs::LogCategoryName[log_category] + "] " + in_message; } diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index d28478a97..0b33b55a3 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -120,6 +120,7 @@ namespace Logs { Loot, Expeditions, DynamicZones, + Scheduler, MaxCategoryID /* Don't Remove this */ }; @@ -199,6 +200,7 @@ namespace Logs { "Loot", "Expeditions", "DynamicZones", + "Scheduler", }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index e32b43b4f..ddd27335f 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -636,6 +636,16 @@ OutF(LogSys, Logs::Detail, Logs::DynamicZones, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogScheduler(message, ...) do {\ + if (LogSys.log_settings[Logs::Scheduler].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Scheduler, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogSchedulerDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Scheduler].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Scheduler, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + #define Log(debug_level, log_category, message, ...) do {\ if (LogSys.log_settings[log_category].is_category_enabled == 1)\ LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ diff --git a/common/platform.cpp b/common/platform.cpp index b67267155..e5481c339 100644 --- a/common/platform.cpp +++ b/common/platform.cpp @@ -46,15 +46,15 @@ std::string GetPlatformName() { switch (GetExecutablePlatformInt()) { case EQEmuExePlatform::ExePlatformWorld: - return "WorldServer"; + return "World"; case EQEmuExePlatform::ExePlatformQueryServ: - return "QueryServer"; + return "QS"; case EQEmuExePlatform::ExePlatformZone: - return "ZoneServer"; + return "Zone"; case EQEmuExePlatform::ExePlatformUCS: return "UCS"; case EQEmuExePlatform::ExePlatformLogin: - return "LoginServer"; + return "Login"; case EQEmuExePlatform::ExePlatformSocket_Server: return "SocketServer"; case EQEmuExePlatform::ExePlatformSharedMemory: @@ -70,4 +70,4 @@ std::string GetPlatformName() default: return ""; } -} \ No newline at end of file +} diff --git a/common/repositories/base/base_server_scheduled_events_repository.h b/common/repositories/base/base_server_scheduled_events_repository.h new file mode 100644 index 000000000..1cc319d6a --- /dev/null +++ b/common/repositories/base/base_server_scheduled_events_repository.h @@ -0,0 +1,427 @@ +/** + * DO NOT MODIFY THIS FILE + * + * This repository was automatically generated and is NOT to be modified directly. + * Any repository modifications are meant to be made to the repository extending the base. + * Any modifications to base repositories are to be made by the generator only + * + * @generator ./utils/scripts/generators/repository-generator.pl + * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories + */ + +#ifndef EQEMU_BASE_SERVER_SCHEDULED_EVENTS_REPOSITORY_H +#define EQEMU_BASE_SERVER_SCHEDULED_EVENTS_REPOSITORY_H + +#include "../../database.h" +#include "../../string_util.h" + +class BaseServerScheduledEventsRepository { +public: + struct ServerScheduledEvents { + int id; + std::string description; + std::string event_type; + std::string event_data; + int minute_start; + int hour_start; + int day_start; + int month_start; + int year_start; + int minute_end; + int hour_end; + int day_end; + int month_end; + int year_end; + std::string cron_expression; + std::string created_at; + std::string deleted_at; + }; + + static std::string PrimaryKey() + { + return std::string("id"); + } + + static std::vector Columns() + { + return { + "id", + "description", + "event_type", + "event_data", + "minute_start", + "hour_start", + "day_start", + "month_start", + "year_start", + "minute_end", + "hour_end", + "day_end", + "month_end", + "year_end", + "cron_expression", + "created_at", + "deleted_at", + }; + } + + static std::string ColumnsRaw() + { + return std::string(implode(", ", Columns())); + } + + static std::string TableName() + { + return std::string("server_scheduled_events"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + ColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static ServerScheduledEvents NewEntity() + { + ServerScheduledEvents entry{}; + + entry.id = 0; + entry.description = ""; + entry.event_type = ""; + entry.event_data = ""; + entry.minute_start = 0; + entry.hour_start = 0; + entry.day_start = 0; + entry.month_start = 0; + entry.year_start = 0; + entry.minute_end = 0; + entry.hour_end = 0; + entry.day_end = 0; + entry.month_end = 0; + entry.year_end = 0; + entry.cron_expression = ""; + entry.created_at = ""; + entry.deleted_at = ""; + + return entry; + } + + static ServerScheduledEvents GetServerScheduledEventsEntry( + const std::vector &server_scheduled_eventss, + int server_scheduled_events_id + ) + { + for (auto &server_scheduled_events : server_scheduled_eventss) { + if (server_scheduled_events.id == server_scheduled_events_id) { + return server_scheduled_events; + } + } + + return NewEntity(); + } + + static ServerScheduledEvents FindOne( + Database& db, + int server_scheduled_events_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE id = {} LIMIT 1", + BaseSelect(), + server_scheduled_events_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + ServerScheduledEvents entry{}; + + entry.id = atoi(row[0]); + entry.description = row[1] ? row[1] : ""; + entry.event_type = row[2] ? row[2] : ""; + entry.event_data = row[3] ? row[3] : ""; + entry.minute_start = atoi(row[4]); + entry.hour_start = atoi(row[5]); + entry.day_start = atoi(row[6]); + entry.month_start = atoi(row[7]); + entry.year_start = atoi(row[8]); + entry.minute_end = atoi(row[9]); + entry.hour_end = atoi(row[10]); + entry.day_end = atoi(row[11]); + entry.month_end = atoi(row[12]); + entry.year_end = atoi(row[13]); + entry.cron_expression = row[14] ? row[14] : ""; + entry.created_at = row[15] ? row[15] : ""; + entry.deleted_at = row[16] ? row[16] : ""; + + return entry; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int server_scheduled_events_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + server_scheduled_events_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + ServerScheduledEvents server_scheduled_events_entry + ) + { + std::vector update_values; + + auto columns = Columns(); + + update_values.push_back(columns[1] + " = '" + EscapeString(server_scheduled_events_entry.description) + "'"); + update_values.push_back(columns[2] + " = '" + EscapeString(server_scheduled_events_entry.event_type) + "'"); + update_values.push_back(columns[3] + " = '" + EscapeString(server_scheduled_events_entry.event_data) + "'"); + update_values.push_back(columns[4] + " = " + std::to_string(server_scheduled_events_entry.minute_start)); + update_values.push_back(columns[5] + " = " + std::to_string(server_scheduled_events_entry.hour_start)); + update_values.push_back(columns[6] + " = " + std::to_string(server_scheduled_events_entry.day_start)); + update_values.push_back(columns[7] + " = " + std::to_string(server_scheduled_events_entry.month_start)); + update_values.push_back(columns[8] + " = " + std::to_string(server_scheduled_events_entry.year_start)); + update_values.push_back(columns[9] + " = " + std::to_string(server_scheduled_events_entry.minute_end)); + update_values.push_back(columns[10] + " = " + std::to_string(server_scheduled_events_entry.hour_end)); + update_values.push_back(columns[11] + " = " + std::to_string(server_scheduled_events_entry.day_end)); + update_values.push_back(columns[12] + " = " + std::to_string(server_scheduled_events_entry.month_end)); + update_values.push_back(columns[13] + " = " + std::to_string(server_scheduled_events_entry.year_end)); + update_values.push_back(columns[14] + " = '" + EscapeString(server_scheduled_events_entry.cron_expression) + "'"); + update_values.push_back(columns[15] + " = '" + EscapeString(server_scheduled_events_entry.created_at) + "'"); + update_values.push_back(columns[16] + " = '" + EscapeString(server_scheduled_events_entry.deleted_at) + "'"); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + implode(", ", update_values), + PrimaryKey(), + server_scheduled_events_entry.id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static ServerScheduledEvents InsertOne( + Database& db, + ServerScheduledEvents server_scheduled_events_entry + ) + { + std::vector insert_values; + + insert_values.push_back(std::to_string(server_scheduled_events_entry.id)); + insert_values.push_back("'" + EscapeString(server_scheduled_events_entry.description) + "'"); + insert_values.push_back("'" + EscapeString(server_scheduled_events_entry.event_type) + "'"); + insert_values.push_back("'" + EscapeString(server_scheduled_events_entry.event_data) + "'"); + insert_values.push_back(std::to_string(server_scheduled_events_entry.minute_start)); + insert_values.push_back(std::to_string(server_scheduled_events_entry.hour_start)); + insert_values.push_back(std::to_string(server_scheduled_events_entry.day_start)); + insert_values.push_back(std::to_string(server_scheduled_events_entry.month_start)); + insert_values.push_back(std::to_string(server_scheduled_events_entry.year_start)); + insert_values.push_back(std::to_string(server_scheduled_events_entry.minute_end)); + insert_values.push_back(std::to_string(server_scheduled_events_entry.hour_end)); + insert_values.push_back(std::to_string(server_scheduled_events_entry.day_end)); + insert_values.push_back(std::to_string(server_scheduled_events_entry.month_end)); + insert_values.push_back(std::to_string(server_scheduled_events_entry.year_end)); + insert_values.push_back("'" + EscapeString(server_scheduled_events_entry.cron_expression) + "'"); + insert_values.push_back("'" + EscapeString(server_scheduled_events_entry.created_at) + "'"); + insert_values.push_back("'" + EscapeString(server_scheduled_events_entry.deleted_at) + "'"); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseInsert(), + implode(",", insert_values) + ) + ); + + if (results.Success()) { + server_scheduled_events_entry.id = results.LastInsertedID(); + return server_scheduled_events_entry; + } + + server_scheduled_events_entry = NewEntity(); + + return server_scheduled_events_entry; + } + + static int InsertMany( + Database& db, + std::vector server_scheduled_events_entries + ) + { + std::vector insert_chunks; + + for (auto &server_scheduled_events_entry: server_scheduled_events_entries) { + std::vector insert_values; + + insert_values.push_back(std::to_string(server_scheduled_events_entry.id)); + insert_values.push_back("'" + EscapeString(server_scheduled_events_entry.description) + "'"); + insert_values.push_back("'" + EscapeString(server_scheduled_events_entry.event_type) + "'"); + insert_values.push_back("'" + EscapeString(server_scheduled_events_entry.event_data) + "'"); + insert_values.push_back(std::to_string(server_scheduled_events_entry.minute_start)); + insert_values.push_back(std::to_string(server_scheduled_events_entry.hour_start)); + insert_values.push_back(std::to_string(server_scheduled_events_entry.day_start)); + insert_values.push_back(std::to_string(server_scheduled_events_entry.month_start)); + insert_values.push_back(std::to_string(server_scheduled_events_entry.year_start)); + insert_values.push_back(std::to_string(server_scheduled_events_entry.minute_end)); + insert_values.push_back(std::to_string(server_scheduled_events_entry.hour_end)); + insert_values.push_back(std::to_string(server_scheduled_events_entry.day_end)); + insert_values.push_back(std::to_string(server_scheduled_events_entry.month_end)); + insert_values.push_back(std::to_string(server_scheduled_events_entry.year_end)); + insert_values.push_back("'" + EscapeString(server_scheduled_events_entry.cron_expression) + "'"); + insert_values.push_back("'" + EscapeString(server_scheduled_events_entry.created_at) + "'"); + insert_values.push_back("'" + EscapeString(server_scheduled_events_entry.deleted_at) + "'"); + + insert_chunks.push_back("(" + implode(",", insert_values) + ")"); + } + + std::vector insert_values; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseInsert(), + implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static std::vector All(Database& db) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{}", + BaseSelect() + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + ServerScheduledEvents entry{}; + + entry.id = atoi(row[0]); + entry.description = row[1] ? row[1] : ""; + entry.event_type = row[2] ? row[2] : ""; + entry.event_data = row[3] ? row[3] : ""; + entry.minute_start = atoi(row[4]); + entry.hour_start = atoi(row[5]); + entry.day_start = atoi(row[6]); + entry.month_start = atoi(row[7]); + entry.year_start = atoi(row[8]); + entry.minute_end = atoi(row[9]); + entry.hour_end = atoi(row[10]); + entry.day_end = atoi(row[11]); + entry.month_end = atoi(row[12]); + entry.year_end = atoi(row[13]); + entry.cron_expression = row[14] ? row[14] : ""; + entry.created_at = row[15] ? row[15] : ""; + entry.deleted_at = row[16] ? row[16] : ""; + + all_entries.push_back(entry); + } + + return all_entries; + } + + static std::vector GetWhere(Database& db, std::string where_filter) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {}", + BaseSelect(), + where_filter + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + ServerScheduledEvents entry{}; + + entry.id = atoi(row[0]); + entry.description = row[1] ? row[1] : ""; + entry.event_type = row[2] ? row[2] : ""; + entry.event_data = row[3] ? row[3] : ""; + entry.minute_start = atoi(row[4]); + entry.hour_start = atoi(row[5]); + entry.day_start = atoi(row[6]); + entry.month_start = atoi(row[7]); + entry.year_start = atoi(row[8]); + entry.minute_end = atoi(row[9]); + entry.hour_end = atoi(row[10]); + entry.day_end = atoi(row[11]); + entry.month_end = atoi(row[12]); + entry.year_end = atoi(row[13]); + entry.cron_expression = row[14] ? row[14] : ""; + entry.created_at = row[15] ? row[15] : ""; + entry.deleted_at = row[16] ? row[16] : ""; + + all_entries.push_back(entry); + } + + return all_entries; + } + + static int DeleteWhere(Database& db, std::string where_filter) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {}", + TableName(), + where_filter + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int Truncate(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "TRUNCATE TABLE {}", + TableName() + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + +}; + +#endif //EQEMU_BASE_SERVER_SCHEDULED_EVENTS_REPOSITORY_H diff --git a/common/repositories/server_scheduled_events_repository.h b/common/repositories/server_scheduled_events_repository.h new file mode 100644 index 000000000..edcc21dbc --- /dev/null +++ b/common/repositories/server_scheduled_events_repository.h @@ -0,0 +1,70 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) + * + * 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_SERVER_SCHEDULED_EVENTS_REPOSITORY_H +#define EQEMU_SERVER_SCHEDULED_EVENTS_REPOSITORY_H + +#include "../database.h" +#include "../string_util.h" +#include "base/base_server_scheduled_events_repository.h" + +class ServerScheduledEventsRepository: public BaseServerScheduledEventsRepository { +public: + + /** + * This file was auto generated and can be modified and extended upon + * + * Base repository methods are automatically + * generated in the "base" version of this repository. The base repository + * is immutable and to be left untouched, while methods in this class + * are used as extension methods for more specific persistence-layer + * accessors or mutators. + * + * Base Methods (Subject to be expanded upon in time) + * + * Note: Not all tables are designed appropriately to fit functionality with all base methods + * + * InsertOne + * UpdateOne + * DeleteOne + * FindOne + * GetWhere(std::string where_filter) + * DeleteWhere(std::string where_filter) + * InsertMany + * All + * + * Example custom methods in a repository + * + * ServerScheduledEventsRepository::GetByZoneAndVersion(int zone_id, int zone_version) + * ServerScheduledEventsRepository::GetWhereNeverExpires() + * ServerScheduledEventsRepository::GetWhereXAndY() + * ServerScheduledEventsRepository::DeleteWhereXAndY() + * + * Most of the above could be covered by base methods, but if you as a developer + * find yourself re-using logic for other parts of the code, its best to just make a + * method that can be re-used easily elsewhere especially if it can use a base repository + * method and encapsulate filters there + */ + + // Custom extended repository methods here + +}; + +#endif //EQEMU_SERVER_SCHEDULED_EVENTS_REPOSITORY_H diff --git a/common/server_event_scheduler.cpp b/common/server_event_scheduler.cpp new file mode 100644 index 000000000..1034eebfe --- /dev/null +++ b/common/server_event_scheduler.cpp @@ -0,0 +1,247 @@ +#include "../common/database.h" +#include "../common/string_util.h" +#include "server_event_scheduler.h" +#include "../common/cron/croncpp.h" +#include +#include +#include + +ServerEventScheduler::ServerEventScheduler() +{ + m_last_polled_minute = -1; + m_events = {}; + m_active_events = {}; +} + +ServerEventScheduler::~ServerEventScheduler() = default; + +void ServerEventScheduler::LoadScheduledEvents() +{ + if (!ValidateDatabaseConnection()) { + return; + } + + std::time_t time = std::time(nullptr); + std::tm *now = std::localtime(&time); + + m_events = ServerScheduledEventsRepository::GetWhere(*m_database, "deleted_at is null"); + for (auto &e: m_events) { + + auto start = BuildStartTimeFromEvent(e, now); + auto end = BuildEndTimeFromEvent(e, now); + + // data excluded from output because it can be very large + + LogScheduler( + "Loaded Event ({}) [{}] type [{}] start [{}/{}/{} {:02}:{:02}:00] end [{}/{}/{} {:02}:{:02}:00] cron [{}] created [{}]", + e.id, + e.description, + e.event_type, + start.tm_mon + 1, + start.tm_mday, + start.tm_year + 1900, + start.tm_hour, + start.tm_min, + end.tm_mon + 1, + end.tm_mday, + end.tm_year + 1900, + end.tm_hour, + end.tm_min, + e.cron_expression, + e.created_at + ); + } + + LogScheduler("Loaded scheduled events [{}]", m_events.size()); +} + +// checks to see if event is ready to be activated +bool ServerEventScheduler::ValidateEventReadyToActivate( + ServerScheduledEventsRepository::ServerScheduledEvents &e +) +{ + + // if there is a cron expression, it will try to parse it first before falling back to + // alternative time logic + if (!e.cron_expression.empty()) { + try { + auto cron = cron::make_cron(e.cron_expression); + std::time_t cron_now = std::time(nullptr); + std::time_t cron_next = cron::cron_next(cron, cron_now); + + // we have to pad our now window just a tad so we don't miss the cron window + if ((cron_now + 10) >= cron_next) { + LogScheduler("Cron time has been met! Event scheduling ({}) [{}]", e.id, e.description); + return true; + } + + LogSchedulerDetail("Cron now [{}] cron next [{}]\n", cron_now, cron_next); + } + catch (cron::bad_cronexpr const &ex) { + LogScheduler( + "Error: Cron expression error [{}] see [https://github.com/mariusbancila/croncpp#cron-expressions]", + ex.what() + ); + } + + return false; + } + + std::time_t time = std::time(nullptr); + std::tm *now = std::localtime(&time); + time_t now_time_unix = mktime(now); + auto start = BuildStartTimeFromEvent(e, now); + auto end = BuildEndTimeFromEvent(e, now); + time_t start_time_unix = mktime(&start); + + bool doesnt_end = ( + e.year_end == 0 && + e.month_end == 0 && + e.day_end == 0 && + e.hour_end == 0 && + e.minute_end == 0 + ); + + time_t end_time_unix; + if (!doesnt_end) { + end_time_unix = mktime(&end); + } + + if (now_time_unix >= start_time_unix && (doesnt_end || now_time_unix < end_time_unix)) { + LogSchedulerDetail( + "[ValidateEventReadyToActivate] now_time [{}] start_time [{}] doesnt_end [{}] end_time [{}]", + now_time_unix, + start_time_unix, + doesnt_end ? "true" : "false", + end_time_unix + ); + return true; + } + + return false; +} + +ServerEventScheduler *ServerEventScheduler::SetDatabase(Database *db) +{ + m_database = db; + + return this; +} + +bool ServerEventScheduler::ValidateDatabaseConnection() +{ + if (!m_database) { + LogError("[ServerEventScheduler::LoadScheduledEvents] No database connection"); + return false; + } + + return true; +} + +// in this function we simply look at events we have internally and events +// in the database and determine if any edits have been made +// this helps inform decisions to tell all zones to reload their events +bool ServerEventScheduler::CheckIfEventsChanged() +{ + auto events = ServerScheduledEventsRepository::GetWhere(*m_database, "deleted_at is null"); + + // first check if the size changed, if it did this is the easiest step + if (m_events.size() != events.size()) { + LogSchedulerDetail("[CheckIfEventsChanged] Event size has changed"); + m_events = events; + return true; + } + + // compare fields of database fields to internal events to see if any fields changed + for (auto &e: m_events) { + for (auto &dbe: events) { + if (dbe.id == e.id) { + if ( + dbe.description != e.description || + dbe.event_type != e.event_type || + dbe.event_data != e.event_data || + dbe.minute_start != e.minute_start || + dbe.hour_start != e.hour_start || + dbe.day_start != e.day_start || + dbe.month_start != e.month_start || + dbe.year_start != e.year_start || + dbe.minute_end != e.minute_end || + dbe.hour_end != e.hour_end || + dbe.day_end != e.day_end || + dbe.month_end != e.month_end || + dbe.year_end != e.year_end || + dbe.cron_expression != e.cron_expression || + dbe.created_at != e.created_at || + dbe.deleted_at != e.deleted_at + ) { + LogSchedulerDetail("[CheckIfEventsChanged] Field change detected"); + m_events = events; + return true; + } + } + } + } + + return false; +} + +// checks if event is active +bool ServerEventScheduler::IsEventActive(ServerScheduledEventsRepository::ServerScheduledEvents &e) +{ + for (auto &a: m_active_events) { + if (a.id == e.id) { + return true; + } + } + + return false; +} + +bool ServerEventScheduler::RemoveActiveEvent(ServerScheduledEventsRepository::ServerScheduledEvents &e) +{ + m_active_events.erase( + std::remove_if( + m_active_events.begin(), + m_active_events.end(), + [&](ServerScheduledEventsRepository::ServerScheduledEvents const &active_event) { + return active_event.id == e.id; + } + ), + m_active_events.end()); + + return false; +} + +std::tm ServerEventScheduler::BuildStartTimeFromEvent( + ServerScheduledEventsRepository::ServerScheduledEvents &e, + std::tm *now +) +{ + struct tm time{}; + time.tm_year = ((e.year_start > 0) ? e.year_start - 1900 : now->tm_year); + time.tm_mon = ((e.month_start > 0) ? e.month_start - 1 : now->tm_mon); + time.tm_mday = ((e.day_start > 0) ? e.day_start : now->tm_mday); + time.tm_hour = ((e.hour_start > 0) ? e.hour_start : now->tm_hour); + time.tm_min = ((e.minute_start > 0) ? e.minute_start : now->tm_min); + time.tm_sec = 0; + time.tm_isdst = now->tm_isdst; + + return time; +} + +std::tm ServerEventScheduler::BuildEndTimeFromEvent( + ServerScheduledEventsRepository::ServerScheduledEvents &e, + std::tm *now +) +{ + struct tm time{}; + time.tm_year = ((e.year_end > 0) ? e.year_end - 1900 : now->tm_year); + time.tm_mon = ((e.month_end > 0) ? e.month_end - 1 : now->tm_mon); + time.tm_mday = ((e.day_end > 0) ? e.day_end : now->tm_mday); + time.tm_hour = ((e.hour_end > 0) ? e.hour_end : now->tm_hour); + time.tm_min = ((e.minute_end > 0) ? e.minute_end : now->tm_min); + time.tm_sec = 0; + time.tm_isdst = now->tm_isdst; + + return time; +} diff --git a/common/server_event_scheduler.h b/common/server_event_scheduler.h new file mode 100644 index 000000000..5671eaf7c --- /dev/null +++ b/common/server_event_scheduler.h @@ -0,0 +1,57 @@ +#ifndef EQEMU_SERVER_EVENT_SCHEDULER_H +#define EQEMU_SERVER_EVENT_SCHEDULER_H + +#include "../common/repositories/server_scheduled_events_repository.h" +#include +#include + +namespace ServerEvents { + static const std::string EVENT_TYPE_HOT_ZONE_ACTIVE = "hot_zone_activate"; + static const std::string EVENT_TYPE_BROADCAST = "broadcast"; + static const std::string EVENT_TYPE_RELOAD_WORLD = "reload_world"; + static const std::string EVENT_TYPE_RULE_CHANGE = "rule_change"; + static const std::string EVENT_TYPE_CONTENT_FLAG_CHANGE = "content_flag_change"; +} + +class ServerEventScheduler { +public: + virtual ~ServerEventScheduler(); + ServerEventScheduler(); + ServerEventScheduler *SetDatabase(Database *db); + void LoadScheduledEvents(); + bool CheckIfEventsChanged(); + +protected: + + // events directly from the database + std::vector m_events; + + // used to track only when it is convenient to undo an action from an active event + // typically there should be two separate events to turn something on / off + // hotzones use this right now simply to keep us from toggling off the hotzone + // every minute we trigger and then immediately turning it right back on + std::vector m_active_events; + + // simple ticker used to determine when the last polled minute was so that when the minute + // changes we fire checking the scheduler + int m_last_polled_minute; + + // validates an event is currently active or not + bool ValidateEventReadyToActivate(ServerScheduledEventsRepository::ServerScheduledEvents &e); + + // is event active + bool IsEventActive(ServerScheduledEventsRepository::ServerScheduledEvents &e); + + // remove active event + bool RemoveActiveEvent(ServerScheduledEventsRepository::ServerScheduledEvents &e); + + // build time object from event + std::tm BuildStartTimeFromEvent(ServerScheduledEventsRepository::ServerScheduledEvents &e, tm *now); + std::tm BuildEndTimeFromEvent(ServerScheduledEventsRepository::ServerScheduledEvents &e, tm *now); + + // reference to database + Database *m_database; + bool ValidateDatabaseConnection(); +}; + +#endif //EQEMU_SERVER_EVENT_SCHEDULER_H diff --git a/common/servertalk.h b/common/servertalk.h index 384bcc15b..7fbac5f6e 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -222,6 +222,7 @@ #define ServerOP_UCSServerStatusRequest 0x4009 #define ServerOP_UCSServerStatusReply 0x4010 #define ServerOP_HotReloadQuests 0x4011 +#define ServerOP_UpdateSchedulerEvents 0x4012 #define ServerOP_CZCastSpellPlayer 0x4500 #define ServerOP_CZCastSpellGroup 0x4501 @@ -320,7 +321,7 @@ #define ServerOP_QSPlayerDropItem 0x5007 /* Query Serv Generic Packet Flag/Type Enumeration */ -enum { QSG_LFGuild = 0 }; +enum { QSG_LFGuild = 0 }; enum { QSG_LFGuild_PlayerMatches = 0, QSG_LFGuild_UpdatePlayerInfo, QSG_LFGuild_RequestPlayerInfo, QSG_LFGuild_UpdateGuildInfo, QSG_LFGuild_GuildMatches, QSG_LFGuild_RequestGuildInfo }; @@ -1933,7 +1934,7 @@ struct WWRemoveTask_Struct { uint32 task_id; uint8 min_status; uint8 max_status; - + }; struct WWResetActivity_Struct { diff --git a/common/version.h b/common/version.h index 56b4f3b0c..d66f8b060 100644 --- a/common/version.h +++ b/common/version.h @@ -34,7 +34,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9161 +#define CURRENT_BINARY_DATABASE_VERSION 9162 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9027 diff --git a/utils/scripts/generators/repository-generator.pl b/utils/scripts/generators/repository-generator.pl index 838642b98..ce3246dad 100644 --- a/utils/scripts/generators/repository-generator.pl +++ b/utils/scripts/generators/repository-generator.pl @@ -237,7 +237,7 @@ foreach my $table_to_generate (@tables) { elsif ($column_default eq "''") { $default_value = '""'; } - elsif ((trim($column_default) eq "" || $column_default eq "NULL") && $column_type =~ /text|varchar/i) { + elsif ((trim($column_default) eq "" || $column_default eq "NULL") && $column_type =~ /text|varchar|datetime/i) { $default_value = '""'; } diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index df613531c..ba19f8c37 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -415,6 +415,7 @@ 9159|2020_12_22_expedition_system.sql|SELECT * FROM db_version WHERE version >= 9159|empty| 9160|2021_02_14_npc_exp_mod.sql|SHOW COLUMNS from `npc_types` LIKE 'exp_mod'|empty| 9161|2021_02_15_npc_spell_entries_unsigned.sql|SELECT * FROM db_version WHERE version >= 9161|empty| +9162|2021_02_17_server_scheduled_events|SELECT * FROM db_version WHERE version >= 9162|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2021_02_17_server_scheduled_events.sql b/utils/sql/git/required/2021_02_17_server_scheduled_events.sql new file mode 100644 index 000000000..deb69423f --- /dev/null +++ b/utils/sql/git/required/2021_02_17_server_scheduled_events.sql @@ -0,0 +1,21 @@ +CREATE TABLE `server_scheduled_events` +( + `id` int(11) NOT NULL AUTO_INCREMENT, + `description` varchar(255) DEFAULT NULL, + `event_type` varchar(100) DEFAULT NULL, + `event_data` text DEFAULT NULL, + `minute_start` int(11) DEFAULT 0, + `hour_start` int(11) DEFAULT 0, + `day_start` int(11) DEFAULT 0, + `month_start` int(11) DEFAULT 0, + `year_start` int(11) DEFAULT 0, + `minute_end` int(11) DEFAULT 0, + `hour_end` int(11) DEFAULT 0, + `day_end` int(11) DEFAULT 0, + `month_end` int(11) DEFAULT 0, + `year_end` int(11) DEFAULT 0, + `cron_expression` varchar(100) DEFAULT NULL, + `created_at` datetime DEFAULT NULL, + `deleted_at` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; diff --git a/world/CMakeLists.txt b/world/CMakeLists.txt index 183f4a27d..9a7ef5750 100644 --- a/world/CMakeLists.txt +++ b/world/CMakeLists.txt @@ -25,6 +25,7 @@ SET(world_sources web_interface.cpp web_interface_eqw.cpp wguild_mgr.cpp + world_event_scheduler.cpp world_config.cpp world_console_connection.cpp world_server_command_handler.cpp @@ -65,6 +66,7 @@ SET(world_headers world_tcp_connection.h world_server_command_handler.h worlddb.h + world_event_scheduler.h world_store.h zonelist.h zoneserver.h diff --git a/world/main.cpp b/world/main.cpp index c5c67ab87..a094c8b4f 100644 --- a/world/main.cpp +++ b/world/main.cpp @@ -97,6 +97,7 @@ union semun { #include "../common/content/world_content_service.h" #include "../common/repositories/merchantlist_temp_repository.h" #include "world_store.h" +#include "world_event_scheduler.h" WorldStore world_store; ClientList client_list; @@ -107,6 +108,7 @@ UCSConnection UCSLink; QueryServConnection QSLink; LauncherList launcher_list; AdventureManager adventure_manager; +WorldEventScheduler event_scheduler; EQ::Random emu_random; volatile bool RunLoops = true; uint32 numclients = 0; @@ -442,6 +444,8 @@ int main(int argc, char** argv) { content_db.LoadCharacterCreateAllocations(); content_db.LoadCharacterCreateCombos(); + event_scheduler.SetDatabase(&database)->LoadScheduledEvents(); + std::unique_ptr console; if (Config->TelnetEnabled) { LogInfo("Console (TCP) listener started"); @@ -603,6 +607,8 @@ int main(int argc, char** argv) { } } + event_scheduler.Process(&zoneserver_list); + client_list.Process(); if (PurgeInstanceTimer.Check()) { diff --git a/world/world_event_scheduler.cpp b/world/world_event_scheduler.cpp new file mode 100644 index 000000000..444b91965 --- /dev/null +++ b/world/world_event_scheduler.cpp @@ -0,0 +1,64 @@ +#include "world_event_scheduler.h" +#include "../common/servertalk.h" + +void WorldEventScheduler::Process(ZSList *zs_list) +{ + std::time_t time = std::time(nullptr); + std::tm *now = std::localtime(&time); + + // once a minute polling + if (m_last_polled_minute != now->tm_min) { + + // refresh; world polls and tells zones if they should update if there is a change + if (CheckIfEventsChanged()) { + LogSchedulerDetail("Event changes detected, forcing zones to refresh their schedules..."); + auto pack = new ServerPacket(ServerOP_UpdateSchedulerEvents, 0); + zs_list->SendPacket(pack); + safe_delete(pack); + } + + int month = (now->tm_mon + 1); + int year = (now->tm_year + 1900); + + LogSchedulerDetail( + "Polling year [{}] month [{}] day [{}] hour [{}] minute [{}]", + year, + month, + now->tm_mday, + now->tm_hour, + now->tm_min + ); + + for (auto &e: m_events) { + + // discard uninteresting events as its less work to calculate time on events we don't care about + // different processes are interested in different events + if ( + e.event_type != ServerEvents::EVENT_TYPE_BROADCAST && + e.event_type != ServerEvents::EVENT_TYPE_RELOAD_WORLD + ) { + continue; + } + + // validate event is ready to activate and run it + if (ValidateEventReadyToActivate(e)) { + if (e.event_type == ServerEvents::EVENT_TYPE_BROADCAST) { + LogScheduler("Sending broadcast [{}]", e.event_data.c_str()); + zs_list->SendEmoteMessage(nullptr, 0, 0, 15, e.event_data.c_str()); + } + + if (e.event_type == ServerEvents::EVENT_TYPE_RELOAD_WORLD) { + LogScheduler("Sending reload world event [{}]", e.event_data.c_str()); + + auto pack = new ServerPacket(ServerOP_ReloadWorld, sizeof(ReloadWorld_Struct)); + auto *reload_world = (ReloadWorld_Struct *) pack->pBuffer; + reload_world->Option = 1; + zs_list->SendPacket(pack); + safe_delete(pack); + } + } + } + + m_last_polled_minute = now->tm_min; + } +} diff --git a/world/world_event_scheduler.h b/world/world_event_scheduler.h new file mode 100644 index 000000000..b2a6725df --- /dev/null +++ b/world/world_event_scheduler.h @@ -0,0 +1,12 @@ +#ifndef EQEMU_EVENT_SCHEDULER_H +#define EQEMU_EVENT_SCHEDULER_H + +#include "../common/server_event_scheduler.h" +#include "zonelist.h" + +class WorldEventScheduler : public ServerEventScheduler { +public: + void Process(ZSList *zs_list); +}; + +#endif //EQEMU_EVENT_SCHEDULER_H diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index d28c8b85b..0430274f9 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -152,6 +152,7 @@ SET(zone_sources zone.cpp zone_config.cpp zonedb.cpp + zone_event_scheduler.cpp zone_reload.cpp zone_store.cpp zoning.cpp) @@ -265,6 +266,7 @@ SET(zone_headers worldserver.h xtargetautohaters.h zone.h + zone_event_scheduler.h zone_config.h zonedb.h zonedump.h diff --git a/zone/main.cpp b/zone/main.cpp index dc708a517..b81a41f5b 100644 --- a/zone/main.cpp +++ b/zone/main.cpp @@ -78,6 +78,7 @@ #include #include "../common/unix.h" #include "zone_store.h" +#include "zone_event_scheduler.h" #endif @@ -100,8 +101,9 @@ QueryServ *QServ = 0; TaskManager *task_manager = 0; NpcScaleManager *npc_scale_manager; QuestParserCollection *parse = 0; -EQEmuLogSys LogSys; -WorldContentService content_service; +EQEmuLogSys LogSys; +ZoneEventScheduler event_scheduler; +WorldContentService content_service; const SPDat_Spell_Struct* spells; int32 SPDAT_RECORDS = -1; const ZoneConfig *Config; @@ -387,6 +389,8 @@ int main(int argc, char** argv) { ZoneStore::LoadContentFlags(); + event_scheduler.SetDatabase(&database)->LoadScheduledEvents(); + #ifdef BOTS LogInfo("Loading bot commands"); int botretval = bot_command_init(); @@ -430,6 +434,7 @@ int main(int argc, char** argv) { parse->ReloadQuests(); worldserver.Connect(); + worldserver.SetScheduler(&event_scheduler); Timer InterserverTimer(INTERSERVER_TIMER); // does MySQL pings and auto-reconnect #ifdef EQPROFILE @@ -541,11 +546,11 @@ int main(int argc, char** argv) { entity_list.CorpseProcess(); entity_list.TrapProcess(); entity_list.RaidProcess(); - entity_list.Process(); entity_list.MobProcess(); entity_list.BeaconProcess(); entity_list.EncounterProcess(); + event_scheduler.Process(zone, &content_service); if (zone) { if (!zone->Process()) { diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 1b4758ff2..65f43676b 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -55,7 +55,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "zone_config.h" #include "zone_reload.h" - extern EntityList entity_list; extern Zone* zone; extern volatile bool is_zone_loaded; @@ -2815,6 +2814,15 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) break; } + case ServerOP_UpdateSchedulerEvents: { + LogScheduler("Received signal from world to update"); + if (m_zone_scheduler) { + m_zone_scheduler->LoadScheduledEvents(); + } + + break; + } + case ServerOP_HotReloadQuests: { if (!zone) { @@ -3301,3 +3309,13 @@ void WorldServer::OnKeepAlive(EQ::Timer *t) ServerPacket pack(ServerOP_KeepAlive, 0); SendPacket(&pack); } + +ZoneEventScheduler *WorldServer::GetScheduler() const +{ + return m_zone_scheduler; +} + +void WorldServer::SetScheduler(ZoneEventScheduler *scheduler) +{ + WorldServer::m_zone_scheduler = scheduler; +} diff --git a/zone/worldserver.h b/zone/worldserver.h index 1eee0b948..bcccb67b7 100644 --- a/zone/worldserver.h +++ b/zone/worldserver.h @@ -20,6 +20,7 @@ #include "../common/eq_packet_structs.h" #include "../common/net/servertalk_client_connection.h" +#include "zone_event_scheduler.h" class ServerPacket; class EQApplicationPacket; @@ -76,6 +77,11 @@ private: std::unique_ptr m_connection; std::unique_ptr m_keepalive; + + ZoneEventScheduler *m_zone_scheduler; +public: + ZoneEventScheduler *GetScheduler() const; + void SetScheduler(ZoneEventScheduler *scheduler); }; #endif diff --git a/zone/zone.cpp b/zone/zone.cpp index 5fd0d5706..b69e9f2f3 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -941,7 +941,6 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) spawn2_timer(1000), hot_reload_timer(1000), qglobal_purge_timer(30000), - hotzone_timer(120000), m_SafePoint(0.0f,0.0f,0.0f), m_Graveyard(0.0f,0.0f,0.0f,0.0f) { @@ -1582,8 +1581,6 @@ bool Zone::Process() { } } - if(hotzone_timer.Check()) { UpdateHotzone(); } - mMovementManager->Process(); return true; @@ -2602,19 +2599,9 @@ uint32 Zone::GetSpawnKillCount(uint32 in_spawnid) { return 0; } -void Zone::UpdateHotzone() +void Zone::SetIsHotzone(bool is_hotzone) { - std::string query = StringFormat("SELECT hotzone FROM zone WHERE short_name = '%s'", GetShortName()); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) - return; - - if (results.RowCount() == 0) - return; - - auto row = results.begin(); - - is_hotzone = atoi(row[0]) == 0 ? false: true; + Zone::is_hotzone = is_hotzone; } void Zone::RequestUCSServerStatus() { diff --git a/zone/zone.h b/zone/zone.h index e02bfd8c2..8b2e6b57c 100755 --- a/zone/zone.h +++ b/zone/zone.h @@ -289,7 +289,6 @@ public: void SpawnConditionChanged(const SpawnCondition &c, int16 old_value); void SpawnStatus(Mob *client); void StartShutdownTimer(uint32 set_time = (RuleI(Zone, AutoShutdownDelay))); - void UpdateHotzone(); void UpdateQGlobal(uint32 qid, QGlobal newGlobal); void weatherSend(Client *client = nullptr); @@ -356,6 +355,7 @@ public: */ void mod_init(); void mod_repop(); + void SetIsHotzone(bool is_hotzone); private: bool allow_mercs; @@ -401,7 +401,6 @@ private: Timer *Weather_Timer; Timer autoshutdown_timer; Timer clientauth_timer; - Timer hotzone_timer; Timer initgrids_timer; Timer qglobal_purge_timer; ZoneSpellsBlocked *blocked_spells; diff --git a/zone/zone_event_scheduler.cpp b/zone/zone_event_scheduler.cpp new file mode 100644 index 000000000..a300a0489 --- /dev/null +++ b/zone/zone_event_scheduler.cpp @@ -0,0 +1,165 @@ +#include "zone_event_scheduler.h" +#include "../common/rulesys.h" + +void ZoneEventScheduler::Process(Zone *zone, WorldContentService *content_service) +{ + std::time_t time = std::time(nullptr); + std::tm *now = std::localtime(&time); + + // once a minute polling + if (m_last_polled_minute != now->tm_min) { + int month = (now->tm_mon + 1); + int year = (now->tm_year + 1900); + + LogSchedulerDetail( + "Polling year [{}] month [{}] day [{}] hour [{}] minute [{}]", + year, + month, + now->tm_mday, + now->tm_hour, + now->tm_min + ); + + // because stored active events could have a reference of time that has been changed since + // the time has been updated, we need to make sure we update internal fields so that + // the scheduler can properly end events if we set a new end date + SyncEventDataWithActiveEvents(); + + // active events + for (auto &e: m_active_events) { + LogSchedulerDetail("Looping active event [{}]", e.description); + + // if event becomes no longer active + if (!ValidateEventReadyToActivate(e)) { + LogSchedulerDetail("Looping active event validated [{}]", e.event_type); + if (e.event_type == ServerEvents::EVENT_TYPE_HOT_ZONE_ACTIVE) { + LogScheduler("Deactivating event [{}] disabling hotzone status", e.description); + for (auto &short_name: split(e.event_data, ',')) { + if (zone->GetShortName() == short_name) { + zone->SetIsHotzone(false); + break; + } + } + RemoveActiveEvent(e); + } + + if (e.event_type == ServerEvents::EVENT_TYPE_RULE_CHANGE) { + LogScheduler("Deactivating event [{}] resetting rules to normal", e.description); + RuleManager::Instance()->LoadRules(m_database, RuleManager::Instance()->GetActiveRuleset(), true); + + // force active events clear and reapply all active events because we reset the entire state + // ideally if we could revert only the state of which was originally set we would only remove one active event + m_active_events.clear(); + } + + if (e.event_type == ServerEvents::EVENT_TYPE_CONTENT_FLAG_CHANGE) { + auto flag_name = e.event_data; + if (!flag_name.empty()) { + LogScheduler("Deactivating event [{}] resetting content flags", e.description); + content_service->ReloadContentFlags(*m_database); + } + + // force active events clear and reapply all active events because we reset the entire state + // ideally if we could revert only the state of which was originally set we would only remove one active event + m_active_events.clear(); + } + } + } + + // check for active + for (auto &e: m_events) { + + // discard uninteresting events as its less work to calculate time on events we don't care about + // different processes are interested in different events + if ( + e.event_type != ServerEvents::EVENT_TYPE_HOT_ZONE_ACTIVE && + e.event_type != ServerEvents::EVENT_TYPE_CONTENT_FLAG_CHANGE && + e.event_type != ServerEvents::EVENT_TYPE_RULE_CHANGE + ) { + continue; + } + + // the scheduler as of today manipulates events in memory and is preferred to be that way + // the scheduler changes temporary "state" in the server for a period of time for things such as + // hotzone activation, content flag activation, rule value activation + // when these events expire, the events become untoggled in memory + // there can be support for one-time events that are more suitable to run from worlds scheduler + // such as broadcasts, reloads + if (ValidateEventReadyToActivate(e) && !IsEventActive(e)) { + if (e.event_type == ServerEvents::EVENT_TYPE_HOT_ZONE_ACTIVE) { + for (auto &short_name: split(e.event_data, ',')) { + if (zone->GetShortName() == short_name) { + zone->SetIsHotzone(true); + LogScheduler("Activating Event [{}] Enabling zone as hotzone", e.description); + break; + } + } + m_active_events.push_back(e); + } + + if (e.event_type == ServerEvents::EVENT_TYPE_RULE_CHANGE) { + auto params = split(e.event_data, '='); + auto rule_key = params[0]; + auto rule_value = params[1]; + if (!rule_key.empty() && !rule_value.empty()) { + LogScheduler( + "Activating Event [{}] scheduled rule change, setting rule [{}] to [{}]", + e.description, + rule_key, + rule_value + ); + RuleManager::Instance()->SetRule(rule_key.c_str(), rule_value.c_str(), nullptr, false, true); + } + m_active_events.push_back(e); + } + + if (e.event_type == ServerEvents::EVENT_TYPE_CONTENT_FLAG_CHANGE) { + auto flag_name = e.event_data; + if (!flag_name.empty()) { + LogScheduler( + "Activating Event [{}] scheduled content flag change, setting flag [{}] to enabled", + e.description, + flag_name + ); + + auto flags = content_service->GetContentFlags(); + flags.push_back(flag_name); + content_service->SetContentFlags(flags); + m_active_events.push_back(e); + } + } + } + } + + m_last_polled_minute = now->tm_min; + } +} + +// because stored active events could have a reference of time that has been changed since +// the time has been updated, we need to make sure we update internal fields so that +// the scheduler can properly end events if we set a new end date +void ZoneEventScheduler::SyncEventDataWithActiveEvents() +{ + for (auto &a: m_active_events) { + for (auto &e: m_events) { + if (e.id == a.id) { + a.description = e.description; + a.event_type = e.event_type; + a.event_data = e.event_data; + a.minute_start = e.minute_start; + a.hour_start = e.hour_start; + a.day_start = e.day_start; + a.month_start = e.month_start; + a.year_start = e.year_start; + a.minute_end = e.minute_end; + a.hour_end = e.hour_end; + a.day_end = e.day_end; + a.month_end = e.month_end; + a.year_end = e.year_end; + a.cron_expression = e.cron_expression; + a.created_at = e.created_at; + a.deleted_at = e.deleted_at; + } + } + } +} diff --git a/zone/zone_event_scheduler.h b/zone/zone_event_scheduler.h new file mode 100644 index 000000000..a2e6c67ad --- /dev/null +++ b/zone/zone_event_scheduler.h @@ -0,0 +1,14 @@ +#ifndef EQEMU_ZONE_EVENT_SCHEDULER_H +#define EQEMU_ZONE_EVENT_SCHEDULER_H + +#include "../common/server_event_scheduler.h" +#include "zone.h" +#include "../common/content/world_content_service.h" + +class ZoneEventScheduler : public ServerEventScheduler { +public: + void Process(Zone *zone, WorldContentService *content_service); + void SyncEventDataWithActiveEvents(); +}; + +#endif //EQEMU_ZONE_EVENT_SCHEDULER_H diff --git a/zone/zone_store.cpp b/zone/zone_store.cpp index 2a1b2da79..467c3af25 100644 --- a/zone/zone_store.cpp +++ b/zone/zone_store.cpp @@ -146,20 +146,7 @@ ZoneRepository::Zone ZoneStore::GetZone(const char *in_zone_name) */ void ZoneStore::LoadContentFlags() { - std::vector set_content_flags; - auto content_flags = ContentFlagsRepository::GetWhere(database, "enabled = 1"); - - set_content_flags.reserve(content_flags.size()); - for (auto &flags: content_flags) { - set_content_flags.push_back(flags.flag_name); - } - - LogInfo( - "Enabled content flags [{}]", - implode(", ", set_content_flags) - ); - - content_service.SetContentFlags(set_content_flags); + content_service.ReloadContentFlags(database); } /** From 4ac32d89e8cdda7e1c0f22375c30a015e8729bb4 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 29 Mar 2021 03:01:07 -0500 Subject: [PATCH 011/624] [Hotfix] DB Manifest --- utils/sql/db_update_manifest.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index ba19f8c37..566058e3c 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -415,7 +415,7 @@ 9159|2020_12_22_expedition_system.sql|SELECT * FROM db_version WHERE version >= 9159|empty| 9160|2021_02_14_npc_exp_mod.sql|SHOW COLUMNS from `npc_types` LIKE 'exp_mod'|empty| 9161|2021_02_15_npc_spell_entries_unsigned.sql|SELECT * FROM db_version WHERE version >= 9161|empty| -9162|2021_02_17_server_scheduled_events|SELECT * FROM db_version WHERE version >= 9162|empty| +9162|2021_02_17_server_scheduled_events.sql|SELECT * FROM db_version WHERE version >= 9162|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not From 10b1f7e1ca338cc74ba63d448b2a965e37d7679c Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Mon, 29 Mar 2021 19:09:36 -0400 Subject: [PATCH 012/624] Add missing includes to fix windows compile (#1314) --- world/world_event_scheduler.cpp | 1 + zone/zone_event_scheduler.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/world/world_event_scheduler.cpp b/world/world_event_scheduler.cpp index 444b91965..d32d44758 100644 --- a/world/world_event_scheduler.cpp +++ b/world/world_event_scheduler.cpp @@ -1,5 +1,6 @@ #include "world_event_scheduler.h" #include "../common/servertalk.h" +#include void WorldEventScheduler::Process(ZSList *zs_list) { diff --git a/zone/zone_event_scheduler.cpp b/zone/zone_event_scheduler.cpp index a300a0489..c666ce233 100644 --- a/zone/zone_event_scheduler.cpp +++ b/zone/zone_event_scheduler.cpp @@ -1,5 +1,6 @@ #include "zone_event_scheduler.h" #include "../common/rulesys.h" +#include void ZoneEventScheduler::Process(Zone *zone, WorldContentService *content_service) { From d117ce0bf280fde37c297454431aada698051359 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Wed, 31 Mar 2021 00:18:43 -0500 Subject: [PATCH 013/624] [Hotfix] Incorrect Perl usage method (docs) --- zone/embparser_api.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 1ed0df341..b662d5212 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -911,9 +911,9 @@ XS(XS__get_spell_level) { uint8 spell_level = IsValidSpell(spell_id) ? GetSpellLevel(spell_id, class_id) : 0; uint8 server_max_level = RuleI(Character, MaxLevel); - if (spell_level && spell_level > server_max_level) + if (spell_level && spell_level > server_max_level) spell_level = 0; - + XSprePUSH; PUSHu((UV)spell_level); @@ -2953,7 +2953,7 @@ XS(XS__ModifyNPCStat); XS(XS__ModifyNPCStat) { dXSARGS; if (items != 2) - Perl_croak(aTHX_ "Usage: quest::ModifyNPCStat(string key, string value)"); + Perl_croak(aTHX_ "Usage: quest::modifynpcstat(string key, string value)"); quest_manager.ModifyNPCStat(SvPV_nolen(ST(0)), SvPV_nolen(ST(1))); @@ -6469,7 +6469,7 @@ XS(XS__secondstotime) { sv_setpv(TARG, time_string.c_str()); XSprePUSH; PUSHTARG; - XSRETURN(1); + XSRETURN(1); } /* From a9a83a2052269524d6f85f96d90d5b2416adc4fa Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 1 Apr 2021 14:19:29 -0400 Subject: [PATCH 014/624] [Bug Fix] Misc Bard Song Fixes (#1317) * Bards song with a mana cost shouldn't repulse * Bard songs with SE_TemporaryPets shouldn't repulse This is mostly just an issue with Vet AAs, the actually songs have a recast, so are already handled * SE_Familiar should be prevented as well --- zone/spells.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index b4e001ad2..2720b12db 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1038,8 +1038,10 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo // So long recast bard songs need special bard logic, although the effects don't repulse like other songs // This is basically a hack to get that effect // You can hold down the long recast spells, but you only get the effects once + // Songs with mana cost also do not repulse + // AAs that use SE_TemporaryPets or SE_Familiar also do not repulse // TODO fuck bards. - if (spells[spell_id].recast_time == 0) { + if (spells[spell_id].recast_time == 0 && spells[spell_id].mana == 0 && !IsEffectInSpell(spell_id, SE_TemporaryPets) && !IsEffectInSpell(spell_id, SE_Familiar)) { bardsong = spell_id; bardsong_slot = slot; //NOTE: theres a lot more target types than this to think about... From 084b253ff7af4f57f95ab00a63d551eca05f268f Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 1 Apr 2021 14:19:47 -0400 Subject: [PATCH 015/624] Hack to fix repulsable bard charms (#1320) This isn't the exactly the right way, but it's behavior is much closer to live than current for Solon's Song of the Sirens (725), the other bard charms don't repulse due to mana cost --- zone/spells.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/zone/spells.cpp b/zone/spells.cpp index 2720b12db..19b9a8f2b 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2647,6 +2647,12 @@ bool Mob::ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, CastingSlot slo } void Mob::BardPulse(uint16 spell_id, Mob *caster) { + // so for Solon's Song of the Sirens (725) if we're repulsing, we need to skip + // other charms have mana and don't repulse + // This is probably not the ideal place for this, but it will work + if (IsCharmed() && GetOwner() == caster && IsEffectInSpell(spell_id, SE_Charm)) { + return; + } int buffs_i; int buff_count = GetMaxTotalSlots(); for (buffs_i = 0; buffs_i < buff_count; buffs_i++) { From 050e78b1b6e137c97d4cf6d630bdca61261d60a7 Mon Sep 17 00:00:00 2001 From: RoTPvP <77220477+RoT-PvP@users.noreply.github.com> Date: Fri, 2 Apr 2021 20:44:59 -0700 Subject: [PATCH 016/624] Added a check to stop Bard song for Mezz/Stun (#1319) * Added a check to stop Bard song for Mezz/Stun * Cleaned Song stun / mezz stop * Update client_process.cpp * removed bard check & added else where * code clean up Co-authored-by: ProducerZekServer --- zone/spells.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 19b9a8f2b..ed4c6c57a 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4924,11 +4924,13 @@ void Mob::Stun(int duration) if(stunned && stunned_timer.GetRemainingTime() > uint32(duration)) return; - if(IsValidSpell(casting_spell_id) && !spells[casting_spell_id].uninterruptable) { + auto spell_id = bardsong ? bardsong : casting_spell_id; + + if(IsValidSpell(spell_id) && !spells[spell_id].uninterruptable) { int persistent_casting = spellbonuses.PersistantCasting + itembonuses.PersistantCasting + aabonuses.PersistantCasting; if(zone->random.Int(0,99) > persistent_casting) - InterruptSpell(); + InterruptSpell(spell_id); } if(duration > 0) @@ -4984,9 +4986,11 @@ void Mob::Mesmerize() { mezzed = true; - if (casting_spell_id) - InterruptSpell(); + auto spell_id = bardsong ? bardsong : casting_spell_id; + if (spell_id) + InterruptSpell(spell_id); + StopNavigation(); } From 0534a2c6be2b00b5f564ffb001ed0e858ff9c237 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 7 Apr 2021 02:17:30 -0400 Subject: [PATCH 017/624] Switch scheduled hot zone check to search instead of split (#1313) Using search_deliminated_string here is significantly faster than splitting, most likely because of dynamic memory management Some tests on my system: ---------------------------------------------------- Benchmark Time CPU Iterations ---------------------------------------------------- bench_split 864 ns 864 ns 807922 bench_search 35 ns 35 ns 20265205 This test was a case where the string was present somewhere in the middle which gave a ~96% speed up ---------------------------------------------------- Benchmark Time CPU Iterations ---------------------------------------------------- bench_split 936 ns 936 ns 725518 bench_search 61 ns 61 ns 11156359 This test was when the string was not present, which will be the vast majority of times this is actually checked, was ~93% speed up --- zone/zone_event_scheduler.cpp | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/zone/zone_event_scheduler.cpp b/zone/zone_event_scheduler.cpp index c666ce233..657fed78b 100644 --- a/zone/zone_event_scheduler.cpp +++ b/zone/zone_event_scheduler.cpp @@ -35,11 +35,8 @@ void ZoneEventScheduler::Process(Zone *zone, WorldContentService *content_servic LogSchedulerDetail("Looping active event validated [{}]", e.event_type); if (e.event_type == ServerEvents::EVENT_TYPE_HOT_ZONE_ACTIVE) { LogScheduler("Deactivating event [{}] disabling hotzone status", e.description); - for (auto &short_name: split(e.event_data, ',')) { - if (zone->GetShortName() == short_name) { - zone->SetIsHotzone(false); - break; - } + if (search_deliminated_string(e.event_data, zone->GetShortName()) != std::string::npos) { + zone->SetIsHotzone(false); } RemoveActiveEvent(e); } @@ -88,12 +85,9 @@ void ZoneEventScheduler::Process(Zone *zone, WorldContentService *content_servic // such as broadcasts, reloads if (ValidateEventReadyToActivate(e) && !IsEventActive(e)) { if (e.event_type == ServerEvents::EVENT_TYPE_HOT_ZONE_ACTIVE) { - for (auto &short_name: split(e.event_data, ',')) { - if (zone->GetShortName() == short_name) { - zone->SetIsHotzone(true); - LogScheduler("Activating Event [{}] Enabling zone as hotzone", e.description); - break; - } + if (search_deliminated_string(e.event_data, zone->GetShortName()) != std::string::npos) { + zone->SetIsHotzone(true); + LogScheduler("Activating Event [{}] Enabling zone as hotzone", e.description); } m_active_events.push_back(e); } From dadc1b28436abcdee3ffaaa897b344fd64227263 Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Wed, 7 Apr 2021 02:20:35 -0400 Subject: [PATCH 018/624] [Expeditions] Refactor expedition caching (#1315) Add common expedition base class Use repository for zone and world expedition caching World now stores members and leader as Member objects instead of ids This improves readability of the caching methods and lets world cache expedition dzs and members like zone. World also now caches expeditions as unique_ptr which will be necessary for future dz callback lambdas that capture 'this' so addresses don't change on cache vector resizes. --- common/CMakeLists.txt | 2 + common/eq_constants.h | 9 + common/expedition_base.cpp | 93 +++++++ common/expedition_base.h | 74 ++++++ .../base_expedition_lockouts_repository.h | 2 +- .../expedition_lockouts_repository.h | 52 ++++ .../expedition_members_repository.h | 62 +++++ common/repositories/expeditions_repository.h | 94 +++++++ world/dynamic_zone.cpp | 15 -- world/dynamic_zone.h | 4 - world/expedition.cpp | 58 ++--- world/expedition.h | 21 +- world/expedition_database.cpp | 84 ------- world/expedition_database.h | 2 - world/expedition_message.cpp | 10 +- world/expedition_state.cpp | 107 +++++--- world/expedition_state.h | 12 +- world/main.cpp | 2 +- zone/command.cpp | 6 +- zone/dynamic_zone.cpp | 16 -- zone/dynamic_zone.h | 2 - zone/expedition.cpp | 229 +++++------------- zone/expedition.h | 59 +---- zone/expedition_database.cpp | 94 ------- zone/expedition_database.h | 24 -- zone/lua_expedition.cpp | 2 +- zone/perl_expedition.cpp | 4 +- 27 files changed, 580 insertions(+), 559 deletions(-) create mode 100644 common/expedition_base.cpp create mode 100644 common/expedition_base.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index bb932490c..08db08585 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -32,6 +32,7 @@ SET(common_sources eq_stream_proxy.cpp eqtime.cpp event_sub.cpp + expedition_base.cpp expedition_lockout_timer.cpp extprofile.cpp faction.cpp @@ -495,6 +496,7 @@ SET(common_headers eqtime.h errmsg.h event_sub.h + expedition_base.h expedition_lockout_timer.h extprofile.h faction.h diff --git a/common/eq_constants.h b/common/eq_constants.h index cb042043f..b17dcc2be 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -474,4 +474,13 @@ enum class DynamicZoneType Quest }; +enum class ExpeditionMemberStatus : uint8_t +{ + Unknown = 0, + Online, + Offline, + InDynamicZone, + LinkDead +}; + #endif /*COMMON_EQ_CONSTANTS_H*/ diff --git a/common/expedition_base.cpp b/common/expedition_base.cpp new file mode 100644 index 000000000..fa4962706 --- /dev/null +++ b/common/expedition_base.cpp @@ -0,0 +1,93 @@ +#include "expedition_base.h" +#include "repositories/expeditions_repository.h" + +ExpeditionBase::ExpeditionBase(uint32_t id, const std::string& uuid, + const std::string& expedition_name, const ExpeditionMember& leader, + uint32_t min_players, uint32_t max_players +) : + m_id(id), + m_uuid(uuid), + m_expedition_name(expedition_name), + m_leader(leader), + m_min_players(min_players), + m_max_players(max_players) +{ +} + +void ExpeditionBase::LoadRepositoryResult(ExpeditionsRepository::ExpeditionWithLeader&& entry) +{ + m_id = entry.id; + m_uuid = std::move(entry.uuid); + m_expedition_name = std::move(entry.expedition_name); + m_min_players = entry.min_players; + m_max_players = entry.max_players; + m_add_replay_on_join = entry.add_replay_on_join; + m_is_locked = entry.is_locked; + m_leader.char_id = entry.leader_id; + m_leader.name = std::move(entry.leader_name); +} + +void ExpeditionBase::AddMemberFromRepositoryResult( + ExpeditionMembersRepository::MemberWithName&& entry) +{ + auto status = ExpeditionMemberStatus::Unknown; + AddInternalMember({ entry.character_id, std::move(entry.character_name), status }); +} + +void ExpeditionBase::AddInternalMember(const ExpeditionMember& member) +{ + if (!HasMember(member.char_id)) + { + m_members.emplace_back(member); + } +} + +void ExpeditionBase::RemoveInternalMember(uint32_t character_id) +{ + m_members.erase(std::remove_if(m_members.begin(), m_members.end(), + [&](const ExpeditionMember& member) { return member.char_id == character_id; } + ), m_members.end()); +} + + +bool ExpeditionBase::HasMember(uint32_t character_id) +{ + return std::any_of(m_members.begin(), m_members.end(), [&](const ExpeditionMember& member) { + return member.char_id == character_id; + }); +} + +bool ExpeditionBase::HasMember(const std::string& character_name) +{ + return std::any_of(m_members.begin(), m_members.end(), [&](const ExpeditionMember& member) { + return (strcasecmp(member.name.c_str(), character_name.c_str()) == 0); + }); +} + +ExpeditionMember ExpeditionBase::GetMemberData(uint32_t character_id) +{ + auto it = std::find_if(m_members.begin(), m_members.end(), [&](const ExpeditionMember& member) { + return member.char_id == character_id; + }); + + ExpeditionMember member_data; + if (it != m_members.end()) + { + member_data = *it; + } + return member_data; +} + +ExpeditionMember ExpeditionBase::GetMemberData(const std::string& character_name) +{ + auto it = std::find_if(m_members.begin(), m_members.end(), [&](const ExpeditionMember& member) { + return (strcasecmp(member.name.c_str(), character_name.c_str()) == 0); + }); + + ExpeditionMember member_data; + if (it != m_members.end()) + { + member_data = *it; + } + return member_data; +} diff --git a/common/expedition_base.h b/common/expedition_base.h new file mode 100644 index 000000000..aeb072fae --- /dev/null +++ b/common/expedition_base.h @@ -0,0 +1,74 @@ +#ifndef COMMON_EXPEDITION_BASE_H +#define COMMON_EXPEDITION_BASE_H + +#include "eq_constants.h" +#include "repositories/expeditions_repository.h" +#include "repositories/expedition_members_repository.h" +#include +#include +#include + +struct ExpeditionMember +{ + uint32_t char_id = 0; + std::string name; + ExpeditionMemberStatus status = ExpeditionMemberStatus::Online; + + ExpeditionMember() = default; + ExpeditionMember(uint32_t id, const std::string& name_) + : char_id(id), name(name_) {} + ExpeditionMember(uint32_t id, const std::string& name_, ExpeditionMemberStatus status_) + : char_id(id), name(name_), status(status_) {} + + bool IsValid() const { return char_id != 0 && !name.empty(); } +}; + +class ExpeditionBase +{ +public: + virtual ~ExpeditionBase() = default; + ExpeditionBase(const ExpeditionBase&) = default; + ExpeditionBase(ExpeditionBase&&) = default; + ExpeditionBase& operator=(const ExpeditionBase&) = default; + ExpeditionBase& operator=(ExpeditionBase&&) = default; + + uint32_t GetID() const { return m_id; } + uint32_t GetLeaderID() const { return m_leader.char_id; } + uint32_t GetMinPlayers() const { return m_min_players; } + uint32_t GetMaxPlayers() const { return m_max_players; } + uint32_t GetMemberCount() const { return static_cast(m_members.size()); } + const std::string& GetName() const { return m_expedition_name; } + const std::string& GetLeaderName() const { return m_leader.name; } + const std::string& GetUUID() const { return m_uuid; } + const std::vector& GetMembers() const { return m_members; } + + void AddInternalMember(const ExpeditionMember& member); + void ClearInternalMembers() { m_members.clear(); } + bool HasMember(const std::string& character_name); + bool HasMember(uint32_t character_id); + bool IsEmpty() const { return m_members.empty(); } + void RemoveInternalMember(uint32_t character_id); + + void LoadRepositoryResult(ExpeditionsRepository::ExpeditionWithLeader&& entry); + void AddMemberFromRepositoryResult(ExpeditionMembersRepository::MemberWithName&& entry); + +protected: + ExpeditionBase() = default; + ExpeditionBase(uint32_t id, const std::string& uuid, const std::string& expedition_name, + const ExpeditionMember& leader, uint32_t min_players, uint32_t max_players); + + ExpeditionMember GetMemberData(uint32_t character_id); + ExpeditionMember GetMemberData(const std::string& character_name); + + uint32_t m_id = 0; + uint32_t m_min_players = 0; + uint32_t m_max_players = 0; + bool m_is_locked = false; + bool m_add_replay_on_join = true; + std::string m_uuid; + std::string m_expedition_name; + ExpeditionMember m_leader; + std::vector m_members; +}; + +#endif diff --git a/common/repositories/base/base_expedition_lockouts_repository.h b/common/repositories/base/base_expedition_lockouts_repository.h index f2f2bd5be..e9c5ceda2 100644 --- a/common/repositories/base/base_expedition_lockouts_repository.h +++ b/common/repositories/base/base_expedition_lockouts_repository.h @@ -78,7 +78,7 @@ public: entry.id = 0; entry.expedition_id = 0; entry.event_name = ""; - entry.expire_time = current_timestamp(); + entry.expire_time = ""; entry.duration = 0; entry.from_expedition_uuid = ""; diff --git a/common/repositories/expedition_lockouts_repository.h b/common/repositories/expedition_lockouts_repository.h index 0f33c4209..5eab18915 100644 --- a/common/repositories/expedition_lockouts_repository.h +++ b/common/repositories/expedition_lockouts_repository.h @@ -65,6 +65,58 @@ public: // Custom extended repository methods here + struct ExpeditionLockoutsWithTimestamp { + uint32_t id; + uint32_t expedition_id; + std::string event_name; + time_t expire_time; + int duration; + std::string from_expedition_uuid; + }; + + static std::vector GetWithTimestamp( + Database& db, const std::vector& expedition_ids) + { + if (expedition_ids.empty()) + { + return {}; + } + + std::vector all_entries; + + auto results = db.QueryDatabase(fmt::format(SQL( + SELECT + id, + expedition_id, + event_name, + UNIX_TIMESTAMP(expire_time), + duration, + from_expedition_uuid + FROM expedition_lockouts + WHERE expedition_id IN ({}) + ), + fmt::join(expedition_ids, ",") + )); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) + { + ExpeditionLockoutsWithTimestamp entry{}; + + int col = 0; + entry.id = strtoul(row[col++], nullptr, 10); + entry.expedition_id = strtoul(row[col++], nullptr, 10); + entry.event_name = row[col++]; + entry.expire_time = strtoull(row[col++], nullptr, 10); + entry.duration = strtol(row[col++], nullptr, 10); + entry.from_expedition_uuid = row[col++]; + + all_entries.emplace_back(std::move(entry)); + } + + return all_entries; + } }; #endif //EQEMU_EXPEDITION_LOCKOUTS_REPOSITORY_H diff --git a/common/repositories/expedition_members_repository.h b/common/repositories/expedition_members_repository.h index 87304a99b..55b5bddd7 100644 --- a/common/repositories/expedition_members_repository.h +++ b/common/repositories/expedition_members_repository.h @@ -65,6 +65,68 @@ public: // Custom extended repository methods here + struct MemberWithName { + uint32_t id; + uint32_t expedition_id; + uint32_t character_id; + int is_current_member; + std::string character_name; + }; + + static std::string SelectMembersWithNames() + { + return std::string(SQL( + SELECT + expedition_members.id, + expedition_members.expedition_id, + expedition_members.character_id, + expedition_members.is_current_member, + character_data.name + FROM expedition_members + INNER JOIN character_data ON expedition_members.character_id = character_data.id + )); + } + + static std::vector GetWithNames(Database& db, + const std::vector& expedition_ids) + { + if (expedition_ids.empty()) + { + return {}; + } + + std::vector all_entries; + + auto results = db.QueryDatabase(fmt::format(SQL( + {} + WHERE expedition_members.expedition_id IN ({}) + AND expedition_members.is_current_member = TRUE; + ), + SelectMembersWithNames(), + fmt::join(expedition_ids, ",") + )); + + if (results.Success()) + { + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) + { + MemberWithName entry{}; + + int col = 0; + entry.id = strtoul(row[col++], nullptr, 10); + entry.expedition_id = strtoul(row[col++], nullptr, 10); + entry.character_id = strtoul(row[col++], nullptr, 10); + entry.is_current_member = strtoul(row[col++], nullptr, 10); + entry.character_name = row[col++]; + + all_entries.emplace_back(std::move(entry)); + } + } + + return all_entries; + } }; #endif //EQEMU_EXPEDITION_MEMBERS_REPOSITORY_H diff --git a/common/repositories/expeditions_repository.h b/common/repositories/expeditions_repository.h index 3cd2f2352..47c821060 100644 --- a/common/repositories/expeditions_repository.h +++ b/common/repositories/expeditions_repository.h @@ -65,6 +65,100 @@ public: // Custom extended repository methods here + struct ExpeditionWithLeader + { + uint32_t id; + std::string uuid; + uint32_t dynamic_zone_id; + std::string expedition_name; + uint32_t min_players; + uint32_t max_players; + int add_replay_on_join; + int is_locked; + uint32_t leader_id; + std::string leader_name; + }; + + static std::string SelectExpeditionsJoinLeader() + { + return std::string(SQL( + SELECT + expeditions.id, + expeditions.uuid, + expeditions.dynamic_zone_id, + expeditions.expedition_name, + expeditions.min_players, + expeditions.max_players, + expeditions.add_replay_on_join, + expeditions.is_locked, + expeditions.leader_id, + character_data.name leader_name + FROM expeditions + INNER JOIN character_data ON expeditions.leader_id = character_data.id + )); + } + + static ExpeditionWithLeader FillExpeditionWithLeaderFromRow(MySQLRequestRow& row) + { + ExpeditionWithLeader entry{}; + + int col = 0; + entry.id = strtoul(row[col++], nullptr, 10); + entry.uuid = row[col++]; + entry.dynamic_zone_id = strtoul(row[col++], nullptr, 10); + entry.expedition_name = row[col++]; + entry.min_players = strtoul(row[col++], nullptr, 10); + entry.max_players = strtoul(row[col++], nullptr, 10); + entry.add_replay_on_join = strtoul(row[col++], nullptr, 10); + entry.is_locked = strtoul(row[col++], nullptr, 10); + entry.leader_id = strtoul(row[col++], nullptr, 10); + entry.leader_name = row[col++]; + + return entry; + } + + static std::vector GetAllWithLeaderName(Database& db) + { + std::vector all_entries; + + auto results = db.QueryDatabase(fmt::format( + "{} ORDER BY expeditions.id;", + SelectExpeditionsJoinLeader() + )); + + if (results.Success()) + { + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) + { + ExpeditionWithLeader entry = FillExpeditionWithLeaderFromRow(row); + all_entries.emplace_back(std::move(entry)); + } + } + + return all_entries; + } + + static ExpeditionWithLeader GetWithLeaderName(Database& db, uint32_t expedition_id) + { + ExpeditionWithLeader entry{}; + + auto results = db.QueryDatabase(fmt::format( + "{} WHERE expeditions.id = {};", + SelectExpeditionsJoinLeader(), + expedition_id + )); + + if (results.Success() && results.RowCount() > 0) + { + auto row = results.begin(); + entry = FillExpeditionWithLeaderFromRow(row); + } + + return entry; + } + struct CharacterExpedition { uint32_t id; diff --git a/world/dynamic_zone.cpp b/world/dynamic_zone.cpp index a845c5b1c..dad35db82 100644 --- a/world/dynamic_zone.cpp +++ b/world/dynamic_zone.cpp @@ -9,21 +9,6 @@ extern ZSList zoneserver_list; -DynamicZone::DynamicZone( - uint32_t id, uint32_t zone_id, uint32_t instance_id, uint32_t zone_version, - uint32_t start_time, uint32_t duration, DynamicZoneType type -) -{ - m_id = id; - m_instance_id = instance_id; - m_zone_id = zone_id; - m_zone_version = zone_version; - m_start_time = std::chrono::system_clock::from_time_t(start_time); - m_duration = std::chrono::seconds(duration); - m_type = type; - m_expire_time = m_start_time + m_duration; -} - Database& DynamicZone::GetDatabase() { return database; diff --git a/world/dynamic_zone.h b/world/dynamic_zone.h index a82ff9d0e..60eb5b65a 100644 --- a/world/dynamic_zone.h +++ b/world/dynamic_zone.h @@ -19,10 +19,6 @@ class DynamicZone : public DynamicZoneBase public: using DynamicZoneBase::DynamicZoneBase; // inherit base constructors - DynamicZone() = default; - DynamicZone(uint32_t id, uint32_t zone_id, uint32_t instance_id, uint32_t zone_version, - uint32_t start_time, uint32_t duration, DynamicZoneType type); - static DynamicZone* FindDynamicZoneByID(uint32_t dz_id); static void HandleZoneMessage(ServerPacket* pack); diff --git a/world/expedition.cpp b/world/expedition.cpp index a72275c94..723098013 100644 --- a/world/expedition.cpp +++ b/world/expedition.cpp @@ -30,37 +30,25 @@ extern ClientList client_list; extern ZSList zoneserver_list; -Expedition::Expedition(uint32_t expedition_id, const DynamicZone& dz, uint32_t leader_id -) : - m_expedition_id(expedition_id), - m_dynamic_zone(dz), - m_leader_id(leader_id), +Expedition::Expedition() : m_choose_leader_cooldown_timer{ static_cast(RuleI(Expedition, ChooseLeaderCooldownTime)) } { m_warning_cooldown_timer.Enable(); } -void Expedition::AddMember(uint32_t character_id) +void Expedition::SetDynamicZone(DynamicZone&& dz) { - if (!HasMember(character_id)) - { - m_member_ids.emplace_back(character_id); - } -} + dz.SetName(GetName()); + dz.SetLeaderName(GetLeaderName()); -bool Expedition::HasMember(uint32_t character_id) -{ - return std::any_of(m_member_ids.begin(), m_member_ids.end(), - [&](uint32_t member_id) { return member_id == character_id; }); + m_dynamic_zone = std::move(dz); } void Expedition::RemoveMember(uint32_t character_id) { - m_member_ids.erase(std::remove_if(m_member_ids.begin(), m_member_ids.end(), - [&](uint32_t member_id) { return member_id == character_id; } - ), m_member_ids.end()); + RemoveInternalMember(character_id); - if (character_id == m_leader_id) + if (character_id == m_leader.char_id) { ChooseNewLeader(); } @@ -68,7 +56,7 @@ void Expedition::RemoveMember(uint32_t character_id) void Expedition::ChooseNewLeader() { - if (m_member_ids.empty() || !m_choose_leader_cooldown_timer.Check()) + if (m_members.empty() || !m_choose_leader_cooldown_timer.Check()) { m_choose_leader_needed = true; return; @@ -76,34 +64,38 @@ void Expedition::ChooseNewLeader() // we don't track expedition member status in world so may choose a linkdead member // this is fine since it will trigger another change when that member goes offline - auto it = std::find_if(m_member_ids.begin(), m_member_ids.end(), [&](uint32_t member_id) { - auto member_cle = (member_id != m_leader_id) ? client_list.FindCLEByCharacterID(member_id) : nullptr; - return (member_id != m_leader_id && member_cle && member_cle->GetOnline() == CLE_Status::InZone); + auto it = std::find_if(m_members.begin(), m_members.end(), [&](const ExpeditionMember& member) { + if (member.char_id != m_leader.char_id) { + auto member_cle = client_list.FindCLEByCharacterID(member.char_id); + return (member_cle && member_cle->GetOnline() == CLE_Status::InZone); + } + return false; }); - if (it == m_member_ids.end()) + if (it == m_members.end()) { // no online members found, fallback to choosing any member - it = std::find_if(m_member_ids.begin(), m_member_ids.end(), - [&](uint32_t member_id) { return (member_id != m_leader_id); }); + it = std::find_if(m_members.begin(), m_members.end(), + [&](const ExpeditionMember& member) { return (member.char_id != m_leader.char_id); }); } - if (it != m_member_ids.end() && SetNewLeader(*it)) + if (it != m_members.end() && SetNewLeader(*it)) { m_choose_leader_needed = false; } } -bool Expedition::SetNewLeader(uint32_t character_id) +bool Expedition::SetNewLeader(const ExpeditionMember& member) { - if (!HasMember(character_id)) + if (!HasMember(member.char_id)) { return false; } - LogExpeditionsModerate("Replacing [{}] leader [{}] with [{}]", m_expedition_id, m_leader_id, character_id); - ExpeditionDatabase::UpdateLeaderID(m_expedition_id, character_id); - m_leader_id = character_id; + LogExpeditionsModerate("Replacing [{}] leader [{}] with [{}]", m_id, m_leader.name, member.name); + ExpeditionDatabase::UpdateLeaderID(m_id, member.char_id); + m_leader = member; + m_dynamic_zone.SetLeaderName(m_leader.name); SendZonesLeaderChanged(); return true; } @@ -133,7 +125,7 @@ void Expedition::SendZonesLeaderChanged() auto pack = std::make_unique(ServerOP_ExpeditionLeaderChanged, pack_size); auto buf = reinterpret_cast(pack->pBuffer); buf->expedition_id = GetID(); - buf->leader_id = m_leader_id; + buf->leader_id = m_leader.char_id; zoneserver_list.SendPacket(pack.get()); } diff --git a/world/expedition.h b/world/expedition.h index 2f48739df..11ae083ed 100644 --- a/world/expedition.h +++ b/world/expedition.h @@ -22,43 +22,34 @@ #define WORLD_EXPEDITION_H #include "dynamic_zone.h" +#include "../common/expedition_base.h" #include "../common/timer.h" -#include #include -#include -class Expedition +class Expedition : public ExpeditionBase { public: - Expedition() = default; - Expedition(uint32_t expedition_id, const DynamicZone& dz, uint32_t leader_id); + Expedition(); - void AddMember(uint32_t character_id); void RemoveMember(uint32_t character_id); - void RemoveAllMembers() { m_member_ids.clear(); } void CheckExpireWarning(); void CheckLeader(); void ChooseNewLeader(); DynamicZone& GetDynamicZone() { return m_dynamic_zone; } - uint32_t GetID() const { return m_expedition_id; } - bool HasMember(uint32_t character_id); - bool IsEmpty() const { return m_member_ids.empty(); } - bool IsValid() const { return m_expedition_id != 0; } bool Process(); + void SendZonesExpeditionDeleted(); void SendZonesExpireWarning(uint32_t minutes_remaining); - bool SetNewLeader(uint32_t new_leader_id); + void SetDynamicZone(DynamicZone&& dz); + bool SetNewLeader(const ExpeditionMember& member); private: void SendZonesLeaderChanged(); - uint32_t m_expedition_id = 0; - uint32_t m_leader_id = 0; bool m_choose_leader_needed = false; Timer m_choose_leader_cooldown_timer; Timer m_warning_cooldown_timer; DynamicZone m_dynamic_zone; - std::vector m_member_ids; }; #endif diff --git a/world/expedition_database.cpp b/world/expedition_database.cpp index 2f54d7f3d..c3738901a 100644 --- a/world/expedition_database.cpp +++ b/world/expedition_database.cpp @@ -71,90 +71,6 @@ void ExpeditionDatabase::PurgeExpiredCharacterLockouts() database.QueryDatabase(query); } -std::vector ExpeditionDatabase::LoadExpeditions(uint32_t select_expedition_id) -{ - std::vector expeditions; - - std::string query = SQL( - SELECT - expeditions.id, - expeditions.dynamic_zone_id, - instance_list.id, - instance_list.zone, - instance_list.version, - instance_list.start_time, - instance_list.duration, - expeditions.leader_id, - expedition_members.character_id - FROM expeditions - INNER JOIN dynamic_zones ON expeditions.dynamic_zone_id = dynamic_zones.id - INNER JOIN instance_list ON dynamic_zones.instance_id = instance_list.id - INNER JOIN expedition_members ON expedition_members.expedition_id = expeditions.id - AND expedition_members.is_current_member = TRUE - ); - - if (select_expedition_id != 0) - { - query.append(fmt::format(" WHERE expeditions.id = {};", select_expedition_id)); - } - else - { - query.append(" ORDER BY expeditions.id;"); - } - - auto results = database.QueryDatabase(query); - if (results.Success()) - { - uint32_t last_expedition_id = 0; - - for (auto row = results.begin(); row != results.end(); ++row) - { - uint32_t expedition_id = strtoul(row[0], nullptr, 10); - - if (last_expedition_id != expedition_id) - { - DynamicZone dynamic_zone{ - static_cast(strtoul(row[1], nullptr, 10)), // dz_id - static_cast(strtoul(row[3], nullptr, 10)), // dz_zone_id - static_cast(strtoul(row[2], nullptr, 10)), // dz_instance_id - static_cast(strtoul(row[4], nullptr, 10)), // dz_zone_version - static_cast(strtoul(row[5], nullptr, 10)), // start_time - static_cast(strtoul(row[6], nullptr, 10)), // duration - DynamicZoneType::Expedition - }; - - expeditions.emplace_back( - expedition_id, - dynamic_zone, - static_cast(strtoul(row[7], nullptr, 10)) // leader_id - ); - } - - last_expedition_id = expedition_id; - - uint32_t member_id = static_cast(strtoul(row[8], nullptr, 10)); - expeditions.back().AddMember(member_id); - } - } - - return expeditions; -} - -Expedition ExpeditionDatabase::LoadExpedition(uint32_t expedition_id) -{ - LogExpeditions("Loading expedition [{}] for world cache", expedition_id); - - Expedition expedition; - - auto expeditions = LoadExpeditions(expedition_id); - if (!expeditions.empty()) - { - expedition = expeditions.front(); - } - - return expedition; -} - void ExpeditionDatabase::DeleteExpeditions(const std::vector& expedition_ids) { LogExpeditionsDetail("Deleting [{}] expedition(s)", expedition_ids.size()); diff --git a/world/expedition_database.h b/world/expedition_database.h index 8342a292a..b86fd8b5d 100644 --- a/world/expedition_database.h +++ b/world/expedition_database.h @@ -29,8 +29,6 @@ class Expedition; namespace ExpeditionDatabase { void DeleteExpeditions(const std::vector& expedition_ids); - std::vector LoadExpeditions(uint32_t select_expedition_id = 0); - Expedition LoadExpedition(uint32_t expedition_id); void MoveMembersToSafeReturn(const std::vector& expedition_ids); void PurgeExpiredExpeditions(); void PurgeExpiredCharacterLockouts(); diff --git a/world/expedition_message.cpp b/world/expedition_message.cpp index 46ecfa9cc..999dfd44a 100644 --- a/world/expedition_message.cpp +++ b/world/expedition_message.cpp @@ -43,22 +43,22 @@ void ExpeditionMessage::HandleZoneMessage(ServerPacket* pack) case ServerOP_ExpeditionCreate: { auto buf = reinterpret_cast(pack->pBuffer); - expedition_state.AddExpedition(buf->expedition_id); + expedition_state.CacheFromDatabase(buf->expedition_id); zoneserver_list.SendPacket(pack); break; } case ServerOP_ExpeditionMemberChange: { auto buf = reinterpret_cast(pack->pBuffer); - expedition_state.MemberChange(buf->expedition_id, buf->char_id, buf->removed); + expedition_state.MemberChange(buf->expedition_id, { buf->char_id, buf->char_name }, buf->removed); zoneserver_list.SendPacket(pack); break; } case ServerOP_ExpeditionMemberSwap: { auto buf = reinterpret_cast(pack->pBuffer); - expedition_state.MemberChange(buf->expedition_id, buf->add_char_id, false); - expedition_state.MemberChange(buf->expedition_id, buf->remove_char_id, true); + expedition_state.MemberChange(buf->expedition_id, { buf->add_char_id, buf->add_char_name }, false); + expedition_state.MemberChange(buf->expedition_id, { buf->remove_char_id, buf->remove_char_name }, true); zoneserver_list.SendPacket(pack); break; } @@ -141,7 +141,7 @@ void ExpeditionMessage::MakeLeader(ServerPacket* pack) auto expedition = expedition_state.GetExpedition(buf->expedition_id); if (expedition) { - buf->is_success = expedition->SetNewLeader(new_leader_cle->CharID()); + buf->is_success = expedition->SetNewLeader({ new_leader_cle->CharID(), new_leader_cle->name() }); } buf->is_online = true; diff --git a/world/expedition_state.cpp b/world/expedition_state.cpp index 815ef6c58..19693e279 100644 --- a/world/expedition_state.cpp +++ b/world/expedition_state.cpp @@ -21,7 +21,9 @@ #include "expedition_state.h" #include "expedition.h" #include "expedition_database.h" +#include "worlddb.h" #include "../common/eqemu_logsys.h" +#include "../common/repositories/expedition_members_repository.h" #include ExpeditionState expedition_state; @@ -29,66 +31,99 @@ ExpeditionState expedition_state; Expedition* ExpeditionState::GetExpedition(uint32_t expedition_id) { auto it = std::find_if(m_expeditions.begin(), m_expeditions.end(), - [&](const Expedition& expedition) { return expedition.GetID() == expedition_id; }); + [&](const std::unique_ptr& expedition) { + return expedition->GetID() == expedition_id; + }); - return (it != m_expeditions.end()) ? &(*it) : nullptr; + return (it != m_expeditions.end()) ? it->get() : nullptr; } Expedition* ExpeditionState::GetExpeditionByDynamicZoneID(uint32_t dz_id) { auto it = std::find_if(m_expeditions.begin(), m_expeditions.end(), - [&](Expedition& expedition) { return expedition.GetDynamicZone().GetID() == dz_id; }); + [&](const std::unique_ptr& expedition) { + return expedition->GetDynamicZone().GetID() == dz_id; + }); - return (it != m_expeditions.end()) ? &(*it) : nullptr; + return (it != m_expeditions.end()) ? it->get() : nullptr; } -void ExpeditionState::LoadActiveExpeditions() +void ExpeditionState::CacheFromDatabase(uint32_t expedition_id) { - BenchTimer benchmark; - - m_expeditions = ExpeditionDatabase::LoadExpeditions(); - - auto elapsed = benchmark.elapsed(); - LogExpeditions("World caching [{}] expeditions took [{}s]", m_expeditions.size(), elapsed); -} - -void ExpeditionState::AddExpedition(uint32_t expedition_id) -{ - if (expedition_id == 0) + if (expedition_id == 0 || GetExpedition(expedition_id)) { return; } - auto expedition = ExpeditionDatabase::LoadExpedition(expedition_id); + auto expedition = ExpeditionsRepository::GetWithLeaderName(database, expedition_id); + CacheExpeditions({ std::move(expedition) }); +} - if (expedition.IsValid()) +void ExpeditionState::CacheAllFromDatabase() +{ + BenchTimer benchmark; + + auto expeditions = ExpeditionsRepository::GetAllWithLeaderName(database); + m_expeditions.clear(); + m_expeditions.reserve(expeditions.size()); + + CacheExpeditions(std::move(expeditions)); + + LogExpeditions("Caching [{}] expedition(s) took [{}s]", m_expeditions.size(), benchmark.elapsed()); +} + +void ExpeditionState::CacheExpeditions( + std::vector&& expedition_entries) +{ + // bulk load expedition dzs and members before caching + std::vector expedition_ids; + std::vector dynamic_zone_ids; + for (const auto& entry : expedition_entries) { - auto existing_expedition = GetExpedition(expedition_id); - if (!existing_expedition) + expedition_ids.emplace_back(entry.id); + dynamic_zone_ids.emplace_back(entry.dynamic_zone_id); + } + + auto dynamic_zones = DynamicZonesRepository::GetWithInstance(database, dynamic_zone_ids); + auto expedition_members = ExpeditionMembersRepository::GetWithNames(database, expedition_ids); + + for (auto& entry : expedition_entries) + { + auto expedition = std::make_unique(); + expedition->LoadRepositoryResult(std::move(entry)); + + auto dz_entry_iter = std::find_if(dynamic_zones.begin(), dynamic_zones.end(), + [&](const DynamicZonesRepository::DynamicZoneInstance& dz_entry) { + return dz_entry.id == entry.dynamic_zone_id; + }); + + if (dz_entry_iter != dynamic_zones.end()) { - m_expeditions.emplace_back(expedition); + expedition->SetDynamicZone(std::move(*dz_entry_iter)); } + + for (auto& member : expedition_members) + { + if (member.expedition_id == expedition->GetID()) + { + expedition->AddMemberFromRepositoryResult(std::move(member)); + } + } + + m_expeditions.emplace_back(std::move(expedition)); } } -void ExpeditionState::RemoveExpedition(uint32_t expedition_id) -{ - m_expeditions.erase(std::remove_if(m_expeditions.begin(), m_expeditions.end(), - [&](const Expedition& expedition) { - return expedition.GetID() == expedition_id; - } - ), m_expeditions.end()); -} - -void ExpeditionState::MemberChange(uint32_t expedition_id, uint32_t character_id, bool remove) +void ExpeditionState::MemberChange( + uint32_t expedition_id, const ExpeditionMember& member, bool remove) { auto expedition = GetExpedition(expedition_id); if (expedition) { if (remove) { - expedition->RemoveMember(character_id); + expedition->RemoveMember(member.char_id); } else { - expedition->AddMember(character_id); + expedition->AddInternalMember(member); } } } @@ -98,7 +133,7 @@ void ExpeditionState::RemoveAllMembers(uint32_t expedition_id) auto expedition = GetExpedition(expedition_id); if (expedition) { - expedition->RemoveAllMembers(); + expedition->ClearInternalMembers(); } } @@ -113,10 +148,10 @@ void ExpeditionState::Process() for (auto it = m_expeditions.begin(); it != m_expeditions.end();) { - bool is_deleted = it->Process(); + bool is_deleted = (*it)->Process(); if (is_deleted) { - expedition_ids.emplace_back(it->GetID()); + expedition_ids.emplace_back((*it)->GetID()); } it = is_deleted ? m_expeditions.erase(it) : it + 1; } diff --git a/world/expedition_state.h b/world/expedition_state.h index d7fb27d09..da8cdb306 100644 --- a/world/expedition_state.h +++ b/world/expedition_state.h @@ -21,6 +21,7 @@ #ifndef WORLD_EXPEDITION_STATE_H #define WORLD_EXPEDITION_STATE_H +#include "../common/repositories/expeditions_repository.h" #include "../common/rulesys.h" #include "../common/timer.h" #include @@ -29,21 +30,22 @@ extern class ExpeditionState expedition_state; class Expedition; +struct ExpeditionMember; class ExpeditionState { public: - void AddExpedition(uint32_t expedition_id); + void CacheExpeditions(std::vector&& expedition_entries); + void CacheFromDatabase(uint32_t expedition_id); + void CacheAllFromDatabase(); Expedition* GetExpedition(uint32_t expedition_id); Expedition* GetExpeditionByDynamicZoneID(uint32_t dz_id); - void LoadActiveExpeditions(); - void MemberChange(uint32_t expedition_id, uint32_t character_id, bool remove); + void MemberChange(uint32_t expedition_id, const ExpeditionMember& member, bool remove); void Process(); void RemoveAllMembers(uint32_t expedition_id); - void RemoveExpedition(uint32_t expedition_id); private: - std::vector m_expeditions; + std::vector> m_expeditions; Timer m_process_throttle_timer{static_cast(RuleI(DynamicZone, WorldProcessRate))}; }; diff --git a/world/main.cpp b/world/main.cpp index a094c8b4f..f150db0e4 100644 --- a/world/main.cpp +++ b/world/main.cpp @@ -438,7 +438,7 @@ int main(int argc, char** argv) { PurgeInstanceTimer.Start(450000); LogInfo("Loading active expeditions"); - expedition_state.LoadActiveExpeditions(); + expedition_state.CacheAllFromDatabase(); LogInfo("Loading char create info"); content_db.LoadCharacterCreateAllocations(); diff --git a/zone/command.cpp b/zone/command.cpp index 709bb92c4..e49dea4d2 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -6900,20 +6900,20 @@ void command_dz(Client* c, const Seperator* sep) auto leader_saylink = EQ::SayLinkEngine::GenerateQuestSaylink(fmt::format( "#goto {}", expedition->GetLeaderName()), false, expedition->GetLeaderName()); auto zone_saylink = EQ::SayLinkEngine::GenerateQuestSaylink(fmt::format( - "#zoneinstance {}", expedition->GetInstanceID()), false, "zone"); + "#zoneinstance {}", expedition->GetDynamicZone().GetInstanceID()), false, "zone"); auto seconds = expedition->GetDynamicZone().GetSecondsRemaining(); c->Message(Chat::White, fmt::format( "expedition id: [{}] dz id: [{}] name: [{}] leader: [{}] {}: [{}]:[{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]", expedition->GetID(), - expedition->GetDynamicZoneID(), + expedition->GetDynamicZone().GetID(), expedition->GetName(), leader_saylink, zone_saylink, ZoneName(expedition->GetDynamicZone().GetZoneID()), expedition->GetDynamicZone().GetZoneID(), - expedition->GetInstanceID(), + expedition->GetDynamicZone().GetInstanceID(), expedition->GetDynamicZone().GetZoneVersion(), expedition->GetMemberCount(), seconds / 3600, // hours diff --git a/zone/dynamic_zone.cpp b/zone/dynamic_zone.cpp index 9d9f35012..0b19e5ade 100644 --- a/zone/dynamic_zone.cpp +++ b/zone/dynamic_zone.cpp @@ -64,22 +64,6 @@ DynamicZone* DynamicZone::FindDynamicZoneByID(uint32_t dz_id) return nullptr; } -std::unordered_map DynamicZone::LoadMultipleDzFromDatabase( - const std::vector& dynamic_zone_ids) -{ - LogDynamicZonesDetail("Loading dynamic zone data for [{}] instances", dynamic_zone_ids.size()); - - std::unordered_map dynamic_zones; - - auto entries = DynamicZonesRepository::GetWithInstance(database, dynamic_zone_ids); - for (auto& entry : entries) - { - dynamic_zones.emplace(entry.id, std::move(entry)); - } - - return dynamic_zones; -} - void DynamicZone::StartAllClientRemovalTimers() { for (const auto& client_iter : entity_list.GetClientList()) diff --git a/zone/dynamic_zone.h b/zone/dynamic_zone.h index 98949953d..3b421330a 100644 --- a/zone/dynamic_zone.h +++ b/zone/dynamic_zone.h @@ -42,8 +42,6 @@ public: DynamicZone(uint32_t zone_id, uint32_t version, uint32_t duration, DynamicZoneType type); static DynamicZone* FindDynamicZoneByID(uint32_t dz_id); - static std::unordered_map LoadMultipleDzFromDatabase( - const std::vector& dynamic_zone_ids); static void HandleWorldMessage(ServerPacket* pack); void SetSecondsRemaining(uint32_t seconds_remaining) override; diff --git a/zone/expedition.cpp b/zone/expedition.cpp index 27bb86079..0c1481d7d 100644 --- a/zone/expedition.cpp +++ b/zone/expedition.cpp @@ -27,6 +27,8 @@ #include "zonedb.h" #include "../common/eqemu_logsys.h" #include "../common/expedition_lockout_timer.h" +#include "../common/repositories/expedition_lockouts_repository.h" +#include "../common/repositories/expedition_members_repository.h" #include "../common/util/uuid.h" extern WorldServer worldserver; @@ -49,13 +51,7 @@ const int32_t Expedition::EVENT_TIMER_ID = 1; Expedition::Expedition( uint32_t id, const std::string& uuid, DynamicZone&& dz, const std::string& expedition_name, const ExpeditionMember& leader, uint32_t min_players, uint32_t max_players -) : - m_id(id), - m_uuid(uuid), - m_expedition_name(expedition_name), - m_leader(leader), - m_min_players(min_players), - m_max_players(max_players) +) : ExpeditionBase(id, uuid, expedition_name, leader, min_players, max_players) { SetDynamicZone(std::move(dz)); } @@ -122,7 +118,7 @@ Expedition* Expedition::TryCreate( "Created [{}] [{}] instance id: [{}] leader: [{}] minplayers: [{}] maxplayers: [{}]", expedition->GetID(), expedition->GetName(), - expedition->GetInstanceID(), + expedition->GetDynamicZone().GetInstanceID(), expedition->GetLeaderName(), expedition->GetMinPlayers(), expedition->GetMaxPlayers() @@ -150,113 +146,88 @@ Expedition* Expedition::TryCreate( return nullptr; } -void Expedition::CacheExpeditions(MySQLRequestResult& results) +void Expedition::CacheExpeditions( + std::vector&& expedition_entries) { - if (!results.Success() || !zone) + if (!zone) { return; } + // bulk load expedition dzs, members, and internal lockouts before caching std::vector expedition_ids; - std::vector dynamic_zone_ids;; - std::vector> expedition_character_ids; - - using col = LoadExpeditionColumns::eLoadExpeditionColumns; - - uint32_t last_expedition_id = 0; - - for (auto row = results.begin(); row != results.end(); ++row) + std::vector dynamic_zone_ids; + for (const auto& entry : expedition_entries) { - auto expedition_id = strtoul(row[col::id], nullptr, 10); + expedition_ids.emplace_back(entry.id); + dynamic_zone_ids.emplace_back(entry.dynamic_zone_id); + } - if (expedition_id != last_expedition_id) + auto dynamic_zones = DynamicZonesRepository::GetWithInstance(database, dynamic_zone_ids); + auto expedition_members = ExpeditionMembersRepository::GetWithNames(database, expedition_ids); + auto expedition_lockouts = ExpeditionLockoutsRepository::GetWithTimestamp(database, expedition_ids); + + std::vector> expedition_character_ids; // for online status request + + for (auto& entry : expedition_entries) + { + auto expedition = std::make_unique(); + expedition->LoadRepositoryResult(std::move(entry)); + + auto dz_entry_iter = std::find_if(dynamic_zones.begin(), dynamic_zones.end(), + [&](const DynamicZonesRepository::DynamicZoneInstance& dz_entry) { + return dz_entry.id == entry.dynamic_zone_id; + }); + + if (dz_entry_iter != dynamic_zones.end()) { - expedition_ids.emplace_back(expedition_id); - - uint32_t leader_id = strtoul(row[col::leader_id], nullptr, 10); - uint32_t dynamic_zone_id = strtoul(row[col::dz_id], nullptr, 10); - - dynamic_zone_ids.emplace_back(dynamic_zone_id); - - std::unique_ptr expedition = std::make_unique( - expedition_id, - row[col::uuid], // expedition uuid - DynamicZone{ dynamic_zone_id }, - row[col::expedition_name], // expedition name - ExpeditionMember{ leader_id, row[col::leader_name] }, // expedition leader id, name - strtoul(row[col::min_players], nullptr, 10), // min_players - strtoul(row[col::max_players], nullptr, 10) // max_players - ); - - bool add_replay_on_join = (strtoul(row[col::add_replay_on_join], nullptr, 10) != 0); - bool is_locked = (strtoul(row[col::is_locked], nullptr, 10) != 0); - - expedition->SetReplayLockoutOnMemberJoin(add_replay_on_join); - expedition->SetLocked(is_locked, ExpeditionLockMessage::None); - - zone->expedition_cache.emplace(expedition_id, std::move(expedition)); + expedition->SetDynamicZone(std::move(*dz_entry_iter)); } - last_expedition_id = expedition_id; - - // looping expedition members - auto current_expedition = Expedition::FindCachedExpeditionByID(last_expedition_id); - if (current_expedition) + for (auto& member : expedition_members) { - auto member_id = strtoul(row[col::member_id], nullptr, 10); - current_expedition->AddInternalMember( - row[col::member_name], member_id, ExpeditionMemberStatus::Offline); - expedition_character_ids.emplace_back(expedition_id, member_id); + if (member.expedition_id == expedition->GetID()) + { + expedition->AddMemberFromRepositoryResult(std::move(member)); + expedition_character_ids.emplace_back(expedition->GetID(), member.character_id); + } } + + for (auto& lockout_entry : expedition_lockouts) + { + if (lockout_entry.expedition_id == expedition->GetID()) + { + ExpeditionLockoutTimer lockout{ + std::move(lockout_entry.from_expedition_uuid), + expedition->GetName(), + std::move(lockout_entry.event_name), + static_cast(lockout_entry.expire_time), + static_cast(lockout_entry.duration) + }; + + std::string event_name = lockout.GetEventName(); // copy for key since we're moving it + expedition->m_lockouts.emplace(std::move(event_name), std::move(lockout)); + } + } + + auto inserted = zone->expedition_cache.emplace(entry.id, std::move(expedition)); + inserted.first->second->SendUpdatesToZoneMembers(); } // ask world for online members from all cached expeditions at once Expedition::SendWorldGetOnlineMembers(expedition_character_ids); - - // bulk load dynamic zone data and expedition lockouts for cached expeditions - auto dynamic_zones = DynamicZone::LoadMultipleDzFromDatabase(dynamic_zone_ids); - auto expedition_lockouts = ExpeditionDatabase::LoadMultipleExpeditionLockouts(expedition_ids); - - for (const auto& expedition_id : expedition_ids) - { - auto expedition = Expedition::FindCachedExpeditionByID(expedition_id); - if (expedition) - { - auto dz_iter = dynamic_zones.find(expedition->GetDynamicZoneID()); - if (dz_iter != dynamic_zones.end()) - { - expedition->SetDynamicZone(std::move(dz_iter->second)); - } - - auto lockout_iter = expedition_lockouts.find(expedition->GetID()); - if (lockout_iter != expedition_lockouts.end()) - { - expedition->m_lockouts = lockout_iter->second; - } - - // send member updates now that all data is loaded for the cached expedition - expedition->SendUpdatesToZoneMembers(); - } - } } void Expedition::CacheFromDatabase(uint32_t expedition_id) { - if (zone) + if (zone && expedition_id != 0) { BenchTimer benchmark; - auto results = ExpeditionDatabase::LoadExpedition(expedition_id); - if (!results.Success()) - { - LogExpeditions("Failed to load Expedition [{}] for zone cache", expedition_id); - return; - } + auto expedition = ExpeditionsRepository::GetWithLeaderName(database, expedition_id); + CacheExpeditions({ std::move(expedition) }); - CacheExpeditions(results); - - auto elapsed = benchmark.elapsed(); - LogExpeditions("Caching new expedition [{}] took [{}s]", expedition_id, elapsed); + LogExpeditions("Caching new expedition [{}] took [{}s]", expedition_id, benchmark.elapsed()); } } @@ -269,20 +240,13 @@ bool Expedition::CacheAllFromDatabase() BenchTimer benchmark; + auto expeditions = ExpeditionsRepository::GetAllWithLeaderName(database); zone->expedition_cache.clear(); + zone->expedition_cache.reserve(expeditions.size()); - // load all active expeditions and members to current zone cache - auto results = ExpeditionDatabase::LoadAllExpeditions(); - if (!results.Success()) - { - LogExpeditions("Failed to load Expeditions for zone cache"); - return false; - } + CacheExpeditions(std::move(expeditions)); - CacheExpeditions(results); - - auto elapsed = benchmark.elapsed(); - LogExpeditions("Caching [{}] expedition(s) took [{}s]", zone->expedition_cache.size(), elapsed); + LogExpeditions("Caching [{}] expedition(s) took [{}s]", zone->expedition_cache.size(), benchmark.elapsed()); return true; } @@ -391,48 +355,6 @@ bool Expedition::HasReplayLockout() return HasLockout(DZ_REPLAY_TIMER_NAME); } -bool Expedition::HasMember(uint32_t character_id) -{ - return std::any_of(m_members.begin(), m_members.end(), [&](const ExpeditionMember& member) { - return member.char_id == character_id; - }); -} - -bool Expedition::HasMember(const std::string& character_name) -{ - return std::any_of(m_members.begin(), m_members.end(), [&](const ExpeditionMember& member) { - return (strcasecmp(member.name.c_str(), character_name.c_str()) == 0); - }); -} - -ExpeditionMember Expedition::GetMemberData(uint32_t character_id) -{ - auto it = std::find_if(m_members.begin(), m_members.end(), [&](const ExpeditionMember& member) { - return member.char_id == character_id; - }); - - ExpeditionMember member_data; - if (it != m_members.end()) - { - member_data = *it; - } - return member_data; -} - -ExpeditionMember Expedition::GetMemberData(const std::string& character_name) -{ - auto it = std::find_if(m_members.begin(), m_members.end(), [&](const ExpeditionMember& member) { - return (strcasecmp(member.name.c_str(), character_name.c_str()) == 0); - }); - - ExpeditionMember member_data; - if (it != m_members.end()) - { - member_data = *it; - } - return member_data; -} - void Expedition::SetReplayLockoutOnMemberJoin(bool add_on_join, bool update_db) { m_add_replay_on_join = add_on_join; @@ -526,20 +448,6 @@ void Expedition::RemoveLockout(const std::string& event_name) SendWorldLockoutUpdate(lockout, true); } -void Expedition::AddInternalMember( - const std::string& char_name, uint32_t character_id, ExpeditionMemberStatus status) -{ - auto it = std::find_if(m_members.begin(), m_members.end(), - [character_id](const ExpeditionMember& member) { - return member.char_id == character_id; - }); - - if (it == m_members.end()) - { - m_members.emplace_back(character_id, char_name, status); - } -} - bool Expedition::AddMember(const std::string& add_char_name, uint32_t add_char_id) { if (HasMember(add_char_id)) @@ -644,10 +552,7 @@ void Expedition::UpdateMemberStatus(uint32_t update_member_id, ExpeditionMemberS } // if zone already had this member status cached avoid packet update to clients - auto it = std::find_if(m_members.begin(), m_members.end(), - [&](const ExpeditionMember& member) { return member.char_id == update_member_id; }); - - if (it != m_members.end() && it->status == status) + if (member_data.status == status) { return; } @@ -1197,7 +1102,7 @@ void Expedition::ProcessMemberAdded(const std::string& char_name, uint32_t added leader_client->MessageString(Chat::Yellow, EXPEDITION_MEMBER_ADDED, char_name.c_str(), m_expedition_name.c_str()); } - AddInternalMember(char_name, added_char_id, ExpeditionMemberStatus::Online); + AddInternalMember({ added_char_id, char_name, ExpeditionMemberStatus::Online }); Client* member_client = entity_list.GetClientByCharID(added_char_id); if (member_client) diff --git a/zone/expedition.h b/zone/expedition.h index 5b2a40043..d6e906fc7 100644 --- a/zone/expedition.h +++ b/zone/expedition.h @@ -22,8 +22,9 @@ #define EXPEDITION_H #include "dynamic_zone.h" -#include "../common/eq_constants.h" +#include "../common/expedition_base.h" #include "../common/expedition_lockout_timer.h" +#include "../common/repositories/expeditions_repository.h" #include #include #include @@ -34,21 +35,11 @@ class Client; class EQApplicationPacket; struct ExpeditionInvite; class ExpeditionRequest; -class MySQLRequestResult; class ServerPacket; extern const char* const DZ_YOU_NOT_ASSIGNED; extern const char* const EXPEDITION_OTHER_BELONGS; -enum class ExpeditionMemberStatus : uint8_t -{ - Unknown = 0, - Online, - Offline, - InDynamicZone, - LinkDead -}; - enum class ExpeditionLockMessage : uint8_t { None = 0, @@ -56,25 +47,10 @@ enum class ExpeditionLockMessage : uint8_t Begin }; -struct ExpeditionMember -{ - uint32_t char_id = 0; - std::string name; - ExpeditionMemberStatus status = ExpeditionMemberStatus::Online; - - ExpeditionMember() = default; - ExpeditionMember(uint32_t char_id_, const std::string& name_) - : char_id(char_id_), name(name_) {} - ExpeditionMember(uint32_t char_id_, const std::string& name_, ExpeditionMemberStatus status_) - : char_id(char_id_), name(name_), status(status_) {} - - bool IsValid() const { return char_id != 0 && !name.empty(); } -}; - -class Expedition +class Expedition : public ExpeditionBase { public: - Expedition() = delete; + Expedition() = default; Expedition(uint32_t id, const std::string& uuid, DynamicZone&& dz, const std::string& expedition_name, const ExpeditionMember& leader, uint32_t min_players, uint32_t max_players); @@ -103,23 +79,10 @@ public: const std::string& expedition_name = {}, const std::string& event_name = {}); static void AddLockoutClients(const ExpeditionLockoutTimer& lockout, uint32_t exclude_id = 0); - uint32_t GetDynamicZoneID() const { return m_dynamiczone.GetID(); } - uint32_t GetID() const { return m_id; } - uint16_t GetInstanceID() const { return m_dynamiczone.GetInstanceID(); } - uint32_t GetLeaderID() const { return m_leader.char_id; } - uint32_t GetMinPlayers() const { return m_min_players; } - uint32_t GetMaxPlayers() const { return m_max_players; } - uint32_t GetMemberCount() const { return static_cast(m_members.size()); } DynamicZone& GetDynamicZone() { return m_dynamiczone; } - const std::string& GetName() const { return m_expedition_name; } - const std::string& GetLeaderName() const { return m_leader.name; } - const std::string& GetUUID() const { return m_uuid; } const std::unordered_map& GetLockouts() const { return m_lockouts; } - const std::vector& GetMembers() const { return m_members; } bool AddMember(const std::string& add_char_name, uint32_t add_char_id); - bool HasMember(const std::string& character_name); - bool HasMember(uint32_t character_id); void RemoveAllMembers(bool enable_removal_timers = true); bool RemoveMember(const std::string& remove_char_name); void SetMemberStatus(Client* client, ExpeditionMemberStatus status); @@ -164,13 +127,12 @@ public: static const int32_t EVENT_TIMER_ID; private: - static void CacheExpeditions(MySQLRequestResult& results); + static void CacheExpeditions(std::vector&& expeditions); static void SendWorldGetOnlineMembers(const std::vector>& expedition_character_ids); static void SendWorldCharacterLockout(uint32_t character_id, const ExpeditionLockoutTimer& lockout, bool remove); void AddLockout(const ExpeditionLockoutTimer& lockout, bool members_only = false); void AddLockoutDurationClients(const ExpeditionLockoutTimer& lockout, int seconds, uint32_t exclude_id = 0); - void AddInternalMember(const std::string& char_name, uint32_t char_id, ExpeditionMemberStatus status); bool ConfirmLeaderCommand(Client* requester); bool ProcessAddConflicts(Client* leader_client, Client* add_client, bool swapping); void ProcessLeaderChanged(uint32_t new_leader_id); @@ -207,8 +169,6 @@ private: const std::string& swap_remove_name, Client* leader_client = nullptr); void UpdateMemberStatus(uint32_t update_character_id, ExpeditionMemberStatus status); - ExpeditionMember GetMemberData(uint32_t character_id); - ExpeditionMember GetMemberData(const std::string& character_name); std::unique_ptr CreateExpireWarningPacket(uint32_t minutes_remaining); std::unique_ptr CreateInfoPacket(bool clear = false); std::unique_ptr CreateInvitePacket(const std::string& inviter_name, const std::string& swap_remove_name); @@ -217,16 +177,7 @@ private: std::unique_ptr CreateMemberListStatusPacket(const std::string& name, ExpeditionMemberStatus status); std::unique_ptr CreateLeaderNamePacket(); - uint32_t m_id = 0; - uint32_t m_min_players = 0; - uint32_t m_max_players = 0; - bool m_is_locked = false; - bool m_add_replay_on_join = true; - std::string m_uuid; - std::string m_expedition_name; DynamicZone m_dynamiczone { DynamicZoneType::Expedition }; - ExpeditionMember m_leader; - std::vector m_members; std::unordered_map m_lockouts; std::unordered_map m_npc_loot_events; // only valid inside dz zone std::unordered_map m_spawn_loot_events; // only valid inside dz zone diff --git a/zone/expedition_database.cpp b/zone/expedition_database.cpp index d628d8fd1..e1b5c0bf6 100644 --- a/zone/expedition_database.cpp +++ b/zone/expedition_database.cpp @@ -51,52 +51,6 @@ uint32_t ExpeditionDatabase::InsertExpedition( return results.LastInsertedID(); } -std::string ExpeditionDatabase::LoadExpeditionsSelectQuery() -{ - return std::string(SQL( - SELECT - expeditions.id, - expeditions.uuid, - expeditions.dynamic_zone_id, - expeditions.expedition_name, - expeditions.leader_id, - expeditions.min_players, - expeditions.max_players, - expeditions.add_replay_on_join, - expeditions.is_locked, - character_data.name leader_name, - expedition_members.character_id, - member_data.name - FROM expeditions - INNER JOIN character_data ON expeditions.leader_id = character_data.id - INNER JOIN expedition_members ON expeditions.id = expedition_members.expedition_id - AND expedition_members.is_current_member = TRUE - INNER JOIN character_data member_data ON expedition_members.character_id = member_data.id - )); -} - -MySQLRequestResult ExpeditionDatabase::LoadExpedition(uint32_t expedition_id) -{ - LogExpeditionsDetail("Loading expedition [{}]", expedition_id); - - std::string query = fmt::format(SQL( - {} WHERE expeditions.id = {}; - ), LoadExpeditionsSelectQuery(), expedition_id); - - return database.QueryDatabase(query); -} - -MySQLRequestResult ExpeditionDatabase::LoadAllExpeditions() -{ - LogExpeditionsDetail("Loading all expeditions from database"); - - std::string query = fmt::format(SQL( - {} ORDER BY expeditions.id; - ), LoadExpeditionsSelectQuery()); - - return database.QueryDatabase(query); -} - std::vector ExpeditionDatabase::LoadCharacterLockouts(uint32_t character_id) { LogExpeditionsDetail("Loading character [{}] lockouts", character_id); @@ -170,54 +124,6 @@ std::vector ExpeditionDatabase::LoadCharacterLockouts( return lockouts; } -std::unordered_map> -ExpeditionDatabase::LoadMultipleExpeditionLockouts( - const std::vector& expedition_ids) -{ - LogExpeditionsDetail("Loading internal lockouts for [{}] expeditions", expedition_ids.size()); - - std::string in_expedition_ids_query = fmt::format("{}", fmt::join(expedition_ids, ",")); - - // these are loaded into the same container type expeditions use to store lockouts - std::unordered_map> lockouts; - - if (!in_expedition_ids_query.empty()) - { - std::string query = fmt::format(SQL( - SELECT - expedition_lockouts.expedition_id, - expedition_lockouts.from_expedition_uuid, - expeditions.expedition_name, - expedition_lockouts.event_name, - UNIX_TIMESTAMP(expedition_lockouts.expire_time), - expedition_lockouts.duration - FROM expedition_lockouts - INNER JOIN expeditions ON expedition_lockouts.expedition_id = expeditions.id - WHERE expedition_id IN ({}) - ORDER BY expedition_id; - ), in_expedition_ids_query); - - auto results = database.QueryDatabase(query); - - if (results.Success()) - { - for (auto row = results.begin(); row != results.end(); ++row) - { - auto expedition_id = strtoul(row[0], nullptr, 10); - lockouts[expedition_id].emplace(row[3], ExpeditionLockoutTimer{ - row[1], // expedition_uuid - row[2], // expedition_name - row[3], // event_name - strtoull(row[4], nullptr, 10), // expire_time - static_cast(strtoul(row[5], nullptr, 10)) // original duration - }); - } - } - } - - return lockouts; -} - void ExpeditionDatabase::DeleteAllCharacterLockouts(uint32_t character_id) { LogExpeditionsDetail("Deleting all character [{}] lockouts", character_id); diff --git a/zone/expedition_database.h b/zone/expedition_database.h index 2c72ee9ad..9e0487b36 100644 --- a/zone/expedition_database.h +++ b/zone/expedition_database.h @@ -38,14 +38,9 @@ namespace ExpeditionDatabase uint32_t InsertExpedition( const std::string& uuid, uint32_t instance_id, const std::string& expedition_name, uint32_t leader_id, uint32_t min_players, uint32_t max_players); - std::string LoadExpeditionsSelectQuery(); - MySQLRequestResult LoadExpedition(uint32_t expedition_id); - MySQLRequestResult LoadAllExpeditions(); std::vector LoadCharacterLockouts(uint32_t character_id); std::vector LoadCharacterLockouts(uint32_t character_id, const std::string& expedition_name); - std::unordered_map> - LoadMultipleExpeditionLockouts(const std::vector& expedition_ids); void DeleteAllMembers(uint32_t expedition_id); void DeleteMember(uint32_t expedition_id, uint32_t character_id); void DeleteAllCharacterLockouts(uint32_t character_id); @@ -73,23 +68,4 @@ namespace ExpeditionDatabase const ExpeditionLockoutTimer& lockout, int seconds); }; -namespace LoadExpeditionColumns -{ - enum eLoadExpeditionColumns - { - id = 0, - uuid, - dz_id, - expedition_name, - leader_id, - min_players, - max_players, - add_replay_on_join, - is_locked, - leader_name, - member_id, - member_name - }; -}; - #endif diff --git a/zone/lua_expedition.cpp b/zone/lua_expedition.cpp index 1bd872cc2..434cc5726 100644 --- a/zone/lua_expedition.cpp +++ b/zone/lua_expedition.cpp @@ -59,7 +59,7 @@ void Lua_Expedition::AddReplayLockoutDuration(int seconds, bool members_only) { uint32_t Lua_Expedition::GetDynamicZoneID() { Lua_Safe_Call_Int(); - return self->GetDynamicZoneID(); + return self->GetDynamicZone().GetID(); } uint32_t Lua_Expedition::GetID() { diff --git a/zone/perl_expedition.cpp b/zone/perl_expedition.cpp index 3095c3770..ccba1e6a8 100644 --- a/zone/perl_expedition.cpp +++ b/zone/perl_expedition.cpp @@ -142,7 +142,7 @@ XS(XS_Expedition_GetDynamicZoneID) { Expedition* THIS = nullptr; VALIDATE_THIS_IS_EXPEDITION; - XSRETURN_UV(THIS->GetDynamicZoneID()); + XSRETURN_UV(THIS->GetDynamicZone().GetID()); } XS(XS_Expedition_GetID); @@ -168,7 +168,7 @@ XS(XS_Expedition_GetInstanceID) { Expedition* THIS = nullptr; VALIDATE_THIS_IS_EXPEDITION; - XSRETURN_UV(THIS->GetInstanceID()); + XSRETURN_UV(THIS->GetDynamicZone().GetInstanceID()); } XS(XS_Expedition_GetLeaderName); From 324d48aa903fe02403e1893aa4c586fb8ccc272b Mon Sep 17 00:00:00 2001 From: Peter Rigby Date: Wed, 7 Apr 2021 07:22:42 +0100 Subject: [PATCH 019/624] [Fix] Moved assigning of AISpellVar variables before AI_Start() so that any values that override the rule values will not be ignored (#1321) --- zone/npc.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/zone/npc.cpp b/zone/npc.cpp index 7f7c61a58..87d7b57fc 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -284,6 +284,19 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi entity_list.MakeNameUnique(name); npc_aggro = npc_type_data->npc_aggro; + + AISpellVar.fail_recast = static_cast(RuleI(Spells, AI_SpellCastFinishedFailRecast)); + AISpellVar.engaged_no_sp_recast_min = static_cast(RuleI(Spells, AI_EngagedNoSpellMinRecast)); + AISpellVar.engaged_no_sp_recast_max = static_cast(RuleI(Spells, AI_EngagedNoSpellMaxRecast)); + AISpellVar.engaged_beneficial_self_chance = static_cast (RuleI(Spells, AI_EngagedBeneficialSelfChance)); + AISpellVar.engaged_beneficial_other_chance = static_cast (RuleI(Spells, AI_EngagedBeneficialOtherChance)); + AISpellVar.engaged_detrimental_chance = static_cast (RuleI(Spells, AI_EngagedDetrimentalChance)); + AISpellVar.pursue_no_sp_recast_min = static_cast(RuleI(Spells, AI_PursueNoSpellMinRecast)); + AISpellVar.pursue_no_sp_recast_max = static_cast(RuleI(Spells, AI_PursueNoSpellMaxRecast)); + AISpellVar.pursue_detrimental_chance = static_cast (RuleI(Spells, AI_PursueDetrimentalChance)); + AISpellVar.idle_no_sp_recast_min = static_cast(RuleI(Spells, AI_IdleNoSpellMinRecast)); + AISpellVar.idle_no_sp_recast_max = static_cast(RuleI(Spells, AI_IdleNoSpellMaxRecast)); + AISpellVar.idle_beneficial_chance = static_cast (RuleI(Spells, AI_IdleBeneficialChance)); AI_Init(); AI_Start(); @@ -397,19 +410,6 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi SetMana(GetMaxMana()); - AISpellVar.fail_recast = static_cast(RuleI(Spells, AI_SpellCastFinishedFailRecast)); - AISpellVar.engaged_no_sp_recast_min = static_cast(RuleI(Spells, AI_EngagedNoSpellMinRecast)); - AISpellVar.engaged_no_sp_recast_max = static_cast(RuleI(Spells, AI_EngagedNoSpellMaxRecast)); - AISpellVar.engaged_beneficial_self_chance = static_cast (RuleI(Spells, AI_EngagedBeneficialSelfChance)); - AISpellVar.engaged_beneficial_other_chance = static_cast (RuleI(Spells, AI_EngagedBeneficialOtherChance)); - AISpellVar.engaged_detrimental_chance = static_cast (RuleI(Spells, AI_EngagedDetrimentalChance)); - AISpellVar.pursue_no_sp_recast_min = static_cast(RuleI(Spells, AI_PursueNoSpellMinRecast)); - AISpellVar.pursue_no_sp_recast_max = static_cast(RuleI(Spells, AI_PursueNoSpellMaxRecast)); - AISpellVar.pursue_detrimental_chance = static_cast (RuleI(Spells, AI_PursueDetrimentalChance)); - AISpellVar.idle_no_sp_recast_min = static_cast(RuleI(Spells, AI_IdleNoSpellMinRecast)); - AISpellVar.idle_no_sp_recast_max = static_cast(RuleI(Spells, AI_IdleNoSpellMaxRecast)); - AISpellVar.idle_beneficial_chance = static_cast (RuleI(Spells, AI_IdleBeneficialChance)); - if (GetBodyType() == BT_Animal && !RuleB(NPC, AnimalsOpenDoors)) { m_can_open_doors = false; } From 0f23bd24cca2f4c678ceec480d0a99da63936510 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 22 Apr 2021 23:41:19 -0400 Subject: [PATCH 020/624] [Quest API] Convert Client GetLastName() method export to Mob export. (#1331) --- zone/lua_client.cpp | 6 ------ zone/lua_client.h | 1 - zone/lua_mob.cpp | 8 +++++++- zone/lua_mob.h | 1 + zone/perl_client.cpp | 19 ------------------- zone/perl_mob.cpp | 19 +++++++++++++++++++ 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index c963feec0..9541176c8 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -169,11 +169,6 @@ int Lua_Client::GetLanguageSkill(int skill_id) { return self->GetLanguageSkill(skill_id); } -const char *Lua_Client::GetLastName() { - Lua_Safe_Call_String(); - return self->GetLastName(); -} - int Lua_Client::GetLDoNPointsTheme(int theme) { Lua_Safe_Call_Int(); return self->GetLDoNPointsTheme(theme); @@ -2096,7 +2091,6 @@ luabind::scope lua_register_client() { .def("GetRaceBitmask", (int(Lua_Client::*)(void))&Lua_Client::GetRaceBitmask) .def("GetBaseFace", (int(Lua_Client::*)(void))&Lua_Client::GetBaseFace) .def("GetLanguageSkill", (int(Lua_Client::*)(int))&Lua_Client::GetLanguageSkill) - .def("GetLastName", (const char *(Lua_Client::*)(void))&Lua_Client::GetLastName) .def("GetLDoNPointsTheme", (int(Lua_Client::*)(int))&Lua_Client::GetLDoNPointsTheme) .def("GetBaseSTR", (int(Lua_Client::*)(void))&Lua_Client::GetBaseSTR) .def("GetBaseSTA", (int(Lua_Client::*)(void))&Lua_Client::GetBaseSTA) diff --git a/zone/lua_client.h b/zone/lua_client.h index 55b98087a..923231808 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -60,7 +60,6 @@ public: int GetRaceBitmask(); int GetBaseFace(); int GetLanguageSkill(int skill_id); - const char *GetLastName(); int GetLDoNPointsTheme(int theme); int GetBaseSTR(); int GetBaseSTA(); diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index e702b8bf1..2948c8c71 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -2342,6 +2342,11 @@ Lua_HateList Lua_Mob::GetHateListByDistance(int distance) { return ret; } +const char *Lua_Mob::GetLastName() { + Lua_Safe_Call_String(); + return self->GetLastName(); +} + luabind::scope lua_register_mob() { return luabind::class_("Mob") .def(luabind::constructor<>()) @@ -2743,7 +2748,8 @@ luabind::scope lua_register_mob() { .def("GetBucketRemaining", (std::string(Lua_Mob::*)(std::string))&Lua_Mob::GetBucketRemaining) .def("SetBucket", (void(Lua_Mob::*)(std::string,std::string))&Lua_Mob::SetBucket) .def("SetBucket", (void(Lua_Mob::*)(std::string,std::string,std::string))&Lua_Mob::SetBucket) - .def("IsHorse", &Lua_Mob::IsHorse); + .def("IsHorse", &Lua_Mob::IsHorse) + .def("GetLastName", &Lua_Mob::GetLastName); } luabind::scope lua_register_special_abilities() { diff --git a/zone/lua_mob.h b/zone/lua_mob.h index 4b090765c..dbb3c8110 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -110,6 +110,7 @@ public: int GetClass(); int GetLevel(); const char *GetCleanName(); + const char *GetLastName(); Lua_Mob GetTarget(); void SetTarget(Lua_Mob t); double GetHPRatio(); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 04e93beae..567067f64 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -446,24 +446,6 @@ XS(XS_Client_GetLanguageSkill) { XSRETURN(1); } -XS(XS_Client_GetLastName); /* prototype to pass -Wmissing-prototypes */ -XS(XS_Client_GetLastName) { - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: Client::GetLastName(THIS)"); // @categories Account and Character - { - Client *THIS; - Const_char *RETVAL; - dXSTARG; - VALIDATE_THIS_IS_CLIENT; - RETVAL = THIS->GetLastName(); - sv_setpv(TARG, RETVAL); - XSprePUSH; - PUSHTARG; - } - XSRETURN(1); -} - XS(XS_Client_GetLDoNPointsTheme); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_GetLDoNPointsTheme) { dXSARGS; @@ -5385,7 +5367,6 @@ XS(boot_Client) { newXSproto(strcpy(buf, "GetItemIDAt"), XS_Client_GetItemIDAt, file, "$$"); newXSproto(strcpy(buf, "GetItemInInventory"), XS_Client_GetItemInInventory, file, "$$"); newXSproto(strcpy(buf, "GetLanguageSkill"), XS_Client_GetLanguageSkill, file, "$$"); - newXSproto(strcpy(buf, "GetLastName"), XS_Client_GetLastName, file, "$"); newXSproto(strcpy(buf, "GetLDoNLosses"), XS_Client_GetLDoNLosses, file, "$"); newXSproto(strcpy(buf, "GetLDoNLossesTheme"), XS_Client_GetLDoNLossesTheme, file, "$$"); newXSproto(strcpy(buf, "GetLDoNPointsTheme"), XS_Client_GetLDoNPointsTheme, file, "$"); diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index f5dc1067a..fb4606d27 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -6235,6 +6235,24 @@ XS(XS_Mob_GetHateClosest) { XSRETURN(1); } +XS(XS_Mob_GetLastName); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetLastName) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetLastName(THIS)"); // @categories Script Utility + { + Mob *THIS; + Const_char *last_name; + dXSTARG; + VALIDATE_THIS_IS_MOB; + last_name = THIS->GetLastName(); + sv_setpv(TARG, last_name); + XSprePUSH; + PUSHTARG; + } + XSRETURN(1); +} + #ifdef __cplusplus extern "C" #endif @@ -6583,6 +6601,7 @@ XS(boot_Mob) { newXSproto(strcpy(buf, "SetBucket"), XS_Mob_SetBucket, file, "$$$;$"); newXSproto(strcpy(buf, "GetHateClosest"), XS_Mob_GetHateClosest, file, "$"); newXSproto(strcpy(buf, "GetHateListByDistance"), XS_Mob_GetHateListByDistance, file, "$;$"); + newXSproto(strcpy(buf, "GetLastName"), XS_Mob_GetLastName, file, "$"); XSRETURN_YES; } From 5893730704689065373cac13220cd0d694331933 Mon Sep 17 00:00:00 2001 From: splose Date: Thu, 22 Apr 2021 23:42:14 -0400 Subject: [PATCH 021/624] [Rules] Add rule 'GM:MinStatusToBypassLockedServer' (#1330) * Add rule 'GM:MinStatusToBypassLockedServer' Default Status: 100 Description: Players >= this status can log in to the server even when it is locked * Add rule 'GM:MinStatusToBypassLockedServer' Default Status: 100 Description: Players >= this status can log in to the server even when it is locked --- common/ruletypes.h | 1 + world/login_server.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 4edda9277..b66061dbd 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -209,6 +209,7 @@ RULE_CATEGORY(GM) RULE_INT(GM, MinStatusToSummonItem, 250, "Minimum required status to summon items") RULE_INT(GM, MinStatusToZoneAnywhere, 250, "Minimum required status to zone anywhere") RULE_INT(GM, MinStatusToLevelTarget, 100, "Minimum required status to set the level of a player") +RULE_INT(GM, MinStatusToBypassLockedServer, 100, "Players >= this status can log in to the server even when it is locked") RULE_CATEGORY_END() RULE_CATEGORY(World) diff --git a/world/login_server.cpp b/world/login_server.cpp index 590a3f0db..da649495e 100644 --- a/world/login_server.cpp +++ b/world/login_server.cpp @@ -92,7 +92,7 @@ void LoginServer::ProcessUsertoWorldReqLeg(uint16_t opcode, EQ::Net::Packet &p) utwrs->response = UserToWorldStatusSuccess; if (Config->Locked) { - if (status < 100) { + if (status < (RuleI(GM, MinStatusToBypassLockedServer))) { LogDebug("[ProcessUsertoWorldReqLeg] Server locked and status is not high enough for account_id [{0}]", utwr->lsaccountid); utwrs->response = UserToWorldStatusWorldUnavail; SendPacket(&outpack); @@ -101,7 +101,7 @@ void LoginServer::ProcessUsertoWorldReqLeg(uint16_t opcode, EQ::Net::Packet &p) } int32 x = Config->MaxClients; - if ((int32) numplayers >= x && x != -1 && x != 255 && status < 80) { + if ((int32) numplayers >= x && x != -1 && x != 255 && status < (RuleI(GM, MinStatusToBypassLockedServer))) { LogDebug("[ProcessUsertoWorldReqLeg] World at capacity account_id [{0}]", utwr->lsaccountid); utwrs->response = UserToWorldStatusWorldAtCapacity; SendPacket(&outpack); @@ -170,7 +170,7 @@ void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p) utwrs->response = UserToWorldStatusSuccess; if (Config->Locked == true) { - if (status < 100) { + if (status < (RuleI(GM, MinStatusToBypassLockedServer))) { LogDebug("[ProcessUsertoWorldReq] Server locked and status is not high enough for account_id [{0}]", utwr->lsaccountid); utwrs->response = UserToWorldStatusWorldUnavail; SendPacket(&outpack); @@ -179,7 +179,7 @@ void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p) } int32 x = Config->MaxClients; - if ((int32) numplayers >= x && x != -1 && x != 255 && status < 80) { + if ((int32) numplayers >= x && x != -1 && x != 255 && status < (RuleI(GM, MinStatusToBypassLockedServer))) { LogDebug("[ProcessUsertoWorldReq] World at capacity account_id [{0}]", utwr->lsaccountid); utwrs->response = UserToWorldStatusWorldAtCapacity; SendPacket(&outpack); From 00fb9bc9f95830169de1f52a5a613ce5b3263bf0 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 22 Apr 2021 23:49:44 -0400 Subject: [PATCH 022/624] [Bug Fix] Zone Heading for Binds, Summons, Teleports, and Zoning. (#1328) * For as long as I can remember people have had issues with zoning in, facing the wrong way, and walking through a zone line. With this we will be able to set zone's safe heading as well as preserve heading on summon (NPC or GM) and teleports between zones. This affects several pre-existing quest methods and extends their parameters to allow for the addition of heading. The following functions have had heading added. Lua - client:SetBindPoint() - client:SetStartZone() Perl - $client->SetBindPoint() - $client->SetStartZone() - quest::rebind() SetStartZone parameter list was fixed also. This converts some pre-existing methods from glm::vec3() to glm::vec4() and has an overload where necessary to use a glm::vec3() method versus glm::vec4() method. This shouldn't affect any pre-existing servers and will allow PEQ and others to document safe headings for zones properly. * Removed possible memory leaks. * Fix SQL. * Fix client message. * Fix debug log. * Fix log message. * Fix call in rebind overload. * Fix floats. * Add default to column. --- common/database.cpp | 46 +- common/database.h | 2 +- common/database_conversions.cpp | 10 +- common/eq_packet_structs.h | 4 +- common/patches/rof.cpp | 2 +- common/patches/rof2.cpp | 2 +- common/patches/rof2_structs.h | 4 +- common/patches/rof_structs.h | 4 +- common/patches/sod.cpp | 2 +- common/patches/sod_structs.h | 4 +- common/patches/sof.cpp | 2 +- common/patches/sof_structs.h | 4 +- common/patches/titanium.cpp | 2 +- common/patches/titanium_structs.h | 4 +- common/patches/uf.cpp | 2 +- common/patches/uf_structs.h | 4 +- common/version.h | 2 +- .../player_profile_set/eq_player_structs.h | 4 +- utils/sql/db_update_manifest.txt | 1 + .../2021_04_17_zone_safe_heading_changes.sql | 1 + world/client.cpp | 14 +- world/worlddb.cpp | 457 +++++++++++------- world/worlddb.h | 2 +- zone/attack.cpp | 4 +- zone/client.cpp | 30 +- zone/client.h | 7 +- zone/client_packet.cpp | 123 +++-- zone/client_process.cpp | 6 +- zone/command.cpp | 5 +- zone/embparser_api.cpp | 19 +- zone/lua_client.cpp | 8 +- zone/lua_client.h | 2 + zone/npc.h | 4 +- zone/perl_client.cpp | 68 +-- zone/questmgr.cpp | 13 +- zone/questmgr.h | 3 +- zone/zone.cpp | 2 +- zone/zone.h | 4 +- zone/zonedb.cpp | 6 +- zone/zonedb.h | 2 +- zone/zoning.cpp | 293 ++++++----- 41 files changed, 697 insertions(+), 481 deletions(-) create mode 100644 utils/sql/git/required/2021_04_17_zone_safe_heading_changes.sql diff --git a/common/database.cpp b/common/database.cpp index 5a2d53940..5c1ec9a6f 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -703,11 +703,11 @@ bool Database::SaveCharacterCreate(uint32 character_id, uint32 account_id, Playe "(%u, %u, %u, %f, %f, %f, %f, %i), " "(%u, %u, %u, %f, %f, %f, %f, %i), " "(%u, %u, %u, %f, %f, %f, %f, %i)", - character_id, pp->binds[0].zoneId, 0, pp->binds[0].x, pp->binds[0].y, pp->binds[0].z, pp->binds[0].heading, 0, - character_id, pp->binds[1].zoneId, 0, pp->binds[1].x, pp->binds[1].y, pp->binds[1].z, pp->binds[1].heading, 1, - character_id, pp->binds[2].zoneId, 0, pp->binds[2].x, pp->binds[2].y, pp->binds[2].z, pp->binds[2].heading, 2, - character_id, pp->binds[3].zoneId, 0, pp->binds[3].x, pp->binds[3].y, pp->binds[3].z, pp->binds[3].heading, 3, - character_id, pp->binds[4].zoneId, 0, pp->binds[4].x, pp->binds[4].y, pp->binds[4].z, pp->binds[4].heading, 4 + character_id, pp->binds[0].zone_id, 0, pp->binds[0].x, pp->binds[0].y, pp->binds[0].z, pp->binds[0].heading, 0, + character_id, pp->binds[1].zone_id, 0, pp->binds[1].x, pp->binds[1].y, pp->binds[1].z, pp->binds[1].heading, 1, + character_id, pp->binds[2].zone_id, 0, pp->binds[2].x, pp->binds[2].y, pp->binds[2].z, pp->binds[2].heading, 2, + character_id, pp->binds[3].zone_id, 0, pp->binds[3].x, pp->binds[3].y, pp->binds[3].z, pp->binds[3].heading, 3, + character_id, pp->binds[4].zone_id, 0, pp->binds[4].x, pp->binds[4].y, pp->binds[4].z, pp->binds[4].heading, 4 ); results = QueryDatabase(query); /* HoTT Ability */ @@ -971,10 +971,20 @@ bool Database::SetVariable(const std::string varname, const std::string &varvalu } // Get zone starting points from DB -bool Database::GetSafePoints(const char* short_name, uint32 version, float* safe_x, float* safe_y, float* safe_z, int16* minstatus, uint8* minlevel, char *flag_needed) { - - std::string query = StringFormat("SELECT safe_x, safe_y, safe_z, min_status, min_level, flag_needed FROM zone " - " WHERE short_name='%s' AND (version=%i OR version=0) ORDER BY version DESC", short_name, version); +bool Database::GetSafePoints(const char* zone_short_name, uint32 instance_version, float* safe_x, float* safe_y, float* safe_z, float* safe_heading, int16* min_status, uint8* min_level, char *flag_needed) { + std::string query = fmt::format( + SQL( + SELECT + `safe_x`, `safe_y`, `safe_z`, `safe_heading`, `min_status`, `min_level`, `flag_needed` + FROM + zone + WHERE + `short_name` = '{}' + AND + (`version` = {} OR `version` = 0) + ORDER BY `version` DESC + ), zone_short_name, instance_version + ); auto results = QueryDatabase(query); if (!results.Success()) @@ -987,16 +997,24 @@ bool Database::GetSafePoints(const char* short_name, uint32 version, float* safe if (safe_x != nullptr) *safe_x = atof(row[0]); + if (safe_y != nullptr) *safe_y = atof(row[1]); + if (safe_z != nullptr) *safe_z = atof(row[2]); - if (minstatus != nullptr) - *minstatus = atoi(row[3]); - if (minlevel != nullptr) - *minlevel = atoi(row[4]); + + if (safe_heading != nullptr) + *safe_heading = atof(row[3]); + + if (min_status != nullptr) + *min_status = atoi(row[4]); + + if (min_level != nullptr) + *min_level = atoi(row[5]); + if (flag_needed != nullptr) - strcpy(flag_needed, row[5]); + strcpy(flag_needed, row[6]); return true; } diff --git a/common/database.h b/common/database.h index db428b981..c15e200bc 100644 --- a/common/database.h +++ b/common/database.h @@ -242,7 +242,7 @@ public: /* General Queries */ - bool GetSafePoints(const char* short_name, uint32 version, float* safe_x = 0, float* safe_y = 0, float* safe_z = 0, int16* minstatus = 0, uint8* minlevel = 0, char *flag_needed = nullptr); + bool GetSafePoints(const char* zone_short_name, uint32 instance_version, float* safe_x = 0, float* safe_y = 0, float* safe_z = 0, float* safe_heading = 0, int16* minstatus = 0, uint8* minlevel = 0, char *flag_needed = nullptr); bool GetZoneGraveyard(const uint32 graveyard_id, uint32* graveyard_zoneid = 0, float* graveyard_x = 0, float* graveyard_y = 0, float* graveyard_z = 0, float* graveyard_heading = 0); bool GetZoneLongName(const char* short_name, char** long_name, char* file_name = 0, float* safe_x = 0, float* safe_y = 0, float* safe_z = 0, uint32* graveyard_id = 0, uint32* maxclients = 0); bool LoadPTimers(uint32 charid, PTimerList &into); diff --git a/common/database_conversions.cpp b/common/database_conversions.cpp index d49651f4d..561e1679b 100644 --- a/common/database_conversions.cpp +++ b/common/database_conversions.cpp @@ -48,7 +48,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA namespace Convert { struct BindStruct { - /*000*/ uint32 zoneId; + /*000*/ uint32 zone_id; /*004*/ float x; /*008*/ float y; /*012*/ float z; @@ -1320,18 +1320,18 @@ bool Database::CheckDatabaseConvertPPDeblob(){ if (rquery != ""){ results = QueryDatabase(rquery); } /* Run Bind Home Convert */ - if (pp->binds[4].zoneId < 999 && !_ISNAN_(pp->binds[4].x) && !_ISNAN_(pp->binds[4].y) && !_ISNAN_(pp->binds[4].z) && !_ISNAN_(pp->binds[4].heading)) { + if (pp->binds[4].zone_id < 999 && !_ISNAN_(pp->binds[4].x) && !_ISNAN_(pp->binds[4].y) && !_ISNAN_(pp->binds[4].z) && !_ISNAN_(pp->binds[4].heading)) { rquery = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" " VALUES (%u, %u, %u, %f, %f, %f, %f, 1)", - character_id, pp->binds[4].zoneId, 0, pp->binds[4].x, pp->binds[4].y, pp->binds[4].z, pp->binds[4].heading); + character_id, pp->binds[4].zone_id, 0, pp->binds[4].x, pp->binds[4].y, pp->binds[4].z, pp->binds[4].heading); if (rquery != ""){ results = QueryDatabase(rquery); } } /* Run Bind Convert */ - if (pp->binds[0].zoneId < 999 && !_ISNAN_(pp->binds[0].x) && !_ISNAN_(pp->binds[0].y) && !_ISNAN_(pp->binds[0].z) && !_ISNAN_(pp->binds[0].heading)) { + if (pp->binds[0].zone_id < 999 && !_ISNAN_(pp->binds[0].x) && !_ISNAN_(pp->binds[0].y) && !_ISNAN_(pp->binds[0].z) && !_ISNAN_(pp->binds[0].heading)) { rquery = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" " VALUES (%u, %u, %u, %f, %f, %f, %f, 0)", - character_id, pp->binds[0].zoneId, 0, pp->binds[0].x, pp->binds[0].y, pp->binds[0].z, pp->binds[0].heading); + character_id, pp->binds[0].zone_id, 0, pp->binds[0].x, pp->binds[0].y, pp->binds[0].z, pp->binds[0].heading); if (rquery != ""){ results = QueryDatabase(rquery); } } /* Run Language Convert */ diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 401e62e59..808839b27 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -829,7 +829,7 @@ struct LeadershipAA_Struct { * Size: 20 Octets */ struct BindStruct { - /*000*/ uint32 zoneId; + /*000*/ uint32 zone_id; /*004*/ float x; /*008*/ float y; /*012*/ float z; @@ -1772,7 +1772,7 @@ struct GMZoneRequest_Struct { /*0068*/ float x; /*0072*/ float y; /*0076*/ float z; -/*0080*/ char unknown0080[4]; +/*0080*/ float heading; /*0084*/ uint32 success; // 0 if command failed, 1 if succeeded? /*0088*/ // /*072*/ int8 success; // =0 client->server, =1 server->client, -X=specific error diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index cb5400f1f..b607502c4 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -1968,7 +1968,7 @@ namespace RoF for (int r = 0; r < 5; r++) { - outapp->WriteUInt32(emu->binds[r].zoneId); + outapp->WriteUInt32(emu->binds[r].zone_id); outapp->WriteFloat(emu->binds[r].x); outapp->WriteFloat(emu->binds[r].y); outapp->WriteFloat(emu->binds[r].z); diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 4a902061d..a6426a279 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -2025,7 +2025,7 @@ namespace RoF2 for (int r = 0; r < 5; r++) { - outapp->WriteUInt32(emu->binds[r].zoneId); + outapp->WriteUInt32(emu->binds[r].zone_id); outapp->WriteFloat(emu->binds[r].x); outapp->WriteFloat(emu->binds[r].y); outapp->WriteFloat(emu->binds[r].z); diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index c5111da6d..3041024c7 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -1053,7 +1053,7 @@ struct LeadershipAA_Struct { * Size: 20 Octets */ struct BindStruct { - /*000*/ uint32 zoneId; + /*000*/ uint32 zone_id; /*004*/ float x; /*008*/ float y; /*012*/ float z; @@ -2084,7 +2084,7 @@ struct GMZoneRequest_Struct { /*0068*/ float x; /*0072*/ float y; /*0076*/ float z; -/*0080*/ char unknown0080[4]; +/*0080*/ float heading; /*0084*/ uint32 success; // 0 if command failed, 1 if succeeded? /*0088*/ // /*072*/ int8 success; // =0 client->server, =1 server->client, -X=specific error diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index a67e7b972..1afc79334 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -998,7 +998,7 @@ struct LeadershipAA_Struct { * Size: 20 Octets */ struct BindStruct { - /*000*/ uint32 zoneId; + /*000*/ uint32 zone_id; /*004*/ float x; /*008*/ float y; /*012*/ float z; @@ -2062,7 +2062,7 @@ struct GMZoneRequest_Struct { /*0068*/ float x; /*0072*/ float y; /*0076*/ float z; -/*0080*/ char unknown0080[4]; +/*0080*/ float heading; /*0084*/ uint32 success; // 0 if command failed, 1 if succeeded? /*0088*/ // /*072*/ int8 success; // =0 client->server, =1 server->client, -X=specific error diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index d6eae5838..9f49f9651 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -1475,7 +1475,7 @@ namespace SoD eq->level1 = emu->level; // OUT(unknown00022[2]); for (r = 0; r < 5; r++) { - OUT(binds[r].zoneId); + OUT(binds[r].zone_id); OUT(binds[r].x); OUT(binds[r].y); OUT(binds[r].z); diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 4e7735bf1..8070de172 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -803,7 +803,7 @@ struct LeadershipAA_Struct { * Size: 20 Octets */ struct BindStruct { - /*000*/ uint32 zoneId; + /*000*/ uint32 zone_id; /*004*/ float x; /*008*/ float y; /*012*/ float z; @@ -1714,7 +1714,7 @@ struct GMZoneRequest_Struct { /*0068*/ float x; /*0072*/ float y; /*0076*/ float z; -/*0080*/ char unknown0080[4]; +/*0080*/ float heading; /*0084*/ uint32 success; // 0 if command failed, 1 if succeeded? /*0088*/ // /*072*/ int8 success; // =0 client->server, =1 server->client, -X=specific error diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index f24f265ff..4fb9642a8 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -1140,7 +1140,7 @@ namespace SoF eq->level1 = emu->level; // OUT(unknown00022[2]); for (r = 0; r < 5; r++) { - OUT(binds[r].zoneId); + OUT(binds[r].zone_id); OUT(binds[r].x); OUT(binds[r].y); OUT(binds[r].z); diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index bf88bf5c2..96edbeb98 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -804,7 +804,7 @@ struct LeadershipAA_Struct { * Size: 20 Octets */ struct BindStruct { - /*000*/ uint32 zoneId; + /*000*/ uint32 zone_id; /*004*/ float x; /*008*/ float y; /*012*/ float z; @@ -1742,7 +1742,7 @@ struct GMZoneRequest_Struct { /*0068*/ float x; /*0072*/ float y; /*0076*/ float z; -/*0080*/ char unknown0080[4]; +/*0080*/ float heading; /*0084*/ uint32 success; // 0 if command failed, 1 if succeeded? /*0088*/ // /*072*/ int8 success; // =0 client->server, =1 server->client, -X=specific error diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index f7d97b8d9..9bc1f161f 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -1022,7 +1022,7 @@ namespace Titanium eq->level1 = emu->level; // OUT(unknown00022[2]); for (r = 0; r < 5; r++) { - OUT(binds[r].zoneId); + OUT(binds[r].zone_id); OUT(binds[r].x); OUT(binds[r].y); OUT(binds[r].z); diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index b2beae0cd..e3d0313a3 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -738,7 +738,7 @@ struct LeadershipAA_Struct { * Size: 20 Octets */ struct BindStruct { - /*000*/ uint32 zoneId; + /*000*/ uint32 zone_id; /*004*/ float x; /*008*/ float y; /*012*/ float z; @@ -1509,7 +1509,7 @@ struct GMZoneRequest_Struct { /*0068*/ float x; /*0072*/ float y; /*0076*/ float z; -/*0080*/ char unknown0080[4]; +/*0080*/ float heading; /*0084*/ uint32 success; // 0 if command failed, 1 if succeeded? /*0088*/ // /*072*/ int8 success; // =0 client->server, =1 server->client, -X=specific error diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index 47dafbbf3..2be12c733 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -1705,7 +1705,7 @@ namespace UF eq->level1 = emu->level; // OUT(unknown00022[2]); for (r = 0; r < 5; r++) { - OUT(binds[r].zoneId); + OUT(binds[r].zone_id); OUT(binds[r].x); OUT(binds[r].y); OUT(binds[r].z); diff --git a/common/patches/uf_structs.h b/common/patches/uf_structs.h index 4a4ac8a36..5ac8ac3c2 100644 --- a/common/patches/uf_structs.h +++ b/common/patches/uf_structs.h @@ -833,7 +833,7 @@ struct LeadershipAA_Struct { * Size: 20 Octets */ struct BindStruct { - /*000*/ uint32 zoneId; + /*000*/ uint32 zone_id; /*004*/ float x; /*008*/ float y; /*012*/ float z; @@ -1755,7 +1755,7 @@ struct GMZoneRequest_Struct { /*0068*/ float x; /*0072*/ float y; /*0076*/ float z; -/*0080*/ char unknown0080[4]; +/*0080*/ float heading; /*0084*/ uint32 success; // 0 if command failed, 1 if succeeded? /*0088*/ // /*072*/ int8 success; // =0 client->server, =1 server->client, -X=specific error diff --git a/common/version.h b/common/version.h index d66f8b060..0ad111e7c 100644 --- a/common/version.h +++ b/common/version.h @@ -34,7 +34,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9162 +#define CURRENT_BINARY_DATABASE_VERSION 9163 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9027 diff --git a/utils/deprecated/player_profile_set/player_profile_set/eq_player_structs.h b/utils/deprecated/player_profile_set/player_profile_set/eq_player_structs.h index e732757a4..3cfd276d6 100644 --- a/utils/deprecated/player_profile_set/player_profile_set/eq_player_structs.h +++ b/utils/deprecated/player_profile_set/player_profile_set/eq_player_structs.h @@ -720,7 +720,7 @@ struct RaidLeadershipAA_Struct { * Size: 20 Octets */ struct BindStruct { - /*000*/ uint32 zoneId; + /*000*/ uint32 zone_id; /*004*/ float x; /*008*/ float y; /*012*/ float z; @@ -1486,7 +1486,7 @@ struct GMZoneRequest_Struct { /*0068*/ float x; /*0072*/ float y; /*0076*/ float z; -/*0080*/ char unknown0080[4]; +/*0080*/ float heading; /*0084*/ int32 success; // 0 if command failed, 1 if succeeded? /*0088*/ // /*072*/ sint8 success; // =0 client->server, =1 server->client, -X=specific error diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 566058e3c..393dc23e8 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -416,6 +416,7 @@ 9160|2021_02_14_npc_exp_mod.sql|SHOW COLUMNS from `npc_types` LIKE 'exp_mod'|empty| 9161|2021_02_15_npc_spell_entries_unsigned.sql|SELECT * FROM db_version WHERE version >= 9161|empty| 9162|2021_02_17_server_scheduled_events.sql|SELECT * FROM db_version WHERE version >= 9162|empty| +9163|2021_04_17_zone_safe_heading_changes.sql|SHOW COLUMNS FROM `zone` LIKE 'safe_heading'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2021_04_17_zone_safe_heading_changes.sql b/utils/sql/git/required/2021_04_17_zone_safe_heading_changes.sql new file mode 100644 index 000000000..c10a5c6ff --- /dev/null +++ b/utils/sql/git/required/2021_04_17_zone_safe_heading_changes.sql @@ -0,0 +1 @@ +ALTER TABLE zone ADD COLUMN safe_heading float NOT NULL DEFAULT 0 AFTER safe_z; diff --git a/world/client.cpp b/world/client.cpp index 963369255..03c6195e8 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -1569,25 +1569,25 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) } /* Set Home Binds -- yep, all of them */ - pp.binds[1].zoneId = pp.zone_id; + pp.binds[1].zone_id = pp.zone_id; pp.binds[1].x = pp.x; pp.binds[1].y = pp.y; pp.binds[1].z = pp.z; pp.binds[1].heading = pp.heading; - pp.binds[2].zoneId = pp.zone_id; + pp.binds[2].zone_id = pp.zone_id; pp.binds[2].x = pp.x; pp.binds[2].y = pp.y; pp.binds[2].z = pp.z; pp.binds[2].heading = pp.heading; - pp.binds[3].zoneId = pp.zone_id; + pp.binds[3].zone_id = pp.zone_id; pp.binds[3].x = pp.x; pp.binds[3].y = pp.y; pp.binds[3].z = pp.z; pp.binds[3].heading = pp.heading; - pp.binds[4].zoneId = pp.zone_id; + pp.binds[4].zone_id = pp.zone_id; pp.binds[4].x = pp.x; pp.binds[4].y = pp.y; pp.binds[4].z = pp.z; @@ -1601,7 +1601,7 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) /* Will either be the same as home or tutorial if enabled. */ if(RuleB(World, StartZoneSameAsBindOnCreation)) { - pp.binds[0].zoneId = pp.zone_id; + pp.binds[0].zone_id = pp.zone_id; pp.binds[0].x = pp.x; pp.binds[0].y = pp.y; pp.binds[0].z = pp.z; @@ -1611,9 +1611,9 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) Log(Logs::Detail, Logs::WorldServer, "Current location: %s (%d) %0.2f, %0.2f, %0.2f, %0.2f", ZoneName(pp.zone_id), pp.zone_id, pp.x, pp.y, pp.z, pp.heading); Log(Logs::Detail, Logs::WorldServer, "Bind location: %s (%d) %0.2f, %0.2f, %0.2f", - ZoneName(pp.binds[0].zoneId), pp.binds[0].zoneId, pp.binds[0].x, pp.binds[0].y, pp.binds[0].z); + ZoneName(pp.binds[0].zone_id), pp.binds[0].zone_id, pp.binds[0].x, pp.binds[0].y, pp.binds[0].z); Log(Logs::Detail, Logs::WorldServer, "Home location: %s (%d) %0.2f, %0.2f, %0.2f", - ZoneName(pp.binds[4].zoneId), pp.binds[4].zoneId, pp.binds[4].x, pp.binds[4].y, pp.binds[4].z); + ZoneName(pp.binds[4].zone_id), pp.binds[4].zone_id, pp.binds[4].x, pp.binds[4].y, pp.binds[4].z); /* Starting Items inventory */ content_db.SetStartingItems(&pp, &inv, pp.race, pp.class_, pp.deity, pp.zone_id, pp.name, GetAdmin()); diff --git a/world/worlddb.cpp b/world/worlddb.cpp index 7bc5a801f..69d15c751 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -54,42 +54,48 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **o character_limit = 8; } - std::string character_list_query = StringFormat( - "SELECT " - "`id`, " // 0 - "name, " // 1 - "gender, " // 2 - "race, " // 3 - "class, " // 4 - "`level`, " // 5 - "deity, " // 6 - "last_login, " // 7 - "time_played, " // 8 - "hair_color, " // 9 - "beard_color, " // 10 - "eye_color_1, " // 11 - "eye_color_2, " // 12 - "hair_style, " // 13 - "beard, " // 14 - "face, " // 15 - "drakkin_heritage, " // 16 - "drakkin_tattoo, " // 17 - "drakkin_details, " // 18 - "zone_id " // 19 - "FROM " - "character_data " - "WHERE `account_id` = %i AND deleted_at IS NULL ORDER BY `name` LIMIT %u", + std::string character_list_query = fmt::format( + SQL( + SELECT + `id`, + `name`, + `gender`, + `race`, + `class`, + `level`, + `deity`, + `last_login`, + `time_played`, + `hair_color`, + `beard_color`, + `eye_color_1`, + `eye_color_2`, + `hair_style`, + `beard`, + `face`, + `drakkin_heritage`, + `drakkin_tattoo`, + `drakkin_details`, + `zone_id` + FROM + `character_data` + WHERE + `account_id` = {} + AND + `deleted_at` IS NULL + ORDER BY `name` + LIMIT {} + ), account_id, character_limit ); auto results = database.QueryDatabase(character_list_query); - size_t character_count = results.RowCount(); if (character_count == 0) { - *out_app = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct)); + *out_app = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct)); CharacterSelect_Struct *cs = (CharacterSelect_Struct *) (*out_app)->pBuffer; - cs->CharCount = 0; + cs->CharCount = 0; cs->TotalChars = character_limit; return; } @@ -97,155 +103,181 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **o size_t packet_size = sizeof(CharacterSelect_Struct) + (sizeof(CharacterSelectEntry_Struct) * character_count); *out_app = new EQApplicationPacket(OP_SendCharInfo, packet_size); - unsigned char *buff_ptr = (*out_app)->pBuffer; - CharacterSelect_Struct *cs = (CharacterSelect_Struct *) buff_ptr; + unsigned char *buff_ptr = (*out_app)->pBuffer; + CharacterSelect_Struct *cs = (CharacterSelect_Struct *) buff_ptr; - cs->CharCount = character_count; + cs->CharCount = character_count; cs->TotalChars = character_limit; buff_ptr += sizeof(CharacterSelect_Struct); for (auto row = results.begin(); row != results.end(); ++row) { CharacterSelectEntry_Struct *p_character_select_entry_struct = (CharacterSelectEntry_Struct *) buff_ptr; - PlayerProfile_Struct player_profile_struct; - EQ::InventoryProfile inventory_profile; + PlayerProfile_Struct player_profile_struct; + EQ::InventoryProfile inventory_profile; player_profile_struct.SetPlayerProfileVersion(EQ::versions::ConvertClientVersionToMobVersion(client_version)); inventory_profile.SetInventoryVersion(client_version); inventory_profile.SetGMInventory(true); // charsel can not interact with items..but, no harm in setting to full expansion support uint32 character_id = (uint32) atoi(row[0]); - uint8 has_home = 0; - uint8 has_bind = 0; + uint8 has_home = 0; + uint8 has_bind = 0; memset(&player_profile_struct, 0, sizeof(PlayerProfile_Struct)); - memset(p_character_select_entry_struct->Name, 0, sizeof(p_character_select_entry_struct->Name)); strcpy(p_character_select_entry_struct->Name, row[1]); - p_character_select_entry_struct->Class = (uint8) atoi(row[4]); - p_character_select_entry_struct->Race = (uint32) atoi(row[3]); - p_character_select_entry_struct->Level = (uint8) atoi(row[5]); + p_character_select_entry_struct->Class = (uint8) atoi(row[4]); + p_character_select_entry_struct->Race = (uint32) atoi(row[3]); + p_character_select_entry_struct->Level = (uint8) atoi(row[5]); p_character_select_entry_struct->ShroudClass = p_character_select_entry_struct->Class; - p_character_select_entry_struct->ShroudRace = p_character_select_entry_struct->Race; - p_character_select_entry_struct->Zone = (uint16) atoi(row[19]); - p_character_select_entry_struct->Instance = 0; - p_character_select_entry_struct->Gender = (uint8) atoi(row[2]); - p_character_select_entry_struct->Face = (uint8) atoi(row[15]); + p_character_select_entry_struct->ShroudRace = p_character_select_entry_struct->Race; + p_character_select_entry_struct->Zone = (uint16) atoi(row[19]); + p_character_select_entry_struct->Instance = 0; + p_character_select_entry_struct->Gender = (uint8) atoi(row[2]); + p_character_select_entry_struct->Face = (uint8) atoi(row[15]); for (uint32 material_slot = 0; material_slot < EQ::textures::materialCount; material_slot++) { - p_character_select_entry_struct->Equip[material_slot].Material = 0; - p_character_select_entry_struct->Equip[material_slot].Unknown1 = 0; - p_character_select_entry_struct->Equip[material_slot].EliteModel = 0; + p_character_select_entry_struct->Equip[material_slot].Material = 0; + p_character_select_entry_struct->Equip[material_slot].Unknown1 = 0; + p_character_select_entry_struct->Equip[material_slot].EliteModel = 0; p_character_select_entry_struct->Equip[material_slot].HerosForgeModel = 0; - p_character_select_entry_struct->Equip[material_slot].Unknown2 = 0; - p_character_select_entry_struct->Equip[material_slot].Color = 0; + p_character_select_entry_struct->Equip[material_slot].Unknown2 = 0; + p_character_select_entry_struct->Equip[material_slot].Color = 0; } - p_character_select_entry_struct->Unknown15 = 0xFF; - p_character_select_entry_struct->Unknown19 = 0xFF; - p_character_select_entry_struct->DrakkinTattoo = (uint32) atoi(row[17]); - p_character_select_entry_struct->DrakkinDetails = (uint32) atoi(row[18]); - p_character_select_entry_struct->Deity = (uint32) atoi(row[6]); - p_character_select_entry_struct->PrimaryIDFile = 0; // Processed Below + p_character_select_entry_struct->Unknown15 = 0xFF; + p_character_select_entry_struct->Unknown19 = 0xFF; + p_character_select_entry_struct->DrakkinTattoo = (uint32) atoi(row[17]); + p_character_select_entry_struct->DrakkinDetails = (uint32) atoi(row[18]); + p_character_select_entry_struct->Deity = (uint32) atoi(row[6]); + p_character_select_entry_struct->PrimaryIDFile = 0; // Processed Below p_character_select_entry_struct->SecondaryIDFile = 0; // Processed Below - p_character_select_entry_struct->HairColor = (uint8) atoi(row[9]); - p_character_select_entry_struct->BeardColor = (uint8) atoi(row[10]); - p_character_select_entry_struct->EyeColor1 = (uint8) atoi(row[11]); - p_character_select_entry_struct->EyeColor2 = (uint8) atoi(row[12]); - p_character_select_entry_struct->HairStyle = (uint8) atoi(row[13]); - p_character_select_entry_struct->Beard = (uint8) atoi(row[14]); - p_character_select_entry_struct->GoHome = 0; // Processed Below - p_character_select_entry_struct->Tutorial = 0; // Processed Below + p_character_select_entry_struct->HairColor = (uint8) atoi(row[9]); + p_character_select_entry_struct->BeardColor = (uint8) atoi(row[10]); + p_character_select_entry_struct->EyeColor1 = (uint8) atoi(row[11]); + p_character_select_entry_struct->EyeColor2 = (uint8) atoi(row[12]); + p_character_select_entry_struct->HairStyle = (uint8) atoi(row[13]); + p_character_select_entry_struct->Beard = (uint8) atoi(row[14]); + p_character_select_entry_struct->GoHome = 0; // Processed Below + p_character_select_entry_struct->Tutorial = 0; // Processed Below p_character_select_entry_struct->DrakkinHeritage = (uint32) atoi(row[16]); - p_character_select_entry_struct->Unknown1 = 0; - p_character_select_entry_struct->Enabled = 1; - p_character_select_entry_struct->LastLogin = (uint32) atoi(row[7]); // RoF2 value: 1212696584 - p_character_select_entry_struct->Unknown2 = 0; + p_character_select_entry_struct->Unknown1 = 0; + p_character_select_entry_struct->Enabled = 1; + p_character_select_entry_struct->LastLogin = (uint32) atoi(row[7]); // RoF2 value: 1212696584 + p_character_select_entry_struct->Unknown2 = 0; if (RuleB(World, EnableReturnHomeButton)) { int now = time(nullptr); - if ((now - atoi(row[7])) >= RuleI(World, MinOfflineTimeToReturnHome)) { + if ((now - atoi(row[7])) >= RuleI(World, MinOfflineTimeToReturnHome)) p_character_select_entry_struct->GoHome = 1; - } } - if (RuleB(World, EnableTutorialButton) && (p_character_select_entry_struct->Level <= RuleI(World, MaxLevelForTutorial))) { + if (RuleB(World, EnableTutorialButton) && (p_character_select_entry_struct->Level <= RuleI(World, MaxLevelForTutorial))) p_character_select_entry_struct->Tutorial = 1; - } /** * Bind */ - character_list_query = StringFormat( - "SELECT `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot` FROM `character_bind` WHERE `id` = %i LIMIT 5", + character_list_query = fmt::format( + SQL( + SELECT + `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot` + FROM + `character_bind` + WHERE + `id` = {} + LIMIT 5 + ), character_id ); - auto results_bind = database.QueryDatabase(character_list_query); - auto bind_count = results_bind.RowCount(); - for (auto row_b = results_bind.begin(); row_b != results_bind.end(); ++row_b) { + auto results_bind = database.QueryDatabase(character_list_query); + auto bind_count = results_bind.RowCount(); + for (auto row_b = results_bind.begin(); row_b != results_bind.end(); ++row_b) { if (row_b[6] && atoi(row_b[6]) == 4) { has_home = 1; // If our bind count is less than 5, we need to actually make use of this data so lets parse it if (bind_count < 5) { - player_profile_struct.binds[4].zoneId = atoi(row_b[0]); + player_profile_struct.binds[4].zone_id = atoi(row_b[0]); player_profile_struct.binds[4].instance_id = atoi(row_b[1]); - player_profile_struct.binds[4].x = atof(row_b[2]); - player_profile_struct.binds[4].y = atof(row_b[3]); - player_profile_struct.binds[4].z = atof(row_b[4]); - player_profile_struct.binds[4].heading = atof(row_b[5]); + player_profile_struct.binds[4].x = atof(row_b[2]); + player_profile_struct.binds[4].y = atof(row_b[3]); + player_profile_struct.binds[4].z = atof(row_b[4]); + player_profile_struct.binds[4].heading = atof(row_b[5]); } } - if (row_b[6] && atoi(row_b[6]) == 0) { has_bind = 1; } + + if (row_b[6] && atoi(row_b[6]) == 0) + has_bind = 1; } if (has_home == 0 || has_bind == 0) { - character_list_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 %s", + std::string character_list_query = fmt::format( + SQL( + SELECT + `zone_id`, `bind_id`, `x`, `y`, `z`, `heading` + FROM + `start_zones` + WHERE + `player_class` = {} + AND + `player_deity` = {} + AND + `player_race` = {} {} + ), p_character_select_entry_struct->Class, p_character_select_entry_struct->Deity, p_character_select_entry_struct->Race, ContentFilterCriteria::apply().c_str() ); - auto results_bind = content_db.QueryDatabase(character_list_query); - for (auto row_d = results_bind.begin(); row_d != results_bind.end(); ++row_d) { + auto results_bind = content_db.QueryDatabase(character_list_query); + for (auto row_d = results_bind.begin(); row_d != results_bind.end(); ++row_d) { /* If a bind_id is specified, make them start there */ if (atoi(row_d[1]) != 0) { - player_profile_struct.binds[4].zoneId = (uint32) atoi(row_d[1]); + player_profile_struct.binds[4].zone_id = (uint32) atoi(row_d[1]); content_db.GetSafePoints( - ZoneName(player_profile_struct.binds[4].zoneId), + ZoneName(player_profile_struct.binds[4].zone_id), 0, &player_profile_struct.binds[4].x, &player_profile_struct.binds[4].y, - &player_profile_struct.binds[4].z + &player_profile_struct.binds[4].z, + &player_profile_struct.binds[4].heading ); } /* Otherwise, use the zone and coordinates given */ else { - player_profile_struct.binds[4].zoneId = (uint32) atoi(row_d[0]); + player_profile_struct.binds[4].zone_id = (uint32) atoi(row_d[0]); float x = atof(row_d[2]); float y = atof(row_d[3]); float z = atof(row_d[4]); - if (x == 0 && y == 0 && z == 0) { + float heading = atof(row_d[5]); + if (x == 0 && y == 0 && z == 0 && heading == 0) { content_db.GetSafePoints( - ZoneName(player_profile_struct.binds[4].zoneId), + ZoneName(player_profile_struct.binds[4].zone_id), 0, &x, &y, - &z + &z, + &heading ); } player_profile_struct.binds[4].x = x; player_profile_struct.binds[4].y = y; player_profile_struct.binds[4].z = z; + player_profile_struct.binds[4].heading = heading; } } player_profile_struct.binds[0] = player_profile_struct.binds[4]; /* If no home bind set, set it */ if (has_home == 0) { - std::string query = StringFormat( - "REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)" - " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", + std::string query = fmt::format( + SQL( + REPLACE INTO + `character_bind` + (`id`, `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot`) + VALUES ({}, {}, {}, {}, {}, {}, {}, {}) + ), character_id, - player_profile_struct.binds[4].zoneId, + player_profile_struct.binds[4].zone_id, 0, player_profile_struct.binds[4].x, player_profile_struct.binds[4].y, @@ -253,15 +285,19 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **o player_profile_struct.binds[4].heading, 4 ); - auto results_bset = QueryDatabase(query); + auto results_bset = QueryDatabase(query); } /* If no regular bind set, set it */ if (has_bind == 0) { - std::string query = StringFormat( - "REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)" - " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", + std::string query = fmt::format( + SQL( + REPLACE INTO + `character_bind` + (`id`, `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot`) + VALUES ({}, {}, {}, {}, {}, {}, {}, {}) + ), character_id, - player_profile_struct.binds[0].zoneId, + player_profile_struct.binds[0].zone_id, 0, player_profile_struct.binds[0].x, player_profile_struct.binds[0].y, @@ -269,7 +305,7 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **o player_profile_struct.binds[0].heading, 0 ); - auto results_bset = QueryDatabase(query); + auto results_bset = QueryDatabase(query); } } /* If our bind count is less than 5, then we have null data that needs to be filled in. */ @@ -277,15 +313,18 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **o // we know that home and main bind must be valid here, so we don't check those // we also use home to fill in the null data like live does. for (int i = 1; i < 4; i++) { - if (player_profile_struct.binds[i].zoneId != 0) { // we assume 0 is the only invalid one ... + if (player_profile_struct.binds[i].zone_id != 0) // we assume 0 is the only invalid one ... continue; - } - std::string query = StringFormat( - "REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)" - " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", + std::string query = fmt::format( + SQL( + REPLACE INTO + `character_bind` + (`id`, `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot`) + VALUES ({}, {}, {}, {}, {}, {}, {}, {}) + ), character_id, - player_profile_struct.binds[4].zoneId, + player_profile_struct.binds[4].zone_id, 0, player_profile_struct.binds[4].x, player_profile_struct.binds[4].y, @@ -293,40 +332,45 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **o player_profile_struct.binds[4].heading, i ); - auto results_bset = QueryDatabase(query); + auto results_bset = QueryDatabase(query); } } - character_list_query = StringFormat( - "SELECT slot, red, green, blue, use_tint, color FROM `character_material` WHERE `id` = %u", + character_list_query = fmt::format( + SQL( + SELECT + `slot`, `red`, `green`, `blue`, `use_tint`, `color` + FROM + `character_material` + WHERE + `id` = {} + ), character_id ); - auto results_b = database.QueryDatabase(character_list_query); - uint8 slot = 0; - for (auto row_b = results_b.begin(); row_b != results_b.end(); ++row_b) { + auto results_b = database.QueryDatabase(character_list_query); + uint8 slot = 0; + for (auto row_b = results_b.begin(); row_b != results_b.end(); ++row_b) { slot = atoi(row_b[0]); - player_profile_struct.item_tint.Slot[slot].Red = atoi(row_b[1]); - player_profile_struct.item_tint.Slot[slot].Green = atoi(row_b[2]); - player_profile_struct.item_tint.Slot[slot].Blue = atoi(row_b[3]); + player_profile_struct.item_tint.Slot[slot].Red = atoi(row_b[1]); + player_profile_struct.item_tint.Slot[slot].Green = atoi(row_b[2]); + player_profile_struct.item_tint.Slot[slot].Blue = atoi(row_b[3]); player_profile_struct.item_tint.Slot[slot].UseTint = atoi(row_b[4]); } if (GetCharSelInventory(account_id, p_character_select_entry_struct->Name, &inventory_profile)) { - const EQ::ItemData *item = nullptr; - const EQ::ItemInstance *inst = nullptr; + const EQ::ItemData *item = nullptr; + const EQ::ItemInstance *inst = nullptr; int16 inventory_slot = 0; - for (uint32 matslot = EQ::textures::textureBegin; matslot < EQ::textures::materialCount; matslot++) { inventory_slot = EQ::InventoryProfile::CalcSlotFromMaterial(matslot); if (inventory_slot == INVALID_INDEX) { continue; } inst = inventory_profile.GetItem(inventory_slot); - if (inst == nullptr) { + if (inst == nullptr) continue; - } + item = inst->GetItem(); - if (item == nullptr) { + if (item == nullptr) continue; - } if (matslot > 6) { uint32 item_id_file = 0; @@ -334,54 +378,59 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **o if (inst->GetOrnamentationIDFile() != 0) { item_id_file = inst->GetOrnamentationIDFile(); p_character_select_entry_struct->Equip[matslot].Material = item_id_file; - } - else { + } else { if (strlen(item->IDFile) > 2) { item_id_file = atoi(&item->IDFile[2]); p_character_select_entry_struct->Equip[matslot].Material = item_id_file; } } + if (matslot == EQ::textures::weaponPrimary) { p_character_select_entry_struct->PrimaryIDFile = item_id_file; - } - else { + } else { p_character_select_entry_struct->SecondaryIDFile = item_id_file; } - } - else { - uint32 color = 0; - if (player_profile_struct.item_tint.Slot[matslot].UseTint) { - color = player_profile_struct.item_tint.Slot[matslot].Color; - } - else { - color = inst->GetColor(); - } - + } else { // Armor Materials/Models - p_character_select_entry_struct->Equip[matslot].Material = item->Material; - p_character_select_entry_struct->Equip[matslot].EliteModel = item->EliteMaterial; + uint32 color = ( + player_profile_struct.item_tint.Slot[matslot].UseTint ? + player_profile_struct.item_tint.Slot[matslot].Color : + inst->GetColor() + ); + p_character_select_entry_struct->Equip[matslot].Material = item->Material; + p_character_select_entry_struct->Equip[matslot].EliteModel = item->EliteMaterial; p_character_select_entry_struct->Equip[matslot].HerosForgeModel = inst->GetOrnamentHeroModel(matslot); - p_character_select_entry_struct->Equip[matslot].Color = color; + p_character_select_entry_struct->Equip[matslot].Color = color; } } - } - else { + } else { printf("Error loading inventory for %s\n", p_character_select_entry_struct->Name); } - buff_ptr += sizeof(CharacterSelectEntry_Struct); } } -int WorldDatabase::MoveCharacterToBind(int CharID, uint8 bindnum) +int WorldDatabase::MoveCharacterToBind(int character_id, uint8 bind_number) { /* if an invalid bind point is specified, use the primary bind */ - if (bindnum > 4) - { - bindnum = 0; - } + if (bind_number > 4) + bind_number = 0; - std::string query = StringFormat("SELECT zone_id, instance_id, x, y, z FROM character_bind WHERE id = %u AND slot = %u LIMIT 1", CharID, bindnum); + std::string query = fmt::format( + SQL( + SELECT + zone_id, instance_id, x, y, z, heading + FROM + character_bind + WHERE + id = {} + AND + slot = {} + LIMIT 1 + ), + character_id, + bind_number + ); auto results = database.QueryDatabase(query); if(!results.Success() || results.RowCount() == 0) { return 0; @@ -398,8 +447,27 @@ int WorldDatabase::MoveCharacterToBind(int CharID, uint8 bindnum) heading = atof(row[5]); } - query = StringFormat("UPDATE character_data SET zone_id = '%d', zone_instance = '%d', x = '%f', y = '%f', z = '%f', heading = '%f' WHERE id = %u", - zone_id, instance_id, x, y, z, heading, CharID); + query = fmt::format( + SQL( + UPDATE + `character_data` + SET + `zone_id` = {}, + `zone_instance` = {}, + `x` = {}, + `y` = {}, + `z` = {}, + `heading` = {} + WHERE `id` = {} + ), + zone_id, + instance_id, + x, + y, + z, + heading, + character_id + ); results = database.QueryDatabase(query); if(!results.Success()) { @@ -434,7 +502,7 @@ bool WorldDatabase::GetStartZone( p_player_profile_struct->binds[0].x = 0; p_player_profile_struct->binds[0].y = 0; p_player_profile_struct->binds[0].z = 0; - p_player_profile_struct->binds[0].zoneId = 0; + p_player_profile_struct->binds[0].zone_id = 0; p_player_profile_struct->binds[0].instance_id = 0; // see if we have an entry for start_zone. We can support both titanium & SOF+ by having two entries per class/race/deity combo with different zone_ids @@ -480,35 +548,47 @@ bool WorldDatabase::GetStartZone( else { LogInfo("Found starting location in start_zones"); auto row = results.begin(); - p_player_profile_struct->x = atof(row[0]); - p_player_profile_struct->y = atof(row[1]); - p_player_profile_struct->z = atof(row[2]); - p_player_profile_struct->heading = atof(row[3]); - p_player_profile_struct->zone_id = atoi(row[4]); - p_player_profile_struct->binds[0].zoneId = atoi(row[5]); - p_player_profile_struct->binds[0].x = atof(row[6]); - p_player_profile_struct->binds[0].y = atof(row[7]); - p_player_profile_struct->binds[0].z = atof(row[8]); + p_player_profile_struct->x = atof(row[0]); + p_player_profile_struct->y = atof(row[1]); + p_player_profile_struct->z = atof(row[2]); + p_player_profile_struct->heading = atof(row[3]); + p_player_profile_struct->zone_id = atoi(row[4]); + p_player_profile_struct->binds[0].zone_id = atoi(row[5]); + p_player_profile_struct->binds[0].x = atof(row[6]); + p_player_profile_struct->binds[0].y = atof(row[7]); + p_player_profile_struct->binds[0].z = atof(row[8]); + p_player_profile_struct->binds[0].heading = atof(row[3]); } - if (p_player_profile_struct->x == 0 && p_player_profile_struct->y == 0 && p_player_profile_struct->z == 0) { + if ( + p_player_profile_struct->x == 0 && + p_player_profile_struct->y == 0 && + p_player_profile_struct->z == 0 && + p_player_profile_struct->heading == 0 + ) { content_db.GetSafePoints( ZoneName(p_player_profile_struct->zone_id), 0, &p_player_profile_struct->x, &p_player_profile_struct->y, - &p_player_profile_struct->z + &p_player_profile_struct->z, + &p_player_profile_struct->heading ); } - if (p_player_profile_struct->binds[0].x == 0 && p_player_profile_struct->binds[0].y == 0 && - p_player_profile_struct->binds[0].z == 0) { + if ( + p_player_profile_struct->binds[0].x == 0 && + p_player_profile_struct->binds[0].y == 0 && + p_player_profile_struct->binds[0].z == 0 && + p_player_profile_struct->binds[0].heading == 0 + ) { content_db.GetSafePoints( - ZoneName(p_player_profile_struct->binds[0].zoneId), + ZoneName(p_player_profile_struct->binds[0].zone_id), 0, &p_player_profile_struct->binds[0].x, &p_player_profile_struct->binds[0].y, - &p_player_profile_struct->binds[0].z + &p_player_profile_struct->binds[0].z, + &p_player_profile_struct->binds[0].heading ); } @@ -520,10 +600,11 @@ void WorldDatabase::SetSoFDefaultStartZone(PlayerProfile_Struct* in_pp, CharCrea in_pp->zone_id = in_cc->start_zone; } else { - in_pp->x = in_pp->binds[0].x = -51; - in_pp->y = in_pp->binds[0].y = -20; - in_pp->z = in_pp->binds[0].z = 0.79; - in_pp->zone_id = in_pp->binds[0].zoneId = 394; // Crescent Reach. + in_pp->x = in_pp->binds[0].x = -51.0f; + in_pp->y = in_pp->binds[0].y = -20.0f; + in_pp->z = in_pp->binds[0].z = 0.79f; + in_pp->heading = in_pp->binds[0].heading = 0.0f; + in_pp->zone_id = in_pp->binds[0].zone_id = 394; // Crescent Reach. } } @@ -536,91 +617,91 @@ void WorldDatabase::SetTitaniumDefaultStartZone(PlayerProfile_Struct* in_pp, Cha if (in_cc->deity == 203) // Cazic-Thule Erudites go to Paineel { in_pp->zone_id = 75; // paineel - in_pp->binds[0].zoneId = 75; + in_pp->binds[0].zone_id = 75; } else { in_pp->zone_id = 24; // erudnext - in_pp->binds[0].zoneId = 38; // tox + in_pp->binds[0].zone_id = 38; // tox } break; } case 1: { in_pp->zone_id = 2; // qeynos2 - in_pp->binds[0].zoneId = 2; // qeynos2 + in_pp->binds[0].zone_id = 2; // qeynos2 break; } case 2: { in_pp->zone_id = 29; // halas - in_pp->binds[0].zoneId = 30; // everfrost + in_pp->binds[0].zone_id = 30; // everfrost break; } case 3: { in_pp->zone_id = 19; // rivervale - in_pp->binds[0].zoneId = 20; // kithicor + in_pp->binds[0].zone_id = 20; // kithicor break; } case 4: { in_pp->zone_id = 9; // freportw - in_pp->binds[0].zoneId = 9; // freportw + in_pp->binds[0].zone_id = 9; // freportw break; } case 5: { in_pp->zone_id = 40; // neriaka - in_pp->binds[0].zoneId = 25; // nektulos + in_pp->binds[0].zone_id = 25; // nektulos break; } case 6: { in_pp->zone_id = 52; // gukta - in_pp->binds[0].zoneId = 46; // innothule + in_pp->binds[0].zone_id = 46; // innothule break; } case 7: { in_pp->zone_id = 49; // oggok - in_pp->binds[0].zoneId = 47; // feerrott + in_pp->binds[0].zone_id = 47; // feerrott break; } case 8: { in_pp->zone_id = 60; // kaladima - in_pp->binds[0].zoneId = 68; // butcher + in_pp->binds[0].zone_id = 68; // butcher break; } case 9: { in_pp->zone_id = 54; // gfaydark - in_pp->binds[0].zoneId = 54; // gfaydark + in_pp->binds[0].zone_id = 54; // gfaydark break; } case 10: { in_pp->zone_id = 61; // felwithea - in_pp->binds[0].zoneId = 54; // gfaydark + in_pp->binds[0].zone_id = 54; // gfaydark break; } case 11: { in_pp->zone_id = 55; // akanon - in_pp->binds[0].zoneId = 56; // steamfont + in_pp->binds[0].zone_id = 56; // steamfont break; } case 12: { in_pp->zone_id = 82; // cabwest - in_pp->binds[0].zoneId = 78; // fieldofbone + in_pp->binds[0].zone_id = 78; // fieldofbone break; } case 13: { in_pp->zone_id = 155; // sharvahl - in_pp->binds[0].zoneId = 155; // sharvahl + in_pp->binds[0].zone_id = 155; // sharvahl break; } } diff --git a/world/worlddb.h b/world/worlddb.h index 42fbf96b6..6431f175e 100644 --- a/world/worlddb.h +++ b/world/worlddb.h @@ -31,7 +31,7 @@ class WorldDatabase : public SharedDatabase { public: bool GetStartZone(PlayerProfile_Struct* p_player_profile_struct, CharCreate_Struct* p_char_create_struct, bool is_titanium); void GetCharSelectInfo(uint32 account_id, EQApplicationPacket **out_app, uint32 client_version_bit); - int MoveCharacterToBind(int CharID, uint8 bindnum = 0); + int MoveCharacterToBind(int character_id, uint8 bind_number = 0); void GetLauncherList(std::vector &result); bool GetCharacterLevel(const char *name, int &level); diff --git a/zone/attack.cpp b/zone/attack.cpp index 3f614598a..2f85dbaaf 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1655,7 +1655,7 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQ::skills::Skill d->spawn_id = GetID(); d->killer_id = killerMob ? killerMob->GetID() : 0; d->corpseid = GetID(); - d->bindzoneid = m_pp.binds[0].zoneId; + d->bindzoneid = m_pp.binds[0].zone_id; d->spell_id = spell == SPELL_UNKNOWN ? 0xffffffff : spell; d->attack_skill = spell != SPELL_UNKNOWN ? 0xe7 : attack_skill; d->damage = damage; @@ -1892,7 +1892,7 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQ::skills::Skill r->MemberZoned(this); dead_timer.Start(5000, true); - m_pp.zone_id = m_pp.binds[0].zoneId; + m_pp.zone_id = m_pp.binds[0].zone_id; m_pp.zoneInstance = m_pp.binds[0].instance_id; database.MoveCharacterToZone(this->CharacterID(), m_pp.zone_id); Save(); diff --git a/zone/client.cpp b/zone/client.cpp index c34a314be..6ffda19a9 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -163,7 +163,7 @@ Client::Client(EQStreamInterface* ieqs) helm_toggle_timer(250), aggro_meter_timer(AGGRO_METER_UPDATE_MS), m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number - m_ZoneSummonLocation(-2.0f,-2.0f,-2.0f), + m_ZoneSummonLocation(-2.0f,-2.0f,-2.0f,-2.0f), m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f), m_AutoAttackTargetLocation(0.0f, 0.0f, 0.0f), last_region_type(RegionTypeUnsupported), @@ -441,7 +441,7 @@ Client::~Client() { if(IsHoveringForRespawn()) { - m_pp.zone_id = m_pp.binds[0].zoneId; + m_pp.zone_id = m_pp.binds[0].zone_id; m_pp.zoneInstance = m_pp.binds[0].instance_id; m_Position.x = m_pp.binds[0].x; m_Position.y = m_pp.binds[0].y; @@ -658,7 +658,7 @@ bool Client::Save(uint8 iCommitNow) { /* Save Current Bind Points */ for (int i = 0; i < 5; i++) - if (m_pp.binds[i].zoneId) + if (m_pp.binds[i].zone_id) database.SaveCharacterBindPoint(CharacterID(), m_pp.binds[i], i); /* Save Character Buffs */ @@ -3959,7 +3959,7 @@ void Client::Sacrifice(Client *caster) Death_Struct *d = (Death_Struct *)app.pBuffer; d->spawn_id = GetID(); d->killer_id = caster ? caster->GetID() : 0; - d->bindzoneid = GetPP().binds[0].zoneId; + d->bindzoneid = GetPP().binds[0].zone_id; d->spell_id = SPELL_UNKNOWN; d->attack_skill = 0xe7; d->damage = 0; @@ -4007,7 +4007,7 @@ void Client::SendOPTranslocateConfirm(Mob *Caster, uint16 SpellID) { PendingTranslocateData.spell_id = ts->SpellID = SpellID; if((SpellID == 1422) || (SpellID == 1334) || (SpellID == 3243)) { - PendingTranslocateData.zone_id = ts->ZoneID = m_pp.binds[0].zoneId; + PendingTranslocateData.zone_id = ts->ZoneID = m_pp.binds[0].zone_id; PendingTranslocateData.instance_id = m_pp.binds[0].instance_id; PendingTranslocateData.x = ts->x = m_pp.binds[0].x; PendingTranslocateData.y = ts->y = m_pp.binds[0].y; @@ -4892,7 +4892,7 @@ void Client::SendRespawnBinds() BindStruct* b = &m_pp.binds[0]; RespawnOption opt; opt.name = "Bind Location"; - opt.zone_id = b->zoneId; + opt.zone_id = b->zone_id; opt.instance_id = b->instance_id; opt.x = b->x; opt.y = b->y; @@ -5289,11 +5289,11 @@ void Client::NotifyNewTitlesAvailable() } -void Client::SetStartZone(uint32 zoneid, float x, float y, float z) +void Client::SetStartZone(uint32 zoneid, float x, float y, float z, float heading) { // setting city to zero allows the player to use /setstartcity to set the city themselves if(zoneid == 0) { - m_pp.binds[4].zoneId = 0; + m_pp.binds[4].zone_id = 0; this->Message(Chat::Yellow,"Your starting city has been reset. Use /setstartcity to choose a new one"); return; } @@ -5303,24 +5303,32 @@ void Client::SetStartZone(uint32 zoneid, float x, float y, float z) if(target_zone_name == nullptr) return; - m_pp.binds[4].zoneId = zoneid; + m_pp.binds[4].zone_id = zoneid; if(zone->GetInstanceID() != 0 && zone->IsInstancePersistent()) { m_pp.binds[4].instance_id = zone->GetInstanceID(); } if (x == 0 && y == 0 && z == 0) { - content_db.GetSafePoints(ZoneName(m_pp.binds[4].zoneId), 0, &m_pp.binds[4].x, &m_pp.binds[4].y, &m_pp.binds[4].z); + content_db.GetSafePoints( + ZoneName(m_pp.binds[4].zone_id), + 0, + &m_pp.binds[4].x, + &m_pp.binds[4].y, + &m_pp.binds[4].z, + &m_pp.binds[4].heading + ); } else { m_pp.binds[4].x = x; m_pp.binds[4].y = y; m_pp.binds[4].z = z; + m_pp.binds[4].heading = heading; } } uint32 Client::GetStartZone() { - return m_pp.binds[4].zoneId; + return m_pp.binds[4].zone_id; } void Client::ShowSkillsWindow() diff --git a/zone/client.h b/zone/client.h index d2abf3efb..741408d3f 100644 --- a/zone/client.h +++ b/zone/client.h @@ -426,7 +426,7 @@ public: inline const float GetBindY(uint32 index = 0) const { return m_pp.binds[index].y; } inline const float GetBindZ(uint32 index = 0) const { return m_pp.binds[index].z; } inline const float GetBindHeading(uint32 index = 0) const { return m_pp.binds[index].heading; } - inline uint32 GetBindZoneID(uint32 index = 0) const { return m_pp.binds[index].zoneId; } + inline uint32 GetBindZoneID(uint32 index = 0) const { return m_pp.binds[index].zone_id; } inline uint32 GetBindInstanceID(uint32 index = 0) const { return m_pp.binds[index].instance_id; } int32 CalcMaxMana(); int32 CalcBaseMana(); @@ -644,7 +644,8 @@ public: void GoToSafeCoords(uint16 zone_id, uint16 instance_id); void Gate(uint8 bindnum = 0); void SetBindPoint(int bind_num = 0, int to_zone = -1, int to_instance = 0, const glm::vec3& location = glm::vec3()); - void SetStartZone(uint32 zoneid, float x = 0.0f, float y =0.0f, float z = 0.0f); + void SetBindPoint2(int bind_num = 0, int to_zone = -1, int to_instance = 0, const glm::vec4& location = glm::vec4()); + void SetStartZone(uint32 zoneid, float x = 0.0f, float y =0.0f, float z = 0.0f, float heading = 0.0f); uint32 GetStartZone(void); void MovePC(const char* zonename, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited); void MovePC(uint32 zoneID, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited); @@ -1765,7 +1766,7 @@ private: void ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z, float heading, uint8 ignorerestrictions, ZoneMode zm); void ProcessMovePC(uint32 zoneID, uint32 instance_id, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited); - glm::vec3 m_ZoneSummonLocation; + glm::vec4 m_ZoneSummonLocation; uint16 zonesummon_id; uint8 zonesummon_ignorerestrictions; ZoneMode zone_mode; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index a48925e5e..3ffb2d785 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1332,10 +1332,11 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) strcpy(lastname, m_pp.last_name); /* If PP is set to weird coordinates */ if ((m_pp.x == -1 && m_pp.y == -1 && m_pp.z == -1) || (m_pp.x == -2 && m_pp.y == -2 && m_pp.z == -2)) { - auto safePoint = zone->GetSafePoint(); - m_pp.x = safePoint.x; - m_pp.y = safePoint.y; - m_pp.z = safePoint.z; + auto zone_safe_point = zone->GetSafePoint(); + m_pp.x = zone_safe_point.x; + m_pp.y = zone_safe_point.y; + m_pp.z = zone_safe_point.z; + m_pp.heading = zone_safe_point.w; } /* If too far below ground, then fix */ // float ground_z = GetGroundZ(m_pp.x, m_pp.y, m_pp.z); @@ -6594,34 +6595,45 @@ void Client::Handle_OP_GMZoneRequest(const EQApplicationPacket *app) } GMZoneRequest_Struct* gmzr = (GMZoneRequest_Struct*)app->pBuffer; - float tarx = -1, tary = -1, tarz = -1; + float target_x = -1, target_y = -1, target_z = -1, target_heading; - int16 minstatus = 0; - uint8 minlevel = 0; - char tarzone[32]; - uint16 zid = gmzr->zone_id; + int16 min_status = 0; + uint8 min_level = 0; + char target_zone[32]; + uint16 zone_id = gmzr->zone_id; if (gmzr->zone_id == 0) - zid = zonesummon_id; - const char * zname = ZoneName(zid); - if (zname == nullptr) - tarzone[0] = 0; + zone_id = zonesummon_id; + + const char* zone_short_name = ZoneName(zone_id); + if (zone_short_name == nullptr) + target_zone[0] = 0; else - strcpy(tarzone, zname); + strcpy(target_zone, zone_short_name); // this both loads the safe points and does a sanity check on zone name - if (!content_db.GetSafePoints(tarzone, 0, &tarx, &tary, &tarz, &minstatus, &minlevel)) { - tarzone[0] = 0; + if (!content_db.GetSafePoints( + target_zone, + 0, + &target_x, + &target_y, + &target_z, + &target_heading, + &min_status, + &min_level + )) { + target_zone[0] = 0; } auto outapp = new EQApplicationPacket(OP_GMZoneRequest, sizeof(GMZoneRequest_Struct)); GMZoneRequest_Struct* gmzr2 = (GMZoneRequest_Struct*)outapp->pBuffer; strcpy(gmzr2->charname, this->GetName()); gmzr2->zone_id = gmzr->zone_id; - gmzr2->x = tarx; - gmzr2->y = tary; - gmzr2->z = tarz; + gmzr2->x = target_x; + gmzr2->y = target_y; + gmzr2->z = target_z; + gmzr2->heading = target_heading; // Next line stolen from ZoneChange as well... - This gives us a nicer message than the normal "zone is down" message... - if (tarzone[0] != 0 && admin >= minstatus && GetLevel() >= minlevel) + if (target_zone[0] != 0 && admin >= min_status && GetLevel() >= min_level) gmzr2->success = 1; else { std::cout << "GetZoneSafeCoords failed. zoneid = " << gmzr->zone_id << "; czone = " << zone->GetZoneID() << std::endl; @@ -12663,8 +12675,8 @@ void Client::Handle_OP_SetServerFilter(const EQApplicationPacket *app) 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 && m_pp.binds[4].zoneId != 189) { - Message(Chat::Yellow, "Your home city has already been set.", m_pp.binds[4].zoneId, ZoneName(m_pp.binds[4].zoneId)); + if (m_pp.binds[4].zone_id != 0 && m_pp.binds[4].zone_id != 189) { + Message(Chat::Yellow, "Your home city has already been set.", m_pp.binds[4].zone_id, ZoneName(m_pp.binds[4].zone_id)); return; } @@ -12674,13 +12686,22 @@ void Client::Handle_OP_SetStartCity(const EQApplicationPacket *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 %s", + float x = 0.0f, y = 0.0f, z = 0.0f, heading = 0.0f; + uint32 zone_id = 0; + uint32 start_city = (uint32)strtol((const char*)app->pBuffer, nullptr, 10); + std::string query = fmt::format( + SQL( + SELECT + `zone_id`, `bind_id`, `x`, `y`, `z`, `heading` + FROM + `start_zones` + WHERE + player_class = {} + AND + player_deity = {} + AND + player_race = {} {} + ), m_pp.class_, m_pp.deity, m_pp.race, @@ -12692,31 +12713,46 @@ void Client::Handle_OP_SetStartCity(const EQApplicationPacket *app) return; } - bool validCity = false; + bool valid_city = false; for (auto row = results.begin(); row != results.end(); ++row) { if (atoi(row[1]) != 0) - zoneid = atoi(row[1]); + zone_id = atoi(row[1]); else - zoneid = atoi(row[0]); + zone_id = atoi(row[0]); - if (zoneid != startCity) + if (zone_id != start_city) continue; - validCity = true; + valid_city = true; x = atof(row[2]); y = atof(row[3]); z = atof(row[4]); + heading = atof(row[5]); } - if (validCity) { + if (valid_city) { Message(Chat::Yellow, "Your home city has been set"); - SetStartZone(startCity, x, y, z); + SetStartZone(start_city, x, y, z, heading); 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); + query = fmt::format( + SQL( + SELECT + `zone_id`, `bind_id` + FROM + `start_zones` + WHERE + player_class = {} + AND + player_deity = {} + AND + player_race = {} + ), + m_pp.class_, + m_pp.deity, + m_pp.race + ); results = content_db.QueryDatabase(query); if (!results.Success()) return; @@ -12725,13 +12761,12 @@ void Client::Handle_OP_SetStartCity(const EQApplicationPacket *app) for (auto row = results.begin(); row != results.end(); ++row) { if (atoi(row[1]) != 0) - zoneid = atoi(row[1]); + zone_id = atoi(row[1]); else - zoneid = atoi(row[0]); + zone_id = atoi(row[0]); - char* name = nullptr; - content_db.GetZoneLongName(ZoneName(zoneid), &name); - Message(Chat::Yellow, "%d - %s", zoneid, name); + std::string zone_long_name = zone_store.GetZoneLongName(zone_id); + Message(Chat::Yellow, "%d - %s", zone_id, zone_long_name.c_str()); } } diff --git a/zone/client_process.cpp b/zone/client_process.cpp index bc8e2fd1e..384d3aab5 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -131,9 +131,9 @@ bool Client::Process() { CheckManaEndUpdate(); if (dead && dead_timer.Check()) { - database.MoveCharacterToZone(GetName(), m_pp.binds[0].zoneId); + database.MoveCharacterToZone(GetName(), m_pp.binds[0].zone_id); - m_pp.zone_id = m_pp.binds[0].zoneId; + m_pp.zone_id = m_pp.binds[0].zone_id; m_pp.zoneInstance = m_pp.binds[0].instance_id; m_pp.x = m_pp.binds[0].x; m_pp.y = m_pp.binds[0].y; @@ -1996,7 +1996,7 @@ void Client::HandleRespawnFromHover(uint32 Option) BindStruct* b = &m_pp.binds[0]; default_to_bind = new RespawnOption; default_to_bind->name = "Bind Location"; - default_to_bind->zone_id = b->zoneId; + default_to_bind->zone_id = b->zone_id; default_to_bind->instance_id = b->instance_id; default_to_bind->x = b->x; default_to_bind->y = b->y; diff --git a/zone/command.cpp b/zone/command.cpp index e49dea4d2..4991d25f6 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -5199,7 +5199,7 @@ void command_gmzone(Client *c, const Seperator *sep) } if (instance_id > 0) { - float target_x = -1, target_y = -1, target_z = -1; + float target_x = -1, target_y = -1, target_z = -1, target_heading = -1; int16 min_status = 0; uint8 min_level = 0; @@ -5209,6 +5209,7 @@ void command_gmzone(Client *c, const Seperator *sep) &target_x, &target_y, &target_z, + &target_heading, &min_status, &min_level )) { @@ -5218,7 +5219,7 @@ void command_gmzone(Client *c, const Seperator *sep) c->Message(Chat::Yellow, "Zoning to private GM instance (%s) (%u)", zone_short_name, instance_id); c->AssignToInstance(instance_id); - c->MovePC(zone_id, instance_id, target_x, target_y, target_z, 0, 1); + c->MovePC(zone_id, instance_id, target_x, target_y, target_z, target_heading, 1); } } diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index b662d5212..8a45470b7 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -1495,14 +1495,19 @@ XS(XS__ding) { XS(XS__rebind); XS(XS__rebind) { dXSARGS; - if (items != 4) - Perl_croak(aTHX_ "Usage: quest::rebind(int zone_id, float x, float y, float z)"); - - int zone_id = (int) SvIV(ST(0)); - auto location = glm::vec3((float) SvNV(ST(1)), (float) SvNV(ST(2)), (float) SvNV(ST(3))); - - quest_manager.rebind(zone_id, location); + if (items < 4 || items > 5) + Perl_croak(aTHX_ "Usage: quest::rebind(int zone_id, float x, float y, float z, [float heading])"); + int zone_id = (int) SvIV(ST(0)); + float target_x = (float) SvNV(ST(1)); + float target_y = (float) SvNV(ST(2)); + float target_z = (float) SvNV(ST(3)); + if (items > 4) { + float target_heading = (float) SvNV(ST(4)); + quest_manager.rebind(zone_id, glm::vec4(target_x, target_y, target_z, target_heading)); + } else { + quest_manager.rebind(zone_id, glm::vec3(target_x, target_y, target_z)); + } XSRETURN_EMPTY; } diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 9541176c8..1a71026f2 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -299,6 +299,11 @@ void Lua_Client::SetBindPoint(int to_zone, int to_instance, float new_x, float n self->SetBindPoint(0, to_zone, to_instance, glm::vec3(new_x, new_y, new_z)); } +void Lua_Client::SetBindPoint(int to_zone, int to_instance, float new_x, float new_y, float new_z, float new_heading) { + Lua_Safe_Call_Void(); + self->SetBindPoint2(0, to_zone, to_instance, glm::vec4(new_x, new_y, new_z, new_heading)); +} + float Lua_Client::GetBindX() { Lua_Safe_Call_Real(); return self->GetBindX(); @@ -2116,7 +2121,8 @@ luabind::scope lua_register_client() { .def("SetBindPoint", (void(Lua_Client::*)(int,int))&Lua_Client::SetBindPoint) .def("SetBindPoint", (void(Lua_Client::*)(int,int,float))&Lua_Client::SetBindPoint) .def("SetBindPoint", (void(Lua_Client::*)(int,int,float,float))&Lua_Client::SetBindPoint) - .def("SetBindPoint", (void(Lua_Client::*)(int,int,float,float, float))&Lua_Client::SetBindPoint) + .def("SetBindPoint", (void(Lua_Client::*)(int,int,float,float,float))&Lua_Client::SetBindPoint) + .def("SetBindPoint", (void(Lua_Client::*)(int,int,float,float,float,float))&Lua_Client::SetBindPoint) .def("GetBindX", (float(Lua_Client::*)(void))&Lua_Client::GetBindX) .def("GetBindX", (float(Lua_Client::*)(int))&Lua_Client::GetBindX) .def("GetBindY", (float(Lua_Client::*)(void))&Lua_Client::GetBindY) diff --git a/zone/lua_client.h b/zone/lua_client.h index 923231808..6d28656c1 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -86,6 +86,7 @@ public: void SetBindPoint(int to_zone, int to_instance, float new_x); void SetBindPoint(int to_zone, int to_instance, float new_x, float new_y); void SetBindPoint(int to_zone, int to_instance, float new_x, float new_y, float new_z); + void SetBindPoint(int to_zone, int to_instance, float new_x, float new_y, float new_z, float new_heading); float GetBindX(); float GetBindX(int index); float GetBindY(); @@ -249,6 +250,7 @@ public: void SetStartZone(int zone_id, float x); void SetStartZone(int zone_id, float x, float y); void SetStartZone(int zone_id, float x, float y, float z); + void SetStartZone(int zone_id, float x, float y, float z, float heading); void KeyRingAdd(uint32 item); bool KeyRingCheck(uint32 item); void AddPVPPoints(uint32 points); diff --git a/zone/npc.h b/zone/npc.h index de64e6b59..b23d15568 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -178,8 +178,8 @@ public: FACTION_VALUE CheckNPCFactionAlly(int32 other_faction); virtual FACTION_VALUE GetReverseFactionCon(Mob* iOther); - void GoToBind(uint8 bindnum = 0) { GMMove(m_SpawnPoint.x, m_SpawnPoint.y, m_SpawnPoint.z, m_SpawnPoint.w); } - void Gate(uint8 bindnum = 0); + void GoToBind(uint8 bind_number = 0) { GMMove(m_SpawnPoint.x, m_SpawnPoint.y, m_SpawnPoint.z, m_SpawnPoint.w); } + void Gate(uint8 bind_number = 0); void GetPetState(SpellBuff_Struct *buffs, uint32 *items, char *name); void SetPetState(SpellBuff_Struct *buffs, uint32 *items); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 567067f64..e3b5f4f64 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -753,47 +753,28 @@ XS(XS_Client_SetEXP) { XS(XS_Client_SetBindPoint); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_SetBindPoint) { dXSARGS; - if (items < 1 || items > 6) - Perl_croak(aTHX_ "Usage: Client::SetBindPoint(THIS, int to_zone = -1, int to_instance = 0, float new_x = 0.0f, float new_y = 0.0f, float new_z = 0.0f)"); // @categories Account and Character, Stats and Attributes + if (items < 1 || items > 7) + Perl_croak(aTHX_ "Usage: Client::SetBindPoint(THIS, [int to_zone = -1, int to_instance = 0, float new_x = 0.0f, float new_y = 0.0f, float new_z = 0.0f, float new_heading = 0.0f])"); // @categories Account and Character, Stats and Attributes { Client *THIS; - int to_zone; - int to_instance; - float new_x; - float new_y; - float new_z; + int to_zone = -1; + int to_instance = 0; + float new_x = 0.0f, new_y = 0.0f, new_z = 0.0f, new_heading = 0.0f; VALIDATE_THIS_IS_CLIENT; - if (items < 2) - to_zone = -1; - else { + if (items > 1) to_zone = (int) SvIV(ST(1)); - } - - if (items < 3) - to_instance = 0; - else { + if (items > 2) to_instance = (int) SvIV(ST(2)); - } - - if (items < 4) - new_x = 0.0f; - else { + if (items > 3) new_x = (float) SvNV(ST(3)); - } - - if (items < 5) - new_y = 0.0f; - else { + if (items > 4) new_y = (float) SvNV(ST(4)); - } - - if (items < 6) - new_z = 0.0f; - else { + if (items > 5) new_z = (float) SvNV(ST(5)); - } + if (items > 6) + new_heading = (float) SvNV(ST(6)); - THIS->SetBindPoint(0, to_zone, to_instance, glm::vec3(new_x, new_y, new_z)); + THIS->SetBindPoint2(0, to_zone, to_instance, glm::vec4(new_x, new_y, new_z, new_heading)); } XSRETURN_EMPTY; } @@ -3258,23 +3239,22 @@ XS(XS_Client_GetStartZone) { XS(XS_Client_SetStartZone); XS(XS_Client_SetStartZone) { dXSARGS; - if (items != 2 && items != 5) - Perl_croak(aTHX_ - "Usage: Client::SetStartZone(THIS, uint32 zone_id, [float x = 0], [float y = 0], [float z = 0])"); + if (items != 2 && items != 5 && items != 6) + Perl_croak(aTHX_ "Usage: Client::SetStartZone(THIS, uint32 zone_id, [float x = 0, float y = 0, float z = 0, [float heading = 0]])"); { Client *THIS; uint32 zoneid = (uint32) SvUV(ST(1)); - float x = 0; - float y = 0; - float z = 0; + float x = 0.0f, y = 0.0f, z = 0.0f, heading = 0.0f; VALIDATE_THIS_IS_CLIENT; if (items == 5) { - x = SvNV(ST(2)); - y = SvNV(ST(3)); - z = SvNV(ST(4)); + x = (float) SvNV(ST(2)); + y = (float) SvNV(ST(3)); + z = (float) SvNV(ST(4)); } + if (items == 6) + heading = (float) SvNV(ST(5)); - THIS->SetStartZone(zoneid, x, y, z); + THIS->SetStartZone(zoneid, x, y, z, heading); } XSRETURN_EMPTY; } @@ -5485,7 +5465,7 @@ XS(boot_Client) { newXSproto(strcpy(buf, "SetBaseRace"), XS_Client_SetBaseRace, file, "$$"); newXSproto(strcpy(buf, "SetBecomeNPC"), XS_Client_SetBecomeNPC, file, "$$"); newXSproto(strcpy(buf, "SetBecomeNPCLevel"), XS_Client_SetBecomeNPCLevel, file, "$$"); - newXSproto(strcpy(buf, "SetBindPoint"), XS_Client_SetBindPoint, file, "$;$$$$$"); + newXSproto(strcpy(buf, "SetBindPoint"), XS_Client_SetBindPoint, file, "$;$$$$$$"); newXSproto(strcpy(buf, "SetConsumption"), XS_Client_SetConsumption, file, "$$$"); newXSproto(strcpy(buf, "SetClientMaxLevel"), XS_Client_SetClientMaxLevel, file, "$$"); newXSproto(strcpy(buf, "SetCustomItemData"), XS_Client_SetCustomItemData, file, "$$$$"); @@ -5509,7 +5489,7 @@ XS(boot_Client) { newXSproto(strcpy(buf, "SetSecondaryWeaponOrnamentation"), XS_Client_SetSecondaryWeaponOrnamentation, file, "$$"); newXSproto(strcpy(buf, "SetSkill"), XS_Client_SetSkill, file, "$$$"); newXSproto(strcpy(buf, "SetSkillPoints"), XS_Client_SetSkillPoints, file, "$$"); - newXSproto(strcpy(buf, "SetStartZone"), XS_Client_SetStartZone, file, "$$"); + newXSproto(strcpy(buf, "SetStartZone"), XS_Client_SetStartZone, file, "$$;$$$$"); newXSproto(strcpy(buf, "SetStats"), XS_Client_SetStats, file, "$$$"); newXSproto(strcpy(buf, "SetThirst"), XS_Client_SetThirst, file, "$$"); newXSproto(strcpy(buf, "SetTint"), XS_Client_SetTint, file, "$$$"); diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index fc3e27239..c84633822 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1673,10 +1673,17 @@ void QuestManager::ding() { } -void QuestManager::rebind(int zoneid, const glm::vec3& location) { +void QuestManager::rebind(int zone_id, const glm::vec3& location) { QuestManagerCurrentQuestVars(); if(initiator && initiator->IsClient()) { - initiator->SetBindPoint(0, zoneid, 0, location); + initiator->SetBindPoint(0, zone_id, 0, location); + } +} + +void QuestManager::rebind(int zone_id, const glm::vec4& location) { + QuestManagerCurrentQuestVars(); + if(initiator && initiator->IsClient()) { + initiator->SetBindPoint2(0, zone_id, 0, location); } } @@ -4285,4 +4292,4 @@ std::string QuestManager::secondstotime(int duration) { time_string = fmt::format("{} {}", seconds, second_string); } return time_string; -} \ No newline at end of file +} diff --git a/zone/questmgr.h b/zone/questmgr.h index 0bb765fed..3c109f4f5 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -151,7 +151,8 @@ public: void targlobal(const char *varname, const char *value, const char *duration, int npcid, int charid, int zoneid); void delglobal(const char *varname); void ding(); - void rebind(int zoneid, const glm::vec3& location); + void rebind(int zone_id, const glm::vec3& location); + void rebind(int zone_id, const glm::vec4& location); void start(int wp); void stop(); void pause(int duration); diff --git a/zone/zone.cpp b/zone/zone.cpp index b69e9f2f3..2f974c35a 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -941,7 +941,7 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) spawn2_timer(1000), hot_reload_timer(1000), qglobal_purge_timer(30000), - m_SafePoint(0.0f,0.0f,0.0f), + m_SafePoint(0.0f,0.0f,0.0f,0.0f), m_Graveyard(0.0f,0.0f,0.0f,0.0f) { zoneid = in_zoneid; diff --git a/zone/zone.h b/zone/zone.h index 8b2e6b57c..c7c6daf12 100755 --- a/zone/zone.h +++ b/zone/zone.h @@ -167,7 +167,7 @@ public: inline const uint32 &graveyard_zoneid() { return pgraveyard_zoneid; } inline const uint32 GetInstanceID() const { return instanceid; } inline const uint32 GetZoneID() const { return zoneid; } - inline glm::vec3 GetSafePoint() { return m_SafePoint; } + inline glm::vec4 GetSafePoint() { return m_SafePoint; } inline glm::vec4 GetGraveyardPoint() { return m_Graveyard; } inline std::vector GetGlobalLootTables(NPC *mob) const { return m_global_loot.GetGlobalLootTables(mob); } inline Timer *GetInstanceTimer() { return Instance_Timer; } @@ -376,7 +376,7 @@ private: char *map_name; char *short_name; char file_name[16]; - glm::vec3 m_SafePoint; + glm::vec4 m_SafePoint; glm::vec4 m_Graveyard; int default_ruleset; int zone_total_blocked_spells; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 27f54862b..d68620c38 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1470,7 +1470,7 @@ bool ZoneDatabase::LoadCharacterBindPoint(uint32 character_id, PlayerProfile_Str if (index < 0 || index > 4) continue; - pp->binds[index].zoneId = atoi(row[1]); + pp->binds[index].zone_id = atoi(row[1]); pp->binds[index].instance_id = atoi(row[2]); pp->binds[index].x = atoi(row[3]); pp->binds[index].y = atoi(row[4]); @@ -1493,10 +1493,10 @@ bool ZoneDatabase::SaveCharacterBindPoint(uint32 character_id, const BindStruct std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot) VALUES (%u, " "%u, %u, %f, %f, %f, %f, %i)", - character_id, bind.zoneId, bind.instance_id, bind.x, bind.y, bind.z, bind.heading, bind_num); + character_id, bind.zone_id, bind.instance_id, bind.x, bind.y, bind.z, bind.heading, bind_num); LogDebug("ZoneDatabase::SaveCharacterBindPoint for character ID: [{}] zone_id: [{}] instance_id: [{}] position: [{}] [{}] [{}] [{}] bind_num: [{}]", - character_id, bind.zoneId, bind.instance_id, bind.x, bind.y, bind.z, bind.heading, bind_num); + character_id, bind.zone_id, bind.instance_id, bind.x, bind.y, bind.z, bind.heading, bind_num); auto results = QueryDatabase(query); if (!results.RowsAffected()) diff --git a/zone/zonedb.h b/zone/zonedb.h index d2fae4232..b77c157d3 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -319,7 +319,7 @@ public: bool SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level, uint32 charges); bool SaveCharacterBandolier(uint32 character_id, uint8 bandolier_id, uint8 bandolier_slot, uint32 item_id, uint32 icon, const char* bandolier_name); - bool SaveCharacterBindPoint(uint32 character_id, const BindStruct &bind, uint32 bind_num); + bool SaveCharacterBindPoint(uint32 character_id, const BindStruct &bind, uint32 bind_number); bool SaveCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); bool SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp); bool SaveCharacterDisc(uint32 character_id, uint32 slot_id, uint32 disc_id); diff --git a/zone/zoning.cpp b/zone/zoning.cpp index 675767754..854671260 100644 --- a/zone/zoning.cpp +++ b/zone/zoning.cpp @@ -78,11 +78,11 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { target_zone_id = zonesummon_id; break; case GateToBindPoint: - target_zone_id = m_pp.binds[0].zoneId; + target_zone_id = m_pp.binds[0].zone_id; target_instance_id = m_pp.binds[0].instance_id; break; case ZoneToBindPoint: - target_zone_id = m_pp.binds[0].zoneId; + target_zone_id = m_pp.binds[0].zone_id; target_instance_id = m_pp.binds[0].instance_id; break; case ZoneSolicited: //we told the client to zone somewhere, so we know where they are going. @@ -170,11 +170,21 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { } /* Load up the Safe Coordinates, restrictions and verify the zone name*/ - float safe_x, safe_y, safe_z; - int16 minstatus = 0; - uint8 minlevel = 0; + float safe_x, safe_y, safe_z, safe_heading; + int16 min_status = 0; + uint8 min_level = 0; char flag_needed[128]; - if(!content_db.GetSafePoints(target_zone_name, database.GetInstanceVersion(target_instance_id), &safe_x, &safe_y, &safe_z, &minstatus, &minlevel, flag_needed)) { + if(!content_db.GetSafePoints( + target_zone_name, + database.GetInstanceVersion(target_instance_id), + &safe_x, + &safe_y, + &safe_z, + &safe_heading, + &min_status, + &min_level, + flag_needed + )) { //invalid zone... Message(Chat::Red, "Invalid target zone while getting safe points."); LogError("Zoning [{}]: Unable to get safe coordinates for zone [{}]", GetName(), target_zone_name); @@ -189,41 +199,54 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { //handle circumvention of zone restrictions //we need the value when creating the outgoing packet as well. - uint8 ignorerestrictions = zonesummon_ignorerestrictions; + uint8 ignore_restrictions = zonesummon_ignorerestrictions; zonesummon_ignorerestrictions = 0; - float dest_x=0, dest_y=0, dest_z=0, dest_h; - dest_h = GetHeading(); + float target_x = 0, target_y = 0, target_z = 0, target_heading = 0; switch(zone_mode) { case EvacToSafeCoords: case ZoneToSafeCoords: - LogDebug("Zoning [{}] to safe coords ([{}],[{}],[{}]) in [{}] ([{}])", GetName(), safe_x, safe_y, safe_z, target_zone_name, target_zone_id); - dest_x = safe_x; - dest_y = safe_y; - dest_z = safe_z; + LogDebug( + "Zoning [{}] to safe coords ([{}], [{}], [{}], [{}]) in [{}] ([{}])", + GetName(), + safe_x, + safe_y, + safe_z, + safe_heading, + target_zone_name, + target_zone_id + ); + target_x = safe_x; + target_y = safe_y; + target_z = safe_z; + target_heading = safe_heading; break; case GMSummon: - dest_x = m_ZoneSummonLocation.x; - dest_y = m_ZoneSummonLocation.y; - dest_z = m_ZoneSummonLocation.z; - ignorerestrictions = 1; + target_x = m_ZoneSummonLocation.x; + target_y = m_ZoneSummonLocation.y; + target_z = m_ZoneSummonLocation.z; + target_heading = m_ZoneSummonLocation.w; + ignore_restrictions = 1; break; case GateToBindPoint: - dest_x = m_pp.binds[0].x; - dest_y = m_pp.binds[0].y; - dest_z = m_pp.binds[0].z; + target_x = m_pp.binds[0].x; + target_x = m_pp.binds[0].y; + target_x = m_pp.binds[0].z; + target_x = m_pp.binds[0].heading; break; case ZoneToBindPoint: - dest_x = m_pp.binds[0].x; - dest_y = m_pp.binds[0].y; - dest_z = m_pp.binds[0].z; - ignorerestrictions = 1; //can always get to our bind point? seems exploitable + target_x = m_pp.binds[0].x; + target_y = m_pp.binds[0].y; + target_z = m_pp.binds[0].z; + target_heading = m_pp.binds[0].heading; + ignore_restrictions = 1; //can always get to our bind point? seems exploitable break; case ZoneSolicited: //we told the client to zone somewhere, so we know where they are going. //recycle zonesummon variables - dest_x = m_ZoneSummonLocation.x; - dest_y = m_ZoneSummonLocation.y; - dest_z = m_ZoneSummonLocation.z; + target_x = m_ZoneSummonLocation.x; + target_y = m_ZoneSummonLocation.y; + target_z = m_ZoneSummonLocation.z; + target_heading = m_ZoneSummonLocation.w; break; case ZoneUnsolicited: //client came up with this on its own. //client requested a zoning... what are the cases when this could happen? @@ -234,21 +257,24 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { //999999 is a placeholder for 'same as where they were from' if(zone_point->target_x == 999999) - dest_x = GetX(); + target_x = GetX(); else - dest_x = zone_point->target_x; + target_x = zone_point->target_x; + if(zone_point->target_y == 999999) - dest_y = GetY(); + target_y = GetY(); else - dest_y = zone_point->target_y; + target_y = zone_point->target_y; + if(zone_point->target_z == 999999) - dest_z=GetZ(); + target_z = GetZ(); else - dest_z = zone_point->target_z; + target_z = zone_point->target_z; + if(zone_point->target_heading == 999) - dest_h = GetHeading(); + target_heading = GetHeading(); else - dest_h = zone_point->target_heading; + target_heading = zone_point->target_heading; break; } @@ -272,12 +298,12 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { //not sure when we would use ZONE_ERROR_NOTREADY //enforce min status and level - if (!ignorerestrictions && (Admin() < minstatus || GetLevel() < minlevel)) + if (!ignore_restrictions && (Admin() < min_status || GetLevel() < min_level)) { myerror = ZONE_ERROR_NOEXPERIENCE; } - if(!ignorerestrictions && flag_needed[0] != '\0') { + if(!ignore_restrictions && flag_needed[0] != '\0') { //the flag needed string is not empty, meaning a flag is required. if(Admin() < minStatusToIgnoreZoneFlags && !HasZoneFlag(target_zone_id)) { @@ -343,7 +369,7 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { if(myerror == 1) { //we have successfully zoned - DoZoneSuccess(zc, target_zone_id, target_instance_id, dest_x, dest_y, dest_z, dest_h, ignorerestrictions); + DoZoneSuccess(zc, target_zone_id, target_instance_id, target_x, target_y, target_z, target_heading, ignore_restrictions); } else { LogError("Zoning [{}]: Rules prevent this char from zoning into [{}]", GetName(), target_zone_name); SendZoneError(zc, myerror); @@ -463,7 +489,7 @@ void Client::DoZoneSuccess(ZoneChange_Struct *zc, uint16 zone_id, uint32 instanc //reset to unsolicited. zone_mode = ZoneUnsolicited; - m_ZoneSummonLocation = glm::vec3(); + m_ZoneSummonLocation = glm::vec4(); zonesummon_id = 0; zonesummon_ignorerestrictions = 0; } @@ -647,29 +673,24 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z return; } iZoneNameLength = strlen(pZoneName); - glm::vec3 safePoint; - + glm::vec4 zone_safe_point; switch(zm) { case EvacToSafeCoords: case ZoneToSafeCoords: - safePoint = zone->GetSafePoint(); - x = safePoint.x; - y = safePoint.y; - z = safePoint.z; - SetHeading(heading); + zone_safe_point = zone->GetSafePoint(); + x = zone_safe_point.x; + y = zone_safe_point.y; + z = zone_safe_point.z; + heading = zone_safe_point.w; break; case GMSummon: m_Position = glm::vec4(x, y, z, heading); - m_ZoneSummonLocation = glm::vec3(m_Position); - SetHeading(heading); - + m_ZoneSummonLocation = m_Position; zonesummon_id = zoneID; zonesummon_ignorerestrictions = 1; break; case ZoneSolicited: - m_ZoneSummonLocation = glm::vec3(x,y,z); - SetHeading(heading); - + m_ZoneSummonLocation = glm::vec4(x, y, z, heading); zonesummon_id = zoneID; zonesummon_ignorerestrictions = ignorerestrictions; break; @@ -684,23 +705,20 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z y = m_Position.y = m_pp.binds[0].y; z = m_Position.z = m_pp.binds[0].z; heading = m_pp.binds[0].heading; - zonesummon_ignorerestrictions = 1; LogDebug("Player [{}] has died and will be zoned to bind point in zone: [{}] at LOC x=[{}], y=[{}], z=[{}], heading=[{}]", GetName(), pZoneName, m_pp.binds[0].x, m_pp.binds[0].y, m_pp.binds[0].z, m_pp.binds[0].heading); break; case SummonPC: - m_ZoneSummonLocation = glm::vec3(x, y, z); - m_Position = glm::vec4(m_ZoneSummonLocation, 0.0f); - SetHeading(heading); + m_ZoneSummonLocation = glm::vec4(x, y, z, heading); + m_Position = m_ZoneSummonLocation; break; case Rewind: LogDebug("[{}] has requested a /rewind from [{}], [{}], [{}], to [{}], [{}], [{}] in [{}]", GetName(), m_Position.x, m_Position.y, m_Position.z, m_RewindLocation.x, m_RewindLocation.y, m_RewindLocation.z, zone->GetShortName()); - m_ZoneSummonLocation = glm::vec3(x, y, z); - m_Position = glm::vec4(m_ZoneSummonLocation, 0.0f); - SetHeading(heading); + m_ZoneSummonLocation = glm::vec4(x, y, z, heading); + m_Position = m_ZoneSummonLocation; break; default: LogError("Client::ZonePC() received a reguest to perform an unsupported client zone operation"); @@ -847,7 +865,7 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z { if(zm != EvacToSafeCoords && zm != ZoneToSafeCoords && zm != ZoneToBindPoint) { - m_ZoneSummonLocation = glm::vec3(); + m_ZoneSummonLocation = glm::vec4(); zonesummon_id = 0; zonesummon_ignorerestrictions = 0; zone_mode = ZoneUnsolicited; @@ -866,61 +884,100 @@ void Client::GoToSafeCoords(uint16 zone_id, uint16 instance_id) { } -void Mob::Gate(uint8 bindnum) { - GoToBind(bindnum); +void Mob::Gate(uint8 bind_number) { + GoToBind(bind_number); if (RuleB(NPC, NPCHealOnGate) && this->IsNPC() && this->GetHPRatio() <= RuleR(NPC, NPCHealOnGateAmount)) { auto HealAmount = (RuleR(NPC, NPCHealOnGateAmount) / 100); SetHP(int(this->GetMaxHP() * HealAmount)); } } -void Client::Gate(uint8 bindnum) { - Mob::Gate(bindnum); +void Client::Gate(uint8 bind_number) { + Mob::Gate(bind_number); } -void NPC::Gate(uint8 bindnum) { +void NPC::Gate(uint8 bind_number) { entity_list.MessageCloseString(this, true, RuleI(Range, SpellMessages), Chat::Spells, GATES, GetCleanName()); - Mob::Gate(bindnum); + Mob::Gate(bind_number); } -void Client::SetBindPoint(int bind_num, int to_zone, int to_instance, const glm::vec3 &location) +void Client::SetBindPoint(int bind_number, int to_zone, int to_instance, const glm::vec3 &location) { - if (bind_num < 0 || bind_num >= 4) - bind_num = 0; + if (bind_number < 0 || bind_number >= 4) + bind_number = 0; if (to_zone == -1) { - m_pp.binds[bind_num].zoneId = zone->GetZoneID(); - m_pp.binds[bind_num].instance_id = - (zone->GetInstanceID() != 0 && zone->IsInstancePersistent()) ? zone->GetInstanceID() : 0; - m_pp.binds[bind_num].x = m_Position.x; - m_pp.binds[bind_num].y = m_Position.y; - m_pp.binds[bind_num].z = m_Position.z; + m_pp.binds[bind_number].zone_id = zone->GetZoneID(); + m_pp.binds[bind_number].instance_id = (zone->GetInstanceID() != 0 && zone->IsInstancePersistent()) ? zone->GetInstanceID() : 0; + m_pp.binds[bind_number].x = m_Position.x; + m_pp.binds[bind_number].y = m_Position.y; + m_pp.binds[bind_number].z = m_Position.z; } else { - m_pp.binds[bind_num].zoneId = to_zone; - m_pp.binds[bind_num].instance_id = to_instance; - m_pp.binds[bind_num].x = location.x; - m_pp.binds[bind_num].y = location.y; - m_pp.binds[bind_num].z = location.z; + m_pp.binds[bind_number].zone_id = to_zone; + m_pp.binds[bind_number].instance_id = to_instance; + m_pp.binds[bind_number].x = location.x; + m_pp.binds[bind_number].y = location.y; + m_pp.binds[bind_number].z = location.z; } - database.SaveCharacterBindPoint(this->CharacterID(), m_pp.binds[bind_num], bind_num); + database.SaveCharacterBindPoint(this->CharacterID(), m_pp.binds[bind_number], bind_number); } -void Client::GoToBind(uint8 bindnum) { +void Client::SetBindPoint2(int bind_number, int to_zone, int to_instance, const glm::vec4 &location) +{ + if (bind_number < 0 || bind_number >= 4) + bind_number = 0; + + if (to_zone == -1) { + m_pp.binds[bind_number].zone_id = zone->GetZoneID(); + m_pp.binds[bind_number].instance_id = (zone->GetInstanceID() != 0 && zone->IsInstancePersistent()) ? zone->GetInstanceID() : 0; + m_pp.binds[bind_number].x = m_Position.x; + m_pp.binds[bind_number].y = m_Position.y; + m_pp.binds[bind_number].z = m_Position.z; + m_pp.binds[bind_number].heading = m_Position.w; + } else { + m_pp.binds[bind_number].zone_id = to_zone; + m_pp.binds[bind_number].instance_id = to_instance; + m_pp.binds[bind_number].x = location.x; + m_pp.binds[bind_number].y = location.y; + m_pp.binds[bind_number].z = location.z; + m_pp.binds[bind_number].heading = location.w; + } + database.SaveCharacterBindPoint(this->CharacterID(), m_pp.binds[bind_number], bind_number); +} + +void Client::GoToBind(uint8 bind_number) { // if the bind number is invalid, use the primary bind - if(bindnum > 4) - bindnum = 0; + if(bind_number > 4) + bind_number = 0; // move the client, which will zone them if needed. // ignore restrictions on the zone request..? - if(bindnum == 0) - MovePC(m_pp.binds[0].zoneId, m_pp.binds[0].instance_id, 0.0f, 0.0f, 0.0f, 0.0f, 1, GateToBindPoint); + if(bind_number == 0) + MovePC( + m_pp.binds[0].zone_id, + m_pp.binds[0].instance_id, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 1, + GateToBindPoint + ); else - MovePC(m_pp.binds[bindnum].zoneId, m_pp.binds[bindnum].instance_id, m_pp.binds[bindnum].x, m_pp.binds[bindnum].y, m_pp.binds[bindnum].z, m_pp.binds[bindnum].heading, 1); + MovePC( + m_pp.binds[bind_number].zone_id, + m_pp.binds[bind_number].instance_id, + m_pp.binds[bind_number].x, + m_pp.binds[bind_number].y, + m_pp.binds[bind_number].z, + m_pp.binds[bind_number].heading, + 1 + ); } void Client::GoToDeath() { - MovePC(m_pp.binds[0].zoneId, m_pp.binds[0].instance_id, 0.0f, 0.0f, 0.0f, 0.0f, 1, ZoneToBindPoint); + MovePC(m_pp.binds[0].zone_id, m_pp.binds[0].instance_id, 0.0f, 0.0f, 0.0f, 0.0f, 1, ZoneToBindPoint); } void Client::SetZoneFlag(uint32 zone_id) { @@ -982,26 +1039,28 @@ void Client::SendZoneFlagInfo(Client *to) const { to->Message(Chat::White, "Flags for %s:", GetName()); for(; cur != end; ++cur) { - uint32 zoneid = *cur; - - const char *short_name = ZoneName(zoneid); - - char *long_name = nullptr; - content_db.GetZoneLongName(short_name, &long_name); - if(long_name == nullptr) - long_name = empty; - - float safe_x, safe_y, safe_z; - int16 minstatus = 0; - uint8 minlevel = 0; + uint32 zone_id = *cur; + const char* zone_short_name = ZoneName(zone_id); + std::string zone_long_name = zone_store.GetZoneLongName(zone_id); + float safe_x, safe_y, safe_z, safe_heading; + int16 min_status = 0; + uint8 min_level = 0; char flag_name[128]; - if(!content_db.GetSafePoints(short_name, 0, &safe_x, &safe_y, &safe_z, &minstatus, &minlevel, flag_name)) { + if(!content_db.GetSafePoints( + zone_short_name, + 0, + &safe_x, + &safe_y, + &safe_z, + &safe_heading, + &min_status, + &min_level, + flag_name + )) { strcpy(flag_name, "(ERROR GETTING NAME)"); } - to->Message(Chat::White, "Has Flag %s for zone %s (%d,%s)", flag_name, long_name, zoneid, short_name); - if(long_name != empty) - delete[] long_name; + to->Message(Chat::White, "Has Flag %s for zone %s (%d,%s)", flag_name, zone_long_name.c_str(), zone_id, zone_short_name); } } @@ -1013,22 +1072,32 @@ bool Client::CanBeInZone() { if(Admin() >= RuleI(GM, MinStatusToZoneAnywhere)) return(true); - float safe_x, safe_y, safe_z; - int16 minstatus = 0; - uint8 minlevel = 0; + float safe_x, safe_y, safe_z, safe_heading; + int16 min_status = 0; + uint8 min_level = 0; char flag_needed[128]; - if(!content_db.GetSafePoints(zone->GetShortName(), zone->GetInstanceVersion(), &safe_x, &safe_y, &safe_z, &minstatus, &minlevel, flag_needed)) { + if(!content_db.GetSafePoints( + zone->GetShortName(), + zone->GetInstanceVersion(), + &safe_x, + &safe_y, + &safe_z, + &safe_heading, + &min_status, + &min_level, + flag_needed + )) { //this should not happen... LogDebug("[CLIENT] Unable to query zone info for ourself [{}]", zone->GetShortName()); return(false); } - if(GetLevel() < minlevel) { - LogDebug("[CLIENT] Character does not meet min level requirement ([{}] < [{}])!", GetLevel(), minlevel); + if(GetLevel() < min_level) { + LogDebug("[CLIENT] Character does not meet min level requirement ([{}] < [{}])!", GetLevel(), min_level); return(false); } - if(Admin() < minstatus) { - LogDebug("[CLIENT] Character does not meet min status requirement ([{}] < [{}])!", Admin(), minstatus); + if(Admin() < min_status) { + LogDebug("[CLIENT] Character does not meet min status requirement ([{}] < [{}])!", Admin(), min_status); return(false); } From dba3010c898d4e590f63eb7018af08efc044303c Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 23 Apr 2021 00:36:39 -0400 Subject: [PATCH 023/624] [Strings] Split String Optimizations (#1325) * Switch the 2 split calls to SplitString * Nuke duplicate split in favor of SplitString #1263 * Add a test for SplitString * Optimize SplitString Benchmarking: -------------------------------------------------------------- Benchmark Time CPU Iterations -------------------------------------------------------------- bench_oldsplit 5201 ns 5201 ns 129500 bench_split 1269 ns 1269 ns 548906 This is splitting a VERY long SpecialAbilities string. This is ~75% speed up. --- common/string_util.cpp | 31 +++++++++++-------------------- common/string_util.h | 3 +-- tests/string_util_test.h | 10 ++++++++++ zone/mob_info.cpp | 2 +- zone/zone_event_scheduler.cpp | 2 +- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/common/string_util.cpp b/common/string_util.cpp index f267397c5..6c97dd076 100644 --- a/common/string_util.cpp +++ b/common/string_util.cpp @@ -74,18 +74,6 @@ const std::string str_tolower(std::string s) return s; } -std::vector split(std::string str_to_split, char delimiter) -{ - std::stringstream ss(str_to_split); - std::string item; - std::vector exploded_values; - while (std::getline(ss, item, delimiter)) { - exploded_values.push_back(item); - } - - return exploded_values; -} - const std::string str_toupper(std::string s) { std::transform( @@ -113,15 +101,18 @@ const std::string StringFormat(const char *format, ...) return output; } -std::vector SplitString(const std::string &str, char delim) { +std::vector SplitString(const std::string &str, const char delim) { std::vector ret; - std::stringstream ss(str); - std::string item; - - while(std::getline(ss, item, delim)) { - ret.push_back(item); - } - + std::string::size_type start = 0; + auto end = str.find(delim); + while (end != std::string::npos) { + ret.emplace_back(str, start, end - start); + start = end + 1; + end = str.find(delim, start); + } + // this will catch the last word since the string is unlikely to end with a delimiter + if (str.length() > start) + ret.emplace_back(str, start, str.length() - start); return ret; } diff --git a/common/string_util.h b/common/string_util.h index 31d566bde..5fd91281c 100644 --- a/common/string_util.h +++ b/common/string_util.h @@ -39,7 +39,6 @@ const std::string str_tolower(std::string s); const std::string str_toupper(std::string s); const std::string ucfirst(std::string s); -std::vector split(std::string str_to_split, char delimiter); const std::string StringFormat(const char* format, ...); const std::string vStringFormat(const char* format, va_list args); std::vector wrap(std::vector &src, std::string character); @@ -177,7 +176,7 @@ std::vector join_tuple(const std::string &glue, const std::pair SplitString(const std::string &s, char delim); +std::vector SplitString(const std::string &s, const char delim = ','); std::string::size_type search_deliminated_string(const std::string &haystack, const std::string &needle, const char deliminator = ','); std::string EscapeString(const char *src, size_t sz); std::string EscapeString(const std::string &s); diff --git a/tests/string_util_test.h b/tests/string_util_test.h index 0333d0391..1fdd325f9 100644 --- a/tests/string_util_test.h +++ b/tests/string_util_test.h @@ -30,6 +30,7 @@ public: TEST_ADD(StringUtilTest::EscapeStringTest); TEST_ADD(StringUtilTest::EscapeStringMemoryTest); TEST_ADD(StringUtilTest::SearchDeliminatedStringTest); + TEST_ADD(StringUtilTest::SplitStringTest); } ~StringUtilTest() { @@ -108,6 +109,15 @@ public: TEST_ASSERT(search_deliminated_string(h, "bef") == std::string::npos); TEST_ASSERT(search_deliminated_string(h, "wwi") == std::string::npos); } + + void SplitStringTest() { + std::string s = "123,456,789,"; + auto v = SplitString(s, ','); + TEST_ASSERT(v.size() == 3); + TEST_ASSERT(v[0] == "123"); + TEST_ASSERT(v[1] == "456"); + TEST_ASSERT(v[2] == "789"); + } }; #endif diff --git a/zone/mob_info.cpp b/zone/mob_info.cpp index 554d5d571..5b7011f33 100644 --- a/zone/mob_info.cpp +++ b/zone/mob_info.cpp @@ -557,7 +557,7 @@ inline std::string WriteDisplayInfoSection( * "total_to_hit" = "Total To Hit" */ if (attribute_name.find('_') != std::string::npos) { - std::vector split_string = split(attribute_name, '_'); + auto split_string = SplitString(attribute_name, '_'); std::string new_attribute_name; for (std::string &string_value : split_string) { new_attribute_name += ucfirst(string_value) + " "; diff --git a/zone/zone_event_scheduler.cpp b/zone/zone_event_scheduler.cpp index 657fed78b..4bdb2cd6f 100644 --- a/zone/zone_event_scheduler.cpp +++ b/zone/zone_event_scheduler.cpp @@ -93,7 +93,7 @@ void ZoneEventScheduler::Process(Zone *zone, WorldContentService *content_servic } if (e.event_type == ServerEvents::EVENT_TYPE_RULE_CHANGE) { - auto params = split(e.event_data, '='); + auto params = SplitString(e.event_data, '='); auto rule_key = params[0]; auto rule_value = params[1]; if (!rule_key.empty() && !rule_value.empty()) { From c18562b150bd124b75a6216a3345fc5fceaeb5d0 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Fri, 23 Apr 2021 00:40:17 -0400 Subject: [PATCH 024/624] [Bug] Fix ignore_primary_assist. (#1323) * [Bug] Fix ignore_primary_assist. * Fix to continue loop instead of return Co-authored-by: Noudess --- zone/npc.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/zone/npc.cpp b/zone/npc.cpp index 87d7b57fc..2a90bf360 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -3294,17 +3294,16 @@ void NPC::AIYellForHelp(Mob *sender, Mob *attacker) * then jump in if they are our friend */ if (mob->GetLevel() >= 50 || attacker->GetLevelCon(mob->GetLevel()) != CON_GRAY) { - bool use_primary_faction = false; if (mob->GetPrimaryFaction() == sender->CastToNPC()->GetPrimaryFaction()) { const NPCFactionList *cf = content_db.GetNPCFactionEntry(mob->CastToNPC()->GetNPCFactionID()); if (cf) { - if (cf->assistprimaryfaction != 0) { - use_primary_faction = true; + if (cf->assistprimaryfaction == 0) { + continue; //Same faction and ignore primary assist } } } - if (use_primary_faction || sender->GetReverseFactionCon(mob) <= FACTION_AMIABLE) { + if (sender->GetReverseFactionCon(mob) <= FACTION_AMIABLE) { //attacking someone on same faction, or a friend //Father Nitwit: make sure we can see them. if (mob->CheckLosFN(sender)) { From 13a50f78068a1cffc481232d6757a0bf00359470 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 23 Apr 2021 00:52:00 -0400 Subject: [PATCH 025/624] [Quest API] Add HTML color tag method to Perl and Lua. (#1324) * Add HTML color tag method to Perl and Lua. - Add quest::gethexcolorcode(color_name) to Perl. - Add eq.get_hex_color_code(color_name) to Lua. Full color list here: https://pastebin.com/rUYKr1ye * Convert to static and use strcasecmp over strcmp. Co-authored-by: Chris Miles --- zone/embparser_api.cpp | 18 +++ zone/lua_general.cpp | 5 + zone/questmgr.cpp | 323 +++++++++++++++++++++++++++++++++++++++++ zone/questmgr.h | 1 + 4 files changed, 347 insertions(+) diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 8a45470b7..4ab576cd2 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -6477,6 +6477,23 @@ XS(XS__secondstotime) { XSRETURN(1); } +XS(XS__gethexcolorcode); +XS(XS__gethexcolorcode) { + dXSARGS; + if (items != 1) { + Perl_croak(aTHX_ "Usage: quest::gethexcolorcode(std::string color_name)"); + } + + dXSTARG; + std::string hex_color_code; + std::string color_name = SvPV_nolen(ST(0)); + hex_color_code = quest_manager.gethexcolorcode(color_name); + sv_setpv(TARG, hex_color_code.c_str()); + XSprePUSH; + PUSHTARG; + XSRETURN(1); +} + /* This is the callback perl will look for to setup the quest package's XSUBs @@ -6685,6 +6702,7 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "forcedooropen"), XS__forcedooropen, file); newXS(strcpy(buf, "getcharidbyname"), XS__getcharidbyname, file); newXS(strcpy(buf, "getclassname"), XS__getclassname, file); + newXS(strcpy(buf, "gethexcolorcode"), XS__gethexcolorcode, file); newXS(strcpy(buf, "getcurrencyid"), XS__getcurrencyid, file); newXS(strcpy(buf, "get_expedition"), XS__get_expedition, file); newXS(strcpy(buf, "get_expedition_by_char_id"), XS__get_expedition_by_char_id, file); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 668028eb8..a1b0a1a26 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -2322,6 +2322,10 @@ std::string lua_seconds_to_time(int duration) { return quest_manager.secondstotime(duration); } +std::string lua_get_hex_color_code(std::string color_name) { + return quest_manager.gethexcolorcode(color_name); +} + #define LuaCreateNPCParse(name, c_type, default_value) do { \ cur = table[#name]; \ if(luabind::type(cur) != LUA_TNIL) { \ @@ -2863,6 +2867,7 @@ luabind::scope lua_register_general() { luabind::def("debug", (void(*)(std::string, int))&lua_debug), luabind::def("log_combat", (void(*)(std::string))&lua_log_combat), luabind::def("seconds_to_time", &lua_seconds_to_time), + luabind::def("get_hex_color_code", &lua_get_hex_color_code), /** * Expansions diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index c84633822..3872e2a35 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -4293,3 +4293,326 @@ std::string QuestManager::secondstotime(int duration) { } return time_string; } + +std::string QuestManager::gethexcolorcode(std::string color_name) { + static const std::map colors = { + { "Black", "#000000" }, + { "Brown", "#804000" }, + { "Burgundy", "#800000" }, + { "Cadet Blue", "#77BFC7" }, + { "Cadet Blue1", "#4C787E" }, + { "Chartreuse", "#8AFB17" }, + { "Chartreuse1", "#7FE817" }, + { "Chartreuse2", "#6CC417" }, + { "Chartreuse3", "#437C17" }, + { "Chocolate", "#C85A17" }, + { "Coral", "#F76541" }, + { "Coral1", "#E55B3C" }, + { "Coral2", "#C34A2C" }, + { "Cornflower Blue", "#151B8D" }, + { "Cyan", "#00FFFF" }, + { "Cyan1", "#57FEFF" }, + { "Cyan2", "#50EBEC" }, + { "Cyan3", "#46C7C7" }, + { "Cyan4", "#307D7E" }, + { "Dark Blue", "#0000A0" }, + { "Dark Goldenrod", "#AF7817" }, + { "Dark Goldenrod1", "#FBB117" }, + { "Dark Goldenrod2", "#E8A317" }, + { "Dark Goldenrod3", "#C58917" }, + { "Dark Goldenrod4", "#7F5217" }, + { "Dark Green", "#254117" }, + { "Dark Grey", "#808080" }, + { "Dark Olive Green", "#CCFB5D" }, + { "Dark Olive Green2", "#BCE954" }, + { "Dark Olive Green3", "#A0C544" }, + { "Dark Olive Green4", "#667C26" }, + { "Dark Orange", "#F88017" }, + { "Dark Orange1", "#F87217" }, + { "Dark Orange2", "#E56717" }, + { "Dark Orange3", "#7E3117" }, + { "Dark Orange3", "#C35617" }, + { "Dark Orchid", "#7D1B7E" }, + { "Dark Orchid1", "#B041FF" }, + { "Dark Orchid2", "#A23BEC" }, + { "Dark Orchid3", "#8B31C7" }, + { "Dark Orchid4", "#571B7e" }, + { "Dark Purple", "#800080" }, + { "Dark Salmon", "#E18B6B" }, + { "Dark Sea Green", "#8BB381" }, + { "Dark Sea Green1", "#C3FDB8" }, + { "Dark Sea Green2", "#B5EAAA" }, + { "Dark Sea Green3", "#99C68E" }, + { "Dark Sea Green4", "#617C58" }, + { "Dark Slate Blue", "#2B3856" }, + { "Dark Slate Gray", "#25383C" }, + { "Dark Slate Gray1", "#9AFEFF" }, + { "Dark Slate Gray2", "#8EEBEC" }, + { "Dark Slate Gray3", "#78c7c7" }, + { "Dark Slate Gray4", "#4C7D7E" }, + { "Dark Turquoise", "#3B9C9C" }, + { "Dark Violet", "#842DCE" }, + { "Deep Pink", "#F52887" }, + { "Deep Pink1", "#E4287C" }, + { "Deep Pink2", "#C12267" }, + { "Deep Pink3", "#7D053F" }, + { "Deep Sky Blue", "#3BB9FF" }, + { "Deep Sky Blue1", "#38ACEC" }, + { "Deep Sky Blue2", "#3090C7" }, + { "Deep Sky Blue3", "#25587E" }, + { "Dim Gray", "#463E41" }, + { "Dodger Blue", "#1589FF" }, + { "Dodger Blue1", "#157DEC" }, + { "Dodger Blue2", "#1569C7" }, + { "Dodger Blue3", "#153E7E" }, + { "Firebrick", "#800517" }, + { "Firebrick1", "#F62817" }, + { "Firebrick2", "#E42217" }, + { "Firebrick3", "#C11B17" }, + { "Forest Green", "#4E9258" }, + { "Forest Green1", "#808000" }, + { "Gold", "#D4A017" }, + { "Gold1", "#FDD017" }, + { "Gold2", "#EAC117" }, + { "Gold3", "#C7A317" }, + { "Gold4", "#806517" }, + { "Goldenrod", "#EDDA74" }, + { "Goldenrod1", "#FBB917" }, + { "Goldenrod2", "#E9AB17" }, + { "Goldenrod3", "#C68E17" }, + { "Goldenrod4", "#805817" }, + { "Grass Green", "#408080" }, + { "Gray", "#736F6E" }, + { "Gray1", "#150517" }, + { "Gray2", "#250517" }, + { "Gray3", "#2B1B17" }, + { "Gray4", "#302217" }, + { "Gray5", "#302226" }, + { "Gray6", "#342826" }, + { "Gray7", "#34282C" }, + { "Gray8", "#382D2C" }, + { "Gray9", "#3b3131" }, + { "Gray10", "#3E3535" }, + { "Gray11", "#413839" }, + { "Gray12", "#41383C" }, + { "Gray13", "#463E3F" }, + { "Gray14", "#4A4344" }, + { "Gray15", "#4C4646" }, + { "Gray16", "#4E4848" }, + { "Gray17", "#504A4B" }, + { "Gray18", "#544E4F" }, + { "Gray19", "#565051" }, + { "Gray19", "#595454" }, + { "Gray20", "#5C5858" }, + { "Gray21", "#5F5A59" }, + { "Gray22", "#625D5D" }, + { "Gray23", "#646060" }, + { "Gray24", "#666362" }, + { "Gray25", "#696565" }, + { "Gray26", "#6D6968" }, + { "Gray27", "#6E6A6B" }, + { "Gray28", "#726E6D" }, + { "Gray29", "#747170" }, + { "Green", "#00FF00" }, + { "Green1", "#5FFB17" }, + { "Green2", "#59E817" }, + { "Green3", "#4CC417" }, + { "Green4", "#347C17" }, + { "Green Yellow", "#B1FB17" }, + { "Hot Pink", "#F660AB" }, + { "Hot Pink1", "#F665AB" }, + { "Hot Pink2", "#E45E9D" }, + { "Hot Pink3", "#C25283" }, + { "Hot Pink4", "#7D2252" }, + { "Indian Red", "#F75D59" }, + { "Indian Red2", "#E55451" }, + { "Indian Red3", "#C24641" }, + { "Indian Red4", "#7E2217" }, + { "Khaki", "#ADA96E" }, + { "Khaki1", "#FFF380" }, + { "Khaki2", "#EDE275" }, + { "Khaki3", "#C9BE62" }, + { "Khaki4", "#827839" }, + { "Lavender", "#E3E4FA" }, + { "Lavender Blush", "#FDEEF4" }, + { "Lavender Blush1", "#EBDDE2" }, + { "Lavender Blush2", "#C8BBBE" }, + { "Lavender Blush3", "#817679" }, + { "Lawn Green", "#87F717" }, + { "Lemon Chiffon", "#FFF8C6" }, + { "Lemon Chiffon1", "#ECE5B6" }, + { "Lemon Chiffon2", "#C9C299" }, + { "Lemon Chiffon3", "#827B60" }, + { "Light Blue", "#0000FF" }, + { "Light Blue1", "#ADDFFF" }, + { "Light Blue2", "#BDEDFF" }, + { "Light Blue3", "#AFDCEC" }, + { "Light Blue4", "#95B9C7" }, + { "Light Blue5", "#5E767E" }, + { "Light Coral", "#E77471" }, + { "Light Cyan", "#E0FFFF" }, + { "Light Cyan1", "#CFECEC" }, + { "Light Cyan2", "#AFC7C7" }, + { "Light Cyan3", "#717D7D" }, + { "Light Golden", "#ECD672" }, + { "Light Goldenrod", "#ECD872" }, + { "Light Goldenrod1", "#FFE87C" }, + { "Light Goldenrod2", "#C8B560" }, + { "Light Goldenrod3", "#817339" }, + { "Light Goldenrod Yellow", "#FAF8CC" }, + { "Light Grey", "#C0C0C0" }, + { "Light Pink", "#FAAFBA" }, + { "Light Pink1", "#F9A7B0" }, + { "Light Pink2", "#E799A3" }, + { "Light Pink3", "#C48189" }, + { "Light Pink4", "#7F4E52" }, + { "Light Purple", "#FF0080" }, + { "Light Salmon", "#F9966B" }, + { "Light Salmon1", "#E78A61" }, + { "Light Salmon2", "#C47451" }, + { "Light Salmon3", "#7F462C" }, + { "Light Sea Green", "#3EA99F" }, + { "Light Sky Blue", "#82CAFA" }, + { "Light Sky Blue1", "#A0CFEC" }, + { "Light Sky Blue2", "#87AFC7" }, + { "Light Sky Blue3", "#566D7E" }, + { "Light Slate Blue", "#736AFF" }, + { "Light Slate Gray", "#6D7B8D" }, + { "Light Steel Blue", "#728FCE" }, + { "Light Steel Blue1", "#C6DEFF" }, + { "Light Steel Blue2", "#B7CEEC" }, + { "Light Steel Blue3", "#646D7E" }, + { "Lime Green", "#41A317" }, + { "Magenta", "#FF00FF" }, + { "Magenta1", "#F433FF" }, + { "Magenta2", "#E238EC" }, + { "Magenta3", "#C031C7" }, + { "Maroon", "#810541" }, + { "Maroon1", "#F535AA" }, + { "Maroon2", "#E3319D" }, + { "Maroon3", "#C12283" }, + { "Maroon4", "#7D0552" }, + { "Medium Aquamarine", "#348781" }, + { "Medium Forest Green", "#347235" }, + { "Medium Orchid", "#B048B5" }, + { "Medium Orchid1", "#D462FF" }, + { "Medium Orchid2", "#C45AEC" }, + { "Medium Orchid3", "#A74AC7" }, + { "Medium Orchid4", "#6A287E" }, + { "Medium Purple", "#8467D7" }, + { "Medium Purple1", "#9E7BFF" }, + { "Medium Purple2", "#9172EC" }, + { "Medium Purple3", "#7A5DC7" }, + { "Medium Purple4", "#4E387E" }, + { "Medium Sea Green", "#306754" }, + { "Medium Slate Blue", "#5E5A80" }, + { "Medium Spring Green", "#348017" }, + { "Medium Turquoise", "#48CCCD" }, + { "Medium Violet Red", "#CA226B" }, + { "Midnight Blue", "#151B54" }, + { "Orange", "#FF8040" }, + { "Pale Turquoise", "#92C7C7" }, + { "Pale Turquoise1", "#5E7D7E" }, + { "Pale Violet Red", "#D16587" }, + { "Pale Violet Red1", "#F778A1" }, + { "Pale Violet Red2", "#E56E94" }, + { "Pale Violet Red3", "#C25A7C" }, + { "Pale Violet Red4", "#7E354D" }, + { "Pastel Green", "#00FF00" }, + { "Pink", "#FAAFBE" }, + { "Pink1", "#FF00FF" }, + { "Pink2", "#E7A1B0" }, + { "Pink3", "#C48793" }, + { "Pink4", "#7F525D" }, + { "Plum", "#B93B8F" }, + { "Plum1", "#F9B7FF" }, + { "Plum2", "#E6A9EC" }, + { "Plum3", "#C38EC7" }, + { "Plum4", "#7E587E" }, + { "Purple", "#8E35EF" }, + { "Purple1", "#893BFF" }, + { "Purple2", "#7F38EC" }, + { "Purple3", "#6C2DC7" }, + { "Purple4", "#461B7E" }, + { "Red", "#FF0000" }, + { "Red1", "#F62217" }, + { "Red2", "#E41B17" }, + { "Rosy Brown", "#B38481" }, + { "Rosy Brown1", "#FBBBB9" }, + { "Rosy Brown2", "#E8ADAA" }, + { "Rosy Brown3", "#C5908E" }, + { "Rosy Brown4", "#7F5A58" }, + { "Royal Blue", "#2B60DE" }, + { "Royal Blue1", "#306EFF" }, + { "Royal Blue2", "#2B65EC" }, + { "Royal Blue3", "#2554C7" }, + { "Royal Blue4", "#15317E" }, + { "Salmon1", "#F88158" }, + { "Salmon2", "#E67451" }, + { "Salmon3", "#C36241" }, + { "Salmon4", "#7E3817" }, + { "Sandy Brown", "#EE9A4D" }, + { "Sea Green", "#4E8975" }, + { "Sea Green1", "#6AFB92" }, + { "Sea Green2", "#64E986" }, + { "Sea Green3", "#54C571" }, + { "Sea Green4", "#387C44" }, + { "Sienna", "#8A4117" }, + { "Sienna1", "#F87431" }, + { "Sienna2", "#E66C2C" }, + { "Sienna3", "#C35817" }, + { "Sienna4", "#7E3517" }, + { "Sky Blue", "#82CAFF" }, + { "Sky Blue1", "#6698FF" }, + { "Sky Blue2", "#79BAEC" }, + { "Sky Blue3", "#659EC7" }, + { "Sky Blue4", "#41627E" }, + { "Slate Blue", "#357EC7" }, + { "Slate Blue1", "#737CA1" }, + { "Slate Blue2", "#6960EC" }, + { "Slate Blue3", "#342D7E" }, + { "Slate Gray", "#657383" }, + { "Slate Gray1", "#C2DFFF" }, + { "Slate Gray2", "#B4CFEC" }, + { "Slate Gray3", "#98AFC7" }, + { "Slate Gray4", "#616D7E" }, + { "Spring Green", "#4AA02C" }, + { "Spring Green1", "#5EFB6E" }, + { "Spring Green2", "#57E964" }, + { "Spring Green3", "#4CC552" }, + { "Spring Green4", "#347C2C" }, + { "Steel Blue", "#4863A0" }, + { "Steel Blue1", "#5CB3FF" }, + { "Steel Blue2", "#56A5EC" }, + { "Steel Blue3", "#488AC7" }, + { "Steel Blue4", "#2B547E" }, + { "Thistle", "#D2B9D3" }, + { "Thistle1", "#FCDFFF" }, + { "Thistle2", "#E9CFEC" }, + { "Thistle3", "#C6AEC7" }, + { "Thistle4", "#806D7E" }, + { "Turquoise", "#00FFFF" }, + { "Turquoise1", "#43C6DB" }, + { "Turquoise2", "#52F3FF" }, + { "Turquoise3", "#4EE2EC" }, + { "Turquoise4", "#43BFC7" }, + { "Violet", "#8D38C9" }, + { "Violet Red", "#F6358A" }, + { "Violet Red1", "#F6358A" }, + { "Violet Red2", "#E4317F" }, + { "Violet Red3", "#C12869" }, + { "Violet Red4", "#7D0541" }, + { "White", "#FFFFFF" }, + { "Yellow", "#FFFF00" }, + { "Yellow1", "#FFFC17" }, + { "Yellow Green", "#52D017" } + }; + for (auto color : colors) { + if (!strcasecmp(color.first.c_str(), color_name.c_str())) { + return color.second; + } + } + + return std::string(); +} + diff --git a/zone/questmgr.h b/zone/questmgr.h index 3c109f4f5..f17ffda1a 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -373,6 +373,7 @@ public: void ClearNPCTypeCache(int npctype_id); void ReloadZoneStaticData(); std::string secondstotime(int duration); + std::string gethexcolorcode(std::string color_name); Client *GetInitiator() const; NPC *GetNPC() const; From dd06033a588a752a685d0c4477fa7109b0e38532 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 23 Apr 2021 08:47:39 -0400 Subject: [PATCH 026/624] Add character-specific zone-based experience modifiers. (#1326) * Add character-specific zone-based experience modifiers. This will allow server operators to give players individual experience modifiers (regular and AA). Zone ID 0 will server as a global modifier for players, if no rows are found the modifier defaults to 1.0 so experience is neither increased nor decreased. Setting a zone-specific modifier will override the zone ID 0 global modifier. Requires a SQL update: sql/git/required/2021_04_11_character_exp_modifiers.sql - Add quest::getaaexpmodifierbycharid(character_id, zone_id) to Perl. - Add eq.get_aa_exp_modifier_by_char_id(character_id, zone_id) to Lua. - Add quest::getexpmodifierbycharid(character_id, zone_id) to Perl. - Add eq.get_exp_modifier_by_char_id(character_id, zone_id) to Lua. - Add quest::setaaexpmodifierbycharid(character_id, zone_id, aa_modifier) to Perl. - Add eq.set_aa_exp_modifier_by_char_id(character_id, zone_id, aa_modifier) to Lua. - Add quest::setexpmodifierbycharid(character_id, zone_id, exp_modifier) to Perl. - Add eq.set_exp_modifier_by_char_id(character_id, zone_id, exp_modifier) to Lua. - Add $client->GetAAEXPModifier(character_id, zone_id) to Perl. - Add client:GetAAEXPModifier(character_id, zone_id) to Lua. - Add $client->GetEXPModifier(character_id, zone_id) to Perl. - Add client:GetEXPModifier(character_id, zone_id) to Lua. - Add $client->SetAAEXPModifier(zone_id, aa_modifier) to Perl. - Add client:SetAAEXPModifier(zone_id, aa_modifier) to Lua. - Add $client->SetEXPModifier(zone_id, exp_modifier) to Perl. - Add client:SetEXPModifier(zone_id, exp_modifier) to Lua. * Removed unneeded []. * Fix variable name, * Fix variable name. * Fix version.h. * Rename 2021_04_11_character_exp_modifiers.sql to 2021_04_23_character_exp_modifiers.sql * Update db_update_manifest.txt --- common/ruletypes.h | 1 + common/version.h | 2 +- utils/sql/db_update_manifest.txt | 1 + .../2021_04_23_character_exp_modifiers.sql | 7 ++ zone/client.h | 5 ++ zone/embparser_api.cpp | 62 ++++++++++++++ zone/exp.cpp | 8 ++ zone/lua_client.cpp | 26 +++++- zone/lua_client.h | 4 + zone/lua_general.cpp | 22 ++++- zone/perl_client.cpp | 70 ++++++++++++++++ zone/questmgr.cpp | 14 ++++ zone/questmgr.h | 6 +- zone/zonedb.cpp | 82 +++++++++++++++++++ zone/zonedb.h | 5 ++ 15 files changed, 311 insertions(+), 4 deletions(-) create mode 100644 utils/sql/git/required/2021_04_23_character_exp_modifiers.sql diff --git a/common/ruletypes.h b/common/ruletypes.h index b66061dbd..15cc43575 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -162,6 +162,7 @@ RULE_BOOL(Character, UseNoJunkFishing, false, "Disregards junk items when fishin RULE_BOOL(Character, SoftDeletes, true, "When characters are deleted in character select, they are only soft deleted") RULE_INT(Character, DefaultGuild, 0, "If not 0, new characters placed into the guild # indicated") RULE_BOOL(Character, ProcessFearedProximity, false, "Processes proximity checks when feared") +RULE_BOOL(Character, EnableCharacterEXPMods, false, "Enables character zone-based experience modifiers.") RULE_CATEGORY_END() RULE_CATEGORY(Mercs) diff --git a/common/version.h b/common/version.h index 0ad111e7c..7b66c1dc4 100644 --- a/common/version.h +++ b/common/version.h @@ -34,7 +34,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9163 +#define CURRENT_BINARY_DATABASE_VERSION 9164 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9027 diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 393dc23e8..071a20e14 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -417,6 +417,7 @@ 9161|2021_02_15_npc_spell_entries_unsigned.sql|SELECT * FROM db_version WHERE version >= 9161|empty| 9162|2021_02_17_server_scheduled_events.sql|SELECT * FROM db_version WHERE version >= 9162|empty| 9163|2021_04_17_zone_safe_heading_changes.sql|SHOW COLUMNS FROM `zone` LIKE 'safe_heading'|empty| +9164|2021_04_23_character_exp_modifiers.sql|SHOW TABLES LIKE 'character_exp_modifiers'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2021_04_23_character_exp_modifiers.sql b/utils/sql/git/required/2021_04_23_character_exp_modifiers.sql new file mode 100644 index 000000000..4a1bcb5de --- /dev/null +++ b/utils/sql/git/required/2021_04_23_character_exp_modifiers.sql @@ -0,0 +1,7 @@ +CREATE TABLE `character_exp_modifiers` ( + `character_id` int NOT NULL, + `zone_id` int NOT NULL, + `aa_modifier` float NOT NULL, + `exp_modifier` float NOT NULL, + PRIMARY KEY (`character_id`, `zone_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Compact; diff --git a/zone/client.h b/zone/client.h index 741408d3f..6e2840217 100644 --- a/zone/client.h +++ b/zone/client.h @@ -600,6 +600,11 @@ public: inline uint32 GetEXP() const { return m_pp.exp; } + inline double GetAAEXPModifier(uint32 zone_id) const { return database.GetAAEXPModifier(CharacterID(), zone_id); }; + inline double GetEXPModifier(uint32 zone_id) const { return database.GetEXPModifier(CharacterID(), zone_id); }; + inline void SetAAEXPModifier(uint32 zone_id, double aa_modifier) { database.SetAAEXPModifier(CharacterID(), zone_id, aa_modifier); }; + inline void SetEXPModifier(uint32 zone_id, double exp_modifier) { database.SetEXPModifier(CharacterID(), zone_id, exp_modifier); }; + bool UpdateLDoNPoints(int32 points, uint32 theme); void SetPVPPoints(uint32 Points) { m_pp.PVPCurrentPoints = Points; } uint32 GetPVPPoints() { return m_pp.PVPCurrentPoints; } diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 4ab576cd2..b95e7946e 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -6494,6 +6494,64 @@ XS(XS__gethexcolorcode) { XSRETURN(1); } +XS(XS__getaaexpmodifierbycharid); +XS(XS__getaaexpmodifierbycharid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::getaaexpmodifierbycharid(uint32 character_id, uint32 zone_id)"); + + dXSTARG; + double aa_modifier; + uint32 character_id = (uint32) SvUV(ST(0)); + uint32 zone_id = (uint32) SvUV(ST(1)); + aa_modifier = quest_manager.GetAAEXPModifierByCharID(character_id, zone_id); + XSprePUSH; + PUSHn((double) aa_modifier); + XSRETURN(1); +} + +XS(XS__getexpmodifierbycharid); +XS(XS__getexpmodifierbycharid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::getexpmodifierbycharid(uint32 character_id, uint32 zone_id)"); + + dXSTARG; + double exp_modifier; + uint32 character_id = (uint32) SvUV(ST(0)); + uint32 zone_id = (uint32) SvUV(ST(1)); + exp_modifier = quest_manager.GetEXPModifierByCharID(character_id, zone_id); + XSprePUSH; + PUSHn((double) exp_modifier); + XSRETURN(1); +} + +XS(XS__setaaexpmodifierbycharid); +XS(XS__setaaexpmodifierbycharid) { + dXSARGS; + if (items != 3) { + Perl_croak(aTHX_ "Usage: quest::setaaexpmodifierbycharid(uint32 character_id, uint32 zone_id, float aa_modifier)"); + } + uint32 character_id = (uint32) SvUV(ST(0)); + uint32 zone_id = (uint32) SvUV(ST(1)); + double aa_modifier = (double) SvNV(ST(2)); + quest_manager.SetAAEXPModifierByCharID(character_id, zone_id, aa_modifier); + XSRETURN_EMPTY; +} + +XS(XS__setexpmodifierbycharid); +XS(XS__setexpmodifierbycharid) { + dXSARGS; + if (items != 3) { + Perl_croak(aTHX_ "Usage: quest::setexpmodifierbycharid(uint32 character_id, uint32 zone_id, float exp_modifier)"); + } + uint32 character_id = (uint32) SvUV(ST(0)); + uint32 zone_id = (uint32) SvUV(ST(1)); + double exp_modifier = (double) SvNV(ST(2)); + quest_manager.SetEXPModifierByCharID(character_id, zone_id, exp_modifier); + XSRETURN_EMPTY; +} + /* This is the callback perl will look for to setup the quest package's XSUBs @@ -6700,10 +6758,12 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "follow"), XS__follow, file); newXS(strcpy(buf, "forcedoorclose"), XS__forcedoorclose, file); newXS(strcpy(buf, "forcedooropen"), XS__forcedooropen, file); + newXS(strcpy(buf, "getaaexpmodifierbycharid"), XS__getaaexpmodifierbycharid, file); newXS(strcpy(buf, "getcharidbyname"), XS__getcharidbyname, file); newXS(strcpy(buf, "getclassname"), XS__getclassname, file); newXS(strcpy(buf, "gethexcolorcode"), XS__gethexcolorcode, file); newXS(strcpy(buf, "getcurrencyid"), XS__getcurrencyid, file); + newXS(strcpy(buf, "getexpmodifierbycharid"), XS__getexpmodifierbycharid, file); newXS(strcpy(buf, "get_expedition"), XS__get_expedition, file); newXS(strcpy(buf, "get_expedition_by_char_id"), XS__get_expedition_by_char_id, file); newXS(strcpy(buf, "get_expedition_by_dz_id"), XS__get_expedition_by_dz_id, file); @@ -6792,10 +6852,12 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "scribespells"), XS__scribespells, file); newXS(strcpy(buf, "secondstotime"), XS__secondstotime, file); newXS(strcpy(buf, "selfcast"), XS__selfcast, file); + newXS(strcpy(buf, "setaaexpmodifierbycharid"), XS__setaaexpmodifierbycharid, file); newXS(strcpy(buf, "set_proximity"), XS__set_proximity, file); newXS(strcpy(buf, "set_zone_flag"), XS__set_zone_flag, file); newXS(strcpy(buf, "setallskill"), XS__setallskill, file); newXS(strcpy(buf, "setanim"), XS__setanim, file); + newXS(strcpy(buf, "setexpmodifierbycharid"), XS__setexpmodifierbycharid, file); newXS(strcpy(buf, "setglobal"), XS__setglobal, file); newXS(strcpy(buf, "setguild"), XS__setguild, file); newXS(strcpy(buf, "sethp"), XS__sethp, file); diff --git a/zone/exp.cpp b/zone/exp.cpp index 608a7c4ad..9b4df5492 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -327,6 +327,10 @@ void Client::CalculateStandardAAExp(uint32 &add_aaxp, uint8 conlevel, bool resex add_aaxp *= RuleR(Character, FinalExpMultiplier); } + if (RuleB(Character, EnableCharacterEXPMods)) { + add_aaxp *= GetAAEXPModifier(this->GetZoneID()); + } + add_aaxp = (uint32)(RuleR(Character, AAExpMultiplier) * add_aaxp * aatotalmod); } @@ -486,6 +490,10 @@ void Client::CalculateExp(uint32 in_add_exp, uint32 &add_exp, uint32 &add_aaxp, add_exp *= RuleR(Character, FinalExpMultiplier); } + if (RuleB(Character, EnableCharacterEXPMods)) { + add_exp *= GetEXPModifier(this->GetZoneID()); + } + add_exp = GetEXP() + add_exp; } diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 1a71026f2..6b6265ce5 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -2063,6 +2063,26 @@ void Lua_Client::Fling(float value, float target_x, float target_y, float target self->Fling(value, target_x, target_y, target_z, ignore_los, clipping); } +double Lua_Client::GetAAEXPModifier(uint32 zone_id) { + Lua_Safe_Call_Real(); + return self->GetAAEXPModifier(zone_id); +} + +double Lua_Client::GetEXPModifier(uint32 zone_id) { + Lua_Safe_Call_Real(); + return self->GetEXPModifier(zone_id); +} + +void Lua_Client::SetAAEXPModifier(uint32 zone_id, double aa_modifier) { + Lua_Safe_Call_Void(); + self->SetAAEXPModifier(zone_id, aa_modifier); +} + +void Lua_Client::SetEXPModifier(uint32 zone_id, double exp_modifier) { + Lua_Safe_Call_Void(); + self->SetEXPModifier(zone_id, exp_modifier); +} + luabind::scope lua_register_client() { return luabind::class_("Client") .def(luabind::constructor<>()) @@ -2411,7 +2431,11 @@ luabind::scope lua_register_client() { .def("MovePCDynamicZone", (void(Lua_Client::*)(std::string, int, bool))&Lua_Client::MovePCDynamicZone) .def("Fling", (void(Lua_Client::*)(float,float,float,float))&Lua_Client::Fling) .def("Fling", (void(Lua_Client::*)(float,float,float,float,bool))&Lua_Client::Fling) - .def("Fling", (void(Lua_Client::*)(float,float,float,float,bool,bool))&Lua_Client::Fling); + .def("Fling", (void(Lua_Client::*)(float,float,float,float,bool,bool))&Lua_Client::Fling) + .def("GetAAEXPModifier", (double(Lua_Client::*)(uint32))&Lua_Client::GetAAEXPModifier) + .def("GetEXPModifier", (double(Lua_Client::*)(uint32))&Lua_Client::GetEXPModifier) + .def("SetAAEXPModifier", (void(Lua_Client::*)(uint32,double))&Lua_Client::SetAAEXPModifier) + .def("SetEXPModifier", (void(Lua_Client::*)(uint32,double))&Lua_Client::SetEXPModifier); } luabind::scope lua_register_inventory_where() { diff --git a/zone/lua_client.h b/zone/lua_client.h index 6d28656c1..bfd0f6b84 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -70,6 +70,10 @@ public: int GetBaseWIS(); int GetWeight(); uint32 GetEXP(); + double GetEXPModifier(uint32 zone_id); + double GetAAEXPModifier(uint32 zone_id); + void SetAAEXPModifier(uint32 zone_id, double aa_modifier); + void SetEXPModifier(uint32 zone_id, double exp_modifier); uint32 GetAAExp(); uint32 GetAAPercent(); uint32 GetTotalSecondsPlayed(); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index a1b0a1a26..a121194be 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -2326,6 +2326,22 @@ std::string lua_get_hex_color_code(std::string color_name) { return quest_manager.gethexcolorcode(color_name); } +double lua_get_aa_exp_modifier_by_char_id(uint32 character_id, uint32 zone_id) { + return database.GetAAEXPModifier(character_id, zone_id); +} + +double lua_get_exp_modifier_by_char_id(uint32 character_id, uint32 zone_id) { + return database.GetEXPModifier(character_id, zone_id); +} + +void lua_set_aa_exp_modifier_by_char_id(uint32 character_id, uint32 zone_id, double aa_modifier) { + database.SetAAEXPModifier(character_id, zone_id, aa_modifier); +} + +void lua_set_exp_modifier_by_char_id(uint32 character_id, uint32 zone_id, double exp_modifier) { + database.SetEXPModifier(character_id, zone_id, exp_modifier); +} + #define LuaCreateNPCParse(name, c_type, default_value) do { \ cur = table[#name]; \ if(luabind::type(cur) != LUA_TNIL) { \ @@ -2868,7 +2884,11 @@ luabind::scope lua_register_general() { luabind::def("log_combat", (void(*)(std::string))&lua_log_combat), luabind::def("seconds_to_time", &lua_seconds_to_time), luabind::def("get_hex_color_code", &lua_get_hex_color_code), - + luabind::def("get_aa_exp_modifier_by_char_id", &lua_get_aa_exp_modifier_by_char_id), + luabind::def("get_exp_modifier_by_char_id", &lua_get_exp_modifier_by_char_id), + luabind::def("set_aa_exp_modifier_by_char_id", &lua_set_aa_exp_modifier_by_char_id), + luabind::def("set_exp_modifier_by_char_id", &lua_set_exp_modifier_by_char_id), + /** * Expansions */ diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index e3b5f4f64..4d675d8fb 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -5224,6 +5224,72 @@ XS(XS_Client_GetInventory) { XSRETURN(1); } +XS(XS_Client_GetAAEXPModifier); +XS(XS_Client_GetAAEXPModifier) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::GetAAEXPModifier(THIS, uint32 zone_id)"); + { + Client* THIS; + double aa_modifier = 1.0f; + uint32 zone_id = (uint32)SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_CLIENT; + aa_modifier = THIS->GetAAEXPModifier(zone_id); + XSprePUSH; + PUSHn((double) aa_modifier); + } + XSRETURN(1); +} + +XS(XS_Client_GetEXPModifier); +XS(XS_Client_GetEXPModifier) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::GetEXPModifier(THIS, uint32 zone_id)"); + { + Client* THIS; + double exp_modifier = 1.0f; + uint32 zone_id = (uint32)SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_CLIENT; + exp_modifier = THIS->GetEXPModifier(zone_id); + XSprePUSH; + PUSHn((double) exp_modifier); + } + XSRETURN(1); +} + +XS(XS_Client_SetAAEXPModifier); +XS(XS_Client_SetAAEXPModifier) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::SetAAEXPModifier(THIS, uint32 zone_id, float aa_modifier)"); + { + Client* THIS; + uint32 zone_id = (uint32)SvUV(ST(1)); + double aa_modifier = (double) SvNV(ST(2)); + VALIDATE_THIS_IS_CLIENT; + THIS->SetAAEXPModifier(zone_id, aa_modifier); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_SetEXPModifier); +XS(XS_Client_SetEXPModifier) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::SetEXPModifier(THIS, uint32 zone_id, float exp_modifier)"); + { + Client* THIS; + uint32 zone_id = (uint32)SvUV(ST(1)); + double exp_modifier = (double) SvNV(ST(2)); + VALIDATE_THIS_IS_CLIENT; + THIS->SetEXPModifier(zone_id, exp_modifier); + } + XSRETURN_EMPTY; +} + #ifdef __cplusplus extern "C" #endif @@ -5285,6 +5351,7 @@ XS(boot_Client) { newXSproto(strcpy(buf, "ForageItem"), XS_Client_ForageItem, file, "$"); newXSproto(strcpy(buf, "Freeze"), XS_Client_Freeze, file, "$"); newXSproto(strcpy(buf, "GetAAExp"), XS_Client_GetAAExp, file, "$"); + newXSproto(strcpy(buf, "GetAAEXPModifier"), XS_Client_GetAAEXPModifier, file, "$$"); newXSproto(strcpy(buf, "GetAALevel"), XS_Client_GetAALevel, file, "$$"); newXSproto(strcpy(buf, "GetAAPercent"), XS_Client_GetAAPercent, file, "$"); newXSproto(strcpy(buf, "GetAAPoints"), XS_Client_GetAAPoints, file, "$$"); @@ -5328,6 +5395,7 @@ XS(boot_Client) { newXSproto(strcpy(buf, "GetEndurance"), XS_Client_GetEndurance, file, "$"); newXSproto(strcpy(buf, "GetEnduranceRatio"), XS_Client_GetEnduranceRatio, file, "$"); newXSproto(strcpy(buf, "GetEXP"), XS_Client_GetEXP, file, "$"); + newXSproto(strcpy(buf, "GetEXPModifier"), XS_Client_GetEXPModifier, file, "$$"); newXSproto(strcpy(buf, "GetExpedition"), XS_Client_GetExpedition, file, "$"); newXSproto(strcpy(buf, "GetExpeditionLockouts"), XS_Client_GetExpeditionLockouts, file, "$;$"); newXSproto(strcpy(buf, "GetFace"), XS_Client_GetFace, file, "$"); @@ -5454,6 +5522,7 @@ XS(boot_Client) { newXSproto(strcpy(buf, "SendToGuildHall"), XS_Client_SendToGuildHall, file, "$"); newXSproto(strcpy(buf, "SendWebLink"), XS_Client_SendWebLink, file, "$:$"); newXSproto(strcpy(buf, "SendZoneFlagInfo"), XS_Client_SendZoneFlagInfo, file, "$$"); + newXSproto(strcpy(buf, "SetAAEXPModifier"), XS_Client_SetAAEXPModifier, file, "$$$"); newXSproto(strcpy(buf, "SetAAPoints"), XS_Client_SetAAPoints, file, "$$"); newXSproto(strcpy(buf, "SetAATitle"), XS_Client_SetAATitle, file, "$$;$"); newXSproto(strcpy(buf, "SetAccountFlag"), XS_Client_SetAccountFlag, file, "$$"); @@ -5475,6 +5544,7 @@ XS(boot_Client) { newXSproto(strcpy(buf, "SetEbonCrystals"), XS_Client_SetEbonCrystals, file, "$$"); newXSproto(strcpy(buf, "SetEndurance"), XS_Client_SetEndurance, file, "$$"); newXSproto(strcpy(buf, "SetEXP"), XS_Client_SetEXP, file, "$$$;$"); + newXSproto(strcpy(buf, "SetEXPModifier"), XS_Client_SetEXPModifier, file, "$$$"); newXSproto(strcpy(buf, "SetFactionLevel"), XS_Client_SetFactionLevel, file, "$$$$$$"); newXSproto(strcpy(buf, "SetFactionLevel2"), XS_Client_SetFactionLevel2, file, "$$$$$$$"); newXSproto(strcpy(buf, "SetFeigned"), XS_Client_SetFeigned, file, "$$"); diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 3872e2a35..1a1eba4fc 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -4616,3 +4616,17 @@ std::string QuestManager::gethexcolorcode(std::string color_name) { return std::string(); } +double QuestManager::GetAAEXPModifierByCharID(uint32 character_id, uint32 zone_id) const { + return database.GetAAEXPModifier(character_id, zone_id); +} +double QuestManager::GetEXPModifierByCharID(uint32 character_id, uint32 zone_id) const { + return database.GetEXPModifier(character_id, zone_id); +} + +void QuestManager::SetAAEXPModifierByCharID(uint32 character_id, uint32 zone_id, double aa_modifier) { + database.SetAAEXPModifier(character_id, zone_id, aa_modifier); +} + +void QuestManager::SetEXPModifierByCharID(uint32 character_id, uint32 zone_id, double exp_modifier) { + database.SetEXPModifier(character_id, zone_id, exp_modifier); +} diff --git a/zone/questmgr.h b/zone/questmgr.h index f17ffda1a..83ffe6f5b 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -373,7 +373,11 @@ public: void ClearNPCTypeCache(int npctype_id); void ReloadZoneStaticData(); std::string secondstotime(int duration); - std::string gethexcolorcode(std::string color_name); + std::string gethexcolorcode(std::string color_name); + double GetAAEXPModifierByCharID(uint32 character_id, uint32 zone_id) const; + double GetEXPModifierByCharID(uint32 character_id, uint32 zone_id) const; + void SetAAEXPModifierByCharID(uint32 character_id, uint32 zone_id, double aa_modifier); + void SetEXPModifierByCharID(uint32 character_id, uint32 zone_id, double exp_modifier); Client *GetInitiator() const; NPC *GetNPC() const; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index d68620c38..327e43058 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -4893,3 +4893,85 @@ uint32 ZoneDatabase::SaveSaylinkID(const char* saylink_text) return results.LastInsertedID(); } + +double ZoneDatabase::GetAAEXPModifier(uint32 character_id, uint32 zone_id) const { + std::string query = fmt::format( + SQL( + SELECT + `aa_modifier` + FROM + `character_exp_modifiers` + WHERE + `character_id` = {} + AND + (`zone_id` = {} OR `zone_id` = 0) + ORDER BY `zone_id` DESC + LIMIT 1 + ), + character_id, + zone_id + ); + auto results = database.QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + return atof(row[0]); + } + return 1.0f; +} + +double ZoneDatabase::GetEXPModifier(uint32 character_id, uint32 zone_id) const { + std::string query = fmt::format( + SQL( + SELECT + `exp_modifier` + FROM + `character_exp_modifiers` + WHERE + `character_id` = {} + AND + (`zone_id` = {} OR `zone_id` = 0) + ORDER BY `zone_id` DESC + LIMIT 1 + ), + character_id, + zone_id + ); + auto results = database.QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + return atof(row[0]); + } + return 1.0f; +} + +void ZoneDatabase::SetAAEXPModifier(uint32 character_id, uint32 zone_id, double aa_modifier) { + float exp_modifier = GetEXPModifier(character_id, zone_id); + std::string query = fmt::format( + SQL( + REPLACE INTO + `character_exp_modifiers` + VALUES + ({}, {}, {}, {}) + ), + character_id, + zone_id, + aa_modifier, + exp_modifier + ); + database.QueryDatabase(query); +} + +void ZoneDatabase::SetEXPModifier(uint32 character_id, uint32 zone_id, double exp_modifier) { + float aa_modifier = GetAAEXPModifier(character_id, zone_id); + std::string query = fmt::format( + SQL( + REPLACE INTO + `character_exp_modifiers` + VALUES + ({}, {}, {}, {}) + ), + character_id, + zone_id, + aa_modifier, + exp_modifier + ); + database.QueryDatabase(query); +} diff --git a/zone/zonedb.h b/zone/zonedb.h index b77c157d3..d7e41ecb0 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -331,6 +331,11 @@ public: bool SaveCharacterSkill(uint32 character_id, uint32 skill_id, uint32 value); bool SaveCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); bool SaveCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp); + + double GetAAEXPModifier(uint32 character_id, uint32 zone_id) const; + double GetEXPModifier(uint32 character_id, uint32 zone_id) const; + void SetAAEXPModifier(uint32 character_id, uint32 zone_id, double aa_modifier); + void SetEXPModifier(uint32 character_id, uint32 zone_id, double exp_modifier); /* Character Inventory */ bool NoRentExpired(const char* name); From 6fb687871ce0bd31e01daaeebedbaa8ca710683f Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Tue, 27 Apr 2021 17:21:37 -0400 Subject: [PATCH 027/624] Fix calls to GetSafePoints to not pass null_ptr as zonename (#1336) * Fix calls to GetSafePoints to not pass null_ptr as zonename * Fix GetSafePoints to check and deal will nullptr being sent as short_zone_name * Remove unintentinal formatting change * Fix some typos/cut-n-paste errors I assume Co-authored-by: Noudess --- common/database.cpp | 4 ++++ world/worlddb.cpp | 8 ++++---- zone/zoning.cpp | 6 +++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index 5c1ec9a6f..a75c65480 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -972,6 +972,10 @@ bool Database::SetVariable(const std::string varname, const std::string &varvalu // Get zone starting points from DB bool Database::GetSafePoints(const char* zone_short_name, uint32 instance_version, float* safe_x, float* safe_y, float* safe_z, float* safe_heading, int16* min_status, uint8* min_level, char *flag_needed) { + + if (zone_short_name == nullptr) + return false; + std::string query = fmt::format( SQL( SELECT diff --git a/world/worlddb.cpp b/world/worlddb.cpp index 69d15c751..14decb79d 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -235,7 +235,7 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **o if (atoi(row_d[1]) != 0) { player_profile_struct.binds[4].zone_id = (uint32) atoi(row_d[1]); content_db.GetSafePoints( - ZoneName(player_profile_struct.binds[4].zone_id), + ZoneName(player_profile_struct.binds[4].zone_id, true), 0, &player_profile_struct.binds[4].x, &player_profile_struct.binds[4].y, @@ -252,7 +252,7 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **o float heading = atof(row_d[5]); if (x == 0 && y == 0 && z == 0 && heading == 0) { content_db.GetSafePoints( - ZoneName(player_profile_struct.binds[4].zone_id), + ZoneName(player_profile_struct.binds[4].zone_id, true), 0, &x, &y, @@ -567,7 +567,7 @@ bool WorldDatabase::GetStartZone( p_player_profile_struct->heading == 0 ) { content_db.GetSafePoints( - ZoneName(p_player_profile_struct->zone_id), + ZoneName(p_player_profile_struct->zone_id, true), 0, &p_player_profile_struct->x, &p_player_profile_struct->y, @@ -583,7 +583,7 @@ bool WorldDatabase::GetStartZone( p_player_profile_struct->binds[0].heading == 0 ) { content_db.GetSafePoints( - ZoneName(p_player_profile_struct->binds[0].zone_id), + ZoneName(p_player_profile_struct->binds[0].zone_id, true), 0, &p_player_profile_struct->binds[0].x, &p_player_profile_struct->binds[0].y, diff --git a/zone/zoning.cpp b/zone/zoning.cpp index 854671260..0d088aadf 100644 --- a/zone/zoning.cpp +++ b/zone/zoning.cpp @@ -230,9 +230,9 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { break; case GateToBindPoint: target_x = m_pp.binds[0].x; - target_x = m_pp.binds[0].y; - target_x = m_pp.binds[0].z; - target_x = m_pp.binds[0].heading; + target_y = m_pp.binds[0].y; + target_z = m_pp.binds[0].z; + target_heading = m_pp.binds[0].heading; break; case ZoneToBindPoint: target_x = m_pp.binds[0].x; From 1637ea95cb42d640b443bea730ee00bf00e0d501 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 27 Apr 2021 17:46:59 -0400 Subject: [PATCH 028/624] Fix Perl NPC GetAvoidanceRating() Perl Croak. (#1333) --- zone/perl_npc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index 4dd9b77b4..223207787 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -1459,7 +1459,7 @@ XS(XS_NPC_GetAvoidanceRating); /* prototype to pass -Wmissing-prototypes */ XS(XS_NPC_GetAvoidanceRating) { dXSARGS; if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetAvoidanceyRating(THIS)"); // @categories Stats and Attributes + Perl_croak(aTHX_ "Usage: NPC::GetAvoidanceRating(THIS)"); // @categories Stats and Attributes { NPC *THIS; int32 RETVAL; From 39de9c04f66112153f26886765d32bcb8330fbbf Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 27 Apr 2021 19:52:00 -0400 Subject: [PATCH 029/624] Add new experience modifiers table to GetCharacterTables() and GetPlayerTables() in database_schema.h. (#1338) --- common/database_schema.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/database_schema.h b/common/database_schema.h index eb539cdb9..8d8e81441 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -51,6 +51,7 @@ namespace DatabaseSchema { {"character_disciplines", "id"}, {"character_enabledtasks", "charid"}, {"character_expedition_lockouts", "character_id"}, + {"character_exp_modifiers", "character_id"}, {"character_inspect_messages", "id"}, {"character_item_recast", "id"}, {"character_languages", "id"}, @@ -116,6 +117,7 @@ namespace DatabaseSchema { "character_disciplines", "character_enabledtasks", "character_expedition_lockouts", + "character_exp_modifiers", "character_inspect_messages", "character_item_recast", "character_languages", From c063d9512ed3d6320647b0ce6c891243fa305f6e Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 27 Apr 2021 19:52:39 -0400 Subject: [PATCH 030/624] [Rule] Add a rule to disable the Pet Resist buff (#1337) Spells:July242002PetResists defaults to true --- common/ruletypes.h | 1 + zone/spells.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 15cc43575..5a30bfc2c 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -368,6 +368,7 @@ RULE_BOOL(Spells, AllowItemTGB, false, "Target group buff (/tgb) doesn't work wi RULE_BOOL(Spells, NPCInnateProcOverride, true, "NPC innate procs override the target type to single target") RULE_BOOL(Spells, OldRainTargets, false, "Use old incorrectly implemented maximum targets for rains") RULE_BOOL(Spells, NPCSpellPush, false, "Enable spell push on NPCs") +RULE_BOOL(Spells, July242002PetResists, true, "Enable Pets using PCs resist change from July 24 2002") RULE_CATEGORY_END() RULE_CATEGORY(Combat) diff --git a/zone/spells.cpp b/zone/spells.cpp index ed4c6c57a..13c7ff1cf 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4531,7 +4531,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use // JULY 24, 2002 changes int level = GetLevel(); - if (IsPetOwnerClient() && caster->IsNPC() && !caster->IsPetOwnerClient()) { + if (RuleB(Spells,July242002PetResists) && IsPetOwnerClient() && caster->IsNPC() && !caster->IsPetOwnerClient()) { auto owner = GetOwner(); if (owner != nullptr) { target_resist = std::max(target_resist, owner->GetResist(resist_type)); From 4358e24dab6cded6c885881af2c3cd2c2216af9c Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 27 Apr 2021 19:53:34 -0400 Subject: [PATCH 031/624] [Bug Fix] Fix use-after-free corruption with some DB calls (#1335) --- common/database.cpp | 20 ++++++++++++-------- common/database.h | 4 ++-- zone/embparser_api.cpp | 7 ++++--- zone/lua_general.cpp | 4 ++-- zone/questmgr.cpp | 14 ++++++++------ zone/questmgr.h | 4 ++-- 6 files changed, 30 insertions(+), 23 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index a75c65480..385f07c18 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -870,36 +870,40 @@ void Database::GetCharName(uint32 char_id, char* name) { } } -const char* Database::GetCharNameByID(uint32 char_id) { +std::string Database::GetCharNameByID(uint32 char_id) { std::string query = fmt::format("SELECT `name` FROM `character_data` WHERE id = {}", char_id); auto results = QueryDatabase(query); + std::string res; if (!results.Success()) { - return ""; + return res; } if (results.RowCount() == 0) { - return ""; + return res; } auto row = results.begin(); - return row[0]; + res = row[0]; + return res; } -const char* Database::GetNPCNameByID(uint32 npc_id) { +std::string Database::GetNPCNameByID(uint32 npc_id) { std::string query = fmt::format("SELECT `name` FROM `npc_types` WHERE id = {}", npc_id); auto results = QueryDatabase(query); + std::string res; if (!results.Success()) { - return ""; + return res; } if (results.RowCount() == 0) { - return ""; + return res; } auto row = results.begin(); - return row[0]; + res = row[0]; + return res; } bool Database::LoadVariables() { diff --git a/common/database.h b/common/database.h index c15e200bc..f6d93d5aa 100644 --- a/common/database.h +++ b/common/database.h @@ -138,8 +138,8 @@ public: void GetAccountName(uint32 accountid, char* name, uint32* oLSAccountID = 0); void GetCharName(uint32 char_id, char* name); - const char *GetCharNameByID(uint32 char_id); - const char *GetNPCNameByID(uint32 npc_id); + std::string GetCharNameByID(uint32 char_id); + std::string GetNPCNameByID(uint32 npc_id); void LoginIP(uint32 AccountID, const char* LoginIP); /* Instancing */ diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index b95e7946e..f0ba69526 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -3033,9 +3033,9 @@ XS(XS__getnpcnamebyid) { dXSTARG; uint32 npc_id = (int) SvIV(ST(0)); - const char *npc_name = quest_manager.getnpcnamebyid(npc_id); + auto npc_name = quest_manager.getnpcnamebyid(npc_id); - sv_setpv(TARG, npc_name); + sv_setpv(TARG, npc_name.c_str()); XSprePUSH; PUSHTARG; XSRETURN(1); @@ -3423,8 +3423,9 @@ XS(XS__getcharnamebyid) { Const_char *RETVAL; uint32 char_id = (int) SvUV(ST(0)); + auto name = quest_manager.getcharnamebyid(char_id); - RETVAL = quest_manager.getcharnamebyid(char_id); + RETVAL = name.c_str(); sv_setpv(TARG, RETVAL); XSprePUSH; diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index a121194be..62f0544c7 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -901,7 +901,7 @@ bool lua_delete_data(std::string bucket_key) { return DataBucket::DeleteData(bucket_key); } -const char *lua_get_char_name_by_id(uint32 char_id) { +std::string lua_get_char_name_by_id(uint32 char_id) { return database.GetCharNameByID(char_id); } @@ -937,7 +937,7 @@ int lua_get_group_id_by_char_id(uint32 char_id) { return database.GetGroupIDByCharID(char_id); } -const char *lua_get_npc_name_by_id(uint32 npc_id) { +std::string lua_get_npc_name_by_id(uint32 npc_id) { return quest_manager.getnpcnamebyid(npc_id); } diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 1a1eba4fc..561f3eb3a 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -2781,11 +2781,12 @@ std::string QuestManager::getitemname(uint32 item_id) { return item_name; } -const char *QuestManager::getnpcnamebyid(uint32 npc_id) { +std::string QuestManager::getnpcnamebyid(uint32 npc_id) { + std::string res; if (npc_id > 0) { - return database.GetNPCNameByID(npc_id); + res = database.GetNPCNameByID(npc_id); } - return ""; + return res; } uint16 QuestManager::CreateInstance(const char *zone, int16 version, uint32 duration) @@ -2991,11 +2992,12 @@ std::string QuestManager::saylink(char *saylink_text, bool silent, const char *l return EQ::SayLinkEngine::GenerateQuestSaylink(saylink_text, silent, link_name); } -const char* QuestManager::getcharnamebyid(uint32 char_id) { +std::string QuestManager::getcharnamebyid(uint32 char_id) { + std::string res; if (char_id > 0) { - return database.GetCharNameByID(char_id); + res = database.GetCharNameByID(char_id); } - return ""; + return res; } uint32 QuestManager::getcharidbyname(const char* name) { diff --git a/zone/questmgr.h b/zone/questmgr.h index 83ffe6f5b..95166886f 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -265,7 +265,7 @@ public: void FlagInstanceByRaidLeader(uint32 zone, int16 version); const char* varlink(char* perltext, int item_id); std::string saylink(char *saylink_text, bool silent, const char *link_name); - const char* getcharnamebyid(uint32 char_id); + std::string getcharnamebyid(uint32 char_id); uint32 getcharidbyname(const char* name); std::string getclassname(uint8 class_id, uint8 level = 0); int getcurrencyid(uint32 item_id); @@ -273,7 +273,7 @@ public: const char* getguildnamebyid(int guild_id); int getguildidbycharid(uint32 char_id); int getgroupidbycharid(uint32 char_id); - const char* getnpcnamebyid(uint32 npc_id); + std::string getnpcnamebyid(uint32 npc_id); int getraididbycharid(uint32 char_id); void SetRunning(bool val); bool IsRunning(); From abf73947f434b98524016e35554449bb47b4ca8b Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Tue, 27 Apr 2021 19:53:56 -0400 Subject: [PATCH 032/624] [Dynamic Zones] Rename dynamic zone structs (#1327) * Rename dz member id field The name of the struct is enough to make this implicit * Rename dz member enum and struct Rename ExpeditionMember to DynamicZoneMember Rename ExpeditionMemberStatus to DynamicZoneMemberStatus * Rename dz window packet structs This makes it more clear the window may be used by any dynamic zone system not just expeditions (live missions fill the window when player doesn't have an active expedition). * Rename dz window packet fields --- common/dynamic_zone_base.h | 15 ++++ common/eq_constants.h | 2 +- common/eq_packet_structs.h | 21 +++--- common/expedition_base.cpp | 32 ++++----- common/expedition_base.h | 33 +++------ common/patches/rof.cpp | 20 +++--- common/patches/rof2.cpp | 20 +++--- common/patches/rof2_structs.h | 22 +++--- common/patches/rof_structs.h | 18 ++--- common/patches/sod.cpp | 20 +++--- common/patches/sod_structs.h | 18 ++--- common/patches/sof.cpp | 20 +++--- common/patches/sof_structs.h | 18 ++--- common/patches/titanium.cpp | 20 +++--- common/patches/titanium_structs.h | 18 ++--- common/patches/uf.cpp | 20 +++--- common/patches/uf_structs.h | 18 ++--- world/expedition.cpp | 18 ++--- world/expedition.h | 2 +- world/expedition_state.cpp | 4 +- world/expedition_state.h | 4 +- zone/client.cpp | 6 +- zone/client_process.cpp | 6 +- zone/expedition.cpp | 112 +++++++++++++++--------------- zone/expedition.h | 10 +-- zone/expedition_database.cpp | 16 ++--- zone/expedition_database.h | 10 +-- zone/expedition_request.cpp | 2 +- zone/expedition_request.h | 4 +- zone/lua_expedition.cpp | 2 +- zone/perl_expedition.cpp | 2 +- 31 files changed, 267 insertions(+), 266 deletions(-) diff --git a/common/dynamic_zone_base.h b/common/dynamic_zone_base.h index 2c157b2b5..075c4e606 100644 --- a/common/dynamic_zone_base.h +++ b/common/dynamic_zone_base.h @@ -13,6 +13,21 @@ class Database; class ServerPacket; +struct DynamicZoneMember +{ + uint32_t id = 0; + std::string name; + DynamicZoneMemberStatus status = DynamicZoneMemberStatus::Online; + + DynamicZoneMember() = default; + DynamicZoneMember(uint32_t id, std::string name_) + : id(id), name{std::move(name_)} {} + DynamicZoneMember(uint32_t id, std::string name_, DynamicZoneMemberStatus status_) + : id(id), name{std::move(name_)}, status(status_) {} + + bool IsValid() const { return id != 0 && !name.empty(); } +}; + struct DynamicZoneLocation { uint32_t zone_id = 0; diff --git a/common/eq_constants.h b/common/eq_constants.h index b17dcc2be..a585bdd87 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -474,7 +474,7 @@ enum class DynamicZoneType Quest }; -enum class ExpeditionMemberStatus : uint8_t +enum class DynamicZoneMemberStatus : uint8_t { Unknown = 0, Online, diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 808839b27..f5d47546e 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -4861,30 +4861,31 @@ struct ExpeditionInviteResponse_Struct /*079*/ uint8 unknown079; // padding garbage? }; -struct ExpeditionInfo_Struct +struct DynamicZoneInfo_Struct { /*000*/ uint32 client_id; /*004*/ uint32 unknown004; // added after titanium -/*008*/ uint32 assigned; // padded bool, 0: not in expedition (clear data), 1: in expedition +/*008*/ uint32 assigned; // padded bool, 0: clear info, 1: fill window info /*012*/ uint32 max_players; -/*016*/ char expedition_name[128]; +/*016*/ char dz_name[128]; /*144*/ char leader_name[64]; +//*208*/ uint32 dz_type; // only in newer clients, if not 1 (expedition type) window does not auto show when dz info assigned }; -struct ExpeditionMemberEntry_Struct +struct DynamicZoneMemberEntry_Struct { -/*000*/ char name[64]; // variable length, null terminated, max 0x40 (64) -/*064*/ uint8 expedition_status; // 0: unknown, 1: Online, 2: Offline, 3: In Dynamic Zone, 4: Link Dead +/*000*/ char name[64]; // variable length, null terminated, max 0x40 (64) +/*064*/ uint8 online_status; // 0: unknown, 1: Online, 2: Offline, 3: In Dynamic Zone, 4: Link Dead }; -struct ExpeditionMemberList_Struct +struct DynamicZoneMemberList_Struct { /*000*/ uint32 client_id; /*004*/ uint32 member_count; -/*008*/ ExpeditionMemberEntry_Struct members[0]; // variable length +/*008*/ DynamicZoneMemberEntry_Struct members[0]; // variable length }; -struct ExpeditionMemberListName_Struct +struct DynamicZoneMemberListName_Struct { /*000*/ uint32 client_id; /*004*/ uint32 unknown004; @@ -4907,7 +4908,7 @@ struct ExpeditionLockoutTimers_Struct /*008*/ ExpeditionLockoutTimerEntry_Struct timers[0]; }; -struct ExpeditionSetLeaderName_Struct +struct DynamicZoneLeaderName_Struct { /*000*/ uint32 client_id; /*004*/ uint32 unknown004; diff --git a/common/expedition_base.cpp b/common/expedition_base.cpp index fa4962706..732a759e7 100644 --- a/common/expedition_base.cpp +++ b/common/expedition_base.cpp @@ -2,7 +2,7 @@ #include "repositories/expeditions_repository.h" ExpeditionBase::ExpeditionBase(uint32_t id, const std::string& uuid, - const std::string& expedition_name, const ExpeditionMember& leader, + const std::string& expedition_name, const DynamicZoneMember& leader, uint32_t min_players, uint32_t max_players ) : m_id(id), @@ -23,20 +23,20 @@ void ExpeditionBase::LoadRepositoryResult(ExpeditionsRepository::ExpeditionWithL m_max_players = entry.max_players; m_add_replay_on_join = entry.add_replay_on_join; m_is_locked = entry.is_locked; - m_leader.char_id = entry.leader_id; + m_leader.id = entry.leader_id; m_leader.name = std::move(entry.leader_name); } void ExpeditionBase::AddMemberFromRepositoryResult( ExpeditionMembersRepository::MemberWithName&& entry) { - auto status = ExpeditionMemberStatus::Unknown; + auto status = DynamicZoneMemberStatus::Unknown; AddInternalMember({ entry.character_id, std::move(entry.character_name), status }); } -void ExpeditionBase::AddInternalMember(const ExpeditionMember& member) +void ExpeditionBase::AddInternalMember(const DynamicZoneMember& member) { - if (!HasMember(member.char_id)) + if (!HasMember(member.id)) { m_members.emplace_back(member); } @@ -45,32 +45,32 @@ void ExpeditionBase::AddInternalMember(const ExpeditionMember& member) void ExpeditionBase::RemoveInternalMember(uint32_t character_id) { m_members.erase(std::remove_if(m_members.begin(), m_members.end(), - [&](const ExpeditionMember& member) { return member.char_id == character_id; } + [&](const DynamicZoneMember& member) { return member.id == character_id; } ), m_members.end()); } bool ExpeditionBase::HasMember(uint32_t character_id) { - return std::any_of(m_members.begin(), m_members.end(), [&](const ExpeditionMember& member) { - return member.char_id == character_id; + return std::any_of(m_members.begin(), m_members.end(), [&](const DynamicZoneMember& member) { + return member.id == character_id; }); } bool ExpeditionBase::HasMember(const std::string& character_name) { - return std::any_of(m_members.begin(), m_members.end(), [&](const ExpeditionMember& member) { + return std::any_of(m_members.begin(), m_members.end(), [&](const DynamicZoneMember& member) { return (strcasecmp(member.name.c_str(), character_name.c_str()) == 0); }); } -ExpeditionMember ExpeditionBase::GetMemberData(uint32_t character_id) +DynamicZoneMember ExpeditionBase::GetMemberData(uint32_t character_id) { - auto it = std::find_if(m_members.begin(), m_members.end(), [&](const ExpeditionMember& member) { - return member.char_id == character_id; + auto it = std::find_if(m_members.begin(), m_members.end(), [&](const DynamicZoneMember& member) { + return member.id == character_id; }); - ExpeditionMember member_data; + DynamicZoneMember member_data; if (it != m_members.end()) { member_data = *it; @@ -78,13 +78,13 @@ ExpeditionMember ExpeditionBase::GetMemberData(uint32_t character_id) return member_data; } -ExpeditionMember ExpeditionBase::GetMemberData(const std::string& character_name) +DynamicZoneMember ExpeditionBase::GetMemberData(const std::string& character_name) { - auto it = std::find_if(m_members.begin(), m_members.end(), [&](const ExpeditionMember& member) { + auto it = std::find_if(m_members.begin(), m_members.end(), [&](const DynamicZoneMember& member) { return (strcasecmp(member.name.c_str(), character_name.c_str()) == 0); }); - ExpeditionMember member_data; + DynamicZoneMember member_data; if (it != m_members.end()) { member_data = *it; diff --git a/common/expedition_base.h b/common/expedition_base.h index aeb072fae..25c5cfd33 100644 --- a/common/expedition_base.h +++ b/common/expedition_base.h @@ -1,28 +1,13 @@ #ifndef COMMON_EXPEDITION_BASE_H #define COMMON_EXPEDITION_BASE_H -#include "eq_constants.h" +#include "dynamic_zone_base.h" #include "repositories/expeditions_repository.h" #include "repositories/expedition_members_repository.h" #include #include #include -struct ExpeditionMember -{ - uint32_t char_id = 0; - std::string name; - ExpeditionMemberStatus status = ExpeditionMemberStatus::Online; - - ExpeditionMember() = default; - ExpeditionMember(uint32_t id, const std::string& name_) - : char_id(id), name(name_) {} - ExpeditionMember(uint32_t id, const std::string& name_, ExpeditionMemberStatus status_) - : char_id(id), name(name_), status(status_) {} - - bool IsValid() const { return char_id != 0 && !name.empty(); } -}; - class ExpeditionBase { public: @@ -33,16 +18,16 @@ public: ExpeditionBase& operator=(ExpeditionBase&&) = default; uint32_t GetID() const { return m_id; } - uint32_t GetLeaderID() const { return m_leader.char_id; } + uint32_t GetLeaderID() const { return m_leader.id; } uint32_t GetMinPlayers() const { return m_min_players; } uint32_t GetMaxPlayers() const { return m_max_players; } uint32_t GetMemberCount() const { return static_cast(m_members.size()); } const std::string& GetName() const { return m_expedition_name; } const std::string& GetLeaderName() const { return m_leader.name; } const std::string& GetUUID() const { return m_uuid; } - const std::vector& GetMembers() const { return m_members; } + const std::vector& GetMembers() const { return m_members; } - void AddInternalMember(const ExpeditionMember& member); + void AddInternalMember(const DynamicZoneMember& member); void ClearInternalMembers() { m_members.clear(); } bool HasMember(const std::string& character_name); bool HasMember(uint32_t character_id); @@ -55,10 +40,10 @@ public: protected: ExpeditionBase() = default; ExpeditionBase(uint32_t id, const std::string& uuid, const std::string& expedition_name, - const ExpeditionMember& leader, uint32_t min_players, uint32_t max_players); + const DynamicZoneMember& leader, uint32_t min_players, uint32_t max_players); - ExpeditionMember GetMemberData(uint32_t character_id); - ExpeditionMember GetMemberData(const std::string& character_name); + DynamicZoneMember GetMemberData(uint32_t character_id); + DynamicZoneMember GetMemberData(const std::string& character_name); uint32_t m_id = 0; uint32_t m_min_players = 0; @@ -67,8 +52,8 @@ protected: bool m_add_replay_on_join = true; std::string m_uuid; std::string m_expedition_name; - ExpeditionMember m_leader; - std::vector m_members; + DynamicZoneMember m_leader; + std::vector m_members; }; #endif diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index b607502c4..38b109677 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -772,13 +772,13 @@ namespace RoF ENCODE(OP_DzExpeditionInfo) { - ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); - SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); + ENCODE_LENGTH_EXACT(DynamicZoneInfo_Struct); + SETUP_DIRECT_ENCODE(DynamicZoneInfo_Struct, structs::DynamicZoneInfo_Struct); OUT(client_id); OUT(assigned); OUT(max_players); - strn0cpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name)); + strn0cpy(eq->dz_name, emu->dz_name, sizeof(eq->dz_name)); strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name)); FINISH_ENCODE(); @@ -824,8 +824,8 @@ namespace RoF ENCODE(OP_DzSetLeaderName) { - ENCODE_LENGTH_EXACT(ExpeditionSetLeaderName_Struct); - SETUP_DIRECT_ENCODE(ExpeditionSetLeaderName_Struct, structs::ExpeditionSetLeaderName_Struct); + ENCODE_LENGTH_EXACT(DynamicZoneLeaderName_Struct); + SETUP_DIRECT_ENCODE(DynamicZoneLeaderName_Struct, structs::DynamicZoneLeaderName_Struct); OUT(client_id); strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name)); @@ -835,7 +835,7 @@ namespace RoF ENCODE(OP_DzMemberList) { - SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); + SETUP_VAR_ENCODE(DynamicZoneMemberList_Struct); SerializeBuffer buf; buf.WriteUInt32(emu->client_id); @@ -843,7 +843,7 @@ namespace RoF for (uint32 i = 0; i < emu->member_count; ++i) { buf.WriteString(emu->members[i].name); - buf.WriteUInt8(emu->members[i].expedition_status); + buf.WriteUInt8(emu->members[i].online_status); } __packet->size = buf.size(); @@ -855,8 +855,8 @@ namespace RoF ENCODE(OP_DzMemberListName) { - ENCODE_LENGTH_EXACT(ExpeditionMemberListName_Struct); - SETUP_DIRECT_ENCODE(ExpeditionMemberListName_Struct, structs::ExpeditionMemberListName_Struct); + ENCODE_LENGTH_EXACT(DynamicZoneMemberListName_Struct); + SETUP_DIRECT_ENCODE(DynamicZoneMemberListName_Struct, structs::DynamicZoneMemberListName_Struct); OUT(client_id); OUT(add_name); @@ -867,7 +867,7 @@ namespace RoF ENCODE(OP_DzMemberListStatus) { - auto emu = reinterpret_cast((*p)->pBuffer); + auto emu = reinterpret_cast((*p)->pBuffer); if (emu->member_count == 1) { ENCODE_FORWARD(OP_DzMemberList); diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index a6426a279..55fbfe2c8 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -821,13 +821,13 @@ namespace RoF2 ENCODE(OP_DzExpeditionInfo) { - ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); - SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); + ENCODE_LENGTH_EXACT(DynamicZoneInfo_Struct); + SETUP_DIRECT_ENCODE(DynamicZoneInfo_Struct, structs::DynamicZoneInfo_Struct); OUT(client_id); OUT(assigned); OUT(max_players); - strn0cpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name)); + strn0cpy(eq->dz_name, emu->dz_name, sizeof(eq->dz_name)); strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name)); FINISH_ENCODE(); @@ -873,8 +873,8 @@ namespace RoF2 ENCODE(OP_DzSetLeaderName) { - ENCODE_LENGTH_EXACT(ExpeditionSetLeaderName_Struct); - SETUP_DIRECT_ENCODE(ExpeditionSetLeaderName_Struct, structs::ExpeditionSetLeaderName_Struct); + ENCODE_LENGTH_EXACT(DynamicZoneLeaderName_Struct); + SETUP_DIRECT_ENCODE(DynamicZoneLeaderName_Struct, structs::DynamicZoneLeaderName_Struct); OUT(client_id); strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name)); @@ -884,7 +884,7 @@ namespace RoF2 ENCODE(OP_DzMemberList) { - SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); + SETUP_VAR_ENCODE(DynamicZoneMemberList_Struct); SerializeBuffer buf; buf.WriteUInt32(emu->client_id); @@ -892,7 +892,7 @@ namespace RoF2 for (uint32 i = 0; i < emu->member_count; ++i) { buf.WriteString(emu->members[i].name); - buf.WriteUInt8(emu->members[i].expedition_status); + buf.WriteUInt8(emu->members[i].online_status); } __packet->size = buf.size(); @@ -904,8 +904,8 @@ namespace RoF2 ENCODE(OP_DzMemberListName) { - ENCODE_LENGTH_EXACT(ExpeditionMemberListName_Struct); - SETUP_DIRECT_ENCODE(ExpeditionMemberListName_Struct, structs::ExpeditionMemberListName_Struct); + ENCODE_LENGTH_EXACT(DynamicZoneMemberListName_Struct); + SETUP_DIRECT_ENCODE(DynamicZoneMemberListName_Struct, structs::DynamicZoneMemberListName_Struct); OUT(client_id); OUT(add_name); @@ -916,7 +916,7 @@ namespace RoF2 ENCODE(OP_DzMemberListStatus) { - auto emu = reinterpret_cast((*p)->pBuffer); + auto emu = reinterpret_cast((*p)->pBuffer); if (emu->member_count == 1) { ENCODE_FORWARD(OP_DzMemberList); diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index 3041024c7..a37e25d61 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -4908,31 +4908,31 @@ struct ExpeditionInviteResponse_Struct /*079*/ uint8 unknown079; // padding garbage? }; -struct ExpeditionInfo_Struct +struct DynamicZoneInfo_Struct { /*000*/ uint32 client_id; /*004*/ uint32 unknown004; -/*008*/ uint32 assigned; // padded bool, 0: not in expedition (clear data), 1: in expedition +/*008*/ uint32 assigned; // padded bool, 0: clear info, 1: fill window info /*012*/ uint32 max_players; -/*016*/ char expedition_name[128]; +/*016*/ char dz_name[128]; /*144*/ char leader_name[64]; -//*208*/ uint32 unknown208; // live sends 01 00 00 00 here but client doesn't read it +//*208*/ uint32 dz_type; // only in newer clients, if not 1 (expedition type) window does not auto show when dz info assigned }; -struct ExpeditionMemberEntry_Struct +struct DynamicZoneMemberEntry_Struct { -/*000*/ char name[1]; // variable length, null terminated, max 0x40 (64) -/*000*/ uint8 expedition_status; // 0: unknown 1: Online, 2: Offline, 3: In Dynamic Zone, 4: Link Dead +/*000*/ char name[1]; // variable length, null terminated, max 0x40 (64) +/*000*/ uint8 online_status; // 0: unknown 1: Online, 2: Offline, 3: In Dynamic Zone, 4: Link Dead }; -struct ExpeditionMemberList_Struct +struct DynamicZoneMemberList_Struct { /*000*/ uint32 client_id; /*004*/ uint32 member_count; // number of players in window -/*008*/ ExpeditionMemberEntry_Struct members[0]; // variable length +/*008*/ DynamicZoneMemberEntry_Struct members[0]; // variable length }; -struct ExpeditionMemberListName_Struct +struct DynamicZoneMemberListName_Struct { /*000*/ uint32 client_id; /*004*/ uint32 unknown004; @@ -4955,7 +4955,7 @@ struct ExpeditionLockoutTimers_Struct /*008*/ ExpeditionLockoutTimerEntry_Struct timers[0]; }; -struct ExpeditionSetLeaderName_Struct +struct DynamicZoneLeaderName_Struct { /*000*/ uint32 client_id; /*004*/ uint32 unknown004; diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index 1afc79334..a8297b2ea 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -4841,30 +4841,30 @@ struct ExpeditionInviteResponse_Struct /*079*/ uint8 unknown079; // padding garbage? }; -struct ExpeditionInfo_Struct +struct DynamicZoneInfo_Struct { /*000*/ uint32 client_id; /*004*/ uint32 unknown004; /*008*/ uint32 assigned; // padded bool /*012*/ uint32 max_players; -/*016*/ char expedition_name[128]; +/*016*/ char dz_name[128]; /*144*/ char leader_name[64]; }; -struct ExpeditionMemberEntry_Struct +struct DynamicZoneMemberEntry_Struct { -/*000*/ char name[1]; // variable length, null terminated, max 0x40 (64) -/*000*/ uint8 expedition_status; // 0: unknown 1: Online, 2: Offline, 3: In Dynamic Zone, 4: Link Dead +/*000*/ char name[1]; // variable length, null terminated, max 0x40 (64) +/*000*/ uint8 online_status; // 0: unknown 1: Online, 2: Offline, 3: In Dynamic Zone, 4: Link Dead }; -struct ExpeditionMemberList_Struct +struct DynamicZoneMemberList_Struct { /*000*/ uint32 client_id; /*004*/ uint32 member_count; // number of players in window -/*008*/ ExpeditionMemberEntry_Struct members[0]; // variable length +/*008*/ DynamicZoneMemberEntry_Struct members[0]; // variable length }; -struct ExpeditionMemberListName_Struct +struct DynamicZoneMemberListName_Struct { /*000*/ uint32 client_id; /*004*/ uint32 unknown004; @@ -4887,7 +4887,7 @@ struct ExpeditionLockoutTimers_Struct /*008*/ ExpeditionLockoutTimerEntry_Struct timers[0]; }; -struct ExpeditionSetLeaderName_Struct +struct DynamicZoneLeaderName_Struct { /*000*/ uint32 client_id; /*004*/ uint32 unknown004; diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index 9f49f9651..404190d09 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -545,13 +545,13 @@ namespace SoD ENCODE(OP_DzExpeditionInfo) { - ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); - SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); + ENCODE_LENGTH_EXACT(DynamicZoneInfo_Struct); + SETUP_DIRECT_ENCODE(DynamicZoneInfo_Struct, structs::DynamicZoneInfo_Struct); OUT(client_id); OUT(assigned); OUT(max_players); - strn0cpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name)); + strn0cpy(eq->dz_name, emu->dz_name, sizeof(eq->dz_name)); strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name)); FINISH_ENCODE(); @@ -597,8 +597,8 @@ namespace SoD ENCODE(OP_DzSetLeaderName) { - ENCODE_LENGTH_EXACT(ExpeditionSetLeaderName_Struct); - SETUP_DIRECT_ENCODE(ExpeditionSetLeaderName_Struct, structs::ExpeditionSetLeaderName_Struct); + ENCODE_LENGTH_EXACT(DynamicZoneLeaderName_Struct); + SETUP_DIRECT_ENCODE(DynamicZoneLeaderName_Struct, structs::DynamicZoneLeaderName_Struct); OUT(client_id); strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name)); @@ -608,7 +608,7 @@ namespace SoD ENCODE(OP_DzMemberList) { - SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); + SETUP_VAR_ENCODE(DynamicZoneMemberList_Struct); SerializeBuffer buf; buf.WriteUInt32(emu->client_id); @@ -616,7 +616,7 @@ namespace SoD for (uint32 i = 0; i < emu->member_count; ++i) { buf.WriteString(emu->members[i].name); - buf.WriteUInt8(emu->members[i].expedition_status); + buf.WriteUInt8(emu->members[i].online_status); } __packet->size = buf.size(); @@ -628,8 +628,8 @@ namespace SoD ENCODE(OP_DzMemberListName) { - ENCODE_LENGTH_EXACT(ExpeditionMemberListName_Struct); - SETUP_DIRECT_ENCODE(ExpeditionMemberListName_Struct, structs::ExpeditionMemberListName_Struct); + ENCODE_LENGTH_EXACT(DynamicZoneMemberListName_Struct); + SETUP_DIRECT_ENCODE(DynamicZoneMemberListName_Struct, structs::DynamicZoneMemberListName_Struct); OUT(client_id); OUT(add_name); @@ -640,7 +640,7 @@ namespace SoD ENCODE(OP_DzMemberListStatus) { - auto emu = reinterpret_cast((*p)->pBuffer); + auto emu = reinterpret_cast((*p)->pBuffer); if (emu->member_count == 1) { ENCODE_FORWARD(OP_DzMemberList); diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 8070de172..70866db8f 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -4196,30 +4196,30 @@ struct ExpeditionInviteResponse_Struct /*079*/ uint8 unknown079; // padding garbage? }; -struct ExpeditionInfo_Struct +struct DynamicZoneInfo_Struct { /*000*/ uint32 client_id; /*004*/ uint32 unknown004; /*008*/ uint32 assigned; // padded bool /*012*/ uint32 max_players; -/*016*/ char expedition_name[128]; +/*016*/ char dz_name[128]; /*144*/ char leader_name[64]; }; -struct ExpeditionMemberEntry_Struct +struct DynamicZoneMemberEntry_Struct { -/*000*/ char name[1]; // variable length, null terminated, max 0x40 (64) -/*000*/ uint8 expedition_status; // 0: unknown 1: Online, 2: Offline, 3: In Dynamic Zone, 4: Link Dead +/*000*/ char name[1]; // variable length, null terminated, max 0x40 (64) +/*000*/ uint8 online_status; // 0: unknown 1: Online, 2: Offline, 3: In Dynamic Zone, 4: Link Dead }; -struct ExpeditionMemberList_Struct +struct DynamicZoneMemberList_Struct { /*000*/ uint32 client_id; /*004*/ uint32 member_count; // number of players in window -/*008*/ ExpeditionMemberEntry_Struct members[0]; // variable length +/*008*/ DynamicZoneMemberEntry_Struct members[0]; // variable length }; -struct ExpeditionMemberListName_Struct +struct DynamicZoneMemberListName_Struct { /*000*/ uint32 client_id; /*004*/ uint32 unknown004; @@ -4242,7 +4242,7 @@ struct ExpeditionLockoutTimers_Struct /*008*/ ExpeditionLockoutTimerEntry_Struct timers[0]; }; -struct ExpeditionSetLeaderName_Struct +struct DynamicZoneLeaderName_Struct { /*000*/ uint32 client_id; /*004*/ uint32 unknown004; diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index 4fb9642a8..07758bba6 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -533,13 +533,13 @@ namespace SoF ENCODE(OP_DzExpeditionInfo) { - ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); - SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); + ENCODE_LENGTH_EXACT(DynamicZoneInfo_Struct); + SETUP_DIRECT_ENCODE(DynamicZoneInfo_Struct, structs::DynamicZoneInfo_Struct); OUT(client_id); OUT(assigned); OUT(max_players); - strn0cpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name)); + strn0cpy(eq->dz_name, emu->dz_name, sizeof(eq->dz_name)); strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name)); FINISH_ENCODE(); @@ -585,8 +585,8 @@ namespace SoF ENCODE(OP_DzSetLeaderName) { - ENCODE_LENGTH_EXACT(ExpeditionSetLeaderName_Struct); - SETUP_DIRECT_ENCODE(ExpeditionSetLeaderName_Struct, structs::ExpeditionSetLeaderName_Struct); + ENCODE_LENGTH_EXACT(DynamicZoneLeaderName_Struct); + SETUP_DIRECT_ENCODE(DynamicZoneLeaderName_Struct, structs::DynamicZoneLeaderName_Struct); OUT(client_id); strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name)); @@ -596,7 +596,7 @@ namespace SoF ENCODE(OP_DzMemberList) { - SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); + SETUP_VAR_ENCODE(DynamicZoneMemberList_Struct); SerializeBuffer buf; buf.WriteUInt32(emu->client_id); @@ -604,7 +604,7 @@ namespace SoF for (uint32 i = 0; i < emu->member_count; ++i) { buf.WriteString(emu->members[i].name); - buf.WriteUInt8(emu->members[i].expedition_status); + buf.WriteUInt8(emu->members[i].online_status); } __packet->size = buf.size(); @@ -616,8 +616,8 @@ namespace SoF ENCODE(OP_DzMemberListName) { - ENCODE_LENGTH_EXACT(ExpeditionMemberListName_Struct); - SETUP_DIRECT_ENCODE(ExpeditionMemberListName_Struct, structs::ExpeditionMemberListName_Struct); + ENCODE_LENGTH_EXACT(DynamicZoneMemberListName_Struct); + SETUP_DIRECT_ENCODE(DynamicZoneMemberListName_Struct, structs::DynamicZoneMemberListName_Struct); OUT(client_id); OUT(add_name); @@ -628,7 +628,7 @@ namespace SoF ENCODE(OP_DzMemberListStatus) { - auto emu = reinterpret_cast((*p)->pBuffer); + auto emu = reinterpret_cast((*p)->pBuffer); if (emu->member_count == 1) { ENCODE_FORWARD(OP_DzMemberList); diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index 96edbeb98..97b200a91 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -4112,29 +4112,29 @@ struct ExpeditionInviteResponse_Struct /*075*/ uint8 unknown079; // padding/garbage? }; -struct ExpeditionInfo_Struct +struct DynamicZoneInfo_Struct { /*000*/ uint32 client_id; /*004*/ uint32 assigned; // padded bool /*008*/ uint32 max_players; -/*012*/ char expedition_name[128]; +/*012*/ char dz_name[128]; /*140*/ char leader_name[64]; }; -struct ExpeditionMemberEntry_Struct +struct DynamicZoneMemberEntry_Struct { -/*000*/ char name[1]; // variable length, null terminated, max 0x40 (64) -/*000*/ uint8 expedition_status; // 0: unknown 1: Online, 2: Offline, 3: In Dynamic Zone, 4: Link Dead +/*000*/ char name[1]; // variable length, null terminated, max 0x40 (64) +/*000*/ uint8 online_status; // 0: unknown 1: Online, 2: Offline, 3: In Dynamic Zone, 4: Link Dead }; -struct ExpeditionMemberList_Struct +struct DynamicZoneMemberList_Struct { /*000*/ uint32 client_id; /*004*/ uint32 member_count; -/*008*/ ExpeditionMemberEntry_Struct members[0]; // variable length +/*008*/ DynamicZoneMemberEntry_Struct members[0]; // variable length }; -struct ExpeditionMemberListName_Struct +struct DynamicZoneMemberListName_Struct { /*000*/ uint32 client_id; /*004*/ uint32 add_name; // padded bool, 0: remove name, 1: add name with unknown status @@ -4156,7 +4156,7 @@ struct ExpeditionLockoutTimers_Struct /*008*/ ExpeditionLockoutTimerEntry_Struct timers[0]; }; -struct ExpeditionSetLeaderName_Struct +struct DynamicZoneLeaderName_Struct { /*000*/ uint32 client_id; /*004*/ char leader_name[64]; diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index 9bc1f161f..bb35d9f87 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -476,13 +476,13 @@ namespace Titanium ENCODE(OP_DzExpeditionInfo) { - ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); - SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); + ENCODE_LENGTH_EXACT(DynamicZoneInfo_Struct); + SETUP_DIRECT_ENCODE(DynamicZoneInfo_Struct, structs::DynamicZoneInfo_Struct); OUT(client_id); OUT(assigned); OUT(max_players); - strn0cpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name)); + strn0cpy(eq->dz_name, emu->dz_name, sizeof(eq->dz_name)); strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name)); FINISH_ENCODE(); @@ -528,8 +528,8 @@ namespace Titanium ENCODE(OP_DzSetLeaderName) { - ENCODE_LENGTH_EXACT(ExpeditionSetLeaderName_Struct); - SETUP_DIRECT_ENCODE(ExpeditionSetLeaderName_Struct, structs::ExpeditionSetLeaderName_Struct); + ENCODE_LENGTH_EXACT(DynamicZoneLeaderName_Struct); + SETUP_DIRECT_ENCODE(DynamicZoneLeaderName_Struct, structs::DynamicZoneLeaderName_Struct); OUT(client_id); strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name)); @@ -539,7 +539,7 @@ namespace Titanium ENCODE(OP_DzMemberList) { - SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); + SETUP_VAR_ENCODE(DynamicZoneMemberList_Struct); SerializeBuffer buf; buf.WriteUInt32(emu->client_id); @@ -547,7 +547,7 @@ namespace Titanium for (uint32 i = 0; i < emu->member_count; ++i) { buf.WriteString(emu->members[i].name); - buf.WriteUInt8(emu->members[i].expedition_status); + buf.WriteUInt8(emu->members[i].online_status); } __packet->size = buf.size(); @@ -559,8 +559,8 @@ namespace Titanium ENCODE(OP_DzMemberListName) { - ENCODE_LENGTH_EXACT(ExpeditionMemberListName_Struct); - SETUP_DIRECT_ENCODE(ExpeditionMemberListName_Struct, structs::ExpeditionMemberListName_Struct); + ENCODE_LENGTH_EXACT(DynamicZoneMemberListName_Struct); + SETUP_DIRECT_ENCODE(DynamicZoneMemberListName_Struct, structs::DynamicZoneMemberListName_Struct); OUT(client_id); OUT(add_name); @@ -571,7 +571,7 @@ namespace Titanium ENCODE(OP_DzMemberListStatus) { - auto emu = reinterpret_cast((*p)->pBuffer); + auto emu = reinterpret_cast((*p)->pBuffer); if (emu->member_count == 1) { ENCODE_FORWARD(OP_DzMemberList); diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index e3d0313a3..2846288bc 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -3323,29 +3323,29 @@ struct ExpeditionInviteResponse_Struct /*075*/ uint8 unknown079; // padding/garbage? }; -struct ExpeditionInfo_Struct +struct DynamicZoneInfo_Struct { /*000*/ uint32 client_id; /*004*/ uint32 assigned; // padded bool /*008*/ uint32 max_players; -/*012*/ char expedition_name[128]; +/*012*/ char dz_name[128]; /*140*/ char leader_name[64]; }; -struct ExpeditionMemberEntry_Struct +struct DynamicZoneMemberEntry_Struct { -/*000*/ char name[1]; // variable length, null terminated, max 0x40 (64) -/*000*/ uint8 expedition_status; // 0: unknown 1: Online, 2: Offline, 3: In Dynamic Zone, 4: Link Dead +/*000*/ char name[1]; // variable length, null terminated, max 0x40 (64) +/*000*/ uint8 online_status; // 0: unknown 1: Online, 2: Offline, 3: In Dynamic Zone, 4: Link Dead }; -struct ExpeditionMemberList_Struct +struct DynamicZoneMemberList_Struct { /*000*/ uint32 client_id; /*004*/ uint32 member_count; -/*008*/ ExpeditionMemberEntry_Struct members[0]; // variable length +/*008*/ DynamicZoneMemberEntry_Struct members[0]; // variable length }; -struct ExpeditionMemberListName_Struct +struct DynamicZoneMemberListName_Struct { /*000*/ uint32 client_id; /*004*/ uint32 add_name; // padded bool, 0: remove name, 1: add name with unknown status @@ -3367,7 +3367,7 @@ struct ExpeditionLockoutTimers_Struct /*008*/ ExpeditionLockoutTimerEntry_Struct timers[0]; }; -struct ExpeditionSetLeaderName_Struct +struct DynamicZoneLeaderName_Struct { /*000*/ uint32 client_id; /*004*/ char leader_name[64]; diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index 2be12c733..c23415737 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -675,13 +675,13 @@ namespace UF ENCODE(OP_DzExpeditionInfo) { - ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); - SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); + ENCODE_LENGTH_EXACT(DynamicZoneInfo_Struct); + SETUP_DIRECT_ENCODE(DynamicZoneInfo_Struct, structs::DynamicZoneInfo_Struct); OUT(client_id); OUT(assigned); OUT(max_players); - strn0cpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name)); + strn0cpy(eq->dz_name, emu->dz_name, sizeof(eq->dz_name)); strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name)); FINISH_ENCODE(); @@ -727,8 +727,8 @@ namespace UF ENCODE(OP_DzSetLeaderName) { - ENCODE_LENGTH_EXACT(ExpeditionSetLeaderName_Struct); - SETUP_DIRECT_ENCODE(ExpeditionSetLeaderName_Struct, structs::ExpeditionSetLeaderName_Struct); + ENCODE_LENGTH_EXACT(DynamicZoneLeaderName_Struct); + SETUP_DIRECT_ENCODE(DynamicZoneLeaderName_Struct, structs::DynamicZoneLeaderName_Struct); OUT(client_id); strn0cpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name)); @@ -738,7 +738,7 @@ namespace UF ENCODE(OP_DzMemberList) { - SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); + SETUP_VAR_ENCODE(DynamicZoneMemberList_Struct); SerializeBuffer buf; buf.WriteUInt32(emu->client_id); @@ -746,7 +746,7 @@ namespace UF for (uint32 i = 0; i < emu->member_count; ++i) { buf.WriteString(emu->members[i].name); - buf.WriteUInt8(emu->members[i].expedition_status); + buf.WriteUInt8(emu->members[i].online_status); } __packet->size = buf.size(); @@ -758,8 +758,8 @@ namespace UF ENCODE(OP_DzMemberListName) { - ENCODE_LENGTH_EXACT(ExpeditionMemberListName_Struct); - SETUP_DIRECT_ENCODE(ExpeditionMemberListName_Struct, structs::ExpeditionMemberListName_Struct); + ENCODE_LENGTH_EXACT(DynamicZoneMemberListName_Struct); + SETUP_DIRECT_ENCODE(DynamicZoneMemberListName_Struct, structs::DynamicZoneMemberListName_Struct); OUT(client_id); OUT(add_name); @@ -770,7 +770,7 @@ namespace UF ENCODE(OP_DzMemberListStatus) { - auto emu = reinterpret_cast((*p)->pBuffer); + auto emu = reinterpret_cast((*p)->pBuffer); if (emu->member_count == 1) { ENCODE_FORWARD(OP_DzMemberList); diff --git a/common/patches/uf_structs.h b/common/patches/uf_structs.h index 5ac8ac3c2..6c1b4a4ed 100644 --- a/common/patches/uf_structs.h +++ b/common/patches/uf_structs.h @@ -4277,30 +4277,30 @@ struct ExpeditionInviteResponse_Struct /*079*/ uint8 unknown079; // padding garbage? }; -struct ExpeditionInfo_Struct +struct DynamicZoneInfo_Struct { /*000*/ uint32 client_id; /*004*/ uint32 unknown004; /*008*/ uint32 assigned; // padded bool /*012*/ uint32 max_players; -/*016*/ char expedition_name[128]; +/*016*/ char dz_name[128]; /*144*/ char leader_name[64]; }; -struct ExpeditionMemberEntry_Struct +struct DynamicZoneMemberEntry_Struct { -/*000*/ char name[1]; // variable length, null terminated, max 0x40 (64) -/*000*/ uint8 expedition_status; // 0: unknown 1: Online, 2: Offline, 3: In Dynamic Zone, 4: Link Dead +/*000*/ char name[1]; // variable length, null terminated, max 0x40 (64) +/*000*/ uint8 online_status; // 0: unknown 1: Online, 2: Offline, 3: In Dynamic Zone, 4: Link Dead }; -struct ExpeditionMemberList_Struct +struct DynamicZoneMemberList_Struct { /*000*/ uint32 client_id; /*004*/ uint32 member_count; // number of players in window -/*008*/ ExpeditionMemberEntry_Struct members[0]; // variable length +/*008*/ DynamicZoneMemberEntry_Struct members[0]; // variable length }; -struct ExpeditionMemberListName_Struct +struct DynamicZoneMemberListName_Struct { /*000*/ uint32 client_id; /*004*/ uint32 unknown004; @@ -4323,7 +4323,7 @@ struct ExpeditionLockoutTimers_Struct /*008*/ ExpeditionLockoutTimerEntry_Struct timers[0]; }; -struct ExpeditionSetLeaderName_Struct +struct DynamicZoneLeaderName_Struct { /*000*/ uint32 client_id; /*004*/ uint32 unknown004; diff --git a/world/expedition.cpp b/world/expedition.cpp index 723098013..fceb6c7dc 100644 --- a/world/expedition.cpp +++ b/world/expedition.cpp @@ -48,7 +48,7 @@ void Expedition::RemoveMember(uint32_t character_id) { RemoveInternalMember(character_id); - if (character_id == m_leader.char_id) + if (character_id == m_leader.id) { ChooseNewLeader(); } @@ -64,9 +64,9 @@ void Expedition::ChooseNewLeader() // we don't track expedition member status in world so may choose a linkdead member // this is fine since it will trigger another change when that member goes offline - auto it = std::find_if(m_members.begin(), m_members.end(), [&](const ExpeditionMember& member) { - if (member.char_id != m_leader.char_id) { - auto member_cle = client_list.FindCLEByCharacterID(member.char_id); + auto it = std::find_if(m_members.begin(), m_members.end(), [&](const DynamicZoneMember& member) { + if (member.id != m_leader.id) { + auto member_cle = client_list.FindCLEByCharacterID(member.id); return (member_cle && member_cle->GetOnline() == CLE_Status::InZone); } return false; @@ -76,7 +76,7 @@ void Expedition::ChooseNewLeader() { // no online members found, fallback to choosing any member it = std::find_if(m_members.begin(), m_members.end(), - [&](const ExpeditionMember& member) { return (member.char_id != m_leader.char_id); }); + [&](const DynamicZoneMember& member) { return (member.id != m_leader.id); }); } if (it != m_members.end() && SetNewLeader(*it)) @@ -85,15 +85,15 @@ void Expedition::ChooseNewLeader() } } -bool Expedition::SetNewLeader(const ExpeditionMember& member) +bool Expedition::SetNewLeader(const DynamicZoneMember& member) { - if (!HasMember(member.char_id)) + if (!HasMember(member.id)) { return false; } LogExpeditionsModerate("Replacing [{}] leader [{}] with [{}]", m_id, m_leader.name, member.name); - ExpeditionDatabase::UpdateLeaderID(m_id, member.char_id); + ExpeditionDatabase::UpdateLeaderID(m_id, member.id); m_leader = member; m_dynamic_zone.SetLeaderName(m_leader.name); SendZonesLeaderChanged(); @@ -125,7 +125,7 @@ void Expedition::SendZonesLeaderChanged() auto pack = std::make_unique(ServerOP_ExpeditionLeaderChanged, pack_size); auto buf = reinterpret_cast(pack->pBuffer); buf->expedition_id = GetID(); - buf->leader_id = m_leader.char_id; + buf->leader_id = m_leader.id; zoneserver_list.SendPacket(pack.get()); } diff --git a/world/expedition.h b/world/expedition.h index 11ae083ed..db0647093 100644 --- a/world/expedition.h +++ b/world/expedition.h @@ -41,7 +41,7 @@ public: void SendZonesExpeditionDeleted(); void SendZonesExpireWarning(uint32_t minutes_remaining); void SetDynamicZone(DynamicZone&& dz); - bool SetNewLeader(const ExpeditionMember& member); + bool SetNewLeader(const DynamicZoneMember& member); private: void SendZonesLeaderChanged(); diff --git a/world/expedition_state.cpp b/world/expedition_state.cpp index 19693e279..33a92d61c 100644 --- a/world/expedition_state.cpp +++ b/world/expedition_state.cpp @@ -115,13 +115,13 @@ void ExpeditionState::CacheExpeditions( } void ExpeditionState::MemberChange( - uint32_t expedition_id, const ExpeditionMember& member, bool remove) + uint32_t expedition_id, const DynamicZoneMember& member, bool remove) { auto expedition = GetExpedition(expedition_id); if (expedition) { if (remove) { - expedition->RemoveMember(member.char_id); + expedition->RemoveMember(member.id); } else { expedition->AddInternalMember(member); } diff --git a/world/expedition_state.h b/world/expedition_state.h index da8cdb306..90f4a4293 100644 --- a/world/expedition_state.h +++ b/world/expedition_state.h @@ -30,7 +30,7 @@ extern class ExpeditionState expedition_state; class Expedition; -struct ExpeditionMember; +struct DynamicZoneMember; class ExpeditionState { @@ -40,7 +40,7 @@ public: void CacheAllFromDatabase(); Expedition* GetExpedition(uint32_t expedition_id); Expedition* GetExpeditionByDynamicZoneID(uint32_t dz_id); - void MemberChange(uint32_t expedition_id, const ExpeditionMember& member, bool remove); + void MemberChange(uint32_t expedition_id, const DynamicZoneMember& member, bool remove); void Process(); void RemoveAllMembers(uint32_t expedition_id); diff --git a/zone/client.cpp b/zone/client.cpp index 6ffda19a9..2d6ad9994 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -3401,7 +3401,7 @@ void Client::LinkDead() Expedition* expedition = GetExpedition(); if (expedition) { - expedition->SetMemberStatus(this, ExpeditionMemberStatus::LinkDead); + expedition->SetMemberStatus(this, DynamicZoneMemberStatus::LinkDead); } // save_timer.Start(2500); @@ -9550,11 +9550,11 @@ void Client::UpdateExpeditionInfoAndLockouts() if (expedition->GetDynamicZone().IsCurrentZoneDzInstance()) { expedition->SyncCharacterLockouts(CharacterID(), m_expedition_lockouts); - expedition->SetMemberStatus(this, ExpeditionMemberStatus::InDynamicZone); + expedition->SetMemberStatus(this, DynamicZoneMemberStatus::InDynamicZone); } else { - expedition->SetMemberStatus(this, ExpeditionMemberStatus::Online); + expedition->SetMemberStatus(this, DynamicZoneMemberStatus::Online); } } diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 384d3aab5..9c2175b27 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -183,7 +183,7 @@ bool Client::Process() { Expedition* expedition = GetExpedition(); if (expedition) { - expedition->SetMemberStatus(this, ExpeditionMemberStatus::Offline); + expedition->SetMemberStatus(this, DynamicZoneMemberStatus::Offline); } return false; //delete client @@ -578,7 +578,7 @@ bool Client::Process() { Expedition* expedition = GetExpedition(); if (expedition) { - expedition->SetMemberStatus(this, ExpeditionMemberStatus::LinkDead); + expedition->SetMemberStatus(this, DynamicZoneMemberStatus::LinkDead); } } } @@ -714,7 +714,7 @@ void Client::OnDisconnect(bool hard_disconnect) { Expedition* expedition = GetExpedition(); if (expedition && !bZoning) { - expedition->SetMemberStatus(this, ExpeditionMemberStatus::Offline); + expedition->SetMemberStatus(this, DynamicZoneMemberStatus::Offline); } RemoveAllAuras(); diff --git a/zone/expedition.cpp b/zone/expedition.cpp index 0c1481d7d..c3ef81e6a 100644 --- a/zone/expedition.cpp +++ b/zone/expedition.cpp @@ -50,7 +50,7 @@ const int32_t Expedition::EVENT_TIMER_ID = 1; Expedition::Expedition( uint32_t id, const std::string& uuid, DynamicZone&& dz, const std::string& expedition_name, - const ExpeditionMember& leader, uint32_t min_players, uint32_t max_players + const DynamicZoneMember& leader, uint32_t min_players, uint32_t max_players ) : ExpeditionBase(id, uuid, expedition_name, leader, min_players, max_players) { SetDynamicZone(std::move(dz)); @@ -109,7 +109,7 @@ Expedition* Expedition::TryCreate( expedition_uuid, std::move(dynamiczone), request.GetExpeditionName(), - ExpeditionMember{ request.GetLeaderID(), request.GetLeaderName() }, + DynamicZoneMember{ request.GetLeaderID(), request.GetLeaderName() }, request.GetMinPlayers(), request.GetMaxPlayers() ); @@ -264,7 +264,7 @@ void Expedition::SaveMembers(ExpeditionRequest& request) std::vector member_ids; for (const auto& member : m_members) { - member_ids.emplace_back(member.char_id); + member_ids.emplace_back(member.id); } ExpeditionDatabase::InsertMembers(m_id, m_members); @@ -484,11 +484,11 @@ bool Expedition::RemoveMember(const std::string& remove_char_name) return false; } - ExpeditionDatabase::DeleteMember(m_id, member.char_id); - m_dynamiczone.RemoveCharacter(member.char_id); + ExpeditionDatabase::DeleteMember(m_id, member.id); + m_dynamiczone.RemoveCharacter(member.id); - ProcessMemberRemoved(member.name, member.char_id); - SendWorldMemberChanged(member.name, member.char_id, true); + ProcessMemberRemoved(member.name, member.id); + SendWorldMemberChanged(member.name, member.id, true); return true; } @@ -507,17 +507,17 @@ void Expedition::SwapMember(Client* add_client, const std::string& remove_char_n } // make remove and add atomic to avoid racing with separate world messages - ExpeditionDatabase::DeleteMember(m_id, member.char_id); + ExpeditionDatabase::DeleteMember(m_id, member.id); ExpeditionDatabase::InsertMember(m_id, add_client->CharacterID()); - m_dynamiczone.RemoveCharacter(member.char_id); + m_dynamiczone.RemoveCharacter(member.id); m_dynamiczone.AddCharacter(add_client->CharacterID()); - ProcessMemberRemoved(member.name, member.char_id); + ProcessMemberRemoved(member.name, member.id); ProcessMemberAdded(add_client->GetName(), add_client->CharacterID()); - SendWorldMemberSwapped(member.name, member.char_id, add_client->GetName(), add_client->CharacterID()); + SendWorldMemberSwapped(member.name, member.id, add_client->GetName(), add_client->CharacterID()); } -void Expedition::SetMemberStatus(Client* client, ExpeditionMemberStatus status) +void Expedition::SetMemberStatus(Client* client, DynamicZoneMemberStatus status) { if (client) { @@ -526,14 +526,14 @@ void Expedition::SetMemberStatus(Client* client, ExpeditionMemberStatus status) // world could detect this itself but it'd have to process member status updates // a member coming online will trigger a leader change if all members were offline - if (m_leader.status == ExpeditionMemberStatus::Offline) + if (m_leader.status == DynamicZoneMemberStatus::Offline) { SendWorldExpeditionUpdate(ServerOP_ExpeditionChooseNewLeader); } } } -void Expedition::UpdateMemberStatus(uint32_t update_member_id, ExpeditionMemberStatus status) +void Expedition::UpdateMemberStatus(uint32_t update_member_id, DynamicZoneMemberStatus status) { auto member_data = GetMemberData(update_member_id); if (!member_data.IsValid()) @@ -541,12 +541,12 @@ void Expedition::UpdateMemberStatus(uint32_t update_member_id, ExpeditionMemberS return; } - if (status == ExpeditionMemberStatus::InDynamicZone && !RuleB(Expedition, EnableInDynamicZoneStatus)) + if (status == DynamicZoneMemberStatus::InDynamicZone && !RuleB(Expedition, EnableInDynamicZoneStatus)) { - status = ExpeditionMemberStatus::Online; + status = DynamicZoneMemberStatus::Online; } - if (update_member_id == m_leader.char_id) + if (update_member_id == m_leader.id) { m_leader.status = status; } @@ -561,12 +561,12 @@ void Expedition::UpdateMemberStatus(uint32_t update_member_id, ExpeditionMemberS for (auto& member : m_members) { - if (member.char_id == update_member_id) + if (member.id == update_member_id) { member.status = status; } - Client* member_client = entity_list.GetClientByCharID(member.char_id); + Client* member_client = entity_list.GetClientByCharID(member.id); if (member_client) { member_client->QueuePacket(outapp_member_status.get()); @@ -732,7 +732,7 @@ void Expedition::DzInviteResponse(Client* add_client, bool accepted, const std:: // a null leader_client is handled by SendLeaderMessage fallbacks // note current leader receives invite reply messages (if leader changed) - Client* leader_client = entity_list.GetClientByCharID(m_leader.char_id); + Client* leader_client = entity_list.GetClientByCharID(m_leader.id); if (!accepted) { @@ -756,7 +756,7 @@ void Expedition::DzInviteResponse(Client* add_client, bool accepted, const std:: if (was_swap_invite) { auto swap_member = GetMemberData(swap_remove_name); - if (!swap_member.IsValid() || !ExpeditionDatabase::HasMember(m_id, swap_member.char_id)) + if (!swap_member.IsValid() || !ExpeditionDatabase::HasMember(m_id, swap_member.id)) { has_conflicts = true; } @@ -808,7 +808,7 @@ bool Expedition::ConfirmLeaderCommand(Client* requester) return false; } - if (m_leader.char_id != requester->CharacterID()) + if (m_leader.id != requester->CharacterID()) { requester->MessageString(Chat::System, EXPEDITION_NOT_LEADER, m_leader.name.c_str()); return false; @@ -877,7 +877,7 @@ void Expedition::DzAddPlayer( if (member_data.IsValid()) { // live prioritizes offline message before already a member message - if (member_data.status == ExpeditionMemberStatus::Offline) + if (member_data.status == DynamicZoneMemberStatus::Offline) { requester->MessageString(Chat::Red, DZADD_NOT_ONLINE, add_char_name.c_str()); } @@ -1054,12 +1054,12 @@ void Expedition::ProcessLeaderChanged(uint32_t new_leader_id) auto outapp_leader = CreateLeaderNamePacket(); for (const auto& member : m_members) { - Client* member_client = entity_list.GetClientByCharID(member.char_id); + Client* member_client = entity_list.GetClientByCharID(member.id); if (member_client) { member_client->QueuePacket(outapp_leader.get()); - if (member.char_id == new_leader_id && RuleB(Expedition, AlwaysNotifyNewLeaderOnChange)) + if (member.id == new_leader_id && RuleB(Expedition, AlwaysNotifyNewLeaderOnChange)) { member_client->MessageString(Chat::Yellow, DZMAKELEADER_YOU); } @@ -1096,13 +1096,13 @@ void Expedition::ProcessMakeLeader(Client* old_leader_client, Client* new_leader void Expedition::ProcessMemberAdded(const std::string& char_name, uint32_t added_char_id) { // adds the member to this expedition and notifies both leader and new member - Client* leader_client = entity_list.GetClientByCharID(m_leader.char_id); + Client* leader_client = entity_list.GetClientByCharID(m_leader.id); if (leader_client) { leader_client->MessageString(Chat::Yellow, EXPEDITION_MEMBER_ADDED, char_name.c_str(), m_expedition_name.c_str()); } - AddInternalMember({ added_char_id, char_name, ExpeditionMemberStatus::Online }); + AddInternalMember({ added_char_id, char_name, DynamicZoneMemberStatus::Online }); Client* member_client = entity_list.GetClientByCharID(added_char_id); if (member_client) @@ -1129,7 +1129,7 @@ void Expedition::ProcessMemberRemoved(const std::string& removed_char_name, uint { bool is_removed = (it->name == removed_char_name); - Client* member_client = entity_list.GetClientByCharID(it->char_id); + Client* member_client = entity_list.GetClientByCharID(it->id); if (member_client) { // all members receive the removed player name packet @@ -1175,7 +1175,7 @@ void Expedition::ProcessLockoutDuration( for (const auto& member : m_members) { - Client* member_client = entity_list.GetClientByCharID(member.char_id); + Client* member_client = entity_list.GetClientByCharID(member.id); if (member_client) { member_client->AddExpeditionLockoutDuration(m_expedition_name, @@ -1192,7 +1192,7 @@ void Expedition::ProcessLockoutDuration( void Expedition::AddLockoutDurationClients( const ExpeditionLockoutTimer& lockout, int seconds, uint32_t exclude_id) { - std::vector lockout_clients; + std::vector lockout_clients; for (const auto& client_iter : entity_list.GetClientList()) { Client* client = client_iter.second; @@ -1228,7 +1228,7 @@ void Expedition::ProcessLockoutUpdate( for (const auto& member : m_members) { - Client* member_client = entity_list.GetClientByCharID(member.char_id); + Client* member_client = entity_list.GetClientByCharID(member.id); if (member_client) { if (!remove) @@ -1254,7 +1254,7 @@ void Expedition::ProcessLockoutUpdate( void Expedition::AddLockoutClients( const ExpeditionLockoutTimer& lockout, uint32_t exclude_expedition_id) { - std::vector lockout_clients; + std::vector lockout_clients; for (const auto& client_iter : entity_list.GetClientList()) { Client* client = client_iter.second; @@ -1283,7 +1283,7 @@ void Expedition::SendNewMemberAddedToZoneMembers(const std::string& added_name) { if (member.name != added_name) // new member already updated { - Client* member_client = entity_list.GetClientByCharID(member.char_id); + Client* member_client = entity_list.GetClientByCharID(member.id); if (member_client) { member_client->QueuePacket(outapp_members.get()); @@ -1301,7 +1301,7 @@ void Expedition::SendUpdatesToZoneMembers(bool clear, bool message_on_clear) for (const auto& member : m_members) { - Client* member_client = entity_list.GetClientByCharID(member.char_id); + Client* member_client = entity_list.GetClientByCharID(member.id); if (member_client) { member_client->SetExpeditionID(clear ? 0 : GetID()); @@ -1349,13 +1349,13 @@ std::unique_ptr Expedition::CreateExpireWarningPacket(uint3 std::unique_ptr Expedition::CreateInfoPacket(bool clear) { - uint32_t outsize = sizeof(ExpeditionInfo_Struct); + uint32_t outsize = sizeof(DynamicZoneInfo_Struct); auto outapp = std::make_unique(OP_DzExpeditionInfo, outsize); - auto info = reinterpret_cast(outapp->pBuffer); + auto info = reinterpret_cast(outapp->pBuffer); if (!clear) { info->assigned = true; - strn0cpy(info->expedition_name, m_expedition_name.c_str(), sizeof(info->expedition_name)); + strn0cpy(info->dz_name, m_expedition_name.c_str(), sizeof(info->dz_name)); strn0cpy(info->leader_name, m_leader.name.c_str(), sizeof(info->leader_name)); info->max_players = m_max_players; } @@ -1380,10 +1380,10 @@ std::unique_ptr Expedition::CreateInvitePacket( std::unique_ptr Expedition::CreateMemberListPacket(bool clear) { uint32_t member_count = clear ? 0 : static_cast(m_members.size()); - uint32_t member_entries_size = sizeof(ExpeditionMemberEntry_Struct) * member_count; - uint32_t outsize = sizeof(ExpeditionMemberList_Struct) + member_entries_size; + uint32_t member_entries_size = sizeof(DynamicZoneMemberEntry_Struct) * member_count; + uint32_t outsize = sizeof(DynamicZoneMemberList_Struct) + member_entries_size; auto outapp = std::make_unique(OP_DzMemberList, outsize); - auto buf = reinterpret_cast(outapp->pBuffer); + auto buf = reinterpret_cast(outapp->pBuffer); buf->member_count = member_count; @@ -1392,7 +1392,7 @@ std::unique_ptr Expedition::CreateMemberListPacket(bool cle for (auto i = 0; i < m_members.size(); ++i) { strn0cpy(buf->members[i].name, m_members[i].name.c_str(), sizeof(buf->members[i].name)); - buf->members[i].expedition_status = static_cast(m_members[i].status); + buf->members[i].online_status = static_cast(m_members[i].status); } } @@ -1402,35 +1402,35 @@ std::unique_ptr Expedition::CreateMemberListPacket(bool cle std::unique_ptr Expedition::CreateMemberListNamePacket( const std::string& name, bool remove_name) { - uint32_t outsize = sizeof(ExpeditionMemberListName_Struct); + uint32_t outsize = sizeof(DynamicZoneMemberListName_Struct); auto outapp = std::make_unique(OP_DzMemberListName, outsize); - auto buf = reinterpret_cast(outapp->pBuffer); + auto buf = reinterpret_cast(outapp->pBuffer); buf->add_name = !remove_name; strn0cpy(buf->name, name.c_str(), sizeof(buf->name)); return outapp; } std::unique_ptr Expedition::CreateMemberListStatusPacket( - const std::string& name, ExpeditionMemberStatus status) + const std::string& name, DynamicZoneMemberStatus status) { // member list status uses member list struct with a single entry - uint32_t outsize = sizeof(ExpeditionMemberList_Struct) + sizeof(ExpeditionMemberEntry_Struct); + uint32_t outsize = sizeof(DynamicZoneMemberList_Struct) + sizeof(DynamicZoneMemberEntry_Struct); auto outapp = std::make_unique(OP_DzMemberListStatus, outsize); - auto buf = reinterpret_cast(outapp->pBuffer); + auto buf = reinterpret_cast(outapp->pBuffer); buf->member_count = 1; - auto entry = reinterpret_cast(buf->members); + auto entry = static_cast(buf->members); strn0cpy(entry->name, name.c_str(), sizeof(entry->name)); - entry->expedition_status = static_cast(status); + entry->online_status = static_cast(status); return outapp; } std::unique_ptr Expedition::CreateLeaderNamePacket() { - uint32_t outsize = sizeof(ExpeditionSetLeaderName_Struct); + uint32_t outsize = sizeof(DynamicZoneLeaderName_Struct); auto outapp = std::make_unique(OP_DzSetLeaderName, outsize); - auto buf = reinterpret_cast(outapp->pBuffer); + auto buf = reinterpret_cast(outapp->pBuffer); strn0cpy(buf->leader_name, m_leader.name.c_str(), sizeof(buf->leader_name)); return outapp; } @@ -1521,7 +1521,7 @@ void Expedition::SendWorldMemberChanged(const std::string& char_name, uint32_t c worldserver.SendPacket(pack.get()); } -void Expedition::SendWorldMemberStatus(uint32_t character_id, ExpeditionMemberStatus status) +void Expedition::SendWorldMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status) { uint32_t pack_size = sizeof(ServerExpeditionMemberStatus_Struct); auto pack = std::make_unique(ServerOP_ExpeditionMemberStatus, pack_size); @@ -1794,7 +1794,7 @@ void Expedition::HandleWorldMessage(ServerPacket* pack) auto expedition = Expedition::FindCachedExpeditionByID(buf->expedition_id); if (expedition) { - expedition->UpdateMemberStatus(buf->character_id, static_cast(buf->status)); + expedition->UpdateMemberStatus(buf->character_id, static_cast(buf->status)); } } break; @@ -1836,10 +1836,10 @@ void Expedition::HandleWorldMessage(ServerPacket* pack) if (expedition) { auto is_online = member->character_online; - auto status = is_online ? ExpeditionMemberStatus::Online : ExpeditionMemberStatus::Offline; + auto status = is_online ? DynamicZoneMemberStatus::Online : DynamicZoneMemberStatus::Offline; if (is_online && expedition->GetDynamicZone().IsInstanceID(member->character_instance_id)) { - status = ExpeditionMemberStatus::InDynamicZone; + status = DynamicZoneMemberStatus::InDynamicZone; } expedition->UpdateMemberStatus(member->character_id, status); } @@ -1922,7 +1922,7 @@ void Expedition::SendCompassUpdateToZoneMembers() { for (const auto& member : m_members) { - Client* member_client = entity_list.GetClientByCharID(member.char_id); + Client* member_client = entity_list.GetClientByCharID(member.id); if (member_client) { member_client->SendDzCompassUpdate(); @@ -2036,7 +2036,7 @@ void Expedition::SendMembersExpireWarning(uint32_t minutes_remaining) auto outapp = CreateExpireWarningPacket(minutes_remaining); for (const auto& member : m_members) { - Client* member_client = entity_list.GetClientByCharID(member.char_id); + Client* member_client = entity_list.GetClientByCharID(member.id); if (member_client) { member_client->QueuePacket(outapp.get()); diff --git a/zone/expedition.h b/zone/expedition.h index d6e906fc7..adce73fcd 100644 --- a/zone/expedition.h +++ b/zone/expedition.h @@ -52,7 +52,7 @@ class Expedition : public ExpeditionBase public: Expedition() = default; Expedition(uint32_t id, const std::string& uuid, DynamicZone&& dz, const std::string& expedition_name, - const ExpeditionMember& leader, uint32_t min_players, uint32_t max_players); + const DynamicZoneMember& leader, uint32_t min_players, uint32_t max_players); static Expedition* TryCreate(Client* requester, DynamicZone& dynamiczone, ExpeditionRequest& request); @@ -85,7 +85,7 @@ public: bool AddMember(const std::string& add_char_name, uint32_t add_char_id); void RemoveAllMembers(bool enable_removal_timers = true); bool RemoveMember(const std::string& remove_char_name); - void SetMemberStatus(Client* client, ExpeditionMemberStatus status); + void SetMemberStatus(Client* client, DynamicZoneMemberStatus status); void SwapMember(Client* add_client, const std::string& remove_char_name); bool IsLocked() const { return m_is_locked; } @@ -160,21 +160,21 @@ private: void SendWorldLockoutUpdate( const ExpeditionLockoutTimer& lockout, bool remove, bool members_only = false); void SendWorldMemberChanged(const std::string& char_name, uint32_t char_id, bool remove); - void SendWorldMemberStatus(uint32_t character_id, ExpeditionMemberStatus status); + void SendWorldMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status); void SendWorldMemberSwapped(const std::string& remove_char_name, uint32_t remove_char_id, const std::string& add_char_name, uint32_t add_char_id); void SendWorldSettingChanged(uint16_t server_opcode, bool setting_value); void SetDynamicZone(DynamicZone&& dz); void TryAddClient(Client* add_client, const std::string& inviter_name, const std::string& swap_remove_name, Client* leader_client = nullptr); - void UpdateMemberStatus(uint32_t update_character_id, ExpeditionMemberStatus status); + void UpdateMemberStatus(uint32_t update_character_id, DynamicZoneMemberStatus status); std::unique_ptr CreateExpireWarningPacket(uint32_t minutes_remaining); std::unique_ptr CreateInfoPacket(bool clear = false); std::unique_ptr CreateInvitePacket(const std::string& inviter_name, const std::string& swap_remove_name); std::unique_ptr CreateMemberListPacket(bool clear = false); std::unique_ptr CreateMemberListNamePacket(const std::string& name, bool remove_name); - std::unique_ptr CreateMemberListStatusPacket(const std::string& name, ExpeditionMemberStatus status); + std::unique_ptr CreateMemberListStatusPacket(const std::string& name, DynamicZoneMemberStatus status); std::unique_ptr CreateLeaderNamePacket(); DynamicZone m_dynamiczone { DynamicZoneType::Expedition }; diff --git a/zone/expedition_database.cpp b/zone/expedition_database.cpp index e1b5c0bf6..af6948f5f 100644 --- a/zone/expedition_database.cpp +++ b/zone/expedition_database.cpp @@ -174,7 +174,7 @@ void ExpeditionDatabase::DeleteCharacterLockout( } void ExpeditionDatabase::DeleteMembersLockout( - const std::vector& members, + const std::vector& members, const std::string& expedition_name, const std::string& event_name) { LogExpeditionsDetail("Deleting members lockout: [{}]:[{}]", expedition_name, event_name); @@ -182,7 +182,7 @@ void ExpeditionDatabase::DeleteMembersLockout( std::string query_character_ids; for (const auto& member : members) { - fmt::format_to(std::back_inserter(query_character_ids), "{},", member.char_id); + fmt::format_to(std::back_inserter(query_character_ids), "{},", member.id); } if (!query_character_ids.empty()) @@ -312,7 +312,7 @@ void ExpeditionDatabase::InsertCharacterLockouts(uint32_t character_id, } void ExpeditionDatabase::InsertMembersLockout( - const std::vector& members, const ExpeditionLockoutTimer& lockout) + const std::vector& members, const ExpeditionLockoutTimer& lockout) { LogExpeditionsDetail( "Inserting members lockout [{}]:[{}] with expire time [{}]", @@ -324,7 +324,7 @@ void ExpeditionDatabase::InsertMembersLockout( { fmt::format_to(std::back_inserter(insert_values), "({}, FROM_UNIXTIME({}), {}, '{}', '{}', '{}'),", - member.char_id, + member.id, lockout.GetExpireTime(), lockout.GetDuration(), lockout.GetExpeditionUUID(), @@ -431,7 +431,7 @@ void ExpeditionDatabase::InsertMember(uint32_t expedition_id, uint32_t character } void ExpeditionDatabase::InsertMembers( - uint32_t expedition_id, const std::vector& members) + uint32_t expedition_id, const std::vector& members) { LogExpeditionsDetail("Inserting characters into expedition [{}]", expedition_id); @@ -440,7 +440,7 @@ void ExpeditionDatabase::InsertMembers( { fmt::format_to(std::back_inserter(insert_values), "({}, {}),", - expedition_id, member.char_id + expedition_id, member.id ); } @@ -504,7 +504,7 @@ void ExpeditionDatabase::UpdateReplayLockoutOnJoin(uint32_t expedition_id, bool database.QueryDatabase(query); } -void ExpeditionDatabase::AddLockoutDuration(const std::vector& members, +void ExpeditionDatabase::AddLockoutDuration(const std::vector& members, const ExpeditionLockoutTimer& lockout, int seconds) { LogExpeditionsDetail( @@ -516,7 +516,7 @@ void ExpeditionDatabase::AddLockoutDuration(const std::vector& { fmt::format_to(std::back_inserter(insert_values), "({}, FROM_UNIXTIME({}), {}, '{}', '{}', '{}'),", - member.char_id, + member.id, lockout.GetExpireTime(), lockout.GetDuration(), lockout.GetExpeditionUUID(), diff --git a/zone/expedition_database.h b/zone/expedition_database.h index 9e0487b36..cce81c0fc 100644 --- a/zone/expedition_database.h +++ b/zone/expedition_database.h @@ -30,7 +30,7 @@ class Expedition; class ExpeditionLockoutTimer; -struct ExpeditionMember; +struct DynamicZoneMember; class MySQLRequestResult; namespace ExpeditionDatabase @@ -48,23 +48,23 @@ namespace ExpeditionDatabase void DeleteCharacterLockout(uint32_t character_id, const std::string& expedition_name, const std::string& event_name); void DeleteLockout(uint32_t expedition_id, const std::string& event_name); - void DeleteMembersLockout(const std::vector& members, + void DeleteMembersLockout(const std::vector& members, const std::string& expedition_name, const std::string& event_name); uint32_t GetExpeditionIDFromCharacterID(uint32_t character_id); uint32_t GetMemberCount(uint32_t expedition_id); bool HasMember(uint32_t expedition_id, uint32_t character_id); void InsertCharacterLockouts(uint32_t character_id, const std::vector& lockouts); - void InsertMembersLockout(const std::vector& members, + void InsertMembersLockout(const std::vector& members, const ExpeditionLockoutTimer& lockout); void InsertLockout(uint32_t expedition_id, const ExpeditionLockoutTimer& lockout); void InsertLockouts(uint32_t expedition_id, const std::unordered_map& lockouts); void InsertMember(uint32_t expedition_id, uint32_t character_id); - void InsertMembers(uint32_t expedition_id, const std::vector& members); + void InsertMembers(uint32_t expedition_id, const std::vector& members); void UpdateLockState(uint32_t expedition_id, bool is_locked); void UpdateReplayLockoutOnJoin(uint32_t expedition_id, bool add_on_join); - void AddLockoutDuration(const std::vector& members, + void AddLockoutDuration(const std::vector& members, const ExpeditionLockoutTimer& lockout, int seconds); }; diff --git a/zone/expedition_request.cpp b/zone/expedition_request.cpp index 465859df5..a1a2aa48e 100644 --- a/zone/expedition_request.cpp +++ b/zone/expedition_request.cpp @@ -221,7 +221,7 @@ bool ExpeditionRequest::CheckMembersForConflicts(const std::vector& return true; } - m_members.emplace_back(character.id, character.name, ExpeditionMemberStatus::Online); + m_members.emplace_back(character.id, character.name, DynamicZoneMemberStatus::Online); character_ids.emplace_back(character.id); } diff --git a/zone/expedition_request.h b/zone/expedition_request.h index b148d3132..64c594d3c 100644 --- a/zone/expedition_request.h +++ b/zone/expedition_request.h @@ -47,7 +47,7 @@ public: const std::string& GetNotAllAddedMessage() const { return m_not_all_added_msg; } uint32_t GetMinPlayers() const { return m_min_players; } uint32_t GetMaxPlayers() const { return m_max_players; } - std::vector GetMembers() const { return m_members; } + std::vector GetMembers() const { return m_members; } std::unordered_map GetLockouts() const { return m_lockouts; } private: @@ -72,7 +72,7 @@ private: std::string m_expedition_name; std::string m_leader_name; std::string m_not_all_added_msg; - std::vector m_members; + std::vector m_members; std::unordered_map m_lockouts; }; diff --git a/zone/lua_expedition.cpp b/zone/lua_expedition.cpp index 434cc5726..f19e417ff 100644 --- a/zone/lua_expedition.cpp +++ b/zone/lua_expedition.cpp @@ -115,7 +115,7 @@ luabind::object Lua_Expedition::GetMembers(lua_State* L) { auto self = reinterpret_cast(d_); for (const auto& member : self->GetMembers()) { - lua_table[member.name] = member.char_id; + lua_table[member.name] = member.id; } } return lua_table; diff --git a/zone/perl_expedition.cpp b/zone/perl_expedition.cpp index ccba1e6a8..c0c483cfb 100644 --- a/zone/perl_expedition.cpp +++ b/zone/perl_expedition.cpp @@ -266,7 +266,7 @@ XS(XS_Expedition_GetMembers) { for (const auto& member : members) { hv_store(hash, member.name.c_str(), static_cast(member.name.size()), - newSVuv(member.char_id), 0); + newSVuv(member.id), 0); } ST(0) = sv_2mortal(newRV_noinc((SV*)hash)); From 39f5949441421a48f03b5652fb19025175cb9311 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Sun, 9 May 2021 16:39:54 -0400 Subject: [PATCH 033/624] #grid delete was deleting the grids everywhere (#1346) Fix to only delete correct zone grid. --- zone/waypoints.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index 8ed940194..90fc25d1e 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -1029,7 +1029,7 @@ void ZoneDatabase::ModifyGrid(Client *client, bool remove, uint32 id, uint8 type return; } - std::string query = StringFormat("DELETE FROM grid where id=%i", id); + std::string query = StringFormat("DELETE FROM grid where id=%i and zoneid=%i", id, zoneid); auto results = QueryDatabase(query); query = StringFormat("DELETE FROM grid_entries WHERE zoneid = %i AND gridid = %i", zoneid, id); From 26d374d52a113f76f04ca7b9a08dbe321b1b4633 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 10 May 2021 02:05:46 -0400 Subject: [PATCH 034/624] [Quest API] Add IsRaidTarget() to Perl and Lua (#1347) - Add $npc->IsRaidTarget() to Perl. - Add npc:IsRaidTarget() to Lua. --- zone/lua_npc.cpp | 9 ++++++++- zone/lua_npc.h | 1 + zone/perl_npc.cpp | 17 +++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/zone/lua_npc.cpp b/zone/lua_npc.cpp index 32d268fb5..c47870653 100644 --- a/zone/lua_npc.cpp +++ b/zone/lua_npc.cpp @@ -564,6 +564,12 @@ void Lua_NPC::ScaleNPC(uint8 npc_level) self->ScaleNPC(npc_level); } +bool Lua_NPC::IsRaidTarget() +{ + Lua_Safe_Call_Bool(); + return self->IsRaidTarget(); +} + luabind::scope lua_register_npc() { return luabind::class_("NPC") .def(luabind::constructor<>()) @@ -677,7 +683,8 @@ luabind::scope lua_register_npc() { .def("GetRawAC", (int(Lua_NPC::*)(void))&Lua_NPC::GetRawAC) .def("GetAvoidanceRating", &Lua_NPC::GetAvoidanceRating) .def("RecalculateSkills", (void(Lua_NPC::*)(void))&Lua_NPC::RecalculateSkills) - .def("ScaleNPC", (void(Lua_NPC::*)(uint8))&Lua_NPC::ScaleNPC); + .def("ScaleNPC", (void(Lua_NPC::*)(uint8))&Lua_NPC::ScaleNPC) + .def("IsRaidTarget", (bool(Lua_NPC::*)(void))&Lua_NPC::IsRaidTarget); } #endif diff --git a/zone/lua_npc.h b/zone/lua_npc.h index 042a114ef..00c4623cb 100644 --- a/zone/lua_npc.h +++ b/zone/lua_npc.h @@ -137,6 +137,7 @@ public: void SetSimpleRoamBox(float box_size, float move_distance, int move_delay); void RecalculateSkills(); void ScaleNPC(uint8 npc_level); + bool IsRaidTarget(); }; #endif diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index 223207787..99cd83b86 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -1725,6 +1725,22 @@ XS(XS_NPC_ScaleNPC) { XSRETURN_EMPTY; } +XS(XS_NPC_IsRaidTarget); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_IsRaidTarget) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::IsRaidTarget(THIS)"); // @categories Stats and Attributes + { + NPC *THIS; + bool is_raid_target; + VALIDATE_THIS_IS_NPC; + is_raid_target = THIS->IsRaidTarget(); + ST(0) = boolSV(is_raid_target); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + #ifdef __cplusplus extern "C" #endif @@ -1842,6 +1858,7 @@ XS(boot_NPC) { newXSproto(strcpy(buf, "SetSimpleRoamBox"), XS_NPC_SetSimpleRoamBox, file, "$$;$$"); newXSproto(strcpy(buf, "RecalculateSkills"), XS_NPC_RecalculateSkills, file, "$"); newXSproto(strcpy(buf, "ScaleNPC"), XS_NPC_ScaleNPC, file, "$$"); + newXSproto(strcpy(buf, "IsRaidTarget"), XS_NPC_IsRaidTarget, file, "$"); XSRETURN_YES; } From 0ce7c11d36e874ffdba8919c809fb50d375ef33d Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Mon, 10 May 2021 02:07:19 -0400 Subject: [PATCH 035/624] [Expeditions] Track DZ member status in world (#1341) World now caches and tracks member statuses so it can send them to zones that request them on startup. Prior to this the cle would be searched in world for every zone startup caching request, now it's only searched once when a new expedition is created. Bulk loading statuses removed since it would only be needed on world startup now and likely have no clients in the client list anyway. This also lets world choose non-linkdead members on expedition leader changes and better detect when a leader change needs to occur --- common/dynamic_zone_base.h | 2 + common/expedition_base.cpp | 25 +++++++ common/expedition_base.h | 1 + common/servertalk.h | 17 ++--- world/expedition.cpp | 63 ++++++++++++++++- world/expedition.h | 4 +- world/expedition_message.cpp | 58 ++++++---------- world/expedition_message.h | 3 +- world/expedition_state.cpp | 2 + world/zoneserver.cpp | 5 +- zone/expedition.cpp | 127 +++++++++-------------------------- zone/expedition.h | 5 +- zone/worldserver.cpp | 2 +- 13 files changed, 157 insertions(+), 157 deletions(-) diff --git a/common/dynamic_zone_base.h b/common/dynamic_zone_base.h index 075c4e606..e321ce704 100644 --- a/common/dynamic_zone_base.h +++ b/common/dynamic_zone_base.h @@ -25,6 +25,8 @@ struct DynamicZoneMember DynamicZoneMember(uint32_t id, std::string name_, DynamicZoneMemberStatus status_) : id(id), name{std::move(name_)}, status(status_) {} + bool IsOnline() const { return status == DynamicZoneMemberStatus::Online || + status == DynamicZoneMemberStatus::InDynamicZone; } bool IsValid() const { return id != 0 && !name.empty(); } }; diff --git a/common/expedition_base.cpp b/common/expedition_base.cpp index 732a759e7..cb1882dca 100644 --- a/common/expedition_base.cpp +++ b/common/expedition_base.cpp @@ -1,5 +1,6 @@ #include "expedition_base.h" #include "repositories/expeditions_repository.h" +#include "rulesys.h" ExpeditionBase::ExpeditionBase(uint32_t id, const std::string& uuid, const std::string& expedition_name, const DynamicZoneMember& leader, @@ -91,3 +92,27 @@ DynamicZoneMember ExpeditionBase::GetMemberData(const std::string& character_nam } return member_data; } + +bool ExpeditionBase::SetInternalMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status) +{ + if (status == DynamicZoneMemberStatus::InDynamicZone && !RuleB(Expedition, EnableInDynamicZoneStatus)) + { + status = DynamicZoneMemberStatus::Online; + } + + if (character_id == m_leader.id) + { + m_leader.status = status; + } + + auto it = std::find_if(m_members.begin(), m_members.end(), + [&](const DynamicZoneMember& member) { return member.id == character_id; }); + + if (it != m_members.end() && it->status != status) + { + it->status = status; + return true; + } + + return false; +} diff --git a/common/expedition_base.h b/common/expedition_base.h index 25c5cfd33..cdd2382f7 100644 --- a/common/expedition_base.h +++ b/common/expedition_base.h @@ -33,6 +33,7 @@ public: bool HasMember(uint32_t character_id); bool IsEmpty() const { return m_members.empty(); } void RemoveInternalMember(uint32_t character_id); + bool SetInternalMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status); void LoadRepositoryResult(ExpeditionsRepository::ExpeditionWithLeader&& entry); void AddMemberFromRepositoryResult(ExpeditionMembersRepository::MemberWithName&& entry); diff --git a/common/servertalk.h b/common/servertalk.h index 7fbac5f6e..b7c59e123 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -148,7 +148,7 @@ #define ServerOP_ExpeditionMemberChange 0x0404 #define ServerOP_ExpeditionMemberSwap 0x0405 #define ServerOP_ExpeditionMemberStatus 0x0406 -#define ServerOP_ExpeditionGetOnlineMembers 0x0407 +#define ServerOP_ExpeditionGetMemberStatuses 0x0407 #define ServerOP_ExpeditionDzAddPlayer 0x0408 #define ServerOP_ExpeditionDzMakeLeader 0x0409 #define ServerOP_ExpeditionCharacterLockout 0x040d @@ -159,7 +159,6 @@ #define ServerOP_ExpeditionMembersRemoved 0x0412 #define ServerOP_ExpeditionLockoutDuration 0x0414 #define ServerOP_ExpeditionExpireWarning 0x0416 -#define ServerOP_ExpeditionChooseNewLeader 0x0417 #define ServerOP_DzAddRemoveCharacter 0x0450 #define ServerOP_DzRemoveAllCharacters 0x0451 @@ -2035,19 +2034,15 @@ struct ServerExpeditionMemberStatus_Struct { uint32 character_id; }; -struct ServerExpeditionCharacterEntry_Struct { - uint32 expedition_id; +struct ServerExpeditionMemberStatusEntry_Struct { uint32 character_id; - uint32 character_zone_id; - uint16 character_instance_id; - uint8 character_online; // 0: offline 1: online + uint8 online_status; // 0: unknown 1: Online 2: Offline 3: In Dynamic Zone 4: Link Dead }; -struct ServerExpeditionCharacters_Struct { - uint32 sender_zone_id; - uint16 sender_instance_id; +struct ServerExpeditionMemberStatuses_Struct { + uint32 expedition_id; uint32 count; - ServerExpeditionCharacterEntry_Struct entries[0]; + ServerExpeditionMemberStatusEntry_Struct entries[0]; }; struct ServerExpeditionLockout_Struct { diff --git a/world/expedition.cpp b/world/expedition.cpp index fceb6c7dc..a68ddd093 100644 --- a/world/expedition.cpp +++ b/world/expedition.cpp @@ -62,10 +62,8 @@ void Expedition::ChooseNewLeader() return; } - // we don't track expedition member status in world so may choose a linkdead member - // this is fine since it will trigger another change when that member goes offline auto it = std::find_if(m_members.begin(), m_members.end(), [&](const DynamicZoneMember& member) { - if (member.id != m_leader.id) { + if (member.id != m_leader.id && member.IsOnline()) { auto member_cle = client_list.FindCLEByCharacterID(member.id); return (member_cle && member_cle->GetOnline() == CLE_Status::InZone); } @@ -171,3 +169,62 @@ bool Expedition::Process() return false; } + +void Expedition::UpdateMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status) +{ + SetInternalMemberStatus(character_id, status); + + // any member status update will trigger a leader fix if leader was offline + if (m_leader.status == DynamicZoneMemberStatus::Offline) + { + ChooseNewLeader(); + } +} + +void Expedition::SendZoneMemberStatuses(uint16_t zone_id, uint16_t instance_id) +{ + const auto& members = GetMembers(); + + uint32_t members_count = static_cast(members.size()); + uint32_t entries_size = sizeof(ServerExpeditionMemberStatusEntry_Struct) * members_count; + uint32_t pack_size = sizeof(ServerExpeditionMemberStatuses_Struct) + entries_size; + auto pack = std::make_unique(ServerOP_ExpeditionGetMemberStatuses, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + buf->expedition_id = GetID(); + buf->count = members_count; + + for (int i = 0; i < members.size(); ++i) + { + buf->entries[i].character_id = members[i].id; + buf->entries[i].online_status = static_cast(members[i].status); + } + + zoneserver_list.SendPacket(zone_id, instance_id, pack.get()); +} + +void Expedition::CacheMemberStatuses() +{ + // called when a new expedition is cached to fill member statuses + std::string zone_name{}; + std::vector all_clients; + all_clients.reserve(client_list.GetClientCount()); + client_list.GetClients(zone_name.c_str(), all_clients); + + for (const auto& member : m_members) + { + auto it = std::find_if(all_clients.begin(), all_clients.end(), + [&](const ClientListEntry* cle) { return (cle && cle->CharID() == member.id); }); + + auto status = DynamicZoneMemberStatus::Offline; + if (it != all_clients.end()) + { + status = DynamicZoneMemberStatus::Online; + if (GetDynamicZone().IsSameDz((*it)->zone(), (*it)->instance())) + { + status = DynamicZoneMemberStatus::InDynamicZone; + } + } + + SetInternalMemberStatus(member.id, status); + } +} diff --git a/world/expedition.h b/world/expedition.h index db0647093..119f78685 100644 --- a/world/expedition.h +++ b/world/expedition.h @@ -32,16 +32,18 @@ public: Expedition(); void RemoveMember(uint32_t character_id); + void CacheMemberStatuses(); void CheckExpireWarning(); void CheckLeader(); void ChooseNewLeader(); DynamicZone& GetDynamicZone() { return m_dynamic_zone; } bool Process(); - + void SendZoneMemberStatuses(uint16_t zone_id, uint16_t instance_id); void SendZonesExpeditionDeleted(); void SendZonesExpireWarning(uint32_t minutes_remaining); void SetDynamicZone(DynamicZone&& dz); bool SetNewLeader(const DynamicZoneMember& member); + void UpdateMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status); private: void SendZonesLeaderChanged(); diff --git a/world/expedition_message.cpp b/world/expedition_message.cpp index 999dfd44a..36eee5ceb 100644 --- a/world/expedition_message.cpp +++ b/world/expedition_message.cpp @@ -35,11 +35,6 @@ void ExpeditionMessage::HandleZoneMessage(ServerPacket* pack) { switch (pack->opcode) { - case ServerOP_ExpeditionChooseNewLeader: - { - ExpeditionMessage::ChooseNewLeader(pack); - break; - } case ServerOP_ExpeditionCreate: { auto buf = reinterpret_cast(pack->pBuffer); @@ -69,9 +64,21 @@ void ExpeditionMessage::HandleZoneMessage(ServerPacket* pack) zoneserver_list.SendPacket(pack); break; } - case ServerOP_ExpeditionGetOnlineMembers: + case ServerOP_ExpeditionMemberStatus: { - ExpeditionMessage::GetOnlineMembers(pack); + auto buf = reinterpret_cast(pack->pBuffer); + auto expedition = expedition_state.GetExpedition(buf->expedition_id); + if (expedition) + { + auto status = static_cast(buf->status); + expedition->UpdateMemberStatus(buf->character_id, status); + } + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_ExpeditionGetMemberStatuses: + { + ExpeditionMessage::GetMemberStatuses(pack); break; } case ServerOP_ExpeditionDzAddPlayer: @@ -157,31 +164,14 @@ void ExpeditionMessage::MakeLeader(ServerPacket* pack) } } -void ExpeditionMessage::GetOnlineMembers(ServerPacket* pack) +void ExpeditionMessage::GetMemberStatuses(ServerPacket* pack) { - auto buf = reinterpret_cast(pack->pBuffer); - - // not efficient but only requested during caching - char zone_name[64] = {0}; - std::vector all_clients; - all_clients.reserve(client_list.GetClientCount()); - client_list.GetClients(zone_name, all_clients); - - for (uint32_t i = 0; i < buf->count; ++i) + auto buf = reinterpret_cast(pack->pBuffer); + auto expedition = expedition_state.GetExpedition(buf->expedition_id); + if (expedition) { - auto it = std::find_if(all_clients.begin(), all_clients.end(), [&](const ClientListEntry* cle) { - return (cle && cle->CharID() == buf->entries[i].character_id); - }); - - if (it != all_clients.end()) - { - buf->entries[i].character_zone_id = (*it)->zone(); - buf->entries[i].character_instance_id = (*it)->instance(); - buf->entries[i].character_online = true; - } + expedition->SendZoneMemberStatuses(buf->sender_zone_id, buf->sender_instance_id); } - - zoneserver_list.SendPacket(buf->sender_zone_id, buf->sender_instance_id, pack); } void ExpeditionMessage::SaveInvite(ServerPacket* pack) @@ -211,13 +201,3 @@ void ExpeditionMessage::RequestInvite(ServerPacket* pack) } } } - -void ExpeditionMessage::ChooseNewLeader(ServerPacket* pack) -{ - auto buf = reinterpret_cast(pack->pBuffer); - auto expedition = expedition_state.GetExpedition(buf->expedition_id); - if (expedition) - { - expedition->ChooseNewLeader(); - } -} diff --git a/world/expedition_message.h b/world/expedition_message.h index 8789577da..135ef0cba 100644 --- a/world/expedition_message.h +++ b/world/expedition_message.h @@ -26,8 +26,7 @@ class ServerPacket; namespace ExpeditionMessage { void AddPlayer(ServerPacket* pack); - void ChooseNewLeader(ServerPacket* pack); - void GetOnlineMembers(ServerPacket* pack); + void GetMemberStatuses(ServerPacket* pack); void HandleZoneMessage(ServerPacket* pack); void MakeLeader(ServerPacket* pack); void RequestInvite(ServerPacket* pack); diff --git a/world/expedition_state.cpp b/world/expedition_state.cpp index 33a92d61c..c1d520c69 100644 --- a/world/expedition_state.cpp +++ b/world/expedition_state.cpp @@ -110,6 +110,8 @@ void ExpeditionState::CacheExpeditions( } } + expedition->CacheMemberStatuses(); + m_expeditions.emplace_back(std::move(expedition)); } } diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 9a319774a..4e37b1d7e 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -1366,17 +1366,16 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { case ServerOP_ExpeditionLockout: case ServerOP_ExpeditionLockoutDuration: case ServerOP_ExpeditionLockState: - case ServerOP_ExpeditionMemberStatus: case ServerOP_ExpeditionReplayOnJoin: case ServerOP_ExpeditionExpireWarning: { zoneserver_list.SendPacket(pack); break; } - case ServerOP_ExpeditionChooseNewLeader: case ServerOP_ExpeditionCreate: - case ServerOP_ExpeditionGetOnlineMembers: + case ServerOP_ExpeditionGetMemberStatuses: case ServerOP_ExpeditionMemberChange: + case ServerOP_ExpeditionMemberStatus: case ServerOP_ExpeditionMemberSwap: case ServerOP_ExpeditionMembersRemoved: case ServerOP_ExpeditionDzAddPlayer: diff --git a/zone/expedition.cpp b/zone/expedition.cpp index c3ef81e6a..7d404ff03 100644 --- a/zone/expedition.cpp +++ b/zone/expedition.cpp @@ -167,8 +167,6 @@ void Expedition::CacheExpeditions( auto expedition_members = ExpeditionMembersRepository::GetWithNames(database, expedition_ids); auto expedition_lockouts = ExpeditionLockoutsRepository::GetWithTimestamp(database, expedition_ids); - std::vector> expedition_character_ids; // for online status request - for (auto& entry : expedition_entries) { auto expedition = std::make_unique(); @@ -189,7 +187,6 @@ void Expedition::CacheExpeditions( if (member.expedition_id == expedition->GetID()) { expedition->AddMemberFromRepositoryResult(std::move(member)); - expedition_character_ids.emplace_back(expedition->GetID(), member.character_id); } } @@ -210,12 +207,11 @@ void Expedition::CacheExpeditions( } } + expedition->SendWorldExpeditionUpdate(ServerOP_ExpeditionGetMemberStatuses); + auto inserted = zone->expedition_cache.emplace(entry.id, std::move(expedition)); inserted.first->second->SendUpdatesToZoneMembers(); } - - // ask world for online members from all cached expeditions at once - Expedition::SendWorldGetOnlineMembers(expedition_character_ids); } void Expedition::CacheFromDatabase(uint32_t expedition_id) @@ -521,19 +517,12 @@ void Expedition::SetMemberStatus(Client* client, DynamicZoneMemberStatus status) { if (client) { - UpdateMemberStatus(client->CharacterID(), status); + SendMemberStatusToZoneMembers(client->CharacterID(), status); SendWorldMemberStatus(client->CharacterID(), status); - - // world could detect this itself but it'd have to process member status updates - // a member coming online will trigger a leader change if all members were offline - if (m_leader.status == DynamicZoneMemberStatus::Offline) - { - SendWorldExpeditionUpdate(ServerOP_ExpeditionChooseNewLeader); - } } } -void Expedition::UpdateMemberStatus(uint32_t update_member_id, DynamicZoneMemberStatus status) +void Expedition::SendMemberStatusToZoneMembers(uint32_t update_member_id, DynamicZoneMemberStatus status) { auto member_data = GetMemberData(update_member_id); if (!member_data.IsValid()) @@ -541,35 +530,19 @@ void Expedition::UpdateMemberStatus(uint32_t update_member_id, DynamicZoneMember return; } - if (status == DynamicZoneMemberStatus::InDynamicZone && !RuleB(Expedition, EnableInDynamicZoneStatus)) - { - status = DynamicZoneMemberStatus::Online; - } - - if (update_member_id == m_leader.id) - { - m_leader.status = status; - } - // if zone already had this member status cached avoid packet update to clients - if (member_data.status == status) + bool changed = SetInternalMemberStatus(update_member_id, status); + if (changed) { - return; - } - - auto outapp_member_status = CreateMemberListStatusPacket(member_data.name, status); - - for (auto& member : m_members) - { - if (member.id == update_member_id) + member_data = GetMemberData(update_member_id); // rules may override status + auto outapp_member_status = CreateMemberListStatusPacket(member_data.name, member_data.status); + for (auto& member : m_members) { - member.status = status; - } - - Client* member_client = entity_list.GetClientByCharID(member.id); - if (member_client) - { - member_client->QueuePacket(outapp_member_status.get()); + Client* member_client = entity_list.GetClientByCharID(member.id); + if (member_client) + { + member_client->QueuePacket(outapp_member_status.get()); + } } } } @@ -1095,6 +1068,8 @@ void Expedition::ProcessMakeLeader(Client* old_leader_client, Client* new_leader void Expedition::ProcessMemberAdded(const std::string& char_name, uint32_t added_char_id) { + AddInternalMember({ added_char_id, char_name, DynamicZoneMemberStatus::Online }); + // adds the member to this expedition and notifies both leader and new member Client* leader_client = entity_list.GetClientByCharID(m_leader.id); if (leader_client) @@ -1102,18 +1077,16 @@ void Expedition::ProcessMemberAdded(const std::string& char_name, uint32_t added leader_client->MessageString(Chat::Yellow, EXPEDITION_MEMBER_ADDED, char_name.c_str(), m_expedition_name.c_str()); } - AddInternalMember({ added_char_id, char_name, DynamicZoneMemberStatus::Online }); - Client* member_client = entity_list.GetClientByCharID(added_char_id); if (member_client) { member_client->SetExpeditionID(GetID()); member_client->SendDzCompassUpdate(); - SendClientExpeditionInfo(member_client); + member_client->QueuePacket(CreateInfoPacket().get()); member_client->MessageString(Chat::Yellow, EXPEDITION_MEMBER_ADDED, char_name.c_str(), m_expedition_name.c_str()); } - SendNewMemberAddedToZoneMembers(char_name); + SendMemberListToZoneMembers(); } void Expedition::ProcessMemberRemoved(const std::string& removed_char_name, uint32_t removed_char_id) @@ -1271,23 +1244,16 @@ void Expedition::AddLockoutClients( } } -void Expedition::SendNewMemberAddedToZoneMembers(const std::string& added_name) +void Expedition::SendMemberListToZoneMembers() { - // live only sends MemberListName when members are added from a swap, otherwise - // it sends expedition info (unnecessary) and the full member list - // we send a full member list update for both cases since MemberListName adds as - // "unknown" status (either due to unknown packet fields or future client change) auto outapp_members = CreateMemberListPacket(false); for (const auto& member : m_members) { - if (member.name != added_name) // new member already updated + Client* member_client = entity_list.GetClientByCharID(member.id); + if (member_client) { - Client* member_client = entity_list.GetClientByCharID(member.id); - if (member_client) - { - member_client->QueuePacket(outapp_members.get()); - } + member_client->QueuePacket(outapp_members.get()); } } } @@ -1562,29 +1528,6 @@ void Expedition::SendWorldSettingChanged(uint16_t server_opcode, bool setting_va worldserver.SendPacket(pack.get()); } -void Expedition::SendWorldGetOnlineMembers( - const std::vector>& expedition_character_ids) -{ - // request online status of characters - uint32_t count = static_cast(expedition_character_ids.size()); - uint32_t entries_size = sizeof(ServerExpeditionCharacterEntry_Struct) * count; - uint32_t pack_size = sizeof(ServerExpeditionCharacters_Struct) + entries_size; - auto pack = std::make_unique(ServerOP_ExpeditionGetOnlineMembers, pack_size); - auto buf = reinterpret_cast(pack->pBuffer); - buf->sender_zone_id = zone ? zone->GetZoneID() : 0; - buf->sender_instance_id = zone ? zone->GetInstanceID() : 0; - buf->count = count; - for (uint32_t i = 0; i < buf->count; ++i) - { - buf->entries[i].expedition_id = expedition_character_ids[i].first; - buf->entries[i].character_id = expedition_character_ids[i].second; - buf->entries[i].character_zone_id = 0; - buf->entries[i].character_instance_id = 0; - buf->entries[i].character_online = false; - } - worldserver.SendPacket(pack.get()); -} - void Expedition::SendWorldCharacterLockout( uint32_t character_id, const ExpeditionLockoutTimer& lockout, bool remove) { @@ -1794,7 +1737,8 @@ void Expedition::HandleWorldMessage(ServerPacket* pack) auto expedition = Expedition::FindCachedExpeditionByID(buf->expedition_id); if (expedition) { - expedition->UpdateMemberStatus(buf->character_id, static_cast(buf->status)); + auto status = static_cast(buf->status); + expedition->SendMemberStatusToZoneMembers(buf->character_id, status); } } break; @@ -1825,24 +1769,19 @@ void Expedition::HandleWorldMessage(ServerPacket* pack) } break; } - case ServerOP_ExpeditionGetOnlineMembers: + case ServerOP_ExpeditionGetMemberStatuses: { - // reply from world for online member statuses request (for multiple expeditions) - auto buf = reinterpret_cast(pack->pBuffer); - for (uint32_t i = 0; i < buf->count; ++i) + // reply from world for online member statuses request + auto buf = reinterpret_cast(pack->pBuffer); + auto expedition = Expedition::FindCachedExpeditionByID(buf->expedition_id); + if (expedition) { - auto member = reinterpret_cast(&buf->entries[i]); - auto expedition = Expedition::FindCachedExpeditionByID(member->expedition_id); - if (expedition) + for (uint32_t i = 0; i < buf->count; ++i) { - auto is_online = member->character_online; - auto status = is_online ? DynamicZoneMemberStatus::Online : DynamicZoneMemberStatus::Offline; - if (is_online && expedition->GetDynamicZone().IsInstanceID(member->character_instance_id)) - { - status = DynamicZoneMemberStatus::InDynamicZone; - } - expedition->UpdateMemberStatus(member->character_id, status); + auto status = static_cast(buf->entries[i].online_status); + expedition->SetInternalMemberStatus(buf->entries[i].character_id, status); } + expedition->SendMemberListToZoneMembers(); } break; } diff --git a/zone/expedition.h b/zone/expedition.h index adce73fcd..f5e7834bd 100644 --- a/zone/expedition.h +++ b/zone/expedition.h @@ -128,7 +128,6 @@ public: private: static void CacheExpeditions(std::vector&& expeditions); - static void SendWorldGetOnlineMembers(const std::vector>& expedition_character_ids); static void SendWorldCharacterLockout(uint32_t character_id, const ExpeditionLockoutTimer& lockout, bool remove); void AddLockout(const ExpeditionLockoutTimer& lockout, bool members_only = false); @@ -148,8 +147,9 @@ private: Client* client, const std::string& inviter_name, const std::string& swap_remove_name); void SendLeaderMessage(Client* leader_client, uint16_t chat_type, uint32_t string_id, const std::initializer_list& args = {}); + void SendMemberListToZoneMembers(); + void SendMemberStatusToZoneMembers(uint32_t update_character_id, DynamicZoneMemberStatus status); void SendMembersExpireWarning(uint32_t minutes); - void SendNewMemberAddedToZoneMembers(const std::string& added_name); void SendUpdatesToZoneMembers(bool clear = false, bool message_on_clear = true); void SendCompassUpdateToZoneMembers(); void SendWorldExpeditionUpdate(uint16_t server_opcode); @@ -167,7 +167,6 @@ private: void SetDynamicZone(DynamicZone&& dz); void TryAddClient(Client* add_client, const std::string& inviter_name, const std::string& swap_remove_name, Client* leader_client = nullptr); - void UpdateMemberStatus(uint32_t update_character_id, DynamicZoneMemberStatus status); std::unique_ptr CreateExpireWarningPacket(uint32_t minutes_remaining); std::unique_ptr CreateInfoPacket(bool clear = false); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 65f43676b..545625d0d 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -2905,7 +2905,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) case ServerOP_ExpeditionMemberStatus: case ServerOP_ExpeditionMembersRemoved: case ServerOP_ExpeditionReplayOnJoin: - case ServerOP_ExpeditionGetOnlineMembers: + case ServerOP_ExpeditionGetMemberStatuses: case ServerOP_ExpeditionDzAddPlayer: case ServerOP_ExpeditionDzMakeLeader: case ServerOP_ExpeditionCharacterLockout: From 843aac631b194a9ea99e123ff2468b93cad44925 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Mon, 10 May 2021 02:10:13 -0400 Subject: [PATCH 036/624] [Bug Fix] Add debugging and fix edge case where no target for aggro mob (#1344) Co-authored-by: Noudess --- zone/npc.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/zone/npc.cpp b/zone/npc.cpp index 2a90bf360..9bea0ec67 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -916,6 +916,10 @@ bool NPC::Process() if (assist_timer.Check() && IsEngaged() && !Charmed() && !HasAssistAggro() && NPCAssistCap() < RuleI(Combat, NPCAssistCap)) { + // Some cases like flash of light used for aggro haven't set target + if (!GetTarget()) { + SetTarget(hate_list.GetEntWithMostHateOnList(this)); + } AIYellForHelp(this, GetTarget()); if (NPCAssistCap() > 0 && !assist_cap_timer.Enabled()) assist_cap_timer.Start(RuleI(Combat, NPCAssistCapTimer)); @@ -3217,6 +3221,11 @@ bool NPC::AICheckCloseBeneficialSpells( */ void NPC::AIYellForHelp(Mob *sender, Mob *attacker) { + LogAIYellForHelp("Mob[{}] Target[{}]", + (sender == nullptr ? "NULL MOB" : GetCleanName()), + (attacker == nullptr ? "NULL TARGET" : attacker->GetCleanName()) + ); + if (!sender || !attacker) { return; } @@ -3225,11 +3234,14 @@ void NPC::AIYellForHelp(Mob *sender, Mob *attacker) * If we dont have a faction set, we're gonna be indiff to everybody */ if (sender->GetPrimaryFaction() == 0) { + LogAIYellForHelp("No Primary Faction"); return; } - if (sender->HasAssistAggro()) + if (sender->HasAssistAggro()) { + LogAIYellForHelp("I have assist aggro"); return; + } LogAIYellForHelp( "NPC [{}] ID [{}] is starting to scan", From 0758250ad129ab747a2f1931df0f1b4a0967d819 Mon Sep 17 00:00:00 2001 From: JeffyW Date: Sun, 9 May 2021 23:15:11 -0700 Subject: [PATCH 037/624] [Installer] $ENV support, formatting, and optimizations (#1340) * [Installer] $ENV support Add support for passing in installation variables instead of install_variables.txt. In particular: - EQEMU_SERVER_SKIP_UPDATE - EQEMU_SERVER_SKIP_MAPS_UPDATE Both work the same as files with the same name. - MYSQL_DATABASE - MYSQL_USER - MYSQL_PASSWORD All get read into the same $installation_variables collection, and are still overridden by any values found in installation_variables.txt - MYSQL_HOST Added to the list of $installation_variables, supported in installation_variables.txt, and overrides the default host for where the database lives (allowing it to be a separate container) - MYSQL_ROOT_PASSWORD If set, will use "root" as the user for DROP/CREATE DATABASE calls, to prevent requiring the "eq" user requiring more permission than is given by default with the MySql/MariaDB containers. * [Installer] new_server_with_bots Enabling bots requires both updated binaries and some database scripts. Fetching source and compiling the code is the longest part of setting up a new server. If you know you want bots, rather than having to do it twice, we can just accept an option to do it all at the same time. * [Installer] Git/Source/Build optimizations Source code is the same for bot/non-bot, so switch to a common directory for source and separate directories for build. If the source directory already exists with a .git subfolder, rather than attempt to re-clone, instead do a git pull to still get the latest code. Helps when using a shared volume but rebuilding containers. * [Installer] Formatting / Cleanup Fixed some indentation, added an existing option to the utility script output, removed some excess newlines, and moved a debug output to help identifying errors. --- utils/scripts/eqemu_server.pl | 172 ++++++++++++++++++++++------------ 1 file changed, 111 insertions(+), 61 deletions(-) diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl index 83688ad2a..da79fc448 100755 --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -62,7 +62,8 @@ if (-e "skip_internet_connection_check.txt") { # skip self update ############################################# my $skip_self_update_check = 0; -if (-e "eqemu_server_skip_update.txt") { +if (-e "eqemu_server_skip_update.txt" || defined($ENV{'EQEMU_SERVER_SKIP_UPDATE'})) { + print "[Info] Skipping self check\n"; $skip_self_update_check = 1; } @@ -70,7 +71,8 @@ if (-e "eqemu_server_skip_update.txt") { # skip maps update ############################################# my $skip_self_maps_update_check = 0; -if (-e "eqemu_server_skip_maps_update.txt") { +if (-e "eqemu_server_skip_maps_update.txt" || defined($ENV{'EQEMU_SERVER_SKIP_MAPS_UPDATE'})) { + print "[Info] Skipping maps update\n"; $skip_self_maps_update_check = 1; } @@ -106,7 +108,7 @@ if (-e "eqemu_update.pl") { print "[Info] For EQEmu Server management utilities - run eqemu_server.pl\n" if $ARGV[0] eq "ran_from_world"; my $skip_checks = 0; -if ($ARGV[0] && $ARGV[0] eq "new_server") { +if ($ARGV[0] && ($ARGV[0] eq "new_server" || $ARGV[0] eq "new_server_with_bots")) { $skip_checks = 1; } @@ -238,10 +240,11 @@ sub show_install_summary_info } if ($OS eq "Linux") { print "[Install] Linux Utility Scripts:\n"; - print " - server_start.sh Starts EQEmu server (Quiet) with 30 dynamic zones, UCS & Queryserv, dynamic zones\n"; - print " - server_start_dev.sh Starts EQEmu server with 10 dynamic zones, UCS & Queryserv, dynamic zones all verbose\n"; - print " - server_stop.sh Stops EQEmu Server (No warning)\n"; - print " - server_status.sh Prints the status of the EQEmu Server processes\n"; + print " - server_start.sh Starts EQEmu server (Quiet) with 30 dynamic zones, UCS & Queryserv, dynamic zones\n"; + print " - server_start_with_login.sh Starts EQEmu server (Quiet) with 30 dynamic zones, UCS & Queryserv, dynamic zones\n"; + print " - server_start_dev.sh Starts EQEmu server with 10 dynamic zones, UCS & Queryserv, dynamic zones all verbose\n"; + print " - server_stop.sh Stops EQEmu Server (No warning)\n"; + print " - server_status.sh Prints the status of the EQEmu Server processes\n"; } print "[Configure] eqemu_config.json Edit to change server settings and name\n"; @@ -251,6 +254,7 @@ sub show_install_summary_info sub new_server { + $build_options = $_[0]; $file_count = 0; opendir(DIR, ".") or die $!; while (my $file = readdir(DIR)) { @@ -265,9 +269,7 @@ sub new_server exit; } - if (-e "install_variables.txt" || -e "../install_variables.txt") { - get_installation_variables(); - } + get_installation_variables(); while (1) { @@ -307,7 +309,7 @@ sub new_server if ($mysql_pass == 1) { - if ((!-e "install_variables.txt" && !-e "../install_variables.txt")) { + if ($database_name eq "" && !-e "install_variables.txt" && !-e "../install_variables.txt") { print "[New Server] Success! We have a database connection\n"; check_for_input("Specify a NEW database name that PEQ will be installed to: "); @@ -323,11 +325,12 @@ sub new_server } analytics_insertion("new_server::install", $database_name); - if ($OS eq "Linux") { - build_linux_source("login"); - } + # This shouldn't be necessary, as we call do_linux_login_server_setup as the last step in do_installer_routines() + # if ($OS eq "Linux") { + # build_linux_source("login"); + # } - do_installer_routines(); + do_installer_routines($build_options); if ($OS eq "Linux") { print `chmod 755 *.sh`; @@ -415,7 +418,6 @@ sub check_xml_to_json_conversion sub build_linux_source { - $build_options = $_[0]; $cmake_options = ""; @@ -434,7 +436,9 @@ sub build_linux_source } } my $eqemu_server_directory = "/home/eqemu"; - my $source_dir = $eqemu_server_directory . '/' . $last_directory . '_source' . $source_folder_post_fix; + # source between bots and not is the same, just different build results, so use the same source folder, different build folders + my $source_dir = $eqemu_server_directory . '/' . $last_directory . '_source'; + my $build_dir = $eqemu_server_directory . '/' . $last_directory . '_build' . $source_folder_post_fix; $current_directory = trim($current_directory); @@ -446,22 +450,22 @@ sub build_linux_source chdir($source_dir); - print `git clone https://github.com/EQEmu/Server.git`; + if (!-d "$source_dir/.git") { + print `git clone --recurse-submodules https://github.com/EQEmu/Server.git $source_dir`; + } + else { + print `git pull --recurse-submodules`; + } - mkdir($source_dir . "/Server/build") if (!-e $source_dir . "/Server/build"); - chdir($source_dir . "/Server"); - - print `git submodule init`; - print `git submodule update`; - - chdir($source_dir . "/Server/build"); + mkdir($build_dir) if (!-e $build_dir); + chdir($build_dir); print "Generating CMake build files...\n"; if ($os_flavor eq "fedora_core") { - print `cmake $cmake_options -DEQEMU_BUILD_LOGIN=ON -DEQEMU_BUILD_LUA=ON -DLUA_INCLUDE_DIR=/usr/include/lua-5.1/ -G "Unix Makefiles" ..`; + print `cmake $cmake_options -DEQEMU_BUILD_LOGIN=ON -DEQEMU_BUILD_LUA=ON -DLUA_INCLUDE_DIR=/usr/include/lua-5.1/ -G "Unix Makefiles" $source_dir`; } else { - print `cmake $cmake_options -DEQEMU_BUILD_LOGIN=ON -DEQEMU_BUILD_LUA=ON -G "Unix Makefiles" ..`; + print `cmake $cmake_options -DEQEMU_BUILD_LOGIN=ON -DEQEMU_BUILD_LUA=ON -G "Unix Makefiles" $source_dir`; } print "Building EQEmu Server code. This will take a while."; @@ -470,21 +474,22 @@ sub build_linux_source chdir($current_directory); - print `ln -s -f $source_dir/Server/build/bin/eqlaunch .`; - print `ln -s -f $source_dir/Server/build/bin/export_client_files .`; - print `ln -s -f $source_dir/Server/build/bin/import_client_files .`; - print `ln -s -f $source_dir/Server/build/bin/libcommon.a .`; - print `ln -s -f $source_dir/Server/build/bin/libluabind.a .`; - print `ln -s -f $source_dir/Server/build/bin/queryserv .`; - print `ln -s -f $source_dir/Server/build/bin/shared_memory .`; - print `ln -s -f $source_dir/Server/build/bin/ucs .`; - print `ln -s -f $source_dir/Server/build/bin/world .`; - print `ln -s -f $source_dir/Server/build/bin/zone .`; - print `ln -s -f $source_dir/Server/build/bin/loginserver .`; + print `ln -s -f $build_dir/bin/eqlaunch .`; + print `ln -s -f $build_dir/bin/export_client_files .`; + print `ln -s -f $build_dir/bin/import_client_files .`; + print `ln -s -f $build_dir/bin/libcommon.a .`; + print `ln -s -f $build_dir/bin/libluabind.a .`; + print `ln -s -f $build_dir/bin/queryserv .`; + print `ln -s -f $build_dir/bin/shared_memory .`; + print `ln -s -f $build_dir/bin/ucs .`; + print `ln -s -f $build_dir/bin/world .`; + print `ln -s -f $build_dir/bin/zone .`; + print `ln -s -f $build_dir/bin/loginserver .`; } sub do_installer_routines { + $build_options = $_[0]; print "[Install] EQEmu Server Installer... LOADING... PLEASE WAIT...\n"; #::: Make some local server directories... @@ -517,9 +522,25 @@ sub do_installer_routines fetch_utility_scripts(); #::: Database Routines + $root_user = $user; + $root_password = $pass; print "[Database] Creating Database '" . $db_name . "'\n"; - print `"$path" --host $host --user $user --password="$pass" -N -B -e "DROP DATABASE IF EXISTS $db_name;"`; - print `"$path" --host $host --user $user --password="$pass" -N -B -e "CREATE DATABASE $db_name"`; + if (defined($ENV{'MYSQL_ROOT_PASSWORD'})) + { + # In the case that the user doesn't have privileges to create databases, support passing in the root password during setup + print "[Database] Using 'root' for database management.\n"; + $root_user = "root"; + $root_password = $ENV{'MYSQL_ROOT_PASSWORD'}; + } + print `"$path" --host $host --user $root_user --password="$root_password" -N -B -e "DROP DATABASE IF EXISTS $db_name;"`; + print `"$path" --host $host --user $root_user --password="$root_password" -N -B -e "CREATE DATABASE $db_name"`; + if (defined($ENV{'MYSQL_ROOT_PASSWORD'})) + { + # If we used root, make sure $user has permissions on db + print "[Database] Assigning ALL PRIVILEGES to $user on $db_name.\n"; + print `"$path" --host $host --user $root_user --password="$root_password" -N -B -e "GRANT ALL PRIVILEGES ON $db_name.* TO '$user.%'"`; + print `"$path" --host $host --user $root_user --password="$root_password" -N -B -e "FLUSH PRIVILEGES"`; + } my $world_path = "world"; if (-e "bin/world") { @@ -548,6 +569,11 @@ sub do_installer_routines print "[Database] Fetching and Applying Latest Database Updates...\n"; main_db_management(); + # if bots + if ($build_options =~ /bots/i) { + bots_db_management(); + } + remove_duplicate_rule_values(); if ($OS eq "Windows") { @@ -555,7 +581,7 @@ sub do_installer_routines do_windows_login_server_setup(); } if ($OS eq "Linux") { - do_linux_login_server_setup(); + do_linux_login_server_setup($build_options); } } @@ -775,6 +801,12 @@ sub do_self_update_check_routine sub get_installation_variables { + # Read installation variables from the ENV if set, but override them with install_variables.txt + if ($ENV{"MYSQL_HOST"}) { $installation_variables{"mysql_host"} = $ENV{"MYSQL_HOST"}; } + if ($ENV{"MYSQL_DATABASE"}) { $installation_variables{"mysql_eqemu_db_name"} = $ENV{"MYSQL_DATABASE"}; } + if ($ENV{"MYSQL_USER"}) { $installation_variables{"mysql_eqemu_user"} = $ENV{"MYSQL_USER"} } + if ($ENV{"MYSQL_PASSWORD"}) { $installation_variables{"mysql_eqemu_password"} = $ENV{"MYSQL_PASSWORD"} } + #::: Fetch installation variables before building the config if ($OS eq "Linux") { if (-e "../install_variables.txt") { @@ -808,9 +840,9 @@ sub do_install_config_json my $content; open(my $fh, '<', "eqemu_config_template.json") or die "cannot open file $filename"; { - local $/; - $content = <$fh>; -} + local $/; + $content = <$fh>; + } close($fh); $config = $json->decode($content); @@ -826,9 +858,18 @@ sub do_install_config_json $db_name = "peq"; } + if ($installation_variables{"mysql_host"}) { + $host = $installation_variables{"mysql_host"}; + } + else { + $host = "127.0.0.1"; + } + + $config->{"server"}{"database"}{"host"} = $host; $config->{"server"}{"database"}{"username"} = $installation_variables{"mysql_eqemu_user"}; $config->{"server"}{"database"}{"password"} = $installation_variables{"mysql_eqemu_password"}; $config->{"server"}{"database"}{"db"} = $db_name; + $config->{"server"}{"qsdatabase"}{"host"} = $host; $config->{"server"}{"qsdatabase"}{"username"} = $installation_variables{"mysql_eqemu_user"}; $config->{"server"}{"qsdatabase"}{"password"} = $installation_variables{"mysql_eqemu_password"}; $config->{"server"}{"qsdatabase"}{"db"} = $db_name; @@ -854,9 +895,9 @@ sub do_install_config_login_json my $content; open(my $fh, '<', "login_template.json") or die "cannot open file $filename"; { - local $/; - $content = <$fh>; -} + local $/; + $content = <$fh>; + } close($fh); $config = $json->decode($content); @@ -868,7 +909,14 @@ sub do_install_config_login_json $db_name = "peq"; } - $config->{"database"}{"host"} = "127.0.0.1"; + if ($installation_variables{"mysql_host"}) { + $host = $installation_variables{"mysql_host"}; + } + else { + $host = "127.0.0.1"; + } + + $config->{"database"}{"host"} = $host; $config->{"database"}{"user"} = $installation_variables{"mysql_eqemu_user"}; $config->{"database"}{"password"} = $installation_variables{"mysql_eqemu_password"}; $config->{"database"}{"db"} = $db_name; @@ -1087,6 +1135,10 @@ sub show_menu_prompt new_server(); $dc = 1; } + elsif ($input eq "new_server_with_bots") { + new_server("bots"); + $dc = 1; + } elsif ($input eq "setup_bots") { setup_bots(); $dc = 1; @@ -1154,11 +1206,12 @@ sub print_main_menu print "\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"; print ">>> EQEmu Server Main Menu >>>>>>>>>>>>\n"; print ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n"; - print " [database] Enter database management menu \n"; - print " [assets] Manage server assets \n"; - print " [new_server] New folder EQEmu/PEQ install - Assumes MySQL/Perl installed \n"; - print " [setup_bots] Enables bots on server - builds code and database requirements \n"; - print " [conversions] Routines used for conversion of scripts/data \n"; + print " [database] Enter database management menu \n"; + print " [assets] Manage server assets \n"; + print " [new_server] New folder EQEmu/PEQ install - Assumes MySQL/Perl installed \n"; + print " [new_server_with_bots] New folder EQEmu/PEQ install with bots enabled - Assumes MySQL/Perl installed \n"; + print " [setup_bots] Enables bots on server - builds code and database requirements \n"; + print " [conversions] Routines used for conversion of scripts/data \n"; print "\n"; print " exit \n"; print "\n"; @@ -1298,12 +1351,12 @@ sub script_exit sub check_db_version_table { if (get_mysql_result("SHOW TABLES LIKE 'db_version'") eq "" && $db) { + print "[Database] Table 'db_version' does not exist.... Creating...\n\n"; print get_mysql_result(" CREATE TABLE db_version ( version int(11) DEFAULT '0' ) ENGINE=InnoDB DEFAULT CHARSET=latin1; INSERT INTO db_version (version) VALUES ('1000');"); - print "[Database] Table 'db_version' does not exist.... Creating...\n\n"; } } @@ -1452,9 +1505,9 @@ sub read_eqemu_config_json my $content; open(my $fh, '<', "eqemu_config.json") or die "cannot open file $filename"; { - local $/; - $content = <$fh>; -} + local $/; + $content = <$fh>; + } close($fh); $config = $json->decode($content); @@ -1696,8 +1749,7 @@ sub do_windows_login_server_setup sub do_linux_login_server_setup { - - build_linux_source(); + build_linux_source($_[0]); for my $file (@files) { $destination_file = $file; @@ -2526,7 +2578,6 @@ sub run_database_check } } - sub fetch_missing_db_update { $db_update = $_[0]; @@ -2708,7 +2759,6 @@ sub quest_heading_convert print "Total matches: " . $total_matches . "\n"; } - sub quest_faction_convert { From b335568bf9b8ce111e39d62ed430859c231f2cd0 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Mon, 10 May 2021 02:15:38 -0400 Subject: [PATCH 038/624] Prevent client update while on boat if boat turning (#1343) Co-authored-by: Noudess --- zone/client_packet.cpp | 2 ++ zone/mob.cpp | 1 + zone/mob.h | 1 + zone/mob_movement_manager.cpp | 4 ++++ 4 files changed, 8 insertions(+) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 3ffb2d785..0a67550f6 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -4474,6 +4474,8 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { LogError("Can't find boat for client position offset."); } else { + if (boat->turning) return; + // Calculate angle from boat heading to EQ heading double theta = std::fmod(((boat->GetHeading() * 360.0) / 512.0),360.0); double thetar = (theta * M_PI) / 180.0; diff --git a/zone/mob.cpp b/zone/mob.cpp index dbee488e5..0138bdbfd 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -131,6 +131,7 @@ Mob::Mob( AI_Init(); SetMoving(false); moved = false; + turning = false; m_RewindLocation = glm::vec3(); m_RelativePosition = glm::vec4(); diff --git a/zone/mob.h b/zone/mob.h index cad133442..791c5a3e7 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1486,6 +1486,7 @@ public: bool GetWasSpawnedInWater() const; void SetSpawnedInWater(bool spawned_in_water); + bool turning; protected: diff --git a/zone/mob_movement_manager.cpp b/zone/mob_movement_manager.cpp index ecef56c3a..c323a13c2 100644 --- a/zone/mob_movement_manager.cpp +++ b/zone/mob_movement_manager.cpp @@ -63,6 +63,7 @@ public: if (!m_started) { m_started = true; + mob->turning = true; mob->SetMoving(true); if (dist > 15.0f && rotate_to_speed > 0.0 && rotate_to_speed <= 25.0) { //send basic rotation @@ -84,6 +85,7 @@ public: mob->SetHeading(to); mob->SetMoving(false); mob_movement_manager->SendCommandToClients(mob, 0.0, 0.0, 0.0, 0.0, 0, ClientRangeCloseMedium); + mob->turning = false; return true; } @@ -1370,7 +1372,9 @@ void MobMovementManager::UpdatePathBoat(Mob *who, float x, float y, float z, Mob { auto eiter = _impl->Entries.find(who); auto &ent = (*eiter); + float to = who->CalculateHeadingToTarget(x, y); + PushRotateTo(ent.second, who, to, mode); PushSwimTo(ent.second, x, y, z, mode); PushStopMoving(ent.second); } From 2edda6e743b429666e80e22d2fe3cec5a3612797 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Mon, 10 May 2021 02:21:43 -0400 Subject: [PATCH 039/624] [Feature] Allow any spawn2 spawned mob to path while zone is idle if new flag is set. (#1339) * Changes to allow any spawn2 to be marked to path even when zone is idle * Fixed for Kingly review of PR Co-authored-by: Noudess --- common/version.h | 2 +- utils/sql/db_update_manifest.txt | 1 + .../git/required/2021_04_28_idle_pathing.sql | 13 +++ zone/entity.cpp | 14 +-- zone/lua_general.cpp | 15 +++- zone/spawn2.cpp | 88 ++++++++++--------- zone/spawn2.h | 4 +- 7 files changed, 87 insertions(+), 50 deletions(-) create mode 100644 utils/sql/git/required/2021_04_28_idle_pathing.sql diff --git a/common/version.h b/common/version.h index 7b66c1dc4..af6a207e5 100644 --- a/common/version.h +++ b/common/version.h @@ -34,7 +34,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9164 +#define CURRENT_BINARY_DATABASE_VERSION 9165 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9027 diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 071a20e14..55c8facd7 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -418,6 +418,7 @@ 9162|2021_02_17_server_scheduled_events.sql|SELECT * FROM db_version WHERE version >= 9162|empty| 9163|2021_04_17_zone_safe_heading_changes.sql|SHOW COLUMNS FROM `zone` LIKE 'safe_heading'|empty| 9164|2021_04_23_character_exp_modifiers.sql|SHOW TABLES LIKE 'character_exp_modifiers'|empty| +9165|2021_04_28_idle_pathing.sql|SHOW COLUMNS FROM `spawn2` LIKE 'path_when_zone_idle'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2021_04_28_idle_pathing.sql b/utils/sql/git/required/2021_04_28_idle_pathing.sql new file mode 100644 index 000000000..c146b9f24 --- /dev/null +++ b/utils/sql/git/required/2021_04_28_idle_pathing.sql @@ -0,0 +1,13 @@ +-- Add new path_when_zone_idle flag to allow some spawns to path in empty zones +ALTER TABLE spawn2 ADD COLUMN path_when_zone_idle tinyint(1) NOT NULL DEFAULT 0 AFTER pathgrid; + +-- Update spawns that used to path in empty zones because of their grid type +-- to behave the same using the new mechanism. The code that checked path grid +-- types has been removed as it was coincidentally coupled to idle movement. +-- The new flag path_when_zone_idle is the new mechanism, and allows any moving +-- mob, not just those on grids, to path while the zone is idle. +UPDATE spawn2 s +LEFT JOIN zone z on z.short_name = s.zone +LEFT JOIN grid g on g.id = s.pathgrid AND g.zoneid = z.zoneidnumber +SET path_when_zone_idle = 1 +WHERE pathgrid != 0 AND g.type in (4, 6) diff --git a/zone/entity.cpp b/zone/entity.cpp index bf6e44a2d..5cc12dc72 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -517,13 +517,15 @@ void EntityList::MobProcess() mob_settle_timer->Disable(); } + Spawn2* s2 = mob->CastToNPC()->respawn2; + + // Perform normal mob processing if any of these are true: + // -- zone is not empty + // -- a quest has turned it on for this zone while zone is idle + // -- the entity's spawn2 point is marked as path_while_zone_idle + // -- the zone is newly empty and we're allowing mobs to settle if (zone->process_mobs_while_empty || numclients > 0 || - mob->GetWanderType() == 4 || mob->GetWanderType() == 6 || - mob_settle_timer->Enabled()) { - // Normal processing, or assuring that spawns that should - // path and depop do that. Otherwise all of these type mobs - // will be up and at starting positions, or waiting at the zoneline - // if they chased the PCs when idle zone wakes up. + (s2 && s2->PathWhenZoneIdle()) || mob_settle_timer->Enabled()) { mob_dead = !mob->Process(); } else { diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 62f0544c7..e01e32549 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -1732,6 +1732,7 @@ void lua_add_spawn_point(luabind::adl::object table) { uint32 variance; uint32 timeleft = 0; uint32 grid = 0; + bool path_when_zone_idle = false; int condition_id = 0; int condition_min_value = 0; bool enabled = true; @@ -1841,6 +1842,14 @@ void lua_add_spawn_point(luabind::adl::object table) { } } + cur = table["path_when_zone_idle"]; + if(luabind::type(cur) != LUA_TNIL) { + try { + path_when_zone_idle = luabind::object_cast(cur); + } catch(luabind::cast_failed &) { + } + } + cur = table["condition_id"]; if(luabind::type(cur) != LUA_TNIL) { try { @@ -1875,8 +1884,10 @@ void lua_add_spawn_point(luabind::adl::object table) { lua_remove_spawn_point(spawn2_id); - auto t = new Spawn2(spawn2_id, spawngroup_id, x, y, z, heading, respawn, variance, timeleft, grid, - condition_id, condition_min_value, enabled, static_cast(animation)); + auto t = new Spawn2(spawn2_id, spawngroup_id, x, y, z, heading, respawn, + variance, timeleft, grid, path_when_zone_idle, condition_id, + condition_min_value, enabled, static_cast(animation)); + zone->spawn2_list.Insert(t); } } diff --git a/zone/spawn2.cpp b/zone/spawn2.cpp index 37b8cc943..b6689d715 100644 --- a/zone/spawn2.cpp +++ b/zone/spawn2.cpp @@ -70,7 +70,8 @@ CREATE TABLE spawn_events ( Spawn2::Spawn2(uint32 in_spawn2_id, uint32 spawngroup_id, float in_x, float in_y, float in_z, float in_heading, uint32 respawn, uint32 variance, uint32 timeleft, uint32 grid, - uint16 in_cond_id, int16 in_min_value, bool in_enabled, EmuAppearance anim) + bool in_path_when_zone_idle, uint16 in_cond_id, int16 in_min_value, + bool in_enabled, EmuAppearance anim) : timer(100000), killcount(0) { spawn2_id = in_spawn2_id; @@ -82,6 +83,7 @@ Spawn2::Spawn2(uint32 in_spawn2_id, uint32 spawngroup_id, respawn_ = respawn; variance_ = variance; grid_ = grid; + path_when_zone_idle = in_path_when_zone_idle; condition_id = in_cond_id; condition_min_value = in_min_value; npcthis = nullptr; @@ -474,6 +476,7 @@ bool ZoneDatabase::PopulateZoneSpawnListClose(uint32 zoneid, LinkedList "respawntime, " "variance, " "pathgrid, " + "path_when_zone_idle, " "_condition, " "cond_value, " "enabled, " @@ -494,7 +497,7 @@ bool ZoneDatabase::PopulateZoneSpawnListClose(uint32 zoneid, LinkedList uint32 spawn_time_left = 0; Spawn2* new_spawn = 0; - bool perl_enabled = atoi(row[11]) == 1 ? true : false; + bool perl_enabled = atoi(row[12]) == 1 ? true : false; if (spawn_times.count(atoi(row[0])) != 0) spawn_time_left = spawn_times[atoi(row[0])]; @@ -508,21 +511,22 @@ bool ZoneDatabase::PopulateZoneSpawnListClose(uint32 zoneid, LinkedList if (mob_distance > repop_distance) continue; - new_spawn = new Spawn2( // - atoi(row[0]), // uint32 in_spawn2_id - atoi(row[1]), // uint32 spawngroup_id - atof(row[2]), // float in_x - atof(row[3]), // float in_y - atof(row[4]), // float in_z - atof(row[5]), // float in_heading - atoi(row[6]), // uint32 respawn - atoi(row[7]), // uint32 variance - spawn_time_left, // uint32 timeleft - atoi(row[8]), // uint32 grid - atoi(row[9]), // uint16 in_cond_id - atoi(row[10]), // int16 in_min_value - perl_enabled, // bool in_enabled - (EmuAppearance)atoi(row[12]) // EmuAppearance anim + new_spawn = new Spawn2( + atoi(row[0]), // uint32 in_spawn2_id + atoi(row[1]), // uint32 spawngroup_id + atof(row[2]), // float in_x + atof(row[3]), // float in_y + atof(row[4]), // float in_z + atof(row[5]), // float in_heading + atoi(row[6]), // uint32 respawn + atoi(row[7]), // uint32 variance + spawn_time_left, // uint32 timeleft + atoi(row[8]), // uint32 grid + (bool)atoi(row[9]), // bool path_when_zone_idle + atoi(row[10]), // uint16 in_cond_id + atoi(row[11]), // int16 in_min_value + perl_enabled, // bool in_enabled + (EmuAppearance)atoi(row[13]) // EmuAppearance anim ); spawn2_list.Insert(new_spawn); @@ -578,6 +582,7 @@ bool ZoneDatabase::PopulateZoneSpawnList(uint32 zoneid, LinkedList &spa "respawntime, " "variance, " "pathgrid, " + "path_when_zone_idle, " "_condition, " "cond_value, " "enabled, " @@ -598,26 +603,27 @@ bool ZoneDatabase::PopulateZoneSpawnList(uint32 zoneid, LinkedList &spa uint32 spawn_time_left = 0; Spawn2* new_spawn = 0; - bool perl_enabled = atoi(row[11]) == 1 ? true : false; + bool perl_enabled = atoi(row[12]) == 1 ? true : false; if (spawn_times.count(atoi(row[0])) != 0) spawn_time_left = spawn_times[atoi(row[0])]; - new_spawn = new Spawn2( // - atoi(row[0]), // uint32 in_spawn2_id - atoi(row[1]), // uint32 spawngroup_id - atof(row[2]), // float in_x - atof(row[3]), // float in_y - atof(row[4]), // float in_z - atof(row[5]), // float in_heading - atoi(row[6]), // uint32 respawn - atoi(row[7]), // uint32 variance - spawn_time_left, // uint32 timeleft - atoi(row[8]), // uint32 grid - atoi(row[9]), // uint16 in_cond_id - atoi(row[10]), // int16 in_min_value - perl_enabled, // bool in_enabled - (EmuAppearance)atoi(row[12]) // EmuAppearance anim + new_spawn = new Spawn2( + atoi(row[0]), // uint32 in_spawn2_id + atoi(row[1]), // uint32 spawngroup_id + atof(row[2]), // float in_x + atof(row[3]), // float in_y + atof(row[4]), // float in_z + atof(row[5]), // float in_heading + atoi(row[6]), // uint32 respawn + atoi(row[7]), // uint32 variance + spawn_time_left, // uint32 timeleft + atoi(row[8]), // uint32 grid + (bool)atoi(row[9]), // bool path_when_zone_idle + atoi(row[10]), // uint16 in_cond_id + atoi(row[11]), // int16 in_min_value + perl_enabled, // bool in_enabled + (EmuAppearance)atoi(row[13]) // EmuAppearance anim ); spawn2_list.Insert(new_spawn); @@ -632,9 +638,10 @@ bool ZoneDatabase::PopulateZoneSpawnList(uint32 zoneid, LinkedList &spa Spawn2* ZoneDatabase::LoadSpawn2(LinkedList &spawn2_list, uint32 spawn2id, uint32 timeleft) { std::string query = StringFormat("SELECT id, spawngroupID, x, y, z, heading, " - "respawntime, variance, pathgrid, _condition, " - "cond_value, enabled, animation FROM spawn2 " - "WHERE id = %i", spawn2id); + "respawntime, variance, pathgrid, " + "path_when_zone_idle, _condition, " + "cond_value, enabled, animation FROM spawn2 " + "WHERE id = %i", spawn2id); auto results = QueryDatabase(query); if (!results.Success()) { return nullptr; @@ -646,11 +653,12 @@ Spawn2* ZoneDatabase::LoadSpawn2(LinkedList &spawn2_list, uint32 spawn2 auto row = results.begin(); - bool perl_enabled = atoi(row[11]) == 1 ? true : false; + bool perl_enabled = atoi(row[12]) == 1 ? true : false; - auto newSpawn = new Spawn2(atoi(row[0]), atoi(row[1]), atof(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), - atoi(row[6]), atoi(row[7]), timeleft, atoi(row[8]), atoi(row[9]), atoi(row[10]), - perl_enabled, (EmuAppearance)atoi(row[12])); + auto newSpawn = new Spawn2(atoi(row[0]), atoi(row[1]), atof(row[2]), + atof(row[3]), atof(row[4]), atof(row[5]), atoi(row[6]), atoi(row[7]), + timeleft, atoi(row[8]), (bool) atoi(row[9]), atoi(row[10]), + atoi(row[11]), perl_enabled, (EmuAppearance)atoi(row[13])); spawn2_list.Insert(newSpawn); diff --git a/zone/spawn2.h b/zone/spawn2.h index bf6530876..3f8b2e8ee 100644 --- a/zone/spawn2.h +++ b/zone/spawn2.h @@ -32,7 +32,7 @@ public: Spawn2(uint32 spawn2_id, uint32 spawngroup_id, float x, float y, float z, float heading, uint32 respawn, uint32 variance, - uint32 timeleft = 0, uint32 grid = 0, + uint32 timeleft = 0, uint32 grid = 0, bool in_path_when_zone_idle=false, uint16 cond_id = SC_AlwaysEnabled, int16 min_value = 0, bool in_enabled = true, EmuAppearance anim = eaStanding); ~Spawn2(); @@ -54,6 +54,7 @@ public: float GetY() { return y; } float GetZ() { return z; } float GetHeading() { return heading; } + bool PathWhenZoneIdle() { return path_when_zone_idle; } void SetRespawnTimer(uint32 newrespawntime) { respawn_ = newrespawntime; }; void SetVariance(uint32 newvariance) { variance_ = newvariance; } const uint32 GetVariance() const { return variance_; } @@ -86,6 +87,7 @@ private: float heading; uint32 variance_; uint32 grid_; + bool path_when_zone_idle; uint16 condition_id; int16 condition_min_value; bool enabled; From e18b33241467504ab55cab53b696f513edc59379 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 10 May 2021 02:34:40 -0400 Subject: [PATCH 040/624] [CLE] Reset iterator in IsAccountInGame() (#1345) --- world/clientlist.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/world/clientlist.cpp b/world/clientlist.cpp index 008a1115b..0f216c0ee 100644 --- a/world/clientlist.cpp +++ b/world/clientlist.cpp @@ -1244,7 +1244,7 @@ void ClientList::RemoveCLEByLSID(uint32 iLSID) bool ClientList::IsAccountInGame(uint32 iLSID) { LinkedListIterator iterator(clientlist); - + iterator.Reset(); while (iterator.MoreElements()) { if (iterator.GetData()->LSID() == iLSID && iterator.GetData()->Online() == CLE_Status::InZone) { return true; From 6ce273baf56feaf8be9cf699f31359fb911de017 Mon Sep 17 00:00:00 2001 From: JJ <3617814+joligario@users.noreply.github.com> Date: Mon, 10 May 2021 12:13:58 -0400 Subject: [PATCH 041/624] [SQL] Tweak SQL from #1339 [skip ci] (#1348) --- utils/sql/git/required/2021_04_28_idle_pathing.sql | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/sql/git/required/2021_04_28_idle_pathing.sql b/utils/sql/git/required/2021_04_28_idle_pathing.sql index c146b9f24..2249163fc 100644 --- a/utils/sql/git/required/2021_04_28_idle_pathing.sql +++ b/utils/sql/git/required/2021_04_28_idle_pathing.sql @@ -7,7 +7,7 @@ ALTER TABLE spawn2 ADD COLUMN path_when_zone_idle tinyint(1) NOT NULL DEFAULT 0 -- The new flag path_when_zone_idle is the new mechanism, and allows any moving -- mob, not just those on grids, to path while the zone is idle. UPDATE spawn2 s -LEFT JOIN zone z on z.short_name = s.zone -LEFT JOIN grid g on g.id = s.pathgrid AND g.zoneid = z.zoneidnumber +LEFT JOIN zone z ON z.short_name = s.zone +LEFT JOIN grid g ON g.id = s.pathgrid AND g.zoneid = z.zoneidnumber SET path_when_zone_idle = 1 -WHERE pathgrid != 0 AND g.type in (4, 6) +WHERE pathgrid != 0 AND g.type IN (4, 6); From 1f978ffd72cd3120bb3cc4cb5e73b6305d05ab79 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 12 May 2021 20:13:05 -0400 Subject: [PATCH 042/624] [Bug Fix] EntityList::AESpell was off by one (#1351) We need to do the spell effect first, then check the limits --- zone/effects.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index b62a2b383..b095c84a3 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -917,6 +917,9 @@ void EntityList::AESpell( } } + current_mob->CalcSpellPowerDistanceMod(spell_id, distance_to_target); + caster_mob->SpellOnTarget(spell_id, current_mob, false, true, resist_adjust); + /** * Increment hit count if max targets */ @@ -926,9 +929,6 @@ void EntityList::AESpell( break; } } - - current_mob->CalcSpellPowerDistanceMod(spell_id, distance_to_target); - caster_mob->SpellOnTarget(spell_id, current_mob, false, true, resist_adjust); } LogAoeCast("Done iterating [{}]", caster_mob->GetCleanName()); From 1f896d05ed3d328b56ef3a9635636cf2b8b430d8 Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Wed, 12 May 2021 20:16:29 -0400 Subject: [PATCH 043/624] [Repository Generator] Fix repository generator on windows (#1353) Remove hardcoded port --- utils/scripts/generators/repository-generator.pl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/utils/scripts/generators/repository-generator.pl b/utils/scripts/generators/repository-generator.pl index ce3246dad..99f8eb856 100644 --- a/utils/scripts/generators/repository-generator.pl +++ b/utils/scripts/generators/repository-generator.pl @@ -33,8 +33,9 @@ my $repository_generation_option = $ARGV[2] ? $ARGV[2] : "all"; ############################################# # world path ############################################# -my $world_path = $server_path . "/world"; -my $world_path_bin = $server_path . "/bin/world"; +my $world_binary = ($^O eq "MSWin32") ? "world.exe" : "world"; +my $world_path = $server_path . "/" . $world_binary; +my $world_path_bin = $server_path . "/bin/" . $world_binary; my $found_world_path = ""; if (-e $world_path) { @@ -81,7 +82,8 @@ my $database_name = $config->{"server"}{"database"}{"db"}; my $host = $config->{"server"}{"database"}{"host"}; my $user = $config->{"server"}{"database"}{"username"}; my $pass = $config->{"server"}{"database"}{"password"}; -my $dsn = "dbi:mysql:$database_name:$host:3306"; +my $port = $config->{"server"}{"database"}{"port"}; +my $dsn = "dbi:mysql:$database_name:$host:$port"; my $connect = DBI->connect($dsn, $user, $pass); my @tables = (); From 4cc24dea75e611d96810e2107c2bf95d9a197993 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Wed, 12 May 2021 20:17:55 -0400 Subject: [PATCH 044/624] [GM Command] #list npcs Goto option now goes to higher Z if selected NPC is a boat. (#1349) * #list npcs Goto option now goes to higher Z if npc selected is a boat. Makes using #list npcs and using the goto button more useful when NPC is a boat, as normal Z will put you in the water. * Use ?: instead of bool multiply * Update command.cpp --- zone/command.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/command.cpp b/zone/command.cpp index 4991d25f6..20cb4856f 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -1610,7 +1610,7 @@ void command_list(Client *c, const Seperator *sep) "#goto %.0f %0.f %.0f", entity->GetX(), entity->GetY(), - entity->GetZ()); + entity->GetZ() + (entity->IsBoat() ? 50 : 0)); c->Message( 0, From c1c2d7b3020d6f7547972c6070078e5180a46d9e Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Wed, 12 May 2021 20:59:48 -0400 Subject: [PATCH 045/624] [Dynamic Zones] Store min and max players on dz (#1355) This starts some changes that move storage of things from expeditions to dynamic zone class so other systems can possibly use them. This will also make it easier to move window packet creation methods to DynamicZone. For now these will remain on the expeditions table in the database. This can be re-evaluated once other components are moved and seeing how other systems may want to handle their player requirements. --- common/dynamic_zone_base.h | 6 ++++++ common/expedition_base.cpp | 9 ++------- common/expedition_base.h | 6 +----- world/expedition_state.cpp | 4 ++++ zone/expedition.cpp | 26 ++++++++++++++++---------- zone/expedition.h | 2 +- 6 files changed, 30 insertions(+), 23 deletions(-) diff --git a/common/dynamic_zone_base.h b/common/dynamic_zone_base.h index e321ce704..5a84fa8fa 100644 --- a/common/dynamic_zone_base.h +++ b/common/dynamic_zone_base.h @@ -61,6 +61,8 @@ public: uint64_t GetExpireTime() const { return std::chrono::system_clock::to_time_t(m_expire_time); } uint32_t GetID() const { return m_id; } uint16_t GetInstanceID() const { return static_cast(m_instance_id); } + uint32_t GetMaxPlayers() const { return m_max_players; } + uint32_t GetMinPlayers() const { return m_min_players; } uint32_t GetSecondsRemaining() const; uint16_t GetZoneID() const { return static_cast(m_zone_id); } uint32_t GetZoneIndex() const { return (m_instance_id << 16) | (m_zone_id & 0xffff); } @@ -86,6 +88,8 @@ public: void SetCompass(const DynamicZoneLocation& location, bool update_db = false); void SetCompass(uint32_t zone_id, float x, float y, float z, bool update_db = false); void SetLeaderName(const std::string& leader_name) { m_leader_name = leader_name; } + void SetMaxPlayers(uint32_t max_players) { m_max_players = max_players; } + void SetMinPlayers(uint32_t min_players) { m_min_players = min_players; } void SetName(const std::string& name) { m_name = name; } void SetSafeReturn(const DynamicZoneLocation& location, bool update_db = false); void SetSafeReturn(uint32_t zone_id, float x, float y, float z, float heading, bool update_db = false); @@ -113,6 +117,8 @@ protected: uint32_t m_zone_id = 0; uint32_t m_instance_id = 0; uint32_t m_zone_version = 0; + uint32_t m_min_players = 0; + uint32_t m_max_players = 0; bool m_never_expires = false; bool m_has_zonein = false; std::string m_name; diff --git a/common/expedition_base.cpp b/common/expedition_base.cpp index cb1882dca..a5f890204 100644 --- a/common/expedition_base.cpp +++ b/common/expedition_base.cpp @@ -3,15 +3,12 @@ #include "rulesys.h" ExpeditionBase::ExpeditionBase(uint32_t id, const std::string& uuid, - const std::string& expedition_name, const DynamicZoneMember& leader, - uint32_t min_players, uint32_t max_players + const std::string& expedition_name, const DynamicZoneMember& leader ) : m_id(id), m_uuid(uuid), m_expedition_name(expedition_name), - m_leader(leader), - m_min_players(min_players), - m_max_players(max_players) + m_leader(leader) { } @@ -20,8 +17,6 @@ void ExpeditionBase::LoadRepositoryResult(ExpeditionsRepository::ExpeditionWithL m_id = entry.id; m_uuid = std::move(entry.uuid); m_expedition_name = std::move(entry.expedition_name); - m_min_players = entry.min_players; - m_max_players = entry.max_players; m_add_replay_on_join = entry.add_replay_on_join; m_is_locked = entry.is_locked; m_leader.id = entry.leader_id; diff --git a/common/expedition_base.h b/common/expedition_base.h index cdd2382f7..e93c707b5 100644 --- a/common/expedition_base.h +++ b/common/expedition_base.h @@ -19,8 +19,6 @@ public: uint32_t GetID() const { return m_id; } uint32_t GetLeaderID() const { return m_leader.id; } - uint32_t GetMinPlayers() const { return m_min_players; } - uint32_t GetMaxPlayers() const { return m_max_players; } uint32_t GetMemberCount() const { return static_cast(m_members.size()); } const std::string& GetName() const { return m_expedition_name; } const std::string& GetLeaderName() const { return m_leader.name; } @@ -41,14 +39,12 @@ public: protected: ExpeditionBase() = default; ExpeditionBase(uint32_t id, const std::string& uuid, const std::string& expedition_name, - const DynamicZoneMember& leader, uint32_t min_players, uint32_t max_players); + const DynamicZoneMember& leader); DynamicZoneMember GetMemberData(uint32_t character_id); DynamicZoneMember GetMemberData(const std::string& character_name); uint32_t m_id = 0; - uint32_t m_min_players = 0; - uint32_t m_max_players = 0; bool m_is_locked = false; bool m_add_replay_on_join = true; std::string m_uuid; diff --git a/world/expedition_state.cpp b/world/expedition_state.cpp index c1d520c69..8f3b6b423 100644 --- a/world/expedition_state.cpp +++ b/world/expedition_state.cpp @@ -110,6 +110,10 @@ void ExpeditionState::CacheExpeditions( } } + // stored on expedition in db but on dz in memory cache + expedition->GetDynamicZone().SetMinPlayers(entry.min_players); + expedition->GetDynamicZone().SetMaxPlayers(entry.max_players); + expedition->CacheMemberStatuses(); m_expeditions.emplace_back(std::move(expedition)); diff --git a/zone/expedition.cpp b/zone/expedition.cpp index 7d404ff03..cb6fa4199 100644 --- a/zone/expedition.cpp +++ b/zone/expedition.cpp @@ -50,8 +50,8 @@ const int32_t Expedition::EVENT_TIMER_ID = 1; Expedition::Expedition( uint32_t id, const std::string& uuid, DynamicZone&& dz, const std::string& expedition_name, - const DynamicZoneMember& leader, uint32_t min_players, uint32_t max_players -) : ExpeditionBase(id, uuid, expedition_name, leader, min_players, max_players) + const DynamicZoneMember& leader +) : ExpeditionBase(id, uuid, expedition_name, leader) { SetDynamicZone(std::move(dz)); } @@ -80,6 +80,9 @@ Expedition* Expedition::TryCreate( return nullptr; } + dynamiczone.SetMinPlayers(request.GetMinPlayers()); + dynamiczone.SetMaxPlayers(request.GetMaxPlayers()); + auto dynamic_zone_id = dynamiczone.Create(); if (dynamic_zone_id == 0) { @@ -109,9 +112,7 @@ Expedition* Expedition::TryCreate( expedition_uuid, std::move(dynamiczone), request.GetExpeditionName(), - DynamicZoneMember{ request.GetLeaderID(), request.GetLeaderName() }, - request.GetMinPlayers(), - request.GetMaxPlayers() + DynamicZoneMember{ request.GetLeaderID(), request.GetLeaderName() } ); LogExpeditions( @@ -120,8 +121,8 @@ Expedition* Expedition::TryCreate( expedition->GetName(), expedition->GetDynamicZone().GetInstanceID(), expedition->GetLeaderName(), - expedition->GetMinPlayers(), - expedition->GetMaxPlayers() + expedition->GetDynamicZone().GetMinPlayers(), + expedition->GetDynamicZone().GetMaxPlayers() ); expedition->SaveMembers(request); @@ -207,6 +208,10 @@ void Expedition::CacheExpeditions( } } + // stored on expedition in db but on dz in memory cache + expedition->GetDynamicZone().SetMinPlayers(entry.min_players); + expedition->GetDynamicZone().SetMaxPlayers(entry.max_players); + expedition->SendWorldExpeditionUpdate(ServerOP_ExpeditionGetMemberStatuses); auto inserted = zone->expedition_cache.emplace(entry.id, std::move(expedition)); @@ -673,9 +678,10 @@ bool Expedition::ProcessAddConflicts(Client* leader_client, Client* add_client, { has_conflict = true; } - else if (member_count >= m_max_players) + else if (member_count >= GetDynamicZone().GetMaxPlayers()) { - SendLeaderMessage(leader_client, Chat::Red, DZADD_EXCEED_MAX, { fmt::format_int(m_max_players).str() }); + SendLeaderMessage(leader_client, Chat::Red, DZADD_EXCEED_MAX, { + fmt::format_int(GetDynamicZone().GetMaxPlayers()).str() }); has_conflict = true; } } @@ -1323,7 +1329,7 @@ std::unique_ptr Expedition::CreateInfoPacket(bool clear) info->assigned = true; strn0cpy(info->dz_name, m_expedition_name.c_str(), sizeof(info->dz_name)); strn0cpy(info->leader_name, m_leader.name.c_str(), sizeof(info->leader_name)); - info->max_players = m_max_players; + info->max_players = GetDynamicZone().GetMaxPlayers(); } return outapp; } diff --git a/zone/expedition.h b/zone/expedition.h index f5e7834bd..b3404d696 100644 --- a/zone/expedition.h +++ b/zone/expedition.h @@ -52,7 +52,7 @@ class Expedition : public ExpeditionBase public: Expedition() = default; Expedition(uint32_t id, const std::string& uuid, DynamicZone&& dz, const std::string& expedition_name, - const DynamicZoneMember& leader, uint32_t min_players, uint32_t max_players); + const DynamicZoneMember& leader); static Expedition* TryCreate(Client* requester, DynamicZone& dynamiczone, ExpeditionRequest& request); From a59ffc6e6b858f386e4799d5dea873ca81e9bab1 Mon Sep 17 00:00:00 2001 From: Dencelle Date: Sat, 15 May 2021 16:33:45 -0500 Subject: [PATCH 046/624] [Bug Fix] EntityList::AESpell fix for Pacify / Mez (#1354) * [Bug Fix] EntityList::AESpell fix for Pacify / Mez this fixes AE pacify / Mez spells only landing on 4 when it shouldn't have a cap * Update effects.cpp added constants * Update effects.cpp opmization thanks to @mackal * Update effects.cpp this fixes the unlimited issue * added Spells:AOEMaxTargets to rules incase a server doesn't want there to be a absolute unlimited AOE targeting for spells * Update ruletypes.h --- common/ruletypes.h | 1 + zone/effects.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 5a30bfc2c..36a5630e4 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -369,6 +369,7 @@ RULE_BOOL(Spells, NPCInnateProcOverride, true, "NPC innate procs override the ta RULE_BOOL(Spells, OldRainTargets, false, "Use old incorrectly implemented maximum targets for rains") RULE_BOOL(Spells, NPCSpellPush, false, "Enable spell push on NPCs") RULE_BOOL(Spells, July242002PetResists, true, "Enable Pets using PCs resist change from July 24 2002") +RULE_INT(Spells, AOEMaxTargets, 0, "Max number of targets a Targeted AOE spell can cast on. Set to 0 for no limit.") RULE_CATEGORY_END() RULE_CATEGORY(Combat) diff --git a/zone/effects.cpp b/zone/effects.cpp index b095c84a3..6123ede4a 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -784,14 +784,14 @@ void EntityList::AESpell( /** * Max AOE targets */ - int max_targets_allowed = 0; // unlimited + int max_targets_allowed = RuleI(Range, AOEMaxTargets); // unlimited if (max_targets) { // rains pass this in since they need to preserve the count through waves max_targets_allowed = *max_targets; } else if (spells[spell_id].aemaxtargets) { max_targets_allowed = spells[spell_id].aemaxtargets; } - else if (IsTargetableAESpell(spell_id) && is_detrimental_spell && !is_npc) { + else if (IsTargetableAESpell(spell_id) && is_detrimental_spell && !is_npc && !IsEffectInSpell(spell_id, SE_Lull) && !IsEffectInSpell(spell_id, SE_Mez)) { max_targets_allowed = 4; } From b65cf4c0810ce16fc285774fdb3351d79cad1600 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 19 May 2021 19:40:51 -0400 Subject: [PATCH 047/624] This skill bonus was suppose to only apply to monks with epics (#1364) This also scales up to 280 skill, so under that, you don't get as high of a bonus --- zone/attack.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 2f85dbaaf..de9a6101e 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3014,9 +3014,10 @@ int Mob::GetHandToHandDelay(void) epic = 280; else if (GetRace() == IKSAR) iksar = 1; - if (epic > skill) - skill = epic; - return iksar - skill / 21 + 38; + // the delay bonus from the monk epic scales up to a skill of 280 + if (epic >= skill) + epic = skill; + return iksar - epic / 21 + 38; } int delay = 35; From 93329b4b068ee0569ef72927214961336349b48b Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 24 May 2021 21:15:37 -0400 Subject: [PATCH 048/624] [Shared Bank] Add additional popup to shared bank warning message, as client-side filters can cause the message to be unseen. (#1368) --- zone/client_process.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 9c2175b27..a78095a30 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -1450,7 +1450,12 @@ void Client::OPMoveCoin(const EQApplicationPacket* app) } else{ if (to_bucket == &m_pp.platinum_shared || from_bucket == &m_pp.platinum_shared){ - this->Message(Chat::Red, "::: WARNING! ::: SHARED BANK IS DISABLED AND YOUR PLATINUM WILL BE DESTROYED IF YOU PUT IT HERE"); + this->SendPopupToClient( + "Shared Bank Warning", + "::: WARNING! :::
" + "SHARED BANK IS DISABLED AND YOUR PLATINUM WILL BE DESTROYED IF YOU PUT IT HERE!
" + ); + this->Message(Chat::Red, "::: WARNING! ::: SHARED BANK IS DISABLED AND YOUR PLATINUM WILL BE DESTROYED IF YOU PUT IT HERE!"); } } } From e5b9d72b81e428aaac453281f127f1a4d83dd084 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 24 May 2021 21:21:39 -0400 Subject: [PATCH 049/624] [Fix] Fix Spell Cast Time reduction issues (#1369) Remove the overloads that don't make sense (bots probably doesn't make sense either, but too lazy) Fix the formulas Removed the Spells:MaxCastTimeReduction rule since this is HARDCODED in the client so it doesn't really make sense to have it as a customization point. If you want to hack the client, change the hardcode as well I guess. --- common/ruletypes.h | 1 - zone/bot.cpp | 17 ++++++++--------- zone/client.h | 1 - zone/effects.cpp | 23 ----------------------- zone/merc.cpp | 13 ------------- zone/merc.h | 1 - zone/mob.cpp | 26 +++++++++----------------- 7 files changed, 17 insertions(+), 65 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 36a5630e4..22ea02741 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -326,7 +326,6 @@ RULE_INT(Spells, CharismaEffectivenessCap, 255, "Determines how much resist modi RULE_BOOL(Spells, CharismaCharmDuration, false, "Allow CHA resist mod to extend charm duration") RULE_INT(Spells, CharmBreakCheckChance, 25, "Determines chance for a charm break check to occur each buff tick") RULE_BOOL(Spells, CharmDisablesSpecialAbilities, false, "When charm is cast on an NPC, strip their special abilities") -RULE_INT(Spells, MaxCastTimeReduction, 50, "Maximum percent your spell cast time can be reduced by spell haste") RULE_INT(Spells, RootBreakFromSpells, 55, "Chance for root to break when cast on") RULE_INT(Spells, DeathSaveCharismaMod, 3, "Determines how much charisma effects chance of death save firing") RULE_INT(Spells, DivineInterventionHeal, 8000, "Divine intervention heal amount") diff --git a/zone/bot.cpp b/zone/bot.cpp index 59bdf9788..99bcd079b 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -7101,12 +7101,14 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { } int32 Bot::GetActSpellCasttime(uint16 spell_id, int32 casttime) { - int32 cast_reducer = 0; - cast_reducer += GetBotFocusEffect(focusSpellHaste, spell_id); + int32 cast_reducer = GetBotFocusEffect(focusSpellHaste, spell_id); uint8 botlevel = GetLevel(); uint8 botclass = GetClass(); - if (botlevel >= 51 && casttime >= 3000 && !BeneficialSpell(spell_id) && (botclass == SHADOWKNIGHT || botclass == RANGER || botclass == PALADIN || botclass == BEASTLORD )) - cast_reducer += ((GetLevel() - 50) * 3); + if (botlevel >= 51 && casttime >= 3000 && !spells[spell_id].goodEffect && + (botclass == SHADOWKNIGHT || botclass == RANGER || botclass == PALADIN || botclass == BEASTLORD)) { + int level_mod = std::min(15, botlevel - 50); + cast_reducer += level_mod * 3; + } if((casttime >= 4000) && BeneficialSpell(spell_id) && IsBuffSpell(spell_id)) { switch (GetAA(aaSpellCastingDeftness)) { @@ -7176,11 +7178,8 @@ int32 Bot::GetActSpellCasttime(uint16 spell_id, int32 casttime) { } } - if (cast_reducer > RuleI(Spells, MaxCastTimeReduction)) - cast_reducer = RuleI(Spells, MaxCastTimeReduction); - - casttime = (casttime * (100 - cast_reducer) / 100); - return casttime; + casttime = casttime * (100 - cast_reducer) / 100; + return std::max(casttime, casttime / 2); } int32 Bot::GetActSpellCost(uint16 spell_id, int32 cost) { diff --git a/zone/client.h b/zone/client.h index 6e2840217..9d4059450 100644 --- a/zone/client.h +++ b/zone/client.h @@ -550,7 +550,6 @@ public: inline virtual int32 GetDelayDeath() const { return aabonuses.DelayDeath + spellbonuses.DelayDeath + itembonuses.DelayDeath + 11; } int32 GetActSpellCost(uint16 spell_id, int32); - int32 GetActSpellCasttime(uint16 spell_id, int32); virtual bool CheckFizzle(uint16 spell_id); virtual bool CheckSpellLevelRestriction(uint16 spell_id); virtual int GetCurrentBuffSlots() const; diff --git a/zone/effects.cpp b/zone/effects.cpp index 6123ede4a..da3608416 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -407,29 +407,6 @@ int32 Mob::GetActSpellDuration(uint16 spell_id, int32 duration) return ifocused + 1; } -int32 Client::GetActSpellCasttime(uint16 spell_id, int32 casttime) -{ - int32 cast_reducer = 0; - cast_reducer += GetFocusEffect(focusSpellHaste, spell_id); - - //this function loops through the effects of spell_id many times - //could easily be consolidated. - - if (GetLevel() >= 51 && casttime >= 3000 && !BeneficialSpell(spell_id) - && (GetClass() == SHADOWKNIGHT || GetClass() == RANGER - || GetClass() == PALADIN || GetClass() == BEASTLORD )) - cast_reducer += (GetLevel()-50)*3; - - //LIVE AA SpellCastingDeftness, QuickBuff, QuickSummoning, QuickEvacuation, QuickDamage - - if (cast_reducer > RuleI(Spells, MaxCastTimeReduction)) - cast_reducer = RuleI(Spells, MaxCastTimeReduction); - - casttime = (casttime*(100 - cast_reducer)/100); - - return casttime; -} - bool Client::TrainDiscipline(uint32 itemid) { //get the item info diff --git a/zone/merc.cpp b/zone/merc.cpp index 1da2cd1ae..3b8631edd 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -2756,19 +2756,6 @@ int32 Merc::GetActSpellCost(uint16 spell_id, int32 cost) return cost; } -int32 Merc::GetActSpellCasttime(uint16 spell_id, int32 casttime) -{ - int32 cast_reducer = 0; - cast_reducer += GetFocusEffect(focusSpellHaste, spell_id); - - if (cast_reducer > RuleI(Spells, MaxCastTimeReduction)) - cast_reducer = RuleI(Spells, MaxCastTimeReduction); - - casttime = (casttime*(100 - cast_reducer)/100); - - return casttime; -} - int8 Merc::GetChanceToCastBySpellType(uint32 spellType) { int mercStance = (int)GetStance(); int8 mercClass = GetClass(); diff --git a/zone/merc.h b/zone/merc.h index efd27f703..d8feeab4f 100644 --- a/zone/merc.h +++ b/zone/merc.h @@ -83,7 +83,6 @@ public: Corpse* GetGroupMemberCorpse(); // Merc Spell Casting Methods - virtual int32 GetActSpellCasttime(uint16 spell_id, int32 casttime); virtual int32 GetActSpellCost(uint16 spell_id, int32 cost); int8 GetChanceToCastBySpellType(uint32 spellType); void SetSpellRecastTimer(uint16 timer_id, uint16 spellid, uint32 recast_delay); diff --git a/zone/mob.cpp b/zone/mob.cpp index 0138bdbfd..ddd658b0d 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3112,26 +3112,18 @@ uint32 Mob::GetLevelHP(uint8 tlevel) return multiplier; } -int32 Mob::GetActSpellCasttime(uint16 spell_id, int32 casttime) { +int32 Mob::GetActSpellCasttime(uint16 spell_id, int32 casttime) +{ + int32 cast_reducer = GetFocusEffect(focusSpellHaste, spell_id); - int32 cast_reducer = 0; - cast_reducer += GetFocusEffect(focusSpellHaste, spell_id); - - if (level >= 60 && casttime > 1000) - { - casttime = casttime / 2; - if (casttime < 1000) - casttime = 1000; - } else if (level >= 50 && casttime > 1000) { - int32 cast_deduction = (casttime*(level - 49))/5; - if (cast_deduction > casttime/2) - casttime /= 2; - else - casttime -= cast_deduction; + if (level > 50 && casttime >= 3000 && !spells[spell_id].goodEffect && + (GetClass() == RANGER || GetClass() == SHADOWKNIGHT || GetClass() == PALADIN || GetClass() == BEASTLORD)) { + int level_mod = std::min(15, GetLevel() - 50); + cast_reducer += level_mod * 3; } - casttime = (casttime*(100 - cast_reducer)/100); - return casttime; + casttime = casttime * (100 - cast_reducer) / 100; + return std::max(casttime, casttime / 2); } void Mob::ExecWeaponProc(const EQ::ItemInstance *inst, uint16 spell_id, Mob *on, int level_override) { From efa245d636df81ce0d971361341941e8a8f31f9b Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Mon, 24 May 2021 21:22:31 -0400 Subject: [PATCH 050/624] [Quest API] Add write overloads to lua packet quest api (#1366) Makes prototyping easier without having to manually track an offset Also add GetWritePosition and SetWritePosition --- zone/lua_packet.cpp | 76 ++++++++++++++++++++++++++++++++++++++++----- zone/lua_packet.h | 10 ++++++ 2 files changed, 78 insertions(+), 8 deletions(-) diff --git a/zone/lua_packet.cpp b/zone/lua_packet.cpp index 4936259e6..0457a8310 100644 --- a/zone/lua_packet.cpp +++ b/zone/lua_packet.cpp @@ -89,6 +89,21 @@ void Lua_Packet::SetRawOpcode(int op) { self->SetOpcode(static_cast(op)); } +int Lua_Packet::GetWritePosition() { + Lua_Safe_Call_Int(); + return self->GetWritePosition(); +} + +void Lua_Packet::SetWritePosition(int offset) { + Lua_Safe_Call_Void(); + self->SetWritePosition(offset); +} + +void Lua_Packet::WriteInt8(int value) { + Lua_Safe_Call_Void(); + self->WriteUInt8(static_cast(value)); +} + void Lua_Packet::WriteInt8(int offset, int value) { Lua_Safe_Call_Void(); @@ -97,6 +112,11 @@ void Lua_Packet::WriteInt8(int offset, int value) { } } +void Lua_Packet::WriteInt16(int value) { + Lua_Safe_Call_Void(); + self->WriteUInt16(static_cast(value)); +} + void Lua_Packet::WriteInt16(int offset, int value) { Lua_Safe_Call_Void(); @@ -105,6 +125,11 @@ void Lua_Packet::WriteInt16(int offset, int value) { } } +void Lua_Packet::WriteInt32(int value) { + Lua_Safe_Call_Void(); + self->WriteUInt32(static_cast(value)); +} + void Lua_Packet::WriteInt32(int offset, int value) { Lua_Safe_Call_Void(); @@ -113,6 +138,11 @@ void Lua_Packet::WriteInt32(int offset, int value) { } } +void Lua_Packet::WriteInt64(int64 value) { + Lua_Safe_Call_Void(); + self->WriteUInt64(static_cast(value)); +} + void Lua_Packet::WriteInt64(int offset, int64 value) { Lua_Safe_Call_Void(); @@ -121,6 +151,11 @@ void Lua_Packet::WriteInt64(int offset, int64 value) { } } +void Lua_Packet::WriteFloat(float value) { + Lua_Safe_Call_Void(); + self->WriteFloat(value); +} + void Lua_Packet::WriteFloat(int offset, float value) { Lua_Safe_Call_Void(); @@ -129,6 +164,11 @@ void Lua_Packet::WriteFloat(int offset, float value) { } } +void Lua_Packet::WriteDouble(double value) { + Lua_Safe_Call_Void(); + self->WriteDouble(value); +} + void Lua_Packet::WriteDouble(int offset, double value) { Lua_Safe_Call_Void(); @@ -137,6 +177,11 @@ void Lua_Packet::WriteDouble(int offset, double value) { } } +void Lua_Packet::WriteString(std::string value) { + Lua_Safe_Call_Void(); + self->WriteString(value.c_str()); +} + void Lua_Packet::WriteString(int offset, std::string value) { Lua_Safe_Call_Void(); @@ -146,6 +191,11 @@ void Lua_Packet::WriteString(int offset, std::string value) { } } +void Lua_Packet::WriteFixedLengthString(std::string value) { + Lua_Safe_Call_Void(); + self->WriteLengthString(static_cast(value.size()), value.c_str()); +} + void Lua_Packet::WriteFixedLengthString(int offset, std::string value, int string_length) { Lua_Safe_Call_Void(); @@ -283,14 +333,24 @@ luabind::scope lua_register_packet() { .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) - .def("WriteInt64", &Lua_Packet::WriteInt64) - .def("WriteFloat", &Lua_Packet::WriteFloat) - .def("WriteDouble", &Lua_Packet::WriteDouble) - .def("WriteString", &Lua_Packet::WriteString) - .def("WriteFixedLengthString", &Lua_Packet::WriteFixedLengthString) + .def("GetWritePosition", &Lua_Packet::GetWritePosition) + .def("SetWritePosition", &Lua_Packet::SetWritePosition) + .def("WriteInt8", (void(Lua_Packet::*)(int))&Lua_Packet::WriteInt8) + .def("WriteInt8", (void(Lua_Packet::*)(int, int))&Lua_Packet::WriteInt8) + .def("WriteInt16", (void(Lua_Packet::*)(int))&Lua_Packet::WriteInt16) + .def("WriteInt16", (void(Lua_Packet::*)(int, int))&Lua_Packet::WriteInt16) + .def("WriteInt32", (void(Lua_Packet::*)(int))&Lua_Packet::WriteInt32) + .def("WriteInt32", (void(Lua_Packet::*)(int, int))&Lua_Packet::WriteInt32) + .def("WriteInt64", (void(Lua_Packet::*)(int64))&Lua_Packet::WriteInt64) + .def("WriteInt64", (void(Lua_Packet::*)(int, int64))&Lua_Packet::WriteInt64) + .def("WriteFloat", (void(Lua_Packet::*)(float))&Lua_Packet::WriteFloat) + .def("WriteFloat", (void(Lua_Packet::*)(int, float))&Lua_Packet::WriteFloat) + .def("WriteDouble", (void(Lua_Packet::*)(double))&Lua_Packet::WriteDouble) + .def("WriteDouble", (void(Lua_Packet::*)(int, double))&Lua_Packet::WriteDouble) + .def("WriteString", (void(Lua_Packet::*)(std::string))&Lua_Packet::WriteString) + .def("WriteString", (void(Lua_Packet::*)(int, std::string))&Lua_Packet::WriteString) + .def("WriteFixedLengthString", (void(Lua_Packet::*)(std::string))&Lua_Packet::WriteFixedLengthString) + .def("WriteFixedLengthString", (void(Lua_Packet::*)(int, std::string, int))&Lua_Packet::WriteFixedLengthString) .def("ReadInt8", &Lua_Packet::ReadInt8) .def("ReadInt16", &Lua_Packet::ReadInt16) .def("ReadInt32", &Lua_Packet::ReadInt32) diff --git a/zone/lua_packet.h b/zone/lua_packet.h index a3c5ac553..9558b00f7 100644 --- a/zone/lua_packet.h +++ b/zone/lua_packet.h @@ -31,13 +31,23 @@ public: void SetOpcode(int op); int GetRawOpcode(); void SetRawOpcode(int op); + int GetWritePosition(); + void SetWritePosition(int offset); + void WriteInt8(int value); void WriteInt8(int offset, int value); + void WriteInt16(int value); void WriteInt16(int offset, int value); + void WriteInt32(int value); void WriteInt32(int offset, int value); + void WriteInt64(int64 value); void WriteInt64(int offset, int64 value); + void WriteFloat(float value); void WriteFloat(int offset, float value); + void WriteDouble(double value); void WriteDouble(int offset, double value); + void WriteString(std::string value); void WriteString(int offset, std::string value); + void WriteFixedLengthString(std::string value); void WriteFixedLengthString(int offset, std::string value, int string_length); int ReadInt8(int offset); int ReadInt16(int offset); From 7458b5f32f79856bd5b46fed6bd02a9c8b79b385 Mon Sep 17 00:00:00 2001 From: TurmoilToad Date: Mon, 24 May 2021 21:23:50 -0400 Subject: [PATCH 051/624] [Docs] Create CODE_OF_CONDUCT.md (#1360) --- CODE_OF_CONDUCT.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..22dc230bf --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,22 @@ +We expect contributors and community members to act professionally and respectfully, and we expect our forums and Discord channels to be dignified environments that expand the community and enhance the learning experience for new members. + +Specifically: + +* Respect people, their ideas, and their work. +* Be kind. Be courteous. Be welcoming. +* Listen. Consider and acknowledge people's points before responding. +* Be respectful of differing viewpoints and experience levels. +* Accept constructive criticism and work together toward decisions. +* Focus on what is best for the community and users. + +Examples of unacceptable behavior by participants include: + +* The use of violent threats, trolling, insulting/derogatory comments, abusive or discriminatory language, or personal attacks. +* Public or private harassment. +* Publishing others' private information, such as a physical or electronic address, without explicit permission. +* Conduct which could reasonably be considered inappropriate in a professional setting. +* Advocating for or encouraging any of the above behaviors. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, forum posts, Discord messages, and other contributions that are not aligned with this code of conduct. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project. From e8b94a11f1d4b1115e6738297079dcfe3bcc68f7 Mon Sep 17 00:00:00 2001 From: splose Date: Mon, 24 May 2021 21:29:27 -0400 Subject: [PATCH 052/624] [Rules] Add rule to allow you to cast invis on already invis'd players (#1361) --- common/ruletypes.h | 1 + zone/spells.cpp | 47 ++++++++++++++++++++++++---------------------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 22ea02741..524898fa5 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -369,6 +369,7 @@ RULE_BOOL(Spells, OldRainTargets, false, "Use old incorrectly implemented maximu RULE_BOOL(Spells, NPCSpellPush, false, "Enable spell push on NPCs") RULE_BOOL(Spells, July242002PetResists, true, "Enable Pets using PCs resist change from July 24 2002") RULE_INT(Spells, AOEMaxTargets, 0, "Max number of targets a Targeted AOE spell can cast on. Set to 0 for no limit.") +RULE_BOOL(Spells, AllowDoubleInvis, false, "Allows you to cast invisibility spells on a player that is already invisible") RULE_CATEGORY_END() RULE_CATEGORY(Combat) diff --git a/zone/spells.cpp b/zone/spells.cpp index 13c7ff1cf..f7536cd04 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3607,33 +3607,36 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r // Prevent double invising, which made you uninvised // Not sure if all 3 should be stacking - if(IsEffectInSpell(spell_id, SE_Invisibility)) - { - if(spelltar->invisible) - { - spelltar->MessageString(Chat::SpellFailure, ALREADY_INVIS, GetCleanName()); - safe_delete(action_packet); - return false; - } - } - if(IsEffectInSpell(spell_id, SE_InvisVsUndead)) - { - if(spelltar->invisible_undead) + if (!RuleB(Spells, AllowDoubleInvis)) { + if (IsEffectInSpell(spell_id, SE_Invisibility)) { - spelltar->MessageString(Chat::SpellFailure, ALREADY_INVIS, GetCleanName()); - safe_delete(action_packet); - return false; + if (spelltar->invisible) + { + spelltar->MessageString(Chat::SpellFailure, ALREADY_INVIS, GetCleanName()); + safe_delete(action_packet); + return false; + } } - } - if(IsEffectInSpell(spell_id, SE_InvisVsAnimals)) - { - if(spelltar->invisible_animals) + if (IsEffectInSpell(spell_id, SE_InvisVsUndead)) { - spelltar->MessageString(Chat::SpellFailure, ALREADY_INVIS, GetCleanName()); - safe_delete(action_packet); - return false; + if (spelltar->invisible_undead) + { + spelltar->MessageString(Chat::SpellFailure, ALREADY_INVIS, GetCleanName()); + safe_delete(action_packet); + return false; + } + } + + if (IsEffectInSpell(spell_id, SE_InvisVsAnimals)) + { + if (spelltar->invisible_animals) + { + spelltar->MessageString(Chat::SpellFailure, ALREADY_INVIS, GetCleanName()); + safe_delete(action_packet); + return false; + } } } From 15328196e2f6e36200df77f60e4d8efa3a3f0ca8 Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Mon, 24 May 2021 22:14:32 -0400 Subject: [PATCH 053/624] [Expeditions] Store members on dynamic zone (#1358) This moves members from expeditions so other systems can use them Replace expedition_members table with dynamic_zone_members Move 'EnableInDynamicZoneStatus' rule to DynamicZone namespace Modify #dz list to show dz members (not instance players) and type name Move various queries to repository methods --- common/CMakeLists.txt | 4 +- common/database_instances.cpp | 9 +- common/database_schema.h | 2 +- common/dynamic_zone_base.cpp | 159 ++++++++++- common/dynamic_zone_base.h | 27 +- common/expedition_base.cpp | 90 ------- common/expedition_base.h | 18 +- ...=> base_dynamic_zone_members_repository.h} | 108 ++++---- .../dynamic_zone_members_repository.h | 249 ++++++++++++++++++ .../repositories/dynamic_zones_repository.h | 9 +- .../expedition_members_repository.h | 132 ---------- common/repositories/expeditions_repository.h | 39 ++- .../instance_list_player_repository.h | 28 ++ common/ruletypes.h | 2 +- common/version.h | 2 +- utils/sql/db_update_manifest.txt | 1 + .../2021_02_12_dynamic_zone_members.sql | 11 + world/dynamic_zone.cpp | 7 +- world/dynamic_zone.h | 2 +- world/expedition.cpp | 35 ++- world/expedition_database.cpp | 28 +- world/expedition_state.cpp | 20 +- zone/client_packet.cpp | 2 +- zone/command.cpp | 6 +- zone/expedition.cpp | 129 ++++----- zone/expedition.h | 1 - zone/expedition_database.cpp | 128 --------- zone/expedition_database.h | 7 - zone/expedition_request.h | 4 +- zone/lua_expedition.cpp | 4 +- zone/perl_expedition.cpp | 5 +- 31 files changed, 683 insertions(+), 585 deletions(-) rename common/repositories/base/{base_expedition_members_repository.h => base_dynamic_zone_members_repository.h} (59%) create mode 100644 common/repositories/dynamic_zone_members_repository.h delete mode 100644 common/repositories/expedition_members_repository.h create mode 100644 utils/sql/git/required/2021_02_12_dynamic_zone_members.sql diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 08db08585..084ac9c4f 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -177,10 +177,10 @@ SET(repositories repositories/base/base_discovered_items_repository.h repositories/base/base_doors_repository.h repositories/base/base_dynamic_zones_repository.h + repositories/base/base_dynamic_zone_members_repository.h repositories/base/base_eventlog_repository.h repositories/base/base_expeditions_repository.h repositories/base/base_expedition_lockouts_repository.h - repositories/base/base_expedition_members_repository.h repositories/base/base_faction_base_data_repository.h repositories/base/base_faction_list_repository.h repositories/base/base_faction_list_mod_repository.h @@ -341,10 +341,10 @@ SET(repositories repositories/discovered_items_repository.h repositories/doors_repository.h repositories/dynamic_zones_repository.h + repositories/dynamic_zone_members_repository.h repositories/eventlog_repository.h repositories/expeditions_repository.h repositories/expedition_lockouts_repository.h - repositories/expedition_members_repository.h repositories/faction_base_data_repository.h repositories/faction_list_repository.h repositories/faction_list_mod_repository.h diff --git a/common/database_instances.cpp b/common/database_instances.cpp index 0c4be4bd6..75edd9fd3 100644 --- a/common/database_instances.cpp +++ b/common/database_instances.cpp @@ -20,6 +20,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "../common/rulesys.h" #include "../common/string_util.h" #include "../common/timer.h" +#include "../common/repositories/dynamic_zone_members_repository.h" +#include "../common/repositories/dynamic_zones_repository.h" #include "database.h" @@ -493,8 +495,8 @@ void Database::DeleteInstance(uint16 instance_id) query = StringFormat("DELETE FROM spawn_condition_values WHERE instance_id=%u", instance_id); QueryDatabase(query); - query = fmt::format("DELETE FROM dynamic_zones WHERE instance_id={}", instance_id); - QueryDatabase(query); + DynamicZoneMembersRepository::DeleteByInstance(*this, instance_id); + DynamicZonesRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id)); BuryCorpsesInInstance(instance_id); } @@ -585,7 +587,8 @@ void Database::PurgeExpiredInstances() QueryDatabase(fmt::format("DELETE FROM respawn_times WHERE instance_id IN ({})", imploded_instance_ids)); QueryDatabase(fmt::format("DELETE FROM spawn_condition_values WHERE instance_id IN ({})", imploded_instance_ids)); QueryDatabase(fmt::format("UPDATE character_corpses SET is_buried = 1, instance_id = 0 WHERE instance_id IN ({})", imploded_instance_ids)); - QueryDatabase(fmt::format("DELETE FROM dynamic_zones WHERE instance_id IN ({})", imploded_instance_ids)); + DynamicZoneMembersRepository::DeleteByManyInstances(*this, imploded_instance_ids); + DynamicZonesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", imploded_instance_ids)); } void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration) diff --git a/common/database_schema.h b/common/database_schema.h index 8d8e81441..b338a9bc5 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -309,10 +309,10 @@ namespace DatabaseSchema { "banned_ips", "bug_reports", "bugs", + "dynamic_zone_members", "dynamic_zones", "eventlog", "expedition_lockouts", - "expedition_members", "expeditions", "gm_ips", "group_id", diff --git a/common/dynamic_zone_base.cpp b/common/dynamic_zone_base.cpp index 8e29722d3..f3a499ee8 100644 --- a/common/dynamic_zone_base.cpp +++ b/common/dynamic_zone_base.cpp @@ -3,6 +3,7 @@ #include "eqemu_logsys.h" #include "repositories/instance_list_repository.h" #include "repositories/instance_list_player_repository.h" +#include "rulesys.h" #include "servertalk.h" DynamicZoneBase::DynamicZoneBase(DynamicZonesRepository::DynamicZoneInstance&& entry) @@ -99,6 +100,13 @@ void DynamicZoneBase::LoadRepositoryResult(DynamicZonesRepository::DynamicZoneIn m_expire_time = m_start_time + m_duration; } +void DynamicZoneBase::AddMemberFromRepositoryResult( + DynamicZoneMembersRepository::MemberWithName&& entry) +{ + auto status = DynamicZoneMemberStatus::Unknown; + AddInternalMember({ entry.character_id, std::move(entry.character_name), status }); +} + uint32_t DynamicZoneBase::SaveToDatabase() { LogDynamicZonesDetail("Saving dz instance [{}] to database", m_instance_id); @@ -131,12 +139,14 @@ uint32_t DynamicZoneBase::SaveToDatabase() void DynamicZoneBase::AddCharacter(uint32_t character_id) { + DynamicZoneMembersRepository::AddMember(GetDatabase(), m_id, character_id); GetDatabase().AddClientToInstance(m_instance_id, character_id); SendInstanceAddRemoveCharacter(character_id, false); // stops client kick timer } void DynamicZoneBase::RemoveCharacter(uint32_t character_id) { + DynamicZoneMembersRepository::RemoveMember(GetDatabase(), m_id, character_id); GetDatabase().RemoveClientFromInstance(m_instance_id, character_id); SendInstanceAddRemoveCharacter(character_id, true); // start client kick timer } @@ -153,24 +163,35 @@ void DynamicZoneBase::RemoveAllCharacters(bool enable_removal_timers) SendInstanceRemoveAllCharacters(); } + DynamicZoneMembersRepository::RemoveAllMembers(GetDatabase(), m_id); GetDatabase().RemoveClientsFromInstance(GetInstanceID()); } -void DynamicZoneBase::SaveInstanceMembersToDatabase(const std::vector& character_ids) +void DynamicZoneBase::SaveMembers(const std::vector& members) { - LogDynamicZonesDetail("Saving [{}] members for instance [{}]", character_ids.size(), m_instance_id); + LogDynamicZonesDetail("Saving [{}] member(s) for dz [{}]", members.size(), m_id); + m_members = members; + + // the lower level instance_list_players needs to be kept updated as well + std::vector insert_members; std::vector insert_players; - - for (const auto& character_id : character_ids) + for (const auto& member : m_members) { - InstanceListPlayerRepository::InstanceListPlayer entry{}; - entry.id = static_cast(m_instance_id); - entry.charid = static_cast(character_id); - insert_players.emplace_back(entry); + DynamicZoneMembersRepository::DynamicZoneMembers member_entry{}; + member_entry.dynamic_zone_id = m_id; + member_entry.character_id = member.id; + member_entry.is_current_member = true; + insert_members.emplace_back(member_entry); + + InstanceListPlayerRepository::InstanceListPlayer player_entry; + player_entry.id = static_cast(m_instance_id); + player_entry.charid = static_cast(member.id); + insert_players.emplace_back(player_entry); } - InstanceListPlayerRepository::InsertMany(GetDatabase(), insert_players); + DynamicZoneMembersRepository::InsertOrUpdateMany(GetDatabase(), insert_members); + InstanceListPlayerRepository::InsertOrUpdateMany(GetDatabase(), insert_players); } void DynamicZoneBase::SetCompass(const DynamicZoneLocation& location, bool update_db) @@ -287,3 +308,123 @@ std::unique_ptr DynamicZoneBase::CreateServerDzLocationPacket( return pack; } + +uint32_t DynamicZoneBase::GetDatabaseMemberCount() +{ + return DynamicZoneMembersRepository::GetCountWhere(GetDatabase(), + fmt::format("dynamic_zone_id = {} AND is_current_member = TRUE", m_id)); +} + +bool DynamicZoneBase::HasDatabaseMember(uint32_t character_id) +{ + if (character_id == 0) + { + return false; + } + + auto entries = DynamicZoneMembersRepository::GetWhere(GetDatabase(), fmt::format( + "dynamic_zone_id = {} AND character_id = {} AND is_current_member = TRUE", + m_id, character_id + )); + + return entries.size() != 0; +} + +void DynamicZoneBase::AddInternalMember(const DynamicZoneMember& member) +{ + if (!HasMember(member.id)) + { + m_members.emplace_back(member); + } +} + +void DynamicZoneBase::RemoveInternalMember(uint32_t character_id) +{ + m_members.erase(std::remove_if(m_members.begin(), m_members.end(), + [&](const DynamicZoneMember& member) { return member.id == character_id; } + ), m_members.end()); +} + +bool DynamicZoneBase::HasMember(uint32_t character_id) +{ + return std::any_of(m_members.begin(), m_members.end(), + [&](const DynamicZoneMember& member) { return member.id == character_id; }); +} + +bool DynamicZoneBase::HasMember(const std::string& character_name) +{ + return std::any_of(m_members.begin(), m_members.end(), + [&](const DynamicZoneMember& member) { + return strcasecmp(member.name.c_str(), character_name.c_str()) == 0; + }); +} + +DynamicZoneMember DynamicZoneBase::GetMemberData(uint32_t character_id) +{ + auto it = std::find_if(m_members.begin(), m_members.end(), + [&](const DynamicZoneMember& member) { return member.id == character_id; }); + + DynamicZoneMember member_data; + if (it != m_members.end()) + { + member_data = *it; + } + return member_data; +} + +DynamicZoneMember DynamicZoneBase::GetMemberData(const std::string& character_name) +{ + auto it = std::find_if(m_members.begin(), m_members.end(), + [&](const DynamicZoneMember& member) { + return strcasecmp(member.name.c_str(), character_name.c_str()) == 0; + }); + + DynamicZoneMember member_data; + if (it != m_members.end()) + { + member_data = *it; + } + return member_data; +} + +bool DynamicZoneBase::SetInternalMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status) +{ + if (status == DynamicZoneMemberStatus::InDynamicZone && !RuleB(DynamicZone, EnableInDynamicZoneStatus)) + { + status = DynamicZoneMemberStatus::Online; + } + + if (character_id == m_leader.id) + { + m_leader.status = status; + } + + auto it = std::find_if(m_members.begin(), m_members.end(), + [&](const DynamicZoneMember& member) { return member.id == character_id; }); + + if (it != m_members.end() && it->status != status) + { + it->status = status; + return true; + } + + return false; +} + +std::string DynamicZoneBase::GetDynamicZoneTypeName(DynamicZoneType dz_type) +{ + switch (dz_type) + { + case DynamicZoneType::Expedition: + return "Expedition"; + case DynamicZoneType::Tutorial: + return "Tutorial"; + case DynamicZoneType::Task: + return "Task"; + case DynamicZoneType::Mission: + return "Mission"; + case DynamicZoneType::Quest: + return "Quest"; + } + return "Unknown"; +} diff --git a/common/dynamic_zone_base.h b/common/dynamic_zone_base.h index 5a84fa8fa..bc325532e 100644 --- a/common/dynamic_zone_base.h +++ b/common/dynamic_zone_base.h @@ -3,6 +3,7 @@ #include "eq_constants.h" #include "repositories/dynamic_zones_repository.h" +#include "repositories/dynamic_zone_members_repository.h" #include #include #include @@ -56,27 +57,42 @@ public: DynamicZoneBase(DynamicZoneType type) : m_type(type) {} DynamicZoneBase(DynamicZonesRepository::DynamicZoneInstance&& entry); + static std::string GetDynamicZoneTypeName(DynamicZoneType dz_type); + virtual void SetSecondsRemaining(uint32_t seconds_remaining) = 0; uint64_t GetExpireTime() const { return std::chrono::system_clock::to_time_t(m_expire_time); } uint32_t GetID() const { return m_id; } uint16_t GetInstanceID() const { return static_cast(m_instance_id); } uint32_t GetMaxPlayers() const { return m_max_players; } + uint32_t GetMemberCount() const { return static_cast(m_members.size()); } uint32_t GetMinPlayers() const { return m_min_players; } uint32_t GetSecondsRemaining() const; uint16_t GetZoneID() const { return static_cast(m_zone_id); } uint32_t GetZoneIndex() const { return (m_instance_id << 16) | (m_zone_id & 0xffff); } uint32_t GetZoneVersion() const { return m_zone_version; } DynamicZoneType GetType() const { return m_type; } - const std::string& GetLeaderName() const { return m_leader_name; } + const std::string& GetLeaderName() const { return m_leader.name; } const std::string& GetName() const { return m_name; } + const DynamicZoneMember& GetLeader() const { return m_leader; } + const std::vector& GetMembers() const { return m_members; } const DynamicZoneLocation& GetCompassLocation() const { return m_compass; } const DynamicZoneLocation& GetSafeReturnLocation() const { return m_safereturn; } const DynamicZoneLocation& GetZoneInLocation() const { return m_zonein; } std::chrono::system_clock::duration GetDurationRemaining() const { return m_expire_time - std::chrono::system_clock::now(); } void AddCharacter(uint32_t character_id); + void AddInternalMember(const DynamicZoneMember& member); + void AddMemberFromRepositoryResult(DynamicZoneMembersRepository::MemberWithName&& entry); + void ClearInternalMembers() { m_members.clear(); } uint32_t Create(); + uint32_t GetDatabaseMemberCount(); + DynamicZoneMember GetMemberData(uint32_t character_id); + DynamicZoneMember GetMemberData(const std::string& character_name); + bool HasDatabaseMember(uint32_t character_id); + bool HasMember(uint32_t character_id); + bool HasMember(const std::string& character_name); + bool HasMembers() const { return !m_members.empty(); } bool HasZoneInLocation() const { return m_has_zonein; } bool IsExpired() const { return m_expire_time < std::chrono::system_clock::now(); } bool IsInstanceID(uint32_t instance_id) const { return (m_instance_id != 0 && m_instance_id == instance_id); } @@ -84,10 +100,12 @@ public: bool IsSameDz(uint32_t zone_id, uint32_t instance_id) const { return zone_id == m_zone_id && instance_id == m_instance_id; } void RemoveAllCharacters(bool enable_removal_timers = true); void RemoveCharacter(uint32_t character_id); - void SaveInstanceMembersToDatabase(const std::vector& character_ids); + void RemoveInternalMember(uint32_t character_id); + void SaveMembers(const std::vector& members); void SetCompass(const DynamicZoneLocation& location, bool update_db = false); void SetCompass(uint32_t zone_id, float x, float y, float z, bool update_db = false); - void SetLeaderName(const std::string& leader_name) { m_leader_name = leader_name; } + bool SetInternalMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status); + void SetLeader(const DynamicZoneMember& leader) { m_leader = leader; } void SetMaxPlayers(uint32_t max_players) { m_max_players = max_players; } void SetMinPlayers(uint32_t min_players) { m_min_players = min_players; } void SetName(const std::string& name) { m_name = name; } @@ -122,7 +140,7 @@ protected: bool m_never_expires = false; bool m_has_zonein = false; std::string m_name; - std::string m_leader_name; + DynamicZoneMember m_leader; DynamicZoneType m_type{ DynamicZoneType::None }; DynamicZoneLocation m_compass; DynamicZoneLocation m_safereturn; @@ -130,6 +148,7 @@ protected: std::chrono::seconds m_duration; std::chrono::time_point m_start_time; std::chrono::time_point m_expire_time; + std::vector m_members; }; #endif diff --git a/common/expedition_base.cpp b/common/expedition_base.cpp index a5f890204..af961e4a9 100644 --- a/common/expedition_base.cpp +++ b/common/expedition_base.cpp @@ -1,6 +1,5 @@ #include "expedition_base.h" #include "repositories/expeditions_repository.h" -#include "rulesys.h" ExpeditionBase::ExpeditionBase(uint32_t id, const std::string& uuid, const std::string& expedition_name, const DynamicZoneMember& leader @@ -22,92 +21,3 @@ void ExpeditionBase::LoadRepositoryResult(ExpeditionsRepository::ExpeditionWithL m_leader.id = entry.leader_id; m_leader.name = std::move(entry.leader_name); } - -void ExpeditionBase::AddMemberFromRepositoryResult( - ExpeditionMembersRepository::MemberWithName&& entry) -{ - auto status = DynamicZoneMemberStatus::Unknown; - AddInternalMember({ entry.character_id, std::move(entry.character_name), status }); -} - -void ExpeditionBase::AddInternalMember(const DynamicZoneMember& member) -{ - if (!HasMember(member.id)) - { - m_members.emplace_back(member); - } -} - -void ExpeditionBase::RemoveInternalMember(uint32_t character_id) -{ - m_members.erase(std::remove_if(m_members.begin(), m_members.end(), - [&](const DynamicZoneMember& member) { return member.id == character_id; } - ), m_members.end()); -} - - -bool ExpeditionBase::HasMember(uint32_t character_id) -{ - return std::any_of(m_members.begin(), m_members.end(), [&](const DynamicZoneMember& member) { - return member.id == character_id; - }); -} - -bool ExpeditionBase::HasMember(const std::string& character_name) -{ - return std::any_of(m_members.begin(), m_members.end(), [&](const DynamicZoneMember& member) { - return (strcasecmp(member.name.c_str(), character_name.c_str()) == 0); - }); -} - -DynamicZoneMember ExpeditionBase::GetMemberData(uint32_t character_id) -{ - auto it = std::find_if(m_members.begin(), m_members.end(), [&](const DynamicZoneMember& member) { - return member.id == character_id; - }); - - DynamicZoneMember member_data; - if (it != m_members.end()) - { - member_data = *it; - } - return member_data; -} - -DynamicZoneMember ExpeditionBase::GetMemberData(const std::string& character_name) -{ - auto it = std::find_if(m_members.begin(), m_members.end(), [&](const DynamicZoneMember& member) { - return (strcasecmp(member.name.c_str(), character_name.c_str()) == 0); - }); - - DynamicZoneMember member_data; - if (it != m_members.end()) - { - member_data = *it; - } - return member_data; -} - -bool ExpeditionBase::SetInternalMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status) -{ - if (status == DynamicZoneMemberStatus::InDynamicZone && !RuleB(Expedition, EnableInDynamicZoneStatus)) - { - status = DynamicZoneMemberStatus::Online; - } - - if (character_id == m_leader.id) - { - m_leader.status = status; - } - - auto it = std::find_if(m_members.begin(), m_members.end(), - [&](const DynamicZoneMember& member) { return member.id == character_id; }); - - if (it != m_members.end() && it->status != status) - { - it->status = status; - return true; - } - - return false; -} diff --git a/common/expedition_base.h b/common/expedition_base.h index e93c707b5..cb6f6a591 100644 --- a/common/expedition_base.h +++ b/common/expedition_base.h @@ -3,10 +3,8 @@ #include "dynamic_zone_base.h" #include "repositories/expeditions_repository.h" -#include "repositories/expedition_members_repository.h" #include #include -#include class ExpeditionBase { @@ -19,38 +17,24 @@ public: uint32_t GetID() const { return m_id; } uint32_t GetLeaderID() const { return m_leader.id; } - uint32_t GetMemberCount() const { return static_cast(m_members.size()); } const std::string& GetName() const { return m_expedition_name; } const std::string& GetLeaderName() const { return m_leader.name; } const std::string& GetUUID() const { return m_uuid; } - const std::vector& GetMembers() const { return m_members; } - - void AddInternalMember(const DynamicZoneMember& member); - void ClearInternalMembers() { m_members.clear(); } - bool HasMember(const std::string& character_name); - bool HasMember(uint32_t character_id); - bool IsEmpty() const { return m_members.empty(); } - void RemoveInternalMember(uint32_t character_id); - bool SetInternalMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status); + const DynamicZoneMember& GetLeader() const { return m_leader; } void LoadRepositoryResult(ExpeditionsRepository::ExpeditionWithLeader&& entry); - void AddMemberFromRepositoryResult(ExpeditionMembersRepository::MemberWithName&& entry); protected: ExpeditionBase() = default; ExpeditionBase(uint32_t id, const std::string& uuid, const std::string& expedition_name, const DynamicZoneMember& leader); - DynamicZoneMember GetMemberData(uint32_t character_id); - DynamicZoneMember GetMemberData(const std::string& character_name); - uint32_t m_id = 0; bool m_is_locked = false; bool m_add_replay_on_join = true; std::string m_uuid; std::string m_expedition_name; DynamicZoneMember m_leader; - std::vector m_members; }; #endif diff --git a/common/repositories/base/base_expedition_members_repository.h b/common/repositories/base/base_dynamic_zone_members_repository.h similarity index 59% rename from common/repositories/base/base_expedition_members_repository.h rename to common/repositories/base/base_dynamic_zone_members_repository.h index dde0be588..07fee6723 100644 --- a/common/repositories/base/base_expedition_members_repository.h +++ b/common/repositories/base/base_dynamic_zone_members_repository.h @@ -4,22 +4,22 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ -#ifndef EQEMU_BASE_EXPEDITION_MEMBERS_REPOSITORY_H -#define EQEMU_BASE_EXPEDITION_MEMBERS_REPOSITORY_H +#ifndef EQEMU_BASE_DYNAMIC_ZONE_MEMBERS_REPOSITORY_H +#define EQEMU_BASE_DYNAMIC_ZONE_MEMBERS_REPOSITORY_H #include "../../database.h" #include "../../string_util.h" -class BaseExpeditionMembersRepository { +class BaseDynamicZoneMembersRepository { public: - struct ExpeditionMembers { + struct DynamicZoneMembers { int id; - int expedition_id; + int dynamic_zone_id; int character_id; int is_current_member; }; @@ -33,7 +33,7 @@ public: { return { "id", - "expedition_id", + "dynamic_zone_id", "character_id", "is_current_member", }; @@ -46,7 +46,7 @@ public: static std::string TableName() { - return std::string("expedition_members"); + return std::string("dynamic_zone_members"); } static std::string BaseSelect() @@ -67,51 +67,51 @@ public: ); } - static ExpeditionMembers NewEntity() + static DynamicZoneMembers NewEntity() { - ExpeditionMembers entry{}; + DynamicZoneMembers entry{}; entry.id = 0; - entry.expedition_id = 0; + entry.dynamic_zone_id = 0; entry.character_id = 0; entry.is_current_member = 1; return entry; } - static ExpeditionMembers GetExpeditionMembersEntry( - const std::vector &expedition_memberss, - int expedition_members_id + static DynamicZoneMembers GetDynamicZoneMembersEntry( + const std::vector &dynamic_zone_memberss, + int dynamic_zone_members_id ) { - for (auto &expedition_members : expedition_memberss) { - if (expedition_members.id == expedition_members_id) { - return expedition_members; + for (auto &dynamic_zone_members : dynamic_zone_memberss) { + if (dynamic_zone_members.id == dynamic_zone_members_id) { + return dynamic_zone_members; } } return NewEntity(); } - static ExpeditionMembers FindOne( + static DynamicZoneMembers FindOne( Database& db, - int expedition_members_id + int dynamic_zone_members_id ) { auto results = db.QueryDatabase( fmt::format( "{} WHERE id = {} LIMIT 1", BaseSelect(), - expedition_members_id + dynamic_zone_members_id ) ); auto row = results.begin(); if (results.RowCount() == 1) { - ExpeditionMembers entry{}; + DynamicZoneMembers entry{}; entry.id = atoi(row[0]); - entry.expedition_id = atoi(row[1]); + entry.dynamic_zone_id = atoi(row[1]); entry.character_id = atoi(row[2]); entry.is_current_member = atoi(row[3]); @@ -123,7 +123,7 @@ public: static int DeleteOne( Database& db, - int expedition_members_id + int dynamic_zone_members_id ) { auto results = db.QueryDatabase( @@ -131,7 +131,7 @@ public: "DELETE FROM {} WHERE {} = {}", TableName(), PrimaryKey(), - expedition_members_id + dynamic_zone_members_id ) ); @@ -140,16 +140,16 @@ public: static int UpdateOne( Database& db, - ExpeditionMembers expedition_members_entry + DynamicZoneMembers dynamic_zone_members_entry ) { std::vector update_values; auto columns = Columns(); - update_values.push_back(columns[1] + " = " + std::to_string(expedition_members_entry.expedition_id)); - update_values.push_back(columns[2] + " = " + std::to_string(expedition_members_entry.character_id)); - update_values.push_back(columns[3] + " = " + std::to_string(expedition_members_entry.is_current_member)); + update_values.push_back(columns[1] + " = " + std::to_string(dynamic_zone_members_entry.dynamic_zone_id)); + update_values.push_back(columns[2] + " = " + std::to_string(dynamic_zone_members_entry.character_id)); + update_values.push_back(columns[3] + " = " + std::to_string(dynamic_zone_members_entry.is_current_member)); auto results = db.QueryDatabase( fmt::format( @@ -157,24 +157,24 @@ public: TableName(), implode(", ", update_values), PrimaryKey(), - expedition_members_entry.id + dynamic_zone_members_entry.id ) ); return (results.Success() ? results.RowsAffected() : 0); } - static ExpeditionMembers InsertOne( + static DynamicZoneMembers InsertOne( Database& db, - ExpeditionMembers expedition_members_entry + DynamicZoneMembers dynamic_zone_members_entry ) { std::vector insert_values; - insert_values.push_back(std::to_string(expedition_members_entry.id)); - insert_values.push_back(std::to_string(expedition_members_entry.expedition_id)); - insert_values.push_back(std::to_string(expedition_members_entry.character_id)); - insert_values.push_back(std::to_string(expedition_members_entry.is_current_member)); + insert_values.push_back(std::to_string(dynamic_zone_members_entry.id)); + insert_values.push_back(std::to_string(dynamic_zone_members_entry.dynamic_zone_id)); + insert_values.push_back(std::to_string(dynamic_zone_members_entry.character_id)); + insert_values.push_back(std::to_string(dynamic_zone_members_entry.is_current_member)); auto results = db.QueryDatabase( fmt::format( @@ -185,29 +185,29 @@ public: ); if (results.Success()) { - expedition_members_entry.id = results.LastInsertedID(); - return expedition_members_entry; + dynamic_zone_members_entry.id = results.LastInsertedID(); + return dynamic_zone_members_entry; } - expedition_members_entry = NewEntity(); + dynamic_zone_members_entry = NewEntity(); - return expedition_members_entry; + return dynamic_zone_members_entry; } static int InsertMany( Database& db, - std::vector expedition_members_entries + std::vector dynamic_zone_members_entries ) { std::vector insert_chunks; - for (auto &expedition_members_entry: expedition_members_entries) { + for (auto &dynamic_zone_members_entry: dynamic_zone_members_entries) { std::vector insert_values; - insert_values.push_back(std::to_string(expedition_members_entry.id)); - insert_values.push_back(std::to_string(expedition_members_entry.expedition_id)); - insert_values.push_back(std::to_string(expedition_members_entry.character_id)); - insert_values.push_back(std::to_string(expedition_members_entry.is_current_member)); + insert_values.push_back(std::to_string(dynamic_zone_members_entry.id)); + insert_values.push_back(std::to_string(dynamic_zone_members_entry.dynamic_zone_id)); + insert_values.push_back(std::to_string(dynamic_zone_members_entry.character_id)); + insert_values.push_back(std::to_string(dynamic_zone_members_entry.is_current_member)); insert_chunks.push_back("(" + implode(",", insert_values) + ")"); } @@ -225,9 +225,9 @@ public: return (results.Success() ? results.RowsAffected() : 0); } - static std::vector All(Database& db) + static std::vector All(Database& db) { - std::vector all_entries; + std::vector all_entries; auto results = db.QueryDatabase( fmt::format( @@ -239,10 +239,10 @@ public: all_entries.reserve(results.RowCount()); for (auto row = results.begin(); row != results.end(); ++row) { - ExpeditionMembers entry{}; + DynamicZoneMembers entry{}; entry.id = atoi(row[0]); - entry.expedition_id = atoi(row[1]); + entry.dynamic_zone_id = atoi(row[1]); entry.character_id = atoi(row[2]); entry.is_current_member = atoi(row[3]); @@ -252,9 +252,9 @@ public: return all_entries; } - static std::vector GetWhere(Database& db, std::string where_filter) + static std::vector GetWhere(Database& db, std::string where_filter) { - std::vector all_entries; + std::vector all_entries; auto results = db.QueryDatabase( fmt::format( @@ -267,10 +267,10 @@ public: all_entries.reserve(results.RowCount()); for (auto row = results.begin(); row != results.end(); ++row) { - ExpeditionMembers entry{}; + DynamicZoneMembers entry{}; entry.id = atoi(row[0]); - entry.expedition_id = atoi(row[1]); + entry.dynamic_zone_id = atoi(row[1]); entry.character_id = atoi(row[2]); entry.is_current_member = atoi(row[3]); @@ -307,4 +307,4 @@ public: }; -#endif //EQEMU_BASE_EXPEDITION_MEMBERS_REPOSITORY_H +#endif //EQEMU_BASE_DYNAMIC_ZONE_MEMBERS_REPOSITORY_H diff --git a/common/repositories/dynamic_zone_members_repository.h b/common/repositories/dynamic_zone_members_repository.h new file mode 100644 index 000000000..2392b0965 --- /dev/null +++ b/common/repositories/dynamic_zone_members_repository.h @@ -0,0 +1,249 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) + * + * 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_DYNAMIC_ZONE_MEMBERS_REPOSITORY_H +#define EQEMU_DYNAMIC_ZONE_MEMBERS_REPOSITORY_H + +#include "../database.h" +#include "../string_util.h" +#include "base/base_dynamic_zone_members_repository.h" + +class DynamicZoneMembersRepository: public BaseDynamicZoneMembersRepository { +public: + + /** + * This file was auto generated and can be modified and extended upon + * + * Base repository methods are automatically + * generated in the "base" version of this repository. The base repository + * is immutable and to be left untouched, while methods in this class + * are used as extension methods for more specific persistence-layer + * accessors or mutators. + * + * Base Methods (Subject to be expanded upon in time) + * + * Note: Not all tables are designed appropriately to fit functionality with all base methods + * + * InsertOne + * UpdateOne + * DeleteOne + * FindOne + * GetWhere(std::string where_filter) + * DeleteWhere(std::string where_filter) + * InsertMany + * All + * + * Example custom methods in a repository + * + * DynamicZoneMembersRepository::GetByZoneAndVersion(int zone_id, int zone_version) + * DynamicZoneMembersRepository::GetWhereNeverExpires() + * DynamicZoneMembersRepository::GetWhereXAndY() + * DynamicZoneMembersRepository::DeleteWhereXAndY() + * + * Most of the above could be covered by base methods, but if you as a developer + * find yourself re-using logic for other parts of the code, its best to just make a + * method that can be re-used easily elsewhere especially if it can use a base repository + * method and encapsulate filters there + */ + + // Custom extended repository methods here + + struct MemberWithName { + uint32_t id; + uint32_t dynamic_zone_id; + uint32_t character_id; + int is_current_member; + std::string character_name; + }; + + static std::string SelectMembersWithNames() + { + return std::string(SQL( + SELECT + dynamic_zone_members.id, + dynamic_zone_members.dynamic_zone_id, + dynamic_zone_members.character_id, + dynamic_zone_members.is_current_member, + character_data.name + FROM dynamic_zone_members + INNER JOIN character_data ON dynamic_zone_members.character_id = character_data.id + )); + } + + static std::vector GetWithNames(Database& db, + const std::vector& dynamic_zone_ids) + { + if (dynamic_zone_ids.empty()) + { + return {}; + } + + std::vector all_entries; + + auto results = db.QueryDatabase(fmt::format(SQL( + {} + WHERE dynamic_zone_members.dynamic_zone_id IN ({}) + AND dynamic_zone_members.is_current_member = TRUE; + ), + SelectMembersWithNames(), + fmt::join(dynamic_zone_ids, ",") + )); + + if (results.Success()) + { + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) + { + MemberWithName entry{}; + + int col = 0; + entry.id = strtoul(row[col++], nullptr, 10); + entry.dynamic_zone_id = strtoul(row[col++], nullptr, 10); + entry.character_id = strtoul(row[col++], nullptr, 10); + entry.is_current_member = strtoul(row[col++], nullptr, 10); + entry.character_name = row[col++]; + + all_entries.emplace_back(std::move(entry)); + } + } + + return all_entries; + } + + static int DeleteByInstance(Database& db, int instance_id) + { + auto results = db.QueryDatabase(fmt::format(SQL( + DELETE dynamic_zone_members + FROM dynamic_zone_members + INNER JOIN dynamic_zones ON dynamic_zone_members.dynamic_zone_id = dynamic_zones.id + WHERE dynamic_zones.instance_id = {} + ), instance_id)); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int DeleteByManyInstances(Database& db, const std::string& joined_instance_ids) + { + auto results = db.QueryDatabase(fmt::format(SQL( + DELETE dynamic_zone_members + FROM dynamic_zone_members + INNER JOIN dynamic_zones ON dynamic_zone_members.dynamic_zone_id = dynamic_zones.id + WHERE dynamic_zones.instance_id IN ({}) + ), joined_instance_ids)); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int GetCountWhere(Database& db, const std::string& where_filter) + { + auto results = db.QueryDatabase(fmt::format( + "SELECT COUNT(*) FROM {} WHERE {};", TableName(), where_filter)); + + uint32_t count = 0; + if (results.Success() && results.RowCount() > 0) + { + auto row = results.begin(); + count = strtoul(row[0], nullptr, 10); + } + return count; + } + + static void AddMember(Database& db, uint32_t dynamic_zone_id, uint32_t character_id) + { + db.QueryDatabase(fmt::format(SQL( + INSERT INTO {} + (dynamic_zone_id, character_id) + VALUES + ({}, {}) + ON DUPLICATE KEY UPDATE is_current_member = TRUE; + ), + TableName(), + dynamic_zone_id, + character_id + )); + } + + static void RemoveMember(Database& db, uint32_t dynamic_zone_id, uint32_t character_id) + { + db.QueryDatabase(fmt::format(SQL( + UPDATE {} SET is_current_member = FALSE + WHERE dynamic_zone_id = {} AND character_id = {}; + ), + TableName(), dynamic_zone_id, character_id + )); + } + + static void RemoveAllMembers(Database& db, uint32_t dynamic_zone_id) + { + db.QueryDatabase(fmt::format(SQL( + UPDATE {} SET is_current_member = FALSE + WHERE dynamic_zone_id = {}; + ), + TableName(), dynamic_zone_id + )); + } + + static void RemoveAllMembers(Database& db, std::vector dynamic_zone_ids) + { + if (!dynamic_zone_ids.empty()) + { + db.QueryDatabase(fmt::format(SQL( + UPDATE {} SET is_current_member = FALSE + WHERE dynamic_zone_id IN ({}); + ), + TableName(), fmt::join(dynamic_zone_ids, ",") + )); + } + } + + static int InsertOrUpdateMany(Database& db, + const std::vector& dynamic_zone_members_entries) + { + std::vector insert_chunks; + + for (auto &dynamic_zone_members_entry: dynamic_zone_members_entries) + { + std::vector insert_values; + + insert_values.push_back(std::to_string(dynamic_zone_members_entry.id)); + insert_values.push_back(std::to_string(dynamic_zone_members_entry.dynamic_zone_id)); + insert_values.push_back(std::to_string(dynamic_zone_members_entry.character_id)); + insert_values.push_back(std::to_string(dynamic_zone_members_entry.is_current_member)); + + insert_chunks.push_back("(" + implode(",", insert_values) + ")"); + } + + std::vector insert_values; + + auto results = db.QueryDatabase( + fmt::format( + "INSERT INTO {} ({}) VALUES {} ON DUPLICATE KEY UPDATE is_current_member = TRUE;", + TableName(), + ColumnsRaw(), + implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } +}; + +#endif //EQEMU_DYNAMIC_ZONE_MEMBERS_REPOSITORY_H diff --git a/common/repositories/dynamic_zones_repository.h b/common/repositories/dynamic_zones_repository.h index 27fd406ca..24d944209 100644 --- a/common/repositories/dynamic_zones_repository.h +++ b/common/repositories/dynamic_zones_repository.h @@ -249,7 +249,7 @@ public: int version; uint32_t start_time; int duration; - int player_count; + int member_count; }; static std::string SelectDynamicZoneInstancePlayerCount() @@ -263,10 +263,11 @@ public: instance_list.version, instance_list.start_time, instance_list.duration, - COUNT(instance_list_player.id) member_count + COUNT(dynamic_zone_members.character_id) member_count FROM dynamic_zones INNER JOIN instance_list ON dynamic_zones.instance_id = instance_list.id - LEFT JOIN instance_list_player ON instance_list.id = instance_list_player.id + LEFT JOIN dynamic_zone_members ON dynamic_zones.id = dynamic_zone_members.dynamic_zone_id + AND dynamic_zone_members.is_current_member = TRUE GROUP BY instance_list.id ORDER BY dynamic_zones.id; )); @@ -293,7 +294,7 @@ public: entry.version = strtol(row[col++], nullptr, 10); entry.start_time = strtoul(row[col++], nullptr, 10); entry.duration = strtol(row[col++], nullptr, 10); - entry.player_count = strtol(row[col++], nullptr, 10); + entry.member_count = strtol(row[col++], nullptr, 10); all_entries.emplace_back(std::move(entry)); } diff --git a/common/repositories/expedition_members_repository.h b/common/repositories/expedition_members_repository.h deleted file mode 100644 index 55b5bddd7..000000000 --- a/common/repositories/expedition_members_repository.h +++ /dev/null @@ -1,132 +0,0 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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_EXPEDITION_MEMBERS_REPOSITORY_H -#define EQEMU_EXPEDITION_MEMBERS_REPOSITORY_H - -#include "../database.h" -#include "../string_util.h" -#include "base/base_expedition_members_repository.h" - -class ExpeditionMembersRepository: public BaseExpeditionMembersRepository { -public: - - /** - * This file was auto generated and can be modified and extended upon - * - * Base repository methods are automatically - * generated in the "base" version of this repository. The base repository - * is immutable and to be left untouched, while methods in this class - * are used as extension methods for more specific persistence-layer - * accessors or mutators. - * - * Base Methods (Subject to be expanded upon in time) - * - * Note: Not all tables are designed appropriately to fit functionality with all base methods - * - * InsertOne - * UpdateOne - * DeleteOne - * FindOne - * GetWhere(std::string where_filter) - * DeleteWhere(std::string where_filter) - * InsertMany - * All - * - * Example custom methods in a repository - * - * ExpeditionMembersRepository::GetByZoneAndVersion(int zone_id, int zone_version) - * ExpeditionMembersRepository::GetWhereNeverExpires() - * ExpeditionMembersRepository::GetWhereXAndY() - * ExpeditionMembersRepository::DeleteWhereXAndY() - * - * Most of the above could be covered by base methods, but if you as a developer - * find yourself re-using logic for other parts of the code, its best to just make a - * method that can be re-used easily elsewhere especially if it can use a base repository - * method and encapsulate filters there - */ - - // Custom extended repository methods here - - struct MemberWithName { - uint32_t id; - uint32_t expedition_id; - uint32_t character_id; - int is_current_member; - std::string character_name; - }; - - static std::string SelectMembersWithNames() - { - return std::string(SQL( - SELECT - expedition_members.id, - expedition_members.expedition_id, - expedition_members.character_id, - expedition_members.is_current_member, - character_data.name - FROM expedition_members - INNER JOIN character_data ON expedition_members.character_id = character_data.id - )); - } - - static std::vector GetWithNames(Database& db, - const std::vector& expedition_ids) - { - if (expedition_ids.empty()) - { - return {}; - } - - std::vector all_entries; - - auto results = db.QueryDatabase(fmt::format(SQL( - {} - WHERE expedition_members.expedition_id IN ({}) - AND expedition_members.is_current_member = TRUE; - ), - SelectMembersWithNames(), - fmt::join(expedition_ids, ",") - )); - - if (results.Success()) - { - all_entries.reserve(results.RowCount()); - - for (auto row = results.begin(); row != results.end(); ++row) - { - MemberWithName entry{}; - - int col = 0; - entry.id = strtoul(row[col++], nullptr, 10); - entry.expedition_id = strtoul(row[col++], nullptr, 10); - entry.character_id = strtoul(row[col++], nullptr, 10); - entry.is_current_member = strtoul(row[col++], nullptr, 10); - entry.character_name = row[col++]; - - all_entries.emplace_back(std::move(entry)); - } - } - - return all_entries; - } -}; - -#endif //EQEMU_EXPEDITION_MEMBERS_REPOSITORY_H diff --git a/common/repositories/expeditions_repository.h b/common/repositories/expeditions_repository.h index 47c821060..654b676b0 100644 --- a/common/repositories/expeditions_repository.h +++ b/common/repositories/expeditions_repository.h @@ -184,11 +184,11 @@ public: character_data.name, MAX(expeditions.id) FROM character_data - LEFT JOIN expedition_members - ON character_data.id = expedition_members.character_id - AND expedition_members.is_current_member = TRUE + LEFT JOIN dynamic_zone_members + ON character_data.id = dynamic_zone_members.character_id + AND dynamic_zone_members.is_current_member = TRUE LEFT JOIN expeditions - ON expedition_members.expedition_id = expeditions.id + ON dynamic_zone_members.dynamic_zone_id = expeditions.dynamic_zone_id WHERE character_data.name IN ({}) GROUP BY character_data.id ORDER BY FIELD(character_data.name, {}) @@ -214,6 +214,37 @@ public: return entries; } + + static uint32_t GetIDByMemberID(Database& db, uint32_t character_id) + { + if (character_id == 0) + { + return 0; + } + + uint32_t expedition_id = 0; + + auto results = db.QueryDatabase(fmt::format(SQL( + SELECT + expeditions.id + FROM expeditions + INNER JOIN dynamic_zone_members + ON expeditions.dynamic_zone_id = dynamic_zone_members.dynamic_zone_id + WHERE + dynamic_zone_members.character_id = {} + AND dynamic_zone_members.is_current_member = TRUE; + ), + character_id + )); + + if (results.Success() && results.RowCount() > 0) + { + auto row = results.begin(); + expedition_id = std::strtoul(row[0], nullptr, 10); + } + + return expedition_id; + } }; #endif //EQEMU_EXPEDITIONS_REPOSITORY_H diff --git a/common/repositories/instance_list_player_repository.h b/common/repositories/instance_list_player_repository.h index f865e5fd8..3a526a73e 100644 --- a/common/repositories/instance_list_player_repository.h +++ b/common/repositories/instance_list_player_repository.h @@ -65,6 +65,34 @@ public: // Custom extended repository methods here + static int InsertOrUpdateMany(Database& db, + const std::vector& instance_list_player_entries) + { + std::vector insert_chunks; + + for (auto &instance_list_player_entry: instance_list_player_entries) + { + std::vector insert_values; + + insert_values.push_back(std::to_string(instance_list_player_entry.id)); + insert_values.push_back(std::to_string(instance_list_player_entry.charid)); + + insert_chunks.push_back("(" + implode(",", insert_values) + ")"); + } + + std::vector insert_values; + + auto results = db.QueryDatabase( + fmt::format( + "INSERT INTO {} ({}) VALUES {} ON DUPLICATE KEY UPDATE id = VALUES(id)", + TableName(), + ColumnsRaw(), + implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } }; #endif //EQEMU_INSTANCE_LIST_PLAYER_REPOSITORY_H diff --git a/common/ruletypes.h b/common/ruletypes.h index 524898fa5..722ce5dc3 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -742,7 +742,6 @@ RULE_CATEGORY(Expedition) RULE_INT(Expedition, MinStatusToBypassPlayerCountRequirements, 80, "Minimum GM status to bypass minimum player requirements for Expedition creation") RULE_BOOL(Expedition, AlwaysNotifyNewLeaderOnChange, false, "Always notify clients when made expedition leader. If false (live-like) new leaders are only notified when made leader via /dzmakeleader") RULE_REAL(Expedition, LockoutDurationMultiplier, 1.0, "Multiplies lockout duration by this value when new lockouts are added") -RULE_BOOL(Expedition, EnableInDynamicZoneStatus, false, "Enables the 'In Dynamic Zone' member status in expedition window. If false (live-like) players inside the dynamic zone will show as 'Online'") RULE_INT(Expedition, ChooseLeaderCooldownTime, 2000, "Cooldown time (milliseconds) between choosing a new leader for automatic leader changes") RULE_CATEGORY_END() @@ -750,6 +749,7 @@ RULE_CATEGORY(DynamicZone) RULE_INT(DynamicZone, ClientRemovalDelayMS, 60000, "Delay (milliseconds) until a client is teleported out of dynamic zone after being removed as member") RULE_BOOL(DynamicZone, EmptyShutdownEnabled, true, "Enable early instance shutdown for dynamic zones that have no members") RULE_INT(DynamicZone, EmptyShutdownDelaySeconds, 1500, "Seconds to set dynamic zone instance expiration if early shutdown enabled") +RULE_BOOL(DynamicZone, EnableInDynamicZoneStatus, false, "Enables the 'In Dynamic Zone' member status in dynamic zone window. If false (live-like) players inside the dynamic zone will show as 'Online'") RULE_INT(DynamicZone, WorldProcessRate, 6000, "Timer interval (milliseconds) that systems check their dynamic zone states") RULE_CATEGORY_END() diff --git a/common/version.h b/common/version.h index af6a207e5..70bb6c410 100644 --- a/common/version.h +++ b/common/version.h @@ -34,7 +34,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9165 +#define CURRENT_BINARY_DATABASE_VERSION 9166 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9027 diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 55c8facd7..8efa47aa9 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -419,6 +419,7 @@ 9163|2021_04_17_zone_safe_heading_changes.sql|SHOW COLUMNS FROM `zone` LIKE 'safe_heading'|empty| 9164|2021_04_23_character_exp_modifiers.sql|SHOW TABLES LIKE 'character_exp_modifiers'|empty| 9165|2021_04_28_idle_pathing.sql|SHOW COLUMNS FROM `spawn2` LIKE 'path_when_zone_idle'|empty| +9166|2021_02_12_dynamic_zone_members.sql|SHOW TABLES LIKE 'dynamic_zone_members'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2021_02_12_dynamic_zone_members.sql b/utils/sql/git/required/2021_02_12_dynamic_zone_members.sql new file mode 100644 index 000000000..767f65012 --- /dev/null +++ b/utils/sql/git/required/2021_02_12_dynamic_zone_members.sql @@ -0,0 +1,11 @@ +CREATE TABLE `dynamic_zone_members` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `dynamic_zone_id` int(10) unsigned NOT NULL DEFAULT 0, + `character_id` int(10) unsigned NOT NULL DEFAULT 0, + `is_current_member` tinyint(3) unsigned NOT NULL DEFAULT 1, + PRIMARY KEY (`id`), + UNIQUE KEY `dynamic_zone_id_character_id` (`dynamic_zone_id`,`character_id`), + KEY `character_id` (`character_id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +DROP TABLE `expedition_members`; diff --git a/world/dynamic_zone.cpp b/world/dynamic_zone.cpp index dad35db82..943ea83a9 100644 --- a/world/dynamic_zone.cpp +++ b/world/dynamic_zone.cpp @@ -25,11 +25,12 @@ DynamicZone* DynamicZone::FindDynamicZoneByID(uint32_t dz_id) return nullptr; } -DynamicZoneStatus DynamicZone::Process(bool force_expire) +DynamicZoneStatus DynamicZone::Process() { DynamicZoneStatus status = DynamicZoneStatus::Normal; - if (force_expire || IsExpired()) + // force expire if no members + if (!HasMembers() || IsExpired()) { status = DynamicZoneStatus::Expired; @@ -38,7 +39,7 @@ DynamicZoneStatus DynamicZone::Process(bool force_expire) { status = DynamicZoneStatus::ExpiredEmpty; - if (force_expire && !m_is_pending_early_shutdown && RuleB(DynamicZone, EmptyShutdownEnabled)) + if (!HasMembers() && !m_is_pending_early_shutdown && RuleB(DynamicZone, EmptyShutdownEnabled)) { SetSecondsRemaining(RuleI(DynamicZone, EmptyShutdownDelaySeconds)); m_is_pending_early_shutdown = true; diff --git a/world/dynamic_zone.h b/world/dynamic_zone.h index 60eb5b65a..597604f62 100644 --- a/world/dynamic_zone.h +++ b/world/dynamic_zone.h @@ -24,7 +24,7 @@ public: void SetSecondsRemaining(uint32_t seconds_remaining) override; - DynamicZoneStatus Process(bool force_expire); + DynamicZoneStatus Process(); protected: Database& GetDatabase() override; diff --git a/world/expedition.cpp b/world/expedition.cpp index a68ddd093..4b29e4f0a 100644 --- a/world/expedition.cpp +++ b/world/expedition.cpp @@ -39,14 +39,14 @@ Expedition::Expedition() : void Expedition::SetDynamicZone(DynamicZone&& dz) { dz.SetName(GetName()); - dz.SetLeaderName(GetLeaderName()); + dz.SetLeader(GetLeader()); m_dynamic_zone = std::move(dz); } void Expedition::RemoveMember(uint32_t character_id) { - RemoveInternalMember(character_id); + GetDynamicZone().RemoveInternalMember(character_id); if (character_id == m_leader.id) { @@ -56,13 +56,14 @@ void Expedition::RemoveMember(uint32_t character_id) void Expedition::ChooseNewLeader() { - if (m_members.empty() || !m_choose_leader_cooldown_timer.Check()) + const auto& members = GetDynamicZone().GetMembers(); + if (members.empty() || !m_choose_leader_cooldown_timer.Check()) { m_choose_leader_needed = true; return; } - auto it = std::find_if(m_members.begin(), m_members.end(), [&](const DynamicZoneMember& member) { + auto it = std::find_if(members.begin(), members.end(), [&](const DynamicZoneMember& member) { if (member.id != m_leader.id && member.IsOnline()) { auto member_cle = client_list.FindCLEByCharacterID(member.id); return (member_cle && member_cle->GetOnline() == CLE_Status::InZone); @@ -70,14 +71,14 @@ void Expedition::ChooseNewLeader() return false; }); - if (it == m_members.end()) + if (it == members.end()) { // no online members found, fallback to choosing any member - it = std::find_if(m_members.begin(), m_members.end(), + it = std::find_if(members.begin(), members.end(), [&](const DynamicZoneMember& member) { return (member.id != m_leader.id); }); } - if (it != m_members.end() && SetNewLeader(*it)) + if (it != members.end() && SetNewLeader(*it)) { m_choose_leader_needed = false; } @@ -85,7 +86,7 @@ void Expedition::ChooseNewLeader() bool Expedition::SetNewLeader(const DynamicZoneMember& member) { - if (!HasMember(member.id)) + if (!GetDynamicZone().HasMember(member.id)) { return false; } @@ -93,7 +94,7 @@ bool Expedition::SetNewLeader(const DynamicZoneMember& member) LogExpeditionsModerate("Replacing [{}] leader [{}] with [{}]", m_id, m_leader.name, member.name); ExpeditionDatabase::UpdateLeaderID(m_id, member.id); m_leader = member; - m_dynamic_zone.SetLeaderName(m_leader.name); + m_dynamic_zone.SetLeader(m_leader); SendZonesLeaderChanged(); return true; } @@ -156,7 +157,7 @@ bool Expedition::Process() { // returns true if expedition needs to be deleted from world cache and db // expedition is not deleted until its dz has no clients to prevent exploits - auto status = m_dynamic_zone.Process(IsEmpty()); // force expire if no members + auto status = m_dynamic_zone.Process(); if (status == DynamicZoneStatus::ExpiredEmpty) { LogExpeditions("Expedition [{}] expired or empty, notifying zones and deleting", GetID()); @@ -172,7 +173,13 @@ bool Expedition::Process() void Expedition::UpdateMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status) { - SetInternalMemberStatus(character_id, status); + GetDynamicZone().SetInternalMemberStatus(character_id, status); + + // temporary until move to using dz leader object completely + if (character_id == m_leader.id) + { + m_leader.status = GetDynamicZone().GetLeader().status; + } // any member status update will trigger a leader fix if leader was offline if (m_leader.status == DynamicZoneMemberStatus::Offline) @@ -183,7 +190,7 @@ void Expedition::UpdateMemberStatus(uint32_t character_id, DynamicZoneMemberStat void Expedition::SendZoneMemberStatuses(uint16_t zone_id, uint16_t instance_id) { - const auto& members = GetMembers(); + const auto& members = GetDynamicZone().GetMembers(); uint32_t members_count = static_cast(members.size()); uint32_t entries_size = sizeof(ServerExpeditionMemberStatusEntry_Struct) * members_count; @@ -210,7 +217,7 @@ void Expedition::CacheMemberStatuses() all_clients.reserve(client_list.GetClientCount()); client_list.GetClients(zone_name.c_str(), all_clients); - for (const auto& member : m_members) + for (const auto& member : GetDynamicZone().GetMembers()) { auto it = std::find_if(all_clients.begin(), all_clients.end(), [&](const ClientListEntry* cle) { return (cle && cle->CharID() == member.id); }); @@ -225,6 +232,6 @@ void Expedition::CacheMemberStatuses() } } - SetInternalMemberStatus(member.id, status); + GetDynamicZone().SetInternalMemberStatus(member.id, status); } } diff --git a/world/expedition_database.cpp b/world/expedition_database.cpp index c3738901a..5c51a9e70 100644 --- a/world/expedition_database.cpp +++ b/world/expedition_database.cpp @@ -21,26 +21,28 @@ #include "expedition_database.h" #include "expedition.h" #include "worlddb.h" +#include "../common/repositories/dynamic_zone_members_repository.h" void ExpeditionDatabase::PurgeExpiredExpeditions() { std::string query = SQL( SELECT - expeditions.id + expeditions.id, + expeditions.dynamic_zone_id FROM expeditions LEFT JOIN dynamic_zones ON expeditions.dynamic_zone_id = dynamic_zones.id LEFT JOIN instance_list ON dynamic_zones.instance_id = instance_list.id LEFT JOIN ( - SELECT expedition_id, COUNT(IF(is_current_member = TRUE, 1, NULL)) member_count - FROM expedition_members - GROUP BY expedition_id - ) expedition_members - ON expedition_members.expedition_id = expeditions.id + SELECT dynamic_zone_id, COUNT(IF(is_current_member = TRUE, 1, NULL)) member_count + FROM dynamic_zone_members + GROUP BY dynamic_zone_id + ) dynamic_zone_members + ON dynamic_zone_members.dynamic_zone_id = expeditions.dynamic_zone_id WHERE instance_list.id IS NULL - OR expedition_members.member_count IS NULL - OR expedition_members.member_count = 0 + OR dynamic_zone_members.member_count IS NULL + OR dynamic_zone_members.member_count = 0 OR (instance_list.start_time + instance_list.duration) <= UNIX_TIMESTAMP(); ); @@ -48,15 +50,18 @@ void ExpeditionDatabase::PurgeExpiredExpeditions() if (results.Success()) { std::vector expedition_ids; + std::vector dynamic_zone_ids; for (auto row = results.begin(); row != results.end(); ++row) { expedition_ids.emplace_back(static_cast(strtoul(row[0], nullptr, 10))); + dynamic_zone_ids.emplace_back(static_cast(strtoul(row[1], nullptr, 10))); } if (!expedition_ids.empty()) { ExpeditionDatabase::MoveMembersToSafeReturn(expedition_ids); ExpeditionDatabase::DeleteExpeditions(expedition_ids); + DynamicZoneMembersRepository::RemoveAllMembers(database, dynamic_zone_ids); } } } @@ -82,9 +87,6 @@ void ExpeditionDatabase::DeleteExpeditions(const std::vector& expediti auto query = fmt::format("DELETE FROM expeditions WHERE id IN ({});", expedition_ids_query); database.QueryDatabase(query); - query = fmt::format("DELETE FROM expedition_members WHERE expedition_id IN ({});", expedition_ids_query); - database.QueryDatabase(query); - query = fmt::format("DELETE FROM expedition_lockouts WHERE expedition_id IN ({});", expedition_ids_query); database.QueryDatabase(query); } @@ -108,8 +110,8 @@ void ExpeditionDatabase::MoveMembersToSafeReturn(const std::vector& ex // only offline members still in expired dz zones should be updated here std::string query = fmt::format(SQL( UPDATE character_data - INNER JOIN expedition_members ON character_data.id = expedition_members.character_id - INNER JOIN expeditions ON expedition_members.expedition_id = expeditions.id + INNER JOIN dynamic_zone_members ON character_data.id = dynamic_zone_members.character_id + INNER JOIN expeditions ON dynamic_zone_members.dynamic_zone_id = expeditions.dynamic_zone_id INNER JOIN dynamic_zones ON expeditions.dynamic_zone_id = dynamic_zones.id INNER JOIN instance_list ON dynamic_zones.instance_id = instance_list.id AND character_data.zone_instance = instance_list.id diff --git a/world/expedition_state.cpp b/world/expedition_state.cpp index 8f3b6b423..a0cd245fa 100644 --- a/world/expedition_state.cpp +++ b/world/expedition_state.cpp @@ -22,8 +22,9 @@ #include "expedition.h" #include "expedition_database.h" #include "worlddb.h" +#include "../common/dynamic_zone_base.h" #include "../common/eqemu_logsys.h" -#include "../common/repositories/expedition_members_repository.h" +#include "../common/repositories/dynamic_zone_members_repository.h" #include ExpeditionState expedition_state; @@ -76,16 +77,14 @@ void ExpeditionState::CacheExpeditions( std::vector&& expedition_entries) { // bulk load expedition dzs and members before caching - std::vector expedition_ids; std::vector dynamic_zone_ids; for (const auto& entry : expedition_entries) { - expedition_ids.emplace_back(entry.id); dynamic_zone_ids.emplace_back(entry.dynamic_zone_id); } auto dynamic_zones = DynamicZonesRepository::GetWithInstance(database, dynamic_zone_ids); - auto expedition_members = ExpeditionMembersRepository::GetWithNames(database, expedition_ids); + auto dynamic_zone_members = DynamicZoneMembersRepository::GetWithNames(database, dynamic_zone_ids); for (auto& entry : expedition_entries) { @@ -102,11 +101,11 @@ void ExpeditionState::CacheExpeditions( expedition->SetDynamicZone(std::move(*dz_entry_iter)); } - for (auto& member : expedition_members) + for (auto& member : dynamic_zone_members) { - if (member.expedition_id == expedition->GetID()) + if (member.dynamic_zone_id == entry.dynamic_zone_id) { - expedition->AddMemberFromRepositoryResult(std::move(member)); + expedition->GetDynamicZone().AddMemberFromRepositoryResult(std::move(member)); } } @@ -129,7 +128,7 @@ void ExpeditionState::MemberChange( if (remove) { expedition->RemoveMember(member.id); } else { - expedition->AddInternalMember(member); + expedition->GetDynamicZone().AddInternalMember(member); } } } @@ -139,7 +138,7 @@ void ExpeditionState::RemoveAllMembers(uint32_t expedition_id) auto expedition = GetExpedition(expedition_id); if (expedition) { - expedition->ClearInternalMembers(); + expedition->GetDynamicZone().ClearInternalMembers(); } } @@ -151,6 +150,7 @@ void ExpeditionState::Process() } std::vector expedition_ids; + std::vector dynamic_zone_ids; for (auto it = m_expeditions.begin(); it != m_expeditions.end();) { @@ -158,6 +158,7 @@ void ExpeditionState::Process() if (is_deleted) { expedition_ids.emplace_back((*it)->GetID()); + dynamic_zone_ids.emplace_back((*it)->GetDynamicZone().GetID()); } it = is_deleted ? m_expeditions.erase(it) : it + 1; } @@ -166,5 +167,6 @@ void ExpeditionState::Process() { ExpeditionDatabase::MoveMembersToSafeReturn(expedition_ids); ExpeditionDatabase::DeleteExpeditions(expedition_ids); + DynamicZoneMembersRepository::RemoveAllMembers(database, dynamic_zone_ids); } } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 0a67550f6..2125b8216 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1717,7 +1717,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) /* Task Packets */ LoadClientTaskState(); - m_expedition_id = ExpeditionDatabase::GetExpeditionIDFromCharacterID(CharacterID()); + m_expedition_id = ExpeditionsRepository::GetIDByMemberID(database, CharacterID()); /** * DevTools Load Settings diff --git a/zone/command.cpp b/zone/command.cpp index 20cb4856f..3eaea3cca 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -6916,7 +6916,7 @@ void command_dz(Client* c, const Seperator* sep) expedition->GetDynamicZone().GetZoneID(), expedition->GetDynamicZone().GetInstanceID(), expedition->GetDynamicZone().GetZoneVersion(), - expedition->GetMemberCount(), + expedition->GetDynamicZone().GetMemberCount(), seconds / 3600, // hours (seconds / 60) % 60, // minutes seconds % 60 // seconds @@ -6982,12 +6982,12 @@ void command_dz(Client* c, const Seperator* sep) c->Message(Chat::White, fmt::format( "dz id: [{}] type: [{}] {}: [{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]", dz.id, - dz.type, + DynamicZone::GetDynamicZoneTypeName(static_cast(dz.type)), zone_saylink, dz.zone, dz.instance, dz.version, - dz.player_count, + dz.member_count, seconds / 3600, // hours (seconds / 60) % 60, // minutes seconds % 60 // seconds diff --git a/zone/expedition.cpp b/zone/expedition.cpp index cb6fa4199..6b0465162 100644 --- a/zone/expedition.cpp +++ b/zone/expedition.cpp @@ -27,8 +27,8 @@ #include "zonedb.h" #include "../common/eqemu_logsys.h" #include "../common/expedition_lockout_timer.h" +#include "../common/repositories/dynamic_zone_members_repository.h" #include "../common/repositories/expedition_lockouts_repository.h" -#include "../common/repositories/expedition_members_repository.h" #include "../common/util/uuid.h" extern WorldServer worldserver; @@ -59,7 +59,7 @@ Expedition::Expedition( void Expedition::SetDynamicZone(DynamicZone&& dz) { dz.SetName(GetName()); - dz.SetLeaderName(GetLeaderName()); + dz.SetLeader(GetLeader()); m_dynamiczone = std::move(dz); m_dynamiczone.RegisterOnCompassChange([this]() { SendCompassUpdateToZoneMembers(); }); @@ -125,7 +125,7 @@ Expedition* Expedition::TryCreate( expedition->GetDynamicZone().GetMaxPlayers() ); - expedition->SaveMembers(request); + expedition->GetDynamicZone().SaveMembers(request.GetMembers()); expedition->SaveLockouts(request); auto inserted = zone->expedition_cache.emplace(expedition_id, std::move(expedition)); @@ -165,7 +165,7 @@ void Expedition::CacheExpeditions( } auto dynamic_zones = DynamicZonesRepository::GetWithInstance(database, dynamic_zone_ids); - auto expedition_members = ExpeditionMembersRepository::GetWithNames(database, expedition_ids); + auto dynamic_zone_members = DynamicZoneMembersRepository::GetWithNames(database, dynamic_zone_ids); auto expedition_lockouts = ExpeditionLockoutsRepository::GetWithTimestamp(database, expedition_ids); for (auto& entry : expedition_entries) @@ -183,11 +183,11 @@ void Expedition::CacheExpeditions( expedition->SetDynamicZone(std::move(*dz_entry_iter)); } - for (auto& member : expedition_members) + for (auto& member : dynamic_zone_members) { - if (member.expedition_id == expedition->GetID()) + if (member.dynamic_zone_id == entry.dynamic_zone_id) { - expedition->AddMemberFromRepositoryResult(std::move(member)); + expedition->GetDynamicZone().AddMemberFromRepositoryResult(std::move(member)); } } @@ -258,27 +258,13 @@ void Expedition::SaveLockouts(ExpeditionRequest& request) ExpeditionDatabase::InsertLockouts(m_id, m_lockouts); } -void Expedition::SaveMembers(ExpeditionRequest& request) -{ - m_members = request.GetMembers(); - - std::vector member_ids; - for (const auto& member : m_members) - { - member_ids.emplace_back(member.id); - } - - ExpeditionDatabase::InsertMembers(m_id, m_members); - m_dynamiczone.SaveInstanceMembersToDatabase(member_ids); -} - Expedition* Expedition::FindCachedExpeditionByCharacterID(uint32_t character_id) { if (zone) { for (const auto& expedition : zone->expedition_cache) { - if (expedition.second->HasMember(character_id)) + if (expedition.second->GetDynamicZone().HasMember(character_id)) { return expedition.second.get(); } @@ -293,7 +279,7 @@ Expedition* Expedition::FindCachedExpeditionByCharacterName(const std::string& c { for (const auto& expedition : zone->expedition_cache) { - if (expedition.second->HasMember(char_name)) + if (expedition.second->GetDynamicZone().HasMember(char_name)) { return expedition.second.get(); } @@ -384,7 +370,7 @@ void Expedition::AddLockout(const ExpeditionLockoutTimer& lockout, bool members_ { ExpeditionDatabase::InsertLockout(m_id, lockout); } - ExpeditionDatabase::InsertMembersLockout(m_members, lockout); + ExpeditionDatabase::InsertMembersLockout(GetDynamicZone().GetMembers(), lockout); ProcessLockoutUpdate(lockout, false, members_only); SendWorldLockoutUpdate(lockout, false, members_only); @@ -414,7 +400,7 @@ void Expedition::AddLockoutDuration(const std::string& event_name, int seconds, // processing lockout duration applies multiplier again in client methods, // update database with modified value now but pass original on int modified_seconds = static_cast(seconds * RuleR(Expedition, LockoutDurationMultiplier)); - ExpeditionDatabase::AddLockoutDuration(m_members, lockout, modified_seconds); + ExpeditionDatabase::AddLockoutDuration(GetDynamicZone().GetMembers(), lockout, modified_seconds); ProcessLockoutDuration(lockout, seconds, members_only); SendWorldLockoutDuration(lockout, seconds, members_only); @@ -442,7 +428,7 @@ void Expedition::UpdateLockoutDuration( void Expedition::RemoveLockout(const std::string& event_name) { ExpeditionDatabase::DeleteLockout(m_id, event_name); - ExpeditionDatabase::DeleteMembersLockout(m_members, m_expedition_name, event_name); + ExpeditionDatabase::DeleteMembersLockout(GetDynamicZone().GetMembers(), m_expedition_name, event_name); ExpeditionLockoutTimer lockout{m_uuid, m_expedition_name, event_name, 0, 0}; ProcessLockoutUpdate(lockout, true); @@ -451,12 +437,11 @@ void Expedition::RemoveLockout(const std::string& event_name) bool Expedition::AddMember(const std::string& add_char_name, uint32_t add_char_id) { - if (HasMember(add_char_id)) + if (GetDynamicZone().HasMember(add_char_id)) { return false; } - ExpeditionDatabase::InsertMember(m_id, add_char_id); m_dynamiczone.AddCharacter(add_char_id); ProcessMemberAdded(add_char_name, add_char_id); @@ -469,23 +454,20 @@ void Expedition::RemoveAllMembers(bool enable_removal_timers) { m_dynamiczone.RemoveAllCharacters(enable_removal_timers); - ExpeditionDatabase::DeleteAllMembers(m_id); - SendUpdatesToZoneMembers(true); SendWorldExpeditionUpdate(ServerOP_ExpeditionMembersRemoved); - m_members.clear(); + GetDynamicZone().ClearInternalMembers(); } bool Expedition::RemoveMember(const std::string& remove_char_name) { - auto member = GetMemberData(remove_char_name); + auto member = GetDynamicZone().GetMemberData(remove_char_name); if (!member.IsValid()) { return false; } - ExpeditionDatabase::DeleteMember(m_id, member.id); m_dynamiczone.RemoveCharacter(member.id); ProcessMemberRemoved(member.name, member.id); @@ -501,15 +483,13 @@ void Expedition::SwapMember(Client* add_client, const std::string& remove_char_n return; } - auto member = GetMemberData(remove_char_name); + auto member = GetDynamicZone().GetMemberData(remove_char_name); if (!member.IsValid()) { return; } // make remove and add atomic to avoid racing with separate world messages - ExpeditionDatabase::DeleteMember(m_id, member.id); - ExpeditionDatabase::InsertMember(m_id, add_client->CharacterID()); m_dynamiczone.RemoveCharacter(member.id); m_dynamiczone.AddCharacter(add_client->CharacterID()); @@ -529,19 +509,19 @@ void Expedition::SetMemberStatus(Client* client, DynamicZoneMemberStatus status) void Expedition::SendMemberStatusToZoneMembers(uint32_t update_member_id, DynamicZoneMemberStatus status) { - auto member_data = GetMemberData(update_member_id); + auto member_data = GetDynamicZone().GetMemberData(update_member_id); if (!member_data.IsValid()) { return; } // if zone already had this member status cached avoid packet update to clients - bool changed = SetInternalMemberStatus(update_member_id, status); + bool changed = GetDynamicZone().SetInternalMemberStatus(update_member_id, status); if (changed) { - member_data = GetMemberData(update_member_id); // rules may override status + member_data = GetDynamicZone().GetMemberData(update_member_id); // rules may override status auto outapp_member_status = CreateMemberListStatusPacket(member_data.name, member_data.status); - for (auto& member : m_members) + for (auto& member : GetDynamicZone().GetMembers()) { Client* member_client = entity_list.GetClientByCharID(member.id); if (member_client) @@ -673,7 +653,7 @@ bool Expedition::ProcessAddConflicts(Client* leader_client, Client* add_client, // member swapping integrity is handled by invite response if (!swapping) { - auto member_count = ExpeditionDatabase::GetMemberCount(m_id); + auto member_count = GetDynamicZone().GetDatabaseMemberCount(); if (member_count == 0) { has_conflict = true; @@ -734,8 +714,8 @@ void Expedition::DzInviteResponse(Client* add_client, bool accepted, const std:: // error if swapping and character was already removed before the accept if (was_swap_invite) { - auto swap_member = GetMemberData(swap_remove_name); - if (!swap_member.IsValid() || !ExpeditionDatabase::HasMember(m_id, swap_member.id)) + auto swap_member = GetDynamicZone().GetMemberData(swap_remove_name); + if (!swap_member.IsValid() || !GetDynamicZone().HasDatabaseMember(swap_member.id)) { has_conflicts = true; } @@ -852,7 +832,7 @@ void Expedition::DzAddPlayer( } else { - auto member_data = GetMemberData(add_char_name); + auto member_data = GetDynamicZone().GetMemberData(add_char_name); if (member_data.IsValid()) { // live prioritizes offline message before already a member message @@ -950,7 +930,7 @@ void Expedition::DzSwapPlayer( return; } - if (remove_char_name.empty() || !HasMember(remove_char_name)) + if (remove_char_name.empty() || !GetDynamicZone().HasMember(remove_char_name)) { requester->MessageString(Chat::Red, DZSWAP_CANNOT_REMOVE, FormatName(remove_char_name).c_str()); return; @@ -966,7 +946,7 @@ void Expedition::DzPlayerList(Client* requester) requester->MessageString(Chat::Yellow, EXPEDITION_LEADER, m_leader.name.c_str()); std::string member_names; - for (const auto& member : m_members) + for (const auto& member : GetDynamicZone().GetMembers()) { fmt::format_to(std::back_inserter(member_names), "{}, ", member.name); } @@ -1017,7 +997,7 @@ void Expedition::SetLocked( void Expedition::ProcessLeaderChanged(uint32_t new_leader_id) { - auto new_leader = GetMemberData(new_leader_id); + auto new_leader = GetDynamicZone().GetMemberData(new_leader_id); if (!new_leader.IsValid()) { LogExpeditions("Processed invalid new leader id [{}] for expedition [{}]", new_leader_id, m_id); @@ -1027,11 +1007,11 @@ void Expedition::ProcessLeaderChanged(uint32_t new_leader_id) LogExpeditionsModerate("Replaced [{}] leader [{}] with [{}]", m_id, m_leader.name, new_leader.name); m_leader = new_leader; - m_dynamiczone.SetLeaderName(m_leader.name); + m_dynamiczone.SetLeader(m_leader); // update each client's expedition window in this zone auto outapp_leader = CreateLeaderNamePacket(); - for (const auto& member : m_members) + for (const auto& member : GetDynamicZone().GetMembers()) { Client* member_client = entity_list.GetClientByCharID(member.id); if (member_client) @@ -1074,7 +1054,7 @@ void Expedition::ProcessMakeLeader(Client* old_leader_client, Client* new_leader void Expedition::ProcessMemberAdded(const std::string& char_name, uint32_t added_char_id) { - AddInternalMember({ added_char_id, char_name, DynamicZoneMemberStatus::Online }); + GetDynamicZone().AddInternalMember({ added_char_id, char_name, DynamicZoneMemberStatus::Online }); // adds the member to this expedition and notifies both leader and new member Client* leader_client = entity_list.GetClientByCharID(m_leader.id); @@ -1097,24 +1077,22 @@ void Expedition::ProcessMemberAdded(const std::string& char_name, uint32_t added void Expedition::ProcessMemberRemoved(const std::string& removed_char_name, uint32_t removed_char_id) { - if (m_members.empty()) + if (GetDynamicZone().GetMembers().empty()) { return; } auto outapp_member_name = CreateMemberListNamePacket(removed_char_name, true); - for (auto it = m_members.begin(); it != m_members.end();) + for (const auto& member : GetDynamicZone().GetMembers()) { - bool is_removed = (it->name == removed_char_name); - - Client* member_client = entity_list.GetClientByCharID(it->id); + Client* member_client = entity_list.GetClientByCharID(member.id); if (member_client) { // all members receive the removed player name packet member_client->QueuePacket(outapp_member_name.get()); - if (is_removed) + if (member.id == removed_char_id) { // live doesn't clear expedition info on clients removed while inside dz. // it instead let's the dz kick timer do it even if character zones out @@ -1123,17 +1101,15 @@ void Expedition::ProcessMemberRemoved(const std::string& removed_char_name, uint member_client->SendDzCompassUpdate(); member_client->QueuePacket(CreateInfoPacket(true).get()); member_client->MessageString(Chat::Yellow, EXPEDITION_REMOVED, - it->name.c_str(), m_expedition_name.c_str()); + member.name.c_str(), m_expedition_name.c_str()); } } - - it = is_removed ? m_members.erase(it) : it + 1; } - LogExpeditionsDetail( - "Processed member [{}] ({}) removal from [{}], cache member count: [{}]", - removed_char_name, removed_char_id, m_id, m_members.size() - ); + GetDynamicZone().RemoveInternalMember(removed_char_id); + + LogExpeditionsDetail("Processed member [{}] ({}) removal from [{}], cache member count: [{}]", + removed_char_name, removed_char_id, m_id, GetDynamicZone().GetMemberCount()); } void Expedition::ProcessLockoutDuration( @@ -1152,7 +1128,7 @@ void Expedition::ProcessLockoutDuration( } } - for (const auto& member : m_members) + for (const auto& member : GetDynamicZone().GetMembers()) { Client* member_client = entity_list.GetClientByCharID(member.id); if (member_client) @@ -1205,7 +1181,7 @@ void Expedition::ProcessLockoutUpdate( } } - for (const auto& member : m_members) + for (const auto& member : GetDynamicZone().GetMembers()) { Client* member_client = entity_list.GetClientByCharID(member.id); if (member_client) @@ -1254,7 +1230,7 @@ void Expedition::SendMemberListToZoneMembers() { auto outapp_members = CreateMemberListPacket(false); - for (const auto& member : m_members) + for (const auto& member : GetDynamicZone().GetMembers()) { Client* member_client = entity_list.GetClientByCharID(member.id); if (member_client) @@ -1266,12 +1242,12 @@ void Expedition::SendMemberListToZoneMembers() void Expedition::SendUpdatesToZoneMembers(bool clear, bool message_on_clear) { - if (!m_members.empty()) + if (GetDynamicZone().HasMembers()) { auto outapp_info = CreateInfoPacket(clear); auto outapp_members = CreateMemberListPacket(clear); - for (const auto& member : m_members) + for (const auto& member : GetDynamicZone().GetMembers()) { Client* member_client = entity_list.GetClientByCharID(member.id); if (member_client) @@ -1351,7 +1327,7 @@ std::unique_ptr Expedition::CreateInvitePacket( std::unique_ptr Expedition::CreateMemberListPacket(bool clear) { - uint32_t member_count = clear ? 0 : static_cast(m_members.size()); + uint32_t member_count = clear ? 0 : static_cast(GetDynamicZone().GetMemberCount()); uint32_t member_entries_size = sizeof(DynamicZoneMemberEntry_Struct) * member_count; uint32_t outsize = sizeof(DynamicZoneMemberList_Struct) + member_entries_size; auto outapp = std::make_unique(OP_DzMemberList, outsize); @@ -1361,10 +1337,11 @@ std::unique_ptr Expedition::CreateMemberListPacket(bool cle if (!clear) { - for (auto i = 0; i < m_members.size(); ++i) + const auto& members = GetDynamicZone().GetMembers(); + for (auto i = 0; i < members.size(); ++i) { - strn0cpy(buf->members[i].name, m_members[i].name.c_str(), sizeof(buf->members[i].name)); - buf->members[i].online_status = static_cast(m_members[i].status); + strn0cpy(buf->members[i].name, members[i].name.c_str(), sizeof(buf->members[i].name)); + buf->members[i].online_status = static_cast(members[i].status); } } @@ -1662,7 +1639,7 @@ void Expedition::HandleWorldMessage(ServerPacket* pack) if (expedition) { expedition->SendUpdatesToZoneMembers(true); - expedition->m_members.clear(); + expedition->GetDynamicZone().ClearInternalMembers(); } } break; @@ -1785,7 +1762,7 @@ void Expedition::HandleWorldMessage(ServerPacket* pack) for (uint32_t i = 0; i < buf->count; ++i) { auto status = static_cast(buf->entries[i].online_status); - expedition->SetInternalMemberStatus(buf->entries[i].character_id, status); + expedition->GetDynamicZone().SetInternalMemberStatus(buf->entries[i].character_id, status); } expedition->SendMemberListToZoneMembers(); } @@ -1865,7 +1842,7 @@ void Expedition::HandleWorldMessage(ServerPacket* pack) void Expedition::SendCompassUpdateToZoneMembers() { - for (const auto& member : m_members) + for (const auto& member : GetDynamicZone().GetMembers()) { Client* member_client = entity_list.GetClientByCharID(member.id); if (member_client) @@ -1979,7 +1956,7 @@ void Expedition::SendMembersExpireWarning(uint32_t minutes_remaining) { // expeditions warn members in all zones not just the dz auto outapp = CreateExpireWarningPacket(minutes_remaining); - for (const auto& member : m_members) + for (const auto& member : GetDynamicZone().GetMembers()) { Client* member_client = entity_list.GetClientByCharID(member.id); if (member_client) diff --git a/zone/expedition.h b/zone/expedition.h index b3404d696..56ffffc3c 100644 --- a/zone/expedition.h +++ b/zone/expedition.h @@ -142,7 +142,6 @@ private: void ProcessMemberAdded(const std::string& added_char_name, uint32_t added_char_id); void ProcessMemberRemoved(const std::string& removed_char_name, uint32_t removed_char_id); void SaveLockouts(ExpeditionRequest& request); - void SaveMembers(ExpeditionRequest& request); void SendClientExpeditionInvite( Client* client, const std::string& inviter_name, const std::string& swap_remove_name); void SendLeaderMessage(Client* leader_client, uint16_t chat_type, uint32_t string_id, diff --git a/zone/expedition_database.cpp b/zone/expedition_database.cpp index af6948f5f..eb31c301c 100644 --- a/zone/expedition_database.cpp +++ b/zone/expedition_database.cpp @@ -213,67 +213,6 @@ void ExpeditionDatabase::DeleteLockout(uint32_t expedition_id, const std::string database.QueryDatabase(query); } -uint32_t ExpeditionDatabase::GetExpeditionIDFromCharacterID(uint32_t character_id) -{ - LogExpeditionsDetail("Getting expedition id for character [{}]", character_id); - - uint32_t expedition_id = 0; - auto query = fmt::format(SQL( - SELECT expedition_id FROM expedition_members - WHERE character_id = {} AND is_current_member = TRUE; - ), character_id); - - auto results = database.QueryDatabase(query); - if (results.Success() && results.RowCount() > 0) - { - auto row = results.begin(); - expedition_id = strtoul(row[0], nullptr, 10); - } - return expedition_id; -} - -uint32_t ExpeditionDatabase::GetMemberCount(uint32_t expedition_id) -{ - LogExpeditionsDetail("Getting expedition [{}] member count from db", expedition_id); - - uint32_t member_count = 0; - if (expedition_id != 0) - { - auto query = fmt::format(SQL( - SELECT COUNT(*) - FROM expedition_members - WHERE expedition_id = {} AND is_current_member = TRUE; - ), expedition_id); - - auto results = database.QueryDatabase(query); - if (results.Success() && results.RowCount() > 0) - { - auto row = results.begin(); - member_count = strtoul(row[0], nullptr, 10); - } - } - return member_count; -} - -bool ExpeditionDatabase::HasMember(uint32_t expedition_id, uint32_t character_id) -{ - LogExpeditionsDetail("Checking db expedition [{}] for character [{}]", expedition_id, character_id); - - if (expedition_id == 0 || character_id == 0) - { - return false; - } - - auto query = fmt::format(SQL( - SELECT id - FROM expedition_members - WHERE expedition_id = {} AND character_id = {} AND is_current_member = TRUE; - ), expedition_id, character_id); - - auto results = database.QueryDatabase(query); - return (results.Success() && results.RowCount() > 0); -} - void ExpeditionDatabase::InsertCharacterLockouts(uint32_t character_id, const std::vector& lockouts) { @@ -415,50 +354,6 @@ void ExpeditionDatabase::InsertLockouts( } } -void ExpeditionDatabase::InsertMember(uint32_t expedition_id, uint32_t character_id) -{ - LogExpeditionsDetail("Inserting character [{}] into expedition [{}]", character_id, expedition_id); - - auto query = fmt::format(SQL( - INSERT INTO expedition_members - (expedition_id, character_id) - VALUES - ({}, {}) - ON DUPLICATE KEY UPDATE is_current_member = TRUE; - ), expedition_id, character_id); - - database.QueryDatabase(query); -} - -void ExpeditionDatabase::InsertMembers( - uint32_t expedition_id, const std::vector& members) -{ - LogExpeditionsDetail("Inserting characters into expedition [{}]", expedition_id); - - std::string insert_values; - for (const auto& member : members) - { - fmt::format_to(std::back_inserter(insert_values), - "({}, {}),", - expedition_id, member.id - ); - } - - if (!insert_values.empty()) - { - insert_values.pop_back(); // trailing comma - - auto query = fmt::format(SQL( - INSERT INTO expedition_members - (expedition_id, character_id) - VALUES {} - ON DUPLICATE KEY UPDATE is_current_member = TRUE; - ), insert_values); - - database.QueryDatabase(query); - } -} - void ExpeditionDatabase::UpdateLockState(uint32_t expedition_id, bool is_locked) { LogExpeditionsDetail("Updating lock state [{}] for expedition [{}]", is_locked, expedition_id); @@ -470,29 +365,6 @@ void ExpeditionDatabase::UpdateLockState(uint32_t expedition_id, bool is_locked) database.QueryDatabase(query); } -void ExpeditionDatabase::DeleteMember(uint32_t expedition_id, uint32_t character_id) -{ - LogExpeditionsDetail("Removing member [{}] from expedition [{}]", character_id, expedition_id); - - auto query = fmt::format(SQL( - UPDATE expedition_members SET is_current_member = FALSE - WHERE expedition_id = {} AND character_id = {}; - ), expedition_id, character_id); - - database.QueryDatabase(query); -} - -void ExpeditionDatabase::DeleteAllMembers(uint32_t expedition_id) -{ - LogExpeditionsDetail("Removing all members of expedition [{}]", expedition_id); - - auto query = fmt::format(SQL( - UPDATE expedition_members SET is_current_member = FALSE WHERE expedition_id = {}; - ), expedition_id); - - database.QueryDatabase(query); -} - void ExpeditionDatabase::UpdateReplayLockoutOnJoin(uint32_t expedition_id, bool add_on_join) { LogExpeditionsDetail("Updating replay lockout on join [{}] for expedition [{}]", add_on_join, expedition_id); diff --git a/zone/expedition_database.h b/zone/expedition_database.h index cce81c0fc..e83332920 100644 --- a/zone/expedition_database.h +++ b/zone/expedition_database.h @@ -41,8 +41,6 @@ namespace ExpeditionDatabase std::vector LoadCharacterLockouts(uint32_t character_id); std::vector LoadCharacterLockouts(uint32_t character_id, const std::string& expedition_name); - void DeleteAllMembers(uint32_t expedition_id); - void DeleteMember(uint32_t expedition_id, uint32_t character_id); void DeleteAllCharacterLockouts(uint32_t character_id); void DeleteAllCharacterLockouts(uint32_t character_id, const std::string& expedition_name); void DeleteCharacterLockout(uint32_t character_id, const std::string& expedition_name, @@ -50,9 +48,6 @@ namespace ExpeditionDatabase void DeleteLockout(uint32_t expedition_id, const std::string& event_name); void DeleteMembersLockout(const std::vector& members, const std::string& expedition_name, const std::string& event_name); - uint32_t GetExpeditionIDFromCharacterID(uint32_t character_id); - uint32_t GetMemberCount(uint32_t expedition_id); - bool HasMember(uint32_t expedition_id, uint32_t character_id); void InsertCharacterLockouts(uint32_t character_id, const std::vector& lockouts); void InsertMembersLockout(const std::vector& members, @@ -60,8 +55,6 @@ namespace ExpeditionDatabase void InsertLockout(uint32_t expedition_id, const ExpeditionLockoutTimer& lockout); void InsertLockouts(uint32_t expedition_id, const std::unordered_map& lockouts); - void InsertMember(uint32_t expedition_id, uint32_t character_id); - void InsertMembers(uint32_t expedition_id, const std::vector& members); void UpdateLockState(uint32_t expedition_id, bool is_locked); void UpdateReplayLockoutOnJoin(uint32_t expedition_id, bool add_on_join); void AddLockoutDuration(const std::vector& members, diff --git a/zone/expedition_request.h b/zone/expedition_request.h index 64c594d3c..b5e69e3ef 100644 --- a/zone/expedition_request.h +++ b/zone/expedition_request.h @@ -47,8 +47,8 @@ public: const std::string& GetNotAllAddedMessage() const { return m_not_all_added_msg; } uint32_t GetMinPlayers() const { return m_min_players; } uint32_t GetMaxPlayers() const { return m_max_players; } - std::vector GetMembers() const { return m_members; } - std::unordered_map GetLockouts() const { return m_lockouts; } + const std::vector& GetMembers() const { return m_members; } + const std::unordered_map& GetLockouts() const { return m_lockouts; } private: bool CanMembersJoin(const std::vector& member_names); diff --git a/zone/lua_expedition.cpp b/zone/lua_expedition.cpp index f19e417ff..b3a442a24 100644 --- a/zone/lua_expedition.cpp +++ b/zone/lua_expedition.cpp @@ -104,7 +104,7 @@ std::string Lua_Expedition::GetLootEventBySpawnID(uint32_t spawn_id) { uint32_t Lua_Expedition::GetMemberCount() { Lua_Safe_Call_Int(); - return self->GetMemberCount(); + return self->GetDynamicZone().GetMemberCount(); } luabind::object Lua_Expedition::GetMembers(lua_State* L) { @@ -113,7 +113,7 @@ luabind::object Lua_Expedition::GetMembers(lua_State* L) { if (d_) { auto self = reinterpret_cast(d_); - for (const auto& member : self->GetMembers()) + for (const auto& member : self->GetDynamicZone().GetMembers()) { lua_table[member.name] = member.id; } diff --git a/zone/perl_expedition.cpp b/zone/perl_expedition.cpp index c0c483cfb..3715e754a 100644 --- a/zone/perl_expedition.cpp +++ b/zone/perl_expedition.cpp @@ -247,7 +247,7 @@ XS(XS_Expedition_GetMemberCount) { Expedition* THIS = nullptr; VALIDATE_THIS_IS_EXPEDITION; - XSRETURN_UV(THIS->GetMemberCount()); + XSRETURN_UV(THIS->GetDynamicZone().GetMemberCount()); } XS(XS_Expedition_GetMembers); @@ -262,8 +262,7 @@ XS(XS_Expedition_GetMembers) { HV* hash = newHV(); - auto members = THIS->GetMembers(); - for (const auto& member : members) + for (const auto& member : THIS->GetDynamicZone().GetMembers()) { hv_store(hash, member.name.c_str(), static_cast(member.name.size()), newSVuv(member.id), 0); From e14acd680299041d1c656d61b0a13e2279a63e71 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 24 May 2021 22:15:41 -0400 Subject: [PATCH 054/624] [Quest API] Add several methods to Perl/Lua API for LDoN stuff. (#1356) - Swapped parameters in mostly unused functions to be theme_id first and points second. (No examples in PEQ quests.) - Add $client->AddLDoNLoss(theme_id) to Perl. - Add $client->AddLDoNWin(theme_id) to Perl. - Add quest::crosszoneaddldonlossbycharid(character_id, theme_id) to Perl. - Add quest::crosszoneaddldonlossbygroupid(group_id, theme_id) to Perl. - Add quest::crosszoneaddldonlossbyraidid(raid_id, theme_id) to Perl. - Add quest::crosszoneaddldonlossbyguildid(guild_id, theme_id) to Perl. - Add quest::crosszoneaddldonlossbyexpeditionid(expedition_id, theme_id) to Perl. - Add quest::crosszoneaddldonpointsbycharid(character_id, theme_id, points) to Perl. - Add quest::crosszoneaddldonpointsbygroupid(group_id, theme_id, points) to Perl. - Add quest::crosszoneaddldonpointsbyraidid(raid_id, theme_id, points) to Perl. - Add quest::crosszoneaddldonpointsbyguildid(guild_id, theme_id, points) to Perl. - Add quest::crosszoneaddldonpointsbyexpeditionid(expedition_id, theme_id, points) to Perl. - Add quest::crosszoneaddldonwinbycharid(character_id, theme_id) to Perl. - Add quest::crosszoneaddldonwinbygroupid(group_id, theme_id) to Perl. - Add quest::crosszoneaddldonwinbyraidid(raid_id, theme_id) to Perl. - Add quest::crosszoneaddldonwinbyguildid(guild_id, theme_id) to Perl. - Add quest::crosszoneaddldonwinbyexpeditionid(expedition_id, theme_id) to Perl. - Fix quest::addldonloss(theme_id) in Perl. - Fix quest::addldonwin(theme_id) in Perl. - Add client:AddLDoNLoss(theme_id) to Lua. - Add client:AddLDoNWin(theme_id) to Lua. - Add eq.add_ldon_loss(theme_id) to Lua. - Add eq.add_ldon_points(theme_id, points) to Lua. - Add eq.add_ldon_win(theme_id) to Lua. - Add eq.cross_zone_add_ldon_loss_by_char_id(character_id, theme_id) to Lua. - Add eq.cross_zone_add_ldon_loss_by_group_id(group_id, theme_id) to Lua. - Add eq.cross_zone_add_ldon_loss_by_raid_id(raid_id, theme_id) to Lua. - Add eq.cross_zone_add_ldon_loss_by_guild_id(guild_id, theme_id) to Lua. - Add eq.cross_zone_add_ldon_loss_by_expedition_id(expedition_id, theme_id) to Lua. - Add eq.cross_zone_add_ldon_points_by_char_id(character_id, theme_id, points) to Lua. - Add eq.cross_zone_add_ldon_points_by_group_id(group_id, theme_id, points) to Lua. - Add eq.cross_zone_add_ldon_points_by_raid_id(raid_id, theme_id, points) to Lua. - Add eq.cross_zone_add_ldon_points_by_guild_id(guild_id, theme_id, points) to Lua. - Add eq.cross_zone_add_ldon_points_by_expedition_id(expedition_id, theme_id, points) to Lua. - Add eq.cross_zone_add_ldon_win_by_char_id(character_id, theme_id) to Lua. - Add eq.cross_zone_add_ldon_win_by_group_id(group_id, theme_id) to Lua. - Add eq.cross_zone_add_ldon_win_by_raid_id(raid_id, theme_id) to Lua. - Add eq.cross_zone_add_ldon_win_by_guild_id(guild_id, theme_id) to Lua. - Add eq.cross_zone_add_ldon_win_by_expedition_id(expedition_id, theme_id) to Lua. --- common/servertalk.h | 23 ++++ world/zoneserver.cpp | 1 + zone/client.cpp | 226 +++++++++++++++++----------------- zone/client.h | 6 +- zone/client_packet.cpp | 4 +- zone/command.cpp | 4 +- zone/embparser_api.cpp | 268 +++++++++++++++++++++++++++++++++++++---- zone/lua_client.cpp | 20 ++- zone/lua_client.h | 4 +- zone/lua_general.cpp | 121 ++++++++++++++++++- zone/perl_client.cpp | 46 +++++-- zone/questmgr.cpp | 24 +++- zone/questmgr.h | 7 +- zone/worldserver.cpp | 109 +++++++++++++++++ 14 files changed, 692 insertions(+), 171 deletions(-) diff --git a/common/servertalk.h b/common/servertalk.h index b7c59e123..b4a5000ad 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -287,6 +287,7 @@ #define ServerOP_CZTaskRemoveRaid 0x4561 #define ServerOP_CZTaskRemoveGuild 0x4562 #define ServerOP_CZClientMessageString 0x4563 +#define ServerOP_CZLDoNUpdate 0x4564 #define ServerOP_WWAssignTask 0x4750 #define ServerOP_WWCastSpell 0x4751 @@ -319,6 +320,20 @@ #define ServerOP_QSSendQuery 0x5006 #define ServerOP_QSPlayerDropItem 0x5007 +enum { + CZLDoNUpdateType_Character = 0, + CZLDoNUpdateType_Group, + CZLDoNUpdateType_Raid, + CZLDoNUpdateType_Guild, + CZLDoNUpdateType_Expedition +}; + +enum { + CZLDoNUpdateSubtype_Win = 0, + CZLDoNUpdateSubtype_Loss, + CZLDoNUpdateSubtype_Points +}; + /* Query Serv Generic Packet Flag/Type Enumeration */ enum { QSG_LFGuild = 0 }; enum { QSG_LFGuild_PlayerMatches = 0, QSG_LFGuild_UpdatePlayerInfo, QSG_LFGuild_RequestPlayerInfo, QSG_LFGuild_UpdateGuildInfo, QSG_LFGuild_GuildMatches, @@ -1862,6 +1877,14 @@ struct CZTaskRemoveGuild_Struct { uint32 task_id; }; +struct CZLDoNUpdate_Struct { + uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition + uint8 update_subtype; // 0 - Win, 1 - Loss, 2 - Points + int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, or Expedition ID based on update type + uint32 theme_id; + int points; // Always 1, except for when Points are used +}; + struct WWAssignTask_Struct { uint16 npc_entity_id; uint32 task_id; diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 4e37b1d7e..c350de44d 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -1302,6 +1302,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { case ServerOP_CZTaskRemoveGroup: case ServerOP_CZTaskRemoveRaid: case ServerOP_CZTaskRemoveGuild: + case ServerOP_CZLDoNUpdate: case ServerOP_WWAssignTask: case ServerOP_WWCastSpell: case ServerOP_WWDisableTask: diff --git a/zone/client.cpp b/zone/client.cpp index 2d6ad9994..10c58a265 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -1374,8 +1374,7 @@ void Client::SetMaxHP() { Save(); } -bool Client::UpdateLDoNPoints(int32 points, uint32 theme) -{ +bool Client::UpdateLDoNPoints(uint32 theme_id, int points) { /* make sure total stays in sync with individual buckets m_pp.ldon_points_available = m_pp.ldon_points_guk @@ -1384,113 +1383,97 @@ bool Client::UpdateLDoNPoints(int32 points, uint32 theme) +m_pp.ldon_points_ruj +m_pp.ldon_points_tak; */ - if(points < 0) - { - if(m_pp.ldon_points_available < (0-points)) + if(points < 0) { + if(m_pp.ldon_points_available < (0 - points)) return false; } - switch(theme) + + switch (theme_id) { - // handle generic points (theme=0) - case 0: - { // no theme, so distribute evenly across all - int splitpts=points/5; - int gukpts=splitpts+(points%5); - int mirpts=splitpts; - int mmcpts=splitpts; - int rujpts=splitpts; - int takpts=splitpts; - - splitpts=0; - - if(points < 0) - { - if(m_pp.ldon_points_available < (0-points)) - { + case 0: { // No theme, so distribute evenly across all + int split_points = (points / 5); + int guk_points = (split_points + (points % 5)); + int mir_points = split_points; + int mmc_points = split_points; + int ruj_points = split_points; + int tak_points = split_points; + split_points = 0; + if(points < 0) { + if(m_pp.ldon_points_available < (0 - points)) { return false; } - if(m_pp.ldon_points_guk < (0-gukpts)) - { - mirpts+=gukpts+m_pp.ldon_points_guk; - gukpts=0-m_pp.ldon_points_guk; + + if(m_pp.ldon_points_guk < (0 - guk_points)) { + mir_points += (guk_points + m_pp.ldon_points_guk); + guk_points = (0 - m_pp.ldon_points_guk); } - if(m_pp.ldon_points_mir < (0-mirpts)) - { - mmcpts+=mirpts+m_pp.ldon_points_mir; - mirpts=0-m_pp.ldon_points_mir; + + if(m_pp.ldon_points_mir < (0 - mir_points)) { + mmc_points += (mir_points + m_pp.ldon_points_mir); + mir_points = (0 - m_pp.ldon_points_mir); } - if(m_pp.ldon_points_mmc < (0-mmcpts)) - { - rujpts+=mmcpts+m_pp.ldon_points_mmc; - mmcpts=0-m_pp.ldon_points_mmc; + + if(m_pp.ldon_points_mmc < (0 - mmc_points)) { + ruj_points += (mmc_points + m_pp.ldon_points_mmc); + mmc_points = (0 - m_pp.ldon_points_mmc); } - if(m_pp.ldon_points_ruj < (0-rujpts)) - { - takpts+=rujpts+m_pp.ldon_points_ruj; - rujpts=0-m_pp.ldon_points_ruj; + + if(m_pp.ldon_points_ruj < (0 - ruj_points)) { + tak_points += (ruj_points + m_pp.ldon_points_ruj); + ruj_points = (0 - m_pp.ldon_points_ruj); } - if(m_pp.ldon_points_tak < (0-takpts)) - { - splitpts=takpts+m_pp.ldon_points_tak; - takpts=0-m_pp.ldon_points_tak; + + if(m_pp.ldon_points_tak < (0 - tak_points)) { + split_points = (tak_points + m_pp.ldon_points_tak); + tak_points = (0 - m_pp.ldon_points_tak); } } - m_pp.ldon_points_guk += gukpts; - m_pp.ldon_points_mir+=mirpts; - m_pp.ldon_points_mmc += mmcpts; - m_pp.ldon_points_ruj += rujpts; - m_pp.ldon_points_tak += takpts; - points-=splitpts; - // if anything left, recursively loop thru again - if (splitpts !=0) - UpdateLDoNPoints(splitpts,0); + m_pp.ldon_points_guk += guk_points; + m_pp.ldon_points_mir += mir_points; + m_pp.ldon_points_mmc += mmc_points; + m_pp.ldon_points_ruj += ruj_points; + m_pp.ldon_points_tak += tak_points; + points -= split_points; + if (split_points != 0) // if anything left, recursively loop thru again + UpdateLDoNPoints(0, split_points); + break; } - case 1: - { - if(points < 0) - { - if(m_pp.ldon_points_guk < (0-points)) + case 1: { + if(points < 0) { + if(m_pp.ldon_points_guk < (0 - points)) return false; } m_pp.ldon_points_guk += points; break; } - case 2: - { - if(points < 0) - { - if(m_pp.ldon_points_mir < (0-points)) + case 2: { + if(points < 0) { + if(m_pp.ldon_points_mir < (0 - points)) return false; } m_pp.ldon_points_mir += points; break; } - case 3: - { - if(points < 0) - { - if(m_pp.ldon_points_mmc < (0-points)) + case 3: { + if(points < 0) { + if(m_pp.ldon_points_mmc < (0 - points)) return false; } m_pp.ldon_points_mmc += points; break; } - case 4: - { - if(points < 0) - { - if(m_pp.ldon_points_ruj < (0-points)) + case 4: { + if(points < 0) { + if(m_pp.ldon_points_ruj < (0 - points)) return false; } m_pp.ldon_points_ruj += points; break; } - case 5: - { - if(points < 0) - { - if(m_pp.ldon_points_tak < (0-points)) + case 5: { + if(points < 0) { + if(m_pp.ldon_points_tak < (0 - points)) return false; } m_pp.ldon_points_tak += points; @@ -1498,7 +1481,6 @@ bool Client::UpdateLDoNPoints(int32 points, uint32 theme) } } m_pp.ldon_points_available += points; - auto outapp = new EQApplicationPacket(OP_AdventurePointsUpdate, sizeof(AdventurePoints_Update_Struct)); AdventurePoints_Update_Struct* apus = (AdventurePoints_Update_Struct*)outapp->pBuffer; apus->ldon_available_points = m_pp.ldon_points_available; @@ -1511,8 +1493,6 @@ bool Client::UpdateLDoNPoints(int32 points, uint32 theme) QueuePacket(outapp); safe_delete(outapp); return true; - - return(false); } void Client::SetSkill(EQ::skills::SkillType skillid, uint16 value) { @@ -5566,51 +5546,61 @@ uint32 Client::GetLDoNLossesTheme(uint32 t) } } -void Client::UpdateLDoNWins(uint32 t, int32 n) +void Client::AddLDoNLoss(uint32 theme_id) { - switch(t) + switch (theme_id) { - case 1: - m_pp.ldon_wins_guk = n; - break; - case 2: - m_pp.ldon_wins_mir = n; - break; - case 3: - m_pp.ldon_wins_mmc = n; - break; - case 4: - m_pp.ldon_wins_ruj = n; - break; - case 5: - m_pp.ldon_wins_tak = n; - break; - default: - return; + case 1: + m_pp.ldon_losses_guk += 1; + database.UpdateAdventureStatsEntry(CharacterID(), theme_id, false); + break; + case 2: + m_pp.ldon_losses_mir += 1; + database.UpdateAdventureStatsEntry(CharacterID(), theme_id, false); + break; + case 3: + m_pp.ldon_losses_mmc += 1; + database.UpdateAdventureStatsEntry(CharacterID(), theme_id, false); + break; + case 4: + m_pp.ldon_losses_ruj += 1; + database.UpdateAdventureStatsEntry(CharacterID(), theme_id, false); + break; + case 5: + m_pp.ldon_losses_tak += 1; + database.UpdateAdventureStatsEntry(CharacterID(), theme_id, false); + break; + default: + return; } } -void Client::UpdateLDoNLosses(uint32 t, int32 n) +void Client::AddLDoNWin(uint32 theme_id) { - switch(t) + switch (theme_id) { - case 1: - m_pp.ldon_losses_guk = n; - break; - case 2: - m_pp.ldon_losses_mir = n; - break; - case 3: - m_pp.ldon_losses_mmc = n; - break; - case 4: - m_pp.ldon_losses_ruj = n; - break; - case 5: - m_pp.ldon_losses_tak = n; - break; - default: - return; + case 1: + m_pp.ldon_wins_guk += 1; + database.UpdateAdventureStatsEntry(CharacterID(), theme_id, true); + break; + case 2: + m_pp.ldon_wins_mir += 1; + database.UpdateAdventureStatsEntry(CharacterID(), theme_id, true); + break; + case 3: + m_pp.ldon_wins_mmc += 1; + database.UpdateAdventureStatsEntry(CharacterID(), theme_id, true); + break; + case 4: + m_pp.ldon_wins_ruj += 1; + database.UpdateAdventureStatsEntry(CharacterID(), theme_id, true); + break; + case 5: + m_pp.ldon_wins_tak += 1; + database.UpdateAdventureStatsEntry(CharacterID(), theme_id, true); + break; + default: + return; } } @@ -6043,7 +6033,7 @@ void Client::ClearCurrentAdventure() void Client::AdventureFinish(bool win, int theme, int points) { - UpdateLDoNPoints(points, theme); + UpdateLDoNPoints(theme, points); auto outapp = new EQApplicationPacket(OP_AdventureFinish, sizeof(AdventureFinish_Struct)); AdventureFinish_Struct *af = (AdventureFinish_Struct*)outapp->pBuffer; af->win_lose = win ? 1 : 0; diff --git a/zone/client.h b/zone/client.h index 9d4059450..2c8dc2e2b 100644 --- a/zone/client.h +++ b/zone/client.h @@ -604,7 +604,7 @@ public: inline void SetAAEXPModifier(uint32 zone_id, double aa_modifier) { database.SetAAEXPModifier(CharacterID(), zone_id, aa_modifier); }; inline void SetEXPModifier(uint32 zone_id, double exp_modifier) { database.SetEXPModifier(CharacterID(), zone_id, exp_modifier); }; - bool UpdateLDoNPoints(int32 points, uint32 theme); + bool UpdateLDoNPoints(uint32 theme_id, int points); void SetPVPPoints(uint32 Points) { m_pp.PVPCurrentPoints = Points; } uint32 GetPVPPoints() { return m_pp.PVPCurrentPoints; } void AddPVPPoints(uint32 Points); @@ -1308,8 +1308,8 @@ public: uint32 GetLDoNWinsTheme(uint32 t); uint32 GetLDoNLossesTheme(uint32 t); uint32 GetLDoNPointsTheme(uint32 t); - void UpdateLDoNWins(uint32 t, int32 n); - void UpdateLDoNLosses(uint32 t, int32 n); + void AddLDoNWin(uint32 theme_id); + void AddLDoNLoss(uint32 theme_id); void CheckLDoNHail(Mob *target); void CheckEmoteHail(Mob *target, const char* message); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 2125b8216..f4ef29224 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -2032,7 +2032,7 @@ void Client::Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app) { int32 requiredpts = (int32)item->LDoNPrice*-1; - if (!UpdateLDoNPoints(requiredpts, 6)) + if (!UpdateLDoNPoints(6, requiredpts)) return; } else if (aps->Type == DiscordMerchant) @@ -2260,7 +2260,7 @@ void Client::Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app) { case ADVENTUREMERCHANT: { - UpdateLDoNPoints(price, 6); + UpdateLDoNPoints(6, price); break; } case NORRATHS_KEEPERS_MERCHANT: diff --git a/zone/command.cpp b/zone/command.cpp index 3eaea3cca..83d035952 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -8432,13 +8432,13 @@ void command_set_adventure_points(Client *c, const Seperator *sep) if(!sep->arg[1][0]) { - c->Message(Chat::White, "Usage: #setadventurepoints [points] [theme]"); + c->Message(Chat::White, "Usage: #setadventurepoints [theme] [points]"); return; } if(!sep->IsNumber(1) || !sep->IsNumber(2)) { - c->Message(Chat::White, "Usage: #setadventurepoints [points] [theme]"); + c->Message(Chat::White, "Usage: #setadventurepoints [theme] [points]"); return; } diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index f0ba69526..44c159b76 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -1592,27 +1592,22 @@ XS(XS__addldonpoints); XS(XS__addldonpoints) { dXSARGS; if (items != 2) - Perl_croak(aTHX_ "Usage: quest::addldonpoints(int points, int theme_id)"); - -long points = (long)SvIV(ST(0)); -unsigned long theme_id = (unsigned long)SvUV(ST(1)); - -quest_manager.addldonpoints(points, theme_id); - -XSRETURN_EMPTY; + Perl_croak(aTHX_ "Usage: quest::addldonpoints(uint32 theme_id, int points)"); + + uint32 theme_id = (uint32) SvUV(ST(0)); + int points = (int) SvIV(ST(1)); + quest_manager.addldonpoints(theme_id, points); + XSRETURN_EMPTY; } XS(XS__addldonwin); XS(XS__addldonwin) { dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::addldonwin(int wins, int theme_id)"); - - long wins = (long)SvIV(ST(0)); - unsigned long theme_id = (unsigned long)SvUV(ST(1)); - - quest_manager.addldonwin(wins, theme_id); + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::addldonwin(uint32 theme_id)"); + uint32 theme_id = (uint32) SvUV(ST(0)); + quest_manager.addldonwin(theme_id); XSRETURN_EMPTY; } @@ -1620,13 +1615,10 @@ XS(XS__addldonloss); XS(XS__addldonloss) { dXSARGS; if (items != 2) - Perl_croak(aTHX_ "Usage: quest::addldonloss(int losses, int theme_id)"); - - long losses = (long)SvIV(ST(0)); - unsigned long theme_id = (unsigned long)SvUV(ST(1)); - - quest_manager.addldonloss(losses, theme_id); + Perl_croak(aTHX_ "Usage: quest::addldonloss(uint32 theme_id)"); + uint32 theme_id = (uint32) SvUV(ST(0)); + quest_manager.addldonloss(theme_id); XSRETURN_EMPTY; } @@ -6553,6 +6545,221 @@ XS(XS__setexpmodifierbycharid) { XSRETURN_EMPTY; } +XS(XS__crosszoneaddldonlossbycharid); +XS(XS__crosszoneaddldonlossbycharid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonlossbycharid(int character_id, uint32 theme_id)"); + + uint8 update_type = CZLDoNUpdateType_Character; + uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + int character_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id); + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonpointsbycharid); +XS(XS__crosszoneaddldonpointsbycharid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonpointsbycharid(int character_id, uint32 theme_id, int points)"); + + uint8 update_type = CZLDoNUpdateType_Character; + uint8 update_subtype = CZLDoNUpdateSubtype_Points; + int character_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + int points = (int) SvIV(ST(2)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id, points); + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonwinbycharid); +XS(XS__crosszoneaddldonwinbycharid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonwinbycharid(int character_id, uint32 theme_id)"); + + uint8 update_type = CZLDoNUpdateType_Character; + uint8 update_subtype = CZLDoNUpdateSubtype_Win; + int character_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id); + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonlossbygroupid); +XS(XS__crosszoneaddldonlossbygroupid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonlossbygroupid(int group_id, uint32 theme_id)"); + + uint8 update_type = CZLDoNUpdateType_Group; + uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + int group_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id); + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonpointsbygroupid); +XS(XS__crosszoneaddldonpointsbygroupid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonpointsbygroupid(int group_id, uint32 theme_id, int points)"); + + uint8 update_type = CZLDoNUpdateType_Group; + uint8 update_subtype = CZLDoNUpdateSubtype_Points; + int group_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + int points = (int) SvIV(ST(2)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id, points); + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonwinbygroupid); +XS(XS__crosszoneaddldonwinbygroupid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonwinbygroupid(int group_id, uint32 theme_id)"); + + uint8 update_type = CZLDoNUpdateType_Group; + uint8 update_subtype = CZLDoNUpdateSubtype_Win; + int group_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id); + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonlossbyraidid); +XS(XS__crosszoneaddldonlossbyraidid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonlossbyraidid(int raid_id, uint32 theme_id)"); + + uint8 update_type = CZLDoNUpdateType_Raid; + uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + int raid_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id); + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonpointsbyraidid); +XS(XS__crosszoneaddldonpointsbyraidid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonpointsbyraidid(int raid_id, uint32 theme_id, int points)"); + + uint8 update_type = CZLDoNUpdateType_Raid; + uint8 update_subtype = CZLDoNUpdateSubtype_Points; + int raid_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + int points = (int) SvIV(ST(2)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id, points); + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonwinbyraidid); +XS(XS__crosszoneaddldonwinbyraidid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonwinbyraidid(int raid_id, uint32 theme_id)"); + + uint8 update_type = CZLDoNUpdateType_Raid; + uint8 update_subtype = CZLDoNUpdateSubtype_Win; + int raid_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id); + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonlossbyguildid); +XS(XS__crosszoneaddldonlossbyguildid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonlossbyguildid(int guild_id, uint32 theme_id)"); + + uint8 update_type = CZLDoNUpdateType_Guild; + uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + int guild_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id); + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonpointsbyguildid); +XS(XS__crosszoneaddldonpointsbyguildid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonpointsbyguildid(int guild_id, uint32 theme_id, int points)"); + + uint8 update_type = CZLDoNUpdateType_Guild; + uint8 update_subtype = CZLDoNUpdateSubtype_Points; + int guild_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + int points = (int) SvIV(ST(2)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id, points); + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonwinbyguildid); +XS(XS__crosszoneaddldonwinbyguildid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonwinbyguildid(int guild_id, uint32 theme_id)"); + + uint8 update_type = CZLDoNUpdateType_Guild; + uint8 update_subtype = CZLDoNUpdateSubtype_Win; + int guild_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id); + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonlossbyexpeditionid); +XS(XS__crosszoneaddldonlossbyexpeditionid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonlossbyexpeditionid(uint32 expedition_id, uint32 theme_id)"); + + uint8 update_type = CZLDoNUpdateType_Expedition; + uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + uint32 expedition_id = (uint32) SvUV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id); + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonpointsbyexpeditionid); +XS(XS__crosszoneaddldonpointsbyexpeditionid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonpointsbyexpeditionid(uint32 expedition_id, uint32 theme_id, int points)"); + + uint8 update_type = CZLDoNUpdateType_Expedition; + uint8 update_subtype = CZLDoNUpdateSubtype_Points; + uint32 expedition_id = (uint32) SvUV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + int points = (int) SvIV(ST(2)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id, points); + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonwinbyexpeditionid); +XS(XS__crosszoneaddldonwinbyexpeditionid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonwinbyexpeditionid(uint32 expedition_id, uint32 theme_id)"); + + uint8 update_type = CZLDoNUpdateType_Expedition; + uint8 update_subtype = CZLDoNUpdateSubtype_Win; + uint32 expedition_id = (uint32) SvUV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id); + XSRETURN_EMPTY; +} + /* This is the callback perl will look for to setup the quest package's XSUBs @@ -6627,9 +6834,9 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "activetasksinset"), XS__activetasksinset, file); newXS(strcpy(buf, "add_expedition_lockout_all_clients"), XS__add_expedition_lockout_all_clients, file); newXS(strcpy(buf, "add_expedition_lockout_by_char_id"), XS__add_expedition_lockout_by_char_id, file); - newXS(strcpy(buf, "addldonloss"), XS__addldonpoints, file); + newXS(strcpy(buf, "addldonloss"), XS__addldonloss, file); newXS(strcpy(buf, "addldonpoints"), XS__addldonpoints, file); - newXS(strcpy(buf, "addldonwin"), XS__addldonpoints, file); + newXS(strcpy(buf, "addldonwin"), XS__addldonwin, file); newXS(strcpy(buf, "addloot"), XS__addloot, file); newXS(strcpy(buf, "addskill"), XS__addskill, file); newXS(strcpy(buf, "assigntask"), XS__assigntask, file); @@ -6652,6 +6859,21 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "creategroundobjectfrommodel"), XS__CreateGroundObjectFromModel, file); newXS(strcpy(buf, "createguild"), XS__createguild, file); newXS(strcpy(buf, "createitem"), XS__createitem, file); + newXS(strcpy(buf, "crosszoneaddldonlossbycharid"), XS__crosszoneaddldonlossbycharid, file); + newXS(strcpy(buf, "crosszoneaddldonlossbygroupid"), XS__crosszoneaddldonlossbygroupid, file); + newXS(strcpy(buf, "crosszoneaddldonlossbyraidid"), XS__crosszoneaddldonlossbyraidid, file); + newXS(strcpy(buf, "crosszoneaddldonlossbyguildid"), XS__crosszoneaddldonlossbyguildid, file); + newXS(strcpy(buf, "crosszoneaddldonlossbyexpeditionid"), XS__crosszoneaddldonlossbyexpeditionid, file); + newXS(strcpy(buf, "crosszoneaddldonpointsbycharid"), XS__crosszoneaddldonpointsbycharid, file); + newXS(strcpy(buf, "crosszoneaddldonpointsbygroupid"), XS__crosszoneaddldonpointsbygroupid, file); + newXS(strcpy(buf, "crosszoneaddldonpointsbyraidid"), XS__crosszoneaddldonpointsbyraidid, file); + newXS(strcpy(buf, "crosszoneaddldonpointsbyguildid"), XS__crosszoneaddldonpointsbyguildid, file); + newXS(strcpy(buf, "crosszoneaddldonpointsbyexpeditionid"), XS__crosszoneaddldonpointsbyexpeditionid, file); + newXS(strcpy(buf, "crosszoneaddldonwinbycharid"), XS__crosszoneaddldonwinbycharid, file); + newXS(strcpy(buf, "crosszoneaddldonwinbygroupid"), XS__crosszoneaddldonwinbygroupid, file); + newXS(strcpy(buf, "crosszoneaddldonwinbyraidid"), XS__crosszoneaddldonwinbyraidid, file); + newXS(strcpy(buf, "crosszoneaddldonwinbyguildid"), XS__crosszoneaddldonwinbyguildid, file); + newXS(strcpy(buf, "crosszoneaddldonwinbyexpeditionid"), XS__crosszoneaddldonwinbyexpeditionid, file); newXS(strcpy(buf, "crosszoneassigntaskbycharid"), XS__crosszoneassigntaskbycharid, file); newXS(strcpy(buf, "crosszoneassigntaskbygroupid"), XS__crosszoneassigntaskbygroupid, file); newXS(strcpy(buf, "crosszoneassigntaskbyraidid"), XS__crosszoneassigntaskbyraidid, file); diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 6b6265ce5..a84a52813 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -234,9 +234,9 @@ uint32 Lua_Client::GetTotalSecondsPlayed() { return self->GetTotalSecondsPlayed(); } -void Lua_Client::UpdateLDoNPoints(int points, uint32 theme) { +void Lua_Client::UpdateLDoNPoints(uint32 theme_id, int points) { Lua_Safe_Call_Void(); - self->UpdateLDoNPoints(points, theme); + self->UpdateLDoNPoints(theme_id, points); } void Lua_Client::SetDeity(int v) { @@ -2083,6 +2083,16 @@ void Lua_Client::SetEXPModifier(uint32 zone_id, double exp_modifier) { self->SetEXPModifier(zone_id, exp_modifier); } +void Lua_Client::AddLDoNLoss(uint32 theme_id) { + Lua_Safe_Call_Void(); + self->AddLDoNLoss(theme_id); +} + +void Lua_Client::AddLDoNWin(uint32 theme_id) { + Lua_Safe_Call_Void(); + self->AddLDoNWin(theme_id); +} + luabind::scope lua_register_client() { return luabind::class_("Client") .def(luabind::constructor<>()) @@ -2129,7 +2139,7 @@ luabind::scope lua_register_client() { .def("GetAAExp", (uint32(Lua_Client::*)(void))&Lua_Client::GetAAExp) .def("GetAAPercent", (uint32(Lua_Client::*)(void))&Lua_Client::GetAAPercent) .def("GetTotalSecondsPlayed", (uint32(Lua_Client::*)(void))&Lua_Client::GetTotalSecondsPlayed) - .def("UpdateLDoNPoints", (void(Lua_Client::*)(int,uint32))&Lua_Client::UpdateLDoNPoints) + .def("UpdateLDoNPoints", (void(Lua_Client::*)(uint32,int))&Lua_Client::UpdateLDoNPoints) .def("SetDeity", (void(Lua_Client::*)(int))&Lua_Client::SetDeity) .def("AddEXP", (void(Lua_Client::*)(uint32))&Lua_Client::AddEXP) .def("AddEXP", (void(Lua_Client::*)(uint32,int))&Lua_Client::AddEXP) @@ -2435,7 +2445,9 @@ luabind::scope lua_register_client() { .def("GetAAEXPModifier", (double(Lua_Client::*)(uint32))&Lua_Client::GetAAEXPModifier) .def("GetEXPModifier", (double(Lua_Client::*)(uint32))&Lua_Client::GetEXPModifier) .def("SetAAEXPModifier", (void(Lua_Client::*)(uint32,double))&Lua_Client::SetAAEXPModifier) - .def("SetEXPModifier", (void(Lua_Client::*)(uint32,double))&Lua_Client::SetEXPModifier); + .def("SetEXPModifier", (void(Lua_Client::*)(uint32,double))&Lua_Client::SetEXPModifier) + .def("AddLDoNLoss", (void(Lua_Client::*)(uint32))&Lua_Client::AddLDoNLoss) + .def("AddLDoNWin", (void(Lua_Client::*)(uint32))&Lua_Client::AddLDoNWin); } luabind::scope lua_register_inventory_where() { diff --git a/zone/lua_client.h b/zone/lua_client.h index bfd0f6b84..743d32f65 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -77,7 +77,9 @@ public: uint32 GetAAExp(); uint32 GetAAPercent(); uint32 GetTotalSecondsPlayed(); - void UpdateLDoNPoints(int points, uint32 theme); + void AddLDoNLoss(uint32 theme_id); + void AddLDoNWin(uint32 theme_id); + void UpdateLDoNPoints(uint32 theme_id, int points); void SetDeity(int v); void AddEXP(uint32 add_exp); void AddEXP(uint32 add_exp, int conlevel); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index e01e32549..2e26b460f 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -2353,6 +2353,108 @@ void lua_set_exp_modifier_by_char_id(uint32 character_id, uint32 zone_id, double database.SetEXPModifier(character_id, zone_id, exp_modifier); } +void lua_add_ldon_loss(uint32 theme_id) { + quest_manager.addldonloss(theme_id); +} + +void lua_add_ldon_points(uint32 theme_id, int points) { + quest_manager.addldonpoints(theme_id, points); +} + +void lua_add_ldon_win(uint32 theme_id) { + quest_manager.addldonwin(theme_id); +} + +void lua_cross_zone_add_ldon_loss_by_char_id(int character_id, uint32 theme_id) { + uint8 update_type = CZLDoNUpdateType_Character; + uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id); +} + +void lua_cross_zone_add_ldon_points_by_char_id(int character_id, uint32 theme_id, int points) { + uint8 update_type = CZLDoNUpdateType_Character; + uint8 update_subtype = CZLDoNUpdateSubtype_Points; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id, points); +} + +void lua_cross_zone_add_ldon_win_by_char_id(int character_id, uint32 theme_id) { + uint8 update_type = CZLDoNUpdateType_Character; + uint8 update_subtype = CZLDoNUpdateSubtype_Win; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id); +} + +void lua_cross_zone_add_ldon_loss_by_group_id(int group_id, uint32 theme_id) { + uint8 update_type = CZLDoNUpdateType_Group; + uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id); +} + +void lua_cross_zone_add_ldon_points_by_group_id(int group_id, uint32 theme_id, int points) { + uint8 update_type = CZLDoNUpdateType_Group; + uint8 update_subtype = CZLDoNUpdateSubtype_Points; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id, points); +} + +void lua_cross_zone_add_ldon_win_by_group_id(int group_id, uint32 theme_id) { + uint8 update_type = CZLDoNUpdateType_Group; + uint8 update_subtype = CZLDoNUpdateSubtype_Win; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id); +} + +void lua_cross_zone_add_ldon_loss_by_raid_id(int raid_id, uint32 theme_id) { + uint8 update_type = CZLDoNUpdateType_Raid; + uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id); +} + +void lua_cross_zone_add_ldon_points_by_raid_id(int raid_id, uint32 theme_id, int points) { + uint8 update_type = CZLDoNUpdateType_Raid; + uint8 update_subtype = CZLDoNUpdateSubtype_Points; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id, points); +} + +void lua_cross_zone_add_ldon_win_by_raid_id(int raid_id, uint32 theme_id) { + uint8 update_type = CZLDoNUpdateType_Raid; + uint8 update_subtype = CZLDoNUpdateSubtype_Win; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id); +} + +void lua_cross_zone_add_ldon_loss_by_guild_id(int guild_id, uint32 theme_id) { + uint8 update_type = CZLDoNUpdateType_Guild; + uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id); +} + +void lua_cross_zone_add_ldon_points_by_guild_id(int guild_id, uint32 theme_id, int points) { + uint8 update_type = CZLDoNUpdateType_Guild; + uint8 update_subtype = CZLDoNUpdateSubtype_Points; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id, points); +} + +void lua_cross_zone_add_ldon_win_by_guild_id(int guild_id, uint32 theme_id) { + uint8 update_type = CZLDoNUpdateType_Guild; + uint8 update_subtype = CZLDoNUpdateSubtype_Win; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id); +} + +void lua_cross_zone_add_ldon_loss_by_expedition_id(uint32 expedition_id, uint32 theme_id) { + uint8 update_type = CZLDoNUpdateType_Expedition; + uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id); +} + +void lua_cross_zone_add_ldon_points_by_expedition_id(uint32 expedition_id, uint32 theme_id, int points) { + uint8 update_type = CZLDoNUpdateType_Expedition; + uint8 update_subtype = CZLDoNUpdateSubtype_Points; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id, points); +} + +void lua_cross_zone_add_ldon_win_by_expedition_id(uint32 expedition_id, uint32 theme_id) { + uint8 update_type = CZLDoNUpdateType_Expedition; + uint8 update_subtype = CZLDoNUpdateSubtype_Win; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id); +} + #define LuaCreateNPCParse(name, c_type, default_value) do { \ cur = table[#name]; \ if(luabind::type(cur) != LUA_TNIL) { \ @@ -2899,7 +3001,24 @@ luabind::scope lua_register_general() { luabind::def("get_exp_modifier_by_char_id", &lua_get_exp_modifier_by_char_id), luabind::def("set_aa_exp_modifier_by_char_id", &lua_set_aa_exp_modifier_by_char_id), luabind::def("set_exp_modifier_by_char_id", &lua_set_exp_modifier_by_char_id), - + luabind::def("add_ldon_loss", &lua_add_ldon_loss), + luabind::def("add_ldon_points", &lua_add_ldon_points), + luabind::def("add_ldon_win", &lua_add_ldon_win), + luabind::def("cross_zone_add_ldon_loss_by_char_id", &lua_cross_zone_add_ldon_loss_by_char_id), + luabind::def("cross_zone_add_ldon_points_by_char_id", &lua_cross_zone_add_ldon_points_by_char_id), + luabind::def("cross_zone_add_ldon_win_by_char_id", &lua_cross_zone_add_ldon_win_by_char_id), + luabind::def("cross_zone_add_ldon_loss_by_group_id", &lua_cross_zone_add_ldon_loss_by_group_id), + luabind::def("cross_zone_add_ldon_points_by_group_id", &lua_cross_zone_add_ldon_points_by_group_id), + luabind::def("cross_zone_add_ldon_win_by_group_id", &lua_cross_zone_add_ldon_win_by_group_id), + luabind::def("cross_zone_add_ldon_loss_by_raid_id", &lua_cross_zone_add_ldon_loss_by_raid_id), + luabind::def("cross_zone_add_ldon_points_by_raid_id", &lua_cross_zone_add_ldon_points_by_raid_id), + luabind::def("cross_zone_add_ldon_win_by_raid_id", &lua_cross_zone_add_ldon_win_by_raid_id), + luabind::def("cross_zone_add_ldon_loss_by_guild_id", &lua_cross_zone_add_ldon_loss_by_guild_id), + luabind::def("cross_zone_add_ldon_points_by_guild_id", &lua_cross_zone_add_ldon_points_by_guild_id), + luabind::def("cross_zone_add_ldon_win_by_guild_id", &lua_cross_zone_add_ldon_win_by_guild_id), + luabind::def("cross_zone_add_ldon_loss_by_expedition_id", &lua_cross_zone_add_ldon_loss_by_expedition_id), + luabind::def("cross_zone_add_ldon_points_by_expedition_id", &lua_cross_zone_add_ldon_points_by_expedition_id), + luabind::def("cross_zone_add_ldon_win_by_expedition_id", &lua_cross_zone_add_ldon_win_by_expedition_id), /** * Expansions */ diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 4d675d8fb..054664c69 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -672,15 +672,15 @@ XS(XS_Client_UpdateLDoNPoints); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_UpdateLDoNPoints) { dXSARGS; if (items != 3) - Perl_croak(aTHX_ "Usage: Client::UpdateLDoNPoints(THIS, int32 points, uint32 theme)"); // @categories Currency and Points + Perl_croak(aTHX_ "Usage: Client::UpdateLDoNPoints(THIS, uint32 theme_id, int points)"); // @categories Currency and Points { Client *THIS; - bool RETVAL; - int32 points = (int32) SvIV(ST(1)); - uint32 theme = (uint32) SvUV(ST(2)); + bool RETVAL; + uint32 theme_id = (uint32) SvUV(ST(1)); + int points = (int) SvIV(ST(2)); VALIDATE_THIS_IS_CLIENT; - RETVAL = THIS->UpdateLDoNPoints(points, theme); - ST(0) = boolSV(RETVAL); + RETVAL = THIS->UpdateLDoNPoints(theme_id, points); + ST(0) = boolSV(RETVAL); sv_2mortal(ST(0)); } XSRETURN(1); @@ -5272,7 +5272,7 @@ XS(XS_Client_SetAAEXPModifier) { VALIDATE_THIS_IS_CLIENT; THIS->SetAAEXPModifier(zone_id, aa_modifier); } - XSRETURN_EMPTY; + XSRETURN_EMPTY; } XS(XS_Client_SetEXPModifier); @@ -5287,7 +5287,35 @@ XS(XS_Client_SetEXPModifier) { VALIDATE_THIS_IS_CLIENT; THIS->SetEXPModifier(zone_id, exp_modifier); } - XSRETURN_EMPTY; + XSRETURN_EMPTY; +} + +XS(XS_Client_AddLDoNLoss); +XS(XS_Client_AddLDoNLoss) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::AddLDoNLoss(THIS, uint32 theme_id)"); + { + Client* THIS; + uint32 theme_id = (uint32) SvUV(ST(1)); + VALIDATE_THIS_IS_CLIENT; + THIS->AddLDoNLoss(theme_id); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_AddLDoNWin); +XS(XS_Client_AddLDoNWin) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::AddLDoNWin(THIS, uint32 theme_id)"); + { + Client* THIS; + uint32 theme_id = (uint32) SvUV(ST(1)); + VALIDATE_THIS_IS_CLIENT; + THIS->AddLDoNWin(theme_id); + } + XSRETURN_EMPTY; } #ifdef __cplusplus @@ -5318,6 +5346,8 @@ XS(boot_Client) { newXSproto(strcpy(buf, "AddEXP"), XS_Client_AddEXP, file, "$$;$$"); newXSproto(strcpy(buf, "AddExpeditionLockout"), XS_Client_AddExpeditionLockout, file, "$$$$;$"); newXSproto(strcpy(buf, "AddExpeditionLockoutDuration"), XS_Client_AddExpeditionLockoutDuration, file, "$$$$;$"); + newXSproto(strcpy(buf, "AddLDoNLoss"), XS_Client_AddLDoNLoss, file, "$$"); + newXSproto(strcpy(buf, "AddLDoNWin"), XS_Client_AddLDoNWin, file, "$$"); newXSproto(strcpy(buf, "AddLevelBasedExp"), XS_Client_AddLevelBasedExp, file, "$$;$$"); newXSproto(strcpy(buf, "AddMoneyToPP"), XS_Client_AddMoneyToPP, file, "$$$$$$"); newXSproto(strcpy(buf, "AddPVPPoints"), XS_Client_AddPVPPoints, file, "$$"); diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 561f3eb3a..09046f775 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1727,22 +1727,22 @@ void QuestManager::resume() { owner->CastToNPC()->ResumeWandering(); } -void QuestManager::addldonpoints(int32 points, uint32 theme) { +void QuestManager::addldonpoints(uint32 theme_id, int points) { QuestManagerCurrentQuestVars(); if(initiator) - initiator->UpdateLDoNPoints(points, theme); + initiator->UpdateLDoNPoints(theme_id, points); } -void QuestManager::addldonwin(int32 wins, uint32 theme) { +void QuestManager::addldonloss(uint32 theme_id) { QuestManagerCurrentQuestVars(); if(initiator) - initiator->UpdateLDoNWins(theme, wins); + initiator->AddLDoNLoss(theme_id); } -void QuestManager::addldonloss(int32 losses, uint32 theme) { +void QuestManager::addldonwin(uint32 theme_id) { QuestManagerCurrentQuestVars(); if(initiator) - initiator->UpdateLDoNLosses(theme, losses); + initiator->AddLDoNWin(theme_id); } void QuestManager::setnexthpevent(int at) { @@ -4632,3 +4632,15 @@ void QuestManager::SetAAEXPModifierByCharID(uint32 character_id, uint32 zone_id, void QuestManager::SetEXPModifierByCharID(uint32 character_id, uint32 zone_id, double exp_modifier) { database.SetEXPModifier(character_id, zone_id, exp_modifier); } + +void QuestManager::CrossZoneLDoNUpdate(uint8 type, uint8 subtype, int identifier, uint32 theme_id, int points) { + auto pack = new ServerPacket(ServerOP_CZLDoNUpdate, sizeof(CZLDoNUpdate_Struct)); + CZLDoNUpdate_Struct* CZLU = (CZLDoNUpdate_Struct*)pack->pBuffer; + CZLU->update_type = type; + CZLU->update_subtype = subtype; + CZLU->update_identifier = identifier; + CZLU->theme_id = theme_id; + CZLU->points = points; + worldserver.SendPacket(pack); + safe_delete(pack); +} diff --git a/zone/questmgr.h b/zone/questmgr.h index 95166886f..815a10e35 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -158,9 +158,9 @@ public: void pause(int duration); void moveto(const glm::vec4& position, bool saveguardspot); void resume(); - void addldonpoints(int32 points, uint32 theme); - void addldonwin(int32 wins, uint32 theme); - void addldonloss(int32 losses, uint32 theme); + void addldonpoints(uint32 theme_id, int points); + void addldonloss(uint32 theme_id); + void addldonwin(uint32 theme_id); void setnexthpevent(int at); void setnextinchpevent(int at); void respawn(int npc_type, int grid); @@ -308,6 +308,7 @@ public: void CrossZoneFailTaskByGroupID(int group_id, uint32 task_id); void CrossZoneFailTaskByRaidID(int raid_id, uint32 task_id); void CrossZoneFailTaskByGuildID(int guild_id, uint32 task_id); + void CrossZoneLDoNUpdate(uint8 type, uint8 subtype, int identifier, uint32 theme_id, int points = 1); void CrossZoneMarqueeByCharID(int character_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message); void CrossZoneMarqueeByGroupID(int group_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message); void CrossZoneMarqueeByRaidID(int raid_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 545625d0d..beef1c8f8 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1932,6 +1932,115 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } break; } + case ServerOP_CZLDoNUpdate: + { + CZLDoNUpdate_Struct* CZLU = (CZLDoNUpdate_Struct*) pack->pBuffer; + uint8 update_type = CZLU->update_type; + uint8 update_subtype = CZLU->update_subtype; + int update_identifier = CZLU->update_identifier; + uint32 theme_id = CZLU->theme_id; + int points = CZLU->points; + if (update_type == CZLDoNUpdateType_Character) { + auto client = entity_list.GetClientByCharID(update_identifier); + if (client) { + switch (update_subtype) { + case CZLDoNUpdateSubtype_Loss: + client->AddLDoNLoss(theme_id); + break; + case CZLDoNUpdateSubtype_Points: + client->UpdateLDoNPoints(theme_id, points); + break; + case CZLDoNUpdateSubtype_Win: + client->AddLDoNWin(theme_id); + break; + default: + break; + } + } + break; + } else if (update_type == CZLDoNUpdateType_Group) { + auto client_group = entity_list.GetGroupByID(update_identifier); + if (client_group) { + for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { + if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { + auto client_group_member = client_group->members[member_index]->CastToClient(); + switch (update_subtype) { + case CZLDoNUpdateSubtype_Loss: + client_group_member->AddLDoNLoss(theme_id); + break; + case CZLDoNUpdateSubtype_Points: + client_group_member->UpdateLDoNPoints(theme_id, points); + break; + case CZLDoNUpdateSubtype_Win: + client_group_member->AddLDoNWin(theme_id); + break; + default: + break; + } + } + } + } + } else if (update_type == CZLDoNUpdateType_Raid) { + auto client_raid = entity_list.GetRaidByID(update_identifier); + if (client_raid) { + for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { + auto client_raid_member = client_raid->members[member_index].member; + if (client_raid_member && client_raid_member->IsClient()) { + switch (update_subtype) { + case CZLDoNUpdateSubtype_Loss: + client_raid_member->AddLDoNLoss(theme_id); + break; + case CZLDoNUpdateSubtype_Points: + client_raid_member->UpdateLDoNPoints(theme_id, points); + break; + case CZLDoNUpdateSubtype_Win: + client_raid_member->AddLDoNWin(theme_id); + break; + default: + break; + } + } + } + } + } else if (update_type == CZLDoNUpdateType_Guild) { + for (auto &client : entity_list.GetClientList()) { + if (client.second->GuildID() > 0 && client.second->GuildID() == update_identifier) { + switch (update_subtype) { + case CZLDoNUpdateSubtype_Loss: + client.second->AddLDoNLoss(theme_id); + break; + case CZLDoNUpdateSubtype_Points: + client.second->UpdateLDoNPoints(theme_id, points); + break; + case CZLDoNUpdateSubtype_Win: + client.second->AddLDoNWin(theme_id); + break; + default: + break; + } + } + } + } else if (update_type == CZLDoNUpdateType_Expedition) { + for (auto &client : entity_list.GetClientList()) { + if (client.second->GetExpedition() && client.second->GetExpedition()->GetID() == update_identifier) { + switch (update_subtype) { + case CZLDoNUpdateSubtype_Loss: + client.second->AddLDoNLoss(theme_id); + break; + case CZLDoNUpdateSubtype_Points: + client.second->UpdateLDoNPoints(theme_id, points); + break; + case CZLDoNUpdateSubtype_Win: + client.second->AddLDoNWin(theme_id); + break; + default: + break; + } + } + } + } + break; + } case ServerOP_CZMarqueePlayer: { CZMarqueePlayer_Struct* CZMS = (CZMarqueePlayer_Struct*) pack->pBuffer; From 542ec386609a5cfb82d765cbc4c5ed6622840ff9 Mon Sep 17 00:00:00 2001 From: Dencelle Date: Mon, 24 May 2021 21:16:46 -0500 Subject: [PATCH 055/624] [Bug Fix] Fix for charges not being sold correctly (#1357) * fix for charges not being sold correctly https://github.com/EQEmu/Server/issues/1350 this fixes this issue * Update client_packet.cpp * Update zone.cpp this completes the fix for all charge items being sold to merchants * code opmizations could probably be fixed up better but this is a little cleaner * Update snake_casing Co-authored-by: Chris Miles --- zone/client_packet.cpp | 9 +++------ zone/zone.cpp | 7 +++---- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index f4ef29224..9ececb451 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -12990,9 +12990,9 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) int16 freeslotid = INVALID_INDEX; int16 charges = 0; - if (item->Stackable || item->MaxCharges > 1) + if (item->Stackable || tmpmer_used) charges = mp->quantity; - else + else if ( item->MaxCharges > 1) charges = item->MaxCharges; EQ::ItemInstance* inst = database.CreateItem(item, charges); @@ -13238,12 +13238,9 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app) 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) { + if ((freeslot = zone->SaveTempItem(vendor->CastToNPC()->MerchantType, vendor->GetNPCTypeID(), itemid, charges, true)) > 0) { EQ::ItemInstance* inst2 = inst->Clone(); while (true) { diff --git a/zone/zone.cpp b/zone/zone.cpp index 2f974c35a..3dceb1250 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -419,12 +419,11 @@ int Zone::SaveTempItem(uint32 merchantid, uint32 npcid, uint32 item, int32 charg if (!ml.origslot) { ml.origslot = ml.slot; } - - if (charges > 0) { + bool is_stackable = database.GetItem(item)->Stackable; + if ((is_stackable && charges > 0) || (!is_stackable && sold)) { database.SaveMerchantTemp(npcid, ml.origslot, item, ml.charges); tmp_merlist.push_back(ml); - } - else { + } else { database.DeleteMerchantTemp(npcid, ml.origslot); } } From 71e9dd5a3ca4c2d577abdddfb41bde22a2c9fab2 Mon Sep 17 00:00:00 2001 From: RoTPvP <77220477+RoT-PvP@users.noreply.github.com> Date: Sun, 30 May 2021 18:22:52 -0700 Subject: [PATCH 056/624] [PVP] Pvp guard assist code. (Guards will assist in PvP based on faction) (#1367) * Added Guard Assist Code * Added PvP Rule and Detrimental Spell Check * Added IsGuard() Method * Change from uint to bool * Added a faction check to IsGuard() * Simplified Guard Checks, reduced costs * Added IsNPC check to guard check * simplified pet check * Removed Magic numbers * Formatting Fix * Code fixes * Fixed constants Co-authored-by: ProducerZekServer --- common/eq_constants.h | 7 +++++++ common/races.h | 1 - common/ruletypes.h | 1 + zone/attack.cpp | 37 +++++++++++++++++++++++++++++++++++++ zone/npc.cpp | 32 ++++++++++++++++++++++++++++++++ zone/npc.h | 1 + zone/spells.cpp | 19 +++++++++++++++++++ 7 files changed, 97 insertions(+), 1 deletion(-) diff --git a/common/eq_constants.h b/common/eq_constants.h index a585bdd87..057051ff6 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -438,6 +438,13 @@ static const uint8 SkillDamageTypes[EQ::skills::HIGHEST_SKILL + 1] = // change t static const uint32 MAX_SPELL_DB_ID_VAL = 65535; +static const uint32 DB_SPELL_CAZIC_TOUCH = 982; +static const uint32 DB_SPELL_TOUCH_OF_VINITRAS = 2859; + +static const uint32 DB_FACTION_GEM_CHOPPERS = 255; +static const uint32 DB_FACTION_HERETICS = 265; +static const uint32 DB_FACTION_KING_AKANON = 333; + enum ChatChannelNames : uint16 { ChatChannel_Guild = 0, diff --git a/common/races.h b/common/races.h index 5e7411a25..b492fb03a 100644 --- a/common/races.h +++ b/common/races.h @@ -860,7 +860,6 @@ uint16 GetRaceIDFromPlayerRaceBit(uint32 player_race_bit); float GetRaceGenderDefaultHeight(int race, int gender); - // player race-/gender-based model feature validators namespace PlayerAppearance { diff --git a/common/ruletypes.h b/common/ruletypes.h index 722ce5dc3..e2c25fb02 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -163,6 +163,7 @@ RULE_BOOL(Character, SoftDeletes, true, "When characters are deleted in characte RULE_INT(Character, DefaultGuild, 0, "If not 0, new characters placed into the guild # indicated") RULE_BOOL(Character, ProcessFearedProximity, false, "Processes proximity checks when feared") RULE_BOOL(Character, EnableCharacterEXPMods, false, "Enables character zone-based experience modifiers.") +RULE_BOOL(Character, PVPEnableGuardFactionAssist, true, "Enables faction based assisting against the aggresor in pvp.") RULE_CATEGORY_END() RULE_CATEGORY(Mercs) diff --git a/zone/attack.cpp b/zone/attack.cpp index de9a6101e..fbf4af53e 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1522,6 +1522,25 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b other->AddToHateList(this, hate); + //Guard Assist Code + if (RuleB(Character, PVPEnableGuardFactionAssist)) { + if (IsClient() || (HasOwner() && GetOwner()->IsClient())) { + auto& mob_list = entity_list.GetCloseMobList(other); + for (auto& e : mob_list) { + auto mob = e.second; + if (mob->IsNPC() && mob->CastToNPC()->IsGuard()) { + float distance = Distance(other->CastToClient()->m_Position, mob->GetPosition()); + if ((mob->CheckLosFN(other) || mob->CheckLosFN(this)) && distance <= 70) { + auto petorowner = GetOwnerOrSelf(); + if (other->GetReverseFactionCon(mob) <= petorowner->GetReverseFactionCon(mob)) { + mob->AddToHateList(this); + } + } + } + } + } + } + /////////////////////////////////////////////////////////// ////// Send Attack Damage /////////////////////////////////////////////////////////// @@ -2002,6 +2021,24 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool } } + //Guard Assist Code + if (RuleB(Character, PVPEnableGuardFactionAssist)) { + if (IsClient() || (HasOwner() && GetOwner()->IsClient())) { + auto& mob_list = entity_list.GetCloseMobList(other); + for (auto& e : mob_list) { + auto mob = e.second; + if (mob->IsNPC() && mob->CastToNPC()->IsGuard()) { + float distance = Distance(other->GetPosition(), mob->GetPosition()); + if ((mob->CheckLosFN(other) || mob->CheckLosFN(this)) && distance <= 70) { + if (other->GetReverseFactionCon(mob) <= GetOwner()->GetReverseFactionCon(mob)) { + mob->AddToHateList(this); + } + } + } + } + } + } + int weapon_damage = GetWeaponDamage(other, weapon); //do attack animation regardless of whether or not we can hit below diff --git a/zone/npc.cpp b/zone/npc.cpp index 9bea0ec67..325f8fb70 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -3368,3 +3368,35 @@ void NPC::ScaleNPC(uint8 npc_level) { npc_scale_manager->ResetNPCScaling(this); npc_scale_manager->ScaleNPC(this); } + +bool NPC::IsGuard() +{ + switch (GetRace()) { + case RT_GUARD: + if (GetTexture() == 1 || GetTexture() == 2) + return true; + break; + case RT_IKSAR_2: + if (GetTexture() == 1) + return true; + break; + case RT_GUARD_2: + case RT_GUARD_3: + case RT_GUARD_4: + case RT_HUMAN_3: + case RT_HALFLING_2: + case RT_ERUDITE_2: + case RT_BARBARIAN_2: + case RT_DARK_ELF_2: + case RT_TROLL_2: + case OGGOK_CITIZEN: + case RT_DWARF_2: + return true; + default: + break; + } + if (GetPrimaryFaction() == DB_FACTION_GEM_CHOPPERS || GetPrimaryFaction() == DB_FACTION_HERETICS || GetPrimaryFaction() == DB_FACTION_KING_AKANON) { //these 3 factions of guards use player races instead of their own races so we must define them by faction. + return true; + } + return false; +} diff --git a/zone/npc.h b/zone/npc.h index b23d15568..995542e0d 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -175,6 +175,7 @@ public: bool DatabaseCastAccepted(int spell_id); bool IsFactionListAlly(uint32 other_faction); + bool IsGuard(); FACTION_VALUE CheckNPCFactionAlly(int32 other_faction); virtual FACTION_VALUE GetReverseFactionCon(Mob* iOther); diff --git a/zone/spells.cpp b/zone/spells.cpp index f7536cd04..15a1151e0 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2044,6 +2044,25 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui if(!IsValidSpell(spell_id)) return false; + //Guard Assist Code + if (RuleB(Character, PVPEnableGuardFactionAssist) && IsDetrimentalSpell(spell_id) && spell_target != this) { + if (IsClient() || (HasOwner() && GetOwner()->IsClient())) { + auto& mob_list = entity_list.GetCloseMobList(spell_target); + for (auto& e : mob_list) { + auto mob = e.second; + if (mob->IsNPC() && mob->CastToNPC()->IsGuard()) { + float distance = Distance(spell_target->GetPosition(), mob->GetPosition()); + if ((mob->CheckLosFN(spell_target) || mob->CheckLosFN(this)) && distance <= 70) { + auto petorowner = GetOwnerOrSelf(); + if (spell_target->GetReverseFactionCon(mob) <= petorowner->GetReverseFactionCon(mob)) { + mob->AddToHateList(this); + } + } + } + } + } + } + if( spells[spell_id].zonetype == 1 && !zone->CanCastOutdoor()){ if(IsClient()){ if(!CastToClient()->GetGM()){ From f0d0c837108e6925103b268e33d4792ecdc9fe23 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 3 Jun 2021 11:17:56 -0400 Subject: [PATCH 057/624] Magic numbers bad (#1373) --- common/spdat.h | 1 + zone/bonuses.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/common/spdat.h b/common/spdat.h index d6d6ef144..8f78cc992 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -32,6 +32,7 @@ #define SPELL_HARM_TOUCH2 2821 #define SPELL_IMP_HARM_TOUCH 2774 #define SPELL_NPC_HARM_TOUCH 929 +#define SPELL_AVATAR_ST_PROC 2434 #define EFFECT_COUNT 12 diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index fc513577d..19ad4a45e 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -204,7 +204,7 @@ void Client::ProcessItemCaps() // The Sleeper Tomb Avatar proc counts towards item ATK // The client uses a 100 here, so using a 100 here the client and server will agree // For example, if you set the effect to be 200 it will get 100 item ATK and 100 spell ATK - if (IsValidSpell(2434) && FindBuff(2434)) { + if (IsValidSpell(SPELL_AVATAR_ST_PROC) && FindBuff(SPELL_AVATAR_ST_PROC)) { itembonuses.ATK += 100; spellbonuses.ATK -= 100; } From 854a09fc842b3059dfd268c02d90d5ed7c732fac Mon Sep 17 00:00:00 2001 From: splose Date: Fri, 11 Jun 2021 14:27:52 -0400 Subject: [PATCH 058/624] [Bug Fix] Allow GMs to chat when stunned (#1380) --- zone/client_packet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 9ececb451..6d277b140 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -4214,7 +4214,7 @@ void Client::Handle_OP_ChannelMessage(const EQApplicationPacket *app) 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()) { + if (IsAIControlled() && !GetGM()) { Message(Chat::Red, "You try to speak but cant move your mouth!"); return; } From 02526072f3c851188dac07b22eafec73a89881e9 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 11 Jun 2021 14:28:35 -0400 Subject: [PATCH 059/624] [Quest API] Add Lua_Mob::GetShuffledHateList (#1381) This returns the hate list but in a random order. This is useful to prevent repeated (potentially infinite ...) calls to GetHateRandom() --- zone/lua_mob.cpp | 18 ++++++++++++++++++ zone/lua_mob.h | 1 + 2 files changed, 19 insertions(+) diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index 2948c8c71..869d6b27c 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -967,6 +967,23 @@ Lua_HateList Lua_Mob::GetHateList() { return ret; } +Lua_HateList Lua_Mob::GetShuffledHateList() { + Lua_Safe_Call_Class(Lua_HateList); + Lua_HateList ret; + + auto h_list = self->GetHateList(); + auto iter = h_list.begin(); + while(iter != h_list.end()) { + Lua_HateEntry e(*iter); + ret.entries.push_back(e); + ++iter; + } + + zone->random.Shuffle(ret.entries.begin(), ret.entries.end()); + + return ret; +} + Lua_Mob Lua_Mob::GetHateTop() { Lua_Safe_Call_Class(Lua_Mob); return Lua_Mob(self->GetHateTop()); @@ -2513,6 +2530,7 @@ luabind::scope lua_register_mob() { .def("GetPet", &Lua_Mob::GetPet) .def("GetOwner", &Lua_Mob::GetOwner) .def("GetHateList", &Lua_Mob::GetHateList) + .def("GetShuffledHateList", &Lua_Mob::GetShuffledHateList) .def("GetHateListByDistance", (Lua_HateList(Lua_Mob::*)(void))&Lua_Mob::GetHateListByDistance) .def("GetHateListByDistance", (Lua_HateList(Lua_Mob::*)(int))&Lua_Mob::GetHateListByDistance) .def("GetHateTop", (Lua_Mob(Lua_Mob::*)(void))&Lua_Mob::GetHateTop) diff --git a/zone/lua_mob.h b/zone/lua_mob.h index dbb3c8110..45bf78af6 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -199,6 +199,7 @@ public: Lua_Mob GetPet(); Lua_Mob GetOwner(); Lua_HateList GetHateList(); + Lua_HateList GetShuffledHateList(); Lua_HateList GetHateListByDistance(); Lua_HateList GetHateListByDistance(int distance); Lua_Mob GetHateTop(); From 0461ac7912c4a04bebad40c1e0a8b043eb4fd791 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 11 Jun 2021 14:29:09 -0400 Subject: [PATCH 060/624] [Rule] Allow Skill ups from items (Default: On) (#1376) --- common/ruletypes.h | 1 + zone/spells.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index e2c25fb02..a261afc25 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -164,6 +164,7 @@ RULE_INT(Character, DefaultGuild, 0, "If not 0, new characters placed into the g RULE_BOOL(Character, ProcessFearedProximity, false, "Processes proximity checks when feared") RULE_BOOL(Character, EnableCharacterEXPMods, false, "Enables character zone-based experience modifiers.") RULE_BOOL(Character, PVPEnableGuardFactionAssist, true, "Enables faction based assisting against the aggresor in pvp.") +RULE_BOOL(Character, SkillUpFromItems, true, "Allow Skill ups from clickable items") RULE_CATEGORY_END() RULE_CATEGORY(Mercs) diff --git a/zone/spells.cpp b/zone/spells.cpp index 15a1151e0..ec5a2c978 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1419,7 +1419,9 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo if(IsClient()) { Client *c = CastToClient(); - c->CheckSongSkillIncrease(spell_id); + if((IsFromItem && RuleB(Character, SkillUpFromItems)) || !IsFromItem) { + c->CheckSongSkillIncrease(spell_id); + } if (spells[spell_id].EndurTimerIndex > 0 && slot < CastingSlot::MaxGems) c->SetLinkedSpellReuseTimer(spells[spell_id].EndurTimerIndex, spells[spell_id].recast_time / 1000); c->MemorizeSpell(static_cast(slot), spell_id, memSpellSpellbar); @@ -1442,7 +1444,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo SetMana(GetMana()); // skills - if (EQ::skills::IsCastingSkill(spells[spell_id].skill)) { + if (EQ::skills::IsCastingSkill(spells[spell_id].skill) && ((IsFromItem && RuleB(Character, SkillUpFromItems)) || !IsFromItem)) { c->CheckIncreaseSkill(spells[spell_id].skill, nullptr); // increased chance of gaining channel skill if you regained concentration From c3456ebea0a8cf094e015d43d2df8eb33d8b230c Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 11 Jun 2021 14:30:56 -0400 Subject: [PATCH 061/624] [Bots] Remove hardcoded race-class combinations from bots. (#1375) * [Bots] Remove hardcoded race-class combinations from bots. - Allows server operators to directly influence via a database table the classes a specific bot race can be. - Previously this was hardcoded and required a source modification to do. - Allowed races, classes, and genders have been removed due to redundancy at this point. * Remove const cast and modify saylink definition. --- common/ruletypes.h | 3 - common/version.h | 2 +- .../sql/git/bots/bots_db_update_manifest.txt | 1 + .../2021_06_04_bot_create_combinations.sql | 34 ++ zone/bot.cpp | 323 +----------------- zone/bot.h | 2 - zone/bot_command.cpp | 131 ++++--- zone/bot_command.h | 1 + zone/bot_database.cpp | 14 + zone/bot_database.h | 1 + 10 files changed, 147 insertions(+), 365 deletions(-) create mode 100644 utils/sql/git/bots/required/2021_06_04_bot_create_combinations.sql diff --git a/common/ruletypes.h b/common/ruletypes.h index a261afc25..2e8529d70 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -563,9 +563,6 @@ RULE_BOOL(Bots, BotGroupXP, false, "Determines whether client gets experience fo RULE_BOOL(Bots, BotLevelsWithOwner, false, "Auto-updates spawned bots as owner levels/de-levels (false is original behavior)") RULE_INT(Bots, BotCharacterLevel, 0, "If level is greater that value player can spawn bots if BotCharacterLevelEnabled is true") RULE_INT(Bots, CasterStopMeleeLevel, 13, "Level at which caster bots stop melee attacks") -RULE_INT(Bots, AllowedClasses, 0xFFFFFFFF, "Bitmask of allowed bot classes") -RULE_INT(Bots, AllowedRaces, 0xFFFFFFFF, "Bitmask of allowed bot races") -RULE_INT(Bots, AllowedGenders, 0x3, "Bitmask of allowed bot genders") RULE_BOOL(Bots, AllowOwnerOptionAltCombat, true, "When option is enabled, bots will use an auto-/shared-aggro combat model") RULE_BOOL(Bots, AllowOwnerOptionAutoDefend, true, "When option is enabled, bots will defend their owner on enemy aggro") RULE_REAL(Bots, LeashDistance, 562500.0f, "Distance a bot is allowed to travel from leash owner before being pulled back (squared value)") diff --git a/common/version.h b/common/version.h index 70bb6c410..e04ba3418 100644 --- a/common/version.h +++ b/common/version.h @@ -37,7 +37,7 @@ #define CURRENT_BINARY_DATABASE_VERSION 9166 #ifdef BOTS - #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9027 + #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9028 #else #define CURRENT_BINARY_BOTS_DATABASE_VERSION 0 // must be 0 #endif diff --git a/utils/sql/git/bots/bots_db_update_manifest.txt b/utils/sql/git/bots/bots_db_update_manifest.txt index 86e7c49dd..221020683 100644 --- a/utils/sql/git/bots/bots_db_update_manifest.txt +++ b/utils/sql/git/bots/bots_db_update_manifest.txt @@ -26,6 +26,7 @@ 9025|2019_08_26_bots_owner_option_spawn_message.sql|SELECT * FROM db_version WHERE bots_version >= 9025|empty| 9026|2019_09_09_bots_owner_options_rework.sql|SHOW COLUMNS FROM `bot_owner_options` LIKE 'option_type'|empty| 9027|2020_03_30_bots_view_update.sql|SELECT * FROM db_version WHERE bots_version >= 9027|empty| +9028|2021_06_04_bot_create_combinations.sql|SHOW TABLES LIKE 'bot_create_combinations'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/bots/required/2021_06_04_bot_create_combinations.sql b/utils/sql/git/bots/required/2021_06_04_bot_create_combinations.sql new file mode 100644 index 000000000..00974fcda --- /dev/null +++ b/utils/sql/git/bots/required/2021_06_04_bot_create_combinations.sql @@ -0,0 +1,34 @@ +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for bot_create_combinations +-- ---------------------------- +DROP TABLE IF EXISTS `bot_create_combinations`; +CREATE TABLE `bot_create_combinations` ( + `race` int UNSIGNED NOT NULL DEFAULT 0, + `classes` int UNSIGNED NOT NULL DEFAULT 0, + PRIMARY KEY (`race`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Compact; + +-- ---------------------------- +-- Records of bot_create_combinations +-- ---------------------------- +INSERT INTO `bot_create_combinations` VALUES (1, 15871); -- Human +INSERT INTO `bot_create_combinations` VALUES (2, 49921); -- Barbarian +INSERT INTO `bot_create_combinations` VALUES (3, 15382); -- Erudite +INSERT INTO `bot_create_combinations` VALUES (4, 425); -- Wood Elf +INSERT INTO `bot_create_combinations` VALUES (5, 14342); -- High Elf +INSERT INTO `bot_create_combinations` VALUES (6, 15635); -- Dark Elf +INSERT INTO `bot_create_combinations` VALUES (7, 429); -- Half Elf +INSERT INTO `bot_create_combinations` VALUES (8, 33031); -- Dwarf +INSERT INTO `bot_create_combinations` VALUES (9, 49681); -- Troll +INSERT INTO `bot_create_combinations` VALUES (10, 49681); -- Ogre +INSERT INTO `bot_create_combinations` VALUES (11, 303); -- Halfling +INSERT INTO `bot_create_combinations` VALUES (12, 15639); -- Gnome +INSERT INTO `bot_create_combinations` VALUES (128, 18001); -- Iksar +INSERT INTO `bot_create_combinations` VALUES (130, 50049); -- Vah Shir +INSERT INTO `bot_create_combinations` VALUES (330, 3863); -- Froglok +INSERT INTO `bot_create_combinations` VALUES (522, 15871); -- Drakkin + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/zone/bot.cpp b/zone/bot.cpp index 99bcd079b..e3abb5935 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -1701,206 +1701,15 @@ bool Bot::IsValidRaceClassCombo() return Bot::IsValidRaceClassCombo(GetRace(), GetClass()); } -bool Bot::IsValidRaceClassCombo(uint16 r, uint8 c) +bool Bot::IsValidRaceClassCombo(uint16 bot_race, uint8 bot_class) { - switch (r) { - case HUMAN: - switch (c) { - case WARRIOR: - case CLERIC: - case PALADIN: - case RANGER: - case SHADOWKNIGHT: - case DRUID: - case MONK: - case BARD: - case ROGUE: - case NECROMANCER: - case WIZARD: - case MAGICIAN: - case ENCHANTER: - return true; - } - break; - case BARBARIAN: - switch (c) { - case WARRIOR: - case ROGUE: - case SHAMAN: - case BEASTLORD: - case BERSERKER: - return true; - } - break; - case ERUDITE: - switch (c) { - case CLERIC: - case PALADIN: - case SHADOWKNIGHT: - case NECROMANCER: - case WIZARD: - case MAGICIAN: - case ENCHANTER: - return true; - } - break; - case WOOD_ELF: - switch (c) { - case WARRIOR: - case RANGER: - case DRUID: - case BARD: - case ROGUE: - return true; - } - break; - case HIGH_ELF: - switch (c) { - case CLERIC: - case PALADIN: - case WIZARD: - case MAGICIAN: - case ENCHANTER: - return true; - } - break; - case DARK_ELF: - switch (c) { - case WARRIOR: - case CLERIC: - case SHADOWKNIGHT: - case ROGUE: - case NECROMANCER: - case WIZARD: - case MAGICIAN: - case ENCHANTER: - return true; - } - break; - case HALF_ELF: - switch (c) { - case WARRIOR: - case PALADIN: - case RANGER: - case DRUID: - case BARD: - case ROGUE: - return true; - } - break; - case DWARF: - switch (c) { - case WARRIOR: - case CLERIC: - case PALADIN: - case ROGUE: - case BERSERKER: - return true; - } - break; - case TROLL: - switch (c) { - case WARRIOR: - case SHADOWKNIGHT: - case SHAMAN: - case BEASTLORD: - case BERSERKER: - return true; - } - break; - case OGRE: - switch (c) { - case WARRIOR: - case SHADOWKNIGHT: - case SHAMAN: - case BEASTLORD: - case BERSERKER: - return true; - } - break; - case HALFLING: - switch (c) { - case WARRIOR: - case CLERIC: - case PALADIN: - case RANGER: - case DRUID: - case ROGUE: - return true; - } - break; - case GNOME: - switch (c) { - case WARRIOR: - case CLERIC: - case PALADIN: - case SHADOWKNIGHT: - case ROGUE: - case NECROMANCER: - case WIZARD: - case MAGICIAN: - case ENCHANTER: - return true; - } - break; - case IKSAR: - switch (c) { - case WARRIOR: - case SHADOWKNIGHT: - case MONK: - case SHAMAN: - case NECROMANCER: - case BEASTLORD: - return true; - } - break; - case VAHSHIR: - switch (c) { - case WARRIOR: - case BARD: - case ROGUE: - case SHAMAN: - case BEASTLORD: - case BERSERKER: - return true; - } - break; - case FROGLOK: - switch (c) { - case WARRIOR: - case CLERIC: - case PALADIN: - case SHADOWKNIGHT: - case ROGUE: - case SHAMAN: - case NECROMANCER: - case WIZARD: - return true; - } - break; - case DRAKKIN: - switch (c) { - case WARRIOR: - case CLERIC: - case PALADIN: - case RANGER: - case SHADOWKNIGHT: - case DRUID: - case MONK: - case BARD: - case ROGUE: - case NECROMANCER: - case WIZARD: - case MAGICIAN: - case ENCHANTER: - return true; - } - break; - default: - break; + bool is_valid = false; + auto classes = database.botdb.GetRaceClassBitmask(bot_race); + auto bot_class_bitmask = GetPlayerClassBit(bot_class); + if (classes & bot_class_bitmask) { + is_valid = true; } - - return false; + return is_valid; } bool Bot::IsValidName() @@ -4264,124 +4073,6 @@ void Bot::LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp) { } } -std::string Bot::ClassIdToString(uint16 classId) { - std::string Result; - - if(classId > 0 && classId < 17) { - switch(classId) { - case 1: - Result = std::string("Warrior"); - break; - case 2: - Result = std::string("Cleric"); - break; - case 3: - Result = std::string("Paladin"); - break; - case 4: - Result = std::string("Ranger"); - break; - case 5: - Result = std::string("Shadowknight"); - break; - case 6: - Result = std::string("Druid"); - break; - case 7: - Result = std::string("Monk"); - break; - case 8: - Result = std::string("Bard"); - break; - case 9: - Result = std::string("Rogue"); - break; - case 10: - Result = std::string("Shaman"); - break; - case 11: - Result = std::string("Necromancer"); - break; - case 12: - Result = std::string("Wizard"); - break; - case 13: - Result = std::string("Magician"); - break; - case 14: - Result = std::string("Enchanter"); - break; - case 15: - Result = std::string("Beastlord"); - break; - case 16: - Result = std::string("Berserker"); - break; - } - } - - return Result; -} - -std::string Bot::RaceIdToString(uint16 raceId) { - std::string Result; - - if(raceId > 0) { - switch(raceId) { - case 1: - Result = std::string("Human"); - break; - case 2: - Result = std::string("Barbarian"); - break; - case 3: - Result = std::string("Erudite"); - break; - case 4: - Result = std::string("Wood Elf"); - break; - case 5: - Result = std::string("High Elf"); - break; - case 6: - Result = std::string("Dark Elf"); - break; - case 7: - Result = std::string("Half Elf"); - break; - case 8: - Result = std::string("Dwarf"); - break; - case 9: - Result = std::string("Troll"); - break; - case 10: - Result = std::string("Ogre"); - break; - case 11: - Result = std::string("Halfling"); - break; - case 12: - Result = std::string("Gnome"); - break; - case 128: - Result = std::string("Iksar"); - break; - case 130: - Result = std::string("Vah Shir"); - break; - case 330: - Result = std::string("Froglok"); - break; - case 522: - Result = std::string("Drakkin"); - break; - } - } - - return Result; -} - void Bot::SendBotArcheryWearChange(uint8 material_slot, uint32 material, uint32 color) { EQApplicationPacket* outapp = new EQApplicationPacket(OP_WearChange, sizeof(WearChange_Struct)); WearChange_Struct* wc = (WearChange_Struct*)outapp->pBuffer; diff --git a/zone/bot.h b/zone/bot.h index caa556cf9..5ed980fe8 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -358,8 +358,6 @@ public: static uint32 SpawnedBotCount(uint32 botOwnerCharacterID); static void LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp); //static bool SetBotOwnerCharacterID(uint32 botID, uint32 botOwnerCharacterID, std::string* errorMessage); - static std::string ClassIdToString(uint16 classId); - static std::string RaceIdToString(uint16 raceId); static bool IsBotAttackAllowed(Mob* attacker, Mob* target, bool& hasRuleDefined); static Bot* GetBotByBotClientOwnerAndBotName(Client* c, std::string botName); static void ProcessBotGroupInvite(Client* c, std::string botName); diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 10b869d9c..65c2cc70a 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1119,7 +1119,7 @@ private: for (bcst_levels::iterator levels_iter = bot_levels.begin(); levels_iter != bot_levels.end(); ++levels_iter) { if (levels_iter->second < test_iter->second) test_iter = levels_iter; - if (strcasecmp(Bot::ClassIdToString(levels_iter->first).c_str(), Bot::ClassIdToString(test_iter->first).c_str()) < 0 && levels_iter->second <= test_iter->second) + if (strcasecmp(GetClassIDName(levels_iter->first), GetClassIDName(test_iter->first)) < 0 && levels_iter->second <= test_iter->second) test_iter = levels_iter; } @@ -1131,8 +1131,8 @@ private: else bot_segment = " or %s(%u)"; - required_bots_map[type_index].append(StringFormat(bot_segment.c_str(), Bot::ClassIdToString(test_iter->first).c_str(), test_iter->second)); - required_bots_map_by_class[type_index][test_iter->first] = StringFormat("%s(%u)", Bot::ClassIdToString(test_iter->first).c_str(), test_iter->second); + required_bots_map[type_index].append(StringFormat(bot_segment.c_str(), GetClassIDName(test_iter->first), test_iter->second)); + required_bots_map_by_class[type_index][test_iter->first] = StringFormat("%s(%u)", GetClassIDName(test_iter->first), test_iter->second); bot_levels.erase(test_iter); } } @@ -1428,6 +1428,7 @@ int bot_command_init(void) bot_command_add("suspend", "Suspends a bot's AI processing until released", 0, bot_command_suspend) || bot_command_add("taunt", "Toggles taunt use by a bot", 0, bot_command_taunt) || bot_command_add("track", "Orders a capable bot to track enemies", 0, bot_command_track) || + bot_command_add("viewcombos", "Views bot race class combinations", 0, bot_command_view_combos) || bot_command_add("waterbreathing", "Orders a bot to cast a water breathing spell", 0, bot_command_water_breathing) ) { bot_command_deinit(); @@ -5107,6 +5108,68 @@ void bot_subcommand_bot_clone(Client *c, const Seperator *sep) c->Message(m_action, "Bot '%s' was successfully cloned to bot '%s'", my_bot->GetCleanName(), bot_name.c_str()); } +void bot_command_view_combos(Client *c, const Seperator *sep) +{ + const std::string class_substrs[17] = { "", + "%u (WAR)", "%u (CLR)", "%u (PAL)", "%u (RNG)", + "%u (SHD)", "%u (DRU)", "%u (MNK)", "%u (BRD)", + "%u (ROG)", "%u (SHM)", "%u (NEC)", "%u (WIZ)", + "%u (MAG)", "%u (ENC)", "%u (BST)", "%u (BER)" + }; + + const std::string race_substrs[17] = { "", + "%u (HUM)", "%u (BAR)", "%u (ERU)", "%u (ELF)", + "%u (HIE)", "%u (DEF)", "%u (HEF)", "%u (DWF)", + "%u (TRL)", "%u (OGR)", "%u (HFL)", "%u (GNM)", + "%u (IKS)", "%u (VAH)", "%u (FRG)", "%u (DRK)" + }; + + const uint16 race_values[17] = { 0, + HUMAN, BARBARIAN, ERUDITE, WOOD_ELF, + HIGH_ELF, DARK_ELF, HALF_ELF, DWARF, + TROLL, OGRE, HALFLING, GNOME, + IKSAR, VAHSHIR, FROGLOK, DRAKKIN + }; + if (helper_command_alias_fail(c, "bot_command_view_combos", sep->arg[0], "viewcombos")) + return; + if (helper_is_help_or_usage(sep->arg[1])) { + std::string window_title = "Bot Races"; + std::string window_text; + std::string message_separator = " "; + c->Message(m_usage, "Usage: %s [bot_race]", sep->arg[0]); + window_text.append("Races:"); + for (int race_id = 0; race_id <= 15; ++race_id) { + window_text.append(message_separator); + window_text.append(StringFormat(race_substrs[race_id + 1].c_str(), race_values[race_id + 1])); + message_separator = ", "; + } + c->SendPopupToClient(window_title.c_str(), window_text.c_str()); + return; + } + + if (sep->arg[1][0] == '\0' || !sep->IsNumber(1)) { + c->Message(m_fail, "Invalid Race!"); + return; + } + uint16 bot_race = atoi(sep->arg[1]); + auto classes_bitmask = database.botdb.GetRaceClassBitmask(bot_race); + auto race_name = GetRaceIDName(bot_race); + std::string window_title = "Bot Classes"; + std::string window_text; + std::string message_separator = " "; + c->Message(m_usage, "%s can be these classes.", race_name); + window_text.append("Classes:"); + for (int class_id = 0; class_id <= 15; ++class_id) { + if (classes_bitmask & GetPlayerClassBit(class_id)) { + window_text.append(message_separator); + window_text.append(StringFormat(class_substrs[class_id].c_str(), class_id)); + message_separator = ", "; + } + } + c->SendPopupToClient(window_title.c_str(), window_text.c_str()); + return; +} + void bot_subcommand_bot_create(Client *c, const Seperator *sep) { const std::string class_substrs[17] = { "", @@ -5148,10 +5211,7 @@ void bot_subcommand_bot_create(Client *c, const Seperator *sep) message_separator = " "; object_count = 1; for (int i = 0; i <= 15; ++i) { - if (((1 << i) & RuleI(Bots, AllowedClasses)) == 0) - continue; - - window_text.append(const_cast(message_separator)); + window_text.append(message_separator); if (object_count >= object_max) { window_text.append("
"); object_count = 0; @@ -5166,10 +5226,7 @@ void bot_subcommand_bot_create(Client *c, const Seperator *sep) message_separator = " "; object_count = 1; for (int i = 0; i <= 15; ++i) { - if (((1 << i) & RuleI(Bots, AllowedRaces)) == 0) - continue; - - window_text.append(const_cast(message_separator)); + window_text.append(message_separator); if (object_count >= object_max) { window_text.append("
"); object_count = 0; @@ -5183,12 +5240,8 @@ void bot_subcommand_bot_create(Client *c, const Seperator *sep) window_text.append("Genders:"); message_separator = " "; for (int i = 0; i <= 1; ++i) { - if (((1 << i) & RuleI(Bots, AllowedGenders)) == 0) - continue; - - window_text.append(const_cast(message_separator)); + window_text.append(message_separator); window_text.append(StringFormat(gender_substrs[i].c_str(), i)); - message_separator = ", "; } @@ -5802,9 +5855,9 @@ void bot_subcommand_bot_list(Client *c, const Seperator *sep) c->Message(Chat::White, "[%s] is a level %u %s %s %s who is owned by %s", ((c->CharacterID() == bots_iter.Owner_ID) && (!botCheckNotOnline) ? (EQ::SayLinkEngine::GenerateQuestSaylink(botspawn_saylink, false, bots_iter.Name).c_str()) : (bots_iter.Name)), bots_iter.Level, - Bot::RaceIdToString(bots_iter.Race).c_str(), + GetRaceIDName(bots_iter.Race), ((bots_iter.Gender == FEMALE) ? ("Female") : ((bots_iter.Gender == MALE) ? ("Male") : ("Neuter"))), - Bot::ClassIdToString(bots_iter.Class).c_str(), + GetClassIDName(bots_iter.Class), bots_iter.Owner ); if (c->CharacterID() == bots_iter.Owner_ID) { ++bots_owned; } @@ -5977,7 +6030,7 @@ void bot_subcommand_bot_report(Client *c, const Seperator *sep) if (!bot_iter) continue; - std::string report_msg = StringFormat("%s %s reports", Bot::ClassIdToString(bot_iter->GetClass()).c_str(), bot_iter->GetCleanName()); + std::string report_msg = StringFormat("%s %s reports", GetClassIDName(bot_iter->GetClass()), bot_iter->GetCleanName()); report_msg.append(StringFormat(": %3.1f%% health", bot_iter->GetHPRatio())); if (!IsNonSpellFighterClass(bot_iter->GetClass())) report_msg.append(StringFormat(": %3.1f%% mana", bot_iter->GetManaRatio())); @@ -8672,33 +8725,25 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas return bot_id; } - auto class_bit = GetPlayerClassBit(bot_class); - if ((class_bit & RuleI(Bots, AllowedClasses)) == PLAYER_CLASS_UNKNOWN_BIT) { - bot_owner->Message(m_fail, "Class '%s' bots are not allowed on this server", GetPlayerClassName(bot_class)); - return bot_id; - } - - auto race_bit = GetPlayerRaceBit(bot_race); - if ((race_bit & RuleI(Bots, AllowedRaces)) == PLAYER_RACE_UNKNOWN_BIT) { - bot_owner->Message(m_fail, "Race '%s' bots are not allowed on this server", GetPlayerRaceName(bot_class)); - return bot_id; - } - if (!Bot::IsValidRaceClassCombo(bot_race, bot_class)) { - bot_owner->Message(m_fail, "'%s'(%u):'%s'(%u) is an invalid race-class combination", - Bot::RaceIdToString(bot_race).c_str(), bot_race, Bot::ClassIdToString(bot_class).c_str(), bot_class); + const char* bot_race_name = GetRaceIDName(bot_race); + const char* bot_class_name = GetClassIDName(bot_class); + std::string view_saylink = EQ::SayLinkEngine::GenerateQuestSaylink(fmt::format("^viewcombos {}", bot_race), false, "view"); + bot_owner->Message( + m_fail, + fmt::format( + "{} {} is an invalid race-class combination, would you like to {} proper combinations for {}?", + bot_race_name, + bot_class_name, + view_saylink, + bot_race_name + ).c_str() + ); return bot_id; } - if (bot_gender > FEMALE || (((1 << bot_gender) & RuleI(Bots, AllowedGenders)) == 0)) { - if (RuleI(Bots, AllowedGenders) == 3) - bot_owner->Message(m_fail, "gender: %u(M), %u(F)", MALE, FEMALE); - else if (RuleI(Bots, AllowedGenders) == 2) - bot_owner->Message(m_fail, "gender: %u(F)", FEMALE); - else if (RuleI(Bots, AllowedGenders) == 1) - bot_owner->Message(m_fail, "gender: %u(M)", MALE); - else - bot_owner->Message(m_fail, "gender: ERROR - No valid genders exist"); + if (bot_gender > FEMALE) { + bot_owner->Message(m_fail, "gender: %u (M), %u (F)", MALE, FEMALE); return bot_id; } @@ -8710,7 +8755,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas return bot_id; } if (bot_count >= max_bot_count) { - bot_owner->Message(m_fail, "You have reached the maximum limit of %i bots", max_bot_count); + bot_owner->Message(m_fail, "You have reached the maximum limit of %i bots.", max_bot_count); return bot_id; } diff --git a/zone/bot_command.h b/zone/bot_command.h index 84a56f239..34ced3872 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -593,6 +593,7 @@ void bot_command_summon_corpse(Client *c, const Seperator *sep); void bot_command_suspend(Client *c, const Seperator *sep); void bot_command_taunt(Client *c, const Seperator *sep); void bot_command_track(Client *c, const Seperator *sep); +void bot_command_view_combos(Client *c, const Seperator *sep); void bot_command_water_breathing(Client *c, const Seperator *sep); diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index c419fd355..b9d0bd6cd 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -2952,6 +2952,20 @@ uint8 BotDatabase::GetSpellCastingChance(uint8 spell_type_index, uint8 class_ind return Bot::spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index]; } +uint16 BotDatabase::GetRaceClassBitmask(uint16 bot_race) +{ + std::string query = fmt::format( + "SELECT `classes` FROM `bot_create_combinations` WHERE `race` = {}", + bot_race + ); + auto results = database.QueryDatabase(query); + uint16 classes = 0; + if (results.RowCount() == 1) { + auto row = results.begin(); + classes = atoi(row[0]); + } + return classes; +} /* fail::Bot functions */ const char* BotDatabase::fail::QueryNameAvailablity() { return "Failed to query name availability"; } diff --git a/zone/bot_database.h b/zone/bot_database.h index 57cf185ce..9ebb2b0c5 100644 --- a/zone/bot_database.h +++ b/zone/bot_database.h @@ -186,6 +186,7 @@ public: /* Bot miscellaneous functions */ uint8 GetSpellCastingChance(uint8 spell_type_index, uint8 class_index, uint8 stance_index, uint8 conditional_index); + uint16 GetRaceClassBitmask(uint16 bot_race); class fail { public: From 5b3ab59b7c5d1cd14ad764b83d831f12f263edc0 Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Fri, 11 Jun 2021 14:31:25 -0400 Subject: [PATCH 062/624] [Expeditions] Avoid expedition leader change if only member (#1372) This fixes an edge case where a player could be made leader when added to an expedition that only had a single member previously. If a leader in a two-member expedition quit (forcing a leader change) and the new leader went offline while throttled, a leader change flag would be set until a non-leader was available. The first added member would then be made the new leader. This could also potentially occur on world startup due to the initial throttle timer state but member statuses aren't processed there yet --- world/expedition.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/world/expedition.cpp b/world/expedition.cpp index 4b29e4f0a..4cc82ca03 100644 --- a/world/expedition.cpp +++ b/world/expedition.cpp @@ -182,7 +182,7 @@ void Expedition::UpdateMemberStatus(uint32_t character_id, DynamicZoneMemberStat } // any member status update will trigger a leader fix if leader was offline - if (m_leader.status == DynamicZoneMemberStatus::Offline) + if (m_leader.status == DynamicZoneMemberStatus::Offline && GetDynamicZone().GetMemberCount() > 1) { ChooseNewLeader(); } From b61cc85b5f205c599373175c2666ed0116252b98 Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Fri, 11 Jun 2021 14:31:50 -0400 Subject: [PATCH 063/624] [Expeditions] Move member compass updates to dz (#1371) --- zone/dynamic_zone.cpp | 13 +++++++++++-- zone/dynamic_zone.h | 5 +---- zone/expedition.cpp | 13 ------------- zone/expedition.h | 1 - 4 files changed, 12 insertions(+), 20 deletions(-) diff --git a/zone/dynamic_zone.cpp b/zone/dynamic_zone.cpp index 0b19e5ade..177d9c04c 100644 --- a/zone/dynamic_zone.cpp +++ b/zone/dynamic_zone.cpp @@ -209,8 +209,17 @@ void DynamicZone::HandleWorldMessage(ServerPacket* pack) void DynamicZone::ProcessCompassChange(const DynamicZoneLocation& location) { DynamicZoneBase::ProcessCompassChange(location); - if (m_on_compass_change) + SendCompassUpdateToZoneMembers(); +} + +void DynamicZone::SendCompassUpdateToZoneMembers() +{ + for (const auto& member : m_members) { - m_on_compass_change(); + Client* member_client = entity_list.GetClientByCharID(member.id); + if (member_client) + { + member_client->SendDzCompassUpdate(); + } } } diff --git a/zone/dynamic_zone.h b/zone/dynamic_zone.h index 3b421330a..a56947749 100644 --- a/zone/dynamic_zone.h +++ b/zone/dynamic_zone.h @@ -23,7 +23,6 @@ #include "../common/dynamic_zone_base.h" #include -#include #include #include #include @@ -47,7 +46,6 @@ public: void SetSecondsRemaining(uint32_t seconds_remaining) override; bool IsCurrentZoneDzInstance() const; - void RegisterOnCompassChange(const std::function& on_change) { m_on_compass_change = on_change; } void SetUpdatedDuration(uint32_t seconds); protected: @@ -61,8 +59,7 @@ protected: private: static void StartAllClientRemovalTimers(); - - std::function m_on_compass_change; + void SendCompassUpdateToZoneMembers(); }; #endif diff --git a/zone/expedition.cpp b/zone/expedition.cpp index 6b0465162..728e036d0 100644 --- a/zone/expedition.cpp +++ b/zone/expedition.cpp @@ -62,7 +62,6 @@ void Expedition::SetDynamicZone(DynamicZone&& dz) dz.SetLeader(GetLeader()); m_dynamiczone = std::move(dz); - m_dynamiczone.RegisterOnCompassChange([this]() { SendCompassUpdateToZoneMembers(); }); } Expedition* Expedition::TryCreate( @@ -1840,18 +1839,6 @@ void Expedition::HandleWorldMessage(ServerPacket* pack) } } -void Expedition::SendCompassUpdateToZoneMembers() -{ - for (const auto& member : GetDynamicZone().GetMembers()) - { - Client* member_client = entity_list.GetClientByCharID(member.id); - if (member_client) - { - member_client->SendDzCompassUpdate(); - } - } -} - bool Expedition::CanClientLootCorpse(Client* client, uint32_t npc_type_id, uint32_t spawn_id) { if (client && m_dynamiczone.IsCurrentZoneDzInstance()) diff --git a/zone/expedition.h b/zone/expedition.h index 56ffffc3c..106035479 100644 --- a/zone/expedition.h +++ b/zone/expedition.h @@ -150,7 +150,6 @@ private: void SendMemberStatusToZoneMembers(uint32_t update_character_id, DynamicZoneMemberStatus status); void SendMembersExpireWarning(uint32_t minutes); void SendUpdatesToZoneMembers(bool clear = false, bool message_on_clear = true); - void SendCompassUpdateToZoneMembers(); void SendWorldExpeditionUpdate(uint16_t server_opcode); void SendWorldAddPlayerInvite(const std::string& inviter_name, const std::string& swap_remove_name, const std::string& add_name, bool pending = false); From ebdb8e5d90a147bf5addd84bf0002af95acb7c18 Mon Sep 17 00:00:00 2001 From: regneq Date: Fri, 11 Jun 2021 11:32:35 -0700 Subject: [PATCH 064/624] [Time] strict spawn_events now take into account EQ minute. (#1370) * strict spawn_events now take into account EQ minute. This should fixed the eqtime spawn condition from falling behind. * change a log to logspawns and add a comment in ExecEvent function. * moved the comment to the note paramenter in the rule for last commit. --- common/ruletypes.h | 1 + zone/spawn2.cpp | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 2e8529d70..da2078434 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -269,6 +269,7 @@ RULE_BOOL(Zone, EnableZoneControllerGlobals, false, "Enables the ability to use RULE_INT(Zone, GlobalLootMultiplier, 1, "Sets Global Loot drop multiplier for database based drops, useful for double, triple loot etc") RULE_BOOL(Zone, KillProcessOnDynamicShutdown, true, "When process has booted a zone and has hit its zone shut down timer, it will hard kill the process to free memory back to the OS") RULE_INT(Zone, SecondsBeforeIdle, 60, "Seconds before IDLE_WHEN_EMPTY define kicks in") +RULE_INT(Zone, SpawnEventMin, 3, "When strict is set in spawn_events, specifies the max EQ minutes into the trigger hour a spawn_event will fire. Going below 3 may cause the spawn_event to not fire.") RULE_CATEGORY_END() RULE_CATEGORY(Map) diff --git a/zone/spawn2.cpp b/zone/spawn2.cpp index b6689d715..f15ff12c3 100644 --- a/zone/spawn2.cpp +++ b/zone/spawn2.cpp @@ -832,8 +832,11 @@ void SpawnConditionManager::Process() { if(EQTime::IsTimeBefore(&tod, &cevent.next)) { //this event has been triggered. //execute the event - if(!cevent.strict || (cevent.strict && cevent.next.hour == tod.hour && cevent.next.day == tod.day && cevent.next.month == tod.month && cevent.next.year == tod.year)) + uint8 min = cevent.next.minute + RuleI(Zone, SpawnEventMin); + if(!cevent.strict || (cevent.strict && tod.minute < min && cevent.next.hour == tod.hour && cevent.next.day == tod.day && cevent.next.month == tod.month && cevent.next.year == tod.year)) ExecEvent(cevent, true); + else + LogSpawns("Event {}: Is strict, ExecEvent is skipped.", cevent.id); //add the period of the event to the trigger time EQTime::AddMinutes(cevent.period, &cevent.next); @@ -858,6 +861,7 @@ void SpawnConditionManager::ExecEvent(SpawnEvent &event, bool send_update) { std::map::iterator condi; condi = spawn_conditions.find(event.condition_id); if(condi == spawn_conditions.end()) { + //If we're here, strict has already been checked. Check again in case hour has changed. LogSpawns("Event [{}]: Unable to find condition [{}] to execute on", event.id, event.condition_id); return; //unable to find the spawn condition to operate on } From d54cd08560e54aaffa3cacab0ceab5b1dec7481c Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 11 Jun 2021 14:41:08 -0400 Subject: [PATCH 065/624] [Spells] Adds a rule to allow right-click memorize from spell scrolls. (#1377) * [Spells] Adds a rule to allow right-click memorize from spell scrolls. * Typo. --- common/ruletypes.h | 1 + zone/client.cpp | 2 +- zone/client.h | 1 + zone/client_packet.cpp | 7 +++- zone/effects.cpp | 81 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 2 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index da2078434..0bec71b80 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -373,6 +373,7 @@ RULE_BOOL(Spells, NPCSpellPush, false, "Enable spell push on NPCs") RULE_BOOL(Spells, July242002PetResists, true, "Enable Pets using PCs resist change from July 24 2002") RULE_INT(Spells, AOEMaxTargets, 0, "Max number of targets a Targeted AOE spell can cast on. Set to 0 for no limit.") RULE_BOOL(Spells, AllowDoubleInvis, false, "Allows you to cast invisibility spells on a player that is already invisible") +RULE_BOOL(Spells, AllowSpellMemorizeFromItem, false, "Allows players to memorize spells by right-clicking spell scrolls") RULE_CATEGORY_END() RULE_CATEGORY(Combat) diff --git a/zone/client.cpp b/zone/client.cpp index 10c58a265..614917da9 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -10102,7 +10102,7 @@ std::vector Client::GetScribedSpells() { if (IsValidSpell(m_pp.spell_book[index])) { scribed_spells.push_back(m_pp.spell_book[index]); } - } + } return scribed_spells; } diff --git a/zone/client.h b/zone/client.h index 2c8dc2e2b..c48ea2237 100644 --- a/zone/client.h +++ b/zone/client.h @@ -982,6 +982,7 @@ public: void ResetTrade(); void DropInst(const EQ::ItemInstance* inst); bool TrainDiscipline(uint32 itemid); + bool MemorizeSpellFromItem(uint32 item_id); void TrainDiscBySpellID(int32 spell_id); uint32 GetDisciplineTimer(uint32 timer_id); int GetDiscSlotBySpellID(int32 spellid); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 6d277b140..ea7ad932a 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -8876,7 +8876,12 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) } else if (item->ItemType == EQ::item::ItemTypeSpell) { - return; + if (RuleB(Spells, AllowSpellMemorizeFromItem)) { + DeleteItemInInventory(slot_id, 1, true); + MemorizeSpellFromItem(item->ID); + } else { + return; + } } else if ((item->Click.Type == EQ::item::ItemEffectClick) || (item->Click.Type == EQ::item::ItemEffectExpendable) || (item->Click.Type == EQ::item::ItemEffectEquipClick) || (item->Click.Type == EQ::item::ItemEffectClick2)) { diff --git a/zone/effects.cpp b/zone/effects.cpp index da3608416..b68887ad9 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -509,6 +509,87 @@ bool Client::TrainDiscipline(uint32 itemid) { return(false); } +bool Client::MemorizeSpellFromItem(uint32 item_id) { + const EQ::ItemData *item = database.GetItem(item_id); + if(item == nullptr) { + Message(Chat::Red, "Unable to find the scroll!"); + LogError("Unable to find scroll id [{}]\n", (unsigned long)item_id); + return false; + } + + if (!item->IsClassCommon() || item->ItemType != EQ::item::ItemTypeSpell) { + Message(Chat::Red, "Invalid item type, you cannot learn from this item."); + SummonItem(item_id); + return false; + } + + if(!( + item->Name[0] == 'S' && + item->Name[1] == 'p' && + item->Name[2] == 'e' && + item->Name[3] == 'l' && + item->Name[4] == 'l' && + item->Name[5] == ':' && + item->Name[6] == ' ' + )) { + Message(Chat::Red, "This item is not a scroll."); + SummonItem(item_id); + return false; + } + int player_class = GetClass(); + uint32 cbit = 1 << (player_class - 1); + if(!(item->Classes & cbit)) { + Message(Chat::Red, "Your class cannot learn from this scroll."); + SummonItem(item_id); + return false; + } + + uint32 spell_id = item->Scroll.Effect; + if(!IsValidSpell(spell_id)) { + Message(Chat::Red, "This scroll contains invalid knowledge."); + return false; + } + + const SPDat_Spell_Struct &spell = spells[spell_id]; + uint8 level_to_use = spell.classes[player_class - 1]; + if(level_to_use == 255) { + Message(Chat::Red, "Your class cannot learn from this scroll."); + SummonItem(item_id); + return false; + } + + if(level_to_use > GetLevel()) { + Message(Chat::Red, "You must be at least level %d to learn this spell.", level_to_use); + SummonItem(item_id); + return false; + } + + for(int index = 0; index < EQ::spells::SPELLBOOK_SIZE; index++) { + if (!HasSpellScribed(spell_id)) { + auto next_slot = GetNextAvailableSpellBookSlot(); + if (next_slot != -1) { + ScribeSpell(spell_id, next_slot); + return true; + } else { + Message( + Chat::Red, + "Unable to scribe spell %s (%i) to spellbook: no more spell book slots available.", + ((spell_id >= 0 && spell_id < SPDAT_RECORDS) ? spells[spell_id].name : "Out-of-range"), + spell_id + ); + SummonItem(item_id); + return false; + } + } else { + Message(Chat::Red, "You already know this spell."); + SummonItem(item_id); + return false; + } + } + Message(Chat::Red, "You have learned too many spells and can learn no more."); + return false; +} + void Client::TrainDiscBySpellID(int32 spell_id) { int i; From d9d6a64941bd4b30bb144732274fd6aa2d4c99d0 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 11 Jun 2021 14:46:30 -0400 Subject: [PATCH 066/624] [Bots] Add Bot scripting capabilities to the source. (#1378) - This will allow server operators to interact with bots within a script in Perl or Lua. --- zone/embparser.cpp | 8 ++++ zone/embperl.cpp | 8 ++++ zone/entity.cpp | 10 +++++ zone/entity.h | 5 +++ zone/lua_bot.cpp | 13 +++++++ zone/lua_bot.h | 30 ++++++++++++++ zone/lua_entity.cpp | 15 +++++++ zone/lua_entity.h | 6 +++ zone/lua_entity_list.cpp | 45 +++++++++++++++++++++ zone/lua_entity_list.h | 15 +++++++ zone/lua_parser.cpp | 10 +++++ zone/perl_bot.cpp | 84 ++++++++++++++++++++++++++++++++++++++++ zone/perl_entity.cpp | 63 ++++++++++++++++++++++++++++++ zone/perl_mob.cpp | 26 +++++++++++++ 14 files changed, 338 insertions(+) create mode 100644 zone/lua_bot.cpp create mode 100644 zone/lua_bot.h create mode 100644 zone/perl_bot.cpp diff --git a/zone/embparser.cpp b/zone/embparser.cpp index c19ac6671..db37a58e7 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -977,6 +977,14 @@ void PerlembParser::MapFunctions() "package Expedition;" "&boot_Expedition;" +#ifdef BOTS + "package Bot;" + "our @ISA = qw(NPC);" // Bot inherits NPC + "&boot_Mob;" // load our Mob XS + "&boot_NPC;" // load our NPC XS + "&boot_Bot;" // load our Bot XS +#endif + #endif "package main;" "}" diff --git a/zone/embperl.cpp b/zone/embperl.cpp index b05a429a1..1300df0b2 100644 --- a/zone/embperl.cpp +++ b/zone/embperl.cpp @@ -39,6 +39,9 @@ EXTERN_C XS(boot_Object); EXTERN_C XS(boot_Doors); EXTERN_C XS(boot_PerlPacket); EXTERN_C XS(boot_Expedition); +#ifdef BOTS +EXTERN_C XS(boot_Bot); +#endif #endif #endif @@ -91,6 +94,11 @@ EXTERN_C void xs_init(pTHX) newXS(strcpy(buf, "Object::boot_Object"), boot_Object, file); newXS(strcpy(buf, "Doors::boot_Doors"), boot_Doors, file); newXS(strcpy(buf, "Expedition::boot_Expedition"), boot_Expedition, file); +#ifdef BOTS + newXS(strcpy(buf, "Bot::boot_Mob"), boot_Mob, file); + newXS(strcpy(buf, "Bot::boot_NPC"), boot_NPC, file); + newXS(strcpy(buf, "Bot::boot_Bot"), boot_Bot, file); +#endif ; #endif #endif diff --git a/zone/entity.cpp b/zone/entity.cpp index 5cc12dc72..81087da21 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -4769,6 +4769,16 @@ void EntityList::GetClientList(std::list &c_list) } } +#ifdef BOTS +void EntityList::GetBotList(std::list &b_list) +{ + b_list.clear(); + for (auto bot_iterator : bot_list) { + b_list.push_back(bot_iterator); + } +} +#endif + void EntityList::GetCorpseList(std::list &c_list) { c_list.clear(); diff --git a/zone/entity.h b/zone/entity.h index b4855b42e..5eec26f35 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -514,6 +514,9 @@ public: inline const std::unordered_map &GetNPCList() { return npc_list; } inline const std::unordered_map &GetMercList() { return merc_list; } inline const std::unordered_map &GetClientList() { return client_list; } +#ifdef BOTS + inline const std::list &GetBotList() { return bot_list; } +#endif inline const std::unordered_map &GetCorpseList() { return corpse_list; } inline const std::unordered_map &GetObjectList() { return object_list; } inline const std::unordered_map &GetDoorsList() { return door_list; } @@ -593,6 +596,8 @@ private: void ShowSpawnWindow(Client* client, int Distance, bool NamedOnly); // TODO: Implement ShowSpawnWindow in the bot class but it needs entity list stuff void ScanCloseClientMobs(std::unordered_map& close_mobs, Mob* scanning_mob); + + void GetBotList(std::list &b_list); private: std::list bot_list; #endif diff --git a/zone/lua_bot.cpp b/zone/lua_bot.cpp new file mode 100644 index 000000000..78bb7049c --- /dev/null +++ b/zone/lua_bot.cpp @@ -0,0 +1,13 @@ +#ifdef LUA_EQEMU + +#include "lua.hpp" +#include + +#include "bot.h" +#include "lua_bot.h" + +luabind::scope lua_register_bot() { + return luabind::class_("Bot").def(luabind::constructor<>()); +} + +#endif diff --git a/zone/lua_bot.h b/zone/lua_bot.h new file mode 100644 index 000000000..3e7d17631 --- /dev/null +++ b/zone/lua_bot.h @@ -0,0 +1,30 @@ +#ifndef EQEMU_LUA_BOT_H +#define EQEMU_LUA_BOT_H +#ifdef LUA_EQEMU + +#include "lua_mob.h" + +class Bot; +class Lua_Bot; + +namespace luabind { + struct scope; +} + +luabind::scope lua_register_bot(); + +class Lua_Bot : public Lua_Mob +{ + typedef Bot NativeType; +public: + Lua_Bot() { SetLuaPtrData(nullptr); } + Lua_Bot(Bot *d) { SetLuaPtrData(reinterpret_cast(d)); } + virtual ~Lua_Bot() { } + + operator Bot*() { + return reinterpret_cast(GetLuaPtrData()); + } +}; + +#endif +#endif diff --git a/zone/lua_entity.cpp b/zone/lua_entity.cpp index 725e78f5e..11ac06199 100644 --- a/zone/lua_entity.cpp +++ b/zone/lua_entity.cpp @@ -12,6 +12,10 @@ #include "lua_object.h" #include "lua_door.h" +#ifdef BOTS +#include "lua_bot.h" +#endif + bool Lua_Entity::IsClient() { Lua_Safe_Call_Bool(); return self->IsClient(); @@ -118,6 +122,14 @@ Lua_Door Lua_Entity::CastToDoor() { return Lua_Door(m); } +#ifdef BOTS +Lua_Bot Lua_Entity::CastToBot() { + void *d = GetLuaPtrData(); + Bot *b = reinterpret_cast(d); + return Lua_Bot(b); +} +#endif + luabind::scope lua_register_entity() { return luabind::class_("Entity") .def(luabind::constructor<>()) @@ -138,6 +150,9 @@ luabind::scope lua_register_entity() { .def("IsBot", &Lua_Entity::IsBot) .def("GetID", &Lua_Entity::GetID) .def("CastToClient", &Lua_Entity::CastToClient) +#ifdef BOTS + .def("CastToBot", &Lua_Entity::CastToBot) +#endif .def("CastToNPC", &Lua_Entity::CastToNPC) .def("CastToMob", &Lua_Entity::CastToMob) .def("CastToCorpse", &Lua_Entity::CastToCorpse) diff --git a/zone/lua_entity.h b/zone/lua_entity.h index b45208581..4954d13ff 100644 --- a/zone/lua_entity.h +++ b/zone/lua_entity.h @@ -6,6 +6,9 @@ class Entity; class Lua_Client; +#ifdef BOTS +class Lua_Bot; +#endif class Lua_NPC; class Lua_Mob; struct Lua_HateList; @@ -54,6 +57,9 @@ public: Lua_Corpse CastToCorpse(); Lua_Object CastToObject(); Lua_Door CastToDoor(); +#ifdef BOTS + Lua_Bot CastToBot(); +#endif }; #endif diff --git a/zone/lua_entity_list.cpp b/zone/lua_entity_list.cpp index e4f2b207d..9d6a812ed 100644 --- a/zone/lua_entity_list.cpp +++ b/zone/lua_entity_list.cpp @@ -17,6 +17,10 @@ #include "lua_raid.h" #include "lua_spawn.h" +#ifdef BOTS +#include "lua_bot.h" +#endif + struct Lua_Mob_List { std::vector entries; }; @@ -29,6 +33,12 @@ struct Lua_Client_List { std::vector entries; }; +#ifdef BOTS +struct Lua_Bot_List { + std::vector entries; +}; +#endif + struct Lua_Corpse_List { std::vector entries; }; @@ -345,6 +355,29 @@ Lua_Client_List Lua_EntityList::GetClientList() { return ret; } +#ifdef BOTS +Lua_Bot Lua_EntityList::GetBotByID(uint32 bot_id) { + Lua_Safe_Call_Class(Lua_Bot); + return Lua_Bot(self->GetBotByBotID(bot_id)); +} + +Lua_Bot Lua_EntityList::GetBotByName(std::string bot_name) { + Lua_Safe_Call_Class(Lua_Bot); + return Lua_Bot(self->GetBotByBotName(bot_name)); +} + +Lua_Bot_List Lua_EntityList::GetBotList() { + Lua_Safe_Call_Class(Lua_Bot_List); + Lua_Bot_List ret; + auto &bot_list = self->GetBotList(); + for (auto bot_iterator : bot_list) { + ret.entries.push_back(Lua_Bot(bot_iterator)); + } + + return ret; +} +#endif + Lua_Client_List Lua_EntityList::GetShuffledClientList() { Lua_Safe_Call_Class(Lua_Client_List); Lua_Client_List ret; @@ -502,6 +535,11 @@ luabind::scope lua_register_entity_list() { .def("GetRandomClient", (Lua_Client(Lua_EntityList::*)(float, float, float, float, Lua_Client))&Lua_EntityList::GetRandomClient) .def("GetMobList", (Lua_Mob_List(Lua_EntityList::*)(void))&Lua_EntityList::GetMobList) .def("GetClientList", (Lua_Client_List(Lua_EntityList::*)(void))&Lua_EntityList::GetClientList) +#ifdef BOTS + .def("GetBotByID", (Lua_Bot(Lua_EntityList::*)(uint32))&Lua_EntityList::GetBotByID) + .def("GetBotByName", (Lua_Bot(Lua_EntityList::*)(std::string))&Lua_EntityList::GetBotByName) + .def("GetBotList", (Lua_Bot_List(Lua_EntityList::*)(void))&Lua_EntityList::GetBotList) +#endif .def("GetShuffledClientList", (Lua_Client_List(Lua_EntityList::*)(void))&Lua_EntityList::GetShuffledClientList) .def("GetNPCList", (Lua_NPC_List(Lua_EntityList::*)(void))&Lua_EntityList::GetNPCList) .def("GetCorpseList", (Lua_Corpse_List(Lua_EntityList::*)(void))&Lua_EntityList::GetCorpseList) @@ -522,6 +560,13 @@ luabind::scope lua_register_client_list() { .def_readwrite("entries", &Lua_Client_List::entries, luabind::return_stl_iterator); } +#ifdef BOTS +luabind::scope lua_register_bot_list() { + return luabind::class_("BotList") + .def_readwrite("entries", &Lua_Bot_List::entries, luabind::return_stl_iterator); +} +#endif + luabind::scope lua_register_npc_list() { return luabind::class_("NPCList") .def_readwrite("entries", &Lua_NPC_List::entries, luabind::return_stl_iterator); diff --git a/zone/lua_entity_list.h b/zone/lua_entity_list.h index 04030e58e..4fa7b97b9 100644 --- a/zone/lua_entity_list.h +++ b/zone/lua_entity_list.h @@ -7,6 +7,9 @@ class EntityList; class Lua_Mob; class Lua_Client; +#ifdef BOTS +class Lua_Bot; +#endif class Lua_NPC; class Lua_Door; class Lua_Corpse; @@ -16,6 +19,9 @@ class Lua_Raid; class Lua_Spawn; struct Lua_Mob_List; struct Lua_Client_List; +#ifdef BOTS +struct Lua_Bot_List; +#endif struct Lua_NPC_List; struct Lua_Corpse_List; struct Lua_Object_List; @@ -35,6 +41,10 @@ luabind::scope lua_register_object_list(); luabind::scope lua_register_door_list(); luabind::scope lua_register_spawn_list(); +#ifdef BOTS +luabind::scope lua_register_bot_list(); +#endif + class Lua_EntityList : public Lua_Ptr { typedef EntityList NativeType; @@ -110,6 +120,11 @@ public: Lua_Spawn_List GetSpawnList(); void SignalAllClients(int signal); void ChannelMessage(Lua_Mob from, int channel_num, int language, const char *message); +#ifdef BOTS + Lua_Bot GetBotByID(uint32 bot_id); + Lua_Bot GetBotByName(std::string bot_name); + Lua_Bot_List GetBotList(); +#endif }; #endif diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index 59b10d4bf..1fc0fc47a 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -40,6 +40,10 @@ #include "lua_encounter.h" #include "lua_stat_bonuses.h" +#ifdef BOTS +#include "lua_bot.h" +#endif + const char *LuaEvents[_LargestEventID] = { "event_say", "event_trade", @@ -1090,6 +1094,9 @@ void LuaParser::MapFunctions(lua_State *L) { lua_register_special_abilities(), lua_register_npc(), lua_register_client(), +#ifdef BOTS + lua_register_bot(), +#endif lua_register_inventory(), lua_register_inventory_where(), lua_register_iteminst(), @@ -1101,6 +1108,9 @@ void LuaParser::MapFunctions(lua_State *L) { lua_register_entity_list(), lua_register_mob_list(), lua_register_client_list(), +#ifdef BOTS + lua_register_bot_list(), +#endif lua_register_npc_list(), lua_register_corpse_list(), lua_register_object_list(), diff --git a/zone/perl_bot.cpp b/zone/perl_bot.cpp new file mode 100644 index 000000000..d78c82d52 --- /dev/null +++ b/zone/perl_bot.cpp @@ -0,0 +1,84 @@ +/* EQEMu: Everquest Server Emulator +Copyright (C) 2001-2004 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 +*/ + +#include "../common/features.h" +#ifdef EMBPERL_XS_CLASSES +#include "../common/global_define.h" +#include "embperl.h" + +#ifdef seed +#undef seed +#endif + +#include "bot.h" + +#ifdef THIS +#undef THIS +#endif + +#define VALIDATE_THIS_IS_BOT \ + do { \ + if (sv_derived_from(ST(0), "Bot")) { \ + IV tmp = SvIV((SV*)SvRV(ST(0))); \ + THIS = INT2PTR(Bot*, tmp); \ + } else { \ + Perl_croak(aTHX_ "THIS is not of type Bot"); \ + } \ + if (THIS == nullptr) { \ + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); \ + } \ + } while (0); + +XS(XS_Bot_GetOwner); +XS(XS_Bot_GetOwner) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Bot::GetOwner(THIS)"); // @categories Script Utility, Bot + { + Bot* THIS; + Mob* bot_owner; + VALIDATE_THIS_IS_BOT; + bot_owner = THIS->GetBotOwner(); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Mob", (void*)bot_owner); + } + XSRETURN(1); +} + +#ifdef __cplusplus +extern "C" +#endif + +XS(boot_Bot); +XS(boot_Bot) +{ + dXSARGS; + char file[256]; + strncpy(file, __FILE__, 256); + file[255] = 0; + + if (items != 1) + fprintf(stderr, "boot_Bot does not take any arguments."); + + char buf[128]; + XS_VERSION_BOOTCHECK; + + newXSproto(strcpy(buf, "GetOwner"), XS_Bot_GetOwner, file, "$"); + + XSRETURN_YES; +} + +#endif //EMBPERL_XS_CLASSES \ No newline at end of file diff --git a/zone/perl_entity.cpp b/zone/perl_entity.cpp index c2b3d6a32..ec1e855cd 100644 --- a/zone/perl_entity.cpp +++ b/zone/perl_entity.cpp @@ -1310,6 +1310,64 @@ XS(XS_EntityList_GetClientList) { XSRETURN(num_clients); } +#ifdef BOTS +XS(XS_EntityList_GetBotByID); +XS(XS_EntityList_GetBotByID) { + dXSARGS; + int bot_count = 0; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetBotByID(THIS, uint32 bot_id)"); // @categories Script Utility, Bot + { + EntityList* THIS; + Bot* RETVAL; + uint32 bot_id = (uint32) SvUV(ST(1)); + VALIDATE_THIS_IS_ENTITY; + RETVAL = THIS->GetBotByBotID(bot_id); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Bot", (void *) RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetBotByName); +XS(XS_EntityList_GetBotByName) { + dXSARGS; + int bot_count = 0; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetBotByName(THIS, string bot_name)"); // @categories Script Utility, Bot + { + EntityList* THIS; + Bot* RETVAL; + std::string bot_name = (std::string) SvPV_nolen(ST(1)); + VALIDATE_THIS_IS_ENTITY; + RETVAL = THIS->GetBotByBotName(bot_name); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Bot", (void *) RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetBotList); +XS(XS_EntityList_GetBotList) { + dXSARGS; + int bot_count = 0; + if (items != 1) + Perl_croak(aTHX_ "Usage: EntityList::GetBotList(THIS)"); // @categories Script Utility, Bot + { + EntityList *THIS; + VALIDATE_THIS_IS_ENTITY; + auto current_bot_list = THIS->GetBotList(); + for (auto bot_iterator : current_bot_list) { + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Bot", (void *)bot_iterator); + XPUSHs(ST(0)); + bot_count++; + } + } + XSRETURN(bot_count); +} +#endif + XS(XS_EntityList_GetNPCList); /* prototype to pass -Wmissing-prototypes */ XS(XS_EntityList_GetNPCList) { dXSARGS; @@ -1517,6 +1575,11 @@ XS(boot_EntityList) { newXSproto(strcpy(buf, "GetObjectList"), XS_EntityList_GetObjectList, file, "$"); newXSproto(strcpy(buf, "GetDoorsList"), XS_EntityList_GetDoorsList, file, "$"); newXSproto(strcpy(buf, "SignalAllClients"), XS_EntityList_SignalAllClients, file, "$$"); +#ifdef BOTS + newXSproto(strcpy(buf, "GetBotByID"), XS_EntityList_GetBotByID, file, "$$"); + newXSproto(strcpy(buf, "GetBotByName"), XS_EntityList_GetBotByName, file, "$$"); + newXSproto(strcpy(buf, "GetBotList"), XS_EntityList_GetBotList, file, "$"); +#endif XSRETURN_YES; } diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index fb4606d27..28830ef2b 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -42,6 +42,10 @@ typedef const char Const_char; #include "client.h" #include "../common/spdat.h" +#ifdef BOTS +#include "bot.h" +#endif + #ifdef THIS /* this macro seems to leak out on some systems */ #undef THIS #endif @@ -6253,6 +6257,25 @@ XS(XS_Mob_GetLastName) { XSRETURN(1); } +#ifdef BOTS +XS(XS_Mob_CastToBot); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_CastToBot) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::CastToBot(THIS)"); + { + Mob* THIS; + Bot* RETVAL; + VALIDATE_THIS_IS_MOB; + RETVAL = THIS->CastToBot(); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Bot", (void*)RETVAL); + } + XSRETURN(1); +} +#endif + #ifdef __cplusplus extern "C" #endif @@ -6602,6 +6625,9 @@ XS(boot_Mob) { newXSproto(strcpy(buf, "GetHateClosest"), XS_Mob_GetHateClosest, file, "$"); newXSproto(strcpy(buf, "GetHateListByDistance"), XS_Mob_GetHateListByDistance, file, "$;$"); newXSproto(strcpy(buf, "GetLastName"), XS_Mob_GetLastName, file, "$"); +#ifdef BOTS + newXSproto(strcpy(buf, "CastToBot"), XS_Mob_CastToBot, file, "$"); +#endif XSRETURN_YES; } From f0bf3826bde56438ab086418fd4be0b3836c0855 Mon Sep 17 00:00:00 2001 From: Dencelle Date: Fri, 11 Jun 2021 15:55:23 -0500 Subject: [PATCH 067/624] [Bug Fix] NPC not breaking charm correctly (#1363) * [Bug Fix] NPC not breaking charm correctly #947 and #905 fixes the issue with charm breaking and spells being cast after to cause a faction war. this removes dots to stop faction wars also. dot removal part needs better testing to ensure it works as intended * Remove this-> since it is implied * Update spell_effects.cpp * clear all this-> * pMob to mob * Added rule Spells:PreventFactionWarOnCharmBreak Co-authored-by: Chris Miles --- common/ruletypes.h | 1 + zone/spell_effects.cpp | 31 ++++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 0bec71b80..c9989383c 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -372,6 +372,7 @@ RULE_BOOL(Spells, OldRainTargets, false, "Use old incorrectly implemented maximu RULE_BOOL(Spells, NPCSpellPush, false, "Enable spell push on NPCs") RULE_BOOL(Spells, July242002PetResists, true, "Enable Pets using PCs resist change from July 24 2002") RULE_INT(Spells, AOEMaxTargets, 0, "Max number of targets a Targeted AOE spell can cast on. Set to 0 for no limit.") +RULE_BOOL(Spells, PreventFactionWarOnCharmBreak, false, "Enable spell interupts and dot removal on charm break to prevent faction wars.") RULE_BOOL(Spells, AllowDoubleInvis, false, "Allows you to cast invisibility spells on a player that is already invisible") RULE_BOOL(Spells, AllowSpellMemorizeFromItem, false, "Allows players to memorize spells by right-clicking spell scrolls") RULE_CATEGORY_END() diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index b6c2f9cf8..5fe9b713a 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -4011,6 +4011,35 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) if (IsAIControlled()) { // clear the hate list of the mobs + if (RuleB(Spells, PreventFactionWarOnCharmBreak)) { + for (auto mob : hate_list.GetHateList()) { + auto tar = mob->entity_on_hatelist; + if (tar->IsCasting()) { + tar->InterruptSpell(tar->CastingSpellID()); + } + uint32 buff_count = tar->GetMaxTotalSlots(); + for (unsigned int j = 0; j < buff_count; j++) { + if (tar->GetBuffs()[j].spellid != SPELL_UNKNOWN) { + auto spell = spells[tar->GetBuffs()[j].spellid]; + if (spell.goodEffect == 0 && IsEffectInSpell(spell.id, SE_CurrentHP) && tar->GetBuffs()[j].casterid == GetID()) { + tar->BuffFadeBySpellID(spell.id); + } + } + } + } + if (IsCasting()) { + InterruptSpell(CastingSpellID()); + } + uint32 buff_count = GetMaxTotalSlots(); + for (unsigned int j = 0; j < buff_count; j++) { + if (GetBuffs()[j].spellid != SPELL_UNKNOWN) { + auto spell = spells[this->GetBuffs()[j].spellid]; + if (spell.goodEffect == 0 && IsEffectInSpell(spell.id, SE_CurrentHP)) { + BuffFadeBySpellID(spell.id); + } + } + } + } entity_list.ReplaceWithTarget(this, tempmob); WipeHateList(); if(tempmob) @@ -4022,7 +4051,7 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) auto app = new EQApplicationPacket(OP_Charm, sizeof(Charm_Struct)); Charm_Struct *ps = (Charm_Struct*)app->pBuffer; ps->owner_id = tempmob->GetID(); - ps->pet_id = this->GetID(); + ps->pet_id = GetID(); ps->command = 0; entity_list.QueueClients(this, app); safe_delete(app); From b87c5484b1ba303dd076f0836aee10ac68ce063c Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 11 Jun 2021 16:57:14 -0400 Subject: [PATCH 068/624] [Pets] Unhardcode Beastlord pet values. (#1379) * [Pets] Unhardcode Beastlord pet values. - Create a Beastlord pets table to allow server operators to easily customize Beastlord pets without a source modification. * Add table to schema. --- common/database_schema.h | 1 + common/version.h | 2 +- utils/sql/db_update_manifest.txt | 1 + .../required/2021_06_06_beastlord_pets.sql | 28 ++++++++ zone/pets.cpp | 69 ++++++++++--------- zone/zonedb.h | 14 ++++ 6 files changed, 81 insertions(+), 34 deletions(-) create mode 100644 utils/sql/git/required/2021_06_06_beastlord_pets.sql diff --git a/common/database_schema.h b/common/database_schema.h index b338a9bc5..25bc70f22 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -217,6 +217,7 @@ namespace DatabaseSchema { "npc_types_tint", "object", "pets", + "pets_beastlord_data", "pets_equipmentset", "pets_equipmentset_entries", "proximities", diff --git a/common/version.h b/common/version.h index e04ba3418..514bed8c7 100644 --- a/common/version.h +++ b/common/version.h @@ -34,7 +34,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9166 +#define CURRENT_BINARY_DATABASE_VERSION 9167 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9028 diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 8efa47aa9..8fd174822 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -420,6 +420,7 @@ 9164|2021_04_23_character_exp_modifiers.sql|SHOW TABLES LIKE 'character_exp_modifiers'|empty| 9165|2021_04_28_idle_pathing.sql|SHOW COLUMNS FROM `spawn2` LIKE 'path_when_zone_idle'|empty| 9166|2021_02_12_dynamic_zone_members.sql|SHOW TABLES LIKE 'dynamic_zone_members'|empty| +9167|2021_06_06_beastlord_pets.sql|SHOW TABLES LIKE 'pets_beastlord_data'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2021_06_06_beastlord_pets.sql b/utils/sql/git/required/2021_06_06_beastlord_pets.sql new file mode 100644 index 000000000..afa9229c0 --- /dev/null +++ b/utils/sql/git/required/2021_06_06_beastlord_pets.sql @@ -0,0 +1,28 @@ +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for pets_beastlord_data +-- ---------------------------- +DROP TABLE IF EXISTS `pets_beastlord_data`; +CREATE TABLE `pets_beastlord_data` ( + `player_race` int UNSIGNED NOT NULL DEFAULT 1, + `pet_race` int UNSIGNED NOT NULL DEFAULT 42, + `texture` tinyint UNSIGNED NOT NULL DEFAULT 0, + `helm_texture` tinyint UNSIGNED NOT NULL DEFAULT 0, + `gender` tinyint UNSIGNED NOT NULL DEFAULT 2, + `size_modifier` float UNSIGNED NULL DEFAULT 1, + `face` tinyint UNSIGNED NOT NULL DEFAULT 0, + PRIMARY KEY (`player_race`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Compact; + +-- ---------------------------- +-- Records of pets_beastlord_data +-- ---------------------------- +INSERT INTO `pets_beastlord_data` VALUES (2, 42, 2, 0, 2, 1, 0); -- Barbarian +INSERT INTO `pets_beastlord_data` VALUES (9, 91, 0, 0, 2, 2.5, 0); -- Troll +INSERT INTO `pets_beastlord_data` VALUES (10, 43, 3, 0, 2, 1, 0); -- Ogre +INSERT INTO `pets_beastlord_data` VALUES (128, 42, 0, 0, 1, 2, 0); -- Iksar +INSERT INTO `pets_beastlord_data` VALUES (130, 63, 0, 0, 2, 0.8, 0); -- Vah Shir + +SET FOREIGN_KEY_CHECKS = 1; \ No newline at end of file diff --git a/zone/pets.cpp b/zone/pets.cpp index e88f0f246..1c833f088 100644 --- a/zone/pets.cpp +++ b/zone/pets.cpp @@ -299,39 +299,16 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, strcat(npc_type->name, "`s_pet"); } - //handle beastlord pet appearance - if(record.petnaming == 2) - { - switch(GetBaseRace()) - { - case VAHSHIR: - npc_type->race = TIGER; - npc_type->size *= 0.8f; - break; - case TROLL: - npc_type->race = ALLIGATOR; - npc_type->size *= 2.5f; - break; - case OGRE: - npc_type->race = BEAR; - npc_type->texture = 3; - npc_type->gender = 2; - break; - case BARBARIAN: - npc_type->race = WOLF; - npc_type->texture = 2; - break; - case IKSAR: - npc_type->race = WOLF; - npc_type->texture = 0; - npc_type->gender = 1; - npc_type->size *= 2.0f; - npc_type->luclinface = 0; - break; - default: - npc_type->race = WOLF; - npc_type->texture = 0; - } + // Beastlord Pets + if(record.petnaming == 2) { + uint16 race_id = GetBaseRace(); + auto beastlord_pet_data = content_db.GetBeastlordPetData(race_id); + npc_type->race = beastlord_pet_data.race_id; + npc_type->texture = beastlord_pet_data.texture; + npc_type->helmtexture = beastlord_pet_data.helm_texture; + npc_type->gender = beastlord_pet_data.gender; + npc_type->size *= beastlord_pet_data.size_modifier; + npc_type->luclinface = beastlord_pet_data.face; } // handle monster summoning pet appearance @@ -716,3 +693,29 @@ bool Pet::CheckSpellLevelRestriction(uint16 spell_id) return owner->CheckSpellLevelRestriction(spell_id); return true; } + +BeastlordPetData::PetStruct ZoneDatabase::GetBeastlordPetData(uint16 race_id) { + BeastlordPetData::PetStruct beastlord_pet_data; + std::string query = fmt::format( + SQL( + SELECT + `pet_race`, `texture`, `helm_texture`, `gender`, `size_modifier`, `face` + FROM `pets_beastlord_data` + WHERE `player_race` = {} + ), + race_id + ); + auto results = QueryDatabase(query); + if (!results.Success() || results.RowCount() != 1) { + return beastlord_pet_data; + } + + auto row = results.begin(); + beastlord_pet_data.race_id = atoi(row[0]); + beastlord_pet_data.texture = atoi(row[1]); + beastlord_pet_data.helm_texture = atoi(row[2]); + beastlord_pet_data.gender = atoi(row[3]); + beastlord_pet_data.size_modifier = atof(row[4]); + beastlord_pet_data.face = atoi(row[5]); + return beastlord_pet_data; +} diff --git a/zone/zonedb.h b/zone/zonedb.h index d7e41ecb0..e05e0872a 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -15,6 +15,8 @@ #include "bot_database.h" #endif +#define WOLF 42 + class Client; class Corpse; class Merc; @@ -243,6 +245,17 @@ struct ClientMercEntry { uint32 npcid; }; +namespace BeastlordPetData { + struct PetStruct { + uint16 race_id = WOLF; + uint8 texture = 0; + uint8 helm_texture = 0; + uint8 gender = 2; + float size_modifier = 1.0f; + uint8 face = 0; + }; +} + class ZoneDatabase : public SharedDatabase { typedef std::list ItemList; public: @@ -458,6 +471,7 @@ public: bool GetPetEntry(const char *pet_type, PetRecord *into); bool GetPoweredPetEntry(const char *pet_type, int16 petpower, PetRecord *into); bool GetBasePetItems(int32 equipmentset, uint32 *items); + BeastlordPetData::PetStruct GetBeastlordPetData(uint16 race_id); void AddLootTableToNPC(NPC* npc, uint32 loottable_id, ItemList* itemlist, uint32* copper, uint32* silver, uint32* gold, uint32* plat); void AddLootDropToNPC(NPC* npc, uint32 lootdrop_id, ItemList* item_list, uint8 droplimit, uint8 mindrop); uint32 GetMaxNPCSpellsID(); From 6e61f6d0ba264d0c8c9bc3e8bfaf2f1e1912348f Mon Sep 17 00:00:00 2001 From: RoTPvP <77220477+RoT-PvP@users.noreply.github.com> Date: Fri, 11 Jun 2021 14:10:30 -0700 Subject: [PATCH 069/624] [Spells] Added a pet check to Cazic Touch (#1365) * Added a pet check to Cazic Touch * Added a pet check to Cazic Touch * Added rule option to Cazic pet Check * Removed Magic Numbers * Bracket fix * Revert "Bracket fix" This reverts commit 3deb3e0cade7bbb48946f394b96b6c98911b3ae8. * Bracket fix * Update spells.cpp * Fixed constants * Revert "Fixed constants" This reverts commit 68502effd38cd5b84961a4c96f93504040dcffd5. * Update eq_constants.h * Update eq_constants.h Co-authored-by: ProducerZekServer Co-authored-by: Chris Miles --- common/eq_constants.h | 7 +++---- common/ruletypes.h | 1 + zone/spells.cpp | 15 ++++++++++++--- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/common/eq_constants.h b/common/eq_constants.h index 057051ff6..1a4f3c462 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -440,10 +440,9 @@ static const uint32 MAX_SPELL_DB_ID_VAL = 65535; static const uint32 DB_SPELL_CAZIC_TOUCH = 982; static const uint32 DB_SPELL_TOUCH_OF_VINITRAS = 2859; - -static const uint32 DB_FACTION_GEM_CHOPPERS = 255; -static const uint32 DB_FACTION_HERETICS = 265; -static const uint32 DB_FACTION_KING_AKANON = 333; +static const uint32 DB_FACTION_GEM_CHOPPERS = 255; +static const uint32 DB_FACTION_HERETICS = 265; +static const uint32 DB_FACTION_KING_AKANON = 333; enum ChatChannelNames : uint16 { diff --git a/common/ruletypes.h b/common/ruletypes.h index c9989383c..ae1625ef5 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -372,6 +372,7 @@ RULE_BOOL(Spells, OldRainTargets, false, "Use old incorrectly implemented maximu RULE_BOOL(Spells, NPCSpellPush, false, "Enable spell push on NPCs") RULE_BOOL(Spells, July242002PetResists, true, "Enable Pets using PCs resist change from July 24 2002") RULE_INT(Spells, AOEMaxTargets, 0, "Max number of targets a Targeted AOE spell can cast on. Set to 0 for no limit.") +RULE_BOOL(Spells, CazicTouchTargetsPetOwner, true, "If True, causes Cazic Touch to swap targets from pet to pet owner if a pet is tanking.") RULE_BOOL(Spells, PreventFactionWarOnCharmBreak, false, "Enable spell interupts and dot removal on charm break to prevent faction wars.") RULE_BOOL(Spells, AllowDoubleInvis, false, "Allows you to cast invisibility spells on a player that is already invisible") RULE_BOOL(Spells, AllowSpellMemorizeFromItem, false, "Allows players to memorize spells by right-clicking spell scrolls") diff --git a/zone/spells.cpp b/zone/spells.cpp index ec5a2c978..0197d35c6 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2046,7 +2046,16 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui if(!IsValidSpell(spell_id)) return false; - //Guard Assist Code + //Death Touch targets the pet owner instead of the pet when said pet is tanking. + if ((RuleB(Spells, CazicTouchTargetsPetOwner) && spell_target->HasOwner()) && spell_id == DB_SPELL_CAZIC_TOUCH || spell_id == DB_SPELL_TOUCH_OF_VINITRAS) { + Mob* owner = spell_target->GetOwner(); + + if (owner) { + spell_target = owner; + } + } + + //Guard Assist Code if (RuleB(Character, PVPEnableGuardFactionAssist) && IsDetrimentalSpell(spell_id) && spell_target != this) { if (IsClient() || (HasOwner() && GetOwner()->IsClient())) { auto& mob_list = entity_list.GetCloseMobList(spell_target); @@ -2062,8 +2071,8 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui } } } - } - } + } + } if( spells[spell_id].zonetype == 1 && !zone->CanCastOutdoor()){ if(IsClient()){ From cc46297b32f2e4a537fe67661b7c7aecef2eee95 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 11 Jun 2021 23:52:47 -0400 Subject: [PATCH 070/624] [Bug Fix] Fix CMakeLists.txt so compile works. (#1387) * [Bug Fix] Fix CMakeLists.txt so compile works. * Typo. * Add ifdefs to bot files so they're not used unless bots are enabled. --- zone/CMakeLists.txt | 3 +++ zone/lua_bot.cpp | 2 ++ zone/lua_bot.h | 2 ++ zone/perl_bot.cpp | 4 +++- 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 0430274f9..5f00e689a 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -45,6 +45,7 @@ SET(zone_sources horse.cpp inventory.cpp loottables.cpp + lua_bot.cpp lua_bit.cpp lua_corpse.cpp lua_client.cpp @@ -102,6 +103,7 @@ SET(zone_sources pathfinder_nav_mesh.cpp pathfinder_null.cpp pathing.cpp + perl_bot.cpp perl_client.cpp perl_doors.cpp perl_entity.cpp @@ -195,6 +197,7 @@ SET(zone_headers hate_list.h heal_rotation.h horse.h + lua_bot.h lua_bit.h lua_client.h lua_corpse.h diff --git a/zone/lua_bot.cpp b/zone/lua_bot.cpp index 78bb7049c..69ddc1187 100644 --- a/zone/lua_bot.cpp +++ b/zone/lua_bot.cpp @@ -1,3 +1,4 @@ +#ifdef BOTS #ifdef LUA_EQEMU #include "lua.hpp" @@ -11,3 +12,4 @@ luabind::scope lua_register_bot() { } #endif +#endif \ No newline at end of file diff --git a/zone/lua_bot.h b/zone/lua_bot.h index 3e7d17631..fffb844ea 100644 --- a/zone/lua_bot.h +++ b/zone/lua_bot.h @@ -1,3 +1,4 @@ +#ifdef BOTS #ifndef EQEMU_LUA_BOT_H #define EQEMU_LUA_BOT_H #ifdef LUA_EQEMU @@ -28,3 +29,4 @@ public: #endif #endif +#endif \ No newline at end of file diff --git a/zone/perl_bot.cpp b/zone/perl_bot.cpp index d78c82d52..d1a9e92a2 100644 --- a/zone/perl_bot.cpp +++ b/zone/perl_bot.cpp @@ -13,6 +13,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifdef BOTS #include "../common/features.h" #ifdef EMBPERL_XS_CLASSES #include "../common/global_define.h" @@ -81,4 +82,5 @@ XS(boot_Bot) XSRETURN_YES; } -#endif //EMBPERL_XS_CLASSES \ No newline at end of file +#endif //EMBPERL_XS_CLASSES +#endif //BOTS \ No newline at end of file From 00dd7c2b71472564fb61287fc7bbb716a426e5cf Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 12 Jun 2021 00:41:06 -0400 Subject: [PATCH 071/624] [Quest API] Add getcleannpcnamebyid(npc_id) to Perl/Lua. (#1383) * [Quest API] Add optional clean name parameter to getnpcnamebyid in Perl/Lua. - Allows Server Operators to grab the clean name without having to clean it up in their Perl/Lua. * Convert from a parameter to a method. * Add safer method. * Convert to proper type. --- common/database.cpp | 20 ++++++++++++++++++++ common/database.h | 1 + common/string_util.h | 13 +++++++++++++ zone/embparser_api.cpp | 17 ++++++++++++++++- zone/lua_general.cpp | 5 +++++ zone/questmgr.cpp | 8 ++++++++ zone/questmgr.h | 1 + 7 files changed, 64 insertions(+), 1 deletion(-) diff --git a/common/database.cpp b/common/database.cpp index 385f07c18..1290d289a 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -906,6 +906,26 @@ std::string Database::GetNPCNameByID(uint32 npc_id) { return res; } +std::string Database::GetCleanNPCNameByID(uint32 npc_id) { + std::string query = fmt::format("SELECT `name` FROM `npc_types` WHERE id = {}", npc_id); + auto results = QueryDatabase(query); + std::string res; + std::string mob_name; + + if (!results.Success()) { + return res; + } + + if (results.RowCount() == 0) { + return res; + } + + auto row = results.begin(); + mob_name = row[0]; + CleanMobName(mob_name.begin(), mob_name.end(), std::back_inserter(res)); + return res; +} + bool Database::LoadVariables() { auto results = QueryDatabase(StringFormat("SELECT varname, value, unix_timestamp() FROM variables where unix_timestamp(ts) >= %d", varcache.last_update)); diff --git a/common/database.h b/common/database.h index f6d93d5aa..1b3b736ae 100644 --- a/common/database.h +++ b/common/database.h @@ -140,6 +140,7 @@ public: void GetCharName(uint32 char_id, char* name); std::string GetCharNameByID(uint32 char_id); std::string GetNPCNameByID(uint32 npc_id); + std::string GetCleanNPCNameByID(uint32 npc_id); void LoginIP(uint32 AccountID, const char* LoginIP); /* Instancing */ diff --git a/common/string_util.h b/common/string_util.h index 5fd91281c..8c4a6878f 100644 --- a/common/string_util.h +++ b/common/string_util.h @@ -206,4 +206,17 @@ std::string convert2digit(int n, std::string suffix); std::string numberToWords(unsigned long long int n); std::string FormatName(const std::string& char_name); +template +auto CleanMobName(InputIterator first, InputIterator last, OutputIterator result) +{ + for (; first != last; ++first) { + if(*first == '_') { + *result = ' '; + } else if (isalpha(*first) || *first == '`') { + *result = *first; + } + } + return result; +} + #endif diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 44c159b76..4c92c4969 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -3026,7 +3026,6 @@ XS(XS__getnpcnamebyid) { dXSTARG; uint32 npc_id = (int) SvIV(ST(0)); auto npc_name = quest_manager.getnpcnamebyid(npc_id); - sv_setpv(TARG, npc_name.c_str()); XSprePUSH; PUSHTARG; @@ -6760,6 +6759,21 @@ XS(XS__crosszoneaddldonwinbyexpeditionid) { XSRETURN_EMPTY; } +XS(XS__getcleannpcnamebyid); +XS(XS__getcleannpcnamebyid) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::getcleannpcnamebyid(uint32 npc_id)"); + + dXSTARG; + uint32 npc_id = (uint32) SvUV(ST(0)); + auto npc_name = quest_manager.getcleannpcnamebyid(npc_id); + sv_setpv(TARG, npc_name.c_str()); + XSprePUSH; + PUSHTARG; + XSRETURN(1); +} + /* This is the callback perl will look for to setup the quest package's XSUBs @@ -6984,6 +6998,7 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "getaaexpmodifierbycharid"), XS__getaaexpmodifierbycharid, file); newXS(strcpy(buf, "getcharidbyname"), XS__getcharidbyname, file); newXS(strcpy(buf, "getclassname"), XS__getclassname, file); + newXS(strcpy(buf, "getcleannpcnamebyid"), XS__getcleannpcnamebyid, file); newXS(strcpy(buf, "gethexcolorcode"), XS__gethexcolorcode, file); newXS(strcpy(buf, "getcurrencyid"), XS__getcurrencyid, file); newXS(strcpy(buf, "getexpmodifierbycharid"), XS__getexpmodifierbycharid, file); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 2e26b460f..046b8c7d6 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -2455,6 +2455,10 @@ void lua_cross_zone_add_ldon_win_by_expedition_id(uint32 expedition_id, uint32 t quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id); } +std::string lua_get_clean_npc_name_by_id(uint32 npc_id) { + return quest_manager.getcleannpcnamebyid(npc_id); +} + #define LuaCreateNPCParse(name, c_type, default_value) do { \ cur = table[#name]; \ if(luabind::type(cur) != LUA_TNIL) { \ @@ -2796,6 +2800,7 @@ luabind::scope lua_register_general() { luabind::def("get_char_id_by_name", (uint32(*)(const char*))&lua_get_char_id_by_name), luabind::def("get_class_name", (std::string(*)(uint8))&lua_get_class_name), luabind::def("get_class_name", (std::string(*)(uint8,uint8))&lua_get_class_name), + luabind::def("get_clean_npc_name_by_id", &lua_get_clean_npc_name_by_id), luabind::def("get_currency_id", &lua_get_currency_id), luabind::def("get_currency_item_id", &lua_get_currency_item_id), luabind::def("get_guild_name_by_id", &lua_get_guild_name_by_id), diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 09046f775..640d5f4f6 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -2789,6 +2789,14 @@ std::string QuestManager::getnpcnamebyid(uint32 npc_id) { return res; } +std::string QuestManager::getcleannpcnamebyid(uint32 npc_id) { + std::string res; + if (npc_id > 0) { + res = database.GetCleanNPCNameByID(npc_id); + } + return res; +} + uint16 QuestManager::CreateInstance(const char *zone, int16 version, uint32 duration) { QuestManagerCurrentQuestVars(); diff --git a/zone/questmgr.h b/zone/questmgr.h index 815a10e35..999e128e6 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -274,6 +274,7 @@ public: int getguildidbycharid(uint32 char_id); int getgroupidbycharid(uint32 char_id); std::string getnpcnamebyid(uint32 npc_id); + std::string getcleannpcnamebyid(uint32 npc_id); int getraididbycharid(uint32 char_id); void SetRunning(bool val); bool IsRunning(); From 6e12d2fd49bfe1ede027446ae7a727d180958cdf Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 12 Jun 2021 12:32:36 -0400 Subject: [PATCH 072/624] [Commands] Add #viewzoneloot [item id] command. (#1382) * [Commands] Add #viewzoneloot [item id] command. - Allows GMs to search for a specific item across all the loot currently available on the spawned NPCs in the zone. - Specifying item ID 0 will allow GMs to see all the droppable items, I tested in Sanctus Seru (a huge zone) and it sent approximately 1,200 messages, which didn't lag or desync my client. * Adjustments. * Adjustments. --- zone/command.cpp | 110 +++++++++++++++++++++++++++++++++++++++++++++++ zone/command.h | 1 + zone/npc.h | 1 + 3 files changed, 112 insertions(+) diff --git a/zone/command.cpp b/zone/command.cpp index 83d035952..81725d064 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -433,6 +433,7 @@ int command_init(void) command_add("version", "- Display current version of EQEmu server", 0, command_version) || command_add("viewnpctype", "[npctype id] - Show info about an npctype", 100, command_viewnpctype) || command_add("viewpetition", "[petition number] - View a petition", 20, command_viewpetition) || + command_add("viewzoneloot", "[item id] - Allows you to search a zone's loot for a specific item ID. (0 shows all loot in the zone)", 80, command_viewzoneloot) || command_add("wc", "[wear slot] [material] - Sends an OP_WearChange for your target", 200, command_wc) || command_add("weather", "[0/1/2/3] (Off/Rain/Snow/Manual) - Change the weather", 80, command_weather) || command_add("who", "[search]", 20, command_who) || @@ -14106,6 +14107,115 @@ void command_network(Client *c, const Seperator *sep) } } +void command_viewzoneloot(Client *c, const Seperator *sep) +{ + std::map zone_loot_list; + auto npc_list = entity_list.GetNPCList(); + uint32 loot_amount = 0, loot_id = 1, search_item_id = 0; + if (sep->argnum == 1 && sep->IsNumber(1)) { + search_item_id = atoi(sep->arg[1]); + } else if (sep->argnum == 1 && !sep->IsNumber(1)) { + c->Message( + Chat::Yellow, + "Usage: #viewzoneloot [item id]" + ); + return; + } + for (auto npc_entity : npc_list) { + auto current_npc_item_list = npc_entity.second->GetItemList(); + zone_loot_list.insert({ npc_entity.second->GetID(), current_npc_item_list }); + } + for (auto loot_item : zone_loot_list) { + uint32 current_entity_id = loot_item.first; + auto current_item_list = loot_item.second; + auto current_npc = entity_list.GetNPCByID(current_entity_id); + std::string npc_link; + if (current_npc) { + std::string npc_name = current_npc->GetCleanName(); + uint32 instance_id = zone->GetInstanceID(); + uint32 zone_id = zone->GetZoneID(); + std::string command_link = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format( + "#{} {} {} {} {}", + (instance_id != 0 ? "zoneinstance" : "zone"), + (instance_id != 0 ? instance_id : zone_id), + current_npc->GetX(), + current_npc->GetY(), + current_npc->GetZ() + ), + false, + "Goto" + ); + npc_link = fmt::format( + " NPC: {} (ID {}) [{}]", + npc_name, + current_entity_id, + command_link + ); + } + + for (auto current_item : current_item_list) { + if (search_item_id == 0 || current_item->item_id == search_item_id) { + EQ::SayLinkEngine linker; + linker.SetLinkType(EQ::saylink::SayLinkLootItem); + linker.SetLootData(current_item); + c->Message( + Chat::White, + fmt::format( + "{}. {} ({}){}", + loot_id, + linker.GenerateLink(), + current_item->item_id, + npc_link + ).c_str() + ); + loot_id++; + loot_amount++; + } + } + } + + + if (search_item_id != 0) { + std::string drop_string = ( + loot_amount > 0 ? + fmt::format( + "dropping in {} {}", + loot_amount, + (loot_amount > 1 ? "places" : "place") + ) : + "not dropping" + ); + c->Message( + Chat::White, + fmt::format( + "{} ({}) is {}.", + database.CreateItemLink(search_item_id), + search_item_id, + drop_string + ).c_str() + ); + } else { + std::string drop_string = ( + loot_amount > 0 ? + fmt::format( + "{} {} {}", + (loot_amount > 1 ? "items" : "item"), + (loot_amount > 1 ? "are" : "is"), + (loot_amount > 1 ? "dropping" : "not dropping") + ) : + "items are dropping" + ); + c->Message( + Chat::White, + fmt::format( + "{} {}.", + loot_amount, + drop_string + ).c_str() + ); + } +} // All new code added to command.cpp should be BEFORE this comment line. Do no append code to this file below the BOTS code block. #ifdef BOTS #include "bot_command.h" diff --git a/zone/command.h b/zone/command.h index 303e0f132..041fd8832 100644 --- a/zone/command.h +++ b/zone/command.h @@ -354,6 +354,7 @@ void command_zone(Client *c, const Seperator *sep); void command_zone_instance(Client *c, const Seperator *sep); void command_zonebootup(Client *c, const Seperator *sep); void command_zonelock(Client *c, const Seperator *sep); +void command_viewzoneloot(Client *c, const Seperator *sep); void command_zoneshutdown(Client *c, const Seperator *sep); void command_zonespawn(Client *c, const Seperator *sep); void command_zonestatus(Client *c, const Seperator *sep); diff --git a/zone/npc.h b/zone/npc.h index 995542e0d..121503215 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -198,6 +198,7 @@ public: void RemoveItem(uint32 item_id, uint16 quantity = 0, uint16 slot = 0); void CheckTrivialMinMaxLevelDrop(Mob *killer); void ClearItemList(); + inline const ItemList &GetItemList() { return itemlist; } ServerLootItem_Struct* GetItem(int slot_id); void AddCash(uint16 in_copper, uint16 in_silver, uint16 in_gold, uint16 in_platinum); void AddCash(); From 88526eac21475f6f67d775f7d9774eabf3659abe Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 12 Jun 2021 12:34:19 -0400 Subject: [PATCH 073/624] [Quest API] Add ChangeLastName() and ClearLastName() to Lua. (#1386) --- zone/lua_npc.cpp | 16 +++++++++++++++- zone/lua_npc.h | 2 ++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/zone/lua_npc.cpp b/zone/lua_npc.cpp index c47870653..d7dbe06e8 100644 --- a/zone/lua_npc.cpp +++ b/zone/lua_npc.cpp @@ -570,6 +570,18 @@ bool Lua_NPC::IsRaidTarget() return self->IsRaidTarget(); } +void Lua_NPC::ChangeLastName(const char *lastname) +{ + Lua_Safe_Call_Void(); + self->ChangeLastName(lastname); +} + +void Lua_NPC::ClearLastName() +{ + Lua_Safe_Call_Void(); + self->ClearLastName(); +} + luabind::scope lua_register_npc() { return luabind::class_("NPC") .def(luabind::constructor<>()) @@ -684,7 +696,9 @@ luabind::scope lua_register_npc() { .def("GetAvoidanceRating", &Lua_NPC::GetAvoidanceRating) .def("RecalculateSkills", (void(Lua_NPC::*)(void))&Lua_NPC::RecalculateSkills) .def("ScaleNPC", (void(Lua_NPC::*)(uint8))&Lua_NPC::ScaleNPC) - .def("IsRaidTarget", (bool(Lua_NPC::*)(void))&Lua_NPC::IsRaidTarget); + .def("IsRaidTarget", (bool(Lua_NPC::*)(void))&Lua_NPC::IsRaidTarget) + .def("ChangeLastName", (void(Lua_NPC::*)(const char*))&Lua_NPC::ChangeLastName) + .def("ClearLastName", (void(Lua_NPC::*)(void))&Lua_NPC::ClearLastName); } #endif diff --git a/zone/lua_npc.h b/zone/lua_npc.h index 00c4623cb..2860366ef 100644 --- a/zone/lua_npc.h +++ b/zone/lua_npc.h @@ -138,6 +138,8 @@ public: void RecalculateSkills(); void ScaleNPC(uint8 npc_level); bool IsRaidTarget(); + void ChangeLastName(const char *lastname); + void ClearLastName(); }; #endif From a0063997e1f3a63f3b8e694d070d3ded794b5f73 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 12 Jun 2021 12:34:55 -0400 Subject: [PATCH 074/624] [Quest API] Add SetHideMe() to Perl/Lua. (#1388) - Add $client->SetHideMe(hide_me_state) to Perl. - Add client:SetHideMe(hide_me_state) to Lua. --- zone/lua_client.cpp | 8 +++++++- zone/lua_client.h | 1 + zone/perl_client.cpp | 15 +++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index a84a52813..6d700a689 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -2093,6 +2093,11 @@ void Lua_Client::AddLDoNWin(uint32 theme_id) { self->AddLDoNWin(theme_id); } +void Lua_Client::SetHideMe(bool hide_me_state) { + Lua_Safe_Call_Void(); + self->SetHideMe(hide_me_state); +} + luabind::scope lua_register_client() { return luabind::class_("Client") .def(luabind::constructor<>()) @@ -2447,7 +2452,8 @@ luabind::scope lua_register_client() { .def("SetAAEXPModifier", (void(Lua_Client::*)(uint32,double))&Lua_Client::SetAAEXPModifier) .def("SetEXPModifier", (void(Lua_Client::*)(uint32,double))&Lua_Client::SetEXPModifier) .def("AddLDoNLoss", (void(Lua_Client::*)(uint32))&Lua_Client::AddLDoNLoss) - .def("AddLDoNWin", (void(Lua_Client::*)(uint32))&Lua_Client::AddLDoNWin); + .def("AddLDoNWin", (void(Lua_Client::*)(uint32))&Lua_Client::AddLDoNWin) + .def("SetHideMe", (void(Lua_Client::*)(bool))&Lua_Client::SetHideMe); } luabind::scope lua_register_inventory_where() { diff --git a/zone/lua_client.h b/zone/lua_client.h index 743d32f65..60c11fbbb 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -357,6 +357,7 @@ public: void DisableAreaEndRegen(); void EnableAreaRegens(int value); void DisableAreaRegens(); + void SetHideMe(bool hide_me_state); void SetPrimaryWeaponOrnamentation(uint32 model_id); void SetSecondaryWeaponOrnamentation(uint32 model_id); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 054664c69..60efbf787 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -5318,6 +5318,20 @@ XS(XS_Client_AddLDoNWin) { XSRETURN_EMPTY; } +XS(XS_Client_SetHideMe); +XS(XS_Client_SetHideMe) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::SetHideMe(THIS, bool hide_me_state)"); + { + Client* THIS; + bool hide_me_state = (bool) SvTRUE(ST(1)); + VALIDATE_THIS_IS_CLIENT; + THIS->SetHideMe(hide_me_state); + } + XSRETURN_EMPTY; +} + #ifdef __cplusplus extern "C" #endif @@ -5579,6 +5593,7 @@ XS(boot_Client) { newXSproto(strcpy(buf, "SetFactionLevel2"), XS_Client_SetFactionLevel2, file, "$$$$$$$"); newXSproto(strcpy(buf, "SetFeigned"), XS_Client_SetFeigned, file, "$$"); newXSproto(strcpy(buf, "SetGM"), XS_Client_SetGM, file, "$$"); + newXSproto(strcpy(buf, "SetHideMe"), XS_Client_SetHideMe, file, "$$"); newXSproto(strcpy(buf, "SetHorseId"), XS_Client_SetHorseId, file, "$$"); newXSproto(strcpy(buf, "SetHunger"), XS_Client_SetHunger, file, "$$"); newXSproto(strcpy(buf, "SetLanguageSkill"), XS_Client_SetLanguageSkill, file, "$$$"); From d162f255367c63199c7634200a85dbb0841eb6e9 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 12 Jun 2021 12:36:19 -0400 Subject: [PATCH 075/624] [Commands] Add #findclass [search criteria] command. (#1384) * [Commands] Add #findclass [search criteria] command. - Allows GMs to find a class by name or ID. - Modify some verbiage in command messages that were improper. * Update find functions to use strings instead of chars. --- zone/command.cpp | 192 +++++++++++++++++++++++++++++++++++------------ zone/command.h | 1 + 2 files changed, 144 insertions(+), 49 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 81725d064..edcd169b3 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -212,6 +212,7 @@ int command_init(void) command_add("face", "- Change the face of your target", 80, command_face) || command_add("faction", "[Find (criteria | all ) | Review (criteria | all) | Reset (id)] - Resets Player's Faction", 80, command_faction) || command_add("findaliases", "[search criteria]- Searches for available command aliases, by alias or command", 0, command_findaliases) || + command_add("findclass", "[search criteria] - Search for a class", 50, command_findclass) || command_add("findnpctype", "[search criteria] - Search database NPC types", 100, command_findnpctype) || command_add("findrace", "[search criteria] - Search for a race", 50, command_findrace) || command_add("findspell", "[search criteria] - Search for a spell", 50, command_findspell) || @@ -2689,34 +2690,101 @@ void command_showskills(Client *c, const Seperator *sep) c->Message(Chat::White, "Skill [%d] is at [%d] - %u", i, t->GetSkill(i), t->GetRawSkill(i)); } -void command_findrace(Client *c, const Seperator *sep) +void command_findclass(Client *c, const Seperator *sep) { if (sep->arg[1][0] == 0) { - c->Message(Chat::White, "Usage: #findrace [race name]"); + c->Message(Chat::White, "Usage: #findclass [search criteria]"); } else if (Seperator::IsNumber(sep->argplus[1])) { int search_id = atoi(sep->argplus[1]); - std::string race_name = GetRaceIDName(search_id); - if (race_name != std::string("")) { - c->Message(Chat::White, "Race %d: %s", search_id, race_name.c_str()); + std::string class_name = GetClassIDName(search_id); + if (class_name.length() > 0) { + c->Message( + Chat::White, + fmt::format( + "Class {}: {}", + search_id, + class_name + ).c_str() + ); return; } } else { - const char *search_criteria = sep->argplus[1]; + std::string search_criteria = str_tolower(sep->argplus[1]); int found_count = 0; - char race_name[64]; - char search_string[65]; - strn0cpy(search_string, search_criteria, sizeof(search_string)); - strupr(search_string); - char *string_location; - for (int race_id = RACE_HUMAN_1; race_id <= RT_PEGASUS_3; race_id++) { - strn0cpy(race_name, GetRaceIDName(race_id), sizeof(race_name)); - strupr(race_name); - string_location = strstr(race_name, search_string); - if (string_location != nullptr) { - c->Message(Chat::White, "Race %d: %s", race_id, GetRaceIDName(race_id)); - found_count++; + for (int class_id = WARRIOR; class_id <= MERCERNARY_MASTER; class_id++) { + std::string class_name = GetClassIDName(class_id); + std::string class_name_lower = str_tolower(class_name); + if (search_criteria.length() > 0 && class_name_lower.find(search_criteria) == std::string::npos) { + continue; } + c->Message( + Chat::White, + fmt::format( + "Class {}: {}", + class_id, + class_name + ).c_str() + ); + found_count++; + + if (found_count == 20) { + break; + } + } + + if (found_count == 20) { + c->Message(Chat::White, "20 Classes found... max reached."); + } else { + c->Message( + Chat::White, + fmt::format( + "{} Class(es) found.", + found_count + ).c_str() + ); + } + } +} + +void command_findrace(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #findrace [search criteria]"); + } else if (Seperator::IsNumber(sep->argplus[1])) { + int search_id = atoi(sep->argplus[1]); + std::string race_name = GetRaceIDName(search_id); + if (race_name.length() > 0) { + c->Message( + Chat::White, + fmt::format( + "Race {}: {}", + search_id, + race_name + ).c_str() + ); + return; + } + } else { + std::string search_criteria = str_tolower(sep->argplus[1]); + int found_count = 0; + for (int race_id = RACE_HUMAN_1; race_id <= RT_PEGASUS_3; race_id++) { + std::string race_name = GetRaceIDName(race_id); + std::string race_name_lower = str_tolower(race_name); + if (search_criteria.length() > 0 && race_name_lower.find(search_criteria) == std::string::npos) { + continue; + } + + c->Message( + Chat::White, + fmt::format( + "Race {}: {}", + race_id, + race_name + ).c_str() + ); + found_count++; + if (found_count == 20) { break; } @@ -2724,51 +2792,77 @@ void command_findrace(Client *c, const Seperator *sep) if (found_count == 20) { c->Message(Chat::White, "20 Races found... max reached."); } else { - c->Message(Chat::White, "%i Race(s) found.", found_count); + c->Message( + Chat::White, + fmt::format( + "{} Race(s) found.", + found_count + ).c_str() + ); } } } void command_findspell(Client *c, const Seperator *sep) { - if (sep->arg[1][0] == 0) - c->Message(Chat::White, "Usage: #FindSpell [spellname]"); - else if (SPDAT_RECORDS <= 0) + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #findspell [search criteria]"); + } else if (SPDAT_RECORDS <= 0) { c->Message(Chat::White, "Spells not loaded"); - else if (Seperator::IsNumber(sep->argplus[1])) { - int spellid = atoi(sep->argplus[1]); - if (spellid <= 0 || spellid >= SPDAT_RECORDS) { - c->Message(Chat::White, "Error: Number out of range"); - } - else { - c->Message(Chat::White, " %i: %s", spellid, spells[spellid].name); + } else if (Seperator::IsNumber(sep->argplus[1])) { + int spell_id = atoi(sep->argplus[1]); + if (!IsValidSpell(spell_id)) { + c->Message(Chat::White, "Error: Invalid Spell"); + } else { + c->Message( + Chat::White, + fmt::format( + "{}: {}", + spell_id, + spells[spell_id].name + ).c_str() + ); } } else { - int count=0; - //int iSearchLen = strlen(sep->argplus[1])+1; - char sName[64]; - char sCriteria[65]; - strn0cpy(sCriteria, sep->argplus[1], 64); - strupr(sCriteria); - for (int i=0; iMessage(Chat::White, " %i: %s", i, spells[i].name); - count++; + std::string search_criteria = str_tolower(sep->argplus[1]); + int found_count = 0; + for (int i = 0; i < SPDAT_RECORDS; i++) { + auto current_spell = spells[i]; + if (current_spell.name[0] != 0) { + std::string spell_name = current_spell.name; + std::string spell_name_lower = str_tolower(spell_name); + if (search_criteria.length() > 0 && spell_name_lower.find(search_criteria) == std::string::npos) { + continue; } - else if (count > 20) + + c->Message( + Chat::White, + fmt::format( + "{}: {}", + i, + spell_name + ).c_str() + ); + found_count++; + + if (found_count == 20) { break; + } } } - if (count > 20) - c->Message(Chat::White, "20 spells found... max reached."); - else - c->Message(Chat::White, "%i spells found.", count); + + if (found_count == 20) { + c->Message(Chat::White, "20 Spells found... max reached."); + } else { + c->Message( + Chat::White, + fmt::format( + "{} Spell(s) found.", + found_count + ).c_str() + ); + } } } diff --git a/zone/command.h b/zone/command.h index 041fd8832..bc9d06ff8 100644 --- a/zone/command.h +++ b/zone/command.h @@ -104,6 +104,7 @@ void command_equipitem(Client *c, const Seperator *sep); void command_face(Client *c, const Seperator *sep); void command_faction(Client *c, const Seperator *sep); void command_findaliases(Client *c, const Seperator *sep); +void command_findclass(Client *c, const Seperator *sep); void command_findnpctype(Client *c, const Seperator *sep); void command_findrace(Client *c, const Seperator *sep); void command_findspell(Client *c, const Seperator *sep); From e285a88e13fb9178d40968b4fbbe58f334ee2503 Mon Sep 17 00:00:00 2001 From: Kurt Gilpin Date: Sat, 12 Jun 2021 12:13:48 -0500 Subject: [PATCH 076/624] Fix crash when casting with no target (#1390) --- zone/spells.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 0197d35c6..bcbe48d9f 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2047,7 +2047,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui return false; //Death Touch targets the pet owner instead of the pet when said pet is tanking. - if ((RuleB(Spells, CazicTouchTargetsPetOwner) && spell_target->HasOwner()) && spell_id == DB_SPELL_CAZIC_TOUCH || spell_id == DB_SPELL_TOUCH_OF_VINITRAS) { + if ((RuleB(Spells, CazicTouchTargetsPetOwner) && spell_target && spell_target->HasOwner()) && spell_id == DB_SPELL_CAZIC_TOUCH || spell_id == DB_SPELL_TOUCH_OF_VINITRAS) { Mob* owner = spell_target->GetOwner(); if (owner) { From 4a067e4e9b6953e6cd3c4de3db43c87895f66268 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 13 Jun 2021 02:03:21 -0500 Subject: [PATCH 077/624] [Fix] Fix illusions (#1389) --- zone/mob.cpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index ddd658b0d..2d936634e 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1859,19 +1859,20 @@ void Mob::SendIllusionPacket( } // update internal values for mob - size = (in_size <= 0.0f) ? GetSize() : in_size; - texture = new_texture; - helmtexture = new_helmtexture; - haircolor = new_haircolor; - beardcolor = new_beardcolor; - eyecolor1 = new_eyecolor1; - eyecolor2 = new_eyecolor2; - hairstyle = new_hairstyle; - luclinface = new_luclinface; - beard = new_beard; - drakkin_heritage = new_drakkin_heritage; - drakkin_tattoo = new_drakkin_tattoo; - drakkin_details = new_drakkin_details; + // TODO: Move this to its own SetIllusion function later + // size = (in_size <= 0.0f) ? GetSize() : in_size; + // texture = new_texture; + // helmtexture = new_helmtexture; + // haircolor = new_haircolor; + // beardcolor = new_beardcolor; + // eyecolor1 = new_eyecolor1; + // eyecolor2 = new_eyecolor2; + // hairstyle = new_hairstyle; + // luclinface = new_luclinface; + // beard = new_beard; + // drakkin_heritage = new_drakkin_heritage; + // drakkin_tattoo = new_drakkin_tattoo; + // drakkin_details = new_drakkin_details; auto outapp = new EQApplicationPacket(OP_Illusion, sizeof(Illusion_Struct)); Illusion_Struct *is = (Illusion_Struct *) outapp->pBuffer; From e1e5873398f89e339f33faebbd26b278783e27de Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 13 Jun 2021 02:09:23 -0500 Subject: [PATCH 078/624] [Hotfix] Fix crash pertaining to new PVPEnableGuardFactionAssist code (#1393) Pushing through due to crash severity on master --- zone/spells.cpp | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index bcbe48d9f..757ec0645 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -383,7 +383,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, Chat::SpellFailure, (IsClient() ? FilterPCSpells : FilterNPCSpells), (fizzle_msg == MISS_NOTE ? MISSED_NOTE_OTHER : SPELL_FIZZLE_OTHER), - /* + /* MessageFormat: You miss a note, bringing your song to a close! (if missed note) MessageFormat: A missed note brings %1's song to a close! MessageFormat: %1's spell fizzles! @@ -1235,7 +1235,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo else { if (!RuleB(Character, PetsUseReagents) && (IsEffectInSpell(spell_id, SE_SummonPet) || IsEffectInSpell(spell_id, SE_NecPet))) { //bypass reagent cost - } + } else if(c->GetInv().HasItem(component, component_count, invWhereWorn|invWherePersonal) == -1) // item not found { if (!missingreags) @@ -1421,7 +1421,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo Client *c = CastToClient(); if((IsFromItem && RuleB(Character, SkillUpFromItems)) || !IsFromItem) { c->CheckSongSkillIncrease(spell_id); - } + } if (spells[spell_id].EndurTimerIndex > 0 && slot < CastingSlot::MaxGems) c->SetLinkedSpellReuseTimer(spells[spell_id].EndurTimerIndex, spells[spell_id].recast_time / 1000); c->MemorizeSpell(static_cast(slot), spell_id, memSpellSpellbar); @@ -2049,14 +2049,14 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui //Death Touch targets the pet owner instead of the pet when said pet is tanking. if ((RuleB(Spells, CazicTouchTargetsPetOwner) && spell_target && spell_target->HasOwner()) && spell_id == DB_SPELL_CAZIC_TOUCH || spell_id == DB_SPELL_TOUCH_OF_VINITRAS) { Mob* owner = spell_target->GetOwner(); - + if (owner) { spell_target = owner; } } - + //Guard Assist Code - if (RuleB(Character, PVPEnableGuardFactionAssist) && IsDetrimentalSpell(spell_id) && spell_target != this) { + if (RuleB(Character, PVPEnableGuardFactionAssist) && spell_target && IsDetrimentalSpell(spell_id) && spell_target != this) { if (IsClient() || (HasOwner() && GetOwner()->IsClient())) { auto& mob_list = entity_list.GetCloseMobList(spell_target); for (auto& e : mob_list) { @@ -3430,7 +3430,7 @@ int Mob::CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite) firstfree = i; } if(ret == -1) { - + LogAI("Buff [{}] would conflict with [{}] in slot [{}], reporting stack failure", spellid, curbuf.spellid, i); return -1; // stop the spell, can't stack it } @@ -3565,7 +3565,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r spelltar, /* Sender */ action_packet, /* Packet */ true, /* Ignore Sender */ - RuleI(Range, SpellMessages), + RuleI(Range, SpellMessages), this, /* Skip this Mob */ true, /* Packet ACK */ (spelltar->IsClient() ? FilterPCSpells : FilterNPCSpells) /* EQ Filter Type: (8 or 9) */ @@ -3908,7 +3908,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r spelltar->CastToClient()->BreakSneakWhenCastOn(this, true); spelltar->CastToClient()->BreakFeignDeathWhenCastOn(true); } - + spelltar->CheckNumHitsRemaining(NumHit::IncomingSpells); CheckNumHitsRemaining(NumHit::OutgoingSpells); @@ -4024,7 +4024,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r spelltar, /* Sender */ message_packet, /* Packet */ false, /* Ignore Sender */ - RuleI(Range, SpellMessages), + RuleI(Range, SpellMessages), 0, /* Skip this mob */ true, /* Packet ACK */ (spelltar->IsClient() ? FilterPCSpells : FilterNPCSpells) /* Message Filter Type: (8 or 9) */ @@ -4098,17 +4098,17 @@ bool Mob::FindBuff(uint16 spellid) uint16 Mob::FindBuffBySlot(int slot) { if (buffs[slot].spellid != SPELL_UNKNOWN) return buffs[slot].spellid; - + return 0; } uint32 Mob::BuffCount() { uint32 active_buff_count = 0; int buff_count = GetMaxTotalSlots(); - for (int i = 0; i < buff_count; i++) + for (int i = 0; i < buff_count; i++) if (buffs[i].spellid != SPELL_UNKNOWN) active_buff_count++; - + return active_buff_count; } @@ -5023,7 +5023,7 @@ void Mob::Mesmerize() if (spell_id) InterruptSpell(spell_id); - + StopNavigation(); } @@ -5141,7 +5141,7 @@ uint32 Client::GetSpellIDByBookSlot(int book_slot) { uint16 Client::FindMemmedSpellBySlot(int slot) { if (m_pp.mem_spells[slot] != 0xFFFFFFFF) return m_pp.mem_spells[slot]; - + return 0; } @@ -5150,7 +5150,7 @@ int Client::MemmedCount() { for (int i = 0; i < EQ::spells::SPELL_GEM_COUNT; i++) if (m_pp.mem_spells[i] != 0xFFFFFFFF) memmed_count++; - + return memmed_count; } @@ -5328,7 +5328,7 @@ bool Client::SpellBucketCheck(uint16 spell_id, uint32 char_id) { if (results.RowCount() != 1) return true; - + auto row = results.begin(); spell_bucket_name = row[0]; spell_bucket_value = atoi(row[1]); From 8d90b5a2e7356594bd1bf8ffef61e5257a9e8cbd Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 13 Jun 2021 18:06:27 -0500 Subject: [PATCH 079/624] [Hotfix] Illusion Revert (#1398) * Revert some "fixes", clean some code up * Use RaceGender default height data for when calculating size during SendIllusionPacket which should alleviate some inconsistencies for new clients zoning in and seeing the entity * Some code cleanup --- common/eq_packet_structs.h | 50 ++++++++--------- zone/mob.cpp | 106 +++++++++++++++++++------------------ zone/mob_appearance.cpp | 4 +- zone/spell_effects.cpp | 45 ++++++++-------- 4 files changed, 103 insertions(+), 102 deletions(-) diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index f5d47546e..ac08fe8d1 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -2131,31 +2131,31 @@ struct AdventureLeaderboard_Struct };*/ struct Illusion_Struct { //size: 256 - SoF -/*000*/ uint32 spawnid; -/*004*/ char charname[64]; // -/*068*/ uint16 race; // -/*070*/ char unknown006[2]; -/*072*/ uint8 gender; -/*073*/ uint8 texture; -/*074*/ uint8 unknown008; // -/*075*/ uint8 unknown009; // -/*076*/ uint8 helmtexture; // -/*077*/ uint8 unknown010; // -/*078*/ uint8 unknown011; // -/*079*/ uint8 unknown012; // -/*080*/ uint32 face; // -/*084*/ uint8 hairstyle; // -/*085*/ uint8 haircolor; // -/*086*/ uint8 beard; // -/*087*/ uint8 beardcolor; // -/*088*/ float size; // -/*092*/ uint32 drakkin_heritage; // -/*096*/ uint32 drakkin_tattoo; // -/*100*/ uint32 drakkin_details; // -/*104*/ EQ::TintProfile armor_tint; // -/*140*/ uint8 eyecolor1; // Field Not Identified in any Illusion Struct -/*141*/ uint8 eyecolor2; // Field Not Identified in any Illusion Struct -/*142*/ uint8 unknown138[114]; // +/*000*/ uint32 spawnid; +/*004*/ char charname[64]; // +/*068*/ uint16 race; // +/*070*/ char unknown006[2]; +/*072*/ uint8 gender; +/*073*/ uint8 texture; +/*074*/ uint8 unknown008; // +/*075*/ uint8 unknown009; // +/*076*/ uint8 helmtexture; // +/*077*/ uint8 unknown010; // +/*078*/ uint8 unknown011; // +/*079*/ uint8 unknown012; // +/*080*/ uint32 face; // +/*084*/ uint8 hairstyle; // +/*085*/ uint8 haircolor; // +/*086*/ uint8 beard; // +/*087*/ uint8 beardcolor; // +/*088*/ float size; // +/*092*/ uint32 drakkin_heritage; // +/*096*/ uint32 drakkin_tattoo; // +/*100*/ uint32 drakkin_details; // +/*104*/ EQ::TintProfile armor_tint; // +/*140*/ uint8 eyecolor1; // Field Not Identified in any Illusion Struct +/*141*/ uint8 eyecolor2; // Field Not Identified in any Illusion Struct +/*142*/ uint8 unknown138[114]; // /*256*/ }; diff --git a/zone/mob.cpp b/zone/mob.cpp index 2d936634e..84014d2ee 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1811,23 +1811,25 @@ void Mob::SendIllusionPacket( new_drakkin_details = (in_drakkin_details == 0xFFFFFFFF) ? GetDrakkinDetails() : in_drakkin_details; new_aa_title = in_aa_title; - // Reset features to Base from the Player Profile - if (IsClient() && in_race == 0) { - race = CastToClient()->GetBaseRace(); - gender = CastToClient()->GetBaseGender(); + bool reset_features_to_player_profile = IsClient() && in_race == 0; + if (reset_features_to_player_profile) { + auto c = CastToClient(); + race = c->GetBaseRace(); + gender = c->GetBaseGender(); new_texture = texture = 0xFF; new_helmtexture = helmtexture = 0xFF; - new_haircolor = haircolor = CastToClient()->GetBaseHairColor(); - new_beardcolor = beardcolor = CastToClient()->GetBaseBeardColor(); - new_eyecolor1 = eyecolor1 = CastToClient()->GetBaseEyeColor(); - new_eyecolor2 = eyecolor2 = CastToClient()->GetBaseEyeColor(); - new_hairstyle = hairstyle = CastToClient()->GetBaseHairStyle(); - new_luclinface = luclinface = CastToClient()->GetBaseFace(); - new_beard = beard = CastToClient()->GetBaseBeard(); + new_haircolor = haircolor = c->GetBaseHairColor(); + new_beardcolor = beardcolor = c->GetBaseBeardColor(); + new_eyecolor1 = eyecolor1 = c->GetBaseEyeColor(); + new_eyecolor2 = eyecolor2 = c->GetBaseEyeColor(); + new_hairstyle = hairstyle = c->GetBaseHairStyle(); + new_luclinface = luclinface = c->GetBaseFace(); + new_beard = beard = c->GetBaseBeard(); new_aa_title = aa_title = 0xFF; - new_drakkin_heritage = drakkin_heritage = CastToClient()->GetBaseHeritage(); - new_drakkin_tattoo = drakkin_tattoo = CastToClient()->GetBaseTattoo(); - new_drakkin_details = drakkin_details = CastToClient()->GetBaseDetails(); + new_drakkin_heritage = drakkin_heritage = c->GetBaseHeritage(); + new_drakkin_tattoo = drakkin_tattoo = c->GetBaseTattoo(); + new_drakkin_details = drakkin_details = c->GetBaseDetails(); + switch (race) { case OGRE: size = 9; @@ -1858,50 +1860,52 @@ void Mob::SendIllusionPacket( } } - // update internal values for mob - // TODO: Move this to its own SetIllusion function later - // size = (in_size <= 0.0f) ? GetSize() : in_size; - // texture = new_texture; - // helmtexture = new_helmtexture; - // haircolor = new_haircolor; - // beardcolor = new_beardcolor; - // eyecolor1 = new_eyecolor1; - // eyecolor2 = new_eyecolor2; - // hairstyle = new_hairstyle; - // luclinface = new_luclinface; - // beard = new_beard; - // drakkin_heritage = new_drakkin_heritage; - // drakkin_tattoo = new_drakkin_tattoo; - // drakkin_details = new_drakkin_details; + // update internal values for mob from illusion + size = (in_size <= 0.0f) ? GetRaceGenderDefaultHeight(race, gender) : in_size; + texture = new_texture; + helmtexture = new_helmtexture; + haircolor = new_haircolor; + beardcolor = new_beardcolor; + eyecolor1 = new_eyecolor1; + eyecolor2 = new_eyecolor2; + hairstyle = new_hairstyle; + luclinface = new_luclinface; + beard = new_beard; + drakkin_heritage = new_drakkin_heritage; + drakkin_tattoo = new_drakkin_tattoo; + drakkin_details = new_drakkin_details; - auto outapp = new EQApplicationPacket(OP_Illusion, sizeof(Illusion_Struct)); - Illusion_Struct *is = (Illusion_Struct *) outapp->pBuffer; - is->spawnid = GetID(); - strcpy(is->charname, GetCleanName()); - is->race = race; - is->gender = gender; - is->texture = new_texture; - is->helmtexture = new_helmtexture; - is->haircolor = new_haircolor; - is->beardcolor = new_beardcolor; - is->beard = new_beard; - is->eyecolor1 = new_eyecolor1; - is->eyecolor2 = new_eyecolor2; - is->hairstyle = new_hairstyle; - is->face = new_luclinface; - is->drakkin_heritage = new_drakkin_heritage; - is->drakkin_tattoo = new_drakkin_tattoo; - is->drakkin_details = new_drakkin_details; - is->size = size; + // send packet + auto outapp = new EQApplicationPacket(OP_Illusion, sizeof(Illusion_Struct)); + auto *i = (Illusion_Struct *) outapp->pBuffer; + i->spawnid = GetID(); + strcpy(i->charname, GetCleanName()); + i->race = race; + i->gender = gender; + i->texture = new_texture; + i->helmtexture = new_helmtexture; + i->haircolor = new_haircolor; + i->beardcolor = new_beardcolor; + i->beard = new_beard; + i->eyecolor1 = new_eyecolor1; + i->eyecolor2 = new_eyecolor2; + i->hairstyle = new_hairstyle; + i->face = new_luclinface; + i->drakkin_heritage = new_drakkin_heritage; + i->drakkin_tattoo = new_drakkin_tattoo; + i->drakkin_details = new_drakkin_details; + i->size = size; entity_list.QueueClients(this, outapp); safe_delete(outapp); - /* Refresh armor and tints after send illusion packet */ + // Refresh armor and tints after send illusion packet SendArmorAppearance(); - LogSpells( - "Illusion: Race [{}] Gender [{}] Texture [{}] HelmTexture [{}] HairColor [{}] BeardColor [{}] EyeColor1 [{}] EyeColor2 [{}] HairStyle [{}] Face [{}] DrakkinHeritage [{}] DrakkinTattoo [{}] DrakkinDetails [{}] Size [{}]", + LogMobAppearance( + "[SendIllusionPacket] race [{}] gender [{}] new_texture [{}] new_helmtexture [{}] new_haircolor [{}] new_beardcolor [{}] " + "new_eyecolor1 [{}] new_eyecolor2 [{}] new_hairstyle [{}] new_luclinface [{}] new_drakkin_heritage [{}] " + "new_drakkin_tattoo [{}] new_drakkin_details [{}] size [{}]", race, gender, new_texture, diff --git a/zone/mob_appearance.cpp b/zone/mob_appearance.cpp index c02d27e11..e0629b83a 100644 --- a/zone/mob_appearance.cpp +++ b/zone/mob_appearance.cpp @@ -378,9 +378,7 @@ void Mob::SendArmorAppearance(Client *one_client) * The other packets work for primary/secondary. */ - Log(Logs::Detail, Logs::MobAppearance, "Mob::SendArmorAppearance [%s]", - this->GetCleanName() - ); + LogMobAppearance("[SendArmorAppearance] [{}]", GetCleanName()); if (IsPlayerRace(race)) { if (!IsClient()) { diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 5fe9b713a..cb2895954 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1319,7 +1319,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #endif if (caster) effect_value = caster->ApplySpellEffectiveness(spell_id, effect_value); - + buffs[buffslot].melee_rune = effect_value; break; } @@ -1476,8 +1476,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove } } - for (int x = EQ::textures::textureBegin; x <= EQ::textures::LastTintableTexture; x++) - SendWearChange(x); + SendArmorAppearance(); if (caster == this && spell.id != 287 && spell.id != 601 && (spellbonuses.IllusionPersistence || aabonuses.IllusionPersistence || @@ -1841,7 +1840,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove Group* group = client_target->GetGroup(); if (!group->IsGroupMember(caster)) { if (caster != this) { - caster->MessageString(Chat::Red, SUMMON_ONLY_GROUP_CORPSE); + caster->MessageString(Chat::Red, SUMMON_ONLY_GROUP_CORPSE); break; } } @@ -1862,7 +1861,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove } } } - + if (client_target) { if (database.CountCharacterCorpses(client_target->CharacterID()) == 0) { if (caster == this) { @@ -2305,7 +2304,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove } case SE_AETaunt: - { + { #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "AE Taunt"); #endif @@ -3878,30 +3877,30 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) case SE_Illusion: { SendIllusionPacket(0, GetBaseGender()); - if(GetRace() == OGRE){ + if (GetRace() == OGRE) { SendAppearancePacket(AT_Size, 9); } - else if(GetRace() == TROLL){ + else if (GetRace() == TROLL) { SendAppearancePacket(AT_Size, 8); } - else if(GetRace() == VAHSHIR || GetRace() == FROGLOK || GetRace() == BARBARIAN){ + else if (GetRace() == VAHSHIR || GetRace() == FROGLOK || GetRace() == BARBARIAN) { SendAppearancePacket(AT_Size, 7); } - else if(GetRace() == HALF_ELF || GetRace() == WOOD_ELF || GetRace() == DARK_ELF){ + else if (GetRace() == HALF_ELF || GetRace() == WOOD_ELF || GetRace() == DARK_ELF) { SendAppearancePacket(AT_Size, 5); } - else if(GetRace() == DWARF){ + else if (GetRace() == DWARF) { SendAppearancePacket(AT_Size, 4); } - else if(GetRace() == HALFLING || GetRace() == GNOME){ + else if (GetRace() == HALFLING || GetRace() == GNOME) { SendAppearancePacket(AT_Size, 3); } - else{ + else { SendAppearancePacket(AT_Size, 6); } - for (int x = EQ::textures::textureBegin; x <= EQ::textures::LastTintableTexture; x++){ - SendWearChange(x); - } + + SendArmorAppearance(); + break; } @@ -4144,7 +4143,7 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) CastToClient()->SetControlledMobId(0); } } - + } } @@ -6788,24 +6787,24 @@ void Mob::ResourceTap(int32 damage, uint16 spellid) for (int i = 0; i < EFFECT_COUNT; i++) { if (spells[spellid].effectid[i] == SE_ResourceTap) { damage = (damage * spells[spellid].base[i]) / 1000; - + if (damage) { if (spells[spellid].max[i] && (damage > spells[spellid].max[i])) damage = spells[spellid].max[i]; - + if (spells[spellid].base2[i] == 0) { // HP Tap if (damage > 0) HealDamage(damage); else Damage(this, -damage, 0, EQ::skills::SkillEvocation, false); } - + if (spells[spellid].base2[i] == 1) // Mana Tap SetMana(GetMana() + damage); - + if (spells[spellid].base2[i] == 2 && IsClient()) // Endurance Tap CastToClient()->SetEndurance(CastToClient()->GetEndurance() + damage); - + } } } @@ -6982,7 +6981,7 @@ void Client::BreakFeignDeathWhenCastOn(bool IsResisted) MessageString(Chat::SpellFailure,FD_CAST_ON_NO_BREAK); return; } - + SetFeigned(false); MessageString(Chat::SpellFailure,FD_CAST_ON); } From 0e4361955d01e5ef81f462e9484b4fcae71ee0f1 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 13 Jun 2021 19:06:36 -0400 Subject: [PATCH 080/624] [Quest API] Add ResetAllDisciplineTimers() to Perl/Lua. (#1395) - Add $client->ResetAllDisciplineTimers() to Perl. - Add client:ResetAllDisciplineTimers() to Lua. --- zone/client.h | 1 + zone/effects.cpp | 16 +++++++++++++--- zone/lua_client.cpp | 8 +++++++- zone/lua_client.h | 1 + zone/perl_client.cpp | 14 ++++++++++++++ 5 files changed, 36 insertions(+), 4 deletions(-) diff --git a/zone/client.h b/zone/client.h index c48ea2237..beaf191b4 100644 --- a/zone/client.h +++ b/zone/client.h @@ -985,6 +985,7 @@ public: bool MemorizeSpellFromItem(uint32 item_id); void TrainDiscBySpellID(int32 spell_id); uint32 GetDisciplineTimer(uint32 timer_id); + void ResetAllDisciplineTimers(); int GetDiscSlotBySpellID(int32 spellid); void ResetDisciplineTimer(uint32 timer_id); void SendDisciplineUpdate(); diff --git a/zone/effects.cpp b/zone/effects.cpp index b68887ad9..17a600634 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -722,7 +722,7 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) { uint32 Client::GetDisciplineTimer(uint32 timer_id) { pTimerType disc_timer_id = pTimerDisciplineReuseStart + timer_id; uint32 disc_timer = 0; - if (GetPTimers().Enabled((uint32)disc_timer_id)) { + if (GetPTimers().Enabled(disc_timer_id)) { disc_timer = GetPTimers().GetRemainingTime(disc_timer_id); } return disc_timer; @@ -730,12 +730,22 @@ uint32 Client::GetDisciplineTimer(uint32 timer_id) { void Client::ResetDisciplineTimer(uint32 timer_id) { pTimerType disc_timer_id = pTimerDisciplineReuseStart + timer_id; - if (GetPTimers().Enabled((uint32)disc_timer_id)) { - GetPTimers().Clear(&database, (uint32)disc_timer_id); + if (GetPTimers().Enabled(disc_timer_id)) { + GetPTimers().Clear(&database, disc_timer_id); } SendDisciplineTimer(timer_id, 0); } +void Client::ResetAllDisciplineTimers() { + for (pTimerType disc_timer_id = pTimerDisciplineReuseStart; disc_timer_id <= pTimerDisciplineReuseEnd; disc_timer_id++) { + uint32 current_timer_id = (disc_timer_id - pTimerDisciplineReuseStart); + if (GetPTimers().Enabled(disc_timer_id)) { + GetPTimers().Clear(&database, disc_timer_id); + } + SendDisciplineTimer(current_timer_id, 0); + } +} + bool Client::HasDisciplineLearned(uint16 spell_id) { bool has_learned = false; for (auto index = 0; index < MAX_PP_DISCIPLINES; ++index) { diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 6d700a689..501b9d5de 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -2098,6 +2098,11 @@ void Lua_Client::SetHideMe(bool hide_me_state) { self->SetHideMe(hide_me_state); } +void Lua_Client::ResetAllDisciplineTimers() { + Lua_Safe_Call_Void(); + self->ResetAllDisciplineTimers(); +} + luabind::scope lua_register_client() { return luabind::class_("Client") .def(luabind::constructor<>()) @@ -2453,7 +2458,8 @@ luabind::scope lua_register_client() { .def("SetEXPModifier", (void(Lua_Client::*)(uint32,double))&Lua_Client::SetEXPModifier) .def("AddLDoNLoss", (void(Lua_Client::*)(uint32))&Lua_Client::AddLDoNLoss) .def("AddLDoNWin", (void(Lua_Client::*)(uint32))&Lua_Client::AddLDoNWin) - .def("SetHideMe", (void(Lua_Client::*)(bool))&Lua_Client::SetHideMe); + .def("SetHideMe", (void(Lua_Client::*)(bool))&Lua_Client::SetHideMe) + .def("ResetAllDisciplineTimers", (void(Lua_Client::*)(void))&Lua_Client::ResetAllDisciplineTimers); } luabind::scope lua_register_inventory_where() { diff --git a/zone/lua_client.h b/zone/lua_client.h index 60c11fbbb..28465d57a 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -230,6 +230,7 @@ public: void ResetTrade(); uint32 GetDisciplineTimer(uint32 timer_id); void ResetDisciplineTimer(uint32 timer_id); + void ResetAllDisciplineTimers(); bool UseDiscipline(int spell_id, int target_id); bool HasDisciplineLearned(uint16 spell_id); int GetCharacterFactionLevel(int faction_id); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 60efbf787..034611fb4 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -5332,6 +5332,19 @@ XS(XS_Client_SetHideMe) { XSRETURN_EMPTY; } +XS(XS_Client_ResetAllDisciplineTimers); +XS(XS_Client_ResetAllDisciplineTimers) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::ResetAllDisciplineTimers(THIS)"); // @categories Spells and Disciplines + { + Client *THIS; + VALIDATE_THIS_IS_CLIENT; + THIS->ResetAllDisciplineTimers(); + } + XSRETURN_EMPTY; +} + #ifdef __cplusplus extern "C" #endif @@ -5552,6 +5565,7 @@ XS(boot_Client) { newXSproto(strcpy(buf, "RemoveExpeditionLockout"), XS_Client_RemoveExpeditionLockout, file, "$$$"); newXSproto(strcpy(buf, "RemoveNoRent"), XS_Client_RemoveNoRent, file, "$"); newXSproto(strcpy(buf, "ResetAA"), XS_Client_ResetAA, file, "$"); + newXSproto(strcpy(buf, "ResetAllDisciplineTimers"), XS_Client_ResetAllDisciplineTimers, file, "$"); newXSproto(strcpy(buf, "ResetDisciplineTimer"), XS_Client_ResetDisciplineTimer, file, "$$"); newXSproto(strcpy(buf, "ResetTrade"), XS_Client_ResetTrade, file, "$"); newXSproto(strcpy(buf, "Save"), XS_Client_Save, file, "$$"); From bcb0e43d13bed7c31752739a6a1cdcb9e30826b0 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 13 Jun 2021 18:06:43 -0500 Subject: [PATCH 081/624] [Logging] Simplify Log Settings Initialization (#1394) * Simplify logging loading * Fix log injections and reduce verbosity --- client_files/export/main.cpp | 5 +- client_files/import/main.cpp | 5 +- common/database.cpp | 89 -------------- common/database.h | 3 +- common/eqemu_logsys.cpp | 80 +++++++++++- common/eqemu_logsys.h | 114 ++++++------------ .../base/base_logsys_categories_repository.h | 2 +- loginserver/database.cpp | 91 +------------- loginserver/database.h | 5 - loginserver/main.cpp | 3 +- queryserv/database.cpp | 86 ------------- queryserv/database.h | 2 - queryserv/queryserv.cpp | 26 ++-- shared_memory/main.cpp | 6 +- ucs/database.cpp | 86 ------------- ucs/database.h | 3 +- ucs/ucs.cpp | 6 +- world/main.cpp | 8 +- world/zoneserver.cpp | 6 +- zone/main.cpp | 7 +- zone/worldserver.cpp | 2 +- 21 files changed, 154 insertions(+), 481 deletions(-) diff --git a/client_files/export/main.cpp b/client_files/export/main.cpp index fd8b19026..ff7f1f3dd 100644 --- a/client_files/export/main.cpp +++ b/client_files/export/main.cpp @@ -83,8 +83,9 @@ int main(int argc, char **argv) content_db.SetMysql(database.getMySQL()); } - database.LoadLogSettings(LogSys.log_settings); - LogSys.StartFileLogs(); + LogSys.SetDatabase(&database) + ->LoadLogDatabaseSettings() + ->StartFileLogs(); std::string arg_1; diff --git a/client_files/import/main.cpp b/client_files/import/main.cpp index 64b2aee41..2fbdf421a 100644 --- a/client_files/import/main.cpp +++ b/client_files/import/main.cpp @@ -80,8 +80,9 @@ int main(int argc, char **argv) { content_db.SetMysql(database.getMySQL()); } - database.LoadLogSettings(LogSys.log_settings); - LogSys.StartFileLogs(); + LogSys.SetDatabase(&database) + ->LoadLogDatabaseSettings() + ->StartFileLogs(); ImportSpells(&content_db); ImportSkillCaps(&content_db); diff --git a/common/database.cpp b/common/database.cpp index 1290d289a..cf9752350 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -2191,95 +2191,6 @@ uint32 Database::GetRaidIDByCharID(uint32 character_id) { return 0; } -/** - * @param log_settings - */ -void Database::LoadLogSettings(EQEmuLogSys::LogSettings *log_settings) -{ - std::string query = - "SELECT " - "log_category_id, " - "log_category_description, " - "log_to_console, " - "log_to_file, " - "log_to_gmsay " - "FROM " - "logsys_categories " - "ORDER BY log_category_id"; - - auto results = QueryDatabase(query); - int log_category_id = 0; - - int *categories_in_database = new int[1000]; - - for (auto row = results.begin(); row != results.end(); ++row) { - log_category_id = atoi(row[0]); - if (log_category_id <= Logs::None || log_category_id >= Logs::MaxCategoryID) { - continue; - } - - log_settings[log_category_id].log_to_console = static_cast(atoi(row[2])); - log_settings[log_category_id].log_to_file = static_cast(atoi(row[3])); - log_settings[log_category_id].log_to_gmsay = static_cast(atoi(row[4])); - - /** - * Determine if any output method is enabled for the category - * and set it to 1 so it can used to check if category is enabled - */ - const bool log_to_console = log_settings[log_category_id].log_to_console > 0; - const bool log_to_file = log_settings[log_category_id].log_to_file > 0; - const bool log_to_gmsay = log_settings[log_category_id].log_to_gmsay > 0; - const bool is_category_enabled = log_to_console || log_to_file || log_to_gmsay; - - if (is_category_enabled) { - log_settings[log_category_id].is_category_enabled = 1; - } - - /** - * This determines whether or not the process needs to actually file log anything. - * If we go through this whole loop and nothing is set to any debug level, there is no point to create a file or keep anything open - */ - if (log_settings[log_category_id].log_to_file > 0) { - LogSys.file_logs_enabled = true; - } - - categories_in_database[log_category_id] = 1; - } - - /** - * Auto inject categories that don't exist in the database... - */ - for (int log_index = Logs::AA; log_index != Logs::MaxCategoryID; log_index++) { - if (categories_in_database[log_index] != 1) { - - LogInfo( - "New Log Category [{0}] doesn't exist... Automatically adding to [logsys_categories] table...", - Logs::LogCategoryName[log_index] - ); - - auto inject_query = fmt::format( - "INSERT INTO logsys_categories " - "(log_category_id, " - "log_category_description, " - "log_to_console, " - "log_to_file, " - "log_to_gmsay) " - "VALUES " - "({0}, '{1}', {2}, {3}, {4})", - log_index, - EscapeString(Logs::LogCategoryName[log_index]), - std::to_string(log_settings[log_index].log_to_console), - std::to_string(log_settings[log_index].log_to_file), - std::to_string(log_settings[log_index].log_to_gmsay) - ); - - QueryDatabase(inject_query); - } - } - - delete[] categories_in_database; -} - int Database::CountInvSnapshots() { std::string query = StringFormat("SELECT COUNT(*) FROM (SELECT * FROM `inventory_snapshots` a GROUP BY `charid`, `time_index`) b"); auto results = QueryDatabase(query); diff --git a/common/database.h b/common/database.h index 1b3b736ae..79c52c514 100644 --- a/common/database.h +++ b/common/database.h @@ -78,6 +78,7 @@ class PTimerList; #define SQL(...) #__VA_ARGS__ +class LogSettings; class Database : public DBcore { public: Database(); @@ -268,8 +269,6 @@ public: int CountInvSnapshots(); void ClearInvSnapshots(bool from_now = false); - /* EQEmuLogSys */ - void LoadLogSettings(EQEmuLogSys::LogSettings* log_settings); private: diff --git a/common/eqemu_logsys.cpp b/common/eqemu_logsys.cpp index ad43ee61d..950c17107 100644 --- a/common/eqemu_logsys.cpp +++ b/common/eqemu_logsys.cpp @@ -22,8 +22,8 @@ #include "rulesys.h" #include "platform.h" #include "string_util.h" -#include "database.h" #include "misc.h" +#include "repositories/logsys_categories_repository.h" #include #include @@ -31,6 +31,7 @@ #include #include #include +#include std::ofstream process_log; @@ -96,7 +97,7 @@ EQEmuLogSys::EQEmuLogSys() */ EQEmuLogSys::~EQEmuLogSys() = default; -void EQEmuLogSys::LoadLogSettingsDefaults() +EQEmuLogSys *EQEmuLogSys::LoadLogSettingsDefaults() { /** * Get Executable platform currently running this code (Zone/World/etc) @@ -177,6 +178,8 @@ void EQEmuLogSys::LoadLogSettingsDefaults() else if (EQEmuLogSys::log_platform == EQEmuExePlatform::ExePlatformHC) { platform_file_name = "hc"; } + + return this; } /** @@ -601,3 +604,76 @@ void EQEmuLogSys::EnableConsoleLogging() log_settings[log_index].is_category_enabled = 1; } } + +EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings() +{ + auto categories = LogsysCategoriesRepository::GetWhere( + *m_database, + "TRUE ORDER BY log_category_id" + ); + + // keep track of categories + std::vector db_categories{}; + db_categories.reserve(categories.size()); + + // loop through database categories + for (auto &c: categories) { + if (c.log_category_id <= Logs::None || c.log_category_id >= Logs::MaxCategoryID) { + continue; + } + + log_settings[c.log_category_id].log_to_console = static_cast(c.log_to_console); + log_settings[c.log_category_id].log_to_file = static_cast(c.log_to_file); + log_settings[c.log_category_id].log_to_gmsay = static_cast(c.log_to_gmsay); + + // Determine if any output method is enabled for the category + // and set it to 1 so it can used to check if category is enabled + const bool log_to_console = log_settings[c.log_category_id].log_to_console > 0; + const bool log_to_file = log_settings[c.log_category_id].log_to_file > 0; + const bool log_to_gmsay = log_settings[c.log_category_id].log_to_gmsay > 0; + const bool is_category_enabled = log_to_console || log_to_file || log_to_gmsay; + + if (is_category_enabled) { + log_settings[c.log_category_id].is_category_enabled = 1; + } + + // This determines whether or not the process needs to actually file log anything. + // If we go through this whole loop and nothing is set to any debug level, there + // is no point to create a file or keep anything open + if (log_settings[c.log_category_id].log_to_file > 0) { + LogSys.file_logs_enabled = true; + } + + db_categories.emplace_back(c.log_category_id); + } + + // Auto inject categories that don't exist in the database... + for (int i = Logs::AA; i != Logs::MaxCategoryID; i++) { + if (std::find(db_categories.begin(), db_categories.end(), i) == db_categories.end()) { + LogInfo( + "Automatically adding new log category [{0}]", + Logs::LogCategoryName[i] + ); + + auto new_category = LogsysCategoriesRepository::NewEntity(); + new_category.log_category_id = i; + new_category.log_category_description = EscapeString(Logs::LogCategoryName[i]); + new_category.log_to_console = log_settings[i].log_to_console; + new_category.log_to_gmsay = log_settings[i].log_to_gmsay; + new_category.log_to_file = log_settings[i].log_to_file; + + LogsysCategoriesRepository::InsertOne(*m_database, new_category); + } + } + + LogInfo("Loaded [{}] log categories", categories.size()); + + return this; +} + +EQEmuLogSys *EQEmuLogSys::SetDatabase(Database *db) +{ + m_database = db; + + return this; +} diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 0b33b55a3..4db6cedb2 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -23,8 +23,9 @@ #include #include -#include +#include #include +#include #ifdef _WIN32 #ifdef utf16_to_utf8 @@ -37,9 +38,9 @@ namespace Logs { enum DebugLevel { - General = 1, /* 1 - Low-Level general debugging, useful info on single line */ - Moderate, /* 2 - Informational based, used in functions, when particular things load */ - Detail /* 3 - Use this for extreme detail in logging, usually in extreme debugging in the stack or interprocess communication */ + General = 1, // 1 - Low-Level general debugging, useful info on single line + Moderate, // 2 - Informational based, used in functions, when particular things load + Detail // 3 - Use this for extreme detail in logging, usually in extreme debugging in the stack or interprocess communication }; /** @@ -127,7 +128,7 @@ namespace Logs { /** * If you add to this, make sure you update LogCategory */ - static const char* LogCategoryName[LogCategory::MaxCategoryID] = { + static const char *LogCategoryName[LogCategory::MaxCategoryID] = { "", "AA", "AI", @@ -206,6 +207,8 @@ namespace Logs { #include "eqemu_logsys_log_aliases.h" +class Database; + class EQEmuLogSys { public: EQEmuLogSys(); @@ -216,7 +219,8 @@ public: * This should be handled on deconstructor but to be safe we use it anyways. */ void CloseFileLogs(); - void LoadLogSettingsDefaults(); + EQEmuLogSys *LoadLogSettingsDefaults(); + EQEmuLogSys *LoadLogDatabaseSettings(); /** * @param directory_name @@ -246,7 +250,7 @@ public: * Used in file logs to prepend a timestamp entry for logs * @param time_stamp */ - void SetCurrentTimeStamp(char* time_stamp); + void SetCurrentTimeStamp(char *time_stamp); /** * @param log_name @@ -273,101 +277,53 @@ public: /** * Internally used memory reference for all log settings per category * These are loaded via DB and have defaults loaded in LoadLogSettingsDefaults - * Database loaded via Database::LoadLogSettings(log_settings) + * Database loaded via LogSys.SetDatabase(&database)->LoadLogDatabaseSettings(); */ LogSettings log_settings[Logs::LogCategory::MaxCategoryID]{}; bool file_logs_enabled = false; - /** - * Sets Executable platform (Zone/World/UCS) etc. - */ - int log_platform = 0; + int log_platform = 0; + std::string platform_file_name; - /** - * File name used in writing logs - */ - std::string platform_file_name; - /** - * GMSay Client Message colors mapped by category - * - * @param log_category - * @return - */ + // gmsay uint16 GetGMSayColorFromCategory(uint16 log_category); - /** - * @param f - */ - void SetGMSayHandler(std::function f) { on_log_gmsay_hook = f; } + EQEmuLogSys * SetGMSayHandler(std::function f) { + on_log_gmsay_hook = f; + return this; + } - /** - * @param f - */ - void SetConsoleHandler(std::function f) { on_log_console_hook = f; } - - /** - * Silence console logging - */ + // console + void SetConsoleHandler( + std::function f + ) { on_log_console_hook = f; } void SilenceConsoleLogging(); - - /** - * Turn on all console logging - */ void EnableConsoleLogging(); + // database + EQEmuLogSys *SetDatabase(Database *db); + private: - /** - * Callback pointer to zone process for hooking logs to zone using GMSay - */ - std::function on_log_gmsay_hook; - std::function on_log_console_hook; + // reference to database + Database *m_database; + + std::function on_log_gmsay_hook; + std::function on_log_console_hook; - /** - * Formats log messages like '[Category] This is a log message' - */ std::string FormatOutMessageString(uint16 log_category, const std::string &in_message); - - /** - * Linux console color messages mapped by category - * - * @param log_category - * @return - */ std::string GetLinuxConsoleColorFromCategory(uint16 log_category); - - /** - * Windows console color messages mapped by category - */ uint16 GetWindowsConsoleColorFromCategory(uint16 log_category); - /** - * @param debug_level - * @param log_category - * @param message - */ void ProcessConsoleMessage(uint16 debug_level, uint16 log_category, const std::string &message); - - /** - * @param debug_level - * @param log_category - * @param message - */ void ProcessGMSay(uint16 debug_level, uint16 log_category, const std::string &message); - - /** - * @param debug_level - * @param log_category - * @param message - */ void ProcessLogWrite(uint16 debug_level, uint16 log_category, const std::string &message); - - /** - * @param log_category - * @return - */ bool IsRfc5424LogCategory(uint16 log_category); }; diff --git a/common/repositories/base/base_logsys_categories_repository.h b/common/repositories/base/base_logsys_categories_repository.h index 4077e32f7..fd15aec24 100644 --- a/common/repositories/base/base_logsys_categories_repository.h +++ b/common/repositories/base/base_logsys_categories_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/loginserver/database.cpp b/loginserver/database.cpp index 35f49e0eb..8bf481d20 100644 --- a/loginserver/database.cpp +++ b/loginserver/database.cpp @@ -598,95 +598,6 @@ MySQLRequestResult Database::GetLoginserverApiTokens() return QueryDatabase("SELECT token, can_write, can_read FROM login_api_tokens"); } -/** - * @param log_settings - */ -void Database::LoadLogSettings(EQEmuLogSys::LogSettings *log_settings) -{ - std::string query = - "SELECT " - "log_category_id, " - "log_category_description, " - "log_to_console, " - "log_to_file, " - "log_to_gmsay " - "FROM " - "logsys_categories " - "ORDER BY log_category_id"; - - auto results = QueryDatabase(query); - int log_category_id = 0; - - int *categories_in_database = new int[1000]; - - for (auto row = results.begin(); row != results.end(); ++row) { - log_category_id = atoi(row[0]); - if (log_category_id <= Logs::None || log_category_id >= Logs::MaxCategoryID) { - continue; - } - - log_settings[log_category_id].log_to_console = static_cast(atoi(row[2])); - log_settings[log_category_id].log_to_file = static_cast(atoi(row[3])); - log_settings[log_category_id].log_to_gmsay = static_cast(atoi(row[4])); - - /** - * Determine if any output method is enabled for the category - * and set it to 1 so it can used to check if category is enabled - */ - const bool log_to_console = log_settings[log_category_id].log_to_console > 0; - const bool log_to_file = log_settings[log_category_id].log_to_file > 0; - const bool log_to_gmsay = log_settings[log_category_id].log_to_gmsay > 0; - const bool is_category_enabled = log_to_console || log_to_file || log_to_gmsay; - - if (is_category_enabled) { - log_settings[log_category_id].is_category_enabled = 1; - } - - /** - * This determines whether or not the process needs to actually file log anything. - * If we go through this whole loop and nothing is set to any debug level, there is no point to create a file or keep anything open - */ - if (log_settings[log_category_id].log_to_file > 0) { - LogSys.file_logs_enabled = true; - } - - categories_in_database[log_category_id] = 1; - } - - /** - * Auto inject categories that don't exist in the database... - */ - for (int log_index = Logs::AA; log_index != Logs::MaxCategoryID; log_index++) { - if (categories_in_database[log_index] != 1) { - - LogInfo( - "New Log Category [{0}] doesn't exist... Automatically adding to [logsys_categories] table...", - Logs::LogCategoryName[log_index] - ); - - auto inject_query = fmt::format( - "INSERT INTO logsys_categories " - "(log_category_id, " - "log_category_description, " - "log_to_console, " - "log_to_file, " - "log_to_gmsay) " - "VALUES " - "({0}, '{1}', {2}, {3}, {4})", - log_index, - EscapeString(Logs::LogCategoryName[log_index]), - std::to_string(log_settings[log_index].log_to_console), - std::to_string(log_settings[log_index].log_to_file), - std::to_string(log_settings[log_index].log_to_gmsay) - ); - - QueryDatabase(inject_query); - } - } - - delete[] categories_in_database; -} - /** * @param account_name * @param account_password @@ -806,4 +717,4 @@ Database::DbLoginServerAccount Database::GetLoginServerAccountByAccountName( } return login_server_account; -} \ No newline at end of file +} diff --git a/loginserver/database.h b/loginserver/database.h index 8ffcb187b..ecb809ee9 100644 --- a/loginserver/database.h +++ b/loginserver/database.h @@ -201,11 +201,6 @@ public: unsigned int &server_admin_id ); - /** - * @param log_settings - */ - void LoadLogSettings(EQEmuLogSys::LogSettings *log_settings); - /** * @param write_mode * @param read_mode diff --git a/loginserver/main.cpp b/loginserver/main.cpp index 9e6006342..d1f001957 100644 --- a/loginserver/main.cpp +++ b/loginserver/main.cpp @@ -165,8 +165,7 @@ int main(int argc, char **argv) LoadDatabaseConnection(); if (argc == 1) { - server.db->LoadLogSettings(LogSys.log_settings); - LogSys.StartFileLogs(); + LogSys.LoadLogDatabaseSettings()->StartFileLogs(); } /** diff --git a/queryserv/database.cpp b/queryserv/database.cpp index 20465e6b5..8f2de678d 100644 --- a/queryserv/database.cpp +++ b/queryserv/database.cpp @@ -448,89 +448,3 @@ void Database::GeneralQueryReceive(ServerPacket *pack) safe_delete_array(queryBuffer); } - -void Database::LoadLogSettings(EQEmuLogSys::LogSettings *log_settings) -{ - std::string query = - "SELECT " - "log_category_id, " - "log_category_description, " - "log_to_console, " - "log_to_file, " - "log_to_gmsay " - "FROM " - "logsys_categories " - "ORDER BY log_category_id"; - - auto results = QueryDatabase(query); - int log_category_id = 0; - - int *categories_in_database = new int[1000]; - - for (auto row = results.begin(); row != results.end(); ++row) { - log_category_id = atoi(row[0]); - if (log_category_id <= Logs::None || log_category_id >= Logs::MaxCategoryID) { - continue; - } - - log_settings[log_category_id].log_to_console = static_cast(atoi(row[2])); - log_settings[log_category_id].log_to_file = static_cast(atoi(row[3])); - log_settings[log_category_id].log_to_gmsay = static_cast(atoi(row[4])); - - /** - * Determine if any output method is enabled for the category - * and set it to 1 so it can used to check if category is enabled - */ - const bool log_to_console = log_settings[log_category_id].log_to_console > 0; - const bool log_to_file = log_settings[log_category_id].log_to_file > 0; - const bool log_to_gmsay = log_settings[log_category_id].log_to_gmsay > 0; - const bool is_category_enabled = log_to_console || log_to_file || log_to_gmsay; - - if (is_category_enabled) { - log_settings[log_category_id].is_category_enabled = 1; - } - - /** - * This determines whether or not the process needs to actually file log anything. - * If we go through this whole loop and nothing is set to any debug level, there is no point to create a file or keep anything open - */ - if (log_settings[log_category_id].log_to_file > 0) { - LogSys.file_logs_enabled = true; - } - - categories_in_database[log_category_id] = 1; - } - - /** - * Auto inject categories that don't exist in the database... - */ - for (int log_index = Logs::AA; log_index != Logs::MaxCategoryID; log_index++) { - if (categories_in_database[log_index] != 1) { - - LogInfo( - "New Log Category [{0}] doesn't exist... Automatically adding to [logsys_categories] table...", - Logs::LogCategoryName[log_index] - ); - - auto inject_query = fmt::format( - "INSERT INTO logsys_categories " - "(log_category_id, " - "log_category_description, " - "log_to_console, " - "log_to_file, " - "log_to_gmsay) " - "VALUES " - "({0}, '{1}', {2}, {3}, {4})", - log_index, - EscapeString(Logs::LogCategoryName[log_index]), - std::to_string(log_settings[log_index].log_to_console), - std::to_string(log_settings[log_index].log_to_file), - std::to_string(log_settings[log_index].log_to_gmsay) - ); - - QueryDatabase(inject_query); - } - } - - delete[] categories_in_database; -} \ No newline at end of file diff --git a/queryserv/database.h b/queryserv/database.h index 13815c575..9aa6383a3 100644 --- a/queryserv/database.h +++ b/queryserv/database.h @@ -53,8 +53,6 @@ public: void LogMerchantTransaction(QSMerchantLogTransaction_Struct* QS, uint32 Items); void GeneralQueryReceive(ServerPacket *pack); - void LoadLogSettings(EQEmuLogSys::LogSettings* log_settings); - protected: void HandleMysqlError(uint32 errnum); private: diff --git a/queryserv/queryserv.cpp b/queryserv/queryserv.cpp index 67a371a8e..005d54c6a 100644 --- a/queryserv/queryserv.cpp +++ b/queryserv/queryserv.cpp @@ -42,15 +42,15 @@ const queryservconfig *Config; WorldServer *worldserver = 0; EQEmuLogSys LogSys; -void CatchSignal(int sig_num) { - RunLoops = false; +void CatchSignal(int sig_num) { + RunLoops = false; } int main() { RegisterExecutablePlatform(ExePlatformQueryServ); LogSys.LoadLogSettingsDefaults(); - set_exception_handler(); - Timer LFGuildExpireTimer(60000); + set_exception_handler(); + Timer LFGuildExpireTimer(60000); LogInfo("Starting EQEmu QueryServ"); if (!queryservconfig::LoadConfig()) { @@ -58,11 +58,11 @@ int main() { return 1; } - Config = queryservconfig::get(); - WorldShortName = Config->ShortName; + Config = queryservconfig::get(); + WorldShortName = Config->ShortName; LogInfo("Connecting to MySQL"); - + /* MySQL Connection */ if (!database.Connect( Config->QSDatabaseHost.c_str(), @@ -74,9 +74,9 @@ int main() { return 1; } - /* Register Log System and Settings */ - database.LoadLogSettings(LogSys.log_settings); - LogSys.StartFileLogs(); + LogSys.SetDatabase(&database) + ->LoadLogDatabaseSettings() + ->StartFileLogs(); if (signal(SIGINT, CatchSignal) == SIG_ERR) { LogInfo("Could not set signal handler"); @@ -89,13 +89,13 @@ int main() { /* Initial Connection to Worldserver */ worldserver = new WorldServer; - worldserver->Connect(); + worldserver->Connect(); /* Load Looking For Guild Manager */ lfguildmanager.LoadDatabase(); - while(RunLoops) { - Timer::SetCurrentTime(); + while(RunLoops) { + Timer::SetCurrentTime(); if(LFGuildExpireTimer.Check()) lfguildmanager.ExpireEntries(); diff --git a/shared_memory/main.cpp b/shared_memory/main.cpp index 7cf54f946..57416aac5 100644 --- a/shared_memory/main.cpp +++ b/shared_memory/main.cpp @@ -120,9 +120,9 @@ int main(int argc, char **argv) content_db.SetMysql(database.getMySQL()); } - /* Register Log System and Settings */ - database.LoadLogSettings(LogSys.log_settings); - LogSys.StartFileLogs(); + LogSys.SetDatabase(&database) + ->LoadLogDatabaseSettings() + ->StartFileLogs(); std::string shared_mem_directory = Config->SharedMemDir; if (MakeDirectory(shared_mem_directory)) { diff --git a/ucs/database.cpp b/ucs/database.cpp index 108b17871..d3e01b54d 100644 --- a/ucs/database.cpp +++ b/ucs/database.cpp @@ -673,89 +673,3 @@ void Database::GetFriendsAndIgnore(int charID, std::vector &friends } } - -void Database::LoadLogSettings(EQEmuLogSys::LogSettings *log_settings) -{ - std::string query = - "SELECT " - "log_category_id, " - "log_category_description, " - "log_to_console, " - "log_to_file, " - "log_to_gmsay " - "FROM " - "logsys_categories " - "ORDER BY log_category_id"; - - auto results = QueryDatabase(query); - int log_category_id = 0; - - int *categories_in_database = new int[1000]; - - for (auto row = results.begin(); row != results.end(); ++row) { - log_category_id = atoi(row[0]); - if (log_category_id <= Logs::None || log_category_id >= Logs::MaxCategoryID) { - continue; - } - - log_settings[log_category_id].log_to_console = static_cast(atoi(row[2])); - log_settings[log_category_id].log_to_file = static_cast(atoi(row[3])); - log_settings[log_category_id].log_to_gmsay = static_cast(atoi(row[4])); - - /** - * Determine if any output method is enabled for the category - * and set it to 1 so it can used to check if category is enabled - */ - const bool log_to_console = log_settings[log_category_id].log_to_console > 0; - const bool log_to_file = log_settings[log_category_id].log_to_file > 0; - const bool log_to_gmsay = log_settings[log_category_id].log_to_gmsay > 0; - const bool is_category_enabled = log_to_console || log_to_file || log_to_gmsay; - - if (is_category_enabled) { - log_settings[log_category_id].is_category_enabled = 1; - } - - /** - * This determines whether or not the process needs to actually file log anything. - * If we go through this whole loop and nothing is set to any debug level, there is no point to create a file or keep anything open - */ - if (log_settings[log_category_id].log_to_file > 0) { - LogSys.file_logs_enabled = true; - } - - categories_in_database[log_category_id] = 1; - } - - /** - * Auto inject categories that don't exist in the database... - */ - for (int log_index = Logs::AA; log_index != Logs::MaxCategoryID; log_index++) { - if (categories_in_database[log_index] != 1) { - - LogInfo( - "New Log Category [{0}] doesn't exist... Automatically adding to [logsys_categories] table...", - Logs::LogCategoryName[log_index] - ); - - auto inject_query = fmt::format( - "INSERT INTO logsys_categories " - "(log_category_id, " - "log_category_description, " - "log_to_console, " - "log_to_file, " - "log_to_gmsay) " - "VALUES " - "({0}, '{1}', {2}, {3}, {4})", - log_index, - EscapeString(Logs::LogCategoryName[log_index]), - std::to_string(log_settings[log_index].log_to_console), - std::to_string(log_settings[log_index].log_to_file), - std::to_string(log_settings[log_index].log_to_gmsay) - ); - - QueryDatabase(inject_query); - } - } - - delete[] categories_in_database; -} diff --git a/ucs/database.h b/ucs/database.h index ade93ef42..fc9b96d19 100644 --- a/ucs/database.h +++ b/ucs/database.h @@ -57,8 +57,7 @@ public: void ExpireMail(); void AddFriendOrIgnore(int CharID, int Type, std::string Name); void RemoveFriendOrIgnore(int CharID, int Type, std::string Name); - void GetFriendsAndIgnore(int CharID, std::vector &Friends, std::vector &Ignorees); - void LoadLogSettings(EQEmuLogSys::LogSettings* log_settings); + void GetFriendsAndIgnore(int CharID, std::vector &Friends, std::vector &Ignorees); protected: diff --git a/ucs/ucs.cpp b/ucs/ucs.cpp index 11f6ed103..5914b4fcc 100644 --- a/ucs/ucs.cpp +++ b/ucs/ucs.cpp @@ -97,9 +97,9 @@ int main() { return 1; } - /* Register Log System and Settings */ - database.LoadLogSettings(LogSys.log_settings); - LogSys.StartFileLogs(); + LogSys.SetDatabase(&database) + ->LoadLogDatabaseSettings() + ->StartFileLogs(); char tmp[64]; diff --git a/world/main.cpp b/world/main.cpp index f150db0e4..2aaafca16 100644 --- a/world/main.cpp +++ b/world/main.cpp @@ -297,11 +297,9 @@ int main(int argc, char** argv) { guild_mgr.SetDatabase(&database); - /** - * Logging - */ - database.LoadLogSettings(LogSys.log_settings); - LogSys.StartFileLogs(); + LogSys.SetDatabase(&database) + ->LoadLogDatabaseSettings() + ->StartFileLogs(); /** * Parse simple CLI passes diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index c350de44d..c53b474af 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -812,7 +812,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { } case ServerOP_ReloadLogs: { zoneserver_list.SendPacket(pack); - database.LoadLogSettings(LogSys.log_settings); + LogSys.LoadLogDatabaseSettings(); break; } case ServerOP_ReloadRules: { @@ -1289,11 +1289,11 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { case ServerOP_CZTaskDisablePlayer: case ServerOP_CZTaskDisableGroup: case ServerOP_CZTaskDisableRaid: - case ServerOP_CZTaskDisableGuild: + case ServerOP_CZTaskDisableGuild: case ServerOP_CZTaskEnablePlayer: case ServerOP_CZTaskEnableGroup: case ServerOP_CZTaskEnableRaid: - case ServerOP_CZTaskEnableGuild: + case ServerOP_CZTaskEnableGuild: case ServerOP_CZTaskFailPlayer: case ServerOP_CZTaskFailGroup: case ServerOP_CZTaskFailRaid: diff --git a/zone/main.cpp b/zone/main.cpp index b81a41f5b..a152e9945 100644 --- a/zone/main.cpp +++ b/zone/main.cpp @@ -254,9 +254,10 @@ int main(int argc, char** argv) { } /* Register Log System and Settings */ - LogSys.SetGMSayHandler(&Zone::GMSayHookCallBackProcess); - database.LoadLogSettings(LogSys.log_settings); - LogSys.StartFileLogs(); + LogSys.SetDatabase(&database) + ->LoadLogDatabaseSettings() + ->SetGMSayHandler(&Zone::GMSayHookCallBackProcess) + ->StartFileLogs(); /* Guilds */ guild_mgr.SetDatabase(&database); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index beef1c8f8..5a5595f6f 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1833,7 +1833,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) break; } case ServerOP_ReloadLogs: { - database.LoadLogSettings(LogSys.log_settings); + LogSys.LoadLogDatabaseSettings(); break; } case ServerOP_ReloadPerlExportSettings: { From 45eea666a143145c4bcdc51aeaf58cb9b67bf96b Mon Sep 17 00:00:00 2001 From: Kurt Gilpin Date: Sun, 13 Jun 2021 18:06:58 -0500 Subject: [PATCH 082/624] [Items] Allow any bag type 51 to be used for Trader (#1392) * Allow any bag type 51 to be used for Trader Most commonly this would allow the different color satchels to be used in Trader mode. PEQ database has 1 item (Yellow Trader's Satchel Token - 35037) marked as type 51, but otherwise only the proper bags are already set. Bonus of removing the hard-coded ID from source. * Updated Fixed where I missed it in a couple more spots too. * Update bonuses.cpp --- zone/bonuses.cpp | 6 +++--- zone/trading.cpp | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 19ad4a45e..e6c99f154 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -3382,10 +3382,10 @@ bool Client::CalcItemScale(uint32 slot_x, uint32 slot_y) { if (Trader) if (i >= EQ::invbag::GENERAL_BAGS_BEGIN && i <= EQ::invbag::GENERAL_BAGS_END) { EQ::ItemInstance* parent_item = m_inv.GetItem(EQ::InventoryProfile::CalcSlotId(i)); - if (parent_item && parent_item->GetItem()->ID == 17899) // trader satchel + if (parent_item && parent_item->GetItem()->BagType == EQ::item::BagTypeTradersSatchel) continue; } - + bool update_slot = false; if(inst->IsScaling()) { @@ -3468,7 +3468,7 @@ bool Client::DoItemEnterZone(uint32 slot_x, uint32 slot_y) { if (Trader) if (i >= EQ::invbag::GENERAL_BAGS_BEGIN && i <= EQ::invbag::GENERAL_BAGS_END) { EQ::ItemInstance* parent_item = m_inv.GetItem(EQ::InventoryProfile::CalcSlotId(i)); - if (parent_item && parent_item->GetItem()->ID == 17899) // trader satchel + if (parent_item && parent_item->GetItem()->BagType == EQ::item::BagTypeTradersSatchel) continue; } diff --git a/zone/trading.cpp b/zone/trading.cpp index 1d75bd941..92b132477 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -1266,7 +1266,7 @@ uint32 Client::FindTraderItemSerialNumber(int32 ItemID) { uint16 SlotID = 0; for (int i = EQ::invslot::GENERAL_BEGIN; i <= EQ::invslot::GENERAL_END; i++){ item = this->GetInv().GetItem(i); - if (item && item->GetItem()->ID == 17899){ //Traders Satchel + if (item && item->GetItem()->BagType == EQ::item::BagTypeTradersSatchel){ for (int x = EQ::invbag::SLOT_BEGIN; x <= EQ::invbag::SLOT_END; x++) { // we already have the parent bag and a contents iterator..why not just iterate the bag!?? SlotID = EQ::InventoryProfile::CalcSlotId(i, x); @@ -1289,7 +1289,7 @@ EQ::ItemInstance* Client::FindTraderItemBySerialNumber(int32 SerialNumber){ uint16 SlotID = 0; for (int i = EQ::invslot::GENERAL_BEGIN; i <= EQ::invslot::GENERAL_END; i++){ item = this->GetInv().GetItem(i); - if(item && item->GetItem()->ID == 17899){ //Traders Satchel + if (item && item->GetItem()->BagType == EQ::item::BagTypeTradersSatchel){ for (int x = EQ::invbag::SLOT_BEGIN; x <= EQ::invbag::SLOT_END; x++) { // we already have the parent bag and a contents iterator..why not just iterate the bag!?? SlotID = EQ::InventoryProfile::CalcSlotId(i, x); @@ -1322,7 +1322,7 @@ GetItems_Struct* Client::GetTraderItems(){ if (ndx >= 80) break; item = this->GetInv().GetItem(i); - if(item && item->GetItem()->ID == 17899){ //Traders Satchel + if (item && item->GetItem()->BagType == EQ::item::BagTypeTradersSatchel){ for (int x = EQ::invbag::SLOT_BEGIN; x <= EQ::invbag::SLOT_END; x++) { if (ndx >= 80) break; @@ -1349,7 +1349,7 @@ uint16 Client::FindTraderItem(int32 SerialNumber, uint16 Quantity){ uint16 SlotID = 0; for (int i = EQ::invslot::GENERAL_BEGIN; i <= EQ::invslot::GENERAL_END; i++) { item = this->GetInv().GetItem(i); - if(item && item->GetItem()->ID == 17899){ //Traders Satchel + if (item && item->GetItem()->BagType == EQ::item::BagTypeTradersSatchel){ for (int x = EQ::invbag::SLOT_BEGIN; x <= EQ::invbag::SLOT_END; x++){ SlotID = EQ::InventoryProfile::CalcSlotId(i, x); From 2ca37ae838f4fd5590cb51fd7b8189e4ee08b151 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 13 Jun 2021 19:44:54 -0400 Subject: [PATCH 083/624] [Quest API] Add Popup(title, text, popup_id, negative_id, button_type, duration, button_name_one, button_name_two, sound_controls) to Lua. (#1396) - Add client:Popup(title, text, popup_id, negative_id, button_type, duration, button_name_one, button_name_two, sound_controls) to Lua. - There is no overload for only using button_name_one, as the SendFullPopup requires both button names to be set. --- zone/lua_client.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ zone/lua_client.h | 7 +++++++ 2 files changed, 49 insertions(+) diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 501b9d5de..35ac56006 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -2098,6 +2098,41 @@ void Lua_Client::SetHideMe(bool hide_me_state) { self->SetHideMe(hide_me_state); } +void Lua_Client::Popup(const char* title, const char* text) { + Lua_Safe_Call_Void(); + self->SendFullPopup(title, text); +} + +void Lua_Client::Popup(const char* title, const char* text, uint32 popup_id) { + Lua_Safe_Call_Void(); + self->SendFullPopup(title, text, popup_id); +} + +void Lua_Client::Popup(const char* title, const char* text, uint32 popup_id, uint32 negative_id) { + Lua_Safe_Call_Void(); + self->SendFullPopup(title, text, popup_id, negative_id); +} + +void Lua_Client::Popup(const char* title, const char* text, uint32 popup_id, uint32 negative_id, uint32 button_type) { + Lua_Safe_Call_Void(); + self->SendFullPopup(title, text, popup_id, negative_id, button_type); +} + +void Lua_Client::Popup(const char* title, const char* text, uint32 popup_id, uint32 negative_id, uint32 button_type, uint32 duration) { + Lua_Safe_Call_Void(); + self->SendFullPopup(title, text, popup_id, negative_id, button_type, duration); +} + +void Lua_Client::Popup(const char* title, const char* text, uint32 popup_id, uint32 negative_id, uint32 button_type, uint32 duration, const char* button_name_one, const char* button_name_two) { + Lua_Safe_Call_Void(); + self->SendFullPopup(title, text, popup_id, negative_id, button_type, duration, button_name_one, button_name_two); +} + +void Lua_Client::Popup(const char* title, const char* text, uint32 popup_id, uint32 negative_id, uint32 button_type, uint32 duration, const char* button_name_one, const char* button_name_two, uint32 sound_controls) { + Lua_Safe_Call_Void(); + self->SendFullPopup(title, text, popup_id, negative_id, button_type, duration, button_name_one, button_name_two, sound_controls); +} + void Lua_Client::ResetAllDisciplineTimers() { Lua_Safe_Call_Void(); self->ResetAllDisciplineTimers(); @@ -2459,6 +2494,13 @@ luabind::scope lua_register_client() { .def("AddLDoNLoss", (void(Lua_Client::*)(uint32))&Lua_Client::AddLDoNLoss) .def("AddLDoNWin", (void(Lua_Client::*)(uint32))&Lua_Client::AddLDoNWin) .def("SetHideMe", (void(Lua_Client::*)(bool))&Lua_Client::SetHideMe) + .def("Popup", (void(Lua_Client::*)(const char*,const char*))& Lua_Client::Popup) + .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32))& Lua_Client::Popup) + .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32))& Lua_Client::Popup) + .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32,uint32))& Lua_Client::Popup) + .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32,uint32,uint32))& Lua_Client::Popup) + .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32,uint32,uint32,const char*,const char*))& Lua_Client::Popup) + .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32,uint32,uint32,const char*,const char*,uint32))&Lua_Client::Popup) .def("ResetAllDisciplineTimers", (void(Lua_Client::*)(void))&Lua_Client::ResetAllDisciplineTimers); } diff --git a/zone/lua_client.h b/zone/lua_client.h index 28465d57a..f13b69daf 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -359,6 +359,13 @@ public: void EnableAreaRegens(int value); void DisableAreaRegens(); void SetHideMe(bool hide_me_state); + void Popup(const char* title, const char* text); + void Popup(const char* title, const char* text, uint32 popup_id); + void Popup(const char* title, const char* text, uint32 popup_id, uint32 negative_id); + void Popup(const char* title, const char* text, uint32 popup_id, uint32 negative_id, uint32 button_type); + void Popup(const char* title, const char* text, uint32 popup_id, uint32 negative_id, uint32 button_type, uint32 duration); + void Popup(const char* title, const char* text, uint32 popup_id, uint32 negative_id, uint32 button_type, uint32 duration, const char* button_name_one, const char* button_name_two); + void Popup(const char* title, const char* text, uint32 popup_id, uint32 negative_id, uint32 button_type, uint32 duration, const char* button_name_one, const char* button_name_two, uint32 sound_controls); void SetPrimaryWeaponOrnamentation(uint32 model_id); void SetSecondaryWeaponOrnamentation(uint32 model_id); From 22333ee40b41d5f66ea81bbcca92574341e8d90c Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 13 Jun 2021 20:04:03 -0500 Subject: [PATCH 084/624] Fix Loginserver log setting db load init --- loginserver/main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/loginserver/main.cpp b/loginserver/main.cpp index d1f001957..45469ff12 100644 --- a/loginserver/main.cpp +++ b/loginserver/main.cpp @@ -165,7 +165,9 @@ int main(int argc, char **argv) LoadDatabaseConnection(); if (argc == 1) { - LogSys.LoadLogDatabaseSettings()->StartFileLogs(); + LogSys.SetDatabase(server.db) + ->LoadLogDatabaseSettings() + ->StartFileLogs(); } /** From 3886636ec7d78b5e4c12dc4cb3ad57e2e7fb3cb5 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 13 Jun 2021 22:41:38 -0400 Subject: [PATCH 085/624] [Commands] Modify #grid and #wp. (#1399) - #grid add will no longer let you put in a duplicate grid. - Grid nodes now spawn with invul/immune to damage. - Grid nodes now set an entity variable "grid_id" on spawn. - This allows grid nodes to be specifically despawned by "grid_id" entity variable, meaning you can view multiple grids at once and not despawn them all accidentally. - #grid hide will despawn your targeted NPC's Grid nodes. - #grid add, #grid show, #grid delete, and #grid hide send messages to let GM know what's going on. - #wp add and #wp delete now send messages to let the GM know what's going on. - #wpadd now send messages to let the GM know what's going on. --- zone/command.cpp | 232 +++++++++++++++++++++++++++++---------------- zone/entity.cpp | 9 ++ zone/entity.h | 1 + zone/npc.cpp | 46 +++++---- zone/npc.h | 2 +- zone/waypoints.cpp | 18 ++++ zone/zonedb.h | 1 + 7 files changed, 202 insertions(+), 107 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index edcd169b3..994562d06 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -439,8 +439,8 @@ int command_init(void) command_add("weather", "[0/1/2/3] (Off/Rain/Snow/Manual) - Change the weather", 80, command_weather) || command_add("who", "[search]", 20, command_who) || command_add("worldshutdown", "- Shut down world and all zones", 200, command_worldshutdown) || - command_add("wp", "[add/delete] [grid_num] [pause] [wp_num] [-h] - Add/delete a waypoint to/from a wandering grid", 170, command_wp) || - command_add("wpadd", "[pause] [-h] - Add your current location as a waypoint to your NPC target's AI path", 170, command_wpadd) || + command_add("wp", "[add|delete] [grid_id] [pause] [waypoint_id] [-h] - Add or delete a waypoint by grid ID. (-h to use current heading)", 170, command_wp) || + command_add("wpadd", "[pause] [-h] - Add your current location as a waypoint to your NPC target's AI path. (-h to use current heading)", 170, command_wpadd) || command_add("wpinfo", "- Show waypoint info about your NPC target", 170, command_wpinfo) || command_add("worldwide", "Performs world-wide GM functions such as cast (can be extended for other commands). Use caution", 250, command_worldwide) || command_add("xtargets", "Show your targets Extended Targets and optionally set how many xtargets they can have.", 250, command_xtargets) || @@ -2470,28 +2470,58 @@ void command_setlsinfo(Client *c, const Seperator *sep) void command_grid(Client *c, const Seperator *sep) { - if (strcasecmp("max", sep->arg[1]) == 0) { - c->Message(Chat::White, "Highest grid ID in this zone: %d", content_db.GetHighestGrid(zone->GetZoneID())); - } - else if (strcasecmp("add", sep->arg[1]) == 0) { - content_db.ModifyGrid(c, false, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), zone->GetZoneID()); - } - else if (strcasecmp("show", sep->arg[1]) == 0) { - - Mob *target = c->GetTarget(); - - if (!target || !target->IsNPC()) { - c->Message(Chat::White, "You need a NPC target!"); + auto command_type = sep->arg[1]; + auto zone_id = zone->GetZoneID(); + if (strcasecmp("max", command_type) == 0) { + c->Message( + Chat::White, + fmt::format( + "Highest grid ID in this zone is {}.", + content_db.GetHighestGrid(zone_id) + ).c_str() + ); + } else if (strcasecmp("add", command_type) == 0) { + auto grid_id = atoi(sep->arg[2]); + auto wander_type = atoi(sep->arg[3]); + auto pause_type = atoi(sep->arg[4]); + if (!content_db.GridExistsInZone(zone_id, grid_id)) { + content_db.ModifyGrid(c, false, grid_id, wander_type, pause_type, zone_id); + c->Message( + Chat::White, + fmt::format( + "Grid {} added to zone ID {} with wander type {} and pause type {}.", + grid_id, + zone_id, + wander_type, + pause_type + ).c_str() + ); + } else { + c->Message( + Chat::White, + fmt::format( + "Grid {} already exists in zone ID {}.", + grid_id, + zone_id + ).c_str() + ); return; } - - std::string query = StringFormat( + } else if (strcasecmp("show", command_type) == 0) { + Mob *target = c->GetTarget(); + if (!target || !target->IsNPC()) { + c->Message(Chat::White, "You need to target an NPC!"); + return; + } + + auto grid_id = target->CastToNPC()->GetGrid(); + std::string query = fmt::format( "SELECT `x`, `y`, `z`, `heading`, `number` " "FROM `grid_entries` " - "WHERE `zoneid` = %u and `gridid` = %i " + "WHERE `zoneid` = {} AND `gridid` = {} " "ORDER BY `number`", - zone->GetZoneID(), - target->CastToNPC()->GetGrid() + zone_id, + grid_id ); auto results = content_db.QueryDatabase(query); @@ -2501,33 +2531,21 @@ void command_grid(Client *c, const Seperator *sep) } if (results.RowCount() == 0) { - c->Message(Chat::White, "No grid found"); + c->Message(Chat::White, "No grid found."); return; } - /** - * Depop any node npc's already spawned - */ - auto &mob_list = entity_list.GetMobList(); - for (auto itr = mob_list.begin(); itr != mob_list.end(); ++itr) { - Mob *mob = itr->second; - if (mob->IsNPC() && mob->GetRace() == 2254) { - mob->Depop(); - } - } + // Depop any node npc's already spawned + entity_list.DespawnGridNodes(grid_id); - /** - * Spawn grid nodes - */ + // Spawn grid nodes std::map, int32> zoffset; - - for (auto row = results.begin(); row != results.end(); ++row) { + for (auto row : results) { glm::vec4 node_position = glm::vec4(atof(row[0]), atof(row[1]), atof(row[2]), atof(row[3])); - std::vector node_loc { - node_position.x, - node_position.y, - node_position.z + node_position.x, + node_position.y, + node_position.z }; // If we already have a node at this location, set the z offset @@ -2536,46 +2554,98 @@ void command_grid(Client *c, const Seperator *sep) auto search = zoffset.find(node_loc); if (search != zoffset.end()) { search->second = search->second + 3; - } - else { + } else { zoffset[node_loc] = 0.0; } node_position.z += zoffset[node_loc]; - - NPC::SpawnGridNodeNPC(node_position,atoi(row[4]),zoffset[node_loc]); + NPC::SpawnGridNodeNPC(node_position, grid_id, atoi(row[4]), zoffset[node_loc]); } - } - else if (strcasecmp("delete", sep->arg[1]) == 0) { - content_db.ModifyGrid(c, true, atoi(sep->arg[2]), 0, 0, zone->GetZoneID()); - } - else { - c->Message(Chat::White, "Usage: #grid add/delete grid_num wandertype pausetype"); - c->Message(Chat::White, "Usage: #grid max - displays the highest grid ID used in this zone (for add)"); - c->Message(Chat::White, "Usage: #grid show - displays wp nodes as boxes"); + c->Message( + Chat::White, + fmt::format( + "Spawning nodes for grid {}.", + grid_id + ).c_str() + ); + } else if (strcasecmp("hide", command_type) == 0) { + Mob* target = c->GetTarget(); + if (!target || !target->IsNPC()) { + c->Message(Chat::White, "You need to target an NPC!"); + return; + } + + auto grid_id = target->CastToNPC()->GetGrid(); + entity_list.DespawnGridNodes(grid_id); + c->Message( + Chat::White, + fmt::format( + "Depawning nodes for grid {}.", + grid_id + ).c_str() + ); + } else if (strcasecmp("delete", command_type) == 0) { + auto grid_id = atoi(sep->arg[2]); + content_db.ModifyGrid(c, true, grid_id, 0, 0, zone_id); + c->Message( + Chat::White, + fmt::format( + "Grid {} deleted from zone ID {}.", + grid_id, + zone_id + ).c_str() + ); + } else { + c->Message(Chat::White, "Usage: #grid [add|delete] [grid_id] [wander_type] [pause_type]"); + c->Message(Chat::White, "Usage: #grid [max] - displays the highest grid ID used in this zone (for add)"); + c->Message(Chat::White, "Usage: #grid [show] - displays wp nodes as boxes"); } } void command_wp(Client *c, const Seperator *sep) { - int wp = atoi(sep->arg[4]); + auto command_type = sep->arg[1]; + auto grid_id = atoi(sep->arg[2]); + if (grid_id != 0) { + auto pause = atoi(sep->arg[3]); + auto waypoint = atoi(sep->arg[4]); + auto zone_id = zone->GetZoneID(); + if (strcasecmp("add", command_type) == 0) { + if (waypoint == 0) { // Default to highest if it's left blank, or we enter 0 + waypoint = (content_db.GetHighestWaypoint(zone_id, grid_id) + 1); + } - if (strcasecmp("add", sep->arg[1]) == 0) { - if (wp == 0) //default to highest if it's left blank, or we enter 0 - wp = content_db.GetHighestWaypoint(zone->GetZoneID(), atoi(sep->arg[2])) + 1; - if (strcasecmp("-h", sep->arg[5]) == 0) { - content_db.AddWP(c, atoi(sep->arg[2]),wp, c->GetPosition(), atoi(sep->arg[3]),zone->GetZoneID()); - } - else { - auto position = c->GetPosition(); - position.w = -1; - content_db.AddWP(c, atoi(sep->arg[2]),wp, position, atoi(sep->arg[3]),zone->GetZoneID()); + if (strcasecmp("-h", sep->arg[5]) == 0) { + content_db.AddWP(c, grid_id, waypoint, c->GetPosition(), pause, zone_id); + } else { + auto position = c->GetPosition(); + position.w = -1; + content_db.AddWP(c, grid_id, waypoint, position, pause, zone_id); + } + c->Message( + Chat::White, + fmt::format( + "Waypoint {} added to grid {} with a pause of {} {}.", + waypoint, + grid_id, + pause, + (pause == 1 ? "second" : "seconds") + ).c_str() + ); + } else if (strcasecmp("delete", command_type) == 0) { + content_db.DeleteWaypoint(c, grid_id, waypoint, zone_id); + c->Message( + Chat::White, + fmt::format( + "Waypoint {} deleted from grid {}.", + waypoint, + grid_id + ).c_str() + ); } + } else { + c->Message(Chat::White,"Usage: #wp [add|delete] [grid_id] [pause] [waypoint_id] [-h]"); } - else if (strcasecmp("delete", sep->arg[1]) == 0) - content_db.DeleteWaypoint(c, atoi(sep->arg[2]),wp,zone->GetZoneID()); - else - c->Message(Chat::White,"Usage: #wp add/delete grid_num pause wp_num [-h]"); } void command_iplookup(Client *c, const Seperator *sep) @@ -7843,20 +7913,14 @@ void command_wpinfo(Client *c, const Seperator *sep) void command_wpadd(Client *c, const Seperator *sep) { - int type1 = 0, - type2 = 0, - pause = 0; // Defaults for a new grid - + int type1 = 0, type2 = 0, pause = 0; // Defaults for a new grid Mob *target = c->GetTarget(); if (target && target->IsNPC()) { Spawn2 *s2info = target->CastToNPC()->respawn2; - - if (s2info == - nullptr) // Can't figure out where this mob's spawn came from... maybe a dynamic mob created by #spawn - { + if (s2info == nullptr) { c->Message( Chat::White, - "#wpadd FAILED -- Can't determine which spawn record in the database this mob came from!" + "#wpadd Failed, you must target a valid spawn." ); return; } @@ -7864,8 +7928,7 @@ void command_wpadd(Client *c, const Seperator *sep) if (sep->arg[1][0]) { if (atoi(sep->arg[1]) >= 0) { pause = atoi(sep->arg[1]); - } - else { + } else { c->Message(Chat::White, "Usage: #wpadd [pause] [-h]"); return; } @@ -7875,18 +7938,23 @@ void command_wpadd(Client *c, const Seperator *sep) position.w = -1; } - uint32 tmp_grid = content_db.AddWPForSpawn(c, s2info->GetID(), position, pause, type1, type2, zone->GetZoneID()); + auto zone_id = zone->GetZoneID(); + uint32 tmp_grid = content_db.AddWPForSpawn(c, s2info->GetID(), position, pause, type1, type2, zone_id); if (tmp_grid) { target->CastToNPC()->SetGrid(tmp_grid); } - target->CastToNPC()->AssignWaypoints(target->CastToNPC()->GetGrid()); + auto grid_id = target->CastToNPC()->GetGrid(); + target->CastToNPC()->AssignWaypoints(grid_id); c->Message( Chat::White, - "Waypoint added. Use #wpinfo to see waypoints for this NPC (may need to #repop first)." + fmt::format( + "Waypoint added to grid {} in zone ID {}. Use #wpinfo to see waypoints for this NPC (may need to #repop first).", + grid_id, + zone_id + ).c_str() ); - } - else { + } else { c->Message(Chat::White, "You must target an NPC to use this."); } } diff --git a/zone/entity.cpp b/zone/entity.cpp index 81087da21..53477e5d7 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -5314,3 +5314,12 @@ int EntityList::MovePlayerCorpsesToGraveyard(bool force_move_from_instance) return moved_count; } + +void EntityList::DespawnGridNodes(int32 grid_id) { + for (auto mob_iterator : mob_list) { + Mob *mob = mob_iterator.second; + if (mob->IsNPC() && mob->GetRace() == 2254 && mob->EntityVariableExists("grid_id") && atoi(mob->GetEntityVariable("grid_id")) == grid_id) { + mob->Depop(); + } + } +} diff --git a/zone/entity.h b/zone/entity.h index 5eec26f35..1690799a1 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -234,6 +234,7 @@ public: void RemoveAllCorpsesByCharID(uint32 charid); void RemoveCorpseByDBID(uint32 dbid); int RezzAllCorpsesByCharID(uint32 charid); + void DespawnGridNodes(int32 grid_id); bool IsMobInZone(Mob *who); void ClearClientPetitionQueue(); bool CanAddHateForMob(Mob *p); diff --git a/zone/npc.cpp b/zone/npc.cpp index 325f8fb70..3d65eed93 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -1099,8 +1099,7 @@ bool NPC::SpawnZoneController() return true; } -void NPC::SpawnGridNodeNPC(const glm::vec4 &position, int32 grid_number, int32 zoffset) { - +void NPC::SpawnGridNodeNPC(const glm::vec4 &position, int32 grid_id, int32 grid_number, int32 zoffset) { auto npc_type = new NPCType; memset(npc_type, 0, sizeof(NPCType)); @@ -1112,31 +1111,30 @@ void NPC::SpawnGridNodeNPC(const glm::vec4 &position, int32 grid_number, int32 z strcat(npc_type->name, "(Stacked)"); } - npc_type->current_hp = 4000000; - npc_type->max_hp = 4000000; - npc_type->race = 2254; - npc_type->gender = 2; - npc_type->class_ = 9; - npc_type->deity = 1; - npc_type->level = 200; - npc_type->npc_id = 0; - npc_type->loottable_id = 0; - npc_type->texture = 1; - npc_type->light = 1; - npc_type->size = 1; - npc_type->runspeed = 0; - npc_type->merchanttype = 1; - npc_type->bodytype = 1; - npc_type->show_name = true; - npc_type->findable = true; + npc_type->current_hp = 4000000; + npc_type->max_hp = 4000000; + npc_type->race = 2254; + npc_type->gender = 2; + npc_type->class_ = 9; + npc_type->deity = 1; + npc_type->level = 200; + npc_type->npc_id = 0; + npc_type->loottable_id = 0; + npc_type->texture = 1; + npc_type->light = 1; + npc_type->size = 1; + npc_type->runspeed = 0; + npc_type->merchanttype = 1; + npc_type->bodytype = 1; + npc_type->show_name = true; + npc_type->findable = true; + strn0cpy(npc_type->special_abilities, "24,1^35,1", 512); auto node_position = glm::vec4(position.x, position.y, position.z, position.w); - auto npc = new NPC(npc_type, nullptr, node_position, GravityBehavior::Flying); - - npc->name[strlen(npc->name)-3] = (char) NULL; - + auto npc = new NPC(npc_type, nullptr, node_position, GravityBehavior::Flying); + npc->name[strlen(npc->name) - 3] = (char) NULL; npc->GiveNPCTypeData(npc_type); - + npc->SetEntityVariable("grid_id", itoa(grid_id)); entity_list.AddNPC(npc); } diff --git a/zone/npc.h b/zone/npc.h index 121503215..d75fe67f7 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -114,7 +114,7 @@ public: virtual ~NPC(); static NPC *SpawnNodeNPC(std::string name, std::string last_name, const glm::vec4 &position); - static void SpawnGridNodeNPC(const glm::vec4 &position, int32 grid_number, int32 zoffset); + static void SpawnGridNodeNPC(const glm::vec4 &position, int32 grid_id, int32 grid_number, int32 zoffset); static void SpawnZonePointNodeNPC(std::string name, const glm::vec4 &position); //abstract virtual function implementations requird by base abstract class diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index 90fc25d1e..76db92dd6 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -1036,6 +1036,24 @@ void ZoneDatabase::ModifyGrid(Client *client, bool remove, uint32 id, uint8 type results = QueryDatabase(query); } +bool ZoneDatabase::GridExistsInZone(uint32 zone_id, uint32 grid_id) { + bool grid_exists = false; + std::string query = fmt::format( + "SELECT * FROM `grid` WHERE `id` = {} AND `zoneid` = {}", + grid_id, + zone_id + ); + auto results = QueryDatabase(query); + if (!results.Success()) { + return grid_exists; + } + + if (results.RowCount() == 1) { + grid_exists = true; + } + return grid_exists; +} + /************************************** * AddWP - Adds a new waypoint to a specific grid for a specific zone. */ diff --git a/zone/zonedb.h b/zone/zonedb.h index e05e0872a..7f523e4e1 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -448,6 +448,7 @@ public: void AddWP(Client *c, uint32 gridid, uint32 wpnum, const glm::vec4& position, uint32 pause, uint16 zoneid); uint32 AddWPForSpawn(Client *c, uint32 spawn2id, const glm::vec4& position, uint32 pause, int type1, int type2, uint16 zoneid); void ModifyGrid(Client *c, bool remove, uint32 id, uint8 type = 0, uint8 type2 = 0, uint16 zoneid = 0); + bool GridExistsInZone(uint32 zone_id, uint32 grid_id); void ModifyWP(Client *c, uint32 grid_id, uint32 wp_num, const glm::vec3& location, uint32 script = 0, uint16 zoneid = 0); uint8 GetGridType(uint32 grid, uint32 zoneid); uint8 GetGridType2(uint32 grid, uint16 zoneid); From 7139530787fe6703481763fdbe7cb3e49c61a1f1 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 13 Jun 2021 21:42:30 -0500 Subject: [PATCH 086/624] [Library] Update httplib (#1401) * Update httplib * Update syntax for new httplib and run on own thread * Only log if path is set in request --- common/http/httplib.h | 8067 +++++++++++++++++++++++++++++++++-------- loginserver/main.cpp | 33 +- 2 files changed, 6540 insertions(+), 1560 deletions(-) diff --git a/common/http/httplib.h b/common/http/httplib.h index 827d5f1e0..e697e106a 100644 --- a/common/http/httplib.h +++ b/common/http/httplib.h @@ -1,13 +1,104 @@ // // httplib.h // -// Copyright (c) 2019 Yuji Hirose. All rights reserved. +// Copyright (c) 2021 Yuji Hirose. All rights reserved. // MIT License // #ifndef CPPHTTPLIB_HTTPLIB_H #define CPPHTTPLIB_HTTPLIB_H +/* + * Configuration + */ + +#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND +#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 +#endif + +#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT +#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5 +#endif + +#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND +#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300 +#endif + +#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND +#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_READ_TIMEOUT_SECOND +#define CPPHTTPLIB_READ_TIMEOUT_SECOND 5 +#endif + +#ifndef CPPHTTPLIB_READ_TIMEOUT_USECOND +#define CPPHTTPLIB_READ_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_WRITE_TIMEOUT_SECOND +#define CPPHTTPLIB_WRITE_TIMEOUT_SECOND 5 +#endif + +#ifndef CPPHTTPLIB_WRITE_TIMEOUT_USECOND +#define CPPHTTPLIB_WRITE_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND +#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0 +#endif + +#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND +#ifdef _WIN32 +#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 10000 +#else +#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0 +#endif +#endif + +#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH +#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192 +#endif + +#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT +#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20 +#endif + +#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH +#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits::max)()) +#endif + +#ifndef CPPHTTPLIB_TCP_NODELAY +#define CPPHTTPLIB_TCP_NODELAY false +#endif + +#ifndef CPPHTTPLIB_RECV_BUFSIZ +#define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u) +#endif + +#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ +#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u) +#endif + +#ifndef CPPHTTPLIB_THREAD_POOL_COUNT +#define CPPHTTPLIB_THREAD_POOL_COUNT \ + ((std::max)(8u, std::thread::hardware_concurrency() > 0 \ + ? std::thread::hardware_concurrency() - 1 \ + : 0)) +#endif + +#ifndef CPPHTTPLIB_RECV_FLAGS +#define CPPHTTPLIB_RECV_FLAGS 0 +#endif + +#ifndef CPPHTTPLIB_SEND_FLAGS +#define CPPHTTPLIB_SEND_FLAGS 0 +#endif + +/* + * Headers + */ + #ifdef _WIN32 #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS @@ -17,8 +108,16 @@ #define _CRT_NONSTDC_NO_DEPRECATE #endif //_CRT_NONSTDC_NO_DEPRECATE -#if defined(_MSC_VER) && _MSC_VER < 1900 +#if defined(_MSC_VER) +#ifdef _WIN64 +using ssize_t = __int64; +#else +using ssize_t = int; +#endif + +#if _MSC_VER < 1900 #define snprintf _snprintf_s +#endif #endif // _MSC_VER #ifndef S_ISREG @@ -35,49 +134,97 @@ #include #include + +#include #include +#ifndef WSA_FLAG_NO_HANDLE_INHERIT +#define WSA_FLAG_NO_HANDLE_INHERIT 0x80 +#endif + +#ifdef _MSC_VER #pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "crypt32.lib") +#pragma comment(lib, "cryptui.lib") +#endif #ifndef strcasecmp #define strcasecmp _stricmp #endif // strcasecmp -typedef SOCKET socket_t; -#else +using socket_t = SOCKET; +#ifdef CPPHTTPLIB_USE_POLL +#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout) +#endif + +#else // not _WIN32 + #include #include +#include #include #include +#ifdef __linux__ +#include +#endif +#include +#ifdef CPPHTTPLIB_USE_POLL +#include +#endif +#include #include -#include #include #include #include -typedef int socket_t; +using socket_t = int; #define INVALID_SOCKET (-1) #endif //_WIN32 -#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include +#include +#include +#include #include #include #include +#include #include +#include +#include #include #include #include -#include #ifdef CPPHTTPLIB_OPENSSL_SUPPORT #include +#include #include #include +#if defined(_WIN32) && defined(OPENSSL_USE_APPLINK) +#include +#endif + +#include +#include + +#if OPENSSL_VERSION_NUMBER < 0x1010100fL +#error Sorry, OpenSSL versions prior to 1.1.1 are not supported +#endif + #if OPENSSL_VERSION_NUMBER < 0x10100000L +#include inline const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1) { return M_ASN1_STRING_data(asn1); } @@ -88,147 +235,388 @@ inline const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1) { #include #endif -/* - * Configuration - */ -#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 -#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND 0 -#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5 -#define CPPHTTPLIB_READ_TIMEOUT_SECOND 5 -#define CPPHTTPLIB_READ_TIMEOUT_USECOND 0 -#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192 -#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH (std::numeric_limits::max)() -#define CPPHTTPLIB_RECV_BUFSIZ 4096 +#ifdef CPPHTTPLIB_BROTLI_SUPPORT +#include +#include +#endif +/* + * Declaration + */ namespace httplib { namespace detail { +/* + * Backport std::make_unique from C++14. + * + * NOTE: This code came up with the following stackoverflow post: + * https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique + * + */ + + template + typename std::enable_if::value, std::unique_ptr>::type + make_unique(Args &&...args) { + return std::unique_ptr(new T(std::forward(args)...)); + } + + template + typename std::enable_if::value, std::unique_ptr>::type + make_unique(std::size_t n) { + typedef typename std::remove_extent::type RT; + return std::unique_ptr(new RT[n]); + } + struct ci { bool operator()(const std::string &s1, const std::string &s2) const { - return std::lexicographical_compare( - s1.begin(), s1.end(), s2.begin(), s2.end(), - [](char c1, char c2) { return ::tolower(c1) < ::tolower(c2); }); + return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), + s2.end(), + [](unsigned char c1, unsigned char c2) { + return ::tolower(c1) < ::tolower(c2); + }); } }; } // namespace detail - enum class HttpVersion { v1_0 = 0, v1_1 }; + using Headers = std::multimap; - typedef std::multimap Headers; + using Params = std::multimap; + using Match = std::smatch; - template - std::pair make_range_header(uint64_t value, - Args... args); + using Progress = std::function; - typedef std::multimap Params; - typedef std::smatch Match; - typedef std::function Progress; + struct Response; + using ResponseHandler = std::function; - struct MultipartFile { + struct MultipartFormData { + std::string name; + std::string content; std::string filename; std::string content_type; - size_t offset = 0; - size_t length = 0; }; - typedef std::multimap MultipartFiles; + using MultipartFormDataItems = std::vector; + using MultipartFormDataMap = std::multimap; + + class DataSink { + public: + DataSink() : os(&sb_), sb_(*this) {} + + DataSink(const DataSink &) = delete; + DataSink &operator=(const DataSink &) = delete; + DataSink(DataSink &&) = delete; + DataSink &operator=(DataSink &&) = delete; + + std::function write; + std::function done; + std::function is_writable; + std::ostream os; + + private: + class data_sink_streambuf : public std::streambuf { + public: + explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {} + + protected: + std::streamsize xsputn(const char *s, std::streamsize n) { + sink_.write(s, static_cast(n)); + return n; + } + + private: + DataSink &sink_; + }; + + data_sink_streambuf sb_; + }; + + using ContentProvider = + std::function; + + using ContentProviderWithoutLength = + std::function; + + using ContentProviderResourceReleaser = std::function; + + using ContentReceiverWithProgress = + std::function; + + using ContentReceiver = + std::function; + + using MultipartContentHeader = + std::function; + + class ContentReader { + public: + using Reader = std::function; + using MultipartReader = std::function; + + ContentReader(Reader reader, MultipartReader multipart_reader) + : reader_(std::move(reader)), + multipart_reader_(std::move(multipart_reader)) {} + + bool operator()(MultipartContentHeader header, + ContentReceiver receiver) const { + return multipart_reader_(std::move(header), std::move(receiver)); + } + + bool operator()(ContentReceiver receiver) const { + return reader_(std::move(receiver)); + } + + Reader reader_; + MultipartReader multipart_reader_; + }; + + using Range = std::pair; + using Ranges = std::vector; struct Request { - std::string version; std::string method; - std::string target; std::string path; Headers headers; std::string body; + + std::string remote_addr; + int remote_port = -1; + + // for server + std::string version; + std::string target; Params params; - MultipartFiles files; + MultipartFormDataMap files; + Ranges ranges; Match matches; + // for client + ResponseHandler response_handler; + ContentReceiverWithProgress content_receiver; Progress progress; - #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - const SSL *ssl; + const SSL *ssl = nullptr; #endif bool has_header(const char *key) const; std::string get_header_value(const char *key, size_t id = 0) const; + template + T get_header_value(const char *key, size_t id = 0) const; size_t get_header_value_count(const char *key) const; void set_header(const char *key, const char *val); + void set_header(const char *key, const std::string &val); bool has_param(const char *key) const; std::string get_param_value(const char *key, size_t id = 0) const; size_t get_param_value_count(const char *key) const; + bool is_multipart_form_data() const; + bool has_file(const char *key) const; - MultipartFile get_file_value(const char *key) const; + MultipartFormData get_file_value(const char *key) const; + + // private members... + size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT; + size_t content_length_ = 0; + ContentProvider content_provider_; + bool is_chunked_content_provider_ = false; + size_t authorization_count_ = 0; }; struct Response { std::string version; - int status; + int status = -1; + std::string reason; Headers headers; std::string body; - std::function streamcb; + std::string location; // Redirect location bool has_header(const char *key) const; std::string get_header_value(const char *key, size_t id = 0) const; + template + T get_header_value(const char *key, size_t id = 0) const; size_t get_header_value_count(const char *key) const; void set_header(const char *key, const char *val); + void set_header(const char *key, const std::string &val); - void set_redirect(const char *uri); + void set_redirect(const char *url, int status = 302); + void set_redirect(const std::string &url, int status = 302); void set_content(const char *s, size_t n, const char *content_type); void set_content(const std::string &s, const char *content_type); - Response() : status(-1) {} + void set_content_provider( + size_t length, const char *content_type, ContentProvider provider, + ContentProviderResourceReleaser resource_releaser = nullptr); + + void set_content_provider( + const char *content_type, ContentProviderWithoutLength provider, + ContentProviderResourceReleaser resource_releaser = nullptr); + + void set_chunked_content_provider( + const char *content_type, ContentProviderWithoutLength provider, + ContentProviderResourceReleaser resource_releaser = nullptr); + + Response() = default; + Response(const Response &) = default; + Response &operator=(const Response &) = default; + Response(Response &&) = default; + Response &operator=(Response &&) = default; + ~Response() { + if (content_provider_resource_releaser_) { + content_provider_resource_releaser_(content_provider_success_); + } + } + + // private members... + size_t content_length_ = 0; + ContentProvider content_provider_; + ContentProviderResourceReleaser content_provider_resource_releaser_; + bool is_chunked_content_provider_ = false; + bool content_provider_success_ = false; }; class Stream { public: - virtual ~Stream() {} - virtual int read(char *ptr, size_t size) = 0; - virtual int write(const char *ptr, size_t size1) = 0; - virtual int write(const char *ptr) = 0; - virtual std::string get_remote_addr() const = 0; + virtual ~Stream() = default; + + virtual bool is_readable() const = 0; + virtual bool is_writable() const = 0; + + virtual ssize_t read(char *ptr, size_t size) = 0; + virtual ssize_t write(const char *ptr, size_t size) = 0; + virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0; + virtual socket_t socket() const = 0; template - void write_format(const char *fmt, const Args &... args); + ssize_t write_format(const char *fmt, const Args &...args); + ssize_t write(const char *ptr); + ssize_t write(const std::string &s); }; - class SocketStream : public Stream { + class TaskQueue { public: - SocketStream(socket_t sock); - virtual ~SocketStream(); + TaskQueue() = default; + virtual ~TaskQueue() = default; - virtual int read(char *ptr, size_t size); - virtual int write(const char *ptr, size_t size); - virtual int write(const char *ptr); - virtual std::string get_remote_addr() const; + virtual void enqueue(std::function fn) = 0; + virtual void shutdown() = 0; + + virtual void on_idle(){}; + }; + + class ThreadPool : public TaskQueue { + public: + explicit ThreadPool(size_t n) : shutdown_(false) { + while (n) { + threads_.emplace_back(worker(*this)); + n--; + } + } + + ThreadPool(const ThreadPool &) = delete; + ~ThreadPool() override = default; + + void enqueue(std::function fn) override { + std::unique_lock lock(mutex_); + jobs_.push_back(std::move(fn)); + cond_.notify_one(); + } + + void shutdown() override { + // Stop all worker threads... + { + std::unique_lock lock(mutex_); + shutdown_ = true; + } + + cond_.notify_all(); + + // Join... + for (auto &t : threads_) { + t.join(); + } + } private: - socket_t sock_; + struct worker { + explicit worker(ThreadPool &pool) : pool_(pool) {} + + void operator()() { + for (;;) { + std::function fn; + { + std::unique_lock lock(pool_.mutex_); + + pool_.cond_.wait( + lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; }); + + if (pool_.shutdown_ && pool_.jobs_.empty()) { break; } + + fn = pool_.jobs_.front(); + pool_.jobs_.pop_front(); + } + + assert(true == static_cast(fn)); + fn(); + } + } + + ThreadPool &pool_; + }; + friend struct worker; + + std::vector threads_; + std::list> jobs_; + + bool shutdown_; + + std::condition_variable cond_; + std::mutex mutex_; }; - class BufferStream : public Stream { - public: - BufferStream() {} - virtual ~BufferStream() {} + using Logger = std::function; - virtual int read(char *ptr, size_t size); - virtual int write(const char *ptr, size_t size); - virtual int write(const char *ptr); - virtual std::string get_remote_addr() const; + using SocketOptions = std::function; - const std::string &get_buffer() const; - - private: - std::string buffer; - }; + inline void default_socket_options(socket_t sock) { + int yes = 1; +#ifdef _WIN32 + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&yes), + sizeof(yes)); + setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, + reinterpret_cast(&yes), sizeof(yes)); +#else +#ifdef SO_REUSEPORT + setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast(&yes), + sizeof(yes)); +#else + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&yes), + sizeof(yes)); +#endif +#endif + } class Server { public: - typedef std::function Handler; - typedef std::function Logger; + using Handler = std::function; + + using ExceptionHandler = + std::function; + + enum class HandlerResponse { + Handled, + Unhandled, + }; + using HandlerWithResponse = + std::function; + + using HandlerWithContentReader = std::function; + + using Expect100ContinueHandler = + std::function; Server(); @@ -237,32 +625,72 @@ namespace httplib { virtual bool is_valid() const; Server &Get(const char *pattern, Handler handler); + Server &Get(const char *pattern, size_t pattern_len, Handler handler); Server &Post(const char *pattern, Handler handler); - + Server &Post(const char *pattern, size_t pattern_len, Handler handler); + Server &Post(const char *pattern, HandlerWithContentReader handler); + Server &Post(const char *pattern, size_t pattern_len, + HandlerWithContentReader handler); Server &Put(const char *pattern, Handler handler); + Server &Put(const char *pattern, size_t pattern_len, Handler handler); + Server &Put(const char *pattern, HandlerWithContentReader handler); + Server &Put(const char *pattern, size_t pattern_len, + HandlerWithContentReader handler); Server &Patch(const char *pattern, Handler handler); + Server &Patch(const char *pattern, size_t pattern_len, Handler handler); + Server &Patch(const char *pattern, HandlerWithContentReader handler); + Server &Patch(const char *pattern, size_t pattern_len, + HandlerWithContentReader handler); Server &Delete(const char *pattern, Handler handler); + Server &Delete(const char *pattern, size_t pattern_len, Handler handler); + Server &Delete(const char *pattern, HandlerWithContentReader handler); + Server &Delete(const char *pattern, size_t pattern_len, + HandlerWithContentReader handler); Server &Options(const char *pattern, Handler handler); + Server &Options(const char *pattern, size_t pattern_len, Handler handler); - bool set_base_dir(const char *path); + bool set_base_dir(const char *dir, const char *mount_point = nullptr); + bool set_mount_point(const char *mount_point, const char *dir, + Headers headers = Headers()); + bool remove_mount_point(const char *mount_point); + Server &set_file_extension_and_mimetype_mapping(const char *ext, + const char *mime); + Server &set_file_request_handler(Handler handler); - void set_error_handler(Handler handler); - void set_logger(Logger logger); + Server &set_error_handler(HandlerWithResponse handler); + Server &set_error_handler(Handler handler); + Server &set_exception_handler(ExceptionHandler handler); + Server &set_pre_routing_handler(HandlerWithResponse handler); + Server &set_post_routing_handler(Handler handler); - void set_keep_alive_max_count(size_t count); - void set_payload_max_length(uint64_t length); + Server &set_expect_100_continue_handler(Expect100ContinueHandler handler); + Server &set_logger(Logger logger); + Server &set_address_family(int family); + Server &set_tcp_nodelay(bool on); + Server &set_socket_options(SocketOptions socket_options); + + Server &set_default_headers(Headers headers); + + Server &set_keep_alive_max_count(size_t count); + Server &set_keep_alive_timeout(time_t sec); + + Server &set_read_timeout(time_t sec, time_t usec = 0); + template + Server &set_read_timeout(const std::chrono::duration &duration); + + Server &set_write_timeout(time_t sec, time_t usec = 0); + template + Server &set_write_timeout(const std::chrono::duration &duration); + + Server &set_idle_interval(time_t sec, time_t usec = 0); + template + Server &set_idle_interval(const std::chrono::duration &duration); + + Server &set_payload_max_length(size_t length); + + bool bind_to_port(const char *host, int port, int socket_flags = 0); int bind_to_any_port(const char *host, int socket_flags = 0); - - bool bind(const char *host, int port, int socket_flags = 0) - { - if (bind_internal(host, port, socket_flags) < 0) return false; - - return true; - } - - bool poll(); - bool listen_after_bind(); bool listen(const char *host, int port, int socket_flags = 0); @@ -270,170 +698,734 @@ namespace httplib { bool is_running() const; void stop(); - protected: - bool process_request(Stream &strm, bool last_connection, - bool &connection_close, - std::function setup_request = nullptr); + std::function new_task_queue; - size_t keep_alive_max_count_; - size_t payload_max_length_; + protected: + bool process_request(Stream &strm, bool close_connection, + bool &connection_closed, + const std::function &setup_request); + + std::atomic svr_sock_; + size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT; + time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND; + time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; + time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; + time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND; + time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND; + time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND; + time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND; + size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH; private: - typedef std::vector> Handlers; + using Handlers = std::vector>; + using HandlersForContentReader = + std::vector>; - socket_t create_server_socket(const char *host, int port, - int socket_flags) const; + socket_t create_server_socket(const char *host, int port, int socket_flags, + SocketOptions socket_options) const; int bind_internal(const char *host, int port, int socket_flags); bool listen_internal(); - bool routing(Request &req, Response &res); - bool handle_file_request(Request &req, Response &res); - bool dispatch_request(Request &req, Response &res, Handlers &handlers); + bool routing(Request &req, Response &res, Stream &strm); + bool handle_file_request(const Request &req, Response &res, + bool head = false); + bool dispatch_request(Request &req, Response &res, const Handlers &handlers); + bool + dispatch_request_for_content_reader(Request &req, Response &res, + ContentReader content_reader, + const HandlersForContentReader &handlers); bool parse_request_line(const char *s, Request &req); - void write_response(Stream &strm, bool last_connection, const Request &req, + void apply_ranges(const Request &req, Response &res, + std::string &content_type, std::string &boundary); + bool write_response(Stream &strm, bool close_connection, const Request &req, Response &res); + bool write_response_with_content(Stream &strm, bool close_connection, + const Request &req, Response &res); + bool write_response_core(Stream &strm, bool close_connection, + const Request &req, Response &res, + bool need_apply_ranges); + bool write_content_with_provider(Stream &strm, const Request &req, + Response &res, const std::string &boundary, + const std::string &content_type); + bool read_content(Stream &strm, Request &req, Response &res); + bool + read_content_with_content_receiver(Stream &strm, Request &req, Response &res, + ContentReceiver receiver, + MultipartContentHeader multipart_header, + ContentReceiver multipart_receiver); + bool read_content_core(Stream &strm, Request &req, Response &res, + ContentReceiver receiver, + MultipartContentHeader mulitpart_header, + ContentReceiver multipart_receiver); - virtual bool read_and_close_socket(socket_t sock); + virtual bool process_and_close_socket(socket_t sock); + + struct MountPointEntry { + std::string mount_point; + std::string base_dir; + Headers headers; + }; + std::vector base_dirs_; std::atomic is_running_; - std::atomic svr_sock_; - std::string base_dir_; + std::map file_extension_and_mimetype_map_; + Handler file_request_handler_; Handlers get_handlers_; Handlers post_handlers_; + HandlersForContentReader post_handlers_for_content_reader_; Handlers put_handlers_; + HandlersForContentReader put_handlers_for_content_reader_; Handlers patch_handlers_; + HandlersForContentReader patch_handlers_for_content_reader_; Handlers delete_handlers_; + HandlersForContentReader delete_handlers_for_content_reader_; Handlers options_handlers_; - Handler error_handler_; + HandlerWithResponse error_handler_; + ExceptionHandler exception_handler_; + HandlerWithResponse pre_routing_handler_; + Handler post_routing_handler_; + Logger logger_; + Expect100ContinueHandler expect_100_continue_handler_; + + int address_family_ = AF_UNSPEC; + bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY; + SocketOptions socket_options_ = default_socket_options; + + Headers default_headers_; + }; + + enum class Error { + Success = 0, + Unknown, + Connection, + BindIPAddress, + Read, + Write, + ExceedRedirectCount, + Canceled, + SSLConnection, + SSLLoadingCerts, + SSLServerVerification, + UnsupportedMultipartBoundaryChars, + Compression, + }; + + inline std::ostream &operator<<(std::ostream &os, const Error &obj) { + os << static_cast::type>(obj); + return os; + } + + class Result { + public: + Result(std::unique_ptr &&res, Error err, + Headers &&request_headers = Headers{}) + : res_(std::move(res)), err_(err), + request_headers_(std::move(request_headers)) {} + // Response + operator bool() const { return res_ != nullptr; } + bool operator==(std::nullptr_t) const { return res_ == nullptr; } + bool operator!=(std::nullptr_t) const { return res_ != nullptr; } + const Response &value() const { return *res_; } + Response &value() { return *res_; } + const Response &operator*() const { return *res_; } + Response &operator*() { return *res_; } + const Response *operator->() const { return res_.get(); } + Response *operator->() { return res_.get(); } + + // Error + Error error() const { return err_; } + + // Request Headers + bool has_request_header(const char *key) const; + std::string get_request_header_value(const char *key, size_t id = 0) const; + template + T get_request_header_value(const char *key, size_t id = 0) const; + size_t get_request_header_value_count(const char *key) const; + + private: + std::unique_ptr res_; + Error err_; + Headers request_headers_; + }; + + class ClientImpl { + public: + explicit ClientImpl(const std::string &host); + + explicit ClientImpl(const std::string &host, int port); + + explicit ClientImpl(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path); + + virtual ~ClientImpl(); + + virtual bool is_valid() const; + + Result Get(const char *path); + Result Get(const char *path, const Headers &headers); + Result Get(const char *path, Progress progress); + Result Get(const char *path, const Headers &headers, Progress progress); + Result Get(const char *path, ContentReceiver content_receiver); + Result Get(const char *path, const Headers &headers, + ContentReceiver content_receiver); + Result Get(const char *path, ContentReceiver content_receiver, + Progress progress); + Result Get(const char *path, const Headers &headers, + ContentReceiver content_receiver, Progress progress); + Result Get(const char *path, ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const char *path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const char *path, ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress); + Result Get(const char *path, const Headers &headers, + ResponseHandler response_handler, ContentReceiver content_receiver, + Progress progress); + + Result Get(const char *path, const Params ¶ms, const Headers &headers, + Progress progress = nullptr); + Result Get(const char *path, const Params ¶ms, const Headers &headers, + ContentReceiver content_receiver, Progress progress = nullptr); + Result Get(const char *path, const Params ¶ms, const Headers &headers, + ResponseHandler response_handler, ContentReceiver content_receiver, + Progress progress = nullptr); + + Result Head(const char *path); + Result Head(const char *path, const Headers &headers); + + Result Post(const char *path); + Result Post(const char *path, const char *body, size_t content_length, + const char *content_type); + Result Post(const char *path, const Headers &headers, const char *body, + size_t content_length, const char *content_type); + Result Post(const char *path, const std::string &body, + const char *content_type); + Result Post(const char *path, const Headers &headers, const std::string &body, + const char *content_type); + Result Post(const char *path, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Post(const char *path, ContentProviderWithoutLength content_provider, + const char *content_type); + Result Post(const char *path, const Headers &headers, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Post(const char *path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const char *content_type); + Result Post(const char *path, const Params ¶ms); + Result Post(const char *path, const Headers &headers, const Params ¶ms); + Result Post(const char *path, const MultipartFormDataItems &items); + Result Post(const char *path, const Headers &headers, + const MultipartFormDataItems &items); + Result Post(const char *path, const Headers &headers, + const MultipartFormDataItems &items, const std::string &boundary); + + Result Put(const char *path); + Result Put(const char *path, const char *body, size_t content_length, + const char *content_type); + Result Put(const char *path, const Headers &headers, const char *body, + size_t content_length, const char *content_type); + Result Put(const char *path, const std::string &body, + const char *content_type); + Result Put(const char *path, const Headers &headers, const std::string &body, + const char *content_type); + Result Put(const char *path, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Put(const char *path, ContentProviderWithoutLength content_provider, + const char *content_type); + Result Put(const char *path, const Headers &headers, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Put(const char *path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const char *content_type); + Result Put(const char *path, const Params ¶ms); + Result Put(const char *path, const Headers &headers, const Params ¶ms); + + Result Patch(const char *path); + Result Patch(const char *path, const char *body, size_t content_length, + const char *content_type); + Result Patch(const char *path, const Headers &headers, const char *body, + size_t content_length, const char *content_type); + Result Patch(const char *path, const std::string &body, + const char *content_type); + Result Patch(const char *path, const Headers &headers, + const std::string &body, const char *content_type); + Result Patch(const char *path, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Patch(const char *path, ContentProviderWithoutLength content_provider, + const char *content_type); + Result Patch(const char *path, const Headers &headers, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Patch(const char *path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const char *content_type); + + Result Delete(const char *path); + Result Delete(const char *path, const Headers &headers); + Result Delete(const char *path, const char *body, size_t content_length, + const char *content_type); + Result Delete(const char *path, const Headers &headers, const char *body, + size_t content_length, const char *content_type); + Result Delete(const char *path, const std::string &body, + const char *content_type); + Result Delete(const char *path, const Headers &headers, + const std::string &body, const char *content_type); + + Result Options(const char *path); + Result Options(const char *path, const Headers &headers); + + bool send(Request &req, Response &res, Error &error); + Result send(const Request &req); + + size_t is_socket_open() const; + + void stop(); + + void set_default_headers(Headers headers); + + void set_address_family(int family); + void set_tcp_nodelay(bool on); + void set_socket_options(SocketOptions socket_options); + + void set_connection_timeout(time_t sec, time_t usec = 0); + template + void + set_connection_timeout(const std::chrono::duration &duration); + + void set_read_timeout(time_t sec, time_t usec = 0); + template + void set_read_timeout(const std::chrono::duration &duration); + + void set_write_timeout(time_t sec, time_t usec = 0); + template + void set_write_timeout(const std::chrono::duration &duration); + + void set_basic_auth(const char *username, const char *password); + void set_bearer_token_auth(const char *token); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_digest_auth(const char *username, const char *password); +#endif + + void set_keep_alive(bool on); + void set_follow_location(bool on); + + void set_url_encode(bool on); + + void set_compress(bool on); + + void set_decompress(bool on); + + void set_interface(const char *intf); + + void set_proxy(const char *host, int port); + void set_proxy_basic_auth(const char *username, const char *password); + void set_proxy_bearer_token_auth(const char *token); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_proxy_digest_auth(const char *username, const char *password); +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void enable_server_certificate_verification(bool enabled); +#endif + + void set_logger(Logger logger); + + protected: + struct Socket { + socket_t sock = INVALID_SOCKET; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSL *ssl = nullptr; +#endif + + bool is_open() const { return sock != INVALID_SOCKET; } + }; + + Result send_(Request &&req); + + virtual bool create_and_connect_socket(Socket &socket, Error &error); + + // All of: + // shutdown_ssl + // shutdown_socket + // close_socket + // should ONLY be called when socket_mutex_ is locked. + // Also, shutdown_ssl and close_socket should also NOT be called concurrently + // with a DIFFERENT thread sending requests using that socket. + virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully); + void shutdown_socket(Socket &socket); + void close_socket(Socket &socket); + + bool process_request(Stream &strm, Request &req, Response &res, + bool close_connection, Error &error); + + bool write_content_with_provider(Stream &strm, const Request &req, + Error &error); + + void copy_settings(const ClientImpl &rhs); + + // Socket endoint information + const std::string host_; + const int port_; + const std::string host_and_port_; + + // Current open socket + Socket socket_; + mutable std::mutex socket_mutex_; + std::recursive_mutex request_mutex_; + + // These are all protected under socket_mutex + size_t socket_requests_in_flight_ = 0; + std::thread::id socket_requests_are_from_thread_ = std::thread::id(); + bool socket_should_be_closed_when_request_is_done_ = false; + + // Default headers + Headers default_headers_; + + // Settings + std::string client_cert_path_; + std::string client_key_path_; + + time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND; + time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND; + time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; + time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; + time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND; + time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND; + + std::string basic_auth_username_; + std::string basic_auth_password_; + std::string bearer_token_auth_token_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + std::string digest_auth_username_; + std::string digest_auth_password_; +#endif + + bool keep_alive_ = false; + bool follow_location_ = false; + + bool url_encode_ = true; + + int address_family_ = AF_UNSPEC; + bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY; + SocketOptions socket_options_ = nullptr; + + bool compress_ = false; + bool decompress_ = true; + + std::string interface_; + + std::string proxy_host_; + int proxy_port_ = -1; + + std::string proxy_basic_auth_username_; + std::string proxy_basic_auth_password_; + std::string proxy_bearer_token_auth_token_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + std::string proxy_digest_auth_username_; + std::string proxy_digest_auth_password_; +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + bool server_certificate_verification_ = true; +#endif + Logger logger_; - // TODO: Use thread pool... - std::mutex running_threads_mutex_; - int running_threads_; + private: + socket_t create_client_socket(Error &error) const; + bool read_response_line(Stream &strm, const Request &req, Response &res); + bool write_request(Stream &strm, Request &req, bool close_connection, + Error &error); + bool redirect(Request &req, Response &res, Error &error); + bool handle_request(Stream &strm, Request &req, Response &res, + bool close_connection, Error &error); + std::unique_ptr send_with_content_provider( + Request &req, + // const char *method, const char *path, const Headers &headers, + const char *body, size_t content_length, ContentProvider content_provider, + ContentProviderWithoutLength content_provider_without_length, + const char *content_type, Error &error); + Result send_with_content_provider( + const char *method, const char *path, const Headers &headers, + const char *body, size_t content_length, ContentProvider content_provider, + ContentProviderWithoutLength content_provider_without_length, + const char *content_type); + + virtual bool process_socket(const Socket &socket, + std::function callback); + virtual bool is_ssl() const; }; class Client { public: - Client(const char *host, int port = 80, time_t timeout_sec = 300); + // Universal interface + explicit Client(const char *scheme_host_port); - virtual ~Client(); + explicit Client(const char *scheme_host_port, + const std::string &client_cert_path, + const std::string &client_key_path); - virtual bool is_valid() const; + // HTTP only interface + explicit Client(const std::string &host, int port); - std::shared_ptr Get(const char *path, Progress progress = nullptr); - std::shared_ptr Get(const char *path, const Headers &headers, - Progress progress = nullptr); + explicit Client(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path); - std::shared_ptr Head(const char *path); - std::shared_ptr Head(const char *path, const Headers &headers); + ~Client(); - std::shared_ptr Post(const char *path, const std::string &body, - const char *content_type); - std::shared_ptr Post(const char *path, const Headers &headers, - const std::string &body, - const char *content_type); + bool is_valid() const; - std::shared_ptr Post(const char *path, const Params ¶ms); - std::shared_ptr Post(const char *path, const Headers &headers, - const Params ¶ms); + Result Get(const char *path); + Result Get(const char *path, const Headers &headers); + Result Get(const char *path, Progress progress); + Result Get(const char *path, const Headers &headers, Progress progress); + Result Get(const char *path, ContentReceiver content_receiver); + Result Get(const char *path, const Headers &headers, + ContentReceiver content_receiver); + Result Get(const char *path, ContentReceiver content_receiver, + Progress progress); + Result Get(const char *path, const Headers &headers, + ContentReceiver content_receiver, Progress progress); + Result Get(const char *path, ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const char *path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver); + Result Get(const char *path, const Headers &headers, + ResponseHandler response_handler, ContentReceiver content_receiver, + Progress progress); + Result Get(const char *path, ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress); - std::shared_ptr Put(const char *path, const std::string &body, - const char *content_type); - std::shared_ptr Put(const char *path, const Headers &headers, - const std::string &body, - const char *content_type); + Result Get(const char *path, const Params ¶ms, const Headers &headers, + Progress progress = nullptr); + Result Get(const char *path, const Params ¶ms, const Headers &headers, + ContentReceiver content_receiver, Progress progress = nullptr); + Result Get(const char *path, const Params ¶ms, const Headers &headers, + ResponseHandler response_handler, ContentReceiver content_receiver, + Progress progress = nullptr); - std::shared_ptr Patch(const char *path, const std::string &body, - const char *content_type); - std::shared_ptr Patch(const char *path, const Headers &headers, - const std::string &body, - const char *content_type); + Result Head(const char *path); + Result Head(const char *path, const Headers &headers); - std::shared_ptr Delete(const char *path, - const std::string &body = std::string(), - const char *content_type = nullptr); - std::shared_ptr Delete(const char *path, const Headers &headers, - const std::string &body = std::string(), - const char *content_type = nullptr); + Result Post(const char *path); + Result Post(const char *path, const char *body, size_t content_length, + const char *content_type); + Result Post(const char *path, const Headers &headers, const char *body, + size_t content_length, const char *content_type); + Result Post(const char *path, const std::string &body, + const char *content_type); + Result Post(const char *path, const Headers &headers, const std::string &body, + const char *content_type); + Result Post(const char *path, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Post(const char *path, ContentProviderWithoutLength content_provider, + const char *content_type); + Result Post(const char *path, const Headers &headers, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Post(const char *path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const char *content_type); + Result Post(const char *path, const Params ¶ms); + Result Post(const char *path, const Headers &headers, const Params ¶ms); + Result Post(const char *path, const MultipartFormDataItems &items); + Result Post(const char *path, const Headers &headers, + const MultipartFormDataItems &items); + Result Post(const char *path, const Headers &headers, + const MultipartFormDataItems &items, const std::string &boundary); + Result Put(const char *path); + Result Put(const char *path, const char *body, size_t content_length, + const char *content_type); + Result Put(const char *path, const Headers &headers, const char *body, + size_t content_length, const char *content_type); + Result Put(const char *path, const std::string &body, + const char *content_type); + Result Put(const char *path, const Headers &headers, const std::string &body, + const char *content_type); + Result Put(const char *path, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Put(const char *path, ContentProviderWithoutLength content_provider, + const char *content_type); + Result Put(const char *path, const Headers &headers, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Put(const char *path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const char *content_type); + Result Put(const char *path, const Params ¶ms); + Result Put(const char *path, const Headers &headers, const Params ¶ms); + Result Patch(const char *path); + Result Patch(const char *path, const char *body, size_t content_length, + const char *content_type); + Result Patch(const char *path, const Headers &headers, const char *body, + size_t content_length, const char *content_type); + Result Patch(const char *path, const std::string &body, + const char *content_type); + Result Patch(const char *path, const Headers &headers, + const std::string &body, const char *content_type); + Result Patch(const char *path, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Patch(const char *path, ContentProviderWithoutLength content_provider, + const char *content_type); + Result Patch(const char *path, const Headers &headers, size_t content_length, + ContentProvider content_provider, const char *content_type); + Result Patch(const char *path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const char *content_type); - std::shared_ptr Options(const char *path); - std::shared_ptr Options(const char *path, const Headers &headers); + Result Delete(const char *path); + Result Delete(const char *path, const Headers &headers); + Result Delete(const char *path, const char *body, size_t content_length, + const char *content_type); + Result Delete(const char *path, const Headers &headers, const char *body, + size_t content_length, const char *content_type); + Result Delete(const char *path, const std::string &body, + const char *content_type); + Result Delete(const char *path, const Headers &headers, + const std::string &body, const char *content_type); - bool send(Request &req, Response &res); + Result Options(const char *path); + Result Options(const char *path, const Headers &headers); - protected: - bool process_request(Stream &strm, Request &req, Response &res, - bool &connection_close); + bool send(Request &req, Response &res, Error &error); + Result send(const Request &req); - const std::string host_; - const int port_; - time_t timeout_sec_; - const std::string host_and_port_; + size_t is_socket_open() const; + + void stop(); + + void set_default_headers(Headers headers); + + void set_address_family(int family); + void set_tcp_nodelay(bool on); + void set_socket_options(SocketOptions socket_options); + + void set_connection_timeout(time_t sec, time_t usec = 0); + template + void + set_connection_timeout(const std::chrono::duration &duration); + + void set_read_timeout(time_t sec, time_t usec = 0); + template + void set_read_timeout(const std::chrono::duration &duration); + + void set_write_timeout(time_t sec, time_t usec = 0); + template + void set_write_timeout(const std::chrono::duration &duration); + + void set_basic_auth(const char *username, const char *password); + void set_bearer_token_auth(const char *token); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_digest_auth(const char *username, const char *password); +#endif + + void set_keep_alive(bool on); + void set_follow_location(bool on); + + void set_url_encode(bool on); + + void set_compress(bool on); + + void set_decompress(bool on); + + void set_interface(const char *intf); + + void set_proxy(const char *host, int port); + void set_proxy_basic_auth(const char *username, const char *password); + void set_proxy_bearer_token_auth(const char *token); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_proxy_digest_auth(const char *username, const char *password); +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void enable_server_certificate_verification(bool enabled); +#endif + + void set_logger(Logger logger); + + // SSL +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_ca_cert_path(const char *ca_cert_file_path, + const char *ca_cert_dir_path = nullptr); + + void set_ca_cert_store(X509_STORE *ca_cert_store); + + long get_openssl_verify_result() const; + + SSL_CTX *ssl_context() const; +#endif private: - socket_t create_client_socket() const; - bool read_response_line(Stream &strm, Response &res); - void write_request(Stream &strm, Request &req); + std::unique_ptr cli_; - virtual bool read_and_close_socket(socket_t sock, Request &req, - Response &res); - virtual bool is_ssl() const; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + bool is_ssl_ = false; +#endif }; #ifdef CPPHTTPLIB_OPENSSL_SUPPORT - class SSLSocketStream : public Stream { -public: - SSLSocketStream(socket_t sock, SSL *ssl); - virtual ~SSLSocketStream(); - - virtual int read(char *ptr, size_t size); - virtual int write(const char *ptr, size_t size); - virtual int write(const char *ptr); - virtual std::string get_remote_addr() const; - -private: - socket_t sock_; - SSL *ssl_; -}; - -class SSLServer : public Server { + class SSLServer : public Server { public: SSLServer(const char *cert_path, const char *private_key_path, const char *client_ca_cert_file_path = nullptr, const char *client_ca_cert_dir_path = nullptr); - virtual ~SSLServer(); + SSLServer(X509 *cert, EVP_PKEY *private_key, + X509_STORE *client_ca_cert_store = nullptr); - virtual bool is_valid() const; + ~SSLServer() override; + + bool is_valid() const override; private: - virtual bool read_and_close_socket(socket_t sock); + bool process_and_close_socket(socket_t sock) override; SSL_CTX *ctx_; std::mutex ctx_mutex_; }; -class SSLClient : public Client { +class SSLClient : public ClientImpl { public: - SSLClient(const char *host, int port = 443, time_t timeout_sec = 300, - const char *client_cert_path = nullptr, - const char *client_key_path = nullptr); + explicit SSLClient(const std::string &host); - virtual ~SSLClient(); + explicit SSLClient(const std::string &host, int port); - virtual bool is_valid() const; + explicit SSLClient(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path); - void set_ca_cert_path(const char *ca_ceert_file_path, + explicit SSLClient(const std::string &host, int port, X509 *client_cert, + EVP_PKEY *client_key); + + ~SSLClient() override; + + bool is_valid() const override; + + void set_ca_cert_path(const char *ca_cert_file_path, const char *ca_cert_dir_path = nullptr); - void enable_server_certificate_verification(bool enabled); + + void set_ca_cert_store(X509_STORE *ca_cert_store); long get_openssl_verify_result() const; + SSL_CTX *ssl_context() const; + private: - virtual bool read_and_close_socket(socket_t sock, Request &req, - Response &res); - virtual bool is_ssl() const; + bool create_and_connect_socket(Socket &socket, Error &error) override; + void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override; + void shutdown_ssl_impl(Socket &socket, bool shutdown_socket); + + bool process_socket(const Socket &socket, + std::function callback) override; + bool is_ssl() const override; + + bool connect_with_proxy(Socket &sock, Response &res, bool &success, + Error &error); + bool initialize_ssl(Socket &socket, Error &error); + + bool load_certs(); bool verify_host(X509 *server_cert) const; bool verify_host_with_subject_alt_name(X509 *server_cert) const; @@ -442,285 +1434,127 @@ private: SSL_CTX *ctx_; std::mutex ctx_mutex_; + std::once_flag initialize_cert_; + std::vector host_components_; + std::string ca_cert_file_path_; std::string ca_cert_dir_path_; - bool server_certificate_verification_ = false; long verify_result_ = 0; + + friend class ClientImpl; }; #endif +// ---------------------------------------------------------------------------- + /* * Implementation */ + namespace detail { - template void split(const char *b, const char *e, char d, Fn fn) { - int i = 0; - int beg = 0; - - while (e ? (b + i != e) : (b[i] != '\0')) { - if (b[i] == d) { - fn(&b[beg], &b[i]); - beg = i + 1; - } - i++; - } - - if (i) { fn(&b[beg], &b[i]); } - } - -// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer` -// to store data. The call can set memory on stack for performance. - class stream_line_reader { - public: - stream_line_reader(Stream &strm, char *fixed_buffer, size_t fixed_buffer_size) - : strm_(strm), fixed_buffer_(fixed_buffer), - fixed_buffer_size_(fixed_buffer_size) {} - - const char *ptr() const { - if (glowable_buffer_.empty()) { - return fixed_buffer_; - } else { - return glowable_buffer_.data(); - } - } - - size_t size() const { - if (glowable_buffer_.empty()) { - return fixed_buffer_used_size_; - } else { - return glowable_buffer_.size(); - } - } - - bool getline() { - fixed_buffer_used_size_ = 0; - glowable_buffer_.clear(); - - for (size_t i = 0;; i++) { - char byte; - auto n = strm_.read(&byte, 1); - - if (n < 0) { - return false; - } else if (n == 0) { - if (i == 0) { - return false; - } else { - break; - } - } - - append(byte); - - if (byte == '\n') { break; } - } - + inline bool is_hex(char c, int &v) { + if (0x20 <= c && isdigit(c)) { + v = c - '0'; + return true; + } else if ('A' <= c && c <= 'F') { + v = c - 'A' + 10; + return true; + } else if ('a' <= c && c <= 'f') { + v = c - 'a' + 10; return true; } + return false; + } - private: - void append(char c) { - if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) { - fixed_buffer_[fixed_buffer_used_size_++] = c; - fixed_buffer_[fixed_buffer_used_size_] = '\0'; + inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt, + int &val) { + if (i >= s.size()) { return false; } + + val = 0; + for (; cnt; i++, cnt--) { + if (!s[i]) { return false; } + int v = 0; + if (is_hex(s[i], v)) { + val = val * 16 + v; } else { - if (glowable_buffer_.empty()) { - assert(fixed_buffer_[fixed_buffer_used_size_] == '\0'); - glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_); - } - glowable_buffer_ += c; - } - } - - Stream &strm_; - char *fixed_buffer_; - const size_t fixed_buffer_size_; - size_t fixed_buffer_used_size_; - std::string glowable_buffer_; - }; - - inline int close_socket(socket_t sock) { -#ifdef _WIN32 - return closesocket(sock); -#else - return close(sock); -#endif - } - - inline int select_read(socket_t sock, time_t sec, time_t usec) { - fd_set fds; - FD_ZERO(&fds); - FD_SET(sock, &fds); - - timeval tv; - tv.tv_sec = static_cast(sec); - tv.tv_usec = static_cast(usec); - - return select(static_cast(sock + 1), &fds, nullptr, nullptr, &tv); - } - - inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) { - fd_set fdsr; - FD_ZERO(&fdsr); - FD_SET(sock, &fdsr); - - auto fdsw = fdsr; - auto fdse = fdsr; - - timeval tv; - tv.tv_sec = static_cast(sec); - tv.tv_usec = static_cast(usec); - - if (select(static_cast(sock + 1), &fdsr, &fdsw, &fdse, &tv) < 0) { - return false; - } else if (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw)) { - int error = 0; - socklen_t len = sizeof(error); - if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&error, &len) < 0 || - error) { return false; } - } else { - return false; } - return true; } - template - inline bool read_and_close_socket(socket_t sock, size_t keep_alive_max_count, - T callback) { - bool ret = false; - - if (keep_alive_max_count > 0) { - auto count = keep_alive_max_count; - while (count > 0 && - detail::select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, - CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0) { - SocketStream strm(sock); - auto last_connection = count == 1; - auto connection_close = false; - - ret = callback(strm, last_connection, connection_close); - if (!ret || connection_close) { break; } - - count--; - } - } else { - SocketStream strm(sock); - auto dummy_connection_close = false; - ret = callback(strm, true, dummy_connection_close); - } - - close_socket(sock); + inline std::string from_i_to_hex(size_t n) { + const char *charset = "0123456789abcdef"; + std::string ret; + do { + ret = charset[n & 15] + ret; + n >>= 4; + } while (n > 0); return ret; } - inline int shutdown_socket(socket_t sock) { -#ifdef _WIN32 - return shutdown(sock, SD_BOTH); -#else - return shutdown(sock, SHUT_RDWR); -#endif - } - - template - socket_t create_socket(const char *host, int port, Fn fn, - int socket_flags = 0) { -#ifdef _WIN32 - #define SO_SYNCHRONOUS_NONALERT 0x20 -#define SO_OPENTYPE 0x7008 - - int opt = SO_SYNCHRONOUS_NONALERT; - setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&opt, - sizeof(opt)); -#endif - - // Get address info - struct addrinfo hints; - struct addrinfo *result; - - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = socket_flags; - hints.ai_protocol = 0; - - auto service = std::to_string(port); - - if (getaddrinfo(host, service.c_str(), &hints, &result)) { - return INVALID_SOCKET; + inline size_t to_utf8(int code, char *buff) { + if (code < 0x0080) { + buff[0] = (code & 0x7F); + return 1; + } else if (code < 0x0800) { + buff[0] = static_cast(0xC0 | ((code >> 6) & 0x1F)); + buff[1] = static_cast(0x80 | (code & 0x3F)); + return 2; + } else if (code < 0xD800) { + buff[0] = static_cast(0xE0 | ((code >> 12) & 0xF)); + buff[1] = static_cast(0x80 | ((code >> 6) & 0x3F)); + buff[2] = static_cast(0x80 | (code & 0x3F)); + return 3; + } else if (code < 0xE000) { // D800 - DFFF is invalid... + return 0; + } else if (code < 0x10000) { + buff[0] = static_cast(0xE0 | ((code >> 12) & 0xF)); + buff[1] = static_cast(0x80 | ((code >> 6) & 0x3F)); + buff[2] = static_cast(0x80 | (code & 0x3F)); + return 3; + } else if (code < 0x110000) { + buff[0] = static_cast(0xF0 | ((code >> 18) & 0x7)); + buff[1] = static_cast(0x80 | ((code >> 12) & 0x3F)); + buff[2] = static_cast(0x80 | ((code >> 6) & 0x3F)); + buff[3] = static_cast(0x80 | (code & 0x3F)); + return 4; } - for (auto rp = result; rp; rp = rp->ai_next) { - // Create a socket -#ifdef _WIN32 - auto sock = WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, - nullptr, 0, WSA_FLAG_NO_HANDLE_INHERIT); -#else - auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); -#endif - if (sock == INVALID_SOCKET) { continue; } - -#ifndef _WIN32 - if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { continue; } -#endif - - // Make 'reuse address' option available - int yes = 1; - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)); -#ifdef SO_REUSEPORT - setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (char *)&yes, sizeof(yes)); -#endif - - // bind or connect - if (fn(sock, *rp)) { - freeaddrinfo(result); - return sock; - } - - close_socket(sock); - } - - freeaddrinfo(result); - return INVALID_SOCKET; + // NOTREACHED + return 0; } - inline void set_nonblocking(socket_t sock, bool nonblocking) { -#ifdef _WIN32 - auto flags = nonblocking ? 1UL : 0UL; - ioctlsocket(sock, FIONBIO, &flags); -#else - auto flags = fcntl(sock, F_GETFL, 0); - fcntl(sock, F_SETFL, - nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK))); -#endif - } +// NOTE: This code came up with the following stackoverflow post: +// https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c + inline std::string base64_encode(const std::string &in) { + static const auto lookup = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - inline bool is_connection_error() { -#ifdef _WIN32 - return WSAGetLastError() != WSAEWOULDBLOCK; -#else - return errno != EINPROGRESS; -#endif - } + std::string out; + out.reserve(in.size()); - inline std::string get_remote_addr(socket_t sock) { - struct sockaddr_storage addr; - socklen_t len = sizeof(addr); + int val = 0; + int valb = -6; - if (!getpeername(sock, (struct sockaddr *)&addr, &len)) { - char ipstr[NI_MAXHOST]; - - if (!getnameinfo((struct sockaddr *)&addr, len, ipstr, sizeof(ipstr), - nullptr, 0, NI_NUMERICHOST)) { - return ipstr; + for (auto c : in) { + val = (val << 8) + static_cast(c); + valb += 8; + while (valb >= 0) { + out.push_back(lookup[(val >> valb) & 0x3F]); + valb -= 6; } } - return std::string(); + if (valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); } + + while (out.size() % 4) { + out.push_back('='); + } + + return out; } inline bool is_file(const std::string &path) { @@ -770,239 +1604,32 @@ private: return true; } - inline void read_file(const std::string &path, std::string &out) { - std::ifstream fs(path, std::ios_base::binary); - fs.seekg(0, std::ios_base::end); - auto size = fs.tellg(); - fs.seekg(0); - out.resize(static_cast(size)); - fs.read(&out[0], size); - } + inline std::string encode_query_param(const std::string &value) { + std::ostringstream escaped; + escaped.fill('0'); + escaped << std::hex; - inline std::string file_extension(const std::string &path) { - std::smatch m; - auto pat = std::regex("\\.([a-zA-Z0-9]+)$"); - if (std::regex_search(path, m, pat)) { return m[1].str(); } - return std::string(); - } - - inline const char *find_content_type(const std::string &path) { - auto ext = file_extension(path); - if (ext == "txt") { - return "text/plain"; - } else if (ext == "html") { - return "text/html"; - } else if (ext == "css") { - return "text/css"; - } else if (ext == "jpeg" || ext == "jpg") { - return "image/jpg"; - } else if (ext == "png") { - return "image/png"; - } else if (ext == "gif") { - return "image/gif"; - } else if (ext == "svg") { - return "image/svg+xml"; - } else if (ext == "ico") { - return "image/x-icon"; - } else if (ext == "json") { - return "application/json"; - } else if (ext == "pdf") { - return "application/pdf"; - } else if (ext == "js") { - return "application/javascript"; - } else if (ext == "xml") { - return "application/xml"; - } else if (ext == "xhtml") { - return "application/xhtml+xml"; - } - return nullptr; - } - - inline const char *status_message(int status) { - switch (status) { - case 200: return "OK"; - case 301: return "Moved Permanently"; - case 302: return "Found"; - case 303: return "See Other"; - case 304: return "Not Modified"; - case 400: return "Bad Request"; - case 403: return "Forbidden"; - case 404: return "Not Found"; - case 413: return "Payload Too Large"; - case 414: return "Request-URI Too Long"; - case 415: return "Unsupported Media Type"; - default: - case 500: return "Internal Server Error"; - } - } - - inline bool has_header(const Headers &headers, const char *key) { - return headers.find(key) != headers.end(); - } - - inline const char *get_header_value(const Headers &headers, const char *key, - size_t id = 0, const char *def = nullptr) { - auto it = headers.find(key); - std::advance(it, id); - if (it != headers.end()) { return it->second.c_str(); } - return def; - } - - inline uint64_t get_header_value_uint64(const Headers &headers, const char *key, - int def = 0) { - auto it = headers.find(key); - if (it != headers.end()) { - return std::strtoull(it->second.data(), nullptr, 10); - } - return def; - } - - inline bool read_headers(Stream &strm, Headers &headers) { - static std::regex re(R"((.+?):\s*(.+?)\s*\r\n)"); - - const auto bufsiz = 2048; - char buf[bufsiz]; - - stream_line_reader reader(strm, buf, bufsiz); - - for (;;) { - if (!reader.getline()) { return false; } - if (!strcmp(reader.ptr(), "\r\n")) { break; } - std::cmatch m; - if (std::regex_match(reader.ptr(), m, re)) { - auto key = std::string(m[1]); - auto val = std::string(m[2]); - headers.emplace(key, val); + for (auto c : value) { + if (std::isalnum(static_cast(c)) || c == '-' || c == '_' || + c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' || + c == ')') { + escaped << c; + } else { + escaped << std::uppercase; + escaped << '%' << std::setw(2) + << static_cast(static_cast(c)); + escaped << std::nouppercase; } } - return true; - } - - inline bool read_content_with_length(Stream &strm, std::string &out, size_t len, - Progress progress) { - out.assign(len, 0); - size_t r = 0; - while (r < len) { - auto n = strm.read(&out[r], len - r); - if (n <= 0) { return false; } - - r += n; - - if (progress) { - if (!progress(r, len)) { return false; } - } - } - - return true; - } - - inline void skip_content_with_length(Stream &strm, size_t len) { - char buf[CPPHTTPLIB_RECV_BUFSIZ]; - size_t r = 0; - while (r < len) { - auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ); - if (n <= 0) { return; } - r += n; - } - } - - inline bool read_content_without_length(Stream &strm, std::string &out) { - char buf[CPPHTTPLIB_RECV_BUFSIZ]; - for (;;) { - auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ); - if (n < 0) { - return false; - } else if (n == 0) { - return true; - } - out.append(buf, n); - } - - return true; - } - - inline bool read_content_chunked(Stream &strm, std::string &out) { - const auto bufsiz = 16; - char buf[bufsiz]; - - stream_line_reader reader(strm, buf, bufsiz); - - if (!reader.getline()) { return false; } - - auto chunk_len = std::stoi(reader.ptr(), 0, 16); - - while (chunk_len > 0) { - std::string chunk; - if (!read_content_with_length(strm, chunk, chunk_len, nullptr)) { - return false; - } - - if (!reader.getline()) { return false; } - - if (strcmp(reader.ptr(), "\r\n")) { break; } - - out += chunk; - - if (!reader.getline()) { return false; } - - chunk_len = std::stoi(reader.ptr(), 0, 16); - } - - if (chunk_len == 0) { - // Reader terminator after chunks - if (!reader.getline() || strcmp(reader.ptr(), "\r\n")) return false; - } - - return true; - } - - template - bool read_content(Stream &strm, T &x, uint64_t payload_max_length, - bool &exceed_payload_max_length, - Progress progress = Progress()) { - if (has_header(x.headers, "Content-Length")) { - auto len = get_header_value_uint64(x.headers, "Content-Length", 0); - if (len == 0) { - const auto &encoding = - get_header_value(x.headers, "Transfer-Encoding", 0, ""); - if (!strcasecmp(encoding, "chunked")) { - return read_content_chunked(strm, x.body); - } - } - - if ((len > payload_max_length) || - // For 32-bit platform - (sizeof(size_t) < sizeof(uint64_t) && - len > std::numeric_limits::max())) { - exceed_payload_max_length = true; - skip_content_with_length(strm, len); - return false; - } - - return read_content_with_length(strm, x.body, len, progress); - } else { - const auto &encoding = - get_header_value(x.headers, "Transfer-Encoding", 0, ""); - if (!strcasecmp(encoding, "chunked")) { - return read_content_chunked(strm, x.body); - } - return read_content_without_length(strm, x.body); - } - return true; - } - - template inline void write_headers(Stream &strm, const T &info) { - for (const auto &x : info.headers) { - strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str()); - } - strm.write("\r\n"); + return escaped.str(); } inline std::string encode_url(const std::string &s) { std::string result; + result.reserve(s.size()); - for (auto i = 0; s[i]; i++) { + for (size_t i = 0; s[i]; i++) { switch (s[i]) { case ' ': result += "%20"; break; case '+': result += "%2B"; break; @@ -1010,16 +1637,16 @@ private: case '\n': result += "%0A"; break; case '\'': result += "%27"; break; case ',': result += "%2C"; break; - case ':': result += "%3A"; break; + // case ':': result += "%3A"; break; // ok? probably... case ';': result += "%3B"; break; default: auto c = static_cast(s[i]); if (c >= 0x80) { result += '%'; char hex[4]; - size_t len = snprintf(hex, sizeof(hex) - 1, "%02X", c); + auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c); assert(len == 2); - result.append(hex, len); + result.append(hex, static_cast(len)); } else { result += s[i]; } @@ -1030,80 +1657,8 @@ private: return result; } - inline bool is_hex(char c, int &v) { - if (0x20 <= c && isdigit(c)) { - v = c - '0'; - return true; - } else if ('A' <= c && c <= 'F') { - v = c - 'A' + 10; - return true; - } else if ('a' <= c && c <= 'f') { - v = c - 'a' + 10; - return true; - } - return false; - } - - inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt, - int &val) { - if (i >= s.size()) { return false; } - - val = 0; - for (; cnt; i++, cnt--) { - if (!s[i]) { return false; } - int v = 0; - if (is_hex(s[i], v)) { - val = val * 16 + v; - } else { - return false; - } - } - return true; - } - - inline std::string from_i_to_hex(uint64_t n) { - const char *charset = "0123456789abcdef"; - std::string ret; - do { - ret = charset[n & 15] + ret; - n >>= 4; - } while (n > 0); - return ret; - } - - inline size_t to_utf8(int code, char *buff) { - if (code < 0x0080) { - buff[0] = (code & 0x7F); - return 1; - } else if (code < 0x0800) { - buff[0] = (0xC0 | ((code >> 6) & 0x1F)); - buff[1] = (0x80 | (code & 0x3F)); - return 2; - } else if (code < 0xD800) { - buff[0] = (0xE0 | ((code >> 12) & 0xF)); - buff[1] = (0x80 | ((code >> 6) & 0x3F)); - buff[2] = (0x80 | (code & 0x3F)); - return 3; - } else if (code < 0xE000) { // D800 - DFFF is invalid... - return 0; - } else if (code < 0x10000) { - buff[0] = (0xE0 | ((code >> 12) & 0xF)); - buff[1] = (0x80 | ((code >> 6) & 0x3F)); - buff[2] = (0x80 | (code & 0x3F)); - return 3; - } else if (code < 0x110000) { - buff[0] = (0xF0 | ((code >> 18) & 0x7)); - buff[1] = (0x80 | ((code >> 12) & 0x3F)); - buff[2] = (0x80 | ((code >> 6) & 0x3F)); - buff[3] = (0x80 | (code & 0x3F)); - return 4; - } - - // NOTREACHED - return 0; - } - - inline std::string decode_url(const std::string &s) { + inline std::string decode_url(const std::string &s, + bool convert_plus_to_space) { std::string result; for (size_t i = 0; i < s.size(); i++) { @@ -1123,13 +1678,13 @@ private: int val = 0; if (from_hex_to_i(s, i + 1, 2, val)) { // 2 digits hex codes - result += val; + result += static_cast(val); i += 2; // '00' } else { result += s[i]; } } - } else if (s[i] == '+') { + } else if (convert_plus_to_space && s[i] == '+') { result += ' '; } else { result += s[i]; @@ -1139,18 +1694,1568 @@ private: return result; } + inline void read_file(const std::string &path, std::string &out) { + std::ifstream fs(path, std::ios_base::binary); + fs.seekg(0, std::ios_base::end); + auto size = fs.tellg(); + fs.seekg(0); + out.resize(static_cast(size)); + fs.read(&out[0], static_cast(size)); + } + + inline std::string file_extension(const std::string &path) { + std::smatch m; + static auto re = std::regex("\\.([a-zA-Z0-9]+)$"); + if (std::regex_search(path, m, re)) { return m[1].str(); } + return std::string(); + } + + inline bool is_space_or_tab(char c) { return c == ' ' || c == '\t'; } + + inline std::pair trim(const char *b, const char *e, size_t left, + size_t right) { + while (b + left < e && is_space_or_tab(b[left])) { + left++; + } + while (right > 0 && is_space_or_tab(b[right - 1])) { + right--; + } + return std::make_pair(left, right); + } + + inline std::string trim_copy(const std::string &s) { + auto r = trim(s.data(), s.data() + s.size(), 0, s.size()); + return s.substr(r.first, r.second - r.first); + } + + template void split(const char *b, const char *e, char d, Fn fn) { + size_t i = 0; + size_t beg = 0; + + while (e ? (b + i < e) : (b[i] != '\0')) { + if (b[i] == d) { + auto r = trim(b, e, beg, i); + if (r.first < r.second) { fn(&b[r.first], &b[r.second]); } + beg = i + 1; + } + i++; + } + + if (i) { + auto r = trim(b, e, beg, i); + if (r.first < r.second) { fn(&b[r.first], &b[r.second]); } + } + } + +// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer` +// to store data. The call can set memory on stack for performance. + class stream_line_reader { + public: + stream_line_reader(Stream &strm, char *fixed_buffer, size_t fixed_buffer_size) + : strm_(strm), fixed_buffer_(fixed_buffer), + fixed_buffer_size_(fixed_buffer_size) {} + + const char *ptr() const { + if (glowable_buffer_.empty()) { + return fixed_buffer_; + } else { + return glowable_buffer_.data(); + } + } + + size_t size() const { + if (glowable_buffer_.empty()) { + return fixed_buffer_used_size_; + } else { + return glowable_buffer_.size(); + } + } + + bool end_with_crlf() const { + auto end = ptr() + size(); + return size() >= 2 && end[-2] == '\r' && end[-1] == '\n'; + } + + bool getline() { + fixed_buffer_used_size_ = 0; + glowable_buffer_.clear(); + + for (size_t i = 0;; i++) { + char byte; + auto n = strm_.read(&byte, 1); + + if (n < 0) { + return false; + } else if (n == 0) { + if (i == 0) { + return false; + } else { + break; + } + } + + append(byte); + + if (byte == '\n') { break; } + } + + return true; + } + + private: + void append(char c) { + if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) { + fixed_buffer_[fixed_buffer_used_size_++] = c; + fixed_buffer_[fixed_buffer_used_size_] = '\0'; + } else { + if (glowable_buffer_.empty()) { + assert(fixed_buffer_[fixed_buffer_used_size_] == '\0'); + glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_); + } + glowable_buffer_ += c; + } + } + + Stream &strm_; + char *fixed_buffer_; + const size_t fixed_buffer_size_; + size_t fixed_buffer_used_size_ = 0; + std::string glowable_buffer_; + }; + + inline int close_socket(socket_t sock) { +#ifdef _WIN32 + return closesocket(sock); +#else + return close(sock); +#endif + } + + template inline ssize_t handle_EINTR(T fn) { + ssize_t res = false; + while (true) { + res = fn(); + if (res < 0 && errno == EINTR) { continue; } + break; + } + return res; + } + + inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) { +#ifdef CPPHTTPLIB_USE_POLL + struct pollfd pfd_read; + pfd_read.fd = sock; + pfd_read.events = POLLIN; + + auto timeout = static_cast(sec * 1000 + usec / 1000); + + return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); +#else +#ifndef _WIN32 + if (sock >= FD_SETSIZE) { return 1; } +#endif + + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); + + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); + + return handle_EINTR([&]() { + return select(static_cast(sock + 1), &fds, nullptr, nullptr, &tv); + }); +#endif + } + + inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) { +#ifdef CPPHTTPLIB_USE_POLL + struct pollfd pfd_read; + pfd_read.fd = sock; + pfd_read.events = POLLOUT; + + auto timeout = static_cast(sec * 1000 + usec / 1000); + + return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); +#else +#ifndef _WIN32 + if (sock >= FD_SETSIZE) { return 1; } +#endif + + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); + + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); + + return handle_EINTR([&]() { + return select(static_cast(sock + 1), nullptr, &fds, nullptr, &tv); + }); +#endif + } + + inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) { +#ifdef CPPHTTPLIB_USE_POLL + struct pollfd pfd_read; + pfd_read.fd = sock; + pfd_read.events = POLLIN | POLLOUT; + + auto timeout = static_cast(sec * 1000 + usec / 1000); + + auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); + + if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) { + int error = 0; + socklen_t len = sizeof(error); + auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR, + reinterpret_cast(&error), &len); + return res >= 0 && !error; + } + return false; +#else +#ifndef _WIN32 + if (sock >= FD_SETSIZE) { return false; } +#endif + + fd_set fdsr; + FD_ZERO(&fdsr); + FD_SET(sock, &fdsr); + + auto fdsw = fdsr; + auto fdse = fdsr; + + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); + + auto ret = handle_EINTR([&]() { + return select(static_cast(sock + 1), &fdsr, &fdsw, &fdse, &tv); + }); + + if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) { + int error = 0; + socklen_t len = sizeof(error); + return getsockopt(sock, SOL_SOCKET, SO_ERROR, + reinterpret_cast(&error), &len) >= 0 && + !error; + } + return false; +#endif + } + + class SocketStream : public Stream { + public: + SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec, + time_t write_timeout_sec, time_t write_timeout_usec); + ~SocketStream() override; + + bool is_readable() const override; + bool is_writable() const override; + ssize_t read(char *ptr, size_t size) override; + ssize_t write(const char *ptr, size_t size) override; + void get_remote_ip_and_port(std::string &ip, int &port) const override; + socket_t socket() const override; + + private: + socket_t sock_; + time_t read_timeout_sec_; + time_t read_timeout_usec_; + time_t write_timeout_sec_; + time_t write_timeout_usec_; + }; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + class SSLSocketStream : public Stream { +public: + SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec, + time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec); + ~SSLSocketStream() override; + + bool is_readable() const override; + bool is_writable() const override; + ssize_t read(char *ptr, size_t size) override; + ssize_t write(const char *ptr, size_t size) override; + void get_remote_ip_and_port(std::string &ip, int &port) const override; + socket_t socket() const override; + +private: + socket_t sock_; + SSL *ssl_; + time_t read_timeout_sec_; + time_t read_timeout_usec_; + time_t write_timeout_sec_; + time_t write_timeout_usec_; +}; +#endif + + class BufferStream : public Stream { + public: + BufferStream() = default; + ~BufferStream() override = default; + + bool is_readable() const override; + bool is_writable() const override; + ssize_t read(char *ptr, size_t size) override; + ssize_t write(const char *ptr, size_t size) override; + void get_remote_ip_and_port(std::string &ip, int &port) const override; + socket_t socket() const override; + + const std::string &get_buffer() const; + + private: + std::string buffer; + size_t position = 0; + }; + + inline bool keep_alive(socket_t sock, time_t keep_alive_timeout_sec) { + using namespace std::chrono; + auto start = steady_clock::now(); + while (true) { + auto val = select_read(sock, 0, 10000); + if (val < 0) { + return false; + } else if (val == 0) { + auto current = steady_clock::now(); + auto duration = duration_cast(current - start); + auto timeout = keep_alive_timeout_sec * 1000; + if (duration.count() > timeout) { return false; } + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } else { + return true; + } + } + } + + template + inline bool + process_server_socket_core(socket_t sock, size_t keep_alive_max_count, + time_t keep_alive_timeout_sec, T callback) { + assert(keep_alive_max_count > 0); + auto ret = false; + auto count = keep_alive_max_count; + while (count > 0 && keep_alive(sock, keep_alive_timeout_sec)) { + auto close_connection = count == 1; + auto connection_closed = false; + ret = callback(close_connection, connection_closed); + if (!ret || connection_closed) { break; } + count--; + } + return ret; + } + + template + inline bool + process_server_socket(socket_t sock, size_t keep_alive_max_count, + time_t keep_alive_timeout_sec, time_t read_timeout_sec, + time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec, T callback) { + return process_server_socket_core( + sock, keep_alive_max_count, keep_alive_timeout_sec, + [&](bool close_connection, bool &connection_closed) { + SocketStream strm(sock, read_timeout_sec, read_timeout_usec, + write_timeout_sec, write_timeout_usec); + return callback(strm, close_connection, connection_closed); + }); + } + + template + inline bool process_client_socket(socket_t sock, time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec, T callback) { + SocketStream strm(sock, read_timeout_sec, read_timeout_usec, + write_timeout_sec, write_timeout_usec); + return callback(strm); + } + + inline int shutdown_socket(socket_t sock) { +#ifdef _WIN32 + return shutdown(sock, SD_BOTH); +#else + return shutdown(sock, SHUT_RDWR); +#endif + } + + template + socket_t create_socket(const char *host, int port, int address_family, + int socket_flags, bool tcp_nodelay, + SocketOptions socket_options, + BindOrConnect bind_or_connect) { + // Get address info + struct addrinfo hints; + struct addrinfo *result; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = address_family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = socket_flags; + hints.ai_protocol = 0; + + auto service = std::to_string(port); + + if (getaddrinfo(host, service.c_str(), &hints, &result)) { +#ifdef __linux__ + res_init(); +#endif + return INVALID_SOCKET; + } + + for (auto rp = result; rp; rp = rp->ai_next) { + // Create a socket +#ifdef _WIN32 + auto sock = + WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, nullptr, 0, + WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED); + /** + * Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1 + * and above the socket creation fails on older Windows Systems. + * + * Let's try to create a socket the old way in this case. + * + * Reference: + * https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa + * + * WSA_FLAG_NO_HANDLE_INHERIT: + * This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with + * SP1, and later + * + */ + if (sock == INVALID_SOCKET) { + sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + } +#else + auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); +#endif + if (sock == INVALID_SOCKET) { continue; } + +#ifndef _WIN32 + if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { continue; } +#endif + + if (tcp_nodelay) { + int yes = 1; + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&yes), + sizeof(yes)); + } + + if (socket_options) { socket_options(sock); } + + if (rp->ai_family == AF_INET6) { + int no = 0; + setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&no), + sizeof(no)); + } + + // bind or connect + if (bind_or_connect(sock, *rp)) { + freeaddrinfo(result); + return sock; + } + + close_socket(sock); + } + + freeaddrinfo(result); + return INVALID_SOCKET; + } + + inline void set_nonblocking(socket_t sock, bool nonblocking) { +#ifdef _WIN32 + auto flags = nonblocking ? 1UL : 0UL; + ioctlsocket(sock, FIONBIO, &flags); +#else + auto flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, + nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK))); +#endif + } + + inline bool is_connection_error() { +#ifdef _WIN32 + return WSAGetLastError() != WSAEWOULDBLOCK; +#else + return errno != EINPROGRESS; +#endif + } + + inline bool bind_ip_address(socket_t sock, const char *host) { + struct addrinfo hints; + struct addrinfo *result; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + + if (getaddrinfo(host, "0", &hints, &result)) { return false; } + + auto ret = false; + for (auto rp = result; rp; rp = rp->ai_next) { + const auto &ai = *rp; + if (!::bind(sock, ai.ai_addr, static_cast(ai.ai_addrlen))) { + ret = true; + break; + } + } + + freeaddrinfo(result); + return ret; + } + +#if !defined _WIN32 && !defined ANDROID +#define USE_IF2IP +#endif + +#ifdef USE_IF2IP + inline std::string if2ip(const std::string &ifn) { + struct ifaddrs *ifap; + getifaddrs(&ifap); + for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr && ifn == ifa->ifa_name) { + if (ifa->ifa_addr->sa_family == AF_INET) { + auto sa = reinterpret_cast(ifa->ifa_addr); + char buf[INET_ADDRSTRLEN]; + if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) { + freeifaddrs(ifap); + return std::string(buf, INET_ADDRSTRLEN); + } + } + } + } + freeifaddrs(ifap); + return std::string(); + } +#endif + + inline socket_t create_client_socket( + const char *host, int port, int address_family, bool tcp_nodelay, + SocketOptions socket_options, time_t connection_timeout_sec, + time_t connection_timeout_usec, time_t read_timeout_sec, + time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec, const std::string &intf, Error &error) { + auto sock = create_socket( + host, port, address_family, 0, tcp_nodelay, std::move(socket_options), + [&](socket_t sock2, struct addrinfo &ai) -> bool { + if (!intf.empty()) { +#ifdef USE_IF2IP + auto ip = if2ip(intf); + if (ip.empty()) { ip = intf; } + if (!bind_ip_address(sock2, ip.c_str())) { + error = Error::BindIPAddress; + return false; + } +#endif + } + + set_nonblocking(sock2, true); + + auto ret = + ::connect(sock2, ai.ai_addr, static_cast(ai.ai_addrlen)); + + if (ret < 0) { + if (is_connection_error() || + !wait_until_socket_is_ready(sock2, connection_timeout_sec, + connection_timeout_usec)) { + error = Error::Connection; + return false; + } + } + + set_nonblocking(sock2, false); + + { + timeval tv; + tv.tv_sec = static_cast(read_timeout_sec); + tv.tv_usec = static_cast(read_timeout_usec); + setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)); + } + { + timeval tv; + tv.tv_sec = static_cast(write_timeout_sec); + tv.tv_usec = static_cast(write_timeout_usec); + setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv)); + } + + error = Error::Success; + return true; + }); + + if (sock != INVALID_SOCKET) { + error = Error::Success; + } else { + if (error == Error::Success) { error = Error::Connection; } + } + + return sock; + } + + inline void get_remote_ip_and_port(const struct sockaddr_storage &addr, + socklen_t addr_len, std::string &ip, + int &port) { + if (addr.ss_family == AF_INET) { + port = ntohs(reinterpret_cast(&addr)->sin_port); + } else if (addr.ss_family == AF_INET6) { + port = + ntohs(reinterpret_cast(&addr)->sin6_port); + } + + std::array ipstr{}; + if (!getnameinfo(reinterpret_cast(&addr), addr_len, + ipstr.data(), static_cast(ipstr.size()), nullptr, + 0, NI_NUMERICHOST)) { + ip = ipstr.data(); + } + } + + inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) { + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + + if (!getpeername(sock, reinterpret_cast(&addr), + &addr_len)) { + get_remote_ip_and_port(addr, addr_len, ip, port); + } + } + + inline constexpr unsigned int str2tag_core(const char *s, size_t l, + unsigned int h) { + return (l == 0) ? h + : str2tag_core(s + 1, l - 1, + (h * 33) ^ static_cast(*s)); + } + + inline unsigned int str2tag(const std::string &s) { + return str2tag_core(s.data(), s.size(), 0); + } + + namespace udl { + + inline constexpr unsigned int operator"" _(const char *s, size_t l) { + return str2tag_core(s, l, 0); + } + + } // namespace udl + + inline const char * + find_content_type(const std::string &path, + const std::map &user_data) { + auto ext = file_extension(path); + + auto it = user_data.find(ext); + if (it != user_data.end()) { return it->second.c_str(); } + + using udl::operator""_; + + switch (str2tag(ext)) { + default: return nullptr; + case "css"_: return "text/css"; + case "csv"_: return "text/csv"; + case "txt"_: return "text/plain"; + case "vtt"_: return "text/vtt"; + case "htm"_: + case "html"_: return "text/html"; + + case "apng"_: return "image/apng"; + case "avif"_: return "image/avif"; + case "bmp"_: return "image/bmp"; + case "gif"_: return "image/gif"; + case "png"_: return "image/png"; + case "svg"_: return "image/svg+xml"; + case "webp"_: return "image/webp"; + case "ico"_: return "image/x-icon"; + case "tif"_: return "image/tiff"; + case "tiff"_: return "image/tiff"; + case "jpg"_: + case "jpeg"_: return "image/jpeg"; + + case "mp4"_: return "video/mp4"; + case "mpeg"_: return "video/mpeg"; + case "webm"_: return "video/webm"; + + case "mp3"_: return "audio/mp3"; + case "mpga"_: return "audio/mpeg"; + case "weba"_: return "audio/webm"; + case "wav"_: return "audio/wave"; + + case "otf"_: return "font/otf"; + case "ttf"_: return "font/ttf"; + case "woff"_: return "font/woff"; + case "woff2"_: return "font/woff2"; + + case "7z"_: return "application/x-7z-compressed"; + case "atom"_: return "application/atom+xml"; + case "pdf"_: return "application/pdf"; + case "js"_: + case "mjs"_: return "application/javascript"; + case "json"_: return "application/json"; + case "rss"_: return "application/rss+xml"; + case "tar"_: return "application/x-tar"; + case "xht"_: + case "xhtml"_: return "application/xhtml+xml"; + case "xslt"_: return "application/xslt+xml"; + case "xml"_: return "application/xml"; + case "gz"_: return "application/gzip"; + case "zip"_: return "application/zip"; + case "wasm"_: return "application/wasm"; + } + } + + inline const char *status_message(int status) { + switch (status) { + case 100: return "Continue"; + case 101: return "Switching Protocol"; + case 102: return "Processing"; + case 103: return "Early Hints"; + case 200: return "OK"; + case 201: return "Created"; + case 202: return "Accepted"; + case 203: return "Non-Authoritative Information"; + case 204: return "No Content"; + case 205: return "Reset Content"; + case 206: return "Partial Content"; + case 207: return "Multi-Status"; + case 208: return "Already Reported"; + case 226: return "IM Used"; + case 300: return "Multiple Choice"; + case 301: return "Moved Permanently"; + case 302: return "Found"; + case 303: return "See Other"; + case 304: return "Not Modified"; + case 305: return "Use Proxy"; + case 306: return "unused"; + case 307: return "Temporary Redirect"; + case 308: return "Permanent Redirect"; + case 400: return "Bad Request"; + case 401: return "Unauthorized"; + case 402: return "Payment Required"; + case 403: return "Forbidden"; + case 404: return "Not Found"; + case 405: return "Method Not Allowed"; + case 406: return "Not Acceptable"; + case 407: return "Proxy Authentication Required"; + case 408: return "Request Timeout"; + case 409: return "Conflict"; + case 410: return "Gone"; + case 411: return "Length Required"; + case 412: return "Precondition Failed"; + case 413: return "Payload Too Large"; + case 414: return "URI Too Long"; + case 415: return "Unsupported Media Type"; + case 416: return "Range Not Satisfiable"; + case 417: return "Expectation Failed"; + case 418: return "I'm a teapot"; + case 421: return "Misdirected Request"; + case 422: return "Unprocessable Entity"; + case 423: return "Locked"; + case 424: return "Failed Dependency"; + case 425: return "Too Early"; + case 426: return "Upgrade Required"; + case 428: return "Precondition Required"; + case 429: return "Too Many Requests"; + case 431: return "Request Header Fields Too Large"; + case 451: return "Unavailable For Legal Reasons"; + case 501: return "Not Implemented"; + case 502: return "Bad Gateway"; + case 503: return "Service Unavailable"; + case 504: return "Gateway Timeout"; + case 505: return "HTTP Version Not Supported"; + case 506: return "Variant Also Negotiates"; + case 507: return "Insufficient Storage"; + case 508: return "Loop Detected"; + case 510: return "Not Extended"; + case 511: return "Network Authentication Required"; + + default: + case 500: return "Internal Server Error"; + } + } + + inline bool can_compress_content_type(const std::string &content_type) { + return (!content_type.find("text/") && content_type != "text/event-stream") || + content_type == "image/svg+xml" || + content_type == "application/javascript" || + content_type == "application/json" || + content_type == "application/xml" || + content_type == "application/xhtml+xml"; + } + + enum class EncodingType { None = 0, Gzip, Brotli }; + + inline EncodingType encoding_type(const Request &req, const Response &res) { + auto ret = + detail::can_compress_content_type(res.get_header_value("Content-Type")); + if (!ret) { return EncodingType::None; } + + const auto &s = req.get_header_value("Accept-Encoding"); + (void)(s); + +#ifdef CPPHTTPLIB_BROTLI_SUPPORT + // TODO: 'Accept-Encoding' has br, not br;q=0 + ret = s.find("br") != std::string::npos; + if (ret) { return EncodingType::Brotli; } +#endif + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + // TODO: 'Accept-Encoding' has gzip, not gzip;q=0 + ret = s.find("gzip") != std::string::npos; + if (ret) { return EncodingType::Gzip; } +#endif + + return EncodingType::None; + } + + class compressor { + public: + virtual ~compressor(){}; + + typedef std::function Callback; + virtual bool compress(const char *data, size_t data_length, bool last, + Callback callback) = 0; + }; + + class decompressor { + public: + virtual ~decompressor() {} + + virtual bool is_valid() const = 0; + + typedef std::function Callback; + virtual bool decompress(const char *data, size_t data_length, + Callback callback) = 0; + }; + + class nocompressor : public compressor { + public: + ~nocompressor(){}; + + bool compress(const char *data, size_t data_length, bool /*last*/, + Callback callback) override { + if (!data_length) { return true; } + return callback(data, data_length); + } + }; + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + class gzip_compressor : public compressor { +public: + gzip_compressor() { + std::memset(&strm_, 0, sizeof(strm_)); + strm_.zalloc = Z_NULL; + strm_.zfree = Z_NULL; + strm_.opaque = Z_NULL; + + is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, + Z_DEFAULT_STRATEGY) == Z_OK; + } + + ~gzip_compressor() { deflateEnd(&strm_); } + + bool compress(const char *data, size_t data_length, bool last, + Callback callback) override { + assert(is_valid_); + + auto flush = last ? Z_FINISH : Z_NO_FLUSH; + + strm_.avail_in = static_cast(data_length); + strm_.next_in = const_cast(reinterpret_cast(data)); + + int ret = Z_OK; + + std::array buff{}; + do { + strm_.avail_out = static_cast(buff.size()); + strm_.next_out = reinterpret_cast(buff.data()); + + ret = deflate(&strm_, flush); + if (ret == Z_STREAM_ERROR) { return false; } + + if (!callback(buff.data(), buff.size() - strm_.avail_out)) { + return false; + } + } while (strm_.avail_out == 0); + + assert((last && ret == Z_STREAM_END) || (!last && ret == Z_OK)); + assert(strm_.avail_in == 0); + return true; + } + +private: + bool is_valid_ = false; + z_stream strm_; +}; + +class gzip_decompressor : public decompressor { +public: + gzip_decompressor() { + std::memset(&strm_, 0, sizeof(strm_)); + strm_.zalloc = Z_NULL; + strm_.zfree = Z_NULL; + strm_.opaque = Z_NULL; + + // 15 is the value of wbits, which should be at the maximum possible value + // to ensure that any gzip stream can be decoded. The offset of 32 specifies + // that the stream type should be automatically detected either gzip or + // deflate. + is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK; + } + + ~gzip_decompressor() { inflateEnd(&strm_); } + + bool is_valid() const override { return is_valid_; } + + bool decompress(const char *data, size_t data_length, + Callback callback) override { + assert(is_valid_); + + int ret = Z_OK; + + strm_.avail_in = static_cast(data_length); + strm_.next_in = const_cast(reinterpret_cast(data)); + + std::array buff{}; + while (strm_.avail_in > 0) { + strm_.avail_out = static_cast(buff.size()); + strm_.next_out = reinterpret_cast(buff.data()); + + ret = inflate(&strm_, Z_NO_FLUSH); + assert(ret != Z_STREAM_ERROR); + switch (ret) { + case Z_NEED_DICT: + case Z_DATA_ERROR: + case Z_MEM_ERROR: inflateEnd(&strm_); return false; + } + + if (!callback(buff.data(), buff.size() - strm_.avail_out)) { + return false; + } + } + + return ret == Z_OK || ret == Z_STREAM_END; + } + +private: + bool is_valid_ = false; + z_stream strm_; +}; +#endif + +#ifdef CPPHTTPLIB_BROTLI_SUPPORT + class brotli_compressor : public compressor { +public: + brotli_compressor() { + state_ = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr); + } + + ~brotli_compressor() { BrotliEncoderDestroyInstance(state_); } + + bool compress(const char *data, size_t data_length, bool last, + Callback callback) override { + std::array buff{}; + + auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS; + auto available_in = data_length; + auto next_in = reinterpret_cast(data); + + for (;;) { + if (last) { + if (BrotliEncoderIsFinished(state_)) { break; } + } else { + if (!available_in) { break; } + } + + auto available_out = buff.size(); + auto next_out = buff.data(); + + if (!BrotliEncoderCompressStream(state_, operation, &available_in, + &next_in, &available_out, &next_out, + nullptr)) { + return false; + } + + auto output_bytes = buff.size() - available_out; + if (output_bytes) { + callback(reinterpret_cast(buff.data()), output_bytes); + } + } + + return true; + } + +private: + BrotliEncoderState *state_ = nullptr; +}; + +class brotli_decompressor : public decompressor { +public: + brotli_decompressor() { + decoder_s = BrotliDecoderCreateInstance(0, 0, 0); + decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT + : BROTLI_DECODER_RESULT_ERROR; + } + + ~brotli_decompressor() { + if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); } + } + + bool is_valid() const override { return decoder_s; } + + bool decompress(const char *data, size_t data_length, + Callback callback) override { + if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS || + decoder_r == BROTLI_DECODER_RESULT_ERROR) { + return 0; + } + + const uint8_t *next_in = (const uint8_t *)data; + size_t avail_in = data_length; + size_t total_out; + + decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; + + std::array buff{}; + while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) { + char *next_out = buff.data(); + size_t avail_out = buff.size(); + + decoder_r = BrotliDecoderDecompressStream( + decoder_s, &avail_in, &next_in, &avail_out, + reinterpret_cast(&next_out), &total_out); + + if (decoder_r == BROTLI_DECODER_RESULT_ERROR) { return false; } + + if (!callback(buff.data(), buff.size() - avail_out)) { return false; } + } + + return decoder_r == BROTLI_DECODER_RESULT_SUCCESS || + decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT; + } + +private: + BrotliDecoderResult decoder_r; + BrotliDecoderState *decoder_s = nullptr; +}; +#endif + + inline bool has_header(const Headers &headers, const char *key) { + return headers.find(key) != headers.end(); + } + + inline const char *get_header_value(const Headers &headers, const char *key, + size_t id = 0, const char *def = nullptr) { + auto rng = headers.equal_range(key); + auto it = rng.first; + std::advance(it, static_cast(id)); + if (it != rng.second) { return it->second.c_str(); } + return def; + } + + template + inline T get_header_value(const Headers & /*headers*/, const char * /*key*/, + size_t /*id*/ = 0, uint64_t /*def*/ = 0) {} + + template <> + inline uint64_t get_header_value(const Headers &headers, + const char *key, size_t id, + uint64_t def) { + auto rng = headers.equal_range(key); + auto it = rng.first; + std::advance(it, static_cast(id)); + if (it != rng.second) { + return std::strtoull(it->second.data(), nullptr, 10); + } + return def; + } + + template + inline bool parse_header(const char *beg, const char *end, T fn) { + // Skip trailing spaces and tabs. + while (beg < end && is_space_or_tab(end[-1])) { + end--; + } + + auto p = beg; + while (p < end && *p != ':') { + p++; + } + + if (p == end) { return false; } + + auto key_end = p; + + if (*p++ != ':') { return false; } + + while (p < end && is_space_or_tab(*p)) { + p++; + } + + if (p < end) { + fn(std::string(beg, key_end), decode_url(std::string(p, end), false)); + return true; + } + + return false; + } + + inline bool read_headers(Stream &strm, Headers &headers) { + const auto bufsiz = 2048; + char buf[bufsiz]; + stream_line_reader line_reader(strm, buf, bufsiz); + + for (;;) { + if (!line_reader.getline()) { return false; } + + // Check if the line ends with CRLF. + if (line_reader.end_with_crlf()) { + // Blank line indicates end of headers. + if (line_reader.size() == 2) { break; } + } else { + continue; // Skip invalid line. + } + + // Exclude CRLF + auto end = line_reader.ptr() + line_reader.size() - 2; + + parse_header(line_reader.ptr(), end, + [&](std::string &&key, std::string &&val) { + headers.emplace(std::move(key), std::move(val)); + }); + } + + return true; + } + + inline bool read_content_with_length(Stream &strm, uint64_t len, + Progress progress, + ContentReceiverWithProgress out) { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + + uint64_t r = 0; + while (r < len) { + auto read_len = static_cast(len - r); + auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ)); + if (n <= 0) { return false; } + + if (!out(buf, static_cast(n), r, len)) { return false; } + r += static_cast(n); + + if (progress) { + if (!progress(r, len)) { return false; } + } + } + + return true; + } + + inline void skip_content_with_length(Stream &strm, uint64_t len) { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + uint64_t r = 0; + while (r < len) { + auto read_len = static_cast(len - r); + auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ)); + if (n <= 0) { return; } + r += static_cast(n); + } + } + + inline bool read_content_without_length(Stream &strm, + ContentReceiverWithProgress out) { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + uint64_t r = 0; + for (;;) { + auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ); + if (n < 0) { + return false; + } else if (n == 0) { + return true; + } + + if (!out(buf, static_cast(n), r, 0)) { return false; } + r += static_cast(n); + } + + return true; + } + + inline bool read_content_chunked(Stream &strm, + ContentReceiverWithProgress out) { + const auto bufsiz = 16; + char buf[bufsiz]; + + stream_line_reader line_reader(strm, buf, bufsiz); + + if (!line_reader.getline()) { return false; } + + unsigned long chunk_len; + while (true) { + char *end_ptr; + + chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16); + + if (end_ptr == line_reader.ptr()) { return false; } + if (chunk_len == ULONG_MAX) { return false; } + + if (chunk_len == 0) { break; } + + if (!read_content_with_length(strm, chunk_len, nullptr, out)) { + return false; + } + + if (!line_reader.getline()) { return false; } + + if (strcmp(line_reader.ptr(), "\r\n")) { break; } + + if (!line_reader.getline()) { return false; } + } + + if (chunk_len == 0) { + // Reader terminator after chunks + if (!line_reader.getline() || strcmp(line_reader.ptr(), "\r\n")) + return false; + } + + return true; + } + + inline bool is_chunked_transfer_encoding(const Headers &headers) { + return !strcasecmp(get_header_value(headers, "Transfer-Encoding", 0, ""), + "chunked"); + } + + template + bool prepare_content_receiver(T &x, int &status, + ContentReceiverWithProgress receiver, + bool decompress, U callback) { + if (decompress) { + std::string encoding = x.get_header_value("Content-Encoding"); + std::unique_ptr decompressor; + + if (encoding.find("gzip") != std::string::npos || + encoding.find("deflate") != std::string::npos) { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + decompressor = detail::make_unique(); +#else + status = 415; + return false; +#endif + } else if (encoding.find("br") != std::string::npos) { +#ifdef CPPHTTPLIB_BROTLI_SUPPORT + decompressor = detail::make_unique(); +#else + status = 415; + return false; +#endif + } + + if (decompressor) { + if (decompressor->is_valid()) { + ContentReceiverWithProgress out = [&](const char *buf, size_t n, + uint64_t off, uint64_t len) { + return decompressor->decompress(buf, n, + [&](const char *buf2, size_t n2) { + return receiver(buf2, n2, off, len); + }); + }; + return callback(std::move(out)); + } else { + status = 500; + return false; + } + } + } + + ContentReceiverWithProgress out = [&](const char *buf, size_t n, uint64_t off, + uint64_t len) { + return receiver(buf, n, off, len); + }; + return callback(std::move(out)); + } + + template + bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status, + Progress progress, ContentReceiverWithProgress receiver, + bool decompress) { + return prepare_content_receiver( + x, status, std::move(receiver), decompress, + [&](const ContentReceiverWithProgress &out) { + auto ret = true; + auto exceed_payload_max_length = false; + + if (is_chunked_transfer_encoding(x.headers)) { + ret = read_content_chunked(strm, out); + } else if (!has_header(x.headers, "Content-Length")) { + ret = read_content_without_length(strm, out); + } else { + auto len = get_header_value(x.headers, "Content-Length"); + if (len > payload_max_length) { + exceed_payload_max_length = true; + skip_content_with_length(strm, len); + ret = false; + } else if (len > 0) { + ret = read_content_with_length(strm, len, std::move(progress), out); + } + } + + if (!ret) { status = exceed_payload_max_length ? 413 : 400; } + return ret; + }); + } + + inline ssize_t write_headers(Stream &strm, const Headers &headers) { + ssize_t write_len = 0; + for (const auto &x : headers) { + auto len = + strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str()); + if (len < 0) { return len; } + write_len += len; + } + auto len = strm.write("\r\n"); + if (len < 0) { return len; } + write_len += len; + return write_len; + } + + inline bool write_data(Stream &strm, const char *d, size_t l) { + size_t offset = 0; + while (offset < l) { + auto length = strm.write(d + offset, l - offset); + if (length < 0) { return false; } + offset += static_cast(length); + } + return true; + } + + template + inline bool write_content(Stream &strm, const ContentProvider &content_provider, + size_t offset, size_t length, T is_shutting_down, + Error &error) { + size_t end_offset = offset + length; + auto ok = true; + DataSink data_sink; + + data_sink.write = [&](const char *d, size_t l) -> bool { + if (ok) { + if (write_data(strm, d, l)) { + offset += l; + } else { + ok = false; + } + } + return ok; + }; + + data_sink.is_writable = [&](void) { return ok && strm.is_writable(); }; + + while (offset < end_offset && !is_shutting_down()) { + if (!content_provider(offset, end_offset - offset, data_sink)) { + error = Error::Canceled; + return false; + } + if (!ok) { + error = Error::Write; + return false; + } + } + + error = Error::Success; + return true; + } + + template + inline bool write_content(Stream &strm, const ContentProvider &content_provider, + size_t offset, size_t length, + const T &is_shutting_down) { + auto error = Error::Success; + return write_content(strm, content_provider, offset, length, is_shutting_down, + error); + } + + template + inline bool + write_content_without_length(Stream &strm, + const ContentProvider &content_provider, + const T &is_shutting_down) { + size_t offset = 0; + auto data_available = true; + auto ok = true; + DataSink data_sink; + + data_sink.write = [&](const char *d, size_t l) -> bool { + if (ok) { + offset += l; + if (!write_data(strm, d, l)) { ok = false; } + } + return ok; + }; + + data_sink.done = [&](void) { data_available = false; }; + + data_sink.is_writable = [&](void) { return ok && strm.is_writable(); }; + + while (data_available && !is_shutting_down()) { + if (!content_provider(offset, 0, data_sink)) { return false; } + if (!ok) { return false; } + } + return true; + } + + template + inline bool + write_content_chunked(Stream &strm, const ContentProvider &content_provider, + const T &is_shutting_down, U &compressor, Error &error) { + size_t offset = 0; + auto data_available = true; + auto ok = true; + DataSink data_sink; + + data_sink.write = [&](const char *d, size_t l) -> bool { + if (ok) { + data_available = l > 0; + offset += l; + + std::string payload; + if (compressor.compress(d, l, false, + [&](const char *data, size_t data_len) { + payload.append(data, data_len); + return true; + })) { + if (!payload.empty()) { + // Emit chunked response header and footer for each chunk + auto chunk = + from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n"; + if (!write_data(strm, chunk.data(), chunk.size())) { ok = false; } + } + } else { + ok = false; + } + } + return ok; + }; + + data_sink.done = [&](void) { + if (!ok) { return; } + + data_available = false; + + std::string payload; + if (!compressor.compress(nullptr, 0, true, + [&](const char *data, size_t data_len) { + payload.append(data, data_len); + return true; + })) { + ok = false; + return; + } + + if (!payload.empty()) { + // Emit chunked response header and footer for each chunk + auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n"; + if (!write_data(strm, chunk.data(), chunk.size())) { + ok = false; + return; + } + } + + static const std::string done_marker("0\r\n\r\n"); + if (!write_data(strm, done_marker.data(), done_marker.size())) { + ok = false; + } + }; + + data_sink.is_writable = [&](void) { return ok && strm.is_writable(); }; + + while (data_available && !is_shutting_down()) { + if (!content_provider(offset, 0, data_sink)) { + error = Error::Canceled; + return false; + } + if (!ok) { + error = Error::Write; + return false; + } + } + + error = Error::Success; + return true; + } + + template + inline bool write_content_chunked(Stream &strm, + const ContentProvider &content_provider, + const T &is_shutting_down, U &compressor) { + auto error = Error::Success; + return write_content_chunked(strm, content_provider, is_shutting_down, + compressor, error); + } + + template + inline bool redirect(T &cli, Request &req, Response &res, + const std::string &path, const std::string &location, + Error &error) { + Request new_req = req; + new_req.path = path; + new_req.redirect_count_ -= 1; + + if (res.status == 303 && (req.method != "GET" && req.method != "HEAD")) { + new_req.method = "GET"; + new_req.body.clear(); + new_req.headers.clear(); + } + + Response new_res; + + auto ret = cli.send(new_req, new_res, error); + if (ret) { + req = new_req; + res = new_res; + res.location = location; + } + return ret; + } + + inline std::string params_to_query_str(const Params ¶ms) { + std::string query; + + for (auto it = params.begin(); it != params.end(); ++it) { + if (it != params.begin()) { query += "&"; } + query += it->first; + query += "="; + query += encode_query_param(it->second); + } + return query; + } + + inline std::string append_query_params(const char *path, const Params ¶ms) { + std::string path_with_query = path; + const static std::regex re("[^?]+\\?.*"); + auto delm = std::regex_match(path, re) ? '&' : '?'; + path_with_query += delm + params_to_query_str(params); + return path_with_query; + } + inline void parse_query_text(const std::string &s, Params ¶ms) { - split(&s[0], &s[s.size()], '&', [&](const char *b, const char *e) { + std::set cache; + split(s.data(), s.data() + s.size(), '&', [&](const char *b, const char *e) { + std::string kv(b, e); + if (cache.find(kv) != cache.end()) { return; } + cache.insert(kv); + std::string key; std::string val; - split(b, e, '=', [&](const char *b, const char *e) { + split(b, e, '=', [&](const char *b2, const char *e2) { if (key.empty()) { - key.assign(b, e); + key.assign(b2, e2); } else { - val.assign(b, e); + val.assign(b2, e2); } }); - params.emplace(key, decode_url(val)); + + if (!key.empty()) { + params.emplace(decode_url(key, true), decode_url(val, true)); + } }); } @@ -1158,182 +3263,487 @@ private: std::string &boundary) { auto pos = content_type.find("boundary="); if (pos == std::string::npos) { return false; } - boundary = content_type.substr(pos + 9); - return true; + if (boundary.length() >= 2 && boundary.front() == '"' && + boundary.back() == '"') { + boundary = boundary.substr(1, boundary.size() - 2); + } + return !boundary.empty(); } - inline bool parse_multipart_formdata(const std::string &boundary, - const std::string &body, - MultipartFiles &files) { - static std::string dash = "--"; - static std::string crlf = "\r\n"; + inline bool parse_range_header(const std::string &s, Ranges &ranges) try { + static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))"); + std::smatch m; + if (std::regex_match(s, m, re_first_range)) { + auto pos = static_cast(m.position(1)); + auto len = static_cast(m.length(1)); + bool all_valid_ranges = true; + split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) { + if (!all_valid_ranges) return; + static auto re_another_range = std::regex(R"(\s*(\d*)-(\d*))"); + std::cmatch cm; + if (std::regex_match(b, e, cm, re_another_range)) { + ssize_t first = -1; + if (!cm.str(1).empty()) { + first = static_cast(std::stoll(cm.str(1))); + } - static std::regex re_content_type("Content-Type: (.*?)", - std::regex_constants::icase); + ssize_t last = -1; + if (!cm.str(2).empty()) { + last = static_cast(std::stoll(cm.str(2))); + } - static std::regex re_content_disposition( - "Content-Disposition: form-data; name=\"(.*?)\"(?:; filename=\"(.*?)\")?", - std::regex_constants::icase); - - auto dash_boundary = dash + boundary; - - auto pos = body.find(dash_boundary); - if (pos != 0) { return false; } - - pos += dash_boundary.size(); - - auto next_pos = body.find(crlf, pos); - if (next_pos == std::string::npos) { return false; } - - pos = next_pos + crlf.size(); - - while (pos < body.size()) { - next_pos = body.find(crlf, pos); - if (next_pos == std::string::npos) { return false; } - - std::string name; - MultipartFile file; - - auto header = body.substr(pos, (next_pos - pos)); - - while (pos != next_pos) { - std::smatch m; - if (std::regex_match(header, m, re_content_type)) { - file.content_type = m[1]; - } else if (std::regex_match(header, m, re_content_disposition)) { - name = m[1]; - file.filename = m[2]; + if (first != -1 && last != -1 && first > last) { + all_valid_ranges = false; + return; + } + ranges.emplace_back(std::make_pair(first, last)); } + }); + return all_valid_ranges; + } + return false; + } catch (...) { return false; } - pos = next_pos + crlf.size(); + class MultipartFormDataParser { + public: + MultipartFormDataParser() = default; - next_pos = body.find(crlf, pos); - if (next_pos == std::string::npos) { return false; } + void set_boundary(std::string &&boundary) { boundary_ = boundary; } - header = body.substr(pos, (next_pos - pos)); + bool is_valid() const { return is_valid_; } + + bool parse(const char *buf, size_t n, const ContentReceiver &content_callback, + const MultipartContentHeader &header_callback) { + + static const std::regex re_content_disposition( + "^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename=" + "\"(.*?)\")?\\s*$", + std::regex_constants::icase); + static const std::string dash_ = "--"; + static const std::string crlf_ = "\r\n"; + + buf_.append(buf, n); // TODO: performance improvement + + while (!buf_.empty()) { + switch (state_) { + case 0: { // Initial boundary + auto pattern = dash_ + boundary_ + crlf_; + if (pattern.size() > buf_.size()) { return true; } + auto pos = buf_.find(pattern); + if (pos != 0) { return false; } + buf_.erase(0, pattern.size()); + off_ += pattern.size(); + state_ = 1; + break; + } + case 1: { // New entry + clear_file_info(); + state_ = 2; + break; + } + case 2: { // Headers + auto pos = buf_.find(crlf_); + while (pos != std::string::npos) { + // Empty line + if (pos == 0) { + if (!header_callback(file_)) { + is_valid_ = false; + return false; + } + buf_.erase(0, crlf_.size()); + off_ += crlf_.size(); + state_ = 3; + break; + } + + static const std::string header_name = "content-type:"; + const auto header = buf_.substr(0, pos); + if (start_with_case_ignore(header, header_name)) { + file_.content_type = trim_copy(header.substr(header_name.size())); + } else { + std::smatch m; + if (std::regex_match(header, m, re_content_disposition)) { + file_.name = m[1]; + file_.filename = m[2]; + } + } + + buf_.erase(0, pos + crlf_.size()); + off_ += pos + crlf_.size(); + pos = buf_.find(crlf_); + } + if (state_ != 3) { return true; } + break; + } + case 3: { // Body + { + auto pattern = crlf_ + dash_; + if (pattern.size() > buf_.size()) { return true; } + + auto pos = find_string(buf_, pattern); + + if (!content_callback(buf_.data(), pos)) { + is_valid_ = false; + return false; + } + + off_ += pos; + buf_.erase(0, pos); + } + { + auto pattern = crlf_ + dash_ + boundary_; + if (pattern.size() > buf_.size()) { return true; } + + auto pos = buf_.find(pattern); + if (pos != std::string::npos) { + if (!content_callback(buf_.data(), pos)) { + is_valid_ = false; + return false; + } + + off_ += pos + pattern.size(); + buf_.erase(0, pos + pattern.size()); + state_ = 4; + } else { + if (!content_callback(buf_.data(), pattern.size())) { + is_valid_ = false; + return false; + } + + off_ += pattern.size(); + buf_.erase(0, pattern.size()); + } + } + break; + } + case 4: { // Boundary + if (crlf_.size() > buf_.size()) { return true; } + if (buf_.compare(0, crlf_.size(), crlf_) == 0) { + buf_.erase(0, crlf_.size()); + off_ += crlf_.size(); + state_ = 1; + } else { + auto pattern = dash_ + crlf_; + if (pattern.size() > buf_.size()) { return true; } + if (buf_.compare(0, pattern.size(), pattern) == 0) { + buf_.erase(0, pattern.size()); + off_ += pattern.size(); + is_valid_ = true; + state_ = 5; + } else { + return true; + } + } + break; + } + case 5: { // Done + is_valid_ = false; + return false; + } + } } - pos = next_pos + crlf.size(); - - next_pos = body.find(crlf + dash_boundary, pos); - - if (next_pos == std::string::npos) { return false; } - - file.offset = pos; - file.length = next_pos - pos; - - pos = next_pos + crlf.size() + dash_boundary.size(); - - next_pos = body.find(crlf, pos); - if (next_pos == std::string::npos) { return false; } - - files.emplace(name, file); - - pos = next_pos + crlf.size(); + return true; } - return true; - } + private: + void clear_file_info() { + file_.name.clear(); + file_.filename.clear(); + file_.content_type.clear(); + } + + bool start_with_case_ignore(const std::string &a, + const std::string &b) const { + if (a.size() < b.size()) { return false; } + for (size_t i = 0; i < b.size(); i++) { + if (::tolower(a[i]) != ::tolower(b[i])) { return false; } + } + return true; + } + + bool start_with(const std::string &a, size_t off, + const std::string &b) const { + if (a.size() - off < b.size()) { return false; } + for (size_t i = 0; i < b.size(); i++) { + if (a[i + off] != b[i]) { return false; } + } + return true; + } + + size_t find_string(const std::string &s, const std::string &pattern) const { + auto c = pattern.front(); + + size_t off = 0; + while (off < s.size()) { + auto pos = s.find(c, off); + if (pos == std::string::npos) { return s.size(); } + + auto rem = s.size() - pos; + if (pattern.size() > rem) { return pos; } + + if (start_with(s, pos, pattern)) { return pos; } + + off = pos + 1; + } + + return s.size(); + } + + std::string boundary_; + + std::string buf_; + size_t state_ = 0; + bool is_valid_ = false; + size_t off_ = 0; + MultipartFormData file_; + }; inline std::string to_lower(const char *beg, const char *end) { std::string out; auto it = beg; while (it != end) { - out += ::tolower(*it); + out += static_cast(::tolower(*it)); it++; } return out; } - inline void make_range_header_core(std::string &) {} + inline std::string make_multipart_data_boundary() { + static const char data[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - template - inline void make_range_header_core(std::string &field, uint64_t value) { - if (!field.empty()) { field += ", "; } - field += std::to_string(value) + "-"; + // std::random_device might actually be deterministic on some + // platforms, but due to lack of support in the c++ standard library, + // doing better requires either some ugly hacks or breaking portability. + std::random_device seed_gen; + // Request 128 bits of entropy for initialization + std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()}; + std::mt19937 engine(seed_sequence); + + std::string result = "--cpp-httplib-multipart-data-"; + + for (auto i = 0; i < 16; i++) { + result += data[engine() % (sizeof(data) - 1)]; + } + + return result; } - template - inline void make_range_header_core(std::string &field, uint64_t value1, - uint64_t value2, Args... args) { - if (!field.empty()) { field += ", "; } - field += std::to_string(value1) + "-" + std::to_string(value2); - make_range_header_core(field, args...); + inline std::pair + get_range_offset_and_length(const Request &req, size_t content_length, + size_t index) { + auto r = req.ranges[index]; + + if (r.first == -1 && r.second == -1) { + return std::make_pair(0, content_length); + } + + auto slen = static_cast(content_length); + + if (r.first == -1) { + r.first = (std::max)(static_cast(0), slen - r.second); + r.second = slen - 1; + } + + if (r.second == -1) { r.second = slen - 1; } + return std::make_pair(r.first, static_cast(r.second - r.first) + 1); } -#ifdef CPPHTTPLIB_ZLIB_SUPPORT - inline bool can_compress(const std::string &content_type) { - return !content_type.find("text/") || content_type == "image/svg+xml" || - content_type == "application/javascript" || - content_type == "application/json" || - content_type == "application/xml" || - content_type == "application/xhtml+xml"; + inline std::string make_content_range_header_field(size_t offset, size_t length, + size_t content_length) { + std::string field = "bytes "; + field += std::to_string(offset); + field += "-"; + field += std::to_string(offset + length - 1); + field += "/"; + field += std::to_string(content_length); + return field; + } + + template + bool process_multipart_ranges_data(const Request &req, Response &res, + const std::string &boundary, + const std::string &content_type, + SToken stoken, CToken ctoken, + Content content) { + for (size_t i = 0; i < req.ranges.size(); i++) { + ctoken("--"); + stoken(boundary); + ctoken("\r\n"); + if (!content_type.empty()) { + ctoken("Content-Type: "); + stoken(content_type); + ctoken("\r\n"); + } + + auto offsets = get_range_offset_and_length(req, res.body.size(), i); + auto offset = offsets.first; + auto length = offsets.second; + + ctoken("Content-Range: "); + stoken(make_content_range_header_field(offset, length, res.body.size())); + ctoken("\r\n"); + ctoken("\r\n"); + if (!content(offset, length)) { return false; } + ctoken("\r\n"); + } + + ctoken("--"); + stoken(boundary); + ctoken("--\r\n"); + + return true; + } + + inline bool make_multipart_ranges_data(const Request &req, Response &res, + const std::string &boundary, + const std::string &content_type, + std::string &data) { + return process_multipart_ranges_data( + req, res, boundary, content_type, + [&](const std::string &token) { data += token; }, + [&](const char *token) { data += token; }, + [&](size_t offset, size_t length) { + if (offset < res.body.size()) { + data += res.body.substr(offset, length); + return true; + } + return false; + }); + } + + inline size_t + get_multipart_ranges_data_length(const Request &req, Response &res, + const std::string &boundary, + const std::string &content_type) { + size_t data_length = 0; + + process_multipart_ranges_data( + req, res, boundary, content_type, + [&](const std::string &token) { data_length += token.size(); }, + [&](const char *token) { data_length += strlen(token); }, + [&](size_t /*offset*/, size_t length) { + data_length += length; + return true; + }); + + return data_length; + } + + template + inline bool write_multipart_ranges_data(Stream &strm, const Request &req, + Response &res, + const std::string &boundary, + const std::string &content_type, + const T &is_shutting_down) { + return process_multipart_ranges_data( + req, res, boundary, content_type, + [&](const std::string &token) { strm.write(token); }, + [&](const char *token) { strm.write(token); }, + [&](size_t offset, size_t length) { + return write_content(strm, res.content_provider_, offset, length, + is_shutting_down); + }); + } + + inline std::pair + get_range_offset_and_length(const Request &req, const Response &res, + size_t index) { + auto r = req.ranges[index]; + + if (r.second == -1) { + r.second = static_cast(res.content_length_) - 1; + } + + return std::make_pair(r.first, r.second - r.first + 1); + } + + inline bool expect_content(const Request &req) { + if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" || + req.method == "PRI" || req.method == "DELETE") { + return true; + } + // TODO: check if Content-Length is set + return false; + } + + inline bool has_crlf(const char *s) { + auto p = s; + while (*p) { + if (*p == '\r' || *p == '\n') { return true; } + p++; + } + return false; + } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + template +inline std::string message_digest(const std::string &s, Init init, + Update update, Final final, + size_t digest_length) { + using namespace std; + + std::vector md(digest_length, 0); + CTX ctx; + init(&ctx); + update(&ctx, s.data(), s.size()); + final(md.data(), &ctx); + + stringstream ss; + for (auto c : md) { + ss << setfill('0') << setw(2) << hex << (unsigned int)c; + } + return ss.str(); } -inline void compress(std::string &content) { - z_stream strm; - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - - auto ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, - Z_DEFAULT_STRATEGY); - if (ret != Z_OK) { return; } - - strm.avail_in = content.size(); - strm.next_in = (Bytef *)content.data(); - - std::string compressed; - - const auto bufsiz = 16384; - char buff[bufsiz]; - do { - strm.avail_out = bufsiz; - strm.next_out = (Bytef *)buff; - deflate(&strm, Z_FINISH); - compressed.append(buff, bufsiz - strm.avail_out); - } while (strm.avail_out == 0); - - content.swap(compressed); - - deflateEnd(&strm); +inline std::string MD5(const std::string &s) { + return message_digest(s, MD5_Init, MD5_Update, MD5_Final, + MD5_DIGEST_LENGTH); } -inline void decompress(std::string &content) { - z_stream strm; - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; +inline std::string SHA_256(const std::string &s) { + return message_digest(s, SHA256_Init, SHA256_Update, SHA256_Final, + SHA256_DIGEST_LENGTH); +} - // 15 is the value of wbits, which should be at the maximum possible value to - // ensure that any gzip stream can be decoded. The offset of 16 specifies that - // the stream to decompress will be formatted with a gzip wrapper. - auto ret = inflateInit2(&strm, 16 + 15); - if (ret != Z_OK) { return; } - - strm.avail_in = content.size(); - strm.next_in = (Bytef *)content.data(); - - std::string decompressed; - - const auto bufsiz = 16384; - char buff[bufsiz]; - do { - strm.avail_out = bufsiz; - strm.next_out = (Bytef *)buff; - inflate(&strm, Z_NO_FLUSH); - decompressed.append(buff, bufsiz - strm.avail_out); - } while (strm.avail_out == 0); - - content.swap(decompressed); - - inflateEnd(&strm); +inline std::string SHA_512(const std::string &s) { + return message_digest(s, SHA512_Init, SHA512_Update, SHA512_Final, + SHA512_DIGEST_LENGTH); } #endif #ifdef _WIN32 - class WSInit { + #ifdef CPPHTTPLIB_OPENSSL_SUPPORT +// NOTE: This code came up with the following stackoverflow post: +// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store +inline bool load_system_certs_on_windows(X509_STORE *store) { + auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT"); + + if (!hStore) { return false; } + + PCCERT_CONTEXT pContext = NULL; + while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) != + nullptr) { + auto encoded_cert = + static_cast(pContext->pbCertEncoded); + + auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded); + if (x509) { + X509_STORE_add_cert(store, x509); + X509_free(x509); + } + } + + CertFreeCertificateContext(pContext); + CertCloseStore(hStore, 0); + + return true; +} +#endif + +class WSInit { public: WSInit() { WSADATA wsaData; @@ -1346,16 +3756,157 @@ public: static WSInit wsinit_; #endif +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + inline std::pair make_digest_authentication_header( + const Request &req, const std::map &auth, + size_t cnonce_count, const std::string &cnonce, const std::string &username, + const std::string &password, bool is_proxy = false) { + using namespace std; + + string nc; + { + stringstream ss; + ss << setfill('0') << setw(8) << hex << cnonce_count; + nc = ss.str(); + } + + auto qop = auth.at("qop"); + if (qop.find("auth-int") != std::string::npos) { + qop = "auth-int"; + } else { + qop = "auth"; + } + + std::string algo = "MD5"; + if (auth.find("algorithm") != auth.end()) { algo = auth.at("algorithm"); } + + string response; + { + auto H = algo == "SHA-256" ? detail::SHA_256 + : algo == "SHA-512" ? detail::SHA_512 + : detail::MD5; + + auto A1 = username + ":" + auth.at("realm") + ":" + password; + + auto A2 = req.method + ":" + req.path; + if (qop == "auth-int") { A2 += ":" + H(req.body); } + + response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce + + ":" + qop + ":" + H(A2)); + } + + auto field = "Digest username=\"" + username + "\", realm=\"" + + auth.at("realm") + "\", nonce=\"" + auth.at("nonce") + + "\", uri=\"" + req.path + "\", algorithm=" + algo + + ", qop=" + qop + ", nc=\"" + nc + "\", cnonce=\"" + cnonce + + "\", response=\"" + response + "\""; + + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + return std::make_pair(key, field); +} +#endif + + inline bool parse_www_authenticate(const Response &res, + std::map &auth, + bool is_proxy) { + auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate"; + if (res.has_header(auth_key)) { + static auto re = std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~"); + auto s = res.get_header_value(auth_key); + auto pos = s.find(' '); + if (pos != std::string::npos) { + auto type = s.substr(0, pos); + if (type == "Basic") { + return false; + } else if (type == "Digest") { + s = s.substr(pos + 1); + auto beg = std::sregex_iterator(s.begin(), s.end(), re); + for (auto i = beg; i != std::sregex_iterator(); ++i) { + auto m = *i; + auto key = s.substr(static_cast(m.position(1)), + static_cast(m.length(1))); + auto val = m.length(2) > 0 + ? s.substr(static_cast(m.position(2)), + static_cast(m.length(2))) + : s.substr(static_cast(m.position(3)), + static_cast(m.length(3))); + auth[key] = val; + } + return true; + } + } + } + return false; + } + +// https://stackoverflow.com/questions/440133/how-do-i-create-a-random-alpha-numeric-string-in-c/440240#answer-440240 + inline std::string random_string(size_t length) { + auto randchar = []() -> char { + const char charset[] = "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + const size_t max_index = (sizeof(charset) - 1); + return charset[static_cast(std::rand()) % max_index]; + }; + std::string str(length, 0); + std::generate_n(str.begin(), length, randchar); + return str; + } + + class ContentProviderAdapter { + public: + explicit ContentProviderAdapter( + ContentProviderWithoutLength &&content_provider) + : content_provider_(content_provider) {} + + bool operator()(size_t offset, size_t, DataSink &sink) { + return content_provider_(offset, sink); + } + + private: + ContentProviderWithoutLength content_provider_; + }; + + template + inline void duration_to_sec_and_usec(const T &duration, U callback) { + auto sec = std::chrono::duration_cast(duration).count(); + auto usec = std::chrono::duration_cast( + duration - std::chrono::seconds(sec)) + .count(); + callback(sec, usec); + } + } // namespace detail // Header utilities - template - inline std::pair make_range_header(uint64_t value, - Args... args) { - std::string field; - detail::make_range_header_core(field, value, args...); - field.insert(0, "bytes="); - return std::make_pair("Range", field); + inline std::pair make_range_header(Ranges ranges) { + std::string field = "bytes="; + auto i = 0; + for (auto r : ranges) { + if (i != 0) { field += ", "; } + if (r.first != -1) { field += std::to_string(r.first); } + field += '-'; + if (r.second != -1) { field += std::to_string(r.second); } + i++; + } + return std::make_pair("Range", std::move(field)); + } + + inline std::pair + make_basic_authentication_header(const std::string &username, + const std::string &password, + bool is_proxy = false) { + auto field = "Basic " + detail::base64_encode(username + ":" + password); + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + return std::make_pair(key, std::move(field)); + } + + inline std::pair + make_bearer_token_authentication_header(const std::string &token, + bool is_proxy = false) { + auto field = "Bearer " + token; + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + return std::make_pair(key, std::move(field)); } // Request implementation @@ -1367,13 +3918,26 @@ static WSInit wsinit_; return detail::get_header_value(headers, key, id, ""); } + template + inline T Request::get_header_value(const char *key, size_t id) const { + return detail::get_header_value(headers, key, id, 0); + } + inline size_t Request::get_header_value_count(const char *key) const { auto r = headers.equal_range(key); - return std::distance(r.first, r.second); + return static_cast(std::distance(r.first, r.second)); } inline void Request::set_header(const char *key, const char *val) { - headers.emplace(key, val); + if (!detail::has_crlf(key) && !detail::has_crlf(val)) { + headers.emplace(key, val); + } + } + + inline void Request::set_header(const char *key, const std::string &val) { + if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) { + headers.emplace(key, val); + } } inline bool Request::has_param(const char *key) const { @@ -1381,25 +3945,31 @@ static WSInit wsinit_; } inline std::string Request::get_param_value(const char *key, size_t id) const { - auto it = params.find(key); - std::advance(it, id); - if (it != params.end()) { return it->second; } + auto rng = params.equal_range(key); + auto it = rng.first; + std::advance(it, static_cast(id)); + if (it != rng.second) { return it->second; } return std::string(); } inline size_t Request::get_param_value_count(const char *key) const { auto r = params.equal_range(key); - return std::distance(r.first, r.second); + return static_cast(std::distance(r.first, r.second)); + } + + inline bool Request::is_multipart_form_data() const { + const auto &content_type = get_header_value("Content-Type"); + return !content_type.find("multipart/form-data"); } inline bool Request::has_file(const char *key) const { return files.find(key) != files.end(); } - inline MultipartFile Request::get_file_value(const char *key) const { + inline MultipartFormData Request::get_file_value(const char *key) const { auto it = files.find(key); if (it != files.end()) { return it->second; } - return MultipartFile(); + return MultipartFormData(); } // Response implementation @@ -1412,117 +3982,242 @@ static WSInit wsinit_; return detail::get_header_value(headers, key, id, ""); } + template + inline T Response::get_header_value(const char *key, size_t id) const { + return detail::get_header_value(headers, key, id, 0); + } + inline size_t Response::get_header_value_count(const char *key) const { auto r = headers.equal_range(key); - return std::distance(r.first, r.second); + return static_cast(std::distance(r.first, r.second)); } inline void Response::set_header(const char *key, const char *val) { - headers.emplace(key, val); + if (!detail::has_crlf(key) && !detail::has_crlf(val)) { + headers.emplace(key, val); + } } - inline void Response::set_redirect(const char *url) { - set_header("Location", url); - status = 302; + inline void Response::set_header(const char *key, const std::string &val) { + if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) { + headers.emplace(key, val); + } + } + + inline void Response::set_redirect(const char *url, int stat) { + if (!detail::has_crlf(url)) { + set_header("Location", url); + if (300 <= stat && stat < 400) { + this->status = stat; + } else { + this->status = 302; + } + } + } + + inline void Response::set_redirect(const std::string &url, int stat) { + set_redirect(url.c_str(), stat); } inline void Response::set_content(const char *s, size_t n, const char *content_type) { body.assign(s, n); + + auto rng = headers.equal_range("Content-Type"); + headers.erase(rng.first, rng.second); set_header("Content-Type", content_type); } inline void Response::set_content(const std::string &s, const char *content_type) { - body = s; + set_content(s.data(), s.size(), content_type); + } + + inline void Response::set_content_provider( + size_t in_length, const char *content_type, ContentProvider provider, + ContentProviderResourceReleaser resource_releaser) { + assert(in_length > 0); set_header("Content-Type", content_type); + content_length_ = in_length; + content_provider_ = std::move(provider); + content_provider_resource_releaser_ = resource_releaser; + is_chunked_content_provider_ = false; } -// Rstream implementation - template - inline void Stream::write_format(const char *fmt, const Args &... args) { - const auto bufsiz = 2048; - char buf[bufsiz]; - -#if defined(_MSC_VER) && _MSC_VER < 1900 - auto n = _snprintf_s(buf, bufsiz, bufsiz - 1, fmt, args...); -#else - auto n = snprintf(buf, bufsiz - 1, fmt, args...); -#endif - if (n > 0) { - if (n >= bufsiz - 1) { - std::vector glowable_buf(bufsiz); - - while (n >= static_cast(glowable_buf.size() - 1)) { - glowable_buf.resize(glowable_buf.size() * 2); -#if defined(_MSC_VER) && _MSC_VER < 1900 - n = _snprintf_s(&glowable_buf[0], glowable_buf.size(), - glowable_buf.size() - 1, fmt, args...); -#else - n = snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...); -#endif - } - write(&glowable_buf[0], n); - } else { - write(buf, n); - } - } + inline void Response::set_content_provider( + const char *content_type, ContentProviderWithoutLength provider, + ContentProviderResourceReleaser resource_releaser) { + set_header("Content-Type", content_type); + content_length_ = 0; + content_provider_ = detail::ContentProviderAdapter(std::move(provider)); + content_provider_resource_releaser_ = resource_releaser; + is_chunked_content_provider_ = false; } -// Socket stream implementation - inline SocketStream::SocketStream(socket_t sock) : sock_(sock) {} - - inline SocketStream::~SocketStream() {} - - inline int SocketStream::read(char *ptr, size_t size) { - if (detail::select_read(sock_, CPPHTTPLIB_READ_TIMEOUT_SECOND, - CPPHTTPLIB_READ_TIMEOUT_USECOND) > 0) { - return recv(sock_, ptr, static_cast(size), 0); - } - return -1; + inline void Response::set_chunked_content_provider( + const char *content_type, ContentProviderWithoutLength provider, + ContentProviderResourceReleaser resource_releaser) { + set_header("Content-Type", content_type); + content_length_ = 0; + content_provider_ = detail::ContentProviderAdapter(std::move(provider)); + content_provider_resource_releaser_ = resource_releaser; + is_chunked_content_provider_ = true; } - inline int SocketStream::write(const char *ptr, size_t size) { - return send(sock_, ptr, static_cast(size), 0); +// Result implementation + inline bool Result::has_request_header(const char *key) const { + return request_headers_.find(key) != request_headers_.end(); } - inline int SocketStream::write(const char *ptr) { + inline std::string Result::get_request_header_value(const char *key, + size_t id) const { + return detail::get_header_value(request_headers_, key, id, ""); + } + + template + inline T Result::get_request_header_value(const char *key, size_t id) const { + return detail::get_header_value(request_headers_, key, id, 0); + } + + inline size_t Result::get_request_header_value_count(const char *key) const { + auto r = request_headers_.equal_range(key); + return static_cast(std::distance(r.first, r.second)); + } + +// Stream implementation + inline ssize_t Stream::write(const char *ptr) { return write(ptr, strlen(ptr)); } - inline std::string SocketStream::get_remote_addr() const { - return detail::get_remote_addr(sock_); + inline ssize_t Stream::write(const std::string &s) { + return write(s.data(), s.size()); } + template + inline ssize_t Stream::write_format(const char *fmt, const Args &...args) { + const auto bufsiz = 2048; + std::array buf; + +#if defined(_MSC_VER) && _MSC_VER < 1900 + auto sn = _snprintf_s(buf.data(), bufsiz - 1, buf.size() - 1, fmt, args...); +#else + auto sn = snprintf(buf.data(), buf.size() - 1, fmt, args...); +#endif + if (sn <= 0) { return sn; } + + auto n = static_cast(sn); + + if (n >= buf.size() - 1) { + std::vector glowable_buf(buf.size()); + + while (n >= glowable_buf.size() - 1) { + glowable_buf.resize(glowable_buf.size() * 2); +#if defined(_MSC_VER) && _MSC_VER < 1900 + n = static_cast(_snprintf_s(&glowable_buf[0], glowable_buf.size(), + glowable_buf.size() - 1, fmt, + args...)); +#else + n = static_cast( + snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...)); +#endif + } + return write(&glowable_buf[0], n); + } else { + return write(buf.data(), n); + } + } + + namespace detail { + +// Socket stream implementation + inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec) + : sock_(sock), read_timeout_sec_(read_timeout_sec), + read_timeout_usec_(read_timeout_usec), + write_timeout_sec_(write_timeout_sec), + write_timeout_usec_(write_timeout_usec) {} + + inline SocketStream::~SocketStream() {} + + inline bool SocketStream::is_readable() const { + return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; + } + + inline bool SocketStream::is_writable() const { + return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0; + } + + inline ssize_t SocketStream::read(char *ptr, size_t size) { + if (!is_readable()) { return -1; } + +#ifdef _WIN32 + if (size > static_cast((std::numeric_limits::max)())) { + return -1; + } + return recv(sock_, ptr, static_cast(size), CPPHTTPLIB_RECV_FLAGS); +#else + return handle_EINTR( + [&]() { return recv(sock_, ptr, size, CPPHTTPLIB_RECV_FLAGS); }); +#endif + } + + inline ssize_t SocketStream::write(const char *ptr, size_t size) { + if (!is_writable()) { return -1; } + +#ifdef _WIN32 + if (size > static_cast((std::numeric_limits::max)())) { + return -1; + } + return send(sock_, ptr, static_cast(size), CPPHTTPLIB_SEND_FLAGS); +#else + return handle_EINTR( + [&]() { return send(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS); }); +#endif + } + + inline void SocketStream::get_remote_ip_and_port(std::string &ip, + int &port) const { + return detail::get_remote_ip_and_port(sock_, ip, port); + } + + inline socket_t SocketStream::socket() const { return sock_; } + // Buffer stream implementation - inline int BufferStream::read(char *ptr, size_t size) { -#if defined(_MSC_VER) && _MSC_VER < 1900 - return static_cast(buffer._Copy_s(ptr, size, size)); + inline bool BufferStream::is_readable() const { return true; } + + inline bool BufferStream::is_writable() const { return true; } + + inline ssize_t BufferStream::read(char *ptr, size_t size) { +#if defined(_MSC_VER) && _MSC_VER <= 1900 + auto len_read = buffer._Copy_s(ptr, size, size, position); #else - return static_cast(buffer.copy(ptr, size)); + auto len_read = buffer.copy(ptr, size, position); #endif - } + position += static_cast(len_read); + return static_cast(len_read); + } - inline int BufferStream::write(const char *ptr, size_t size) { - buffer.append(ptr, size); - return static_cast(size); - } + inline ssize_t BufferStream::write(const char *ptr, size_t size) { + buffer.append(ptr, size); + return static_cast(size); + } - inline int BufferStream::write(const char *ptr) { - size_t size = strlen(ptr); - buffer.append(ptr, size); - return static_cast(size); - } + inline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/, + int & /*port*/) const {} - inline std::string BufferStream::get_remote_addr() const { return ""; } + inline socket_t BufferStream::socket() const { return 0; } - inline const std::string &BufferStream::get_buffer() const { return buffer; } + inline const std::string &BufferStream::get_buffer() const { return buffer; } + + } // namespace detail // HTTP server implementation inline Server::Server() - : keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT), - payload_max_length_(CPPHTTPLIB_PAYLOAD_MAX_LENGTH), is_running_(false), - svr_sock_(INVALID_SOCKET), running_threads_(0) { + : new_task_queue( + [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }), + svr_sock_(INVALID_SOCKET), is_running_(false) { #ifndef _WIN32 signal(SIGPIPE, SIG_IGN); #endif @@ -1531,57 +4226,278 @@ static WSInit wsinit_; inline Server::~Server() {} inline Server &Server::Get(const char *pattern, Handler handler) { - get_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return Get(pattern, strlen(pattern), handler); + } + + inline Server &Server::Get(const char *pattern, size_t pattern_len, + Handler handler) { + get_handlers_.push_back( + std::make_pair(std::regex(pattern, pattern_len), std::move(handler))); return *this; } inline Server &Server::Post(const char *pattern, Handler handler) { - post_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return Post(pattern, strlen(pattern), handler); + } + + inline Server &Server::Post(const char *pattern, size_t pattern_len, + Handler handler) { + post_handlers_.push_back( + std::make_pair(std::regex(pattern, pattern_len), std::move(handler))); + return *this; + } + + inline Server &Server::Post(const char *pattern, + HandlerWithContentReader handler) { + return Post(pattern, strlen(pattern), handler); + } + + inline Server &Server::Post(const char *pattern, size_t pattern_len, + HandlerWithContentReader handler) { + post_handlers_for_content_reader_.push_back( + std::make_pair(std::regex(pattern, pattern_len), std::move(handler))); return *this; } inline Server &Server::Put(const char *pattern, Handler handler) { - put_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return Put(pattern, strlen(pattern), handler); + } + + inline Server &Server::Put(const char *pattern, size_t pattern_len, + Handler handler) { + put_handlers_.push_back( + std::make_pair(std::regex(pattern, pattern_len), std::move(handler))); + return *this; + } + + inline Server &Server::Put(const char *pattern, + HandlerWithContentReader handler) { + return Put(pattern, strlen(pattern), handler); + } + + inline Server &Server::Put(const char *pattern, size_t pattern_len, + HandlerWithContentReader handler) { + put_handlers_for_content_reader_.push_back( + std::make_pair(std::regex(pattern, pattern_len), std::move(handler))); return *this; } inline Server &Server::Patch(const char *pattern, Handler handler) { - patch_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return Patch(pattern, strlen(pattern), handler); + } + + inline Server &Server::Patch(const char *pattern, size_t pattern_len, + Handler handler) { + patch_handlers_.push_back( + std::make_pair(std::regex(pattern, pattern_len), std::move(handler))); + return *this; + } + + inline Server &Server::Patch(const char *pattern, + HandlerWithContentReader handler) { + return Patch(pattern, strlen(pattern), handler); + } + + inline Server &Server::Patch(const char *pattern, size_t pattern_len, + HandlerWithContentReader handler) { + patch_handlers_for_content_reader_.push_back( + std::make_pair(std::regex(pattern, pattern_len), std::move(handler))); return *this; } inline Server &Server::Delete(const char *pattern, Handler handler) { - delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return Delete(pattern, strlen(pattern), handler); + } + + inline Server &Server::Delete(const char *pattern, size_t pattern_len, + Handler handler) { + delete_handlers_.push_back( + std::make_pair(std::regex(pattern, pattern_len), std::move(handler))); + return *this; + } + + inline Server &Server::Delete(const char *pattern, + HandlerWithContentReader handler) { + return Delete(pattern, strlen(pattern), handler); + } + + inline Server &Server::Delete(const char *pattern, size_t pattern_len, + HandlerWithContentReader handler) { + delete_handlers_for_content_reader_.push_back( + std::make_pair(std::regex(pattern, pattern_len), std::move(handler))); return *this; } inline Server &Server::Options(const char *pattern, Handler handler) { - options_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return Options(pattern, strlen(pattern), handler); + } + + inline Server &Server::Options(const char *pattern, size_t pattern_len, + Handler handler) { + options_handlers_.push_back( + std::make_pair(std::regex(pattern, pattern_len), std::move(handler))); return *this; } - inline bool Server::set_base_dir(const char *path) { - if (detail::is_dir(path)) { - base_dir_ = path; - return true; + inline bool Server::set_base_dir(const char *dir, const char *mount_point) { + return set_mount_point(mount_point, dir); + } + + inline bool Server::set_mount_point(const char *mount_point, const char *dir, + Headers headers) { + if (detail::is_dir(dir)) { + std::string mnt = mount_point ? mount_point : "/"; + if (!mnt.empty() && mnt[0] == '/') { + base_dirs_.push_back({mnt, dir, std::move(headers)}); + return true; + } } return false; } - inline void Server::set_error_handler(Handler handler) { - error_handler_ = handler; + inline bool Server::remove_mount_point(const char *mount_point) { + for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) { + if (it->mount_point == mount_point) { + base_dirs_.erase(it); + return true; + } + } + return false; } - inline void Server::set_logger(Logger logger) { logger_ = logger; } + inline Server & + Server::set_file_extension_and_mimetype_mapping(const char *ext, + const char *mime) { + file_extension_and_mimetype_map_[ext] = mime; + return *this; + } - inline void Server::set_keep_alive_max_count(size_t count) { + inline Server &Server::set_file_request_handler(Handler handler) { + file_request_handler_ = std::move(handler); + return *this; + } + + inline Server &Server::set_error_handler(HandlerWithResponse handler) { + error_handler_ = std::move(handler); + return *this; + } + + inline Server &Server::set_error_handler(Handler handler) { + error_handler_ = [handler](const Request &req, Response &res) { + handler(req, res); + return HandlerResponse::Handled; + }; + return *this; + } + + inline Server &Server::set_exception_handler(ExceptionHandler handler) { + exception_handler_ = std::move(handler); + return *this; + } + + inline Server &Server::set_pre_routing_handler(HandlerWithResponse handler) { + pre_routing_handler_ = std::move(handler); + return *this; + } + + inline Server &Server::set_post_routing_handler(Handler handler) { + post_routing_handler_ = std::move(handler); + return *this; + } + + inline Server &Server::set_logger(Logger logger) { + logger_ = std::move(logger); + return *this; + } + + inline Server & + Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) { + expect_100_continue_handler_ = std::move(handler); + + return *this; + } + + inline Server &Server::set_address_family(int family) { + address_family_ = family; + return *this; + } + + inline Server &Server::set_tcp_nodelay(bool on) { + tcp_nodelay_ = on; + return *this; + } + + inline Server &Server::set_socket_options(SocketOptions socket_options) { + socket_options_ = std::move(socket_options); + return *this; + } + + inline Server &Server::set_default_headers(Headers headers) { + default_headers_ = std::move(headers); + return *this; + } + + inline Server &Server::set_keep_alive_max_count(size_t count) { keep_alive_max_count_ = count; + return *this; } - inline void Server::set_payload_max_length(uint64_t length) { + inline Server &Server::set_keep_alive_timeout(time_t sec) { + keep_alive_timeout_sec_ = sec; + return *this; + } + + inline Server &Server::set_read_timeout(time_t sec, time_t usec) { + read_timeout_sec_ = sec; + read_timeout_usec_ = usec; + return *this; + } + + template + inline Server & + Server::set_read_timeout(const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec( + duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); }); + return *this; + } + + inline Server &Server::set_write_timeout(time_t sec, time_t usec) { + write_timeout_sec_ = sec; + write_timeout_usec_ = usec; + return *this; + } + + template + inline Server & + Server::set_write_timeout(const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec( + duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); }); + return *this; + } + + inline Server &Server::set_idle_interval(time_t sec, time_t usec) { + idle_interval_sec_ = sec; + idle_interval_usec_ = usec; + return *this; + } + + template + inline Server & + Server::set_idle_interval(const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec( + duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); }); + return *this; + } + + inline Server &Server::set_payload_max_length(size_t length) { payload_max_length_ = length; + return *this; } + inline bool Server::bind_to_port(const char *host, int port, int socket_flags) { + if (bind_internal(host, port, socket_flags) < 0) return false; + return true; + } inline int Server::bind_to_any_port(const char *host, int socket_flags) { return bind_internal(host, 0, socket_flags); } @@ -1589,8 +4505,7 @@ static WSInit wsinit_; inline bool Server::listen_after_bind() { return listen_internal(); } inline bool Server::listen(const char *host, int port, int socket_flags) { - if (bind_internal(host, port, socket_flags) < 0) return false; - return listen_internal(); + return bind_to_port(host, port, socket_flags) && listen_internal(); } inline bool Server::is_running() const { return is_running_; } @@ -1598,22 +4513,23 @@ static WSInit wsinit_; inline void Server::stop() { if (is_running_) { assert(svr_sock_ != INVALID_SOCKET); - std::atomic sock (svr_sock_.exchange(INVALID_SOCKET)); + std::atomic sock(svr_sock_.exchange(INVALID_SOCKET)); detail::shutdown_socket(sock); detail::close_socket(sock); } } inline bool Server::parse_request_line(const char *s, Request &req) { - static std::regex re("(GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS) " - "(([^?]+)(?:\\?(.+?))?) (HTTP/1\\.[01])\r\n"); + const static std::regex re( + "(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH|PRI) " + "(([^? ]+)(?:\\?([^ ]*?))?) (HTTP/1\\.[01])\r\n"); std::cmatch m; if (std::regex_match(s, m, re)) { req.version = std::string(m[5]); req.method = std::string(m[1]); req.target = std::string(m[2]); - req.path = detail::decode_url(m[3]); + req.path = detail::decode_url(m[3], false); // Parse query text auto len = std::distance(m[4].first, m[4].second); @@ -1625,131 +4541,304 @@ static WSInit wsinit_; return false; } - inline void Server::write_response(Stream &strm, bool last_connection, + inline bool Server::write_response(Stream &strm, bool close_connection, const Request &req, Response &res) { + return write_response_core(strm, close_connection, req, res, false); + } + + inline bool Server::write_response_with_content(Stream &strm, + bool close_connection, + const Request &req, + Response &res) { + return write_response_core(strm, close_connection, req, res, true); + } + + inline bool Server::write_response_core(Stream &strm, bool close_connection, + const Request &req, Response &res, + bool need_apply_ranges) { assert(res.status != -1); - if (400 <= res.status && error_handler_) { error_handler_(req, res); } + if (400 <= res.status && error_handler_ && + error_handler_(req, res) == HandlerResponse::Handled) { + need_apply_ranges = true; + } - // Response line - strm.write_format("HTTP/1.1 %d %s\r\n", res.status, - detail::status_message(res.status)); + std::string content_type; + std::string boundary; + if (need_apply_ranges) { apply_ranges(req, res, content_type, boundary); } - // Headers - if (last_connection || req.get_header_value("Connection") == "close") { + // Prepare additional headers + if (close_connection || req.get_header_value("Connection") == "close") { res.set_header("Connection", "close"); - } - - if (!last_connection && req.get_header_value("Connection") == "Keep-Alive") { - res.set_header("Connection", "Keep-Alive"); - } - - if (res.body.empty()) { - if (!res.has_header("Content-Length")) { - if (res.streamcb) { - // Streamed response - res.set_header("Transfer-Encoding", "chunked"); - } else { - res.set_header("Content-Length", "0"); - } - } } else { -#ifdef CPPHTTPLIB_ZLIB_SUPPORT - // TODO: 'Accpet-Encoding' has gzip, not gzip;q=0 - const auto &encodings = req.get_header_value("Accept-Encoding"); - if (encodings.find("gzip") != std::string::npos && - detail::can_compress(res.get_header_value("Content-Type"))) { - detail::compress(res.body); - res.set_header("Content-Encoding", "gzip"); - } -#endif - - if (!res.has_header("Content-Type")) { - res.set_header("Content-Type", "text/plain"); - } - - auto length = std::to_string(res.body.size()); - res.set_header("Content-Length", length.c_str()); + std::stringstream ss; + ss << "timeout=" << keep_alive_timeout_sec_ + << ", max=" << keep_alive_max_count_; + res.set_header("Keep-Alive", ss.str()); } - detail::write_headers(strm, res); + if (!res.has_header("Content-Type") && + (!res.body.empty() || res.content_length_ > 0 || res.content_provider_)) { + res.set_header("Content-Type", "text/plain"); + } + + if (!res.has_header("Content-Length") && res.body.empty() && + !res.content_length_ && !res.content_provider_) { + res.set_header("Content-Length", "0"); + } + + if (!res.has_header("Accept-Ranges") && req.method == "HEAD") { + res.set_header("Accept-Ranges", "bytes"); + } + + if (post_routing_handler_) { post_routing_handler_(req, res); } + + // Response line and headers + { + detail::BufferStream bstrm; + + if (!bstrm.write_format("HTTP/1.1 %d %s\r\n", res.status, + detail::status_message(res.status))) { + return false; + } + + if (!detail::write_headers(bstrm, res.headers)) { return false; } + + // Flush buffer + auto &data = bstrm.get_buffer(); + strm.write(data.data(), data.size()); + } // Body + auto ret = true; if (req.method != "HEAD") { if (!res.body.empty()) { - strm.write(res.body.c_str(), res.body.size()); - } else if (res.streamcb) { - bool chunked_response = !res.has_header("Content-Length"); - uint64_t offset = 0; - bool data_available = true; - while (data_available) { - std::string chunk = res.streamcb(offset); - offset += chunk.size(); - data_available = !chunk.empty(); - // Emit chunked response header and footer for each chunk - if (chunked_response) - chunk = detail::from_i_to_hex(chunk.size()) + "\r\n" + chunk + "\r\n"; - if (strm.write(chunk.c_str(), chunk.size()) < 0) break; // Stop on error + if (!strm.write(res.body)) { ret = false; } + } else if (res.content_provider_) { + if (write_content_with_provider(strm, req, res, boundary, + content_type)) { + res.content_provider_success_ = true; + } else { + res.content_provider_success_ = false; + ret = false; } } } // Log if (logger_) { logger_(req, res); } + + return ret; } - inline bool Server::handle_file_request(Request &req, Response &res) { - if (!base_dir_.empty() && detail::is_valid_path(req.path)) { - std::string path = base_dir_ + req.path; + inline bool + Server::write_content_with_provider(Stream &strm, const Request &req, + Response &res, const std::string &boundary, + const std::string &content_type) { + auto is_shutting_down = [this]() { + return this->svr_sock_ == INVALID_SOCKET; + }; - if (!path.empty() && path.back() == '/') { path += "index.html"; } + if (res.content_length_ > 0) { + if (req.ranges.empty()) { + return detail::write_content(strm, res.content_provider_, 0, + res.content_length_, is_shutting_down); + } else if (req.ranges.size() == 1) { + auto offsets = + detail::get_range_offset_and_length(req, res.content_length_, 0); + auto offset = offsets.first; + auto length = offsets.second; + return detail::write_content(strm, res.content_provider_, offset, length, + is_shutting_down); + } else { + return detail::write_multipart_ranges_data( + strm, req, res, boundary, content_type, is_shutting_down); + } + } else { + if (res.is_chunked_content_provider_) { + auto type = detail::encoding_type(req, res); - if (detail::is_file(path)) { - detail::read_file(path, res.body); - auto type = detail::find_content_type(path); - if (type) { res.set_header("Content-Type", type); } - res.status = 200; - return true; + std::unique_ptr compressor; + if (type == detail::EncodingType::Gzip) { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + compressor = detail::make_unique(); +#endif + } else if (type == detail::EncodingType::Brotli) { +#ifdef CPPHTTPLIB_BROTLI_SUPPORT + compressor = detail::make_unique(); +#endif + } else { + compressor = detail::make_unique(); + } + assert(compressor != nullptr); + + return detail::write_content_chunked(strm, res.content_provider_, + is_shutting_down, *compressor); + } else { + return detail::write_content_without_length(strm, res.content_provider_, + is_shutting_down); } } + } + inline bool Server::read_content(Stream &strm, Request &req, Response &res) { + MultipartFormDataMap::iterator cur; + if (read_content_core( + strm, req, res, + // Regular + [&](const char *buf, size_t n) { + if (req.body.size() + n > req.body.max_size()) { return false; } + req.body.append(buf, n); + return true; + }, + // Multipart + [&](const MultipartFormData &file) { + cur = req.files.emplace(file.name, file); + return true; + }, + [&](const char *buf, size_t n) { + auto &content = cur->second.content; + if (content.size() + n > content.max_size()) { return false; } + content.append(buf, n); + return true; + })) { + const auto &content_type = req.get_header_value("Content-Type"); + if (!content_type.find("application/x-www-form-urlencoded")) { + detail::parse_query_text(req.body, req.params); + } + return true; + } return false; } - inline socket_t Server::create_server_socket(const char *host, int port, - int socket_flags) const { + inline bool Server::read_content_with_content_receiver( + Stream &strm, Request &req, Response &res, ContentReceiver receiver, + MultipartContentHeader multipart_header, + ContentReceiver multipart_receiver) { + return read_content_core(strm, req, res, std::move(receiver), + std::move(multipart_header), + std::move(multipart_receiver)); + } + + inline bool Server::read_content_core(Stream &strm, Request &req, Response &res, + ContentReceiver receiver, + MultipartContentHeader mulitpart_header, + ContentReceiver multipart_receiver) { + detail::MultipartFormDataParser multipart_form_data_parser; + ContentReceiverWithProgress out; + + if (req.is_multipart_form_data()) { + const auto &content_type = req.get_header_value("Content-Type"); + std::string boundary; + if (!detail::parse_multipart_boundary(content_type, boundary)) { + res.status = 400; + return false; + } + + multipart_form_data_parser.set_boundary(std::move(boundary)); + out = [&](const char *buf, size_t n, uint64_t /*off*/, uint64_t /*len*/) { + /* For debug + size_t pos = 0; + while (pos < n) { + auto read_size = std::min(1, n - pos); + auto ret = multipart_form_data_parser.parse( + buf + pos, read_size, multipart_receiver, mulitpart_header); + if (!ret) { return false; } + pos += read_size; + } + return true; + */ + return multipart_form_data_parser.parse(buf, n, multipart_receiver, + mulitpart_header); + }; + } else { + out = [receiver](const char *buf, size_t n, uint64_t /*off*/, + uint64_t /*len*/) { return receiver(buf, n); }; + } + + if (req.method == "DELETE" && !req.has_header("Content-Length")) { + return true; + } + + if (!detail::read_content(strm, req, payload_max_length_, res.status, nullptr, + out, true)) { + return false; + } + + if (req.is_multipart_form_data()) { + if (!multipart_form_data_parser.is_valid()) { + res.status = 400; + return false; + } + } + + return true; + } + + inline bool Server::handle_file_request(const Request &req, Response &res, + bool head) { + for (const auto &entry : base_dirs_) { + // Prefix match + if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) { + std::string sub_path = "/" + req.path.substr(entry.mount_point.size()); + if (detail::is_valid_path(sub_path)) { + auto path = entry.base_dir + sub_path; + if (path.back() == '/') { path += "index.html"; } + + if (detail::is_file(path)) { + detail::read_file(path, res.body); + auto type = + detail::find_content_type(path, file_extension_and_mimetype_map_); + if (type) { res.set_header("Content-Type", type); } + for (const auto &kv : entry.headers) { + res.set_header(kv.first.c_str(), kv.second); + } + res.status = req.has_header("Range") ? 206 : 200; + if (!head && file_request_handler_) { + file_request_handler_(req, res); + } + return true; + } + } + } + } + return false; + } + + inline socket_t + Server::create_server_socket(const char *host, int port, int socket_flags, + SocketOptions socket_options) const { return detail::create_socket( - host, port, + host, port, address_family_, socket_flags, tcp_nodelay_, + std::move(socket_options), [](socket_t sock, struct addrinfo &ai) -> bool { - if (::bind(sock, ai.ai_addr, static_cast(ai.ai_addrlen))) { + if (::bind(sock, ai.ai_addr, static_cast(ai.ai_addrlen))) { return false; } if (::listen(sock, 5)) { // Listen through 5 channels return false; } return true; - }, - socket_flags); + }); } inline int Server::bind_internal(const char *host, int port, int socket_flags) { if (!is_valid()) { return -1; } - svr_sock_ = create_server_socket(host, port, socket_flags); + svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_); if (svr_sock_ == INVALID_SOCKET) { return -1; } if (port == 0) { - struct sockaddr_storage address; - socklen_t len = sizeof(address); - if (getsockname(svr_sock_, reinterpret_cast(&address), - &len) == -1) { + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + if (getsockname(svr_sock_, reinterpret_cast(&addr), + &addr_len) == -1) { return -1; } - if (address.ss_family == AF_INET) { - return ntohs(reinterpret_cast(&address)->sin_port); - } else if (address.ss_family == AF_INET6) { - return ntohs( - reinterpret_cast(&address)->sin6_port); + if (addr.ss_family == AF_INET) { + return ntohs(reinterpret_cast(&addr)->sin_port); + } else if (addr.ss_family == AF_INET6) { + return ntohs(reinterpret_cast(&addr)->sin6_port); } else { return -1; } @@ -1758,119 +4847,150 @@ static WSInit wsinit_; } } - inline bool Server::poll() { - auto ret = true; - - is_running_ = true; - - if (svr_sock_ == INVALID_SOCKET) { - // The server socket was closed by 'stop' method. - return false; - } - - auto val = detail::select_read(svr_sock_, 0, 1000); - - if (val == 0) { // Timeout - return false; - } - - socket_t sock = accept(svr_sock_, nullptr, nullptr); - - if (sock == INVALID_SOCKET) { - if (svr_sock_ != INVALID_SOCKET) { - detail::close_socket(svr_sock_); - ret = false; - } else { - ; // The server socket was closed by user. - } - return false; - } - - read_and_close_socket(sock); - - is_running_ = false; - - return ret; - } - inline bool Server::listen_internal() { auto ret = true; - is_running_ = true; - for (;;) { - if (svr_sock_ == INVALID_SOCKET) { - // The server socket was closed by 'stop' method. - break; - } + { + std::unique_ptr task_queue(new_task_queue()); - auto val = detail::select_read(svr_sock_, 0, 100000); - - if (val == 0) { // Timeout - continue; - } - - socket_t sock = accept(svr_sock_, nullptr, nullptr); - - if (sock == INVALID_SOCKET) { - if (svr_sock_ != INVALID_SOCKET) { - detail::close_socket(svr_sock_); - ret = false; - } else { - ; // The server socket was closed by user. + while (svr_sock_ != INVALID_SOCKET) { +#ifndef _WIN32 + if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) { +#endif + auto val = detail::select_read(svr_sock_, idle_interval_sec_, + idle_interval_usec_); + if (val == 0) { // Timeout + task_queue->on_idle(); + continue; + } +#ifndef _WIN32 } - break; - } +#endif + socket_t sock = accept(svr_sock_, nullptr, nullptr); - // TODO: Use thread pool... - std::thread([=]() { - { - std::lock_guard guard(running_threads_mutex_); - running_threads_++; + if (sock == INVALID_SOCKET) { + if (errno == EMFILE) { + // The per-process limit of open file descriptors has been reached. + // Try to accept new connections after a short sleep. + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + continue; + } + if (svr_sock_ != INVALID_SOCKET) { + detail::close_socket(svr_sock_); + ret = false; + } else { + ; // The server socket was closed by user. + } + break; } - read_and_close_socket(sock); - { - std::lock_guard guard(running_threads_mutex_); - running_threads_--; + timeval tv; + tv.tv_sec = static_cast(read_timeout_sec_); + tv.tv_usec = static_cast(read_timeout_usec_); + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)); + } + { + timeval tv; + tv.tv_sec = static_cast(write_timeout_sec_); + tv.tv_usec = static_cast(write_timeout_usec_); + setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv)); } - }).detach(); - } - // TODO: Use thread pool... - for (;;) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - std::lock_guard guard(running_threads_mutex_); - if (!running_threads_) { break; } +#if __cplusplus > 201703L + task_queue->enqueue([=, this]() { process_and_close_socket(sock); }); +#else + task_queue->enqueue([=]() { process_and_close_socket(sock); }); +#endif + } + + task_queue->shutdown(); } is_running_ = false; - return ret; } - inline bool Server::routing(Request &req, Response &res) { - if (req.method == "GET" && handle_file_request(req, res)) { return true; } + inline bool Server::routing(Request &req, Response &res, Stream &strm) { + if (pre_routing_handler_ && + pre_routing_handler_(req, res) == HandlerResponse::Handled) { + return true; + } + // File handler + bool is_head_request = req.method == "HEAD"; + if ((req.method == "GET" || is_head_request) && + handle_file_request(req, res, is_head_request)) { + return true; + } + + if (detail::expect_content(req)) { + // Content reader handler + { + ContentReader reader( + [&](ContentReceiver receiver) { + return read_content_with_content_receiver( + strm, req, res, std::move(receiver), nullptr, nullptr); + }, + [&](MultipartContentHeader header, ContentReceiver receiver) { + return read_content_with_content_receiver(strm, req, res, nullptr, + std::move(header), + std::move(receiver)); + }); + + if (req.method == "POST") { + if (dispatch_request_for_content_reader( + req, res, std::move(reader), + post_handlers_for_content_reader_)) { + return true; + } + } else if (req.method == "PUT") { + if (dispatch_request_for_content_reader( + req, res, std::move(reader), + put_handlers_for_content_reader_)) { + return true; + } + } else if (req.method == "PATCH") { + if (dispatch_request_for_content_reader( + req, res, std::move(reader), + patch_handlers_for_content_reader_)) { + return true; + } + } else if (req.method == "DELETE") { + if (dispatch_request_for_content_reader( + req, res, std::move(reader), + delete_handlers_for_content_reader_)) { + return true; + } + } + } + + // Read content into `req.body` + if (!read_content(strm, req, res)) { return false; } + } + + // Regular handler if (req.method == "GET" || req.method == "HEAD") { return dispatch_request(req, res, get_handlers_); } else if (req.method == "POST") { return dispatch_request(req, res, post_handlers_); } else if (req.method == "PUT") { return dispatch_request(req, res, put_handlers_); - } else if (req.method == "PATCH") { - return dispatch_request(req, res, patch_handlers_); } else if (req.method == "DELETE") { return dispatch_request(req, res, delete_handlers_); } else if (req.method == "OPTIONS") { return dispatch_request(req, res, options_handlers_); + } else if (req.method == "PATCH") { + return dispatch_request(req, res, patch_handlers_); } + + res.status = 400; return false; } inline bool Server::dispatch_request(Request &req, Response &res, - Handlers &handlers) { + const Handlers &handlers) { for (const auto &x : handlers) { const auto &pattern = x.first; const auto &handler = x.second; @@ -1883,424 +5003,1509 @@ static WSInit wsinit_; return false; } - inline bool - Server::process_request(Stream &strm, bool last_connection, - bool &connection_close, - std::function setup_request) { - const auto bufsiz = 2048; - char buf[bufsiz]; + inline void Server::apply_ranges(const Request &req, Response &res, + std::string &content_type, + std::string &boundary) { + if (req.ranges.size() > 1) { + boundary = detail::make_multipart_data_boundary(); - detail::stream_line_reader reader(strm, buf, bufsiz); + auto it = res.headers.find("Content-Type"); + if (it != res.headers.end()) { + content_type = it->second; + res.headers.erase(it); + } + + res.headers.emplace("Content-Type", + "multipart/byteranges; boundary=" + boundary); + } + + auto type = detail::encoding_type(req, res); + + if (res.body.empty()) { + if (res.content_length_ > 0) { + size_t length = 0; + if (req.ranges.empty()) { + length = res.content_length_; + } else if (req.ranges.size() == 1) { + auto offsets = + detail::get_range_offset_and_length(req, res.content_length_, 0); + auto offset = offsets.first; + length = offsets.second; + auto content_range = detail::make_content_range_header_field( + offset, length, res.content_length_); + res.set_header("Content-Range", content_range); + } else { + length = detail::get_multipart_ranges_data_length(req, res, boundary, + content_type); + } + res.set_header("Content-Length", std::to_string(length)); + } else { + if (res.content_provider_) { + if (res.is_chunked_content_provider_) { + res.set_header("Transfer-Encoding", "chunked"); + if (type == detail::EncodingType::Gzip) { + res.set_header("Content-Encoding", "gzip"); + } else if (type == detail::EncodingType::Brotli) { + res.set_header("Content-Encoding", "br"); + } + } + } + } + } else { + if (req.ranges.empty()) { + ; + } else if (req.ranges.size() == 1) { + auto offsets = + detail::get_range_offset_and_length(req, res.body.size(), 0); + auto offset = offsets.first; + auto length = offsets.second; + auto content_range = detail::make_content_range_header_field( + offset, length, res.body.size()); + res.set_header("Content-Range", content_range); + if (offset < res.body.size()) { + res.body = res.body.substr(offset, length); + } else { + res.body.clear(); + res.status = 416; + } + } else { + std::string data; + if (detail::make_multipart_ranges_data(req, res, boundary, content_type, + data)) { + res.body.swap(data); + } else { + res.body.clear(); + res.status = 416; + } + } + + if (type != detail::EncodingType::None) { + std::unique_ptr compressor; + std::string content_encoding; + + if (type == detail::EncodingType::Gzip) { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + compressor = detail::make_unique(); + content_encoding = "gzip"; +#endif + } else if (type == detail::EncodingType::Brotli) { +#ifdef CPPHTTPLIB_BROTLI_SUPPORT + compressor = detail::make_unique(); + content_encoding = "br"; +#endif + } + + if (compressor) { + std::string compressed; + if (compressor->compress(res.body.data(), res.body.size(), true, + [&](const char *data, size_t data_len) { + compressed.append(data, data_len); + return true; + })) { + res.body.swap(compressed); + res.set_header("Content-Encoding", content_encoding); + } + } + } + + auto length = std::to_string(res.body.size()); + res.set_header("Content-Length", length); + } + } + + inline bool Server::dispatch_request_for_content_reader( + Request &req, Response &res, ContentReader content_reader, + const HandlersForContentReader &handlers) { + for (const auto &x : handlers) { + const auto &pattern = x.first; + const auto &handler = x.second; + + if (std::regex_match(req.path, req.matches, pattern)) { + handler(req, res, content_reader); + return true; + } + } + return false; + } + + inline bool + Server::process_request(Stream &strm, bool close_connection, + bool &connection_closed, + const std::function &setup_request) { + std::array buf{}; + + detail::stream_line_reader line_reader(strm, buf.data(), buf.size()); // Connection has been closed on client - if (!reader.getline()) { return false; } + if (!line_reader.getline()) { return false; } Request req; Response res; res.version = "HTTP/1.1"; + for (const auto &header : default_headers_) { + if (res.headers.find(header.first) == res.headers.end()) { + res.headers.insert(header); + } + } + +#ifdef _WIN32 + // TODO: Increase FD_SETSIZE statically (libzmq), dynamically (MySQL). +#else +#ifndef CPPHTTPLIB_USE_POLL + // Socket file descriptor exceeded FD_SETSIZE... + if (strm.socket() >= FD_SETSIZE) { + Headers dummy; + detail::read_headers(strm, dummy); + res.status = 500; + return write_response(strm, close_connection, req, res); + } +#endif +#endif + // Check if the request URI doesn't exceed the limit - if (reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) { + if (line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) { + Headers dummy; + detail::read_headers(strm, dummy); res.status = 414; - write_response(strm, last_connection, req, res); - return true; + return write_response(strm, close_connection, req, res); } // Request line and headers - if (!parse_request_line(reader.ptr(), req) || + if (!parse_request_line(line_reader.ptr(), req) || !detail::read_headers(strm, req.headers)) { res.status = 400; - write_response(strm, last_connection, req, res); - return true; + return write_response(strm, close_connection, req, res); } if (req.get_header_value("Connection") == "close") { - connection_close = true; + connection_closed = true; } - req.set_header("REMOTE_ADDR", strm.get_remote_addr().c_str()); + if (req.version == "HTTP/1.0" && + req.get_header_value("Connection") != "Keep-Alive") { + connection_closed = true; + } - // Body - if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH") { - bool exceed_payload_max_length = false; - if (!detail::read_content(strm, req, payload_max_length_, - exceed_payload_max_length)) { - res.status = exceed_payload_max_length ? 413 : 400; - write_response(strm, last_connection, req, res); - return !exceed_payload_max_length; - } + strm.get_remote_ip_and_port(req.remote_addr, req.remote_port); + req.set_header("REMOTE_ADDR", req.remote_addr); + req.set_header("REMOTE_PORT", std::to_string(req.remote_port)); - const auto &content_type = req.get_header_value("Content-Type"); - - if (req.get_header_value("Content-Encoding") == "gzip") { -#ifdef CPPHTTPLIB_ZLIB_SUPPORT - detail::decompress(req.body); -#else - res.status = 415; - write_response(strm, last_connection, req, res); - return true; -#endif - } - - if (!content_type.find("application/x-www-form-urlencoded")) { - detail::parse_query_text(req.body, req.params); - } else if (!content_type.find("multipart/form-data")) { - std::string boundary; - if (!detail::parse_multipart_boundary(content_type, boundary) || - !detail::parse_multipart_formdata(boundary, req.body, req.files)) { - res.status = 400; - write_response(strm, last_connection, req, res); - return true; - } + if (req.has_header("Range")) { + const auto &range_header_value = req.get_header_value("Range"); + if (!detail::parse_range_header(range_header_value, req.ranges)) { + res.status = 416; + return write_response(strm, close_connection, req, res); } } - // TODO: Add additional request info if (setup_request) { setup_request(req); } - if (routing(req, res)) { - if (res.status == -1) { res.status = 200; } - } else { - res.status = 404; + if (req.get_header_value("Expect") == "100-continue") { + auto status = 100; + if (expect_100_continue_handler_) { + status = expect_100_continue_handler_(req, res); + } + switch (status) { + case 100: + case 417: + strm.write_format("HTTP/1.1 %d %s\r\n\r\n", status, + detail::status_message(status)); + break; + default: return write_response(strm, close_connection, req, res); + } } - write_response(strm, last_connection, req, res); - return true; + // Rounting + bool routed = false; + try { + routed = routing(req, res, strm); + } catch (std::exception &e) { + if (exception_handler_) { + exception_handler_(req, res, e); + routed = true; + } else { + res.status = 500; + res.set_header("EXCEPTION_WHAT", e.what()); + } + } catch (...) { + res.status = 500; + res.set_header("EXCEPTION_WHAT", "UNKNOWN"); + } + + if (routed) { + if (res.status == -1) { res.status = req.ranges.empty() ? 200 : 206; } + return write_response_with_content(strm, close_connection, req, res); + } else { + if (res.status == -1) { res.status = 404; } + return write_response(strm, close_connection, req, res); + } } inline bool Server::is_valid() const { return true; } - inline bool Server::read_and_close_socket(socket_t sock) { - return detail::read_and_close_socket( - sock, keep_alive_max_count_, - [this](Stream &strm, bool last_connection, bool &connection_close) { - return process_request(strm, last_connection, connection_close); + inline bool Server::process_and_close_socket(socket_t sock) { + auto ret = detail::process_server_socket( + sock, keep_alive_max_count_, keep_alive_timeout_sec_, read_timeout_sec_, + read_timeout_usec_, write_timeout_sec_, write_timeout_usec_, + [this](Stream &strm, bool close_connection, bool &connection_closed) { + return process_request(strm, close_connection, connection_closed, + nullptr); }); + + detail::shutdown_socket(sock); + detail::close_socket(sock); + return ret; } // HTTP client implementation - inline Client::Client(const char *host, int port, time_t timeout_sec) - : host_(host), port_(port), timeout_sec_(timeout_sec), - host_and_port_(host_ + ":" + std::to_string(port_)) {} + inline ClientImpl::ClientImpl(const std::string &host) + : ClientImpl(host, 80, std::string(), std::string()) {} - inline Client::~Client() {} + inline ClientImpl::ClientImpl(const std::string &host, int port) + : ClientImpl(host, port, std::string(), std::string()) {} - inline bool Client::is_valid() const { return true; } + inline ClientImpl::ClientImpl(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path) + // : (Error::Success), host_(host), port_(port), + : host_(host), port_(port), + host_and_port_(host_ + ":" + std::to_string(port_)), + client_cert_path_(client_cert_path), client_key_path_(client_key_path) {} - inline socket_t Client::create_client_socket() const { - return detail::create_socket( - host_.c_str(), port_, [=](socket_t sock, struct addrinfo &ai) -> bool { - detail::set_nonblocking(sock, true); - - auto ret = connect(sock, ai.ai_addr, static_cast(ai.ai_addrlen)); - if (ret < 0) { - if (detail::is_connection_error() || - !detail::wait_until_socket_is_ready(sock, timeout_sec_, 0)) { - detail::close_socket(sock); - return false; - } - } - - detail::set_nonblocking(sock, false); - return true; - }); + inline ClientImpl::~ClientImpl() { + std::lock_guard guard(socket_mutex_); + shutdown_socket(socket_); + close_socket(socket_); } - inline bool Client::read_response_line(Stream &strm, Response &res) { - const auto bufsiz = 2048; - char buf[bufsiz]; + inline bool ClientImpl::is_valid() const { return true; } - detail::stream_line_reader reader(strm, buf, bufsiz); + inline void ClientImpl::copy_settings(const ClientImpl &rhs) { + client_cert_path_ = rhs.client_cert_path_; + client_key_path_ = rhs.client_key_path_; + connection_timeout_sec_ = rhs.connection_timeout_sec_; + read_timeout_sec_ = rhs.read_timeout_sec_; + read_timeout_usec_ = rhs.read_timeout_usec_; + write_timeout_sec_ = rhs.write_timeout_sec_; + write_timeout_usec_ = rhs.write_timeout_usec_; + basic_auth_username_ = rhs.basic_auth_username_; + basic_auth_password_ = rhs.basic_auth_password_; + bearer_token_auth_token_ = rhs.bearer_token_auth_token_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + digest_auth_username_ = rhs.digest_auth_username_; + digest_auth_password_ = rhs.digest_auth_password_; +#endif + keep_alive_ = rhs.keep_alive_; + follow_location_ = rhs.follow_location_; + url_encode_ = rhs.url_encode_; + address_family_ = rhs.address_family_; + tcp_nodelay_ = rhs.tcp_nodelay_; + socket_options_ = rhs.socket_options_; + compress_ = rhs.compress_; + decompress_ = rhs.decompress_; + interface_ = rhs.interface_; + proxy_host_ = rhs.proxy_host_; + proxy_port_ = rhs.proxy_port_; + proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_; + proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_; + proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_; + proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_; +#endif +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + server_certificate_verification_ = rhs.server_certificate_verification_; +#endif + logger_ = rhs.logger_; + } - if (!reader.getline()) { return false; } + inline socket_t ClientImpl::create_client_socket(Error &error) const { + if (!proxy_host_.empty() && proxy_port_ != -1) { + return detail::create_client_socket( + proxy_host_.c_str(), proxy_port_, address_family_, tcp_nodelay_, + socket_options_, connection_timeout_sec_, connection_timeout_usec_, + read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, + write_timeout_usec_, interface_, error); + } + return detail::create_client_socket( + host_.c_str(), port_, address_family_, tcp_nodelay_, socket_options_, + connection_timeout_sec_, connection_timeout_usec_, read_timeout_sec_, + read_timeout_usec_, write_timeout_sec_, write_timeout_usec_, interface_, + error); + } - const static std::regex re("(HTTP/1\\.[01]) (\\d+?) .*\r\n"); + inline bool ClientImpl::create_and_connect_socket(Socket &socket, + Error &error) { + auto sock = create_client_socket(error); + if (sock == INVALID_SOCKET) { return false; } + socket.sock = sock; + return true; + } + + inline void ClientImpl::shutdown_ssl(Socket & /*socket*/, + bool /*shutdown_gracefully*/) { + // If there are any requests in flight from threads other than us, then it's + // a thread-unsafe race because individual ssl* objects are not thread-safe. + assert(socket_requests_in_flight_ == 0 || + socket_requests_are_from_thread_ == std::this_thread::get_id()); + } + + inline void ClientImpl::shutdown_socket(Socket &socket) { + if (socket.sock == INVALID_SOCKET) { return; } + detail::shutdown_socket(socket.sock); + } + + inline void ClientImpl::close_socket(Socket &socket) { + // If there are requests in flight in another thread, usually closing + // the socket will be fine and they will simply receive an error when + // using the closed socket, but it is still a bug since rarely the OS + // may reassign the socket id to be used for a new socket, and then + // suddenly they will be operating on a live socket that is different + // than the one they intended! + assert(socket_requests_in_flight_ == 0 || + socket_requests_are_from_thread_ == std::this_thread::get_id()); + + // It is also a bug if this happens while SSL is still active +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + assert(socket.ssl == nullptr); +#endif + if (socket.sock == INVALID_SOCKET) { return; } + detail::close_socket(socket.sock); + socket.sock = INVALID_SOCKET; + } + + inline bool ClientImpl::read_response_line(Stream &strm, const Request &req, + Response &res) { + std::array buf; + + detail::stream_line_reader line_reader(strm, buf.data(), buf.size()); + + if (!line_reader.getline()) { return false; } + + const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n"); std::cmatch m; - if (std::regex_match(reader.ptr(), m, re)) { + if (!std::regex_match(line_reader.ptr(), m, re)) { + return req.method == "CONNECT"; + } + res.version = std::string(m[1]); + res.status = std::stoi(std::string(m[2])); + res.reason = std::string(m[3]); + + // Ignore '100 Continue' + while (res.status == 100) { + if (!line_reader.getline()) { return false; } // CRLF + if (!line_reader.getline()) { return false; } // next response line + + if (!std::regex_match(line_reader.ptr(), m, re)) { return false; } res.version = std::string(m[1]); res.status = std::stoi(std::string(m[2])); + res.reason = std::string(m[3]); } return true; } - inline bool Client::send(Request &req, Response &res) { - if (req.path.empty()) { return false; } + inline bool ClientImpl::send(Request &req, Response &res, Error &error) { + std::lock_guard request_mutex_guard(request_mutex_); - auto sock = create_client_socket(); - if (sock == INVALID_SOCKET) { return false; } + { + std::lock_guard guard(socket_mutex_); + // Set this to false immediately - if it ever gets set to true by the end of + // the request, we know another thread instructed us to close the socket. + socket_should_be_closed_when_request_is_done_ = false; - return read_and_close_socket(sock, req, res); + auto is_alive = false; + if (socket_.is_open()) { + is_alive = detail::select_write(socket_.sock, 0, 0) > 0; + if (!is_alive) { + // Attempt to avoid sigpipe by shutting down nongracefully if it seems + // like the other side has already closed the connection Also, there + // cannot be any requests in flight from other threads since we locked + // request_mutex_, so safe to close everything immediately + const bool shutdown_gracefully = false; + shutdown_ssl(socket_, shutdown_gracefully); + shutdown_socket(socket_); + close_socket(socket_); + } + } + + if (!is_alive) { + if (!create_and_connect_socket(socket_, error)) { return false; } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + // TODO: refactoring + if (is_ssl()) { + auto &scli = static_cast(*this); + if (!proxy_host_.empty() && proxy_port_ != -1) { + bool success = false; + if (!scli.connect_with_proxy(socket_, res, success, error)) { + return success; + } + } + + if (!scli.initialize_ssl(socket_, error)) { return false; } + } +#endif + } + + // Mark the current socket as being in use so that it cannot be closed by + // anyone else while this request is ongoing, even though we will be + // releasing the mutex. + if (socket_requests_in_flight_ > 1) { + assert(socket_requests_are_from_thread_ == std::this_thread::get_id()); + } + socket_requests_in_flight_ += 1; + socket_requests_are_from_thread_ = std::this_thread::get_id(); + } + + for (const auto &header : default_headers_) { + if (req.headers.find(header.first) == req.headers.end()) { + req.headers.insert(header); + } + } + + auto close_connection = !keep_alive_; + auto ret = process_socket(socket_, [&](Stream &strm) { + return handle_request(strm, req, res, close_connection, error); + }); + + // Briefly lock mutex in order to mark that a request is no longer ongoing + { + std::lock_guard guard(socket_mutex_); + socket_requests_in_flight_ -= 1; + if (socket_requests_in_flight_ <= 0) { + assert(socket_requests_in_flight_ == 0); + socket_requests_are_from_thread_ = std::thread::id(); + } + + if (socket_should_be_closed_when_request_is_done_ || close_connection || + !ret) { + shutdown_ssl(socket_, true); + shutdown_socket(socket_); + close_socket(socket_); + } + } + + if (!ret) { + if (error == Error::Success) { error = Error::Unknown; } + } + + return ret; } - inline void Client::write_request(Stream &strm, Request &req) { - BufferStream bstrm; + inline Result ClientImpl::send(const Request &req) { + auto req2 = req; + return send_(std::move(req2)); + } - // Request line - auto path = detail::encode_url(req.path); + inline Result ClientImpl::send_(Request &&req) { + auto res = detail::make_unique(); + auto error = Error::Success; + auto ret = send(req, *res, error); + return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers)}; + } - bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str()); + inline bool ClientImpl::handle_request(Stream &strm, Request &req, + Response &res, bool close_connection, + Error &error) { + if (req.path.empty()) { + error = Error::Connection; + return false; + } + + auto req_save = req; + + bool ret; + + if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) { + auto req2 = req; + req2.path = "http://" + host_and_port_ + req.path; + ret = process_request(strm, req2, res, close_connection, error); + req = req2; + req.path = req_save.path; + } else { + ret = process_request(strm, req, res, close_connection, error); + } + + if (!ret) { return false; } + + if (300 < res.status && res.status < 400 && follow_location_) { + req = req_save; + ret = redirect(req, res, error); + } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + if ((res.status == 401 || res.status == 407) && + req.authorization_count_ < 5) { + auto is_proxy = res.status == 407; + const auto &username = + is_proxy ? proxy_digest_auth_username_ : digest_auth_username_; + const auto &password = + is_proxy ? proxy_digest_auth_password_ : digest_auth_password_; + + if (!username.empty() && !password.empty()) { + std::map auth; + if (detail::parse_www_authenticate(res, auth, is_proxy)) { + Request new_req = req; + new_req.authorization_count_ += 1; + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + new_req.headers.erase(key); + new_req.headers.insert(detail::make_digest_authentication_header( + req, auth, new_req.authorization_count_, detail::random_string(10), + username, password, is_proxy)); + + Response new_res; + + ret = send(new_req, new_res, error); + if (ret) { res = new_res; } + } + } + } +#endif + + return ret; + } + + inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) { + if (req.redirect_count_ == 0) { + error = Error::ExceedRedirectCount; + return false; + } + + auto location = detail::decode_url(res.get_header_value("location"), true); + if (location.empty()) { return false; } + + const static std::regex re( + R"(^(?:(https?):)?(?://([^:/?#]*)(?::(\d+))?)?([^?#]*(?:\?[^#]*)?)(?:#.*)?)"); + + std::smatch m; + if (!std::regex_match(location, m, re)) { return false; } + + auto scheme = is_ssl() ? "https" : "http"; + + auto next_scheme = m[1].str(); + auto next_host = m[2].str(); + auto port_str = m[3].str(); + auto next_path = m[4].str(); + + auto next_port = port_; + if (!port_str.empty()) { + next_port = std::stoi(port_str); + } else if (!next_scheme.empty()) { + next_port = next_scheme == "https" ? 443 : 80; + } + + if (next_scheme.empty()) { next_scheme = scheme; } + if (next_host.empty()) { next_host = host_; } + if (next_path.empty()) { next_path = "/"; } + + if (next_scheme == scheme && next_host == host_ && next_port == port_) { + return detail::redirect(*this, req, res, next_path, location, error); + } else { + if (next_scheme == "https") { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLClient cli(next_host.c_str(), next_port); + cli.copy_settings(*this); + return detail::redirect(cli, req, res, next_path, location, error); +#else + return false; +#endif + } else { + ClientImpl cli(next_host.c_str(), next_port); + cli.copy_settings(*this); + return detail::redirect(cli, req, res, next_path, location, error); + } + } + } + + inline bool ClientImpl::write_content_with_provider(Stream &strm, + const Request &req, + Error &error) { + auto is_shutting_down = []() { return false; }; + + if (req.is_chunked_content_provider_) { + // TODO: Brotli suport + std::unique_ptr compressor; +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + if (compress_) { + compressor = detail::make_unique(); + } else +#endif + { + compressor = detail::make_unique(); + } + + return detail::write_content_chunked(strm, req.content_provider_, + is_shutting_down, *compressor, error); + } else { + return detail::write_content(strm, req.content_provider_, 0, + req.content_length_, is_shutting_down, error); + } + } // namespace httplib + + inline bool ClientImpl::write_request(Stream &strm, Request &req, + bool close_connection, Error &error) { + // Prepare additional headers + if (close_connection) { req.headers.emplace("Connection", "close"); } - // Headers if (!req.has_header("Host")) { if (is_ssl()) { if (port_ == 443) { - req.set_header("Host", host_.c_str()); + req.headers.emplace("Host", host_); } else { - req.set_header("Host", host_and_port_.c_str()); + req.headers.emplace("Host", host_and_port_); } } else { if (port_ == 80) { - req.set_header("Host", host_.c_str()); + req.headers.emplace("Host", host_); } else { - req.set_header("Host", host_and_port_.c_str()); + req.headers.emplace("Host", host_and_port_); } } } - if (!req.has_header("Accept")) { req.set_header("Accept", "*/*"); } + if (!req.has_header("Accept")) { req.headers.emplace("Accept", "*/*"); } if (!req.has_header("User-Agent")) { - req.set_header("User-Agent", "cpp-httplib/0.2"); + req.headers.emplace("User-Agent", "cpp-httplib/0.7"); } - // TODO: Support KeepAlive connection - // if (!req.has_header("Connection")) { - req.set_header("Connection", "close"); - // } - if (req.body.empty()) { - if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH") { - req.set_header("Content-Length", "0"); + if (req.content_provider_) { + if (!req.is_chunked_content_provider_) { + auto length = std::to_string(req.content_length_); + req.headers.emplace("Content-Length", length); + } + } else { + if (req.method == "POST" || req.method == "PUT" || + req.method == "PATCH") { + req.headers.emplace("Content-Length", "0"); + } } } else { if (!req.has_header("Content-Type")) { - req.set_header("Content-Type", "text/plain"); + req.headers.emplace("Content-Type", "text/plain"); } if (!req.has_header("Content-Length")) { auto length = std::to_string(req.body.size()); - req.set_header("Content-Length", length.c_str()); + req.headers.emplace("Content-Length", length); } } - detail::write_headers(bstrm, req); - - // Body - if (!req.body.empty()) { bstrm.write(req.body.c_str(), req.body.size()); } - - // Flush buffer - auto &data = bstrm.get_buffer(); - strm.write(data.data(), data.size()); - } - - inline bool Client::process_request(Stream &strm, Request &req, Response &res, - bool &connection_close) { - // Send request - write_request(strm, req); - - // Receive response and headers - if (!read_response_line(strm, res) || - !detail::read_headers(strm, res.headers)) { - return false; + if (!basic_auth_password_.empty() || !basic_auth_username_.empty()) { + req.headers.insert(make_basic_authentication_header( + basic_auth_username_, basic_auth_password_, false)); } - if (res.get_header_value("Connection") == "close" || - res.version == "HTTP/1.0") { - connection_close = true; + if (!proxy_basic_auth_username_.empty() && + !proxy_basic_auth_password_.empty()) { + req.headers.insert(make_basic_authentication_header( + proxy_basic_auth_username_, proxy_basic_auth_password_, true)); + } + + if (!bearer_token_auth_token_.empty()) { + req.headers.insert(make_bearer_token_authentication_header( + bearer_token_auth_token_, false)); + } + + if (!proxy_bearer_token_auth_token_.empty()) { + req.headers.insert(make_bearer_token_authentication_header( + proxy_bearer_token_auth_token_, true)); + } + + // Request line and headers + { + detail::BufferStream bstrm; + + const auto &path = url_encode_ ? detail::encode_url(req.path) : req.path; + bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str()); + + detail::write_headers(bstrm, req.headers); + + // Flush buffer + auto &data = bstrm.get_buffer(); + if (!detail::write_data(strm, data.data(), data.size())) { + error = Error::Write; + return false; + } } // Body - if (req.method != "HEAD") { - bool exceed_payload_max_length = false; - if (!detail::read_content(strm, res, std::numeric_limits::max(), - exceed_payload_max_length, req.progress)) { - return false; - } - - if (res.get_header_value("Content-Encoding") == "gzip") { -#ifdef CPPHTTPLIB_ZLIB_SUPPORT - detail::decompress(res.body); -#else - return false; -#endif - } + if (req.body.empty()) { + return write_content_with_provider(strm, req, error); + } else { + return detail::write_data(strm, req.body.data(), req.body.size()); } return true; } - inline bool Client::read_and_close_socket(socket_t sock, Request &req, - Response &res) { - return detail::read_and_close_socket( - sock, 0, - [&](Stream &strm, bool /*last_connection*/, bool &connection_close) { - return process_request(strm, req, res, connection_close); - }); + inline std::unique_ptr ClientImpl::send_with_content_provider( + Request &req, + // const char *method, const char *path, const Headers &headers, + const char *body, size_t content_length, ContentProvider content_provider, + ContentProviderWithoutLength content_provider_without_length, + const char *content_type, Error &error) { + + // Request req; + // req.method = method; + // req.headers = headers; + // req.path = path; + + if (content_type) { req.headers.emplace("Content-Type", content_type); } + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + if (compress_) { req.headers.emplace("Content-Encoding", "gzip"); } +#endif + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + if (compress_ && !content_provider_without_length) { + // TODO: Brotli support + detail::gzip_compressor compressor; + + if (content_provider) { + auto ok = true; + size_t offset = 0; + DataSink data_sink; + + data_sink.write = [&](const char *data, size_t data_len) -> bool { + if (ok) { + auto last = offset + data_len == content_length; + + auto ret = compressor.compress( + data, data_len, last, [&](const char *data, size_t data_len) { + req.body.append(data, data_len); + return true; + }); + + if (ret) { + offset += data_len; + } else { + ok = false; + } + } + return ok; + }; + + data_sink.is_writable = [&](void) { return ok && true; }; + + while (ok && offset < content_length) { + if (!content_provider(offset, content_length - offset, data_sink)) { + error = Error::Canceled; + return nullptr; + } + } + } else { + if (!compressor.compress(body, content_length, true, + [&](const char *data, size_t data_len) { + req.body.append(data, data_len); + return true; + })) { + error = Error::Compression; + return nullptr; + } + } + } else +#endif + { + if (content_provider) { + req.content_length_ = content_length; + req.content_provider_ = std::move(content_provider); + req.is_chunked_content_provider_ = false; + } else if (content_provider_without_length) { + req.content_length_ = 0; + req.content_provider_ = detail::ContentProviderAdapter( + std::move(content_provider_without_length)); + req.is_chunked_content_provider_ = true; + req.headers.emplace("Transfer-Encoding", "chunked"); + } else { + req.body.assign(body, content_length); + ; + } + } + + auto res = detail::make_unique(); + return send(req, *res, error) ? std::move(res) : nullptr; } - inline bool Client::is_ssl() const { return false; } + inline Result ClientImpl::send_with_content_provider( + const char *method, const char *path, const Headers &headers, + const char *body, size_t content_length, ContentProvider content_provider, + ContentProviderWithoutLength content_provider_without_length, + const char *content_type) { + Request req; + req.method = method; + req.headers = headers; + req.path = path; - inline std::shared_ptr Client::Get(const char *path, - Progress progress) { - return Get(path, Headers(), progress); + auto error = Error::Success; + + auto res = send_with_content_provider( + req, + // method, path, headers, + body, content_length, std::move(content_provider), + std::move(content_provider_without_length), content_type, error); + + return Result{std::move(res), error, std::move(req.headers)}; } - inline std::shared_ptr - Client::Get(const char *path, const Headers &headers, Progress progress) { + inline bool ClientImpl::process_request(Stream &strm, Request &req, + Response &res, bool close_connection, + Error &error) { + // Send request + if (!write_request(strm, req, close_connection, error)) { return false; } + + // Receive response and headers + if (!read_response_line(strm, req, res) || + !detail::read_headers(strm, res.headers)) { + error = Error::Read; + return false; + } + + // Body + if ((res.status != 204) && req.method != "HEAD" && req.method != "CONNECT") { + auto redirect = 300 < res.status && res.status < 400 && follow_location_; + + if (req.response_handler && !redirect) { + if (!req.response_handler(res)) { + error = Error::Canceled; + return false; + } + } + + auto out = + req.content_receiver + ? static_cast( + [&](const char *buf, size_t n, uint64_t off, uint64_t len) { + if (redirect) { return true; } + auto ret = req.content_receiver(buf, n, off, len); + if (!ret) { error = Error::Canceled; } + return ret; + }) + : static_cast( + [&](const char *buf, size_t n, uint64_t /*off*/, + uint64_t /*len*/) { + if (res.body.size() + n > res.body.max_size()) { + return false; + } + res.body.append(buf, n); + return true; + }); + + auto progress = [&](uint64_t current, uint64_t total) { + if (!req.progress || redirect) { return true; } + auto ret = req.progress(current, total); + if (!ret) { error = Error::Canceled; } + return ret; + }; + + int dummy_status; + if (!detail::read_content(strm, res, (std::numeric_limits::max)(), + dummy_status, std::move(progress), std::move(out), + decompress_)) { + if (error != Error::Canceled) { error = Error::Read; } + return false; + } + } + + if (res.get_header_value("Connection") == "close" || + (res.version == "HTTP/1.0" && res.reason != "Connection established")) { + // TODO this requires a not-entirely-obvious chain of calls to be correct + // for this to be safe. Maybe a code refactor (such as moving this out to + // the send function and getting rid of the recursiveness of the mutex) + // could make this more obvious. + + // This is safe to call because process_request is only called by + // handle_request which is only called by send, which locks the request + // mutex during the process. It would be a bug to call it from a different + // thread since it's a thread-safety issue to do these things to the socket + // if another thread is using the socket. + std::lock_guard guard(socket_mutex_); + shutdown_ssl(socket_, true); + shutdown_socket(socket_); + close_socket(socket_); + } + + // Log + if (logger_) { logger_(req, res); } + + return true; + } + + inline bool + ClientImpl::process_socket(const Socket &socket, + std::function callback) { + return detail::process_client_socket( + socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, + write_timeout_usec_, std::move(callback)); + } + + inline bool ClientImpl::is_ssl() const { return false; } + + inline Result ClientImpl::Get(const char *path) { + return Get(path, Headers(), Progress()); + } + + inline Result ClientImpl::Get(const char *path, Progress progress) { + return Get(path, Headers(), std::move(progress)); + } + + inline Result ClientImpl::Get(const char *path, const Headers &headers) { + return Get(path, headers, Progress()); + } + + inline Result ClientImpl::Get(const char *path, const Headers &headers, + Progress progress) { Request req; req.method = "GET"; req.path = path; req.headers = headers; - req.progress = progress; + req.progress = std::move(progress); - auto res = std::make_shared(); - - return send(req, *res) ? res : nullptr; + return send_(std::move(req)); } - inline std::shared_ptr Client::Head(const char *path) { + inline Result ClientImpl::Get(const char *path, + ContentReceiver content_receiver) { + return Get(path, Headers(), nullptr, std::move(content_receiver), nullptr); + } + + inline Result ClientImpl::Get(const char *path, + ContentReceiver content_receiver, + Progress progress) { + return Get(path, Headers(), nullptr, std::move(content_receiver), + std::move(progress)); + } + + inline Result ClientImpl::Get(const char *path, const Headers &headers, + ContentReceiver content_receiver) { + return Get(path, headers, nullptr, std::move(content_receiver), nullptr); + } + + inline Result ClientImpl::Get(const char *path, const Headers &headers, + ContentReceiver content_receiver, + Progress progress) { + return Get(path, headers, nullptr, std::move(content_receiver), + std::move(progress)); + } + + inline Result ClientImpl::Get(const char *path, + ResponseHandler response_handler, + ContentReceiver content_receiver) { + return Get(path, Headers(), std::move(response_handler), + std::move(content_receiver), nullptr); + } + + inline Result ClientImpl::Get(const char *path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver) { + return Get(path, headers, std::move(response_handler), + std::move(content_receiver), nullptr); + } + + inline Result ClientImpl::Get(const char *path, + ResponseHandler response_handler, + ContentReceiver content_receiver, + Progress progress) { + return Get(path, Headers(), std::move(response_handler), + std::move(content_receiver), std::move(progress)); + } + + inline Result ClientImpl::Get(const char *path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, + Progress progress) { + Request req; + req.method = "GET"; + req.path = path; + req.headers = headers; + req.response_handler = std::move(response_handler); + req.content_receiver = + [content_receiver](const char *data, size_t data_length, + uint64_t /*offset*/, uint64_t /*total_length*/) { + return content_receiver(data, data_length); + }; + req.progress = std::move(progress); + + return send_(std::move(req)); + } + + inline Result ClientImpl::Get(const char *path, const Params ¶ms, + const Headers &headers, Progress progress) { + if (params.empty()) { return Get(path, headers); } + + std::string path_with_query = detail::append_query_params(path, params); + return Get(path_with_query.c_str(), headers, progress); + } + + inline Result ClientImpl::Get(const char *path, const Params ¶ms, + const Headers &headers, + ContentReceiver content_receiver, + Progress progress) { + return Get(path, params, headers, nullptr, content_receiver, progress); + } + + inline Result ClientImpl::Get(const char *path, const Params ¶ms, + const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, + Progress progress) { + if (params.empty()) { + return Get(path, headers, response_handler, content_receiver, progress); + } + + std::string path_with_query = detail::append_query_params(path, params); + return Get(path_with_query.c_str(), headers, response_handler, + content_receiver, progress); + } + + inline Result ClientImpl::Head(const char *path) { return Head(path, Headers()); } - inline std::shared_ptr Client::Head(const char *path, - const Headers &headers) { + inline Result ClientImpl::Head(const char *path, const Headers &headers) { Request req; req.method = "HEAD"; req.headers = headers; req.path = path; - auto res = std::make_shared(); - - return send(req, *res) ? res : nullptr; + return send_(std::move(req)); } - inline std::shared_ptr Client::Post(const char *path, - const std::string &body, - const char *content_type) { + inline Result ClientImpl::Post(const char *path) { + return Post(path, std::string(), nullptr); + } + + inline Result ClientImpl::Post(const char *path, const char *body, + size_t content_length, + const char *content_type) { + return Post(path, Headers(), body, content_length, content_type); + } + + inline Result ClientImpl::Post(const char *path, const Headers &headers, + const char *body, size_t content_length, + const char *content_type) { + return send_with_content_provider("POST", path, headers, body, content_length, + nullptr, nullptr, content_type); + } + + inline Result ClientImpl::Post(const char *path, const std::string &body, + const char *content_type) { return Post(path, Headers(), body, content_type); } - inline std::shared_ptr Client::Post(const char *path, - const Headers &headers, - const std::string &body, - const char *content_type) { - Request req; - req.method = "POST"; - req.headers = headers; - req.path = path; - - req.headers.emplace("Content-Type", content_type); - req.body = body; - - auto res = std::make_shared(); - - return send(req, *res) ? res : nullptr; + inline Result ClientImpl::Post(const char *path, const Headers &headers, + const std::string &body, + const char *content_type) { + return send_with_content_provider("POST", path, headers, body.data(), + body.size(), nullptr, nullptr, + content_type); } - inline std::shared_ptr Client::Post(const char *path, - const Params ¶ms) { + inline Result ClientImpl::Post(const char *path, const Params ¶ms) { return Post(path, Headers(), params); } - inline std::shared_ptr - Client::Post(const char *path, const Headers &headers, const Params ¶ms) { - std::string query; - for (auto it = params.begin(); it != params.end(); ++it) { - if (it != params.begin()) { query += "&"; } - query += it->first; - query += "="; - query += detail::encode_url(it->second); - } + inline Result ClientImpl::Post(const char *path, size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return Post(path, Headers(), content_length, std::move(content_provider), + content_type); + } + inline Result ClientImpl::Post(const char *path, + ContentProviderWithoutLength content_provider, + const char *content_type) { + return Post(path, Headers(), std::move(content_provider), content_type); + } + + inline Result ClientImpl::Post(const char *path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return send_with_content_provider("POST", path, headers, nullptr, + content_length, std::move(content_provider), + nullptr, content_type); + } + + inline Result ClientImpl::Post(const char *path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const char *content_type) { + return send_with_content_provider("POST", path, headers, nullptr, 0, nullptr, + std::move(content_provider), content_type); + } + + inline Result ClientImpl::Post(const char *path, const Headers &headers, + const Params ¶ms) { + auto query = detail::params_to_query_str(params); return Post(path, headers, query, "application/x-www-form-urlencoded"); } - inline std::shared_ptr Client::Put(const char *path, - const std::string &body, - const char *content_type) { + inline Result ClientImpl::Post(const char *path, + const MultipartFormDataItems &items) { + return Post(path, Headers(), items); + } + + inline Result ClientImpl::Post(const char *path, const Headers &headers, + const MultipartFormDataItems &items) { + return Post(path, headers, items, detail::make_multipart_data_boundary()); + } + inline Result ClientImpl::Post(const char *path, const Headers &headers, + const MultipartFormDataItems &items, + const std::string &boundary) { + for (size_t i = 0; i < boundary.size(); i++) { + char c = boundary[i]; + if (!std::isalnum(c) && c != '-' && c != '_') { + return Result{nullptr, Error::UnsupportedMultipartBoundaryChars}; + } + } + + std::string body; + + for (const auto &item : items) { + body += "--" + boundary + "\r\n"; + body += "Content-Disposition: form-data; name=\"" + item.name + "\""; + if (!item.filename.empty()) { + body += "; filename=\"" + item.filename + "\""; + } + body += "\r\n"; + if (!item.content_type.empty()) { + body += "Content-Type: " + item.content_type + "\r\n"; + } + body += "\r\n"; + body += item.content + "\r\n"; + } + + body += "--" + boundary + "--\r\n"; + + std::string content_type = "multipart/form-data; boundary=" + boundary; + return Post(path, headers, body, content_type.c_str()); + } + + inline Result ClientImpl::Put(const char *path) { + return Put(path, std::string(), nullptr); + } + + inline Result ClientImpl::Put(const char *path, const char *body, + size_t content_length, const char *content_type) { + return Put(path, Headers(), body, content_length, content_type); + } + + inline Result ClientImpl::Put(const char *path, const Headers &headers, + const char *body, size_t content_length, + const char *content_type) { + return send_with_content_provider("PUT", path, headers, body, content_length, + nullptr, nullptr, content_type); + } + + inline Result ClientImpl::Put(const char *path, const std::string &body, + const char *content_type) { return Put(path, Headers(), body, content_type); } - inline std::shared_ptr Client::Put(const char *path, - const Headers &headers, - const std::string &body, - const char *content_type) { - Request req; - req.method = "PUT"; - req.headers = headers; - req.path = path; - - req.headers.emplace("Content-Type", content_type); - req.body = body; - - auto res = std::make_shared(); - - return send(req, *res) ? res : nullptr; + inline Result ClientImpl::Put(const char *path, const Headers &headers, + const std::string &body, + const char *content_type) { + return send_with_content_provider("PUT", path, headers, body.data(), + body.size(), nullptr, nullptr, + content_type); } - inline std::shared_ptr Client::Patch(const char *path, - const std::string &body, - const char *content_type) { + inline Result ClientImpl::Put(const char *path, size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return Put(path, Headers(), content_length, std::move(content_provider), + content_type); + } + + inline Result ClientImpl::Put(const char *path, + ContentProviderWithoutLength content_provider, + const char *content_type) { + return Put(path, Headers(), std::move(content_provider), content_type); + } + + inline Result ClientImpl::Put(const char *path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return send_with_content_provider("PUT", path, headers, nullptr, + content_length, std::move(content_provider), + nullptr, content_type); + } + + inline Result ClientImpl::Put(const char *path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const char *content_type) { + return send_with_content_provider("PUT", path, headers, nullptr, 0, nullptr, + std::move(content_provider), content_type); + } + + inline Result ClientImpl::Put(const char *path, const Params ¶ms) { + return Put(path, Headers(), params); + } + + inline Result ClientImpl::Put(const char *path, const Headers &headers, + const Params ¶ms) { + auto query = detail::params_to_query_str(params); + return Put(path, headers, query, "application/x-www-form-urlencoded"); + } + + inline Result ClientImpl::Patch(const char *path) { + return Patch(path, std::string(), nullptr); + } + + inline Result ClientImpl::Patch(const char *path, const char *body, + size_t content_length, + const char *content_type) { + return Patch(path, Headers(), body, content_length, content_type); + } + + inline Result ClientImpl::Patch(const char *path, const Headers &headers, + const char *body, size_t content_length, + const char *content_type) { + return send_with_content_provider("PATCH", path, headers, body, + content_length, nullptr, nullptr, + content_type); + } + + inline Result ClientImpl::Patch(const char *path, const std::string &body, + const char *content_type) { return Patch(path, Headers(), body, content_type); } - inline std::shared_ptr Client::Patch(const char *path, - const Headers &headers, - const std::string &body, - const char *content_type) { - Request req; - req.method = "PATCH"; - req.headers = headers; - req.path = path; - - req.headers.emplace("Content-Type", content_type); - req.body = body; - - auto res = std::make_shared(); - - return send(req, *res) ? res : nullptr; + inline Result ClientImpl::Patch(const char *path, const Headers &headers, + const std::string &body, + const char *content_type) { + return send_with_content_provider("PATCH", path, headers, body.data(), + body.size(), nullptr, nullptr, + content_type); } - inline std::shared_ptr Client::Delete(const char *path, - const std::string &body, - const char *content_type) { - return Delete(path, Headers(), body, content_type); + inline Result ClientImpl::Patch(const char *path, size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return Patch(path, Headers(), content_length, std::move(content_provider), + content_type); } - inline std::shared_ptr Client::Delete(const char *path, - const Headers &headers, - const std::string &body, - const char *content_type) { + inline Result ClientImpl::Patch(const char *path, + ContentProviderWithoutLength content_provider, + const char *content_type) { + return Patch(path, Headers(), std::move(content_provider), content_type); + } + + inline Result ClientImpl::Patch(const char *path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return send_with_content_provider("PATCH", path, headers, nullptr, + content_length, std::move(content_provider), + nullptr, content_type); + } + + inline Result ClientImpl::Patch(const char *path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const char *content_type) { + return send_with_content_provider("PATCH", path, headers, nullptr, 0, nullptr, + std::move(content_provider), content_type); + } + + inline Result ClientImpl::Delete(const char *path) { + return Delete(path, Headers(), std::string(), nullptr); + } + + inline Result ClientImpl::Delete(const char *path, const Headers &headers) { + return Delete(path, headers, std::string(), nullptr); + } + + inline Result ClientImpl::Delete(const char *path, const char *body, + size_t content_length, + const char *content_type) { + return Delete(path, Headers(), body, content_length, content_type); + } + + inline Result ClientImpl::Delete(const char *path, const Headers &headers, + const char *body, size_t content_length, + const char *content_type) { Request req; req.method = "DELETE"; req.headers = headers; req.path = path; if (content_type) { req.headers.emplace("Content-Type", content_type); } - req.body = body; + req.body.assign(body, content_length); - auto res = std::make_shared(); - - return send(req, *res) ? res : nullptr; + return send_(std::move(req)); } - inline std::shared_ptr Client::Options(const char *path) { + inline Result ClientImpl::Delete(const char *path, const std::string &body, + const char *content_type) { + return Delete(path, Headers(), body.data(), body.size(), content_type); + } + + inline Result ClientImpl::Delete(const char *path, const Headers &headers, + const std::string &body, + const char *content_type) { + return Delete(path, headers, body.data(), body.size(), content_type); + } + + inline Result ClientImpl::Options(const char *path) { return Options(path, Headers()); } - inline std::shared_ptr Client::Options(const char *path, - const Headers &headers) { + inline Result ClientImpl::Options(const char *path, const Headers &headers) { Request req; req.method = "OPTIONS"; - req.path = path; req.headers = headers; + req.path = path; - auto res = std::make_shared(); + return send_(std::move(req)); + } - return send(req, *res) ? res : nullptr; + inline size_t ClientImpl::is_socket_open() const { + std::lock_guard guard(socket_mutex_); + return socket_.is_open(); + } + + inline void ClientImpl::stop() { + std::lock_guard guard(socket_mutex_); + + // If there is anything ongoing right now, the ONLY thread-safe thing we can + // do is to shutdown_socket, so that threads using this socket suddenly + // discover they can't read/write any more and error out. Everything else + // (closing the socket, shutting ssl down) is unsafe because these actions are + // not thread-safe. + if (socket_requests_in_flight_ > 0) { + shutdown_socket(socket_); + + // Aside from that, we set a flag for the socket to be closed when we're + // done. + socket_should_be_closed_when_request_is_done_ = true; + return; + } + + // Otherwise, sitll holding the mutex, we can shut everything down ourselves + shutdown_ssl(socket_, true); + shutdown_socket(socket_); + close_socket(socket_); + } + + inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) { + connection_timeout_sec_ = sec; + connection_timeout_usec_ = usec; + } + + template + inline void ClientImpl::set_connection_timeout( + const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) { + set_connection_timeout(sec, usec); + }); + } + + inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) { + read_timeout_sec_ = sec; + read_timeout_usec_ = usec; + } + + template + inline void ClientImpl::set_read_timeout( + const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec( + duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); }); + } + + inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) { + write_timeout_sec_ = sec; + write_timeout_usec_ = usec; + } + + template + inline void ClientImpl::set_write_timeout( + const std::chrono::duration &duration) { + detail::duration_to_sec_and_usec( + duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); }); + } + + inline void ClientImpl::set_basic_auth(const char *username, + const char *password) { + basic_auth_username_ = username; + basic_auth_password_ = password; + } + + inline void ClientImpl::set_bearer_token_auth(const char *token) { + bearer_token_auth_token_ = token; + } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + inline void ClientImpl::set_digest_auth(const char *username, + const char *password) { + digest_auth_username_ = username; + digest_auth_password_ = password; +} +#endif + + inline void ClientImpl::set_keep_alive(bool on) { keep_alive_ = on; } + + inline void ClientImpl::set_follow_location(bool on) { follow_location_ = on; } + + inline void ClientImpl::set_url_encode(bool on) { url_encode_ = on; } + + inline void ClientImpl::set_default_headers(Headers headers) { + default_headers_ = std::move(headers); + } + + inline void ClientImpl::set_address_family(int family) { + address_family_ = family; + } + + inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; } + + inline void ClientImpl::set_socket_options(SocketOptions socket_options) { + socket_options_ = std::move(socket_options); + } + + inline void ClientImpl::set_compress(bool on) { compress_ = on; } + + inline void ClientImpl::set_decompress(bool on) { decompress_ = on; } + + inline void ClientImpl::set_interface(const char *intf) { interface_ = intf; } + + inline void ClientImpl::set_proxy(const char *host, int port) { + proxy_host_ = host; + proxy_port_ = port; + } + + inline void ClientImpl::set_proxy_basic_auth(const char *username, + const char *password) { + proxy_basic_auth_username_ = username; + proxy_basic_auth_password_ = password; + } + + inline void ClientImpl::set_proxy_bearer_token_auth(const char *token) { + proxy_bearer_token_auth_token_ = token; + } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + inline void ClientImpl::set_proxy_digest_auth(const char *username, + const char *password) { + proxy_digest_auth_username_ = username; + proxy_digest_auth_password_ = password; +} +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + inline void ClientImpl::enable_server_certificate_verification(bool enabled) { + server_certificate_verification_ = enabled; +} +#endif + + inline void ClientImpl::set_logger(Logger logger) { + logger_ = std::move(logger); } /* @@ -2309,119 +6514,222 @@ static WSInit wsinit_; #ifdef CPPHTTPLIB_OPENSSL_SUPPORT namespace detail { -template -inline bool -read_and_close_socket_ssl(socket_t sock, size_t keep_alive_max_count, - // TODO: OpenSSL 1.0.2 occasionally crashes... - // The upcoming 1.1.0 is going to be thread safe. - SSL_CTX *ctx, std::mutex &ctx_mutex, - U SSL_connect_or_accept, V setup, T callback) { +template +inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex, + U SSL_connect_or_accept, V setup) { SSL *ssl = nullptr; { std::lock_guard guard(ctx_mutex); ssl = SSL_new(ctx); } - if (!ssl) { - close_socket(sock); - return false; - } + if (ssl) { + set_nonblocking(sock, true); + auto bio = BIO_new_socket(static_cast(sock), BIO_NOCLOSE); + BIO_set_nbio(bio, 1); + SSL_set_bio(ssl, bio, bio); - auto bio = BIO_new_socket(sock, BIO_NOCLOSE); - SSL_set_bio(ssl, bio, bio); - - if (!setup(ssl)) { - SSL_shutdown(ssl); - { - std::lock_guard guard(ctx_mutex); - SSL_free(ssl); - } - - close_socket(sock); - return false; - } - - bool ret = false; - - if (SSL_connect_or_accept(ssl) == 1) { - if (keep_alive_max_count > 0) { - auto count = keep_alive_max_count; - while (count > 0 && - detail::select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, - CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0) { - SSLSocketStream strm(sock, ssl); - auto last_connection = count == 1; - auto connection_close = false; - - ret = callback(ssl, strm, last_connection, connection_close); - if (!ret || connection_close) { break; } - - count--; + if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) { + SSL_shutdown(ssl); + { + std::lock_guard guard(ctx_mutex); + SSL_free(ssl); } + set_nonblocking(sock, false); + return nullptr; + } + BIO_set_nbio(bio, 0); + set_nonblocking(sock, false); + } + + return ssl; +} + +inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, + bool shutdown_gracefully) { + // sometimes we may want to skip this to try to avoid SIGPIPE if we know + // the remote has closed the network connection + // Note that it is not always possible to avoid SIGPIPE, this is merely a + // best-efforts. + if (shutdown_gracefully) { SSL_shutdown(ssl); } + + std::lock_guard guard(ctx_mutex); + SSL_free(ssl); +} + +template +bool ssl_connect_or_accept_nonblocking(socket_t sock, SSL *ssl, + U ssl_connect_or_accept, + time_t timeout_sec, + time_t timeout_usec) { + int res = 0; + while ((res = ssl_connect_or_accept(ssl)) != 1) { + auto err = SSL_get_error(ssl, res); + switch (err) { + case SSL_ERROR_WANT_READ: + if (select_read(sock, timeout_sec, timeout_usec) > 0) { continue; } + break; + case SSL_ERROR_WANT_WRITE: + if (select_write(sock, timeout_sec, timeout_usec) > 0) { continue; } + break; + default: break; + } + return false; + } + return true; +} + +template +inline bool +process_server_socket_ssl(SSL *ssl, socket_t sock, size_t keep_alive_max_count, + time_t keep_alive_timeout_sec, + time_t read_timeout_sec, time_t read_timeout_usec, + time_t write_timeout_sec, time_t write_timeout_usec, + T callback) { + return process_server_socket_core( + sock, keep_alive_max_count, keep_alive_timeout_sec, + [&](bool close_connection, bool &connection_closed) { + SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec, + write_timeout_sec, write_timeout_usec); + return callback(strm, close_connection, connection_closed); + }); +} + +template +inline bool +process_client_socket_ssl(SSL *ssl, socket_t sock, time_t read_timeout_sec, + time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec, T callback) { + SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec, + write_timeout_sec, write_timeout_usec); + return callback(strm); +} + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +static std::shared_ptr> openSSL_locks_; + +class SSLThreadLocks { +public: + SSLThreadLocks() { + openSSL_locks_ = + std::make_shared>(CRYPTO_num_locks()); + CRYPTO_set_locking_callback(locking_callback); + } + + ~SSLThreadLocks() { CRYPTO_set_locking_callback(nullptr); } + +private: + static void locking_callback(int mode, int type, const char * /*file*/, + int /*line*/) { + auto &lk = (*openSSL_locks_)[static_cast(type)]; + if (mode & CRYPTO_LOCK) { + lk.lock(); } else { - SSLSocketStream strm(sock, ssl); - auto dummy_connection_close = false; - ret = callback(ssl, strm, true, dummy_connection_close); + lk.unlock(); } } +}; - SSL_shutdown(ssl); - { - std::lock_guard guard(ctx_mutex); - SSL_free(ssl); - } - - close_socket(sock); - - return ret; -} +#endif class SSLInit { public: SSLInit() { +#if OPENSSL_VERSION_NUMBER < 0x1010001fL SSL_load_error_strings(); SSL_library_init(); +#else + OPENSSL_init_ssl( + OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); +#endif } - ~SSLInit() { ERR_free_strings(); } + ~SSLInit() { +#if OPENSSL_VERSION_NUMBER < 0x1010001fL + ERR_free_strings(); +#endif + } + +private: +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SSLThreadLocks thread_init_; +#endif }; +// SSL socket stream implementation +inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl, + time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec) + : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec), + read_timeout_usec_(read_timeout_usec), + write_timeout_sec_(write_timeout_sec), + write_timeout_usec_(write_timeout_usec) { + SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY); +} + +inline SSLSocketStream::~SSLSocketStream() {} + +inline bool SSLSocketStream::is_readable() const { + return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; +} + +inline bool SSLSocketStream::is_writable() const { + return detail::select_write(sock_, write_timeout_sec_, write_timeout_usec_) > + 0; +} + +inline ssize_t SSLSocketStream::read(char *ptr, size_t size) { + if (SSL_pending(ssl_) > 0) { + return SSL_read(ssl_, ptr, static_cast(size)); + } else if (is_readable()) { + auto ret = SSL_read(ssl_, ptr, static_cast(size)); + if (ret < 0) { + auto err = SSL_get_error(ssl_, ret); +#ifdef _WIN32 + while (err == SSL_ERROR_WANT_READ || + err == SSL_ERROR_SYSCALL && WSAGetLastError() == WSAETIMEDOUT) { +#else + while (err == SSL_ERROR_WANT_READ) { +#endif + if (SSL_pending(ssl_) > 0) { + return SSL_read(ssl_, ptr, static_cast(size)); + } else if (is_readable()) { + ret = SSL_read(ssl_, ptr, static_cast(size)); + if (ret >= 0) { return ret; } + err = SSL_get_error(ssl_, ret); + } else { + return -1; + } + } + } + return ret; + } + return -1; +} + +inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) { + if (is_writable()) { return SSL_write(ssl_, ptr, static_cast(size)); } + return -1; +} + +inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip, + int &port) const { + detail::get_remote_ip_and_port(sock_, ip, port); +} + +inline socket_t SSLSocketStream::socket() const { return sock_; } + static SSLInit sslinit_; } // namespace detail -// SSL socket stream implementation -inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl) - : sock_(sock), ssl_(ssl) {} - -inline SSLSocketStream::~SSLSocketStream() {} - -inline int SSLSocketStream::read(char *ptr, size_t size) { - if (SSL_pending(ssl_) > 0 || - detail::select_read(sock_, CPPHTTPLIB_READ_TIMEOUT_SECOND, - CPPHTTPLIB_READ_TIMEOUT_USECOND) > 0) { - return SSL_read(ssl_, ptr, size); - } - return -1; -} - -inline int SSLSocketStream::write(const char *ptr, size_t size) { - return SSL_write(ssl_, ptr, size); -} - -inline int SSLSocketStream::write(const char *ptr) { - return write(ptr, strlen(ptr)); -} - -inline std::string SSLSocketStream::get_remote_addr() const { - return detail::get_remote_addr(sock_); -} - // SSL HTTP server implementation inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path, const char *client_ca_cert_file_path, const char *client_ca_cert_dir_path) { - ctx_ = SSL_CTX_new(SSLv23_server_method()); + ctx_ = SSL_CTX_new(TLS_method()); if (ctx_) { SSL_CTX_set_options(ctx_, @@ -2456,39 +6764,111 @@ inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path, } } +inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key, + X509_STORE *client_ca_cert_store) { + ctx_ = SSL_CTX_new(SSLv23_server_method()); + + if (ctx_) { + SSL_CTX_set_options(ctx_, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + + if (SSL_CTX_use_certificate(ctx_, cert) != 1 || + SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } else if (client_ca_cert_store) { + + SSL_CTX_set_cert_store(ctx_, client_ca_cert_store); + + SSL_CTX_set_verify( + ctx_, + SSL_VERIFY_PEER | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, // SSL_VERIFY_CLIENT_ONCE, + nullptr); + } + } +} + inline SSLServer::~SSLServer() { if (ctx_) { SSL_CTX_free(ctx_); } } inline bool SSLServer::is_valid() const { return ctx_; } -inline bool SSLServer::read_and_close_socket(socket_t sock) { - return detail::read_and_close_socket_ssl( - sock, keep_alive_max_count_, ctx_, ctx_mutex_, SSL_accept, - [](SSL * /*ssl*/) { return true; }, - [this](SSL *ssl, Stream &strm, bool last_connection, - bool &connection_close) { - return process_request(strm, last_connection, connection_close, - [&](Request &req) { req.ssl = ssl; }); - }); +inline bool SSLServer::process_and_close_socket(socket_t sock) { + auto ssl = detail::ssl_new( + sock, ctx_, ctx_mutex_, + [&](SSL *ssl) { + return detail::ssl_connect_or_accept_nonblocking( + sock, ssl, SSL_accept, read_timeout_sec_, read_timeout_usec_); + }, + [](SSL * /*ssl*/) { return true; }); + + bool ret = false; + if (ssl) { + ret = detail::process_server_socket_ssl( + ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_, + read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, + write_timeout_usec_, + [this, ssl](Stream &strm, bool close_connection, + bool &connection_closed) { + return process_request(strm, close_connection, connection_closed, + [&](Request &req) { req.ssl = ssl; }); + }); + + // Shutdown gracefully if the result seemed successful, non-gracefully if + // the connection appeared to be closed. + const bool shutdown_gracefully = ret; + detail::ssl_delete(ctx_mutex_, ssl, shutdown_gracefully); + } + + detail::shutdown_socket(sock); + detail::close_socket(sock); + return ret; } // SSL HTTP client implementation -inline SSLClient::SSLClient(const char *host, int port, time_t timeout_sec, - const char *client_cert_path, - const char *client_key_path) - : Client(host, port, timeout_sec) { +inline SSLClient::SSLClient(const std::string &host) + : SSLClient(host, 443, std::string(), std::string()) {} + +inline SSLClient::SSLClient(const std::string &host, int port) + : SSLClient(host, port, std::string(), std::string()) {} + +inline SSLClient::SSLClient(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path) + : ClientImpl(host, port, client_cert_path, client_key_path) { ctx_ = SSL_CTX_new(SSLv23_client_method()); detail::split(&host_[0], &host_[host_.size()], '.', [&](const char *b, const char *e) { host_components_.emplace_back(std::string(b, e)); }); - if (client_cert_path && client_key_path) { - if (SSL_CTX_use_certificate_file(ctx_, client_cert_path, + if (!client_cert_path.empty() && !client_key_path.empty()) { + if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(), SSL_FILETYPE_PEM) != 1 || - SSL_CTX_use_PrivateKey_file(ctx_, client_key_path, SSL_FILETYPE_PEM) != - 1) { + SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(), + SSL_FILETYPE_PEM) != 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } + } +} + +inline SSLClient::SSLClient(const std::string &host, int port, + X509 *client_cert, EVP_PKEY *client_key) + : ClientImpl(host, port) { + ctx_ = SSL_CTX_new(SSLv23_client_method()); + + detail::split(&host_[0], &host_[host_.size()], '.', + [&](const char *b, const char *e) { + host_components_.emplace_back(std::string(b, e)); + }); + if (client_cert != nullptr && client_key != nullptr) { + if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 || + SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) { SSL_CTX_free(ctx_); ctx_ = nullptr; } @@ -2497,6 +6877,10 @@ inline SSLClient::SSLClient(const char *host, int port, time_t timeout_sec, inline SSLClient::~SSLClient() { if (ctx_) { SSL_CTX_free(ctx_); } + // Make sure to shut down SSL since shutdown_ssl will resolve to the + // base function rather than the derived function once we get to the + // base class destructor, and won't free the SSL (causing a leak). + shutdown_ssl_impl(socket_, true); } inline bool SSLClient::is_valid() const { return ctx_; } @@ -2507,59 +6891,197 @@ inline void SSLClient::set_ca_cert_path(const char *ca_cert_file_path, if (ca_cert_dir_path) { ca_cert_dir_path_ = ca_cert_dir_path; } } -inline void SSLClient::enable_server_certificate_verification(bool enabled) { - server_certificate_verification_ = enabled; +inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) { + if (ca_cert_store) { + if (ctx_) { + if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) { + // Free memory allocated for old cert and use new store `ca_cert_store` + SSL_CTX_set_cert_store(ctx_, ca_cert_store); + } + } else { + X509_STORE_free(ca_cert_store); + } + } } inline long SSLClient::get_openssl_verify_result() const { return verify_result_; } -inline bool SSLClient::read_and_close_socket(socket_t sock, Request &req, - Response &res) { +inline SSL_CTX *SSLClient::ssl_context() const { return ctx_; } - return is_valid() && - detail::read_and_close_socket_ssl( - sock, 0, ctx_, ctx_mutex_, - [&](SSL *ssl) { - if (ca_cert_file_path_.empty()) { - SSL_CTX_set_verify(ctx_, SSL_VERIFY_NONE, nullptr); - } else { - if (!SSL_CTX_load_verify_locations( - ctx_, ca_cert_file_path_.c_str(), nullptr)) { - return false; - } - SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER, nullptr); - } +inline bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) { + return is_valid() && ClientImpl::create_and_connect_socket(socket, error); +} - if (SSL_connect(ssl) != 1) { return false; } +// Assumes that socket_mutex_ is locked and that there are no requests in flight +inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res, + bool &success, Error &error) { + success = true; + Response res2; + if (!detail::process_client_socket( + socket.sock, read_timeout_sec_, read_timeout_usec_, + write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) { + Request req2; + req2.method = "CONNECT"; + req2.path = host_and_port_; + return process_request(strm, req2, res2, false, error); + })) { + // Thread-safe to close everything because we are assuming there are no + // requests in flight + shutdown_ssl(socket, true); + shutdown_socket(socket); + close_socket(socket); + success = false; + return false; + } - if (server_certificate_verification_) { - verify_result_ = SSL_get_verify_result(ssl); + if (res2.status == 407) { + if (!proxy_digest_auth_username_.empty() && + !proxy_digest_auth_password_.empty()) { + std::map auth; + if (detail::parse_www_authenticate(res2, auth, true)) { + Response res3; + if (!detail::process_client_socket( + socket.sock, read_timeout_sec_, read_timeout_usec_, + write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) { + Request req3; + req3.method = "CONNECT"; + req3.path = host_and_port_; + req3.headers.insert(detail::make_digest_authentication_header( + req3, auth, 1, detail::random_string(10), + proxy_digest_auth_username_, proxy_digest_auth_password_, + true)); + return process_request(strm, req3, res3, false, error); + })) { + // Thread-safe to close everything because we are assuming there are + // no requests in flight + shutdown_ssl(socket, true); + shutdown_socket(socket); + close_socket(socket); + success = false; + return false; + } + } + } else { + res = res2; + return false; + } + } - if (verify_result_ != X509_V_OK) { return false; } + return true; +} - auto server_cert = SSL_get_peer_certificate(ssl); +inline bool SSLClient::load_certs() { + bool ret = true; - if (server_cert == nullptr) { return false; } + std::call_once(initialize_cert_, [&]() { + std::lock_guard guard(ctx_mutex_); + if (!ca_cert_file_path_.empty()) { + if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(), + nullptr)) { + ret = false; + } + } else if (!ca_cert_dir_path_.empty()) { + if (!SSL_CTX_load_verify_locations(ctx_, nullptr, + ca_cert_dir_path_.c_str())) { + ret = false; + } + } else { +#ifdef _WIN32 + detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_)); +#else + SSL_CTX_set_default_verify_paths(ctx_); +#endif + } + }); - if (!verify_host(server_cert)) { - X509_free(server_cert); - return false; - } - X509_free(server_cert); - } + return ret; +} - return true; - }, - [&](SSL *ssl) { - SSL_set_tlsext_host_name(ssl, host_.c_str()); - return true; - }, - [&](SSL * /*ssl*/, Stream &strm, bool /*last_connection*/, - bool &connection_close) { - return process_request(strm, req, res, connection_close); - }); +inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) { + auto ssl = detail::ssl_new( + socket.sock, ctx_, ctx_mutex_, + [&](SSL *ssl) { + if (server_certificate_verification_) { + if (!load_certs()) { + error = Error::SSLLoadingCerts; + return false; + } + SSL_set_verify(ssl, SSL_VERIFY_NONE, nullptr); + } + + if (!detail::ssl_connect_or_accept_nonblocking( + socket.sock, ssl, SSL_connect, connection_timeout_sec_, + connection_timeout_usec_)) { + error = Error::SSLConnection; + return false; + } + + if (server_certificate_verification_) { + verify_result_ = SSL_get_verify_result(ssl); + + if (verify_result_ != X509_V_OK) { + error = Error::SSLServerVerification; + return false; + } + + auto server_cert = SSL_get_peer_certificate(ssl); + + if (server_cert == nullptr) { + error = Error::SSLServerVerification; + return false; + } + + if (!verify_host(server_cert)) { + X509_free(server_cert); + error = Error::SSLServerVerification; + return false; + } + X509_free(server_cert); + } + + return true; + }, + [&](SSL *ssl) { + SSL_set_tlsext_host_name(ssl, host_.c_str()); + return true; + }); + + if (ssl) { + socket.ssl = ssl; + return true; + } + + shutdown_socket(socket); + close_socket(socket); + return false; +} + +inline void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) { + shutdown_ssl_impl(socket, shutdown_gracefully); +} + +inline void SSLClient::shutdown_ssl_impl(Socket &socket, + bool shutdown_gracefully) { + if (socket.sock == INVALID_SOCKET) { + assert(socket.ssl == nullptr); + return; + } + if (socket.ssl) { + detail::ssl_delete(ctx_mutex_, socket.ssl, shutdown_gracefully); + socket.ssl = nullptr; + } + assert(socket.ssl == nullptr); +} + +inline bool +SSLClient::process_socket(const Socket &socket, + std::function callback) { + assert(socket.ssl); + return detail::process_client_socket_ssl( + socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_, + write_timeout_sec_, write_timeout_usec_, std::move(callback)); } inline bool SSLClient::is_ssl() const { return true; } @@ -2600,6 +7122,7 @@ SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const { struct in_addr addr; size_t addr_len = 0; +#ifndef __MINGW32__ if (inet_pton(AF_INET6, host_.c_str(), &addr6)) { type = GEN_IPADD; addr_len = sizeof(struct in6_addr); @@ -2607,6 +7130,7 @@ SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const { type = GEN_IPADD; addr_len = sizeof(struct in_addr); } +#endif auto alt_names = static_cast( X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr)); @@ -2617,23 +7141,21 @@ SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const { auto count = sk_GENERAL_NAME_num(alt_names); - for (auto i = 0; i < count && !dsn_matched; i++) { + for (decltype(count) i = 0; i < count && !dsn_matched; i++) { auto val = sk_GENERAL_NAME_value(alt_names, i); if (val->type == type) { auto name = (const char *)ASN1_STRING_get0_data(val->d.ia5); auto name_len = (size_t)ASN1_STRING_length(val->d.ia5); - if (strlen(name) == name_len) { - switch (type) { - case GEN_DNS: dsn_matched = check_host_name(name, name_len); break; + switch (type) { + case GEN_DNS: dsn_matched = check_host_name(name, name_len); break; - case GEN_IPADD: - if (!memcmp(&addr6, name, addr_len) || - !memcmp(&addr, name, addr_len)) { - ip_mached = true; - } - break; + case GEN_IPADD: + if (!memcmp(&addr6, name, addr_len) || + !memcmp(&addr, name, addr_len)) { + ip_mached = true; } + break; } } } @@ -2642,7 +7164,6 @@ SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const { } GENERAL_NAMES_free((STACK_OF(GENERAL_NAME) *)alt_names); - return ret; } @@ -2654,7 +7175,9 @@ inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const { auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, name, sizeof(name)); - if (name_len != -1) { return check_host_name(name, name_len); } + if (name_len != -1) { + return check_host_name(name, static_cast(name_len)); + } } return false; @@ -2689,6 +7212,452 @@ inline bool SSLClient::check_host_name(const char *pattern, } #endif +// Universal client implementation + inline Client::Client(const char *scheme_host_port) + : Client(scheme_host_port, std::string(), std::string()) {} + + inline Client::Client(const char *scheme_host_port, + const std::string &client_cert_path, + const std::string &client_key_path) { + const static std::regex re(R"(^(?:([a-z]+)://)?([^:/?#]+)(?::(\d+))?)"); + + std::cmatch m; + if (std::regex_match(scheme_host_port, m, re)) { + auto scheme = m[1].str(); + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + if (!scheme.empty() && (scheme != "http" && scheme != "https")) { +#else + if (!scheme.empty() && scheme != "http") { +#endif + std::string msg = "'" + scheme + "' scheme is not supported."; + throw std::invalid_argument(msg); + return; + } + + auto is_ssl = scheme == "https"; + + auto host = m[2].str(); + + auto port_str = m[3].str(); + auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80); + + if (is_ssl) { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + cli_ = detail::make_unique(host.c_str(), port, + client_cert_path, client_key_path); + is_ssl_ = is_ssl; +#endif + } else { + cli_ = detail::make_unique(host.c_str(), port, + client_cert_path, client_key_path); + } + } else { + cli_ = detail::make_unique(scheme_host_port, 80, + client_cert_path, client_key_path); + } + } + + inline Client::Client(const std::string &host, int port) + : cli_(detail::make_unique(host, port)) {} + + inline Client::Client(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path) + : cli_(detail::make_unique(host, port, client_cert_path, + client_key_path)) {} + + inline Client::~Client() {} + + inline bool Client::is_valid() const { + return cli_ != nullptr && cli_->is_valid(); + } + + inline Result Client::Get(const char *path) { return cli_->Get(path); } + inline Result Client::Get(const char *path, const Headers &headers) { + return cli_->Get(path, headers); + } + inline Result Client::Get(const char *path, Progress progress) { + return cli_->Get(path, std::move(progress)); + } + inline Result Client::Get(const char *path, const Headers &headers, + Progress progress) { + return cli_->Get(path, headers, std::move(progress)); + } + inline Result Client::Get(const char *path, ContentReceiver content_receiver) { + return cli_->Get(path, std::move(content_receiver)); + } + inline Result Client::Get(const char *path, const Headers &headers, + ContentReceiver content_receiver) { + return cli_->Get(path, headers, std::move(content_receiver)); + } + inline Result Client::Get(const char *path, ContentReceiver content_receiver, + Progress progress) { + return cli_->Get(path, std::move(content_receiver), std::move(progress)); + } + inline Result Client::Get(const char *path, const Headers &headers, + ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, headers, std::move(content_receiver), + std::move(progress)); + } + inline Result Client::Get(const char *path, ResponseHandler response_handler, + ContentReceiver content_receiver) { + return cli_->Get(path, std::move(response_handler), + std::move(content_receiver)); + } + inline Result Client::Get(const char *path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver) { + return cli_->Get(path, headers, std::move(response_handler), + std::move(content_receiver)); + } + inline Result Client::Get(const char *path, ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, std::move(response_handler), + std::move(content_receiver), std::move(progress)); + } + inline Result Client::Get(const char *path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, headers, std::move(response_handler), + std::move(content_receiver), std::move(progress)); + } + inline Result Client::Get(const char *path, const Params ¶ms, + const Headers &headers, Progress progress) { + return cli_->Get(path, params, headers, progress); + } + inline Result Client::Get(const char *path, const Params ¶ms, + const Headers &headers, + ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, params, headers, content_receiver, progress); + } + inline Result Client::Get(const char *path, const Params ¶ms, + const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, params, headers, response_handler, content_receiver, + progress); + } + + inline Result Client::Head(const char *path) { return cli_->Head(path); } + inline Result Client::Head(const char *path, const Headers &headers) { + return cli_->Head(path, headers); + } + + inline Result Client::Post(const char *path) { return cli_->Post(path); } + inline Result Client::Post(const char *path, const char *body, + size_t content_length, const char *content_type) { + return cli_->Post(path, body, content_length, content_type); + } + inline Result Client::Post(const char *path, const Headers &headers, + const char *body, size_t content_length, + const char *content_type) { + return cli_->Post(path, headers, body, content_length, content_type); + } + inline Result Client::Post(const char *path, const std::string &body, + const char *content_type) { + return cli_->Post(path, body, content_type); + } + inline Result Client::Post(const char *path, const Headers &headers, + const std::string &body, const char *content_type) { + return cli_->Post(path, headers, body, content_type); + } + inline Result Client::Post(const char *path, size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Post(path, content_length, std::move(content_provider), + content_type); + } + inline Result Client::Post(const char *path, + ContentProviderWithoutLength content_provider, + const char *content_type) { + return cli_->Post(path, std::move(content_provider), content_type); + } + inline Result Client::Post(const char *path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Post(path, headers, content_length, std::move(content_provider), + content_type); + } + inline Result Client::Post(const char *path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const char *content_type) { + return cli_->Post(path, headers, std::move(content_provider), content_type); + } + inline Result Client::Post(const char *path, const Params ¶ms) { + return cli_->Post(path, params); + } + inline Result Client::Post(const char *path, const Headers &headers, + const Params ¶ms) { + return cli_->Post(path, headers, params); + } + inline Result Client::Post(const char *path, + const MultipartFormDataItems &items) { + return cli_->Post(path, items); + } + inline Result Client::Post(const char *path, const Headers &headers, + const MultipartFormDataItems &items) { + return cli_->Post(path, headers, items); + } + inline Result Client::Post(const char *path, const Headers &headers, + const MultipartFormDataItems &items, + const std::string &boundary) { + return cli_->Post(path, headers, items, boundary); + } + inline Result Client::Put(const char *path) { return cli_->Put(path); } + inline Result Client::Put(const char *path, const char *body, + size_t content_length, const char *content_type) { + return cli_->Put(path, body, content_length, content_type); + } + inline Result Client::Put(const char *path, const Headers &headers, + const char *body, size_t content_length, + const char *content_type) { + return cli_->Put(path, headers, body, content_length, content_type); + } + inline Result Client::Put(const char *path, const std::string &body, + const char *content_type) { + return cli_->Put(path, body, content_type); + } + inline Result Client::Put(const char *path, const Headers &headers, + const std::string &body, const char *content_type) { + return cli_->Put(path, headers, body, content_type); + } + inline Result Client::Put(const char *path, size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Put(path, content_length, std::move(content_provider), + content_type); + } + inline Result Client::Put(const char *path, + ContentProviderWithoutLength content_provider, + const char *content_type) { + return cli_->Put(path, std::move(content_provider), content_type); + } + inline Result Client::Put(const char *path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Put(path, headers, content_length, std::move(content_provider), + content_type); + } + inline Result Client::Put(const char *path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const char *content_type) { + return cli_->Put(path, headers, std::move(content_provider), content_type); + } + inline Result Client::Put(const char *path, const Params ¶ms) { + return cli_->Put(path, params); + } + inline Result Client::Put(const char *path, const Headers &headers, + const Params ¶ms) { + return cli_->Put(path, headers, params); + } + inline Result Client::Patch(const char *path) { return cli_->Patch(path); } + inline Result Client::Patch(const char *path, const char *body, + size_t content_length, const char *content_type) { + return cli_->Patch(path, body, content_length, content_type); + } + inline Result Client::Patch(const char *path, const Headers &headers, + const char *body, size_t content_length, + const char *content_type) { + return cli_->Patch(path, headers, body, content_length, content_type); + } + inline Result Client::Patch(const char *path, const std::string &body, + const char *content_type) { + return cli_->Patch(path, body, content_type); + } + inline Result Client::Patch(const char *path, const Headers &headers, + const std::string &body, const char *content_type) { + return cli_->Patch(path, headers, body, content_type); + } + inline Result Client::Patch(const char *path, size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Patch(path, content_length, std::move(content_provider), + content_type); + } + inline Result Client::Patch(const char *path, + ContentProviderWithoutLength content_provider, + const char *content_type) { + return cli_->Patch(path, std::move(content_provider), content_type); + } + inline Result Client::Patch(const char *path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Patch(path, headers, content_length, std::move(content_provider), + content_type); + } + inline Result Client::Patch(const char *path, const Headers &headers, + ContentProviderWithoutLength content_provider, + const char *content_type) { + return cli_->Patch(path, headers, std::move(content_provider), content_type); + } + inline Result Client::Delete(const char *path) { return cli_->Delete(path); } + inline Result Client::Delete(const char *path, const Headers &headers) { + return cli_->Delete(path, headers); + } + inline Result Client::Delete(const char *path, const char *body, + size_t content_length, const char *content_type) { + return cli_->Delete(path, body, content_length, content_type); + } + inline Result Client::Delete(const char *path, const Headers &headers, + const char *body, size_t content_length, + const char *content_type) { + return cli_->Delete(path, headers, body, content_length, content_type); + } + inline Result Client::Delete(const char *path, const std::string &body, + const char *content_type) { + return cli_->Delete(path, body, content_type); + } + inline Result Client::Delete(const char *path, const Headers &headers, + const std::string &body, + const char *content_type) { + return cli_->Delete(path, headers, body, content_type); + } + inline Result Client::Options(const char *path) { return cli_->Options(path); } + inline Result Client::Options(const char *path, const Headers &headers) { + return cli_->Options(path, headers); + } + + inline bool Client::send(Request &req, Response &res, Error &error) { + return cli_->send(req, res, error); + } + + inline Result Client::send(const Request &req) { return cli_->send(req); } + + inline size_t Client::is_socket_open() const { return cli_->is_socket_open(); } + + inline void Client::stop() { cli_->stop(); } + + inline void Client::set_default_headers(Headers headers) { + cli_->set_default_headers(std::move(headers)); + } + + inline void Client::set_address_family(int family) { + cli_->set_address_family(family); + } + + inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); } + + inline void Client::set_socket_options(SocketOptions socket_options) { + cli_->set_socket_options(std::move(socket_options)); + } + + inline void Client::set_connection_timeout(time_t sec, time_t usec) { + cli_->set_connection_timeout(sec, usec); + } + + template + inline void Client::set_connection_timeout( + const std::chrono::duration &duration) { + cli_->set_connection_timeout(duration); + } + + inline void Client::set_read_timeout(time_t sec, time_t usec) { + cli_->set_read_timeout(sec, usec); + } + + template + inline void + Client::set_read_timeout(const std::chrono::duration &duration) { + cli_->set_read_timeout(duration); + } + + inline void Client::set_write_timeout(time_t sec, time_t usec) { + cli_->set_write_timeout(sec, usec); + } + + template + inline void + Client::set_write_timeout(const std::chrono::duration &duration) { + cli_->set_write_timeout(duration); + } + + inline void Client::set_basic_auth(const char *username, const char *password) { + cli_->set_basic_auth(username, password); + } + inline void Client::set_bearer_token_auth(const char *token) { + cli_->set_bearer_token_auth(token); + } +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + inline void Client::set_digest_auth(const char *username, + const char *password) { + cli_->set_digest_auth(username, password); +} +#endif + + inline void Client::set_keep_alive(bool on) { cli_->set_keep_alive(on); } + inline void Client::set_follow_location(bool on) { + cli_->set_follow_location(on); + } + + inline void Client::set_url_encode(bool on) { cli_->set_url_encode(on); } + + inline void Client::set_compress(bool on) { cli_->set_compress(on); } + + inline void Client::set_decompress(bool on) { cli_->set_decompress(on); } + + inline void Client::set_interface(const char *intf) { + cli_->set_interface(intf); + } + + inline void Client::set_proxy(const char *host, int port) { + cli_->set_proxy(host, port); + } + inline void Client::set_proxy_basic_auth(const char *username, + const char *password) { + cli_->set_proxy_basic_auth(username, password); + } + inline void Client::set_proxy_bearer_token_auth(const char *token) { + cli_->set_proxy_bearer_token_auth(token); + } +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + inline void Client::set_proxy_digest_auth(const char *username, + const char *password) { + cli_->set_proxy_digest_auth(username, password); +} +#endif + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + inline void Client::enable_server_certificate_verification(bool enabled) { + cli_->enable_server_certificate_verification(enabled); +} +#endif + + inline void Client::set_logger(Logger logger) { cli_->set_logger(logger); } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + inline void Client::set_ca_cert_path(const char *ca_cert_file_path, + const char *ca_cert_dir_path) { + if (is_ssl_) { + static_cast(*cli_).set_ca_cert_path(ca_cert_file_path, + ca_cert_dir_path); + } +} + +inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) { + if (is_ssl_) { + static_cast(*cli_).set_ca_cert_store(ca_cert_store); + } +} + +inline long Client::get_openssl_verify_result() const { + if (is_ssl_) { + return static_cast(*cli_).get_openssl_verify_result(); + } + return -1; // NOTE: -1 doesn't match any of X509_V_ERR_??? +} + +inline SSL_CTX *Client::ssl_context() const { + if (is_ssl_) { return static_cast(*cli_).ssl_context(); } + return nullptr; +} +#endif + +// ---------------------------------------------------------------------------- + } // namespace httplib -#endif // CPPHTTPLIB_HTTPLIB_H \ No newline at end of file +#endif // CPPHTTPLIB_HTTPLIB_H diff --git a/loginserver/main.cpp b/loginserver/main.cpp index 45469ff12..ec2cb616d 100644 --- a/loginserver/main.cpp +++ b/loginserver/main.cpp @@ -34,6 +34,7 @@ #include #include #include +#include LoginServer server; EQEmuLogSys LogSys; @@ -130,6 +131,23 @@ void LoadServerConfig() ); } +void start_web_server() +{ + int web_api_port = server.config.GetVariableInt("web_api", "port", 6000); + LogInfo("Webserver API now listening on port [{0}]", web_api_port); + + httplib::Server api; + + api.set_logger([](const auto& req, const auto& res) { + if (!req.path.empty()) { + LogInfo("[API] Request [{}] via [{}:{}]", req.path, req.remote_addr, req.remote_port); + } + }); + + LoginserverWebserver::RegisterRoutes(api); + api.listen("0.0.0.0", web_api_port); +} + int main(int argc, char **argv) { RegisterExecutablePlatform(ExePlatformLogin); @@ -195,7 +213,7 @@ int main(int argc, char **argv) * create client manager */ LogInfo("Client Manager Init"); - server.client_manager = new ClientManager(); + server.client_manager = new ClientManager(); if (!server.client_manager) { LogError("Client Manager Failed to Start"); LogInfo("Server Manager Shutdown"); @@ -222,13 +240,10 @@ int main(int argc, char **argv) /** * Web API */ - httplib::Server api; - int web_api_port = server.config.GetVariableInt("web_api", "port", 6000); - bool web_api_enabled = server.config.GetVariableBool("web_api", "enabled", true); + bool web_api_enabled = server.config.GetVariableBool("web_api", "enabled", true); if (web_api_enabled) { - api.bind("0.0.0.0", web_api_port); - LogInfo("Webserver API now listening on port [{0}]", web_api_port); - LoginserverWebserver::RegisterRoutes(api); + std::thread web_api_thread(start_web_server); + web_api_thread.detach(); } LogInfo("[Config] [Logging] IsTraceOn [{0}]", server.options.IsTraceOn()); @@ -251,10 +266,6 @@ int main(int argc, char **argv) server.client_manager->Process(); EQ::EventLoop::Get().Process(); - if (web_api_enabled) { - api.poll(); - } - Sleep(5); } From 4c7f2391cd9b6729ec455a29c90b0349f0272663 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 13 Jun 2021 22:48:48 -0400 Subject: [PATCH 087/624] [Commands] Modify #summonitem and #giveitem. (#1400) - #summonitem will now properly take item augments from an item link when used as a link for summoning. - #summonitem help message and command message will now list proper argument list. - #giveitem will now allow item links like #summonitem with the same functionality level. - #giveitem help message and command message will now list proper argument list. - #giveitem small fix where there were 2 checks for argument count at 7, meaning final argument count (8) did not work. --- zone/command.cpp | 221 +++++++++++++++++++++++++++++------------------ 1 file changed, 135 insertions(+), 86 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 994562d06..c89ded9c3 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -7973,122 +7973,171 @@ void command_interrupt(Client *c, const Seperator *sep) void command_summonitem(Client *c, const Seperator *sep) { - uint32 itemid = 0; - + uint32 item_id = 0; + int16 charges = -1; + uint32 augment_one = 0; + uint32 augment_two = 0; + uint32 augment_three = 0; + uint32 augment_four = 0; + uint32 augment_five = 0; + uint32 augment_six = 0; + int arguments = sep->argnum; std::string cmd_msg = sep->msg; size_t link_open = cmd_msg.find('\x12'); size_t link_close = cmd_msg.find_last_of('\x12'); if (link_open != link_close && (cmd_msg.length() - link_open) > EQ::constants::SAY_LINK_BODY_SIZE) { EQ::SayLinkBody_Struct link_body; EQ::saylink::DegenerateLinkBody(link_body, cmd_msg.substr(link_open + 1, EQ::constants::SAY_LINK_BODY_SIZE)); - itemid = link_body.item_id; - } - else if (!sep->IsNumber(1)) { - c->Message(Chat::White, "Usage: #summonitem [item id | link] [charges], charges are optional"); + item_id = link_body.item_id; + augment_one = link_body.augment_1; + augment_two = link_body.augment_2; + augment_three = link_body.augment_3; + augment_four = link_body.augment_4; + augment_five = link_body.augment_5; + augment_six = link_body.augment_6; + } else if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #summonitem [item id | link] [charges] [augment_one_id] [augment_two_id] [augment_three_id] [augment_four_id] [augment_five_id] [augment_six_id] (Charges are optional.)"); return; + } else { + item_id = atoi(sep->arg[1]); } - else { - itemid = atoi(sep->arg[1]); - } - if (!itemid) { - c->Message(Chat::White, "A valid item id number is required (derived: 0)"); + + if (!item_id) { + c->Message(Chat::White, "Enter a valid item ID."); return; } - int16 item_status = 0; - const EQ::ItemData* item = database.GetItem(itemid); + uint8 item_status = 0; + uint8 current_status = c->Admin(); + const EQ::ItemData* item = database.GetItem(item_id); if (item) { - item_status = static_cast(item->MinStatus); + item_status = item->MinStatus; } - if (item_status > c->Admin()) { - c->Message(Chat::Red, "Error: Insufficient status to summon this item."); + if (item_status > current_status) { + c->Message( + Chat::White, + fmt::format( + "Insufficient status to summon this item, current status is {}, required status is {}.", + current_status, + item_status + ).c_str() + ); } - else if (sep->argnum == 2 && sep->IsNumber(2)) { - c->SummonItem(itemid, atoi(sep->arg[2])); + + if (arguments >= 2 && sep->IsNumber(2)) { + charges = atoi(sep->arg[2]); } - else if (sep->argnum == 3) { - c->SummonItem(itemid, atoi(sep->arg[2]), atoi(sep->arg[3])); + + if (arguments >= 3 && sep->IsNumber(3)) { + augment_one = atoi(sep->arg[3]); } - else if (sep->argnum == 4) { - c->SummonItem(itemid, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4])); + + if (arguments >= 4 && sep->IsNumber(4)) { + augment_two = atoi(sep->arg[4]); } - else if (sep->argnum == 5) { - c->SummonItem(itemid, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), atoi(sep->arg[5])); + + if (arguments >= 5 && sep->IsNumber(5)) { + augment_three = atoi(sep->arg[5]); } - else if (sep->argnum == 6) { - c->SummonItem( - itemid, - atoi(sep->arg[2]), - atoi(sep->arg[3]), - atoi(sep->arg[4]), - atoi(sep->arg[5]), - atoi(sep->arg[6])); + + if (arguments >= 6 && sep->IsNumber(6)) { + augment_four = atoi(sep->arg[6]); } - else if (sep->argnum == 7) { - c->SummonItem( - itemid, - atoi(sep->arg[2]), - atoi(sep->arg[3]), - atoi(sep->arg[4]), - atoi(sep->arg[5]), - atoi(sep->arg[6]), - atoi(sep->arg[7])); + + if (arguments >= 7 && sep->IsNumber(7)) { + augment_five = atoi(sep->arg[7]); } - else if (sep->argnum == 8) { - c->SummonItem( - itemid, - atoi(sep->arg[2]), - atoi(sep->arg[3]), - atoi(sep->arg[4]), - atoi(sep->arg[5]), - atoi(sep->arg[6]), - atoi(sep->arg[7]), - atoi(sep->arg[8])); - } - else { - c->SummonItem(itemid); + + if (arguments == 8 && sep->IsNumber(8)) { + augment_six = atoi(sep->arg[8]); } + c->SummonItem(item_id, charges, augment_one, augment_two, augment_three, augment_four, augment_five, augment_six); } void command_giveitem(Client *c, const Seperator *sep) { - if (!sep->IsNumber(1)) { - c->Message(Chat::Red, "Usage: #summonitem [item id] [charges], charges are optional"); - } else if(c->GetTarget() == nullptr) { + uint32 item_id = 0; + int16 charges = -1; + uint32 augment_one = 0; + uint32 augment_two = 0; + uint32 augment_three = 0; + uint32 augment_four = 0; + uint32 augment_five = 0; + uint32 augment_six = 0; + int arguments = sep->argnum; + std::string cmd_msg = sep->msg; + size_t link_open = cmd_msg.find('\x12'); + size_t link_close = cmd_msg.find_last_of('\x12'); + if (link_open != link_close && (cmd_msg.length() - link_open) > EQ::constants::SAY_LINK_BODY_SIZE) { + EQ::SayLinkBody_Struct link_body; + EQ::saylink::DegenerateLinkBody(link_body, cmd_msg.substr(link_open + 1, EQ::constants::SAY_LINK_BODY_SIZE)); + item_id = link_body.item_id; + augment_one = link_body.augment_1; + augment_two = link_body.augment_2; + augment_three = link_body.augment_3; + augment_four = link_body.augment_4; + augment_five = link_body.augment_5; + augment_six = link_body.augment_6; + } else if (sep->IsNumber(1)) { + item_id = atoi(sep->arg[1]); + } else if (!sep->IsNumber(1)) { + c->Message(Chat::Red, "Usage: #giveitem [item id | link] [charges] [augment_one_id] [augment_two_id] [augment_three_id] [augment_four_id] [augment_five_id] [augment_six_id] (Charges are optional.)"); + } else if (!c->GetTarget()) { c->Message(Chat::Red, "You must target a client to give the item to."); - } else if(!c->GetTarget()->IsClient()) { + } else if (!c->GetTarget()->IsClient()) { c->Message(Chat::Red, "You can only give items to players with this command."); - } else { - Client *t = c->GetTarget()->CastToClient(); - uint32 itemid = atoi(sep->arg[1]); - int16 item_status = 0; - const EQ::ItemData* item = database.GetItem(itemid); - if(item) { - item_status = static_cast(item->MinStatus); - } - - if (item_status > c->Admin()) - c->Message(Chat::Red, "Error: Insufficient status to summon this item."); - else if (sep->argnum==2 && sep->IsNumber(2)) - t->SummonItem(itemid, atoi(sep->arg[2])); - else if (sep->argnum==3) - t->SummonItem(itemid, atoi(sep->arg[2]), atoi(sep->arg[3])); - else if (sep->argnum==4) - t->SummonItem(itemid, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4])); - else if (sep->argnum==5) - t->SummonItem(itemid, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), atoi(sep->arg[5])); - else if (sep->argnum==6) - t->SummonItem(itemid, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), atoi(sep->arg[5]), atoi(sep->arg[6])); - else if (sep->argnum==7) - t->SummonItem(itemid, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), atoi(sep->arg[5]), atoi(sep->arg[6]), atoi(sep->arg[7])); - else if (sep->argnum == 7) - t->SummonItem(itemid, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), atoi(sep->arg[5]), atoi(sep->arg[6]), atoi(sep->arg[7]), atoi(sep->arg[8])); - else { - t->SummonItem(itemid); - } } + + Client *client_target = c->GetTarget()->CastToClient(); + uint8 item_status = 0; + uint8 current_status = c->Admin(); + const EQ::ItemData* item = database.GetItem(item_id); + if (item) { + item_status = item->MinStatus; + } + + if (item_status > current_status) { + c->Message( + Chat::White, + fmt::format( + "Insufficient status to summon this item, current status is {}, required status is {}.", + current_status, + item_status + ).c_str() + ); + } + + if (arguments >= 2 && sep->IsNumber(2)) { + charges = atoi(sep->arg[2]); + } + + if (arguments >= 3 && sep->IsNumber(3)) { + augment_one = atoi(sep->arg[3]); + } + + if (arguments >= 4 && sep->IsNumber(4)) { + augment_two = atoi(sep->arg[4]); + } + + if (arguments >= 5 && sep->IsNumber(5)) { + augment_three = atoi(sep->arg[5]); + } + + if (arguments >= 6 && sep->IsNumber(6)) { + augment_four = atoi(sep->arg[6]); + } + + if (arguments >= 7 && sep->IsNumber(7)) { + augment_five = atoi(sep->arg[7]); + } + + if (arguments == 8 && sep->IsNumber(8)) { + augment_six = atoi(sep->arg[8]); + } + + client_target->SummonItem(item_id, charges, augment_one, augment_two, augment_three, augment_four, augment_five, augment_six); } void command_givemoney(Client *c, const Seperator *sep) From b9d8fb0d915e450614e727dc32e7ae9b181964fa Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 16 Jun 2021 10:30:32 -0400 Subject: [PATCH 088/624] [Quest API] Add rename(name) to Perl/Lua. (#1414) - Add quest::rename(name) to Perl. - Add eq.rename(name) to Lua. --- zone/embparser_api.cpp | 12 ++++++++++++ zone/lua_general.cpp | 5 +++++ zone/questmgr.cpp | 25 +++++++++++++++++++++++++ zone/questmgr.h | 1 + 4 files changed, 43 insertions(+) diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 4c92c4969..4185c145f 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -6774,6 +6774,17 @@ XS(XS__getcleannpcnamebyid) { XSRETURN(1); } +XS(XS__rename); +XS(XS__rename) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::rename(string name)"); + + std::string name = (std::string) SvPV_nolen(ST(0)); + quest_manager.rename(name); + XSRETURN_EMPTY; +} + /* This is the callback perl will look for to setup the quest package's XSUBs @@ -7079,6 +7090,7 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "remove_expedition_lockout_by_char_id"), XS__remove_expedition_lockout_by_char_id, file); newXS(strcpy(buf, "removeitem"), XS__removeitem, file); newXS(strcpy(buf, "removetitle"), XS__removetitle, file); + newXS(strcpy(buf, "rename"), XS__rename, file); newXS(strcpy(buf, "repopzone"), XS__repopzone, file); newXS(strcpy(buf, "resettaskactivity"), XS__resettaskactivity, file); newXS(strcpy(buf, "respawn"), XS__respawn, file); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 046b8c7d6..7b6c5bcdb 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -2459,6 +2459,10 @@ std::string lua_get_clean_npc_name_by_id(uint32 npc_id) { return quest_manager.getcleannpcnamebyid(npc_id); } +void lua_rename(std::string name) { + quest_manager.rename(name); +} + #define LuaCreateNPCParse(name, c_type, default_value) do { \ cur = table[#name]; \ if(luabind::type(cur) != LUA_TNIL) { \ @@ -3024,6 +3028,7 @@ luabind::scope lua_register_general() { luabind::def("cross_zone_add_ldon_loss_by_expedition_id", &lua_cross_zone_add_ldon_loss_by_expedition_id), luabind::def("cross_zone_add_ldon_points_by_expedition_id", &lua_cross_zone_add_ldon_points_by_expedition_id), luabind::def("cross_zone_add_ldon_win_by_expedition_id", &lua_cross_zone_add_ldon_win_by_expedition_id), + luabind::def("rename", &lua_rename), /** * Expansions */ diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 640d5f4f6..1dc080d65 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1044,6 +1044,31 @@ void QuestManager::snow(int weather) { safe_delete(outapp); } +void QuestManager::rename(std::string name) { + QuestManagerCurrentQuestVars(); + if (initiator && initiator->IsClient()) { + std::string current_name = initiator->GetName(); + if (initiator->ChangeFirstName(name.c_str(), current_name.c_str())) { + initiator->Message( + Chat::White, + fmt::format( + "Successfully renamed to {}, kicking to character select.", + name + ).c_str() + ); + initiator->Kick("Name was changed."); + } else { + initiator->Message( + Chat::Red, + fmt::format( + "Failed to rename {} to {}.", + current_name, name + ).c_str() + ); + } + } +} + void QuestManager::surname(const char *name) { QuestManagerCurrentQuestVars(); //Changes the last name. diff --git a/zone/questmgr.h b/zone/questmgr.h index 999e128e6..90d31f434 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -117,6 +117,7 @@ public: void safemove(); void rain(int weather); void snow(int weather); + void rename(std::string name); void surname(const char *name); void permaclass(int class_id); void permarace(int race_id); From 19b14ea2d46f18027a092832a9acead2314dc298 Mon Sep 17 00:00:00 2001 From: splose Date: Wed, 16 Jun 2021 10:31:38 -0400 Subject: [PATCH 089/624] [Bug Fix] Fixed Invis vs Undead / Invis Vs Animals not breaking charm + Added rule for custom capabilities (#1374) * Fixed Invis vs Undead / Invis Vs Animals not breaking charm Added rule for custom capabilities * Fix logging & remove comments * change logic * change logic * if (RuleB(Pets, LivelikeBreakCharmOnInvis) || IsInvisible(formerpet)) --- common/ruletypes.h | 1 + zone/mob.cpp | 10 +++++++--- zone/spell_effects.cpp | 2 ++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index ae1625ef5..9047d0d66 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -206,6 +206,7 @@ RULE_REAL(Pets, AttackCommandRange, 150, "Range at which a pet will respond to a RULE_BOOL(Pets, UnTargetableSwarmPet, false, "Setting whether swarm pets should be targetable") RULE_REAL(Pets, PetPowerLevelCap, 10, "Maximum number of levels a player pet can go up with pet power") RULE_BOOL(Pets, CanTakeNoDrop, false, "Setting whether anyone can give no-drop items to pets") +RULE_BOOL(Pets, LivelikeBreakCharmOnInvis, true, "Default: true will break charm on any type of invis (hide/ivu/iva/etc) false will only break if the pet can not see you (ex. you have an undead pet and cast IVU") RULE_CATEGORY_END() RULE_CATEGORY(GM) diff --git a/zone/mob.cpp b/zone/mob.cpp index 84014d2ee..6de62bf9e 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -549,10 +549,14 @@ void Mob::SetInvisible(uint8 state) invisible = state; SendAppearancePacket(AT_Invis, invisible); // Invis and hide breaks charms - auto formerpet = GetPet(); - if (formerpet && formerpet->GetPetType() == petCharmed && (invisible || hidden || improved_hidden)) - formerpet->BuffFadeByEffect(SE_Charm); + if (formerpet && formerpet->GetPetType() == petCharmed && (invisible || hidden || improved_hidden || invisible_animals || invisible_undead)) { + if (RuleB(Pets, LivelikeBreakCharmOnInvis) || IsInvisible(formerpet)) + formerpet->BuffFadeByEffect(SE_Charm); + } + + LogRules("Pets:LivelikeBreakCharmOnInvis for [{}] | Invis [{}] - Hidden [{}] - Shroud of Stealth [{}] - IVA [{}] - IVU [{}]", GetCleanName(), invisible, hidden, improved_hidden, invisible_animals, invisible_undead); + } } //check to see if `this` is invisible to `other` diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index cb2895954..ceffa5f44 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -600,6 +600,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove snprintf(effect_desc, _EDLEN, "Invisibility to Animals"); #endif invisible_animals = true; + SetInvisible(0); break; } @@ -610,6 +611,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove snprintf(effect_desc, _EDLEN, "Invisibility to Undead"); #endif invisible_undead = true; + SetInvisible(0); break; } case SE_SeeInvis: From f2ffca1a06fe336c853d612e12f36a4447ea22ef Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Wed, 16 Jun 2021 09:31:56 -0500 Subject: [PATCH 090/624] [Command] #gearup Table Auto-Install (#1402) * Update syntax for new httplib and run on own thread * Only log if path is set in request * Auto install tool table if does not exist locally * Add lore and has item checks to reduce verbosity and errors * Formatting * Remove test code from test command --- world/world_server_command_handler.cpp | 2 +- zone/command.cpp | 103 ++++++++++++++++++++----- 2 files changed, 86 insertions(+), 19 deletions(-) diff --git a/world/world_server_command_handler.cpp b/world/world_server_command_handler.cpp index b5d226664..4c5a36cc7 100644 --- a/world/world_server_command_handler.cpp +++ b/world/world_server_command_handler.cpp @@ -469,7 +469,7 @@ namespace WorldserverCommandHandler { "destination_character_name", "destination_account_name" }; - std::vector options = { }; + std::vector options = {}; if (cmd[{"-h", "--help"}]) { return; diff --git a/zone/command.cpp b/zone/command.cpp index c89ded9c3..40c7b6d82 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -76,6 +76,9 @@ #include "npc_scale_manager.h" #include "../common/content/world_content_service.h" +#define CPPHTTPLIB_OPENSSL_SUPPORT +#include "../common/http/httplib.h" + extern QueryServ* QServ; extern WorldServer worldserver; extern TaskManager *task_manager; @@ -2513,7 +2516,7 @@ void command_grid(Client *c, const Seperator *sep) c->Message(Chat::White, "You need to target an NPC!"); return; } - + auto grid_id = target->CastToNPC()->GetGrid(); std::string query = fmt::format( "SELECT `x`, `y`, `z`, `heading`, `number` " @@ -2887,7 +2890,7 @@ void command_findspell(Client *c, const Seperator *sep) c->Message( Chat::White, fmt::format( - "{}: {}", + "{}: {}", spell_id, spells[spell_id].name ).c_str() @@ -2905,7 +2908,7 @@ void command_findspell(Client *c, const Seperator *sep) if (search_criteria.length() > 0 && spell_name_lower.find(search_criteria) == std::string::npos) { continue; } - + c->Message( Chat::White, fmt::format( @@ -2915,7 +2918,7 @@ void command_findspell(Client *c, const Seperator *sep) ).c_str() ); found_count++; - + if (found_count == 20) { break; } @@ -3101,16 +3104,60 @@ void command_race(Client *c, const Seperator *sep) void command_gearup(Client *c, const Seperator *sep) { std::string tool_table_name = "tool_gearup_armor_sets"; - if (!database.DoesTableExist(tool_table_name)) { c->Message( - Chat::Red, + Chat::Yellow, fmt::format( - "Table [{}] does not exist, please source in the optional SQL required for this tool", + "Table [{}] does not exist. Downloading from Github and installing...", tool_table_name ).c_str() ); - return; + + // http get request + httplib::Client cli("https://raw.githubusercontent.com"); + cli.set_connection_timeout(0, 15000000); // 15 sec + cli.set_read_timeout(15, 0); // 15 seconds + cli.set_write_timeout(15, 0); // 15 seconds + + int sourced_queries = 0; + std::string url = "/EQEmu/Server/master/utils/sql/git/optional/2020_07_20_tool_gearup_armor_sets.sql"; + + if (auto res = cli.Get(url.c_str())) { + if (res->status == 200) { + for (auto &s: SplitString(res->body, ';')) { + if (!trim(s).empty()) { + auto results = database.QueryDatabase(s); + if (!results.ErrorMessage().empty()) { + c->Message( + Chat::Yellow, + fmt::format( + "Error sourcing SQL [{}]", results.ErrorMessage() + ).c_str() + ); + return; + } + sourced_queries++; + } + } + } + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Error retrieving URL [{}]", + url + ).c_str() + ); + } + + c->Message( + Chat::Yellow, + fmt::format( + "Table [{}] installed. Sourced [{}] queries", + tool_table_name, sourced_queries + ).c_str() + ); } std::string expansion_arg = sep->arg[1]; @@ -3140,8 +3187,11 @@ void command_gearup(Client *c, const Seperator *sep) ) ); + int items_equipped = 0; + int items_already_have = 0; std::set equipped; - for (auto row = results.begin(); row != results.end(); ++row) { + + for (auto row = results.begin(); row != results.end(); ++row) { int item_id = atoi(row[0]); int slot_id = atoi(row[1]); @@ -3158,17 +3208,34 @@ void command_gearup(Client *c, const Seperator *sep) } if (equipped.find(slot_id) == equipped.end()) { - if (c->CastToMob()->CanClassEquipItem(item_id)) { + const EQ::ItemData *item = database.GetItem(item_id); + bool has_item = (c->GetInv().HasItem(item_id, 1, invWhereWorn) != INVALID_INDEX); + bool can_wear_item = !c->CheckLoreConflict(item) && !has_item; + if (!can_wear_item) { + items_already_have++; + } + + if (c->CastToMob()->CanClassEquipItem(item_id) && can_wear_item) { equipped.insert(slot_id); c->SummonItem( item_id, 0, 0, 0, 0, 0, 0, 0, 0, slot_id ); + items_equipped++; } } } + c->Message( + Chat::White, + fmt::format( + "Equipped items [{}] already had [{}] items equipped", + items_equipped, + items_already_have + ).c_str() + ); + if (expansion_arg.empty()) { results = database.QueryDatabase( fmt::format( @@ -8198,23 +8265,23 @@ void command_itemsearch(Client *c, const Seperator *sep) if (pdest != nullptr) { linker.SetItemData(item); std::string item_id = std::to_string(item->ID); - std::string saylink_commands = - "[" + + std::string saylink_commands = + "[" + EQ::SayLinkEngine::GenerateQuestSaylink( "#si " + item_id, false, "X" - ) + + ) + "] "; if (item->Stackable && item->StackSize > 1) { std::string stack_size = std::to_string(item->StackSize); - saylink_commands += - "[" + + saylink_commands += + "[" + EQ::SayLinkEngine::GenerateQuestSaylink( "#si " + item_id + " " + stack_size, false, stack_size - ) + + ) + "]"; } @@ -14386,7 +14453,7 @@ void command_viewzoneloot(Client *c, const Seperator *sep) } } - + if (search_item_id != 0) { std::string drop_string = ( loot_amount > 0 ? @@ -14424,7 +14491,7 @@ void command_viewzoneloot(Client *c, const Seperator *sep) loot_amount, drop_string ).c_str() - ); + ); } } // All new code added to command.cpp should be BEFORE this comment line. Do no append code to this file below the BOTS code block. From ecdebbc1a73693c53668a3e4d20a7bc4b82c4e3e Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 16 Jun 2021 10:33:10 -0400 Subject: [PATCH 091/624] [Consistency] Deity not diety. (#1407) * [Consistency] Deity not diety. * Uppercase. [skip ci] --- common/faction.h | 8 ++++---- common/shareddb.cpp | 2 +- common/spdat.h | 4 ++-- zone/embparser_api.cpp | 4 ++-- zone/mob.cpp | 2 +- zone/questmgr.cpp | 6 +++--- zone/questmgr.h | 2 +- zone/zonedb.cpp | 6 +++--- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/common/faction.h b/common/faction.h index bc086669d..8c4bd8452 100755 --- a/common/faction.h +++ b/common/faction.h @@ -50,8 +50,8 @@ struct NPCFactionList { struct FactionMods { int32 base; - int16 min; // The lowest your personal earned faction can go - before race/class/diety adjustments. - int16 max; // The highest your personal earned faction can go - before race/class/diety adjustments. + int16 min; // The lowest your personal earned faction can go - before race/class/deity adjustments. + int16 max; // The highest your personal earned faction can go - before race/class/deity adjustments. int32 class_mod; int32 race_mod; int32 deity_mod; @@ -61,8 +61,8 @@ struct Faction { int32 id; std::map mods; int16 base; - int16 min; // The lowest your personal earned faction can go - before race/class/diety adjustments. - int16 max; // The highest your personal earned faction can go - before race/class/diety adjustments. + int16 min; // The lowest your personal earned faction can go - before race/class/deity adjustments. + int16 max; // The highest your personal earned faction can go - before race/class/deity adjustments. char name[50]; }; diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 6f2af55a3..33deb7fe8 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1822,7 +1822,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { sp[tempid].CastingAnim=atoi(row[120]); sp[tempid].SpellAffectIndex=atoi(row[123]); sp[tempid].disallow_sit=atoi(row[124]); - sp[tempid].diety_agnostic=atoi(row[125]); + sp[tempid].deity_agnostic=atoi(row[125]); for (y = 0; y < 16; y++) sp[tempid].deities[y]=atoi(row[126+y]); diff --git a/common/spdat.h b/common/spdat.h index 8f78cc992..a2a26da2e 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -759,11 +759,11 @@ struct SPDat_Spell_Struct /* 122 */ //uint32 TravelType; // -- TRAVELTYPE /* 123 */ uint16 SpellAffectIndex; // -- SPELLAFFECTINDEX /* 124 */ int8 disallow_sit; // 124: high-end Yaulp spells (V, VI, VII, VIII [Rk 1, 2, & 3], & Gallenite's Bark of Fury -- CANCELONSIT -/* 125 */ int8 diety_agnostic;// 125: Words of the Skeptic -- DIETY_AGNOSTIC +/* 125 */ int8 deity_agnostic;// 125: Words of the Skeptic -- DEITY_AGNOSTIC /* 126 */ int8 deities[16]; // Deity check. 201 - 216 per http://www.eqemulator.net/wiki/wikka.php?wakka=DeityList // -1: Restrict to Deity; 1: Restrict to Deity, but only used on non-Live (Test Server "Blessing of ...") spells; 0: Don't restrict // the client actually stores deities in a single int32_t - // -- DIETY_BERTOXXULOUS ... DIETY_VEESHAN + // -- DEITY_BERTOXXULOUS ... DEITY_VEESHAN /* 142 */ //int8 npc_no_cast; // 142: between 0 & 100 -- NPC_NO_CAST /* 143 */ //int ai_pt_bonus; // 143: always set to 0, client doesn't save this -- AI_PT_BONUS /* 144 */ int16 new_icon; // Spell icon used by the client in uifiles/default/spells??.tga, both for spell gems & buff window. Looks to depreciate icon & memicon -- NEW_ICON diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 4185c145f..d25b346d7 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -805,9 +805,9 @@ XS(XS__changedeity) { if (items != 1) Perl_croak(aTHX_ "Usage: quest::changedeity(int deity_id)"); - int diety_id = (int) SvIV(ST(0)); + int deity_id = (int) SvIV(ST(0)); - quest_manager.changedeity(diety_id); + quest_manager.changedeity(deity_id); XSRETURN_EMPTY; } diff --git a/zone/mob.cpp b/zone/mob.cpp index 6de62bf9e..88c7166e8 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -5557,7 +5557,7 @@ int32 Mob::GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot) if (slot < 16){ if (id == "classes") {return spells[spell_id].classes[slot]; } - else if (id == "dieties") {return spells[spell_id].deities[slot];} + else if (id == "deities") {return spells[spell_id].deities[slot];} } if (slot < 12){ diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 1dc080d65..0ec4a4309 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -897,15 +897,15 @@ void QuestManager::sfollow() { owner->SetFollowID(0); } -void QuestManager::changedeity(int diety_id) { +void QuestManager::changedeity(int deity_id) { QuestManagerCurrentQuestVars(); //Changes the deity. if(initiator) { if(initiator->IsClient()) { - initiator->SetDeity(diety_id); - initiator->Message(Chat::Yellow,"Your Deity has been changed/set to: %i", diety_id); + initiator->SetDeity(deity_id); + initiator->Message(Chat::Yellow,"Your Deity has been changed/set to: %i", deity_id); initiator->Save(1); initiator->Kick("Deity change by QuestManager"); } diff --git a/zone/questmgr.h b/zone/questmgr.h index 90d31f434..5b3a21629 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -106,7 +106,7 @@ public: void settarget(const char *type, int target_id); void follow(int entity_id, int distance); void sfollow(); - void changedeity(int diety_id); + void changedeity(int deity_id); void exp(int amt); void level(int newlevel); void traindisc(int discipline_tome_item_id); diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 327e43058..34d437a90 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -3930,8 +3930,8 @@ bool ZoneDatabase::GetFactionData(FactionMods* fm, uint32 class_mod, uint32 race } fm->base = faction_array[faction_id]->base; - fm->min = faction_array[faction_id]->min; // The lowest your personal earned faction can go - before race/class/diety adjustments. - fm->max = faction_array[faction_id]->max; // The highest your personal earned faction can go - before race/class/diety adjustments. + fm->min = faction_array[faction_id]->min; // The lowest your personal earned faction can go - before race/class/deity adjustments. + fm->max = faction_array[faction_id]->max; // The highest your personal earned faction can go - before race/class/deity adjustments. if(class_mod > 0) { char str[32]; @@ -4131,7 +4131,7 @@ bool ZoneDatabase::LoadFactionData() LogInfo("Unable to load Faction Base data..."); } - // load race, class and diety modifiers + // load race, class and deity modifiers query = fmt::format("SELECT `faction_id`, `mod`, `mod_name` FROM `faction_list_mod` WHERE `faction_id` IN ({})", faction_id_criteria); auto modifier_results = QueryDatabase(query); From 5d937b5be9a51c4615ce48045b39e99c1a7cff23 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Wed, 16 Jun 2021 09:51:16 -0500 Subject: [PATCH 092/624] [Hotfix] Correct PR syntax issue https://github.com/EQEmu/Server/pull/1374 --- zone/mob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index 88c7166e8..879cc49f9 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -551,7 +551,7 @@ void Mob::SetInvisible(uint8 state) // Invis and hide breaks charms auto formerpet = GetPet(); if (formerpet && formerpet->GetPetType() == petCharmed && (invisible || hidden || improved_hidden || invisible_animals || invisible_undead)) { - if (RuleB(Pets, LivelikeBreakCharmOnInvis) || IsInvisible(formerpet)) + if (RuleB(Pets, LivelikeBreakCharmOnInvis) || IsInvisible(formerpet)) { formerpet->BuffFadeByEffect(SE_Charm); } From 4f5824b4a199efddebd8faa3d381b5981cf2e04d Mon Sep 17 00:00:00 2001 From: Dencelle Date: Wed, 16 Jun 2021 10:04:34 -0500 Subject: [PATCH 093/624] [Feature] Add lua and perl event for test buff (#1403) * [Feature] Add lua and perl event for test buff * added EnableTestBuff --- common/ruletypes.h | 1 + zone/client_packet.cpp | 4 ++++ zone/embparser.cpp | 3 ++- zone/event_codes.h | 1 + zone/lua_general.cpp | 3 ++- zone/lua_parser.cpp | 4 +++- zone/lua_parser_events.cpp | 3 +++ zone/lua_parser_events.h | 2 ++ 8 files changed, 18 insertions(+), 3 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 9047d0d66..d045b44c4 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -165,6 +165,7 @@ RULE_BOOL(Character, ProcessFearedProximity, false, "Processes proximity checks RULE_BOOL(Character, EnableCharacterEXPMods, false, "Enables character zone-based experience modifiers.") RULE_BOOL(Character, PVPEnableGuardFactionAssist, true, "Enables faction based assisting against the aggresor in pvp.") RULE_BOOL(Character, SkillUpFromItems, true, "Allow Skill ups from clickable items") +RULE_BOOL(Character, EnableTestBuff, false, "Allow the use of /testbuff") RULE_CATEGORY_END() RULE_CATEGORY(Mercs) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index ea7ad932a..92b3080c7 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -14059,6 +14059,10 @@ void Client::Handle_OP_Taunt(const EQApplicationPacket *app) void Client::Handle_OP_TestBuff(const EQApplicationPacket *app) { + if (!RuleB(Character, EnableTestBuff)) { + return; + } + parse->EventPlayer(EVENT_TEST_BUFF, this, "", 0); return; } diff --git a/zone/embparser.cpp b/zone/embparser.cpp index db37a58e7..22e4b3234 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -119,7 +119,8 @@ const char *QuestEventSubroutines[_LargestEventID] = { "EVENT_DEATH_ZONE", "EVENT_USE_SKILL", "EVENT_COMBINE_VALIDATE", - "EVENT_BOT_COMMAND" + "EVENT_BOT_COMMAND", + "EVENT_TEST_BUFF" }; PerlembParser::PerlembParser() : perl(nullptr) diff --git a/zone/event_codes.h b/zone/event_codes.h index 110101cf9..5765c56ff 100644 --- a/zone/event_codes.h +++ b/zone/event_codes.h @@ -88,6 +88,7 @@ typedef enum { EVENT_USE_SKILL, EVENT_COMBINE_VALIDATE, EVENT_BOT_COMMAND, + EVENT_TEST_BUFF, _LargestEventID } QuestEventID; diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 7b6c5bcdb..f83fd76d9 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -3203,7 +3203,8 @@ luabind::scope lua_register_events() { luabind::value("tick", static_cast(EVENT_TICK)), luabind::value("spawn_zone", static_cast(EVENT_SPAWN_ZONE)), luabind::value("death_zone", static_cast(EVENT_DEATH_ZONE)), - luabind::value("use_skill", static_cast(EVENT_USE_SKILL)) + luabind::value("use_skill", static_cast(EVENT_USE_SKILL)), + luabind::value("test_buff", static_cast(EVENT_TEST_BUFF)) ]; } diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index 1fc0fc47a..1342c719e 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -130,7 +130,8 @@ const char *LuaEvents[_LargestEventID] = { "event_death_zone", "event_use_skill", "event_combine_validate", - "event_bot_command" + "event_bot_command", + "event_test_buff" }; extern Zone *zone; @@ -213,6 +214,7 @@ LuaParser::LuaParser() { PlayerArgumentDispatch[EVENT_RESPAWN] = handle_player_respawn; PlayerArgumentDispatch[EVENT_UNHANDLED_OPCODE] = handle_player_packet; PlayerArgumentDispatch[EVENT_USE_SKILL] = handle_player_use_skill; + PlayerArgumentDispatch[EVENT_TEST_BUFF] = handle_test_buff; PlayerArgumentDispatch[EVENT_COMBINE_VALIDATE] = handle_player_combine_validate; PlayerArgumentDispatch[EVENT_BOT_COMMAND] = handle_player_bot_command; diff --git a/zone/lua_parser_events.cpp b/zone/lua_parser_events.cpp index 9a1a2c67f..73acd9232 100644 --- a/zone/lua_parser_events.cpp +++ b/zone/lua_parser_events.cpp @@ -514,6 +514,9 @@ void handle_player_use_skill(QuestInterface *parse, lua_State* L, Client* client lua_setfield(L, -2, "skill_level"); } +void handle_test_buff(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector* extra_pointers) { +} + void handle_player_combine_validate(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector* extra_pointers) { Seperator sep(data.c_str()); diff --git a/zone/lua_parser_events.h b/zone/lua_parser_events.h index 0f826a421..47e7883ae 100644 --- a/zone/lua_parser_events.h +++ b/zone/lua_parser_events.h @@ -97,6 +97,8 @@ void handle_player_null(QuestInterface *parse, lua_State* L, Client* client, std std::vector *extra_pointers); void handle_player_use_skill(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector *extra_pointers); +void handle_test_buff(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, + std::vector* extra_pointers); void handle_player_combine_validate(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector* extra_pointers); void handle_player_bot_command(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, From 966067ae74878f3d1e460725a99a42a470913ca0 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 16 Jun 2021 11:05:13 -0400 Subject: [PATCH 094/624] [Typo] basediff not basedeiff (#1408) --- zone/mob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index 879cc49f9..fafcce2bc 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -5590,7 +5590,7 @@ int32 Mob::GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot) else if (id == "Activated") {return spells[spell_id].Activated;} else if (id == "resisttype") {return spells[spell_id].resisttype;} else if (id == "targettype") {return spells[spell_id].targettype;} - else if (id == "basedeiff") {return spells[spell_id].basediff;} + else if (id == "basediff") {return spells[spell_id].basediff;} else if (id == "skill") {return spells[spell_id].skill;} else if (id == "zonetype") {return spells[spell_id].zonetype;} else if (id == "EnvironmentType") {return spells[spell_id].EnvironmentType;} From 743b61ae1319a780798ea00846c6fb2ed047fea5 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 16 Jun 2021 11:05:30 -0400 Subject: [PATCH 095/624] [Typo] IsDisciplineBuff not IsDisciplineBuf (#1410) --- zone/mob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index fafcce2bc..ecae310b9 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -5612,7 +5612,7 @@ int32 Mob::GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot) else if (id == "bonushate") {return spells[spell_id].bonushate; } else if (id == "EndurCost") {return spells[spell_id].EndurCost; } else if (id == "EndurTimerIndex") {return spells[spell_id].EndurTimerIndex; } - else if (id == "IsDisciplineBuf") {return spells[spell_id].IsDisciplineBuff; } + else if (id == "IsDisciplineBuff") {return spells[spell_id].IsDisciplineBuff; } else if (id == "HateAdded") {return spells[spell_id].HateAdded; } else if (id == "EndurUpkeep") {return spells[spell_id].EndurUpkeep; } else if (id == "numhitstype") {return spells[spell_id].numhitstype; } From 65150b05810b67048729ccf8dcf45301fae2135e Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 16 Jun 2021 11:05:44 -0400 Subject: [PATCH 096/624] [Typo] dot_stacking_exempt not dot_stacking_exemp (#1409) --- zone/mob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index ecae310b9..17e71c1c6 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -5601,7 +5601,7 @@ int32 Mob::GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot) //else if (id == "spellanim") {stat = spells[spell_id].spellanim; } - Not implemented else if (id == "uninterruptable") {return spells[spell_id].uninterruptable; } else if (id == "ResistDiff") {return spells[spell_id].ResistDiff; } - else if (id == "dot_stacking_exemp") {return spells[spell_id].dot_stacking_exempt; } + else if (id == "dot_stacking_exempt") {return spells[spell_id].dot_stacking_exempt; } else if (id == "RecourseLink") {return spells[spell_id].RecourseLink; } else if (id == "no_partial_resist") {return spells[spell_id].no_partial_resist; } else if (id == "short_buff_box") {return spells[spell_id].short_buff_box; } From 797eaf308d0477b5a4588b9887263770a2727a4b Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 16 Jun 2021 11:06:30 -0400 Subject: [PATCH 097/624] [Quest API] Add AddNimbusEffect(effect_id) to Perl. (#1412) - Add $client->AddNimbusEffect(effect_id) to Perl. --- zone/perl_mob.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index 28830ef2b..ad16544bb 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -6257,6 +6257,20 @@ XS(XS_Mob_GetLastName) { XSRETURN(1); } +XS(XS_Mob_AddNimbusEffect); +XS(XS_Mob_AddNimbusEffect) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::AddNimbusEffect(THIS, int effect_id)"); // @categories Script Utility + { + Mob* THIS; + int effect_id = (int) SvIV(ST(1)); + VALIDATE_THIS_IS_MOB; + THIS->AddNimbusEffect(effect_id); + } + XSRETURN_EMPTY; +} + #ifdef BOTS XS(XS_Mob_CastToBot); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_CastToBot) @@ -6625,6 +6639,7 @@ XS(boot_Mob) { newXSproto(strcpy(buf, "GetHateClosest"), XS_Mob_GetHateClosest, file, "$"); newXSproto(strcpy(buf, "GetHateListByDistance"), XS_Mob_GetHateListByDistance, file, "$;$"); newXSproto(strcpy(buf, "GetLastName"), XS_Mob_GetLastName, file, "$"); + newXSproto(strcpy(buf, "AddNimbusEffect"), XS_Mob_AddNimbusEffect, file, "$$"); #ifdef BOTS newXSproto(strcpy(buf, "CastToBot"), XS_Mob_CastToBot, file, "$"); #endif From a98e3b758a2d442222c3dd1d9e3a4a245416447e Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 16 Jun 2021 11:11:38 -0400 Subject: [PATCH 098/624] [Quest API] Add CountItem(item_id) and RemoveItem(item_id, quantity) to Perl/Lua. (#1416) - Add $client->CountItem(item_id) to Perl. - Add $client->RemoveItem(item_id, quantity) to Perl. - Add client:CountItem(item_id) to Lua. - Add client:RemoveItem(item_id, quantity) to Lua. --- zone/client.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++ zone/client.h | 2 ++ zone/lua_client.cpp | 20 +++++++++++++- zone/lua_client.h | 3 +++ zone/perl_client.cpp | 39 +++++++++++++++++++++++++++ zone/questmgr.cpp | 57 ++------------------------------------- 6 files changed, 129 insertions(+), 56 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 614917da9..9fbd42b98 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -10129,3 +10129,67 @@ void Client::SetAFK(uint8 afk_flag) { entity_list.QueueClients(this, outapp); safe_delete(outapp); } + +int Client::CountItem(uint32 item_id) +{ + int quantity = 0; + EQ::ItemInstance *item = nullptr; + static const int16 slots[][2] = { + { EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END }, + { EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END }, + { EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END}, + { EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END }, + { EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END }, + { EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END }, + { EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END }, + }; + const size_t size = sizeof(slots) / sizeof(slots[0]); + for (int slot_index = 0; slot_index < size; ++slot_index) { + for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { + item = GetInv().GetItem(slot_id); + if (item && item->GetID() == item_id) { + quantity += (item->IsStackable() ? item->GetCharges() : 1); + } + } + } + + return quantity; +} + +void Client::RemoveItem(uint32 item_id, uint32 quantity) +{ + EQ::ItemInstance *item = nullptr; + static const int16 slots[][2] = { + { EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END }, + { EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END }, + { EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END}, + { EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END }, + { EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END }, + { EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END }, + { EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END }, + }; + int removed_count = 0; + const size_t size = sizeof(slots) / sizeof(slots[0]); + for (int slot_index = 0; slot_index < size; ++slot_index) { + for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { + if (removed_count == quantity) { + break; + } + + item = GetInv().GetItem(slot_id); + if (item && item->GetID() == item_id) { + int stack_size = item->IsStackable() ? item->GetCharges() : 1; + if ((removed_count + stack_size) <= quantity) { + removed_count += stack_size; + DeleteItemInInventory(slot_id, stack_size, true); + } else { + int amount_left = (quantity - removed_count); + if (amount_left > 0 && stack_size >= amount_left) { + removed_count += amount_left; + DeleteItemInInventory(slot_id, amount_left, true); + } + } + } + } + } +} diff --git a/zone/client.h b/zone/client.h index beaf191b4..9408ff1c7 100644 --- a/zone/client.h +++ b/zone/client.h @@ -907,6 +907,8 @@ public: bool PushItemOnCursor(const EQ::ItemInstance& inst, bool client_update = false); void SendCursorBuffer(); void DeleteItemInInventory(int16 slot_id, int8 quantity = 0, bool client_update = false, bool update_db = true); + int CountItem(uint32 item_id); + void RemoveItem(uint32 item_id, uint32 quantity = 1); bool SwapItem(MoveItem_Struct* move_in); void SwapItemResync(MoveItem_Struct* move_slots); void QSSwapItemAuditor(MoveItem_Struct* move_in, bool postaction_call = false); diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 35ac56006..c08d27fad 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -2138,6 +2138,21 @@ void Lua_Client::ResetAllDisciplineTimers() { self->ResetAllDisciplineTimers(); } +int Lua_Client::CountItem(uint32 item_id) { + Lua_Safe_Call_Int(); + return self->CountItem(item_id); +} + +void Lua_Client::RemoveItem(uint32 item_id) { + Lua_Safe_Call_Void(); + return self->RemoveItem(item_id); +} + +void Lua_Client::RemoveItem(uint32 item_id, uint32 quantity) { + Lua_Safe_Call_Void(); + return self->RemoveItem(item_id, quantity); +} + luabind::scope lua_register_client() { return luabind::class_("Client") .def(luabind::constructor<>()) @@ -2501,7 +2516,10 @@ luabind::scope lua_register_client() { .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32,uint32,uint32))& Lua_Client::Popup) .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32,uint32,uint32,const char*,const char*))& Lua_Client::Popup) .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32,uint32,uint32,const char*,const char*,uint32))&Lua_Client::Popup) - .def("ResetAllDisciplineTimers", (void(Lua_Client::*)(void))&Lua_Client::ResetAllDisciplineTimers); + .def("ResetAllDisciplineTimers", (void(Lua_Client::*)(void))&Lua_Client::ResetAllDisciplineTimers) + .def("CountItem", (int(Lua_Client::*)(uint32))&Lua_Client::CountItem) + .def("RemoveItem", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveItem) + .def("RemoveItem", (void(Lua_Client::*)(uint32,uint32))&Lua_Client::RemoveItem); } luabind::scope lua_register_inventory_where() { diff --git a/zone/lua_client.h b/zone/lua_client.h index f13b69daf..d95af104a 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -366,6 +366,9 @@ public: void Popup(const char* title, const char* text, uint32 popup_id, uint32 negative_id, uint32 button_type, uint32 duration); void Popup(const char* title, const char* text, uint32 popup_id, uint32 negative_id, uint32 button_type, uint32 duration, const char* button_name_one, const char* button_name_two); void Popup(const char* title, const char* text, uint32 popup_id, uint32 negative_id, uint32 button_type, uint32 duration, const char* button_name_one, const char* button_name_two, uint32 sound_controls); + int CountItem(uint32 item_id); + void RemoveItem(uint32 item_id); + void RemoveItem(uint32 item_id, uint32 quantity); void SetPrimaryWeaponOrnamentation(uint32 model_id); void SetSecondaryWeaponOrnamentation(uint32 model_id); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 034611fb4..bcd118fc1 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -5345,6 +5345,43 @@ XS(XS_Client_ResetAllDisciplineTimers) { XSRETURN_EMPTY; } +XS(XS_Client_CountItem); +XS(XS_Client_CountItem) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::CountItem(THIS, uint32 item_id)"); + { + Client* THIS; + int item_count = 0; + uint32 item_id = (uint32) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_CLIENT; + item_count = THIS->CountItem(item_id); + XSprePUSH; + PUSHu((UV) item_count); + } + XSRETURN(1); +} + +XS(XS_Client_RemoveItem); +XS(XS_Client_RemoveItem) { + dXSARGS; + if (items != 2 && items != 3) + Perl_croak(aTHX_ "Usage: Client::RemoveItem(THIS, uint32 item_id, [uint32 quantity = 1])"); // @categories Spells and Disciplines + { + Client *THIS; + uint32 item_id = (uint32) SvUV(ST(1)); + uint32 quantity = 1; + VALIDATE_THIS_IS_CLIENT; + if (items == 3) { + quantity = (uint32) SvUV(ST(2)); + } + + THIS->RemoveItem(item_id, quantity); + } + XSRETURN_EMPTY; +} + #ifdef __cplusplus extern "C" #endif @@ -5395,6 +5432,7 @@ XS(boot_Client) { newXSproto(strcpy(buf, "ClearZoneFlag"), XS_Client_ClearZoneFlag, file, "$$"); newXSproto(strcpy(buf, "CreateExpedition"), XS_Client_CreateExpedition, file, "$$$$$$$;$"); newXSproto(strcpy(buf, "Connected"), XS_Client_Connected, file, "$"); + newXSproto(strcpy(buf, "CountItem"), XS_Client_CountItem, file, "$$"); newXSproto(strcpy(buf, "DecreaseByID"), XS_Client_DecreaseByID, file, "$$$"); newXSproto(strcpy(buf, "DeleteItemInInventory"), XS_Client_DeleteItemInInventory, file, "$$;$$"); newXSproto(strcpy(buf, "Disconnect"), XS_Client_Disconnect, file, "$"); @@ -5563,6 +5601,7 @@ XS(boot_Client) { newXSproto(strcpy(buf, "RefundAA"), XS_Client_RefundAA, file, "$$"); newXSproto(strcpy(buf, "RemoveAllExpeditionLockouts"), XS_Client_RemoveAllExpeditionLockouts, file, "$;$"); newXSproto(strcpy(buf, "RemoveExpeditionLockout"), XS_Client_RemoveExpeditionLockout, file, "$$$"); + newXSproto(strcpy(buf, "RemoveItem"), XS_Client_RemoveItem, file, "$$;$"); newXSproto(strcpy(buf, "RemoveNoRent"), XS_Client_RemoveNoRent, file, "$"); newXSproto(strcpy(buf, "ResetAA"), XS_Client_ResetAA, file, "$"); newXSproto(strcpy(buf, "ResetAllDisciplineTimers"), XS_Client_ResetAllDisciplineTimers, file, "$"); diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 0ec4a4309..8848f7eda 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -2645,65 +2645,12 @@ int QuestManager::collectitems(uint32 item_id, bool remove) int QuestManager::countitem(uint32 item_id) { QuestManagerCurrentQuestVars(); - int quantity = 0; - EQ::ItemInstance *item = nullptr; - static const int16 slots[][2] = { - { EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END }, - { EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END }, - { EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END}, - { EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END }, - { EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END }, - { EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END }, - { EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END }, - }; - const size_t size = sizeof(slots) / sizeof(slots[0]); - for (int slot_index = 0; slot_index < size; ++slot_index) { - for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { - item = initiator->GetInv().GetItem(slot_id); - if (item && item->GetID() == item_id) { - quantity += item->IsStackable() ? item->GetCharges() : 1; - } - } - } - - return quantity; + return initiator->CountItem(item_id); } void QuestManager::removeitem(uint32 item_id, uint32 quantity) { QuestManagerCurrentQuestVars(); - EQ::ItemInstance *item = nullptr; - static const int16 slots[][2] = { - { EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END }, - { EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END }, - { EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END}, - { EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END }, - { EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END }, - { EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END }, - { EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END }, - }; - int removed_count = 0; - const size_t size = sizeof(slots) / sizeof(slots[0]); - for (int slot_index = 0; slot_index < size; ++slot_index) { - for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { - if (removed_count == quantity) - break; - - item = initiator->GetInv().GetItem(slot_id); - if (item && item->GetID() == item_id) { - int stack_size = item->IsStackable() ? item->GetCharges() : 1; - if ((removed_count + stack_size) <= quantity) { - removed_count += stack_size; - initiator->DeleteItemInInventory(slot_id, stack_size, true); - } else { - int amount_left = (quantity - removed_count); - if (amount_left > 0 && stack_size >= amount_left) { - removed_count += amount_left; - initiator->DeleteItemInInventory(slot_id, amount_left, true); - } - } - } - } - } + initiator->RemoveItem(item_id, quantity); } void QuestManager::UpdateSpawnTimer(uint32 id, uint32 newTime) From d34afb6f30c36127dfd5b55ecb5c8678166ad84b Mon Sep 17 00:00:00 2001 From: splose Date: Wed, 16 Jun 2021 11:23:14 -0400 Subject: [PATCH 099/624] Fix for unknown spell effect message with spell effect 459 (DamageModifierV2) (#1419) --- zone/spell_effects.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index ceffa5f44..bd6ef946a 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2847,6 +2847,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_StunResist: case SE_MinDamageModifier: case SE_DamageModifier: + case SE_DamageModifier2: case SE_HitChance: case SE_MeleeSkillCheck: case SE_HundredHands: From f1d1731fc7ae896da3e269ba846e595a73e15047 Mon Sep 17 00:00:00 2001 From: splose Date: Wed, 16 Jun 2021 11:24:07 -0400 Subject: [PATCH 100/624] [Command] Add #npcedit rarespawn (#1418) * add #npcedit rarespawn * minor fix --- zone/command.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/zone/command.cpp b/zone/command.cpp index 40c7b6d82..eb0d9da45 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -9059,6 +9059,7 @@ void command_npcedit(Client *c, const Seperator *sep) c->Message(Chat::White, "#npcedit slow_mitigation - Set an NPC's slow mitigation"); c->Message(Chat::White, "#npcedit flymode - Set an NPC's flymode [0 = ground, 1 = flying, 2 = levitate, 3 = water, 4 = floating]"); c->Message(Chat::White, "#npcedit raidtarget - Set an NPCs raid_target field"); + c->Message(Chat::White, "#npcedit rarespawn - Set an NPCs rare flag"); c->Message(Chat::White, "#npcedit respawntime - Set an NPCs respawn timer in seconds"); } @@ -9667,6 +9668,15 @@ void command_npcedit(Client *c, const Seperator *sep) } } + if (strcasecmp(sep->arg[1], "rarespawn") == 0) { + if (sep->arg[2][0] && sep->IsNumber(sep->arg[2]) && atoi(sep->arg[2]) >= 0) { + c->Message(Chat::Yellow, "NPCID %u is %s as a rare spawn.", npcTypeID, atoi(sep->arg[2]) == 0 ? "no longer designated" : "now designated"); + std::string query = StringFormat("UPDATE npc_types SET rare_spawn = %i WHERE id = %i", atoi(sep->arg[2]), npcTypeID); + content_db.QueryDatabase(query); + return; + } + } + if (strcasecmp(sep->arg[1], "respawntime") == 0) { if (sep->arg[2][0] && sep->IsNumber(sep->arg[2]) && atoi(sep->arg[2]) > 0) { c->Message(Chat::Yellow, "NPCID %u (spawngroup %i) respawn time set to %i.", npcTypeID, c->GetTarget()->CastToNPC()->GetSpawnGroupId(), atoi(sep->arg[2])); From ed6e53be540ba05c33d43bebf3de05c9a45e9657 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 16 Jun 2021 11:42:06 -0400 Subject: [PATCH 101/624] [Quest API] Add getinventoryslotname(slot_id) to Perl/Lua. (#1406) - Add quest::getinventoryslotname(slot_id) to Perl. - Add eq.get_inventory_slot_name(slot_id) to Lua. Co-authored-by: Chris Miles --- zone/embparser_api.cpp | 16 ++++++++++++++++ zone/lua_general.cpp | 7 +++++++ zone/questmgr.cpp | 4 ++++ zone/questmgr.h | 1 + 4 files changed, 28 insertions(+) diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index d25b346d7..94af4cdee 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -6774,6 +6774,21 @@ XS(XS__getcleannpcnamebyid) { XSRETURN(1); } +XS(XS__getinventoryslotname); +XS(XS__getinventoryslotname) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::getinventoryslotname(int16 slot_id)"); + + dXSTARG; + int16 slot_id = (int16) SvIV(ST(0)); + auto slot_name = quest_manager.getinventoryslotname(slot_id); + sv_setpv(TARG, slot_name.c_str()); + XSprePUSH; + PUSHTARG; + XSRETURN(1); +} + XS(XS__rename); XS(XS__rename) { dXSARGS; @@ -7029,6 +7044,7 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "getguildnamebyid"), XS__getguildnamebyid, file); newXS(strcpy(buf, "getguildidbycharid"), XS__getguildidbycharid, file); newXS(strcpy(buf, "getgroupidbycharid"), XS__getgroupidbycharid, file); + newXS(strcpy(buf, "getinventoryslotname"), XS__getinventoryslotname, file); newXS(strcpy(buf, "getraididbycharid"), XS__getraididbycharid, file); newXS(strcpy(buf, "getracename"), XS__getracename, file); newXS(strcpy(buf, "getspellname"), XS__getspellname, file); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index f83fd76d9..896bb4051 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -2459,6 +2459,11 @@ std::string lua_get_clean_npc_name_by_id(uint32 npc_id) { return quest_manager.getcleannpcnamebyid(npc_id); } + +std::string lua_get_inventory_slot_name(int16 slot_id) { + return quest_manager.getinventoryslotname(slot_id); +} + void lua_rename(std::string name) { quest_manager.rename(name); } @@ -3028,7 +3033,9 @@ luabind::scope lua_register_general() { luabind::def("cross_zone_add_ldon_loss_by_expedition_id", &lua_cross_zone_add_ldon_loss_by_expedition_id), luabind::def("cross_zone_add_ldon_points_by_expedition_id", &lua_cross_zone_add_ldon_points_by_expedition_id), luabind::def("cross_zone_add_ldon_win_by_expedition_id", &lua_cross_zone_add_ldon_win_by_expedition_id), + luabind::def("get_inventory_slot_name", &lua_get_inventory_slot_name), luabind::def("rename", &lua_rename), + /** * Expansions */ diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 8848f7eda..83ed76ce7 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -4624,3 +4624,7 @@ void QuestManager::CrossZoneLDoNUpdate(uint8 type, uint8 subtype, int identifier worldserver.SendPacket(pack); safe_delete(pack); } + +std::string QuestManager::getinventoryslotname(int16 slot_id) { + return EQ::invslot::GetInvPossessionsSlotName(slot_id); +} \ No newline at end of file diff --git a/zone/questmgr.h b/zone/questmgr.h index 5b3a21629..bf615835b 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -381,6 +381,7 @@ public: double GetEXPModifierByCharID(uint32 character_id, uint32 zone_id) const; void SetAAEXPModifierByCharID(uint32 character_id, uint32 zone_id, double aa_modifier); void SetEXPModifierByCharID(uint32 character_id, uint32 zone_id, double exp_modifier); + std::string getinventoryslotname(int16 slot_id); Client *GetInitiator() const; NPC *GetNPC() const; From f8a72296e633a2f1f2c3ddf662f1985b782cf324 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 16 Jun 2021 11:45:38 -0400 Subject: [PATCH 102/624] [Quest API] Add getdeityname(deity_id) to Perl/Lua. (#1404) - Add quest::getdeityname(deity_id) to Perl. - Add eq.get_deity_name(deity_id) to Lua. Co-authored-by: Chris Miles --- zone/embparser_api.cpp | 14 ++++++++++++++ zone/lua_general.cpp | 7 ++++++- zone/questmgr.cpp | 8 +++++++- zone/questmgr.h | 1 + 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 94af4cdee..1ab1f05df 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -6774,6 +6774,19 @@ XS(XS__getcleannpcnamebyid) { XSRETURN(1); } + +XS(XS__getdeityname); +XS(XS__getdeityname) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::getdeityname(uint32 deity_id)"); + + dXSTARG; + uint32 deity_id = (uint32) SvUV(ST(0)); + auto deity_name = quest_manager.getdeityname(deity_id); + sv_setpv(TARG, deity_name.c_str()); +} + XS(XS__getinventoryslotname); XS(XS__getinventoryslotname) { dXSARGS; @@ -7041,6 +7054,7 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "get_spawn_condition"), XS__get_spawn_condition, file); newXS(strcpy(buf, "getcharnamebyid"), XS__getcharnamebyid, file); newXS(strcpy(buf, "getcurrencyitemid"), XS__getcurrencyitemid, file); + newXS(strcpy(buf, "getdeityname"), XS__getdeityname, file); newXS(strcpy(buf, "getguildnamebyid"), XS__getguildnamebyid, file); newXS(strcpy(buf, "getguildidbycharid"), XS__getguildidbycharid, file); newXS(strcpy(buf, "getgroupidbycharid"), XS__getgroupidbycharid, file); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 896bb4051..9bfd0b9fb 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -2460,6 +2460,10 @@ std::string lua_get_clean_npc_name_by_id(uint32 npc_id) { } +std::string lua_get_deity_name(uint32 deity_id) { + return quest_manager.getdeityname(deity_id); +} + std::string lua_get_inventory_slot_name(int16 slot_id) { return quest_manager.getinventoryslotname(slot_id); } @@ -3033,9 +3037,10 @@ luabind::scope lua_register_general() { luabind::def("cross_zone_add_ldon_loss_by_expedition_id", &lua_cross_zone_add_ldon_loss_by_expedition_id), luabind::def("cross_zone_add_ldon_points_by_expedition_id", &lua_cross_zone_add_ldon_points_by_expedition_id), luabind::def("cross_zone_add_ldon_win_by_expedition_id", &lua_cross_zone_add_ldon_win_by_expedition_id), + luabind::def("get_deity_name", &lua_get_deity_name), luabind::def("get_inventory_slot_name", &lua_get_inventory_slot_name), luabind::def("rename", &lua_rename), - + /** * Expansions */ diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 83ed76ce7..b796834bc 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -4601,6 +4601,7 @@ std::string QuestManager::gethexcolorcode(std::string color_name) { double QuestManager::GetAAEXPModifierByCharID(uint32 character_id, uint32 zone_id) const { return database.GetAAEXPModifier(character_id, zone_id); } + double QuestManager::GetEXPModifierByCharID(uint32 character_id, uint32 zone_id) const { return database.GetEXPModifier(character_id, zone_id); } @@ -4625,6 +4626,11 @@ void QuestManager::CrossZoneLDoNUpdate(uint8 type, uint8 subtype, int identifier safe_delete(pack); } +std::string QuestManager::getdeityname(uint32 deity_id) { + return EQ::deity::DeityName(static_cast(deity_id)); +} + std::string QuestManager::getinventoryslotname(int16 slot_id) { return EQ::invslot::GetInvPossessionsSlotName(slot_id); -} \ No newline at end of file +} + diff --git a/zone/questmgr.h b/zone/questmgr.h index bf615835b..f58337cbf 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -381,6 +381,7 @@ public: double GetEXPModifierByCharID(uint32 character_id, uint32 zone_id) const; void SetAAEXPModifierByCharID(uint32 character_id, uint32 zone_id, double aa_modifier); void SetEXPModifierByCharID(uint32 character_id, uint32 zone_id, double exp_modifier); + std::string getdeityname(uint32 deity_id); std::string getinventoryslotname(int16 slot_id); Client *GetInitiator() const; From 68fe95786e554dd7a66802f12eab2fc4d727db8b Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 16 Jun 2021 11:49:02 -0400 Subject: [PATCH 103/624] [Quest API] Add getgendername(gender_id) to Perl/Lua. (#1405) - Add quest::getgendername(gender_id) to Perl. - Add eq.get_gender_name(gender_id) to Lua. Co-authored-by: Chris Miles --- zone/embparser_api.cpp | 12 ++++++++++++ zone/lua_general.cpp | 4 ++++ zone/questmgr.cpp | 13 +++++++++++++ zone/questmgr.h | 1 + 4 files changed, 30 insertions(+) diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 1ab1f05df..cc992199f 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -6774,6 +6774,17 @@ XS(XS__getcleannpcnamebyid) { XSRETURN(1); } +XS(XS__getgendername); +XS(XS__getgendername) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::getgendername(uint32 gender_id)"); + + dXSTARG; + uint32 gender_id = (uint32) SvUV(ST(0)); + auto gender_name = quest_manager.getgendername(gender_id); + sv_setpv(TARG, gender_name.c_str()); +} XS(XS__getdeityname); XS(XS__getdeityname) { @@ -7054,6 +7065,7 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "get_spawn_condition"), XS__get_spawn_condition, file); newXS(strcpy(buf, "getcharnamebyid"), XS__getcharnamebyid, file); newXS(strcpy(buf, "getcurrencyitemid"), XS__getcurrencyitemid, file); + newXS(strcpy(buf, "getgendername"), XS__getgendername, file); newXS(strcpy(buf, "getdeityname"), XS__getdeityname, file); newXS(strcpy(buf, "getguildnamebyid"), XS__getguildnamebyid, file); newXS(strcpy(buf, "getguildidbycharid"), XS__getguildidbycharid, file); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 9bfd0b9fb..15e89e2d3 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -2459,6 +2459,9 @@ std::string lua_get_clean_npc_name_by_id(uint32 npc_id) { return quest_manager.getcleannpcnamebyid(npc_id); } +std::string lua_get_gender_name(uint32 gender_id) { + return quest_manager.getgendername(gender_id); +} std::string lua_get_deity_name(uint32 deity_id) { return quest_manager.getdeityname(deity_id); @@ -3037,6 +3040,7 @@ luabind::scope lua_register_general() { luabind::def("cross_zone_add_ldon_loss_by_expedition_id", &lua_cross_zone_add_ldon_loss_by_expedition_id), luabind::def("cross_zone_add_ldon_points_by_expedition_id", &lua_cross_zone_add_ldon_points_by_expedition_id), luabind::def("cross_zone_add_ldon_win_by_expedition_id", &lua_cross_zone_add_ldon_win_by_expedition_id), + luabind::def("get_gender_name", &lua_get_gender_name), luabind::def("get_deity_name", &lua_get_deity_name), luabind::def("get_inventory_slot_name", &lua_get_inventory_slot_name), luabind::def("rename", &lua_rename), diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index b796834bc..b0297a3c9 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -4626,6 +4626,18 @@ void QuestManager::CrossZoneLDoNUpdate(uint8 type, uint8 subtype, int identifier safe_delete(pack); } +std::string QuestManager::getgendername(uint32 gender_id) { + auto gender_name = "Unknown"; + if (gender_id == MALE) { + gender_name = "Male"; + } else if (gender_id == FEMALE) { + gender_name = "Female"; + } else if (gender_id == NEUTER) { + gender_name = "Neuter"; + } + return gender_name; +} + std::string QuestManager::getdeityname(uint32 deity_id) { return EQ::deity::DeityName(static_cast(deity_id)); } @@ -4634,3 +4646,4 @@ std::string QuestManager::getinventoryslotname(int16 slot_id) { return EQ::invslot::GetInvPossessionsSlotName(slot_id); } + diff --git a/zone/questmgr.h b/zone/questmgr.h index f58337cbf..094e2ceef 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -381,6 +381,7 @@ public: double GetEXPModifierByCharID(uint32 character_id, uint32 zone_id) const; void SetAAEXPModifierByCharID(uint32 character_id, uint32 zone_id, double aa_modifier); void SetEXPModifierByCharID(uint32 character_id, uint32 zone_id, double exp_modifier); + std::string getgendername(uint32 gender_id); std::string getdeityname(uint32 deity_id); std::string getinventoryslotname(int16 slot_id); From 40db13d33ea77ed9f15d7dc441a48d013dc9427b Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 17 Jun 2021 12:39:16 -0400 Subject: [PATCH 104/624] [Quest API] Add get_data_remaining(bucket_name) to Perl/Lua. (#1421) - Add quest::get_data_remaining(bucket_name) to Perl. - Add eq.get_data_remaining(bucket_name) to Lua. --- zone/embparser_api.cpp | 16 ++++++++++++++++ zone/lua_general.cpp | 5 +++++ 2 files changed, 21 insertions(+) diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index cc992199f..9792e72a2 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -6824,6 +6824,21 @@ XS(XS__rename) { XSRETURN_EMPTY; } +XS(XS__get_data_remaining); +XS(XS__get_data_remaining) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::get_data_remaining(string bucket_name)"); + + dXSTARG; + std::string bucket_name = (std::string) SvPV_nolen(ST(0)); + + sv_setpv(TARG, DataBucket::GetDataRemaining(bucket_name).c_str()); + XSprePUSH; + PUSHTARG; + XSRETURN(1); +} + /* This is the callback perl will look for to setup the quest package's XSUBs @@ -6878,6 +6893,7 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "get_rule"), XS__get_rule, file); newXS(strcpy(buf, "get_data"), XS__get_data, file); newXS(strcpy(buf, "get_data_expires"), XS__get_data_expires, file); + newXS(strcpy(buf, "get_data_remaining"), XS__get_data_remaining, file); newXS(strcpy(buf, "set_data"), XS__set_data, file); newXS(strcpy(buf, "delete_data"), XS__delete_data, file); newXS(strcpy(buf, "IsBeneficialSpell"), XS__IsBeneficialSpell, file); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 15e89e2d3..29c3ee0d2 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -2475,6 +2475,10 @@ void lua_rename(std::string name) { quest_manager.rename(name); } +std::string lua_get_data_remaining(std::string bucket_name) { + return DataBucket::GetDataRemaining(bucket_name); +} + #define LuaCreateNPCParse(name, c_type, default_value) do { \ cur = table[#name]; \ if(luabind::type(cur) != LUA_TNIL) { \ @@ -3044,6 +3048,7 @@ luabind::scope lua_register_general() { luabind::def("get_deity_name", &lua_get_deity_name), luabind::def("get_inventory_slot_name", &lua_get_inventory_slot_name), luabind::def("rename", &lua_rename), + luabind::def("get_data_remaining", &lua_get_data_remaining), /** * Expansions From 82ad8b5fe207652f2e1b0170e0e9bd77c90f5b9a Mon Sep 17 00:00:00 2001 From: splose Date: Thu, 17 Jun 2021 12:39:25 -0400 Subject: [PATCH 105/624] Adds ability to use the ~~old~~ proper 2HB animation and also allows you to do it on a per-zone basis since its rule-based. (#1420) --- common/ruletypes.h | 7 ++++--- zone/attack.cpp | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index d045b44c4..409769bea 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -450,9 +450,9 @@ RULE_REAL(Combat, AvgDefProcsPerMinute, 2.0, "Average defense procs per minute") RULE_REAL(Combat, DefProcPerMinAgiContrib, 0.075, "How much agility contributes to defensive proc rate") RULE_INT(Combat, SpecialAttackACBonus, 15, "Percent amount of damage per AC gained for certain special attacks (damage = AC*SpecialAttackACBonus/100)") RULE_INT(Combat, NPCFlurryChance, 20, "Chance for NPC to flurry") -RULE_BOOL (Combat,TauntOverLevel, 1, "Allows you to taunt NPC's over warriors level") -RULE_REAL (Combat,TauntSkillFalloff, 0.33, "For every taunt skill point that's not maxed you lose this percentage chance to taunt") -RULE_BOOL (Combat,EXPFromDmgShield, false, "Determine if damage from a damage shield counts for experience gain") +RULE_BOOL(Combat, TauntOverLevel, 1, "Allows you to taunt NPC's over warriors level") +RULE_REAL(Combat, TauntSkillFalloff, 0.33, "For every taunt skill point that's not maxed you lose this percentage chance to taunt") +RULE_BOOL(Combat, EXPFromDmgShield, false, "Determine if damage from a damage shield counts for experience gain") RULE_INT(Combat, MonkACBonusWeight, 15, "Usually, a monk under this weight threshold gets an AC bonus") RULE_INT(Combat, QuiverHasteCap, 1000, "Quiver haste cap 1000 on live for a while, currently 700 on live") RULE_INT(Combat, BerserkerFrenzyStart, 35, "Percentage Health Points below which Warrior and Berserker start frenzy") @@ -474,6 +474,7 @@ RULE_BOOL(Combat, UseNPCDamageClassLevelMods, true, "Uses GetClassLevelDamageMod RULE_BOOL(Combat, UseExtendedPoisonProcs, false, "Allow old school poisons to last until characrer zones, at a lower proc rate") RULE_BOOL(Combat, EnableSneakPull, false, "Enable implementation of Sneak Pull") RULE_INT(Combat, SneakPullAssistRange, 400, "Modified range of assist for sneak pull") +RULE_BOOL(Combat, Classic2HBAnimation, false, "2HB will use the 2 hand piercing animation instead of the overhead slashing animation") RULE_CATEGORY_END() RULE_CATEGORY(NPC) diff --git a/zone/attack.cpp b/zone/attack.cpp index fbf4af53e..fdcf553d9 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -87,7 +87,7 @@ EQ::skills::SkillType Mob::AttackAnimation(int Hand, const EQ::ItemInstance* wea break; case EQ::item::ItemType2HBlunt: // 2H Blunt skillinuse = EQ::skills::Skill2HBlunt; - type = anim2HSlashing; //anim2HWeapon + type = RuleB(Combat, Classic2HBAnimation) ? anim2HWeapon : anim2HSlashing; break; case EQ::item::ItemType2HPiercing: // 2H Piercing if (IsClient() && CastToClient()->ClientVersion() < EQ::versions::ClientVersion::RoF2) From 3f8b67e50004d365b328d8fda3b2e6975f728fe6 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 17 Jun 2021 12:40:01 -0400 Subject: [PATCH 106/624] [Quest API] Add RemoveAllNimbusEffects() to Perl/Lua. (#1413) * [Quest API] Add RemoveAllNimbusEffects() to Perl/Lua. - Add $client->RemoveAllNimbusEffects() to Perl. - Add client:RemoveAllNimbusEffects() to Lua. * Optimize. --- zone/lua_mob.cpp | 8 +++++++- zone/lua_mob.h | 1 + zone/mob.cpp | 16 ++++++++++++++++ zone/mob.h | 1 + zone/perl_mob.cpp | 14 ++++++++++++++ 5 files changed, 39 insertions(+), 1 deletion(-) diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index 869d6b27c..f61ed21c7 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -2364,6 +2364,11 @@ const char *Lua_Mob::GetLastName() { return self->GetLastName(); } +void Lua_Mob::RemoveAllNimbusEffects() { + Lua_Safe_Call_Void(); + self->RemoveAllNimbusEffects(); +} + luabind::scope lua_register_mob() { return luabind::class_("Mob") .def(luabind::constructor<>()) @@ -2767,7 +2772,8 @@ luabind::scope lua_register_mob() { .def("SetBucket", (void(Lua_Mob::*)(std::string,std::string))&Lua_Mob::SetBucket) .def("SetBucket", (void(Lua_Mob::*)(std::string,std::string,std::string))&Lua_Mob::SetBucket) .def("IsHorse", &Lua_Mob::IsHorse) - .def("GetLastName", &Lua_Mob::GetLastName); + .def("GetLastName", &Lua_Mob::GetLastName) + .def("RemoveAllNimbusEffects", &Lua_Mob::RemoveAllNimbusEffects); } luabind::scope lua_register_special_abilities() { diff --git a/zone/lua_mob.h b/zone/lua_mob.h index 45bf78af6..2ae9edb48 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -355,6 +355,7 @@ public: void DoKnockback(Lua_Mob caster, uint32 pushback, uint32 pushup); void AddNimbusEffect(int effect_id); void RemoveNimbusEffect(int effect_id); + void RemoveAllNimbusEffects(); bool IsRunning(); void SetRunning(bool running); void SetBodyType(int new_body, bool overwrite_orig); diff --git a/zone/mob.cpp b/zone/mob.cpp index 17e71c1c6..7f8855290 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4891,6 +4891,22 @@ void Mob::RemoveNimbusEffect(int effectid) safe_delete(outapp); } +void Mob::RemoveAllNimbusEffects() +{ + uint32 nimbus_effects[3] = { nimbus_effect1, nimbus_effect2, nimbus_effect3 }; + for (auto ¤t_nimbus : nimbus_effects) { + auto remove_packet = new EQApplicationPacket(OP_RemoveNimbusEffect, sizeof(RemoveNimbusEffect_Struct)); + auto *remove_effect = (RemoveNimbusEffect_Struct*)remove_packet->pBuffer; + remove_effect->spawnid = GetID(); + remove_effect->nimbus_effect = current_nimbus; + entity_list.QueueClients(this, remove_packet); + safe_delete(remove_packet); + } + nimbus_effect1 = 0; + nimbus_effect2 = 0; + nimbus_effect3 = 0; +} + bool Mob::IsBoat() const { return ( diff --git a/zone/mob.h b/zone/mob.h index 791c5a3e7..dd29b82f9 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -413,6 +413,7 @@ public: inline virtual uint32 GetNimbusEffect3() const { return nimbus_effect3; } void AddNimbusEffect(int effectid); void RemoveNimbusEffect(int effectid); + void RemoveAllNimbusEffects(); inline const glm::vec3& GetTargetRingLocation() const { return m_TargetRing; } inline float GetTargetRingX() const { return m_TargetRing.x; } inline float GetTargetRingY() const { return m_TargetRing.y; } diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index ad16544bb..3690df0c9 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -6257,6 +6257,19 @@ XS(XS_Mob_GetLastName) { XSRETURN(1); } +XS(XS_Mob_RemoveAllNimbusEffects); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_RemoveAllNimbusEffects) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::RemoveAllNimbusEffects(THIS)"); // @categories Script Utility + { + Mob *THIS; + VALIDATE_THIS_IS_MOB; + THIS->RemoveAllNimbusEffects(); + } + XSRETURN_EMPTY; +} + XS(XS_Mob_AddNimbusEffect); XS(XS_Mob_AddNimbusEffect) { dXSARGS; @@ -6639,6 +6652,7 @@ XS(boot_Mob) { newXSproto(strcpy(buf, "GetHateClosest"), XS_Mob_GetHateClosest, file, "$"); newXSproto(strcpy(buf, "GetHateListByDistance"), XS_Mob_GetHateListByDistance, file, "$;$"); newXSproto(strcpy(buf, "GetLastName"), XS_Mob_GetLastName, file, "$"); + newXSproto(strcpy(buf, "RemoveAllNimbusEffects"), XS_Mob_RemoveAllNimbusEffects, file, "$"); newXSproto(strcpy(buf, "AddNimbusEffect"), XS_Mob_AddNimbusEffect, file, "$$"); #ifdef BOTS newXSproto(strcpy(buf, "CastToBot"), XS_Mob_CastToBot, file, "$"); From ccfc8b296f423a8fc67db6ae98b708a90d6d6dc5 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 17 Jun 2021 12:49:20 -0400 Subject: [PATCH 107/624] [Quest API] Add SendToInstance(instance_type, zone_short_name, instance_version, x, y, z, heading, instance_identifier, duration) to Perl/Lua. (#1417) * [Quest API] Add SendToInstance(instance_type, zone_short_name, instance_version, x, y, z, heading, instance_identifier, duration) to Perl/Lua. - Add $client->SendToInstance(instance_type, zone_short_name, instance_version, x, y, z, heading, instance_identifier, duration) to Perl. - Add client:SendToInstance(instance_type, zone_short_name, instance_version, x, y, z, heading, instance_identifier, duration) to Lua. * Fix instance naming. * Add current instance type to bucket name, remove unused variables. * Typo. --- zone/client.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++++ zone/client.h | 1 + zone/lua_client.cpp | 6 ++++++ zone/lua_client.h | 1 + zone/perl_client.cpp | 23 +++++++++++++++++++++ 5 files changed, 79 insertions(+) diff --git a/zone/client.cpp b/zone/client.cpp index 9fbd42b98..eb93f136f 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -10130,6 +10130,54 @@ void Client::SetAFK(uint8 afk_flag) { safe_delete(outapp); } + +void Client::SendToInstance(std::string instance_type, std::string zone_short_name, uint32 instance_version, float x, float y, float z, float heading, std::string instance_identifier, uint32 duration) { + uint32 zone_id = ZoneID(zone_short_name); + std::string current_instance_type = str_tolower(instance_type); + std::string instance_type_name = "public"; + if (current_instance_type.find("solo") != std::string::npos) { + instance_type_name = GetCleanName(); + } else if (current_instance_type.find("group") != std::string::npos) { + uint32 group_id = (GetGroup() ? GetGroup()->GetID() : 0); + instance_type_name = itoa(group_id); + } else if (current_instance_type.find("raid") != std::string::npos) { + uint32 raid_id = (GetRaid() ? GetRaid()->GetID() : 0); + instance_type_name = itoa(raid_id); + } else if (current_instance_type.find("guild") != std::string::npos) { + uint32 guild_id = (GuildID() > 0 ? GuildID() : 0); + instance_type_name = itoa(guild_id); + } + + std::string full_bucket_name = fmt::format( + "{}_{}_{}_{}", + current_instance_type, + instance_type_name, + instance_identifier, + zone_short_name + ); + std::string current_bucket_value = DataBucket::GetData(full_bucket_name); + uint16 instance_id = 0; + + if (current_bucket_value.length() > 0) { + instance_id = atoi(current_bucket_value.c_str()); + } else { + if(!database.GetUnusedInstanceID(instance_id)) { + Message(Chat::White, "Server was unable to find a free instance id."); + return; + } + + if(!database.CreateInstance(instance_id, zone_id, instance_version, duration)) { + Message(Chat::White, "Server was unable to create a new instance."); + return; + } + + DataBucket::SetData(full_bucket_name, itoa(instance_id), itoa(duration)); + } + + AssignToInstance(instance_id); + MovePC(zone_id, instance_id, x, y, z, heading); +} + int Client::CountItem(uint32 item_id) { int quantity = 0; diff --git a/zone/client.h b/zone/client.h index 9408ff1c7..734bc66ff 100644 --- a/zone/client.h +++ b/zone/client.h @@ -662,6 +662,7 @@ public: void MoveZoneInstanceGroup(uint16 instance_id); void MoveZoneInstanceRaid(uint16 instance_id); void SendToGuildHall(); + void SendToInstance(std::string instance_type, std::string zone_short_name, uint32 instance_version, float x, float y, float z, float heading, std::string instance_identifier, uint32 duration); void AssignToInstance(uint16 instance_id); void RemoveFromInstance(uint16 instance_id); void WhoAll(); diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index c08d27fad..233f40622 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -2138,6 +2138,11 @@ void Lua_Client::ResetAllDisciplineTimers() { self->ResetAllDisciplineTimers(); } +void Lua_Client::SendToInstance(std::string instance_type, std::string zone_short_name, uint32 instance_version, float x, float y, float z, float heading, std::string instance_identifier, uint32 duration) { + Lua_Safe_Call_Void(); + self->SendToInstance(instance_type, zone_short_name, instance_version, x, y, z, heading, instance_identifier, duration); +} + int Lua_Client::CountItem(uint32 item_id) { Lua_Safe_Call_Int(); return self->CountItem(item_id); @@ -2517,6 +2522,7 @@ luabind::scope lua_register_client() { .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32,uint32,uint32,const char*,const char*))& Lua_Client::Popup) .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32,uint32,uint32,const char*,const char*,uint32))&Lua_Client::Popup) .def("ResetAllDisciplineTimers", (void(Lua_Client::*)(void))&Lua_Client::ResetAllDisciplineTimers) + .def("SendToInstance", (void(Lua_Client::*)(std::string,std::string,uint32,float,float,float,float,std::string,uint32))&Lua_Client::SendToInstance) .def("CountItem", (int(Lua_Client::*)(uint32))&Lua_Client::CountItem) .def("RemoveItem", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveItem) .def("RemoveItem", (void(Lua_Client::*)(uint32,uint32))&Lua_Client::RemoveItem); diff --git a/zone/lua_client.h b/zone/lua_client.h index d95af104a..51da4b9df 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -41,6 +41,7 @@ public: bool IsLD(); void WorldKick(); void SendToGuildHall(); + void SendToInstance(std::string instance_type, std::string zone_short_name, uint32 instance_version, float x, float y, float z, float heading, std::string instance_identifier, uint32 duration); int GetAnon(); void SetAnon(uint8 anon_flag); int GetAFK(); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index bcd118fc1..36c0ee361 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -5345,6 +5345,28 @@ XS(XS_Client_ResetAllDisciplineTimers) { XSRETURN_EMPTY; } +XS(XS_Client_SendToInstance); +XS(XS_Client_SendToInstance) { + dXSARGS; + if (items != 10) + Perl_croak(aTHX_ "Usage: Client::SendToInstance(THIS, std::string instance_type, std::string zone_short_name, uint32 instance_version, float x, float y, float z, float heading, std::string instance_identifier, uint32 duration)"); + { + Client* THIS; + std::string instance_type = (std::string) SvPV_nolen(ST(1)); + std::string zone_short_name = (std::string) SvPV_nolen(ST(2)); + uint32 instance_version = (uint32) SvUV(ST(3)); + float x = (float) SvNV(ST(4)); + float y = (float) SvNV(ST(5)); + float z = (float) SvNV(ST(6)); + float heading = (float) SvNV(ST(7)); + std::string instance_identifier = (std::string) SvPV_nolen(ST(8)); + uint32 duration = (uint32) SvUV(ST(9)); + VALIDATE_THIS_IS_CLIENT; + THIS->SendToInstance(instance_type, zone_short_name, instance_version, x, y, z, heading, instance_identifier, duration); + } + XSRETURN_EMPTY; +} + XS(XS_Client_CountItem); XS(XS_Client_CountItem) { dXSARGS; @@ -5616,6 +5638,7 @@ XS(boot_Client) { newXSproto(strcpy(buf, "SendSound"), XS_Client_SendSound, file, "$"); newXSproto(strcpy(buf, "SendSpellAnim"), XS_Client_SendSpellAnim, file, "$$$"); newXSproto(strcpy(buf, "SendTargetCommand"), XS_Client_SendTargetCommand, file, "$$"); + newXSproto(strcpy(buf, "SendToInstance"), XS_Client_SendToInstance, file, "$$$$$$$$$$"); newXSproto(strcpy(buf, "SendToGuildHall"), XS_Client_SendToGuildHall, file, "$"); newXSproto(strcpy(buf, "SendWebLink"), XS_Client_SendWebLink, file, "$:$"); newXSproto(strcpy(buf, "SendZoneFlagInfo"), XS_Client_SendZoneFlagInfo, file, "$$"); From 249cbb7bc76b4d6b72bd6bf5d1254ab1072e2816 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 17 Jun 2021 18:42:44 -0400 Subject: [PATCH 108/624] [Quest API] Add CanRaceEquipItem(item_id) to Perl/Lua. (#1411) * [Quest API] Add CanRaceEquipItem(item_id) to Perl/Lua. - Add $mob->CanRaceEquipItem(item_id) to Perl. - Add mob:CanClassEquipItem(item_id) to Lua. - Add mob:CanRaceEquipItem(item_id) to Lua. * Use constants. Co-authored-by: Chris Miles --- zone/lua_mob.cpp | 12 ++++++++++++ zone/lua_mob.h | 3 ++- zone/mob.cpp | 47 +++++++++++++++++++++++++++++++++++++++-------- zone/mob.h | 1 + zone/perl_mob.cpp | 20 +++++++++++++++++++- 5 files changed, 73 insertions(+), 10 deletions(-) diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index f61ed21c7..6eff81a0c 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -2364,6 +2364,16 @@ const char *Lua_Mob::GetLastName() { return self->GetLastName(); } +bool Lua_Mob::CanClassEquipItem(uint32 item_id) { + Lua_Safe_Call_Bool(); + return self->CanClassEquipItem(item_id); +} + +bool Lua_Mob::CanRaceEquipItem(uint32 item_id) { + Lua_Safe_Call_Bool(); + return self->CanRaceEquipItem(item_id); +} + void Lua_Mob::RemoveAllNimbusEffects() { Lua_Safe_Call_Void(); self->RemoveAllNimbusEffects(); @@ -2773,6 +2783,8 @@ luabind::scope lua_register_mob() { .def("SetBucket", (void(Lua_Mob::*)(std::string,std::string,std::string))&Lua_Mob::SetBucket) .def("IsHorse", &Lua_Mob::IsHorse) .def("GetLastName", &Lua_Mob::GetLastName) + .def("CanClassEquipItem", &Lua_Mob::CanClassEquipItem) + .def("CanRaceEquipItem", &Lua_Mob::CanRaceEquipItem) .def("RemoveAllNimbusEffects", &Lua_Mob::RemoveAllNimbusEffects); } diff --git a/zone/lua_mob.h b/zone/lua_mob.h index 2ae9edb48..ebe4a59d0 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -431,7 +431,6 @@ public: int GetBodyType(); int GetOrigBodyType(); void CheckNumHitsRemaining(int type, int32 buff_slot, uint16 spell_id); - void DeleteBucket(std::string bucket_name); std::string GetBucket(std::string bucket_name); std::string GetBucketExpires(std::string bucket_name); @@ -440,6 +439,8 @@ public: void SetBucket(std::string bucket_name, std::string bucket_value); void SetBucket(std::string bucket_name, std::string bucket_value, std::string expiration); bool IsHorse(); + bool CanClassEquipItem(uint32 item_id); + bool CanRaceEquipItem(uint32 item_id); }; #endif diff --git a/zone/mob.cpp b/zone/mob.cpp index 7f8855290..4b945fd23 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -5673,22 +5673,53 @@ bool Mob::CanClassEquipItem(uint32 item_id) const EQ::ItemData* itm = nullptr; itm = database.GetItem(item_id); - if (!itm) + if (!itm) { return false; + } - if(itm->Classes == 65535 ) + auto item_classes = itm->Classes; + if(item_classes == PLAYER_CLASS_ALL_MASK) { return true; + } - if (GetClass() > 16) + auto class_id = GetClass(); + if (class_id > BERSERKER) { return false; + } - int bitmask = 1; - bitmask = bitmask << (GetClass() - 1); - - if(!(itm->Classes & bitmask)) + int class_bitmask = GetPlayerClassBit(class_id); + if(!(item_classes & class_bitmask)) { return false; - else + } else { return true; + } +} + +bool Mob::CanRaceEquipItem(uint32 item_id) +{ + const EQ::ItemData* itm = nullptr; + itm = database.GetItem(item_id); + + if (!itm) { + return false; + } + + auto item_races = itm->Races; + if(item_races == PLAYER_RACE_ALL_MASK) { + return true; + } + + auto race_id = GetBaseRace(); + if (!IsPlayerRace(race_id)) { + return false; + } + + int race_bitmask = GetPlayerRaceBit(race_id); + if(!(item_races & race_bitmask)) { + return false; + } else { + return true; + } } void Mob::SendAddPlayerState(PlayerState new_state) diff --git a/zone/mob.h b/zone/mob.h index dd29b82f9..ea5723fa6 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -441,6 +441,7 @@ public: virtual uint32 GetEquipmentColor(uint8 material_slot) const; virtual uint32 IsEliteMaterialItem(uint8 material_slot) const; bool CanClassEquipItem(uint32 item_id); + bool CanRaceEquipItem(uint32 item_id); bool AffectedBySpellExcludingSlot(int slot, int effect); virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, EQ::skills::SkillType attack_skill) = 0; virtual void Damage(Mob* from, int32 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index 3690df0c9..71fcb02a4 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -5652,7 +5652,7 @@ XS(XS_Mob_CanClassEquipItem) { uint32 item_id = (uint32) SvUV(ST(1)); VALIDATE_THIS_IS_MOB; RETVAL = THIS->CanClassEquipItem(item_id); - ST(0) = boolSV(RETVAL); + ST(0) = boolSV(RETVAL); sv_2mortal(ST(0)); } XSRETURN(1); @@ -6257,6 +6257,23 @@ XS(XS_Mob_GetLastName) { XSRETURN(1); } +XS(XS_Mob_CanRaceEquipItem); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_CanRaceEquipItem) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::CanRaceEquipItem(THIS, uint32 item_id)"); // @categories Inventory and Items, Script Utility + { + Mob *THIS; + bool RETVAL; + uint32 item_id = (uint32) SvUV(ST(1)); + VALIDATE_THIS_IS_MOB; + RETVAL = THIS->CanRaceEquipItem(item_id); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + XS(XS_Mob_RemoveAllNimbusEffects); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_RemoveAllNimbusEffects) { dXSARGS; @@ -6652,6 +6669,7 @@ XS(boot_Mob) { newXSproto(strcpy(buf, "GetHateClosest"), XS_Mob_GetHateClosest, file, "$"); newXSproto(strcpy(buf, "GetHateListByDistance"), XS_Mob_GetHateListByDistance, file, "$;$"); newXSproto(strcpy(buf, "GetLastName"), XS_Mob_GetLastName, file, "$"); + newXSproto(strcpy(buf, "CanRaceEquipItem"), XS_Mob_CanRaceEquipItem, file, "$$"); newXSproto(strcpy(buf, "RemoveAllNimbusEffects"), XS_Mob_RemoveAllNimbusEffects, file, "$"); newXSproto(strcpy(buf, "AddNimbusEffect"), XS_Mob_AddNimbusEffect, file, "$$"); #ifdef BOTS From 27cf5a406849ed9f4a33883789d9013d995d1ece Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 19 Jun 2021 11:35:20 -0400 Subject: [PATCH 109/624] [Commands] Resolve issue with #giveitem crash with no target. (#1425) --- zone/command.cpp | 136 +++++++++++++++++++++++++---------------------- 1 file changed, 72 insertions(+), 64 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index eb0d9da45..89b3a22dd 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -8137,74 +8137,82 @@ void command_giveitem(Client *c, const Seperator *sep) std::string cmd_msg = sep->msg; size_t link_open = cmd_msg.find('\x12'); size_t link_close = cmd_msg.find_last_of('\x12'); - if (link_open != link_close && (cmd_msg.length() - link_open) > EQ::constants::SAY_LINK_BODY_SIZE) { - EQ::SayLinkBody_Struct link_body; - EQ::saylink::DegenerateLinkBody(link_body, cmd_msg.substr(link_open + 1, EQ::constants::SAY_LINK_BODY_SIZE)); - item_id = link_body.item_id; - augment_one = link_body.augment_1; - augment_two = link_body.augment_2; - augment_three = link_body.augment_3; - augment_four = link_body.augment_4; - augment_five = link_body.augment_5; - augment_six = link_body.augment_6; - } else if (sep->IsNumber(1)) { - item_id = atoi(sep->arg[1]); - } else if (!sep->IsNumber(1)) { - c->Message(Chat::Red, "Usage: #giveitem [item id | link] [charges] [augment_one_id] [augment_two_id] [augment_three_id] [augment_four_id] [augment_five_id] [augment_six_id] (Charges are optional.)"); - } else if (!c->GetTarget()) { - c->Message(Chat::Red, "You must target a client to give the item to."); - } else if (!c->GetTarget()->IsClient()) { - c->Message(Chat::Red, "You can only give items to players with this command."); - } - - Client *client_target = c->GetTarget()->CastToClient(); - uint8 item_status = 0; - uint8 current_status = c->Admin(); - const EQ::ItemData* item = database.GetItem(item_id); - if (item) { - item_status = item->MinStatus; - } - - if (item_status > current_status) { - c->Message( - Chat::White, - fmt::format( - "Insufficient status to summon this item, current status is {}, required status is {}.", - current_status, - item_status - ).c_str() - ); - } + if (c->GetTarget()) { + if (!c->GetTarget()->IsClient()) { + c->Message(Chat::Red, "You can only give items to players with this command."); + return; + } + + if (link_open != link_close && (cmd_msg.length() - link_open) > EQ::constants::SAY_LINK_BODY_SIZE) { + EQ::SayLinkBody_Struct link_body; + EQ::saylink::DegenerateLinkBody(link_body, cmd_msg.substr(link_open + 1, EQ::constants::SAY_LINK_BODY_SIZE)); + item_id = link_body.item_id; + augment_one = link_body.augment_1; + augment_two = link_body.augment_2; + augment_three = link_body.augment_3; + augment_four = link_body.augment_4; + augment_five = link_body.augment_5; + augment_six = link_body.augment_6; + } else if (sep->IsNumber(1)) { + item_id = atoi(sep->arg[1]); + } else if (!sep->IsNumber(1)) { + c->Message(Chat::Red, "Usage: #giveitem [item id | link] [charges] [augment_one_id] [augment_two_id] [augment_three_id] [augment_four_id] [augment_five_id] [augment_six_id] (Charges are optional.)"); + return; + } - if (arguments >= 2 && sep->IsNumber(2)) { - charges = atoi(sep->arg[2]); - } - - if (arguments >= 3 && sep->IsNumber(3)) { - augment_one = atoi(sep->arg[3]); - } - - if (arguments >= 4 && sep->IsNumber(4)) { - augment_two = atoi(sep->arg[4]); - } - - if (arguments >= 5 && sep->IsNumber(5)) { - augment_three = atoi(sep->arg[5]); - } - - if (arguments >= 6 && sep->IsNumber(6)) { - augment_four = atoi(sep->arg[6]); - } + Client *client_target = c->GetTarget()->CastToClient(); + uint8 item_status = 0; + uint8 current_status = c->Admin(); + const EQ::ItemData* item = database.GetItem(item_id); + if (item) { + item_status = item->MinStatus; + } + + if (item_status > current_status) { + c->Message( + Chat::White, + fmt::format( + "Insufficient status to summon this item, current status is {}, required status is {}.", + current_status, + item_status + ).c_str() + ); + return; + } + + if (arguments >= 2 && sep->IsNumber(2)) { + charges = atoi(sep->arg[2]); + } + + if (arguments >= 3 && sep->IsNumber(3)) { + augment_one = atoi(sep->arg[3]); + } + + if (arguments >= 4 && sep->IsNumber(4)) { + augment_two = atoi(sep->arg[4]); + } + + if (arguments >= 5 && sep->IsNumber(5)) { + augment_three = atoi(sep->arg[5]); + } + + if (arguments >= 6 && sep->IsNumber(6)) { + augment_four = atoi(sep->arg[6]); + } - if (arguments >= 7 && sep->IsNumber(7)) { - augment_five = atoi(sep->arg[7]); - } + if (arguments >= 7 && sep->IsNumber(7)) { + augment_five = atoi(sep->arg[7]); + } - if (arguments == 8 && sep->IsNumber(8)) { - augment_six = atoi(sep->arg[8]); - } + if (arguments == 8 && sep->IsNumber(8)) { + augment_six = atoi(sep->arg[8]); + } - client_target->SummonItem(item_id, charges, augment_one, augment_two, augment_three, augment_four, augment_five, augment_six); + client_target->SummonItem(item_id, charges, augment_one, augment_two, augment_three, augment_four, augment_five, augment_six); + } else { + c->Message(Chat::Red, "You must target a client to give the item to."); + return; + } } void command_givemoney(Client *c, const Seperator *sep) From 34d5959caea5665366c3faf314210dc6b15c40e3 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 19 Jun 2021 15:11:18 -0400 Subject: [PATCH 110/624] [Typo] row[27] not row[25]. (#1432) --- zone/npc_scale_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/npc_scale_manager.cpp b/zone/npc_scale_manager.cpp index 158ac299b..b810104ca 100644 --- a/zone/npc_scale_manager.cpp +++ b/zone/npc_scale_manager.cpp @@ -240,7 +240,7 @@ bool NpcScaleManager::LoadScaleData() scale_data.spell_scale = atoi(row[25]); scale_data.heal_scale = atoi(row[26]); - if (row[25]) { + if (row[27]) { scale_data.special_abilities = row[27]; } From c214c3a95b9ebca3980dc8c35f807af750a97e39 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 22 Jun 2021 12:08:04 -0400 Subject: [PATCH 111/624] [Bug Fix] spell cast time cap issue introduced in e5b9d72b81 (#1435) Me bad coder. --- zone/bot.cpp | 3 ++- zone/mob.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index e3abb5935..757d77b9a 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -6793,6 +6793,7 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { int32 Bot::GetActSpellCasttime(uint16 spell_id, int32 casttime) { int32 cast_reducer = GetBotFocusEffect(focusSpellHaste, spell_id); + auto min_cap = casttime / 2; uint8 botlevel = GetLevel(); uint8 botclass = GetClass(); if (botlevel >= 51 && casttime >= 3000 && !spells[spell_id].goodEffect && @@ -6870,7 +6871,7 @@ int32 Bot::GetActSpellCasttime(uint16 spell_id, int32 casttime) { } casttime = casttime * (100 - cast_reducer) / 100; - return std::max(casttime, casttime / 2); + return std::max(casttime, min_cap); } int32 Bot::GetActSpellCost(uint16 spell_id, int32 cost) { diff --git a/zone/mob.cpp b/zone/mob.cpp index 4b945fd23..ab77f27c1 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3124,6 +3124,7 @@ uint32 Mob::GetLevelHP(uint8 tlevel) int32 Mob::GetActSpellCasttime(uint16 spell_id, int32 casttime) { int32 cast_reducer = GetFocusEffect(focusSpellHaste, spell_id); + auto min_cap = casttime / 2; if (level > 50 && casttime >= 3000 && !spells[spell_id].goodEffect && (GetClass() == RANGER || GetClass() == SHADOWKNIGHT || GetClass() == PALADIN || GetClass() == BEASTLORD)) { @@ -3132,7 +3133,7 @@ int32 Mob::GetActSpellCasttime(uint16 spell_id, int32 casttime) } casttime = casttime * (100 - cast_reducer) / 100; - return std::max(casttime, casttime / 2); + return std::max(casttime, min_cap); } void Mob::ExecWeaponProc(const EQ::ItemInstance *inst, uint16 spell_id, Mob *on, int level_override) { From 1f154af2cac2f6203887731bf33a18f2d6a71698 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Wed, 23 Jun 2021 00:17:56 -0500 Subject: [PATCH 112/624] [Hotfix] SendIllusion revert to October --- zone/mob.cpp | 81 ++++++++++++++++++++---------------------- zone/spell_effects.cpp | 8 +++-- 2 files changed, 44 insertions(+), 45 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index ab77f27c1..dd1ad6475 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1815,25 +1815,23 @@ void Mob::SendIllusionPacket( new_drakkin_details = (in_drakkin_details == 0xFFFFFFFF) ? GetDrakkinDetails() : in_drakkin_details; new_aa_title = in_aa_title; - bool reset_features_to_player_profile = IsClient() && in_race == 0; - if (reset_features_to_player_profile) { - auto c = CastToClient(); - race = c->GetBaseRace(); - gender = c->GetBaseGender(); + // Reset features to Base from the Player Profile + if (IsClient() && in_race == 0) { + race = CastToClient()->GetBaseRace(); + gender = CastToClient()->GetBaseGender(); new_texture = texture = 0xFF; new_helmtexture = helmtexture = 0xFF; - new_haircolor = haircolor = c->GetBaseHairColor(); - new_beardcolor = beardcolor = c->GetBaseBeardColor(); - new_eyecolor1 = eyecolor1 = c->GetBaseEyeColor(); - new_eyecolor2 = eyecolor2 = c->GetBaseEyeColor(); - new_hairstyle = hairstyle = c->GetBaseHairStyle(); - new_luclinface = luclinface = c->GetBaseFace(); - new_beard = beard = c->GetBaseBeard(); + new_haircolor = haircolor = CastToClient()->GetBaseHairColor(); + new_beardcolor = beardcolor = CastToClient()->GetBaseBeardColor(); + new_eyecolor1 = eyecolor1 = CastToClient()->GetBaseEyeColor(); + new_eyecolor2 = eyecolor2 = CastToClient()->GetBaseEyeColor(); + new_hairstyle = hairstyle = CastToClient()->GetBaseHairStyle(); + new_luclinface = luclinface = CastToClient()->GetBaseFace(); + new_beard = beard = CastToClient()->GetBaseBeard(); new_aa_title = aa_title = 0xFF; - new_drakkin_heritage = drakkin_heritage = c->GetBaseHeritage(); - new_drakkin_tattoo = drakkin_tattoo = c->GetBaseTattoo(); - new_drakkin_details = drakkin_details = c->GetBaseDetails(); - + new_drakkin_heritage = drakkin_heritage = CastToClient()->GetBaseHeritage(); + new_drakkin_tattoo = drakkin_tattoo = CastToClient()->GetBaseTattoo(); + new_drakkin_details = drakkin_details = CastToClient()->GetBaseDetails(); switch (race) { case OGRE: size = 9; @@ -1864,8 +1862,8 @@ void Mob::SendIllusionPacket( } } - // update internal values for mob from illusion - size = (in_size <= 0.0f) ? GetRaceGenderDefaultHeight(race, gender) : in_size; + // update internal values for mob + size = (in_size <= 0.0f) ? GetSize() : in_size; texture = new_texture; helmtexture = new_helmtexture; haircolor = new_haircolor; @@ -1879,37 +1877,34 @@ void Mob::SendIllusionPacket( drakkin_tattoo = new_drakkin_tattoo; drakkin_details = new_drakkin_details; - // send packet - auto outapp = new EQApplicationPacket(OP_Illusion, sizeof(Illusion_Struct)); - auto *i = (Illusion_Struct *) outapp->pBuffer; - i->spawnid = GetID(); - strcpy(i->charname, GetCleanName()); - i->race = race; - i->gender = gender; - i->texture = new_texture; - i->helmtexture = new_helmtexture; - i->haircolor = new_haircolor; - i->beardcolor = new_beardcolor; - i->beard = new_beard; - i->eyecolor1 = new_eyecolor1; - i->eyecolor2 = new_eyecolor2; - i->hairstyle = new_hairstyle; - i->face = new_luclinface; - i->drakkin_heritage = new_drakkin_heritage; - i->drakkin_tattoo = new_drakkin_tattoo; - i->drakkin_details = new_drakkin_details; - i->size = size; + auto outapp = new EQApplicationPacket(OP_Illusion, sizeof(Illusion_Struct)); + Illusion_Struct *is = (Illusion_Struct *) outapp->pBuffer; + is->spawnid = GetID(); + strcpy(is->charname, GetCleanName()); + is->race = race; + is->gender = gender; + is->texture = new_texture; + is->helmtexture = new_helmtexture; + is->haircolor = new_haircolor; + is->beardcolor = new_beardcolor; + is->beard = new_beard; + is->eyecolor1 = new_eyecolor1; + is->eyecolor2 = new_eyecolor2; + is->hairstyle = new_hairstyle; + is->face = new_luclinface; + is->drakkin_heritage = new_drakkin_heritage; + is->drakkin_tattoo = new_drakkin_tattoo; + is->drakkin_details = new_drakkin_details; + is->size = size; entity_list.QueueClients(this, outapp); safe_delete(outapp); - // Refresh armor and tints after send illusion packet + /* Refresh armor and tints after send illusion packet */ SendArmorAppearance(); - LogMobAppearance( - "[SendIllusionPacket] race [{}] gender [{}] new_texture [{}] new_helmtexture [{}] new_haircolor [{}] new_beardcolor [{}] " - "new_eyecolor1 [{}] new_eyecolor2 [{}] new_hairstyle [{}] new_luclinface [{}] new_drakkin_heritage [{}] " - "new_drakkin_tattoo [{}] new_drakkin_details [{}] size [{}]", + LogSpells( + "Illusion: Race [{}] Gender [{}] Texture [{}] HelmTexture [{}] HairColor [{}] BeardColor [{}] EyeColor1 [{}] EyeColor2 [{}] HairStyle [{}] Face [{}] DrakkinHeritage [{}] DrakkinTattoo [{}] DrakkinDetails [{}] Size [{}]", race, gender, new_texture, diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index bd6ef946a..4fdb926b4 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1478,7 +1478,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove } } - SendArmorAppearance(); + for (int x = EQ::textures::textureBegin; x <= EQ::textures::LastTintableTexture; x++) { + SendWearChange(x); + } if (caster == this && spell.id != 287 && spell.id != 601 && (spellbonuses.IllusionPersistence || aabonuses.IllusionPersistence || @@ -3902,7 +3904,9 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) SendAppearancePacket(AT_Size, 6); } - SendArmorAppearance(); + for (int x = EQ::textures::textureBegin; x <= EQ::textures::LastTintableTexture; x++) { + SendWearChange(x); + } break; } From 5a2b5cd295cafb1951c76c9a6783a9cb8f6be902 Mon Sep 17 00:00:00 2001 From: Kurt Gilpin Date: Thu, 24 Jun 2021 14:14:00 -0500 Subject: [PATCH 113/624] [Inventory] Remove Trader's Satchel ID from inventory.cpp (#1423) * Remove Trader's Satchel ID from inventory.cpp Not sure if this is the right way to do this, but seems to work... * Update inventory.cpp Readibility. --- zone/inventory.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zone/inventory.cpp b/zone/inventory.cpp index b3d3b6d2b..8e1b92448 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -1673,7 +1673,10 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { if (dstbag) dstbagid = dstbag->GetItem()->ID; } - if (srcitemid==17899 || srcbagid==17899 || dstitemid==17899 || dstbagid==17899){ + if ((srcbagid && srcbag->GetItem()->BagType == EQ::item::BagTypeTradersSatchel) || + (dstbagid && dstbag->GetItem()->BagType == EQ::item::BagTypeTradersSatchel) || + (srcitemid && src_inst->GetItem()->BagType == EQ::item::BagTypeTradersSatchel) || + (dstitemid && dst_inst->GetItem()->BagType == EQ::item::BagTypeTradersSatchel)) { this->Trader_EndTrader(); this->Message(Chat::Red,"You cannot move your Trader Satchels, or items inside them, while Trading."); } From 1c75236508102252976fa862159463de255510fd Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 25 Jun 2021 15:38:02 -0400 Subject: [PATCH 114/624] [Spells Cleanup] Unify and add most hardcoded spell IDs (#1438) Move the newer stuff added that was recently to the same place all of our previous spell IDs were defined. Either of these solutions were good, but I went with defines since it was less changes I also added a bunch of stuff the client has hardcoded behavior for, but not currently implemented by us. The removed stuff from the command_castspell were reused on live, so I figured it was best to remove them from the restrictions since they are no longer test spells --- common/eq_constants.h | 2 - common/spdat.h | 120 ++++++++++++++++++++++++++++++++++++++++++ zone/attack.cpp | 2 +- zone/command.cpp | 46 +++++++++++++--- zone/spells.cpp | 2 +- 5 files changed, 161 insertions(+), 11 deletions(-) diff --git a/common/eq_constants.h b/common/eq_constants.h index 1a4f3c462..e5fbd9f64 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -438,8 +438,6 @@ static const uint8 SkillDamageTypes[EQ::skills::HIGHEST_SKILL + 1] = // change t static const uint32 MAX_SPELL_DB_ID_VAL = 65535; -static const uint32 DB_SPELL_CAZIC_TOUCH = 982; -static const uint32 DB_SPELL_TOUCH_OF_VINITRAS = 2859; static const uint32 DB_FACTION_GEM_CHOPPERS = 255; static const uint32 DB_FACTION_HERETICS = 265; static const uint32 DB_FACTION_KING_AKANON = 333; diff --git a/common/spdat.h b/common/spdat.h index a2a26da2e..7b3ff5ed7 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -33,6 +33,126 @@ #define SPELL_IMP_HARM_TOUCH 2774 #define SPELL_NPC_HARM_TOUCH 929 #define SPELL_AVATAR_ST_PROC 2434 +#define SPELL_CAZIC_TOUCH 982 +#define SPELL_TOUCH_OF_VINITRAS 2859 +#define SPELL_DESPERATE_HOPE 841 +#define SPELL_CHARM 300 +#define SPELL_METAMORPHOSIS65 2314 +#define SPELL_JT_BUFF 3716 +#define SPELL_CAN_O_WHOOP_ASS 911 +#define SPELL_PHOENIX_CHARM 3014 +#define SPELL_AVATAR_KNOCKBACK 905 +#define SPELL_SHAPECHANGE65 2079 +#define SPELL_SUNSET_HOME1218 1218 +#define SPELL_SUNSET_HOME819 819 +#define SPELL_SHAPECHANGE75 780 +#define SPELL_SHAPECHANGE80 781 +#define SPELL_SHAPECHANGE85 782 +#define SPELL_SHAPECHANGE90 783 +#define SPELL_SHAPECHANGE95 784 +#define SPELL_SHAPECHANGE100 785 +#define SPELL_SHAPECHANGE25 1200 +#define SPELL_SHAPECHANGE30 1201 +#define SPELL_SHAPECHANGE35 1202 +#define SPELL_SHAPECHANGE40 1203 +#define SPELL_SHAPECHANGE45 1204 +#define SPELL_SHAPECHANGE50 1205 +#define SPELL_NPC_AEGOLISM 1343 +#define SPELL_SHAPECHANGE55 1923 +#define SPELL_SHAPECHANGE60 1924 +#define SPELL_COMMAND_OF_DRUZZIL 3355 +#define SPELL_SHAPECHANGE70 6503 +// these have known hardcoded behavior but we don't do anything yet, move them above this comment when fixed +#define SPELL_THE_DAINS_JUSTICE 1476 +#define SPELL_MODULATION 1502 +#define SPELL_TORPOR 1576 +#define SPELL_SPLURT 1620 +#define SPELL_SEBILITE_POX 1814 +#define SPELL_SOUL_WELL 1816 +#define SPELL_MYSTICAL_TRANSVERGENCE 2716 +#define SPELL_ACT_OF_VALOR 2775 +#define SPELL_STOICISM 3694 +#define SPELL_ALTER_PLANE_HATE 666 +#define SPELL_ALTER_PLANE_SKY 674 +#define SPELL_DENONS_DESPERATE_DIRGE 742 +#define SPELL_BOND_OF_SATHIR 833 +#define SPELL_DISEASED_CLOUD 836 +#define SPELL_ACTING_RESIST 775 +#define SPELL_ACTING_SHIELD 776 +#define SPELL_ACTING_GUARD 777 +#define SPELL_GUIDE_ACTING 778 +#define SPELL_BYE_BYE 779 +#define SPELL_ACTING_RESIST_II 1206 +#define SPELL_ACTING_SHIELD_II 1207 +#define SPELL_ACTING_GUARD_II 1208 +#define SPELL_GUIDE_ACTING2 1209 +#define SPELL_BYE_BYTE2 1210 +#define SPELL_GUIDE_CANCEL_MAGIC 1211 +#define SPELL_GUIDE_JOURNEY 1212 +#define SPELL_GUIDE_VISION 1213 +#define SPELL_GUIDE_HEALTH 1214 +#define SPELL_GUIDE_INVULNERABILITY 1215 +#define SPELL_GUIDE_BOLT 1216 +#define SPELL_GUIDE_MEMORY_BLUE 1217 +#define SPELL_GUIDE_ALLIANCE 1219 +#define SPELL_SPECIAL_SIGHT 1220 +#define SPELL_TERROR_OF_DARKNESS 1221 +#define SPELL_TERROR_OF_SHADOWS 1222 +#define SPELL_TERROR_OF_DEATH 1223 +#define SPELL_TERROR_OF_TERRIS 1224 +#define SPELL_VOICE_OF_DARKNESS 1225 +#define SPELL_VOICE_OF_SHADOWS 1226 +#define SPELL_VOICE_OF_DEATH 1227 +#define SPELL_VOICE_OF_TERRIS 1228 +#define SPELL_VENGEANCE_V 1229 +#define SPELL_VENGEANCE_VII 1230 +#define SPELL_VENGEANCE_VIII 1231 +#define SPELL_VENGEANCE_IX 1232 +#define SPELL_CORRUPTED_LACERATION 1233 +#define SPELL_VISIONS_OF_CHAOS 1234 +#define SPELL_VISIONS_OF_PAIN 1235 +#define SPELL_COMMANDING_PRESENCE 1236 +#define SPELL_MALICIOUS_INTENT 1237 +#define SPELL_CURSE_OF_FLAMES 1238 +#define SPELL_DEVOURING_CONFLAGRATION 1239 +#define SPELL_AVATAR_SHIELD 1240 +#define SPELL_AVATAR_SIGHT 1241 +#define SPELL_AVATAR_GUARD 1242 +#define SPELL_AVATAR_RESIST 1243 +#define SPELL_MAGI_BOLT 1244 +#define SPELL_MAGI_STRIKE 1245 +#define SPELL_MAGI_CURSE 1246 +#define SPELL_MAGI_CIRCLE 1247 +#define SPELL_SPIRITUAL_ECHO 1248 +#define SPELL_BRISTLING_ARMAMENT 1249 +#define SPELL_WATON_DESTRUCTION 1250 +#define SPELL_ACTING_MAGIC_RESIST_I 1900 +#define SPELL_ACTING_FIRE_RESIST_I 1901 +#define SPELL_ACTING_COLD_RESIST_I 1902 +#define SPELL_ACTING_POISON_RESIST_I 1903 +#define SPELL_ACTING_DISEASE_RESIST_I 1904 +#define SPELL_ACTING_MAGIC_RESIST_II 1905 +#define SPELL_ACTING_FIRE_RESIST_II 1906 +#define SPELL_ACTING_COLD_RESIST_II 1907 +#define SPELL_ACTING_POISON_RESIST_II 1908 +#define SPELL_ACTING_DISEASE_RESIST_II 1909 +#define SPELL_ACTING_FIRE_SHIELD 1910 +#define SPELL_ACTING_POISON_SHIELD 1911 +#define SPELL_ACTING_COLD_SHIELD 1912 +#define SPELL_ACTING_DISEASE_SHIELD 1913 +#define SPELL_ACTING_ARMOR_I 1914 +#define SPELL_ACTING_ARMOR_II 1915 +#define SPELL_ACTING_ARMOR_III 1916 +#define SPELL_ACTING_HEALTH_I 1917 +#define SPELL_ACTING_HEALTH_II 1918 +#define SPELL_ACTING_HEALTH_III 1919 +#define SPELL_ACTING_HEALTH_IV 1920 +#define SPELL_ACTING_SPIRIT_I 1921 +#define SPELL_ACTING_SPIRIT_II 1922 +#define SPELL_RESURRECTION_SICKNESS 756 +#define SPELL_RESURRECTION_SICKNESS2 5249 +#define SPELL_REVIVAL_SICKNESS 13087 +#define SPELL_MANA_BURN 2751 #define EFFECT_COUNT 12 diff --git a/zone/attack.cpp b/zone/attack.cpp index fdcf553d9..980bd0dc9 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3444,7 +3444,7 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const bool FromDamageShield = (skill_used == EQ::skills::SkillAbjuration); bool ignore_invul = false; if (IsValidSpell(spell_id)) - ignore_invul = spell_id == 982 || spells[spell_id].cast_not_standing; // cazic touch + ignore_invul = spell_id == SPELL_CAZIC_TOUCH || spells[spell_id].cast_not_standing; if (!ignore_invul && (GetInvul() || DivineAura())) { LogCombat("Avoiding [{}] damage due to invulnerability", damage); diff --git a/zone/command.cpp b/zone/command.cpp index 89b3a22dd..50b209af1 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -2939,6 +2939,44 @@ void command_findspell(Client *c, const Seperator *sep) } } +inline bool CastRestrictedSpell(int spellid) +{ + switch (spellid) { + case SPELL_TOUCH_OF_VINITRAS: + case SPELL_DESPERATE_HOPE: + case SPELL_CHARM: + case SPELL_METAMORPHOSIS65: + case SPELL_JT_BUFF: + case SPELL_CAN_O_WHOOP_ASS: + case SPELL_PHOENIX_CHARM: + case SPELL_CAZIC_TOUCH: + case SPELL_AVATAR_KNOCKBACK: + case SPELL_SHAPECHANGE65: + case SPELL_SUNSET_HOME1218: + case SPELL_SUNSET_HOME819: + case SPELL_SHAPECHANGE75: + case SPELL_SHAPECHANGE80: + case SPELL_SHAPECHANGE85: + case SPELL_SHAPECHANGE90: + case SPELL_SHAPECHANGE95: + case SPELL_SHAPECHANGE100: + case SPELL_SHAPECHANGE25: + case SPELL_SHAPECHANGE30: + case SPELL_SHAPECHANGE35: + case SPELL_SHAPECHANGE40: + case SPELL_SHAPECHANGE45: + case SPELL_SHAPECHANGE50: + case SPELL_NPC_AEGOLISM: + case SPELL_SHAPECHANGE55: + case SPELL_SHAPECHANGE60: + case SPELL_COMMAND_OF_DRUZZIL: + case SPELL_SHAPECHANGE70: + return true; + default: + return false; + } +} + void command_castspell(Client *c, const Seperator *sep) { if (!sep->IsNumber(1)) @@ -2948,13 +2986,7 @@ void command_castspell(Client *c, const Seperator *sep) /* Spell restrictions. */ - if (((spellid == 2859) || (spellid == 841) || (spellid == 300) || (spellid == 2314) || - (spellid == 3716) || (spellid == 911) || (spellid == 3014) || (spellid == 982) || - (spellid == 905) || (spellid == 2079) || (spellid == 1218) || (spellid == 819) || - ((spellid >= 780) && (spellid <= 785)) || ((spellid >= 1200) && (spellid <= 1205)) || - ((spellid >= 1342) && (spellid <= 1348)) || (spellid == 1923) || (spellid == 1924) || - (spellid == 3355)) && - c->Admin() < commandCastSpecials) + if (CastRestrictedSpell(spellid) && c->Admin() < commandCastSpecials) c->Message(Chat::Red, "Unable to cast spell."); else if (spellid >= SPDAT_RECORDS) c->Message(Chat::White, "Error: #CastSpell: Argument out of range"); diff --git a/zone/spells.cpp b/zone/spells.cpp index 757ec0645..dee96509e 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2047,7 +2047,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui return false; //Death Touch targets the pet owner instead of the pet when said pet is tanking. - if ((RuleB(Spells, CazicTouchTargetsPetOwner) && spell_target && spell_target->HasOwner()) && spell_id == DB_SPELL_CAZIC_TOUCH || spell_id == DB_SPELL_TOUCH_OF_VINITRAS) { + if ((RuleB(Spells, CazicTouchTargetsPetOwner) && spell_target && spell_target->HasOwner()) && spell_id == SPELL_CAZIC_TOUCH || spell_id == SPELL_TOUCH_OF_VINITRAS) { Mob* owner = spell_target->GetOwner(); if (owner) { From 82d6e0138d257c1af3131e635cb934b4546c7cd0 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Fri, 25 Jun 2021 15:05:47 -0500 Subject: [PATCH 115/624] [Repositories] Regenerate repositories with int64 support, reserved word support (#1440) --- .../base/base_aa_ability_repository.h | 2 +- .../base/base_aa_rank_effects_repository.h | 2 +- .../base/base_aa_rank_prereqs_repository.h | 2 +- .../base/base_aa_ranks_repository.h | 2 +- .../base/base_account_flags_repository.h | 2 +- .../base/base_account_ip_repository.h | 4 +- .../base/base_account_repository.h | 2 +- .../base/base_account_rewards_repository.h | 2 +- .../base/base_adventure_details_repository.h | 2 +- .../base/base_adventure_members_repository.h | 2 +- .../base/base_adventure_stats_repository.h | 2 +- ...venture_template_entry_flavor_repository.h | 2 +- ...base_adventure_template_entry_repository.h | 2 +- .../base/base_adventure_template_repository.h | 2 +- .../base/base_alternate_currency_repository.h | 2 +- .../repositories/base/base_auras_repository.h | 2 +- .../base/base_base_data_repository.h | 20 +- .../base/base_blocked_spells_repository.h | 2 +- .../base/base_bug_reports_repository.h | 6 +- .../repositories/base/base_bugs_repository.h | 2 +- .../repositories/base/base_buyer_repository.h | 2 +- ...base_char_create_combinations_repository.h | 20 +- ...char_create_point_allocations_repository.h | 2 +- .../base/base_char_recipe_list_repository.h | 2 +- .../base_character_activities_repository.h | 2 +- .../base_character_alt_currency_repository.h | 2 +- ...character_alternate_abilities_repository.h | 2 +- .../base/base_character_auras_repository.h | 2 +- .../base_character_bandolier_repository.h | 2 +- .../base/base_character_bind_repository.h | 2 +- .../base/base_character_buffs_repository.h | 2 +- .../base_character_corpse_items_repository.h | 2 +- .../base/base_character_corpses_repository.h | 20 +- .../base/base_character_currency_repository.h | 2 +- .../base/base_character_data_repository.h | 40 +- .../base_character_disciplines_repository.h | 2 +- ...character_expedition_lockouts_repository.h | 2 +- ...se_character_inspect_messages_repository.h | 2 +- .../base_character_item_recast_repository.h | 2 +- .../base_character_languages_repository.h | 2 +- ...haracter_leadership_abilities_repository.h | 2 +- .../base/base_character_material_repository.h | 2 +- .../base_character_memmed_spells_repository.h | 2 +- .../base_character_pet_buffs_repository.h | 2 +- .../base/base_character_pet_info_repository.h | 2 +- .../base_character_pet_inventory_repository.h | 2 +- .../base_character_potionbelt_repository.h | 2 +- .../base/base_character_skills_repository.h | 2 +- .../base/base_character_spells_repository.h | 2 +- .../base/base_character_tasks_repository.h | 2 +- .../base/base_completed_tasks_repository.h | 2 +- .../base/base_content_flags_repository.h | 2 +- .../base/base_damageshieldtypes_repository.h | 2 +- .../base/base_data_buckets_repository.h | 10 +- .../base/base_db_str_repository.h | 2 +- .../base/base_discovered_items_repository.h | 2 +- .../repositories/base/base_doors_repository.h | 11 +- .../base/base_dynamic_zones_repository.h | 2 +- .../base/base_eventlog_repository.h | 4 +- .../base_expedition_lockouts_repository.h | 2 +- .../base/base_expeditions_repository.h | 2 +- .../base/base_faction_base_data_repository.h | 2 +- .../base/base_faction_list_mod_repository.h | 2 +- .../base/base_faction_list_repository.h | 2 +- .../base/base_faction_values_repository.h | 2 +- .../base/base_fishing_repository.h | 2 +- .../base/base_forage_repository.h | 2 +- .../base/base_friends_repository.h | 2 +- .../base/base_global_loot_repository.h | 20 +- .../base/base_gm_ips_repository.h | 2 +- .../base/base_goallists_repository.h | 2 +- .../base/base_graveyard_repository.h | 2 +- .../base/base_ground_spawns_repository.h | 2 +- .../base/base_group_id_repository.h | 2 +- .../base/base_group_leaders_repository.h | 2 +- .../base/base_guild_members_repository.h | 2 +- .../base/base_guild_ranks_repository.h | 2 +- .../base/base_guild_relations_repository.h | 2 +- .../base/base_guilds_repository.h | 2 +- .../base/base_hackers_repository.h | 4 +- .../base_instance_list_player_repository.h | 2 +- .../base/base_instance_list_repository.h | 2 +- .../base/base_inventory_repository.h | 2 +- .../base_inventory_snapshots_repository.h | 2 +- .../base/base_ip_exemptions_repository.h | 2 +- .../base/base_item_tick_repository.h | 2 +- .../repositories/base/base_items_repository.h | 6 +- .../base/base_ldon_trap_entries_repository.h | 2 +- .../base_ldon_trap_templates_repository.h | 2 +- .../base/base_level_exp_mods_repository.h | 2 +- .../base/base_lfguild_repository.h | 2 +- .../base/base_login_accounts_repository.h | 8 +- .../base/base_login_api_tokens_repository.h | 6 +- .../base_login_server_admins_repository.h | 4 +- .../base_login_server_list_types_repository.h | 2 +- .../base_login_world_servers_repository.h | 4 +- .../base/base_lootdrop_entries_repository.h | 2 +- .../base/base_lootdrop_repository.h | 2 +- .../base/base_loottable_entries_repository.h | 2 +- .../base/base_loottable_repository.h | 2 +- .../repositories/base/base_mail_repository.h | 2 +- .../base/base_merchantlist_repository.h | 2 +- .../base/base_merchantlist_temp_repository.h | 2 +- .../base/base_name_filter_repository.h | 2 +- .../base/base_npc_emotes_repository.h | 2 +- .../base_npc_faction_entries_repository.h | 2 +- .../base/base_npc_faction_repository.h | 2 +- .../base_npc_scale_global_base_repository.h | 2 +- ...se_npc_spells_effects_entries_repository.h | 2 +- .../base/base_npc_spells_effects_repository.h | 2 +- .../base/base_npc_spells_entries_repository.h | 2 +- .../base/base_npc_spells_repository.h | 2 +- .../base/base_npc_types_repository.h | 29 +- .../base/base_npc_types_tint_repository.h | 2 +- .../base/base_object_contents_repository.h | 2 +- .../base/base_object_repository.h | 2 +- ...se_perl_event_export_settings_repository.h | 2 +- .../base/base_petitions_repository.h | 10 +- .../base_pets_beastlord_data_repository.h | 338 +++++++++ ...ase_pets_equipmentset_entries_repository.h | 2 +- .../base/base_pets_equipmentset_repository.h | 2 +- .../repositories/base/base_pets_repository.h | 2 +- .../base/base_player_titlesets_repository.h | 2 +- .../base/base_proximities_repository.h | 2 +- .../base/base_quest_globals_repository.h | 2 +- .../base/base_raid_details_repository.h | 2 +- .../base/base_raid_members_repository.h | 2 +- .../base/base_reports_repository.h | 2 +- .../base/base_respawn_times_repository.h | 2 +- .../base/base_rule_sets_repository.h | 2 +- .../base/base_rule_values_repository.h | 2 +- .../base/base_saylink_repository.h | 2 +- .../base/base_skill_caps_repository.h | 20 +- .../base/base_spawn2_repository.h | 75 +- .../base_spawn_condition_values_repository.h | 2 +- .../base/base_spawn_conditions_repository.h | 2 +- .../base/base_spawn_events_repository.h | 2 +- .../base/base_spawnentry_repository.h | 2 +- .../base/base_spawngroup_repository.h | 2 +- .../base/base_spell_buckets_repository.h | 10 +- .../base/base_spell_globals_repository.h | 2 +- .../base/base_spells_new_repository.h | 2 +- .../base/base_start_zones_repository.h | 2 +- .../base/base_starting_items_repository.h | 20 +- .../base/base_task_activities_repository.h | 2 +- .../base/base_tasksets_repository.h | 2 +- .../base/base_timers_repository.h | 2 +- .../base/base_titles_repository.h | 20 +- .../base/base_trader_repository.h | 2 +- ...ase_tradeskill_recipe_entries_repository.h | 2 +- .../base/base_tradeskill_recipe_repository.h | 2 +- .../repositories/base/base_traps_repository.h | 2 +- .../base/base_tribute_levels_repository.h | 2 +- .../base/base_tributes_repository.h | 2 +- ...base_veteran_reward_templates_repository.h | 2 +- .../base/base_zone_points_repository.h | 2 +- .../repositories/base/base_zone_repository.h | 677 +++++++++--------- 157 files changed, 1003 insertions(+), 647 deletions(-) create mode 100644 common/repositories/base/base_pets_beastlord_data_repository.h diff --git a/common/repositories/base/base_aa_ability_repository.h b/common/repositories/base/base_aa_ability_repository.h index fbf16f0ba..a6079b617 100644 --- a/common/repositories/base/base_aa_ability_repository.h +++ b/common/repositories/base/base_aa_ability_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_aa_rank_effects_repository.h b/common/repositories/base/base_aa_rank_effects_repository.h index fa0eebcd1..3fca2393c 100644 --- a/common/repositories/base/base_aa_rank_effects_repository.h +++ b/common/repositories/base/base_aa_rank_effects_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_aa_rank_prereqs_repository.h b/common/repositories/base/base_aa_rank_prereqs_repository.h index 04b1a0a38..a2d0f584a 100644 --- a/common/repositories/base/base_aa_rank_prereqs_repository.h +++ b/common/repositories/base/base_aa_rank_prereqs_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_aa_ranks_repository.h b/common/repositories/base/base_aa_ranks_repository.h index 8145e3e96..57bf035d2 100644 --- a/common/repositories/base/base_aa_ranks_repository.h +++ b/common/repositories/base/base_aa_ranks_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_account_flags_repository.h b/common/repositories/base/base_account_flags_repository.h index af9c9b223..68929ef37 100644 --- a/common/repositories/base/base_account_flags_repository.h +++ b/common/repositories/base/base_account_flags_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_account_ip_repository.h b/common/repositories/base/base_account_ip_repository.h index 1384c4ef2..2a3a93e62 100644 --- a/common/repositories/base/base_account_ip_repository.h +++ b/common/repositories/base/base_account_ip_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ @@ -74,7 +74,7 @@ public: entry.accid = 0; entry.ip = ""; entry.count = 1; - entry.lastused = current_timestamp(); + entry.lastused = ""; return entry; } diff --git a/common/repositories/base/base_account_repository.h b/common/repositories/base/base_account_repository.h index 6283caf13..681b2c1e5 100644 --- a/common/repositories/base/base_account_repository.h +++ b/common/repositories/base/base_account_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_account_rewards_repository.h b/common/repositories/base/base_account_rewards_repository.h index ee6213e16..4fd0ecb63 100644 --- a/common/repositories/base/base_account_rewards_repository.h +++ b/common/repositories/base/base_account_rewards_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_adventure_details_repository.h b/common/repositories/base/base_adventure_details_repository.h index 656ba8908..cf6b27197 100644 --- a/common/repositories/base/base_adventure_details_repository.h +++ b/common/repositories/base/base_adventure_details_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_adventure_members_repository.h b/common/repositories/base/base_adventure_members_repository.h index 4cdc357fd..80f824175 100644 --- a/common/repositories/base/base_adventure_members_repository.h +++ b/common/repositories/base/base_adventure_members_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_adventure_stats_repository.h b/common/repositories/base/base_adventure_stats_repository.h index b1634c080..bd21e2fef 100644 --- a/common/repositories/base/base_adventure_stats_repository.h +++ b/common/repositories/base/base_adventure_stats_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_adventure_template_entry_flavor_repository.h b/common/repositories/base/base_adventure_template_entry_flavor_repository.h index 8b4bfa7b6..fc586a252 100644 --- a/common/repositories/base/base_adventure_template_entry_flavor_repository.h +++ b/common/repositories/base/base_adventure_template_entry_flavor_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_adventure_template_entry_repository.h b/common/repositories/base/base_adventure_template_entry_repository.h index 5e4b9ca7c..6a348b350 100644 --- a/common/repositories/base/base_adventure_template_entry_repository.h +++ b/common/repositories/base/base_adventure_template_entry_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_adventure_template_repository.h b/common/repositories/base/base_adventure_template_repository.h index 076b8abc5..4509268ad 100644 --- a/common/repositories/base/base_adventure_template_repository.h +++ b/common/repositories/base/base_adventure_template_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_alternate_currency_repository.h b/common/repositories/base/base_alternate_currency_repository.h index 832da0f84..1687d66c3 100644 --- a/common/repositories/base/base_alternate_currency_repository.h +++ b/common/repositories/base/base_alternate_currency_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_auras_repository.h b/common/repositories/base/base_auras_repository.h index e884fc50b..478413ffa 100644 --- a/common/repositories/base/base_auras_repository.h +++ b/common/repositories/base/base_auras_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_base_data_repository.h b/common/repositories/base/base_base_data_repository.h index 3574f254a..ea152964b 100644 --- a/common/repositories/base/base_base_data_repository.h +++ b/common/repositories/base/base_base_data_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ @@ -19,7 +19,7 @@ class BaseBaseDataRepository { public: struct BaseData { int level; - int class; + int class_; float hp; float mana; float end; @@ -39,7 +39,7 @@ public: { return { "level", - "class", + "`class`", "hp", "mana", "end", @@ -84,7 +84,7 @@ public: BaseData entry{}; entry.level = 0; - entry.class = 0; + entry.class_ = 0; entry.hp = 0; entry.mana = 0; entry.end = 0; @@ -129,7 +129,7 @@ public: BaseData entry{}; entry.level = atoi(row[0]); - entry.class = atoi(row[1]); + entry.class_ = atoi(row[1]); entry.hp = static_cast(atof(row[2])); entry.mana = static_cast(atof(row[3])); entry.end = static_cast(atof(row[4])); @@ -172,7 +172,7 @@ public: auto columns = Columns(); update_values.push_back(columns[0] + " = " + std::to_string(base_data_entry.level)); - update_values.push_back(columns[1] + " = " + std::to_string(base_data_entry.class)); + update_values.push_back(columns[1] + " = " + std::to_string(base_data_entry.class_)); update_values.push_back(columns[2] + " = " + std::to_string(base_data_entry.hp)); update_values.push_back(columns[3] + " = " + std::to_string(base_data_entry.mana)); update_values.push_back(columns[4] + " = " + std::to_string(base_data_entry.end)); @@ -203,7 +203,7 @@ public: std::vector insert_values; insert_values.push_back(std::to_string(base_data_entry.level)); - insert_values.push_back(std::to_string(base_data_entry.class)); + insert_values.push_back(std::to_string(base_data_entry.class_)); insert_values.push_back(std::to_string(base_data_entry.hp)); insert_values.push_back(std::to_string(base_data_entry.mana)); insert_values.push_back(std::to_string(base_data_entry.end)); @@ -242,7 +242,7 @@ public: std::vector insert_values; insert_values.push_back(std::to_string(base_data_entry.level)); - insert_values.push_back(std::to_string(base_data_entry.class)); + insert_values.push_back(std::to_string(base_data_entry.class_)); insert_values.push_back(std::to_string(base_data_entry.hp)); insert_values.push_back(std::to_string(base_data_entry.mana)); insert_values.push_back(std::to_string(base_data_entry.end)); @@ -285,7 +285,7 @@ public: BaseData entry{}; entry.level = atoi(row[0]); - entry.class = atoi(row[1]); + entry.class_ = atoi(row[1]); entry.hp = static_cast(atof(row[2])); entry.mana = static_cast(atof(row[3])); entry.end = static_cast(atof(row[4])); @@ -319,7 +319,7 @@ public: BaseData entry{}; entry.level = atoi(row[0]); - entry.class = atoi(row[1]); + entry.class_ = atoi(row[1]); entry.hp = static_cast(atof(row[2])); entry.mana = static_cast(atof(row[3])); entry.end = static_cast(atof(row[4])); diff --git a/common/repositories/base/base_blocked_spells_repository.h b/common/repositories/base/base_blocked_spells_repository.h index d17524a2c..3905a30d6 100644 --- a/common/repositories/base/base_blocked_spells_repository.h +++ b/common/repositories/base/base_blocked_spells_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_bug_reports_repository.h b/common/repositories/base/base_bug_reports_repository.h index ecd1335ef..1f5385994 100644 --- a/common/repositories/base/base_bug_reports_repository.h +++ b/common/repositories/base/base_bug_reports_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ @@ -154,9 +154,9 @@ public: entry._unknown_value = 0; entry.bug_report = ""; entry.system_info = ""; - entry.report_datetime = current_timestamp(); + entry.report_datetime = ""; entry.bug_status = 0; - entry.last_review = current_timestamp(); + entry.last_review = ""; entry.last_reviewer = "None"; entry.reviewer_notes = ""; diff --git a/common/repositories/base/base_bugs_repository.h b/common/repositories/base/base_bugs_repository.h index 9ac9b9330..da35828c1 100644 --- a/common/repositories/base/base_bugs_repository.h +++ b/common/repositories/base/base_bugs_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_buyer_repository.h b/common/repositories/base/base_buyer_repository.h index 1b1cb9800..292792ddb 100644 --- a/common/repositories/base/base_buyer_repository.h +++ b/common/repositories/base/base_buyer_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_char_create_combinations_repository.h b/common/repositories/base/base_char_create_combinations_repository.h index ed806d6bd..8423f9751 100644 --- a/common/repositories/base/base_char_create_combinations_repository.h +++ b/common/repositories/base/base_char_create_combinations_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ @@ -20,7 +20,7 @@ public: struct CharCreateCombinations { int allocation_id; int race; - int class; + int class_; int deity; int start_zone; int expansions_req; @@ -36,7 +36,7 @@ public: return { "allocation_id", "race", - "class", + "`class`", "deity", "start_zone", "expansions_req", @@ -77,7 +77,7 @@ public: entry.allocation_id = 0; entry.race = 0; - entry.class = 0; + entry.class_ = 0; entry.deity = 0; entry.start_zone = 0; entry.expansions_req = 0; @@ -118,7 +118,7 @@ public: entry.allocation_id = atoi(row[0]); entry.race = atoi(row[1]); - entry.class = atoi(row[2]); + entry.class_ = atoi(row[2]); entry.deity = atoi(row[3]); entry.start_zone = atoi(row[4]); entry.expansions_req = atoi(row[5]); @@ -157,7 +157,7 @@ public: update_values.push_back(columns[0] + " = " + std::to_string(char_create_combinations_entry.allocation_id)); update_values.push_back(columns[1] + " = " + std::to_string(char_create_combinations_entry.race)); - update_values.push_back(columns[2] + " = " + std::to_string(char_create_combinations_entry.class)); + update_values.push_back(columns[2] + " = " + std::to_string(char_create_combinations_entry.class_)); update_values.push_back(columns[3] + " = " + std::to_string(char_create_combinations_entry.deity)); update_values.push_back(columns[4] + " = " + std::to_string(char_create_combinations_entry.start_zone)); update_values.push_back(columns[5] + " = " + std::to_string(char_create_combinations_entry.expansions_req)); @@ -184,7 +184,7 @@ public: insert_values.push_back(std::to_string(char_create_combinations_entry.allocation_id)); insert_values.push_back(std::to_string(char_create_combinations_entry.race)); - insert_values.push_back(std::to_string(char_create_combinations_entry.class)); + insert_values.push_back(std::to_string(char_create_combinations_entry.class_)); insert_values.push_back(std::to_string(char_create_combinations_entry.deity)); insert_values.push_back(std::to_string(char_create_combinations_entry.start_zone)); insert_values.push_back(std::to_string(char_create_combinations_entry.expansions_req)); @@ -219,7 +219,7 @@ public: insert_values.push_back(std::to_string(char_create_combinations_entry.allocation_id)); insert_values.push_back(std::to_string(char_create_combinations_entry.race)); - insert_values.push_back(std::to_string(char_create_combinations_entry.class)); + insert_values.push_back(std::to_string(char_create_combinations_entry.class_)); insert_values.push_back(std::to_string(char_create_combinations_entry.deity)); insert_values.push_back(std::to_string(char_create_combinations_entry.start_zone)); insert_values.push_back(std::to_string(char_create_combinations_entry.expansions_req)); @@ -258,7 +258,7 @@ public: entry.allocation_id = atoi(row[0]); entry.race = atoi(row[1]); - entry.class = atoi(row[2]); + entry.class_ = atoi(row[2]); entry.deity = atoi(row[3]); entry.start_zone = atoi(row[4]); entry.expansions_req = atoi(row[5]); @@ -288,7 +288,7 @@ public: entry.allocation_id = atoi(row[0]); entry.race = atoi(row[1]); - entry.class = atoi(row[2]); + entry.class_ = atoi(row[2]); entry.deity = atoi(row[3]); entry.start_zone = atoi(row[4]); entry.expansions_req = atoi(row[5]); diff --git a/common/repositories/base/base_char_create_point_allocations_repository.h b/common/repositories/base/base_char_create_point_allocations_repository.h index 7b3f4a2d6..668fc5062 100644 --- a/common/repositories/base/base_char_create_point_allocations_repository.h +++ b/common/repositories/base/base_char_create_point_allocations_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_char_recipe_list_repository.h b/common/repositories/base/base_char_recipe_list_repository.h index cb3e33707..541859f49 100644 --- a/common/repositories/base/base_char_recipe_list_repository.h +++ b/common/repositories/base/base_char_recipe_list_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_character_activities_repository.h b/common/repositories/base/base_character_activities_repository.h index 9c4a7b144..e4014c0a4 100644 --- a/common/repositories/base/base_character_activities_repository.h +++ b/common/repositories/base/base_character_activities_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_character_alt_currency_repository.h b/common/repositories/base/base_character_alt_currency_repository.h index 9e2ebc695..31228ba3b 100644 --- a/common/repositories/base/base_character_alt_currency_repository.h +++ b/common/repositories/base/base_character_alt_currency_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_character_alternate_abilities_repository.h b/common/repositories/base/base_character_alternate_abilities_repository.h index de4ef1cb4..a3616643d 100644 --- a/common/repositories/base/base_character_alternate_abilities_repository.h +++ b/common/repositories/base/base_character_alternate_abilities_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_character_auras_repository.h b/common/repositories/base/base_character_auras_repository.h index 8ac89e458..10f9d3ac4 100644 --- a/common/repositories/base/base_character_auras_repository.h +++ b/common/repositories/base/base_character_auras_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_character_bandolier_repository.h b/common/repositories/base/base_character_bandolier_repository.h index a208ac9da..f4c725fb7 100644 --- a/common/repositories/base/base_character_bandolier_repository.h +++ b/common/repositories/base/base_character_bandolier_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_character_bind_repository.h b/common/repositories/base/base_character_bind_repository.h index 6ae2aee1d..c31979a8d 100644 --- a/common/repositories/base/base_character_bind_repository.h +++ b/common/repositories/base/base_character_bind_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_character_buffs_repository.h b/common/repositories/base/base_character_buffs_repository.h index 6e577e154..51d179c86 100644 --- a/common/repositories/base/base_character_buffs_repository.h +++ b/common/repositories/base/base_character_buffs_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_character_corpse_items_repository.h b/common/repositories/base/base_character_corpse_items_repository.h index a1d50d125..37343835f 100644 --- a/common/repositories/base/base_character_corpse_items_repository.h +++ b/common/repositories/base/base_character_corpse_items_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_character_corpses_repository.h b/common/repositories/base/base_character_corpses_repository.h index bb7df4e0d..f215a351f 100644 --- a/common/repositories/base/base_character_corpses_repository.h +++ b/common/repositories/base/base_character_corpses_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ @@ -38,7 +38,7 @@ public: int level; int race; int gender; - int class; + int class_; int deity; int texture; int helm_texture; @@ -95,7 +95,7 @@ public: "level", "race", "gender", - "class", + "`class`", "deity", "texture", "helm_texture", @@ -177,7 +177,7 @@ public: entry.level = 0; entry.race = 0; entry.gender = 0; - entry.class = 0; + entry.class_ = 0; entry.deity = 0; entry.texture = 0; entry.helm_texture = 0; @@ -259,7 +259,7 @@ public: entry.level = atoi(row[17]); entry.race = atoi(row[18]); entry.gender = atoi(row[19]); - entry.class = atoi(row[20]); + entry.class_ = atoi(row[20]); entry.deity = atoi(row[21]); entry.texture = atoi(row[22]); entry.helm_texture = atoi(row[23]); @@ -338,7 +338,7 @@ public: update_values.push_back(columns[17] + " = " + std::to_string(character_corpses_entry.level)); update_values.push_back(columns[18] + " = " + std::to_string(character_corpses_entry.race)); update_values.push_back(columns[19] + " = " + std::to_string(character_corpses_entry.gender)); - update_values.push_back(columns[20] + " = " + std::to_string(character_corpses_entry.class)); + update_values.push_back(columns[20] + " = " + std::to_string(character_corpses_entry.class_)); update_values.push_back(columns[21] + " = " + std::to_string(character_corpses_entry.deity)); update_values.push_back(columns[22] + " = " + std::to_string(character_corpses_entry.texture)); update_values.push_back(columns[23] + " = " + std::to_string(character_corpses_entry.helm_texture)); @@ -406,7 +406,7 @@ public: insert_values.push_back(std::to_string(character_corpses_entry.level)); insert_values.push_back(std::to_string(character_corpses_entry.race)); insert_values.push_back(std::to_string(character_corpses_entry.gender)); - insert_values.push_back(std::to_string(character_corpses_entry.class)); + insert_values.push_back(std::to_string(character_corpses_entry.class_)); insert_values.push_back(std::to_string(character_corpses_entry.deity)); insert_values.push_back(std::to_string(character_corpses_entry.texture)); insert_values.push_back(std::to_string(character_corpses_entry.helm_texture)); @@ -482,7 +482,7 @@ public: insert_values.push_back(std::to_string(character_corpses_entry.level)); insert_values.push_back(std::to_string(character_corpses_entry.race)); insert_values.push_back(std::to_string(character_corpses_entry.gender)); - insert_values.push_back(std::to_string(character_corpses_entry.class)); + insert_values.push_back(std::to_string(character_corpses_entry.class_)); insert_values.push_back(std::to_string(character_corpses_entry.deity)); insert_values.push_back(std::to_string(character_corpses_entry.texture)); insert_values.push_back(std::to_string(character_corpses_entry.helm_texture)); @@ -562,7 +562,7 @@ public: entry.level = atoi(row[17]); entry.race = atoi(row[18]); entry.gender = atoi(row[19]); - entry.class = atoi(row[20]); + entry.class_ = atoi(row[20]); entry.deity = atoi(row[21]); entry.texture = atoi(row[22]); entry.helm_texture = atoi(row[23]); @@ -633,7 +633,7 @@ public: entry.level = atoi(row[17]); entry.race = atoi(row[18]); entry.gender = atoi(row[19]); - entry.class = atoi(row[20]); + entry.class_ = atoi(row[20]); entry.deity = atoi(row[21]); entry.texture = atoi(row[22]); entry.helm_texture = atoi(row[23]); diff --git a/common/repositories/base/base_character_currency_repository.h b/common/repositories/base/base_character_currency_repository.h index 17134a15d..a50a4cd7b 100644 --- a/common/repositories/base/base_character_currency_repository.h +++ b/common/repositories/base/base_character_currency_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_character_data_repository.h b/common/repositories/base/base_character_data_repository.h index 623a5a875..b95d42447 100644 --- a/common/repositories/base/base_character_data_repository.h +++ b/common/repositories/base/base_character_data_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ @@ -32,7 +32,7 @@ public: float heading; int gender; int race; - int class; + int class_; int level; int deity; int birthday; @@ -72,7 +72,7 @@ public: int sta; int cha; int dex; - int int; + int int_; int agi; int wis; int zone_change_count; @@ -144,7 +144,7 @@ public: "heading", "gender", "race", - "class", + "`class`", "level", "deity", "birthday", @@ -184,7 +184,7 @@ public: "sta", "cha", "dex", - "int", + "`int`", "agi", "wis", "zone_change_count", @@ -281,7 +281,7 @@ public: entry.heading = 0; entry.gender = 0; entry.race = 0; - entry.class = 0; + entry.class_ = 0; entry.level = 0; entry.deity = 0; entry.birthday = 0; @@ -321,7 +321,7 @@ public: entry.sta = 0; entry.cha = 0; entry.dex = 0; - entry.int = 0; + entry.int_ = 0; entry.agi = 0; entry.wis = 0; entry.zone_change_count = 0; @@ -368,7 +368,7 @@ public: entry.aa_points_spent_old = 0; entry.aa_points_old = 0; entry.e_last_invsnapshot = 0; - entry.deleted_at = 0; + entry.deleted_at = ""; return entry; } @@ -418,7 +418,7 @@ public: entry.heading = static_cast(atof(row[11])); entry.gender = atoi(row[12]); entry.race = atoi(row[13]); - entry.class = atoi(row[14]); + entry.class_ = atoi(row[14]); entry.level = atoi(row[15]); entry.deity = atoi(row[16]); entry.birthday = atoi(row[17]); @@ -458,7 +458,7 @@ public: entry.sta = atoi(row[51]); entry.cha = atoi(row[52]); entry.dex = atoi(row[53]); - entry.int = atoi(row[54]); + entry.int_ = atoi(row[54]); entry.agi = atoi(row[55]); entry.wis = atoi(row[56]); entry.zone_change_count = atoi(row[57]); @@ -552,7 +552,7 @@ public: update_values.push_back(columns[11] + " = " + std::to_string(character_data_entry.heading)); update_values.push_back(columns[12] + " = " + std::to_string(character_data_entry.gender)); update_values.push_back(columns[13] + " = " + std::to_string(character_data_entry.race)); - update_values.push_back(columns[14] + " = " + std::to_string(character_data_entry.class)); + update_values.push_back(columns[14] + " = " + std::to_string(character_data_entry.class_)); update_values.push_back(columns[15] + " = " + std::to_string(character_data_entry.level)); update_values.push_back(columns[16] + " = " + std::to_string(character_data_entry.deity)); update_values.push_back(columns[17] + " = " + std::to_string(character_data_entry.birthday)); @@ -592,7 +592,7 @@ public: update_values.push_back(columns[51] + " = " + std::to_string(character_data_entry.sta)); update_values.push_back(columns[52] + " = " + std::to_string(character_data_entry.cha)); update_values.push_back(columns[53] + " = " + std::to_string(character_data_entry.dex)); - update_values.push_back(columns[54] + " = " + std::to_string(character_data_entry.int)); + update_values.push_back(columns[54] + " = " + std::to_string(character_data_entry.int_)); update_values.push_back(columns[55] + " = " + std::to_string(character_data_entry.agi)); update_values.push_back(columns[56] + " = " + std::to_string(character_data_entry.wis)); update_values.push_back(columns[57] + " = " + std::to_string(character_data_entry.zone_change_count)); @@ -675,7 +675,7 @@ public: insert_values.push_back(std::to_string(character_data_entry.heading)); insert_values.push_back(std::to_string(character_data_entry.gender)); insert_values.push_back(std::to_string(character_data_entry.race)); - insert_values.push_back(std::to_string(character_data_entry.class)); + insert_values.push_back(std::to_string(character_data_entry.class_)); insert_values.push_back(std::to_string(character_data_entry.level)); insert_values.push_back(std::to_string(character_data_entry.deity)); insert_values.push_back(std::to_string(character_data_entry.birthday)); @@ -715,7 +715,7 @@ public: insert_values.push_back(std::to_string(character_data_entry.sta)); insert_values.push_back(std::to_string(character_data_entry.cha)); insert_values.push_back(std::to_string(character_data_entry.dex)); - insert_values.push_back(std::to_string(character_data_entry.int)); + insert_values.push_back(std::to_string(character_data_entry.int_)); insert_values.push_back(std::to_string(character_data_entry.agi)); insert_values.push_back(std::to_string(character_data_entry.wis)); insert_values.push_back(std::to_string(character_data_entry.zone_change_count)); @@ -806,7 +806,7 @@ public: insert_values.push_back(std::to_string(character_data_entry.heading)); insert_values.push_back(std::to_string(character_data_entry.gender)); insert_values.push_back(std::to_string(character_data_entry.race)); - insert_values.push_back(std::to_string(character_data_entry.class)); + insert_values.push_back(std::to_string(character_data_entry.class_)); insert_values.push_back(std::to_string(character_data_entry.level)); insert_values.push_back(std::to_string(character_data_entry.deity)); insert_values.push_back(std::to_string(character_data_entry.birthday)); @@ -846,7 +846,7 @@ public: insert_values.push_back(std::to_string(character_data_entry.sta)); insert_values.push_back(std::to_string(character_data_entry.cha)); insert_values.push_back(std::to_string(character_data_entry.dex)); - insert_values.push_back(std::to_string(character_data_entry.int)); + insert_values.push_back(std::to_string(character_data_entry.int_)); insert_values.push_back(std::to_string(character_data_entry.agi)); insert_values.push_back(std::to_string(character_data_entry.wis)); insert_values.push_back(std::to_string(character_data_entry.zone_change_count)); @@ -941,7 +941,7 @@ public: entry.heading = static_cast(atof(row[11])); entry.gender = atoi(row[12]); entry.race = atoi(row[13]); - entry.class = atoi(row[14]); + entry.class_ = atoi(row[14]); entry.level = atoi(row[15]); entry.deity = atoi(row[16]); entry.birthday = atoi(row[17]); @@ -981,7 +981,7 @@ public: entry.sta = atoi(row[51]); entry.cha = atoi(row[52]); entry.dex = atoi(row[53]); - entry.int = atoi(row[54]); + entry.int_ = atoi(row[54]); entry.agi = atoi(row[55]); entry.wis = atoi(row[56]); entry.zone_change_count = atoi(row[57]); @@ -1067,7 +1067,7 @@ public: entry.heading = static_cast(atof(row[11])); entry.gender = atoi(row[12]); entry.race = atoi(row[13]); - entry.class = atoi(row[14]); + entry.class_ = atoi(row[14]); entry.level = atoi(row[15]); entry.deity = atoi(row[16]); entry.birthday = atoi(row[17]); @@ -1107,7 +1107,7 @@ public: entry.sta = atoi(row[51]); entry.cha = atoi(row[52]); entry.dex = atoi(row[53]); - entry.int = atoi(row[54]); + entry.int_ = atoi(row[54]); entry.agi = atoi(row[55]); entry.wis = atoi(row[56]); entry.zone_change_count = atoi(row[57]); diff --git a/common/repositories/base/base_character_disciplines_repository.h b/common/repositories/base/base_character_disciplines_repository.h index 941e36056..a1870e5a0 100644 --- a/common/repositories/base/base_character_disciplines_repository.h +++ b/common/repositories/base/base_character_disciplines_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_character_expedition_lockouts_repository.h b/common/repositories/base/base_character_expedition_lockouts_repository.h index d50dd1f9e..cfdc17a3b 100644 --- a/common/repositories/base/base_character_expedition_lockouts_repository.h +++ b/common/repositories/base/base_character_expedition_lockouts_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_character_inspect_messages_repository.h b/common/repositories/base/base_character_inspect_messages_repository.h index 0a8cbc3e6..d7158e9a4 100644 --- a/common/repositories/base/base_character_inspect_messages_repository.h +++ b/common/repositories/base/base_character_inspect_messages_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_character_item_recast_repository.h b/common/repositories/base/base_character_item_recast_repository.h index 433d0f0f0..fbf105e3d 100644 --- a/common/repositories/base/base_character_item_recast_repository.h +++ b/common/repositories/base/base_character_item_recast_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_character_languages_repository.h b/common/repositories/base/base_character_languages_repository.h index 64217e8d7..96bb48e8f 100644 --- a/common/repositories/base/base_character_languages_repository.h +++ b/common/repositories/base/base_character_languages_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_character_leadership_abilities_repository.h b/common/repositories/base/base_character_leadership_abilities_repository.h index ac872d88b..63ee5a0dd 100644 --- a/common/repositories/base/base_character_leadership_abilities_repository.h +++ b/common/repositories/base/base_character_leadership_abilities_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_character_material_repository.h b/common/repositories/base/base_character_material_repository.h index 1b434cf2c..723828fcc 100644 --- a/common/repositories/base/base_character_material_repository.h +++ b/common/repositories/base/base_character_material_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_character_memmed_spells_repository.h b/common/repositories/base/base_character_memmed_spells_repository.h index b47750533..846d852d5 100644 --- a/common/repositories/base/base_character_memmed_spells_repository.h +++ b/common/repositories/base/base_character_memmed_spells_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_character_pet_buffs_repository.h b/common/repositories/base/base_character_pet_buffs_repository.h index 3dc6eb689..b4ef96b0a 100644 --- a/common/repositories/base/base_character_pet_buffs_repository.h +++ b/common/repositories/base/base_character_pet_buffs_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_character_pet_info_repository.h b/common/repositories/base/base_character_pet_info_repository.h index 91f65d216..b93b2a1e5 100644 --- a/common/repositories/base/base_character_pet_info_repository.h +++ b/common/repositories/base/base_character_pet_info_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_character_pet_inventory_repository.h b/common/repositories/base/base_character_pet_inventory_repository.h index 4e7e26456..b12549dd1 100644 --- a/common/repositories/base/base_character_pet_inventory_repository.h +++ b/common/repositories/base/base_character_pet_inventory_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_character_potionbelt_repository.h b/common/repositories/base/base_character_potionbelt_repository.h index 16d1cbfcd..7606449ce 100644 --- a/common/repositories/base/base_character_potionbelt_repository.h +++ b/common/repositories/base/base_character_potionbelt_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_character_skills_repository.h b/common/repositories/base/base_character_skills_repository.h index d654fd448..16454fa21 100644 --- a/common/repositories/base/base_character_skills_repository.h +++ b/common/repositories/base/base_character_skills_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_character_spells_repository.h b/common/repositories/base/base_character_spells_repository.h index eb34b0d43..29291cf65 100644 --- a/common/repositories/base/base_character_spells_repository.h +++ b/common/repositories/base/base_character_spells_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_character_tasks_repository.h b/common/repositories/base/base_character_tasks_repository.h index 9d8371f34..58ba04194 100644 --- a/common/repositories/base/base_character_tasks_repository.h +++ b/common/repositories/base/base_character_tasks_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_completed_tasks_repository.h b/common/repositories/base/base_completed_tasks_repository.h index bee95bf79..61ec15cf2 100644 --- a/common/repositories/base/base_completed_tasks_repository.h +++ b/common/repositories/base/base_completed_tasks_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_content_flags_repository.h b/common/repositories/base/base_content_flags_repository.h index f59ad8663..3b57905ee 100644 --- a/common/repositories/base/base_content_flags_repository.h +++ b/common/repositories/base/base_content_flags_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_damageshieldtypes_repository.h b/common/repositories/base/base_damageshieldtypes_repository.h index b2c6be57e..9e08f4ed9 100644 --- a/common/repositories/base/base_damageshieldtypes_repository.h +++ b/common/repositories/base/base_damageshieldtypes_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_data_buckets_repository.h b/common/repositories/base/base_data_buckets_repository.h index cef173a69..f50e21592 100644 --- a/common/repositories/base/base_data_buckets_repository.h +++ b/common/repositories/base/base_data_buckets_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ @@ -18,7 +18,7 @@ class BaseDataBucketsRepository { public: struct DataBuckets { - int id; + int64 id; std::string key; std::string value; int expires; @@ -110,7 +110,7 @@ public: if (results.RowCount() == 1) { DataBuckets entry{}; - entry.id = atoi(row[0]); + entry.id = strtoll(row[0], NULL, 10); entry.key = row[1] ? row[1] : ""; entry.value = row[2] ? row[2] : ""; entry.expires = atoi(row[3]); @@ -241,7 +241,7 @@ public: for (auto row = results.begin(); row != results.end(); ++row) { DataBuckets entry{}; - entry.id = atoi(row[0]); + entry.id = strtoll(row[0], NULL, 10); entry.key = row[1] ? row[1] : ""; entry.value = row[2] ? row[2] : ""; entry.expires = atoi(row[3]); @@ -269,7 +269,7 @@ public: for (auto row = results.begin(); row != results.end(); ++row) { DataBuckets entry{}; - entry.id = atoi(row[0]); + entry.id = strtoll(row[0], NULL, 10); entry.key = row[1] ? row[1] : ""; entry.value = row[2] ? row[2] : ""; entry.expires = atoi(row[3]); diff --git a/common/repositories/base/base_db_str_repository.h b/common/repositories/base/base_db_str_repository.h index f9a827d95..e94b29772 100644 --- a/common/repositories/base/base_db_str_repository.h +++ b/common/repositories/base/base_db_str_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_discovered_items_repository.h b/common/repositories/base/base_discovered_items_repository.h index 555566f9d..03affdd09 100644 --- a/common/repositories/base/base_discovered_items_repository.h +++ b/common/repositories/base/base_discovered_items_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_doors_repository.h b/common/repositories/base/base_doors_repository.h index 75d740742..bcb0d5f49 100644 --- a/common/repositories/base/base_doors_repository.h +++ b/common/repositories/base/base_doors_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ @@ -53,7 +53,6 @@ public: int max_expansion; std::string content_flags; std::string content_flags_disabled; - int is_instance_door; }; static std::string PrimaryKey() @@ -99,7 +98,6 @@ public: "max_expansion", "content_flags", "content_flags_disabled", - "is_instance_door", }; } @@ -170,7 +168,6 @@ public: entry.max_expansion = 0; entry.content_flags = ""; entry.content_flags_disabled = ""; - entry.is_instance_door = 0; return entry; } @@ -241,7 +238,6 @@ public: entry.max_expansion = atoi(row[32]); entry.content_flags = row[33] ? row[33] : ""; entry.content_flags_disabled = row[34] ? row[34] : ""; - entry.is_instance_door = atoi(row[35]); return entry; } @@ -309,7 +305,6 @@ public: update_values.push_back(columns[32] + " = " + std::to_string(doors_entry.max_expansion)); update_values.push_back(columns[33] + " = '" + EscapeString(doors_entry.content_flags) + "'"); update_values.push_back(columns[34] + " = '" + EscapeString(doors_entry.content_flags_disabled) + "'"); - update_values.push_back(columns[35] + " = " + std::to_string(doors_entry.is_instance_door)); auto results = db.QueryDatabase( fmt::format( @@ -366,7 +361,6 @@ public: insert_values.push_back(std::to_string(doors_entry.max_expansion)); insert_values.push_back("'" + EscapeString(doors_entry.content_flags) + "'"); insert_values.push_back("'" + EscapeString(doors_entry.content_flags_disabled) + "'"); - insert_values.push_back(std::to_string(doors_entry.is_instance_door)); auto results = db.QueryDatabase( fmt::format( @@ -431,7 +425,6 @@ public: insert_values.push_back(std::to_string(doors_entry.max_expansion)); insert_values.push_back("'" + EscapeString(doors_entry.content_flags) + "'"); insert_values.push_back("'" + EscapeString(doors_entry.content_flags_disabled) + "'"); - insert_values.push_back(std::to_string(doors_entry.is_instance_door)); insert_chunks.push_back("(" + implode(",", insert_values) + ")"); } @@ -500,7 +493,6 @@ public: entry.max_expansion = atoi(row[32]); entry.content_flags = row[33] ? row[33] : ""; entry.content_flags_disabled = row[34] ? row[34] : ""; - entry.is_instance_door = atoi(row[35]); all_entries.push_back(entry); } @@ -560,7 +552,6 @@ public: entry.max_expansion = atoi(row[32]); entry.content_flags = row[33] ? row[33] : ""; entry.content_flags_disabled = row[34] ? row[34] : ""; - entry.is_instance_door = atoi(row[35]); all_entries.push_back(entry); } diff --git a/common/repositories/base/base_dynamic_zones_repository.h b/common/repositories/base/base_dynamic_zones_repository.h index 13aabb07b..283ecd754 100644 --- a/common/repositories/base/base_dynamic_zones_repository.h +++ b/common/repositories/base/base_dynamic_zones_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_eventlog_repository.h b/common/repositories/base/base_eventlog_repository.h index 999fb455b..25b6ea1cc 100644 --- a/common/repositories/base/base_eventlog_repository.h +++ b/common/repositories/base/base_eventlog_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ @@ -89,7 +89,7 @@ public: entry.status = 0; entry.charname = ""; entry.target = "None"; - entry.time = current_timestamp(); + entry.time = ""; entry.descriptiontype = ""; entry.description = ""; entry.event_nid = 0; diff --git a/common/repositories/base/base_expedition_lockouts_repository.h b/common/repositories/base/base_expedition_lockouts_repository.h index e9c5ceda2..68a606923 100644 --- a/common/repositories/base/base_expedition_lockouts_repository.h +++ b/common/repositories/base/base_expedition_lockouts_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_expeditions_repository.h b/common/repositories/base/base_expeditions_repository.h index 8fea9a698..fb8007339 100644 --- a/common/repositories/base/base_expeditions_repository.h +++ b/common/repositories/base/base_expeditions_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_faction_base_data_repository.h b/common/repositories/base/base_faction_base_data_repository.h index c5bfc8c43..9357d4ce8 100644 --- a/common/repositories/base/base_faction_base_data_repository.h +++ b/common/repositories/base/base_faction_base_data_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_faction_list_mod_repository.h b/common/repositories/base/base_faction_list_mod_repository.h index 673c6ac96..0181f5ba3 100644 --- a/common/repositories/base/base_faction_list_mod_repository.h +++ b/common/repositories/base/base_faction_list_mod_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_faction_list_repository.h b/common/repositories/base/base_faction_list_repository.h index 32e20bb6c..f985d7aad 100644 --- a/common/repositories/base/base_faction_list_repository.h +++ b/common/repositories/base/base_faction_list_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_faction_values_repository.h b/common/repositories/base/base_faction_values_repository.h index 181779da3..2176d287b 100644 --- a/common/repositories/base/base_faction_values_repository.h +++ b/common/repositories/base/base_faction_values_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_fishing_repository.h b/common/repositories/base/base_fishing_repository.h index 6ee2bf098..2c6ad7b43 100644 --- a/common/repositories/base/base_fishing_repository.h +++ b/common/repositories/base/base_fishing_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_forage_repository.h b/common/repositories/base/base_forage_repository.h index 0aa684b1b..4f80658cc 100644 --- a/common/repositories/base/base_forage_repository.h +++ b/common/repositories/base/base_forage_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_friends_repository.h b/common/repositories/base/base_friends_repository.h index 187994a85..a01746174 100644 --- a/common/repositories/base/base_friends_repository.h +++ b/common/repositories/base/base_friends_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_global_loot_repository.h b/common/repositories/base/base_global_loot_repository.h index 98170acf5..81ee74549 100644 --- a/common/repositories/base/base_global_loot_repository.h +++ b/common/repositories/base/base_global_loot_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ @@ -27,7 +27,7 @@ public: int rare; int raid; std::string race; - std::string class; + std::string class_; std::string bodytype; std::string zone; int hot_zone; @@ -54,7 +54,7 @@ public: "rare", "raid", "race", - "class", + "`class`", "bodytype", "zone", "hot_zone", @@ -106,7 +106,7 @@ public: entry.rare = 0; entry.raid = 0; entry.race = ""; - entry.class = ""; + entry.class_ = ""; entry.bodytype = ""; entry.zone = ""; entry.hot_zone = 0; @@ -158,7 +158,7 @@ public: entry.rare = atoi(row[6]); entry.raid = atoi(row[7]); entry.race = row[8] ? row[8] : ""; - entry.class = row[9] ? row[9] : ""; + entry.class_ = row[9] ? row[9] : ""; entry.bodytype = row[10] ? row[10] : ""; entry.zone = row[11] ? row[11] : ""; entry.hot_zone = atoi(row[12]); @@ -207,7 +207,7 @@ public: update_values.push_back(columns[6] + " = " + std::to_string(global_loot_entry.rare)); update_values.push_back(columns[7] + " = " + std::to_string(global_loot_entry.raid)); update_values.push_back(columns[8] + " = '" + EscapeString(global_loot_entry.race) + "'"); - update_values.push_back(columns[9] + " = '" + EscapeString(global_loot_entry.class) + "'"); + update_values.push_back(columns[9] + " = '" + EscapeString(global_loot_entry.class_) + "'"); update_values.push_back(columns[10] + " = '" + EscapeString(global_loot_entry.bodytype) + "'"); update_values.push_back(columns[11] + " = '" + EscapeString(global_loot_entry.zone) + "'"); update_values.push_back(columns[12] + " = " + std::to_string(global_loot_entry.hot_zone)); @@ -245,7 +245,7 @@ public: insert_values.push_back(std::to_string(global_loot_entry.rare)); insert_values.push_back(std::to_string(global_loot_entry.raid)); insert_values.push_back("'" + EscapeString(global_loot_entry.race) + "'"); - insert_values.push_back("'" + EscapeString(global_loot_entry.class) + "'"); + insert_values.push_back("'" + EscapeString(global_loot_entry.class_) + "'"); insert_values.push_back("'" + EscapeString(global_loot_entry.bodytype) + "'"); insert_values.push_back("'" + EscapeString(global_loot_entry.zone) + "'"); insert_values.push_back(std::to_string(global_loot_entry.hot_zone)); @@ -291,7 +291,7 @@ public: insert_values.push_back(std::to_string(global_loot_entry.rare)); insert_values.push_back(std::to_string(global_loot_entry.raid)); insert_values.push_back("'" + EscapeString(global_loot_entry.race) + "'"); - insert_values.push_back("'" + EscapeString(global_loot_entry.class) + "'"); + insert_values.push_back("'" + EscapeString(global_loot_entry.class_) + "'"); insert_values.push_back("'" + EscapeString(global_loot_entry.bodytype) + "'"); insert_values.push_back("'" + EscapeString(global_loot_entry.zone) + "'"); insert_values.push_back(std::to_string(global_loot_entry.hot_zone)); @@ -341,7 +341,7 @@ public: entry.rare = atoi(row[6]); entry.raid = atoi(row[7]); entry.race = row[8] ? row[8] : ""; - entry.class = row[9] ? row[9] : ""; + entry.class_ = row[9] ? row[9] : ""; entry.bodytype = row[10] ? row[10] : ""; entry.zone = row[11] ? row[11] : ""; entry.hot_zone = atoi(row[12]); @@ -382,7 +382,7 @@ public: entry.rare = atoi(row[6]); entry.raid = atoi(row[7]); entry.race = row[8] ? row[8] : ""; - entry.class = row[9] ? row[9] : ""; + entry.class_ = row[9] ? row[9] : ""; entry.bodytype = row[10] ? row[10] : ""; entry.zone = row[11] ? row[11] : ""; entry.hot_zone = atoi(row[12]); diff --git a/common/repositories/base/base_gm_ips_repository.h b/common/repositories/base/base_gm_ips_repository.h index 56027c7a2..5fa6b1f54 100644 --- a/common/repositories/base/base_gm_ips_repository.h +++ b/common/repositories/base/base_gm_ips_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_goallists_repository.h b/common/repositories/base/base_goallists_repository.h index c736d9e01..ff937c169 100644 --- a/common/repositories/base/base_goallists_repository.h +++ b/common/repositories/base/base_goallists_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_graveyard_repository.h b/common/repositories/base/base_graveyard_repository.h index 500775a94..f104e2236 100644 --- a/common/repositories/base/base_graveyard_repository.h +++ b/common/repositories/base/base_graveyard_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_ground_spawns_repository.h b/common/repositories/base/base_ground_spawns_repository.h index 6b2a0d5cc..4d8b9168d 100644 --- a/common/repositories/base/base_ground_spawns_repository.h +++ b/common/repositories/base/base_ground_spawns_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_group_id_repository.h b/common/repositories/base/base_group_id_repository.h index 8005f003e..313bfffbe 100644 --- a/common/repositories/base/base_group_id_repository.h +++ b/common/repositories/base/base_group_id_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_group_leaders_repository.h b/common/repositories/base/base_group_leaders_repository.h index 97e7028cd..d549adaaf 100644 --- a/common/repositories/base/base_group_leaders_repository.h +++ b/common/repositories/base/base_group_leaders_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_guild_members_repository.h b/common/repositories/base/base_guild_members_repository.h index 931d0bdf6..577817241 100644 --- a/common/repositories/base/base_guild_members_repository.h +++ b/common/repositories/base/base_guild_members_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_guild_ranks_repository.h b/common/repositories/base/base_guild_ranks_repository.h index b608cfaa5..cbf7763f7 100644 --- a/common/repositories/base/base_guild_ranks_repository.h +++ b/common/repositories/base/base_guild_ranks_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_guild_relations_repository.h b/common/repositories/base/base_guild_relations_repository.h index 2ddcd854f..5afe63413 100644 --- a/common/repositories/base/base_guild_relations_repository.h +++ b/common/repositories/base/base_guild_relations_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_guilds_repository.h b/common/repositories/base/base_guilds_repository.h index e1cd14f14..b38f223fc 100644 --- a/common/repositories/base/base_guilds_repository.h +++ b/common/repositories/base/base_guilds_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_hackers_repository.h b/common/repositories/base/base_hackers_repository.h index 4a492deac..3d25dc90a 100644 --- a/common/repositories/base/base_hackers_repository.h +++ b/common/repositories/base/base_hackers_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ @@ -80,7 +80,7 @@ public: entry.name = ""; entry.hacked = ""; entry.zone = ""; - entry.date = current_timestamp(); + entry.date = ""; return entry; } diff --git a/common/repositories/base/base_instance_list_player_repository.h b/common/repositories/base/base_instance_list_player_repository.h index 4b8aed4ee..92e4687b1 100644 --- a/common/repositories/base/base_instance_list_player_repository.h +++ b/common/repositories/base/base_instance_list_player_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_instance_list_repository.h b/common/repositories/base/base_instance_list_repository.h index 55b31703f..bdf7abfa6 100644 --- a/common/repositories/base/base_instance_list_repository.h +++ b/common/repositories/base/base_instance_list_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_inventory_repository.h b/common/repositories/base/base_inventory_repository.h index 62308ebdf..d17c7ff62 100644 --- a/common/repositories/base/base_inventory_repository.h +++ b/common/repositories/base/base_inventory_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_inventory_snapshots_repository.h b/common/repositories/base/base_inventory_snapshots_repository.h index 4d34062f3..ec91303b1 100644 --- a/common/repositories/base/base_inventory_snapshots_repository.h +++ b/common/repositories/base/base_inventory_snapshots_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_ip_exemptions_repository.h b/common/repositories/base/base_ip_exemptions_repository.h index dadd49b8e..6952c4dab 100644 --- a/common/repositories/base/base_ip_exemptions_repository.h +++ b/common/repositories/base/base_ip_exemptions_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_item_tick_repository.h b/common/repositories/base/base_item_tick_repository.h index 77b0ec71e..2579c3c40 100644 --- a/common/repositories/base/base_item_tick_repository.h +++ b/common/repositories/base/base_item_tick_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_items_repository.h b/common/repositories/base/base_items_repository.h index da124eca0..06a7797b8 100644 --- a/common/repositories/base/base_items_repository.h +++ b/common/repositories/base/base_items_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ @@ -797,8 +797,8 @@ public: entry.scrolllevel2 = 0; entry.scrolllevel = 0; entry.UNK157 = 0; - entry.serialized = 0; - entry.verified = 0; + entry.serialized = ""; + entry.verified = ""; entry.serialization = ""; entry.source = ""; entry.UNK033 = 0; diff --git a/common/repositories/base/base_ldon_trap_entries_repository.h b/common/repositories/base/base_ldon_trap_entries_repository.h index 05fc73bf5..a9e8b9052 100644 --- a/common/repositories/base/base_ldon_trap_entries_repository.h +++ b/common/repositories/base/base_ldon_trap_entries_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_ldon_trap_templates_repository.h b/common/repositories/base/base_ldon_trap_templates_repository.h index 27def1890..022482232 100644 --- a/common/repositories/base/base_ldon_trap_templates_repository.h +++ b/common/repositories/base/base_ldon_trap_templates_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_level_exp_mods_repository.h b/common/repositories/base/base_level_exp_mods_repository.h index eaa4a5f4a..410d4d25c 100644 --- a/common/repositories/base/base_level_exp_mods_repository.h +++ b/common/repositories/base/base_level_exp_mods_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_lfguild_repository.h b/common/repositories/base/base_lfguild_repository.h index 10e2f0682..be7337579 100644 --- a/common/repositories/base/base_lfguild_repository.h +++ b/common/repositories/base/base_lfguild_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_login_accounts_repository.h b/common/repositories/base/base_login_accounts_repository.h index 25dc167d1..7b2e68f11 100644 --- a/common/repositories/base/base_login_accounts_repository.h +++ b/common/repositories/base/base_login_accounts_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ @@ -87,9 +87,9 @@ public: entry.account_email = ""; entry.source_loginserver = ""; entry.last_ip_address = ""; - entry.last_login_date = 0; - entry.created_at = 0; - entry.updated_at = current_timestamp(); + entry.last_login_date = ""; + entry.created_at = ""; + entry.updated_at = ""; return entry; } diff --git a/common/repositories/base/base_login_api_tokens_repository.h b/common/repositories/base/base_login_api_tokens_repository.h index 88ed5c74a..7d5f7f98f 100644 --- a/common/repositories/base/base_login_api_tokens_repository.h +++ b/common/repositories/base/base_login_api_tokens_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ @@ -79,8 +79,8 @@ public: entry.token = ""; entry.can_write = 0; entry.can_read = 0; - entry.created_at = 0; - entry.updated_at = current_timestamp(); + entry.created_at = ""; + entry.updated_at = ""; return entry; } diff --git a/common/repositories/base/base_login_server_admins_repository.h b/common/repositories/base/base_login_server_admins_repository.h index 771eb014f..be6b3bcf7 100644 --- a/common/repositories/base/base_login_server_admins_repository.h +++ b/common/repositories/base/base_login_server_admins_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ @@ -85,7 +85,7 @@ public: entry.first_name = ""; entry.last_name = ""; entry.email = ""; - entry.registration_date = 0; + entry.registration_date = ""; entry.registration_ip_address = ""; return entry; diff --git a/common/repositories/base/base_login_server_list_types_repository.h b/common/repositories/base/base_login_server_list_types_repository.h index 16b47381c..443182c05 100644 --- a/common/repositories/base/base_login_server_list_types_repository.h +++ b/common/repositories/base/base_login_server_list_types_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_login_world_servers_repository.h b/common/repositories/base/base_login_world_servers_repository.h index 96dc1f8ae..cce6a0bff 100644 --- a/common/repositories/base/base_login_world_servers_repository.h +++ b/common/repositories/base/base_login_world_servers_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ @@ -88,7 +88,7 @@ public: entry.short_name = ""; entry.tag_description = ""; entry.login_server_list_type_id = 0; - entry.last_login_date = 0; + entry.last_login_date = ""; entry.last_ip_address = ""; entry.login_server_admin_id = 0; entry.is_server_trusted = 0; diff --git a/common/repositories/base/base_lootdrop_entries_repository.h b/common/repositories/base/base_lootdrop_entries_repository.h index 524b00588..4be4630a3 100644 --- a/common/repositories/base/base_lootdrop_entries_repository.h +++ b/common/repositories/base/base_lootdrop_entries_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_lootdrop_repository.h b/common/repositories/base/base_lootdrop_repository.h index abacd5f8b..d70c3201c 100644 --- a/common/repositories/base/base_lootdrop_repository.h +++ b/common/repositories/base/base_lootdrop_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_loottable_entries_repository.h b/common/repositories/base/base_loottable_entries_repository.h index e8143e7e3..67e073be7 100644 --- a/common/repositories/base/base_loottable_entries_repository.h +++ b/common/repositories/base/base_loottable_entries_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_loottable_repository.h b/common/repositories/base/base_loottable_repository.h index 88ff30ec9..a41229069 100644 --- a/common/repositories/base/base_loottable_repository.h +++ b/common/repositories/base/base_loottable_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_mail_repository.h b/common/repositories/base/base_mail_repository.h index 674881b64..2123e6396 100644 --- a/common/repositories/base/base_mail_repository.h +++ b/common/repositories/base/base_mail_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_merchantlist_repository.h b/common/repositories/base/base_merchantlist_repository.h index 437b419cd..f7dc02df6 100644 --- a/common/repositories/base/base_merchantlist_repository.h +++ b/common/repositories/base/base_merchantlist_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_merchantlist_temp_repository.h b/common/repositories/base/base_merchantlist_temp_repository.h index 1db32b796..21626aa3d 100644 --- a/common/repositories/base/base_merchantlist_temp_repository.h +++ b/common/repositories/base/base_merchantlist_temp_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_name_filter_repository.h b/common/repositories/base/base_name_filter_repository.h index 5f9c19a8b..984f2b142 100644 --- a/common/repositories/base/base_name_filter_repository.h +++ b/common/repositories/base/base_name_filter_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_npc_emotes_repository.h b/common/repositories/base/base_npc_emotes_repository.h index cc0503d16..6660ed59e 100644 --- a/common/repositories/base/base_npc_emotes_repository.h +++ b/common/repositories/base/base_npc_emotes_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_npc_faction_entries_repository.h b/common/repositories/base/base_npc_faction_entries_repository.h index a25a9a258..1084b9a5e 100644 --- a/common/repositories/base/base_npc_faction_entries_repository.h +++ b/common/repositories/base/base_npc_faction_entries_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_npc_faction_repository.h b/common/repositories/base/base_npc_faction_repository.h index 5cb58fafa..87bff25d1 100644 --- a/common/repositories/base/base_npc_faction_repository.h +++ b/common/repositories/base/base_npc_faction_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_npc_scale_global_base_repository.h b/common/repositories/base/base_npc_scale_global_base_repository.h index e701c7686..b7eafe57a 100644 --- a/common/repositories/base/base_npc_scale_global_base_repository.h +++ b/common/repositories/base/base_npc_scale_global_base_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_npc_spells_effects_entries_repository.h b/common/repositories/base/base_npc_spells_effects_entries_repository.h index 563176079..f6ca532ee 100644 --- a/common/repositories/base/base_npc_spells_effects_entries_repository.h +++ b/common/repositories/base/base_npc_spells_effects_entries_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_npc_spells_effects_repository.h b/common/repositories/base/base_npc_spells_effects_repository.h index a6b5e52bd..6983a002e 100644 --- a/common/repositories/base/base_npc_spells_effects_repository.h +++ b/common/repositories/base/base_npc_spells_effects_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_npc_spells_entries_repository.h b/common/repositories/base/base_npc_spells_entries_repository.h index 97139ab2b..a165d6965 100644 --- a/common/repositories/base/base_npc_spells_entries_repository.h +++ b/common/repositories/base/base_npc_spells_entries_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_npc_spells_repository.h b/common/repositories/base/base_npc_spells_repository.h index 448683064..e7f9d784e 100644 --- a/common/repositories/base/base_npc_spells_repository.h +++ b/common/repositories/base/base_npc_spells_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_npc_types_repository.h b/common/repositories/base/base_npc_types_repository.h index b127988fa..25020d8a8 100644 --- a/common/repositories/base/base_npc_types_repository.h +++ b/common/repositories/base/base_npc_types_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ @@ -23,7 +23,7 @@ public: std::string lastname; int level; int race; - int class; + int class_; int bodytype; int hp; int mana; @@ -140,6 +140,7 @@ public: int model; int flymode; int always_aggro; + int exp_mod; }; static std::string PrimaryKey() @@ -155,7 +156,7 @@ public: "lastname", "level", "race", - "class", + "`class`", "bodytype", "hp", "mana", @@ -272,6 +273,7 @@ public: "model", "flymode", "always_aggro", + "exp_mod", }; } @@ -312,7 +314,7 @@ public: entry.lastname = ""; entry.level = 0; entry.race = 0; - entry.class = 0; + entry.class_ = 0; entry.bodytype = 1; entry.hp = 0; entry.mana = 0; @@ -429,6 +431,7 @@ public: entry.model = 0; entry.flymode = -1; entry.always_aggro = 0; + entry.exp_mod = 100; return entry; } @@ -469,7 +472,7 @@ public: entry.lastname = row[2] ? row[2] : ""; entry.level = atoi(row[3]); entry.race = atoi(row[4]); - entry.class = atoi(row[5]); + entry.class_ = atoi(row[5]); entry.bodytype = atoi(row[6]); entry.hp = atoi(row[7]); entry.mana = atoi(row[8]); @@ -586,6 +589,7 @@ public: entry.model = atoi(row[119]); entry.flymode = atoi(row[120]); entry.always_aggro = atoi(row[121]); + entry.exp_mod = atoi(row[122]); return entry; } @@ -623,7 +627,7 @@ public: update_values.push_back(columns[2] + " = '" + EscapeString(npc_types_entry.lastname) + "'"); update_values.push_back(columns[3] + " = " + std::to_string(npc_types_entry.level)); update_values.push_back(columns[4] + " = " + std::to_string(npc_types_entry.race)); - update_values.push_back(columns[5] + " = " + std::to_string(npc_types_entry.class)); + update_values.push_back(columns[5] + " = " + std::to_string(npc_types_entry.class_)); update_values.push_back(columns[6] + " = " + std::to_string(npc_types_entry.bodytype)); update_values.push_back(columns[7] + " = " + std::to_string(npc_types_entry.hp)); update_values.push_back(columns[8] + " = " + std::to_string(npc_types_entry.mana)); @@ -740,6 +744,7 @@ public: update_values.push_back(columns[119] + " = " + std::to_string(npc_types_entry.model)); update_values.push_back(columns[120] + " = " + std::to_string(npc_types_entry.flymode)); update_values.push_back(columns[121] + " = " + std::to_string(npc_types_entry.always_aggro)); + update_values.push_back(columns[122] + " = " + std::to_string(npc_types_entry.exp_mod)); auto results = db.QueryDatabase( fmt::format( @@ -766,7 +771,7 @@ public: insert_values.push_back("'" + EscapeString(npc_types_entry.lastname) + "'"); insert_values.push_back(std::to_string(npc_types_entry.level)); insert_values.push_back(std::to_string(npc_types_entry.race)); - insert_values.push_back(std::to_string(npc_types_entry.class)); + insert_values.push_back(std::to_string(npc_types_entry.class_)); insert_values.push_back(std::to_string(npc_types_entry.bodytype)); insert_values.push_back(std::to_string(npc_types_entry.hp)); insert_values.push_back(std::to_string(npc_types_entry.mana)); @@ -883,6 +888,7 @@ public: insert_values.push_back(std::to_string(npc_types_entry.model)); insert_values.push_back(std::to_string(npc_types_entry.flymode)); insert_values.push_back(std::to_string(npc_types_entry.always_aggro)); + insert_values.push_back(std::to_string(npc_types_entry.exp_mod)); auto results = db.QueryDatabase( fmt::format( @@ -917,7 +923,7 @@ public: insert_values.push_back("'" + EscapeString(npc_types_entry.lastname) + "'"); insert_values.push_back(std::to_string(npc_types_entry.level)); insert_values.push_back(std::to_string(npc_types_entry.race)); - insert_values.push_back(std::to_string(npc_types_entry.class)); + insert_values.push_back(std::to_string(npc_types_entry.class_)); insert_values.push_back(std::to_string(npc_types_entry.bodytype)); insert_values.push_back(std::to_string(npc_types_entry.hp)); insert_values.push_back(std::to_string(npc_types_entry.mana)); @@ -1034,6 +1040,7 @@ public: insert_values.push_back(std::to_string(npc_types_entry.model)); insert_values.push_back(std::to_string(npc_types_entry.flymode)); insert_values.push_back(std::to_string(npc_types_entry.always_aggro)); + insert_values.push_back(std::to_string(npc_types_entry.exp_mod)); insert_chunks.push_back("(" + implode(",", insert_values) + ")"); } @@ -1072,7 +1079,7 @@ public: entry.lastname = row[2] ? row[2] : ""; entry.level = atoi(row[3]); entry.race = atoi(row[4]); - entry.class = atoi(row[5]); + entry.class_ = atoi(row[5]); entry.bodytype = atoi(row[6]); entry.hp = atoi(row[7]); entry.mana = atoi(row[8]); @@ -1189,6 +1196,7 @@ public: entry.model = atoi(row[119]); entry.flymode = atoi(row[120]); entry.always_aggro = atoi(row[121]); + entry.exp_mod = atoi(row[122]); all_entries.push_back(entry); } @@ -1218,7 +1226,7 @@ public: entry.lastname = row[2] ? row[2] : ""; entry.level = atoi(row[3]); entry.race = atoi(row[4]); - entry.class = atoi(row[5]); + entry.class_ = atoi(row[5]); entry.bodytype = atoi(row[6]); entry.hp = atoi(row[7]); entry.mana = atoi(row[8]); @@ -1335,6 +1343,7 @@ public: entry.model = atoi(row[119]); entry.flymode = atoi(row[120]); entry.always_aggro = atoi(row[121]); + entry.exp_mod = atoi(row[122]); all_entries.push_back(entry); } diff --git a/common/repositories/base/base_npc_types_tint_repository.h b/common/repositories/base/base_npc_types_tint_repository.h index 3ba577f36..ea01c8d01 100644 --- a/common/repositories/base/base_npc_types_tint_repository.h +++ b/common/repositories/base/base_npc_types_tint_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_object_contents_repository.h b/common/repositories/base/base_object_contents_repository.h index 576429ba0..3ab7c70b8 100644 --- a/common/repositories/base/base_object_contents_repository.h +++ b/common/repositories/base/base_object_contents_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_object_repository.h b/common/repositories/base/base_object_repository.h index a572ed712..dc098db06 100644 --- a/common/repositories/base/base_object_repository.h +++ b/common/repositories/base/base_object_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_perl_event_export_settings_repository.h b/common/repositories/base/base_perl_event_export_settings_repository.h index d58084463..50c0a63f6 100644 --- a/common/repositories/base/base_perl_event_export_settings_repository.h +++ b/common/repositories/base/base_perl_event_export_settings_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_petitions_repository.h b/common/repositories/base/base_petitions_repository.h index 30dd9feaa..36a781ab0 100644 --- a/common/repositories/base/base_petitions_repository.h +++ b/common/repositories/base/base_petitions_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ @@ -33,7 +33,7 @@ public: int checkouts; int unavailables; int ischeckedout; - int senttime; + int64 senttime; }; static std::string PrimaryKey() @@ -161,7 +161,7 @@ public: entry.checkouts = atoi(row[12]); entry.unavailables = atoi(row[13]); entry.ischeckedout = atoi(row[14]); - entry.senttime = atoi(row[15]); + entry.senttime = strtoll(row[15], NULL, 10); return entry; } @@ -340,7 +340,7 @@ public: entry.checkouts = atoi(row[12]); entry.unavailables = atoi(row[13]); entry.ischeckedout = atoi(row[14]); - entry.senttime = atoi(row[15]); + entry.senttime = strtoll(row[15], NULL, 10); all_entries.push_back(entry); } @@ -380,7 +380,7 @@ public: entry.checkouts = atoi(row[12]); entry.unavailables = atoi(row[13]); entry.ischeckedout = atoi(row[14]); - entry.senttime = atoi(row[15]); + entry.senttime = strtoll(row[15], NULL, 10); all_entries.push_back(entry); } diff --git a/common/repositories/base/base_pets_beastlord_data_repository.h b/common/repositories/base/base_pets_beastlord_data_repository.h new file mode 100644 index 000000000..61c7d75ba --- /dev/null +++ b/common/repositories/base/base_pets_beastlord_data_repository.h @@ -0,0 +1,338 @@ +/** + * DO NOT MODIFY THIS FILE + * + * This repository was automatically generated and is NOT to be modified directly. + * Any repository modifications are meant to be made to the repository extending the base. + * Any modifications to base repositories are to be made by the generator only + * + * @generator ./utils/scripts/generators/repository-generator.pl + * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories + */ + +#ifndef EQEMU_BASE_PETS_BEASTLORD_DATA_REPOSITORY_H +#define EQEMU_BASE_PETS_BEASTLORD_DATA_REPOSITORY_H + +#include "../../database.h" +#include "../../string_util.h" + +class BasePetsBeastlordDataRepository { +public: + struct PetsBeastlordData { + int player_race; + int pet_race; + int texture; + int helm_texture; + int gender; + float size_modifier; + int face; + }; + + static std::string PrimaryKey() + { + return std::string("player_race"); + } + + static std::vector Columns() + { + return { + "player_race", + "pet_race", + "texture", + "helm_texture", + "gender", + "size_modifier", + "face", + }; + } + + static std::string ColumnsRaw() + { + return std::string(implode(", ", Columns())); + } + + static std::string TableName() + { + return std::string("pets_beastlord_data"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + ColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static PetsBeastlordData NewEntity() + { + PetsBeastlordData entry{}; + + entry.player_race = 1; + entry.pet_race = 42; + entry.texture = 0; + entry.helm_texture = 0; + entry.gender = 2; + entry.size_modifier = 1; + entry.face = 0; + + return entry; + } + + static PetsBeastlordData GetPetsBeastlordDataEntry( + const std::vector &pets_beastlord_datas, + int pets_beastlord_data_id + ) + { + for (auto &pets_beastlord_data : pets_beastlord_datas) { + if (pets_beastlord_data.player_race == pets_beastlord_data_id) { + return pets_beastlord_data; + } + } + + return NewEntity(); + } + + static PetsBeastlordData FindOne( + Database& db, + int pets_beastlord_data_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE id = {} LIMIT 1", + BaseSelect(), + pets_beastlord_data_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + PetsBeastlordData entry{}; + + entry.player_race = atoi(row[0]); + entry.pet_race = atoi(row[1]); + entry.texture = atoi(row[2]); + entry.helm_texture = atoi(row[3]); + entry.gender = atoi(row[4]); + entry.size_modifier = static_cast(atof(row[5])); + entry.face = atoi(row[6]); + + return entry; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int pets_beastlord_data_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + pets_beastlord_data_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + PetsBeastlordData pets_beastlord_data_entry + ) + { + std::vector update_values; + + auto columns = Columns(); + + update_values.push_back(columns[0] + " = " + std::to_string(pets_beastlord_data_entry.player_race)); + update_values.push_back(columns[1] + " = " + std::to_string(pets_beastlord_data_entry.pet_race)); + update_values.push_back(columns[2] + " = " + std::to_string(pets_beastlord_data_entry.texture)); + update_values.push_back(columns[3] + " = " + std::to_string(pets_beastlord_data_entry.helm_texture)); + update_values.push_back(columns[4] + " = " + std::to_string(pets_beastlord_data_entry.gender)); + update_values.push_back(columns[5] + " = " + std::to_string(pets_beastlord_data_entry.size_modifier)); + update_values.push_back(columns[6] + " = " + std::to_string(pets_beastlord_data_entry.face)); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + implode(", ", update_values), + PrimaryKey(), + pets_beastlord_data_entry.player_race + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static PetsBeastlordData InsertOne( + Database& db, + PetsBeastlordData pets_beastlord_data_entry + ) + { + std::vector insert_values; + + insert_values.push_back(std::to_string(pets_beastlord_data_entry.player_race)); + insert_values.push_back(std::to_string(pets_beastlord_data_entry.pet_race)); + insert_values.push_back(std::to_string(pets_beastlord_data_entry.texture)); + insert_values.push_back(std::to_string(pets_beastlord_data_entry.helm_texture)); + insert_values.push_back(std::to_string(pets_beastlord_data_entry.gender)); + insert_values.push_back(std::to_string(pets_beastlord_data_entry.size_modifier)); + insert_values.push_back(std::to_string(pets_beastlord_data_entry.face)); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseInsert(), + implode(",", insert_values) + ) + ); + + if (results.Success()) { + pets_beastlord_data_entry.player_race = results.LastInsertedID(); + return pets_beastlord_data_entry; + } + + pets_beastlord_data_entry = NewEntity(); + + return pets_beastlord_data_entry; + } + + static int InsertMany( + Database& db, + std::vector pets_beastlord_data_entries + ) + { + std::vector insert_chunks; + + for (auto &pets_beastlord_data_entry: pets_beastlord_data_entries) { + std::vector insert_values; + + insert_values.push_back(std::to_string(pets_beastlord_data_entry.player_race)); + insert_values.push_back(std::to_string(pets_beastlord_data_entry.pet_race)); + insert_values.push_back(std::to_string(pets_beastlord_data_entry.texture)); + insert_values.push_back(std::to_string(pets_beastlord_data_entry.helm_texture)); + insert_values.push_back(std::to_string(pets_beastlord_data_entry.gender)); + insert_values.push_back(std::to_string(pets_beastlord_data_entry.size_modifier)); + insert_values.push_back(std::to_string(pets_beastlord_data_entry.face)); + + insert_chunks.push_back("(" + implode(",", insert_values) + ")"); + } + + std::vector insert_values; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseInsert(), + implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static std::vector All(Database& db) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{}", + BaseSelect() + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + PetsBeastlordData entry{}; + + entry.player_race = atoi(row[0]); + entry.pet_race = atoi(row[1]); + entry.texture = atoi(row[2]); + entry.helm_texture = atoi(row[3]); + entry.gender = atoi(row[4]); + entry.size_modifier = static_cast(atof(row[5])); + entry.face = atoi(row[6]); + + all_entries.push_back(entry); + } + + return all_entries; + } + + static std::vector GetWhere(Database& db, std::string where_filter) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {}", + BaseSelect(), + where_filter + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + PetsBeastlordData entry{}; + + entry.player_race = atoi(row[0]); + entry.pet_race = atoi(row[1]); + entry.texture = atoi(row[2]); + entry.helm_texture = atoi(row[3]); + entry.gender = atoi(row[4]); + entry.size_modifier = static_cast(atof(row[5])); + entry.face = atoi(row[6]); + + all_entries.push_back(entry); + } + + return all_entries; + } + + static int DeleteWhere(Database& db, std::string where_filter) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {}", + TableName(), + where_filter + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int Truncate(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "TRUNCATE TABLE {}", + TableName() + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + +}; + +#endif //EQEMU_BASE_PETS_BEASTLORD_DATA_REPOSITORY_H diff --git a/common/repositories/base/base_pets_equipmentset_entries_repository.h b/common/repositories/base/base_pets_equipmentset_entries_repository.h index ecd9b2643..1999b35b7 100644 --- a/common/repositories/base/base_pets_equipmentset_entries_repository.h +++ b/common/repositories/base/base_pets_equipmentset_entries_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_pets_equipmentset_repository.h b/common/repositories/base/base_pets_equipmentset_repository.h index 0f27a879f..ebb04c02c 100644 --- a/common/repositories/base/base_pets_equipmentset_repository.h +++ b/common/repositories/base/base_pets_equipmentset_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_pets_repository.h b/common/repositories/base/base_pets_repository.h index 75681e6c4..1dc25f333 100644 --- a/common/repositories/base/base_pets_repository.h +++ b/common/repositories/base/base_pets_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_player_titlesets_repository.h b/common/repositories/base/base_player_titlesets_repository.h index 12c800953..dad10ca0d 100644 --- a/common/repositories/base/base_player_titlesets_repository.h +++ b/common/repositories/base/base_player_titlesets_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_proximities_repository.h b/common/repositories/base/base_proximities_repository.h index 6b1f697b0..8dfff3279 100644 --- a/common/repositories/base/base_proximities_repository.h +++ b/common/repositories/base/base_proximities_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_quest_globals_repository.h b/common/repositories/base/base_quest_globals_repository.h index 0870938fd..7324e7c29 100644 --- a/common/repositories/base/base_quest_globals_repository.h +++ b/common/repositories/base/base_quest_globals_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_raid_details_repository.h b/common/repositories/base/base_raid_details_repository.h index a0f717bc6..a53949908 100644 --- a/common/repositories/base/base_raid_details_repository.h +++ b/common/repositories/base/base_raid_details_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_raid_members_repository.h b/common/repositories/base/base_raid_members_repository.h index b4c9ef94f..2dc350ae1 100644 --- a/common/repositories/base/base_raid_members_repository.h +++ b/common/repositories/base/base_raid_members_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_reports_repository.h b/common/repositories/base/base_reports_repository.h index caebd4107..89aa474b7 100644 --- a/common/repositories/base/base_reports_repository.h +++ b/common/repositories/base/base_reports_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_respawn_times_repository.h b/common/repositories/base/base_respawn_times_repository.h index df69f2be9..0ca8d525f 100644 --- a/common/repositories/base/base_respawn_times_repository.h +++ b/common/repositories/base/base_respawn_times_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_rule_sets_repository.h b/common/repositories/base/base_rule_sets_repository.h index 31dab96d9..181e064a8 100644 --- a/common/repositories/base/base_rule_sets_repository.h +++ b/common/repositories/base/base_rule_sets_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_rule_values_repository.h b/common/repositories/base/base_rule_values_repository.h index 6bd6fe2d6..821ccfdc2 100644 --- a/common/repositories/base/base_rule_values_repository.h +++ b/common/repositories/base/base_rule_values_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_saylink_repository.h b/common/repositories/base/base_saylink_repository.h index e02b1954c..d9206a137 100644 --- a/common/repositories/base/base_saylink_repository.h +++ b/common/repositories/base/base_saylink_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_skill_caps_repository.h b/common/repositories/base/base_skill_caps_repository.h index b17753782..dee1ede92 100644 --- a/common/repositories/base/base_skill_caps_repository.h +++ b/common/repositories/base/base_skill_caps_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ @@ -19,7 +19,7 @@ class BaseSkillCapsRepository { public: struct SkillCaps { int skillID; - int class; + int class_; int level; int cap; int class_; @@ -34,7 +34,7 @@ public: { return { "skillID", - "class", + "`class`", "level", "cap", "class_", @@ -74,7 +74,7 @@ public: SkillCaps entry{}; entry.skillID = 0; - entry.class = 0; + entry.class_ = 0; entry.level = 0; entry.cap = 0; entry.class_ = 0; @@ -114,7 +114,7 @@ public: SkillCaps entry{}; entry.skillID = atoi(row[0]); - entry.class = atoi(row[1]); + entry.class_ = atoi(row[1]); entry.level = atoi(row[2]); entry.cap = atoi(row[3]); entry.class_ = atoi(row[4]); @@ -152,7 +152,7 @@ public: auto columns = Columns(); update_values.push_back(columns[0] + " = " + std::to_string(skill_caps_entry.skillID)); - update_values.push_back(columns[1] + " = " + std::to_string(skill_caps_entry.class)); + update_values.push_back(columns[1] + " = " + std::to_string(skill_caps_entry.class_)); update_values.push_back(columns[2] + " = " + std::to_string(skill_caps_entry.level)); update_values.push_back(columns[3] + " = " + std::to_string(skill_caps_entry.cap)); update_values.push_back(columns[4] + " = " + std::to_string(skill_caps_entry.class_)); @@ -178,7 +178,7 @@ public: std::vector insert_values; insert_values.push_back(std::to_string(skill_caps_entry.skillID)); - insert_values.push_back(std::to_string(skill_caps_entry.class)); + insert_values.push_back(std::to_string(skill_caps_entry.class_)); insert_values.push_back(std::to_string(skill_caps_entry.level)); insert_values.push_back(std::to_string(skill_caps_entry.cap)); insert_values.push_back(std::to_string(skill_caps_entry.class_)); @@ -212,7 +212,7 @@ public: std::vector insert_values; insert_values.push_back(std::to_string(skill_caps_entry.skillID)); - insert_values.push_back(std::to_string(skill_caps_entry.class)); + insert_values.push_back(std::to_string(skill_caps_entry.class_)); insert_values.push_back(std::to_string(skill_caps_entry.level)); insert_values.push_back(std::to_string(skill_caps_entry.cap)); insert_values.push_back(std::to_string(skill_caps_entry.class_)); @@ -250,7 +250,7 @@ public: SkillCaps entry{}; entry.skillID = atoi(row[0]); - entry.class = atoi(row[1]); + entry.class_ = atoi(row[1]); entry.level = atoi(row[2]); entry.cap = atoi(row[3]); entry.class_ = atoi(row[4]); @@ -279,7 +279,7 @@ public: SkillCaps entry{}; entry.skillID = atoi(row[0]); - entry.class = atoi(row[1]); + entry.class_ = atoi(row[1]); entry.level = atoi(row[2]); entry.cap = atoi(row[3]); entry.class_ = atoi(row[4]); diff --git a/common/repositories/base/base_spawn2_repository.h b/common/repositories/base/base_spawn2_repository.h index 921c9262d..6ff7146ee 100644 --- a/common/repositories/base/base_spawn2_repository.h +++ b/common/repositories/base/base_spawn2_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ @@ -29,6 +29,7 @@ public: int respawntime; int variance; int pathgrid; + int path_when_zone_idle; int _condition; int cond_value; int enabled; @@ -58,6 +59,7 @@ public: "respawntime", "variance", "pathgrid", + "path_when_zone_idle", "_condition", "cond_value", "enabled", @@ -112,6 +114,7 @@ public: entry.respawntime = 0; entry.variance = 0; entry.pathgrid = 0; + entry.path_when_zone_idle = 0; entry._condition = 0; entry.cond_value = 1; entry.enabled = 1; @@ -166,14 +169,15 @@ public: entry.respawntime = atoi(row[8]); entry.variance = atoi(row[9]); entry.pathgrid = atoi(row[10]); - entry._condition = atoi(row[11]); - entry.cond_value = atoi(row[12]); - entry.enabled = atoi(row[13]); - entry.animation = atoi(row[14]); - entry.min_expansion = atoi(row[15]); - entry.max_expansion = atoi(row[16]); - entry.content_flags = row[17] ? row[17] : ""; - entry.content_flags_disabled = row[18] ? row[18] : ""; + entry.path_when_zone_idle = atoi(row[11]); + entry._condition = atoi(row[12]); + entry.cond_value = atoi(row[13]); + entry.enabled = atoi(row[14]); + entry.animation = atoi(row[15]); + entry.min_expansion = atoi(row[16]); + entry.max_expansion = atoi(row[17]); + entry.content_flags = row[18] ? row[18] : ""; + entry.content_flags_disabled = row[19] ? row[19] : ""; return entry; } @@ -217,14 +221,15 @@ public: update_values.push_back(columns[8] + " = " + std::to_string(spawn2_entry.respawntime)); update_values.push_back(columns[9] + " = " + std::to_string(spawn2_entry.variance)); update_values.push_back(columns[10] + " = " + std::to_string(spawn2_entry.pathgrid)); - update_values.push_back(columns[11] + " = " + std::to_string(spawn2_entry._condition)); - update_values.push_back(columns[12] + " = " + std::to_string(spawn2_entry.cond_value)); - update_values.push_back(columns[13] + " = " + std::to_string(spawn2_entry.enabled)); - update_values.push_back(columns[14] + " = " + std::to_string(spawn2_entry.animation)); - update_values.push_back(columns[15] + " = " + std::to_string(spawn2_entry.min_expansion)); - update_values.push_back(columns[16] + " = " + std::to_string(spawn2_entry.max_expansion)); - update_values.push_back(columns[17] + " = '" + EscapeString(spawn2_entry.content_flags) + "'"); - update_values.push_back(columns[18] + " = '" + EscapeString(spawn2_entry.content_flags_disabled) + "'"); + update_values.push_back(columns[11] + " = " + std::to_string(spawn2_entry.path_when_zone_idle)); + update_values.push_back(columns[12] + " = " + std::to_string(spawn2_entry._condition)); + update_values.push_back(columns[13] + " = " + std::to_string(spawn2_entry.cond_value)); + update_values.push_back(columns[14] + " = " + std::to_string(spawn2_entry.enabled)); + update_values.push_back(columns[15] + " = " + std::to_string(spawn2_entry.animation)); + update_values.push_back(columns[16] + " = " + std::to_string(spawn2_entry.min_expansion)); + update_values.push_back(columns[17] + " = " + std::to_string(spawn2_entry.max_expansion)); + update_values.push_back(columns[18] + " = '" + EscapeString(spawn2_entry.content_flags) + "'"); + update_values.push_back(columns[19] + " = '" + EscapeString(spawn2_entry.content_flags_disabled) + "'"); auto results = db.QueryDatabase( fmt::format( @@ -257,6 +262,7 @@ public: insert_values.push_back(std::to_string(spawn2_entry.respawntime)); insert_values.push_back(std::to_string(spawn2_entry.variance)); insert_values.push_back(std::to_string(spawn2_entry.pathgrid)); + insert_values.push_back(std::to_string(spawn2_entry.path_when_zone_idle)); insert_values.push_back(std::to_string(spawn2_entry._condition)); insert_values.push_back(std::to_string(spawn2_entry.cond_value)); insert_values.push_back(std::to_string(spawn2_entry.enabled)); @@ -305,6 +311,7 @@ public: insert_values.push_back(std::to_string(spawn2_entry.respawntime)); insert_values.push_back(std::to_string(spawn2_entry.variance)); insert_values.push_back(std::to_string(spawn2_entry.pathgrid)); + insert_values.push_back(std::to_string(spawn2_entry.path_when_zone_idle)); insert_values.push_back(std::to_string(spawn2_entry._condition)); insert_values.push_back(std::to_string(spawn2_entry.cond_value)); insert_values.push_back(std::to_string(spawn2_entry.enabled)); @@ -357,14 +364,15 @@ public: entry.respawntime = atoi(row[8]); entry.variance = atoi(row[9]); entry.pathgrid = atoi(row[10]); - entry._condition = atoi(row[11]); - entry.cond_value = atoi(row[12]); - entry.enabled = atoi(row[13]); - entry.animation = atoi(row[14]); - entry.min_expansion = atoi(row[15]); - entry.max_expansion = atoi(row[16]); - entry.content_flags = row[17] ? row[17] : ""; - entry.content_flags_disabled = row[18] ? row[18] : ""; + entry.path_when_zone_idle = atoi(row[11]); + entry._condition = atoi(row[12]); + entry.cond_value = atoi(row[13]); + entry.enabled = atoi(row[14]); + entry.animation = atoi(row[15]); + entry.min_expansion = atoi(row[16]); + entry.max_expansion = atoi(row[17]); + entry.content_flags = row[18] ? row[18] : ""; + entry.content_flags_disabled = row[19] ? row[19] : ""; all_entries.push_back(entry); } @@ -400,14 +408,15 @@ public: entry.respawntime = atoi(row[8]); entry.variance = atoi(row[9]); entry.pathgrid = atoi(row[10]); - entry._condition = atoi(row[11]); - entry.cond_value = atoi(row[12]); - entry.enabled = atoi(row[13]); - entry.animation = atoi(row[14]); - entry.min_expansion = atoi(row[15]); - entry.max_expansion = atoi(row[16]); - entry.content_flags = row[17] ? row[17] : ""; - entry.content_flags_disabled = row[18] ? row[18] : ""; + entry.path_when_zone_idle = atoi(row[11]); + entry._condition = atoi(row[12]); + entry.cond_value = atoi(row[13]); + entry.enabled = atoi(row[14]); + entry.animation = atoi(row[15]); + entry.min_expansion = atoi(row[16]); + entry.max_expansion = atoi(row[17]); + entry.content_flags = row[18] ? row[18] : ""; + entry.content_flags_disabled = row[19] ? row[19] : ""; all_entries.push_back(entry); } diff --git a/common/repositories/base/base_spawn_condition_values_repository.h b/common/repositories/base/base_spawn_condition_values_repository.h index c54eee597..ca796270f 100644 --- a/common/repositories/base/base_spawn_condition_values_repository.h +++ b/common/repositories/base/base_spawn_condition_values_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_spawn_conditions_repository.h b/common/repositories/base/base_spawn_conditions_repository.h index e0e7fa0ab..4a3435fd7 100644 --- a/common/repositories/base/base_spawn_conditions_repository.h +++ b/common/repositories/base/base_spawn_conditions_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_spawn_events_repository.h b/common/repositories/base/base_spawn_events_repository.h index da52bbb8a..66490d2ca 100644 --- a/common/repositories/base/base_spawn_events_repository.h +++ b/common/repositories/base/base_spawn_events_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_spawnentry_repository.h b/common/repositories/base/base_spawnentry_repository.h index 4a04bf7d6..3bfb7b5e0 100644 --- a/common/repositories/base/base_spawnentry_repository.h +++ b/common/repositories/base/base_spawnentry_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_spawngroup_repository.h b/common/repositories/base/base_spawngroup_repository.h index 0baba76aa..a9ab5e3e6 100644 --- a/common/repositories/base/base_spawngroup_repository.h +++ b/common/repositories/base/base_spawngroup_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_spell_buckets_repository.h b/common/repositories/base/base_spell_buckets_repository.h index 39ce3ff42..7e6d5e46d 100644 --- a/common/repositories/base/base_spell_buckets_repository.h +++ b/common/repositories/base/base_spell_buckets_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ @@ -18,7 +18,7 @@ class BaseSpellBucketsRepository { public: struct SpellBuckets { - int spellid; + int64 spellid; std::string key; std::string value; }; @@ -107,7 +107,7 @@ public: if (results.RowCount() == 1) { SpellBuckets entry{}; - entry.spellid = atoi(row[0]); + entry.spellid = strtoll(row[0], NULL, 10); entry.key = row[1] ? row[1] : ""; entry.value = row[2] ? row[2] : ""; @@ -235,7 +235,7 @@ public: for (auto row = results.begin(); row != results.end(); ++row) { SpellBuckets entry{}; - entry.spellid = atoi(row[0]); + entry.spellid = strtoll(row[0], NULL, 10); entry.key = row[1] ? row[1] : ""; entry.value = row[2] ? row[2] : ""; @@ -262,7 +262,7 @@ public: for (auto row = results.begin(); row != results.end(); ++row) { SpellBuckets entry{}; - entry.spellid = atoi(row[0]); + entry.spellid = strtoll(row[0], NULL, 10); entry.key = row[1] ? row[1] : ""; entry.value = row[2] ? row[2] : ""; diff --git a/common/repositories/base/base_spell_globals_repository.h b/common/repositories/base/base_spell_globals_repository.h index 40ce563ce..26e836597 100644 --- a/common/repositories/base/base_spell_globals_repository.h +++ b/common/repositories/base/base_spell_globals_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_spells_new_repository.h b/common/repositories/base/base_spells_new_repository.h index e59ce3b28..e273667ad 100644 --- a/common/repositories/base/base_spells_new_repository.h +++ b/common/repositories/base/base_spells_new_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_start_zones_repository.h b/common/repositories/base/base_start_zones_repository.h index 75a8595ca..9a627de40 100644 --- a/common/repositories/base/base_start_zones_repository.h +++ b/common/repositories/base/base_start_zones_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_starting_items_repository.h b/common/repositories/base/base_starting_items_repository.h index edb2cafa9..0f40bb16d 100644 --- a/common/repositories/base/base_starting_items_repository.h +++ b/common/repositories/base/base_starting_items_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ @@ -20,7 +20,7 @@ public: struct StartingItems { int id; int race; - int class; + int class_; int deityid; int zoneid; int itemid; @@ -43,7 +43,7 @@ public: return { "id", "race", - "class", + "`class`", "deityid", "zoneid", "itemid", @@ -91,7 +91,7 @@ public: entry.id = 0; entry.race = 0; - entry.class = 0; + entry.class_ = 0; entry.deityid = 0; entry.zoneid = 0; entry.itemid = 0; @@ -139,7 +139,7 @@ public: entry.id = atoi(row[0]); entry.race = atoi(row[1]); - entry.class = atoi(row[2]); + entry.class_ = atoi(row[2]); entry.deityid = atoi(row[3]); entry.zoneid = atoi(row[4]); entry.itemid = atoi(row[5]); @@ -184,7 +184,7 @@ public: auto columns = Columns(); update_values.push_back(columns[1] + " = " + std::to_string(starting_items_entry.race)); - update_values.push_back(columns[2] + " = " + std::to_string(starting_items_entry.class)); + update_values.push_back(columns[2] + " = " + std::to_string(starting_items_entry.class_)); update_values.push_back(columns[3] + " = " + std::to_string(starting_items_entry.deityid)); update_values.push_back(columns[4] + " = " + std::to_string(starting_items_entry.zoneid)); update_values.push_back(columns[5] + " = " + std::to_string(starting_items_entry.itemid)); @@ -218,7 +218,7 @@ public: insert_values.push_back(std::to_string(starting_items_entry.id)); insert_values.push_back(std::to_string(starting_items_entry.race)); - insert_values.push_back(std::to_string(starting_items_entry.class)); + insert_values.push_back(std::to_string(starting_items_entry.class_)); insert_values.push_back(std::to_string(starting_items_entry.deityid)); insert_values.push_back(std::to_string(starting_items_entry.zoneid)); insert_values.push_back(std::to_string(starting_items_entry.itemid)); @@ -260,7 +260,7 @@ public: insert_values.push_back(std::to_string(starting_items_entry.id)); insert_values.push_back(std::to_string(starting_items_entry.race)); - insert_values.push_back(std::to_string(starting_items_entry.class)); + insert_values.push_back(std::to_string(starting_items_entry.class_)); insert_values.push_back(std::to_string(starting_items_entry.deityid)); insert_values.push_back(std::to_string(starting_items_entry.zoneid)); insert_values.push_back(std::to_string(starting_items_entry.itemid)); @@ -306,7 +306,7 @@ public: entry.id = atoi(row[0]); entry.race = atoi(row[1]); - entry.class = atoi(row[2]); + entry.class_ = atoi(row[2]); entry.deityid = atoi(row[3]); entry.zoneid = atoi(row[4]); entry.itemid = atoi(row[5]); @@ -343,7 +343,7 @@ public: entry.id = atoi(row[0]); entry.race = atoi(row[1]); - entry.class = atoi(row[2]); + entry.class_ = atoi(row[2]); entry.deityid = atoi(row[3]); entry.zoneid = atoi(row[4]); entry.itemid = atoi(row[5]); diff --git a/common/repositories/base/base_task_activities_repository.h b/common/repositories/base/base_task_activities_repository.h index ad7f1fee4..e88334e1b 100644 --- a/common/repositories/base/base_task_activities_repository.h +++ b/common/repositories/base/base_task_activities_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_tasksets_repository.h b/common/repositories/base/base_tasksets_repository.h index c3feb4ccd..0944f57b3 100644 --- a/common/repositories/base/base_tasksets_repository.h +++ b/common/repositories/base/base_tasksets_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_timers_repository.h b/common/repositories/base/base_timers_repository.h index f1d6d97b1..ea3774bc2 100644 --- a/common/repositories/base/base_timers_repository.h +++ b/common/repositories/base/base_timers_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_titles_repository.h b/common/repositories/base/base_titles_repository.h index c108c887b..e270b51d1 100644 --- a/common/repositories/base/base_titles_repository.h +++ b/common/repositories/base/base_titles_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ @@ -24,7 +24,7 @@ public: int max_skill_value; int min_aa_points; int max_aa_points; - int class; + int class_; int gender; int char_id; int status; @@ -48,7 +48,7 @@ public: "max_skill_value", "min_aa_points", "max_aa_points", - "class", + "`class`", "gender", "char_id", "status", @@ -97,7 +97,7 @@ public: entry.max_skill_value = -1; entry.min_aa_points = -1; entry.max_aa_points = -1; - entry.class = -1; + entry.class_ = -1; entry.gender = -1; entry.char_id = -1; entry.status = -1; @@ -146,7 +146,7 @@ public: entry.max_skill_value = atoi(row[3]); entry.min_aa_points = atoi(row[4]); entry.max_aa_points = atoi(row[5]); - entry.class = atoi(row[6]); + entry.class_ = atoi(row[6]); entry.gender = atoi(row[7]); entry.char_id = atoi(row[8]); entry.status = atoi(row[9]); @@ -192,7 +192,7 @@ public: update_values.push_back(columns[3] + " = " + std::to_string(titles_entry.max_skill_value)); update_values.push_back(columns[4] + " = " + std::to_string(titles_entry.min_aa_points)); update_values.push_back(columns[5] + " = " + std::to_string(titles_entry.max_aa_points)); - update_values.push_back(columns[6] + " = " + std::to_string(titles_entry.class)); + update_values.push_back(columns[6] + " = " + std::to_string(titles_entry.class_)); update_values.push_back(columns[7] + " = " + std::to_string(titles_entry.gender)); update_values.push_back(columns[8] + " = " + std::to_string(titles_entry.char_id)); update_values.push_back(columns[9] + " = " + std::to_string(titles_entry.status)); @@ -227,7 +227,7 @@ public: insert_values.push_back(std::to_string(titles_entry.max_skill_value)); insert_values.push_back(std::to_string(titles_entry.min_aa_points)); insert_values.push_back(std::to_string(titles_entry.max_aa_points)); - insert_values.push_back(std::to_string(titles_entry.class)); + insert_values.push_back(std::to_string(titles_entry.class_)); insert_values.push_back(std::to_string(titles_entry.gender)); insert_values.push_back(std::to_string(titles_entry.char_id)); insert_values.push_back(std::to_string(titles_entry.status)); @@ -270,7 +270,7 @@ public: insert_values.push_back(std::to_string(titles_entry.max_skill_value)); insert_values.push_back(std::to_string(titles_entry.min_aa_points)); insert_values.push_back(std::to_string(titles_entry.max_aa_points)); - insert_values.push_back(std::to_string(titles_entry.class)); + insert_values.push_back(std::to_string(titles_entry.class_)); insert_values.push_back(std::to_string(titles_entry.gender)); insert_values.push_back(std::to_string(titles_entry.char_id)); insert_values.push_back(std::to_string(titles_entry.status)); @@ -317,7 +317,7 @@ public: entry.max_skill_value = atoi(row[3]); entry.min_aa_points = atoi(row[4]); entry.max_aa_points = atoi(row[5]); - entry.class = atoi(row[6]); + entry.class_ = atoi(row[6]); entry.gender = atoi(row[7]); entry.char_id = atoi(row[8]); entry.status = atoi(row[9]); @@ -355,7 +355,7 @@ public: entry.max_skill_value = atoi(row[3]); entry.min_aa_points = atoi(row[4]); entry.max_aa_points = atoi(row[5]); - entry.class = atoi(row[6]); + entry.class_ = atoi(row[6]); entry.gender = atoi(row[7]); entry.char_id = atoi(row[8]); entry.status = atoi(row[9]); diff --git a/common/repositories/base/base_trader_repository.h b/common/repositories/base/base_trader_repository.h index 1eded1551..1481e6154 100644 --- a/common/repositories/base/base_trader_repository.h +++ b/common/repositories/base/base_trader_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_tradeskill_recipe_entries_repository.h b/common/repositories/base/base_tradeskill_recipe_entries_repository.h index 13c293c77..274c58ca1 100644 --- a/common/repositories/base/base_tradeskill_recipe_entries_repository.h +++ b/common/repositories/base/base_tradeskill_recipe_entries_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_tradeskill_recipe_repository.h b/common/repositories/base/base_tradeskill_recipe_repository.h index 453d8b7dc..019cea88f 100644 --- a/common/repositories/base/base_tradeskill_recipe_repository.h +++ b/common/repositories/base/base_tradeskill_recipe_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_traps_repository.h b/common/repositories/base/base_traps_repository.h index b76072525..07ab550b4 100644 --- a/common/repositories/base/base_traps_repository.h +++ b/common/repositories/base/base_traps_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_tribute_levels_repository.h b/common/repositories/base/base_tribute_levels_repository.h index 2e49dab99..fef67ff27 100644 --- a/common/repositories/base/base_tribute_levels_repository.h +++ b/common/repositories/base/base_tribute_levels_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_tributes_repository.h b/common/repositories/base/base_tributes_repository.h index 1acd2f63b..c3a7f6324 100644 --- a/common/repositories/base/base_tributes_repository.h +++ b/common/repositories/base/base_tributes_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_veteran_reward_templates_repository.h b/common/repositories/base/base_veteran_reward_templates_repository.h index f41c1da3c..283742309 100644 --- a/common/repositories/base/base_veteran_reward_templates_repository.h +++ b/common/repositories/base/base_veteran_reward_templates_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_zone_points_repository.h b/common/repositories/base/base_zone_points_repository.h index e34d9bafb..b29cae43a 100644 --- a/common/repositories/base/base_zone_points_repository.h +++ b/common/repositories/base/base_zone_points_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ diff --git a/common/repositories/base/base_zone_repository.h b/common/repositories/base/base_zone_repository.h index f31b98491..04188ac93 100644 --- a/common/repositories/base/base_zone_repository.h +++ b/common/repositories/base/base_zone_repository.h @@ -4,7 +4,7 @@ * This repository was automatically generated and is NOT to be modified directly. * Any repository modifications are meant to be made to the repository extending the base. * Any modifications to base repositories are to be made by the generator only - * + * * @generator ./utils/scripts/generators/repository-generator.pl * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories */ @@ -26,6 +26,7 @@ public: float safe_x; float safe_y; float safe_z; + float safe_heading; float graveyard_id; int min_level; int min_status; @@ -76,7 +77,7 @@ public: int castoutdoor; int hotzone; int insttype; - int shutdowndelay; + int64 shutdowndelay; int peqzone; int expansion; int suspendbuffs; @@ -127,6 +128,7 @@ public: "safe_x", "safe_y", "safe_z", + "safe_heading", "graveyard_id", "min_level", "min_status", @@ -253,6 +255,7 @@ public: entry.safe_x = 0; entry.safe_y = 0; entry.safe_z = 0; + entry.safe_heading = 0; entry.graveyard_id = 0; entry.min_level = 0; entry.min_status = 0; @@ -379,89 +382,90 @@ public: entry.safe_x = static_cast(atof(row[5])); entry.safe_y = static_cast(atof(row[6])); entry.safe_z = static_cast(atof(row[7])); - entry.graveyard_id = static_cast(atof(row[8])); - entry.min_level = atoi(row[9]); - entry.min_status = atoi(row[10]); - entry.zoneidnumber = atoi(row[11]); - entry.version = atoi(row[12]); - entry.timezone = atoi(row[13]); - entry.maxclients = atoi(row[14]); - entry.ruleset = atoi(row[15]); - entry.note = row[16] ? row[16] : ""; - entry.underworld = static_cast(atof(row[17])); - entry.minclip = static_cast(atof(row[18])); - entry.maxclip = static_cast(atof(row[19])); - entry.fog_minclip = static_cast(atof(row[20])); - entry.fog_maxclip = static_cast(atof(row[21])); - entry.fog_blue = atoi(row[22]); - entry.fog_red = atoi(row[23]); - entry.fog_green = atoi(row[24]); - entry.sky = atoi(row[25]); - entry.ztype = atoi(row[26]); - entry.zone_exp_multiplier = static_cast(atof(row[27])); - entry.walkspeed = static_cast(atof(row[28])); - entry.time_type = atoi(row[29]); - entry.fog_red1 = atoi(row[30]); - entry.fog_green1 = atoi(row[31]); - entry.fog_blue1 = atoi(row[32]); - entry.fog_minclip1 = static_cast(atof(row[33])); - entry.fog_maxclip1 = static_cast(atof(row[34])); - entry.fog_red2 = atoi(row[35]); - entry.fog_green2 = atoi(row[36]); - entry.fog_blue2 = atoi(row[37]); - entry.fog_minclip2 = static_cast(atof(row[38])); - entry.fog_maxclip2 = static_cast(atof(row[39])); - entry.fog_red3 = atoi(row[40]); - entry.fog_green3 = atoi(row[41]); - entry.fog_blue3 = atoi(row[42]); - entry.fog_minclip3 = static_cast(atof(row[43])); - entry.fog_maxclip3 = static_cast(atof(row[44])); - entry.fog_red4 = atoi(row[45]); - entry.fog_green4 = atoi(row[46]); - entry.fog_blue4 = atoi(row[47]); - entry.fog_minclip4 = static_cast(atof(row[48])); - entry.fog_maxclip4 = static_cast(atof(row[49])); - entry.fog_density = static_cast(atof(row[50])); - entry.flag_needed = row[51] ? row[51] : ""; - entry.canbind = atoi(row[52]); - entry.cancombat = atoi(row[53]); - entry.canlevitate = atoi(row[54]); - entry.castoutdoor = atoi(row[55]); - entry.hotzone = atoi(row[56]); - entry.insttype = atoi(row[57]); - entry.shutdowndelay = atoi(row[58]); - entry.peqzone = atoi(row[59]); - entry.expansion = atoi(row[60]); - entry.suspendbuffs = atoi(row[61]); - entry.rain_chance1 = atoi(row[62]); - entry.rain_chance2 = atoi(row[63]); - entry.rain_chance3 = atoi(row[64]); - entry.rain_chance4 = atoi(row[65]); - entry.rain_duration1 = atoi(row[66]); - entry.rain_duration2 = atoi(row[67]); - entry.rain_duration3 = atoi(row[68]); - entry.rain_duration4 = atoi(row[69]); - entry.snow_chance1 = atoi(row[70]); - entry.snow_chance2 = atoi(row[71]); - entry.snow_chance3 = atoi(row[72]); - entry.snow_chance4 = atoi(row[73]); - entry.snow_duration1 = atoi(row[74]); - entry.snow_duration2 = atoi(row[75]); - entry.snow_duration3 = atoi(row[76]); - entry.snow_duration4 = atoi(row[77]); - entry.gravity = static_cast(atof(row[78])); - entry.type = atoi(row[79]); - entry.skylock = atoi(row[80]); - entry.fast_regen_hp = atoi(row[81]); - entry.fast_regen_mana = atoi(row[82]); - entry.fast_regen_endurance = atoi(row[83]); - entry.npc_max_aggro_dist = atoi(row[84]); - entry.max_movement_update_range = atoi(row[85]); - entry.min_expansion = atoi(row[86]); - entry.max_expansion = atoi(row[87]); - entry.content_flags = row[88] ? row[88] : ""; - entry.content_flags_disabled = row[89] ? row[89] : ""; - entry.underworld_teleport_index = atoi(row[90]); + entry.safe_heading = static_cast(atof(row[8])); + entry.graveyard_id = static_cast(atof(row[9])); + entry.min_level = atoi(row[10]); + entry.min_status = atoi(row[11]); + entry.zoneidnumber = atoi(row[12]); + entry.version = atoi(row[13]); + entry.timezone = atoi(row[14]); + entry.maxclients = atoi(row[15]); + entry.ruleset = atoi(row[16]); + entry.note = row[17] ? row[17] : ""; + entry.underworld = static_cast(atof(row[18])); + entry.minclip = static_cast(atof(row[19])); + entry.maxclip = static_cast(atof(row[20])); + entry.fog_minclip = static_cast(atof(row[21])); + entry.fog_maxclip = static_cast(atof(row[22])); + entry.fog_blue = atoi(row[23]); + entry.fog_red = atoi(row[24]); + entry.fog_green = atoi(row[25]); + entry.sky = atoi(row[26]); + entry.ztype = atoi(row[27]); + entry.zone_exp_multiplier = static_cast(atof(row[28])); + entry.walkspeed = static_cast(atof(row[29])); + entry.time_type = atoi(row[30]); + entry.fog_red1 = atoi(row[31]); + entry.fog_green1 = atoi(row[32]); + entry.fog_blue1 = atoi(row[33]); + entry.fog_minclip1 = static_cast(atof(row[34])); + entry.fog_maxclip1 = static_cast(atof(row[35])); + entry.fog_red2 = atoi(row[36]); + entry.fog_green2 = atoi(row[37]); + entry.fog_blue2 = atoi(row[38]); + entry.fog_minclip2 = static_cast(atof(row[39])); + entry.fog_maxclip2 = static_cast(atof(row[40])); + entry.fog_red3 = atoi(row[41]); + entry.fog_green3 = atoi(row[42]); + entry.fog_blue3 = atoi(row[43]); + entry.fog_minclip3 = static_cast(atof(row[44])); + entry.fog_maxclip3 = static_cast(atof(row[45])); + entry.fog_red4 = atoi(row[46]); + entry.fog_green4 = atoi(row[47]); + entry.fog_blue4 = atoi(row[48]); + entry.fog_minclip4 = static_cast(atof(row[49])); + entry.fog_maxclip4 = static_cast(atof(row[50])); + entry.fog_density = static_cast(atof(row[51])); + entry.flag_needed = row[52] ? row[52] : ""; + entry.canbind = atoi(row[53]); + entry.cancombat = atoi(row[54]); + entry.canlevitate = atoi(row[55]); + entry.castoutdoor = atoi(row[56]); + entry.hotzone = atoi(row[57]); + entry.insttype = atoi(row[58]); + entry.shutdowndelay = strtoll(row[59], NULL, 10); + entry.peqzone = atoi(row[60]); + entry.expansion = atoi(row[61]); + entry.suspendbuffs = atoi(row[62]); + entry.rain_chance1 = atoi(row[63]); + entry.rain_chance2 = atoi(row[64]); + entry.rain_chance3 = atoi(row[65]); + entry.rain_chance4 = atoi(row[66]); + entry.rain_duration1 = atoi(row[67]); + entry.rain_duration2 = atoi(row[68]); + entry.rain_duration3 = atoi(row[69]); + entry.rain_duration4 = atoi(row[70]); + entry.snow_chance1 = atoi(row[71]); + entry.snow_chance2 = atoi(row[72]); + entry.snow_chance3 = atoi(row[73]); + entry.snow_chance4 = atoi(row[74]); + entry.snow_duration1 = atoi(row[75]); + entry.snow_duration2 = atoi(row[76]); + entry.snow_duration3 = atoi(row[77]); + entry.snow_duration4 = atoi(row[78]); + entry.gravity = static_cast(atof(row[79])); + entry.type = atoi(row[80]); + entry.skylock = atoi(row[81]); + entry.fast_regen_hp = atoi(row[82]); + entry.fast_regen_mana = atoi(row[83]); + entry.fast_regen_endurance = atoi(row[84]); + entry.npc_max_aggro_dist = atoi(row[85]); + entry.max_movement_update_range = atoi(row[86]); + entry.min_expansion = atoi(row[87]); + entry.max_expansion = atoi(row[88]); + entry.content_flags = row[89] ? row[89] : ""; + entry.content_flags_disabled = row[90] ? row[90] : ""; + entry.underworld_teleport_index = atoi(row[91]); return entry; } @@ -502,89 +506,90 @@ public: update_values.push_back(columns[5] + " = " + std::to_string(zone_entry.safe_x)); update_values.push_back(columns[6] + " = " + std::to_string(zone_entry.safe_y)); update_values.push_back(columns[7] + " = " + std::to_string(zone_entry.safe_z)); - update_values.push_back(columns[8] + " = " + std::to_string(zone_entry.graveyard_id)); - update_values.push_back(columns[9] + " = " + std::to_string(zone_entry.min_level)); - update_values.push_back(columns[10] + " = " + std::to_string(zone_entry.min_status)); - update_values.push_back(columns[11] + " = " + std::to_string(zone_entry.zoneidnumber)); - update_values.push_back(columns[12] + " = " + std::to_string(zone_entry.version)); - update_values.push_back(columns[13] + " = " + std::to_string(zone_entry.timezone)); - update_values.push_back(columns[14] + " = " + std::to_string(zone_entry.maxclients)); - update_values.push_back(columns[15] + " = " + std::to_string(zone_entry.ruleset)); - update_values.push_back(columns[16] + " = '" + EscapeString(zone_entry.note) + "'"); - update_values.push_back(columns[17] + " = " + std::to_string(zone_entry.underworld)); - update_values.push_back(columns[18] + " = " + std::to_string(zone_entry.minclip)); - update_values.push_back(columns[19] + " = " + std::to_string(zone_entry.maxclip)); - update_values.push_back(columns[20] + " = " + std::to_string(zone_entry.fog_minclip)); - update_values.push_back(columns[21] + " = " + std::to_string(zone_entry.fog_maxclip)); - update_values.push_back(columns[22] + " = " + std::to_string(zone_entry.fog_blue)); - update_values.push_back(columns[23] + " = " + std::to_string(zone_entry.fog_red)); - update_values.push_back(columns[24] + " = " + std::to_string(zone_entry.fog_green)); - update_values.push_back(columns[25] + " = " + std::to_string(zone_entry.sky)); - update_values.push_back(columns[26] + " = " + std::to_string(zone_entry.ztype)); - update_values.push_back(columns[27] + " = " + std::to_string(zone_entry.zone_exp_multiplier)); - update_values.push_back(columns[28] + " = " + std::to_string(zone_entry.walkspeed)); - update_values.push_back(columns[29] + " = " + std::to_string(zone_entry.time_type)); - update_values.push_back(columns[30] + " = " + std::to_string(zone_entry.fog_red1)); - update_values.push_back(columns[31] + " = " + std::to_string(zone_entry.fog_green1)); - update_values.push_back(columns[32] + " = " + std::to_string(zone_entry.fog_blue1)); - update_values.push_back(columns[33] + " = " + std::to_string(zone_entry.fog_minclip1)); - update_values.push_back(columns[34] + " = " + std::to_string(zone_entry.fog_maxclip1)); - update_values.push_back(columns[35] + " = " + std::to_string(zone_entry.fog_red2)); - update_values.push_back(columns[36] + " = " + std::to_string(zone_entry.fog_green2)); - update_values.push_back(columns[37] + " = " + std::to_string(zone_entry.fog_blue2)); - update_values.push_back(columns[38] + " = " + std::to_string(zone_entry.fog_minclip2)); - update_values.push_back(columns[39] + " = " + std::to_string(zone_entry.fog_maxclip2)); - update_values.push_back(columns[40] + " = " + std::to_string(zone_entry.fog_red3)); - update_values.push_back(columns[41] + " = " + std::to_string(zone_entry.fog_green3)); - update_values.push_back(columns[42] + " = " + std::to_string(zone_entry.fog_blue3)); - update_values.push_back(columns[43] + " = " + std::to_string(zone_entry.fog_minclip3)); - update_values.push_back(columns[44] + " = " + std::to_string(zone_entry.fog_maxclip3)); - update_values.push_back(columns[45] + " = " + std::to_string(zone_entry.fog_red4)); - update_values.push_back(columns[46] + " = " + std::to_string(zone_entry.fog_green4)); - update_values.push_back(columns[47] + " = " + std::to_string(zone_entry.fog_blue4)); - update_values.push_back(columns[48] + " = " + std::to_string(zone_entry.fog_minclip4)); - update_values.push_back(columns[49] + " = " + std::to_string(zone_entry.fog_maxclip4)); - update_values.push_back(columns[50] + " = " + std::to_string(zone_entry.fog_density)); - update_values.push_back(columns[51] + " = '" + EscapeString(zone_entry.flag_needed) + "'"); - update_values.push_back(columns[52] + " = " + std::to_string(zone_entry.canbind)); - update_values.push_back(columns[53] + " = " + std::to_string(zone_entry.cancombat)); - update_values.push_back(columns[54] + " = " + std::to_string(zone_entry.canlevitate)); - update_values.push_back(columns[55] + " = " + std::to_string(zone_entry.castoutdoor)); - update_values.push_back(columns[56] + " = " + std::to_string(zone_entry.hotzone)); - update_values.push_back(columns[57] + " = " + std::to_string(zone_entry.insttype)); - update_values.push_back(columns[58] + " = " + std::to_string(zone_entry.shutdowndelay)); - update_values.push_back(columns[59] + " = " + std::to_string(zone_entry.peqzone)); - update_values.push_back(columns[60] + " = " + std::to_string(zone_entry.expansion)); - update_values.push_back(columns[61] + " = " + std::to_string(zone_entry.suspendbuffs)); - update_values.push_back(columns[62] + " = " + std::to_string(zone_entry.rain_chance1)); - update_values.push_back(columns[63] + " = " + std::to_string(zone_entry.rain_chance2)); - update_values.push_back(columns[64] + " = " + std::to_string(zone_entry.rain_chance3)); - update_values.push_back(columns[65] + " = " + std::to_string(zone_entry.rain_chance4)); - update_values.push_back(columns[66] + " = " + std::to_string(zone_entry.rain_duration1)); - update_values.push_back(columns[67] + " = " + std::to_string(zone_entry.rain_duration2)); - update_values.push_back(columns[68] + " = " + std::to_string(zone_entry.rain_duration3)); - update_values.push_back(columns[69] + " = " + std::to_string(zone_entry.rain_duration4)); - update_values.push_back(columns[70] + " = " + std::to_string(zone_entry.snow_chance1)); - update_values.push_back(columns[71] + " = " + std::to_string(zone_entry.snow_chance2)); - update_values.push_back(columns[72] + " = " + std::to_string(zone_entry.snow_chance3)); - update_values.push_back(columns[73] + " = " + std::to_string(zone_entry.snow_chance4)); - update_values.push_back(columns[74] + " = " + std::to_string(zone_entry.snow_duration1)); - update_values.push_back(columns[75] + " = " + std::to_string(zone_entry.snow_duration2)); - update_values.push_back(columns[76] + " = " + std::to_string(zone_entry.snow_duration3)); - update_values.push_back(columns[77] + " = " + std::to_string(zone_entry.snow_duration4)); - update_values.push_back(columns[78] + " = " + std::to_string(zone_entry.gravity)); - update_values.push_back(columns[79] + " = " + std::to_string(zone_entry.type)); - update_values.push_back(columns[80] + " = " + std::to_string(zone_entry.skylock)); - update_values.push_back(columns[81] + " = " + std::to_string(zone_entry.fast_regen_hp)); - update_values.push_back(columns[82] + " = " + std::to_string(zone_entry.fast_regen_mana)); - update_values.push_back(columns[83] + " = " + std::to_string(zone_entry.fast_regen_endurance)); - update_values.push_back(columns[84] + " = " + std::to_string(zone_entry.npc_max_aggro_dist)); - update_values.push_back(columns[85] + " = " + std::to_string(zone_entry.max_movement_update_range)); - update_values.push_back(columns[86] + " = " + std::to_string(zone_entry.min_expansion)); - update_values.push_back(columns[87] + " = " + std::to_string(zone_entry.max_expansion)); - update_values.push_back(columns[88] + " = '" + EscapeString(zone_entry.content_flags) + "'"); - update_values.push_back(columns[89] + " = '" + EscapeString(zone_entry.content_flags_disabled) + "'"); - update_values.push_back(columns[90] + " = " + std::to_string(zone_entry.underworld_teleport_index)); + update_values.push_back(columns[8] + " = " + std::to_string(zone_entry.safe_heading)); + update_values.push_back(columns[9] + " = " + std::to_string(zone_entry.graveyard_id)); + update_values.push_back(columns[10] + " = " + std::to_string(zone_entry.min_level)); + update_values.push_back(columns[11] + " = " + std::to_string(zone_entry.min_status)); + update_values.push_back(columns[12] + " = " + std::to_string(zone_entry.zoneidnumber)); + update_values.push_back(columns[13] + " = " + std::to_string(zone_entry.version)); + update_values.push_back(columns[14] + " = " + std::to_string(zone_entry.timezone)); + update_values.push_back(columns[15] + " = " + std::to_string(zone_entry.maxclients)); + update_values.push_back(columns[16] + " = " + std::to_string(zone_entry.ruleset)); + update_values.push_back(columns[17] + " = '" + EscapeString(zone_entry.note) + "'"); + update_values.push_back(columns[18] + " = " + std::to_string(zone_entry.underworld)); + update_values.push_back(columns[19] + " = " + std::to_string(zone_entry.minclip)); + update_values.push_back(columns[20] + " = " + std::to_string(zone_entry.maxclip)); + update_values.push_back(columns[21] + " = " + std::to_string(zone_entry.fog_minclip)); + update_values.push_back(columns[22] + " = " + std::to_string(zone_entry.fog_maxclip)); + update_values.push_back(columns[23] + " = " + std::to_string(zone_entry.fog_blue)); + update_values.push_back(columns[24] + " = " + std::to_string(zone_entry.fog_red)); + update_values.push_back(columns[25] + " = " + std::to_string(zone_entry.fog_green)); + update_values.push_back(columns[26] + " = " + std::to_string(zone_entry.sky)); + update_values.push_back(columns[27] + " = " + std::to_string(zone_entry.ztype)); + update_values.push_back(columns[28] + " = " + std::to_string(zone_entry.zone_exp_multiplier)); + update_values.push_back(columns[29] + " = " + std::to_string(zone_entry.walkspeed)); + update_values.push_back(columns[30] + " = " + std::to_string(zone_entry.time_type)); + update_values.push_back(columns[31] + " = " + std::to_string(zone_entry.fog_red1)); + update_values.push_back(columns[32] + " = " + std::to_string(zone_entry.fog_green1)); + update_values.push_back(columns[33] + " = " + std::to_string(zone_entry.fog_blue1)); + update_values.push_back(columns[34] + " = " + std::to_string(zone_entry.fog_minclip1)); + update_values.push_back(columns[35] + " = " + std::to_string(zone_entry.fog_maxclip1)); + update_values.push_back(columns[36] + " = " + std::to_string(zone_entry.fog_red2)); + update_values.push_back(columns[37] + " = " + std::to_string(zone_entry.fog_green2)); + update_values.push_back(columns[38] + " = " + std::to_string(zone_entry.fog_blue2)); + update_values.push_back(columns[39] + " = " + std::to_string(zone_entry.fog_minclip2)); + update_values.push_back(columns[40] + " = " + std::to_string(zone_entry.fog_maxclip2)); + update_values.push_back(columns[41] + " = " + std::to_string(zone_entry.fog_red3)); + update_values.push_back(columns[42] + " = " + std::to_string(zone_entry.fog_green3)); + update_values.push_back(columns[43] + " = " + std::to_string(zone_entry.fog_blue3)); + update_values.push_back(columns[44] + " = " + std::to_string(zone_entry.fog_minclip3)); + update_values.push_back(columns[45] + " = " + std::to_string(zone_entry.fog_maxclip3)); + update_values.push_back(columns[46] + " = " + std::to_string(zone_entry.fog_red4)); + update_values.push_back(columns[47] + " = " + std::to_string(zone_entry.fog_green4)); + update_values.push_back(columns[48] + " = " + std::to_string(zone_entry.fog_blue4)); + update_values.push_back(columns[49] + " = " + std::to_string(zone_entry.fog_minclip4)); + update_values.push_back(columns[50] + " = " + std::to_string(zone_entry.fog_maxclip4)); + update_values.push_back(columns[51] + " = " + std::to_string(zone_entry.fog_density)); + update_values.push_back(columns[52] + " = '" + EscapeString(zone_entry.flag_needed) + "'"); + update_values.push_back(columns[53] + " = " + std::to_string(zone_entry.canbind)); + update_values.push_back(columns[54] + " = " + std::to_string(zone_entry.cancombat)); + update_values.push_back(columns[55] + " = " + std::to_string(zone_entry.canlevitate)); + update_values.push_back(columns[56] + " = " + std::to_string(zone_entry.castoutdoor)); + update_values.push_back(columns[57] + " = " + std::to_string(zone_entry.hotzone)); + update_values.push_back(columns[58] + " = " + std::to_string(zone_entry.insttype)); + update_values.push_back(columns[59] + " = " + std::to_string(zone_entry.shutdowndelay)); + update_values.push_back(columns[60] + " = " + std::to_string(zone_entry.peqzone)); + update_values.push_back(columns[61] + " = " + std::to_string(zone_entry.expansion)); + update_values.push_back(columns[62] + " = " + std::to_string(zone_entry.suspendbuffs)); + update_values.push_back(columns[63] + " = " + std::to_string(zone_entry.rain_chance1)); + update_values.push_back(columns[64] + " = " + std::to_string(zone_entry.rain_chance2)); + update_values.push_back(columns[65] + " = " + std::to_string(zone_entry.rain_chance3)); + update_values.push_back(columns[66] + " = " + std::to_string(zone_entry.rain_chance4)); + update_values.push_back(columns[67] + " = " + std::to_string(zone_entry.rain_duration1)); + update_values.push_back(columns[68] + " = " + std::to_string(zone_entry.rain_duration2)); + update_values.push_back(columns[69] + " = " + std::to_string(zone_entry.rain_duration3)); + update_values.push_back(columns[70] + " = " + std::to_string(zone_entry.rain_duration4)); + update_values.push_back(columns[71] + " = " + std::to_string(zone_entry.snow_chance1)); + update_values.push_back(columns[72] + " = " + std::to_string(zone_entry.snow_chance2)); + update_values.push_back(columns[73] + " = " + std::to_string(zone_entry.snow_chance3)); + update_values.push_back(columns[74] + " = " + std::to_string(zone_entry.snow_chance4)); + update_values.push_back(columns[75] + " = " + std::to_string(zone_entry.snow_duration1)); + update_values.push_back(columns[76] + " = " + std::to_string(zone_entry.snow_duration2)); + update_values.push_back(columns[77] + " = " + std::to_string(zone_entry.snow_duration3)); + update_values.push_back(columns[78] + " = " + std::to_string(zone_entry.snow_duration4)); + update_values.push_back(columns[79] + " = " + std::to_string(zone_entry.gravity)); + update_values.push_back(columns[80] + " = " + std::to_string(zone_entry.type)); + update_values.push_back(columns[81] + " = " + std::to_string(zone_entry.skylock)); + update_values.push_back(columns[82] + " = " + std::to_string(zone_entry.fast_regen_hp)); + update_values.push_back(columns[83] + " = " + std::to_string(zone_entry.fast_regen_mana)); + update_values.push_back(columns[84] + " = " + std::to_string(zone_entry.fast_regen_endurance)); + update_values.push_back(columns[85] + " = " + std::to_string(zone_entry.npc_max_aggro_dist)); + update_values.push_back(columns[86] + " = " + std::to_string(zone_entry.max_movement_update_range)); + update_values.push_back(columns[87] + " = " + std::to_string(zone_entry.min_expansion)); + update_values.push_back(columns[88] + " = " + std::to_string(zone_entry.max_expansion)); + update_values.push_back(columns[89] + " = '" + EscapeString(zone_entry.content_flags) + "'"); + update_values.push_back(columns[90] + " = '" + EscapeString(zone_entry.content_flags_disabled) + "'"); + update_values.push_back(columns[91] + " = " + std::to_string(zone_entry.underworld_teleport_index)); auto results = db.QueryDatabase( fmt::format( @@ -614,6 +619,7 @@ public: insert_values.push_back(std::to_string(zone_entry.safe_x)); insert_values.push_back(std::to_string(zone_entry.safe_y)); insert_values.push_back(std::to_string(zone_entry.safe_z)); + insert_values.push_back(std::to_string(zone_entry.safe_heading)); insert_values.push_back(std::to_string(zone_entry.graveyard_id)); insert_values.push_back(std::to_string(zone_entry.min_level)); insert_values.push_back(std::to_string(zone_entry.min_status)); @@ -734,6 +740,7 @@ public: insert_values.push_back(std::to_string(zone_entry.safe_x)); insert_values.push_back(std::to_string(zone_entry.safe_y)); insert_values.push_back(std::to_string(zone_entry.safe_z)); + insert_values.push_back(std::to_string(zone_entry.safe_heading)); insert_values.push_back(std::to_string(zone_entry.graveyard_id)); insert_values.push_back(std::to_string(zone_entry.min_level)); insert_values.push_back(std::to_string(zone_entry.min_status)); @@ -858,89 +865,90 @@ public: entry.safe_x = static_cast(atof(row[5])); entry.safe_y = static_cast(atof(row[6])); entry.safe_z = static_cast(atof(row[7])); - entry.graveyard_id = static_cast(atof(row[8])); - entry.min_level = atoi(row[9]); - entry.min_status = atoi(row[10]); - entry.zoneidnumber = atoi(row[11]); - entry.version = atoi(row[12]); - entry.timezone = atoi(row[13]); - entry.maxclients = atoi(row[14]); - entry.ruleset = atoi(row[15]); - entry.note = row[16] ? row[16] : ""; - entry.underworld = static_cast(atof(row[17])); - entry.minclip = static_cast(atof(row[18])); - entry.maxclip = static_cast(atof(row[19])); - entry.fog_minclip = static_cast(atof(row[20])); - entry.fog_maxclip = static_cast(atof(row[21])); - entry.fog_blue = atoi(row[22]); - entry.fog_red = atoi(row[23]); - entry.fog_green = atoi(row[24]); - entry.sky = atoi(row[25]); - entry.ztype = atoi(row[26]); - entry.zone_exp_multiplier = static_cast(atof(row[27])); - entry.walkspeed = static_cast(atof(row[28])); - entry.time_type = atoi(row[29]); - entry.fog_red1 = atoi(row[30]); - entry.fog_green1 = atoi(row[31]); - entry.fog_blue1 = atoi(row[32]); - entry.fog_minclip1 = static_cast(atof(row[33])); - entry.fog_maxclip1 = static_cast(atof(row[34])); - entry.fog_red2 = atoi(row[35]); - entry.fog_green2 = atoi(row[36]); - entry.fog_blue2 = atoi(row[37]); - entry.fog_minclip2 = static_cast(atof(row[38])); - entry.fog_maxclip2 = static_cast(atof(row[39])); - entry.fog_red3 = atoi(row[40]); - entry.fog_green3 = atoi(row[41]); - entry.fog_blue3 = atoi(row[42]); - entry.fog_minclip3 = static_cast(atof(row[43])); - entry.fog_maxclip3 = static_cast(atof(row[44])); - entry.fog_red4 = atoi(row[45]); - entry.fog_green4 = atoi(row[46]); - entry.fog_blue4 = atoi(row[47]); - entry.fog_minclip4 = static_cast(atof(row[48])); - entry.fog_maxclip4 = static_cast(atof(row[49])); - entry.fog_density = static_cast(atof(row[50])); - entry.flag_needed = row[51] ? row[51] : ""; - entry.canbind = atoi(row[52]); - entry.cancombat = atoi(row[53]); - entry.canlevitate = atoi(row[54]); - entry.castoutdoor = atoi(row[55]); - entry.hotzone = atoi(row[56]); - entry.insttype = atoi(row[57]); - entry.shutdowndelay = atoi(row[58]); - entry.peqzone = atoi(row[59]); - entry.expansion = atoi(row[60]); - entry.suspendbuffs = atoi(row[61]); - entry.rain_chance1 = atoi(row[62]); - entry.rain_chance2 = atoi(row[63]); - entry.rain_chance3 = atoi(row[64]); - entry.rain_chance4 = atoi(row[65]); - entry.rain_duration1 = atoi(row[66]); - entry.rain_duration2 = atoi(row[67]); - entry.rain_duration3 = atoi(row[68]); - entry.rain_duration4 = atoi(row[69]); - entry.snow_chance1 = atoi(row[70]); - entry.snow_chance2 = atoi(row[71]); - entry.snow_chance3 = atoi(row[72]); - entry.snow_chance4 = atoi(row[73]); - entry.snow_duration1 = atoi(row[74]); - entry.snow_duration2 = atoi(row[75]); - entry.snow_duration3 = atoi(row[76]); - entry.snow_duration4 = atoi(row[77]); - entry.gravity = static_cast(atof(row[78])); - entry.type = atoi(row[79]); - entry.skylock = atoi(row[80]); - entry.fast_regen_hp = atoi(row[81]); - entry.fast_regen_mana = atoi(row[82]); - entry.fast_regen_endurance = atoi(row[83]); - entry.npc_max_aggro_dist = atoi(row[84]); - entry.max_movement_update_range = atoi(row[85]); - entry.min_expansion = atoi(row[86]); - entry.max_expansion = atoi(row[87]); - entry.content_flags = row[88] ? row[88] : ""; - entry.content_flags_disabled = row[89] ? row[89] : ""; - entry.underworld_teleport_index = atoi(row[90]); + entry.safe_heading = static_cast(atof(row[8])); + entry.graveyard_id = static_cast(atof(row[9])); + entry.min_level = atoi(row[10]); + entry.min_status = atoi(row[11]); + entry.zoneidnumber = atoi(row[12]); + entry.version = atoi(row[13]); + entry.timezone = atoi(row[14]); + entry.maxclients = atoi(row[15]); + entry.ruleset = atoi(row[16]); + entry.note = row[17] ? row[17] : ""; + entry.underworld = static_cast(atof(row[18])); + entry.minclip = static_cast(atof(row[19])); + entry.maxclip = static_cast(atof(row[20])); + entry.fog_minclip = static_cast(atof(row[21])); + entry.fog_maxclip = static_cast(atof(row[22])); + entry.fog_blue = atoi(row[23]); + entry.fog_red = atoi(row[24]); + entry.fog_green = atoi(row[25]); + entry.sky = atoi(row[26]); + entry.ztype = atoi(row[27]); + entry.zone_exp_multiplier = static_cast(atof(row[28])); + entry.walkspeed = static_cast(atof(row[29])); + entry.time_type = atoi(row[30]); + entry.fog_red1 = atoi(row[31]); + entry.fog_green1 = atoi(row[32]); + entry.fog_blue1 = atoi(row[33]); + entry.fog_minclip1 = static_cast(atof(row[34])); + entry.fog_maxclip1 = static_cast(atof(row[35])); + entry.fog_red2 = atoi(row[36]); + entry.fog_green2 = atoi(row[37]); + entry.fog_blue2 = atoi(row[38]); + entry.fog_minclip2 = static_cast(atof(row[39])); + entry.fog_maxclip2 = static_cast(atof(row[40])); + entry.fog_red3 = atoi(row[41]); + entry.fog_green3 = atoi(row[42]); + entry.fog_blue3 = atoi(row[43]); + entry.fog_minclip3 = static_cast(atof(row[44])); + entry.fog_maxclip3 = static_cast(atof(row[45])); + entry.fog_red4 = atoi(row[46]); + entry.fog_green4 = atoi(row[47]); + entry.fog_blue4 = atoi(row[48]); + entry.fog_minclip4 = static_cast(atof(row[49])); + entry.fog_maxclip4 = static_cast(atof(row[50])); + entry.fog_density = static_cast(atof(row[51])); + entry.flag_needed = row[52] ? row[52] : ""; + entry.canbind = atoi(row[53]); + entry.cancombat = atoi(row[54]); + entry.canlevitate = atoi(row[55]); + entry.castoutdoor = atoi(row[56]); + entry.hotzone = atoi(row[57]); + entry.insttype = atoi(row[58]); + entry.shutdowndelay = strtoll(row[59], NULL, 10); + entry.peqzone = atoi(row[60]); + entry.expansion = atoi(row[61]); + entry.suspendbuffs = atoi(row[62]); + entry.rain_chance1 = atoi(row[63]); + entry.rain_chance2 = atoi(row[64]); + entry.rain_chance3 = atoi(row[65]); + entry.rain_chance4 = atoi(row[66]); + entry.rain_duration1 = atoi(row[67]); + entry.rain_duration2 = atoi(row[68]); + entry.rain_duration3 = atoi(row[69]); + entry.rain_duration4 = atoi(row[70]); + entry.snow_chance1 = atoi(row[71]); + entry.snow_chance2 = atoi(row[72]); + entry.snow_chance3 = atoi(row[73]); + entry.snow_chance4 = atoi(row[74]); + entry.snow_duration1 = atoi(row[75]); + entry.snow_duration2 = atoi(row[76]); + entry.snow_duration3 = atoi(row[77]); + entry.snow_duration4 = atoi(row[78]); + entry.gravity = static_cast(atof(row[79])); + entry.type = atoi(row[80]); + entry.skylock = atoi(row[81]); + entry.fast_regen_hp = atoi(row[82]); + entry.fast_regen_mana = atoi(row[83]); + entry.fast_regen_endurance = atoi(row[84]); + entry.npc_max_aggro_dist = atoi(row[85]); + entry.max_movement_update_range = atoi(row[86]); + entry.min_expansion = atoi(row[87]); + entry.max_expansion = atoi(row[88]); + entry.content_flags = row[89] ? row[89] : ""; + entry.content_flags_disabled = row[90] ? row[90] : ""; + entry.underworld_teleport_index = atoi(row[91]); all_entries.push_back(entry); } @@ -973,89 +981,90 @@ public: entry.safe_x = static_cast(atof(row[5])); entry.safe_y = static_cast(atof(row[6])); entry.safe_z = static_cast(atof(row[7])); - entry.graveyard_id = static_cast(atof(row[8])); - entry.min_level = atoi(row[9]); - entry.min_status = atoi(row[10]); - entry.zoneidnumber = atoi(row[11]); - entry.version = atoi(row[12]); - entry.timezone = atoi(row[13]); - entry.maxclients = atoi(row[14]); - entry.ruleset = atoi(row[15]); - entry.note = row[16] ? row[16] : ""; - entry.underworld = static_cast(atof(row[17])); - entry.minclip = static_cast(atof(row[18])); - entry.maxclip = static_cast(atof(row[19])); - entry.fog_minclip = static_cast(atof(row[20])); - entry.fog_maxclip = static_cast(atof(row[21])); - entry.fog_blue = atoi(row[22]); - entry.fog_red = atoi(row[23]); - entry.fog_green = atoi(row[24]); - entry.sky = atoi(row[25]); - entry.ztype = atoi(row[26]); - entry.zone_exp_multiplier = static_cast(atof(row[27])); - entry.walkspeed = static_cast(atof(row[28])); - entry.time_type = atoi(row[29]); - entry.fog_red1 = atoi(row[30]); - entry.fog_green1 = atoi(row[31]); - entry.fog_blue1 = atoi(row[32]); - entry.fog_minclip1 = static_cast(atof(row[33])); - entry.fog_maxclip1 = static_cast(atof(row[34])); - entry.fog_red2 = atoi(row[35]); - entry.fog_green2 = atoi(row[36]); - entry.fog_blue2 = atoi(row[37]); - entry.fog_minclip2 = static_cast(atof(row[38])); - entry.fog_maxclip2 = static_cast(atof(row[39])); - entry.fog_red3 = atoi(row[40]); - entry.fog_green3 = atoi(row[41]); - entry.fog_blue3 = atoi(row[42]); - entry.fog_minclip3 = static_cast(atof(row[43])); - entry.fog_maxclip3 = static_cast(atof(row[44])); - entry.fog_red4 = atoi(row[45]); - entry.fog_green4 = atoi(row[46]); - entry.fog_blue4 = atoi(row[47]); - entry.fog_minclip4 = static_cast(atof(row[48])); - entry.fog_maxclip4 = static_cast(atof(row[49])); - entry.fog_density = static_cast(atof(row[50])); - entry.flag_needed = row[51] ? row[51] : ""; - entry.canbind = atoi(row[52]); - entry.cancombat = atoi(row[53]); - entry.canlevitate = atoi(row[54]); - entry.castoutdoor = atoi(row[55]); - entry.hotzone = atoi(row[56]); - entry.insttype = atoi(row[57]); - entry.shutdowndelay = atoi(row[58]); - entry.peqzone = atoi(row[59]); - entry.expansion = atoi(row[60]); - entry.suspendbuffs = atoi(row[61]); - entry.rain_chance1 = atoi(row[62]); - entry.rain_chance2 = atoi(row[63]); - entry.rain_chance3 = atoi(row[64]); - entry.rain_chance4 = atoi(row[65]); - entry.rain_duration1 = atoi(row[66]); - entry.rain_duration2 = atoi(row[67]); - entry.rain_duration3 = atoi(row[68]); - entry.rain_duration4 = atoi(row[69]); - entry.snow_chance1 = atoi(row[70]); - entry.snow_chance2 = atoi(row[71]); - entry.snow_chance3 = atoi(row[72]); - entry.snow_chance4 = atoi(row[73]); - entry.snow_duration1 = atoi(row[74]); - entry.snow_duration2 = atoi(row[75]); - entry.snow_duration3 = atoi(row[76]); - entry.snow_duration4 = atoi(row[77]); - entry.gravity = static_cast(atof(row[78])); - entry.type = atoi(row[79]); - entry.skylock = atoi(row[80]); - entry.fast_regen_hp = atoi(row[81]); - entry.fast_regen_mana = atoi(row[82]); - entry.fast_regen_endurance = atoi(row[83]); - entry.npc_max_aggro_dist = atoi(row[84]); - entry.max_movement_update_range = atoi(row[85]); - entry.min_expansion = atoi(row[86]); - entry.max_expansion = atoi(row[87]); - entry.content_flags = row[88] ? row[88] : ""; - entry.content_flags_disabled = row[89] ? row[89] : ""; - entry.underworld_teleport_index = atoi(row[90]); + entry.safe_heading = static_cast(atof(row[8])); + entry.graveyard_id = static_cast(atof(row[9])); + entry.min_level = atoi(row[10]); + entry.min_status = atoi(row[11]); + entry.zoneidnumber = atoi(row[12]); + entry.version = atoi(row[13]); + entry.timezone = atoi(row[14]); + entry.maxclients = atoi(row[15]); + entry.ruleset = atoi(row[16]); + entry.note = row[17] ? row[17] : ""; + entry.underworld = static_cast(atof(row[18])); + entry.minclip = static_cast(atof(row[19])); + entry.maxclip = static_cast(atof(row[20])); + entry.fog_minclip = static_cast(atof(row[21])); + entry.fog_maxclip = static_cast(atof(row[22])); + entry.fog_blue = atoi(row[23]); + entry.fog_red = atoi(row[24]); + entry.fog_green = atoi(row[25]); + entry.sky = atoi(row[26]); + entry.ztype = atoi(row[27]); + entry.zone_exp_multiplier = static_cast(atof(row[28])); + entry.walkspeed = static_cast(atof(row[29])); + entry.time_type = atoi(row[30]); + entry.fog_red1 = atoi(row[31]); + entry.fog_green1 = atoi(row[32]); + entry.fog_blue1 = atoi(row[33]); + entry.fog_minclip1 = static_cast(atof(row[34])); + entry.fog_maxclip1 = static_cast(atof(row[35])); + entry.fog_red2 = atoi(row[36]); + entry.fog_green2 = atoi(row[37]); + entry.fog_blue2 = atoi(row[38]); + entry.fog_minclip2 = static_cast(atof(row[39])); + entry.fog_maxclip2 = static_cast(atof(row[40])); + entry.fog_red3 = atoi(row[41]); + entry.fog_green3 = atoi(row[42]); + entry.fog_blue3 = atoi(row[43]); + entry.fog_minclip3 = static_cast(atof(row[44])); + entry.fog_maxclip3 = static_cast(atof(row[45])); + entry.fog_red4 = atoi(row[46]); + entry.fog_green4 = atoi(row[47]); + entry.fog_blue4 = atoi(row[48]); + entry.fog_minclip4 = static_cast(atof(row[49])); + entry.fog_maxclip4 = static_cast(atof(row[50])); + entry.fog_density = static_cast(atof(row[51])); + entry.flag_needed = row[52] ? row[52] : ""; + entry.canbind = atoi(row[53]); + entry.cancombat = atoi(row[54]); + entry.canlevitate = atoi(row[55]); + entry.castoutdoor = atoi(row[56]); + entry.hotzone = atoi(row[57]); + entry.insttype = atoi(row[58]); + entry.shutdowndelay = strtoll(row[59], NULL, 10); + entry.peqzone = atoi(row[60]); + entry.expansion = atoi(row[61]); + entry.suspendbuffs = atoi(row[62]); + entry.rain_chance1 = atoi(row[63]); + entry.rain_chance2 = atoi(row[64]); + entry.rain_chance3 = atoi(row[65]); + entry.rain_chance4 = atoi(row[66]); + entry.rain_duration1 = atoi(row[67]); + entry.rain_duration2 = atoi(row[68]); + entry.rain_duration3 = atoi(row[69]); + entry.rain_duration4 = atoi(row[70]); + entry.snow_chance1 = atoi(row[71]); + entry.snow_chance2 = atoi(row[72]); + entry.snow_chance3 = atoi(row[73]); + entry.snow_chance4 = atoi(row[74]); + entry.snow_duration1 = atoi(row[75]); + entry.snow_duration2 = atoi(row[76]); + entry.snow_duration3 = atoi(row[77]); + entry.snow_duration4 = atoi(row[78]); + entry.gravity = static_cast(atof(row[79])); + entry.type = atoi(row[80]); + entry.skylock = atoi(row[81]); + entry.fast_regen_hp = atoi(row[82]); + entry.fast_regen_mana = atoi(row[83]); + entry.fast_regen_endurance = atoi(row[84]); + entry.npc_max_aggro_dist = atoi(row[85]); + entry.max_movement_update_range = atoi(row[86]); + entry.min_expansion = atoi(row[87]); + entry.max_expansion = atoi(row[88]); + entry.content_flags = row[89] ? row[89] : ""; + entry.content_flags_disabled = row[90] ? row[90] : ""; + entry.underworld_teleport_index = atoi(row[91]); all_entries.push_back(entry); } From 92914d86f10698bc5837b0c03b4f27fe2034f39c Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 5 Jul 2021 13:02:49 -0400 Subject: [PATCH 116/624] Add defines (commented) for further known SPAs (#1446) * Add defines (commented) for further known SPAs Added as comments since that appears what we were doing. This just gets us caught up to dev post. * More SPAs --- common/spdat.h | 71 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 63 insertions(+), 8 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 7b3ff5ed7..837f10f78 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -800,18 +800,73 @@ typedef enum { #define SE_ResourceTap 457 // implemented Coverts a percent of dmg from dmg spells(DD/DoT) to hp/mana/end. #define SE_FactionModPct 458 // implemented Modifies faction gains and losses by percent. #define SE_DamageModifier2 459 // implemented - Modifies melee damage by skill type -//#define SE_Ff_Override_NotFocusable 460 // +//#define SE_Ff_Override_NotFocusable 460 // #define SE_ImprovedDamage2 461 // implemented - Increase spell damage by percent (SE_Fc_Damage_%2) -#define SE_FcDamageAmt2 462 // implemented - Increase spell damage by flat amount (SE_Fc_Damage_Amt2) -//#define SE_Shield_Target 463 // +#define SE_FcDamageAmt2 462 // implemented - Increase spell damage by flat amount (SE_Fc_Damage_Amt2) +//#define SE_Shield_Target 463 // #define SE_PC_Pet_Rampage 464 // implemented - Base1 % chance to do rampage for base2 % of damage each melee round //#define SE_PC_Pet_AE_Rampage 465 // Would assume as above but need to confirm. #define SE_PC_Pet_Flurry_Chance 466 // implemented - Base1 % chance to do flurry from double attack hit. -//#define SE_DS_Mitigation_Amount 467 // -//#define SE_DS_Mitigation_Percentage 468 // -//#define SE_Chance_Best_in_Spell_Grp 469 // -//#define SE_Trigger_Best_in_Spell Grp 470 // -//#define SE_Double_Melee_Round 471 // +//#define SE_DS_Mitigation_Amount 467 // +//#define SE_DS_Mitigation_Percentage 468 // +//#define SE_Chance_Best_in_Spell_Grp 469 // +//#define SE_Trigger_Best_in_Spell Grp 470 // +//#define SE_Double_Melee_Round 471 // +//#define SE_Buy_AA_Rank 472 // +//#define SE_Double_Backstab_Front 473 // +//#define SE_Pet_Crit_Melee_Damage_Pct_Owner 474 // +//#define SE_Trigger_Spell_Non_Item 475 // +//#define SE_Weapon_Stance 476 // +//#define SE_Hatelist_To_Top_Index 477 // +//#define SE_Hatelist_To_Tail_Index 478 // +//#define SE_Ff_Value_Min 479 // +//#define SE_Ff_Value_Max 480 // +//#define SE_Fc_Cast_Spell_On_Land 481 // +//#define SE_Skill_Base_Damage_Mod 482 // +//#define SE_Fc_Spell_Damage_Pct_IncomingPC 483 // +//#define SE_Fc_Spell_Damage_Amt_IncomingPC 484 // +//#define SE_Ff_CasterClass 485 // +//#define SE_Ff_Same_Caster 486 // +//#define SE_Extend_Tradeskill_Cap 487 // +//#define SE_Defender_Melee_Force_Pct_PC 488 // +//#define SE_Worn_Endurance_Regen_Cap 489 // +//#define SE_Ff_ReuseTimeMin 490 // +//#define SE_Ff_ReuseTimeMax 491 // +//#define SE_Ff_Endurance_Min 492 // +//#define SE_Ff_Endurance_Max 493 // +//#define SE_Pet_Add_Atk 494 // +//#define SE_Ff_DurationMax 495 // +//#define SE_Critical_Melee_Damage_Mod_Max 496 // +//#define SE_Ff_FocusCastProcNoBypass 497 // +//#define SE_AddExtraAttackPct_1h_Primary 498 // +//#define SE_AddExtraAttackPct_1h_Secondary 499 // +//#define SE_Fc_CastTimeMod2 500 // +//#define SE_Fc_CastTimeAmt 501 // +//#define SE_Fearstun 502 // +//#define SE_Melee_Damage_Position_Mod 503 // +//#define SE_Melee_Damage_Position_Amt 504 // +//#define SE_Damage_Taken_Position_Mod 505 // +//#define SE_Damage_Taken_Position_Amt 506 // +//#define SE_Fc_Amplify_Mod 507 // +//#define SE_Fc_Amplify_Amt 508 // +//#define SE_Health_Transfer 509 // +//#define SE_Fc_ResistIncoming 510 // +//#define SE_Ff_FocusTimerMin 511 // +//#define SE_Proc_Timer_Modifier 512 // +//#define SE_Mana_Max_Percent 513 // +//#define SE_Endurance_Max_Percent 514 // +//#define SE_AC_Avoidance_Max_Percent 515 // +//#define SE_AC_Mitigation_Max_Percent 516 // +//#define SE_Attack_Offense_Max_Percent 517 // +//#define SE_Attack_Accuracy_Max_Percent 518 // +//#define SE_Luck_Amount 519 // +//#define SE_Luck_Percent 520 // +//#define SE_Endurance_Absorb_Pct_Damage 521 // +//#define SE_Instant_Mana_Pct 522 // +//#define SE_Instant_Endurance_Pct 523 // +//#define SE_Duration_HP_Pct 524 // +//#define SE_Duration_Mana_Pct 525 // +//#define SE_Duration_Endurance_Pct 526 // // LAST From f5ab135906afc662bd3bce3967f833436b43f2af Mon Sep 17 00:00:00 2001 From: Dencelle Date: Wed, 7 Jul 2021 10:55:23 -0500 Subject: [PATCH 117/624] [Commands] Make #maxskills work on target. (#1445) * [Commands] Make #maxskills work on target. * Update command.cpp * don't code when you first wake up... doesn't end well... * another typo brought to you by lack of coffee --- zone/command.cpp | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 50b209af1..d786e505c 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -12852,19 +12852,17 @@ void command_cvs(Client *c, const Seperator *sep) void command_max_all_skills(Client *c, const Seperator *sep) { - if(c) - { - for (int i = 0; i <= EQ::skills::HIGHEST_SKILL; ++i) - { - if (i >= EQ::skills::SkillSpecializeAbjure && i <= EQ::skills::SkillSpecializeEvocation) - { - c->SetSkill((EQ::skills::SkillType)i, 50); - } - else - { - int max_skill_level = content_db.GetSkillCap(c->GetClass(), (EQ::skills::SkillType)i, c->GetLevel()); - c->SetSkill((EQ::skills::SkillType)i, max_skill_level); - } + if(c) { + Client* client_target = (c->GetTarget() ? (c->GetTarget()->IsClient() ? c->GetTarget()->CastToClient() : c) : c); + auto Skills = EQ::skills::GetSkillTypeMap(); + for (auto& skills_iter : Skills) { + auto skill_id = skills_iter.first; + auto current_skill_value = ( + (EQ::skills::IsSpecializedSkill(skill_id)) ? + 50 : + content_db.GetSkillCap(client_target->GetClass(), skill_id, client_target->GetLevel()) + ); + client_target->SetSkill(skill_id, current_skill_value); } } } From 36e009a5bd7d675bd4aba85d465e71ec85abad6d Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Thu, 8 Jul 2021 11:38:57 -0500 Subject: [PATCH 118/624] Allow NPCs to aggro player pets with NPCAggro field set in database and new rule AggroPlayerPets set to true (#1450) Co-authored-by: Natedog2012 --- common/ruletypes.h | 1 + zone/aggro.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 409769bea..c0e740493 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -526,6 +526,7 @@ RULE_INT(Aggro, ClientAggroCheckMovingInterval, 1000, "Interval in which clients RULE_INT(Aggro, ClientAggroCheckIdleInterval, 6000, "Interval in which clients actually check for aggro while idle - in milliseconds - this should be higher than ClientAggroCheckMovingInterval") RULE_REAL(Aggro, PetAttackRange, 40000.0, "Maximum squared range /pet attack works at default is 200") RULE_BOOL(Aggro, NPCAggroMaxDistanceEnabled, true, "If enabled, NPC's will drop aggro beyond 600 units or what is defined at the zone level") +RULE_BOOL(Aggro, AggroPlayerPets, false, "If enabled, NPCs will aggro player pets") RULE_CATEGORY_END() RULE_CATEGORY(TaskSystem) diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 33221b63a..5829bc3a3 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -252,7 +252,7 @@ bool Mob::CheckWillAggro(Mob *mob) { } Mob *pet_owner = mob->GetOwner(); - if (pet_owner && pet_owner->IsClient()) { + if (pet_owner && pet_owner->IsClient() && (!RuleB(Aggro, AggroPlayerPets) || pet_owner->CastToClient()->GetGM())) { return false; } From c72a37a4346d917fb9a83e9d321d5fc625dc5197 Mon Sep 17 00:00:00 2001 From: Gangsta <48196367+GangstaEQ@users.noreply.github.com> Date: Thu, 8 Jul 2021 09:39:58 -0700 Subject: [PATCH 119/624] [Bugfix] Fixes guards assisting mobs against players (#1448) Co-authored-by: ProducerZekServer --- zone/attack.cpp | 4 ++-- zone/spells.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 980bd0dc9..d7450c341 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1524,7 +1524,7 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b //Guard Assist Code if (RuleB(Character, PVPEnableGuardFactionAssist)) { - if (IsClient() || (HasOwner() && GetOwner()->IsClient())) { + if (IsClient() && other->IsClient() || (HasOwner() && GetOwner()->IsClient() && other->IsClient() )) { auto& mob_list = entity_list.GetCloseMobList(other); for (auto& e : mob_list) { auto mob = e.second; @@ -2023,7 +2023,7 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool //Guard Assist Code if (RuleB(Character, PVPEnableGuardFactionAssist)) { - if (IsClient() || (HasOwner() && GetOwner()->IsClient())) { + if (IsClient() && other->IsClient() || (HasOwner() && GetOwner()->IsClient() && other->IsClient())) { auto& mob_list = entity_list.GetCloseMobList(other); for (auto& e : mob_list) { auto mob = e.second; diff --git a/zone/spells.cpp b/zone/spells.cpp index dee96509e..c7029b73d 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2057,7 +2057,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui //Guard Assist Code if (RuleB(Character, PVPEnableGuardFactionAssist) && spell_target && IsDetrimentalSpell(spell_id) && spell_target != this) { - if (IsClient() || (HasOwner() && GetOwner()->IsClient())) { + if (IsClient() && spell_target->IsClient()|| (HasOwner() && GetOwner()->IsClient() && spell_target->IsClient())) { auto& mob_list = entity_list.GetCloseMobList(spell_target); for (auto& e : mob_list) { auto mob = e.second; From ab89fe13b46ebb6059f0dba9314a0b2e35187c9c Mon Sep 17 00:00:00 2001 From: Dencelle Date: Thu, 8 Jul 2021 11:43:13 -0500 Subject: [PATCH 120/624] [Code] Addition of zone constants for hard coding (#1443) --- common/eq_constants.h | 486 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 486 insertions(+) diff --git a/common/eq_constants.h b/common/eq_constants.h index e5fbd9f64..988ac7e60 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -197,6 +197,492 @@ namespace Chat { const uint16 Stun = 340; }; +// generation SQL: +// SELECT CONCAT(' constexpr uint16 ', UPPER(short_name), ' = ' , zoneidnumber, '; // ', long_name) from zone group by zoneidnumber ORDER BY zoneidnumber; +namespace Zones { + constexpr uint16 QEYNOS = 1; // South Qeynos + constexpr uint16 QEYNOS2 = 2; // North Qeynos + constexpr uint16 QRG = 3; // The Surefall Glade + constexpr uint16 QEYTOQRG = 4; // The Qeynos Hills + constexpr uint16 HIGHPASS = 5; // Highpass Hold + constexpr uint16 HIGHKEEP = 6; // High Keep + constexpr uint16 FREPORTN = 8; // North Freeport + constexpr uint16 FREPORTW = 9; // West Freeport + constexpr uint16 FREPORTE = 10; // East Freeport + constexpr uint16 RUNNYEYE = 11; // The Liberated Citadel of Runnyeye + constexpr uint16 QEY2HH1 = 12; // The Western Plains of Karana + constexpr uint16 NORTHKARANA = 13; // The Northern Plains of Karana + constexpr uint16 SOUTHKARANA = 14; // The Southern Plains of Karana + constexpr uint16 EASTKARANA = 15; // Eastern Plains of Karana + constexpr uint16 BEHOLDER = 16; // Gorge of King Xorbb + constexpr uint16 BLACKBURROW = 17; // Blackburrow + constexpr uint16 PAW = 18; // The Lair of the Splitpaw + constexpr uint16 RIVERVALE = 19; // Rivervale + constexpr uint16 KITHICOR = 20; // Kithicor Forest + constexpr uint16 COMMONS = 21; // West Commonlands + constexpr uint16 ECOMMONS = 22; // East Commonlands + constexpr uint16 ERUDNINT = 23; // The Erudin Palace + constexpr uint16 ERUDNEXT = 24; // Erudin + constexpr uint16 NEKTULOS = 25; // The Nektulos Forest + constexpr uint16 CSHOME = 26; // Sunset Home + constexpr uint16 LAVASTORM = 27; // The Lavastorm Mountains + constexpr uint16 NEKTROPOS = 28; // Nektropos + constexpr uint16 HALAS = 29; // Halas + constexpr uint16 EVERFROST = 30; // Everfrost Peaks + constexpr uint16 SOLDUNGA = 31; // Solusek's Eye + constexpr uint16 SOLDUNGB = 32; // Nagafen's Lair + constexpr uint16 MISTY = 33; // Misty Thicket + constexpr uint16 NRO = 34; // Northern Desert of Ro + constexpr uint16 SRO = 35; // Southern Desert of Ro + constexpr uint16 BEFALLEN = 36; // Befallen + constexpr uint16 OASIS = 37; // Oasis of Marr + constexpr uint16 TOX = 38; // Toxxulia Forest + constexpr uint16 HOLE = 39; // The Hole + constexpr uint16 NERIAKA = 40; // Neriak - Foreign Quarter + constexpr uint16 NERIAKB = 41; // Neriak - Commons + constexpr uint16 NERIAKC = 42; // Neriak - 3rd Gate + constexpr uint16 NERIAKD = 43; // Neriak Palace + constexpr uint16 NAJENA = 44; // Najena + constexpr uint16 QCAT = 45; // The Qeynos Aqueduct System + constexpr uint16 INNOTHULE = 46; // Innothule Swamp + constexpr uint16 FEERROTT = 47; // The Feerrott + constexpr uint16 CAZICTHULE = 48; // Accursed Temple of CazicThule + constexpr uint16 OGGOK = 49; // Oggok + constexpr uint16 RATHEMTN = 50; // The Rathe Mountains + constexpr uint16 LAKERATHE = 51; // Lake Rathetear + constexpr uint16 GROBB = 52; // Grobb + constexpr uint16 AVIAK = 53; // Aviak Village + constexpr uint16 GFAYDARK = 54; // The Greater Faydark + constexpr uint16 AKANON = 55; // Ak'Anon + constexpr uint16 STEAMFONT = 56; // Steamfont Mountains + constexpr uint16 LFAYDARK = 57; // The Lesser Faydark + constexpr uint16 CRUSHBONE = 58; // Crushbone + constexpr uint16 MISTMOORE = 59; // The Castle of Mistmoore + constexpr uint16 KALADIMA = 60; // South Kaladim + constexpr uint16 FELWITHEA = 61; // Northern Felwithe + constexpr uint16 FELWITHEB = 62; // Southern Felwithe + constexpr uint16 UNREST = 63; // The Estate of Unrest + constexpr uint16 KEDGE = 64; // Kedge Keep + constexpr uint16 GUKTOP = 65; // The City of Guk + constexpr uint16 GUKBOTTOM = 66; // The Ruins of Old Guk + constexpr uint16 KALADIMB = 67; // North Kaladim + constexpr uint16 BUTCHER = 68; // Butcherblock Mountains + constexpr uint16 OOT = 69; // Ocean of Tears + constexpr uint16 CAULDRON = 70; // Dagnor's Cauldron + constexpr uint16 AIRPLANE = 71; // The Plane of Sky + constexpr uint16 FEARPLANE = 72; // The Plane of Fear + constexpr uint16 PERMAFROST = 73; // The Permafrost Caverns + constexpr uint16 KERRARIDGE = 74; // Kerra Isle + constexpr uint16 PAINEEL = 75; // Paineel + constexpr uint16 HATEPLANE = 76; // Plane of Hate + constexpr uint16 ARENA = 77; // The Arena + constexpr uint16 FIELDOFBONE = 78; // The Field of Bone + constexpr uint16 WARSLIKSWOOD = 79; // The Warsliks Woods + constexpr uint16 SOLTEMPLE = 80; // The Temple of Solusek Ro + constexpr uint16 DROGA = 81; // The Temple of Droga + constexpr uint16 CABWEST = 82; // Cabilis West + constexpr uint16 SWAMPOFNOHOPE = 83; // The Swamp of No Hope + constexpr uint16 FIRIONA = 84; // Firiona Vie + constexpr uint16 LAKEOFILLOMEN = 85; // Lake of Ill Omen + constexpr uint16 DREADLANDS = 86; // The Dreadlands + constexpr uint16 BURNINGWOOD = 87; // The Burning Wood + constexpr uint16 KAESORA = 88; // Kaesora + constexpr uint16 SEBILIS = 89; // The Ruins of Sebilis + constexpr uint16 CITYMIST = 90; // The City of Mist + constexpr uint16 SKYFIRE = 91; // The Skyfire Mountains + constexpr uint16 FRONTIERMTNS = 92; // Frontier Mountains + constexpr uint16 OVERTHERE = 93; // The Overthere + constexpr uint16 EMERALDJUNGLE = 94; // The Emerald Jungle + constexpr uint16 TRAKANON = 95; // Trakanon's Teeth + constexpr uint16 TIMOROUS = 96; // Timorous Deep + constexpr uint16 KURN = 97; // Kurn's Tower + constexpr uint16 ERUDSXING = 98; // Erud's Crossing + constexpr uint16 STONEBRUNT = 100; // The Stonebrunt Mountains + constexpr uint16 WARRENS = 101; // The Warrens + constexpr uint16 KARNOR = 102; // Karnor's Castle + constexpr uint16 CHARDOK = 103; // Chardok + constexpr uint16 DALNIR = 104; // The Crypt of Dalnir + constexpr uint16 CHARASIS = 105; // The Howling Stones + constexpr uint16 CABEAST = 106; // Cabilis East + constexpr uint16 NURGA = 107; // The Mines of Nurga + constexpr uint16 VEESHAN = 108; // Veeshan's Peak + constexpr uint16 VEKSAR = 109; // Veksar + constexpr uint16 ICECLAD = 110; // The Iceclad Ocean + constexpr uint16 FROZENSHADOW = 111; // The Tower of Frozen Shadow + constexpr uint16 VELKETOR = 112; // Velketor's Labyrinth + constexpr uint16 KAEL = 113; // Kael Drakkel + constexpr uint16 SKYSHRINE = 114; // Skyshrine + constexpr uint16 THURGADINA = 115; // The City of Thurgadin + constexpr uint16 EASTWASTES = 116; // Eastern Wastes + constexpr uint16 COBALTSCAR = 117; // Cobaltscar + constexpr uint16 GREATDIVIDE = 118; // The Great Divide + constexpr uint16 WAKENING = 119; // The Wakening Land + constexpr uint16 WESTWASTES = 120; // The Western Wastes + constexpr uint16 CRYSTAL = 121; // The Crystal Caverns + constexpr uint16 NECROPOLIS = 123; // Dragon Necropolis + constexpr uint16 TEMPLEVEESHAN = 124; // The Temple of Veeshan + constexpr uint16 SIRENS = 125; // Siren's Grotto + constexpr uint16 MISCHIEFPLANE = 126; // The Plane of Mischief + constexpr uint16 GROWTHPLANE = 127; // The Plane of Growth + constexpr uint16 SLEEPER = 128; // The Sleeper's Tomb + constexpr uint16 THURGADINB = 129; // Icewell Keep + constexpr uint16 ERUDSXING2 = 130; // Marauders Mire + constexpr uint16 SHADOWHAVEN = 150; // Shadow Haven + constexpr uint16 BAZAAR = 151; // The Bazaar + constexpr uint16 NEXUS = 152; // Nexus + constexpr uint16 ECHO_ = 153; // The Echo Caverns + constexpr uint16 ACRYLIA = 154; // The Acrylia Caverns + constexpr uint16 SHARVAHL = 155; // The City of Shar Vahl + constexpr uint16 PALUDAL = 156; // The Paludal Caverns + constexpr uint16 FUNGUSGROVE = 157; // The Fungus Grove + constexpr uint16 VEXTHAL = 158; // Vex Thal + constexpr uint16 SSERU = 159; // Sanctus Seru + constexpr uint16 KATTA = 160; // Katta Castellum + constexpr uint16 NETHERBIAN = 161; // Netherbian Lair + constexpr uint16 SSRATEMPLE = 162; // Ssraeshza Temple + constexpr uint16 GRIEGSEND = 163; // Grieg's End + constexpr uint16 THEDEEP = 164; // The Deep + constexpr uint16 SHADEWEAVER = 165; // Shadeweaver's Thicket + constexpr uint16 HOLLOWSHADE = 166; // Hollowshade Moor + constexpr uint16 GRIMLING = 167; // Grimling Forest + constexpr uint16 MSERU = 168; // Marus Seru + constexpr uint16 LETALIS = 169; // Mons Letalis + constexpr uint16 TWILIGHT = 170; // The Twilight Sea + constexpr uint16 THEGREY = 171; // The Grey + constexpr uint16 TENEBROUS = 172; // The Tenebrous Mountains + constexpr uint16 MAIDEN = 173; // The Maiden's Eye + constexpr uint16 DAWNSHROUD = 174; // The Dawnshroud Peaks + constexpr uint16 SCARLET = 175; // The Scarlet Desert + constexpr uint16 UMBRAL = 176; // The Umbral Plains + constexpr uint16 AKHEVA = 179; // The Akheva Ruins + constexpr uint16 ARENA2 = 180; // The Arena Two + constexpr uint16 JAGGEDPINE = 181; // The Jaggedpine Forest + constexpr uint16 NEDARIA = 182; // Nedaria's Landing + constexpr uint16 TUTORIAL = 183; // EverQuest Tutorial + constexpr uint16 LOAD = 184; // Loading Zone + constexpr uint16 LOAD2 = 185; // New Loading Zone + constexpr uint16 HATEPLANEB = 186; // The Plane of Hate + constexpr uint16 SHADOWREST = 187; // Shadowrest + constexpr uint16 TUTORIALA = 188; // The Mines of Gloomingdeep + constexpr uint16 TUTORIALB = 189; // The Mines of Gloomingdeep + constexpr uint16 CLZ = 190; // Loading + constexpr uint16 CODECAY = 200; // The Crypt of Decay + constexpr uint16 POJUSTICE = 201; // The Plane of Justice + constexpr uint16 POKNOWLEDGE = 202; // The Plane of Knowledge + constexpr uint16 POTRANQUILITY = 203; // The Plane of Tranquility + constexpr uint16 PONIGHTMARE = 204; // The Plane of Nightmares + constexpr uint16 PODISEASE = 205; // The Plane of Disease + constexpr uint16 POINNOVATION = 206; // The Plane of Innovation + constexpr uint16 POTORMENT = 207; // Torment, the Plane of Pain + constexpr uint16 POVALOR = 208; // The Plane of Valor + constexpr uint16 BOTHUNDER = 209; // Bastion of Thunder + constexpr uint16 POSTORMS = 210; // The Plane of Storms + constexpr uint16 HOHONORA = 211; // The Halls of Honor + constexpr uint16 SOLROTOWER = 212; // The Tower of Solusek Ro + constexpr uint16 POWAR = 213; // Plane of War + constexpr uint16 POTACTICS = 214; // Drunder, the Fortress of Zek + constexpr uint16 POAIR = 215; // The Plane of Air + constexpr uint16 POWATER = 216; // The Plane of Water + constexpr uint16 POFIRE = 217; // The Plane of Fire + constexpr uint16 POEARTHA = 218; // The Plane of Earth + constexpr uint16 POTIMEA = 219; // The Plane of Time + constexpr uint16 HOHONORB = 220; // The Temple of Marr + constexpr uint16 NIGHTMAREB = 221; // The Lair of Terris Thule + constexpr uint16 POEARTHB = 222; // The Plane of Earth + constexpr uint16 POTIMEB = 223; // The Plane of Time + constexpr uint16 GUNTHAK = 224; // The Gulf of Gunthak + constexpr uint16 DULAK = 225; // Dulak's Harbor + constexpr uint16 TORGIRAN = 226; // The Torgiran Mines + constexpr uint16 NADOX = 227; // The Crypt of Nadox + constexpr uint16 HATESFURY = 228; // Hate's Fury + constexpr uint16 GUKA = 229; // Deepest Guk: Cauldron of Lost Souls + constexpr uint16 RUJA = 230; // The Rujarkian Hills: Bloodied Quarries + constexpr uint16 TAKA = 231; // Takish-Hiz: Sunken Library + constexpr uint16 MIRA = 232; // Miragul's Menagerie: Silent Gallery + constexpr uint16 MMCA = 233; // Mistmoore's Catacombs: Forlorn Caverns + constexpr uint16 GUKB = 234; // The Drowning Crypt + constexpr uint16 RUJB = 235; // The Rujarkian Hills: Halls of War + constexpr uint16 TAKB = 236; // Takish-Hiz: Shifting Tower + constexpr uint16 MIRB = 237; // Miragul's Menagerie: Frozen Nightmare + constexpr uint16 MMCB = 238; // Mistmoore's Catacombs: Dreary Grotto + constexpr uint16 GUKC = 239; // Deepest Guk: Ancient Aqueducts + constexpr uint16 RUJC = 240; // The Rujarkian Hills: Wind Bridges + constexpr uint16 TAKC = 241; // Takish-Hiz: Within the Compact + constexpr uint16 MIRC = 242; // The Spider Den + constexpr uint16 MMCC = 243; // Mistmoore's Catacombs: Struggles within the Progeny + constexpr uint16 GUKD = 244; // The Mushroom Grove + constexpr uint16 RUJD = 245; // The Rujarkian Hills: Prison Break + constexpr uint16 TAKD = 246; // Takish-Hiz: Royal Observatory + constexpr uint16 MIRD = 247; // Miragul's Menagerie: Hushed Banquet + constexpr uint16 MMCD = 248; // Mistmoore's Catacombs: Chambers of Eternal Affliction + constexpr uint16 GUKE = 249; // Deepest Guk: The Curse Reborn + constexpr uint16 RUJE = 250; // The Rujarkian Hills: Drudge Hollows + constexpr uint16 TAKE = 251; // Takish-Hiz: River of Recollection + constexpr uint16 MIRE = 252; // The Frosted Halls + constexpr uint16 MMCE = 253; // Mistmoore's Catacombs: Sepulcher of the Damned + constexpr uint16 GUKF = 254; // Deepest Guk: Chapel of the Witnesses + constexpr uint16 RUJF = 255; // The Rujarkian Hills: Fortified Lair of the Taskmasters + constexpr uint16 TAKF = 256; // Takish-Hiz: Sandfall Corridors + constexpr uint16 MIRF = 257; // The Forgotten Wastes + constexpr uint16 MMCF = 258; // Mistmoore's Catacombs: Scion Lair of Fury + constexpr uint16 GUKG = 259; // The Root Garden + constexpr uint16 RUJG = 260; // The Rujarkian Hills: Hidden Vale of Deceit + constexpr uint16 TAKG = 261; // Takish-Hiz: Balancing Chamber + constexpr uint16 MIRG = 262; // Miragul's Menagerie: Heart of the Menagerie + constexpr uint16 MMCG = 263; // Mistmoore's Catacombs: Cesspits of Putrescence + constexpr uint16 GUKH = 264; // Deepest Guk: Accursed Sanctuary + constexpr uint16 RUJH = 265; // The Rujarkian Hills: Blazing Forge + constexpr uint16 TAKH = 266; // Takish-Hiz: Sweeping Tides + constexpr uint16 MIRH = 267; // The Morbid Laboratory + constexpr uint16 MMCH = 268; // Mistmoore's Catacombs: Aisles of Blood + constexpr uint16 RUJI = 269; // The Rujarkian Hills: Arena of Chance + constexpr uint16 TAKI = 270; // Takish-Hiz: Antiquated Palace + constexpr uint16 MIRI = 271; // The Theater of Imprisoned Horror + constexpr uint16 MMCI = 272; // Mistmoore's Catacombs: Halls of Sanguinary Rites + constexpr uint16 RUJJ = 273; // The Rujarkian Hills: Barracks of War + constexpr uint16 TAKJ = 274; // Takish-Hiz: Prismatic Corridors + constexpr uint16 MIRJ = 275; // Miragul's Menagerie: Grand Library + constexpr uint16 MMCJ = 276; // Mistmoore's Catacombs: Infernal Sanctuary + constexpr uint16 CHARDOKB = 277; // Chardok: The Halls of Betrayal + constexpr uint16 SOLDUNGC = 278; // The Caverns of Exile + constexpr uint16 ABYSMAL = 279; // The Abysmal Sea + constexpr uint16 NATIMBI = 280; // Natimbi, the Broken Shores + constexpr uint16 QINIMI = 281; // Qinimi, Court of Nihilia + constexpr uint16 RIWWI = 282; // Riwwi, Coliseum of Games + constexpr uint16 BARINDU = 283; // Barindu, Hanging Gardens + constexpr uint16 FERUBI = 284; // Ferubi, Forgotten Temple of Taelosia + constexpr uint16 SNPOOL = 285; // Sewers of Nihilia, Pool of Sludg + constexpr uint16 SNLAIR = 286; // Sewers of Nihilia, Lair of Trapp + constexpr uint16 SNPLANT = 287; // Sewers of Nihilia, Purifying Pla + constexpr uint16 SNCREMATORY = 288; // Sewers of Nihilia, Emanating Cre + constexpr uint16 TIPT = 289; // Tipt, Treacherous Crags + constexpr uint16 VXED = 290; // Vxed, the Crumbling Caverns + constexpr uint16 YXTTA = 291; // Yxtta, Pulpit of Exiles + constexpr uint16 UQUA = 292; // Uqua, the Ocean God Chantry + constexpr uint16 KODTAZ = 293; // Kod'Taz, Broken Trial Grounds + constexpr uint16 IKKINZ = 294; // Ikkinz, Chambers of Transcendence + constexpr uint16 QVIC = 295; // Qvic, Prayer Grounds of Calling + constexpr uint16 INKTUTA = 296; // Inktu'Ta, the Unmasked Chapel + constexpr uint16 TXEVU = 297; // Txevu, Lair of the Elite + constexpr uint16 TACVI = 298; // Tacvi, The Broken Temple + constexpr uint16 QVICB = 299; // Qvic, the Hidden Vault + constexpr uint16 WALLOFSLAUGHTER = 300; // Wall of Slaughter + constexpr uint16 BLOODFIELDS = 301; // The Bloodfields + constexpr uint16 DRANIKSSCAR = 302; // Dranik's Scar + constexpr uint16 CAUSEWAY = 303; // Nobles' Causeway + constexpr uint16 CHAMBERSA = 304; // Muramite Proving Grounds + constexpr uint16 CHAMBERSB = 305; // Muramite Proving Grounds + constexpr uint16 CHAMBERSC = 306; // Muramite Proving Grounds + constexpr uint16 CHAMBERSD = 307; // Muramite Proving Grounds + constexpr uint16 CHAMBERSE = 308; // Muramite Proving Grounds + constexpr uint16 CHAMBERSF = 309; // Muramite Proving Grounds + constexpr uint16 PROVINGGROUNDS = 316; // Muramite Proving Grounds + constexpr uint16 ANGUISH = 317; // Anguish, the Fallen Palace + constexpr uint16 DRANIKHOLLOWSA = 318; // Dranik's Hollows + constexpr uint16 DRANIKHOLLOWSB = 319; // Dranik's Hollows + constexpr uint16 DRANIKHOLLOWSC = 320; // Dranik's Hollows + constexpr uint16 DRANIKCATACOMBSA = 328; // Catacombs of Dranik + constexpr uint16 DRANIKCATACOMBSB = 329; // Catacombs of Dranik + constexpr uint16 DRANIKCATACOMBSC = 330; // Catacombs of Dranik + constexpr uint16 DRANIKSEWERSA = 331; // Sewers of Dranik + constexpr uint16 DRANIKSEWERSB = 332; // Sewers of Dranik + constexpr uint16 DRANIKSEWERSC = 333; // Sewers of Dranik + constexpr uint16 RIFTSEEKERS = 334; // Riftseekers' Sanctum + constexpr uint16 HARBINGERS = 335; // Harbinger's Spire + constexpr uint16 DRANIK = 336; // The Ruined City of Dranik + constexpr uint16 BROODLANDS = 337; // The Broodlands + constexpr uint16 STILLMOONA = 338; // Stillmoon Temple + constexpr uint16 STILLMOONB = 339; // The Ascent + constexpr uint16 THUNDERCREST = 340; // Thundercrest Isles + constexpr uint16 DELVEA = 341; // Lavaspinner's Lair + constexpr uint16 DELVEB = 342; // Tirranun's Delve + constexpr uint16 THENEST = 343; // The Nest + constexpr uint16 GUILDLOBBY = 344; // Guild Lobby + constexpr uint16 GUILDHALL = 345; // Guild Hall + constexpr uint16 BARTER = 346; // The Barter Hall + constexpr uint16 ILLSALIN = 347; // Ruins of Illsalin + constexpr uint16 ILLSALINA = 348; // Illsalin Marketplace + constexpr uint16 ILLSALINB = 349; // Temple of Korlach + constexpr uint16 ILLSALINC = 350; // The Nargil Pits + constexpr uint16 DREADSPIRE = 351; // Dreadspire Keep + constexpr uint16 DRACHNIDHIVE = 354; // The Hive + constexpr uint16 DRACHNIDHIVEA = 355; // The Hatchery + constexpr uint16 DRACHNIDHIVEB = 356; // The Cocoons + constexpr uint16 DRACHNIDHIVEC = 357; // Queen Sendaii`s Lair + constexpr uint16 WESTKORLACH = 358; // Stoneroot Falls + constexpr uint16 WESTKORLACHA = 359; // Prince's Manor + constexpr uint16 WESTKORLACHB = 360; // Caverns of the Lost + constexpr uint16 WESTKORLACHC = 361; // Lair of the Korlach + constexpr uint16 EASTKORLACH = 362; // The Undershore + constexpr uint16 EASTKORLACHA = 363; // Snarlstone Dens + constexpr uint16 SHADOWSPINE = 364; // Shadow Spine + constexpr uint16 CORATHUS = 365; // Corathus Creep + constexpr uint16 CORATHUSA = 366; // Sporali Caverns + constexpr uint16 CORATHUSB = 367; // The Corathus Mines + constexpr uint16 NEKTULOSA = 368; // Shadowed Grove + constexpr uint16 ARCSTONE = 369; // Arcstone, Isle of Spirits + constexpr uint16 RELIC = 370; // Relic, the Artifact City + constexpr uint16 SKYLANCE = 371; // Skylance + constexpr uint16 DEVASTATION = 372; // The Devastation + constexpr uint16 DEVASTATIONA = 373; // The Seething Wall + constexpr uint16 RAGE = 374; // Sverag, Stronghold of Rage + constexpr uint16 RAGEA = 375; // Razorthorn, Tower of Sullon Zek + constexpr uint16 TAKISHRUINS = 376; // Ruins of Takish-Hiz + constexpr uint16 TAKISHRUINSA = 377; // The Root of Ro + constexpr uint16 ELDDAR = 378; // The Elddar Forest + constexpr uint16 ELDDARA = 379; // Tunare's Shrine + constexpr uint16 THEATER = 380; // Theater of Blood + constexpr uint16 THEATERA = 381; // Deathknell, Tower of Dissonance + constexpr uint16 FREEPORTEAST = 382; // East Freeport + constexpr uint16 FREEPORTWEST = 383; // West Freeport + constexpr uint16 FREEPORTSEWERS = 384; // Freeport Sewers + constexpr uint16 FREEPORTACADEMY = 385; // Academy of Arcane Sciences + constexpr uint16 FREEPORTTEMPLE = 386; // Temple of Marr + constexpr uint16 FREEPORTMILITIA = 387; // Freeport Militia House: My Precious + constexpr uint16 FREEPORTARENA = 388; // Arena + constexpr uint16 FREEPORTCITYHALL = 389; // City Hall + constexpr uint16 FREEPORTTHEATER = 390; // Theater of the Tranquil + constexpr uint16 FREEPORTHALL = 391; // Hall of Truth: Bounty + constexpr uint16 NORTHRO = 392; // North Desert of Ro + constexpr uint16 SOUTHRO = 393; // South Desert of Ro + constexpr uint16 CRESCENT = 394; // Crescent Reach + constexpr uint16 MOORS = 395; // Blightfire Moors + constexpr uint16 STONEHIVE = 396; // Stone Hive + constexpr uint16 MESA = 397; // Goru`kar Mesa + constexpr uint16 ROOST = 398; // Blackfeather Roost + constexpr uint16 STEPPES = 399; // The Steppes + constexpr uint16 ICEFALL = 400; // Icefall Glacier + constexpr uint16 VALDEHOLM = 401; // Valdeholm + constexpr uint16 FROSTCRYPT = 402; // Frostcrypt, Throne of the Shade King + constexpr uint16 SUNDEROCK = 403; // Sunderock Springs + constexpr uint16 VERGALID = 404; // Vergalid Mines + constexpr uint16 DIREWIND = 405; // Direwind Cliffs + constexpr uint16 ASHENGATE = 406; // Ashengate, Reliquary of the Scale + constexpr uint16 HIGHPASSHOLD = 407; // Highpass Hold + constexpr uint16 COMMONLANDS = 408; // The Commonlands + constexpr uint16 OCEANOFTEARS = 409; // The Ocean of Tears + constexpr uint16 KITHFOREST = 410; // Kithicor Forest + constexpr uint16 BEFALLENB = 411; // Befallen + constexpr uint16 HIGHPASSKEEP = 412; // HighKeep + constexpr uint16 INNOTHULEB = 413; // The Innothule Swamp + constexpr uint16 TOXXULIA = 414; // Toxxulia Forest + constexpr uint16 MISTYTHICKET = 415; // The Misty Thicket + constexpr uint16 KATTACASTRUM = 416; // Katta Castrum + constexpr uint16 THALASSIUS = 417; // Thalassius, the Coral Keep + constexpr uint16 ATIIKI = 418; // Jewel of Atiiki + constexpr uint16 ZHISZA = 419; // Zhisza, the Shissar Sanctuary + constexpr uint16 SILYSSAR = 420; // Silyssar, New Chelsith + constexpr uint16 SOLTERIS = 421; // Solteris, the Throne of Ro + constexpr uint16 BARREN = 422; // Barren Coast + constexpr uint16 BURIEDSEA = 423; // The Buried Sea + constexpr uint16 JARDELSHOOK = 424; // Jardel's Hook + constexpr uint16 MONKEYROCK = 425; // Monkey Rock + constexpr uint16 SUNCREST = 426; // Suncrest Isle + constexpr uint16 DEADBONE = 427; // Deadbone Reef + constexpr uint16 BLACKSAIL = 428; // Blacksail Folly + constexpr uint16 MAIDENSGRAVE = 429; // Maiden's Grave + constexpr uint16 REDFEATHER = 430; // Redfeather Isle + constexpr uint16 SHIPMVP = 431; // The Open Sea + constexpr uint16 SHIPMVU = 432; // The Open Sea + constexpr uint16 SHIPPVU = 433; // The Open Sea + constexpr uint16 SHIPUVU = 434; // The Open Sea + constexpr uint16 SHIPMVM = 435; // The Open Sea + constexpr uint16 MECHANOTUS = 436; // Fortress Mechanotus + constexpr uint16 MANSION = 437; // Meldrath's Majestic Mansion + constexpr uint16 STEAMFACTORY = 438; // The Steam Factory + constexpr uint16 SHIPWORKSHOP = 439; // S.H.I.P. Workshop + constexpr uint16 GYROSPIREB = 440; // Gyrospire Beza + constexpr uint16 GYROSPIREZ = 441; // Gyrospire Zeka + constexpr uint16 DRAGONSCALE = 442; // Dragonscale Hills + constexpr uint16 LOPINGPLAINS = 443; // Loping Plains + constexpr uint16 HILLSOFSHADE = 444; // Hills of Shade + constexpr uint16 BLOODMOON = 445; // Bloodmoon Keep + constexpr uint16 CRYSTALLOS = 446; // Crystallos, Lair of the Awakened + constexpr uint16 GUARDIAN = 447; // The Mechamatic Guardian + constexpr uint16 STEAMFONTMTS = 448; // The Steamfont Mountains + constexpr uint16 CRYPTOFSHADE = 449; // Crypt of Shade + constexpr uint16 DRAGONSCALEB = 451; // Deepscar's Den + constexpr uint16 OLDFIELDOFBONE = 452; // Field of Scale + constexpr uint16 OLDKAESORAA = 453; // Kaesora Library + constexpr uint16 OLDKAESORAB = 454; // Kaesora Hatchery + constexpr uint16 OLDKURN = 455; // Kurn's Tower + constexpr uint16 OLDKITHICOR = 456; // Bloody Kithicor + constexpr uint16 OLDCOMMONS = 457; // Old Commonlands + constexpr uint16 OLDHIGHPASS = 458; // Highpass Hold + constexpr uint16 THEVOIDA = 459; // The Void + constexpr uint16 THEVOIDB = 460; // The Void + constexpr uint16 THEVOIDC = 461; // The Void + constexpr uint16 THEVOIDD = 462; // The Void + constexpr uint16 THEVOIDE = 463; // The Void + constexpr uint16 THEVOIDF = 464; // The Void + constexpr uint16 THEVOIDG = 465; // The Void + constexpr uint16 OCEANGREENHILLS = 466; // Oceangreen Hills + constexpr uint16 OCEANGREENVILLAGE = 467; // Oceangreen Village + constexpr uint16 OLDBLACKBURROW = 468; // BlackBurrow + constexpr uint16 BERTOXTEMPLE = 469; // Temple of Bertoxxulous + constexpr uint16 DISCORD = 470; // Korafax, Home of the Riders + constexpr uint16 DISCORDTOWER = 471; // Citadel of the Worldslayer + constexpr uint16 OLDBLOODFIELD = 472; // Old Bloodfields + constexpr uint16 PRECIPICEOFWAR = 473; // The Precipice of War + constexpr uint16 OLDDRANIK = 474; // City of Dranik + constexpr uint16 TOSKIRAKK = 475; // Toskirakk + constexpr uint16 KORASCIAN = 476; // Korascian Warrens + constexpr uint16 RATHECHAMBER = 477; // Rathe Council Chamber + constexpr uint16 BRELLSREST = 480; // Brell's Rest + constexpr uint16 FUNGALFOREST = 481; // Fungal Forest + constexpr uint16 UNDERQUARRY = 482; // The Underquarry + constexpr uint16 COOLINGCHAMBER = 483; // The Cooling Chamber + constexpr uint16 SHININGCITY = 484; // Kernagir, the Shining City + constexpr uint16 ARTHICREX = 485; // Arthicrex + constexpr uint16 FOUNDATION = 486; // The Foundation + constexpr uint16 LICHENCREEP = 487; // Lichen Creep + constexpr uint16 PELLUCID = 488; // Pellucid Grotto + constexpr uint16 STONESNAKE = 489; // Volska's Husk + constexpr uint16 BRELLSTEMPLE = 490; // Brell's Temple + constexpr uint16 CONVORTEUM = 491; // The Convorteum + constexpr uint16 BRELLSARENA = 492; // Brell's Arena + constexpr uint16 WEDDINGCHAPEL = 493; // Wedding Chapel + constexpr uint16 WEDDINGCHAPELDARK = 494; // Wedding Chapel + constexpr uint16 DRAGONCRYPT = 495; // Lair of the Risen + constexpr uint16 FEERROTT2 = 700; // The Feerrott + constexpr uint16 THULEHOUSE1 = 701; // House of Thule + constexpr uint16 THULEHOUSE2 = 702; // House of Thule, Upper Floors + constexpr uint16 HOUSEGARDEN = 703; // The Grounds + constexpr uint16 THULELIBRARY = 704; // The Library + constexpr uint16 WELL = 705; // The Well + constexpr uint16 FALLEN = 706; // Erudin Burning + constexpr uint16 MORELLCASTLE = 707; // Morell's Castle + constexpr uint16 SOMNIUM = 708; // Sanctum Somnium + constexpr uint16 ALKABORMARE = 709; // Al'Kabor's Nightmare + constexpr uint16 MIRAGULMARE = 710; // Miragul's Nightmare + constexpr uint16 THULEDREAM = 711; // Fear Itself + constexpr uint16 NEIGHBORHOOD = 712; // Sunrise Hills + constexpr uint16 ARGATH = 724; // Argath, Bastion of Illdaera + constexpr uint16 ARELIS = 725; // Valley of Lunanyn + constexpr uint16 SARITHCITY = 726; // Sarith, City of Tides + constexpr uint16 RUBAK = 727; // Rubak Oseka, Temple of the Sea + constexpr uint16 BEASTDOMAIN = 728; // Beasts' Domain + constexpr uint16 RESPLENDENT = 729; // The Resplendent Temple + constexpr uint16 PILLARSALRA = 730; // Pillars of Alra + constexpr uint16 WINDSONG = 731; // Windsong Sanctuary + constexpr uint16 CITYOFBRONZE = 732; // Erillion, City of Bronze + constexpr uint16 SEPULCHER = 733; // Sepulcher of Order + constexpr uint16 EASTSEPULCHER = 734; // Sepulcher East + constexpr uint16 WESTSEPULCHER = 735; // Sepulcher West + constexpr uint16 SHARDSLANDING = 752; // Shard's Landing + constexpr uint16 XORBB = 753; // Valley of King Xorbb + constexpr uint16 KAELSHARD = 754; // Kael Drakkel: The King's Madness + constexpr uint16 EASTWASTESSHARD = 755; // East Wastes: Zeixshi-Kar's Awakening + constexpr uint16 CRYSTALSHARD = 756; // The Crystal Caverns: Fragment of Fear + constexpr uint16 BREEDINGGROUNDS = 757; // The Breeding Grounds + constexpr uint16 EVILTREE = 758; // Evantil, the Vile Oak + constexpr uint16 GRELLETH = 759; // Grelleth's Palace, the Chateau of Filth + constexpr uint16 CHAPTERHOUSE = 760; // Chapterhouse of the Fallen + constexpr uint16 ARTTEST = 996; // Art Testing Domain + constexpr uint16 FHALLS = 998; // The Forgotten Halls + constexpr uint16 APPRENTICE = 999; // Designer Apprentice +} + //ZoneChange_Struct->success values #define ZONE_ERROR_NOMSG 0 #define ZONE_ERROR_NOTREADY -1 From b68607a6ef731c1df1e36e1d44551231a6c328f9 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Thu, 8 Jul 2021 11:43:35 -0500 Subject: [PATCH 121/624] [Repository Generator] Add int64/bigint support, add support for reserved words (#1439) --- .../generators/repository-generator.pl | 111 ++++++++++++------ 1 file changed, 76 insertions(+), 35 deletions(-) diff --git a/utils/scripts/generators/repository-generator.pl b/utils/scripts/generators/repository-generator.pl index 99f8eb856..12012ea5a 100644 --- a/utils/scripts/generators/repository-generator.pl +++ b/utils/scripts/generators/repository-generator.pl @@ -130,7 +130,7 @@ foreach my $table_to_generate (@tables) { # These tables don't have a typical schema my @table_ignore_list = ( "character_enabledtasks", - "grid", # Manually created + "grid", # Manually created "grid_entries", # Manually created # "tradeskill_recipe", # Manually created # "character_recipe_list", # Manually created @@ -184,14 +184,15 @@ foreach my $table_to_generate (@tables) { $ex->execute($database_name, $table_to_generate); - my $longest_column_length = 0; - my $longest_data_type_length = 0; - while (my @row = $ex->fetchrow_array()) { - my $column_name = $row[0]; - my $data_type = $row[2]; + my $longest_column_length = 0; + my $longest_data_type_length = 0; + while (my @row = $ex->fetchrow_array()) { + my $column_name = $row[0]; + my $column_name_formatted = format_column_name_for_cpp_var($column_name); + my $data_type = $row[2]; - if ($longest_column_length < length($column_name)) { - $longest_column_length = length($column_name); + if ($longest_column_length < length($column_name_formatted)) { + $longest_column_length = length($column_name_formatted); } my $struct_data_type = translate_mysql_data_type_to_c($data_type); @@ -215,24 +216,30 @@ foreach my $table_to_generate (@tables) { my %table_primary_key = (); $ex->execute($database_name, $table_to_generate); - while (my @row = $ex->fetchrow_array()) { - my $column_name = $row[0]; - my $table_name = $row[1]; - my $data_type = $row[2]; - my $column_type = $row[3]; - my $ordinal_position = $row[4]; - my $column_key = $row[5]; - my $column_default = ($row[6] ? $row[6] : ""); - my $extra = ($row[7] ? $row[7] : ""); + while (my @row = $ex->fetchrow_array()) { + my $column_name = $row[0]; + my $column_name_formatted = format_column_name_for_cpp_var($column_name); + my $table_name = $row[1]; + my $data_type = $row[2]; + my $column_type = $row[3]; + my $ordinal_position = $row[4]; + my $column_key = $row[5]; + my $column_default = ($row[6] ? $row[6] : ""); + my $extra = ($row[7] ? $row[7] : ""); if (!$table_primary_key{$table_name}) { - if (($column_key eq "PRI" && $data_type =~/int/) || ($ordinal_position == 0 && $column_name =~ /id/i)) { + if (($column_key eq "PRI" && $data_type =~ /int/) || ($ordinal_position == 0 && $column_name =~ /id/i)) { $table_primary_key{$table_name} = $column_name; } } + print $column_default . "\n"; + my $default_value = 0; - if ($column_default ne "NULL" && $column_default ne "") { + if ($column_default eq "current_timestamp()") { + $default_value = '""'; + } + elsif ($column_default ne "NULL" && $column_default ne "") { $column_default =~ s/'/"/g; $default_value = $column_default; } @@ -246,19 +253,19 @@ foreach my $table_to_generate (@tables) { my $struct_data_type = translate_mysql_data_type_to_c($data_type); # struct - $table_struct_columns .= sprintf("\t\t\%-${longest_data_type_length}s %s;\n", $struct_data_type, $column_name); + $table_struct_columns .= sprintf("\t\t\%-${longest_data_type_length}s %s;\n", $struct_data_type, $column_name_formatted); # new entity - $default_entries .= sprintf("\t\tentry.%-${longest_column_length}s = %s;\n", $column_name, $default_value); + $default_entries .= sprintf("\t\tentry.%-${longest_column_length}s = %s;\n", $column_name_formatted, $default_value); # column names (string) - $column_names_quoted .= sprintf("\t\t\t\"%s\",\n", $column_name); + $column_names_quoted .= sprintf("\t\t\t\"%s\",\n", format_column_name_for_mysql($column_name)); # update one if ($extra ne "auto_increment") { - my $query_value = sprintf('\'" + EscapeString(%s_entry.%s) + "\'");', $table_name, $column_name); + my $query_value = sprintf('\'" + EscapeString(%s_entry.%s) + "\'");', $table_name, $column_name_formatted); if ($data_type =~ /int|float|double|decimal/) { - $query_value = sprintf('" + std::to_string(%s_entry.%s));', $table_name, $column_name); + $query_value = sprintf('" + std::to_string(%s_entry.%s));', $table_name, $column_name_formatted); } $update_one_entries .= sprintf( @@ -269,26 +276,30 @@ foreach my $table_to_generate (@tables) { } # insert - my $value = sprintf("\"'\" + EscapeString(%s_entry.%s) + \"'\"", $table_name, $column_name); + my $value = sprintf("\"'\" + EscapeString(%s_entry.%s) + \"'\"", $table_name, $column_name_formatted); if ($data_type =~ /int|float|double|decimal/) { - $value = sprintf('std::to_string(%s_entry.%s)', $table_name, $column_name); + $value = sprintf('std::to_string(%s_entry.%s)', $table_name, $column_name_formatted); } $insert_one_entries .= sprintf("\t\tinsert_values.push_back(%s);\n", $value); $insert_many_entries .= sprintf("\t\t\tinsert_values.push_back(%s);\n", $value); # find one / all (select) - if ($data_type =~ /int/) { - $all_entries .= sprintf("\t\t\tentry.%-${longest_column_length}s = atoi(row[%s]);\n", $column_name, $index); - $find_one_entries .= sprintf("\t\t\tentry.%-${longest_column_length}s = atoi(row[%s]);\n", $column_name, $index); + if ($data_type =~ /bigint/) { + $all_entries .= sprintf("\t\t\tentry.%-${longest_column_length}s = strtoll(row[%s], NULL, 10);\n", $column_name_formatted, $index); + $find_one_entries .= sprintf("\t\t\tentry.%-${longest_column_length}s = strtoll(row[%s], NULL, 10);\n", $column_name_formatted, $index); + } + elsif ($data_type =~ /int/) { + $all_entries .= sprintf("\t\t\tentry.%-${longest_column_length}s = atoi(row[%s]);\n", $column_name_formatted, $index); + $find_one_entries .= sprintf("\t\t\tentry.%-${longest_column_length}s = atoi(row[%s]);\n", $column_name_formatted, $index); } elsif ($data_type =~ /float|double|decimal/) { - $all_entries .= sprintf("\t\t\tentry.%-${longest_column_length}s = static_cast(atof(row[%s]));\n", $column_name, $index); - $find_one_entries .= sprintf("\t\t\tentry.%-${longest_column_length}s = static_cast(atof(row[%s]));\n", $column_name, $index); + $all_entries .= sprintf("\t\t\tentry.%-${longest_column_length}s = static_cast(atof(row[%s]));\n", $column_name_formatted, $index); + $find_one_entries .= sprintf("\t\t\tentry.%-${longest_column_length}s = static_cast(atof(row[%s]));\n", $column_name_formatted, $index); } else { - $all_entries .= sprintf("\t\t\tentry.%-${longest_column_length}s = row[%s] ? row[%s] : \"\";\n", $column_name, $index, $index); - $find_one_entries .= sprintf("\t\t\tentry.%-${longest_column_length}s = row[%s] ? row[%s] : \"\";\n", $column_name, $index, $index); + $all_entries .= sprintf("\t\t\tentry.%-${longest_column_length}s = row[%s] ? row[%s] : \"\";\n", $column_name_formatted, $index, $index); + $find_one_entries .= sprintf("\t\t\tentry.%-${longest_column_length}s = row[%s] ? row[%s] : \"\";\n", $column_name_formatted, $index, $index); } # print $column_name . "\n"; @@ -468,8 +479,7 @@ sub translate_mysql_data_type_to_c { $struct_data_type = 'int'; } elsif ($mysql_data_type =~ /bigint/) { - $struct_data_type = 'int'; - # Use regular int for now until we have 64 support + $struct_data_type = 'int64'; } elsif ($mysql_data_type =~ /int/) { $struct_data_type = 'int'; @@ -480,3 +490,34 @@ sub translate_mysql_data_type_to_c { return $struct_data_type; } + +# This is so we can change reserved words on the cpp side to something that will continue be functional in the compilers +sub get_reserved_cpp_variable_names { + return ( + "class", + "int" + ); +} + +sub format_column_name_for_cpp_var { + my $column_name = $_[0]; + + for my $word (get_reserved_cpp_variable_names()) { + if ($word eq $column_name) { + return $column_name . "_"; + } + } + + return $column_name; +} + +sub format_column_name_for_mysql { + my $column_name = $_[0]; + for my $word (get_reserved_cpp_variable_names()) { + if ($word eq $column_name) { + return "`" . $column_name . "`"; + } + } + + return $column_name; +} From a8e12c82a7c79f2afd19bd5d4e10c2c9beb44557 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Thu, 8 Jul 2021 11:44:02 -0500 Subject: [PATCH 122/624] [Repository Usage] Migrate NPC Scale Manager to use repositories (#1441) --- zone/npc_scale_manager.cpp | 264 ++++++++++++++----------------------- 1 file changed, 102 insertions(+), 162 deletions(-) diff --git a/zone/npc_scale_manager.cpp b/zone/npc_scale_manager.cpp index b810104ca..086abf75d 100644 --- a/zone/npc_scale_manager.cpp +++ b/zone/npc_scale_manager.cpp @@ -20,9 +20,11 @@ #include "npc_scale_manager.h" #include "../common/string_util.h" +#include "../common/repositories/npc_scale_global_base_repository.h" +#include "../common/repositories/npc_types_repository.h" /** - * @param npc + * @param npc */ void NpcScaleManager::ScaleNPC(NPC *npc) { @@ -164,9 +166,10 @@ void NpcScaleManager::ScaleNPC(NPC *npc) } } -void NpcScaleManager::ResetNPCScaling(NPC *npc) { +void NpcScaleManager::ResetNPCScaling(NPC *npc) +{ for (const auto &scaling_stat : scaling_stats) { - std::string stat_name = fmt::format("modify_stat_{}", scaling_stat); + std::string stat_name = fmt::format("modify_stat_{}", scaling_stat); std::string reset_value = "0"; if (npc->EntityVariableExists(stat_name.c_str())) { npc->ModifyNPCStat(scaling_stat.c_str(), reset_value.c_str()); @@ -176,72 +179,39 @@ void NpcScaleManager::ResetNPCScaling(NPC *npc) { bool NpcScaleManager::LoadScaleData() { - auto results = content_db.QueryDatabase( - "SELECT " - "type," - "level," - "ac," - "hp," - "accuracy," - "slow_mitigation," - "attack," - "strength," - "stamina," - "dexterity," - "agility," - "intelligence," - "wisdom," - "charisma," - "magic_resist," - "cold_resist," - "fire_resist," - "poison_resist," - "disease_resist," - "corruption_resist," - "physical_resist," - "min_dmg," - "max_dmg," - "hp_regen_rate," - "attack_delay," - "spell_scale," - "heal_scale," - "special_abilities" - " FROM `npc_scale_global_base`" - ); - - for (auto row = results.begin(); row != results.end(); ++row) { + for (auto &s: NpcScaleGlobalBaseRepository::All(content_db)) { global_npc_scale scale_data; - scale_data.type = atoi(row[0]); - scale_data.level = atoi(row[1]); - scale_data.ac = atoi(row[2]); - scale_data.hp = atoi(row[3]); - scale_data.accuracy = atoi(row[4]); - scale_data.slow_mitigation = atoi(row[5]); - scale_data.attack = atoi(row[6]); - scale_data.strength = atoi(row[7]); - scale_data.stamina = atoi(row[8]); - scale_data.dexterity = atoi(row[9]); - scale_data.agility = atoi(row[10]); - scale_data.intelligence = atoi(row[11]); - scale_data.wisdom = atoi(row[12]); - scale_data.charisma = atoi(row[13]); - scale_data.magic_resist = atoi(row[14]); - scale_data.cold_resist = atoi(row[15]); - scale_data.fire_resist = atoi(row[16]); - scale_data.poison_resist = atoi(row[17]); - scale_data.disease_resist = atoi(row[18]); - scale_data.corruption_resist = atoi(row[19]); - scale_data.physical_resist = atoi(row[20]); - scale_data.min_dmg = atoi(row[21]); - scale_data.max_dmg = atoi(row[22]); - scale_data.hp_regen_rate = atoi(row[23]); - scale_data.attack_delay = atoi(row[24]); - scale_data.spell_scale = atoi(row[25]); - scale_data.heal_scale = atoi(row[26]); + scale_data.type = s.type; + scale_data.level = s.level; + scale_data.ac = s.ac; + scale_data.hp = s.hp; + scale_data.accuracy = s.accuracy; + scale_data.slow_mitigation = s.slow_mitigation; + scale_data.attack = s.attack; + scale_data.strength = s.strength; + scale_data.stamina = s.stamina; + scale_data.dexterity = s.dexterity; + scale_data.agility = s.agility; + scale_data.intelligence = s.intelligence; + scale_data.wisdom = s.wisdom; + scale_data.charisma = s.charisma; + scale_data.magic_resist = s.magic_resist; + scale_data.cold_resist = s.cold_resist; + scale_data.fire_resist = s.fire_resist; + scale_data.poison_resist = s.poison_resist; + scale_data.disease_resist = s.disease_resist; + scale_data.corruption_resist = s.corruption_resist; + scale_data.physical_resist = s.physical_resist; + scale_data.min_dmg = s.min_dmg; + scale_data.max_dmg = s.max_dmg; + scale_data.hp_regen_rate = s.hp_regen_rate; + scale_data.attack_delay = s.attack_delay; + scale_data.spell_scale = s.spell_scale; + scale_data.heal_scale = s.heal_scale; - if (row[27]) { - scale_data.special_abilities = row[27]; + if (!s.special_abilities.empty()) { + scale_data.special_abilities = s.special_abilities; } npc_global_base_scaling_data.insert( @@ -489,9 +459,9 @@ bool NpcScaleManager::ApplyGlobalBaseScalingToNPCStatically(NPC *&npc) int8 npc_type = GetNPCScalingType(npc); int npc_level = npc->GetLevel(); - global_npc_scale scale_data = GetGlobalScaleDataForTypeLevel(npc_type, npc_level); + global_npc_scale g = GetGlobalScaleDataForTypeLevel(npc_type, npc_level); - if (!scale_data.level) { + if (!g.level) { LogNPCScaling( "NpcScaleManager::ApplyGlobalBaseScalingToNPCStatically NPC: [{}] - scaling data not found for type: [{}] level: [{}]", npc->GetCleanName(), @@ -502,67 +472,39 @@ bool NpcScaleManager::ApplyGlobalBaseScalingToNPCStatically(NPC *&npc) return false; } - std::string query = StringFormat( - "UPDATE `npc_types` SET " - "AC = %i, " - "hp = %i, " - "Accuracy = %i, " - "slow_mitigation = %i, " - "ATK = %i, " - "STR = %i, " - "STA = %i, " - "DEX = %i, " - "AGI = %i, " - "_INT = %i, " - "WIS = %i, " - "CHA = %i, " - "MR = %i, " - "CR = %i, " - "FR = %i, " - "PR = %i, " - "DR = %i, " - "Corrup = %i, " - "PhR = %i, " - "mindmg = %i, " - "maxdmg = %i, " - "hp_regen_rate = %i, " - "attack_delay = %i, " - "spellscale = %i, " - "healscale = %i, " - "special_abilities = '%s' " - "WHERE `id` = %i", - scale_data.ac, - scale_data.hp, - scale_data.accuracy, - scale_data.slow_mitigation, - scale_data.attack, - scale_data.strength, - scale_data.stamina, - scale_data.dexterity, - scale_data.agility, - scale_data.intelligence, - scale_data.wisdom, - scale_data.charisma, - scale_data.magic_resist, - scale_data.cold_resist, - scale_data.fire_resist, - scale_data.poison_resist, - scale_data.disease_resist, - scale_data.corruption_resist, - scale_data.physical_resist, - scale_data.min_dmg, - scale_data.max_dmg, - scale_data.hp_regen_rate, - scale_data.attack_delay, - scale_data.spell_scale, - scale_data.heal_scale, - EscapeString(scale_data.special_abilities).c_str(), - npc->GetNPCTypeID() - ); + auto n = NpcTypesRepository::FindOne(content_db, (int) npc->GetNPCTypeID()); + if (n.id > 0) { + n.AC = g.ac; + n.hp = g.hp; + n.Accuracy = g.accuracy; + n.slow_mitigation = g.slow_mitigation; + n.ATK = g.attack; + n.STR = g.strength; + n.STA = g.stamina; + n.DEX = g.dexterity; + n.AGI = g.agility; + n._INT = g.intelligence; + n.WIS = g.wisdom; + n.CHA = g.charisma; + n.MR = g.magic_resist; + n.CR = g.cold_resist; + n.FR = g.fire_resist; + n.PR = g.poison_resist; + n.DR = g.disease_resist; + n.Corrup = g.corruption_resist; + n.PhR = g.physical_resist; + n.mindmg = g.min_dmg; + n.maxdmg = g.max_dmg; + n.hp_regen_rate = g.hp_regen_rate; + n.attack_delay = g.attack_delay; + n.spellscale = (float) g.spell_scale; + n.healscale = (float) g.heal_scale; + n.special_abilities = g.special_abilities; - auto results = content_db.QueryDatabase(query); + return NpcTypesRepository::UpdateOne(content_db, n); + } - return results.Success(); + return false; } /** @@ -575,9 +517,9 @@ bool NpcScaleManager::ApplyGlobalBaseScalingToNPCDynamically(NPC *&npc) int8 npc_type = GetNPCScalingType(npc); int npc_level = npc->GetLevel(); - global_npc_scale scale_data = GetGlobalScaleDataForTypeLevel(npc_type, npc_level); + global_npc_scale d = GetGlobalScaleDataForTypeLevel(npc_type, npc_level); - if (!scale_data.level) { + if (!d.level) { LogNPCScaling( "NpcScaleManager::ApplyGlobalBaseScalingToNPCDynamically NPC: [{}] - scaling data not found for type: [{}] level: [{}]", npc->GetCleanName(), @@ -588,39 +530,37 @@ bool NpcScaleManager::ApplyGlobalBaseScalingToNPCDynamically(NPC *&npc) return false; } - std::string query = StringFormat( - "UPDATE `npc_types` SET " - "AC = 0, " - "hp = 0, " - "Accuracy = 0, " - "slow_mitigation = 0, " - "ATK = 0, " - "STR = 0, " - "STA = 0, " - "DEX = 0, " - "AGI = 0, " - "_INT = 0, " - "WIS = 0, " - "CHA = 0, " - "MR = 0, " - "CR = 0, " - "FR = 0, " - "PR = 0, " - "DR = 0, " - "Corrup = 0, " - "PhR = 0, " - "mindmg = 0, " - "maxdmg = 0, " - "hp_regen_rate = 0, " - "attack_delay = 0, " - "spellscale = 0, " - "healscale = 0, " - "special_abilities = '' " - "WHERE `id` = %i", - npc->GetNPCTypeID() - ); + auto n = NpcTypesRepository::FindOne(content_db, (int) npc->GetNPCTypeID()); + if (n.id > 0) { + n.AC = 0; + n.hp = 0; + n.Accuracy = 0; + n.slow_mitigation = 0; + n.ATK = 0; + n.STR = 0; + n.STA = 0; + n.DEX = 0; + n.AGI = 0; + n._INT = 0; + n.WIS = 0; + n.CHA = 0; + n.MR = 0; + n.CR = 0; + n.FR = 0; + n.PR = 0; + n.DR = 0; + n.Corrup = 0; + n.PhR = 0; + n.mindmg = 0; + n.maxdmg = 0; + n.hp_regen_rate = 0; + n.attack_delay = 0; + n.spellscale = 0; + n.healscale = 0; + n.special_abilities = ""; - auto results = content_db.QueryDatabase(query); + return NpcTypesRepository::UpdateOne(content_db, n); + } - return results.Success(); + return false; } From 8a2a1b152e3b85015c83498fdfc2aadb495f7375 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 14 Jul 2021 23:15:04 -0400 Subject: [PATCH 123/624] [Feature] New SPAs pass 1 (#1454) * Implemented SPA Duration Pct Implemented new spell effects SE_Duration_HP_Pct 524 SE_Duration_Mana_Pct 525 SE_Duration_Endurance_Pct 526 Consumes 'base1' % of your maximum health/mana/endurance every 6 seconds. 'max' is maximum amount that can be consumed per tic. Additional Functionality Can be used as a heal/gain % by setting the base1 value to a positive. * Implemented SPA Instant Mana/End pct Fixes for SPA 524-526 Implemented SE_Instant_Mana_Pct 522 SE_Instant_Endurance_Pct 523 Extracts 'base1' percent of your maximum mana/endurance, or 'max', whichever is lower. * Implemented: SPA 521 EndAbsorbPctDmg Implemented SE_Endurance_Absorb_Pct_Damage 521 Absorb Damage using Endurance: base1 % (base2 End per 1 HP) Note: Both base1 and base2 need to be divided by 100 for actually value * Implemented SE_HealthTransfer 509 Implemented SE_Health_Transfer 509 'life burn' Consume base2 % of Hit Points to Damage for base % of Hit Points Can be used for heal Act of Valor * Implemented SPA 515,516,518,496 Implemented SE_AC_Avoidance_Max_Percent 515 SE_AC_Mitigation_Max_Percent 516 SE_Attack_Accuracy_Max_Percent 518 Above are stackable defense and offensive mods SE_Critical_Melee_Damage_Mod_Max 496 - This is a non stackable melee critical modifier * Implemented SPA 503 , 505 SE_Melee_Damage_Position_Mod 503 define SE_Damage_Taken_Position_Mod 505 SPA 503 increase/decreases melee damage by percent base1 based on your position base2 0=back 1=front SPA 504 increase/decreases melee damage taken by percent base1 based on your position base2 0=back 1=front * Implemented 467,468 Implemented SE_DS_Mitigation_Amount 467 SE_DS_Mitigation_Percentage 468 Reduce incoming DS by amt or percentage. base1 is value, if a reduction is desired it should be set to negative for both. * Fixes Formula fixes * Update spdat.h Added spa descriptions. * Fixes for PR removed debug shouts fixed description issue --- common/spdat.h | 38 +++---- zone/attack.cpp | 50 ++++++++- zone/bonuses.cpp | 218 +++++++++++++++++++++++++++++++++++++- zone/common.h | 12 ++- zone/lua_stat_bonuses.cpp | 2 +- zone/mob.cpp | 89 +++++++++++++++- zone/mob.h | 5 +- zone/special_attacks.cpp | 4 + zone/spell_effects.cpp | 113 +++++++++++++++++++- zone/spells.cpp | 2 + 10 files changed, 498 insertions(+), 35 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 837f10f78..24aac5890 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -62,6 +62,8 @@ #define SPELL_SHAPECHANGE60 1924 #define SPELL_COMMAND_OF_DRUZZIL 3355 #define SPELL_SHAPECHANGE70 6503 +#define SPELL_MANA_BURN 2751 +#define SPELL_LIFE_BURN 2755 // these have known hardcoded behavior but we don't do anything yet, move them above this comment when fixed #define SPELL_THE_DAINS_JUSTICE 1476 #define SPELL_MODULATION 1502 @@ -152,7 +154,7 @@ #define SPELL_RESURRECTION_SICKNESS 756 #define SPELL_RESURRECTION_SICKNESS2 5249 #define SPELL_REVIVAL_SICKNESS 13087 -#define SPELL_MANA_BURN 2751 + #define EFFECT_COUNT 12 @@ -807,13 +809,13 @@ typedef enum { #define SE_PC_Pet_Rampage 464 // implemented - Base1 % chance to do rampage for base2 % of damage each melee round //#define SE_PC_Pet_AE_Rampage 465 // Would assume as above but need to confirm. #define SE_PC_Pet_Flurry_Chance 466 // implemented - Base1 % chance to do flurry from double attack hit. -//#define SE_DS_Mitigation_Amount 467 // -//#define SE_DS_Mitigation_Percentage 468 // +#define SE_DS_Mitigation_Amount 467 // implemented - Modify incoming damage shield damage by a flat amount +#define SE_DS_Mitigation_Percentage 468 // implemented - Modify incoming damage shield damage by percentage //#define SE_Chance_Best_in_Spell_Grp 469 // //#define SE_Trigger_Best_in_Spell Grp 470 // //#define SE_Double_Melee_Round 471 // //#define SE_Buy_AA_Rank 472 // -//#define SE_Double_Backstab_Front 473 // +#define SE_Double_Backstab_Front 473 // implemented - Chance to double backstab from front //#define SE_Pet_Crit_Melee_Damage_Pct_Owner 474 // //#define SE_Trigger_Spell_Non_Item 475 // //#define SE_Weapon_Stance 476 // @@ -836,37 +838,37 @@ typedef enum { //#define SE_Ff_Endurance_Max 493 // //#define SE_Pet_Add_Atk 494 // //#define SE_Ff_DurationMax 495 // -//#define SE_Critical_Melee_Damage_Mod_Max 496 // +#define SE_Critical_Melee_Damage_Mod_Max 496 // implemented - increase or decrease by percent critical damage (not stackable) //#define SE_Ff_FocusCastProcNoBypass 497 // //#define SE_AddExtraAttackPct_1h_Primary 498 // //#define SE_AddExtraAttackPct_1h_Secondary 499 // //#define SE_Fc_CastTimeMod2 500 // //#define SE_Fc_CastTimeAmt 501 // //#define SE_Fearstun 502 // -//#define SE_Melee_Damage_Position_Mod 503 // +#define SE_Melee_Damage_Position_Mod 503 // implemented - modify melee damage by pct if done from Front or Behind //#define SE_Melee_Damage_Position_Amt 504 // -//#define SE_Damage_Taken_Position_Mod 505 // +#define SE_Damage_Taken_Position_Mod 505 // implemented - mitigate melee damage by pct if dmg taken from Front or Behind //#define SE_Damage_Taken_Position_Amt 506 // //#define SE_Fc_Amplify_Mod 507 // //#define SE_Fc_Amplify_Amt 508 // -//#define SE_Health_Transfer 509 // +#define SE_Health_Transfer 509 // implemented - exchange health for damage or healing on a target. ie Lifeburn/Act of Valor //#define SE_Fc_ResistIncoming 510 // //#define SE_Ff_FocusTimerMin 511 // //#define SE_Proc_Timer_Modifier 512 // //#define SE_Mana_Max_Percent 513 // //#define SE_Endurance_Max_Percent 514 // -//#define SE_AC_Avoidance_Max_Percent 515 // -//#define SE_AC_Mitigation_Max_Percent 516 // -//#define SE_Attack_Offense_Max_Percent 517 // -//#define SE_Attack_Accuracy_Max_Percent 518 // +#define SE_AC_Avoidance_Max_Percent 515 // implemented - stackable avoidance modifier +#define SE_AC_Mitigation_Max_Percent 516 // implemented - stackable defense modifier +//#define SE_Attack_Offense_Max_Percent 517 // +#define SE_Attack_Accuracy_Max_Percent 518 // implemented - stackable accurary modifer //#define SE_Luck_Amount 519 // //#define SE_Luck_Percent 520 // -//#define SE_Endurance_Absorb_Pct_Damage 521 // -//#define SE_Instant_Mana_Pct 522 // -//#define SE_Instant_Endurance_Pct 523 // -//#define SE_Duration_HP_Pct 524 // -//#define SE_Duration_Mana_Pct 525 // -//#define SE_Duration_Endurance_Pct 526 // +#define SE_Endurance_Absorb_Pct_Damage 521 // implemented - Reduces % of Damage using Endurance, drains endurance at a ratio (ie. 0.05 Endurance per Hit Point) +#define SE_Instant_Mana_Pct 522 // implemented - Increase/Decrease mana by percent of max mana +#define SE_Instant_Endurance_Pct 523 // implemented - Increase/Decrease mana by percent of max endurance +#define SE_Duration_HP_Pct 524 // implemented - Decrease Current Hit Points by % of Total Hit Points per Tick, up to a MAX per tick +#define SE_Duration_Mana_Pct 525 // implemented - Decrease Current Mana by % of Total Mana per Tick, up to a MAX per tick +#define SE_Duration_Endurance_Pct 526 // implemented - Decrease Current Endurance by % of Total Hit Points per Tick, up to a MAX per tick // LAST diff --git a/zone/attack.cpp b/zone/attack.cpp index d7450c341..303d46fe5 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -188,6 +188,11 @@ int Mob::GetTotalToHit(EQ::skills::SkillType skill, int chance_mod) if (skill != EQ::skills::SkillArchery && skill != EQ::skills::SkillThrowing) accuracy += itembonuses.HitChance; + //518 Increase ATK accuracy by percentage, stackable + auto atkhit_bonus = itembonuses.Attack_Accuracy_Max_Percent + aabonuses.Attack_Accuracy_Max_Percent + spellbonuses.Attack_Accuracy_Max_Percent; + if (atkhit_bonus) + accuracy += round(static_cast(accuracy) * static_cast(atkhit_bonus) * 0.0001); + // 216 Melee Accuracy Amt aka SE_Accuracy -- flat bonus accuracy += itembonuses.Accuracy[EQ::skills::HIGHEST_SKILL + 1] + aabonuses.Accuracy[EQ::skills::HIGHEST_SKILL + 1] + @@ -233,6 +238,11 @@ int Mob::compute_defense() if (IsClient()) defense += CastToClient()->GetHeroicAGI() / 10; + //516 SE_AC_Mitigation_Max_Percent + auto ac_bonus = itembonuses.AC_Mitigation_Max_Percent + aabonuses.AC_Mitigation_Max_Percent + spellbonuses.AC_Mitigation_Max_Percent; + if (ac_bonus) + defense += round(static_cast(defense) * static_cast(ac_bonus) * 0.0001); + defense += itembonuses.AvoidMeleeChance; // item mod2 if (IsNPC()) defense += CastToNPC()->GetAvoidanceRating(); @@ -255,7 +265,12 @@ int Mob::GetTotalDefense() auto evasion_bonus = spellbonuses.AvoidMeleeChanceEffect; // we check this first since it has a special case if (evasion_bonus >= 10000) return -1; - // + + // 515 SE_AC_Avoidance_Max_Percent + auto ac_aviodance_bonus = itembonuses.AC_Avoidance_Max_Percent + aabonuses.AC_Avoidance_Max_Percent + spellbonuses.AC_Avoidance_Max_Percent; + if (ac_aviodance_bonus) + avoidance += round(static_cast(avoidance) * static_cast(ac_aviodance_bonus) * 0.0001); + // 172 Evasion aka SE_AvoidMeleeChance evasion_bonus += itembonuses.AvoidMeleeChanceEffect + aabonuses.AvoidMeleeChanceEffect; // item bonus here isn't mod2 avoidance @@ -2887,6 +2902,10 @@ void Mob::DamageShield(Mob* attacker, bool spell_ds) { DS += aabonuses.DamageShield; //Live AA - coat of thistles. (negative value) DS -= itembonuses.DamageShield; //+Damage Shield should only work when you already have a DS spell + DS -= attacker->aabonuses.DS_Mitigation_Amount + attacker->itembonuses.DS_Mitigation_Amount + attacker->spellbonuses.DS_Mitigation_Amount; //Negative value to reduce + //Do not allow flat amount reductions to reduce past 0. + if (DS >= 0) + return; //Spell data for damage shield mitigation shows a negative value for spells for clients and positive //value for spells that effect pets. Unclear as to why. For now will convert all positive to be consistent. @@ -2896,7 +2915,12 @@ void Mob::DamageShield(Mob* attacker, bool spell_ds) { attacker->aabonuses.DSMitigationOffHand; DS -= DS*mitigation / 100; } - DS -= DS * attacker->itembonuses.DSMitigation / 100; + + int ds_mitigation = attacker->itembonuses.DSMitigation; + // Subtract mitigations because DS_Mitigation_Percentage is a negative value when reducing total, thus final value will be positive + ds_mitigation -= attacker->aabonuses.DS_Mitigation_Percentage + attacker->itembonuses.DS_Mitigation_Percentage + attacker->spellbonuses.DS_Mitigation_Percentage; //Negative value to reduce + + DS -= DS * ds_mitigation / 100; } attacker->Damage(this, -DS, spellid, EQ::skills::SkillAbjuration/*hackish*/, false); //we can assume there is a spell now @@ -3323,8 +3347,8 @@ int32 Mob::ReduceAllDamage(int32 damage) if (damage <= 0) return damage; - if (spellbonuses.ManaAbsorbPercentDamage[0]) { - int32 mana_reduced = damage * spellbonuses.ManaAbsorbPercentDamage[0] / 100; + if (spellbonuses.ManaAbsorbPercentDamage) { + int32 mana_reduced = damage * spellbonuses.ManaAbsorbPercentDamage / 100; if (GetMana() >= mana_reduced) { damage -= mana_reduced; SetMana(GetMana() - mana_reduced); @@ -3332,6 +3356,19 @@ int32 Mob::ReduceAllDamage(int32 damage) } } + if (spellbonuses.EnduranceAbsorbPercentDamage[0]) { + int32 damage_reduced = damage * spellbonuses.EnduranceAbsorbPercentDamage[0] / 10000; //If hit for 1000, at 10% then lower damage by 100; + int32 endurance_drain = damage_reduced * spellbonuses.EnduranceAbsorbPercentDamage[1] / 10000; //Reduce endurance by 0.05% per HP loss + if (endurance_drain < 1) + endurance_drain = 1; + + if (IsClient() && CastToClient()->GetEndurance() >= endurance_drain) { + damage -= damage_reduced; + CastToClient()->SetEndurance(CastToClient()->GetEndurance() - endurance_drain); + TryTriggerOnValueAmount(false, false, true); + } + } + CheckNumHitsRemaining(NumHit::IncomingDamage); return(damage); @@ -4628,6 +4665,7 @@ void Mob::ApplyMeleeDamageMods(uint16 skill, int &damage, Mob *defender, ExtraAt int dmgbonusmod = 0; dmgbonusmod += GetMeleeDamageMod_SE(skill); + dmgbonusmod += GetMeleeDmgPositionMod(defender); if (opts) dmgbonusmod += opts->melee_damage_bonus_flat; @@ -5241,7 +5279,9 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac if (spec_mod > 0) hit.damage_done = (hit.damage_done * spec_mod) / 100; - hit.damage_done += (hit.damage_done * defender->GetSkillDmgTaken(hit.skill, opts) / 100) + (defender->GetFcDamageAmtIncoming(this, 0, true, hit.skill)); + int pct_damage_reduction = defender->GetSkillDmgTaken(hit.skill, opts) + defender->GetPositionalDmgTaken(this); + + hit.damage_done += (hit.damage_done * pct_damage_reduction / 100) + (defender->GetFcDamageAmtIncoming(this, 0, true, hit.skill)); CheckNumHitsRemaining(NumHit::OutgoingHitSuccess); } diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index e6c99f154..bc8075275 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -978,6 +978,9 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) case SE_FrontalBackstabChance: newbon->FrontalBackstabChance += base1; break; + case SE_Double_Backstab_Front: + newbon->Double_Backstab_Front += base1; + break; case SE_BlockBehind: newbon->BlockBehind += base1; break; @@ -1102,6 +1105,19 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) break; } + case SE_Critical_Melee_Damage_Mod_Max: + { + // Bad data or unsupported new skill + if (base2 > EQ::skills::HIGHEST_SKILL) + break; + int skill = base2 == ALL_SKILLS ? EQ::skills::HIGHEST_SKILL + 1 : base2; + if (base1 < 0 && newbon->CritDmgModNoStack[skill] > base1) + newbon->CritDmgModNoStack[skill] = base1; + else if (base1 > 0 && newbon->CritDmgModNoStack[skill] < base1) + newbon->CritDmgModNoStack[skill] = base1; + break; + } + case SE_CriticalSpellChance: { newbon->CriticalSpellChance += base1; @@ -1487,6 +1503,50 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) } break; + case SE_Attack_Accuracy_Max_Percent: + newbon->Attack_Accuracy_Max_Percent += base1; + break; + + case SE_AC_Mitigation_Max_Percent: + newbon->AC_Mitigation_Max_Percent += base1; + break; + + case SE_AC_Avoidance_Max_Percent: + newbon->AC_Avoidance_Max_Percent += base1; + break; + + case SE_Damage_Taken_Position_Mod: + { + //Mitigate if damage taken from behind base2 = 0, from front base2 = 1 + if (base2 < 0 || base2 > 2) + break; + else if (base1 < 0 && newbon->Damage_Taken_Position_Mod[base2] > base1) + newbon->Damage_Taken_Position_Mod[base2] = base1; + else if (base1 > 0 && newbon->Damage_Taken_Position_Mod[base2] < base1) + newbon->Damage_Taken_Position_Mod[base2] = base1; + break; + } + + case SE_Melee_Damage_Position_Mod: + { + if (base2 < 0 || base2 > 2) + break; + else if (base1 < 0 && newbon->Melee_Damage_Position_Mod[base2] > base1) + newbon->Melee_Damage_Position_Mod[base2] = base1; + else if (base1 > 0 && newbon->Melee_Damage_Position_Mod[base2] < base1) + newbon->Melee_Damage_Position_Mod[base2] = base1; + break; + } + + case SE_DS_Mitigation_Amount: + newbon->DS_Mitigation_Amount += base1; + break; + + case SE_DS_Mitigation_Percentage: + newbon->DS_Mitigation_Percentage += base1; + break; + + // to do case SE_PetDiscipline: break; @@ -2485,6 +2545,20 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne break; } + case SE_Critical_Melee_Damage_Mod_Max: + { + // Bad data or unsupported new skill + if (base2 > EQ::skills::HIGHEST_SKILL) + break; + int skill = base2 == ALL_SKILLS ? EQ::skills::HIGHEST_SKILL + 1 : base2; + if (effect_value < 0 && new_bonus->CritDmgModNoStack[skill] > effect_value) + new_bonus->CritDmgModNoStack[skill] = effect_value; + else if (effect_value > 0 && new_bonus->CritDmgModNoStack[skill] < effect_value) { + new_bonus->CritDmgModNoStack[skill] = effect_value; + } + break; + } + case SE_ReduceSkillTimer: { if(new_bonus->SkillReuseTime[base2] < effect_value) @@ -2690,9 +2764,17 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_ManaAbsorbPercentDamage: { - if (new_bonus->ManaAbsorbPercentDamage[0] < effect_value){ - new_bonus->ManaAbsorbPercentDamage[0] = effect_value; - new_bonus->ManaAbsorbPercentDamage[1] = buffslot; + if (new_bonus->ManaAbsorbPercentDamage < effect_value){ + new_bonus->ManaAbsorbPercentDamage = effect_value; + } + break; + } + + case SE_Endurance_Absorb_Pct_Damage: + { + if (new_bonus->EnduranceAbsorbPercentDamage[0] < effect_value) { + new_bonus->EnduranceAbsorbPercentDamage[0] = effect_value; + new_bonus->EnduranceAbsorbPercentDamage[1] = base2; } break; } @@ -2762,6 +2844,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne new_bonus->FrontalBackstabChance += effect_value; break; + case SE_Double_Backstab_Front: + new_bonus->Double_Backstab_Front += effect_value; + break; + case SE_ConsumeProjectile: new_bonus->ConsumeProjectile += effect_value; break; @@ -3250,6 +3336,57 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne if (new_bonus->trap_slots < effect_value) new_bonus->trap_slots = effect_value; break; + + case SE_Attack_Accuracy_Max_Percent: + new_bonus->Attack_Accuracy_Max_Percent += effect_value; + break; + + + case SE_AC_Mitigation_Max_Percent: + new_bonus->AC_Mitigation_Max_Percent += effect_value; + break; + + case SE_AC_Avoidance_Max_Percent: + new_bonus->AC_Avoidance_Max_Percent += effect_value; + break; + + case SE_Damage_Taken_Position_Mod: + { + //Mitigate if damage taken from behind base2 = 0, from front base2 = 1 + if (base2 < 0 || base2 > 2) + break; + if (AdditiveWornBonus) + new_bonus->Damage_Taken_Position_Mod[base2] += effect_value; + else if (effect_value < 0 && new_bonus->Damage_Taken_Position_Mod[base2] > effect_value) + new_bonus->Damage_Taken_Position_Mod[base2] = effect_value; + else if (effect_value > 0 && new_bonus->Damage_Taken_Position_Mod[base2] < effect_value) + new_bonus->Damage_Taken_Position_Mod[base2] = effect_value; + break; + } + + case SE_Melee_Damage_Position_Mod: + { + //Increase damage by percent from behind base2 = 0, from front base2 = 1 + if (base2 < 0 || base2 > 2) + break; + if (AdditiveWornBonus) + new_bonus->Melee_Damage_Position_Mod[base2] += effect_value; + else if (effect_value < 0 && new_bonus->Melee_Damage_Position_Mod[base2] > effect_value) + new_bonus->Melee_Damage_Position_Mod[base2] = effect_value; + else if (effect_value > 0 && new_bonus->Melee_Damage_Position_Mod[base2] < effect_value) + new_bonus->Melee_Damage_Position_Mod[base2] = effect_value; + break; + } + + case SE_DS_Mitigation_Amount: + new_bonus->DS_Mitigation_Amount += effect_value; + break; + + case SE_DS_Mitigation_Percentage: + new_bonus->DS_Mitigation_Percentage += effect_value; + break; + + //Special custom cases for loading effects on to NPC from 'npc_spels_effects' table if (IsAISpellEffect) { @@ -4258,6 +4395,17 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) break; } + case SE_Critical_Melee_Damage_Mod_Max: + { + for (int e = 0; e < EQ::skills::HIGHEST_SKILL + 1; e++) + { + spellbonuses.CritDmgModNoStack[e] = effect_value; + aabonuses.CritDmgModNoStack[e] = effect_value; + itembonuses.CritDmgModNoStack[e] = effect_value; + } + break; + } + case SE_SkillDamageAmount: { for (int e = 0; e < EQ::skills::HIGHEST_SKILL + 1; e++) @@ -4353,8 +4501,12 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) break; case SE_ManaAbsorbPercentDamage: - spellbonuses.ManaAbsorbPercentDamage[0] = effect_value; - spellbonuses.ManaAbsorbPercentDamage[1] = -1; + spellbonuses.ManaAbsorbPercentDamage = effect_value; + break; + + case SE_Endurance_Absorb_Pct_Damage: + spellbonuses.EnduranceAbsorbPercentDamage[0] = effect_value; + spellbonuses.EnduranceAbsorbPercentDamage[1] = effect_value; break; case SE_ShieldBlock: @@ -4428,6 +4580,12 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) itembonuses.FrontalBackstabChance = effect_value; break; + case SE_Double_Backstab_Front: + spellbonuses.Double_Backstab_Front = effect_value; + aabonuses.Double_Backstab_Front = effect_value; + itembonuses.Double_Backstab_Front = effect_value; + break; + case SE_ConsumeProjectile: spellbonuses.ConsumeProjectile = effect_value; aabonuses.ConsumeProjectile = effect_value; @@ -4770,6 +4928,56 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) aabonuses.IllusionPersistence = false; break; + case SE_Attack_Accuracy_Max_Percent: + spellbonuses.Attack_Accuracy_Max_Percent = effect_value; + itembonuses.Attack_Accuracy_Max_Percent = effect_value; + aabonuses.Attack_Accuracy_Max_Percent = effect_value; + break; + + + case SE_AC_Mitigation_Max_Percent: + spellbonuses.AC_Mitigation_Max_Percent = effect_value; + itembonuses.AC_Mitigation_Max_Percent = effect_value; + aabonuses.AC_Mitigation_Max_Percent = effect_value; + break; + + case SE_AC_Avoidance_Max_Percent: + spellbonuses.AC_Avoidance_Max_Percent = effect_value; + itembonuses.AC_Avoidance_Max_Percent = effect_value; + aabonuses.AC_Avoidance_Max_Percent = effect_value; + break; + + case SE_Melee_Damage_Position_Mod: + spellbonuses.Melee_Damage_Position_Mod[0] = effect_value; + aabonuses.Melee_Damage_Position_Mod[0] = effect_value; + itembonuses.Melee_Damage_Position_Mod[0] = effect_value; + spellbonuses.Melee_Damage_Position_Mod[1] = effect_value; + aabonuses.Melee_Damage_Position_Mod[1] = effect_value; + itembonuses.Melee_Damage_Position_Mod[1] = effect_value; + break; + + case SE_Damage_Taken_Position_Mod: + spellbonuses.Damage_Taken_Position_Mod[0] = effect_value; + aabonuses.Damage_Taken_Position_Mod[0] = effect_value; + itembonuses.Damage_Taken_Position_Mod[0] = effect_value; + spellbonuses.Damage_Taken_Position_Mod[1] = effect_value; + aabonuses.Damage_Taken_Position_Mod[1] = effect_value; + itembonuses.Damage_Taken_Position_Mod[1] = effect_value; + break; + + + case SE_DS_Mitigation_Amount: + spellbonuses.DS_Mitigation_Amount = effect_value; + itembonuses.DS_Mitigation_Amount = effect_value; + aabonuses.DS_Mitigation_Amount = effect_value; + break; + + case SE_DS_Mitigation_Percentage: + spellbonuses.DS_Mitigation_Percentage = effect_value; + itembonuses.DS_Mitigation_Percentage = effect_value; + aabonuses.DS_Mitigation_Percentage = effect_value; + break; + case SE_SkillProcSuccess:{ for(int e = 0; e < MAX_SKILL_PROCS; e++) { diff --git a/zone/common.h b/zone/common.h index fd513bdde..01c074b11 100644 --- a/zone/common.h +++ b/zone/common.h @@ -461,6 +461,7 @@ struct StatBonuses { uint32 SpellOnKill[MAX_SPELL_TRIGGER*3]; // Chance to proc after killing a mob uint32 SpellOnDeath[MAX_SPELL_TRIGGER*2]; // Chance to have effect cast when you die int32 CritDmgMod[EQ::skills::HIGHEST_SKILL + 2]; // All Skills + -1 + int32 CritDmgModNoStack[EQ::skills::HIGHEST_SKILL + 2];// Critical melee damage modifier by percent, does not stack. int32 SkillReuseTime[EQ::skills::HIGHEST_SKILL + 1]; // Reduces skill timers int32 SkillDamageAmount[EQ::skills::HIGHEST_SKILL + 2]; // All Skills + -1 int32 TwoHandBluntBlock; // chance to block when wielding two hand blunt weapon @@ -494,7 +495,8 @@ struct StatBonuses { uint32 MitigateDotRune[4]; // 0 = Mitigation value 1 = Buff Slot 2 = Max mitigation per tick 3 = Rune Amt bool TriggerMeleeThreshold; // Has Melee Threshhold bool TriggerSpellThreshold; // Has Spell Threshhold - uint32 ManaAbsorbPercentDamage[2]; // 0 = Mitigation value 1 = Buff Slot + uint32 ManaAbsorbPercentDamage; // 0 = Mitigation value + int32 EnduranceAbsorbPercentDamage[2]; // 0 = Mitigation value 1 = Percent Endurance drain per HP lost int32 ShieldBlock; // Chance to Shield Block int32 BlockBehind; // Chance to Block Behind (with our without shield) bool CriticalRegenDecay; // increase critical regen chance, decays based on spell level cast @@ -523,6 +525,14 @@ struct StatBonuses { uint32 SkillProcSuccess[MAX_SKILL_PROCS]; // Max number of spells containing skill_procs_success. uint32 PC_Pet_Rampage[2]; // 0= % chance to rampage, 1=damage modifier uint32 PC_Pet_Flurry; // Percent chance flurry from double attack + int32 Attack_Accuracy_Max_Percent; // Increase ATK accuracy by percent. + int32 AC_Mitigation_Max_Percent; // Increase AC mitigation by percent + int32 AC_Avoidance_Max_Percent; // Increase AC avoidance by percent + int32 Damage_Taken_Position_Mod[2]; // base = percent melee damage reduction base2 0=back 1=front. [0]Back[1]Front + int32 Melee_Damage_Position_Mod[2]; // base = percent melee damage increase base2 0=back 1=front. [0]Back[1]Front + int32 Double_Backstab_Front; // base = percent chance to double back stab front + int32 DS_Mitigation_Amount; // base = flat amt DS mitigation. Negative value to reduce + int32 DS_Mitigation_Percentage; // base = percent amt of DS mitigation. Negative value to reduce // AAs int8 Packrat; //weight reduction for items, 1 point = 10% diff --git a/zone/lua_stat_bonuses.cpp b/zone/lua_stat_bonuses.cpp index d311cf123..b6561199b 100644 --- a/zone/lua_stat_bonuses.cpp +++ b/zone/lua_stat_bonuses.cpp @@ -1162,7 +1162,7 @@ uint32 Lua_StatBonuses::GetMitigateDotRune(int idx) const { uint32 Lua_StatBonuses::GetManaAbsorbPercentDamage(int idx) const { Lua_Safe_Call_Int(); - return self->ManaAbsorbPercentDamage[idx]; + return self->ManaAbsorbPercentDamage; } int32 Lua_StatBonuses::GetImprovedTaunt(int idx) const { diff --git a/zone/mob.cpp b/zone/mob.cpp index dd1ad6475..46d9447f9 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3711,6 +3711,32 @@ void Mob::TryTwincast(Mob *caster, Mob *target, uint32 spell_id) } } +//Used for effects that should occur after the completion of the spell +void Mob::TryOnSpellFinished(Mob *caster, Mob *target, uint16 spell_id) +{ + if (!IsValidSpell(spell_id)) + return; + + /*Apply damage from Lifeburn type effects on caster at end of spell cast. + This allows for the AE spells to function without repeatedly killing caster + Damage or heal portion can be found as regular single use spell effect + */ + if (IsEffectInSpell(spell_id, SE_Health_Transfer)){ + for (int i = 0; i < EFFECT_COUNT; i++) { + + if (spells[spell_id].effectid[i] == SE_Health_Transfer) { + int new_hp = GetMaxHP(); + new_hp -= GetMaxHP() * spells[spell_id].base[i] / 1000; + + if (new_hp > 0) + SetHP(new_hp); + else + Kill(); + } + } + } +} + int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining) { if (!IsValidSpell(spell_id)) @@ -3771,7 +3797,7 @@ int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining) return value; } -int16 Mob::GetSkillDmgTaken(const EQ::skills::SkillType skill_used, ExtraAttackOptions *opts) +int32 Mob::GetSkillDmgTaken(const EQ::skills::SkillType skill_used, ExtraAttackOptions *opts) { int skilldmg_mod = 0; @@ -3790,6 +3816,33 @@ int16 Mob::GetSkillDmgTaken(const EQ::skills::SkillType skill_used, ExtraAttackO return skilldmg_mod; } +int32 Mob::GetPositionalDmgTaken(Mob *attacker) +{ + if (!attacker) + return 0; + + int front_arc = 0; + int back_arc = 0; + int total_mod = 0; + + back_arc += itembonuses.Damage_Taken_Position_Mod[0] + aabonuses.Damage_Taken_Position_Mod[0] + spellbonuses.Damage_Taken_Position_Mod[0]; + front_arc += itembonuses.Damage_Taken_Position_Mod[1] + aabonuses.Damage_Taken_Position_Mod[1] + spellbonuses.Damage_Taken_Position_Mod[1]; + + if (back_arc || front_arc) { //Do they have this bonus? + if (attacker->BehindMob(this, attacker->GetX(), attacker->GetY()))//Check if attacker is striking from behind + total_mod = back_arc; //If so, apply the back arc modifier only + else + total_mod = front_arc;//If not, apply the front arc modifer only + } + + total_mod = round(static_cast(total_mod) * 0.1); + + if (total_mod < -100) + total_mod = -100; + + return total_mod; +} + int16 Mob::GetHealRate(uint16 spell_id, Mob* caster) { int16 heal_rate = 0; @@ -4598,10 +4651,13 @@ int16 Mob::GetCritDmgMod(uint16 skill) { int critDmg_mod = 0; - // All skill dmg mod + Skill specific + // All skill dmg mod + Skill specific [SPA 330 and 496] critDmg_mod += itembonuses.CritDmgMod[EQ::skills::HIGHEST_SKILL + 1] + spellbonuses.CritDmgMod[EQ::skills::HIGHEST_SKILL + 1] + aabonuses.CritDmgMod[EQ::skills::HIGHEST_SKILL + 1] + itembonuses.CritDmgMod[skill] + spellbonuses.CritDmgMod[skill] + aabonuses.CritDmgMod[skill]; + critDmg_mod += itembonuses.CritDmgModNoStack[EQ::skills::HIGHEST_SKILL + 1] + spellbonuses.CritDmgModNoStack[EQ::skills::HIGHEST_SKILL + 1] + aabonuses.CritDmgModNoStack[EQ::skills::HIGHEST_SKILL + 1] + + itembonuses.CritDmgModNoStack[skill] + spellbonuses.CritDmgModNoStack[skill] + aabonuses.CritDmgModNoStack[skill]; + return critDmg_mod; } @@ -4692,6 +4748,35 @@ int16 Mob::GetCrippBlowChance() return crip_chance; } + +int16 Mob::GetMeleeDmgPositionMod(Mob* defender) +{ + if (!defender) + return 0; + + int front_arc = 0; + int back_arc = 0; + int total_mod = 0; + + back_arc += itembonuses.Melee_Damage_Position_Mod[0] + aabonuses.Melee_Damage_Position_Mod[0] + spellbonuses.Melee_Damage_Position_Mod[0]; + front_arc += itembonuses.Melee_Damage_Position_Mod[1] + aabonuses.Melee_Damage_Position_Mod[1] + spellbonuses.Melee_Damage_Position_Mod[1]; + + if (back_arc || front_arc) { //Do they have this bonus? + if (BehindMob(defender, GetX(), GetY()))//Check if attacker is striking from behind + total_mod = back_arc; //If so, apply the back arc modifier only + else + total_mod = front_arc;//If not, apply the front arc modifer only + } + + total_mod = round(static_cast(total_mod) * 0.1); + + if (total_mod < -100) + total_mod = -100; + + return total_mod; + +} + int16 Mob::GetSkillReuseTime(uint16 skill) { int skill_reduction = this->itembonuses.SkillReuseTime[skill] + this->spellbonuses.SkillReuseTime[skill] + this->aabonuses.SkillReuseTime[skill]; diff --git a/zone/mob.h b/zone/mob.h index ea5723fa6..c0ce7bf5a 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -353,6 +353,7 @@ public: void CalcDestFromHeading(float heading, float distance, float MaxZDiff, float StartX, float StartY, float &dX, float &dY, float &dZ); void BeamDirectional(uint16 spell_id, int16 resist_adjust); void ConeDirectional(uint16 spell_id, int16 resist_adjust); + void TryOnSpellFinished(Mob *caster, Mob *target, uint16 spell_id); //Buff void BuffProcess(); @@ -802,7 +803,8 @@ public: int32 GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining); int32 GetFcDamageAmtIncoming(Mob *caster, uint32 spell_id, bool use_skill = false, uint16 skill=0); int32 GetFocusIncoming(focusType type, int effect, Mob *caster, uint32 spell_id); - int16 GetSkillDmgTaken(const EQ::skills::SkillType skill_used, ExtraAttackOptions *opts = nullptr); + int32 GetSkillDmgTaken(const EQ::skills::SkillType skill_used, ExtraAttackOptions *opts = nullptr); + int32 GetPositionalDmgTaken(Mob *attacker); void DoKnockback(Mob *caster, uint32 pushback, uint32 pushup); int16 CalcResistChanceBonus(); int16 CalcFearResistChance(); @@ -816,6 +818,7 @@ public: int16 GetMeleeDamageMod_SE(uint16 skill); int16 GetMeleeMinDamageMod_SE(uint16 skill); int16 GetCrippBlowChance(); + int16 GetMeleeDmgPositionMod(Mob* defender); int16 GetSkillReuseTime(uint16 skill); int GetCriticalChanceBonus(uint16 skill); int16 GetSkillDmgAmt(uint16 skill); diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 717bc4fbf..be3408c81 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -585,6 +585,10 @@ void Mob::TryBackstab(Mob *other, int ReuseTime) { if(IsClient()) CastToClient()->CheckIncreaseSkill(EQ::skills::SkillBackstab, other, 10); m_specialattacks = eSpecialAttacks::None; + + int double_bs_front = aabonuses.Double_Backstab_Front + itembonuses.Double_Backstab_Front + spellbonuses.Double_Backstab_Front; + if (double_bs_front && other->GetHP() > 0 && zone->random.Roll(double_bs_front)) + RogueBackstab(other, false, ReuseTime); } else { //We do a single regular attack if we attack from the front without chaotic stab Attack(other, EQ::invslot::slotPrimary); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 4fdb926b4..801700bd5 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -275,11 +275,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #endif int32 dmg = effect_value; - if (spell_id == 2751 && caster) //Manaburn + if (spell_id == SPELL_MANA_BURN && caster) //Manaburn { dmg = caster->GetMana()*-3; caster->SetMana(0); - } else if (spell_id == 2755 && caster) //Lifeburn + } else if (spell_id == SPELL_LIFE_BURN && caster) //Lifeburn { dmg = caster->GetHP(); // just your current HP caster->SetHP(dmg / 4); // 2003 patch notes say ~ 1/4 HP. Should this be 1/4 your current HP or do 3/4 max HP dmg? Can it kill you? @@ -303,6 +303,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove break; } + case SE_PercentalHeal: { #ifdef SPELL_EFFECT_SPAM @@ -2828,6 +2829,52 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove break; } + + case SE_Instant_Mana_Pct: { + effect_value = spells[spell_id].base[i]; + int32 amt = abs(GetMaxMana() * effect_value / 10000); + if (spells[spell_id].max[i] && amt > spells[spell_id].max[i]) + amt = spells[spell_id].max[i]; + + if (effect_value < 0) { + SetMana(GetMana() - amt); + } + else { + SetMana(GetMana() + amt); + } + break; + } + + case SE_Instant_Endurance_Pct: { + effect_value = spells[spell_id].base[i]; + if (IsClient()) { + int32 amt = abs(CastToClient()->GetMaxEndurance() * effect_value / 10000); + if (spells[spell_id].max[i] && amt > spells[spell_id].max[i]) + amt = spells[spell_id].max[i]; + + if (effect_value < 0) { + CastToClient()->SetEndurance(CastToClient()->GetEndurance() - amt); + } + else { + CastToClient()->SetEndurance(CastToClient()->GetEndurance() + amt); + } + } + break; + } + /*Calc for base1 is found in TryOnSpellFinished() due to needing to account for AOE functionality + since effect can potentially kill caster*/ + case SE_Health_Transfer: { + effect_value = spells[spell_id].base2[i]; + int32 amt = abs(caster->GetMaxHP() * effect_value / 1000); + + if (effect_value < 0) + Damage(caster, amt, spell_id, spell.skill, false, buffslot, false); + else + HealDamage(amt, caster); + break; + } + + case SE_PersistentEffect: MakeAura(spell_id); break; @@ -3071,6 +3118,19 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_SkillProc: case SE_SkillProcSuccess: case SE_SpellResistReduction: + case SE_Duration_HP_Pct: + case SE_Duration_Mana_Pct: + case SE_Duration_Endurance_Pct: + case SE_Endurance_Absorb_Pct_Damage: + case SE_AC_Mitigation_Max_Percent: + case SE_AC_Avoidance_Max_Percent: + case SE_Attack_Accuracy_Max_Percent: + case SE_Critical_Melee_Damage_Mod_Max: + case SE_Melee_Damage_Position_Mod: + case SE_Damage_Taken_Position_Mod: + case SE_DS_Mitigation_Amount: + case SE_DS_Mitigation_Percentage: + case SE_Double_Backstab_Front: { break; } @@ -3777,6 +3837,55 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) break; } + case SE_Duration_HP_Pct: { + effect_value = spells[buff.spellid].base[i]; + int32 amt = abs(GetMaxHP() * effect_value / 100); + if (spells[buff.spellid].max[i] && amt > spells[buff.spellid].max[i]) + amt = spells[buff.spellid].max[i]; + + if (effect_value < 0) { + Damage(this, amt, 0, EQ::skills::SkillEvocation, false); + } + else { + HealDamage(amt); + } + break; + } + + case SE_Duration_Mana_Pct: { + effect_value = spells[buff.spellid].base[i]; + int32 amt = abs(GetMaxMana() * effect_value / 100); + if (spells[buff.spellid].max[i] && amt > spells[buff.spellid].max[i]) + amt = spells[buff.spellid].max[i]; + + if (effect_value < 0) { + + SetMana(GetMana() - amt); + } + else { + SetMana(GetMana() + amt); + } + break; + } + + case SE_Duration_Endurance_Pct: { + effect_value = spells[buff.spellid].base[i]; + + if (IsClient()) { + int32 amt = abs(CastToClient()->GetMaxEndurance() * effect_value / 100); + if (spells[buff.spellid].max[i] && amt > spells[buff.spellid].max[i]) + amt = spells[buff.spellid].max[i]; + + if (effect_value < 0) { + CastToClient()->SetEndurance(CastToClient()->GetEndurance() - amt); + } + else { + CastToClient()->SetEndurance(CastToClient()->GetEndurance() + amt); + } + } + break; + } + default: { // do we need to do anyting here? } diff --git a/zone/spells.cpp b/zone/spells.cpp index c7029b73d..5e0db7c8a 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1393,6 +1393,8 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo TrySympatheticProc(target, spell_id); } + TryOnSpellFinished(this, target, spell_id); + TryTwincast(this, target, spell_id); TryTriggerOnCast(spell_id, 0); From 7decf74505aa965060ff11124bc460ebb498a36b Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Thu, 15 Jul 2021 19:00:50 -0500 Subject: [PATCH 124/624] Add Rank to lua Spell --- zone/lua_spell.cpp | 8 +++++++- zone/lua_spell.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/zone/lua_spell.cpp b/zone/lua_spell.cpp index 18353efaa..c7730efff 100644 --- a/zone/lua_spell.cpp +++ b/zone/lua_spell.cpp @@ -474,6 +474,11 @@ int Lua_Spell::GetDamageShieldType() { return self->DamageShieldType; } +int Lua_Spell::GetRank() { + Lua_Safe_Call_Int(); + return self->rank; +} + luabind::scope lua_register_spell() { return luabind::class_("Spell") .def(luabind::constructor<>()) @@ -561,7 +566,8 @@ luabind::scope lua_register_spell() { .def("MaxDist", &Lua_Spell::GetMaxDist) .def("MaxDistMod", &Lua_Spell::GetMaxDistMod) .def("MinRange", &Lua_Spell::GetMinRange) - .def("DamageShieldType", &Lua_Spell::GetDamageShieldType); + .def("DamageShieldType", &Lua_Spell::GetDamageShieldType) + .def("Rank", &Lua_Spell::GetRank); } #endif diff --git a/zone/lua_spell.h b/zone/lua_spell.h index bfa5a19ed..e2def8a6a 100644 --- a/zone/lua_spell.h +++ b/zone/lua_spell.h @@ -107,6 +107,7 @@ public: float GetMaxDistMod(); float GetMinRange(); int GetDamageShieldType(); + int GetRank(); }; #endif From ddb14187b009a242a2f5706a5fb78d04d793da2a Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 16 Jul 2021 21:50:46 -0400 Subject: [PATCH 125/624] Add some knobs to our RNG class Also included is an additive lagged fibonacci generator that should is very similar to EQ's. Also added BIASED_INT_DIST in case someone wants to use "bad" int distribution to more closely match EQ as well. An option to set a custom engine (just in case people would like to play with other std engines) is available. There is also support for GCC's SIMD accelerated extension to std random engines. All these options are hidden behind advanced options in CMake since they're rather advanced knobs. --- CMakeLists.txt | 41 ++++++ common/CMakeLists.txt | 1 + common/additive_lagged_fibonacci_engine.h | 147 ++++++++++++++++++++++ common/random.h | 46 +++++-- 4 files changed, 228 insertions(+), 7 deletions(-) create mode 100644 common/additive_lagged_fibonacci_engine.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 526d68099..55d193624 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -131,6 +131,47 @@ OPTION(EQEMU_BUILD_TESTS "Build utility tests." OFF) OPTION(EQEMU_BUILD_CLIENT_FILES "Build Client Import/Export Data Programs." ON) OPTION(EQEMU_PREFER_LUA "Build with normal Lua even if LuaJIT is found." OFF) +#PRNG options +OPTION(EQEMU_ADDITIVE_LFIB_PRNG "Use Additive LFib for PRNG." OFF) +MARK_AS_ADVANCED(EQEMU_ADDITIVE_LFIB_PRNG) +OPTION(EQEMU_BIASED_INT_DIST "Use biased int dist instead of uniform." OFF) +MARK_AS_ADVANCED(EQEMU_BIASED_INT_DIST) +SET(EQEMU_CUSTOM_PRNG_ENGINE "" CACHE STRING "Custom random engine. (ex. std::default_random_engine)") +MARK_AS_ADVANCED(EQEMU_CUSTOM_PRNG_ENGINE) + +IF(CMAKE_COMPILER_IS_GNUCXX) + OPTION(EQEMU_SFMT19937 "Use GCC's extention for SIMD Fast MT19937." OFF) + MARK_AS_ADVANCED(EQEMU_SFMT19937) +ENDIF() + +IF(EQEMU_ADDITIVE_LFIB_PRNG) + ADD_DEFINITIONS(-DUSE_ADDITIVE_LFIB_PRNG) + IF(EQEMU_SFMT19937) + MESSAGE(STATUS "SFMT19937 and ADDITITVE_LFIB_PRNG both set, SFMT19937 ignored.") + SET(EQEMU_SFMT19937 OFF) + ENDIF() + IF(NOT EQEMU_CUSTOM_PRNG_ENGINE STREQUAL "") + MESSAGE(STATUS "CUSTOM_PRNG_ENGINE and ADDITITVE_LFIB_PRNG both set, CUSTOM_PRNG_ENGINE ignored.") + SET(EQEMU_CUSTOM_PRNG_ENGINE "") + ENDIF() +ENDIF() + +IF(EQEMU_SFMT19937) + ADD_DEFINITIONS(-DUSE_SFMT19937) + IF(NOT EQEMU_CUSTOM_PRNG_ENGINE STREQUAL "") + MESSAGE(STATUS "CUSTOM_PRNG_ENGINE and SFMT19937 both set, CUSTOM_PRNG_ENGINE ignored.") + SET(EQEMU_CUSTOM_PRNG_ENGINE "") + ENDIF() +ENDIF() + +IF(NOT EQEMU_CUSTOM_PRNG_ENGINE STREQUAL "") + ADD_DEFINITIONS(-DUSE_CUSTOM_PRNG_ENGINE=${EQEMU_CUSTOM_PRNG_ENGINE}) +ENDIF() + +IF(EQEMU_BIASED_INT_DIST) + ADD_DEFINITIONS(-DBIASED_INT_DIST) +ENDIF() + IF(EQEMU_COMMANDS_LOGGING) ADD_DEFINITIONS(-DCOMMANDS_LOGGING) ENDIF(EQEMU_COMMANDS_LOGGING) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 084ac9c4f..47edd22e8 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -451,6 +451,7 @@ SET(repositories ) SET(common_headers + additive_lagged_fibonacci_engine.h any.h base_packet.h base_data.h diff --git a/common/additive_lagged_fibonacci_engine.h b/common/additive_lagged_fibonacci_engine.h new file mode 100644 index 000000000..b396b4436 --- /dev/null +++ b/common/additive_lagged_fibonacci_engine.h @@ -0,0 +1,147 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2021 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 +*/ + +#pragma once + +#include +#include +#include +#include +#include + +/* + * This is an additive lagged fibonacci generator as seen in The Art of Computer Programming, Vol. 2 + * This should roughly match the implementation that EQ's client uses and be compatible with our Random class + * + * EQ's rand looks like it was from an example implementation that as posted on pscode.com + * + * You might also want to consider defining BIASED_INT_DIST as well to more closely match EQ + */ + +namespace EQ { + template + class additive_lagged_fibonacci_engine { + static_assert(std::is_unsigned::value, "result_type must be an unsigned integral type"); + static_assert(0u < j && j < k, "0 < j < k"); + static_assert(0u < w && w <= std::numeric_limits::digits, + "template argument substituting w out of bounds"); + + public: + using result_type = UIntType; + static constexpr size_t word_size = w; + static constexpr size_t short_lag = j; + static constexpr size_t long_lag = k; + static constexpr result_type default_seed = 19780503u; // default for subtract_with_carry_engine + + additive_lagged_fibonacci_engine() : additive_lagged_fibonacci_engine(default_seed) {} + + explicit additive_lagged_fibonacci_engine(result_type sd) { seed(sd); } + + void seed(result_type seed = default_seed) + { + state1 = long_lag - long_lag; + state2 = long_lag - short_lag; + state[0] = static_cast(seed) & ((1u << word_size) - 1); + state[1] = 1; + for (int i = 2; i < long_lag; ++i) + state[i] = (state[i - 1] + state[i - 2]) & ((1u << word_size) - 1); + return; + } + // TODO: seed via seed_seq + + static constexpr result_type min() { return 0; } + static constexpr result_type max() { return ((1u << word_size) - 1) >> 6; } + + void discard(unsigned long long z) { + for (; z != 0ULL; --z) + (*this)(); + } + + result_type operator()() { + result_type rand = (state[state1] + state[state2]) & ((1u << word_size) - 1); + state[state1] = rand; + if (++state1 == long_lag) + state1 = 0; + if (++state2 == long_lag) + state2 = 0; + + return rand >> 6; + } + + private: + result_type state1; + result_type state2; + result_type state[long_lag]; + + public: + template + friend bool operator==(const additive_lagged_fibonacci_engine &x, + const additive_lagged_fibonacci_engine &y) + { + return std::equal(x.state, x.state + long_lag, y.state) && x.state1 == y.state1 && + x.state2 == y.state2; + } + + template + friend bool operator!=(const additive_lagged_fibonacci_engine &x, + const additive_lagged_fibonacci_engine &y) + { return !(x == y); } + + template + friend std::basic_ostream & + operator<<(std::basic_istream &os, additive_lagged_fibonacci_engine &x) + { + using ios_base = typename std::basic_istream::ios_base; + + const typename ios_base::fmtflags flags = os.flags(); + const CharT fill = os.fill(); + const CharT space = os.widen(' '); + os.flags(ios_base::dec | ios_base::fixed | ios_base::left); + os.fill(space); + + for (size_t i = 0; i < long_lag; ++i) + os << x.state[i] << space; + os << x.state1 << space << x.state2; + + os.flags(flags); + os.fill(fill); + return os; + } + + template + friend std::basic_istream & + operator>>(std::basic_istream &is, additive_lagged_fibonacci_engine &x) + { + using ios_base = typename std::basic_istream::ios_base; + + const typename ios_base::fmtflags flags = is.flags(); + is.flags(ios_base::dec | ios_base::skipws); + + for (size_t i = 0; i < long_lag; ++i) + is >> x.state[i]; + is >> x.state1; + is >> x.state2; + + is.flags(flags); + return is; + } + }; + + using EQRand = additive_lagged_fibonacci_engine; +}; + diff --git a/common/random.h b/common/random.h index 203755efd..6d351121f 100644 --- a/common/random.h +++ b/common/random.h @@ -1,5 +1,5 @@ /* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2014 EQEMu Development Team (http://eqemulator.net) + Copyright (C) 2001-2021 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 @@ -25,10 +25,24 @@ #include #include -/* This uses mt19937 seeded with the std::random_device - * The idea is to have this be included as a member of another class - * so mocking out for testing is easier - * If you need to reseed random.Reseed() +#ifdef USE_SFMT19937 +// only GCC supports, so lets turn it off +#ifndef __GNUC__ +#undef USE_SFMT19937 +#endif +#ifdef __clang__ +#undef USE_SFMT19937 +#endif +#endif + +#ifdef USE_ADDITIVE_LFIB_PRNG +#include "additive_lagged_fibonacci_engine.h" +#elif defined(USE_SFMT19937) +#include +#endif + +/* This uses mt19937 (or other compatible engine) seeded with the std::random_device. The idea is to have this be + * included as a member of another class so mocking out for testing is easier If you need to reseed random.Reseed() * Eventually this should be derived from an abstract base class */ @@ -40,7 +54,12 @@ namespace EQ { { if (low > high) std::swap(low, high); +// EQ uses biased int distribution, so I guess we can support it :P +#ifdef BIASED_INT_DIST + return low + m_gen() % (high - low + 1); +#else return int_dist(m_gen, int_param_t(low, high)); // [low, high] +#endif } // AKA old MakeRandomFloat @@ -98,11 +117,24 @@ namespace EQ { } private: +#ifndef BIASED_INT_DIST typedef std::uniform_int_distribution::param_type int_param_t; - typedef std::uniform_real_distribution::param_type real_param_t; - std::mt19937 m_gen; std::uniform_int_distribution int_dist; +#endif + typedef std::uniform_real_distribution::param_type real_param_t; std::uniform_real_distribution real_dist; +// define USE_CUSTOM_PRNG_ENGINE to any supported random engine like: +// #define USE_CUSTOM_PRNG_ENGINE std::default_random_engine +#ifdef USE_ADDITIVE_LFIB_PRNG + EQRand +#elif defined(USE_SFMT19937) + __gnu_cxx::sfmt19937 +#elif defined(USE_CUSTOM_PRNG_ENGINE) + USE_CUSTOM_PRNG_ENGINE +#else + std::mt19937 +#endif + m_gen; }; } From 2b74d71ff5e86d40b24792ebc3a45535cf0292ba Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 20 Jul 2021 11:06:20 -0400 Subject: [PATCH 126/624] [Feature] New SPAs pass 2 (#1459) * Implemented SPA Duration Pct Implemented new spell effects SE_Duration_HP_Pct 524 SE_Duration_Mana_Pct 525 SE_Duration_Endurance_Pct 526 Consumes 'base1' % of your maximum health/mana/endurance every 6 seconds. 'max' is maximum amount that can be consumed per tic. Additional Functionality Can be used as a heal/gain % by setting the base1 value to a positive. * Implemented SPA Instant Mana/End pct Fixes for SPA 524-526 Implemented SE_Instant_Mana_Pct 522 SE_Instant_Endurance_Pct 523 Extracts 'base1' percent of your maximum mana/endurance, or 'max', whichever is lower. * Implemented: SPA 521 EndAbsorbPctDmg Implemented SE_Endurance_Absorb_Pct_Damage 521 Absorb Damage using Endurance: base1 % (base2 End per 1 HP) Note: Both base1 and base2 need to be divided by 100 for actually value * Implemented SE_HealthTransfer 509 Implemented SE_Health_Transfer 509 'life burn' Consume base2 % of Hit Points to Damage for base % of Hit Points Can be used for heal Act of Valor * Implemented SPA 515,516,518,496 Implemented SE_AC_Avoidance_Max_Percent 515 SE_AC_Mitigation_Max_Percent 516 SE_Attack_Accuracy_Max_Percent 518 Above are stackable defense and offensive mods SE_Critical_Melee_Damage_Mod_Max 496 - This is a non stackable melee critical modifier * Implemented SPA 503 , 505 SE_Melee_Damage_Position_Mod 503 define SE_Damage_Taken_Position_Mod 505 SPA 503 increase/decreases melee damage by percent base1 based on your position base2 0=back 1=front SPA 504 increase/decreases melee damage taken by percent base1 based on your position base2 0=back 1=front * Implemented 467,468 Implemented SE_DS_Mitigation_Amount 467 SE_DS_Mitigation_Percentage 468 Reduce incoming DS by amt or percentage. base1 is value, if a reduction is desired it should be set to negative for both. * Fixes Formula fixes * Update spdat.h Added spa descriptions. * Implemented SPA 469, 470 Implemented SE_Chance_Best_in_Spell_Grp 469 Chance to cast highest scribed spell within a spell group. All base2 spells share roll chance, only 1 cast. SE_Trigger_Best_in_Spell_Grp 470 Chance to cast highest scribed spell within a spell group. Each spell has own chance. Additional Changes: Rewrote TrySpellTrigger function used for SPA 340 since it incorporates SPA 469. Improved code so that chance of spell being triggered should be more accurate statistically. * Implemented SPA 474, 494 Implemented SE_Pet_Crit_Melee_Damage_Pct_Owner 474 - Gives pets a critical melee damage modifier from the owner SE_Pet_Add_Atk 494 - Gives pet a ATK bonus from the owner Fixed SE_PetMeleeMitigation 397 - The bonus was not being calculated * Implemented SPA 465,477,478 Implemented SE_PC_Pet_AE_Rampage 465 Chance for pet to AE rampage with a damage modifier SE_Hatelist_To_Top_Index 477 Chance to be put on top of RAMPAGE list SE_Hatelist_To_Tail_Index 478 Chance to be put on bottom of RAMPAGE list * Implemented Implemented SE_Fearstun 502 Stun with a max level limit. Normal stun restrictions don't apply. Base1 duration, base2 PC duration, max is level limit SE_TwinCastBlocker 39 Previously unused spell effect that is now used on live. Simply, if this effect is present in a spell then the spell can not be twin cast. * Implemented SPA 483 Implemented Fc_Spell_Damage_Pct_IncomingPC 483 - Focus effect that modifies iby percent incoming spell damage on the target. Base1= min Base2= max. Final percent is random between max and min each time focus is applied from a spell cast. Note: Written to stack with similar functioning focus SPA 269 SE_FcSpellVulnerability. * Implemented SPA 484 Implemented SE_Fc_Spell_Damage_Amt_IncomingPC 484 // focus effect that modifies incoming spell damage by flat amount. Consider it a debuff that adds damage to incoming spells. Positive value to add additional damage. * Implemented SPA 481, 485,486,512 Implemented SE_Fc_Cast_Spell_On_Land 481 Focus effect that is checked when a spell is cast on a target, if target has this focus effect and all limiting criteria are met, then the target will cast a spell as specified by the focus. Can be given a roll chance for success. Base1=Chance, Base2=Spellid Note: This spell has a huge amount of potential applications. See 'Alliance' type spells on live. (ie live spell 50247) Implemented associated focus limits seen in live spells. SE_Ff_CasterClass 485 - Caster of spell on target with a focus effect that is checked by incoming spells must be specified class or classes. SE_Ff_Same_Caster 486 -Caster of spell on target with a focus effect that is checked by incoming spells 0=Must be different caster 1=Must be same caster The following is an associated effect seen with SPA 481 SE_Proc_Timer_Modifier 512 This provides a way to rate limit the amount of spell triggers generated by SPA 481. For example after 1 successful spell trigger no additional spells can be triggered for 1.5 seconds. Ie. Base=1 and Base2 1500. Written in a flexible format to allow scaling of multiple different buffs with this effect at same time. * Stacking fixes for new effects Stacking fixes for new effects. * merge with upstream master merge and update up spdat.h * Update spdat.h * Fix for bolt spell targeting self if target zone/died while casting. Fix for bolt spell targeting self if target zone/died while casting. Despite the name being "ST_TargetOptional", this target type is reserved for projectile spells which all require a target, thus should be treated like any other targeted spell. --- common/spdat.cpp | 6 + common/spdat.h | 47 +++--- zone/aggro.cpp | 1 + zone/attack.cpp | 77 ++++++--- zone/bonuses.cpp | 65 +++++++ zone/client.h | 1 + zone/client_process.cpp | 3 + zone/common.h | 11 +- zone/mob.cpp | 251 +++++++++++++++++++++------ zone/mob.h | 14 +- zone/mob_ai.cpp | 24 ++- zone/spell_effects.cpp | 365 +++++++++++++++++++++++++++++++++++++--- zone/spells.cpp | 38 ++++- zone/zonedb.cpp | 2 + 14 files changed, 766 insertions(+), 139 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index fe6e4db9c..6c6e7dcd7 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1218,6 +1218,12 @@ bool IsEffectIgnoredInStacking(int spa) case SE_LimitUseType: case SE_GravityEffect: case 425: + //Spell effects implemented after ROF2, following same pattern, lets assume these should go here. + case SE_Fc_Spell_Damage_Pct_IncomingPC: + case SE_Fc_Spell_Damage_Amt_IncomingPC: + case SE_Ff_CasterClass: + case SE_Ff_Same_Caster: + case SE_Proc_Timer_Modifier: return true; default: return false; diff --git a/common/spdat.h b/common/spdat.h index 24aac5890..4dbbc7bbb 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -165,6 +165,7 @@ #define MAX_SYMPATHETIC_PROCS 10 // Number of sympathetic procs a client can have (This is arbitrary) + const int Z_AGGRO=10; const uint32 MobAISpellRange=100; // max range of buffs @@ -278,7 +279,7 @@ enum RESISTTYPE //Target Type IDs typedef enum { -/* 01 */ ST_TargetOptional = 0x01, +/* 01 */ ST_TargetOptional = 0x01, //only used for targeted projectile spells /* 02 */ ST_AEClientV1 = 0x02, /* 03 */ ST_GroupTeleport = 0x03, /* 04 */ ST_AECaster = 0x04, @@ -381,7 +382,7 @@ typedef enum { #define SE_PoisonCounter 36 // implemented //#define SE_DetectHostile 37 // not used //#define SE_DetectMagic 38 // not used -//#define SE_DetectPoison 39 // not used +#define SE_TwinCastBlocker 39 // implemented - If present in spell, then the spell can not be twincast. #define SE_DivineAura 40 // implemented #define SE_Destroy 41 // implemented - Disintegrate, Banishment of Shadows #define SE_ShadowStep 42 // implemented @@ -464,7 +465,7 @@ typedef enum { #define SE_AttackSpeed3 119 // implemented #define SE_HealRate 120 // implemented - reduces healing by a % #define SE_ReverseDS 121 // implemented -//#define SE_ReduceSkill 122 // not used +//#define SE_ReduceSkill 122 // not implemented TODO: Now used on live, decreases skills by percent #define SE_Screech 123 // implemented Spell Blocker(If have buff with value +1 will block any effect with -1) #define SE_ImprovedDamage 124 // implemented #define SE_ImprovedHeal 125 // implemented @@ -488,7 +489,7 @@ typedef enum { #define SE_LimitCastTimeMin 143 // implemented #define SE_LimitCastTimeMax 144 // implemented (*not used in any known live spell) #define SE_Teleport2 145 // implemented - Banishment of the Pantheon -//#define SE_ElectricityResist 146 // *not implemented (Lightning Rod: 23233) +//#define SE_ElectricityResist 146 // *not implemented TODO: Now used on live, xyz for teleport spells? also in temp pets? #define SE_PercentalHeal 147 // implemented #define SE_StackingCommand_Block 148 // implemented? #define SE_StackingCommand_Overwrite 149 // implemented? @@ -638,7 +639,7 @@ typedef enum { #define SE_FrontalStunResist 293 // implemented[AA] - Reduce chance to be stunned from front. -- live descriptions sounds like this isn't limited to frontal anymore #define SE_CriticalSpellChance 294 // implemented - increase chance to critical hit and critical damage modifier. //#define SE_ReduceTimerSpecial 295 // not used -#define SE_FcSpellVulnerability 296 // implemented - increase in incoming spell damage +#define SE_FcSpellVulnerability 296 // implemented - increase in incoming spell damage [base1= min dmg base2= max dmg] #define SE_FcDamageAmtIncoming 297 // implemented - debuff that adds points damage to spells cast on target (focus effect). #define SE_ChangeHeight 298 // implemented #define SE_WakeTheDead 299 // implemented @@ -674,7 +675,7 @@ typedef enum { #define SE_ManaAbsorbPercentDamage 329 // implemented #define SE_CriticalDamageMob 330 // implemented #define SE_Salvage 331 // implemented - chance to recover items that would be destroyed in failed tradeskill combine -#define SE_SummonToCorpse 332 // *not implemented AA - Call of the Wild (Druid/Shaman Res spell with no exp) +#define SE_SummonToCorpse 332 // *not implemented AA - Call of the Wild (Druid/Shaman Res spell with no exp) TOOD: implement this. #define SE_CastOnRuneFadeEffect 333 // implemented #define SE_BardAEDot 334 // implemented #define SE_BlockNextSpellFocus 335 // implemented - base1 chance to block next spell ie Puratus (8494) @@ -682,7 +683,7 @@ typedef enum { #define SE_PercentXPIncrease 337 // implemented #define SE_SummonAndResAllCorpses 338 // implemented #define SE_TriggerOnCast 339 // implemented -#define SE_SpellTrigger 340 // implemented - chance to trigger spell +#define SE_SpellTrigger 340 // implemented - chance to trigger spell [Share rolls with 469] All base2 spells share roll chance, only 1 cast. #define SE_ItemAttackCapIncrease 341 // implemented[AA] - increases the maximum amount of attack you can gain from items. #define SE_ImmuneFleeing 342 // implemented - stop mob from fleeing #define SE_InterruptCasting 343 // implemented - % chance to interrupt spells being cast every tic. Cacophony (8272) @@ -739,7 +740,7 @@ typedef enum { #define SE_FcHealAmtIncoming 394 // implemented - Adds/Removes amount of healing on target by X value with foucs restrictions. #define SE_FcHealPctCritIncoming 395 // implemented[AA] - Increases chance of having a heal crit when cast on you. [focus limited] #define SE_FcHealAmtCrit 396 // implemented - Adds a direct healing amount to spells -#define SE_PetMeleeMitigation 397 // implemented[AA] - additional mitigation to your pets. Adds AC. +#define SE_PetMeleeMitigation 397 // implemented[AA] - additional mitigation to your pets. Adds AC #define SE_SwarmPetDuration 398 // implemented - Affects the duration of swarm pets #define SE_FcTwincast 399 // implemented - cast 2 spells for every 1 #define SE_HealGroupFromMana 400 // implemented - Drains mana and heals for each point of mana drained @@ -807,28 +808,28 @@ typedef enum { #define SE_FcDamageAmt2 462 // implemented - Increase spell damage by flat amount (SE_Fc_Damage_Amt2) //#define SE_Shield_Target 463 // #define SE_PC_Pet_Rampage 464 // implemented - Base1 % chance to do rampage for base2 % of damage each melee round -//#define SE_PC_Pet_AE_Rampage 465 // Would assume as above but need to confirm. +#define SE_PC_Pet_AE_Rampage 465 // implemented - Base1 % chance to do AE rampage for base2 % of damage each melee round #define SE_PC_Pet_Flurry_Chance 466 // implemented - Base1 % chance to do flurry from double attack hit. #define SE_DS_Mitigation_Amount 467 // implemented - Modify incoming damage shield damage by a flat amount #define SE_DS_Mitigation_Percentage 468 // implemented - Modify incoming damage shield damage by percentage -//#define SE_Chance_Best_in_Spell_Grp 469 // -//#define SE_Trigger_Best_in_Spell Grp 470 // +#define SE_Chance_Best_in_Spell_Grp 469 // implemented - Chance to cast highest scribed spell within a spell group. All base2 spells share roll chance, only 1 cast. +#define SE_Trigger_Best_in_Spell_Grp 470 // implemented - Chance to cast highest scribed spell within a spell group. Each spell has own chance. //#define SE_Double_Melee_Round 471 // //#define SE_Buy_AA_Rank 472 // #define SE_Double_Backstab_Front 473 // implemented - Chance to double backstab from front -//#define SE_Pet_Crit_Melee_Damage_Pct_Owner 474 // -//#define SE_Trigger_Spell_Non_Item 475 // +#define SE_Pet_Crit_Melee_Damage_Pct_Owner 474 // implemenetd - Critical damage mod applied to pets from owner +#define SE_Trigger_Spell_Non_Item 475 // implemented - Trigger spell on cast only if not from item click. //#define SE_Weapon_Stance 476 // -//#define SE_Hatelist_To_Top_Index 477 // -//#define SE_Hatelist_To_Tail_Index 478 // +#define SE_Hatelist_To_Top_Index 477 // Implemented - Chance to be set to top of rampage list +#define SE_Hatelist_To_Tail_Index 478 // Implemented - Chance to be set to bottom of rampage list //#define SE_Ff_Value_Min 479 // //#define SE_Ff_Value_Max 480 // -//#define SE_Fc_Cast_Spell_On_Land 481 // +#define SE_Fc_Cast_Spell_On_Land 481 // Implemented - [FOCUS] Spells cast on target with this Focus Effect will have chance to cause a Spell to be cast if limits met. //#define SE_Skill_Base_Damage_Mod 482 // -//#define SE_Fc_Spell_Damage_Pct_IncomingPC 483 // -//#define SE_Fc_Spell_Damage_Amt_IncomingPC 484 // -//#define SE_Ff_CasterClass 485 // -//#define SE_Ff_Same_Caster 486 // +#define SE_Fc_Spell_Damage_Pct_IncomingPC 483 // Implemented - [FOCUS] modifies incoming spell damage by percent +#define SE_Fc_Spell_Damage_Amt_IncomingPC 484 // Implemented - [FOCUS] modifies incoming spell damage by flat amount. Typically adds damage to incoming spells. +#define SE_Ff_CasterClass 485 // Implemented - [FOCUS LIMIT] Caster of spell on target with a focus effect that is checked by incoming spells must be specified class. +#define SE_Ff_Same_Caster 486 // Implemented - [FOCUS LIMIT] Caster of spell on target with a focus effect that is checked by incoming spells 0=Must be different caster 1=Must be same caster //#define SE_Extend_Tradeskill_Cap 487 // //#define SE_Defender_Melee_Force_Pct_PC 488 // //#define SE_Worn_Endurance_Regen_Cap 489 // @@ -836,7 +837,7 @@ typedef enum { //#define SE_Ff_ReuseTimeMax 491 // //#define SE_Ff_Endurance_Min 492 // //#define SE_Ff_Endurance_Max 493 // -//#define SE_Pet_Add_Atk 494 // +#define SE_Pet_Add_Atk 494 // implemented - Bonus on pet owner which gives their pet increased attack stat //#define SE_Ff_DurationMax 495 // #define SE_Critical_Melee_Damage_Mod_Max 496 // implemented - increase or decrease by percent critical damage (not stackable) //#define SE_Ff_FocusCastProcNoBypass 497 // @@ -844,7 +845,7 @@ typedef enum { //#define SE_AddExtraAttackPct_1h_Secondary 499 // //#define SE_Fc_CastTimeMod2 500 // //#define SE_Fc_CastTimeAmt 501 // -//#define SE_Fearstun 502 // +#define SE_Fearstun 502 // implemented - Stun with a max level limit. Normal stun restrictions don't apply. #define SE_Melee_Damage_Position_Mod 503 // implemented - modify melee damage by pct if done from Front or Behind //#define SE_Melee_Damage_Position_Amt 504 // #define SE_Damage_Taken_Position_Mod 505 // implemented - mitigate melee damage by pct if dmg taken from Front or Behind @@ -854,7 +855,7 @@ typedef enum { #define SE_Health_Transfer 509 // implemented - exchange health for damage or healing on a target. ie Lifeburn/Act of Valor //#define SE_Fc_ResistIncoming 510 // //#define SE_Ff_FocusTimerMin 511 // -//#define SE_Proc_Timer_Modifier 512 // +#define SE_Proc_Timer_Modifier 512 // implemented - spell trigger limiter used currently with SPA 481, ie. limit to 1 proc every 1.5 seconds (base=1 base2=1500). //#define SE_Mana_Max_Percent 513 // //#define SE_Endurance_Max_Percent 514 // #define SE_AC_Avoidance_Max_Percent 515 // implemented - stackable avoidance modifier diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 5829bc3a3..a3f52fa55 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -1018,6 +1018,7 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc) case SE_Mez: case SE_Charm: case SE_Fear: + case SE_Fearstun: AggroAmount += default_aggro; break; case SE_Root: diff --git a/zone/attack.cpp b/zone/attack.cpp index 303d46fe5..d09271c55 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -274,14 +274,8 @@ int Mob::GetTotalDefense() // 172 Evasion aka SE_AvoidMeleeChance evasion_bonus += itembonuses.AvoidMeleeChanceEffect + aabonuses.AvoidMeleeChanceEffect; // item bonus here isn't mod2 avoidance - Mob *owner = nullptr; - if (IsPet()) - owner = GetOwner(); - else if (IsNPC() && CastToNPC()->GetSwarmOwner()) - owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner()); - - if (owner) // 215 Pet Avoidance % aka SE_PetAvoidance - evasion_bonus += owner->aabonuses.PetAvoidance + owner->spellbonuses.PetAvoidance + owner->itembonuses.PetAvoidance; + // 215 Pet Avoidance % aka SE_PetAvoidance + evasion_bonus += GetPetAvoidanceBonusFromOwner(); // Evasion is a percentage bonus according to AA descriptions if (evasion_bonus) @@ -823,13 +817,7 @@ int Mob::ACSum(bool skip_caps) // According to the guild hall Combat Dummies, a level 50 classic EQ mob it should be ~115 // For a 60 PoP mob ~120, 70 OoW ~120 ac += GetAC(); - Mob *owner = nullptr; - if (IsPet()) - owner = GetOwner(); - else if (CastToNPC()->GetSwarmOwner()) - owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner()); - if (owner) - ac += owner->aabonuses.PetAvoidance + owner->spellbonuses.PetAvoidance + owner->itembonuses.PetAvoidance; + ac += GetPetACBonusFromOwner(); auto spell_aa_ac = aabonuses.AC + spellbonuses.AC; ac += GetSkill(EQ::skills::SkillDefense) / 5; if (EQ::ValueWithin(static_cast(GetClass()), NECROMANCER, ENCHANTER)) @@ -925,7 +913,7 @@ int Mob::offense(EQ::skills::SkillType skill) if (stat_bonus >= 75) offense += (2 * stat_bonus - 150) / 3; - offense += GetATK(); + offense += GetATK() + GetPetATKBonusFromOwner(); return offense; } @@ -4343,7 +4331,7 @@ void Mob::TryPetCriticalHit(Mob *defender, DamageHitInfo &hit) if (critChance > 0) { if (zone->random.Roll(critChance)) { - critMod += GetCritDmgMod(hit.skill); + critMod += GetCritDmgMod(hit.skill, owner); hit.damage_done += 5; hit.damage_done = (hit.damage_done * critMod) / 100; @@ -5266,15 +5254,22 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac if (mod > 0) spec_mod = mod; if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) { - int spell = spellbonuses.PC_Pet_Rampage[1] + itembonuses.PC_Pet_Rampage[1] + aabonuses.PC_Pet_Rampage[1]; - if (spell > spec_mod) - spec_mod = spell; + //SE_PC_Pet_Rampage SPA 464 on pet, damage modifier + int spell_mod = spellbonuses.PC_Pet_Rampage[1] + itembonuses.PC_Pet_Rampage[1] + aabonuses.PC_Pet_Rampage[1]; + if (spell_mod > spec_mod) + spec_mod = spell_mod; } } else if (IsSpecialAttack(eSpecialAttacks::AERampage)) { int mod = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 2); if (mod > 0) spec_mod = mod; + if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) { + //SE_PC_Pet_AE_Rampage SPA 465 on pet, damage modifier + int spell_mod = spellbonuses.PC_Pet_AE_Rampage[1] + itembonuses.PC_Pet_AE_Rampage[1] + aabonuses.PC_Pet_AE_Rampage[1]; + if (spell_mod > spec_mod) + spec_mod = spell_mod; + } } if (spec_mod > 0) hit.damage_done = (hit.damage_done * spec_mod) / 100; @@ -5625,6 +5620,48 @@ void Mob::DoOffHandAttackRounds(Mob *target, ExtraAttackOptions *opts) } } + +int Mob::GetPetAvoidanceBonusFromOwner() +{ + Mob *owner = nullptr; + if (IsPet()) + owner = GetOwner(); + else if (IsNPC() && CastToNPC()->GetSwarmOwner()) + owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner()); + + if (owner) + return owner->aabonuses.PetAvoidance + owner->spellbonuses.PetAvoidance + owner->itembonuses.PetAvoidance; + + return 0; +} +int Mob::GetPetACBonusFromOwner() +{ + Mob *owner = nullptr; + if (IsPet()) + owner = GetOwner(); + else if (IsNPC() && CastToNPC()->GetSwarmOwner()) + owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner()); + + if (owner) + return owner->aabonuses.PetMeleeMitigation + owner->spellbonuses.PetMeleeMitigation + owner->itembonuses.PetMeleeMitigation; + + return 0; +} +int Mob::GetPetATKBonusFromOwner() +{ + Mob *owner = nullptr; + if (IsPet()) + owner = GetOwner(); + else if (IsNPC() && CastToNPC()->GetSwarmOwner()) + owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner()); + + if (owner) + return owner->aabonuses.Pet_Add_Atk + owner->spellbonuses.Pet_Add_Atk + owner->itembonuses.Pet_Add_Atk; + + return 0; +} + + bool Mob::GetWasSpawnedInWater() const { return spawned_in_water; } diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index bc8075275..3f067aee9 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1438,6 +1438,13 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) break; } + case SE_PC_Pet_AE_Rampage: { + newbon->PC_Pet_AE_Rampage[0] += base1; //Chance to rampage + if (newbon->PC_Pet_AE_Rampage[1] < base2) + newbon->PC_Pet_AE_Rampage[1] = base2; //Damage modifer - take highest + break; + } + case SE_PC_Pet_Flurry_Chance: newbon->PC_Pet_Flurry += base1; //Chance to Flurry break; @@ -1546,6 +1553,13 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) newbon->DS_Mitigation_Percentage += base1; break; + case SE_Pet_Crit_Melee_Damage_Pct_Owner: + newbon->Pet_Crit_Melee_Damage_Pct_Owner += base1; + break; + + case SE_Pet_Add_Atk: + newbon->Pet_Add_Atk += base1; + break; // to do case SE_PetDiscipline: @@ -3282,6 +3296,13 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne break; } + case SE_PC_Pet_AE_Rampage: { + new_bonus->PC_Pet_AE_Rampage[0] += effect_value; //Chance to rampage + if (new_bonus->PC_Pet_AE_Rampage[1] < base2) + new_bonus->PC_Pet_AE_Rampage[1] = base2; //Damage modifer - take highest + break; + } + case SE_PC_Pet_Flurry_Chance: new_bonus->PC_Pet_Flurry += effect_value; //Chance to Flurry break; @@ -3386,7 +3407,13 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne new_bonus->DS_Mitigation_Percentage += effect_value; break; + case SE_Pet_Crit_Melee_Damage_Pct_Owner: + new_bonus->Pet_Crit_Melee_Damage_Pct_Owner += effect_value; + break; + case SE_Pet_Add_Atk: + new_bonus->Pet_Add_Atk += effect_value; + break; //Special custom cases for loading effects on to NPC from 'npc_spels_effects' table if (IsAISpellEffect) { @@ -3708,6 +3735,8 @@ uint8 Mob::IsFocusEffect(uint16 spell_id,int effect_index, bool AA,uint32 aa_eff return 0; //This is calculated as an actual bonus case SE_FcSpellVulnerability: return focusSpellVulnerability; + case SE_Fc_Spell_Damage_Pct_IncomingPC: + return focusFcSpellDamagePctIncomingPC; case SE_BlockNextSpellFocus: //return focusBlockNextSpell; return 0; //This is calculated as an actual bonus @@ -3725,6 +3754,8 @@ uint8 Mob::IsFocusEffect(uint16 spell_id,int effect_index, bool AA,uint32 aa_eff return focusFcDamagePctCrit; case SE_FcDamageAmtIncoming: return focusFcDamageAmtIncoming; + case SE_Fc_Spell_Damage_Amt_IncomingPC: + return focusFcSpellDamageAmtIncomingPC; case SE_FcHealAmtIncoming: return focusFcHealAmtIncoming; case SE_FcHealPctIncoming: @@ -3739,6 +3770,8 @@ uint8 Mob::IsFocusEffect(uint16 spell_id,int effect_index, bool AA,uint32 aa_eff return focusFcMute; case SE_FcTimerRefresh: return focusFcTimerRefresh; + case SE_Fc_Cast_Spell_On_Land: + return focusFcCastSpellOnLand; case SE_FcStunTimeMod: return focusFcStunTimeMod; case SE_FcHealPctCritIncoming: @@ -3747,6 +3780,7 @@ uint8 Mob::IsFocusEffect(uint16 spell_id,int effect_index, bool AA,uint32 aa_eff return focusFcHealAmt; case SE_FcHealAmtCrit: return focusFcHealAmtCrit; + } return 0; } @@ -4978,6 +5012,37 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) aabonuses.DS_Mitigation_Percentage = effect_value; break; + case SE_Pet_Crit_Melee_Damage_Pct_Owner: + spellbonuses.Pet_Crit_Melee_Damage_Pct_Owner = effect_value; + itembonuses.Pet_Crit_Melee_Damage_Pct_Owner = effect_value; + aabonuses.Pet_Crit_Melee_Damage_Pct_Owner = effect_value; + break; + + case SE_Pet_Add_Atk: + spellbonuses.Pet_Add_Atk = effect_value; + itembonuses.Pet_Add_Atk = effect_value; + aabonuses.Pet_Add_Atk = effect_value; + break; + + case SE_PC_Pet_Rampage: + spellbonuses.PC_Pet_Rampage[0] = effect_value; + itembonuses.PC_Pet_Rampage[0] = effect_value; + aabonuses.PC_Pet_Rampage[0] = effect_value; + spellbonuses.PC_Pet_Rampage[1] = effect_value; + itembonuses.PC_Pet_Rampage[1] = effect_value; + aabonuses.PC_Pet_Rampage[1] = effect_value; + break; + + case SE_PC_Pet_AE_Rampage: + spellbonuses.PC_Pet_AE_Rampage[0] = effect_value; + itembonuses.PC_Pet_AE_Rampage[0] = effect_value; + aabonuses.PC_Pet_AE_Rampage[0] = effect_value; + spellbonuses.PC_Pet_AE_Rampage[1] = effect_value; + itembonuses.PC_Pet_AE_Rampage[1] = effect_value; + aabonuses.PC_Pet_AE_Rampage[1] = effect_value; + break; + + case SE_SkillProcSuccess:{ for(int e = 0; e < MAX_SKILL_PROCS; e++) { diff --git a/zone/client.h b/zone/client.h index 734bc66ff..74e2eed54 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1021,6 +1021,7 @@ public: int GetNextAvailableSpellBookSlot(int starting_slot = 0); inline uint32 GetSpellByBookSlot(int book_slot) { return m_pp.spell_book[book_slot]; } inline bool HasSpellScribed(int spellid) { return FindSpellBookSlotBySpellID(spellid) != -1; } + uint32 GetHighestScribedSpellinSpellGroup(uint32 spell_group); uint16 GetMaxSkillAfterSpecializationRules(EQ::skills::SkillType skillid, uint16 maxSkill); void SendPopupToClient(const char *Title, const char *Text, uint32 PopupID = 0, uint32 Buttons = 0, uint32 Duration = 0); void SendFullPopup(const char *Title, const char *Text, uint32 PopupID = 0, uint32 NegativeID = 0, uint32 Buttons = 0, uint32 Duration = 0, const char *ButtonName0 = 0, const char *ButtonName1 = 0, uint32 SoundControls = 0); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index a78095a30..d805be19a 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -538,6 +538,9 @@ bool Client::Process() { } } + if (focus_proc_limit_timer.Check() && !dead) + FocusProcLimitProcess(); + if (client_state == CLIENT_KICKED) { Save(); OnDisconnect(true); diff --git a/zone/common.h b/zone/common.h index 01c074b11..1acb335de 100644 --- a/zone/common.h +++ b/zone/common.h @@ -124,6 +124,7 @@ typedef enum { //focus types focusSpellHateMod, focusTriggerOnCast, focusSpellVulnerability, + focusFcSpellDamagePctIncomingPC, focusTwincast, focusSympatheticProc, focusFcDamageAmt, @@ -135,6 +136,8 @@ typedef enum { //focus types focusBlockNextSpell, focusFcHealPctIncoming, focusFcDamageAmtIncoming, + focusFcSpellDamageAmtIncomingPC, + focusFcCastSpellOnLand, focusFcHealAmtIncoming, focusFcBaseEffects, focusIncreaseNumHits, @@ -319,6 +322,8 @@ struct Buffs_Struct { int32 ExtraDIChance; int16 RootBreakChance; //Not saved to dbase uint32 instrument_mod; + int16 focusproclimit_time; //timer to limit number of procs from focus effects + int16 focusproclimit_procamt; //amount of procs that can be cast before timer limiter is set bool persistant_buff; bool client; //True if the caster is a client bool UpdateClient; @@ -524,6 +529,7 @@ struct StatBonuses { uint32 SkillProc[MAX_SKILL_PROCS]; // Max number of spells containing skill_procs. uint32 SkillProcSuccess[MAX_SKILL_PROCS]; // Max number of spells containing skill_procs_success. uint32 PC_Pet_Rampage[2]; // 0= % chance to rampage, 1=damage modifier + uint32 PC_Pet_AE_Rampage[2]; // 0= % chance to AE rampage, 1=damage modifier uint32 PC_Pet_Flurry; // Percent chance flurry from double attack int32 Attack_Accuracy_Max_Percent; // Increase ATK accuracy by percent. int32 AC_Mitigation_Max_Percent; // Increase AC mitigation by percent @@ -532,7 +538,10 @@ struct StatBonuses { int32 Melee_Damage_Position_Mod[2]; // base = percent melee damage increase base2 0=back 1=front. [0]Back[1]Front int32 Double_Backstab_Front; // base = percent chance to double back stab front int32 DS_Mitigation_Amount; // base = flat amt DS mitigation. Negative value to reduce - int32 DS_Mitigation_Percentage; // base = percent amt of DS mitigation. Negative value to reduce + int32 DS_Mitigation_Percentage; // base = percent amt of DS mitigation. Negative value to reduce + int32 Pet_Crit_Melee_Damage_Pct_Owner; // base = percent mod for pet critcal damage from owner + int32 Pet_Add_Atk; // base = Pet ATK bonus from owner + // AAs int8 Packrat; //weight reduction for items, 1 point = 10% diff --git a/zone/mob.cpp b/zone/mob.cpp index 46d9447f9..e26c5406c 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -102,6 +102,7 @@ Mob::Mob( ranged_timer(2000), tic_timer(6000), mana_timer(2000), + focus_proc_limit_timer(250), spellend_timer(0), rewind_timer(30000), bindwound_timer(10000), @@ -3537,57 +3538,82 @@ void Mob::TriggerOnCast(uint32 focus_spell, uint32 spell_id, bool aa_trigger) } } + + + bool Mob::TrySpellTrigger(Mob *target, uint32 spell_id, int effect) { - if(!target || !IsValidSpell(spell_id)) + if (!target || !IsValidSpell(spell_id)) return false; - int spell_trig = 0; - // Count all the percentage chances to trigger for all effects - for(int i = 0; i < EFFECT_COUNT; i++) - { - if (spells[spell_id].effectid[i] == SE_SpellTrigger) - spell_trig += spells[spell_id].base[i]; - } - // If all the % add to 100, then only one of the effects can fire but one has to fire. - if (spell_trig == 100) - { - int trig_chance = 100; - for(int i = 0; i < EFFECT_COUNT; i++) - { - if (spells[spell_id].effectid[i] == SE_SpellTrigger) - { - if(zone->random.Int(0, trig_chance) <= spells[spell_id].base[i]) - { - // If we trigger an effect then its over. - if (IsValidSpell(spells[spell_id].base2[i])){ - SpellFinished(spells[spell_id].base2[i], target, EQ::spells::CastingSlot::Item, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff); - return true; - } - } - else - { - // Increase the chance to fire for the next effect, if all effects fail, the final effect will fire. - trig_chance -= spells[spell_id].base[i]; - } - } + /*The effects SE_SpellTrigger (SPA 340) and SE_Chance_Best_in_Spell_Grp (SPA 469) work as follows, you typically will have 2-3 different spells each with their own + chance to be triggered with all chances equaling up to 100 pct, with only 1 spell out of the group being ultimately cast. + (ie Effect1 trigger spellA with 30% chance, Effect2 triggers spellB with 20% chance, Effect3 triggers spellC with 50% chance). + The following function ensures a stastically accurate chance for each spell to be cast based on their chance values. These effects are also used in spells where there + is only 1 effect using the trigger effect. In those situations we simply roll a chance for that spell to be cast once. + Note: Both SPA 340 and 469 can be in same spell and both cummulative add up to 100 pct chances. SPA469 only difference being the spell cast will + be "best in spell group", instead of a defined spell_id.*/ - } - } - // if the chances don't add to 100, then each effect gets a chance to fire, chance for no trigger as well. - else + int chance_array[EFFECT_COUNT] = {}; + int total_chance = 0; + int effect_slot = effect; + bool CastSpell = false; + + for (int i = 0; i < EFFECT_COUNT; i++) { - if(zone->random.Int(0, 100) <= spells[spell_id].base[effect]) - { - if (IsValidSpell(spells[spell_id].base2[effect])){ - SpellFinished(spells[spell_id].base2[effect], target, EQ::spells::CastingSlot::Item, 0, -1, spells[spells[spell_id].base2[effect]].ResistDiff); - return true; //Only trigger once of these per spell effect. + if (spells[spell_id].effectid[i] == SE_SpellTrigger || spells[spell_id].effectid[i] == SE_Chance_Best_in_Spell_Grp) + total_chance += spells[spell_id].base[i]; + } + + if (total_chance == 100) + { + int current_chance = 0; + int cummulative_chance = 0; + + for (int i = 0; i < EFFECT_COUNT; i++){ + //Find spells with SPA 340 and add the cummulative percent chances to the roll array + if ((spells[spell_id].effectid[i] == SE_SpellTrigger) || (spells[spell_id].effectid[i] == SE_Chance_Best_in_Spell_Grp)){ + + cummulative_chance = current_chance + spells[spell_id].base[i]; + chance_array[i] = cummulative_chance; + current_chance = cummulative_chance; + } + } + int random_roll = zone->random.Int(1, 100); + //Determine which spell out of the group of the spells (each with own percent chance out of 100) will be cast based on a single roll. + for (int i = 0; i < EFFECT_COUNT; i++){ + if (chance_array[i] != 0 && random_roll <= chance_array[i]) { + effect_slot = i; + CastSpell = true; + break; } } } + + //If the chances don't add to 100, then each effect gets a chance to fire, chance for no trigger as well. + else if (zone->random.Roll(spells[spell_id].base[effect])) { + CastSpell = true; //In this case effect_slot is what was passed into function. + } + + if (CastSpell) { + if (spells[spell_id].effectid[effect_slot] == SE_SpellTrigger && IsValidSpell(spells[spell_id].base2[effect_slot])) { + SpellFinished(spells[spell_id].base2[effect_slot], target, EQ::spells::CastingSlot::Item, 0, -1, spells[spells[spell_id].base2[effect_slot]].ResistDiff); + return true; + } + else if (IsClient() & spells[spell_id].effectid[effect_slot] == SE_Chance_Best_in_Spell_Grp) { + uint32 best_spell_id = CastToClient()->GetHighestScribedSpellinSpellGroup(spells[spell_id].base2[effect_slot]); + if (IsValidSpell(best_spell_id)) { + SpellFinished(best_spell_id, target, EQ::spells::CastingSlot::Item, 0, -1, spells[best_spell_id].ResistDiff); + } + return true;//Do nothing if you don't have the any spell in spell group scribed. + } + } + return false; } + + void Mob::TryTriggerOnValueAmount(bool IsHP, bool IsMana, bool IsEndur, bool IsPet) { /* @@ -3676,6 +3702,9 @@ void Mob::TryTwincast(Mob *caster, Mob *target, uint32 spell_id) if(!IsValidSpell(spell_id)) return; + if (IsEffectInSpell(spell_id, SE_TwinCastBlocker)) + return; + if(IsClient()) { int32 focus = CastToClient()->GetFocusEffect(focusTwincast, spell_id); @@ -3739,34 +3768,46 @@ void Mob::TryOnSpellFinished(Mob *caster, Mob *target, uint16 spell_id) int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining) { + /* + Modifies incoming spell damage by percent, to increase or decrease damage, can be limited to specific resists. + Can be applied through quest function, spell focus or npc_spells_effects table. This function is run on the target of the spell. + */ + if (!IsValidSpell(spell_id)) return 0; if (!caster) return 0; - int32 value = 0; + int32 total_mod = 0; + int32 innate_mod = 0; + int32 fc_spell_vulnerability_mod = 0; + int32 fc_spell_damage_pct_incomingPC_mod = 0; - //Apply innate vulnerabilities + //Apply innate vulnerabilities from quest functions and tables if (Vulnerability_Mod[GetSpellResistType(spell_id)] != 0) - value = Vulnerability_Mod[GetSpellResistType(spell_id)]; - + innate_mod = Vulnerability_Mod[GetSpellResistType(spell_id)]; else if (Vulnerability_Mod[HIGHEST_RESIST+1] != 0) - value = Vulnerability_Mod[HIGHEST_RESIST+1]; + innate_mod = Vulnerability_Mod[HIGHEST_RESIST+1]; - //Apply spell derived vulnerabilities - if (spellbonuses.FocusEffects[focusSpellVulnerability]){ + //[Apply spell derived vulnerabilities] Step 1: Check this focus effect exists on the mob. + if (spellbonuses.FocusEffects[focusSpellVulnerability]){ int32 tmp_focus = 0; int tmp_buffslot = -1; + /* + Find all buffs that may contain SPA 296, then find which slot has the highest possible effect. Since the focus can use + a min and max amount value to determine final focus amt. To find the best focus, use only max value if possible. Once the + best is found. Run it again to get the final value randoming between min and max. + */ int buff_count = GetMaxTotalSlots(); for(int i = 0; i < buff_count; i++) { if((IsValidSpell(buffs[i].spellid) && IsEffectInSpell(buffs[i].spellid, SE_FcSpellVulnerability))){ - int32 focus = caster->CalcFocusEffect(focusSpellVulnerability, buffs[i].spellid, spell_id, true); + int32 focus = caster->CalcFocusEffect(focusSpellVulnerability, buffs[i].spellid, spell_id, true, buffs[tmp_buffslot].casterid); if (!focus) continue; @@ -3780,21 +3821,61 @@ int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining) tmp_focus = focus; tmp_buffslot = i; } - } } - tmp_focus = caster->CalcFocusEffect(focusSpellVulnerability, buffs[tmp_buffslot].spellid, spell_id); - - if (tmp_focus < -99) - tmp_focus = -99; - - value += tmp_focus; + fc_spell_vulnerability_mod = caster->CalcFocusEffect(focusSpellVulnerability, buffs[tmp_buffslot].spellid, spell_id, false, buffs[tmp_buffslot].casterid); if (tmp_buffslot >= 0) CheckNumHitsRemaining(NumHit::MatchingSpells, tmp_buffslot); } - return value; + + if (spellbonuses.FocusEffects[focusFcSpellDamagePctIncomingPC]) { + + int32 tmp_focus = 0; + int tmp_buffslot = -1; + + /* + Find all buffs that may contain SPA 483, then find which slot has the highest possible effect. Since the focus can use + a min and max amount value to determine final focus amt. To find the best focus, use only max value if possible. Once the + best is found. Run it again to get the final value randoming between min and max. + */ + int buff_count = GetMaxTotalSlots(); + for (int i = 0; i < buff_count; i++) { + + if ((IsValidSpell(buffs[i].spellid) && IsEffectInSpell(buffs[i].spellid, SE_Fc_Spell_Damage_Pct_IncomingPC))) { + + int32 focus = caster->CalcFocusEffect(focusFcSpellDamagePctIncomingPC, buffs[i].spellid, spell_id, true, buffs[tmp_buffslot].casterid); + + if (!focus) + continue; + + if (tmp_focus && focus > tmp_focus) { + tmp_focus = focus; + tmp_buffslot = i; + } + + else if (!tmp_focus) { + tmp_focus = focus; + tmp_buffslot = i; + } + } + } + + fc_spell_damage_pct_incomingPC_mod = caster->CalcFocusEffect(focusFcSpellDamagePctIncomingPC, buffs[tmp_buffslot].spellid, spell_id, false, buffs[tmp_buffslot].casterid); + + if (tmp_buffslot >= 0) + CheckNumHitsRemaining(NumHit::MatchingSpells, tmp_buffslot); + } + + total_mod = fc_spell_vulnerability_mod + fc_spell_damage_pct_incomingPC_mod; + + //Don't let focus derived mods reduce past 99% mitigation. Quest related can, and for custom functionality if negative will give a healing affect instead of damage. + if (total_mod < -99) + total_mod = -99; + + total_mod += innate_mod; + return total_mod; } int32 Mob::GetSkillDmgTaken(const EQ::skills::SkillType skill_used, ExtraAttackOptions *opts) @@ -3856,6 +3937,61 @@ int16 Mob::GetHealRate(uint16 spell_id, Mob* caster) { return heal_rate; } +void Mob::SetBottomRampageList() +{ + auto &mob_list = entity_list.GetCloseMobList(this); + + for (auto &e : mob_list) { + auto mob = e.second; + if (!mob) { + continue; + } + + if (!mob->GetSpecialAbility(SPECATK_RAMPAGE)) { + continue; + } + + if (mob->IsNPC() && mob->CheckAggro(this)) { + for (int i = 0; i < mob->RampageArray.size(); i++) { + // Find this mob in the rampage list + if (this->GetID() == mob->RampageArray[i]) { + //Move to bottom of Rampage List + auto it = mob->RampageArray.begin() + i; + std::rotate(it, it + 1, mob->RampageArray.end()); + } + } + } + } +} + +void Mob::SetTopRampageList() +{ + auto &mob_list = entity_list.GetCloseMobList(this); + + for (auto &e : mob_list) { + auto mob = e.second; + if (!mob) { + continue; + } + + if (!mob->GetSpecialAbility(SPECATK_RAMPAGE)) { + continue; + } + + if (mob->IsNPC() && mob->CheckAggro(this)) { + for (int i = 0; i < mob->RampageArray.size(); i++) { + // Find this mob in the rampage list + if (this->GetID() == mob->RampageArray[i]) { + //Move to Top of Rampage List + auto it = mob->RampageArray.begin() + i; + std::rotate(it, it + 1, mob->RampageArray.end()); + std::rotate(mob->RampageArray.rbegin(), mob->RampageArray.rbegin() + 1, mob->RampageArray.rend()); + } + } + } + } +} + bool Mob::TryFadeEffect(int slot) { if (!buffs[slot].spellid) @@ -4647,7 +4783,7 @@ bool Mob::TrySpellOnDeath() //in death because the heal will not register before the script kills you. } -int16 Mob::GetCritDmgMod(uint16 skill) +int16 Mob::GetCritDmgMod(uint16 skill, Mob* owner) { int critDmg_mod = 0; @@ -4658,6 +4794,9 @@ int16 Mob::GetCritDmgMod(uint16 skill) critDmg_mod += itembonuses.CritDmgModNoStack[EQ::skills::HIGHEST_SKILL + 1] + spellbonuses.CritDmgModNoStack[EQ::skills::HIGHEST_SKILL + 1] + aabonuses.CritDmgModNoStack[EQ::skills::HIGHEST_SKILL + 1] + itembonuses.CritDmgModNoStack[skill] + spellbonuses.CritDmgModNoStack[skill] + aabonuses.CritDmgModNoStack[skill]; + if (owner) //Checked in TryPetCriticalHit + critDmg_mod += owner->aabonuses.Pet_Crit_Melee_Damage_Pct_Owner + owner->itembonuses.Pet_Crit_Melee_Damage_Pct_Owner + owner->spellbonuses.Pet_Crit_Melee_Damage_Pct_Owner; + return critDmg_mod; } diff --git a/zone/mob.h b/zone/mob.h index c0ce7bf5a..4e051d76f 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -770,7 +770,7 @@ public: void QuestJournalledSay(Client *QuestInitiator, const char *str, Journal::Options &opts); int32 GetItemStat(uint32 itemid, const char *identifier); - int16 CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus=false); + int16 CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus=false, uint16 casterid=0); uint8 IsFocusEffect(uint16 spellid, int effect_index, bool AA=false,uint32 aa_effect=0); void SendIllusionPacket(uint16 in_race, uint8 in_gender = 0xFF, uint8 in_texture = 0xFF, uint8 in_helmtexture = 0xFF, uint8 in_haircolor = 0xFF, uint8 in_beardcolor = 0xFF, uint8 in_eyecolor1 = 0xFF, uint8 in_eyecolor2 = 0xFF, @@ -814,7 +814,7 @@ public: void CastOnCure(uint32 spell_id); void CastOnNumHitFade(uint32 spell_id); void SlowMitigation(Mob* caster); - int16 GetCritDmgMod(uint16 skill); + int16 GetCritDmgMod(uint16 skill, Mob* owner = nullptr); int16 GetMeleeDamageMod_SE(uint16 skill); int16 GetMeleeMinDamageMod_SE(uint16 skill); int16 GetCrippBlowChance(); @@ -837,6 +837,10 @@ public: inline int16 GetSpellPowerDistanceMod() const { return SpellPowerDistanceMod; }; inline void SetSpellPowerDistanceMod(int16 value) { SpellPowerDistanceMod = value; }; int32 GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot = 0); + + void CastSpellOnLand(Mob* caster, uint32 spell_id); + void FocusProcLimitProcess(); + bool ApplyFocusProcLimiter(uint32 spell_id, int buffslot = -1); void ModSkillDmgTaken(EQ::skills::SkillType skill_num, int value); int16 GetModSkillDmgTaken(const EQ::skills::SkillType skill_num); @@ -909,6 +913,9 @@ public: inline bool IsTempPet() const { return _IsTempPet; } inline void SetTempPet(bool value) { _IsTempPet = value; } inline bool IsHorse() { return is_horse; } + int GetPetAvoidanceBonusFromOwner(); + int GetPetACBonusFromOwner(); + int GetPetATKBonusFromOwner(); inline const bodyType GetBodyType() const { return bodytype; } inline const bodyType GetOrigBodyType() const { return orig_bodytype; } @@ -966,6 +973,8 @@ public: bool Rampage(ExtraAttackOptions *opts); bool AddRampage(Mob*); void ClearRampage(); + void SetBottomRampageList(); + void SetTopRampageList(); void AreaRampage(ExtraAttackOptions *opts); inline bool IsSpecialAttack(eSpecialAttacks in) { return m_specialattacks == in; } @@ -1414,6 +1423,7 @@ protected: int16 slow_mitigation; // Allows for a slow mitigation (100 = 100%, 50% = 50%) Timer tic_timer; Timer mana_timer; + Timer focus_proc_limit_timer; //spell casting vars Timer spellend_timer; diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 306c6dd64..3766a2546 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1125,6 +1125,9 @@ void Mob::AI_Process() { ProjectileAttack(); + if (focus_proc_limit_timer.Check()) + FocusProcLimitProcess(); + auto npcSpawnPoint = CastToNPC()->GetSpawnPoint(); if (GetSpecialAbility(TETHER)) { float tether_range = static_cast(GetSpecialAbilityParam(TETHER, 0)); @@ -1218,16 +1221,15 @@ void Mob::AI_Process() { } } + + //SE_PC_Pet_Rampage SPA 464 on pet, chance modifier if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) { - if (spellbonuses.PC_Pet_Rampage[0] || itembonuses.PC_Pet_Rampage[0] || - aabonuses.PC_Pet_Rampage[0]) { - int chance = spellbonuses.PC_Pet_Rampage[0] + itembonuses.PC_Pet_Rampage[0] + - aabonuses.PC_Pet_Rampage[0]; - if (zone->random.Roll(chance)) { - Rampage(nullptr); - } + int chance = spellbonuses.PC_Pet_Rampage[0] + itembonuses.PC_Pet_Rampage[0] + aabonuses.PC_Pet_Rampage[0]; + if (chance && zone->random.Roll(chance)) { + Rampage(nullptr); } } + if (GetSpecialAbility(SPECATK_RAMPAGE) && !specialed) { int rampage_chance = GetSpecialAbilityParam(SPECATK_RAMPAGE, 0); @@ -1263,6 +1265,14 @@ void Mob::AI_Process() { } } + //SE_PC_Pet_Rampage SPA 465 on pet, chance modifier + if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) { + int chance = spellbonuses.PC_Pet_AE_Rampage[0] + itembonuses.PC_Pet_AE_Rampage[0] + aabonuses.PC_Pet_AE_Rampage[0]; + if (chance && zone->random.Roll(chance)) { + Rampage(nullptr); + } + } + if (GetSpecialAbility(SPECATK_AREA_RAMPAGE) && !specialed) { int rampage_chance = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 0); rampage_chance = rampage_chance > 0 ? rampage_chance : 20; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 801700bd5..e5e447b5b 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -197,7 +197,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if (!IsPowerDistModSpell(spell_id)) SetSpellPowerDistanceMod(0); - bool SE_SpellTrigger_HasCast = false; + bool spell_trigger_cast_complete = false; //Used with SE_Spell_Trigger and SE_Chance_Best_in_Spell_Grp, true when spell has been triggered. // if buff slot, use instrument mod there, otherwise calc it uint32 instrument_mod = buffslot > -1 ? buffs[buffslot].instrument_mod : caster ? caster->GetInstrumentMod(spell_id) : 10; @@ -2822,9 +2822,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_SpellTrigger: { - if (!SE_SpellTrigger_HasCast) { + if (!spell_trigger_cast_complete) { if (caster && caster->TrySpellTrigger(this, spell_id, i)) - SE_SpellTrigger_HasCast = true; + spell_trigger_cast_complete = true; } break; } @@ -2873,8 +2873,83 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove HealDamage(amt, caster); break; } - + case SE_Chance_Best_in_Spell_Grp: { + if (!spell_trigger_cast_complete) { + if (caster && caster->TrySpellTrigger(this, spell_id, i)) + spell_trigger_cast_complete = true; + } + break; + } + + case SE_Trigger_Best_in_Spell_Grp: { + + if (caster && !caster->IsClient()) + break; + + if (zone->random.Roll(spells[spell_id].base[i])) { + uint32 best_spell_id = caster->CastToClient()->GetHighestScribedSpellinSpellGroup(spells[spell_id].base2[i]); + + if (caster && IsValidSpell(best_spell_id)) + caster->SpellFinished(best_spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[best_spell_id].ResistDiff); + } + break; + } + + case SE_Trigger_Spell_Non_Item: { + //Only trigger if not from item + if (caster && caster->IsClient() && GetCastedSpellInvSlot() > 0) + break; + + if (zone->random.Roll(spells[spell_id].base[i]) && IsValidSpell(spells[spell_id].base2[i])) + caster->SpellFinished(spells[spell_id].base2[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff); + + break; + } + + case SE_Hatelist_To_Tail_Index: { + if (caster && zone->random.Roll(spells[spell_id].base[i])) + caster->SetBottomRampageList(); + break; + } + + case SE_Hatelist_To_Top_Index: { + if (caster && zone->random.Roll(spells[spell_id].base[i])) + caster->SetTopRampageList(); + break; + } + + case SE_Fearstun: { + //Normal 'stun' restrictions do not apply. base1=duration, base2=PC duration, max =lv restrict + if (!caster) + break; + + if (IsNPC() && GetSpecialAbility(UNSTUNABLE)) { + caster->MessageString(Chat::SpellFailure, IMMUNE_STUN); + break; + } + + if (IsNPC() && GetSpecialAbility(UNFEARABLE)) { + caster->MessageString(Chat::SpellFailure, IMMUNE_FEAR); + break; + } + + if (spells[spell_id].max[i] == 0 || GetLevel() <= spells[spell_id].max[i]) { + if (IsClient() && spells[spell_id].base2[i]) + Stun(spells[spell_id].base2[i]); + else + Stun(spells[spell_id].base[i]); + } + else + caster->MessageString(Chat::SpellFailure, FEAR_TOO_HIGH); + break; + } + + case SE_Proc_Timer_Modifier:{ + buffs[buffslot].focusproclimit_procamt = spells[spell_id].base[i]; //Set max amount of procs before lockout timer + break; + } + case SE_PersistentEffect: MakeAura(spell_id); break; @@ -2990,6 +3065,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_HealRate: case SE_SkillDamageTaken: case SE_FcSpellVulnerability: + case SE_Fc_Spell_Damage_Pct_IncomingPC: + case SE_Fc_Spell_Damage_Amt_IncomingPC: case SE_FcTwincast: case SE_DelayDeath: case SE_CastOnFadeEffect: @@ -3131,6 +3208,12 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_DS_Mitigation_Amount: case SE_DS_Mitigation_Percentage: case SE_Double_Backstab_Front: + case SE_Pet_Crit_Melee_Damage_Pct_Owner: + case SE_Pet_Add_Atk: + case SE_TwinCastBlocker: + case SE_Fc_Cast_Spell_On_Land: + case SE_Ff_CasterClass: + case SE_Ff_Same_Caster: { break; } @@ -4406,13 +4489,14 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) case SE_Blank: break; - // Handle Focus Limits + // Handle Focus Limits case SE_LimitResist: if (base1 < 0) { if (spell.resisttype == -base1) // Exclude LimitFailure = true; - } else { + } + else { LimitInclude[0] = true; if (spell.resisttype == base1) // Include LimitInclude[1] = true; @@ -4433,12 +4517,13 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) // every level over cap reduces the effect by base2 percent unless from a clicky when // ItemCastsUseFocus is true if (lvldiff > 0 && (spell_level <= RuleI(Character, MaxLevel) || - RuleB(Character, ItemCastsUseFocus) == false)) { + RuleB(Character, ItemCastsUseFocus) == false)) { if (base2 > 0) { lvlModifier -= base2 * lvldiff; if (lvlModifier < 1) LimitFailure = true; - } else + } + else LimitFailure = true; } break; @@ -4462,7 +4547,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) if (base1 < 0) { // Exclude if (spell_id == -base1) LimitFailure = true; - } else { + } + else { LimitInclude[2] = true; if (spell_id == base1) // Include LimitInclude[3] = true; @@ -4479,13 +4565,15 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) if (base1 < 0) { if (IsEffectInSpell(spell_id, -base1)) // Exclude LimitFailure = true; - } else { + } + else { LimitInclude[4] = true; // they use 33 here for all classes ... unsure if the type check is really needed if (base1 == SE_SummonPet && type == focusReagentCost) { if (IsSummonPetSpell(spell_id) || IsSummonSkeletonSpell(spell_id)) LimitInclude[5] = true; - } else { + } + else { if (IsEffectInSpell(spell_id, base1)) // Include LimitInclude[5] = true; } @@ -4519,7 +4607,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) if (base1 < 0) { if (-base1 == spell.targettype) // Exclude LimitFailure = true; - } else { + } + else { LimitInclude[6] = true; if (base1 == spell.targettype) // Include LimitInclude[7] = true; @@ -4538,7 +4627,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) if (base1 < 0) { if (-base1 == spell.spellgroup) // Exclude LimitFailure = true; - } else { + } + else { LimitInclude[8] = true; if (base1 == spell.spellgroup) // Include LimitInclude[9] = true; @@ -4549,7 +4639,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) if (base1 < 0) { if (-base1 == spell.skill) LimitFailure = true; - } else { + } + else { LimitInclude[10] = true; if (base1 == spell.skill) LimitInclude[11] = true; @@ -4560,7 +4651,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) if (base1 < 0) { // Exclude if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass)) return (0); - } else { + } + else { LimitInclude[12] = true; if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass)) // Include LimitInclude[13] = true; @@ -4571,7 +4663,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) if (base1 < 0) { // Exclude if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass)) return (0); - } else { + } + else { LimitInclude[14] = true; if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass)) // Include LimitInclude[15] = true; @@ -4600,7 +4693,12 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) LimitFailure = true; break; - // Handle Focus Effects + /* These are not applicable to AA's because there is never a 'caster' of the 'buff' with the focus effect. + case SE_Ff_Same_Caster: + case SE_Ff_CasterClass: + */ + + // Handle Focus Effects case SE_ImprovedDamage: if (type == focusImprovedDamage && base1 > value) value = base1; @@ -4667,11 +4765,13 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) if (value > 0) { if (base1 > value) value = base1; - } else { + } + else { if (base1 < value) value = base1; } - } else + } + else value = base1; } break; @@ -4685,7 +4785,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) if (type == focusTriggerOnCast) { if (zone->random.Roll(base1)) { value = base2; - } else { + } + else { value = 0; LimitFailure = true; } @@ -4697,6 +4798,11 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) value = base1; break; + case SE_Fc_Spell_Damage_Pct_IncomingPC: + if (type == focusFcSpellDamagePctIncomingPC) + value = base1; + break; + case SE_BlockNextSpellFocus: if (type == focusBlockNextSpell) { if (zone->random.Roll(base1)) @@ -4709,7 +4815,7 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) value = base1; break; - // Note if using these as AA, make sure this is first focus used. + // Note if using these as AA, make sure this is first focus used. case SE_SympatheticProc: if (type == focusSympatheticProc) value = base2; @@ -4735,6 +4841,11 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) value = base1; break; + case SE_Fc_Spell_Damage_Amt_IncomingPC: + if (type == focusFcSpellDamageAmtIncomingPC) + value = base1; + break; + case SE_FcHealAmtIncoming: if (type == focusFcHealAmtIncoming) value = base1; @@ -4789,6 +4900,14 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) if (type == focusFcStunTimeMod) value = base1; break; + + case SE_Fc_Cast_Spell_On_Land: + if (type == focusFcCastSpellOnLand) { + if (zone->random.Roll(base1)) { + value = base2; + } + break; + } } } @@ -4805,8 +4924,13 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) //given an item/spell's focus ID and the spell being cast, determine the focus ammount, if any //assumes that spell_id is not a bard spell and that both ids are valid spell ids -int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus) +int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus, uint16 casterid) { + /* + 'this' is always the caster of the spell_id, most foci check for effects on the caster, however some check for effects on the target. + 'casterid' is the casterid of the caster of spell_id, used when spell_id is cast on a target with a focus effect that is checked by incoming spell. + */ + if (!IsValidSpell(focus_id) || !IsValidSpell(spell_id)) return 0; @@ -5040,6 +5164,23 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo } break; + case SE_Ff_Same_Caster://hmm do i need to pass casterid from buff slot here + if (focus_spell.base[i] == 0) { + if (casterid == GetID()) + return 0;//Mob casting is same as target, fail if you are casting on yourself. + } + else if (focus_spell.base[i] == 1) { + if (casterid != GetID()) + return 0;//Mob casting is not same as target, fail if you are not casting on yourself. + } + break; + + case SE_Ff_CasterClass: + // Do not use this limit more then once per spell. If multiple class, treat value like items would. + if (!PassLimitClass(focus_spell.base[i], GetClass())) + return 0; + break; + // handle effects case SE_ImprovedDamage: if (type == focusImprovedDamage) { @@ -5211,13 +5352,30 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo if (type == focusSpellVulnerability) { if (best_focus) { if (focus_spell.base2[i] != 0) - value = focus_spell.base2[i]; + value = focus_spell.base2[i]; //max damage else - value = focus_spell.base[i]; + value = focus_spell.base[i]; //min damage } else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; + value = focus_spell.base[i]; //If no max damage set, then default to min damage } else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); + value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); //else random for value + } + } + break; + + case SE_Fc_Spell_Damage_Pct_IncomingPC: + if (type == focusFcSpellDamagePctIncomingPC) { + if (best_focus) { + if (focus_spell.base2[i] != 0) + value = focus_spell.base2[i]; //max damage + else + value = focus_spell.base[i]; //min damage + } + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { + value = focus_spell.base[i]; //If no max damage set, then default to min damage + } + else { + value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); //else random for value } } break; @@ -5247,6 +5405,11 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo value = focus_spell.base[i]; break; + case SE_Fc_Spell_Damage_Amt_IncomingPC: + if (type == focusFcSpellDamageAmtIncomingPC) + value = focus_spell.base[i]; + break; + case SE_FcHealAmtIncoming: if (type == focusFcHealAmtIncoming) value = focus_spell.base[i]; @@ -5307,6 +5470,14 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo value = focus_spell.base[i]; break; + case SE_Fc_Cast_Spell_On_Land: + if (type == focusFcCastSpellOnLand) { + if (zone->random.Roll(focus_spell.base[i])) { + value = focus_spell.base2[i]; + } + break; + } + #if EQDEBUG >= 6 // this spits up a lot of garbage when calculating spell focuses // since they have all kinds of extra effects on them. @@ -6232,6 +6403,7 @@ bool Mob::DoHPToManaCovert(uint16 mana_cost) int32 Mob::GetFcDamageAmtIncoming(Mob *caster, uint32 spell_id, bool use_skill, uint16 skill ) { //Used to check focus derived from SE_FcDamageAmtIncoming which adds direct damage to Spells or Skill based attacks. + //Used to check focus derived from SE_Fc_Spell_Damage_Amt_IncomingPC which adds direct damage to Spells. int32 dmg = 0; bool limit_exists = false; bool skill_found = false; @@ -6280,6 +6452,20 @@ int32 Mob::GetFcDamageAmtIncoming(Mob *caster, uint32 spell_id, bool use_skill, } } } + if (spellbonuses.FocusEffects[focusFcSpellDamageAmtIncomingPC]) { + int buff_count = GetMaxTotalSlots(); + for (int i = 0; i < buff_count; i++) { + + if ((IsValidSpell(buffs[i].spellid) && (IsEffectInSpell(buffs[i].spellid, SE_FcDamageAmtIncoming)))) { + + int32 focus = caster->CalcFocusEffect(focusFcSpellDamageAmtIncomingPC, buffs[i].spellid, spell_id); + if (focus) { + dmg += focus; + CheckNumHitsRemaining(NumHit::MatchingSpells, i); + } + } + } + } return dmg; } @@ -6967,6 +7153,133 @@ void Mob::TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker){ } } +void Mob::CastSpellOnLand(Mob* caster, uint32 spell_id) +{ + /* + This function checks for incoming spells on a mob, if they meet the criteria for focus SE_Fc_Cast_Spell_on_Land then + a new spell will be cast by THIS mob as specified by the focus effect. Note: Chance to cast the spell is determined in + the CalcFocusEffect function if not 100pct. + ApplyFocusProcLimiter() function checks for SE_Proc_Timer_Modifier which allows for limiting how often a spell from effect can be triggered + for example, if set to base=1 and base2= 1500, then for everyone 1 successful trigger, you will be unable to trigger again for 1.5 seconds. + + Live only has this focus in buffs/debuffs that can be placed on a target. TODO: Will consider adding support for it as AA and Item. + */ + if (!caster) + return; + + uint32 trigger_spell_id = 0; + + //Step 1: Check this focus effect exists on the mob. + if (spellbonuses.FocusEffects[focusFcCastSpellOnLand]) { + + int buff_count = GetMaxTotalSlots(); + for (int i = 0; i < buff_count; i++) { + + if ((IsValidSpell(buffs[i].spellid) && (buffs[i].spellid != spell_id) && IsEffectInSpell(buffs[i].spellid, SE_Fc_Cast_Spell_On_Land))) { + + //Step 2: Check if we pass all focus limiters and focus chance roll + trigger_spell_id = caster->CalcFocusEffect(focusFcCastSpellOnLand, buffs[i].spellid, spell_id, false, buffs[i].casterid); + + if (IsValidSpell(trigger_spell_id) && (trigger_spell_id != spell_id)) { + + //Step 3: Check if SE_Proc_Time_Modifier is present and if so apply it. + if (ApplyFocusProcLimiter(buffs[i].spellid, i)) { + //Step 4: Cast spells + if (IsBeneficialSpell(trigger_spell_id)) { + SpellFinished(trigger_spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[trigger_spell_id].ResistDiff); + } + else { + Mob* current_target = GetTarget(); + //For now don't let players cast detrimental effects on themselves if they are targeting themselves. Need to confirm behavior. + if (current_target && current_target->GetID() != GetID()) + SpellFinished(trigger_spell_id, current_target, EQ::spells::CastingSlot::Item, 0, -1, spells[trigger_spell_id].ResistDiff); + } + } + + if (i >= 0) + CheckNumHitsRemaining(NumHit::MatchingSpells, i); + } + } + } + } +} + +bool Mob::ApplyFocusProcLimiter(uint32 spell_id, int buffslot) +{ + if (buffslot < 0) + return false; + + //Do not allow spell cast if timer is active. + if (buffs[buffslot].focusproclimit_time > 0) + return false; + + /* + SE_Proc_Timer_Modifier + base1= amount of total procs allowed until lock out timer is triggered, should be set to at least 1 in any spell for the effect to function. + base2= lock out timer, which prevents any more procs set in ms 1500 = 1.5 seconds + This system allows easy scaling for multiple different buffs with same effects each having seperate active individual timer checks. Ie. + */ + + if (IsValidSpell(spell_id)) { + + for (int i = 0; i < EFFECT_COUNT; i++) { + + //Step 1: Find which slot the spell effect is in. + if (spells[spell_id].effectid[i] == SE_Proc_Timer_Modifier) { + + //Step 2: Check if you still have procs left to trigger, and if so reduce available procs + if (buffs[buffslot].focusproclimit_procamt > 0) { + --buffs[buffslot].focusproclimit_procamt; //Reduce total amount of triggers possible. + } + + //Step 3: If you used all the procs in the time frame then set proc amount back to max + if (buffs[buffslot].focusproclimit_procamt == 0 && spells[spell_id].base[i] > 0) { + buffs[buffslot].focusproclimit_procamt = spells[spell_id].base[i];//reset to max + + //Step 4: Check if timer exists on this spell, and then set it, and activiate global timer if not active + if (buffs[buffslot].focusproclimit_time ==0 && spells[spell_id].base2[i] > 0) { + buffs[buffslot].focusproclimit_time = spells[spell_id].base2[i];//set time + + //Step 5: If timer is not already running, then start it. + if (!focus_proc_limit_timer.Enabled()) { + focus_proc_limit_timer.Start(250); + } + + return true; + } + } + } + } + } + return true; +} + +void Mob::FocusProcLimitProcess() +{ + /* + Fast 250 ms uinversal timer for checking Focus effects that have a proc rate limiter set in actual time. + */ + bool stop_timer = true; + int buff_count = GetMaxTotalSlots(); + for (int buffs_i = 0; buffs_i < buff_count; ++buffs_i) + { + if (IsValidSpell(buffs[buffs_i].spellid)) + { + if (buffs[buffs_i].focusproclimit_time > 0) { + buffs[buffs_i].focusproclimit_time -= 250; + stop_timer = false; + } + + if (buffs[buffs_i].focusproclimit_time < 0) + buffs[buffs_i].focusproclimit_time = 0; + } + } + + if (stop_timer) { + focus_proc_limit_timer.Disable(); + } +} + bool Mob::CheckSpellCategory(uint16 spell_id, int category_id, int effect_id){ if (!IsValidSpell(spell_id) || !category_id) diff --git a/zone/spells.cpp b/zone/spells.cpp index 5e0db7c8a..c246c79a8 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -406,8 +406,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, spell.targettype == ST_Self || spell.targettype == ST_AECaster || spell.targettype == ST_Ring || - spell.targettype == ST_Beam || - spell.targettype == ST_TargetOptional) && target_id == 0) + spell.targettype == ST_Beam) && target_id == 0) { LogSpells("Spell [{}] auto-targeted the caster. Group? [{}], target type [{}]", spell_id, IsGroupSpell(spell_id), spell.targettype); target_id = GetID(); @@ -1585,8 +1584,12 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce case ST_TargetOptional: { - if(!spell_target) - spell_target = this; + if (!spell_target) + { + LogSpells("Spell [{}] canceled: invalid target (normal)", spell_id); + MessageString(Chat::Red, SPELL_NEED_TAR); + return false; // can't cast these unless we have a target + } CastAction = SingleTarget; break; } @@ -3350,6 +3353,8 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid buffs[emptyslot].dot_rune = 0; buffs[emptyslot].ExtraDIChance = 0; buffs[emptyslot].RootBreakChance = 0; + buffs[emptyslot].focusproclimit_time = 0; + buffs[emptyslot].focusproclimit_procamt = 0; buffs[emptyslot].instrument_mod = caster ? caster->GetInstrumentMod(spell_id) : 10; if (level_override > 0) { @@ -3972,6 +3977,10 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r return false; } + //Check SE_Fc_Cast_Spell_On_Land SPA 481 on target, if hit by this spell and Conditions are Met then target will cast the specified spell. + if (spelltar) + spelltar->CastSpellOnLand(this, spell_id); + if (IsValidSpell(spells[spell_id].RecourseLink) && spells[spell_id].RecourseLink != spell_id) SpellFinished(spells[spell_id].RecourseLink, this, CastingSlot::Item, 0, -1, spells[spells[spell_id].RecourseLink].ResistDiff); @@ -5251,6 +5260,27 @@ int Client::FindSpellBookSlotBySpellID(uint16 spellid) { return -1; //default } +uint32 Client::GetHighestScribedSpellinSpellGroup(uint32 spell_group) +{ + //Typical live spells follow 1/5/10 rank value for actual ranks 1/2/3, but this can technically be set as anything. + + int highest_rank = 0; //highest ranked found in spellgroup + uint32 highest_spell_id = 0; //spell_id of the highest ranked spell you have scribed in that spell rank. + + for (int i = 0; i < EQ::spells::SPELLBOOK_SIZE; i++) { + + if (IsValidSpell(m_pp.spell_book[i])) { + if (spells[m_pp.spell_book[i]].spellgroup == spell_group) { + if (highest_rank < spells[m_pp.spell_book[i]].rank) { + highest_rank = spells[m_pp.spell_book[i]].rank; + highest_spell_id = m_pp.spell_book[i]; + } + } + } + } + return highest_spell_id; +} + bool Client::SpellGlobalCheck(uint16 spell_id, uint32 char_id) { std::string spell_global_name; int spell_global_value; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 34d437a90..9c493a915 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -3666,6 +3666,8 @@ void ZoneDatabase::LoadBuffs(Client *client) buffs[slot_id].caston_z = caston_z; buffs[slot_id].ExtraDIChance = ExtraDIChance; buffs[slot_id].RootBreakChance = 0; + buffs[slot_id].focusproclimit_time = 0; + buffs[slot_id].focusproclimit_procamt = 0; buffs[slot_id].UpdateClient = false; buffs[slot_id].instrument_mod = instrument_mod; } From fe7cb764b2c41707766ac127c5f5ea713cc31258 Mon Sep 17 00:00:00 2001 From: KimLS Date: Thu, 22 Jul 2021 19:03:05 -0700 Subject: [PATCH 127/624] Fix for compile issue when you either don't have openSSL or you're using a version not supported by httplib --- CMakeLists.txt | 3 +++ zone/command.cpp | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 526d68099..b7548f5ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -198,6 +198,9 @@ ELSEIF(OpenSSL_FOUND) SET(TLS_LIBRARY_LIBS ${OPENSSL_LIBRARIES}) SET(TLS_LIBRARY_INCLUDE ${OPENSSL_INCLUDE_DIR}) ADD_DEFINITIONS(-DEQEMU_USE_OPENSSL) + IF(${OPENSSL_VERSION} VERSION_GREATER_EQUAL "1.1.1") + ADD_DEFINITIONS(-DCPPHTTPLIB_OPENSSL_SUPPORT) + ENDIF() ELSEIF(MBEDTLS_FOUND) SET(TLS_LIBRARY_TYPE " mbedTLS") SET(TLS_LIBRARY_ENABLED ON) diff --git a/zone/command.cpp b/zone/command.cpp index d786e505c..a02da9ab1 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -75,8 +75,6 @@ #include "mob_movement_manager.h" #include "npc_scale_manager.h" #include "../common/content/world_content_service.h" - -#define CPPHTTPLIB_OPENSSL_SUPPORT #include "../common/http/httplib.h" extern QueryServ* QServ; From b40140422704c1cd014680884eebd16322e0b35a Mon Sep 17 00:00:00 2001 From: splose Date: Mon, 26 Jul 2021 12:21:06 -0400 Subject: [PATCH 128/624] [Quest API] Add $client->SetGMStatus() (#1465) * add $client->SetGMStatus() * add UpdateAdmin after setting status --- zone/client.cpp | 8 +++++++- zone/client.h | 1 + zone/perl_client.cpp | 17 +++++++++++++++++ zone/zonedb.cpp | 18 ++++++++++++++++++ zone/zonedb.h | 3 +++ 5 files changed, 46 insertions(+), 1 deletion(-) diff --git a/zone/client.cpp b/zone/client.cpp index eb93f136f..c49f36eb7 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -10130,7 +10130,6 @@ void Client::SetAFK(uint8 afk_flag) { safe_delete(outapp); } - void Client::SendToInstance(std::string instance_type, std::string zone_short_name, uint32 instance_version, float x, float y, float z, float heading, std::string instance_identifier, uint32 duration) { uint32 zone_id = ZoneID(zone_short_name); std::string current_instance_type = str_tolower(instance_type); @@ -10241,3 +10240,10 @@ void Client::RemoveItem(uint32 item_id, uint32 quantity) } } } + +void Client::SetAdminStatus(int newStatus) { + if (this->Admin() != newStatus) + database.UpdateGMStatus(this->AccountID(), newStatus); + + return; +} diff --git a/zone/client.h b/zone/client.h index 74e2eed54..f3af176c0 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1523,6 +1523,7 @@ public: void LoadAccountFlags(); void SetAccountFlag(std::string flag, std::string val); std::string GetAccountFlag(std::string flag); + void SetAdminStatus(int newStatus); float GetDamageMultiplier(EQ::skills::SkillType how_long_has_this_been_missing); void Consume(const EQ::ItemData *item, uint8 type, int16 slot, bool auto_consume); void PlayMP3(const char* fname); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 36c0ee361..7613d624f 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -3413,6 +3413,22 @@ XS(XS_Client_ReadBook) { XSRETURN_EMPTY; } +XS(XS_Client_SetGMStatus); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetGMStatus) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::SetGMStatus(THIS, int newStatus)"); // @categories Script Utility + { + Client *THIS; + uint32 accID = THIS->AccountID(); + int newStatus = (int)SvIV(ST(1)); + VALIDATE_THIS_IS_CLIENT; + THIS->SetAdminStatus(newStatus); + THIS->UpdateAdmin(true); + } + XSRETURN_EMPTY; +} + XS(XS_Client_UpdateGroupAAs); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_UpdateGroupAAs) { dXSARGS; @@ -5706,6 +5722,7 @@ XS(boot_Client) { newXSproto(strcpy(buf, "UntrainDisc"), XS_Client_UntrainDisc, file, "$$;$"); newXSproto(strcpy(buf, "UntrainDiscAll"), XS_Client_UntrainDiscAll, file, "$;$"); newXSproto(strcpy(buf, "UpdateAdmin"), XS_Client_UpdateAdmin, file, "$;$"); + newXSproto(strcpy(buf, "SetGMStatus"), XS_Client_SetGMStatus, file, "$$"); newXSproto(strcpy(buf, "UpdateGroupAAs"), XS_Client_UpdateGroupAAs, file, "$$$"); newXSproto(strcpy(buf, "UpdateLDoNPoints"), XS_Client_UpdateLDoNPoints, file, "$$$"); newXSproto(strcpy(buf, "UpdateTaskActivity"), XS_Client_UpdateTaskActivity, file, "$$$$;$"); diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 9c493a915..7e195ab14 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -4977,3 +4977,21 @@ void ZoneDatabase::SetEXPModifier(uint32 character_id, uint32 zone_id, double ex ); database.QueryDatabase(query); } + +void ZoneDatabase::UpdateGMStatus(uint32 accID, int newStatus) +{ + if (accID) { + std::string query = fmt::format( + SQL( + UPDATE + `account` + SET `status` = {} + WHERE + `id` = {} + ), + newStatus, + accID + ); + database.QueryDatabase(query); + } +} diff --git a/zone/zonedb.h b/zone/zonedb.h index 7f523e4e1..61eb97ae1 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -289,6 +289,9 @@ public: void DeleteBuyLines(uint32 CharID); void UpdateBuyLine(uint32 CharID, uint32 BuySlot, uint32 Quantity); + + void UpdateGMStatus(uint32 accID, int newStatus); + /** ************************************************ * Character From 792a3b144311d00ac6c06ef86317010ec95e402b Mon Sep 17 00:00:00 2001 From: E Spause Date: Mon, 26 Jul 2021 13:03:17 -0400 Subject: [PATCH 129/624] Add SetGMStatus to LUA, cleanup unused variable, cleanup naming of new function added to Client class, remove unneeded return on void function. (#1471) * Fix issue #1469 - remove unused variable in perl_client * Add SetGMStatus to LUA, clean up naming in client.cpp to be consistent with the perl/lua naming, remove unneeded return in void function * Delete PERL_CLIENT.ipch --- zone/client.cpp | 4 +--- zone/client.h | 2 +- zone/lua_client.cpp | 8 +++++++- zone/lua_client.h | 1 + zone/perl_client.cpp | 3 +-- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index c49f36eb7..ea2ea6fdd 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -10241,9 +10241,7 @@ void Client::RemoveItem(uint32 item_id, uint32 quantity) } } -void Client::SetAdminStatus(int newStatus) { +void Client::SetGMStatus(int newStatus) { if (this->Admin() != newStatus) database.UpdateGMStatus(this->AccountID(), newStatus); - - return; } diff --git a/zone/client.h b/zone/client.h index f3af176c0..0e73370c3 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1523,7 +1523,7 @@ public: void LoadAccountFlags(); void SetAccountFlag(std::string flag, std::string val); std::string GetAccountFlag(std::string flag); - void SetAdminStatus(int newStatus); + void SetGMStatus(int newStatus); float GetDamageMultiplier(EQ::skills::SkillType how_long_has_this_been_missing); void Consume(const EQ::ItemData *item, uint8 type, int16 slot, bool auto_consume); void PlayMP3(const char* fname); diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 233f40622..29699aeff 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -2158,6 +2158,11 @@ void Lua_Client::RemoveItem(uint32 item_id, uint32 quantity) { return self->RemoveItem(item_id, quantity); } +void Lua_Client::SetGMStatus(uint32 newStatus) { + Lua_Safe_Call_Void(); + return self->SetGMStatus(newStatus); +} + luabind::scope lua_register_client() { return luabind::class_("Client") .def(luabind::constructor<>()) @@ -2525,7 +2530,8 @@ luabind::scope lua_register_client() { .def("SendToInstance", (void(Lua_Client::*)(std::string,std::string,uint32,float,float,float,float,std::string,uint32))&Lua_Client::SendToInstance) .def("CountItem", (int(Lua_Client::*)(uint32))&Lua_Client::CountItem) .def("RemoveItem", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveItem) - .def("RemoveItem", (void(Lua_Client::*)(uint32,uint32))&Lua_Client::RemoveItem); + .def("RemoveItem", (void(Lua_Client::*)(uint32,uint32))&Lua_Client::RemoveItem) + .def("SetGMStatus", (void(Lua_Client::*)(int32))& Lua_Client::SetGMStatus); } luabind::scope lua_register_inventory_where() { diff --git a/zone/lua_client.h b/zone/lua_client.h index 51da4b9df..1d44640e7 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -370,6 +370,7 @@ public: int CountItem(uint32 item_id); void RemoveItem(uint32 item_id); void RemoveItem(uint32 item_id, uint32 quantity); + void SetGMStatus(uint32 newStatus); void SetPrimaryWeaponOrnamentation(uint32 model_id); void SetSecondaryWeaponOrnamentation(uint32 model_id); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 7613d624f..1fc989bf7 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -3420,10 +3420,9 @@ XS(XS_Client_SetGMStatus) { Perl_croak(aTHX_ "Usage: Client::SetGMStatus(THIS, int newStatus)"); // @categories Script Utility { Client *THIS; - uint32 accID = THIS->AccountID(); int newStatus = (int)SvIV(ST(1)); VALIDATE_THIS_IS_CLIENT; - THIS->SetAdminStatus(newStatus); + THIS->SetGMStatus(newStatus); THIS->UpdateAdmin(true); } XSRETURN_EMPTY; From 5d92d484a16158382cfdff83606ab7cc70ae0836 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Mon, 26 Jul 2021 22:20:13 -0400 Subject: [PATCH 130/624] Projectiles Update (#1468) Fixed spell projectiles whose angle was not being calculated correctly. Significantly improved all projectile timings. At least up to range of 300 should see more accurate timings for damage/effects occurring upon projectile impact. Will be noticed most significantly for all spell projectiles and for longer range archery. --- zone/special_attacks.cpp | 71 ++++++++++++++++++++++++++++++---------- zone/spell_effects.cpp | 49 +++++++++++++++++++-------- 2 files changed, 89 insertions(+), 31 deletions(-) diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index be3408c81..6c49948d5 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -929,11 +929,31 @@ bool Mob::TryProjectileAttack(Mob *other, const EQ::ItemData *item, EQ::skills:: if (slot < 0) return false; - float speed_mod = speed; - + float distance_mod = 0.0f; float distance = other->CalculateDistance(GetX(), GetY(), GetZ()); - float hit = - 1200.0f + (10 * distance / speed_mod); // Calcuation: 60 = Animation Lag, 1.8 = Speed modifier for speed of (4) + + /* + New Distance Mod constant (7/25/21 update), modifier is needed to adjust slower speeds to have correct impact times at short distances. + We use archery 4.0 speed as a baseline for the forumla. At speed 1.5 at 50 pct distance mod is needed, where as speed 4.0 there is no modifer. + Therefore, we derive out our modifer as follows. distance_mod = (speed - 4) * ((50 - 0)/(1.5-4)). The ratio there is -20.0f. distance_mod = (speed - 4) * -20.0f + For distances >125 we use different modifier, this was all meticulously tested by eye to get the best possible outcome for projectile impact times. Not perfect though. + */ + + if (distance <= 125.0f) { + if (speed != 4.0f) { //Standard functions will always be 4.0f for archery. + distance_mod = (speed - 4.0f) * -20.0f; + distance += distance * distance_mod / 100.0f; + } + } + else if (distance > 125.0f && distance <= 200.0f) + distance = 3.14f * (distance / 2.0f); //Get distance of arc to better reflect projectile path length + + else if (distance > 200.0f) { + distance = distance * 1.30f; //Add 30% to base distance if over 200 range to tighten up hit timing. + distance = 3.14f * (distance / 2.0f); //Get distance of arc to better reflect projectile path length + } + + float hit = 1200.0f + (10 * distance / speed); ProjectileAtk[slot].increment = 1; ProjectileAtk[slot].hit_increment = static_cast(hit); // This projected hit time if target does NOT MOVE @@ -951,14 +971,12 @@ bool Mob::TryProjectileAttack(Mob *other, const EQ::ItemData *item, EQ::skills:: ProjectileAtk[slot].ammo_slot = 0; ProjectileAtk[slot].skill = skillInUse; - ProjectileAtk[slot].speed_mod = speed_mod; + ProjectileAtk[slot].speed_mod = speed; SetProjectileAttack(true); if (item) SendItemAnimation(other, item, skillInUse, speed); - // else if (IsNPC()) - // ProjectileAnimation(other, 0,false,speed,0,0,0,CastToNPC()->GetAmmoIDfile(),skillInUse); return true; } @@ -978,23 +996,40 @@ void Mob::ProjectileAttack() disable = false; Mob *target = entity_list.GetMobID(ProjectileAtk[i].target_id); - if (target && target->IsMoving()) { // Only recalculate hit increment if target moving - // Due to frequency that we need to check increment the targets position variables may not be - // updated even if moving. Do a simple check before calculating distance. + if (target && target->IsMoving()) { + /* + Only recalculate hit increment if target is moving. + Due to frequency that we need to check increment the targets position variables may not be + updated even if moving. Do a simple check before calculating distance. + */ if (ProjectileAtk[i].tlast_x != target->GetX() || ProjectileAtk[i].tlast_y != target->GetY()) { + ProjectileAtk[i].tlast_x = target->GetX(); ProjectileAtk[i].tlast_y = target->GetY(); - float distance = target->CalculateDistance( - ProjectileAtk[i].origin_x, ProjectileAtk[i].origin_y, ProjectileAtk[i].origin_z); - float hit = 1200.0f + (10 * distance / ProjectileAtk[i].speed_mod); // Calcuation: 60 = - // Animation Lag, 1.8 = - // Speed modifier for speed - // of (4) + + //Recalculate from the original location the projectile was fired in relation to the current targets location. + float distance = target->CalculateDistance(ProjectileAtk[i].origin_x, ProjectileAtk[i].origin_y, ProjectileAtk[i].origin_z); + float distance_mod = 0.0f; + + if (distance <= 125.0f) { + distance_mod = (ProjectileAtk[i].speed_mod - 4.0f) * -20.0f; + distance += distance * distance_mod / 100.0f; + } + else if (distance > 125.0f && distance <= 200.0f) + distance = 3.14f * (distance / 2.0f); //Get distance of arc to better reflect projectile path length + + else if (distance > 200.0f) { + distance = distance * 1.30f; //Add 30% to base distance if over 200 range to tighten up hit timing. + distance = 3.14f * (distance / 2.0f); //Get distance of arc to better reflect projectile path length + } + + float hit = 1200.0f + (10 * distance / ProjectileAtk[i].speed_mod); + ProjectileAtk[i].hit_increment = static_cast(hit); } } - // We hit I guess? + // Check if we hit. if (ProjectileAtk[i].hit_increment <= ProjectileAtk[i].increment) { if (target) { if (IsNPC()) { @@ -1496,7 +1531,7 @@ void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, f speed = 4.0; } if(!angle) { - angle = CalculateHeadingToTarget(to->GetX(), to->GetY()) * 2; + angle = CalculateHeadingToTarget(to->GetX(), to->GetY()); } if(!tilt) { tilt = 125; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index e5e447b5b..d20df5ace 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -6995,7 +6995,7 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama return false; } -bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed){ +bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed) { /*For mage 'Bolt' line and other various spells. -This is mostly accurate for how the modern clients handle this effect. @@ -7020,7 +7020,7 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed){ //Make sure there is an avialable bolt to be cast. for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { - if (ProjectileAtk[i].target_id == 0){ + if (ProjectileAtk[i].target_id == 0) { slot = i; break; } @@ -7029,11 +7029,35 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed){ if (slot < 0) return false; + float arc = 0.0f; + float distance_mod = 0.0f; + if (CheckLosFN(spell_target)) { - float speed_mod = speed; //Constant for adjusting speeds to match calculated impact time. float distance = spell_target->CalculateDistance(GetX(), GetY(), GetZ()); - float hit = 1200.0f + (10 * distance / speed_mod); + + /* + New Distance Mod constant (7/25/21 update), modifier is needed to adjust slower speeds to have correct impact times at short distances. + We use archery 4.0 speed as a baseline for the forumla. At speed 1.5 at 50 pct distance mod is needed, where as speed 4.0 there is no modifer. + Therefore, we derive out our modifer as follows. distance_mod = (speed - 4) * ((50 - 0)/(1.5-4)). The ratio there is -20.0f. distance_mod = (speed - 4) * -20.0f + For distances >125 we use different modifier, this was all meticulously tested by eye to get the best possible outcome for projectile impact times. Not perfect though. + */ + + if (distance <= 125.0f) { + distance_mod = (speed - 4.0f) * -20.0f; + distance += distance * distance_mod / 100.0f; + } + else if (distance > 125.0f && distance <= 200.0f) + distance = 3.14f * (distance / 2.0f); //Get distance of arc to better reflect projectile path length + + else if (distance > 200.0f) { + arc = 50.0f - ((distance - 200.0f) * 0.266f); //Arc angle gets drastically larger if >200 distance, lets lower it down gradually for better effect. + arc = std::max(arc, 20.0f); //No lower than 20 arc + distance = distance * 1.30f; //Add 30% to base distance if over 200 range to tighten up hit timing. + distance = 3.14f * (distance / 2.0f); //Get distance of arc to better reflect projectile path length + } + + float hit = 1200.0f + (10 * distance / speed); ProjectileAtk[slot].increment = 1; ProjectileAtk[slot].hit_increment = static_cast(hit); //This projected hit time if target does NOT MOVE @@ -7043,34 +7067,33 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed){ ProjectileAtk[slot].origin_y = GetY(); ProjectileAtk[slot].origin_z = GetZ(); ProjectileAtk[slot].skill = EQ::skills::SkillConjuration; - ProjectileAtk[slot].speed_mod = speed_mod; + ProjectileAtk[slot].speed_mod = speed; SetProjectileAttack(true); } //This will use the correct graphic as defined in the player_1 field of spells_new table. Found in UF+ spell files. if (RuleB(Spells, UseLiveSpellProjectileGFX)) { - ProjectileAnimation(spell_target,0, false, speed,0,0,0, spells[spell_id].player_1); + ProjectileAnimation(spell_target, 0, false, speed, 0.0f, 0.0f, arc, spells[spell_id].player_1); } - //This allows limited support for server using older spell files that do not contain data for bolt graphics. else { //Only use fire graphic for fire spells. if (spells[spell_id].resisttype == RESIST_FIRE) { - if (IsClient()){ + if (IsClient()) { if (CastToClient()->ClientVersionBit() <= 4) //Titanium needs alternate graphic. - ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_Titanium)), false, speed); + ProjectileAnimation(spell_target, (RuleI(Spells, FRProjectileItem_Titanium)), false, speed, 0.0f, 0.0f, arc); else - ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_SOF)), false, speed); - } + ProjectileAnimation(spell_target, (RuleI(Spells, FRProjectileItem_SOF)), false, speed, 0.0f, 0.0f, arc); + } else - ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_NPC)), false, speed); + ProjectileAnimation(spell_target, (RuleI(Spells, FRProjectileItem_NPC)), false, speed, 0.0f, 0.0f, arc); } //Default to an arrow if not using a mage bolt (Use up to date spell file and enable above rules for best results) else - ProjectileAnimation(spell_target,0, 1, speed); + ProjectileAnimation(spell_target, 0, 1, speed, 0.0f, 0.0f, arc); } if (spells[spell_id].CastingAnim == 64) From 7e85224202e313e0de876d8458ab32ab29daaa18 Mon Sep 17 00:00:00 2001 From: Gangsta <48196367+GangstaEQ@users.noreply.github.com> Date: Mon, 26 Jul 2021 23:03:17 -0700 Subject: [PATCH 131/624] [Merchants] Fix issue where an item purchased with 1 charges actually is bought with 0 charges Co-authored-by: ProducerZekServer --- zone/client_packet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 92b3080c7..41d66705c 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -12997,7 +12997,7 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) int16 charges = 0; if (item->Stackable || tmpmer_used) charges = mp->quantity; - else if ( item->MaxCharges > 1) + else if ( item->MaxCharges >= 1) charges = item->MaxCharges; EQ::ItemInstance* inst = database.CreateItem(item, charges); From fee8772bb6ecfa949f2f88458a433c8191f4073b Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 29 Jul 2021 18:55:06 -0400 Subject: [PATCH 132/624] Implemented SPA 482 SE_Skill_Base_Damage_Mod (#1474) * Implemented SPA 482 SE_Skill_Base_Damage_Mod Implemented SPA 482 SE_Skill_Base_Damage_Mod Modifies base melee damage by skill Base: pct Limit: skill(-1=ALL), max: none * Update spell_effects.cpp fix to remove unknown spa message --- common/spdat.h | 2 +- zone/bonuses.cpp | 36 ++++++++++++++++++++++++++++++++++++ zone/common.h | 1 + zone/mob.cpp | 3 +++ zone/spell_effects.cpp | 1 + 5 files changed, 42 insertions(+), 1 deletion(-) diff --git a/common/spdat.h b/common/spdat.h index 4dbbc7bbb..aaa2dcc6e 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -825,7 +825,7 @@ typedef enum { //#define SE_Ff_Value_Min 479 // //#define SE_Ff_Value_Max 480 // #define SE_Fc_Cast_Spell_On_Land 481 // Implemented - [FOCUS] Spells cast on target with this Focus Effect will have chance to cause a Spell to be cast if limits met. -//#define SE_Skill_Base_Damage_Mod 482 // +#define SE_Skill_Base_Damage_Mod 482 // implemented, @OffBonus, modify base melee damage by percent, base: pct, limit: skill(-1=ALL), max: none #define SE_Fc_Spell_Damage_Pct_IncomingPC 483 // Implemented - [FOCUS] modifies incoming spell damage by percent #define SE_Fc_Spell_Damage_Amt_IncomingPC 484 // Implemented - [FOCUS] modifies incoming spell damage by flat amount. Typically adds damage to incoming spells. #define SE_Ff_CasterClass 485 // Implemented - [FOCUS LIMIT] Caster of spell on target with a focus effect that is checked by incoming spells must be specified class. diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 3f067aee9..437468dcb 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1177,6 +1177,17 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) break; } + case SE_Skill_Base_Damage_Mod: { + // Bad data or unsupported new skill + if (base2 > EQ::skills::HIGHEST_SKILL) + break; + if (base2 == ALL_SKILLS) + newbon->DamageModifier3[EQ::skills::HIGHEST_SKILL + 1] += base1; + else + newbon->DamageModifier3[base2] += base1; + break; + } + case SE_SlayUndead: { if (newbon->SlayUndead[1] < base1) newbon->SlayUndead[0] = base1; // Rate @@ -2323,6 +2334,19 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne break; } + case SE_Skill_Base_Damage_Mod: + { + // Bad data or unsupported new skill + if (base2 > EQ::skills::HIGHEST_SKILL) + break; + int skill = base2 == ALL_SKILLS ? EQ::skills::HIGHEST_SKILL + 1 : base2; + if (effect_value < 0 && new_bonus->DamageModifier3[skill] > effect_value) + new_bonus->DamageModifier3[skill] = effect_value; + else if (effect_value > 0 && new_bonus->DamageModifier3[skill] < effect_value) + new_bonus->DamageModifier3[skill] = effect_value; + break; + } + case SE_MinDamageModifier: { // Bad data or unsupported new skill @@ -4242,6 +4266,18 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) break; } + case SE_Skill_Base_Damage_Mod: + { + for (int e = 0; e < EQ::skills::HIGHEST_SKILL + 1; e++) + { + spellbonuses.DamageModifier3[e] = effect_value; + aabonuses.DamageModifier3[e] = effect_value; + itembonuses.DamageModifier3[e] = effect_value; + } + break; + } + + case SE_MinDamageModifier: { for (int e = 0; e < EQ::skills::HIGHEST_SKILL + 1; e++) diff --git a/zone/common.h b/zone/common.h index 1acb335de..16d762915 100644 --- a/zone/common.h +++ b/zone/common.h @@ -442,6 +442,7 @@ struct StatBonuses { int32 HitChanceEffect[EQ::skills::HIGHEST_SKILL + 2]; //Spell effect Chance to Hit, straight percent increase int32 DamageModifier[EQ::skills::HIGHEST_SKILL + 2]; //i int32 DamageModifier2[EQ::skills::HIGHEST_SKILL + 2]; //i + int32 DamageModifier3[EQ::skills::HIGHEST_SKILL + 2]; //i int32 MinDamageModifier[EQ::skills::HIGHEST_SKILL + 2]; //i int32 ProcChance; // ProcChance/10 == % increase i = CombatEffects int32 ProcChanceSPA; // ProcChance from spell effects diff --git a/zone/mob.cpp b/zone/mob.cpp index e26c5406c..9ae5be42f 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4856,6 +4856,9 @@ int16 Mob::GetMeleeDamageMod_SE(uint16 skill) dmg_mod += itembonuses.DamageModifier2[EQ::skills::HIGHEST_SKILL + 1] + spellbonuses.DamageModifier2[EQ::skills::HIGHEST_SKILL + 1] + aabonuses.DamageModifier2[EQ::skills::HIGHEST_SKILL + 1] + itembonuses.DamageModifier2[skill] + spellbonuses.DamageModifier2[skill] + aabonuses.DamageModifier2[skill]; + dmg_mod += itembonuses.DamageModifier3[EQ::skills::HIGHEST_SKILL + 1] + spellbonuses.DamageModifier3[EQ::skills::HIGHEST_SKILL + 1] + aabonuses.DamageModifier3[EQ::skills::HIGHEST_SKILL + 1] + + itembonuses.DamageModifier3[skill] + spellbonuses.DamageModifier3[skill] + aabonuses.DamageModifier3[skill]; + if(dmg_mod < -100) dmg_mod = -100; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index d20df5ace..a5c61fb5d 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3214,6 +3214,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_Fc_Cast_Spell_On_Land: case SE_Ff_CasterClass: case SE_Ff_Same_Caster: + case SE_Skill_Base_Damage_Mod: { break; } From a50663e0a4d5bec43731ad39da6647c67081b0dc Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 29 Jul 2021 18:56:02 -0400 Subject: [PATCH 133/624] Fix SE_TwinCastBlocker to block twinproc (#1476) Improvement to SE_TwinCastBlocker implementation. Will now ensure both spell casted and weapon proced twincasts can be effectively blocked. --- zone/mob.cpp | 3 --- zone/spell_effects.cpp | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index 9ae5be42f..ce966726c 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3702,9 +3702,6 @@ void Mob::TryTwincast(Mob *caster, Mob *target, uint32 spell_id) if(!IsValidSpell(spell_id)) return; - if (IsEffectInSpell(spell_id, SE_TwinCastBlocker)) - return; - if(IsClient()) { int32 focus = CastToClient()->GetFocusEffect(focusTwincast, spell_id); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index a5c61fb5d..eb75c7892 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -4812,7 +4812,7 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) break; case SE_FcTwincast: - if (type == focusTwincast) + if (type == focusTwincast && !IsEffectInSpell(spell_id, SE_TwinCastBlocker)) value = base1; break; @@ -5382,7 +5382,7 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo break; case SE_FcTwincast: - if (type == focusTwincast) + if (type == focusTwincast && !IsEffectInSpell(spell_id, SE_TwinCastBlocker)) value = focus_spell.base[i]; break; From 78b15a0214a0e9dcb21bb2a7c4a2cccadc044715 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 29 Jul 2021 19:19:35 -0400 Subject: [PATCH 134/624] Implemented SPA 498 and 499 (extra attack chance effects) (#1475) * Implemented SPA 498 and 499 Implemented SE_AddExtraAttackPct_1h_Primary 498 , gives your double attacks a percent chance to perform an extra attack with 1-handed primary weapon, base: chance, limit: amt attacks max: none SE_AddExtraAttackPct_1h_Secondary 499 gives your double attacks a percent chance to perform an extra attack with 1-handed secondary weapon, base: chance, limit: amt attacks max: none Added limit functionality to similar effect SPA 266 SPA 266 will now be calculated to take highest percent value when applying bonus. (was additive, which does not seem correct based on AA data) * Update attack.cpp code update * Update bonuses.cpp code update * Update spdat.h added commas * Update spell_effects.cpp fix to remove unknown spa message Co-authored-by: Michael Cook (mackal) --- common/spdat.h | 6 +-- zone/attack.cpp | 39 ++++++++++++++--- zone/bonuses.cpp | 89 +++++++++++++++++++++++++++++++++++---- zone/common.h | 4 +- zone/lua_stat_bonuses.cpp | 2 +- zone/merc.cpp | 2 +- zone/spell_effects.cpp | 2 + 7 files changed, 124 insertions(+), 20 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index aaa2dcc6e..c41c22347 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -609,7 +609,7 @@ typedef enum { #define SE_TradeSkillMastery 263 // implemented - lets you raise more than one tradeskill above master. #define SE_HastenedAASkill 264 // implemented #define SE_MasteryofPast 265 // implemented[AA] - Spells less than effect values level can not be fizzled -#define SE_ExtraAttackChance 266 // implemented - increase chance to score an extra attack with a 2-Handed Weapon. +#define SE_ExtraAttackChance 266 // implemented, @OffBonus, gives your double attacks a percent chance to perform an extra attack with 2-handed primary weapon, base: chance, limit: amt attacks, max: none #define SE_AddPetCommand 267 // implemented - sets command base2 to base1 #define SE_ReduceTradeskillFail 268 // implemented - reduces chance to fail with given tradeskill by a percent chance #define SE_MaxBindWound 269 // implemented[AA] - Increase max HP you can bind wound. @@ -841,8 +841,8 @@ typedef enum { //#define SE_Ff_DurationMax 495 // #define SE_Critical_Melee_Damage_Mod_Max 496 // implemented - increase or decrease by percent critical damage (not stackable) //#define SE_Ff_FocusCastProcNoBypass 497 // -//#define SE_AddExtraAttackPct_1h_Primary 498 // -//#define SE_AddExtraAttackPct_1h_Secondary 499 // +#define SE_AddExtraAttackPct_1h_Primary 498 // implemented, @OffBonus, gives your double attacks a percent chance to perform an extra attack with 1-handed primary weapon, base: chance, limit: amt attacks, max: none +#define SE_AddExtraAttackPct_1h_Secondary 499 //implemented, @OffBonus, gives your double attacks a percent chance to perform an extra attack with 1-handed secondary weapon, base: chance, limit: amt attacks, max: none //#define SE_Fc_CastTimeMod2 500 // //#define SE_Fc_CastTimeAmt 501 // #define SE_Fearstun 502 // implemented - Stun with a max level limit. Normal stun restrictions don't apply. diff --git a/zone/attack.cpp b/zone/attack.cpp index d09271c55..aa6c30bd4 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -5483,13 +5483,40 @@ void Client::DoAttackRounds(Mob *target, int hand, bool IsFromSpell) CheckIncreaseSkill(EQ::skills::SkillDoubleAttack, target, -10); if (CheckDoubleAttack()) { Attack(target, hand, false, false, IsFromSpell); - - // Modern AA description: Increases your chance of ... performing one additional hit with a 2-handed weapon when double attacking by 2%. + if (hand == EQ::invslot::slotPrimary) { - auto extraattackchance = aabonuses.ExtraAttackChance + spellbonuses.ExtraAttackChance + - itembonuses.ExtraAttackChance; - if (extraattackchance && HasTwoHanderEquipped() && zone->random.Roll(extraattackchance)) - Attack(target, hand, false, false, IsFromSpell); + + if (HasTwoHanderEquipped()) { + auto extraattackchance = aabonuses.ExtraAttackChance[0] + spellbonuses.ExtraAttackChance[0] + + itembonuses.ExtraAttackChance[0]; + if (extraattackchance && zone->random.Roll(extraattackchance)) { + auto extraattackamt = std::max({ aabonuses.ExtraAttackChance[1], spellbonuses.ExtraAttackChance[1], itembonuses.ExtraAttackChance[1] }); + for (int i = 0; i < extraattackamt; i++) { + Attack(target, hand, false, false, IsFromSpell); + } + } + } + else { + auto extraattackchance_primary = aabonuses.ExtraAttackChancePrimary[0] + spellbonuses.ExtraAttackChancePrimary[0] + + itembonuses.ExtraAttackChancePrimary[0]; + if (extraattackchance_primary && zone->random.Roll(extraattackchance_primary)) { + auto extraattackamt_primary = std::max({ aabonuses.ExtraAttackChancePrimary[1], spellbonuses.ExtraAttackChancePrimary[1], itembonuses.ExtraAttackChancePrimary[1] }); + for (int i = 0; i < extraattackamt_primary; i++) { + Attack(target, hand, false, false, IsFromSpell); + } + } + } + } + + if (hand == EQ::invslot::slotSecondary) { + auto extraattackchance_secondary = aabonuses.ExtraAttackChanceSecondary[0] + spellbonuses.ExtraAttackChanceSecondary[0] + + itembonuses.ExtraAttackChanceSecondary[0]; + if (extraattackchance_secondary && zone->random.Roll(extraattackchance_secondary)) { + auto extraattackamt_secondary = std::max({ aabonuses.ExtraAttackChanceSecondary[1], spellbonuses.ExtraAttackChanceSecondary[1], itembonuses.ExtraAttackChanceSecondary[1] }); + for (int i = 0; i < extraattackamt_secondary; i++) { + Attack(target, hand, false, false, IsFromSpell); + } + } } // you can only triple from the main hand diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 437468dcb..e52e61533 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -860,9 +860,6 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) case SE_MaxBindWound: newbon->MaxBindWound += base1; break; - case SE_ExtraAttackChance: - newbon->ExtraAttackChance += base1; - break; case SE_SeeInvis: newbon->SeeInvis = base1; break; @@ -984,7 +981,6 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) case SE_BlockBehind: newbon->BlockBehind += base1; break; - case SE_StrikeThrough: case SE_StrikeThrough2: newbon->StrikeThrough += base1; @@ -1572,6 +1568,34 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) newbon->Pet_Add_Atk += base1; break; + case SE_ExtraAttackChance: + { + if (newbon->ExtraAttackChance[0] < base1) { + newbon->ExtraAttackChance[0] = base1; + newbon->ExtraAttackChance[1] = base2 ? base2 : 1; + } + break; + } + + case SE_AddExtraAttackPct_1h_Primary: + { + if (newbon->ExtraAttackChancePrimary[0] < base1) { + newbon->ExtraAttackChancePrimary[0] = base1; + newbon->ExtraAttackChancePrimary[1] = base2 ? base2 : 1; + } + break; + } + + case SE_AddExtraAttackPct_1h_Secondary: + { + + if (newbon->ExtraAttackChanceSecondary[0] < base1) { + newbon->ExtraAttackChanceSecondary[0] = base1; + newbon->ExtraAttackChanceSecondary[1] = base2 ? base2 : 1; + } + break; + } + // to do case SE_PetDiscipline: break; @@ -2382,8 +2406,45 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne } case SE_ExtraAttackChance: - new_bonus->ExtraAttackChance += effect_value; + { + if (AdditiveWornBonus) { + new_bonus->ExtraAttackChance[0] += effect_value; + new_bonus->ExtraAttackChance[1] = base2 ? base2 : 1; + } + if (new_bonus->ExtraAttackChance[0] < effect_value) { + new_bonus->ExtraAttackChance[0] = effect_value; + new_bonus->ExtraAttackChance[1] = base2 ? base2 : 1; + } break; + } + + case SE_AddExtraAttackPct_1h_Primary: + { + if (AdditiveWornBonus) { + new_bonus->ExtraAttackChancePrimary[0] += effect_value; + new_bonus->ExtraAttackChancePrimary[1] = base2 ? base2 : 1; + } + + if (new_bonus->ExtraAttackChancePrimary[0] < effect_value) { + new_bonus->ExtraAttackChancePrimary[0] = effect_value; + new_bonus->ExtraAttackChancePrimary[1] = base2 ? base2 : 1; + } + break; + } + + case SE_AddExtraAttackPct_1h_Secondary: + { + if (AdditiveWornBonus) { + new_bonus->ExtraAttackChanceSecondary[0] += effect_value; + new_bonus->ExtraAttackChanceSecondary[1] = base2 ? base2 : 1; + } + + if (new_bonus->ExtraAttackChanceSecondary[0] < effect_value) { + new_bonus->ExtraAttackChanceSecondary[0] = effect_value; + new_bonus->ExtraAttackChanceSecondary[1] = base2 ? base2 : 1; + } + break; + } case SE_PercentXPIncrease: { @@ -4302,9 +4363,21 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) break; case SE_ExtraAttackChance: - spellbonuses.ExtraAttackChance = effect_value; - aabonuses.ExtraAttackChance = effect_value; - itembonuses.ExtraAttackChance = effect_value; + spellbonuses.ExtraAttackChance[0] = effect_value; + aabonuses.ExtraAttackChance[0] = effect_value; + itembonuses.ExtraAttackChance[0] = effect_value; + break; + + case SE_AddExtraAttackPct_1h_Primary: + spellbonuses.ExtraAttackChancePrimary[0] = effect_value; + aabonuses.ExtraAttackChancePrimary[0] = effect_value; + itembonuses.ExtraAttackChancePrimary[0] = effect_value; + break; + + case SE_AddExtraAttackPct_1h_Secondary: + spellbonuses.ExtraAttackChanceSecondary[0] = effect_value; + aabonuses.ExtraAttackChanceSecondary[0] = effect_value; + itembonuses.ExtraAttackChanceSecondary[0] = effect_value; break; case SE_PercentXPIncrease: diff --git a/zone/common.h b/zone/common.h index 16d762915..e2aa19af5 100644 --- a/zone/common.h +++ b/zone/common.h @@ -446,7 +446,9 @@ struct StatBonuses { int32 MinDamageModifier[EQ::skills::HIGHEST_SKILL + 2]; //i int32 ProcChance; // ProcChance/10 == % increase i = CombatEffects int32 ProcChanceSPA; // ProcChance from spell effects - int32 ExtraAttackChance; + int32 ExtraAttackChance[2]; // base chance(w/ 2H weapon)=0, amt of extra attacks=1 + int32 ExtraAttackChancePrimary[2]; // base chance=0, , amt of extra attacks=1 + int32 ExtraAttackChanceSecondary[2]; // base chance=0, , amt of extra attacks=1 int32 DoTShielding; int32 DivineSaveChance[2]; // Second Chance (base1 = chance, base2 = spell on trigger) uint32 DeathSave[4]; // Death Pact [0](value = 1 partial 2 = full) [1]=slot [2]=LvLimit [3]=HealAmt diff --git a/zone/lua_stat_bonuses.cpp b/zone/lua_stat_bonuses.cpp index b6561199b..c007e09ca 100644 --- a/zone/lua_stat_bonuses.cpp +++ b/zone/lua_stat_bonuses.cpp @@ -572,7 +572,7 @@ int32 Lua_StatBonuses::GetProcChanceSPA() const { int32 Lua_StatBonuses::GetExtraAttackChance() const { Lua_Safe_Call_Int(); - return self->ExtraAttackChance; + return self->ExtraAttackChance[0]; } int32 Lua_StatBonuses::GetDoTShielding() const { diff --git a/zone/merc.cpp b/zone/merc.cpp index 3b8631edd..62b02c8a0 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -1624,7 +1624,7 @@ void Merc::AI_Process() { } } - int16 ExtraAttackChanceBonus = spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance + aabonuses.ExtraAttackChance; + int16 ExtraAttackChanceBonus = spellbonuses.ExtraAttackChance[0] + itembonuses.ExtraAttackChance[0] + aabonuses.ExtraAttackChance[0]; if (GetTarget() && ExtraAttackChanceBonus) { if(zone->random.Roll(ExtraAttackChanceBonus)) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index eb75c7892..f39340876 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3214,6 +3214,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_Fc_Cast_Spell_On_Land: case SE_Ff_CasterClass: case SE_Ff_Same_Caster: + case SE_AddExtraAttackPct_1h_Primary: + case SE_AddExtraAttackPct_1h_Secondary: case SE_Skill_Base_Damage_Mod: { break; From 187d6e9dc435c5113655bbabd3bd02782401a384 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 29 Jul 2021 19:20:21 -0400 Subject: [PATCH 135/624] [Spells] Bug fix for AOE Harmony/Lull (#1472) * Bug fix for AOE Harmony/Lull type spells. Fixed bug with SPA 30 SE_SE_ChangeFrenzyRad and SPA 86 SE_Harmony allowing those spells to affect NPC's above their level restrictions when cast as a 'Targeted AE' spell (ie. Harmony, Wake of Tranquility) when the targeted NPC was bellow level restricted range, but the NPC's next to them were above it. As coded now, the adjacent NPC's if over level limit will still get the buff applied to them BUT will not get any benefits from the buff. This bug was originally reported by: Isaaru * Live like behavior Implemented the live like behavior, if this case occurs on live, the buff is not applied to the targets over the level limit and "Your target looks unaffected" message is given. * code optimization code optimization --- zone/bonuses.cpp | 6 +++++- zone/mob.h | 1 + zone/spell_effects.cpp | 18 ++++++++++++++++++ zone/spells.cpp | 20 +++++++++----------- 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index e52e61533..6ae0075bd 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1750,7 +1750,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_ChangeFrenzyRad: { - // redundant to have level check here + if (max != 0 && GetLevel() > max) + break; + if(new_bonus->AggroRange == -1 || effect_value < new_bonus->AggroRange) { new_bonus->AggroRange = static_cast(effect_value); @@ -1760,6 +1762,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_Harmony: { + if (max != 0 && GetLevel() > max) + break; // Harmony effect as buff - kinda tricky // harmony could stack with a lull spell, which has better aggro range // take the one with less range in any case diff --git a/zone/mob.h b/zone/mob.h index 4e051d76f..ee3801833 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -837,6 +837,7 @@ public: inline int16 GetSpellPowerDistanceMod() const { return SpellPowerDistanceMod; }; inline void SetSpellPowerDistanceMod(int16 value) { SpellPowerDistanceMod = value; }; int32 GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot = 0); + bool HarmonySpellLevelCheck(int32 spell_id, Mob* target = nullptr); void CastSpellOnLand(Mob* caster, uint32 spell_id); void FocusProcLimitProcess(); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index f39340876..18cd28117 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -7441,3 +7441,21 @@ void Client::BreakFeignDeathWhenCastOn(bool IsResisted) MessageString(Chat::SpellFailure,FD_CAST_ON); } } + +bool Mob::HarmonySpellLevelCheck(int32 spell_id, Mob *target) +{ + //'this' = caster of spell + if (!target) { + return false; + } + + for (int i = 0; i < EFFECT_COUNT; i++) { + // not important to check limit on SE_Lull as it doesnt have one and if the other components won't land, then SE_Lull wont either + if (spells[spell_id].effectid[i] == SE_ChangeFrenzyRad || spells[spell_id].effectid[i] == SE_Harmony) { + if ((spells[spell_id].max[i] != 0 && target->GetLevel() > spells[spell_id].max[i]) || target->GetSpecialAbility(IMMUNE_PACIFY)) { + return false; + } + } + } + return true; +} diff --git a/zone/spells.cpp b/zone/spells.cpp index c246c79a8..9a2d6f27c 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -224,17 +224,9 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, if (spellbonuses.NegateIfCombat) BuffFadeByEffect(SE_NegateIfCombat); - if(IsClient() && GetTarget() && IsHarmonySpell(spell_id)) - { - for(int i = 0; i < EFFECT_COUNT; i++) { - // not important to check limit on SE_Lull as it doesnt have one and if the other components won't land, then SE_Lull wont either - if (spells[spell_id].effectid[i] == SE_ChangeFrenzyRad || spells[spell_id].effectid[i] == SE_Harmony) { - if((spells[spell_id].max[i] != 0 && GetTarget()->GetLevel() > spells[spell_id].max[i]) || GetTarget()->GetSpecialAbility(IMMUNE_PACIFY)) { - InterruptSpell(CANNOT_AFFECT_NPC, 0x121, spell_id); - return(false); - } - } - } + if (IsClient() && IsHarmonySpell(spell_id) && !HarmonySpellLevelCheck(spell_id, entity_list.GetMobID(target_id))) { + InterruptSpell(SPELL_NO_EFFECT, 0x121, spell_id); + return false; } if (HasActiveSong() && IsBardSong(spell_id)) { @@ -3789,6 +3781,12 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r return false; } } + //Need this to account for special AOE cases. + if (IsClient() && IsHarmonySpell(spell_id) && !HarmonySpellLevelCheck(spell_id, spelltar)) { + MessageString(Chat::SpellFailure, SPELL_NO_EFFECT); + return false; + } + // Block next spell effect should be used up first(since its blocking the next spell) if(CanBlockSpell()) { int buff_count = GetMaxTotalSlots(); From c3f8b8073b3ddb24269c78860be6d6f812271713 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 30 Jul 2021 12:09:44 -0400 Subject: [PATCH 136/624] Fix bots with ExtraAttackChance changes (#1480) This should probably be updated to match everything in client, but this will at least fix compile --- zone/bot.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 757d77b9a..0cb352a18 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3272,7 +3272,9 @@ void Bot::AI_Process() } TEST_COMBATANTS(); - int32 ExtraAttackChanceBonus = (spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance + aabonuses.ExtraAttackChance); + auto ExtraAttackChanceBonus = + (spellbonuses.ExtraAttackChance[0] + itembonuses.ExtraAttackChance[0] + + aabonuses.ExtraAttackChance[0]); if (ExtraAttackChanceBonus) { if (p_item && p_item->GetItem()->IsType2HWeapon()) { From 2d296eb317e2aee2ff24206f1e1a5478cdb19023 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 30 Jul 2021 12:47:22 -0400 Subject: [PATCH 137/624] [CI] Enable Bots (Typo) in Drone Config (#1481) --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index 749ab62c5..6b9e0425b 100644 --- a/.drone.yml +++ b/.drone.yml @@ -13,4 +13,4 @@ steps: image: akkadius/eqemu-server:latest commands: - sudo chown eqemu:eqemu /drone/src/ * -R - - git submodule init && git submodule update && mkdir -p build && cd build && cmake -DEQEMU_BUILD_LOGIN=ON -DEQEMU_BUILD_BOTS=ON -DEQEMU_BUILD_LUA=ON -G 'Unix Makefiles' .. && make -j$((`nproc`-4)) \ No newline at end of file + - git submodule init && git submodule update && mkdir -p build && cd build && cmake -DEQEMU_BUILD_LOGIN=ON -DEQEMU_ENABLE_BOTS=ON -DEQEMU_BUILD_LUA=ON -G 'Unix Makefiles' .. && make -j$((`nproc`-4)) From 72056ffba378ae75f79d5bbf5f1543c89cada1e7 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 1 Aug 2021 14:06:57 -0400 Subject: [PATCH 138/624] [Spells] Focus related functions to use int32 instead of int16 Need to increase from int16 to int32 when calculating focus due it causing issues with returning spell ids for some effects which can easily be over the int16 limit. CalcAAFocus and CalcFocusEffect were doing all math in int16 also, no reason not to increase to int32. --- zone/client.h | 4 +-- zone/merc.cpp | 26 ++++++++--------- zone/merc.h | 2 +- zone/mob.h | 8 ++--- zone/npc.h | 2 +- zone/spell_effects.cpp | 66 +++++++++++++++++++++--------------------- 6 files changed, 54 insertions(+), 54 deletions(-) diff --git a/zone/client.h b/zone/client.h index 0e73370c3..c20c9057c 100644 --- a/zone/client.h +++ b/zone/client.h @@ -889,7 +889,7 @@ public: void SendClearAA(); inline uint32 GetAAXP() const { return m_pp.expAA; } inline uint32 GetAAPercent() const { return m_epp.perAA; } - int16 CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id); + int32 CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id); void SetAATitle(const char *Title); void SetTitleSuffix(const char *txt); void MemorizeSpell(uint32 slot, uint32 spellid, uint32 scribing); @@ -1602,7 +1602,7 @@ protected: void MakeBuffFadePacket(uint16 spell_id, int slot_id, bool send_message = true); bool client_data_loaded; - int16 GetFocusEffect(focusType type, uint16 spell_id); + int32 GetFocusEffect(focusType type, uint16 spell_id); uint16 GetSympatheticFocusEffect(focusType type, uint16 spell_id); void FinishAlternateAdvancementPurchase(AA::Rank *rank, bool ignore_cost); diff --git a/zone/merc.cpp b/zone/merc.cpp index 62b02c8a0..a24809418 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -2552,11 +2552,11 @@ bool Merc::CheckAENuke(Merc* caster, Mob* tar, uint16 spell_id, uint8 &numTarget return false; } -int16 Merc::GetFocusEffect(focusType type, uint16 spell_id) { +int32 Merc::GetFocusEffect(focusType type, uint16 spell_id) { - int16 realTotal = 0; - int16 realTotal2 = 0; - int16 realTotal3 = 0; + int32 realTotal = 0; + int32 realTotal2 = 0; + int32 realTotal3 = 0; bool rand_effectiveness = false; //Improved Healing, Damage & Mana Reduction are handled differently in that some are random percentages @@ -2572,10 +2572,10 @@ int16 Merc::GetFocusEffect(focusType type, uint16 spell_id) { const EQ::ItemData* TempItem = nullptr; const EQ::ItemData* UsedItem = nullptr; - uint16 UsedFocusID = 0; - int16 Total = 0; - int16 focus_max = 0; - int16 focus_max_real = 0; + int32 UsedFocusID = 0; + int32 Total = 0; + int32 focus_max = 0; + int32 focus_max_real = 0; //item focus for (int x = EQ::invslot::EQUIPMENT_BEGIN; x <= EQ::invslot::EQUIPMENT_END; ++x) @@ -2623,14 +2623,14 @@ int16 Merc::GetFocusEffect(focusType type, uint16 spell_id) { if (spellbonuses.FocusEffects[type]){ //Spell Focus - int16 Total2 = 0; - int16 focus_max2 = 0; - int16 focus_max_real2 = 0; + int32 Total2 = 0; + int32 focus_max2 = 0; + int32 focus_max_real2 = 0; int buff_tracker = -1; int buff_slot = 0; - uint16 focusspellid = 0; - uint16 focusspell_tracker = 0; + int32 focusspellid = 0; + int32 focusspell_tracker = 0; uint32 buff_max = GetMaxTotalSlots(); for (buff_slot = 0; buff_slot < buff_max; buff_slot++) { focusspellid = buffs[buff_slot].spellid; diff --git a/zone/merc.h b/zone/merc.h index d8feeab4f..44ea7dda9 100644 --- a/zone/merc.h +++ b/zone/merc.h @@ -272,7 +272,7 @@ protected: void AddItemBonuses(const EQ::ItemData *item, StatBonuses* newbon); int CalcRecommendedLevelBonus(uint8 level, uint8 reclevel, int basestat); - int16 GetFocusEffect(focusType type, uint16 spell_id); + int32 GetFocusEffect(focusType type, uint16 spell_id); std::vector merc_spells; std::map timers; diff --git a/zone/mob.h b/zone/mob.h index ee3801833..aadeb6645 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -770,7 +770,7 @@ public: void QuestJournalledSay(Client *QuestInitiator, const char *str, Journal::Options &opts); int32 GetItemStat(uint32 itemid, const char *identifier); - int16 CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus=false, uint16 casterid=0); + int32 CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus=false, uint16 casterid=0); uint8 IsFocusEffect(uint16 spellid, int effect_index, bool AA=false,uint32 aa_effect=0); void SendIllusionPacket(uint16 in_race, uint8 in_gender = 0xFF, uint8 in_texture = 0xFF, uint8 in_helmtexture = 0xFF, uint8 in_haircolor = 0xFF, uint8 in_beardcolor = 0xFF, uint8 in_eyecolor1 = 0xFF, uint8 in_eyecolor2 = 0xFF, @@ -839,9 +839,9 @@ public: int32 GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot = 0); bool HarmonySpellLevelCheck(int32 spell_id, Mob* target = nullptr); - void CastSpellOnLand(Mob* caster, uint32 spell_id); + void CastSpellOnLand(Mob* caster, int32 spell_id); void FocusProcLimitProcess(); - bool ApplyFocusProcLimiter(uint32 spell_id, int buffslot = -1); + bool ApplyFocusProcLimiter(int32 spell_id, int buffslot = -1); void ModSkillDmgTaken(EQ::skills::SkillType skill_num, int value); int16 GetModSkillDmgTaken(const EQ::skills::SkillType skill_num); @@ -1383,7 +1383,7 @@ protected: virtual #endif int GetBaseSkillDamage(EQ::skills::SkillType skill, Mob *target = nullptr); - virtual int16 GetFocusEffect(focusType type, uint16 spell_id) { return 0; } + virtual int32 GetFocusEffect(focusType type, uint16 spell_id) { return 0; } void CalculateNewFearpoint(); float FindGroundZ(float new_x, float new_y, float z_offset=0.0); float FindDestGroundZ(glm::vec3 dest, float z_offset=0.0); diff --git a/zone/npc.h b/zone/npc.h index d75fe67f7..f28394b9b 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -544,7 +544,7 @@ protected: virtual bool AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes, bool bInnates = false); virtual bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0); AISpellsVar_Struct AISpellVar; - int16 GetFocusEffect(focusType type, uint16 spell_id); + int32 GetFocusEffect(focusType type, uint16 spell_id); uint16 innate_proc_spell_id; uint32 npc_spells_effects_id; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 18cd28117..d64af2f19 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -4421,11 +4421,11 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) CalcBonuses(); } -int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) +int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) { const SPDat_Spell_Struct &spell = spells[spell_id]; - int16 value = 0; + int32 value = 0; int lvlModifier = 100; int spell_level = 0; int lvldiff = 0; @@ -4927,7 +4927,7 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) //given an item/spell's focus ID and the spell being cast, determine the focus ammount, if any //assumes that spell_id is not a bard spell and that both ids are valid spell ids -int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus, uint16 casterid) +int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus, uint16 casterid) { /* 'this' is always the caster of the spell_id, most foci check for effects on the caster, however some check for effects on the target. @@ -4940,11 +4940,11 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo const SPDat_Spell_Struct &focus_spell = spells[focus_id]; const SPDat_Spell_Struct &spell = spells[spell_id]; - int16 value = 0; + int32 value = 0; int lvlModifier = 100; int spell_level = 0; int lvldiff = 0; - uint32 Caston_spell_id = 0; + int32 Caston_spell_id = 0; bool LimitInclude[MaxLimitInclude] = {false}; /* Certain limits require only one of several Include conditions to be true. Ie. Add damage to fire OR ice @@ -5625,7 +5625,7 @@ uint16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) { return 0; } -int16 Client::GetFocusEffect(focusType type, uint16 spell_id) +int32 Client::GetFocusEffect(focusType type, uint16 spell_id) { if (IsBardSong(spell_id) && type != focusFcBaseEffects && type != focusSpellDuration) return 0; @@ -5633,9 +5633,9 @@ int16 Client::GetFocusEffect(focusType type, uint16 spell_id) if (spells[spell_id].not_focusable) return 0; - int16 realTotal = 0; - int16 realTotal2 = 0; - int16 realTotal3 = 0; + int32 realTotal = 0; + int32 realTotal2 = 0; + int32 realTotal3 = 0; bool rand_effectiveness = false; //Improved Healing, Damage & Mana Reduction are handled differently in that some are random percentages @@ -5649,9 +5649,9 @@ int16 Client::GetFocusEffect(focusType type, uint16 spell_id) const EQ::ItemData* TempItem = nullptr; const EQ::ItemData* UsedItem = nullptr; uint16 UsedFocusID = 0; - int16 Total = 0; - int16 focus_max = 0; - int16 focus_max_real = 0; + int32 Total = 0; + int32 focus_max = 0; + int32 focus_max_real = 0; //item focus for (int x = EQ::invslot::EQUIPMENT_BEGIN; x <= EQ::invslot::EQUIPMENT_END; x++) @@ -5812,14 +5812,14 @@ int16 Client::GetFocusEffect(focusType type, uint16 spell_id) if (spellbonuses.FocusEffects[type]){ //Spell Focus - int16 Total2 = 0; - int16 focus_max2 = 0; - int16 focus_max_real2 = 0; + int32 Total2 = 0; + int32 focus_max2 = 0; + int32 focus_max_real2 = 0; int buff_tracker = -1; int buff_slot = 0; - uint16 focusspellid = 0; - uint16 focusspell_tracker = 0; + int32 focusspellid = 0; + int32 focusspell_tracker = 0; int buff_max = GetMaxTotalSlots(); for (buff_slot = 0; buff_slot < buff_max; buff_slot++) { focusspellid = buffs[buff_slot].spellid; @@ -5865,7 +5865,7 @@ int16 Client::GetFocusEffect(focusType type, uint16 spell_id) // AA Focus if (aabonuses.FocusEffects[type]){ - int16 Total3 = 0; + int32 Total3 = 0; for (const auto &aa : aa_ranks) { auto ability_rank = zone->GetAlternateAdvancementAbilityAndRank(aa.first, aa.second.first); @@ -5895,20 +5895,20 @@ int16 Client::GetFocusEffect(focusType type, uint16 spell_id) //by reagent conservation for obvious reasons. //Non-Live like feature to allow for an additive focus bonus to be applied from foci that are placed in worn slot. (No limit checks) - int16 worneffect_bonus = 0; + int32 worneffect_bonus = 0; if (RuleB(Spells, UseAdditiveFocusFromWornSlot)) worneffect_bonus = itembonuses.FocusEffectsWorn[type]; return realTotal + realTotal2 + realTotal3 + worneffect_bonus; } -int16 NPC::GetFocusEffect(focusType type, uint16 spell_id) { +int32 NPC::GetFocusEffect(focusType type, uint16 spell_id) { if (spells[spell_id].not_focusable) return 0; - int16 realTotal = 0; - int16 realTotal2 = 0; + int32 realTotal = 0; + int32 realTotal2 = 0; bool rand_effectiveness = false; //Improved Healing, Damage & Mana Reduction are handled differently in that some are random percentages @@ -5921,9 +5921,9 @@ int16 NPC::GetFocusEffect(focusType type, uint16 spell_id) { const EQ::ItemData* TempItem = nullptr; const EQ::ItemData* UsedItem = nullptr; uint16 UsedFocusID = 0; - int16 Total = 0; - int16 focus_max = 0; - int16 focus_max_real = 0; + int32 Total = 0; + int32 focus_max = 0; + int32 focus_max_real = 0; //item focus for (int i = EQ::invslot::EQUIPMENT_BEGIN; i <= EQ::invslot::EQUIPMENT_END; i++){ @@ -5969,14 +5969,14 @@ int16 NPC::GetFocusEffect(focusType type, uint16 spell_id) { if (RuleB(Spells, NPC_UseFocusFromSpells) && spellbonuses.FocusEffects[type]){ //Spell Focus - int16 Total2 = 0; - int16 focus_max2 = 0; - int16 focus_max_real2 = 0; + int32 Total2 = 0; + int32 focus_max2 = 0; + int32 focus_max_real2 = 0; int buff_tracker = -1; int buff_slot = 0; - uint16 focusspellid = 0; - uint16 focusspell_tracker = 0; + int32 focusspellid = 0; + int32 focusspell_tracker = 0; int buff_max = GetMaxTotalSlots(); for (buff_slot = 0; buff_slot < buff_max; buff_slot++) { focusspellid = buffs[buff_slot].spellid; @@ -7179,7 +7179,7 @@ void Mob::TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker){ } } -void Mob::CastSpellOnLand(Mob* caster, uint32 spell_id) +void Mob::CastSpellOnLand(Mob* caster, int32 spell_id) { /* This function checks for incoming spells on a mob, if they meet the criteria for focus SE_Fc_Cast_Spell_on_Land then @@ -7193,7 +7193,7 @@ void Mob::CastSpellOnLand(Mob* caster, uint32 spell_id) if (!caster) return; - uint32 trigger_spell_id = 0; + int32 trigger_spell_id = 0; //Step 1: Check this focus effect exists on the mob. if (spellbonuses.FocusEffects[focusFcCastSpellOnLand]) { @@ -7230,7 +7230,7 @@ void Mob::CastSpellOnLand(Mob* caster, uint32 spell_id) } } -bool Mob::ApplyFocusProcLimiter(uint32 spell_id, int buffslot) +bool Mob::ApplyFocusProcLimiter(int32 spell_id, int buffslot) { if (buffslot < 0) return false; From 38beb804a39a9b67838fdf65bde50ec536d1da57 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 1 Aug 2021 14:13:14 -0400 Subject: [PATCH 139/624] [Spells] Added constant labeling to all StatBonuses that use as arrays. (#1485) * constexpr labels added * more updates * more updates * completed * Update common.h * Namespace constants, few minor spelling tweaks Co-authored-by: Akkadius --- zone/attack.cpp | 210 +++++++------- zone/bonuses.cpp | 588 +++++++++++++++++++-------------------- zone/client_mods.cpp | 12 +- zone/common.h | 58 +++- zone/merc.cpp | 14 +- zone/mob.cpp | 22 +- zone/mob_ai.cpp | 6 +- zone/special_attacks.cpp | 48 ++-- zone/spell_effects.cpp | 68 ++--- zone/spells.cpp | 28 +- 10 files changed, 554 insertions(+), 500 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index aa6c30bd4..83bbf225c 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -87,7 +87,7 @@ EQ::skills::SkillType Mob::AttackAnimation(int Hand, const EQ::ItemInstance* wea break; case EQ::item::ItemType2HBlunt: // 2H Blunt skillinuse = EQ::skills::Skill2HBlunt; - type = RuleB(Combat, Classic2HBAnimation) ? anim2HWeapon : anim2HSlashing; + type = RuleB(Combat, Classic2HBAnimation) ? anim2HWeapon : anim2HSlashing; break; case EQ::item::ItemType2HPiercing: // 2H Piercing if (IsClient() && CastToClient()->ClientVersion() < EQ::versions::ClientVersion::RoF2) @@ -265,7 +265,7 @@ int Mob::GetTotalDefense() auto evasion_bonus = spellbonuses.AvoidMeleeChanceEffect; // we check this first since it has a special case if (evasion_bonus >= 10000) return -1; - + // 515 SE_AC_Avoidance_Max_Percent auto ac_aviodance_bonus = itembonuses.AC_Avoidance_Max_Percent + aabonuses.AC_Avoidance_Max_Percent + spellbonuses.AC_Avoidance_Max_Percent; if (ac_aviodance_bonus) @@ -870,7 +870,7 @@ int Mob::GetBestMeleeSkill() { EQ::skills::Skill1HBlunt, EQ::skills::Skill1HSlashing, EQ::skills::Skill2HBlunt, - EQ::skills::Skill2HSlashing, + EQ::skills::Skill2HSlashing, EQ::skills::SkillHandtoHand, EQ::skills::Skill1HPiercing, EQ::skills::Skill2HPiercing, @@ -1547,12 +1547,12 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b /////////////////////////////////////////////////////////// ////// Send Attack Damage /////////////////////////////////////////////////////////// - if (my_hit.damage_done > 0 && aabonuses.SkillAttackProc[0] && aabonuses.SkillAttackProc[1] == my_hit.skill && - IsValidSpell(aabonuses.SkillAttackProc[2])) { - float chance = aabonuses.SkillAttackProc[0] / 1000.0f; + if (my_hit.damage_done > 0 && aabonuses.SkillAttackProc[SBIndex::SKILLPROC_CHANCE] && aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SKILL] == my_hit.skill && + IsValidSpell(aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID])) { + float chance = aabonuses.SkillAttackProc[SBIndex::SKILLPROC_CHANCE] / 1000.0f; if (zone->random.Roll(chance)) - SpellFinished(aabonuses.SkillAttackProc[2], other, EQ::spells::CastingSlot::Item, 0, -1, - spells[aabonuses.SkillAttackProc[2]].ResistDiff); + SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID], other, EQ::spells::CastingSlot::Item, 0, -1, + spells[aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID]].ResistDiff); } other->Damage(this, my_hit.damage_done, SPELL_UNKNOWN, my_hit.skill, true, -1, false, m_specialattacks); @@ -2746,9 +2746,9 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b if (damage > GetHP()) damage = GetHP(); - if (spellbonuses.ImprovedTaunt[1] && (GetLevel() < spellbonuses.ImprovedTaunt[0]) - && other && (buffs[spellbonuses.ImprovedTaunt[2]].casterid != other->GetID())) - hate = (hate*spellbonuses.ImprovedTaunt[1]) / 100; + if (spellbonuses.ImprovedTaunt[SBIndex::IMPROVED_TAUNT_AGGRO_MOD] && (GetLevel() < spellbonuses.ImprovedTaunt[SBIndex::IMPROVED_TAUNT_MAX_LVL]) + && other && (buffs[spellbonuses.ImprovedTaunt[SBIndex::IMPROVED_TAUNT_BUFFSLOT]].casterid != other->GetID())) + hate = (hate*spellbonuses.ImprovedTaunt[SBIndex::IMPROVED_TAUNT_AGGRO_MOD]) / 100; hate_list.AddEntToHateList(other, hate, damage, bFrenzy, !iBuffTic); @@ -2806,7 +2806,7 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b // owner must get on list, but he's not actually gained any hate yet if ( !owner->GetSpecialAbility(IMMUNE_AGGRO) && - !(this->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && owner->IsClient()) && + !(this->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && owner->IsClient()) && !(this->GetSpecialAbility(IMMUNE_AGGRO_NPC) && owner->IsNPC()) ) { if (owner->IsClient() && !CheckAggro(owner)) @@ -2818,9 +2818,9 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b if (mypet && !mypet->IsHeld() && !mypet->IsPetStop()) { // I have a pet, add other to it if ( - !mypet->IsFamiliar() && + !mypet->IsFamiliar() && !mypet->GetSpecialAbility(IMMUNE_AGGRO) && - !(mypet->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && this->IsClient()) && + !(mypet->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && this->IsClient()) && !(mypet->GetSpecialAbility(IMMUNE_AGGRO_NPC) && this->IsNPC()) ) { mypet->hate_list.AddEntToHateList(other, 0, 0, bFrenzy); @@ -2828,9 +2828,9 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b } else if (myowner) { // I am a pet, add other to owner if it's NPC/LD if ( - myowner->IsAIControlled() && + myowner->IsAIControlled() && !myowner->GetSpecialAbility(IMMUNE_AGGRO) && - !(this->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && myowner->IsClient()) && + !(this->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && myowner->IsClient()) && !(this->GetSpecialAbility(IMMUNE_AGGRO_NPC) && myowner->IsNPC()) ) { myowner->hate_list.AddEntToHateList(other, 0, 0, bFrenzy); @@ -2907,7 +2907,7 @@ void Mob::DamageShield(Mob* attacker, bool spell_ds) { int ds_mitigation = attacker->itembonuses.DSMitigation; // Subtract mitigations because DS_Mitigation_Percentage is a negative value when reducing total, thus final value will be positive ds_mitigation -= attacker->aabonuses.DS_Mitigation_Percentage + attacker->itembonuses.DS_Mitigation_Percentage + attacker->spellbonuses.DS_Mitigation_Percentage; //Negative value to reduce - + DS -= DS * ds_mitigation / 100; } attacker->Damage(this, -DS, spellid, EQ::skills::SkillAbjuration/*hackish*/, false); @@ -3122,8 +3122,8 @@ int32 Mob::ReduceDamage(int32 damage) int32 slot = -1; bool DisableMeleeRune = false; - if (spellbonuses.NegateAttacks[0]) { - slot = spellbonuses.NegateAttacks[1]; + if (spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_EXISTS]) { + slot = spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_BUFFSLOT]; if (slot >= 0) { if (--buffs[slot].numhits == 0) { @@ -3131,21 +3131,21 @@ int32 Mob::ReduceDamage(int32 damage) BuffFadeBySlot(slot, true); } - if (spellbonuses.NegateAttacks[2] && (damage > spellbonuses.NegateAttacks[2])) - damage -= spellbonuses.NegateAttacks[2]; + if (spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_MAX_DMG_ABSORB_PER_HIT] && (damage > spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_MAX_DMG_ABSORB_PER_HIT])) + damage -= spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_MAX_DMG_ABSORB_PER_HIT]; else return DMG_RUNE; } } //Only mitigate if damage is above the minimium specified. - if (spellbonuses.MeleeThresholdGuard[0]) { - slot = spellbonuses.MeleeThresholdGuard[1]; + if (spellbonuses.MeleeThresholdGuard[SBIndex::THRESHOLDGUARD_MITIGATION_PERCENT]) { + slot = spellbonuses.MeleeThresholdGuard[SBIndex::THRESHOLDGUARD_BUFFSLOT]; - if (slot >= 0 && (damage > spellbonuses.MeleeThresholdGuard[2])) + if (slot >= 0 && (damage > spellbonuses.MeleeThresholdGuard[SBIndex::THRESHOLDGUARD_MIN_DMG_TO_TRIGGER])) { DisableMeleeRune = true; - int damage_to_reduce = damage * spellbonuses.MeleeThresholdGuard[0] / 100; + int damage_to_reduce = damage * spellbonuses.MeleeThresholdGuard[SBIndex::THRESHOLDGUARD_MITIGATION_PERCENT] / 100; if (damage_to_reduce >= buffs[slot].melee_rune) { LogSpells("Mob::ReduceDamage SE_MeleeThresholdGuard [{}] damage negated, [{}] damage remaining, fading buff", damage_to_reduce, buffs[slot].melee_rune); @@ -3162,16 +3162,16 @@ int32 Mob::ReduceDamage(int32 damage) } } - if (spellbonuses.MitigateMeleeRune[0] && !DisableMeleeRune) { - slot = spellbonuses.MitigateMeleeRune[1]; + if (spellbonuses.MitigateMeleeRune[SBIndex::MITIGATION_RUNE_PERCENT] && !DisableMeleeRune) { + slot = spellbonuses.MitigateMeleeRune[SBIndex::MITIGATION_RUNE_BUFFSLOT]; if (slot >= 0) { - int damage_to_reduce = damage * spellbonuses.MitigateMeleeRune[0] / 100; + int damage_to_reduce = damage * spellbonuses.MitigateMeleeRune[SBIndex::MITIGATION_RUNE_PERCENT] / 100; - if (spellbonuses.MitigateMeleeRune[2] && (damage_to_reduce > spellbonuses.MitigateMeleeRune[2])) - damage_to_reduce = spellbonuses.MitigateMeleeRune[2]; + if (spellbonuses.MitigateMeleeRune[SBIndex::MITIGATION_RUNE_MAX_DMG_ABSORB_PER_HIT] && (damage_to_reduce > spellbonuses.MitigateMeleeRune[SBIndex::MITIGATION_RUNE_MAX_DMG_ABSORB_PER_HIT])) + damage_to_reduce = spellbonuses.MitigateMeleeRune[SBIndex::MITIGATION_RUNE_MAX_DMG_ABSORB_PER_HIT]; - if (spellbonuses.MitigateMeleeRune[3] && (damage_to_reduce >= buffs[slot].melee_rune)) + if (spellbonuses.MitigateMeleeRune[SBIndex::MITIGATION_RUNE_MAX_HP_AMT] && (damage_to_reduce >= buffs[slot].melee_rune)) { LogSpells("Mob::ReduceDamage SE_MitigateMeleeDamage [{}] damage negated, [{}] damage remaining, fading buff", damage_to_reduce, buffs[slot].melee_rune); damage -= buffs[slot].melee_rune; @@ -3182,7 +3182,7 @@ int32 Mob::ReduceDamage(int32 damage) { LogSpells("Mob::ReduceDamage SE_MitigateMeleeDamage [{}] damage negated, [{}] damage remaining", damage_to_reduce, buffs[slot].melee_rune); - if (spellbonuses.MitigateMeleeRune[3]) + if (spellbonuses.MitigateMeleeRune[SBIndex::MITIGATION_RUNE_MAX_HP_AMT]) buffs[slot].melee_rune = (buffs[slot].melee_rune - damage_to_reduce); damage -= damage_to_reduce; @@ -3193,7 +3193,7 @@ int32 Mob::ReduceDamage(int32 damage) if (damage < 1) return DMG_RUNE; - if (spellbonuses.MeleeRune[0] && spellbonuses.MeleeRune[1] >= 0) + if (spellbonuses.MeleeRune[SBIndex::RUNE_AMOUNT] && spellbonuses.MeleeRune[SBIndex::POSITIONAL_DAMAGE_MOD] >= 0) damage = RuneAbsorb(damage, SE_Rune); if (damage < 1) @@ -3211,8 +3211,8 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi int32 slot = -1; // See if we block the spell outright first - if (!iBuffTic && spellbonuses.NegateAttacks[0]) { - slot = spellbonuses.NegateAttacks[1]; + if (!iBuffTic && spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_EXISTS]) { + slot = spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_BUFFSLOT]; if (slot >= 0) { if (--buffs[slot].numhits == 0) { @@ -3220,8 +3220,8 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi BuffFadeBySlot(slot, true); } - if (spellbonuses.NegateAttacks[2] && (damage > spellbonuses.NegateAttacks[2])) - damage -= spellbonuses.NegateAttacks[2]; + if (spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_MAX_DMG_ABSORB_PER_HIT] && (damage > spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_MAX_DMG_ABSORB_PER_HIT])) + damage -= spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_MAX_DMG_ABSORB_PER_HIT]; else return 0; } @@ -3231,16 +3231,16 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi if (iBuffTic) { damage -= (damage * itembonuses.DoTShielding / 100); - if (spellbonuses.MitigateDotRune[0]) { - slot = spellbonuses.MitigateDotRune[1]; + if (spellbonuses.MitigateDotRune[SBIndex::MITIGATION_RUNE_PERCENT]) { + slot = spellbonuses.MitigateDotRune[SBIndex::MITIGATION_RUNE_BUFFSLOT]; if (slot >= 0) { - int damage_to_reduce = damage * spellbonuses.MitigateDotRune[0] / 100; + int damage_to_reduce = damage * spellbonuses.MitigateDotRune[SBIndex::MITIGATION_RUNE_PERCENT] / 100; - if (spellbonuses.MitigateDotRune[2] && (damage_to_reduce > spellbonuses.MitigateDotRune[2])) - damage_to_reduce = spellbonuses.MitigateDotRune[2]; + if (spellbonuses.MitigateDotRune[SBIndex::MITIGATION_RUNE_MAX_DMG_ABSORB_PER_HIT] && (damage_to_reduce > spellbonuses.MitigateDotRune[SBIndex::MITIGATION_RUNE_MAX_DMG_ABSORB_PER_HIT])) + damage_to_reduce = spellbonuses.MitigateDotRune[SBIndex::MITIGATION_RUNE_MAX_DMG_ABSORB_PER_HIT]; - if (spellbonuses.MitigateDotRune[3] && (damage_to_reduce >= buffs[slot].dot_rune)) + if (spellbonuses.MitigateDotRune[SBIndex::MITIGATION_RUNE_MAX_HP_AMT] && (damage_to_reduce >= buffs[slot].dot_rune)) { damage -= buffs[slot].dot_rune; if (!TryFadeEffect(slot)) @@ -3248,7 +3248,7 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi } else { - if (spellbonuses.MitigateDotRune[3]) + if (spellbonuses.MitigateDotRune[SBIndex::MITIGATION_RUNE_MAX_HP_AMT]) buffs[slot].dot_rune = (buffs[slot].dot_rune - damage_to_reduce); damage -= damage_to_reduce; @@ -3264,13 +3264,13 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi damage -= (damage * itembonuses.SpellShield / 100); //Only mitigate if damage is above the minimium specified. - if (spellbonuses.SpellThresholdGuard[0]) { - slot = spellbonuses.SpellThresholdGuard[1]; + if (spellbonuses.SpellThresholdGuard[SBIndex::THRESHOLDGUARD_MITIGATION_PERCENT]) { + slot = spellbonuses.SpellThresholdGuard[SBIndex::THRESHOLDGUARD_BUFFSLOT]; - if (slot >= 0 && (damage > spellbonuses.MeleeThresholdGuard[2])) + if (slot >= 0 && (damage > spellbonuses.MeleeThresholdGuard[SBIndex::THRESHOLDGUARD_MIN_DMG_TO_TRIGGER])) { DisableSpellRune = true; - int damage_to_reduce = damage * spellbonuses.SpellThresholdGuard[0] / 100; + int damage_to_reduce = damage * spellbonuses.SpellThresholdGuard[SBIndex::THRESHOLDGUARD_MITIGATION_PERCENT] / 100; if (damage_to_reduce >= buffs[slot].magic_rune) { damage -= buffs[slot].magic_rune; @@ -3286,16 +3286,16 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi } // Do runes now. - if (spellbonuses.MitigateSpellRune[0] && !DisableSpellRune) { - slot = spellbonuses.MitigateSpellRune[1]; + if (spellbonuses.MitigateSpellRune[SBIndex::MITIGATION_RUNE_PERCENT] && !DisableSpellRune) { + slot = spellbonuses.MitigateSpellRune[SBIndex::MITIGATION_RUNE_BUFFSLOT]; if (slot >= 0) { - int damage_to_reduce = damage * spellbonuses.MitigateSpellRune[0] / 100; + int damage_to_reduce = damage * spellbonuses.MitigateSpellRune[SBIndex::MITIGATION_RUNE_PERCENT] / 100; - if (spellbonuses.MitigateSpellRune[2] && (damage_to_reduce > spellbonuses.MitigateSpellRune[2])) - damage_to_reduce = spellbonuses.MitigateSpellRune[2]; + if (spellbonuses.MitigateSpellRune[SBIndex::MITIGATION_RUNE_MAX_DMG_ABSORB_PER_HIT] && (damage_to_reduce > spellbonuses.MitigateSpellRune[SBIndex::MITIGATION_RUNE_MAX_DMG_ABSORB_PER_HIT])) + damage_to_reduce = spellbonuses.MitigateSpellRune[SBIndex::MITIGATION_RUNE_MAX_DMG_ABSORB_PER_HIT]; - if (spellbonuses.MitigateSpellRune[3] && (damage_to_reduce >= buffs[slot].magic_rune)) + if (spellbonuses.MitigateSpellRune[SBIndex::MITIGATION_RUNE_MAX_HP_AMT] && (damage_to_reduce >= buffs[slot].magic_rune)) { LogSpells("Mob::ReduceDamage SE_MitigateSpellDamage [{}] damage negated, [{}] damage remaining, fading buff", damage_to_reduce, buffs[slot].magic_rune); damage -= buffs[slot].magic_rune; @@ -3306,7 +3306,7 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi { LogSpells("Mob::ReduceDamage SE_MitigateMeleeDamage [{}] damage negated, [{}] damage remaining", damage_to_reduce, buffs[slot].magic_rune); - if (spellbonuses.MitigateSpellRune[3]) + if (spellbonuses.MitigateSpellRune[SBIndex::MITIGATION_RUNE_MAX_HP_AMT]) buffs[slot].magic_rune = (buffs[slot].magic_rune - damage_to_reduce); damage -= damage_to_reduce; @@ -3318,10 +3318,10 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi return 0; //Regular runes absorb spell damage (except dots) - Confirmed on live. - if (spellbonuses.MeleeRune[0] && spellbonuses.MeleeRune[1] >= 0) + if (spellbonuses.MeleeRune[SBIndex::RUNE_AMOUNT] && spellbonuses.MeleeRune[SBIndex::POSITIONAL_DAMAGE_MOD] >= 0) damage = RuneAbsorb(damage, SE_Rune); - if (spellbonuses.AbsorbMagicAtt[0] && spellbonuses.AbsorbMagicAtt[1] >= 0) + if (spellbonuses.AbsorbMagicAtt[SBIndex::RUNE_AMOUNT] && spellbonuses.AbsorbMagicAtt[SBIndex::POSITIONAL_DAMAGE_MOD] >= 0) damage = RuneAbsorb(damage, SE_AbsorbMagicAtt); if (damage < 1) @@ -3344,9 +3344,9 @@ int32 Mob::ReduceAllDamage(int32 damage) } } - if (spellbonuses.EnduranceAbsorbPercentDamage[0]) { - int32 damage_reduced = damage * spellbonuses.EnduranceAbsorbPercentDamage[0] / 10000; //If hit for 1000, at 10% then lower damage by 100; - int32 endurance_drain = damage_reduced * spellbonuses.EnduranceAbsorbPercentDamage[1] / 10000; //Reduce endurance by 0.05% per HP loss + if (spellbonuses.EnduranceAbsorbPercentDamage[SBIndex::ENDURANCE_ABSORD_MITIGIATION]) { + int32 damage_reduced = damage * spellbonuses.EnduranceAbsorbPercentDamage[SBIndex::ENDURANCE_ABSORD_MITIGIATION] / 10000; //If hit for 1000, at 10% then lower damage by 100; + int32 endurance_drain = damage_reduced * spellbonuses.EnduranceAbsorbPercentDamage[SBIndex::ENDURANCE_ABSORD_DRAIN_PER_HP] / 10000; //Reduce endurance by 0.05% per HP loss if (endurance_drain < 1) endurance_drain = 1; @@ -3529,7 +3529,7 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const // emote goes with every one ... even npcs entity_list.MessageClose(this, true, RuleI(Range, SpellMessages), Chat::Emote, "%s beams a smile at %s", attacker->GetCleanName(), this->GetCleanName()); } - + // If a client pet is damaged while sitting, stand, fix sit button, // and remove sitting regen. Removes bug where client clicks sit // during battle and gains pet hp-regen and bugs the sit button. @@ -3542,7 +3542,7 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const // fix GUI sit button to be unpressed and stop sitting regen owner->CastToClient()->SetPetCommandState(PET_BUTTON_SIT, 0); SetAppearance(eaStanding); - } + } } } //end `if there is some damage being done and theres anattacker person involved` @@ -3552,16 +3552,16 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const // pets that have Hold and no Focus will add NPCs if they're engaged // pets that have Hold and Focus will not add NPCs if ( - pet && - !pet->IsFamiliar() && - !pet->GetSpecialAbility(IMMUNE_AGGRO) && - !pet->IsEngaged() && - attacker && - !(pet->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && attacker->IsClient()) && - !(pet->GetSpecialAbility(IMMUNE_AGGRO_NPC) && attacker->IsNPC()) && - attacker != this && - !attacker->IsCorpse() && - !pet->IsGHeld() && + pet && + !pet->IsFamiliar() && + !pet->GetSpecialAbility(IMMUNE_AGGRO) && + !pet->IsEngaged() && + attacker && + !(pet->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && attacker->IsClient()) && + !(pet->GetSpecialAbility(IMMUNE_AGGRO_NPC) && attacker->IsNPC()) && + attacker != this && + !attacker->IsCorpse() && + !pet->IsGHeld() && !attacker->IsTrap() ) { if (!pet->IsHeld()) { @@ -4384,12 +4384,12 @@ void Mob::TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions * // 1: Try Slay Undead if (defender->GetBodyType() == BT_Undead || defender->GetBodyType() == BT_SummonedUndead || defender->GetBodyType() == BT_Vampire) { - int SlayRateBonus = aabonuses.SlayUndead[0] + itembonuses.SlayUndead[0] + spellbonuses.SlayUndead[0]; + int SlayRateBonus = aabonuses.SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] + itembonuses.SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] + spellbonuses.SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD]; if (SlayRateBonus) { float slayChance = static_cast(SlayRateBonus) / 10000.0f; if (zone->random.Roll(slayChance)) { int SlayDmgBonus = std::max( - { aabonuses.SlayUndead[1], itembonuses.SlayUndead[1], spellbonuses.SlayUndead[1] }); + {aabonuses.SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD], itembonuses.SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD], spellbonuses.SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] }); hit.damage_done = std::max(hit.damage_done, hit.base_damage) + 5; hit.damage_done = (hit.damage_done * SlayDmgBonus) / 100; @@ -4560,18 +4560,18 @@ bool Mob::TryFinishingBlow(Mob *defender, int &damage) if (defender && !defender->IsClient() && defender->GetHPRatio() < 10) { uint32 FB_Dmg = - aabonuses.FinishingBlow[1] + spellbonuses.FinishingBlow[1] + itembonuses.FinishingBlow[1]; + aabonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] + spellbonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] + itembonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_DMG]; uint32 FB_Level = 0; - FB_Level = aabonuses.FinishingBlowLvl[0]; - if (FB_Level < spellbonuses.FinishingBlowLvl[0]) - FB_Level = spellbonuses.FinishingBlowLvl[0]; - else if (FB_Level < itembonuses.FinishingBlowLvl[0]) - FB_Level = itembonuses.FinishingBlowLvl[0]; + FB_Level = aabonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX]; + if (FB_Level < spellbonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX]) + FB_Level = spellbonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX]; + else if (FB_Level < itembonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX]) + FB_Level = itembonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX]; // modern AA description says rank 1 (500) is 50% chance int ProcChance = - aabonuses.FinishingBlow[0] + spellbonuses.FinishingBlow[0] + spellbonuses.FinishingBlow[0]; + aabonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE] + spellbonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE] + spellbonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE]; if (FB_Level && FB_Dmg && (defender->GetLevel() <= FB_Level) && (ProcChance >= zone->random.Int(1, 1000))) { @@ -4622,8 +4622,8 @@ void Mob::DoRiposte(Mob *defender) return; } - DoubleRipChance = defender->aabonuses.GiveDoubleRiposte[0] + defender->spellbonuses.GiveDoubleRiposte[0] + - defender->itembonuses.GiveDoubleRiposte[0]; + DoubleRipChance = defender->aabonuses.GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_CHANCE] + defender->spellbonuses.GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_CHANCE] + + defender->itembonuses.GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_CHANCE]; // Live AA - Double Riposte if (DoubleRipChance && zone->random.Roll(DoubleRipChance)) { @@ -4636,15 +4636,15 @@ void Mob::DoRiposte(Mob *defender) // Double Riposte effect, allows for a chance to do RIPOSTE with a skill specific special attack (ie Return Kick). // Coded narrowly: Limit to one per client. Limit AA only. [1 = Skill Attack Chance, 2 = Skill] - DoubleRipChance = defender->aabonuses.GiveDoubleRiposte[1]; + DoubleRipChance = defender->aabonuses.GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_SKILL_ATK_CHANCE]; if (DoubleRipChance && zone->random.Roll(DoubleRipChance)) { LogCombat("Preforming a return SPECIAL ATTACK ([{}] percent chance)", DoubleRipChance); if (defender->GetClass() == MONK) - defender->MonkSpecialAttack(this, defender->aabonuses.GiveDoubleRiposte[2]); + defender->MonkSpecialAttack(this, defender->aabonuses.GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_SKILL]); else if (defender->IsClient()) // so yeah, even if you don't have the skill you can still do the attack :P (and we don't crash anymore) - defender->CastToClient()->DoClassAttacks(this, defender->aabonuses.GiveDoubleRiposte[2], true); + defender->CastToClient()->DoClassAttacks(this, defender->aabonuses.GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_SKILL], true); } } @@ -5067,13 +5067,13 @@ bool Mob::TryRootFadeByDamage(int buffslot, Mob* attacker) { - Root break chance values obtained from live parses. */ - if (!attacker || !spellbonuses.Root[0] || spellbonuses.Root[1] < 0) + if (!attacker || !spellbonuses.Root[SBIndex::ROOT_EXISTS] || spellbonuses.Root[SBIndex::ROOT_BUFFSLOT] < 0) return false; - if (IsDetrimentalSpell(spellbonuses.Root[1]) && spellbonuses.Root[1] != buffslot) { + if (IsDetrimentalSpell(spellbonuses.Root[SBIndex::ROOT_BUFFSLOT]) && spellbonuses.Root[SBIndex::ROOT_BUFFSLOT] != buffslot) { int BreakChance = RuleI(Spells, RootBreakFromSpells); - BreakChance -= BreakChance*buffs[spellbonuses.Root[1]].RootBreakChance / 100; + BreakChance -= BreakChance * buffs[spellbonuses.Root[SBIndex::ROOT_BUFFSLOT]].RootBreakChance / 100; int level_diff = attacker->GetLevel() - GetLevel(); //Use baseline if level difference <= 1 (ie. If target is (1) level less than you, or equal or greater level) @@ -5092,8 +5092,8 @@ bool Mob::TryRootFadeByDamage(int buffslot, Mob* attacker) { if (zone->random.Roll(BreakChance)) { - if (!TryFadeEffect(spellbonuses.Root[1])) { - BuffFadeBySlot(spellbonuses.Root[1]); + if (!TryFadeEffect(spellbonuses.Root[SBIndex::ROOT_BUFFSLOT])) { + BuffFadeBySlot(spellbonuses.Root[SBIndex::ROOT_BUFFSLOT]); LogCombat("Spell broke root! BreakChance percent chance"); return true; } @@ -5109,7 +5109,7 @@ int32 Mob::RuneAbsorb(int32 damage, uint16 type) uint32 buff_max = GetMaxTotalSlots(); if (type == SE_Rune) { for (uint32 slot = 0; slot < buff_max; slot++) { - if (slot == spellbonuses.MeleeRune[1] && spellbonuses.MeleeRune[0] && buffs[slot].melee_rune && IsValidSpell(buffs[slot].spellid)) { + if (slot == spellbonuses.MeleeRune[SBIndex::POSITIONAL_DAMAGE_MOD] && spellbonuses.MeleeRune[SBIndex::RUNE_AMOUNT] && buffs[slot].melee_rune && IsValidSpell(buffs[slot].spellid)) { int melee_rune_left = buffs[slot].melee_rune; if (melee_rune_left > damage) @@ -5133,7 +5133,7 @@ int32 Mob::RuneAbsorb(int32 damage, uint16 type) else { for (uint32 slot = 0; slot < buff_max; slot++) { - if (slot == spellbonuses.AbsorbMagicAtt[1] && spellbonuses.AbsorbMagicAtt[0] && buffs[slot].magic_rune && IsValidSpell(buffs[slot].spellid)) { + if (slot == spellbonuses.AbsorbMagicAtt[SBIndex::POSITIONAL_DAMAGE_MOD] && spellbonuses.AbsorbMagicAtt[SBIndex::RUNE_AMOUNT] && buffs[slot].magic_rune && IsValidSpell(buffs[slot].spellid)) { int magic_rune_left = buffs[slot].magic_rune; if (magic_rune_left > damage) { @@ -5255,7 +5255,7 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac spec_mod = mod; if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) { //SE_PC_Pet_Rampage SPA 464 on pet, damage modifier - int spell_mod = spellbonuses.PC_Pet_Rampage[1] + itembonuses.PC_Pet_Rampage[1] + aabonuses.PC_Pet_Rampage[1]; + int spell_mod = spellbonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] + itembonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] + aabonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD]; if (spell_mod > spec_mod) spec_mod = spell_mod; } @@ -5266,7 +5266,7 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac spec_mod = mod; if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) { //SE_PC_Pet_AE_Rampage SPA 465 on pet, damage modifier - int spell_mod = spellbonuses.PC_Pet_AE_Rampage[1] + itembonuses.PC_Pet_AE_Rampage[1] + aabonuses.PC_Pet_AE_Rampage[1]; + int spell_mod = spellbonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] + itembonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] + aabonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD]; if (spell_mod > spec_mod) spec_mod = spell_mod; } @@ -5483,24 +5483,24 @@ void Client::DoAttackRounds(Mob *target, int hand, bool IsFromSpell) CheckIncreaseSkill(EQ::skills::SkillDoubleAttack, target, -10); if (CheckDoubleAttack()) { Attack(target, hand, false, false, IsFromSpell); - + if (hand == EQ::invslot::slotPrimary) { if (HasTwoHanderEquipped()) { - auto extraattackchance = aabonuses.ExtraAttackChance[0] + spellbonuses.ExtraAttackChance[0] + - itembonuses.ExtraAttackChance[0]; + auto extraattackchance = aabonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] + spellbonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] + + itembonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE]; if (extraattackchance && zone->random.Roll(extraattackchance)) { - auto extraattackamt = std::max({ aabonuses.ExtraAttackChance[1], spellbonuses.ExtraAttackChance[1], itembonuses.ExtraAttackChance[1] }); + auto extraattackamt = std::max({aabonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_NUM_ATKS], spellbonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_NUM_ATKS], itembonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_NUM_ATKS] }); for (int i = 0; i < extraattackamt; i++) { Attack(target, hand, false, false, IsFromSpell); } } } else { - auto extraattackchance_primary = aabonuses.ExtraAttackChancePrimary[0] + spellbonuses.ExtraAttackChancePrimary[0] + - itembonuses.ExtraAttackChancePrimary[0]; + auto extraattackchance_primary = aabonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] + spellbonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] + + itembonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE]; if (extraattackchance_primary && zone->random.Roll(extraattackchance_primary)) { - auto extraattackamt_primary = std::max({ aabonuses.ExtraAttackChancePrimary[1], spellbonuses.ExtraAttackChancePrimary[1], itembonuses.ExtraAttackChancePrimary[1] }); + auto extraattackamt_primary = std::max({aabonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_NUM_ATKS], spellbonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_NUM_ATKS], itembonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_NUM_ATKS] }); for (int i = 0; i < extraattackamt_primary; i++) { Attack(target, hand, false, false, IsFromSpell); } @@ -5509,10 +5509,10 @@ void Client::DoAttackRounds(Mob *target, int hand, bool IsFromSpell) } if (hand == EQ::invslot::slotSecondary) { - auto extraattackchance_secondary = aabonuses.ExtraAttackChanceSecondary[0] + spellbonuses.ExtraAttackChanceSecondary[0] + - itembonuses.ExtraAttackChanceSecondary[0]; + auto extraattackchance_secondary = aabonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] + spellbonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] + + itembonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE]; if (extraattackchance_secondary && zone->random.Roll(extraattackchance_secondary)) { - auto extraattackamt_secondary = std::max({ aabonuses.ExtraAttackChanceSecondary[1], spellbonuses.ExtraAttackChanceSecondary[1], itembonuses.ExtraAttackChanceSecondary[1] }); + auto extraattackamt_secondary = std::max({aabonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_NUM_ATKS], spellbonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_NUM_ATKS], itembonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_NUM_ATKS] }); for (int i = 0; i < extraattackamt_secondary; i++) { Attack(target, hand, false, false, IsFromSpell); } diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 6ae0075bd..53952e6ea 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -48,7 +48,7 @@ void Mob::CalcBonuses() SetAttackTimer(); CalcAC(); - /* Fast walking NPC's are prone to disappear into walls/hills + /* Fast walking NPC's are prone to disappear into walls/hills We set this here because NPC's can cast spells to change walkspeed/runspeed */ float get_walk_speed = static_cast(0.025f * this->GetWalkspeed()); @@ -232,7 +232,7 @@ void Client::AddItemBonuses(const EQ::ItemInstance *inst, StatBonuses *newbon, b if (GetLevel() < inst->GetItemRequiredLevel(true)) { return; } - + // So there isn't a very nice way to get the real rec level from the aug's inst, so we just pass it in, only // used for augs auto rec_level = isAug ? rec_override : inst->GetItemRecommendedLevel(true); @@ -476,7 +476,7 @@ void Client::AddItemBonuses(const EQ::ItemInstance *inst, StatBonuses *newbon, b newbon->percussionMod = item->BardValue; break; } - + // Add Item Faction Mods if (item->FactionMod1) { if (item->FactionAmt1 > 0 && item->FactionAmt1 > GetItemFactionBonus(item->FactionMod1)) { @@ -1145,9 +1145,9 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) case SE_SkillAttackProc: { // You can only have one of these per client. [AA Dragon Punch] - newbon->SkillAttackProc[0] = base1; // Chance base 1000 = 100% proc rate - newbon->SkillAttackProc[1] = base2; // Skill to Proc Off - newbon->SkillAttackProc[2] = rank.spell; // spell to proc + newbon->SkillAttackProc[SBIndex::SKILLPROC_CHANCE] = base1; // Chance base 1000 = 100% proc rate + newbon->SkillAttackProc[SBIndex::SKILLPROC_SKILL] = base2; // Skill to Proc Off + newbon->SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID] = rank.spell; // spell to proc break; } @@ -1185,9 +1185,9 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) } case SE_SlayUndead: { - if (newbon->SlayUndead[1] < base1) - newbon->SlayUndead[0] = base1; // Rate - newbon->SlayUndead[1] = base2; // Damage Modifier + if (newbon->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] < base1) + newbon->SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] = base1; // Rate + newbon->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = base2; // Damage Modifier break; } @@ -1199,13 +1199,13 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) case SE_GiveDoubleRiposte: { // 0=Regular Riposte 1=Skill Attack Riposte 2=Skill if (base2 == 0) { - if (newbon->GiveDoubleRiposte[0] < base1) - newbon->GiveDoubleRiposte[0] = base1; + if (newbon->GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_CHANCE] < base1) + newbon->GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_CHANCE] = base1; } // Only for special attacks. - else if (base2 > 0 && (newbon->GiveDoubleRiposte[1] < base1)) { - newbon->GiveDoubleRiposte[1] = base1; - newbon->GiveDoubleRiposte[2] = base2; + else if (base2 > 0 && (newbon->GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_SKILL_ATK_CHANCE] < base1)) { + newbon->GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_SKILL_ATK_CHANCE] = base1; + newbon->GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_SKILL] = base2; } break; @@ -1217,7 +1217,7 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) if (base2 > EQ::skills::HIGHEST_SKILL) break; - if (newbon->RaiseSkillCap[base2] < base1) + if (newbon->RaiseSkillCap[base2] < base1) newbon->RaiseSkillCap[base2] = base1; break; } @@ -1239,9 +1239,9 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) } case SE_DivineSave: { - if (newbon->DivineSaveChance[0] < base1) { - newbon->DivineSaveChance[0] = base1; - newbon->DivineSaveChance[1] = base2; + if (newbon->DivineSaveChance[SBIndex::DIVINE_SAVE_CHANCE] < base1) { + newbon->DivineSaveChance[SBIndex::DIVINE_SAVE_CHANCE] = base1; + newbon->DivineSaveChance[SBIndex::DIVINE_SAVE_SPELL_TRIGGER_ID] = base2; } break; } @@ -1272,18 +1272,18 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) case SE_FinishingBlow: { // base1 = chance, base2 = damage - if (newbon->FinishingBlow[1] < base2) { - newbon->FinishingBlow[0] = base1; - newbon->FinishingBlow[1] = base2; + if (newbon->FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] < base2) { + newbon->FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = base1; + newbon->FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] = base2; } break; } case SE_FinishingBlowLvl: { // base1 = level, base2 = ??? (Set to 200 in AA data, possible proc rate mod?) - if (newbon->FinishingBlowLvl[0] < base1) { - newbon->FinishingBlowLvl[0] = base1; - newbon->FinishingBlowLvl[1] = base2; + if (newbon->FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] < base1) { + newbon->FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = base1; + newbon->FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = base2; } break; } @@ -1344,32 +1344,32 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) } case SE_HeadShot: { - if (newbon->HeadShot[1] < base2) { - newbon->HeadShot[0] = base1; - newbon->HeadShot[1] = base2; + if (newbon->HeadShot[SBIndex::FINISHING_EFFECT_DMG] < base2) { + newbon->HeadShot[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = base1; + newbon->HeadShot[SBIndex::FINISHING_EFFECT_DMG] = base2; } break; } case SE_HeadShotLevel: { - if (newbon->HSLevel[0] < base1) - newbon->HSLevel[0] = base1; - newbon->HSLevel[1] = base2; + if (newbon->HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] < base1) + newbon->HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = base1; + newbon->HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = base2; break; } case SE_Assassinate: { - if (newbon->Assassinate[1] < base2) { - newbon->Assassinate[0] = base1; - newbon->Assassinate[1] = base2; + if (newbon->Assassinate[SBIndex::FINISHING_EFFECT_DMG] < base2) { + newbon->Assassinate[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = base1; + newbon->Assassinate[SBIndex::FINISHING_EFFECT_DMG] = base2; } break; } case SE_AssassinateLevel: { - if (newbon->AssassinateLevel[0] < base1) { - newbon->AssassinateLevel[0] = base1; - newbon->AssassinateLevel[1] = base2; + if (newbon->AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] < base1) { + newbon->AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = base1; + newbon->AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = base2; } break; } @@ -1439,20 +1439,20 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) break; case SE_PC_Pet_Rampage: { - newbon->PC_Pet_Rampage[0] += base1; //Chance to rampage - if (newbon->PC_Pet_Rampage[1] < base2) - newbon->PC_Pet_Rampage[1] = base2; //Damage modifer - take highest + newbon->PC_Pet_Rampage[SBIndex::PET_RAMPAGE_CHANCE] += base1; //Chance to rampage + if (newbon->PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] < base2) + newbon->PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = base2; //Damage modifer - take highest break; } case SE_PC_Pet_AE_Rampage: { - newbon->PC_Pet_AE_Rampage[0] += base1; //Chance to rampage - if (newbon->PC_Pet_AE_Rampage[1] < base2) - newbon->PC_Pet_AE_Rampage[1] = base2; //Damage modifer - take highest + newbon->PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_CHANCE] += base1; //Chance to rampage + if (newbon->PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] < base2) + newbon->PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = base2; //Damage modifer - take highest break; } - case SE_PC_Pet_Flurry_Chance: + case SE_PC_Pet_Flurry_Chance: newbon->PC_Pet_Flurry += base1; //Chance to Flurry break; @@ -1570,18 +1570,18 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) case SE_ExtraAttackChance: { - if (newbon->ExtraAttackChance[0] < base1) { - newbon->ExtraAttackChance[0] = base1; - newbon->ExtraAttackChance[1] = base2 ? base2 : 1; + if (newbon->ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] < base1) { + newbon->ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] = base1; + newbon->ExtraAttackChance[SBIndex::EXTRA_ATTACK_NUM_ATKS] = base2 ? base2 : 1; } break; } case SE_AddExtraAttackPct_1h_Primary: { - if (newbon->ExtraAttackChancePrimary[0] < base1) { - newbon->ExtraAttackChancePrimary[0] = base1; - newbon->ExtraAttackChancePrimary[1] = base2 ? base2 : 1; + if (newbon->ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] < base1) { + newbon->ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] = base1; + newbon->ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_NUM_ATKS] = base2 ? base2 : 1; } break; } @@ -1589,9 +1589,9 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) case SE_AddExtraAttackPct_1h_Secondary: { - if (newbon->ExtraAttackChanceSecondary[0] < base1) { - newbon->ExtraAttackChanceSecondary[0] = base1; - newbon->ExtraAttackChanceSecondary[1] = base2 ? base2 : 1; + if (newbon->ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] < base1) { + newbon->ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] = base1; + newbon->ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_NUM_ATKS] = base2 ? base2 : 1; } break; } @@ -2412,12 +2412,12 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_ExtraAttackChance: { if (AdditiveWornBonus) { - new_bonus->ExtraAttackChance[0] += effect_value; - new_bonus->ExtraAttackChance[1] = base2 ? base2 : 1; + new_bonus->ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] += effect_value; + new_bonus->ExtraAttackChance[SBIndex::EXTRA_ATTACK_NUM_ATKS] = base2 ? base2 : 1; } - if (new_bonus->ExtraAttackChance[0] < effect_value) { - new_bonus->ExtraAttackChance[0] = effect_value; - new_bonus->ExtraAttackChance[1] = base2 ? base2 : 1; + if (new_bonus->ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] < effect_value) { + new_bonus->ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; + new_bonus->ExtraAttackChance[SBIndex::EXTRA_ATTACK_NUM_ATKS] = base2 ? base2 : 1; } break; } @@ -2425,13 +2425,13 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_AddExtraAttackPct_1h_Primary: { if (AdditiveWornBonus) { - new_bonus->ExtraAttackChancePrimary[0] += effect_value; - new_bonus->ExtraAttackChancePrimary[1] = base2 ? base2 : 1; + new_bonus->ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] += effect_value; + new_bonus->ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_NUM_ATKS] = base2 ? base2 : 1; } - if (new_bonus->ExtraAttackChancePrimary[0] < effect_value) { - new_bonus->ExtraAttackChancePrimary[0] = effect_value; - new_bonus->ExtraAttackChancePrimary[1] = base2 ? base2 : 1; + if (new_bonus->ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] < effect_value) { + new_bonus->ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; + new_bonus->ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_NUM_ATKS] = base2 ? base2 : 1; } break; } @@ -2439,13 +2439,13 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_AddExtraAttackPct_1h_Secondary: { if (AdditiveWornBonus) { - new_bonus->ExtraAttackChanceSecondary[0] += effect_value; - new_bonus->ExtraAttackChanceSecondary[1] = base2 ? base2 : 1; + new_bonus->ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] += effect_value; + new_bonus->ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_NUM_ATKS] = base2 ? base2 : 1; } - if (new_bonus->ExtraAttackChanceSecondary[0] < effect_value) { - new_bonus->ExtraAttackChanceSecondary[0] = effect_value; - new_bonus->ExtraAttackChanceSecondary[1] = base2 ? base2 : 1; + if (new_bonus->ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] < effect_value) { + new_bonus->ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; + new_bonus->ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_NUM_ATKS] = base2 ? base2 : 1; } break; } @@ -2459,13 +2459,13 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_DeathSave: { - if(new_bonus->DeathSave[0] < effect_value) + if(new_bonus->DeathSave[SBIndex::DEATH_SAVE_TYPE] < effect_value) { - new_bonus->DeathSave[0] = effect_value; //1='Partial' 2='Full' - new_bonus->DeathSave[1] = buffslot; + new_bonus->DeathSave[SBIndex::DEATH_SAVE_TYPE] = effect_value; //1='Partial' 2='Full' + new_bonus->DeathSave[SBIndex::DEATH_SAVE_BUFFSLOT] = buffslot; //These are used in later expansion spell effects. - new_bonus->DeathSave[2] = base2;//Min level for HealAmt - new_bonus->DeathSave[3] = max;//HealAmt + new_bonus->DeathSave[SBIndex::DEATH_SAVE_MIN_LEVEL_FOR_HEAL] = base2;//Min level for HealAmt + new_bonus->DeathSave[SBIndex::DEATH_SAVE_HEAL_AMT] = max;//HealAmt } break; } @@ -2473,14 +2473,14 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_DivineSave: { if (AdditiveWornBonus) { - new_bonus->DivineSaveChance[0] += effect_value; - new_bonus->DivineSaveChance[1] = 0; + new_bonus->DivineSaveChance[SBIndex::DIVINE_SAVE_CHANCE] += effect_value; + new_bonus->DivineSaveChance[SBIndex::DIVINE_SAVE_SPELL_TRIGGER_ID] = 0; } - else if(new_bonus->DivineSaveChance[0] < effect_value) + else if(new_bonus->DivineSaveChance[SBIndex::DIVINE_SAVE_CHANCE] < effect_value) { - new_bonus->DivineSaveChance[0] = effect_value; - new_bonus->DivineSaveChance[1] = base2; + new_bonus->DivineSaveChance[SBIndex::DIVINE_SAVE_CHANCE] = effect_value; + new_bonus->DivineSaveChance[SBIndex::DIVINE_SAVE_SPELL_TRIGGER_ID] = base2; } break; } @@ -2712,39 +2712,39 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_LimitHPPercent: { - if(new_bonus->HPPercCap[0] != 0 && new_bonus->HPPercCap[0] > effect_value){ - new_bonus->HPPercCap[0] = effect_value; - new_bonus->HPPercCap[1] = base2; + if(new_bonus->HPPercCap[SBIndex::RESOURCE_PERCENT_CAP] != 0 && new_bonus->HPPercCap[SBIndex::RESOURCE_PERCENT_CAP] > effect_value){ + new_bonus->HPPercCap[SBIndex::RESOURCE_PERCENT_CAP] = effect_value; + new_bonus->HPPercCap[SBIndex::RESOURCE_AMOUNT_CAP] = base2; } - else if(new_bonus->HPPercCap[0] == 0){ - new_bonus->HPPercCap[0] = effect_value; - new_bonus->HPPercCap[1] = base2; + else if(new_bonus->HPPercCap[SBIndex::RESOURCE_PERCENT_CAP] == 0){ + new_bonus->HPPercCap[SBIndex::RESOURCE_PERCENT_CAP] = effect_value; + new_bonus->HPPercCap[SBIndex::RESOURCE_AMOUNT_CAP] = base2; } break; } case SE_LimitManaPercent: { - if(new_bonus->ManaPercCap[0] != 0 && new_bonus->ManaPercCap[0] > effect_value){ - new_bonus->ManaPercCap[0] = effect_value; - new_bonus->ManaPercCap[1] = base2; + if(new_bonus->ManaPercCap[SBIndex::RESOURCE_PERCENT_CAP] != 0 && new_bonus->ManaPercCap[SBIndex::RESOURCE_PERCENT_CAP] > effect_value){ + new_bonus->ManaPercCap[SBIndex::RESOURCE_PERCENT_CAP] = effect_value; + new_bonus->ManaPercCap[SBIndex::RESOURCE_AMOUNT_CAP] = base2; } - else if(new_bonus->ManaPercCap[0] == 0) { - new_bonus->ManaPercCap[0] = effect_value; - new_bonus->ManaPercCap[1] = base2; + else if(new_bonus->ManaPercCap[SBIndex::RESOURCE_PERCENT_CAP] == 0) { + new_bonus->ManaPercCap[SBIndex::RESOURCE_PERCENT_CAP] = effect_value; + new_bonus->ManaPercCap[SBIndex::RESOURCE_AMOUNT_CAP] = base2; } break; } case SE_LimitEndPercent: { - if(new_bonus->EndPercCap[0] != 0 && new_bonus->EndPercCap[0] > effect_value) { - new_bonus->EndPercCap[0] = effect_value; - new_bonus->EndPercCap[1] = base2; + if(new_bonus->EndPercCap[SBIndex::RESOURCE_PERCENT_CAP] != 0 && new_bonus->EndPercCap[SBIndex::RESOURCE_PERCENT_CAP] > effect_value) { + new_bonus->EndPercCap[SBIndex::RESOURCE_PERCENT_CAP] = effect_value; + new_bonus->EndPercCap[SBIndex::RESOURCE_AMOUNT_CAP] = base2; } - else if(new_bonus->EndPercCap[0] == 0){ - new_bonus->EndPercCap[0] = effect_value; - new_bonus->EndPercCap[1] = base2; + else if(new_bonus->EndPercCap[SBIndex::RESOURCE_PERCENT_CAP] == 0){ + new_bonus->EndPercCap[SBIndex::RESOURCE_PERCENT_CAP] = effect_value; + new_bonus->EndPercCap[SBIndex::RESOURCE_AMOUNT_CAP] = base2; } break; @@ -2802,22 +2802,22 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_NegateAttacks: { - if (!new_bonus->NegateAttacks[0] || - ((new_bonus->NegateAttacks[0] && new_bonus->NegateAttacks[2]) && (new_bonus->NegateAttacks[2] < max))){ - new_bonus->NegateAttacks[0] = 1; - new_bonus->NegateAttacks[1] = buffslot; - new_bonus->NegateAttacks[2] = max; + if (!new_bonus->NegateAttacks[SBIndex::NEGATE_ATK_EXISTS] || + ((new_bonus->NegateAttacks[SBIndex::NEGATE_ATK_EXISTS] && new_bonus->NegateAttacks[SBIndex::NEGATE_ATK_MAX_DMG_ABSORB_PER_HIT]) && (new_bonus->NegateAttacks[SBIndex::NEGATE_ATK_MAX_DMG_ABSORB_PER_HIT] < max))){ + new_bonus->NegateAttacks[SBIndex::NEGATE_ATK_EXISTS] = 1; + new_bonus->NegateAttacks[SBIndex::NEGATE_ATK_BUFFSLOT] = buffslot; + new_bonus->NegateAttacks[SBIndex::NEGATE_ATK_MAX_DMG_ABSORB_PER_HIT] = max; } break; } case SE_MitigateMeleeDamage: { - if (new_bonus->MitigateMeleeRune[0] < effect_value){ - new_bonus->MitigateMeleeRune[0] = effect_value; - new_bonus->MitigateMeleeRune[1] = buffslot; - new_bonus->MitigateMeleeRune[2] = base2; - new_bonus->MitigateMeleeRune[3] = max; + if (new_bonus->MitigateMeleeRune[SBIndex::MITIGATION_RUNE_PERCENT] < effect_value){ + new_bonus->MitigateMeleeRune[SBIndex::MITIGATION_RUNE_PERCENT] = effect_value; + new_bonus->MitigateMeleeRune[SBIndex::MITIGATION_RUNE_BUFFSLOT] = buffslot; + new_bonus->MitigateMeleeRune[SBIndex::MITIGATION_RUNE_MAX_DMG_ABSORB_PER_HIT] = base2; + new_bonus->MitigateMeleeRune[SBIndex::MITIGATION_RUNE_MAX_HP_AMT] = max; } break; } @@ -2825,42 +2825,42 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_MeleeThresholdGuard: { - if (new_bonus->MeleeThresholdGuard[0] < effect_value){ - new_bonus->MeleeThresholdGuard[0] = effect_value; - new_bonus->MeleeThresholdGuard[1] = buffslot; - new_bonus->MeleeThresholdGuard[2] = base2; + if (new_bonus->MeleeThresholdGuard[SBIndex::THRESHOLDGUARD_MITIGATION_PERCENT] < effect_value){ + new_bonus->MeleeThresholdGuard[SBIndex::THRESHOLDGUARD_MITIGATION_PERCENT] = effect_value; + new_bonus->MeleeThresholdGuard[SBIndex::THRESHOLDGUARD_BUFFSLOT] = buffslot; + new_bonus->MeleeThresholdGuard[SBIndex::THRESHOLDGUARD_MIN_DMG_TO_TRIGGER] = base2; } break; } case SE_SpellThresholdGuard: { - if (new_bonus->SpellThresholdGuard[0] < effect_value){ - new_bonus->SpellThresholdGuard[0] = effect_value; - new_bonus->SpellThresholdGuard[1] = buffslot; - new_bonus->SpellThresholdGuard[2] = base2; + if (new_bonus->SpellThresholdGuard[SBIndex::THRESHOLDGUARD_MITIGATION_PERCENT] < effect_value){ + new_bonus->SpellThresholdGuard[SBIndex::THRESHOLDGUARD_MITIGATION_PERCENT] = effect_value; + new_bonus->SpellThresholdGuard[SBIndex::THRESHOLDGUARD_BUFFSLOT] = buffslot; + new_bonus->SpellThresholdGuard[SBIndex::THRESHOLDGUARD_MIN_DMG_TO_TRIGGER] = base2; } break; } case SE_MitigateSpellDamage: { - if (new_bonus->MitigateSpellRune[0] < effect_value){ - new_bonus->MitigateSpellRune[0] = effect_value; - new_bonus->MitigateSpellRune[1] = buffslot; - new_bonus->MitigateSpellRune[2] = base2; - new_bonus->MitigateSpellRune[3] = max; + if (new_bonus->MitigateSpellRune[SBIndex::MITIGATION_RUNE_PERCENT] < effect_value){ + new_bonus->MitigateSpellRune[SBIndex::MITIGATION_RUNE_PERCENT] = effect_value; + new_bonus->MitigateSpellRune[SBIndex::MITIGATION_RUNE_BUFFSLOT] = buffslot; + new_bonus->MitigateSpellRune[SBIndex::MITIGATION_RUNE_MAX_DMG_ABSORB_PER_HIT] = base2; + new_bonus->MitigateSpellRune[SBIndex::MITIGATION_RUNE_MAX_HP_AMT] = max; } break; } case SE_MitigateDotDamage: { - if (new_bonus->MitigateDotRune[0] < effect_value){ - new_bonus->MitigateDotRune[0] = effect_value; - new_bonus->MitigateDotRune[1] = buffslot; - new_bonus->MitigateDotRune[2] = base2; - new_bonus->MitigateDotRune[3] = max; + if (new_bonus->MitigateDotRune[SBIndex::MITIGATION_RUNE_PERCENT] < effect_value){ + new_bonus->MitigateDotRune[SBIndex::MITIGATION_RUNE_PERCENT] = effect_value; + new_bonus->MitigateDotRune[SBIndex::MITIGATION_RUNE_BUFFSLOT] = buffslot; + new_bonus->MitigateDotRune[SBIndex::MITIGATION_RUNE_MAX_DMG_ABSORB_PER_HIT] = base2; + new_bonus->MitigateDotRune[SBIndex::MITIGATION_RUNE_MAX_HP_AMT] = max; } break; } @@ -2875,9 +2875,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_Endurance_Absorb_Pct_Damage: { - if (new_bonus->EnduranceAbsorbPercentDamage[0] < effect_value) { - new_bonus->EnduranceAbsorbPercentDamage[0] = effect_value; - new_bonus->EnduranceAbsorbPercentDamage[1] = base2; + if (new_bonus->EnduranceAbsorbPercentDamage[SBIndex::ENDURANCE_ABSORD_MITIGIATION] < effect_value) { + new_bonus->EnduranceAbsorbPercentDamage[SBIndex::ENDURANCE_ABSORD_MITIGIATION] = effect_value; + new_bonus->EnduranceAbsorbPercentDamage[SBIndex::ENDURANCE_ABSORD_DRAIN_PER_HP] = base2; } break; } @@ -3117,17 +3117,17 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne { //Only allow for regular double riposte chance. if(new_bonus->GiveDoubleRiposte[base2] == 0){ - if(new_bonus->GiveDoubleRiposte[0] < effect_value) - new_bonus->GiveDoubleRiposte[0] = effect_value; + if(new_bonus->GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_CHANCE] < effect_value) + new_bonus->GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_CHANCE] = effect_value; } break; } case SE_SlayUndead: { - if(new_bonus->SlayUndead[1] < effect_value) - new_bonus->SlayUndead[0] = effect_value; // Rate - new_bonus->SlayUndead[1] = base2; // Damage Modifier + if(new_bonus->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] < effect_value) + new_bonus->SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] = effect_value; // Rate + new_bonus->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = base2; // Damage Modifier break; } @@ -3141,10 +3141,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne break; case SE_ImprovedTaunt: - if (new_bonus->ImprovedTaunt[0] < effect_value) { - new_bonus->ImprovedTaunt[0] = effect_value; - new_bonus->ImprovedTaunt[1] = base2; - new_bonus->ImprovedTaunt[2] = buffslot; + if (new_bonus->ImprovedTaunt[SBIndex::IMPROVED_TAUNT_MAX_LVL] < effect_value) { + new_bonus->ImprovedTaunt[SBIndex::IMPROVED_TAUNT_MAX_LVL] = effect_value; + new_bonus->ImprovedTaunt[SBIndex::IMPROVED_TAUNT_AGGRO_MOD] = base2; + new_bonus->ImprovedTaunt[SBIndex::IMPROVED_TAUNT_BUFFSLOT] = buffslot; } break; @@ -3158,38 +3158,38 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne break; case SE_Root: - if (new_bonus->Root[0] && (new_bonus->Root[1] > buffslot)){ - new_bonus->Root[0] = 1; - new_bonus->Root[1] = buffslot; + if (new_bonus->Root[SBIndex::ROOT_EXISTS] && (new_bonus->Root[SBIndex::ROOT_BUFFSLOT] > buffslot)){ + new_bonus->Root[SBIndex::ROOT_EXISTS] = 1; + new_bonus->Root[SBIndex::ROOT_BUFFSLOT] = buffslot; } - else if (!new_bonus->Root[0]){ - new_bonus->Root[0] = 1; - new_bonus->Root[1] = buffslot; + else if (!new_bonus->Root[SBIndex::ROOT_EXISTS]){ + new_bonus->Root[SBIndex::ROOT_EXISTS] = 1; + new_bonus->Root[SBIndex::ROOT_BUFFSLOT] = buffslot; } break; case SE_Rune: - if (new_bonus->MeleeRune[0] && (new_bonus->MeleeRune[1] > buffslot)){ + if (new_bonus->MeleeRune[SBIndex::RUNE_AMOUNT] && (new_bonus->MeleeRune[SBIndex::RUNE_BUFFSLOT] > buffslot)){ - new_bonus->MeleeRune[0] = effect_value; - new_bonus->MeleeRune[1] = buffslot; + new_bonus->MeleeRune[SBIndex::RUNE_AMOUNT] = effect_value; + new_bonus->MeleeRune[SBIndex::RUNE_BUFFSLOT] = buffslot; } - else if (!new_bonus->MeleeRune[0]){ - new_bonus->MeleeRune[0] = effect_value; - new_bonus->MeleeRune[1] = buffslot; + else if (!new_bonus->MeleeRune[SBIndex::RUNE_AMOUNT]){ + new_bonus->MeleeRune[SBIndex::RUNE_AMOUNT] = effect_value; + new_bonus->MeleeRune[SBIndex::RUNE_BUFFSLOT] = buffslot; } break; case SE_AbsorbMagicAtt: - if (new_bonus->AbsorbMagicAtt[0] && (new_bonus->AbsorbMagicAtt[1] > buffslot)){ - new_bonus->AbsorbMagicAtt[0] = effect_value; - new_bonus->AbsorbMagicAtt[1] = buffslot; + if (new_bonus->AbsorbMagicAtt[SBIndex::RUNE_AMOUNT] && (new_bonus->AbsorbMagicAtt[SBIndex::RUNE_BUFFSLOT] > buffslot)){ + new_bonus->AbsorbMagicAtt[SBIndex::RUNE_AMOUNT] = effect_value; + new_bonus->AbsorbMagicAtt[SBIndex::RUNE_BUFFSLOT] = buffslot; } - else if (!new_bonus->AbsorbMagicAtt[0]){ - new_bonus->AbsorbMagicAtt[0] = effect_value; - new_bonus->AbsorbMagicAtt[1] = buffslot; + else if (!new_bonus->AbsorbMagicAtt[SBIndex::RUNE_AMOUNT]){ + new_bonus->AbsorbMagicAtt[SBIndex::RUNE_AMOUNT] = effect_value; + new_bonus->AbsorbMagicAtt[SBIndex::RUNE_BUFFSLOT] = buffslot; } break; @@ -3222,23 +3222,23 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne break; case SE_AStacker: - new_bonus->AStacker[0] = 1; - new_bonus->AStacker[1] = effect_value; + new_bonus->AStacker[SBIndex::BUFFSTACKER_EXISTS] = 1; + new_bonus->AStacker[SBIndex::BUFFSTACKER_VALUE] = effect_value; break; case SE_BStacker: - new_bonus->BStacker[0] = 1; - new_bonus->BStacker[1] = effect_value; + new_bonus->BStacker[SBIndex::BUFFSTACKER_EXISTS] = 1; + new_bonus->BStacker[SBIndex::BUFFSTACKER_VALUE] = effect_value; break; case SE_CStacker: - new_bonus->CStacker[0] = 1; - new_bonus->CStacker[1] = effect_value; + new_bonus->CStacker[SBIndex::BUFFSTACKER_EXISTS] = 1; + new_bonus->CStacker[SBIndex::BUFFSTACKER_VALUE] = effect_value; break; case SE_DStacker: - new_bonus->DStacker[0] = 1; - new_bonus->DStacker[1] = effect_value; + new_bonus->DStacker[SBIndex::BUFFSTACKER_EXISTS] = 1; + new_bonus->DStacker[SBIndex::BUFFSTACKER_VALUE] = effect_value; break; case SE_Berserk: @@ -3262,36 +3262,36 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_HeadShot: { - if(new_bonus->HeadShot[1] < base2){ - new_bonus->HeadShot[0] = effect_value; - new_bonus->HeadShot[1] = base2; + if(new_bonus->HeadShot[SBIndex::FINISHING_EFFECT_DMG] < base2){ + new_bonus->HeadShot[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; + new_bonus->HeadShot[SBIndex::FINISHING_EFFECT_DMG] = base2; } break; } case SE_HeadShotLevel: { - if(new_bonus->HSLevel[0] < effect_value) { - new_bonus->HSLevel[0] = effect_value; - new_bonus->HSLevel[1] = base2; + if(new_bonus->HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] < effect_value) { + new_bonus->HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; + new_bonus->HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = base2; } break; } case SE_Assassinate: { - if(new_bonus->Assassinate[1] < base2){ - new_bonus->Assassinate[0] = effect_value; - new_bonus->Assassinate[1] = base2; + if(new_bonus->Assassinate[SBIndex::FINISHING_EFFECT_DMG] < base2){ + new_bonus->Assassinate[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; + new_bonus->Assassinate[SBIndex::FINISHING_EFFECT_DMG] = base2; } break; } case SE_AssassinateLevel: { - if(new_bonus->AssassinateLevel[0] < effect_value) { - new_bonus->AssassinateLevel[0] = effect_value; - new_bonus->AssassinateLevel[1] = base2; + if(new_bonus->AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] < effect_value) { + new_bonus->AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; + new_bonus->AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = base2; } break; } @@ -3299,9 +3299,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_FinishingBlow: { //base1 = chance, base2 = damage - if (new_bonus->FinishingBlow[1] < base2){ - new_bonus->FinishingBlow[0] = effect_value; - new_bonus->FinishingBlow[1] = base2; + if (new_bonus->FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] < base2){ + new_bonus->FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; + new_bonus->FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] = base2; } break; } @@ -3309,9 +3309,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_FinishingBlowLvl: { //base1 = level, base2 = ??? (Set to 200 in AA data, possible proc rate mod?) - if (new_bonus->FinishingBlowLvl[0] < effect_value){ - new_bonus->FinishingBlowLvl[0] = effect_value; - new_bonus->FinishingBlowLvl[1] = base2; + if (new_bonus->FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] < effect_value){ + new_bonus->FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; + new_bonus->FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = base2; } break; } @@ -3379,20 +3379,20 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne } case SE_PC_Pet_Rampage: { - new_bonus->PC_Pet_Rampage[0] += effect_value; //Chance to rampage - if (new_bonus->PC_Pet_Rampage[1] < base2) - new_bonus->PC_Pet_Rampage[1] = base2; //Damage modifer - take highest + new_bonus->PC_Pet_Rampage[SBIndex::PET_RAMPAGE_CHANCE] += effect_value; //Chance to rampage + if (new_bonus->PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] < base2) + new_bonus->PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = base2; //Damage modifer - take highest break; } case SE_PC_Pet_AE_Rampage: { - new_bonus->PC_Pet_AE_Rampage[0] += effect_value; //Chance to rampage - if (new_bonus->PC_Pet_AE_Rampage[1] < base2) - new_bonus->PC_Pet_AE_Rampage[1] = base2; //Damage modifer - take highest + new_bonus->PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_CHANCE] += effect_value; //Chance to rampage + if (new_bonus->PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] < base2) + new_bonus->PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = base2; //Damage modifer - take highest break; } - case SE_PC_Pet_Flurry_Chance: + case SE_PC_Pet_Flurry_Chance: new_bonus->PC_Pet_Flurry += effect_value; //Chance to Flurry break; @@ -3421,8 +3421,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_RaiseSkillCap: { if (base2 > EQ::skills::HIGHEST_SKILL) break; - - if (new_bonus->RaiseSkillCap[base2] < effect_value) + + if (new_bonus->RaiseSkillCap[base2] < effect_value) new_bonus->RaiseSkillCap[base2] = effect_value; break; } @@ -3467,7 +3467,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne break; if (AdditiveWornBonus) new_bonus->Damage_Taken_Position_Mod[base2] += effect_value; - else if (effect_value < 0 && new_bonus->Damage_Taken_Position_Mod[base2] > effect_value) + else if (effect_value < 0 && new_bonus->Damage_Taken_Position_Mod[base2] > effect_value) new_bonus->Damage_Taken_Position_Mod[base2] = effect_value; else if (effect_value > 0 && new_bonus->Damage_Taken_Position_Mod[base2] < effect_value) new_bonus->Damage_Taken_Position_Mod[base2] = effect_value; @@ -3503,7 +3503,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_Pet_Add_Atk: new_bonus->Pet_Add_Atk += effect_value; break; - + //Special custom cases for loading effects on to NPC from 'npc_spels_effects' table if (IsAISpellEffect) { @@ -3638,7 +3638,7 @@ bool Client::CalcItemScale(uint32 slot_x, uint32 slot_y) { if (parent_item && parent_item->GetItem()->BagType == EQ::item::BagTypeTradersSatchel) continue; } - + bool update_slot = false; if(inst->IsScaling()) { @@ -4367,21 +4367,21 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) break; case SE_ExtraAttackChance: - spellbonuses.ExtraAttackChance[0] = effect_value; - aabonuses.ExtraAttackChance[0] = effect_value; - itembonuses.ExtraAttackChance[0] = effect_value; + spellbonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; + aabonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; + itembonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; break; case SE_AddExtraAttackPct_1h_Primary: - spellbonuses.ExtraAttackChancePrimary[0] = effect_value; - aabonuses.ExtraAttackChancePrimary[0] = effect_value; - itembonuses.ExtraAttackChancePrimary[0] = effect_value; + spellbonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; + aabonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; + itembonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; break; case SE_AddExtraAttackPct_1h_Secondary: - spellbonuses.ExtraAttackChanceSecondary[0] = effect_value; - aabonuses.ExtraAttackChanceSecondary[0] = effect_value; - itembonuses.ExtraAttackChanceSecondary[0] = effect_value; + spellbonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; + aabonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; + itembonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; break; case SE_PercentXPIncrease: @@ -4616,35 +4616,35 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) } case SE_NegateAttacks: - spellbonuses.NegateAttacks[0] = effect_value; - spellbonuses.NegateAttacks[1] = effect_value; + spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_EXISTS] = effect_value; + spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_BUFFSLOT] = effect_value; break; case SE_MitigateMeleeDamage: - spellbonuses.MitigateMeleeRune[0] = effect_value; - spellbonuses.MitigateMeleeRune[1] = -1; + spellbonuses.MitigateMeleeRune[SBIndex::MITIGATION_RUNE_PERCENT] = effect_value; + spellbonuses.MitigateMeleeRune[SBIndex::MITIGATION_RUNE_BUFFSLOT] = -1; break; case SE_MeleeThresholdGuard: - spellbonuses.MeleeThresholdGuard[0] = effect_value; - spellbonuses.MeleeThresholdGuard[1] = -1; - spellbonuses.MeleeThresholdGuard[1] = effect_value; + spellbonuses.MeleeThresholdGuard[SBIndex::THRESHOLDGUARD_MITIGATION_PERCENT] = effect_value; + spellbonuses.MeleeThresholdGuard[SBIndex::THRESHOLDGUARD_BUFFSLOT] = -1; + spellbonuses.MeleeThresholdGuard[SBIndex::THRESHOLDGUARD_BUFFSLOT] = effect_value; break; case SE_SpellThresholdGuard: - spellbonuses.SpellThresholdGuard[0] = effect_value; - spellbonuses.SpellThresholdGuard[1] = -1; - spellbonuses.SpellThresholdGuard[1] = effect_value; + spellbonuses.SpellThresholdGuard[SBIndex::THRESHOLDGUARD_MITIGATION_PERCENT] = effect_value; + spellbonuses.SpellThresholdGuard[SBIndex::THRESHOLDGUARD_BUFFSLOT] = -1; + spellbonuses.SpellThresholdGuard[SBIndex::THRESHOLDGUARD_BUFFSLOT] = effect_value; break; case SE_MitigateSpellDamage: - spellbonuses.MitigateSpellRune[0] = effect_value; - spellbonuses.MitigateSpellRune[1] = -1; + spellbonuses.MitigateSpellRune[SBIndex::MITIGATION_RUNE_PERCENT] = effect_value; + spellbonuses.MitigateSpellRune[SBIndex::MITIGATION_RUNE_BUFFSLOT] = -1; break; case SE_MitigateDotDamage: - spellbonuses.MitigateDotRune[0] = effect_value; - spellbonuses.MitigateDotRune[1] = -1; + spellbonuses.MitigateDotRune[SBIndex::MITIGATION_RUNE_PERCENT] = effect_value; + spellbonuses.MitigateDotRune[SBIndex::MITIGATION_RUNE_BUFFSLOT] = -1; break; case SE_ManaAbsorbPercentDamage: @@ -4652,8 +4652,8 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) break; case SE_Endurance_Absorb_Pct_Damage: - spellbonuses.EnduranceAbsorbPercentDamage[0] = effect_value; - spellbonuses.EnduranceAbsorbPercentDamage[1] = effect_value; + spellbonuses.EnduranceAbsorbPercentDamage[SBIndex::ENDURANCE_ABSORD_MITIGIATION] = effect_value; + spellbonuses.EnduranceAbsorbPercentDamage[SBIndex::ENDURANCE_ABSORD_DRAIN_PER_HP] = effect_value; break; case SE_ShieldBlock: @@ -4894,18 +4894,18 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) break; case SE_GiveDoubleRiposte: - spellbonuses.GiveDoubleRiposte[0] = effect_value; - itembonuses.GiveDoubleRiposte[0] = effect_value; - aabonuses.GiveDoubleRiposte[0] = effect_value; + spellbonuses.GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_CHANCE] = effect_value; + itembonuses.GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_CHANCE] = effect_value; + aabonuses.GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_CHANCE] = effect_value; break; case SE_SlayUndead: - spellbonuses.SlayUndead[0] = effect_value; - spellbonuses.SlayUndead[1] = effect_value; - itembonuses.SlayUndead[0] = effect_value; - itembonuses.SlayUndead[1] = effect_value; - aabonuses.SlayUndead[0] = effect_value; - aabonuses.SlayUndead[1] = effect_value; + spellbonuses.SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] = effect_value; + spellbonuses.SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = effect_value; + itembonuses.SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] = effect_value; + itembonuses.SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = effect_value; + aabonuses.SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] = effect_value; + aabonuses.SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = effect_value; break; case SE_DoubleRangedAttack: @@ -4955,9 +4955,9 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) break; case SE_ImprovedTaunt: - spellbonuses.ImprovedTaunt[0] = effect_value; - spellbonuses.ImprovedTaunt[1] = effect_value; - spellbonuses.ImprovedTaunt[2] = -1; + spellbonuses.ImprovedTaunt[SBIndex::IMPROVED_TAUNT_MAX_LVL] = effect_value; + spellbonuses.ImprovedTaunt[SBIndex::IMPROVED_TAUNT_AGGRO_MOD] = effect_value; + spellbonuses.ImprovedTaunt[SBIndex::IMPROVED_TAUNT_BUFFSLOT] = -1; break; case SE_FrenziedDevastation: @@ -4967,18 +4967,18 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) break; case SE_Root: - spellbonuses.Root[0] = effect_value; - spellbonuses.Root[1] = -1; + spellbonuses.Root[SBIndex::ROOT_EXISTS] = effect_value; + spellbonuses.Root[SBIndex::ROOT_BUFFSLOT] = -1; break; case SE_Rune: - spellbonuses.MeleeRune[0] = effect_value; - spellbonuses.MeleeRune[1] = -1; + spellbonuses.MeleeRune[SBIndex::RUNE_AMOUNT] = effect_value; + spellbonuses.MeleeRune[SBIndex::RUNE_BUFFSLOT] = -1; break; case SE_AbsorbMagicAtt: - spellbonuses.AbsorbMagicAtt[0] = effect_value; - spellbonuses.AbsorbMagicAtt[1] = -1; + spellbonuses.AbsorbMagicAtt[SBIndex::RUNE_AMOUNT] = effect_value; + spellbonuses.AbsorbMagicAtt[SBIndex::RUNE_BUFFSLOT] = -1; break; case SE_Berserk: @@ -5006,57 +5006,57 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) break; case SE_HeadShot: - spellbonuses.HeadShot[0] = effect_value; - aabonuses.HeadShot[0] = effect_value; - itembonuses.HeadShot[0] = effect_value; - spellbonuses.HeadShot[1] = effect_value; - aabonuses.HeadShot[1] = effect_value; - itembonuses.HeadShot[1] = effect_value; + spellbonuses.HeadShot[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; + aabonuses.HeadShot[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; + itembonuses.HeadShot[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; + spellbonuses.HeadShot[SBIndex::FINISHING_EFFECT_DMG] = effect_value; + aabonuses.HeadShot[SBIndex::FINISHING_EFFECT_DMG] = effect_value; + itembonuses.HeadShot[SBIndex::FINISHING_EFFECT_DMG] = effect_value; break; case SE_HeadShotLevel: - spellbonuses.HSLevel[0] = effect_value; - aabonuses.HSLevel[0] = effect_value; - itembonuses.HSLevel[0] = effect_value; - spellbonuses.HSLevel[1] = effect_value; - aabonuses.HSLevel[1] = effect_value; - itembonuses.HSLevel[1] = effect_value; + spellbonuses.HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; + aabonuses.HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; + itembonuses.HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; + spellbonuses.HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; + aabonuses.HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; + itembonuses.HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; break; case SE_Assassinate: - spellbonuses.Assassinate[0] = effect_value; - aabonuses.Assassinate[0] = effect_value; - itembonuses.Assassinate[0] = effect_value; - spellbonuses.Assassinate[1] = effect_value; - aabonuses.Assassinate[1] = effect_value; - itembonuses.Assassinate[1] = effect_value; + spellbonuses.Assassinate[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; + aabonuses.Assassinate[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; + itembonuses.Assassinate[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; + spellbonuses.Assassinate[SBIndex::FINISHING_EFFECT_DMG] = effect_value; + aabonuses.Assassinate[SBIndex::FINISHING_EFFECT_DMG] = effect_value; + itembonuses.Assassinate[SBIndex::FINISHING_EFFECT_DMG] = effect_value; break; case SE_AssassinateLevel: - spellbonuses.AssassinateLevel[0] = effect_value; - aabonuses.AssassinateLevel[0] = effect_value; - itembonuses.AssassinateLevel[0] = effect_value; - spellbonuses.AssassinateLevel[1] = effect_value; - aabonuses.AssassinateLevel[1] = effect_value; - itembonuses.AssassinateLevel[1] = effect_value; + spellbonuses.AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; + aabonuses.AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; + itembonuses.AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; + spellbonuses.AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; + aabonuses.AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; + itembonuses.AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; break; case SE_FinishingBlow: - spellbonuses.FinishingBlow[0] = effect_value; - aabonuses.FinishingBlow[0] = effect_value; - itembonuses.FinishingBlow[0] = effect_value; - spellbonuses.FinishingBlow[1] = effect_value; - aabonuses.FinishingBlow[1] = effect_value; - itembonuses.FinishingBlow[1] = effect_value; + spellbonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; + aabonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; + itembonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; + spellbonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] = effect_value; + aabonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] = effect_value; + itembonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] = effect_value; break; case SE_FinishingBlowLvl: - spellbonuses.FinishingBlowLvl[0] = effect_value; - aabonuses.FinishingBlowLvl[0] = effect_value; - itembonuses.FinishingBlowLvl[0] = effect_value; - spellbonuses.FinishingBlowLvl[1] = effect_value; - aabonuses.FinishingBlowLvl[1] = effect_value; - itembonuses.FinishingBlowLvl[1] = effect_value; + spellbonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; + aabonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; + itembonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; + spellbonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; + aabonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; + itembonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; break; case SE_Sanctuary: @@ -5095,21 +5095,21 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) break; case SE_Melee_Damage_Position_Mod: - spellbonuses.Melee_Damage_Position_Mod[0] = effect_value; - aabonuses.Melee_Damage_Position_Mod[0] = effect_value; - itembonuses.Melee_Damage_Position_Mod[0] = effect_value; - spellbonuses.Melee_Damage_Position_Mod[1] = effect_value; - aabonuses.Melee_Damage_Position_Mod[1] = effect_value; - itembonuses.Melee_Damage_Position_Mod[1] = effect_value; + spellbonuses.Melee_Damage_Position_Mod[SBIndex::POSITIONAL_DAMAGE_MOD] = effect_value; + aabonuses.Melee_Damage_Position_Mod[SBIndex::POSITIONAL_DAMAGE_MOD] = effect_value; + itembonuses.Melee_Damage_Position_Mod[SBIndex::POSITIONAL_DAMAGE_MOD] = effect_value; + spellbonuses.Melee_Damage_Position_Mod[SBIndex::POSITIONAL_LOCATION] = effect_value; + aabonuses.Melee_Damage_Position_Mod[SBIndex::POSITIONAL_LOCATION] = effect_value; + itembonuses.Melee_Damage_Position_Mod[SBIndex::POSITIONAL_LOCATION] = effect_value; break; case SE_Damage_Taken_Position_Mod: - spellbonuses.Damage_Taken_Position_Mod[0] = effect_value; - aabonuses.Damage_Taken_Position_Mod[0] = effect_value; - itembonuses.Damage_Taken_Position_Mod[0] = effect_value; - spellbonuses.Damage_Taken_Position_Mod[1] = effect_value; - aabonuses.Damage_Taken_Position_Mod[1] = effect_value; - itembonuses.Damage_Taken_Position_Mod[1] = effect_value; + spellbonuses.Damage_Taken_Position_Mod[SBIndex::POSITIONAL_DAMAGE_MOD] = effect_value; + aabonuses.Damage_Taken_Position_Mod[SBIndex::POSITIONAL_DAMAGE_MOD] = effect_value; + itembonuses.Damage_Taken_Position_Mod[SBIndex::POSITIONAL_DAMAGE_MOD] = effect_value; + spellbonuses.Damage_Taken_Position_Mod[SBIndex::POSITIONAL_LOCATION] = effect_value; + aabonuses.Damage_Taken_Position_Mod[SBIndex::POSITIONAL_LOCATION] = effect_value; + itembonuses.Damage_Taken_Position_Mod[SBIndex::POSITIONAL_LOCATION] = effect_value; break; @@ -5138,21 +5138,21 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) break; case SE_PC_Pet_Rampage: - spellbonuses.PC_Pet_Rampage[0] = effect_value; - itembonuses.PC_Pet_Rampage[0] = effect_value; - aabonuses.PC_Pet_Rampage[0] = effect_value; - spellbonuses.PC_Pet_Rampage[1] = effect_value; - itembonuses.PC_Pet_Rampage[1] = effect_value; - aabonuses.PC_Pet_Rampage[1] = effect_value; + spellbonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_CHANCE] = effect_value; + itembonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_CHANCE] = effect_value; + aabonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_CHANCE] = effect_value; + spellbonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = effect_value; + itembonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = effect_value; + aabonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = effect_value; break; case SE_PC_Pet_AE_Rampage: - spellbonuses.PC_Pet_AE_Rampage[0] = effect_value; - itembonuses.PC_Pet_AE_Rampage[0] = effect_value; - aabonuses.PC_Pet_AE_Rampage[0] = effect_value; - spellbonuses.PC_Pet_AE_Rampage[1] = effect_value; - itembonuses.PC_Pet_AE_Rampage[1] = effect_value; - aabonuses.PC_Pet_AE_Rampage[1] = effect_value; + spellbonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_CHANCE] = effect_value; + itembonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_CHANCE] = effect_value; + aabonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_CHANCE] = effect_value; + spellbonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = effect_value; + itembonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = effect_value; + aabonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = effect_value; break; diff --git a/zone/client_mods.cpp b/zone/client_mods.cpp index ec01a00b5..d31bc6c9b 100644 --- a/zone/client_mods.cpp +++ b/zone/client_mods.cpp @@ -328,10 +328,10 @@ int32 Client::CalcMaxHP() if (current_hp > max_hp) { current_hp = max_hp; } - int hp_perc_cap = spellbonuses.HPPercCap[0]; + int hp_perc_cap = spellbonuses.HPPercCap[SBIndex::RESOURCE_PERCENT_CAP]; if (hp_perc_cap) { int curHP_cap = (max_hp * hp_perc_cap) / 100; - if (current_hp > curHP_cap || (spellbonuses.HPPercCap[1] && current_hp > spellbonuses.HPPercCap[1])) { + if (current_hp > curHP_cap || (spellbonuses.HPPercCap[SBIndex::RESOURCE_AMOUNT_CAP] && current_hp > spellbonuses.HPPercCap[SBIndex::RESOURCE_AMOUNT_CAP])) { current_hp = curHP_cap; } @@ -591,10 +591,10 @@ int32 Client::CalcMaxMana() if (current_mana > max_mana) { current_mana = max_mana; } - int mana_perc_cap = spellbonuses.ManaPercCap[0]; + int mana_perc_cap = spellbonuses.ManaPercCap[SBIndex::RESOURCE_PERCENT_CAP]; if (mana_perc_cap) { int curMana_cap = (max_mana * mana_perc_cap) / 100; - if (current_mana > curMana_cap || (spellbonuses.ManaPercCap[1] && current_mana > spellbonuses.ManaPercCap[1])) { + if (current_mana > curMana_cap || (spellbonuses.ManaPercCap[SBIndex::RESOURCE_AMOUNT_CAP] && current_mana > spellbonuses.ManaPercCap[SBIndex::RESOURCE_AMOUNT_CAP])) { current_mana = curMana_cap; } } @@ -1614,10 +1614,10 @@ void Client::CalcMaxEndurance() if (current_endurance > max_end) { current_endurance = max_end; } - int end_perc_cap = spellbonuses.EndPercCap[0]; + int end_perc_cap = spellbonuses.EndPercCap[SBIndex::RESOURCE_PERCENT_CAP]; if (end_perc_cap) { int curEnd_cap = (max_end * end_perc_cap) / 100; - if (current_endurance > curEnd_cap || (spellbonuses.EndPercCap[1] && current_endurance > spellbonuses.EndPercCap[1])) { + if (current_endurance > curEnd_cap || (spellbonuses.EndPercCap[SBIndex::RESOURCE_AMOUNT_CAP] && current_endurance > spellbonuses.EndPercCap[SBIndex::RESOURCE_AMOUNT_CAP])) { current_endurance = curEnd_cap; } } diff --git a/zone/common.h b/zone/common.h index e2aa19af5..83be866e4 100644 --- a/zone/common.h +++ b/zone/common.h @@ -503,8 +503,8 @@ struct StatBonuses { uint32 MitigateDotRune[4]; // 0 = Mitigation value 1 = Buff Slot 2 = Max mitigation per tick 3 = Rune Amt bool TriggerMeleeThreshold; // Has Melee Threshhold bool TriggerSpellThreshold; // Has Spell Threshhold - uint32 ManaAbsorbPercentDamage; // 0 = Mitigation value - int32 EnduranceAbsorbPercentDamage[2]; // 0 = Mitigation value 1 = Percent Endurance drain per HP lost + uint32 ManaAbsorbPercentDamage; // 0 = Mitigation value + int32 EnduranceAbsorbPercentDamage[2]; // 0 = Mitigation value 1 = Percent Endurance drain per HP lost int32 ShieldBlock; // Chance to Shield Block int32 BlockBehind; // Chance to Block Behind (with our without shield) bool CriticalRegenDecay; // increase critical regen chance, decays based on spell level cast @@ -512,7 +512,7 @@ struct StatBonuses { bool CriticalDotDecay; // increase critical dot chance, decays based on spell level cast bool DivineAura; // invulnerability bool DistanceRemoval; // Check if Cancle if Moved effect is present - int32 ImprovedTaunt[3]; // 0 = Max Level 1 = Aggro modifier 2 = buffid + int32 ImprovedTaunt[3]; // 0 = Max Level 1 = Aggro modifier 2 = buff slot int8 Root[2]; // The lowest buff slot a root can be found. [0] = Bool if has root [1] = buff slot int32 FrenziedDevastation; // base1= AArank(used) base2= chance increase spell criticals + all DD spells 2x mana. uint32 AbsorbMagicAtt[2]; // 0 = magic rune value 1 = buff slot @@ -616,6 +616,58 @@ struct StatBonuses { bool hunger; // Song of Sustenance -- min caps to 3500 }; +// StatBonus Indexes +namespace SBIndex { + constexpr uint16 BUFFSTACKER_EXISTS = 0; // SPA 446-449 + constexpr uint16 BUFFSTACKER_VALUE = 1; // SPA 446-449 + constexpr uint16 EXTRA_ATTACK_CHANCE = 0; // SPA 266,498,499 + constexpr uint16 EXTRA_ATTACK_NUM_ATKS = 1; // SPA 266,498,499 + constexpr uint16 DIVINE_SAVE_CHANCE = 0; // SPA 232 + constexpr uint16 DIVINE_SAVE_SPELL_TRIGGER_ID = 1; // SPA 232 + constexpr uint16 DEATH_SAVE_TYPE = 0; // SPA 150 + constexpr uint16 DEATH_SAVE_BUFFSLOT = 1; // SPA 150 + constexpr uint16 DEATH_SAVE_MIN_LEVEL_FOR_HEAL = 2; // SPA 150 + constexpr uint16 DEATH_SAVE_HEAL_AMT = 3; // SPA 150 + constexpr uint16 RESOURCE_PERCENT_CAP = 0; // SPA 408-410 + constexpr uint16 RESOURCE_AMOUNT_CAP = 1; // SPA 408-419 + constexpr uint16 NEGATE_ATK_EXISTS = 0; // SPA 163 + constexpr uint16 NEGATE_ATK_BUFFSLOT = 1; // SPA 163 + constexpr uint16 NEGATE_ATK_MAX_DMG_ABSORB_PER_HIT = 2; // SPA 163 + constexpr uint16 MITIGATION_RUNE_PERCENT = 0; // SPA 161,162,450 + constexpr uint16 MITIGATION_RUNE_BUFFSLOT = 1; // SPA 161,162,450 + constexpr uint16 MITIGATION_RUNE_MAX_DMG_ABSORB_PER_HIT = 2; // SPA 161,162,450 + constexpr uint16 MITIGATION_RUNE_MAX_HP_AMT = 3; // SPA 161,162,450 + constexpr uint16 THRESHOLDGUARD_MITIGATION_PERCENT = 0; // SPA 451,452 + constexpr uint16 THRESHOLDGUARD_BUFFSLOT = 1; // SPA 451,452 + constexpr uint16 THRESHOLDGUARD_MIN_DMG_TO_TRIGGER = 2; // SPA 451,452 + constexpr uint16 ENDURANCE_ABSORD_MITIGIATION = 0; // SPA 521 + constexpr uint16 ENDURANCE_ABSORD_DRAIN_PER_HP = 1; // SPA 521 + constexpr uint16 IMPROVED_TAUNT_MAX_LVL = 0; // SPA 444 + constexpr uint16 IMPROVED_TAUNT_AGGRO_MOD = 1; // SPA 444 + constexpr uint16 IMPROVED_TAUNT_BUFFSLOT = 2; // SPA 444 + constexpr uint16 ROOT_EXISTS = 0; // SPA 99 + constexpr uint16 ROOT_BUFFSLOT = 1; // SPA 99 + constexpr uint16 RUNE_AMOUNT = 0; // SPA 55 + constexpr uint16 RUNE_BUFFSLOT = 1; // SPA 78 + constexpr uint16 POSITIONAL_DAMAGE_MOD = 0; // SPA 503-506 + constexpr uint16 POSITIONAL_LOCATION = 1; // SPA 503-506 + constexpr uint16 PET_RAMPAGE_CHANCE = 0; // SPA 464,465 + constexpr uint16 PET_RAMPAGE_DMG_MOD = 1; // SPA 465,465 + constexpr uint16 SKILLPROC_CHANCE = 0; // SPA 427 + constexpr uint16 SKILLPROC_SKILL = 1; // SPA 427 + constexpr uint16 SKILLPROC_SPELL_ID = 2; // SPA 427 + constexpr uint16 SLAYUNDEAD_RATE_MOD = 0; // SPA 219 + constexpr uint16 SLAYUNDEAD_DMG_MOD = 1; // SPA 219 + constexpr uint16 DOUBLE_RIPOSTE_CHANCE = 0; // SPA 223 + constexpr uint16 DOUBLE_RIPOSTE_SKILL_ATK_CHANCE = 1; // SPA 223 + constexpr uint16 DOUBLE_RIPOSTE_SKILL = 2; // SPA 223 + constexpr uint16 FINISHING_EFFECT_PROC_CHANCE = 0; // SPA 278, 439, 217 + constexpr uint16 FINISHING_EFFECT_DMG = 1; // SPA 278, 439, 217 + constexpr uint16 FINISHING_EFFECT_LEVEL_MAX = 0; // SPA 440, 345, 346 + constexpr uint16 FINISHING_EFFECT_LEVEL_CHANCE_BONUS = 1; // SPA 440, 345, 346 +}; + + typedef struct { uint16 spellID; diff --git a/zone/merc.cpp b/zone/merc.cpp index a24809418..2a68e8907 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -862,10 +862,10 @@ int32 Merc::CalcMaxHP() { if (current_hp > max_hp) current_hp = max_hp; - int hp_perc_cap = spellbonuses.HPPercCap[0]; + int hp_perc_cap = spellbonuses.HPPercCap[SBIndex::RESOURCE_PERCENT_CAP]; if(hp_perc_cap) { int curHP_cap = (max_hp * hp_perc_cap) / 100; - if (current_hp > curHP_cap || (spellbonuses.HPPercCap[1] && current_hp > spellbonuses.HPPercCap[1])) + if (current_hp > curHP_cap || (spellbonuses.HPPercCap[SBIndex::RESOURCE_AMOUNT_CAP] && current_hp > spellbonuses.HPPercCap[SBIndex::RESOURCE_AMOUNT_CAP])) current_hp = curHP_cap; } @@ -904,10 +904,10 @@ int32 Merc::CalcMaxMana() current_mana = max_mana; } - int mana_perc_cap = spellbonuses.ManaPercCap[0]; + int mana_perc_cap = spellbonuses.ManaPercCap[SBIndex::RESOURCE_PERCENT_CAP]; if(mana_perc_cap) { int curMana_cap = (max_mana * mana_perc_cap) / 100; - if (current_mana > curMana_cap || (spellbonuses.ManaPercCap[1] && current_mana > spellbonuses.ManaPercCap[1])) + if (current_mana > curMana_cap || (spellbonuses.ManaPercCap[SBIndex::RESOURCE_AMOUNT_CAP] && current_mana > spellbonuses.ManaPercCap[SBIndex::RESOURCE_AMOUNT_CAP])) current_mana = curMana_cap; } @@ -999,10 +999,10 @@ void Merc::CalcMaxEndurance() cur_end = max_end; } - int end_perc_cap = spellbonuses.EndPercCap[0]; + int end_perc_cap = spellbonuses.EndPercCap[SBIndex::RESOURCE_PERCENT_CAP]; if(end_perc_cap) { int curEnd_cap = (max_end * end_perc_cap) / 100; - if (cur_end > curEnd_cap || (spellbonuses.EndPercCap[1] && cur_end > spellbonuses.EndPercCap[1])) + if (cur_end > curEnd_cap || (spellbonuses.EndPercCap[SBIndex::RESOURCE_AMOUNT_CAP] && cur_end > spellbonuses.EndPercCap[SBIndex::RESOURCE_AMOUNT_CAP])) cur_end = curEnd_cap; } } @@ -1624,7 +1624,7 @@ void Merc::AI_Process() { } } - int16 ExtraAttackChanceBonus = spellbonuses.ExtraAttackChance[0] + itembonuses.ExtraAttackChance[0] + aabonuses.ExtraAttackChance[0]; + int16 ExtraAttackChanceBonus = spellbonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] + itembonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] + aabonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE]; if (GetTarget() && ExtraAttackChanceBonus) { if(zone->random.Roll(ExtraAttackChanceBonus)) diff --git a/zone/mob.cpp b/zone/mob.cpp index ce966726c..60d8d8839 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3547,9 +3547,9 @@ bool Mob::TrySpellTrigger(Mob *target, uint32 spell_id, int effect) return false; /*The effects SE_SpellTrigger (SPA 340) and SE_Chance_Best_in_Spell_Grp (SPA 469) work as follows, you typically will have 2-3 different spells each with their own - chance to be triggered with all chances equaling up to 100 pct, with only 1 spell out of the group being ultimately cast. - (ie Effect1 trigger spellA with 30% chance, Effect2 triggers spellB with 20% chance, Effect3 triggers spellC with 50% chance). - The following function ensures a stastically accurate chance for each spell to be cast based on their chance values. These effects are also used in spells where there + chance to be triggered with all chances equaling up to 100 pct, with only 1 spell out of the group being ultimately cast. + (ie Effect1 trigger spellA with 30% chance, Effect2 triggers spellB with 20% chance, Effect3 triggers spellC with 50% chance). + The following function ensures a stastically accurate chance for each spell to be cast based on their chance values. These effects are also used in spells where there is only 1 effect using the trigger effect. In those situations we simply roll a chance for that spell to be cast once. Note: Both SPA 340 and 469 can be in same spell and both cummulative add up to 100 pct chances. SPA469 only difference being the spell cast will be "best in spell group", instead of a defined spell_id.*/ @@ -3558,7 +3558,7 @@ bool Mob::TrySpellTrigger(Mob *target, uint32 spell_id, int effect) int total_chance = 0; int effect_slot = effect; bool CastSpell = false; - + for (int i = 0; i < EFFECT_COUNT; i++) { if (spells[spell_id].effectid[i] == SE_SpellTrigger || spells[spell_id].effectid[i] == SE_Chance_Best_in_Spell_Grp) @@ -3743,7 +3743,7 @@ void Mob::TryOnSpellFinished(Mob *caster, Mob *target, uint16 spell_id) if (!IsValidSpell(spell_id)) return; - /*Apply damage from Lifeburn type effects on caster at end of spell cast. + /*Apply damage from Lifeburn type effects on caster at end of spell cast. This allows for the AE spells to function without repeatedly killing caster Damage or heal portion can be found as regular single use spell effect */ @@ -3766,7 +3766,7 @@ void Mob::TryOnSpellFinished(Mob *caster, Mob *target, uint16 spell_id) int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining) { /* - Modifies incoming spell damage by percent, to increase or decrease damage, can be limited to specific resists. + Modifies incoming spell damage by percent, to increase or decrease damage, can be limited to specific resists. Can be applied through quest function, spell focus or npc_spells_effects table. This function is run on the target of the spell. */ @@ -3789,7 +3789,7 @@ int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining) innate_mod = Vulnerability_Mod[HIGHEST_RESIST+1]; //[Apply spell derived vulnerabilities] Step 1: Check this focus effect exists on the mob. - if (spellbonuses.FocusEffects[focusSpellVulnerability]){ + if (spellbonuses.FocusEffects[focusSpellVulnerability]){ int32 tmp_focus = 0; int tmp_buffslot = -1; @@ -3903,8 +3903,8 @@ int32 Mob::GetPositionalDmgTaken(Mob *attacker) int back_arc = 0; int total_mod = 0; - back_arc += itembonuses.Damage_Taken_Position_Mod[0] + aabonuses.Damage_Taken_Position_Mod[0] + spellbonuses.Damage_Taken_Position_Mod[0]; - front_arc += itembonuses.Damage_Taken_Position_Mod[1] + aabonuses.Damage_Taken_Position_Mod[1] + spellbonuses.Damage_Taken_Position_Mod[1]; + back_arc += itembonuses.Damage_Taken_Position_Mod[SBIndex::POSITIONAL_DAMAGE_MOD] + aabonuses.Damage_Taken_Position_Mod[SBIndex::POSITIONAL_DAMAGE_MOD] + spellbonuses.Damage_Taken_Position_Mod[SBIndex::POSITIONAL_DAMAGE_MOD]; + front_arc += itembonuses.Damage_Taken_Position_Mod[SBIndex::POSITIONAL_LOCATION] + aabonuses.Damage_Taken_Position_Mod[SBIndex::POSITIONAL_LOCATION] + spellbonuses.Damage_Taken_Position_Mod[SBIndex::POSITIONAL_LOCATION]; if (back_arc || front_arc) { //Do they have this bonus? if (attacker->BehindMob(this, attacker->GetX(), attacker->GetY()))//Check if attacker is striking from behind @@ -4897,8 +4897,8 @@ int16 Mob::GetMeleeDmgPositionMod(Mob* defender) int back_arc = 0; int total_mod = 0; - back_arc += itembonuses.Melee_Damage_Position_Mod[0] + aabonuses.Melee_Damage_Position_Mod[0] + spellbonuses.Melee_Damage_Position_Mod[0]; - front_arc += itembonuses.Melee_Damage_Position_Mod[1] + aabonuses.Melee_Damage_Position_Mod[1] + spellbonuses.Melee_Damage_Position_Mod[1]; + back_arc += itembonuses.Melee_Damage_Position_Mod[SBIndex::POSITIONAL_DAMAGE_MOD] + aabonuses.Melee_Damage_Position_Mod[SBIndex::POSITIONAL_DAMAGE_MOD] + spellbonuses.Melee_Damage_Position_Mod[SBIndex::POSITIONAL_DAMAGE_MOD]; + front_arc += itembonuses.Melee_Damage_Position_Mod[SBIndex::POSITIONAL_LOCATION] + aabonuses.Melee_Damage_Position_Mod[SBIndex::POSITIONAL_LOCATION] + spellbonuses.Melee_Damage_Position_Mod[SBIndex::POSITIONAL_LOCATION]; if (back_arc || front_arc) { //Do they have this bonus? if (BehindMob(defender, GetX(), GetY()))//Check if attacker is striking from behind diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 3766a2546..09dd36df3 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1224,12 +1224,12 @@ void Mob::AI_Process() { //SE_PC_Pet_Rampage SPA 464 on pet, chance modifier if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) { - int chance = spellbonuses.PC_Pet_Rampage[0] + itembonuses.PC_Pet_Rampage[0] + aabonuses.PC_Pet_Rampage[0]; + int chance = spellbonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_CHANCE] + itembonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_CHANCE] + aabonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_CHANCE]; if (chance && zone->random.Roll(chance)) { Rampage(nullptr); } } - + if (GetSpecialAbility(SPECATK_RAMPAGE) && !specialed) { int rampage_chance = GetSpecialAbilityParam(SPECATK_RAMPAGE, 0); @@ -1267,7 +1267,7 @@ void Mob::AI_Process() { //SE_PC_Pet_Rampage SPA 465 on pet, chance modifier if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) { - int chance = spellbonuses.PC_Pet_AE_Rampage[0] + itembonuses.PC_Pet_AE_Rampage[0] + aabonuses.PC_Pet_AE_Rampage[0]; + int chance = spellbonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_CHANCE] + itembonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_CHANCE] + aabonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_CHANCE]; if (chance && zone->random.Roll(chance)) { Rampage(nullptr); } diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 6c49948d5..4d614731a 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -193,12 +193,12 @@ void Mob::DoSpecialAttackDamage(Mob *who, EQ::skills::SkillType skill, int32 bas DoAttack(who, my_hit); who->AddToHateList(this, hate, 0); - if (my_hit.damage_done > 0 && aabonuses.SkillAttackProc[0] && aabonuses.SkillAttackProc[1] == skill && - IsValidSpell(aabonuses.SkillAttackProc[2])) { - float chance = aabonuses.SkillAttackProc[0] / 1000.0f; + if (my_hit.damage_done > 0 && aabonuses.SkillAttackProc[SBIndex::SKILLPROC_CHANCE] && aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SKILL] == skill && + IsValidSpell(aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID])) { + float chance = aabonuses.SkillAttackProc[SBIndex::SKILLPROC_CHANCE] / 1000.0f; if (zone->random.Roll(chance)) - SpellFinished(aabonuses.SkillAttackProc[2], who, EQ::spells::CastingSlot::Item, 0, -1, - spells[aabonuses.SkillAttackProc[2]].ResistDiff); + SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID], who, EQ::spells::CastingSlot::Item, 0, -1, + spells[aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID]].ResistDiff); } who->Damage(this, my_hit.damage_done, SPELL_UNKNOWN, skill, false); @@ -996,21 +996,21 @@ void Mob::ProjectileAttack() disable = false; Mob *target = entity_list.GetMobID(ProjectileAtk[i].target_id); - if (target && target->IsMoving()) { + if (target && target->IsMoving()) { /* Only recalculate hit increment if target is moving. - Due to frequency that we need to check increment the targets position variables may not be + Due to frequency that we need to check increment the targets position variables may not be updated even if moving. Do a simple check before calculating distance. */ if (ProjectileAtk[i].tlast_x != target->GetX() || ProjectileAtk[i].tlast_y != target->GetY()) { - + ProjectileAtk[i].tlast_x = target->GetX(); ProjectileAtk[i].tlast_y = target->GetY(); //Recalculate from the original location the projectile was fired in relation to the current targets location. float distance = target->CalculateDistance(ProjectileAtk[i].origin_x, ProjectileAtk[i].origin_y, ProjectileAtk[i].origin_z); float distance_mod = 0.0f; - + if (distance <= 125.0f) { distance_mod = (ProjectileAtk[i].speed_mod - 4.0f) * -20.0f; distance += distance * distance_mod / 100.0f; @@ -1022,14 +1022,14 @@ void Mob::ProjectileAttack() distance = distance * 1.30f; //Add 30% to base distance if over 200 range to tighten up hit timing. distance = 3.14f * (distance / 2.0f); //Get distance of arc to better reflect projectile path length } - + float hit = 1200.0f + (10 * distance / ProjectileAtk[i].speed_mod); ProjectileAtk[i].hit_increment = static_cast(hit); } } - // Check if we hit. + // Check if we hit. if (ProjectileAtk[i].hit_increment <= ProjectileAtk[i].increment) { if (target) { if (IsNPC()) { @@ -2061,9 +2061,9 @@ int Mob::TryHeadShot(Mob *defender, EQ::skills::SkillType skillInUse) // Only works on YOUR target. if (defender && defender->GetBodyType() == BT_Humanoid && !defender->IsClient() && skillInUse == EQ::skills::SkillArchery && GetTarget() == defender) { - uint32 HeadShot_Dmg = aabonuses.HeadShot[1] + spellbonuses.HeadShot[1] + itembonuses.HeadShot[1]; + uint32 HeadShot_Dmg = aabonuses.HeadShot[SBIndex::FINISHING_EFFECT_DMG] + spellbonuses.HeadShot[SBIndex::FINISHING_EFFECT_DMG] + itembonuses.HeadShot[SBIndex::FINISHING_EFFECT_DMG]; uint8 HeadShot_Level = 0; // Get Highest Headshot Level - HeadShot_Level = std::max({aabonuses.HSLevel[0], spellbonuses.HSLevel[0], itembonuses.HSLevel[0]}); + HeadShot_Level = std::max({aabonuses.HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX], spellbonuses.HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX], itembonuses.HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX]}); if (HeadShot_Dmg && HeadShot_Level && (defender->GetLevel() <= HeadShot_Level)) { int chance = GetDEX(); @@ -2071,10 +2071,10 @@ int Mob::TryHeadShot(Mob *defender, EQ::skills::SkillType skillInUse) if (IsClient()) chance += CastToClient()->GetHeroicDEX() / 25; chance *= 10; - int norm = aabonuses.HSLevel[1]; + int norm = aabonuses.HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS]; if (norm > 0) chance = chance * norm / 100; - chance += aabonuses.HeadShot[0] + spellbonuses.HeadShot[0] + itembonuses.HeadShot[0]; + chance += aabonuses.HeadShot[SBIndex::FINISHING_EFFECT_PROC_CHANCE] + spellbonuses.HeadShot[SBIndex::FINISHING_EFFECT_PROC_CHANCE] + itembonuses.HeadShot[SBIndex::FINISHING_EFFECT_PROC_CHANCE]; if (zone->random.Int(1, 1000) <= chance) { entity_list.MessageCloseString( this, false, 200, Chat::MeleeCrit, FATAL_BOW_SHOT, @@ -2097,7 +2097,7 @@ int Mob::TryAssassinate(Mob *defender, EQ::skills::SkillType skillInUse) if (IsClient()) chance += CastToClient()->GetHeroicDEX(); chance *= 10; - int norm = aabonuses.AssassinateLevel[1]; + int norm = aabonuses.AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS]; if (norm > 0) chance = chance * norm / 100; } else if (skillInUse == EQ::skills::SkillThrowing) { @@ -2107,14 +2107,14 @@ int Mob::TryAssassinate(Mob *defender, EQ::skills::SkillType skillInUse) chance += 5; } - chance += aabonuses.Assassinate[0] + spellbonuses.Assassinate[0] + itembonuses.Assassinate[0]; + chance += aabonuses.Assassinate[SBIndex::FINISHING_EFFECT_PROC_CHANCE] + spellbonuses.Assassinate[SBIndex::FINISHING_EFFECT_PROC_CHANCE] + itembonuses.Assassinate[SBIndex::FINISHING_EFFECT_PROC_CHANCE]; uint32 Assassinate_Dmg = - aabonuses.Assassinate[1] + spellbonuses.Assassinate[1] + itembonuses.Assassinate[1]; + aabonuses.Assassinate[SBIndex::FINISHING_EFFECT_DMG] + spellbonuses.Assassinate[SBIndex::FINISHING_EFFECT_DMG] + itembonuses.Assassinate[SBIndex::FINISHING_EFFECT_DMG]; uint8 Assassinate_Level = 0; // Get Highest Headshot Level Assassinate_Level = std::max( - {aabonuses.AssassinateLevel[0], spellbonuses.AssassinateLevel[0], itembonuses.AssassinateLevel[0]}); + {aabonuses.AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX], spellbonuses.AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX], itembonuses.AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX]}); // revamped AAs require AA line I believe? if (!Assassinate_Level) @@ -2198,12 +2198,12 @@ void Mob::DoMeleeSkillAttackDmg(Mob *other, uint16 weapon_damage, EQ::skills::Sk } other->AddToHateList(this, hate, 0); - if (damage > 0 && aabonuses.SkillAttackProc[0] && aabonuses.SkillAttackProc[1] == skillinuse && - IsValidSpell(aabonuses.SkillAttackProc[2])) { - float chance = aabonuses.SkillAttackProc[0] / 1000.0f; + if (damage > 0 && aabonuses.SkillAttackProc[SBIndex::SKILLPROC_CHANCE] && aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SKILL] == skillinuse && + IsValidSpell(aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID])) { + float chance = aabonuses.SkillAttackProc[SBIndex::SKILLPROC_CHANCE] / 1000.0f; if (zone->random.Roll(chance)) - SpellFinished(aabonuses.SkillAttackProc[2], other, EQ::spells::CastingSlot::Item, 0, -1, - spells[aabonuses.SkillAttackProc[2]].ResistDiff); + SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID], other, EQ::spells::CastingSlot::Item, 0, -1, + spells[aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID]].ResistDiff); } other->Damage(this, damage, SPELL_UNKNOWN, skillinuse); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index d64af2f19..037db0a95 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2903,10 +2903,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if (zone->random.Roll(spells[spell_id].base[i]) && IsValidSpell(spells[spell_id].base2[i])) caster->SpellFinished(spells[spell_id].base2[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff); - + break; } - + case SE_Hatelist_To_Tail_Index: { if (caster && zone->random.Roll(spells[spell_id].base[i])) caster->SetBottomRampageList(); @@ -2940,7 +2940,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove else Stun(spells[spell_id].base[i]); } - else + else caster->MessageString(Chat::SpellFailure, FEAR_TOO_HIGH); break; } @@ -2949,7 +2949,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove buffs[buffslot].focusproclimit_procamt = spells[spell_id].base[i]; //Set max amount of procs before lockout timer break; } - + case SE_PersistentEffect: MakeAura(spell_id); break; @@ -3928,11 +3928,11 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) int32 amt = abs(GetMaxHP() * effect_value / 100); if (spells[buff.spellid].max[i] && amt > spells[buff.spellid].max[i]) amt = spells[buff.spellid].max[i]; - - if (effect_value < 0) { + + if (effect_value < 0) { Damage(this, amt, 0, EQ::skills::SkillEvocation, false); } - else { + else { HealDamage(amt); } break; @@ -3945,7 +3945,7 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) amt = spells[buff.spellid].max[i]; if (effect_value < 0) { - + SetMana(GetMana() - amt); } else { @@ -6192,16 +6192,16 @@ bool Mob::TryDivineSave() -If desired, additional spells can be triggered from the AA/item/spell effect, generally a heal. */ - int32 SuccessChance = aabonuses.DivineSaveChance[0] + itembonuses.DivineSaveChance[0] + spellbonuses.DivineSaveChance[0]; + int32 SuccessChance = aabonuses.DivineSaveChance[SBIndex::DIVINE_SAVE_CHANCE] + itembonuses.DivineSaveChance[SBIndex::DIVINE_SAVE_CHANCE] + spellbonuses.DivineSaveChance[SBIndex::DIVINE_SAVE_CHANCE]; if (SuccessChance && zone->random.Roll(SuccessChance)) { SetHP(1); int32 EffectsToTry[] = { - aabonuses.DivineSaveChance[1], - itembonuses.DivineSaveChance[1], - spellbonuses.DivineSaveChance[1] + aabonuses.DivineSaveChance[SBIndex::DIVINE_SAVE_SPELL_TRIGGER_ID], + itembonuses.DivineSaveChance[SBIndex::DIVINE_SAVE_SPELL_TRIGGER_ID], + spellbonuses.DivineSaveChance[SBIndex::DIVINE_SAVE_SPELL_TRIGGER_ID] }; //Fade the divine save effect here after saving the old effects off. //That way, if desired, the effect could apply SE_DivineSave again. @@ -6236,10 +6236,10 @@ bool Mob::TryDeathSave() { -In later expansions this SE_DeathSave was given a level limit and a heal value in its effect data. */ - if (spellbonuses.DeathSave[0]){ + if (spellbonuses.DeathSave[SBIndex::DEATH_SAVE_TYPE]){ int SuccessChance = 0; - int buffSlot = spellbonuses.DeathSave[1]; + int buffSlot = spellbonuses.DeathSave[SBIndex::DEATH_SAVE_BUFFSLOT]; int32 UD_HealMod = 0; int HealAmt = 300; //Death Pact max Heal @@ -6254,12 +6254,12 @@ bool Mob::TryDeathSave() { if(zone->random.Roll(SuccessChance)) { - if(spellbonuses.DeathSave[0] == 2) + if(spellbonuses.DeathSave[SBIndex::DEATH_SAVE_TYPE] == 2) HealAmt = RuleI(Spells, DivineInterventionHeal); //8000HP is how much LIVE Divine Intervention max heals //Check if bonus Heal amount can be applied ([3] Bonus Heal [2] Level limit) - if (spellbonuses.DeathSave[3] && (GetLevel() >= spellbonuses.DeathSave[2])) - HealAmt += spellbonuses.DeathSave[3]; + if (spellbonuses.DeathSave[SBIndex::DEATH_SAVE_HEAL_AMT] && (GetLevel() >= spellbonuses.DeathSave[SBIndex::DEATH_SAVE_MIN_LEVEL_FOR_HEAL])) + HealAmt += spellbonuses.DeathSave[SBIndex::DEATH_SAVE_HEAL_AMT]; if ((GetMaxHP() - GetHP()) < HealAmt) HealAmt = GetMaxHP() - GetHP(); @@ -6267,7 +6267,7 @@ bool Mob::TryDeathSave() { SetHP((GetHP()+HealAmt)); Message(263, "The gods have healed you for %i points of damage.", HealAmt); - if(spellbonuses.DeathSave[0] == 2) + if(spellbonuses.DeathSave[SBIndex::DEATH_SAVE_TYPE] == 2) entity_list.MessageCloseString( this, false, @@ -6291,12 +6291,12 @@ bool Mob::TryDeathSave() { if(zone->random.Roll(SuccessChance)) { - if(spellbonuses.DeathSave[0] == 2) + if(spellbonuses.DeathSave[SBIndex::DEATH_SAVE_TYPE] == 2) HealAmt = RuleI(Spells, DivineInterventionHeal); //Check if bonus Heal amount can be applied ([3] Bonus Heal [2] Level limit) - if (spellbonuses.DeathSave[3] && (GetLevel() >= spellbonuses.DeathSave[2])) - HealAmt += spellbonuses.DeathSave[3]; + if (spellbonuses.DeathSave[SBIndex::DEATH_SAVE_HEAL_AMT] && (GetLevel() >= spellbonuses.DeathSave[SBIndex::DEATH_SAVE_MIN_LEVEL_FOR_HEAL])) + HealAmt += spellbonuses.DeathSave[SBIndex::DEATH_SAVE_HEAL_AMT]; HealAmt = HealAmt*UD_HealMod/100; @@ -6306,7 +6306,7 @@ bool Mob::TryDeathSave() { SetHP((GetHP()+HealAmt)); Message(263, "The gods have healed you for %i points of damage.", HealAmt); - if(spellbonuses.DeathSave[0] == 2) + if(spellbonuses.DeathSave[SBIndex::DEATH_SAVE_TYPE] == 2) entity_list.MessageCloseString( this, false, @@ -6645,22 +6645,22 @@ bool Mob::TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier){ bool Mob::ImprovedTaunt(){ - if (spellbonuses.ImprovedTaunt[0]){ + if (spellbonuses.ImprovedTaunt[SBIndex::IMPROVED_TAUNT_MAX_LVL]){ - if (GetLevel() > spellbonuses.ImprovedTaunt[0]) + if (GetLevel() > spellbonuses.ImprovedTaunt[SBIndex::IMPROVED_TAUNT_MAX_LVL]) return false; - if (spellbonuses.ImprovedTaunt[2] >= 0){ + if (spellbonuses.ImprovedTaunt[SBIndex::IMPROVED_TAUNT_BUFFSLOT] >= 0){ - target = entity_list.GetMob(buffs[spellbonuses.ImprovedTaunt[2]].casterid); + target = entity_list.GetMob(buffs[spellbonuses.ImprovedTaunt[SBIndex::IMPROVED_TAUNT_BUFFSLOT]].casterid); if (target){ SetTarget(target); return true; } else { - if(!TryFadeEffect(spellbonuses.ImprovedTaunt[2])) - BuffFadeBySlot(spellbonuses.ImprovedTaunt[2], true); //If caster killed removed effect. + if(!TryFadeEffect(spellbonuses.ImprovedTaunt[SBIndex::IMPROVED_TAUNT_BUFFSLOT])) + BuffFadeBySlot(spellbonuses.ImprovedTaunt[SBIndex::IMPROVED_TAUNT_BUFFSLOT], true); //If caster killed removed effect. } } } @@ -7187,7 +7187,7 @@ void Mob::CastSpellOnLand(Mob* caster, int32 spell_id) the CalcFocusEffect function if not 100pct. ApplyFocusProcLimiter() function checks for SE_Proc_Timer_Modifier which allows for limiting how often a spell from effect can be triggered for example, if set to base=1 and base2= 1500, then for everyone 1 successful trigger, you will be unable to trigger again for 1.5 seconds. - + Live only has this focus in buffs/debuffs that can be placed on a target. TODO: Will consider adding support for it as AA and Item. */ if (!caster) @@ -7221,7 +7221,7 @@ void Mob::CastSpellOnLand(Mob* caster, int32 spell_id) SpellFinished(trigger_spell_id, current_target, EQ::spells::CastingSlot::Item, 0, -1, spells[trigger_spell_id].ResistDiff); } } - + if (i >= 0) CheckNumHitsRemaining(NumHit::MatchingSpells, i); } @@ -7237,13 +7237,13 @@ bool Mob::ApplyFocusProcLimiter(int32 spell_id, int buffslot) //Do not allow spell cast if timer is active. if (buffs[buffslot].focusproclimit_time > 0) - return false; + return false; /* - SE_Proc_Timer_Modifier + SE_Proc_Timer_Modifier base1= amount of total procs allowed until lock out timer is triggered, should be set to at least 1 in any spell for the effect to function. base2= lock out timer, which prevents any more procs set in ms 1500 = 1.5 seconds - This system allows easy scaling for multiple different buffs with same effects each having seperate active individual timer checks. Ie. + This system allows easy scaling for multiple different buffs with same effects each having seperate active individual timer checks. Ie. */ if (IsValidSpell(spell_id)) { @@ -7270,7 +7270,7 @@ bool Mob::ApplyFocusProcLimiter(int32 spell_id, int buffslot) if (!focus_proc_limit_timer.Enabled()) { focus_proc_limit_timer.Start(250); } - + return true; } } diff --git a/zone/spells.cpp b/zone/spells.cpp index 9a2d6f27c..72ef70e40 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2945,31 +2945,33 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, } } - /*Buff stacking prevention spell effects (446 - 449) works as follows... If B prevent A, if C prevent B, if D prevent C. - If checking same type ie A vs A, which ever effect base value is higher will take hold. - Special check is added to make sure the buffs stack properly when applied from fade on duration effect, since the buff - is not fully removed at the time of the trgger*/ - if (spellbonuses.AStacker[0]) { - if ((effect2 == SE_AStacker) && (sp2.effectid[i] <= spellbonuses.AStacker[1])) + /* + Buff stacking prevention spell effects (446 - 449) works as follows... If B prevent A, if C prevent B, if D prevent C. + If checking same type ie A vs A, which ever effect base value is higher will take hold. + Special check is added to make sure the buffs stack properly when applied from fade on duration effect, since the buff + is not fully removed at the time of the trigger + */ + if (spellbonuses.AStacker[SBIndex::BUFFSTACKER_EXISTS]) { + if ((effect2 == SE_AStacker) && (sp2.effectid[i] <= spellbonuses.AStacker[SBIndex::BUFFSTACKER_VALUE])) return -1; } - if (spellbonuses.BStacker[0]) { - if ((effect2 == SE_BStacker) && (sp2.effectid[i] <= spellbonuses.BStacker[1])) + if (spellbonuses.BStacker[SBIndex::BUFFSTACKER_EXISTS]) { + if ((effect2 == SE_BStacker) && (sp2.effectid[i] <= spellbonuses.BStacker[SBIndex::BUFFSTACKER_VALUE])) return -1; if ((effect2 == SE_AStacker) && (!IsCastonFadeDurationSpell(spellid1) && buffs[buffslot].ticsremaining != 1 && IsEffectInSpell(spellid1, SE_BStacker))) return -1; } - if (spellbonuses.CStacker[0]) { - if ((effect2 == SE_CStacker) && (sp2.effectid[i] <= spellbonuses.CStacker[1])) + if (spellbonuses.CStacker[SBIndex::BUFFSTACKER_EXISTS]) { + if ((effect2 == SE_CStacker) && (sp2.effectid[i] <= spellbonuses.CStacker[SBIndex::BUFFSTACKER_VALUE])) return -1; if ((effect2 == SE_BStacker) && (!IsCastonFadeDurationSpell(spellid1) && buffs[buffslot].ticsremaining != 1 && IsEffectInSpell(spellid1, SE_CStacker))) return -1; } - if (spellbonuses.DStacker[0]) { - if ((effect2 == SE_DStacker) && (sp2.effectid[i] <= spellbonuses.DStacker[1])) + if (spellbonuses.DStacker[SBIndex::BUFFSTACKER_EXISTS]) { + if ((effect2 == SE_DStacker) && (sp2.effectid[i] <= spellbonuses.DStacker[SBIndex::BUFFSTACKER_VALUE])) return -1; if ((effect2 == SE_CStacker) && (!IsCastonFadeDurationSpell(spellid1) && buffs[buffslot].ticsremaining != 1 && IsEffectInSpell(spellid1, SE_DStacker))) return -1; @@ -3786,7 +3788,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r MessageString(Chat::SpellFailure, SPELL_NO_EFFECT); return false; } - + // Block next spell effect should be used up first(since its blocking the next spell) if(CanBlockSpell()) { int buff_count = GetMaxTotalSlots(); From 93b0264a8b81e3ecf49d5c94825ab21213ce14e3 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 1 Aug 2021 14:19:42 -0400 Subject: [PATCH 140/624] Update to focus SE_BlockNextSpell (#1479) Minor code changes to make the focus be checked consistent with how all other focuses are checked. No change in functionality. --- zone/bonuses.cpp | 7 +------ zone/common.h | 2 -- zone/lua_stat_bonuses.cpp | 2 +- zone/mob.h | 2 +- 4 files changed, 3 insertions(+), 10 deletions(-) diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 53952e6ea..92bd537b9 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -2750,10 +2750,6 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne break; } - case SE_BlockNextSpellFocus: - new_bonus->BlockNextSpell = true; - break; - case SE_NegateSpellEffect: new_bonus->NegateEffects = true; break; @@ -3827,8 +3823,7 @@ uint8 Mob::IsFocusEffect(uint16 spell_id,int effect_index, bool AA,uint32 aa_eff case SE_Fc_Spell_Damage_Pct_IncomingPC: return focusFcSpellDamagePctIncomingPC; case SE_BlockNextSpellFocus: - //return focusBlockNextSpell; - return 0; //This is calculated as an actual bonus + return focusBlockNextSpell; case SE_FcTwincast: return focusTwincast; case SE_SympatheticProc: diff --git a/zone/common.h b/zone/common.h index 83be866e4..754d2b61f 100644 --- a/zone/common.h +++ b/zone/common.h @@ -483,8 +483,6 @@ struct StatBonuses { int HPPercCap[2]; //Spell effect that limits you to being healed/regening beyond a % of your max int ManaPercCap[2]; // ^^ 0 = % Cap 1 = Flat Amount Cap int EndPercCap[2]; // ^^ - bool BlockNextSpell; // Indicates whether the client can block a spell or not - //uint16 BlockSpellEffect[EFFECT_COUNT]; // Prevents spells with certain effects from landing on you *no longer used bool ImmuneToFlee; // Bypass the fleeing flag uint32 VoiceGraft; // Stores the ID of the mob with which to talk through int32 SpellProcChance; // chance to proc from sympathetic spell effects diff --git a/zone/lua_stat_bonuses.cpp b/zone/lua_stat_bonuses.cpp index c007e09ca..ef012c6d7 100644 --- a/zone/lua_stat_bonuses.cpp +++ b/zone/lua_stat_bonuses.cpp @@ -677,7 +677,7 @@ int Lua_StatBonuses::GetXPRateMod() const { bool Lua_StatBonuses::GetBlockNextSpell() const { Lua_Safe_Call_Bool(); - return self->BlockNextSpell; + //return self->BlockNextSpell; bonus no longer used due to effect being a focus } bool Lua_StatBonuses::GetImmuneToFlee() const { diff --git a/zone/mob.h b/zone/mob.h index aadeb6645..6bc9b8a8f 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -823,7 +823,7 @@ public: int GetCriticalChanceBonus(uint16 skill); int16 GetSkillDmgAmt(uint16 skill); bool TryReflectSpell(uint32 spell_id); - bool CanBlockSpell() const { return(spellbonuses.BlockNextSpell); } + inline bool CanBlockSpell() const { return(spellbonuses.FocusEffects[focusBlockNextSpell]); } bool DoHPToManaCovert(uint16 mana_cost = 0); int32 ApplySpellEffectiveness(int16 spell_id, int32 value, bool IsBard = false, uint16 caster_id=0); int8 GetDecayEffectValue(uint16 spell_id, uint16 spelleffect); From f01cf74fa6c14b97e4e1b979e4d2d4d58b7cb074 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 1 Aug 2021 14:26:44 -0400 Subject: [PATCH 141/624] [Spells] Update SPA 339 SE_TriggerOnCast (#1478) * Recoded SE_TriggerOnCast Focus effect Recoded SE_TriggerOnCast focus effect to be consistent with how all other focuses are checked. No longer an arbitrary limit as to number of a focus effects of this type you can have. * new command: resetdisc_timer usage: #resetdisc_timer [all | timer_id] * syntax fixes syntax improvements * minor fix changed numhits check * Update spell_effects.cpp * added better support for spell procs that don't require target. * syntax * Formatting and syntax tweaks Co-authored-by: Akkadius --- common/spdat.cpp | 11 ++++ common/spdat.h | 1 + zone/bonuses.cpp | 40 +------------- zone/command.cpp | 50 ++++++++++++----- zone/command.h | 1 + zone/mob.cpp | 53 ------------------ zone/mob.h | 4 +- zone/spell_effects.cpp | 121 ++++++++++++++++++++++++++++++++++++++++- zone/spells.cpp | 4 +- 9 files changed, 174 insertions(+), 111 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 6c6e7dcd7..132b74523 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1282,3 +1282,14 @@ const char* GetSpellName(uint16 spell_id) return spells[spell_id].name; } +bool SpellRequiresTarget(int spell_id) +{ + if (spells[spell_id].targettype == ST_AEClientV1 || + spells[spell_id].targettype == ST_Self || + spells[spell_id].targettype == ST_AECaster || + spells[spell_id].targettype == ST_Ring || + spells[spell_id].targettype == ST_Beam) { + return false; + } + return true; +} diff --git a/common/spdat.h b/common/spdat.h index c41c22347..ea8524f82 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -1151,6 +1151,7 @@ bool IsStackableDot(uint16 spell_id); bool IsBardOnlyStackEffect(int effect); bool IsCastWhileInvis(uint16 spell_id); bool IsEffectIgnoredInStacking(int spa); +bool SpellRequiresTarget(int targettype); int CalcPetHp(int levelb, int classb, int STA = 75); int GetSpellEffectDescNum(uint16 spell_id); diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 92bd537b9..f1106fdf5 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1065,19 +1065,6 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) } break; - case SE_TriggerOnCast: - - for (int i = 0; i < MAX_SPELL_TRIGGER; i++) { - if (newbon->SpellTriggers[i] == rank.id) - break; - - if (!newbon->SpellTriggers[i]) { - // Save the 'rank.id' of each triggerable effect to an array - newbon->SpellTriggers[i] = rank.id; - break; - } - } - break; case SE_CriticalHitChance: { // Bad data or unsupported new skill @@ -2538,19 +2525,6 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne break; } - case SE_TriggerOnCast: - { - for(int e = 0; e < MAX_SPELL_TRIGGER; e++) - { - if(!new_bonus->SpellTriggers[e]) - { - new_bonus->SpellTriggers[e] = spell_id; - break; - } - } - break; - } - case SE_SpellCritChance: new_bonus->CriticalSpellChance += effect_value; break; @@ -3816,8 +3790,7 @@ uint8 Mob::IsFocusEffect(uint16 spell_id,int effect_index, bool AA,uint32 aa_eff case SE_ReduceReuseTimer: return focusReduceRecastTime; case SE_TriggerOnCast: - //return focusTriggerOnCast; - return 0; //This is calculated as an actual bonus + return focusTriggerOnCast; case SE_FcSpellVulnerability: return focusSpellVulnerability; case SE_Fc_Spell_Damage_Pct_IncomingPC: @@ -4433,17 +4406,6 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) break; } - case SE_TriggerOnCast: - { - for(int e = 0; e < MAX_SPELL_TRIGGER; e++) - { - spellbonuses.SpellTriggers[e] = effect_value; - aabonuses.SpellTriggers[e] = effect_value; - itembonuses.SpellTriggers[e] = effect_value; - } - break; - } - case SE_SpellCritChance: spellbonuses.CriticalSpellChance = effect_value; aabonuses.CriticalSpellChance = effect_value; diff --git a/zone/command.cpp b/zone/command.cpp index a02da9ab1..665b2098b 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -359,6 +359,7 @@ int command_init(void) command_add("repop", "[delay] - Repop the zone with optional delay", 100, command_repop) || command_add("resetaa", "- Resets a Player's AA in their profile and refunds spent AA's to unspent, may disconnect player.", 200, command_resetaa) || command_add("resetaa_timer", "Command to reset AA cooldown timers.", 200, command_resetaa_timer) || + command_add("resetdisc_timer", "Command to reset all discipline cooldown timers.", 200, command_resetdisc_timer) || command_add("revoke", "[charname] [1/0] - Makes charname unable to talk on OOC", 200, command_revoke) || command_add("roambox", "Manages roambox settings for an NPC", 200, command_roambox) || command_add("rules", "(subcommand) - Manage server rules", 250, command_rules) || @@ -8121,31 +8122,31 @@ void command_summonitem(Client *c, const Seperator *sep) ).c_str() ); } - + if (arguments >= 2 && sep->IsNumber(2)) { charges = atoi(sep->arg[2]); } - + if (arguments >= 3 && sep->IsNumber(3)) { augment_one = atoi(sep->arg[3]); } - + if (arguments >= 4 && sep->IsNumber(4)) { augment_two = atoi(sep->arg[4]); } - + if (arguments >= 5 && sep->IsNumber(5)) { augment_three = atoi(sep->arg[5]); } - + if (arguments >= 6 && sep->IsNumber(6)) { augment_four = atoi(sep->arg[6]); } - + if (arguments >= 7 && sep->IsNumber(7)) { augment_five = atoi(sep->arg[7]); } - + if (arguments == 8 && sep->IsNumber(8)) { augment_six = atoi(sep->arg[8]); } @@ -8189,7 +8190,7 @@ void command_giveitem(Client *c, const Seperator *sep) c->Message(Chat::Red, "Usage: #giveitem [item id | link] [charges] [augment_one_id] [augment_two_id] [augment_three_id] [augment_four_id] [augment_five_id] [augment_six_id] (Charges are optional.)"); return; } - + Client *client_target = c->GetTarget()->CastToClient(); uint8 item_status = 0; uint8 current_status = c->Admin(); @@ -8197,7 +8198,7 @@ void command_giveitem(Client *c, const Seperator *sep) if (item) { item_status = item->MinStatus; } - + if (item_status > current_status) { c->Message( Chat::White, @@ -8209,23 +8210,23 @@ void command_giveitem(Client *c, const Seperator *sep) ); return; } - + if (arguments >= 2 && sep->IsNumber(2)) { charges = atoi(sep->arg[2]); } - + if (arguments >= 3 && sep->IsNumber(3)) { augment_one = atoi(sep->arg[3]); } - + if (arguments >= 4 && sep->IsNumber(4)) { augment_two = atoi(sep->arg[4]); } - + if (arguments >= 5 && sep->IsNumber(5)) { augment_three = atoi(sep->arg[5]); } - + if (arguments >= 6 && sep->IsNumber(6)) { augment_four = atoi(sep->arg[6]); } @@ -13725,6 +13726,27 @@ void command_resetaa_timer(Client *c, const Seperator *sep) { } } +void command_resetdisc_timer(Client *c, const Seperator *sep) +{ + Client *target = c->GetTarget()->CastToClient(); + if (!c->GetTarget() || !c->GetTarget()->IsClient()) { + target = c; + } + + if (sep->IsNumber(1)) { + int timer_id = atoi(sep->arg[1]); + c->Message(Chat::White, "Reset of disc timer %i for %s", timer_id, c->GetName()); + c->ResetDisciplineTimer(timer_id); + } + else if (!strcasecmp(sep->arg[1], "all")) { + c->Message(Chat::White, "Reset all disc timers for %s", c->GetName()); + c->ResetAllDisciplineTimers(); + } + else { + c->Message(Chat::White, "usage: #resetdisc_timer [all | timer_id]"); + } +} + void command_reloadaa(Client *c, const Seperator *sep) { c->Message(Chat::White, "Reloading Alternate Advancement Data..."); zone->LoadAlternateAdvancement(); diff --git a/zone/command.h b/zone/command.h index bc9d06ff8..cb3f5ea04 100644 --- a/zone/command.h +++ b/zone/command.h @@ -256,6 +256,7 @@ void command_reloadzps(Client *c, const Seperator *sep); void command_repop(Client *c, const Seperator *sep); void command_resetaa(Client* c,const Seperator *sep); void command_resetaa_timer(Client *c, const Seperator *sep); +void command_resetdisc_timer(Client *c, const Seperator *sep); void command_revoke(Client *c, const Seperator *sep); void command_roambox(Client *c, const Seperator *sep); void command_rules(Client *c, const Seperator *sep); diff --git a/zone/mob.cpp b/zone/mob.cpp index 60d8d8839..ec4c719a9 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3488,59 +3488,6 @@ void Mob::SetNimbusEffect(uint32 nimbus_effect) } } -void Mob::TryTriggerOnCast(uint32 spell_id, bool aa_trigger) -{ - if(!IsValidSpell(spell_id)) - return; - - if (aabonuses.SpellTriggers[0] || spellbonuses.SpellTriggers[0] || itembonuses.SpellTriggers[0]){ - - for(int i = 0; i < MAX_SPELL_TRIGGER; i++){ - - if(aabonuses.SpellTriggers[i] && IsClient()) - TriggerOnCast(aabonuses.SpellTriggers[i], spell_id,1); - - if(spellbonuses.SpellTriggers[i]) - TriggerOnCast(spellbonuses.SpellTriggers[i], spell_id,0); - - if(itembonuses.SpellTriggers[i]) - TriggerOnCast(spellbonuses.SpellTriggers[i], spell_id,0); - } - } -} - -void Mob::TriggerOnCast(uint32 focus_spell, uint32 spell_id, bool aa_trigger) -{ - if (!IsValidSpell(focus_spell) || !IsValidSpell(spell_id)) - return; - - uint32 trigger_spell_id = 0; - - if (aa_trigger && IsClient()) { - // focus_spell = aaid - auto rank = zone->GetAlternateAdvancementRank(focus_spell); - if (rank) - trigger_spell_id = CastToClient()->CalcAAFocus(focusTriggerOnCast, *rank, spell_id); - - if (IsValidSpell(trigger_spell_id) && GetTarget()) - SpellFinished(trigger_spell_id, GetTarget(), EQ::spells::CastingSlot::Item, 0, -1, - spells[trigger_spell_id].ResistDiff); - } - - else { - trigger_spell_id = CalcFocusEffect(focusTriggerOnCast, focus_spell, spell_id); - - if (IsValidSpell(trigger_spell_id) && GetTarget()) { - SpellFinished(trigger_spell_id, GetTarget(), EQ::spells::CastingSlot::Item, 0, -1, - spells[trigger_spell_id].ResistDiff); - CheckNumHitsRemaining(NumHit::MatchingSpells, -1, focus_spell); - } - } -} - - - - bool Mob::TrySpellTrigger(Mob *target, uint32 spell_id, int effect) { if (!target || !IsValidSpell(spell_id)) diff --git a/zone/mob.h b/zone/mob.h index 6bc9b8a8f..128c99f55 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -791,8 +791,8 @@ public: bool TryDeathSave(); bool TryDivineSave(); void DoBuffWearOffEffect(uint32 index); - void TryTriggerOnCast(uint32 spell_id, bool aa_trigger); - void TriggerOnCast(uint32 focus_spell, uint32 spell_id, bool aa_trigger); + void TryTriggerOnCastFocusEffect(focusType type, uint16 spell_id); + bool TryTriggerOnCastProc(uint16 focusspellid, uint16 spell_id, uint16 proc_spellid); bool TrySpellTrigger(Mob *target, uint32 spell_id, int effect); void TryTriggerOnValueAmount(bool IsHP = false, bool IsMana = false, bool IsEndur = false, bool IsPet = false); void TryTwincast(Mob *caster, Mob *target, uint32 spell_id); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 037db0a95..d947d6845 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -4463,7 +4463,7 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) when the next valid focus effect is found. */ - if (IsFocusEffect(0, 0, true, effect) || (effect == SE_TriggerOnCast)) { + if (IsFocusEffect(0, 0, true, effect)) { FocusCount++; // If limit found on prior check next, else end loop. if (FocusCount > 1) { @@ -5504,6 +5504,125 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo return (value * lvlModifier / 100); } +void Mob::TryTriggerOnCastFocusEffect(focusType type, uint16 spell_id) +{ + if (IsBardSong(spell_id)) { + return; + } + + if (!IsValidSpell(spell_id)) { + return; + } + + int32 focus_spell_id = 0; + int32 proc_spellid = 0; + + // item focus + if (IsClient() && itembonuses.FocusEffects[type]) { + const EQ::ItemData *temp_item = nullptr; + + for (int x = EQ::invslot::EQUIPMENT_BEGIN; x <= EQ::invslot::EQUIPMENT_END; x++) { + temp_item = nullptr; + EQ::ItemInstance *ins = CastToClient()->GetInv().GetItem(x); + if (!ins) { + continue; + } + temp_item = ins->GetItem(); + if (temp_item && temp_item->Focus.Effect > 0 && IsValidSpell(temp_item->Focus.Effect)) { + focus_spell_id = temp_item->Focus.Effect; + if (!IsEffectInSpell(focus_spell_id, SE_TriggerOnCast)) { + continue; + } + + proc_spellid = CalcFocusEffect(type, focus_spell_id, spell_id); + if (proc_spellid) { + TryTriggerOnCastProc(focus_spell_id, spell_id, proc_spellid); + } + } + + for (int y = EQ::invaug::SOCKET_BEGIN; y <= EQ::invaug::SOCKET_END; ++y) { + EQ::ItemInstance *aug = ins->GetAugment(y); + if (aug) { + const EQ::ItemData *temp_item_aug = aug->GetItem(); + if (temp_item_aug && temp_item_aug->Focus.Effect > 0 && IsValidSpell(temp_item_aug->Focus.Effect)) { + focus_spell_id = temp_item_aug->Focus.Effect; + + if (!IsEffectInSpell(focus_spell_id, SE_TriggerOnCast)) { + continue; + } + + proc_spellid = CalcFocusEffect(type, focus_spell_id, spell_id); + if (proc_spellid) { + TryTriggerOnCastProc(focus_spell_id, spell_id, proc_spellid); + } + } + } + } + } + } + + // Spell Focus + if (spellbonuses.FocusEffects[type]) { + int buff_slot = 0; + for (buff_slot = 0; buff_slot < GetMaxTotalSlots(); buff_slot++) { + focus_spell_id = buffs[buff_slot].spellid; + if (!IsValidSpell(focus_spell_id)) { + continue; + } + + if (!IsEffectInSpell(focus_spell_id, SE_TriggerOnCast)) { + continue; + } + + proc_spellid = CalcFocusEffect(type, focus_spell_id, spell_id); + if (proc_spellid) { + TryTriggerOnCastProc(focus_spell_id, spell_id, proc_spellid); + CheckNumHitsRemaining(NumHit::MatchingSpells, buff_slot); + } + } + } + + // Only use of this focus per AA effect. + if (IsClient() && aabonuses.FocusEffects[type]) { + for (const auto &aa : aa_ranks) { + auto ability_rank = zone->GetAlternateAdvancementAbilityAndRank(aa.first, aa.second.first); + auto ability = ability_rank.first; + auto rank = ability_rank.second; + + if (!ability) { + continue; + } + + if (rank->effects.empty()) { + continue; + } + + proc_spellid = CastToClient()->CalcAAFocus(type, *rank, spell_id); + if (proc_spellid) { + TryTriggerOnCastProc(0, spell_id, proc_spellid); + } + } + } +} + +bool Mob::TryTriggerOnCastProc(uint16 focusspellid, uint16 spell_id, uint16 proc_spellid) +{ + // We confirm spell_id and focuspellid are valid before passing into this. + if (IsValidSpell(proc_spellid) && spell_id != focusspellid && spell_id != proc_spellid) { + Mob* proc_target = GetTarget(); + if (proc_target) { + SpellFinished(proc_spellid, proc_target, EQ::spells::CastingSlot::Item, 0, -1, spells[proc_spellid].ResistDiff); + return true; + } + // Edge cases where proc spell does not require a target such as PBAE, allows proc to still occur even if target potentially dead. Live spells exist with PBAE procs. + else if (!SpellRequiresTarget(proc_spellid)) { + SpellFinished(proc_spellid, this, EQ::spells::CastingSlot::Item, 0, -1, spells[proc_spellid].ResistDiff); + return true; + } + } + return false; +} + uint16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) { if (IsBardSong(spell_id)) diff --git a/zone/spells.cpp b/zone/spells.cpp index 72ef70e40..7b7d894e3 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1384,11 +1384,11 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo TrySympatheticProc(target, spell_id); } - TryOnSpellFinished(this, target, spell_id); + TryOnSpellFinished(this, target, spell_id); //Use for effects that should be checked after SpellFinished is completed. TryTwincast(this, target, spell_id); - TryTriggerOnCast(spell_id, 0); + TryTriggerOnCastFocusEffect(focusTriggerOnCast, spell_id); if(DeleteChargeFromSlot >= 0) CastToClient()->DeleteItemInInventory(DeleteChargeFromSlot, 1, true); From d2706701458dd58c928949886d4fe32f28cd27bd Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 1 Aug 2021 14:47:17 -0400 Subject: [PATCH 142/624] [Spells] Update for SPA 403 and 404 (#1482) * Update for SPA403 and 404 Update SPA SE_LimitSpellClass: 403 SPA SE_LimitSpellSubclass: 404 Now use spell table values from column 221 and 222 respectively. Unknown what the values mean in these fields, but at least live spells work properly. Added FocusLImitInclude Enum to improved focus effect function readability. * Formatting Co-authored-by: Akkadius --- common/shareddb.cpp | 2 + common/spdat.h | 29 +- zone/mob.h | 1 - zone/spell_effects.cpp | 1250 +++++++++++++++++++++------------------- 4 files changed, 681 insertions(+), 601 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 33deb7fe8..258450200 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1883,6 +1883,8 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { sp[tempid].override_crit_chance = atoi(row[217]); sp[tempid].aemaxtargets = atoi(row[218]); sp[tempid].no_heal_damage_item_mod = atoi(row[219]); + sp[tempid].spell_class = atoi(row[221]); + sp[tempid].spell_subclass = atoi(row[222]); sp[tempid].persistdeath = atoi(row[224]) != 0; sp[tempid].min_dist = atof(row[227]); sp[tempid].min_dist_mod = atof(row[228]); diff --git a/common/spdat.h b/common/spdat.h index ea8524f82..089d593d5 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -170,6 +170,25 @@ const int Z_AGGRO=10; const uint32 MobAISpellRange=100; // max range of buffs +enum FocusLimitIncludes { + IncludeExistsSELimitResist = 0, + IncludeFoundSELimitResist = 1, + IncludeExistsSELimitSpell = 2, + IncludeFoundSELimitSpell = 3, + IncludeExistsSELimitEffect = 4, + IncludeFoundSELimitEffect = 5, + IncludeExistsSELimitTarget = 6, + IncludeFoundSELimitTarget = 7, + IncludeExistsSELimitSpellGroup = 8, + IncludeFoundSELimitSpellGroup = 9, + IncludeExistsSELimitCastingSkill = 10, + IncludeFoundSELimitCastingSkill = 11, + IncludeExistsSELimitSpellClass = 12, + IncludeFoundSELimitSpellClass = 13, + IncludeExistsSELimitSpellSubclass = 14, + IncludeFoundSELimitSpellSubclass = 15 +}; + enum SpellTypes : uint32 { SpellType_Nuke = (1 << 0), @@ -746,8 +765,8 @@ typedef enum { #define SE_HealGroupFromMana 400 // implemented - Drains mana and heals for each point of mana drained #define SE_ManaDrainWithDmg 401 // implemented - Deals damage based on the amount of mana drained #define SE_EndDrainWithDmg 402 // implemented - Deals damage for the amount of endurance drained -#define SE_LimitSpellClass 403 // implemented - Limits to specific types of spells (see CheckSpellCategory) -#define SE_LimitSpellSubclass 404 // *not implemented - Limits to specific types of spells (see CheckSpellCategory) [Categories NOT defined yet] +#define SE_LimitSpellClass 403 // implemented, @Ff, 'Spell Category' using table field 'spell_class' that a spell focus can require or exclude, base1: category type, Include: Positive Exclude: Negative +#define SE_LimitSpellSubclass 404 // implemented, @Ff, 'Spell Category Subclass' using table field 'spell_subclass' that a spell focus can require or exclude, base1: category type, Include: Positive Exclude: Negative #define SE_TwoHandBluntBlock 405 // implemented - chance to block attacks when using two hand blunt weapons (similiar to shield block) #define SE_CastonNumHitFade 406 // implemented - casts a spell when a buff fades due to its numhits being depleted #define SE_CastonFocusEffect 407 // implemented - casts a spell if focus limits are met (ie triggers when a focus effects is applied) @@ -860,7 +879,7 @@ typedef enum { //#define SE_Endurance_Max_Percent 514 // #define SE_AC_Avoidance_Max_Percent 515 // implemented - stackable avoidance modifier #define SE_AC_Mitigation_Max_Percent 516 // implemented - stackable defense modifier -//#define SE_Attack_Offense_Max_Percent 517 // +//#define SE_Attack_Offense_Max_Percent 517 // #define SE_Attack_Accuracy_Max_Percent 518 // implemented - stackable accurary modifer //#define SE_Luck_Amount 519 // //#define SE_Luck_Percent 520 // @@ -1018,8 +1037,8 @@ struct SPDat_Spell_Struct /* 218 */ int aemaxtargets; //Is used for various AE effects -- MAX_TARGETS /* 219 */ int no_heal_damage_item_mod; // -- NO_HEAL_DAMAGE_ITEM_MOD /* 220 */ //int caster_requirement_id; // -- CASTER_REQUIREMENT_ID -/* 221 */ //int spell_class; // -- SPELL_CLASS -/* 222 */ //int spell_subclass; // -- SPELL_SUBCLASS +/* 221 */ int spell_class; // -- SPELL_CLASS +/* 222 */ int spell_subclass; // -- SPELL_SUBCLASS /* 223 */ //int ai_valid_targets; // -- AI_VALID_TARGETS /* 224 */ bool persistdeath; // buff doesn't get stripped on death -- NO_STRIP_ON_DEATH /* 225 */ //float base_effects_focus_slope; // -- BASE_EFFECTS_FOCUS_SLOPE diff --git a/zone/mob.h b/zone/mob.h index 128c99f55..5a548dae3 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -349,7 +349,6 @@ public: bool TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed = 1.5f); void ResourceTap(int32 damage, uint16 spell_id); void TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker); - bool CheckSpellCategory(uint16 spell_id, int category_id, int effect_id); void CalcDestFromHeading(float heading, float distance, float MaxZDiff, float StartX, float StartY, float &dX, float &dY, float &dZ); void BeamDirectional(uint16 spell_id, int16 resist_adjust); void ConeDirectional(uint16 spell_id, int16 resist_adjust); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index d947d6845..4813f3b8f 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -4500,9 +4500,9 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) LimitFailure = true; } else { - LimitInclude[0] = true; + LimitInclude[IncludeExistsSELimitResist] = true; if (spell.resisttype == base1) // Include - LimitInclude[1] = true; + LimitInclude[IncludeFoundSELimitResist] = true; } break; @@ -4552,9 +4552,9 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) LimitFailure = true; } else { - LimitInclude[2] = true; + LimitInclude[IncludeExistsSELimitSpell] = true; if (spell_id == base1) // Include - LimitInclude[3] = true; + LimitInclude[IncludeFoundSELimitSpell] = true; } break; @@ -4570,15 +4570,15 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) LimitFailure = true; } else { - LimitInclude[4] = true; + LimitInclude[IncludeExistsSELimitEffect] = true; // they use 33 here for all classes ... unsure if the type check is really needed if (base1 == SE_SummonPet && type == focusReagentCost) { if (IsSummonPetSpell(spell_id) || IsSummonSkeletonSpell(spell_id)) - LimitInclude[5] = true; + LimitInclude[IncludeFoundSELimitEffect] = true; } else { if (IsEffectInSpell(spell_id, base1)) // Include - LimitInclude[5] = true; + LimitInclude[IncludeFoundSELimitEffect] = true; } } break; @@ -4612,9 +4612,9 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) LimitFailure = true; } else { - LimitInclude[6] = true; + LimitInclude[IncludeExistsSELimitTarget] = true; if (base1 == spell.targettype) // Include - LimitInclude[7] = true; + LimitInclude[IncludeFoundSELimitTarget] = true; } break; @@ -4644,33 +4644,33 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) LimitFailure = true; } else { - LimitInclude[10] = true; + LimitInclude[IncludeExistsSELimitCastingSkill] = true; if (base1 == spell.skill) - LimitInclude[11] = true; + LimitInclude[IncludeFoundSELimitCastingSkill] = true; } break; case SE_LimitSpellClass: if (base1 < 0) { // Exclude - if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass)) - return (0); + if (-base1 == spell.spell_class); + LimitFailure = true; } else { - LimitInclude[12] = true; - if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass)) // Include - LimitInclude[13] = true; + LimitInclude[IncludeExistsSELimitSpellClass] = true; + if (base1 == spell.spell_class) // Include + LimitInclude[IncludeFoundSELimitSpellClass] = true; } break; case SE_LimitSpellSubclass: if (base1 < 0) { // Exclude - if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass)) - return (0); + if (-base1 == spell.spell_subclass); + LimitFailure = true; } else { - LimitInclude[14] = true; - if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass)) // Include - LimitInclude[15] = true; + LimitInclude[IncludeExistsSELimitSpellSubclass] = true; + if (base1 == spell.spell_subclass) // Include + LimitInclude[IncludeFoundSELimitSpellSubclass] = true; } break; @@ -4934,17 +4934,19 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo 'casterid' is the casterid of the caster of spell_id, used when spell_id is cast on a target with a focus effect that is checked by incoming spell. */ - if (!IsValidSpell(focus_id) || !IsValidSpell(spell_id)) + if (!IsValidSpell(focus_id) || !IsValidSpell(spell_id)) { return 0; + } const SPDat_Spell_Struct &focus_spell = spells[focus_id]; - const SPDat_Spell_Struct &spell = spells[spell_id]; + const SPDat_Spell_Struct &spell = spells[spell_id]; - int32 value = 0; - int lvlModifier = 100; - int spell_level = 0; - int lvldiff = 0; - int32 Caston_spell_id = 0; + + int16 value = 0; + int lvlModifier = 100; + int spell_level = 0; + int lvldiff = 0; + uint32 Caston_spell_id = 0; bool LimitInclude[MaxLimitInclude] = {false}; /* Certain limits require only one of several Include conditions to be true. Ie. Add damage to fire OR ice @@ -4964,541 +4966,650 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo switch (focus_spell.effectid[i]) { - case SE_Blank: - break; - - case SE_LimitResist: - if (focus_spell.base[i] < 0) { - if (spell.resisttype == -focus_spell.base[i]) // Exclude - return 0; - } else { - LimitInclude[0] = true; - if (spell.resisttype == focus_spell.base[i]) // Include - LimitInclude[1] = true; - } - break; - - case SE_LimitInstant: - if (focus_spell.base[i] == 1 && spell.buffduration) // Fail if not instant - return 0; - if (focus_spell.base[i] == 0 && (spell.buffduration == 0)) // Fail if instant - return 0; - - break; - - case SE_LimitMaxLevel: - if (IsNPC()) + case SE_Blank: break; - spell_level = spell.classes[(GetClass() % 17) - 1]; - lvldiff = spell_level - focus_spell.base[i]; - // every level over cap reduces the effect by focus_spell.base2[i] percent unless from a clicky - // when ItemCastsUseFocus is true - if (lvldiff > 0 && (spell_level <= RuleI(Character, MaxLevel) || - RuleB(Character, ItemCastsUseFocus) == false)) { - if (focus_spell.base2[i] > 0) { - lvlModifier -= focus_spell.base2[i] * lvldiff; - if (lvlModifier < 1) + + case SE_LimitResist: + if (focus_spell.base[i] < 0) { + if (spell.resisttype == -focus_spell.base[i]) { // Exclude return 0; - } else - return 0; - } - break; - - case SE_LimitMinLevel: - if (IsNPC()) - break; - if (spell.classes[(GetClass() % 17) - 1] < focus_spell.base[i]) - return (0); - break; - - case SE_LimitCastTimeMin: - if (spells[spell_id].cast_time < (uint16)focus_spell.base[i]) - return (0); - break; - - case SE_LimitCastTimeMax: - if (spells[spell_id].cast_time > (uint16)focus_spell.base[i]) - return (0); - break; - - case SE_LimitSpell: - if (focus_spell.base[i] < 0) { // Exclude - if (spell_id == -focus_spell.base[i]) - return (0); - } else { - LimitInclude[2] = true; - if (spell_id == focus_spell.base[i]) // Include - LimitInclude[3] = true; - } - break; - - case SE_LimitMinDur: - if (focus_spell.base[i] > - CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration)) - return (0); - break; - - case SE_LimitEffect: - if (focus_spell.base[i] < 0) { - if (IsEffectInSpell(spell_id, -focus_spell.base[i])) // Exclude - return 0; - } else { - LimitInclude[4] = true; - if (IsEffectInSpell(spell_id, focus_spell.base[i])) // Include - LimitInclude[5] = true; - } - break; - - case SE_LimitSpellType: - switch (focus_spell.base[i]) { - case 0: - if (!IsDetrimentalSpell(spell_id)) - return 0; - break; - case 1: - if (!IsBeneficialSpell(spell_id)) - return 0; - break; - default: - LogInfo("CalcFocusEffect: unknown limit spelltype [{}]", - focus_spell.base[i]); - } - break; - - case SE_LimitManaMin: - if (spell.mana < focus_spell.base[i]) - return 0; - break; - - case SE_LimitManaMax: - if (spell.mana > focus_spell.base[i]) - return 0; - break; - - case SE_LimitTarget: - if (focus_spell.base[i] < 0) { - if (-focus_spell.base[i] == spell.targettype) // Exclude - return 0; - } else { - LimitInclude[6] = true; - if (focus_spell.base[i] == spell.targettype) // Include - LimitInclude[7] = true; - } - break; - - case SE_LimitCombatSkills: - if (focus_spell.base[i] == 0 && - (IsCombatSkill(spell_id) || IsCombatProc(spell_id))) // Exclude Discs / Procs - return 0; - else if (focus_spell.base[i] == 1 && - (!IsCombatSkill(spell_id) || !IsCombatProc(spell_id))) // Exclude Spells - return 0; - - break; - - case SE_LimitSpellGroup: - if (focus_spell.base[i] < 0) { - if (-focus_spell.base[i] == spell.spellgroup) // Exclude - return 0; - } else { - LimitInclude[8] = true; - if (focus_spell.base[i] == spell.spellgroup) // Include - LimitInclude[9] = true; - } - break; - - case SE_LimitCastingSkill: - if (focus_spell.base[i] < 0) { - if (-focus_spell.base[i] == spell.skill) - return 0; - } else { - LimitInclude[10] = true; - if (focus_spell.base[i] == spell.skill) - LimitInclude[11] = true; - } - break; - - case SE_LimitClass: - // Do not use this limit more then once per spell. If multiple class, treat value like items - // would. - if (!PassLimitClass(focus_spell.base[i], GetClass())) - return 0; - break; - - case SE_LimitRace: - if (focus_spell.base[i] != GetRace()) - return 0; - break; - - case SE_LimitUseMin: - if (focus_spell.base[i] > spell.numhits) - return 0; - break; - - case SE_LimitUseType: - if (focus_spell.base[i] != spell.numhitstype) - return 0; - break; - - case SE_CastonFocusEffect: - if (focus_spell.base[i] > 0) - Caston_spell_id = focus_spell.base[i]; - break; - - case SE_LimitSpellClass: - if (focus_spell.base[i] < 0) { // Exclude - if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellClass)) - return (0); - } else { - LimitInclude[12] = true; - if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellClass)) // Include - LimitInclude[13] = true; - } - break; - - case SE_LimitSpellSubclass: - if (focus_spell.base[i] < 0) { // Exclude - if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellSubclass)) - return (0); - } else { - LimitInclude[14] = true; - if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellSubclass)) // Include - LimitInclude[15] = true; - } - break; - - case SE_Ff_Same_Caster://hmm do i need to pass casterid from buff slot here - if (focus_spell.base[i] == 0) { - if (casterid == GetID()) - return 0;//Mob casting is same as target, fail if you are casting on yourself. - } - else if (focus_spell.base[i] == 1) { - if (casterid != GetID()) - return 0;//Mob casting is not same as target, fail if you are not casting on yourself. - } - break; - - case SE_Ff_CasterClass: - // Do not use this limit more then once per spell. If multiple class, treat value like items would. - if (!PassLimitClass(focus_spell.base[i], GetClass())) - return 0; - break; - - // handle effects - case SE_ImprovedDamage: - if (type == focusImprovedDamage) { - // This is used to determine which focus should be used for the random calculation - if (best_focus) { - // If the spell contains a value in the base2 field then that is the max value - if (focus_spell.base2[i] != 0) { - value = focus_spell.base2[i]; } - // If the spell does not contain a base2 value, then its a straight non random - // value - else { - value = focus_spell.base[i]; - } - } - // Actual focus calculation starts here - else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; - } else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); - } - } - break; - - case SE_ImprovedDamage2: - if (type == focusImprovedDamage2) { - if (best_focus) { - if (focus_spell.base2[i] != 0) { - value = focus_spell.base2[i]; - } - else { - value = focus_spell.base[i]; - } - } - else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; - } else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); - } - } - break; - - case SE_ImprovedHeal: - if (type == focusImprovedHeal) { - if (best_focus) { - if (focus_spell.base2[i] != 0) { - value = focus_spell.base2[i]; - } else { - value = focus_spell.base[i]; - } - } else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; - } else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); - } - } - break; - - case SE_ReduceManaCost: - if (type == focusManaCost) { - if (best_focus) { - if (focus_spell.base2[i] != 0) { - value = focus_spell.base2[i]; - } else { - value = focus_spell.base[i]; - } - } else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; - } else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); - } - } - break; - - case SE_IncreaseSpellHaste: - if (type == focusSpellHaste && focus_spell.base[i] > value) - value = focus_spell.base[i]; - break; - - case SE_IncreaseSpellDuration: - if (type == focusSpellDuration && focus_spell.base[i] > value) - value = focus_spell.base[i]; - break; - - case SE_SpellDurationIncByTic: - if (type == focusSpellDurByTic && focus_spell.base[i] > value) - value = focus_spell.base[i]; - break; - - case SE_SwarmPetDuration: - if (type == focusSwarmPetDuration && focus_spell.base[i] > value) - value = focus_spell.base[i]; - break; - - case SE_IncreaseRange: - if (type == focusRange && focus_spell.base[i] > value) - value = focus_spell.base[i]; - break; - - case SE_ReduceReagentCost: - if (type == focusReagentCost && focus_spell.base[i] > value) - value = focus_spell.base[i]; - break; - - case SE_PetPowerIncrease: - if (type == focusPetPower && focus_spell.base[i] > value) - value = focus_spell.base[i]; - break; - - case SE_SpellResistReduction: - if (type == focusResistRate) { - if (best_focus) { - if (focus_spell.base2[i] != 0) { - value = focus_spell.base2[i]; - } else { - value = focus_spell.base[i]; - } - } else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; - } else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); - } - } - break; - - case SE_SpellHateMod: - if (type == focusSpellHateMod) { - if (value != 0) { - if (value > 0) { - if (focus_spell.base[i] > value) - value = focus_spell.base[i]; - } else { - if (focus_spell.base[i] < value) - value = focus_spell.base[i]; - } - } else - value = focus_spell.base[i]; - } - break; - - case SE_ReduceReuseTimer: - if (type == focusReduceRecastTime) - value = focus_spell.base[i] / 1000; - break; - - case SE_TriggerOnCast: - if (type == focusTriggerOnCast) { - if (zone->random.Roll(focus_spell.base[i])) - value = focus_spell.base2[i]; - else - value = 0; - } - break; - - case SE_BlockNextSpellFocus: - if (type == focusBlockNextSpell) { - if (zone->random.Roll(focus_spell.base[i])) - value = 1; - } - break; - - case SE_SympatheticProc: - if (type == focusSympatheticProc) { - value = focus_id; - } - break; - - case SE_FcSpellVulnerability: - if (type == focusSpellVulnerability) { - if (best_focus) { - if (focus_spell.base2[i] != 0) - value = focus_spell.base2[i]; //max damage - else - value = focus_spell.base[i]; //min damage - } else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; //If no max damage set, then default to min damage - } else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); //else random for value - } - } - break; - - case SE_Fc_Spell_Damage_Pct_IncomingPC: - if (type == focusFcSpellDamagePctIncomingPC) { - if (best_focus) { - if (focus_spell.base2[i] != 0) - value = focus_spell.base2[i]; //max damage - else - value = focus_spell.base[i]; //min damage - } - else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; //If no max damage set, then default to min damage } else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); //else random for value + LimitInclude[IncludeExistsSELimitResist] = true; + if (spell.resisttype == focus_spell.base[i]) { // Include + LimitInclude[IncludeFoundSELimitResist] = true; + } } - } - break; + break; - case SE_FcTwincast: - if (type == focusTwincast && !IsEffectInSpell(spell_id, SE_TwinCastBlocker)) - value = focus_spell.base[i]; - break; - - case SE_FcDamageAmt: - if (type == focusFcDamageAmt) - value = focus_spell.base[i]; - break; - - case SE_FcDamageAmt2: - if (type == focusFcDamageAmt2) - value = focus_spell.base[i]; - break; - - case SE_FcDamageAmtCrit: - if (type == focusFcDamageAmtCrit) - value = focus_spell.base[i]; - break; - - case SE_FcDamageAmtIncoming: - if (type == focusFcDamageAmtIncoming) - value = focus_spell.base[i]; - break; - - case SE_Fc_Spell_Damage_Amt_IncomingPC: - if (type == focusFcSpellDamageAmtIncomingPC) - value = focus_spell.base[i]; - break; - - case SE_FcHealAmtIncoming: - if (type == focusFcHealAmtIncoming) - value = focus_spell.base[i]; - break; - - case SE_FcDamagePctCrit: - if (type == focusFcDamagePctCrit) - value = focus_spell.base[i]; - break; - - case SE_FcHealPctCritIncoming: - if (type == focusFcHealPctCritIncoming) - value = focus_spell.base[i]; - break; - - case SE_FcHealAmtCrit: - if (type == focusFcHealAmtCrit) - value = focus_spell.base[i]; - break; - - case SE_FcHealAmt: - if (type == focusFcHealAmt) - value = focus_spell.base[i]; - break; - - case SE_FcHealPctIncoming: - if (type == focusFcHealPctIncoming) - value = focus_spell.base[i]; - break; - - case SE_FcBaseEffects: - if (type == focusFcBaseEffects) - value = focus_spell.base[i]; - break; - - case SE_FcIncreaseNumHits: - if (type == focusIncreaseNumHits) - value = focus_spell.base[i]; - break; - - case SE_FcLimitUse: - if (type == focusFcLimitUse) - value = focus_spell.base[i]; - break; - - case SE_FcMute: - if (type == focusFcMute) - value = focus_spell.base[i]; - break; - - case SE_FcStunTimeMod: - if (type == focusFcStunTimeMod) - value = focus_spell.base[i]; - break; - - case SE_FcTimerRefresh: - if (type == focusFcTimerRefresh) - value = focus_spell.base[i]; - break; - - case SE_Fc_Cast_Spell_On_Land: - if (type == focusFcCastSpellOnLand) { - if (zone->random.Roll(focus_spell.base[i])) { - value = focus_spell.base2[i]; + case SE_LimitInstant: + if (focus_spell.base[i] == 1 && spell.buffduration) { // Fail if not instant + return 0; + } + if (focus_spell.base[i] == 0 && (spell.buffduration == 0)) { // Fail if instant + return 0; + } + + break; + + case SE_LimitMaxLevel: + if (IsNPC()) { + break; + } + spell_level = spell.classes[(GetClass() % 17) - 1]; + lvldiff = spell_level - focus_spell.base[i]; + // every level over cap reduces the effect by focus_spell.base2[i] percent unless from a clicky + // when ItemCastsUseFocus is true + if (lvldiff > 0 && (spell_level <= RuleI(Character, MaxLevel) || + RuleB(Character, ItemCastsUseFocus) == false)) { + if (focus_spell.base2[i] > 0) { + lvlModifier -= focus_spell.base2[i] * lvldiff; + if (lvlModifier < 1) { + return 0; + } + } + else { + return 0; + } + } + break; + + case SE_LimitMinLevel: + if (IsNPC()) { + break; + } + if (spell.classes[(GetClass() % 17) - 1] < focus_spell.base[i]) { + return (0); + } + break; + + case SE_LimitCastTimeMin: + if (spells[spell_id].cast_time < (uint16) focus_spell.base[i]) { + return (0); + } + break; + + case SE_LimitCastTimeMax: + if (spells[spell_id].cast_time > (uint16) focus_spell.base[i]) { + return (0); + } + break; + + case SE_LimitSpell: + if (focus_spell.base[i] < 0) { // Exclude + if (spell_id == -focus_spell.base[i]) { + return (0); + } + } + else { + LimitInclude[IncludeExistsSELimitSpell] = true; + if (spell_id == focus_spell.base[i]) { // Include + LimitInclude[IncludeFoundSELimitSpell] = true; + } + } + break; + + case SE_LimitMinDur: + if (focus_spell.base[i] > + CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration)) { + return (0); + } + break; + + case SE_LimitEffect: + if (focus_spell.base[i] < 0) { + if (IsEffectInSpell(spell_id, -focus_spell.base[i])) { // Exclude + return 0; + } + } + else { + LimitInclude[IncludeExistsSELimitEffect] = true; + if (IsEffectInSpell(spell_id, focus_spell.base[i])) { // Include + LimitInclude[IncludeFoundSELimitEffect] = true; + } + } + break; + + case SE_LimitSpellType: + switch (focus_spell.base[i]) { + case 0: + if (!IsDetrimentalSpell(spell_id)) { + return 0; + } + break; + case 1: + if (!IsBeneficialSpell(spell_id)) { + return 0; + } + break; + default: + LogInfo("CalcFocusEffect: unknown limit spelltype [{}]", + focus_spell.base[i]); + } + break; + + case SE_LimitManaMin: + if (spell.mana < focus_spell.base[i]) { + return 0; + } + break; + + case SE_LimitManaMax: + if (spell.mana > focus_spell.base[i]) { + return 0; + } + break; + + case SE_LimitTarget: + if (focus_spell.base[i] < 0) { + if (-focus_spell.base[i] == spell.targettype) { // Exclude + return 0; + } + } + else { + LimitInclude[IncludeExistsSELimitTarget] = true; + if (focus_spell.base[i] == spell.targettype) { // Include + LimitInclude[IncludeFoundSELimitTarget] = true; + } + } + break; + + case SE_LimitCombatSkills: + if (focus_spell.base[i] == 0 && + (IsCombatSkill(spell_id) || IsCombatProc(spell_id))) { // Exclude Discs / Procs + return 0; + } + else if (focus_spell.base[i] == 1 && + (!IsCombatSkill(spell_id) || !IsCombatProc(spell_id))) { // Exclude Spells + return 0; + } + + break; + + case SE_LimitSpellGroup: + if (focus_spell.base[i] < 0) { + if (-focus_spell.base[i] == spell.spellgroup) { // Exclude + return 0; + } + } + else { + LimitInclude[IncludeExistsSELimitSpellGroup] = true; + if (focus_spell.base[i] == spell.spellgroup) { // Include + LimitInclude[IncludeFoundSELimitSpellGroup] = true; + } + } + break; + + case SE_LimitCastingSkill: + if (focus_spell.base[i] < 0) { + if (-focus_spell.base[i] == spell.skill) { + return 0; + } + } + else { + LimitInclude[IncludeExistsSELimitCastingSkill] = true; + if (focus_spell.base[i] == spell.skill) { + LimitInclude[IncludeFoundSELimitCastingSkill] = true; + } + } + break; + + case SE_LimitClass: + // Do not use this limit more then once per spell. If multiple class, treat value like items + // would. + if (!PassLimitClass(focus_spell.base[i], GetClass())) { + return 0; + } + break; + + case SE_LimitRace: + if (focus_spell.base[i] != GetRace()) { + return 0; + } + break; + + case SE_LimitUseMin: + if (focus_spell.base[i] > spell.numhits) { + return 0; + } + break; + + case SE_LimitUseType: + if (focus_spell.base[i] != spell.numhitstype) { + return 0; + } + break; + + case SE_CastonFocusEffect: + if (focus_spell.base[i] > 0) { + Caston_spell_id = focus_spell.base[i]; + } + break; + + case SE_LimitSpellClass: + if (focus_spell.base[i] < 0) { // Exclude + if (-focus_spell.base[i] == spell.spell_class) { + return (0); + } + } + else { + LimitInclude[IncludeExistsSELimitSpellClass] = true; + if (focus_spell.base[i] == spell.spell_class) { // Include + LimitInclude[IncludeFoundSELimitSpellClass] = true; + } + } + break; + + case SE_LimitSpellSubclass: + if (focus_spell.base[i] < 0) { // Exclude + if (-focus_spell.base[i] == spell.spell_subclass) { + return (0); + } + } + else { + LimitInclude[IncludeExistsSELimitSpellSubclass] = true; + if (focus_spell.base[i] == spell.spell_subclass) { // Include + LimitInclude[IncludeFoundSELimitSpellSubclass] = true; + } + } + break; + + case SE_Ff_Same_Caster://hmm do i need to pass casterid from buff slot here + if (focus_spell.base[i] == 0) { + if (casterid == GetID()) { + return 0; + }//Mob casting is same as target, fail if you are casting on yourself. + } + else if (focus_spell.base[i] == 1) { + if (casterid != GetID()) { + return 0; + }//Mob casting is not same as target, fail if you are not casting on yourself. + } + break; + + case SE_Ff_CasterClass: + // Do not use this limit more then once per spell. If multiple class, treat value like items would. + if (!PassLimitClass(focus_spell.base[i], GetClass())) { + return 0; + } + break; + + // handle effects + case SE_ImprovedDamage: + if (type == focusImprovedDamage) { + // This is used to determine which focus should be used for the random calculation + if (best_focus) { + // If the spell contains a value in the base2 field then that is the max value + if (focus_spell.base2[i] != 0) { + value = focus_spell.base2[i]; + } + // If the spell does not contain a base2 value, then its a straight non random + // value + else { + value = focus_spell.base[i]; + } + } + // Actual focus calculation starts here + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { + value = focus_spell.base[i]; + } + else { + value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); + } + } + break; + + case SE_ImprovedDamage2: + if (type == focusImprovedDamage2) { + if (best_focus) { + if (focus_spell.base2[i] != 0) { + value = focus_spell.base2[i]; + } + else { + value = focus_spell.base[i]; + } + } + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { + value = focus_spell.base[i]; + } + else { + value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); + } + } + break; + + case SE_ImprovedHeal: + if (type == focusImprovedHeal) { + if (best_focus) { + if (focus_spell.base2[i] != 0) { + value = focus_spell.base2[i]; + } + else { + value = focus_spell.base[i]; + } + } + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { + value = focus_spell.base[i]; + } + else { + value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); + } + } + break; + + case SE_ReduceManaCost: + if (type == focusManaCost) { + if (best_focus) { + if (focus_spell.base2[i] != 0) { + value = focus_spell.base2[i]; + } + else { + value = focus_spell.base[i]; + } + } + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { + value = focus_spell.base[i]; + } + else { + value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); + } + } + break; + + case SE_IncreaseSpellHaste: + if (type == focusSpellHaste && focus_spell.base[i] > value) { + value = focus_spell.base[i]; + } + break; + + case SE_IncreaseSpellDuration: + if (type == focusSpellDuration && focus_spell.base[i] > value) { + value = focus_spell.base[i]; + } + break; + + case SE_SpellDurationIncByTic: + if (type == focusSpellDurByTic && focus_spell.base[i] > value) { + value = focus_spell.base[i]; + } + break; + + case SE_SwarmPetDuration: + if (type == focusSwarmPetDuration && focus_spell.base[i] > value) { + value = focus_spell.base[i]; + } + break; + + case SE_IncreaseRange: + if (type == focusRange && focus_spell.base[i] > value) { + value = focus_spell.base[i]; + } + break; + + case SE_ReduceReagentCost: + if (type == focusReagentCost && focus_spell.base[i] > value) { + value = focus_spell.base[i]; + } + break; + + case SE_PetPowerIncrease: + if (type == focusPetPower && focus_spell.base[i] > value) { + value = focus_spell.base[i]; + } + break; + + case SE_SpellResistReduction: + if (type == focusResistRate) { + if (best_focus) { + if (focus_spell.base2[i] != 0) { + value = focus_spell.base2[i]; + } + else { + value = focus_spell.base[i]; + } + } + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { + value = focus_spell.base[i]; + } + else { + value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); + } + } + break; + + case SE_SpellHateMod: + if (type == focusSpellHateMod) { + if (value != 0) { + if (value > 0) { + if (focus_spell.base[i] > value) { + value = focus_spell.base[i]; + } + } + else { + if (focus_spell.base[i] < value) { + value = focus_spell.base[i]; + } + } + } + else { + value = focus_spell.base[i]; + } + } + break; + + case SE_ReduceReuseTimer: + if (type == focusReduceRecastTime) { + value = focus_spell.base[i] / 1000; + } + break; + + case SE_TriggerOnCast: + if (type == focusTriggerOnCast) { + if (zone->random.Roll(focus_spell.base[i])) { + value = focus_spell.base2[i]; + } + else { + value = 0; + } + } + break; + + case SE_BlockNextSpellFocus: + if (type == focusBlockNextSpell) { + if (zone->random.Roll(focus_spell.base[i])) { + value = 1; + } + } + break; + + case SE_SympatheticProc: + if (type == focusSympatheticProc) { + value = focus_id; + } + break; + + case SE_FcSpellVulnerability: + if (type == focusSpellVulnerability) { + if (best_focus) { + if (focus_spell.base2[i] != 0) { + value = focus_spell.base2[i]; //max damage + } + else { + value = focus_spell.base[i]; + } //min damage + } + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { + value = focus_spell.base[i]; //If no max damage set, then default to min damage + } + else { + value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); //else random for value + } + } + break; + + case SE_Fc_Spell_Damage_Pct_IncomingPC: + if (type == focusFcSpellDamagePctIncomingPC) { + if (best_focus) { + if (focus_spell.base2[i] != 0) { + value = focus_spell.base2[i]; //max damage + } + else { + value = focus_spell.base[i]; + } //min damage + } + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { + value = focus_spell.base[i]; //If no max damage set, then default to min damage + } + else { + value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); //else random for value + } + } + break; + + case SE_FcTwincast: + if (type == focusTwincast && !IsEffectInSpell(spell_id, SE_TwinCastBlocker)) { + value = focus_spell.base[i]; + } + break; + + case SE_FcDamageAmt: + if (type == focusFcDamageAmt) { + value = focus_spell.base[i]; + } + break; + + case SE_FcDamageAmt2: + if (type == focusFcDamageAmt2) { + value = focus_spell.base[i]; + } + break; + + case SE_FcDamageAmtCrit: + if (type == focusFcDamageAmtCrit) { + value = focus_spell.base[i]; + } + break; + + case SE_FcDamageAmtIncoming: + if (type == focusFcDamageAmtIncoming) { + value = focus_spell.base[i]; + } + break; + + case SE_Fc_Spell_Damage_Amt_IncomingPC: + if (type == focusFcSpellDamageAmtIncomingPC) { + value = focus_spell.base[i]; + } + break; + + case SE_FcHealAmtIncoming: + if (type == focusFcHealAmtIncoming) { + value = focus_spell.base[i]; + } + break; + + case SE_FcDamagePctCrit: + if (type == focusFcDamagePctCrit) { + value = focus_spell.base[i]; + } + break; + + case SE_FcHealPctCritIncoming: + if (type == focusFcHealPctCritIncoming) { + value = focus_spell.base[i]; + } + break; + + case SE_FcHealAmtCrit: + if (type == focusFcHealAmtCrit) { + value = focus_spell.base[i]; + } + break; + + case SE_FcHealAmt: + if (type == focusFcHealAmt) { + value = focus_spell.base[i]; + } + break; + + case SE_FcHealPctIncoming: + if (type == focusFcHealPctIncoming) { + value = focus_spell.base[i]; + } + break; + + case SE_FcBaseEffects: + if (type == focusFcBaseEffects) { + value = focus_spell.base[i]; + } + break; + + case SE_FcIncreaseNumHits: + if (type == focusIncreaseNumHits) { + value = focus_spell.base[i]; + } + break; + + case SE_FcLimitUse: + if (type == focusFcLimitUse) { + value = focus_spell.base[i]; + } + break; + + case SE_FcMute: + if (type == focusFcMute) { + value = focus_spell.base[i]; + } + break; + + case SE_FcStunTimeMod: + if (type == focusFcStunTimeMod) { + value = focus_spell.base[i]; + } + break; + + case SE_FcTimerRefresh: + if (type == focusFcTimerRefresh) { + value = focus_spell.base[i]; + } + break; + + case SE_Fc_Cast_Spell_On_Land: + if (type == focusFcCastSpellOnLand) { + if (zone->random.Roll(focus_spell.base[i])) { + value = focus_spell.base2[i]; + } + break; } - break; - } #if EQDEBUG >= 6 - // this spits up a lot of garbage when calculating spell focuses - // since they have all kinds of extra effects on them. - default: - LogInfo("CalcFocusEffect: unknown effectid [{}]", - focus_spell.effectid[i]); + // this spits up a lot of garbage when calculating spell focuses + // since they have all kinds of extra effects on them. + default: + LogInfo("CalcFocusEffect: unknown effectid [{}]", + focus_spell.effectid[i]); #endif } } for (int e = 0; e < MaxLimitInclude; e += 2) { - if (LimitInclude[e] && !LimitInclude[e + 1]) + if (LimitInclude[e] && !LimitInclude[e + 1]) { return 0; + } } if (Caston_spell_id) { - if (IsValidSpell(Caston_spell_id) && (Caston_spell_id != spell_id)) - SpellFinished(Caston_spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[Caston_spell_id].ResistDiff); + if (IsValidSpell(Caston_spell_id) && (Caston_spell_id != spell_id)) { + SpellFinished( + Caston_spell_id, + this, + EQ::spells::CastingSlot::Item, + 0, + -1, + spells[Caston_spell_id].ResistDiff + ); + } } return (value * lvlModifier / 100); @@ -7425,57 +7536,6 @@ void Mob::FocusProcLimitProcess() } } -bool Mob::CheckSpellCategory(uint16 spell_id, int category_id, int effect_id){ - - if (!IsValidSpell(spell_id) || !category_id) - return false; - - int effectid = 0; - int category = 0; - - /*Category ID SE_LimitSpellClass [(+) Include (-) Exclude] - 1 = UNK - 2 = Cures - 3 = Offensive Spells - 4 = UNK - 5 = UNK - 6 = Lifetap - */ - - /*Category ID SE_LimitSpellSubClass [(+) Include (-) Exclude] - 5 = UNK - 8 = UNK - */ - - if (effect_id == SE_LimitSpellClass) { - - switch(category_id) - { - case 2: - if (IsCureSpell(spell_id)) - return true; - break; - - case 3: - if (IsDetrimentalSpell(spell_id)) - return true; - break; - - case 6: - if (spells[spell_id].targettype == ST_Tap || spells[spell_id].targettype == ST_TargetAETap) - return true; - break; - } - } - - else if (effect_id == SE_LimitSpellSubclass) { - //Pending Implementation when category types are figured out. - return false; - } - - return false; -} - void Mob::CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster) { if (IsPowerDistModSpell(spell_id)){ From e71b11fcbab7482553ec28987e000a9fbb4686cb Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 1 Aug 2021 18:45:05 -0400 Subject: [PATCH 143/624] [Spells] New SPA focus effects and focus limits (#1462) * Implemented SPA Duration Pct Implemented new spell effects SE_Duration_HP_Pct 524 SE_Duration_Mana_Pct 525 SE_Duration_Endurance_Pct 526 Consumes 'base1' % of your maximum health/mana/endurance every 6 seconds. 'max' is maximum amount that can be consumed per tic. Additional Functionality Can be used as a heal/gain % by setting the base1 value to a positive. * Implemented SPA Instant Mana/End pct Fixes for SPA 524-526 Implemented SE_Instant_Mana_Pct 522 SE_Instant_Endurance_Pct 523 Extracts 'base1' percent of your maximum mana/endurance, or 'max', whichever is lower. * Implemented: SPA 521 EndAbsorbPctDmg Implemented SE_Endurance_Absorb_Pct_Damage 521 Absorb Damage using Endurance: base1 % (base2 End per 1 HP) Note: Both base1 and base2 need to be divided by 100 for actually value * Implemented SE_HealthTransfer 509 Implemented SE_Health_Transfer 509 'life burn' Consume base2 % of Hit Points to Damage for base % of Hit Points Can be used for heal Act of Valor * Implemented SPA 515,516,518,496 Implemented SE_AC_Avoidance_Max_Percent 515 SE_AC_Mitigation_Max_Percent 516 SE_Attack_Accuracy_Max_Percent 518 Above are stackable defense and offensive mods SE_Critical_Melee_Damage_Mod_Max 496 - This is a non stackable melee critical modifier * Implemented SPA 503 , 505 SE_Melee_Damage_Position_Mod 503 define SE_Damage_Taken_Position_Mod 505 SPA 503 increase/decreases melee damage by percent base1 based on your position base2 0=back 1=front SPA 504 increase/decreases melee damage taken by percent base1 based on your position base2 0=back 1=front * Implemented 467,468 Implemented SE_DS_Mitigation_Amount 467 SE_DS_Mitigation_Percentage 468 Reduce incoming DS by amt or percentage. base1 is value, if a reduction is desired it should be set to negative for both. * Fixes Formula fixes * Update spdat.h Added spa descriptions. * Implemented SPA 469, 470 Implemented SE_Chance_Best_in_Spell_Grp 469 Chance to cast highest scribed spell within a spell group. All base2 spells share roll chance, only 1 cast. SE_Trigger_Best_in_Spell_Grp 470 Chance to cast highest scribed spell within a spell group. Each spell has own chance. Additional Changes: Rewrote TrySpellTrigger function used for SPA 340 since it incorporates SPA 469. Improved code so that chance of spell being triggered should be more accurate statistically. * Implemented SPA 474, 494 Implemented SE_Pet_Crit_Melee_Damage_Pct_Owner 474 - Gives pets a critical melee damage modifier from the owner SE_Pet_Add_Atk 494 - Gives pet a ATK bonus from the owner Fixed SE_PetMeleeMitigation 397 - The bonus was not being calculated * Implemented SPA 465,477,478 Implemented SE_PC_Pet_AE_Rampage 465 Chance for pet to AE rampage with a damage modifier SE_Hatelist_To_Top_Index 477 Chance to be put on top of RAMPAGE list SE_Hatelist_To_Tail_Index 478 Chance to be put on bottom of RAMPAGE list * Implemented Implemented SE_Fearstun 502 Stun with a max level limit. Normal stun restrictions don't apply. Base1 duration, base2 PC duration, max is level limit SE_TwinCastBlocker 39 Previously unused spell effect that is now used on live. Simply, if this effect is present in a spell then the spell can not be twin cast. * Implemented SPA 483 Implemented Fc_Spell_Damage_Pct_IncomingPC 483 - Focus effect that modifies iby percent incoming spell damage on the target. Base1= min Base2= max. Final percent is random between max and min each time focus is applied from a spell cast. Note: Written to stack with similar functioning focus SPA 269 SE_FcSpellVulnerability. * Implemented SPA 484 Implemented SE_Fc_Spell_Damage_Amt_IncomingPC 484 // focus effect that modifies incoming spell damage by flat amount. Consider it a debuff that adds damage to incoming spells. Positive value to add additional damage. * Implemented SPA 481, 485,486,512 Implemented SE_Fc_Cast_Spell_On_Land 481 Focus effect that is checked when a spell is cast on a target, if target has this focus effect and all limiting criteria are met, then the target will cast a spell as specified by the focus. Can be given a roll chance for success. Base1=Chance, Base2=Spellid Note: This spell has a huge amount of potential applications. See 'Alliance' type spells on live. (ie live spell 50247) Implemented associated focus limits seen in live spells. SE_Ff_CasterClass 485 - Caster of spell on target with a focus effect that is checked by incoming spells must be specified class or classes. SE_Ff_Same_Caster 486 -Caster of spell on target with a focus effect that is checked by incoming spells 0=Must be different caster 1=Must be same caster The following is an associated effect seen with SPA 481 SE_Proc_Timer_Modifier 512 This provides a way to rate limit the amount of spell triggers generated by SPA 481. For example after 1 successful spell trigger no additional spells can be triggered for 1.5 seconds. Ie. Base=1 and Base2 1500. Written in a flexible format to allow scaling of multiple different buffs with this effect at same time. * Stacking fixes for new effects Stacking fixes for new effects. * merge with upstream master merge and update up spdat.h * Update spdat.h * Fix for bolt spell targeting self if target zone/died while casting. Fix for bolt spell targeting self if target zone/died while casting. Despite the name being "ST_TargetOptional", this target type is reserved for projectile spells which all require a target, thus should be treated like any other targeted spell. * Implemented new focus and limits SE_Fc_Amplify_Mod 507 @Fc, On Caster, damage-heal-dot mod pct, base: pct SE_Fc_Amplify_Amt 508 @Fc, On Caster, damage-heal-dot mod flat amt, base: amt SE_Fc_ResistIncoming 510 implemented, @Fc, On Target, resist modifier, base: amt SE_Fc_CastTimeMod2 500 @Fc, On Caster, cast time mod pct, base: pct SE_Fc_CastTimeAmt 501 @Fc, On Caster, cast time mod flat amt, base: milliseconds SE_Ff_DurationMax 495 @Ff, Max duration of spell that can be focused, base: tics SE_Ff_ReuseTimeMin 490 @Ff, Minimum recast time of a spell that can be focused, base: recast time SE_Ff_ReuseTimeMax 491 @Ff, Max recast time of a spell that can be focused, base: recast time SE_Ff_Endurance_Min 492 @Ff, Minimum endurance cost of a spell that can be focused, base: endurance cost SE_Ff_Endurance_Max 493 @Ff, Max endurance cost of a spell that can be focused, base: endurance cost SE_Ff_Value_Min 479 @Ff, Minimum base value of a spell that can be focused, base: spells to be focused base1 value SE_Ff_Value_Max 480 @Ff, Max base value of a spell that can be focused, base: spells to be focused base1 value SE_Ff_Override_NotFocusable 460 @Fc, Allow spell to be focused event if flagged with 'not_focusable' in spell table, base: 1 Added basic focus and limit descriptions to source. * Update spell_effects.cpp hotfix * fix for SE_Ff_Override_Notfocusable Fix for SE_Ff_Override_Notfocusable Logic was not correct. Changed where spell field 'not_focusable' makes check to properly account for this effect. * Update to SE_Fc_CastTimeMod2 Update to SE_Fc_CastTimeMod2 Found sources that show it can reduce cast time to instant. Rewrote formulas to account for such. * fix unintentional change, reverted back. * fixed missing break statements fixed missing break statements * update to IsFocusLimit missing limit cases added to IsFocusLimit * Format CalcFocusEffect * Revert "Format CalcFocusEffect" This reverts commit e5b81791ee82e45a2c0b819bd15f2c9adc94cfd1. * Revert "Revert "Format CalcFocusEffect"" This reverts commit a1ce29a8754704bbcce8f751fb9082383771d878. * Post merge fixes * More post merge adjustments * Another post merge change * Add LimitResist constants back * Swap int16's for 32's * int32 fix Co-authored-by: Akkadius --- common/spdat.cpp | 51 ++ common/spdat.h | 132 ++--- zone/bonuses.cpp | 10 + zone/common.h | 83 +-- zone/effects.cpp | 14 +- zone/mob.cpp | 10 +- zone/spell_effects.cpp | 1081 +++++++++++++++++++++++++--------------- zone/spells.cpp | 5 + 8 files changed, 864 insertions(+), 522 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 132b74523..d7b38096e 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1224,6 +1224,57 @@ bool IsEffectIgnoredInStacking(int spa) case SE_Ff_CasterClass: case SE_Ff_Same_Caster: case SE_Proc_Timer_Modifier: + case SE_TwinCastBlocker: + case SE_Fc_CastTimeAmt: + case SE_Fc_CastTimeMod2: + case SE_Ff_DurationMax: + case SE_Ff_Endurance_Max: + case SE_Ff_Endurance_Min: + case SE_Ff_ReuseTimeMin: + case SE_Ff_ReuseTimeMax: + case SE_Ff_Value_Min: + case SE_Ff_Value_Max: + return true; + default: + return false; + } +} + +bool IsFocusLimit(int spa) +{ + switch (spa) { + case SE_LimitMaxLevel: + case SE_LimitResist: + case SE_LimitTarget: + case SE_LimitEffect: + case SE_LimitSpellType: + case SE_LimitSpell: + case SE_LimitMinDur: + case SE_LimitInstant: + case SE_LimitMinLevel: + case SE_LimitCastTimeMin: + case SE_LimitCastTimeMax: + case SE_LimitCombatSkills: + case SE_LimitManaMin: + case SE_LimitSpellGroup: + case SE_LimitManaMax: + case SE_LimitSpellClass: + case SE_LimitSpellSubclass: + case SE_LimitClass: + case SE_LimitRace: + case SE_LimitCastingSkill: + case SE_LimitUseMin: + case SE_LimitUseType: + case SE_Ff_Override_NotFocusable: + case SE_Ff_CasterClass: + case SE_Ff_Same_Caster: + case SE_Ff_DurationMax: + case SE_Ff_Endurance_Max: + case SE_Ff_Endurance_Min: + case SE_Ff_ReuseTimeMin: + case SE_Ff_ReuseTimeMax: + case SE_Ff_Value_Min: + case SE_Ff_Value_Max: return true; default: return false; diff --git a/common/spdat.h b/common/spdat.h index 089d593d5..e4ffb23be 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -489,24 +489,24 @@ typedef enum { #define SE_ImprovedDamage 124 // implemented #define SE_ImprovedHeal 125 // implemented #define SE_SpellResistReduction 126 // implemented -#define SE_IncreaseSpellHaste 127 // implemented -#define SE_IncreaseSpellDuration 128 // implemented -#define SE_IncreaseRange 129 // implemented -#define SE_SpellHateMod 130 // implemented -#define SE_ReduceReagentCost 131 // implemented -#define SE_ReduceManaCost 132 // implemented -#define SE_FcStunTimeMod 133 // implemented - Modify duration of stuns. -#define SE_LimitMaxLevel 134 // implemented -#define SE_LimitResist 135 // implemented -#define SE_LimitTarget 136 // implemented -#define SE_LimitEffect 137 // implemented -#define SE_LimitSpellType 138 // implemented -#define SE_LimitSpell 139 // implemented -#define SE_LimitMinDur 140 // implemented -#define SE_LimitInstant 141 // implemented -#define SE_LimitMinLevel 142 // implemented -#define SE_LimitCastTimeMin 143 // implemented -#define SE_LimitCastTimeMax 144 // implemented (*not used in any known live spell) +#define SE_IncreaseSpellHaste 127 // implemented, @Fc, On Caster, cast time mod pct, base: pct +#define SE_IncreaseSpellDuration 128 // implemented, @Fc, On Caster, spell duration mod pct, base: pct +#define SE_IncreaseRange 129 // implemented, @Fc, On Caster, spell range mod pct, base: pct +#define SE_SpellHateMod 130 // implemented, @Fc, On Caster, spell hate mod pct, base: min pct, limit: max pct +#define SE_ReduceReagentCost 131 // implemented, @Fc, On Caster, do not consume reagent pct chance, base: min pct, limit: max pct +#define SE_ReduceManaCost 132 // implemented, @Fc, On Caster, reduce mana cost by pct, base: min pct, limt: max pct +#define SE_FcStunTimeMod 133 // implemented, @Fc, On Caster, spell range mod pct, base: pct +#define SE_LimitMaxLevel 134 // implemented, @Ff, Max level of spell that can be focused, if base2 then decrease effectiviness by base2 % per level over max, base: lv, base2: effectiveness pct +#define SE_LimitResist 135 // implemented, @Ff, Resist Type(s) that a spell focus can require or exclude, base1: resist type, Include: Positive Exclude: Negative +#define SE_LimitTarget 136 // implemented, @Ff, Target Type(s) that a spell focus can require or exclude, base1: target type, Include: Positive Exclude: Negative +#define SE_LimitEffect 137 // implemented, @Ff, Spell effect(s) that a spell focus can require or exclude, base1: SPA id, Include: Positive Exclude: Negative +#define SE_LimitSpellType 138 // implemented, @Ff, Only allow focus spells that are Beneficial or Detrimental, base1: 0=det 1=bene +#define SE_LimitSpell 139 // implemented, @Ff, Specific spell id(s) that a spell focus can require or exclude, base1: SPA id, Include: Positive Exclude: Negative +#define SE_LimitMinDur 140 // implemented, @Ff, Mininum duration of spell that can be focused, base1: tics +#define SE_LimitInstant 141 // implemented, @Ff, Include or exclude if an isntant cast spell can be focused, base1: 0=Exclude if Instant 1=Allow only if Instant +#define SE_LimitMinLevel 142 // implemented, @Ff, Mininum level of spell that can be focused, base1: lv +#define SE_LimitCastTimeMin 143 // implemented, @Ff, Mininum cast time of spell that can be focused, base1: milliseconds +#define SE_LimitCastTimeMax 144 // implemented, @Ff, Max cast time of spell that can be focused, base1: milliseconds #define SE_Teleport2 145 // implemented - Banishment of the Pantheon //#define SE_ElectricityResist 146 // *not implemented TODO: Now used on live, xyz for teleport spells? also in temp pets? #define SE_PercentalHeal 147 // implemented @@ -529,7 +529,7 @@ typedef enum { #define SE_AppraiseLDonChest 164 // implemented #define SE_DisarmLDoNTrap 165 // implemented #define SE_UnlockLDoNChest 166 // implemented -#define SE_PetPowerIncrease 167 // implemented +#define SE_PetPowerIncrease 167 // implemented, @Fc, On Caster, pet power mod, base: value #define SE_MeleeMitigation 168 // implemented #define SE_CriticalHitChance 169 // implemented #define SE_SpellCritChance 170 // implemented @@ -648,8 +648,8 @@ typedef enum { #define SE_DoubleSpecialAttack 283 // implemented[AA] - Chance to perform second special attack as monk //#define SE_LoHSetHeal 284 // not used #define SE_NimbleEvasion 285 // *not implemented - base1 = 100 for max -#define SE_FcDamageAmt 286 // implemented - adds direct spell damage -#define SE_SpellDurationIncByTic 287 // implemented +#define SE_FcDamageAmt 286 // implemented, @Fc, On Caster, spell damage mod flat amt, base: amt +#define SE_SpellDurationIncByTic 287 // implemented, @Fc, SPA: 287, SE_SpellDurationIncByTic, On Caster, spell buff duration mod, base: tics #define SE_SkillAttackProc 288 // implemented[AA] - Chance to proc spell on skill attack usage (ex. Dragon Punch) #define SE_CastOnFadeEffect 289 // implemented - Triggers only if fades after natural duration. #define SE_IncreaseRunSpeedCap 290 // implemented[AA] - increases run speed over the hard cap @@ -658,22 +658,22 @@ typedef enum { #define SE_FrontalStunResist 293 // implemented[AA] - Reduce chance to be stunned from front. -- live descriptions sounds like this isn't limited to frontal anymore #define SE_CriticalSpellChance 294 // implemented - increase chance to critical hit and critical damage modifier. //#define SE_ReduceTimerSpecial 295 // not used -#define SE_FcSpellVulnerability 296 // implemented - increase in incoming spell damage [base1= min dmg base2= max dmg] -#define SE_FcDamageAmtIncoming 297 // implemented - debuff that adds points damage to spells cast on target (focus effect). +#define SE_FcSpellVulnerability 296 // implemented, @Fc, On Target, spell damage taken mod pct, base: min pct, limit: max pct +#define SE_FcDamageAmtIncoming 297 // implemetned, @Fc, On Target, damage taken flat amt, base: amt #define SE_ChangeHeight 298 // implemented #define SE_WakeTheDead 299 // implemented #define SE_Doppelganger 300 // implemented #define SE_ArcheryDamageModifier 301 // implemented[AA] - increase archery damage by percent -#define SE_FcDamagePctCrit 302 // implemented - spell focus that is applied after critical hits has been calculated. -#define SE_FcDamageAmtCrit 303 // implemented - adds direct spell damage +#define SE_FcDamagePctCrit 302 // implemented, @Fc, On Caster, spell damage mod pct, base: min pct, limit: max pct, Note: applied after critical hits has been calculated. +#define SE_FcDamageAmtCrit 303 // implemented, @Fc, On Caster, spell damage mod flat amt, base: amt #define SE_OffhandRiposteFail 304 // implemented as bonus - enemy cannot riposte offhand attacks #define SE_MitigateDamageShield 305 // implemented - off hand attacks only (Shielding Resistance) //#define SE_ArmyOfTheDead 306 // *not implemented NecroAA - This ability calls up to five shades of nearby corpses back to life to serve the necromancer. The soulless abominations will mindlessly fight the target until called back to the afterlife some time later. The first rank summons up to three shades that serve for 60 seconds, and each additional rank adds one more possible shade and increases their duration by 15 seconds //#define SE_Appraisal 307 // *not implemented Rogue AA - This ability allows you to estimate the selling price of an item you are holding on your cursor. #define SE_SuspendMinion 308 // implemented #define SE_GateCastersBindpoint 309 // implemented - Gate to casters bind point -#define SE_ReduceReuseTimer 310 // implemented -#define SE_LimitCombatSkills 311 // implemented - Excludes focus from procs (except if proc is a memorizable spell) +#define SE_ReduceReuseTimer 310 // implemented, @Fc, On Caster, disc reuse time mod, base: milliseconds +#define SE_LimitCombatSkills 311 // implemented, @Ff, Include or exclude combat skills or procs (non-memorizable spells) from being focused, base1: 0=Exclude if proc 1=Allow only if proc #define SE_Sanctuary 312 // implemented - Places caster at bottom hate list, effect fades if cast cast spell on targets other than self. #define SE_ForageAdditionalItems 313 // implemented[AA] - chance to forage additional items #define SE_Invisibility2 314 // implemented - fixed duration invisible @@ -697,11 +697,11 @@ typedef enum { #define SE_SummonToCorpse 332 // *not implemented AA - Call of the Wild (Druid/Shaman Res spell with no exp) TOOD: implement this. #define SE_CastOnRuneFadeEffect 333 // implemented #define SE_BardAEDot 334 // implemented -#define SE_BlockNextSpellFocus 335 // implemented - base1 chance to block next spell ie Puratus (8494) +#define SE_BlockNextSpellFocus 335 // implemented, @Fc, On Caster, chance to block next spell, base: chance //#define SE_IllusionaryTarget 336 // not used #define SE_PercentXPIncrease 337 // implemented #define SE_SummonAndResAllCorpses 338 // implemented -#define SE_TriggerOnCast 339 // implemented +#define SE_TriggerOnCast 339 // implemented, @Fc, On Caster, cast on spell use, base: chance pct limit: spellid #define SE_SpellTrigger 340 // implemented - chance to trigger spell [Share rolls with 469] All base2 spells share roll chance, only 1 cast. #define SE_ItemAttackCapIncrease 341 // implemented[AA] - increases the maximum amount of attack you can gain from items. #define SE_ImmuneFleeing 342 // implemented - stop mob from fleeing @@ -710,7 +710,7 @@ typedef enum { #define SE_AssassinateLevel 345 // implemented as bonus - AA Assisination max level to kill #define SE_HeadShotLevel 346 // implemented[AA] - HeadShot max level to kill #define SE_DoubleRangedAttack 347 // implemented - chance at an additional archery attack (consumes arrow) -#define SE_LimitManaMin 348 // implemented +#define SE_LimitManaMin 348 // implemented, @Ff, Mininum mana of spell that can be focused, base1: mana amt #define SE_ShieldEquipDmgMod 349 // implemented[AA] Increase melee base damage (indirectly increasing hate) when wearing a shield. #define SE_ManaBurn 350 // implemented - Drains mana for damage/heal at a defined ratio up to a defined maximum amount of mana. #define SE_PersistentEffect 351 // *not implemented. creates a trap/totem that casts a spell (spell id + base1?) when anything comes near it. can probably make a beacon for this @@ -719,7 +719,7 @@ typedef enum { //#define SE_DeactivateAllTraps 354 // *not implemented - looks to be some type of invulnerability? Test DAT (8757) //#define SE_LearnTrap 355 // *not implemented - looks to be some type of invulnerability? Test LT (8758) //#define SE_ChangeTriggerType 356 // not used -#define SE_FcMute 357 // implemented - silences casting of spells that contain specific spell effects (focus limited) +#define SE_FcMute 357 // implemented, @Fc, On Caster, prevents spell casting, base: chance pct #define SE_CurrentManaOnce 358 // implemented //#define SE_PassiveSenseTrap 359 // *not implemented - Invulnerability (Brell's Blessing) #define SE_ProcOnKillShot 360 // implemented - a buff that has a base1 % to cast spell base2 when you kill a "challenging foe" base3 min level @@ -745,20 +745,20 @@ typedef enum { #define SE_Knockdown 380 // implemented - small knock back(handled by client) //#define SE_KnockTowardCaster 381 // *not implemented (Call of Hither) knocks you back to caster (value) distance units infront #define SE_NegateSpellEffect 382 // implemented - negates specific spell bonuses for duration of the debuff. -#define SE_SympatheticProc 383 // implemented - focus on items that has chance to proc a spell when you cast +#define SE_SympatheticProc 383 // implemented, @Fc, On Caster, cast on spell use, base: variable proc chance on cast time, limit: spellid #define SE_Leap 384 // implemented - Leap effect, ie stomping leap -#define SE_LimitSpellGroup 385 // implemented - Limits to spell group(ie type 3 reuse reduction augs that are class specific and thus all share s SG) +#define SE_LimitSpellGroup 385 // implemented, @Ff, Spell group(s) that a spell focus can require or exclude, base1: spellgroup id, Include: Positive Exclude: Negative #define SE_CastOnCurer 386 // implemented - Casts a spell on the person curing #define SE_CastOnCure 387 // implemented - Casts a spell on the cured person #define SE_SummonCorpseZone 388 // implemented - summons a corpse from any zone(nec AA) -#define SE_FcTimerRefresh 389 // implemented - Refresh spell icons +#define SE_FcTimerRefresh 389 // implemented, @Fc, On Caster, reset all recast timers, base: 1 //#define SE_FcTimerLockout 390 // *not implemented - Sets recast timers to specific value, focus limited. -#define SE_LimitManaMax 391 // implemented -#define SE_FcHealAmt 392 // implemented - Adds or removes healing from spells -#define SE_FcHealPctIncoming 393 // implemented - HealRate with focus restrictions. -#define SE_FcHealAmtIncoming 394 // implemented - Adds/Removes amount of healing on target by X value with foucs restrictions. -#define SE_FcHealPctCritIncoming 395 // implemented[AA] - Increases chance of having a heal crit when cast on you. [focus limited] -#define SE_FcHealAmtCrit 396 // implemented - Adds a direct healing amount to spells +#define SE_LimitManaMax 391 // implemented, @Ff, Mininum mana of spell that can be focused, base1: mana amt +#define SE_FcHealAmt 392 // implemented, @Fc, On Caster, spell healing mod flat amt, base: amt +#define SE_FcHealPctIncoming 393 // implemented, @Fc, On Target, heal received critical chance mod, base: chance pct +#define SE_FcHealAmtIncoming 394 // implemented, @Fc, On Target, heal received mod flat amt, base: amt +#define SE_FcHealPctCritIncoming 395 // implemented, @Fc, On Target, heal received mod pct, base: pct +#define SE_FcHealAmtCrit 396 // implemented, @Fc, On Caster, spell healing mod flat amt, base: amt #define SE_PetMeleeMitigation 397 // implemented[AA] - additional mitigation to your pets. Adds AC #define SE_SwarmPetDuration 398 // implemented - Affects the duration of swarm pets #define SE_FcTwincast 399 // implemented - cast 2 spells for every 1 @@ -773,19 +773,19 @@ typedef enum { #define SE_LimitHPPercent 408 // implemented - limited to a certain percent of your hp(ie heals up to 50%) #define SE_LimitManaPercent 409 // implemented - limited to a certain percent of your mana #define SE_LimitEndPercent 410 // implemented - limited to a certain percent of your end -#define SE_LimitClass 411 // implemented - Limits to spells of a certain class (Note: The class value in dbase is +1 in relation to item class value) -#define SE_LimitRace 412 // implemented - Limits to spells cast by a certain race (Note: not used in any known live spells) -#define SE_FcBaseEffects 413 // implemented - Increases the power of bard songs, skill attacks, runes, bard allowed foci, damage/heal -#define SE_LimitCastingSkill 414 // implemented - Limit a focus to include spells cast using a specific skill. +#define SE_LimitClass 411 // implemented, @Ff, Class(es) that can use the spell focus, base1: class(es), Note: The class value in dbase is +1 in relation to item class value, set as you would item for multiple classes +#define SE_LimitRace 412 // implemented, @Ff, Race that can use the spell focus, base1: race, Note: not used in any known live spells. Use only single race at a time. +#define SE_FcBaseEffects 413 // implemented, @Fc, On Caster, base spell effectiveness mod pct, base: pct +#define SE_LimitCastingSkill 414 // implemented, @Ff, Spell and singing skills(s) that a spell focus can require or exclude, base1: skill id, Include: Positive Exclude: Negative //#define SE_FFItemClass 415 // not used - base1 matches ItemType, base2 matches SubType, -1 ignored, max is bitmask of valid slots #define SE_ACv2 416 // implemented - New AC spell effect #define SE_ManaRegen_v2 417 // implemented - New mana regen effect #define SE_SkillDamageAmount2 418 // implemented - adds skill damage directly to certain attacks #define SE_AddMeleeProc 419 // implemented - Adds a proc -#define SE_FcLimitUse 420 // implemented - increases numhits count by percent (Note: not used in any known live spells) -#define SE_FcIncreaseNumHits 421 // implemented[AA] - increases number of hits a buff has till fade. (focus) -#define SE_LimitUseMin 422 // implemented - limit a focus to require a min amount of numhits value (used with above) -#define SE_LimitUseType 423 // implemented - limit a focus to require a certain numhits type +#define SE_FcLimitUse 420 // implemented, @Fc, On Caster, numhits mod pct, base: pct, Note: not used in any known live spells +#define SE_FcIncreaseNumHits 421 // implemented, @Fc, On Caster, numhits mod flat amt, base: amt +#define SE_LimitUseMin 422 // implemented, @Ff Minium amount of numhits for a spell to be focused, base: numhit amt +#define SE_LimitUseType 423 // implemented, @Ff Focus will only affect if has this numhits type, base: numhit type #define SE_GravityEffect 424 // implemented - Pulls/pushes you toward/away the mob at a set pace //#define SE_Display 425 // *not implemented - Illusion: Flying Dragon(21626) #define SE_IncreaseExtTargetWindow 426 // *not implmented[AA] - increases the capacity of your extended target window @@ -822,9 +822,9 @@ typedef enum { #define SE_ResourceTap 457 // implemented Coverts a percent of dmg from dmg spells(DD/DoT) to hp/mana/end. #define SE_FactionModPct 458 // implemented Modifies faction gains and losses by percent. #define SE_DamageModifier2 459 // implemented - Modifies melee damage by skill type -//#define SE_Ff_Override_NotFocusable 460 // -#define SE_ImprovedDamage2 461 // implemented - Increase spell damage by percent (SE_Fc_Damage_%2) -#define SE_FcDamageAmt2 462 // implemented - Increase spell damage by flat amount (SE_Fc_Damage_Amt2) +#define SE_Ff_Override_NotFocusable 460 // implemented, @Fc, Allow spell to be focused event if flagged with 'not_focusable' in spell table, base: 1 +#define SE_ImprovedDamage2 461 // implemented, @Fc, On Caster, spell damage mod pct, base: min pct, limit: max pct +#define SE_FcDamageAmt2 462 // implemented, @Fc, On Caster, spell damage mod flat amt, base: amt //#define SE_Shield_Target 463 // #define SE_PC_Pet_Rampage 464 // implemented - Base1 % chance to do rampage for base2 % of damage each melee round #define SE_PC_Pet_AE_Rampage 465 // implemented - Base1 % chance to do AE rampage for base2 % of damage each melee round @@ -841,8 +841,13 @@ typedef enum { //#define SE_Weapon_Stance 476 // #define SE_Hatelist_To_Top_Index 477 // Implemented - Chance to be set to top of rampage list #define SE_Hatelist_To_Tail_Index 478 // Implemented - Chance to be set to bottom of rampage list -//#define SE_Ff_Value_Min 479 // -//#define SE_Ff_Value_Max 480 // +#define SE_Ff_Value_Min 479 // implemented, @Ff, Minimum base value of a spell that can be focused, base: spells to be focused base1 value +#define SE_Ff_Value_Max 480 // implemented, @Ff, Max base value of a spell that can be focused, base: spells to be focused base1 value +#define SE_Fc_Cast_Spell_On_Land 481 // implemented, @Fc, On Target, cast spell if hit by spell, base: chance pct, limit: spellid +#define SE_Fc_Spell_Damage_Pct_IncomingPC 483 // implemented, @Fc, On Target, spell damage taken mod pct, base: min pct, limit: max pct +#define SE_Fc_Spell_Damage_Amt_IncomingPC 484 // implemented, @Fc, On Target, damage taken flat amt, base: amt +#define SE_Ff_CasterClass 485 // implemented, @Ff, Caster of spell on target with a focus effect that is checked by incoming spells must be specified class(es). base1: class(es), Note: Set multiple classes same as would for items +#define SE_Ff_Same_Caster 486 // implemented, @Ff, Caster of spell on target with a focus effect that is checked by incoming spells, base1: 0=Must be different caster 1=Must be same caster #define SE_Fc_Cast_Spell_On_Land 481 // Implemented - [FOCUS] Spells cast on target with this Focus Effect will have chance to cause a Spell to be cast if limits met. #define SE_Skill_Base_Damage_Mod 482 // implemented, @OffBonus, modify base melee damage by percent, base: pct, limit: skill(-1=ALL), max: none #define SE_Fc_Spell_Damage_Pct_IncomingPC 483 // Implemented - [FOCUS] modifies incoming spell damage by percent @@ -852,27 +857,27 @@ typedef enum { //#define SE_Extend_Tradeskill_Cap 487 // //#define SE_Defender_Melee_Force_Pct_PC 488 // //#define SE_Worn_Endurance_Regen_Cap 489 // -//#define SE_Ff_ReuseTimeMin 490 // -//#define SE_Ff_ReuseTimeMax 491 // -//#define SE_Ff_Endurance_Min 492 // -//#define SE_Ff_Endurance_Max 493 // +#define SE_Ff_ReuseTimeMin 490 // implemented, @Ff, Minimum recast time of a spell that can be focused, base: recast time +#define SE_Ff_ReuseTimeMax 491 // implemented, @Ff, Max recast time of a spell that can be focused, base: recast time +#define SE_Ff_Endurance_Min 492 // implemented, @Ff, Minimum endurance cost of a spell that can be focused, base: endurance cost +#define SE_Ff_Endurance_Max 493 // implemented, @Ff, Max endurance cost of a spell that can be focused, base: endurance cost #define SE_Pet_Add_Atk 494 // implemented - Bonus on pet owner which gives their pet increased attack stat -//#define SE_Ff_DurationMax 495 // +#define SE_Ff_DurationMax 495 // implemented, @Ff, Max duration of spell that can be focused, base: tics #define SE_Critical_Melee_Damage_Mod_Max 496 // implemented - increase or decrease by percent critical damage (not stackable) //#define SE_Ff_FocusCastProcNoBypass 497 // #define SE_AddExtraAttackPct_1h_Primary 498 // implemented, @OffBonus, gives your double attacks a percent chance to perform an extra attack with 1-handed primary weapon, base: chance, limit: amt attacks, max: none #define SE_AddExtraAttackPct_1h_Secondary 499 //implemented, @OffBonus, gives your double attacks a percent chance to perform an extra attack with 1-handed secondary weapon, base: chance, limit: amt attacks, max: none -//#define SE_Fc_CastTimeMod2 500 // -//#define SE_Fc_CastTimeAmt 501 // +#define SE_Fc_CastTimeMod2 500 // implemented, @Fc, On Caster, cast time mod pct, base: pct, Note: Can reduce to instant cast +#define SE_Fc_CastTimeAmt 501 // implemented, @Fc, On Caster, cast time mod flat amt, base: milliseconds, Note: Can reduce to instant cast #define SE_Fearstun 502 // implemented - Stun with a max level limit. Normal stun restrictions don't apply. #define SE_Melee_Damage_Position_Mod 503 // implemented - modify melee damage by pct if done from Front or Behind //#define SE_Melee_Damage_Position_Amt 504 // #define SE_Damage_Taken_Position_Mod 505 // implemented - mitigate melee damage by pct if dmg taken from Front or Behind //#define SE_Damage_Taken_Position_Amt 506 // -//#define SE_Fc_Amplify_Mod 507 // -//#define SE_Fc_Amplify_Amt 508 // +#define SE_Fc_Amplify_Mod 507 // implemented, @Fc, On Caster, damage-heal-dot mod pct, base: pct +#define SE_Fc_Amplify_Amt 508 // implemented, @Fc, On Caster, damage-heal-dot mod flat amt, base: amt #define SE_Health_Transfer 509 // implemented - exchange health for damage or healing on a target. ie Lifeburn/Act of Valor -//#define SE_Fc_ResistIncoming 510 // +#define SE_Fc_ResistIncoming 510 // implemented, @Fc, On Target, resist modifier, base: amt //#define SE_Ff_FocusTimerMin 511 // #define SE_Proc_Timer_Modifier 512 // implemented - spell trigger limiter used currently with SPA 481, ie. limit to 1 proc every 1.5 seconds (base=1 base2=1500). //#define SE_Mana_Max_Percent 513 // @@ -1170,6 +1175,7 @@ bool IsStackableDot(uint16 spell_id); bool IsBardOnlyStackEffect(int effect); bool IsCastWhileInvis(uint16 spell_id); bool IsEffectIgnoredInStacking(int spa); +bool IsFocusLimit(int spa); bool SpellRequiresTarget(int targettype); int CalcPetHp(int levelb, int classb, int STA = 75); diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index f1106fdf5..57ca16d6a 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -3785,6 +3785,12 @@ uint8 Mob::IsFocusEffect(uint16 spell_id,int effect_index, bool AA,uint32 aa_eff return focusPetPower; case SE_SpellResistReduction: return focusResistRate; + case SE_Fc_ResistIncoming: + focusFcResistIncoming; + case SE_Fc_Amplify_Mod: + focusFcResistIncoming; + case SE_Fc_Amplify_Amt: + focusFcResistIncoming; case SE_SpellHateMod: return focusSpellHateMod; case SE_ReduceReuseTimer: @@ -3831,6 +3837,10 @@ uint8 Mob::IsFocusEffect(uint16 spell_id,int effect_index, bool AA,uint32 aa_eff return focusFcCastSpellOnLand; case SE_FcStunTimeMod: return focusFcStunTimeMod; + case SE_Fc_CastTimeMod2: + return focusFcCastTimeMod2; + case SE_Fc_CastTimeAmt: + return focusFcCastTimeAmt; case SE_FcHealPctCritIncoming: return focusFcHealPctCritIncoming; case SE_FcHealAmt: diff --git a/zone/common.h b/zone/common.h index 754d2b61f..51fd36225 100644 --- a/zone/common.h +++ b/zone/common.h @@ -108,46 +108,49 @@ #define AURA_HARDCAP 2 typedef enum { //focus types - focusSpellHaste = 1, - focusSpellDuration, - focusRange, - focusReagentCost, - focusManaCost, - focusImprovedHeal, - focusImprovedDamage, - focusImprovedDamage2, - focusImprovedDOT, //i dont know about this... - focusFcDamagePctCrit, - focusImprovedUndeadDamage, - focusPetPower, - focusResistRate, - focusSpellHateMod, - focusTriggerOnCast, - focusSpellVulnerability, - focusFcSpellDamagePctIncomingPC, - focusTwincast, - focusSympatheticProc, - focusFcDamageAmt, - focusFcDamageAmt2, - focusFcDamageAmtCrit, - focusSpellDurByTic, - focusSwarmPetDuration, - focusReduceRecastTime, - focusBlockNextSpell, - focusFcHealPctIncoming, - focusFcDamageAmtIncoming, - focusFcSpellDamageAmtIncomingPC, - focusFcCastSpellOnLand, - focusFcHealAmtIncoming, - focusFcBaseEffects, - focusIncreaseNumHits, - focusFcLimitUse, - focusFcMute, - focusFcTimerRefresh, - focusFcStunTimeMod, - focusFcHealPctCritIncoming, - focusFcHealAmt, - focusFcHealAmtCrit, + focusSpellHaste = 1, //@Fc, SPA: 127, SE_IncreaseSpellHaste, On Caster, cast time mod pct, base: pct + focusSpellDuration, //@Fc, SPA: 128, SE_IncreaseSpellDuration, On Caster, spell duration mod pct, base: pct + focusRange, //@Fc, SPA: 129, SE_IncreaseRange, On Caster, spell range mod pct, base: pct + focusReagentCost, //@Fc, SPA: 131, SE_ReduceReagentCost, On Caster, do not consume reagent pct chance, base: min pct, limit: max pct + focusManaCost, //@Fc, SPA: 132, SE_ReduceManaCost, On Caster, reduce mana cost by pct, base: min pct, limt: max pct + focusImprovedHeal, //@Fc, SPA: 125, SE_ImprovedHeal, On Caster, spell healing mod pct, base: min pct, limit: max pct + focusImprovedDamage, //@Fc, SPA: 124, SE_ImprovedDamage, On Caster, spell damage mod pct, base: min pct, limit: max pct + focusImprovedDamage2, //@Fc, SPA: 461, SE_ImprovedDamage2, On Caster, spell damage mod pct, base: min pct, limit: max pct + focusFcDamagePctCrit, //@Fc, SPA: 302, SE_FcDamagePctCrit, On Caster, spell damage mod pct, base: min pct, limit: max pct + focusPetPower, //@Fc, SPA: 167, SE_PetPowerIncrease, On Caster, pet power mod, base: value + focusResistRate, //@Fc, SPA: 126, SE_SpellResistReduction, On Caster, casted spell resist mod pct, base: min pct, limit: max pct + focusSpellHateMod, //@Fc, SPA: 130, SE_SpellHateMod, On Caster, spell hate mod pct, base: min pct, limit: max pct + focusTriggerOnCast, //@Fc, SPA: 339, SE_TriggerOnCast, On Caster, cast on spell use, base: chance pct limit: spellid + focusSpellVulnerability, //@Fc, SPA: 296, SE_FcSpellVulnerability, On Target, spell damage taken mod pct, base: min pct, limit: max pct + focusFcSpellDamagePctIncomingPC, //@Fc, SPA: 483, SE_Fc_Spell_Damage_Pct_IncomingPC, On Target, spell damage taken mod pct, base: min pct, limit: max pct + focusTwincast, //@Fc, SPA: 399, SE_FcTwincast, On Caster, chance cast spell twice, base: chance pct + focusSympatheticProc, //@Fc, SPA: 383, SE_SympatheticProc, On Caster, cast on spell use, base: variable proc chance on cast time, limit: spellid + focusFcDamageAmt, //@Fc, SPA: 286, SE_FcDamageAmt, On Caster, spell damage mod flat amt, base: amt + focusFcDamageAmt2, //@Fc, SPA: 462, SE_FcDamageAmt2, On Caster, spell damage mod flat amt, base: amt + focusFcDamageAmtCrit, //@Fc, SPA: 303, SE_FFcDamageAmtCrit, On Caster, spell damage mod flat amt, base: amt + focusSpellDurByTic, //@Fc, SPA: 287, SE_SpellDurationIncByTic, On Caster, spell buff duration mod, base: tics + focusSwarmPetDuration, //@Fc, SPA: 398, SE_SwarmPetDuration, On Caster, swarm pet duration mod, base: milliseconds + focusReduceRecastTime, //@Fc, SPA: 310, SE_ReduceReuseTimer, On Caster, disc reuse time mod, base: milliseconds + focusBlockNextSpell, //@Fc, SPA: 335, SE_BlockNextSpellFocus, On Caster, chance to block next spell, base: chance + focusFcHealPctIncoming, //@Fc, SPA: 395, SE_FcHealPctCritIncoming, On Target, heal received mod pct, base: pct + focusFcDamageAmtIncoming, //@Fc, SPA: 297, SE_FcDamageAmtIncoming, On Target, damage taken flat amt, base: amt + focusFcSpellDamageAmtIncomingPC, //@Fc, SPA: 484, SE_Fc_Spell_Damage_Amt_IncomingPC, On Target, damage taken flat amt, base: amt + focusFcCastSpellOnLand, //@Fc, SPA: 481, SE_Fc_Cast_Spell_On_Land, On Target, cast spell if hit by spell, base: chance pct, limit: spellid + focusFcHealAmtIncoming, //@Fc, SPA: 394, SE_FcHealAmtIncoming, On Target, heal received mod flat amt, base: amt + focusFcBaseEffects, //@Fc, SPA: 413, SE_FcBaseEffects, On Caster, base spell effectiveness mod pct, base: pct + focusIncreaseNumHits, //@Fc, SPA: 421, SE_FcIncreaseNumHits, On Caster, numhits mod flat amt, base: amt + focusFcLimitUse, //@Fc, SPA: 420, SE_FcLimitUse, On Caster, numhits mod pct, base: pct + focusFcMute, //@Fc, SPA: 357, SE_FcMute, On Caster, prevents spell casting, base: chance pct + focusFcTimerRefresh, //@Fc, SPA: 389, SE_FcTimerRefresh, On Caster, reset all recast timers, base: 1 + focusFcStunTimeMod, //@Fc, SPA: 133, SE_FcStunTimeMod, On Caster, stun time mod pct, base: chance pct + focusFcResistIncoming, //@Fc, SPA: 510, SE_Fc_Resist_Incoming, On Target, resist modifier, base: amt + focusFcAmplifyMod, //@Fc, SPA: 507, SE_Fc_Amplify_Mod, On Caster, damage-heal-dot mod pct, base: pct + focusFcAmplifyAmt, //@Fc, SPA: 508, SE_Fc_Amplify_Amt, On Caster, damage-heal-dot mod flat amt, base: amt + focusFcCastTimeMod2, //@Fc, SPA: 500, SE_Fc_CastTimeMod2, On Caster, cast time mod pct, base: pct + focusFcCastTimeAmt, //@Fc, SPA: 501, SE_Fc_CastTimeAmt, On Caster, cast time mod flat amt, base: milliseconds + focusFcHealPctCritIncoming, //@Fc, SPA: 393, SE_FcHealPctCritIncoming, On Target, heal received critical chance mod, base: chance pct + focusFcHealAmt, //@Fc, SPA: 392, SE_FcHealAmt, On Caster, spell healing mod flat amt, base: amt + focusFcHealAmtCrit, //@Fc, SPA: 396, SE_FcHealAmtCrit, On Caster, spell healing mod flat amt, base: amt } focusType; //Any new FocusType needs to be added to the Mob::IsFocus function #define HIGHEST_FOCUS focusFcHealAmtCrit //Should always be last focusType in enum diff --git a/zone/effects.cpp b/zone/effects.cpp index 17a600634..37726df12 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -103,6 +103,7 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { value += value_BaseEffect*GetFocusEffect(focusImprovedDamage2, spell_id)/100; value += int(value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100; + value += int(value_BaseEffect*GetFocusEffect(focusFcAmplifyMod, spell_id) / 100)*ratio / 100; if (target) { value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100; @@ -113,6 +114,7 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { value -= GetFocusEffect(focusFcDamageAmt, spell_id); value -= GetFocusEffect(focusFcDamageAmt2, spell_id); + value -= GetFocusEffect(focusFcAmplifyAmt, spell_id); if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg) value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value)*ratio / 100; @@ -140,6 +142,7 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { value += value_BaseEffect*GetFocusEffect(focusImprovedDamage2, spell_id)/100; value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100; + value += value_BaseEffect*GetFocusEffect(focusFcAmplifyMod, spell_id)/100; if (target) { value += value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100; @@ -150,6 +153,7 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { value -= GetFocusEffect(focusFcDamageAmt, spell_id); value -= GetFocusEffect(focusFcDamageAmt2, spell_id); + value -= GetFocusEffect(focusFcAmplifyAmt, spell_id); if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg) value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); @@ -191,11 +195,13 @@ int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) { value += int(value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100)*ratio/100; value += int(value_BaseEffect*GetFocusEffect(focusImprovedDamage2, spell_id)/100)*ratio/100; value += int(value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100; + value += int(value_BaseEffect*GetFocusEffect(focusFcAmplifyMod, spell_id) / 100)*ratio/100; value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100; extra_dmg = target->GetFcDamageAmtIncoming(this, spell_id) + int(GetFocusEffect(focusFcDamageAmtCrit, spell_id)*ratio/100) + GetFocusEffect(focusFcDamageAmt, spell_id) + - GetFocusEffect(focusFcDamageAmt2, spell_id); + GetFocusEffect(focusFcDamageAmt2, spell_id) + + GetFocusEffect(focusFcAmplifyAmt, spell_id); if (extra_dmg) { int duration = CalcBuffDuration(this, this, spell_id); @@ -211,11 +217,13 @@ int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) { value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100; value += value_BaseEffect*GetFocusEffect(focusImprovedDamage2, spell_id)/100; value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100; + value += value_BaseEffect*GetFocusEffect(focusFcAmplifyMod, spell_id)/100; value += value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100; extra_dmg = target->GetFcDamageAmtIncoming(this, spell_id) + GetFocusEffect(focusFcDamageAmtCrit, spell_id) + GetFocusEffect(focusFcDamageAmt, spell_id) + - GetFocusEffect(focusFcDamageAmt2, spell_id); + GetFocusEffect(focusFcDamageAmt2, spell_id) + + GetFocusEffect(focusFcAmplifyAmt, spell_id); if (extra_dmg) { int duration = CalcBuffDuration(this, this, spell_id); @@ -275,6 +283,7 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { value = value_BaseEffect; value += int(value_BaseEffect*GetFocusEffect(focusImprovedHeal, spell_id)/100); + value += int(value_BaseEffect*GetFocusEffect(focusFcAmplifyMod, spell_id) / 100); // Instant Heals if(spells[spell_id].buffduration < 1) { @@ -297,6 +306,7 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { value *= modifier; value += GetFocusEffect(focusFcHealAmtCrit, spell_id) * modifier; value += GetFocusEffect(focusFcHealAmt, spell_id); + value += GetFocusEffect(focusFcAmplifyAmt, spell_id); value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); if(!spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt && spells[spell_id].classes[(GetClass()%17) - 1] >= GetLevel() - 5) diff --git a/zone/mob.cpp b/zone/mob.cpp index ec4c719a9..aac0078a3 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3120,7 +3120,8 @@ uint32 Mob::GetLevelHP(uint8 tlevel) int32 Mob::GetActSpellCasttime(uint16 spell_id, int32 casttime) { int32 cast_reducer = GetFocusEffect(focusSpellHaste, spell_id); - auto min_cap = casttime / 2; + int32 cast_reducer_amt = GetFocusEffect(focusFcCastTimeAmt, spell_id); + int32 cast_reducer_no_limit = GetFocusEffect(focusFcCastTimeMod2, spell_id); if (level > 50 && casttime >= 3000 && !spells[spell_id].goodEffect && (GetClass() == RANGER || GetClass() == SHADOWKNIGHT || GetClass() == PALADIN || GetClass() == BEASTLORD)) { @@ -3128,8 +3129,13 @@ int32 Mob::GetActSpellCasttime(uint16 spell_id, int32 casttime) cast_reducer += level_mod * 3; } + cast_reducer = std::min(cast_reducer, 50); //Max cast time with focusSpellHaste and level reducer is 50% of cast time. + cast_reducer += cast_reducer_no_limit; casttime = casttime * (100 - cast_reducer) / 100; - return std::max(casttime, min_cap); + casttime -= cast_reducer_amt; + + return std::max(casttime, 0); + } void Mob::ExecWeaponProc(const EQ::ItemInstance *inst, uint16 spell_id, Mob *on, int level_override) { diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 4813f3b8f..947b30803 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3214,9 +3214,22 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_Fc_Cast_Spell_On_Land: case SE_Ff_CasterClass: case SE_Ff_Same_Caster: + case SE_Fc_ResistIncoming: + case SE_Fc_Amplify_Amt: + case SE_Fc_Amplify_Mod: + case SE_Fc_CastTimeAmt: + case SE_Fc_CastTimeMod2: + case SE_Ff_DurationMax: + case SE_Ff_Endurance_Max: + case SE_Ff_Endurance_Min: + case SE_Ff_ReuseTimeMin: + case SE_Ff_ReuseTimeMax: + case SE_Ff_Value_Min: + case SE_Ff_Value_Max: case SE_AddExtraAttackPct_1h_Primary: case SE_AddExtraAttackPct_1h_Secondary: case SE_Skill_Base_Damage_Mod: + { break; } @@ -4425,16 +4438,20 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) { const SPDat_Spell_Struct &spell = spells[spell_id]; - int32 value = 0; - int lvlModifier = 100; - int spell_level = 0; - int lvldiff = 0; - uint32 effect = 0; - int32 base1 = 0; - int32 base2 = 0; - uint32 slot = 0; + bool not_focusable = spells[spell_id].not_focusable; - bool LimitFailure = false; + int32 value = 0; + int lvlModifier = 100; + int spell_level = 0; + int lvldiff = 0; + uint32 effect = 0; + int32 base1 = 0; + int32 base2 = 0; + uint32 slot = 0; + + int index_id = -1; + + bool LimitFailure = false; bool LimitInclude[MaxLimitInclude] = {false}; /* Certain limits require only one of several Include conditions to be true. Ie. Add damage to fire OR ice spells. @@ -4448,13 +4465,14 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) 14/15 SE_LimitSpellSubClass: Remember: Update MaxLimitInclude in spdat.h if adding new limits that require Includes */ + int FocusCount = 0; for (const auto &e : rank.effects) { effect = e.effect_id; - base1 = e.base1; - base2 = e.base2; - slot = e.slot; + base1 = e.base1; + base2 = e.base2; + slot = e.slot; /* AA Foci's can contain multiple focus effects within the same AA. @@ -4468,17 +4486,18 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) // If limit found on prior check next, else end loop. if (FocusCount > 1) { - for (int e = 0; e < MaxLimitInclude; e += 2) { - if (LimitInclude[e] && !LimitInclude[e + 1]) + for (int i = 0; i < MaxLimitInclude; i += 2) { + if (LimitInclude[i] && !LimitInclude[i + 1]) { LimitFailure = true; + } } if (LimitFailure) { - value = 0; + value = 0; LimitFailure = false; - for (int e = 0; e < MaxLimitInclude; e++) { - LimitInclude[e] = false; // Reset array + for (bool & l : LimitInclude) { + l = false; // Reset array } } @@ -4489,438 +4508,594 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) } switch (effect) { - case SE_Blank: - break; + case SE_Blank: + break; // Handle Focus Limits - - case SE_LimitResist: - if (base1 < 0) { - if (spell.resisttype == -base1) // Exclude - LimitFailure = true; - } - else { - LimitInclude[IncludeExistsSELimitResist] = true; - if (spell.resisttype == base1) // Include - LimitInclude[IncludeFoundSELimitResist] = true; - } - break; - - case SE_LimitInstant: - if (base1 == 1 && spell.buffduration) // Fail if not instant - LimitFailure = true; - if (base1 == 0 && (spell.buffduration == 0)) // Fail if instant - LimitFailure = true; - - break; - - case SE_LimitMaxLevel: - spell_level = spell.classes[(GetClass() % 17) - 1]; - lvldiff = spell_level - base1; - // every level over cap reduces the effect by base2 percent unless from a clicky when - // ItemCastsUseFocus is true - if (lvldiff > 0 && (spell_level <= RuleI(Character, MaxLevel) || - RuleB(Character, ItemCastsUseFocus) == false)) { - if (base2 > 0) { - lvlModifier -= base2 * lvldiff; - if (lvlModifier < 1) + case SE_LimitResist: + if (base1 < 0) { + if (spell.resisttype == -base1) { // Exclude LimitFailure = true; - } - else - LimitFailure = true; - } - break; - - case SE_LimitMinLevel: - if ((spell.classes[(GetClass() % 17) - 1]) < base1) - LimitFailure = true; - break; - - case SE_LimitCastTimeMin: - if (static_cast(spell.cast_time) < base1) - LimitFailure = true; - break; - - case SE_LimitCastTimeMax: - if (static_cast(spell.cast_time) > base1) - LimitFailure = true; - break; - - case SE_LimitSpell: - if (base1 < 0) { // Exclude - if (spell_id == -base1) - LimitFailure = true; - } - else { - LimitInclude[IncludeExistsSELimitSpell] = true; - if (spell_id == base1) // Include - LimitInclude[IncludeFoundSELimitSpell] = true; - } - break; - - case SE_LimitMinDur: - if (base1 > CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration)) - LimitFailure = true; - - break; - - case SE_LimitEffect: - if (base1 < 0) { - if (IsEffectInSpell(spell_id, -base1)) // Exclude - LimitFailure = true; - } - else { - LimitInclude[IncludeExistsSELimitEffect] = true; - // they use 33 here for all classes ... unsure if the type check is really needed - if (base1 == SE_SummonPet && type == focusReagentCost) { - if (IsSummonPetSpell(spell_id) || IsSummonSkeletonSpell(spell_id)) - LimitInclude[IncludeFoundSELimitEffect] = true; + } } else { - if (IsEffectInSpell(spell_id, base1)) // Include - LimitInclude[IncludeFoundSELimitEffect] = true; + LimitInclude[IncludeExistsSELimitResist] = true; + if (spell.resisttype == base1) { // Include + LimitInclude[IncludeFoundSELimitResist] = true; + } } - } - break; - - case SE_LimitSpellType: - switch (base1) { - case 0: - if (!IsDetrimentalSpell(spell_id)) - LimitFailure = true; break; - case 1: - if (!IsBeneficialSpell(spell_id)) + + case SE_LimitInstant: + if (base1 == 1 && spell.buffduration) { // Fail if not instant LimitFailure = true; + } + if (base1 == 0 && (spell.buffduration == 0)) { // Fail if instant + LimitFailure = true; + } + break; - } - break; - case SE_LimitManaMin: - if (spell.mana < base1) - LimitFailure = true; - break; - - case SE_LimitManaMax: - if (spell.mana > base1) - LimitFailure = true; - break; - - case SE_LimitTarget: - if (base1 < 0) { - if (-base1 == spell.targettype) // Exclude - LimitFailure = true; - } - else { - LimitInclude[IncludeExistsSELimitTarget] = true; - if (base1 == spell.targettype) // Include - LimitInclude[IncludeFoundSELimitTarget] = true; - } - break; - - case SE_LimitCombatSkills: - if (base1 == 0 && (IsCombatSkill(spell_id) || IsCombatProc(spell_id))) // Exclude Discs / Procs - LimitFailure = true; - else if (base1 == 1 && (!IsCombatSkill(spell_id) || !IsCombatProc(spell_id))) // Exclude Spells - LimitFailure = true; - - break; - - case SE_LimitSpellGroup: - if (base1 < 0) { - if (-base1 == spell.spellgroup) // Exclude - LimitFailure = true; - } - else { - LimitInclude[8] = true; - if (base1 == spell.spellgroup) // Include - LimitInclude[9] = true; - } - break; - - case SE_LimitCastingSkill: - if (base1 < 0) { - if (-base1 == spell.skill) - LimitFailure = true; - } - else { - LimitInclude[IncludeExistsSELimitCastingSkill] = true; - if (base1 == spell.skill) - LimitInclude[IncludeFoundSELimitCastingSkill] = true; - } - break; - - case SE_LimitSpellClass: - if (base1 < 0) { // Exclude - if (-base1 == spell.spell_class); - LimitFailure = true; - } - else { - LimitInclude[IncludeExistsSELimitSpellClass] = true; - if (base1 == spell.spell_class) // Include - LimitInclude[IncludeFoundSELimitSpellClass] = true; - } - break; - - case SE_LimitSpellSubclass: - if (base1 < 0) { // Exclude - if (-base1 == spell.spell_subclass); - LimitFailure = true; - } - else { - LimitInclude[IncludeExistsSELimitSpellSubclass] = true; - if (base1 == spell.spell_subclass) // Include - LimitInclude[IncludeFoundSELimitSpellSubclass] = true; - } - break; - - case SE_LimitClass: - // Do not use this limit more then once per spell. If multiple class, treat value like items - // would. - if (!PassLimitClass(base1, GetClass())) - LimitFailure = true; - break; - - case SE_LimitRace: - if (base1 != GetRace()) - LimitFailure = true; - break; - - case SE_LimitUseMin: - if (base1 > spell.numhits) - LimitFailure = true; - break; - - case SE_LimitUseType: - if (base1 != spell.numhitstype) - LimitFailure = true; - break; - - /* These are not applicable to AA's because there is never a 'caster' of the 'buff' with the focus effect. - case SE_Ff_Same_Caster: - case SE_Ff_CasterClass: - */ - - // Handle Focus Effects - case SE_ImprovedDamage: - if (type == focusImprovedDamage && base1 > value) - value = base1; - break; - - case SE_ImprovedDamage2: - if (type == focusImprovedDamage2 && base1 > value) - value = base1; - break; - - case SE_ImprovedHeal: - if (type == focusImprovedHeal && base1 > value) - value = base1; - break; - - case SE_ReduceManaCost: - if (type == focusManaCost) - value = base1; - break; - - case SE_IncreaseSpellHaste: - if (type == focusSpellHaste && base1 > value) - value = base1; - break; - - case SE_IncreaseSpellDuration: - if (type == focusSpellDuration && base1 > value) - value = base1; - break; - - case SE_SpellDurationIncByTic: - if (type == focusSpellDurByTic && base1 > value) - value = base1; - break; - - case SE_SwarmPetDuration: - if (type == focusSwarmPetDuration && base1 > value) - value = base1; - break; - - case SE_IncreaseRange: - if (type == focusRange && base1 > value) - value = base1; - break; - - case SE_ReduceReagentCost: - if (type == focusReagentCost && base1 > value) - value = base1; - break; - - case SE_PetPowerIncrease: - if (type == focusPetPower && base1 > value) - value = base1; - break; - - case SE_SpellResistReduction: - if (type == focusResistRate && base1 > value) - value = base1; - break; - - case SE_SpellHateMod: - if (type == focusSpellHateMod) { - if (value != 0) { - if (value > 0) { - if (base1 > value) - value = base1; + case SE_LimitMaxLevel: + spell_level = spell.classes[(GetClass() % 17) - 1]; + lvldiff = spell_level - base1; + // every level over cap reduces the effect by base2 percent unless from a clicky when + // ItemCastsUseFocus is true + if (lvldiff > 0 && (spell_level <= RuleI(Character, MaxLevel) || RuleB(Character, ItemCastsUseFocus) == false)) { + if (base2 > 0) { + lvlModifier -= base2 * lvldiff; + if (lvlModifier < 1) { + LimitFailure = true; + } } else { - if (base1 < value) - value = base1; + LimitFailure = true; } } - else - value = base1; - } - break; + break; - case SE_ReduceReuseTimer: - if (type == focusReduceRecastTime) - value = base1 / 1000; - break; - - case SE_TriggerOnCast: - if (type == focusTriggerOnCast) { - if (zone->random.Roll(base1)) { - value = base2; - } - else { - value = 0; + case SE_LimitMinLevel: + if ((spell.classes[(GetClass() % 17) - 1]) < base1) { LimitFailure = true; } break; - } - case SE_FcSpellVulnerability: - if (type == focusSpellVulnerability) - value = base1; - break; + case SE_LimitCastTimeMin: + if (static_cast(spell.cast_time) < base1) { + LimitFailure = true; + } + break; - case SE_Fc_Spell_Damage_Pct_IncomingPC: - if (type == focusFcSpellDamagePctIncomingPC) - value = base1; - break; + case SE_LimitCastTimeMax: + if (static_cast(spell.cast_time) > base1) { + LimitFailure = true; + } + break; - case SE_BlockNextSpellFocus: - if (type == focusBlockNextSpell) { - if (zone->random.Roll(base1)) - value = 1; - } - break; + case SE_LimitSpell: + if (base1 < 0) { // Exclude + if (spell_id == -base1) { + LimitFailure = true; + } + } + else { + LimitInclude[IncludeExistsSELimitSpell] = true; + if (spell_id == base1) { // Include + LimitInclude[IncludeFoundSELimitSpell] = true; + } + } + break; - case SE_FcTwincast: - if (type == focusTwincast && !IsEffectInSpell(spell_id, SE_TwinCastBlocker)) - value = base1; - break; + case SE_LimitMinDur: + if (base1 > CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration)) { + LimitFailure = true; + } + break; - // Note if using these as AA, make sure this is first focus used. - case SE_SympatheticProc: - if (type == focusSympatheticProc) - value = base2; - break; + case SE_LimitEffect: + if (base1 < 0) { + if (IsEffectInSpell(spell_id, -base1)) { // Exclude + LimitFailure = true; + } + } + else { + LimitInclude[IncludeExistsSELimitEffect] = true; + // they use 33 here for all classes ... unsure if the type check is really needed + if (base1 == SE_SummonPet && type == focusReagentCost) { + if (IsSummonPetSpell(spell_id) || IsSummonSkeletonSpell(spell_id)) { + LimitInclude[IncludeFoundSELimitEffect] = true; + } + } + else { + if (IsEffectInSpell(spell_id, base1)) { // Include + LimitInclude[IncludeFoundSELimitEffect] = true; + } + } + } + break; - case SE_FcDamageAmt: - if (type == focusFcDamageAmt) - value = base1; - break; + case SE_LimitSpellType: + switch (base1) { + case 0: + if (!IsDetrimentalSpell(spell_id)) { + LimitFailure = true; + } + break; + case 1: + if (!IsBeneficialSpell(spell_id)) { + LimitFailure = true; + } + break; + } + break; - case SE_FcDamageAmt2: - if (type == focusFcDamageAmt2) - value = base1; - break; + case SE_LimitManaMin: + if (spell.mana < base1) { + LimitFailure = true; + } + break; - case SE_FcDamageAmtCrit: - if (type == focusFcDamageAmtCrit) - value = base1; - break; + case SE_LimitManaMax: + if (spell.mana > base1) { + LimitFailure = true; + } + break; - case SE_FcDamageAmtIncoming: - if (type == focusFcDamageAmtIncoming) - value = base1; - break; + case SE_LimitTarget: + if (base1 < 0) { + if (-base1 == spell.targettype) { // Exclude + LimitFailure = true; + } + } + else { + LimitInclude[IncludeExistsSELimitTarget] = true; + if (base1 == spell.targettype) { // Include + LimitInclude[IncludeFoundSELimitTarget] = true; + } + } + break; - case SE_Fc_Spell_Damage_Amt_IncomingPC: - if (type == focusFcSpellDamageAmtIncomingPC) - value = base1; - break; + case SE_LimitCombatSkills: + if (base1 == 0 && (IsCombatSkill(spell_id) || IsCombatProc(spell_id))) { // Exclude Discs / Procs + LimitFailure = true; + } + else if (base1 == 1 && (!IsCombatSkill(spell_id) || !IsCombatProc(spell_id))) { // Exclude Spells + LimitFailure = true; + } - case SE_FcHealAmtIncoming: - if (type == focusFcHealAmtIncoming) - value = base1; - break; + break; - case SE_FcHealPctCritIncoming: - if (type == focusFcHealPctCritIncoming) - value = base1; - break; + case SE_LimitSpellGroup: + if (base1 < 0) { + if (-base1 == spell.spellgroup) { // Exclude + LimitFailure = true; + } + } + else { + LimitInclude[IncludeExistsSELimitSpellGroup] = true; + if (base1 == spell.spellgroup) { // Include + LimitInclude[IncludeFoundSELimitSpellGroup] = true; + } + } + break; - case SE_FcHealAmtCrit: - if (type == focusFcHealAmtCrit) - value = base1; - break; + case SE_LimitCastingSkill: + if (base1 < 0) { + if (-base1 == spell.skill) { + LimitFailure = true; + } + } + else { + LimitInclude[IncludeExistsSELimitCastingSkill] = true; + if (base1 == spell.skill) { + LimitInclude[IncludeFoundSELimitCastingSkill] = true; + } + } + break; - case SE_FcHealAmt: - if (type == focusFcHealAmt) - value = base1; - break; + case SE_LimitSpellClass: + if (base1 < 0) { // Exclude + if (-base1 == spell.spell_class) { + LimitFailure = true; + } + } + else { + LimitInclude[IncludeExistsSELimitSpellClass] = true; + if (base1 == spell.spell_class) { // Include + LimitInclude[IncludeFoundSELimitSpellClass] = true; + } + } + break; - case SE_FcHealPctIncoming: - if (type == focusFcHealPctIncoming) - value = base1; - break; + case SE_LimitSpellSubclass: + if (base1 < 0) { // Exclude + if (-base1 == spell.spell_subclass) { + LimitFailure = true; + } + } + else { + LimitInclude[IncludeExistsSELimitSpellSubclass] = true; + if (base1 == spell.spell_subclass) { // Include + LimitInclude[IncludeFoundSELimitSpellSubclass] = true; + } + } + break; - case SE_FcBaseEffects: - if (type == focusFcBaseEffects) - value = base1; - break; + case SE_LimitClass: + // Do not use this limit more then once per spell. If multiple class, treat value like items + // would. + if (!PassLimitClass(base1, GetClass())) { + LimitFailure = true; + } + break; - case SE_FcDamagePctCrit: - if (type == focusFcDamagePctCrit) - value = base1; - break; + case SE_LimitRace: + if (base1 != GetRace()) { + LimitFailure = true; + } + break; - case SE_FcIncreaseNumHits: - if (type == focusIncreaseNumHits) - value = base1; - break; + case SE_LimitUseMin: + if (base1 > spell.numhits) { + LimitFailure = true; + } + break; - case SE_FcLimitUse: - if (type == focusFcLimitUse) - value = base1; - break; + case SE_LimitUseType: + if (base1 != spell.numhitstype) { + LimitFailure = true; + } + break; - case SE_FcMute: - if (type == focusFcMute) - value = base1; - break; + case SE_Ff_DurationMax: + if (base1 > spell.buffduration) { + LimitFailure = true; + } + break; - case SE_FcStunTimeMod: - if (type == focusFcStunTimeMod) - value = base1; - break; + case SE_Ff_Endurance_Min: + if (spell.EndurCost < base1) { + LimitFailure = true; + } + break; - case SE_Fc_Cast_Spell_On_Land: - if (type == focusFcCastSpellOnLand) { - if (zone->random.Roll(base1)) { + case SE_Ff_Endurance_Max: + if (spell.EndurCost > base1) { + LimitFailure = true; + } + break; + + case SE_Ff_ReuseTimeMin: + if (spell.recast_time < base1) { + LimitFailure = true; + } + break; + + case SE_Ff_ReuseTimeMax: + if (spell.recast_time > base1) { + LimitFailure = true; + } + break; + + case SE_Ff_Value_Min: + index_id = GetSpellEffectIndex(spell_id, base2); + if (index_id >= 0 && spell.base[index_id] < base1) { + LimitFailure = true; + } + break; + + case SE_Ff_Value_Max: + index_id = GetSpellEffectIndex(spell_id, base2); + if (index_id >= 0 && spell.base[index_id] > base1) { + LimitFailure = true; + } + break; + + case SE_Ff_Override_NotFocusable: + if (base1 == 1) { + not_focusable = false; + } + break; + + + /* These are not applicable to AA's because there is never a 'caster' of the 'buff' with the focus effect. + case SE_Ff_Same_Caster: + case SE_Ff_CasterClass: + */ + + // Handle Focus Effects + case SE_ImprovedDamage: + if (type == focusImprovedDamage && base1 > value) { + value = base1; + } + break; + + case SE_ImprovedDamage2: + if (type == focusImprovedDamage2 && base1 > value) { + value = base1; + } + break; + + case SE_Fc_Amplify_Mod: + if (type == focusFcAmplifyMod && base1 > value) { + value = base1; + } + break; + + case SE_ImprovedHeal: + if (type == focusImprovedHeal && base1 > value) { + value = base1; + } + break; + + case SE_ReduceManaCost: + if (type == focusManaCost) { + value = base1; + } + break; + + case SE_IncreaseSpellHaste: + if (type == focusSpellHaste && base1 > value) { + value = base1; + } + break; + + case SE_Fc_CastTimeMod2: + if (type == focusFcCastTimeMod2 && base1 > value) { + value = base1; + } + break; + + case SE_Fc_CastTimeAmt: + if (type == focusFcCastTimeAmt && base1 > value) { + value = base1; + } + break; + + case SE_IncreaseSpellDuration: + if (type == focusSpellDuration && base1 > value) { + value = base1; + } + break; + + case SE_SpellDurationIncByTic: + if (type == focusSpellDurByTic && base1 > value) { + value = base1; + } + break; + + case SE_SwarmPetDuration: + if (type == focusSwarmPetDuration && base1 > value) { + value = base1; + } + break; + + case SE_IncreaseRange: + if (type == focusRange && base1 > value) { + value = base1; + } + break; + + case SE_ReduceReagentCost: + if (type == focusReagentCost && base1 > value) { + value = base1; + } + break; + + case SE_PetPowerIncrease: + if (type == focusPetPower && base1 > value) { + value = base1; + } + break; + + case SE_SpellResistReduction: + if (type == focusResistRate && base1 > value) { + value = base1; + } + break; + + case SE_Fc_ResistIncoming: + if (type == focusFcResistIncoming && base1 > value) { + value = base1; + } + break; + + case SE_SpellHateMod: + if (type == focusSpellHateMod) { + if (value != 0) { + if (value > 0) { + if (base1 > value) { + value = base1; + } + } + else { + if (base1 < value) { + value = base1; + } + } + } + else { + value = base1; + } + } + break; + + case SE_ReduceReuseTimer: + if (type == focusReduceRecastTime) { + value = base1 / 1000; + } + break; + + case SE_TriggerOnCast: + if (type == focusTriggerOnCast) { + if (zone->random.Roll(base1)) { + value = base2; + } + else { + value = 0; + LimitFailure = true; + } + break; + } + + case SE_FcSpellVulnerability: + if (type == focusSpellVulnerability) { + value = base1; + } + break; + + case SE_Fc_Spell_Damage_Pct_IncomingPC: + if (type == focusFcSpellDamagePctIncomingPC) { + value = base1; + } + break; + + case SE_BlockNextSpellFocus: + if (type == focusBlockNextSpell) { + if (zone->random.Roll(base1)) { + value = 1; + } + } + break; + + case SE_FcTwincast: + if (type == focusTwincast && !IsEffectInSpell(spell_id, SE_TwinCastBlocker)) { + value = base1; + } + break; + + // Note if using these as AA, make sure this is first focus used. + case SE_SympatheticProc: + if (type == focusSympatheticProc) { value = base2; } break; - } + + case SE_FcDamageAmt: + if (type == focusFcDamageAmt) { + value = base1; + } + break; + + case SE_FcDamageAmt2: + if (type == focusFcDamageAmt2) { + value = base1; + } + break; + + case SE_Fc_Amplify_Amt: + if (type == focusFcAmplifyAmt) { + value = base1; + } + break; + + case SE_FcDamageAmtCrit: + if (type == focusFcDamageAmtCrit) { + value = base1; + } + break; + + case SE_FcDamageAmtIncoming: + if (type == focusFcDamageAmtIncoming) { + value = base1; + } + break; + + case SE_Fc_Spell_Damage_Amt_IncomingPC: + if (type == focusFcSpellDamageAmtIncomingPC) { + value = base1; + } + break; + + case SE_FcHealAmtIncoming: + if (type == focusFcHealAmtIncoming) { + value = base1; + } + break; + + case SE_FcHealPctCritIncoming: + if (type == focusFcHealPctCritIncoming) { + value = base1; + } + break; + + case SE_FcHealAmtCrit: + if (type == focusFcHealAmtCrit) { + value = base1; + } + break; + + case SE_FcHealAmt: + if (type == focusFcHealAmt) { + value = base1; + } + break; + + case SE_FcHealPctIncoming: + if (type == focusFcHealPctIncoming) { + value = base1; + } + break; + + case SE_FcBaseEffects: + if (type == focusFcBaseEffects) { + value = base1; + } + break; + + case SE_FcDamagePctCrit: + if (type == focusFcDamagePctCrit) { + value = base1; + } + break; + + case SE_FcIncreaseNumHits: + if (type == focusIncreaseNumHits) { + value = base1; + } + break; + + case SE_FcLimitUse: + if (type == focusFcLimitUse) { + value = base1; + } + break; + + case SE_FcMute: + if (type == focusFcMute) { + value = base1; + } + break; + + case SE_FcStunTimeMod: + if (type == focusFcStunTimeMod) { + value = base1; + } + break; + + case SE_Fc_Cast_Spell_On_Land: + if (type == focusFcCastSpellOnLand) { + if (zone->random.Roll(base1)) { + value = base2; + } + break; + } } } for (int e = 0; e < MaxLimitInclude; e += 2) { - if (LimitInclude[e] && !LimitInclude[e + 1]) + if (LimitInclude[e] && !LimitInclude[e + 1]) { return 0; + } } - if (LimitFailure) + if (LimitFailure) { return 0; + } + + if (not_focusable) { + return 0; + } return (value * lvlModifier / 100); } @@ -4938,19 +5113,25 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo return 0; } + // No further checks if spell_id no_focusable, unless spell focus_id contains an override limiter. + if (spells[spell_id].not_focusable && !IsEffectInSpell(focus_id, SE_Ff_Override_NotFocusable)) { + return 0; + } + const SPDat_Spell_Struct &focus_spell = spells[focus_id]; const SPDat_Spell_Struct &spell = spells[spell_id]; - - int16 value = 0; + int32 value = 0; int lvlModifier = 100; int spell_level = 0; int lvldiff = 0; uint32 Caston_spell_id = 0; + int index_id = -1; bool LimitInclude[MaxLimitInclude] = {false}; - /* Certain limits require only one of several Include conditions to be true. Ie. Add damage to fire OR ice - spells. + /* Certain limits require only one of several Include conditions to be true. Determined by limits being negative or positive + Ie. Add damage to fire OR ice spells. If positive we 'Include', by checking each limit of same type to look for match until found. Opposed to + just 'Excluding', where if set to negative, if we find that match then focus fails, ie Add damage to all spells BUT Fire. 0/1 SE_LimitResist 2/3 SE_LimitSpell 4/5 SE_LimitEffect @@ -5084,8 +5265,8 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo } break; default: - LogInfo("CalcFocusEffect: unknown limit spelltype [{}]", - focus_spell.base[i]); + LogInfo("CalcFocusEffect: unknown limit spelltype [{}]", focus_spell.base[i]); + break; } break; @@ -5120,8 +5301,7 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo (IsCombatSkill(spell_id) || IsCombatProc(spell_id))) { // Exclude Discs / Procs return 0; } - else if (focus_spell.base[i] == 1 && - (!IsCombatSkill(spell_id) || !IsCombatProc(spell_id))) { // Exclude Spells + else if (focus_spell.base[i] == 1 && (!IsCombatSkill(spell_id) || !IsCombatProc(spell_id))) { // Exclude Spells return 0; } @@ -5190,7 +5370,7 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_LimitSpellClass: if (focus_spell.base[i] < 0) { // Exclude if (-focus_spell.base[i] == spell.spell_class) { - return (0); + return 0; } } else { @@ -5204,7 +5384,7 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_LimitSpellSubclass: if (focus_spell.base[i] < 0) { // Exclude if (-focus_spell.base[i] == spell.spell_subclass) { - return (0); + return 0; } } else { @@ -5235,6 +5415,50 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo } break; + case SE_Ff_DurationMax: + if (focus_spell.base[i] > spell.buffduration) { + return 0; + } + break; + + case SE_Ff_Endurance_Min: + if (spell.EndurCost < focus_spell.base[i]) { + return 0; + } + break; + + case SE_Ff_Endurance_Max: + if (spell.EndurCost > focus_spell.base[i]) { + return 0; + } + break; + + case SE_Ff_ReuseTimeMin: + if (spell.recast_time < focus_spell.base[i]) { + return 0; + } + break; + + case SE_Ff_ReuseTimeMax: + if (spell.recast_time > focus_spell.base[i]) { + return 0; + } + break; + + case SE_Ff_Value_Min: + index_id = GetSpellEffectIndex(spell_id, focus_spell.base2[i]); + if (index_id >= 0 && spell.base[index_id] < focus_spell.base[i]) { + return 0; + } + break; + + case SE_Ff_Value_Max: + index_id = GetSpellEffectIndex(spell_id, focus_spell.base2[i]); + if (index_id >= 0 && spell.base[index_id] > focus_spell.base[i]) { + return 0; + } + break; + // handle effects case SE_ImprovedDamage: if (type == focusImprovedDamage) { @@ -5279,6 +5503,12 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo } break; + case SE_Fc_Amplify_Mod: + if (type == focusFcAmplifyMod && focus_spell.base[i] > value) { + value = focus_spell.base[i]; + } + break; + case SE_ImprovedHeal: if (type == focusImprovedHeal) { if (best_focus) { @@ -5323,6 +5553,18 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo } break; + case SE_Fc_CastTimeMod2: + if (type == focusFcCastTimeMod2 && focus_spell.base[i] > value) { + value = focus_spell.base[i]; + } + break; + + case SE_Fc_CastTimeAmt: + if (type == focusFcCastTimeAmt && focus_spell.base[i] > value) { + value = focus_spell.base[i]; + } + break; + case SE_IncreaseSpellDuration: if (type == focusSpellDuration && focus_spell.base[i] > value) { value = focus_spell.base[i]; @@ -5378,6 +5620,12 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo } break; + case SE_Fc_ResistIncoming: + if (type == focusFcResistIncoming && focus_spell.base[i] > value) { + value = focus_spell.base[i]; + } + break; + case SE_SpellHateMod: if (type == focusSpellHateMod) { if (value != 0) { @@ -5485,6 +5733,12 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo } break; + case SE_Fc_Amplify_Amt: + if (type == focusFcAmplifyAmt) { + value = focus_spell.base[i]; + } + break; + case SE_FcDamageAmtCrit: if (type == focusFcDamageAmtCrit) { value = focus_spell.base[i]; @@ -5860,18 +6114,17 @@ int32 Client::GetFocusEffect(focusType type, uint16 spell_id) if (IsBardSong(spell_id) && type != focusFcBaseEffects && type != focusSpellDuration) return 0; - if (spells[spell_id].not_focusable) - return 0; - int32 realTotal = 0; int32 realTotal2 = 0; int32 realTotal3 = 0; + bool rand_effectiveness = false; //Improved Healing, Damage & Mana Reduction are handled differently in that some are random percentages //In these cases we need to find the most powerful effect, so that each piece of gear wont get its own chance - if(RuleB(Spells, LiveLikeFocusEffects) && (type == focusManaCost || type == focusImprovedHeal || type == focusImprovedDamage || type == focusImprovedDamage2 || type == focusResistRate)) + if (RuleB(Spells, LiveLikeFocusEffects) && (type == focusManaCost || type == focusImprovedHeal || type == focusImprovedDamage || type == focusImprovedDamage2 || type == focusResistRate)) { rand_effectiveness = true; + } //Check if item focus effect exists for the client. if (itembonuses.FocusEffects[type]){ @@ -6134,11 +6387,9 @@ int32 Client::GetFocusEffect(focusType type, uint16 spell_id) int32 NPC::GetFocusEffect(focusType type, uint16 spell_id) { - if (spells[spell_id].not_focusable) - return 0; - int32 realTotal = 0; int32 realTotal2 = 0; + bool rand_effectiveness = false; //Improved Healing, Damage & Mana Reduction are handled differently in that some are random percentages diff --git a/zone/spells.cpp b/zone/spells.cpp index 7b7d894e3..a26c9e2dc 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4531,8 +4531,13 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use resist_modifier += caster->GetSpecialAbilityParam(CASTING_RESIST_DIFF, 0); int focus_resist = caster->GetFocusEffect(focusResistRate, spell_id); + resist_modifier -= 2 * focus_resist; + int focus_incoming_resist = GetFocusEffect(focusFcResistIncoming, spell_id); + + resist_modifier -= focus_incoming_resist; + //Check for fear resist bool IsFear = false; if(IsFearSpell(spell_id)) From 0e7cfe96ef921fd7981d102b9e820167b2bf9b3d Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 1 Aug 2021 20:23:59 -0400 Subject: [PATCH 144/624] [Spells] Update to SPA 130 and SPA 131 focus calculation, focus code improvements (#1486) [Spells] Update to SPA 130 and SPA 131 focus calculation, focus code improvements #use instead of PR 1483 --- zone/mob.h | 2 + zone/spell_effects.cpp | 171 ++++++++++++----------------------------- 2 files changed, 52 insertions(+), 121 deletions(-) diff --git a/zone/mob.h b/zone/mob.h index 5a548dae3..970c117ae 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -837,6 +837,8 @@ public: inline void SetSpellPowerDistanceMod(int16 value) { SpellPowerDistanceMod = value; }; int32 GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot = 0); bool HarmonySpellLevelCheck(int32 spell_id, Mob* target = nullptr); + bool CanFocusUseRandomEffectivenessByType(focusType type); + int GetFocusRandomEffectivenessValue(int focus_base, int focus_base2, bool best_focus = 0); void CastSpellOnLand(Mob* caster, int32 spell_id); void FocusProcLimitProcess(); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 947b30803..a463e29e7 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -5462,44 +5462,13 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo // handle effects case SE_ImprovedDamage: if (type == focusImprovedDamage) { - // This is used to determine which focus should be used for the random calculation - if (best_focus) { - // If the spell contains a value in the base2 field then that is the max value - if (focus_spell.base2[i] != 0) { - value = focus_spell.base2[i]; - } - // If the spell does not contain a base2 value, then its a straight non random - // value - else { - value = focus_spell.base[i]; - } - } - // Actual focus calculation starts here - else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; - } - else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); - } + value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); } break; case SE_ImprovedDamage2: if (type == focusImprovedDamage2) { - if (best_focus) { - if (focus_spell.base2[i] != 0) { - value = focus_spell.base2[i]; - } - else { - value = focus_spell.base[i]; - } - } - else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; - } - else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); - } + value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); } break; @@ -5511,39 +5480,13 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_ImprovedHeal: if (type == focusImprovedHeal) { - if (best_focus) { - if (focus_spell.base2[i] != 0) { - value = focus_spell.base2[i]; - } - else { - value = focus_spell.base[i]; - } - } - else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; - } - else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); - } + value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); } break; case SE_ReduceManaCost: if (type == focusManaCost) { - if (best_focus) { - if (focus_spell.base2[i] != 0) { - value = focus_spell.base2[i]; - } - else { - value = focus_spell.base[i]; - } - } - else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; - } - else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); - } + value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); } break; @@ -5590,8 +5533,8 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo break; case SE_ReduceReagentCost: - if (type == focusReagentCost && focus_spell.base[i] > value) { - value = focus_spell.base[i]; + if (type == focusReagentCost) { + value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); } break; @@ -5603,20 +5546,7 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_SpellResistReduction: if (type == focusResistRate) { - if (best_focus) { - if (focus_spell.base2[i] != 0) { - value = focus_spell.base2[i]; - } - else { - value = focus_spell.base[i]; - } - } - else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; - } - else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); - } + value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); } break; @@ -5628,21 +5558,7 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_SpellHateMod: if (type == focusSpellHateMod) { - if (value != 0) { - if (value > 0) { - if (focus_spell.base[i] > value) { - value = focus_spell.base[i]; - } - } - else { - if (focus_spell.base[i] < value) { - value = focus_spell.base[i]; - } - } - } - else { - value = focus_spell.base[i]; - } + value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); } break; @@ -5679,39 +5595,13 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_FcSpellVulnerability: if (type == focusSpellVulnerability) { - if (best_focus) { - if (focus_spell.base2[i] != 0) { - value = focus_spell.base2[i]; //max damage - } - else { - value = focus_spell.base[i]; - } //min damage - } - else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; //If no max damage set, then default to min damage - } - else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); //else random for value - } + value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); } break; case SE_Fc_Spell_Damage_Pct_IncomingPC: if (type == focusFcSpellDamagePctIncomingPC) { - if (best_focus) { - if (focus_spell.base2[i] != 0) { - value = focus_spell.base2[i]; //max damage - } - else { - value = focus_spell.base[i]; - } //min damage - } - else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { - value = focus_spell.base[i]; //If no max damage set, then default to min damage - } - else { - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); //else random for value - } + value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); } break; @@ -6122,7 +6012,7 @@ int32 Client::GetFocusEffect(focusType type, uint16 spell_id) //Improved Healing, Damage & Mana Reduction are handled differently in that some are random percentages //In these cases we need to find the most powerful effect, so that each piece of gear wont get its own chance - if (RuleB(Spells, LiveLikeFocusEffects) && (type == focusManaCost || type == focusImprovedHeal || type == focusImprovedDamage || type == focusImprovedDamage2 || type == focusResistRate)) { + if (RuleB(Spells, LiveLikeFocusEffects) && CanFocusUseRandomEffectivenessByType(type)) { rand_effectiveness = true; } @@ -7889,3 +7779,42 @@ bool Mob::HarmonySpellLevelCheck(int32 spell_id, Mob *target) } return true; } + +bool Mob::CanFocusUseRandomEffectivenessByType(focusType type) +{ + switch (type) { + case focusImprovedDamage: + case focusImprovedDamage2: + case focusImprovedHeal: + case focusManaCost: + case focusResistRate: + case focusFcDamagePctCrit: + case focusReagentCost: + case focusSpellHateMod: + case focusSpellVulnerability: + case focusFcSpellDamagePctIncomingPC: + return true; + } + + return false; +} + +int Mob::GetFocusRandomEffectivenessValue(int focus_base, int focus_base2, bool best_focus) +{ + int value = 0; + // This is used to determine which focus should be used for the random calculation + if (best_focus) { + // If the spell does not contain a base2 value, then its a straight non random + value = focus_base; + // If the spell contains a value in the base2 field then that is the max value + if (focus_base2 != 0) { + value = focus_base2; + } + return value; + } + else if (focus_base2 == 0 || focus_base == focus_base2) { // Actual focus calculation starts here + return focus_base; + } + + return zone->random.Int(focus_base, focus_base2); +} From bb3c918eaca846dcb33976f4a4b44ca3628dc965 Mon Sep 17 00:00:00 2001 From: Gangsta <48196367+GangstaEQ@users.noreply.github.com> Date: Sun, 1 Aug 2021 18:44:06 -0700 Subject: [PATCH 145/624] [Spells] IsInvisSpell() Method + InvisRequireGroup Rule (#1453) * IsInvis() Method + InvisRequireGroup Rule * Fixed issues with invis rule crashes * Fixed issues with invis rule crashes * Invis Require Group nullptr fix * Invis Group Require Fix crash * Fixes Self only Invis Crashes * Formatting, reverse check order to prevent unnecessary processing Co-authored-by: ProducerZekServer Co-authored-by: Akkadius --- common/ruletypes.h | 1 + common/spdat.cpp | 12 ++++++++++++ common/spdat.h | 1 + zone/spells.cpp | 23 +++++++++++++++++++++++ 4 files changed, 37 insertions(+) diff --git a/common/ruletypes.h b/common/ruletypes.h index c0e740493..82c4f9ecc 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -378,6 +378,7 @@ RULE_BOOL(Spells, CazicTouchTargetsPetOwner, true, "If True, causes Cazic Touch RULE_BOOL(Spells, PreventFactionWarOnCharmBreak, false, "Enable spell interupts and dot removal on charm break to prevent faction wars.") RULE_BOOL(Spells, AllowDoubleInvis, false, "Allows you to cast invisibility spells on a player that is already invisible") RULE_BOOL(Spells, AllowSpellMemorizeFromItem, false, "Allows players to memorize spells by right-clicking spell scrolls") +RULE_BOOL(Spells, InvisRequiresGroup, false, "Invis requires the the target to be in group.") RULE_CATEGORY_END() RULE_CATEGORY(Combat) diff --git a/common/spdat.cpp b/common/spdat.cpp index d7b38096e..e5d85ba9e 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -260,6 +260,18 @@ bool IsDetrimentalSpell(uint16 spell_id) return !IsBeneficialSpell(spell_id); } +bool IsInvisSpell(uint16 spell_id) +{ + if (IsEffectInSpell(spell_id, SE_Invisibility) || + IsEffectInSpell(spell_id, SE_Invisibility2) || + IsEffectInSpell(spell_id, SE_InvisVsUndead) || + IsEffectInSpell(spell_id, SE_InvisVsUndead2) || + IsEffectInSpell(spell_id, SE_InvisVsAnimals)) { + return true; + } + return false; +} + bool IsInvulnerabilitySpell(uint16 spell_id) { return IsEffectInSpell(spell_id, SE_DivineAura); diff --git a/common/spdat.h b/common/spdat.h index e4ffb23be..3bcb17487 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -1083,6 +1083,7 @@ bool IsPercentalHealSpell(uint16 spell_id); bool IsGroupOnlySpell(uint16 spell_id); bool IsBeneficialSpell(uint16 spell_id); bool IsDetrimentalSpell(uint16 spell_id); +bool IsInvisSpell(uint16 spell_id); bool IsInvulnerabilitySpell(uint16 spell_id); bool IsCHDurationSpell(uint16 spell_id); bool IsPoisonCounterSpell(uint16 spell_id); diff --git a/zone/spells.cpp b/zone/spells.cpp index a26c9e2dc..12bc97493 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -431,6 +431,28 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, // ok now we know the target casting_spell_targetid = target_id; + if (RuleB(Spells, InvisRequiresGroup) && IsInvisSpell(spell_id)) { + if (GetTarget() && GetTarget()->IsClient()) { + Client *spell_target = entity_list.GetClientByID(target_id); + if (spell_target && spell_target->GetID() != GetID()) { + if (!spell_target->IsGrouped()) { + InterruptSpell(spell_id); + Message(Chat::Red, "You cannot invis someone who is not in your group."); + return false; + } + else if (spell_target->IsGrouped()) { + Group *target_group = spell_target->GetGroup(); + Group *my_group = GetGroup(); + if (target_group && my_group && (target_group->GetID() != my_group->GetID())) { + InterruptSpell(spell_id); + Message(Chat::Red, "You cannot invis someone who is not in your group."); + return false; + } + } + } + } + } + // We don't get actual mana cost here, that's done when we consume the mana if (mana_cost == -1) mana_cost = spell.mana; @@ -2786,6 +2808,7 @@ int Mob::CalcBuffDuration(Mob *caster, Mob *target, uint16 spell_id, int32 caste spell_id != 287 && spell_id != 601 && IsEffectInSpell(spell_id, SE_Illusion)) res = 10000; // ~16h override + res = mod_buff_duration(res, caster, target, spell_id); LogSpells("Spell [{}]: Casting level [{}], formula [{}], base_duration [{}]: result [{}]", From 38a84cae935bde9d9823fb3678738d7be929698e Mon Sep 17 00:00:00 2001 From: Gangsta <48196367+GangstaEQ@users.noreply.github.com> Date: Sun, 1 Aug 2021 18:58:05 -0700 Subject: [PATCH 146/624] [Quest API] Sit method (#1449) * quest api sit method * alphabetical * Fix * fix again * Ok real fix unprivated * Add Lua Export Co-authored-by: ProducerZekServer Co-authored-by: Akkadius --- zone/client.cpp | 4 ++++ zone/client.h | 1 + zone/lua_client.cpp | 8 +++++++- zone/lua_client.h | 3 ++- zone/perl_client.cpp | 14 ++++++++++++++ 5 files changed, 28 insertions(+), 2 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index ea2ea6fdd..29ab42c9e 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -2010,6 +2010,10 @@ void Client::Stand() { SetAppearance(eaStanding, false); } +void Client::Sit() { + SetAppearance(eaSitting, false); +} + void Client::ChangeLastName(const char* in_lastname) { memset(m_pp.last_name, 0, sizeof(m_pp.last_name)); strn0cpy(m_pp.last_name, in_lastname, sizeof(m_pp.last_name)); diff --git a/zone/client.h b/zone/client.h index c20c9057c..1da699d9b 100644 --- a/zone/client.h +++ b/zone/client.h @@ -394,6 +394,7 @@ public: void Duck(); void Stand(); + void Sit(); virtual void SetMaxHP(); int32 LevelRegen(); diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 29699aeff..10f0e372c 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -94,6 +94,11 @@ void Lua_Client::Duck() { self->Duck(); } +void Lua_Client::Sit() { + Lua_Safe_Call_Void(); + self->Sit(); +} + void Lua_Client::DyeArmorBySlot(uint8 slot, uint8 red, uint8 green, uint8 blue) { Lua_Safe_Call_Void(); self->DyeArmorBySlot(slot, red, green, blue); @@ -2080,7 +2085,7 @@ void Lua_Client::SetAAEXPModifier(uint32 zone_id, double aa_modifier) { void Lua_Client::SetEXPModifier(uint32 zone_id, double exp_modifier) { Lua_Safe_Call_Void(); - self->SetEXPModifier(zone_id, exp_modifier); + self->SetEXPModifier(zone_id, exp_modifier); } void Lua_Client::AddLDoNLoss(uint32 theme_id) { @@ -2181,6 +2186,7 @@ luabind::scope lua_register_client() { .def("SetAFK", (void(Lua_Client::*)(uint8))&Lua_Client::SetAFK) .def("GetAnon", (int(Lua_Client::*)(void))&Lua_Client::GetAnon) .def("SetAnon", (void(Lua_Client::*)(uint8))&Lua_Client::SetAnon) + .def("Sit", (void(Lua_Client::*)(void))&Lua_Client::Sit) .def("Duck", (void(Lua_Client::*)(void))&Lua_Client::Duck) .def("DyeArmorBySlot", (void(Lua_Client::*)(uint8,uint8,uint8,uint8))&Lua_Client::DyeArmorBySlot) .def("DyeArmorBySlot", (void(Lua_Client::*)(uint8,uint8,uint8,uint8,uint8))&Lua_Client::DyeArmorBySlot) diff --git a/zone/lua_client.h b/zone/lua_client.h index 1d44640e7..65219af22 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -31,6 +31,7 @@ public: } void SendSound(); + void Sit(); void Save(); void Save(int commit_now); void SaveBackup(); @@ -56,7 +57,7 @@ public: bool GetGM(); void SetBaseClass(int v); void SetBaseRace(int v); - void SetBaseGender(int v); + void SetBaseGender(int v); int GetClassBitmask(); int GetRaceBitmask(); int GetBaseFace(); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 1fc989bf7..e1aab97fc 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -2111,6 +2111,19 @@ XS(XS_Client_IsStanding) XSRETURN(1); } +XS(XS_Client_Sit); +XS(XS_Client_Sit) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::Sit(THIS)"); + { + Client *THIS; + VALIDATE_THIS_IS_CLIENT; + THIS->Sit(); + } + XSRETURN_EMPTY; +} + XS(XS_Client_IsSitting); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_IsSitting) { dXSARGS; @@ -5702,6 +5715,7 @@ XS(boot_Client) { newXSproto(strcpy(buf, "SetTitleSuffix"), XS_Client_SetTitleSuffix, file, "$$;$"); newXSproto(strcpy(buf, "SetZoneFlag"), XS_Client_SetZoneFlag, file, "$$"); newXSproto(strcpy(buf, "SilentMessage"), XS_Client_SilentMessage, file, "$$"); + newXSproto(strcpy(buf, "Sit"), XS_Client_Sit, file, "$"); newXSproto(strcpy(buf, "SlotConvert2"), XS_Client_SlotConvert2, file, "$$"); newXSproto(strcpy(buf, "Stand"), XS_Client_Stand, file, "$"); newXSproto(strcpy(buf, "SummonItem"), XS_Client_SummonItem, file, "$$;$$$$$$$$"); From c69446c4604f58b684fa8bf1f88012d3b493e2cd Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Mon, 2 Aug 2021 22:42:41 -0400 Subject: [PATCH 147/624] [Spells] SPIndex fix for wrong const in variable (#1487) * SPIndex fix for mislabeled spell SPIndex fix for mislabeled spell All other SPIndex variables were checked again without any additional errors found. * spdat h merge fix --- common/spdat.h | 7 +------ zone/attack.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 3bcb17487..df059f358 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -844,16 +844,11 @@ typedef enum { #define SE_Ff_Value_Min 479 // implemented, @Ff, Minimum base value of a spell that can be focused, base: spells to be focused base1 value #define SE_Ff_Value_Max 480 // implemented, @Ff, Max base value of a spell that can be focused, base: spells to be focused base1 value #define SE_Fc_Cast_Spell_On_Land 481 // implemented, @Fc, On Target, cast spell if hit by spell, base: chance pct, limit: spellid +#define SE_Skill_Base_Damage_Mod 482 // implemented, @OffBonus, modify base melee damage by percent, base: pct, limit: skill(-1=ALL), max: none #define SE_Fc_Spell_Damage_Pct_IncomingPC 483 // implemented, @Fc, On Target, spell damage taken mod pct, base: min pct, limit: max pct #define SE_Fc_Spell_Damage_Amt_IncomingPC 484 // implemented, @Fc, On Target, damage taken flat amt, base: amt #define SE_Ff_CasterClass 485 // implemented, @Ff, Caster of spell on target with a focus effect that is checked by incoming spells must be specified class(es). base1: class(es), Note: Set multiple classes same as would for items #define SE_Ff_Same_Caster 486 // implemented, @Ff, Caster of spell on target with a focus effect that is checked by incoming spells, base1: 0=Must be different caster 1=Must be same caster -#define SE_Fc_Cast_Spell_On_Land 481 // Implemented - [FOCUS] Spells cast on target with this Focus Effect will have chance to cause a Spell to be cast if limits met. -#define SE_Skill_Base_Damage_Mod 482 // implemented, @OffBonus, modify base melee damage by percent, base: pct, limit: skill(-1=ALL), max: none -#define SE_Fc_Spell_Damage_Pct_IncomingPC 483 // Implemented - [FOCUS] modifies incoming spell damage by percent -#define SE_Fc_Spell_Damage_Amt_IncomingPC 484 // Implemented - [FOCUS] modifies incoming spell damage by flat amount. Typically adds damage to incoming spells. -#define SE_Ff_CasterClass 485 // Implemented - [FOCUS LIMIT] Caster of spell on target with a focus effect that is checked by incoming spells must be specified class. -#define SE_Ff_Same_Caster 486 // Implemented - [FOCUS LIMIT] Caster of spell on target with a focus effect that is checked by incoming spells 0=Must be different caster 1=Must be same caster //#define SE_Extend_Tradeskill_Cap 487 // //#define SE_Defender_Melee_Force_Pct_PC 488 // //#define SE_Worn_Endurance_Regen_Cap 489 // diff --git a/zone/attack.cpp b/zone/attack.cpp index 83bbf225c..6df1c04bb 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3193,7 +3193,7 @@ int32 Mob::ReduceDamage(int32 damage) if (damage < 1) return DMG_RUNE; - if (spellbonuses.MeleeRune[SBIndex::RUNE_AMOUNT] && spellbonuses.MeleeRune[SBIndex::POSITIONAL_DAMAGE_MOD] >= 0) + if (spellbonuses.MeleeRune[SBIndex::RUNE_AMOUNT] && spellbonuses.MeleeRune[SBIndex::RUNE_BUFFSLOT] >= 0) damage = RuneAbsorb(damage, SE_Rune); if (damage < 1) @@ -3318,10 +3318,10 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi return 0; //Regular runes absorb spell damage (except dots) - Confirmed on live. - if (spellbonuses.MeleeRune[SBIndex::RUNE_AMOUNT] && spellbonuses.MeleeRune[SBIndex::POSITIONAL_DAMAGE_MOD] >= 0) + if (spellbonuses.MeleeRune[SBIndex::RUNE_AMOUNT] && spellbonuses.MeleeRune[SBIndex::RUNE_BUFFSLOT] >= 0) damage = RuneAbsorb(damage, SE_Rune); - if (spellbonuses.AbsorbMagicAtt[SBIndex::RUNE_AMOUNT] && spellbonuses.AbsorbMagicAtt[SBIndex::POSITIONAL_DAMAGE_MOD] >= 0) + if (spellbonuses.AbsorbMagicAtt[SBIndex::RUNE_AMOUNT] && spellbonuses.AbsorbMagicAtt[SBIndex::RUNE_BUFFSLOT] >= 0) damage = RuneAbsorb(damage, SE_AbsorbMagicAtt); if (damage < 1) @@ -5109,7 +5109,7 @@ int32 Mob::RuneAbsorb(int32 damage, uint16 type) uint32 buff_max = GetMaxTotalSlots(); if (type == SE_Rune) { for (uint32 slot = 0; slot < buff_max; slot++) { - if (slot == spellbonuses.MeleeRune[SBIndex::POSITIONAL_DAMAGE_MOD] && spellbonuses.MeleeRune[SBIndex::RUNE_AMOUNT] && buffs[slot].melee_rune && IsValidSpell(buffs[slot].spellid)) { + if (slot == spellbonuses.MeleeRune[SBIndex::RUNE_BUFFSLOT] && spellbonuses.MeleeRune[SBIndex::RUNE_AMOUNT] && buffs[slot].melee_rune && IsValidSpell(buffs[slot].spellid)) { int melee_rune_left = buffs[slot].melee_rune; if (melee_rune_left > damage) @@ -5133,7 +5133,7 @@ int32 Mob::RuneAbsorb(int32 damage, uint16 type) else { for (uint32 slot = 0; slot < buff_max; slot++) { - if (slot == spellbonuses.AbsorbMagicAtt[SBIndex::POSITIONAL_DAMAGE_MOD] && spellbonuses.AbsorbMagicAtt[SBIndex::RUNE_AMOUNT] && buffs[slot].magic_rune && IsValidSpell(buffs[slot].spellid)) { + if (slot == spellbonuses.AbsorbMagicAtt[SBIndex::RUNE_BUFFSLOT] && spellbonuses.AbsorbMagicAtt[SBIndex::RUNE_AMOUNT] && buffs[slot].magic_rune && IsValidSpell(buffs[slot].spellid)) { int magic_rune_left = buffs[slot].magic_rune; if (magic_rune_left > damage) { From 51ad6d65dc0749d5a91844f254fd7d3d053fa772 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 10 Aug 2021 15:46:37 -0400 Subject: [PATCH 148/624] [Spells] Implemented SPA 476 SE_Weapons_Stance and Live-like AA Enable/Disable Toggle (#1477) * Work started on SPA 476 defines * bonus structure add bonus structure set up * updates spa476 updates spa476 * spell bonus now functional spell bonus working well. * major update with debug messages aa, item and spell now working * Pre clean up, effect implemented working for AA, spells, items, all checked for stacking issues. * removed debug messages removed debug messages * spdat description added spdat description added * minor fix removed debug shout removed unneeded code check. * syntax updates, minor fixes syntax updates, minor fixes * syntax fixes syntax fixes * improvements to code moved function to check at swap item. Easier to manage and more live like behavior. Required minor adjustment Still working on AA toggle. * updates to aa buy, functionalish * Syntax / Formatting * Add break / default to switch * updates * completed v2 * Major revisions Main function check moved to when items are swapped and out of when ever bonus are recalculated. AA Toggle and data structure now more accurate to live. * Update aa.cpp * debug removed * implemented SE_Buy_AA_Rank Closer to live. * Update aa.cpp broadening AA toggle to be more general use. * improved various checks aa toggle is now broadly implemented to be usable with any passive effect. Co-authored-by: Akkadius --- common/spdat.cpp | 1 + common/spdat.h | 5 +- zone/aa.cpp | 208 ++++++++++++++++++++++++++++++++--- zone/bonuses.cpp | 61 ++++++++++- zone/client.cpp | 241 +++++++++++++++++++++++++++++++++++++++-- zone/client.h | 7 ++ zone/client_packet.cpp | 2 + zone/common.h | 20 +++- zone/inventory.cpp | 1 + zone/mob.cpp | 9 ++ zone/mob.h | 7 ++ zone/spell_effects.cpp | 21 +++- 12 files changed, 548 insertions(+), 35 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index e5d85ba9e..2b45a8300 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1236,6 +1236,7 @@ bool IsEffectIgnoredInStacking(int spa) case SE_Ff_CasterClass: case SE_Ff_Same_Caster: case SE_Proc_Timer_Modifier: + case SE_Weapon_Stance: case SE_TwinCastBlocker: case SE_Fc_CastTimeAmt: case SE_Fc_CastTimeMod2: diff --git a/common/spdat.h b/common/spdat.h index df059f358..affa7bd8c 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -64,6 +64,7 @@ #define SPELL_SHAPECHANGE70 6503 #define SPELL_MANA_BURN 2751 #define SPELL_LIFE_BURN 2755 +#define SPELL_TOUCH_OF_THE_DIVINE 4789 // these have known hardcoded behavior but we don't do anything yet, move them above this comment when fixed #define SPELL_THE_DAINS_JUSTICE 1476 #define SPELL_MODULATION 1502 @@ -834,11 +835,11 @@ typedef enum { #define SE_Chance_Best_in_Spell_Grp 469 // implemented - Chance to cast highest scribed spell within a spell group. All base2 spells share roll chance, only 1 cast. #define SE_Trigger_Best_in_Spell_Grp 470 // implemented - Chance to cast highest scribed spell within a spell group. Each spell has own chance. //#define SE_Double_Melee_Round 471 // -//#define SE_Buy_AA_Rank 472 // +#define SE_Buy_AA_Rank 472 // implemented, @Special, Used in AA abilities that have Enable/Disable toggle. Spell on Disabled Rank has this effect in it, base: 1, limit: none, max: none, Note: This will not just buy an AA #define SE_Double_Backstab_Front 473 // implemented - Chance to double backstab from front #define SE_Pet_Crit_Melee_Damage_Pct_Owner 474 // implemenetd - Critical damage mod applied to pets from owner #define SE_Trigger_Spell_Non_Item 475 // implemented - Trigger spell on cast only if not from item click. -//#define SE_Weapon_Stance 476 // +#define SE_Weapon_Stance 476 // implemented, @Misc, Apply a specific spell buffs automatically depending 2Hander, Shield or Duel Wield is equiped, base: spellid, base: 0=2H 1=Shield 2=DW, max: none #define SE_Hatelist_To_Top_Index 477 // Implemented - Chance to be set to top of rampage list #define SE_Hatelist_To_Tail_Index 478 // Implemented - Chance to be set to bottom of rampage list #define SE_Ff_Value_Min 479 // implemented, @Ff, Minimum base value of a spell that can be focused, base: spells to be focused base1 value diff --git a/zone/aa.cpp b/zone/aa.cpp index 0b19065bf..fff2ae727 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -1162,6 +1162,7 @@ void Client::IncrementAlternateAdvancementRank(int rank_id) { void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { AA::Rank *rank = zone->GetAlternateAdvancementRank(rank_id); + if(!rank) { return; } @@ -1178,9 +1179,11 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { if(!CanUseAlternateAdvancementRank(rank)) { return; } + + bool use_toggle_passive_hotkey = UseTogglePassiveHotkey(*rank); //make sure it is not a passive - if(!rank->effects.empty()) { + if(!rank->effects.empty() && !use_toggle_passive_hotkey) { return; } @@ -1188,7 +1191,6 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { // We don't have the AA if (!GetAA(rank_id, &charges)) return; - //if expendable make sure we have charges if(ability->charges > 0 && charges < 1) return; @@ -1241,15 +1243,21 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { } } - // Bards can cast instant cast AAs while they are casting another song - if(spells[rank->spell].cast_time == 0 && GetClass() == BARD && IsBardSong(casting_spell_id)) { - if(!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQ::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].ResistDiff, false)) { - return; + if (use_toggle_passive_hotkey) { + TogglePassiveAlternativeAdvancement(*rank, ability->id); + } + else { + // Bards can cast instant cast AAs while they are casting another song + if (spells[rank->spell].cast_time == 0 && GetClass() == BARD && IsBardSong(casting_spell_id)) { + if (!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQ::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].ResistDiff, false)) { + return; + } + ExpendAlternateAdvancementCharge(ability->id); } - ExpendAlternateAdvancementCharge(ability->id); - } else { - if(!CastSpell(rank->spell, target_id, EQ::spells::CastingSlot::AltAbility, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, cooldown, nullptr, rank->id)) { - return; + else { + if (!CastSpell(rank->spell, target_id, EQ::spells::CastingSlot::AltAbility, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, cooldown, nullptr, rank->id)) { + return; + } } } @@ -1287,16 +1295,16 @@ int Mob::GetAlternateAdvancementCooldownReduction(AA::Rank *rank_in) { } void Mob::ExpendAlternateAdvancementCharge(uint32 aa_id) { - for(auto &iter : aa_ranks) { + for (auto &iter : aa_ranks) { AA::Ability *ability = zone->GetAlternateAdvancementAbility(iter.first); - if(ability && aa_id == ability->id) { - if(iter.second.second > 0) { + if (ability && aa_id == ability->id) { + if (iter.second.second > 0) { iter.second.second -= 1; - if(iter.second.second == 0) { - if(IsClient()) { + if (iter.second.second == 0) { + if (IsClient()) { AA::Rank *r = ability->GetRankByPointsSpent(iter.second.first); - if(r) { + if (r) { CastToClient()->GetEPP().expended_aa += r->cost; } } @@ -1307,7 +1315,7 @@ void Mob::ExpendAlternateAdvancementCharge(uint32 aa_id) { aa_ranks.erase(iter.first); } - if(IsClient()) { + if (IsClient()) { Client *c = CastToClient(); c->SaveAA(); c->SendAlternateAdvancementPoints(); @@ -1796,3 +1804,169 @@ bool Mob::CheckAATimer(int timer) } return false; } + +void Client::TogglePassiveAlternativeAdvancement(const AA::Rank &rank, uint32 ability_id) +{ + /* + Certain AA, like Weapon Stance line use a special toggle Hotkey to enable or disable the AA's passive abilities. + This is occurs by doing the following. Each 'rank' of Weapon Stance is actually 2 actual ranks. + First rank is always the Disabled version which cost X amount of AA. Second rank is the Enabled version which cost 0 AA. + When you buy the first rank, you make a hotkey that on live say 'Weapon Stance Disabled', if you clik that it then BUYS the + next rank of AA (cost 0) which switches the hotkey to 'Enabled Weapon Stance' and you are given the passive buff effects. + If you click the Enabled hotkey, it causes you to lose an AA rank and once again be disabled. Thus, you are switching between + two AA ranks. Thefore when creating an AA using this ability, you need generate both ranks. Follow the same pattern for additional ranks. + + IMPORTANT! The toggle system can be used to Enable or Disable ANY passive AA. You just need to follow the instructions on how to create it. + Example: Enable or Disable a buff that gives a large hate modifier. Play may Enable when tanking and Disable when DPS ect. + + Note: On live the Enabled rank is shown having a Charge of 1, while Disabled rank has no charges. Our current code doesn't support that. Do not use charges. + Note: Live uses a spell 'Disable Ability' ID 46164 to trigger a script to do the AA rank changes. At present time it is not coded to require that, any spell id works. + Note: Discovered a bug on ROF2, where when you buy first rank of an AA with a hotkey, it will always display the title of the second rank in the database. Be aware. No easy fix. + + Dev Note(Kayen 8/1/21): The system as set up is very similar to live, with exception that live gives the Enabled rank 1 Charge. The code here emulates what happens when a + charge would be expended. + + Instructions for how to make the AA - assuming a basic level of knowledge of how AA's work. + - aa_abilities table : Create new ability with a hotkey, type 3, zero charges + - aa_ranks table : [Disabled rank] First rank, should have a cost > 0 (this is what you buy), Set hotkeys, MUST SET A SPELL CONTAINING EFFECT SE_Buy_AA_Rank(SPA 472), set a short recast timer. + [Enabled rank] Second rank, should have a cost = 0, Set hotkeys, Set any valid spell ID you want (it has to exist but does nothing), set a short recast timer. + *Recommend if doing custom, just make the hotkey titled 'Toggle ' and use for both. + + - aa_rank_effects table : [Disabled rank] No data needed in the aa_ranks_effect table + [Enabled rank] Second rank set effect_id = 457 (weapon stance), slot 1,2,3, base1= spell triggers, base= weapon type (0=2H,1=SH,2=DW), for slot 1,2,3 + + Example SQL -Disabled + DO NOT ADD any data to the aa_rank_effects for this rank_id + + -Enabled + INSERT INTO aa_rank_effects (rank_id, slot, effect_id, base1, base2) VALUES (20003, 1, 476, 145,0); + INSERT INTO aa_rank_effects (rank_id, slot, effect_id, base1, base2) VALUES (20003, 2, 476, 174,1); + INSERT INTO aa_rank_effects (rank_id, slot, effect_id, base1, base2) VALUES (20003, 3, 476, 172,2); + + Warning: If you want to design an AA that only uses one weapon type to trigger, like will only apply buff if Shield. Do not include data for other types. Never have a base value=0 + in the Enabled rank. + + */ + + bool enable_next_rank = IsEffectInSpell(rank.spell, SE_Buy_AA_Rank); + + if (enable_next_rank) { + + //Enable + TogglePurchaseAlternativeAdvancementRank(rank.next_id); + Message(Chat::Spells, "You enable an ability."); //Message live gives you. Should come from spell. + + AA::Rank *rank_next = zone->GetAlternateAdvancementRank(rank.next_id); + + //Add checks for any special cases for toggle. + if (IsEffectinAlternateAdvancementRankEffects(*rank_next, SE_Weapon_Stance)) { + weaponstance.aabonus_enabled = true; + ApplyWeaponsStance(); + } + return; + } + else { + + //Disable + ResetAlternateAdvancementRank(ability_id); + TogglePurchaseAlternativeAdvancementRank(rank.prev_id); + Message(Chat::Spells, "You disable an ability."); //Message live gives you. Should come from spell. + + //Add checks for any special cases for toggle. + if (IsEffectinAlternateAdvancementRankEffects(rank, SE_Weapon_Stance)) { + weaponstance.aabonus_enabled = false; + BuffFadeBySpellID(weaponstance.aabonus_buff_spell_id); + } + return; + } +} + +bool Client::UseTogglePassiveHotkey(const AA::Rank &rank) { + + /* + Disabled rank needs a rank spell containing the SE_Buy_AA_Rank effect to return true. + Enabled rank checks to see if the prior rank contains a rank spell with SE_Buy_AA_Rank, if so true. + + Note: On live the enabled rank is Expendable with Charge 1. + + We have already confirmed the rank spell is valid before this function is called. + */ + + + if (IsEffectInSpell(rank.spell, SE_Buy_AA_Rank)) {//Checked when is Disabled. + return true; + } + else if (rank.prev_id != -1) {//Check when effect is Enabled. + AA::Rank *rank_prev = zone->GetAlternateAdvancementRank(rank.prev_id); + + if (IsEffectInSpell(rank_prev->spell, SE_Buy_AA_Rank)) { + return true; + } + } + return false; +} + +bool Client::IsEffectinAlternateAdvancementRankEffects(const AA::Rank &rank, int effect_id) { + + for (const auto &e : rank.effects) { + + if (e.effect_id == effect_id) { + return true; + } + } + return false; +} + +void Client::ResetAlternateAdvancementRank(uint32 aa_id) { + + /* + Resets your AA to baseline + */ + + for(auto &iter : aa_ranks) { + + AA::Ability *ability = zone->GetAlternateAdvancementAbility(iter.first); + + if(ability && aa_id == ability->id) { + RemoveExpendedAA(ability->first_rank_id); + aa_ranks.erase(iter.first); + SaveAA(); + SendAlternateAdvancementPoints(); + return; + } + } +} + +void Client::TogglePurchaseAlternativeAdvancementRank(int rank_id){ + + /* + Stripped down version of purchasing AA. Will give no messages. + Used with toggle hotkey functions. + */ + + AA::Rank *rank = zone->GetAlternateAdvancementRank(rank_id); + if (!rank) { + return; + } + + if (!rank->base_ability) { + return; + } + + if (!CanPurchaseAlternateAdvancementRank(rank, false, false)) { + return; + } + + rank_id = rank->base_ability->first_rank_id; + SetAA(rank_id, rank->current_value, 0); + + if (rank->next) { + SendAlternateAdvancementRank(rank->base_ability->id, rank->next->current_value); + } + + SaveAA(); + SendAlternateAdvancementPoints(); + SendAlternateAdvancementStats(); + CalcBonuses(); +} + diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 57ca16d6a..33c229ba2 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -154,6 +154,7 @@ void Client::CalcItemBonuses(StatBonuses* newbon) { SetShieldEquiped(false); SetTwoHandBluntEquiped(false); SetTwoHanderEquipped(false); + SetDuelWeaponsEquiped(false); unsigned int i; // Update: MainAmmo should only calc skill mods (TODO: Check for other cases) @@ -171,8 +172,13 @@ void Client::CalcItemBonuses(StatBonuses* newbon) { SetTwoHandBluntEquiped(true); SetTwoHanderEquipped(true); } - else if (i == EQ::invslot::slotPrimary && (item && (item->ItemType == EQ::item::ItemType2HSlash || item->ItemType == EQ::item::ItemType2HPiercing))) + else if (i == EQ::invslot::slotPrimary && (item && (item->ItemType == EQ::item::ItemType2HSlash || item->ItemType == EQ::item::ItemType2HPiercing))) { SetTwoHanderEquipped(true); + } + } + + if (CanThisClassDualWield()) { + SetDuelWeaponsEquiped(true); } //tribute items @@ -1555,6 +1561,25 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) newbon->Pet_Add_Atk += base1; break; + case SE_Weapon_Stance: + { + if (IsValidSpell(base1)) { //base1 is the spell_id of buff + if (base2 <= WEAPON_STANCE_TYPE_MAX) { //0=2H, 1=Shield, 2=DW + if (IsValidSpell(newbon->WeaponStance[base2])) { //Check if we already a spell_id saved for this effect + if (spells[newbon->WeaponStance[base2]].rank < spells[base1].rank) { //If so, check if any new spellids with higher rank exist (live spells for this are ranked). + newbon->WeaponStance[base2] = base1; //Overwrite with new effect + SetWeaponStanceEnabled(true); + } + } + else { + newbon->WeaponStance[base2] = base1; //If no prior effect exists, then apply + SetWeaponStanceEnabled(true); + } + } + } + break; + } + case SE_ExtraAttackChance: { if (newbon->ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] < base1) { @@ -1583,6 +1608,7 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) break; } + // to do case SE_PetDiscipline: break; @@ -1603,6 +1629,7 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) case SE_TrapCircumvention: break; + // not handled here case SE_HastenedAASkill: // not handled here but don't want to clutter debug log -- these may need to be verified to ignore @@ -3474,6 +3501,38 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne new_bonus->Pet_Add_Atk += effect_value; break; + case SE_Weapon_Stance: { + if (IsValidSpell(effect_value)) { //base1 is the spell_id of buff + if (base2 <= WEAPON_STANCE_TYPE_MAX) { //0=2H, 1=Shield, 2=DW + if (IsValidSpell(new_bonus->WeaponStance[base2])) { //Check if we already a spell_id saved for this effect + if (spells[new_bonus->WeaponStance[base2]].rank < spells[effect_value].rank) { //If so, check if any new spellids with higher rank exist (live spells for this are ranked). + new_bonus->WeaponStance[base2] = effect_value; //Overwrite with new effect + SetWeaponStanceEnabled(true); + + if (WornType) { + weaponstance.itembonus_enabled = true; + } + else { + weaponstance.spellbonus_enabled = true; + } + } + } + else { + new_bonus->WeaponStance[base2] = effect_value; //If no prior effect exists, then apply + SetWeaponStanceEnabled(true); + + if (WornType) { + weaponstance.itembonus_enabled = true; + } + else { + weaponstance.spellbonus_enabled = true; + } + } + } + } + break; + } + //Special custom cases for loading effects on to NPC from 'npc_spels_effects' table if (IsAISpellEffect) { diff --git a/zone/client.cpp b/zone/client.cpp index 29ab42c9e..98a3deec6 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -1412,7 +1412,7 @@ bool Client::UpdateLDoNPoints(uint32 theme_id, int points) { mmc_points += (mir_points + m_pp.ldon_points_mir); mir_points = (0 - m_pp.ldon_points_mir); } - + if(m_pp.ldon_points_mmc < (0 - mmc_points)) { ruj_points += (mmc_points + m_pp.ldon_points_mmc); mmc_points = (0 - m_pp.ldon_points_mmc); @@ -2419,9 +2419,9 @@ bool Client::CheckIncreaseSkill(EQ::skills::SkillType skillid, Mob *against_who, parse->EventPlayer(EVENT_USE_SKILL, this, buffer, 0); if (against_who) { if ( - against_who->GetSpecialAbility(IMMUNE_AGGRO) || - against_who->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) || - against_who->IsClient() || + against_who->GetSpecialAbility(IMMUNE_AGGRO) || + against_who->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) || + against_who->IsClient() || GetLevelCon(against_who->GetLevel()) == CON_GRAY ) { //false by default @@ -9966,7 +9966,7 @@ void Client::MovePCDynamicZone(const std::string& zone_name, int zone_version, b MovePCDynamicZone(zone_id, zone_version, msg_if_invalid); } -void Client::Fling(float value, float target_x, float target_y, float target_z, bool ignore_los, bool clipping) { +void Client::Fling(float value, float target_x, float target_y, float target_z, bool ignore_los, bool clipping) { BuffFadeByEffect(SE_Levitate); if (CheckLosFN(target_x, target_y, target_z, 6.0f) || ignore_los) { auto outapp_fling = new EQApplicationPacket(OP_Fling, sizeof(fling_struct)); @@ -9975,7 +9975,7 @@ void Client::Fling(float value, float target_x, float target_y, float target_z, flingTo->collision = 0; else flingTo->collision = -1; - + flingTo->travel_time = -1; flingTo->unk3 = 1; flingTo->disable_fall_damage = 1; @@ -10030,7 +10030,7 @@ std::vector Client::GetLearnableDisciplines(uint8 min_level, uint8 max_leve if (learnable) { learnable_disciplines.push_back(spell_id); } - } + } return learnable_disciplines; } @@ -10040,7 +10040,7 @@ std::vector Client::GetLearnedDisciplines() { if (IsValidSpell(m_pp.disciplines.values[index])) { learned_disciplines.push_back(m_pp.disciplines.values[index]); } - } + } return learned_disciplines; } @@ -10050,7 +10050,7 @@ std::vector Client::GetMemmedSpells() { if (IsValidSpell(m_pp.mem_spells[index])) { memmed_spells.push_back(m_pp.mem_spells[index]); } - } + } return memmed_spells; } @@ -10096,7 +10096,7 @@ std::vector Client::GetScribeableSpells(uint8 min_level, uint8 max_level) { if (scribeable) { scribeable_spells.push_back(spell_id); } - } + } return scribeable_spells; } @@ -10174,7 +10174,7 @@ void Client::SendToInstance(std::string instance_type, std::string zone_short_na return; } - DataBucket::SetData(full_bucket_name, itoa(instance_id), itoa(duration)); + DataBucket::SetData(full_bucket_name, itoa(instance_id), itoa(duration)); } AssignToInstance(instance_id); @@ -10221,7 +10221,7 @@ void Client::RemoveItem(uint32 item_id, uint32 quantity) }; int removed_count = 0; const size_t size = sizeof(slots) / sizeof(slots[0]); - for (int slot_index = 0; slot_index < size; ++slot_index) { + for (int slot_index = 0; slot_index < size; ++slot_index) { for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { if (removed_count == quantity) { break; @@ -10249,3 +10249,220 @@ void Client::SetGMStatus(int newStatus) { if (this->Admin() != newStatus) database.UpdateGMStatus(this->AccountID(), newStatus); } + +void Client::ApplyWeaponsStance() +{ + /* + + If you have a weapons stance bonus from at least one bonus type, each time you change weapons this function will ensure the correct + associated buffs are applied, and previous buff is removed. If your weapon stance bonus is completely removed it will, ensure buff is + also removed (ie, removing an item that has worn effect with weapon stance, or clicking off a buff). If client no longer has/never had + any spells/item/aa bonuses with weapon stance effect this function will only do a simple bool check. + + Note: Live like behavior is once you have the triggered buff you can manually click it off to remove it. Swaping any items in inventory will + reapply it automatically. + + Only buff spells should be used as triggered spell effect. IsBuffSpell function also checks spell id validity. + WeaponStance bonus arrary: 0=2H Weapon 1=Shield 2=Dualweild + + Toggling ON or OFF + - From spells, just remove the Primary buff that contains the WeaponStance effect in it. + - For items with worn effect, unequip the item. + - For AA abilities, a hotkey is used to Enable and Disable the effect. See. Client::TogglePassiveAlternativeAdvancement in aa.cpp for extensive details. + + Rank + - Most important for AA, but if you have more than one of WeaponStance effect for a given type, the spell trigger buff will apply whatever has the highest + 'rank' value from the spells table. AA's on live for this effect naturally do this. Be awere of this if making custom spells/worn effects/AA. + + When creating weapon stance effects, you do not need to use all three types. For example, can make an effect where you only get a buff from equiping shield. + + */ + + if (!IsWeaponStanceEnabled()) { + return; + } + + bool enabled = false; + bool item_bonus_exists = false; + bool aa_bonus_exists = false; + + if (weaponstance.spellbonus_enabled) { + + if (spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_2H] || spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD] || + spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD]) { + + enabled = true; + + // Check if no longer has correct combination of weapon type and buff, if so remove buff. + if (!HasTwoHanderEquipped() && IsBuffSpell(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_2H]) && + FindBuff(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_2H])) { + BuffFadeBySpellID(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_2H]); + } + else if (!HasShieldEquiped() && IsBuffSpell(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD]) && + FindBuff(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD])) { + BuffFadeBySpellID(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD]); + } + else if (!HasDualWeaponsEquiped() && + IsBuffSpell(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD]) && + FindBuff(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD])) { + BuffFadeBySpellID(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD]); + } + // If you have correct combination of weapon type and bonus, and do not already have buff, then apply buff. + if (HasTwoHanderEquipped() && IsBuffSpell(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_2H])) { + if (!FindBuff(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_2H])) { + SpellOnTarget(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_2H], this); + } + weaponstance.spellbonus_buff_spell_id = spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_2H]; + } + else if (HasShieldEquiped() && IsBuffSpell(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD])) { + + if (!FindBuff(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD])) { + SpellOnTarget(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD], this); + } + weaponstance.spellbonus_buff_spell_id = spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD]; + } + else if (HasDualWeaponsEquiped() && IsBuffSpell(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD])) { + + if (!FindBuff(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD])) { + SpellOnTarget(spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD], this); + } + weaponstance.spellbonus_buff_spell_id = spellbonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD]; + } + } + } + + // Spellbonus effect removal is checked in BuffFadeBySlot(int slot, bool iRecalcBonuses) in spell_effects.cpp when the buff is clicked off or fades. + + if (weaponstance.itembonus_enabled) { + + if (itembonuses.WeaponStance[WEAPON_STANCE_TYPE_2H] || itembonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD] || + itembonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD]) { + + enabled = true; + item_bonus_exists = true; + + + // Edge case check if have multiple items with WeaponStance worn effect. Make sure correct buffs are applied if items are removed but others left on. + if (weaponstance.itembonus_buff_spell_id) { + + bool buff_desync = true; + if (weaponstance.itembonus_buff_spell_id == itembonuses.WeaponStance[WEAPON_STANCE_TYPE_2H] || + weaponstance.itembonus_buff_spell_id == itembonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD] || + (weaponstance.itembonus_buff_spell_id == itembonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD])) { + buff_desync = false; + } + + if (buff_desync) { + int fade_spell = weaponstance.itembonus_buff_spell_id; + weaponstance.itembonus_buff_spell_id = 0; //Need to zero this before we fade to prevent any recursive loops. + BuffFadeBySpellID(fade_spell); + } + } + + // Check if no longer has correct combination of weapon type and buff, if so remove buff. + if (!HasTwoHanderEquipped() && IsBuffSpell(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_2H]) && + FindBuff(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_2H])) { + BuffFadeBySpellID(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_2H]); + } + else if (!HasShieldEquiped() && IsBuffSpell(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD]) && + FindBuff(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD])) { + BuffFadeBySpellID(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD]); + } + else if (!HasDualWeaponsEquiped() && IsBuffSpell(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD]) && + FindBuff(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD])) { + BuffFadeBySpellID(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD]); + } + + // If you have correct combination of weapon type and bonus, and do not already have buff, then apply buff. + if (HasTwoHanderEquipped() && IsBuffSpell(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_2H])) { + + if (!FindBuff(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_2H])) { + SpellOnTarget(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_2H], this); + } + weaponstance.itembonus_buff_spell_id = itembonuses.WeaponStance[WEAPON_STANCE_TYPE_2H]; + } + else if (HasShieldEquiped() && IsBuffSpell(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD])) { + + if (!FindBuff(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD])) { + SpellOnTarget(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD], this); + } + weaponstance.itembonus_buff_spell_id = itembonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD]; + } + else if (HasDualWeaponsEquiped() && IsBuffSpell(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD])) { + if (!FindBuff(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD])) { + SpellOnTarget(itembonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD], this); + } + weaponstance.itembonus_buff_spell_id = itembonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD]; + } + } + } + + // Itembonus effect removal when item is removed + if (!item_bonus_exists && weaponstance.itembonus_enabled) { + weaponstance.itembonus_enabled = false; + + if (weaponstance.itembonus_buff_spell_id) { + BuffFadeBySpellID(weaponstance.itembonus_buff_spell_id); + weaponstance.itembonus_buff_spell_id = 0; + } + } + + if (weaponstance.aabonus_enabled) { + + if (aabonuses.WeaponStance[WEAPON_STANCE_TYPE_2H] || aabonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD] || + aabonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD]) { + + enabled = true; + aa_bonus_exists = true; + + //Check if no longer has correct combination of weapon type and buff, if so remove buff. + if (!HasTwoHanderEquipped() && IsBuffSpell(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_2H]) && + FindBuff(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_2H])) { + BuffFadeBySpellID(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_2H]); + } + + else if (!HasShieldEquiped() && IsBuffSpell(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD]) && + FindBuff(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD])) { + BuffFadeBySpellID(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD]); + } + + else if (!HasDualWeaponsEquiped() && IsBuffSpell(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD]) && + FindBuff(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD])) { + BuffFadeBySpellID(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD]); + } + + //If you have correct combination of weapon type and bonus, and do not already have buff, then apply buff. + if (HasTwoHanderEquipped() && IsBuffSpell(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_2H])) { + if (!FindBuff(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_2H])) { + SpellOnTarget(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_2H], this); + } + weaponstance.aabonus_buff_spell_id = aabonuses.WeaponStance[WEAPON_STANCE_TYPE_2H]; + } + + else if (HasShieldEquiped() && IsBuffSpell(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD])) { + if (!FindBuff(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD])) { + SpellOnTarget(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD], this); + } + weaponstance.aabonus_buff_spell_id = aabonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD]; + } + + else if (HasDualWeaponsEquiped() && IsBuffSpell(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD])) { + + if (!FindBuff(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD])) { + SpellOnTarget(aabonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD], this); + } + weaponstance.aabonus_buff_spell_id = aabonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD]; + } + } + } + + // AA bonus removal is checked in TogglePassiveAA in aa.cpp. when the hot key is toggled. + + // If no bonuses remain present, prevent additional future checks until new bonus is applied. + if (!enabled) { + SetWeaponStanceEnabled(false); + weaponstance.aabonus_enabled = false; + weaponstance.itembonus_enabled = false; + weaponstance.spellbonus_enabled = false; + } +} diff --git a/zone/client.h b/zone/client.h index 1da699d9b..06f57c8dd 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1554,6 +1554,13 @@ public: void ShowNumHits(); // work around function for numhits not showing on buffs + void ApplyWeaponsStance(); + void TogglePassiveAlternativeAdvancement(const AA::Rank &rank, uint32 ability_id); + bool UseTogglePassiveHotkey(const AA::Rank &rank); + void TogglePurchaseAlternativeAdvancementRank(int rank_id); + void ResetAlternateAdvancementRank(uint32 aa_id); + bool IsEffectinAlternateAdvancementRankEffects(const AA::Rank &rank, int effect_id); + void TripInterrogateInvState() { interrogateinv_flag = true; } bool GetInterrogateInvState() { return interrogateinv_flag; } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 41d66705c..08a442b85 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1717,6 +1717,8 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) /* Task Packets */ LoadClientTaskState(); + ApplyWeaponsStance(); + m_expedition_id = ExpeditionsRepository::GetIDByMemberID(database, CharacterID()); /** diff --git a/zone/common.h b/zone/common.h index 51fd36225..e92ef309e 100644 --- a/zone/common.h +++ b/zone/common.h @@ -106,6 +106,8 @@ #define PET_BUTTON_SPELLHOLD 9 #define AURA_HARDCAP 2 +#define WEAPON_STANCE_TYPE_MAX 2 + typedef enum { //focus types focusSpellHaste = 1, //@Fc, SPA: 127, SE_IncreaseSpellHaste, On Caster, cast time mod pct, base: pct @@ -325,7 +327,7 @@ struct Buffs_Struct { int32 ExtraDIChance; int16 RootBreakChance; //Not saved to dbase uint32 instrument_mod; - int16 focusproclimit_time; //timer to limit number of procs from focus effects + int16 focusproclimit_time; //timer to limit number of procs from focus effects int16 focusproclimit_procamt; //amount of procs that can be cast before timer limiter is set bool persistant_buff; bool client; //True if the caster is a client @@ -545,7 +547,7 @@ struct StatBonuses { int32 DS_Mitigation_Percentage; // base = percent amt of DS mitigation. Negative value to reduce int32 Pet_Crit_Melee_Damage_Pct_Owner; // base = percent mod for pet critcal damage from owner int32 Pet_Add_Atk; // base = Pet ATK bonus from owner - + int32 WeaponStance[WEAPON_STANCE_TYPE_MAX +1];// base = trigger spell id, base2 = 0 is 2h, 1 is shield, 2 is dual wield, [0]spid 2h, [1]spid shield, [2]spid DW // AAs int8 Packrat; //weight reduction for items, 1 point = 10% @@ -682,6 +684,20 @@ struct Shielders_Struct { uint16 shielder_bonus; }; +struct WeaponStance_Struct { + bool enabled; + bool spellbonus_enabled; + bool itembonus_enabled; + bool aabonus_enabled; + int spellbonus_buff_spell_id; + int itembonus_buff_spell_id; + int aabonus_buff_spell_id; +}; + +constexpr uint16 WEAPON_STANCE_TYPE_2H = 0; +constexpr uint16 WEAPON_STANCE_TYPE_SHIELD = 1; +constexpr uint16 WEAPON_STANCE_TYPE_DUAL_WIELD = 2; + typedef struct { uint16 increment; diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 8e1b92448..20f1989fb 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -2026,6 +2026,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { // Step 8: Re-calc stats CalcBonuses(); + ApplyWeaponsStance(); return true; } diff --git a/zone/mob.cpp b/zone/mob.cpp index aac0078a3..64333363b 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -234,6 +234,7 @@ Mob::Mob( has_shieldequiped = false; has_twohandbluntequiped = false; has_twohanderequipped = false; + has_duelweaponsequiped = false; can_facestab = false; has_numhits = false; has_MGB = false; @@ -408,6 +409,14 @@ Mob::Mob( viral_spells[i] = 0; } + weaponstance.enabled = false; + weaponstance.spellbonus_enabled = false; //Set when bonus is applied + weaponstance.itembonus_enabled = false; //Set when bonus is applied + weaponstance.aabonus_enabled = false; //Controlled by function TogglePassiveAA + weaponstance.spellbonus_buff_spell_id = 0; + weaponstance.itembonus_buff_spell_id = 0; + weaponstance.aabonus_buff_spell_id = 0; + pStandingPetOrder = SPO_Follow; pseudo_rooted = false; diff --git a/zone/mob.h b/zone/mob.h index 970c117ae..acabdce05 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -432,6 +432,8 @@ public: inline void SetTwoHandBluntEquiped(bool val) { has_twohandbluntequiped = val; } bool HasTwoHanderEquipped() { return has_twohanderequipped; } void SetTwoHanderEquipped(bool val) { has_twohanderequipped = val; } + bool HasDualWeaponsEquiped() const { return has_duelweaponsequiped; } + inline void SetDuelWeaponsEquiped(bool val) { has_duelweaponsequiped = val; } bool CanFacestab() { return can_facestab; } void SetFacestab(bool val) { can_facestab = val; } virtual uint16 GetSkill(EQ::skills::SkillType skill_num) const { return 0; } @@ -1128,6 +1130,10 @@ public: Shielders_Struct shielder[MAX_SHIELDERS]; Trade* trade; + WeaponStance_Struct weaponstance; + bool IsWeaponStanceEnabled() const { return weaponstance.enabled; } + inline void SetWeaponStanceEnabled(bool val) { weaponstance.enabled = val; } + inline glm::vec4 GetCurrentWayPoint() const { return m_CurrentWayPoint; } inline float GetCWPP() const { return(static_cast(cur_wp_pause)); } inline int GetCWP() const { return(cur_wp); } @@ -1489,6 +1495,7 @@ protected: bool has_shieldequiped; bool has_twohandbluntequiped; bool has_twohanderequipped; + bool has_duelweaponsequiped; bool can_facestab; bool has_numhits; bool has_MGB; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index a463e29e7..fa2d1eca4 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2950,6 +2950,13 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove break; } + case SE_Weapon_Stance: { + if (IsClient()) { + CastToClient()->ApplyWeaponsStance(); + } + break; + } + case SE_PersistentEffect: MakeAura(spell_id); break; @@ -3229,6 +3236,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_AddExtraAttackPct_1h_Primary: case SE_AddExtraAttackPct_1h_Secondary: case SE_Skill_Base_Damage_Mod: + case SE_Buy_AA_Rank: { break; @@ -4359,6 +4367,17 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) } } + case SE_Weapon_Stance: + { + /* + If we click off the spell buff (or fades naturally) giving us + Weapon Stance effects it should remove all associated buff. + */ + if (weaponstance.spellbonus_buff_spell_id) { + BuffFadeBySpellID(weaponstance.spellbonus_buff_spell_id); + } + weaponstance.spellbonus_enabled = false; + } } } @@ -6585,7 +6604,7 @@ bool Mob::TryDivineSave() } } - SpellOnTarget(4789, this); //Touch of the Divine=4789, an Invulnerability/HoT/Purify effect + SpellOnTarget(SPELL_TOUCH_OF_THE_DIVINE, this); //Touch of the Divine=4789, an Invulnerability/HoT/Purify effect SendHPUpdate(); return true; } From 416fadd554ae9e61922f585ad097519ddc7449a2 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 10 Aug 2021 15:46:52 -0400 Subject: [PATCH 149/624] [Spells] Implemented SPA 504 and 506 (#1488) * Implemented SPA 504 and 506 SE_Damage_Taken_Position_Amt 506 // implemented - modify melee damage by amt if dmg taken from Front or Behind SE_Melee_Damage_Position_Amt 504 // implemented - modify melee damage by amt if done from Front or Behind * fix, description updates * Update spdat.h --- common/spdat.h | 8 ++--- zone/attack.cpp | 4 +-- zone/bonuses.cpp | 82 +++++++++++++++++++++++++++++++++++------- zone/common.h | 6 ++-- zone/mob.cpp | 55 +++++++++++++++++++++++++--- zone/mob.h | 2 ++ zone/spell_effects.cpp | 2 ++ 7 files changed, 135 insertions(+), 24 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index affa7bd8c..5e29cc4e3 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -866,10 +866,10 @@ typedef enum { #define SE_Fc_CastTimeMod2 500 // implemented, @Fc, On Caster, cast time mod pct, base: pct, Note: Can reduce to instant cast #define SE_Fc_CastTimeAmt 501 // implemented, @Fc, On Caster, cast time mod flat amt, base: milliseconds, Note: Can reduce to instant cast #define SE_Fearstun 502 // implemented - Stun with a max level limit. Normal stun restrictions don't apply. -#define SE_Melee_Damage_Position_Mod 503 // implemented - modify melee damage by pct if done from Front or Behind -//#define SE_Melee_Damage_Position_Amt 504 // -#define SE_Damage_Taken_Position_Mod 505 // implemented - mitigate melee damage by pct if dmg taken from Front or Behind -//#define SE_Damage_Taken_Position_Amt 506 // +#define SE_Melee_Damage_Position_Mod 503 // implemented, @OffBonus, modify melee damage by percent if done from Front or Behind opponent, base: pct, limit: 0=back 1=front, max: none +#define SE_Melee_Damage_Position_Amt 504 // implemented, @OffBonus, modify melee damage by flat amount if done from Front or Behind opponent, base: amt, limit: 0=back 1=front, max: none +#define SE_Damage_Taken_Position_Mod 505 // implemented, @DefBonus, modify melee damage by percent if dmg taken from Front or Behind, base: pct, limit: 0=back 1=front, max: none +#define SE_Damage_Taken_Position_Amt 506 // implemented -@DefBonus, modify melee damage by flat amount if dmg taken from your Front or Behind, base: amt, limit: 0=back 1=front, max: none #define SE_Fc_Amplify_Mod 507 // implemented, @Fc, On Caster, damage-heal-dot mod pct, base: pct #define SE_Fc_Amplify_Amt 508 // implemented, @Fc, On Caster, damage-heal-dot mod flat amt, base: amt #define SE_Health_Transfer 509 // implemented - exchange health for damage or healing on a target. ie Lifeburn/Act of Valor diff --git a/zone/attack.cpp b/zone/attack.cpp index 6df1c04bb..0a828fcb7 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -5219,7 +5219,7 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac // Seems the crit message is generated before some of them :P // worn item +skill dmg, SPA 220, 418. Live has a normalized version that should be here too - hit.min_damage += GetSkillDmgAmt(hit.skill); + hit.min_damage += GetSkillDmgAmt(hit.skill) + GetPositionalDmgAmt(defender); // shielding mod2 if (defender->itembonuses.MeleeMitigation) @@ -5276,7 +5276,7 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac int pct_damage_reduction = defender->GetSkillDmgTaken(hit.skill, opts) + defender->GetPositionalDmgTaken(this); - hit.damage_done += (hit.damage_done * pct_damage_reduction / 100) + (defender->GetFcDamageAmtIncoming(this, 0, true, hit.skill)); + hit.damage_done += (hit.damage_done * pct_damage_reduction / 100) + (defender->GetFcDamageAmtIncoming(this, 0, true, hit.skill)) + defender->GetPositionalDmgTakenAmt(this); CheckNumHitsRemaining(NumHit::OutgoingHitSuccess); } diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 33c229ba2..8c4b5893f 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1545,6 +1545,26 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) break; } + case SE_Damage_Taken_Position_Amt: + { + //Mitigate if damage taken from behind base2 = 0, from front base2 = 1 + if (base2 < 0 || base2 > 2) + break; + + newbon->Damage_Taken_Position_Amt[base2] += base1; + break; + } + + case SE_Melee_Damage_Position_Amt: + { + //Mitigate if damage taken from behind base2 = 0, from front base2 = 1 + if (base2 < 0 || base2 > 2) + break; + + newbon->Melee_Damage_Position_Amt[base2] += base1; + break; + } + case SE_DS_Mitigation_Amount: newbon->DS_Mitigation_Amount += base1; break; @@ -3485,6 +3505,26 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne break; } + case SE_Damage_Taken_Position_Amt: + { + //Mitigate if damage taken from behind base2 = 0, from front base2 = 1 + if (base2 < 0 || base2 > 2) + break; + + new_bonus->Damage_Taken_Position_Amt[base2] += effect_value; + break; + } + + case SE_Melee_Damage_Position_Amt: + { + //Mitigate if damage taken from behind base2 = 0, from front base2 = 1 + if (base2 < 0 || base2 > 2) + break; + + new_bonus->Melee_Damage_Position_Amt[base2] += effect_value; + break; + } + case SE_DS_Mitigation_Amount: new_bonus->DS_Mitigation_Amount += effect_value; break; @@ -5121,21 +5161,39 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) break; case SE_Melee_Damage_Position_Mod: - spellbonuses.Melee_Damage_Position_Mod[SBIndex::POSITIONAL_DAMAGE_MOD] = effect_value; - aabonuses.Melee_Damage_Position_Mod[SBIndex::POSITIONAL_DAMAGE_MOD] = effect_value; - itembonuses.Melee_Damage_Position_Mod[SBIndex::POSITIONAL_DAMAGE_MOD] = effect_value; - spellbonuses.Melee_Damage_Position_Mod[SBIndex::POSITIONAL_LOCATION] = effect_value; - aabonuses.Melee_Damage_Position_Mod[SBIndex::POSITIONAL_LOCATION] = effect_value; - itembonuses.Melee_Damage_Position_Mod[SBIndex::POSITIONAL_LOCATION] = effect_value; + spellbonuses.Melee_Damage_Position_Mod[SBIndex::POSITION_BACK] = effect_value; + aabonuses.Melee_Damage_Position_Mod[SBIndex::POSITION_BACK] = effect_value; + itembonuses.Melee_Damage_Position_Mod[SBIndex::POSITION_BACK] = effect_value; + spellbonuses.Melee_Damage_Position_Mod[SBIndex::POSITION_FRONT] = effect_value; + aabonuses.Melee_Damage_Position_Mod[SBIndex::POSITION_FRONT] = effect_value; + itembonuses.Melee_Damage_Position_Mod[SBIndex::POSITION_FRONT] = effect_value; break; case SE_Damage_Taken_Position_Mod: - spellbonuses.Damage_Taken_Position_Mod[SBIndex::POSITIONAL_DAMAGE_MOD] = effect_value; - aabonuses.Damage_Taken_Position_Mod[SBIndex::POSITIONAL_DAMAGE_MOD] = effect_value; - itembonuses.Damage_Taken_Position_Mod[SBIndex::POSITIONAL_DAMAGE_MOD] = effect_value; - spellbonuses.Damage_Taken_Position_Mod[SBIndex::POSITIONAL_LOCATION] = effect_value; - aabonuses.Damage_Taken_Position_Mod[SBIndex::POSITIONAL_LOCATION] = effect_value; - itembonuses.Damage_Taken_Position_Mod[SBIndex::POSITIONAL_LOCATION] = effect_value; + spellbonuses.Damage_Taken_Position_Mod[SBIndex::POSITION_BACK] = effect_value; + aabonuses.Damage_Taken_Position_Mod[SBIndex::POSITION_BACK] = effect_value; + itembonuses.Damage_Taken_Position_Mod[SBIndex::POSITION_BACK] = effect_value; + spellbonuses.Damage_Taken_Position_Mod[SBIndex::POSITION_FRONT] = effect_value; + aabonuses.Damage_Taken_Position_Mod[SBIndex::POSITION_FRONT] = effect_value; + itembonuses.Damage_Taken_Position_Mod[SBIndex::POSITION_FRONT] = effect_value; + break; + + case SE_Melee_Damage_Position_Amt: + spellbonuses.Melee_Damage_Position_Amt[SBIndex::POSITION_BACK] = effect_value; + aabonuses.Melee_Damage_Position_Amt[SBIndex::POSITION_BACK] = effect_value; + itembonuses.Melee_Damage_Position_Amt[SBIndex::POSITION_BACK] = effect_value; + spellbonuses.Melee_Damage_Position_Amt[SBIndex::POSITION_FRONT] = effect_value; + aabonuses.Melee_Damage_Position_Amt[SBIndex::POSITION_FRONT] = effect_value; + itembonuses.Melee_Damage_Position_Amt[SBIndex::POSITION_FRONT] = effect_value; + break; + + case SE_Damage_Taken_Position_Amt: + spellbonuses.Damage_Taken_Position_Amt[SBIndex::POSITION_BACK] = effect_value; + aabonuses.Damage_Taken_Position_Amt[SBIndex::POSITION_BACK] = effect_value; + itembonuses.Damage_Taken_Position_Amt[SBIndex::POSITION_BACK] = effect_value; + spellbonuses.Damage_Taken_Position_Amt[SBIndex::POSITION_FRONT] = effect_value; + aabonuses.Damage_Taken_Position_Amt[SBIndex::POSITION_FRONT] = effect_value; + itembonuses.Damage_Taken_Position_Amt[SBIndex::POSITION_FRONT] = effect_value; break; diff --git a/zone/common.h b/zone/common.h index e92ef309e..ddcb034fc 100644 --- a/zone/common.h +++ b/zone/common.h @@ -542,6 +542,8 @@ struct StatBonuses { int32 AC_Avoidance_Max_Percent; // Increase AC avoidance by percent int32 Damage_Taken_Position_Mod[2]; // base = percent melee damage reduction base2 0=back 1=front. [0]Back[1]Front int32 Melee_Damage_Position_Mod[2]; // base = percent melee damage increase base2 0=back 1=front. [0]Back[1]Front + int32 Damage_Taken_Position_Amt[2]; // base = flat amt melee damage reduction base2 0=back 1=front. [0]Back[1]Front + int32 Melee_Damage_Position_Amt[2]; // base = flat amt melee damage increase base2 0=back 1=front. [0]Back[1]Front int32 Double_Backstab_Front; // base = percent chance to double back stab front int32 DS_Mitigation_Amount; // base = flat amt DS mitigation. Negative value to reduce int32 DS_Mitigation_Percentage; // base = percent amt of DS mitigation. Negative value to reduce @@ -652,8 +654,8 @@ namespace SBIndex { constexpr uint16 ROOT_BUFFSLOT = 1; // SPA 99 constexpr uint16 RUNE_AMOUNT = 0; // SPA 55 constexpr uint16 RUNE_BUFFSLOT = 1; // SPA 78 - constexpr uint16 POSITIONAL_DAMAGE_MOD = 0; // SPA 503-506 - constexpr uint16 POSITIONAL_LOCATION = 1; // SPA 503-506 + constexpr uint16 POSITION_BACK = 0; // SPA 503-506 + constexpr uint16 POSITION_FRONT = 1; // SPA 503-506 constexpr uint16 PET_RAMPAGE_CHANCE = 0; // SPA 464,465 constexpr uint16 PET_RAMPAGE_DMG_MOD = 1; // SPA 465,465 constexpr uint16 SKILLPROC_CHANCE = 0; // SPA 427 diff --git a/zone/mob.cpp b/zone/mob.cpp index 64333363b..ab68f0f6c 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3865,8 +3865,8 @@ int32 Mob::GetPositionalDmgTaken(Mob *attacker) int back_arc = 0; int total_mod = 0; - back_arc += itembonuses.Damage_Taken_Position_Mod[SBIndex::POSITIONAL_DAMAGE_MOD] + aabonuses.Damage_Taken_Position_Mod[SBIndex::POSITIONAL_DAMAGE_MOD] + spellbonuses.Damage_Taken_Position_Mod[SBIndex::POSITIONAL_DAMAGE_MOD]; - front_arc += itembonuses.Damage_Taken_Position_Mod[SBIndex::POSITIONAL_LOCATION] + aabonuses.Damage_Taken_Position_Mod[SBIndex::POSITIONAL_LOCATION] + spellbonuses.Damage_Taken_Position_Mod[SBIndex::POSITIONAL_LOCATION]; + back_arc += itembonuses.Damage_Taken_Position_Mod[SBIndex::POSITION_BACK] + aabonuses.Damage_Taken_Position_Mod[SBIndex::POSITION_BACK] + spellbonuses.Damage_Taken_Position_Mod[SBIndex::POSITION_BACK]; + front_arc += itembonuses.Damage_Taken_Position_Mod[SBIndex::POSITION_FRONT] + aabonuses.Damage_Taken_Position_Mod[SBIndex::POSITION_FRONT] + spellbonuses.Damage_Taken_Position_Mod[SBIndex::POSITION_FRONT]; if (back_arc || front_arc) { //Do they have this bonus? if (attacker->BehindMob(this, attacker->GetX(), attacker->GetY()))//Check if attacker is striking from behind @@ -3883,6 +3883,29 @@ int32 Mob::GetPositionalDmgTaken(Mob *attacker) return total_mod; } +int32 Mob::GetPositionalDmgTakenAmt(Mob *attacker) +{ + if (!attacker) + return 0; + + int front_arc = 0; + int back_arc = 0; + int total_amt = 0; + + back_arc += itembonuses.Damage_Taken_Position_Amt[SBIndex::POSITION_BACK] + aabonuses.Damage_Taken_Position_Amt[SBIndex::POSITION_BACK] + spellbonuses.Damage_Taken_Position_Amt[SBIndex::POSITION_BACK]; + front_arc += itembonuses.Damage_Taken_Position_Amt[SBIndex::POSITION_FRONT] + aabonuses.Damage_Taken_Position_Amt[SBIndex::POSITION_FRONT] + spellbonuses.Damage_Taken_Position_Amt[SBIndex::POSITION_FRONT]; + + if (back_arc || front_arc) { + if (attacker->BehindMob(this, attacker->GetX(), attacker->GetY())) + total_amt = back_arc; + else + total_amt = front_arc; + } + + return total_amt; +} + + int16 Mob::GetHealRate(uint16 spell_id, Mob* caster) { int16 heal_rate = 0; @@ -4859,8 +4882,8 @@ int16 Mob::GetMeleeDmgPositionMod(Mob* defender) int back_arc = 0; int total_mod = 0; - back_arc += itembonuses.Melee_Damage_Position_Mod[SBIndex::POSITIONAL_DAMAGE_MOD] + aabonuses.Melee_Damage_Position_Mod[SBIndex::POSITIONAL_DAMAGE_MOD] + spellbonuses.Melee_Damage_Position_Mod[SBIndex::POSITIONAL_DAMAGE_MOD]; - front_arc += itembonuses.Melee_Damage_Position_Mod[SBIndex::POSITIONAL_LOCATION] + aabonuses.Melee_Damage_Position_Mod[SBIndex::POSITIONAL_LOCATION] + spellbonuses.Melee_Damage_Position_Mod[SBIndex::POSITIONAL_LOCATION]; + back_arc += itembonuses.Melee_Damage_Position_Mod[SBIndex::POSITION_BACK] + aabonuses.Melee_Damage_Position_Mod[SBIndex::POSITION_BACK] + spellbonuses.Melee_Damage_Position_Mod[SBIndex::POSITION_BACK]; + front_arc += itembonuses.Melee_Damage_Position_Mod[SBIndex::POSITION_FRONT] + aabonuses.Melee_Damage_Position_Mod[SBIndex::POSITION_FRONT] + spellbonuses.Melee_Damage_Position_Mod[SBIndex::POSITION_FRONT]; if (back_arc || front_arc) { //Do they have this bonus? if (BehindMob(defender, GetX(), GetY()))//Check if attacker is striking from behind @@ -4899,6 +4922,30 @@ int16 Mob::GetSkillDmgAmt(uint16 skill) return skill_dmg; } +int16 Mob::GetPositionalDmgAmt(Mob* defender) +{ + if (!defender) + return 0; + + //SPA 504 + int front_arc_dmg_amt = 0; + int back_arc_dmg_amt = 0; + + int total_amt = 0; + + back_arc_dmg_amt += itembonuses.Melee_Damage_Position_Amt[SBIndex::POSITION_BACK] + aabonuses.Melee_Damage_Position_Amt[SBIndex::POSITION_BACK] + spellbonuses.Melee_Damage_Position_Amt[SBIndex::POSITION_BACK]; + front_arc_dmg_amt += itembonuses.Melee_Damage_Position_Amt[SBIndex::POSITION_FRONT] + aabonuses.Melee_Damage_Position_Amt[SBIndex::POSITION_FRONT] + spellbonuses.Melee_Damage_Position_Amt[SBIndex::POSITION_FRONT]; + + if (back_arc_dmg_amt || front_arc_dmg_amt) { + if (BehindMob(defender, GetX(), GetY())) + total_amt = back_arc_dmg_amt; + else + total_amt = front_arc_dmg_amt; + } + + return total_amt; +} + void Mob::MeleeLifeTap(int32 damage) { int32 lifetap_amt = 0; diff --git a/zone/mob.h b/zone/mob.h index acabdce05..49b3fa2c8 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -806,6 +806,7 @@ public: int32 GetFocusIncoming(focusType type, int effect, Mob *caster, uint32 spell_id); int32 GetSkillDmgTaken(const EQ::skills::SkillType skill_used, ExtraAttackOptions *opts = nullptr); int32 GetPositionalDmgTaken(Mob *attacker); + int32 GetPositionalDmgTakenAmt(Mob *attacker); void DoKnockback(Mob *caster, uint32 pushback, uint32 pushup); int16 CalcResistChanceBonus(); int16 CalcFearResistChance(); @@ -823,6 +824,7 @@ public: int16 GetSkillReuseTime(uint16 skill); int GetCriticalChanceBonus(uint16 skill); int16 GetSkillDmgAmt(uint16 skill); + int16 GetPositionalDmgAmt(Mob* defender); bool TryReflectSpell(uint32 spell_id); inline bool CanBlockSpell() const { return(spellbonuses.FocusEffects[focusBlockNextSpell]); } bool DoHPToManaCovert(uint16 mana_cost = 0); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index fa2d1eca4..11dfb5b38 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3212,6 +3212,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_Critical_Melee_Damage_Mod_Max: case SE_Melee_Damage_Position_Mod: case SE_Damage_Taken_Position_Mod: + case SE_Melee_Damage_Position_Amt: + case SE_Damage_Taken_Position_Amt: case SE_DS_Mitigation_Amount: case SE_DS_Mitigation_Percentage: case SE_Double_Backstab_Front: From 2c01fe59ce50f8fd9a72c2dc7190dafa56b72b3c Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 10 Aug 2021 19:39:12 -0400 Subject: [PATCH 150/624] [Spells] Implemented SPA 489 SE_Worn_Endurance_Regen_Cap (#1493) Implemented SE_Worn_Endurance_Regen_Cap 489 modify worn item regen cap base: amt, limit: none, max: none Also added support to allow item mana regen cap to check item and spell bonuses. --- common/spdat.h | 2 +- zone/bonuses.cpp | 17 +++++++++++++++++ zone/client_mods.cpp | 4 ++-- zone/common.h | 1 + zone/spell_effects.cpp | 2 +- 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 5e29cc4e3..2a53e9aaa 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -852,7 +852,7 @@ typedef enum { #define SE_Ff_Same_Caster 486 // implemented, @Ff, Caster of spell on target with a focus effect that is checked by incoming spells, base1: 0=Must be different caster 1=Must be same caster //#define SE_Extend_Tradeskill_Cap 487 // //#define SE_Defender_Melee_Force_Pct_PC 488 // -//#define SE_Worn_Endurance_Regen_Cap 489 // +#define SE_Worn_Endurance_Regen_Cap 489 // implemented, modify worn regen cap, base: amt, limit: none, max: none #define SE_Ff_ReuseTimeMin 490 // implemented, @Ff, Minimum recast time of a spell that can be focused, base: recast time #define SE_Ff_ReuseTimeMax 491 // implemented, @Ff, Max recast time of a spell that can be focused, base: recast time #define SE_Ff_Endurance_Min 492 // implemented, @Ff, Minimum endurance cost of a spell that can be focused, base: endurance cost diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 8c4b5893f..9ba4f4237 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1628,6 +1628,9 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) break; } + case SE_Worn_Endurance_Regen_Cap: + newbon->ItemEnduranceRegenCap += base1; + break; // to do case SE_PetDiscipline: @@ -3541,6 +3544,14 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne new_bonus->Pet_Add_Atk += effect_value; break; + case SE_Worn_Endurance_Regen_Cap: + new_bonus->ItemEnduranceRegenCap += effect_value; + break; + + case SE_ItemManaRegenCapIncrease: + new_bonus->ItemManaRegenCap += effect_value; + break; + case SE_Weapon_Stance: { if (IsValidSpell(effect_value)) { //base1 is the spell_id of buff if (base2 <= WEAPON_STANCE_TYPE_MAX) { //0=2H, 1=Shield, 2=DW @@ -4925,6 +4936,12 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) itembonuses.ItemHPRegenCap = effect_value; break; + case SE_Worn_Endurance_Regen_Cap: + spellbonuses.ItemEnduranceRegenCap = effect_value; + aabonuses.ItemEnduranceRegenCap = effect_value; + itembonuses.ItemEnduranceRegenCap = effect_value; + break; + case SE_OffhandRiposteFail: spellbonuses.OffhandRiposteFail = effect_value; aabonuses.OffhandRiposteFail = effect_value; diff --git a/zone/client_mods.cpp b/zone/client_mods.cpp index d31bc6c9b..f51406150 100644 --- a/zone/client_mods.cpp +++ b/zone/client_mods.cpp @@ -781,7 +781,7 @@ int32 Client::CalcManaRegen(bool bCombat) int32 Client::CalcManaRegenCap() { - int32 cap = RuleI(Character, ItemManaRegenCap) + aabonuses.ItemManaRegenCap; + int32 cap = RuleI(Character, ItemManaRegenCap) + aabonuses.ItemManaRegenCap + itembonuses.ItemManaRegenCap + spellbonuses.ItemManaRegenCap; return (cap * RuleI(Character, ManaRegenMultiplier) / 100); } @@ -1751,7 +1751,7 @@ int32 Client::CalcEnduranceRegen(bool bCombat) int32 Client::CalcEnduranceRegenCap() { - int cap = RuleI(Character, ItemEnduranceRegenCap); + int cap = RuleI(Character, ItemEnduranceRegenCap) + aabonuses.ItemEnduranceRegenCap + itembonuses.ItemEnduranceRegenCap + spellbonuses.ItemEnduranceRegenCap; return (cap * RuleI(Character, EnduranceRegenMultiplier) / 100); } diff --git a/zone/common.h b/zone/common.h index ddcb034fc..feccc7910 100644 --- a/zone/common.h +++ b/zone/common.h @@ -549,6 +549,7 @@ struct StatBonuses { int32 DS_Mitigation_Percentage; // base = percent amt of DS mitigation. Negative value to reduce int32 Pet_Crit_Melee_Damage_Pct_Owner; // base = percent mod for pet critcal damage from owner int32 Pet_Add_Atk; // base = Pet ATK bonus from owner + int32 ItemEnduranceRegenCap; // modify endurance regen cap int32 WeaponStance[WEAPON_STANCE_TYPE_MAX +1];// base = trigger spell id, base2 = 0 is 2h, 1 is shield, 2 is dual wield, [0]spid 2h, [1]spid shield, [2]spid DW // AAs diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 11dfb5b38..a8626c570 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3238,8 +3238,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_AddExtraAttackPct_1h_Primary: case SE_AddExtraAttackPct_1h_Secondary: case SE_Skill_Base_Damage_Mod: + case SE_Worn_Endurance_Regen_Cap: case SE_Buy_AA_Rank: - { break; } From b539c63326bb6e1a89bcfb09f38358be793355ea Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 10 Aug 2021 19:40:07 -0400 Subject: [PATCH 151/624] [cmake] Update min cmake version due to fe7cb76 (#1489) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 351aca1a7..9022ea306 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 3.2) +CMAKE_MINIMUM_REQUIRED(VERSION 3.7) SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/" ${CMAKE_MODULE_PATH}) From c82139736743c207338523d59ae56c043801e620 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 11 Aug 2021 02:38:38 -0400 Subject: [PATCH 152/624] [Spells] Implemented SPA 471 SE_Double_Melee_Round (#1492) * Implemented SPA 471 SE_Double_Melee_Round #define SE_Double_Melee_Round 471 Percent chance to repeat primary weapon round with a percent damage modifier, base: pct chance repeat, limit: pct dmg mod, max: none * minor fixes * tab to spaces --- common/spdat.h | 2 +- zone/bonuses.cpp | 29 +++++++++++++++++++++++++++++ zone/client_process.cpp | 5 +++++ zone/common.h | 3 +++ zone/mob.cpp | 20 ++++++++++++++++++++ zone/mob.h | 5 +++++ zone/spell_effects.cpp | 1 + 7 files changed, 64 insertions(+), 1 deletion(-) diff --git a/common/spdat.h b/common/spdat.h index 2a53e9aaa..816d8a908 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -834,7 +834,7 @@ typedef enum { #define SE_DS_Mitigation_Percentage 468 // implemented - Modify incoming damage shield damage by percentage #define SE_Chance_Best_in_Spell_Grp 469 // implemented - Chance to cast highest scribed spell within a spell group. All base2 spells share roll chance, only 1 cast. #define SE_Trigger_Best_in_Spell_Grp 470 // implemented - Chance to cast highest scribed spell within a spell group. Each spell has own chance. -//#define SE_Double_Melee_Round 471 // +#define SE_Double_Melee_Round 471 // implemented, @OffBonus, percent chance to repeat primary weapon round with a percent damage modifier, base: pct chance repeat, limit: pct dmg mod, max: none #define SE_Buy_AA_Rank 472 // implemented, @Special, Used in AA abilities that have Enable/Disable toggle. Spell on Disabled Rank has this effect in it, base: 1, limit: none, max: none, Note: This will not just buy an AA #define SE_Double_Backstab_Front 473 // implemented - Chance to double backstab from front #define SE_Pet_Crit_Melee_Damage_Pct_Owner 474 // implemenetd - Critical damage mod applied to pets from owner diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 9ba4f4237..94660345f 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1628,6 +1628,15 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) break; } + case SE_Double_Melee_Round: + { + if (newbon->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] < base1) { + newbon->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] = base1; + newbon->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_DMG_BONUS] = base2; + } + break; + } + case SE_Worn_Endurance_Regen_Cap: newbon->ItemEnduranceRegenCap += base1; break; @@ -2487,6 +2496,20 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne break; } + case SE_Double_Melee_Round: + { + if (AdditiveWornBonus) { + new_bonus->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] += effect_value; + new_bonus->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_DMG_BONUS] += base2; + } + + if (new_bonus->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] < effect_value) { + new_bonus->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] = effect_value; + new_bonus->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_DMG_BONUS] = base2; + } + break; + } + case SE_PercentXPIncrease: { if(new_bonus->XPRateMod < effect_value) @@ -4472,6 +4495,12 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) itembonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; break; + case SE_Double_Melee_Round: + spellbonuses.DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] = effect_value; + aabonuses.DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] = effect_value; + itembonuses.DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] = effect_value; + break; + case SE_PercentXPIncrease: spellbonuses.XPRateMod = effect_value; aabonuses.XPRateMod = effect_value; diff --git a/zone/client_process.cpp b/zone/client_process.cpp index d805be19a..c984ee800 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -394,6 +394,11 @@ bool Client::Process() { TriggerDefensiveProcs(auto_attack_target, EQ::invslot::slotPrimary, false); DoAttackRounds(auto_attack_target, EQ::invslot::slotPrimary); + + if (TryDoubleMeleeRoundEffect()) { + DoAttackRounds(auto_attack_target, EQ::invslot::slotPrimary); + } + if (CheckAATimer(aaTimerRampage)) { entity_list.AEAttack(this, 30); } diff --git a/zone/common.h b/zone/common.h index feccc7910..5afde8019 100644 --- a/zone/common.h +++ b/zone/common.h @@ -454,6 +454,7 @@ struct StatBonuses { int32 ExtraAttackChance[2]; // base chance(w/ 2H weapon)=0, amt of extra attacks=1 int32 ExtraAttackChancePrimary[2]; // base chance=0, , amt of extra attacks=1 int32 ExtraAttackChanceSecondary[2]; // base chance=0, , amt of extra attacks=1 + int32 DoubleMeleeRound[2]; // base chance=0, damage mod=1 int32 DoTShielding; int32 DivineSaveChance[2]; // Second Chance (base1 = chance, base2 = spell on trigger) uint32 DeathSave[4]; // Death Pact [0](value = 1 partial 2 = full) [1]=slot [2]=LvLimit [3]=HealAmt @@ -671,6 +672,8 @@ namespace SBIndex { constexpr uint16 FINISHING_EFFECT_DMG = 1; // SPA 278, 439, 217 constexpr uint16 FINISHING_EFFECT_LEVEL_MAX = 0; // SPA 440, 345, 346 constexpr uint16 FINISHING_EFFECT_LEVEL_CHANCE_BONUS = 1; // SPA 440, 345, 346 + constexpr uint16 DOUBLE_MELEE_ROUND_CHANCE = 0; // SPA 471 + constexpr uint16 DOUBLE_MELEE_ROUND_DMG_BONUS = 1; // SPA 471 }; diff --git a/zone/mob.cpp b/zone/mob.cpp index ab68f0f6c..bf537e7ce 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -471,6 +471,8 @@ Mob::Mob( AssistAggro = false; npc_assist_cap = 0; + use_double_melee_round_dmg_bonus = false; + #ifdef BOTS m_manual_follow = false; #endif @@ -4841,6 +4843,10 @@ int16 Mob::GetMeleeDamageMod_SE(uint16 skill) dmg_mod += itembonuses.DamageModifier3[EQ::skills::HIGHEST_SKILL + 1] + spellbonuses.DamageModifier3[EQ::skills::HIGHEST_SKILL + 1] + aabonuses.DamageModifier3[EQ::skills::HIGHEST_SKILL + 1] + itembonuses.DamageModifier3[skill] + spellbonuses.DamageModifier3[skill] + aabonuses.DamageModifier3[skill]; + if (GetUseDoubleMeleeRoundDmgBonus()) { + dmg_mod += itembonuses.DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_DMG_BONUS] + spellbonuses.DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_DMG_BONUS] + aabonuses.DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_DMG_BONUS]; + } + if(dmg_mod < -100) dmg_mod = -100; @@ -4964,6 +4970,20 @@ void Mob::MeleeLifeTap(int32 damage) { } } +bool Mob::TryDoubleMeleeRoundEffect() { + + auto chance = aabonuses.DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] + itembonuses.DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] + + spellbonuses.DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE]; + + if (chance && zone->random.Roll(chance)) { + SetUseDoubleMeleeRoundDmgBonus(true); + return true; + } + + SetUseDoubleMeleeRoundDmgBonus(false); + return false; +} + bool Mob::TryReflectSpell(uint32 spell_id) { if (!spells[spell_id].reflectable) diff --git a/zone/mob.h b/zone/mob.h index 49b3fa2c8..2f0c166b0 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -844,6 +844,10 @@ public: bool CanFocusUseRandomEffectivenessByType(focusType type); int GetFocusRandomEffectivenessValue(int focus_base, int focus_base2, bool best_focus = 0); + bool TryDoubleMeleeRoundEffect(); + bool GetUseDoubleMeleeRoundDmgBonus() const { return use_double_melee_round_dmg_bonus; } + inline void SetUseDoubleMeleeRoundDmgBonus(bool val) { use_double_melee_round_dmg_bonus = val; } + void CastSpellOnLand(Mob* caster, int32 spell_id); void FocusProcLimitProcess(); bool ApplyFocusProcLimiter(int32 spell_id, int buffslot = -1); @@ -1498,6 +1502,7 @@ protected: bool has_twohandbluntequiped; bool has_twohanderequipped; bool has_duelweaponsequiped; + bool use_double_melee_round_dmg_bonus; bool can_facestab; bool has_numhits; bool has_MGB; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index a8626c570..5160e32ce 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3237,6 +3237,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_Ff_Value_Max: case SE_AddExtraAttackPct_1h_Primary: case SE_AddExtraAttackPct_1h_Secondary: + case SE_Double_Melee_Round: case SE_Skill_Base_Damage_Mod: case SE_Worn_Endurance_Regen_Cap: case SE_Buy_AA_Rank: From 9c62bf3c2f44a1c896ad662a92c37c354be620c8 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 13 Aug 2021 16:01:29 -0400 Subject: [PATCH 153/624] [Spells] Fix SPA 209 SE_DispelBeneficial not causing aggro (#1495) Fix SPA 209 SE_DispelBeneficial not causing aggro. --- zone/aggro.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/aggro.cpp b/zone/aggro.cpp index a3f52fa55..9d9673568 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -1105,6 +1105,7 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc) } case SE_CancelMagic: case SE_DispelDetrimental: + case SE_DispelBeneficial: dispel = true; break; case SE_ReduceHate: From d40d21121a7471a2b68d6dca1ae4f213bd5b0d69 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 15 Aug 2021 23:59:10 -0400 Subject: [PATCH 154/624] [Feature] Implemented /shield ability and related affects (#1494) * shield ability initial work * updates * update * updates * Update client_process.cpp * major updates optimized pet support perl support * updates * minor update * fix merge error * requested changes * variable fix * optimization * minor update * Revert "optimization" This reverts commit 27e11e758b23933ba8b6878d12d3eeb1e780aeda. * fix reset variables on shield_target if shielder dies or zones during shielding. * edge case fix Catch and fix situations where shield target doesn't have shielder variable cleared. Can occur if shielder . uses ability when target is not in combat then zones. * combined packet and mob function Shield now uses a common pathway through ShieldAbility, added parameters to perl function * Addressing formatting for Kayen * Fix function typo Co-authored-by: Akkadius --- common/ptimer.h | 2 + common/spdat.h | 8 +-- zone/attack.cpp | 56 +++++++++++++++-- zone/bonuses.cpp | 49 +++++++++++++-- zone/client.cpp | 13 ---- zone/client.h | 1 - zone/client_packet.cpp | 107 +++++++++++-------------------- zone/client_process.cpp | 30 +-------- zone/common.h | 13 ++-- zone/mob.cpp | 135 +++++++++++++++++++++++++++++++++++++--- zone/mob.h | 34 +++++++--- zone/mob_ai.cpp | 4 ++ zone/perl_mob.cpp | 86 +++++++++++++------------ zone/spell_effects.cpp | 14 ++++- zone/string_ids.h | 2 + 15 files changed, 363 insertions(+), 191 deletions(-) diff --git a/common/ptimer.h b/common/ptimer.h index 2232b55b5..349f20534 100644 --- a/common/ptimer.h +++ b/common/ptimer.h @@ -45,6 +45,8 @@ enum : int { //values for pTimerType pTimerLinkedSpellReuseStart = 28, pTimerLinkedSpellReuseEnd = 48, + pTimerShieldAbility = 86, + pTimerLayHands = 87, //these IDs are used by client too pTimerHarmTouch = 89, //so dont change them diff --git a/common/spdat.h b/common/spdat.h index 816d8a908..8c5726c53 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -573,7 +573,7 @@ typedef enum { #define SE_FleshToBone 207 // implemented //#define SE_PurgePoison 208 // not used #define SE_DispelBeneficial 209 // implemented -//#define SE_PetShield 210 // *not implemented +#define SE_PetShield 210 // implmented, @ShieldAbility, allows pet to 'shield' owner for 50 pct of damage taken for a duration, base: Time multiplier 1=12 seconds, 2=24 ect, limit: mitigation on pet owner override (not on live), max: mitigation on pet overide (not on live) #define SE_AEMelee 211 // implemented TO DO: Implement to allow NPC use (client only atm). #define SE_FrenziedDevastation 212 // implemented - increase spell criticals + all DD spells cast 2x mana. #define SE_PetMaxHP 213 // implemented[AA] - increases the maximum hit points of your pet @@ -593,7 +593,7 @@ typedef enum { #define SE_ReduceSkillTimer 227 // implemented #define SE_ReduceFallDamage 228 // implented - reduce the damage that you take from falling #define SE_PersistantCasting 229 // implemented -#define SE_ExtendedShielding 230 // not used as bonus - increase range of /shield ability +#define SE_ExtendedShielding 230 // implemented, @ShieldAbility, extends the range of your /shield ability by an amount of distance, base: distance units, limit: none, max: none #define SE_StunBashChance 231 // implemented - increase chance to stun from bash. #define SE_DivineSave 232 // implemented (base1 == % chance on death to insta-res) (base2 == spell cast on save) #define SE_Metabolism 233 // implemented - Modifies food/drink consumption rates. @@ -618,7 +618,7 @@ typedef enum { #define SE_FrontalBackstabChance 252 // implemented[AA] - chance to perform a full damage backstab from front. #define SE_FrontalBackstabMinDmg 253 // implemented[AA] - allow a frontal backstab for mininum damage. #define SE_Blank 254 // implemented -#define SE_ShieldDuration 255 // not implemented as bonus - increases duration of /shield +#define SE_ShieldDuration 255 // implemented, , @ShieldAbility, extends the duration of your /shield ability, base: seconds, limit: none, max: none #define SE_ShroudofStealth 256 // implemented #define SE_PetDiscipline 257 // not implemented as bonus - /pet hold - official name is GivePetHold #define SE_TripleBackstab 258 // implemented[AA] - chance to perform a triple backstab @@ -729,7 +729,7 @@ typedef enum { #define SE_BandolierSlots 363 // *not implemented[AA] 'Battle Ready' expands the bandolier by one additional save slot per rank. #define SE_TripleAttackChance 364 // implemented #define SE_ProcOnSpellKillShot 365 // implemented - chance to trigger a spell on kill when the kill is caused by a specific spell with this effect in it (10470 Venin) -#define SE_GroupShielding 366 // *not implemented[AA] This gives you /shieldgroup +//#define SE_GroupShielding 366 // *not implemented[AA] This gives you /shieldgroup #define SE_SetBodyType 367 // implemented - set body type of base1 so it can be affected by spells that are limited to that type (Plant, Animal, Undead, etc) //#define SE_FactionMod 368 // *not implemented - increases faction with base1 (faction id, live won't match up w/ ours) by base2 #define SE_CorruptionCounter 369 // implemented diff --git a/zone/attack.cpp b/zone/attack.cpp index 0a828fcb7..fac1301e5 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1655,9 +1655,7 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQ::skills::Skill int exploss = 0; LogCombat("Fatal blow dealt by [{}] with [{}] damage, spell [{}], skill [{}]", killerMob ? killerMob->GetName() : "Unknown", damage, spell, attack_skill); - /* - #1: Send death packet to everyone - */ + // #1: Send death packet to everyone uint8 killed_level = GetLevel(); SendLogoutPackets(); @@ -1684,13 +1682,12 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQ::skills::Skill app.priority = 6; entity_list.QueueClients(this, &app); - /* - #2: figure out things that affect the player dying and mark them dead - */ + // #2: figure out things that affect the player dying and mark them dead InterruptSpell(); SetPet(0); SetHorseId(0); + ShieldAbilityClearVariables(); dead = true; if (GetMerc()) { @@ -2252,6 +2249,8 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy Log(Logs::Detail, Logs::Attack, "%s Mobs currently Aggro %i", __FUNCTION__, zone->MobsAggroCount()); } + ShieldAbilityClearVariables(); + SetHP(0); SetPet(0); @@ -5278,9 +5277,54 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac hit.damage_done += (hit.damage_done * pct_damage_reduction / 100) + (defender->GetFcDamageAmtIncoming(this, 0, true, hit.skill)) + defender->GetPositionalDmgTakenAmt(this); + if (defender->GetShielderID()) { + DoShieldDamageOnShielder(defender, hit.damage_done, hit.skill); + hit.damage_done -= hit.damage_done * defender->GetShieldTargetMitigation() / 100; //Default shielded takes 50 pct damage + } + CheckNumHitsRemaining(NumHit::OutgoingHitSuccess); } +void Mob::DoShieldDamageOnShielder(Mob *shield_target, int hit_damage_done, EQ::skills::SkillType skillInUse) +{ + if (!shield_target) { + return; + } + + Mob *shielder = entity_list.GetMob(shield_target->GetShielderID()); + if (!shielder) { + shield_target->SetShielderID(0); + shield_target->SetShieldTargetMitigation(0); + return; + } + + if (shield_target->CalculateDistance(shielder->GetX(), shielder->GetY(), shielder->GetZ()) > static_cast(shielder->GetMaxShielderDistance())) { + shielder->SetShieldTargetID(0); + shielder->SetShielderMitigation(0); + shielder->SetShielderMaxDistance(0); + shielder->shield_timer.Disable(); + shield_target->SetShielderID(0); + shield_target->SetShieldTargetMitigation(0); + return; //Too far away, no message is given thoughh. + } + + int mitigation = shielder->GetShielderMitigation(); //Default shielder mitigates 25 pct of damage taken, this can be increased up to max 50 by equiping a shield item + if (shielder->IsClient() && shielder->HasShieldEquiped()) { + EQ::ItemInstance* inst = shielder->CastToClient()->GetInv().GetItem(EQ::invslot::slotSecondary); + if (inst) { + const EQ::ItemData* shield = inst->GetItem(); + if (shield && shield->ItemType == EQ::item::ItemTypeShield) { + mitigation += shield->AC * 50 / 100; //1% increase per 2 AC + std::min(50, mitigation);//50 pct max mitigation bonus from /shield + } + } + } + + hit_damage_done -= hit_damage_done * mitigation / 100; + shielder->Damage(this, hit_damage_done, SPELL_UNKNOWN, skillInUse, true, -1, false, m_specialattacks); + shielder->CheckNumHitsRemaining(NumHit::OutgoingHitSuccess); +} + void Mob::CommonBreakInvisibleFromCombat() { //break invis when you attack diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 94660345f..cf1a0ccc0 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1633,6 +1633,23 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) if (newbon->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] < base1) { newbon->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] = base1; newbon->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_DMG_BONUS] = base2; + + } + break; + } + + case SE_ExtendedShielding: + { + if (newbon->ExtendedShielding < base1) { + newbon->ExtendedShielding = base1; + } + break; + } + + case SE_ShieldDuration: + { + if (newbon->ShieldDuration < base1) { + newbon->ShieldDuration = base1; } break; } @@ -1650,10 +1667,6 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) break; case SE_SecondaryForte: break; - case SE_ExtendedShielding: - break; - case SE_ShieldDuration: - break; case SE_ReduceApplyPoisonTime: break; case SE_NimbleEvasion: @@ -3567,6 +3580,34 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne new_bonus->Pet_Add_Atk += effect_value; break; + case SE_ExtendedShielding: + { + if (AdditiveWornBonus) { + new_bonus->ExtendedShielding += effect_value; + } + else if (effect_value < 0 && new_bonus->ExtendedShielding > effect_value){ + new_bonus->ExtendedShielding = effect_value; + } + else if (effect_value > 0 && new_bonus->ExtendedShielding < effect_value){ + new_bonus->ExtendedShielding = effect_value; + } + break; + } + + case SE_ShieldDuration: + { + if (AdditiveWornBonus) { + new_bonus->ShieldDuration += effect_value; + } + else if (effect_value < 0 && new_bonus->ShieldDuration > effect_value){ + new_bonus->ShieldDuration = effect_value; + } + else if (effect_value > 0 && new_bonus->ShieldDuration < effect_value){ + new_bonus->ShieldDuration = effect_value; + } + break; + } + case SE_Worn_Endurance_Regen_Cap: new_bonus->ItemEnduranceRegenCap += effect_value; break; diff --git a/zone/client.cpp b/zone/client.cpp index 98a3deec6..040601ad0 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -138,7 +138,6 @@ Client::Client(EQStreamInterface* ieqs) linkdead_timer(RuleI(Zone,ClientLinkdeadMS)), dead_timer(2000), global_channel_timer(1000), - shield_timer(500), fishing_timer(8000), endupkeep_timer(1000), forget_timer(0), @@ -200,7 +199,6 @@ Client::Client(EQStreamInterface* ieqs) account_id = 0; admin = 0; lsaccountid = 0; - shield_target = nullptr; guild_id = GUILD_NONE; guildrank = 0; GuildBanker = false; @@ -236,7 +234,6 @@ Client::Client(EQStreamInterface* ieqs) pQueuedSaveWorkID = 0; position_update_same_count = 0; fishing_timer.Disable(); - shield_timer.Disable(); dead_timer.Disable(); camp_timer.Disable(); autosave_timer.Disable(); @@ -420,16 +417,6 @@ Client::~Client() { } } - if (shield_target) { - 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; - } - } - shield_target = nullptr; - } - if(GetTarget()) GetTarget()->IsTargeted(-1); diff --git a/zone/client.h b/zone/client.h index 06f57c8dd..ff80cebc7 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1804,7 +1804,6 @@ private: Timer linkdead_timer; Timer dead_timer; Timer global_channel_timer; - Timer shield_timer; Timer fishing_timer; Timer endupkeep_timer; Timer forget_timer; // our 2 min everybody forgets you timer diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 08a442b85..50d3cd375 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -12806,87 +12806,52 @@ void Client::Handle_OP_SetTitle(const EQApplicationPacket *app) void Client::Handle_OP_Shielding(const EQApplicationPacket *app) { + /* + /shield command mechanics + Warriors get this skill at level 30 + Used by typing /shield while targeting a player + While active for the duration of 12 seconds baseline. The 'shield target' will take 50 pct less damage and + the 'shielder' will be hit with the damage taken by the 'shield target' after all applicable mitigiont is calculated, + the damage on the 'shielder' will be reduced by 25 percent, this reduction can be increased to 50 pct if equiping a shield. + You receive a 1% increase in mitigation for every 2 AC on the shield. + Shielder must stay with in a close distance (15 units) to your 'shield target'. If either move out of range, shield ends, no message given. + Both duration and shield range can be modified by AA. + Recast is 3 minutes. + + For custom use cases, Mob::ShieldAbility can be used in quests with all parameters being altered. This functional + is also used for SPA 201 SE_PetShield, which functions in a simalar manner with pet shielding owner. + + Note: If either the shielder or the shield target die all variables are reset on both. + + */ + if (app->size != sizeof(Shielding_Struct)) { LogError("OP size error: OP_Shielding expected:[{}] got:[{}]", sizeof(Shielding_Struct), app->size); return; } - if (GetClass() != WARRIOR) - { + + if (GetLevel() < 30) { //Client gives message + return; + } + + if (GetClass() != WARRIOR){ return; } - if (shield_target) - { - entity_list.MessageCloseString( - 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; - } - } + pTimerType timer = pTimerShieldAbility; + + if (!p_timers.Expired(&database, timer, false)) { + uint32 remain = p_timers.GetRemainingTime(timer); + Message(Chat::White, "You can use the ability /shield in %d minutes %d seconds.", ((remain) / 60), (remain % 60)); + return; } + Shielding_Struct* shield = (Shielding_Struct*)app->pBuffer; - shield_target = entity_list.GetMob(shield->target_id); - bool ack = false; - EQ::ItemInstance* inst = GetInv().GetItem(EQ::invslot::slotSecondary); - if (!shield_target) - return; - if (inst) - { - const EQ::ItemData* shield = inst->GetItem(); - if (shield && shield->ItemType == EQ::item::ItemTypeShield) - { - for (int x = 0; x < 2; x++) - { - if (shield_target->shielder[x].shielder_id == 0) - { - entity_list.MessageCloseString( - 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) - { - MessageString(Chat::White, ALREADY_SHIELDED); - shield_target = 0; - return; + + if (ShieldAbility(shield->target_id, 15, 12000, 50, 25, true, false)) { + p_timers.Start(timer, SHIELD_ABILITY_RECAST_TIME); } + return; } diff --git a/zone/client_process.cpp b/zone/client_process.cpp index c984ee800..6106ee8ae 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -464,33 +464,9 @@ bool Client::Process() { if (gravity_timer.Check()) DoGravityEffect(); } - - if (shield_timer.Check()) - { - if (shield_target) - { - if (!CombatRange(shield_target)) - { - entity_list.MessageCloseString( - this, false, 100, 0, - END_SHIELDING, GetCleanName(), shield_target->GetCleanName()); - 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; - } - } - shield_target = 0; - shield_timer.Disable(); - } - } - else - { - shield_target = 0; - shield_timer.Disable(); - } + + if (shield_timer.Check()) { + ShieldAbilityFinish(); } SpellProcess(); diff --git a/zone/common.h b/zone/common.h index 5afde8019..4945a880c 100644 --- a/zone/common.h +++ b/zone/common.h @@ -22,8 +22,6 @@ #define SEE_POSITION 0.5f //ratio of GetSize() where NPCs try to see for LOS #define CHECK_LOS_STEP 1.0f -#define MAX_SHIELDERS 2 //I dont know if this is based on a client limit - #define ARCHETYPE_HYBRID 1 #define ARCHETYPE_CASTER 2 #define ARCHETYPE_MELEE 3 @@ -109,6 +107,8 @@ #define WEAPON_STANCE_TYPE_MAX 2 +#define SHIELD_ABILITY_RECAST_TIME 180 + typedef enum { //focus types focusSpellHaste = 1, //@Fc, SPA: 127, SE_IncreaseSpellHaste, On Caster, cast time mod pct, base: pct focusSpellDuration, //@Fc, SPA: 128, SE_IncreaseSpellDuration, On Caster, spell duration mod pct, base: pct @@ -553,8 +553,11 @@ struct StatBonuses { int32 ItemEnduranceRegenCap; // modify endurance regen cap int32 WeaponStance[WEAPON_STANCE_TYPE_MAX +1];// base = trigger spell id, base2 = 0 is 2h, 1 is shield, 2 is dual wield, [0]spid 2h, [1]spid shield, [2]spid DW + // AAs - int8 Packrat; //weight reduction for items, 1 point = 10% + int32 ShieldDuration; // extends duration of /shield ability + int32 ExtendedShielding; // extends range of /shield ability + int8 Packrat; // weight reduction for items, 1 point = 10% uint8 BuffSlotIncrease; // Increases number of available buff slots uint32 DelayDeath; // how far below 0 hp you can go int8 BaseMovementSpeed; // Adjust base run speed, does not stack with other movement bonuses. @@ -685,10 +688,6 @@ typedef struct int level_override; } tProc; -struct Shielders_Struct { - uint32 shielder_id; - uint16 shielder_bonus; -}; struct WeaponStance_Struct { bool enabled; diff --git a/zone/mob.cpp b/zone/mob.cpp index bf537e7ce..6d4dfc0e9 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -261,7 +261,6 @@ Mob::Mob( MR = CR = FR = DR = PR = Corrup = PhR = 0; ExtraHaste = 0; bEnraged = false; - shield_target = nullptr; current_mana = 0; max_mana = 0; hp_regen = in_hp_regen; @@ -376,11 +375,13 @@ Mob::Mob( silenced = false; amnesiad = false; inWater = false; - int m; - for (m = 0; m < MAX_SHIELDERS; m++) { - shielder[m].shielder_id = 0; - shielder[m].shielder_bonus = 0; - } + + shield_timer.Disable(); + m_shield_target_id = 0; + m_shielder_id = 0; + m_shield_target_mitigation = 0; + m_shielder_mitigation = 0; + m_shielder_max_distance = 0; destructibleobject = false; wandertype = 0; @@ -3144,7 +3145,7 @@ int32 Mob::GetActSpellCasttime(uint16 spell_id, int32 casttime) cast_reducer += cast_reducer_no_limit; casttime = casttime * (100 - cast_reducer) / 100; casttime -= cast_reducer_amt; - + return std::max(casttime, 0); } @@ -4944,11 +4945,11 @@ int16 Mob::GetPositionalDmgAmt(Mob* defender) if (back_arc_dmg_amt || front_arc_dmg_amt) { if (BehindMob(defender, GetX(), GetY())) - total_amt = back_arc_dmg_amt; + total_amt = back_arc_dmg_amt; else total_amt = front_arc_dmg_amt; } - + return total_amt; } @@ -6196,6 +6197,122 @@ float Mob::GetDefaultRaceSize() const { return GetRaceGenderDefaultHeight(race, gender); } +bool Mob::ShieldAbility(uint32 target_id, int shielder_max_distance, int shield_duration, int shield_target_mitigation, int shielder_mitigation, bool use_aa, bool can_shield_npc) +{ + Mob* shield_target = entity_list.GetMob(target_id); + if (!shield_target) { + return false; + } + + if (!can_shield_npc && shield_target->IsNPC()) { + if (IsClient()) { + MessageString(Chat::White, SHIELD_TARGET_NPC); + } + return false; + } + + if (shield_target->GetID() == GetID()) { //Client will give message "You can not shield yourself" + return false; + } + + //Edge case situations. If 'Shield Target' still has Shielder set but Shielder is not in zone. Catch and fix here. + if (shield_target->GetShielderID() && !entity_list.GetMob(shield_target->GetShielderID())) { + shield_target->SetShielderID(0); + } + + if (GetShielderID() && !entity_list.GetMob(GetShielderID())) { + SetShielderID(0); + } + + //You have a shielder, or your 'Shield Target' already has a 'Shielder' + if (GetShielderID() || shield_target->GetShielderID()) { + if (IsClient()) { + MessageString(Chat::White, ALREADY_SHIELDED); + } + return false; + } + + //You are being shielded or already have a 'Shield Target' + if (GetShieldTargetID() || shield_target->GetShieldTargetID()) { + if (IsClient()) { + MessageString(Chat::White, ALREADY_SHIELDING); + } + return false; + } + + //AA to increase SPA 230 extended shielding (default live is 15 distance units) + if (use_aa) { + shielder_max_distance += aabonuses.ExtendedShielding + itembonuses.ExtendedShielding + spellbonuses.ExtendedShielding; + shielder_max_distance = std::max(shielder_max_distance, 0); + } + + if (shield_target->CalculateDistance(GetX(), GetY(), GetZ()) > static_cast(shielder_max_distance)) { + return false; //Live does not give a message when out of range. + } + + entity_list.MessageCloseString(this, false, 100, 0, START_SHIELDING, GetCleanName(), shield_target->GetCleanName()); + + SetShieldTargetID(shield_target->GetID()); + SetShielderMitigation(shield_target_mitigation); + SetShielderMaxDistance(shielder_max_distance); + + shield_target->SetShielderID(GetID()); + shield_target->SetShieldTargetMitigation(shield_target_mitigation); + + //Calculate AA for adding time SPA 255 extend shield duration (Baseline ability is 12 seconds) + if (use_aa) { + shield_duration += (aabonuses.ShieldDuration + itembonuses.ShieldDuration + spellbonuses.ShieldDuration) * 1000; + shield_duration = std::max(shield_duration, 1); //Incase of negative modifiers lets just make min duration 1 ms. + } + + shield_timer.Start(static_cast(shield_duration)); + return true; +} + +void Mob::ShieldAbilityFinish() +{ + Mob* shield_target = entity_list.GetMob(GetShieldTargetID()); + + if (shield_target) { + entity_list.MessageCloseString(this, false, 100, 0, END_SHIELDING, GetCleanName(), shield_target->GetCleanName()); + shield_target->SetShielderID(0); + shield_target->SetShieldTargetMitigation(0); + } + SetShieldTargetID(0); + SetShielderMitigation(0); + SetShielderMaxDistance(0); + shield_timer.Disable(); +} + +void Mob::ShieldAbilityClearVariables() +{ + //If 'shield target' dies + if (GetShielderID()){ + Mob* shielder = entity_list.GetMob(GetShielderID()); + if (shielder) { + shielder->SetShieldTargetID(0); + shielder->SetShielderMitigation(0); + shielder->SetShielderMaxDistance(0); + shielder->shield_timer.Disable(); + } + SetShielderID(0); + SetShieldTargetMitigation(0); + } + + //If 'shielder' dies + if (GetShieldTargetID()) { + Mob* shield_target = entity_list.GetMob(GetShieldTargetID()); + if (shield_target) { + shield_target->SetShielderID(0); + shield_target->SetShieldTargetMitigation(0); + } + SetShieldTargetID(0); + SetShielderMitigation(0); + SetShielderMaxDistance(0); + shield_timer.Disable(); + } +} + #ifdef BOTS bool Mob::JoinHealRotationTargetPool(std::shared_ptr* heal_rotation) { diff --git a/zone/mob.h b/zone/mob.h index 2f0c166b0..a951d7cb3 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -843,11 +843,11 @@ public: bool HarmonySpellLevelCheck(int32 spell_id, Mob* target = nullptr); bool CanFocusUseRandomEffectivenessByType(focusType type); int GetFocusRandomEffectivenessValue(int focus_base, int focus_base2, bool best_focus = 0); - + bool TryDoubleMeleeRoundEffect(); bool GetUseDoubleMeleeRoundDmgBonus() const { return use_double_melee_round_dmg_bonus; } inline void SetUseDoubleMeleeRoundDmgBonus(bool val) { use_double_melee_round_dmg_bonus = val; } - + void CastSpellOnLand(Mob* caster, int32 spell_id); void FocusProcLimitProcess(); bool ApplyFocusProcLimiter(int32 spell_id, int buffslot = -1); @@ -1094,8 +1094,6 @@ public: void InstillDoubt(Mob *who); int16 GetResist(uint8 type) const; - Mob* GetShieldTarget() const { return shield_target; } - void SetShieldTarget(Mob* mob) { shield_target = mob; } bool HasActiveSong() const { return(bardsong != 0); } bool Charmed() const { return typeofpet == petCharmed; } static uint32 GetLevelHP(uint8 tlevel); @@ -1133,13 +1131,28 @@ public: bool IsMoved() { return moved; } void SetMoved(bool moveflag) { moved = moveflag; } - Shielders_Struct shielder[MAX_SHIELDERS]; Trade* trade; + bool ShieldAbility(uint32 target_id, int shielder_max_distance = 15, int shield_duration = 12000, int shield_target_mitigation = 50, int shielder_mitigation = 75, bool use_aa = false, bool can_shield_npc = true); + void DoShieldDamageOnShielder(Mob *shield_target, int hit_damage_done, EQ::skills::SkillType skillInUse); + void ShieldAbilityFinish(); + void ShieldAbilityClearVariables(); + inline uint32 GetShielderID() const { return m_shielder_id; } + inline void SetShielderID(uint32 val) { m_shielder_id = val; } + inline uint32 GetShieldTargetID() const { return m_shield_target_id; } + inline void SetShieldTargetID(uint32 val) { m_shield_target_id = val; } + inline int GetShieldTargetMitigation() const { return m_shield_target_mitigation; } + inline void SetShieldTargetMitigation(int val) { m_shield_target_mitigation = val; } + inline int GetShielderMitigation() const { return m_shielder_mitigation; } + inline void SetShielderMitigation(int val) { m_shielder_mitigation = val; } + inline int GetMaxShielderDistance() const { return m_shielder_max_distance; } + inline void SetShielderMaxDistance(int val) { m_shielder_max_distance = val; } + WeaponStance_Struct weaponstance; bool IsWeaponStanceEnabled() const { return weaponstance.enabled; } inline void SetWeaponStanceEnabled(bool val) { weaponstance.enabled = val; } + inline glm::vec4 GetCurrentWayPoint() const { return m_CurrentWayPoint; } inline float GetCWPP() const { return(static_cast(cur_wp_pause)); } inline int GetCWP() const { return(cur_wp); } @@ -1439,6 +1452,13 @@ protected: Timer mana_timer; Timer focus_proc_limit_timer; + Timer shield_timer; + uint32 m_shield_target_id; + uint32 m_shielder_id; + int m_shield_target_mitigation; + int m_shielder_mitigation; + int m_shielder_max_distance; + //spell casting vars Timer spellend_timer; uint16 casting_spell_id; @@ -1485,8 +1505,6 @@ protected: uint8 aa_title; - Mob* shield_target; - int ExtraHaste; // for the #haste command bool mezzed; bool stunned; @@ -1626,7 +1644,7 @@ protected: std::unordered_map> aa_ranks; Timer aa_timers[aaTimerMax]; - + bool is_horse; AuraMgr aura_mgr; diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 09dd36df3..962d63f3d 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1128,6 +1128,10 @@ void Mob::AI_Process() { if (focus_proc_limit_timer.Check()) FocusProcLimitProcess(); + if (shield_timer.Check()) { + ShieldAbilityFinish(); + } + auto npcSpawnPoint = CastToNPC()->GetSpawnPoint(); if (GetSpecialAbility(TETHER)) { float tether_range = static_cast(GetSpecialAbilityParam(TETHER, 0)); diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index 71fcb02a4..24a9ec3b9 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -4179,44 +4179,6 @@ XS(XS_Mob_GetResist) { XSRETURN(1); } -XS(XS_Mob_GetShieldTarget); /* prototype to pass -Wmissing-prototypes */ -XS(XS_Mob_GetShieldTarget) { - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: Mob::GetShieldTarget(THIS)"); // @categories Script Utility - { - Mob *THIS; - Mob *RETVAL; - VALIDATE_THIS_IS_MOB; - RETVAL = THIS->GetShieldTarget(); - ST(0) = sv_newmortal(); - sv_setref_pv(ST(0), "Mob", (void *) RETVAL); - } - XSRETURN(1); -} - -XS(XS_Mob_SetShieldTarget); /* prototype to pass -Wmissing-prototypes */ -XS(XS_Mob_SetShieldTarget) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: Mob::SetShieldTarget(THIS, mob)"); // @categories Script Utility - { - Mob *THIS; - Mob *mob; - VALIDATE_THIS_IS_MOB; - if (sv_derived_from(ST(1), "Mob")) { - IV tmp = SvIV((SV *) SvRV(ST(1))); - mob = INT2PTR(Mob *, tmp); - } else - Perl_croak(aTHX_ "mob is not of type Mob"); - if (mob == nullptr) - Perl_croak(aTHX_ "mob is nullptr, avoiding crash."); - - THIS->SetShieldTarget(mob); - } - XSRETURN_EMPTY; -} - XS(XS_Mob_Charmed); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_Charmed) { dXSARGS; @@ -6301,6 +6263,51 @@ XS(XS_Mob_AddNimbusEffect) { XSRETURN_EMPTY; } +XS(XS_Mob_ShieldAbility); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_ShieldAbility) { + dXSARGS; + if (items < 2 || items > 6) + Perl_croak(aTHX_ "Usage: Mob::ShieldAbility(THIS, uint32 target_id, [int32 shielder__max_distance = 15], [int32 shield_duration = 12000], [int32 shield_target_mitigation= 50], [int32 shielder_mitigation = 50], [bool use_aa = false], bool [can_shield_npc = true]"); // @categories Spells and Disciplines + { + Mob *THIS; + uint32 target_id = (uint32)SvUV(ST(1)); + int32 shielder_max_distance = (int32)SvUV(ST(2)); + int32 shield_duration = (int32)SvUV(ST(3)); + int32 shield_target_mitigation = (int32)SvUV(ST(4)); + int32 shielder_mitigation = (int32)SvUV(ST(5)); + bool use_aa = (bool)SvTRUE(ST(6)); + bool can_shield_npc = (bool)SvTRUE(ST(7)); + + VALIDATE_THIS_IS_MOB; + if (items < 3) { + shielder_max_distance = 15; + } + + if (items < 4) { + shield_duration = 12000; + } + + if (items < 5) { + shield_target_mitigation = 50; + } + + if (items < 6) { + shielder_mitigation = 50; + } + + if (items < 7) { + use_aa = false; + } + + if (items < 8) { + can_shield_npc = true; + } + THIS->ShieldAbility(target_id, shielder_max_distance, shield_duration, shield_duration, shield_duration, use_aa, can_shield_npc); + + } + XSRETURN_EMPTY; +} + #ifdef BOTS XS(XS_Mob_CastToBot); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_CastToBot) @@ -6561,8 +6568,6 @@ XS(boot_Mob) { newXSproto(strcpy(buf, "DontRootMeBefore"), XS_Mob_DontRootMeBefore, file, "$"); newXSproto(strcpy(buf, "DontSnareMeBefore"), XS_Mob_DontSnareMeBefore, file, "$"); newXSproto(strcpy(buf, "GetResist"), XS_Mob_GetResist, file, "$$"); - newXSproto(strcpy(buf, "GetShieldTarget"), XS_Mob_GetShieldTarget, file, "$"); - newXSproto(strcpy(buf, "SetShieldTarget"), XS_Mob_SetShieldTarget, file, "$$"); newXSproto(strcpy(buf, "Charmed"), XS_Mob_Charmed, file, "$"); newXSproto(strcpy(buf, "GetLevelHP"), XS_Mob_GetLevelHP, file, "$$"); newXSproto(strcpy(buf, "GetZoneID"), XS_Mob_GetZoneID, file, "$"); @@ -6672,6 +6677,7 @@ XS(boot_Mob) { newXSproto(strcpy(buf, "CanRaceEquipItem"), XS_Mob_CanRaceEquipItem, file, "$$"); newXSproto(strcpy(buf, "RemoveAllNimbusEffects"), XS_Mob_RemoveAllNimbusEffects, file, "$"); newXSproto(strcpy(buf, "AddNimbusEffect"), XS_Mob_AddNimbusEffect, file, "$$"); + newXSproto(strcpy(buf, "ShieldAbility"), XS_Mob_ShieldAbility, file, "$$$$$$$$"); #ifdef BOTS newXSproto(strcpy(buf, "CastToBot"), XS_Mob_CastToBot, file, "$"); #endif diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 5160e32ce..5db67a2b8 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2950,6 +2950,19 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove break; } + case SE_PetShield: { + if (IsPet()) { + Mob* petowner = GetOwner(); + if (petowner) { + int shield_duration = spells[spell_id].base[i] * 12 * 1000; + int shield_target_mitigation = spells[spell_id].base2[i] ? spells[spell_id].base2[i] : 50; + int shielder_mitigation = spells[spell_id].max[i] ? spells[spell_id].base2[i] : 50; + ShieldAbility(petowner->GetID(), 25, shield_duration, shield_target_mitigation, shielder_mitigation); + break; + } + } + } + case SE_Weapon_Stance: { if (IsClient()) { CastToClient()->ApplyWeaponsStance(); @@ -3170,7 +3183,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_LimitManaMax: case SE_DoubleRangedAttack: case SE_ShieldEquipDmgMod: - case SE_GroupShielding: case SE_TriggerOnReqTarget: case SE_LimitRace: case SE_FcLimitUse: diff --git a/zone/string_ids.h b/zone/string_ids.h index 25c62d6b2..eca1f23cf 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -287,7 +287,9 @@ #define SUSPEND_MINION_SUSPEND 3268 //%1 tells you, 'By your command, master.' #define ONLY_SUMMONED_PETS 3269 //3269 This effect only works with summoned pets. #define SUSPEND_MINION_FIGHTING 3270 //Your pet must be at peace, first. +#define SHIELD_TARGET_NPC 3278 //You must first target a living Player Character. #define ALREADY_SHIELDED 3279 //Either you or your target is already being shielded. +#define ALREADY_SHIELDING 3280 //Either you or your target is already shielding another. #define START_SHIELDING 3281 //%1 begins to use %2 as a living shield! #define END_SHIELDING 3282 //%1 ceases protecting %2. #define TRADESKILL_MISSING_ITEM 3455 //You are missing a %1. From bde5d6931c962bdbd10bbd16b46f27d70d915fc5 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Mon, 16 Aug 2021 00:17:04 -0400 Subject: [PATCH 155/624] [Spells] Implemented SPA 390 SE_FcTimerLockout (#1496) * Implemented SPA 390 SE_FcTimerLockout Implemented SPA 390 SE_FcTimerLockout This focus limited effect sets any spell that meets the criteria of the of the focus limits to be a on recast timer. Base value: recast duration in milliseconds. Note: This focus can only be applied from spells (not item or AA) Note: Although reinforced by the server, to appear visually correct both server side and client side spell values need to match (ie. need to matching spells_new values). Example spell: Suppression of Fire ID 16973. Sets any fire spell in the clients spell bar to a 2 second recast when the client is affect by the spell. * Formatting * Use range based for Co-authored-by: Akkadius --- common/spdat.h | 4 ++-- zone/bonuses.cpp | 2 ++ zone/common.h | 3 ++- zone/spell_effects.cpp | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 8c5726c53..91bacaebf 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -752,8 +752,8 @@ typedef enum { #define SE_CastOnCurer 386 // implemented - Casts a spell on the person curing #define SE_CastOnCure 387 // implemented - Casts a spell on the cured person #define SE_SummonCorpseZone 388 // implemented - summons a corpse from any zone(nec AA) -#define SE_FcTimerRefresh 389 // implemented, @Fc, On Caster, reset all recast timers, base: 1 -//#define SE_FcTimerLockout 390 // *not implemented - Sets recast timers to specific value, focus limited. +#define SE_FcTimerRefresh 389 // implemented, @Fc, On Caster, reset all recast timers, base: 1, Note: Applied from casted spells only +#define SE_FcTimerLockout 390 // implemented, @Fc, On Caster, set a spell to be on recast timer, base: recast duration milliseconds, Note: Applied from casted spells only #define SE_LimitManaMax 391 // implemented, @Ff, Mininum mana of spell that can be focused, base1: mana amt #define SE_FcHealAmt 392 // implemented, @Fc, On Caster, spell healing mod flat amt, base: amt #define SE_FcHealPctIncoming 393 // implemented, @Fc, On Target, heal received critical chance mod, base: chance pct diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index cf1a0ccc0..4b3d8e340 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -4007,6 +4007,8 @@ uint8 Mob::IsFocusEffect(uint16 spell_id,int effect_index, bool AA,uint32 aa_eff return focusFcMute; case SE_FcTimerRefresh: return focusFcTimerRefresh; + case SE_FcTimerLockout: + return focusFcTimerLockout; case SE_Fc_Cast_Spell_On_Land: return focusFcCastSpellOnLand; case SE_FcStunTimeMod: diff --git a/zone/common.h b/zone/common.h index 4945a880c..e107d0abe 100644 --- a/zone/common.h +++ b/zone/common.h @@ -143,7 +143,8 @@ typedef enum { //focus types focusIncreaseNumHits, //@Fc, SPA: 421, SE_FcIncreaseNumHits, On Caster, numhits mod flat amt, base: amt focusFcLimitUse, //@Fc, SPA: 420, SE_FcLimitUse, On Caster, numhits mod pct, base: pct focusFcMute, //@Fc, SPA: 357, SE_FcMute, On Caster, prevents spell casting, base: chance pct - focusFcTimerRefresh, //@Fc, SPA: 389, SE_FcTimerRefresh, On Caster, reset all recast timers, base: 1 + focusFcTimerRefresh, //@Fc, SPA: 389, SE_FcTimerRefresh, On Caster, reset spell recast timer, base: 1 + focusFcTimerLockout, //@Fc, SPA: 390, SE_FcTimerLockout, On Caster, set a spell to be on recast timer, base: recast duration milliseconds focusFcStunTimeMod, //@Fc, SPA: 133, SE_FcStunTimeMod, On Caster, stun time mod pct, base: chance pct focusFcResistIncoming, //@Fc, SPA: 510, SE_Fc_Resist_Incoming, On Target, resist modifier, base: amt focusFcAmplifyMod, //@Fc, SPA: 507, SE_Fc_Amplify_Mod, On Caster, damage-heal-dot mod pct, base: pct diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 5db67a2b8..793980c98 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2559,6 +2559,35 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove break; } + case SE_FcTimerLockout: { + if (IsClient()) { + for (unsigned int mem_spell : CastToClient()->m_pp.mem_spells) { + if (IsValidSpell(mem_spell)) { + int32 new_recast_timer = CalcFocusEffect( + focusFcTimerLockout, + spell_id, + mem_spell + ); + if (new_recast_timer) { + bool apply_recast_timer = true; + if (IsCasting() && casting_spell_id == mem_spell) { + apply_recast_timer = false; + } + if (apply_recast_timer) { + new_recast_timer = new_recast_timer / 1000; + CastToClient()->GetPTimers().Start( + pTimerSpellStart + mem_spell, + static_cast(new_recast_timer) + ); + } + } + } + } + SetMana(GetMana()); + } + break; + } + case SE_HealGroupFromMana: { if(!caster) break; @@ -5753,6 +5782,12 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo } break; + case SE_FcTimerLockout: + if (type == focusFcTimerLockout) { + value = focus_spell.base[i]; + } + break; + case SE_Fc_Cast_Spell_On_Land: if (type == focusFcCastSpellOnLand) { if (zone->random.Roll(focus_spell.base[i])) { From 1c8231eb9ef2093613ab7791a991e9d0b089ea97 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 15 Aug 2021 21:39:38 -0700 Subject: [PATCH 156/624] [Netcode] Remove security from servertalk connections (#1464) * Remove security from servertalk connections * Remove the two hello steps before handshake that are now obsolete out * Revert "Remove the two hello steps before handshake that are now obsolete out" This reverts commit 32d61ea2381c1bddf8b08c5240899116d0fd3e80. * Keep old values for enums * Use downgrade security handshake for backwards compat * Send handshake instead of hello to fast connect * Add connect callback so it will actually work --- common/net/servertalk_client_connection.cpp | 178 ++------------------ common/net/servertalk_client_connection.h | 18 +- common/net/servertalk_common.h | 2 +- common/net/servertalk_server.cpp | 4 +- common/net/servertalk_server.h | 13 -- common/net/servertalk_server_connection.cpp | 160 +----------------- common/net/servertalk_server_connection.h | 21 +-- 7 files changed, 21 insertions(+), 375 deletions(-) diff --git a/common/net/servertalk_client_connection.cpp b/common/net/servertalk_client_connection.cpp index 88cfc021a..c6de2e8f8 100644 --- a/common/net/servertalk_client_connection.cpp +++ b/common/net/servertalk_client_connection.cpp @@ -22,31 +22,10 @@ EQ::Net::ServertalkClient::~ServertalkClient() void EQ::Net::ServertalkClient::Send(uint16_t opcode, EQ::Net::Packet &p) { EQ::Net::DynamicPacket out; -#ifdef ENABLE_SECURITY - if (m_encrypted) { - if (p.Length() == 0) { - p.PutUInt8(0, 0); - } - - out.PutUInt32(0, p.Length() + crypto_secretbox_MACBYTES); - out.PutUInt16(4, opcode); - - std::unique_ptr cipher(new unsigned char[p.Length() + crypto_secretbox_MACBYTES]); - - crypto_box_easy_afternm(&cipher[0], (unsigned char*)p.Data(), p.Length(), m_nonce_ours, m_shared_key); - (*(uint64_t*)&m_nonce_ours[0])++; - out.PutData(6, &cipher[0], p.Length() + crypto_secretbox_MACBYTES); - } - else { - out.PutUInt32(0, p.Length()); - out.PutUInt16(4, opcode); - out.PutPacket(6, p); - } -#else out.PutUInt32(0, p.Length()); out.PutUInt16(4, opcode); out.PutPacket(6, p); -#endif + InternalSend(ServertalkMessage, out); } @@ -87,14 +66,18 @@ void EQ::Net::ServertalkClient::Connect() m_connection = connection; m_connection->OnDisconnect([this](EQ::Net::TCPConnection *c) { LogF(Logs::General, Logs::TCPConnection, "Connection lost to {0}:{1}, attempting to reconnect...", m_addr, m_port); - m_encrypted = false; m_connection.reset(); }); m_connection->OnRead(std::bind(&EQ::Net::ServertalkClient::ProcessData, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); m_connection->Start(); - SendHello(); + SendHandshake(); + + if (m_on_connect_cb) { + m_on_connect_cb(this); + } + m_connecting = false; }); } @@ -188,67 +171,11 @@ void EQ::Net::ServertalkClient::ProcessReadBuffer() void EQ::Net::ServertalkClient::ProcessHello(EQ::Net::Packet &p) { -#ifdef ENABLE_SECURITY - memset(m_public_key_ours, 0, crypto_box_PUBLICKEYBYTES); - memset(m_public_key_theirs, 0, crypto_box_PUBLICKEYBYTES); - memset(m_private_key_ours, 0, crypto_box_SECRETKEYBYTES); - memset(m_nonce_ours, 0, crypto_box_NONCEBYTES); - memset(m_nonce_theirs, 0, crypto_box_NONCEBYTES); - memset(m_shared_key, 0, crypto_box_BEFORENMBYTES); - m_encrypted = false; - try { - bool enc = p.GetInt8(0) == 1 ? true : false; - - if (enc) { - if (p.Length() == (1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES)) { - memcpy(m_public_key_theirs, (char*)p.Data() + 1, crypto_box_PUBLICKEYBYTES); - memcpy(m_nonce_theirs, (char*)p.Data() + 1 + crypto_box_PUBLICKEYBYTES, crypto_box_NONCEBYTES); - m_encrypted = true; - - SendHandshake(); - - if (m_on_connect_cb) { - m_on_connect_cb(this); - } - } - else { - LogError("Could not process hello, size != {0}", 1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES); - } - } - else { - SendHandshake(); - - if (m_on_connect_cb) { - m_on_connect_cb(this); - } - } - } - catch (std::exception &ex) { - LogError("Error parsing hello from server: {0}", ex.what()); - m_connection->Disconnect(); + SendHandshake(); if (m_on_connect_cb) { - m_on_connect_cb(nullptr); - } - } -#else - try { - bool enc = p.GetInt8(0) == 1 ? true : false; - - if (enc) { - SendHandshake(true); - - if (m_on_connect_cb) { - m_on_connect_cb(this); - } - } - else { - SendHandshake(); - - if (m_on_connect_cb) { - m_on_connect_cb(this); - } + m_on_connect_cb(this); } } catch (std::exception &ex) { @@ -259,7 +186,6 @@ void EQ::Net::ServertalkClient::ProcessHello(EQ::Net::Packet &p) m_on_connect_cb(nullptr); } } -#endif } void EQ::Net::ServertalkClient::ProcessMessage(EQ::Net::Packet &p) @@ -269,45 +195,7 @@ void EQ::Net::ServertalkClient::ProcessMessage(EQ::Net::Packet &p) auto opcode = p.GetUInt16(4); if (length > 0) { auto data = p.GetString(6, length); -#ifdef ENABLE_SECURITY - if (m_encrypted) { - size_t message_len = length - crypto_secretbox_MACBYTES; - std::unique_ptr decrypted_text(new unsigned char[message_len]); - if (crypto_box_open_easy_afternm(&decrypted_text[0], (unsigned char*)&data[0], length, m_nonce_theirs, m_shared_key)) - { - LogError("Error decrypting message from server"); - (*(uint64_t*)&m_nonce_theirs[0])++; - return; - } - EQ::Net::StaticPacket decrypted_packet(&decrypted_text[0], message_len); - - (*(uint64_t*)&m_nonce_theirs[0])++; - - auto cb = m_message_callbacks.find(opcode); - if (cb != m_message_callbacks.end()) { - cb->second(opcode, decrypted_packet); - } - - if (m_message_callback) { - m_message_callback(opcode, decrypted_packet); - } - } - else { - size_t message_len = length; - EQ::Net::StaticPacket packet(&data[0], message_len); - - auto cb = m_message_callbacks.find(opcode); - if (cb != m_message_callbacks.end()) { - cb->second(opcode, packet); - } - - if (m_message_callback) { - m_message_callback(opcode, packet); - } - } - -#else size_t message_len = length; EQ::Net::StaticPacket packet(&data[0], message_len); @@ -319,7 +207,6 @@ void EQ::Net::ServertalkClient::ProcessMessage(EQ::Net::Packet &p) if (m_message_callback) { m_message_callback(opcode, packet); } -#endif } } catch (std::exception &ex) { @@ -327,54 +214,11 @@ void EQ::Net::ServertalkClient::ProcessMessage(EQ::Net::Packet &p) } } -void EQ::Net::ServertalkClient::SendHandshake(bool downgrade) +void EQ::Net::ServertalkClient::SendHandshake() { EQ::Net::DynamicPacket handshake; -#ifdef ENABLE_SECURITY - if (m_encrypted) { - crypto_box_keypair(m_public_key_ours, m_private_key_ours); - randombytes_buf(m_nonce_ours, crypto_box_NONCEBYTES); - - crypto_box_beforenm(m_shared_key, m_public_key_theirs, m_private_key_ours); - - handshake.PutData(0, m_public_key_ours, crypto_box_PUBLICKEYBYTES); - handshake.PutData(crypto_box_PUBLICKEYBYTES, m_nonce_ours, crypto_box_NONCEBYTES); - - memset(m_public_key_ours, 0, crypto_box_PUBLICKEYBYTES); - memset(m_public_key_theirs, 0, crypto_box_PUBLICKEYBYTES); - memset(m_private_key_ours, 0, crypto_box_SECRETKEYBYTES); - - size_t cipher_length = m_identifier.length() + 1 + m_credentials.length() + 1 + crypto_secretbox_MACBYTES; - size_t data_length = m_identifier.length() + 1 + m_credentials.length() + 1; - - std::unique_ptr signed_buffer(new unsigned char[cipher_length]); - std::unique_ptr data_buffer(new unsigned char[data_length]); - - memset(&data_buffer[0], 0, data_length); - memcpy(&data_buffer[0], m_identifier.c_str(), m_identifier.length()); - memcpy(&data_buffer[1 + m_identifier.length()], m_credentials.c_str(), m_credentials.length()); - - crypto_box_easy_afternm(&signed_buffer[0], &data_buffer[0], data_length, m_nonce_ours, m_shared_key); - - (*(uint64_t*)&m_nonce_ours[0])++; - - handshake.PutData(crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES, &signed_buffer[0], cipher_length); - } - else { - handshake.PutString(0, m_identifier); - handshake.PutString(m_identifier.length() + 1, m_credentials); - handshake.PutUInt8(m_identifier.length() + 1 + m_credentials.length(), 0); - } -#else handshake.PutString(0, m_identifier); handshake.PutString(m_identifier.length() + 1, m_credentials); handshake.PutUInt8(m_identifier.length() + 1 + m_credentials.length(), 0); -#endif - - if (downgrade) { - InternalSend(ServertalkClientDowngradeSecurityHandshake, handshake); - } - else { - InternalSend(ServertalkClientHandshake, handshake); - } + InternalSend(ServertalkClientDowngradeSecurityHandshake, handshake); } diff --git a/common/net/servertalk_client_connection.h b/common/net/servertalk_client_connection.h index ad5c24d65..704b48ded 100644 --- a/common/net/servertalk_client_connection.h +++ b/common/net/servertalk_client_connection.h @@ -4,9 +4,6 @@ #include "../event/timer.h" #include "servertalk_common.h" #include "packet.h" -#ifdef ENABLE_SECURITY -#include -#endif namespace EQ { @@ -34,8 +31,7 @@ namespace EQ void ProcessReadBuffer(); void ProcessHello(EQ::Net::Packet &p); void ProcessMessage(EQ::Net::Packet &p); - void SendHandshake() { SendHandshake(false); } - void SendHandshake(bool downgrade); + void SendHandshake(); std::unique_ptr m_timer; @@ -45,23 +41,11 @@ namespace EQ bool m_connecting; int m_port; bool m_ipv6; - bool m_encrypted; std::shared_ptr m_connection; std::vector m_buffer; std::unordered_map> m_message_callbacks; std::function m_message_callback; std::function m_on_connect_cb; - -#ifdef ENABLE_SECURITY - unsigned char m_public_key_ours[crypto_box_PUBLICKEYBYTES]; - unsigned char m_private_key_ours[crypto_box_SECRETKEYBYTES]; - unsigned char m_nonce_ours[crypto_box_NONCEBYTES]; - - unsigned char m_public_key_theirs[crypto_box_PUBLICKEYBYTES]; - unsigned char m_nonce_theirs[crypto_box_NONCEBYTES]; - - unsigned char m_shared_key[crypto_box_BEFORENMBYTES]; -#endif }; } } diff --git a/common/net/servertalk_common.h b/common/net/servertalk_common.h index f30d21b00..aa779b5ed 100644 --- a/common/net/servertalk_common.h +++ b/common/net/servertalk_common.h @@ -15,4 +15,4 @@ namespace EQ ServertalkMessage, }; } -} \ No newline at end of file +} diff --git a/common/net/servertalk_server.cpp b/common/net/servertalk_server.cpp index 297d4cd31..d362362d3 100644 --- a/common/net/servertalk_server.cpp +++ b/common/net/servertalk_server.cpp @@ -10,12 +10,10 @@ EQ::Net::ServertalkServer::~ServertalkServer() void EQ::Net::ServertalkServer::Listen(const ServertalkServerOptions& opts) { - m_encrypted = opts.encrypted; m_credentials = opts.credentials; - m_allow_downgrade = opts.allow_downgrade; m_server = std::make_unique(); m_server->Listen(opts.port, opts.ipv6, [this](std::shared_ptr connection) { - m_unident_connections.push_back(std::make_shared(connection, this, m_encrypted, m_allow_downgrade)); + m_unident_connections.push_back(std::make_shared(connection, this)); }); } diff --git a/common/net/servertalk_server.h b/common/net/servertalk_server.h index e6062d847..f58243e98 100644 --- a/common/net/servertalk_server.h +++ b/common/net/servertalk_server.h @@ -5,10 +5,6 @@ #include #include -#ifdef ENABLE_SECURITY -#include -#endif - namespace EQ { namespace Net @@ -17,18 +13,9 @@ namespace EQ { int port; bool ipv6; - bool encrypted; - bool allow_downgrade; std::string credentials; ServertalkServerOptions() { -#ifdef ENABLE_SECURITY - encrypted = true; - allow_downgrade = true; -#else - encrypted = false; - allow_downgrade = true; -#endif ipv6 = false; } }; diff --git a/common/net/servertalk_server_connection.cpp b/common/net/servertalk_server_connection.cpp index 6bfae6ea1..2258d1372 100644 --- a/common/net/servertalk_server_connection.cpp +++ b/common/net/servertalk_server_connection.cpp @@ -3,12 +3,10 @@ #include "../eqemu_logsys.h" #include "../util/uuid.h" -EQ::Net::ServertalkServerConnection::ServertalkServerConnection(std::shared_ptr c, EQ::Net::ServertalkServer *parent, bool encrypted, bool allow_downgrade) +EQ::Net::ServertalkServerConnection::ServertalkServerConnection(std::shared_ptr c, EQ::Net::ServertalkServer *parent) { m_connection = c; m_parent = parent; - m_encrypted = encrypted; - m_allow_downgrade = allow_downgrade; m_uuid = EQ::Util::UUID::Generate().ToString(); m_connection->OnRead(std::bind(&ServertalkServerConnection::OnRead, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); m_connection->OnDisconnect(std::bind(&ServertalkServerConnection::OnDisconnect, this, std::placeholders::_1)); @@ -22,30 +20,10 @@ EQ::Net::ServertalkServerConnection::~ServertalkServerConnection() void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet & p) { EQ::Net::DynamicPacket out; -#ifdef ENABLE_SECURITY - if (m_encrypted) { - if (p.Length() == 0) { - p.PutUInt8(0, 0); - } - - out.PutUInt32(0, p.Length() + crypto_secretbox_MACBYTES); - out.PutUInt16(4, opcode); - - std::unique_ptr cipher(new unsigned char[p.Length() + crypto_secretbox_MACBYTES]); - crypto_box_easy_afternm(&cipher[0], (unsigned char*)p.Data(), p.Length(), m_nonce_ours, m_shared_key); - (*(uint64_t*)&m_nonce_ours[0])++; - out.PutData(6, &cipher[0], p.Length() + crypto_secretbox_MACBYTES); - } - else { - out.PutUInt32(0, p.Length()); - out.PutUInt16(4, opcode); - out.PutPacket(6, p); - } -#else out.PutUInt32(0, p.Length()); out.PutUInt16(4, opcode); out.PutPacket(6, p); -#endif + InternalSend(ServertalkMessage, out); } @@ -109,6 +87,7 @@ void EQ::Net::ServertalkServerConnection::ProcessReadBuffer() } break; case ServertalkClientHandshake: + case ServertalkClientDowngradeSecurityHandshake: ProcessHandshake(p); break; case ServertalkMessage: @@ -125,10 +104,8 @@ void EQ::Net::ServertalkServerConnection::ProcessReadBuffer() } break; case ServertalkClientHandshake: - ProcessHandshake(p); - break; case ServertalkClientDowngradeSecurityHandshake: - ProcessHandshake(p, true); + ProcessHandshake(p); break; case ServertalkMessage: ProcessMessage(p); @@ -155,29 +132,7 @@ void EQ::Net::ServertalkServerConnection::OnDisconnect(TCPConnection *c) void EQ::Net::ServertalkServerConnection::SendHello() { EQ::Net::DynamicPacket hello; - -#ifdef ENABLE_SECURITY - memset(m_public_key_ours, 0, crypto_box_PUBLICKEYBYTES); - memset(m_public_key_theirs, 0, crypto_box_PUBLICKEYBYTES); - memset(m_private_key_ours, 0, crypto_box_SECRETKEYBYTES); - memset(m_nonce_ours, 0, crypto_box_NONCEBYTES); - memset(m_nonce_theirs, 0, crypto_box_NONCEBYTES); - - if (m_encrypted) { - hello.PutInt8(0, 1); - - crypto_box_keypair(m_public_key_ours, m_private_key_ours); - randombytes_buf(m_nonce_ours, crypto_box_NONCEBYTES); - - hello.PutData(1, m_public_key_ours, crypto_box_PUBLICKEYBYTES); - hello.PutData(1 + crypto_box_PUBLICKEYBYTES, m_nonce_ours, crypto_box_NONCEBYTES); - } - else { - hello.PutInt8(0, 0); - } -#else hello.PutInt8(0, 0); -#endif InternalSend(ServertalkServerHello, hello); } @@ -197,71 +152,8 @@ void EQ::Net::ServertalkServerConnection::InternalSend(ServertalkPacketType type m_connection->Write((const char*)out.Data(), out.Length()); } -void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p, bool downgrade_security) +void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p) { -#ifdef ENABLE_SECURITY - if (downgrade_security && m_allow_downgrade && m_encrypted) { - LogF(Logs::General, Logs::TCPConnection, "Downgraded encrypted connection to plaintext because otherside didn't support encryption {0}:{1}", - m_connection->RemoteIP(), m_connection->RemotePort()); - m_encrypted = false; - } - - if (m_encrypted) { - try { - if (p.Length() > (crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES)) { - memcpy(m_public_key_theirs, (char*)p.Data(), crypto_box_PUBLICKEYBYTES); - memcpy(m_nonce_theirs, (char*)p.Data() + crypto_box_PUBLICKEYBYTES, crypto_box_NONCEBYTES); - - crypto_box_beforenm(m_shared_key, m_public_key_theirs, m_private_key_ours); - - size_t cipher_len = p.Length() - crypto_box_PUBLICKEYBYTES - crypto_box_NONCEBYTES; - size_t message_len = cipher_len - crypto_secretbox_MACBYTES; - std::unique_ptr decrypted_text(new unsigned char[message_len]); - - if (crypto_box_open_easy_afternm(&decrypted_text[0], (unsigned char*)p.Data() + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES, cipher_len, m_nonce_theirs, m_shared_key)) - { - LogError("Error decrypting handshake from client, dropping connection."); - m_connection->Disconnect(); - return; - } - - m_identifier = (const char*)&decrypted_text[0]; - std::string credentials = (const char*)&decrypted_text[0] + (m_identifier.length() + 1); - - if (!m_parent->CheckCredentials(credentials)) { - LogError("Got incoming connection with invalid credentials during handshake, dropping connection."); - m_connection->Disconnect(); - return; - } - - m_parent->ConnectionIdentified(this); - (*(uint64_t*)&m_nonce_theirs[0])++; - } - } - catch (std::exception &ex) { - LogError("Error parsing handshake from client: {0}", ex.what()); - m_connection->Disconnect(); - } - } - else { - try { - m_identifier = p.GetCString(0); - auto credentials = p.GetCString(m_identifier.length() + 1); - - if (!m_parent->CheckCredentials(credentials)) { - LogError("Got incoming connection with invalid credentials during handshake, dropping connection."); - m_connection->Disconnect(); - return; - } - - m_parent->ConnectionIdentified(this); - } - catch (std::exception &ex) { - LogError("Error parsing handshake from client: {0}", ex.what()); - m_connection->Disconnect(); - } - } -#else try { m_identifier = p.GetCString(0); auto credentials = p.GetCString(m_identifier.length() + 1); @@ -278,7 +170,6 @@ void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p, b LogError("Error parsing handshake from client: {0}", ex.what()); m_connection->Disconnect(); } -#endif } void EQ::Net::ServertalkServerConnection::ProcessMessage(EQ::Net::Packet &p) @@ -288,46 +179,6 @@ void EQ::Net::ServertalkServerConnection::ProcessMessage(EQ::Net::Packet &p) auto opcode = p.GetUInt16(4); if (length > 0) { auto data = p.GetString(6, length); -#ifdef ENABLE_SECURITY - if (m_encrypted) { - size_t message_len = length - crypto_secretbox_MACBYTES; - std::unique_ptr decrypted_text(new unsigned char[message_len]); - - if (crypto_box_open_easy_afternm(&decrypted_text[0], (unsigned char*)&data[0], length, m_nonce_theirs, m_shared_key)) - { - LogError("Error decrypting message from client"); - (*(uint64_t*)&m_nonce_theirs[0])++; - return; - } - - EQ::Net::StaticPacket decrypted_packet(&decrypted_text[0], message_len); - - (*(uint64_t*)&m_nonce_theirs[0])++; - - auto cb = m_message_callbacks.find(opcode); - if (cb != m_message_callbacks.end()) { - cb->second(opcode, decrypted_packet); - } - - if (m_message_callback) { - m_message_callback(opcode, decrypted_packet); - } - } - else { - size_t message_len = length; - EQ::Net::StaticPacket packet(&data[0], message_len); - - auto cb = m_message_callbacks.find(opcode); - if (cb != m_message_callbacks.end()) { - cb->second(opcode, packet); - } - - if (m_message_callback) { - m_message_callback(opcode, packet); - } - } - -#else size_t message_len = length; EQ::Net::StaticPacket packet(&data[0], message_len); @@ -339,7 +190,6 @@ void EQ::Net::ServertalkServerConnection::ProcessMessage(EQ::Net::Packet &p) if (m_message_callback) { m_message_callback(opcode, packet); } -#endif } } catch (std::exception &ex) { diff --git a/common/net/servertalk_server_connection.h b/common/net/servertalk_server_connection.h index 112a8f2a6..f8110f51a 100644 --- a/common/net/servertalk_server_connection.h +++ b/common/net/servertalk_server_connection.h @@ -4,9 +4,6 @@ #include "servertalk_common.h" #include "packet.h" #include -#ifdef ENABLE_SECURITY -#include -#endif namespace EQ { @@ -16,7 +13,7 @@ namespace EQ class ServertalkServerConnection { public: - ServertalkServerConnection(std::shared_ptr c, ServertalkServer *parent, bool encrypted, bool allow_downgrade); + ServertalkServerConnection(std::shared_ptr c, ServertalkServer *parent); ~ServertalkServerConnection(); void Send(uint16_t opcode, EQ::Net::Packet &p); @@ -33,8 +30,7 @@ namespace EQ void OnDisconnect(TCPConnection* c); void SendHello(); void InternalSend(ServertalkPacketType type, EQ::Net::Packet &p); - void ProcessHandshake(EQ::Net::Packet &p) { ProcessHandshake(p, false); } - void ProcessHandshake(EQ::Net::Packet &p, bool security_downgrade); + void ProcessHandshake(EQ::Net::Packet &p); void ProcessMessage(EQ::Net::Packet &p); std::shared_ptr m_connection; @@ -45,19 +41,6 @@ namespace EQ std::function m_message_callback; std::string m_identifier; std::string m_uuid; - - bool m_encrypted; - bool m_allow_downgrade; -#ifdef ENABLE_SECURITY - unsigned char m_public_key_ours[crypto_box_PUBLICKEYBYTES]; - unsigned char m_private_key_ours[crypto_box_SECRETKEYBYTES]; - unsigned char m_nonce_ours[crypto_box_NONCEBYTES]; - - unsigned char m_public_key_theirs[crypto_box_PUBLICKEYBYTES]; - unsigned char m_nonce_theirs[crypto_box_NONCEBYTES]; - - unsigned char m_shared_key[crypto_box_BEFORENMBYTES]; -#endif }; } } From f4bd7c53c0baac2a4e4c27bc14e15414499ab4a6 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Tue, 31 Aug 2021 00:33:31 -0500 Subject: [PATCH 157/624] [Logging] Implement World GMSay Logging (#1505) * Implement world GM say logging * Add missed callback function * Update min status --- common/ruletypes.h | 1 + world/main.cpp | 50 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 82c4f9ecc..a3ec71a47 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -727,6 +727,7 @@ RULE_CATEGORY_END() RULE_CATEGORY(Logging) RULE_BOOL(Logging, PrintFileFunctionAndLine, false, "Ex: [World Server] [net.cpp::main:309] Loading variables...") +RULE_BOOL(Logging, WorldGMSayLogging, true, "Relay worldserver logging to zone processes via GM say output") RULE_CATEGORY_END() RULE_CATEGORY(HotReload) diff --git a/world/main.cpp b/world/main.cpp index 2aaafca16..d36561544 100644 --- a/world/main.cpp +++ b/world/main.cpp @@ -229,6 +229,44 @@ void RegisterLoginservers() } } +static void GMSayHookCallBackProcessWorld(uint16 log_category, std::string message) +{ + // Cut messages down to 4000 max to prevent client crash + if (!message.empty()) { + message = message.substr(0, 4000); + } + + // Replace Occurrences of % or MessageStatus will crash + find_replace(message, std::string("%"), std::string(".")); + + if (message.find('\n') != std::string::npos) { + auto message_split = SplitString(message, '\n'); + + for (size_t iter = 0; iter < message_split.size(); ++iter) { + zoneserver_list.SendEmoteMessage( + nullptr, + 0, + 80, + LogSys.GetGMSayColorFromCategory(log_category), + " %s%s", + (iter == 0 ? " ---" : ""), + message_split[iter].c_str() + ); + } + + return; + } + + zoneserver_list.SendEmoteMessage( + nullptr, + 0, + 80, + LogSys.GetGMSayColorFromCategory(log_category), + "%s", + message.c_str() + ); +} + /** * World process entrypoint * @@ -297,9 +335,15 @@ int main(int argc, char** argv) { guild_mgr.SetDatabase(&database); - LogSys.SetDatabase(&database) - ->LoadLogDatabaseSettings() - ->StartFileLogs(); + // logging system init + auto logging = LogSys.SetDatabase(&database) + ->LoadLogDatabaseSettings(); + + if (RuleB(Logging, WorldGMSayLogging)) { + logging->SetGMSayHandler(&GMSayHookCallBackProcessWorld); + } + + logging->StartFileLogs(); /** * Parse simple CLI passes From 228e0007cab1f418fb6fd4414f63dd783a2ccd94 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Tue, 31 Aug 2021 00:33:49 -0500 Subject: [PATCH 158/624] Pad zero size packets which is what we did on encrypted connections prior to #1464 (#1504) --- common/net/servertalk_client_connection.cpp | 5 +++++ common/net/servertalk_server_connection.cpp | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/common/net/servertalk_client_connection.cpp b/common/net/servertalk_client_connection.cpp index c6de2e8f8..0c789330d 100644 --- a/common/net/servertalk_client_connection.cpp +++ b/common/net/servertalk_client_connection.cpp @@ -21,6 +21,11 @@ EQ::Net::ServertalkClient::~ServertalkClient() void EQ::Net::ServertalkClient::Send(uint16_t opcode, EQ::Net::Packet &p) { + // pad zero size packets + if (p.Length() == 0) { + p.PutUInt8(0, 0); + } + EQ::Net::DynamicPacket out; out.PutUInt32(0, p.Length()); out.PutUInt16(4, opcode); diff --git a/common/net/servertalk_server_connection.cpp b/common/net/servertalk_server_connection.cpp index 2258d1372..0942c9d50 100644 --- a/common/net/servertalk_server_connection.cpp +++ b/common/net/servertalk_server_connection.cpp @@ -19,6 +19,11 @@ EQ::Net::ServertalkServerConnection::~ServertalkServerConnection() void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet & p) { + // pad zero size packets + if (p.Length() == 0) { + p.PutUInt8(0, 0); + } + EQ::Net::DynamicPacket out; out.PutUInt32(0, p.Length()); out.PutUInt16(4, opcode); From 06890f695aaeb210ae4bd6eec8bad6c7d527c577 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Tue, 31 Aug 2021 00:34:10 -0500 Subject: [PATCH 159/624] [Repositories] Add datetime support to repositories (#1503) --- .../template/base_repository.template | 15 ++++- .../generators/repository-generator.pl | 55 +++++++++++++------ 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/common/repositories/template/base_repository.template b/common/repositories/template/base_repository.template index 8b1695049..d4ab7b6fc 100644 --- a/common/repositories/template/base_repository.template +++ b/common/repositories/template/base_repository.template @@ -14,6 +14,7 @@ #include "../../database.h" #include "../../string_util.h" +#include class Base{{TABLE_NAME_CLASS}}Repository { public: @@ -33,11 +34,23 @@ public: }; } + static std::vector SelectColumns() + { + return { +{{SELECT_COLUMNS_LIST_QUOTED}} + }; + } + static std::string ColumnsRaw() { return std::string(implode(", ", Columns())); } + static std::string SelectColumnsRaw() + { + return std::string(implode(", ", SelectColumns())); + } + static std::string TableName() { return std::string("{{TABLE_NAME_VAR}}"); @@ -47,7 +60,7 @@ public: { return fmt::format( "SELECT {} FROM {}", - ColumnsRaw(), + SelectColumnsRaw(), TableName() ); } diff --git a/utils/scripts/generators/repository-generator.pl b/utils/scripts/generators/repository-generator.pl index 12012ea5a..b387a7d63 100644 --- a/utils/scripts/generators/repository-generator.pl +++ b/utils/scripts/generators/repository-generator.pl @@ -203,17 +203,18 @@ foreach my $table_to_generate (@tables) { } # 2nd pass - my $default_entries = ""; - my $insert_one_entries = ""; - my $insert_many_entries = ""; - my $find_one_entries = ""; - my $column_names_quoted = ""; - my $table_struct_columns = ""; - my $update_one_entries = ""; - my $all_entries = ""; - my $index = 0; - my %table_data = (); - my %table_primary_key = (); + my $default_entries = ""; + my $insert_one_entries = ""; + my $insert_many_entries = ""; + my $find_one_entries = ""; + my $column_names_quoted = ""; + my $select_column_names_quoted = ""; + my $table_struct_columns = ""; + my $update_one_entries = ""; + my $all_entries = ""; + my $index = 0; + my %table_data = (); + my %table_primary_key = (); $ex->execute($database_name, $table_to_generate); while (my @row = $ex->fetchrow_array()) { @@ -233,11 +234,9 @@ foreach my $table_to_generate (@tables) { } } - print $column_default . "\n"; - my $default_value = 0; if ($column_default eq "current_timestamp()") { - $default_value = '""'; + $default_value = "std::time(nullptr)" } elsif ($column_default ne "NULL" && $column_default ne "") { $column_default =~ s/'/"/g; @@ -246,7 +245,7 @@ foreach my $table_to_generate (@tables) { elsif ($column_default eq "''") { $default_value = '""'; } - elsif ((trim($column_default) eq "" || $column_default eq "NULL") && $column_type =~ /text|varchar|datetime/i) { + elsif ((trim($column_default) eq "" || $column_default eq "NULL") && $column_type =~ /text|varchar/i) { $default_value = '""'; } @@ -260,6 +259,12 @@ foreach my $table_to_generate (@tables) { # column names (string) $column_names_quoted .= sprintf("\t\t\t\"%s\",\n", format_column_name_for_mysql($column_name)); + if ($data_type =~ /datetime/) { + $select_column_names_quoted .= sprintf("\t\t\t\"UNIX_TIMESTAMP(%s)\",\n", format_column_name_for_mysql($column_name)); + } + else { + $select_column_names_quoted .= sprintf("\t\t\t\"%s\",\n", format_column_name_for_mysql($column_name)); + } # update one if ($extra ne "auto_increment") { @@ -267,6 +272,9 @@ foreach my $table_to_generate (@tables) { if ($data_type =~ /int|float|double|decimal/) { $query_value = sprintf('" + std::to_string(%s_entry.%s));', $table_name, $column_name_formatted); } + elsif ($data_type =~ /datetime/) { + $query_value = sprintf('FROM_UNIXTIME(" + (%s_entry.%s > 0 ? std::to_string(%s_entry.%s) : "null") + ")");', $table_name, $column_name_formatted, $table_name, $column_name_formatted); + } $update_one_entries .= sprintf( "\t\t" . 'update_values.push_back(columns[%s] + " = %s' . "\n", @@ -280,14 +288,21 @@ foreach my $table_to_generate (@tables) { if ($data_type =~ /int|float|double|decimal/) { $value = sprintf('std::to_string(%s_entry.%s)', $table_name, $column_name_formatted); } + elsif ($data_type =~ /datetime/) { + $value = sprintf('"FROM_UNIXTIME(" + (%s_entry.%s > 0 ? std::to_string(%s_entry.%s) : "null") + ")"', $table_name, $column_name_formatted, $table_name, $column_name_formatted); + } $insert_one_entries .= sprintf("\t\tinsert_values.push_back(%s);\n", $value); $insert_many_entries .= sprintf("\t\t\tinsert_values.push_back(%s);\n", $value); # find one / all (select) if ($data_type =~ /bigint/) { - $all_entries .= sprintf("\t\t\tentry.%-${longest_column_length}s = strtoll(row[%s], NULL, 10);\n", $column_name_formatted, $index); - $find_one_entries .= sprintf("\t\t\tentry.%-${longest_column_length}s = strtoll(row[%s], NULL, 10);\n", $column_name_formatted, $index); + $all_entries .= sprintf("\t\t\tentry.%-${longest_column_length}s = strtoll(row[%s], nullptr, 10);\n", $column_name_formatted, $index); + $find_one_entries .= sprintf("\t\t\tentry.%-${longest_column_length}s = strtoll(row[%s], nullptr, 10);\n", $column_name_formatted, $index); + } + elsif ($data_type =~ /datetime/) { + $all_entries .= sprintf("\t\t\tentry.%-${longest_column_length}s = strtoll(row[%s] ? row[%s] : \"-1\", nullptr, 10);\n", $column_name_formatted, $index, $index); + $find_one_entries .= sprintf("\t\t\tentry.%-${longest_column_length}s = strtoll(row[%s] ? row[%s] : \"-1\", nullptr, 10);\n", $column_name_formatted, $index, $index); } elsif ($data_type =~ /int/) { $all_entries .= sprintf("\t\t\tentry.%-${longest_column_length}s = atoi(row[%s]);\n", $column_name_formatted, $index); @@ -366,6 +381,7 @@ foreach my $table_to_generate (@tables) { } chomp($column_names_quoted); + chomp($select_column_names_quoted); chomp($table_struct_columns); chomp($default_entries); chomp($update_one_entries); @@ -391,6 +407,7 @@ foreach my $table_to_generate (@tables) { $new_base_repository =~ s/\{\{DATABASE_CONNECTION}}/$database_connection/g; $new_base_repository =~ s/\{\{DEFAULT_ENTRIES}}/$default_entries/g; $new_base_repository =~ s/\{\{COLUMNS_LIST_QUOTED}}/$column_names_quoted/g; + $new_base_repository =~ s/\{\{SELECT_COLUMNS_LIST_QUOTED}}/$select_column_names_quoted/g; $new_base_repository =~ s/\{\{TABLE_STRUCT_COLUMNS}}/$table_struct_columns/g; $new_base_repository =~ s/\{\{FIND_ONE_ENTRIES}}/$find_one_entries/g; $new_base_repository =~ s/\{\{UPDATE_ONE_ENTRIES}}/$update_one_entries/g; @@ -409,6 +426,7 @@ foreach my $table_to_generate (@tables) { $new_repository =~ s/\{\{DATABASE_CONNECTION}}/$database_connection/g; $new_repository =~ s/\{\{DEFAULT_ENTRIES}}/$default_entries/g; $new_repository =~ s/\{\{COLUMNS_LIST_QUOTED}}/$column_names_quoted/g; + $new_repository =~ s/\{\{SELECT_COLUMNS_LIST_QUOTED}}/$select_column_names_quoted/g; $new_repository =~ s/\{\{TABLE_STRUCT_COLUMNS}}/$table_struct_columns/g; $new_repository =~ s/\{\{FIND_ONE_ENTRIES}}/$find_one_entries/g; $new_repository =~ s/\{\{UPDATE_ONE_ENTRIES}}/$update_one_entries/g; @@ -487,6 +505,9 @@ sub translate_mysql_data_type_to_c { elsif ($mysql_data_type =~ /float|double|decimal/) { $struct_data_type = 'float'; } + elsif ($mysql_data_type =~ /datetime/) { + $struct_data_type = 'time_t'; + } return $struct_data_type; } From 3b01608a716412fd635afd9f40bb100bc6cbc697 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Tue, 31 Aug 2021 00:34:28 -0500 Subject: [PATCH 160/624] [Server] Tweak inter process keepalive timers; this is a very tiny overhead for inter-process chatter to check for connection liveness (#1502) --- loginserver/world_server.cpp | 2 +- world/login_server.cpp | 2 +- world/zonelist.cpp | 2 +- zone/worldserver.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/loginserver/world_server.cpp b/loginserver/world_server.cpp index 43846af32..90b4ea160 100644 --- a/loginserver/world_server.cpp +++ b/loginserver/world_server.cpp @@ -72,7 +72,7 @@ WorldServer::WorldServer(std::shared_ptr wo std::bind(&WorldServer::ProcessLSAccountUpdate, this, std::placeholders::_1, std::placeholders::_2) ); - m_keepalive = std::make_unique(5000, true, std::bind(&WorldServer::OnKeepAlive, this, std::placeholders::_1)); + m_keepalive = std::make_unique(1000, true, std::bind(&WorldServer::OnKeepAlive, this, std::placeholders::_1)); } WorldServer::~WorldServer() = default; diff --git a/world/login_server.cpp b/world/login_server.cpp index da649495e..8d93a6128 100644 --- a/world/login_server.cpp +++ b/world/login_server.cpp @@ -552,7 +552,7 @@ bool LoginServer::Connect() ); } - m_keepalive = std::make_unique(5000, true, std::bind(&LoginServer::OnKeepAlive, this, std::placeholders::_1)); + m_keepalive = std::make_unique(1000, true, std::bind(&LoginServer::OnKeepAlive, this, std::placeholders::_1)); return true; } diff --git a/world/zonelist.cpp b/world/zonelist.cpp index 51acd0c01..666ed2208 100644 --- a/world/zonelist.cpp +++ b/world/zonelist.cpp @@ -43,7 +43,7 @@ ZSList::ZSList() memset(pLockedZones, 0, sizeof(pLockedZones)); m_tick = std::make_unique(5000, true, std::bind(&ZSList::OnTick, this, std::placeholders::_1)); - m_keepalive = std::make_unique(2500, true, std::bind(&ZSList::OnKeepAlive, this, std::placeholders::_1)); + m_keepalive = std::make_unique(1500, true, std::bind(&ZSList::OnKeepAlive, this, std::placeholders::_1)); } ZSList::~ZSList() { diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 5a5595f6f..ce1cf892c 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -86,7 +86,7 @@ void WorldServer::Connect() m_connection->OnMessage(std::bind(&WorldServer::HandleMessage, this, std::placeholders::_1, std::placeholders::_2)); - m_keepalive = std::make_unique(2500, true, std::bind(&WorldServer::OnKeepAlive, this, std::placeholders::_1)); + m_keepalive = std::make_unique(1000, true, std::bind(&WorldServer::OnKeepAlive, this, std::placeholders::_1)); } bool WorldServer::SendPacket(ServerPacket *pack) From 5f3c0540449d316229fee851cb39f9423fd24b55 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 31 Aug 2021 01:35:18 -0400 Subject: [PATCH 161/624] [Spells] Updated pet suspend code to use spell effect data and bonuses (#1501) --- common/spdat.h | 4 ++-- zone/bonuses.cpp | 8 ++++++++ zone/client.cpp | 24 +++++++++++++----------- zone/client.h | 2 +- zone/client_packet.cpp | 3 ++- zone/common.h | 2 +- zone/spell_effects.cpp | 3 +-- 7 files changed, 28 insertions(+), 18 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 91bacaebf..b39ee4904 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -514,7 +514,7 @@ typedef enum { #define SE_StackingCommand_Block 148 // implemented? #define SE_StackingCommand_Overwrite 149 // implemented? #define SE_DeathSave 150 // implemented -#define SE_SuspendPet 151 // *not implemented as bonus +#define SE_SuspendPet 151 // implemented, @Pet, allow caster to have an extra suspended pet, base: 0=no buffs/items 1=buffs+items, limit: none, max: none #define SE_TemporaryPets 152 // implemented #define SE_BalanceHP 153 // implemented #define SE_DispelDetrimental 154 // implemented @@ -671,7 +671,7 @@ typedef enum { #define SE_MitigateDamageShield 305 // implemented - off hand attacks only (Shielding Resistance) //#define SE_ArmyOfTheDead 306 // *not implemented NecroAA - This ability calls up to five shades of nearby corpses back to life to serve the necromancer. The soulless abominations will mindlessly fight the target until called back to the afterlife some time later. The first rank summons up to three shades that serve for 60 seconds, and each additional rank adds one more possible shade and increases their duration by 15 seconds //#define SE_Appraisal 307 // *not implemented Rogue AA - This ability allows you to estimate the selling price of an item you are holding on your cursor. -#define SE_SuspendMinion 308 // implemented +#define SE_ZoneSuspendMinion 308 // implemented, @Pet, allow suspended pets to be resummoned upon zoning, base: 1, limit: none, max: none, Calc: Bool #define SE_GateCastersBindpoint 309 // implemented - Gate to casters bind point #define SE_ReduceReuseTimer 310 // implemented, @Fc, On Caster, disc reuse time mod, base: milliseconds #define SE_LimitCombatSkills 311 // implemented, @Ff, Include or exclude combat skills or procs (non-memorizable spells) from being focused, base1: 0=Exclude if proc 1=Allow only if proc diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 4b3d8e340..68e35a9bf 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1658,6 +1658,10 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) newbon->ItemEnduranceRegenCap += base1; break; + case SE_ZoneSuspendMinion: + newbon->ZoneSuspendMinion = base1; + break; + // to do case SE_PetDiscipline: break; @@ -3648,6 +3652,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne break; } + case SE_ZoneSuspendMinion: + new_bonus->ZoneSuspendMinion = effect_value; + break; + //Special custom cases for loading effects on to NPC from 'npc_spels_effects' table if (IsAISpellEffect) { diff --git a/zone/client.cpp b/zone/client.cpp index 040601ad0..a90d424fa 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -5596,18 +5596,20 @@ void Client::AddLDoNWin(uint32 theme_id) } -void Client::SuspendMinion() +void Client::SuspendMinion(int value) { + /* + SPA 151 Allows an extra pet to be saved and resummoned later. + Casting with a pet but without a suspended pet will suspend the pet + Casting without a pet and with a suspended pet will unsuspend the pet + effect value 0 = save pet with no buffs or equipment + effect value 1 = save pet with buffs and equipment + effect value 2 = unknown + Note: SPA 308 allows for suspended pets to be resummoned after zoning. + */ + NPC *CurrentPet = GetPet()->CastToNPC(); - int AALevel = GetAA(aaSuspendedMinion); - - if(AALevel == 0) - return; - - if(GetLevel() < 62) - return; - if(!CurrentPet) { if(m_suspendedminion.SpellID > 0) @@ -5629,7 +5631,7 @@ void Client::SuspendMinion() return; } - if(AALevel >= 2) + if(value >= 1) { CurrentPet->SetPetState(m_suspendedminion.Buffs, m_suspendedminion.Items); @@ -5698,7 +5700,7 @@ void Client::SuspendMinion() m_suspendedminion.petpower = CurrentPet->GetPetPower(); m_suspendedminion.size = CurrentPet->GetSize(); - if(AALevel >= 2) + if(value >= 1) CurrentPet->GetPetState(m_suspendedminion.Buffs, m_suspendedminion.Items, m_suspendedminion.Name); else strn0cpy(m_suspendedminion.Name, CurrentPet->GetName(), 64); // Name stays even at rank 1 diff --git a/zone/client.h b/zone/client.h index ff80cebc7..004e905da 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1383,7 +1383,7 @@ public: uint32 GetCorpseCount() { return database.GetCharacterCorpseCount(CharacterID()); } uint32 GetCorpseID(int corpse) { return database.GetCharacterCorpseID(CharacterID(), corpse); } uint32 GetCorpseItemAt(int corpse_id, int slot_id) { return database.GetCharacterCorpseItemAt(corpse_id, slot_id); } - void SuspendMinion(); + void SuspendMinion(int value); void Doppelganger(uint16 spell_id, Mob *target, const char *name_override, int pet_count, int pet_duration); void NotifyNewTitlesAvailable(); void Signal(uint32 data); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 50d3cd375..64d45bb06 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1659,8 +1659,9 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) m_petinfo.SpellID = 0; } /* Moved here so it's after where we load the pet data. */ - if (!GetAA(aaPersistentMinion)) + if (!aabonuses.ZoneSuspendMinion && !spellbonuses.ZoneSuspendMinion && !itembonuses.ZoneSuspendMinion) { memset(&m_suspendedminion, 0, sizeof(PetInfo)); + } /* Server Zone Entry Packet */ outapp = new EQApplicationPacket(OP_ZoneEntry, sizeof(ServerZoneEntry_Struct)); diff --git a/zone/common.h b/zone/common.h index e107d0abe..0561ebb36 100644 --- a/zone/common.h +++ b/zone/common.h @@ -553,7 +553,7 @@ struct StatBonuses { int32 Pet_Add_Atk; // base = Pet ATK bonus from owner int32 ItemEnduranceRegenCap; // modify endurance regen cap int32 WeaponStance[WEAPON_STANCE_TYPE_MAX +1];// base = trigger spell id, base2 = 0 is 2h, 1 is shield, 2 is dual wield, [0]spid 2h, [1]spid shield, [2]spid DW - + bool ZoneSuspendMinion; // base 1 allows suspended minions to zone // AAs int32 ShieldDuration; // extends duration of /shield ability diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 793980c98..0284fd4c6 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2534,11 +2534,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove break; } - case SE_SuspendMinion: case SE_SuspendPet: { if(IsClient()) - CastToClient()->SuspendMinion(); + CastToClient()->SuspendMinion(spell.base[i]); break; } From cb3f8daedd6ddeaabf35329b4b0320801e10fe3b Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 31 Aug 2021 01:41:20 -0400 Subject: [PATCH 162/624] [Spells] Major update to cast restriction code and new spell field 'caster_requirement_id' (field220) implemented (#1508) * Implemented spells_new table 'field220' as 'caster_requirement_id' Implemented spells_new table 'field220' as 'caster_requirement_id' * Update spell_effects.cpp * updates to CastRestriction enum using live description updated entire function missing and new types added many fixes * updates * code fixes * updates * updates * Update spdat.h * typo fix --- common/bodytypes.h | 18 +- common/shareddb.cpp | 1 + common/spdat.h | 313 ++++++++++- zone/mob.cpp | 1 + zone/mob.h | 3 +- zone/spell_effects.cpp | 1179 ++++++++++++++++++++++++++++++---------- zone/spells.cpp | 19 +- 7 files changed, 1242 insertions(+), 292 deletions(-) diff --git a/common/bodytypes.h b/common/bodytypes.h index fd64b1825..0528e14f6 100644 --- a/common/bodytypes.h +++ b/common/bodytypes.h @@ -27,16 +27,17 @@ typedef enum { BT_Extraplanar = 6, BT_Magical = 7, //this name might be a bit off, BT_SummonedUndead = 8, - BT_RaidGiant = 9, - // ... + BT_RaidGiant = 9, //Velious era Raid Giant + BT_RaidColdain = 10, //Velious era Raid Coldain BT_NoTarget = 11, //no name, can't target this bodytype BT_Vampire = 12, BT_Atenha_Ra = 13, BT_Greater_Akheva = 14, BT_Khati_Sha = 15, - BT_Seru = 16, //not confirmed.... + BT_Seru = 16, + BT_Grieg_Veneficus = 17, BT_Draz_Nurakk = 18, - BT_Zek = 19, + BT_Zek = 19, //"creatures from the Plane of War." BT_Luggald = 20, BT_Animal = 21, BT_Insect = 22, @@ -46,17 +47,18 @@ typedef enum { BT_Dragon = 26, BT_Summoned2 = 27, BT_Summoned3 = 28, - //29 + BT_Dragon2 = 29, //database data indicates this is a dragon type (kunark and DoN?) BT_VeliousDragon = 30, //might not be a tight set - // ... + BT_Familiar = 31, BT_Dragon3 = 32, BT_Boxes = 33, BT_Muramite = 34, //tribal dudes // ... BT_NoTarget2 = 60, // ... - BT_SwarmPet = 63, //is this valid, or made up? - // ... + BT_SwarmPet = 63, //Looks like weapon proc related temp pets and few misc pets, should not be used for checking swarm pets in general. + BT_MonsterSummon = 64, + // 65, trap or effect related? BT_InvisMan = 66, //no name, seen on 'InvisMan', can be /targeted BT_Special = 67 } bodyType; diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 258450200..042f0cf6a 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1883,6 +1883,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { sp[tempid].override_crit_chance = atoi(row[217]); sp[tempid].aemaxtargets = atoi(row[218]); sp[tempid].no_heal_damage_item_mod = atoi(row[219]); + sp[tempid].caster_requirement_id = atoi(row[220]); sp[tempid].spell_class = atoi(row[221]); sp[tempid].spell_subclass = atoi(row[222]); sp[tempid].persistdeath = atoi(row[224]) != 0; diff --git a/common/spdat.h b/common/spdat.h index b39ee4904..a63df94cc 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -155,6 +155,17 @@ #define SPELL_RESURRECTION_SICKNESS 756 #define SPELL_RESURRECTION_SICKNESS2 5249 #define SPELL_REVIVAL_SICKNESS 13087 +#define SPELL_PACT_OF_HATE_RECOURSE 40375 +#define SPELL_INCENDIARY_OOZE_BUFF 32513 + +//spellgroup ids +#define SPELLGROUP_FRENZIED_BURNOUT 2754 +#define SPELLGROUP_ILLUSION_OF_GRANDEUR 38603 +#define SPELLGROUP_ROGUES_FURY 16861 +#define SPELLGROUP_HARMONIOUS_PRECISION 15634 +#define SPELLGROUP_HARMONIOUS_EXPANSE 15633 +#define SPELLGROUP_FURIOUS_RAMPAGE 38106 +#define SPELLGROUP_SHROUD_OF_PRAYER 41050 @@ -189,6 +200,306 @@ enum FocusLimitIncludes { IncludeExistsSELimitSpellSubclass = 14, IncludeFoundSELimitSpellSubclass = 15 }; +/* + The id's correspond to 'type' 39 in live(2021) dbstr_us gives the message for target and caster restricted effects. These are not present in the ROF2 dbstr_us. + If from CasterRestriction spell field. "Your target does not meet the spell requirements. ." Msg in combat window, color red. + If set as limit in a direct damage or heal spell (SPA 0) do not give message. +*/ +enum SpellRestriction +{ + UNKNOWN_3 = 3, // | caster restriction | seen in spell 30183 Mind Spiral + IS_NOT_ON_HORSE = 5, // | caster restriction | + IS_ANIMAL_OR_HUMANOID = 100, // This spell will only work on animals or humanoid creatures. + IS_DRAGON = 101, // This spell will only work on dragons. + IS_ANIMAL_OR_INSECT = 102, // This spell will only work on animals or insects. + IS_BODY_TYPE_MISC = 103, // This spell will only work on humanoids, lycanthropes, giants, Kael Drakkel giants, Coldain, animals, insects, constructs, dragons, Skyshrine dragons, Muramites, or creatures constructed from magic. + IS_BODY_TYPE_MISC2 = 104, // This spell will only work on humanoids, lycanthropes, giants, Kael Drakkel giants, Coldain, animals, or insects. + IS_PLANT = 105, // This spell will only work on plants. + IS_GIANT = 106, // This spell will only work on animals. | Live used to have this on spells restricted to Giants, but those spells were removed... We still have them + IS_NOT_ANIMAL_OR_HUMANOID = 108, // This spell will only work on targets that are neither animals or humanoid. + IS_BIXIE = 109, // This spell will only work on bixies. + IS_HARPY = 110, // This spell will only work on harpies. + IS_GNOLL = 111, // This spell will only work on gnolls. + IS_SPORALI = 112, // This spell will only work on fungusoids. + IS_KOBOLD = 113, // This spell will only work on kobolds. + IS_FROSTCRYPT_SHADE = 114, // This spell will only work on undead creatures or the Shades of Frostcrypt. + IS_DRAKKIN = 115, // This spell will only work on Drakkin. + IS_UNDEAD_OR_VALDEHOLM_GIANT = 116, // This spell will only work on undead creatures or the inhabitants of Valdeholm. + IS_ANIMAL_OR_PLANT = 117, // This spell will only work on plants or animals. + IS_SUMMONED = 118, // This spell will only work on constructs, elementals, or summoned elemental minions. + IS_WIZARD_USED_ON_MAGE_FIRE_PET = 119, // This spell will only work on wizards. | Live uses this on high level mage fire pets, which are wizard class + IS_UNDEAD = 120, // + IS_NOT_UNDEAD_OR_SUMMONED_OR_VAMPIRE = 121, // This spell will only work on creatures that are not undead, constructs, elementals, or vampires. + IS_FAE_OR_PIXIE = 122, // This spell will only work on Fae or pixies. + IS_HUMANOID = 123, // + IS_UNDEAD_AND_HP_LESS_THAN_10_PCT = 124, // The Essence Extractor whirrs but does not light up. + IS_CLOCKWORK_AND_HP_LESS_THAN_45_PCT = 125, // This spell will only work on clockwork gnomes. + IS_WISP_AND_HP_LESS_THAN_10_PCT = 126, // This spell will only work on wisps at or below 10% of their maximum HP. + IS_CLASS_MELEE_THAT_CAN_BASH_OR_KICK_EXCEPT_BARD = 127, // This spell will only work on non-bard targets that can bash or kick. + IS_CLASS_PURE_MELEE = 128, // This spell will only affect melee classes (warriors, monks, rogues, and berserkers). + IS_CLASS_PURE_CASTER = 129, // This spell will only affect pure caster classes (necromancers, wizards, magicians, and enchanters). + IS_CLASS_HYBRID_CLASS = 130, // This spell will only affect hybrid classes (paladins, rangers, shadow knights, bards, and beastlords). + IS_CLASS_WARRIOR = 131, // This spell will only affect Warriors. + IS_CLASS_CLERIC = 132, // This spell will only affect Clerics. + IS_CLASS_PALADIN = 133, // This spell will only affect Paladins. + IS_CLASS_RANGER = 134, // This spell will only affect Rangers. + IS_CLASS_SHADOWKNIGHT = 135, // This spell will only affect Shadow Knights. + IS_CLASS_DRUID = 136, // This spell will only affect Druids. + IS_CLASS_MONK = 137, // This spell will only affect Monks. + IS_CLASS_BARD = 138, // This spell will only affect Bards. + IS_CLASS_ROGUE = 139, // This spell will only affect Rogues. + IS_CLASS_SHAMAN = 140, // This spell will only affect Shamans. + IS_CLASS_NECRO = 141, // This spell will only affect Necromancers. + IS_CLASS_WIZARD = 142, // This spell will only affect Wizards. + IS_CLASS_MAGE = 143, // This spell will only affect Magicians. + IS_CLASS_ENCHANTER = 144, // This spell will only affect Enchanters. + IS_CLASS_BEASTLORD = 145, // This spell will only affect Beastlords. + IS_CLASS_BERSERKER = 146, // This spell will only affect Berserkers. + IS_CLASS_CLR_SHM_DRU = 147, // This spell will only affect priest classes (clerics, druids, and shaman). + IS_CLASS_NOT_WAR_PAL_SK = 148, // This spell will not affect Warriors, Paladins, or Shadow Knights. + IS_LEVEL_UNDER_100 = 150, // This spell will not affect any target over level 100. + IS_NOT_RAID_BOSS = 190, // This spell will not affect raid bosses. + IS_RAID_BOSS = 191, // This spell will only affect raid bosses. + FRENZIED_BURNOUT_ACTIVE = 192, // This spell will only cast if you have Frenzied Burnout active. + FRENZIED_BURNOUT_NOT_ACTIVE = 193, // This spell will only cast if you do not have Frenzied Burnout active. + UNKNOWN_199 = 199, // + IS_HP_ABOVE_75_PCT = 201, // + IS_HP_LESS_THAN_20_PCT = 203, // Your target's HP must be at 20% of its maximum or below. | caster restriction | + IS_HP_LESS_THAN_50_PCT = 204, // Your target's HP must be at 50% of its maximum or below. | caster restriction | + IS_HP_LESS_THAN_75_PCT = 205, // Your target's HP must be at 75% of its maximum or below. + IS_NOT_IN_COMBAT = 216, // This spell will only affect creatures that are not in combat. + HAS_AT_LEAST_1_PET_ON_HATELIST = 221, // + HAS_AT_LEAST_2_PETS_ON_HATELIST = 222, // + HAS_AT_LEAST_3_PETS_ON_HATELIST = 223, // + HAS_AT_LEAST_4_PETS_ON_HATELIST = 224, // + HAS_AT_LEAST_5_PETS_ON_HATELIST = 225, // + HAS_AT_LEAST_6_PETS_ON_HATELIST = 226, // + HAS_AT_LEAST_7_PETS_ON_HATELIST = 227, // + HAS_AT_LEAST_8_PETS_ON_HATELIST = 228, // + HAS_AT_LEAST_9_PETS_ON_HATELIST = 229, // + HAS_AT_LEAST_10_PETS_ON_HATELIST = 230, // + HAS_AT_LEAST_11_PETS_ON_HATELIST = 231, // + HAS_AT_LEAST_12_PETS_ON_HATELIST = 232, // + HAS_AT_LEAST_13_PETS_ON_HATELIST = 233, // + HAS_AT_LEAST_14_PETS_ON_HATELIST = 234, // + HAS_AT_LEAST_15_PETS_ON_HATELIST = 235, // + HAS_AT_LEAST_16_PETS_ON_HATELIST = 236, // + HAS_AT_LEAST_17_PETS_ON_HATELIST = 237, // + HAS_AT_LEAST_18_PETS_ON_HATELIST = 238, // + HAS_AT_LEAST_19_PETS_ON_HATELIST = 239, // + HAS_AT_LEAST_20_PETS_ON_HATELIST = 240, // + IS_HP_LESS_THAN_35_PCT = 250, // Your target's HP must be at 35% of its maximum or below. + HAS_BETWEEN_1_TO_2_PETS_ON_HATELIST = 260, // between 1 and 2 pets + HAS_BETWEEN_3_TO_5_PETS_ON_HATELIST = 261, // between 3 and 5 pets + HAS_BETWEEN_6_TO_9_PETS_ON_HATELIST = 262, // between 6 and 9 pets + HAS_BETWEEN_10_TO_14_PETS_ON_HATELIST = 263, // between 10 and 14 pets + HAS_MORE_THAN_14_PETS_ON_HATELIST = 264, // 15 or more pets + IS_CLASS_CHAIN_OR_PLATE = 304, // This spell will only affect plate or chain wearing classes. + IS_HP_BETWEEN_5_AND_9_PCT = 350, // Your target's HP must be between 5% and 9% of its maximum. + IS_HP_BETWEEN_10_AND_14_PCT = 351, // Your target's HP must be between 10% and 14% of its maximum. + IS_HP_BETWEEN_15_AND_19_PCT = 352, // Your target's HP must be between 15% and 19% of its maximum. + IS_HP_BETWEEN_20_AND_24_PCT = 353, // Your target's HP must be between 20% and 24% of its maximum. + IS_HP_BETWEEN_25_AND_29_PCT = 354, // Your target's HP must be between 25% and 29% of its maximum. + IS_HP_BETWEEN_30_AND_34_PCT = 355, // Your target's HP must be between 30% and 34% of its maximum. + IS_HP_BETWEEN_35_AND_39_PCT = 356, // Your target's HP must be between 35% and 39% of its maximum. + IS_HP_BETWEEN_40_AND_44_PCT = 357, // Your target's HP must be between 40% and 44% of its maximum. + IS_HP_BETWEEN_45_AND_49_PCT = 358, // Your target's HP must be between 45% and 49% of its maximum. + IS_HP_BETWEEN_50_AND_54_PCT = 359, // Your target's HP must be between 50% and 54% of its maximum. + IS_HP_BETWEEN_55_AND_59_PCT = 360, // Your target's HP must be between 55% and 59% of its maximum. + IS_HP_BETWEEN_5_AND_15_PCT = 398, // Your target's HP must be between 5% and 15% of its maximum. + IS_HP_BETWEEN_15_AND_25_PCT = 399, // Your target's HP must be between 15% and 25% of its maximum. + IS_HP_BETWEEN_1_AND_25_PCT = 400, // Your target's HP must be at 25% of its maximum or below. + IS_HP_BETWEEN_25_AND_35_PCT = 401, // Your target's HP must be between 25% and 35% of its maximum. + IS_HP_BETWEEN_35_AND_45_PCT = 402, // Your target's HP must be between 35% and 45% of its maximum. + IS_HP_BETWEEN_45_AND_55_PCT = 403, // Your target's HP must be between 45% and 55% of its maximum. + IS_HP_BETWEEN_55_AND_65_PCT = 404, // Your target's HP must be between 55% and 65% of its maximum. + IS_HP_BETWEEN_65_AND_75_PCT = 405, // Your target's HP must be between 65% and 75% of its maximum. + IS_HP_BETWEEN_75_AND_85_PCT = 406, // Your target's HP must be between 75% and 85% of its maximum. + IS_HP_BETWEEN_85_AND_95_PCT = 407, // Your target's HP must be between 85% and 95% of its maximum. + IS_HP_ABOVE_45_PCT = 408, // Your target's HP must be at least 45% of its maximum. + IS_HP_ABOVE_55_PCT = 409, // Your target's HP must be at least 55% of its maximum. + UNKNOWN_TOO_MUCH_HP_410 = 410, // Your target has too much HP to be affected by this spell. + UNKNOWN_TOO_MUCH_HP_411 = 411, // Your target has too much HP to be affected by this spell. + IS_HP_ABOVE_99_PCT = 412, // + IS_MANA_ABOVE_10_PCT = 429, // You must have at least 10% of your maximum mana available to cast this spell. | caster restriction | + IS_HP_BELOW_5_PCT = 501, // + IS_HP_BELOW_10_PCT = 502, // + IS_HP_BELOW_15_PCT = 503, // + IS_HP_BELOW_20_PCT = 504, // Your target's HP must be at 20% of its maximum or below. + IS_HP_BELOW_25_PCT = 505, // + IS_HP_BELOW_30_PCT = 506, // + IS_HP_BELOW_35_PCT = 507, // + IS_HP_BELOW_40_PCT = 508, // + IS_HP_BELOW_45_PCT = 509, // Your target's HP must be at 45% of its maximum or below. + IS_HP_BELOW_50_PCT = 510, // + IS_HP_BELOW_55_PCT = 511, // + IS_HP_BELOW_60_PCT = 512, // + IS_HP_BELOW_65_PCT = 513, // + IS_HP_BELOW_70_PCT = 514, // + IS_HP_BELOW_75_PCT = 515, // + IS_HP_BELOW_80_PCT = 516, // + IS_HP_BELOW_85_PCT = 517, // + IS_HP_BELOW_90_PCT = 518, // This ability requires you to be at or below 90% of your maximum HP. | caster restriction | + IS_HP_BELOW_95_PCT = 519, // + IS_MANA_BELOW_UNKNOWN_PCT = 521, // + IS_ENDURANCE_BELOW_40_PCT = 522, // + IS_MANA_BELOW_40_PCT = 523, // + IS_HP_ABOVE_20_PCT = 524, // Your target's HP must be at least 21% of its maximum. + IS_BODY_TYPE_UNDEFINED = 600, // This spell will only work on creatures with an undefined body type. + IS_BODY_TYPE_HUMANOID = 601, // This spell will only work on humanoid creatures. + IS_BODY_TYPE_WEREWOLF = 602, // This spell will only work on lycanthrope creatures. + IS_BODY_TYPE_UNDEAD = 603, // This spell will only work on undead creatures. + IS_BODY_TYPE_GIANTS = 604, // This spell will only work on giants. + IS_BODY_TYPE_CONSTRUCTS = 605, // This spell will only work on constructs. + IS_BODY_TYPE_EXTRAPLANAR = 606, // This spell will only work on extraplanar creatures. + IS_BODY_TYPE_MAGICAL_CREATURE = 607, // This spell will only work on creatures constructed from magic. + IS_BODY_TYPE_UNDEADPET = 608, // This spell will only work on animated undead servants. + IS_BODY_TYPE_KAELGIANT = 609, // This spell will only work on the Giants of Kael Drakkal. + IS_BODY_TYPE_COLDAIN = 610, // This spell will only work on Coldain Dwarves. + IS_BODY_TYPE_VAMPIRE = 612, // This spell will only work on vampires. + IS_BODY_TYPE_ATEN_HA_RA = 613, // This spell will only work on Aten Ha Ra. + IS_BODY_TYPE_GREATER_AHKEVANS = 614, // This spell will only work on Greater Ahkevans. + IS_BODY_TYPE_KHATI_SHA = 615, // This spell will only work on Khati Sha. + IS_BODY_TYPE_LORD_INQUISITOR_SERU = 616, // This spell will only work on Lord Inquisitor Seru. + IS_BODY_TYPE_GRIEG_VENEFICUS = 617, // This spell will only work on Grieg Veneficus. + IS_BODY_TYPE_FROM_PLANE_OF_WAR = 619, // This spell will only work on creatures from the Plane of War. + IS_BODY_TYPE_LUGGALD = 620, // This spell will only work on Luggalds. + IS_BODY_TYPE_ANIMAL = 621, // This spell will only work on animals. + IS_BODY_TYPE_INSECT = 622, // This spell will only work on insects. + IS_BODY_TYPE_MONSTER = 623, // This spell will only work on monsters. + IS_BODY_TYPE_ELEMENTAL = 624, // This spell will only work on elemental creatures. + IS_BODY_TYPE_PLANT = 625, // This spell will only work on plants. + IS_BODY_TYPE_DRAGON2 = 626, // This spell will only work on dragons. + IS_BODY_TYPE_SUMMONED_ELEMENTAL = 627, // This spell will only work on summoned elementals. + IS_BODY_TYPE_WARDER = 628, // + IS_BODY_TYPE_DRAGON_OF_TOV = 630, // This spell will only work on Dragons of Veeshan's Temple. + IS_BODY_TYPE_FAMILIAR = 631, // This spell will only work on familiars. + IS_BODY_TYPE_MURAMITE = 634, // This spell will only work on Muramites. + IS_NOT_UNDEAD_OR_SUMMONED = 635, // + IS_NOT_PLANT = 636, // This spell will not affect plants. + IS_NOT_CLIENT = 700, // This spell will not work on adventurers. + IS_CLIENT = 701, // This spell will only work on adventurers. + IS_LEVEL_ABOVE_42_AND_IS_CLIENT = 800, // This spell will only work on level 43 or higher adventurers. + UNKNOWN_812 = 812, // | seen in spell 22616 Thaumatize Pet Mana Regen Base | + UNKNOWN_814 = 814, // | seen in spell 22704 Vegetentacles I | + IS_TREANT = 815, // This spell will only work on treants. + IS_BIXIE2 = 816, // This spell will only work on bixies. + IS_SCARECROW = 817, // This spell will only work on scarecrows. + IS_VAMPIRE_OR_UNDEAD_OR_UNDEADPET = 818, // This spell will only work on vampires, undead, or animated undead creatures. + IS_NOT_VAMPIRE_OR_UNDEAD = 819, // This spell will not work on vampires or undead creatures. + IS_CLASS_KNIGHT_HYBRID_MELEE = 820, // This spell will only work on knights, hybrids, or melee classes. + IS_CLASS_WARRIOR_CASTER_PRIEST = 821, // This spell will only work on warriors, casters, or priests. + UNKNOWN_822 = 822, // | seen in spell 22870 Morell's Distraction 822 | + IS_END_BELOW_21_PCT = 825, // This ability requires you to be at or below 21% of your maximum endurance. + IS_END_BELOW_25_PCT = 826, // This ability requires you to be at or below 25% of your maximum endurance. + IS_END_BELOW_29_PCT = 827, // This ability requires you to be at or below 29% of your maximum endurance. + IS_REGULAR_SERVER = 836, // + IS_PROGRESSION_SERVER = 837, // + IS_GOD_EXPANSION_UNLOCKED = 839, // + UNKNOWN_840 = 840, // | caster restriction | seen in spell 6883 Expedient Recovery + UNKNOWN_841 = 841, // | caster restriction | seen in spell 32192 Merciless Blow + IS_HUMANOID_LEVEL_84_MAX = 842, // + IS_HUMANOID_LEVEL_86_MAX = 843, // + IS_HUMANOID_LEVEL_88_MAX = 844, // + HAS_CRYSTALLIZED_FLAME_BUFF = 845, // This spell will only work on targets afflicted by Crystallized Flame. | On live spell does not appear to be a buff + HAS_INCENDIARY_OOZE_BUFF = 847, // This spell will only work on targets afflicted by Incendiary Ooze. + IS_LEVEL_90_MAX = 860, // + IS_LEVEL_92_MAX = 861, // + IS_LEVEL_94_MAX = 862, // + IS_LEVEL_95_MAX = 863, // + IS_LEVEL_97_MAX = 864, // + IS_LEVEL_99_MAX = 865, // + HAS_WEAPONSTANCE_DEFENSIVE_PROFICIENCY = 866, // | caster restriction | + HAS_WEAPONSTANCE_TWO_HAND_PROFICIENCY = 867, // | caster restriction | + HAS_WEAPONSTANCE_DUAL_WEILD_PROFICIENCY = 868, // | caster restriction | + IS_LEVEL_100_MAX = 869, // + IS_LEVEL_102_MAX = 870, // + IS_LEVEL_104_MAX = 871, // + IS_LEVEL_105_MAX = 872, // + IS_LEVEL_107_MAX = 873, // + IS_LEVEL_109_MAX = 874, // + IS_LEVEL_110_MAX = 875, // + IS_LEVEL_112_MAX = 876, // + IS_LEVEL_114_MAX = 877, // + HAS_TBL_ESIANTI_ACCESS = 997, // This spell will only transport adventurers who have gained access to Esianti: Palace of the Winds. | not implemented + HAS_ITEM_CLOCKWORK_SCRAPS = 999, // + IS_BETWEEN_LEVEL_1_AND_75 = 1000, // + IS_BETWEEN_LEVEL_76_AND_85 = 1001, // + IS_BETWEEN_LEVEL_86_AND_95 = 1002, // + IS_BETWEEN_LEVEL_96_AND_105 = 1003, // + IS_HP_LESS_THAN_80_PCT = 1004, // + IS_LEVEL_ABOVE_34 = 1474, // Your target must be level 35 or higher. + IN_TWO_HANDED_STANCE = 2000, // You must be in your two-handed stance to use this ability. + IN_DUAL_WIELD_HANDED_STANCE = 2001, // You must be in your dual-wielding stance to use this ability. + IN_SHIELD_STANCE = 2002, // You must be in your shield stance to use this ability. + NOT_IN_TWO_HANDED_STANCE = 2010, // You may not use this ability if you are in your two-handed stance. + NOT_IN_DUAL_WIELD_HANDED_STANCE = 2011, // You may not use this ability if you are in your dual-wielding stance. + NOT_IN_SHIELD_STANCE = 2012, // You may not use this ability if you are in your shield stance. + LEVEL_46_MAX = 2761, // + DISABLED_UNTIL_EXPANSION_ROK = 7000, // This ability is disabled until Ruins of Kunark. + DISABLED_UNTIL_EXPANSION_SOV = 7001, // This ability is disabled until Scars of Velious. + DISABLED_UNTIL_EXPANSION_SOL = 7002, // This ability is disabled until Shadows of Luclin. + DISABLED_UNTIL_EXPANSION_POP = 7003, // This ability is disabled until Planes of Power. + DISABLED_UNTIL_EXPANSION_LOY = 7004, // This ability is disabled until Legacy of Ykesha. + DISABLED_UNTIL_EXPANSION_LDON = 7005, // This ability is disabled until Lost Dungeons of Norrath. + DISABLED_UNTIL_EXPANSION_GOD = 7006, // This ability is disabled until Gates of Discord. + DISABLED_UNTIL_EXPANSION_OOW = 7007, // This ability is disabled until Omens of War. + DISABLED_UNTIL_EXPANSION_DON = 7008, // This ability is disabled until Dragons of Norrath. + DISABLED_UNTIL_EXPANSION_DOD = 7009, // This ability is disabled until Depths of Darkhollow. + DISABLED_UNTIL_EXPANSION_POR = 7010, // This ability is disabled until Prophecy of Ro. + DISABLED_UNTIL_EXPANSION_TSS = 7011, // This ability is disabled until Serpent's Spine. + DISABLED_UNTIL_EXPANSION_TBS = 7012, // This ability is disabled until Buried Sea. + DISABLED_UNTIL_EXPANSION_SOF = 7013, // This ability is disabled until Secrets of Faydwer. + DISABLED_UNTIL_EXPANSION_SOD = 7014, // This ability is disabled until Seeds of Destruction. + DISABLED_UNTIL_EXPANSION_UF = 7015, // This ability is disabled until Underfoot. + DISABLED_UNTIL_EXPANSION_HOT = 7016, // This ability is disabled until House of Thule. + DISABLED_UNTIL_EXPANSION_VOA = 7017, // This ability is disabled until Veil of Alaris. + DISABLED_UNTIL_EXPANSION_ROF = 7018, // This ability is disabled until Rain of Fear. + DISABLED_UNTIL_EXPANSION_COF = 7019, // This ability is disabled until Call of the Forsaken. + DISABLED_UNTIL_EXPANSION_TDS = 7020, // This ability is disabled until Darkened Sea. + DISABLED_UNTIL_EXPANSION_TBM = 7021, // This ability is disabled until Broken Mirror. + DISABLED_UNTIL_EXPANSION_EOK = 7022, // This ability is disabled until Empires of Kunark. + DISABLED_UNTIL_EXPANSION_ROS = 7023, // This ability is disabled until Ring of Scale. + DISABLED_UNTIL_EXPANSION_TBL = 7024, // This ability is disabled until The Burning Lands. + DISABLED_UNTIL_EXPANSION_TOV = 7025, // This ability is disabled until Torment of Velious. + DISABLED_UNTIL_EXPANSION_COV = 7026, // This ability is disabled until Claws of Veeshan. + HAS_NO_MANA_BURN_BUFF = 8450, // This spell will not take hold until the effects of the previous Mana Burn have expired. + IS_RACE_FIRST_CUSTOM = 10000, // | custom range to restrict targets or casters by race *not on live* | + IS_RACE_LAST_CUSTOM = 11000, // | custom range to restrict targets or casters by race *not on live* | + IS_CLIENT_AND_MALE_PLATE_USER = 11044, // Your target wouldn't look right as that Jann. + IS_CLEINT_AND_MALE_DRUID_ENCHANTER_MAGICIAN_NECROANCER_SHAMAN_OR_WIZARD = 11090, // Your target wouldn't look right as that Jann. + IS_CLIENT_AND_MALE_BEASTLORD_BERSERKER_MONK_RANGER_OR_ROGUE = 11209, // Your target wouldn't look right as that Jann. + IS_CLIENT_AND_FEMALE_PLATE_USER = 11210, // Your target wouldn't look right as that Jann. + IS_CLIENT_AND_FEMALE_DRUID_ENCHANTER_MAGICIAN_NECROANCER_SHAMAN_OR_WIZARD = 11211, // Your target wouldn't look right as that Jann. + IS_CLIENT_AND_FEMALE_BEASTLORD_BERSERKER_MONK_RANGER_OR_ROGUE = 11248, // Your target wouldn't look right as that Jann. + HAS_TRAVELED_TO_STRATOS = 11260, // You must travel to Stratos at least once before wishing to go there. + HAS_TRAVELED_TO_AALISHAI = 11261, // You must travel to Aalishai at least once before wishing to go there. + HAS_TRAVELED_TO_MEARATS = 11268, // You must travel to Mearatas at least once before wishing to go there. + HAS_NO_ILLUSIONS_OF_GRANDEUR_BUFF = 12519, // + IS_HP_ABOVE_50_PCT = 16010, // + IS_HP_UNDER_50_PCT = 16031, // + IS_OFF_HAND_EQUIPED = 27672, // You must be wielding a weapon or shield in your offhand to use this ability. + HAS_NO_PACT_OF_FATE_RECOURSE_BUFF = 29556, // This spell will not work while Pact of Fate Recourse is active. | caster restriction | + HAS_NO_SHROUD_OF_PRAYER_BUFF = 32339, // Your target cannot receive another Quiet Prayer this soon. + IS_MANA_BELOW_20_PCT = 38311, // This ability requires you to be at or below 20% of your maximum mana. + IS_MANA_ABOVE_50_PCT = 38312, // This ability requires you to be at or above 50% of your maximum mana. + COMPLETED_ACHIEVEMENT_LEGENDARY_ANSWERER = 39281, // You have completed Legendary Answerer. + HAS_NO_ROGUES_FURY_BUFF = 40297, // This spell will not affect anyone that currently has Rogue's Fury active. | caster restriction | + NOT_COMPLETED_ACHIEVEMENT_LEGENDARY_ANSWERER = 42280, // You must complete Legendary Answerer. + IS_SUMMONED_OR_UNDEAD = 49326, // + IS_CLASS_CASTER_PRIEST = 49529, // + IS_END_OR_MANA_ABOVE_20_PCT = 49543, // You must have at least 20% of your maximum mana and endurance to use this ability. //pure melee class check end, other check mana + IS_END_OR_MANA_BELOW_30_PCT = 49573, // Your target already has 30% or more of their maximum mana or endurance. //pure melee class check the, other check more + IS_CLASS_BARD2 = 49574, // + IS_NOT_CLASS_BARD = 49575, // + HAS_NO_FURIOUS_RAMPAGE_BUFF = 49612, // This ability cannot be activated while Furious Rampage is active. + IS_END_OR_MANA_BELOW_30_PCT2 = 49809, // You can only perform this solo if you have less than 30% mana or endurance. + HAS_NO_HARMONIOUS_PRECISION_BUFF = 50003, // This spell will not work if you have the Harmonious Precision line active. + HAS_NO_HARMONIOUS_EXPANSE_BUFF = 50009, // This spell will not work if you have the Harmonious Expanse line active. + UNKNOWN_99999 = 99999, // | caster restriction | works will spell 27672 Strike of Ire +}; + + enum SpellTypes : uint32 { @@ -1037,7 +1348,7 @@ struct SPDat_Spell_Struct /* 217 */ int override_crit_chance; //Places a cap on the max chance to critical -- OVERRIDE_CRIT_CHANCE /* 218 */ int aemaxtargets; //Is used for various AE effects -- MAX_TARGETS /* 219 */ int no_heal_damage_item_mod; // -- NO_HEAL_DAMAGE_ITEM_MOD -/* 220 */ //int caster_requirement_id; // -- CASTER_REQUIREMENT_ID +/* 220 */ int caster_requirement_id; // -- CASTER_REQUIREMENT_ID /* 221 */ int spell_class; // -- SPELL_CLASS /* 222 */ int spell_subclass; // -- SPELL_SUBCLASS /* 223 */ //int ai_valid_targets; // -- AI_VALID_TARGETS diff --git a/zone/mob.cpp b/zone/mob.cpp index 6d4dfc0e9..2bbb15c30 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -5903,6 +5903,7 @@ int32 Mob::GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot) else if (id == "rank") {return spells[spell_id].rank; } else if (id == "no_resist") {return spells[spell_id].no_resist; } else if (id == "CastRestriction") {return spells[spell_id].CastRestriction; } + else if (id == "caster_requirement_id") { return spells[spell_id].caster_requirement_id; } else if (id == "AllowRest") {return spells[spell_id].AllowRest; } else if (id == "InCombat") {return spells[spell_id].InCombat; } else if (id == "OutofCombat") {return spells[spell_id].OutofCombat; } diff --git a/zone/mob.h b/zone/mob.h index a951d7cb3..1a3bffc4f 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -420,6 +420,7 @@ public: inline float GetTargetRingZ() const { return m_TargetRing.z; } inline bool HasEndurUpkeep() const { return endur_upkeep; } inline void SetEndurUpkeep(bool val) { endur_upkeep = val; } + bool HasBuffWithSpellGroup(int spellgroup); //Basic Stats/Inventory virtual void SetLevel(uint8 in_level, bool command = false) { level = in_level; } @@ -832,7 +833,7 @@ public: int8 GetDecayEffectValue(uint16 spell_id, uint16 spelleffect); int32 GetExtraSpellAmt(uint16 spell_id, int32 extra_spell_amt, int32 base_spell_dmg); void MeleeLifeTap(int32 damage); - bool PassCastRestriction(bool UseCastRestriction = true, int16 value = 0, bool IsDamage = true); + bool PassCastRestriction(int value); bool ImprovedTaunt(); bool TryRootFadeByDamage(int buffslot, Mob* attacker); float GetSlowMitigation() const { return slow_mitigation; } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 0284fd4c6..848edd656 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -231,12 +231,14 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if (buffslot >= 0) break; + if (spells[spell_id].base2[i] && !PassCastRestriction(spells[spell_id].base2[i])) { + break; //no messages are given on live if this fails. + } + // for offensive spells check if we have a spell rune on int32 dmg = effect_value; if(dmg < 0) { - if (!PassCastRestriction(false, spells[spell_id].base2[i], true)) - break; // take partial damage into account dmg = (int32) (dmg * partial / 100); @@ -253,9 +255,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove else if(dmg > 0) { //healing spell... - if (!PassCastRestriction(false, spells[spell_id].base2[i], false)) - break; - if(caster) dmg = caster->GetActSpellHealing(spell_id, dmg, this); @@ -289,15 +288,15 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove // hack fix for client health not reflecting server value last_hp = 0; + if (spells[spell_id].base2[i] && !PassCastRestriction(spells[spell_id].base2[i])) { + break; + } + //do any AAs apply to these spells? if(dmg < 0) { - if (!PassCastRestriction(false, spells[spell_id].base2[i], true)) - break; dmg = -dmg; Damage(caster, dmg, spell_id, spell.skill, false, buffslot, false); } else { - if (!PassCastRestriction(false, spells[spell_id].base2[i], false)) - break; HealDamage(dmg, caster); } break; @@ -3750,8 +3749,11 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) switch (effect) { case SE_CurrentHP: { - if (!PassCastRestriction(false, spells[buff.spellid].base2[i], true)) + + if (spells[buff.spellid].base2[i] && !PassCastRestriction(spells[buff.spellid].base2[i])) { break; + } + effect_value = CalcSpellEffectValue(buff.spellid, i, buff.casterlevel, buff.instrument_mod, caster, buff.ticsremaining); // Handle client cast DOTs here. @@ -7108,331 +7110,948 @@ bool Mob::ImprovedTaunt(){ } -bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDamage) +bool Mob::PassCastRestriction(int value) { - /*If return TRUE spell met all restrictions and can continue (this = target). - This check is used when the spell_new field CastRestriction is defined OR spell effect '0'(DD/Heal) has a defined limit - Range 1 : UNKNOWN -- the spells with this seem to not have a restiction, true for now - Range 100 : *Animal OR Humanoid - Range 101 : *Dragon - Range 102 : *Animal OR Insect - Range 103 : NOT USED - Range 104 : *Animal - Range 105 : Plant - Range 106 : *Giant - Range 107 : NOT USED - Range 108 : NOT Animal or Humaniod - Range 109 : *Bixie - Range 111 : *Harpy - Range 112 : *Sporali - Range 113 : *Kobold - Range 114 : *Shade Giant - Range 115 : *Drakkin - Range 116 : NOT USED - Range 117 : *Animal OR Plant - Range 118 : *Summoned - Range 119 : *Firepet - Range 120 : Undead - Range 121 : *Living (NOT Undead) - Range 122 : *Fairy - Range 123 : Humanoid - Range 124 : *Undead HP < 10% - Range 125 : *Clockwork HP < 10% - Range 126 : *Wisp HP < 10% - Range 127 : UNKNOWN - Range 128 : pure melee -- guess - Range 129 : pure caster -- guess - Range 130 : hybrid -- guess - Range 150 : UNKNOWN - Range 190 : No Raid boss flag *not implemented - Range 191 : This spell will deal less damage to 'exceptionally strong targets' - Raid boss flag *not implemented - Range 201 : Damage if HP > 75% - Range 203 : Damage if HP < 20% - Range 216 : TARGET NOT IN COMBAT - Range 221 - 249 : Causing damage dependent on how many pets/swarmpets are attacking your target. - Range 250 : Damage if HP < 35% - Range 300 - 303 : UNKOWN *not implemented - Range 304 : Chain + Plate class (buffs) - Range 399 - 409 : Heal if HP within a specified range (400 = 0-25% 401 = 25 - 35% 402 = 35-45% ect) - Range 410 - 411 : UNKOWN -- examples are auras that cast on NPCs maybe in combat/out of combat? - Range 500 - 599 : Heal if HP less than a specified value - Range 600 - 699 : Limit to Body Type [base2 - 600 = Body] - Range 700 : NPC only -- from patch notes "Wizard - Arcane Fusion no longer deals damage to non-NPC targets. This should ensure that wizards who fail their Bucolic Gambit are slightly less likely to annihilate themselves." - Range 701 : NOT PET - Range 800 : UKNOWN -- Target's Target Test (16598) - Range 812 : UNKNOWN -- triggered by Thaumatize Owner - Range 814 : UNKNOWN -- Vegetentacles - Range 815 : UNKNOWN -- Pumpkin Pulp Splash - Range 816 : UNKNOWN -- Rotten Fruit Splash - Range 817 : UNKNOWN -- Tainted Bixie Pollen Splash - Range 818 - 819 : If Undead/If Not Undead - Range 820 - 822 : UKNOWN - Range 835 : Unknown *not implemented - Range 836 - 837 : Progression Server / Live Server *not fully implemented - Range 839 : Progression Server and GoD released -- broken until Oct 21 2015 on live *not fully implemented - Range 842 - 844 : Humaniod lv MAX ((842 - 800) * 2) - Range 845 - 847 : UNKNOWN - Range 860 - 871 : Humanoid lv MAX 860 = 90, 871 = 104 *not implemented - Range 10000 - 11000 : Limit to Race [base2 - 10000 = Race] (*Not on live: Too useful a function to not implement) - THIS IS A WORK IN PROGRESS + /* + Restriction ID corresponds to the type 39 value in dstr_us on live clients (2021). See enum SpellRestriction for full list. + Modern client will give a message corresponding the type 39 field in the dstr_us for many of these effects upon failure. + + Use with spell_news table field 'CastRestriction' which limits targets by restrictions below and 'caster_requirement' (field220) + which limits caster by restrictions below. + These restrictions also apply to direct damage,dot, heal spells using SPA 0 or SPA 79 by placing a restriction id in the LIMIT field. + + Note: (ID 221 - 249) For effect seen in mage spell 'Shock of Many' which increases damage based on number of pets on targets hatelist. The way it is implemented + works for how our ROF2 spell file handles the effect where each slot fires individually, while on live it only takes the highest + value. In the future the way check is done will need to be adjusted to check a defined range instead of just great than. */ - if (value <= 0) + if (value <= 0) { return true; + } - if (IsDamage || UseCastRestriction) { + switch(value) + { + case 1: + return true; + break; - switch(value) - { - case 1: + case IS_NOT_ON_HORSE: + if (IsClient() && !CastToClient()->GetHorseId()) return true; + break; - case 100: - if ((GetBodyType() == BT_Animal) || (GetBodyType() == BT_Humanoid)) - return true; - break; + case IS_ANIMAL_OR_HUMANOID: + if ((GetBodyType() == BT_Animal) || (GetBodyType() == BT_Humanoid)) + return true; + break; - case 101: - if (GetBodyType() == BT_Dragon || GetBodyType() == BT_VeliousDragon || GetBodyType() == BT_Dragon3) - return true; - break; + case IS_DRAGON: + if (GetBodyType() == BT_Dragon || GetBodyType() == BT_VeliousDragon || GetBodyType() == BT_Dragon3) + return true; + break; - case 102: - if ((GetBodyType() == BT_Animal) || (GetBodyType() == BT_Insect)) - return true; - break; + case IS_ANIMAL_OR_INSECT: + if ((GetBodyType() == BT_Animal) || (GetBodyType() == BT_Insect)) + return true; + break; - case 104: - if (GetBodyType() == BT_Animal) - return true; - break; + case IS_BODY_TYPE_MISC: + if ((GetBodyType() == BT_Humanoid) || (GetBodyType() == BT_Lycanthrope) || (GetBodyType() == BT_Giant) || + (GetBodyType() == BT_RaidGiant) || (GetBodyType() == BT_RaidColdain) || (GetBodyType() == BT_Animal)|| + (GetBodyType() == BT_Construct) || (GetBodyType() == BT_Dragon) || (GetBodyType() == BT_Insect)|| + (GetBodyType() == BT_VeliousDragon) || (GetBodyType() == BT_Muramite) || (GetBodyType() == BT_Magical)) + return true; + break; - case 105: - if (GetBodyType() == BT_Plant) - return true; - break; + case IS_BODY_TYPE_MISC2: + if ((GetBodyType() == BT_Humanoid) || (GetBodyType() == BT_Lycanthrope) || (GetBodyType() == BT_Giant) || + (GetBodyType() == BT_RaidGiant) || (GetBodyType() == BT_RaidColdain) || (GetBodyType() == BT_Animal) || + (GetBodyType() == BT_Insect)) + return true; + break; - case 106: - if (GetBodyType() == BT_Giant) - return true; - break; + case IS_PLANT: + if (GetBodyType() == BT_Plant) + return true; + break; - case 108: - if ((GetBodyType() != BT_Animal) || (GetBodyType() != BT_Humanoid)) - return true; - break; + case IS_GIANT: + if (GetBodyType() == BT_Giant) + return true; + break; - case 109: - if ((GetRace() == 520) ||(GetRace() == 79)) - return true; - break; + case IS_NOT_ANIMAL_OR_HUMANOID: + if ((GetBodyType() != BT_Animal) || (GetBodyType() != BT_Humanoid)) + return true; + break; - case 111: - if ((GetRace() == 527) ||(GetRace() == 11)) - return true; - break; + case IS_BIXIE: + case IS_BIXIE2: + if ((GetRace() == RT_BIXIE) ||(GetRace() == RT_BIXIE_2)) + return true; + break; - case 112: - if ((GetRace() == 456) ||(GetRace() == 28)) - return true; - break; + case IS_HARPY: + if ((GetRace() == RT_HARPY) ||(GetRace() == RT_HARPY_2)) + return true; + break; - case 113: - if ((GetRace() == 456) ||(GetRace() == 48)) - return true; - break; + case IS_GNOLL: + if ((GetRace() == RT_GNOLL) || (GetRace() == RT_GNOLL_2) || (GetRace() == RT_GNOLL_3)) + return true; + break; - case 114: - if (GetRace() == 526) - return true; - break; + case IS_SPORALI: + if ((GetRace() == RT_SPORALI) ||(GetRace() == RT_FUNGUSMAN)) + return true; + break; - case 115: - if (GetRace() == 522) - return true; - break; + case IS_KOBOLD: + if ((GetRace() == RT_KOBOLD) ||(GetRace() == RT_KOBOLD_2)) + return true; + break; - case 117: - if ((GetBodyType() == BT_Animal) || (GetBodyType() == BT_Plant)) - return true; - break; + case IS_FROSTCRYPT_SHADE: + if (GetRace() == RT_GIANT_SHADE) + return true; + break; - case 118: - if (GetBodyType() == BT_Summoned) - return true; - break; + case IS_DRAKKIN: + if (GetRace() == RT_DRAKKIN) + return true; + break; - case 119: - if (IsPet() && ((GetRace() == 212) || ((GetRace() == 75) && GetTexture() == 1))) - return true; - break; + case IS_UNDEAD_OR_VALDEHOLM_GIANT: + if (GetBodyType() == BT_Undead || GetRace() == RT_GIANT_12 || GetRace() == RT_GIANT_13) + return true; + break; - case 120: - if (GetBodyType() == BT_Undead) - return true; - break; + case IS_ANIMAL_OR_PLANT: + if ((GetBodyType() == BT_Animal) || (GetBodyType() == BT_Plant)) + return true; + break; - case 121: - if (GetBodyType() != BT_Undead) - return true; - break; + case IS_SUMMONED: + if (GetBodyType() == BT_Summoned) + return true; + break; - case 122: - if ((GetRace() == 473) || (GetRace() == 425)) - return true; - break; + case IS_CLASS_WIZARD: + case IS_WIZARD_USED_ON_MAGE_FIRE_PET: + if (GetClass() == WIZARD) + return true; + break; - case 123: - if (GetBodyType() == BT_Humanoid) - return true; - break; + case IS_UNDEAD: + if (GetBodyType() == BT_Undead) + return true; + break; - case 124: - if ((GetBodyType() == BT_Undead) && (GetHPRatio() < 10)) - return true; - break; + case IS_NOT_UNDEAD_OR_SUMMONED_OR_VAMPIRE: + if ((GetBodyType() != BT_Undead) && (GetBodyType() != BT_Summoned) && (GetBodyType() != BT_Vampire)) + return true; + break; - case 125: - if ((GetRace() == 457 || GetRace() == 88) && (GetHPRatio() < 10)) - return true; - break; + case IS_FAE_OR_PIXIE: + if ((GetRace() == RT_PIXIE) || (GetRace() == RT_FAY_DRAKE)) + return true; + break; - case 126: - if ((GetRace() == 581 || GetRace() == 69) && (GetHPRatio() < 10)) - return true; - break; + case IS_HUMANOID: + if (GetBodyType() == BT_Humanoid) + return true; + break; - case 201: - if (GetHPRatio() > 75) - return true; - break; + case IS_UNDEAD_AND_HP_LESS_THAN_10_PCT: + if ((GetBodyType() == BT_Undead) && (GetHPRatio() < 10)) + return true; + break; - case 204: - if (GetHPRatio() < 20) - return true; - break; + case IS_CLOCKWORK_AND_HP_LESS_THAN_45_PCT: + if ((GetRace() == RT_GNOMEWORK || GetRace() == RACE_CLOCKWORK_GNOME_88) && (GetHPRatio() < 45)) + return true; + break; - case 216: - if (!IsEngaged()) - return true; - break; + case IS_WISP_AND_HP_LESS_THAN_10_PCT: + if ((GetRace() == RT_WILL_O_WISP) && (GetHPRatio() < 10)) + return true; + break; - case 250: - if (GetHPRatio() < 35) - return true; - break; + case IS_CLASS_MELEE_THAT_CAN_BASH_OR_KICK_EXCEPT_BARD: + if ((GetClass() != BARD) && (GetClass() != ROGUE) && IsFighterClass(GetClass())) + return true; + break; - case 304: - if (IsClient() && - ((GetClass() == WARRIOR) || (GetClass() == BARD) || (GetClass() == SHADOWKNIGHT) || (GetClass() == PALADIN) || (GetClass() == CLERIC) - || (GetClass() == RANGER) || (GetClass() == SHAMAN) || (GetClass() == ROGUE) || (GetClass() == BERSERKER))) - return true; - break; + case IS_CLASS_PURE_MELEE: + if (GetClass() == ROGUE || GetClass() == WARRIOR || GetClass() == BERSERKER || GetClass() == MONK) + return true; + break; - case 700: - if (IsNPC()) - return true; - break; + case IS_CLASS_PURE_CASTER: + if (IsINTCasterClass(GetClass())) + return true; + break; - case 701: - if (!IsPet()) - return true; - break; + case IS_CLASS_HYBRID_CLASS: + if (IsHybridClass(GetClass())) + return true; + break; + + case IS_CLASS_WARRIOR: + if (GetClass() == WARRIOR) + return true; + break; - case 818: - if (GetBodyType() == BT_Undead) - return true; - break; + case IS_CLASS_CLERIC: + if (GetClass() == CLERIC) + return true; + break; - case 819: - if (GetBodyType() != BT_Undead) - return true; - break; + case IS_CLASS_PALADIN: + if (GetClass() == PALADIN) + return true; + break; - case 836: - return true; // todo implement progression flag assume not progression for now + case IS_CLASS_RANGER: + if (GetClass() == RANGER) + return true; + break; - case 837: - return false; // todo implement progression flag assume not progression for now + case IS_CLASS_SHADOWKNIGHT: + if (GetClass() == SHADOWKNIGHT) + return true; + break; - case 839: - return true; // todo implement progression flag assume not progression for now, this one is a check if GoD is live + case IS_CLASS_DRUID: + if (GetClass() == DRUID) + return true; + break; - case 842: - if (GetBodyType() == BT_Humanoid && GetLevel() <= 84) - return true; - break; + case IS_CLASS_MONK: + if (GetClass() == MONK) + return true; + break; - case 843: - if (GetBodyType() == BT_Humanoid && GetLevel() <= 86) - return true; - break; + case IS_CLASS_BARD2: + case IS_CLASS_BARD: + if (GetClass() == BARD) + return true; + break; - case 844: - if (GetBodyType() == BT_Humanoid && GetLevel() <= 88) - return true; - break; - } + case IS_CLASS_ROGUE: + if (GetClass() == ROGUE) + return true; + break; - //Limit to amount of pets - if (value >= 221 && value <= 249){ + case IS_CLASS_SHAMAN: + if (GetClass() == SHAMAN) + return true; + break; + + case IS_CLASS_NECRO: + if (GetClass() == NECROMANCER) + return true; + break; + + case IS_CLASS_MAGE: + if (GetClass() == MAGICIAN) + return true; + break; + + case IS_CLASS_ENCHANTER: + if (GetClass() == ENCHANTER) + return true; + break; + + case IS_CLASS_BEASTLORD: + if (GetClass() == BEASTLORD) + return true; + break; + + case IS_CLASS_BERSERKER: + if (GetClass() == BERSERKER) + return true; + break; + + case IS_CLASS_CLR_SHM_DRU: + if (IsWISCasterClass(GetClass())) + return true; + break; + + case IS_CLASS_NOT_WAR_PAL_SK: + if ((GetClass() != WARRIOR) && (GetClass() != PALADIN) && (GetClass() != SHADOWKNIGHT)) + return true; + break; + + case IS_LEVEL_UNDER_100: + if (GetLevel() < 100) + return true; + break; + + case IS_NOT_RAID_BOSS: + if (!IsRaidTarget()) + return true; + break; + + case IS_RAID_BOSS: + if (IsRaidTarget()) + return true; + break; + + case FRENZIED_BURNOUT_ACTIVE: + if (HasBuffWithSpellGroup(SPELLGROUP_FRENZIED_BURNOUT)) + return true; + break; + + case FRENZIED_BURNOUT_NOT_ACTIVE: + if (!HasBuffWithSpellGroup(SPELLGROUP_FRENZIED_BURNOUT)) + return true; + break; + + case IS_HP_ABOVE_75_PCT: + if (GetHPRatio() > 75) + return true; + break; + + case IS_HP_LESS_THAN_20_PCT: + if (GetHPRatio() <= 20) + return true; + break; + + case IS_HP_LESS_THAN_50_PCT: + if (GetHPRatio() <= 50) + return true; + break; + + case IS_HP_LESS_THAN_75_PCT: + if (GetHPRatio() <= 75) + return true; + break; + + case IS_NOT_IN_COMBAT: + if (!IsEngaged()) + return true; + break; + + case IS_HP_LESS_THAN_35_PCT: + if (GetHPRatio() <= 35) + return true; + break; + + case HAS_BETWEEN_1_TO_2_PETS_ON_HATELIST: { int count = hate_list.GetSummonedPetCountOnHateList(this); + if (count >= 1 && count <= 2) { + return true; + } + break; + } - for (int base2_value = 221; base2_value <= 249; ++base2_value){ - if (value == base2_value){ - if (count >= (base2_value - 220)){ - return true; - } + case HAS_BETWEEN_3_TO_5_PETS_ON_HATELIST: { + int count = hate_list.GetSummonedPetCountOnHateList(this); + if (count >= 3 && count <= 5) { + return true; + } + break; + } + + case HAS_BETWEEN_6_TO_9_PETS_ON_HATELIST: { + int count = hate_list.GetSummonedPetCountOnHateList(this); + if (count >= 6 && count <= 9) { + return true; + } + break; + } + + case HAS_BETWEEN_10_TO_14_PETS_ON_HATELIST: { + int count = hate_list.GetSummonedPetCountOnHateList(this); + if (count >= 10 && count <= 14) { + return true; + } + break; + } + + case HAS_MORE_THAN_14_PETS_ON_HATELIST: { + int count = hate_list.GetSummonedPetCountOnHateList(this); + if (count > 14) { + return true; + } + break; + } + + case IS_CLASS_CHAIN_OR_PLATE: + if (IsClient() && + ((GetClass() == WARRIOR) || (GetClass() == BARD) || (GetClass() == SHADOWKNIGHT) || (GetClass() == PALADIN) || (GetClass() == CLERIC) + || (GetClass() == RANGER) || (GetClass() == SHAMAN) || (GetClass() == ROGUE) || (GetClass() == BERSERKER))) + return true; + break; + + case IS_HP_BETWEEN_5_AND_9_PCT: + if (GetHPRatio() >= 5 && GetHPRatio() <= 9) + return true; + break; + + case IS_HP_BETWEEN_10_AND_14_PCT: + if (GetHPRatio() >= 10 && GetHPRatio() <= 14) + return true; + break; + + case IS_HP_BETWEEN_15_AND_19_PCT: + if (GetHPRatio() >= 15 && GetHPRatio() <= 19) + return true; + break; + + case IS_HP_BETWEEN_20_AND_24_PCT: + if (GetHPRatio() >= 20 && GetHPRatio() <= 24) + return true; + break; + + case IS_HP_BETWEEN_25_AND_29_PCT: + if (GetHPRatio() >= 25 && GetHPRatio() <= 29) + return true; + break; + + case IS_HP_BETWEEN_30_AND_34_PCT: + if (GetHPRatio() >= 30 && GetHPRatio() <= 34) + return true; + break; + + case IS_HP_BETWEEN_35_AND_39_PCT: + if (GetHPRatio() >= 35 && GetHPRatio() <= 39) + return true; + break; + + case IS_HP_BETWEEN_40_AND_44_PCT: + if (GetHPRatio() >= 40 && GetHPRatio() <= 44) + return true; + break; + + case IS_HP_BETWEEN_45_AND_49_PCT: + if (GetHPRatio() >= 45 && GetHPRatio() <= 49) + return true; + break; + + case IS_HP_BETWEEN_50_AND_54_PCT: + if (GetHPRatio() >= 50 && GetHPRatio() <= 54) + return true; + break; + + case IS_HP_BETWEEN_55_AND_59_PCT: + if (GetHPRatio() >= 55 && GetHPRatio() <= 59) + return true; + break; + + case IS_HP_BETWEEN_5_AND_15_PCT: + if (GetHPRatio() >= 5 && GetHPRatio() <= 15) + return true; + break; + + case IS_HP_BETWEEN_15_AND_25_PCT: + if (GetHPRatio() >= 15 && GetHPRatio() <= 25) + return true; + break; + + case IS_HP_BETWEEN_1_AND_25_PCT: + if (GetHPRatio() <= 25) + return true; + break; + + case IS_HP_BETWEEN_25_AND_35_PCT: + if (GetHPRatio() > 25 && GetHPRatio() <= 35) + return true; + break; + + case IS_HP_BETWEEN_35_AND_45_PCT: + if (GetHPRatio() > 35 && GetHPRatio() <= 45) + return true; + break; + + case IS_HP_BETWEEN_45_AND_55_PCT: + if (GetHPRatio() > 45 && GetHPRatio() <= 55) + return true; + break; + + case IS_HP_BETWEEN_55_AND_65_PCT: + if (GetHPRatio() > 55 && GetHPRatio() <= 65) + return true; + break; + + case IS_HP_BETWEEN_65_AND_75_PCT: + if (GetHPRatio() > 65 && GetHPRatio() <= 75) + return true; + break; + + case IS_HP_BETWEEN_75_AND_85_PCT: + if (GetHPRatio() > 75 && GetHPRatio() <= 85) + return true; + break; + + case IS_HP_BETWEEN_85_AND_95_PCT: + if (GetHPRatio() > 85 && GetHPRatio() <= 95) + return true; + break; + + case IS_HP_ABOVE_45_PCT: + if (GetHPRatio() > 45) + return true; + break; + + case IS_HP_ABOVE_55_PCT: + if (GetHPRatio() > 55) + return true; + break; + + case IS_MANA_ABOVE_10_PCT: + if (GetManaRatio() > 10) + return true; + break; + + case IS_ENDURANCE_BELOW_40_PCT: + if (IsClient() && CastToClient()->GetEndurancePercent() <= 40) + return true; + break; + + case IS_MANA_BELOW_40_PCT: + if (GetManaRatio() <= 40) + return true; + break; + + case IS_HP_ABOVE_20_PCT: + if (GetHPRatio() > 20) + return true; + break; + + case IS_NOT_UNDEAD_OR_SUMMONED: + if ((GetBodyType() != BT_Undead) && (GetBodyType() != BT_Summoned)) + return true; + break; + + case IS_NOT_PLANT: + if (GetBodyType() != BT_Plant) + return true; + break; + + case IS_NOT_CLIENT: + if (!IsClient()) + return true; + break; + + case IS_CLIENT: + if (IsClient()) + return true; + break; + + case IS_LEVEL_ABOVE_42_AND_IS_CLIENT: + if (IsClient() && GetLevel() > 42) + return true; + break; + + case IS_TREANT: + if (GetRace() == RT_TREANT || GetRace() == RT_TREANT_2 || GetRace() == RT_TREANT_3) + return true; + break; + + case IS_SCARECROW: + if (GetRace() == RT_SCARECROW || GetRace() == RT_SCARECROW_2) + return true; + break; + + case IS_VAMPIRE_OR_UNDEAD_OR_UNDEADPET: + if (GetBodyType() == BT_Vampire || GetBodyType() == BT_Undead || GetBodyType() == BT_SummonedUndead) + return true; + break; + + case IS_NOT_VAMPIRE_OR_UNDEAD: + if (GetBodyType() != BT_Vampire && GetBodyType() != BT_Undead && GetBodyType() != BT_SummonedUndead) + return true; + break; + + case IS_CLASS_KNIGHT_HYBRID_MELEE: + if (IsHybridClass(GetClass()) || IsNonSpellFighterClass(GetClass())) + return true; + break; + + case IS_CLASS_WARRIOR_CASTER_PRIEST: + if (IsCasterClass(GetClass()) || GetClass() == WARRIOR) + return true; + break; + + case IS_END_BELOW_21_PCT: + if (IsClient() && CastToClient()->GetEndurancePercent() <= 21) + return true; + break; + + case IS_END_BELOW_25_PCT: + if (IsClient() && CastToClient()->GetEndurancePercent() <= 25) + return true; + break; + + case IS_END_BELOW_29_PCT: + if (IsClient() && CastToClient()->GetEndurancePercent() <= 29) + return true; + break; + + case IS_REGULAR_SERVER: + return true; // todo implement progression flag assume not progression for now + break; + + case IS_PROGRESSION_SERVER: + return false; // todo implement progression flag assume not progression for now + break; + + case IS_GOD_EXPANSION_UNLOCKED: + return true; // todo implement progression flag assume not progression for now, this one is a check if GoD is live + break; + + case IS_HUMANOID_LEVEL_84_MAX: + if (GetBodyType() == BT_Humanoid && GetLevel() <= 84) + return true; + break; + + case IS_HUMANOID_LEVEL_86_MAX: + if (GetBodyType() == BT_Humanoid && GetLevel() <= 86) + return true; + break; + + case IS_HUMANOID_LEVEL_88_MAX: + if (GetBodyType() == BT_Humanoid && GetLevel() <= 88) + return true; + break; + + case IS_LEVEL_90_MAX: + if (GetLevel() <= 90) + return true; + break; + + case IS_LEVEL_92_MAX: + if (GetLevel() <= 92) + return true; + break; + + case IS_LEVEL_94_MAX: + if (GetLevel() <= 94) + return true; + break; + + case IS_LEVEL_95_MAX: + if (GetLevel() <= 95) + return true; + break; + + case IS_LEVEL_97_MAX: + if (GetLevel() <= 97) + return true; + break; + + case IS_LEVEL_99_MAX: + if (GetLevel() <= 99) + return true; + break; + + case IS_LEVEL_100_MAX: + if (GetLevel() <= 100) + return true; + break; + + case IS_LEVEL_102_MAX: + if (GetLevel() <= 102) + return true; + break; + + case IS_LEVEL_104_MAX: + if (GetLevel() <= 104) + return true; + break; + case IS_LEVEL_105_MAX: + if (GetLevel() <= 105) + return true; + break; + + case IS_LEVEL_107_MAX: + if (GetLevel() <= 107) + return true; + break; + + case IS_LEVEL_109_MAX: + if (GetLevel() <= 109) + return true; + break; + case IS_LEVEL_110_MAX: + if (GetLevel() <= 110) + return true; + break; + + case IS_LEVEL_112_MAX: + if (GetLevel() <= 112) + return true; + break; + + case IS_LEVEL_114_MAX: + if (GetLevel() <= 114) + return true; + break; + + case IS_BETWEEN_LEVEL_1_AND_75: + if (GetLevel() >= 1 && GetLevel() <= 75) + return true; + break; + + case IS_BETWEEN_LEVEL_76_AND_85: + if (GetLevel() >= 76 && GetLevel() <= 85) + return true; + break; + + case IS_BETWEEN_LEVEL_86_AND_95: + if (GetLevel() >= 86 && GetLevel() <= 95) + return true; + break; + + case IS_BETWEEN_LEVEL_96_AND_105: + if (GetLevel() >= 96 && GetLevel() <= 105) + return true; + break; + + case IS_HP_LESS_THAN_80_PCT: + if (GetHPRatio() < 80) + return true; + break; + + case IS_LEVEL_ABOVE_34: + if (GetLevel() < 34) + return true; + break; + + case HAS_NO_MANA_BURN_BUFF: { + bool has_effect = false; + for (int i = 0; i < GetMaxTotalSlots(); i++) { + if (IsValidSpell(buffs[i].spellid) && IsEffectInSpell(buffs[i].spellid, SE_ManaBurn)) { + has_effect = true; } } - } - - //Limit to Body Type - if (value >= 600 && value <= 699){ - if (GetBodyType() == (value - 600)) + if (!has_effect) { return true; - } - - //Limit to Race. *Not implemented on live - if (value >= 10000 && value <= 11000){ - if (GetRace() == (value - 10000)) - return true; - } - } //End Damage - - if (!IsDamage || UseCastRestriction) { - - //Heal only if HP within specified range. [Doesn't follow a set forumla for all values...] - if (value >= 400 && value <= 408){ - for (int base2_value = 400; base2_value <= 408; ++base2_value){ - if (value == base2_value){ - - if (value == 400 && GetHPRatio() <= 25) - return true; - - else if (value == base2_value){ - if (GetHPRatio() > 25+((base2_value - 401)*10) && GetHPRatio() <= 35+((base2_value - 401)*10)) - return true; - } - } } + break; } - - else if (value >= 500 && value <= 549){ - for (int base2_value = 500; base2_value <= 520; ++base2_value){ - if (value == base2_value){ - if (GetHPRatio() < (base2_value - 500)*5) - return true; - } - } - } - - else if (value == 399) { - if (GetHPRatio() > 15 && GetHPRatio() <= 25) + + case IS_CLIENT_AND_MALE_PLATE_USER: + if (IsClient() && GetGender() == MALE && IsPlateClass(GetClass())) return true; - } - } // End Heal + break; + case IS_CLEINT_AND_MALE_DRUID_ENCHANTER_MAGICIAN_NECROANCER_SHAMAN_OR_WIZARD: + if (IsClient() && GetGender() == MALE && (IsCasterClass(GetClass()) && GetClass() != CLERIC)) + return true; + break; + + case IS_CLIENT_AND_MALE_BEASTLORD_BERSERKER_MONK_RANGER_OR_ROGUE: + if (IsClient() && GetGender() == MALE && + (GetClass() == BEASTLORD || GetClass() == BERSERKER || GetClass() == MONK || GetClass() == RANGER || GetClass() == ROGUE)) + return true; + break; + + case IS_CLIENT_AND_FEMALE_PLATE_USER: + if (IsClient() && GetGender() == FEMALE && IsPlateClass(GetClass())) + return true; + break; + + case IS_CLIENT_AND_FEMALE_DRUID_ENCHANTER_MAGICIAN_NECROANCER_SHAMAN_OR_WIZARD: + if (IsClient() && GetGender() == FEMALE && (IsCasterClass(GetClass()) && GetClass() != CLERIC)) + return true; + break; + + case IS_CLIENT_AND_FEMALE_BEASTLORD_BERSERKER_MONK_RANGER_OR_ROGUE: + if (IsClient() && GetGender() == FEMALE && + (GetClass() == BEASTLORD || GetClass() == BERSERKER || GetClass() == MONK || GetClass() == RANGER || GetClass() == ROGUE)) + return true; + break; + + case IS_HP_ABOVE_50_PCT: + if (GetHPRatio() > 50) + return true; + break; + + case IS_HP_BELOW_50_PCT: + if (GetHPRatio() <= 50) + return true; + break; + + case IS_OFF_HAND_EQUIPED: + if (HasShieldEquiped() || CanThisClassDualWield()) + return true; + break; + + case IS_MANA_BELOW_20_PCT: + if (GetManaRatio() <= 20) + return true; + break; + + case IS_MANA_ABOVE_50_PCT: + if (GetManaRatio() >= 50) + return true; + break; + + case IS_SUMMONED_OR_UNDEAD: + if (GetBodyType() == BT_Summoned || GetBodyType() == BT_Undead) + return true; + break; + + + case IS_CLASS_CASTER_PRIEST: + if (IsCasterClass(GetClass())) + return true; + break; + + case IS_END_OR_MANA_ABOVE_20_PCT: { + if (IsNonSpellFighterClass(GetClass()) && CastToClient()->GetEndurancePercent() >= 20) { + return true; + } + else if (!IsNonSpellFighterClass(GetClass()) && GetManaRatio() >= 20) { + return true; + } + break; + } + + case IS_END_OR_MANA_BELOW_30_PCT: + case IS_END_OR_MANA_BELOW_30_PCT2: { + if (IsNonSpellFighterClass(GetClass()) && CastToClient()->GetEndurancePercent() <= 30) { + return true; + } + else if (!IsNonSpellFighterClass(GetClass()) && GetManaRatio() <= 30) { + return true; + } + break; + } + + case IS_NOT_CLASS_BARD: + if (GetClass() != BARD) + return true; + break; + + case HAS_NO_PACT_OF_FATE_RECOURSE_BUFF: + if (!FindBuff(SPELL_PACT_OF_HATE_RECOURSE)) + return true; + break; + + case HAS_NO_ROGUES_FURY_BUFF: + if (!HasBuffWithSpellGroup(SPELLGROUP_ROGUES_FURY)) + return true; + break; + + case HAS_NO_ILLUSIONS_OF_GRANDEUR_BUFF: + if (!HasBuffWithSpellGroup(SPELLGROUP_ILLUSION_OF_GRANDEUR)) + return true; + break; + + case HAS_NO_HARMONIOUS_PRECISION_BUFF: + if (!HasBuffWithSpellGroup(SPELLGROUP_HARMONIOUS_PRECISION)) + return true; + break; + + case HAS_NO_HARMONIOUS_EXPANSE_BUFF: + if (!HasBuffWithSpellGroup(SPELLGROUP_HARMONIOUS_EXPANSE)) + return true; + break; + + case HAS_NO_FURIOUS_RAMPAGE_BUFF: + if (!HasBuffWithSpellGroup(SPELLGROUP_FURIOUS_RAMPAGE)) + return true; + break; + + case HAS_NO_SHROUD_OF_PRAYER_BUFF: + if (!HasBuffWithSpellGroup(SPELLGROUP_SHROUD_OF_PRAYER)) + return true; + break; + + case HAS_INCENDIARY_OOZE_BUFF: + if (FindBuff(SPELL_INCENDIARY_OOZE_BUFF)) + return true; + break; + + //Not handled, just allow them to pass for now. + case UNKNOWN_3: + case HAS_CRYSTALLIZED_FLAME_BUFF: + case UNKNOWN_199: + case UNKNOWN_TOO_MUCH_HP_410: + case UNKNOWN_TOO_MUCH_HP_411: + case HAS_TBL_ESIANTI_ACCESS: + case HAS_ITEM_CLOCKWORK_SCRAPS: + case IN_TWO_HANDED_STANCE: + case IN_DUAL_WIELD_HANDED_STANCE: + case IN_SHIELD_STANCE: + case NOT_IN_TWO_HANDED_STANCE: + case NOT_IN_DUAL_WIELD_HANDED_STANCE: + case NOT_IN_SHIELD_STANCE: + case DISABLED_UNTIL_EXPANSION_ROK: + case DISABLED_UNTIL_EXPANSION_SOV: + case DISABLED_UNTIL_EXPANSION_SOL: + case DISABLED_UNTIL_EXPANSION_POP: + case DISABLED_UNTIL_EXPANSION_LOY: + case DISABLED_UNTIL_EXPANSION_LDON: + case DISABLED_UNTIL_EXPANSION_GOD: + case DISABLED_UNTIL_EXPANSION_OOW: + case DISABLED_UNTIL_EXPANSION_DON: + case DISABLED_UNTIL_EXPANSION_DOD: + case DISABLED_UNTIL_EXPANSION_POR: + case DISABLED_UNTIL_EXPANSION_TSS: + case DISABLED_UNTIL_EXPANSION_TBS: + case DISABLED_UNTIL_EXPANSION_SOF: + case DISABLED_UNTIL_EXPANSION_SOD: + case DISABLED_UNTIL_EXPANSION_UF: + case DISABLED_UNTIL_EXPANSION_HOT: + case DISABLED_UNTIL_EXPANSION_VOA: + case DISABLED_UNTIL_EXPANSION_ROF: + case DISABLED_UNTIL_EXPANSION_COF: + case DISABLED_UNTIL_EXPANSION_TDS: + case DISABLED_UNTIL_EXPANSION_TBM: + case DISABLED_UNTIL_EXPANSION_EOK: + case DISABLED_UNTIL_EXPANSION_ROS: + case DISABLED_UNTIL_EXPANSION_TBL: + case DISABLED_UNTIL_EXPANSION_TOV: + case DISABLED_UNTIL_EXPANSION_COV: + case HAS_TRAVELED_TO_STRATOS: + case HAS_TRAVELED_TO_AALISHAI: + case HAS_TRAVELED_TO_MEARATS: + case COMPLETED_ACHIEVEMENT_LEGENDARY_ANSWERER: + case NOT_COMPLETED_ACHIEVEMENT_LEGENDARY_ANSWERER: + case HAS_WEAPONSTANCE_DEFENSIVE_PROFICIENCY: + case HAS_WEAPONSTANCE_TWO_HAND_PROFICIENCY: + case HAS_WEAPONSTANCE_DUAL_WEILD_PROFICIENCY: + case UNKNOWN_812: + case UNKNOWN_814: + case UNKNOWN_822: + case UNKNOWN_840: + case UNKNOWN_841: + case UNKNOWN_99999: + return true; + break; + } + + if (value >= HAS_AT_LEAST_1_PET_ON_HATELIST && value <= HAS_AT_LEAST_20_PETS_ON_HATELIST) { + int count = hate_list.GetSummonedPetCountOnHateList(this); + int minium_amount_of_pets_needed = (1 + value) - HAS_AT_LEAST_1_PET_ON_HATELIST; + + if (count >= minium_amount_of_pets_needed) { + return true; + } + } + + if (value >= IS_HP_BELOW_5_PCT && value <= IS_HP_BELOW_95_PCT) { + int hp_below_amt = 5 * ((1 + value) - IS_HP_BELOW_5_PCT); + if (GetHPRatio() <= hp_below_amt) { + return true; + } + } + + if (value >= IS_BODY_TYPE_UNDEFINED && value <= IS_BODY_TYPE_MURAMITE){ + if (GetBodyType() == (value - IS_BODY_TYPE_UNDEFINED)) + return true; + } + + //Limit to Race. *Not implemented on live, too much potential not to give an option here. + if (value >= IS_RACE_FIRST_CUSTOM && value <= IS_RACE_LAST_CUSTOM){ + if (GetRace() == (value - IS_RACE_FIRST_CUSTOM)) + return true; + } return false; } diff --git a/zone/spells.cpp b/zone/spells.cpp index 12bc97493..f5a756f79 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1535,8 +1535,13 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce if (isproc && IsNPC() && CastToNPC()->GetInnateProcSpellID() == spell_id) targetType = ST_Target; - if (spell_target && !spell_target->PassCastRestriction(true, spells[spell_id].CastRestriction)){ - MessageString(Chat::Red,SPELL_NEED_TAR); + if (spell_target && spells[spell_id].CastRestriction && !spell_target->PassCastRestriction(spells[spell_id].CastRestriction)){ + Message(Chat::Red, "Your target does not meet the spell requirements."); //Current live also adds description after this from dbstr_us type 39 + return false; + } + + if (spells[spell_id].caster_requirement_id && !PassCastRestriction(spells[spell_id].caster_requirement_id)) { + MessageString(Chat::Red, SPELL_WOULDNT_HOLD); return false; } @@ -4146,6 +4151,16 @@ uint32 Mob::BuffCount() { return active_buff_count; } +bool Mob::HasBuffWithSpellGroup(int spellgroup) +{ + for (int i = 0; i < GetMaxTotalSlots(); i++) { + if (IsValidSpell(buffs[i].spellid) && spells[buffs[i].spellid].spellgroup == spellgroup) { + return true; + } + } + return false; +} + // removes all buffs void Mob::BuffFadeAll() { From 642cbfcadcac816c840ea56c3fa2297737fafa6a Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 31 Aug 2021 01:41:43 -0400 Subject: [PATCH 163/624] [Bug Fix] Shared Bank Charges Fix (#1511) - Shared bank charges were being set to int8 on select, meaning any item that stacks over 127 would break if put in shared bank, causing loss of items. --- common/shareddb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 042f0cf6a..7e638d6e0 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -501,7 +501,7 @@ bool SharedDatabase::GetSharedBank(uint32 id, EQ::InventoryProfile *inv, bool is for (auto row = results.begin(); row != results.end(); ++row) { int16 slot_id = (int16)atoi(row[0]); uint32 item_id = (uint32)atoi(row[1]); - int8 charges = (int8)atoi(row[2]); + int16 charges = (int16)atoi(row[2]); uint32 aug[EQ::invaug::SOCKET_COUNT]; aug[0] = (uint32)atoi(row[3]); From 26299354b69e0fedcd09ba9e573d77acfe10270b Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 31 Aug 2021 01:42:08 -0400 Subject: [PATCH 164/624] [Quest API] Adds new methods to NPCs and Corpses (#1510) - Add $npc->HasItem(item_id) to Perl. - Add $npc->CountItem(item_id) to Perl. - Add $npc->GetItemIDBySlot(loot_slot) to Perl. - Add $npc->GetFirstSlotByItemID(item_id) to Perl. - Add $corpse->HasItem(item_id) to Perl. - Add $corpse->CountItem(item_id) to Perl. - Add $corpse->GetItemIDBySlot(loot_slot) to Perl. - Add $corpse->GetFirstSlotByItemID(item_id) to Perl. - Add npc:HasItem(item_id) to Lua. - Add npc:CountItem(item_id) to Lua. - Add npc:GetItemIDBySlot(loot_slot) to Lua. - Add npc:GetFirstSlotByItemID(item_id) to Lua. - Add corpse:HasItem(item_id) to Lua. - Add corpse:CountItem(item_id) to Lua. - Add corpse:GetItemIDBySlot(loot_slot) to Lua. - Add corpse:GetFirstSlotByItemID(item_id) to Lua. These methods will allow server operators to view the loot a current has in a slot, the first slot found by item ID, count the item by ID, and see if the NPC has the item. With that functionality you could build a custom loot system and modify loot more dynamically. --- zone/corpse.cpp | 69 ++++++++++++++++++++++++++++++++++ zone/corpse.h | 4 ++ zone/lua_corpse.cpp | 26 ++++++++++++- zone/lua_corpse.h | 4 ++ zone/lua_npc.cpp | 30 ++++++++++++++- zone/lua_npc.h | 4 ++ zone/npc.cpp | 69 ++++++++++++++++++++++++++++++++++ zone/npc.h | 4 ++ zone/perl_npc.cpp | 75 +++++++++++++++++++++++++++++++++++++ zone/perl_player_corpse.cpp | 75 +++++++++++++++++++++++++++++++++++++ 10 files changed, 358 insertions(+), 2 deletions(-) diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 5113b6fdf..ae123e2da 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -1454,6 +1454,75 @@ void Corpse::QueryLoot(Client* to) { } } +bool Corpse::HasItem(uint32 item_id) { + if (!database.GetItem(item_id)) { + return false; + } + + for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { + ServerLootItem_Struct* loot_item = *current_item; + if (!loot_item) { + LogError("NPC::CountItem() - ItemList error, null item"); + continue; + } + + if (!loot_item->item_id || !database.GetItem(loot_item->item_id)) { + LogError("NPC::CountItem() - Database error, invalid item"); + continue; + } + + if (loot_item->item_id == item_id) { + return true; + } + } + return false; +} + +uint16 Corpse::CountItem(uint32 item_id) { + uint16 item_count = 0; + if (!database.GetItem(item_id)) { + return item_count; + } + + for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { + ServerLootItem_Struct* loot_item = *current_item; + if (!loot_item) { + LogError("NPC::CountItem() - ItemList error, null item"); + continue; + } + + if (!loot_item->item_id || !database.GetItem(loot_item->item_id)) { + LogError("NPC::CountItem() - Database error, invalid item"); + continue; + } + + if (loot_item->item_id == item_id) { + item_count += loot_item->charges; + } + } + return item_count; +} + +uint32 Corpse::GetItemIDBySlot(uint16 loot_slot) { + for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { + ServerLootItem_Struct* loot_item = *current_item; + if (loot_item->lootslot == loot_slot) { + return loot_item->item_id; + } + } + return 0; +} + +uint16 Corpse::GetFirstSlotByItemID(uint32 item_id) { + for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { + ServerLootItem_Struct* loot_item = *current_item; + if (loot_item->item_id == item_id) { + return loot_item->lootslot; + } + } + return 0; +} + bool Corpse::Summon(Client* client, bool spell, bool CheckDistance) { uint32 dist2 = 10000; // pow(100, 2); if (!spell) { diff --git a/zone/corpse.h b/zone/corpse.h index cd564e80d..80d3b4e8f 100644 --- a/zone/corpse.h +++ b/zone/corpse.h @@ -113,6 +113,10 @@ class Corpse : public Mob { /* Corpse: Loot */ void QueryLoot(Client* to); + bool HasItem(uint32 item_id); + uint16 CountItem(uint32 item_id); + uint32 GetItemIDBySlot(uint16 loot_slot); + uint16 GetFirstSlotByItemID(uint32 item_id); void LootItem(Client* client, const EQApplicationPacket* app); void EndLoot(Client* client, const EQApplicationPacket* app); void MakeLootRequestPackets(Client* client, const EQApplicationPacket* app); diff --git a/zone/lua_corpse.cpp b/zone/lua_corpse.cpp index 789555a54..85e4150f7 100644 --- a/zone/lua_corpse.cpp +++ b/zone/lua_corpse.cpp @@ -152,6 +152,26 @@ void Lua_Corpse::AddLooter(Lua_Mob who) { self->AddLooter(who); } +bool Lua_Corpse::HasItem(uint32 item_id) { + Lua_Safe_Call_Bool(); + return self->HasItem(item_id); +} + +uint16 Lua_Corpse::CountItem(uint32 item_id) { + Lua_Safe_Call_Int(); + return self->CountItem(item_id); +} + +uint32 Lua_Corpse::GetItemIDBySlot(uint16 loot_slot) { + Lua_Safe_Call_Int(); + return self->GetItemIDBySlot(loot_slot); +} + +uint16 Lua_Corpse::GetFirstSlotByItemID(uint32 item_id) { + Lua_Safe_Call_Int(); + return self->GetFirstSlotByItemID(item_id); +} + luabind::scope lua_register_corpse() { return luabind::class_("Corpse") .def(luabind::constructor<>()) @@ -185,7 +205,11 @@ luabind::scope lua_register_corpse() { .def("GetSilver", (uint32(Lua_Corpse::*)(void))&Lua_Corpse::GetSilver) .def("GetGold", (uint32(Lua_Corpse::*)(void))&Lua_Corpse::GetGold) .def("GetPlatinum", (uint32(Lua_Corpse::*)(void))&Lua_Corpse::GetPlatinum) - .def("AddLooter", (void(Lua_Corpse::*)(Lua_Mob))&Lua_Corpse::AddLooter); + .def("AddLooter", (void(Lua_Corpse::*)(Lua_Mob))&Lua_Corpse::AddLooter) + .def("HasItem", (bool(Lua_Corpse::*)(uint32))&Lua_Corpse::HasItem) + .def("CountItem", (uint16(Lua_Corpse::*)(uint32))&Lua_Corpse::CountItem) + .def("GetItemIDBySlot", (uint32(Lua_Corpse::*)(uint16))&Lua_Corpse::GetItemIDBySlot) + .def("GetFirstSlotByItemID", (uint16(Lua_Corpse::*)(uint32))&Lua_Corpse::GetFirstSlotByItemID); } #endif diff --git a/zone/lua_corpse.h b/zone/lua_corpse.h index dfb3b4b6d..679fe4948 100644 --- a/zone/lua_corpse.h +++ b/zone/lua_corpse.h @@ -54,6 +54,10 @@ public: uint32 GetGold(); uint32 GetPlatinum(); void AddLooter(Lua_Mob who); + bool HasItem(uint32 item_id); + uint16 CountItem(uint32 item_id); + uint32 GetItemIDBySlot(uint16 loot_slot); + uint16 GetFirstSlotByItemID(uint32 item_id); }; #endif diff --git a/zone/lua_npc.cpp b/zone/lua_npc.cpp index d7dbe06e8..af975b913 100644 --- a/zone/lua_npc.cpp +++ b/zone/lua_npc.cpp @@ -582,6 +582,30 @@ void Lua_NPC::ClearLastName() self->ClearLastName(); } +bool Lua_NPC::HasItem(uint32 item_id) +{ + Lua_Safe_Call_Bool(); + return self->HasItem(item_id); +} + +uint16 Lua_NPC::CountItem(uint32 item_id) +{ + Lua_Safe_Call_Int(); + return self->CountItem(item_id); +} + +uint32 Lua_NPC::GetItemIDBySlot(uint16 loot_slot) +{ + Lua_Safe_Call_Int(); + return self->GetItemIDBySlot(loot_slot); +} + +uint16 Lua_NPC::GetFirstSlotByItemID(uint32 item_id) +{ + Lua_Safe_Call_Int(); + return self->GetFirstSlotByItemID(item_id); +} + luabind::scope lua_register_npc() { return luabind::class_("NPC") .def(luabind::constructor<>()) @@ -698,7 +722,11 @@ luabind::scope lua_register_npc() { .def("ScaleNPC", (void(Lua_NPC::*)(uint8))&Lua_NPC::ScaleNPC) .def("IsRaidTarget", (bool(Lua_NPC::*)(void))&Lua_NPC::IsRaidTarget) .def("ChangeLastName", (void(Lua_NPC::*)(const char*))&Lua_NPC::ChangeLastName) - .def("ClearLastName", (void(Lua_NPC::*)(void))&Lua_NPC::ClearLastName); + .def("ClearLastName", (void(Lua_NPC::*)(void))&Lua_NPC::ClearLastName) + .def("HasItem", (bool(Lua_NPC::*)(uint32))&Lua_NPC::HasItem) + .def("CountItem", (uint16(Lua_NPC::*)(uint32))&Lua_NPC::CountItem) + .def("GetItemIDBySlot", (uint32(Lua_NPC::*)(uint16))&Lua_NPC::GetItemIDBySlot) + .def("GetFirstSlotByItemID", (uint16(Lua_NPC::*)(uint32))&Lua_NPC::GetFirstSlotByItemID); } #endif diff --git a/zone/lua_npc.h b/zone/lua_npc.h index 2860366ef..e4773636f 100644 --- a/zone/lua_npc.h +++ b/zone/lua_npc.h @@ -140,6 +140,10 @@ public: bool IsRaidTarget(); void ChangeLastName(const char *lastname); void ClearLastName(); + bool HasItem(uint32 item_id); + uint16 CountItem(uint32 item_id); + uint32 GetItemIDBySlot(uint16 slot_id); + uint16 GetFirstSlotByItemID(uint32 item_id); }; #endif diff --git a/zone/npc.cpp b/zone/npc.cpp index 3d65eed93..d7e4aa796 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -674,6 +674,75 @@ void NPC::QueryLoot(Client* to) to->Message(Chat::White, "| %i Platinum %i Gold %i Silver %i Copper", platinum, gold, silver, copper); } +bool NPC::HasItem(uint32 item_id) { + if (!database.GetItem(item_id)) { + return false; + } + + for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { + ServerLootItem_Struct* loot_item = *current_item; + if (!loot_item) { + LogError("NPC::CountItem() - ItemList error, null item"); + continue; + } + + if (!loot_item->item_id || !database.GetItem(loot_item->item_id)) { + LogError("NPC::CountItem() - Database error, invalid item"); + continue; + } + + if (loot_item->item_id == item_id) { + return true; + } + } + return false; +} + +uint16 NPC::CountItem(uint32 item_id) { + uint16 item_count = 0; + if (!database.GetItem(item_id)) { + return item_count; + } + + for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { + ServerLootItem_Struct* loot_item = *current_item; + if (!loot_item) { + LogError("NPC::CountItem() - ItemList error, null item"); + continue; + } + + if (!loot_item->item_id || !database.GetItem(loot_item->item_id)) { + LogError("NPC::CountItem() - Database error, invalid item"); + continue; + } + + if (loot_item->item_id == item_id) { + item_count += loot_item->charges; + } + } + return item_count; +} + +uint32 NPC::GetItemIDBySlot(uint16 loot_slot) { + for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { + ServerLootItem_Struct* loot_item = *current_item; + if (loot_item->lootslot == loot_slot) { + return loot_item->item_id; + } + } + return 0; +} + +uint16 NPC::GetFirstSlotByItemID(uint32 item_id) { + for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { + ServerLootItem_Struct* loot_item = *current_item; + if (loot_item->item_id == item_id) { + return loot_item->lootslot; + } + } + return 0; +} + void NPC::AddCash(uint16 in_copper, uint16 in_silver, uint16 in_gold, uint16 in_platinum) { if(in_copper >= 0) copper = in_copper; diff --git a/zone/npc.h b/zone/npc.h index f28394b9b..1521eb4de 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -204,6 +204,10 @@ public: void AddCash(); void RemoveCash(); void QueryLoot(Client* to); + bool HasItem(uint32 item_id); + uint16 CountItem(uint32 item_id); + uint32 GetItemIDBySlot(uint16 loot_slot); + uint16 GetFirstSlotByItemID(uint32 item_id); uint32 CountLoot(); inline uint32 GetLoottableID() const { return loottable_id; } virtual void UpdateEquipmentLight(); diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index 99cd83b86..2a5061bd0 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -1741,6 +1741,77 @@ XS(XS_NPC_IsRaidTarget) { XSRETURN(1); } +XS(XS_NPC_HasItem); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_HasItem) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::HasItem(THIS, uint32 item_id)"); // @categories Script Utility + { + NPC *THIS; + bool has_item = false; + uint32 item_id = (uint32) SvUV(ST(1)); + VALIDATE_THIS_IS_NPC; + has_item = THIS->HasItem(item_id); + ST(0) = boolSV(has_item); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_NPC_CountItem); +XS(XS_NPC_CountItem) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::CountItem(THIS, uint32 item_id)"); // @categories Script Utility + { + NPC *THIS; + uint16 item_count = 0; + uint32 item_id = (uint32) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_NPC; + item_count = THIS->CountItem(item_id); + XSprePUSH; + PUSHu((UV) item_count); + } + XSRETURN(1); +} + +XS(XS_NPC_GetItemIDBySlot); +XS(XS_NPC_GetItemIDBySlot) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::GetItemIDBySlot(THIS, uint16 loot_slot)"); // @categories Script Utility + { + NPC *THIS; + uint32 item_id = 0; + uint16 loot_slot = (uint16) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_NPC; + item_id = THIS->GetItemIDBySlot(loot_slot); + XSprePUSH; + PUSHu((UV) item_id); + } + XSRETURN(1); +} + +XS(XS_NPC_GetFirstSlotByItemID); +XS(XS_NPC_GetFirstSlotByItemID) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::GetFirstSlotByItemID(THIS, uint32 item_id)"); // @categories Script Utility + { + NPC *THIS; + uint16 loot_slot = 0; + uint32 item_id = (uint32) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_NPC; + loot_slot = THIS->GetFirstSlotByItemID(item_id); + XSprePUSH; + PUSHu((UV) loot_slot); + } + XSRETURN(1); +} + #ifdef __cplusplus extern "C" #endif @@ -1859,6 +1930,10 @@ XS(boot_NPC) { newXSproto(strcpy(buf, "RecalculateSkills"), XS_NPC_RecalculateSkills, file, "$"); newXSproto(strcpy(buf, "ScaleNPC"), XS_NPC_ScaleNPC, file, "$$"); newXSproto(strcpy(buf, "IsRaidTarget"), XS_NPC_IsRaidTarget, file, "$"); + newXSproto(strcpy(buf, "HasItem"), XS_NPC_HasItem, file, "$$"); + newXSproto(strcpy(buf, "CountItem"), XS_NPC_CountItem, file, "$$"); + newXSproto(strcpy(buf, "GetItemIDBySlot"), XS_NPC_GetItemIDBySlot, file, "$$"); + newXSproto(strcpy(buf, "GetFirstSlotByItemID"), XS_NPC_GetFirstSlotByItemID, file, "$$"); XSRETURN_YES; } diff --git a/zone/perl_player_corpse.cpp b/zone/perl_player_corpse.cpp index 8979d2102..dc6aaeb8b 100644 --- a/zone/perl_player_corpse.cpp +++ b/zone/perl_player_corpse.cpp @@ -528,6 +528,77 @@ XS(XS_Corpse_IsRezzed) { XSRETURN(1); } +XS(XS_Corpse_HasItem); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_HasItem) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Corpse::HasItem(THIS, uint32 item_id)"); // @categories Script Utility + { + Corpse *THIS; + bool has_item = false; + uint32 item_id = (uint32) SvUV(ST(1)); + VALIDATE_THIS_IS_CORPSE; + has_item = THIS->HasItem(item_id); + ST(0) = boolSV(has_item); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Corpse_CountItem); +XS(XS_Corpse_CountItem) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Corpse::CountItem(THIS, uint32 item_id)"); // @categories Script Utility + { + Corpse *THIS; + uint16 item_count = 0; + uint32 item_id = (uint32) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_CORPSE; + item_count = THIS->CountItem(item_id); + XSprePUSH; + PUSHu((UV) item_count); + } + XSRETURN(1); +} + +XS(XS_Corpse_GetItemIDBySlot); +XS(XS_Corpse_GetItemIDBySlot) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Corpse::GetItemIDBySlot(THIS, uint16 loot_slot)"); // @categories Script Utility + { + Corpse *THIS; + uint32 item_id = 0; + uint16 loot_slot = (uint16) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_CORPSE; + item_id = THIS->GetItemIDBySlot(loot_slot); + XSprePUSH; + PUSHu((UV) item_id); + } + XSRETURN(1); +} + +XS(XS_Corpse_GetFirstSlotByItemID); +XS(XS_Corpse_GetFirstSlotByItemID) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Corpse::GetFirstSlotByItemID(THIS, uint32 item_id)"); // @categories Script Utility + { + Corpse *THIS; + uint16 loot_slot = 0; + uint32 item_id = (uint32) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_CORPSE; + loot_slot = THIS->GetFirstSlotByItemID(item_id); + XSprePUSH; + PUSHu((UV) loot_slot); + } + XSRETURN(1); +} + #ifdef __cplusplus extern "C" #endif @@ -574,6 +645,10 @@ XS(boot_Corpse) { newXSproto(strcpy(buf, "AllowMobLoot"), XS_Corpse_AllowMobLoot, file, "$$$"); newXSproto(strcpy(buf, "AddLooter"), XS_Corpse_AddLooter, file, "$$"); newXSproto(strcpy(buf, "IsRezzed"), XS_Corpse_IsRezzed, file, "$"); + newXSproto(strcpy(buf, "HasItem"), XS_Corpse_HasItem, file, "$$"); + newXSproto(strcpy(buf, "CountItem"), XS_Corpse_CountItem, file, "$$"); + newXSproto(strcpy(buf, "GetItemIDBySlot"), XS_Corpse_GetItemIDBySlot, file, "$$"); + newXSproto(strcpy(buf, "GetFirstSlotByItemID"), XS_Corpse_GetFirstSlotByItemID, file, "$$"); XSRETURN_YES; } From 7b069dcf2036b54d107ab6ce701e400e37328b35 Mon Sep 17 00:00:00 2001 From: Dencelle Date: Tue, 31 Aug 2021 01:08:31 -0500 Subject: [PATCH 165/624] [Cheat Detection] Anti-Cheat reimplementation (#1434) * [Cheat Detection] Anti-Cheat reimplementation * minor patch fixes * ceiling to server side runspeed Warp(LT) was picking up a bunch of expected 6.2 but it was reported back as 6.5, this should help reduce the amount of false positives we get * use ceil instead of std::ceilf for linux * boat false positive fix * stopping the double detection * fixes and cleanup * auto merge tricked me... * dummy divide by 0 checks this should prevent anyone from setting Zone:MQWarpDetectionDistanceFactor to 0 and causing a crash. * Formatting * encapsulation to its own class and clean up * more detections * typo * OP_UnderWorld implmentation * Update client_packet.h * Syntax changes, formatting, cleanup * preventing crashes due to invalid packet size * typos and clearer logic * seperated the catagory for cheats * Updated MQGhost for more detail Co-authored-by: Akkadius --- common/emu_oplist.h | 1 + common/eq_constants.h | 1 + common/eq_packet_structs.h | 17 ++ common/eqemu_logsys.cpp | 1 + common/eqemu_logsys.h | 2 + common/eqemu_logsys_log_aliases.h | 10 + common/ruletypes.h | 15 ++ zone/CMakeLists.txt | 8 +- zone/cheat_manager.cpp | 386 ++++++++++++++++++++++++++++++ zone/cheat_manager.h | 88 +++++++ zone/client.cpp | 9 +- zone/client.h | 15 +- zone/client_packet.cpp | 44 +++- zone/client_packet.h | 2 + zone/client_process.cpp | 2 + zone/embparser.cpp | 8 + zone/event_codes.h | 1 + zone/lua_general.cpp | 1 + zone/lua_parser.cpp | 2 + zone/lua_parser_events.cpp | 12 + zone/lua_parser_events.h | 2 + zone/mob.cpp | 1 + zone/spells.cpp | 30 ++- zone/zone.cpp | 6 +- zone/zoning.cpp | 26 +- 25 files changed, 664 insertions(+), 26 deletions(-) create mode 100644 zone/cheat_manager.cpp create mode 100644 zone/cheat_manager.h diff --git a/common/emu_oplist.h b/common/emu_oplist.h index 46fd564e6..d81ed3a55 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -567,4 +567,5 @@ N(OP_ZoneServerReady), N(OP_ZoneSpawns), N(OP_ZoneUnavail), N(OP_ResetAA), +N(OP_UnderWorld), // mail and chat opcodes located in ../mail_oplist.h diff --git a/common/eq_constants.h b/common/eq_constants.h index 988ac7e60..fb6f0c557 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -65,6 +65,7 @@ #define AT_FindBits 46 // set FindBits, whatever those are! #define AT_TextureType 48 // TextureType #define AT_FacePick 49 // Turns off face pick window? maybe ... +#define AT_AntiCheat 51 // sent by the client randomly telling the server how long since last action has occured #define AT_GuildShow 52 // this is what MQ2 call sit, not sure #define AT_Offline 53 // Offline mode diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index ac08fe8d1..308ba22e9 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -5530,6 +5530,23 @@ struct SayLinkBodyFrame_Struct { /*056*/ }; +struct UpdateMovementEntry { + /* 00 */ float Y; + /* 04 */ float X; + /* 08 */ float Z; + /* 12 */ uint8 type; + /* 13 */ unsigned int timestamp; + /* 17 */ +}; + +struct UnderWorld { + /* 00 */ int spawn_id; + /* 04 */ float y; + /* 08 */ float x; + /* 12 */ float z; + /* 16 */ +}; + // Restore structure packing to default #pragma pack() diff --git a/common/eqemu_logsys.cpp b/common/eqemu_logsys.cpp index 950c17107..3666735e4 100644 --- a/common/eqemu_logsys.cpp +++ b/common/eqemu_logsys.cpp @@ -129,6 +129,7 @@ EQEmuLogSys *EQEmuLogSys::LoadLogSettingsDefaults() log_settings[Logs::HotReload].log_to_console = static_cast(Logs::General); log_settings[Logs::Loot].log_to_gmsay = static_cast(Logs::General); log_settings[Logs::Scheduler].log_to_console = static_cast(Logs::General); + log_settings[Logs::Cheat].log_to_console = static_cast(Logs::General); /** * RFC 5424 diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 4db6cedb2..6e881eecc 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -122,6 +122,7 @@ namespace Logs { Expeditions, DynamicZones, Scheduler, + Cheat, MaxCategoryID /* Don't Remove this */ }; @@ -202,6 +203,7 @@ namespace Logs { "Expeditions", "DynamicZones", "Scheduler", + "Cheat" }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index ddd27335f..b683d9111 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -646,6 +646,16 @@ OutF(LogSys, Logs::Detail, Logs::Scheduler, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogCheat(message, ...) do {\ + if (LogSys.log_settings[Logs::Cheat].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Cheat, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogCheatDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Cheat].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Cheat, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + #define Log(debug_level, log_category, message, ...) do {\ if (LogSys.log_settings[log_category].is_category_enabled == 1)\ LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ diff --git a/common/ruletypes.h b/common/ruletypes.h index a3ec71a47..a86764570 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -762,6 +762,21 @@ RULE_BOOL(DynamicZone, EnableInDynamicZoneStatus, false, "Enables the 'In Dynami RULE_INT(DynamicZone, WorldProcessRate, 6000, "Timer interval (milliseconds) that systems check their dynamic zone states") RULE_CATEGORY_END() +RULE_CATEGORY(Cheat) +RULE_REAL(Cheat, MQWarpDetectionDistanceFactor, 9.0, "clients move at 4.4 about if in a straight line but with movement and to acct for lag we raise it a bit") +RULE_INT(Cheat, MQWarpExemptStatus, -1, "Required status level to exempt the MQWarpDetector. Set to -1 to disable this feature.") +RULE_INT(Cheat, MQZoneExemptStatus, -1, "Required status level to exempt the MQZoneDetector. Set to -1 to disable this feature.") +RULE_INT(Cheat, MQGateExemptStatus, -1, "Required status level to exempt the MQGateDetector. Set to -1 to disable this feature.") +RULE_INT(Cheat, MQGhostExemptStatus, -1, "Required status level to exempt the MQGhostDetector. Set to -1 to disable this feature.") +RULE_INT(Cheat, MQFastMemExemptStatus, -1, "Required status level to exempt the MQFastMemDetector. Set to -1 to disable this feature.") +RULE_BOOL(Cheat, EnableMQWarpDetector, true, "Enable the MQWarp Detector. Set to False to disable this feature.") +RULE_BOOL(Cheat, EnableMQZoneDetector, true, "Enable the MQZone Detector. Set to False to disable this feature.") +RULE_BOOL(Cheat, EnableMQGateDetector, true, "Enable the MQGate Detector. Set to False to disable this feature.") +RULE_BOOL(Cheat, EnableMQGhostDetector, true, "Enable the MQGhost Detector. Set to False to disable this feature.") +RULE_BOOL(Cheat, EnableMQFastMemDetector, true, "Enable the MQFastMem Detector. Set to False to disable this feature.") +RULE_BOOL(Cheat, MarkMQWarpLT, false, "Mark clients makeing smaller warps") +RULE_CATEGORY_END() + #undef RULE_CATEGORY #undef RULE_INT #undef RULE_REAL diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 5f00e689a..554d3e717 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -14,6 +14,7 @@ SET(zone_sources bot_command.cpp bot_database.cpp botspellsai.cpp + cheat_manager.cpp client.cpp client_mods.cpp client_packet.cpp @@ -157,7 +158,8 @@ SET(zone_sources zone_event_scheduler.cpp zone_reload.cpp zone_store.cpp - zoning.cpp) + zoning.cpp +) SET(zone_headers aa.h @@ -171,6 +173,7 @@ SET(zone_headers bot_command.h bot_database.h bot_structs.h + cheat_manager.h client.h client_packet.h command.h @@ -274,7 +277,8 @@ SET(zone_headers zonedb.h zonedump.h zone_reload.h - zone_store.h) + zone_store.h +) ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers}) diff --git a/zone/cheat_manager.cpp b/zone/cheat_manager.cpp new file mode 100644 index 000000000..10269286d --- /dev/null +++ b/zone/cheat_manager.cpp @@ -0,0 +1,386 @@ +#include "cheat_manager.h" +#include "client.h" +#include "quest_parser_collection.h" + +void CheatManager::SetClient(Client *cli) +{ + m_target = cli; +} + +void CheatManager::SetExemptStatus(ExemptionType type, bool v) +{ + if (v == true) { + MovementCheck(); + } + m_exemption[type] = v; +} + +bool CheatManager::GetExemptStatus(ExemptionType type) +{ + return m_exemption[type]; +} + +void CheatManager::CheatDetected(CheatTypes type, glm::vec3 position1, glm::vec3 position2) +{ + switch (type) { + case MQWarp: + if (m_time_since_last_warp_detection.GetRemainingTime() == 0 && RuleB(Cheat, EnableMQWarpDetector) && + ((m_target->Admin() < RuleI(Cheat, MQWarpExemptStatus) || (RuleI(Cheat, MQWarpExemptStatus)) == -1))) { + std::string message = fmt::format( + "/MQWarp (large warp detection) with location from x [{:.2f}] y [{:.2f}] z [{:.2f}] to x [{:.2f}] y [{:.2f}] z [{:.2f}] Distance [{:.2f}]", + position1.x, + position1.y, + position1.z, + position2.x, + position2.y, + position2.z, + Distance(position1, position2) + ); + database.SetMQDetectionFlag( + m_target->AccountName(), + m_target->GetName(), + message.c_str(), + zone->GetShortName() + ); + LogCheat(message); + std::string export_string = fmt::format("{} {} {}", position1.x, position1.y, position1.z); + parse->EventPlayer(EVENT_WARP, m_target, export_string, 0); + } + break; + case MQWarpAbsolute: + if (RuleB(Cheat, EnableMQWarpDetector) && + ((m_target->Admin() < RuleI(Cheat, MQWarpExemptStatus) || (RuleI(Cheat, MQWarpExemptStatus)) == -1))) { + std::string message = fmt::format( + "/MQWarp (Absolute) with location from x [{:.2f}] y [{:.2f}] z [{:.2f}] to x [{:.2f}] y [{:.2f}] z [{:.2f}] Distance [{:.2f}]", + position1.x, + position1.y, + position1.z, + position2.x, + position2.y, + position2.z, + Distance(position1, position2) + ); + database.SetMQDetectionFlag( + m_target->AccountName(), + m_target->GetName(), + message.c_str(), + zone->GetShortName() + ); + LogCheat(message); + std::string export_string = fmt::format("{} {} {}", position1.x, position1.y, position1.z); + parse->EventPlayer(EVENT_WARP, m_target, export_string, 0); + m_time_since_last_warp_detection.Start(2500); + } + break; + case MQWarpShadowStep: + if (RuleB(Cheat, EnableMQWarpDetector) && + ((m_target->Admin() < RuleI(Cheat, MQWarpExemptStatus) || (RuleI(Cheat, MQWarpExemptStatus)) == -1))) { + std::string message = fmt::format( + "/MQWarp(ShadowStep) with location from x [{:.2f}] y [{:.2f}] z [{:.2f}] the target was shadow step exempt but we still found this suspicious.", + position1.x, + position1.y, + position1.z + ); + database.SetMQDetectionFlag( + m_target->AccountName(), + m_target->GetName(), + message.c_str(), + zone->GetShortName() + ); + LogCheat(message); + } + break; + case MQWarpKnockBack: + if (RuleB(Cheat, EnableMQWarpDetector) && + ((m_target->Admin() < RuleI(Cheat, MQWarpExemptStatus) || (RuleI(Cheat, MQWarpExemptStatus)) == -1))) { + std::string message = fmt::format( + "/MQWarp(Knockback) with location from x [{:.2f}] y [{:.2f}] z [{:.2f}] the target was Knock Back exempt but we still found this suspicious.", + position1.x, + position1.y, + position1.z + ); + database.SetMQDetectionFlag( + m_target->AccountName(), + m_target->GetName(), + message.c_str(), + zone->GetShortName() + ); + LogCheat(message); + } + break; + + case MQWarpLight: + if (RuleB(Cheat, EnableMQWarpDetector) && + ((m_target->Admin() < RuleI(Cheat, MQWarpExemptStatus) || (RuleI(Cheat, MQWarpExemptStatus)) == -1))) { + if (RuleB(Cheat, MarkMQWarpLT)) { + std::string message = fmt::format( + "/MQWarp(Knockback) with location from x [{:.2f}] y [{:.2f}] z [{:.2f}] running fast but not fast enough to get killed, possibly: small warp, speed hack, excessive lag, marked as suspicious.", + position1.x, + position1.y, + position1.z + ); + database.SetMQDetectionFlag( + m_target->AccountName(), + m_target->GetName(), + message.c_str(), + zone->GetShortName() + ); + LogCheat(message); + } + } + break; + + case MQZone: + if (RuleB(Cheat, EnableMQZoneDetector) && + ((m_target->Admin() < RuleI(Cheat, MQZoneExemptStatus) || (RuleI(Cheat, MQZoneExemptStatus)) == -1))) { + std::string message = fmt::format( + "/MQZone used at x [{:.2f}] y [{:.2f}] z [{:.2f}]", + position1.x, + position1.y, + position1.z + ); + database.SetMQDetectionFlag( + m_target->AccountName(), + m_target->GetName(), + message.c_str(), + zone->GetShortName() + ); + LogCheat(message); + } + break; + case MQZoneUnknownDest: + if (RuleB(Cheat, EnableMQZoneDetector) && + ((m_target->Admin() < RuleI(Cheat, MQZoneExemptStatus) || (RuleI(Cheat, MQZoneExemptStatus)) == -1))) { + std::string message = fmt::format( + "/MQZone used at x [{:.2f}] y [{:.2f}] z [{:.2f}] with Unknown Destination", + position1.x, + position1.y, + position1.z + ); + database.SetMQDetectionFlag( + m_target->AccountName(), + m_target->GetName(), + message.c_str(), + zone->GetShortName()); + LogCheat(message); + } + break; + case MQGate: + if (RuleB(Cheat, EnableMQGateDetector) && + ((m_target->Admin() < RuleI(Cheat, MQGateExemptStatus) || (RuleI(Cheat, MQGateExemptStatus)) == -1))) { + std::string message = fmt::format( + "/MQGate used at x [{:.2f}] y [{:.2f}] z [{:.2f}]", + position1.x, + position1.y, + position1.z + ); + database.SetMQDetectionFlag( + m_target->AccountName(), + m_target->GetName(), + message.c_str(), + zone->GetShortName() + ); + LogCheat(message); + } + break; + case MQGhost: + // this isn't just for ghost, its also for if a person isn't sending their MovementHistory packet also. + if (RuleB(Cheat, EnableMQGhostDetector) && + ((m_target->Admin() < RuleI(Cheat, MQGhostExemptStatus) || (RuleI(Cheat, MQGhostExemptStatus)) == -1))) { + database.SetMQDetectionFlag( + m_target->AccountName(), + m_target->GetName(), + "Packet blocking detected.", + zone->GetShortName()); + LogCheat("{} was caught not sending the proper packets as regularly as they were suppose to."); + } + break; + case MQFastMem: + if (RuleB(Cheat, EnableMQFastMemDetector) && + ((m_target->Admin() < RuleI(Cheat, MQFastMemExemptStatus) || + (RuleI(Cheat, MQFastMemExemptStatus)) == -1))) { + std::string message = fmt::format( + "/MQFastMem used at x [{:.2f}] y [{:.2f}] z [{:.2f}]", + position1.x, + position1.y, + position1.z + ); + database.SetMQDetectionFlag( + m_target->AccountName(), + m_target->GetName(), + message.c_str(), + zone->GetShortName() + ); + LogCheat(message); + } + break; + default: + std::string message = fmt::format( + "Unhandled HackerDetection flag with location from x [{:.2f}] y [{:.2f}] z [{:.2f}]", + position1.x, + position1.y, + position1.z + ); + database.SetMQDetectionFlag( + m_target->AccountName(), + m_target->GetName(), + message.c_str(), + zone->GetShortName() + ); + LogCheat(message); + break; + } +} + +void CheatManager::MovementCheck(glm::vec3 updated_position) +{ + if (m_time_since_last_movement_history.GetRemainingTime() == 0) { + CheatDetected(MQGhost, updated_position); + } + + float dist = DistanceNoZ(m_target->GetPosition(), updated_position); + uint32 cur_time = Timer::GetCurrentTime(); + if (dist == 0) { + if (m_distance_since_last_position_check > 0.0f) { + MovementCheck(0); + } + else { + m_time_since_last_position_check = cur_time; + m_cheat_detect_moved = false; + } + } + else { + m_distance_since_last_position_check += dist; + m_cheat_detect_moved = true; + if (m_time_since_last_position_check == 0) { + m_time_since_last_position_check = cur_time; + } + else { + MovementCheck(2500); + } + } +} + +void CheatManager::MovementCheck(uint32 time_between_checks) +{ + uint32 cur_time = Timer::GetCurrentTime(); + if ((cur_time - m_time_since_last_position_check) > time_between_checks) { + float estimated_speed = + (m_distance_since_last_position_check * 100) / (float) (cur_time - m_time_since_last_position_check); + float run_speed = m_target->GetRunspeed() / + std::min(RuleR(Cheat, MQWarpDetectionDistanceFactor), 1.0f); // MQWarpDetection shouldn't go below 1.0f so we can't end up dividing by 0. + if (estimated_speed > run_speed) { + bool using_gm_speed = m_target->GetGMSpeed(); + bool is_immobile = m_target->GetRunspeed() == 0; // this covers stuns, roots, mez, and pseudorooted. + if (!using_gm_speed && !is_immobile) { + if (GetExemptStatus(ShadowStep)) { + if (m_distance_since_last_position_check > 800) { + CheatDetected( + MQWarpShadowStep, + glm::vec3( + m_target->GetX(), + m_target->GetY(), + m_target->GetZ() + ) + ); + } + } + else if (GetExemptStatus(KnockBack)) { + if (estimated_speed > 30.0f) { + CheatDetected(MQWarpKnockBack, glm::vec3(m_target->GetX(), m_target->GetY(), m_target->GetZ())); + } + } + else if (!GetExemptStatus(Port)) { + if (estimated_speed > (run_speed * 1.5)) { + CheatDetected(MQWarp, glm::vec3(m_target->GetX(), m_target->GetY(), m_target->GetZ())); + m_time_since_last_position_check = cur_time; + m_distance_since_last_position_check = 0.0f; + } + else { + CheatDetected(MQWarpLight, glm::vec3(m_target->GetX(), m_target->GetY(), m_target->GetZ())); + } + } + } + } + if (time_between_checks != 1000) { + SetExemptStatus(ShadowStep, false); + SetExemptStatus(KnockBack, false); + SetExemptStatus(Port, false); + } + m_time_since_last_position_check = cur_time; + m_distance_since_last_position_check = 0.0f; + } +} + +void CheatManager::CheckMemTimer() +{ + if (m_target == nullptr) { + return; + } + if (m_time_since_last_memorization - Timer::GetCurrentTime() <= 1) { + glm::vec3 pos = m_target->GetPosition(); + CheatDetected(MQFastMem, pos); + } + m_time_since_last_memorization = Timer::GetCurrentTime(); +} + +void CheatManager::ProcessMovementHistory(const EQApplicationPacket *app) +{ + // if they haven't sent sent the packet within this time... they are probably spoofing... + // linux users reported that they don't send this packet at all but i can't prove they don't so i'm not sure if thats a fake or not. + m_time_since_last_movement_history.Start(70000); + if (GetExemptStatus(Port)) { + return; + } + auto *m_MovementHistory = (UpdateMovementEntry *) app->pBuffer; + if (app->size < sizeof(UpdateMovementEntry)) + { + LogDebug("Size mismatch in OP_MovementHistoryList, expected {}, got [{}]", sizeof(UpdateMovementEntry), app->size); + DumpPacket(app); + return; + } + + for (int index = 0; index < (app->size) / sizeof(UpdateMovementEntry); index++) { + glm::vec3 to = glm::vec3(m_MovementHistory[index].X, m_MovementHistory[index].Y, m_MovementHistory[index].Z); + switch (m_MovementHistory[index].type) { + case UpdateMovementType::ZoneLine: + SetExemptStatus(Port, true); + break; + case UpdateMovementType::TeleportA: + if (index != 0) { + glm::vec3 from = glm::vec3( + m_MovementHistory[index - 1].X, + m_MovementHistory[index - 1].Y, + m_MovementHistory[index - 1].Z + ); + CheatDetected(MQWarpAbsolute, from, to); + } + SetExemptStatus(Port, false); + break; + } + } +} + +void CheatManager::ProcessSpawnApperance(uint16 spawn_id, uint16 type, uint32 parameter) +{ + if (type == AT_Anim && parameter == ANIM_SIT) { + m_time_since_last_memorization = Timer::GetCurrentTime(); + } + else if (spawn_id == 0 && type == AT_AntiCheat) { + m_time_since_last_action = parameter; + } +} + +void CheatManager::ProcessItemVerifyRequest(int32 slot_id, uint32 target_id) +{ + if (slot_id == -1 && m_warp_counter != target_id) { + m_warp_counter = target_id; + } +} + +void CheatManager::ClientProcess() +{ + if (!m_cheat_detect_moved) { + m_time_since_last_position_check = Timer::GetCurrentTime(); + } +} diff --git a/zone/cheat_manager.h b/zone/cheat_manager.h new file mode 100644 index 000000000..43efe55af --- /dev/null +++ b/zone/cheat_manager.h @@ -0,0 +1,88 @@ +#ifndef ANTICHEAT_H +#define ANTICHEAT_H +class CheatManager; +class Client; + +#include "../common/timer.h" +#include "../common/rulesys.h" +#include +#include "../common/eq_packet_structs.h" +#include "../common/eq_packet.h" + +typedef enum { + Collision = 1, + TeleportB, + TeleportA, + ZoneLine, + Unknown0x5, + Unknown0x6, + SpellA, // Titanium - UF + Unknown0x8, + SpellB // Used in RoF+ +} UpdateMovementType; + +typedef enum { + ShadowStep, + KnockBack, + Port, + Assist, + Sense, + MAX_EXEMPTIONS +} ExemptionType; + +typedef enum { + MQWarp, + MQWarpShadowStep, + MQWarpKnockBack, + MQWarpLight, + MQZone, + MQZoneUnknownDest, + MQGate, + MQGhost, + MQFastMem, + MQWarpAbsolute +} CheatTypes; + +class CheatManager { +public: + CheatManager() + { + SetExemptStatus(ShadowStep, false); + SetExemptStatus(KnockBack, false); + SetExemptStatus(Port, false); + SetExemptStatus(Assist, false); + SetExemptStatus(Sense, false); + m_distance_since_last_position_check = 0.0f; + m_cheat_detect_moved = false; + m_target = nullptr; + m_time_since_last_memorization = 0; + m_time_since_last_position_check = 0; + m_time_since_last_warp_detection.Start(); + m_time_since_last_movement_history.Start(70000); + m_warp_counter = 0; + } + void SetClient(Client *cli); + void SetExemptStatus(ExemptionType type, bool v); + bool GetExemptStatus(ExemptionType type); + void CheatDetected(CheatTypes type, glm::vec3 position1, glm::vec3 position2 = glm::vec3(0, 0, 0)); + void MovementCheck(glm::vec3 updated_position); + void MovementCheck(uint32 time_between_checks = 1000); + void CheckMemTimer(); + void ProcessMovementHistory(const EQApplicationPacket *app); + void ProcessSpawnApperance(uint16 spawn_id, uint16 type, uint32 parameter); + void ProcessItemVerifyRequest(int32 slot_id, uint32 target_id); + void ClientProcess(); +private: + bool m_exemption[ExemptionType::MAX_EXEMPTIONS]{}; + float m_distance_since_last_position_check; + bool m_cheat_detect_moved; + + Client *m_target; + uint32 m_time_since_last_position_check; + uint32 m_time_since_last_memorization; + uint32 m_time_since_last_action{}; + Timer m_time_since_last_warp_detection; + Timer m_time_since_last_movement_history; + uint32 m_warp_counter; +}; +#endif ANTICHEAT_H diff --git a/zone/client.cpp b/zone/client.cpp index a90d424fa..f221c5fd9 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -61,6 +61,7 @@ extern volatile bool RunLoops; #include "mob_movement_manager.h" #include "../common/content/world_content_service.h" #include "../common/expedition_lockout_timer.h" +#include "cheat_manager.h" extern QueryServ* QServ; extern EntityList entity_list; @@ -177,7 +178,7 @@ Client::Client(EQStreamInterface* ieqs) for (int client_filter = 0; client_filter < _FilterCount; client_filter++) ClientFilters[client_filter] = FilterShow; - + cheat_manager.SetClient(this); mMovementManager->AddClient(this); character_id = 0; conn_state = NoPacketsReceived; @@ -10258,7 +10259,7 @@ void Client::ApplyWeaponsStance() - From spells, just remove the Primary buff that contains the WeaponStance effect in it. - For items with worn effect, unequip the item. - For AA abilities, a hotkey is used to Enable and Disable the effect. See. Client::TogglePassiveAlternativeAdvancement in aa.cpp for extensive details. - + Rank - Most important for AA, but if you have more than one of WeaponStance effect for a given type, the spell trigger buff will apply whatever has the highest 'rank' value from the spells table. AA's on live for this effect naturally do this. Be awere of this if making custom spells/worn effects/AA. @@ -10270,7 +10271,7 @@ void Client::ApplyWeaponsStance() if (!IsWeaponStanceEnabled()) { return; } - + bool enabled = false; bool item_bonus_exists = false; bool aa_bonus_exists = false; @@ -10326,7 +10327,7 @@ void Client::ApplyWeaponsStance() if (itembonuses.WeaponStance[WEAPON_STANCE_TYPE_2H] || itembonuses.WeaponStance[WEAPON_STANCE_TYPE_SHIELD] || itembonuses.WeaponStance[WEAPON_STANCE_TYPE_DUAL_WIELD]) { - + enabled = true; item_bonus_exists = true; diff --git a/zone/client.h b/zone/client.h index 004e905da..f9d0e724c 100644 --- a/zone/client.h +++ b/zone/client.h @@ -66,6 +66,7 @@ namespace EQ #include "zone_store.h" #include "task_manager.h" #include "task_client_state.h" +#include "cheat_manager.h" #ifdef _WINDOWS // since windows defines these within windef.h (which windows.h include) @@ -120,17 +121,6 @@ typedef enum { EvacToSafeCoords } ZoneMode; -typedef enum { - MQWarp, - MQWarpShadowStep, - MQWarpKnockBack, - MQWarpLight, - MQZone, - MQZoneUnknownDest, - MQGate, - MQGhost -} CheatTypes; - enum { HideCorpseNone = 0, HideCorpseAll = 1, @@ -604,7 +594,7 @@ public: inline double GetEXPModifier(uint32 zone_id) const { return database.GetEXPModifier(CharacterID(), zone_id); }; inline void SetAAEXPModifier(uint32 zone_id, double aa_modifier) { database.SetAAEXPModifier(CharacterID(), zone_id, aa_modifier); }; inline void SetEXPModifier(uint32 zone_id, double exp_modifier) { database.SetEXPModifier(CharacterID(), zone_id, exp_modifier); }; - + bool UpdateLDoNPoints(uint32 theme_id, int points); void SetPVPPoints(uint32 Points) { m_pp.PVPCurrentPoints = Points; } uint32 GetPVPPoints() { return m_pp.PVPCurrentPoints; } @@ -1598,6 +1588,7 @@ public: Raid *p_raid_instance; void ShowDevToolsMenu(); + CheatManager cheat_manager; protected: friend class Mob; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 64d45bb06..a1e3eb9c0 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -209,7 +209,7 @@ void MapOpcodes() 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_FloatListThing] = &Client::Handle_OP_MovementHistoryList; ConnectedOpcodes[OP_Forage] = &Client::Handle_OP_Forage; ConnectedOpcodes[OP_FriendsWho] = &Client::Handle_OP_FriendsWho; ConnectedOpcodes[OP_GetGuildMOTD] = &Client::Handle_OP_GetGuildMOTD; @@ -413,6 +413,7 @@ void MapOpcodes() ConnectedOpcodes[OP_YellForHelp] = &Client::Handle_OP_YellForHelp; ConnectedOpcodes[OP_ZoneChange] = &Client::Handle_OP_ZoneChange; ConnectedOpcodes[OP_ResetAA] = &Client::Handle_OP_ResetAA; + ConnectedOpcodes[OP_UnderWorld] = &Client::Handle_OP_UnderWorld; } void ClearMappedOpcode(EmuOpcode op) @@ -2922,6 +2923,7 @@ void Client::Handle_OP_Assist(const EQApplicationPacket *app) Mob *new_target = assistee->GetTarget(); if (new_target && (GetGM() || Distance(m_Position, assistee->GetPosition()) <= TARGETING_RANGE)) { + cheat_manager.SetExemptStatus(Assist, true); eid->entity_id = new_target->GetID(); } else { eid->entity_id = 0; @@ -4488,7 +4490,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { double cosine = std::cos(thetar); double sine = std::sin(thetar); - double normalizedx, normalizedy; + double normalizedx, normalizedy; normalizedx = cx * cosine - -cy * sine; normalizedy = -cx * sine + cy * cosine; @@ -4500,6 +4502,8 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { } } + cheat_manager.MovementCheck(glm::vec3(cx, cy, cz)); + if (IsDraggingCorpse()) DragCorpses(); @@ -8765,6 +8769,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) slot_id = request->slot; target_id = request->target; + cheat_manager.ProcessItemVerifyRequest(request->slot, request->target); EQApplicationPacket *outapp = nullptr; outapp = new EQApplicationPacket(OP_ItemVerifyReply, sizeof(ItemVerifyReply_Struct)); @@ -9614,6 +9619,7 @@ return; void Client::Handle_OP_MemorizeSpell(const EQApplicationPacket *app) { + cheat_manager.CheckMemTimer(); OPMemorizeSpell(app); return; } @@ -13465,6 +13471,8 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) } SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)app->pBuffer; + cheat_manager.ProcessSpawnApperance(sa->spawn_id, sa->type, sa->parameter); + if (sa->spawn_id != GetID()) return; @@ -13917,6 +13925,11 @@ void Client::Handle_OP_TargetCommand(const EQApplicationPacket *app) GetTarget()->IsTargeted(1); return; } + else if (cheat_manager.GetExemptStatus(Assist)) { + GetTarget()->IsTargeted(1); + cheat_manager.SetExemptStatus(Assist, false); + return; + } else if (GetTarget()->IsClient()) { //make sure this client is in our raid/group @@ -13932,6 +13945,15 @@ void Client::Handle_OP_TargetCommand(const EQApplicationPacket *app) SetTarget((Mob*)nullptr); return; } + else if (cheat_manager.GetExemptStatus(Port)) { + GetTarget()->IsTargeted(1); + return; + } + else if (cheat_manager.GetExemptStatus(Sense)) { + GetTarget()->IsTargeted(1); + cheat_manager.SetExemptStatus(Sense, false); + return; + } else if (IsXTarget(GetTarget())) { GetTarget()->IsTargeted(1); @@ -15173,3 +15195,21 @@ void Client::Handle_OP_ResetAA(const EQApplicationPacket *app) } return; } + +void Client::Handle_OP_MovementHistoryList(const EQApplicationPacket* app) { + cheat_manager.ProcessMovementHistory(app); +} + +void Client::Handle_OP_UnderWorld(const EQApplicationPacket* app) { + UnderWorld* m_UnderWorld = (UnderWorld*)app->pBuffer; + if (app->size != sizeof(UnderWorld)) + { + LogDebug("Size mismatch in OP_UnderWorld, expected {}, got [{}]", sizeof(UnderWorld), app->size); + DumpPacket(app); + return; + } + auto dist = Distance(glm::vec3(m_UnderWorld->x, m_UnderWorld->y, zone->newzone_data.underworld), glm::vec3(m_UnderWorld->x, m_UnderWorld->y, m_UnderWorld->z)); + cheat_manager.MovementCheck(glm::vec3(m_UnderWorld->x, m_UnderWorld->y, m_UnderWorld->z)); + if (m_UnderWorld->spawn_id == GetID() && dist <= 5.0f && zone->newzone_data.underworld_teleport_index != 0) + cheat_manager.SetExemptStatus(Port, true); +} diff --git a/zone/client_packet.h b/zone/client_packet.h index 3951a1761..e04d43483 100644 --- a/zone/client_packet.h +++ b/zone/client_packet.h @@ -313,3 +313,5 @@ void Handle_OP_YellForHelp(const EQApplicationPacket *app); void Handle_OP_ZoneChange(const EQApplicationPacket *app); void Handle_OP_ResetAA(const EQApplicationPacket *app); + void Handle_OP_MovementHistoryList(const EQApplicationPacket* app); + void Handle_OP_UnderWorld(const EQApplicationPacket* app); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 6106ee8ae..2739db30f 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -203,6 +203,8 @@ bool Client::Process() { if (IsStunned() && stunned_timer.Check()) Mob::UnStun(); + cheat_manager.ClientProcess(); + if (bardsong_timer.Check() && bardsong != 0) { //NOTE: this is kinda a heavy-handed check to make sure the mob still exists before //doing the next pulse on them... diff --git a/zone/embparser.cpp b/zone/embparser.cpp index 22e4b3234..f34208eff 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -120,6 +120,7 @@ const char *QuestEventSubroutines[_LargestEventID] = { "EVENT_USE_SKILL", "EVENT_COMBINE_VALIDATE", "EVENT_BOT_COMMAND", + "EVENT_WARP", "EVENT_TEST_BUFF" }; @@ -1636,6 +1637,13 @@ void PerlembParser::ExportEventVariables( ExportVar(package_name.c_str(), "langid", extradata); break; } + case EVENT_WARP: { + Seperator sep(data); + ExportVar(package_name.c_str(), "from_x", sep.arg[0]); + ExportVar(package_name.c_str(), "from_y", sep.arg[1]); + ExportVar(package_name.c_str(), "from_z", sep.arg[2]); + break; + } default: { break; diff --git a/zone/event_codes.h b/zone/event_codes.h index 5765c56ff..186b4cc80 100644 --- a/zone/event_codes.h +++ b/zone/event_codes.h @@ -88,6 +88,7 @@ typedef enum { EVENT_USE_SKILL, EVENT_COMBINE_VALIDATE, EVENT_BOT_COMMAND, + EVENT_WARP, EVENT_TEST_BUFF, _LargestEventID } QuestEventID; diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 29c3ee0d2..c1e72890f 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -3225,6 +3225,7 @@ luabind::scope lua_register_events() { luabind::value("spawn_zone", static_cast(EVENT_SPAWN_ZONE)), luabind::value("death_zone", static_cast(EVENT_DEATH_ZONE)), luabind::value("use_skill", static_cast(EVENT_USE_SKILL)), + luabind::value("warp", static_cast(EVENT_WARP)), luabind::value("test_buff", static_cast(EVENT_TEST_BUFF)) ]; } diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index 1342c719e..148230550 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -131,6 +131,7 @@ const char *LuaEvents[_LargestEventID] = { "event_use_skill", "event_combine_validate", "event_bot_command", + "event_warp", "event_test_buff" }; @@ -217,6 +218,7 @@ LuaParser::LuaParser() { PlayerArgumentDispatch[EVENT_TEST_BUFF] = handle_test_buff; PlayerArgumentDispatch[EVENT_COMBINE_VALIDATE] = handle_player_combine_validate; PlayerArgumentDispatch[EVENT_BOT_COMMAND] = handle_player_bot_command; + PlayerArgumentDispatch[EVENT_WARP] = handle_player_warp; ItemArgumentDispatch[EVENT_ITEM_CLICK] = handle_item_click; ItemArgumentDispatch[EVENT_ITEM_CLICK_CAST] = handle_item_click; diff --git a/zone/lua_parser_events.cpp b/zone/lua_parser_events.cpp index 73acd9232..f0940b761 100644 --- a/zone/lua_parser_events.cpp +++ b/zone/lua_parser_events.cpp @@ -561,6 +561,18 @@ void handle_player_bot_command(QuestInterface* parse, lua_State* L, Client* clie lua_setfield(L, -2, "args"); } +void handle_player_warp(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector* extra_pointers) { + Seperator sep(data.c_str()); + lua_pushnumber(L, std::stof(sep.arg[0])); + lua_setfield(L, -2, "from_x"); + + lua_pushnumber(L, std::stof(sep.arg[1])); + lua_setfield(L, -2, "from_y"); + + lua_pushnumber(L, std::stof(sep.arg[2])); + lua_setfield(L, -2, "from_z"); +} + //Item void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQ::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data, std::vector *extra_pointers) { diff --git a/zone/lua_parser_events.h b/zone/lua_parser_events.h index 47e7883ae..64dfdb5d9 100644 --- a/zone/lua_parser_events.h +++ b/zone/lua_parser_events.h @@ -103,6 +103,8 @@ void handle_player_combine_validate(QuestInterface* parse, lua_State* L, Client* std::vector* extra_pointers); void handle_player_bot_command(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector *extra_pointers); +void handle_player_warp(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, + std::vector* extra_pointers); //Item void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQ::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data, diff --git a/zone/mob.cpp b/zone/mob.cpp index 2bbb15c30..f749e89cc 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4657,6 +4657,7 @@ void Mob::DoKnockback(Mob *caster, uint32 pushback, uint32 pushup) { if(IsClient()) { + CastToClient()->cheat_manager.SetExemptStatus(KnockBack, true); auto outapp_push = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)outapp_push->pBuffer; diff --git a/zone/spells.cpp b/zone/spells.cpp index f5a756f79..567d239c6 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2743,6 +2743,22 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) { action->effect_flag = 4; + if (spells[spell_id].pushback != 0.0f || spells[spell_id].pushup != 0.0f) + { + if (IsClient()) + { + if (!IsBuffSpell(spell_id)) + { + CastToClient()->cheat_manager.SetExemptStatus(KnockBack, true); + } + } + } + + if (IsClient() && IsEffectInSpell(spell_id, SE_ShadowStep)) + { + CastToClient()->cheat_manager.SetExemptStatus(ShadowStep, true); + } + if(!IsEffectInSpell(spell_id, SE_BindAffinity)) { CastToClient()->QueuePacket(packet); @@ -4028,7 +4044,14 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r if(spells[spell_id].pushback != 0.0f || spells[spell_id].pushup != 0.0f) { - if (RuleB(Spells, NPCSpellPush) && !spelltar->IsRooted() && spelltar->ForcedMovement == 0) { + if (spelltar->IsClient()) + { + if (!IsBuffSpell(spell_id)) + { + spelltar->CastToClient()->cheat_manager.SetExemptStatus(KnockBack, true); + } + } + else if (RuleB(Spells, NPCSpellPush) && !spelltar->IsRooted() && spelltar->ForcedMovement == 0) { spelltar->m_Delta.x += action->force * g_Math.FastSin(action->hit_heading); spelltar->m_Delta.y += action->force * g_Math.FastCos(action->hit_heading); spelltar->m_Delta.z += action->hit_pitch; @@ -4036,6 +4059,11 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r } } + if (spelltar->IsClient() && IsEffectInSpell(spell_id, SE_ShadowStep)) + { + spelltar->CastToClient()->cheat_manager.SetExemptStatus(ShadowStep, true); + } + if(!IsEffectInSpell(spell_id, SE_BindAffinity)) { if(spelltar != this && spelltar->IsClient()) // send to target diff --git a/zone/zone.cpp b/zone/zone.cpp index 3dceb1250..aae61ad23 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -1911,7 +1911,11 @@ ZonePoint* Zone::GetClosestZonePoint(const glm::vec3& location, uint32 to, Clien // this shouldn't open up any exploits since those situations are detected later on if ((zone->HasWaterMap() && !zone->watermap->InZoneLine(glm::vec3(client->GetPosition()))) || (!zone->HasWaterMap() && closest_dist > 400.0f && closest_dist < max_distance2)) { - //TODO cheat detection + if (client) { + if (!client->cheat_manager.GetExemptStatus(Port)) { + client->cheat_manager.CheatDetected(MQZoneUnknownDest, location); + } + } LogInfo("WARNING: Closest zone point for zone id [{}] is [{}], you might need to update your zone_points table if you dont arrive at the right spot", to, closest_dist); LogInfo(". [{}]", to_string(location).c_str()); } diff --git a/zone/zoning.cpp b/zone/zoning.cpp index 0d088aadf..300e9d1bf 100644 --- a/zone/zoning.cpp +++ b/zone/zoning.cpp @@ -99,9 +99,14 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { //unable to find a zone point... is there anything else //that can be a valid un-zolicited zone request? - //Todo cheat detection Message(Chat::Red, "Invalid unsolicited zone request."); LogError("Zoning [{}]: Invalid unsolicited zone request to zone id [{}]", GetName(), target_zone_id); + if (GetBindZoneID() == target_zone_id) { + cheat_manager.CheatDetected(MQGate, glm::vec3(zc->x, zc->y, zc->z)); + } + else { + cheat_manager.CheatDetected(MQZone, glm::vec3(zc->x, zc->y, zc->z)); + } SendZoneCancel(zc); return; } @@ -134,7 +139,12 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { //then we assume this is invalid. if(!zone_point || zone_point->target_zone_id != target_zone_id) { LogError("Zoning [{}]: Invalid unsolicited zone request to zone id [{}]", GetName(), target_zone_id); - //todo cheat detection + if (GetBindZoneID() == target_zone_id) { + cheat_manager.CheatDetected(MQGate, glm::vec3(zc->x, zc->y, zc->z)); + } + else { + cheat_manager.CheatDetected(MQZone, glm::vec3(zc->x, zc->y, zc->z)); + } SendZoneCancel(zc); return; } @@ -282,7 +292,12 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { //for now, there are no other cases... //could not find a valid reason for them to be zoning, stop it. - //todo cheat detection + if (GetBindZoneID() == target_zone_id) { + cheat_manager.CheatDetected(MQGate, glm::vec3(zc->x, zc->y, zc->z)); + } + else { + cheat_manager.CheatDetected(MQZone, glm::vec3(zc->x, zc->y, zc->z)); + } LogError("Zoning [{}]: Invalid unsolicited zone request to zone id [{}]. Not near a zone point", GetName(), target_zone_name); SendZoneCancel(zc); return; @@ -379,6 +394,7 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { void Client::SendZoneCancel(ZoneChange_Struct *zc) { //effectively zone them right back to where they were //unless we find a better way to stop the zoning process. + cheat_manager.SetExemptStatus(Port, true); EQApplicationPacket *outapp = nullptr; outapp = new EQApplicationPacket(OP_ZoneChange, sizeof(ZoneChange_Struct)); ZoneChange_Struct *zc2 = (ZoneChange_Struct*)outapp->pBuffer; @@ -397,7 +413,7 @@ void Client::SendZoneCancel(ZoneChange_Struct *zc) { void Client::SendZoneError(ZoneChange_Struct *zc, int8 err) { LogError("Zone [{}] is not available because target wasn't found or character insufficent level", zc->zoneID); - + cheat_manager.SetExemptStatus(Port, true); EQApplicationPacket *outapp = nullptr; outapp = new EQApplicationPacket(OP_ZoneChange, sizeof(ZoneChange_Struct)); ZoneChange_Struct *zc2 = (ZoneChange_Struct*)outapp->pBuffer; @@ -667,6 +683,8 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z pShortZoneName = ZoneName(zoneID); content_db.GetZoneLongName(pShortZoneName, &pZoneName); + cheat_manager.SetExemptStatus(Port, true); + if(!pZoneName) { Message(Chat::Red, "Invalid zone number specified"); safe_delete_array(pZoneName); From 87a4f64ff04fdc4ad64b383c300a7289ef378faf Mon Sep 17 00:00:00 2001 From: Akkadius Date: Tue, 31 Aug 2021 01:24:21 -0500 Subject: [PATCH 166/624] [Compile Fix] Squelch warnings --- zone/cheat_manager.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zone/cheat_manager.h b/zone/cheat_manager.h index 43efe55af..78aa89ba6 100644 --- a/zone/cheat_manager.h +++ b/zone/cheat_manager.h @@ -85,4 +85,5 @@ private: Timer m_time_since_last_movement_history; uint32 m_warp_counter; }; -#endif ANTICHEAT_H + +#endif //ANTICHEAT_H From fec1b1538e4f94b8276d0de536e8198b9e964430 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 31 Aug 2021 02:31:56 -0400 Subject: [PATCH 167/624] [Quest API] Modify GetItemStat() and GetSpellStat() functionality. (#1509) - Added quest::getitemstat(item_id, stat_identifier) to Perl. - Added quest::getspellstat(spell_id, stat_identifier, slot) to Perl. - Added eq.get_item_stat(item_id, stat_identifier) to Lua. - Added eq.get_spell_stat(spell_id, stat_identifier, slot) to Lua. Wasn't sure where to put the GetItemStatValue() method so I put it in inventory profile, I can move it wherever is preferred. These additions will allow people to grab item and spell stat values in plugins without needing a mob object. --- common/inventory_profile.h | 1 + common/spdat.cpp | 113 ++++++++++ common/spdat.h | 1 + zone/embparser_api.cpp | 43 ++++ zone/inventory.cpp | 338 +++++++++++++++++++++++++++ zone/lua_general.cpp | 15 ++ zone/mob.cpp | 452 +------------------------------------ zone/questmgr.cpp | 8 + zone/questmgr.h | 2 + 9 files changed, 523 insertions(+), 450 deletions(-) diff --git a/common/inventory_profile.h b/common/inventory_profile.h index 2ff3d0565..654605aff 100644 --- a/common/inventory_profile.h +++ b/common/inventory_profile.h @@ -190,6 +190,7 @@ namespace EQ void SetCustomItemData(uint32 character_id, int16 slot_id, std::string identifier, float value); void SetCustomItemData(uint32 character_id, int16 slot_id, std::string identifier, bool value); std::string GetCustomItemData(int16 slot_id, std::string identifier); + static int GetItemStatValue(uint32 item_id, const char* identifier); protected: /////////////////////////////// // Protected Methods diff --git a/common/spdat.cpp b/common/spdat.cpp index 2b45a8300..a95088a71 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1357,3 +1357,116 @@ bool SpellRequiresTarget(int spell_id) } return true; } + +int GetSpellStatValue(uint32 spell_id, const char* stat_identifier, uint8 slot) +{ + if (!IsValidSpell(spell_id)) + return 0; + + if (!stat_identifier) + return 0; + + if (slot > 0) + slot -= 1; + + std::string id = stat_identifier; + for(uint32 i = 0; i < id.length(); ++i) { + id[i] = tolower(id[i]); + } + + if (slot < 16) { + if (id == "classes") { return spells[spell_id].classes[slot]; } + else if (id == "deities") { return spells[spell_id].deities[slot]; } + } + + if (slot < 12) { + if (id == "base") { return spells[spell_id].base[slot]; } + else if (id == "base2") { return spells[spell_id].base2[slot]; } + else if (id == "max") { return spells[spell_id].max[slot]; } + else if (id == "formula") { return spells[spell_id].formula[slot]; } + else if (id == "effectid") { return spells[spell_id].effectid[slot]; } + } + + if (slot < 4) { + if (id == "components") { return spells[spell_id].components[slot]; } + else if (id == "component_counts") { return spells[spell_id].component_counts[slot]; } + else if (id == "NoexpendReagent") { return spells[spell_id].NoexpendReagent[slot]; } + } + + if (id == "range") { return static_cast(spells[spell_id].range); } + else if (id == "aoerange") { return static_cast(spells[spell_id].aoerange); } + else if (id == "pushback") { return static_cast(spells[spell_id].pushback); } + else if (id == "pushup") { return static_cast(spells[spell_id].pushup); } + else if (id == "cast_time") { return spells[spell_id].cast_time; } + else if (id == "recovery_time") { return spells[spell_id].recovery_time; } + else if (id == "recast_time") { return spells[spell_id].recast_time; } + else if (id == "buffdurationformula") { return spells[spell_id].buffdurationformula; } + else if (id == "buffduration") { return spells[spell_id].buffduration; } + else if (id == "AEDuration") { return spells[spell_id].AEDuration; } + else if (id == "mana") { return spells[spell_id].mana; } + //else if (id == "LightType") {stat = spells[spell_id].LightType; } - Not implemented + else if (id == "goodEffect") { return spells[spell_id].goodEffect; } + else if (id == "Activated") { return spells[spell_id].Activated; } + else if (id == "resisttype") { return spells[spell_id].resisttype; } + else if (id == "targettype") { return spells[spell_id].targettype; } + else if (id == "basediff") { return spells[spell_id].basediff; } + else if (id == "skill") { return spells[spell_id].skill; } + else if (id == "zonetype") { return spells[spell_id].zonetype; } + else if (id == "EnvironmentType") { return spells[spell_id].EnvironmentType; } + else if (id == "TimeOfDay") { return spells[spell_id].TimeOfDay; } + else if (id == "CastingAnim") { return spells[spell_id].CastingAnim; } + else if (id == "SpellAffectIndex") { return spells[spell_id].SpellAffectIndex; } + else if (id == "disallow_sit") { return spells[spell_id].disallow_sit; } + //else if (id == "spellanim") {stat = spells[spell_id].spellanim; } - Not implemented + else if (id == "uninterruptable") { return spells[spell_id].uninterruptable; } + else if (id == "ResistDiff") { return spells[spell_id].ResistDiff; } + else if (id == "dot_stacking_exempt") { return spells[spell_id].dot_stacking_exempt; } + else if (id == "RecourseLink") { return spells[spell_id].RecourseLink; } + else if (id == "no_partial_resist") { return spells[spell_id].no_partial_resist; } + else if (id == "short_buff_box") { return spells[spell_id].short_buff_box; } + else if (id == "descnum") { return spells[spell_id].descnum; } + else if (id == "effectdescnum") { return spells[spell_id].effectdescnum; } + else if (id == "npc_no_los") { return spells[spell_id].npc_no_los; } + else if (id == "reflectable") { return spells[spell_id].reflectable; } + else if (id == "bonushate") { return spells[spell_id].bonushate; } + else if (id == "EndurCost") { return spells[spell_id].EndurCost; } + else if (id == "EndurTimerIndex") { return spells[spell_id].EndurTimerIndex; } + else if (id == "IsDisciplineBuff") { return spells[spell_id].IsDisciplineBuff; } + else if (id == "HateAdded") { return spells[spell_id].HateAdded; } + else if (id == "EndurUpkeep") { return spells[spell_id].EndurUpkeep; } + else if (id == "numhitstype") { return spells[spell_id].numhitstype; } + else if (id == "numhits") { return spells[spell_id].numhits; } + else if (id == "pvpresistbase") { return spells[spell_id].pvpresistbase; } + else if (id == "pvpresistcalc") { return spells[spell_id].pvpresistcalc; } + else if (id == "pvpresistcap") { return spells[spell_id].pvpresistcap; } + else if (id == "spell_category") { return spells[spell_id].spell_category; } + else if (id == "can_mgb") { return spells[spell_id].can_mgb; } + else if (id == "dispel_flag") { return spells[spell_id].dispel_flag; } + else if (id == "MinResist") { return spells[spell_id].MinResist; } + else if (id == "MaxResist") { return spells[spell_id].MaxResist; } + else if (id == "viral_targets") { return spells[spell_id].viral_targets; } + else if (id == "viral_timer") { return spells[spell_id].viral_timer; } + else if (id == "NimbusEffect") { return spells[spell_id].NimbusEffect; } + else if (id == "directional_start") { return static_cast(spells[spell_id].directional_start); } + else if (id == "directional_end") { return static_cast(spells[spell_id].directional_end); } + else if (id == "not_focusable") { return spells[spell_id].not_focusable; } + else if (id == "suspendable") { return spells[spell_id].suspendable; } + else if (id == "viral_range") { return spells[spell_id].viral_range; } + else if (id == "spellgroup") { return spells[spell_id].spellgroup; } + else if (id == "rank") { return spells[spell_id].rank; } + else if (id == "no_resist") { return spells[spell_id].no_resist; } + else if (id == "CastRestriction") { return spells[spell_id].CastRestriction; } + else if (id == "AllowRest") { return spells[spell_id].AllowRest; } + else if (id == "InCombat") { return spells[spell_id].InCombat; } + else if (id == "OutofCombat") { return spells[spell_id].OutofCombat; } + else if (id == "aemaxtargets") { return spells[spell_id].aemaxtargets; } + else if (id == "no_heal_damage_item_mod") { return spells[spell_id].no_heal_damage_item_mod; } + else if (id == "persistdeath") { return spells[spell_id].persistdeath; } + else if (id == "min_dist") { return static_cast(spells[spell_id].min_dist); } + else if (id == "min_dist_mod") { return static_cast(spells[spell_id].min_dist_mod); } + else if (id == "max_dist") { return static_cast(spells[spell_id].max_dist); } + else if (id == "min_range") { return static_cast(spells[spell_id].min_range); } + else if (id == "DamageShieldType") { return spells[spell_id].DamageShieldType; } + + return 0; +} \ No newline at end of file diff --git a/common/spdat.h b/common/spdat.h index a63df94cc..3688bfc6d 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -1495,5 +1495,6 @@ int32 GetFuriousBash(uint16 spell_id); bool IsShortDurationBuff(uint16 spell_id); bool IsSpellUsableThisZoneType(uint16 spell_id, uint8 zone_type); const char *GetSpellName(uint16 spell_id); +int GetSpellStatValue(uint32 spell_id, const char* stat_identifier, uint8 slot = 0); #endif diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 9792e72a2..89125d713 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -6839,6 +6839,47 @@ XS(XS__get_data_remaining) { XSRETURN(1); } +XS(XS__getitemstat); +XS(XS__getitemstat) { + dXSARGS; + int stat_value; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::getitemstat(uint32 item_id, string stat_identifier)"); + + dXSTARG; + uint32 item_id = (uint32) SvUV(ST(0)); + std::string stat_identifier = (std::string) SvPV_nolen(ST(1)); + stat_value = quest_manager.getitemstat(item_id, stat_identifier); + + XSprePUSH; + PUSHi((IV)stat_value); + + XSRETURN(1); +} + +XS(XS__getspellstat); +XS(XS__getspellstat) { + dXSARGS; + int stat_value; + if (items != 2 && items != 3) + Perl_croak(aTHX_ "Usage: quest::getspellstat(uint32 spell_id, string stat_identifier, uint8 slot)"); + + dXSTARG; + uint32 spell_id = (uint32) SvUV(ST(0)); + std::string stat_identifier = (std::string) SvPV_nolen(ST(1)); + uint8 slot = 0; + if (items == 3) + slot = (uint8) SvUV(ST(2)); + + stat_value = quest_manager.getspellstat(spell_id, stat_identifier, slot); + + XSprePUSH; + PUSHi((IV)stat_value); + + XSRETURN(1); +} + + /* This is the callback perl will look for to setup the quest package's XSUBs @@ -7077,6 +7118,7 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "getinventoryslotid"), XS__getinventoryslotid, file); newXS(strcpy(buf, "getitemname"), XS__getitemname, file); newXS(strcpy(buf, "getItemName"), XS_qc_getItemName, file); + newXS(strcpy(buf, "getitemstat"), XS__getitemstat, file); newXS(strcpy(buf, "getnpcnamebyid"), XS__getnpcnamebyid, file); newXS(strcpy(buf, "get_spawn_condition"), XS__get_spawn_condition, file); newXS(strcpy(buf, "getcharnamebyid"), XS__getcharnamebyid, file); @@ -7091,6 +7133,7 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "getracename"), XS__getracename, file); newXS(strcpy(buf, "getspellname"), XS__getspellname, file); newXS(strcpy(buf, "get_spell_level"), XS__get_spell_level, file); + newXS(strcpy(buf, "getspellstat"), XS__getspellstat, file); newXS(strcpy(buf, "getskillname"), XS__getskillname, file); newXS(strcpy(buf, "getlevel"), XS__getlevel, file); newXS(strcpy(buf, "getplayerburiedcorpsecount"), XS__getplayerburiedcorpsecount, file); diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 20f1989fb..0a805ad07 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -3653,3 +3653,341 @@ std::string EQ::InventoryProfile::GetCustomItemData(int16 slot_id, std::string i } return ""; } + +int EQ::InventoryProfile::GetItemStatValue(uint32 item_id, const char* identifier) { + const EQ::ItemInstance* inst = database.CreateItem(item_id); + if (!inst) + return 0; + + const EQ::ItemData* item = inst->GetItem(); + if (!item) + return 0; + + if (!identifier) + return 0; + + int32 stat = 0; + + std::string id = identifier; + for(uint32 i = 0; i < id.length(); ++i) { + id[i] = tolower(id[i]); + } + if (id == "itemclass") + stat = int32(item->ItemClass); + if (id == "id") + stat = int32(item->ID); + if (id == "idfile") + stat = atoi(&item->IDFile[2]); + if (id == "weight") + stat = int32(item->Weight); + if (id == "norent") + stat = int32(item->NoRent); + if (id == "nodrop") + stat = int32(item->NoDrop); + if (id == "size") + stat = int32(item->Size); + if (id == "slots") + stat = int32(item->Slots); + if (id == "price") + stat = int32(item->Price); + if (id == "icon") + stat = int32(item->Icon); + if (id == "loregroup") + stat = int32(item->LoreGroup); + if (id == "loreflag") + stat = int32(item->LoreFlag); + if (id == "pendingloreflag") + stat = int32(item->PendingLoreFlag); + if (id == "artifactflag") + stat = int32(item->ArtifactFlag); + if (id == "summonedflag") + stat = int32(item->SummonedFlag); + if (id == "fvnodrop") + stat = int32(item->FVNoDrop); + if (id == "favor") + stat = int32(item->Favor); + if (id == "guildfavor") + stat = int32(item->GuildFavor); + if (id == "pointtype") + stat = int32(item->PointType); + if (id == "bagtype") + stat = int32(item->BagType); + if (id == "bagslots") + stat = int32(item->BagSlots); + if (id == "bagsize") + stat = int32(item->BagSize); + if (id == "bagwr") + stat = int32(item->BagWR); + if (id == "benefitflag") + stat = int32(item->BenefitFlag); + if (id == "tradeskills") + stat = int32(item->Tradeskills); + if (id == "cr") + stat = int32(item->CR); + if (id == "dr") + stat = int32(item->DR); + if (id == "pr") + stat = int32(item->PR); + if (id == "mr") + stat = int32(item->MR); + if (id == "fr") + stat = int32(item->FR); + if (id == "astr") + stat = int32(item->AStr); + if (id == "asta") + stat = int32(item->ASta); + if (id == "aagi") + stat = int32(item->AAgi); + if (id == "adex") + stat = int32(item->ADex); + if (id == "acha") + stat = int32(item->ACha); + if (id == "aint") + stat = int32(item->AInt); + if (id == "awis") + stat = int32(item->AWis); + if (id == "hp") + stat = int32(item->HP); + if (id == "mana") + stat = int32(item->Mana); + if (id == "ac") + stat = int32(item->AC); + if (id == "deity") + stat = int32(item->Deity); + if (id == "skillmodvalue") + stat = int32(item->SkillModValue); + if (id == "skillmodtype") + stat = int32(item->SkillModType); + if (id == "banedmgrace") + stat = int32(item->BaneDmgRace); + if (id == "banedmgamt") + stat = int32(item->BaneDmgAmt); + if (id == "banedmgbody") + stat = int32(item->BaneDmgBody); + if (id == "magic") + stat = int32(item->Magic); + if (id == "casttime_") + stat = int32(item->CastTime_); + if (id == "reqlevel") + stat = int32(item->ReqLevel); + if (id == "bardtype") + stat = int32(item->BardType); + if (id == "bardvalue") + stat = int32(item->BardValue); + if (id == "light") + stat = int32(item->Light); + if (id == "delay") + stat = int32(item->Delay); + if (id == "reclevel") + stat = int32(item->RecLevel); + if (id == "recskill") + stat = int32(item->RecSkill); + if (id == "elemdmgtype") + stat = int32(item->ElemDmgType); + if (id == "elemdmgamt") + stat = int32(item->ElemDmgAmt); + if (id == "range") + stat = int32(item->Range); + if (id == "damage") + stat = int32(item->Damage); + if (id == "color") + stat = int32(item->Color); + if (id == "classes") + stat = int32(item->Classes); + if (id == "races") + stat = int32(item->Races); + if (id == "maxcharges") + stat = int32(item->MaxCharges); + if (id == "itemtype") + stat = int32(item->ItemType); + if (id == "material") + stat = int32(item->Material); + if (id == "casttime") + stat = int32(item->CastTime); + if (id == "elitematerial") + stat = int32(item->EliteMaterial); + if (id == "herosforgemodel") + stat = int32(item->HerosForgeModel); + if (id == "procrate") + stat = int32(item->ProcRate); + if (id == "combateffects") + stat = int32(item->CombatEffects); + if (id == "shielding") + stat = int32(item->Shielding); + if (id == "stunresist") + stat = int32(item->StunResist); + if (id == "strikethrough") + stat = int32(item->StrikeThrough); + if (id == "extradmgskill") + stat = int32(item->ExtraDmgSkill); + if (id == "extradmgamt") + stat = int32(item->ExtraDmgAmt); + if (id == "spellshield") + stat = int32(item->SpellShield); + if (id == "avoidance") + stat = int32(item->Avoidance); + if (id == "accuracy") + stat = int32(item->Accuracy); + if (id == "charmfileid") + stat = int32(item->CharmFileID); + if (id == "factionmod1") + stat = int32(item->FactionMod1); + if (id == "factionmod2") + stat = int32(item->FactionMod2); + if (id == "factionmod3") + stat = int32(item->FactionMod3); + if (id == "factionmod4") + stat = int32(item->FactionMod4); + if (id == "factionamt1") + stat = int32(item->FactionAmt1); + if (id == "factionamt2") + stat = int32(item->FactionAmt2); + if (id == "factionamt3") + stat = int32(item->FactionAmt3); + if (id == "factionamt4") + stat = int32(item->FactionAmt4); + if (id == "augtype") + stat = int32(item->AugType); + if (id == "ldontheme") + stat = int32(item->LDoNTheme); + if (id == "ldonprice") + stat = int32(item->LDoNPrice); + if (id == "ldonsold") + stat = int32(item->LDoNSold); + if (id == "banedmgraceamt") + stat = int32(item->BaneDmgRaceAmt); + if (id == "augrestrict") + stat = int32(item->AugRestrict); + if (id == "endur") + stat = int32(item->Endur); + if (id == "dotshielding") + stat = int32(item->DotShielding); + if (id == "attack") + stat = int32(item->Attack); + if (id == "regen") + stat = int32(item->Regen); + if (id == "manaregen") + stat = int32(item->ManaRegen); + if (id == "enduranceregen") + stat = int32(item->EnduranceRegen); + if (id == "haste") + stat = int32(item->Haste); + if (id == "damageshield") + stat = int32(item->DamageShield); + if (id == "recastdelay") + stat = int32(item->RecastDelay); + if (id == "recasttype") + stat = int32(item->RecastType); + if (id == "augdistiller") + stat = int32(item->AugDistiller); + if (id == "attuneable") + stat = int32(item->Attuneable); + if (id == "nopet") + stat = int32(item->NoPet); + if (id == "potionbelt") + stat = int32(item->PotionBelt); + if (id == "stackable") + stat = int32(item->Stackable); + if (id == "notransfer") + stat = int32(item->NoTransfer); + if (id == "questitemflag") + stat = int32(item->QuestItemFlag); + if (id == "stacksize") + stat = int32(item->StackSize); + if (id == "potionbeltslots") + stat = int32(item->PotionBeltSlots); + if (id == "book") + stat = int32(item->Book); + if (id == "booktype") + stat = int32(item->BookType); + if (id == "svcorruption") + stat = int32(item->SVCorruption); + if (id == "purity") + stat = int32(item->Purity); + if (id == "backstabdmg") + stat = int32(item->BackstabDmg); + if (id == "dsmitigation") + stat = int32(item->DSMitigation); + if (id == "heroicstr") + stat = int32(item->HeroicStr); + if (id == "heroicint") + stat = int32(item->HeroicInt); + if (id == "heroicwis") + stat = int32(item->HeroicWis); + if (id == "heroicagi") + stat = int32(item->HeroicAgi); + if (id == "heroicdex") + stat = int32(item->HeroicDex); + if (id == "heroicsta") + stat = int32(item->HeroicSta); + if (id == "heroiccha") + stat = int32(item->HeroicCha); + if (id == "heroicmr") + stat = int32(item->HeroicMR); + if (id == "heroicfr") + stat = int32(item->HeroicFR); + if (id == "heroiccr") + stat = int32(item->HeroicCR); + if (id == "heroicdr") + stat = int32(item->HeroicDR); + if (id == "heroicpr") + stat = int32(item->HeroicPR); + if (id == "heroicsvcorrup") + stat = int32(item->HeroicSVCorrup); + if (id == "healamt") + stat = int32(item->HealAmt); + if (id == "spelldmg") + stat = int32(item->SpellDmg); + if (id == "ldonsellbackrate") + stat = int32(item->LDoNSellBackRate); + if (id == "scriptfileid") + stat = int32(item->ScriptFileID); + if (id == "expendablearrow") + stat = int32(item->ExpendableArrow); + if (id == "clairvoyance") + stat = int32(item->Clairvoyance); + // Begin Effects + if (id == "clickeffect") + stat = int32(item->Click.Effect); + if (id == "clicktype") + stat = int32(item->Click.Type); + if (id == "clicklevel") + stat = int32(item->Click.Level); + if (id == "clicklevel2") + stat = int32(item->Click.Level2); + if (id == "proceffect") + stat = int32(item->Proc.Effect); + if (id == "proctype") + stat = int32(item->Proc.Type); + if (id == "proclevel") + stat = int32(item->Proc.Level); + if (id == "proclevel2") + stat = int32(item->Proc.Level2); + if (id == "worneffect") + stat = int32(item->Worn.Effect); + if (id == "worntype") + stat = int32(item->Worn.Type); + if (id == "wornlevel") + stat = int32(item->Worn.Level); + if (id == "wornlevel2") + stat = int32(item->Worn.Level2); + if (id == "focuseffect") + stat = int32(item->Focus.Effect); + if (id == "focustype") + stat = int32(item->Focus.Type); + if (id == "focuslevel") + stat = int32(item->Focus.Level); + if (id == "focuslevel2") + stat = int32(item->Focus.Level2); + if (id == "scrolleffect") + stat = int32(item->Scroll.Effect); + if (id == "scrolltype") + stat = int32(item->Scroll.Type); + if (id == "scrolllevel") + stat = int32(item->Scroll.Level); + if (id == "scrolllevel2") + stat = int32(item->Scroll.Level2); + + safe_delete(inst); + return stat; +} diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index c1e72890f..756907fd2 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -2479,6 +2479,18 @@ std::string lua_get_data_remaining(std::string bucket_name) { return DataBucket::GetDataRemaining(bucket_name); } +int lua_get_item_stat(uint32 item_id, std::string stat_identifier) { + return quest_manager.getitemstat(item_id, stat_identifier); +} + +int lua_get_spell_stat(uint32 spell_id, std::string stat_identifier) { + return quest_manager.getspellstat(spell_id, stat_identifier); +} + +int lua_get_spell_stat(uint32 spell_id, std::string stat_identifier, uint8 slot) { + return quest_manager.getspellstat(spell_id, stat_identifier, slot); +} + #define LuaCreateNPCParse(name, c_type, default_value) do { \ cur = table[#name]; \ if(luabind::type(cur) != LUA_TNIL) { \ @@ -3049,6 +3061,9 @@ luabind::scope lua_register_general() { luabind::def("get_inventory_slot_name", &lua_get_inventory_slot_name), luabind::def("rename", &lua_rename), luabind::def("get_data_remaining", &lua_get_data_remaining), + luabind::def("get_item_stat", &lua_get_item_stat), + luabind::def("get_spell_stat", (int(*)(uint32,std::string))&lua_get_spell_stat), + luabind::def("get_spell_stat", (int(*)(uint32,std::string,uint8))&lua_get_spell_stat), /** * Expansions diff --git a/zone/mob.cpp b/zone/mob.cpp index f749e89cc..0e7314951 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4060,343 +4060,7 @@ void Mob::TrySympatheticProc(Mob *target, uint32 spell_id) int32 Mob::GetItemStat(uint32 itemid, const char *identifier) { - const EQ::ItemInstance* inst = database.CreateItem(itemid); - if (!inst) - return 0; - - const EQ::ItemData* item = inst->GetItem(); - if (!item) - return 0; - - if (!identifier) - return 0; - - int32 stat = 0; - - std::string id = identifier; - for(uint32 i = 0; i < id.length(); ++i) - { - id[i] = tolower(id[i]); - } - - if (id == "itemclass") - stat = int32(item->ItemClass); - if (id == "id") - stat = int32(item->ID); - if (id == "idfile") - stat = atoi(&item->IDFile[2]); - if (id == "weight") - stat = int32(item->Weight); - if (id == "norent") - stat = int32(item->NoRent); - if (id == "nodrop") - stat = int32(item->NoDrop); - if (id == "size") - stat = int32(item->Size); - if (id == "slots") - stat = int32(item->Slots); - if (id == "price") - stat = int32(item->Price); - if (id == "icon") - stat = int32(item->Icon); - if (id == "loregroup") - stat = int32(item->LoreGroup); - if (id == "loreflag") - stat = int32(item->LoreFlag); - if (id == "pendingloreflag") - stat = int32(item->PendingLoreFlag); - if (id == "artifactflag") - stat = int32(item->ArtifactFlag); - if (id == "summonedflag") - stat = int32(item->SummonedFlag); - if (id == "fvnodrop") - stat = int32(item->FVNoDrop); - if (id == "favor") - stat = int32(item->Favor); - if (id == "guildfavor") - stat = int32(item->GuildFavor); - if (id == "pointtype") - stat = int32(item->PointType); - if (id == "bagtype") - stat = int32(item->BagType); - if (id == "bagslots") - stat = int32(item->BagSlots); - if (id == "bagsize") - stat = int32(item->BagSize); - if (id == "bagwr") - stat = int32(item->BagWR); - if (id == "benefitflag") - stat = int32(item->BenefitFlag); - if (id == "tradeskills") - stat = int32(item->Tradeskills); - if (id == "cr") - stat = int32(item->CR); - if (id == "dr") - stat = int32(item->DR); - if (id == "pr") - stat = int32(item->PR); - if (id == "mr") - stat = int32(item->MR); - if (id == "fr") - stat = int32(item->FR); - if (id == "astr") - stat = int32(item->AStr); - if (id == "asta") - stat = int32(item->ASta); - if (id == "aagi") - stat = int32(item->AAgi); - if (id == "adex") - stat = int32(item->ADex); - if (id == "acha") - stat = int32(item->ACha); - if (id == "aint") - stat = int32(item->AInt); - if (id == "awis") - stat = int32(item->AWis); - if (id == "hp") - stat = int32(item->HP); - if (id == "mana") - stat = int32(item->Mana); - if (id == "ac") - stat = int32(item->AC); - if (id == "deity") - stat = int32(item->Deity); - if (id == "skillmodvalue") - stat = int32(item->SkillModValue); - if (id == "skillmodtype") - stat = int32(item->SkillModType); - if (id == "banedmgrace") - stat = int32(item->BaneDmgRace); - if (id == "banedmgamt") - stat = int32(item->BaneDmgAmt); - if (id == "banedmgbody") - stat = int32(item->BaneDmgBody); - if (id == "magic") - stat = int32(item->Magic); - if (id == "casttime_") - stat = int32(item->CastTime_); - if (id == "reqlevel") - stat = int32(item->ReqLevel); - if (id == "bardtype") - stat = int32(item->BardType); - if (id == "bardvalue") - stat = int32(item->BardValue); - if (id == "light") - stat = int32(item->Light); - if (id == "delay") - stat = int32(item->Delay); - if (id == "reclevel") - stat = int32(item->RecLevel); - if (id == "recskill") - stat = int32(item->RecSkill); - if (id == "elemdmgtype") - stat = int32(item->ElemDmgType); - if (id == "elemdmgamt") - stat = int32(item->ElemDmgAmt); - if (id == "range") - stat = int32(item->Range); - if (id == "damage") - stat = int32(item->Damage); - if (id == "color") - stat = int32(item->Color); - if (id == "classes") - stat = int32(item->Classes); - if (id == "races") - stat = int32(item->Races); - if (id == "maxcharges") - stat = int32(item->MaxCharges); - if (id == "itemtype") - stat = int32(item->ItemType); - if (id == "material") - stat = int32(item->Material); - if (id == "casttime") - stat = int32(item->CastTime); - if (id == "elitematerial") - stat = int32(item->EliteMaterial); - if (id == "herosforgemodel") - stat = int32(item->HerosForgeModel); - if (id == "procrate") - stat = int32(item->ProcRate); - if (id == "combateffects") - stat = int32(item->CombatEffects); - if (id == "shielding") - stat = int32(item->Shielding); - if (id == "stunresist") - stat = int32(item->StunResist); - if (id == "strikethrough") - stat = int32(item->StrikeThrough); - if (id == "extradmgskill") - stat = int32(item->ExtraDmgSkill); - if (id == "extradmgamt") - stat = int32(item->ExtraDmgAmt); - if (id == "spellshield") - stat = int32(item->SpellShield); - if (id == "avoidance") - stat = int32(item->Avoidance); - if (id == "accuracy") - stat = int32(item->Accuracy); - if (id == "charmfileid") - stat = int32(item->CharmFileID); - if (id == "factionmod1") - stat = int32(item->FactionMod1); - if (id == "factionmod2") - stat = int32(item->FactionMod2); - if (id == "factionmod3") - stat = int32(item->FactionMod3); - if (id == "factionmod4") - stat = int32(item->FactionMod4); - if (id == "factionamt1") - stat = int32(item->FactionAmt1); - if (id == "factionamt2") - stat = int32(item->FactionAmt2); - if (id == "factionamt3") - stat = int32(item->FactionAmt3); - if (id == "factionamt4") - stat = int32(item->FactionAmt4); - if (id == "augtype") - stat = int32(item->AugType); - if (id == "ldontheme") - stat = int32(item->LDoNTheme); - if (id == "ldonprice") - stat = int32(item->LDoNPrice); - if (id == "ldonsold") - stat = int32(item->LDoNSold); - if (id == "banedmgraceamt") - stat = int32(item->BaneDmgRaceAmt); - if (id == "augrestrict") - stat = int32(item->AugRestrict); - if (id == "endur") - stat = int32(item->Endur); - if (id == "dotshielding") - stat = int32(item->DotShielding); - if (id == "attack") - stat = int32(item->Attack); - if (id == "regen") - stat = int32(item->Regen); - if (id == "manaregen") - stat = int32(item->ManaRegen); - if (id == "enduranceregen") - stat = int32(item->EnduranceRegen); - if (id == "haste") - stat = int32(item->Haste); - if (id == "damageshield") - stat = int32(item->DamageShield); - if (id == "recastdelay") - stat = int32(item->RecastDelay); - if (id == "recasttype") - stat = int32(item->RecastType); - if (id == "augdistiller") - stat = int32(item->AugDistiller); - if (id == "attuneable") - stat = int32(item->Attuneable); - if (id == "nopet") - stat = int32(item->NoPet); - if (id == "potionbelt") - stat = int32(item->PotionBelt); - if (id == "stackable") - stat = int32(item->Stackable); - if (id == "notransfer") - stat = int32(item->NoTransfer); - if (id == "questitemflag") - stat = int32(item->QuestItemFlag); - if (id == "stacksize") - stat = int32(item->StackSize); - if (id == "potionbeltslots") - stat = int32(item->PotionBeltSlots); - if (id == "book") - stat = int32(item->Book); - if (id == "booktype") - stat = int32(item->BookType); - if (id == "svcorruption") - stat = int32(item->SVCorruption); - if (id == "purity") - stat = int32(item->Purity); - if (id == "backstabdmg") - stat = int32(item->BackstabDmg); - if (id == "dsmitigation") - stat = int32(item->DSMitigation); - if (id == "heroicstr") - stat = int32(item->HeroicStr); - if (id == "heroicint") - stat = int32(item->HeroicInt); - if (id == "heroicwis") - stat = int32(item->HeroicWis); - if (id == "heroicagi") - stat = int32(item->HeroicAgi); - if (id == "heroicdex") - stat = int32(item->HeroicDex); - if (id == "heroicsta") - stat = int32(item->HeroicSta); - if (id == "heroiccha") - stat = int32(item->HeroicCha); - if (id == "heroicmr") - stat = int32(item->HeroicMR); - if (id == "heroicfr") - stat = int32(item->HeroicFR); - if (id == "heroiccr") - stat = int32(item->HeroicCR); - if (id == "heroicdr") - stat = int32(item->HeroicDR); - if (id == "heroicpr") - stat = int32(item->HeroicPR); - if (id == "heroicsvcorrup") - stat = int32(item->HeroicSVCorrup); - if (id == "healamt") - stat = int32(item->HealAmt); - if (id == "spelldmg") - stat = int32(item->SpellDmg); - if (id == "ldonsellbackrate") - stat = int32(item->LDoNSellBackRate); - if (id == "scriptfileid") - stat = int32(item->ScriptFileID); - if (id == "expendablearrow") - stat = int32(item->ExpendableArrow); - if (id == "clairvoyance") - stat = int32(item->Clairvoyance); - // Begin Effects - if (id == "clickeffect") - stat = int32(item->Click.Effect); - if (id == "clicktype") - stat = int32(item->Click.Type); - if (id == "clicklevel") - stat = int32(item->Click.Level); - if (id == "clicklevel2") - stat = int32(item->Click.Level2); - if (id == "proceffect") - stat = int32(item->Proc.Effect); - if (id == "proctype") - stat = int32(item->Proc.Type); - if (id == "proclevel") - stat = int32(item->Proc.Level); - if (id == "proclevel2") - stat = int32(item->Proc.Level2); - if (id == "worneffect") - stat = int32(item->Worn.Effect); - if (id == "worntype") - stat = int32(item->Worn.Type); - if (id == "wornlevel") - stat = int32(item->Worn.Level); - if (id == "wornlevel2") - stat = int32(item->Worn.Level2); - if (id == "focuseffect") - stat = int32(item->Focus.Effect); - if (id == "focustype") - stat = int32(item->Focus.Type); - if (id == "focuslevel") - stat = int32(item->Focus.Level); - if (id == "focuslevel2") - stat = int32(item->Focus.Level2); - if (id == "scrolleffect") - stat = int32(item->Scroll.Effect); - if (id == "scrolltype") - stat = int32(item->Scroll.Type); - if (id == "scrolllevel") - stat = int32(item->Scroll.Level); - if (id == "scrolllevel2") - stat = int32(item->Scroll.Level2); - - safe_delete(inst); - return stat; + return EQ::InventoryProfile::GetItemStatValue(itemid, identifier); } std::string Mob::GetGlobal(const char *varname) { @@ -5805,119 +5469,7 @@ bool Mob::GetSeeInvisible(uint8 see_invis) int32 Mob::GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot) { - if (!IsValidSpell(spell_id)) - return 0; - - if (!identifier) - return 0; - - int32 stat = 0; - - if (slot > 0) - slot = slot - 1; - - std::string id = identifier; - for(uint32 i = 0; i < id.length(); ++i) - { - id[i] = tolower(id[i]); - } - - if (slot < 16){ - if (id == "classes") {return spells[spell_id].classes[slot]; } - else if (id == "deities") {return spells[spell_id].deities[slot];} - } - - if (slot < 12){ - if (id == "base") {return spells[spell_id].base[slot];} - else if (id == "base2") {return spells[spell_id].base2[slot];} - else if (id == "max") {return spells[spell_id].max[slot];} - else if (id == "formula") {return spells[spell_id].formula[slot];} - else if (id == "effectid") {return spells[spell_id].effectid[slot];} - } - - if (slot < 4){ - if (id == "components") { return spells[spell_id].components[slot];} - else if (id == "component_counts") { return spells[spell_id].component_counts[slot];} - else if (id == "NoexpendReagent") {return spells[spell_id].NoexpendReagent[slot];} - } - - if (id == "range") {return static_cast(spells[spell_id].range); } - else if (id == "aoerange") {return static_cast(spells[spell_id].aoerange);} - else if (id == "pushback") {return static_cast(spells[spell_id].pushback);} - else if (id == "pushup") {return static_cast(spells[spell_id].pushup);} - else if (id == "cast_time") {return spells[spell_id].cast_time;} - else if (id == "recovery_time") {return spells[spell_id].recovery_time;} - else if (id == "recast_time") {return spells[spell_id].recast_time;} - else if (id == "buffdurationformula") {return spells[spell_id].buffdurationformula;} - else if (id == "buffduration") {return spells[spell_id].buffduration;} - else if (id == "AEDuration") {return spells[spell_id].AEDuration;} - else if (id == "mana") {return spells[spell_id].mana;} - //else if (id == "LightType") {stat = spells[spell_id].LightType;} - Not implemented - else if (id == "goodEffect") {return spells[spell_id].goodEffect;} - else if (id == "Activated") {return spells[spell_id].Activated;} - else if (id == "resisttype") {return spells[spell_id].resisttype;} - else if (id == "targettype") {return spells[spell_id].targettype;} - else if (id == "basediff") {return spells[spell_id].basediff;} - else if (id == "skill") {return spells[spell_id].skill;} - else if (id == "zonetype") {return spells[spell_id].zonetype;} - else if (id == "EnvironmentType") {return spells[spell_id].EnvironmentType;} - else if (id == "TimeOfDay") {return spells[spell_id].TimeOfDay;} - else if (id == "CastingAnim") {return spells[spell_id].CastingAnim;} - else if (id == "SpellAffectIndex") {return spells[spell_id].SpellAffectIndex; } - else if (id == "disallow_sit") {return spells[spell_id].disallow_sit; } - //else if (id == "spellanim") {stat = spells[spell_id].spellanim; } - Not implemented - else if (id == "uninterruptable") {return spells[spell_id].uninterruptable; } - else if (id == "ResistDiff") {return spells[spell_id].ResistDiff; } - else if (id == "dot_stacking_exempt") {return spells[spell_id].dot_stacking_exempt; } - else if (id == "RecourseLink") {return spells[spell_id].RecourseLink; } - else if (id == "no_partial_resist") {return spells[spell_id].no_partial_resist; } - else if (id == "short_buff_box") {return spells[spell_id].short_buff_box; } - else if (id == "descnum") {return spells[spell_id].descnum; } - else if (id == "effectdescnum") {return spells[spell_id].effectdescnum; } - else if (id == "npc_no_los") {return spells[spell_id].npc_no_los; } - else if (id == "reflectable") {return spells[spell_id].reflectable; } - else if (id == "bonushate") {return spells[spell_id].bonushate; } - else if (id == "EndurCost") {return spells[spell_id].EndurCost; } - else if (id == "EndurTimerIndex") {return spells[spell_id].EndurTimerIndex; } - else if (id == "IsDisciplineBuff") {return spells[spell_id].IsDisciplineBuff; } - else if (id == "HateAdded") {return spells[spell_id].HateAdded; } - else if (id == "EndurUpkeep") {return spells[spell_id].EndurUpkeep; } - else if (id == "numhitstype") {return spells[spell_id].numhitstype; } - else if (id == "numhits") {return spells[spell_id].numhits; } - else if (id == "pvpresistbase") {return spells[spell_id].pvpresistbase; } - else if (id == "pvpresistcalc") {return spells[spell_id].pvpresistcalc; } - else if (id == "pvpresistcap") {return spells[spell_id].pvpresistcap; } - else if (id == "spell_category") {return spells[spell_id].spell_category; } - else if (id == "can_mgb") {return spells[spell_id].can_mgb; } - else if (id == "dispel_flag") {return spells[spell_id].dispel_flag; } - else if (id == "MinResist") {return spells[spell_id].MinResist; } - else if (id == "MaxResist") {return spells[spell_id].MaxResist; } - else if (id == "viral_targets") {return spells[spell_id].viral_targets; } - else if (id == "viral_timer") {return spells[spell_id].viral_timer; } - else if (id == "NimbusEffect") {return spells[spell_id].NimbusEffect; } - else if (id == "directional_start") {return static_cast(spells[spell_id].directional_start); } - else if (id == "directional_end") {return static_cast(spells[spell_id].directional_end); } - else if (id == "not_focusable") {return spells[spell_id].not_focusable; } - else if (id == "suspendable") {return spells[spell_id].suspendable; } - else if (id == "viral_range") {return spells[spell_id].viral_range; } - else if (id == "spellgroup") {return spells[spell_id].spellgroup; } - else if (id == "rank") {return spells[spell_id].rank; } - else if (id == "no_resist") {return spells[spell_id].no_resist; } - else if (id == "CastRestriction") {return spells[spell_id].CastRestriction; } - else if (id == "caster_requirement_id") { return spells[spell_id].caster_requirement_id; } - else if (id == "AllowRest") {return spells[spell_id].AllowRest; } - else if (id == "InCombat") {return spells[spell_id].InCombat; } - else if (id == "OutofCombat") {return spells[spell_id].OutofCombat; } - else if (id == "aemaxtargets") {return spells[spell_id].aemaxtargets; } - else if (id == "no_heal_damage_item_mod") {return spells[spell_id].no_heal_damage_item_mod; } - else if (id == "persistdeath") {return spells[spell_id].persistdeath; } - else if (id == "min_dist") {return static_cast(spells[spell_id].min_dist); } - else if (id == "min_dist_mod") {return static_cast(spells[spell_id].min_dist_mod); } - else if (id == "max_dist") {return static_cast(spells[spell_id].max_dist); } - else if (id == "min_range") {return static_cast(spells[spell_id].min_range); } - else if (id == "DamageShieldType") {return spells[spell_id].DamageShieldType; } - - return stat; + return GetSpellStatValue(spell_id, identifier, slot); } bool Mob::CanClassEquipItem(uint32 item_id) diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index b0297a3c9..4a92b76f9 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -4646,4 +4646,12 @@ std::string QuestManager::getinventoryslotname(int16 slot_id) { return EQ::invslot::GetInvPossessionsSlotName(slot_id); } +int QuestManager::getitemstat(uint32 item_id, std::string stat_identifier) { + QuestManagerCurrentQuestVars(); + return EQ::InventoryProfile::GetItemStatValue(item_id, stat_identifier.c_str()); +} +int QuestManager::getspellstat(uint32 spell_id, std::string stat_identifier, uint8 slot) { + QuestManagerCurrentQuestVars(); + return GetSpellStatValue(spell_id, stat_identifier.c_str(), slot); +} diff --git a/zone/questmgr.h b/zone/questmgr.h index 094e2ceef..5d0c735ba 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -384,6 +384,8 @@ public: std::string getgendername(uint32 gender_id); std::string getdeityname(uint32 deity_id); std::string getinventoryslotname(int16 slot_id); + int getitemstat(uint32 item_id, std::string stat_identifier); + int getspellstat(uint32 spell_id, std::string stat_identifier, uint8 slot = 0); Client *GetInitiator() const; NPC *GetNPC() const; From 95258278810aa5312f58382512b987514c040832 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 31 Aug 2021 02:32:16 -0400 Subject: [PATCH 168/624] [Spells] Allow SE_SecondaryForte 248 to be calculated as a bonus instead of hardcoded AA (#1507) * start * Update client.cpp * Update client.cpp * Update bonuses.cpp * Update bonuses.cpp Co-authored-by: Chris Miles --- zone/bonuses.cpp | 9 +++++++-- zone/client.cpp | 4 ++-- zone/common.h | 1 + 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 68e35a9bf..d806704d3 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1658,6 +1658,13 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) newbon->ItemEnduranceRegenCap += base1; break; + + case SE_SecondaryForte: + if (newbon->SecondaryForte < base1) { + newbon->SecondaryForte = base1; + } + break; + case SE_ZoneSuspendMinion: newbon->ZoneSuspendMinion = base1; break; @@ -1669,8 +1676,6 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) break; case SE_BandolierSlots: break; - case SE_SecondaryForte: - break; case SE_ReduceApplyPoisonTime: break; case SE_NimbleEvasion: diff --git a/zone/client.cpp b/zone/client.cpp index f221c5fd9..7b499c62a 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -2503,8 +2503,8 @@ uint16 Client::GetMaxSkillAfterSpecializationRules(EQ::skills::SkillType skillid uint16 PrimarySpecialization = 0, SecondaryForte = 0; uint16 PrimarySkillValue = 0, SecondarySkillValue = 0; - - uint16 MaxSpecializations = GetAA(aaSecondaryForte) ? 2 : 1; + + uint16 MaxSpecializations = aabonuses.SecondaryForte ? 2 : 1; if (skillid >= EQ::skills::SkillSpecializeAbjure && skillid <= EQ::skills::SkillSpecializeEvocation) { diff --git a/zone/common.h b/zone/common.h index 0561ebb36..772441025 100644 --- a/zone/common.h +++ b/zone/common.h @@ -556,6 +556,7 @@ struct StatBonuses { bool ZoneSuspendMinion; // base 1 allows suspended minions to zone // AAs + uint16 SecondaryForte; // allow a second skill to be specialized with a cap of this value. int32 ShieldDuration; // extends duration of /shield ability int32 ExtendedShielding; // extends range of /shield ability int8 Packrat; // weight reduction for items, 1 point = 10% From 7f823256f46bbe0646a2365d2342d705c001d6f6 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Fri, 3 Sep 2021 12:59:17 -0500 Subject: [PATCH 169/624] [Hotfix] Fixing FMT Format Crash (#1516) --- zone/cheat_manager.cpp | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/zone/cheat_manager.cpp b/zone/cheat_manager.cpp index 10269286d..f0115e017 100644 --- a/zone/cheat_manager.cpp +++ b/zone/cheat_manager.cpp @@ -9,7 +9,7 @@ void CheatManager::SetClient(Client *cli) void CheatManager::SetExemptStatus(ExemptionType type, bool v) { - if (v == true) { + if (v) { MovementCheck(); } m_exemption[type] = v; @@ -76,7 +76,7 @@ void CheatManager::CheatDetected(CheatTypes type, glm::vec3 position1, glm::vec3 if (RuleB(Cheat, EnableMQWarpDetector) && ((m_target->Admin() < RuleI(Cheat, MQWarpExemptStatus) || (RuleI(Cheat, MQWarpExemptStatus)) == -1))) { std::string message = fmt::format( - "/MQWarp(ShadowStep) with location from x [{:.2f}] y [{:.2f}] z [{:.2f}] the target was shadow step exempt but we still found this suspicious.", + "/MQWarp (ShadowStep) with location from x [{:.2f}] y [{:.2f}] z [{:.2f}] the target was shadow step exempt but we still found this suspicious.", position1.x, position1.y, position1.z @@ -94,7 +94,7 @@ void CheatManager::CheatDetected(CheatTypes type, glm::vec3 position1, glm::vec3 if (RuleB(Cheat, EnableMQWarpDetector) && ((m_target->Admin() < RuleI(Cheat, MQWarpExemptStatus) || (RuleI(Cheat, MQWarpExemptStatus)) == -1))) { std::string message = fmt::format( - "/MQWarp(Knockback) with location from x [{:.2f}] y [{:.2f}] z [{:.2f}] the target was Knock Back exempt but we still found this suspicious.", + "/MQWarp (Knockback) with location from x [{:.2f}] y [{:.2f}] z [{:.2f}] the target was Knock Back exempt but we still found this suspicious.", position1.x, position1.y, position1.z @@ -161,7 +161,8 @@ void CheatManager::CheatDetected(CheatTypes type, glm::vec3 position1, glm::vec3 m_target->AccountName(), m_target->GetName(), message.c_str(), - zone->GetShortName()); + zone->GetShortName() + ); LogCheat(message); } break; @@ -186,13 +187,19 @@ void CheatManager::CheatDetected(CheatTypes type, glm::vec3 position1, glm::vec3 case MQGhost: // this isn't just for ghost, its also for if a person isn't sending their MovementHistory packet also. if (RuleB(Cheat, EnableMQGhostDetector) && - ((m_target->Admin() < RuleI(Cheat, MQGhostExemptStatus) || (RuleI(Cheat, MQGhostExemptStatus)) == -1))) { + ((m_target->Admin() < RuleI(Cheat, MQGhostExemptStatus) || + (RuleI(Cheat, MQGhostExemptStatus)) == -1))) { database.SetMQDetectionFlag( m_target->AccountName(), m_target->GetName(), "Packet blocking detected.", - zone->GetShortName()); - LogCheat("{} was caught not sending the proper packets as regularly as they were suppose to."); + zone->GetShortName() + ); + LogCheat( + "[MQGhost] [{}] [{}] was caught not sending the proper packets as regularly as they were suppose to.", + m_target->AccountName(), + m_target->GetName() + ); } break; case MQFastMem: @@ -267,11 +274,16 @@ void CheatManager::MovementCheck(uint32 time_between_checks) if ((cur_time - m_time_since_last_position_check) > time_between_checks) { float estimated_speed = (m_distance_since_last_position_check * 100) / (float) (cur_time - m_time_since_last_position_check); - float run_speed = m_target->GetRunspeed() / - std::min(RuleR(Cheat, MQWarpDetectionDistanceFactor), 1.0f); // MQWarpDetection shouldn't go below 1.0f so we can't end up dividing by 0. + + // MQWarpDetection shouldn't go below 1.0f so we can't end up dividing by 0. + float run_speed = m_target->GetRunspeed() / + std::min( + RuleR(Cheat, MQWarpDetectionDistanceFactor), + 1.0f + ); if (estimated_speed > run_speed) { bool using_gm_speed = m_target->GetGMSpeed(); - bool is_immobile = m_target->GetRunspeed() == 0; // this covers stuns, roots, mez, and pseudorooted. + bool is_immobile = m_target->GetRunspeed() == 0; // this covers stuns, roots, mez, and pseudorooted. if (!using_gm_speed && !is_immobile) { if (GetExemptStatus(ShadowStep)) { if (m_distance_since_last_position_check > 800) { @@ -333,9 +345,12 @@ void CheatManager::ProcessMovementHistory(const EQApplicationPacket *app) return; } auto *m_MovementHistory = (UpdateMovementEntry *) app->pBuffer; - if (app->size < sizeof(UpdateMovementEntry)) - { - LogDebug("Size mismatch in OP_MovementHistoryList, expected {}, got [{}]", sizeof(UpdateMovementEntry), app->size); + if (app->size < sizeof(UpdateMovementEntry)) { + LogDebug( + "Size mismatch in OP_MovementHistoryList, expected {}, got [{}]", + sizeof(UpdateMovementEntry), + app->size + ); DumpPacket(app); return; } From e1df72d64d985061c4d276ee08d0c57b893cec05 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Fri, 3 Sep 2021 19:47:25 -0500 Subject: [PATCH 170/624] [Hitpoints] Remove HP Update Throttling (#1517) * Remove HP update throttling and increase HP update resolution and accuracy * Make some log statements detail * Add better logging messages * Remove old self update throttle block check preventing updates to self --- zone/client.cpp | 4 +- zone/client.h | 2 - zone/client_process.cpp | 5 +- zone/mob.cpp | 110 ++++++++++++++++------------------------ 4 files changed, 48 insertions(+), 73 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 7b499c62a..499fc659f 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -169,8 +169,6 @@ Client::Client(EQStreamInterface* ieqs) last_region_type(RegionTypeUnsupported), m_dirtyautohaters(false), mob_close_scan_timer(6000), - hp_self_update_throttle_timer(300), - hp_other_update_throttle_timer(500), position_update_timer(10000), consent_throttle_timer(2000), tmSitting(0) @@ -2503,7 +2501,7 @@ uint16 Client::GetMaxSkillAfterSpecializationRules(EQ::skills::SkillType skillid uint16 PrimarySpecialization = 0, SecondaryForte = 0; uint16 PrimarySkillValue = 0, SecondarySkillValue = 0; - + uint16 MaxSpecializations = aabonuses.SecondaryForte ? 2 : 1; if (skillid >= EQ::skills::SkillSpecializeAbjure && skillid <= EQ::skills::SkillSpecializeEvocation) diff --git a/zone/client.h b/zone/client.h index f9d0e724c..11cbac385 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1818,8 +1818,6 @@ private: Timer helm_toggle_timer; Timer aggro_meter_timer; Timer mob_close_scan_timer; - Timer hp_self_update_throttle_timer; /* This is to prevent excessive packet sending under trains/fast combat */ - Timer hp_other_update_throttle_timer; /* This is to keep clients from DOSing the server with macros that change client targets constantly */ Timer position_update_timer; /* Timer used when client hasn't updated within a 10 second window */ Timer consent_throttle_timer; Timer dynamiczone_removal_timer; diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 2739db30f..428c734ab 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -119,8 +119,9 @@ bool Client::Process() { // SendHPUpdate calls hpupdate_timer.Start so it can delay this timer, so lets not reset with the check // since the function will anyways - if (hpupdate_timer.Check(false)) + if (hpupdate_timer.Check(false)) { SendHPUpdate(); + } /* I haven't naturally updated my position in 10 seconds, updating manually */ if (!is_client_moving && position_update_timer.Check()) { @@ -466,7 +467,7 @@ bool Client::Process() { if (gravity_timer.Check()) DoGravityEffect(); } - + if (shield_timer.Check()) { ShieldAbilityFinish(); } diff --git a/zone/mob.cpp b/zone/mob.cpp index 0e7314951..58ac71d21 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1355,82 +1355,72 @@ void Mob::CreateHPPacket(EQApplicationPacket* app) } } -void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= false*/) { +void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= false*/) +{ - /** - * If our HP is different from last HP update call - let's update selves - */ + // If our HP is different from last HP update call - let's update selves if (IsClient()) { - // delay to allow the client to catch up on buff states + // delay allowing the client to catch up on buff states if (max_hp != last_max_hp) { - last_max_hp = max_hp; - CastToClient()->hp_self_update_throttle_timer.Trigger(); - return; } if (current_hp != last_hp || force_update_all) { - /** - * This is to prevent excessive packet sending under trains/fast combat - */ - if (this->CastToClient()->hp_self_update_throttle_timer.Check() || force_update_all) { - Log(Logs::General, Logs::HPUpdate, - "Mob::SendHPUpdate :: Update HP of self (%s) HP: %i/%i last: %i/%i skip_self: %s", - this->GetCleanName(), - current_hp, - max_hp, - last_hp, - last_max_hp, - (skip_self ? "true" : "false") - ); + // This is to prevent excessive packet sending under trains/fast combat + LogHPUpdate( + "[SendHPUpdate] Update HP of self [{}] HP: [{}/{}] last: [{}/{}] skip_self: [{}]", + GetCleanName(), + current_hp, + max_hp, + last_hp, + last_max_hp, + (skip_self ? "true" : "false") + ); - if (!skip_self || this->CastToClient()->ClientVersion() >= EQ::versions::ClientVersion::SoD) { - auto client_packet = new EQApplicationPacket(OP_HPUpdate, sizeof(SpawnHPUpdate_Struct)); - auto *hp_packet_client = (SpawnHPUpdate_Struct *) client_packet->pBuffer; + if (!skip_self || this->CastToClient()->ClientVersion() >= EQ::versions::ClientVersion::SoD) { + auto client_packet = new EQApplicationPacket(OP_HPUpdate, sizeof(SpawnHPUpdate_Struct)); + auto *hp_packet_client = (SpawnHPUpdate_Struct *) client_packet->pBuffer; - hp_packet_client->cur_hp = static_cast(CastToClient()->GetHP() - itembonuses.HP); - hp_packet_client->spawn_id = GetID(); - hp_packet_client->max_hp = CastToClient()->GetMaxHP() - itembonuses.HP; + hp_packet_client->cur_hp = static_cast(CastToClient()->GetHP() - itembonuses.HP); + hp_packet_client->spawn_id = GetID(); + hp_packet_client->max_hp = CastToClient()->GetMaxHP() - itembonuses.HP; - CastToClient()->QueuePacket(client_packet); + CastToClient()->QueuePacket(client_packet); - safe_delete(client_packet); + safe_delete(client_packet); - ResetHPUpdateTimer(); - } - - /** - * Used to check if HP has changed to update self next round - */ - last_hp = current_hp; + ResetHPUpdateTimer(); } + + // Used to check if HP has changed to update self next round + last_hp = current_hp; } } auto current_hp_percent = GetIntHPRatio(); - Log(Logs::General, - Logs::HPUpdate, - "Mob::SendHPUpdate :: SendHPUpdate %s HP is %i last %i", - this->GetCleanName(), + LogHPUpdateDetail( + "[SendHPUpdate] Client [{}] HP is [{}] last [{}]", + GetCleanName(), current_hp_percent, - last_hp_percent); + last_hp_percent + ); if (current_hp_percent == last_hp_percent && !force_update_all) { - Log(Logs::General, Logs::HPUpdate, "Mob::SendHPUpdate :: Same HP - skipping update"); + LogHPUpdateDetail("[SendHPUpdate] Same HP for mob [{}] skipping update", GetCleanName()); ResetHPUpdateTimer(); return; } else { if (IsClient() && RuleB(Character, MarqueeHPUpdates)) { - this->CastToClient()->SendHPUpdateMarquee(); + CastToClient()->SendHPUpdateMarquee(); } - Log(Logs::General, Logs::HPUpdate, "Mob::SendHPUpdate :: HP Changed - Send update"); + LogHPUpdate("[SendHPUpdate] HP Changed for mob [{}] send update", GetCleanName()); last_hp_percent = current_hp_percent; } @@ -1440,24 +1430,16 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal CreateHPPacket(&hp_packet); - /** - * Update those who have us targeted - */ + // update those who have us targeted entity_list.QueueClientsByTarget(this, &hp_packet, false, 0, false, true, EQ::versions::maskAllClients); - /** - * Update those who have us on x-target - */ + // Update those who have us on x-target entity_list.QueueClientsByXTarget(this, &hp_packet, false); - /** - * Update groups using Group LAA health name tag counter - */ + // Update groups using Group LAA health name tag counter entity_list.QueueToGroupsForNPCHealthAA(this, &hp_packet); - /** - * Group - */ + // Group if (IsGrouped()) { group = entity_list.GetGroupByMob(this); if (group) { @@ -1465,9 +1447,7 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal } } - /** - * Raid - */ + // Raid if (IsClient()) { Raid *raid = entity_list.GetRaidByClient(CastToClient()); if (raid) { @@ -1475,9 +1455,7 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal } } - /** - * Pet - */ + // Pet if (GetOwner() && GetOwner()->IsClient()) { GetOwner()->CastToClient()->QueuePacket(&hp_packet, false); group = entity_list.GetGroupByClient(GetOwner()->CastToClient()); @@ -3282,8 +3260,8 @@ void Mob::SetTarget(Mob *mob) else if (IsClient()) { parse->EventPlayer(EVENT_TARGET_CHANGE, CastToClient(), "", 0); - if (this->CastToClient()->admin > 200) { - this->DisplayInfo(mob); + if (CastToClient()->admin > 200) { + DisplayInfo(mob); } #ifdef BOTS @@ -3295,8 +3273,8 @@ void Mob::SetTarget(Mob *mob) GetOwner()->CastToClient()->UpdateXTargetType(MyPetTarget, mob); } - if (this->IsClient() && this->GetTarget() && this->CastToClient()->hp_other_update_throttle_timer.Check()) { - this->GetTarget()->SendHPUpdate(false, true); + if (IsClient() && GetTarget()) { + GetTarget()->SendHPUpdate(false, true); } } From 119018cf41e9bfa0a1ecc48c3599b02f725545d0 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 3 Sep 2021 20:47:33 -0400 Subject: [PATCH 171/624] [Quest API] Add GetHealScale() and GetSpellScale() to Perl and Lua. (#1515) --- zone/lua_npc.cpp | 16 +++++++++++++++- zone/lua_npc.h | 2 ++ zone/perl_npc.cpp | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/zone/lua_npc.cpp b/zone/lua_npc.cpp index af975b913..c8bc012c9 100644 --- a/zone/lua_npc.cpp +++ b/zone/lua_npc.cpp @@ -606,6 +606,18 @@ uint16 Lua_NPC::GetFirstSlotByItemID(uint32 item_id) return self->GetFirstSlotByItemID(item_id); } +float Lua_NPC::GetHealScale() +{ + Lua_Safe_Call_Real(); + return self->GetHealScale(); +} + +float Lua_NPC::GetSpellScale() +{ + Lua_Safe_Call_Real(); + return self->GetSpellScale(); +} + luabind::scope lua_register_npc() { return luabind::class_("NPC") .def(luabind::constructor<>()) @@ -726,7 +738,9 @@ luabind::scope lua_register_npc() { .def("HasItem", (bool(Lua_NPC::*)(uint32))&Lua_NPC::HasItem) .def("CountItem", (uint16(Lua_NPC::*)(uint32))&Lua_NPC::CountItem) .def("GetItemIDBySlot", (uint32(Lua_NPC::*)(uint16))&Lua_NPC::GetItemIDBySlot) - .def("GetFirstSlotByItemID", (uint16(Lua_NPC::*)(uint32))&Lua_NPC::GetFirstSlotByItemID); + .def("GetFirstSlotByItemID", (uint16(Lua_NPC::*)(uint32))&Lua_NPC::GetFirstSlotByItemID) + .def("GetHealScale", (float(Lua_NPC::*)(void))&Lua_NPC::GetHealScale) + .def("GetSpellScale", (float(Lua_NPC::*)(void))&Lua_NPC::GetSpellScale); } #endif diff --git a/zone/lua_npc.h b/zone/lua_npc.h index e4773636f..1577a1e20 100644 --- a/zone/lua_npc.h +++ b/zone/lua_npc.h @@ -144,6 +144,8 @@ public: uint16 CountItem(uint32 item_id); uint32 GetItemIDBySlot(uint16 slot_id); uint16 GetFirstSlotByItemID(uint32 item_id); + float GetHealScale(); + float GetSpellScale(); }; #endif diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index 2a5061bd0..6496e5863 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -1812,6 +1812,40 @@ XS(XS_NPC_GetFirstSlotByItemID) { XSRETURN(1); } +XS(XS_NPC_GetHealScale); +XS(XS_NPC_GetHealScale) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetHealScale(THIS)"); // @categories Stats and Attributes + { + NPC *THIS; + float healscale; + dXSTARG; + VALIDATE_THIS_IS_NPC; + healscale = THIS->GetHealScale(); + XSprePUSH; + PUSHn((double) healscale); + } + XSRETURN(1); +} + +XS(XS_NPC_GetSpellScale); +XS(XS_NPC_GetSpellScale) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetSpellScale(THIS)"); // @categories Stats and Attributes + { + NPC *THIS; + float spellscale; + dXSTARG; + VALIDATE_THIS_IS_NPC; + spellscale = THIS->GetSpellScale(); + XSprePUSH; + PUSHn((double) spellscale); + } + XSRETURN(1); +} + #ifdef __cplusplus extern "C" #endif @@ -1934,6 +1968,8 @@ XS(boot_NPC) { newXSproto(strcpy(buf, "CountItem"), XS_NPC_CountItem, file, "$$"); newXSproto(strcpy(buf, "GetItemIDBySlot"), XS_NPC_GetItemIDBySlot, file, "$$"); newXSproto(strcpy(buf, "GetFirstSlotByItemID"), XS_NPC_GetFirstSlotByItemID, file, "$$"); + newXSproto(strcpy(buf, "GetHealScale"), XS_NPC_GetHealScale, file, "$"); + newXSproto(strcpy(buf, "GetSpellScale"), XS_NPC_GetSpellScale, file, "$"); XSRETURN_YES; } From 59434e01016c0bb15822020577bb91363464cc10 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 3 Sep 2021 21:15:07 -0400 Subject: [PATCH 172/624] [Spells] Updated SE_NegateSpellEffect SPA 382, new functionality (#1514) * updated SPA 382 Updated SE_NegateSpellEffect SPA 382 Now uses spell values base1 which allows you to limit which bonuses are negated. * Update spdat.h * minor update * Update bonuses.cpp reset bool correctly * Update bonuses.cpp * Update bonuses.cpp --- common/spdat.h | 13 +- zone/bonuses.cpp | 1320 ++++++++++++++++++++++++---------------------- zone/mob.h | 2 +- 3 files changed, 691 insertions(+), 644 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 3688bfc6d..1834b4a29 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -499,7 +499,16 @@ enum SpellRestriction UNKNOWN_99999 = 99999, // | caster restriction | works will spell 27672 Strike of Ire }; - +enum NegateSpellEffectType +{ + NEGATE_SPA_ALL_BONUSES = 0, + NEGATE_SPA_SPELLBONUS = 1, + NEGATE_SPA_ITEMBONUS = 2, + NEGATE_SPA_SPELLBONUS_AND_ITEMBONUS = 3, + NEGATE_SPA_AABONUS = 4, + NEGATE_SPA_SPELLBONUS_AND_AABONUS = 5, + NEGATE_SPA_ITEMBONUS_AND_AABONUS = 6, +}; enum SpellTypes : uint32 { @@ -1056,7 +1065,7 @@ typedef enum { #define SE_ShadowStepDirectional 379 // implemented - handled by client #define SE_Knockdown 380 // implemented - small knock back(handled by client) //#define SE_KnockTowardCaster 381 // *not implemented (Call of Hither) knocks you back to caster (value) distance units infront -#define SE_NegateSpellEffect 382 // implemented - negates specific spell bonuses for duration of the debuff. +#define SE_NegateSpellEffect 382 // implemented, @Debuff, negates specific spell effect benefits for the duration of the debuff, base: see NegateSpellEffecttype Enum, limit: SPA id, max: none #define SE_SympatheticProc 383 // implemented, @Fc, On Caster, cast on spell use, base: variable proc chance on cast time, limit: spellid #define SE_Leap 384 // implemented - Leap effect, ie stomping leap #define SE_LimitSpellGroup 385 // implemented, @Ff, Spell group(s) that a spell focus can require or exclude, base1: spellgroup id, Include: Positive Exclude: Negative diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index d806704d3..19ae9aac1 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1743,11 +1743,11 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon) if (IsNPC()) CastToNPC()->ApplyAISpellEffects(newbon); - //Removes the spell bonuses that are effected by a 'negate' debuff. + //Disables a specific spell effect bonus completely, can also be limited to negate only item, AA or spell bonuses. if (spellbonuses.NegateEffects){ for(i = 0; i < buff_count; i++) { if( (buffs[i].spellid != SPELL_UNKNOWN) && (IsEffectInSpell(buffs[i].spellid, SE_NegateSpellEffect)) ) - NegateSpellsBonuses(buffs[i].spellid); + NegateSpellEffectBonuses(buffs[i].spellid); } } @@ -4041,23 +4041,64 @@ uint8 Mob::IsFocusEffect(uint16 spell_id,int effect_index, bool AA,uint32 aa_eff return 0; } -void Mob::NegateSpellsBonuses(uint16 spell_id) +void Mob::NegateSpellEffectBonuses(uint16 spell_id) { - if(!IsValidSpell(spell_id)) + if (!IsValidSpell(spell_id)) return; int effect_value = 0; for (int i = 0; i < EFFECT_COUNT; i++) { - if (spells[spell_id].effectid[i] == SE_NegateSpellEffect){ + bool negate_spellbonus = false; + bool negate_aabonus = false; + bool negate_itembonus = false; + + if (spells[spell_id].effectid[i] == SE_NegateSpellEffect) { + + //Set negate types + switch (spells[spell_id].base[i]) + { + case NEGATE_SPA_ALL_BONUSES: + negate_spellbonus = true; + negate_aabonus = true; + negate_itembonus = true; + break; + + case NEGATE_SPA_SPELLBONUS: + negate_spellbonus = true; + break; + + case NEGATE_SPA_ITEMBONUS: + negate_itembonus = true; + break; + + case NEGATE_SPA_AABONUS: + negate_aabonus = true; + break; + + case NEGATE_SPA_ITEMBONUS_AND_AABONUS: + negate_aabonus = true; + negate_itembonus = true; + break; + + case NEGATE_SPA_SPELLBONUS_AND_AABONUS: + negate_aabonus = true; + negate_spellbonus = true; + break; + + case NEGATE_SPA_SPELLBONUS_AND_ITEMBONUS: + negate_spellbonus = true; + negate_itembonus = true; + break; + } //Negate focus effects - for(int e = 0; e < HIGHEST_FOCUS+1; e++) + for (int e = 0; e < HIGHEST_FOCUS + 1; e++) { if (spellbonuses.FocusEffects[e] == spells[spell_id].base2[i]) { - spellbonuses.FocusEffects[e] = effect_value; + if (negate_spellbonus) { spellbonuses.FocusEffects[e] = effect_value; } continue; } } @@ -4066,402 +4107,400 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) switch (spells[spell_id].base2[i]) { case SE_CurrentHP: - if(spells[spell_id].base[i] == 1) { - spellbonuses.HPRegen = effect_value; - aabonuses.HPRegen = effect_value; - itembonuses.HPRegen = effect_value; - } + if (negate_spellbonus) { spellbonuses.HPRegen = effect_value; } + if (negate_aabonus) { aabonuses.HPRegen = effect_value; } + if (negate_itembonus) { itembonuses.HPRegen = effect_value; } break; case SE_CurrentEndurance: - spellbonuses.EnduranceRegen = effect_value; - aabonuses.EnduranceRegen = effect_value; - itembonuses.EnduranceRegen = effect_value; + if (negate_spellbonus) { spellbonuses.EnduranceRegen = effect_value; } + if (negate_aabonus) { aabonuses.EnduranceRegen = effect_value; } + if (negate_itembonus) { itembonuses.EnduranceRegen = effect_value; } break; case SE_ChangeFrenzyRad: - spellbonuses.AggroRange = static_cast(effect_value); - aabonuses.AggroRange = static_cast(effect_value); - itembonuses.AggroRange = static_cast(effect_value); + if (negate_spellbonus) { spellbonuses.AggroRange = static_cast(effect_value); } + if (negate_aabonus) { aabonuses.AggroRange = static_cast(effect_value); } + if (negate_itembonus) { itembonuses.AggroRange = static_cast(effect_value); } break; case SE_Harmony: - spellbonuses.AssistRange = static_cast(effect_value); - aabonuses.AssistRange = static_cast(effect_value); - itembonuses.AssistRange = static_cast(effect_value); + if (negate_spellbonus) { spellbonuses.AssistRange = static_cast(effect_value); } + if (negate_aabonus) { aabonuses.AssistRange = static_cast(effect_value); } + if (negate_itembonus) { itembonuses.AssistRange = static_cast(effect_value); } break; case SE_AttackSpeed: - spellbonuses.haste = effect_value; - aabonuses.haste = effect_value; - itembonuses.haste = effect_value; + if (negate_spellbonus) { spellbonuses.haste = effect_value; } + if (negate_aabonus) { aabonuses.haste = effect_value; } + if (negate_itembonus) { itembonuses.haste = effect_value; } break; case SE_AttackSpeed2: - spellbonuses.hastetype2 = effect_value; - aabonuses.hastetype2 = effect_value; - itembonuses.hastetype2 = effect_value; + if (negate_spellbonus) { spellbonuses.hastetype2 = effect_value; } + if (negate_aabonus) { aabonuses.hastetype2 = effect_value; } + if (negate_itembonus) { itembonuses.hastetype2 = effect_value; } break; case SE_AttackSpeed3: { if (effect_value > 0) { - spellbonuses.hastetype3 = effect_value; - aabonuses.hastetype3 = effect_value; - itembonuses.hastetype3 = effect_value; + if (negate_spellbonus) { spellbonuses.hastetype3 = effect_value; } + if (negate_aabonus) { aabonuses.hastetype3 = effect_value; } + if (negate_itembonus) { itembonuses.hastetype3 = effect_value; } } break; } case SE_AttackSpeed4: - spellbonuses.inhibitmelee = effect_value; - aabonuses.inhibitmelee = effect_value; - itembonuses.inhibitmelee = effect_value; + if (negate_spellbonus) { spellbonuses.inhibitmelee = effect_value; } + if (negate_aabonus) { aabonuses.inhibitmelee = effect_value; } + if (negate_itembonus) { itembonuses.inhibitmelee = effect_value; } break; case SE_TotalHP: - spellbonuses.HP = effect_value; - aabonuses.HP = effect_value; - itembonuses.HP = effect_value; + if (negate_spellbonus) { spellbonuses.HP = effect_value; } + if (negate_aabonus) { aabonuses.HP = effect_value; } + if (negate_itembonus) { itembonuses.HP = effect_value; } break; case SE_ManaRegen_v2: case SE_CurrentMana: - spellbonuses.ManaRegen = effect_value; - aabonuses.ManaRegen = effect_value; - itembonuses.ManaRegen = effect_value; + if (negate_spellbonus) { spellbonuses.ManaRegen = effect_value; } + if (negate_aabonus) { aabonuses.ManaRegen = effect_value; } + if (negate_itembonus) { itembonuses.ManaRegen = effect_value; } break; case SE_ManaPool: - spellbonuses.Mana = effect_value; - itembonuses.Mana = effect_value; - aabonuses.Mana = effect_value; + if (negate_spellbonus) { spellbonuses.Mana = effect_value; } + if (negate_itembonus) { itembonuses.Mana = effect_value; } + if (negate_aabonus) { aabonuses.Mana = effect_value; } break; case SE_Stamina: - spellbonuses.EnduranceReduction = effect_value; - itembonuses.EnduranceReduction = effect_value; - aabonuses.EnduranceReduction = effect_value; + if (negate_spellbonus) { spellbonuses.EnduranceReduction = effect_value; } + if (negate_itembonus) { itembonuses.EnduranceReduction = effect_value; } + if (negate_aabonus) { aabonuses.EnduranceReduction = effect_value; } break; case SE_ACv2: case SE_ArmorClass: - spellbonuses.AC = effect_value; - aabonuses.AC = effect_value; - itembonuses.AC = effect_value; + if (negate_spellbonus) { spellbonuses.AC = effect_value; } + if (negate_aabonus) { aabonuses.AC = effect_value; } + if (negate_itembonus) { itembonuses.AC = effect_value; } break; case SE_ATK: - spellbonuses.ATK = effect_value; - aabonuses.ATK = effect_value; - itembonuses.ATK = effect_value; + if (negate_spellbonus) { spellbonuses.ATK = effect_value; } + if (negate_aabonus) { aabonuses.ATK = effect_value; } + if (negate_itembonus) { itembonuses.ATK = effect_value; } break; case SE_STR: - spellbonuses.STR = effect_value; - itembonuses.STR = effect_value; - aabonuses.STR = effect_value; + if (negate_spellbonus) { spellbonuses.STR = effect_value; } + if (negate_itembonus) { itembonuses.STR = effect_value; } + if (negate_aabonus) { aabonuses.STR = effect_value; } break; case SE_DEX: - spellbonuses.DEX = effect_value; - aabonuses.DEX = effect_value; - itembonuses.DEX = effect_value; + if (negate_spellbonus) { spellbonuses.DEX = effect_value; } + if (negate_aabonus) { aabonuses.DEX = effect_value; } + if (negate_itembonus) { itembonuses.DEX = effect_value; } break; case SE_AGI: - itembonuses.AGI = effect_value; - aabonuses.AGI = effect_value; - spellbonuses.AGI = effect_value; + if (negate_itembonus) { itembonuses.AGI = effect_value; } + if (negate_aabonus) { aabonuses.AGI = effect_value; } + if (negate_spellbonus) { spellbonuses.AGI = effect_value; } break; case SE_STA: - spellbonuses.STA = effect_value; - itembonuses.STA = effect_value; - aabonuses.STA = effect_value; + if (negate_spellbonus) { spellbonuses.STA = effect_value; } + if (negate_itembonus) { itembonuses.STA = effect_value; } + if (negate_aabonus) { aabonuses.STA = effect_value; } break; case SE_INT: - spellbonuses.INT = effect_value; - aabonuses.INT = effect_value; - itembonuses.INT = effect_value; + if (negate_spellbonus) { spellbonuses.INT = effect_value; } + if (negate_aabonus) { aabonuses.INT = effect_value; } + if (negate_itembonus) { itembonuses.INT = effect_value; } break; case SE_WIS: - spellbonuses.WIS = effect_value; - aabonuses.WIS = effect_value; - itembonuses.WIS = effect_value; + if (negate_spellbonus) { spellbonuses.WIS = effect_value; } + if (negate_aabonus) { aabonuses.WIS = effect_value; } + if (negate_itembonus) { itembonuses.WIS = effect_value; } break; case SE_CHA: - itembonuses.CHA = effect_value; - spellbonuses.CHA = effect_value; - aabonuses.CHA = effect_value; + if (negate_itembonus) { itembonuses.CHA = effect_value; } + if (negate_spellbonus) { spellbonuses.CHA = effect_value; } + if (negate_aabonus) { aabonuses.CHA = effect_value; } break; case SE_AllStats: { - spellbonuses.STR = effect_value; - spellbonuses.DEX = effect_value; - spellbonuses.AGI = effect_value; - spellbonuses.STA = effect_value; - spellbonuses.INT = effect_value; - spellbonuses.WIS = effect_value; - spellbonuses.CHA = effect_value; + if (negate_spellbonus) { spellbonuses.STR = effect_value; } + if (negate_spellbonus) { spellbonuses.DEX = effect_value; } + if (negate_spellbonus) { spellbonuses.AGI = effect_value; } + if (negate_spellbonus) { spellbonuses.STA = effect_value; } + if (negate_spellbonus) { spellbonuses.INT = effect_value; } + if (negate_spellbonus) { spellbonuses.WIS = effect_value; } + if (negate_spellbonus) { spellbonuses.CHA = effect_value; } - itembonuses.STR = effect_value; - itembonuses.DEX = effect_value; - itembonuses.AGI = effect_value; - itembonuses.STA = effect_value; - itembonuses.INT = effect_value; - itembonuses.WIS = effect_value; - itembonuses.CHA = effect_value; + if (negate_itembonus) { itembonuses.STR = effect_value; } + if (negate_itembonus) { itembonuses.DEX = effect_value; } + if (negate_itembonus) { itembonuses.AGI = effect_value; } + if (negate_itembonus) { itembonuses.STA = effect_value; } + if (negate_itembonus) { itembonuses.INT = effect_value; } + if (negate_itembonus) { itembonuses.WIS = effect_value; } + if (negate_itembonus) { itembonuses.CHA = effect_value; } - aabonuses.STR = effect_value; - aabonuses.DEX = effect_value; - aabonuses.AGI = effect_value; - aabonuses.STA = effect_value; - aabonuses.INT = effect_value; - aabonuses.WIS = effect_value; - aabonuses.CHA = effect_value; + if (negate_aabonus) { aabonuses.STR = effect_value; } + if (negate_aabonus) { aabonuses.DEX = effect_value; } + if (negate_aabonus) { aabonuses.AGI = effect_value; } + if (negate_aabonus) { aabonuses.STA = effect_value; } + if (negate_aabonus) { aabonuses.INT = effect_value; } + if (negate_aabonus) { aabonuses.WIS = effect_value; } + if (negate_aabonus) { aabonuses.CHA = effect_value; } break; } case SE_ResistFire: - spellbonuses.FR = effect_value; - itembonuses.FR = effect_value; - aabonuses.FR = effect_value; + if (negate_spellbonus) { spellbonuses.FR = effect_value; } + if (negate_itembonus) { itembonuses.FR = effect_value; } + if (negate_aabonus) { aabonuses.FR = effect_value; } break; case SE_ResistCold: - spellbonuses.CR = effect_value; - aabonuses.CR = effect_value; - itembonuses.CR = effect_value; + if (negate_spellbonus) { spellbonuses.CR = effect_value; } + if (negate_aabonus) { aabonuses.CR = effect_value; } + if (negate_itembonus) { itembonuses.CR = effect_value; } break; case SE_ResistPoison: - spellbonuses.PR = effect_value; - aabonuses.PR = effect_value; - itembonuses.PR = effect_value; + if (negate_spellbonus) { spellbonuses.PR = effect_value; } + if (negate_aabonus) { aabonuses.PR = effect_value; } + if (negate_itembonus) { itembonuses.PR = effect_value; } break; case SE_ResistDisease: - spellbonuses.DR = effect_value; - itembonuses.DR = effect_value; - aabonuses.DR = effect_value; + if (negate_spellbonus) { spellbonuses.DR = effect_value; } + if (negate_itembonus) { itembonuses.DR = effect_value; } + if (negate_aabonus) { aabonuses.DR = effect_value; } break; case SE_ResistMagic: - spellbonuses.MR = effect_value; - aabonuses.MR = effect_value; - itembonuses.MR = effect_value; + if (negate_spellbonus) { spellbonuses.MR = effect_value; } + if (negate_aabonus) { aabonuses.MR = effect_value; } + if (negate_itembonus) { itembonuses.MR = effect_value; } break; case SE_ResistAll: { - spellbonuses.MR = effect_value; - spellbonuses.DR = effect_value; - spellbonuses.PR = effect_value; - spellbonuses.CR = effect_value; - spellbonuses.FR = effect_value; + if (negate_spellbonus) { spellbonuses.MR = effect_value; } + if (negate_spellbonus) { spellbonuses.DR = effect_value; } + if (negate_spellbonus) { spellbonuses.PR = effect_value; } + if (negate_spellbonus) { spellbonuses.CR = effect_value; } + if (negate_spellbonus) { spellbonuses.FR = effect_value; } - aabonuses.MR = effect_value; - aabonuses.DR = effect_value; - aabonuses.PR = effect_value; - aabonuses.CR = effect_value; - aabonuses.FR = effect_value; + if (negate_aabonus) { aabonuses.MR = effect_value; } + if (negate_aabonus) { aabonuses.DR = effect_value; } + if (negate_aabonus) { aabonuses.PR = effect_value; } + if (negate_aabonus) { aabonuses.CR = effect_value; } + if (negate_aabonus) { aabonuses.FR = effect_value; } - itembonuses.MR = effect_value; - itembonuses.DR = effect_value; - itembonuses.PR = effect_value; - itembonuses.CR = effect_value; - itembonuses.FR = effect_value; + if (negate_itembonus) { itembonuses.MR = effect_value; } + if (negate_itembonus) { itembonuses.DR = effect_value; } + if (negate_itembonus) { itembonuses.PR = effect_value; } + if (negate_itembonus) { itembonuses.CR = effect_value; } + if (negate_itembonus) { itembonuses.FR = effect_value; } break; } case SE_ResistCorruption: - spellbonuses.Corrup = effect_value; - itembonuses.Corrup = effect_value; - aabonuses.Corrup = effect_value; + if (negate_spellbonus) { spellbonuses.Corrup = effect_value; } + if (negate_itembonus) { itembonuses.Corrup = effect_value; } + if (negate_aabonus) { aabonuses.Corrup = effect_value; } break; case SE_CastingLevel: // Brilliance of Ro - spellbonuses.adjusted_casting_skill = effect_value; - aabonuses.adjusted_casting_skill = effect_value; - itembonuses.adjusted_casting_skill = effect_value; + if (negate_spellbonus) { spellbonuses.adjusted_casting_skill = effect_value; } + if (negate_aabonus) { aabonuses.adjusted_casting_skill = effect_value; } + if (negate_itembonus) { itembonuses.adjusted_casting_skill = effect_value; } break; case SE_CastingLevel2: - spellbonuses.effective_casting_level = effect_value; - aabonuses.effective_casting_level = effect_value; - itembonuses.effective_casting_level = effect_value; + if (negate_spellbonus) { spellbonuses.effective_casting_level = effect_value; } + if (negate_aabonus) { aabonuses.effective_casting_level = effect_value; } + if (negate_itembonus) { itembonuses.effective_casting_level = effect_value; } break; case SE_MovementSpeed: - spellbonuses.movementspeed = effect_value; - aabonuses.movementspeed = effect_value; - itembonuses.movementspeed = effect_value; + if (negate_spellbonus) { spellbonuses.movementspeed = effect_value; } + if (negate_aabonus) { aabonuses.movementspeed = effect_value; } + if (negate_itembonus) { itembonuses.movementspeed = effect_value; } break; case SE_SpellDamageShield: - spellbonuses.SpellDamageShield = effect_value; - aabonuses.SpellDamageShield = effect_value; - itembonuses.SpellDamageShield = effect_value; + if (negate_spellbonus) { spellbonuses.SpellDamageShield = effect_value; } + if (negate_aabonus) { aabonuses.SpellDamageShield = effect_value; } + if (negate_itembonus) { itembonuses.SpellDamageShield = effect_value; } break; case SE_DamageShield: - spellbonuses.DamageShield = effect_value; - aabonuses.DamageShield = effect_value; - itembonuses.DamageShield = effect_value; + if (negate_spellbonus) { spellbonuses.DamageShield = effect_value; } + if (negate_aabonus) { aabonuses.DamageShield = effect_value; } + if (negate_itembonus) { itembonuses.DamageShield = effect_value; } break; case SE_ReverseDS: - spellbonuses.ReverseDamageShield = effect_value; - aabonuses.ReverseDamageShield = effect_value; - itembonuses.ReverseDamageShield = effect_value; + if (negate_spellbonus) { spellbonuses.ReverseDamageShield = effect_value; } + if (negate_aabonus) { aabonuses.ReverseDamageShield = effect_value; } + if (negate_itembonus) { itembonuses.ReverseDamageShield = effect_value; } break; case SE_Reflect: - spellbonuses.reflect_chance = effect_value; - aabonuses.reflect_chance = effect_value; - itembonuses.reflect_chance = effect_value; + if (negate_spellbonus) { spellbonuses.reflect_chance = effect_value; } + if (negate_aabonus) { aabonuses.reflect_chance = effect_value; } + if (negate_itembonus) { itembonuses.reflect_chance = effect_value; } break; case SE_Amplification: - spellbonuses.Amplification = effect_value; - itembonuses.Amplification = effect_value; - aabonuses.Amplification = effect_value; + if (negate_spellbonus) { spellbonuses.Amplification = effect_value; } + if (negate_itembonus) { itembonuses.Amplification = effect_value; } + if (negate_aabonus) { aabonuses.Amplification = effect_value; } break; case SE_ChangeAggro: - spellbonuses.hatemod = effect_value; - itembonuses.hatemod = effect_value; - aabonuses.hatemod = effect_value; + if (negate_spellbonus) { spellbonuses.hatemod = effect_value; } + if (negate_itembonus) { itembonuses.hatemod = effect_value; } + if (negate_aabonus) { aabonuses.hatemod = effect_value; } break; case SE_MeleeMitigation: - spellbonuses.MeleeMitigationEffect = effect_value; - itembonuses.MeleeMitigationEffect = effect_value; - aabonuses.MeleeMitigationEffect = effect_value; + if (negate_spellbonus) { spellbonuses.MeleeMitigationEffect = effect_value; } + if (negate_itembonus) { itembonuses.MeleeMitigationEffect = effect_value; } + if (negate_aabonus) { aabonuses.MeleeMitigationEffect = effect_value; } break; case SE_CriticalHitChance: { for (int e = 0; e < EQ::skills::HIGHEST_SKILL + 1; e++) { - spellbonuses.CriticalHitChance[e] = effect_value; - aabonuses.CriticalHitChance[e] = effect_value; - itembonuses.CriticalHitChance[e] = effect_value; + if (negate_spellbonus) { spellbonuses.CriticalHitChance[e] = effect_value; } + if (negate_aabonus) { aabonuses.CriticalHitChance[e] = effect_value; } + if (negate_itembonus) { itembonuses.CriticalHitChance[e] = effect_value; } } } case SE_CrippBlowChance: - spellbonuses.CrippBlowChance = effect_value; - aabonuses.CrippBlowChance = effect_value; - itembonuses.CrippBlowChance = effect_value; + if (negate_spellbonus) { spellbonuses.CrippBlowChance = effect_value; } + if (negate_aabonus) { aabonuses.CrippBlowChance = effect_value; } + if (negate_itembonus) { itembonuses.CrippBlowChance = effect_value; } break; case SE_AvoidMeleeChance: - spellbonuses.AvoidMeleeChanceEffect = effect_value; - aabonuses.AvoidMeleeChanceEffect = effect_value; - itembonuses.AvoidMeleeChanceEffect = effect_value; + if (negate_spellbonus) { spellbonuses.AvoidMeleeChanceEffect = effect_value; } + if (negate_aabonus) { aabonuses.AvoidMeleeChanceEffect = effect_value; } + if (negate_itembonus) { itembonuses.AvoidMeleeChanceEffect = effect_value; } break; case SE_RiposteChance: - spellbonuses.RiposteChance = effect_value; - aabonuses.RiposteChance = effect_value; - itembonuses.RiposteChance = effect_value; + if (negate_spellbonus) { spellbonuses.RiposteChance = effect_value; } + if (negate_aabonus) { aabonuses.RiposteChance = effect_value; } + if (negate_itembonus) { itembonuses.RiposteChance = effect_value; } break; case SE_DodgeChance: - spellbonuses.DodgeChance = effect_value; - aabonuses.DodgeChance = effect_value; - itembonuses.DodgeChance = effect_value; + if (negate_spellbonus) { spellbonuses.DodgeChance = effect_value; } + if (negate_aabonus) { aabonuses.DodgeChance = effect_value; } + if (negate_itembonus) { itembonuses.DodgeChance = effect_value; } break; case SE_ParryChance: - spellbonuses.ParryChance = effect_value; - aabonuses.ParryChance = effect_value; - itembonuses.ParryChance = effect_value; + if (negate_spellbonus) { spellbonuses.ParryChance = effect_value; } + if (negate_aabonus) { aabonuses.ParryChance = effect_value; } + if (negate_itembonus) { itembonuses.ParryChance = effect_value; } break; case SE_DualWieldChance: - spellbonuses.DualWieldChance = effect_value; - aabonuses.DualWieldChance = effect_value; - itembonuses.DualWieldChance = effect_value; + if (negate_spellbonus) { spellbonuses.DualWieldChance = effect_value; } + if (negate_aabonus) { aabonuses.DualWieldChance = effect_value; } + if (negate_itembonus) { itembonuses.DualWieldChance = effect_value; } break; case SE_DoubleAttackChance: - spellbonuses.DoubleAttackChance = effect_value; - aabonuses.DoubleAttackChance = effect_value; - itembonuses.DoubleAttackChance = effect_value; + if (negate_spellbonus) { spellbonuses.DoubleAttackChance = effect_value; } + if (negate_aabonus) { aabonuses.DoubleAttackChance = effect_value; } + if (negate_itembonus) { itembonuses.DoubleAttackChance = effect_value; } break; case SE_TripleAttackChance: - spellbonuses.TripleAttackChance = effect_value; - aabonuses.TripleAttackChance = effect_value; - itembonuses.TripleAttackChance = effect_value; + if (negate_spellbonus) { spellbonuses.TripleAttackChance = effect_value; } + if (negate_aabonus) { aabonuses.TripleAttackChance = effect_value; } + if (negate_itembonus) { itembonuses.TripleAttackChance = effect_value; } break; case SE_MeleeLifetap: - spellbonuses.MeleeLifetap = effect_value; - aabonuses.MeleeLifetap = effect_value; - itembonuses.MeleeLifetap = effect_value; + if (negate_spellbonus) { spellbonuses.MeleeLifetap = effect_value; } + if (negate_aabonus) { aabonuses.MeleeLifetap = effect_value; } + if (negate_itembonus) { itembonuses.MeleeLifetap = effect_value; } break; case SE_AllInstrumentMod: { - spellbonuses.singingMod = effect_value; - spellbonuses.brassMod = effect_value; - spellbonuses.percussionMod = effect_value; - spellbonuses.windMod = effect_value; - spellbonuses.stringedMod = effect_value; + if (negate_spellbonus) { spellbonuses.singingMod = effect_value; } + if (negate_spellbonus) { spellbonuses.brassMod = effect_value; } + if (negate_spellbonus) { spellbonuses.percussionMod = effect_value; } + if (negate_spellbonus) { spellbonuses.windMod = effect_value; } + if (negate_spellbonus) { spellbonuses.stringedMod = effect_value; } - itembonuses.singingMod = effect_value; - itembonuses.brassMod = effect_value; - itembonuses.percussionMod = effect_value; - itembonuses.windMod = effect_value; - itembonuses.stringedMod = effect_value; + if (negate_itembonus) { itembonuses.singingMod = effect_value; } + if (negate_itembonus) { itembonuses.brassMod = effect_value; } + if (negate_itembonus) { itembonuses.percussionMod = effect_value; } + if (negate_itembonus) { itembonuses.windMod = effect_value; } + if (negate_itembonus) { itembonuses.stringedMod = effect_value; } - aabonuses.singingMod = effect_value; - aabonuses.brassMod = effect_value; - aabonuses.percussionMod = effect_value; - aabonuses.windMod = effect_value; - aabonuses.stringedMod = effect_value; + if (negate_aabonus) { aabonuses.singingMod = effect_value; } + if (negate_aabonus) { aabonuses.brassMod = effect_value; } + if (negate_aabonus) { aabonuses.percussionMod = effect_value; } + if (negate_aabonus) { aabonuses.windMod = effect_value; } + if (negate_aabonus) { aabonuses.stringedMod = effect_value; } break; } case SE_ResistSpellChance: - spellbonuses.ResistSpellChance = effect_value; - aabonuses.ResistSpellChance = effect_value; - itembonuses.ResistSpellChance = effect_value; + if (negate_spellbonus) { spellbonuses.ResistSpellChance = effect_value; } + if (negate_aabonus) { aabonuses.ResistSpellChance = effect_value; } + if (negate_itembonus) { itembonuses.ResistSpellChance = effect_value; } break; case SE_ResistFearChance: - spellbonuses.Fearless = false; - spellbonuses.ResistFearChance = effect_value; - aabonuses.ResistFearChance = effect_value; - itembonuses.ResistFearChance = effect_value; + if (negate_spellbonus) { spellbonuses.Fearless = false; } + if (negate_spellbonus) { spellbonuses.ResistFearChance = effect_value; } + if (negate_aabonus) { aabonuses.ResistFearChance = effect_value; } + if (negate_itembonus) { itembonuses.ResistFearChance = effect_value; } break; case SE_Fearless: - spellbonuses.Fearless = false; - aabonuses.Fearless = false; - itembonuses.Fearless = false; + if (negate_spellbonus) { spellbonuses.Fearless = false; } + if (negate_aabonus) { aabonuses.Fearless = false; } + if (negate_itembonus) { itembonuses.Fearless = false; } break; case SE_HundredHands: - spellbonuses.HundredHands = effect_value; - aabonuses.HundredHands = effect_value; - itembonuses.HundredHands = effect_value; + if (negate_spellbonus) { spellbonuses.HundredHands = effect_value; } + if (negate_aabonus) { aabonuses.HundredHands = effect_value; } + if (negate_itembonus) { itembonuses.HundredHands = effect_value; } break; case SE_MeleeSkillCheck: { - spellbonuses.MeleeSkillCheck = effect_value; - spellbonuses.MeleeSkillCheckSkill = effect_value; + if (negate_spellbonus) { spellbonuses.MeleeSkillCheck = effect_value; } + if (negate_spellbonus) { spellbonuses.MeleeSkillCheckSkill = effect_value; } break; } @@ -4469,9 +4508,9 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) { for (int e = 0; e < EQ::skills::HIGHEST_SKILL + 1; e++) { - spellbonuses.HitChanceEffect[e] = effect_value; - aabonuses.HitChanceEffect[e] = effect_value; - itembonuses.HitChanceEffect[e] = effect_value; + if (negate_spellbonus) { spellbonuses.HitChanceEffect[e] = effect_value; } + if (negate_aabonus) { aabonuses.HitChanceEffect[e] = effect_value; } + if (negate_itembonus) { itembonuses.HitChanceEffect[e] = effect_value; } } break; } @@ -4480,9 +4519,9 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) { for (int e = 0; e < EQ::skills::HIGHEST_SKILL + 1; e++) { - spellbonuses.DamageModifier[e] = effect_value; - aabonuses.DamageModifier[e] = effect_value; - itembonuses.DamageModifier[e] = effect_value; + if (negate_spellbonus) { spellbonuses.DamageModifier[e] = effect_value; } + if (negate_aabonus) { aabonuses.DamageModifier[e] = effect_value; } + if (negate_itembonus) { itembonuses.DamageModifier[e] = effect_value; } } break; } @@ -4491,9 +4530,9 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) { for (int e = 0; e < EQ::skills::HIGHEST_SKILL + 1; e++) { - spellbonuses.DamageModifier2[e] = effect_value; - aabonuses.DamageModifier2[e] = effect_value; - itembonuses.DamageModifier2[e] = effect_value; + if (negate_spellbonus) { spellbonuses.DamageModifier2[e] = effect_value; } + if (negate_aabonus) { aabonuses.DamageModifier2[e] = effect_value; } + if (negate_itembonus) { itembonuses.DamageModifier2[e] = effect_value; } } break; } @@ -4502,9 +4541,9 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) { for (int e = 0; e < EQ::skills::HIGHEST_SKILL + 1; e++) { - spellbonuses.DamageModifier3[e] = effect_value; - aabonuses.DamageModifier3[e] = effect_value; - itembonuses.DamageModifier3[e] = effect_value; + if (negate_spellbonus) { spellbonuses.DamageModifier3[e] = effect_value; } + if (negate_aabonus) { aabonuses.DamageModifier3[e] = effect_value; } + if (negate_itembonus) { itembonuses.DamageModifier3[e] = effect_value; } } break; } @@ -4514,169 +4553,169 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) { for (int e = 0; e < EQ::skills::HIGHEST_SKILL + 1; e++) { - spellbonuses.MinDamageModifier[e] = effect_value; - aabonuses.MinDamageModifier[e] = effect_value; - itembonuses.MinDamageModifier[e] = effect_value; + if (negate_spellbonus) { spellbonuses.MinDamageModifier[e] = effect_value; } + if (negate_aabonus) { aabonuses.MinDamageModifier[e] = effect_value; } + if (negate_itembonus) { itembonuses.MinDamageModifier[e] = effect_value; } } break; } case SE_StunResist: - spellbonuses.StunResist = effect_value; - aabonuses.StunResist = effect_value; - itembonuses.StunResist = effect_value; + if (negate_spellbonus) { spellbonuses.StunResist = effect_value; } + if (negate_aabonus) { aabonuses.StunResist = effect_value; } + if (negate_itembonus) { itembonuses.StunResist = effect_value; } break; case SE_ProcChance: - spellbonuses.ProcChanceSPA = effect_value; - aabonuses.ProcChanceSPA = effect_value; - itembonuses.ProcChanceSPA = effect_value; + if (negate_spellbonus) { spellbonuses.ProcChanceSPA = effect_value; } + if (negate_aabonus) { aabonuses.ProcChanceSPA = effect_value; } + if (negate_itembonus) { itembonuses.ProcChanceSPA = effect_value; } break; case SE_ExtraAttackChance: - spellbonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; - aabonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; - itembonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; + if (negate_spellbonus) { spellbonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; } + if (negate_aabonus) { aabonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; } + if (negate_itembonus) { itembonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; } break; case SE_AddExtraAttackPct_1h_Primary: - spellbonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; - aabonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; - itembonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; + if (negate_spellbonus) { spellbonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; } + if (negate_aabonus) { aabonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; } + if (negate_itembonus) { itembonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; } break; case SE_AddExtraAttackPct_1h_Secondary: - spellbonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; - aabonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; - itembonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; + if (negate_spellbonus) { spellbonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; } + if (negate_aabonus) { aabonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; } + if (negate_itembonus) { itembonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; } break; case SE_Double_Melee_Round: - spellbonuses.DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] = effect_value; - aabonuses.DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] = effect_value; - itembonuses.DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] = effect_value; + if (negate_spellbonus) { spellbonuses.DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] = effect_value; } + if (negate_aabonus) { aabonuses.DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] = effect_value; } + if (negate_itembonus) { itembonuses.DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] = effect_value; } break; case SE_PercentXPIncrease: - spellbonuses.XPRateMod = effect_value; - aabonuses.XPRateMod = effect_value; - itembonuses.XPRateMod = effect_value; + if (negate_spellbonus) { spellbonuses.XPRateMod = effect_value; } + if (negate_aabonus) { aabonuses.XPRateMod = effect_value; } + if (negate_itembonus) { itembonuses.XPRateMod = effect_value; } break; case SE_Flurry: - spellbonuses.FlurryChance = effect_value; - aabonuses.FlurryChance = effect_value; - itembonuses.FlurryChance = effect_value; + if (negate_spellbonus) { spellbonuses.FlurryChance = effect_value; } + if (negate_aabonus) { aabonuses.FlurryChance = effect_value; } + if (negate_itembonus) { itembonuses.FlurryChance = effect_value; } break; case SE_Accuracy: { - spellbonuses.Accuracy[EQ::skills::HIGHEST_SKILL + 1] = effect_value; - itembonuses.Accuracy[EQ::skills::HIGHEST_SKILL + 1] = effect_value; + if (negate_spellbonus) { spellbonuses.Accuracy[EQ::skills::HIGHEST_SKILL + 1] = effect_value; } + if (negate_itembonus) { itembonuses.Accuracy[EQ::skills::HIGHEST_SKILL + 1] = effect_value; } for (int e = 0; e < EQ::skills::HIGHEST_SKILL + 1; e++) - { - aabonuses.Accuracy[e] = effect_value; - } + { + if (negate_aabonus) { aabonuses.Accuracy[e] = effect_value; } + } break; } case SE_MaxHPChange: - spellbonuses.MaxHPChange = effect_value; - aabonuses.MaxHPChange = effect_value; - itembonuses.MaxHPChange = effect_value; + if (negate_spellbonus) { spellbonuses.MaxHPChange = effect_value; } + if (negate_aabonus) { aabonuses.MaxHPChange = effect_value; } + if (negate_itembonus) { itembonuses.MaxHPChange = effect_value; } break; case SE_EndurancePool: - spellbonuses.Endurance = effect_value; - aabonuses.Endurance = effect_value; - itembonuses.Endurance = effect_value; + if (negate_spellbonus) { spellbonuses.Endurance = effect_value; } + if (negate_aabonus) { aabonuses.Endurance = effect_value; } + if (negate_itembonus) { itembonuses.Endurance = effect_value; } break; case SE_HealRate: - spellbonuses.HealRate = effect_value; - aabonuses.HealRate = effect_value; - itembonuses.HealRate = effect_value; + if (negate_spellbonus) { spellbonuses.HealRate = effect_value; } + if (negate_aabonus) { aabonuses.HealRate = effect_value; } + if (negate_itembonus) { itembonuses.HealRate = effect_value; } break; case SE_SkillDamageTaken: { for (int e = 0; e < EQ::skills::HIGHEST_SKILL + 1; e++) { - spellbonuses.SkillDmgTaken[e] = effect_value; - aabonuses.SkillDmgTaken[e] = effect_value; - itembonuses.SkillDmgTaken[e] = effect_value; + if (negate_spellbonus) { spellbonuses.SkillDmgTaken[e] = effect_value; } + if (negate_aabonus) { aabonuses.SkillDmgTaken[e] = effect_value; } + if (negate_itembonus) { itembonuses.SkillDmgTaken[e] = effect_value; } } break; } case SE_SpellCritChance: - spellbonuses.CriticalSpellChance = effect_value; - aabonuses.CriticalSpellChance = effect_value; - itembonuses.CriticalSpellChance = effect_value; + if (negate_spellbonus) { spellbonuses.CriticalSpellChance = effect_value; } + if (negate_aabonus) { aabonuses.CriticalSpellChance = effect_value; } + if (negate_itembonus) { itembonuses.CriticalSpellChance = effect_value; } break; case SE_CriticalSpellChance: - spellbonuses.CriticalSpellChance = effect_value; - spellbonuses.SpellCritDmgIncrease = effect_value; - aabonuses.CriticalSpellChance = effect_value; - aabonuses.SpellCritDmgIncrease = effect_value; - itembonuses.CriticalSpellChance = effect_value; - itembonuses.SpellCritDmgIncrease = effect_value; + if (negate_spellbonus) { spellbonuses.CriticalSpellChance = effect_value; } + if (negate_spellbonus) { spellbonuses.SpellCritDmgIncrease = effect_value; } + if (negate_aabonus) { aabonuses.CriticalSpellChance = effect_value; } + if (negate_aabonus) { aabonuses.SpellCritDmgIncrease = effect_value; } + if (negate_itembonus) { itembonuses.CriticalSpellChance = effect_value; } + if (negate_itembonus) { itembonuses.SpellCritDmgIncrease = effect_value; } break; case SE_SpellCritDmgIncrease: - spellbonuses.SpellCritDmgIncrease = effect_value; - aabonuses.SpellCritDmgIncrease = effect_value; - itembonuses.SpellCritDmgIncrease = effect_value; + if (negate_spellbonus) { spellbonuses.SpellCritDmgIncrease = effect_value; } + if (negate_aabonus) { aabonuses.SpellCritDmgIncrease = effect_value; } + if (negate_itembonus) { itembonuses.SpellCritDmgIncrease = effect_value; } break; case SE_DotCritDmgIncrease: - spellbonuses.DotCritDmgIncrease = effect_value; - aabonuses.DotCritDmgIncrease = effect_value; - itembonuses.DotCritDmgIncrease = effect_value; + if (negate_spellbonus) { spellbonuses.DotCritDmgIncrease = effect_value; } + if (negate_aabonus) { aabonuses.DotCritDmgIncrease = effect_value; } + if (negate_itembonus) { itembonuses.DotCritDmgIncrease = effect_value; } break; case SE_CriticalHealChance: - spellbonuses.CriticalHealChance = effect_value; - aabonuses.CriticalHealChance = effect_value; - itembonuses.CriticalHealChance = effect_value; + if (negate_spellbonus) { spellbonuses.CriticalHealChance = effect_value; } + if (negate_aabonus) { aabonuses.CriticalHealChance = effect_value; } + if (negate_itembonus) { itembonuses.CriticalHealChance = effect_value; } break; case SE_CriticalHealOverTime: - spellbonuses.CriticalHealOverTime = effect_value; - aabonuses.CriticalHealOverTime = effect_value; - itembonuses.CriticalHealOverTime = effect_value; + if (negate_spellbonus) { spellbonuses.CriticalHealOverTime = effect_value; } + if (negate_aabonus) { aabonuses.CriticalHealOverTime = effect_value; } + if (negate_itembonus) { itembonuses.CriticalHealOverTime = effect_value; } break; case SE_MitigateDamageShield: - spellbonuses.DSMitigationOffHand = effect_value; - itembonuses.DSMitigationOffHand = effect_value; - aabonuses.DSMitigationOffHand = effect_value; + if (negate_spellbonus) { spellbonuses.DSMitigationOffHand = effect_value; } + if (negate_itembonus) { itembonuses.DSMitigationOffHand = effect_value; } + if (negate_aabonus) { aabonuses.DSMitigationOffHand = effect_value; } break; case SE_CriticalDoTChance: - spellbonuses.CriticalDoTChance = effect_value; - aabonuses.CriticalDoTChance = effect_value; - itembonuses.CriticalDoTChance = effect_value; + if (negate_spellbonus) { spellbonuses.CriticalDoTChance = effect_value; } + if (negate_aabonus) { aabonuses.CriticalDoTChance = effect_value; } + if (negate_itembonus) { itembonuses.CriticalDoTChance = effect_value; } break; case SE_ProcOnKillShot: { - for(int e = 0; e < MAX_SPELL_TRIGGER*3; e=3) + for (int e = 0; e < MAX_SPELL_TRIGGER * 3; e = 3) { - spellbonuses.SpellOnKill[e] = effect_value; - spellbonuses.SpellOnKill[e+1] = effect_value; - spellbonuses.SpellOnKill[e+2] = effect_value; + if (negate_spellbonus) { spellbonuses.SpellOnKill[e] = effect_value; } + if (negate_spellbonus) { spellbonuses.SpellOnKill[e + 1] = effect_value; } + if (negate_spellbonus) { spellbonuses.SpellOnKill[e + 2] = effect_value; } - aabonuses.SpellOnKill[e] = effect_value; - aabonuses.SpellOnKill[e+1] = effect_value; - aabonuses.SpellOnKill[e+2] = effect_value; + if (negate_aabonus) { aabonuses.SpellOnKill[e] = effect_value; } + if (negate_aabonus) { aabonuses.SpellOnKill[e + 1] = effect_value; } + if (negate_aabonus) { aabonuses.SpellOnKill[e + 2] = effect_value; } - itembonuses.SpellOnKill[e] = effect_value; - itembonuses.SpellOnKill[e+1] = effect_value; - itembonuses.SpellOnKill[e+2] = effect_value; + if (negate_itembonus) { itembonuses.SpellOnKill[e] = effect_value; } + if (negate_itembonus) { itembonuses.SpellOnKill[e + 1] = effect_value; } + if (negate_itembonus) { itembonuses.SpellOnKill[e + 2] = effect_value; } } break; } @@ -4686,8 +4725,8 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) { for(int e = 0; e < MAX_SPELL_TRIGGER; e=2) { - spellbonuses.SpellOnDeath[e] = SPELL_UNKNOWN; - spellbonuses.SpellOnDeath[e+1] = effect_value; + if (negate_spellbonus) { spellbonuses.SpellOnDeath[e] = SPELL_UNKNOWN; + if (negate_spellbonus) { spellbonuses.SpellOnDeath[e+1] = effect_value; } } break; } @@ -4697,9 +4736,9 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) { for (int e = 0; e < EQ::skills::HIGHEST_SKILL + 1; e++) { - spellbonuses.CritDmgMod[e] = effect_value; - aabonuses.CritDmgMod[e] = effect_value; - itembonuses.CritDmgMod[e] = effect_value; + if (negate_spellbonus) { spellbonuses.CritDmgMod[e] = effect_value; } + if (negate_aabonus) { aabonuses.CritDmgMod[e] = effect_value; } + if (negate_itembonus) { itembonuses.CritDmgMod[e] = effect_value; } } break; } @@ -4708,9 +4747,9 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) { for (int e = 0; e < EQ::skills::HIGHEST_SKILL + 1; e++) { - spellbonuses.CritDmgModNoStack[e] = effect_value; - aabonuses.CritDmgModNoStack[e] = effect_value; - itembonuses.CritDmgModNoStack[e] = effect_value; + if (negate_spellbonus) { spellbonuses.CritDmgModNoStack[e] = effect_value; } + if (negate_aabonus) { aabonuses.CritDmgModNoStack[e] = effect_value; } + if (negate_itembonus) { itembonuses.CritDmgModNoStack[e] = effect_value; } } break; } @@ -4719,648 +4758,647 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) { for (int e = 0; e < EQ::skills::HIGHEST_SKILL + 1; e++) { - spellbonuses.SkillDamageAmount[e] = effect_value; - aabonuses.SkillDamageAmount[e] = effect_value; - itembonuses.SkillDamageAmount[e] = effect_value; + if (negate_spellbonus) { spellbonuses.SkillDamageAmount[e] = effect_value; } + if (negate_aabonus) { aabonuses.SkillDamageAmount[e] = effect_value; } + if (negate_itembonus) { itembonuses.SkillDamageAmount[e] = effect_value; } } break; } case SE_IncreaseBlockChance: - spellbonuses.IncreaseBlockChance = effect_value; - aabonuses.IncreaseBlockChance = effect_value; - itembonuses.IncreaseBlockChance = effect_value; + if (negate_spellbonus) { spellbonuses.IncreaseBlockChance = effect_value; } + if (negate_aabonus) { aabonuses.IncreaseBlockChance = effect_value; } + if (negate_itembonus) { itembonuses.IncreaseBlockChance = effect_value; } break; case SE_PersistantCasting: - spellbonuses.PersistantCasting = effect_value; - itembonuses.PersistantCasting = effect_value; - aabonuses.PersistantCasting = effect_value; + if (negate_spellbonus) { spellbonuses.PersistantCasting = effect_value; } + if (negate_itembonus) { itembonuses.PersistantCasting = effect_value; } + if (negate_aabonus) { aabonuses.PersistantCasting = effect_value; } break; case SE_ImmuneFleeing: - spellbonuses.ImmuneToFlee = false; + if (negate_spellbonus) { spellbonuses.ImmuneToFlee = false; } break; case SE_DelayDeath: - spellbonuses.DelayDeath = effect_value; - aabonuses.DelayDeath = effect_value; - itembonuses.DelayDeath = effect_value; + if (negate_spellbonus) { spellbonuses.DelayDeath = effect_value; } + if (negate_aabonus) { aabonuses.DelayDeath = effect_value; } + if (negate_itembonus) { itembonuses.DelayDeath = effect_value; } break; case SE_SpellProcChance: - spellbonuses.SpellProcChance = effect_value; - itembonuses.SpellProcChance = effect_value; - aabonuses.SpellProcChance = effect_value; + if (negate_spellbonus) { spellbonuses.SpellProcChance = effect_value; } + if (negate_itembonus) { itembonuses.SpellProcChance = effect_value; } + if (negate_aabonus) { aabonuses.SpellProcChance = effect_value; } break; case SE_CharmBreakChance: - spellbonuses.CharmBreakChance = effect_value; - aabonuses.CharmBreakChance = effect_value; - itembonuses.CharmBreakChance = effect_value; + if (negate_spellbonus) { spellbonuses.CharmBreakChance = effect_value; } + if (negate_aabonus) { aabonuses.CharmBreakChance = effect_value; } + if (negate_itembonus) { itembonuses.CharmBreakChance = effect_value; } break; case SE_BardSongRange: - spellbonuses.SongRange = effect_value; - aabonuses.SongRange = effect_value; - itembonuses.SongRange = effect_value; + if (negate_spellbonus) { spellbonuses.SongRange = effect_value; } + if (negate_aabonus) { aabonuses.SongRange = effect_value; } + if (negate_itembonus) { itembonuses.SongRange = effect_value; } break; case SE_SkillDamageAmount2: { for (int e = 0; e < EQ::skills::HIGHEST_SKILL + 1; e++) { - spellbonuses.SkillDamageAmount2[e] = effect_value; - aabonuses.SkillDamageAmount2[e] = effect_value; - itembonuses.SkillDamageAmount2[e] = effect_value; + if (negate_spellbonus) { spellbonuses.SkillDamageAmount2[e] = effect_value; } + if (negate_aabonus) { aabonuses.SkillDamageAmount2[e] = effect_value; } + if (negate_itembonus) { itembonuses.SkillDamageAmount2[e] = effect_value; } } break; } case SE_NegateAttacks: - spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_EXISTS] = effect_value; - spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_BUFFSLOT] = effect_value; + if (negate_spellbonus) { spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_EXISTS] = effect_value; } + if (negate_spellbonus) { spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_BUFFSLOT] = effect_value; } break; case SE_MitigateMeleeDamage: - spellbonuses.MitigateMeleeRune[SBIndex::MITIGATION_RUNE_PERCENT] = effect_value; - spellbonuses.MitigateMeleeRune[SBIndex::MITIGATION_RUNE_BUFFSLOT] = -1; + if (negate_spellbonus) { spellbonuses.MitigateMeleeRune[SBIndex::MITIGATION_RUNE_PERCENT] = effect_value; } + if (negate_spellbonus) { spellbonuses.MitigateMeleeRune[SBIndex::MITIGATION_RUNE_BUFFSLOT] = -1; } break; case SE_MeleeThresholdGuard: - spellbonuses.MeleeThresholdGuard[SBIndex::THRESHOLDGUARD_MITIGATION_PERCENT] = effect_value; - spellbonuses.MeleeThresholdGuard[SBIndex::THRESHOLDGUARD_BUFFSLOT] = -1; - spellbonuses.MeleeThresholdGuard[SBIndex::THRESHOLDGUARD_BUFFSLOT] = effect_value; + if (negate_spellbonus) { spellbonuses.MeleeThresholdGuard[SBIndex::THRESHOLDGUARD_MITIGATION_PERCENT] = effect_value; } + if (negate_spellbonus) { spellbonuses.MeleeThresholdGuard[SBIndex::THRESHOLDGUARD_BUFFSLOT] = -1; } + if (negate_spellbonus) { spellbonuses.MeleeThresholdGuard[SBIndex::THRESHOLDGUARD_BUFFSLOT] = effect_value; } break; case SE_SpellThresholdGuard: - spellbonuses.SpellThresholdGuard[SBIndex::THRESHOLDGUARD_MITIGATION_PERCENT] = effect_value; - spellbonuses.SpellThresholdGuard[SBIndex::THRESHOLDGUARD_BUFFSLOT] = -1; - spellbonuses.SpellThresholdGuard[SBIndex::THRESHOLDGUARD_BUFFSLOT] = effect_value; + if (negate_spellbonus) { spellbonuses.SpellThresholdGuard[SBIndex::THRESHOLDGUARD_MITIGATION_PERCENT] = effect_value; } + if (negate_spellbonus) { spellbonuses.SpellThresholdGuard[SBIndex::THRESHOLDGUARD_BUFFSLOT] = -1; } + if (negate_spellbonus) { spellbonuses.SpellThresholdGuard[SBIndex::THRESHOLDGUARD_BUFFSLOT] = effect_value; } break; case SE_MitigateSpellDamage: - spellbonuses.MitigateSpellRune[SBIndex::MITIGATION_RUNE_PERCENT] = effect_value; - spellbonuses.MitigateSpellRune[SBIndex::MITIGATION_RUNE_BUFFSLOT] = -1; + if (negate_spellbonus) { spellbonuses.MitigateSpellRune[SBIndex::MITIGATION_RUNE_PERCENT] = effect_value; } + if (negate_spellbonus) { spellbonuses.MitigateSpellRune[SBIndex::MITIGATION_RUNE_BUFFSLOT] = -1; } break; case SE_MitigateDotDamage: - spellbonuses.MitigateDotRune[SBIndex::MITIGATION_RUNE_PERCENT] = effect_value; - spellbonuses.MitigateDotRune[SBIndex::MITIGATION_RUNE_BUFFSLOT] = -1; + if (negate_spellbonus) { spellbonuses.MitigateDotRune[SBIndex::MITIGATION_RUNE_PERCENT] = effect_value; } + if (negate_spellbonus) { spellbonuses.MitigateDotRune[SBIndex::MITIGATION_RUNE_BUFFSLOT] = -1; } break; case SE_ManaAbsorbPercentDamage: - spellbonuses.ManaAbsorbPercentDamage = effect_value; + if (negate_spellbonus) { spellbonuses.ManaAbsorbPercentDamage = effect_value; } break; case SE_Endurance_Absorb_Pct_Damage: - spellbonuses.EnduranceAbsorbPercentDamage[SBIndex::ENDURANCE_ABSORD_MITIGIATION] = effect_value; - spellbonuses.EnduranceAbsorbPercentDamage[SBIndex::ENDURANCE_ABSORD_DRAIN_PER_HP] = effect_value; + if (negate_spellbonus) { spellbonuses.EnduranceAbsorbPercentDamage[SBIndex::ENDURANCE_ABSORD_MITIGIATION] = effect_value; } + if (negate_spellbonus) { spellbonuses.EnduranceAbsorbPercentDamage[SBIndex::ENDURANCE_ABSORD_DRAIN_PER_HP] = effect_value; } break; case SE_ShieldBlock: - spellbonuses.ShieldBlock = effect_value; - aabonuses.ShieldBlock = effect_value; - itembonuses.ShieldBlock = effect_value; + if (negate_spellbonus) { spellbonuses.ShieldBlock = effect_value; } + if (negate_aabonus) { aabonuses.ShieldBlock = effect_value; } + if (negate_itembonus) { itembonuses.ShieldBlock = effect_value; } case SE_BlockBehind: - spellbonuses.BlockBehind = effect_value; - aabonuses.BlockBehind = effect_value; - itembonuses.BlockBehind = effect_value; + if (negate_spellbonus) { spellbonuses.BlockBehind = effect_value; } + if (negate_aabonus) { aabonuses.BlockBehind = effect_value; } + if (negate_itembonus) { itembonuses.BlockBehind = effect_value; } break; case SE_Blind: - spellbonuses.IsBlind = false; + if (negate_spellbonus) { spellbonuses.IsBlind = false; } break; case SE_Fear: - spellbonuses.IsFeared = false; + if (negate_spellbonus) { spellbonuses.IsFeared = false; } break; case SE_FrontalStunResist: - spellbonuses.FrontalStunResist = effect_value; - aabonuses.FrontalStunResist = effect_value; - itembonuses.FrontalStunResist = effect_value; + if (negate_spellbonus) { spellbonuses.FrontalStunResist = effect_value; } + if (negate_aabonus) { aabonuses.FrontalStunResist = effect_value; } + if (negate_itembonus) { itembonuses.FrontalStunResist = effect_value; } break; case SE_ImprovedBindWound: - aabonuses.BindWound = effect_value; - itembonuses.BindWound = effect_value; - spellbonuses.BindWound = effect_value; + if (negate_aabonus) { aabonuses.BindWound = effect_value; } + if (negate_itembonus) { itembonuses.BindWound = effect_value; } + if (negate_spellbonus) { spellbonuses.BindWound = effect_value; } break; case SE_MaxBindWound: - spellbonuses.MaxBindWound = effect_value; - aabonuses.MaxBindWound = effect_value; - itembonuses.MaxBindWound = effect_value; + if (negate_spellbonus) { spellbonuses.MaxBindWound = effect_value; } + if (negate_aabonus) { aabonuses.MaxBindWound = effect_value; } + if (negate_itembonus) { itembonuses.MaxBindWound = effect_value; } break; case SE_BaseMovementSpeed: - spellbonuses.BaseMovementSpeed = effect_value; - aabonuses.BaseMovementSpeed = effect_value; - itembonuses.BaseMovementSpeed = effect_value; + if (negate_spellbonus) { spellbonuses.BaseMovementSpeed = effect_value; } + if (negate_aabonus) { aabonuses.BaseMovementSpeed = effect_value; } + if (negate_itembonus) { itembonuses.BaseMovementSpeed = effect_value; } break; case SE_IncreaseRunSpeedCap: - itembonuses.IncreaseRunSpeedCap = effect_value; - aabonuses.IncreaseRunSpeedCap = effect_value; - spellbonuses.IncreaseRunSpeedCap = effect_value; + if (negate_itembonus) { itembonuses.IncreaseRunSpeedCap = effect_value; } + if (negate_aabonus) { aabonuses.IncreaseRunSpeedCap = effect_value; } + if (negate_spellbonus) { spellbonuses.IncreaseRunSpeedCap = effect_value; } break; case SE_DoubleSpecialAttack: - spellbonuses.DoubleSpecialAttack = effect_value; - aabonuses.DoubleSpecialAttack = effect_value; - itembonuses.DoubleSpecialAttack = effect_value; + if (negate_spellbonus) { spellbonuses.DoubleSpecialAttack = effect_value; } + if (negate_aabonus) { aabonuses.DoubleSpecialAttack = effect_value; } + if (negate_itembonus) { itembonuses.DoubleSpecialAttack = effect_value; } break; case SE_TripleBackstab: - spellbonuses.TripleBackstab = effect_value; - aabonuses.TripleBackstab = effect_value; - itembonuses.TripleBackstab = effect_value; + if (negate_spellbonus) { spellbonuses.TripleBackstab = effect_value; } + if (negate_aabonus) { aabonuses.TripleBackstab = effect_value; } + if (negate_itembonus) { itembonuses.TripleBackstab = effect_value; } break; case SE_FrontalBackstabMinDmg: - spellbonuses.FrontalBackstabMinDmg = false; + if (negate_spellbonus) { spellbonuses.FrontalBackstabMinDmg = false; } break; case SE_FrontalBackstabChance: - spellbonuses.FrontalBackstabChance = effect_value; - aabonuses.FrontalBackstabChance = effect_value; - itembonuses.FrontalBackstabChance = effect_value; + if (negate_spellbonus) { spellbonuses.FrontalBackstabChance = effect_value; } + if (negate_aabonus) { aabonuses.FrontalBackstabChance = effect_value; } + if (negate_itembonus) { itembonuses.FrontalBackstabChance = effect_value; } break; case SE_Double_Backstab_Front: - spellbonuses.Double_Backstab_Front = effect_value; - aabonuses.Double_Backstab_Front = effect_value; - itembonuses.Double_Backstab_Front = effect_value; + if (negate_spellbonus) { spellbonuses.Double_Backstab_Front = effect_value; } + if (negate_aabonus) { aabonuses.Double_Backstab_Front = effect_value; } + if (negate_itembonus) { itembonuses.Double_Backstab_Front = effect_value; } break; case SE_ConsumeProjectile: - spellbonuses.ConsumeProjectile = effect_value; - aabonuses.ConsumeProjectile = effect_value; - itembonuses.ConsumeProjectile = effect_value; + if (negate_spellbonus) { spellbonuses.ConsumeProjectile = effect_value; } + if (negate_aabonus) { aabonuses.ConsumeProjectile = effect_value; } + if (negate_itembonus) { itembonuses.ConsumeProjectile = effect_value; } break; case SE_ForageAdditionalItems: - spellbonuses.ForageAdditionalItems = effect_value; - aabonuses.ForageAdditionalItems = effect_value; - itembonuses.ForageAdditionalItems = effect_value; + if (negate_spellbonus) { spellbonuses.ForageAdditionalItems = effect_value; } + if (negate_aabonus) { aabonuses.ForageAdditionalItems = effect_value; } + if (negate_itembonus) { itembonuses.ForageAdditionalItems = effect_value; } break; case SE_Salvage: - spellbonuses.SalvageChance = effect_value; - aabonuses.SalvageChance = effect_value; - itembonuses.SalvageChance = effect_value; + if (negate_spellbonus) { spellbonuses.SalvageChance = effect_value; } + if (negate_aabonus) { aabonuses.SalvageChance = effect_value; } + if (negate_itembonus) { itembonuses.SalvageChance = effect_value; } break; case SE_ArcheryDamageModifier: - spellbonuses.ArcheryDamageModifier = effect_value; - aabonuses.ArcheryDamageModifier = effect_value; - itembonuses.ArcheryDamageModifier = effect_value; + if (negate_spellbonus) { spellbonuses.ArcheryDamageModifier = effect_value; } + if (negate_aabonus) { aabonuses.ArcheryDamageModifier = effect_value; } + if (negate_itembonus) { itembonuses.ArcheryDamageModifier = effect_value; } break; case SE_SecondaryDmgInc: - spellbonuses.SecondaryDmgInc = false; - aabonuses.SecondaryDmgInc = false; - itembonuses.SecondaryDmgInc = false; + if (negate_spellbonus) { spellbonuses.SecondaryDmgInc = false; } + if (negate_aabonus) { aabonuses.SecondaryDmgInc = false; } + if (negate_itembonus) { itembonuses.SecondaryDmgInc = false; } break; case SE_StrikeThrough: - spellbonuses.StrikeThrough = effect_value; - aabonuses.StrikeThrough = effect_value; - itembonuses.StrikeThrough = effect_value; + if (negate_spellbonus) { spellbonuses.StrikeThrough = effect_value; } + if (negate_aabonus) { aabonuses.StrikeThrough = effect_value; } + if (negate_itembonus) { itembonuses.StrikeThrough = effect_value; } break; case SE_StrikeThrough2: - spellbonuses.StrikeThrough = effect_value; - aabonuses.StrikeThrough = effect_value; - itembonuses.StrikeThrough = effect_value; + if (negate_spellbonus) { spellbonuses.StrikeThrough = effect_value; } + if (negate_aabonus) { aabonuses.StrikeThrough = effect_value; } + if (negate_itembonus) { itembonuses.StrikeThrough = effect_value; } break; case SE_GiveDoubleAttack: - spellbonuses.GiveDoubleAttack = effect_value; - aabonuses.GiveDoubleAttack = effect_value; - itembonuses.GiveDoubleAttack = effect_value; + if (negate_spellbonus) { spellbonuses.GiveDoubleAttack = effect_value; } + if (negate_aabonus) { aabonuses.GiveDoubleAttack = effect_value; } + if (negate_itembonus) { itembonuses.GiveDoubleAttack = effect_value; } break; case SE_PetCriticalHit: - spellbonuses.PetCriticalHit = effect_value; - aabonuses.PetCriticalHit = effect_value; - itembonuses.PetCriticalHit = effect_value; + if (negate_spellbonus) { spellbonuses.PetCriticalHit = effect_value; } + if (negate_aabonus) { aabonuses.PetCriticalHit = effect_value; } + if (negate_itembonus) { itembonuses.PetCriticalHit = effect_value; } break; case SE_CombatStability: - spellbonuses.CombatStability = effect_value; - aabonuses.CombatStability = effect_value; - itembonuses.CombatStability = effect_value; + if (negate_spellbonus) { spellbonuses.CombatStability = effect_value; } + if (negate_aabonus) { aabonuses.CombatStability = effect_value; } + if (negate_itembonus) { itembonuses.CombatStability = effect_value; } break; case SE_PetAvoidance: - spellbonuses.PetAvoidance = effect_value; - aabonuses.PetAvoidance = effect_value; - itembonuses.PetAvoidance = effect_value; + if (negate_spellbonus) { spellbonuses.PetAvoidance = effect_value; } + if (negate_aabonus) { aabonuses.PetAvoidance = effect_value; } + if (negate_itembonus) { itembonuses.PetAvoidance = effect_value; } break; case SE_Ambidexterity: - spellbonuses.Ambidexterity = effect_value; - aabonuses.Ambidexterity = effect_value; - itembonuses.Ambidexterity = effect_value; + if (negate_spellbonus) { spellbonuses.Ambidexterity = effect_value; } + if (negate_aabonus) { aabonuses.Ambidexterity = effect_value; } + if (negate_itembonus) { itembonuses.Ambidexterity = effect_value; } break; case SE_PetMaxHP: - spellbonuses.PetMaxHP = effect_value; - aabonuses.PetMaxHP = effect_value; - itembonuses.PetMaxHP = effect_value; + if (negate_spellbonus) { spellbonuses.PetMaxHP = effect_value; } + if (negate_aabonus) { aabonuses.PetMaxHP = effect_value; } + if (negate_itembonus) { itembonuses.PetMaxHP = effect_value; } break; case SE_PetFlurry: - spellbonuses.PetFlurry = effect_value; - aabonuses.PetFlurry = effect_value; - itembonuses.PetFlurry = effect_value; + if (negate_spellbonus) { spellbonuses.PetFlurry = effect_value; } + if (negate_aabonus) { aabonuses.PetFlurry = effect_value; } + if (negate_itembonus) { itembonuses.PetFlurry = effect_value; } break; case SE_GivePetGroupTarget: - spellbonuses.GivePetGroupTarget = false; - aabonuses.GivePetGroupTarget = false; - itembonuses.GivePetGroupTarget = false; + if (negate_spellbonus) { spellbonuses.GivePetGroupTarget = false; } + if (negate_aabonus) { aabonuses.GivePetGroupTarget = false; } + if (negate_itembonus) { itembonuses.GivePetGroupTarget = false; } break; case SE_PetMeleeMitigation: - spellbonuses.PetMeleeMitigation = effect_value; - itembonuses.PetMeleeMitigation = effect_value; - aabonuses.PetMeleeMitigation = effect_value; + if (negate_spellbonus) { spellbonuses.PetMeleeMitigation = effect_value; } + if (negate_itembonus) { itembonuses.PetMeleeMitigation = effect_value; } + if (negate_aabonus) { aabonuses.PetMeleeMitigation = effect_value; } break; case SE_RootBreakChance: - spellbonuses.RootBreakChance = effect_value; - aabonuses.RootBreakChance = effect_value; - itembonuses.RootBreakChance = effect_value; + if (negate_spellbonus) { spellbonuses.RootBreakChance = effect_value; } + if (negate_aabonus) { aabonuses.RootBreakChance = effect_value; } + if (negate_itembonus) { itembonuses.RootBreakChance = effect_value; } break; case SE_ChannelChanceItems: - spellbonuses.ChannelChanceItems = effect_value; - aabonuses.ChannelChanceItems = effect_value; - itembonuses.ChannelChanceItems = effect_value; + if (negate_spellbonus) { spellbonuses.ChannelChanceItems = effect_value; } + if (negate_aabonus) { aabonuses.ChannelChanceItems = effect_value; } + if (negate_itembonus) { itembonuses.ChannelChanceItems = effect_value; } break; case SE_ChannelChanceSpells: - spellbonuses.ChannelChanceSpells = effect_value; - aabonuses.ChannelChanceSpells = effect_value; - itembonuses.ChannelChanceSpells = effect_value; + if (negate_spellbonus) { spellbonuses.ChannelChanceSpells = effect_value; } + if (negate_aabonus) { aabonuses.ChannelChanceSpells = effect_value; } + if (negate_itembonus) { itembonuses.ChannelChanceSpells = effect_value; } break; case SE_UnfailingDivinity: - spellbonuses.UnfailingDivinity = effect_value; - aabonuses.UnfailingDivinity = effect_value; - itembonuses.UnfailingDivinity = effect_value; + if (negate_spellbonus) { spellbonuses.UnfailingDivinity = effect_value; } + if (negate_aabonus) { aabonuses.UnfailingDivinity = effect_value; } + if (negate_itembonus) { itembonuses.UnfailingDivinity = effect_value; } break; case SE_ItemHPRegenCapIncrease: - spellbonuses.ItemHPRegenCap = effect_value; - aabonuses.ItemHPRegenCap = effect_value; - itembonuses.ItemHPRegenCap = effect_value; + if (negate_spellbonus) { spellbonuses.ItemHPRegenCap = effect_value; } + if (negate_aabonus) { aabonuses.ItemHPRegenCap = effect_value; } + if (negate_itembonus) { itembonuses.ItemHPRegenCap = effect_value; } break; case SE_Worn_Endurance_Regen_Cap: - spellbonuses.ItemEnduranceRegenCap = effect_value; - aabonuses.ItemEnduranceRegenCap = effect_value; - itembonuses.ItemEnduranceRegenCap = effect_value; + if (negate_spellbonus) { spellbonuses.ItemEnduranceRegenCap = effect_value; } + if (negate_aabonus) { aabonuses.ItemEnduranceRegenCap = effect_value; } + if (negate_itembonus) { itembonuses.ItemEnduranceRegenCap = effect_value; } break; case SE_OffhandRiposteFail: - spellbonuses.OffhandRiposteFail = effect_value; - aabonuses.OffhandRiposteFail = effect_value; - itembonuses.OffhandRiposteFail = effect_value; + if (negate_spellbonus) { spellbonuses.OffhandRiposteFail = effect_value; } + if (negate_aabonus) { aabonuses.OffhandRiposteFail = effect_value; } + if (negate_itembonus) { itembonuses.OffhandRiposteFail = effect_value; } break; case SE_ItemAttackCapIncrease: - aabonuses.ItemATKCap = effect_value; - itembonuses.ItemATKCap = effect_value; - spellbonuses.ItemATKCap = effect_value; + if (negate_aabonus) { aabonuses.ItemATKCap = effect_value; } + if (negate_itembonus) { itembonuses.ItemATKCap = effect_value; } + if (negate_spellbonus) { spellbonuses.ItemATKCap = effect_value; } break; case SE_SpellEffectResistChance: { - for(int e = 0; e < MAX_RESISTABLE_EFFECTS*2; e+=2) + for (int e = 0; e < MAX_RESISTABLE_EFFECTS * 2; e += 2) { - spellbonuses.SEResist[e] = effect_value; - spellbonuses.SEResist[e+1] = effect_value; + if (negate_spellbonus) { spellbonuses.SEResist[e] = effect_value; } + if (negate_spellbonus) { spellbonuses.SEResist[e + 1] = effect_value; } } break; } case SE_MasteryofPast: - spellbonuses.MasteryofPast = effect_value; - aabonuses.MasteryofPast = effect_value; - itembonuses.MasteryofPast = effect_value; + if (negate_spellbonus) { spellbonuses.MasteryofPast = effect_value; } + if (negate_aabonus) { aabonuses.MasteryofPast = effect_value; } + if (negate_itembonus) { itembonuses.MasteryofPast = effect_value; } break; case SE_DoubleRiposte: - spellbonuses.DoubleRiposte = effect_value; - itembonuses.DoubleRiposte = effect_value; - aabonuses.DoubleRiposte = effect_value; + if (negate_spellbonus) { spellbonuses.DoubleRiposte = effect_value; } + if (negate_itembonus) { itembonuses.DoubleRiposte = effect_value; } + if (negate_aabonus) { aabonuses.DoubleRiposte = effect_value; } break; case SE_GiveDoubleRiposte: - spellbonuses.GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_CHANCE] = effect_value; - itembonuses.GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_CHANCE] = effect_value; - aabonuses.GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_CHANCE] = effect_value; + if (negate_spellbonus) { spellbonuses.GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_CHANCE] = effect_value; } + if (negate_itembonus) { itembonuses.GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_CHANCE] = effect_value; } + if (negate_aabonus) { aabonuses.GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_CHANCE] = effect_value; } break; case SE_SlayUndead: - spellbonuses.SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] = effect_value; - spellbonuses.SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = effect_value; - itembonuses.SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] = effect_value; - itembonuses.SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = effect_value; - aabonuses.SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] = effect_value; - aabonuses.SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = effect_value; + if (negate_spellbonus) { spellbonuses.SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] = effect_value; } + if (negate_spellbonus) { spellbonuses.SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = effect_value; } + if (negate_itembonus) { itembonuses.SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] = effect_value; } + if (negate_itembonus) { itembonuses.SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = effect_value; } + if (negate_aabonus) { aabonuses.SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] = effect_value; } + if (negate_aabonus) { aabonuses.SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = effect_value; } break; case SE_DoubleRangedAttack: - spellbonuses.DoubleRangedAttack = effect_value; - aabonuses.DoubleRangedAttack = effect_value; - itembonuses.DoubleRangedAttack = effect_value; + if (negate_spellbonus) { spellbonuses.DoubleRangedAttack = effect_value; } + if (negate_aabonus) { aabonuses.DoubleRangedAttack = effect_value; } + if (negate_itembonus) { itembonuses.DoubleRangedAttack = effect_value; } break; case SE_ShieldEquipDmgMod: - spellbonuses.ShieldEquipDmgMod = effect_value; - aabonuses.ShieldEquipDmgMod = effect_value; - itembonuses.ShieldEquipDmgMod = effect_value; + if (negate_spellbonus) { spellbonuses.ShieldEquipDmgMod = effect_value; } + if (negate_aabonus) { aabonuses.ShieldEquipDmgMod = effect_value; } + if (negate_itembonus) { itembonuses.ShieldEquipDmgMod = effect_value; } break; case SE_TriggerMeleeThreshold: - spellbonuses.TriggerMeleeThreshold = false; + if (negate_spellbonus) { spellbonuses.TriggerMeleeThreshold = false; } break; case SE_TriggerSpellThreshold: - spellbonuses.TriggerSpellThreshold = false; + if (negate_spellbonus) { spellbonuses.TriggerSpellThreshold = false; } break; case SE_DivineAura: - spellbonuses.DivineAura = false; + if (negate_spellbonus) { spellbonuses.DivineAura = false; } break; case SE_StunBashChance: - spellbonuses.StunBashChance = effect_value; - itembonuses.StunBashChance = effect_value; - aabonuses.StunBashChance = effect_value; + if (negate_spellbonus) { spellbonuses.StunBashChance = effect_value; } + if (negate_itembonus) { itembonuses.StunBashChance = effect_value; } + if (negate_aabonus) { aabonuses.StunBashChance = effect_value; } break; case SE_IncreaseChanceMemwipe: - spellbonuses.IncreaseChanceMemwipe = effect_value; - itembonuses.IncreaseChanceMemwipe = effect_value; - aabonuses.IncreaseChanceMemwipe = effect_value; + if (negate_spellbonus) { spellbonuses.IncreaseChanceMemwipe = effect_value; } + if (negate_itembonus) { itembonuses.IncreaseChanceMemwipe = effect_value; } + if (negate_aabonus) { aabonuses.IncreaseChanceMemwipe = effect_value; } break; case SE_CriticalMend: - spellbonuses.CriticalMend = effect_value; - itembonuses.CriticalMend = effect_value; - aabonuses.CriticalMend = effect_value; + if (negate_spellbonus) { spellbonuses.CriticalMend = effect_value; } + if (negate_itembonus) { itembonuses.CriticalMend = effect_value; } + if (negate_aabonus) { aabonuses.CriticalMend = effect_value; } break; case SE_DistanceRemoval: - spellbonuses.DistanceRemoval = false; + if (negate_spellbonus) { spellbonuses.DistanceRemoval = false; } break; case SE_ImprovedTaunt: - spellbonuses.ImprovedTaunt[SBIndex::IMPROVED_TAUNT_MAX_LVL] = effect_value; - spellbonuses.ImprovedTaunt[SBIndex::IMPROVED_TAUNT_AGGRO_MOD] = effect_value; - spellbonuses.ImprovedTaunt[SBIndex::IMPROVED_TAUNT_BUFFSLOT] = -1; + if (negate_spellbonus) { spellbonuses.ImprovedTaunt[SBIndex::IMPROVED_TAUNT_MAX_LVL] = effect_value; } + if (negate_spellbonus) { spellbonuses.ImprovedTaunt[SBIndex::IMPROVED_TAUNT_AGGRO_MOD] = effect_value; } + if (negate_spellbonus) { spellbonuses.ImprovedTaunt[SBIndex::IMPROVED_TAUNT_BUFFSLOT] = -1; } break; case SE_FrenziedDevastation: - spellbonuses.FrenziedDevastation = effect_value; - aabonuses.FrenziedDevastation = effect_value; - itembonuses.FrenziedDevastation = effect_value; + if (negate_spellbonus) { spellbonuses.FrenziedDevastation = effect_value; } + if (negate_aabonus) { aabonuses.FrenziedDevastation = effect_value; } + if (negate_itembonus) { itembonuses.FrenziedDevastation = effect_value; } break; case SE_Root: - spellbonuses.Root[SBIndex::ROOT_EXISTS] = effect_value; - spellbonuses.Root[SBIndex::ROOT_BUFFSLOT] = -1; + if (negate_spellbonus) { spellbonuses.Root[SBIndex::ROOT_EXISTS] = effect_value; } + if (negate_spellbonus) { spellbonuses.Root[SBIndex::ROOT_BUFFSLOT] = -1; } break; case SE_Rune: - spellbonuses.MeleeRune[SBIndex::RUNE_AMOUNT] = effect_value; - spellbonuses.MeleeRune[SBIndex::RUNE_BUFFSLOT] = -1; + if (negate_spellbonus) { spellbonuses.MeleeRune[SBIndex::RUNE_AMOUNT] = effect_value; } + if (negate_spellbonus) { spellbonuses.MeleeRune[SBIndex::RUNE_BUFFSLOT] = -1; } break; case SE_AbsorbMagicAtt: - spellbonuses.AbsorbMagicAtt[SBIndex::RUNE_AMOUNT] = effect_value; - spellbonuses.AbsorbMagicAtt[SBIndex::RUNE_BUFFSLOT] = -1; + if (negate_spellbonus) { spellbonuses.AbsorbMagicAtt[SBIndex::RUNE_AMOUNT] = effect_value; } + if (negate_spellbonus) { spellbonuses.AbsorbMagicAtt[SBIndex::RUNE_BUFFSLOT] = -1; } break; case SE_Berserk: - spellbonuses.BerserkSPA = false; - aabonuses.BerserkSPA = false; - itembonuses.BerserkSPA = false; + if (negate_spellbonus) { spellbonuses.BerserkSPA = false; } + if (negate_aabonus) { aabonuses.BerserkSPA = false; } + if (negate_itembonus) { itembonuses.BerserkSPA = false; } break; case SE_Vampirism: - spellbonuses.Vampirism = effect_value; - aabonuses.Vampirism = effect_value; - itembonuses.Vampirism = effect_value; + if (negate_spellbonus) { spellbonuses.Vampirism = effect_value; } + if (negate_aabonus) { aabonuses.Vampirism = effect_value; } + if (negate_itembonus) { itembonuses.Vampirism = effect_value; } break; case SE_Metabolism: - spellbonuses.Metabolism = effect_value; - aabonuses.Metabolism = effect_value; - itembonuses.Metabolism = effect_value; + if (negate_spellbonus) { spellbonuses.Metabolism = effect_value; } + if (negate_aabonus) { aabonuses.Metabolism = effect_value; } + if (negate_itembonus) { itembonuses.Metabolism = effect_value; } break; case SE_ImprovedReclaimEnergy: - spellbonuses.ImprovedReclaimEnergy = effect_value; - aabonuses.ImprovedReclaimEnergy = effect_value; - itembonuses.ImprovedReclaimEnergy = effect_value; + if (negate_spellbonus) { spellbonuses.ImprovedReclaimEnergy = effect_value; } + if (negate_aabonus) { aabonuses.ImprovedReclaimEnergy = effect_value; } + if (negate_itembonus) { itembonuses.ImprovedReclaimEnergy = effect_value; } break; case SE_HeadShot: - spellbonuses.HeadShot[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; - aabonuses.HeadShot[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; - itembonuses.HeadShot[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; - spellbonuses.HeadShot[SBIndex::FINISHING_EFFECT_DMG] = effect_value; - aabonuses.HeadShot[SBIndex::FINISHING_EFFECT_DMG] = effect_value; - itembonuses.HeadShot[SBIndex::FINISHING_EFFECT_DMG] = effect_value; + if (negate_spellbonus) { spellbonuses.HeadShot[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; } + if (negate_aabonus) { aabonuses.HeadShot[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; } + if (negate_itembonus) { itembonuses.HeadShot[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; } + if (negate_spellbonus) { spellbonuses.HeadShot[SBIndex::FINISHING_EFFECT_DMG] = effect_value; } + if (negate_aabonus) { aabonuses.HeadShot[SBIndex::FINISHING_EFFECT_DMG] = effect_value; } + if (negate_itembonus) { itembonuses.HeadShot[SBIndex::FINISHING_EFFECT_DMG] = effect_value; } break; case SE_HeadShotLevel: - spellbonuses.HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; - aabonuses.HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; - itembonuses.HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; - spellbonuses.HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; - aabonuses.HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; - itembonuses.HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; + if (negate_spellbonus) { spellbonuses.HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; } + if (negate_aabonus) { aabonuses.HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; } + if (negate_itembonus) { itembonuses.HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; } + if (negate_spellbonus) { spellbonuses.HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; } + if (negate_aabonus) { aabonuses.HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; } + if (negate_itembonus) { itembonuses.HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; } break; case SE_Assassinate: - spellbonuses.Assassinate[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; - aabonuses.Assassinate[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; - itembonuses.Assassinate[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; - spellbonuses.Assassinate[SBIndex::FINISHING_EFFECT_DMG] = effect_value; - aabonuses.Assassinate[SBIndex::FINISHING_EFFECT_DMG] = effect_value; - itembonuses.Assassinate[SBIndex::FINISHING_EFFECT_DMG] = effect_value; + if (negate_spellbonus) { spellbonuses.Assassinate[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; } + if (negate_aabonus) { aabonuses.Assassinate[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; } + if (negate_itembonus) { itembonuses.Assassinate[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; } + if (negate_spellbonus) { spellbonuses.Assassinate[SBIndex::FINISHING_EFFECT_DMG] = effect_value; } + if (negate_aabonus) { aabonuses.Assassinate[SBIndex::FINISHING_EFFECT_DMG] = effect_value; } + if (negate_itembonus) { itembonuses.Assassinate[SBIndex::FINISHING_EFFECT_DMG] = effect_value; } break; case SE_AssassinateLevel: - spellbonuses.AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; - aabonuses.AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; - itembonuses.AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; - spellbonuses.AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; - aabonuses.AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; - itembonuses.AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; + if (negate_spellbonus) { spellbonuses.AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; } + if (negate_aabonus) { aabonuses.AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; } + if (negate_itembonus) { itembonuses.AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; } + if (negate_spellbonus) { spellbonuses.AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; } + if (negate_aabonus) { aabonuses.AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; } + if (negate_itembonus) { itembonuses.AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; } break; case SE_FinishingBlow: - spellbonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; - aabonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; - itembonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; - spellbonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] = effect_value; - aabonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] = effect_value; - itembonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] = effect_value; + if (negate_spellbonus) { spellbonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; } + if (negate_aabonus) { aabonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; } + if (negate_itembonus) { itembonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; } + if (negate_spellbonus) { spellbonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] = effect_value; } + if (negate_aabonus) { aabonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] = effect_value; } + if (negate_itembonus) { itembonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] = effect_value; } break; case SE_FinishingBlowLvl: - spellbonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; - aabonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; - itembonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; - spellbonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; - aabonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; - itembonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; + if (negate_spellbonus) { spellbonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; } + if (negate_aabonus) { aabonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; } + if (negate_itembonus) { itembonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; } + if (negate_spellbonus) { spellbonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; } + if (negate_aabonus) { aabonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; } + if (negate_itembonus) { itembonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; } break; case SE_Sanctuary: - spellbonuses.Sanctuary = false; + if (negate_spellbonus) { spellbonuses.Sanctuary = false; } break; case SE_FactionModPct: - spellbonuses.FactionModPct = effect_value; - itembonuses.FactionModPct = effect_value; - aabonuses.FactionModPct = effect_value; + if (negate_spellbonus) { spellbonuses.FactionModPct = effect_value; } + if (negate_itembonus) { itembonuses.FactionModPct = effect_value; } + if (negate_aabonus) { aabonuses.FactionModPct = effect_value; } break; case SE_IllusionPersistence: - spellbonuses.IllusionPersistence = false; - itembonuses.IllusionPersistence = false; - aabonuses.IllusionPersistence = false; + if (negate_spellbonus) { spellbonuses.IllusionPersistence = false; } + if (negate_itembonus) { itembonuses.IllusionPersistence = false; } + if (negate_aabonus) { aabonuses.IllusionPersistence = false; } break; case SE_Attack_Accuracy_Max_Percent: - spellbonuses.Attack_Accuracy_Max_Percent = effect_value; - itembonuses.Attack_Accuracy_Max_Percent = effect_value; - aabonuses.Attack_Accuracy_Max_Percent = effect_value; + if (negate_spellbonus) { spellbonuses.Attack_Accuracy_Max_Percent = effect_value; } + if (negate_itembonus) { itembonuses.Attack_Accuracy_Max_Percent = effect_value; } + if (negate_aabonus) { aabonuses.Attack_Accuracy_Max_Percent = effect_value; } break; case SE_AC_Mitigation_Max_Percent: - spellbonuses.AC_Mitigation_Max_Percent = effect_value; - itembonuses.AC_Mitigation_Max_Percent = effect_value; - aabonuses.AC_Mitigation_Max_Percent = effect_value; + if (negate_spellbonus) { spellbonuses.AC_Mitigation_Max_Percent = effect_value; } + if (negate_itembonus) { itembonuses.AC_Mitigation_Max_Percent = effect_value; } + if (negate_aabonus) { aabonuses.AC_Mitigation_Max_Percent = effect_value; } break; case SE_AC_Avoidance_Max_Percent: - spellbonuses.AC_Avoidance_Max_Percent = effect_value; - itembonuses.AC_Avoidance_Max_Percent = effect_value; - aabonuses.AC_Avoidance_Max_Percent = effect_value; + if (negate_spellbonus) { spellbonuses.AC_Avoidance_Max_Percent = effect_value; } + if (negate_itembonus) { itembonuses.AC_Avoidance_Max_Percent = effect_value; } + if (negate_aabonus) { aabonuses.AC_Avoidance_Max_Percent = effect_value; } break; case SE_Melee_Damage_Position_Mod: - spellbonuses.Melee_Damage_Position_Mod[SBIndex::POSITION_BACK] = effect_value; - aabonuses.Melee_Damage_Position_Mod[SBIndex::POSITION_BACK] = effect_value; - itembonuses.Melee_Damage_Position_Mod[SBIndex::POSITION_BACK] = effect_value; - spellbonuses.Melee_Damage_Position_Mod[SBIndex::POSITION_FRONT] = effect_value; - aabonuses.Melee_Damage_Position_Mod[SBIndex::POSITION_FRONT] = effect_value; - itembonuses.Melee_Damage_Position_Mod[SBIndex::POSITION_FRONT] = effect_value; + if (negate_spellbonus) { spellbonuses.Melee_Damage_Position_Mod[SBIndex::POSITION_BACK] = effect_value; } + if (negate_aabonus) { aabonuses.Melee_Damage_Position_Mod[SBIndex::POSITION_BACK] = effect_value; } + if (negate_itembonus) { itembonuses.Melee_Damage_Position_Mod[SBIndex::POSITION_BACK] = effect_value; } + if (negate_spellbonus) { spellbonuses.Melee_Damage_Position_Mod[SBIndex::POSITION_FRONT] = effect_value; } + if (negate_aabonus) { aabonuses.Melee_Damage_Position_Mod[SBIndex::POSITION_FRONT] = effect_value; } + if (negate_itembonus) { itembonuses.Melee_Damage_Position_Mod[SBIndex::POSITION_FRONT] = effect_value; } break; case SE_Damage_Taken_Position_Mod: - spellbonuses.Damage_Taken_Position_Mod[SBIndex::POSITION_BACK] = effect_value; - aabonuses.Damage_Taken_Position_Mod[SBIndex::POSITION_BACK] = effect_value; - itembonuses.Damage_Taken_Position_Mod[SBIndex::POSITION_BACK] = effect_value; - spellbonuses.Damage_Taken_Position_Mod[SBIndex::POSITION_FRONT] = effect_value; - aabonuses.Damage_Taken_Position_Mod[SBIndex::POSITION_FRONT] = effect_value; - itembonuses.Damage_Taken_Position_Mod[SBIndex::POSITION_FRONT] = effect_value; + if (negate_spellbonus) { spellbonuses.Damage_Taken_Position_Mod[SBIndex::POSITION_BACK] = effect_value; } + if (negate_aabonus) { aabonuses.Damage_Taken_Position_Mod[SBIndex::POSITION_BACK] = effect_value; } + if (negate_itembonus) { itembonuses.Damage_Taken_Position_Mod[SBIndex::POSITION_BACK] = effect_value; } + if (negate_spellbonus) { spellbonuses.Damage_Taken_Position_Mod[SBIndex::POSITION_FRONT] = effect_value; } + if (negate_aabonus) { aabonuses.Damage_Taken_Position_Mod[SBIndex::POSITION_FRONT] = effect_value; } + if (negate_itembonus) { itembonuses.Damage_Taken_Position_Mod[SBIndex::POSITION_FRONT] = effect_value; } break; case SE_Melee_Damage_Position_Amt: - spellbonuses.Melee_Damage_Position_Amt[SBIndex::POSITION_BACK] = effect_value; - aabonuses.Melee_Damage_Position_Amt[SBIndex::POSITION_BACK] = effect_value; - itembonuses.Melee_Damage_Position_Amt[SBIndex::POSITION_BACK] = effect_value; - spellbonuses.Melee_Damage_Position_Amt[SBIndex::POSITION_FRONT] = effect_value; - aabonuses.Melee_Damage_Position_Amt[SBIndex::POSITION_FRONT] = effect_value; - itembonuses.Melee_Damage_Position_Amt[SBIndex::POSITION_FRONT] = effect_value; + if (negate_spellbonus) { spellbonuses.Melee_Damage_Position_Amt[SBIndex::POSITION_BACK] = effect_value; } + if (negate_aabonus) { aabonuses.Melee_Damage_Position_Amt[SBIndex::POSITION_BACK] = effect_value; } + if (negate_itembonus) { itembonuses.Melee_Damage_Position_Amt[SBIndex::POSITION_BACK] = effect_value; } + if (negate_spellbonus) { spellbonuses.Melee_Damage_Position_Amt[SBIndex::POSITION_FRONT] = effect_value; } + if (negate_aabonus) { aabonuses.Melee_Damage_Position_Amt[SBIndex::POSITION_FRONT] = effect_value; } + if (negate_itembonus) { itembonuses.Melee_Damage_Position_Amt[SBIndex::POSITION_FRONT] = effect_value; } break; case SE_Damage_Taken_Position_Amt: - spellbonuses.Damage_Taken_Position_Amt[SBIndex::POSITION_BACK] = effect_value; - aabonuses.Damage_Taken_Position_Amt[SBIndex::POSITION_BACK] = effect_value; - itembonuses.Damage_Taken_Position_Amt[SBIndex::POSITION_BACK] = effect_value; - spellbonuses.Damage_Taken_Position_Amt[SBIndex::POSITION_FRONT] = effect_value; - aabonuses.Damage_Taken_Position_Amt[SBIndex::POSITION_FRONT] = effect_value; - itembonuses.Damage_Taken_Position_Amt[SBIndex::POSITION_FRONT] = effect_value; + if (negate_spellbonus) { spellbonuses.Damage_Taken_Position_Amt[SBIndex::POSITION_BACK] = effect_value; } + if (negate_aabonus) { aabonuses.Damage_Taken_Position_Amt[SBIndex::POSITION_BACK] = effect_value; } + if (negate_itembonus) { itembonuses.Damage_Taken_Position_Amt[SBIndex::POSITION_BACK] = effect_value; } + if (negate_spellbonus) { spellbonuses.Damage_Taken_Position_Amt[SBIndex::POSITION_FRONT] = effect_value; } + if (negate_aabonus) { aabonuses.Damage_Taken_Position_Amt[SBIndex::POSITION_FRONT] = effect_value; } + if (negate_itembonus) { itembonuses.Damage_Taken_Position_Amt[SBIndex::POSITION_FRONT] = effect_value; } break; case SE_DS_Mitigation_Amount: - spellbonuses.DS_Mitigation_Amount = effect_value; - itembonuses.DS_Mitigation_Amount = effect_value; - aabonuses.DS_Mitigation_Amount = effect_value; + if (negate_spellbonus) { spellbonuses.DS_Mitigation_Amount = effect_value; } + if (negate_itembonus) { itembonuses.DS_Mitigation_Amount = effect_value; } + if (negate_aabonus) { aabonuses.DS_Mitigation_Amount = effect_value; } break; case SE_DS_Mitigation_Percentage: - spellbonuses.DS_Mitigation_Percentage = effect_value; - itembonuses.DS_Mitigation_Percentage = effect_value; - aabonuses.DS_Mitigation_Percentage = effect_value; + if (negate_spellbonus) { spellbonuses.DS_Mitigation_Percentage = effect_value; } + if (negate_itembonus) { itembonuses.DS_Mitigation_Percentage = effect_value; } + if (negate_aabonus) { aabonuses.DS_Mitigation_Percentage = effect_value; } break; case SE_Pet_Crit_Melee_Damage_Pct_Owner: - spellbonuses.Pet_Crit_Melee_Damage_Pct_Owner = effect_value; - itembonuses.Pet_Crit_Melee_Damage_Pct_Owner = effect_value; - aabonuses.Pet_Crit_Melee_Damage_Pct_Owner = effect_value; + if (negate_spellbonus) { spellbonuses.Pet_Crit_Melee_Damage_Pct_Owner = effect_value; } + if (negate_itembonus) { itembonuses.Pet_Crit_Melee_Damage_Pct_Owner = effect_value; } + if (negate_aabonus) { aabonuses.Pet_Crit_Melee_Damage_Pct_Owner = effect_value; } break; case SE_Pet_Add_Atk: - spellbonuses.Pet_Add_Atk = effect_value; - itembonuses.Pet_Add_Atk = effect_value; - aabonuses.Pet_Add_Atk = effect_value; + if (negate_spellbonus) { spellbonuses.Pet_Add_Atk = effect_value; } + if (negate_itembonus) { itembonuses.Pet_Add_Atk = effect_value; } + if (negate_aabonus) { aabonuses.Pet_Add_Atk = effect_value; } break; case SE_PC_Pet_Rampage: - spellbonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_CHANCE] = effect_value; - itembonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_CHANCE] = effect_value; - aabonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_CHANCE] = effect_value; - spellbonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = effect_value; - itembonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = effect_value; - aabonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = effect_value; + if (negate_spellbonus) { spellbonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_CHANCE] = effect_value; } + if (negate_itembonus) { itembonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_CHANCE] = effect_value; } + if (negate_aabonus) { aabonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_CHANCE] = effect_value; } + if (negate_spellbonus) { spellbonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = effect_value; } + if (negate_itembonus) { itembonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = effect_value; } + if (negate_aabonus) { aabonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = effect_value; } break; case SE_PC_Pet_AE_Rampage: - spellbonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_CHANCE] = effect_value; - itembonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_CHANCE] = effect_value; - aabonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_CHANCE] = effect_value; - spellbonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = effect_value; - itembonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = effect_value; - aabonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = effect_value; + if (negate_spellbonus) { spellbonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_CHANCE] = effect_value; } + if (negate_itembonus) { itembonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_CHANCE] = effect_value; } + if (negate_aabonus) { aabonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_CHANCE] = effect_value; } + if (negate_spellbonus) { spellbonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = effect_value; } + if (negate_itembonus) { itembonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = effect_value; } + if (negate_aabonus) { aabonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = effect_value; } break; - case SE_SkillProcSuccess:{ - for(int e = 0; e < MAX_SKILL_PROCS; e++) + case SE_SkillProcSuccess: { + for (int e = 0; e < MAX_SKILL_PROCS; e++) { - spellbonuses.SkillProcSuccess[e] = effect_value; - itembonuses.SkillProcSuccess[e] = effect_value; - aabonuses.SkillProcSuccess[e] = effect_value; + if (negate_spellbonus) { spellbonuses.SkillProcSuccess[e] = effect_value; } + if (negate_itembonus) { itembonuses.SkillProcSuccess[e] = effect_value; } + if (negate_aabonus) { aabonuses.SkillProcSuccess[e] = effect_value; } } - } + } - case SE_SkillProc:{ - for(int e = 0; e < MAX_SKILL_PROCS; e++) + case SE_SkillProc: { + for (int e = 0; e < MAX_SKILL_PROCS; e++) { - spellbonuses.SkillProc[e] = effect_value; - itembonuses.SkillProc[e] = effect_value; - aabonuses.SkillProc[e] = effect_value; + if (negate_spellbonus) { spellbonuses.SkillProc[e] = effect_value; } + if (negate_itembonus) { itembonuses.SkillProc[e] = effect_value; } + if (negate_aabonus) { aabonuses.SkillProc[e] = effect_value; } } - } + } } } } } - diff --git a/zone/mob.h b/zone/mob.h index 1a3bffc4f..339bd1ce0 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -297,7 +297,7 @@ public: void ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* newbon, uint16 casterID = 0, uint8 WornType = 0, int32 ticsremaining = 0, int buffslot = -1, int instrument_mod = 10, bool IsAISpellEffect = false, uint16 effect_id = 0, int32 se_base = 0, int32 se_limit = 0, int32 se_max = 0); - void NegateSpellsBonuses(uint16 spell_id); + void NegateSpellEffectBonuses(uint16 spell_id); virtual float GetActSpellRange(uint16 spell_id, float range, bool IsBard = false); virtual int32 GetActSpellDamage(uint16 spell_id, int32 value, Mob* target = nullptr); virtual int32 GetActDoTDamage(uint16 spell_id, int32 value, Mob* target); From af6d344e12b6a3fe837a4bc746811a10f55c7bfb Mon Sep 17 00:00:00 2001 From: Logan Date: Fri, 3 Sep 2021 18:15:24 -0700 Subject: [PATCH 173/624] [Mods] Added Hastev3Cap (#1506) * Added Hastev3Cap * Added Hastev3Cap rule --- common/ruletypes.h | 1 + zone/client_mods.cpp | 7 ++++++- zone/mob.cpp | 13 +++++++++---- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index a86764570..906b9d867 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -91,6 +91,7 @@ RULE_INT(Character, ItemDSMitigationCap, 50, "Limit on damageshield mitigation g RULE_INT(Character, ItemEnduranceRegenCap, 15, "Limit on endurance regeneration granted by items") RULE_INT(Character, ItemExtraDmgCap, 150, "Cap for bonuses to melee skills like Bash, Frenzy, etc.") RULE_INT(Character, HasteCap, 100, "Haste cap for non-v3(over haste) haste") +RULE_INT(Character, Hastev3Cap, 25, "Haste cap for v3(over haste) haste") RULE_INT(Character, SkillUpModifier, 100, "The probability for a skill-up is multiplied by value/100") RULE_BOOL(Character, SharedBankPlat, false, "Shared bank platinum. Off by default to prevent duplication") RULE_BOOL(Character, BindAnywhere, false, "Allows players to bind their soul anywhere in the world") diff --git a/zone/client_mods.cpp b/zone/client_mods.cpp index f51406150..a1eea7154 100644 --- a/zone/client_mods.cpp +++ b/zone/client_mods.cpp @@ -1054,7 +1054,12 @@ int Client::CalcHaste() } // 51+ 25 (despite there being higher spells...), 1-50 10 if (level > 50) { // 51+ - h += spellbonuses.hastetype3 > 25 ? 25 : spellbonuses.hastetype3; + cap = RuleI(Character, Hastev3Cap); + if (spellbonuses.hastetype3 > cap) { + h += cap; + } else { + h += spellbonuses.hastetype3; + } } else { // 1-50 h += spellbonuses.hastetype3 > 10 ? 10 : spellbonuses.hastetype3; diff --git a/zone/mob.cpp b/zone/mob.cpp index 58ac71d21..e46aba587 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3235,11 +3235,16 @@ int Mob::GetHaste() h = cap; // 51+ 25 (despite there being higher spells...), 1-50 10 - if (level > 50) // 51+ - h += spellbonuses.hastetype3 > 25 ? 25 : spellbonuses.hastetype3; - else // 1-50 + if (level > 50) { // 51+ + cap = RuleI(Character, Hastev3Cap); + if (spellbonuses.hastetype3 > cap) { + h += cap; + } else { + h += spellbonuses.hastetype3; + } + } else { // 1-50 h += spellbonuses.hastetype3 > 10 ? 10 : spellbonuses.hastetype3; - + } h += ExtraHaste; //GM granted haste. return 100 + h; From 41352f77ae2fe9d0e782022d55104a643ea87d2e Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 3 Sep 2021 21:19:39 -0400 Subject: [PATCH 174/624] [Spells] Implement PVP resist and duration overrides (#1513) * Make use of PVP resist field * Implement PVP duration formulas --- .../base/base_spells_new_repository.h | 36 +++++++++---------- common/shareddb.cpp | 2 ++ common/spdat.h | 4 +-- common/version.h | 2 +- utils/sql/db_update_manifest.txt | 1 + .../git/required/2021_08_31_pvp_duration.sql | 2 ++ zone/lua_spell.cpp | 12 +++++++ zone/lua_spell.h | 2 ++ zone/spells.cpp | 23 ++++++++++-- 9 files changed, 60 insertions(+), 24 deletions(-) create mode 100644 utils/sql/git/required/2021_08_31_pvp_duration.sql diff --git a/common/repositories/base/base_spells_new_repository.h b/common/repositories/base/base_spells_new_repository.h index e273667ad..111140f27 100644 --- a/common/repositories/base/base_spells_new_repository.h +++ b/common/repositories/base/base_spells_new_repository.h @@ -199,8 +199,8 @@ public: int pvpresistcalc; int pvpresistcap; int spell_category; - int field181; - int field182; + int pvp_duration; + int pvp_duration_cap; int pcnpc_only_flag; int cast_not_standing; int can_mgb; @@ -446,8 +446,8 @@ public: "pvpresistcalc", "pvpresistcap", "spell_category", - "field181", - "field182", + "pvp_duration", + "pvp_duration_cap", "pcnpc_only_flag", "cast_not_standing", "can_mgb", @@ -718,8 +718,8 @@ public: entry.pvpresistcalc = 100; entry.pvpresistcap = -150; entry.spell_category = -99; - entry.field181 = 7; - entry.field182 = 65; + entry.pvp_duration = 0; + entry.pvp_duration_cap = 0; entry.pcnpc_only_flag = 0; entry.cast_not_standing = 0; entry.can_mgb = 0; @@ -990,8 +990,8 @@ public: entry.pvpresistcalc = atoi(row[178]); entry.pvpresistcap = atoi(row[179]); entry.spell_category = atoi(row[180]); - entry.field181 = atoi(row[181]); - entry.field182 = atoi(row[182]); + entry.pvp_duration = atoi(row[181]); + entry.pvp_duration_cap = atoi(row[182]); entry.pcnpc_only_flag = atoi(row[183]); entry.cast_not_standing = atoi(row[184]); entry.can_mgb = atoi(row[185]); @@ -1260,8 +1260,8 @@ public: update_values.push_back(columns[178] + " = " + std::to_string(spells_new_entry.pvpresistcalc)); update_values.push_back(columns[179] + " = " + std::to_string(spells_new_entry.pvpresistcap)); update_values.push_back(columns[180] + " = " + std::to_string(spells_new_entry.spell_category)); - update_values.push_back(columns[181] + " = " + std::to_string(spells_new_entry.field181)); - update_values.push_back(columns[182] + " = " + std::to_string(spells_new_entry.field182)); + update_values.push_back(columns[181] + " = " + std::to_string(spells_new_entry.pvp_duration)); + update_values.push_back(columns[182] + " = " + std::to_string(spells_new_entry.pvp_duration_cap)); update_values.push_back(columns[183] + " = " + std::to_string(spells_new_entry.pcnpc_only_flag)); update_values.push_back(columns[184] + " = " + std::to_string(spells_new_entry.cast_not_standing)); update_values.push_back(columns[185] + " = " + std::to_string(spells_new_entry.can_mgb)); @@ -1518,8 +1518,8 @@ public: insert_values.push_back(std::to_string(spells_new_entry.pvpresistcalc)); insert_values.push_back(std::to_string(spells_new_entry.pvpresistcap)); insert_values.push_back(std::to_string(spells_new_entry.spell_category)); - insert_values.push_back(std::to_string(spells_new_entry.field181)); - insert_values.push_back(std::to_string(spells_new_entry.field182)); + insert_values.push_back(std::to_string(spells_new_entry.pvp_duration)); + insert_values.push_back(std::to_string(spells_new_entry.pvp_duration_cap)); insert_values.push_back(std::to_string(spells_new_entry.pcnpc_only_flag)); insert_values.push_back(std::to_string(spells_new_entry.cast_not_standing)); insert_values.push_back(std::to_string(spells_new_entry.can_mgb)); @@ -1784,8 +1784,8 @@ public: insert_values.push_back(std::to_string(spells_new_entry.pvpresistcalc)); insert_values.push_back(std::to_string(spells_new_entry.pvpresistcap)); insert_values.push_back(std::to_string(spells_new_entry.spell_category)); - insert_values.push_back(std::to_string(spells_new_entry.field181)); - insert_values.push_back(std::to_string(spells_new_entry.field182)); + insert_values.push_back(std::to_string(spells_new_entry.pvp_duration)); + insert_values.push_back(std::to_string(spells_new_entry.pvp_duration_cap)); insert_values.push_back(std::to_string(spells_new_entry.pcnpc_only_flag)); insert_values.push_back(std::to_string(spells_new_entry.cast_not_standing)); insert_values.push_back(std::to_string(spells_new_entry.can_mgb)); @@ -2054,8 +2054,8 @@ public: entry.pvpresistcalc = atoi(row[178]); entry.pvpresistcap = atoi(row[179]); entry.spell_category = atoi(row[180]); - entry.field181 = atoi(row[181]); - entry.field182 = atoi(row[182]); + entry.pvp_duration = atoi(row[181]); + entry.pvp_duration_cap = atoi(row[182]); entry.pcnpc_only_flag = atoi(row[183]); entry.cast_not_standing = atoi(row[184]); entry.can_mgb = atoi(row[185]); @@ -2315,8 +2315,8 @@ public: entry.pvpresistcalc = atoi(row[178]); entry.pvpresistcap = atoi(row[179]); entry.spell_category = atoi(row[180]); - entry.field181 = atoi(row[181]); - entry.field182 = atoi(row[182]); + entry.pvp_duration = atoi(row[181]); + entry.pvp_duration_cap = atoi(row[182]); entry.pcnpc_only_flag = atoi(row[183]); entry.cast_not_standing = atoi(row[184]); entry.can_mgb = atoi(row[185]); diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 7e638d6e0..7c86067bc 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1855,6 +1855,8 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { sp[tempid].pvpresistcalc=atoi(row[178]); sp[tempid].pvpresistcap=atoi(row[179]); sp[tempid].spell_category=atoi(row[180]); + sp[tempid].pvp_duration = atoi(row[181]); + sp[tempid].pvp_duration_cap = atoi(row[182]); sp[tempid].pcnpc_only_flag=atoi(row[183]); sp[tempid].cast_not_standing = atoi(row[184]) != 0; sp[tempid].can_mgb=atoi(row[185]); diff --git a/common/spdat.h b/common/spdat.h index 1834b4a29..81cb122eb 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -1318,8 +1318,8 @@ struct SPDat_Spell_Struct /* 178 */ int pvpresistcalc; // -- PVP_RESIST_PER_LEVEL /* 179 */ int pvpresistcap; // -- PVP_RESIST_CAP /* 180 */ int spell_category; // -- GLOBAL_GROUP -/* 181 */ //int pvp_duration; // buffdurationformula for PvP -- PVP_DURATION -/* 182 */ //int pvp_duration_cap; // buffduration for PvP -- PVP_DURATION_CAP +/* 181 */ int pvp_duration; // buffdurationformula for PvP -- PVP_DURATION +/* 182 */ int pvp_duration_cap; // buffduration for PvP -- PVP_DURATION_CAP /* 183 */ int pcnpc_only_flag; // valid values are 0, 1 = PCs (and mercs), and 2 = NPCs (and not mercs) -- PCNPC_ONLY_FLAG /* 184 */ bool cast_not_standing; // this is checked in the client's EQ_Spell::IsCastWhileInvisSpell, this also blocks SE_InterruptCasting from affecting this spell -- CAST_NOT_STANDING /* 185 */ bool can_mgb; // 0=no, -1 or 1 = yes -- CAN_MGB diff --git a/common/version.h b/common/version.h index 514bed8c7..18a11b1b1 100644 --- a/common/version.h +++ b/common/version.h @@ -34,7 +34,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9167 +#define CURRENT_BINARY_DATABASE_VERSION 9168 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9028 diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 8fd174822..8e6a7a40e 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -421,6 +421,7 @@ 9165|2021_04_28_idle_pathing.sql|SHOW COLUMNS FROM `spawn2` LIKE 'path_when_zone_idle'|empty| 9166|2021_02_12_dynamic_zone_members.sql|SHOW TABLES LIKE 'dynamic_zone_members'|empty| 9167|2021_06_06_beastlord_pets.sql|SHOW TABLES LIKE 'pets_beastlord_data'|empty| +9168|2021_08_31_pvp_duration.sql|SHOW COLUMNS FROM `spells_new` LIKE 'pvp_duration'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2021_08_31_pvp_duration.sql b/utils/sql/git/required/2021_08_31_pvp_duration.sql new file mode 100644 index 000000000..e0fe00bfc --- /dev/null +++ b/utils/sql/git/required/2021_08_31_pvp_duration.sql @@ -0,0 +1,2 @@ +ALTER TABLE `spells_new` CHANGE `field181` `pvp_duration` int(11) NOT NULL DEFAULT '0'; +ALTER TABLE `spells_new` CHANGE `field182` `pvp_duration_cap` int(11) NOT NULL DEFAULT '0'; diff --git a/zone/lua_spell.cpp b/zone/lua_spell.cpp index c7730efff..5e7dcff73 100644 --- a/zone/lua_spell.cpp +++ b/zone/lua_spell.cpp @@ -354,6 +354,16 @@ int Lua_Spell::GetSpellCategory() { return self->spell_category; } +int Lua_Spell::GetPVPDuration() { + Lua_Safe_Call_Int(); + return self->pvp_duration; +} + +int Lua_Spell::GetPVPDurationCap() { + Lua_Safe_Call_Int(); + return self->pvp_duration_cap; +} + int Lua_Spell::GetCanMGB() { Lua_Safe_Call_Int(); return self->can_mgb; @@ -543,6 +553,8 @@ luabind::scope lua_register_spell() { .def("PVPResistCalc", &Lua_Spell::GetPVPResistCalc) .def("PVPResistCap", &Lua_Spell::GetPVPResistCap) .def("SpellCategory", &Lua_Spell::GetSpellCategory) + .def("PVPDuration", &Lua_Spell::GetPVPDuration) + .def("PVPDurationCap", &Lua_Spell::GetPVPDurationCap) .def("CanMGB", &Lua_Spell::GetCanMGB) .def("DispelFlag", &Lua_Spell::GetDispelFlag) .def("MinResist", &Lua_Spell::GetMinResist) diff --git a/zone/lua_spell.h b/zone/lua_spell.h index e2def8a6a..b9af8a564 100644 --- a/zone/lua_spell.h +++ b/zone/lua_spell.h @@ -83,6 +83,8 @@ public: int GetPVPResistCalc(); int GetPVPResistCap(); int GetSpellCategory(); + int GetPVPDuration(); + int GetPVPDurationCap(); int GetCanMGB(); int GetDispelFlag(); int GetMinResist(); diff --git a/zone/spells.cpp b/zone/spells.cpp index 567d239c6..c098060a0 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2816,8 +2816,14 @@ int Mob::CalcBuffDuration(Mob *caster, Mob *target, uint16 spell_id, int32 caste if(!target) target = caster; - formula = spells[spell_id].buffdurationformula; - duration = spells[spell_id].buffduration; + // PVP duration + if (IsDetrimentalSpell(spell_id) && target->IsClient() && caster->IsClient()) { + formula = spells[spell_id].pvp_duration; + duration = spells[spell_id].pvp_duration_cap; + } else { + formula = spells[spell_id].buffdurationformula; + duration = spells[spell_id].buffduration; + } int castlevel = caster->GetCasterLevel(spell_id); if(caster_level_override > 0) @@ -4591,7 +4597,18 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use } //Get resist modifier and adjust it based on focus 2 resist about eq to 1% resist chance - int resist_modifier = (use_resist_override) ? resist_override : spells[spell_id].ResistDiff; + int resist_modifier = 0; + if (use_resist_override) { + resist_modifier = resist_override; + } else { + // PVP, we don't have the normal per_level or cap stuff implemented ... so ahh do that + // and make sure the PVP versions are also handled. + if (IsClient() && caster->IsClient()) { + resist_modifier = spells[spell_id].pvpresistbase; + } else { + resist_modifier = spells[spell_id].ResistDiff; + } + } if(caster->GetSpecialAbility(CASTING_RESIST_DIFF)) resist_modifier += caster->GetSpecialAbilityParam(CASTING_RESIST_DIFF, 0); From 943c623be07e25931681073d194509be11f2733a Mon Sep 17 00:00:00 2001 From: splose Date: Sun, 5 Sep 2021 00:17:59 -0400 Subject: [PATCH 175/624] [Hitpoints] More HP Fixes - Remove Hacks (#1518) * Fix HP update throttling * Remove more hacks Co-authored-by: Akkadius --- zone/attack.cpp | 2 +- zone/client_mods.cpp | 3 --- zone/mob.cpp | 21 +++++---------------- zone/mob.h | 2 +- 4 files changed, 7 insertions(+), 21 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index fac1301e5..5b2bc8a19 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3739,7 +3739,7 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const //send an HP update if we are hurt if (GetHP() < GetMaxHP()) - SendHPUpdate(!iBuffTic); // the OP_Damage actually updates the client in these cases, so we skip the HP update for them + SendHPUpdate(); // the OP_Damage actually updates the client in these cases, so we skip the HP update for them } //end `if damage was done` //send damage packet... diff --git a/zone/client_mods.cpp b/zone/client_mods.cpp index a1eea7154..b415b6938 100644 --- a/zone/client_mods.cpp +++ b/zone/client_mods.cpp @@ -337,9 +337,6 @@ int32 Client::CalcMaxHP() } } - // hack fix for client health not reflecting server value - last_max_hp = 0; - return max_hp; } diff --git a/zone/mob.cpp b/zone/mob.cpp index e46aba587..287bdad92 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -190,7 +190,6 @@ Mob::Mob( last_hp_percent = 0; last_hp = 0; - last_max_hp = 0; current_speed = base_runspeed; @@ -1355,32 +1354,22 @@ void Mob::CreateHPPacket(EQApplicationPacket* app) } } -void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= false*/) +void Mob::SendHPUpdate(bool force_update_all) { // If our HP is different from last HP update call - let's update selves if (IsClient()) { - - // delay allowing the client to catch up on buff states - if (max_hp != last_max_hp) { - last_max_hp = max_hp; - return; - } - if (current_hp != last_hp || force_update_all) { - // This is to prevent excessive packet sending under trains/fast combat LogHPUpdate( - "[SendHPUpdate] Update HP of self [{}] HP: [{}/{}] last: [{}/{}] skip_self: [{}]", + "[SendHPUpdate] Update HP of self [{}] current_hp [{}] max_hp [{}] last_hp [{}]", GetCleanName(), current_hp, max_hp, - last_hp, - last_max_hp, - (skip_self ? "true" : "false") + last_hp ); - if (!skip_self || this->CastToClient()->ClientVersion() >= EQ::versions::ClientVersion::SoD) { + if (CastToClient()->ClientVersion() >= EQ::versions::ClientVersion::SoD) { auto client_packet = new EQApplicationPacket(OP_HPUpdate, sizeof(SpawnHPUpdate_Struct)); auto *hp_packet_client = (SpawnHPUpdate_Struct *) client_packet->pBuffer; @@ -3279,7 +3268,7 @@ void Mob::SetTarget(Mob *mob) } if (IsClient() && GetTarget()) { - GetTarget()->SendHPUpdate(false, true); + GetTarget()->SendHPUpdate(true); } } diff --git a/zone/mob.h b/zone/mob.h index 339bd1ce0..53170b958 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -680,7 +680,7 @@ public: static void CreateSpawnPacket(EQApplicationPacket* app, NewSpawn_Struct* ns); virtual void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho); void CreateHPPacket(EQApplicationPacket* app); - void SendHPUpdate(bool skip_self = false, bool force_update_all = false); + void SendHPUpdate(bool force_update_all = false); virtual void ResetHPUpdateTimer() {}; // does nothing //Util From e7dd8d49a91f61abec7218a25e500e4ad0fb5ee6 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 5 Sep 2021 01:21:23 -0500 Subject: [PATCH 176/624] [Shared Tasks] Shared Tasks System Implementation (#1451) * Shared tasks WIP; lots of logging; shared tasks and tasks work internally the same for now; lots to cleanup yet * Update task_manager.cpp * Add tables * World message handler * Zone message handler * More messaging * More rearranging * Task creation work (wip) * Tweaks * Decoupled things, added a shared task manager, moved logic to the manager, created the shared task object, now creating a sense of state on creation and members, zero validation, happy path * Cleanup unnecessary getter * More work on shared task persistence and state loading * Add int64 support into repositories * More state handling, creation loads all tables * Wrap up shared task state creation and removal * Move more lookup operations to preloading (memory). Restore shared task state during world bootup * Implement shared task updates * Add members other than just leader in task confirmations * Update shared_task_manager.cpp * Hook task cancellation for shared task removal (middleware) * Remove dynamic_zone_id from SharedTasks model in repositories (for now) since we will likely be one to many with DZ objects * Get members to show up in the window on creation * Add opcodes, cleanup * Add opcode handlers * Split some methods out, self removal of shared task and updating members * Implement offline shared task sync * Style changes * Send memberlist on initial login; implement remove player from shared task window * Refactorings, cleanup * Implement make leader in shared tasks window * Implement add player, sync shared task state after add * Add opcodes for remaining clients * Shared task invite dialogue window implementation and response handling (including validation) * Logging * Remove comment * Some cleanup * Pass NPC context through shared task request logic * Remove extra SharedTaskMember fields * Add message constants * Remove static * Only use dz for expedition request This passes expedition creation parameters through DynamicZone instead of injecting ExpeditionRequest since it can hold creation data now * Store expedition leader on dz This shifts to using the leader object that exists in the core dynamic zone object. It will be moved to the dynamic zone table later with other columns that should just be on the dz to make loading easier. Expeditions are probably the only dz type that will use this for window updates and command auth. Other systems on live do fill the window but don't keep it updated * Store expedition name on dz This uses the name stored on dz (for window packets) instead of duplicating it. This will be moved completely to dz table later * Store uuid on dynamic zone This lets dynamic zones generate the uuid instead of expeditions. Other dz type systems may want to make use of this. Lockouts should also be moved to dynamic zones at some point in the future so this will be necessary for that * Move expedition db columns to dz These columns should just belong to the core dynamic zone. This will simplify loading from the database and in the future a separate expedition table may no longer be necessary. * Move window packet methods to dz It makes more sense for these methods to be in the core This will also allow support for other systems to use the window, though live behavior that updates the window for shared task missions when not in an expedition is likely unintended since it's not updated on changes. * Store dynamic zone ids on clients These will now be used for client dynamic zone lookups to remove dependency on any dz type system caches * Move member management to dz This moves server messaging for adding and removing members to internal dynamic zone methods Set default dz member status to Unknown * Move member status caching to dz This moves world member status caching into internal dz methods Zone member updates for created expeditions are now async and sent after world replies with member statuses. Prior to this two memberlist packets were sent to members in other zones on creation to update statuses. This also fixes a bug with member statuses being wrong for offline raid members in the zone that created an expedition. Note that live kicks offline players out of raids so this is only to support emu behavior. * Move member status updates to dz * Set dz member status on all client dzs This also renames the zone entry dz update method and moves window update to a dynamic zone method. Eventually expedition components should just be merged with dz and handled as another dz type * Save instance safe return on characters Add character_instance_safereturns table and repository Previously dz safe return only worked for online characters via the dz kicktimer or offline characters with a workaround that moved them when an expedition was deleted. There were various edge cases that would cause characters to be moved to bind instead (succoring after removal, camping before kick timer, removed while offline, bulk kickplayers removal with some offline) This updates a character's instance safereturn every time they enter a zone. If a character enters world in an instance that expired or are no longer part of they'll be moved to their instance safereturn (if the safereturn data is for the same zone-instance). Bind is still a fallback This may also be used for non-dz instancing so it's named generically This removes the expedition MoveMembersToSafeReturn workaround which deprecates the is_current_member column of dynamic_zone_members and will be removed in a followup patch. * Remove is_current_member from dz members This was only being used in the workaround to move past members to dz safereturns if they were still inside the dz but not online * Let dz check leader in world This moves expedition leader processing in world to the dynamic zone. This is a step in phasing out the separate expedition class for things that can run off the dynamic zone core with simple dz type checks This greatly simplifies checking leader on member and status changes without needing callbacks. Other dz types that may use the dz leader object can just handle it directly on the dz the same as expeditions * Let dz handle member expire warnings This moves expire warning checks to dz. This will make it easier for other dz types to issue expire warnings if needed * Use separate dynamic zone cache Dynamic zones are no longer member objects of expeditions and have been placed into their own cache. This was done so other dz types can be cached without relying on their systems. Client and zone dz Lookups are now independent of any system This continues the process of phasing out a separate expedition cache. Eventually expeditions can just be run directly as dynamic zones internally with a few dz type checks. Add dz serialization methods (cereal) for passing server dz creation Modify #dz list to show cache and database separately. Also adds #dz cache reload. This command will reload expeditions too since they currently hold references to the dz in their own zone cache. Add a dynamic zone processing class to world to process all types and move expedition processing to it * Move expedition makeleader processing to dz * Let dz handle expedition deletions This removes the need for separate expedition cache in world This will greatly simplify world dynamic zone caching and processing. Dynamic zones that are expeditions can just handle this directly. Once lockouts and other components are completely moved to dynamic zones the separate expedition cache in zone will also no longer be necessary * Remove ExpeditionBase class Since world no longer caches expeditions this will not be necessary * Fix windows compile * Implement task dz creation Prototype dz creation for shared tasks * Add and remove shared task members from dz Also keep leader updated (used in choose zone window) * Fix client crash on failed shared task * Fix linux compile and warning * Check client nullptr for dz message This was accidently removed when expedition makeleader was moved * Disable dz creation for solo tasks * Add shared task repository headers to CMakeLists * Add shared task dynamic zones table * Add shared task dz database persistence * Get members from db on shared task dz creation This fixes a case where removing a member from a shared task dz would fail if the member's name was empty. This could happen if the shared task dz was created while a member was offline. This also changes the dz member removal method to only check id. It might be possible to change all dz member validations to only check ids since names are primarily for window updates, but shared task dz member names need to be non-empty anyway to support possible live-like dz window usage in the future. * Add character message methods to world Add simple and eqstr message methods to ClientList Add shared task manager methods to message all members or leader * Add SyncClientSharedTaskState and nested sync strategies to cover M3 work * Fix whitespace * Implement task request cooldown timer This implements the task request cooldown (15 seconds) that live uses when a task is accepted. This will also need to be set when shared tasks are offered (likely due to additional group/raid validations) * Implement shared task selector validation This implements the validation and filtering that occurs before the task selection window is sent to a client for shared tasks To keep things live-like, task selectors that contain a shared task will be run through shared task validation and drop non-shared tasks. Live doesn't mix types in task selections and this makes validation simpler. Also note that live sends shared task selectors via a different opcode than solo tasks but that has not been implemented yet * Add separate shared task select opcodes Live uses separate opcodes for solo and shared task selection windows * Convert ActivityType to enum class * Refactor task selector serialization This adds serializer methods to task and task objective structs for the task selection windows. This combines the duplicate task selector methods to reduce code duplication and simplify serialization * Add shared task selector This sends shared task selection window using the shared task specific opcode and adds an opcode handler for shared task accepts which are sent by client in response to setting selection window to shared task type. * Refactor task objective serialization This adds a serialization method to the task objective struct for serializing objectives in the window list and combines the separate client-based methods to reduce duplicated code. * Add task level spread and player count columns * Implement shared task accept validation This adds a common method for shared task character request queries * Add task replay and request timer columns * Add character task timers table * Use shared task accept time on clients This overrides client task accept time with shared task's creation time. This is needed for accurate window task timers and lockout messages especially for characters added to shared tasks post creation * Implement task timer lockouts This implements replay and request task timers for solo and shared tasks * Add solo and shared task timer validation * Remove logging of padding array This gets interpreted as a c string which may not be null terminated * Implement /kickplayers task This also fixes current CancelTask behavior for leader which was performing kickplayers functionality through the remove task button * Implement /taskquit command * Implement shared task invite validation Remove active invitation before invite accept validation * Remove local client db persistence during SyncClientSharedTaskRemoveLocalIfNotExists * Add missing accept time arg to assign task * Only validate non-zero task invite requirements * Fix task error log crash * Separate task cooldown timer messaging * Use method to check for client shared task * Avoid unneeded task invite validation query Only need to query character data for levels for non-zero level spread * Implement /tasktimers command May want to add some type of throttled caching mechanism for this in the future * Add /tasktimers rate limiter * Intercept shared task completion; more work to come * Change SharedTaskActivityState and SharedTasks time objects to datetime * Add updated_time updates to SharedTaskActivities * Mark shared tasks as complete when all activities are completed * Save a database query on shared task completion and use the active record in memory * Don't record shared task completions to the quest log * Implement RecordSharedTaskCompletion, add tables, repositories * Update shared_task_manager.cpp * Update shared_task_manager.cpp * Add shared task replay timers This is still not feature complete. On live any past members that ever joined the shared task will receive a replay timer when it's completed * Create FindCharactersInSharedTasks that searches through memory * Remove namespace shorthand and formatting * More minor cleanup * Implement PurgeAllSharedTasks via #task command * Add #task purgetimers * Decrease m_keepalive time between processes * Remove type ordering in /tasktimer query * Add comment for task packet reward multiplier This is likely a reward multiplier that changes text color based on value to represent any scaled bonus or penalty * Add replay timers to past members This implements the live behavior that adds replay timers to any previous member of a shared task. This likely exists to avoid possible exploits. Shared task member history is stored in memory and is used to assign replay timers. This history will be lost on world crashes or restarts but is simpler than saving past member state in database. This also makes world send shared task replay timer messages since past members need to be messaged now * Move PurgeTaskTimers client method to tasks.cpp * Remove dz members when purging shared tasks Server dz states need to be updated before shared tasks are deleted * Use exact name in shared task invites This removes the wildcards from shared task invite character queries which was sometimes selecting the wrong character Taskadd validation is called even for invalid characters to allow for proper messages to occur * Clear declined active shared task invitations This also notifies leader for declined shared task invites * Store shared task member names This adds back the character name field to SharedTaskMember. This should make serialization easier in the future and reduce database lookups when names are needed for /task commands * Implement /taskplayerlist command * Replace queries with member name lookups Now that shared task members store names these queries are unnecessary This also adds not-a-member messages for /taskremove and /taskmakeleader * Implement shared task member change packet This avoids sending the full member list to members when a single member is added or removed and lets the client generate chat messages for it. * Serialize shared task member list from world This uses cereal to serialize the full member list from world and removes the zone query workarounds * Initialize client task state array This was causing sql query errors on client state reloads The client task information array was uninitialized resulting in being filled with 0xcdcdcdcd values in msvc debug builds. Under release builds this may have resulted in indeterminate values A better fix would be to refactor some of this legacy code * Add shared task command messages Add messages for non-leader task commands This adds taskadd, taskremove, taskmakeleader, and taskquit messages The leader receives double messages for taskremove like live due to the client generated message as well as the explicit one. It also receives double server messages if the leader /taskremoves self. * Replace some task messages with eqstrs This also updates to use live colors * Avoid shared task invite leader lookup query Since member names are stored now this query is also unnecessary * Avoid reloading client state on shared task accept This was unnecessarily reloading client task state when added to a shared task. This also resulted in all active tasks being resent to shared task members on creation. The shared task itself is the only task that needs to be sent which is handled by AcceptNewTask. * Remove active shared task invite on zone Live doesn't re-send shared task invites after zoning like it does for expeditions so there's no need to keep these around. This fixes active invitations never getting reset on characters that zone or go offline. * Choose new shared task leader if leader removed * Add separate shared task kickplayers method * Enable EVENT_CAST_ON for clients This will be required for a shared task objective (The Creator) in DoN * Revert "Avoid reloading client state on shared task accept" This reverts commit 3af14fee2de8b109ffb6c2b2fc67731e1531a665. Without this clients added to a task after some objectives have been completed don't get updated state. Will need to investigate this later * Disallow looting inside a dz by non-members Non-members of a dynamic zone should not be allowed to loot npcs inside it. This should have been disabled for expeditions already but was still allowed due to an oversight (or live behavior changed). This is less critical for shared tasks since members can be added and removed at will without leaving a dz but still an important feature. * Change load where criteria * Increase task completion emote column size * Use eqstr for task item reward message * Implement radiant and ebon crystal rewards This adds reward columns for radiant and ebon crystals to the tasks table and updates task description serialization * Send task completion emote before rewards This matches live and makes it a little easier to see item rewards when tasks have a long completion emote. This also changes it to send via the same normal message opcode that live uses. * Do not send a shared task in completed task history * Allow EVENT_TASK_STAGE_COMPLETE for quest goals This invokes event_task_stage_complete for task elements flagged with a quest controlled goal method. It should be expected behavior that a completed task stage always fires this event even if a quest controls it * Add SyncSharedTaskZoneClientDoneCountState * Swap return for continue in this case * Formatting * Simplify * Formatting * Formatting * Formatting * Remove errant check * Formatting, add setter for shared tasks * Remove debugging * Comments in PR * More PR follow up * Formatting * Cleanup * Update packet comments * Comments * More cleanup * Send command error message if not in shared task /taskadd is the only command with this feedback on live. Newer live clients also generate this instead of the server sending the message * Implement expire_time on SharedTask object and add a purge on world bootup * Comment * Add SyncClientSharedTaskStateToLocal where clients fall out of sync and no longer have a task locally * Clamp shared task activity updates to max done count and discard updates out of bounds * Fix packet send * Revert packet send * Adjust clamping OOO for completed time check. Add completed tables to purge truncation * Refactor kill update logic so that shared task kill updates only update one client instead of all clients * Cleanup how we're checking for active tasks * Forward task sets that contain shared tasks This forwards task sets that contain a shared task to shared task selector validation like normal task selectors * Change eqstr for empty solo task offers This is the message live appears to use if all task offers are filtered out by solo task validation * Fix max active tasks client message This message starts at the third argument. It was maybe intended to be an npc say message but live just sends it as a normal eqstr with the first two arguments nulled. * Load client task state after zoning complete This fixes a possible race where a character removed from a shared task while zoning would be stuck with an incorrect character activities state after zoning was completed. This was caused by the character loading task state to early on zone entry but never receiving the remove player message from world since they are missing from the world cle until zoning is completed. Loading client state after zone connection is completed makes sure the client has the latest state and available to the world cle * Send message to clients removed while zoning This message should usually only be sent to characters that were removed from a shared task while zoning but will occur for any sync state removals where a message wouldn't have already occured. * Post rebase fix * HG comment for checking active task * Addressing HG comments around zeroing out a shared task id * Remove errant comment * Post rebase database manifest updates * Update eqemu_logsys_log_aliases.h * More rebase catches * Bump database version for last commit Co-authored-by: hg <4683435+hgtw@users.noreply.github.com> --- common/CMakeLists.txt | 23 +- common/database_schema.h | 13 +- common/dynamic_zone_base.cpp | 263 ++- common/dynamic_zone_base.h | 87 +- common/emu_oplist.h | 17 +- common/eq_packet_structs.h | 63 +- common/eqemu_logsys.cpp | 6 +- common/eqemu_logsys.h | 4 +- common/eqemu_logsys_log_aliases.h | 22 + common/expedition_base.cpp | 23 - common/expedition_base.h | 40 - ...haracter_instance_safereturns_repository.h | 355 ++++ .../base_character_task_timers_repository.h | 336 +++ ...ed_shared_task_activity_state_repository.h | 337 +++ ...completed_shared_task_members_repository.h | 317 +++ .../base_completed_shared_tasks_repository.h | 347 ++++ .../base_dynamic_zone_members_repository.h | 33 +- .../base/base_dynamic_zones_repository.h | 191 +- .../base/base_expeditions_repository.h | 77 +- ...se_shared_task_activity_state_repository.h | 337 +++ ...ase_shared_task_dynamic_zones_repository.h | 293 +++ .../base_shared_task_members_repository.h | 302 +++ .../base/base_shared_tasks_repository.h | 346 ++++ .../repositories/base/base_tasks_repository.h | 238 ++- ...haracter_instance_safereturns_repository.h | 108 + .../character_task_timers_repository.h | 70 + ...ed_shared_task_activity_state_repository.h | 70 + ...completed_shared_task_members_repository.h | 70 + .../completed_shared_tasks_repository.h | 70 + .../dynamic_zone_members_repository.h | 32 +- .../repositories/dynamic_zones_repository.h | 81 +- common/repositories/expeditions_repository.h | 96 - .../shared_task_activity_state_repository.h | 70 + .../shared_task_dynamic_zones_repository.h | 70 + .../shared_task_members_repository.h | 70 + common/repositories/shared_tasks_repository.h | 70 + common/ruletypes.h | 1 + common/servertalk.h | 109 +- common/shared_tasks.cpp | 139 ++ common/shared_tasks.h | 204 ++ common/tasks.h | 445 ++++ common/version.h | 2 +- utils/patches/patch_RoF.conf | 4 +- utils/patches/patch_RoF2.conf | 17 +- utils/patches/patch_SoD.conf | 16 +- utils/patches/patch_SoF.conf | 5 +- utils/patches/patch_Titanium.conf | 18 +- utils/patches/patch_UF.conf | 17 +- utils/sql/db_update_manifest.txt | 4 + .../2021_03_03_instance_safereturns.sql | 13 + ...2021_03_30_remove_dz_is_current_member.sql | 6 + .../git/required/2021_05_21_shared_tasks.sql | 97 + .../2021_06_06_dynamic_zone_moved_columns.sql | 23 + world/CMakeLists.txt | 10 +- world/client.cpp | 42 +- world/cliententry.cpp | 4 + world/clientlist.cpp | 151 +- world/clientlist.h | 7 + world/dynamic_zone.cpp | 261 ++- world/dynamic_zone.h | 22 +- world/dynamic_zone_manager.cpp | 166 ++ world/dynamic_zone_manager.h | 32 + world/expedition.cpp | 237 --- world/expedition.h | 57 - world/expedition_database.cpp | 63 +- world/expedition_database.h | 3 - world/expedition_message.cpp | 60 +- world/expedition_message.h | 1 - world/expedition_state.cpp | 172 -- world/expedition_state.h | 52 - world/main.cpp | 244 ++- world/shared_task_manager.cpp | 1820 +++++++++++++++++ world/shared_task_manager.h | 132 ++ world/shared_task_world_messaging.cpp | 355 ++++ world/shared_task_world_messaging.h | 18 + world/worlddb.cpp | 49 + world/worlddb.h | 1 + world/zonelist.cpp | 2 +- world/zoneserver.cpp | 34 +- zone/CMakeLists.txt | 2 + zone/attack.cpp | 20 +- zone/client.cpp | 201 +- zone/client.h | 42 +- zone/client_packet.cpp | 346 +++- zone/client_packet.h | 10 + zone/client_process.cpp | 17 +- zone/command.cpp | 135 +- zone/corpse.cpp | 16 +- zone/dynamic_zone.cpp | 658 +++++- zone/dynamic_zone.h | 35 +- zone/expedition.cpp | 825 ++------ zone/expedition.h | 56 +- zone/expedition_database.cpp | 16 +- zone/expedition_database.h | 4 +- zone/expedition_request.cpp | 10 +- zone/expedition_request.h | 3 +- zone/forage.cpp | 4 +- zone/lua_client.cpp | 66 +- zone/lua_client.h | 1 + zone/lua_expedition.cpp | 32 +- zone/lua_packet.cpp | 4 +- zone/lua_parser.cpp | 1 + zone/perl_expedition.cpp | 32 +- zone/shared_task_zone_messaging.cpp | 176 ++ zone/shared_task_zone_messaging.h | 12 + zone/string_ids.h | 7 + zone/task_client_state.cpp | 1117 ++++++++-- zone/task_client_state.h | 52 +- zone/task_manager.cpp | 1000 +++++---- zone/task_manager.h | 21 +- zone/tasks.cpp | 40 +- zone/tasks.h | 133 +- zone/tradeskills.cpp | 2 +- zone/worldserver.cpp | 44 +- zone/zone.cpp | 22 +- zone/zone.h | 1 + 116 files changed, 12094 insertions(+), 3131 deletions(-) delete mode 100644 common/expedition_base.cpp delete mode 100644 common/expedition_base.h create mode 100644 common/repositories/base/base_character_instance_safereturns_repository.h create mode 100644 common/repositories/base/base_character_task_timers_repository.h create mode 100644 common/repositories/base/base_completed_shared_task_activity_state_repository.h create mode 100644 common/repositories/base/base_completed_shared_task_members_repository.h create mode 100644 common/repositories/base/base_completed_shared_tasks_repository.h create mode 100644 common/repositories/base/base_shared_task_activity_state_repository.h create mode 100644 common/repositories/base/base_shared_task_dynamic_zones_repository.h create mode 100644 common/repositories/base/base_shared_task_members_repository.h create mode 100644 common/repositories/base/base_shared_tasks_repository.h create mode 100644 common/repositories/character_instance_safereturns_repository.h create mode 100644 common/repositories/character_task_timers_repository.h create mode 100644 common/repositories/completed_shared_task_activity_state_repository.h create mode 100644 common/repositories/completed_shared_task_members_repository.h create mode 100644 common/repositories/completed_shared_tasks_repository.h create mode 100644 common/repositories/shared_task_activity_state_repository.h create mode 100644 common/repositories/shared_task_dynamic_zones_repository.h create mode 100644 common/repositories/shared_task_members_repository.h create mode 100644 common/repositories/shared_tasks_repository.h create mode 100644 common/shared_tasks.cpp create mode 100644 common/shared_tasks.h create mode 100644 common/tasks.h create mode 100644 utils/sql/git/required/2021_03_03_instance_safereturns.sql create mode 100644 utils/sql/git/required/2021_03_30_remove_dz_is_current_member.sql create mode 100644 utils/sql/git/required/2021_05_21_shared_tasks.sql create mode 100644 utils/sql/git/required/2021_06_06_dynamic_zone_moved_columns.sql create mode 100644 world/dynamic_zone_manager.cpp create mode 100644 world/dynamic_zone_manager.h delete mode 100644 world/expedition.cpp delete mode 100644 world/expedition.h delete mode 100644 world/expedition_state.cpp delete mode 100644 world/expedition_state.h create mode 100644 world/shared_task_manager.cpp create mode 100644 world/shared_task_manager.h create mode 100644 world/shared_task_world_messaging.cpp create mode 100644 world/shared_task_world_messaging.h create mode 100644 zone/shared_task_zone_messaging.cpp create mode 100644 zone/shared_task_zone_messaging.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 47edd22e8..6e8a826fc 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -32,7 +32,6 @@ SET(common_sources eq_stream_proxy.cpp eqtime.cpp event_sub.cpp - expedition_base.cpp expedition_lockout_timer.cpp extprofile.cpp faction.cpp @@ -72,6 +71,7 @@ SET(common_sources serialize_buffer.cpp server_event_scheduler.cpp serverinfo.cpp + shared_tasks.cpp shareddb.cpp skills.cpp spdat.cpp @@ -154,6 +154,7 @@ SET(repositories repositories/base/base_character_disciplines_repository.h repositories/base/base_character_expedition_lockouts_repository.h repositories/base/base_character_inspect_messages_repository.h + repositories/base/base_character_instance_safereturns_repository.h repositories/base/base_character_item_recast_repository.h repositories/base/base_character_languages_repository.h repositories/base/base_character_leadership_abilities_repository.h @@ -165,11 +166,15 @@ SET(repositories repositories/base/base_character_potionbelt_repository.h repositories/base/base_character_skills_repository.h repositories/base/base_character_spells_repository.h + repositories/base/base_character_task_timers_repository.h repositories/base/base_character_tasks_repository.h repositories/base/base_char_create_combinations_repository.h repositories/base/base_char_create_point_allocations_repository.h repositories/base/base_char_recipe_list_repository.h repositories/base/base_completed_tasks_repository.h + repositories/base/base_completed_shared_tasks_repository.h + repositories/base/base_completed_shared_task_members_repository.h + repositories/base/base_completed_shared_task_activity_state_repository.h repositories/base/base_content_flags_repository.h repositories/base/base_damageshieldtypes_repository.h repositories/base/base_data_buckets_repository.h @@ -255,6 +260,10 @@ SET(repositories repositories/base/base_rule_values_repository.h repositories/base/base_saylink_repository.h repositories/base/base_server_scheduled_events_repository.h + repositories/base/base_shared_tasks_repository.h + repositories/base/base_shared_task_activity_state_repository.h + repositories/base/base_shared_task_dynamic_zones_repository.h + repositories/base/base_shared_task_members_repository.h repositories/base/base_skill_caps_repository.h repositories/base/base_spawn2_repository.h repositories/base/base_spawnentry_repository.h @@ -318,6 +327,7 @@ SET(repositories repositories/character_disciplines_repository.h repositories/character_expedition_lockouts_repository.h repositories/character_inspect_messages_repository.h + repositories/character_instance_safereturns_repository.h repositories/character_item_recast_repository.h repositories/character_languages_repository.h repositories/character_leadership_abilities_repository.h @@ -329,11 +339,15 @@ SET(repositories repositories/character_potionbelt_repository.h repositories/character_skills_repository.h repositories/character_spells_repository.h + repositories/character_task_timers_repository.h repositories/character_tasks_repository.h repositories/char_create_combinations_repository.h repositories/char_create_point_allocations_repository.h repositories/char_recipe_list_repository.h repositories/completed_tasks_repository.h + repositories/completed_shared_tasks_repository.h + repositories/completed_shared_task_members_repository.h + repositories/completed_shared_task_activity_state_repository.h repositories/content_flags_repository.h repositories/damageshieldtypes_repository.h repositories/data_buckets_repository.h @@ -419,6 +433,10 @@ SET(repositories repositories/rule_values_repository.h repositories/saylink_repository.h repositories/server_scheduled_events_repository.h + repositories/shared_tasks_repository.h + repositories/shared_task_activity_state_repository.h + repositories/shared_task_dynamic_zones_repository.h + repositories/shared_task_members_repository.h repositories/skill_caps_repository.h repositories/spawn2_repository.h repositories/spawnentry_repository.h @@ -497,7 +515,6 @@ SET(common_headers eqtime.h errmsg.h event_sub.h - expedition_base.h expedition_lockout_timer.h extprofile.h faction.h @@ -553,11 +570,13 @@ SET(common_headers server_event_scheduler.h serverinfo.h servertalk.h + shared_tasks.h shareddb.h skills.h spdat.h string_util.h struct_strategy.h + tasks.h textures.h timer.h types.h diff --git a/common/database_schema.h b/common/database_schema.h index 25bc70f22..ba5fb1c45 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -53,6 +53,7 @@ namespace DatabaseSchema { {"character_expedition_lockouts", "character_id"}, {"character_exp_modifiers", "character_id"}, {"character_inspect_messages", "id"}, + {"character_instance_safereturns", "character_id"}, {"character_item_recast", "id"}, {"character_languages", "id"}, {"character_leadership_abilities", "id"}, @@ -64,6 +65,7 @@ namespace DatabaseSchema { {"character_potionbelt", "id"}, {"character_skills", "id"}, {"character_spells", "id"}, + {"character_task_timers", "character_id"}, {"character_tasks", "charid"}, {"character_tribute", "id"}, {"completed_tasks", "charid"}, @@ -119,6 +121,7 @@ namespace DatabaseSchema { "character_expedition_lockouts", "character_exp_modifiers", "character_inspect_messages", + "character_instance_safereturns", "character_item_recast", "character_languages", "character_leadership_abilities", @@ -130,6 +133,7 @@ namespace DatabaseSchema { "character_potionbelt", "character_skills", "character_spells", + "character_task_timers", "character_tasks", "character_tribute", "completed_tasks", @@ -310,6 +314,9 @@ namespace DatabaseSchema { "banned_ips", "bug_reports", "bugs", + "completed_shared_task_activity_state", + "completed_shared_task_members", + "completed_shared_tasks", "dynamic_zone_members", "dynamic_zones", "eventlog", @@ -319,8 +326,8 @@ namespace DatabaseSchema { "group_id", "group_leaders", "hackers", - "ip_exemptions", "instance_list", + "ip_exemptions", "item_tick", "lfguild", "merchantlist_temp", @@ -332,6 +339,10 @@ namespace DatabaseSchema { "respawn_times", "saylink", "server_scheduled_events", + "shared_task_activity_state", + "shared_task_dynamic_zones", + "shared_task_members", + "shared_tasks", }; } diff --git a/common/dynamic_zone_base.cpp b/common/dynamic_zone_base.cpp index f3a499ee8..f791ed0b5 100644 --- a/common/dynamic_zone_base.cpp +++ b/common/dynamic_zone_base.cpp @@ -5,6 +5,7 @@ #include "repositories/instance_list_player_repository.h" #include "rulesys.h" #include "servertalk.h" +#include "util/uuid.h" DynamicZoneBase::DynamicZoneBase(DynamicZonesRepository::DynamicZoneInstance&& entry) { @@ -13,16 +14,12 @@ DynamicZoneBase::DynamicZoneBase(DynamicZonesRepository::DynamicZoneInstance&& e uint32_t DynamicZoneBase::Create() { - if (m_id != 0) - { - return m_id; - } - if (GetInstanceID() == 0) { CreateInstance(); } + m_uuid = EQ::Util::UUID::Generate().ToString(); m_id = SaveToDatabase(); return m_id; @@ -75,6 +72,11 @@ uint32_t DynamicZoneBase::CreateInstance() void DynamicZoneBase::LoadRepositoryResult(DynamicZonesRepository::DynamicZoneInstance&& dz_entry) { m_id = dz_entry.id; + m_uuid = std::move(dz_entry.uuid); + m_name = std::move(dz_entry.name); + m_leader.id = dz_entry.leader_id; + m_min_players = dz_entry.min_players; + m_max_players = dz_entry.max_players; m_instance_id = dz_entry.instance_id; m_type = static_cast(dz_entry.type); m_compass.zone_id = dz_entry.compass_zone_id; @@ -104,6 +106,12 @@ void DynamicZoneBase::AddMemberFromRepositoryResult( DynamicZoneMembersRepository::MemberWithName&& entry) { auto status = DynamicZoneMemberStatus::Unknown; + + if (m_leader.id == entry.character_id) + { + m_leader.name = entry.character_name; + } + AddInternalMember({ entry.character_id, std::move(entry.character_name), status }); } @@ -114,6 +122,11 @@ uint32_t DynamicZoneBase::SaveToDatabase() if (m_instance_id != 0) { auto insert_dz = DynamicZonesRepository::NewEntity(); + insert_dz.uuid = m_uuid; + insert_dz.name = m_name; + insert_dz.leader_id = m_leader.id; + insert_dz.min_players = m_min_players; + insert_dz.max_players = m_max_players; insert_dz.instance_id = m_instance_id, insert_dz.type = static_cast(m_type); insert_dz.compass_zone_id = m_compass.zone_id; @@ -137,34 +150,80 @@ uint32_t DynamicZoneBase::SaveToDatabase() return 0; } -void DynamicZoneBase::AddCharacter(uint32_t character_id) +bool DynamicZoneBase::AddMember(const DynamicZoneMember& add_member) { - DynamicZoneMembersRepository::AddMember(GetDatabase(), m_id, character_id); - GetDatabase().AddClientToInstance(m_instance_id, character_id); - SendInstanceAddRemoveCharacter(character_id, false); // stops client kick timer -} - -void DynamicZoneBase::RemoveCharacter(uint32_t character_id) -{ - DynamicZoneMembersRepository::RemoveMember(GetDatabase(), m_id, character_id); - GetDatabase().RemoveClientFromInstance(m_instance_id, character_id); - SendInstanceAddRemoveCharacter(character_id, true); // start client kick timer -} - -void DynamicZoneBase::RemoveAllCharacters(bool enable_removal_timers) -{ - if (GetInstanceID() == 0) + if (HasMember(add_member.id)) { - return; + return false; } - if (enable_removal_timers) + DynamicZoneMembersRepository::AddMember(GetDatabase(), m_id, add_member.id); + GetDatabase().AddClientToInstance(m_instance_id, add_member.id); + + ProcessMemberAddRemove(add_member, false); + SendServerPacket(CreateServerMemberAddRemovePacket(add_member, false).get()); + + return true; +} + +bool DynamicZoneBase::RemoveMember(uint32_t character_id) +{ + auto remove_member = GetMemberData(character_id); + return RemoveMember(remove_member); +} + +bool DynamicZoneBase::RemoveMember(const std::string& character_name) +{ + auto remove_member = GetMemberData(character_name); + return RemoveMember(remove_member); +} + +bool DynamicZoneBase::RemoveMember(const DynamicZoneMember& remove_member) +{ + if (remove_member.id == 0) { - SendInstanceRemoveAllCharacters(); + return false; } + DynamicZoneMembersRepository::RemoveMember(GetDatabase(), m_id, remove_member.id); + GetDatabase().RemoveClientFromInstance(m_instance_id, remove_member.id); + + ProcessMemberAddRemove(remove_member, true); + SendServerPacket(CreateServerMemberAddRemovePacket(remove_member, true).get()); + + return true; +} + +bool DynamicZoneBase::SwapMember( + const DynamicZoneMember& add_member, const std::string& remove_char_name) +{ + auto remove_member = GetMemberData(remove_char_name); + if (!add_member.IsValid() || !remove_member.IsValid()) + { + return false; + } + + // make remove and add atomic to avoid racing with separate world messages + DynamicZoneMembersRepository::RemoveMember(GetDatabase(), m_id, remove_member.id); + GetDatabase().RemoveClientFromInstance(m_instance_id, remove_member.id); + + DynamicZoneMembersRepository::AddMember(GetDatabase(), m_id, add_member.id); + GetDatabase().AddClientToInstance(m_instance_id, add_member.id); + + ProcessMemberAddRemove(remove_member, true); + ProcessMemberAddRemove(add_member, false); + SendServerPacket(CreateServerMemberSwapPacket(remove_member, add_member).get()); + + return true; +} + +void DynamicZoneBase::RemoveAllMembers() +{ DynamicZoneMembersRepository::RemoveAllMembers(GetDatabase(), m_id); GetDatabase().RemoveClientsFromInstance(GetInstanceID()); + + ProcessRemoveAllMembers(); + SendServerPacket(CreateServerRemoveAllMembersPacket().get()); } void DynamicZoneBase::SaveMembers(const std::vector& members) @@ -181,7 +240,6 @@ void DynamicZoneBase::SaveMembers(const std::vector& members) DynamicZoneMembersRepository::DynamicZoneMembers member_entry{}; member_entry.dynamic_zone_id = m_id; member_entry.character_id = member.id; - member_entry.is_current_member = true; insert_members.emplace_back(member_entry); InstanceListPlayerRepository::InstanceListPlayer player_entry; @@ -206,7 +264,7 @@ void DynamicZoneBase::SetCompass(const DynamicZoneLocation& location, bool updat DynamicZonesRepository::UpdateCompass(GetDatabase(), m_id, m_compass.zone_id, m_compass.x, m_compass.y, m_compass.z); - SendGlobalLocationChange(ServerOP_DzSetCompass, location); + SendServerPacket(CreateServerDzLocationPacket(ServerOP_DzSetCompass, location).get()); } } @@ -227,7 +285,7 @@ void DynamicZoneBase::SetSafeReturn(const DynamicZoneLocation& location, bool up DynamicZonesRepository::UpdateSafeReturn(GetDatabase(), m_id, m_safereturn.zone_id, m_safereturn.x, m_safereturn.y, m_safereturn.z, m_safereturn.heading); - SendGlobalLocationChange(ServerOP_DzSetSafeReturn, location); + SendServerPacket(CreateServerDzLocationPacket(ServerOP_DzSetSafeReturn, location).get()); } } @@ -249,7 +307,7 @@ void DynamicZoneBase::SetZoneInLocation(const DynamicZoneLocation& location, boo DynamicZonesRepository::UpdateZoneIn(GetDatabase(), m_id, m_zone_id, m_zonein.x, m_zonein.y, m_zonein.z, m_zonein.heading, m_has_zonein); - SendGlobalLocationChange(ServerOP_DzSetZoneIn, location); + SendServerPacket(CreateServerDzLocationPacket(ServerOP_DzSetZoneIn, location).get()); } } @@ -258,35 +316,71 @@ void DynamicZoneBase::SetZoneInLocation(float x, float y, float z, float heading SetZoneInLocation({ 0, x, y, z, heading }, update_db); } +void DynamicZoneBase::SetLeader(const DynamicZoneMember& new_leader, bool update_db) +{ + m_leader = new_leader; + + if (update_db) + { + DynamicZonesRepository::UpdateLeaderID(GetDatabase(), m_id, new_leader.id); + } +} + uint32_t DynamicZoneBase::GetSecondsRemaining() const { auto remaining = std::chrono::duration_cast(GetDurationRemaining()).count(); return std::max(0, static_cast(remaining)); } -std::unique_ptr DynamicZoneBase::CreateServerAddRemoveCharacterPacket( - uint32_t character_id, bool removed) +std::unique_ptr DynamicZoneBase::CreateServerMemberAddRemovePacket( + const DynamicZoneMember& member, bool removed) { - constexpr uint32_t pack_size = sizeof(ServerDzCharacter_Struct); - auto pack = std::make_unique(ServerOP_DzAddRemoveCharacter, pack_size); - auto buf = reinterpret_cast(pack->pBuffer); - buf->zone_id = GetZoneID(); - buf->instance_id = GetInstanceID(); - buf->remove = removed; - buf->character_id = character_id; + constexpr uint32_t pack_size = sizeof(ServerDzMember_Struct); + auto pack = std::make_unique(ServerOP_DzAddRemoveMember, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + buf->dz_id = GetID(); + buf->dz_zone_id = GetZoneID(); + buf->dz_instance_id = GetInstanceID(); + buf->sender_zone_id = GetCurrentZoneID(); + buf->sender_instance_id = GetCurrentInstanceID(); + buf->removed = removed; + buf->character_id = member.id; + buf->character_status = static_cast(member.status); + strn0cpy(buf->character_name, member.name.c_str(), sizeof(buf->character_name)); return pack; } -std::unique_ptr DynamicZoneBase::CreateServerRemoveAllCharactersPacket() +std::unique_ptr DynamicZoneBase::CreateServerMemberSwapPacket( + const DynamicZoneMember& remove_member, const DynamicZoneMember& add_member) { - constexpr uint32_t pack_size = sizeof(ServerDzCharacter_Struct); - auto pack = std::make_unique(ServerOP_DzRemoveAllCharacters, pack_size); - auto buf = reinterpret_cast(pack->pBuffer); - buf->zone_id = GetZoneID(); - buf->instance_id = GetInstanceID(); - buf->remove = true; - buf->character_id = 0; + constexpr uint32_t pack_size = sizeof(ServerDzMemberSwap_Struct); + auto pack = std::make_unique(ServerOP_DzSwapMembers, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + buf->dz_id = GetID(); + buf->dz_zone_id = GetZoneID(); + buf->dz_instance_id = GetInstanceID(); + buf->sender_zone_id = GetCurrentZoneID(); + buf->sender_instance_id = GetCurrentInstanceID(); + buf->add_character_status = static_cast(add_member.status); + buf->add_character_id = add_member.id; + buf->remove_character_id = remove_member.id; + strn0cpy(buf->add_character_name, add_member.name.c_str(), sizeof(buf->add_character_name)); + strn0cpy(buf->remove_character_name, remove_member.name.c_str(), sizeof(buf->remove_character_name)); + + return pack; +} + +std::unique_ptr DynamicZoneBase::CreateServerRemoveAllMembersPacket() +{ + constexpr uint32_t pack_size = sizeof(ServerDzID_Struct); + auto pack = std::make_unique(ServerOP_DzRemoveAllMembers, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + buf->dz_id = GetID(); + buf->dz_zone_id = GetZoneID(); + buf->dz_instance_id = GetInstanceID(); + buf->sender_zone_id = GetCurrentZoneID(); + buf->sender_instance_id = GetCurrentInstanceID(); return pack; } @@ -309,10 +403,25 @@ std::unique_ptr DynamicZoneBase::CreateServerDzLocationPacket( return pack; } +std::unique_ptr DynamicZoneBase::CreateServerMemberStatusPacket( + uint32_t character_id, DynamicZoneMemberStatus status) +{ + constexpr uint32_t pack_size = sizeof(ServerDzMemberStatus_Struct); + auto pack = std::make_unique(ServerOP_DzUpdateMemberStatus, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + buf->dz_id = GetID(); + buf->sender_zone_id = GetCurrentZoneID(); + buf->sender_instance_id = GetCurrentInstanceID(); + buf->status = static_cast(status); + buf->character_id = character_id; + + return pack; +} + uint32_t DynamicZoneBase::GetDatabaseMemberCount() { return DynamicZoneMembersRepository::GetCountWhere(GetDatabase(), - fmt::format("dynamic_zone_id = {} AND is_current_member = TRUE", m_id)); + fmt::format("dynamic_zone_id = {}", m_id)); } bool DynamicZoneBase::HasDatabaseMember(uint32_t character_id) @@ -323,7 +432,7 @@ bool DynamicZoneBase::HasDatabaseMember(uint32_t character_id) } auto entries = DynamicZoneMembersRepository::GetWhere(GetDatabase(), fmt::format( - "dynamic_zone_id = {} AND character_id = {} AND is_current_member = TRUE", + "dynamic_zone_id = {} AND character_id = {}", m_id, character_id )); @@ -411,6 +520,33 @@ bool DynamicZoneBase::SetInternalMemberStatus(uint32_t character_id, DynamicZone return false; } +void DynamicZoneBase::SetMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status) +{ + auto update_member = GetMemberData(character_id); + if (update_member.IsValid()) + { + ProcessMemberStatusChange(character_id, status); + SendServerPacket(CreateServerMemberStatusPacket(character_id, status).get()); + } +} + +void DynamicZoneBase::ProcessMemberAddRemove(const DynamicZoneMember& member, bool removed) +{ + if (!removed) + { + AddInternalMember(member); + } + else + { + RemoveInternalMember(member.id); + } +} + +bool DynamicZoneBase::ProcessMemberStatusChange(uint32_t character_id, DynamicZoneMemberStatus status) +{ + return SetInternalMemberStatus(character_id, status); +} + std::string DynamicZoneBase::GetDynamicZoneTypeName(DynamicZoneType dz_type) { switch (dz_type) @@ -428,3 +564,36 @@ std::string DynamicZoneBase::GetDynamicZoneTypeName(DynamicZoneType dz_type) } return "Unknown"; } + +EQ::Net::DynamicPacket DynamicZoneBase::GetSerializedDzPacket() +{ + EQ::Net::DynamicPacket dyn_pack; + dyn_pack.PutSerialize(0, *this); + + LogDynamicZonesDetail("Serialized server dz size [{}]", dyn_pack.Length()); + return dyn_pack; +} + +std::unique_ptr DynamicZoneBase::CreateServerDzCreatePacket( + uint16_t origin_zone_id, uint16_t origin_instance_id) +{ + EQ::Net::DynamicPacket dyn_pack = GetSerializedDzPacket(); + + auto pack_size = sizeof(ServerDzCreateSerialized_Struct) + dyn_pack.Length(); + auto pack = std::make_unique(ServerOP_DzCreated, static_cast(pack_size)); + auto buf = reinterpret_cast(pack->pBuffer); + buf->origin_zone_id = origin_zone_id; + buf->origin_instance_id = origin_instance_id; + buf->cereal_size = static_cast(dyn_pack.Length()); + memcpy(buf->cereal_data, dyn_pack.Data(), dyn_pack.Length()); + + return pack; +} + +void DynamicZoneBase::LoadSerializedDzPacket(char* cereal_data, uint32_t cereal_size) +{ + LogDynamicZonesDetail("Deserializing server dz size [{}]", cereal_size); + EQ::Util::MemoryStreamReader ss(cereal_data, cereal_size); + cereal::BinaryInputArchive archive(ss); + archive(*this); +} diff --git a/common/dynamic_zone_base.h b/common/dynamic_zone_base.h index bc325532e..eafea5ca0 100644 --- a/common/dynamic_zone_base.h +++ b/common/dynamic_zone_base.h @@ -2,6 +2,7 @@ #define COMMON_DYNAMIC_ZONE_BASE_H #include "eq_constants.h" +#include "net/packet.h" #include "repositories/dynamic_zones_repository.h" #include "repositories/dynamic_zone_members_repository.h" #include @@ -18,7 +19,7 @@ struct DynamicZoneMember { uint32_t id = 0; std::string name; - DynamicZoneMemberStatus status = DynamicZoneMemberStatus::Online; + DynamicZoneMemberStatus status = DynamicZoneMemberStatus::Unknown; DynamicZoneMember() = default; DynamicZoneMember(uint32_t id, std::string name_) @@ -29,6 +30,12 @@ struct DynamicZoneMember bool IsOnline() const { return status == DynamicZoneMemberStatus::Online || status == DynamicZoneMemberStatus::InDynamicZone; } bool IsValid() const { return id != 0 && !name.empty(); } + + template + void serialize(Archive& archive) + { + archive(id, name, status); + } }; struct DynamicZoneLocation @@ -42,6 +49,12 @@ struct DynamicZoneLocation DynamicZoneLocation() = default; DynamicZoneLocation(uint32_t zone_id_, float x_, float y_, float z_, float heading_) : zone_id(zone_id_), x(x_), y(y_), z(z_), heading(heading_) {} + + template + void serialize(Archive& archive) + { + archive(zone_id, x, y, z, heading); + } }; class DynamicZoneBase @@ -64,6 +77,7 @@ public: uint64_t GetExpireTime() const { return std::chrono::system_clock::to_time_t(m_expire_time); } uint32_t GetID() const { return m_id; } uint16_t GetInstanceID() const { return static_cast(m_instance_id); } + uint32_t GetLeaderID() const { return m_leader.id; } uint32_t GetMaxPlayers() const { return m_max_players; } uint32_t GetMemberCount() const { return static_cast(m_members.size()); } uint32_t GetMinPlayers() const { return m_min_players; } @@ -74,6 +88,7 @@ public: DynamicZoneType GetType() const { return m_type; } const std::string& GetLeaderName() const { return m_leader.name; } const std::string& GetName() const { return m_name; } + const std::string& GetUUID() const { return m_uuid; } const DynamicZoneMember& GetLeader() const { return m_leader; } const std::vector& GetMembers() const { return m_members; } const DynamicZoneLocation& GetCompassLocation() const { return m_compass; } @@ -81,14 +96,12 @@ public: const DynamicZoneLocation& GetZoneInLocation() const { return m_zonein; } std::chrono::system_clock::duration GetDurationRemaining() const { return m_expire_time - std::chrono::system_clock::now(); } - void AddCharacter(uint32_t character_id); - void AddInternalMember(const DynamicZoneMember& member); + bool AddMember(const DynamicZoneMember& add_member); void AddMemberFromRepositoryResult(DynamicZoneMembersRepository::MemberWithName&& entry); - void ClearInternalMembers() { m_members.clear(); } - uint32_t Create(); uint32_t GetDatabaseMemberCount(); DynamicZoneMember GetMemberData(uint32_t character_id); DynamicZoneMember GetMemberData(const std::string& character_name); + EQ::Net::DynamicPacket GetSerializedDzPacket(); bool HasDatabaseMember(uint32_t character_id); bool HasMember(uint32_t character_id); bool HasMember(const std::string& character_name); @@ -98,38 +111,52 @@ public: bool IsInstanceID(uint32_t instance_id) const { return (m_instance_id != 0 && m_instance_id == instance_id); } bool IsValid() const { return m_instance_id != 0; } bool IsSameDz(uint32_t zone_id, uint32_t instance_id) const { return zone_id == m_zone_id && instance_id == m_instance_id; } - void RemoveAllCharacters(bool enable_removal_timers = true); - void RemoveCharacter(uint32_t character_id); - void RemoveInternalMember(uint32_t character_id); + void LoadSerializedDzPacket(char* cereal_data, uint32_t cereal_size); + void RemoveAllMembers(); + bool RemoveMember(uint32_t character_id); + bool RemoveMember(const std::string& character_name); + bool RemoveMember(const DynamicZoneMember& remove_member); void SaveMembers(const std::vector& members); void SetCompass(const DynamicZoneLocation& location, bool update_db = false); void SetCompass(uint32_t zone_id, float x, float y, float z, bool update_db = false); - bool SetInternalMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status); - void SetLeader(const DynamicZoneMember& leader) { m_leader = leader; } + void SetDuration(uint32_t seconds) { m_duration = std::chrono::seconds(seconds); } + void SetLeader(const DynamicZoneMember& leader, bool update_db = false); void SetMaxPlayers(uint32_t max_players) { m_max_players = max_players; } + void SetMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status); void SetMinPlayers(uint32_t min_players) { m_min_players = min_players; } void SetName(const std::string& name) { m_name = name; } void SetSafeReturn(const DynamicZoneLocation& location, bool update_db = false); void SetSafeReturn(uint32_t zone_id, float x, float y, float z, float heading, bool update_db = false); + void SetType(DynamicZoneType type) { m_type = type; } + void SetUUID(std::string uuid) { m_uuid = std::move(uuid); } void SetZoneInLocation(const DynamicZoneLocation& location, bool update_db = false); void SetZoneInLocation(float x, float y, float z, float heading, bool update_db = false); + bool SwapMember(const DynamicZoneMember& add_member, const std::string& remove_char_name); protected: virtual uint16_t GetCurrentInstanceID() { return 0; } virtual uint16_t GetCurrentZoneID() { return 0; } virtual Database& GetDatabase() = 0; virtual void ProcessCompassChange(const DynamicZoneLocation& location) { m_compass = location; } - virtual void SendInstanceAddRemoveCharacter(uint32_t character_id, bool remove) = 0; - virtual void SendInstanceRemoveAllCharacters() = 0; - virtual void SendGlobalLocationChange(uint16_t server_opcode, const DynamicZoneLocation& location) = 0; + virtual void ProcessMemberAddRemove(const DynamicZoneMember& member, bool removed); + virtual bool ProcessMemberStatusChange(uint32_t member_id, DynamicZoneMemberStatus status); + virtual void ProcessRemoveAllMembers(bool silent = false) { m_members.clear(); } + virtual bool SendServerPacket(ServerPacket* packet) = 0; + void AddInternalMember(const DynamicZoneMember& member); + uint32_t Create(); uint32_t CreateInstance(); void LoadRepositoryResult(DynamicZonesRepository::DynamicZoneInstance&& dz_entry); + void RemoveInternalMember(uint32_t character_id); uint32_t SaveToDatabase(); + bool SetInternalMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status); - std::unique_ptr CreateServerAddRemoveCharacterPacket(uint32_t character_id, bool removed); - std::unique_ptr CreateServerRemoveAllCharactersPacket(); + std::unique_ptr CreateServerDzCreatePacket(uint16_t origin_zone_id, uint16_t origin_instance_id); std::unique_ptr CreateServerDzLocationPacket(uint16_t server_opcode, const DynamicZoneLocation& location); + std::unique_ptr CreateServerMemberAddRemovePacket(const DynamicZoneMember& member, bool removed); + std::unique_ptr CreateServerMemberStatusPacket(uint32_t character_id, DynamicZoneMemberStatus status); + std::unique_ptr CreateServerMemberSwapPacket(const DynamicZoneMember& remove_member, const DynamicZoneMember& add_member); + std::unique_ptr CreateServerRemoveAllMembersPacket(); uint32_t m_id = 0; uint32_t m_zone_id = 0; @@ -139,7 +166,9 @@ protected: uint32_t m_max_players = 0; bool m_never_expires = false; bool m_has_zonein = false; + bool m_has_member_statuses = false; std::string m_name; + std::string m_uuid; DynamicZoneMember m_leader; DynamicZoneType m_type{ DynamicZoneType::None }; DynamicZoneLocation m_compass; @@ -149,6 +178,34 @@ protected: std::chrono::time_point m_start_time; std::chrono::time_point m_expire_time; std::vector m_members; + +public: + template + void serialize(Archive& archive) + { + archive( + m_id, + m_zone_id, + m_instance_id, + m_zone_version, + m_min_players, + m_max_players, + m_never_expires, + m_has_zonein, + m_has_member_statuses, + m_name, + m_uuid, + m_leader, + m_type, + m_compass, + m_safereturn, + m_zonein, + m_duration, + m_start_time, + m_expire_time, + m_members + ); + } }; #endif diff --git a/common/emu_oplist.h b/common/emu_oplist.h index d81ed3a55..869a9bd3a 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -355,7 +355,6 @@ N(OP_OpenContainer), N(OP_OpenDiscordMerchant), N(OP_OpenGuildTributeMaster), N(OP_OpenInventory), -N(OP_OpenNewTasksWindow), N(OP_OpenTributeMaster), N(OP_PDeletePetition), N(OP_PetBuffWindow), @@ -464,6 +463,19 @@ N(OP_SetServerFilter), N(OP_SetStartCity), N(OP_SetTitle), N(OP_SetTitleReply), +N(OP_SharedTaskMemberList), +N(OP_SharedTaskAddPlayer), +N(OP_SharedTaskRemovePlayer), +N(OP_SharedTaskMakeLeader), +N(OP_SharedTaskMemberInvite), +N(OP_SharedTaskInvite), +N(OP_SharedTaskInviteResponse), +N(OP_SharedTaskAcceptNew), +N(OP_SharedTaskMemberChange), +N(OP_SharedTaskPlayerList), +N(OP_SharedTaskSelectWindow), +N(OP_SharedTaskQuit), +N(OP_TaskTimers), N(OP_Shielding), N(OP_ShopDelItem), N(OP_ShopEnd), @@ -499,7 +511,8 @@ N(OP_TaskActivityComplete), N(OP_TaskDescription), N(OP_TaskHistoryReply), N(OP_TaskHistoryRequest), -N(OP_TaskMemberList), +N(OP_TaskRequestTimer), +N(OP_TaskSelectWindow), N(OP_Taunt), N(OP_TestBuff), N(OP_TGB), diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 308ba22e9..47f14bb4e 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -3714,17 +3714,66 @@ struct SetTitleReply_Struct { uint32 entity_id; }; -struct TaskMemberList_Struct { -/*00*/ uint32 gopher_id; -/*04*/ uint32 unknown04; -/*08*/ uint32 member_count; //1 less than the number of members -/*12*/ char list_pointer[0]; +struct SharedTaskMemberList_Struct { +/*00*/ uint32 gopher_id; +/*04*/ uint32 unknown04; +/*08*/ uint32 member_count; //1 less than the number of members +///*12*/ char list_pointer[0]; + char member_name[1]; //null terminated string + uint32 monster_mission; // class chosen + uint8 task_leader; //boolean flag + /* list is of the form: - char member_name[1] //null terminated string uint8 task_leader //boolean flag */ }; +struct SharedTaskQuit_Struct { + int32 field1; + int32 field2; + int32 field3; +}; + +struct SharedTaskAddPlayer_Struct { + int32 field1; + int32 field2; + char player_name[64]; +}; + +struct SharedTaskMakeLeader_Struct { + int32 field1; + int32 field2; + char player_name[64]; +}; + +struct SharedTaskRemovePlayer_Struct { + int32 field1; + int32 field2; + char player_name[64]; +}; + +struct SharedTaskInvite_Struct { + int32_t unknown00; // probably the unique character id sent in some packets + int32_t invite_id; // invite id sent back in response + char task_name[64]; + char inviter_name[64]; +}; + +struct SharedTaskInviteResponse_Struct { + int32_t unknown00; // 0 + int32_t invite_id; // same id sent in the invite, probably for server verification + int8_t accepted; // 0: declined 1: accepted + int8_t padding[3]; // padding garbage probably +}; + +struct SharedTaskAccept_Struct { + int32_t unknown00; + int32_t unknown04; + uint32_t npc_entity_id; // npc task giver entity id (sent in selection window) + uint32_t task_id; + float reward_multiplier; // added after titanium (sent in selection window) +}; + #if 0 // Old struct not used by Task System implementation but left for reference @@ -3817,7 +3866,7 @@ struct TaskHistory_Struct { #endif struct AcceptNewTask_Struct { - uint32 unknown00; + uint32 task_type; // type sent in selection window uint32 task_id; //set to 0 for 'decline' uint32 task_master_id; //entity ID }; diff --git a/common/eqemu_logsys.cpp b/common/eqemu_logsys.cpp index 3666735e4..83e9a45dd 100644 --- a/common/eqemu_logsys.cpp +++ b/common/eqemu_logsys.cpp @@ -235,9 +235,11 @@ void EQEmuLogSys::ProcessGMSay( } /** - * Check to see if the process that actually ran this is zone + * Processes that actually support hooks */ - if (EQEmuLogSys::log_platform == EQEmuExePlatform::ExePlatformZone) { + if (EQEmuLogSys::log_platform == EQEmuExePlatform::ExePlatformZone || + EQEmuLogSys::log_platform == EQEmuExePlatform::ExePlatformWorld + ) { on_log_gmsay_hook(log_category, message); } } diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 6e881eecc..c5ced8925 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -123,6 +123,7 @@ namespace Logs { DynamicZones, Scheduler, Cheat, + ClientList, MaxCategoryID /* Don't Remove this */ }; @@ -203,7 +204,8 @@ namespace Logs { "Expeditions", "DynamicZones", "Scheduler", - "Cheat" + "Cheat", + "ClientList", }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index b683d9111..c5f7cd422 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -656,6 +656,16 @@ OutF(LogSys, Logs::Detail, Logs::Cheat, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogClientList(message, ...) do {\ + if (LogSys.log_settings[Logs::ClientList].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::ClientList, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogClientListDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::ClientList].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::ClientList, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + #define Log(debug_level, log_category, message, ...) do {\ if (LogSys.log_settings[log_category].is_category_enabled == 1)\ LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ @@ -1028,6 +1038,18 @@ #define LogDynamicZonesDetail(message, ...) do {\ } while (0) +#define LogCheatList(message, ...) do {\ +} while (0) + +#define LogCheatDetail(message, ...) do {\ +} while (0) + +#define LogClientList(message, ...) do {\ +} while (0) + +#define LogClientListDetail(message, ...) do {\ +} while (0) + #define Log(debug_level, log_category, message, ...) do {\ } while (0) diff --git a/common/expedition_base.cpp b/common/expedition_base.cpp deleted file mode 100644 index af961e4a9..000000000 --- a/common/expedition_base.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "expedition_base.h" -#include "repositories/expeditions_repository.h" - -ExpeditionBase::ExpeditionBase(uint32_t id, const std::string& uuid, - const std::string& expedition_name, const DynamicZoneMember& leader -) : - m_id(id), - m_uuid(uuid), - m_expedition_name(expedition_name), - m_leader(leader) -{ -} - -void ExpeditionBase::LoadRepositoryResult(ExpeditionsRepository::ExpeditionWithLeader&& entry) -{ - m_id = entry.id; - m_uuid = std::move(entry.uuid); - m_expedition_name = std::move(entry.expedition_name); - m_add_replay_on_join = entry.add_replay_on_join; - m_is_locked = entry.is_locked; - m_leader.id = entry.leader_id; - m_leader.name = std::move(entry.leader_name); -} diff --git a/common/expedition_base.h b/common/expedition_base.h deleted file mode 100644 index cb6f6a591..000000000 --- a/common/expedition_base.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef COMMON_EXPEDITION_BASE_H -#define COMMON_EXPEDITION_BASE_H - -#include "dynamic_zone_base.h" -#include "repositories/expeditions_repository.h" -#include -#include - -class ExpeditionBase -{ -public: - virtual ~ExpeditionBase() = default; - ExpeditionBase(const ExpeditionBase&) = default; - ExpeditionBase(ExpeditionBase&&) = default; - ExpeditionBase& operator=(const ExpeditionBase&) = default; - ExpeditionBase& operator=(ExpeditionBase&&) = default; - - uint32_t GetID() const { return m_id; } - uint32_t GetLeaderID() const { return m_leader.id; } - const std::string& GetName() const { return m_expedition_name; } - const std::string& GetLeaderName() const { return m_leader.name; } - const std::string& GetUUID() const { return m_uuid; } - const DynamicZoneMember& GetLeader() const { return m_leader; } - - void LoadRepositoryResult(ExpeditionsRepository::ExpeditionWithLeader&& entry); - -protected: - ExpeditionBase() = default; - ExpeditionBase(uint32_t id, const std::string& uuid, const std::string& expedition_name, - const DynamicZoneMember& leader); - - uint32_t m_id = 0; - bool m_is_locked = false; - bool m_add_replay_on_join = true; - std::string m_uuid; - std::string m_expedition_name; - DynamicZoneMember m_leader; -}; - -#endif diff --git a/common/repositories/base/base_character_instance_safereturns_repository.h b/common/repositories/base/base_character_instance_safereturns_repository.h new file mode 100644 index 000000000..735c2e4c0 --- /dev/null +++ b/common/repositories/base/base_character_instance_safereturns_repository.h @@ -0,0 +1,355 @@ +/** + * DO NOT MODIFY THIS FILE + * + * This repository was automatically generated and is NOT to be modified directly. + * Any repository modifications are meant to be made to the repository extending the base. + * Any modifications to base repositories are to be made by the generator only + * + * @generator ./utils/scripts/generators/repository-generator.pl + * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories + */ + +#ifndef EQEMU_BASE_CHARACTER_INSTANCE_SAFERETURNS_REPOSITORY_H +#define EQEMU_BASE_CHARACTER_INSTANCE_SAFERETURNS_REPOSITORY_H + +#include "../../database.h" +#include "../../string_util.h" + +class BaseCharacterInstanceSafereturnsRepository { +public: + struct CharacterInstanceSafereturns { + int id; + int character_id; + int instance_zone_id; + int instance_id; + int safe_zone_id; + float safe_x; + float safe_y; + float safe_z; + float safe_heading; + }; + + static std::string PrimaryKey() + { + return std::string("id"); + } + + static std::vector Columns() + { + return { + "id", + "character_id", + "instance_zone_id", + "instance_id", + "safe_zone_id", + "safe_x", + "safe_y", + "safe_z", + "safe_heading", + }; + } + + static std::string ColumnsRaw() + { + return std::string(implode(", ", Columns())); + } + + static std::string TableName() + { + return std::string("character_instance_safereturns"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + ColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static CharacterInstanceSafereturns NewEntity() + { + CharacterInstanceSafereturns entry{}; + + entry.id = 0; + entry.character_id = 0; + entry.instance_zone_id = 0; + entry.instance_id = 0; + entry.safe_zone_id = 0; + entry.safe_x = 0; + entry.safe_y = 0; + entry.safe_z = 0; + entry.safe_heading = 0; + + return entry; + } + + static CharacterInstanceSafereturns GetCharacterInstanceSafereturnsEntry( + const std::vector &character_instance_safereturnss, + int character_instance_safereturns_id + ) + { + for (auto &character_instance_safereturns : character_instance_safereturnss) { + if (character_instance_safereturns.id == character_instance_safereturns_id) { + return character_instance_safereturns; + } + } + + return NewEntity(); + } + + static CharacterInstanceSafereturns FindOne( + Database& db, + int character_instance_safereturns_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE id = {} LIMIT 1", + BaseSelect(), + character_instance_safereturns_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + CharacterInstanceSafereturns entry{}; + + entry.id = atoi(row[0]); + entry.character_id = atoi(row[1]); + entry.instance_zone_id = atoi(row[2]); + entry.instance_id = atoi(row[3]); + entry.safe_zone_id = atoi(row[4]); + entry.safe_x = static_cast(atof(row[5])); + entry.safe_y = static_cast(atof(row[6])); + entry.safe_z = static_cast(atof(row[7])); + entry.safe_heading = static_cast(atof(row[8])); + + return entry; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int character_instance_safereturns_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + character_instance_safereturns_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + CharacterInstanceSafereturns character_instance_safereturns_entry + ) + { + std::vector update_values; + + auto columns = Columns(); + + update_values.push_back(columns[1] + " = " + std::to_string(character_instance_safereturns_entry.character_id)); + update_values.push_back(columns[2] + " = " + std::to_string(character_instance_safereturns_entry.instance_zone_id)); + update_values.push_back(columns[3] + " = " + std::to_string(character_instance_safereturns_entry.instance_id)); + update_values.push_back(columns[4] + " = " + std::to_string(character_instance_safereturns_entry.safe_zone_id)); + update_values.push_back(columns[5] + " = " + std::to_string(character_instance_safereturns_entry.safe_x)); + update_values.push_back(columns[6] + " = " + std::to_string(character_instance_safereturns_entry.safe_y)); + update_values.push_back(columns[7] + " = " + std::to_string(character_instance_safereturns_entry.safe_z)); + update_values.push_back(columns[8] + " = " + std::to_string(character_instance_safereturns_entry.safe_heading)); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + implode(", ", update_values), + PrimaryKey(), + character_instance_safereturns_entry.id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static CharacterInstanceSafereturns InsertOne( + Database& db, + CharacterInstanceSafereturns character_instance_safereturns_entry + ) + { + std::vector insert_values; + + insert_values.push_back(std::to_string(character_instance_safereturns_entry.id)); + insert_values.push_back(std::to_string(character_instance_safereturns_entry.character_id)); + insert_values.push_back(std::to_string(character_instance_safereturns_entry.instance_zone_id)); + insert_values.push_back(std::to_string(character_instance_safereturns_entry.instance_id)); + insert_values.push_back(std::to_string(character_instance_safereturns_entry.safe_zone_id)); + insert_values.push_back(std::to_string(character_instance_safereturns_entry.safe_x)); + insert_values.push_back(std::to_string(character_instance_safereturns_entry.safe_y)); + insert_values.push_back(std::to_string(character_instance_safereturns_entry.safe_z)); + insert_values.push_back(std::to_string(character_instance_safereturns_entry.safe_heading)); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseInsert(), + implode(",", insert_values) + ) + ); + + if (results.Success()) { + character_instance_safereturns_entry.id = results.LastInsertedID(); + return character_instance_safereturns_entry; + } + + character_instance_safereturns_entry = NewEntity(); + + return character_instance_safereturns_entry; + } + + static int InsertMany( + Database& db, + std::vector character_instance_safereturns_entries + ) + { + std::vector insert_chunks; + + for (auto &character_instance_safereturns_entry: character_instance_safereturns_entries) { + std::vector insert_values; + + insert_values.push_back(std::to_string(character_instance_safereturns_entry.id)); + insert_values.push_back(std::to_string(character_instance_safereturns_entry.character_id)); + insert_values.push_back(std::to_string(character_instance_safereturns_entry.instance_zone_id)); + insert_values.push_back(std::to_string(character_instance_safereturns_entry.instance_id)); + insert_values.push_back(std::to_string(character_instance_safereturns_entry.safe_zone_id)); + insert_values.push_back(std::to_string(character_instance_safereturns_entry.safe_x)); + insert_values.push_back(std::to_string(character_instance_safereturns_entry.safe_y)); + insert_values.push_back(std::to_string(character_instance_safereturns_entry.safe_z)); + insert_values.push_back(std::to_string(character_instance_safereturns_entry.safe_heading)); + + insert_chunks.push_back("(" + implode(",", insert_values) + ")"); + } + + std::vector insert_values; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseInsert(), + implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static std::vector All(Database& db) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{}", + BaseSelect() + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + CharacterInstanceSafereturns entry{}; + + entry.id = atoi(row[0]); + entry.character_id = atoi(row[1]); + entry.instance_zone_id = atoi(row[2]); + entry.instance_id = atoi(row[3]); + entry.safe_zone_id = atoi(row[4]); + entry.safe_x = static_cast(atof(row[5])); + entry.safe_y = static_cast(atof(row[6])); + entry.safe_z = static_cast(atof(row[7])); + entry.safe_heading = static_cast(atof(row[8])); + + all_entries.push_back(entry); + } + + return all_entries; + } + + static std::vector GetWhere(Database& db, std::string where_filter) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {}", + BaseSelect(), + where_filter + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + CharacterInstanceSafereturns entry{}; + + entry.id = atoi(row[0]); + entry.character_id = atoi(row[1]); + entry.instance_zone_id = atoi(row[2]); + entry.instance_id = atoi(row[3]); + entry.safe_zone_id = atoi(row[4]); + entry.safe_x = static_cast(atof(row[5])); + entry.safe_y = static_cast(atof(row[6])); + entry.safe_z = static_cast(atof(row[7])); + entry.safe_heading = static_cast(atof(row[8])); + + all_entries.push_back(entry); + } + + return all_entries; + } + + static int DeleteWhere(Database& db, std::string where_filter) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {}", + TableName(), + where_filter + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int Truncate(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "TRUNCATE TABLE {}", + TableName() + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + +}; + +#endif //EQEMU_BASE_CHARACTER_INSTANCE_SAFERETURNS_REPOSITORY_H diff --git a/common/repositories/base/base_character_task_timers_repository.h b/common/repositories/base/base_character_task_timers_repository.h new file mode 100644 index 000000000..f7b5fda49 --- /dev/null +++ b/common/repositories/base/base_character_task_timers_repository.h @@ -0,0 +1,336 @@ +/** + * DO NOT MODIFY THIS FILE + * + * This repository was automatically generated and is NOT to be modified directly. + * Any repository modifications are meant to be made to the repository extending the base. + * Any modifications to base repositories are to be made by the generator only + * + * @generator ./utils/scripts/generators/repository-generator.pl + * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories + */ + +#ifndef EQEMU_BASE_CHARACTER_TASK_TIMERS_REPOSITORY_H +#define EQEMU_BASE_CHARACTER_TASK_TIMERS_REPOSITORY_H + +#include "../../database.h" +#include "../../string_util.h" +#include + +class BaseCharacterTaskTimersRepository { +public: + struct CharacterTaskTimers { + int id; + int character_id; + int task_id; + int timer_type; + time_t expire_time; + }; + + static std::string PrimaryKey() + { + return std::string("id"); + } + + static std::vector Columns() + { + return { + "id", + "character_id", + "task_id", + "timer_type", + "expire_time", + }; + } + + static std::vector SelectColumns() + { + return { + "id", + "character_id", + "task_id", + "timer_type", + "UNIX_TIMESTAMP(expire_time)", + }; + } + + static std::string ColumnsRaw() + { + return std::string(implode(", ", Columns())); + } + + static std::string SelectColumnsRaw() + { + return std::string(implode(", ", SelectColumns())); + } + + static std::string TableName() + { + return std::string("character_task_timers"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + SelectColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static CharacterTaskTimers NewEntity() + { + CharacterTaskTimers entry{}; + + entry.id = 0; + entry.character_id = 0; + entry.task_id = 0; + entry.timer_type = 0; + entry.expire_time = std::time(nullptr); + + return entry; + } + + static CharacterTaskTimers GetCharacterTaskTimersEntry( + const std::vector &character_task_timerss, + int character_task_timers_id + ) + { + for (auto &character_task_timers : character_task_timerss) { + if (character_task_timers.id == character_task_timers_id) { + return character_task_timers; + } + } + + return NewEntity(); + } + + static CharacterTaskTimers FindOne( + Database& db, + int character_task_timers_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE id = {} LIMIT 1", + BaseSelect(), + character_task_timers_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + CharacterTaskTimers entry{}; + + entry.id = atoi(row[0]); + entry.character_id = atoi(row[1]); + entry.task_id = atoi(row[2]); + entry.timer_type = atoi(row[3]); + entry.expire_time = strtoll(row[4], nullptr, 10); + + return entry; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int character_task_timers_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + character_task_timers_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + CharacterTaskTimers character_task_timers_entry + ) + { + std::vector update_values; + + auto columns = Columns(); + + update_values.push_back(columns[1] + " = " + std::to_string(character_task_timers_entry.character_id)); + update_values.push_back(columns[2] + " = " + std::to_string(character_task_timers_entry.task_id)); + update_values.push_back(columns[3] + " = " + std::to_string(character_task_timers_entry.timer_type)); + update_values.push_back(columns[4] + " = FROM_UNIXTIME(" + std::to_string(character_task_timers_entry.expire_time) + ")"); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + implode(", ", update_values), + PrimaryKey(), + character_task_timers_entry.id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static CharacterTaskTimers InsertOne( + Database& db, + CharacterTaskTimers character_task_timers_entry + ) + { + std::vector insert_values; + + insert_values.push_back(std::to_string(character_task_timers_entry.id)); + insert_values.push_back(std::to_string(character_task_timers_entry.character_id)); + insert_values.push_back(std::to_string(character_task_timers_entry.task_id)); + insert_values.push_back(std::to_string(character_task_timers_entry.timer_type)); + insert_values.push_back("FROM_UNIXTIME(" + std::to_string(character_task_timers_entry.expire_time) + ")"); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseInsert(), + implode(",", insert_values) + ) + ); + + if (results.Success()) { + character_task_timers_entry.id = results.LastInsertedID(); + return character_task_timers_entry; + } + + character_task_timers_entry = NewEntity(); + + return character_task_timers_entry; + } + + static int InsertMany( + Database& db, + std::vector character_task_timers_entries + ) + { + std::vector insert_chunks; + + for (auto &character_task_timers_entry: character_task_timers_entries) { + std::vector insert_values; + + insert_values.push_back(std::to_string(character_task_timers_entry.id)); + insert_values.push_back(std::to_string(character_task_timers_entry.character_id)); + insert_values.push_back(std::to_string(character_task_timers_entry.task_id)); + insert_values.push_back(std::to_string(character_task_timers_entry.timer_type)); + insert_values.push_back("FROM_UNIXTIME(" + std::to_string(character_task_timers_entry.expire_time) + ")"); + + insert_chunks.push_back("(" + implode(",", insert_values) + ")"); + } + + std::vector insert_values; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseInsert(), + implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static std::vector All(Database& db) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{}", + BaseSelect() + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + CharacterTaskTimers entry{}; + + entry.id = atoi(row[0]); + entry.character_id = atoi(row[1]); + entry.task_id = atoi(row[2]); + entry.timer_type = atoi(row[3]); + entry.expire_time = strtoll(row[4], nullptr, 10); + + all_entries.push_back(entry); + } + + return all_entries; + } + + static std::vector GetWhere(Database& db, std::string where_filter) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {}", + BaseSelect(), + where_filter + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + CharacterTaskTimers entry{}; + + entry.id = atoi(row[0]); + entry.character_id = atoi(row[1]); + entry.task_id = atoi(row[2]); + entry.timer_type = atoi(row[3]); + entry.expire_time = strtoll(row[4], nullptr, 10); + + all_entries.push_back(entry); + } + + return all_entries; + } + + static int DeleteWhere(Database& db, std::string where_filter) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {}", + TableName(), + where_filter + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int Truncate(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "TRUNCATE TABLE {}", + TableName() + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + +}; + +#endif //EQEMU_BASE_CHARACTER_TASK_TIMERS_REPOSITORY_H diff --git a/common/repositories/base/base_completed_shared_task_activity_state_repository.h b/common/repositories/base/base_completed_shared_task_activity_state_repository.h new file mode 100644 index 000000000..83244b31d --- /dev/null +++ b/common/repositories/base/base_completed_shared_task_activity_state_repository.h @@ -0,0 +1,337 @@ +/** + * DO NOT MODIFY THIS FILE + * + * This repository was automatically generated and is NOT to be modified directly. + * Any repository modifications are meant to be made to the repository extending the base. + * Any modifications to base repositories are to be made by the generator only + * + * @generator ./utils/scripts/generators/repository-generator.pl + * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories + */ + +#ifndef EQEMU_BASE_COMPLETED_SHARED_TASK_ACTIVITY_STATE_REPOSITORY_H +#define EQEMU_BASE_COMPLETED_SHARED_TASK_ACTIVITY_STATE_REPOSITORY_H + +#include "../../database.h" +#include "../../string_util.h" +#include + +class BaseCompletedSharedTaskActivityStateRepository { +public: + struct CompletedSharedTaskActivityState { + int64 shared_task_id; + int activity_id; + int done_count; + time_t updated_time; + time_t completed_time; + }; + + static std::string PrimaryKey() + { + return std::string("shared_task_id"); + } + + static std::vector Columns() + { + return { + "shared_task_id", + "activity_id", + "done_count", + "updated_time", + "completed_time", + }; + } + + static std::vector SelectColumns() + { + return { + "shared_task_id", + "activity_id", + "done_count", + "UNIX_TIMESTAMP(updated_time)", + "UNIX_TIMESTAMP(completed_time)", + }; + } + + static std::string ColumnsRaw() + { + return std::string(implode(", ", Columns())); + } + + static std::string SelectColumnsRaw() + { + return std::string(implode(", ", SelectColumns())); + } + + static std::string TableName() + { + return std::string("completed_shared_task_activity_state"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + SelectColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static CompletedSharedTaskActivityState NewEntity() + { + CompletedSharedTaskActivityState entry{}; + + entry.shared_task_id = 0; + entry.activity_id = 0; + entry.done_count = 0; + entry.updated_time = 0; + entry.completed_time = 0; + + return entry; + } + + static CompletedSharedTaskActivityState GetCompletedSharedTaskActivityStateEntry( + const std::vector &completed_shared_task_activity_states, + int completed_shared_task_activity_state_id + ) + { + for (auto &completed_shared_task_activity_state : completed_shared_task_activity_states) { + if (completed_shared_task_activity_state.shared_task_id == completed_shared_task_activity_state_id) { + return completed_shared_task_activity_state; + } + } + + return NewEntity(); + } + + static CompletedSharedTaskActivityState FindOne( + Database& db, + int completed_shared_task_activity_state_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE id = {} LIMIT 1", + BaseSelect(), + completed_shared_task_activity_state_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + CompletedSharedTaskActivityState entry{}; + + entry.shared_task_id = strtoll(row[0], nullptr, 10); + entry.activity_id = atoi(row[1]); + entry.done_count = atoi(row[2]); + entry.updated_time = strtoll(row[3] ? row[3] : "-1", nullptr, 10); + entry.completed_time = strtoll(row[4] ? row[4] : "-1", nullptr, 10); + + return entry; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int completed_shared_task_activity_state_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + completed_shared_task_activity_state_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + CompletedSharedTaskActivityState completed_shared_task_activity_state_entry + ) + { + std::vector update_values; + + auto columns = Columns(); + + update_values.push_back(columns[0] + " = " + std::to_string(completed_shared_task_activity_state_entry.shared_task_id)); + update_values.push_back(columns[1] + " = " + std::to_string(completed_shared_task_activity_state_entry.activity_id)); + update_values.push_back(columns[2] + " = " + std::to_string(completed_shared_task_activity_state_entry.done_count)); + update_values.push_back(columns[3] + " = FROM_UNIXTIME(" + (completed_shared_task_activity_state_entry.updated_time > 0 ? std::to_string(completed_shared_task_activity_state_entry.updated_time) : "null") + ")"); + update_values.push_back(columns[4] + " = FROM_UNIXTIME(" + (completed_shared_task_activity_state_entry.completed_time > 0 ? std::to_string(completed_shared_task_activity_state_entry.completed_time) : "null") + ")"); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + implode(", ", update_values), + PrimaryKey(), + completed_shared_task_activity_state_entry.shared_task_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static CompletedSharedTaskActivityState InsertOne( + Database& db, + CompletedSharedTaskActivityState completed_shared_task_activity_state_entry + ) + { + std::vector insert_values; + + insert_values.push_back(std::to_string(completed_shared_task_activity_state_entry.shared_task_id)); + insert_values.push_back(std::to_string(completed_shared_task_activity_state_entry.activity_id)); + insert_values.push_back(std::to_string(completed_shared_task_activity_state_entry.done_count)); + insert_values.push_back("FROM_UNIXTIME(" + (completed_shared_task_activity_state_entry.updated_time > 0 ? std::to_string(completed_shared_task_activity_state_entry.updated_time) : "null") + ")"); + insert_values.push_back("FROM_UNIXTIME(" + (completed_shared_task_activity_state_entry.completed_time > 0 ? std::to_string(completed_shared_task_activity_state_entry.completed_time) : "null") + ")"); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseInsert(), + implode(",", insert_values) + ) + ); + + if (results.Success()) { + completed_shared_task_activity_state_entry.shared_task_id = results.LastInsertedID(); + return completed_shared_task_activity_state_entry; + } + + completed_shared_task_activity_state_entry = NewEntity(); + + return completed_shared_task_activity_state_entry; + } + + static int InsertMany( + Database& db, + std::vector completed_shared_task_activity_state_entries + ) + { + std::vector insert_chunks; + + for (auto &completed_shared_task_activity_state_entry: completed_shared_task_activity_state_entries) { + std::vector insert_values; + + insert_values.push_back(std::to_string(completed_shared_task_activity_state_entry.shared_task_id)); + insert_values.push_back(std::to_string(completed_shared_task_activity_state_entry.activity_id)); + insert_values.push_back(std::to_string(completed_shared_task_activity_state_entry.done_count)); + insert_values.push_back("FROM_UNIXTIME(" + (completed_shared_task_activity_state_entry.updated_time > 0 ? std::to_string(completed_shared_task_activity_state_entry.updated_time) : "null") + ")"); + insert_values.push_back("FROM_UNIXTIME(" + (completed_shared_task_activity_state_entry.completed_time > 0 ? std::to_string(completed_shared_task_activity_state_entry.completed_time) : "null") + ")"); + + insert_chunks.push_back("(" + implode(",", insert_values) + ")"); + } + + std::vector insert_values; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseInsert(), + implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static std::vector All(Database& db) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{}", + BaseSelect() + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + CompletedSharedTaskActivityState entry{}; + + entry.shared_task_id = strtoll(row[0], nullptr, 10); + entry.activity_id = atoi(row[1]); + entry.done_count = atoi(row[2]); + entry.updated_time = strtoll(row[3] ? row[3] : "-1", nullptr, 10); + entry.completed_time = strtoll(row[4] ? row[4] : "-1", nullptr, 10); + + all_entries.push_back(entry); + } + + return all_entries; + } + + static std::vector GetWhere(Database& db, std::string where_filter) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {}", + BaseSelect(), + where_filter + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + CompletedSharedTaskActivityState entry{}; + + entry.shared_task_id = strtoll(row[0], nullptr, 10); + entry.activity_id = atoi(row[1]); + entry.done_count = atoi(row[2]); + entry.updated_time = strtoll(row[3] ? row[3] : "-1", nullptr, 10); + entry.completed_time = strtoll(row[4] ? row[4] : "-1", nullptr, 10); + + all_entries.push_back(entry); + } + + return all_entries; + } + + static int DeleteWhere(Database& db, std::string where_filter) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {}", + TableName(), + where_filter + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int Truncate(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "TRUNCATE TABLE {}", + TableName() + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + +}; + +#endif //EQEMU_BASE_COMPLETED_SHARED_TASK_ACTIVITY_STATE_REPOSITORY_H diff --git a/common/repositories/base/base_completed_shared_task_members_repository.h b/common/repositories/base/base_completed_shared_task_members_repository.h new file mode 100644 index 000000000..05f72ce3a --- /dev/null +++ b/common/repositories/base/base_completed_shared_task_members_repository.h @@ -0,0 +1,317 @@ +/** + * DO NOT MODIFY THIS FILE + * + * This repository was automatically generated and is NOT to be modified directly. + * Any repository modifications are meant to be made to the repository extending the base. + * Any modifications to base repositories are to be made by the generator only + * + * @generator ./utils/scripts/generators/repository-generator.pl + * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories + */ + +#ifndef EQEMU_BASE_COMPLETED_SHARED_TASK_MEMBERS_REPOSITORY_H +#define EQEMU_BASE_COMPLETED_SHARED_TASK_MEMBERS_REPOSITORY_H + +#include "../../database.h" +#include "../../string_util.h" +#include + +class BaseCompletedSharedTaskMembersRepository { +public: + struct CompletedSharedTaskMembers { + int64 shared_task_id; + int64 character_id; + int is_leader; + }; + + static std::string PrimaryKey() + { + return std::string("shared_task_id"); + } + + static std::vector Columns() + { + return { + "shared_task_id", + "character_id", + "is_leader", + }; + } + + static std::vector SelectColumns() + { + return { + "shared_task_id", + "character_id", + "is_leader", + }; + } + + static std::string ColumnsRaw() + { + return std::string(implode(", ", Columns())); + } + + static std::string SelectColumnsRaw() + { + return std::string(implode(", ", SelectColumns())); + } + + static std::string TableName() + { + return std::string("completed_shared_task_members"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + SelectColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static CompletedSharedTaskMembers NewEntity() + { + CompletedSharedTaskMembers entry{}; + + entry.shared_task_id = 0; + entry.character_id = 0; + entry.is_leader = 0; + + return entry; + } + + static CompletedSharedTaskMembers GetCompletedSharedTaskMembersEntry( + const std::vector &completed_shared_task_memberss, + int completed_shared_task_members_id + ) + { + for (auto &completed_shared_task_members : completed_shared_task_memberss) { + if (completed_shared_task_members.shared_task_id == completed_shared_task_members_id) { + return completed_shared_task_members; + } + } + + return NewEntity(); + } + + static CompletedSharedTaskMembers FindOne( + Database& db, + int completed_shared_task_members_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE id = {} LIMIT 1", + BaseSelect(), + completed_shared_task_members_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + CompletedSharedTaskMembers entry{}; + + entry.shared_task_id = strtoll(row[0], nullptr, 10); + entry.character_id = strtoll(row[1], nullptr, 10); + entry.is_leader = atoi(row[2]); + + return entry; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int completed_shared_task_members_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + completed_shared_task_members_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + CompletedSharedTaskMembers completed_shared_task_members_entry + ) + { + std::vector update_values; + + auto columns = Columns(); + + update_values.push_back(columns[0] + " = " + std::to_string(completed_shared_task_members_entry.shared_task_id)); + update_values.push_back(columns[1] + " = " + std::to_string(completed_shared_task_members_entry.character_id)); + update_values.push_back(columns[2] + " = " + std::to_string(completed_shared_task_members_entry.is_leader)); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + implode(", ", update_values), + PrimaryKey(), + completed_shared_task_members_entry.shared_task_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static CompletedSharedTaskMembers InsertOne( + Database& db, + CompletedSharedTaskMembers completed_shared_task_members_entry + ) + { + std::vector insert_values; + + insert_values.push_back(std::to_string(completed_shared_task_members_entry.shared_task_id)); + insert_values.push_back(std::to_string(completed_shared_task_members_entry.character_id)); + insert_values.push_back(std::to_string(completed_shared_task_members_entry.is_leader)); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseInsert(), + implode(",", insert_values) + ) + ); + + if (results.Success()) { + completed_shared_task_members_entry.shared_task_id = results.LastInsertedID(); + return completed_shared_task_members_entry; + } + + completed_shared_task_members_entry = NewEntity(); + + return completed_shared_task_members_entry; + } + + static int InsertMany( + Database& db, + std::vector completed_shared_task_members_entries + ) + { + std::vector insert_chunks; + + for (auto &completed_shared_task_members_entry: completed_shared_task_members_entries) { + std::vector insert_values; + + insert_values.push_back(std::to_string(completed_shared_task_members_entry.shared_task_id)); + insert_values.push_back(std::to_string(completed_shared_task_members_entry.character_id)); + insert_values.push_back(std::to_string(completed_shared_task_members_entry.is_leader)); + + insert_chunks.push_back("(" + implode(",", insert_values) + ")"); + } + + std::vector insert_values; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseInsert(), + implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static std::vector All(Database& db) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{}", + BaseSelect() + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + CompletedSharedTaskMembers entry{}; + + entry.shared_task_id = strtoll(row[0], nullptr, 10); + entry.character_id = strtoll(row[1], nullptr, 10); + entry.is_leader = atoi(row[2]); + + all_entries.push_back(entry); + } + + return all_entries; + } + + static std::vector GetWhere(Database& db, std::string where_filter) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {}", + BaseSelect(), + where_filter + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + CompletedSharedTaskMembers entry{}; + + entry.shared_task_id = strtoll(row[0], nullptr, 10); + entry.character_id = strtoll(row[1], nullptr, 10); + entry.is_leader = atoi(row[2]); + + all_entries.push_back(entry); + } + + return all_entries; + } + + static int DeleteWhere(Database& db, std::string where_filter) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {}", + TableName(), + where_filter + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int Truncate(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "TRUNCATE TABLE {}", + TableName() + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + +}; + +#endif //EQEMU_BASE_COMPLETED_SHARED_TASK_MEMBERS_REPOSITORY_H diff --git a/common/repositories/base/base_completed_shared_tasks_repository.h b/common/repositories/base/base_completed_shared_tasks_repository.h new file mode 100644 index 000000000..9c1e3a87d --- /dev/null +++ b/common/repositories/base/base_completed_shared_tasks_repository.h @@ -0,0 +1,347 @@ +/** + * DO NOT MODIFY THIS FILE + * + * This repository was automatically generated and is NOT to be modified directly. + * Any repository modifications are meant to be made to the repository extending the base. + * Any modifications to base repositories are to be made by the generator only + * + * @generator ./utils/scripts/generators/repository-generator.pl + * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories + */ + +#ifndef EQEMU_BASE_COMPLETED_SHARED_TASKS_REPOSITORY_H +#define EQEMU_BASE_COMPLETED_SHARED_TASKS_REPOSITORY_H + +#include "../../database.h" +#include "../../string_util.h" +#include + +class BaseCompletedSharedTasksRepository { +public: + struct CompletedSharedTasks { + int64 id; + int task_id; + time_t accepted_time; + time_t expire_time; + time_t completion_time; + int is_locked; + }; + + static std::string PrimaryKey() + { + return std::string("id"); + } + + static std::vector Columns() + { + return { + "id", + "task_id", + "accepted_time", + "expire_time", + "completion_time", + "is_locked", + }; + } + + static std::vector SelectColumns() + { + return { + "id", + "task_id", + "UNIX_TIMESTAMP(accepted_time)", + "UNIX_TIMESTAMP(expire_time)", + "UNIX_TIMESTAMP(completion_time)", + "is_locked", + }; + } + + static std::string ColumnsRaw() + { + return std::string(implode(", ", Columns())); + } + + static std::string SelectColumnsRaw() + { + return std::string(implode(", ", SelectColumns())); + } + + static std::string TableName() + { + return std::string("completed_shared_tasks"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + SelectColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static CompletedSharedTasks NewEntity() + { + CompletedSharedTasks entry{}; + + entry.id = 0; + entry.task_id = 0; + entry.accepted_time = 0; + entry.expire_time = 0; + entry.completion_time = 0; + entry.is_locked = 0; + + return entry; + } + + static CompletedSharedTasks GetCompletedSharedTasksEntry( + const std::vector &completed_shared_taskss, + int completed_shared_tasks_id + ) + { + for (auto &completed_shared_tasks : completed_shared_taskss) { + if (completed_shared_tasks.id == completed_shared_tasks_id) { + return completed_shared_tasks; + } + } + + return NewEntity(); + } + + static CompletedSharedTasks FindOne( + Database& db, + int completed_shared_tasks_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE id = {} LIMIT 1", + BaseSelect(), + completed_shared_tasks_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + CompletedSharedTasks entry{}; + + entry.id = strtoll(row[0], nullptr, 10); + entry.task_id = atoi(row[1]); + entry.accepted_time = strtoll(row[2] ? row[2] : "-1", nullptr, 10); + entry.expire_time = strtoll(row[3] ? row[3] : "-1", nullptr, 10); + entry.completion_time = strtoll(row[4] ? row[4] : "-1", nullptr, 10); + entry.is_locked = atoi(row[5]); + + return entry; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int completed_shared_tasks_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + completed_shared_tasks_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + CompletedSharedTasks completed_shared_tasks_entry + ) + { + std::vector update_values; + + auto columns = Columns(); + + update_values.push_back(columns[0] + " = " + std::to_string(completed_shared_tasks_entry.id)); + update_values.push_back(columns[1] + " = " + std::to_string(completed_shared_tasks_entry.task_id)); + update_values.push_back(columns[2] + " = FROM_UNIXTIME(" + (completed_shared_tasks_entry.accepted_time > 0 ? std::to_string(completed_shared_tasks_entry.accepted_time) : "null") + ")"); + update_values.push_back(columns[3] + " = FROM_UNIXTIME(" + (completed_shared_tasks_entry.expire_time > 0 ? std::to_string(completed_shared_tasks_entry.expire_time) : "null") + ")"); + update_values.push_back(columns[4] + " = FROM_UNIXTIME(" + (completed_shared_tasks_entry.completion_time > 0 ? std::to_string(completed_shared_tasks_entry.completion_time) : "null") + ")"); + update_values.push_back(columns[5] + " = " + std::to_string(completed_shared_tasks_entry.is_locked)); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + implode(", ", update_values), + PrimaryKey(), + completed_shared_tasks_entry.id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static CompletedSharedTasks InsertOne( + Database& db, + CompletedSharedTasks completed_shared_tasks_entry + ) + { + std::vector insert_values; + + insert_values.push_back(std::to_string(completed_shared_tasks_entry.id)); + insert_values.push_back(std::to_string(completed_shared_tasks_entry.task_id)); + insert_values.push_back("FROM_UNIXTIME(" + (completed_shared_tasks_entry.accepted_time > 0 ? std::to_string(completed_shared_tasks_entry.accepted_time) : "null") + ")"); + insert_values.push_back("FROM_UNIXTIME(" + (completed_shared_tasks_entry.expire_time > 0 ? std::to_string(completed_shared_tasks_entry.expire_time) : "null") + ")"); + insert_values.push_back("FROM_UNIXTIME(" + (completed_shared_tasks_entry.completion_time > 0 ? std::to_string(completed_shared_tasks_entry.completion_time) : "null") + ")"); + insert_values.push_back(std::to_string(completed_shared_tasks_entry.is_locked)); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseInsert(), + implode(",", insert_values) + ) + ); + + if (results.Success()) { + completed_shared_tasks_entry.id = results.LastInsertedID(); + return completed_shared_tasks_entry; + } + + completed_shared_tasks_entry = NewEntity(); + + return completed_shared_tasks_entry; + } + + static int InsertMany( + Database& db, + std::vector completed_shared_tasks_entries + ) + { + std::vector insert_chunks; + + for (auto &completed_shared_tasks_entry: completed_shared_tasks_entries) { + std::vector insert_values; + + insert_values.push_back(std::to_string(completed_shared_tasks_entry.id)); + insert_values.push_back(std::to_string(completed_shared_tasks_entry.task_id)); + insert_values.push_back("FROM_UNIXTIME(" + (completed_shared_tasks_entry.accepted_time > 0 ? std::to_string(completed_shared_tasks_entry.accepted_time) : "null") + ")"); + insert_values.push_back("FROM_UNIXTIME(" + (completed_shared_tasks_entry.expire_time > 0 ? std::to_string(completed_shared_tasks_entry.expire_time) : "null") + ")"); + insert_values.push_back("FROM_UNIXTIME(" + (completed_shared_tasks_entry.completion_time > 0 ? std::to_string(completed_shared_tasks_entry.completion_time) : "null") + ")"); + insert_values.push_back(std::to_string(completed_shared_tasks_entry.is_locked)); + + insert_chunks.push_back("(" + implode(",", insert_values) + ")"); + } + + std::vector insert_values; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseInsert(), + implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static std::vector All(Database& db) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{}", + BaseSelect() + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + CompletedSharedTasks entry{}; + + entry.id = strtoll(row[0], nullptr, 10); + entry.task_id = atoi(row[1]); + entry.accepted_time = strtoll(row[2] ? row[2] : "-1", nullptr, 10); + entry.expire_time = strtoll(row[3] ? row[3] : "-1", nullptr, 10); + entry.completion_time = strtoll(row[4] ? row[4] : "-1", nullptr, 10); + entry.is_locked = atoi(row[5]); + + all_entries.push_back(entry); + } + + return all_entries; + } + + static std::vector GetWhere(Database& db, std::string where_filter) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {}", + BaseSelect(), + where_filter + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + CompletedSharedTasks entry{}; + + entry.id = strtoll(row[0], nullptr, 10); + entry.task_id = atoi(row[1]); + entry.accepted_time = strtoll(row[2] ? row[2] : "-1", nullptr, 10); + entry.expire_time = strtoll(row[3] ? row[3] : "-1", nullptr, 10); + entry.completion_time = strtoll(row[4] ? row[4] : "-1", nullptr, 10); + entry.is_locked = atoi(row[5]); + + all_entries.push_back(entry); + } + + return all_entries; + } + + static int DeleteWhere(Database& db, std::string where_filter) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {}", + TableName(), + where_filter + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int Truncate(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "TRUNCATE TABLE {}", + TableName() + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + +}; + +#endif //EQEMU_BASE_COMPLETED_SHARED_TASKS_REPOSITORY_H diff --git a/common/repositories/base/base_dynamic_zone_members_repository.h b/common/repositories/base/base_dynamic_zone_members_repository.h index 07fee6723..b0f41ca08 100644 --- a/common/repositories/base/base_dynamic_zone_members_repository.h +++ b/common/repositories/base/base_dynamic_zone_members_repository.h @@ -21,7 +21,6 @@ public: int id; int dynamic_zone_id; int character_id; - int is_current_member; }; static std::string PrimaryKey() @@ -35,7 +34,6 @@ public: "id", "dynamic_zone_id", "character_id", - "is_current_member", }; } @@ -71,10 +69,9 @@ public: { DynamicZoneMembers entry{}; - entry.id = 0; - entry.dynamic_zone_id = 0; - entry.character_id = 0; - entry.is_current_member = 1; + entry.id = 0; + entry.dynamic_zone_id = 0; + entry.character_id = 0; return entry; } @@ -110,10 +107,9 @@ public: if (results.RowCount() == 1) { DynamicZoneMembers entry{}; - entry.id = atoi(row[0]); - entry.dynamic_zone_id = atoi(row[1]); - entry.character_id = atoi(row[2]); - entry.is_current_member = atoi(row[3]); + entry.id = atoi(row[0]); + entry.dynamic_zone_id = atoi(row[1]); + entry.character_id = atoi(row[2]); return entry; } @@ -149,7 +145,6 @@ public: update_values.push_back(columns[1] + " = " + std::to_string(dynamic_zone_members_entry.dynamic_zone_id)); update_values.push_back(columns[2] + " = " + std::to_string(dynamic_zone_members_entry.character_id)); - update_values.push_back(columns[3] + " = " + std::to_string(dynamic_zone_members_entry.is_current_member)); auto results = db.QueryDatabase( fmt::format( @@ -174,7 +169,6 @@ public: insert_values.push_back(std::to_string(dynamic_zone_members_entry.id)); insert_values.push_back(std::to_string(dynamic_zone_members_entry.dynamic_zone_id)); insert_values.push_back(std::to_string(dynamic_zone_members_entry.character_id)); - insert_values.push_back(std::to_string(dynamic_zone_members_entry.is_current_member)); auto results = db.QueryDatabase( fmt::format( @@ -207,7 +201,6 @@ public: insert_values.push_back(std::to_string(dynamic_zone_members_entry.id)); insert_values.push_back(std::to_string(dynamic_zone_members_entry.dynamic_zone_id)); insert_values.push_back(std::to_string(dynamic_zone_members_entry.character_id)); - insert_values.push_back(std::to_string(dynamic_zone_members_entry.is_current_member)); insert_chunks.push_back("(" + implode(",", insert_values) + ")"); } @@ -241,10 +234,9 @@ public: for (auto row = results.begin(); row != results.end(); ++row) { DynamicZoneMembers entry{}; - entry.id = atoi(row[0]); - entry.dynamic_zone_id = atoi(row[1]); - entry.character_id = atoi(row[2]); - entry.is_current_member = atoi(row[3]); + entry.id = atoi(row[0]); + entry.dynamic_zone_id = atoi(row[1]); + entry.character_id = atoi(row[2]); all_entries.push_back(entry); } @@ -269,10 +261,9 @@ public: for (auto row = results.begin(); row != results.end(); ++row) { DynamicZoneMembers entry{}; - entry.id = atoi(row[0]); - entry.dynamic_zone_id = atoi(row[1]); - entry.character_id = atoi(row[2]); - entry.is_current_member = atoi(row[3]); + entry.id = atoi(row[0]); + entry.dynamic_zone_id = atoi(row[1]); + entry.character_id = atoi(row[2]); all_entries.push_back(entry); } diff --git a/common/repositories/base/base_dynamic_zones_repository.h b/common/repositories/base/base_dynamic_zones_repository.h index 283ecd754..a6f43bb73 100644 --- a/common/repositories/base/base_dynamic_zones_repository.h +++ b/common/repositories/base/base_dynamic_zones_repository.h @@ -18,23 +18,28 @@ class BaseDynamicZonesRepository { public: struct DynamicZones { - int id; - int instance_id; - int type; - int compass_zone_id; - float compass_x; - float compass_y; - float compass_z; - int safe_return_zone_id; - float safe_return_x; - float safe_return_y; - float safe_return_z; - float safe_return_heading; - float zone_in_x; - float zone_in_y; - float zone_in_z; - float zone_in_heading; - int has_zone_in; + int id; + int instance_id; + int type; + std::string uuid; + std::string name; + int leader_id; + int min_players; + int max_players; + int compass_zone_id; + float compass_x; + float compass_y; + float compass_z; + int safe_return_zone_id; + float safe_return_x; + float safe_return_y; + float safe_return_z; + float safe_return_heading; + float zone_in_x; + float zone_in_y; + float zone_in_z; + float zone_in_heading; + int has_zone_in; }; static std::string PrimaryKey() @@ -48,6 +53,11 @@ public: "id", "instance_id", "type", + "uuid", + "name", + "leader_id", + "min_players", + "max_players", "compass_zone_id", "compass_x", "compass_y", @@ -100,6 +110,11 @@ public: entry.id = 0; entry.instance_id = 0; entry.type = 0; + entry.uuid = ""; + entry.name = ""; + entry.leader_id = 0; + entry.min_players = 0; + entry.max_players = 0; entry.compass_zone_id = 0; entry.compass_x = 0; entry.compass_y = 0; @@ -152,20 +167,25 @@ public: entry.id = atoi(row[0]); entry.instance_id = atoi(row[1]); entry.type = atoi(row[2]); - entry.compass_zone_id = atoi(row[3]); - entry.compass_x = static_cast(atof(row[4])); - entry.compass_y = static_cast(atof(row[5])); - entry.compass_z = static_cast(atof(row[6])); - entry.safe_return_zone_id = atoi(row[7]); - entry.safe_return_x = static_cast(atof(row[8])); - entry.safe_return_y = static_cast(atof(row[9])); - entry.safe_return_z = static_cast(atof(row[10])); - entry.safe_return_heading = static_cast(atof(row[11])); - entry.zone_in_x = static_cast(atof(row[12])); - entry.zone_in_y = static_cast(atof(row[13])); - entry.zone_in_z = static_cast(atof(row[14])); - entry.zone_in_heading = static_cast(atof(row[15])); - entry.has_zone_in = atoi(row[16]); + entry.uuid = row[3] ? row[3] : ""; + entry.name = row[4] ? row[4] : ""; + entry.leader_id = atoi(row[5]); + entry.min_players = atoi(row[6]); + entry.max_players = atoi(row[7]); + entry.compass_zone_id = atoi(row[8]); + entry.compass_x = static_cast(atof(row[9])); + entry.compass_y = static_cast(atof(row[10])); + entry.compass_z = static_cast(atof(row[11])); + entry.safe_return_zone_id = atoi(row[12]); + entry.safe_return_x = static_cast(atof(row[13])); + entry.safe_return_y = static_cast(atof(row[14])); + entry.safe_return_z = static_cast(atof(row[15])); + entry.safe_return_heading = static_cast(atof(row[16])); + entry.zone_in_x = static_cast(atof(row[17])); + entry.zone_in_y = static_cast(atof(row[18])); + entry.zone_in_z = static_cast(atof(row[19])); + entry.zone_in_heading = static_cast(atof(row[20])); + entry.has_zone_in = atoi(row[21]); return entry; } @@ -201,20 +221,25 @@ public: update_values.push_back(columns[1] + " = " + std::to_string(dynamic_zones_entry.instance_id)); update_values.push_back(columns[2] + " = " + std::to_string(dynamic_zones_entry.type)); - update_values.push_back(columns[3] + " = " + std::to_string(dynamic_zones_entry.compass_zone_id)); - update_values.push_back(columns[4] + " = " + std::to_string(dynamic_zones_entry.compass_x)); - update_values.push_back(columns[5] + " = " + std::to_string(dynamic_zones_entry.compass_y)); - update_values.push_back(columns[6] + " = " + std::to_string(dynamic_zones_entry.compass_z)); - update_values.push_back(columns[7] + " = " + std::to_string(dynamic_zones_entry.safe_return_zone_id)); - update_values.push_back(columns[8] + " = " + std::to_string(dynamic_zones_entry.safe_return_x)); - update_values.push_back(columns[9] + " = " + std::to_string(dynamic_zones_entry.safe_return_y)); - update_values.push_back(columns[10] + " = " + std::to_string(dynamic_zones_entry.safe_return_z)); - update_values.push_back(columns[11] + " = " + std::to_string(dynamic_zones_entry.safe_return_heading)); - update_values.push_back(columns[12] + " = " + std::to_string(dynamic_zones_entry.zone_in_x)); - update_values.push_back(columns[13] + " = " + std::to_string(dynamic_zones_entry.zone_in_y)); - update_values.push_back(columns[14] + " = " + std::to_string(dynamic_zones_entry.zone_in_z)); - update_values.push_back(columns[15] + " = " + std::to_string(dynamic_zones_entry.zone_in_heading)); - update_values.push_back(columns[16] + " = " + std::to_string(dynamic_zones_entry.has_zone_in)); + update_values.push_back(columns[3] + " = '" + EscapeString(dynamic_zones_entry.uuid) + "'"); + update_values.push_back(columns[4] + " = '" + EscapeString(dynamic_zones_entry.name) + "'"); + update_values.push_back(columns[5] + " = " + std::to_string(dynamic_zones_entry.leader_id)); + update_values.push_back(columns[6] + " = " + std::to_string(dynamic_zones_entry.min_players)); + update_values.push_back(columns[7] + " = " + std::to_string(dynamic_zones_entry.max_players)); + update_values.push_back(columns[8] + " = " + std::to_string(dynamic_zones_entry.compass_zone_id)); + update_values.push_back(columns[9] + " = " + std::to_string(dynamic_zones_entry.compass_x)); + update_values.push_back(columns[10] + " = " + std::to_string(dynamic_zones_entry.compass_y)); + update_values.push_back(columns[11] + " = " + std::to_string(dynamic_zones_entry.compass_z)); + update_values.push_back(columns[12] + " = " + std::to_string(dynamic_zones_entry.safe_return_zone_id)); + update_values.push_back(columns[13] + " = " + std::to_string(dynamic_zones_entry.safe_return_x)); + update_values.push_back(columns[14] + " = " + std::to_string(dynamic_zones_entry.safe_return_y)); + update_values.push_back(columns[15] + " = " + std::to_string(dynamic_zones_entry.safe_return_z)); + update_values.push_back(columns[16] + " = " + std::to_string(dynamic_zones_entry.safe_return_heading)); + update_values.push_back(columns[17] + " = " + std::to_string(dynamic_zones_entry.zone_in_x)); + update_values.push_back(columns[18] + " = " + std::to_string(dynamic_zones_entry.zone_in_y)); + update_values.push_back(columns[19] + " = " + std::to_string(dynamic_zones_entry.zone_in_z)); + update_values.push_back(columns[20] + " = " + std::to_string(dynamic_zones_entry.zone_in_heading)); + update_values.push_back(columns[21] + " = " + std::to_string(dynamic_zones_entry.has_zone_in)); auto results = db.QueryDatabase( fmt::format( @@ -239,6 +264,11 @@ public: insert_values.push_back(std::to_string(dynamic_zones_entry.id)); insert_values.push_back(std::to_string(dynamic_zones_entry.instance_id)); insert_values.push_back(std::to_string(dynamic_zones_entry.type)); + insert_values.push_back("'" + EscapeString(dynamic_zones_entry.uuid) + "'"); + insert_values.push_back("'" + EscapeString(dynamic_zones_entry.name) + "'"); + insert_values.push_back(std::to_string(dynamic_zones_entry.leader_id)); + insert_values.push_back(std::to_string(dynamic_zones_entry.min_players)); + insert_values.push_back(std::to_string(dynamic_zones_entry.max_players)); insert_values.push_back(std::to_string(dynamic_zones_entry.compass_zone_id)); insert_values.push_back(std::to_string(dynamic_zones_entry.compass_x)); insert_values.push_back(std::to_string(dynamic_zones_entry.compass_y)); @@ -285,6 +315,11 @@ public: insert_values.push_back(std::to_string(dynamic_zones_entry.id)); insert_values.push_back(std::to_string(dynamic_zones_entry.instance_id)); insert_values.push_back(std::to_string(dynamic_zones_entry.type)); + insert_values.push_back("'" + EscapeString(dynamic_zones_entry.uuid) + "'"); + insert_values.push_back("'" + EscapeString(dynamic_zones_entry.name) + "'"); + insert_values.push_back(std::to_string(dynamic_zones_entry.leader_id)); + insert_values.push_back(std::to_string(dynamic_zones_entry.min_players)); + insert_values.push_back(std::to_string(dynamic_zones_entry.max_players)); insert_values.push_back(std::to_string(dynamic_zones_entry.compass_zone_id)); insert_values.push_back(std::to_string(dynamic_zones_entry.compass_x)); insert_values.push_back(std::to_string(dynamic_zones_entry.compass_y)); @@ -335,20 +370,25 @@ public: entry.id = atoi(row[0]); entry.instance_id = atoi(row[1]); entry.type = atoi(row[2]); - entry.compass_zone_id = atoi(row[3]); - entry.compass_x = static_cast(atof(row[4])); - entry.compass_y = static_cast(atof(row[5])); - entry.compass_z = static_cast(atof(row[6])); - entry.safe_return_zone_id = atoi(row[7]); - entry.safe_return_x = static_cast(atof(row[8])); - entry.safe_return_y = static_cast(atof(row[9])); - entry.safe_return_z = static_cast(atof(row[10])); - entry.safe_return_heading = static_cast(atof(row[11])); - entry.zone_in_x = static_cast(atof(row[12])); - entry.zone_in_y = static_cast(atof(row[13])); - entry.zone_in_z = static_cast(atof(row[14])); - entry.zone_in_heading = static_cast(atof(row[15])); - entry.has_zone_in = atoi(row[16]); + entry.uuid = row[3] ? row[3] : ""; + entry.name = row[4] ? row[4] : ""; + entry.leader_id = atoi(row[5]); + entry.min_players = atoi(row[6]); + entry.max_players = atoi(row[7]); + entry.compass_zone_id = atoi(row[8]); + entry.compass_x = static_cast(atof(row[9])); + entry.compass_y = static_cast(atof(row[10])); + entry.compass_z = static_cast(atof(row[11])); + entry.safe_return_zone_id = atoi(row[12]); + entry.safe_return_x = static_cast(atof(row[13])); + entry.safe_return_y = static_cast(atof(row[14])); + entry.safe_return_z = static_cast(atof(row[15])); + entry.safe_return_heading = static_cast(atof(row[16])); + entry.zone_in_x = static_cast(atof(row[17])); + entry.zone_in_y = static_cast(atof(row[18])); + entry.zone_in_z = static_cast(atof(row[19])); + entry.zone_in_heading = static_cast(atof(row[20])); + entry.has_zone_in = atoi(row[21]); all_entries.push_back(entry); } @@ -376,20 +416,25 @@ public: entry.id = atoi(row[0]); entry.instance_id = atoi(row[1]); entry.type = atoi(row[2]); - entry.compass_zone_id = atoi(row[3]); - entry.compass_x = static_cast(atof(row[4])); - entry.compass_y = static_cast(atof(row[5])); - entry.compass_z = static_cast(atof(row[6])); - entry.safe_return_zone_id = atoi(row[7]); - entry.safe_return_x = static_cast(atof(row[8])); - entry.safe_return_y = static_cast(atof(row[9])); - entry.safe_return_z = static_cast(atof(row[10])); - entry.safe_return_heading = static_cast(atof(row[11])); - entry.zone_in_x = static_cast(atof(row[12])); - entry.zone_in_y = static_cast(atof(row[13])); - entry.zone_in_z = static_cast(atof(row[14])); - entry.zone_in_heading = static_cast(atof(row[15])); - entry.has_zone_in = atoi(row[16]); + entry.uuid = row[3] ? row[3] : ""; + entry.name = row[4] ? row[4] : ""; + entry.leader_id = atoi(row[5]); + entry.min_players = atoi(row[6]); + entry.max_players = atoi(row[7]); + entry.compass_zone_id = atoi(row[8]); + entry.compass_x = static_cast(atof(row[9])); + entry.compass_y = static_cast(atof(row[10])); + entry.compass_z = static_cast(atof(row[11])); + entry.safe_return_zone_id = atoi(row[12]); + entry.safe_return_x = static_cast(atof(row[13])); + entry.safe_return_y = static_cast(atof(row[14])); + entry.safe_return_z = static_cast(atof(row[15])); + entry.safe_return_heading = static_cast(atof(row[16])); + entry.zone_in_x = static_cast(atof(row[17])); + entry.zone_in_y = static_cast(atof(row[18])); + entry.zone_in_z = static_cast(atof(row[19])); + entry.zone_in_heading = static_cast(atof(row[20])); + entry.has_zone_in = atoi(row[21]); all_entries.push_back(entry); } diff --git a/common/repositories/base/base_expeditions_repository.h b/common/repositories/base/base_expeditions_repository.h index fb8007339..f77be8bfa 100644 --- a/common/repositories/base/base_expeditions_repository.h +++ b/common/repositories/base/base_expeditions_repository.h @@ -18,15 +18,10 @@ class BaseExpeditionsRepository { public: struct Expeditions { - int id; - std::string uuid; - int dynamic_zone_id; - std::string expedition_name; - int leader_id; - int min_players; - int max_players; - int add_replay_on_join; - int is_locked; + int id; + int dynamic_zone_id; + int add_replay_on_join; + int is_locked; }; static std::string PrimaryKey() @@ -38,12 +33,7 @@ public: { return { "id", - "uuid", "dynamic_zone_id", - "expedition_name", - "leader_id", - "min_players", - "max_players", "add_replay_on_join", "is_locked", }; @@ -82,12 +72,7 @@ public: Expeditions entry{}; entry.id = 0; - entry.uuid = ""; entry.dynamic_zone_id = 0; - entry.expedition_name = ""; - entry.leader_id = 0; - entry.min_players = 0; - entry.max_players = 0; entry.add_replay_on_join = 1; entry.is_locked = 0; @@ -126,14 +111,9 @@ public: Expeditions entry{}; entry.id = atoi(row[0]); - entry.uuid = row[1] ? row[1] : ""; - entry.dynamic_zone_id = atoi(row[2]); - entry.expedition_name = row[3] ? row[3] : ""; - entry.leader_id = atoi(row[4]); - entry.min_players = atoi(row[5]); - entry.max_players = atoi(row[6]); - entry.add_replay_on_join = atoi(row[7]); - entry.is_locked = atoi(row[8]); + entry.dynamic_zone_id = atoi(row[1]); + entry.add_replay_on_join = atoi(row[2]); + entry.is_locked = atoi(row[3]); return entry; } @@ -167,14 +147,9 @@ public: auto columns = Columns(); - update_values.push_back(columns[1] + " = '" + EscapeString(expeditions_entry.uuid) + "'"); - update_values.push_back(columns[2] + " = " + std::to_string(expeditions_entry.dynamic_zone_id)); - update_values.push_back(columns[3] + " = '" + EscapeString(expeditions_entry.expedition_name) + "'"); - update_values.push_back(columns[4] + " = " + std::to_string(expeditions_entry.leader_id)); - update_values.push_back(columns[5] + " = " + std::to_string(expeditions_entry.min_players)); - update_values.push_back(columns[6] + " = " + std::to_string(expeditions_entry.max_players)); - update_values.push_back(columns[7] + " = " + std::to_string(expeditions_entry.add_replay_on_join)); - update_values.push_back(columns[8] + " = " + std::to_string(expeditions_entry.is_locked)); + update_values.push_back(columns[1] + " = " + std::to_string(expeditions_entry.dynamic_zone_id)); + update_values.push_back(columns[2] + " = " + std::to_string(expeditions_entry.add_replay_on_join)); + update_values.push_back(columns[3] + " = " + std::to_string(expeditions_entry.is_locked)); auto results = db.QueryDatabase( fmt::format( @@ -197,12 +172,7 @@ public: std::vector insert_values; insert_values.push_back(std::to_string(expeditions_entry.id)); - insert_values.push_back("'" + EscapeString(expeditions_entry.uuid) + "'"); insert_values.push_back(std::to_string(expeditions_entry.dynamic_zone_id)); - insert_values.push_back("'" + EscapeString(expeditions_entry.expedition_name) + "'"); - insert_values.push_back(std::to_string(expeditions_entry.leader_id)); - insert_values.push_back(std::to_string(expeditions_entry.min_players)); - insert_values.push_back(std::to_string(expeditions_entry.max_players)); insert_values.push_back(std::to_string(expeditions_entry.add_replay_on_join)); insert_values.push_back(std::to_string(expeditions_entry.is_locked)); @@ -235,12 +205,7 @@ public: std::vector insert_values; insert_values.push_back(std::to_string(expeditions_entry.id)); - insert_values.push_back("'" + EscapeString(expeditions_entry.uuid) + "'"); insert_values.push_back(std::to_string(expeditions_entry.dynamic_zone_id)); - insert_values.push_back("'" + EscapeString(expeditions_entry.expedition_name) + "'"); - insert_values.push_back(std::to_string(expeditions_entry.leader_id)); - insert_values.push_back(std::to_string(expeditions_entry.min_players)); - insert_values.push_back(std::to_string(expeditions_entry.max_players)); insert_values.push_back(std::to_string(expeditions_entry.add_replay_on_join)); insert_values.push_back(std::to_string(expeditions_entry.is_locked)); @@ -277,14 +242,9 @@ public: Expeditions entry{}; entry.id = atoi(row[0]); - entry.uuid = row[1] ? row[1] : ""; - entry.dynamic_zone_id = atoi(row[2]); - entry.expedition_name = row[3] ? row[3] : ""; - entry.leader_id = atoi(row[4]); - entry.min_players = atoi(row[5]); - entry.max_players = atoi(row[6]); - entry.add_replay_on_join = atoi(row[7]); - entry.is_locked = atoi(row[8]); + entry.dynamic_zone_id = atoi(row[1]); + entry.add_replay_on_join = atoi(row[2]); + entry.is_locked = atoi(row[3]); all_entries.push_back(entry); } @@ -310,14 +270,9 @@ public: Expeditions entry{}; entry.id = atoi(row[0]); - entry.uuid = row[1] ? row[1] : ""; - entry.dynamic_zone_id = atoi(row[2]); - entry.expedition_name = row[3] ? row[3] : ""; - entry.leader_id = atoi(row[4]); - entry.min_players = atoi(row[5]); - entry.max_players = atoi(row[6]); - entry.add_replay_on_join = atoi(row[7]); - entry.is_locked = atoi(row[8]); + entry.dynamic_zone_id = atoi(row[1]); + entry.add_replay_on_join = atoi(row[2]); + entry.is_locked = atoi(row[3]); all_entries.push_back(entry); } diff --git a/common/repositories/base/base_shared_task_activity_state_repository.h b/common/repositories/base/base_shared_task_activity_state_repository.h new file mode 100644 index 000000000..e599f3cb7 --- /dev/null +++ b/common/repositories/base/base_shared_task_activity_state_repository.h @@ -0,0 +1,337 @@ +/** + * DO NOT MODIFY THIS FILE + * + * This repository was automatically generated and is NOT to be modified directly. + * Any repository modifications are meant to be made to the repository extending the base. + * Any modifications to base repositories are to be made by the generator only + * + * @generator ./utils/scripts/generators/repository-generator.pl + * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories + */ + +#ifndef EQEMU_BASE_SHARED_TASK_ACTIVITY_STATE_REPOSITORY_H +#define EQEMU_BASE_SHARED_TASK_ACTIVITY_STATE_REPOSITORY_H + +#include "../../database.h" +#include "../../string_util.h" +#include + +class BaseSharedTaskActivityStateRepository { +public: + struct SharedTaskActivityState { + int64 shared_task_id; + int activity_id; + int done_count; + time_t updated_time; + time_t completed_time; + }; + + static std::string PrimaryKey() + { + return std::string("shared_task_id"); + } + + static std::vector Columns() + { + return { + "shared_task_id", + "activity_id", + "done_count", + "updated_time", + "completed_time", + }; + } + + static std::vector SelectColumns() + { + return { + "shared_task_id", + "activity_id", + "done_count", + "UNIX_TIMESTAMP(updated_time)", + "UNIX_TIMESTAMP(completed_time)", + }; + } + + static std::string ColumnsRaw() + { + return std::string(implode(", ", Columns())); + } + + static std::string SelectColumnsRaw() + { + return std::string(implode(", ", SelectColumns())); + } + + static std::string TableName() + { + return std::string("shared_task_activity_state"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + SelectColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static SharedTaskActivityState NewEntity() + { + SharedTaskActivityState entry{}; + + entry.shared_task_id = 0; + entry.activity_id = 0; + entry.done_count = 0; + entry.updated_time = 0; + entry.completed_time = 0; + + return entry; + } + + static SharedTaskActivityState GetSharedTaskActivityStateEntry( + const std::vector &shared_task_activity_states, + int shared_task_activity_state_id + ) + { + for (auto &shared_task_activity_state : shared_task_activity_states) { + if (shared_task_activity_state.shared_task_id == shared_task_activity_state_id) { + return shared_task_activity_state; + } + } + + return NewEntity(); + } + + static SharedTaskActivityState FindOne( + Database& db, + int shared_task_activity_state_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE id = {} LIMIT 1", + BaseSelect(), + shared_task_activity_state_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + SharedTaskActivityState entry{}; + + entry.shared_task_id = strtoll(row[0], nullptr, 10); + entry.activity_id = atoi(row[1]); + entry.done_count = atoi(row[2]); + entry.updated_time = strtoll(row[3] ? row[3] : "-1", nullptr, 10); + entry.completed_time = strtoll(row[4] ? row[4] : "-1", nullptr, 10); + + return entry; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int shared_task_activity_state_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + shared_task_activity_state_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + SharedTaskActivityState shared_task_activity_state_entry + ) + { + std::vector update_values; + + auto columns = Columns(); + + update_values.push_back(columns[0] + " = " + std::to_string(shared_task_activity_state_entry.shared_task_id)); + update_values.push_back(columns[1] + " = " + std::to_string(shared_task_activity_state_entry.activity_id)); + update_values.push_back(columns[2] + " = " + std::to_string(shared_task_activity_state_entry.done_count)); + update_values.push_back(columns[3] + " = FROM_UNIXTIME(" + (shared_task_activity_state_entry.updated_time > 0 ? std::to_string(shared_task_activity_state_entry.updated_time) : "null") + ")"); + update_values.push_back(columns[4] + " = FROM_UNIXTIME(" + (shared_task_activity_state_entry.completed_time > 0 ? std::to_string(shared_task_activity_state_entry.completed_time) : "null") + ")"); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + implode(", ", update_values), + PrimaryKey(), + shared_task_activity_state_entry.shared_task_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static SharedTaskActivityState InsertOne( + Database& db, + SharedTaskActivityState shared_task_activity_state_entry + ) + { + std::vector insert_values; + + insert_values.push_back(std::to_string(shared_task_activity_state_entry.shared_task_id)); + insert_values.push_back(std::to_string(shared_task_activity_state_entry.activity_id)); + insert_values.push_back(std::to_string(shared_task_activity_state_entry.done_count)); + insert_values.push_back("FROM_UNIXTIME(" + (shared_task_activity_state_entry.updated_time > 0 ? std::to_string(shared_task_activity_state_entry.updated_time) : "null") + ")"); + insert_values.push_back("FROM_UNIXTIME(" + (shared_task_activity_state_entry.completed_time > 0 ? std::to_string(shared_task_activity_state_entry.completed_time) : "null") + ")"); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseInsert(), + implode(",", insert_values) + ) + ); + + if (results.Success()) { + shared_task_activity_state_entry.shared_task_id = results.LastInsertedID(); + return shared_task_activity_state_entry; + } + + shared_task_activity_state_entry = NewEntity(); + + return shared_task_activity_state_entry; + } + + static int InsertMany( + Database& db, + std::vector shared_task_activity_state_entries + ) + { + std::vector insert_chunks; + + for (auto &shared_task_activity_state_entry: shared_task_activity_state_entries) { + std::vector insert_values; + + insert_values.push_back(std::to_string(shared_task_activity_state_entry.shared_task_id)); + insert_values.push_back(std::to_string(shared_task_activity_state_entry.activity_id)); + insert_values.push_back(std::to_string(shared_task_activity_state_entry.done_count)); + insert_values.push_back("FROM_UNIXTIME(" + (shared_task_activity_state_entry.updated_time > 0 ? std::to_string(shared_task_activity_state_entry.updated_time) : "null") + ")"); + insert_values.push_back("FROM_UNIXTIME(" + (shared_task_activity_state_entry.completed_time > 0 ? std::to_string(shared_task_activity_state_entry.completed_time) : "null") + ")"); + + insert_chunks.push_back("(" + implode(",", insert_values) + ")"); + } + + std::vector insert_values; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseInsert(), + implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static std::vector All(Database& db) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{}", + BaseSelect() + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + SharedTaskActivityState entry{}; + + entry.shared_task_id = strtoll(row[0], nullptr, 10); + entry.activity_id = atoi(row[1]); + entry.done_count = atoi(row[2]); + entry.updated_time = strtoll(row[3] ? row[3] : "-1", nullptr, 10); + entry.completed_time = strtoll(row[4] ? row[4] : "-1", nullptr, 10); + + all_entries.push_back(entry); + } + + return all_entries; + } + + static std::vector GetWhere(Database& db, std::string where_filter) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {}", + BaseSelect(), + where_filter + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + SharedTaskActivityState entry{}; + + entry.shared_task_id = strtoll(row[0], nullptr, 10); + entry.activity_id = atoi(row[1]); + entry.done_count = atoi(row[2]); + entry.updated_time = strtoll(row[3] ? row[3] : "-1", nullptr, 10); + entry.completed_time = strtoll(row[4] ? row[4] : "-1", nullptr, 10); + + all_entries.push_back(entry); + } + + return all_entries; + } + + static int DeleteWhere(Database& db, std::string where_filter) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {}", + TableName(), + where_filter + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int Truncate(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "TRUNCATE TABLE {}", + TableName() + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + +}; + +#endif //EQEMU_BASE_SHARED_TASK_ACTIVITY_STATE_REPOSITORY_H diff --git a/common/repositories/base/base_shared_task_dynamic_zones_repository.h b/common/repositories/base/base_shared_task_dynamic_zones_repository.h new file mode 100644 index 000000000..ab60aa54d --- /dev/null +++ b/common/repositories/base/base_shared_task_dynamic_zones_repository.h @@ -0,0 +1,293 @@ +/** + * DO NOT MODIFY THIS FILE + * + * This repository was automatically generated and is NOT to be modified directly. + * Any repository modifications are meant to be made to the repository extending the base. + * Any modifications to base repositories are to be made by the generator only + * + * @generator ./utils/scripts/generators/repository-generator.pl + * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories + */ + +#ifndef EQEMU_BASE_SHARED_TASK_DYNAMIC_ZONES_REPOSITORY_H +#define EQEMU_BASE_SHARED_TASK_DYNAMIC_ZONES_REPOSITORY_H + +#include "../../database.h" +#include "../../string_util.h" + +class BaseSharedTaskDynamicZonesRepository { +public: + struct SharedTaskDynamicZones { + int64 shared_task_id; + int dynamic_zone_id; + }; + + static std::string PrimaryKey() + { + return std::string("shared_task_id"); + } + + static std::vector Columns() + { + return { + "shared_task_id", + "dynamic_zone_id", + }; + } + + static std::string ColumnsRaw() + { + return std::string(implode(", ", Columns())); + } + + static std::string TableName() + { + return std::string("shared_task_dynamic_zones"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + ColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static SharedTaskDynamicZones NewEntity() + { + SharedTaskDynamicZones entry{}; + + entry.shared_task_id = 0; + entry.dynamic_zone_id = 0; + + return entry; + } + + static SharedTaskDynamicZones GetSharedTaskDynamicZonesEntry( + const std::vector &shared_task_dynamic_zoness, + int shared_task_dynamic_zones_id + ) + { + for (auto &shared_task_dynamic_zones : shared_task_dynamic_zoness) { + if (shared_task_dynamic_zones.shared_task_id == shared_task_dynamic_zones_id) { + return shared_task_dynamic_zones; + } + } + + return NewEntity(); + } + + static SharedTaskDynamicZones FindOne( + Database& db, + int shared_task_dynamic_zones_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE id = {} LIMIT 1", + BaseSelect(), + shared_task_dynamic_zones_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + SharedTaskDynamicZones entry{}; + + entry.shared_task_id = strtoll(row[0], NULL, 10); + entry.dynamic_zone_id = atoi(row[1]); + + return entry; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int shared_task_dynamic_zones_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + shared_task_dynamic_zones_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + SharedTaskDynamicZones shared_task_dynamic_zones_entry + ) + { + std::vector update_values; + + auto columns = Columns(); + + update_values.push_back(columns[0] + " = " + std::to_string(shared_task_dynamic_zones_entry.shared_task_id)); + update_values.push_back(columns[1] + " = " + std::to_string(shared_task_dynamic_zones_entry.dynamic_zone_id)); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + implode(", ", update_values), + PrimaryKey(), + shared_task_dynamic_zones_entry.shared_task_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static SharedTaskDynamicZones InsertOne( + Database& db, + SharedTaskDynamicZones shared_task_dynamic_zones_entry + ) + { + std::vector insert_values; + + insert_values.push_back(std::to_string(shared_task_dynamic_zones_entry.shared_task_id)); + insert_values.push_back(std::to_string(shared_task_dynamic_zones_entry.dynamic_zone_id)); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseInsert(), + implode(",", insert_values) + ) + ); + + if (results.Success()) { + shared_task_dynamic_zones_entry.shared_task_id = results.LastInsertedID(); + return shared_task_dynamic_zones_entry; + } + + shared_task_dynamic_zones_entry = NewEntity(); + + return shared_task_dynamic_zones_entry; + } + + static int InsertMany( + Database& db, + std::vector shared_task_dynamic_zones_entries + ) + { + std::vector insert_chunks; + + for (auto &shared_task_dynamic_zones_entry: shared_task_dynamic_zones_entries) { + std::vector insert_values; + + insert_values.push_back(std::to_string(shared_task_dynamic_zones_entry.shared_task_id)); + insert_values.push_back(std::to_string(shared_task_dynamic_zones_entry.dynamic_zone_id)); + + insert_chunks.push_back("(" + implode(",", insert_values) + ")"); + } + + std::vector insert_values; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseInsert(), + implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static std::vector All(Database& db) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{}", + BaseSelect() + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + SharedTaskDynamicZones entry{}; + + entry.shared_task_id = strtoll(row[0], NULL, 10); + entry.dynamic_zone_id = atoi(row[1]); + + all_entries.push_back(entry); + } + + return all_entries; + } + + static std::vector GetWhere(Database& db, std::string where_filter) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {}", + BaseSelect(), + where_filter + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + SharedTaskDynamicZones entry{}; + + entry.shared_task_id = strtoll(row[0], NULL, 10); + entry.dynamic_zone_id = atoi(row[1]); + + all_entries.push_back(entry); + } + + return all_entries; + } + + static int DeleteWhere(Database& db, std::string where_filter) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {}", + TableName(), + where_filter + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int Truncate(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "TRUNCATE TABLE {}", + TableName() + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + +}; + +#endif //EQEMU_BASE_SHARED_TASK_DYNAMIC_ZONES_REPOSITORY_H diff --git a/common/repositories/base/base_shared_task_members_repository.h b/common/repositories/base/base_shared_task_members_repository.h new file mode 100644 index 000000000..6e5d36227 --- /dev/null +++ b/common/repositories/base/base_shared_task_members_repository.h @@ -0,0 +1,302 @@ +/** + * DO NOT MODIFY THIS FILE + * + * This repository was automatically generated and is NOT to be modified directly. + * Any repository modifications are meant to be made to the repository extending the base. + * Any modifications to base repositories are to be made by the generator only + * + * @generator ./utils/scripts/generators/repository-generator.pl + * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories + */ + +#ifndef EQEMU_BASE_SHARED_TASK_MEMBERS_REPOSITORY_H +#define EQEMU_BASE_SHARED_TASK_MEMBERS_REPOSITORY_H + +#include "../../database.h" +#include "../../string_util.h" + +class BaseSharedTaskMembersRepository { +public: + struct SharedTaskMembers { + int64 shared_task_id; + int64 character_id; + int is_leader; + }; + + static std::string PrimaryKey() + { + return std::string("shared_task_id"); + } + + static std::vector Columns() + { + return { + "shared_task_id", + "character_id", + "is_leader", + }; + } + + static std::string ColumnsRaw() + { + return std::string(implode(", ", Columns())); + } + + static std::string TableName() + { + return std::string("shared_task_members"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + ColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static SharedTaskMembers NewEntity() + { + SharedTaskMembers entry{}; + + entry.shared_task_id = 0; + entry.character_id = 0; + entry.is_leader = 0; + + return entry; + } + + static SharedTaskMembers GetSharedTaskMembersEntry( + const std::vector &shared_task_memberss, + int shared_task_members_id + ) + { + for (auto &shared_task_members : shared_task_memberss) { + if (shared_task_members.shared_task_id == shared_task_members_id) { + return shared_task_members; + } + } + + return NewEntity(); + } + + static SharedTaskMembers FindOne( + Database& db, + int shared_task_members_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE id = {} LIMIT 1", + BaseSelect(), + shared_task_members_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + SharedTaskMembers entry{}; + + entry.shared_task_id = strtoll(row[0], NULL, 10); + entry.character_id = strtoll(row[1], NULL, 10); + entry.is_leader = atoi(row[2]); + + return entry; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int shared_task_members_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + shared_task_members_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + SharedTaskMembers shared_task_members_entry + ) + { + std::vector update_values; + + auto columns = Columns(); + + update_values.push_back(columns[0] + " = " + std::to_string(shared_task_members_entry.shared_task_id)); + update_values.push_back(columns[1] + " = " + std::to_string(shared_task_members_entry.character_id)); + update_values.push_back(columns[2] + " = " + std::to_string(shared_task_members_entry.is_leader)); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + implode(", ", update_values), + PrimaryKey(), + shared_task_members_entry.shared_task_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static SharedTaskMembers InsertOne( + Database& db, + SharedTaskMembers shared_task_members_entry + ) + { + std::vector insert_values; + + insert_values.push_back(std::to_string(shared_task_members_entry.shared_task_id)); + insert_values.push_back(std::to_string(shared_task_members_entry.character_id)); + insert_values.push_back(std::to_string(shared_task_members_entry.is_leader)); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseInsert(), + implode(",", insert_values) + ) + ); + + if (results.Success()) { + shared_task_members_entry.shared_task_id = results.LastInsertedID(); + return shared_task_members_entry; + } + + shared_task_members_entry = NewEntity(); + + return shared_task_members_entry; + } + + static int InsertMany( + Database& db, + std::vector shared_task_members_entries + ) + { + std::vector insert_chunks; + + for (auto &shared_task_members_entry: shared_task_members_entries) { + std::vector insert_values; + + insert_values.push_back(std::to_string(shared_task_members_entry.shared_task_id)); + insert_values.push_back(std::to_string(shared_task_members_entry.character_id)); + insert_values.push_back(std::to_string(shared_task_members_entry.is_leader)); + + insert_chunks.push_back("(" + implode(",", insert_values) + ")"); + } + + std::vector insert_values; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseInsert(), + implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static std::vector All(Database& db) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{}", + BaseSelect() + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + SharedTaskMembers entry{}; + + entry.shared_task_id = strtoll(row[0], NULL, 10); + entry.character_id = strtoll(row[1], NULL, 10); + entry.is_leader = atoi(row[2]); + + all_entries.push_back(entry); + } + + return all_entries; + } + + static std::vector GetWhere(Database& db, std::string where_filter) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {}", + BaseSelect(), + where_filter + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + SharedTaskMembers entry{}; + + entry.shared_task_id = strtoll(row[0], NULL, 10); + entry.character_id = strtoll(row[1], NULL, 10); + entry.is_leader = atoi(row[2]); + + all_entries.push_back(entry); + } + + return all_entries; + } + + static int DeleteWhere(Database& db, std::string where_filter) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {}", + TableName(), + where_filter + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int Truncate(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "TRUNCATE TABLE {}", + TableName() + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + +}; + +#endif //EQEMU_BASE_SHARED_TASK_MEMBERS_REPOSITORY_H diff --git a/common/repositories/base/base_shared_tasks_repository.h b/common/repositories/base/base_shared_tasks_repository.h new file mode 100644 index 000000000..405bf09ed --- /dev/null +++ b/common/repositories/base/base_shared_tasks_repository.h @@ -0,0 +1,346 @@ +/** + * DO NOT MODIFY THIS FILE + * + * This repository was automatically generated and is NOT to be modified directly. + * Any repository modifications are meant to be made to the repository extending the base. + * Any modifications to base repositories are to be made by the generator only + * + * @generator ./utils/scripts/generators/repository-generator.pl + * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories + */ + +#ifndef EQEMU_BASE_SHARED_TASKS_REPOSITORY_H +#define EQEMU_BASE_SHARED_TASKS_REPOSITORY_H + +#include "../../database.h" +#include "../../string_util.h" +#include + +class BaseSharedTasksRepository { +public: + struct SharedTasks { + int64 id; + int task_id; + time_t accepted_time; + time_t expire_time; + time_t completion_time; + int is_locked; + }; + + static std::string PrimaryKey() + { + return std::string("id"); + } + + static std::vector Columns() + { + return { + "id", + "task_id", + "accepted_time", + "expire_time", + "completion_time", + "is_locked", + }; + } + + static std::vector SelectColumns() + { + return { + "id", + "task_id", + "UNIX_TIMESTAMP(accepted_time)", + "UNIX_TIMESTAMP(expire_time)", + "UNIX_TIMESTAMP(completion_time)", + "is_locked", + }; + } + + static std::string ColumnsRaw() + { + return std::string(implode(", ", Columns())); + } + + static std::string SelectColumnsRaw() + { + return std::string(implode(", ", SelectColumns())); + } + + static std::string TableName() + { + return std::string("shared_tasks"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + SelectColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static SharedTasks NewEntity() + { + SharedTasks entry{}; + + entry.id = 0; + entry.task_id = 0; + entry.accepted_time = 0; + entry.expire_time = 0; + entry.completion_time = 0; + entry.is_locked = 0; + + return entry; + } + + static SharedTasks GetSharedTasksEntry( + const std::vector &shared_taskss, + int shared_tasks_id + ) + { + for (auto &shared_tasks : shared_taskss) { + if (shared_tasks.id == shared_tasks_id) { + return shared_tasks; + } + } + + return NewEntity(); + } + + static SharedTasks FindOne( + Database& db, + int shared_tasks_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE id = {} LIMIT 1", + BaseSelect(), + shared_tasks_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + SharedTasks entry{}; + + entry.id = strtoll(row[0], nullptr, 10); + entry.task_id = atoi(row[1]); + entry.accepted_time = strtoll(row[2] ? row[2] : "-1", nullptr, 10); + entry.expire_time = strtoll(row[3] ? row[3] : "-1", nullptr, 10); + entry.completion_time = strtoll(row[4] ? row[4] : "-1", nullptr, 10); + entry.is_locked = atoi(row[5]); + + return entry; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int shared_tasks_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + shared_tasks_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + SharedTasks shared_tasks_entry + ) + { + std::vector update_values; + + auto columns = Columns(); + + update_values.push_back(columns[1] + " = " + std::to_string(shared_tasks_entry.task_id)); + update_values.push_back(columns[2] + " = FROM_UNIXTIME(" + (shared_tasks_entry.accepted_time > 0 ? std::to_string(shared_tasks_entry.accepted_time) : "null") + ")"); + update_values.push_back(columns[3] + " = FROM_UNIXTIME(" + (shared_tasks_entry.expire_time > 0 ? std::to_string(shared_tasks_entry.expire_time) : "null") + ")"); + update_values.push_back(columns[4] + " = FROM_UNIXTIME(" + (shared_tasks_entry.completion_time > 0 ? std::to_string(shared_tasks_entry.completion_time) : "null") + ")"); + update_values.push_back(columns[5] + " = " + std::to_string(shared_tasks_entry.is_locked)); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + implode(", ", update_values), + PrimaryKey(), + shared_tasks_entry.id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static SharedTasks InsertOne( + Database& db, + SharedTasks shared_tasks_entry + ) + { + std::vector insert_values; + + insert_values.push_back(std::to_string(shared_tasks_entry.id)); + insert_values.push_back(std::to_string(shared_tasks_entry.task_id)); + insert_values.push_back("FROM_UNIXTIME(" + (shared_tasks_entry.accepted_time > 0 ? std::to_string(shared_tasks_entry.accepted_time) : "null") + ")"); + insert_values.push_back("FROM_UNIXTIME(" + (shared_tasks_entry.expire_time > 0 ? std::to_string(shared_tasks_entry.expire_time) : "null") + ")"); + insert_values.push_back("FROM_UNIXTIME(" + (shared_tasks_entry.completion_time > 0 ? std::to_string(shared_tasks_entry.completion_time) : "null") + ")"); + insert_values.push_back(std::to_string(shared_tasks_entry.is_locked)); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseInsert(), + implode(",", insert_values) + ) + ); + + if (results.Success()) { + shared_tasks_entry.id = results.LastInsertedID(); + return shared_tasks_entry; + } + + shared_tasks_entry = NewEntity(); + + return shared_tasks_entry; + } + + static int InsertMany( + Database& db, + std::vector shared_tasks_entries + ) + { + std::vector insert_chunks; + + for (auto &shared_tasks_entry: shared_tasks_entries) { + std::vector insert_values; + + insert_values.push_back(std::to_string(shared_tasks_entry.id)); + insert_values.push_back(std::to_string(shared_tasks_entry.task_id)); + insert_values.push_back("FROM_UNIXTIME(" + (shared_tasks_entry.accepted_time > 0 ? std::to_string(shared_tasks_entry.accepted_time) : "null") + ")"); + insert_values.push_back("FROM_UNIXTIME(" + (shared_tasks_entry.expire_time > 0 ? std::to_string(shared_tasks_entry.expire_time) : "null") + ")"); + insert_values.push_back("FROM_UNIXTIME(" + (shared_tasks_entry.completion_time > 0 ? std::to_string(shared_tasks_entry.completion_time) : "null") + ")"); + insert_values.push_back(std::to_string(shared_tasks_entry.is_locked)); + + insert_chunks.push_back("(" + implode(",", insert_values) + ")"); + } + + std::vector insert_values; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseInsert(), + implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static std::vector All(Database& db) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{}", + BaseSelect() + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + SharedTasks entry{}; + + entry.id = strtoll(row[0], nullptr, 10); + entry.task_id = atoi(row[1]); + entry.accepted_time = strtoll(row[2] ? row[2] : "-1", nullptr, 10); + entry.expire_time = strtoll(row[3] ? row[3] : "-1", nullptr, 10); + entry.completion_time = strtoll(row[4] ? row[4] : "-1", nullptr, 10); + entry.is_locked = atoi(row[5]); + + all_entries.push_back(entry); + } + + return all_entries; + } + + static std::vector GetWhere(Database& db, std::string where_filter) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {}", + BaseSelect(), + where_filter + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + SharedTasks entry{}; + + entry.id = strtoll(row[0], nullptr, 10); + entry.task_id = atoi(row[1]); + entry.accepted_time = strtoll(row[2] ? row[2] : "-1", nullptr, 10); + entry.expire_time = strtoll(row[3] ? row[3] : "-1", nullptr, 10); + entry.completion_time = strtoll(row[4] ? row[4] : "-1", nullptr, 10); + entry.is_locked = atoi(row[5]); + + all_entries.push_back(entry); + } + + return all_entries; + } + + static int DeleteWhere(Database& db, std::string where_filter) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {}", + TableName(), + where_filter + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int Truncate(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "TRUNCATE TABLE {}", + TableName() + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + +}; + +#endif //EQEMU_BASE_SHARED_TASKS_REPOSITORY_H diff --git a/common/repositories/base/base_tasks_repository.h b/common/repositories/base/base_tasks_repository.h index 3d1982bf2..23c76d788 100644 --- a/common/repositories/base/base_tasks_repository.h +++ b/common/repositories/base/base_tasks_repository.h @@ -14,6 +14,7 @@ #include "../../database.h" #include "../../string_util.h" +#include class BaseTasksRepository { public: @@ -29,11 +30,18 @@ public: int cashreward; int xpreward; int rewardmethod; + int reward_radiant_crystals; + int reward_ebon_crystals; int minlevel; int maxlevel; + int level_spread; + int min_players; + int max_players; int repeatable; int faction_reward; std::string completion_emote; + int replay_timer_seconds; + int request_timer_seconds; }; static std::string PrimaryKey() @@ -55,11 +63,47 @@ public: "cashreward", "xpreward", "rewardmethod", + "reward_radiant_crystals", + "reward_ebon_crystals", "minlevel", "maxlevel", + "level_spread", + "min_players", + "max_players", "repeatable", "faction_reward", "completion_emote", + "replay_timer_seconds", + "request_timer_seconds", + }; + } + + static std::vector SelectColumns() + { + return { + "id", + "type", + "duration", + "duration_code", + "title", + "description", + "reward", + "rewardid", + "cashreward", + "xpreward", + "rewardmethod", + "reward_radiant_crystals", + "reward_ebon_crystals", + "minlevel", + "maxlevel", + "level_spread", + "min_players", + "max_players", + "repeatable", + "faction_reward", + "completion_emote", + "replay_timer_seconds", + "request_timer_seconds", }; } @@ -68,6 +112,11 @@ public: return std::string(implode(", ", Columns())); } + static std::string SelectColumnsRaw() + { + return std::string(implode(", ", SelectColumns())); + } + static std::string TableName() { return std::string("tasks"); @@ -77,7 +126,7 @@ public: { return fmt::format( "SELECT {} FROM {}", - ColumnsRaw(), + SelectColumnsRaw(), TableName() ); } @@ -95,22 +144,29 @@ public: { Tasks entry{}; - entry.id = 0; - entry.type = 0; - entry.duration = 0; - entry.duration_code = 0; - entry.title = ""; - entry.description = ""; - entry.reward = ""; - entry.rewardid = 0; - entry.cashreward = 0; - entry.xpreward = 0; - entry.rewardmethod = 2; - entry.minlevel = 0; - entry.maxlevel = 0; - entry.repeatable = 1; - entry.faction_reward = 0; - entry.completion_emote = ""; + entry.id = 0; + entry.type = 0; + entry.duration = 0; + entry.duration_code = 0; + entry.title = ""; + entry.description = ""; + entry.reward = ""; + entry.rewardid = 0; + entry.cashreward = 0; + entry.xpreward = 0; + entry.rewardmethod = 2; + entry.reward_radiant_crystals = 0; + entry.reward_ebon_crystals = 0; + entry.minlevel = 0; + entry.maxlevel = 0; + entry.level_spread = 0; + entry.min_players = 0; + entry.max_players = 0; + entry.repeatable = 1; + entry.faction_reward = 0; + entry.completion_emote = ""; + entry.replay_timer_seconds = 0; + entry.request_timer_seconds = 0; return entry; } @@ -146,22 +202,29 @@ public: if (results.RowCount() == 1) { Tasks entry{}; - entry.id = atoi(row[0]); - entry.type = atoi(row[1]); - entry.duration = atoi(row[2]); - entry.duration_code = atoi(row[3]); - entry.title = row[4] ? row[4] : ""; - entry.description = row[5] ? row[5] : ""; - entry.reward = row[6] ? row[6] : ""; - entry.rewardid = atoi(row[7]); - entry.cashreward = atoi(row[8]); - entry.xpreward = atoi(row[9]); - entry.rewardmethod = atoi(row[10]); - entry.minlevel = atoi(row[11]); - entry.maxlevel = atoi(row[12]); - entry.repeatable = atoi(row[13]); - entry.faction_reward = atoi(row[14]); - entry.completion_emote = row[15] ? row[15] : ""; + entry.id = atoi(row[0]); + entry.type = atoi(row[1]); + entry.duration = atoi(row[2]); + entry.duration_code = atoi(row[3]); + entry.title = row[4] ? row[4] : ""; + entry.description = row[5] ? row[5] : ""; + entry.reward = row[6] ? row[6] : ""; + entry.rewardid = atoi(row[7]); + entry.cashreward = atoi(row[8]); + entry.xpreward = atoi(row[9]); + entry.rewardmethod = atoi(row[10]); + entry.reward_radiant_crystals = atoi(row[11]); + entry.reward_ebon_crystals = atoi(row[12]); + entry.minlevel = atoi(row[13]); + entry.maxlevel = atoi(row[14]); + entry.level_spread = atoi(row[15]); + entry.min_players = atoi(row[16]); + entry.max_players = atoi(row[17]); + entry.repeatable = atoi(row[18]); + entry.faction_reward = atoi(row[19]); + entry.completion_emote = row[20] ? row[20] : ""; + entry.replay_timer_seconds = atoi(row[21]); + entry.request_timer_seconds = atoi(row[22]); return entry; } @@ -206,11 +269,18 @@ public: update_values.push_back(columns[8] + " = " + std::to_string(tasks_entry.cashreward)); update_values.push_back(columns[9] + " = " + std::to_string(tasks_entry.xpreward)); update_values.push_back(columns[10] + " = " + std::to_string(tasks_entry.rewardmethod)); - update_values.push_back(columns[11] + " = " + std::to_string(tasks_entry.minlevel)); - update_values.push_back(columns[12] + " = " + std::to_string(tasks_entry.maxlevel)); - update_values.push_back(columns[13] + " = " + std::to_string(tasks_entry.repeatable)); - update_values.push_back(columns[14] + " = " + std::to_string(tasks_entry.faction_reward)); - update_values.push_back(columns[15] + " = '" + EscapeString(tasks_entry.completion_emote) + "'"); + update_values.push_back(columns[11] + " = " + std::to_string(tasks_entry.reward_radiant_crystals)); + update_values.push_back(columns[12] + " = " + std::to_string(tasks_entry.reward_ebon_crystals)); + update_values.push_back(columns[13] + " = " + std::to_string(tasks_entry.minlevel)); + update_values.push_back(columns[14] + " = " + std::to_string(tasks_entry.maxlevel)); + update_values.push_back(columns[15] + " = " + std::to_string(tasks_entry.level_spread)); + update_values.push_back(columns[16] + " = " + std::to_string(tasks_entry.min_players)); + update_values.push_back(columns[17] + " = " + std::to_string(tasks_entry.max_players)); + update_values.push_back(columns[18] + " = " + std::to_string(tasks_entry.repeatable)); + update_values.push_back(columns[19] + " = " + std::to_string(tasks_entry.faction_reward)); + update_values.push_back(columns[20] + " = '" + EscapeString(tasks_entry.completion_emote) + "'"); + update_values.push_back(columns[21] + " = " + std::to_string(tasks_entry.replay_timer_seconds)); + update_values.push_back(columns[22] + " = " + std::to_string(tasks_entry.request_timer_seconds)); auto results = db.QueryDatabase( fmt::format( @@ -243,11 +313,18 @@ public: insert_values.push_back(std::to_string(tasks_entry.cashreward)); insert_values.push_back(std::to_string(tasks_entry.xpreward)); insert_values.push_back(std::to_string(tasks_entry.rewardmethod)); + insert_values.push_back(std::to_string(tasks_entry.reward_radiant_crystals)); + insert_values.push_back(std::to_string(tasks_entry.reward_ebon_crystals)); insert_values.push_back(std::to_string(tasks_entry.minlevel)); insert_values.push_back(std::to_string(tasks_entry.maxlevel)); + insert_values.push_back(std::to_string(tasks_entry.level_spread)); + insert_values.push_back(std::to_string(tasks_entry.min_players)); + insert_values.push_back(std::to_string(tasks_entry.max_players)); insert_values.push_back(std::to_string(tasks_entry.repeatable)); insert_values.push_back(std::to_string(tasks_entry.faction_reward)); insert_values.push_back("'" + EscapeString(tasks_entry.completion_emote) + "'"); + insert_values.push_back(std::to_string(tasks_entry.replay_timer_seconds)); + insert_values.push_back(std::to_string(tasks_entry.request_timer_seconds)); auto results = db.QueryDatabase( fmt::format( @@ -288,11 +365,18 @@ public: insert_values.push_back(std::to_string(tasks_entry.cashreward)); insert_values.push_back(std::to_string(tasks_entry.xpreward)); insert_values.push_back(std::to_string(tasks_entry.rewardmethod)); + insert_values.push_back(std::to_string(tasks_entry.reward_radiant_crystals)); + insert_values.push_back(std::to_string(tasks_entry.reward_ebon_crystals)); insert_values.push_back(std::to_string(tasks_entry.minlevel)); insert_values.push_back(std::to_string(tasks_entry.maxlevel)); + insert_values.push_back(std::to_string(tasks_entry.level_spread)); + insert_values.push_back(std::to_string(tasks_entry.min_players)); + insert_values.push_back(std::to_string(tasks_entry.max_players)); insert_values.push_back(std::to_string(tasks_entry.repeatable)); insert_values.push_back(std::to_string(tasks_entry.faction_reward)); insert_values.push_back("'" + EscapeString(tasks_entry.completion_emote) + "'"); + insert_values.push_back(std::to_string(tasks_entry.replay_timer_seconds)); + insert_values.push_back(std::to_string(tasks_entry.request_timer_seconds)); insert_chunks.push_back("(" + implode(",", insert_values) + ")"); } @@ -326,22 +410,29 @@ public: for (auto row = results.begin(); row != results.end(); ++row) { Tasks entry{}; - entry.id = atoi(row[0]); - entry.type = atoi(row[1]); - entry.duration = atoi(row[2]); - entry.duration_code = atoi(row[3]); - entry.title = row[4] ? row[4] : ""; - entry.description = row[5] ? row[5] : ""; - entry.reward = row[6] ? row[6] : ""; - entry.rewardid = atoi(row[7]); - entry.cashreward = atoi(row[8]); - entry.xpreward = atoi(row[9]); - entry.rewardmethod = atoi(row[10]); - entry.minlevel = atoi(row[11]); - entry.maxlevel = atoi(row[12]); - entry.repeatable = atoi(row[13]); - entry.faction_reward = atoi(row[14]); - entry.completion_emote = row[15] ? row[15] : ""; + entry.id = atoi(row[0]); + entry.type = atoi(row[1]); + entry.duration = atoi(row[2]); + entry.duration_code = atoi(row[3]); + entry.title = row[4] ? row[4] : ""; + entry.description = row[5] ? row[5] : ""; + entry.reward = row[6] ? row[6] : ""; + entry.rewardid = atoi(row[7]); + entry.cashreward = atoi(row[8]); + entry.xpreward = atoi(row[9]); + entry.rewardmethod = atoi(row[10]); + entry.reward_radiant_crystals = atoi(row[11]); + entry.reward_ebon_crystals = atoi(row[12]); + entry.minlevel = atoi(row[13]); + entry.maxlevel = atoi(row[14]); + entry.level_spread = atoi(row[15]); + entry.min_players = atoi(row[16]); + entry.max_players = atoi(row[17]); + entry.repeatable = atoi(row[18]); + entry.faction_reward = atoi(row[19]); + entry.completion_emote = row[20] ? row[20] : ""; + entry.replay_timer_seconds = atoi(row[21]); + entry.request_timer_seconds = atoi(row[22]); all_entries.push_back(entry); } @@ -366,22 +457,29 @@ public: for (auto row = results.begin(); row != results.end(); ++row) { Tasks entry{}; - entry.id = atoi(row[0]); - entry.type = atoi(row[1]); - entry.duration = atoi(row[2]); - entry.duration_code = atoi(row[3]); - entry.title = row[4] ? row[4] : ""; - entry.description = row[5] ? row[5] : ""; - entry.reward = row[6] ? row[6] : ""; - entry.rewardid = atoi(row[7]); - entry.cashreward = atoi(row[8]); - entry.xpreward = atoi(row[9]); - entry.rewardmethod = atoi(row[10]); - entry.minlevel = atoi(row[11]); - entry.maxlevel = atoi(row[12]); - entry.repeatable = atoi(row[13]); - entry.faction_reward = atoi(row[14]); - entry.completion_emote = row[15] ? row[15] : ""; + entry.id = atoi(row[0]); + entry.type = atoi(row[1]); + entry.duration = atoi(row[2]); + entry.duration_code = atoi(row[3]); + entry.title = row[4] ? row[4] : ""; + entry.description = row[5] ? row[5] : ""; + entry.reward = row[6] ? row[6] : ""; + entry.rewardid = atoi(row[7]); + entry.cashreward = atoi(row[8]); + entry.xpreward = atoi(row[9]); + entry.rewardmethod = atoi(row[10]); + entry.reward_radiant_crystals = atoi(row[11]); + entry.reward_ebon_crystals = atoi(row[12]); + entry.minlevel = atoi(row[13]); + entry.maxlevel = atoi(row[14]); + entry.level_spread = atoi(row[15]); + entry.min_players = atoi(row[16]); + entry.max_players = atoi(row[17]); + entry.repeatable = atoi(row[18]); + entry.faction_reward = atoi(row[19]); + entry.completion_emote = row[20] ? row[20] : ""; + entry.replay_timer_seconds = atoi(row[21]); + entry.request_timer_seconds = atoi(row[22]); all_entries.push_back(entry); } diff --git a/common/repositories/character_instance_safereturns_repository.h b/common/repositories/character_instance_safereturns_repository.h new file mode 100644 index 000000000..89bfddead --- /dev/null +++ b/common/repositories/character_instance_safereturns_repository.h @@ -0,0 +1,108 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) + * + * 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_CHARACTER_INSTANCE_SAFERETURNS_REPOSITORY_H +#define EQEMU_CHARACTER_INSTANCE_SAFERETURNS_REPOSITORY_H + +#include "../database.h" +#include "../string_util.h" +#include "base/base_character_instance_safereturns_repository.h" + +class CharacterInstanceSafereturnsRepository: public BaseCharacterInstanceSafereturnsRepository { +public: + + /** + * This file was auto generated and can be modified and extended upon + * + * Base repository methods are automatically + * generated in the "base" version of this repository. The base repository + * is immutable and to be left untouched, while methods in this class + * are used as extension methods for more specific persistence-layer + * accessors or mutators. + * + * Base Methods (Subject to be expanded upon in time) + * + * Note: Not all tables are designed appropriately to fit functionality with all base methods + * + * InsertOne + * UpdateOne + * DeleteOne + * FindOne + * GetWhere(std::string where_filter) + * DeleteWhere(std::string where_filter) + * InsertMany + * All + * + * Example custom methods in a repository + * + * CharacterInstanceSafereturnsRepository::GetByZoneAndVersion(int zone_id, int zone_version) + * CharacterInstanceSafereturnsRepository::GetWhereNeverExpires() + * CharacterInstanceSafereturnsRepository::GetWhereXAndY() + * CharacterInstanceSafereturnsRepository::DeleteWhereXAndY() + * + * Most of the above could be covered by base methods, but if you as a developer + * find yourself re-using logic for other parts of the code, its best to just make a + * method that can be re-used easily elsewhere especially if it can use a base repository + * method and encapsulate filters there + */ + + // Custom extended repository methods here + + static CharacterInstanceSafereturns InsertOneOrUpdate( + Database& db, CharacterInstanceSafereturns& character_instance_safereturns_entry) + { + std::vector insert_values; + + insert_values.push_back(std::to_string(character_instance_safereturns_entry.id)); + insert_values.push_back(std::to_string(character_instance_safereturns_entry.character_id)); + insert_values.push_back(std::to_string(character_instance_safereturns_entry.instance_zone_id)); + insert_values.push_back(std::to_string(character_instance_safereturns_entry.instance_id)); + insert_values.push_back(std::to_string(character_instance_safereturns_entry.safe_zone_id)); + insert_values.push_back(std::to_string(character_instance_safereturns_entry.safe_x)); + insert_values.push_back(std::to_string(character_instance_safereturns_entry.safe_y)); + insert_values.push_back(std::to_string(character_instance_safereturns_entry.safe_z)); + insert_values.push_back(std::to_string(character_instance_safereturns_entry.safe_heading)); + + auto results = db.QueryDatabase(fmt::format(SQL( + {} VALUES ({}) + ON DUPLICATE KEY UPDATE + instance_zone_id = VALUES(instance_zone_id), + instance_id = VALUES(instance_id), + safe_zone_id = VALUES(safe_zone_id), + safe_x = VALUES(safe_x), + safe_y = VALUES(safe_y), + safe_z = VALUES(safe_z), + safe_heading = VALUES(safe_heading) + ), + BaseInsert(), + implode(",", insert_values) + )); + + if (results.Success()) + { + character_instance_safereturns_entry.id = results.LastInsertedID(); + return character_instance_safereturns_entry; + } + + return NewEntity(); + } +}; + +#endif //EQEMU_CHARACTER_INSTANCE_SAFERETURNS_REPOSITORY_H diff --git a/common/repositories/character_task_timers_repository.h b/common/repositories/character_task_timers_repository.h new file mode 100644 index 000000000..7f0b07962 --- /dev/null +++ b/common/repositories/character_task_timers_repository.h @@ -0,0 +1,70 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) + * + * 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_CHARACTER_TASK_TIMERS_REPOSITORY_H +#define EQEMU_CHARACTER_TASK_TIMERS_REPOSITORY_H + +#include "../database.h" +#include "../string_util.h" +#include "base/base_character_task_timers_repository.h" + +class CharacterTaskTimersRepository: public BaseCharacterTaskTimersRepository { +public: + + /** + * This file was auto generated and can be modified and extended upon + * + * Base repository methods are automatically + * generated in the "base" version of this repository. The base repository + * is immutable and to be left untouched, while methods in this class + * are used as extension methods for more specific persistence-layer + * accessors or mutators. + * + * Base Methods (Subject to be expanded upon in time) + * + * Note: Not all tables are designed appropriately to fit functionality with all base methods + * + * InsertOne + * UpdateOne + * DeleteOne + * FindOne + * GetWhere(std::string where_filter) + * DeleteWhere(std::string where_filter) + * InsertMany + * All + * + * Example custom methods in a repository + * + * CharacterTaskTimersRepository::GetByZoneAndVersion(int zone_id, int zone_version) + * CharacterTaskTimersRepository::GetWhereNeverExpires() + * CharacterTaskTimersRepository::GetWhereXAndY() + * CharacterTaskTimersRepository::DeleteWhereXAndY() + * + * Most of the above could be covered by base methods, but if you as a developer + * find yourself re-using logic for other parts of the code, its best to just make a + * method that can be re-used easily elsewhere especially if it can use a base repository + * method and encapsulate filters there + */ + + // Custom extended repository methods here + +}; + +#endif //EQEMU_CHARACTER_TASK_TIMERS_REPOSITORY_H diff --git a/common/repositories/completed_shared_task_activity_state_repository.h b/common/repositories/completed_shared_task_activity_state_repository.h new file mode 100644 index 000000000..afb0ef3fd --- /dev/null +++ b/common/repositories/completed_shared_task_activity_state_repository.h @@ -0,0 +1,70 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) + * + * 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_COMPLETED_SHARED_TASK_ACTIVITY_STATE_REPOSITORY_H +#define EQEMU_COMPLETED_SHARED_TASK_ACTIVITY_STATE_REPOSITORY_H + +#include "../database.h" +#include "../string_util.h" +#include "base/base_completed_shared_task_activity_state_repository.h" + +class CompletedSharedTaskActivityStateRepository: public BaseCompletedSharedTaskActivityStateRepository { +public: + + /** + * This file was auto generated and can be modified and extended upon + * + * Base repository methods are automatically + * generated in the "base" version of this repository. The base repository + * is immutable and to be left untouched, while methods in this class + * are used as extension methods for more specific persistence-layer + * accessors or mutators. + * + * Base Methods (Subject to be expanded upon in time) + * + * Note: Not all tables are designed appropriately to fit functionality with all base methods + * + * InsertOne + * UpdateOne + * DeleteOne + * FindOne + * GetWhere(std::string where_filter) + * DeleteWhere(std::string where_filter) + * InsertMany + * All + * + * Example custom methods in a repository + * + * CompletedSharedTaskActivityStateRepository::GetByZoneAndVersion(int zone_id, int zone_version) + * CompletedSharedTaskActivityStateRepository::GetWhereNeverExpires() + * CompletedSharedTaskActivityStateRepository::GetWhereXAndY() + * CompletedSharedTaskActivityStateRepository::DeleteWhereXAndY() + * + * Most of the above could be covered by base methods, but if you as a developer + * find yourself re-using logic for other parts of the code, its best to just make a + * method that can be re-used easily elsewhere especially if it can use a base repository + * method and encapsulate filters there + */ + + // Custom extended repository methods here + +}; + +#endif //EQEMU_COMPLETED_SHARED_TASK_ACTIVITY_STATE_REPOSITORY_H diff --git a/common/repositories/completed_shared_task_members_repository.h b/common/repositories/completed_shared_task_members_repository.h new file mode 100644 index 000000000..6c0cf609a --- /dev/null +++ b/common/repositories/completed_shared_task_members_repository.h @@ -0,0 +1,70 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) + * + * 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_COMPLETED_SHARED_TASK_MEMBERS_REPOSITORY_H +#define EQEMU_COMPLETED_SHARED_TASK_MEMBERS_REPOSITORY_H + +#include "../database.h" +#include "../string_util.h" +#include "base/base_completed_shared_task_members_repository.h" + +class CompletedSharedTaskMembersRepository: public BaseCompletedSharedTaskMembersRepository { +public: + + /** + * This file was auto generated and can be modified and extended upon + * + * Base repository methods are automatically + * generated in the "base" version of this repository. The base repository + * is immutable and to be left untouched, while methods in this class + * are used as extension methods for more specific persistence-layer + * accessors or mutators. + * + * Base Methods (Subject to be expanded upon in time) + * + * Note: Not all tables are designed appropriately to fit functionality with all base methods + * + * InsertOne + * UpdateOne + * DeleteOne + * FindOne + * GetWhere(std::string where_filter) + * DeleteWhere(std::string where_filter) + * InsertMany + * All + * + * Example custom methods in a repository + * + * CompletedSharedTaskMembersRepository::GetByZoneAndVersion(int zone_id, int zone_version) + * CompletedSharedTaskMembersRepository::GetWhereNeverExpires() + * CompletedSharedTaskMembersRepository::GetWhereXAndY() + * CompletedSharedTaskMembersRepository::DeleteWhereXAndY() + * + * Most of the above could be covered by base methods, but if you as a developer + * find yourself re-using logic for other parts of the code, its best to just make a + * method that can be re-used easily elsewhere especially if it can use a base repository + * method and encapsulate filters there + */ + + // Custom extended repository methods here + +}; + +#endif //EQEMU_COMPLETED_SHARED_TASK_MEMBERS_REPOSITORY_H diff --git a/common/repositories/completed_shared_tasks_repository.h b/common/repositories/completed_shared_tasks_repository.h new file mode 100644 index 000000000..bbf46f16c --- /dev/null +++ b/common/repositories/completed_shared_tasks_repository.h @@ -0,0 +1,70 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) + * + * 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_COMPLETED_SHARED_TASKS_REPOSITORY_H +#define EQEMU_COMPLETED_SHARED_TASKS_REPOSITORY_H + +#include "../database.h" +#include "../string_util.h" +#include "base/base_completed_shared_tasks_repository.h" + +class CompletedSharedTasksRepository: public BaseCompletedSharedTasksRepository { +public: + + /** + * This file was auto generated and can be modified and extended upon + * + * Base repository methods are automatically + * generated in the "base" version of this repository. The base repository + * is immutable and to be left untouched, while methods in this class + * are used as extension methods for more specific persistence-layer + * accessors or mutators. + * + * Base Methods (Subject to be expanded upon in time) + * + * Note: Not all tables are designed appropriately to fit functionality with all base methods + * + * InsertOne + * UpdateOne + * DeleteOne + * FindOne + * GetWhere(std::string where_filter) + * DeleteWhere(std::string where_filter) + * InsertMany + * All + * + * Example custom methods in a repository + * + * CompletedSharedTasksRepository::GetByZoneAndVersion(int zone_id, int zone_version) + * CompletedSharedTasksRepository::GetWhereNeverExpires() + * CompletedSharedTasksRepository::GetWhereXAndY() + * CompletedSharedTasksRepository::DeleteWhereXAndY() + * + * Most of the above could be covered by base methods, but if you as a developer + * find yourself re-using logic for other parts of the code, its best to just make a + * method that can be re-used easily elsewhere especially if it can use a base repository + * method and encapsulate filters there + */ + + // Custom extended repository methods here + +}; + +#endif //EQEMU_COMPLETED_SHARED_TASKS_REPOSITORY_H diff --git a/common/repositories/dynamic_zone_members_repository.h b/common/repositories/dynamic_zone_members_repository.h index 2392b0965..3ccdd17b8 100644 --- a/common/repositories/dynamic_zone_members_repository.h +++ b/common/repositories/dynamic_zone_members_repository.h @@ -69,7 +69,6 @@ public: uint32_t id; uint32_t dynamic_zone_id; uint32_t character_id; - int is_current_member; std::string character_name; }; @@ -80,32 +79,17 @@ public: dynamic_zone_members.id, dynamic_zone_members.dynamic_zone_id, dynamic_zone_members.character_id, - dynamic_zone_members.is_current_member, character_data.name FROM dynamic_zone_members INNER JOIN character_data ON dynamic_zone_members.character_id = character_data.id )); } - static std::vector GetWithNames(Database& db, - const std::vector& dynamic_zone_ids) + static std::vector GetAllWithNames(Database& db) { - if (dynamic_zone_ids.empty()) - { - return {}; - } - std::vector all_entries; - auto results = db.QueryDatabase(fmt::format(SQL( - {} - WHERE dynamic_zone_members.dynamic_zone_id IN ({}) - AND dynamic_zone_members.is_current_member = TRUE; - ), - SelectMembersWithNames(), - fmt::join(dynamic_zone_ids, ",") - )); - + auto results = db.QueryDatabase(SelectMembersWithNames()); if (results.Success()) { all_entries.reserve(results.RowCount()); @@ -118,7 +102,6 @@ public: entry.id = strtoul(row[col++], nullptr, 10); entry.dynamic_zone_id = strtoul(row[col++], nullptr, 10); entry.character_id = strtoul(row[col++], nullptr, 10); - entry.is_current_member = strtoul(row[col++], nullptr, 10); entry.character_name = row[col++]; all_entries.emplace_back(std::move(entry)); @@ -173,7 +156,7 @@ public: (dynamic_zone_id, character_id) VALUES ({}, {}) - ON DUPLICATE KEY UPDATE is_current_member = TRUE; + ON DUPLICATE KEY UPDATE id = id; ), TableName(), dynamic_zone_id, @@ -184,7 +167,7 @@ public: static void RemoveMember(Database& db, uint32_t dynamic_zone_id, uint32_t character_id) { db.QueryDatabase(fmt::format(SQL( - UPDATE {} SET is_current_member = FALSE + DELETE FROM {} WHERE dynamic_zone_id = {} AND character_id = {}; ), TableName(), dynamic_zone_id, character_id @@ -194,7 +177,7 @@ public: static void RemoveAllMembers(Database& db, uint32_t dynamic_zone_id) { db.QueryDatabase(fmt::format(SQL( - UPDATE {} SET is_current_member = FALSE + DELETE FROM {} WHERE dynamic_zone_id = {}; ), TableName(), dynamic_zone_id @@ -206,7 +189,7 @@ public: if (!dynamic_zone_ids.empty()) { db.QueryDatabase(fmt::format(SQL( - UPDATE {} SET is_current_member = FALSE + DELETE FROM {} WHERE dynamic_zone_id IN ({}); ), TableName(), fmt::join(dynamic_zone_ids, ",") @@ -226,7 +209,6 @@ public: insert_values.push_back(std::to_string(dynamic_zone_members_entry.id)); insert_values.push_back(std::to_string(dynamic_zone_members_entry.dynamic_zone_id)); insert_values.push_back(std::to_string(dynamic_zone_members_entry.character_id)); - insert_values.push_back(std::to_string(dynamic_zone_members_entry.is_current_member)); insert_chunks.push_back("(" + implode(",", insert_values) + ")"); } @@ -235,7 +217,7 @@ public: auto results = db.QueryDatabase( fmt::format( - "INSERT INTO {} ({}) VALUES {} ON DUPLICATE KEY UPDATE is_current_member = TRUE;", + "INSERT INTO {} ({}) VALUES {} ON DUPLICATE KEY UPDATE id = id;", TableName(), ColumnsRaw(), implode(",", insert_chunks) diff --git a/common/repositories/dynamic_zones_repository.h b/common/repositories/dynamic_zones_repository.h index 24d944209..677fcc2f8 100644 --- a/common/repositories/dynamic_zones_repository.h +++ b/common/repositories/dynamic_zones_repository.h @@ -68,6 +68,11 @@ public: struct DynamicZoneInstance { uint32_t id; + std::string uuid; + std::string name; + int leader_id; + int min_players; + int max_players; int instance_id; int type; int compass_zone_id; @@ -97,6 +102,11 @@ public: return std::string(SQL( SELECT dynamic_zones.id, + dynamic_zones.uuid, + dynamic_zones.name, + dynamic_zones.leader_id, + dynamic_zones.min_players, + dynamic_zones.max_players, dynamic_zones.instance_id, dynamic_zones.type, dynamic_zones.compass_zone_id, @@ -130,6 +140,11 @@ public: int col = 0; entry.id = strtoul(row[col++], nullptr, 10); + entry.uuid = row[col++]; + entry.name = row[col++]; + entry.leader_id = strtol(row[col++], nullptr, 10); + entry.min_players = strtol(row[col++], nullptr, 10); + entry.max_players = strtol(row[col++], nullptr, 10); entry.instance_id = strtol(row[col++], nullptr, 10); entry.type = strtol(row[col++], nullptr, 10); entry.compass_zone_id = strtol(row[col++], nullptr, 10); @@ -157,21 +172,15 @@ public: return entry; } - static std::vector GetWithInstance(Database& db, - const std::vector& dynamic_zone_ids) + static std::vector AllWithInstanceNotExpired(Database& db) { - if (dynamic_zone_ids.empty()) - { - return {}; - } - std::vector all_entries; - auto results = db.QueryDatabase(fmt::format( - "{} WHERE dynamic_zones.id IN ({}) ORDER BY dynamic_zones.id;", - SelectDynamicZoneJoinInstance(), - fmt::join(dynamic_zone_ids, ",") - )); + auto results = db.QueryDatabase(fmt::format(SQL( + {} WHERE + (instance_list.start_time + instance_list.duration) > UNIX_TIMESTAMP() + AND instance_list.never_expires = 0 + ), SelectDynamicZoneJoinInstance())); if (results.Success()) { @@ -240,6 +249,18 @@ public: } } + static void UpdateLeaderID(Database& db, uint32_t dz_id, uint32_t leader_id) + { + if (dz_id != 0) + { + std::string query = fmt::format(SQL( + UPDATE {} SET leader_id = {} WHERE {} = {}; + ), TableName(), leader_id, PrimaryKey(), dz_id); + + db.QueryDatabase(query); + } + } + struct DynamicZoneInstancePlayerCount { uint32_t id; @@ -267,7 +288,6 @@ public: FROM dynamic_zones INNER JOIN instance_list ON dynamic_zones.instance_id = instance_list.id LEFT JOIN dynamic_zone_members ON dynamic_zones.id = dynamic_zone_members.dynamic_zone_id - AND dynamic_zone_members.is_current_member = TRUE GROUP BY instance_list.id ORDER BY dynamic_zones.id; )); @@ -301,6 +321,41 @@ public: } return all_entries; } + + static std::vector GetStaleIDs(Database& db) + { + std::vector all_entries; + + // dzs with no members, missing instance, or expired instance + auto results = db.QueryDatabase(SQL( + SELECT + dynamic_zones.id + FROM dynamic_zones + LEFT JOIN instance_list ON dynamic_zones.instance_id = instance_list.id + LEFT JOIN + ( + SELECT dynamic_zone_id, COUNT(*) member_count + FROM dynamic_zone_members + GROUP BY dynamic_zone_id + ) dynamic_zone_members + ON dynamic_zone_members.dynamic_zone_id = dynamic_zones.id + WHERE + instance_list.id IS NULL + OR dynamic_zone_members.member_count IS NULL + OR dynamic_zone_members.member_count = 0 + OR ((instance_list.start_time + instance_list.duration) <= UNIX_TIMESTAMP() + AND instance_list.never_expires = 0); + )); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) + { + all_entries.push_back(atoi(row[0])); + } + + return all_entries; + } }; #endif //EQEMU_DYNAMIC_ZONES_REPOSITORY_H diff --git a/common/repositories/expeditions_repository.h b/common/repositories/expeditions_repository.h index 654b676b0..1470cbad0 100644 --- a/common/repositories/expeditions_repository.h +++ b/common/repositories/expeditions_repository.h @@ -65,100 +65,6 @@ public: // Custom extended repository methods here - struct ExpeditionWithLeader - { - uint32_t id; - std::string uuid; - uint32_t dynamic_zone_id; - std::string expedition_name; - uint32_t min_players; - uint32_t max_players; - int add_replay_on_join; - int is_locked; - uint32_t leader_id; - std::string leader_name; - }; - - static std::string SelectExpeditionsJoinLeader() - { - return std::string(SQL( - SELECT - expeditions.id, - expeditions.uuid, - expeditions.dynamic_zone_id, - expeditions.expedition_name, - expeditions.min_players, - expeditions.max_players, - expeditions.add_replay_on_join, - expeditions.is_locked, - expeditions.leader_id, - character_data.name leader_name - FROM expeditions - INNER JOIN character_data ON expeditions.leader_id = character_data.id - )); - } - - static ExpeditionWithLeader FillExpeditionWithLeaderFromRow(MySQLRequestRow& row) - { - ExpeditionWithLeader entry{}; - - int col = 0; - entry.id = strtoul(row[col++], nullptr, 10); - entry.uuid = row[col++]; - entry.dynamic_zone_id = strtoul(row[col++], nullptr, 10); - entry.expedition_name = row[col++]; - entry.min_players = strtoul(row[col++], nullptr, 10); - entry.max_players = strtoul(row[col++], nullptr, 10); - entry.add_replay_on_join = strtoul(row[col++], nullptr, 10); - entry.is_locked = strtoul(row[col++], nullptr, 10); - entry.leader_id = strtoul(row[col++], nullptr, 10); - entry.leader_name = row[col++]; - - return entry; - } - - static std::vector GetAllWithLeaderName(Database& db) - { - std::vector all_entries; - - auto results = db.QueryDatabase(fmt::format( - "{} ORDER BY expeditions.id;", - SelectExpeditionsJoinLeader() - )); - - if (results.Success()) - { - all_entries.reserve(results.RowCount()); - - for (auto row = results.begin(); row != results.end(); ++row) - { - ExpeditionWithLeader entry = FillExpeditionWithLeaderFromRow(row); - all_entries.emplace_back(std::move(entry)); - } - } - - return all_entries; - } - - static ExpeditionWithLeader GetWithLeaderName(Database& db, uint32_t expedition_id) - { - ExpeditionWithLeader entry{}; - - auto results = db.QueryDatabase(fmt::format( - "{} WHERE expeditions.id = {};", - SelectExpeditionsJoinLeader(), - expedition_id - )); - - if (results.Success() && results.RowCount() > 0) - { - auto row = results.begin(); - entry = FillExpeditionWithLeaderFromRow(row); - } - - return entry; - } - struct CharacterExpedition { uint32_t id; @@ -186,7 +92,6 @@ public: FROM character_data LEFT JOIN dynamic_zone_members ON character_data.id = dynamic_zone_members.character_id - AND dynamic_zone_members.is_current_member = TRUE LEFT JOIN expeditions ON dynamic_zone_members.dynamic_zone_id = expeditions.dynamic_zone_id WHERE character_data.name IN ({}) @@ -232,7 +137,6 @@ public: ON expeditions.dynamic_zone_id = dynamic_zone_members.dynamic_zone_id WHERE dynamic_zone_members.character_id = {} - AND dynamic_zone_members.is_current_member = TRUE; ), character_id )); diff --git a/common/repositories/shared_task_activity_state_repository.h b/common/repositories/shared_task_activity_state_repository.h new file mode 100644 index 000000000..1e48c7e76 --- /dev/null +++ b/common/repositories/shared_task_activity_state_repository.h @@ -0,0 +1,70 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) + * + * 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_SHARED_TASK_ACTIVITY_STATE_REPOSITORY_H +#define EQEMU_SHARED_TASK_ACTIVITY_STATE_REPOSITORY_H + +#include "../database.h" +#include "../string_util.h" +#include "base/base_shared_task_activity_state_repository.h" + +class SharedTaskActivityStateRepository: public BaseSharedTaskActivityStateRepository { +public: + + /** + * This file was auto generated and can be modified and extended upon + * + * Base repository methods are automatically + * generated in the "base" version of this repository. The base repository + * is immutable and to be left untouched, while methods in this class + * are used as extension methods for more specific persistence-layer + * accessors or mutators. + * + * Base Methods (Subject to be expanded upon in time) + * + * Note: Not all tables are designed appropriately to fit functionality with all base methods + * + * InsertOne + * UpdateOne + * DeleteOne + * FindOne + * GetWhere(std::string where_filter) + * DeleteWhere(std::string where_filter) + * InsertMany + * All + * + * Example custom methods in a repository + * + * SharedTaskActivityStateRepository::GetByZoneAndVersion(int zone_id, int zone_version) + * SharedTaskActivityStateRepository::GetWhereNeverExpires() + * SharedTaskActivityStateRepository::GetWhereXAndY() + * SharedTaskActivityStateRepository::DeleteWhereXAndY() + * + * Most of the above could be covered by base methods, but if you as a developer + * find yourself re-using logic for other parts of the code, its best to just make a + * method that can be re-used easily elsewhere especially if it can use a base repository + * method and encapsulate filters there + */ + + // Custom extended repository methods here + +}; + +#endif //EQEMU_SHARED_TASK_ACTIVITY_STATE_REPOSITORY_H diff --git a/common/repositories/shared_task_dynamic_zones_repository.h b/common/repositories/shared_task_dynamic_zones_repository.h new file mode 100644 index 000000000..61ee9c9f5 --- /dev/null +++ b/common/repositories/shared_task_dynamic_zones_repository.h @@ -0,0 +1,70 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) + * + * 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_SHARED_TASK_DYNAMIC_ZONES_REPOSITORY_H +#define EQEMU_SHARED_TASK_DYNAMIC_ZONES_REPOSITORY_H + +#include "../database.h" +#include "../string_util.h" +#include "base/base_shared_task_dynamic_zones_repository.h" + +class SharedTaskDynamicZonesRepository: public BaseSharedTaskDynamicZonesRepository { +public: + + /** + * This file was auto generated and can be modified and extended upon + * + * Base repository methods are automatically + * generated in the "base" version of this repository. The base repository + * is immutable and to be left untouched, while methods in this class + * are used as extension methods for more specific persistence-layer + * accessors or mutators. + * + * Base Methods (Subject to be expanded upon in time) + * + * Note: Not all tables are designed appropriately to fit functionality with all base methods + * + * InsertOne + * UpdateOne + * DeleteOne + * FindOne + * GetWhere(std::string where_filter) + * DeleteWhere(std::string where_filter) + * InsertMany + * All + * + * Example custom methods in a repository + * + * SharedTaskDynamicZonesRepository::GetByZoneAndVersion(int zone_id, int zone_version) + * SharedTaskDynamicZonesRepository::GetWhereNeverExpires() + * SharedTaskDynamicZonesRepository::GetWhereXAndY() + * SharedTaskDynamicZonesRepository::DeleteWhereXAndY() + * + * Most of the above could be covered by base methods, but if you as a developer + * find yourself re-using logic for other parts of the code, its best to just make a + * method that can be re-used easily elsewhere especially if it can use a base repository + * method and encapsulate filters there + */ + + // Custom extended repository methods here + +}; + +#endif //EQEMU_SHARED_TASK_DYNAMIC_ZONES_REPOSITORY_H diff --git a/common/repositories/shared_task_members_repository.h b/common/repositories/shared_task_members_repository.h new file mode 100644 index 000000000..51b8606ad --- /dev/null +++ b/common/repositories/shared_task_members_repository.h @@ -0,0 +1,70 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) + * + * 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_SHARED_TASK_MEMBERS_REPOSITORY_H +#define EQEMU_SHARED_TASK_MEMBERS_REPOSITORY_H + +#include "../database.h" +#include "../string_util.h" +#include "base/base_shared_task_members_repository.h" + +class SharedTaskMembersRepository: public BaseSharedTaskMembersRepository { +public: + + /** + * This file was auto generated and can be modified and extended upon + * + * Base repository methods are automatically + * generated in the "base" version of this repository. The base repository + * is immutable and to be left untouched, while methods in this class + * are used as extension methods for more specific persistence-layer + * accessors or mutators. + * + * Base Methods (Subject to be expanded upon in time) + * + * Note: Not all tables are designed appropriately to fit functionality with all base methods + * + * InsertOne + * UpdateOne + * DeleteOne + * FindOne + * GetWhere(std::string where_filter) + * DeleteWhere(std::string where_filter) + * InsertMany + * All + * + * Example custom methods in a repository + * + * SharedTaskMembersRepository::GetByZoneAndVersion(int zone_id, int zone_version) + * SharedTaskMembersRepository::GetWhereNeverExpires() + * SharedTaskMembersRepository::GetWhereXAndY() + * SharedTaskMembersRepository::DeleteWhereXAndY() + * + * Most of the above could be covered by base methods, but if you as a developer + * find yourself re-using logic for other parts of the code, its best to just make a + * method that can be re-used easily elsewhere especially if it can use a base repository + * method and encapsulate filters there + */ + + // Custom extended repository methods here + +}; + +#endif //EQEMU_SHARED_TASK_MEMBERS_REPOSITORY_H diff --git a/common/repositories/shared_tasks_repository.h b/common/repositories/shared_tasks_repository.h new file mode 100644 index 000000000..3ec764beb --- /dev/null +++ b/common/repositories/shared_tasks_repository.h @@ -0,0 +1,70 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) + * + * 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_SHARED_TASKS_REPOSITORY_H +#define EQEMU_SHARED_TASKS_REPOSITORY_H + +#include "../database.h" +#include "../string_util.h" +#include "base/base_shared_tasks_repository.h" + +class SharedTasksRepository: public BaseSharedTasksRepository { +public: + + /** + * This file was auto generated and can be modified and extended upon + * + * Base repository methods are automatically + * generated in the "base" version of this repository. The base repository + * is immutable and to be left untouched, while methods in this class + * are used as extension methods for more specific persistence-layer + * accessors or mutators. + * + * Base Methods (Subject to be expanded upon in time) + * + * Note: Not all tables are designed appropriately to fit functionality with all base methods + * + * InsertOne + * UpdateOne + * DeleteOne + * FindOne + * GetWhere(std::string where_filter) + * DeleteWhere(std::string where_filter) + * InsertMany + * All + * + * Example custom methods in a repository + * + * SharedTasksRepository::GetByZoneAndVersion(int zone_id, int zone_version) + * SharedTasksRepository::GetWhereNeverExpires() + * SharedTasksRepository::GetWhereXAndY() + * SharedTasksRepository::DeleteWhereXAndY() + * + * Most of the above could be covered by base methods, but if you as a developer + * find yourself re-using logic for other parts of the code, its best to just make a + * method that can be re-used easily elsewhere especially if it can use a base repository + * method and encapsulate filters there + */ + + // Custom extended repository methods here + +}; + +#endif //EQEMU_SHARED_TASKS_REPOSITORY_H diff --git a/common/ruletypes.h b/common/ruletypes.h index 906b9d867..37bf96711 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -538,6 +538,7 @@ RULE_BOOL(TaskSystem, RecordCompletedTasks, true, "Record completed tasks") RULE_BOOL(TaskSystem, RecordCompletedOptionalActivities, false, "Record completed optional activities") RULE_BOOL(TaskSystem, KeepOneRecordPerCompletedTask, true, "Keep only one record per completed task") RULE_BOOL(TaskSystem, EnableTaskProximity, true, "Enable task proximity system") +RULE_INT(TaskSystem, RequestCooldownTimerSeconds, 15, "Seconds between allowing characters to request tasks (live-like default: 15 seconds)") RULE_CATEGORY_END() RULE_CATEGORY(Range) diff --git a/common/servertalk.h b/common/servertalk.h index b4a5000ad..3774a07cc 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -6,7 +6,10 @@ #include "../common/eq_packet_structs.h" #include "../common/net/packet.h" #include +#include +#include #include +#include #define SERVER_TIMEOUT 45000 // how often keepalive gets sent #define INTERSERVER_TIMER 10000 @@ -141,14 +144,9 @@ #define ServerOP_LFPMatches 0x0214 #define ServerOP_ClientVersionSummary 0x0215 +// expedition #define ServerOP_ExpeditionCreate 0x0400 -#define ServerOP_ExpeditionDeleted 0x0401 -#define ServerOP_ExpeditionLeaderChanged 0x0402 #define ServerOP_ExpeditionLockout 0x0403 -#define ServerOP_ExpeditionMemberChange 0x0404 -#define ServerOP_ExpeditionMemberSwap 0x0405 -#define ServerOP_ExpeditionMemberStatus 0x0406 -#define ServerOP_ExpeditionGetMemberStatuses 0x0407 #define ServerOP_ExpeditionDzAddPlayer 0x0408 #define ServerOP_ExpeditionDzMakeLeader 0x0409 #define ServerOP_ExpeditionCharacterLockout 0x040d @@ -156,17 +154,23 @@ #define ServerOP_ExpeditionRequestInvite 0x040f #define ServerOP_ExpeditionReplayOnJoin 0x0410 #define ServerOP_ExpeditionLockState 0x0411 -#define ServerOP_ExpeditionMembersRemoved 0x0412 #define ServerOP_ExpeditionLockoutDuration 0x0414 -#define ServerOP_ExpeditionExpireWarning 0x0416 -#define ServerOP_DzAddRemoveCharacter 0x0450 -#define ServerOP_DzRemoveAllCharacters 0x0451 +// dz +#define ServerOP_DzAddRemoveMember 0x0450 +#define ServerOP_DzRemoveAllMembers 0x0451 #define ServerOP_DzSetSecondsRemaining 0x0452 #define ServerOP_DzDurationUpdate 0x0453 #define ServerOP_DzSetCompass 0x0454 #define ServerOP_DzSetSafeReturn 0x0455 #define ServerOP_DzSetZoneIn 0x0456 +#define ServerOP_DzSwapMembers 0x0457 +#define ServerOP_DzGetMemberStatuses 0x0458 +#define ServerOP_DzUpdateMemberStatus 0x0459 +#define ServerOP_DzLeaderChanged 0x045a +#define ServerOP_DzExpireWarning 0x045b +#define ServerOP_DzCreated 0x045c +#define ServerOP_DzDeleted 0x045d #define ServerOP_LSInfo 0x1000 #define ServerOP_LSStatus 0x1001 @@ -395,7 +399,10 @@ public: } void WriteUInt8(uint8 value) { *(uint8 *)(pBuffer + _wpos) = value; _wpos += sizeof(uint8); } + void WriteInt8(uint8_t value) { *(uint8_t *)(pBuffer + _wpos) = value; _wpos += sizeof(uint8_t); } void WriteUInt32(uint32 value) { *(uint32 *)(pBuffer + _wpos) = value; _wpos += sizeof(uint32); } + void WriteInt32(int32_t value) { *(int32_t *)(pBuffer + _wpos) = value; _wpos += sizeof(int32_t); } + void WriteString(const char * str) { uint32 len = static_cast(strlen(str)) + 1; memcpy(pBuffer + _wpos, str, len); _wpos += len; } uint8 ReadUInt8() { uint8 value = *(uint8 *)(pBuffer + _rpos); _rpos += sizeof(uint8); return value; } @@ -2025,47 +2032,28 @@ struct ServerExpeditionID_Struct { uint32 sender_instance_id; }; -struct ServerExpeditionLeaderID_Struct { - uint32 expedition_id; +struct ServerDzLeaderID_Struct { + uint32 dz_id; uint32 leader_id; }; -struct ServerExpeditionMemberChange_Struct { - uint32 expedition_id; - uint32 sender_zone_id; - uint16 sender_instance_id; - uint8 removed; // 0: added, 1: removed - uint32 char_id; - char char_name[64]; -}; - -struct ServerExpeditionMemberSwap_Struct { - uint32 expedition_id; - uint32 sender_zone_id; - uint16 sender_instance_id; - uint32 add_char_id; - uint32 remove_char_id; - char add_char_name[64]; - char remove_char_name[64]; -}; - -struct ServerExpeditionMemberStatus_Struct { - uint32 expedition_id; +struct ServerDzMemberStatus_Struct { + uint32 dz_id; uint32 sender_zone_id; uint16 sender_instance_id; uint8 status; // 0: unknown 1: Online 2: Offline 3: In Dynamic Zone 4: Link Dead uint32 character_id; }; -struct ServerExpeditionMemberStatusEntry_Struct { +struct ServerDzMemberStatusEntry_Struct { uint32 character_id; uint8 online_status; // 0: unknown 1: Online 2: Offline 3: In Dynamic Zone 4: Link Dead }; -struct ServerExpeditionMemberStatuses_Struct { - uint32 expedition_id; +struct ServerDzMemberStatuses_Struct { + uint32 dz_id; uint32 count; - ServerExpeditionMemberStatusEntry_Struct entries[0]; + ServerDzMemberStatusEntry_Struct entries[0]; }; struct ServerExpeditionLockout_Struct { @@ -2109,8 +2097,8 @@ struct ServerExpeditionCharacterID_Struct { uint32_t character_id; }; -struct ServerExpeditionExpireWarning_Struct { - uint32_t expedition_id; +struct ServerDzExpireWarning_Struct { + uint32_t dz_id; uint32_t minutes_remaining; }; @@ -2123,13 +2111,21 @@ struct ServerDzCommand_Struct { }; struct ServerDzCommandMakeLeader_Struct { - uint32 expedition_id; + uint32 dz_id; uint32 requester_id; uint8 is_online; // set by world, 0: new leader name offline, 1: online uint8 is_success; // set by world, 0: makeleader failed, 1: success (is online member) char new_leader_name[64]; }; +struct ServerDzID_Struct { + uint32 dz_id; + uint16 dz_zone_id; + uint16 dz_instance_id; // for cache-independent redundancy (messages to dz's instance) + uint32 sender_zone_id; + uint16 sender_instance_id; +}; + struct ServerDzLocation_Struct { uint32 dz_id; uint32 sender_zone_id; @@ -2141,11 +2137,29 @@ struct ServerDzLocation_Struct { float heading; }; -struct ServerDzCharacter_Struct { - uint16 zone_id; - uint16 instance_id; - uint8 remove; // 0: added 1: removed +struct ServerDzMember_Struct { + uint32 dz_id; + uint16 dz_zone_id; + uint16 dz_instance_id; // for cache redundancy + uint16 sender_zone_id; + uint16 sender_instance_id; + uint8 removed; // 0: added, 1: removed uint32 character_id; + uint8 character_status; // 0: unknown 1: Online 2: Offline 3: In Dynamic Zone 4: Link Dead + char character_name[64]; +}; + +struct ServerDzMemberSwap_Struct { + uint32 dz_id; + uint16 dz_zone_id; + uint16 dz_instance_id; // for cache redundancy + uint16 sender_zone_id; + uint16 sender_instance_id; + uint32 add_character_id; + uint32 remove_character_id; + uint8 add_character_status; + char add_character_name[64]; + char remove_character_name[64]; }; struct ServerDzSetDuration_Struct { @@ -2153,6 +2167,13 @@ struct ServerDzSetDuration_Struct { uint32 seconds; }; +struct ServerDzCreateSerialized_Struct { + uint16_t origin_zone_id; + uint16_t origin_instance_id; + uint32_t cereal_size; + char cereal_data[0]; +}; + #pragma pack() #endif diff --git a/common/shared_tasks.cpp b/common/shared_tasks.cpp new file mode 100644 index 000000000..18ffbfe78 --- /dev/null +++ b/common/shared_tasks.cpp @@ -0,0 +1,139 @@ +#include "shared_tasks.h" +#include "repositories/character_data_repository.h" +#include + +std::vector SharedTask::GetActivityState() const +{ + return m_shared_task_activity_state; +} + +std::vector SharedTask::GetMembers() const +{ + return m_members; +} + +void SharedTask::SetSharedTaskActivityState(const std::vector &activity_state) +{ + SharedTask::m_shared_task_activity_state = activity_state; +} + +void SharedTask::SetTaskData(const TasksRepository::Tasks &task_data) +{ + SharedTask::m_task_data = task_data; +} + +void SharedTask::SetTaskActivityData(const std::vector &task_activity_data) +{ + SharedTask::m_task_activity_data = task_activity_data; +} + +const TasksRepository::Tasks &SharedTask::GetTaskData() const +{ + return m_task_data; +} + +const std::vector &SharedTask::GetTaskActivityData() const +{ + return m_task_activity_data; +} + +void SharedTask::SetMembers(const std::vector &members) +{ + SharedTask::m_members = members; +} + +const SharedTasksRepository::SharedTasks &SharedTask::GetDbSharedTask() const +{ + return m_db_shared_task; +} + +void SharedTask::SetDbSharedTask(const SharedTasksRepository::SharedTasks &m_db_shared_task) +{ + SharedTask::m_db_shared_task = m_db_shared_task; +} + +SharedTaskRequestCharacters SharedTask::GetRequestCharacters(Database &db, uint32_t requested_character_id) +{ + SharedTaskRequestCharacters request{}; + + request.group_type = SharedTaskRequestGroupType::Group; + request.characters = CharacterDataRepository::GetWhere( + db, fmt::format( + "id IN (select charid from group_id where groupid = (select groupid from group_id where charid = {}))", + requested_character_id + ) + ); + + if (request.characters.empty()) { + request.group_type = SharedTaskRequestGroupType::Raid; + request.characters = CharacterDataRepository::GetWhere( + db, fmt::format( + "id IN (select charid from raid_members where raidid = (select raidid from raid_members where charid = {}))", + requested_character_id + ) + ); + } + + if (request.characters.empty()) // solo request + { + request.group_type = SharedTaskRequestGroupType::Solo; + request.characters = CharacterDataRepository::GetWhere( + db, fmt::format( + "id = {} LIMIT 1", + requested_character_id + ) + ); + } + + request.lowest_level = std::numeric_limits::max(); + request.highest_level = 0; + for (const auto &character: request.characters) { + request.lowest_level = std::min(request.lowest_level, character.level); + request.highest_level = std::max(request.highest_level, character.level); + request.character_ids.emplace_back(character.id); // convenience + } + + return request; +} + +void SharedTask::AddCharacterToMemberHistory(uint32_t character_id) +{ + auto it = std::find(member_id_history.begin(), member_id_history.end(), character_id); + if (it == member_id_history.end()) { + member_id_history.emplace_back(character_id); + } +} + +SharedTaskMember SharedTask::FindMemberFromCharacterID(uint32_t character_id) const +{ + auto it = std::find_if( + m_members.begin(), m_members.end(), + [&](const SharedTaskMember &member) { + return member.character_id == character_id; + } + ); + + return it != m_members.end() ? *it : SharedTaskMember{}; +} + +SharedTaskMember SharedTask::FindMemberFromCharacterName(const std::string &character_name) const +{ + auto it = std::find_if( + m_members.begin(), m_members.end(), + [&](const SharedTaskMember &member) { + return strcasecmp(member.character_name.c_str(), character_name.c_str()) == 0; + } + ); + + return it != m_members.end() ? *it : SharedTaskMember{}; +} + +SharedTaskMember SharedTask::GetLeader() const +{ + for (const auto &member : m_members) { + if (member.is_leader) { + return member; + } + } + return {}; +} diff --git a/common/shared_tasks.h b/common/shared_tasks.h new file mode 100644 index 000000000..2a2363ffd --- /dev/null +++ b/common/shared_tasks.h @@ -0,0 +1,204 @@ +#ifndef EQEMU_SHARED_TASKS_H +#define EQEMU_SHARED_TASKS_H + +#include "database.h" +#include "types.h" +#include "repositories/character_data_repository.h" +#include "repositories/tasks_repository.h" +#include "repositories/task_activities_repository.h" +#include "repositories/shared_tasks_repository.h" +#include +#include +#include + +// ops +#define ServerOP_SharedTaskRequest 0x0300 // zone -> world. Player trying to get task. Relayed world -> zone on confirmation +#define ServerOP_SharedTaskAddPlayer 0x0301 // bidirectional. /taskaddplayer request zone -> world. success world -> zone +#define ServerOP_SharedTaskMakeLeader 0x0302 // zone -> world -> zone +#define ServerOP_SharedTaskRemovePlayer 0x0303 // zone -> world -> zone +#define ServerOP_SharedTaskAttemptRemove 0x0304 // zone -> world. Player trying to delete task +#define ServerOP_SharedTaskUpdate 0x0305 // zone -> world. Client sending task update to world. Relayed world -> zone on confirmation +#define ServerOP_SharedTaskMemberlist 0x0306 // world -> zone. Send shared task memberlist +#define ServerOP_SharedTaskRequestMemberlist 0x0307 // zone -> world. Send shared task memberlist (zone in initial for now, could change) +#define ServerOP_SharedTaskAcceptNewTask 0x0308 // world -> zone. World verified, continue AcceptNewTask +#define ServerOP_SharedTaskInvitePlayer 0x0309 // world -> zone. Sends task invite to player +#define ServerOP_SharedTaskInviteAcceptedPlayer 0x0310 // zone -> world. Confirming task invite +#define ServerOP_SharedTaskCreateDynamicZone 0x0311 // zone -> world +#define ServerOP_SharedTaskPurgeAllCommand 0x0312 // zone -> world +#define ServerOP_SharedTaskPlayerList 0x0313 // zone -> world /taskplayerlist command +#define ServerOP_SharedTaskMemberChange 0x0314 // world -> zone. Send shared task single member added/removed (client also handles message) +#define ServerOP_SharedTaskKickPlayers 0x0315 // zone -> world /kickplayers task + +enum class SharedTaskRequestGroupType { + Solo = 0, + Group, + Raid +}; + +// used in +// ServerOP_SharedTaskRequest + +// ServerOP_SharedTaskAcceptNewTask +struct ServerSharedTaskRequest_Struct { + uint32 requested_character_id; + uint32 requested_task_id; + uint32 requested_npc_type_id; // original task logic passthrough + uint32 accept_time; +}; + +// ServerOP_SharedTaskInvitePlayer +struct ServerSharedTaskInvitePlayer_Struct { + uint32 requested_character_id; + uint32 invite_shared_task_id; + char task_name[64]; + char inviter_name[64]; +}; + +// ServerOP_SharedTaskAttemptRemove +// gets re-used when sent back to clients +struct ServerSharedTaskAttemptRemove_Struct { + uint32 requested_character_id; + uint32 requested_task_id; + bool remove_from_db; +}; + +// used in the shared task request process (currently) +struct SharedTaskMember { + uint32 character_id = 0; + std::string character_name; + bool is_leader = false; + + template + void serialize(Archive& archive) + { + archive(character_id, character_name, is_leader); + } +}; + +// used in shared task requests to validate group/raid members +struct SharedTaskRequestCharacters { + int lowest_level; + int highest_level; + SharedTaskRequestGroupType group_type; + std::vector character_ids; + std::vector characters; +}; + +// ServerOP_SharedTaskMemberlist +// builds the buffer and sends to clients directly +struct ServerSharedTaskMemberListPacket_Struct { + uint32 destination_character_id; + uint32 cereal_size; + char cereal_serialized_members[0]; // serialized member list using cereal +}; + +struct ServerSharedTaskMemberChangePacket_Struct { + uint32 destination_character_id; + uint32 shared_task_id; + bool removed; + char player_name[64]; +}; + +struct SharedTaskActivityStateEntry { + uint32 activity_id; + uint32 done_count; + uint32 max_done_count; // goalcount + uint32 updated_time; + uint32 completed_time; +}; + +struct ServerSharedTaskActivityUpdate_Struct { + uint32 source_character_id; + uint32 task_id; + uint32 activity_id; + uint32 done_count; + bool ignore_quest_update; +}; + +struct ServerSharedTaskRequestMemberlist_Struct { + uint32 source_character_id; + uint32 task_id; +}; + +struct ServerSharedTaskRemovePlayer_Struct { + uint32 source_character_id; + uint32 task_id; + char player_name[64]; +}; + +struct ServerSharedTaskAddPlayer_Struct { + uint32 source_character_id; + uint32 task_id; + char player_name[64]; +}; + +struct ServerSharedTaskMakeLeader_Struct { + uint32 source_character_id; + uint32 task_id; + char player_name[64]; +}; + +struct ServerSharedTaskInviteAccepted_Struct { + uint32 source_character_id; + uint32 shared_task_id; + bool accepted; + char player_name[64]; +}; + +struct ServerSharedTaskCreateDynamicZone_Struct { + uint32 source_character_id; + uint32 task_id; + uint32 cereal_size; + char cereal_data[0]; // serialized dz with creation parameters +}; + +struct ServerSharedTaskPlayerList_Struct { + uint32 source_character_id; + uint32 task_id; +}; + +struct ServerSharedTaskKickPlayers_Struct { + uint32 source_character_id; + uint32 task_id; +}; + +class SharedTask { +public: + // used in both zone and world validation + static SharedTaskRequestCharacters GetRequestCharacters(Database& db, uint32_t requested_character_id); + + void AddCharacterToMemberHistory(uint32_t character_id); + SharedTaskMember FindMemberFromCharacterID(uint32_t character_id) const; + SharedTaskMember FindMemberFromCharacterName(const std::string& character_name) const; + SharedTaskMember GetLeader() const; + std::vector GetActivityState() const; + std::vector GetMembers() const; + + // getters + const std::vector &GetTaskActivityData() const; + const TasksRepository::Tasks &GetTaskData() const; + + // setters + void SetMembers(const std::vector &members); + void SetSharedTaskActivityState(const std::vector &activity_state); + void SetTaskActivityData(const std::vector &task_activity_data); + void SetTaskData(const TasksRepository::Tasks &task_data); + + // active record of database shared task + const SharedTasksRepository::SharedTasks &GetDbSharedTask() const; + void SetDbSharedTask(const SharedTasksRepository::SharedTasks &m_db_shared_task); + + std::vector m_shared_task_activity_state; + std::vector m_members; + std::vector member_id_history; // past and present members for replay timers + std::vector dynamic_zone_ids; + +protected: + SharedTasksRepository::SharedTasks m_db_shared_task; + + // reference to task data (only for this shared task) + TasksRepository::Tasks m_task_data; + std::vector m_task_activity_data; +}; + +#endif //EQEMU_SHARED_TASKS_H diff --git a/common/tasks.h b/common/tasks.h new file mode 100644 index 000000000..8d19910e7 --- /dev/null +++ b/common/tasks.h @@ -0,0 +1,445 @@ +#ifndef EQEMU_TASKS_H +#define EQEMU_TASKS_H + +#include "serialize_buffer.h" + +#define MAXTASKS 10000 +#define MAXTASKSETS 1000 +#define MAXACTIVEQUESTS 19 // The Client has a hard cap of 19 active quests, 29 in SoD+ +#define MAXCHOOSERENTRIES 40 // The Max Chooser (Task Selector entries) is capped at 40 in the Titanium Client. +#define MAXACTIVITIESPERTASK 20 // The Client has a hard cap of 20 activities per task. +#define TASKSLOTEMPTY 0 // This is used to determine if a client's active task slot is empty. + +#define TASKSLOTTASK 0 +#define TASKSLOTSHAREDTASK 0 + +// Command Codes for worldserver ServerOP_ReloadTasks +#define RELOADTASKS 0 +#define RELOADTASKGOALLISTS 1 +#define RELOADTASKPROXIMITIES 2 +#define RELOADTASKSETS 3 + +typedef enum { + METHODSINGLEID = 0, + METHODLIST = 1, + METHODQUEST = 2 +} TaskMethodType; + +enum class TaskActivityType : int32_t // task element/objective +{ + Unknown = -1, // hidden + None = 0, + Deliver = 1, + Kill = 2, + Loot = 3, + SpeakWith = 4, + Explore = 5, + TradeSkill = 6, + Fish = 7, + Forage = 8, + CastOn = 9, + SkillOn = 10, + Touch = 11, + Collect = 13, + GiveCash = 100 +}; + +enum class TaskTimerType +{ + Replay = 0, + Request +}; + +struct ActivityInformation { + int step_number; + TaskActivityType activity_type; + std::string target_name; // name mob, location -- default empty, max length 64 + std::string item_list; // likely defaults to empty + std::string skill_list; // IDs ; separated -- default -1 + std::string spell_list; // IDs ; separated -- default 0 + std::string description_override; // overrides auto generated description -- default empty, max length 128 + int skill_id; // older clients, first id from above + int spell_id; // older clients, first id from above + int goal_id; + TaskMethodType goal_method; + int goal_count; + int deliver_to_npc; + std::vector zone_ids; + std::string zones; // IDs ; separated, ZoneID is the first in this list for older clients -- default empty string, max length 64 + bool optional; + + inline bool CheckZone(int zone_id) + { + if (zone_ids.empty()) { + return true; + } + return std::find(zone_ids.begin(), zone_ids.end(), zone_id) != zone_ids.end(); + } + + void SerializeSelector(SerializeBuffer& out, EQ::versions::ClientVersion client_version) const + { + out.WriteInt32(static_cast(activity_type)); + out.WriteInt32(0); // solo/group/raid request type? (no longer in live) + out.WriteString(target_name); // target name used in objective type string (max 64) + + if (client_version >= EQ::versions::ClientVersion::RoF) + { + out.WriteLengthString(item_list); // used in objective type string (can be empty for none) + out.WriteInt32(activity_type == TaskActivityType::GiveCash ? 1 : goal_count); + out.WriteLengthString(skill_list); // used in SkillOn objective type string, "-1" for none + out.WriteLengthString(spell_list); // used in CastOn objective type string, "0" for none + out.WriteString(zones); // used in objective zone column and task select "begins in" (may have multiple, "0" for "unknown zone", empty for "ALL") + } + else + { + out.WriteString(item_list); + out.WriteInt32(activity_type == TaskActivityType::GiveCash ? 1 : goal_count); + out.WriteInt32(skill_id); + out.WriteInt32(spell_id); + out.WriteInt32(zone_ids.empty() ? 0 : zone_ids.front()); + } + + out.WriteString(description_override); + + if (client_version >= EQ::versions::ClientVersion::RoF) { + out.WriteString(zones); // serialized again after description (seems unused) + } + } + + void SerializeObjective(SerializeBuffer& out, EQ::versions::ClientVersion client_version, int done_count) const + { + // cash objectives internally repurpose goal_count to store cash amount and + // done_count as a boolean (should not be sent as actual completed/goal counts) + int real_goal_count = goal_count; + if (activity_type == TaskActivityType::GiveCash) + { + done_count = (done_count >= goal_count) ? 1 : 0; + real_goal_count = 1; + } + + out.WriteInt32(static_cast(activity_type)); + + if (client_version >= EQ::versions::ClientVersion::RoF) { + out.WriteInt8(optional ? 1 : 0); + } else { + out.WriteInt32(optional ? 1 : 0); + } + + out.WriteInt32(0); // solo/group/raid request type? (no longer in live) + out.WriteString(target_name); // target name used in objective type string (max 64) + + if (client_version >= EQ::versions::ClientVersion::RoF) + { + out.WriteLengthString(item_list); // used in objective type string (can be empty for none) + out.WriteInt32(real_goal_count); + out.WriteLengthString(skill_list); // used in SkillOn objective type string, "-1" for none + out.WriteLengthString(spell_list); // used in CastOn objective type string, "0" for none + out.WriteString(zones); // used in objective zone column and task select "begins in" ("0" for "unknown zone", empty for "ALL") + } + else + { + out.WriteString(item_list); + out.WriteInt32(real_goal_count); + out.WriteInt32(skill_id); + out.WriteInt32(spell_id); + out.WriteInt32(zone_ids.empty() ? 0 : zone_ids.front()); + } + + out.WriteInt32(0); // unknown id + out.WriteString(description_override); + out.WriteInt32(done_count); + out.WriteInt8(1); // unknown + + if (client_version >= EQ::versions::ClientVersion::RoF) + { + out.WriteString(zones); // serialized again after description (seems unused) + } + } +}; + +typedef enum { + ActivitiesSequential = 0, + ActivitiesStepped = 1 +} SequenceType; + +enum class TaskType { + Task = 0, // can have at max 1 + Shared = 1, // can have at max 1 + Quest = 2, // can have at max 19 or 29 depending on client + E = 3 // can have at max 19 or 29 depending on client, not present in live anymore +}; + +static const uint8 TASK_TYPE_TASK = 0; +static const uint8 TASK_TYPE_SHARED = 1; +static const uint8 TASK_TYPE_QUEST = 2; + +enum class DurationCode { + None = 0, + Short = 1, + Medium = 2, + Long = 3 +}; + +struct TaskInformation { + TaskType type; + int duration{}; + DurationCode duration_code; // description for time investment for when duration == 0 + std::string title{}; // max length 64 + std::string description{}; // max length 4000, 2048 on Tit + std::string reward{}; + std::string item_link{}; // max length 128 older clients, item link gets own string + std::string completion_emote{}; // emote after completing task, yellow. Maybe should make more generic ... but yellow for now! + int reward_id{}; + int cash_reward{}; // Expressed in copper + int experience_reward{}; + int faction_reward{}; // just a npc_faction_id + TaskMethodType reward_method; + int reward_radiant_crystals; + int reward_ebon_crystals; + int activity_count{}; + SequenceType sequence_mode; + int last_step{}; + short min_level{}; + short max_level{}; + int level_spread; + int min_players; + int max_players; + bool repeatable{}; + int replay_timer_seconds; + int request_timer_seconds; + ActivityInformation activity_information[MAXACTIVITIESPERTASK]; + + void SerializeSelector(SerializeBuffer& out, EQ::versions::ClientVersion client_version) const + { + if (client_version != EQ::versions::ClientVersion::Titanium) { + out.WriteFloat(1.0f); // reward multiplier (affects color, <1.0: yellow-red, 1.0: white, >1.0: lightgreen-green) + } + + out.WriteUInt32(duration); // task duration (seconds) (0: task_duration_code used, "Unlimited" on live) + out.WriteUInt32(static_cast(duration_code)); // 1: Short 2: Medium 3: Long anything else Unlimited (no longer in live) + out.WriteString(title); // max 64 with null + out.WriteString(description); // max 4000 with null + + if (client_version != EQ::versions::ClientVersion::Titanium) { + out.WriteUInt8(0); // 0: no rewards 1: enables "Reward Preview" button + } + + // selector only needs to send the first objective to fill description starting zone + out.WriteUInt32(std::min(activity_count, 1)); // number of task objectives + if (activity_count > 0) + { + out.WriteUInt32(0); // objective index + activity_information[0].SerializeSelector(out, client_version); + } + } +}; + +typedef enum { + ActivityHidden = 0, + ActivityActive = 1, + ActivityCompleted = 2 +} ActivityState; + +struct ClientActivityInformation { + int activity_id; + int done_count; + ActivityState activity_state; + bool updated; // Flag so we know if we need to updated the database +}; + +struct ClientTaskInformation { + int slot; // intrusive, but makes things easier :P + int task_id; + int current_step; + int accepted_time; + bool updated; + ClientActivityInformation activity[MAXACTIVITIESPERTASK]; +}; + +struct CompletedTaskInformation { + int task_id; + int completed_time; + bool activity_done[MAXACTIVITIESPERTASK]; +}; + +namespace Tasks { + + inline int GetActivityStateIdentifier(ActivityState activity_state) + { + switch (activity_state) { + case ActivityHidden: + return 0; + case ActivityActive: + return 1; + case ActivityCompleted: + return 2; + default: + return 0; + } + } + inline std::string GetActivityStateDescription(ActivityState activity_state) + { + switch (activity_state) { + case ActivityHidden: + return "Hidden"; + case ActivityActive: + return "Active"; + case ActivityCompleted: + return "Completed"; + default: + return "Hidden"; + } + } + + inline int GetTaskTypeIdentifier(TaskType task_type) + { + switch (task_type) { + case TaskType::Task: + return 0; + case TaskType::Shared: + return 1; + case TaskType::Quest: + return 2; + case TaskType::E: + return 3; + default: + return 0; + } + } + inline std::string GetTaskTypeDescription(TaskType task_type) + { + switch (task_type) { + case TaskType::Task: + return "Task"; + case TaskType::Shared: + return "Shared"; + case TaskType::Quest: + return "Quest"; + case TaskType::E: + return "E"; + default: + return "Task"; + } + } +} + +namespace SharedTaskMessage { + constexpr uint16 TASK_ASSIGN_WAIT_REPLAY_TIMER = 8017; // This task can not be assigned to you because you must wait %1d:%2h:%3m before you can do another task of this type. + constexpr uint16 COULD_NOT_USE_COMMAND = 8272; // You could not use this command because you are not currently assigned to a shared task. + constexpr uint16 AVG_LVL_LOW = 8553; // You can not be assigned this shared task because your party's average level is too low. + constexpr uint16 AVG_LVL_HIGH = 8889; // You can not be assigned this shared task because your party's average level is too high. + constexpr uint16 LVL_SPREAD_HIGH = 8890; // You can not be assigned this shared task because your party's level spread is too high. + constexpr uint16 PARTY_EXCEED_MAX_PLAYER = 8891; // You can not be assigned this shared task because your party exceeds the maximum allowed number of players. + constexpr uint16 LEADER_NOT_MEET_REQUIREMENTS = 8892; // You can not be assigned this shared task because the leader does not meet the shared task requirements. + constexpr uint16 SHARED_TASK_NOT_MEET_MIN_NUM_PLAYER = 8895; // You can not be assigned this shared task because your party does not contain the minimum required number of players. + constexpr uint16 WILL_REMOVE_ZONE_TWO_MIN_RAID_NOT_MIN_NUM_PLAYER = 8908; // %1 will be removed from their zone in two minutes because your raid does not meet the minimum requirement of qualified players. + constexpr uint16 WILL_REMOVE_ZONE_TWO_MIN_GROUP_NOT_MIN_NUM_PLAYER = 8909; // %1 will be removed from their zone in two minutes because your group does not meet the minimum requirement of qualified players. + constexpr uint16 WILL_REMOVE_AREA_TWO_MIN_RAID_NOT_MIN_NUM_PLAYER = 8910; // %1 will be removed from their area in two minutes because your raid does not meet the minimum requirement of qualified players. + constexpr uint16 WILL_REMOVE_AREA_TWO_MIN_GROUP_NOT_MIN_NUM_PLAYER = 8911; // %1 will be removed from their area in two minutes because your group does not meet the minimum requirement of qualified players. + constexpr uint16 HAS_REMOVED_ZONE_TWO_MIN_RAID_NOT_MIN_NUM_PLAYER = 8912; // %1 has been removed from their zone because your raid does not meet the minimum requirement of qualified players. + constexpr uint16 HAS_REMOVED_ZONE_TWO_MIN_GROUP_NOT_MIN_NUM_PLAYER = 8913; // %1 has been removed from their zone because your group does not meet the minimum requirement of qualified players. + constexpr uint16 HAS_REMOVED_AREA_TWO_MIN_RAID_NOT_MIN_NUM_PLAYER = 8914; // %1 has been removed from their area because your raid does not meet the minimum requirement of qualified players. + constexpr uint16 HAS_REMOVED_AREA_TWO_MIN_GROUP_NOT_MIN_NUM_PLAYER = 8915; // %1 has been removed from their area because your group does not meet the minimum requirement of qualified players. + constexpr uint16 SEND_INVITE_TO = 8916; // Sending a shared task invitation to %1. + constexpr uint16 COULD_NOT_BE_INVITED = 8917; // %1 could not be invited to join you. + constexpr uint16 YOU_ARE_NOT_LEADER_COMMAND_ISSUE = 8919; // You are not the shared task leader. Only %1 can issue this command. + constexpr uint16 SWAP_SENDING_INVITATION_TO = 8920; // Sending an invitation to: %1. They must accept in order to swap party members. + constexpr uint16 SWAP_ACCEPTED_OFFER = 8921; // %1 has accepted your offer to join your shared task. Swapping %1 for %2. + constexpr uint16 IS_NOT_MEMBER = 8922; // %1 is not a member of this shared task. + constexpr uint16 NOT_ALLOW_PLAYER_REMOVE = 8923; // The shared task is not allowing players to be removed from it at this time. + constexpr uint16 PLAYER_HAS_BEEN_REMOVED = 8924; // %1 has been removed from your shared task, '%2'. + constexpr uint16 TRANSFER_LEADERSHIP_NOT_ONLINE = 8925; // %1 is not currently online. You can only transfer leadership to an online member of the shared task. + constexpr uint16 MADE_LEADER = 8926; // %1 has been made the leader for this shared task. + constexpr uint16 YOU_MADE_LEADER = 8927; // You have been made the leader of this shared task. + constexpr uint16 LEADER_PRINT = 8928; // Shared Task Leader: %1 + constexpr uint16 MEMBERS_PRINT = 8929; // Shared Task Members: %1 + constexpr uint16 PLAYER_ACCEPTED_OFFER_JOIN = 8930; // %1 has accepted your offer to join your shared task. + constexpr uint16 PLAYER_HAS_BEEN_ADDED = 8931; // %1 has been added to your shared task, '%2'. + constexpr uint16 ACCEPTED_OFFER_TO_JOIN_BUT_COULD_NOT = 8932; // %1 accepted your offer to join your shared task but could not. + constexpr uint16 PLAYER_DECLINED_OFFER = 8933; // %1 has declined your offer to join your shared task. + constexpr uint16 PLAYER_HAS_ASKED_YOU_TO_JOIN = 8934; // %1 has asked you to join the shared task '%2'. Would you like to join? + constexpr uint16 NO_REQUEST_BECAUSE_HAVE_ONE = 8935; // You may not request a shared task because you already have one. + constexpr uint16 NO_REQUEST_BECAUSE_RAID_HAS_ONE = 8936; // You may not request a shared task because someone in your raid, %1, already has one. + constexpr uint16 NO_REQUEST_BECAUSE_GROUP_HAS_ONE = 8937; // You may not request a shared task because someone in your group, %1, already has one. + constexpr uint16 YOU_DO_NOT_MEET_REQ_AVAILABLE = 8938; // You do not meet the requirements for any available shared tasks. + constexpr uint16 YOUR_RAID_DOES_NOT_MEET_REQ = 8939; // Your raid does not meet the requirements for any available shared tasks. + constexpr uint16 YOUR_GROUP_DOES_NOT_MEET_REQ = 8940; // Your group does not meet the requirements for any available shared tasks. + constexpr uint16 YOUR_GROUP__RAID_DOES_NOT_MEET_REQ = 8941; // You can not be assigned this shared task because the raid or group does not meet the shared task requirements. + constexpr uint16 YOU_NO_LONGER_MEMBER = 8942; // You are no longer a member of the shared task. + constexpr uint16 YOU_MAY_NOT_REQUEST_EXPANSION = 8943; // You may not request this shared task because you do not have the required expansion. + constexpr uint16 PLAYER_MAY_NOT_REQUEST_EXPANSION = 8944; // You may not request this shared task because %1 does not have the required expansion. + constexpr uint16 TWO_MIN_REQ_TASK_TERMINATED = 8945; // If your party does not meet the requirements in two minutes, the shared task will be terminated. + constexpr uint16 YOU_MUST_WAIT_REPLAY_TIMER = 8946; // You may not request this shared task because you must wait %1d:%2h:%3m before you can do another task of this type. + constexpr uint16 PLAYER_MUST_WAIT_REPLAY_TIMER = 8947; // You may not request this shared task because %1 must wait %2d:%3h:%4m before they can do another task of this type. + constexpr uint16 PLAYER_NOW_LEADER = 8948; // %1 is now the leader of your shared task, '%2'. + constexpr uint16 HAS_ENDED = 8951; // Your shared task, '%1', has ended. + constexpr uint16 YOU_ALREADY_LEADER = 8952; // You are already the leader of the shared task. + constexpr uint16 TASK_NO_LONGER_ACTIVE = 8953; // Your shared task, '%1', is no longer active. + constexpr uint16 YOU_HAVE_BEEN_ADDED_TO_TASK = 8954; // You have been added to the shared task '%1'. + constexpr uint16 YOU_ARE_NOW_LEADER = 8955; // You are now the leader of your shared task, '%1'. + constexpr uint16 YOU_HAVE_BEEN_REMOVED = 8956; // You have been removed from the shared task '%1'. + constexpr uint16 YOU_ARE_NO_LONGER_A_MEMBER = 8960; // You are no longer a member of the shared task, '%1'. + constexpr uint16 YOUR_TASK_NOW_LOCKED = 8961; // Your shared task is now locked. You may no longer add or remove players. + constexpr uint16 TASK_NOT_ALLOWING_PLAYERS_AT_TIME = 8962; // The shared task is not allowing players to be added at this time. + constexpr uint16 PLAYER_NOT_ONLINE_TO_ADD = 8963; // %1 is not currently online. A player needs to be online to be added to a shared task. + constexpr uint16 CANT_ADD_PLAYER_ALREADY_MEMBER = 8964; // You can not add %1 because they are already a member of this shared task. + constexpr uint16 CANT_ADD_PLAYER_ALREADY_ASSIGNED = 8965; // You can not add %1 because they are already assigned to another shared task. + constexpr uint16 PLAYER_ALREADY_OUTSTANDING_INVITATION_THIS = 8966; // %1 already has an outstanding invitation to join this shared task. + constexpr uint16 PLAYER_ALREADY_OUTSTANDING_ANOTHER = 8967; // %1 already has an outstanding invitation to join another shared task. Players may only have one invitation outstanding. + constexpr uint16 CANT_ADD_PLAYER_MAX_PLAYERS = 8968; // You can not add another player since you currently have the maximum number of players allowed (%1) in this shared task. + constexpr uint16 CANT_ADD_PLAYER_MAX_LEVEL_SPREAD = 8969; // You can not add this player because you would exceed the maximum level spread (%1) for this shared task. + constexpr uint16 CANT_ADD_PLAYER_MAX_AVERAGE_LEVEL = 8970; // You can not add this player because you would exceed the maximum average level for this shared task. + constexpr uint16 CANT_ADD_PLAYER_FALL_MIN_AVG_LEVEL = 8971; // You can not add this player because you would fall below the minimum average level for this shared task. + constexpr uint16 PLAYER_DOES_NOT_OWN_EXPANSION = 8972; // %1 does not own the expansion needed for this shared task. + constexpr uint16 CANT_ADD_PLAYER_PARTY_FILTER_REQ_FOR_TASK = 8973; // You can not add this player because your party would no longer meet the filter requirements for this shared task. + constexpr uint16 CANT_ADD_PLAYER_ONE_OF_GROUP_RAID_HAS_TASK = 8977; // You can not add %1 because they or one of their group or raid members is in another shared task. + constexpr uint16 CANT_JOIN_GROUP_ACTIVE_TASK = 8978; // You can not join that group because you have an active shared task. + constexpr uint16 CANT_ADD_PLAYER_REPLAY_TIMER = 8979; // You may not add %1 because they must wait %2d:%3h:%4m before they can do another task of this type. + constexpr uint16 CANT_LOOT_BECAUSE_TASK_LOCKED_BELONG = 8980; // You may not loot that corpse because you are not in the shared task the corpse belongs to. + constexpr uint16 CANT_ADD_PLAYER_BECAUSE_GROUP_RAID_BELONG_TASK = 8981; // The player could not be added to the raid because they or one of their group members is in a different shared task. + constexpr uint16 PLAYER_CANT_ADD_GROUP_BECAUSE_DIFF_TASK = 8982; // %1 can not be added to the group because they are in a different shared task. + constexpr uint16 YOU_CANT_ADD_TO_GROUP_BECAUSE_DIFF_TASK = 8983; // You can not be added to the group because you are in a different shared task. + constexpr uint16 PLAYER_CANT_ADD_RAID_BECAUSE_DIFF_TASK = 8984; // %1 can not be added to the raid because they are in a different shared task. + constexpr uint16 YOU_CANT_ADD_RAID_BECAUSE_DIFF_TASK = 8985; // You can not be added to the raid because you are in a different shared task. + constexpr uint16 REPLAY_TIMER_REMAINING = 8987; // '%1' replay timer: %2d:%3h:%4m remaining. + constexpr uint16 YOU_NO_CURRENT_REPLAY_TIMERS = 8989; // You do not currently have any task replay timers. + constexpr uint16 SURE_QUIT_TASK = 8995; // Are you sure you want to quit the task '%1'? + constexpr uint16 SURE_REMOVE_SELF_FROM_TASK = 8996; // Are you sure you want to remove yourself from the shared task '%1' + constexpr uint16 TASK_ASSIGN_WAIT_REQUEST_TIMER = 14506; // This task can not be assigned to you because you must wait %1d:%2h:%3m before you can request another task of this type. + constexpr uint16 REQUEST_TIMER_REMAINING = 14507; // '%1' request timer: %2d:%3h:%4m remaining. + constexpr uint16 YOU_MUST_WAIT_REQUEST_TIMER = 14508; // You may not request this shared task because you must wait %1d:%2h:%3m before you can request another task of this type. + constexpr uint16 RECEIVED_REQUEST_TIMER = 14509; // You have received a request timer for '%1': %2d:%3h:%4m remaining. + constexpr uint16 RECEIVED_REPLAY_TIMER = 14510; // You have received a replay timer for '%1': %2d:%3h:%4m remaining. + constexpr uint16 PLAYER_MUST_WAIT_REQUEST_TIMER = 14511; // You may not request this shared task because %1 must wait %2d:%3h:%4m before they can request another task of this type. + constexpr uint16 CANT_ADD_PLAYER_REQUEST_TIMER = 14512; // You may not add %1 because they must wait %2d:%3h:%4m before they can request another task of this type. + + // for eqstrs not in current emu clients (some are also used by non-shared tasks) + constexpr auto GetEQStr(uint16 eqstr_id) + { + switch (eqstr_id) + { + case SharedTaskMessage::COULD_NOT_USE_COMMAND: + return "You could not use this command because you are not currently assigned to a shared task."; + case SharedTaskMessage::TASK_ASSIGN_WAIT_REQUEST_TIMER: + return "This task can not be assigned to you because you must wait {}d:{}h:{}m before you can request another task of this type."; + case SharedTaskMessage::REQUEST_TIMER_REMAINING: + return "'{}' request timer: {}d:{}h:{}m remaining."; + case SharedTaskMessage::YOU_MUST_WAIT_REQUEST_TIMER: + return "You may not request this shared task because you must wait {}d:{}h:{}m before you can request another task of this type."; + case SharedTaskMessage::RECEIVED_REQUEST_TIMER: + return "You have received a request timer for '{}': {}d:{}h:{}m remaining."; + case SharedTaskMessage::RECEIVED_REPLAY_TIMER: + return "You have received a replay timer for '{}': {}d:{}h:{}m remaining."; + case SharedTaskMessage::PLAYER_MUST_WAIT_REQUEST_TIMER: + return "You may not request this shared task because {} must wait {}d:{}h:{}m before they can request another task of this type."; + case SharedTaskMessage::CANT_ADD_PLAYER_REQUEST_TIMER: + return "You may not add {} because they must wait {}d:{}h:{}m before they can request another task of this type."; + default: + LogTasks("[GetEQStr] Unhandled eqstr id [{}]", eqstr_id); + break; + } + return "Unknown EQStr"; + } +} + +#endif //EQEMU_TASKS_H diff --git a/common/version.h b/common/version.h index 18a11b1b1..bd84935ae 100644 --- a/common/version.h +++ b/common/version.h @@ -34,7 +34,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9168 +#define CURRENT_BINARY_DATABASE_VERSION 9172 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9028 diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf index 76178bf62..93175209e 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -560,12 +560,14 @@ OP_TaskActivityComplete=0x71cd OP_AcceptNewTask=0x394d OP_CancelTask=0x0c7f OP_TaskMemberList=0x748e # Was 0x1656 -OP_OpenNewTasksWindow=0x436c # Was 0x11de +OP_TaskSelectWindow=0x436c +OP_SharedTaskSelectWindow=0x436c OP_AvaliableTask=0x2bf8 # Was 0x2377 OP_TaskHistoryRequest=0x6cf6 OP_TaskHistoryReply=0x25eb OP_DeclineAllTasks=0x0000 OP_TaskRequestTimer=0x4b76 +OP_SharedTaskQuit=0x0000 # Title opcodes OP_NewTitlesAvailable=0x45d1 diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index 96f3258a8..ac69e5550 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -567,13 +567,26 @@ OP_CompletedTasks=0x4eba OP_TaskActivityComplete=0x5e19 OP_AcceptNewTask=0x0a23 OP_CancelTask=0x39f0 -OP_TaskMemberList=0x5727 -OP_OpenNewTasksWindow=0x48a2 OP_AvaliableTask=0x36e8 OP_TaskHistoryRequest=0x5f1c OP_TaskHistoryReply=0x3d05 OP_DeclineAllTasks=0x0000 OP_TaskRequestTimer=0x7a48 +OP_TaskSelectWindow=0x705b + +# Shared Tasks +OP_SharedTaskMemberList=0x1e7d # +OP_SharedTaskRemovePlayer=0x4865 # /taskremoveplayer +OP_SharedTaskAddPlayer=0x36e8 # /taskaddplayer +OP_SharedTaskMakeLeader=0x37f2 # /taskmakeleader +OP_SharedTaskInvite=0x3444 # Dialog window +OP_SharedTaskInviteResponse=0x7582 # Dialog window response +OP_SharedTaskAcceptNew=0x6646 # +OP_SharedTaskMemberChange=0x0119 # +OP_TaskTimers=0x2b0f # /tasktimers +OP_SharedTaskQuit=0x322e # /taskquit +OP_SharedTaskSelectWindow=0x48a2 +OP_SharedTaskPlayerList=0x5727 # /taskplayerlist # Title opcodes OP_NewTitlesAvailable=0x0d32 diff --git a/utils/patches/patch_SoD.conf b/utils/patches/patch_SoD.conf index 1ef8a138a..dd3ee0384 100644 --- a/utils/patches/patch_SoD.conf +++ b/utils/patches/patch_SoD.conf @@ -527,8 +527,6 @@ OP_LDoNOpen=0x6129 # C # Task packets OP_TaskActivityComplete=0x4df0 # C -OP_TaskMemberList=0x34ed # C -OP_OpenNewTasksWindow=0x4dd5 # C OP_AvaliableTask=0x2136 # C OP_AcceptNewTask=0x5832 # C OP_TaskHistoryRequest=0x29d7 # C @@ -536,7 +534,21 @@ OP_TaskHistoryReply=0x3d2a # C OP_CancelTask=0x726b # C OP_DeclineAllTasks=0x0000 # OP_TaskRequestTimer=0x2e70 +OP_TaskSelectWindow=0x009b +# Shared Tasks +OP_SharedTaskMemberList=0x55f4 # +OP_SharedTaskRemovePlayer=0x4a12 # /taskremoveplayer +OP_SharedTaskAddPlayer=0x2136 # /taskaddplayer +OP_SharedTaskMakeLeader=0x1da9 # /taskmakeleader +OP_SharedTaskInvite=0x40c5 # Dialog window +OP_SharedTaskInviteResponse=0x7abb # Dialog window response +OP_SharedTaskAcceptNew=0x4751 # Not sure why this has a separate handler +OP_SharedTaskMemberChange=0x98f6 # Not sure yet? +OP_TaskTimers=0x01cb # /tasktimers +OP_SharedTaskQuit=0x5854 # /taskquit +OP_SharedTaskSelectWindow=0x4dd5 +OP_SharedTaskPlayerList=0x34ed # /taskplayerlist OP_Shroud=0x6d1f OP_ShroudRemove=0x17f6 diff --git a/utils/patches/patch_SoF.conf b/utils/patches/patch_SoF.conf index 1c5fd3eda..e6aa71168 100644 --- a/utils/patches/patch_SoF.conf +++ b/utils/patches/patch_SoF.conf @@ -496,7 +496,8 @@ OP_LDoNOpen=0x4b92 #Xinu 03/19/09 #Task packets OP_TaskActivityComplete=0x7338 # -OP_OpenNewTasksWindow=0x17C3 # +OP_SharedTaskSelectWindow=0x17C3 +OP_TaskSelectWindow=0x17C3 OP_AvaliableTask=0x5d1d #Xinu 03/19/09 OP_AcceptNewTask=0x66A8 # OP_TaskHistoryRequest=0x3035 # @@ -511,7 +512,7 @@ OP_TaskMakeLeader=0x5050 OP_TaskAddPlayer=0x5d1d OP_TaskRemovePlayer=0x516f OP_TaskPlayerList=0x0ad6 -OP_TaskQuit=0x2c8c +OP_SharedTaskQuit=0x2c8c OP_TaskRequestTimer=0x0b08 #Title opcodes diff --git a/utils/patches/patch_Titanium.conf b/utils/patches/patch_Titanium.conf index 32d434e93..2c647f95c 100644 --- a/utils/patches/patch_Titanium.conf +++ b/utils/patches/patch_Titanium.conf @@ -459,8 +459,7 @@ OP_TaskActivityComplete=0x54eb OP_CompletedTasks=0x76a2 # ShowEQ 10/27/05 OP_TaskDescription=0x5ef7 # ShowEQ 10/27/05 OP_TaskActivity=0x682d # ShowEQ 10/27/05 -OP_TaskMemberList=0x722f #not sure -OP_OpenNewTasksWindow=0x5e7c #combined with OP_AvaliableTask I think +OP_TaskSelectWindow=0x5e7c OP_AvaliableTask=0x0000 OP_AcceptNewTask=0x207f OP_TaskHistoryRequest=0x5df4 @@ -473,11 +472,22 @@ OP_TaskMemberChange=0x5886 OP_TaskMakeLeader=0x1b25 OP_TaskAddPlayer=0x6bc4 OP_TaskRemovePlayer=0x37b9 -OP_TaskPlayerList=0x3961 -OP_TaskQuit=0x35dd OP_TaskRequestTimer=0x6a1d #task complete related: 0x0000 (24 bytes), 0x0000 (8 bytes), 0x0000 (4 bytes) +# Shared Tasks +OP_SharedTaskMemberList=0x722f # +OP_SharedTaskRemovePlayer=0x37b9 # /taskremoveplayer +OP_SharedTaskAddPlayer=0x6934 # /taskaddplayer +OP_SharedTaskMakeLeader=0x1b25 # /taskmakeleader +OP_SharedTaskInvite=0x79b4 # Dialog window +OP_SharedTaskInviteResponse=0x0358 # Dialog window response +OP_SharedTaskAcceptNew=0x194d # Not sure why this has a separate handler +OP_SharedTaskMemberChange=0x5886 # Not sure yet? +OP_TaskTimers=0x6a1d # /tasktimers +OP_SharedTaskQuit=0x35dd # /taskquit +OP_SharedTaskSelectWindow=0x013f +OP_SharedTaskPlayerList=0x3961 # /taskplayerlist OP_RequestClientZoneChange=0x7834 # ShowEQ 10/27/05 diff --git a/utils/patches/patch_UF.conf b/utils/patches/patch_UF.conf index 8efce91f6..4dd97fad0 100644 --- a/utils/patches/patch_UF.conf +++ b/utils/patches/patch_UF.conf @@ -551,8 +551,6 @@ OP_LDoNInspect=0x0aaa # Task packets OP_TaskActivityComplete=0x5832 # C -OP_TaskMemberList=0x66ba # C -OP_OpenNewTasksWindow=0x98f6 # C OP_AvaliableTask=0x6255 # C Mispelled? OP_AcceptNewTask=0x17d5 # C OP_TaskHistoryRequest=0x547c # C @@ -560,6 +558,21 @@ OP_TaskHistoryReply=0x4524 # C OP_CancelTask=0x3bf5 # C OP_DeclineAllTasks=0x0000 # OP_TaskRequestTimer=0x719e +OP_TaskSelectWindow=0x7309 + +# Shared Tasks +OP_SharedTaskMemberList=0x584e # +OP_SharedTaskRemovePlayer=0x18e2 # /taskremoveplayer +OP_SharedTaskAddPlayer=0x6255 # /taskaddplayer +OP_SharedTaskMakeLeader=0x5933 # /taskmakeleader +OP_SharedTaskInvite=0x55f4 # Dialog window +OP_SharedTaskInviteResponse=0x26e5 # Dialog window response +OP_SharedTaskAcceptNew=0x6ded # Not sure why this has a separate handler +OP_SharedTaskMemberChange=0x1402 # Not sure yet? +OP_TaskTimers=0x09b4 # /tasktimers +OP_SharedTaskQuit=0x6aba # /taskquit +OP_SharedTaskSelectWindow=0x98f6 +OP_SharedTaskPlayerList=0x66ba # /taskplayerlist # Title opcodes OP_NewTitlesAvailable=0x4b49 # C diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 8e6a7a40e..cddfb7717 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -422,6 +422,10 @@ 9166|2021_02_12_dynamic_zone_members.sql|SHOW TABLES LIKE 'dynamic_zone_members'|empty| 9167|2021_06_06_beastlord_pets.sql|SHOW TABLES LIKE 'pets_beastlord_data'|empty| 9168|2021_08_31_pvp_duration.sql|SHOW COLUMNS FROM `spells_new` LIKE 'pvp_duration'|empty| +9169|2021_06_06_dynamic_zone_moved_columns.sql|SELECT * FROM db_version WHERE version >= 9169|empty| +9170|2021_03_03_instance_safereturns.sql|SHOW TABLES LIKE 'character_instance_safereturns'|empty| +9171|2021_03_30_remove_dz_is_current_member.sql|SHOW COLUMNS FROM `dynamic_zone_members` LIKE 'is_current_member'|not_empty| +9172|2021_05_21_shared_tasks.sql|SHOW TABLES LIKE 'shared_tasks'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2021_03_03_instance_safereturns.sql b/utils/sql/git/required/2021_03_03_instance_safereturns.sql new file mode 100644 index 000000000..6687b17a7 --- /dev/null +++ b/utils/sql/git/required/2021_03_03_instance_safereturns.sql @@ -0,0 +1,13 @@ +CREATE TABLE `character_instance_safereturns` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `character_id` int(10) unsigned NOT NULL, + `instance_zone_id` int(11) NOT NULL DEFAULT 0, + `instance_id` int(11) NOT NULL DEFAULT 0, + `safe_zone_id` int(11) NOT NULL DEFAULT 0, + `safe_x` float NOT NULL DEFAULT 0, + `safe_y` float NOT NULL DEFAULT 0, + `safe_z` float NOT NULL DEFAULT 0, + `safe_heading` float NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + UNIQUE KEY `character_id` (`character_id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 diff --git a/utils/sql/git/required/2021_03_30_remove_dz_is_current_member.sql b/utils/sql/git/required/2021_03_30_remove_dz_is_current_member.sql new file mode 100644 index 000000000..c3293ac2d --- /dev/null +++ b/utils/sql/git/required/2021_03_30_remove_dz_is_current_member.sql @@ -0,0 +1,6 @@ +-- remove any non-current members for new behavior +DELETE FROM `dynamic_zone_members` +WHERE is_current_member = 0; + +ALTER TABLE `dynamic_zone_members` + DROP COLUMN `is_current_member`; diff --git a/utils/sql/git/required/2021_05_21_shared_tasks.sql b/utils/sql/git/required/2021_05_21_shared_tasks.sql new file mode 100644 index 000000000..351cb5ba8 --- /dev/null +++ b/utils/sql/git/required/2021_05_21_shared_tasks.sql @@ -0,0 +1,97 @@ +-- shared task tables +CREATE TABLE `shared_tasks` +( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `task_id` int(11) DEFAULT NULL, + `accepted_time` datetime DEFAULT NULL, + `expire_time` datetime DEFAULT NULL, + `completion_time` datetime DEFAULT NULL, + `is_locked` tinyint(1) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; + +CREATE TABLE `shared_task_members` +( + `shared_task_id` bigint(20) NOT NULL, + `character_id` bigint(20) NOT NULL, + `is_leader` tinyint(4) DEFAULT NULL, + PRIMARY KEY (`shared_task_id`, `character_id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +CREATE TABLE `shared_task_activity_state` +( + `shared_task_id` bigint(20) NOT NULL, + `activity_id` int(11) NOT NULL, + `done_count` int(11) DEFAULT NULL, + `updated_time` datetime DEFAULT NULL, + `completed_time` datetime DEFAULT NULL, + PRIMARY KEY (`shared_task_id`, `activity_id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +CREATE TABLE `shared_task_dynamic_zones` +( + `shared_task_id` bigint(20) NOT NULL, + `dynamic_zone_id` int(10) unsigned NOT NULL, + PRIMARY KEY (`shared_task_id`, `dynamic_zone_id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- completed shared task tables - simply stores completed for reporting and logging + +CREATE TABLE `completed_shared_tasks` +( + `id` bigint(20) NOT NULL, + `task_id` int(11) DEFAULT NULL, + `accepted_time` datetime DEFAULT NULL, + `expire_time` datetime DEFAULT NULL, + `completion_time` datetime DEFAULT NULL, + `is_locked` tinyint(1) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +CREATE TABLE `completed_shared_task_members` +( + `shared_task_id` bigint(20) NOT NULL, + `character_id` bigint(20) NOT NULL, + `is_leader` tinyint(4) DEFAULT NULL, + PRIMARY KEY (`shared_task_id`, `character_id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +CREATE TABLE `completed_shared_task_activity_state` +( + `shared_task_id` bigint(20) NOT NULL, + `activity_id` int(11) NOT NULL, + `done_count` int(11) DEFAULT NULL, + `updated_time` datetime DEFAULT NULL, + `completed_time` datetime DEFAULT NULL, + PRIMARY KEY (`shared_task_id`, `activity_id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- tasks + +ALTER TABLE `tasks` + ADD COLUMN `level_spread` INT UNSIGNED NOT NULL DEFAULT 0 AFTER `maxlevel`, + ADD COLUMN `min_players` INT UNSIGNED NOT NULL DEFAULT 0 AFTER `level_spread`, + ADD COLUMN `max_players` INT UNSIGNED NOT NULL DEFAULT 0 AFTER `min_players`, + ADD COLUMN `replay_timer_seconds` INT UNSIGNED NOT NULL DEFAULT 0 AFTER `completion_emote`, + ADD COLUMN `request_timer_seconds` INT UNSIGNED NOT NULL DEFAULT 0 AFTER `replay_timer_seconds`; + +-- character timers + +CREATE TABLE `character_task_timers` +( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `character_id` int(10) unsigned NOT NULL DEFAULT 0, + `task_id` int(10) unsigned NOT NULL DEFAULT 0, + `timer_type` int(11) NOT NULL DEFAULT 0, + `expire_time` datetime NOT NULL DEFAULT current_timestamp(), + PRIMARY KEY (`id`), + KEY `character_id` (`character_id`), + KEY `task_id` (`task_id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +ALTER TABLE `tasks` + CHANGE COLUMN `completion_emote` `completion_emote` VARCHAR (512) NOT NULL DEFAULT '' COLLATE 'latin1_swedish_ci' AFTER `faction_reward`; + +ALTER TABLE `tasks` + ADD COLUMN `reward_radiant_crystals` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `rewardmethod`, + ADD COLUMN `reward_ebon_crystals` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `reward_radiant_crystals`; diff --git a/utils/sql/git/required/2021_06_06_dynamic_zone_moved_columns.sql b/utils/sql/git/required/2021_06_06_dynamic_zone_moved_columns.sql new file mode 100644 index 000000000..29635e8af --- /dev/null +++ b/utils/sql/git/required/2021_06_06_dynamic_zone_moved_columns.sql @@ -0,0 +1,23 @@ +ALTER TABLE `dynamic_zones` + ADD COLUMN `uuid` VARCHAR(36) NOT NULL COLLATE 'latin1_swedish_ci' AFTER `type`, + ADD COLUMN `name` VARCHAR(128) NOT NULL DEFAULT '' COLLATE 'latin1_swedish_ci' AFTER `uuid`, + ADD COLUMN `leader_id` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `name`, + ADD COLUMN `min_players` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `leader_id`, + ADD COLUMN `max_players` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `min_players`; + +-- migrate any currently active expeditions +UPDATE dynamic_zones +INNER JOIN expeditions ON expeditions.dynamic_zone_id = dynamic_zones.id +SET + dynamic_zones.uuid = expeditions.uuid, + dynamic_zones.name = expeditions.expedition_name, + dynamic_zones.leader_id = expeditions.leader_id, + dynamic_zones.min_players = expeditions.min_players, + dynamic_zones.max_players = expeditions.max_players; + +ALTER TABLE `expeditions` + DROP COLUMN `uuid`, + DROP COLUMN `expedition_name`, + DROP COLUMN `leader_id`, + DROP COLUMN `min_players`, + DROP COLUMN `max_players`; diff --git a/world/CMakeLists.txt b/world/CMakeLists.txt index 9a7ef5750..650d8f189 100644 --- a/world/CMakeLists.txt +++ b/world/CMakeLists.txt @@ -8,12 +8,11 @@ SET(world_sources clientlist.cpp console.cpp dynamic_zone.cpp + dynamic_zone_manager.cpp eql_config.cpp eqemu_api_world_data_service.cpp - expedition.cpp expedition_database.cpp expedition_message.cpp - expedition_state.cpp launcher_link.cpp launcher_list.cpp lfplist.cpp @@ -21,6 +20,8 @@ SET(world_sources login_server_list.cpp main.cpp queryserv.cpp + shared_task_manager.cpp + shared_task_world_messaging.cpp ucs.cpp web_interface.cpp web_interface_eqw.cpp @@ -44,18 +45,19 @@ SET(world_headers clientlist.h console.h dynamic_zone.h + dynamic_zone_manager.h eql_config.h eqemu_api_world_data_service.h - expedition.h expedition_database.h expedition_message.h - expedition_state.h launcher_link.h launcher_list.h lfplist.h login_server.h login_server_list.h queryserv.h + shared_task_manager.h + shared_task_world_messaging.h sof_char_create_data.h ucs.h web_interface.h diff --git a/world/client.cpp b/world/client.cpp index 03c6195e8..d5305eb67 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -121,7 +121,7 @@ Client::Client(EQStreamInterface* ieqs) m_ClientVersion = eqs->ClientVersion(); m_ClientVersionBit = EQ::versions::ConvertClientVersionToClientVersionBit(m_ClientVersion); - + numclients++; } @@ -833,19 +833,12 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { if(instance_id > 0) { - if(!database.VerifyInstanceAlive(instance_id, GetCharID())) + if (!database.VerifyInstanceAlive(instance_id, GetCharID()) || + !database.VerifyZoneInstance(zone_id, instance_id)) { - zone_id = database.MoveCharacterToBind(charid); + zone_id = database.MoveCharacterToInstanceSafeReturn(charid, zone_id, instance_id); instance_id = 0; } - else - { - if(!database.VerifyZoneInstance(zone_id, instance_id)) - { - zone_id = database.MoveCharacterToBind(charid); - instance_id = 0; - } - } } if(!is_player_zoning) { @@ -1154,34 +1147,23 @@ void Client::EnterWorld(bool TryBootup) { return; ZoneServer* zone_server = nullptr; - if(instance_id > 0) + if (instance_id > 0) { - if(database.VerifyInstanceAlive(instance_id, GetCharID())) - { - if(database.VerifyZoneInstance(zone_id, instance_id)) - { - zone_server = zoneserver_list.FindByInstanceID(instance_id); - } - else - { - instance_id = 0; - zone_server = nullptr; - database.MoveCharacterToBind(GetCharID()); - TellClientZoneUnavailable(); - return; - } - } - else + if (!database.VerifyInstanceAlive(instance_id, GetCharID()) || + !database.VerifyZoneInstance(zone_id, instance_id)) { instance_id = 0; - zone_server = nullptr; - database.MoveCharacterToBind(GetCharID()); + database.MoveCharacterToInstanceSafeReturn(GetCharID(), zone_id, instance_id); TellClientZoneUnavailable(); return; } + + zone_server = zoneserver_list.FindByInstanceID(instance_id); } else + { zone_server = zoneserver_list.FindByZoneID(zone_id); + } const char *zone_name = ZoneName(zone_id, true); if (zone_server) { diff --git a/world/cliententry.cpp b/world/cliententry.cpp index 9aa2b9ae0..9e10f495d 100644 --- a/world/cliententry.cpp +++ b/world/cliententry.cpp @@ -20,6 +20,7 @@ #include "clientlist.h" #include "login_server.h" #include "login_server_list.h" +#include "shared_task_manager.h" #include "worlddb.h" #include "zoneserver.h" #include "world_config.h" @@ -30,6 +31,7 @@ extern uint32 numplayers; extern LoginServerList loginserverlist; extern ClientList client_list; extern volatile bool RunLoops; +extern SharedTaskManager shared_task_manager; /** * @param in_id @@ -249,6 +251,8 @@ void ClientListEntry::LeavingZone(ZoneServer *iZS, CLE_Status iOnline) } SetOnline(iOnline); + shared_task_manager.RemoveActiveInvitationByCharacterID(CharID()); + if (pzoneserver) { pzoneserver->RemovePlayer(); LSUpdate(pzoneserver); diff --git a/world/clientlist.cpp b/world/clientlist.cpp index 0f216c0ee..ac3c2c82f 100644 --- a/world/clientlist.cpp +++ b/world/clientlist.cpp @@ -332,30 +332,91 @@ void ClientList::CLCheckStale() { } } -void ClientList::ClientUpdate(ZoneServer* zoneserver, ServerClientList_Struct* scl) { - LinkedListIterator iterator(clientlist); - ClientListEntry* cle; +void ClientList::ClientUpdate(ZoneServer *zoneserver, ServerClientList_Struct *scl) +{ + LinkedListIterator iterator(clientlist); + ClientListEntry *cle; iterator.Reset(); - while(iterator.MoreElements()) { + while (iterator.MoreElements()) { if (iterator.GetData()->GetID() == scl->wid) { cle = iterator.GetData(); - if (scl->remove == 2){ + if (scl->remove == 2) { cle->LeavingZone(zoneserver, CLE_Status::Offline); } - else if (scl->remove == 1) + else if (scl->remove == 1) { cle->LeavingZone(zoneserver, CLE_Status::Zoning); - else + } + else { cle->Update(zoneserver, scl); + } return; } iterator.Advance(); } - if (scl->remove == 2) + if (scl->remove == 2) { cle = new ClientListEntry(GetNextCLEID(), zoneserver, scl, CLE_Status::Online); - else if (scl->remove == 1) + } + else if (scl->remove == 1) { cle = new ClientListEntry(GetNextCLEID(), zoneserver, scl, CLE_Status::Zoning); - else + } + else { cle = new ClientListEntry(GetNextCLEID(), zoneserver, scl, CLE_Status::InZone); + } + + LogClientListDetail( + "[ClientUpdate] " + " remove [{}]" + " wid [{}]" + " IP [{}]" + " zone [{}]" + " instance_id [{}]" + " Admin [{}]" + " charid [{}]" + " name [{}]" + " AccountID [{}]" + " AccountName [{}]" + " LSAccountID [{}]" + " lskey [{}]" + " race [{}]" + " class_ [{}]" + " level [{}]" + " anon [{}]" + " tellsoff [{}]" + " guild_id [{}]" + " LFG [{}]" + " gm [{}]" + " ClientVersion [{}]" + " LFGFromLevel [{}]" + " LFGToLevel [{}]" + " LFGMatchFilter [{}]" + " LFGComments [{}]", + scl->remove, + scl->wid, + scl->IP, + scl->zone, + scl->instance_id, + scl->Admin, + scl->charid, + scl->name, + scl->AccountID, + scl->AccountName, + scl->LSAccountID, + scl->lskey, + scl->race, + scl->class_, + scl->level, + scl->anon, + scl->tellsoff, + scl->guild_id, + scl->LFG, + scl->gm, + scl->ClientVersion, + scl->LFGFromLevel, + scl->LFGToLevel, + scl->LFGMatchFilter, + scl->LFGComments + ); + clientlist.Insert(cle); zoneserver->ChangeWID(scl->charid, cle->GetID()); } @@ -1508,3 +1569,73 @@ void ClientList::GetClientList(Json::Value &response) Iterator.Advance(); } } + +void ClientList::SendCharacterMessage(uint32_t character_id, int chat_type, const std::string& message) +{ + auto character = FindCLEByCharacterID(character_id); + SendCharacterMessage(character, chat_type, message); +} + +void ClientList::SendCharacterMessage(const std::string& character_name, int chat_type, const std::string& message) +{ + auto character = FindCharacter(character_name.c_str()); + SendCharacterMessage(character, chat_type, message); +} + +void ClientList::SendCharacterMessage(ClientListEntry* character, int chat_type, const std::string& message) +{ + if (!character || !character->Server()) + { + return; + } + + uint32_t pack_size = sizeof(CZMessagePlayer_Struct); + auto pack = std::make_unique(ServerOP_CZMessagePlayer, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + buf->type = chat_type; + strn0cpy(buf->character_name, character->name(), sizeof(buf->character_name)); + strn0cpy(buf->message, message.c_str(), sizeof(buf->message)); + + character->Server()->SendPacket(pack.get()); +} + +void ClientList::SendCharacterMessageID(uint32_t character_id, + int chat_type, int eqstr_id, std::initializer_list args) +{ + auto character = FindCLEByCharacterID(character_id); + SendCharacterMessageID(character, chat_type, eqstr_id, args); +} + +void ClientList::SendCharacterMessageID(const std::string& character_name, + int chat_type, int eqstr_id, std::initializer_list args) +{ + auto character = FindCharacter(character_name.c_str()); + SendCharacterMessageID(character, chat_type, eqstr_id, args); +} + +void ClientList::SendCharacterMessageID(ClientListEntry* character, + int chat_type, int eqstr_id, std::initializer_list args) +{ + if (!character || !character->Server()) + { + return; + } + + SerializeBuffer serialized_args; + for (const auto& arg : args) + { + serialized_args.WriteString(arg); + } + + uint32_t args_size = static_cast(serialized_args.size()); + uint32_t pack_size = sizeof(CZClientMessageString_Struct) + args_size; + auto pack = std::make_unique(ServerOP_CZClientMessageString, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + buf->string_id = eqstr_id; + buf->chat_type = chat_type; + strn0cpy(buf->character_name, character->name(), sizeof(buf->character_name)); + buf->args_size = args_size; + memcpy(buf->args, serialized_args.buffer(), serialized_args.size()); + + character->Server()->SendPacket(pack.get()); +} diff --git a/world/clientlist.h b/world/clientlist.h index 3386a6f14..ed66a11c2 100644 --- a/world/clientlist.h +++ b/world/clientlist.h @@ -72,6 +72,13 @@ public: void GetClientList(Json::Value &response); + void SendCharacterMessage(uint32_t character_id, int chat_type, const std::string& message); + void SendCharacterMessage(const std::string& character_name, int chat_type, const std::string& message); + void SendCharacterMessage(ClientListEntry* character, int chat_type, const std::string& message); + void SendCharacterMessageID(uint32_t character_id, int chat_type, int eqstr_id, std::initializer_list args = {}); + void SendCharacterMessageID(const std::string& character_name, int chat_type, int eqstr_id, std::initializer_list args = {}); + void SendCharacterMessageID(ClientListEntry* character, int chat_type, int eqstr_id, std::initializer_list args = {}); + private: void OnTick(EQ::Timer *t); inline uint32 GetNextCLEID() { return NextCLEID++; } diff --git a/world/dynamic_zone.cpp b/world/dynamic_zone.cpp index 943ea83a9..945b41ec9 100644 --- a/world/dynamic_zone.cpp +++ b/world/dynamic_zone.cpp @@ -1,12 +1,14 @@ #include "dynamic_zone.h" -#include "expedition.h" -#include "expedition_state.h" +#include "cliententry.h" +#include "clientlist.h" +#include "dynamic_zone_manager.h" #include "worlddb.h" #include "zonelist.h" #include "zoneserver.h" #include "../common/eqemu_logsys.h" #include "../common/repositories/instance_list_repository.h" +extern ClientList client_list; extern ZSList zoneserver_list; Database& DynamicZone::GetDatabase() @@ -14,17 +16,72 @@ Database& DynamicZone::GetDatabase() return database; } +bool DynamicZone::SendServerPacket(ServerPacket* packet) +{ + return zoneserver_list.SendPacket(packet); +} + DynamicZone* DynamicZone::FindDynamicZoneByID(uint32_t dz_id) { - auto expedition = expedition_state.GetExpeditionByDynamicZoneID(dz_id); - if (expedition) + auto dz = dynamic_zone_manager.dynamic_zone_cache.find(dz_id); + if (dz != dynamic_zone_manager.dynamic_zone_cache.end()) { - return &expedition->GetDynamicZone(); + return dz->second.get(); } - // todo: other system caches return nullptr; } +void DynamicZone::ChooseNewLeader() +{ + if (m_members.empty() || !m_choose_leader_cooldown_timer.Check()) + { + m_choose_leader_needed = true; + return; + } + + auto it = std::find_if(m_members.begin(), m_members.end(), [&](const DynamicZoneMember& member) { + if (member.id != GetLeaderID() && member.IsOnline()) { + auto member_cle = client_list.FindCLEByCharacterID(member.id); + return (member_cle && member_cle->GetOnline() == CLE_Status::InZone); + } + return false; + }); + + if (it == m_members.end()) + { + // no online members found, fallback to choosing any member + it = std::find_if(m_members.begin(), m_members.end(), + [&](const DynamicZoneMember& member) { return member.id != GetLeaderID(); }); + } + + if (it != m_members.end() && SetNewLeader(it->id)) + { + m_choose_leader_needed = false; + } +} + +bool DynamicZone::SetNewLeader(uint32_t member_id) +{ + auto new_leader = GetMemberData(member_id); + if (!new_leader.IsValid()) + { + return false; + } + + LogDynamicZonesDetail("Replacing dz [{}] leader [{}] with [{}]", GetID(), GetLeaderName(), new_leader.name); + SetLeader(new_leader, true); + SendZonesLeaderChanged(); + return true; +} + +void DynamicZone::CheckLeader() +{ + if (m_choose_leader_needed) + { + ChooseNewLeader(); + } +} + DynamicZoneStatus DynamicZone::Process() { DynamicZoneStatus status = DynamicZoneStatus::Normal; @@ -47,9 +104,24 @@ DynamicZoneStatus DynamicZone::Process() } } + if (GetType() == DynamicZoneType::Expedition && status != DynamicZoneStatus::ExpiredEmpty) + { + CheckExpireWarning(); + CheckLeader(); + } + return status; } +void DynamicZone::SendZonesDynamicZoneDeleted() +{ + uint32_t pack_size = sizeof(ServerDzID_Struct); + auto pack = std::make_unique(ServerOP_DzDeleted, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + buf->dz_id = GetID(); + zoneserver_list.SendPacket(pack.get()); +} + void DynamicZone::SetSecondsRemaining(uint32_t seconds_remaining) { auto now = std::chrono::system_clock::now(); @@ -82,10 +154,25 @@ void DynamicZone::SendZonesDurationUpdate() zoneserver_list.SendPacket(pack.get()); } +void DynamicZone::SendZonesLeaderChanged() +{ + uint32_t pack_size = sizeof(ServerDzLeaderID_Struct); + auto pack = std::make_unique(ServerOP_DzLeaderChanged, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + buf->dz_id = GetID(); + buf->leader_id = GetLeaderID(); + zoneserver_list.SendPacket(pack.get()); +} + void DynamicZone::HandleZoneMessage(ServerPacket* pack) { switch (pack->opcode) { + case ServerOP_DzCreated: + { + dynamic_zone_manager.CacheNewDynamicZone(pack); + break; + } case ServerOP_DzSetCompass: case ServerOP_DzSetSafeReturn: case ServerOP_DzSetZoneIn: @@ -110,15 +197,41 @@ void DynamicZone::HandleZoneMessage(ServerPacket* pack) zoneserver_list.SendPacket(pack); break; } - case ServerOP_DzAddRemoveCharacter: - case ServerOP_DzRemoveAllCharacters: + case ServerOP_DzAddRemoveMember: { - auto buf = reinterpret_cast(pack->pBuffer); - ZoneServer* instance_zs = zoneserver_list.FindByInstanceID(buf->instance_id); - if (instance_zs) + auto buf = reinterpret_cast(pack->pBuffer); + auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); + if (dz) { - instance_zs->SendPacket(pack); + auto status = static_cast(buf->character_status); + dz->ProcessMemberAddRemove({ buf->character_id, buf->character_name, status }, buf->removed); } + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_DzSwapMembers: + { + auto buf = reinterpret_cast(pack->pBuffer); + auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); + if (dz) + { + // we add first in world so new member can be chosen if leader is removed + auto status = static_cast(buf->add_character_status); + dz->ProcessMemberAddRemove({ buf->add_character_id, buf->add_character_name, status }, false); + dz->ProcessMemberAddRemove({ buf->remove_character_id, buf->remove_character_name }, true); + } + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_DzRemoveAllMembers: + { + auto buf = reinterpret_cast(pack->pBuffer); + auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); + if (dz) + { + dz->ProcessRemoveAllMembers(); + } + zoneserver_list.SendPacket(pack); break; } case ServerOP_DzSetSecondsRemaining: @@ -131,31 +244,131 @@ void DynamicZone::HandleZoneMessage(ServerPacket* pack) } break; } + case ServerOP_DzGetMemberStatuses: + { + auto buf = reinterpret_cast(pack->pBuffer); + auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); + if (dz) + { + dz->SendZoneMemberStatuses(buf->sender_zone_id, buf->sender_instance_id); + } + break; + } + case ServerOP_DzUpdateMemberStatus: + { + auto buf = reinterpret_cast(pack->pBuffer); + auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); + if (dz) + { + auto status = static_cast(buf->status); + dz->ProcessMemberStatusChange(buf->character_id, status); + } + zoneserver_list.SendPacket(pack); + break; + } }; } -void DynamicZone::SendInstanceAddRemoveCharacter(uint32_t character_id, bool remove) +void DynamicZone::ProcessMemberAddRemove(const DynamicZoneMember& member, bool removed) { - ZoneServer* instance_zs = zoneserver_list.FindByInstanceID(GetInstanceID()); - if (instance_zs) + DynamicZoneBase::ProcessMemberAddRemove(member, removed); + + if (GetType() == DynamicZoneType::Expedition && removed && member.id == GetLeaderID()) { - auto pack = CreateServerAddRemoveCharacterPacket(character_id, remove); - instance_zs->SendPacket(pack.get()); + ChooseNewLeader(); } } -void DynamicZone::SendInstanceRemoveAllCharacters() +bool DynamicZone::ProcessMemberStatusChange(uint32_t character_id, DynamicZoneMemberStatus status) { - ZoneServer* instance_zs = zoneserver_list.FindByInstanceID(GetInstanceID()); - if (instance_zs) + bool changed = DynamicZoneBase::SetInternalMemberStatus(character_id, status); + if (changed && GetType() == DynamicZoneType::Expedition) { - auto pack = CreateServerRemoveAllCharactersPacket(); - instance_zs->SendPacket(pack.get()); + // any member status update will trigger a leader fix if leader was offline + if (GetLeader().status == DynamicZoneMemberStatus::Offline && GetMemberCount() > 1) + { + ChooseNewLeader(); + } } + return changed; } -void DynamicZone::SendGlobalLocationChange(uint16_t server_opcode, const DynamicZoneLocation& location) +void DynamicZone::SendZonesExpireWarning(uint32_t minutes_remaining) { - auto pack = CreateServerDzLocationPacket(server_opcode, location); + uint32_t pack_size = sizeof(ServerDzExpireWarning_Struct); + auto pack = std::make_unique(ServerOP_DzExpireWarning, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + buf->dz_id = GetID(); + buf->minutes_remaining = minutes_remaining; zoneserver_list.SendPacket(pack.get()); } + +void DynamicZone::SendZoneMemberStatuses(uint16_t zone_id, uint16_t instance_id) +{ + uint32_t members_count = static_cast(m_members.size()); + uint32_t entries_size = sizeof(ServerDzMemberStatusEntry_Struct) * members_count; + uint32_t pack_size = sizeof(ServerDzMemberStatuses_Struct) + entries_size; + auto pack = std::make_unique(ServerOP_DzGetMemberStatuses, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + buf->dz_id = GetID(); + buf->count = members_count; + + for (int i = 0; i < m_members.size(); ++i) + { + buf->entries[i].character_id = m_members[i].id; + buf->entries[i].online_status = static_cast(m_members[i].status); + } + + zoneserver_list.SendPacket(zone_id, instance_id, pack.get()); +} + +void DynamicZone::CacheMemberStatuses() +{ + if (m_has_member_statuses) + { + return; + } + + // called when a new dz is cached to fill member statuses + std::string zone_name{}; + std::vector all_clients; + all_clients.reserve(client_list.GetClientCount()); + client_list.GetClients(zone_name.c_str(), all_clients); + + for (const auto& member : m_members) + { + auto it = std::find_if(all_clients.begin(), all_clients.end(), + [&](const ClientListEntry* cle) { return (cle && cle->CharID() == member.id); }); + + auto status = DynamicZoneMemberStatus::Offline; + if (it != all_clients.end()) + { + status = DynamicZoneMemberStatus::Online; + if (IsSameDz((*it)->zone(), (*it)->instance())) + { + status = DynamicZoneMemberStatus::InDynamicZone; + } + } + + SetInternalMemberStatus(member.id, status); + } + + m_has_member_statuses = true; +} + +void DynamicZone::CheckExpireWarning() +{ + if (m_warning_cooldown_timer.Check(false)) + { + using namespace std::chrono_literals; + auto remaining = GetDurationRemaining(); + if ((remaining > 14min && remaining < 15min) || + (remaining > 4min && remaining < 5min) || + (remaining > 0min && remaining < 1min)) + { + int minutes = std::chrono::duration_cast(remaining).count() + 1; + SendZonesExpireWarning(minutes); + m_warning_cooldown_timer.Start(120000); // 2 minute cooldown after a warning + } + } +} diff --git a/world/dynamic_zone.h b/world/dynamic_zone.h index 597604f62..6a3d5c953 100644 --- a/world/dynamic_zone.h +++ b/world/dynamic_zone.h @@ -2,6 +2,8 @@ #define WORLD_DYNAMIC_ZONE_H #include "../common/dynamic_zone_base.h" +#include "../common/rulesys.h" +#include "../common/timer.h" class Database; class ServerPacket; @@ -24,18 +26,32 @@ public: void SetSecondsRemaining(uint32_t seconds_remaining) override; + void CacheMemberStatuses(); DynamicZoneStatus Process(); + bool SetNewLeader(uint32_t member_id); protected: Database& GetDatabase() override; - void SendInstanceAddRemoveCharacter(uint32_t character_id, bool remove) override; - void SendInstanceRemoveAllCharacters() override; - void SendGlobalLocationChange(uint16_t server_opcode, const DynamicZoneLocation& location) override; + void ProcessMemberAddRemove(const DynamicZoneMember& member, bool removed) override; + bool ProcessMemberStatusChange(uint32_t member_id, DynamicZoneMemberStatus status) override; + bool SendServerPacket(ServerPacket* packet) override; private: + friend class DynamicZoneManager; + + void CheckExpireWarning(); + void CheckLeader(); + void ChooseNewLeader(); + void SendZoneMemberStatuses(uint16_t zone_id, uint16_t instance_id); void SendZonesDurationUpdate(); + void SendZonesDynamicZoneDeleted(); + void SendZonesExpireWarning(uint32_t minutes_remaining); + void SendZonesLeaderChanged(); bool m_is_pending_early_shutdown = false; + bool m_choose_leader_needed = false; + Timer m_choose_leader_cooldown_timer{ static_cast(RuleI(Expedition, ChooseLeaderCooldownTime)) }; + Timer m_warning_cooldown_timer{ 1 }; // non-zero so it's enabled initially }; #endif diff --git a/world/dynamic_zone_manager.cpp b/world/dynamic_zone_manager.cpp new file mode 100644 index 000000000..e71ca5cdc --- /dev/null +++ b/world/dynamic_zone_manager.cpp @@ -0,0 +1,166 @@ +#include "dynamic_zone_manager.h" +#include "dynamic_zone.h" +#include "worlddb.h" +#include "zonelist.h" +#include "zoneserver.h" +#include "../common/rulesys.h" +#include "../common/repositories/expeditions_repository.h" +#include "../common/repositories/expedition_lockouts_repository.h" + +extern ZSList zoneserver_list; + +DynamicZoneManager dynamic_zone_manager; + +DynamicZoneManager::DynamicZoneManager() : + m_process_throttle_timer{ static_cast(RuleI(DynamicZone, WorldProcessRate)) } +{ +} + +void DynamicZoneManager::PurgeExpiredDynamicZones() +{ + // purge when no members, instance is expired, or instance doesn't exist. + // this prevents characters remaining members of dzs that expired while + // server was offline but delayed instance purging hasn't cleaned yet + auto dz_ids = DynamicZonesRepository::GetStaleIDs(database); + + if (!dz_ids.empty()) + { + LogDynamicZones("Purging [{}] dynamic zone(s)", dz_ids.size()); + + DynamicZoneMembersRepository::DeleteWhere(database, + fmt::format("dynamic_zone_id IN ({})", fmt::join(dz_ids, ","))); + DynamicZonesRepository::DeleteWhere(database, + fmt::format("id IN ({})", fmt::join(dz_ids, ","))); + } +} + +DynamicZone* DynamicZoneManager::CreateNew( + DynamicZone& dz_request, const std::vector& members) +{ + // this creates a new dz instance and saves it to both db and cache + uint32_t dz_id = dz_request.Create(); + if (dz_id == 0) + { + LogDynamicZones("Failed to create dynamic zone for zone [{}]", dz_request.GetZoneID()); + return nullptr; + } + + auto dz = std::make_unique(dz_request); + if (!members.empty()) + { + dz->SaveMembers(members); + dz->CacheMemberStatuses(); + } + + LogDynamicZones("Created new dz [{}] for zone [{}]", dz_id, dz_request.GetZoneID()); + + auto pack = dz->CreateServerDzCreatePacket(0, 0); + zoneserver_list.SendPacket(pack.get()); + + auto inserted = dynamic_zone_cache.emplace(dz_id, std::move(dz)); + return inserted.first->second.get(); +} + +void DynamicZoneManager::CacheNewDynamicZone(ServerPacket* pack) +{ + auto buf = reinterpret_cast(pack->pBuffer); + + auto new_dz = std::make_unique(); + new_dz->LoadSerializedDzPacket(buf->cereal_data, buf->cereal_size); + new_dz->CacheMemberStatuses(); + + // reserialize with member statuses cached before forwarding (restore origin zone) + auto repack = new_dz->CreateServerDzCreatePacket(buf->origin_zone_id, buf->origin_instance_id); + + uint32_t dz_id = new_dz->GetID(); + dynamic_zone_cache.emplace(dz_id, std::move(new_dz)); + LogDynamicZones("Cached new dynamic zone [{}]", dz_id); + + zoneserver_list.SendPacket(repack.get()); +} + +void DynamicZoneManager::CacheAllFromDatabase() +{ + BenchTimer bench; + + auto dynamic_zones = DynamicZonesRepository::AllWithInstanceNotExpired(database); + auto dynamic_zone_members = DynamicZoneMembersRepository::GetAllWithNames(database); + + dynamic_zone_cache.clear(); + dynamic_zone_cache.reserve(dynamic_zones.size()); + + for (auto& entry : dynamic_zones) + { + uint32_t dz_id = entry.id; + auto dz = std::make_unique(std::move(entry)); + + for (auto& member : dynamic_zone_members) + { + if (member.dynamic_zone_id == dz_id) + { + dz->AddMemberFromRepositoryResult(std::move(member)); + } + } + + // note leader status won't be updated here until leader is set by owning system (expeditions) + dz->CacheMemberStatuses(); + + dynamic_zone_cache.emplace(dz_id, std::move(dz)); + } + + LogDynamicZones("Caching [{}] dynamic zone(s) took [{}s]", dynamic_zone_cache.size(), bench.elapsed()); +} + +void DynamicZoneManager::Process() +{ + if (!m_process_throttle_timer.Check()) + { + return; + } + + std::vector dynamic_zone_ids; + + for (const auto& dz_iter : dynamic_zone_cache) + { + DynamicZone* dz = dz_iter.second.get(); + + // dynamic zone is not deleted until its zone has no clients to prevent exploits + // clients should be removed by zone-based kick timers if expired but not empty + DynamicZoneStatus status = dz->Process(); + if (status == DynamicZoneStatus::ExpiredEmpty) + { + LogDynamicZones("[{}] expired with [{}] members, notifying zones and deleting", dz->GetID(), dz->GetMemberCount()); + dynamic_zone_ids.emplace_back(dz->GetID()); + dz->SendZonesDynamicZoneDeleted(); // delete dz from zone caches + } + } + + if (!dynamic_zone_ids.empty()) + { + for (const auto& dz_id : dynamic_zone_ids) + { + dynamic_zone_cache.erase(dz_id); + } + + // need to look up expedition ids until lockouts are moved to dynamic zones + std::vector expedition_ids; + auto expeditions = ExpeditionsRepository::GetWhere(database, + fmt::format("dynamic_zone_id IN ({})", fmt::join(dynamic_zone_ids, ","))); + + if (!expeditions.empty()) + { + for (const auto& expedition : expeditions) + { + expedition_ids.emplace_back(expedition.id); + } + ExpeditionLockoutsRepository::DeleteWhere(database, + fmt::format("expedition_id IN ({})", fmt::join(expedition_ids, ","))); + } + + ExpeditionsRepository::DeleteWhere(database, + fmt::format("dynamic_zone_id IN ({})", fmt::join(dynamic_zone_ids, ","))); + DynamicZoneMembersRepository::RemoveAllMembers(database, dynamic_zone_ids); + DynamicZonesRepository::DeleteWhere(database, + fmt::format("id IN ({})", fmt::join(dynamic_zone_ids, ","))); + } +} diff --git a/world/dynamic_zone_manager.h b/world/dynamic_zone_manager.h new file mode 100644 index 000000000..ce59f62ee --- /dev/null +++ b/world/dynamic_zone_manager.h @@ -0,0 +1,32 @@ +#ifndef WORLD_DYNAMIC_ZONE_MANAGER_H +#define WORLD_DYNAMIC_ZONE_MANAGER_H + +#include "../common/timer.h" +#include +#include +#include + +extern class DynamicZoneManager dynamic_zone_manager; + +class DynamicZone; +struct DynamicZoneMember; +class ServerPacket; + +class DynamicZoneManager +{ +public: + DynamicZoneManager(); + + void CacheAllFromDatabase(); + void CacheNewDynamicZone(ServerPacket* pack); + DynamicZone* CreateNew(DynamicZone& dz_request, const std::vector& members); + void Process(); + void PurgeExpiredDynamicZones(); + + std::unordered_map> dynamic_zone_cache; + +private: + Timer m_process_throttle_timer{}; +}; + +#endif diff --git a/world/expedition.cpp b/world/expedition.cpp deleted file mode 100644 index 4cc82ca03..000000000 --- a/world/expedition.cpp +++ /dev/null @@ -1,237 +0,0 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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 - * - */ - -#include "expedition.h" -#include "expedition_database.h" -#include "cliententry.h" -#include "clientlist.h" -#include "zonelist.h" -#include "zoneserver.h" -#include "../common/eqemu_logsys.h" -#include "../common/rulesys.h" - -extern ClientList client_list; -extern ZSList zoneserver_list; - -Expedition::Expedition() : - m_choose_leader_cooldown_timer{ static_cast(RuleI(Expedition, ChooseLeaderCooldownTime)) } -{ - m_warning_cooldown_timer.Enable(); -} - -void Expedition::SetDynamicZone(DynamicZone&& dz) -{ - dz.SetName(GetName()); - dz.SetLeader(GetLeader()); - - m_dynamic_zone = std::move(dz); -} - -void Expedition::RemoveMember(uint32_t character_id) -{ - GetDynamicZone().RemoveInternalMember(character_id); - - if (character_id == m_leader.id) - { - ChooseNewLeader(); - } -} - -void Expedition::ChooseNewLeader() -{ - const auto& members = GetDynamicZone().GetMembers(); - if (members.empty() || !m_choose_leader_cooldown_timer.Check()) - { - m_choose_leader_needed = true; - return; - } - - auto it = std::find_if(members.begin(), members.end(), [&](const DynamicZoneMember& member) { - if (member.id != m_leader.id && member.IsOnline()) { - auto member_cle = client_list.FindCLEByCharacterID(member.id); - return (member_cle && member_cle->GetOnline() == CLE_Status::InZone); - } - return false; - }); - - if (it == members.end()) - { - // no online members found, fallback to choosing any member - it = std::find_if(members.begin(), members.end(), - [&](const DynamicZoneMember& member) { return (member.id != m_leader.id); }); - } - - if (it != members.end() && SetNewLeader(*it)) - { - m_choose_leader_needed = false; - } -} - -bool Expedition::SetNewLeader(const DynamicZoneMember& member) -{ - if (!GetDynamicZone().HasMember(member.id)) - { - return false; - } - - LogExpeditionsModerate("Replacing [{}] leader [{}] with [{}]", m_id, m_leader.name, member.name); - ExpeditionDatabase::UpdateLeaderID(m_id, member.id); - m_leader = member; - m_dynamic_zone.SetLeader(m_leader); - SendZonesLeaderChanged(); - return true; -} - -void Expedition::SendZonesExpeditionDeleted() -{ - uint32_t pack_size = sizeof(ServerExpeditionID_Struct); - auto pack = std::make_unique(ServerOP_ExpeditionDeleted, pack_size); - auto buf = reinterpret_cast(pack->pBuffer); - buf->expedition_id = GetID(); - zoneserver_list.SendPacket(pack.get()); -} - -void Expedition::SendZonesExpireWarning(uint32_t minutes_remaining) -{ - uint32_t pack_size = sizeof(ServerExpeditionExpireWarning_Struct); - auto pack = std::make_unique(ServerOP_ExpeditionExpireWarning, pack_size); - auto buf = reinterpret_cast(pack->pBuffer); - buf->expedition_id = GetID(); - buf->minutes_remaining = minutes_remaining; - zoneserver_list.SendPacket(pack.get()); -} - -void Expedition::SendZonesLeaderChanged() -{ - uint32_t pack_size = sizeof(ServerExpeditionLeaderID_Struct); - auto pack = std::make_unique(ServerOP_ExpeditionLeaderChanged, pack_size); - auto buf = reinterpret_cast(pack->pBuffer); - buf->expedition_id = GetID(); - buf->leader_id = m_leader.id; - zoneserver_list.SendPacket(pack.get()); -} - -void Expedition::CheckExpireWarning() -{ - if (m_warning_cooldown_timer.Check(false)) - { - using namespace std::chrono_literals; - auto remaining = GetDynamicZone().GetDurationRemaining(); - if ((remaining > 14min && remaining < 15min) || - (remaining > 4min && remaining < 5min) || - (remaining > 0min && remaining < 1min)) - { - int minutes = std::chrono::duration_cast(remaining).count() + 1; - SendZonesExpireWarning(minutes); - m_warning_cooldown_timer.Start(70000); // 1 minute 10 seconds - } - } -} - -void Expedition::CheckLeader() -{ - if (m_choose_leader_needed) - { - ChooseNewLeader(); - } -} - -bool Expedition::Process() -{ - // returns true if expedition needs to be deleted from world cache and db - // expedition is not deleted until its dz has no clients to prevent exploits - auto status = m_dynamic_zone.Process(); - if (status == DynamicZoneStatus::ExpiredEmpty) - { - LogExpeditions("Expedition [{}] expired or empty, notifying zones and deleting", GetID()); - SendZonesExpeditionDeleted(); - return true; - } - - CheckExpireWarning(); - CheckLeader(); - - return false; -} - -void Expedition::UpdateMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status) -{ - GetDynamicZone().SetInternalMemberStatus(character_id, status); - - // temporary until move to using dz leader object completely - if (character_id == m_leader.id) - { - m_leader.status = GetDynamicZone().GetLeader().status; - } - - // any member status update will trigger a leader fix if leader was offline - if (m_leader.status == DynamicZoneMemberStatus::Offline && GetDynamicZone().GetMemberCount() > 1) - { - ChooseNewLeader(); - } -} - -void Expedition::SendZoneMemberStatuses(uint16_t zone_id, uint16_t instance_id) -{ - const auto& members = GetDynamicZone().GetMembers(); - - uint32_t members_count = static_cast(members.size()); - uint32_t entries_size = sizeof(ServerExpeditionMemberStatusEntry_Struct) * members_count; - uint32_t pack_size = sizeof(ServerExpeditionMemberStatuses_Struct) + entries_size; - auto pack = std::make_unique(ServerOP_ExpeditionGetMemberStatuses, pack_size); - auto buf = reinterpret_cast(pack->pBuffer); - buf->expedition_id = GetID(); - buf->count = members_count; - - for (int i = 0; i < members.size(); ++i) - { - buf->entries[i].character_id = members[i].id; - buf->entries[i].online_status = static_cast(members[i].status); - } - - zoneserver_list.SendPacket(zone_id, instance_id, pack.get()); -} - -void Expedition::CacheMemberStatuses() -{ - // called when a new expedition is cached to fill member statuses - std::string zone_name{}; - std::vector all_clients; - all_clients.reserve(client_list.GetClientCount()); - client_list.GetClients(zone_name.c_str(), all_clients); - - for (const auto& member : GetDynamicZone().GetMembers()) - { - auto it = std::find_if(all_clients.begin(), all_clients.end(), - [&](const ClientListEntry* cle) { return (cle && cle->CharID() == member.id); }); - - auto status = DynamicZoneMemberStatus::Offline; - if (it != all_clients.end()) - { - status = DynamicZoneMemberStatus::Online; - if (GetDynamicZone().IsSameDz((*it)->zone(), (*it)->instance())) - { - status = DynamicZoneMemberStatus::InDynamicZone; - } - } - - GetDynamicZone().SetInternalMemberStatus(member.id, status); - } -} diff --git a/world/expedition.h b/world/expedition.h deleted file mode 100644 index 119f78685..000000000 --- a/world/expedition.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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 WORLD_EXPEDITION_H -#define WORLD_EXPEDITION_H - -#include "dynamic_zone.h" -#include "../common/expedition_base.h" -#include "../common/timer.h" -#include - -class Expedition : public ExpeditionBase -{ -public: - Expedition(); - - void RemoveMember(uint32_t character_id); - void CacheMemberStatuses(); - void CheckExpireWarning(); - void CheckLeader(); - void ChooseNewLeader(); - DynamicZone& GetDynamicZone() { return m_dynamic_zone; } - bool Process(); - void SendZoneMemberStatuses(uint16_t zone_id, uint16_t instance_id); - void SendZonesExpeditionDeleted(); - void SendZonesExpireWarning(uint32_t minutes_remaining); - void SetDynamicZone(DynamicZone&& dz); - bool SetNewLeader(const DynamicZoneMember& member); - void UpdateMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status); - -private: - void SendZonesLeaderChanged(); - - bool m_choose_leader_needed = false; - Timer m_choose_leader_cooldown_timer; - Timer m_warning_cooldown_timer; - DynamicZone m_dynamic_zone; -}; - -#endif diff --git a/world/expedition_database.cpp b/world/expedition_database.cpp index 5c51a9e70..a3c4ca4d0 100644 --- a/world/expedition_database.cpp +++ b/world/expedition_database.cpp @@ -19,8 +19,9 @@ */ #include "expedition_database.h" -#include "expedition.h" #include "worlddb.h" +#include "../common/repositories/expeditions_repository.h" +#include "../common/repositories/expedition_lockouts_repository.h" #include "../common/repositories/dynamic_zone_members_repository.h" void ExpeditionDatabase::PurgeExpiredExpeditions() @@ -34,7 +35,7 @@ void ExpeditionDatabase::PurgeExpiredExpeditions() LEFT JOIN instance_list ON dynamic_zones.instance_id = instance_list.id LEFT JOIN ( - SELECT dynamic_zone_id, COUNT(IF(is_current_member = TRUE, 1, NULL)) member_count + SELECT dynamic_zone_id, COUNT(*) member_count FROM dynamic_zone_members GROUP BY dynamic_zone_id ) dynamic_zone_members @@ -59,8 +60,9 @@ void ExpeditionDatabase::PurgeExpiredExpeditions() if (!expedition_ids.empty()) { - ExpeditionDatabase::MoveMembersToSafeReturn(expedition_ids); - ExpeditionDatabase::DeleteExpeditions(expedition_ids); + auto joined_expedition_ids = fmt::join(expedition_ids, ","); + ExpeditionsRepository::DeleteWhere(database, fmt::format("id IN ({})", joined_expedition_ids)); + ExpeditionLockoutsRepository::DeleteWhere(database, fmt::format("expedition_id IN ({})", joined_expedition_ids)); DynamicZoneMembersRepository::RemoveAllMembers(database, dynamic_zone_ids); } } @@ -75,56 +77,3 @@ void ExpeditionDatabase::PurgeExpiredCharacterLockouts() database.QueryDatabase(query); } - -void ExpeditionDatabase::DeleteExpeditions(const std::vector& expedition_ids) -{ - LogExpeditionsDetail("Deleting [{}] expedition(s)", expedition_ids.size()); - - std::string expedition_ids_query = fmt::format("{}", fmt::join(expedition_ids, ",")); - - if (!expedition_ids_query.empty()) - { - auto query = fmt::format("DELETE FROM expeditions WHERE id IN ({});", expedition_ids_query); - database.QueryDatabase(query); - - query = fmt::format("DELETE FROM expedition_lockouts WHERE expedition_id IN ({});", expedition_ids_query); - database.QueryDatabase(query); - } -} - -void ExpeditionDatabase::UpdateLeaderID(uint32_t expedition_id, uint32_t leader_id) -{ - LogExpeditionsDetail("Updating leader [{}] for expedition [{}]", leader_id, expedition_id); - - auto query = fmt::format(SQL( - UPDATE expeditions SET leader_id = {} WHERE id = {}; - ), leader_id, expedition_id); - - database.QueryDatabase(query); -} - -void ExpeditionDatabase::MoveMembersToSafeReturn(const std::vector& expedition_ids) -{ - LogExpeditionsDetail("Moving members from [{}] expedition(s) to safereturn", expedition_ids.size()); - - // only offline members still in expired dz zones should be updated here - std::string query = fmt::format(SQL( - UPDATE character_data - INNER JOIN dynamic_zone_members ON character_data.id = dynamic_zone_members.character_id - INNER JOIN expeditions ON dynamic_zone_members.dynamic_zone_id = expeditions.dynamic_zone_id - INNER JOIN dynamic_zones ON expeditions.dynamic_zone_id = dynamic_zones.id - INNER JOIN instance_list ON dynamic_zones.instance_id = instance_list.id - AND character_data.zone_instance = instance_list.id - AND character_data.zone_id = instance_list.zone - SET - zone_id = IF(safe_return_zone_id > 0, safe_return_zone_id, zone_id), - zone_instance = IF(safe_return_zone_id > 0, 0, zone_instance), - x = IF(safe_return_zone_id > 0, safe_return_x, x), - y = IF(safe_return_zone_id > 0, safe_return_y, y), - z = IF(safe_return_zone_id > 0, safe_return_z, z), - heading = IF(safe_return_zone_id > 0, safe_return_heading, heading) - WHERE expeditions.id IN ({}); - ), fmt::join(expedition_ids, ",")); - - database.QueryDatabase(query); -} diff --git a/world/expedition_database.h b/world/expedition_database.h index b86fd8b5d..a885dfc83 100644 --- a/world/expedition_database.h +++ b/world/expedition_database.h @@ -28,11 +28,8 @@ class Expedition; namespace ExpeditionDatabase { - void DeleteExpeditions(const std::vector& expedition_ids); - void MoveMembersToSafeReturn(const std::vector& expedition_ids); void PurgeExpiredExpeditions(); void PurgeExpiredCharacterLockouts(); - void UpdateLeaderID(uint32_t expedition_id, uint32_t leader_id); }; #endif diff --git a/world/expedition_message.cpp b/world/expedition_message.cpp index 36eee5ceb..93f6ccd6e 100644 --- a/world/expedition_message.cpp +++ b/world/expedition_message.cpp @@ -18,9 +18,8 @@ * */ -#include "expedition.h" +#include "dynamic_zone.h" #include "expedition_message.h" -#include "expedition_state.h" #include "cliententry.h" #include "clientlist.h" #include "zonelist.h" @@ -37,50 +36,9 @@ void ExpeditionMessage::HandleZoneMessage(ServerPacket* pack) { case ServerOP_ExpeditionCreate: { - auto buf = reinterpret_cast(pack->pBuffer); - expedition_state.CacheFromDatabase(buf->expedition_id); zoneserver_list.SendPacket(pack); break; } - case ServerOP_ExpeditionMemberChange: - { - auto buf = reinterpret_cast(pack->pBuffer); - expedition_state.MemberChange(buf->expedition_id, { buf->char_id, buf->char_name }, buf->removed); - zoneserver_list.SendPacket(pack); - break; - } - case ServerOP_ExpeditionMemberSwap: - { - auto buf = reinterpret_cast(pack->pBuffer); - expedition_state.MemberChange(buf->expedition_id, { buf->add_char_id, buf->add_char_name }, false); - expedition_state.MemberChange(buf->expedition_id, { buf->remove_char_id, buf->remove_char_name }, true); - zoneserver_list.SendPacket(pack); - break; - } - case ServerOP_ExpeditionMembersRemoved: - { - auto buf = reinterpret_cast(pack->pBuffer); - expedition_state.RemoveAllMembers(buf->expedition_id); - zoneserver_list.SendPacket(pack); - break; - } - case ServerOP_ExpeditionMemberStatus: - { - auto buf = reinterpret_cast(pack->pBuffer); - auto expedition = expedition_state.GetExpedition(buf->expedition_id); - if (expedition) - { - auto status = static_cast(buf->status); - expedition->UpdateMemberStatus(buf->character_id, status); - } - zoneserver_list.SendPacket(pack); - break; - } - case ServerOP_ExpeditionGetMemberStatuses: - { - ExpeditionMessage::GetMemberStatuses(pack); - break; - } case ServerOP_ExpeditionDzAddPlayer: { ExpeditionMessage::AddPlayer(pack); @@ -145,10 +103,10 @@ void ExpeditionMessage::MakeLeader(ServerPacket* pack) ClientListEntry* new_leader_cle = client_list.FindCharacter(buf->new_leader_name); if (new_leader_cle && new_leader_cle->Server()) { - auto expedition = expedition_state.GetExpedition(buf->expedition_id); - if (expedition) + auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); + if (dz && dz->GetLeaderID() == buf->requester_id) { - buf->is_success = expedition->SetNewLeader({ new_leader_cle->CharID(), new_leader_cle->name() }); + buf->is_success = dz->SetNewLeader(new_leader_cle->CharID()); } buf->is_online = true; @@ -164,16 +122,6 @@ void ExpeditionMessage::MakeLeader(ServerPacket* pack) } } -void ExpeditionMessage::GetMemberStatuses(ServerPacket* pack) -{ - auto buf = reinterpret_cast(pack->pBuffer); - auto expedition = expedition_state.GetExpedition(buf->expedition_id); - if (expedition) - { - expedition->SendZoneMemberStatuses(buf->sender_zone_id, buf->sender_instance_id); - } -} - void ExpeditionMessage::SaveInvite(ServerPacket* pack) { auto buf = reinterpret_cast(pack->pBuffer); diff --git a/world/expedition_message.h b/world/expedition_message.h index 135ef0cba..b97651dbc 100644 --- a/world/expedition_message.h +++ b/world/expedition_message.h @@ -26,7 +26,6 @@ class ServerPacket; namespace ExpeditionMessage { void AddPlayer(ServerPacket* pack); - void GetMemberStatuses(ServerPacket* pack); void HandleZoneMessage(ServerPacket* pack); void MakeLeader(ServerPacket* pack); void RequestInvite(ServerPacket* pack); diff --git a/world/expedition_state.cpp b/world/expedition_state.cpp deleted file mode 100644 index a0cd245fa..000000000 --- a/world/expedition_state.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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 - * - */ - -#include "expedition_state.h" -#include "expedition.h" -#include "expedition_database.h" -#include "worlddb.h" -#include "../common/dynamic_zone_base.h" -#include "../common/eqemu_logsys.h" -#include "../common/repositories/dynamic_zone_members_repository.h" -#include - -ExpeditionState expedition_state; - -Expedition* ExpeditionState::GetExpedition(uint32_t expedition_id) -{ - auto it = std::find_if(m_expeditions.begin(), m_expeditions.end(), - [&](const std::unique_ptr& expedition) { - return expedition->GetID() == expedition_id; - }); - - return (it != m_expeditions.end()) ? it->get() : nullptr; -} - -Expedition* ExpeditionState::GetExpeditionByDynamicZoneID(uint32_t dz_id) -{ - auto it = std::find_if(m_expeditions.begin(), m_expeditions.end(), - [&](const std::unique_ptr& expedition) { - return expedition->GetDynamicZone().GetID() == dz_id; - }); - - return (it != m_expeditions.end()) ? it->get() : nullptr; -} - -void ExpeditionState::CacheFromDatabase(uint32_t expedition_id) -{ - if (expedition_id == 0 || GetExpedition(expedition_id)) - { - return; - } - - auto expedition = ExpeditionsRepository::GetWithLeaderName(database, expedition_id); - CacheExpeditions({ std::move(expedition) }); -} - -void ExpeditionState::CacheAllFromDatabase() -{ - BenchTimer benchmark; - - auto expeditions = ExpeditionsRepository::GetAllWithLeaderName(database); - m_expeditions.clear(); - m_expeditions.reserve(expeditions.size()); - - CacheExpeditions(std::move(expeditions)); - - LogExpeditions("Caching [{}] expedition(s) took [{}s]", m_expeditions.size(), benchmark.elapsed()); -} - -void ExpeditionState::CacheExpeditions( - std::vector&& expedition_entries) -{ - // bulk load expedition dzs and members before caching - std::vector dynamic_zone_ids; - for (const auto& entry : expedition_entries) - { - dynamic_zone_ids.emplace_back(entry.dynamic_zone_id); - } - - auto dynamic_zones = DynamicZonesRepository::GetWithInstance(database, dynamic_zone_ids); - auto dynamic_zone_members = DynamicZoneMembersRepository::GetWithNames(database, dynamic_zone_ids); - - for (auto& entry : expedition_entries) - { - auto expedition = std::make_unique(); - expedition->LoadRepositoryResult(std::move(entry)); - - auto dz_entry_iter = std::find_if(dynamic_zones.begin(), dynamic_zones.end(), - [&](const DynamicZonesRepository::DynamicZoneInstance& dz_entry) { - return dz_entry.id == entry.dynamic_zone_id; - }); - - if (dz_entry_iter != dynamic_zones.end()) - { - expedition->SetDynamicZone(std::move(*dz_entry_iter)); - } - - for (auto& member : dynamic_zone_members) - { - if (member.dynamic_zone_id == entry.dynamic_zone_id) - { - expedition->GetDynamicZone().AddMemberFromRepositoryResult(std::move(member)); - } - } - - // stored on expedition in db but on dz in memory cache - expedition->GetDynamicZone().SetMinPlayers(entry.min_players); - expedition->GetDynamicZone().SetMaxPlayers(entry.max_players); - - expedition->CacheMemberStatuses(); - - m_expeditions.emplace_back(std::move(expedition)); - } -} - -void ExpeditionState::MemberChange( - uint32_t expedition_id, const DynamicZoneMember& member, bool remove) -{ - auto expedition = GetExpedition(expedition_id); - if (expedition) - { - if (remove) { - expedition->RemoveMember(member.id); - } else { - expedition->GetDynamicZone().AddInternalMember(member); - } - } -} - -void ExpeditionState::RemoveAllMembers(uint32_t expedition_id) -{ - auto expedition = GetExpedition(expedition_id); - if (expedition) - { - expedition->GetDynamicZone().ClearInternalMembers(); - } -} - -void ExpeditionState::Process() -{ - if (!m_process_throttle_timer.Check()) - { - return; - } - - std::vector expedition_ids; - std::vector dynamic_zone_ids; - - for (auto it = m_expeditions.begin(); it != m_expeditions.end();) - { - bool is_deleted = (*it)->Process(); - if (is_deleted) - { - expedition_ids.emplace_back((*it)->GetID()); - dynamic_zone_ids.emplace_back((*it)->GetDynamicZone().GetID()); - } - it = is_deleted ? m_expeditions.erase(it) : it + 1; - } - - if (!expedition_ids.empty()) - { - ExpeditionDatabase::MoveMembersToSafeReturn(expedition_ids); - ExpeditionDatabase::DeleteExpeditions(expedition_ids); - DynamicZoneMembersRepository::RemoveAllMembers(database, dynamic_zone_ids); - } -} diff --git a/world/expedition_state.h b/world/expedition_state.h deleted file mode 100644 index 90f4a4293..000000000 --- a/world/expedition_state.h +++ /dev/null @@ -1,52 +0,0 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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 WORLD_EXPEDITION_STATE_H -#define WORLD_EXPEDITION_STATE_H - -#include "../common/repositories/expeditions_repository.h" -#include "../common/rulesys.h" -#include "../common/timer.h" -#include -#include - -extern class ExpeditionState expedition_state; - -class Expedition; -struct DynamicZoneMember; - -class ExpeditionState -{ -public: - void CacheExpeditions(std::vector&& expedition_entries); - void CacheFromDatabase(uint32_t expedition_id); - void CacheAllFromDatabase(); - Expedition* GetExpedition(uint32_t expedition_id); - Expedition* GetExpeditionByDynamicZoneID(uint32_t dz_id); - void MemberChange(uint32_t expedition_id, const DynamicZoneMember& member, bool remove); - void Process(); - void RemoveAllMembers(uint32_t expedition_id); - -private: - std::vector> m_expeditions; - Timer m_process_throttle_timer{static_cast(RuleI(DynamicZone, WorldProcessRate))}; -}; - -#endif diff --git a/world/main.cpp b/world/main.cpp index d36561544..7d3da9bfd 100644 --- a/world/main.cpp +++ b/world/main.cpp @@ -46,6 +46,7 @@ #include "../common/crash.h" #include "client.h" #include "worlddb.h" + #ifdef _WINDOWS #include #define snprintf _snprintf @@ -53,12 +54,14 @@ #define strcasecmp _stricmp #include #else + #include #include "../common/unix.h" #include #include #include #include + #if not defined (FREEBSD) && not defined (DARWIN) union semun { int val; @@ -88,41 +91,45 @@ union semun { #include "queryserv.h" #include "web_interface.h" #include "console.h" +#include "dynamic_zone_manager.h" #include "expedition_database.h" -#include "expedition_state.h" #include "../common/net/servertalk_server.h" #include "../zone/data_bucket.h" #include "world_server_command_handler.h" #include "../common/content/world_content_service.h" +#include "../common/repositories/character_task_timers_repository.h" #include "../common/repositories/merchantlist_temp_repository.h" #include "world_store.h" #include "world_event_scheduler.h" +#include "shared_task_manager.h" -WorldStore world_store; -ClientList client_list; -GroupLFPList LFPGroupList; -ZSList zoneserver_list; -LoginServerList loginserverlist; -UCSConnection UCSLink; +WorldStore world_store; +ClientList client_list; +GroupLFPList LFPGroupList; +ZSList zoneserver_list; +LoginServerList loginserverlist; +UCSConnection UCSLink; QueryServConnection QSLink; -LauncherList launcher_list; -AdventureManager adventure_manager; +LauncherList launcher_list; +AdventureManager adventure_manager; WorldEventScheduler event_scheduler; -EQ::Random emu_random; -volatile bool RunLoops = true; -uint32 numclients = 0; -uint32 numzones = 0; -bool holdzones = false; -const WorldConfig *Config; -EQEmuLogSys LogSys; +SharedTaskManager shared_task_manager; +EQ::Random emu_random; +volatile bool RunLoops = true; +uint32 numclients = 0; +uint32 numzones = 0; +bool holdzones = false; +const WorldConfig *Config; +EQEmuLogSys LogSys; WorldContentService content_service; -WebInterfaceList web_interface; +WebInterfaceList web_interface; void CatchSignal(int sig_num); void CheckForServerScript(bool force_download = false); -inline void UpdateWindowTitle(std::string new_title) { +inline void UpdateWindowTitle(std::string new_title) +{ #ifdef _WINDOWS SetConsoleTitle(new_title.c_str()); #endif @@ -154,7 +161,7 @@ void LoadDatabaseConnections() */ if (!Config->ContentDbHost.empty()) { if (!content_db.Connect( - Config->ContentDbHost.c_str() , + Config->ContentDbHost.c_str(), Config->ContentDbUsername.c_str(), Config->ContentDbPassword.c_str(), Config->ContentDbName.c_str(), @@ -164,7 +171,8 @@ void LoadDatabaseConnections() LogError("Cannot continue without a content database connection"); std::exit(1); } - } else { + } + else { content_db.SetMysql(database.getMySQL()); } @@ -174,7 +182,7 @@ void CheckForXMLConfigUpgrade() { if (!std::ifstream("eqemu_config.json") && std::ifstream("eqemu_config.xml")) { CheckForServerScript(true); - if(system("perl eqemu_server.pl convert_xml")); + if (system("perl eqemu_server.pl convert_xml")) {} } else { CheckForServerScript(); @@ -205,7 +213,7 @@ void RegisterLoginservers() } } else { - LinkedList loginlist = Config->loginlist; + LinkedList loginlist = Config->loginlist; LinkedListIterator iterator(loginlist); iterator.Reset(); while (iterator.MoreElements()) { @@ -274,7 +282,8 @@ static void GMSayHookCallBackProcessWorld(uint16 log_category, std::string messa * @param argv * @return */ -int main(int argc, char** argv) { +int main(int argc, char **argv) +{ RegisterExecutablePlatform(ExePlatformWorld); LogSys.LoadLogSettingsDefaults(); set_exception_handler(); @@ -445,7 +454,7 @@ int main(int argc, char** argv) { LogInfo("Loading EQ time of day"); TimeOfDay_Struct eqTime; - time_t realtime; + time_t realtime; eqTime = database.LoadTime(realtime); zoneserver_list.worldclock.SetCurrentEQTimeOfDay(eqTime, realtime); Timer EQTimeTimer(600000); @@ -457,37 +466,50 @@ int main(int argc, char** argv) { LogInfo("Deleted [{}] stale player corpses from database", database.DeleteStalePlayerCorpses()); LogInfo("Loading adventures"); - if (!adventure_manager.LoadAdventureTemplates()) - { + if (!adventure_manager.LoadAdventureTemplates()) { LogInfo("Unable to load adventure templates"); } - if (!adventure_manager.LoadAdventureEntries()) - { + if (!adventure_manager.LoadAdventureEntries()) { LogInfo("Unable to load adventure templates"); } adventure_manager.LoadLeaderboardInfo(); + LogInfo("Purging expired dynamic zones and members"); + dynamic_zone_manager.PurgeExpiredDynamicZones(); + LogInfo("Purging expired expeditions"); ExpeditionDatabase::PurgeExpiredExpeditions(); ExpeditionDatabase::PurgeExpiredCharacterLockouts(); + LogInfo("Purging expired character task timers"); + CharacterTaskTimersRepository::DeleteWhere(database, "expire_time <= NOW()"); + LogInfo("Purging expired instances"); database.PurgeExpiredInstances(); Timer PurgeInstanceTimer(450000); PurgeInstanceTimer.Start(450000); - LogInfo("Loading active expeditions"); - expedition_state.CacheAllFromDatabase(); + LogInfo("Loading dynamic zones"); + dynamic_zone_manager.CacheAllFromDatabase(); LogInfo("Loading char create info"); content_db.LoadCharacterCreateAllocations(); content_db.LoadCharacterCreateCombos(); + LogInfo("Initializing [EventScheduler]"); event_scheduler.SetDatabase(&database)->LoadScheduledEvents(); + LogInfo("Initializing [SharedTaskManager]"); + shared_task_manager.SetDatabase(&database) + ->SetContentDatabase(&content_db) + ->LoadTaskData() + ->LoadSharedTaskState(); + + shared_task_manager.PurgeExpiredSharedTasks(); + std::unique_ptr console; if (Config->TelnetEnabled) { LogInfo("Console (TCP) listener started"); @@ -500,8 +522,8 @@ int main(int argc, char** argv) { server_connection = std::make_unique(); EQ::Net::ServertalkServerOptions server_opts; - server_opts.port = Config->WorldTCPPort; - server_opts.ipv6 = false; + server_opts.port = Config->WorldTCPPort; + server_opts.ipv6 = false; server_opts.credentials = Config->SharedKey; server_connection->Listen(server_opts); LogInfo("Server (TCP) listener started"); @@ -516,50 +538,62 @@ int main(int argc, char** argv) { } ); - server_connection->OnConnectionRemoved("Zone", [](std::shared_ptr connection) { - LogInfo("Removed Zone Server connection from [{0}]", - connection->GetUUID()); + server_connection->OnConnectionRemoved( + "Zone", [](std::shared_ptr connection) { + LogInfo("Removed Zone Server connection from [{0}]", + connection->GetUUID()); - numzones--; - zoneserver_list.Remove(connection->GetUUID()); - }); + numzones--; + zoneserver_list.Remove(connection->GetUUID()); + } + ); - server_connection->OnConnectionIdentified("Launcher", [](std::shared_ptr connection) { - LogInfo("New Launcher connection from [{2}] at [{0}:{1}]", - connection->Handle()->RemoteIP(), connection->Handle()->RemotePort(), connection->GetUUID()); + server_connection->OnConnectionIdentified( + "Launcher", [](std::shared_ptr connection) { + LogInfo("New Launcher connection from [{2}] at [{0}:{1}]", + connection->Handle()->RemoteIP(), connection->Handle()->RemotePort(), connection->GetUUID()); - launcher_list.Add(connection); - }); + launcher_list.Add(connection); + } + ); - server_connection->OnConnectionRemoved("Launcher", [](std::shared_ptr connection) { - LogInfo("Removed Launcher connection from [{0}]", - connection->GetUUID()); + server_connection->OnConnectionRemoved( + "Launcher", [](std::shared_ptr connection) { + LogInfo("Removed Launcher connection from [{0}]", + connection->GetUUID()); - launcher_list.Remove(connection); - }); + launcher_list.Remove(connection); + } + ); - server_connection->OnConnectionIdentified("QueryServ", [](std::shared_ptr connection) { - LogInfo("New Query Server connection from [{2}] at [{0}:{1}]", - connection->Handle()->RemoteIP(), connection->Handle()->RemotePort(), connection->GetUUID()); + server_connection->OnConnectionIdentified( + "QueryServ", [](std::shared_ptr connection) { + LogInfo("New Query Server connection from [{2}] at [{0}:{1}]", + connection->Handle()->RemoteIP(), connection->Handle()->RemotePort(), connection->GetUUID()); - QSLink.AddConnection(connection); - }); + QSLink.AddConnection(connection); + } + ); - server_connection->OnConnectionRemoved("QueryServ", [](std::shared_ptr connection) { - LogInfo("Removed Query Server connection from [{0}]", - connection->GetUUID()); + server_connection->OnConnectionRemoved( + "QueryServ", [](std::shared_ptr connection) { + LogInfo("Removed Query Server connection from [{0}]", + connection->GetUUID()); - QSLink.RemoveConnection(connection); - }); + QSLink.RemoveConnection(connection); + } + ); - server_connection->OnConnectionIdentified("UCS", [](std::shared_ptr connection) { - LogInfo("New UCS Server connection from [{2}] at [{0}:{1}]", - connection->Handle()->RemoteIP(), connection->Handle()->RemotePort(), connection->GetUUID()); + server_connection->OnConnectionIdentified( + "UCS", [](std::shared_ptr connection) { + LogInfo("New UCS Server connection from [{2}] at [{0}:{1}]", + connection->Handle()->RemoteIP(), connection->Handle()->RemotePort(), connection->GetUUID()); - UCSLink.SetConnection(connection); + UCSLink.SetConnection(connection); - zoneserver_list.UpdateUCSServerAvailable(); - }); + zoneserver_list.UpdateUCSServerAvailable(); + } + ); server_connection->OnConnectionRemoved( "UCS", [](std::shared_ptr connection) { @@ -575,26 +609,30 @@ int main(int argc, char** argv) { } ); - server_connection->OnConnectionIdentified("WebInterface", [](std::shared_ptr connection) { - LogInfo("New WebInterface Server connection from [{2}] at [{0}:{1}]", - connection->Handle()->RemoteIP(), connection->Handle()->RemotePort(), connection->GetUUID()); + server_connection->OnConnectionIdentified( + "WebInterface", [](std::shared_ptr connection) { + LogInfo("New WebInterface Server connection from [{2}] at [{0}:{1}]", + connection->Handle()->RemoteIP(), connection->Handle()->RemotePort(), connection->GetUUID()); - web_interface.AddConnection(connection); - }); + web_interface.AddConnection(connection); + } + ); - server_connection->OnConnectionRemoved("WebInterface", [](std::shared_ptr connection) { - LogInfo("Removed WebInterface Server connection from [{0}]", - connection->GetUUID()); + server_connection->OnConnectionRemoved( + "WebInterface", [](std::shared_ptr connection) { + LogInfo("Removed WebInterface Server connection from [{0}]", + connection->GetUUID()); - web_interface.RemoveConnection(connection); - }); + web_interface.RemoveConnection(connection); + } + ); EQStreamManagerInterfaceOptions opts(9000, false, false); - opts.daybreak_options.resend_delay_ms = RuleI(Network, ResendDelayBaseMS); + opts.daybreak_options.resend_delay_ms = RuleI(Network, ResendDelayBaseMS); opts.daybreak_options.resend_delay_factor = RuleR(Network, ResendDelayFactor); - opts.daybreak_options.resend_delay_min = RuleI(Network, ResendDelayMinMS); - opts.daybreak_options.resend_delay_max = RuleI(Network, ResendDelayMaxMS); - opts.daybreak_options.outgoing_data_rate = RuleR(Network, ClientDataRate); + opts.daybreak_options.resend_delay_min = RuleI(Network, ResendDelayMinMS); + opts.daybreak_options.resend_delay_max = RuleI(Network, ResendDelayMaxMS); + opts.daybreak_options.outgoing_data_rate = RuleR(Network, ClientDataRate); EQ::Net::EQStreamManager eqsm(opts); @@ -607,14 +645,16 @@ int main(int argc, char** argv) { zoneserver_list.reminder->Disable(); Timer InterserverTimer(INTERSERVER_TIMER); // does MySQL pings and auto-reconnect InterserverTimer.Trigger(); - uint8 ReconnectCounter = 100; + uint8 ReconnectCounter = 100; std::shared_ptr eqs; - EQStreamInterface *eqsi; + EQStreamInterface *eqsi; - eqsm.OnNewConnection([&stream_identifier](std::shared_ptr stream) { - stream_identifier.AddStream(stream); - LogInfo("New connection from IP {0}:{1}", stream->GetRemoteIP(), ntohs(stream->GetRemotePort())); - }); + eqsm.OnNewConnection( + [&stream_identifier](std::shared_ptr stream) { + stream_identifier.AddStream(stream); + LogInfo("New connection from IP {0}:{1}", stream->GetRemoteIP(), ntohs(stream->GetRemotePort())); + } + ); while (RunLoops) { Timer::SetCurrentTime(); @@ -626,7 +666,7 @@ int main(int argc, char** argv) { //check the stream identifier for any now-identified streams while ((eqsi = stream_identifier.PopIdentified())) { //now that we know what patch they are running, start up their client object - struct in_addr in{}; + struct in_addr in{}; in.s_addr = eqsi->GetRemoteIP(); if (RuleB(World, UseBannedIPsTable)) { //Lieka: Check to see if we have the responsibility for blocking IPs. LogInfo("Checking inbound connection [{}] against BannedIPs table", inet_ntoa(in)); @@ -642,7 +682,11 @@ int main(int argc, char** argv) { } } if (!RuleB(World, UseBannedIPsTable)) { - LogInfo("New connection from [{}]:[{}], processing connection", inet_ntoa(in), ntohs(eqsi->GetRemotePort())); + LogInfo( + "New connection from [{}]:[{}], processing connection", + inet_ntoa(in), + ntohs(eqsi->GetRemotePort()) + ); auto client = new Client(eqsi); // @merth: client->zoneattempt=0; client_list.Add(client); @@ -657,6 +701,7 @@ int main(int argc, char** argv) { database.PurgeExpiredInstances(); database.PurgeAllDeletedDataBuckets(); ExpeditionDatabase::PurgeExpiredCharacterLockouts(); + CharacterTaskTimersRepository::DeleteWhere(database, "expire_time <= NOW()"); } if (EQTimeTimer.Check()) { @@ -672,14 +717,18 @@ int main(int argc, char** argv) { launcher_list.Process(); LFPGroupList.Process(); adventure_manager.Process(); - expedition_state.Process(); + dynamic_zone_manager.Process(); if (InterserverTimer.Check()) { InterserverTimer.Start(); database.ping(); content_db.ping(); - std::string window_title = StringFormat("World: %s Clients: %i", Config->LongName.c_str(), client_list.GetClientCount()); + std::string window_title = StringFormat( + "World: %s Clients: %i", + Config->LongName.c_str(), + client_list.GetClientCount() + ); UpdateWindowTitle(window_title); } @@ -696,12 +745,14 @@ int main(int argc, char** argv) { return 0; } -void CatchSignal(int sig_num) { +void CatchSignal(int sig_num) +{ LogInfo("Caught signal [{}]", sig_num); RunLoops = false; } -void UpdateWindowTitle(char* iNewTitle) { +void UpdateWindowTitle(char *iNewTitle) +{ #ifdef _WINDOWS char tmp[500]; if (iNewTitle) { @@ -714,18 +765,23 @@ void UpdateWindowTitle(char* iNewTitle) { #endif } -void CheckForServerScript(bool force_download) { +void CheckForServerScript(bool force_download) +{ /* Fetch EQEmu Server script */ if (!std::ifstream("eqemu_server.pl") || force_download) { - if(force_download) - std::remove("eqemu_server.pl"); /* Delete local before fetch */ + if (force_download) { + std::remove("eqemu_server.pl"); + } /* Delete local before fetch */ std::cout << "Pulling down EQEmu Server Maintenance Script (eqemu_server.pl)..." << std::endl; #ifdef _WIN32 if(system("perl -MLWP::UserAgent -e \"require LWP::UserAgent; my $ua = LWP::UserAgent->new; $ua->timeout(10); $ua->env_proxy; my $response = $ua->get('https://raw.githubusercontent.com/EQEmu/Server/master/utils/scripts/eqemu_server.pl'); if ($response->is_success){ open(FILE, '> eqemu_server.pl'); print FILE $response->decoded_content; close(FILE); }\"")); #else - if(system("wget -N --no-check-certificate --quiet -O eqemu_server.pl https://raw.githubusercontent.com/EQEmu/Server/master/utils/scripts/eqemu_server.pl")); + if (system( + "wget -N --no-check-certificate --quiet -O eqemu_server.pl https://raw.githubusercontent.com/EQEmu/Server/master/utils/scripts/eqemu_server.pl" + )) {} #endif } } + diff --git a/world/shared_task_manager.cpp b/world/shared_task_manager.cpp new file mode 100644 index 000000000..d28559f90 --- /dev/null +++ b/world/shared_task_manager.cpp @@ -0,0 +1,1820 @@ +#include "shared_task_manager.h" +#include "cliententry.h" +#include "clientlist.h" +#include "dynamic_zone.h" +#include "dynamic_zone_manager.h" +#include "zonelist.h" +#include "zoneserver.h" +#include "shared_task_world_messaging.h" +#include "../common/repositories/character_data_repository.h" +#include "../common/repositories/character_task_timers_repository.h" +#include "../common/repositories/shared_task_members_repository.h" +#include "../common/repositories/shared_task_activity_state_repository.h" +#include "../common/repositories/completed_shared_tasks_repository.h" +#include "../common/repositories/completed_shared_task_members_repository.h" +#include "../common/repositories/completed_shared_task_activity_state_repository.h" +#include "../common/repositories/shared_task_dynamic_zones_repository.h" +#include + +extern ClientList client_list; +extern ZSList zoneserver_list; + +SharedTaskManager *SharedTaskManager::SetDatabase(Database *db) +{ + SharedTaskManager::m_database = db; + + return this; +} + +SharedTaskManager *SharedTaskManager::SetContentDatabase(Database *db) +{ + SharedTaskManager::m_content_database = db; + + return this; +} + +std::vector SharedTaskManager::GetRequestMembers( + uint32 requestor_character_id, + const std::vector &characters +) +{ + std::vector request_members = {}; + request_members.reserve(characters.size()); + + for (const auto &character : characters) { + SharedTaskMember member = {}; + member.character_id = character.id; + member.character_name = character.name; + + // if the solo/raid/group member is a leader, make sure we tag it as such + if (character.id == requestor_character_id) { + member.is_leader = true; + } + + request_members.emplace_back(member); + } + + return request_members; +} + +void SharedTaskManager::AttemptSharedTaskCreation( + uint32 requested_task_id, + uint32 requested_character_id, + uint32 npc_type_id +) +{ + auto task = GetSharedTaskDataByTaskId(requested_task_id); + if (task.id != 0 && task.type == TASK_TYPE_SHARED) { + LogTasksDetail( + "[AttemptSharedTaskCreation] Found Shared Task ({}) [{}]", + requested_task_id, + task.title + ); + } + + // shared task validation + auto request = SharedTask::GetRequestCharacters(*m_database, requested_character_id); + if (!CanRequestSharedTask(task.id, requested_character_id, request)) { + LogTasksDetail("[AttemptSharedTaskCreation] Shared task validation failed"); + return; + } + + auto request_members = GetRequestMembers(requested_character_id, request.characters); + if (!request_members.empty()) { + for (auto &m: request_members) { + LogTasksDetail( + "[AttemptSharedTaskCreation] Request Members ({})", + m.character_id + ); + } + } + + if (request_members.empty()) { + LogTasksDetail("[AttemptSharedTaskCreation] No additional request members found... Just leader"); + } + + // new shared task instance + auto new_shared_task = SharedTask{}; + auto activities = TaskActivitiesRepository::GetWhere(*m_content_database, fmt::format("taskid = {}", task.id)); + + // new shared task db object + auto shared_task_entity = SharedTasksRepository::NewEntity(); + shared_task_entity.task_id = (int) requested_task_id; + shared_task_entity.accepted_time = static_cast(std::time(nullptr)); + shared_task_entity.expire_time = task.duration > 0 ? (std::time(nullptr) + task.duration) : 0; + + auto created_db_shared_task = SharedTasksRepository::InsertOne(*m_database, shared_task_entity); + + // active record + new_shared_task.SetDbSharedTask(created_db_shared_task); + + // request timer lockouts + std::vector task_timers; + task_timers.reserve(request_members.size()); + + // persist members + std::vector shared_task_db_members = {}; + shared_task_db_members.reserve(request_members.size()); + for (auto &m: request_members) { + auto e = SharedTaskMembersRepository::NewEntity(); + + e.character_id = m.character_id; + e.is_leader = (m.is_leader ? 1 : 0); + e.shared_task_id = new_shared_task.GetDbSharedTask().id; + + shared_task_db_members.emplace_back(e); + + new_shared_task.AddCharacterToMemberHistory(m.character_id); // memory member history + + if (task.request_timer_seconds > 0) { + auto timer = CharacterTaskTimersRepository::NewEntity(); + timer.character_id = m.character_id; + timer.task_id = task.id; + timer.timer_type = static_cast(TaskTimerType::Request); + timer.expire_time = shared_task_entity.accepted_time + task.request_timer_seconds; + + task_timers.emplace_back(timer); + } + } + + SharedTaskMembersRepository::InsertMany(*m_database, shared_task_db_members); + + if (!task_timers.empty()) { + CharacterTaskTimersRepository::InsertMany(*m_database, task_timers); + } + + // activity state (memory) + std::vector shared_task_activity_state = {}; + shared_task_activity_state.reserve(activities.size()); + for (auto &a: activities) { + + // entry + auto e = SharedTaskActivityStateEntry{}; + e.activity_id = a.activityid; + e.done_count = 0; + e.max_done_count = a.goalcount; + + shared_task_activity_state.emplace_back(e); + } + + // activity state (database) + std::vector shared_task_db_activities = {}; + shared_task_db_activities.reserve(activities.size()); + for (auto &a: activities) { + + // entry + auto e = SharedTaskActivityStateRepository::NewEntity(); + e.shared_task_id = new_shared_task.GetDbSharedTask().id; + e.activity_id = a.activityid; + e.done_count = 0; + + shared_task_db_activities.emplace_back(e); + } + + SharedTaskActivityStateRepository::InsertMany(*m_database, shared_task_db_activities); + + // state + new_shared_task.SetSharedTaskActivityState(shared_task_activity_state); + + // set database data in memory to make it easier for any later referencing + new_shared_task.SetTaskData(task); + new_shared_task.SetTaskActivityData(activities); + new_shared_task.SetMembers(request_members); + + // add to shared tasks list + m_shared_tasks.emplace_back(new_shared_task); + + // send accept to members + for (auto &m: request_members) { + // only requester (leader) receives back the npc context to trigger task accept event + uint32_t npc_context_id = m.character_id == requested_character_id ? npc_type_id : 0; + SendAcceptNewSharedTaskPacket( + m.character_id, + requested_task_id, + npc_context_id, + shared_task_entity.accepted_time + ); + } + SendSharedTaskMemberListToAllMembers(&new_shared_task); + + LogTasks( + "[AttemptSharedTaskCreation] shared_task_id [{}] created successfully | task_id [{}] member_count [{}] activity_count [{}] current tasks in state [{}]", + new_shared_task.GetDbSharedTask().id, + task.id, + request_members.size(), + shared_task_activity_state.size(), + m_shared_tasks.size() + ); +} + +void SharedTaskManager::AttemptSharedTaskRemoval( + uint32 requested_task_id, + uint32 requested_character_id, + bool remove_from_db // inherited from zone logic - we're just passing through +) +{ + auto task = GetSharedTaskDataByTaskId(requested_task_id); + if (task.id != 0 && task.type == TASK_TYPE_SHARED) { + LogTasksDetail( + "[AttemptSharedTaskRemoval] Found Shared Task data ({}) [{}]", + requested_task_id, + task.title + ); + } + + auto t = FindSharedTaskByTaskIdAndCharacterId(requested_task_id, requested_character_id); + if (t) { + auto removed = t->FindMemberFromCharacterID(requested_character_id); + + // remove self + RemovePlayerFromSharedTask(t, requested_character_id); + SendRemovePlayerFromSharedTaskPacket( + requested_character_id, + requested_task_id, + remove_from_db + ); + + // inform clients of removal of self + SendSharedTaskMemberRemovedToAllMembers(t, removed.character_name); + + client_list.SendCharacterMessageID( + requested_character_id, Chat::Yellow, + SharedTaskMessage::PLAYER_HAS_BEEN_REMOVED, {removed.character_name, task.title} + ); + + if (removed.is_leader) { + ChooseNewLeader(t); + } + } +} + +void SharedTaskManager::RemoveEveryoneFromSharedTask(SharedTask *t, uint32 requested_character_id) +{ + // caller validates leader + LogTasksDetail("[RemoveEveryoneFromSharedTask] Leader [{}]", requested_character_id); + + // inform clients of removal + for (auto &m: t->GetMembers()) { + LogTasksDetail( + "[RemoveEveryoneFromSharedTask] Sending removal to [{}] task_id [{}]", + m.character_id, + t->GetTaskData().id + ); + + SendRemovePlayerFromSharedTaskPacket(m.character_id, t->GetTaskData().id, true); + + client_list.SendCharacterMessageID( + m.character_id, Chat::Yellow, + SharedTaskMessage::YOU_HAVE_BEEN_REMOVED, {t->GetTaskData().title} + ); + } + + client_list.SendCharacterMessageID( + requested_character_id, + Chat::Red, + SharedTaskMessage::PLAYER_HAS_BEEN_REMOVED, + {"Everyone", t->GetTaskData().title} + ); + + RemoveAllMembersFromDynamicZones(t); + + // persistence + DeleteSharedTask(t->GetDbSharedTask().id); + + PrintSharedTaskState(); +} + +void SharedTaskManager::DeleteSharedTask(int64 shared_task_id) +{ + LogTasksDetail( + "[DeleteSharedTask] shared_task_id [{}]", + shared_task_id + ); + + // remove internally + m_shared_tasks.erase( + std::remove_if( + m_shared_tasks.begin(), + m_shared_tasks.end(), + [&](SharedTask const &s) { + return s.GetDbSharedTask().id == shared_task_id; + } + ), + m_shared_tasks.end() + ); + + // database + SharedTasksRepository::DeleteWhere(*m_database, fmt::format("id = {}", shared_task_id)); + SharedTaskMembersRepository::DeleteWhere(*m_database, fmt::format("shared_task_id = {}", shared_task_id)); + SharedTaskActivityStateRepository::DeleteWhere(*m_database, fmt::format("shared_task_id = {}", shared_task_id)); + SharedTaskDynamicZonesRepository::DeleteWhere(*m_database, fmt::format("shared_task_id = {}", shared_task_id)); +} + +void SharedTaskManager::LoadSharedTaskState() +{ + LogTasksDetail("[LoadSharedTaskState] Restoring state from the database"); + + // load shared tasks + std::vector shared_tasks = {}; + + // eager load all activity state data + auto shared_tasks_activity_state_data = SharedTaskActivityStateRepository::All(*m_database); + + // eager load all member state data + auto shared_task_members_data = SharedTaskMembersRepository::All(*m_database); + + // load character data for member names + std::vector shared_task_character_data; + if (!shared_task_members_data.empty()) { + std::vector character_ids; + for (const auto &m: shared_task_members_data) { + character_ids.emplace_back(m.character_id); + } + + shared_task_character_data = CharacterDataRepository::GetWhere( + *m_database, + fmt::format("id IN ({})", fmt::join(character_ids, ",")) + ); + } + + auto shared_task_dynamic_zones_data = SharedTaskDynamicZonesRepository::All(*m_database); + + // load shared tasks not already completed + auto st = SharedTasksRepository::GetWhere(*m_database, "TRUE"); + shared_tasks.reserve(st.size()); + for (auto &s: st) { + SharedTask ns = {}; + + LogTasksDetail( + "[LoadSharedTaskState] Loading shared_task_id [{}] task_id [{}]", + s.id, + s.task_id + ); + + // shared task db data + ns.SetDbSharedTask(s); + + // set database task data for internal referencing + auto task_data = GetSharedTaskDataByTaskId(s.task_id); + + LogTasksDetail("[LoadSharedTaskState] [GetSharedTaskDataByTaskId] task_id [{}]", task_data.id); + + ns.SetTaskData(task_data); + + // set database task data for internal referencing + auto activities_data = GetSharedTaskActivityDataByTaskId(s.task_id); + ns.SetTaskActivityData(activities_data); + + // load activity state into memory + std::vector shared_task_activity_state = {}; + + // loop through shared task activity state data referencing from memory instead of + // querying inside this loop each time + for (auto &sta: shared_tasks_activity_state_data) { + + // filter by current shared task id + if (sta.shared_task_id == s.id) { + + auto e = SharedTaskActivityStateEntry{}; + e.activity_id = sta.activity_id; + e.done_count = sta.done_count; + + // get max done count from activities data + // loop through activities data in memory and grep on task_id, activity_id to pull goalcount + for (auto &ad: activities_data) { + if (ad.taskid == s.task_id && ad.activityid == sta.activity_id) { + LogTasksDetail( + "[LoadSharedTaskState] shared_task_id [{}] task_id [{}] activity_id [{}] done_count [{}] max_done_count (goalcount) [{}]", + s.id, + s.task_id, + sta.activity_id, + e.done_count, + ad.goalcount + ); + + e.max_done_count = ad.goalcount; + e.completed_time = sta.completed_time; + e.updated_time = sta.updated_time; + } + } + + shared_task_activity_state.emplace_back(e); + } + } + + ns.SetSharedTaskActivityState(shared_task_activity_state); + + // members + std::vector shared_task_members = {}; + for (auto &m: shared_task_members_data) { + if (m.shared_task_id == s.id) { + SharedTaskMember member = {}; + member.character_id = m.character_id; + member.is_leader = (m.is_leader ? 1 : 0); + + auto it = std::find_if( + shared_task_character_data.begin(), shared_task_character_data.end(), + [&](const CharacterDataRepository::CharacterData &character) { + return character.id == m.character_id; + } + ); + + if (it != shared_task_character_data.end()) { + member.character_name = it->name; + } + + shared_task_members.emplace_back(member); + + LogTasksDetail( + "[LoadSharedTaskState] shared_task_id [{}] adding member character_id [{}] character_name [{}] is_leader [{}]", + s.id, + member.character_id, + member.character_name, + member.is_leader + ); + + // add member to history (if restoring state from a world restart we lost real past member history) + ns.AddCharacterToMemberHistory(m.character_id); + } + } + + ns.SetMembers(shared_task_members); + + // dynamic zones + for (const auto &dz_entry : shared_task_dynamic_zones_data) { + if (dz_entry.shared_task_id == s.id) { + ns.dynamic_zone_ids.emplace_back(static_cast(dz_entry.dynamic_zone_id)); + + LogTasksDetail( + "[LoadSharedTaskState] shared_task_id [{}] adding dynamic_zone_id [{}]", + s.id, + dz_entry.dynamic_zone_id + ); + } + } + + LogTasks( + "[LoadSharedTaskState] Loaded shared task state | shared_task_id [{}] task_id [{}] task_title [{}] member_count [{}] state_activity_count [{}]", + s.id, + task_data.id, + task_data.title, + ns.GetMembers().size(), + ns.GetActivityState().size() + ); + + shared_tasks.emplace_back(ns); + } + + SetSharedTasks(shared_tasks); + + LogTasks( + "[LoadSharedTaskState] Loaded [{}] shared tasks", + m_shared_tasks.size() + ); + + PrintSharedTaskState(); +} + +SharedTaskManager *SharedTaskManager::LoadTaskData() +{ + m_task_data = TasksRepository::All(*m_content_database); + m_task_activity_data = TaskActivitiesRepository::All(*m_content_database); + + LogTasks("[LoadTaskData] Loaded tasks [{}] activities [{}]", m_task_data.size(), m_task_activity_data.size()); + + return this; +} + +TasksRepository::Tasks SharedTaskManager::GetSharedTaskDataByTaskId(uint32 task_id) +{ + for (auto &t: m_task_data) { + if (t.id == task_id && t.type == TASK_TYPE_SHARED) { + return t; + } + } + + return TasksRepository::NewEntity(); +} + +std::vector +SharedTaskManager::GetSharedTaskActivityDataByTaskId(uint32 task_id) +{ + std::vector activities = {}; + + for (auto &a: m_task_activity_data) { + if (a.taskid == task_id) { + activities.emplace_back(a); + } + } + + return activities; +} + +void SharedTaskManager::SharedTaskActivityUpdate( + uint32 source_character_id, + uint32 task_id, + uint32 activity_id, + uint32 done_count, + bool ignore_quest_update +) +{ + auto shared_task = FindSharedTaskByTaskIdAndCharacterId(task_id, source_character_id); + if (shared_task) { + LogTasksDetail( + "[SharedTaskActivityUpdate] shared_task_id [{}] character_id [{}] task_id [{}] activity_id [{}] done_count [{}]", + shared_task->GetDbSharedTask().id, + source_character_id, + task_id, + activity_id, + done_count + ); + + for (auto &a : shared_task->m_shared_task_activity_state) { + if (a.activity_id == activity_id) { + + // discard updates out of bounds + if (a.done_count == a.max_done_count) { + LogTasksDetail( + "[SharedTaskActivityUpdate] done_count [{}] is greater than max [{}] discarding...", + done_count, + a.max_done_count + ); + return; + } + + // if we are progressing + if (a.done_count < done_count) { + LogTasksDetail( + "[SharedTaskActivityUpdate] Propagating update for shared_task_id [{}] character_id [{}] task_id [{}] activity_id [{}] old_done_count [{}] new_done_count [{}]", + shared_task->GetDbSharedTask().id, + source_character_id, + task_id, + activity_id, + a.done_count, + done_count + ); + + a.done_count = done_count; + a.updated_time = std::time(nullptr); + + // if the update came in larger than the max for whatever reason, clamp + if (a.done_count > a.max_done_count) { + a.done_count = a.max_done_count; + } + + // if the activity is done, lets mark it as such + if (a.done_count == a.max_done_count) { + a.completed_time = std::time(nullptr); + } + + // sync state as each update comes in (for now) + SaveSharedTaskActivityState( + shared_task->GetDbSharedTask().id, + shared_task->m_shared_task_activity_state + ); + + shared_task->SetSharedTaskActivityState(shared_task->m_shared_task_activity_state); + + LogTasksDetail( + "[SharedTaskActivityUpdate] Debug done_count [{}]", + a.done_count + ); + + // loop through members - send update + for (auto &m: shared_task->GetMembers()) { + + // confirm task update to client(s) + auto p = std::make_unique( + ServerOP_SharedTaskUpdate, + sizeof(ServerSharedTaskActivityUpdate_Struct) + ); + + auto d = reinterpret_cast(p->pBuffer); + d->source_character_id = m.character_id; + d->task_id = task_id; + d->activity_id = activity_id; + d->done_count = done_count; + d->ignore_quest_update = ignore_quest_update; + + // get requested character zone server + ClientListEntry *c = client_list.FindCLEByCharacterID(m.character_id); + if (c && c->Server()) { + c->Server()->SendPacket(p.get()); + } + } + + break; + } + + LogTasksDetail( + "[SharedTaskActivityUpdate] Discarding duplicate update for shared_task_id [{}] character_id [{}] task_id [{}] activity_id [{}] done_count [{}] ignore_quest_update [{}]", + shared_task->GetDbSharedTask().id, + source_character_id, + task_id, + activity_id, + done_count, + (ignore_quest_update ? "true" : "false") + ); + } + } + + // check if completed + bool is_shared_task_completed = true; + for (auto &a : shared_task->m_shared_task_activity_state) { + if (a.done_count != a.max_done_count) { + is_shared_task_completed = false; + } + } + + // mark completed + if (is_shared_task_completed) { + auto t = shared_task->GetDbSharedTask(); + if (t.id > 0) { + LogTasksDetail( + "[SharedTaskActivityUpdate] Marking shared task [{}] completed", + shared_task->GetDbSharedTask().id + ); + + // set record + t.completion_time = std::time(nullptr); + t.is_locked = true; + // update database + SharedTasksRepository::UpdateOne(*m_database, t); + // update internally + shared_task->SetDbSharedTask(t); + // record completion + RecordSharedTaskCompletion(shared_task); + // replay timer lockouts + AddReplayTimers(shared_task); + } + } + } +} + +SharedTask *SharedTaskManager::FindSharedTaskByTaskIdAndCharacterId(uint32 task_id, uint32 character_id) +{ + for (auto &s: m_shared_tasks) { + // grep for task + if (s.GetTaskData().id == task_id) { + // find member in shared task + for (auto &m: s.GetMembers()) { + if (m.character_id == character_id) { + return &s; + } + } + } + } + + return nullptr; +} + +void SharedTaskManager::SaveSharedTaskActivityState( + int64 shared_task_id, + std::vector activity_state +) +{ + // transfer from memory to database + std::vector shared_task_db_activities = {}; + shared_task_db_activities.reserve(activity_state.size()); + + for (auto &a: activity_state) { + + // entry + auto e = SharedTaskActivityStateRepository::NewEntity(); + e.shared_task_id = shared_task_id; + e.activity_id = (int) a.activity_id; + e.done_count = (int) a.done_count; + e.completed_time = (int) a.completed_time; + e.updated_time = (int) a.updated_time; + + shared_task_db_activities.emplace_back(e); + } + + SharedTaskActivityStateRepository::DeleteWhere(*m_database, fmt::format("shared_task_id = {}", shared_task_id)); + SharedTaskActivityStateRepository::InsertMany(*m_database, shared_task_db_activities); +} + +bool SharedTaskManager::IsSharedTaskLeader(SharedTask *s, uint32 character_id) +{ + for (auto &m: s->GetMembers()) { + if (m.character_id == character_id && m.is_leader) { + return true; + } + } + + return false; +} + +void SharedTaskManager::SendAcceptNewSharedTaskPacket( + uint32 character_id, + uint32 task_id, + uint32_t npc_context_id, + int accept_time +) +{ + auto p = std::make_unique( + ServerOP_SharedTaskAcceptNewTask, + sizeof(ServerSharedTaskRequest_Struct) + ); + + auto d = reinterpret_cast(p->pBuffer); + d->requested_character_id = character_id; + d->requested_task_id = task_id; + d->requested_npc_type_id = npc_context_id; + d->accept_time = accept_time; + + // get requested character zone server + ClientListEntry *cle = client_list.FindCLEByCharacterID(character_id); + if (cle && cle->Server()) { + cle->Server()->SendPacket(p.get()); + } +} + +void SharedTaskManager::SendRemovePlayerFromSharedTaskPacket( + uint32 character_id, + uint32 task_id, + bool remove_from_db +) +{ + // confirm shared task request: inform clients + auto p = std::make_unique( + ServerOP_SharedTaskAttemptRemove, + sizeof(ServerSharedTaskAttemptRemove_Struct) + ); + + auto d = reinterpret_cast(p->pBuffer); + d->requested_character_id = character_id; + d->requested_task_id = task_id; + d->remove_from_db = remove_from_db; + + // get requested character zone server + ClientListEntry *cle = client_list.FindCLEByCharacterID(character_id); + if (cle && cle->Server()) { + cle->Server()->SendPacket(p.get()); + } +} + +void SharedTaskManager::SendSharedTaskMemberList(uint32 character_id, const std::vector &members) +{ + EQ::Net::DynamicPacket dyn_pack; + dyn_pack.PutSerialize(0, members); + + SendSharedTaskMemberList(character_id, dyn_pack); +} + +void SharedTaskManager::SendSharedTaskMemberList(uint32 character_id, const EQ::Net::DynamicPacket &serialized_members) +{ + // send member list packet + auto p = std::make_unique( + ServerOP_SharedTaskMemberlist, + sizeof(ServerSharedTaskMemberListPacket_Struct) + serialized_members.Length() + ); + + auto d = reinterpret_cast(p->pBuffer); + d->destination_character_id = character_id; + d->cereal_size = static_cast(serialized_members.Length()); + memcpy(d->cereal_serialized_members, serialized_members.Data(), serialized_members.Length()); + + // send memberlist + ClientListEntry *cle = client_list.FindCLEByCharacterID(character_id); + if (cle && cle->Server()) { + cle->Server()->SendPacket(p.get()); + } +} + +void SharedTaskManager::SendSharedTaskMemberChange( + uint32 character_id, + int64 shared_task_id, + const std::string &player_name, + bool removed +) +{ + uint32_t size = sizeof(ServerSharedTaskMemberChangePacket_Struct); + auto p = std::make_unique(ServerOP_SharedTaskMemberChange, size); + + auto d = reinterpret_cast(p->pBuffer); + d->destination_character_id = character_id; + d->shared_task_id = shared_task_id; + d->removed = removed; + strn0cpy(d->player_name, player_name.c_str(), sizeof(d->player_name)); + + ClientListEntry *cle = client_list.FindCLEByCharacterID(character_id); + if (cle && cle->Server()) { + cle->Server()->SendPacket(p.get()); + } +} + +void SharedTaskManager::RemovePlayerFromSharedTask(SharedTask *s, uint32 character_id) +{ + SharedTaskMembersRepository::DeleteWhere( + *m_database, + fmt::format( + "shared_task_id = {} and character_id = {}", + s->GetDbSharedTask().id, + character_id + ) + ); + + // remove internally + s->m_members.erase( + std::remove_if( + s->m_members.begin(), + s->m_members.end(), + [&](SharedTaskMember const &m) { + return m.character_id == character_id; + } + ), + s->m_members.end() + ); + + for (const auto &dz_id : s->dynamic_zone_ids) { + auto dz = DynamicZone::FindDynamicZoneByID(dz_id); + if (dz) { + dz->RemoveMember(character_id); + } + } +} + +void SharedTaskManager::PrintSharedTaskState() +{ + for (auto &s: m_shared_tasks) { + auto task = GetSharedTaskDataByTaskId(s.GetDbSharedTask().task_id); + + LogTasksDetail("[PrintSharedTaskState] # Shared Task"); + + LogTasksDetail( + "[PrintSharedTaskState] shared_task_id [{}] task_id [{}] task_title [{}] member_count [{}] state_activity_count [{}]", + s.GetDbSharedTask().id, + task.id, + task.title, + s.GetMembers().size(), + s.GetActivityState().size() + ); + + LogTasksDetail("[PrintSharedTaskState] # Activities"); + + // activity state + for (auto &a: s.m_shared_task_activity_state) { + LogTasksDetail( + "[PrintSharedTaskState] -- activity_id [{}] done_count [{}] max_done_count [{}] completed_time [{}]", + a.activity_id, + a.done_count, + a.max_done_count, + a.completed_time + ); + } + + LogTasksDetail("[PrintSharedTaskState] # Members"); + + // members + for (auto &m: s.m_members) { + LogTasksDetail( + "[PrintSharedTaskState] -- character_id [{}] is_leader [{}]", + m.character_id, + m.is_leader + ); + } + + LogTasksDetail("[PrintSharedTaskState] # Dynamic Zones"); + + for (auto &dz_id: s.dynamic_zone_ids) { + LogTasksDetail( + "[PrintSharedTaskState] -- dynamic_zone_id [{}]", + dz_id + ); + } + } +} + +void SharedTaskManager::RemovePlayerFromSharedTaskByPlayerName(SharedTask *s, const std::string &character_name) +{ + auto member = s->FindMemberFromCharacterName(character_name); + if (member.character_id == 0) { + SendLeaderMessageID(s, Chat::Red, SharedTaskMessage::IS_NOT_MEMBER, {character_name}); + return; + } + + LogTasksDetail( + "[RemovePlayerFromSharedTaskByPlayerName] shared_task_id [{}] character_name [{}]", + s->GetDbSharedTask().id, + character_name + ); + + auto leader = s->GetLeader(); // get leader now for msg in case leader is one removed + + RemovePlayerFromSharedTask(s, member.character_id); + SendRemovePlayerFromSharedTaskPacket( + member.character_id, + s->GetDbSharedTask().task_id, + true + ); + + SendSharedTaskMemberRemovedToAllMembers(s, member.character_name); + + // leader and removed player get server messages (leader sees two messages) + // results in double messages if leader removed self (live behavior) + client_list.SendCharacterMessageID( + leader.character_id, Chat::Yellow, + SharedTaskMessage::PLAYER_HAS_BEEN_REMOVED, {member.character_name, s->GetTaskData().title} + ); + + client_list.SendCharacterMessageID( + member.character_id, Chat::Yellow, + SharedTaskMessage::PLAYER_HAS_BEEN_REMOVED, {member.character_name, s->GetTaskData().title} + ); + + if (member.is_leader) { + ChooseNewLeader(s); + } +} + +void SharedTaskManager::SendSharedTaskMemberListToAllMembers(SharedTask *s) +{ + // serialize once so we don't re-serialize it for every member + EQ::Net::DynamicPacket dyn_pack; + dyn_pack.PutSerialize(0, s->GetMembers()); + + for (auto &m: s->GetMembers()) { + SendSharedTaskMemberList( + m.character_id, + dyn_pack + ); + } +} + +void SharedTaskManager::SendSharedTaskMemberAddedToAllMembers(SharedTask *s, const std::string &player_name) +{ + for (const auto &m : s->GetMembers()) { + SendSharedTaskMemberChange(m.character_id, s->GetDbSharedTask().id, player_name, false); + } +} + +void SharedTaskManager::SendSharedTaskMemberRemovedToAllMembers(SharedTask *s, const std::string &player_name) +{ + for (const auto &m : s->GetMembers()) { + SendSharedTaskMemberChange(m.character_id, s->GetDbSharedTask().id, player_name, true); + } +} + +void SharedTaskManager::MakeLeaderByPlayerName(SharedTask *s, const std::string &character_name) +{ + auto new_leader = s->FindMemberFromCharacterName(character_name); + if (new_leader.character_id == 0) { + SendLeaderMessageID(s, Chat::Red, SharedTaskMessage::IS_NOT_MEMBER, {character_name}); + return; + } + + bool found_new_leader = false; + + std::vector members = s->GetMembers(); + for (auto &m: members) { + LogTasksDetail( + "[MakeLeaderByPlayerName] character_id [{}] m.character_id [{}]", + new_leader.character_id, + m.character_id + ); + + m.is_leader = false; + + // destination character is in shared task, make swap + if (new_leader.character_id == m.character_id) { + found_new_leader = true; + LogTasksDetail( + "[MakeLeaderByPlayerName] shared_task_id [{}] character_name [{}]", + s->GetDbSharedTask().id, + character_name + ); + + m.is_leader = true; + } + } + + if (found_new_leader) { + s->SetMembers(members); + SaveMembers(s, members); + SendSharedTaskMemberListToAllMembers(s); + SendMembersMessageID( + s, Chat::Yellow, SharedTaskMessage::PLAYER_NOW_LEADER, + {new_leader.character_name, s->GetTaskData().title} + ); + + for (const auto &dz_id : s->dynamic_zone_ids) { + auto dz = DynamicZone::FindDynamicZoneByID(dz_id); + if (dz) { + dz->SetNewLeader(static_cast(new_leader.character_id)); + } + } + } +} + +void SharedTaskManager::SaveMembers(SharedTask *s, std::vector members) +{ + std::vector dm = {}; + dm.reserve(members.size()); + for (auto &m: members) { + auto e = SharedTaskMembersRepository::NewEntity(); + + e.character_id = m.character_id; + e.is_leader = (m.is_leader ? 1 : 0); + e.shared_task_id = s->GetDbSharedTask().id; + + dm.emplace_back(e); + } + + SharedTaskMembersRepository::DeleteWhere(*m_database, fmt::format("shared_task_id = {}", s->GetDbSharedTask().id)); + SharedTaskMembersRepository::InsertMany(*m_database, dm); +} + +void SharedTaskManager::InvitePlayerByPlayerName(SharedTask *s, const std::string &player_name) +{ + auto character = CharacterDataRepository::GetWhere( + *m_database, + fmt::format("`name` = '{}' LIMIT 1", EscapeString(player_name)) + ); + + auto character_id = !character.empty() ? character.front().id : 0; + + // we call validation even for an invalid player so error messages occur + if (CanAddPlayer(s, character_id, player_name, false)) { + // send dialogue window + SendSharedTaskInvitePacket(s, character_id); + + // keep track of active invitations at world + QueueActiveInvitation(s->GetDbSharedTask().id, character_id); + } +} + +void SharedTaskManager::SendSharedTaskInvitePacket(SharedTask *s, int64 invited_character_id) +{ + auto leader = s->GetLeader(); + + // found leader + if (leader.character_id > 0) { + + // init packet + auto p = std::make_unique( + ServerOP_SharedTaskInvitePlayer, + sizeof(ServerSharedTaskInvitePlayer_Struct) + ); + + // fill + auto d = reinterpret_cast(p->pBuffer); + d->requested_character_id = invited_character_id; + d->invite_shared_task_id = s->GetDbSharedTask().id; + strn0cpy(d->inviter_name, leader.character_name.c_str(), sizeof(d->inviter_name)); + strn0cpy(d->task_name, s->GetTaskData().title.c_str(), sizeof(d->task_name)); + + // get requested character zone server + ClientListEntry *cle = client_list.FindCLEByCharacterID(invited_character_id); + if (cle && cle->Server()) { + SendLeaderMessageID(s, Chat::Yellow, SharedTaskMessage::SEND_INVITE_TO, {cle->name()}); + cle->Server()->SendPacket(p.get()); + } + } +} + +void SharedTaskManager::AddPlayerByCharacterIdAndName( + SharedTask *s, + int64 character_id, + const std::string &character_name +) +{ + // fetch + std::vector members = s->GetMembers(); + + // create + auto new_member = SharedTaskMember{}; + new_member.character_id = character_id; + new_member.character_name = character_name; + + bool does_member_exist = false; + + for (auto &m: s->GetMembers()) { + if (m.character_id == character_id) { + does_member_exist = true; + } + } + + if (!does_member_exist && CanAddPlayer(s, character_id, character_name, true)) { + members.push_back(new_member); + + // add request timer (validation will prevent non-expired duplicates) + if (s->GetTaskData().request_timer_seconds > 0) { + auto expire_time = s->GetDbSharedTask().accepted_time + s->GetTaskData().request_timer_seconds; + if (expire_time > std::time(nullptr)) // not already expired + { + auto timer = CharacterTaskTimersRepository::NewEntity(); + timer.character_id = character_id; + timer.task_id = s->GetDbSharedTask().task_id; + timer.timer_type = static_cast(TaskTimerType::Request); + timer.expire_time = expire_time; + + CharacterTaskTimersRepository::InsertOne(*m_database, timer); + } + } + + // inform client + SendAcceptNewSharedTaskPacket(character_id, s->GetTaskData().id, 0, s->GetDbSharedTask().accepted_time); + + // add to shared task + SendSharedTaskMemberAddedToAllMembers(s, character_name); + s->SetMembers(members); + SaveMembers(s, members); + SendSharedTaskMemberList(character_id, s->GetMembers()); // new member gets full member list + s->AddCharacterToMemberHistory(character_id); + + // add to dzs tied to shared task + for (const auto &dz_id : s->dynamic_zone_ids) { + auto dz = DynamicZone::FindDynamicZoneByID(dz_id); + if (dz) { + auto status = DynamicZoneMemberStatus::Online; + dz->AddMember({static_cast(character_id), character_name, status}); + } + } + } +} + +SharedTask *SharedTaskManager::FindSharedTaskById(int64 shared_task_id) +{ + for (auto &s: m_shared_tasks) { + if (s.GetDbSharedTask().id == shared_task_id) { + return &s; + } + } + + return nullptr; +} + +void SharedTaskManager::QueueActiveInvitation(int64 shared_task_id, int64 character_id) +{ + LogTasksDetail( + "[QueueActiveInvitation] shared_task_id [{}] character_id [{}]", + shared_task_id, + character_id + ); + + auto active_invitation = SharedTaskActiveInvitation{}; + active_invitation.shared_task_id = shared_task_id; + active_invitation.character_id = character_id; + + m_active_invitations.emplace_back(active_invitation); +} + +bool SharedTaskManager::IsInvitationActive(uint32 shared_task_id, uint32 character_id) +{ + LogTasksDetail( + "[IsInvitationActive] shared_task_id [{}] character_id [{}]", + shared_task_id, + character_id + ); + + for (auto &i: m_active_invitations) { + if (i.character_id == character_id && i.shared_task_id == shared_task_id) { + return true; + } + } + + return false; +} + +void SharedTaskManager::RemoveActiveInvitation(int64 shared_task_id, int64 character_id) +{ + LogTasksDetail( + "[RemoveActiveInvitation] shared_task_id [{}] character_id [{}] pre_removal_count [{}]", + shared_task_id, + character_id, + m_active_invitations.size() + ); + + // remove internally + m_active_invitations.erase( + std::remove_if( + m_active_invitations.begin(), + m_active_invitations.end(), + [&](SharedTaskActiveInvitation const &i) { + return i.shared_task_id == shared_task_id && i.character_id == character_id; + } + ), + m_active_invitations.end() + ); + + LogTasksDetail( + "[RemoveActiveInvitation] shared_task_id [{}] character_id [{}] post_removal_count [{}]", + shared_task_id, + character_id, + m_active_invitations.size() + ); +} + +void SharedTaskManager::RemoveActiveInvitationByCharacterID(uint32_t character_id) +{ + m_active_invitations.erase( + std::remove_if( + m_active_invitations.begin(), m_active_invitations.end(), + [&](SharedTaskActiveInvitation const &i) { + return i.character_id == character_id; + } + ), m_active_invitations.end() + ); +} + +void SharedTaskManager::CreateDynamicZone(SharedTask *shared_task, DynamicZone &dz_request) +{ + std::vector dz_members; + for (const auto &member : shared_task->GetMembers()) { + dz_members.emplace_back(member.character_id, member.character_name); + if (member.is_leader) { + dz_request.SetLeader({member.character_id, member.character_name}); + } + } + + auto new_dz = dynamic_zone_manager.CreateNew(dz_request, dz_members); + if (new_dz) { + auto shared_task_dz = SharedTaskDynamicZonesRepository::NewEntity(); + shared_task_dz.shared_task_id = shared_task->GetDbSharedTask().id; + shared_task_dz.dynamic_zone_id = new_dz->GetID(); + + SharedTaskDynamicZonesRepository::InsertOne(*m_database, shared_task_dz); + + shared_task->dynamic_zone_ids.emplace_back(new_dz->GetID()); + } +} + +void SharedTaskManager::SendLeaderMessage(SharedTask *shared_task, int chat_type, const std::string &message) +{ + if (!shared_task) { + return; + } + + for (const auto &member : shared_task->GetMembers()) { + if (member.is_leader) { + client_list.SendCharacterMessage(member.character_id, chat_type, message); + break; + } + } +} + +void SharedTaskManager::SendLeaderMessageID( + SharedTask *shared_task, int chat_type, + int eqstr_id, std::initializer_list args +) +{ + if (!shared_task) { + return; + } + + for (const auto &member : shared_task->GetMembers()) { + if (member.is_leader) { + client_list.SendCharacterMessageID(member.character_id, chat_type, eqstr_id, args); + break; + } + } +} + +void SharedTaskManager::SendMembersMessage(SharedTask *shared_task, int chat_type, const std::string &message) +{ + if (!shared_task) { + return; + } + + for (const auto &member : shared_task->GetMembers()) { + client_list.SendCharacterMessage(member.character_id, chat_type, message); + } +} + +void SharedTaskManager::SendMembersMessageID( + SharedTask *shared_task, + int chat_type, + int eqstr_id, + std::initializer_list args +) +{ + if (!shared_task || shared_task->GetMembers().empty()) { + return; + } + + // serialize here since using client_list methods would re-serialize for every member + SerializeBuffer serialized_args; + for (const auto &arg : args) { + serialized_args.WriteString(arg); + } + + uint32_t args_size = static_cast(serialized_args.size()); + uint32_t pack_size = sizeof(CZClientMessageString_Struct) + args_size; + auto pack = std::make_unique(ServerOP_CZClientMessageString, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + buf->string_id = eqstr_id; + buf->chat_type = chat_type; + buf->args_size = args_size; + memcpy(buf->args, serialized_args.buffer(), serialized_args.size()); + + for (const auto &member : shared_task->GetMembers()) { + auto character = client_list.FindCLEByCharacterID(member.character_id); + if (character && character->Server()) { + strn0cpy(buf->character_name, character->name(), sizeof(buf->character_name)); + character->Server()->SendPacket(pack.get()); + } + } +} + +bool SharedTaskManager::CanRequestSharedTask( + uint32_t task_id, + uint32_t character_id, + const SharedTaskRequestCharacters &request +) +{ + auto task = GetSharedTaskDataByTaskId(task_id); + if (task.id == 0) { + return false; + } + + // this attempts to follow live validation order + + // check if any party members are already in a shared task + auto shared_task_members = FindCharactersInSharedTasks(request.character_ids); + if (!shared_task_members.empty()) { + // messages for every character already in a shared task + for (const auto &member : shared_task_members) { + auto it = std::find_if( + request.characters.begin(), request.characters.end(), + [&](const CharacterDataRepository::CharacterData &char_data) { + return char_data.id == member; + } + ); + + if (it != request.characters.end()) { + if (it->id == character_id) { + client_list.SendCharacterMessageID( + character_id, + Chat::Red, + SharedTaskMessage::NO_REQUEST_BECAUSE_HAVE_ONE + ); + } + else if (request.group_type == SharedTaskRequestGroupType::Group) { + client_list.SendCharacterMessageID( + character_id, + Chat::Red, + SharedTaskMessage::NO_REQUEST_BECAUSE_GROUP_HAS_ONE, + {it->name} + ); + } + else { + client_list.SendCharacterMessageID( + character_id, + Chat::Red, + SharedTaskMessage::NO_REQUEST_BECAUSE_RAID_HAS_ONE, + {it->name} + ); + } + } + } + + return false; + } + + // check if any party member's minimum level is too low (pre-2014 this was average level) + if (task.minlevel > 0 && request.lowest_level < task.minlevel) { + client_list.SendCharacterMessageID(character_id, Chat::Red, SharedTaskMessage::AVG_LVL_LOW); + return false; + } + + // check if any party member's maximum level is too high (pre-2014 this was average level) + if (task.maxlevel > 0 && request.highest_level > task.maxlevel) { + client_list.SendCharacterMessageID(character_id, Chat::Red, SharedTaskMessage::AVG_LVL_HIGH); + return false; + } + + // allow gm/dev bypass for minimum player count requirements + auto requester = client_list.FindCLEByCharacterID(character_id); + bool is_gm = (requester && requester->GetGM()); + + // check if party member count is below the minimum + if (!is_gm && task.min_players > 0 && request.characters.size() < task.min_players) { + client_list.SendCharacterMessageID( + character_id, + Chat::Red, + SharedTaskMessage::SHARED_TASK_NOT_MEET_MIN_NUM_PLAYER + ); + return false; + } + + // check if party member count is above the maximum + // todo: live creates the shared task but truncates members if it exceeds max (sorted by leader and raid group numbers) + if (task.max_players > 0 && request.characters.size() > task.max_players) { + client_list.SendCharacterMessageID(character_id, Chat::Red, SharedTaskMessage::PARTY_EXCEED_MAX_PLAYER); + return false; + } + + // check if party level spread exceeds task's maximum + if (task.level_spread > 0 && (request.highest_level - request.lowest_level) > task.level_spread) { + client_list.SendCharacterMessageID(character_id, Chat::Red, SharedTaskMessage::LVL_SPREAD_HIGH); + return false; + } + + // check if any party members have a replay or request timer for the task (limit 1, replay checked first) + auto character_task_timers = CharacterTaskTimersRepository::GetWhere( + *m_database, fmt::format( + "character_id IN ({}) AND task_id = {} AND expire_time > NOW() ORDER BY timer_type ASC LIMIT 1", + fmt::join(request.character_ids, ","), task_id + ) + ); + + if (!character_task_timers.empty()) { + auto timer_type = static_cast(character_task_timers.front().timer_type); + auto seconds = character_task_timers.front().expire_time - std::time(nullptr); + auto days = fmt::format_int(seconds / 86400).str(); + auto hours = fmt::format_int((seconds / 3600) % 24).str(); + auto mins = fmt::format_int((seconds / 60) % 60).str(); + + if (character_task_timers.front().character_id == character_id) { + if (timer_type == TaskTimerType::Replay) { + client_list.SendCharacterMessageID( + character_id, + Chat::Red, + SharedTaskMessage::YOU_MUST_WAIT_REPLAY_TIMER, {days, hours, mins} + ); + } + else if (timer_type == TaskTimerType::Request) { + client_list.SendCharacterMessage( + character_id, + Chat::Red, fmt::format( + SharedTaskMessage::GetEQStr(SharedTaskMessage::YOU_MUST_WAIT_REQUEST_TIMER), days, hours, mins + ) + ); + } + } + else { + auto it = std::find_if( + request.characters.begin(), request.characters.end(), + [&](const CharacterDataRepository::CharacterData &char_data) { + return char_data.id == character_task_timers.front().character_id; + } + ); + + if (it != request.characters.end() && timer_type == TaskTimerType::Replay) { + client_list.SendCharacterMessageID( + character_id, + Chat::Red, + SharedTaskMessage::PLAYER_MUST_WAIT_REPLAY_TIMER, + {it->name, days, hours, mins} + ); + } + else if (it != request.characters.end() && timer_type == TaskTimerType::Request) { + client_list.SendCharacterMessage( + character_id, + Chat::Red, + fmt::format( + SharedTaskMessage::GetEQStr(SharedTaskMessage::PLAYER_MUST_WAIT_REQUEST_TIMER), + it->name, + days, + hours, + mins + ) + ); + } + } + + return false; + } + + return true; +} + +bool SharedTaskManager::CanAddPlayer(SharedTask *s, uint32_t character_id, std::string player_name, bool accepted) +{ + // this attempts to follow live validation order + + bool allow_invite = true; + + // check if task is locked + if (s->GetDbSharedTask().is_locked) { + SendLeaderMessageID(s, Chat::Red, SharedTaskMessage::TASK_NOT_ALLOWING_PLAYERS_AT_TIME); + allow_invite = false; + } + + // check if player is online and in cle (other checks require online) + auto cle = client_list.FindCLEByCharacterID(character_id); + if (!cle || !cle->Server()) { + SendLeaderMessageID(s, Chat::Red, SharedTaskMessage::PLAYER_NOT_ONLINE_TO_ADD, {player_name}); + SendLeaderMessageID(s, Chat::Red, SharedTaskMessage::COULD_NOT_BE_INVITED, {player_name}); + return false; + } + + player_name = cle->name(); + + // check if player is already in a shared task + auto shared_task_members = SharedTaskMembersRepository::GetWhere( + *m_database, + fmt::format("character_id = {} LIMIT 1", character_id) + ); + + if (!shared_task_members.empty()) { + auto shared_task_id = shared_task_members.front().shared_task_id; + if (shared_task_id == s->GetDbSharedTask().id) { + SendLeaderMessageID(s, Chat::Red, SharedTaskMessage::CANT_ADD_PLAYER_ALREADY_MEMBER, {player_name}); + } + else { + SendLeaderMessageID(s, Chat::Red, SharedTaskMessage::CANT_ADD_PLAYER_ALREADY_ASSIGNED, {player_name}); + } + allow_invite = false; + } + + // check if player has an outstanding invite + for (const auto &invite : m_active_invitations) { + if (invite.character_id == character_id) { + if (invite.shared_task_id == s->GetDbSharedTask().id) { + SendLeaderMessageID( + s, + Chat::Red, + SharedTaskMessage::PLAYER_ALREADY_OUTSTANDING_INVITATION_THIS, + {player_name} + ); + } + else { + SendLeaderMessageID(s, Chat::Red, SharedTaskMessage::PLAYER_ALREADY_OUTSTANDING_ANOTHER, {player_name}); + } + allow_invite = false; + break; + } + } + + // check if player has a replay or request timer lockout + // todo: live allows characters with a request timer to be re-invited if they quit, but only until they zone? (investigate/edge case) + auto task_timers = CharacterTaskTimersRepository::GetWhere( + *m_database, fmt::format( + "character_id = {} AND task_id = {} AND expire_time > NOW() ORDER BY timer_type ASC LIMIT 1", + character_id, s->GetDbSharedTask().task_id + )); + + if (!task_timers.empty()) { + auto timer_type = static_cast(task_timers.front().timer_type); + auto seconds = task_timers.front().expire_time - std::time(nullptr); + auto days = fmt::format_int(seconds / 86400).str(); + auto hours = fmt::format_int((seconds / 3600) % 24).str(); + auto mins = fmt::format_int((seconds / 60) % 60).str(); + + if (timer_type == TaskTimerType::Replay) { + SendLeaderMessageID( + s, + Chat::Red, + SharedTaskMessage::CANT_ADD_PLAYER_REPLAY_TIMER, {player_name, days, hours, mins} + ); + } + else { + SendLeaderMessage( + s, + Chat::Red, + fmt::format( + SharedTaskMessage::GetEQStr(SharedTaskMessage::CANT_ADD_PLAYER_REQUEST_TIMER), + player_name, + days, + hours, + mins + ) + ); + } + + allow_invite = false; + } + + // check if task has maximum players + if (s->GetTaskData().max_players > 0 && s->GetMembers().size() >= s->GetTaskData().max_players) { + auto max = fmt::format_int(s->GetTaskData().max_players).str(); + SendLeaderMessageID(s, Chat::Red, SharedTaskMessage::CANT_ADD_PLAYER_MAX_PLAYERS, {max}); + allow_invite = false; + } + + // check if task would exceed max level spread + if (s->GetTaskData().level_spread > 0) { + auto characters = CharacterDataRepository::GetWhere( + *m_database, + fmt::format( + "id IN (select character_id from shared_task_members where shared_task_id = {})", + s->GetDbSharedTask().id + ) + ); + + int lowest_level = cle->level(); + int highest_level = cle->level(); + + for (const auto &character : characters) { + lowest_level = std::min(lowest_level, character.level); + highest_level = std::max(highest_level, character.level); + } + + if ((highest_level - lowest_level) > s->GetTaskData().level_spread) { + auto max_spread = fmt::format_int(s->GetTaskData().level_spread).str(); + SendLeaderMessageID(s, Chat::Red, SharedTaskMessage::CANT_ADD_PLAYER_MAX_LEVEL_SPREAD, {max_spread}); + allow_invite = false; + } + } + + // check if player is below minimum level of task (pre-2014 this was average level) + if (s->GetTaskData().minlevel > 0 && cle->level() < s->GetTaskData().minlevel) { + SendLeaderMessageID(s, Chat::Red, SharedTaskMessage::CANT_ADD_PLAYER_FALL_MIN_AVG_LEVEL); + allow_invite = false; + } + + // check if player is above maximum level of task (pre-2014 this was average level) + if (s->GetTaskData().maxlevel > 0 && cle->level() > s->GetTaskData().maxlevel) { + SendLeaderMessageID(s, Chat::Red, SharedTaskMessage::CANT_ADD_PLAYER_MAX_AVERAGE_LEVEL); + allow_invite = false; + } + + if (!allow_invite) { + if (!accepted) { + SendLeaderMessageID(s, Chat::Red, SharedTaskMessage::COULD_NOT_BE_INVITED, {player_name}); + } + else { + SendLeaderMessageID(s, Chat::Red, SharedTaskMessage::ACCEPTED_OFFER_TO_JOIN_BUT_COULD_NOT, {player_name}); + } + } + + return allow_invite; +} + +void SharedTaskManager::RecordSharedTaskCompletion(SharedTask *s) +{ + // shared task + auto t = s->GetDbSharedTask(); + auto ct = CompletedSharedTasksRepository::NewEntity(); + + ct.id = t.id; + ct.task_id = t.task_id; + ct.accepted_time = t.accepted_time; + ct.expire_time = t.expire_time; + ct.completion_time = t.completion_time; + ct.is_locked = t.is_locked; + + CompletedSharedTasksRepository::InsertOne(*m_database, ct); + + // completed members + std::vector completed_members = {}; + + for (auto &m: s->GetMembers()) { + auto cm = CompletedSharedTaskMembersRepository::NewEntity(); + + cm.shared_task_id = t.id; + cm.character_id = m.character_id; + cm.is_leader = m.is_leader; + + completed_members.emplace_back(cm); + } + + CompletedSharedTaskMembersRepository::InsertMany(*m_database, completed_members); + + // activities + std::vector completed_states = {}; + + for (auto &a: s->GetActivityState()) { + auto cs = CompletedSharedTaskActivityStateRepository::NewEntity(); + + cs.shared_task_id = t.id; + cs.activity_id = (int) a.activity_id; + cs.done_count = (int) a.done_count; + cs.updated_time = a.updated_time; + cs.completed_time = a.completed_time; + + completed_states.emplace_back(cs); + } + + CompletedSharedTaskActivityStateRepository::InsertMany(*m_database, completed_states); + +} + +void SharedTaskManager::AddReplayTimers(SharedTask *s) +{ + if (s->GetTaskData().replay_timer_seconds > 0) { + auto expire_time = s->GetDbSharedTask().accepted_time + s->GetTaskData().replay_timer_seconds; + auto seconds = expire_time - std::time(nullptr); + if (seconds > 0) // not already expired + { + std::vector task_timers; + + // on live past members of the shared task also receive lockouts (use member history) + for (const auto &member_id : s->member_id_history) { + auto timer = CharacterTaskTimersRepository::NewEntity(); + timer.character_id = member_id; + timer.task_id = s->GetTaskData().id; + timer.timer_type = static_cast(TaskTimerType::Replay); + timer.expire_time = expire_time; + + task_timers.emplace_back(timer); + + client_list.SendCharacterMessage( + member_id, + Chat::Yellow, + fmt::format( + SharedTaskMessage::GetEQStr(SharedTaskMessage::RECEIVED_REPLAY_TIMER), + s->GetTaskData().title, + fmt::format_int(seconds / 86400).c_str(), // days + fmt::format_int((seconds / 3600) % 24).c_str(), // hours + fmt::format_int((seconds / 60) % 60).c_str() // minutes + ) + ); + } + + if (!task_timers.empty()) { + // replay timers replace any existing timer (even if it expires sooner) + // this can occur if a player has a timer for being a past member of + // a shared task but joined another before the first was completed + CharacterTaskTimersRepository::DeleteWhere( + *m_database, + fmt::format( + "task_id = {} AND character_id IN ({})", + s->GetTaskData().id, fmt::join(s->member_id_history, ",") + ) + ); + + CharacterTaskTimersRepository::InsertMany(*m_database, task_timers); + } + } + } +} + +// memory search +std::vector SharedTaskManager::FindCharactersInSharedTasks(const std::vector &find_characters) +{ + std::vector characters = {}; + + for (auto &s: m_shared_tasks) { + // loop through members + for (auto &m: s.GetMembers()) { + // compare members with requested characters + for (auto &find_character_id: find_characters) { + // found character, add to list + if (find_character_id == m.character_id) { + characters.emplace_back(m.character_id); + } + } + } + } + + return characters; +} + +void SharedTaskManager::PurgeAllSharedTasks() +{ + for (auto &shared_task : m_shared_tasks) { + RemoveAllMembersFromDynamicZones(&shared_task); + } + + SharedTasksRepository::Truncate(*m_database); + SharedTaskMembersRepository::Truncate(*m_database); + SharedTaskActivityStateRepository::Truncate(*m_database); + SharedTaskDynamicZonesRepository::Truncate(*m_database); + CompletedSharedTasksRepository::Truncate(*m_database); + CompletedSharedTaskMembersRepository::Truncate(*m_database); + CompletedSharedTaskActivityStateRepository::Truncate(*m_database); + + LoadSharedTaskState(); +} + +void SharedTaskManager::RemoveAllMembersFromDynamicZones(SharedTask *s) +{ + for (const auto &dz_id : s->dynamic_zone_ids) { + auto dz = DynamicZone::FindDynamicZoneByID(dz_id); + if (dz) { + dz->RemoveAllMembers(); + } + } +} + +void SharedTaskManager::ChooseNewLeader(SharedTask *s) +{ + // live doesn't prioritize choosing an online player here + auto members = s->GetMembers(); + auto it = std::find_if( + members.begin(), members.end(), + [&](const SharedTaskMember &member) { + return !member.is_leader; + } + ); + + if (it != members.end()) { + MakeLeaderByPlayerName(s, it->character_name); + } +} + +const std::vector &SharedTaskManager::GetSharedTasks() const +{ + return m_shared_tasks; +} + +void SharedTaskManager::SetSharedTasks(const std::vector &shared_tasks) +{ + SharedTaskManager::m_shared_tasks = shared_tasks; +} + +SharedTaskManager *SharedTaskManager::PurgeExpiredSharedTasks() +{ + auto now = std::time(nullptr); + for (auto &s: m_shared_tasks) { + if (s.GetDbSharedTask().expire_time > 0 && s.GetDbSharedTask().expire_time <= now) { + LogTasksDetail("[PurgeExpiredSharedTasks] Deleting expired task [{}]", s.GetDbSharedTask().id); + DeleteSharedTask(s.GetDbSharedTask().id); + } + } + + return this; +} diff --git a/world/shared_task_manager.h b/world/shared_task_manager.h new file mode 100644 index 000000000..1bbd071cd --- /dev/null +++ b/world/shared_task_manager.h @@ -0,0 +1,132 @@ +#ifndef EQEMU_SHARED_TASK_MANAGER_H +#define EQEMU_SHARED_TASK_MANAGER_H + +#include "../common/database.h" +#include "../common/shared_tasks.h" + +class DynamicZone; + +namespace EQ { + namespace Net { + class DynamicPacket; + } +} + +struct SharedTaskActiveInvitation { + uint32 shared_task_id; + uint32 character_id; +}; + +class SharedTaskManager { +public: + SharedTaskManager *SetDatabase(Database *db); + SharedTaskManager *SetContentDatabase(Database *db); + + // loads task data into memory + SharedTaskManager *LoadTaskData(); + + // loads shared task state into memory + void LoadSharedTaskState(); + + // helper, references task memory data + TasksRepository::Tasks GetSharedTaskDataByTaskId(uint32 task_id); + std::vector GetSharedTaskActivityDataByTaskId(uint32 task_id); + + // gets group / raid members belonging to requested character + std::vector GetRequestMembers( + uint32 requestor_character_id, + const std::vector &characters + ); + + // client attempting to create a shared task + void AttemptSharedTaskCreation(uint32 requested_task_id, uint32 requested_character_id, uint32 npc_type_id); + void AttemptSharedTaskRemoval(uint32 requested_task_id, uint32 requested_character_id, bool remove_from_db); + + // shared task activity update middleware + void SharedTaskActivityUpdate( + uint32 source_character_id, + uint32 task_id, + uint32 activity_id, + uint32 done_count, + bool ignore_quest_update + ); + + SharedTask *FindSharedTaskByTaskIdAndCharacterId(uint32 task_id, uint32 character_id); + SharedTask *FindSharedTaskById(int64 shared_task_id); + + void DeleteSharedTask(int64 shared_task_id); + void SaveSharedTaskActivityState(int64 shared_task_id, std::vector activity_state); + + bool IsSharedTaskLeader(SharedTask *s, uint32 character_id); + void SendAcceptNewSharedTaskPacket(uint32 character_id, uint32 task_id, uint32_t npc_context_id, int accept_time); + void SendRemovePlayerFromSharedTaskPacket(uint32 character_id, uint32 task_id, bool remove_from_db); + void SendSharedTaskMemberList(uint32 character_id, const std::vector &members); + void SendSharedTaskMemberList(uint32 character_id, const EQ::Net::DynamicPacket &serialized_members); + void SendSharedTaskMemberChange( + uint32 character_id, + int64 shared_task_id, + const std::string &player_name, + bool removed + ); + void RemovePlayerFromSharedTask(SharedTask *s, uint32 character_id); + void PrintSharedTaskState(); + void RemovePlayerFromSharedTaskByPlayerName(SharedTask *s, const std::string &character_name); + void RemoveEveryoneFromSharedTask(SharedTask *s, uint32 requested_character_id); + + void MakeLeaderByPlayerName(SharedTask *s, const std::string &character_name); + void AddPlayerByCharacterIdAndName(SharedTask *s, int64 character_id, const std::string &character_name); + void InvitePlayerByPlayerName(SharedTask *s, const std::string &player_name); + + // invitations + void QueueActiveInvitation(int64 shared_task_id, int64 character_id); + bool IsInvitationActive(uint32 shared_task_id, uint32 character_id); + void RemoveActiveInvitation(int64 shared_task_id, int64 character_id); + void RemoveActiveInvitationByCharacterID(uint32_t character_id); + + // dz + void CreateDynamicZone(SharedTask *s, DynamicZone &dz_request); + + void PurgeAllSharedTasks(); + + // messages + void SendLeaderMessage(SharedTask *s, int chat_type, const std::string &message); + void SendLeaderMessageID(SharedTask *s, int chat_type, int eqstr_id, std::initializer_list args = {}); + void SendMembersMessage(SharedTask *s, int chat_type, const std::string &message); + void SendMembersMessageID(SharedTask *s, int chat_type, int eqstr_id, std::initializer_list args = {}); + + const std::vector &GetSharedTasks() const; + void SetSharedTasks(const std::vector &shared_tasks); + + SharedTaskManager * PurgeExpiredSharedTasks(); +protected: + // reference to database + Database *m_database; + Database *m_content_database; + + // reference to task data (all) + std::vector m_task_data{}; + std::vector m_task_activity_data{}; + + // internal shared tasks list + std::vector m_shared_tasks{}; + + // store a reference of active invitations that have been sent to players + std::vector m_active_invitations{}; + + void AddReplayTimers(SharedTask *s); + bool CanAddPlayer(SharedTask *s, uint32_t character_id, std::string player_name, bool accepted); + bool CanRequestSharedTask(uint32_t task_id, uint32_t character_id, const SharedTaskRequestCharacters &request); + void ChooseNewLeader(SharedTask *s); + void SendSharedTaskMemberListToAllMembers(SharedTask *s); + void SendSharedTaskMemberAddedToAllMembers(SharedTask *s, const std::string &player_name); + void SendSharedTaskMemberRemovedToAllMembers(SharedTask *s, const std::string &player_name); + void SaveMembers(SharedTask *s, std::vector members); + void SendSharedTaskInvitePacket(SharedTask *s, int64 invited_character_id); + void RecordSharedTaskCompletion(SharedTask *s); + void RemoveAllMembersFromDynamicZones(SharedTask *s); + + // memory search + std::vector FindCharactersInSharedTasks(const std::vector &find_characters); +}; + +#endif //EQEMU_SHARED_TASK_MANAGER_H diff --git a/world/shared_task_world_messaging.cpp b/world/shared_task_world_messaging.cpp new file mode 100644 index 000000000..26da007f2 --- /dev/null +++ b/world/shared_task_world_messaging.cpp @@ -0,0 +1,355 @@ +#include "shared_task_world_messaging.h" +#include "cliententry.h" +#include "worlddb.h" +#include "../common/shared_tasks.h" +#include "../common/eqemu_logsys.h" +#include "../common/repositories/tasks_repository.h" +#include "../common/tasks.h" +#include "cliententry.h" +#include "clientlist.h" +#include "zonelist.h" +#include "zoneserver.h" +#include "shared_task_manager.h" +#include "../common/repositories/shared_task_members_repository.h" +#include "../common/repositories/task_activities_repository.h" +#include "dynamic_zone.h" + +extern ClientList client_list; +extern ZSList zoneserver_list; +extern SharedTaskManager shared_task_manager; + +void SharedTaskWorldMessaging::HandleZoneMessage(ServerPacket *pack) +{ + switch (pack->opcode) { + case ServerOP_SharedTaskRequest: { + auto *r = (ServerSharedTaskRequest_Struct *) pack->pBuffer; + LogTasksDetail( + "[ServerOP_SharedTaskRequest] Received request from character [{}] task_id [{}] npc_type_id [{}]", + r->requested_character_id, + r->requested_task_id, + r->requested_npc_type_id + ); + + shared_task_manager.AttemptSharedTaskCreation( + r->requested_task_id, + r->requested_character_id, + r->requested_npc_type_id + ); + + break; + } + case ServerOP_SharedTaskAttemptRemove: { + auto *r = (ServerSharedTaskAttemptRemove_Struct *) pack->pBuffer; + LogTasksDetail( + "[ServerOP_SharedTaskAttemptRemove] Received request from character [{}] task_id [{}] remove_from_db [{}]", + r->requested_character_id, + r->requested_task_id, + r->remove_from_db + ); + + shared_task_manager.AttemptSharedTaskRemoval( + r->requested_task_id, + r->requested_character_id, + r->remove_from_db + ); + + break; + } + case ServerOP_SharedTaskKickPlayers: { + auto r = reinterpret_cast(pack->pBuffer); + LogTasksDetail( + "[ServerOP_SharedTaskKickPlayers] Received request from character [{}] task_id [{}]", + r->source_character_id, + r->task_id + ); + + auto t = shared_task_manager.FindSharedTaskByTaskIdAndCharacterId(r->task_id, r->source_character_id); + if (t) { + auto leader = t->GetLeader(); + if (leader.character_id != r->source_character_id) { + client_list.SendCharacterMessageID( + r->source_character_id, Chat::Red, + SharedTaskMessage::YOU_ARE_NOT_LEADER_COMMAND_ISSUE, {leader.character_name} + ); + } + else { + shared_task_manager.RemoveEveryoneFromSharedTask(t, r->source_character_id); + } + } + + break; + } + case ServerOP_SharedTaskUpdate: { + auto *r = (ServerSharedTaskActivityUpdate_Struct *) pack->pBuffer; + + LogTasksDetail( + "[ServerOP_SharedTaskUpdate] Received request from character [{}] task_id [{}] activity_id [{}] donecount [{}] ignore_quest_update [{}]", + r->source_character_id, + r->task_id, + r->activity_id, + r->done_count, + (r->ignore_quest_update ? "true" : "false") + ); + + shared_task_manager.SharedTaskActivityUpdate( + r->source_character_id, + r->task_id, + r->activity_id, + r->done_count, + r->ignore_quest_update + ); + + break; + } + case ServerOP_SharedTaskRequestMemberlist: { + auto *r = (ServerSharedTaskRequestMemberlist_Struct *) pack->pBuffer; + + LogTasksDetail( + "[ServerOP_SharedTaskRequestMemberlist] Received request from character [{}] task_id [{}]", + r->source_character_id, + r->task_id + ); + + auto t = shared_task_manager.FindSharedTaskByTaskIdAndCharacterId(r->task_id, r->source_character_id); + if (t) { + LogTasksDetail( + "[ServerOP_SharedTaskRequestMemberlist] Found shared task character [{}] shared_task_id [{}]", + r->source_character_id, + t->GetDbSharedTask().id + ); + + shared_task_manager.SendSharedTaskMemberList( + r->source_character_id, + t->GetMembers() + ); + } + + break; + } + case ServerOP_SharedTaskRemovePlayer: { + auto *r = (ServerSharedTaskRemovePlayer_Struct *) pack->pBuffer; + + LogTasksDetail( + "[ServerOP_SharedTaskRemovePlayer] Received request from character [{}] task_id [{}] player_name [{}]", + r->source_character_id, + r->task_id, + r->player_name + ); + + auto t = shared_task_manager.FindSharedTaskByTaskIdAndCharacterId(r->task_id, r->source_character_id); + if (t) { + LogTasksDetail( + "[ServerOP_SharedTaskRemovePlayer] Found shared task character [{}] shared_task_id [{}]", + r->source_character_id, + t->GetDbSharedTask().id + ); + + auto leader = t->GetLeader(); + if (leader.character_id != r->source_character_id) { + client_list.SendCharacterMessageID( + r->source_character_id, Chat::Red, + SharedTaskMessage::YOU_ARE_NOT_LEADER_COMMAND_ISSUE, {leader.character_name} + ); + } + else { + LogTasksDetail( + "[ServerOP_SharedTaskRemovePlayer] character_id [{}] shared_task_id [{}] is_leader", + r->source_character_id, + t->GetDbSharedTask().id + ); + + std::string character_name = r->player_name; + shared_task_manager.RemovePlayerFromSharedTaskByPlayerName(t, character_name); + } + } + + break; + } + case ServerOP_SharedTaskMakeLeader: { + auto *r = (ServerSharedTaskMakeLeader_Struct *) pack->pBuffer; + + LogTasksDetail( + "[ServerOP_SharedTaskMakeLeader] Received request from character [{}] task_id [{}] player_name [{}]", + r->source_character_id, + r->task_id, + r->player_name + ); + + auto t = shared_task_manager.FindSharedTaskByTaskIdAndCharacterId(r->task_id, r->source_character_id); + if (t) { + LogTasksDetail( + "[ServerOP_SharedTaskMakeLeader] Found shared task character [{}] shared_task_id [{}]", + r->source_character_id, + t->GetDbSharedTask().id + ); + + auto leader = t->GetLeader(); + if (leader.character_id != r->source_character_id) { + client_list.SendCharacterMessageID( + r->source_character_id, Chat::Red, + SharedTaskMessage::YOU_ARE_NOT_LEADER_COMMAND_ISSUE, {leader.character_name} + ); + } + else if (strcasecmp(leader.character_name.c_str(), r->player_name) == 0) { + client_list.SendCharacterMessageID( + r->source_character_id, + Chat::Red, + SharedTaskMessage::YOU_ALREADY_LEADER + ); + } + else { + LogTasksDetail( + "[ServerOP_SharedTaskMakeLeader] character_id [{}] shared_task_id [{}] is_leader", + r->source_character_id, + t->GetDbSharedTask().id + ); + + std::string character_name = r->player_name; + shared_task_manager.MakeLeaderByPlayerName(t, character_name); + } + } + + break; + } + case ServerOP_SharedTaskAddPlayer: { + auto *r = (ServerSharedTaskAddPlayer_Struct *) pack->pBuffer; + + LogTasksDetail( + "[ServerOP_SharedTaskAddPlayer] Received request from character [{}] task_id [{}] player_name [{}]", + r->source_character_id, + r->task_id, + r->player_name + ); + + auto t = shared_task_manager.FindSharedTaskByTaskIdAndCharacterId(r->task_id, r->source_character_id); + if (t) { + LogTasksDetail( + "[ServerOP_SharedTaskAddPlayer] Found shared task character [{}] shared_task_id [{}]", + r->source_character_id, + t->GetDbSharedTask().id + ); + + auto leader = t->GetLeader(); + if (leader.character_id != r->source_character_id) { + // taskadd is client sided with System color in newer clients, server side might still be red + client_list.SendCharacterMessageID( + r->source_character_id, Chat::Red, + SharedTaskMessage::YOU_ARE_NOT_LEADER_COMMAND_ISSUE, {leader.character_name} + ); + } + else { + LogTasksDetail( + "[ServerOP_SharedTaskAddPlayer] character_id [{}] shared_task_id [{}] is_leader", + r->source_character_id, + t->GetDbSharedTask().id + ); + + std::string character_name = r->player_name; + shared_task_manager.InvitePlayerByPlayerName(t, character_name); + } + } + + break; + } + case ServerOP_SharedTaskInviteAcceptedPlayer: { + auto *r = (ServerSharedTaskInviteAccepted_Struct *) pack->pBuffer; + + LogTasksDetail( + "[ServerOP_SharedTaskInviteAcceptedPlayer] Received request from source_character_id [{}] shared_task_id [{}] accepted [{}]", + r->source_character_id, + r->shared_task_id, + r->accepted + ); + + auto t = shared_task_manager.FindSharedTaskById(r->shared_task_id); + if (t && shared_task_manager.IsInvitationActive(r->shared_task_id, r->source_character_id)) { + LogTasksDetail( + "[ServerOP_SharedTaskInviteAcceptedPlayer] Found shared task character [{}] shared_task_id [{}]", + r->source_character_id, + t->GetDbSharedTask().id + ); + + shared_task_manager.RemoveActiveInvitation(r->shared_task_id, r->source_character_id); + + if (r->accepted) { + shared_task_manager.AddPlayerByCharacterIdAndName(t, r->source_character_id, r->player_name); + } + else { + shared_task_manager.SendLeaderMessageID( + t, + Chat::Red, + SharedTaskMessage::PLAYER_DECLINED_OFFER, + {r->player_name} + ); + } + } + break; + } + case ServerOP_SharedTaskCreateDynamicZone: { + auto buf = reinterpret_cast(pack->pBuffer); + + LogTasksDetail( + "[ServerOP_SharedTaskCreateDynamicZone] Received request from source_character_id [{}] task_id [{}]", + buf->source_character_id, + buf->task_id + ); + + auto t = shared_task_manager.FindSharedTaskByTaskIdAndCharacterId(buf->task_id, buf->source_character_id); + if (t) { + DynamicZone dz; + dz.LoadSerializedDzPacket(buf->cereal_data, buf->cereal_size); + + shared_task_manager.CreateDynamicZone(t, dz); + } + break; + } + case ServerOP_SharedTaskPurgeAllCommand: { + LogTasksDetail("[ServerOP_SharedTaskPurgeAllCommand] Received request to purge all shared tasks"); + + shared_task_manager.PurgeAllSharedTasks(); + auto p = std::make_unique( + ServerOP_SharedTaskPurgeAllCommand, + 0 + ); + + zoneserver_list.SendPacket(p.get()); + + break; + } + case ServerOP_SharedTaskPlayerList: { + auto buf = reinterpret_cast(pack->pBuffer); + + LogTasksDetail( + "[ServerOP_SharedTaskPlayerList] Received request from source_character_id [{}] task_id [{}]", + buf->source_character_id, + buf->task_id + ); + + auto s = shared_task_manager.FindSharedTaskByTaskIdAndCharacterId(buf->task_id, buf->source_character_id); + if (s) { + std::vector player_names; + + for (const auto &member : s->GetMembers()) { + player_names.emplace_back(member.character_name); + + if (member.is_leader) { + client_list.SendCharacterMessageID( + buf->source_character_id, Chat::Yellow, + SharedTaskMessage::LEADER_PRINT, {member.character_name} + ); + } + } + + std::string player_list = fmt::format("{}", fmt::join(player_names, ", ")); + client_list.SendCharacterMessageID( + buf->source_character_id, Chat::Yellow, + SharedTaskMessage::MEMBERS_PRINT, {player_list} + ); + } + + break; + } + default: + break; + } +} diff --git a/world/shared_task_world_messaging.h b/world/shared_task_world_messaging.h new file mode 100644 index 000000000..e8062e2ec --- /dev/null +++ b/world/shared_task_world_messaging.h @@ -0,0 +1,18 @@ +#ifndef EQEMU_SHARED_TASK_WORLD_MESSAGING_H +#define EQEMU_SHARED_TASK_WORLD_MESSAGING_H + +#include "../common/types.h" +#include "../common/servertalk.h" +#include "../common/shared_tasks.h" +#include "../common/eqemu_logsys.h" +#include "../common/repositories/tasks_repository.h" +#include "../common/tasks.h" + +class SharedTaskWorldMessaging { +public: + static void HandleZoneMessage(ServerPacket *pack); +}; + + +#endif //EQEMU_SHARED_TASK_WORLD_MESSAGING_H + diff --git a/world/worlddb.cpp b/world/worlddb.cpp index 14decb79d..c9cd497ee 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -25,6 +25,7 @@ #include #include #include "sof_char_create_data.h" +#include "../common/repositories/character_instance_safereturns_repository.h" #include "../common/repositories/criteria/content_filter_criteria.h" #include "world_store.h" @@ -477,6 +478,54 @@ int WorldDatabase::MoveCharacterToBind(int character_id, uint8 bind_number) return zone_id; } +int WorldDatabase::MoveCharacterToInstanceSafeReturn( + int character_id, + int instance_zone_id, + int instance_id +) +{ + int zone_id = 0; + + // only moves if safe return is for specified zone instance + auto entries = CharacterInstanceSafereturnsRepository::GetWhere( + database, + fmt::format( + "character_id = {} AND instance_zone_id = {} AND instance_id = {} AND safe_zone_id > 0", + character_id, instance_zone_id, instance_id + ) + ); + + if (!entries.empty()) { + auto entry = entries.front(); + + auto results = QueryDatabase( + fmt::format( + SQL( + UPDATE character_data + SET zone_id = {}, zone_instance = 0, x = {}, y = {}, z = {}, heading = {} + WHERE id = {}; + ), + entry.safe_zone_id, + entry.safe_x, + entry.safe_y, + entry.safe_z, + entry.safe_heading, + character_id + ) + ); + + if (results.Success() && results.RowsAffected() > 0) { + zone_id = entry.safe_zone_id; + } + } + + if (zone_id == 0) { + zone_id = MoveCharacterToBind(character_id); + } + + return zone_id; +} + bool WorldDatabase::GetStartZone( PlayerProfile_Struct *p_player_profile_struct, CharCreate_Struct *p_char_create_struct, diff --git a/world/worlddb.h b/world/worlddb.h index 6431f175e..ae26b0ff7 100644 --- a/world/worlddb.h +++ b/world/worlddb.h @@ -32,6 +32,7 @@ public: bool GetStartZone(PlayerProfile_Struct* p_player_profile_struct, CharCreate_Struct* p_char_create_struct, bool is_titanium); void GetCharSelectInfo(uint32 account_id, EQApplicationPacket **out_app, uint32 client_version_bit); int MoveCharacterToBind(int character_id, uint8 bind_number = 0); + int MoveCharacterToInstanceSafeReturn(int character_id, int instance_zone_id, int instance_id); void GetLauncherList(std::vector &result); bool GetCharacterLevel(const char *name, int &level); diff --git a/world/zonelist.cpp b/world/zonelist.cpp index 666ed2208..9e1239a6c 100644 --- a/world/zonelist.cpp +++ b/world/zonelist.cpp @@ -43,7 +43,7 @@ ZSList::ZSList() memset(pLockedZones, 0, sizeof(pLockedZones)); m_tick = std::make_unique(5000, true, std::bind(&ZSList::OnTick, this, std::placeholders::_1)); - m_keepalive = std::make_unique(1500, true, std::bind(&ZSList::OnKeepAlive, this, std::placeholders::_1)); + m_keepalive = std::make_unique(1000, true, std::bind(&ZSList::OnKeepAlive, this, std::placeholders::_1)); } ZSList::~ZSList() { diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index c53b474af..d6f4c1888 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -38,6 +38,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "world_store.h" #include "dynamic_zone.h" #include "expedition_message.h" +#include "shared_task_world_messaging.h" +#include "../common/shared_tasks.h" extern ClientList client_list; extern GroupLFPList LFPGroupList; @@ -1181,7 +1183,6 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { adventure_manager.IncrementAssassinationCount(*((uint16*)pack->pBuffer)); break; } - case ServerOP_AdventureZoneData: { adventure_manager.GetZoneData(*((uint16*)pack->pBuffer)); @@ -1368,17 +1369,28 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { case ServerOP_ExpeditionLockoutDuration: case ServerOP_ExpeditionLockState: case ServerOP_ExpeditionReplayOnJoin: - case ServerOP_ExpeditionExpireWarning: { zoneserver_list.SendPacket(pack); break; } + case ServerOP_SharedTaskRequest: + case ServerOP_SharedTaskAddPlayer: + case ServerOP_SharedTaskAttemptRemove: + case ServerOP_SharedTaskUpdate: + case ServerOP_SharedTaskRequestMemberlist: + case ServerOP_SharedTaskRemovePlayer: + case ServerOP_SharedTaskInviteAcceptedPlayer: + case ServerOP_SharedTaskMakeLeader: + case ServerOP_SharedTaskCreateDynamicZone: + case ServerOP_SharedTaskPurgeAllCommand: + case ServerOP_SharedTaskPlayerList: + case ServerOP_SharedTaskKickPlayers: + { + SharedTaskWorldMessaging::HandleZoneMessage(pack); + break; + } + case ServerOP_ExpeditionCreate: - case ServerOP_ExpeditionGetMemberStatuses: - case ServerOP_ExpeditionMemberChange: - case ServerOP_ExpeditionMemberStatus: - case ServerOP_ExpeditionMemberSwap: - case ServerOP_ExpeditionMembersRemoved: case ServerOP_ExpeditionDzAddPlayer: case ServerOP_ExpeditionDzMakeLeader: case ServerOP_ExpeditionCharacterLockout: @@ -1388,12 +1400,16 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { ExpeditionMessage::HandleZoneMessage(pack); break; } - case ServerOP_DzAddRemoveCharacter: - case ServerOP_DzRemoveAllCharacters: + case ServerOP_DzCreated: + case ServerOP_DzAddRemoveMember: + case ServerOP_DzSwapMembers: + case ServerOP_DzRemoveAllMembers: + case ServerOP_DzGetMemberStatuses: case ServerOP_DzSetSecondsRemaining: case ServerOP_DzSetCompass: case ServerOP_DzSetSafeReturn: case ServerOP_DzSetZoneIn: + case ServerOP_DzUpdateMemberStatus: { DynamicZone::HandleZoneMessage(pack); break; diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 554d3e717..909c88e77 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -129,6 +129,7 @@ SET(zone_sources quest_parser_collection.cpp raids.cpp raycast_mesh.cpp + shared_task_zone_messaging.cpp spawn2.cpp spawn2.h spawngroup.cpp @@ -255,6 +256,7 @@ SET(zone_headers raids.h raycast_mesh.h skills.h + shared_task_zone_messaging.h spawn2.cpp spawn2.h spawngroup.h diff --git a/zone/attack.cpp b/zone/attack.cpp index 5b2bc8a19..16ee8e69e 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2350,6 +2350,18 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy int32 finalxp = give_exp_client->GetExperienceForKill(this); finalxp = give_exp_client->mod_client_xp(finalxp, this); + // handle task credit on behalf of the killer + if (RuleB(TaskSystem, EnableTaskSystem)) { + LogTasksDetail( + "[NPC::Death] Triggering HandleUpdateTasksOnKill for [{}] npc [{}]", + give_exp_client->GetCleanName(), + GetNPCTypeID() + ); + give_exp_client + ->GetTaskState() + ->HandleUpdateTasksOnKill(give_exp_client, GetNPCTypeID()); + } + if (kr) { if (!IsLdonTreasure && MerchantType == 0) { kr->SplitExp((finalxp), this); @@ -2368,8 +2380,6 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy mod_npc_killed_merit(kr->members[i].member); - if (RuleB(TaskSystem, EnableTaskSystem)) - kr->members[i].member->UpdateTasksOnKill(GetNPCTypeID()); PlayerCount++; } } @@ -2417,9 +2427,6 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy mod_npc_killed_merit(c); - if (RuleB(TaskSystem, EnableTaskSystem)) - c->UpdateTasksOnKill(GetNPCTypeID()); - PlayerCount++; } } @@ -2468,9 +2475,6 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy mod_npc_killed_merit(give_exp_client); - if (RuleB(TaskSystem, EnableTaskSystem)) - give_exp_client->UpdateTasksOnKill(GetNPCTypeID()); - // QueryServ Logging - Solo if (RuleB(QueryServ, PlayerLogNPCKills)) { auto pack = new ServerPacket(ServerOP_QSPlayerLogNPCKills, diff --git a/zone/client.cpp b/zone/client.cpp index 499fc659f..7584a0bd1 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -348,9 +348,10 @@ Client::Client(EQStreamInterface* ieqs) temp_pvp = false; is_client_moving = false; - /** - * GM - */ + // rate limiter + m_list_task_timers_rate_limit.Start(1000); + + // gm SetDisplayMobInfoWindow(true); SetDevToolsEnabled(true); @@ -1550,46 +1551,56 @@ void Client::SendSound(){//Makes a sound. safe_delete(outapp); } -void Client::UpdateWho(uint8 remove) { - if (account_id == 0) +void Client::UpdateWho(uint8 remove) +{ + if (account_id == 0) { return; - if (!worldserver.Connected()) + } + if (!worldserver.Connected()) { return; + } + auto pack = new ServerPacket(ServerOP_ClientList, sizeof(ServerClientList_Struct)); - ServerClientList_Struct* scl = (ServerClientList_Struct*) pack->pBuffer; - scl->remove = remove; - scl->wid = this->GetWID(); - scl->IP = this->GetIP(); - scl->charid = this->CharacterID(); - strcpy(scl->name, this->GetName()); + auto *s = (ServerClientList_Struct *) pack->pBuffer; + s->remove = remove; + s->wid = this->GetWID(); + s->IP = this->GetIP(); + s->charid = this->CharacterID(); + strcpy(s->name, this->GetName()); - scl->gm = GetGM(); - scl->Admin = this->Admin(); - scl->AccountID = this->AccountID(); - strcpy(scl->AccountName, this->AccountName()); - scl->LSAccountID = this->LSAccountID(); - strn0cpy(scl->lskey, lskey, sizeof(scl->lskey)); - scl->zone = zone->GetZoneID(); - scl->instance_id = zone->GetInstanceID(); - scl->race = this->GetRace(); - scl->class_ = GetClass(); - scl->level = GetLevel(); - if (m_pp.anon == 0) - scl->anon = 0; - else if (m_pp.anon == 1) - scl->anon = 1; - else if (m_pp.anon >= 2) - scl->anon = 2; + s->gm = GetGM(); + s->Admin = this->Admin(); + s->AccountID = this->AccountID(); + strcpy(s->AccountName, this->AccountName()); - scl->ClientVersion = static_cast(ClientVersion()); - scl->tellsoff = tellsoff; - scl->guild_id = guild_id; - scl->LFG = LFG; - if(LFG) { - scl->LFGFromLevel = LFGFromLevel; - scl->LFGToLevel = LFGToLevel; - scl->LFGMatchFilter = LFGMatchFilter; - memcpy(scl->LFGComments, LFGComments, sizeof(scl->LFGComments)); + s->LSAccountID = this->LSAccountID(); + strn0cpy(s->lskey, lskey, sizeof(s->lskey)); + + s->zone = zone->GetZoneID(); + s->instance_id = zone->GetInstanceID(); + s->race = this->GetRace(); + s->class_ = GetClass(); + s->level = GetLevel(); + + if (m_pp.anon == 0) { + s->anon = 0; + } + else if (m_pp.anon == 1) { + s->anon = 1; + } + else if (m_pp.anon >= 2) { + s->anon = 2; + } + + s->ClientVersion = static_cast(ClientVersion()); + s->tellsoff = tellsoff; + s->guild_id = guild_id; + s->LFG = LFG; + if (LFG) { + s->LFGFromLevel = LFGFromLevel; + s->LFGToLevel = LFGToLevel; + s->LFGMatchFilter = LFGMatchFilter; + memcpy(s->LFGComments, LFGComments, sizeof(s->LFGComments)); } worldserver.SendPacket(pack); @@ -3368,11 +3379,7 @@ void Client::LinkDead() raid->MemberZoned(this); } - Expedition* expedition = GetExpedition(); - if (expedition) - { - expedition->SetMemberStatus(this, DynamicZoneMemberStatus::LinkDead); - } + SetDynamicZoneMemberStatus(DynamicZoneMemberStatus::LinkDead); // save_timer.Start(2500); linkdead_timer.Start(RuleI(Zone,ClientLinkdeadMS)); @@ -5730,16 +5737,27 @@ void Client::AddPVPPoints(uint32 Points) SendPVPStats(); } -void Client::AddCrystals(uint32 Radiant, uint32 Ebon) +void Client::AddCrystals(uint32 radiant, uint32 ebon) { - m_pp.currentRadCrystals += Radiant; - m_pp.careerRadCrystals += Radiant; - m_pp.currentEbonCrystals += Ebon; - m_pp.careerEbonCrystals += Ebon; + m_pp.currentRadCrystals += radiant; + m_pp.careerRadCrystals += radiant; + m_pp.currentEbonCrystals += ebon; + m_pp.careerEbonCrystals += ebon; SaveCurrency(); SendCrystalCounts(); + + // newer clients handle message client side (older clients likely used eqstr 5967 and 5968, this matches live) + if (radiant > 0) + { + MessageString(Chat::Yellow, YOU_RECEIVE, fmt::format("{} Radiant Crystals", radiant).c_str()); + } + + if (ebon > 0) + { + MessageString(Chat::Yellow, YOU_RECEIVE, fmt::format("{} Ebon Crystals", ebon).c_str()); + } } void Client::SetEbonCrystals(uint32 value) { @@ -9515,28 +9533,25 @@ void Client::SendCrossZoneMessageString( } } -void Client::UpdateExpeditionInfoAndLockouts() +void Client::SendDynamicZoneUpdates() { - // this is processed by client after entering a zone + // bit inefficient since each do lookups but it avoids duplicating code here SendDzCompassUpdate(); + SetDynamicZoneMemberStatus(DynamicZoneMemberStatus::Online); m_expedition_lockouts = ExpeditionDatabase::LoadCharacterLockouts(CharacterID()); + // expeditions are the only dz type that keep the window updated auto expedition = GetExpedition(); if (expedition) { - expedition->SendClientExpeditionInfo(this); + expedition->GetDynamicZone()->SendClientWindowUpdate(this); // live synchronizes lockouts obtained during the active expedition to // members once they zone into the expedition's dynamic zone instance - if (expedition->GetDynamicZone().IsCurrentZoneDzInstance()) + if (expedition->GetDynamicZone()->IsCurrentZoneDzInstance()) { expedition->SyncCharacterLockouts(CharacterID(), m_expedition_lockouts); - expedition->SetMemberStatus(this, DynamicZoneMemberStatus::InDynamicZone); - } - else - { - expedition->SetMemberStatus(this, DynamicZoneMemberStatus::Online); } } @@ -9546,18 +9561,29 @@ void Client::UpdateExpeditionInfoAndLockouts() RequestPendingExpeditionInvite(); } -Expedition* Client::CreateExpedition(DynamicZone& dz_instance, ExpeditionRequest& request) +Expedition* Client::CreateExpedition(DynamicZone& dz, bool disable_messages) { - return Expedition::TryCreate(this, dz_instance, request); + return Expedition::TryCreate(this, dz, disable_messages); } Expedition* Client::CreateExpedition( const std::string& zone_name, uint32 version, uint32 duration, const std::string& expedition_name, uint32 min_players, uint32 max_players, bool disable_messages) { - DynamicZone dz_instance{ ZoneID(zone_name), version, duration, DynamicZoneType::Expedition }; - ExpeditionRequest request{ expedition_name, min_players, max_players, disable_messages }; - return Expedition::TryCreate(this, dz_instance, request); + DynamicZone dz{ ZoneID(zone_name), version, duration, DynamicZoneType::Expedition }; + dz.SetName(expedition_name); + dz.SetMinPlayers(min_players); + dz.SetMaxPlayers(max_players); + + return Expedition::TryCreate(this, dz, disable_messages); +} + +void Client::CreateTaskDynamicZone(int task_id, DynamicZone& dz_request) +{ + if (task_state) + { + task_state->CreateTaskDynamicZone(this, task_id, dz_request); + } } Expedition* Client::GetExpedition() const @@ -9877,24 +9903,59 @@ void Client::GoToDzSafeReturnOrBind(const DynamicZone* dynamic_zone) GoToBind(); } +void Client::AddDynamicZoneID(uint32_t dz_id) +{ + auto it = std::find_if(m_dynamic_zone_ids.begin(), m_dynamic_zone_ids.end(), + [&](uint32_t current_dz_id) { return current_dz_id == dz_id; }); + + if (it == m_dynamic_zone_ids.end()) + { + LogDynamicZonesDetail("Adding dz [{}] to client [{}]", dz_id, GetName()); + m_dynamic_zone_ids.push_back(dz_id); + } +} + +void Client::RemoveDynamicZoneID(uint32_t dz_id) +{ + LogDynamicZonesDetail("Removing dz [{}] from client [{}]", dz_id, GetName()); + m_dynamic_zone_ids.erase(std::remove_if(m_dynamic_zone_ids.begin(), m_dynamic_zone_ids.end(), + [&](uint32_t current_dz_id) { return current_dz_id == dz_id; } + ), m_dynamic_zone_ids.end()); +} + std::vector Client::GetDynamicZones(uint32_t zone_id, int zone_version) { std::vector client_dzs; - // check client systems for any associated dynamic zones optionally filtered by zone - Expedition* expedition = GetExpedition(); - if (expedition && - (zone_id == 0 || expedition->GetDynamicZone().GetZoneID() == zone_id) && - (zone_version < 0 || expedition->GetDynamicZone().GetZoneVersion() == zone_version)) + for (uint32_t dz_id : m_dynamic_zone_ids) { - client_dzs.emplace_back(&expedition->GetDynamicZone()); + auto dz = DynamicZone::FindDynamicZoneByID(dz_id); + if (dz && + (zone_id == 0 || dz->GetZoneID() == zone_id) && + (zone_version < 0 || dz->GetZoneVersion() == zone_version)) + { + client_dzs.emplace_back(dz); + } } - // todo: tasks, missions (shared tasks), and quests with an associated dz to zone_id - return client_dzs; } +void Client::SetDynamicZoneMemberStatus(DynamicZoneMemberStatus status) +{ + // sets status on all associated dzs client may have. if client is online + // inside a dz, only that dz has the "In Dynamic Zone" status set + for (auto& dz : GetDynamicZones()) + { + // the rule to disable this status is handled internally by the dz + if (status == DynamicZoneMemberStatus::Online && dz->IsCurrentZoneDzInstance()) + { + status = DynamicZoneMemberStatus::InDynamicZone; + } + dz->SetMemberStatus(CharacterID(), status); + } +} + void Client::MovePCDynamicZone(uint32 zone_id, int zone_version, bool msg_if_invalid) { if (zone_id == 0) diff --git a/zone/client.h b/zone/client.h index 11cbac385..dcc177e20 100644 --- a/zone/client.h +++ b/zone/client.h @@ -80,6 +80,7 @@ namespace EQ #include #include #include +#include #define CLIENT_TIMEOUT 90000 @@ -1030,6 +1031,9 @@ public: void SendTaskActivityComplete(int task_id, int activity_id, int task_index, TaskType task_type, int task_incomplete=1); void SendTaskFailed(int task_id, int task_index, TaskType task_type); void SendTaskComplete(int task_index); + bool HasTaskRequestCooldownTimer(); + void SendTaskRequestCooldownTimerMessage(); + void StartTaskRequestCooldownTimer(); inline ClientTaskState *GetTaskState() const { return task_state; } inline void CancelTask(int task_index, TaskType task_type) { @@ -1095,7 +1099,7 @@ public: } } inline void UpdateTasksForItem( - ActivityType activity_type, + TaskActivityType activity_type, int item_id, int count = 1 ) @@ -1202,7 +1206,7 @@ public: bool enforce_level_requirement = false ) { if (task_state) { - task_state->AcceptNewTask(this, task_id, npc_id, enforce_level_requirement); + task_state->AcceptNewTask(this, task_id, npc_id, std::time(nullptr), enforce_level_requirement); } } inline int ActiveSpeakTask(int npc_type_id) @@ -1262,6 +1266,15 @@ public: { return (task_state ? task_state->CompletedTasksInSet(task_set_id) : 0); } + void PurgeTaskTimers(); + + // shared task shims / middleware + // these variables are used as a shim to intercept normal localized task functionality + // and pipe it into zone -> world and back to world -> zone + // world is authoritative + bool m_requesting_shared_task = false; + bool m_shared_task_update = false; + bool m_requested_shared_task_removal = false; inline const EQ::versions::ClientVersion ClientVersion() const { return m_ClientVersion; } inline const uint32 ClientVersionBit() const { return m_ClientVersionBit; } @@ -1330,9 +1343,9 @@ public: const std::string& event_Name, int seconds, const std::string& uuid = {}, bool update_db = false); void AddNewExpeditionLockout(const std::string& expedition_name, const std::string& event_name, uint32_t duration, std::string uuid = {}); - Expedition* CreateExpedition(DynamicZone& dz_instance, ExpeditionRequest& request); - Expedition* CreateExpedition( - const std::string& zone_name, uint32 version, uint32 duration, const std::string& expedition_name, + Expedition* CreateExpedition(DynamicZone& dz, bool disable_messages = false); + Expedition* CreateExpedition(const std::string& zone_name, + uint32 version, uint32 duration, const std::string& expedition_name, uint32 min_players, uint32 max_players, bool disable_messages = false); Expedition* GetExpedition() const; uint32 GetExpeditionID() const { return m_expedition_id; } @@ -1350,7 +1363,6 @@ public: void SendExpeditionLockoutTimers(); void SetExpeditionID(uint32 expedition_id) { m_expedition_id = expedition_id; }; void SetPendingExpeditionInvite(ExpeditionInvite&& invite) { m_pending_expedition_invite = invite; } - void UpdateExpeditionInfoAndLockouts(); void DzListTimers(); void SetDzRemovalTimer(bool enable_timer); void SendDzCompassUpdate(); @@ -1360,6 +1372,11 @@ public: std::vector GetDynamicZones(uint32_t zone_id = 0, int zone_version = -1); std::unique_ptr CreateDzSwitchListPacket(const std::vector& dzs); std::unique_ptr CreateCompassPacket(const std::vector& entries); + void AddDynamicZoneID(uint32_t dz_id); + void RemoveDynamicZoneID(uint32_t dz_id); + void SendDynamicZoneUpdates(); + void SetDynamicZoneMemberStatus(DynamicZoneMemberStatus status); + void CreateTaskDynamicZone(int task_id, DynamicZone& dz_request); void CalcItemScale(); bool CalcItemScale(uint32 slot_x, uint32 slot_y); // behavior change: 'slot_y' is now [RANGE]_END and not [RANGE]_END + 1 @@ -1590,6 +1607,9 @@ public: void ShowDevToolsMenu(); CheatManager cheat_manager; + // rate limit + Timer m_list_task_timers_rate_limit = {}; + protected: friend class Mob; void CalcItemBonuses(StatBonuses* newbon); @@ -1821,6 +1841,7 @@ private: Timer position_update_timer; /* Timer used when client hasn't updated within a 10 second window */ Timer consent_throttle_timer; Timer dynamiczone_removal_timer; + Timer task_request_timer; glm::vec3 m_Proximity; glm::vec4 last_position_before_bulk_update; @@ -1855,6 +1876,14 @@ private: ClientTaskState *task_state; int TotalSecondsPlayed; + // we use this very sparingly at the zone level + // used for keeping clients in donecount sync before world sends absolute confirmations of state + int64 m_shared_task_id = 0; +public: + void SetSharedTaskId(int64 shared_task_id); + int64 GetSharedTaskId() const; +private: + //Anti Spam Stuff Timer *KarmaUpdateTimer; uint32 TotalKarma; @@ -1927,6 +1956,7 @@ private: std::vector m_expedition_lockouts; glm::vec3 m_quest_compass; bool m_has_quest_compass = false; + std::vector m_dynamic_zone_ids; #ifdef BOTS diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index a1e3eb9c0..1365dbd56 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -63,7 +63,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "worldserver.h" #include "zone.h" #include "mob_movement_manager.h" +#include "../common/repositories/character_instance_safereturns_repository.h" #include "../common/repositories/criteria/content_filter_criteria.h" +#include "../common/shared_tasks.h" #ifdef BOTS #include "bot.h" @@ -381,6 +383,7 @@ void MapOpcodes() ConnectedOpcodes[OP_TargetCommand] = &Client::Handle_OP_TargetCommand; ConnectedOpcodes[OP_TargetMouse] = &Client::Handle_OP_TargetMouse; ConnectedOpcodes[OP_TaskHistoryRequest] = &Client::Handle_OP_TaskHistoryRequest; + ConnectedOpcodes[OP_TaskTimers] = &Client::Handle_OP_TaskTimers; ConnectedOpcodes[OP_Taunt] = &Client::Handle_OP_Taunt; ConnectedOpcodes[OP_TestBuff] = &Client::Handle_OP_TestBuff; ConnectedOpcodes[OP_TGB] = &Client::Handle_OP_TGB; @@ -414,6 +417,15 @@ void MapOpcodes() ConnectedOpcodes[OP_ZoneChange] = &Client::Handle_OP_ZoneChange; ConnectedOpcodes[OP_ResetAA] = &Client::Handle_OP_ResetAA; ConnectedOpcodes[OP_UnderWorld] = &Client::Handle_OP_UnderWorld; + + // shared tasks + ConnectedOpcodes[OP_SharedTaskRemovePlayer] = &Client::Handle_OP_SharedTaskRemovePlayer; + ConnectedOpcodes[OP_SharedTaskAddPlayer] = &Client::Handle_OP_SharedTaskAddPlayer; + ConnectedOpcodes[OP_SharedTaskMakeLeader] = &Client::Handle_OP_SharedTaskMakeLeader; + ConnectedOpcodes[OP_SharedTaskInviteResponse] = &Client::Handle_OP_SharedTaskInviteResponse; + ConnectedOpcodes[OP_SharedTaskAcceptNew] = &Client::Handle_OP_SharedTaskAccept; + ConnectedOpcodes[OP_SharedTaskQuit] = &Client::Handle_OP_SharedTaskQuit; + ConnectedOpcodes[OP_SharedTaskPlayerList] = &Client::Handle_OP_SharedTaskPlayerList; } void ClearMappedOpcode(EmuOpcode op) @@ -526,6 +538,9 @@ void Client::CompleteConnect() /* Sets GM Flag if needed & Sends Petition Queue */ UpdateAdmin(false); + // Task Packets + LoadClientTaskState(); + if (IsInAGuild()) { uint8 rank = GuildRank(); if (ClientVersion() >= EQ::versions::ClientVersion::RoF) @@ -899,7 +914,7 @@ void Client::CompleteConnect() guild_mgr.RequestOnlineGuildMembers(this->CharacterID(), this->GuildID()); } - UpdateExpeditionInfoAndLockouts(); + SendDynamicZoneUpdates(); /** Request adventure info **/ auto pack = new ServerPacket(ServerOP_AdventureDataRequest, 64); @@ -940,6 +955,25 @@ void Client::CompleteConnect() ShowDevToolsMenu(); } + // shared tasks memberlist + if (GetTaskState()->HasActiveSharedTask()) { + + // struct + auto p = new ServerPacket( + ServerOP_SharedTaskRequestMemberlist, + sizeof(ServerSharedTaskRequestMemberlist_Struct) + ); + + auto *r = (ServerSharedTaskRequestMemberlist_Struct *) p->pBuffer; + + // fill + r->source_character_id = CharacterID(); + r->task_id = GetTaskState()->GetActiveSharedTask().task_id; + + // send + worldserver.SendPacket(p); + safe_delete(p); + } } // connecting opcode handlers @@ -1716,13 +1750,41 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) m_inv.SetGMInventory((bool)m_pp.gm); // reset back to current gm state } - /* Task Packets */ - LoadClientTaskState(); - ApplyWeaponsStance(); + auto dynamic_zone_member_entries = DynamicZoneMembersRepository::GetWhere(database, + fmt::format("character_id = {}", CharacterID())); + + for (const auto& entry : dynamic_zone_member_entries) + { + m_dynamic_zone_ids.emplace_back(entry.dynamic_zone_id); + } + m_expedition_id = ExpeditionsRepository::GetIDByMemberID(database, CharacterID()); + auto dz = zone->GetDynamicZone(); + if (dz && dz->GetSafeReturnLocation().zone_id != 0) + { + auto safereturn = dz->GetSafeReturnLocation(); + + auto safereturn_entry = CharacterInstanceSafereturnsRepository::NewEntity(); + safereturn_entry.character_id = CharacterID(); + safereturn_entry.instance_zone_id = zone->GetZoneID(); + safereturn_entry.instance_id = zone->GetInstanceID(); + safereturn_entry.safe_zone_id = safereturn.zone_id; + safereturn_entry.safe_x = safereturn.x; + safereturn_entry.safe_y = safereturn.y; + safereturn_entry.safe_z = safereturn.z; + safereturn_entry.safe_heading = safereturn.heading; + + CharacterInstanceSafereturnsRepository::InsertOneOrUpdate(database, safereturn_entry); + } + else + { + CharacterInstanceSafereturnsRepository::DeleteWhere(database, + fmt::format("character_id = {}", character_id)); + } + /** * DevTools Load Settings */ @@ -1828,7 +1890,7 @@ void Client::Handle_OP_AcceptNewTask(const EQApplicationPacket *app) AcceptNewTask_Struct *ant = (AcceptNewTask_Struct*)app->pBuffer; if (ant->task_id > 0 && RuleB(TaskSystem, EnableTaskSystem) && task_state) - task_state->AcceptNewTask(this, ant->task_id, ant->task_master_id); + task_state->AcceptNewTask(this, ant->task_id, ant->task_master_id, std::time(nullptr)); } void Client::Handle_OP_AdventureInfoRequest(const EQApplicationPacket *app) @@ -9041,9 +9103,9 @@ void Client::Handle_OP_KickPlayers(const EQApplicationPacket *app) expedition->DzKickPlayers(this); } } - else if (buf->kick_task) + else if (buf->kick_task && GetTaskState() && GetTaskState()->HasActiveSharedTask()) { - // todo: shared tasks + GetTaskState()->KickPlayersSharedTask(this); } } @@ -12820,16 +12882,16 @@ void Client::Handle_OP_Shielding(const EQApplicationPacket *app) While active for the duration of 12 seconds baseline. The 'shield target' will take 50 pct less damage and the 'shielder' will be hit with the damage taken by the 'shield target' after all applicable mitigiont is calculated, the damage on the 'shielder' will be reduced by 25 percent, this reduction can be increased to 50 pct if equiping a shield. - You receive a 1% increase in mitigation for every 2 AC on the shield. + You receive a 1% increase in mitigation for every 2 AC on the shield. Shielder must stay with in a close distance (15 units) to your 'shield target'. If either move out of range, shield ends, no message given. Both duration and shield range can be modified by AA. Recast is 3 minutes. For custom use cases, Mob::ShieldAbility can be used in quests with all parameters being altered. This functional is also used for SPA 201 SE_PetShield, which functions in a simalar manner with pet shielding owner. - + Note: If either the shielder or the shield target die all variables are reset on both. - + */ if (app->size != sizeof(Shielding_Struct)) { @@ -12838,7 +12900,7 @@ void Client::Handle_OP_Shielding(const EQApplicationPacket *app) } if (GetLevel() < 30) { //Client gives message - return; + return; } if (GetClass() != WARRIOR){ @@ -12854,7 +12916,7 @@ void Client::Handle_OP_Shielding(const EQApplicationPacket *app) } Shielding_Struct* shield = (Shielding_Struct*)app->pBuffer; - + if (ShieldAbility(shield->target_id, 15, 12000, 50, 25, true, false)) { p_timers.Start(timer, SHIELD_ABILITY_RECAST_TIME); } @@ -15196,20 +15258,266 @@ void Client::Handle_OP_ResetAA(const EQApplicationPacket *app) return; } -void Client::Handle_OP_MovementHistoryList(const EQApplicationPacket* app) { +void Client::Handle_OP_MovementHistoryList(const EQApplicationPacket *app) +{ cheat_manager.ProcessMovementHistory(app); } -void Client::Handle_OP_UnderWorld(const EQApplicationPacket* app) { - UnderWorld* m_UnderWorld = (UnderWorld*)app->pBuffer; - if (app->size != sizeof(UnderWorld)) - { +void Client::Handle_OP_UnderWorld(const EQApplicationPacket *app) +{ + UnderWorld *m_UnderWorld = (UnderWorld *) app->pBuffer; + if (app->size != sizeof(UnderWorld)) { LogDebug("Size mismatch in OP_UnderWorld, expected {}, got [{}]", sizeof(UnderWorld), app->size); DumpPacket(app); return; } - auto dist = Distance(glm::vec3(m_UnderWorld->x, m_UnderWorld->y, zone->newzone_data.underworld), glm::vec3(m_UnderWorld->x, m_UnderWorld->y, m_UnderWorld->z)); + auto dist = Distance( + glm::vec3(m_UnderWorld->x, m_UnderWorld->y, zone->newzone_data.underworld), + glm::vec3(m_UnderWorld->x, m_UnderWorld->y, m_UnderWorld->z)); cheat_manager.MovementCheck(glm::vec3(m_UnderWorld->x, m_UnderWorld->y, m_UnderWorld->z)); - if (m_UnderWorld->spawn_id == GetID() && dist <= 5.0f && zone->newzone_data.underworld_teleport_index != 0) + if (m_UnderWorld->spawn_id == GetID() && dist <= 5.0f && zone->newzone_data.underworld_teleport_index != 0) { cheat_manager.SetExemptStatus(Port, true); + } +} + +void Client::Handle_OP_SharedTaskRemovePlayer(const EQApplicationPacket *app) +{ + if (app->size != sizeof(SharedTaskRemovePlayer_Struct)) { + LogPacketClientServer( + "Wrong size on Handle_OP_SharedTaskRemovePlayer | got [{}] expected [{}]", + app->size, + sizeof(SharedTaskRemovePlayer_Struct) + ); + return; + } + auto *r = (SharedTaskRemovePlayer_Struct *) app->pBuffer; + + LogTasks( + "[Handle_OP_SharedTaskRemovePlayer] field1 [{}] field2 [{}] player_name [{}]", + r->field1, + r->field2, + r->player_name + ); + + // live no-ops this command if not in a shared task + if (GetTaskState()->HasActiveSharedTask()) { + // struct + auto p = new ServerPacket( + ServerOP_SharedTaskRemovePlayer, + sizeof(ServerSharedTaskRemovePlayer_Struct) + ); + + auto *rp = (ServerSharedTaskRemovePlayer_Struct *) p->pBuffer; + + // fill + rp->source_character_id = CharacterID(); + rp->task_id = GetTaskState()->GetActiveSharedTask().task_id; + strn0cpy(rp->player_name, r->player_name, sizeof(r->player_name)); + + LogTasks( + "[Handle_OP_SharedTaskRemovePlayer] source_character_id [{}] task_id [{}] player_name [{}]", + rp->source_character_id, + rp->task_id, + rp->player_name + ); + + // send + worldserver.SendPacket(p); + safe_delete(p); + } +} + +void Client::Handle_OP_SharedTaskAddPlayer(const EQApplicationPacket *app) +{ + if (app->size != sizeof(SharedTaskAddPlayer_Struct)) { + LogPacketClientServer( + "Wrong size on Handle_OP_SharedTaskAddPlayer | got [{}] expected [{}]", + app->size, + sizeof(SharedTaskAddPlayer_Struct) + ); + return; + } + auto *r = (SharedTaskAddPlayer_Struct *) app->pBuffer; + + LogTasks( + "[SharedTaskAddPlayer_Struct] field1 [{}] field2 [{}] player_name [{}]", + r->field1, + r->field2, + r->player_name + ); + + if (!GetTaskState()->HasActiveSharedTask()) { + // this message is generated client-side in newer clients + Message(Chat::System, SharedTaskMessage::GetEQStr(SharedTaskMessage::COULD_NOT_USE_COMMAND)); + } + else { + // struct + auto p = new ServerPacket( + ServerOP_SharedTaskAddPlayer, + sizeof(ServerSharedTaskAddPlayer_Struct) + ); + + auto *rp = (ServerSharedTaskAddPlayer_Struct *) p->pBuffer; + + // fill + rp->source_character_id = CharacterID(); + rp->task_id = GetTaskState()->GetActiveSharedTask().task_id; + strn0cpy(rp->player_name, r->player_name, sizeof(r->player_name)); + + LogTasks( + "[Handle_OP_SharedTaskRemovePlayer] source_character_id [{}] task_id [{}] player_name [{}]", + rp->source_character_id, + rp->task_id, + rp->player_name + ); + + // send + worldserver.SendPacket(p); + safe_delete(p); + } +} + +void Client::Handle_OP_SharedTaskMakeLeader(const EQApplicationPacket *app) +{ + if (app->size != sizeof(SharedTaskMakeLeader_Struct)) { + LogPacketClientServer( + "Wrong size on Handle_OP_SharedTaskMakeLeader | got [{}] expected [{}]", + app->size, + sizeof(SharedTaskMakeLeader_Struct) + ); + return; + } + + auto *r = (SharedTaskMakeLeader_Struct *) app->pBuffer; + LogTasks( + "[SharedTaskMakeLeader_Struct] field1 [{}] field2 [{}] player_name [{}]", + r->field1, + r->field2, + r->player_name + ); + + // live no-ops this command if not in a shared task + if (GetTaskState()->HasActiveSharedTask()) { + // struct + auto p = new ServerPacket( + ServerOP_SharedTaskMakeLeader, + sizeof(ServerSharedTaskMakeLeader_Struct) + ); + + auto *rp = (ServerSharedTaskMakeLeader_Struct *) p->pBuffer; + + // fill + rp->source_character_id = CharacterID(); + rp->task_id = GetTaskState()->GetActiveSharedTask().task_id; + strn0cpy(rp->player_name, r->player_name, sizeof(r->player_name)); + + LogTasks( + "[Handle_OP_SharedTaskRemovePlayer] source_character_id [{}] task_id [{}] player_name [{}]", + rp->source_character_id, + rp->task_id, + rp->player_name + ); + + // send + worldserver.SendPacket(p); + safe_delete(p); + } +} + +void Client::Handle_OP_SharedTaskInviteResponse(const EQApplicationPacket *app) +{ + if (app->size != sizeof(SharedTaskInviteResponse_Struct)) { + LogPacketClientServer( + "Wrong size on SharedTaskInviteResponse | got [{}] expected [{}]", + app->size, + sizeof(SharedTaskInviteResponse_Struct) + ); + return; + } + + auto *r = (SharedTaskInviteResponse_Struct *) app->pBuffer; + LogTasks( + "[SharedTaskInviteResponse] unknown00 [{}] invite_id [{}] accepted [{}]", + r->unknown00, + r->invite_id, + r->accepted + ); + + // struct + auto p = new ServerPacket( + ServerOP_SharedTaskInviteAcceptedPlayer, + sizeof(ServerSharedTaskInviteAccepted_Struct) + ); + + auto *c = (ServerSharedTaskInviteAccepted_Struct *) p->pBuffer; + + // fill + c->source_character_id = CharacterID(); + c->shared_task_id = r->invite_id; + c->accepted = r->accepted; + strn0cpy(c->player_name, GetName(), sizeof(c->player_name)); + + LogTasks( + "[ServerOP_SharedTaskInviteAcceptedPlayer] source_character_id [{}] shared_task_id [{}]", + c->source_character_id, + c->shared_task_id + ); + + // send + worldserver.SendPacket(p); + safe_delete(p); +} + +void Client::Handle_OP_SharedTaskAccept(const EQApplicationPacket* app) +{ + auto buf = reinterpret_cast(app->pBuffer); + + LogTasksDetail( + "[OP_SharedTaskAccept] unknown00 [{}] unknown04 [{}] npc_entity_id [{}] task_id [{}]", + buf->unknown00, + buf->unknown04, + buf->npc_entity_id, + buf->task_id + ); + + if (buf->task_id > 0 && RuleB(TaskSystem, EnableTaskSystem) && task_state) { + task_state->AcceptNewTask(this, buf->task_id, buf->npc_entity_id, std::time(nullptr)); + } +} + +void Client::Handle_OP_SharedTaskQuit(const EQApplicationPacket* app) +{ + if (GetTaskState()->HasActiveSharedTask()) + { + CancelTask(TASKSLOTSHAREDTASK, TaskType::Shared); + } +} + +void Client::Handle_OP_TaskTimers(const EQApplicationPacket* app) +{ + GetTaskState()->ListTaskTimers(this); +} + +void Client::Handle_OP_SharedTaskPlayerList(const EQApplicationPacket* app) +{ + if (GetTaskState()->HasActiveSharedTask()) + { + uint32_t size = sizeof(ServerSharedTaskPlayerList_Struct); + auto pack = std::make_unique(ServerOP_SharedTaskPlayerList, size); + auto buf = reinterpret_cast(pack->pBuffer); + buf->source_character_id = CharacterID(); + buf->task_id = GetTaskState()->GetActiveSharedTask().task_id; + + worldserver.SendPacket(pack.get()); + } +} + +int64 Client::GetSharedTaskId() const +{ + return m_shared_task_id; +} + +void Client::SetSharedTaskId(int64 shared_task_id) +{ + Client::m_shared_task_id = shared_task_id; } diff --git a/zone/client_packet.h b/zone/client_packet.h index e04d43483..ce85e38c5 100644 --- a/zone/client_packet.h +++ b/zone/client_packet.h @@ -282,6 +282,7 @@ void Handle_OP_TargetCommand(const EQApplicationPacket *app); void Handle_OP_TargetMouse(const EQApplicationPacket *app); void Handle_OP_TaskHistoryRequest(const EQApplicationPacket *app); + void Handle_OP_TaskTimers(const EQApplicationPacket *app); void Handle_OP_Taunt(const EQApplicationPacket *app); void Handle_OP_TestBuff(const EQApplicationPacket *app); void Handle_OP_TGB(const EQApplicationPacket *app); @@ -315,3 +316,12 @@ void Handle_OP_ResetAA(const EQApplicationPacket *app); void Handle_OP_MovementHistoryList(const EQApplicationPacket* app); void Handle_OP_UnderWorld(const EQApplicationPacket* app); + + // shared tasks + void Handle_OP_SharedTaskRemovePlayer(const EQApplicationPacket *app); + void Handle_OP_SharedTaskAddPlayer(const EQApplicationPacket *app); + void Handle_OP_SharedTaskMakeLeader(const EQApplicationPacket *app); + void Handle_OP_SharedTaskInviteResponse(const EQApplicationPacket *app); + void Handle_OP_SharedTaskAccept(const EQApplicationPacket *app); + void Handle_OP_SharedTaskQuit(const EQApplicationPacket *app); + void Handle_OP_SharedTaskPlayerList(const EQApplicationPacket *app); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 428c734ab..0fd418131 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -181,11 +181,7 @@ bool Client::Process() { myraid->MemberZoned(this); } - Expedition* expedition = GetExpedition(); - if (expedition) - { - expedition->SetMemberStatus(this, DynamicZoneMemberStatus::Offline); - } + SetDynamicZoneMemberStatus(DynamicZoneMemberStatus::Offline); return false; //delete client } @@ -562,11 +558,7 @@ bool Client::Process() { AI_Start(CLIENT_LD_TIMEOUT); SendAppearancePacket(AT_Linkdead, 1); - Expedition* expedition = GetExpedition(); - if (expedition) - { - expedition->SetMemberStatus(this, DynamicZoneMemberStatus::LinkDead); - } + SetDynamicZoneMemberStatus(DynamicZoneMemberStatus::LinkDead); } } @@ -698,10 +690,9 @@ void Client::OnDisconnect(bool hard_disconnect) { } } - Expedition* expedition = GetExpedition(); - if (expedition && !bZoning) + if (!bZoning) { - expedition->SetMemberStatus(this, DynamicZoneMemberStatus::Offline); + SetDynamicZoneMemberStatus(DynamicZoneMemberStatus::Offline); } RemoveAllAuras(); diff --git a/zone/command.cpp b/zone/command.cpp index 665b2098b..4c9bcc0bb 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -61,6 +61,7 @@ #include "data_bucket.h" #include "command.h" +#include "dynamic_zone.h" #include "expedition.h" #include "guild_mgr.h" #include "map.h" @@ -76,6 +77,7 @@ #include "npc_scale_manager.h" #include "../common/content/world_content_service.h" #include "../common/http/httplib.h" +#include "../common/shared_tasks.h" extern QueryServ* QServ; extern WorldServer worldserver; @@ -7143,7 +7145,19 @@ void command_dz(Client* c, const Seperator* sep) return; } - if (strcasecmp(sep->arg[1], "expedition") == 0) + if (strcasecmp(sep->arg[1], "cache") == 0) + { + if (strcasecmp(sep->arg[2], "reload") == 0) + { + DynamicZone::CacheAllFromDatabase(); + Expedition::CacheAllFromDatabase(); + c->Message(Chat::White, fmt::format( + "Reloaded [{}] dynamic zone(s) and [{}] expedition(s) from database", + zone->dynamic_zone_cache.size(), zone->expedition_cache.size() + ).c_str()); + } + } + else if (strcasecmp(sep->arg[1], "expedition") == 0) { if (strcasecmp(sep->arg[2], "list") == 0) { @@ -7161,25 +7175,32 @@ void command_dz(Client* c, const Seperator* sep) c->Message(Chat::White, fmt::format("Total Active Expeditions: [{}]", expeditions.size()).c_str()); for (const auto& expedition : expeditions) { + auto dz = expedition->GetDynamicZone(); + if (!dz) + { + LogExpeditions("Expedition [{}] has an invalid dz [{}] in cache", expedition->GetID(), expedition->GetDynamicZoneID()); + continue; + } + auto leader_saylink = EQ::SayLinkEngine::GenerateQuestSaylink(fmt::format( "#goto {}", expedition->GetLeaderName()), false, expedition->GetLeaderName()); auto zone_saylink = EQ::SayLinkEngine::GenerateQuestSaylink(fmt::format( - "#zoneinstance {}", expedition->GetDynamicZone().GetInstanceID()), false, "zone"); + "#zoneinstance {}", dz->GetInstanceID()), false, "zone"); - auto seconds = expedition->GetDynamicZone().GetSecondsRemaining(); + auto seconds = dz->GetSecondsRemaining(); c->Message(Chat::White, fmt::format( "expedition id: [{}] dz id: [{}] name: [{}] leader: [{}] {}: [{}]:[{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]", expedition->GetID(), - expedition->GetDynamicZone().GetID(), + expedition->GetDynamicZoneID(), expedition->GetName(), leader_saylink, zone_saylink, - ZoneName(expedition->GetDynamicZone().GetZoneID()), - expedition->GetDynamicZone().GetZoneID(), - expedition->GetDynamicZone().GetInstanceID(), - expedition->GetDynamicZone().GetZoneVersion(), - expedition->GetDynamicZone().GetMemberCount(), + ZoneName(dz->GetZoneID()), + dz->GetZoneID(), + dz->GetInstanceID(), + dz->GetZoneVersion(), + dz->GetMemberCount(), seconds / 3600, // hours (seconds / 60) % 60, // minutes seconds % 60 // seconds @@ -7201,7 +7222,7 @@ void command_dz(Client* c, const Seperator* sep) { c->Message(Chat::White, fmt::format("Destroying expedition [{}] ({})", expedition_id, expedition->GetName()).c_str()); - expedition->RemoveAllMembers(); + expedition->GetDynamicZone()->RemoveAllMembers(); } else { @@ -7224,9 +7245,46 @@ void command_dz(Client* c, const Seperator* sep) } } else if (strcasecmp(sep->arg[1], "list") == 0) + { + c->Message(Chat::White, fmt::format("Total Dynamic Zones (cache): [{}]", zone->dynamic_zone_cache.size()).c_str()); + + std::vector dynamic_zones; + for (const auto& dz : zone->dynamic_zone_cache) + { + dynamic_zones.emplace_back(dz.second.get()); + } + + std::sort(dynamic_zones.begin(), dynamic_zones.end(), + [](const DynamicZone* lhs, const DynamicZone* rhs) { + return lhs->GetID() < rhs->GetID(); + }); + + for (const auto& dz : dynamic_zones) + { + auto seconds = dz->GetSecondsRemaining(); + auto zone_saylink = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#zoneinstance {}", dz->GetInstanceID()), false, "zone"); + + std::string aligned_type = fmt::format("[{}]", DynamicZone::GetDynamicZoneTypeName(static_cast(dz->GetType()))); + c->Message(Chat::White, fmt::format( + "id: [{}] type: {:>10} {}: [{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]", + dz->GetID(), + aligned_type, + zone_saylink, + dz->GetZoneID(), + dz->GetInstanceID(), + dz->GetZoneVersion(), + dz->GetMemberCount(), + seconds / 3600, // hours + (seconds / 60) % 60, // minutes + seconds % 60 // seconds + ).c_str()); + } + } + else if (strcasecmp(sep->arg[1], "listdb") == 0) { auto dz_list = DynamicZonesRepository::AllDzInstancePlayerCounts(database); - c->Message(Chat::White, fmt::format("Total Dynamic Zones: [{}]", dz_list.size()).c_str()); + c->Message(Chat::White, fmt::format("Total Dynamic Zones (database): [{}]", dz_list.size()).c_str()); auto now = std::chrono::system_clock::now(); @@ -7243,7 +7301,7 @@ void command_dz(Client* c, const Seperator* sep) fmt::format("#zoneinstance {}", dz.instance), false, "zone"); c->Message(Chat::White, fmt::format( - "dz id: [{}] type: [{}] {}: [{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]", + "id: [{}] type: [{}] {}: [{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]", dz.id, DynamicZone::GetDynamicZoneTypeName(static_cast(dz.type)), zone_saylink, @@ -7295,11 +7353,13 @@ void command_dz(Client* c, const Seperator* sep) else { c->Message(Chat::White, "#dz usage:"); + c->Message(Chat::White, "#dz cache reload - reload the current zone cache from db (also reloads expedition cache dependency)"); c->Message(Chat::White, "#dz expedition list - list expeditions in current zone cache"); c->Message(Chat::White, "#dz expedition reload - reload expedition zone cache from database"); c->Message(Chat::White, "#dz expedition destroy - destroy expedition globally (must be in cache)"); c->Message(Chat::White, "#dz expedition unlock - unlock expedition"); - c->Message(Chat::White, "#dz list [all] - list dynamic zone instances from database -- 'all' includes expired"); + c->Message(Chat::White, "#dz list - list all dynamic zone instances from current zone cache"); + c->Message(Chat::White, "#dz listdb [all] - list dynamic zone instances from database -- 'all' includes expired"); c->Message(Chat::White, "#dz lockouts remove - delete all of character's expedition lockouts"); c->Message(Chat::White, "#dz lockouts remove \"\" - delete lockouts by expedition"); c->Message(Chat::White, "#dz lockouts remove \"\" \"\" - delete lockout by expedition event"); @@ -10526,6 +10586,25 @@ void command_task(Client *c, const Seperator *sep) { EQ::SayLinkEngine::GenerateQuestSaylink("#task reload sets", false, "reload sets") ).c_str() ); + c->Message( + Chat::White, + fmt::format( + "--- [{}] Purges targeted characters task timers", + EQ::SayLinkEngine::GenerateQuestSaylink("#task purgetimers", false, "purgetimers") + ).c_str() + ); + + c->Message(Chat::White, "------------------------------------------------"); + c->Message(Chat::White, "# Shared Task System Commands"); + c->Message(Chat::White, "------------------------------------------------"); + c->Message( + Chat::White, + fmt::format( + "--- [{}] Purges all active Shared Tasks in memory and database ", + EQ::SayLinkEngine::GenerateQuestSaylink("#task sharedpurge", false, "sharedpurge") + ).c_str() + ); + return; } @@ -10539,6 +10618,15 @@ void command_task(Client *c, const Seperator *sep) { return; } + if (!strcasecmp(sep->arg[1], "purgetimers")) { + c->Message(15, fmt::format("{}'s task timers have been purged", client_target->GetCleanName()).c_str()); + if (client_target != c) { + client_target->Message(15, "[GM] Your task timers have been purged by a GM"); + } + client_target->PurgeTaskTimers(); + return; + } + if (!strcasecmp(sep->arg[1], "update")) { if (sep->argnum >= 3) { int task_id = atoi(sep->arg[2]); @@ -10565,6 +10653,27 @@ void command_task(Client *c, const Seperator *sep) { return; } + if (!strcasecmp(sep->arg[1], "sharedpurge")) { + if (!strcasecmp(sep->arg[2], "confirm")) { + LogTasksDetail("Sending purge request"); + auto pack = new ServerPacket(ServerOP_SharedTaskPurgeAllCommand, 0); + worldserver.SendPacket(pack); + safe_delete(pack); + + return; + } + + c->Message( + Chat::White, + fmt::format( + "[WARNING] This will purge all active Shared Tasks [{}]?", + EQ::SayLinkEngine::GenerateQuestSaylink("#task sharedpurge confirm", false, "confirm") + ).c_str() + ); + + return; + } + if (!strcasecmp(sep->arg[1], "assign")) { int task_id = atoi(sep->arg[2]); if ((task_id > 0) && (task_id < MAXTASKS)) { diff --git a/zone/corpse.cpp b/zone/corpse.cpp index ae123e2da..498780e1f 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -1261,16 +1261,16 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) return; } - if (zone && zone->GetInstanceID() != 0) + if (!IsPlayerCorpse()) { - // expeditions may prevent looting based on client's lockouts - auto expedition = Expedition::FindCachedExpeditionByZoneInstance(zone->GetZoneID(), zone->GetInstanceID()); - if (expedition && !expedition->CanClientLootCorpse(client, GetNPCTypeID(), GetID())) + // dynamic zones may prevent looting by non-members or based on lockouts + auto dz = zone->GetDynamicZone(); + if (dz && !dz->CanClientLootCorpse(client, GetNPCTypeID(), GetID())) { - client->MessageString(Chat::Red, LOOT_NOT_ALLOWED, inst->GetItem()->Name); + // note on live this message is only sent once on the first loot attempt of an open corpse + client->MessageString(Chat::Loot, LOOT_NOT_ALLOWED, inst->GetItem()->Name); + lootitem->auto_loot = -1; // generates client eqstr 1370 "You may not loot that item from this corpse." client->QueuePacket(app); - SendEndLootErrorPacket(client); - ResetLooter(); delete inst; return; } @@ -1307,7 +1307,7 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) /* Update any tasks that have an activity to loot this item */ if (RuleB(TaskSystem, EnableTaskSystem)) - client->UpdateTasksForItem(ActivityLoot, item->ID); + client->UpdateTasksForItem(TaskActivityType::Loot, item->ID); /* Remove it from Corpse */ if (item_data) { diff --git a/zone/dynamic_zone.cpp b/zone/dynamic_zone.cpp index 177d9c04c..a7f2bb822 100644 --- a/zone/dynamic_zone.cpp +++ b/zone/dynamic_zone.cpp @@ -21,6 +21,7 @@ #include "dynamic_zone.h" #include "client.h" #include "expedition.h" +#include "string_ids.h" #include "worldserver.h" #include "../common/eqemu_logsys.h" @@ -43,6 +44,11 @@ Database& DynamicZone::GetDatabase() return database; } +bool DynamicZone::SendServerPacket(ServerPacket* packet) +{ + return worldserver.SendPacket(packet); +} + uint16_t DynamicZone::GetCurrentInstanceID() { return zone ? static_cast(zone->GetInstanceID()) : 0; @@ -53,17 +59,119 @@ uint16_t DynamicZone::GetCurrentZoneID() return zone ? static_cast(zone->GetZoneID()) : 0; } +DynamicZone* DynamicZone::CreateNew(DynamicZone& dz_request, const std::vector& members) +{ + if (!zone || dz_request.GetID() != 0) + { + return nullptr; + } + + // this creates a new dz instance and saves it to both db and cache + uint32_t dz_id = dz_request.Create(); + if (dz_id == 0) + { + LogDynamicZones("Failed to create dynamic zone for zone [{}]", dz_request.GetZoneID()); + return nullptr; + } + + auto dz = std::make_unique(dz_request); + if (!members.empty()) + { + dz->SaveMembers(members); + } + + LogDynamicZones("Created new dz [{}] for zone [{}]", dz_id, dz_request.GetZoneID()); + + // world must be notified before we request async member updates + auto pack = dz->CreateServerDzCreatePacket(zone->GetZoneID(), zone->GetInstanceID()); + worldserver.SendPacket(pack.get()); + + auto inserted = zone->dynamic_zone_cache.emplace(dz_id, std::move(dz)); + + // expeditions invoke their own updates after installing client update callbacks + if (inserted.first->second->GetType() != DynamicZoneType::Expedition) + { + inserted.first->second->DoAsyncZoneMemberUpdates(); + } + + return inserted.first->second.get(); +} + +void DynamicZone::CacheNewDynamicZone(ServerPacket* pack) +{ + auto buf = reinterpret_cast(pack->pBuffer); + + // caching new dz created in world or another zone (has member statuses set by world) + auto dz = std::make_unique(); + dz->LoadSerializedDzPacket(buf->cereal_data, buf->cereal_size); + + uint32_t dz_id = dz->GetID(); + auto inserted = zone->dynamic_zone_cache.emplace(dz_id, std::move(dz)); + + // expeditions invoke their own updates after installing client update callbacks + if (inserted.first->second->GetType() != DynamicZoneType::Expedition) + { + inserted.first->second->DoAsyncZoneMemberUpdates(); + } + + LogDynamicZones("Cached new dynamic zone [{}]", dz_id); +} + +void DynamicZone::CacheAllFromDatabase() +{ + if (!zone) + { + return; + } + + BenchTimer bench; + + auto dynamic_zones = DynamicZonesRepository::AllWithInstanceNotExpired(database); + auto dynamic_zone_members = DynamicZoneMembersRepository::GetAllWithNames(database); + + zone->dynamic_zone_cache.clear(); + zone->dynamic_zone_cache.reserve(dynamic_zones.size()); + + for (auto& entry : dynamic_zones) + { + uint32_t dz_id = entry.id; + auto dz = std::make_unique(std::move(entry)); + + for (auto& member : dynamic_zone_members) + { + if (member.dynamic_zone_id == dz_id) + { + dz->AddMemberFromRepositoryResult(std::move(member)); + } + } + + zone->dynamic_zone_cache.emplace(dz_id, std::move(dz)); + } + + LogDynamicZones("Caching [{}] dynamic zone(s) took [{}s]", zone->dynamic_zone_cache.size(), bench.elapsed()); +} + DynamicZone* DynamicZone::FindDynamicZoneByID(uint32_t dz_id) { - auto expedition = Expedition::FindCachedExpeditionByDynamicZoneID(dz_id); - if (expedition) + if (!zone) { - return &expedition->GetDynamicZone(); + return nullptr; } - // todo: other system caches + + auto dz = zone->dynamic_zone_cache.find(dz_id); + if (dz != zone->dynamic_zone_cache.end()) + { + return dz->second.get(); + } + return nullptr; } +void DynamicZone::RegisterOnClientAddRemove(std::function on_client_addremove) +{ + m_on_client_addremove = std::move(on_client_addremove); +} + void DynamicZone::StartAllClientRemovalTimers() { for (const auto& client_iter : entity_list.GetClientList()) @@ -75,38 +183,6 @@ void DynamicZone::StartAllClientRemovalTimers() } } -void DynamicZone::SendInstanceRemoveAllCharacters() -{ - // just remove all clients in bulk instead of only characters assigned to the instance - if (IsCurrentZoneDzInstance()) - { - DynamicZone::StartAllClientRemovalTimers(); - } - else if (GetInstanceID() != 0) - { - auto pack = CreateServerRemoveAllCharactersPacket(); - worldserver.SendPacket(pack.get()); - } -} - -void DynamicZone::SendInstanceAddRemoveCharacter(uint32_t character_id, bool removed) -{ - // if removing, sets removal timer on client inside the instance - if (IsCurrentZoneDzInstance()) - { - Client* client = entity_list.GetClientByCharID(character_id); - if (client) - { - client->SetDzRemovalTimer(removed); - } - } - else if (GetInstanceID() != 0) - { - auto pack = CreateServerAddRemoveCharacterPacket(character_id, removed); - worldserver.SendPacket(pack.get()); - } -} - bool DynamicZone::IsCurrentZoneDzInstance() const { return (zone && zone->GetInstanceID() != 0 && zone->GetInstanceID() == GetInstanceID()); @@ -129,8 +205,8 @@ void DynamicZone::SetUpdatedDuration(uint32_t new_duration) m_duration = std::chrono::seconds(new_duration); m_expire_time = m_start_time + m_duration; - LogDynamicZones("Updated zone [{}]:[{}] seconds remaining: [{}]", - m_zone_id, m_instance_id, GetSecondsRemaining()); + LogDynamicZones("Updated dz [{}] zone [{}]:[{}] seconds remaining: [{}]", + m_id, m_zone_id, m_instance_id, GetSecondsRemaining()); if (zone && IsCurrentZoneDzInstance()) { @@ -138,31 +214,115 @@ void DynamicZone::SetUpdatedDuration(uint32_t new_duration) } } -void DynamicZone::SendGlobalLocationChange(uint16_t server_opcode, const DynamicZoneLocation& location) -{ - auto pack = CreateServerDzLocationPacket(server_opcode, location); - worldserver.SendPacket(pack.get()); -} - void DynamicZone::HandleWorldMessage(ServerPacket* pack) { switch (pack->opcode) { - case ServerOP_DzAddRemoveCharacter: + case ServerOP_DzCreated: { - auto buf = reinterpret_cast(pack->pBuffer); - Client* client = entity_list.GetClientByCharID(buf->character_id); - if (client) + auto buf = reinterpret_cast(pack->pBuffer); + if (zone && !zone->IsZone(buf->origin_zone_id, buf->origin_instance_id)) { - client->SetDzRemovalTimer(buf->remove); // instance kick timer + DynamicZone::CacheNewDynamicZone(pack); } break; } - case ServerOP_DzRemoveAllCharacters: + case ServerOP_DzDeleted: { - auto buf = reinterpret_cast(pack->pBuffer); - if (buf->remove) + // sent by world when it deletes an expired or empty dz + // any system that held a reference to the dz should have already been notified + auto buf = reinterpret_cast(pack->pBuffer); + auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); + if (zone && dz) { + dz->SendUpdatesToZoneMembers(true, true); // members silently removed + + // manually handle expeditions to remove any references before the dz is deleted + if (dz->GetType() == DynamicZoneType::Expedition) + { + auto expedition = Expedition::FindCachedExpeditionByDynamicZoneID(dz->GetID()); + if (expedition) + { + LogExpeditionsModerate("Deleting expedition [{}] from zone cache", expedition->GetID()); + zone->expedition_cache.erase(expedition->GetID()); + } + } + + LogDynamicZonesDetail("Deleting dynamic zone [{}] from zone cache", buf->dz_id); + zone->dynamic_zone_cache.erase(buf->dz_id); + } + break; + } + case ServerOP_DzAddRemoveMember: + { + auto buf = reinterpret_cast(pack->pBuffer); + if (zone && !zone->IsZone(buf->sender_zone_id, buf->sender_instance_id)) + { + auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); + if (dz) + { + auto status = static_cast(buf->character_status); + dz->ProcessMemberAddRemove({ buf->character_id, buf->character_name, status }, buf->removed); + } + } + + if (zone && zone->IsZone(buf->dz_zone_id, buf->dz_instance_id)) + { + // cache independent redundancy to kick removed members from dz's instance + Client* client = entity_list.GetClientByCharID(buf->character_id); + if (client) + { + client->SetDzRemovalTimer(buf->removed); + } + } + break; + } + case ServerOP_DzSwapMembers: + { + auto buf = reinterpret_cast(pack->pBuffer); + if (zone && !zone->IsZone(buf->sender_zone_id, buf->sender_instance_id)) + { + auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); + if (dz) + { + auto status = static_cast(buf->add_character_status); + dz->ProcessMemberAddRemove({ buf->remove_character_id, buf->remove_character_name }, true); + dz->ProcessMemberAddRemove({ buf->add_character_id, buf->add_character_name, status }, false); + } + } + + if (zone && zone->IsZone(buf->dz_zone_id, buf->dz_instance_id)) + { + // cache independent redundancy to kick removed members from dz's instance + Client* removed_client = entity_list.GetClientByCharID(buf->remove_character_id); + if (removed_client) + { + removed_client->SetDzRemovalTimer(true); + } + + Client* added_client = entity_list.GetClientByCharID(buf->add_character_id); + if (added_client) + { + added_client->SetDzRemovalTimer(false); + } + } + break; + } + case ServerOP_DzRemoveAllMembers: + { + auto buf = reinterpret_cast(pack->pBuffer); + if (zone && !zone->IsZone(buf->sender_zone_id, buf->sender_instance_id)) + { + auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); + if (dz) + { + dz->ProcessRemoveAllMembers(); + } + } + + if (zone && zone->IsZone(buf->dz_zone_id, buf->dz_instance_id)) + { + // cache independent redundancy to kick removed members from dz's instance DynamicZone::StartAllClientRemovalTimers(); } break; @@ -203,7 +363,140 @@ void DynamicZone::HandleWorldMessage(ServerPacket* pack) } break; } + case ServerOP_DzGetMemberStatuses: + { + // reply from world for online member statuses request for async zone member updates + auto buf = reinterpret_cast(pack->pBuffer); + auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); + if (dz) + { + for (uint32_t i = 0; i < buf->count; ++i) + { + auto status = static_cast(buf->entries[i].online_status); + dz->SetInternalMemberStatus(buf->entries[i].character_id, status); + } + dz->m_has_member_statuses = true; + dz->SendUpdatesToZoneMembers(false, true); + } + break; } + case ServerOP_DzUpdateMemberStatus: + { + auto buf = reinterpret_cast(pack->pBuffer); + if (zone && !zone->IsZone(buf->sender_zone_id, buf->sender_instance_id)) + { + auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); + if (dz) + { + auto status = static_cast(buf->status); + dz->ProcessMemberStatusChange(buf->character_id, status); + } + } + break; + } + case ServerOP_DzLeaderChanged: + { + auto buf = reinterpret_cast(pack->pBuffer); + auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); + if (dz) + { + dz->ProcessLeaderChanged(buf->leader_id); + } + break; + } + case ServerOP_DzExpireWarning: + { + auto buf = reinterpret_cast(pack->pBuffer); + auto dz = DynamicZone::FindDynamicZoneByID(buf->dz_id); + if (dz) + { + dz->SendMembersExpireWarning(buf->minutes_remaining); + } + break; + } + } +} + +std::unique_ptr DynamicZone::CreateExpireWarningPacket(uint32_t minutes_remaining) +{ + uint32_t outsize = sizeof(ExpeditionExpireWarning); + auto outapp = std::make_unique(OP_DzExpeditionEndsWarning, outsize); + auto buf = reinterpret_cast(outapp->pBuffer); + buf->minutes_remaining = minutes_remaining; + return outapp; +} + +std::unique_ptr DynamicZone::CreateInfoPacket(bool clear) +{ + constexpr uint32_t outsize = sizeof(DynamicZoneInfo_Struct); + auto outapp = std::make_unique(OP_DzExpeditionInfo, outsize); + if (!clear) + { + auto info = reinterpret_cast(outapp->pBuffer); + info->assigned = true; + strn0cpy(info->dz_name, m_name.c_str(), sizeof(info->dz_name)); + strn0cpy(info->leader_name, m_leader.name.c_str(), sizeof(info->leader_name)); + info->max_players = m_max_players; + } + return outapp; +} + +std::unique_ptr DynamicZone::CreateMemberListPacket(bool clear) +{ + uint32_t member_count = clear ? 0 : static_cast(m_members.size()); + uint32_t member_entries_size = sizeof(DynamicZoneMemberEntry_Struct) * member_count; + uint32_t outsize = sizeof(DynamicZoneMemberList_Struct) + member_entries_size; + auto outapp = std::make_unique(OP_DzMemberList, outsize); + auto buf = reinterpret_cast(outapp->pBuffer); + + buf->member_count = member_count; + + if (!clear) + { + for (auto i = 0; i < m_members.size(); ++i) + { + strn0cpy(buf->members[i].name, m_members[i].name.c_str(), sizeof(buf->members[i].name)); + buf->members[i].online_status = static_cast(m_members[i].status); + } + } + + return outapp; +} + +std::unique_ptr DynamicZone::CreateMemberListNamePacket( + const std::string& name, bool remove_name) +{ + constexpr uint32_t outsize = sizeof(DynamicZoneMemberListName_Struct); + auto outapp = std::make_unique(OP_DzMemberListName, outsize); + auto buf = reinterpret_cast(outapp->pBuffer); + buf->add_name = !remove_name; + strn0cpy(buf->name, name.c_str(), sizeof(buf->name)); + return outapp; +} + +std::unique_ptr DynamicZone::CreateMemberListStatusPacket( + const std::string& name, DynamicZoneMemberStatus status) +{ + // member list status uses member list struct with a single entry + constexpr uint32_t outsize = sizeof(DynamicZoneMemberList_Struct) + sizeof(DynamicZoneMemberEntry_Struct); + auto outapp = std::make_unique(OP_DzMemberListStatus, outsize); + auto buf = reinterpret_cast(outapp->pBuffer); + buf->member_count = 1; + + auto entry = static_cast(buf->members); + strn0cpy(entry->name, name.c_str(), sizeof(entry->name)); + entry->online_status = static_cast(status); + + return outapp; +} + +std::unique_ptr DynamicZone::CreateLeaderNamePacket() +{ + constexpr uint32_t outsize = sizeof(DynamicZoneLeaderName_Struct); + auto outapp = std::make_unique(OP_DzSetLeaderName, outsize); + auto buf = reinterpret_cast(outapp->pBuffer); + strn0cpy(buf->leader_name, m_leader.name.c_str(), sizeof(buf->leader_name)); + return outapp; } void DynamicZone::ProcessCompassChange(const DynamicZoneLocation& location) @@ -223,3 +516,264 @@ void DynamicZone::SendCompassUpdateToZoneMembers() } } } + +void DynamicZone::SendLeaderNameToZoneMembers() +{ + auto outapp_leader = CreateLeaderNamePacket(); + + for (const auto& member : m_members) + { + Client* member_client = entity_list.GetClientByCharID(member.id); + if (member_client) + { + member_client->QueuePacket(outapp_leader.get()); + + if (member.id == m_leader.id && RuleB(Expedition, AlwaysNotifyNewLeaderOnChange)) + { + member_client->MessageString(Chat::Yellow, DZMAKELEADER_YOU); + } + } + } +} + +void DynamicZone::SendMembersExpireWarning(uint32_t minutes_remaining) +{ + // expeditions warn members in all zones not just the dz + auto outapp = CreateExpireWarningPacket(minutes_remaining); + for (const auto& member : GetMembers()) + { + Client* member_client = entity_list.GetClientByCharID(member.id); + if (member_client) + { + member_client->QueuePacket(outapp.get()); + + // live doesn't actually send the chat message with it + member_client->MessageString(Chat::Yellow, EXPEDITION_MIN_REMAIN, + fmt::format_int(minutes_remaining).c_str()); + } + } +} + +void DynamicZone::SendMemberListToZoneMembers() +{ + auto outapp_members = CreateMemberListPacket(false); + + for (const auto& member : m_members) + { + Client* member_client = entity_list.GetClientByCharID(member.id); + if (member_client) + { + member_client->QueuePacket(outapp_members.get()); + } + } +} + +void DynamicZone::SendMemberListNameToZoneMembers(const std::string& char_name, bool remove) +{ + auto outapp_member_name = CreateMemberListNamePacket(char_name, remove); + + for (const auto& member : m_members) + { + Client* member_client = entity_list.GetClientByCharID(member.id); + if (member_client) + { + member_client->QueuePacket(outapp_member_name.get()); + } + } +} + +void DynamicZone::SendMemberListStatusToZoneMembers(const DynamicZoneMember& update_member) +{ + auto outapp_member_status = CreateMemberListStatusPacket(update_member.name, update_member.status); + + for (const auto& member : m_members) + { + Client* member_client = entity_list.GetClientByCharID(member.id); + if (member_client) + { + member_client->QueuePacket(outapp_member_status.get()); + } + } +} + +void DynamicZone::SendClientWindowUpdate(Client* client) +{ + if (client) + { + client->QueuePacket(CreateInfoPacket().get()); + client->QueuePacket(CreateMemberListPacket().get()); + } +} + +void DynamicZone::SendUpdatesToZoneMembers(bool removing_all, bool silent) +{ + // performs a full update on all members (usually for dz creation or removing all) + if (!HasMembers()) + { + return; + } + + std::unique_ptr outapp_info = nullptr; + std::unique_ptr outapp_members = nullptr; + + // only expeditions use the dz window. on live the window is filled by non + // expeditions when first created but never kept updated. that behavior could + // be replicated in the future by flagging this as a creation update + if (m_type == DynamicZoneType::Expedition) + { + // clearing info also clears member list, no need to send both when removing + outapp_info = CreateInfoPacket(removing_all); + outapp_members = removing_all ? nullptr : CreateMemberListPacket(); + } + + for (const auto& member : GetMembers()) + { + Client* client = entity_list.GetClientByCharID(member.id); + if (client) + { + if (removing_all) { + client->RemoveDynamicZoneID(GetID()); + } else { + client->AddDynamicZoneID(GetID()); + } + + client->SendDzCompassUpdate(); + + if (outapp_info) + { + client->QueuePacket(outapp_info.get()); + } + + if (outapp_members) + { + client->QueuePacket(outapp_members.get()); + } + + // callback to the dz system so it can perform any messages or set client data + if (m_on_client_addremove) + { + m_on_client_addremove(client, removing_all, silent); + } + } + } +} + +void DynamicZone::ProcessMemberAddRemove(const DynamicZoneMember& member, bool removed) +{ + DynamicZoneBase::ProcessMemberAddRemove(member, removed); + + // the affected client always gets a full compass update. for expeditions + // client also gets window info update and all members get a member list update + Client* client = entity_list.GetClientByCharID(member.id); + if (client) + { + if (!removed) { + client->AddDynamicZoneID(GetID()); + } else { + client->RemoveDynamicZoneID(GetID()); + } + + client->SendDzCompassUpdate(); + + if (m_type == DynamicZoneType::Expedition) + { + // sending clear info also clears member list for removed members + client->QueuePacket(CreateInfoPacket(removed).get()); + } + + if (m_on_client_addremove) + { + m_on_client_addremove(client, removed, false); + } + } + + if (m_type == DynamicZoneType::Expedition) + { + // send full list when adding (MemberListName adds with "unknown" status) + if (!removed) { + SendMemberListToZoneMembers(); + } else { + SendMemberListNameToZoneMembers(member.name, true); + } + } +} + +void DynamicZone::ProcessRemoveAllMembers(bool silent) +{ + SendUpdatesToZoneMembers(true, silent); + DynamicZoneBase::ProcessRemoveAllMembers(silent); +} + +void DynamicZone::DoAsyncZoneMemberUpdates() +{ + // gets member statuses from world and performs zone member updates on reply + // if we've already received member statuses we can just update immediately + if (m_has_member_statuses) + { + SendUpdatesToZoneMembers(); + return; + } + + constexpr uint32_t pack_size = sizeof(ServerDzID_Struct); + auto pack = std::make_unique(ServerOP_DzGetMemberStatuses, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + buf->dz_id = GetID(); + buf->sender_zone_id = zone ? zone->GetZoneID() : 0; + buf->sender_instance_id = zone ? zone->GetInstanceID() : 0; + worldserver.SendPacket(pack.get()); +} + +bool DynamicZone::ProcessMemberStatusChange(uint32_t member_id, DynamicZoneMemberStatus status) +{ + bool changed = DynamicZoneBase::ProcessMemberStatusChange(member_id, status); + + if (changed && m_type == DynamicZoneType::Expedition) + { + auto member = GetMemberData(member_id); + if (member.IsValid()) + { + SendMemberListStatusToZoneMembers(member); + } + } + + return changed; +} + +void DynamicZone::ProcessLeaderChanged(uint32_t new_leader_id) +{ + auto new_leader = GetMemberData(new_leader_id); + if (!new_leader.IsValid()) + { + LogDynamicZones("Processed invalid new leader id [{}] for dz [{}]", new_leader_id, m_id); + return; + } + + LogDynamicZones("Replaced [{}] leader [{}] with [{}]", m_id, GetLeaderName(), new_leader.name); + + SetLeader(new_leader); + if (GetType() == DynamicZoneType::Expedition) + { + SendLeaderNameToZoneMembers(); + } +} + +bool DynamicZone::CanClientLootCorpse(Client* client, uint32_t npc_type_id, uint32_t entity_id) +{ + // non-members of a dz cannot loot corpses inside the dz + if (!HasMember(client->CharacterID())) + { + return false; + } + + // expeditions may prevent looting based on client's lockouts + if (GetType() == DynamicZoneType::Expedition) + { + auto expedition = Expedition::FindCachedExpeditionByZoneInstance(zone->GetZoneID(), zone->GetInstanceID()); + if (expedition && !expedition->CanClientLootCorpse(client, npc_type_id, entity_id)) + { + return false; + } + } + + return true; +} diff --git a/zone/dynamic_zone.h b/zone/dynamic_zone.h index a56947749..de23ce238 100644 --- a/zone/dynamic_zone.h +++ b/zone/dynamic_zone.h @@ -27,7 +27,9 @@ #include #include +class Client; class Database; +class EQApplicationPacket; class ServerPacket; extern const char* const CREATE_NOT_ALL_ADDED; @@ -40,26 +42,51 @@ public: DynamicZone() = default; DynamicZone(uint32_t zone_id, uint32_t version, uint32_t duration, DynamicZoneType type); + static void CacheAllFromDatabase(); + static void CacheNewDynamicZone(ServerPacket* pack); + static DynamicZone* CreateNew(DynamicZone& dz_details, const std::vector& members); static DynamicZone* FindDynamicZoneByID(uint32_t dz_id); static void HandleWorldMessage(ServerPacket* pack); void SetSecondsRemaining(uint32_t seconds_remaining) override; + void DoAsyncZoneMemberUpdates(); + bool CanClientLootCorpse(Client* client, uint32_t npc_type_id, uint32_t entity_id); bool IsCurrentZoneDzInstance() const; - void SetUpdatedDuration(uint32_t seconds); + void RegisterOnClientAddRemove(std::function on_client_addremove); + void SendClientWindowUpdate(Client* client); + void SendLeaderNameToZoneMembers(); + void SendMemberListToZoneMembers(); + void SendMemberListNameToZoneMembers(const std::string& char_name, bool remove); + void SendMemberListStatusToZoneMembers(const DynamicZoneMember& member); + void SendRemoveAllMembersToZoneMembers(bool silent) { ProcessRemoveAllMembers(silent); } + + std::unique_ptr CreateExpireWarningPacket(uint32_t minutes_remaining); + std::unique_ptr CreateInfoPacket(bool clear = false); + std::unique_ptr CreateLeaderNamePacket(); + std::unique_ptr CreateMemberListPacket(bool clear = false); + std::unique_ptr CreateMemberListNamePacket(const std::string& name, bool remove_name); + std::unique_ptr CreateMemberListStatusPacket(const std::string& name, DynamicZoneMemberStatus status); protected: uint16_t GetCurrentInstanceID() override; uint16_t GetCurrentZoneID() override; Database& GetDatabase() override; void ProcessCompassChange(const DynamicZoneLocation& location) override; - void SendInstanceAddRemoveCharacter(uint32_t character_id, bool remove) override; - void SendInstanceRemoveAllCharacters() override; - void SendGlobalLocationChange(uint16_t server_opcode, const DynamicZoneLocation& location) override; + void ProcessMemberAddRemove(const DynamicZoneMember& member, bool removed) override; + bool ProcessMemberStatusChange(uint32_t member_id, DynamicZoneMemberStatus status) override; + void ProcessRemoveAllMembers(bool silent = false) override; + bool SendServerPacket(ServerPacket* packet) override; private: static void StartAllClientRemovalTimers(); + void ProcessLeaderChanged(uint32_t new_leader_id); void SendCompassUpdateToZoneMembers(); + void SendMembersExpireWarning(uint32_t minutes); + void SendUpdatesToZoneMembers(bool removing_all = false, bool silent = true); + void SetUpdatedDuration(uint32_t seconds); + + std::function m_on_client_addremove; }; #endif diff --git a/zone/expedition.cpp b/zone/expedition.cpp index 728e036d0..8c1b50dfd 100644 --- a/zone/expedition.cpp +++ b/zone/expedition.cpp @@ -29,7 +29,6 @@ #include "../common/expedition_lockout_timer.h" #include "../common/repositories/dynamic_zone_members_repository.h" #include "../common/repositories/expedition_lockouts_repository.h" -#include "../common/util/uuid.h" extern WorldServer worldserver; extern Zone* zone; @@ -48,30 +47,44 @@ constexpr char LOCK_BEGIN[] = "The trial has begun. You cannot const int32_t Expedition::REPLAY_TIMER_ID = -1; const int32_t Expedition::EVENT_TIMER_ID = 1; -Expedition::Expedition( - uint32_t id, const std::string& uuid, DynamicZone&& dz, const std::string& expedition_name, - const DynamicZoneMember& leader -) : ExpeditionBase(id, uuid, expedition_name, leader) +Expedition::Expedition(DynamicZone* dz, uint32_t id, uint32_t dz_id) : + m_dynamic_zone(dz), + m_id(id), + m_dynamic_zone_id(dz_id) { - SetDynamicZone(std::move(dz)); + assert(m_dynamic_zone != nullptr); // dz must remain valid for lifetime of expedition } -void Expedition::SetDynamicZone(DynamicZone&& dz) +void Expedition::LoadRepositoryResult(const ExpeditionsRepository::Expeditions& entry) { - dz.SetName(GetName()); - dz.SetLeader(GetLeader()); - - m_dynamiczone = std::move(dz); + m_id = entry.id; + m_dynamic_zone_id = entry.dynamic_zone_id; + m_add_replay_on_join = entry.add_replay_on_join; + m_is_locked = entry.is_locked; } -Expedition* Expedition::TryCreate( - Client* requester, DynamicZone& dynamiczone, ExpeditionRequest& request) +void Expedition::RegisterDynamicZoneCallbacks() +{ + uint32_t expedition_id = GetID(); + + GetDynamicZone()->RegisterOnClientAddRemove( + [expedition_id](Client* client, bool removed, bool silent) { + auto expedition = Expedition::FindCachedExpeditionByID(expedition_id); + if (expedition) { + expedition->OnClientAddRemove(client, removed, silent); + } + }); +} + +Expedition* Expedition::TryCreate(Client* requester, DynamicZone& dz_request, bool disable_messages) { if (!requester || !zone) { return nullptr; } + ExpeditionRequest request{ dz_request, disable_messages }; + // request parses leader, members list, and lockouts while validating if (!request.Validate(requester)) { @@ -79,11 +92,10 @@ Expedition* Expedition::TryCreate( return nullptr; } - dynamiczone.SetMinPlayers(request.GetMinPlayers()); - dynamiczone.SetMaxPlayers(request.GetMaxPlayers()); + dz_request.SetLeader({ request.GetLeaderID(), request.GetLeaderName(), DynamicZoneMemberStatus::Online }); - auto dynamic_zone_id = dynamiczone.Create(); - if (dynamic_zone_id == 0) + auto dz = DynamicZone::CreateNew(dz_request, request.GetMembers()); + if (!dz) { // live uses this message when trying to enter an instance that isn't ready // we can use it as the client error message if instance creation fails @@ -92,44 +104,29 @@ Expedition* Expedition::TryCreate( return nullptr; } - std::string expedition_uuid = EQ::Util::UUID::Generate().ToString(); - // unique expedition ids are created from database via auto-increment column - auto expedition_id = ExpeditionDatabase::InsertExpedition( - expedition_uuid, - dynamiczone.GetID(), - request.GetExpeditionName(), - request.GetLeaderID(), - request.GetMinPlayers(), - request.GetMaxPlayers() - ); + auto expedition_id = ExpeditionDatabase::InsertExpedition(dz->GetID()); if (expedition_id) { - auto expedition = std::make_unique( - expedition_id, - expedition_uuid, - std::move(dynamiczone), - request.GetExpeditionName(), - DynamicZoneMember{ request.GetLeaderID(), request.GetLeaderName() } - ); + auto expedition = std::make_unique(dz, expedition_id, dz->GetID()); LogExpeditions( - "Created [{}] [{}] instance id: [{}] leader: [{}] minplayers: [{}] maxplayers: [{}]", + "Created [{}] [{}] dz: [{}] instance id: [{}] leader: [{}] minplayers: [{}] maxplayers: [{}]", expedition->GetID(), expedition->GetName(), - expedition->GetDynamicZone().GetInstanceID(), + dz->GetID(), + dz->GetInstanceID(), expedition->GetLeaderName(), - expedition->GetDynamicZone().GetMinPlayers(), - expedition->GetDynamicZone().GetMaxPlayers() + dz->GetMinPlayers(), + dz->GetMaxPlayers() ); - expedition->GetDynamicZone().SaveMembers(request.GetMembers()); expedition->SaveLockouts(request); + expedition->RegisterDynamicZoneCallbacks(); auto inserted = zone->expedition_cache.emplace(expedition_id, std::move(expedition)); - inserted.first->second->SendUpdatesToZoneMembers(); inserted.first->second->SendWorldExpeditionUpdate(ServerOP_ExpeditionCreate); // cache in other zones inserted.first->second->SendLeaderMessage(request.GetLeaderClient(), Chat::System, EXPEDITION_AVAILABLE, { request.GetExpeditionName() }); @@ -140,6 +137,8 @@ Expedition* Expedition::TryCreate( Chat::System, request.GetNotAllAddedMessage()); } + dz->DoAsyncZoneMemberUpdates(); + return inserted.first->second.get(); } @@ -147,48 +146,36 @@ Expedition* Expedition::TryCreate( } void Expedition::CacheExpeditions( - std::vector&& expedition_entries) + std::vector&& expedition_entries) { if (!zone) { return; } - // bulk load expedition dzs, members, and internal lockouts before caching + // bulk load expedition internal lockouts before caching std::vector expedition_ids; - std::vector dynamic_zone_ids; for (const auto& entry : expedition_entries) { expedition_ids.emplace_back(entry.id); - dynamic_zone_ids.emplace_back(entry.dynamic_zone_id); } - auto dynamic_zones = DynamicZonesRepository::GetWithInstance(database, dynamic_zone_ids); - auto dynamic_zone_members = DynamicZoneMembersRepository::GetWithNames(database, dynamic_zone_ids); + // todo: lockouts need be moved to dynamic zone auto expedition_lockouts = ExpeditionLockoutsRepository::GetWithTimestamp(database, expedition_ids); for (auto& entry : expedition_entries) { - auto expedition = std::make_unique(); - expedition->LoadRepositoryResult(std::move(entry)); - - auto dz_entry_iter = std::find_if(dynamic_zones.begin(), dynamic_zones.end(), - [&](const DynamicZonesRepository::DynamicZoneInstance& dz_entry) { - return dz_entry.id == entry.dynamic_zone_id; - }); - - if (dz_entry_iter != dynamic_zones.end()) + // the expedition's dynamic zone and members should already be in cache + auto dynamic_zone = DynamicZone::FindDynamicZoneByID(entry.dynamic_zone_id); + if (!dynamic_zone) { - expedition->SetDynamicZone(std::move(*dz_entry_iter)); + LogExpeditions("[Warning] Expedition [{}] dz [{}] not found during caching", entry.id, entry.dynamic_zone_id); + continue; } - for (auto& member : dynamic_zone_members) - { - if (member.dynamic_zone_id == entry.dynamic_zone_id) - { - expedition->GetDynamicZone().AddMemberFromRepositoryResult(std::move(member)); - } - } + auto expedition = std::make_unique(dynamic_zone); + expedition->LoadRepositoryResult(entry); + expedition->RegisterDynamicZoneCallbacks(); for (auto& lockout_entry : expedition_lockouts) { @@ -207,14 +194,8 @@ void Expedition::CacheExpeditions( } } - // stored on expedition in db but on dz in memory cache - expedition->GetDynamicZone().SetMinPlayers(entry.min_players); - expedition->GetDynamicZone().SetMaxPlayers(entry.max_players); - - expedition->SendWorldExpeditionUpdate(ServerOP_ExpeditionGetMemberStatuses); - - auto inserted = zone->expedition_cache.emplace(entry.id, std::move(expedition)); - inserted.first->second->SendUpdatesToZoneMembers(); + zone->expedition_cache.emplace(entry.id, std::move(expedition)); + dynamic_zone->DoAsyncZoneMemberUpdates(); } } @@ -224,7 +205,7 @@ void Expedition::CacheFromDatabase(uint32_t expedition_id) { BenchTimer benchmark; - auto expedition = ExpeditionsRepository::GetWithLeaderName(database, expedition_id); + auto expedition = ExpeditionsRepository::GetWhere(database, fmt::format("id = {}", expedition_id)); CacheExpeditions({ std::move(expedition) }); LogExpeditions("Caching new expedition [{}] took [{}s]", expedition_id, benchmark.elapsed()); @@ -240,7 +221,7 @@ bool Expedition::CacheAllFromDatabase() BenchTimer benchmark; - auto expeditions = ExpeditionsRepository::GetAllWithLeaderName(database); + auto expeditions = ExpeditionsRepository::All(database); zone->expedition_cache.clear(); zone->expedition_cache.reserve(expeditions.size()); @@ -263,7 +244,8 @@ Expedition* Expedition::FindCachedExpeditionByCharacterID(uint32_t character_id) { for (const auto& expedition : zone->expedition_cache) { - if (expedition.second->GetDynamicZone().HasMember(character_id)) + auto expedition_dz = expedition.second->GetDynamicZone(); + if (expedition_dz && expedition_dz->HasMember(character_id)) { return expedition.second.get(); } @@ -278,7 +260,8 @@ Expedition* Expedition::FindCachedExpeditionByCharacterName(const std::string& c { for (const auto& expedition : zone->expedition_cache) { - if (expedition.second->GetDynamicZone().HasMember(char_name)) + auto expedition_dz = expedition.second->GetDynamicZone(); + if (expedition_dz && expedition_dz->HasMember(char_name)) { return expedition.second.get(); } @@ -293,7 +276,7 @@ Expedition* Expedition::FindCachedExpeditionByDynamicZoneID(uint32_t dz_id) { for (const auto& cached_expedition : zone->expedition_cache) { - if (cached_expedition.second->GetDynamicZone().GetID() == dz_id) + if (cached_expedition.second->GetDynamicZoneID() == dz_id) { return cached_expedition.second.get(); } @@ -321,8 +304,8 @@ Expedition* Expedition::FindCachedExpeditionByZoneInstance(uint32_t zone_id, uin { for (const auto& cached_expedition : zone->expedition_cache) { - if (cached_expedition.second->GetDynamicZone().GetZoneID() == zone_id && - cached_expedition.second->GetDynamicZone().GetInstanceID() == instance_id) + auto expedition_dz = cached_expedition.second->GetDynamicZone(); + if (expedition_dz && expedition_dz->IsSameDz(zone_id, instance_id)) { return cached_expedition.second.get(); } @@ -359,7 +342,8 @@ void Expedition::AddReplayLockout(uint32_t seconds) void Expedition::AddLockout(const std::string& event_name, uint32_t seconds) { - auto lockout = ExpeditionLockoutTimer::CreateLockout(m_expedition_name, event_name, seconds, m_uuid); + auto lockout = ExpeditionLockoutTimer::CreateLockout(GetName(), + event_name, seconds, GetDynamicZone()->GetUUID()); AddLockout(lockout); } @@ -369,7 +353,7 @@ void Expedition::AddLockout(const ExpeditionLockoutTimer& lockout, bool members_ { ExpeditionDatabase::InsertLockout(m_id, lockout); } - ExpeditionDatabase::InsertMembersLockout(GetDynamicZone().GetMembers(), lockout); + ExpeditionDatabase::InsertMembersLockout(GetDynamicZone()->GetMembers(), lockout); ProcessLockoutUpdate(lockout, false, members_only); SendWorldLockoutUpdate(lockout, false, members_only); @@ -379,8 +363,8 @@ void Expedition::AddLockoutDuration(const std::string& event_name, int seconds, { // lockout timers use unsigned durations to define intent but we may need // to insert a new lockout while still supporting timer reductions - auto lockout = ExpeditionLockoutTimer::CreateLockout( - m_expedition_name, event_name, std::max(0, seconds), m_uuid); + auto lockout = ExpeditionLockoutTimer::CreateLockout(GetName(), + event_name, std::max(0, seconds), GetDynamicZone()->GetUUID()); if (!members_only) { @@ -399,7 +383,7 @@ void Expedition::AddLockoutDuration(const std::string& event_name, int seconds, // processing lockout duration applies multiplier again in client methods, // update database with modified value now but pass original on int modified_seconds = static_cast(seconds * RuleR(Expedition, LockoutDurationMultiplier)); - ExpeditionDatabase::AddLockoutDuration(GetDynamicZone().GetMembers(), lockout, modified_seconds); + ExpeditionDatabase::AddLockoutDuration(GetDynamicZone()->GetMembers(), lockout, modified_seconds); ProcessLockoutDuration(lockout, seconds, members_only); SendWorldLockoutDuration(lockout, seconds, members_only); @@ -420,117 +404,20 @@ void Expedition::UpdateLockoutDuration( seconds = static_cast(seconds * RuleR(Expedition, LockoutDurationMultiplier)); uint64_t expire_time = it->second.GetStartTime() + seconds; - AddLockout({ m_uuid, m_expedition_name, event_name, expire_time, seconds }, members_only); + AddLockout({ GetDynamicZone()->GetUUID(), GetName(), event_name, expire_time, seconds }, members_only); } } void Expedition::RemoveLockout(const std::string& event_name) { ExpeditionDatabase::DeleteLockout(m_id, event_name); - ExpeditionDatabase::DeleteMembersLockout(GetDynamicZone().GetMembers(), m_expedition_name, event_name); + ExpeditionDatabase::DeleteMembersLockout(GetDynamicZone()->GetMembers(), GetName(), event_name); - ExpeditionLockoutTimer lockout{m_uuid, m_expedition_name, event_name, 0, 0}; + ExpeditionLockoutTimer lockout{GetDynamicZone()->GetUUID(), GetName(), event_name, 0, 0}; ProcessLockoutUpdate(lockout, true); SendWorldLockoutUpdate(lockout, true); } -bool Expedition::AddMember(const std::string& add_char_name, uint32_t add_char_id) -{ - if (GetDynamicZone().HasMember(add_char_id)) - { - return false; - } - - m_dynamiczone.AddCharacter(add_char_id); - - ProcessMemberAdded(add_char_name, add_char_id); - SendWorldMemberChanged(add_char_name, add_char_id, false); - - return true; -} - -void Expedition::RemoveAllMembers(bool enable_removal_timers) -{ - m_dynamiczone.RemoveAllCharacters(enable_removal_timers); - - SendUpdatesToZoneMembers(true); - SendWorldExpeditionUpdate(ServerOP_ExpeditionMembersRemoved); - - GetDynamicZone().ClearInternalMembers(); -} - -bool Expedition::RemoveMember(const std::string& remove_char_name) -{ - auto member = GetDynamicZone().GetMemberData(remove_char_name); - if (!member.IsValid()) - { - return false; - } - - m_dynamiczone.RemoveCharacter(member.id); - - ProcessMemberRemoved(member.name, member.id); - SendWorldMemberChanged(member.name, member.id, true); - - return true; -} - -void Expedition::SwapMember(Client* add_client, const std::string& remove_char_name) -{ - if (!add_client || remove_char_name.empty()) - { - return; - } - - auto member = GetDynamicZone().GetMemberData(remove_char_name); - if (!member.IsValid()) - { - return; - } - - // make remove and add atomic to avoid racing with separate world messages - m_dynamiczone.RemoveCharacter(member.id); - m_dynamiczone.AddCharacter(add_client->CharacterID()); - - ProcessMemberRemoved(member.name, member.id); - ProcessMemberAdded(add_client->GetName(), add_client->CharacterID()); - SendWorldMemberSwapped(member.name, member.id, add_client->GetName(), add_client->CharacterID()); -} - -void Expedition::SetMemberStatus(Client* client, DynamicZoneMemberStatus status) -{ - if (client) - { - SendMemberStatusToZoneMembers(client->CharacterID(), status); - SendWorldMemberStatus(client->CharacterID(), status); - } -} - -void Expedition::SendMemberStatusToZoneMembers(uint32_t update_member_id, DynamicZoneMemberStatus status) -{ - auto member_data = GetDynamicZone().GetMemberData(update_member_id); - if (!member_data.IsValid()) - { - return; - } - - // if zone already had this member status cached avoid packet update to clients - bool changed = GetDynamicZone().SetInternalMemberStatus(update_member_id, status); - if (changed) - { - member_data = GetDynamicZone().GetMemberData(update_member_id); // rules may override status - auto outapp_member_status = CreateMemberListStatusPacket(member_data.name, member_data.status); - for (auto& member : GetDynamicZone().GetMembers()) - { - Client* member_client = entity_list.GetClientByCharID(member.id); - if (member_client) - { - member_client->QueuePacket(outapp_member_status.get()); - } - } - } -} - void Expedition::SendClientExpeditionInvite( Client* client, const std::string& inviter_name, const std::string& swap_remove_name) { @@ -547,7 +434,7 @@ void Expedition::SendClientExpeditionInvite( client->SetPendingExpeditionInvite({ m_id, inviter_name, swap_remove_name }); client->MessageString(Chat::System, EXPEDITION_ASKED_TO_JOIN, - m_leader.name.c_str(), m_expedition_name.c_str()); + GetLeaderName().c_str(), GetName().c_str()); // live (as of March 11 2020 patch) sends warnings for lockouts added // during current expedition that client would receive on entering dz @@ -556,12 +443,13 @@ void Expedition::SendClientExpeditionInvite( { // live doesn't issue a warning for the dz's replay timer const ExpeditionLockoutTimer& lockout = lockout_iter.second; - if (!lockout.IsReplayTimer() && !lockout.IsExpired() && lockout.IsFromExpedition(m_uuid) && - !client->HasExpeditionLockout(m_expedition_name, lockout.GetEventName())) + if (!lockout.IsReplayTimer() && !lockout.IsExpired() && + lockout.IsFromExpedition(GetDynamicZone()->GetUUID()) && + !client->HasExpeditionLockout(GetName(), lockout.GetEventName())) { if (!warned) { - client->Message(Chat::System, DZADD_INVITE_WARNING, m_expedition_name.c_str()); + client->Message(Chat::System, DZADD_INVITE_WARNING, GetName().c_str()); warned = true; } @@ -583,7 +471,7 @@ void Expedition::SendClientExpeditionInvite( void Expedition::SendLeaderMessage( Client* leader_client, uint16_t chat_type, uint32_t string_id, const std::initializer_list& args) { - Client::SendCrossZoneMessageString(leader_client, m_leader.name, chat_type, string_id, args); + Client::SendCrossZoneMessageString(leader_client, GetLeaderName(), chat_type, string_id, args); } bool Expedition::ProcessAddConflicts(Client* leader_client, Client* add_client, bool swapping) @@ -595,7 +483,7 @@ bool Expedition::ProcessAddConflicts(Client* leader_client, Client* add_client, bool has_conflict = false; - if (m_dynamiczone.IsCurrentZoneDzInstance()) + if (GetDynamicZone()->IsCurrentZoneDzInstance()) { SendLeaderMessage(leader_client, Chat::Red, DZADD_LEAVE_ZONE_FIRST, { add_client->GetName() }); has_conflict = true; @@ -610,13 +498,13 @@ bool Expedition::ProcessAddConflicts(Client* leader_client, Client* add_client, } // check any extra event lockouts for this expedition that the client has and expedition doesn't - auto client_lockouts = add_client->GetExpeditionLockouts(m_expedition_name); + auto client_lockouts = add_client->GetExpeditionLockouts(GetName()); for (const auto& client_lockout : client_lockouts) { if (client_lockout.IsReplayTimer()) { // client with a replay lockout is allowed only if the replay timer was from this expedition - if (client_lockout.GetExpeditionUUID() != GetUUID()) + if (client_lockout.GetExpeditionUUID() != GetDynamicZone()->GetUUID()) { has_conflict = true; @@ -652,15 +540,15 @@ bool Expedition::ProcessAddConflicts(Client* leader_client, Client* add_client, // member swapping integrity is handled by invite response if (!swapping) { - auto member_count = GetDynamicZone().GetDatabaseMemberCount(); + auto member_count = GetDynamicZone()->GetDatabaseMemberCount(); if (member_count == 0) { has_conflict = true; } - else if (member_count >= GetDynamicZone().GetMaxPlayers()) + else if (member_count >= GetDynamicZone()->GetMaxPlayers()) { SendLeaderMessage(leader_client, Chat::Red, DZADD_EXCEED_MAX, { - fmt::format_int(GetDynamicZone().GetMaxPlayers()).str() }); + fmt::format_int(GetDynamicZone()->GetMaxPlayers()).str() }); has_conflict = true; } } @@ -683,14 +571,12 @@ void Expedition::DzInviteResponse(Client* add_client, bool accepted, const std:: return; } - LogExpeditionsModerate( - "Invite response by [{}] accepted [{}] swap_name [{}]", - add_client->GetName(), accepted, swap_remove_name - ); + LogExpeditionsModerate("Invite response by [{}] accepted [{}] swap_name [{}]", + add_client->GetName(), accepted, swap_remove_name); // a null leader_client is handled by SendLeaderMessage fallbacks // note current leader receives invite reply messages (if leader changed) - Client* leader_client = entity_list.GetClientByCharID(m_leader.id); + Client* leader_client = entity_list.GetClientByCharID(GetLeaderID()); if (!accepted) { @@ -713,8 +599,8 @@ void Expedition::DzInviteResponse(Client* add_client, bool accepted, const std:: // error if swapping and character was already removed before the accept if (was_swap_invite) { - auto swap_member = GetDynamicZone().GetMemberData(swap_remove_name); - if (!swap_member.IsValid() || !GetDynamicZone().HasDatabaseMember(swap_member.id)) + auto swap_member = GetDynamicZone()->GetMemberData(swap_remove_name); + if (!swap_member.IsValid() || !GetDynamicZone()->HasDatabaseMember(swap_member.id)) { has_conflicts = true; } @@ -733,8 +619,8 @@ void Expedition::DzInviteResponse(Client* add_client, bool accepted, const std:: { auto replay_lockout = m_lockouts.find(DZ_REPLAY_TIMER_NAME); if (replay_lockout != m_lockouts.end() && - replay_lockout->second.IsFromExpedition(m_uuid) && - !add_client->HasExpeditionLockout(m_expedition_name, DZ_REPLAY_TIMER_NAME)) + replay_lockout->second.IsFromExpedition(GetDynamicZone()->GetUUID()) && + !add_client->HasExpeditionLockout(GetName(), DZ_REPLAY_TIMER_NAME)) { ExpeditionLockoutTimer replay_timer = replay_lockout->second; // copy replay_timer.Reset(); @@ -742,13 +628,20 @@ void Expedition::DzInviteResponse(Client* add_client, bool accepted, const std:: } } - if (was_swap_invite) - { - SwapMember(add_client, swap_remove_name); + auto status = DynamicZoneMemberStatus::Online; + DynamicZoneMember add_member{ add_client->CharacterID(), add_client->GetName(), status }; + + bool success = false; + if (was_swap_invite) { + success = GetDynamicZone()->SwapMember(add_member, swap_remove_name); + } else { + success = GetDynamicZone()->AddMember(add_member); } - else + + if (success) { - AddMember(add_client->GetName(), add_client->CharacterID()); + SendLeaderMessage(leader_client, Chat::Yellow, EXPEDITION_MEMBER_ADDED, + { add_client->GetName(), GetName().c_str() }); } } } @@ -760,15 +653,15 @@ bool Expedition::ConfirmLeaderCommand(Client* requester) return false; } - if (!m_leader.IsValid()) + if (!GetLeader().IsValid()) { requester->MessageString(Chat::Red, UNABLE_RETRIEVE_LEADER); // unconfirmed message return false; } - if (m_leader.id != requester->CharacterID()) + if (GetLeaderID() != requester->CharacterID()) { - requester->MessageString(Chat::System, EXPEDITION_NOT_LEADER, m_leader.name.c_str()); + requester->MessageString(Chat::System, EXPEDITION_NOT_LEADER, GetLeaderName().c_str()); return false; } @@ -831,7 +724,7 @@ void Expedition::DzAddPlayer( } else { - auto member_data = GetDynamicZone().GetMemberData(add_char_name); + auto member_data = GetDynamicZone()->GetMemberData(add_char_name); if (member_data.IsValid()) { // live prioritizes offline message before already a member message @@ -902,14 +795,16 @@ void Expedition::DzRemovePlayer(Client* requester, std::string char_name) } // live only seems to enforce min_players for requesting expeditions, no need to check here - bool removed = RemoveMember(char_name); + // note: on live members removed when inside a dz instance remain "temporary" + // members for kick timer duration and still receive lockouts across zones (unimplemented) + bool removed = GetDynamicZone()->RemoveMember(char_name); if (!removed) { requester->MessageString(Chat::Red, EXPEDITION_NOT_MEMBER, FormatName(char_name).c_str()); } else { - requester->MessageString(Chat::Yellow, EXPEDITION_REMOVED, FormatName(char_name).c_str(), m_expedition_name.c_str()); + requester->MessageString(Chat::Yellow, EXPEDITION_REMOVED, FormatName(char_name).c_str(), GetName().c_str()); } } @@ -917,7 +812,7 @@ void Expedition::DzQuit(Client* requester) { if (requester) { - RemoveMember(requester->GetName()); + GetDynamicZone()->RemoveMember(requester->GetName()); } } @@ -929,7 +824,7 @@ void Expedition::DzSwapPlayer( return; } - if (remove_char_name.empty() || !GetDynamicZone().HasMember(remove_char_name)) + if (remove_char_name.empty() || !GetDynamicZone()->HasMember(remove_char_name)) { requester->MessageString(Chat::Red, DZSWAP_CANNOT_REMOVE, FormatName(remove_char_name).c_str()); return; @@ -942,10 +837,10 @@ void Expedition::DzPlayerList(Client* requester) { if (requester) { - requester->MessageString(Chat::Yellow, EXPEDITION_LEADER, m_leader.name.c_str()); + requester->MessageString(Chat::Yellow, EXPEDITION_LEADER, GetLeaderName().c_str()); std::string member_names; - for (const auto& member : GetDynamicZone().GetMembers()) + for (const auto& member : GetDynamicZone()->GetMembers()) { fmt::format_to(std::back_inserter(member_names), "{}, ", member.name); } @@ -966,8 +861,8 @@ void Expedition::DzKickPlayers(Client* requester) return; } - RemoveAllMembers(); - requester->MessageString(Chat::Red, EXPEDITION_REMOVED, "Everyone", m_expedition_name.c_str()); + GetDynamicZone()->RemoveAllMembers(); + requester->MessageString(Chat::Red, EXPEDITION_REMOVED, "Everyone", GetName().c_str()); } void Expedition::SetLocked( @@ -975,7 +870,7 @@ void Expedition::SetLocked( { m_is_locked = lock_expedition; - if (m_is_locked && lock_msg != ExpeditionLockMessage::None && m_dynamiczone.IsCurrentZoneDzInstance()) + if (m_is_locked && lock_msg != ExpeditionLockMessage::None && GetDynamicZone()->IsCurrentZoneDzInstance()) { auto msg = (lock_msg == ExpeditionLockMessage::Close) ? LOCK_CLOSE : LOCK_BEGIN; for (const auto& client_iter : entity_list.GetClientList()) @@ -994,123 +889,20 @@ void Expedition::SetLocked( } } -void Expedition::ProcessLeaderChanged(uint32_t new_leader_id) +void Expedition::OnClientAddRemove(Client* client, bool removed, bool silent) { - auto new_leader = GetDynamicZone().GetMemberData(new_leader_id); - if (!new_leader.IsValid()) + if (client) { - LogExpeditions("Processed invalid new leader id [{}] for expedition [{}]", new_leader_id, m_id); - return; - } + client->SetExpeditionID(removed ? 0 : GetID()); - LogExpeditionsModerate("Replaced [{}] leader [{}] with [{}]", m_id, m_leader.name, new_leader.name); - - m_leader = new_leader; - m_dynamiczone.SetLeader(m_leader); - - // update each client's expedition window in this zone - auto outapp_leader = CreateLeaderNamePacket(); - for (const auto& member : GetDynamicZone().GetMembers()) - { - Client* member_client = entity_list.GetClientByCharID(member.id); - if (member_client) + if (!silent) { - member_client->QueuePacket(outapp_leader.get()); - - if (member.id == new_leader_id && RuleB(Expedition, AlwaysNotifyNewLeaderOnChange)) - { - member_client->MessageString(Chat::Yellow, DZMAKELEADER_YOU); - } + auto string_id = removed ? EXPEDITION_REMOVED : EXPEDITION_MEMBER_ADDED; + client->MessageString(Chat::Yellow, string_id, client->GetCleanName(), GetName().c_str()); } } } -void Expedition::ProcessMakeLeader(Client* old_leader_client, Client* new_leader_client, - const std::string& new_leader_name, bool is_success, bool is_online) -{ - if (old_leader_client) - { - // success flag is set by world to indicate new leader set to an online member - if (is_success) - { - old_leader_client->MessageString(Chat::Yellow, DZMAKELEADER_NAME, new_leader_name.c_str()); - } - else if (!is_online) - { - old_leader_client->MessageString(Chat::Red, DZMAKELEADER_NOT_ONLINE, new_leader_name.c_str()); - } - else - { - old_leader_client->MessageString(Chat::Red, EXPEDITION_NOT_MEMBER, new_leader_name.c_str()); - } - } - - if (is_success && new_leader_client && !RuleB(Expedition, AlwaysNotifyNewLeaderOnChange)) - { - new_leader_client->MessageString(Chat::Yellow, DZMAKELEADER_YOU); - } -} - -void Expedition::ProcessMemberAdded(const std::string& char_name, uint32_t added_char_id) -{ - GetDynamicZone().AddInternalMember({ added_char_id, char_name, DynamicZoneMemberStatus::Online }); - - // adds the member to this expedition and notifies both leader and new member - Client* leader_client = entity_list.GetClientByCharID(m_leader.id); - if (leader_client) - { - leader_client->MessageString(Chat::Yellow, EXPEDITION_MEMBER_ADDED, char_name.c_str(), m_expedition_name.c_str()); - } - - Client* member_client = entity_list.GetClientByCharID(added_char_id); - if (member_client) - { - member_client->SetExpeditionID(GetID()); - member_client->SendDzCompassUpdate(); - member_client->QueuePacket(CreateInfoPacket().get()); - member_client->MessageString(Chat::Yellow, EXPEDITION_MEMBER_ADDED, char_name.c_str(), m_expedition_name.c_str()); - } - - SendMemberListToZoneMembers(); -} - -void Expedition::ProcessMemberRemoved(const std::string& removed_char_name, uint32_t removed_char_id) -{ - if (GetDynamicZone().GetMembers().empty()) - { - return; - } - - auto outapp_member_name = CreateMemberListNamePacket(removed_char_name, true); - - for (const auto& member : GetDynamicZone().GetMembers()) - { - Client* member_client = entity_list.GetClientByCharID(member.id); - if (member_client) - { - // all members receive the removed player name packet - member_client->QueuePacket(outapp_member_name.get()); - - if (member.id == removed_char_id) - { - // live doesn't clear expedition info on clients removed while inside dz. - // it instead let's the dz kick timer do it even if character zones out - // before it triggers. for simplicity we'll always clear immediately - member_client->SetExpeditionID(0); - member_client->SendDzCompassUpdate(); - member_client->QueuePacket(CreateInfoPacket(true).get()); - member_client->MessageString(Chat::Yellow, EXPEDITION_REMOVED, - member.name.c_str(), m_expedition_name.c_str()); - } - } - } - - GetDynamicZone().RemoveInternalMember(removed_char_id); - - LogExpeditionsDetail("Processed member [{}] ({}) removal from [{}], cache member count: [{}]", - removed_char_name, removed_char_id, m_id, GetDynamicZone().GetMemberCount()); -} - void Expedition::ProcessLockoutDuration( const ExpeditionLockoutTimer& lockout, int seconds, bool members_only) { @@ -1127,17 +919,17 @@ void Expedition::ProcessLockoutDuration( } } - for (const auto& member : GetDynamicZone().GetMembers()) + for (const auto& member : GetDynamicZone()->GetMembers()) { Client* member_client = entity_list.GetClientByCharID(member.id); if (member_client) { - member_client->AddExpeditionLockoutDuration(m_expedition_name, - lockout.GetEventName(), seconds, m_uuid); + member_client->AddExpeditionLockoutDuration(GetName(), + lockout.GetEventName(), seconds, GetDynamicZone()->GetUUID()); } } - if (m_dynamiczone.IsCurrentZoneDzInstance()) + if (GetDynamicZone()->IsCurrentZoneDzInstance()) { AddLockoutDurationClients(lockout, seconds, GetID()); } @@ -1153,8 +945,8 @@ void Expedition::AddLockoutDurationClients( if (client && (exclude_id == 0 || client->GetExpeditionID() != exclude_id)) { lockout_clients.emplace_back(client->CharacterID(), client->GetName()); - client->AddExpeditionLockoutDuration(m_expedition_name, - lockout.GetEventName(), seconds, m_uuid); + client->AddExpeditionLockoutDuration(GetName(), + lockout.GetEventName(), seconds, GetDynamicZone()->GetUUID()); } } @@ -1180,7 +972,7 @@ void Expedition::ProcessLockoutUpdate( } } - for (const auto& member : GetDynamicZone().GetMembers()) + for (const auto& member : GetDynamicZone()->GetMembers()) { Client* member_client = entity_list.GetClientByCharID(member.id); if (member_client) @@ -1191,7 +983,7 @@ void Expedition::ProcessLockoutUpdate( } else { - member_client->RemoveExpeditionLockout(m_expedition_name, lockout.GetEventName()); + member_client->RemoveExpeditionLockout(GetName(), lockout.GetEventName()); } } } @@ -1199,7 +991,7 @@ void Expedition::ProcessLockoutUpdate( // if this is the expedition's dz instance, all clients inside the zone need // to receive added lockouts. this is done on live to avoid exploits where // members leave the expedition but haven't been kicked from zone yet - if (!remove && m_dynamiczone.IsCurrentZoneDzInstance()) + if (!remove && GetDynamicZone()->IsCurrentZoneDzInstance()) { AddLockoutClients(lockout, GetID()); } @@ -1225,56 +1017,6 @@ void Expedition::AddLockoutClients( } } -void Expedition::SendMemberListToZoneMembers() -{ - auto outapp_members = CreateMemberListPacket(false); - - for (const auto& member : GetDynamicZone().GetMembers()) - { - Client* member_client = entity_list.GetClientByCharID(member.id); - if (member_client) - { - member_client->QueuePacket(outapp_members.get()); - } - } -} - -void Expedition::SendUpdatesToZoneMembers(bool clear, bool message_on_clear) -{ - if (GetDynamicZone().HasMembers()) - { - auto outapp_info = CreateInfoPacket(clear); - auto outapp_members = CreateMemberListPacket(clear); - - for (const auto& member : GetDynamicZone().GetMembers()) - { - Client* member_client = entity_list.GetClientByCharID(member.id); - if (member_client) - { - member_client->SetExpeditionID(clear ? 0 : GetID()); - member_client->SendDzCompassUpdate(); - member_client->QueuePacket(outapp_info.get()); - member_client->QueuePacket(outapp_members.get()); - member_client->SendExpeditionLockoutTimers(); - if (clear && message_on_clear) - { - member_client->MessageString(Chat::Yellow, EXPEDITION_REMOVED, - member_client->GetName(), m_expedition_name.c_str()); - } - } - } - } -} - -void Expedition::SendClientExpeditionInfo(Client* client) -{ - if (client) - { - client->QueuePacket(CreateInfoPacket().get()); - client->QueuePacket(CreateMemberListPacket().get()); - } -} - void Expedition::SendWorldPendingInvite(const ExpeditionInvite& invite, const std::string& add_name) { LogExpeditions( @@ -1285,30 +1027,6 @@ void Expedition::SendWorldPendingInvite(const ExpeditionInvite& invite, const st SendWorldAddPlayerInvite(invite.inviter_name, invite.swap_remove_name, add_name, true); } -std::unique_ptr Expedition::CreateExpireWarningPacket(uint32_t minutes_remaining) -{ - uint32_t outsize = sizeof(ExpeditionExpireWarning); - auto outapp = std::make_unique(OP_DzExpeditionEndsWarning, outsize); - auto buf = reinterpret_cast(outapp->pBuffer); - buf->minutes_remaining = minutes_remaining; - return outapp; -} - -std::unique_ptr Expedition::CreateInfoPacket(bool clear) -{ - uint32_t outsize = sizeof(DynamicZoneInfo_Struct); - auto outapp = std::make_unique(OP_DzExpeditionInfo, outsize); - auto info = reinterpret_cast(outapp->pBuffer); - if (!clear) - { - info->assigned = true; - strn0cpy(info->dz_name, m_expedition_name.c_str(), sizeof(info->dz_name)); - strn0cpy(info->leader_name, m_leader.name.c_str(), sizeof(info->leader_name)); - info->max_players = GetDynamicZone().GetMaxPlayers(); - } - return outapp; -} - std::unique_ptr Expedition::CreateInvitePacket( const std::string& inviter_name, const std::string& swap_remove_name) { @@ -1316,70 +1034,11 @@ std::unique_ptr Expedition::CreateInvitePacket( auto outapp = std::make_unique(OP_DzExpeditionInvite, outsize); auto outbuf = reinterpret_cast(outapp->pBuffer); strn0cpy(outbuf->inviter_name, inviter_name.c_str(), sizeof(outbuf->inviter_name)); - strn0cpy(outbuf->expedition_name, m_expedition_name.c_str(), sizeof(outbuf->expedition_name)); + strn0cpy(outbuf->expedition_name, GetName().c_str(), sizeof(outbuf->expedition_name)); strn0cpy(outbuf->swap_name, swap_remove_name.c_str(), sizeof(outbuf->swap_name)); outbuf->swapping = !swap_remove_name.empty(); - outbuf->dz_zone_id = m_dynamiczone.GetZoneID(); - outbuf->dz_instance_id = m_dynamiczone.GetInstanceID(); - return outapp; -} - -std::unique_ptr Expedition::CreateMemberListPacket(bool clear) -{ - uint32_t member_count = clear ? 0 : static_cast(GetDynamicZone().GetMemberCount()); - uint32_t member_entries_size = sizeof(DynamicZoneMemberEntry_Struct) * member_count; - uint32_t outsize = sizeof(DynamicZoneMemberList_Struct) + member_entries_size; - auto outapp = std::make_unique(OP_DzMemberList, outsize); - auto buf = reinterpret_cast(outapp->pBuffer); - - buf->member_count = member_count; - - if (!clear) - { - const auto& members = GetDynamicZone().GetMembers(); - for (auto i = 0; i < members.size(); ++i) - { - strn0cpy(buf->members[i].name, members[i].name.c_str(), sizeof(buf->members[i].name)); - buf->members[i].online_status = static_cast(members[i].status); - } - } - - return outapp; -} - -std::unique_ptr Expedition::CreateMemberListNamePacket( - const std::string& name, bool remove_name) -{ - uint32_t outsize = sizeof(DynamicZoneMemberListName_Struct); - auto outapp = std::make_unique(OP_DzMemberListName, outsize); - auto buf = reinterpret_cast(outapp->pBuffer); - buf->add_name = !remove_name; - strn0cpy(buf->name, name.c_str(), sizeof(buf->name)); - return outapp; -} - -std::unique_ptr Expedition::CreateMemberListStatusPacket( - const std::string& name, DynamicZoneMemberStatus status) -{ - // member list status uses member list struct with a single entry - uint32_t outsize = sizeof(DynamicZoneMemberList_Struct) + sizeof(DynamicZoneMemberEntry_Struct); - auto outapp = std::make_unique(OP_DzMemberListStatus, outsize); - auto buf = reinterpret_cast(outapp->pBuffer); - buf->member_count = 1; - - auto entry = static_cast(buf->members); - strn0cpy(entry->name, name.c_str(), sizeof(entry->name)); - entry->online_status = static_cast(status); - - return outapp; -} - -std::unique_ptr Expedition::CreateLeaderNamePacket() -{ - uint32_t outsize = sizeof(DynamicZoneLeaderName_Struct); - auto outapp = std::make_unique(OP_DzSetLeaderName, outsize); - auto buf = reinterpret_cast(outapp->pBuffer); - strn0cpy(buf->leader_name, m_leader.name.c_str(), sizeof(buf->leader_name)); + outbuf->dz_zone_id = GetDynamicZone()->GetZoneID(); + outbuf->dz_instance_id = GetDynamicZone()->GetInstanceID(); return outapp; } @@ -1448,56 +1107,12 @@ void Expedition::SendWorldMakeLeaderRequest(uint32_t requester_id, const std::st uint32_t pack_size = sizeof(ServerDzCommandMakeLeader_Struct); auto pack = std::make_unique(ServerOP_ExpeditionDzMakeLeader, pack_size); auto buf = reinterpret_cast(pack->pBuffer); - buf->expedition_id = GetID(); + buf->dz_id = GetDynamicZone()->GetID(); buf->requester_id = requester_id; strn0cpy(buf->new_leader_name, new_leader_name.c_str(), sizeof(buf->new_leader_name)); worldserver.SendPacket(pack.get()); } -void Expedition::SendWorldMemberChanged(const std::string& char_name, uint32_t char_id, bool remove) -{ - // notify other zones of added or removed member - uint32_t pack_size = sizeof(ServerExpeditionMemberChange_Struct); - auto pack = std::make_unique(ServerOP_ExpeditionMemberChange, pack_size); - auto buf = reinterpret_cast(pack->pBuffer); - buf->expedition_id = GetID(); - buf->sender_zone_id = zone ? zone->GetZoneID() : 0; - buf->sender_instance_id = zone ? zone->GetInstanceID() : 0; - buf->removed = remove; - buf->char_id = char_id; - strn0cpy(buf->char_name, char_name.c_str(), sizeof(buf->char_name)); - worldserver.SendPacket(pack.get()); -} - -void Expedition::SendWorldMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status) -{ - uint32_t pack_size = sizeof(ServerExpeditionMemberStatus_Struct); - auto pack = std::make_unique(ServerOP_ExpeditionMemberStatus, pack_size); - auto buf = reinterpret_cast(pack->pBuffer); - buf->expedition_id = GetID(); - buf->sender_zone_id = zone ? zone->GetZoneID() : 0; - buf->sender_instance_id = zone ? zone->GetInstanceID() : 0; - buf->status = static_cast(status); - buf->character_id = character_id; - worldserver.SendPacket(pack.get()); -} - -void Expedition::SendWorldMemberSwapped( - const std::string& remove_char_name, uint32_t remove_char_id, const std::string& add_char_name, uint32_t add_char_id) -{ - uint32_t pack_size = sizeof(ServerExpeditionMemberSwap_Struct); - auto pack = std::make_unique(ServerOP_ExpeditionMemberSwap, pack_size); - auto buf = reinterpret_cast(pack->pBuffer); - buf->expedition_id = GetID(); - buf->sender_zone_id = zone ? zone->GetZoneID() : 0; - buf->sender_instance_id = zone ? zone->GetInstanceID() : 0; - buf->add_char_id = add_char_id; - buf->remove_char_id = remove_char_id; - strn0cpy(buf->add_char_name, add_char_name.c_str(), sizeof(buf->add_char_name)); - strn0cpy(buf->remove_char_name, remove_char_name.c_str(), sizeof(buf->remove_char_name)); - worldserver.SendPacket(pack.get()); -} - void Expedition::SendWorldSettingChanged(uint16_t server_opcode, bool setting_value) { uint32_t pack_size = sizeof(ServerExpeditionSetting_Struct); @@ -1615,44 +1230,6 @@ void Expedition::HandleWorldMessage(ServerPacket* pack) } break; } - case ServerOP_ExpeditionDeleted: - { - // sent by world when it deletes expired or empty expeditions - auto buf = reinterpret_cast(pack->pBuffer); - auto expedition = Expedition::FindCachedExpeditionByID(buf->expedition_id); - if (zone && expedition) - { - expedition->SendUpdatesToZoneMembers(true, false); // any members silently removed - - LogExpeditionsModerate("Deleting expedition [{}] from zone cache", buf->expedition_id); - zone->expedition_cache.erase(buf->expedition_id); - } - break; - } - case ServerOP_ExpeditionMembersRemoved: - { - auto buf = reinterpret_cast(pack->pBuffer); - if (zone && !zone->IsZone(buf->sender_zone_id, buf->sender_instance_id)) - { - auto expedition = Expedition::FindCachedExpeditionByID(buf->expedition_id); - if (expedition) - { - expedition->SendUpdatesToZoneMembers(true); - expedition->GetDynamicZone().ClearInternalMembers(); - } - } - break; - } - case ServerOP_ExpeditionLeaderChanged: - { - auto buf = reinterpret_cast(pack->pBuffer); - auto expedition = Expedition::FindCachedExpeditionByID(buf->expedition_id); - if (expedition) - { - expedition->ProcessLeaderChanged(buf->leader_id); - } - break; - } case ServerOP_ExpeditionLockout: case ServerOP_ExpeditionLockoutDuration: { @@ -1662,7 +1239,7 @@ void Expedition::HandleWorldMessage(ServerPacket* pack) auto expedition = Expedition::FindCachedExpeditionByID(buf->expedition_id); if (expedition) { - ExpeditionLockoutTimer lockout{ expedition->GetUUID(), expedition->GetName(), + ExpeditionLockoutTimer lockout{ expedition->GetDynamicZone()->GetUUID(), expedition->GetName(), buf->event_name, buf->expire_time, buf->duration }; if (pack->opcode == ServerOP_ExpeditionLockout) @@ -1677,54 +1254,6 @@ void Expedition::HandleWorldMessage(ServerPacket* pack) } break; } - case ServerOP_ExpeditionMemberChange: - { - auto buf = reinterpret_cast(pack->pBuffer); - if (zone && !zone->IsZone(buf->sender_zone_id, buf->sender_instance_id)) - { - auto expedition = Expedition::FindCachedExpeditionByID(buf->expedition_id); - if (expedition) - { - if (buf->removed) - { - expedition->ProcessMemberRemoved(buf->char_name, buf->char_id); - } - else - { - expedition->ProcessMemberAdded(buf->char_name, buf->char_id); - } - } - } - break; - } - case ServerOP_ExpeditionMemberSwap: - { - auto buf = reinterpret_cast(pack->pBuffer); - if (zone && !zone->IsZone(buf->sender_zone_id, buf->sender_instance_id)) - { - auto expedition = Expedition::FindCachedExpeditionByID(buf->expedition_id); - if (expedition) - { - expedition->ProcessMemberRemoved(buf->remove_char_name, buf->remove_char_id); - expedition->ProcessMemberAdded(buf->add_char_name, buf->add_char_id); - } - } - break; - } - case ServerOP_ExpeditionMemberStatus: - { - auto buf = reinterpret_cast(pack->pBuffer); - if (zone && !zone->IsZone(buf->sender_zone_id, buf->sender_instance_id)) - { - auto expedition = Expedition::FindCachedExpeditionByID(buf->expedition_id); - if (expedition) - { - auto status = static_cast(buf->status); - expedition->SendMemberStatusToZoneMembers(buf->character_id, status); - } - } - break; - } case ServerOP_ExpeditionLockState: { auto buf = reinterpret_cast(pack->pBuffer); @@ -1751,22 +1280,6 @@ void Expedition::HandleWorldMessage(ServerPacket* pack) } break; } - case ServerOP_ExpeditionGetMemberStatuses: - { - // reply from world for online member statuses request - auto buf = reinterpret_cast(pack->pBuffer); - auto expedition = Expedition::FindCachedExpeditionByID(buf->expedition_id); - if (expedition) - { - for (uint32_t i = 0; i < buf->count; ++i) - { - auto status = static_cast(buf->entries[i].online_status); - expedition->GetDynamicZone().SetInternalMemberStatus(buf->entries[i].character_id, status); - } - expedition->SendMemberListToZoneMembers(); - } - break; - } case ServerOP_ExpeditionDzAddPlayer: { auto buf = reinterpret_cast(pack->pBuffer); @@ -1793,13 +1306,24 @@ void Expedition::HandleWorldMessage(ServerPacket* pack) case ServerOP_ExpeditionDzMakeLeader: { auto buf = reinterpret_cast(pack->pBuffer); - auto expedition = Expedition::FindCachedExpeditionByID(buf->expedition_id); - if (expedition) + auto old_leader_client = entity_list.GetClientByCharID(buf->requester_id); + auto new_leader_client = entity_list.GetClientByName(buf->new_leader_name); + + if (old_leader_client) { - auto old_leader_client = entity_list.GetClientByCharID(buf->requester_id); - auto new_leader_client = entity_list.GetClientByName(buf->new_leader_name); - expedition->ProcessMakeLeader(old_leader_client, new_leader_client, - buf->new_leader_name, buf->is_success, buf->is_online); + // success flag is set by world to indicate new leader set to an online member + if (buf->is_success) { + old_leader_client->MessageString(Chat::Yellow, DZMAKELEADER_NAME, buf->new_leader_name); + } else if (!buf->is_online) { + old_leader_client->MessageString(Chat::Red, DZMAKELEADER_NOT_ONLINE, buf->new_leader_name); + } else { + old_leader_client->MessageString(Chat::Red, EXPEDITION_NOT_MEMBER, buf->new_leader_name); + } + } + + if (buf->is_success && new_leader_client && !RuleB(Expedition, AlwaysNotifyNewLeaderOnChange)) + { + new_leader_client->MessageString(Chat::Yellow, DZMAKELEADER_YOU); } break; } @@ -1826,22 +1350,12 @@ void Expedition::HandleWorldMessage(ServerPacket* pack) } break; } - case ServerOP_ExpeditionExpireWarning: - { - auto buf = reinterpret_cast(pack->pBuffer); - auto expedition = Expedition::FindCachedExpeditionByID(buf->expedition_id); - if (expedition) - { - expedition->SendMembersExpireWarning(buf->minutes_remaining); - } - break; - } } } bool Expedition::CanClientLootCorpse(Client* client, uint32_t npc_type_id, uint32_t spawn_id) { - if (client && m_dynamiczone.IsCurrentZoneDzInstance()) + if (client && GetDynamicZone()->IsCurrentZoneDzInstance()) { // entity id takes priority, falls back to checking by npc type if not set std::string event_name = GetLootEventBySpawnID(spawn_id); @@ -1853,7 +1367,7 @@ bool Expedition::CanClientLootCorpse(Client* client, uint32_t npc_type_id, uint3 if (!event_name.empty()) { auto client_lockout = client->GetExpeditionLockout(GetName(), event_name); - if (!client_lockout || client_lockout->GetExpeditionUUID() != GetUUID()) + if (!client_lockout || client_lockout->GetExpeditionUUID() != GetDynamicZone()->GetUUID()) { // client lockout not received in this expedition, prevent looting LogExpeditions( @@ -1870,7 +1384,7 @@ bool Expedition::CanClientLootCorpse(Client* client, uint32_t npc_type_id, uint3 void Expedition::SetLootEventByNPCTypeID(uint32_t npc_type_id, const std::string& event_name) { - if (npc_type_id && m_dynamiczone.IsCurrentZoneDzInstance()) + if (npc_type_id && GetDynamicZone()->IsCurrentZoneDzInstance()) { LogExpeditions("Setting loot event [{}] for npc type id [{}]", event_name, npc_type_id); m_npc_loot_events[npc_type_id] = event_name; @@ -1879,7 +1393,7 @@ void Expedition::SetLootEventByNPCTypeID(uint32_t npc_type_id, const std::string void Expedition::SetLootEventBySpawnID(uint32_t spawn_id, const std::string& event_name) { - if (spawn_id && m_dynamiczone.IsCurrentZoneDzInstance()) + if (spawn_id && GetDynamicZone()->IsCurrentZoneDzInstance()) { LogExpeditions("Setting loot event [{}] for entity id [{}]", event_name, spawn_id); m_spawn_loot_events[spawn_id] = event_name; @@ -1890,7 +1404,7 @@ std::string Expedition::GetLootEventByNPCTypeID(uint32_t npc_type_id) { std::string event_name; - if (npc_type_id && m_dynamiczone.IsCurrentZoneDzInstance()) + if (npc_type_id && GetDynamicZone()->IsCurrentZoneDzInstance()) { auto it = m_npc_loot_events.find(npc_type_id); if (it != m_npc_loot_events.end()) @@ -1906,7 +1420,7 @@ std::string Expedition::GetLootEventBySpawnID(uint32_t spawn_id) { std::string event_name; - if (spawn_id && m_dynamiczone.IsCurrentZoneDzInstance()) + if (spawn_id && GetDynamicZone()->IsCurrentZoneDzInstance()) { auto it = m_spawn_loot_events.find(spawn_id); if (it != m_spawn_loot_events.end()) @@ -1939,22 +1453,6 @@ std::vector Expedition::GetExpeditionLockoutsByCharacter return lockouts; } -void Expedition::SendMembersExpireWarning(uint32_t minutes_remaining) -{ - // expeditions warn members in all zones not just the dz - auto outapp = CreateExpireWarningPacket(minutes_remaining); - for (const auto& member : GetDynamicZone().GetMembers()) - { - Client* member_client = entity_list.GetClientByCharID(member.id); - if (member_client) - { - member_client->QueuePacket(outapp.get()); - member_client->MessageString(Chat::Yellow, EXPEDITION_MIN_REMAIN, - fmt::format_int(minutes_remaining).c_str()); - } - } -} - void Expedition::SyncCharacterLockouts( uint32_t character_id, std::vector& client_lockouts) { @@ -1967,7 +1465,8 @@ void Expedition::SyncCharacterLockouts( for (const auto& lockout_iter : m_lockouts) { const ExpeditionLockoutTimer& lockout = lockout_iter.second; - if (lockout.IsReplayTimer() || lockout.IsExpired() || lockout.GetExpeditionUUID() != m_uuid) + if (lockout.IsReplayTimer() || lockout.IsExpired() || + lockout.GetExpeditionUUID() != GetDynamicZone()->GetUUID()) { continue; } @@ -1983,7 +1482,7 @@ void Expedition::SyncCharacterLockouts( client_lockouts.emplace_back(lockout); // insert missing } else if (client_lockout_iter->GetSecondsRemaining() < lockout.GetSecondsRemaining() && - client_lockout_iter->GetExpeditionUUID() != m_uuid) + client_lockout_iter->GetExpeditionUUID() != GetDynamicZone()->GetUUID()) { // only update lockout timer not uuid so loot event apis still work modified = true; diff --git a/zone/expedition.h b/zone/expedition.h index 106035479..73b63ade1 100644 --- a/zone/expedition.h +++ b/zone/expedition.h @@ -22,7 +22,6 @@ #define EXPEDITION_H #include "dynamic_zone.h" -#include "../common/expedition_base.h" #include "../common/expedition_lockout_timer.h" #include "../common/repositories/expeditions_repository.h" #include @@ -47,14 +46,14 @@ enum class ExpeditionLockMessage : uint8_t Begin }; -class Expedition : public ExpeditionBase +class Expedition { public: - Expedition() = default; - Expedition(uint32_t id, const std::string& uuid, DynamicZone&& dz, const std::string& expedition_name, - const DynamicZoneMember& leader); + Expedition() = delete; + Expedition(DynamicZone* dz) : m_dynamic_zone(dz) { assert(m_dynamic_zone != nullptr); } + Expedition(DynamicZone* dz, uint32_t id, uint32_t dz_id); - static Expedition* TryCreate(Client* requester, DynamicZone& dynamiczone, ExpeditionRequest& request); + static Expedition* TryCreate(Client* requester, DynamicZone& dynamiczone, bool disable_messages); static void CacheFromDatabase(uint32_t expedition_id); static bool CacheAllFromDatabase(); @@ -79,14 +78,15 @@ public: const std::string& expedition_name = {}, const std::string& event_name = {}); static void AddLockoutClients(const ExpeditionLockoutTimer& lockout, uint32_t exclude_id = 0); - DynamicZone& GetDynamicZone() { return m_dynamiczone; } + uint32_t GetID() const { return m_id; } + uint32_t GetDynamicZoneID() const { return m_dynamic_zone_id; } + DynamicZone* GetDynamicZone() const { return m_dynamic_zone; } + const DynamicZoneMember& GetLeader() { return GetDynamicZone()->GetLeader(); } + uint32_t GetLeaderID() { return GetDynamicZone()->GetLeaderID(); } + const std::string& GetLeaderName() { return GetDynamicZone()->GetLeaderName(); } const std::unordered_map& GetLockouts() const { return m_lockouts; } - - bool AddMember(const std::string& add_char_name, uint32_t add_char_id); - void RemoveAllMembers(bool enable_removal_timers = true); - bool RemoveMember(const std::string& remove_char_name); - void SetMemberStatus(Client* client, DynamicZoneMemberStatus status); - void SwapMember(Client* add_client, const std::string& remove_char_name); + const std::string& GetName() { return GetDynamicZone()->GetName(); } + void RegisterDynamicZoneCallbacks(); bool IsLocked() const { return m_is_locked; } void SetLocked(bool lock_expedition, ExpeditionLockMessage lock_msg, @@ -109,7 +109,6 @@ public: void SetLootEventByNPCTypeID(uint32_t npc_type_id, const std::string& event_name); void SetLootEventBySpawnID(uint32_t spawn_id, const std::string& event_name); - void SendClientExpeditionInfo(Client* client); void SendWorldMakeLeaderRequest(uint32_t requester_id, const std::string& new_leader_name); void SendWorldPendingInvite(const ExpeditionInvite& invite, const std::string& add_name); @@ -127,29 +126,22 @@ public: static const int32_t EVENT_TIMER_ID; private: - static void CacheExpeditions(std::vector&& expeditions); + static void CacheExpeditions(std::vector&& expeditions); static void SendWorldCharacterLockout(uint32_t character_id, const ExpeditionLockoutTimer& lockout, bool remove); void AddLockout(const ExpeditionLockoutTimer& lockout, bool members_only = false); void AddLockoutDurationClients(const ExpeditionLockoutTimer& lockout, int seconds, uint32_t exclude_id = 0); bool ConfirmLeaderCommand(Client* requester); + void OnClientAddRemove(Client* client, bool removed, bool silent); + void LoadRepositoryResult(const ExpeditionsRepository::Expeditions& entry); bool ProcessAddConflicts(Client* leader_client, Client* add_client, bool swapping); - void ProcessLeaderChanged(uint32_t new_leader_id); void ProcessLockoutDuration(const ExpeditionLockoutTimer& lockout, int seconds, bool members_only = false); void ProcessLockoutUpdate(const ExpeditionLockoutTimer& lockout, bool remove, bool members_only = false); - void ProcessMakeLeader(Client* old_leader, Client* new_leader, - const std::string& new_leader_name, bool is_success, bool is_online); - void ProcessMemberAdded(const std::string& added_char_name, uint32_t added_char_id); - void ProcessMemberRemoved(const std::string& removed_char_name, uint32_t removed_char_id); void SaveLockouts(ExpeditionRequest& request); void SendClientExpeditionInvite( Client* client, const std::string& inviter_name, const std::string& swap_remove_name); void SendLeaderMessage(Client* leader_client, uint16_t chat_type, uint32_t string_id, const std::initializer_list& args = {}); - void SendMemberListToZoneMembers(); - void SendMemberStatusToZoneMembers(uint32_t update_character_id, DynamicZoneMemberStatus status); - void SendMembersExpireWarning(uint32_t minutes); - void SendUpdatesToZoneMembers(bool clear = false, bool message_on_clear = true); void SendWorldExpeditionUpdate(uint16_t server_opcode); void SendWorldAddPlayerInvite(const std::string& inviter_name, const std::string& swap_remove_name, const std::string& add_name, bool pending = false); @@ -157,24 +149,18 @@ private: const ExpeditionLockoutTimer& lockout, int seconds, bool members_only = false); void SendWorldLockoutUpdate( const ExpeditionLockoutTimer& lockout, bool remove, bool members_only = false); - void SendWorldMemberChanged(const std::string& char_name, uint32_t char_id, bool remove); - void SendWorldMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status); - void SendWorldMemberSwapped(const std::string& remove_char_name, uint32_t remove_char_id, - const std::string& add_char_name, uint32_t add_char_id); void SendWorldSettingChanged(uint16_t server_opcode, bool setting_value); void SetDynamicZone(DynamicZone&& dz); void TryAddClient(Client* add_client, const std::string& inviter_name, const std::string& swap_remove_name, Client* leader_client = nullptr); - std::unique_ptr CreateExpireWarningPacket(uint32_t minutes_remaining); - std::unique_ptr CreateInfoPacket(bool clear = false); std::unique_ptr CreateInvitePacket(const std::string& inviter_name, const std::string& swap_remove_name); - std::unique_ptr CreateMemberListPacket(bool clear = false); - std::unique_ptr CreateMemberListNamePacket(const std::string& name, bool remove_name); - std::unique_ptr CreateMemberListStatusPacket(const std::string& name, DynamicZoneMemberStatus status); - std::unique_ptr CreateLeaderNamePacket(); - DynamicZone m_dynamiczone { DynamicZoneType::Expedition }; + uint32_t m_id = 0; + uint32_t m_dynamic_zone_id = 0; + bool m_is_locked = false; + bool m_add_replay_on_join = true; + DynamicZone* m_dynamic_zone = nullptr; // should never be null, will exist for lifetime of expedition std::unordered_map m_lockouts; std::unordered_map m_npc_loot_events; // only valid inside dz zone std::unordered_map m_spawn_loot_events; // only valid inside dz zone diff --git a/zone/expedition_database.cpp b/zone/expedition_database.cpp index eb31c301c..9a3bfb837 100644 --- a/zone/expedition_database.cpp +++ b/zone/expedition_database.cpp @@ -26,25 +26,21 @@ #include "../common/string_util.h" #include -uint32_t ExpeditionDatabase::InsertExpedition( - const std::string& uuid, uint32_t dz_id, const std::string& expedition_name, - uint32_t leader_id, uint32_t min_players, uint32_t max_players) +uint32_t ExpeditionDatabase::InsertExpedition(uint32_t dz_id) { - LogExpeditionsDetail( - "Inserting new expedition [{}] leader [{}] uuid [{}]", expedition_name, leader_id, uuid - ); + LogExpeditionsDetail("Inserting new expedition dz [{}]", dz_id); std::string query = fmt::format(SQL( INSERT INTO expeditions - (uuid, dynamic_zone_id, expedition_name, leader_id, min_players, max_players) + (dynamic_zone_id) VALUES - ('{}', {}, '{}', {}, {}, {}); - ), uuid, dz_id, EscapeString(expedition_name), leader_id, min_players, max_players); + ({}); + ), dz_id); auto results = database.QueryDatabase(query); if (!results.Success()) { - LogExpeditions("Failed to obtain an expedition id for [{}]", expedition_name); + LogExpeditions("Failed to obtain an expedition id for dz [{}]", dz_id); return 0; } diff --git a/zone/expedition_database.h b/zone/expedition_database.h index e83332920..5e35fb7b3 100644 --- a/zone/expedition_database.h +++ b/zone/expedition_database.h @@ -35,9 +35,7 @@ class MySQLRequestResult; namespace ExpeditionDatabase { - uint32_t InsertExpedition( - const std::string& uuid, uint32_t instance_id, const std::string& expedition_name, - uint32_t leader_id, uint32_t min_players, uint32_t max_players); + uint32_t InsertExpedition(uint32_t dz_id); std::vector LoadCharacterLockouts(uint32_t character_id); std::vector LoadCharacterLockouts(uint32_t character_id, const std::string& expedition_name); diff --git a/zone/expedition_request.cpp b/zone/expedition_request.cpp index a1a2aa48e..98cad6050 100644 --- a/zone/expedition_request.cpp +++ b/zone/expedition_request.cpp @@ -30,12 +30,10 @@ constexpr char SystemName[] = "expedition"; -ExpeditionRequest::ExpeditionRequest(std::string expedition_name, - uint32_t min_players, uint32_t max_players, bool disable_messages -) : - m_expedition_name(std::move(expedition_name)), - m_min_players(min_players), - m_max_players(max_players), +ExpeditionRequest::ExpeditionRequest(const DynamicZone& dz, bool disable_messages) : + m_expedition_name(dz.GetName()), + m_min_players(dz.GetMinPlayers()), + m_max_players(dz.GetMaxPlayers()), m_disable_messages(disable_messages) { } diff --git a/zone/expedition_request.h b/zone/expedition_request.h index b5e69e3ef..6f1396a42 100644 --- a/zone/expedition_request.h +++ b/zone/expedition_request.h @@ -35,8 +35,7 @@ class Raid; class ExpeditionRequest { public: - ExpeditionRequest(std::string expedition_name, uint32_t min_players, - uint32_t max_players, bool disable_messages = false); + ExpeditionRequest(const DynamicZone& dz, bool disable_messages = false); bool Validate(Client* requester); diff --git a/zone/forage.cpp b/zone/forage.cpp index 75ae00197..375788420 100644 --- a/zone/forage.cpp +++ b/zone/forage.cpp @@ -367,7 +367,7 @@ void Client::GoFish() PushItemOnCursor(*inst); SendItemPacket(EQ::invslot::slotCursor, inst, ItemPacketLimbo); if (RuleB(TaskSystem, EnableTaskSystem)) - UpdateTasksForItem(ActivityFish, food_id); + UpdateTasksForItem(TaskActivityType::Fish, food_id); safe_delete(inst); inst = m_inv.GetItem(EQ::invslot::slotCursor); @@ -487,7 +487,7 @@ void Client::ForageItem(bool guarantee) { PushItemOnCursor(*inst); SendItemPacket(EQ::invslot::slotCursor, inst, ItemPacketLimbo); if(RuleB(TaskSystem, EnableTaskSystem)) - UpdateTasksForItem(ActivityForage, foragedfood); + UpdateTasksForItem(TaskActivityType::Forage, foragedfood); safe_delete(inst); inst = m_inv.GetItem(EQ::invslot::slotCursor); diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 10f0e372c..6dc02bd66 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -1864,6 +1864,7 @@ Lua_Expedition Lua_Client::CreateExpedition(luabind::object expedition_table) { // luabind will catch thrown cast_failed exceptions for invalid/missing args luabind::object instance_info = expedition_table["instance"]; + luabind::object expedition_info = expedition_table["expedition"]; luabind::object zone = instance_info["zone"]; uint32_t zone_id = 0; @@ -1880,6 +1881,9 @@ Lua_Expedition Lua_Client::CreateExpedition(luabind::object expedition_table) { uint32_t zone_duration = luabind::object_cast(instance_info["duration"]); DynamicZone dz{ zone_id, zone_version, zone_duration, DynamicZoneType::Expedition }; + dz.SetName(luabind::object_cast(expedition_info["name"])); + dz.SetMinPlayers(luabind::object_cast(expedition_info["min_players"])); + dz.SetMaxPlayers(luabind::object_cast(expedition_info["max_players"])); // the dz_info table supports optional hash entries for 'compass', 'safereturn', and 'zonein' data if (luabind::type(expedition_table["compass"]) == LUA_TTABLE) @@ -1900,21 +1904,13 @@ Lua_Expedition Lua_Client::CreateExpedition(luabind::object expedition_table) { dz.SetZoneInLocation(zonein_loc); } - luabind::object expedition_info = expedition_table["expedition"]; - - std::string expedition_name = luabind::object_cast(expedition_info["name"]); - uint32_t min_players = luabind::object_cast(expedition_info["min_players"]); - uint32_t max_players = luabind::object_cast(expedition_info["max_players"]); - bool disable_messages = false; - + bool disable_messages = false; if (luabind::type(expedition_info["disable_messages"]) == LUA_TBOOLEAN) { disable_messages = luabind::object_cast(expedition_info["disable_messages"]); } - ExpeditionRequest request{ expedition_name, min_players, max_players, disable_messages }; - - return self->CreateExpedition(dz, request); + return self->CreateExpedition(dz, disable_messages); } Lua_Expedition Lua_Client::CreateExpedition(std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players) { @@ -2053,6 +2049,55 @@ void Lua_Client::MovePCDynamicZone(std::string zone_name, int zone_version, bool return self->MovePCDynamicZone(zone_name, zone_version, msg_if_invalid); } +void Lua_Client::CreateTaskDynamicZone(int task_id, luabind::object dz_table) { + Lua_Safe_Call_Void(); + + if (luabind::type(dz_table) != LUA_TTABLE) + { + return; + } + + // luabind will catch thrown cast_failed exceptions for invalid/missing args + luabind::object instance_info = dz_table["instance"]; + luabind::object zone = instance_info["zone"]; + + uint32_t zone_id = 0; + if (luabind::type(zone) == LUA_TSTRING) + { + zone_id = ZoneID(luabind::object_cast(zone)); + } + else if (luabind::type(zone) == LUA_TNUMBER) + { + zone_id = luabind::object_cast(zone); + } + + uint32_t zone_version = luabind::object_cast(instance_info["version"]); + + // tasks override dz duration so duration is ignored here + DynamicZone dz{ zone_id, zone_version, 0, DynamicZoneType::None }; + + // the dz_info table supports optional hash entries for 'compass', 'safereturn', and 'zonein' data + if (luabind::type(dz_table["compass"]) == LUA_TTABLE) + { + auto compass_loc = GetDynamicZoneLocationFromTable(dz_table["compass"]); + dz.SetCompass(compass_loc); + } + + if (luabind::type(dz_table["safereturn"]) == LUA_TTABLE) + { + auto safereturn_loc = GetDynamicZoneLocationFromTable(dz_table["safereturn"]); + dz.SetSafeReturn(safereturn_loc); + } + + if (luabind::type(dz_table["zonein"]) == LUA_TTABLE) + { + auto zonein_loc = GetDynamicZoneLocationFromTable(dz_table["zonein"]); + dz.SetZoneInLocation(zonein_loc); + } + + self->CreateTaskDynamicZone(task_id, dz); +} + void Lua_Client::Fling(float value, float target_x, float target_y, float target_z) { Lua_Safe_Call_Void(); self->Fling(value, target_x, target_y, target_z); @@ -2515,6 +2560,7 @@ luabind::scope lua_register_client() { .def("MovePCDynamicZone", (void(Lua_Client::*)(std::string))&Lua_Client::MovePCDynamicZone) .def("MovePCDynamicZone", (void(Lua_Client::*)(std::string, int))&Lua_Client::MovePCDynamicZone) .def("MovePCDynamicZone", (void(Lua_Client::*)(std::string, int, bool))&Lua_Client::MovePCDynamicZone) + .def("CreateTaskDynamicZone", &Lua_Client::CreateTaskDynamicZone) .def("Fling", (void(Lua_Client::*)(float,float,float,float))&Lua_Client::Fling) .def("Fling", (void(Lua_Client::*)(float,float,float,float,bool))&Lua_Client::Fling) .def("Fling", (void(Lua_Client::*)(float,float,float,float,bool,bool))&Lua_Client::Fling) diff --git a/zone/lua_client.h b/zone/lua_client.h index 65219af22..3cc2a5c94 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -400,6 +400,7 @@ public: void MovePCDynamicZone(std::string zone_name); void MovePCDynamicZone(std::string zone_name, int zone_version); void MovePCDynamicZone(std::string zone_name, int zone_version, bool msg_if_invalid); + void CreateTaskDynamicZone(int task_id, luabind::object dz_table); void Fling(float value, float target_x, float target_y, float target_z); void Fling(float value, float target_x, float target_y, float target_z, bool ignore_los); void Fling(float value, float target_x, float target_y, float target_z, bool ignore_los, bool clipping); diff --git a/zone/lua_expedition.cpp b/zone/lua_expedition.cpp index b3a442a24..063a68ce9 100644 --- a/zone/lua_expedition.cpp +++ b/zone/lua_expedition.cpp @@ -59,7 +59,7 @@ void Lua_Expedition::AddReplayLockoutDuration(int seconds, bool members_only) { uint32_t Lua_Expedition::GetDynamicZoneID() { Lua_Safe_Call_Int(); - return self->GetDynamicZone().GetID(); + return self->GetDynamicZoneID(); } uint32_t Lua_Expedition::GetID() { @@ -69,7 +69,7 @@ uint32_t Lua_Expedition::GetID() { int Lua_Expedition::GetInstanceID() { Lua_Safe_Call_Int(); - return self->GetDynamicZone().GetInstanceID(); + return self->GetDynamicZone()->GetInstanceID(); } std::string Lua_Expedition::GetLeaderName() { @@ -104,7 +104,7 @@ std::string Lua_Expedition::GetLootEventBySpawnID(uint32_t spawn_id) { uint32_t Lua_Expedition::GetMemberCount() { Lua_Safe_Call_Int(); - return self->GetDynamicZone().GetMemberCount(); + return self->GetDynamicZone()->GetMemberCount(); } luabind::object Lua_Expedition::GetMembers(lua_State* L) { @@ -113,7 +113,7 @@ luabind::object Lua_Expedition::GetMembers(lua_State* L) { if (d_) { auto self = reinterpret_cast(d_); - for (const auto& member : self->GetDynamicZone().GetMembers()) + for (const auto& member : self->GetDynamicZone()->GetMembers()) { lua_table[member.name] = member.id; } @@ -128,27 +128,27 @@ std::string Lua_Expedition::GetName() { int Lua_Expedition::GetSecondsRemaining() { Lua_Safe_Call_Int(); - return self->GetDynamicZone().GetSecondsRemaining(); + return self->GetDynamicZone()->GetSecondsRemaining(); } std::string Lua_Expedition::GetUUID() { Lua_Safe_Call_String(); - return self->GetUUID(); + return self->GetDynamicZone()->GetUUID(); } int Lua_Expedition::GetZoneID() { Lua_Safe_Call_Int(); - return self->GetDynamicZone().GetZoneID(); + return self->GetDynamicZone()->GetZoneID(); } std::string Lua_Expedition::GetZoneName() { Lua_Safe_Call_String(); - return ZoneName(self->GetDynamicZone().GetZoneID()); + return ZoneName(self->GetDynamicZone()->GetZoneID()); } int Lua_Expedition::GetZoneVersion() { Lua_Safe_Call_Int(); - return self->GetDynamicZone().GetZoneVersion(); + return self->GetDynamicZone()->GetZoneVersion(); } bool Lua_Expedition::HasLockout(std::string event_name) { @@ -168,7 +168,7 @@ bool Lua_Expedition::IsLocked() { void Lua_Expedition::RemoveCompass() { Lua_Safe_Call_Void(); - self->GetDynamicZone().SetCompass(0, 0, 0, 0, true); + self->GetDynamicZone()->SetCompass(0, 0, 0, 0, true); } void Lua_Expedition::RemoveLockout(std::string event_name) { @@ -178,12 +178,12 @@ void Lua_Expedition::RemoveLockout(std::string event_name) { void Lua_Expedition::SetCompass(uint32_t zone_id, float x, float y, float z) { Lua_Safe_Call_Void(); - self->GetDynamicZone().SetCompass(zone_id, x, y, z, true); + self->GetDynamicZone()->SetCompass(zone_id, x, y, z, true); } void Lua_Expedition::SetCompass(std::string zone_name, float x, float y, float z) { Lua_Safe_Call_Void(); - self->GetDynamicZone().SetCompass(ZoneID(zone_name), x, y, z, true); + self->GetDynamicZone()->SetCompass(ZoneID(zone_name), x, y, z, true); } void Lua_Expedition::SetLocked(bool lock_expedition) { @@ -218,23 +218,23 @@ void Lua_Expedition::SetReplayLockoutOnMemberJoin(bool enable) { void Lua_Expedition::SetSafeReturn(uint32_t zone_id, float x, float y, float z, float heading) { Lua_Safe_Call_Void(); - self->GetDynamicZone().SetSafeReturn(zone_id, x, y, z, heading, true); + self->GetDynamicZone()->SetSafeReturn(zone_id, x, y, z, heading, true); } void Lua_Expedition::SetSafeReturn(std::string zone_name, float x, float y, float z, float heading) { Lua_Safe_Call_Void(); - self->GetDynamicZone().SetSafeReturn(ZoneID(zone_name), x, y, z, heading, true); + self->GetDynamicZone()->SetSafeReturn(ZoneID(zone_name), x, y, z, heading, true); } void Lua_Expedition::SetSecondsRemaining(uint32_t seconds_remaining) { Lua_Safe_Call_Void(); - self->GetDynamicZone().SetSecondsRemaining(seconds_remaining); + self->GetDynamicZone()->SetSecondsRemaining(seconds_remaining); } void Lua_Expedition::SetZoneInLocation(float x, float y, float z, float heading) { Lua_Safe_Call_Void(); - self->GetDynamicZone().SetZoneInLocation(x, y, z, heading, true); + self->GetDynamicZone()->SetZoneInLocation(x, y, z, heading, true); } void Lua_Expedition::UpdateLockoutDuration(std::string event_name, uint32_t duration) { diff --git a/zone/lua_packet.cpp b/zone/lua_packet.cpp index 0457a8310..15211b097 100644 --- a/zone/lua_packet.cpp +++ b/zone/lua_packet.cpp @@ -675,7 +675,7 @@ luabind::scope lua_register_packet_opcodes() { luabind::value("MobRename", static_cast(OP_MobRename)), luabind::value("TaskDescription", static_cast(OP_TaskDescription)), luabind::value("TaskActivity", static_cast(OP_TaskActivity)), - luabind::value("TaskMemberList", static_cast(OP_TaskMemberList)), + luabind::value("SharedTaskPlayerList", static_cast(OP_SharedTaskPlayerList)), luabind::value("AnnoyingZoneUnknown", static_cast(OP_AnnoyingZoneUnknown)), luabind::value("Some3ByteHPUpdate", static_cast(OP_Some3ByteHPUpdate)), luabind::value("FloatListThing", static_cast(OP_FloatListThing)), @@ -770,7 +770,7 @@ luabind::scope lua_register_packet_opcodes() { luabind::value("ZonePlayerToBind", static_cast(OP_ZonePlayerToBind)), luabind::value("AutoFire", static_cast(OP_AutoFire)), luabind::value("Rewind", static_cast(OP_Rewind)), - luabind::value("OpenNewTasksWindow", static_cast(OP_OpenNewTasksWindow)), + luabind::value("TaskSelectWindow", static_cast(OP_TaskSelectWindow)), luabind::value("TaskActivityComplete", static_cast(OP_TaskActivityComplete)), luabind::value("AcceptNewTask", static_cast(OP_AcceptNewTask)), luabind::value("CancelTask", static_cast(OP_CancelTask)), diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index 148230550..5a0601658 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -198,6 +198,7 @@ LuaParser::LuaParser() { PlayerArgumentDispatch[EVENT_PLAYER_PICKUP] = handle_player_pick_up; PlayerArgumentDispatch[EVENT_CAST] = handle_player_cast; PlayerArgumentDispatch[EVENT_CAST_BEGIN] = handle_player_cast; + PlayerArgumentDispatch[EVENT_CAST_ON] = handle_player_cast; PlayerArgumentDispatch[EVENT_TASK_FAIL] = handle_player_task_fail; PlayerArgumentDispatch[EVENT_ZONE] = handle_player_zone; PlayerArgumentDispatch[EVENT_DUEL_WIN] = handle_player_duel_win; diff --git a/zone/perl_expedition.cpp b/zone/perl_expedition.cpp index 3715e754a..2ac7689fa 100644 --- a/zone/perl_expedition.cpp +++ b/zone/perl_expedition.cpp @@ -142,7 +142,7 @@ XS(XS_Expedition_GetDynamicZoneID) { Expedition* THIS = nullptr; VALIDATE_THIS_IS_EXPEDITION; - XSRETURN_UV(THIS->GetDynamicZone().GetID()); + XSRETURN_UV(THIS->GetDynamicZone()->GetID()); } XS(XS_Expedition_GetID); @@ -168,7 +168,7 @@ XS(XS_Expedition_GetInstanceID) { Expedition* THIS = nullptr; VALIDATE_THIS_IS_EXPEDITION; - XSRETURN_UV(THIS->GetDynamicZone().GetInstanceID()); + XSRETURN_UV(THIS->GetDynamicZone()->GetInstanceID()); } XS(XS_Expedition_GetLeaderName); @@ -247,7 +247,7 @@ XS(XS_Expedition_GetMemberCount) { Expedition* THIS = nullptr; VALIDATE_THIS_IS_EXPEDITION; - XSRETURN_UV(THIS->GetDynamicZone().GetMemberCount()); + XSRETURN_UV(THIS->GetDynamicZone()->GetMemberCount()); } XS(XS_Expedition_GetMembers); @@ -262,7 +262,7 @@ XS(XS_Expedition_GetMembers) { HV* hash = newHV(); - for (const auto& member : THIS->GetDynamicZone().GetMembers()) + for (const auto& member : THIS->GetDynamicZone()->GetMembers()) { hv_store(hash, member.name.c_str(), static_cast(member.name.size()), newSVuv(member.id), 0); @@ -295,7 +295,7 @@ XS(XS_Expedition_GetSecondsRemaining) { Expedition* THIS = nullptr; VALIDATE_THIS_IS_EXPEDITION; - XSRETURN_UV(THIS->GetDynamicZone().GetSecondsRemaining()); + XSRETURN_UV(THIS->GetDynamicZone()->GetSecondsRemaining()); } XS(XS_Expedition_GetUUID); @@ -308,7 +308,7 @@ XS(XS_Expedition_GetUUID) { Expedition* THIS = nullptr; VALIDATE_THIS_IS_EXPEDITION; - XSRETURN_PV(THIS->GetUUID().c_str()); + XSRETURN_PV(THIS->GetDynamicZone()->GetUUID().c_str()); } XS(XS_Expedition_GetZoneID); @@ -321,7 +321,7 @@ XS(XS_Expedition_GetZoneID) { Expedition* THIS = nullptr; VALIDATE_THIS_IS_EXPEDITION; - XSRETURN_UV(THIS->GetDynamicZone().GetZoneID()); + XSRETURN_UV(THIS->GetDynamicZone()->GetZoneID()); } XS(XS_Expedition_GetZoneName); @@ -334,7 +334,7 @@ XS(XS_Expedition_GetZoneName) { Expedition* THIS = nullptr; VALIDATE_THIS_IS_EXPEDITION; - XSRETURN_PV(ZoneName(THIS->GetDynamicZone().GetZoneID())); + XSRETURN_PV(ZoneName(THIS->GetDynamicZone()->GetZoneID())); } XS(XS_Expedition_GetZoneVersion); @@ -347,7 +347,7 @@ XS(XS_Expedition_GetZoneVersion) { Expedition* THIS = nullptr; VALIDATE_THIS_IS_EXPEDITION; - XSRETURN_UV(THIS->GetDynamicZone().GetZoneVersion()); + XSRETURN_UV(THIS->GetDynamicZone()->GetZoneVersion()); } XS(XS_Expedition_HasLockout); @@ -406,7 +406,7 @@ XS(XS_Expedition_RemoveCompass) { Expedition* THIS = nullptr; VALIDATE_THIS_IS_EXPEDITION; - THIS->GetDynamicZone().SetCompass(0, 0, 0, 0, true); + THIS->GetDynamicZone()->SetCompass(0, 0, 0, 0, true); XSRETURN_EMPTY; } @@ -445,12 +445,12 @@ XS(XS_Expedition_SetCompass) { if (SvTYPE(ST(1)) == SVt_PV) { std::string zone_name(SvPV_nolen(ST(1))); - THIS->GetDynamicZone().SetCompass(ZoneID(zone_name), x, y, z, true); + THIS->GetDynamicZone()->SetCompass(ZoneID(zone_name), x, y, z, true); } else if (SvTYPE(ST(1)) == SVt_IV) { uint32_t zone_id = static_cast(SvUV(ST(1))); - THIS->GetDynamicZone().SetCompass(zone_id, x, y, z, true); + THIS->GetDynamicZone()->SetCompass(zone_id, x, y, z, true); } else { @@ -555,12 +555,12 @@ XS(XS_Expedition_SetSafeReturn) { if (SvTYPE(ST(1)) == SVt_PV) { std::string zone_name(SvPV_nolen(ST(1))); - THIS->GetDynamicZone().SetSafeReturn(ZoneID(zone_name), x, y, z, heading, true); + THIS->GetDynamicZone()->SetSafeReturn(ZoneID(zone_name), x, y, z, heading, true); } else if (SvTYPE(ST(1)) == SVt_IV) { uint32_t zone_id = static_cast(SvUV(ST(1))); - THIS->GetDynamicZone().SetSafeReturn(zone_id, x, y, z, heading, true); + THIS->GetDynamicZone()->SetSafeReturn(zone_id, x, y, z, heading, true); } else { @@ -581,7 +581,7 @@ XS(XS_Expedition_SetSecondsRemaining) { VALIDATE_THIS_IS_EXPEDITION; uint32_t seconds_remaining = static_cast(SvUV(ST(1))); - THIS->GetDynamicZone().SetSecondsRemaining(seconds_remaining); + THIS->GetDynamicZone()->SetSecondsRemaining(seconds_remaining); XSRETURN_EMPTY; } @@ -601,7 +601,7 @@ XS(XS_Expedition_SetZoneInLocation) { float z = static_cast(SvNV(ST(3))); float heading = static_cast(SvNV(ST(4))); - THIS->GetDynamicZone().SetZoneInLocation(x, y, z, heading, true); + THIS->GetDynamicZone()->SetZoneInLocation(x, y, z, heading, true); XSRETURN_EMPTY; } diff --git a/zone/shared_task_zone_messaging.cpp b/zone/shared_task_zone_messaging.cpp new file mode 100644 index 000000000..89e262847 --- /dev/null +++ b/zone/shared_task_zone_messaging.cpp @@ -0,0 +1,176 @@ +#include "shared_task_zone_messaging.h" +#include "../common/shared_tasks.h" +#include "../common/servertalk.h" +#include "client.h" +#include "../common/repositories/character_data_repository.h" +#include "../common/repositories/shared_task_members_repository.h" + +#include +#include +#include +#include +#include + +void SharedTaskZoneMessaging::HandleWorldMessage(ServerPacket *pack) +{ + switch (pack->opcode) { + case ServerOP_SharedTaskAcceptNewTask: { + auto p = reinterpret_cast(pack->pBuffer); + auto c = entity_list.GetClientByCharID(p->requested_character_id); + if (c) { + LogTasks("[ServerOP_SharedTaskAcceptNewTask] We're back in zone and I found [{}]", c->GetCleanName()); + + c->m_requesting_shared_task = true; + c->GetTaskState() + ->AcceptNewTask( + c, + (int) p->requested_task_id, + (int) p->requested_npc_type_id, + p->accept_time + ); + c->LoadClientTaskState(); + c->m_requesting_shared_task = false; + } + + break; + } + case ServerOP_SharedTaskUpdate: { + auto p = reinterpret_cast(pack->pBuffer); + auto c = entity_list.GetClientByCharID(p->source_character_id); + if (c) { + LogTasks("[ServerOP_SharedTaskUpdate] We're back in zone and I found [{}]", c->GetCleanName()); + + c->m_shared_task_update = true; + c->GetTaskState()->SharedTaskIncrementDoneCount( + c, + (int) p->task_id, + (int) p->activity_id, + (int) p->done_count, + p->ignore_quest_update + ); + c->m_shared_task_update = false; + } + + break; + } + case ServerOP_SharedTaskAttemptRemove: { + auto p = reinterpret_cast(pack->pBuffer); + auto c = entity_list.GetClientByCharID(p->requested_character_id); + if (c) { + LogTasks("[ServerOP_SharedTaskAttemptRemove] We're back in zone and I found [{}]", c->GetCleanName()); + + c->m_requested_shared_task_removal = true; + c->GetTaskState()->CancelTask( + c, + TASKSLOTSHAREDTASK, + static_cast((int) TASK_TYPE_SHARED), + p->remove_from_db + ); + c->m_requested_shared_task_removal = false; + + c->SetSharedTaskId(0); + } + + break; + } + case ServerOP_SharedTaskMemberlist: { + auto p = reinterpret_cast(pack->pBuffer); + + LogTasks( + "[ServerOP_SharedTaskMemberlist] We're back in zone and I'm searching for [{}]", + p->destination_character_id + ); + + // find character and route packet + auto c = entity_list.GetClientByCharID(p->destination_character_id); + if (c) { + LogTasks("[ServerOP_SharedTaskMemberlist] We're back in zone and I found [{}]", c->GetCleanName()); + + std::vector members; + + // deserialize members from world + EQ::Util::MemoryStreamReader ss(p->cereal_serialized_members, p->cereal_size); + cereal::BinaryInputArchive archive(ss); + archive(members); + + SerializeBuffer buf(sizeof(SharedTaskMemberList_Struct) + 15 * members.size()); + buf.WriteInt32(0); // unknown ids + buf.WriteInt32(0); + buf.WriteInt32((int32) members.size()); + + for (auto &m : members) { + buf.WriteString(m.character_name); + buf.WriteInt32(0); // monster mission + buf.WriteInt8(m.is_leader ? 1 : 0); + } + + auto outapp = std::make_unique(OP_SharedTaskMemberList, buf); + c->QueuePacket(outapp.get()); + } + + break; + } + case ServerOP_SharedTaskMemberChange: { + auto p = reinterpret_cast(pack->pBuffer); + + LogTasksDetail("[ServerOP_SharedTaskMemberChange] Searching for [{}]", p->destination_character_id); + + auto c = entity_list.GetClientByCharID(p->destination_character_id); + if (c) { + LogTasksDetail("[ServerOP_SharedTaskMemberChange] Found [{}]", c->GetCleanName()); + + SerializeBuffer buf; + buf.WriteInt32(0); // unique character id of receiver, leave 0 for emu + buf.WriteInt32(0); // unknown, seen 50, 4, 0 + buf.WriteInt8(p->removed ? 0 : 1); // 0: removed 1: added + buf.WriteString(p->player_name); + + // live sends more after the name but it might just be garbage from + // a re-used buffer (possibly a former name[64] buffer?) + + auto outapp = std::make_unique(OP_SharedTaskMemberChange, buf); + c->QueuePacket(outapp.get()); + } + + break; + } + case ServerOP_SharedTaskInvitePlayer: { + auto p = reinterpret_cast(pack->pBuffer); + auto c = entity_list.GetClientByCharID(p->requested_character_id); + if (c) { + LogTasks("[ServerOP_SharedTaskInvitePlayer] We're back in zone and I found [{}]", c->GetCleanName()); + + // init packet + auto outapp = new EQApplicationPacket(OP_SharedTaskInvite, sizeof(SharedTaskInvite_Struct)); + auto *i = (SharedTaskInvite_Struct *) outapp->pBuffer; + + // fill + i->unknown00 = 0; + i->invite_id = (int) p->invite_shared_task_id; + strn0cpy(i->inviter_name, p->inviter_name, 64); + strn0cpy(i->task_name, p->task_name, 64); + + // sends + c->QueuePacket(outapp); + safe_delete(outapp); + } + + break; + } + case ServerOP_SharedTaskPurgeAllCommand: { + LogTasksDetail("[ServerOP_SharedTaskPurgeAllCommand] Syncing clients"); + + for (auto &client: entity_list.GetClientList()) { + Client *c = client.second; + task_manager->SyncClientSharedTaskState(c, c->GetTaskState()); + c->RemoveClientTaskState(); + c->LoadClientTaskState(); + } + + break; + } + default: + break; + } + +} diff --git a/zone/shared_task_zone_messaging.h b/zone/shared_task_zone_messaging.h new file mode 100644 index 000000000..492e3ac92 --- /dev/null +++ b/zone/shared_task_zone_messaging.h @@ -0,0 +1,12 @@ +#ifndef EQEMU_SHARED_TASK_ZONE_MESSAGING_H +#define EQEMU_SHARED_TASK_ZONE_MESSAGING_H + +class ServerPacket; + +class SharedTaskZoneMessaging { +public: + static void HandleWorldMessage(ServerPacket *pack); +}; + + +#endif //EQEMU_SHARED_TASK_ZONE_MESSAGING_H diff --git a/zone/string_ids.h b/zone/string_ids.h index eca1f23cf..55cec653c 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -173,6 +173,7 @@ #define GENERIC_STRINGID_SAY 554 //%1 says '%T2' #define CANNOT_WAKE 555 //%1 tells you, 'I am unable to wake %2, master.' #define SUMMONING_CORPSE_ZONE 596 //Summoning %1's corpse(s). +#define TASK_NOT_RIGHT_LEVEL 615 //You are not at the right level for this task. #define PET_HOLD_SET_ON 698 //The pet hold mode has been set to on. #define PET_HOLD_SET_OFF 699 //The pet hold mode has been set to off. #define PET_FOCUS_SET_ON 700 //The pet focus mode has been set to on. @@ -295,6 +296,8 @@ #define TRADESKILL_MISSING_ITEM 3455 //You are missing a %1. #define TRADESKILL_MISSING_COMPONENTS 3456 //Sorry, but you don't have everything you need for this recipe in your general inventory. #define TRADESKILL_LEARN_RECIPE 3457 //You have learned the recipe %1! +#define TASK_UPDATED 3471 //Your task '%1' has been updated. +#define YOU_ASSIGNED_TASK 3472 //You have been assigned the task '%1'. #define EXPEDITION_YOU_BELONG 3500 //You cannot create this expedition since you already belong to another. #define EXPEDITION_YOU_PLAYED_HERE 3501 //You cannot create this expedition for another %1d:%2h:%3m since you have recently played here. #define REQUIRED_PLAYER_COUNT 3503 //You do not meet the player count requirement. You have %1 players. You must have at least %2 and no more than %3. @@ -369,7 +372,10 @@ #define FAILED_TAUNT 5811 //You have failed to taunt your target. #define PHYSICAL_RESIST_FAIL 5817 //Your target avoided your %1 ability. #define AA_NO_TARGET 5825 //You must first select a target for this ability! +#define YOU_RECEIVE 5941 //You receive %1. +#define NO_TASK_OFFERS 6009 //Sorry %3, I don't have anything for someone with your abilities. #define MAX_ACTIVE_TASKS 6010 //Sorry %3, you already have the maximum number of active tasks. +#define TASK_REQUEST_COOLDOWN_TIMER 6011 //Sorry, %3, but you can't request another task for %4 minutes and %5 seconds. #define FORAGE_MASTERY 6012 //Your forage mastery has enabled you to find something else! #define GUILD_BANK_CANNOT_DEPOSIT 6097 // Cannot deposit this item. Containers must be empty, and only one of each LORE and no NO TRADE or TEMPORARY items may be deposited. #define GUILD_BANK_FULL 6098 // There is no more room in the Guild Bank. @@ -418,6 +424,7 @@ #define GAIN_GROUP_LEADERSHIP_EXP 8788 // #define GAIN_RAID_LEADERSHIP_EXP 8789 // #define BUFF_MINUTES_REMAINING 8799 //%1 (%2 minutes remaining) +#define YOU_HAVE_BEEN_GIVEN 8994 //You have been given: %1 #define NO_MORE_TRAPS 9002 //You have already placed your maximum number of traps. #define FEAR_TOO_HIGH 9035 //Your target is too high of a level for your fear spell. #define SLOW_MOSTLY_SUCCESSFUL 9029 //Your spell was mostly successful. diff --git a/zone/task_client_state.cpp b/zone/task_client_state.cpp index 3ded9eb27..e00960238 100644 --- a/zone/task_client_state.cpp +++ b/zone/task_client_state.cpp @@ -1,6 +1,7 @@ #include "../common/global_define.h" #include "../common/misc_functions.h" #include "../common/repositories/character_activities_repository.h" +#include "../common/repositories/character_task_timers_repository.h" #include "../common/repositories/character_tasks_repository.h" #include "../common/repositories/completed_tasks_repository.h" #include "../common/rulesys.h" @@ -9,8 +10,13 @@ #include "quest_parser_collection.h" #include "task_client_state.h" #include "zonedb.h" +#include "../common/shared_tasks.h" +#include "worldserver.h" +#include "dynamic_zone.h" +#include "string_ids.h" -extern QueryServ *QServ; +extern WorldServer worldserver; +extern QueryServ *QServ; ClientTaskState::ClientTaskState() { @@ -23,9 +29,13 @@ ClientTaskState::ClientTaskState() m_active_quests[i].task_id = TASKSLOTEMPTY; } + m_active_task = {}; m_active_task.slot = 0; m_active_task.task_id = TASKSLOTEMPTY; - // TODO: shared task + + m_active_shared_task = {}; + m_active_shared_task.slot = 0; + m_active_shared_task.task_id = TASKSLOTEMPTY; } ClientTaskState::~ClientTaskState() @@ -92,7 +102,7 @@ void ClientTaskState::SendTaskHistory(Client *client, int task_index) for (int i = 0; i < p_task_data->activity_count; i++) { if (m_completed_tasks[adjusted_task_index].activity_done[i]) { task_history_reply_data_1 = (TaskHistoryReplyData1_Struct *) reply; - task_history_reply_data_1->ActivityType = p_task_data->activity_information[i].activity_type; + task_history_reply_data_1->ActivityType = static_cast(p_task_data->activity_information[i].activity_type); reply = (char *) task_history_reply_data_1 + sizeof(TaskHistoryReplyData1_Struct); VARSTRUCT_ENCODE_STRING(reply, p_task_data->activity_information[i].target_name.c_str()); VARSTRUCT_ENCODE_STRING(reply, p_task_data->activity_information[i].item_list.c_str()); @@ -321,7 +331,7 @@ bool ClientTaskState::HasSlotForTask(TaskInformation *task) case TaskType::Task: return m_active_task.task_id == TASKSLOTEMPTY; case TaskType::Shared: - return false; // todo + return m_active_shared_task.task_id == TASKSLOTEMPTY; case TaskType::Quest: for (auto &active_quest : m_active_quests) { if (active_quest.task_id == TASKSLOTEMPTY) { @@ -359,20 +369,39 @@ bool ClientTaskState::UnlockActivities(int character_id, ClientTaskInformation & { bool all_activities_complete = true; + LogTasksDetail( + "[UnlockActivities] Fetching task info for character_id [{}] task [{}] slot [{}] current_step [{}] accepted_time [{}] updated [{}]", + character_id, + task_info.task_id, + task_info.slot, + task_info.current_step, + task_info.accepted_time, + task_info.updated + ); + TaskInformation *p_task_data = task_manager->m_task_data[task_info.task_id]; if (p_task_data == nullptr) { return true; } + for (int i = 0; i < p_task_data->activity_count; i++) { + if (task_info.activity[i].activity_id >= 0) { + LogTasksDetail( + "[UnlockActivities] character_id [{}] task [{}] activity_id [{}] done_count [{}] activity_state [{}] updated [{}] sequence [{}]", + character_id, + task_info.task_id, + task_info.activity[i].activity_id, + task_info.activity[i].done_count, + task_info.activity[i].activity_state, + task_info.activity[i].updated, + p_task_data->sequence_mode + ); + } + } + // On loading the client state, all activities that are not completed, are // marked as hidden. For Sequential (non-stepped) mode, we mark the first // activity_information as active if not complete. - LogTasks( - "character_id [{}] task_id [{}] sequence_mode [{}]", - character_id, - task_info.task_id, - p_task_data->sequence_mode - ); if (p_task_data->sequence_mode == ActivitiesSequential) { if (task_info.activity[0].activity_state != ActivityCompleted) { @@ -418,15 +447,18 @@ bool ClientTaskState::UnlockActivities(int character_id, ClientTaskInformation & } } - CompletedTaskInformation completed_task_information{}; - completed_task_information.task_id = task_info.task_id; - completed_task_information.completed_time = time(nullptr); + if (p_task_data->type != TaskType::Shared) { + CompletedTaskInformation completed_task_information{}; + completed_task_information.task_id = task_info.task_id; + completed_task_information.completed_time = time(nullptr); - for (int i = 0; i < p_task_data->activity_count; i++) { - completed_task_information.activity_done[i] = (task_info.activity[i].activity_state == ActivityCompleted); + for (int i = 0; i < p_task_data->activity_count; i++) { + completed_task_information.activity_done[i] = (task_info.activity[i].activity_state == + ActivityCompleted); + } + + m_completed_tasks.push_back(completed_task_information); } - - m_completed_tasks.push_back(completed_task_information); } LogTasks("Returning sequential task, AllActivitiesComplete is [{}]", all_activities_complete); @@ -505,24 +537,42 @@ bool ClientTaskState::UnlockActivities(int character_id, ClientTaskInformation & } } - CompletedTaskInformation completed_task_information{}; - completed_task_information.task_id = task_info.task_id; - completed_task_information.completed_time = time(nullptr); + if (p_task_data->type != TaskType::Shared) { + CompletedTaskInformation completed_task_information{}; + completed_task_information.task_id = task_info.task_id; + completed_task_information.completed_time = time(nullptr); - for (int activity_id = 0; activity_id < p_task_data->activity_count; activity_id++) { - completed_task_information.activity_done[activity_id] = - (task_info.activity[activity_id].activity_state == ActivityCompleted); + for (int activity_id = 0; activity_id < p_task_data->activity_count; activity_id++) { + completed_task_information.activity_done[activity_id] = + (task_info.activity[activity_id].activity_state == ActivityCompleted); + } + + m_completed_tasks.push_back(completed_task_information); } - - m_completed_tasks.push_back(completed_task_information); } return true; } // Mark all non-completed tasks in the current step as active for (int activity = 0; activity < p_task_data->activity_count; activity++) { + LogTasksDetail( + "[UnlockActivities] - Debug task [{}] activity [{}] step_number [{}] current_step [{}]", + task_info.task_id, + activity, + p_task_data->activity_information[activity].step_number, + (int) task_info.current_step + + ); + if ((p_task_data->activity_information[activity].step_number == (int) task_info.current_step) && (task_info.activity[activity].activity_state == ActivityHidden)) { + + LogTasksDetail( + "[UnlockActivities] -- Debug task [{}] activity [{}] (ActivityActive)", + task_info.task_id, + activity + ); + task_info.activity[activity].activity_state = ActivityActive; task_info.activity[activity].updated = true; } @@ -533,21 +583,20 @@ bool ClientTaskState::UnlockActivities(int character_id, ClientTaskInformation & void ClientTaskState::UpdateTasksOnKill(Client *client, int npc_type_id) { - UpdateTasksByNPC(client, ActivityKill, npc_type_id); + UpdateTasksByNPC(client, TaskActivityType::Kill, npc_type_id); } bool ClientTaskState::UpdateTasksOnSpeakWith(Client *client, int npc_type_id) { - return UpdateTasksByNPC(client, ActivitySpeakWith, npc_type_id); + return UpdateTasksByNPC(client, TaskActivityType::SpeakWith, npc_type_id); } -bool ClientTaskState::UpdateTasksByNPC(Client *client, int activity_type, int npc_type_id) +bool ClientTaskState::UpdateTasksByNPC(Client *client, TaskActivityType activity_type, int npc_type_id) { int is_updating = false; - // If the client has no tasks, there is nothing further to check. - if (!task_manager || (m_active_task_count == 0 && m_active_task.task_id == TASKSLOTEMPTY)) { // could be better ... + if (!HasActiveTasks()) { return false; } @@ -583,7 +632,7 @@ bool ClientTaskState::UpdateTasksByNPC(Client *client, int activity_type, int np client->GetName(), current_task->task_id, activity_id, - activity_type, + static_cast(activity_type), npc_type_id ); continue; @@ -624,7 +673,7 @@ int ClientTaskState::ActiveSpeakTask(int npc_type_id) // This method is to be used from Perl quests only and returns the task_id of the first // active task found which has an active SpeakWith activity_information for this NPC. - if (!task_manager || (m_active_task_count == 0 && m_active_task.task_id == TASKSLOTEMPTY)) { // could be better ... + if (!HasActiveTasks()) { return 0; } @@ -648,7 +697,7 @@ int ClientTaskState::ActiveSpeakTask(int npc_type_id) if (client_activity->activity_state != ActivityActive) { continue; } - if (activity_info->activity_type != ActivitySpeakWith) { + if (activity_info->activity_type != TaskActivityType::SpeakWith) { continue; } // Is there a zone restriction on the activity_information ? @@ -670,7 +719,7 @@ int ClientTaskState::ActiveSpeakActivity(int npc_type_id, int task_id) // This method is to be used from Perl quests only and returns the activity_id of the first // active activity_information found in the specified task which is to SpeakWith this NPC. - if (!task_manager || (m_active_task_count == 0 && m_active_task.task_id == TASKSLOTEMPTY)) { // could be better ... + if (!HasActiveTasks()) { return -1; } if (task_id <= 0 || task_id >= MAXTASKS) { @@ -697,7 +746,7 @@ int ClientTaskState::ActiveSpeakActivity(int npc_type_id, int task_id) if (client_activity->activity_state != ActivityActive) { continue; } - if (activity_info->activity_type != ActivitySpeakWith) { + if (activity_info->activity_type != TaskActivityType::SpeakWith) { continue; } // Is there a zone restriction on the activity_information ? @@ -715,7 +764,7 @@ int ClientTaskState::ActiveSpeakActivity(int npc_type_id, int task_id) return 0; } -void ClientTaskState::UpdateTasksForItem(Client *client, ActivityType activity_type, int item_id, int count) +void ClientTaskState::UpdateTasksForItem(Client *client, TaskActivityType activity_type, int item_id, int count) { // This method updates the client's task activities of the specified type which relate @@ -727,11 +776,11 @@ void ClientTaskState::UpdateTasksForItem(Client *client, ActivityType activity_t LogTasks( "[UpdateTasksForItem] activity_type [{}] item_id [{}]", - activity_type, + static_cast(activity_type), item_id ); - if (!task_manager || (m_active_task_count == 0 && m_active_task.task_id == TASKSLOTEMPTY)) { // could be better ... + if (!HasActiveTasks()) { return; } @@ -758,7 +807,7 @@ void ClientTaskState::UpdateTasksForItem(Client *client, ActivityType activity_t continue; } // We are only interested in the ActivityType we were called with - if (activity_info->activity_type != (int) activity_type) { + if (activity_info->activity_type != activity_type) { continue; } // Is there a zone restriction on the activity_information ? @@ -766,7 +815,7 @@ void ClientTaskState::UpdateTasksForItem(Client *client, ActivityType activity_t LogTasks( "[UpdateTasksForItem] Error: Character [{}] activity_information type [{}] for Item [{}] failed zone check", client->GetName(), - activity_type, + static_cast(activity_type), item_id ); continue; @@ -800,7 +849,8 @@ void ClientTaskState::UpdateTasksForItem(Client *client, ActivityType activity_t void ClientTaskState::UpdateTasksOnExplore(Client *client, int explore_id) { LogTasks("[UpdateTasksOnExplore] explore_id [{}]", explore_id); - if (!task_manager || (m_active_task_count == 0 && m_active_task.task_id == TASKSLOTEMPTY)) { // could be better ... + + if (!HasActiveTasks()) { return; } @@ -827,7 +877,7 @@ void ClientTaskState::UpdateTasksOnExplore(Client *client, int explore_id) continue; } // We are only interested in explore activities - if (activity_info->activity_type != ActivityExplore) { + if (activity_info->activity_type != TaskActivityType::Explore) { continue; } if (!activity_info->CheckZone(zone->GetZoneID())) { @@ -890,7 +940,8 @@ bool ClientTaskState::UpdateTasksOnDeliver( bool is_updated = false; LogTasks("[UpdateTasksOnDeliver] [{}]", npc_type_id); - if (!task_manager || (m_active_task_count == 0 && m_active_task.task_id == TASKSLOTEMPTY)) { // could be better ... + + if (!HasActiveTasks()) { return false; } @@ -917,8 +968,8 @@ bool ClientTaskState::UpdateTasksOnDeliver( } // We are only interested in Deliver activities - if (activity_info->activity_type != ActivityDeliver && - activity_info->activity_type != ActivityGiveCash) { + if (activity_info->activity_type != TaskActivityType::Deliver && + activity_info->activity_type != TaskActivityType::GiveCash) { continue; } // Is there a zone restriction on the activity_information ? @@ -936,7 +987,7 @@ bool ClientTaskState::UpdateTasksOnDeliver( } // Is the activity_information related to these items ? // - if ((activity_info->activity_type == ActivityGiveCash) && cash) { + if ((activity_info->activity_type == TaskActivityType::GiveCash) && cash) { LogTasks("[UpdateTasksOnDeliver] Increment on GiveCash"); IncrementDoneCount(client, p_task_data, i, activity_id, cash); is_updated = true; @@ -985,7 +1036,8 @@ void ClientTaskState::UpdateTasksOnTouch(Client *client, int zone_id) // If the client has no tasks, there is nothing further to check. LogTasks("[UpdateTasksOnTouch] [{}] ", zone_id); - if (!task_manager || (m_active_task_count == 0 && m_active_task.task_id == TASKSLOTEMPTY)) { // could be better ... + + if (!HasActiveTasks()) { return; } @@ -1011,7 +1063,7 @@ void ClientTaskState::UpdateTasksOnTouch(Client *client, int zone_id) continue; } // We are only interested in touch activities - if (activity_info->activity_type != ActivityTouch) { + if (activity_info->activity_type != TaskActivityType::Touch) { continue; } if (activity_info->goal_method != METHODSINGLEID) { @@ -1048,13 +1100,58 @@ void ClientTaskState::IncrementDoneCount( bool ignore_quest_update ) { - Log(Logs::General, Logs::Tasks, "[UPDATE] IncrementDoneCount"); - auto info = GetClientTaskInfo(task_information->type, task_index); if (info == nullptr) { return; } + LogTasks( + "[IncrementDoneCount] client [{}] task_id [{}] activity_id [{}] count [{}]", + client->GetCleanName(), + info->task_id, + activity_id, + count + ); + + // shared task shim + // intercept and pass to world first before processing normally + if (!client->m_shared_task_update && task_information->type == TaskType::Shared) { + + // struct + auto pack = new ServerPacket(ServerOP_SharedTaskUpdate, sizeof(ServerSharedTaskActivityUpdate_Struct)); + auto *r = (ServerSharedTaskActivityUpdate_Struct *) pack->pBuffer; + + // fill + r->source_character_id = client->CharacterID(); + r->task_id = info->task_id; + r->activity_id = activity_id; + r->done_count = info->activity[activity_id].done_count + count; + r->ignore_quest_update = ignore_quest_update; + + LogTasksDetail( + "[IncrementDoneCount] shared_task sending client [{}] task_id [{}] activity_id [{}] count [{}] ignore_quest_update [{}]", + r->source_character_id, + r->task_id, + r->activity_id, + r->done_count, + (ignore_quest_update ? "true" : "false") + ); + + SyncSharedTaskZoneClientDoneCountState( + client, + task_information, + task_index, + activity_id, + r->done_count + ); + + // send + worldserver.SendPacket(pack); + safe_delete(pack); + + return; + } + info->activity[activity_id].done_count += count; if (info->activity[activity_id].done_count > task_information->activity_information[activity_id].goal_count) { @@ -1078,8 +1175,7 @@ void ClientTaskState::IncrementDoneCount( info->activity[activity_id].updated = true; // Have we reached the goal count for this activity_information ? if (info->activity[activity_id].done_count >= task_information->activity_information[activity_id].goal_count) { - Log( - Logs::General, Logs::Tasks, "[UPDATE] Done (%i) = Goal (%i) for activity_information %i", + LogTasks("[IncrementDoneCount] done_count [{}] goal_count [{}] activity_id [{}]", info->activity[activity_id].done_count, task_information->activity_information[activity_id].goal_count, activity_id @@ -1089,32 +1185,30 @@ void ClientTaskState::IncrementDoneCount( info->activity[activity_id].activity_state = ActivityCompleted; // Unlock subsequent activities for this task bool task_complete = UnlockActivities(client->CharacterID(), *info); - Log(Logs::General, Logs::Tasks, "[UPDATE] TaskCompleted is %i", task_complete); + LogTasks("[IncrementDoneCount] task_complete is [{}]", task_complete); // and by the 'Task Stage Completed' message client->SendTaskActivityComplete(info->task_id, activity_id, task_index, task_information->type); // Send the updated task/activity_information list to the client task_manager->SendSingleActiveTaskToClient(client, *info, task_complete, false); // Inform the client the task has been updated, both by a chat message - client->Message(Chat::White, "Your task '%s' has been updated.", task_information->title.c_str()); + client->MessageString(Chat::White, TASK_UPDATED, task_information->title.c_str()); - if (task_information->activity_information[activity_id].goal_method != METHODQUEST) { - if (!ignore_quest_update) { - char buf[24]; - snprintf(buf, 23, "%d %d", info->task_id, info->activity[activity_id].activity_id); - buf[23] = '\0'; - parse->EventPlayer(EVENT_TASK_STAGE_COMPLETE, client, buf, 0); - } - /* QS: PlayerLogTaskUpdates :: Update */ - if (RuleB(QueryServ, PlayerLogTaskUpdates)) { - std::string event_desc = StringFormat( - "Task Stage Complete :: taskid:%i activityid:%i donecount:%i in zoneid:%i instid:%i", - info->task_id, - info->activity[activity_id].activity_id, - info->activity[activity_id].done_count, - client->GetZoneID(), - client->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Task_Updates, client->CharacterID(), event_desc); - } + if (!ignore_quest_update) { + char buf[24]; + snprintf(buf, 23, "%d %d", info->task_id, info->activity[activity_id].activity_id); + buf[23] = '\0'; + parse->EventPlayer(EVENT_TASK_STAGE_COMPLETE, client, buf, 0); + } + /* QS: PlayerLogTaskUpdates :: Update */ + if (RuleB(QueryServ, PlayerLogTaskUpdates)) { + std::string event_desc = StringFormat( + "Task Stage Complete :: taskid:%i activityid:%i donecount:%i in zoneid:%i instid:%i", + info->task_id, + info->activity[activity_id].activity_id, + info->activity[activity_id].done_count, + client->GetZoneID(), + client->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Task_Updates, client->CharacterID(), event_desc); } // If this task is now complete, the Completed tasks will have been @@ -1145,16 +1239,24 @@ void ClientTaskState::IncrementDoneCount( QServ->PlayerLogEvent(Player_Log_Task_Updates, client->CharacterID(), event_desc); } - task_manager->SendCompletedTasksToClient(client, this); client->SendTaskActivityComplete(info->task_id, 0, task_index, task_information->type, 0); task_manager->SaveClientState(client, this); - //c->SendTaskComplete(TaskIndex); - client->CancelTask(task_index, task_information->type); - //if(Task->reward_method != METHODQUEST) RewardTask(c, Task); + // If Experience and/or cash rewards are set, reward them from the task even if reward_method is METHODQUEST RewardTask(client, task_information); //RemoveTask(c, TaskIndex); + // add replay timer (world adds timers to shared task members) + AddReplayTimer(client, *info, *task_information); + + // shared tasks linger at the completion step and do not get removed from the task window unlike quests/task + if (task_information->type == TaskType::Shared) { + return; + } + + task_manager->SendCompletedTasksToClient(client, this); + + client->CancelTask(task_index, task_information->type); } } @@ -1164,8 +1266,7 @@ void ClientTaskState::IncrementDoneCount( client, info->task_id, activity_id, - task_index, - task_information->activity_information[activity_id].optional + task_index ); task_manager->SaveClientState(client, this); } @@ -1178,6 +1279,10 @@ void ClientTaskState::RewardTask(Client *client, TaskInformation *task_informati return; } + if (!task_information->completion_emote.empty()) { + client->Message(Chat::Yellow, task_information->completion_emote.c_str()); + } + const EQ::ItemData *item_data; std::vector reward_list; @@ -1187,7 +1292,7 @@ void ClientTaskState::RewardTask(Client *client, TaskInformation *task_informati client->SummonItem(task_information->reward_id); item_data = database.GetItem(task_information->reward_id); if (item_data) { - client->Message(Chat::Yellow, "You receive %s as a reward.", item_data->Name); + client->MessageString(Chat::Yellow, YOU_HAVE_BEEN_GIVEN, item_data->Name); } } break; @@ -1198,7 +1303,7 @@ void ClientTaskState::RewardTask(Client *client, TaskInformation *task_informati client->SummonItem(item_id); item_data = database.GetItem(item_id); if (item_data) { - client->Message(Chat::Yellow, "You receive %s as a reward.", item_data->Name); + client->MessageString(Chat::Yellow, YOU_HAVE_BEEN_GIVEN, item_data->Name); } } break; @@ -1209,13 +1314,6 @@ void ClientTaskState::RewardTask(Client *client, TaskInformation *task_informati } } - if (!task_information->completion_emote.empty()) { - client->SendColoredText( - Chat::Yellow, - task_information->completion_emote - ); - } // unsure if they use this packet or color, should work - // just use normal NPC faction ID stuff if (task_information->faction_reward) { client->SetFactionLevel( @@ -1294,6 +1392,11 @@ void ClientTaskState::RewardTask(Client *client, TaskInformation *task_informati } } + if (task_information->reward_radiant_crystals > 0 || task_information->reward_ebon_crystals > 0) + { + client->AddCrystals(task_information->reward_radiant_crystals, task_information->reward_ebon_crystals); + } + client->SendSound(); } @@ -1303,6 +1406,10 @@ bool ClientTaskState::IsTaskActive(int task_id) return true; } + if (m_active_shared_task.task_id == task_id) { + return true; + } + if (m_active_task_count == 0 || task_id == 0) { return false; } @@ -1325,10 +1432,20 @@ void ClientTaskState::FailTask(Client *client, int task_id) m_active_task_count ); + + // type: Task if (m_active_task.task_id == task_id) { - client->SendTaskFailed(task_id, 0, TaskType::Task); + client->SendTaskFailed(task_id, TASKSLOTTASK, TaskType::Task); // Remove the task from the client - client->CancelTask(0, TaskType::Task); + client->CancelTask(TASKSLOTTASK, TaskType::Task); + return; + } + + // type: Shared Task + if (m_active_shared_task.task_id == task_id) { + client->SendTaskFailed(task_id, TASKSLOTSHAREDTASK, TaskType::Shared); + // Remove the task from the client + client->CancelTask(TASKSLOTSHAREDTASK, TaskType::Shared); return; } @@ -1357,15 +1474,19 @@ bool ClientTaskState::IsTaskActivityActive(int task_id, int activity_id) if (activity_id < 0) { return false; } - if (m_active_task_count == 0 && m_active_task.task_id == TASKSLOTEMPTY) { + if (m_active_task_count == 0 && m_active_task.task_id == TASKSLOTEMPTY && + m_active_shared_task.task_id == TASKSLOTEMPTY) { return false; } int active_task_index = -1; auto task_type = TaskType::Task; - if (m_active_task.task_id == task_id) { - active_task_index = 0; + active_task_index = TASKSLOTTASK; + } + if (m_active_shared_task.task_id == task_id) { + task_type = TaskType::Shared; + active_task_index = TASKSLOTSHAREDTASK; } if (active_task_index == -1) { @@ -1426,7 +1547,8 @@ void ClientTaskState::UpdateTaskActivity( ); // Quick sanity check - if (activity_id < 0 || (m_active_task_count == 0 && m_active_task.task_id == TASKSLOTEMPTY)) { + if (activity_id < 0 || (m_active_task_count == 0 && m_active_task.task_id == TASKSLOTEMPTY && + m_active_shared_task.task_id == TASKSLOTEMPTY)) { return; } @@ -1434,7 +1556,11 @@ void ClientTaskState::UpdateTaskActivity( auto type = TaskType::Task; if (m_active_task.task_id == task_id) { - active_task_index = 0; + active_task_index = TASKSLOTTASK; + } + if (m_active_shared_task.task_id == task_id) { + type = TaskType::Shared; + active_task_index = TASKSLOTSHAREDTASK; } if (active_task_index == -1) { @@ -1494,14 +1620,19 @@ void ClientTaskState::ResetTaskActivity(Client *client, int task_id, int activit ); // Quick sanity check - if (activity_id < 0 || (m_active_task_count == 0 && m_active_task.task_id == TASKSLOTEMPTY)) { + if (activity_id < 0 || (m_active_task_count == 0 && m_active_task.task_id == TASKSLOTEMPTY && + m_active_shared_task.task_id == TASKSLOTEMPTY)) { return; } int active_task_index = -1; auto type = TaskType::Task; if (m_active_task.task_id == task_id) { - active_task_index = 0; + active_task_index = TASKSLOTTASK; + } + if (m_active_shared_task.task_id == task_id) { + type = TaskType::Shared; + active_task_index = TASKSLOTSHAREDTASK; } if (active_task_index == -1) { @@ -1556,32 +1687,67 @@ void ClientTaskState::ResetTaskActivity(Client *client, int task_id, int activit ); } +void ClientTaskState::ShowClientTaskInfoMessage(ClientTaskInformation *task, Client *c) +{ + auto task_data = task_manager->m_task_data[task->task_id]; + + c->Message(Chat::White, "------------------------------------------------"); + c->Message( + Chat::White, "# [%s] | task_id [%i] title [%s] slot (%i)", + Tasks::GetTaskTypeDescription(task_data->type).c_str(), + task->task_id, + task_data->title.c_str(), + task->slot + ); + c->Message(Chat::White, "------------------------------------------------"); + c->Message( + Chat::White, + " -- Description [%s]\n", + task_data->description.c_str() + ); + + for (int activity_id = 0; activity_id < task_manager->GetActivityCount(task->task_id); activity_id++) { + std::vector update_increments = {"1", "5", "50"}; + std::string update_saylinks; + + for (auto &increment: update_increments) { + auto task_update_saylink = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format( + "#task update {} {} {}", + task->task_id, + task->activity[activity_id].activity_id, + increment + ), + false, + increment + ); + + update_saylinks += "[" + task_update_saylink + "] "; + } + + c->Message( + Chat::White, + " --- Update %s activity_id [%i] done_count [%i] state [%d] (%s)", + update_saylinks.c_str(), + task->activity[activity_id].activity_id, + task->activity[activity_id].done_count, + task->activity[activity_id].activity_state, + Tasks::GetActivityStateDescription(task->activity[activity_id].activity_state).c_str() + ); + } +} + void ClientTaskState::ShowClientTasks(Client *client) { client->Message(Chat::White, "------------------------------------------------"); client->Message(Chat::White, "# Task Information | Client [%s]", client->GetCleanName()); // client->Message(Chat::White, "------------------------------------------------"); if (m_active_task.task_id != TASKSLOTEMPTY) { - client->Message( - Chat::White, - "Task: %i %s", - m_active_task.task_id, - task_manager->m_task_data[m_active_task.task_id]->title.c_str() - ); - client->Message( - Chat::White, - " description: [%s]\n", - task_manager->m_task_data[m_active_task.task_id]->description.c_str() - ); - for (int activity_id = 0; activity_id < task_manager->GetActivityCount(m_active_task.task_id); activity_id++) { - client->Message( - Chat::White, - " activity_information: %2d, done_count: %2d, Status: %d (0=Hidden, 1=Active, 2=Complete)", - m_active_task.activity[activity_id].activity_id, - m_active_task.activity[activity_id].done_count, - m_active_task.activity[activity_id].activity_state - ); - } + ShowClientTaskInfoMessage(&m_active_task, client); + } + + if (m_active_shared_task.task_id != TASKSLOTEMPTY) { + ShowClientTaskInfoMessage(&m_active_shared_task, client); } for (auto &active_quest : m_active_quests) { @@ -1589,48 +1755,7 @@ void ClientTaskState::ShowClientTasks(Client *client) continue; } - client->Message(Chat::White, "------------------------------------------------"); - client->Message( - Chat::White, "# Quest | task_id [%i] title [%s]", - active_quest.task_id, - task_manager->m_task_data[active_quest.task_id]->title.c_str() - ); - client->Message(Chat::White, "------------------------------------------------"); - - client->Message( - Chat::White, - " -- Description [%s]\n", - task_manager->m_task_data[active_quest.task_id]->description.c_str() - ); - - for (int activity_id = 0; activity_id < task_manager->GetActivityCount(active_quest.task_id); activity_id++) { - std::vector update_increments = {"1", "5", "50"}; - std::string update_saylinks; - - for (auto &increment: update_increments) { - auto task_update_saylink = EQ::SayLinkEngine::GenerateQuestSaylink( - fmt::format( - "#task update {} {} {}", - active_quest.task_id, - active_quest.activity[activity_id].activity_id, - increment - ), - false, - increment - ); - - update_saylinks += "[" + task_update_saylink + "] "; - } - - client->Message( - Chat::White, - " --- activity_id [%i] done_count [%i] state [%d] (0=hidden 1=active 2=complete) | Update %s", - active_quest.activity[activity_id].activity_id, - active_quest.activity[activity_id].done_count, - active_quest.activity[activity_id].activity_state, - update_saylinks.c_str() - ); - } + ShowClientTaskInfoMessage(&active_quest, client); } client->Message(Chat::White, "------------------------------------------------"); @@ -1639,6 +1764,8 @@ void ClientTaskState::ShowClientTasks(Client *client) // TODO: Shared Task int ClientTaskState::TaskTimeLeft(int task_id) { + + // type "task" if (m_active_task.task_id == task_id) { int time_now = time(nullptr); @@ -1656,6 +1783,24 @@ int ClientTaskState::TaskTimeLeft(int task_id) return (time_left > 0 ? time_left : 0); } + // type "shared task" + if (m_active_shared_task.task_id == task_id) { + int time_now = time(nullptr); + + TaskInformation *p_task_data = task_manager->m_task_data[task_id]; + if (p_task_data == nullptr) { + return -1; + } + + if (!p_task_data->duration) { + return -1; + } + + int time_left = (m_active_shared_task.accepted_time + p_task_data->duration - time_now); + + return (time_left > 0 ? time_left : 0); + } + if (m_active_task_count == 0) { return -1; } @@ -1731,12 +1876,14 @@ bool ClientTaskState::TaskOutOfTime(TaskType task_type, int index) void ClientTaskState::TaskPeriodicChecks(Client *client) { + + // type "task" if (m_active_task.task_id != TASKSLOTEMPTY) { - if (TaskOutOfTime(TaskType::Task, 0)) { + if (TaskOutOfTime(TaskType::Task, TASKSLOTTASK)) { // Send Red Task Failed Message - client->SendTaskFailed(m_active_task.task_id, 0, TaskType::Task); + client->SendTaskFailed(m_active_task.task_id, TASKSLOTTASK, TaskType::Task); // Remove the task from the client - client->CancelTask(0, TaskType::Task); + client->CancelTask(TASKSLOTTASK, TaskType::Task); // It is a conscious decision to only fail one task per call to this method, // otherwise the player will not see all the failed messages where multiple // tasks fail at the same time. @@ -1744,7 +1891,19 @@ void ClientTaskState::TaskPeriodicChecks(Client *client) } } - // TODO: shared tasks -- although that will probably be manager in world checking and telling zones to fail us + // type "shared" + if (m_active_shared_task.task_id != TASKSLOTEMPTY) { + if (TaskOutOfTime(TaskType::Shared, TASKSLOTSHAREDTASK)) { + // Send Red Task Failed Message + client->SendTaskFailed(m_active_shared_task.task_id, TASKSLOTSHAREDTASK, TaskType::Shared); + // Remove the task from the client + client->CancelTask(TASKSLOTSHAREDTASK, TaskType::Shared); + // It is a conscious decision to only fail one task per call to this method, + // otherwise the player will not see all the failed messages where multiple + // tasks fail at the same time. + return; + } + } if (m_active_task_count == 0) { return; @@ -1783,12 +1942,15 @@ bool ClientTaskState::IsTaskActivityCompleted(TaskType task_type, int index, int { switch (task_type) { case TaskType::Task: - if (index != 0) { + if (index != TASKSLOTTASK) { return false; } return m_active_task.activity[activity_id].activity_state == ActivityCompleted; case TaskType::Shared: - return false; // TODO: shared tasks + if (index != TASKSLOTSHAREDTASK) { + return false; + } + return m_active_shared_task.activity[activity_id].activity_state == ActivityCompleted; case TaskType::Quest: if (index < MAXACTIVEQUESTS) { return m_active_quests[index].activity[activity_id].activity_state == ActivityCompleted; @@ -1802,33 +1964,58 @@ bool ClientTaskState::IsTaskActivityCompleted(TaskType task_type, int index, int // should we be defaulting to hidden? ActivityState ClientTaskState::GetTaskActivityState(TaskType task_type, int index, int activity_id) { + ActivityState return_state = ActivityHidden; switch (task_type) { case TaskType::Task: - if (index != 0) { - return ActivityHidden; + if (index != TASKSLOTTASK) { + return_state = ActivityHidden; + break; } - return m_active_task.activity[activity_id].activity_state; + return_state = m_active_task.activity[activity_id].activity_state; + break; case TaskType::Shared: - return ActivityHidden; // TODO: shared tasks + if (index != TASKSLOTSHAREDTASK) { + return_state = ActivityHidden; + break; + } + return_state = m_active_shared_task.activity[activity_id].activity_state; + break; case TaskType::Quest: if (index < MAXACTIVEQUESTS) { - return m_active_quests[index].activity[activity_id].activity_state; + return_state = m_active_quests[index].activity[activity_id].activity_state; + break; } + break; default: - return ActivityHidden; + return_state = ActivityHidden; } + + LogTasksDetail( + "-- [GetTaskActivityState] task_type [{}] ({}) index [{}] activity_id [{}] activity_state [{}] ({})", + Tasks::GetTaskTypeIdentifier(task_type), + Tasks::GetTaskTypeDescription(task_type), + index, + activity_id, + Tasks::GetActivityStateIdentifier(return_state), + Tasks::GetActivityStateDescription(return_state) + ); + + return return_state; } int ClientTaskState::GetTaskActivityDoneCount(TaskType task_type, int index, int activity_id) { switch (task_type) { case TaskType::Task: - if (index != 0) { + if (index != TASKSLOTTASK) { return 0; } return m_active_task.activity[activity_id].done_count; case TaskType::Shared: - return 0; // TODO: shared tasks + if (index != TASKSLOTSHAREDTASK) { + return 0; + } + return m_active_shared_task.activity[activity_id].done_count; case TaskType::Quest: if (index < MAXACTIVEQUESTS) { return m_active_quests[index].activity[activity_id].done_count; @@ -1840,11 +2027,16 @@ int ClientTaskState::GetTaskActivityDoneCount(TaskType task_type, int index, int int ClientTaskState::GetTaskActivityDoneCountFromTaskID(int task_id, int activity_id) { + + // type "task" if (m_active_task.task_id == task_id) { return m_active_task.activity[activity_id].done_count; } - // TODO: shared tasks + // type "shared" + if (m_active_shared_task.task_id == task_id) { + return m_active_shared_task.activity[activity_id].done_count; + } int active_task_index = -1; @@ -1874,7 +2066,8 @@ int ClientTaskState::GetTaskStartTime(TaskType task_type, int index) return m_active_task.accepted_time; case TaskType::Quest: return m_active_quests[index].accepted_time; - case TaskType::Shared: // TODO + case TaskType::Shared: + return m_active_shared_task.accepted_time; default: return -1; } @@ -1887,9 +2080,16 @@ void ClientTaskState::CancelAllTasks(Client *client) // It removes tasks from the in-game client state ready for them to be // resent to the client, in case an updated task fails to load - CancelTask(client, 0, TaskType::Task, false); + // task + // these cancels lock up the client for some reason + CancelTask(client, TASKSLOTTASK, TaskType::Task, false); m_active_task.task_id = TASKSLOTEMPTY; + // shared task + CancelTask(client, TASKSLOTSHAREDTASK, TaskType::Shared, false); + m_active_shared_task.task_id = TASKSLOTEMPTY; + + // "quests" for (int task_index = 0; task_index < MAXACTIVEQUESTS; task_index++) if (m_active_quests[task_index].task_id != TASKSLOTEMPTY) { CancelTask(client, task_index, TaskType::Quest, false); @@ -1899,24 +2099,60 @@ void ClientTaskState::CancelAllTasks(Client *client) // TODO: shared } -void ClientTaskState::CancelTask(Client *client, int sequence_number, TaskType task_type, bool remove_from_db) +void ClientTaskState::CancelTask(Client *c, int sequence_number, TaskType task_type, bool remove_from_db) { + LogTasks("CancelTask"); + + // shared task middleware + // intercept and pass to world first before processing normally + if (!c->m_requested_shared_task_removal && task_type == TaskType::Shared && m_active_shared_task.task_id != 0) { + + // struct + auto pack = new ServerPacket(ServerOP_SharedTaskAttemptRemove, sizeof(ServerSharedTaskAttemptRemove_Struct)); + auto *r = (ServerSharedTaskAttemptRemove_Struct *) pack->pBuffer; + + // fill + r->requested_character_id = c->CharacterID(); + r->requested_task_id = m_active_shared_task.task_id; + r->remove_from_db = remove_from_db; + + // send + worldserver.SendPacket(pack); + safe_delete(pack); + + return; + } + + // packet auto outapp = new EQApplicationPacket(OP_CancelTask, sizeof(CancelTask_Struct)); - CancelTask_Struct *cts = (CancelTask_Struct *) outapp->pBuffer; + // fill + auto *cts = (CancelTask_Struct *) outapp->pBuffer; cts->SequenceNumber = sequence_number; cts->type = static_cast(task_type); - Log(Logs::General, Logs::Tasks, "[UPDATE] CancelTask"); - - client->QueuePacket(outapp); + // send + c->QueuePacket(outapp); safe_delete(outapp); + // persistence if (remove_from_db) { - RemoveTask(client, sequence_number, task_type); + RemoveTask(c, sequence_number, task_type); } } +void ClientTaskState::KickPlayersSharedTask(Client* client) +{ + uint32_t pack_size = sizeof(ServerSharedTaskKickPlayers_Struct); + auto pack = std::make_unique(ServerOP_SharedTaskKickPlayers, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + + buf->source_character_id = client->CharacterID(); + buf->task_id = m_active_shared_task.task_id; + + worldserver.SendPacket(pack.get()); +} + void ClientTaskState::RemoveTask(Client *client, int sequence_number, TaskType task_type) { int character_id = client->CharacterID(); @@ -1925,7 +2161,7 @@ void ClientTaskState::RemoveTask(Client *client, int sequence_number, TaskType t int task_id = -1; switch (task_type) { case TaskType::Task: - if (sequence_number == 0) { + if (sequence_number == TASKSLOTTASK) { task_id = m_active_task.task_id; } break; @@ -1934,7 +2170,11 @@ void ClientTaskState::RemoveTask(Client *client, int sequence_number, TaskType t task_id = m_active_quests[sequence_number].task_id; } break; - case TaskType::Shared: // TODO: + case TaskType::Shared: + if (sequence_number == TASKSLOTSHAREDTASK) { + task_id = m_active_shared_task.task_id; + } + break; default: break; } @@ -1954,7 +2194,8 @@ void ClientTaskState::RemoveTask(Client *client, int sequence_number, TaskType t m_active_task.task_id = TASKSLOTEMPTY; break; case TaskType::Shared: - break; // TODO: shared tasks + m_active_shared_task.task_id = TASKSLOTEMPTY; + break; case TaskType::Quest: m_active_quests[sequence_number].task_id = TASKSLOTEMPTY; m_active_task_count--; @@ -1984,7 +2225,7 @@ void ClientTaskState::RemoveTaskByTaskID(Client *client, uint32 task_id) if (m_active_task.task_id == task_id) { auto outapp = new EQApplicationPacket(OP_CancelTask, sizeof(CancelTask_Struct)); CancelTask_Struct *cts = (CancelTask_Struct *) outapp->pBuffer; - cts->SequenceNumber = 0; + cts->SequenceNumber = TASKSLOTTASK; cts->type = static_cast(task_type); LogTasks("[UPDATE] RemoveTaskByTaskID found Task [{}]", task_id); client->QueuePacket(outapp); @@ -1994,15 +2235,25 @@ void ClientTaskState::RemoveTaskByTaskID(Client *client, uint32 task_id) break; } case TaskType::Shared: { - break; // TODO: shared tasks + if (m_active_shared_task.task_id == task_id) { + auto outapp = new EQApplicationPacket(OP_CancelTask, sizeof(CancelTask_Struct)); + CancelTask_Struct *cts = (CancelTask_Struct *) outapp->pBuffer; + cts->SequenceNumber = TASKSLOTSHAREDTASK; + cts->type = static_cast(task_type); + LogTasks("[UPDATE] RemoveTaskByTaskID found Task [{}]", task_id); + client->QueuePacket(outapp); + safe_delete(outapp); + m_active_shared_task.task_id = TASKSLOTEMPTY; + } + break; } case TaskType::Quest: { for (int active_quest = 0; active_quest < MAXACTIVEQUESTS; active_quest++) { if (m_active_quests[active_quest].task_id == task_id) { auto outapp = new EQApplicationPacket(OP_CancelTask, sizeof(CancelTask_Struct)); CancelTask_Struct *cts = (CancelTask_Struct *) outapp->pBuffer; - cts->SequenceNumber = active_quest; - cts->type = static_cast(task_type); + cts->SequenceNumber = active_quest; + cts->type = static_cast(task_type); LogTasks("[UPDATE] RemoveTaskByTaskID found Quest [{}] at index [{}]", task_id, active_quest); m_active_quests[active_quest].task_id = TASKSLOTEMPTY; m_active_task_count--; @@ -2017,7 +2268,13 @@ void ClientTaskState::RemoveTaskByTaskID(Client *client, uint32 task_id) } } -void ClientTaskState::AcceptNewTask(Client *client, int task_id, int npc_type_id, bool enforce_level_requirement) +void ClientTaskState::AcceptNewTask( + Client *client, + int task_id, + int npc_type_id, + time_t accept_time, + bool enforce_level_requirement +) { if (!task_manager || task_id < 0 || task_id >= MAXTASKS) { client->Message(Chat::Red, "Task system not functioning, or task_id %i out of range.", task_id); @@ -2025,12 +2282,37 @@ void ClientTaskState::AcceptNewTask(Client *client, int task_id, int npc_type_id } auto task = task_manager->m_task_data[task_id]; - if (task == nullptr) { client->Message(Chat::Red, "Invalid task_id %i", task_id); return; } + // shared task + // intercept and pass to world first before processing normally + if (!client->m_requesting_shared_task && task->type == TaskType::Shared) { + LogTasksDetail( + "[AcceptNewTask] Initiating shared_task request | task_id [{}] character_id [{}] name [{}]", + task_id, + client->CharacterID(), + client->GetCleanName() + ); + + // struct + auto pack = new ServerPacket(ServerOP_SharedTaskRequest, sizeof(ServerSharedTaskRequest_Struct)); + auto *r = (ServerSharedTaskRequest_Struct *) pack->pBuffer; + + // fill + r->requested_character_id = client->CharacterID(); + r->requested_task_id = task_id; + r->requested_npc_type_id = npc_type_id; + + // send + worldserver.SendPacket(pack); + safe_delete(pack); + + return; + } + bool max_tasks = false; switch (task->type) { @@ -2040,8 +2322,9 @@ void ClientTaskState::AcceptNewTask(Client *client, int task_id, int npc_type_id } break; case TaskType::Shared: // TODO: shared tasks - // if (something) - max_tasks = true; + if (m_active_shared_task.task_id != TASKSLOTEMPTY) { + max_tasks = true; + } break; case TaskType::Quest: if (m_active_task_count == MAXACTIVEQUESTS) { @@ -2053,11 +2336,7 @@ void ClientTaskState::AcceptNewTask(Client *client, int task_id, int npc_type_id } if (max_tasks) { - client->Message( - Chat::Red, - "You already have the maximum allowable number of active tasks (%i)", - MAXACTIVEQUESTS - ); + client->MessageString(Chat::Yellow, MAX_ACTIVE_TASKS, ".", ".", client->GetName()); return; } @@ -2065,14 +2344,15 @@ void ClientTaskState::AcceptNewTask(Client *client, int task_id, int npc_type_id if (task->type == TaskType::Quest) { for (auto &active_quest : m_active_quests) { if (active_quest.task_id == task_id) { - client->Message(Chat::Red, "You have already been assigned this task."); + // live doesn't have an eqstr for it but this seems to be the string used for this scenario + client->Message(Chat::Yellow, "You are already working on a task for this person, you must finish it before asking for another."); return; } } } if (enforce_level_requirement && !task_manager->ValidateLevel(task_id, client->GetLevel())) { - client->Message(Chat::Red, "You are outside the level range of this task."); + client->MessageString(Chat::Yellow, TASK_NOT_RIGHT_LEVEL); return; } @@ -2080,6 +2360,39 @@ void ClientTaskState::AcceptNewTask(Client *client, int task_id, int npc_type_id return; } + // solo task timer lockout validation + if (task->type != TaskType::Shared) + { + auto task_timers = CharacterTaskTimersRepository::GetWhere(database, fmt::format( + "character_id = {} AND task_id = {} AND expire_time > NOW() ORDER BY timer_type ASC LIMIT 1", + client->CharacterID(), task_id + )); + + if (!task_timers.empty()) + { + auto timer_type = static_cast(task_timers.front().timer_type); + auto seconds = task_timers.front().expire_time - std::time(nullptr); + auto days = fmt::format_int(seconds / 86400).str(); + auto hours = fmt::format_int((seconds / 3600) % 24).str(); + auto mins = fmt::format_int((seconds / 60) % 60).str(); + + // these solo task messages are in SharedTaskMessage for convenience + namespace EQStr = SharedTaskMessage; + if (timer_type == TaskTimerType::Replay) + { + int eqstr_id = EQStr::TASK_ASSIGN_WAIT_REPLAY_TIMER; + client->MessageString(Chat::Red, eqstr_id, days.c_str(), hours.c_str(), mins.c_str()); + } + else if (timer_type == TaskTimerType::Request) + { + int eqstr_id = EQStr::TASK_ASSIGN_WAIT_REQUEST_TIMER; + client->Message(Chat::Red, fmt::format(EQStr::GetEQStr(eqstr_id), days, hours, mins).c_str()); + } + + return; + } + } + // We do it this way, because when the Client cancels a task, it retains the sequence number of the remaining // tasks in it's window, until something causes the TaskDescription packets to be sent again. We could just // resend all the active task data to the client when it cancels a task, but that could be construed as a @@ -2090,9 +2403,11 @@ void ClientTaskState::AcceptNewTask(Client *client, int task_id, int npc_type_id case TaskType::Task: active_slot = &m_active_task; break; - case TaskType::Shared: // TODO: shared - active_slot = nullptr; + + case TaskType::Shared: + active_slot = &m_active_shared_task; break; + case TaskType::Quest: for (int task_index = 0; task_index < MAXACTIVEQUESTS; task_index++) { Log(Logs::General, Logs::Tasks, @@ -2104,22 +2419,19 @@ void ClientTaskState::AcceptNewTask(Client *client, int task_id, int npc_type_id } } break; + default: break; } // This shouldn't happen unless there is a bug in the handling of ActiveTaskCount somewhere if (active_slot == nullptr) { - client->Message( - Chat::Red, - "You already have the maximum allowable number of active tasks (%i)", - MAXACTIVEQUESTS - ); + client->MessageString(Chat::Yellow, MAX_ACTIVE_TASKS, ".", ".", client->GetName()); return; } active_slot->task_id = task_id; - active_slot->accepted_time = time(nullptr); + active_slot->accepted_time = static_cast(accept_time); active_slot->updated = true; active_slot->current_step = -1; @@ -2136,12 +2448,39 @@ void ClientTaskState::AcceptNewTask(Client *client, int task_id, int npc_type_id m_active_task_count++; } + // add request timer (shared task timers are added to members by world) + if (task->request_timer_seconds > 0) + { + auto expire_time = active_slot->accepted_time + task->request_timer_seconds; + + auto seconds = expire_time - std::time(nullptr); + if (seconds > 0) // not already expired + { + if (task->type != TaskType::Shared) + { + auto timer = CharacterTaskTimersRepository::NewEntity(); + timer.character_id = client->CharacterID(); + timer.task_id = task_id; + timer.timer_type = static_cast(TaskTimerType::Request); + timer.expire_time = expire_time; + + CharacterTaskTimersRepository::InsertOne(database, timer); + } + + client->Message(Chat::Yellow, fmt::format( + SharedTaskMessage::GetEQStr(SharedTaskMessage::RECEIVED_REQUEST_TIMER), + task->title, + fmt::format_int(seconds / 86400).c_str(), // days + fmt::format_int((seconds / 3600) % 24).c_str(), // hours + fmt::format_int((seconds / 60) % 60).c_str() // minutes + ).c_str()); + } + } + task_manager->SendSingleActiveTaskToClient(client, *active_slot, false, true); - client->Message( - Chat::White, - "You have been assigned the task '%s'.", - task_manager->m_task_data[task_id]->title.c_str() - ); + client->StartTaskRequestCooldownTimer(); + client->MessageString(Chat::White, YOU_ASSIGNED_TASK, task->title.c_str()); + task_manager->SaveClientState(client, this); std::string buf = std::to_string(task_id); @@ -2175,3 +2514,357 @@ void ClientTaskState::ProcessTaskProximities(Client *client, float x, float y, f UpdateTasksOnExplore(client, explore_id); } } + +void ClientTaskState::SharedTaskIncrementDoneCount( + Client *client, + int task_id, + int activity_id, + int done_count, + bool ignore_quest_update +) +{ + TaskInformation *t = task_manager->m_task_data[task_id]; + + auto info = GetClientTaskInfo(t->type, TASKSLOTSHAREDTASK); + if (info == nullptr) { + return; + } + + // absolute value update + info->activity[activity_id].done_count = done_count; + + LogTasksDetail( + "[SharedTaskIncrementDoneCount] Setting task_id [{}] to absolute done_count value of [{}] via increment [{}]", + task_id, + info->activity[activity_id].done_count, + done_count + ); + + IncrementDoneCount( + client, + t, + TASKSLOTSHAREDTASK, + activity_id, + 0, // no op + ignore_quest_update + ); +} + +const ClientTaskInformation &ClientTaskState::GetActiveSharedTask() const +{ + return m_active_shared_task; +} + +bool ClientTaskState::HasActiveSharedTask() +{ + return GetActiveSharedTask().task_id != 0; +} + +void ClientTaskState::CreateTaskDynamicZone(Client* client, int task_id, DynamicZone& dz_request) +{ + auto task = task_manager->m_task_data[task_id]; + if (!task) + { + return; + } + + // dz should be named the version-based zone name (used in choose zone window and dz window on live) + auto zone_info = zone_store.GetZone(dz_request.GetZoneID(), dz_request.GetZoneVersion()); + dz_request.SetName(zone_info.long_name.empty() ? task->title : zone_info.long_name); + dz_request.SetMinPlayers(task->min_players); + dz_request.SetMaxPlayers(task->max_players); + + // a task might create a dz from an objective so override dz duration to time remaining + // live probably creates the dz with the shared task and just adds members for objectives + std::chrono::seconds seconds(TaskTimeLeft(task_id)); + if (task->duration == 0 || seconds.count() < 0) + { + // todo: maybe add a rule for duration + // cap unlimited duration tasks so instance isn't held indefinitely + // expected behavior is to re-acquire any unlimited tasks that have an expired dz + seconds = std::chrono::duration_cast(std::chrono::hours(24)); + } + + dz_request.SetDuration(static_cast(seconds.count())); + + if (task->type == TaskType::Task || task->type == TaskType::Quest) + { + if (task->type == TaskType::Task) { + dz_request.SetType(DynamicZoneType::Task); + } else { + dz_request.SetType(DynamicZoneType::Quest); + } + + // todo: can enable non-shared task dz creation when dz ids are persisted for them (db also) + //DynamicZoneMember solo_member{ client->CharacterID(), client->GetCleanName() }; + //DynamicZone::CreateNew(dz_request, { solo_member }); + } + else if (task->type == TaskType::Shared) + { + dz_request.SetType(DynamicZoneType::Mission); + + // shared task missions are created in world + EQ::Net::DynamicPacket dyn_pack = dz_request.GetSerializedDzPacket(); + + auto pack_size = sizeof(ServerSharedTaskCreateDynamicZone_Struct) + dyn_pack.Length(); + auto pack = std::make_unique(ServerOP_SharedTaskCreateDynamicZone, static_cast(pack_size)); + auto buf = reinterpret_cast(pack->pBuffer); + buf->source_character_id = client->CharacterID(); + buf->task_id = task_id; + buf->cereal_size = static_cast(dyn_pack.Length()); + memcpy(buf->cereal_data, dyn_pack.Data(), dyn_pack.Length()); + + worldserver.SendPacket(pack.get()); + } + +} + +void ClientTaskState::ListTaskTimers(Client* client) +{ + LogTasksDetail("[ListTaskTimers] Client [{}]", client->GetCleanName()); + + // this isn't live-like but we need to throttle query (alternative is caching timers) + if (!client->m_list_task_timers_rate_limit.Check()) { + client->Message(Chat::White, "Sending messages too fast"); + return; + } + + auto character_task_timers = CharacterTaskTimersRepository::GetWhere( + database, fmt::format( + "character_id = {} AND expire_time > NOW()", + client->CharacterID() + ) + ); + + for (const auto& task_timer : character_task_timers) + { + auto task = task_manager->m_task_data[task_timer.task_id]; + if (task) + { + auto timer_type = static_cast(task_timer.timer_type); + auto seconds = task_timer.expire_time - std::time(nullptr); + auto days = fmt::format_int(seconds / 86400).str(); + auto hours = fmt::format_int((seconds / 3600) % 24).str(); + auto mins = fmt::format_int((seconds / 60) % 60).str(); + + if (timer_type == TaskTimerType::Replay) + { + client->MessageString(Chat::Yellow, SharedTaskMessage::REPLAY_TIMER_REMAINING, + task->title.c_str(), days.c_str(), hours.c_str(), mins.c_str()); + } + else + { + auto eqstr = SharedTaskMessage::GetEQStr(SharedTaskMessage::REQUEST_TIMER_REMAINING); + client->Message(Chat::Yellow, fmt::format(eqstr, task->title, days, hours, mins).c_str()); + } + } + } + + if (character_task_timers.empty()) { + client->MessageString(Chat::Yellow, SharedTaskMessage::YOU_NO_CURRENT_REPLAY_TIMERS); + } +} + +void ClientTaskState::AddReplayTimer(Client* client, ClientTaskInformation& client_task, TaskInformation& task) +{ + // world adds timers for shared tasks and handles messages + if (task.type != TaskType::Shared && task.replay_timer_seconds > 0 && client) + { + int expire_time = client_task.accepted_time + task.replay_timer_seconds; + + auto seconds = expire_time - std::time(nullptr); + if (seconds > 0) // not already expired + { + auto timer = CharacterTaskTimersRepository::NewEntity(); + timer.character_id = client->CharacterID(); + timer.task_id = client_task.task_id; + timer.expire_time = expire_time; + timer.timer_type = static_cast(TaskTimerType::Replay); + + CharacterTaskTimersRepository::InsertOne(database, timer); + + client->Message(Chat::Yellow, fmt::format( + SharedTaskMessage::GetEQStr(SharedTaskMessage::RECEIVED_REPLAY_TIMER), + task.title, + fmt::format_int(seconds / 86400).c_str(), // days + fmt::format_int((seconds / 3600) % 24).c_str(), // hours + fmt::format_int((seconds / 60) % 60).c_str() // minutes + ).c_str()); + } + } +} + +// Sync zone client donecount state +// +// World is always authoritative, but we still need to keep zone state in sync with reality and in this case we need +// to update zone since world hasn't had a chance to let clients at the zone level know it's ok to progress to the next +// donecount +// +// If we send updates too fast and world has not had a chance to confirm and then inform clients to set their +// absolute state we will miss and discard updates because each update sets the same donecount +// +// Example: say we have a 100 kill task and we kill 10 mobs in an AOE, world gets 10 updates at once but they are +// all from the same donecount so world only confirms 1 was actually killed and the task updates only 1 which is not +// intended behavior. +// +// In this case we need to ensure that clients at the zone level increment internally even if they aren't +// necessarily confirmed by world yet because any of them could inform world of the next donecount so we need to sync +// zone-level before sending updates to world +void ClientTaskState::SyncSharedTaskZoneClientDoneCountState( + Client *p_client, + TaskInformation *p_information, + int task_index, + int activity_id, + uint32 done_count +) +{ + for (auto &e : entity_list.GetClientList()) { + auto c = e.second; + if (c->GetSharedTaskId() == p_client->GetSharedTaskId()) { + auto t = c->GetTaskState()->GetClientTaskInfo(p_information->type, task_index); + if (t == nullptr) { + continue; + } + + LogTasksDetail( + "[IncrementDoneCount] Setting internally client [{}] to donecount [{}]", + c->GetCleanName(), + done_count + ); + + t->activity[activity_id].done_count = (int) done_count; + } + } +} + +void ClientTaskState::HandleUpdateTasksOnKill(Client *client, uint32 npc_type_id) +{ + if (!HasActiveTasks()) { + return; + } + + // loop over the union of tasks and quests + for (auto &active_task : m_active_tasks) { + auto current_task = &active_task; + if (current_task->task_id == TASKSLOTEMPTY) { + continue; + } + + // Check if there are any active kill activities for this p_task_data + auto p_task_data = task_manager->m_task_data[current_task->task_id]; + if (p_task_data == nullptr) { + return; + } + + for (int activity_id = 0; activity_id < p_task_data->activity_count; activity_id++) { + ClientActivityInformation *client_activity = ¤t_task->activity[activity_id]; + ActivityInformation *activity_info = &p_task_data->activity_information[activity_id]; + + // We are not interested in completed or hidden activities + if (client_activity->activity_state != ActivityActive) { + continue; + } + + // We are only interested in Kill activities + if (activity_info->activity_type != TaskActivityType::Kill) { + continue; + } + + // Is there a zone restriction on the activity_information ? + if (!activity_info->CheckZone(zone->GetZoneID())) { + LogTasks( + "[HandleUpdateTasksOnKill] character [{}] task_id [{}] activity_id [{}] activity_type [{}] for NPC [{}] failed zone check", + client->GetName(), + current_task->task_id, + activity_id, + static_cast(TaskActivityType::Kill), + npc_type_id + ); + continue; + } + // Is the activity_information to kill this type of NPC ? + switch (activity_info->goal_method) { + case METHODSINGLEID: + if (activity_info->goal_id != npc_type_id) { + LogTasksDetail("[HandleUpdateTasksOnKill] Matched single goal"); + continue; + } + break; + + case METHODLIST: + if (!task_manager->m_goal_list_manager.IsInList( + activity_info->goal_id, + (int) npc_type_id + )) { + LogTasksDetail("[HandleUpdateTasksOnKill] Matched list goal"); + continue; + } + break; + + default: + // If METHODQUEST, don't updated the activity_information here + continue; + } + + LogTasksDetail("[HandleUpdateTasksOnKill] passed checks"); + + // handle actual update + // legacy eqemu task update logic loops through group on kill of npc to update a single task + if (p_task_data->type != TaskType::Shared) { + LogTasksDetail("[HandleUpdateTasksOnKill] Non-Shared Update"); + + Raid *raid = entity_list.GetRaidByClient(client); + if (raid) { + for (auto &e : raid->members) { + if (e.member && e.member->IsClient()) { + Client *c = e.member->CastToClient(); + c->UpdateTasksOnKill(npc_type_id); + } + } + return; + } + + Group *group = entity_list.GetGroupByClient(client); + if (group) { + for (auto &m : group->members) { + if (m && m->IsClient()) { + Client *c = m->CastToClient(); + c->UpdateTasksOnKill(npc_type_id); + } + } + return; + } + } + + LogTasksDetail("[HandleUpdateTasksOnKill] Shared update"); + + // shared tasks only require one client to receive an update to propagate + client->UpdateTasksOnKill(npc_type_id); + } + } +} +bool ClientTaskState::HasActiveTasks() +{ + if (!task_manager) { + return false; + } + + if (m_active_task.task_id != TASKSLOTEMPTY) { + return true; + } + + if (m_active_shared_task.task_id != TASKSLOTEMPTY) { + return true; + } + + bool has_active_quest = false; + for (auto &active_quest : m_active_quests) { + if (active_quest.task_id == TASKSLOTEMPTY) { + continue; + } + + return true; + } + + return false; +} diff --git a/zone/task_client_state.h b/zone/task_client_state.h index 6812e99c6..f45a522c9 100644 --- a/zone/task_client_state.h +++ b/zone/task_client_state.h @@ -20,7 +20,7 @@ public: int GetTaskActivityDoneCount(TaskType task_type, int index, int activity_id); int GetTaskActivityDoneCountFromTaskID(int task_id, int activity_id); int GetTaskStartTime(TaskType task_type, int index); - void AcceptNewTask(Client *client, int task_id, int npc_type_id, bool enforce_level_requirement = false); + void AcceptNewTask(Client *client, int task_id, int npc_type_id, time_t accept_time, bool enforce_level_requirement = false); void FailTask(Client *client, int task_id); int TaskTimeLeft(int task_id); int IsTaskCompleted(int task_id); @@ -29,13 +29,13 @@ public: ActivityState GetTaskActivityState(TaskType task_type, int index, int activity_id); void UpdateTaskActivity(Client *client, int task_id, int activity_id, int count, bool ignore_quest_update = false); void ResetTaskActivity(Client *client, int task_id, int activity_id); - void CancelTask(Client *client, int sequence_number, TaskType task_type, bool remove_from_db = true); + void CancelTask(Client *c, int sequence_number, TaskType task_type, bool remove_from_db = true); void CancelAllTasks(Client *client); void RemoveTask(Client *client, int sequence_number, TaskType task_type); void RemoveTaskByTaskID(Client *client, uint32 task_id); - bool UpdateTasksByNPC(Client *client, int activity_type, int npc_type_id); + bool UpdateTasksByNPC(Client *client, TaskActivityType activity_type, int npc_type_id); void UpdateTasksOnKill(Client *client, int npc_type_id); - void UpdateTasksForItem(Client *client, ActivityType activity_type, int item_id, int count = 1); + void UpdateTasksForItem(Client *client, TaskActivityType activity_type, int item_id, int count = 1); void UpdateTasksOnExplore(Client *client, int explore_id); bool UpdateTasksOnSpeakWith(Client *client, int npc_type_id); bool UpdateTasksOnDeliver(Client *client, std::list &items, int cash, int npc_type_id); @@ -54,13 +54,31 @@ public: int ActiveTasksInSet(int task_set_id); int CompletedTasksInSet(int task_set_id); bool HasSlotForTask(TaskInformation *task); + void CreateTaskDynamicZone(Client* client, int task_id, DynamicZone& dz); + void ListTaskTimers(Client* client); + void KickPlayersSharedTask(Client* client); inline bool HasFreeTaskSlot() { return m_active_task.task_id == TASKSLOTEMPTY; } friend class TaskManager; + // wrapper to call internal IncrementDoneCount + void SharedTaskIncrementDoneCount( + Client *client, + int task_id, + int activity_id, + int done_count, + bool ignore_quest_update = false + ); + + const ClientTaskInformation &GetActiveSharedTask() const; + bool HasActiveSharedTask(); + + + void HandleUpdateTasksOnKill(Client *client, uint32 npc_type_id); private: - bool UnlockActivities(int character_id, ClientTaskInformation &task_info); + void AddReplayTimer(Client *client, ClientTaskInformation& client_task, TaskInformation& task); + void IncrementDoneCount( Client *client, TaskInformation *task_information, @@ -70,16 +88,21 @@ private: bool ignore_quest_update = false ); + bool UnlockActivities(int character_id, ClientTaskInformation &task_info); + inline ClientTaskInformation *GetClientTaskInfo(TaskType task_type, int index) { ClientTaskInformation *info = nullptr; switch (task_type) { case TaskType::Task: - if (index == 0) { + if (index == TASKSLOTTASK) { info = &m_active_task; } break; case TaskType::Shared: + if (index == TASKSLOTSHAREDTASK) { + info = &m_active_shared_task; + } break; case TaskType::Quest: if (index < MAXACTIVEQUESTS) { @@ -95,9 +118,13 @@ private: union { // easier to loop over struct { ClientTaskInformation m_active_task; // only one + + // acts as a read-only "view" of data that is managed by world and the internal task + // system largely behaves like other tasks but shims logic to world where necessary + ClientTaskInformation m_active_shared_task; // only one ClientTaskInformation m_active_quests[MAXACTIVEQUESTS]; }; - ClientTaskInformation m_active_tasks[MAXACTIVEQUESTS + 1]; + ClientTaskInformation m_active_tasks[MAXACTIVEQUESTS + 2] = {}; }; // Shared tasks should be limited to 1 as well int m_active_task_count; @@ -105,6 +132,17 @@ private: std::vector m_completed_tasks; int m_last_completed_task_loaded; bool m_checked_touch_activities; + + static void ShowClientTaskInfoMessage(ClientTaskInformation *task, Client *c); + + void SyncSharedTaskZoneClientDoneCountState( + Client *p_client, + TaskInformation *p_information, + int task_index, + int activity_id, + uint32 done_count + ); + bool HasActiveTasks(); }; diff --git a/zone/task_manager.cpp b/zone/task_manager.cpp index b16d95a4e..7616753e8 100644 --- a/zone/task_manager.cpp +++ b/zone/task_manager.cpp @@ -1,6 +1,7 @@ #include "../common/global_define.h" #include "../common/misc_functions.h" #include "../common/repositories/character_activities_repository.h" +#include "../common/repositories/character_data_repository.h" #include "../common/repositories/character_tasks_repository.h" #include "../common/repositories/completed_tasks_repository.h" #include "../common/repositories/task_activities_repository.h" @@ -9,6 +10,12 @@ #include "client.h" #include "string_ids.h" #include "task_manager.h" +#include "../common/repositories/shared_task_activity_state_repository.h" +#include "../common/repositories/shared_task_members_repository.h" +#include "../common/shared_tasks.h" +#include "worldserver.h" + +extern WorldServer worldserver; TaskManager::TaskManager() { @@ -87,30 +94,38 @@ bool TaskManager::LoadTasks(int single_task) } // load task data - m_task_data[task_id] = new TaskInformation; - m_task_data[task_id]->type = static_cast(task.type); - m_task_data[task_id]->duration = task.duration; - m_task_data[task_id]->duration_code = static_cast(task.duration_code); - m_task_data[task_id]->title = task.title; - m_task_data[task_id]->description = task.description; - m_task_data[task_id]->reward = task.reward; - m_task_data[task_id]->reward_id = task.rewardid; - m_task_data[task_id]->cash_reward = task.cashreward; - m_task_data[task_id]->experience_reward = task.xpreward; - m_task_data[task_id]->reward_method = (TaskMethodType) task.rewardmethod; - m_task_data[task_id]->faction_reward = task.faction_reward; - m_task_data[task_id]->min_level = task.minlevel; - m_task_data[task_id]->max_level = task.maxlevel; - m_task_data[task_id]->repeatable = task.repeatable; - m_task_data[task_id]->completion_emote = task.completion_emote; - m_task_data[task_id]->activity_count = 0; - m_task_data[task_id]->sequence_mode = ActivitiesSequential; - m_task_data[task_id]->last_step = 0; + m_task_data[task_id] = new TaskInformation(); + m_task_data[task_id]->type = static_cast(task.type); + m_task_data[task_id]->duration = task.duration; + m_task_data[task_id]->duration_code = static_cast(task.duration_code); + m_task_data[task_id]->title = task.title; + m_task_data[task_id]->description = task.description; + m_task_data[task_id]->reward = task.reward; + m_task_data[task_id]->reward_id = task.rewardid; + m_task_data[task_id]->cash_reward = task.cashreward; + m_task_data[task_id]->experience_reward = task.xpreward; + m_task_data[task_id]->reward_method = (TaskMethodType) task.rewardmethod; + m_task_data[task_id]->reward_radiant_crystals = task.reward_radiant_crystals; + m_task_data[task_id]->reward_ebon_crystals = task.reward_ebon_crystals; + m_task_data[task_id]->faction_reward = task.faction_reward; + m_task_data[task_id]->min_level = task.minlevel; + m_task_data[task_id]->max_level = task.maxlevel; + m_task_data[task_id]->level_spread = task.level_spread; + m_task_data[task_id]->min_players = task.min_players; + m_task_data[task_id]->max_players = task.max_players; + m_task_data[task_id]->repeatable = task.repeatable; + m_task_data[task_id]->completion_emote = task.completion_emote; + m_task_data[task_id]->replay_timer_seconds = task.replay_timer_seconds; + m_task_data[task_id]->request_timer_seconds = task.request_timer_seconds; + m_task_data[task_id]->activity_count = 0; + m_task_data[task_id]->sequence_mode = ActivitiesSequential; + m_task_data[task_id]->last_step = 0; LogTasksDetail( - "[LoadTasks] (Task) task_id [{}] type [{}] duration [{}] duration_code [{}] title [{}] description [{}] " + "[LoadTasks] (Task) task_id [{}] type [{}] () duration [{}] duration_code [{}] title [{}] description [{}] " " reward [{}] rewardid [{}] cashreward [{}] xpreward [{}] rewardmethod [{}] faction_reward [{}] minlevel [{}] " - " maxlevel [{}] repeatable [{}] completion_emote [{}] ", + " maxlevel [{}] level_spread [{}] min_players [{}] max_players [{}] repeatable [{}] completion_emote [{}]", + " replay_timer_seconds [{}] request_timer_seconds [{}]", task.id, task.type, task.duration, @@ -125,8 +140,13 @@ bool TaskManager::LoadTasks(int single_task) task.faction_reward, task.minlevel, task.maxlevel, + task.level_spread, + task.min_players, + task.max_players, task.repeatable, - task.completion_emote + task.completion_emote, + task.replay_timer_seconds, + task.request_timer_seconds ); } @@ -203,7 +223,7 @@ bool TaskManager::LoadTasks(int single_task) } // set activity data - activity_data->activity_type = task_activity.activitytype; + activity_data->activity_type = static_cast(task_activity.activitytype); activity_data->target_name = task_activity.target_name; activity_data->item_list = task_activity.item_list; activity_data->skill_list = task_activity.skill_list; @@ -231,11 +251,11 @@ bool TaskManager::LoadTasks(int single_task) LogTasksDetail( "[LoadTasks] (Activity) task_id [{}] activity_id [{}] slot [{}] activity_type [{}] goal_id [{}] goal_method [{}] goal_count [{}] zones [{}]" - " target_name [{}] item_list [{}] skill_list [{}] spell_list [{}] description_override [{}]", + " target_name [{}] item_list [{}] skill_list [{}] spell_list [{}] description_override [{}] sequence [{}]", task_id, activity_id, m_task_data[task_id]->activity_count, - activity_data->activity_type, + static_cast(activity_data->activity_type), activity_data->goal_id, activity_data->goal_method, activity_data->goal_count, @@ -244,7 +264,8 @@ bool TaskManager::LoadTasks(int single_task) activity_data->item_list.c_str(), activity_data->skill_list.c_str(), activity_data->spell_list.c_str(), - activity_data->description_override.c_str() + activity_data->description_override.c_str(), + (m_task_data[task_id]->sequence_mode == ActivitiesStepped ? "stepped" : "sequential") ); m_task_data[task_id]->activity_count++; @@ -265,14 +286,15 @@ bool TaskManager::SaveClientState(Client *client, ClientTaskState *client_task_s return false; } - const char *ERR_MYSQLERROR = "[TASKS]Error in TaskManager::SaveClientState %s"; + const char *ERR_MYSQLERROR = "[TASKS]Error in TaskManager::SaveClientState {}"; int character_id = client->CharacterID(); LogTasks("[SaveClientState] character_id [{}]", character_id); if (client_task_state->m_active_task_count > 0 || - client_task_state->m_active_task.task_id != TASKSLOTEMPTY) { // TODO: tasks + client_task_state->m_active_task.task_id != TASKSLOTEMPTY || + client_task_state->m_active_shared_task.task_id != TASKSLOTEMPTY) { for (auto &active_task : client_task_state->m_active_tasks) { int task_id = active_task.task_id; if (task_id == TASKSLOTEMPTY) { @@ -361,15 +383,14 @@ bool TaskManager::SaveClientState(Client *client, ClientTaskState *client_task_s } active_task.updated = false; - for (int activity_index = 0; - activity_index < m_task_data[task_id]->activity_count; - ++activity_index) + for (int activity_index = 0; activity_index < m_task_data[task_id]->activity_count; ++activity_index) { active_task.activity[activity_index].updated = false; + } } } - if (!RuleB(TaskSystem, RecordCompletedTasks) || - (client_task_state->m_completed_tasks.size() <= (unsigned int) client_task_state->m_last_completed_task_loaded)) { + if (!RuleB(TaskSystem, RecordCompletedTasks) || (client_task_state->m_completed_tasks.size() <= + (unsigned int) client_task_state->m_last_completed_task_loaded)) { client_task_state->m_last_completed_task_loaded = client_task_state->m_completed_tasks.size(); return true; } @@ -387,6 +408,11 @@ bool TaskManager::SaveClientState(Client *client, ClientTaskState *client_task_s continue; } + // we don't record completed shared tasks in the task quest log + if (m_task_data[task_id]->type == TaskType::Shared) { + break; + } + // First we save a record with an activity_id of -1. // This indicates this task was completed at the given time. We infer that all // none optional activities were completed. @@ -542,14 +568,22 @@ void TaskManager::TaskSetSelector(Client *client, ClientTaskState *client_task_s return; } + // forward to shared task selector validation if set contains a shared task + for (const auto& task_id : m_task_sets[task_set_id]) + { + if (m_task_data[task_id] && m_task_data[task_id]->type == TaskType::Shared) { + SharedTaskSelector(client, mob, m_task_sets[task_set_id].size(), m_task_sets[task_set_id].data()); + return; + } + } + + if (client->HasTaskRequestCooldownTimer()) { + client->SendTaskRequestCooldownTimerMessage(); + return; + } + if (m_task_sets[task_set_id].empty()) { - // I think this is suppose to be yellow - mob->SayString( - client, - Chat::Yellow, - MAX_ACTIVE_TASKS, - client->GetName() - ); + client->MessageString(Chat::Yellow, NO_TASK_OFFERS, ".", ".", client->GetName()); return; } @@ -584,13 +618,7 @@ void TaskManager::TaskSetSelector(Client *client, ClientTaskState *client_task_s SendTaskSelector(client, mob, task_list_index, task_list); } else { - // TODO: check color, I think this might be only for (Shared) Tasks, w/e -- think should be yellow - mob->SayString( - client, - Chat::Yellow, - MAX_ACTIVE_TASKS, - client->GetName() - ); + client->MessageString(Chat::Yellow, NO_TASK_OFFERS, ".", ".", client->GetName()); } } @@ -614,6 +642,22 @@ void TaskManager::TaskQuestSetSelector( return; } + // live prevents mixing selection types (also uses diff opcodes for solo vs shared tasks) + // to keep shared task validation live-like (and simple), any shared task will + // forward this to shared task validation and non-shared tasks will be dropped + for (int i = 0; i < count; ++i) { + auto task = tasks[i]; + if (m_task_data[task] && m_task_data[task]->type == TaskType::Shared) { + SharedTaskSelector(client, mob, count, tasks); + return; + } + } + + if (client->HasTaskRequestCooldownTimer()) { + client->SendTaskRequestCooldownTimerMessage(); + return; + } + for (int i = 0; i < count; ++i) { auto task = tasks[i]; // verify level, we're not currently on it, repeatable status, if it's a (shared) task @@ -630,24 +674,91 @@ void TaskManager::TaskQuestSetSelector( SendTaskSelector(client, mob, task_list_index, task_list); } else { - // TODO: check color, I think this might be only for (Shared) Tasks, w/e -- think should be yellow - mob->SayString( - client, - Chat::Yellow, - MAX_ACTIVE_TASKS, - client->GetName() - ); + client->MessageString(Chat::Yellow, NO_TASK_OFFERS, ".", ".", client->GetName()); + } +} + +void TaskManager::SharedTaskSelector(Client *client, Mob *mob, int count, const int *tasks) +{ + LogTasks("[UPDATE] SharedTaskSelector called for array size [{}]", count); + + if (count <= 0 || client->HasTaskRequestCooldownTimer()) { + client->SendTaskRequestCooldownTimerMessage(); + return; + } + + // check if requester already has a shared task (no need to query group/raid members if so) + if (client->GetTaskState()->HasActiveSharedTask()) { + client->MessageString(Chat::Red, SharedTaskMessage::NO_REQUEST_BECAUSE_HAVE_ONE); + return; + } + + // get group/raid member character data from db (need to query for character ids) + auto request = SharedTask::GetRequestCharacters(database, client->CharacterID()); + + // check if any group/raid member already has a shared task (already checked solo character) + bool validation_failed = false; + if (request.group_type != SharedTaskRequestGroupType::Solo) { + auto shared_task_members = SharedTaskMembersRepository::GetWhere( + database, + fmt::format("character_id IN ({}) LIMIT 1", fmt::join(request.character_ids, ","))); + + if (!shared_task_members.empty()) { + validation_failed = true; + + auto it = std::find_if( + request.characters.begin(), request.characters.end(), + [&](const CharacterDataRepository::CharacterData &char_data) { + return char_data.id == shared_task_members.front().character_id; + } + ); + + if (it != request.characters.end()) { + if (request.group_type == SharedTaskRequestGroupType::Group) { + client->MessageString( + Chat::Red, + SharedTaskMessage::NO_REQUEST_BECAUSE_GROUP_HAS_ONE, + it->name.c_str()); + } + else { + client->MessageString( + Chat::Red, + SharedTaskMessage::NO_REQUEST_BECAUSE_RAID_HAS_ONE, + it->name.c_str()); + } + } + } + } + + if (!validation_failed) { + // run type and level filters on task selections + int task_list[MAXCHOOSERENTRIES] = {0}; + int task_list_index = 0; + + for (int i = 0; i < count && task_list_index < MAXCHOOSERENTRIES; ++i) { + // todo: are there non repeatable shared tasks? (would need to check all group/raid members) + auto task = tasks[i]; + if (m_task_data[task] && + m_task_data[task]->type == TaskType::Shared && + request.lowest_level >= m_task_data[task]->min_level && + (m_task_data[task]->max_level == 0 || request.highest_level <= m_task_data[task]->max_level)) { + task_list[task_list_index++] = task; + } + } + + // check if any tasks are left to offer after filtering + if (task_list_index > 0) { + SendSharedTaskSelector(client, mob, task_list_index, task_list); + } + else { + client->MessageString(Chat::Red, SharedTaskMessage::YOU_DO_NOT_MEET_REQ_AVAILABLE); + } } } // sends task selector to client void TaskManager::SendTaskSelector(Client *client, Mob *mob, int task_count, int *task_list) { - if (client->ClientVersion() >= EQ::versions::ClientVersion::RoF) { - SendTaskSelectorNew(client, mob, task_count, task_list); - return; - } - // Titanium OpCode: 0x5e7c LogTasks("TaskSelector for [{}] Tasks", task_count); int player_level = client->GetLevel(); @@ -658,98 +769,6 @@ void TaskManager::SendTaskSelector(Client *client, Mob *mob, int task_count, int } } - int valid_task_count = 0; - - for (int i = 0; i < task_count; i++) { - if (!ValidateLevel(task_list[i], player_level)) { - continue; - } - if (client->IsTaskActive(task_list[i])) { - continue; - } - if (!IsTaskRepeatable(task_list[i]) && client->IsTaskCompleted(task_list[i])) { - continue; - } - - valid_task_count++; - } - - if (valid_task_count == 0) { - return; - } - - SerializeBuffer buf(50 * valid_task_count); - - buf.WriteUInt32(valid_task_count); - buf.WriteUInt32(2); // task type, live doesn't let you send more than one type, but we do? - buf.WriteUInt32(mob->GetID()); - - for (int task_index = 0; task_index < task_count; task_index++) { - if (!ValidateLevel(task_list[task_index], player_level)) { - continue; - } - if (client->IsTaskActive(task_list[task_index])) { - continue; - } - if (!IsTaskRepeatable(task_list[task_index]) && client->IsTaskCompleted(task_list[task_index])) { - continue; - } - - buf.WriteUInt32(task_list[task_index]); // task_id - - // affects color, difficulty? - if (client->ClientVersion() != EQ::versions::ClientVersion::Titanium) { - buf.WriteFloat(1.0f); - } - buf.WriteUInt32(m_task_data[task_list[task_index]]->duration); - buf.WriteUInt32(static_cast(m_task_data[task_list[task_index]]->duration_code)); - - buf.WriteString(m_task_data[task_list[task_index]]->title); // max 64 with null - buf.WriteString(m_task_data[task_list[task_index]]->description); // max 4000 with null - - // Has reward set flag - if (client->ClientVersion() != EQ::versions::ClientVersion::Titanium) { - buf.WriteUInt8(0); - } - - buf.WriteUInt32(m_task_data[task_list[task_index]]->activity_count); - - for (int activity_index = 0; - activity_index < m_task_data[task_list[task_index]]->activity_count; - ++activity_index) { - buf.WriteUInt32(activity_index); // ActivityNumber - auto &activity = m_task_data[task_list[task_index]]->activity_information[activity_index]; - buf.WriteUInt32(activity.activity_type); - buf.WriteUInt32(0); // solo, group, raid? - buf.WriteString(activity.target_name); // max length 64, "target name" so like loot x foo from bar (this is bar) - buf.WriteString(activity.item_list); // max length 64 in these clients - buf.WriteUInt32(activity.goal_count); - buf.WriteInt32(activity.skill_id); - buf.WriteInt32(activity.spell_id); - buf.WriteInt32(activity.zone_ids.empty() ? 0 : activity.zone_ids.front()); - buf.WriteString(activity.description_override); - } - } - - auto outapp = new EQApplicationPacket(OP_OpenNewTasksWindow, buf); - - client->QueuePacket(outapp); - safe_delete(outapp); -} - -void TaskManager::SendTaskSelectorNew(Client *client, Mob *mob, int task_count, int *task_list) -{ - LogTasks("SendTaskSelectorNew for [{}] Tasks", task_count); - - int player_level = client->GetLevel(); - - // Check if any of the tasks exist - for (int i = 0; i < task_count; i++) { - if (m_task_data[task_list[i]] != nullptr) { - break; - } - } - int valid_tasks_count = 0; for (int task_index = 0; task_index < task_count; task_index++) { if (!ValidateLevel(task_list[task_index], player_level)) { @@ -788,47 +807,36 @@ void TaskManager::SendTaskSelectorNew(Client *client, Mob *mob, int task_count, continue; } - buf.WriteUInt32(task_list[i]); // task_id - buf.WriteFloat(1.0f); // affects color, difficulty? - buf.WriteUInt32(m_task_data[task_list[i]]->duration); - buf.WriteUInt32(static_cast(m_task_data[task_list[i]]->duration_code)); // 1 = Short, 2 = Medium, 3 = Long, anything else Unlimited - - buf.WriteString(m_task_data[task_list[i]]->title); // max 64 with null - buf.WriteString(m_task_data[task_list[i]]->description); // max 4000 with null - - buf.WriteUInt8(0); // Has reward set flag - buf.WriteUInt32(m_task_data[task_list[i]]->activity_count); // activity_count - - for (int j = 0; j < m_task_data[task_list[i]]->activity_count; ++j) { - buf.WriteUInt32(j); // ActivityNumber - auto &activity = m_task_data[task_list[i]]->activity_information[j]; - buf.WriteUInt32(activity.activity_type); // ActivityType - buf.WriteUInt32(0); // solo, group, raid? - buf.WriteString(activity.target_name); // max length 64, "target name" so like loot x foo from bar (this is bar) - - // this string is item names - buf.WriteLengthString(activity.item_list); - - buf.WriteUInt32(activity.goal_count); // GoalCount - - // this string is skill IDs? probably one of the "use on" tasks - buf.WriteLengthString(activity.skill_list); - - // this string is spell IDs? probably one of the "use on" tasks - buf.WriteLengthString(activity.spell_list); - - //buf.WriteString(itoa(Tasks[TaskList[i]]->activity_information[activity_id].ZoneID)); - buf.WriteString(activity.zones); // Zone number in ascii max length 64, can be multiple with separated by ; - buf.WriteString(activity.description_override); // max length 128 -- overrides the automatic descriptions - // this doesn't appear to be shown to the client at all and isn't the same as zones ... defaults to '0' though - buf.WriteString(activity.zones); // Zone number in ascii max length 64, probably can be separated by ; too, haven't found it used - } + buf.WriteUInt32(task_list[i]); // task_id + m_task_data[task_list[i]]->SerializeSelector(buf, client->ClientVersion()); } - auto outapp = new EQApplicationPacket(OP_OpenNewTasksWindow, buf); + auto outapp = std::make_unique(OP_TaskSelectWindow, buf); + client->QueuePacket(outapp.get()); +} - client->QueuePacket(outapp); - safe_delete(outapp); +void TaskManager::SendSharedTaskSelector(Client *client, Mob *mob, int task_count, int *task_list) +{ + LogTasks("SendSharedTaskSelector for [{}] Tasks", task_count); + + // request timer is only set when shared task selection shown (not for failed validations) + client->StartTaskRequestCooldownTimer(); + + SerializeBuffer buf; + + buf.WriteUInt32(task_count); // number of tasks + // shared task selection (live doesn't mix types) makes client send shared task specific opcode for accepts + buf.WriteUInt32(static_cast(TaskType::Shared)); + buf.WriteUInt32(mob->GetID()); // task giver entity id + + for (int i = 0; i < task_count; ++i) { + int task_id = task_list[i]; + buf.WriteUInt32(task_id); + m_task_data[task_id]->SerializeSelector(buf, client->ClientVersion()); + } + + auto outapp = std::make_unique(OP_SharedTaskSelectWindow, buf); + client->QueuePacket(outapp.get()); } int TaskManager::GetActivityCount(int task_id) @@ -869,7 +877,7 @@ void TaskManager::ExplainTask(Client *client, int task_id) sprintf(ptr, "Act: %3i: ", i); ptr = ptr + strlen(ptr); switch (m_task_data[task_id]->activity_information[i].activity_type) { - case ActivityDeliver: + case TaskActivityType::Deliver: sprintf(ptr, "Deliver"); break; } @@ -953,35 +961,15 @@ void TaskManager::SendTaskActivityShort(Client *client, int task_id, int activit { // This activity_information Packet is sent for activities that have not yet been unlocked and appear as ??? // in the client. - - TaskActivityShort_Struct *task_activity_short; - if (client->ClientVersionBit() & EQ::versions::maskRoFAndLater) { - auto outapp = new EQApplicationPacket(OP_TaskActivity, 25); - outapp->WriteUInt32(client_task_index); - outapp->WriteUInt32(static_cast(m_task_data[task_id]->type)); - outapp->WriteUInt32(task_id); - outapp->WriteUInt32(activity_id); - outapp->WriteUInt32(0); - outapp->WriteUInt32(0xffffffff); - outapp->WriteUInt8(0); - client->FastQueuePacket(&outapp); - - return; - } - - auto outapp = new EQApplicationPacket(OP_TaskActivity, sizeof(TaskActivityShort_Struct)); - - task_activity_short = (TaskActivityShort_Struct *) outapp->pBuffer; - task_activity_short->TaskSequenceNumber = client_task_index; - task_activity_short->unknown2 = static_cast(m_task_data[task_id]->type); - task_activity_short->TaskID = task_id; - task_activity_short->ActivityID = activity_id; - task_activity_short->unknown3 = 0x000000; - task_activity_short->ActivityType = 0xffffffff; - task_activity_short->unknown4 = 0x00000000; - - client->QueuePacket(outapp); - safe_delete(outapp); + auto outapp = std::make_unique(OP_TaskActivity, 25); + outapp->WriteUInt32(client_task_index); + outapp->WriteUInt32(static_cast(m_task_data[task_id]->type)); + outapp->WriteUInt32(task_id); + outapp->WriteUInt32(activity_id); + outapp->WriteUInt32(0); + outapp->WriteUInt32(0xffffffff); + outapp->WriteUInt8(m_task_data[task_id]->activity_information[activity_id].optional ? 1 : 0); + client->QueuePacket(outapp.get()); } void TaskManager::SendTaskActivityLong( @@ -989,85 +977,6 @@ void TaskManager::SendTaskActivityLong( int task_id, int activity_id, int client_task_index, - bool optional, - bool task_complete -) -{ - - if (client->ClientVersion() >= EQ::versions::ClientVersion::RoF) { - SendTaskActivityNew(client, task_id, activity_id, client_task_index, optional, task_complete); - return; - } - - SerializeBuffer buf(100); - - buf.WriteUInt32(client_task_index); - buf.WriteUInt32(static_cast(m_task_data[task_id]->type)); - buf.WriteUInt32(task_id); - buf.WriteUInt32(activity_id); - buf.WriteUInt32(0); // unknown3 - - // We send our 'internal' types as ActivityCastOn. text3 should be set to the activity_information description, so it makes - // no difference to the client. All activity_information updates will be done based on our interal activity_information types. - if ((m_task_data[task_id]->activity_information[activity_id].activity_type > 0) && - m_task_data[task_id]->activity_information[activity_id].activity_type < 100) { - buf.WriteUInt32(m_task_data[task_id]->activity_information[activity_id].activity_type); - } - else { - buf.WriteUInt32(ActivityCastOn); - } // w/e! - - buf.WriteUInt32(optional); - buf.WriteUInt32(0); // solo, group, raid - - buf.WriteString(m_task_data[task_id]->activity_information[activity_id].target_name); // target name string - buf.WriteString(m_task_data[task_id]->activity_information[activity_id].item_list); // item name list - - if (m_task_data[task_id]->activity_information[activity_id].activity_type != ActivityGiveCash) { - buf.WriteUInt32(m_task_data[task_id]->activity_information[activity_id].goal_count); - } - else { - // For our internal type GiveCash, where the goal count has the amount of cash that must be given, - // we don't want the donecount and goalcount fields cluttered up with potentially large numbers, so we just - // send a goalcount of 1, and a bit further down, a donecount of 1 if the activity_information is complete, 0 otherwise. - // The text3 field should decribe the exact activity_information goal, e.g. give 3500gp to Hasten Bootstrutter. - buf.WriteUInt32(1); - } - - buf.WriteUInt32(m_task_data[task_id]->activity_information[activity_id].skill_id); - buf.WriteUInt32(m_task_data[task_id]->activity_information[activity_id].spell_id); - buf.WriteUInt32( - m_task_data[task_id]->activity_information[activity_id].zone_ids.empty() ? 0 - : m_task_data[task_id]->activity_information[activity_id].zone_ids.front()); - buf.WriteUInt32(0); - - buf.WriteString(m_task_data[task_id]->activity_information[activity_id].description_override); - - if (m_task_data[task_id]->activity_information[activity_id].activity_type != ActivityGiveCash) { - buf.WriteUInt32(client->GetTaskActivityDoneCount(m_task_data[task_id]->type, client_task_index, activity_id)); - } - else { - // For internal activity_information types, done_count is either 1 if the activity_information is complete, 0 otherwise. - buf.WriteUInt32((client->GetTaskActivityDoneCount(m_task_data[task_id]->type, client_task_index, activity_id) >= - m_task_data[task_id]->activity_information[activity_id].goal_count)); - } - - buf.WriteUInt32(1); // unknown - - auto outapp = new EQApplicationPacket(OP_TaskActivity, buf); - - client->QueuePacket(outapp); - safe_delete(outapp); - -} - -// Used only by RoF+ Clients -void TaskManager::SendTaskActivityNew( - Client *client, - int task_id, - int activity_id, - int client_task_index, - bool optional, bool task_complete ) { @@ -1079,66 +988,89 @@ void TaskManager::SendTaskActivityNew( buf.WriteUInt32(activity_id); buf.WriteUInt32(0); // unknown3 - // We send our 'internal' types as ActivityCastOn. text3 should be set to the activity_information description, so it makes - // no difference to the client. All activity_information updates will be done based on our interal activity_information types. - if ((m_task_data[task_id]->activity_information[activity_id].activity_type > 0) && - m_task_data[task_id]->activity_information[activity_id].activity_type < 100) { - buf.WriteUInt32(m_task_data[task_id]->activity_information[activity_id].activity_type); - } - else { - buf.WriteUInt32(ActivityCastOn); - } // w/e! + const auto &activity = m_task_data[task_id]->activity_information[activity_id]; + int done_count = client->GetTaskActivityDoneCount(m_task_data[task_id]->type, client_task_index, activity_id); - buf.WriteUInt8(optional); - buf.WriteUInt32(0); // solo, group, raid + activity.SerializeObjective(buf, client->ClientVersion(), done_count); - // One of these unknown fields maybe related to the 'Use On' activity_information types - buf.WriteString(m_task_data[task_id]->activity_information[activity_id].target_name); // target name string + auto outapp = std::make_unique(OP_TaskActivity, buf); + client->QueuePacket(outapp.get()); +} - buf.WriteLengthString(m_task_data[task_id]->activity_information[activity_id].item_list); // item name list - - // Goal Count - if (m_task_data[task_id]->activity_information[activity_id].activity_type != ActivityGiveCash) { - buf.WriteUInt32(m_task_data[task_id]->activity_information[activity_id].goal_count); - } - else { - buf.WriteUInt32(1); - } // GoalCount - - // skill ID list ; separated - buf.WriteLengthString(m_task_data[task_id]->activity_information[activity_id].skill_list); - - // spelll ID list ; separated -- unsure wtf we're doing here - buf.WriteLengthString(m_task_data[task_id]->activity_information[activity_id].spell_list); - - buf.WriteString(m_task_data[task_id]->activity_information[activity_id].zones); - buf.WriteUInt32(0); // unknown7 - - buf.WriteString(m_task_data[task_id]->activity_information[activity_id].description_override); // description override - - if (m_task_data[task_id]->activity_information[activity_id].activity_type != ActivityGiveCash) { - buf.WriteUInt32( - client->GetTaskActivityDoneCount( - m_task_data[task_id]->type, - client_task_index, - activity_id - )); // done_count - } - else { - // For internal activity_information types, done_count is either 1 if the activity_information is complete, 0 otherwise. - buf.WriteUInt32((client->GetTaskActivityDoneCount(m_task_data[task_id]->type, client_task_index, activity_id) >= - m_task_data[task_id]->activity_information[activity_id].goal_count)); +void TaskManager::SendActiveTaskToClient( + ClientTaskInformation *task, + Client *client, + int task_index, + bool task_complete +) +{ + auto state = client->GetTaskState(); + if (!state) { + return; } - buf.WriteUInt8(1); // unknown9 + int start_time = task->accepted_time; + int task_id = task->task_id; + auto task_type = m_task_data[task_id]->type; + auto task_duration = m_task_data[task_id]->duration; - buf.WriteString(m_task_data[task_id]->activity_information[activity_id].zones); + SendActiveTaskDescription( + client, + task_id, + *task, + start_time, + task_duration, + false + ); - auto outapp = new EQApplicationPacket(OP_TaskActivity, buf); + LogTasks("[SendActiveTasksToClient] task_id [{}] activity_count [{}] task_index [{}]", + task_id, + GetActivityCount(task_id), + task_index); - client->QueuePacket(outapp); - safe_delete(outapp); + int sequence = 0; + int fixed_index = task_index; + for (int activity_id = 0; activity_id < GetActivityCount(task_id); activity_id++) { + if (client->GetTaskActivityState(task_type, fixed_index, activity_id) != ActivityHidden) { + LogTasks( + "[SendActiveTasksToClient] (Long Update) task_id [{}] activity_id [{}] fixed_index [{}] task_complete [{}]", + task_id, + activity_id, + fixed_index, + task_complete ? "true" : "false" + ); + if (activity_id == GetActivityCount(task_id) - 1) { + SendTaskActivityLong( + client, + task_id, + activity_id, + fixed_index, + task_complete + ); + } + else { + SendTaskActivityLong( + client, + task_id, + activity_id, + fixed_index, + 0 + ); + } + } + else { + LogTasks( + "[SendActiveTasksToClient] (Short Update) task_id [{}] activity_id [{}] fixed_index [{}]", + task_id, + activity_id, + fixed_index + ); + + SendTaskActivityShort(client, task_id, activity_id, fixed_index); + } + sequence++; + } } void TaskManager::SendActiveTasksToClient(Client *client, bool task_complete) @@ -1148,64 +1080,27 @@ void TaskManager::SendActiveTasksToClient(Client *client, bool task_complete) return; } - for (int task_index = 0; task_index < MAXACTIVEQUESTS + 1; task_index++) { - int task_id = state->m_active_tasks[task_index].task_id; - if ((task_id == 0) || (m_task_data[task_id] == 0)) { + // task + if (state->m_active_task.task_id != TASKSLOTEMPTY) { + SendActiveTaskToClient(&state->m_active_task, client, 0, task_complete); + } + + // shared task + if (state->m_active_shared_task.task_id != TASKSLOTEMPTY) { + SendActiveTaskToClient(&state->m_active_shared_task, client, 0, task_complete); + } + + // quests + for (int task_index = 0; task_index < MAXACTIVEQUESTS; task_index++) { + int task_id = state->m_active_quests[task_index].task_id; + if ((task_id == 0) || (m_task_data[task_id] == nullptr)) { continue; } - int start_time = state->m_active_tasks[task_index].accepted_time; - SendActiveTaskDescription( - client, task_id, state->m_active_tasks[task_index], start_time, m_task_data[task_id]->duration, - false - ); - LogTasks("[SendActiveTasksToClient] task_id [{}] activity_count [{}]", task_id, GetActivityCount(task_id)); + LogTasksDetail("--"); + LogTasksDetail("[SendActiveTasksToClient] Task [{}]", m_task_data[task_id]->title); - int sequence = 0; - int fixed_index = m_task_data[task_id]->type == TaskType::Task ? 0 : task_index - 1; // hmmm fuck - for (int activity_id = 0; activity_id < GetActivityCount(task_id); activity_id++) { - if (client->GetTaskActivityState(m_task_data[task_id]->type, fixed_index, activity_id) != ActivityHidden) { - LogTasks( - "[SendActiveTasksToClient] (Long Update) task_id [{}] activity_id [{}] fixed_index [{}] task_complete [{}]", - task_id, - activity_id, - fixed_index, - task_complete ? "true" : "false" - ); - - if (activity_id == GetActivityCount(task_id) - 1) { - SendTaskActivityLong( - client, - task_id, - activity_id, - fixed_index, - m_task_data[task_id]->activity_information[activity_id].optional, - task_complete - ); - } - else { - SendTaskActivityLong( - client, - task_id, - activity_id, - fixed_index, - m_task_data[task_id]->activity_information[activity_id].optional, - 0 - ); - } - } - else { - LogTasks( - "[SendActiveTasksToClient] (Short Update) task_id [{}] activity_id [{}] fixed_index [{}]", - task_id, - activity_id, - fixed_index - ); - - SendTaskActivityShort(client, task_id, activity_id, fixed_index); - } - sequence++; - } + SendActiveTaskToClient(&state->m_active_quests[task_index], client, task_index, task_complete); } } @@ -1241,16 +1136,10 @@ void TaskManager::SendSingleActiveTaskToClient( activity_id, task_complete); if (activity_id == GetActivityCount(task_id) - 1) { - SendTaskActivityLong( - client, task_id, activity_id, task_info.slot, - m_task_data[task_id]->activity_information[activity_id].optional, task_complete - ); + SendTaskActivityLong(client, task_id, activity_id, task_info.slot, task_complete); } else { - SendTaskActivityLong( - client, task_id, activity_id, task_info.slot, - m_task_data[task_id]->activity_information[activity_id].optional, 0 - ); + SendTaskActivityLong(client, task_id, activity_id, task_info.slot, 0); } } else { @@ -1320,7 +1209,9 @@ void TaskManager::SendActiveTaskDescription( task_description_header->TaskID = task_id; task_description_header->open_window = bring_up_task_journal; task_description_header->task_type = static_cast(m_task_data[task_id]->type); - task_description_header->reward_type = 0; // TODO: 4 says Radiant Crystals else Ebon Crystals when shared task + + constexpr uint32_t reward_radiant_type = 4; // Radiant Crystals, anything else is Ebon for shared tasks + task_description_header->reward_type = m_task_data[task_id]->reward_radiant_crystals > 0 ? reward_radiant_type : 0; Ptr = (char *) task_description_header + sizeof(TaskDescriptionHeader_Struct); @@ -1362,7 +1253,11 @@ void TaskManager::SendActiveTaskDescription( Ptr += m_task_data[task_id]->item_link.length() + 1; tdt = (TaskDescriptionTrailer_Struct *) Ptr; - tdt->Points = 0x00000000; // Points Count TODO: this does have a visible affect on the client ... + // shared tasks show radiant/ebon crystal reward, non-shared tasks show generic points + tdt->Points = m_task_data[task_id]->reward_ebon_crystals; + if (m_task_data[task_id]->reward_radiant_crystals > 0) { + tdt->Points = m_task_data[task_id]->reward_radiant_crystals; + } tdt->has_reward_selection = 0; // TODO: new rewards window client->QueuePacket(outapp); @@ -1375,21 +1270,30 @@ bool TaskManager::LoadClientState(Client *client, ClientTaskState *client_task_s return false; } + client->SetSharedTaskId(0); + int character_id = client->CharacterID(); client_task_state->m_active_task_count = 0; LogTasks("[LoadClientState] for character_id [{}]", character_id); + // in a case where a client somehow lost local state with what state exists in world - we need + // to perform an inverse sync where we inject the task + SyncClientSharedTaskStateToLocal(client); + auto character_tasks = CharacterTasksRepository::GetWhere( database, fmt::format("charid = {} ORDER BY acceptedtime", character_id) ); for (auto &character_task: character_tasks) { - int task_id = character_task.taskid; - int slot = character_task.slot; - auto type = static_cast(character_task.type); + int task_id = character_task.taskid; + int slot = character_task.slot; + + // this used to be loaded from character_tasks + // this should just load from the tasks table + auto type = task_manager->GetTaskType(character_task.taskid); if ((task_id < 0) || (task_id >= MAXTASKS)) { LogTasks( @@ -1400,6 +1304,8 @@ bool TaskManager::LoadClientState(Client *client, ClientTaskState *client_task_s } // client data bucket pointer + // this actually fetches the proper task type instances to be loaded with data + // whether it be quest / task / shared task auto task_info = client_task_state->GetClientTaskInfo(type, slot); if (task_info == nullptr) { LogTasks( @@ -1423,14 +1329,16 @@ bool TaskManager::LoadClientState(Client *client, ClientTaskState *client_task_s i.activity_id = -1; } + // this check keeps a lot of core task updating code from working properly (shared or otherwise) if (type == TaskType::Quest) { ++client_task_state->m_active_task_count; } LogTasks( - "[LoadClientState] character_id [{}] task_id [{}] accepted_time [{}]", + "[LoadClientState] character_id [{}] task_id [{}] slot [{}] accepted_time [{}]", character_id, task_id, + slot, character_task.acceptedtime ); } @@ -1465,12 +1373,18 @@ bool TaskManager::LoadClientState(Client *client, ClientTaskState *client_task_s continue; } + // type: task ClientTaskInformation *task_info = nullptr; if (client_task_state->m_active_task.task_id == task_id) { task_info = &client_task_state->m_active_task; } - // wasn't task + // type: shared task + if (client_task_state->m_active_shared_task.task_id == task_id) { + task_info = &client_task_state->m_active_shared_task; + } + + // type: quest if (task_info == nullptr) { for (auto &active_quest : client_task_state->m_active_quests) { if (active_quest.task_id == task_id) { @@ -1511,6 +1425,8 @@ bool TaskManager::LoadClientState(Client *client, ClientTaskState *client_task_s ); } + SyncClientSharedTaskState(client, client_task_state); + if (RuleB(TaskSystem, RecordCompletedTasks)) { CompletedTaskInformation completed_task_information{}; @@ -1639,18 +1555,262 @@ bool TaskManager::LoadClientState(Client *client, ClientTaskState *client_task_s } } + LogTasksDetail( + "[LoadClientState] m_active_task task_id is [{}] slot [{}]", + client_task_state->m_active_task.task_id, + client_task_state->m_active_task.slot + ); if (client_task_state->m_active_task.task_id != TASKSLOTEMPTY) { client_task_state->UnlockActivities(character_id, client_task_state->m_active_task); + + // purely debugging + LogTasksDetail( + "[LoadClientState] Fetching task info for character_id [{}] task [{}] slot [{}] current_step [{}] accepted_time [{}] updated [{}]", + character_id, + client_task_state->m_active_task.task_id, + client_task_state->m_active_task.slot, + client_task_state->m_active_task.current_step, + client_task_state->m_active_task.accepted_time, + client_task_state->m_active_task.updated + ); + + TaskInformation *p_task_data = task_manager->m_task_data[client_task_state->m_active_task.task_id]; + if (p_task_data != nullptr) { + for (int i = 0; i < p_task_data->activity_count; i++) { + if (client_task_state->m_active_task.activity[i].activity_id >= 0) { + LogTasksDetail( + "[LoadClientState] -- character_id [{}] task [{}] activity_id [{}] done_count [{}] activity_state [{}] updated [{}] sequence [{}]", + character_id, + client_task_state->m_active_task.task_id, + client_task_state->m_active_task.activity[i].activity_id, + client_task_state->m_active_task.activity[i].done_count, + client_task_state->m_active_task.activity[i].activity_state, + client_task_state->m_active_task.activity[i].updated, + p_task_data->sequence_mode + ); + } + } + } } - // TODO: shared + // shared task + LogTasksDetail( + "[LoadClientState] m_active_shared_task task_id is [{}] slot [{}]", + client_task_state->m_active_shared_task.task_id, + client_task_state->m_active_shared_task.slot + ); + if (client_task_state->m_active_shared_task.task_id != TASKSLOTEMPTY) { + client_task_state->UnlockActivities(character_id, client_task_state->m_active_shared_task); + } + + // quests (max 20 or 40 depending on client) for (auto &active_quest : client_task_state->m_active_quests) { if (active_quest.task_id != TASKSLOTEMPTY) { client_task_state->UnlockActivities(character_id, active_quest); } } - LogTasks("[LoadClientState] for Character ID [{}] DONE!", character_id); + LogTasksDetail("[LoadClientState] for Character ID [{}] DONE!", character_id); + LogTasksDetail("---", character_id); return true; } + +void TaskManager::SyncClientSharedTaskState(Client *c, ClientTaskState *cts) +{ + LogTasksDetail( + "[SyncClientSharedTaskState] Syncing client shared task state" + ); + + SyncClientSharedTaskWithPersistedState(c, cts); + SyncClientSharedTaskRemoveLocalIfNotExists(c, cts); +} + +void TaskManager::SyncClientSharedTaskWithPersistedState(Client *c, ClientTaskState *cts) +{ + auto character_tasks = CharacterTasksRepository::GetWhere( + database, + fmt::format("charid = {} ORDER BY acceptedtime", c->CharacterID()) + ); + + for (auto &character_task: character_tasks) { + if (character_task.type == TASK_TYPE_SHARED) { + auto st = SharedTaskMembersRepository::GetWhere( + database, + fmt::format( + "character_id = {}", + c->CharacterID() + ) + ); + + if (!st.empty()) { + int64 shared_task_id = st[0].shared_task_id; + auto activities = SharedTaskActivityStateRepository::GetWhere( + database, + fmt::format( + "shared_task_id = {}", + shared_task_id + ) + ); + + ClientTaskInformation *shared_task = nullptr; + shared_task = &cts->m_active_shared_task; + + // has active shared task + if (cts->HasActiveSharedTask()) { + + LogTasksDetail( + "[SyncClientSharedTaskWithPersistedState] Client [{}] has shared_task, sync with database", + c->GetCleanName() + ); + + bool fell_behind_state = false; + for (auto &a: activities) { + + LogTasksDetail( + "[LoadClientState] shared_task loop local [{}] shared [{}]", + shared_task->activity[a.activity_id].done_count, + a.done_count + ); + + // we're behind shared task state, update self + if (shared_task->activity[a.activity_id].done_count < a.done_count) { + + // update done count + shared_task->activity[a.activity_id].done_count = a.done_count; + + // activity state + shared_task->activity[a.activity_id].activity_state = + (a.completed_time > 0 ? ActivityCompleted : ActivityHidden); + + // set flag to persist later + fell_behind_state = true; + } + } + + // fell behind, force a save of client state + if (fell_behind_state) { + SaveClientState(c, cts); + } + + c->SetSharedTaskId(shared_task_id); + } + } + } + } +} + +void TaskManager::SyncClientSharedTaskRemoveLocalIfNotExists(Client *c, ClientTaskState *cts) +{ + // has active shared task + if (cts->HasActiveSharedTask()) { + auto members = SharedTaskMembersRepository::GetWhere( + database, + fmt::format( + "character_id = {}", + c->CharacterID() + ) + ); + + // if we don't actually have a membership anywhere, remove ourself locally + if (members.empty()) { + LogTasksDetail( + "[SyncClientSharedTaskRemoveLocalIfNotExists] Client [{}] Shared task [{}] doesn't exist in world, removing from local", + c->GetCleanName(), + cts->m_active_shared_task.task_id + ); + + std::string delete_where = fmt::format( + "charid = {} and taskid = {}", + c->CharacterID(), + cts->m_active_shared_task.task_id + ); + CharacterTasksRepository::DeleteWhere(database, delete_where); + CharacterActivitiesRepository::DeleteWhere(database, delete_where); + + c->MessageString(Chat::Yellow, SharedTaskMessage::YOU_ARE_NO_LONGER_A_MEMBER, + m_task_data[cts->m_active_shared_task.task_id]->title.c_str()); + + // remove as active task if doesn't exist + cts->m_active_shared_task = {}; + + // persist removal from local record + SaveClientState(c, cts); + } + } +} + +// in a case where a client somehow lost local state with what state exists in world - we need +// to perform an inverse sync where we inject the task +void TaskManager::SyncClientSharedTaskStateToLocal( + Client *c +) +{ + auto character_tasks = CharacterTasksRepository::GetWhere( + database, + fmt::format("charid = {} ORDER BY acceptedtime", c->CharacterID()) + ); + + bool has_character_shared_task = false; + + for (auto &character_task: character_tasks) { + if (character_task.type == TASK_TYPE_SHARED) { + has_character_shared_task = true; + } + } + + if (!has_character_shared_task) { + LogTasksDetail("[SyncClientSharedTaskStateToLocal] We don't have a shared character task locally"); + auto stm = SharedTaskMembersRepository::GetWhere( + database, + fmt::format( + "character_id = {}", + c->CharacterID() + ) + ); + + if (!stm.empty()) { + LogTasksDetail("[SyncClientSharedTaskStateToLocal] We have membership in database"); + auto s = SharedTasksRepository::FindOne( + database, + (int) stm.front().shared_task_id + ); + + if (s.id > 0) { + LogTasksDetail("[SyncClientSharedTaskStateToLocal] Creating entity"); + + // create task locally + auto ct = CharacterTasksRepository::NewEntity(); + ct.charid = (int) c->CharacterID(); + ct.acceptedtime = (int) s.accepted_time; + ct.taskid = (int) s.task_id; + ct.slot = 0; + ct.type = TASK_TYPE_SHARED; + character_tasks.emplace_back(ct); + CharacterTasksRepository::InsertOne(database, ct); + + // create activities locally + auto activities = SharedTaskActivityStateRepository::GetWhere( + database, + fmt::format( + "shared_task_id = {}", + (int) stm.front().shared_task_id + ) + ); + + std::vector character_activities = {}; + + for (auto &a: activities) { + auto ca = CharacterActivitiesRepository::NewEntity(); + ca.completed = a.completed_time > 0; + ca.charid = (int) c->CharacterID(); + ca.donecount = a.done_count; + ca.taskid = s.task_id; + ca.activityid = a.activity_id; + character_activities.emplace_back(ca); + } + CharacterActivitiesRepository::InsertMany(database, character_activities); + } + } + } +} diff --git a/zone/task_manager.h b/zone/task_manager.h index f1fe41bd9..0914fe888 100644 --- a/zone/task_manager.h +++ b/zone/task_manager.h @@ -6,6 +6,7 @@ #include "task_proximity_manager.h" #include "task_goal_list_manager.h" #include "../common/types.h" +#include "../common/repositories/character_tasks_repository.h" #include #include #include @@ -31,7 +32,6 @@ public: bool LoadClientState(Client *client, ClientTaskState *client_task_state); bool SaveClientState(Client *client, ClientTaskState *client_task_state); void SendTaskSelector(Client *client, Mob *mob, int task_count, int *task_list); - void SendTaskSelectorNew(Client *client, Mob *mob, int task_count, int *task_list); bool ValidateLevel(int task_id, int player_level); std::string GetTaskName(uint32 task_id); TaskType GetTaskType(uint32 task_id); @@ -44,6 +44,7 @@ public: int count, int *tasks ); + void SharedTaskSelector(Client* client, Mob* mob, int count, const int* tasks); void SendActiveTasksToClient(Client *client, bool task_complete = false); void SendSingleActiveTaskToClient( Client *client, @@ -57,15 +58,6 @@ public: int task_id, int activity_id, int client_task_index, - bool optional, - bool task_complete = false - ); - void SendTaskActivityNew( - Client *client, - int task_id, - int activity_id, - int client_task_index, - bool optional, bool task_complete = false ); void SendCompletedTasksToClient(Client *c, ClientTaskState *client_task_state); @@ -77,6 +69,8 @@ public: friend class ClientTaskState; + // shared tasks + void SyncClientSharedTaskState(Client *c, ClientTaskState *cts); private: TaskGoalListManager m_goal_list_manager; @@ -92,6 +86,13 @@ private: bool bring_up_task_journal = false ); + void SendActiveTaskToClient(ClientTaskInformation *task, Client *client, int task_index, bool task_complete); + + // shared tasks + void SyncClientSharedTaskWithPersistedState(Client *c, ClientTaskState *cts); + void SyncClientSharedTaskRemoveLocalIfNotExists(Client *c, ClientTaskState *cts); + void SendSharedTaskSelector(Client* client, Mob* mob, int task_count, int* task_list); + void SyncClientSharedTaskStateToLocal(Client *c); }; diff --git a/zone/tasks.cpp b/zone/tasks.cpp index 4f240bab5..be8a6ca58 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -5,8 +5,10 @@ #include "client.h" #include "queryserv.h" #include "quest_parser_collection.h" +#include "string_ids.h" #include "tasks.h" #include "zonedb.h" +#include "../common/repositories/character_task_timers_repository.h" extern QueryServ *QServ; @@ -17,7 +19,7 @@ void Client::LoadClientTaskState() safe_delete(task_state); } - task_state = new ClientTaskState; + task_state = new ClientTaskState(); if (!task_manager->LoadClientState(this, task_state)) { safe_delete(task_state); } @@ -118,6 +120,42 @@ void Client::SendTaskFailed(int task_id, int task_index, TaskType task_type) safe_delete(outapp); } +bool Client::HasTaskRequestCooldownTimer() +{ + if (task_request_timer.Check(false)) + { + task_request_timer.Disable(); + } + return (!GetGM() && task_request_timer.Enabled()); +} +void Client::SendTaskRequestCooldownTimerMessage() +{ + if (HasTaskRequestCooldownTimer()) + { + uint32_t seconds = task_request_timer.GetRemainingTime() / 1000; + MessageString(Chat::Yellow, TASK_REQUEST_COOLDOWN_TIMER, + ".", ".", // args start at %3 for this eqstr + GetName(), + fmt::format_int(seconds / 60).c_str(), // minutes + fmt::format_int(seconds % 60).c_str() // seconds + ); + } +} +void Client::StartTaskRequestCooldownTimer() +{ + uint32_t milliseconds = RuleI(TaskSystem, RequestCooldownTimerSeconds) * 1000; + task_request_timer.Start(milliseconds); + + uint32_t size = sizeof(uint32_t); + auto outapp = std::make_unique(OP_TaskRequestTimer, size); + outapp->WriteUInt32(milliseconds); + QueuePacket(outapp.get()); +} + +void Client::PurgeTaskTimers() +{ + CharacterTaskTimersRepository::DeleteWhere(database, fmt::format("character_id = {}", CharacterID())); +} diff --git a/zone/tasks.h b/zone/tasks.h index d3fed2c09..56be4fa8d 100644 --- a/zone/tasks.h +++ b/zone/tasks.h @@ -2,24 +2,12 @@ #define TASKS_H #include "../common/types.h" +#include "../common/tasks.h" #include #include #include #include -#define MAXTASKS 10000 -#define MAXTASKSETS 1000 -#define MAXACTIVEQUESTS 19 // The Client has a hard cap of 19 active quests, 29 in SoD+ -#define MAXCHOOSERENTRIES 40 // The Max Chooser (Task Selector entries) is capped at 40 in the Titanium Client. -#define MAXACTIVITIESPERTASK 20 // The Client has a hard cap of 20 activities per task. -#define TASKSLOTEMPTY 0 // This is used to determine if a client's active task slot is empty. - -// Command Codes for worldserver ServerOP_ReloadTasks -#define RELOADTASKS 0 -#define RELOADTASKGOALLISTS 1 -#define RELOADTASKPROXIMITIES 2 -#define RELOADTASKSETS 3 - class Client; class Mob; @@ -27,123 +15,4 @@ namespace EQ { class ItemInstance; } -typedef enum { - METHODSINGLEID = 0, - METHODLIST = 1, - METHODQUEST = 2 -} TaskMethodType; - -struct ActivityInformation { - int step_number; - int activity_type; - std::string target_name; // name mob, location -- default empty - std::string item_list; // likely defaults to empty - std::string skill_list; // IDs ; separated -- default -1 - std::string spell_list; // IDs ; separated -- default 0 - std::string description_override; // overrides auto generated description -- default empty - int skill_id; // older clients, first id from above - int spell_id; // older clients, first id from above - int goal_id; - TaskMethodType goal_method; - int goal_count; - int deliver_to_npc; - std::vector zone_ids; - std::string zones; // IDs ; searated, ZoneID is the first in this list for older clients -- default empty string - bool optional; - - inline bool CheckZone(int zone_id) - { - if (zone_ids.empty()) { - return true; - } - return std::find(zone_ids.begin(), zone_ids.end(), zone_id) != zone_ids.end(); - } -}; - -typedef enum { - ActivitiesSequential = 0, - ActivitiesStepped = 1 -} SequenceType; - -enum class TaskType { - Task = 0, // can have at max 1 - Shared = 1, // can have at max 1 - Quest = 2, // can have at max 19 or 29 depending on client - E = 3 // can have at max 19 or 29 depending on client, not present in live anymore -}; - -enum class DurationCode { - None = 0, - Short = 1, - Medium = 2, - Long = 3 -}; - -struct TaskInformation { - TaskType type; - int duration; - DurationCode duration_code; // description for time investment for when duration == 0 - std::string title; // max length 64 - std::string description; // max length 4000, 2048 on Tit - std::string reward; - std::string item_link; // max length 128 older clients, item link gets own string - std::string completion_emote; // emote after completing task, yellow. Maybe should make more generic ... but yellow for now! - int reward_id; - int cash_reward; // Expressed in copper - int experience_reward; - int faction_reward; // just a npc_faction_id - TaskMethodType reward_method; - int activity_count; - SequenceType sequence_mode; - int last_step; - short min_level; - short max_level; - bool repeatable; - ActivityInformation activity_information[MAXACTIVITIESPERTASK]; -}; - -typedef enum { - ActivityHidden = 0, - ActivityActive = 1, - ActivityCompleted = 2 -} ActivityState; - -typedef enum { - ActivityDeliver = 1, - ActivityKill = 2, - ActivityLoot = 3, - ActivitySpeakWith = 4, - ActivityExplore = 5, - ActivityTradeSkill = 6, - ActivityFish = 7, - ActivityForage = 8, - ActivityCastOn = 9, - ActivitySkillOn = 10, - ActivityTouch = 11, - ActivityCollect = 13, - ActivityGiveCash = 100 -} ActivityType; - -struct ClientActivityInformation { - int activity_id; - int done_count; - ActivityState activity_state; - bool updated; // Flag so we know if we need to updated the database -}; - -struct ClientTaskInformation { - int slot; // intrusive, but makes things easier :P - int task_id; - int current_step; - int accepted_time; - bool updated; - ClientActivityInformation activity[MAXACTIVITIESPERTASK]; -}; - -struct CompletedTaskInformation { - int task_id; - int completed_time; - bool activity_done[MAXACTIVITIESPERTASK]; -}; - #endif diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index 52423b368..4beaedf52 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -1093,7 +1093,7 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { } if (RuleB(TaskSystem, EnableTaskSystem)) { - UpdateTasksForItem(ActivityTradeSkill, itr->first, itr->second); + UpdateTasksForItem(TaskActivityType::TradeSkill, itr->first, itr->second); } ++itr; diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index ce1cf892c..fb4d0d4bb 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -54,6 +54,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "zone.h" #include "zone_config.h" #include "zone_reload.h" +#include "../common/shared_tasks.h" +#include "shared_task_zone_messaging.h" extern EntityList entity_list; extern Zone* zone; @@ -3004,38 +3006,50 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) break; } case ServerOP_ExpeditionCreate: - case ServerOP_ExpeditionDeleted: - case ServerOP_ExpeditionLeaderChanged: case ServerOP_ExpeditionLockout: case ServerOP_ExpeditionLockoutDuration: case ServerOP_ExpeditionLockState: - case ServerOP_ExpeditionMemberChange: - case ServerOP_ExpeditionMemberSwap: - case ServerOP_ExpeditionMemberStatus: - case ServerOP_ExpeditionMembersRemoved: case ServerOP_ExpeditionReplayOnJoin: - case ServerOP_ExpeditionGetMemberStatuses: case ServerOP_ExpeditionDzAddPlayer: case ServerOP_ExpeditionDzMakeLeader: case ServerOP_ExpeditionCharacterLockout: - case ServerOP_ExpeditionExpireWarning: { Expedition::HandleWorldMessage(pack); break; } - case ServerOP_DzAddRemoveCharacter: - case ServerOP_DzRemoveAllCharacters: + case ServerOP_DzCreated: + case ServerOP_DzDeleted: + case ServerOP_DzAddRemoveMember: + case ServerOP_DzSwapMembers: + case ServerOP_DzRemoveAllMembers: case ServerOP_DzDurationUpdate: + case ServerOP_DzGetMemberStatuses: case ServerOP_DzSetCompass: case ServerOP_DzSetSafeReturn: case ServerOP_DzSetZoneIn: + case ServerOP_DzUpdateMemberStatus: + case ServerOP_DzLeaderChanged: + case ServerOP_DzExpireWarning: { DynamicZone::HandleWorldMessage(pack); break; } + case ServerOP_SharedTaskAcceptNewTask: + case ServerOP_SharedTaskUpdate: + case ServerOP_SharedTaskAttemptRemove: + case ServerOP_SharedTaskMemberlist: + case ServerOP_SharedTaskMemberChange: + case ServerOP_SharedTaskInvitePlayer: + case ServerOP_SharedTaskPurgeAllCommand: + { + SharedTaskZoneMessaging::HandleWorldMessage(pack); + break; + } default: { - std::cout << " Unknown ZSopcode:" << (int)pack->opcode; - std::cout << " size:" << pack->size << std::endl; + LogInfo("[HandleMessage] Unknown ZS Opcode [{}] size [{}]", (int)pack->opcode, pack->size); + +// std::cout << " Unknown ZSopcode:" << (int)pack->opcode; +// std::cout << " size:" << pack->size << std::endl; break; } } @@ -3200,13 +3214,16 @@ void WorldServer::HandleReloadTasks(ServerPacket *pack) case RELOADTASKS: entity_list.SaveAllClientsTaskState(); + // TODO: Reload at the world level for shared tasks + if (rts->Parameter == 0) { Log(Logs::General, Logs::Tasks, "[GLOBALLOAD] Reload ALL tasks"); safe_delete(task_manager); task_manager = new TaskManager; task_manager->LoadTasks(); - if (zone) + if (zone) { task_manager->LoadProximities(zone->GetZoneID()); + } entity_list.ReloadAllClientsTaskState(); } else { @@ -3428,3 +3445,4 @@ void WorldServer::SetScheduler(ZoneEventScheduler *scheduler) { WorldServer::m_zone_scheduler = scheduler; } + diff --git a/zone/zone.cpp b/zone/zone.cpp index aae61ad23..709803aab 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -1184,6 +1184,9 @@ bool Zone::Init(bool iStaticZone) { petition_list.ClearPetitions(); petition_list.ReadDatabase(); + LogInfo("Loading dynamic zones"); + DynamicZone::CacheAllFromDatabase(); + LogInfo("Loading active Expeditions"); Expedition::CacheAllFromDatabase(); @@ -1491,16 +1494,14 @@ bool Zone::Process() { { if(Instance_Timer->Check()) { - // if this is a dynamic zone instance notify system associated with it - auto expedition = Expedition::FindCachedExpeditionByZoneInstance(GetZoneID(), GetInstanceID()); - if (expedition) + auto dz = GetDynamicZone(); + if (dz) { - expedition->RemoveAllMembers(false); // entity list will teleport clients out immediately + dz->RemoveAllMembers(); // entity list will teleport clients out immediately } // instance shutting down, move corpses to graveyard or non-instanced zone at same coords entity_list.MovePlayerCorpsesToGraveyard(true); - entity_list.GateAllClientsToSafeReturn(); database.DeleteInstance(GetInstanceID()); Instance_Shutdown_Timer = new Timer(20000); //20 seconds @@ -2726,13 +2727,14 @@ DynamicZone* Zone::GetDynamicZone() return nullptr; } - auto expedition = Expedition::FindCachedExpeditionByZoneInstance(GetZoneID(), GetInstanceID()); - if (expedition) + // todo: cache dynamic zone id on zone later for faster lookup + for (const auto& dz_iter : zone->dynamic_zone_cache) { - return &expedition->GetDynamicZone(); + if (dz_iter.second->IsSameDz(GetZoneID(), GetInstanceID())) + { + return dz_iter.second.get(); + } } - // todo: tasks, missions, and quests with an associated dz for this instance id - return nullptr; } diff --git a/zone/zone.h b/zone/zone.h index c7c6daf12..af6f571ae 100755 --- a/zone/zone.h +++ b/zone/zone.h @@ -222,6 +222,7 @@ public: std::vector zone_grids; std::vector zone_grid_entries; + std::unordered_map> dynamic_zone_cache; std::unordered_map> expedition_cache; time_t weather_timer; From c078257f7077f39e206d7ee873912a0b485dc50f Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 5 Sep 2021 20:29:21 -0500 Subject: [PATCH 177/624] [Quest API] Port DiaWind Plugin to Native Quest API (#1521) * Port DiaWind plugin to native Quest API * Add no logging aliases --- common/eqemu_logsys.h | 2 + common/eqemu_logsys_log_aliases.h | 16 ++ common/string_util.cpp | 45 ++++ common/string_util.h | 3 + zone/CMakeLists.txt | 2 + zone/client.h | 4 +- zone/client_packet.cpp | 8 + zone/dialogue_window.cpp | 338 +++++++++++++++++++++++++ zone/dialogue_window.h | 394 ++++++++++++++++++++++++++++++ zone/lua_client.cpp | 9 + zone/lua_client.h | 2 + zone/perl_client.cpp | 54 +++- zone/questmgr.cpp | 324 +----------------------- 13 files changed, 874 insertions(+), 327 deletions(-) create mode 100644 zone/dialogue_window.cpp create mode 100644 zone/dialogue_window.h diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index c5ced8925..6990764a4 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -124,6 +124,7 @@ namespace Logs { Scheduler, Cheat, ClientList, + DiaWind, MaxCategoryID /* Don't Remove this */ }; @@ -206,6 +207,7 @@ namespace Logs { "Scheduler", "Cheat", "ClientList", + "DialogueWindow", }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index c5f7cd422..10358b572 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -666,6 +666,16 @@ OutF(LogSys, Logs::Detail, Logs::ClientList, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogDiaWind(message, ...) do {\ + if (LogSys.log_settings[Logs::DiaWind].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::DiaWind, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogDiaWindDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::DiaWind].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::DiaWind, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + #define Log(debug_level, log_category, message, ...) do {\ if (LogSys.log_settings[log_category].is_category_enabled == 1)\ LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ @@ -1050,6 +1060,12 @@ #define LogClientListDetail(message, ...) do {\ } while (0) +#define LogDiaWind(message, ...) do {\ +} while (0) + +#define LogDiaWindDetail(message, ...) do {\ +} while (0) + #define Log(debug_level, log_category, message, ...) do {\ } while (0) diff --git a/common/string_util.cpp b/common/string_util.cpp index 6c97dd076..65a35a0d7 100644 --- a/common/string_util.cpp +++ b/common/string_util.cpp @@ -116,6 +116,42 @@ std::vector SplitString(const std::string &str, const char delim) { return ret; } +// this one takes delimiter length into consideration +std::vector split_string(std::string s, std::string delimiter) +{ + size_t pos_start = 0, pos_end, delim_len = delimiter.length(); + std::string token; + std::vector res; + + while ((pos_end = s.find(delimiter, pos_start)) != std::string::npos) { + token = s.substr(pos_start, pos_end - pos_start); + pos_start = pos_end + delim_len; + res.push_back(token); + } + + res.push_back(s.substr(pos_start)); + return res; +} + +std::string get_between(const std::string &s, std::string start_delim, std::string stop_delim) +{ + if (s.find(start_delim) == std::string::npos && s.find(stop_delim) == std::string::npos) { + return ""; + } + + auto first_split = split_string(s, start_delim); + if (!first_split.empty()) { + std::string remaining_block = first_split[1]; + auto second_split = split_string(remaining_block, stop_delim); + if (!second_split.empty()) { + std::string between = second_split[0]; + return between; + } + } + + return ""; +} + std::string::size_type search_deliminated_string(const std::string &haystack, const std::string &needle, const char deliminator) { // this shouldn't go out of bounds, even without obvious bounds checks @@ -280,7 +316,16 @@ void find_replace(std::string &string_subject, const std::string &search_string, string_subject.replace(start_pos, search_string.length(), replace_string); start_pos += replace_string.length(); } +} +std::string replace_string(std::string subject, const std::string &search, const std::string &replace) +{ + size_t pos = 0; + while ((pos = subject.find(search, pos)) != std::string::npos) { + subject.replace(pos, search.length(), replace); + pos += replace.length(); + } + return subject; } void ParseAccountString(const std::string &s, std::string &account, std::string &loginserver) diff --git a/common/string_util.h b/common/string_util.h index 8c4a6878f..b6718172a 100644 --- a/common/string_util.h +++ b/common/string_util.h @@ -177,6 +177,8 @@ std::vector join_tuple(const std::string &glue, const std::pair SplitString(const std::string &s, const char delim = ','); +std::vector split_string(std::string s, std::string delimiter); +std::string get_between(const std::string &s, std::string start_delim, std::string stop_delim); std::string::size_type search_deliminated_string(const std::string &haystack, const std::string &needle, const char deliminator = ','); std::string EscapeString(const char *src, size_t sz); std::string EscapeString(const std::string &s); @@ -185,6 +187,7 @@ void ToLowerString(std::string &s); void ToUpperString(std::string &s); std::string JoinString(const std::vector& ar, const std::string &delim); void find_replace(std::string& string_subject, const std::string& search_string, const std::string& replace_string); +std::string replace_string(std::string subject, const std::string &search, const std::string &replace); void ParseAccountString(const std::string &s, std::string &account, std::string &loginserver); //const char based diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 909c88e77..184b7d318 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -23,6 +23,7 @@ SET(zone_sources corpse.cpp data_bucket.cpp doors.cpp + dialogue_window.cpp dynamic_zone.cpp effects.cpp embparser.cpp @@ -182,6 +183,7 @@ SET(zone_headers corpse.h data_bucket.h doors.h + dialogue_window.h dynamic_zone.h embparser.h embperl.h diff --git a/zone/client.h b/zone/client.h index dcc177e20..cb39637a1 100644 --- a/zone/client.h +++ b/zone/client.h @@ -203,7 +203,9 @@ enum eInnateSkill { InnateDisabled = 255 }; -const uint32 POPUPID_UPDATE_SHOWSTATSWINDOW = 1000000; +const std::string DIAWIND_RESPONSE_KEY = "diawind_npcresponse"; +const uint32 POPUPID_DIAWIND = 999; +const uint32 POPUPID_UPDATE_SHOWSTATSWINDOW = 1000000; struct ClientReward { diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 1365dbd56..dd22111fa 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -11132,6 +11132,7 @@ void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app) /** * Handle any EQEmu defined popup Ids first */ + std::string response; switch (popup_response->popupid) { case POPUPID_UPDATE_SHOWSTATSWINDOW: if (GetTarget() && GetTarget()->IsClient()) { @@ -11143,6 +11144,13 @@ void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app) return; break; + case POPUPID_DIAWIND: + response = GetEntityVariable(DIAWIND_RESPONSE_KEY.c_str()); + if (!response.empty()) { + ChannelMessageReceived(8, 0, 100, response.c_str()); + } + break; + case EQ::popupresponse::MOB_INFO_DISMISS: SetDisplayMobInfoWindow(false); Message(Chat::Yellow, "[DevTools] Window snoozed in this zone..."); diff --git a/zone/dialogue_window.cpp b/zone/dialogue_window.cpp new file mode 100644 index 000000000..99c8944da --- /dev/null +++ b/zone/dialogue_window.cpp @@ -0,0 +1,338 @@ +#include "dialogue_window.h" + +void DialogueWindow::Render(Client *c, std::string markdown) +{ + std::string output = markdown; + + // this is the NPC that the client is interacting with if there is dialogue going on + Mob *target; + if (c->GetTarget()) { + target = c->GetTarget(); + } + + // zero this out + c->SetEntityVariable(DIAWIND_RESPONSE_KEY.c_str(), ""); + + // simple find and replace for the markdown + find_replace(output, "~", ""); + find_replace(output, "{y}", ""); + find_replace(output, "{lb}", ""); + find_replace(output, "{r}", ""); + find_replace(output, "{g}", ""); + find_replace(output, "{gold}", ""); + find_replace(output, "{orange}", ""); + find_replace(output, "{gray}", ""); + find_replace(output, "{tan}", ""); + find_replace(output, "{bullet}", "•"); + find_replace(output, "{name}", "$name"); + find_replace(output, "{linebreak}", "--------------------------------------------------------------------"); + find_replace(output, "{rowpad}", R"({tdpad}<"td>{tdpad}<"td><"tr>)"); + find_replace(output, "{tdpad}", "----------------------"); + find_replace(output, "{in}", "        "); + + // mysterious voice + bool render_mysterious_voice = false; + if (markdown.find("mysterious") != std::string::npos) { + render_mysterious_voice = true; + LogDiaWind("Client [{}] Rendering mysterious voice", c->GetCleanName()); + find_replace(output, "mysterious", ""); + } + + // noquotes + bool render_noquotes = false; + if (markdown.find("noquotes") != std::string::npos) { + render_noquotes = true; + LogDiaWind("Client [{}] Rendering noquotes", c->GetCleanName()); + find_replace(output, "noquotes", ""); + } + + // nobracket + bool render_nobracket = false; + if (markdown.find("nobracket") != std::string::npos) { + render_nobracket = true; + LogDiaWind("Client [{}] Rendering nobracket", c->GetCleanName()); + find_replace(output, "nobracket", ""); + } + + // animations + std::string animation = get_between(output, "+", "+"); + if (!animation.empty()) { + LogDiaWind("Client [{}] Animation is not empty, contents are [{}]", c->GetCleanName(), animation); + find_replace(output, fmt::format("+{}+", animation), ""); + + // we treat the animation field differently if it is a number + if (StringIsNumber(animation)) { + LogDiaWindDetail("Client [{}] Animation is a number, firing animation [{}]", c->GetCleanName(), animation); + target->DoAnim(std::stoi(animation)); + } + else { + for (auto &a: animations) { + if (a.first.find(str_tolower(animation)) != std::string::npos) { + LogDiaWindDetail( + "Client [{}] Animation is a string, firing animation [{}] [{}]", + c->GetCleanName(), + a.second, + a.first + ); + target->DoAnim(a.second); + } + } + } + } + + // window expire time + std::string expire_time = get_between(output, "=", "="); + uint32 window_expire_seconds = 0; + if (!expire_time.empty()) { + LogDiaWind("Client [{}] Window expire time is not empty, contents are [{}]", c->GetCleanName(), expire_time); + find_replace(output, fmt::format("={}=", expire_time), ""); + + // we treat the animation field differently if it is a number + if (StringIsNumber(expire_time)) { + LogDiaWindDetail( + "Client [{}] Window expire time is a number, setting expiration to [{}]", + c->GetCleanName(), + expire_time + ); + window_expire_seconds = std::stoi(expire_time); + } + } + + uint32 popup_id = POPUPID_DIAWIND; + uint32 negative_id = 0; + char *button_name_0 = nullptr; + char *button_name_1 = nullptr; + uint32 sound_controls = 0; + + // window type + std::string wintype; + if (markdown.find("wintype") != std::string::npos) { + LogDiaWind("Client [{}] Rendering wintype option", c->GetCleanName()); + + auto first_split = split_string(output, "wintype:"); + if (!first_split.empty()) { + + // assumed that there is more after the wintype declaration + // wintype:0 +animation+ etc. + auto second_split = split_string(first_split[1], " "); + if (!second_split.empty()) { + wintype = second_split[0]; + LogDiaWindDetail("Client [{}] Rendering wintype option wintype [{}]", c->GetCleanName(), wintype); + } + + // if we're dealing with a string that is at the end + // example wintype:0"); + if (first_split[1].length() == 1) { + wintype = first_split[1]; + LogDiaWindDetail( + "Client [{}] Rendering wintype (end) option wintype [{}]", + c->GetCleanName(), + wintype + ); + } + + find_replace(output, fmt::format("wintype:{}", wintype), ""); + } + } + + // popupid + std::string popupid; + if (markdown.find("popupid") != std::string::npos) { + LogDiaWind("Client [{}] Rendering popupid option", c->GetCleanName()); + + auto first_split = split_string(output, "popupid:"); + if (!first_split.empty()) { + + // assumed that there is more after the popupid declaration + // popupid:0 +animation+ etc. + auto second_split = split_string(first_split[1], " "); + if (!second_split.empty()) { + popupid = second_split[0]; + LogDiaWindDetail("Client [{}] Rendering popupid option popupid [{}]", c->GetCleanName(), popupid); + } + + // if we're dealing with a string that is at the end + // example popupid:0"); + if (first_split[1].length() == 1) { + popupid = first_split[1]; + LogDiaWindDetail( + "Client [{}] Rendering popupid (end) option popupid [{}]", + c->GetCleanName(), + popupid + ); + } + + find_replace(output, fmt::format("popupid:{}", popupid), ""); + + // set the popup id + if (!popupid.empty()) { + popup_id = (StringIsNumber(popupid) ? std::atoi(popupid.c_str()) : 0); + } + } + } + + // bracket responses + std::vector responses; + std::vector bracket_responses; + if (markdown.find('[') != std::string::npos && markdown.find(']') != std::string::npos) { + // copy + std::string content = output; + + // while brackets still exist + int response_index = 0; + while (content.find('[') != std::string::npos && content.find(']') != std::string::npos) { + std::string bracket_message = get_between(content, "[", "]"); + + LogDiaWindDetail( + "Client [{}] Rendering responses ({}) [{}]", + c->GetCleanName(), + response_index, + bracket_message + ); + + // pop message onto responses + responses.emplace_back(bracket_message); + + // pop the response off of the message + find_replace(content, fmt::format("[{}]", bracket_message), ""); + + response_index++; + } + } + + // build saylinks + if (responses.size() > 1) { + for (auto &r: responses) { + bracket_responses.emplace_back( + fmt::format("[{}]", EQ::SayLinkEngine::GenerateQuestSaylink(r, false, r)) + ); + } + } + + // handle silent prompts from the [> silent syntax + std::string silent_message; + if (responses.empty() && markdown.find('[') != std::string::npos && markdown.find('>') != std::string::npos) { + silent_message = get_between(output, "[", ">"); + + // temporary and used during the response + c->SetEntityVariable(DIAWIND_RESPONSE_KEY.c_str(), silent_message.c_str()); + + // pop the silent message off + find_replace(output, fmt::format("[{}>", silent_message), ""); + } + else if (!responses.empty()) { + // handle silent prompts from the single respond bracket syntax [] + silent_message = responses[0]; + + // temporary and used during the response + c->SetEntityVariable(DIAWIND_RESPONSE_KEY.c_str(), silent_message.c_str()); + + // pop the silent message off + find_replace(output, fmt::format("[{}]", silent_message), ""); + } + + // strip brackets + if (render_nobracket) { + find_replace(output, "[", ""); + find_replace(output, "]", ""); + } + + // render title + std::string title; + std::string speaking; + if (target) { + speaking = fmt::format("{} says", target->GetCleanName()); + } + + if (render_mysterious_voice) { + speaking = "A Mysterious Voice says"; + } + + title = fmt::format("Dialogue [{}]", speaking); + + // render quotes + std::string quote_string = "'"; + if (render_noquotes) { + quote_string = ""; + } + + // click response + // window type response + uint32 window_type = (StringIsNumber(wintype) ? std::atoi(wintype.c_str()) : 0); + std::string click_response_button = (window_type == 1 ? "Yes" : "OK"); + std::string click_response = fmt::format( + "Click [{}] to continue...", + click_response_button + ); + + // different response when a timer is set + if (window_expire_seconds > 0) { + click_response = fmt::format( + "This message will disappear in {} second(s)...", + window_expire_seconds + ); + } + + // respond with silent message + if (!silent_message.empty()) { + click_response = fmt::format( + "Click [{}] to respond with [{}]...", + click_response_button, + silent_message + ); + } + + // post processing of color markdowns + // {spring_green_1} = + if (markdown.find('{') != std::string::npos && markdown.find('}') != std::string::npos) { + + // while brackets still exist + int tag_index = 0; + while (output.find('{') != std::string::npos && output.find('}') != std::string::npos) { + std::string color_tag = get_between(output, "{", "}"); + + LogDiaWindDetail( + "Client [{}] Rendering color tags ({}) [{}]", + c->GetCleanName(), + tag_index, + color_tag + ); + + std::string html_tag; + for (const auto& color : html_colors) { + if (color_tag.find(color.first) != std::string::npos) { + // build html tag + html_tag = fmt::format("", color.second); + // pop the response off of the message + find_replace(output, fmt::format("{{{}}}", color.first), html_tag); + } + } + + tag_index++; + } + } + + + // build the final output string + std::string final_output; + final_output = fmt::format("{}{}{}

{}", quote_string, output, quote_string, click_response); + + // send popup + c->SendFullPopup( + title.c_str(), + final_output.c_str(), + popup_id, + negative_id, + window_type, + window_expire_seconds, + button_name_0, + button_name_1, + sound_controls + ); + + // if multiple brackets are presented, send message + if (!bracket_responses.empty()) { + c->Message(Chat::White, " --- Select Response from Options --- "); + c->Message(Chat::White, implode(" ", bracket_responses).c_str()); + } +} diff --git a/zone/dialogue_window.h b/zone/dialogue_window.h new file mode 100644 index 000000000..7b0137550 --- /dev/null +++ b/zone/dialogue_window.h @@ -0,0 +1,394 @@ +#ifndef EQEMU_DIALOGUE_WINDOW_H +#define EQEMU_DIALOGUE_WINDOW_H + + +#include +#include "client.h" + +static const std::map html_colors = { + {"black", "#000000"}, + {"brown", "#804000"}, + {"burgundy", "#800000"}, + {"cadet_blue", "#77BFC7"}, + {"cadet_blue_1", "#4C787E"}, + {"chartreuse", "#8AFB17"}, + {"chartreuse_1", "#7FE817"}, + {"chartreuse_2", "#6CC417"}, + {"chartreuse_3", "#437C17"}, + {"chocolate", "#C85A17"}, + {"coral", "#F76541"}, + {"coral_1", "#E55B3C"}, + {"coral_2", "#C34A2C"}, + {"cornflower_blue", "#151B8D"}, + {"cyan", "#00FFFF"}, + {"cyan_1", "#57FEFF"}, + {"cyan_2", "#50EBEC"}, + {"cyan_3", "#46C7C7"}, + {"cyan_4", "#307D7E"}, + {"dark_blue", "#0000A0"}, + {"dark_goldenrod", "#AF7817"}, + {"dark_goldenrod_1", "#FBB117"}, + {"dark_goldenrod_2", "#E8A317"}, + {"dark_goldenrod_3", "#C58917"}, + {"dark_goldenrod_4", "#7F5217"}, + {"dark_green", "#254117"}, + {"dark_grey", "#808080"}, + {"dark_olive_green", "#CCFB5D"}, + {"dark_olive_green_2", "#BCE954"}, + {"dark_olive_green_3", "#A0C544"}, + {"dark_olive_green_4", "#667C26"}, + {"dark_orange", "#F88017"}, + {"dark_orange_1", "#F87217"}, + {"dark_orange_2", "#E56717"}, + {"dark_orange_3", "#7E3117"}, + {"dark_orange_3", "#C35617"}, + {"dark_orchid", "#7D1B7E"}, + {"dark_orchid_1", "#B041FF"}, + {"dark_orchid_2", "#A23BEC"}, + {"dark_orchid_3", "#8B31C7"}, + {"dark_orchid_4", "#571B7e"}, + {"dark_purple", "#800080"}, + {"dark_salmon", "#E18B6B"}, + {"dark_sea_green", "#8BB381"}, + {"dark_sea_green_1", "#C3FDB8"}, + {"dark_sea_green_2", "#B5EAAA"}, + {"dark_sea_green_3", "#99C68E"}, + {"dark_sea_green_4", "#617C58"}, + {"dark_slate_blue", "#2B3856"}, + {"dark_slate_gray", "#25383C"}, + {"dark_slate_gray_1", "#9AFEFF"}, + {"dark_slate_gray_2", "#8EEBEC"}, + {"dark_slate_gray_3", "#78c7c7"}, + {"dark_slate_gray_4", "#4C7D7E"}, + {"dark_turquoise", "#3B9C9C"}, + {"dark_violet", "#842DCE"}, + {"deep_pink", "#F52887"}, + {"deep_pink_1", "#E4287C"}, + {"deep_pink_2", "#C12267"}, + {"deep_pink_3", "#7D053F"}, + {"deep_sky_blue", "#3BB9FF"}, + {"deep_sky_blue_1", "#38ACEC"}, + {"deep_sky_blue_2", "#3090C7"}, + {"deep_sky_blue_3", "#25587E"}, + {"dim_gray", "#463E41"}, + {"dodger_blue", "#1589FF"}, + {"dodger_blue_1", "#157DEC"}, + {"dodger_blue_2", "#1569C7"}, + {"dodger_blue_3", "#153E7E"}, + {"firebrick", "#800517"}, + {"firebrick_1", "#F62817"}, + {"firebrick_2", "#E42217"}, + {"firebrick_3", "#C11B17"}, + {"forest_green", "#4E9258"}, + {"forest_green_1", "#808000"}, + {"gold", "#D4A017"}, + {"gold_1", "#FDD017"}, + {"gold_2", "#EAC117"}, + {"gold_3", "#C7A317"}, + {"gold_4", "#806517"}, + {"goldenrod", "#EDDA74"}, + {"goldenrod_1", "#FBB917"}, + {"goldenrod_2", "#E9AB17"}, + {"goldenrod_3", "#C68E17"}, + {"goldenrod_4", "#805817"}, + {"grass_green", "#408080"}, + {"gray", "#736F6E"}, + {"gray_1", "#150517"}, + {"gray_2", "#250517"}, + {"gray_3", "#2B1B17"}, + {"gray_4", "#302217"}, + {"gray_5", "#302226"}, + {"gray_6", "#342826"}, + {"gray_7", "#34282C"}, + {"gray_8", "#382D2C"}, + {"gray_9", "#3b3131"}, + {"gray_10", "#3E3535"}, + {"gray_11", "#413839"}, + {"gray_12", "#41383C"}, + {"gray_13", "#463E3F"}, + {"gray_14", "#4A4344"}, + {"gray_15", "#4C4646"}, + {"gray_16", "#4E4848"}, + {"gray_17", "#504A4B"}, + {"gray_18", "#544E4F"}, + {"gray_19", "#565051"}, + {"gray_19", "#595454"}, + {"gray_20", "#5C5858"}, + {"gray_21", "#5F5A59"}, + {"gray_22", "#625D5D"}, + {"gray_23", "#646060"}, + {"gray_24", "#666362"}, + {"gray_25", "#696565"}, + {"gray_26", "#6D6968"}, + {"gray_27", "#6E6A6B"}, + {"gray_28", "#726E6D"}, + {"gray_29", "#747170"}, + {"green", "#00FF00"}, + {"green_1", "#5FFB17"}, + {"green_2", "#59E817"}, + {"green_3", "#4CC417"}, + {"green_4", "#347C17"}, + {"green_yellow", "#B1FB17"}, + {"hot_pink", "#F660AB"}, + {"hot_pink_1", "#F665AB"}, + {"hot_pink_2", "#E45E9D"}, + {"hot_pink_3", "#C25283"}, + {"hot_pink_4", "#7D2252"}, + {"indian_red", "#F75D59"}, + {"indian_red_2", "#E55451"}, + {"indian_red_3", "#C24641"}, + {"indian_red_4", "#7E2217"}, + {"khaki", "#ADA96E"}, + {"khaki_1", "#FFF380"}, + {"khaki_2", "#EDE275"}, + {"khaki_3", "#C9BE62"}, + {"khaki_4", "#827839"}, + {"lavender", "#E3E4FA"}, + {"lavender_blush", "#FDEEF4"}, + {"lavender_blush_1", "#EBDDE2"}, + {"lavender_blush_2", "#C8BBBE"}, + {"lavender_blush_3", "#817679"}, + {"lawn_green", "#87F717"}, + {"lemon_chiffon", "#FFF8C6"}, + {"lemon_chiffon_1", "#ECE5B6"}, + {"lemon_chiffon_2", "#C9C299"}, + {"lemon_chiffon_3", "#827B60"}, + {"light_blue", "#0000FF"}, + {"light_blue_1", "#ADDFFF"}, + {"light_blue_2", "#BDEDFF"}, + {"light_blue_3", "#AFDCEC"}, + {"light_blue_4", "#95B9C7"}, + {"light_blue_5", "#5E767E"}, + {"light_coral", "#E77471"}, + {"light_cyan", "#E0FFFF"}, + {"light_cyan_1", "#CFECEC"}, + {"light_cyan_2", "#AFC7C7"}, + {"light_cyan_3", "#717D7D"}, + {"light_golden", "#ECD672"}, + {"light_goldenrod", "#ECD872"}, + {"light_goldenrod_1", "#FFE87C"}, + {"light_goldenrod_2", "#C8B560"}, + {"light_goldenrod_3", "#817339"}, + {"light_goldenrod_yellow", "#FAF8CC"}, + {"light_grey", "#C0C0C0"}, + {"light_pink", "#FAAFBA"}, + {"light_pink_1", "#F9A7B0"}, + {"light_pink_2", "#E799A3"}, + {"light_pink_3", "#C48189"}, + {"light_pink_4", "#7F4E52"}, + {"light_purple", "#FF0080"}, + {"light_salmon", "#F9966B"}, + {"light_salmon_1", "#E78A61"}, + {"light_salmon_2", "#C47451"}, + {"light_salmon_3", "#7F462C"}, + {"light_sea_green", "#3EA99F"}, + {"light_sky_blue", "#82CAFA"}, + {"light_sky_blue_1", "#A0CFEC"}, + {"light_sky_blue_2", "#87AFC7"}, + {"light_sky_blue_3", "#566D7E"}, + {"light_slate_blue", "#736AFF"}, + {"light_slate_gray", "#6D7B8D"}, + {"light_steel_blue", "#728FCE"}, + {"light_steel_blue_1", "#C6DEFF"}, + {"light_steel_blue_2", "#B7CEEC"}, + {"light_steel_blue_3", "#646D7E"}, + {"lime_green", "#41A317"}, + {"magenta", "#FF00FF"}, + {"magenta_1", "#F433FF"}, + {"magenta_2", "#E238EC"}, + {"magenta_3", "#C031C7"}, + {"maroon", "#810541"}, + {"maroon_1", "#F535AA"}, + {"maroon_2", "#E3319D"}, + {"maroon_3", "#C12283"}, + {"maroon_4", "#7D0552"}, + {"medium_aquamarine", "#348781"}, + {"medium_forest_green", "#347235"}, + {"medium_orchid", "#B048B5"}, + {"medium_orchid_1", "#D462FF"}, + {"medium_orchid_2", "#C45AEC"}, + {"medium_orchid_3", "#A74AC7"}, + {"medium_orchid_4", "#6A287E"}, + {"medium_purple", "#8467D7"}, + {"medium_purple_1", "#9E7BFF"}, + {"medium_purple_2", "#9172EC"}, + {"medium_purple_3", "#7A5DC7"}, + {"medium_purple_4", "#4E387E"}, + {"medium_sea_green", "#306754"}, + {"medium_slate_blue", "#5E5A80"}, + {"medium_spring_green", "#348017"}, + {"medium_turquoise", "#48CCCD"}, + {"medium_violet_red", "#CA226B"}, + {"midnight_blue", "#151B54"}, + {"orange", "#FF8040"}, + {"pale_turquoise", "#92C7C7"}, + {"pale_turquoise_1", "#5E7D7E"}, + {"pale_violet_red", "#D16587"}, + {"pale_violet_red_1", "#F778A1"}, + {"pale_violet_red_2", "#E56E94"}, + {"pale_violet_red_3", "#C25A7C"}, + {"pale_violet_red_4", "#7E354D"}, + {"pastel_green", "#00FF00"}, + {"pink", "#FAAFBE"}, + {"pink_1", "#FF00FF"}, + {"pink_2", "#E7A1B0"}, + {"pink_3", "#C48793"}, + {"pink_4", "#7F525D"}, + {"plum", "#B93B8F"}, + {"plum_1", "#F9B7FF"}, + {"plum_2", "#E6A9EC"}, + {"plum_3", "#C38EC7"}, + {"plum_4", "#7E587E"}, + {"purple", "#8E35EF"}, + {"purple_1", "#893BFF"}, + {"purple_2", "#7F38EC"}, + {"purple_3", "#6C2DC7"}, + {"purple_4", "#461B7E"}, + {"red", "#FF0000"}, + {"red_1", "#F62217"}, + {"red_2", "#E41B17"}, + {"rosy_brown", "#B38481"}, + {"rosy_brown_1", "#FBBBB9"}, + {"rosy_brown_2", "#E8ADAA"}, + {"rosy_brown_3", "#C5908E"}, + {"rosy_brown_4", "#7F5A58"}, + {"royal_blue", "#2B60DE"}, + {"royal_blue_1", "#306EFF"}, + {"royal_blue_2", "#2B65EC"}, + {"royal_blue_3", "#2554C7"}, + {"royal_blue_4", "#15317E"}, + {"salmon_1", "#F88158"}, + {"salmon_2", "#E67451"}, + {"salmon_3", "#C36241"}, + {"salmon_4", "#7E3817"}, + {"sandy_brown", "#EE9A4D"}, + {"sea_green", "#4E8975"}, + {"sea_green_1", "#6AFB92"}, + {"sea_green_2", "#64E986"}, + {"sea_green_3", "#54C571"}, + {"sea_green_4", "#387C44"}, + {"sienna", "#8A4117"}, + {"sienna_1", "#F87431"}, + {"sienna_2", "#E66C2C"}, + {"sienna_3", "#C35817"}, + {"sienna_4", "#7E3517"}, + {"sky_blue", "#82CAFF"}, + {"sky_blue_1", "#6698FF"}, + {"sky_blue_2", "#79BAEC"}, + {"sky_blue_3", "#659EC7"}, + {"sky_blue_4", "#41627E"}, + {"slate_blue", "#357EC7"}, + {"slate_blue_1", "#737CA1"}, + {"slate_blue_2", "#6960EC"}, + {"slate_blue_3", "#342D7E"}, + {"slate_gray", "#657383"}, + {"slate_gray_1", "#C2DFFF"}, + {"slate_gray_2", "#B4CFEC"}, + {"slate_gray_3", "#98AFC7"}, + {"slate_gray_4", "#616D7E"}, + {"spring_green", "#4AA02C"}, + {"spring_green_1", "#5EFB6E"}, + {"spring_green_2", "#57E964"}, + {"spring_green_3", "#4CC552"}, + {"spring_green_4", "#347C2C"}, + {"steel_blue", "#4863A0"}, + {"steel_blue_1", "#5CB3FF"}, + {"steel_blue_2", "#56A5EC"}, + {"steel_blue_3", "#488AC7"}, + {"steel_blue_4", "#2B547E"}, + {"thistle", "#D2B9D3"}, + {"thistle_1", "#FCDFFF"}, + {"thistle_2", "#E9CFEC"}, + {"thistle_3", "#C6AEC7"}, + {"thistle_4", "#806D7E"}, + {"turquoise", "#00FFFF"}, + {"turquoise_1", "#43C6DB"}, + {"turquoise_2", "#52F3FF"}, + {"turquoise_3", "#4EE2EC"}, + {"turquoise_4", "#43BFC7"}, + {"violet", "#8D38C9"}, + {"violet_red", "#F6358A"}, + {"violet_red_1", "#F6358A"}, + {"violet_red_2", "#E4317F"}, + {"violet_red_3", "#C12869"}, + {"violet_red_4", "#7D0541"}, + {"white", "#FFFFFF"}, + {"yellow", "#FFFF00"}, + {"yellow_1", "#FFFC17"}, + {"yellow_green", "#52D017"} +}; + +const std::map animations = { + {"kick", 1}, + {"pierce", 2}, + {"2hslash", 3}, + {"2hblunt", 4}, + {"2hpierce", 4}, + {"throw", 5}, + {"offhand", 6}, + {"bash", 7}, + {"mainhand", 8}, + {"bow", 9}, + {"swim", 10}, + {"roundkick", 11}, + {"gethit", 12}, + {"gethit2", 13}, + {"falling", 14}, + {"drowning", 15}, + {"death", 16}, + {"standby", 17}, + {"standby2", 18}, + {"lunge", 19}, + {"jump", 20}, + {"falling2", 21}, + {"duckwalk", 22}, + {"ladderclimb", 23}, + {"crouch", 24}, + {"swim2", 25}, + {"idle", 26}, + {"cheer", 27}, + {"disgusted", 28}, + {"wave", 29}, + {"rude", 30}, + {"yawn", 31}, + {"movetoside", 33}, + {"iceslide", 35}, + {"kneel", 36}, + {"swim3", 37}, + {"sit", 38}, + {"cast", 42}, + {"cast2", 43}, + {"cast3", 44}, + {"flykick", 45}, + {"tigerclaw", 46}, + {"eaglestrike", 47}, + {"nodyes", 48}, + {"shakeno", 49}, + {"plead", 50}, + {"clap", 51}, + {"blush", 52}, + {"chuckle", 54}, + {"headtilt", 57}, + {"dance", 58}, + {"disagree", 59}, + {"glare", 60}, + {"peer", 61}, + {"kneel", 62}, + {"laugh", 63}, + {"point", 64}, + {"shrug", 65}, + {"handraise", 66}, + {"salute", 67}, + {"shiver", 68}, + {"tapfoot", 69}, + {"bowto", 70}, + }; + + +class DialogueWindow { +public: + static void Render(Client *c, std::string markdown); +}; + + +#endif //EQEMU_DIALOGUE_WINDOW_H diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 6dc02bd66..d758dc6c8 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -15,6 +15,7 @@ #include "lua_group.h" #include "lua_raid.h" #include "lua_packet.h" +#include "dialogue_window.h" #include "../common/expedition_lockout_timer.h" struct InventoryWhere { }; @@ -1824,6 +1825,11 @@ int Lua_Client::GetClientMaxLevel() { return self->GetClientMaxLevel(); } +void Lua_Client::DialogueWindow(std::string markdown) { + Lua_Safe_Call_Void(); + DialogueWindow::Render(self, std::move(markdown)); +} + DynamicZoneLocation GetDynamicZoneLocationFromTable(const luabind::object& lua_table) { DynamicZoneLocation zone_location; @@ -2496,6 +2502,9 @@ luabind::scope lua_register_client() { .def("GetAlternateCurrencyValue", (int(Lua_Client::*)(uint32))&Lua_Client::GetAlternateCurrencyValue) .def("SendWebLink", (void(Lua_Client::*)(const char *))&Lua_Client::SendWebLink) .def("HasSpellScribed", (bool(Lua_Client::*)(int))&Lua_Client::HasSpellScribed) + .def("DiaWind", (void(Lua_Client::*)(std::string))&Lua_Client::DialogueWindow) + .def("DialogueWindow", (void(Lua_Client::*)(std::string))&Lua_Client::DialogueWindow) + .def("SetAccountFlag", (void(Lua_Client::*)(std::string,std::string))&Lua_Client::SetAccountFlag) .def("SetAccountFlag", (void(Lua_Client::*)(std::string,std::string))&Lua_Client::SetAccountFlag) .def("GetAccountFlag", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetAccountFlag) .def("GetGroup", (Lua_Group(Lua_Client::*)(void))&Lua_Client::GetGroup) diff --git a/zone/lua_client.h b/zone/lua_client.h index 3cc2a5c94..985939834 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -379,6 +379,8 @@ public: void SetClientMaxLevel(int value); int GetClientMaxLevel(); + void DialogueWindow(std::string markdown); + Lua_Expedition CreateExpedition(luabind::object expedition_info); Lua_Expedition CreateExpedition(std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players); Lua_Expedition CreateExpedition(std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players, bool disable_messages); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index e1aab97fc..8cb3d56e9 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -39,6 +39,7 @@ #include "client.h" #include "expedition.h" #include "titles.h" +#include "dialogue_window.h" #ifdef THIS /* this macro seems to leak out on some systems */ #undef THIS @@ -2111,11 +2112,11 @@ XS(XS_Client_IsStanding) XSRETURN(1); } -XS(XS_Client_Sit); +XS(XS_Client_Sit); XS(XS_Client_Sit) { dXSARGS; if (items != 1) - Perl_croak(aTHX_ "Usage: Client::Sit(THIS)"); + Perl_croak(aTHX_ "Usage: Client::Sit(THIS)"); { Client *THIS; VALIDATE_THIS_IS_CLIENT; @@ -5052,7 +5053,7 @@ XS(XS_Client_Fling) { VALIDATE_THIS_IS_CLIENT; if (items > 5) ignore_los = (bool) SvTRUE(ST(5)); - + if (items > 6) clipping = (bool) SvTRUE(ST(6)); @@ -5124,7 +5125,7 @@ XS(XS_Client_GetLearnableDisciplines) { min_level = (uint8)SvUV(ST(1)); if (items > 2) max_level = (uint8)SvUV(ST(2)); - + Client* THIS; VALIDATE_THIS_IS_CLIENT; auto learnable_disciplines = THIS->GetLearnableDisciplines(min_level, max_level); @@ -5146,7 +5147,7 @@ XS(XS_Client_GetLearnedDisciplines) { dXSARGS; if (items != 1) Perl_croak(aTHX_ "Usage: Client::GetLearnedDisciplines(THIS)"); - + Client* THIS; VALIDATE_THIS_IS_CLIENT; auto learned_disciplines = THIS->GetLearnedDisciplines(); @@ -5168,7 +5169,7 @@ XS(XS_Client_GetMemmedSpells) { dXSARGS; if (items != 1) Perl_croak(aTHX_ "Usage: Client::GetMemmedSpells(THIS)"); - + Client* THIS; VALIDATE_THIS_IS_CLIENT; auto memmed_spells = THIS->GetMemmedSpells(); @@ -5190,7 +5191,7 @@ XS(XS_Client_GetScribeableSpells) { dXSARGS; if (items < 1 || items > 3) Perl_croak(aTHX_ "Usage: Client::GetScribeableSpells(THIS, [uint8 min_level, uint8 max_level])"); - + uint8 min_level = 1; uint8 max_level = 0; if (items > 1) @@ -5219,7 +5220,7 @@ XS(XS_Client_GetScribedSpells) { dXSARGS; if (items != 1) Perl_croak(aTHX_ "Usage: Client::GetScribedSpells(THIS)"); - + Client* THIS; VALIDATE_THIS_IS_CLIENT; auto scribed_spells = THIS->GetScribedSpells(); @@ -5262,7 +5263,7 @@ XS(XS_Client_GetAAEXPModifier) { double aa_modifier = 1.0f; uint32 zone_id = (uint32)SvUV(ST(1)); dXSTARG; - VALIDATE_THIS_IS_CLIENT; + VALIDATE_THIS_IS_CLIENT; aa_modifier = THIS->GetAAEXPModifier(zone_id); XSprePUSH; PUSHn((double) aa_modifier); @@ -5432,6 +5433,39 @@ XS(XS_Client_RemoveItem) { XSRETURN_EMPTY; } +XS(XS_Client_DialogueWindow); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_DialogueWindow) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::DialogueWindow(THIS, string window_markdown)"); // @categories Script Utility + { + Client *THIS; + dXSTARG; + VALIDATE_THIS_IS_CLIENT; + + std::string window_markdown(SvPV_nolen(ST(1))); + DialogueWindow::Render(THIS, window_markdown); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_DiaWind); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_DiaWind) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::DiaWind(THIS, string window_markdown)"); // @categories Script Utility + { + Client *THIS; + dXSTARG; + VALIDATE_THIS_IS_CLIENT; + + std::string window_markdown(SvPV_nolen(ST(1))); + DialogueWindow::Render(THIS, window_markdown); + + } + XSRETURN_EMPTY; +} + #ifdef __cplusplus extern "C" #endif @@ -5486,6 +5520,8 @@ XS(boot_Client) { newXSproto(strcpy(buf, "DecreaseByID"), XS_Client_DecreaseByID, file, "$$$"); newXSproto(strcpy(buf, "DeleteItemInInventory"), XS_Client_DeleteItemInInventory, file, "$$;$$"); newXSproto(strcpy(buf, "Disconnect"), XS_Client_Disconnect, file, "$"); + newXSproto(strcpy(buf, "DiaWind"), XS_Client_DiaWind, file, "$$"); + newXSproto(strcpy(buf, "DialogueWindow"), XS_Client_DialogueWindow, file, "$$"); newXSproto(strcpy(buf, "DropItem"), XS_Client_DropItem, file, "$$"); newXSproto(strcpy(buf, "Duck"), XS_Client_Duck, file, "$"); newXSproto(strcpy(buf, "DyeArmorBySlot"), XS_Client_DyeArmorBySlot, file, "$$$$$;$"); diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 4a92b76f9..cac4c34a8 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -36,6 +36,7 @@ #include "zone.h" #include "zonedb.h" #include "zone_store.h" +#include "dialogue_window.h" #include #include @@ -1118,17 +1119,17 @@ uint16 QuestManager::scribespells(uint8 max_level, uint8 min_level) { int spells_learned = 0; if (spell_count > 0) { for (auto spell_id : spell_ids) { - if (book_slot == -1) { + if (book_slot == -1) { initiator->Message( Chat::Red, "Unable to scribe spell %s (%i) to Spell Book: Spell Book is Full.", spells[spell_id].name, spell_id ); break; } - + if (initiator->HasSpellScribed(spell_id)) continue; - + initiator->ScribeSpell(spell_id, book_slot); book_slot = initiator->GetNextAvailableSpellBookSlot(book_slot); spells_learned++; @@ -2506,7 +2507,7 @@ void QuestManager::message(int color, const char *message) { QuestManagerCurrentQuestVars(); if (!initiator) return; - + initiator->Message(color, message); } @@ -4277,319 +4278,8 @@ std::string QuestManager::secondstotime(int duration) { } std::string QuestManager::gethexcolorcode(std::string color_name) { - static const std::map colors = { - { "Black", "#000000" }, - { "Brown", "#804000" }, - { "Burgundy", "#800000" }, - { "Cadet Blue", "#77BFC7" }, - { "Cadet Blue1", "#4C787E" }, - { "Chartreuse", "#8AFB17" }, - { "Chartreuse1", "#7FE817" }, - { "Chartreuse2", "#6CC417" }, - { "Chartreuse3", "#437C17" }, - { "Chocolate", "#C85A17" }, - { "Coral", "#F76541" }, - { "Coral1", "#E55B3C" }, - { "Coral2", "#C34A2C" }, - { "Cornflower Blue", "#151B8D" }, - { "Cyan", "#00FFFF" }, - { "Cyan1", "#57FEFF" }, - { "Cyan2", "#50EBEC" }, - { "Cyan3", "#46C7C7" }, - { "Cyan4", "#307D7E" }, - { "Dark Blue", "#0000A0" }, - { "Dark Goldenrod", "#AF7817" }, - { "Dark Goldenrod1", "#FBB117" }, - { "Dark Goldenrod2", "#E8A317" }, - { "Dark Goldenrod3", "#C58917" }, - { "Dark Goldenrod4", "#7F5217" }, - { "Dark Green", "#254117" }, - { "Dark Grey", "#808080" }, - { "Dark Olive Green", "#CCFB5D" }, - { "Dark Olive Green2", "#BCE954" }, - { "Dark Olive Green3", "#A0C544" }, - { "Dark Olive Green4", "#667C26" }, - { "Dark Orange", "#F88017" }, - { "Dark Orange1", "#F87217" }, - { "Dark Orange2", "#E56717" }, - { "Dark Orange3", "#7E3117" }, - { "Dark Orange3", "#C35617" }, - { "Dark Orchid", "#7D1B7E" }, - { "Dark Orchid1", "#B041FF" }, - { "Dark Orchid2", "#A23BEC" }, - { "Dark Orchid3", "#8B31C7" }, - { "Dark Orchid4", "#571B7e" }, - { "Dark Purple", "#800080" }, - { "Dark Salmon", "#E18B6B" }, - { "Dark Sea Green", "#8BB381" }, - { "Dark Sea Green1", "#C3FDB8" }, - { "Dark Sea Green2", "#B5EAAA" }, - { "Dark Sea Green3", "#99C68E" }, - { "Dark Sea Green4", "#617C58" }, - { "Dark Slate Blue", "#2B3856" }, - { "Dark Slate Gray", "#25383C" }, - { "Dark Slate Gray1", "#9AFEFF" }, - { "Dark Slate Gray2", "#8EEBEC" }, - { "Dark Slate Gray3", "#78c7c7" }, - { "Dark Slate Gray4", "#4C7D7E" }, - { "Dark Turquoise", "#3B9C9C" }, - { "Dark Violet", "#842DCE" }, - { "Deep Pink", "#F52887" }, - { "Deep Pink1", "#E4287C" }, - { "Deep Pink2", "#C12267" }, - { "Deep Pink3", "#7D053F" }, - { "Deep Sky Blue", "#3BB9FF" }, - { "Deep Sky Blue1", "#38ACEC" }, - { "Deep Sky Blue2", "#3090C7" }, - { "Deep Sky Blue3", "#25587E" }, - { "Dim Gray", "#463E41" }, - { "Dodger Blue", "#1589FF" }, - { "Dodger Blue1", "#157DEC" }, - { "Dodger Blue2", "#1569C7" }, - { "Dodger Blue3", "#153E7E" }, - { "Firebrick", "#800517" }, - { "Firebrick1", "#F62817" }, - { "Firebrick2", "#E42217" }, - { "Firebrick3", "#C11B17" }, - { "Forest Green", "#4E9258" }, - { "Forest Green1", "#808000" }, - { "Gold", "#D4A017" }, - { "Gold1", "#FDD017" }, - { "Gold2", "#EAC117" }, - { "Gold3", "#C7A317" }, - { "Gold4", "#806517" }, - { "Goldenrod", "#EDDA74" }, - { "Goldenrod1", "#FBB917" }, - { "Goldenrod2", "#E9AB17" }, - { "Goldenrod3", "#C68E17" }, - { "Goldenrod4", "#805817" }, - { "Grass Green", "#408080" }, - { "Gray", "#736F6E" }, - { "Gray1", "#150517" }, - { "Gray2", "#250517" }, - { "Gray3", "#2B1B17" }, - { "Gray4", "#302217" }, - { "Gray5", "#302226" }, - { "Gray6", "#342826" }, - { "Gray7", "#34282C" }, - { "Gray8", "#382D2C" }, - { "Gray9", "#3b3131" }, - { "Gray10", "#3E3535" }, - { "Gray11", "#413839" }, - { "Gray12", "#41383C" }, - { "Gray13", "#463E3F" }, - { "Gray14", "#4A4344" }, - { "Gray15", "#4C4646" }, - { "Gray16", "#4E4848" }, - { "Gray17", "#504A4B" }, - { "Gray18", "#544E4F" }, - { "Gray19", "#565051" }, - { "Gray19", "#595454" }, - { "Gray20", "#5C5858" }, - { "Gray21", "#5F5A59" }, - { "Gray22", "#625D5D" }, - { "Gray23", "#646060" }, - { "Gray24", "#666362" }, - { "Gray25", "#696565" }, - { "Gray26", "#6D6968" }, - { "Gray27", "#6E6A6B" }, - { "Gray28", "#726E6D" }, - { "Gray29", "#747170" }, - { "Green", "#00FF00" }, - { "Green1", "#5FFB17" }, - { "Green2", "#59E817" }, - { "Green3", "#4CC417" }, - { "Green4", "#347C17" }, - { "Green Yellow", "#B1FB17" }, - { "Hot Pink", "#F660AB" }, - { "Hot Pink1", "#F665AB" }, - { "Hot Pink2", "#E45E9D" }, - { "Hot Pink3", "#C25283" }, - { "Hot Pink4", "#7D2252" }, - { "Indian Red", "#F75D59" }, - { "Indian Red2", "#E55451" }, - { "Indian Red3", "#C24641" }, - { "Indian Red4", "#7E2217" }, - { "Khaki", "#ADA96E" }, - { "Khaki1", "#FFF380" }, - { "Khaki2", "#EDE275" }, - { "Khaki3", "#C9BE62" }, - { "Khaki4", "#827839" }, - { "Lavender", "#E3E4FA" }, - { "Lavender Blush", "#FDEEF4" }, - { "Lavender Blush1", "#EBDDE2" }, - { "Lavender Blush2", "#C8BBBE" }, - { "Lavender Blush3", "#817679" }, - { "Lawn Green", "#87F717" }, - { "Lemon Chiffon", "#FFF8C6" }, - { "Lemon Chiffon1", "#ECE5B6" }, - { "Lemon Chiffon2", "#C9C299" }, - { "Lemon Chiffon3", "#827B60" }, - { "Light Blue", "#0000FF" }, - { "Light Blue1", "#ADDFFF" }, - { "Light Blue2", "#BDEDFF" }, - { "Light Blue3", "#AFDCEC" }, - { "Light Blue4", "#95B9C7" }, - { "Light Blue5", "#5E767E" }, - { "Light Coral", "#E77471" }, - { "Light Cyan", "#E0FFFF" }, - { "Light Cyan1", "#CFECEC" }, - { "Light Cyan2", "#AFC7C7" }, - { "Light Cyan3", "#717D7D" }, - { "Light Golden", "#ECD672" }, - { "Light Goldenrod", "#ECD872" }, - { "Light Goldenrod1", "#FFE87C" }, - { "Light Goldenrod2", "#C8B560" }, - { "Light Goldenrod3", "#817339" }, - { "Light Goldenrod Yellow", "#FAF8CC" }, - { "Light Grey", "#C0C0C0" }, - { "Light Pink", "#FAAFBA" }, - { "Light Pink1", "#F9A7B0" }, - { "Light Pink2", "#E799A3" }, - { "Light Pink3", "#C48189" }, - { "Light Pink4", "#7F4E52" }, - { "Light Purple", "#FF0080" }, - { "Light Salmon", "#F9966B" }, - { "Light Salmon1", "#E78A61" }, - { "Light Salmon2", "#C47451" }, - { "Light Salmon3", "#7F462C" }, - { "Light Sea Green", "#3EA99F" }, - { "Light Sky Blue", "#82CAFA" }, - { "Light Sky Blue1", "#A0CFEC" }, - { "Light Sky Blue2", "#87AFC7" }, - { "Light Sky Blue3", "#566D7E" }, - { "Light Slate Blue", "#736AFF" }, - { "Light Slate Gray", "#6D7B8D" }, - { "Light Steel Blue", "#728FCE" }, - { "Light Steel Blue1", "#C6DEFF" }, - { "Light Steel Blue2", "#B7CEEC" }, - { "Light Steel Blue3", "#646D7E" }, - { "Lime Green", "#41A317" }, - { "Magenta", "#FF00FF" }, - { "Magenta1", "#F433FF" }, - { "Magenta2", "#E238EC" }, - { "Magenta3", "#C031C7" }, - { "Maroon", "#810541" }, - { "Maroon1", "#F535AA" }, - { "Maroon2", "#E3319D" }, - { "Maroon3", "#C12283" }, - { "Maroon4", "#7D0552" }, - { "Medium Aquamarine", "#348781" }, - { "Medium Forest Green", "#347235" }, - { "Medium Orchid", "#B048B5" }, - { "Medium Orchid1", "#D462FF" }, - { "Medium Orchid2", "#C45AEC" }, - { "Medium Orchid3", "#A74AC7" }, - { "Medium Orchid4", "#6A287E" }, - { "Medium Purple", "#8467D7" }, - { "Medium Purple1", "#9E7BFF" }, - { "Medium Purple2", "#9172EC" }, - { "Medium Purple3", "#7A5DC7" }, - { "Medium Purple4", "#4E387E" }, - { "Medium Sea Green", "#306754" }, - { "Medium Slate Blue", "#5E5A80" }, - { "Medium Spring Green", "#348017" }, - { "Medium Turquoise", "#48CCCD" }, - { "Medium Violet Red", "#CA226B" }, - { "Midnight Blue", "#151B54" }, - { "Orange", "#FF8040" }, - { "Pale Turquoise", "#92C7C7" }, - { "Pale Turquoise1", "#5E7D7E" }, - { "Pale Violet Red", "#D16587" }, - { "Pale Violet Red1", "#F778A1" }, - { "Pale Violet Red2", "#E56E94" }, - { "Pale Violet Red3", "#C25A7C" }, - { "Pale Violet Red4", "#7E354D" }, - { "Pastel Green", "#00FF00" }, - { "Pink", "#FAAFBE" }, - { "Pink1", "#FF00FF" }, - { "Pink2", "#E7A1B0" }, - { "Pink3", "#C48793" }, - { "Pink4", "#7F525D" }, - { "Plum", "#B93B8F" }, - { "Plum1", "#F9B7FF" }, - { "Plum2", "#E6A9EC" }, - { "Plum3", "#C38EC7" }, - { "Plum4", "#7E587E" }, - { "Purple", "#8E35EF" }, - { "Purple1", "#893BFF" }, - { "Purple2", "#7F38EC" }, - { "Purple3", "#6C2DC7" }, - { "Purple4", "#461B7E" }, - { "Red", "#FF0000" }, - { "Red1", "#F62217" }, - { "Red2", "#E41B17" }, - { "Rosy Brown", "#B38481" }, - { "Rosy Brown1", "#FBBBB9" }, - { "Rosy Brown2", "#E8ADAA" }, - { "Rosy Brown3", "#C5908E" }, - { "Rosy Brown4", "#7F5A58" }, - { "Royal Blue", "#2B60DE" }, - { "Royal Blue1", "#306EFF" }, - { "Royal Blue2", "#2B65EC" }, - { "Royal Blue3", "#2554C7" }, - { "Royal Blue4", "#15317E" }, - { "Salmon1", "#F88158" }, - { "Salmon2", "#E67451" }, - { "Salmon3", "#C36241" }, - { "Salmon4", "#7E3817" }, - { "Sandy Brown", "#EE9A4D" }, - { "Sea Green", "#4E8975" }, - { "Sea Green1", "#6AFB92" }, - { "Sea Green2", "#64E986" }, - { "Sea Green3", "#54C571" }, - { "Sea Green4", "#387C44" }, - { "Sienna", "#8A4117" }, - { "Sienna1", "#F87431" }, - { "Sienna2", "#E66C2C" }, - { "Sienna3", "#C35817" }, - { "Sienna4", "#7E3517" }, - { "Sky Blue", "#82CAFF" }, - { "Sky Blue1", "#6698FF" }, - { "Sky Blue2", "#79BAEC" }, - { "Sky Blue3", "#659EC7" }, - { "Sky Blue4", "#41627E" }, - { "Slate Blue", "#357EC7" }, - { "Slate Blue1", "#737CA1" }, - { "Slate Blue2", "#6960EC" }, - { "Slate Blue3", "#342D7E" }, - { "Slate Gray", "#657383" }, - { "Slate Gray1", "#C2DFFF" }, - { "Slate Gray2", "#B4CFEC" }, - { "Slate Gray3", "#98AFC7" }, - { "Slate Gray4", "#616D7E" }, - { "Spring Green", "#4AA02C" }, - { "Spring Green1", "#5EFB6E" }, - { "Spring Green2", "#57E964" }, - { "Spring Green3", "#4CC552" }, - { "Spring Green4", "#347C2C" }, - { "Steel Blue", "#4863A0" }, - { "Steel Blue1", "#5CB3FF" }, - { "Steel Blue2", "#56A5EC" }, - { "Steel Blue3", "#488AC7" }, - { "Steel Blue4", "#2B547E" }, - { "Thistle", "#D2B9D3" }, - { "Thistle1", "#FCDFFF" }, - { "Thistle2", "#E9CFEC" }, - { "Thistle3", "#C6AEC7" }, - { "Thistle4", "#806D7E" }, - { "Turquoise", "#00FFFF" }, - { "Turquoise1", "#43C6DB" }, - { "Turquoise2", "#52F3FF" }, - { "Turquoise3", "#4EE2EC" }, - { "Turquoise4", "#43BFC7" }, - { "Violet", "#8D38C9" }, - { "Violet Red", "#F6358A" }, - { "Violet Red1", "#F6358A" }, - { "Violet Red2", "#E4317F" }, - { "Violet Red3", "#C12869" }, - { "Violet Red4", "#7D0541" }, - { "White", "#FFFFFF" }, - { "Yellow", "#FFFF00" }, - { "Yellow1", "#FFFC17" }, - { "Yellow Green", "#52D017" } - }; - for (auto color : colors) { + + for (auto color : html_colors) { if (!strcasecmp(color.first.c_str(), color_name.c_str())) { return color.second; } From 930079959cc924e227a5214ab1889936967b521b Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Mon, 6 Sep 2021 20:37:34 -0400 Subject: [PATCH 178/624] [Bug Fix] Resolves issue where loading temporary merchant list "fails" because there aren't any to load. --- zone/zone.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zone/zone.cpp b/zone/zone.cpp index 709803aab..37bcb4db5 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -534,6 +534,10 @@ void Zone::LoadTempMerchantData() ) ); + if (!results.Success() || results.RowCount() == 0) { + return; + } + std::vector npc_ids; for (auto row = results.begin(); row != results.end(); ++row) { npc_ids.push_back(row[0]); From 115567364286cceb7067865d53cec0fdde747057 Mon Sep 17 00:00:00 2001 From: Noudess Date: Thu, 9 Sep 2021 08:42:14 -0400 Subject: [PATCH 179/624] Hack to fix melee chasing fleeing mobs "too far" issues. --- zone/aggro.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 9d9673568..b2013e613 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -851,6 +851,17 @@ bool Mob::CombatRange(Mob* other, float fixed_size_mod, bool aeRampage) size_mod *= RuleR(Combat,HitBoxMod); // used for testing sizemods on different races. size_mod *= fixed_size_mod; // used to extend the size_mod + // Melee chasing fleeing mobs is borked. The client updates don't + // come to the server quickly enough, especially when mob is running + // and/or PC has good run speed. This change is a hack, but it greatly + // improved playability and "you are too far away" while chasing + // a fleeing mob. The Blind check is to make sure that this does not + // apply to disoriented fleeing mobs who need proximity to turn and fight. + if (other->currently_fleeing && !other->IsBlind()) + { + size_mod *= 3; + } + // prevention of ridiculously sized hit boxes if (size_mod > 10000) size_mod = size_mod / 7; @@ -901,6 +912,7 @@ bool Mob::CombatRange(Mob* other, float fixed_size_mod, bool aeRampage) } return true; } + return false; } From 69244a094defc7e44a53dda1e3e484ddf10edff0 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Thu, 9 Sep 2021 09:52:26 -0500 Subject: [PATCH 180/624] Update changelog.txt --- changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 955a0a2e8..2e26a697d 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,7 +3,7 @@ ############################################ # # New changelog can be found here -# https://eqemu.gitbook.io/changelog/ +# https://docs.eqemu.io/server/changelog/server # ############################################ # Deprecated From 05ac8499df6a27d78b70b878a55e9f02b3efd77b Mon Sep 17 00:00:00 2001 From: Noudess Date: Fri, 10 Sep 2021 10:18:59 -0400 Subject: [PATCH 181/624] Fix bug where stacks of non-stackable items are removed when you buy 1. --- zone/zone.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zone/zone.cpp b/zone/zone.cpp index 709803aab..a9943dcf0 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -361,7 +361,8 @@ void Zone::DumpMerchantList(uint32 npcid) { int Zone::SaveTempItem(uint32 merchantid, uint32 npcid, uint32 item, int32 charges, bool sold) { - LogInventory("Transaction of [{}] [{}]", charges, item); + LogInventory("[{}] [{}] charges of [{}]", ((sold) ? "Sold" : "Bought"), + charges, item); //DumpMerchantList(npcid); // Iterate past main items. // If the item being transacted is in this list, return 0; @@ -419,8 +420,7 @@ int Zone::SaveTempItem(uint32 merchantid, uint32 npcid, uint32 item, int32 charg if (!ml.origslot) { ml.origslot = ml.slot; } - bool is_stackable = database.GetItem(item)->Stackable; - if ((is_stackable && charges > 0) || (!is_stackable && sold)) { + if (ml.charges > 0) { database.SaveMerchantTemp(npcid, ml.origslot, item, ml.charges); tmp_merlist.push_back(ml); } else { From 94c1a50cc82eb07b2438410a1004b45457eb157e Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 12 Sep 2021 22:08:04 -0500 Subject: [PATCH 182/624] [GM Command] Door Manipulation Command Port (#1524) * Initial commit * Push latest * Update door_manipulation.cpp * More door work * More doors work * Upload notes * Finalize changes * Remove comment * Add missing chat line * Swapped URI parser with something not using deprecated C++ functions --- common/CMakeLists.txt | 4 +- common/database.cpp | 67 ++ common/database.h | 2 + common/eqemu_logsys.cpp | 2 + common/eqemu_logsys.h | 2 + common/eqemu_logsys_log_aliases.h | 16 + common/http/uri.h | 633 ++++++++++++++ .../base/base_tool_game_objects_repository.h | 346 ++++++++ .../tool_game_objects_repository.h | 70 ++ .../generators/repository-generator.pl | 2 +- zone/CMakeLists.txt | 2 + zone/client.cpp | 10 + zone/client.h | 8 + zone/client_packet.cpp | 15 + zone/command.cpp | 6 + zone/command.h | 1 + zone/doors.cpp | 6 + zone/doors.h | 1 + zone/gm_commands/door_manipulation.cpp | 779 ++++++++++++++++++ zone/gm_commands/door_manipulation.h | 23 + zone/npc.cpp | 19 +- zone/npc.h | 2 +- 22 files changed, 2007 insertions(+), 9 deletions(-) create mode 100644 common/http/uri.h create mode 100644 common/repositories/base/base_tool_game_objects_repository.h create mode 100644 common/repositories/tool_game_objects_repository.h create mode 100644 zone/gm_commands/door_manipulation.cpp create mode 100644 zone/gm_commands/door_manipulation.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 6e8a826fc..e9de82316 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -526,6 +526,7 @@ SET(common_headers guild_base.h guilds.h http/httplib.h + http/uri.h inventory_profile.h inventory_slot.h ipc_mutex.h @@ -638,7 +639,8 @@ SET(common_headers StackWalker/StackWalker.h util/memory_stream.h util/directory.h - util/uuid.h) + util/uuid.h +) SOURCE_GROUP(Event FILES event/event_loop.h diff --git a/common/database.cpp b/common/database.cpp index cf9752350..0a5678b2c 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -39,6 +39,7 @@ #include "unix.h" #include #include + #endif #include "database.h" @@ -46,6 +47,8 @@ #include "extprofile.h" #include "string_util.h" #include "database_schema.h" +#include "http/httplib.h" +#include "http/uri.h" extern Client client; @@ -2418,3 +2421,67 @@ bool Database::CopyCharacter( return true; } +void Database::SourceDatabaseTableFromUrl(std::string table_name, std::string url) +{ + try { + uri request_uri(url); + + LogHTTP( + "[SourceDatabaseTableFromUrl] parsing url [{}] path [{}] host [{}] query_string [{}] protocol [{}] port [{}]", + url, + request_uri.get_path(), + request_uri.get_host(), + request_uri.get_query(), + request_uri.get_scheme(), + request_uri.get_port() + ); + + if (!DoesTableExist(table_name)) { + LogMySQLQuery("Table [{}] does not exist. Downloading from Github and installing...", table_name); + + // http get request + httplib::Client cli( + fmt::format( + "{}://{}", + request_uri.get_scheme(), + request_uri.get_host() + ).c_str() + ); + + cli.set_connection_timeout(0, 60000000); // 60 sec + cli.set_read_timeout(60, 0); // 60 seconds + cli.set_write_timeout(60, 0); // 60 seconds + + int sourced_queries = 0; + + if (auto res = cli.Get(request_uri.get_path().c_str())) { + if (res->status == 200) { + for (auto &s: SplitString(res->body, ';')) { + if (!trim(s).empty()) { + auto results = QueryDatabase(s); + if (!results.ErrorMessage().empty()) { + LogError("Error sourcing SQL [{}]", results.ErrorMessage()); + return; + } + sourced_queries++; + } + } + } + } + else { + LogError("Error retrieving URL [{}]", url); + } + + LogMySQLQuery( + "Table [{}] installed. Sourced [{}] queries", + table_name, + sourced_queries + ); + } + + } + catch (std::invalid_argument iae) { + LogError("[SourceDatabaseTableFromUrl] URI parser error [{}]", iae.what()); + } +} + diff --git a/common/database.h b/common/database.h index 79c52c514..e0eff4e8e 100644 --- a/common/database.h +++ b/common/database.h @@ -269,6 +269,8 @@ public: int CountInvSnapshots(); void ClearInvSnapshots(bool from_now = false); + void SourceDatabaseTableFromUrl(std::string table_name, std::string url); + private: diff --git a/common/eqemu_logsys.cpp b/common/eqemu_logsys.cpp index 83e9a45dd..e5f0c63c1 100644 --- a/common/eqemu_logsys.cpp +++ b/common/eqemu_logsys.cpp @@ -130,6 +130,8 @@ EQEmuLogSys *EQEmuLogSys::LoadLogSettingsDefaults() log_settings[Logs::Loot].log_to_gmsay = static_cast(Logs::General); log_settings[Logs::Scheduler].log_to_console = static_cast(Logs::General); log_settings[Logs::Cheat].log_to_console = static_cast(Logs::General); + log_settings[Logs::HTTP].log_to_console = static_cast(Logs::General); + log_settings[Logs::HTTP].log_to_gmsay = static_cast(Logs::General); /** * RFC 5424 diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 6990764a4..3ec495413 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -125,6 +125,7 @@ namespace Logs { Cheat, ClientList, DiaWind, + HTTP, MaxCategoryID /* Don't Remove this */ }; @@ -208,6 +209,7 @@ namespace Logs { "Cheat", "ClientList", "DialogueWindow", + "HTTP", }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index 10358b572..a949f63a8 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -676,6 +676,16 @@ OutF(LogSys, Logs::Detail, Logs::DiaWind, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogHTTP(message, ...) do {\ + if (LogSys.log_settings[Logs::HTTP].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::HTTP, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogHTTPDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::HTTP].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::HTTP, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + #define Log(debug_level, log_category, message, ...) do {\ if (LogSys.log_settings[log_category].is_category_enabled == 1)\ LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ @@ -1066,6 +1076,12 @@ #define LogDiaWindDetail(message, ...) do {\ } while (0) +#define LogHTTP(message, ...) do {\ +} while (0) + +#define LogHTTPDetail(message, ...) do {\ +} while (0) + #define Log(debug_level, log_category, message, ...) do {\ } while (0) diff --git a/common/http/uri.h b/common/http/uri.h new file mode 100644 index 000000000..04b891e45 --- /dev/null +++ b/common/http/uri.h @@ -0,0 +1,633 @@ +// Copyright (C) 2015 Ben Lewis +// Licensed under the MIT license. +// https://github.com/ben-zen/uri-library + +#pragma once + +#include +#include +#include +#include +#include + +class uri { + /* URIs are broadly divided into two categories: hierarchical and + * non-hierarchical. Both hierarchical URIs and non-hierarchical URIs have a + * few elements in common; all URIs have a scheme of one or more alphanumeric + * characters followed by a colon, and they all may optionally have a query + * component preceded by a question mark, and a fragment component preceded by + * an octothorpe (hash mark: '#'). The query consists of stanzas separated by + * either ampersands ('&') or semicolons (';') (but only one or the other), + * and each stanza consists of a key and an optional value; if the value + * exists, the key and value must be divided by an equals sign. + * + * The following is an example from Wikipedia of a hierarchical URI: + * scheme:[//[user:password@]domain[:port]][/]path[?query][#fragment] + */ + +public: + + enum class scheme_category { + Hierarchical, + NonHierarchical + }; + + enum class component { + Scheme, + Content, + Username, + Password, + Host, + Port, + Path, + Query, + Fragment + }; + + enum class query_argument_separator { + ampersand, + semicolon + }; + + uri( + char const *uri_text, scheme_category category = scheme_category::Hierarchical, + query_argument_separator separator = query_argument_separator::ampersand + ) : + m_category(category), + m_path_is_rooted(false), + m_port(0), + m_separator(separator) + { + setup(std::string(uri_text), category); + }; + + uri( + std::string const &uri_text, scheme_category category = scheme_category::Hierarchical, + query_argument_separator separator = query_argument_separator::ampersand + ) : + m_category(category), + m_path_is_rooted(false), + m_port(0), + m_separator(separator) + { + setup(uri_text, category); + }; + + uri( + std::map const &components, + scheme_category category, + bool rooted_path, + query_argument_separator separator = query_argument_separator::ampersand + ) : + m_category(category), + m_path_is_rooted(rooted_path), + m_separator(separator) + { + if (components.count(component::Scheme)) { + if (components.at(component::Scheme).length() == 0) { + throw std::invalid_argument("Scheme cannot be empty."); + } + m_scheme = components.at(component::Scheme); + } + else { + throw std::invalid_argument("A URI must have a scheme."); + } + + if (category == scheme_category::Hierarchical) { + if (components.count(component::Content)) { + throw std::invalid_argument("The content component is only for use in non-hierarchical URIs."); + } + + bool has_username = components.count(component::Username); + bool has_password = components.count(component::Password); + if (has_username && has_password) { + m_username = components.at(component::Username); + m_password = components.at(component::Password); + } + else if ((has_username && !has_password) || (!has_username && has_password)) { + throw std::invalid_argument("If a username or password is supplied, both must be provided."); + } + + if (components.count(component::Host)) { + m_host = components.at(component::Host); + } + + if (components.count(component::Port)) { + m_port = std::stoul(components.at(component::Port)); + } + + if (components.count(component::Path)) { + m_path = components.at(component::Path); + } + else { + throw std::invalid_argument("A path is required on a hierarchical URI, even an empty path."); + } + } + else { + if (components.count(component::Username) + || components.count(component::Password) + || components.count(component::Host) + || components.count(component::Port) + || components.count(component::Path)) { + throw std::invalid_argument("None of the hierarchical components are allowed in a non-hierarchical URI."); + } + + if (components.count(component::Content)) { + m_content = components.at(component::Content); + } + else { + throw std::invalid_argument( + "Content is a required component for a non-hierarchical URI, even an empty string." + ); + } + } + + if (components.count(component::Query)) { + m_query = components.at(component::Query); + } + + if (components.count(component::Fragment)) { + m_fragment = components.at(component::Fragment); + } + } + + uri(uri const &other, std::map const &replacements) : + m_category(other.m_category), + m_path_is_rooted(other.m_path_is_rooted), + m_separator(other.m_separator) + { + m_scheme = (replacements.count(component::Scheme)) + ? replacements.at(component::Scheme) : other.m_scheme; + + if (m_category == scheme_category::Hierarchical) { + m_username = (replacements.count(component::Username)) + ? replacements.at(component::Username) : other.m_username; + + m_password = (replacements.count(component::Password)) + ? replacements.at(component::Password) : other.m_password; + + m_host = (replacements.count(component::Host)) + ? replacements.at(component::Host) : other.m_host; + + m_port = (replacements.count(component::Port)) + ? std::stoul(replacements.at(component::Port)) : other.m_port; + + m_path = (replacements.count(component::Path)) + ? replacements.at(component::Path) : other.m_path; + } + else { + m_content = (replacements.count(component::Content)) + ? replacements.at(component::Content) : other.m_content; + } + + m_query = (replacements.count(component::Query)) + ? replacements.at(component::Query) : other.m_query; + + m_fragment = (replacements.count(component::Fragment)) + ? replacements.at(component::Fragment) : other.m_fragment; + } + + // Copy constructor; just use the copy assignment operator internally. + uri(uri const &other) + { + *this = other; + }; + + // Copy assignment operator + uri &operator=(uri const &other) + { + if (this != &other) { + m_scheme = other.m_scheme; + m_content = other.m_content; + m_username = other.m_username; + m_password = other.m_password; + m_host = other.m_host; + m_path = other.m_path; + m_query = other.m_query; + m_fragment = other.m_fragment; + m_query_dict = other.m_query_dict; + m_category = other.m_category; + m_port = other.m_port; + m_path_is_rooted = other.m_path_is_rooted; + m_separator = other.m_separator; + } + return *this; + } + + ~uri() {}; + + std::string const &get_scheme() const + { + return m_scheme; + }; + + scheme_category get_scheme_category() const + { + return m_category; + }; + + std::string const &get_content() const + { + if (m_category != scheme_category::NonHierarchical) { + throw std::domain_error("The content component is only valid for non-hierarchical URIs."); + } + return m_content; + }; + + std::string const &get_username() const + { + if (m_category != scheme_category::Hierarchical) { + throw std::domain_error("The username component is only valid for hierarchical URIs."); + } + return m_username; + }; + + std::string const &get_password() const + { + if (m_category != scheme_category::Hierarchical) { + throw std::domain_error("The password component is only valid for hierarchical URIs."); + } + return m_password; + }; + + std::string const &get_host() const + { + if (m_category != scheme_category::Hierarchical) { + throw std::domain_error("The host component is only valid for hierarchical URIs."); + } + return m_host; + }; + + unsigned long get_port() const + { + if (m_category != scheme_category::Hierarchical) { + throw std::domain_error("The port component is only valid for hierarchical URIs."); + } + return m_port; + }; + + std::string const &get_path() const + { + if (m_category != scheme_category::Hierarchical) { + throw std::domain_error("The path component is only valid for hierarchical URIs."); + } + return m_path; + }; + + std::string const &get_query() const + { + return m_query; + }; + + std::map const &get_query_dictionary() const + { + return m_query_dict; + }; + + std::string const &get_fragment() const + { + return m_fragment; + }; + + std::string to_string() const + { + std::string full_uri; + full_uri.append(m_scheme); + full_uri.append(":"); + + if (m_content.length() > m_path.length()) { + full_uri.append("//"); + if (!(m_username.empty() || m_password.empty())) { + full_uri.append(m_username); + full_uri.append(":"); + full_uri.append(m_password); + full_uri.append("@"); + } + + full_uri.append(m_host); + + if (m_port != 0) { + full_uri.append(":"); + full_uri.append(std::to_string(m_port)); + } + } + + if (m_path_is_rooted) { + full_uri.append("/"); + } + full_uri.append(m_path); + + if (!m_query.empty()) { + full_uri.append("?"); + full_uri.append(m_query); + } + + if (!m_fragment.empty()) { + full_uri.append("#"); + full_uri.append(m_fragment); + } + + return full_uri; + }; + +private: + + void setup(std::string const &uri_text, scheme_category category) + { + size_t const uri_length = uri_text.length(); + + if (uri_length == 0) { + throw std::invalid_argument("URIs cannot be of zero length."); + } + + std::string::const_iterator cursor = parse_scheme( + uri_text, + uri_text.begin()); + // After calling parse_scheme, *cursor == ':'; none of the following parsers + // expect a separator character, so we advance the cursor upon calling them. + cursor = parse_content(uri_text, (cursor + 1)); + + if ((cursor != uri_text.end()) && (*cursor == '?')) { + cursor = parse_query(uri_text, (cursor + 1)); + } + + if ((cursor != uri_text.end()) && (*cursor == '#')) { + cursor = parse_fragment(uri_text, (cursor + 1)); + } + + init_query_dictionary(); // If the query string is empty, this will be empty too. + + }; + + std::string::const_iterator parse_scheme( + std::string const &uri_text, + std::string::const_iterator scheme_start + ) + { + std::string::const_iterator scheme_end = scheme_start; + while ((scheme_end != uri_text.end()) && (*scheme_end != ':')) { + if (!(std::isalnum(*scheme_end) || (*scheme_end == '-') + || (*scheme_end == '+') || (*scheme_end == '.'))) { + throw std::invalid_argument( + "Invalid character found in the scheme component. Supplied URI was: \"" + + uri_text + "\"." + ); + } + ++scheme_end; + } + + if (scheme_end == uri_text.end()) { + throw std::invalid_argument( + "End of URI found while parsing the scheme. Supplied URI was: \"" + + uri_text + "\"." + ); + } + + if (scheme_start == scheme_end) { + throw std::invalid_argument( + "Scheme component cannot be zero-length. Supplied URI was: \"" + + uri_text + "\"." + ); + } + + m_scheme = std::move(std::string(scheme_start, scheme_end)); + return scheme_end; + }; + + std::string::const_iterator parse_content( + std::string const &uri_text, + std::string::const_iterator content_start + ) + { + std::string::const_iterator content_end = content_start; + while ((content_end != uri_text.end()) && (*content_end != '?') && (*content_end != '#')) { + ++content_end; + } + + m_content = std::move(std::string(content_start, content_end)); + + if ((m_category == scheme_category::Hierarchical) && (m_content.length() > 0)) { + // If it's a hierarchical URI, the content should be parsed for the hierarchical components. + std::string::const_iterator path_start = m_content.begin(); + std::string::const_iterator path_end = m_content.end(); + if (!m_content.compare(0, 2, "//")) { + // In this case an authority component is present. + std::string::const_iterator authority_cursor = (m_content.begin() + 2); + if (m_content.find_first_of('@') != std::string::npos) { + std::string::const_iterator userpass_divider = parse_username( + uri_text, + m_content, + authority_cursor + ); + authority_cursor = parse_password(uri_text, m_content, (userpass_divider + 1)); + // After this call, *authority_cursor == '@', so we skip over it. + ++authority_cursor; + } + + authority_cursor = parse_host(uri_text, m_content, authority_cursor); + + if ((authority_cursor != m_content.end()) && (*authority_cursor == ':')) { + authority_cursor = parse_port(uri_text, m_content, (authority_cursor + 1)); + } + + if ((authority_cursor != m_content.end()) && (*authority_cursor == '/')) { + // Then the path is rooted, and we should note this. + m_path_is_rooted = true; + path_start = authority_cursor + 1; + } + + // If we've reached the end and no path is present then set path_start + // to the end. + if (authority_cursor == m_content.end()) { + path_start = m_content.end(); + } + } + else if (!m_content.compare(0, 1, "/")) { + m_path_is_rooted = true; + ++path_start; + } + + // We can now build the path based on what remains in the content string, + // since that's all that exists after the host and optional port component. + m_path = std::move(std::string(path_start, path_end)); + } + return content_end; + }; + + std::string::const_iterator parse_username( + std::string const &uri_text, + std::string const &content, + std::string::const_iterator username_start + ) + { + std::string::const_iterator username_end = username_start; + // Since this is only reachable when '@' was in the content string, we can + // ignore the end-of-string case. + while (*username_end != ':') { + if (*username_end == '@') { + throw std::invalid_argument( + "Username must be followed by a password. Supplied URI was: \"" + + uri_text + "\"." + ); + } + ++username_end; + } + m_username = std::move(std::string(username_start, username_end)); + return username_end; + }; + + std::string::const_iterator parse_password( + std::string const &uri_text, + std::string const &content, + std::string::const_iterator password_start + ) + { + std::string::const_iterator password_end = password_start; + while (*password_end != '@') { + ++password_end; + } + + m_password = std::move(std::string(password_start, password_end)); + return password_end; + }; + + std::string::const_iterator parse_host( + std::string const &uri_text, + std::string const &content, + std::string::const_iterator host_start + ) + { + std::string::const_iterator host_end = host_start; + // So, the host can contain a few things. It can be a domain, it can be an + // IPv4 address, it can be an IPv6 address, or an IPvFuture literal. In the + // case of those last two, it's of the form [...] where what's between the + // brackets is a matter of which IPv?? version it is. + while (host_end != content.end()) { + if (*host_end == '[') { + // We're parsing an IPv6 or IPvFuture address, so we should handle that + // instead of the normal procedure. + while ((host_end != content.end()) && (*host_end != ']')) { + ++host_end; + } + + if (host_end == content.end()) { + throw std::invalid_argument( + "End of content component encountered " + "while parsing the host component. " + "Supplied URI was: \"" + + uri_text + "\"." + ); + } + + ++host_end; + break; + // We can stop looping, we found the end of the IP literal, which is the + // whole of the host component when one's in use. + } + else if ((*host_end == ':') || (*host_end == '/')) { + break; + } + else { + ++host_end; + } + } + + m_host = std::move(std::string(host_start, host_end)); + return host_end; + }; + + std::string::const_iterator parse_port( + std::string const &uri_text, + std::string const &content, + std::string::const_iterator port_start + ) + { + std::string::const_iterator port_end = port_start; + while ((port_end != content.end()) && (*port_end != '/')) { + if (!std::isdigit(*port_end)) { + throw std::invalid_argument( + "Invalid character while parsing the port. " + "Supplied URI was: \"" + uri_text + "\"." + ); + } + + ++port_end; + } + + m_port = std::stoul(std::string(port_start, port_end)); + return port_end; + }; + + std::string::const_iterator parse_query( + std::string const &uri_text, + std::string::const_iterator query_start + ) + { + std::string::const_iterator query_end = query_start; + while ((query_end != uri_text.end()) && (*query_end != '#')) { + // Queries can contain almost any character except hash, which is reserved + // for the start of the fragment. + ++query_end; + } + m_query = std::move(std::string(query_start, query_end)); + return query_end; + }; + + std::string::const_iterator parse_fragment( + std::string const &uri_text, + std::string::const_iterator fragment_start + ) + { + m_fragment = std::move(std::string(fragment_start, uri_text.end())); + return uri_text.end(); + }; + + void init_query_dictionary() + { + if (!m_query.empty()) { + // Loop over the query string looking for '&'s, then check each one for + // an '=' to find keys and values; if there's not an '=' then the key + // will have an empty value in the map. + char separator = (m_separator == query_argument_separator::ampersand) ? '&' : ';'; + size_t carat = 0; + size_t stanza_end = m_query.find_first_of(separator); + do { + std::string stanza = m_query.substr( + carat, + ((stanza_end != std::string::npos) ? (stanza_end - carat) : std::string::npos)); + size_t key_value_divider = stanza.find_first_of('='); + std::string key = stanza.substr(0, key_value_divider); + std::string value; + if (key_value_divider != std::string::npos) { + value = stanza.substr((key_value_divider + 1)); + } + + if (m_query_dict.count(key) != 0) { + throw std::invalid_argument("Bad key in the query string!"); + } + + m_query_dict.emplace(key, value); + carat = ((stanza_end != std::string::npos) ? (stanza_end + 1) + : std::string::npos); + stanza_end = m_query.find_first_of(separator, carat); + } while ((stanza_end != std::string::npos) + || (carat != std::string::npos)); + } + } + + std::string m_scheme; + std::string m_content; + std::string m_username; + std::string m_password; + std::string m_host; + std::string m_path; + std::string m_query; + std::string m_fragment; + + std::map m_query_dict; + + scheme_category m_category; + unsigned long m_port; + bool m_path_is_rooted; + query_argument_separator m_separator; +}; diff --git a/common/repositories/base/base_tool_game_objects_repository.h b/common/repositories/base/base_tool_game_objects_repository.h new file mode 100644 index 000000000..3e1f8600f --- /dev/null +++ b/common/repositories/base/base_tool_game_objects_repository.h @@ -0,0 +1,346 @@ +/** + * DO NOT MODIFY THIS FILE + * + * This repository was automatically generated and is NOT to be modified directly. + * Any repository modifications are meant to be made to the repository extending the base. + * Any modifications to base repositories are to be made by the generator only + * + * @generator ./utils/scripts/generators/repository-generator.pl + * @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories + */ + +#ifndef EQEMU_BASE_TOOL_GAME_OBJECTS_REPOSITORY_H +#define EQEMU_BASE_TOOL_GAME_OBJECTS_REPOSITORY_H + +#include "../../database.h" +#include "../../string_util.h" +#include + +class BaseToolGameObjectsRepository { +public: + struct ToolGameObjects { + int id; + int zoneid; + std::string zonesn; + std::string object_name; + std::string file_from; + int is_global; + }; + + static std::string PrimaryKey() + { + return std::string("id"); + } + + static std::vector Columns() + { + return { + "id", + "zoneid", + "zonesn", + "object_name", + "file_from", + "is_global", + }; + } + + static std::vector SelectColumns() + { + return { + "id", + "zoneid", + "zonesn", + "object_name", + "file_from", + "is_global", + }; + } + + static std::string ColumnsRaw() + { + return std::string(implode(", ", Columns())); + } + + static std::string SelectColumnsRaw() + { + return std::string(implode(", ", SelectColumns())); + } + + static std::string TableName() + { + return std::string("tool_game_objects"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + SelectColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static ToolGameObjects NewEntity() + { + ToolGameObjects entry{}; + + entry.id = 0; + entry.zoneid = 0; + entry.zonesn = ""; + entry.object_name = ""; + entry.file_from = ""; + entry.is_global = 0; + + return entry; + } + + static ToolGameObjects GetToolGameObjectsEntry( + const std::vector &tool_game_objectss, + int tool_game_objects_id + ) + { + for (auto &tool_game_objects : tool_game_objectss) { + if (tool_game_objects.id == tool_game_objects_id) { + return tool_game_objects; + } + } + + return NewEntity(); + } + + static ToolGameObjects FindOne( + Database& db, + int tool_game_objects_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE id = {} LIMIT 1", + BaseSelect(), + tool_game_objects_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + ToolGameObjects entry{}; + + entry.id = atoi(row[0]); + entry.zoneid = atoi(row[1]); + entry.zonesn = row[2] ? row[2] : ""; + entry.object_name = row[3] ? row[3] : ""; + entry.file_from = row[4] ? row[4] : ""; + entry.is_global = atoi(row[5]); + + return entry; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int tool_game_objects_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + tool_game_objects_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + ToolGameObjects tool_game_objects_entry + ) + { + std::vector update_values; + + auto columns = Columns(); + + update_values.push_back(columns[1] + " = " + std::to_string(tool_game_objects_entry.zoneid)); + update_values.push_back(columns[2] + " = '" + EscapeString(tool_game_objects_entry.zonesn) + "'"); + update_values.push_back(columns[3] + " = '" + EscapeString(tool_game_objects_entry.object_name) + "'"); + update_values.push_back(columns[4] + " = '" + EscapeString(tool_game_objects_entry.file_from) + "'"); + update_values.push_back(columns[5] + " = " + std::to_string(tool_game_objects_entry.is_global)); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + implode(", ", update_values), + PrimaryKey(), + tool_game_objects_entry.id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static ToolGameObjects InsertOne( + Database& db, + ToolGameObjects tool_game_objects_entry + ) + { + std::vector insert_values; + + insert_values.push_back(std::to_string(tool_game_objects_entry.id)); + insert_values.push_back(std::to_string(tool_game_objects_entry.zoneid)); + insert_values.push_back("'" + EscapeString(tool_game_objects_entry.zonesn) + "'"); + insert_values.push_back("'" + EscapeString(tool_game_objects_entry.object_name) + "'"); + insert_values.push_back("'" + EscapeString(tool_game_objects_entry.file_from) + "'"); + insert_values.push_back(std::to_string(tool_game_objects_entry.is_global)); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseInsert(), + implode(",", insert_values) + ) + ); + + if (results.Success()) { + tool_game_objects_entry.id = results.LastInsertedID(); + return tool_game_objects_entry; + } + + tool_game_objects_entry = NewEntity(); + + return tool_game_objects_entry; + } + + static int InsertMany( + Database& db, + std::vector tool_game_objects_entries + ) + { + std::vector insert_chunks; + + for (auto &tool_game_objects_entry: tool_game_objects_entries) { + std::vector insert_values; + + insert_values.push_back(std::to_string(tool_game_objects_entry.id)); + insert_values.push_back(std::to_string(tool_game_objects_entry.zoneid)); + insert_values.push_back("'" + EscapeString(tool_game_objects_entry.zonesn) + "'"); + insert_values.push_back("'" + EscapeString(tool_game_objects_entry.object_name) + "'"); + insert_values.push_back("'" + EscapeString(tool_game_objects_entry.file_from) + "'"); + insert_values.push_back(std::to_string(tool_game_objects_entry.is_global)); + + insert_chunks.push_back("(" + implode(",", insert_values) + ")"); + } + + std::vector insert_values; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseInsert(), + implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static std::vector All(Database& db) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{}", + BaseSelect() + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + ToolGameObjects entry{}; + + entry.id = atoi(row[0]); + entry.zoneid = atoi(row[1]); + entry.zonesn = row[2] ? row[2] : ""; + entry.object_name = row[3] ? row[3] : ""; + entry.file_from = row[4] ? row[4] : ""; + entry.is_global = atoi(row[5]); + + all_entries.push_back(entry); + } + + return all_entries; + } + + static std::vector GetWhere(Database& db, std::string where_filter) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {}", + BaseSelect(), + where_filter + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + ToolGameObjects entry{}; + + entry.id = atoi(row[0]); + entry.zoneid = atoi(row[1]); + entry.zonesn = row[2] ? row[2] : ""; + entry.object_name = row[3] ? row[3] : ""; + entry.file_from = row[4] ? row[4] : ""; + entry.is_global = atoi(row[5]); + + all_entries.push_back(entry); + } + + return all_entries; + } + + static int DeleteWhere(Database& db, std::string where_filter) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {}", + TableName(), + where_filter + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int Truncate(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "TRUNCATE TABLE {}", + TableName() + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + +}; + +#endif //EQEMU_BASE_TOOL_GAME_OBJECTS_REPOSITORY_H diff --git a/common/repositories/tool_game_objects_repository.h b/common/repositories/tool_game_objects_repository.h new file mode 100644 index 000000000..d4d6009b5 --- /dev/null +++ b/common/repositories/tool_game_objects_repository.h @@ -0,0 +1,70 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) + * + * 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_TOOL_GAME_OBJECTS_REPOSITORY_H +#define EQEMU_TOOL_GAME_OBJECTS_REPOSITORY_H + +#include "../database.h" +#include "../string_util.h" +#include "base/base_tool_game_objects_repository.h" + +class ToolGameObjectsRepository: public BaseToolGameObjectsRepository { +public: + + /** + * This file was auto generated and can be modified and extended upon + * + * Base repository methods are automatically + * generated in the "base" version of this repository. The base repository + * is immutable and to be left untouched, while methods in this class + * are used as extension methods for more specific persistence-layer + * accessors or mutators. + * + * Base Methods (Subject to be expanded upon in time) + * + * Note: Not all tables are designed appropriately to fit functionality with all base methods + * + * InsertOne + * UpdateOne + * DeleteOne + * FindOne + * GetWhere(std::string where_filter) + * DeleteWhere(std::string where_filter) + * InsertMany + * All + * + * Example custom methods in a repository + * + * ToolGameObjectsRepository::GetByZoneAndVersion(int zone_id, int zone_version) + * ToolGameObjectsRepository::GetWhereNeverExpires() + * ToolGameObjectsRepository::GetWhereXAndY() + * ToolGameObjectsRepository::DeleteWhereXAndY() + * + * Most of the above could be covered by base methods, but if you as a developer + * find yourself re-using logic for other parts of the code, its best to just make a + * method that can be re-used easily elsewhere especially if it can use a base repository + * method and encapsulate filters there + */ + + // Custom extended repository methods here + +}; + +#endif //EQEMU_TOOL_GAME_OBJECTS_REPOSITORY_H diff --git a/utils/scripts/generators/repository-generator.pl b/utils/scripts/generators/repository-generator.pl index b387a7d63..bd7514a93 100644 --- a/utils/scripts/generators/repository-generator.pl +++ b/utils/scripts/generators/repository-generator.pl @@ -157,7 +157,7 @@ foreach my $table_to_generate (@tables) { $table_found_in_schema = 0; } - if ($table_found_in_schema == 0) { + if ($table_found_in_schema == 0 && ($requested_table_to_generate eq "" || $requested_table_to_generate eq "all")) { print "Table [$table_to_generate] not found in schema, skipping\n"; next; } diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 184b7d318..9f519b498 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -80,6 +80,7 @@ SET(zone_sources fearpath.cpp forage.cpp global_loot_manager.cpp + gm_commands/door_manipulation.cpp groups.cpp guild.cpp guild_mgr.cpp @@ -198,6 +199,7 @@ SET(zone_headers fastmath.h forage.h global_loot_manager.h + gm_commands/door_manipulation.h groups.h guild_mgr.h hate_list.h diff --git a/zone/client.cpp b/zone/client.cpp index 7584a0bd1..2ad737405 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -10515,3 +10515,13 @@ void Client::ApplyWeaponsStance() weaponstance.spellbonus_enabled = false; } } + +uint16 Client::GetDoorToolEntityId() const +{ + return m_door_tool_entity_id; +} + +void Client::SetDoorToolEntityId(uint16 door_tool_entity_id) +{ + Client::m_door_tool_entity_id = door_tool_entity_id; +} diff --git a/zone/client.h b/zone/client.h index cb39637a1..966d7976c 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1764,9 +1764,17 @@ private: int Haste; //precalced value uint32 tmSitting; // time stamp started sitting, used for HP regen bonus added on MAY 5, 2004 + + // dev tools bool display_mob_info_window; bool dev_tools_enabled; + uint16 m_door_tool_entity_id; +public: + uint16 GetDoorToolEntityId() const; + void SetDoorToolEntityId(uint16 door_tool_entity_id); +private: + int32 max_end; int32 current_endurance; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index dd22111fa..0a05d7580 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -66,6 +66,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "../common/repositories/character_instance_safereturns_repository.h" #include "../common/repositories/criteria/content_filter_criteria.h" #include "../common/shared_tasks.h" +#include "gm_commands/door_manipulation.h" #ifdef BOTS #include "bot.h" @@ -4356,6 +4357,20 @@ void Client::Handle_OP_ClickDoor(const EQApplicationPacket *app) return; } + // set door selected + if (IsDevToolsEnabled()) { + SetDoorToolEntityId(currentdoor->GetEntityID()); + DoorManipulation::CommandHeader(this); + Message( + Chat::White, + fmt::format( + "Door ({}) [{}]", + currentdoor->GetEntityID(), + EQ::SayLinkEngine::GenerateQuestSaylink("#door edit", false, "#door edit") + ).c_str() + ); + } + char buf[20]; snprintf(buf, 19, "%u", cd->doorid); buf[19] = '\0'; diff --git a/zone/command.cpp b/zone/command.cpp index 4c9bcc0bb..4db232d80 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -78,6 +78,7 @@ #include "../common/content/world_content_service.h" #include "../common/http/httplib.h" #include "../common/shared_tasks.h" +#include "gm_commands/door_manipulation.h" extern QueryServ* QServ; extern WorldServer worldserver; @@ -202,6 +203,7 @@ int command_init(void) command_add("disablerecipe", "[recipe_id] - Disables a recipe using the recipe id.", 80, command_disablerecipe) || command_add("disarmtrap", "Analog for ldon disarm trap for the newer clients since we still don't have it working.", 80, command_disarmtrap) || command_add("distance", "- Reports the distance between you and your target.", 80, command_distance) || + command_add("door", "Door editing command", 80, command_door) || command_add("doanim", "[animnum] [type] - Send an EmoteAnim for you or your target", 50, command_doanim) || command_add("dz", "Manage expeditions and dynamic zone instances", 80, command_dz) || command_add("dzkickplayers", "Removes all players from current expedition. (/kickplayers alternative for pre-RoF clients)", 0, command_dzkickplayers) || @@ -12940,6 +12942,10 @@ void command_distance(Client *c, const Seperator *sep) { } } +void command_door(Client *c, const Seperator *sep) { + DoorManipulation::CommandHandler(c, sep); +} + void command_cvs(Client *c, const Seperator *sep) { if(c) diff --git a/zone/command.h b/zone/command.h index cb3f5ea04..9c2aef51b 100644 --- a/zone/command.h +++ b/zone/command.h @@ -90,6 +90,7 @@ void command_devtools(Client *c, const Seperator *sep); void command_details(Client *c, const Seperator *sep); void command_disablerecipe(Client *c, const Seperator *sep); void command_disarmtrap(Client *c, const Seperator *sep); +void command_door(Client *c, const Seperator *sep); void command_distance(Client *c, const Seperator *sep); void command_doanim(Client *c, const Seperator *sep); void command_dz(Client *c, const Seperator *sep); diff --git a/zone/doors.cpp b/zone/doors.cpp index ba3d36d21..2458e7f69 100644 --- a/zone/doors.cpp +++ b/zone/doors.cpp @@ -801,6 +801,12 @@ void Doors::SetIncline(int in) { entity_list.RespawnAllDoors(); } +void Doors::SetInvertState(int in) { + entity_list.DespawnAllDoors(); + invert_state = in; + entity_list.RespawnAllDoors(); +} + void Doors::SetOpenType(uint8 in) { entity_list.DespawnAllDoors(); open_type = in; diff --git a/zone/doors.h b/zone/doors.h index d7fbfc021..455dfd03f 100644 --- a/zone/doors.h +++ b/zone/doors.h @@ -54,6 +54,7 @@ public: void SetDoorName(const char *name); void SetEntityID(uint32 entity) { entity_id = entity; } void SetIncline(int in); + void SetInvertState(int in); void SetKeyItem(uint32 in) { key_item_id = in; } void SetLocation(float x, float y, float z); void SetLockpick(uint16 in) { lockpick = in; } diff --git a/zone/gm_commands/door_manipulation.cpp b/zone/gm_commands/door_manipulation.cpp new file mode 100644 index 000000000..c9a950e52 --- /dev/null +++ b/zone/gm_commands/door_manipulation.cpp @@ -0,0 +1,779 @@ +#include "door_manipulation.h" +#include "../doors.h" +#include "../../common/misc_functions.h" + +#define MAX_CLIENT_MESSAGE_LENGTH 2000 + +void DoorManipulation::CommandHandler(Client *c, const Seperator *sep) +{ + // this should never happen + if (!c) { + return; + } + + // args + std::string arg1(sep->arg[1]); + std::string arg2(sep->arg[2]); + std::string arg3(sep->arg[3]); + + // table check + std::string table_name = "tool_game_objects"; + std::string url = "https://raw.githubusercontent.com/EQEmu/database-tool-sqls/main/tool_game_objects.sql"; + if (!database.DoesTableExist(table_name)) { + c->Message( + Chat::White, + fmt::format( + "Table [{}] does not exist. Downloading from [{}] and installing locally", + table_name, + url + ).c_str() + ); + database.SourceDatabaseTableFromUrl( + table_name, + url + ); + } + + // option + if (arg1.empty()) { + DoorManipulation::CommandHeader(c); + c->Message(Chat::White, "#door create | Creates a door from a model. (Example IT78 creates a campfire)"); + c->Message(Chat::White, "#door setinvertstate [0|1] | Sets selected door invert state"); + c->Message(Chat::White, "#door setincline | Sets selected door incline"); + c->Message(Chat::White, "#door opentype | Sets selected door opentype"); + c->Message( + Chat::White, + fmt::format( + "#door model | Changes door model for selected door or select from [{}] or [{}]", + EQ::SayLinkEngine::GenerateQuestSaylink("#door showmodelszone", false, "local zone"), + EQ::SayLinkEngine::GenerateQuestSaylink("#door showmodelsglobal", false, "global") + ).c_str() + ); + c->Message( + Chat::White, + "#door showmodelsfromfile | Shows models from s3d or eqg file. Example tssequip.eqg or wallet01.eqg" + ); + + c->Message( + Chat::White, + fmt::format( + "{} | Shows available models in the current zone that you are in", + EQ::SayLinkEngine::GenerateQuestSaylink("#door showmodelszone", false, "#door showmodelszone") + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "{} | Shows available models globally by first listing all global model files", + EQ::SayLinkEngine::GenerateQuestSaylink("#door showmodelsglobal", false, "#door showmodelsglobal") + ).c_str() + ); + + c->Message(Chat::White, "#door save | Creates database entry for selected door"); + c->Message( + Chat::White, + fmt::format( + "{} - Brings up editing interface for selected door", + EQ::SayLinkEngine::GenerateQuestSaylink("#door edit", false, "#door edit") + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "{} - lists doors in zone", + EQ::SayLinkEngine::GenerateQuestSaylink("#list doors", false, "#list doors") + ).c_str() + ); + + return; + } + + // edit menu + if (arg1 == "edit") { + Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId()); + if (door) { + c->Message( + Chat::White, + fmt::format( + "Door Selected ID [{}] Name [{}] OpenType [{}] Invertstate [{} | {}/{}] ", + c->GetDoorToolEntityId(), + door->GetDoorName(), + door->GetOpenType(), + door->GetInvertState(), + EQ::SayLinkEngine::GenerateQuestSaylink("#door setinvertstate 0", false, "0"), + EQ::SayLinkEngine::GenerateQuestSaylink("#door setinvertstate 1", false, "1") + ).c_str() + ); + + const std::string move_x_action = "move_x"; + const std::string move_y_action = "move_y"; + const std::string move_z_action = "move_z"; + const std::string move_h_action = "move_h"; + const std::string set_size_action = "set_size"; + + std::vector move_options = { + move_x_action, + move_y_action, + move_z_action, + move_h_action, + set_size_action + }; + std::vector move_x_options_positive; + std::vector move_x_options_negative; + std::vector move_y_options_positive; + std::vector move_y_options_negative; + std::vector move_z_options_positive; + std::vector move_z_options_negative; + std::vector move_h_options_positive; + std::vector move_h_options_negative; + std::vector set_size_options_positive; + std::vector set_size_options_negative; + for (const auto &move_option : move_options) { + if (move_option == move_x_action) { + move_x_options_positive.emplace_back( + EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#door edit {} .25", move_option), + false, + ".25" + ) + ); + + for (int move_index = 0; move_index <= 15; move_index += 5) { + int value = (move_index == 0 ? 1 : move_index); + move_x_options_positive.emplace_back( + EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#door edit {} {}", move_option, value), + false, + fmt::format("{}", std::abs(value)) + ) + ); + } + + for (int move_index = -15; move_index <= 0; move_index += 5) { + int value = (move_index == 0 ? 1 : move_index); + move_x_options_negative.emplace_back( + EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#door edit {} {}", move_option, value), + false, + fmt::format("{}", std::abs(value)) + ) + ); + } + + move_x_options_negative.emplace_back( + EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#door edit {} -.25", move_option), + false, + "-.25" + ) + ); + } + else if (move_option == move_y_action) { + move_y_options_positive.emplace_back( + EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#door edit {} .25", move_option), + false, + ".25" + ) + ); + + for (int move_index = 0; move_index <= 15; move_index += 5) { + int value = (move_index == 0 ? 1 : move_index); + move_y_options_positive.emplace_back( + EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#door edit {} {}", move_option, value), + false, + fmt::format("{}", std::abs(value)) + ) + ); + } + + for (int move_index = -15; move_index <= 0; move_index += 5) { + int value = (move_index == 0 ? 1 : move_index); + move_y_options_negative.emplace_back( + EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#door edit {} {}", move_option, value), + false, + fmt::format("{}", std::abs(value)) + ) + ); + } + + move_y_options_negative.emplace_back( + EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#door edit {} -.25", move_option), + false, + "-.25" + ) + ); + } + else if (move_option == move_z_action) { + move_z_options_positive.emplace_back( + EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#door edit {} .25", move_option), + false, + ".25" + ) + ); + + for (int move_index = 0; move_index <= 15; move_index += 5) { + int value = (move_index == 0 ? 1 : move_index); + move_z_options_positive.emplace_back( + EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#door edit {} {}", move_option, value), + false, + fmt::format("{}", std::abs(value)) + ) + ); + } + + for (int move_index = -15; move_index <= 0; move_index += 5) { + int value = (move_index == 0 ? 1 : move_index); + move_z_options_negative.emplace_back( + EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#door edit {} {}", move_option, value), + false, + fmt::format("{}", std::abs(value)) + ) + ); + } + + move_z_options_negative.emplace_back( + EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#door edit {} -.25", move_option), + false, + "-.25" + ) + ); + } + else if (move_option == move_h_action) { + for (int move_index = 0; move_index <= 50; move_index += 5) { + int value = (move_index == 0 ? 1 : move_index); + move_h_options_positive.emplace_back( + EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#door edit {} {}", move_option, value), + false, + fmt::format("{}", std::abs(value)) + ) + ); + } + + for (int move_index = -50; move_index <= 0; move_index += 5) { + int value = (move_index == 0 ? 1 : move_index); + move_h_options_negative.emplace_back( + EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#door edit {} {}", move_option, value), + false, + fmt::format("{}", std::abs(value)) + ) + ); + } + } + else if (move_option == set_size_action) { + for (int move_index = 0; move_index <= 100; move_index += 10) { + int value = (move_index == 0 ? 1 : move_index); + set_size_options_positive.emplace_back( + EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#door edit {} {}", move_option, value), + false, + fmt::format("{}", std::abs(value)) + ) + ); + } + + for (int move_index = -100; move_index <= 0; move_index += 10) { + int value = (move_index == 0 ? 1 : move_index); + set_size_options_negative.emplace_back( + EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#door edit {} {}", move_option, value), + false, + fmt::format("{}", std::abs(value)) + ) + ); + } + } + } + + // we're passing a move action here + if (!arg3.empty() && StringIsNumber(arg3)) { + int x_move = 0; + int y_move = 0; + int z_move = 0; + int h_move = 0; + int set_size = 0; + + if (arg2 == move_x_action) { + x_move = std::atoi(arg3.c_str()); + } + if (arg2 == move_y_action) { + y_move = std::atoi(arg3.c_str()); + } + if (arg2 == move_z_action) { + z_move = std::atoi(arg3.c_str()); + } + if (arg2 == move_h_action) { + h_move = std::atoi(arg3.c_str()); + } + if (arg2 == set_size_action) { + set_size = std::atoi(arg3.c_str()); + } + + door->SetLocation( + door->GetX() + x_move, + door->GetY() + y_move, + door->GetZ() + z_move + ); + + glm::vec4 door_position = door->GetPosition(); + door_position.w = door_position.w + h_move; + door->SetPosition(door_position); + door->SetSize(door->GetSize() + set_size); + } + + // spawn and move helpers + uint16 helper_mob_x_negative = 0; + uint16 helper_mob_x_positive = 0; + uint16 helper_mob_y_positive = 0; + uint16 helper_mob_y_negative = 0; + + for (auto &n: entity_list.GetNPCList()) { + NPC *npc = n.second; + std::string npc_name = npc->GetName(); + if (npc_name.find("-X") != std::string::npos) { + helper_mob_x_negative = npc->GetID(); + } + if (npc_name.find("-Y") != std::string::npos) { + helper_mob_y_negative = npc->GetID(); + } + if (npc_name.find("+X") != std::string::npos) { + helper_mob_x_positive = npc->GetID(); + } + if (npc_name.find("+Y") != std::string::npos) { + helper_mob_y_positive = npc->GetID(); + } + } + + // -X + glm::vec4 door_position = door->GetPosition(); + if (helper_mob_x_negative == 0) { + door_position.x = door_position.x - 15; + helper_mob_x_negative = NPC::SpawnNodeNPC("-X", "", door_position)->GetID(); + } + else { + auto n = entity_list.GetNPCByID(helper_mob_x_negative); + n->GMMove(door->GetX() - 15, door->GetY(), door->GetZ(), n->GetHeading()); + } + + // +X + door_position = door->GetPosition(); + if (helper_mob_x_positive == 0) { + door_position.x = door_position.x + 15; + helper_mob_x_positive = NPC::SpawnNodeNPC("+X", "", door_position)->GetID(); + } + else { + auto n = entity_list.GetNPCByID(helper_mob_x_positive); + n->GMMove(door->GetX() + 15, door->GetY(), door->GetZ(), n->GetHeading()); + } + + // -Y + door_position = door->GetPosition(); + if (helper_mob_y_negative == 0) { + door_position.y = door_position.y - 15; + helper_mob_y_negative = NPC::SpawnNodeNPC("-Y", "", door_position)->GetID(); + } + else { + auto n = entity_list.GetNPCByID(helper_mob_y_negative); + n->GMMove(door->GetX(), door->GetY() - 15, door->GetZ(), n->GetHeading()); + } + + // +Y + door_position = door->GetPosition(); + if (helper_mob_y_positive == 0) { + door_position.y = door_position.y + 15; + helper_mob_y_positive = NPC::SpawnNodeNPC("+Y", "", door_position)->GetID(); + } + else { + auto n = entity_list.GetNPCByID(helper_mob_y_positive); + n->GMMove(door->GetX(), door->GetY() + 15, door->GetZ(), n->GetHeading()); + } + + c->Message( + Chat::White, + fmt::format( + "Name [{}] [{}] [{}] [{}]", + door->GetDoorName(), + EQ::SayLinkEngine::GenerateQuestSaylink( + "#door save", + false, + "Save" + ), + EQ::SayLinkEngine::GenerateQuestSaylink( + "#door changemodelqueue", + false, + "Change Model" + ), + EQ::SayLinkEngine::GenerateQuestSaylink( + "#door setinclineinc", + false, + "Incline" + ) + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "[{}] - [X] + [{}]", + implode(" | ", move_x_options_negative), + implode(" | ", move_x_options_positive) + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "[{}] - [Y] + [{}]", + implode(" | ", move_y_options_negative), + implode(" | ", move_y_options_positive) + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "[{}] - [Z] + [{}]", + implode(" | ", move_z_options_negative), + implode(" | ", move_z_options_positive) + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "[{}] - [H] + [{}]", + implode(" | ", move_h_options_negative), + implode(" | ", move_h_options_positive) + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "[{}] - [Size] + [{}]", + implode(" | ", set_size_options_negative), + implode(" | ", set_size_options_positive) + ).c_str() + ); + + return; + } + + c->Message(Chat::Red, "Door selection invalid..."); + } + + // create + if (arg1 == "create") { + std::string model = str_toupper(arg2); + uint16 entity_id = entity_list.CreateDoor( + model.c_str(), + c->GetPosition(), + 58, + 100 + ); + + c->Message( + Chat::White, + fmt::format("Creating door entity_id [{}] with model [{}]", entity_id, model).c_str()); + c->SetDoorToolEntityId(entity_id); + } + + // set model + if (arg1 == "model") { + Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId()); + std::string model = str_toupper(arg2); + if (door) { + door->SetDoorName(model.c_str()); + } + } + + // change model queue + if (arg1 == "changemodelqueue") { + c->Message( + Chat::White, + fmt::format( + "#door model | Changes door model for selected door or select from [{}] or [{}]", + EQ::SayLinkEngine::GenerateQuestSaylink("#door showmodelszone", false, "local zone"), + EQ::SayLinkEngine::GenerateQuestSaylink("#door showmodelsglobal", false, "global") + ).c_str() + ); + } + + // open type + if (arg1 == "opentype" && !arg2.empty() && StringIsNumber(arg2)) { + Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId()); + if (door) { + door->SetOpenType(std::atoi(arg2.c_str())); + } + } + + // incline + if (arg1 == "setincline" && !arg2.empty() && StringIsNumber(arg2)) { + Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId()); + if (door) { + door->SetIncline(std::atoi(arg2.c_str())); + } + } + + // incline + if (arg1 == "setinvertstate" && !arg2.empty() && StringIsNumber(arg2)) { + Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId()); + if (door) { + door->SetInvertState(std::atoi(arg2.c_str())); + } + } + + // save + if (arg1 == "save") { + Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId()); + if (door) { + door->CreateDatabaseEntry(); + c->Message(Chat::White, "Door saved"); + } + } + + // incline incremental + if (arg1 == "setinclineinc" && !arg2.empty() && StringIsNumber(arg2)) { + Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId()); + if (door) { + door->SetIncline(door->GetIncline() + std::atoi(arg2.c_str())); + } + } + if (arg1 == "setinclineinc") { + std::map incline_values = { + {.01, "Upright"}, + {63.75, "45 Degrees",}, + {130, "90 Degrees"}, + {192.5, "135 Degrees"}, + {255, "180 Degrees"}, + {321.25, "225 Degrees"}, + {385, "270 Degrees"}, + {448.75, "315 Degrees"}, + {512.5, "360 Degrees"} + }; + + std::vector incline_normal_options; + std::vector incline_positive_options; + std::vector incline_negative_options; + for (auto incline_value : incline_values) { + incline_normal_options.emplace_back( + EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format( + "#door setincline {}", + incline_value.first + ), + false, + incline_value.second + ) + ); + } + + for (int incline_index = 0; incline_index <= 100; incline_index += 10) { + int incline_value = (incline_index == 0 ? 1 : incline_index); + incline_positive_options.emplace_back( + EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format( + "#door setinclineinc {}", + incline_value + ), + false, + itoa(std::abs(incline_value)) + ) + ); + } + + for (int incline_index = -100; incline_index <= 1; incline_index += 10) { + int incline_value = (incline_index == 0 ? -1 : incline_index); + incline_negative_options.emplace_back( + EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format( + "#door setinclineinc {}", + incline_value + ), + false, + itoa(std::abs(incline_value)) + ) + ); + } + + c->Message( + Chat::White, + fmt::format( + "[Incline] [{}]", + implode(" | ", incline_normal_options) + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "[Incline Increments] [{}] - | + [{}]", + implode(" | ", incline_negative_options), + implode(" | ", incline_positive_options) + ).c_str() + ); + } + + // show models in zone + if (arg1 == "showmodelsglobal") { + auto game_objects = ToolGameObjectsRepository::GetWhere( + database, + "object_name LIKE '%IT%' AND zoneid = 0 AND object_name NOT LIKE '%OBJ%' GROUP by file_from" + ); + + if (game_objects.empty()) { + c->Message(Chat::White, "There are no models to display..."); + } + + c->Message(Chat::White, "------------------------------------------------"); + c->Message(Chat::White, "# Models (Global)"); + c->Message(Chat::White, "------------------------------------------------"); + + DisplayModelsFromFileResults(c, game_objects); + } + + // show models in zone + if (arg1 == "showmodelszone") { + auto game_objects = ToolGameObjectsRepository::GetWhere( + database, + fmt::format("zoneid = {}", zone->GetZoneID()) + ); + + if (game_objects.empty()) { + c->Message(Chat::White, "There are no models for this zone..."); + } + + c->Message(Chat::White, "------------------------------------------------"); + c->Message(Chat::White, "# Models from zone"); + c->Message(Chat::White, "------------------------------------------------"); + + DisplayObjectResultToClient(c, game_objects); + } + + // show models from file name + if (arg1 == "showmodelsfromfile" && !arg2.empty()) { + const std::string &file_name = arg2; + auto game_objects = ToolGameObjectsRepository::GetWhere( + database, + fmt::format("file_from = '{}'", file_name) + ); + + if (game_objects.empty()) { + c->Message(Chat::White, "There are no models for this zone..."); + } + + c->Message(Chat::White, "------------------------------------------------"); + c->Message(Chat::White, fmt::format("# Models from file name [{}]", file_name).c_str()); + c->Message(Chat::White, "------------------------------------------------"); + + DisplayObjectResultToClient(c, game_objects); + } +} + +void DoorManipulation::CommandHeader(Client *c) +{ + c->Message(Chat::White, "------------------------------------------------"); + c->Message(Chat::White, "# Door Commands"); + c->Message(Chat::White, "------------------------------------------------"); +} + +void DoorManipulation::DisplayObjectResultToClient( + Client *c, + std::vector game_objects +) +{ + std::vector say_links; + + for (auto &g: game_objects) { + say_links.emplace_back( + fmt::format( + "[{}] ", + EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#door model {}", g.object_name), + false, + g.object_name + ) + ) + ); + } + + int character_length = 0; + std::vector buffered_links; + + for (auto &links: say_links) { + buffered_links.emplace_back(links); + character_length += links.length(); + + // empty buffer + if (character_length > MAX_CLIENT_MESSAGE_LENGTH) { + std::string message_buffer; + + for (auto &buffered_link: buffered_links) { + message_buffer += buffered_link; + } + + c->Message(Chat::White, message_buffer.c_str()); + + // reset + character_length = 0; + buffered_links = {}; + } + } + + if (!buffered_links.empty()) { + c->Message(Chat::White, implode(" ", buffered_links).c_str()); + } +} + +void DoorManipulation::DisplayModelsFromFileResults( + Client *c, + std::vector game_objects +) +{ + std::vector say_links; + + for (auto &g: game_objects) { + say_links.emplace_back( + fmt::format( + "[{}] ", + EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#door showmodelsfromfile {}", g.file_from), + false, + g.file_from + ) + ) + ); + } + + int character_length = 0; + std::vector buffered_links; + + for (auto &links: say_links) { + buffered_links.emplace_back(links); + character_length += links.length(); + + // empty buffer + if (character_length > MAX_CLIENT_MESSAGE_LENGTH) { + std::string message_buffer; + + for (auto &buffered_link: buffered_links) { + message_buffer += buffered_link; + } + + c->Message(Chat::White, message_buffer.c_str()); + + // reset + character_length = 0; + buffered_links = {}; + } + } + + if (!buffered_links.empty()) { + c->Message(Chat::White, implode(" ", buffered_links).c_str()); + } +} diff --git a/zone/gm_commands/door_manipulation.h b/zone/gm_commands/door_manipulation.h new file mode 100644 index 000000000..d448c3bc7 --- /dev/null +++ b/zone/gm_commands/door_manipulation.h @@ -0,0 +1,23 @@ +#ifndef EQEMU_DOOR_MANIPULATION_H +#define EQEMU_DOOR_MANIPULATION_H + +#include "../client.h" +#include "../../common/repositories/tool_game_objects_repository.h" + +class DoorManipulation { + +public: + static void CommandHandler(Client *c, const Seperator *sep); + static void CommandHeader(Client *c); + static void DisplayObjectResultToClient( + Client *c, + std::vector game_objects + ); + static void DisplayModelsFromFileResults( + Client *c, + std::vector game_objects + ); +}; + + +#endif //EQEMU_DOOR_MANIPULATION_H diff --git a/zone/npc.cpp b/zone/npc.cpp index d7e4aa796..85a843ca0 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -284,7 +284,7 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi entity_list.MakeNameUnique(name); npc_aggro = npc_type_data->npc_aggro; - + AISpellVar.fail_recast = static_cast(RuleI(Spells, AI_SpellCastFinishedFailRecast)); AISpellVar.engaged_no_sp_recast_min = static_cast(RuleI(Spells, AI_EngagedNoSpellMinRecast)); AISpellVar.engaged_no_sp_recast_max = static_cast(RuleI(Spells, AI_EngagedNoSpellMaxRecast)); @@ -694,7 +694,7 @@ bool NPC::HasItem(uint32 item_id) { if (loot_item->item_id == item_id) { return true; } - } + } return false; } @@ -1124,7 +1124,7 @@ bool NPC::SpawnZoneController() memset(npc_type, 0, sizeof(NPCType)); strncpy(npc_type->name, "zone_controller", 60); - npc_type->current_hp = 2000000000; + npc_type->current_hp = 2000000000; npc_type->max_hp = 2000000000; npc_type->hp_regen = 100000000; npc_type->race = 240; @@ -1207,7 +1207,7 @@ void NPC::SpawnGridNodeNPC(const glm::vec4 &position, int32 grid_id, int32 grid_ entity_list.AddNPC(npc); } -void NPC::SpawnZonePointNodeNPC(std::string name, const glm::vec4 &position) +NPC * NPC::SpawnZonePointNodeNPC(std::string name, const glm::vec4 &position) { auto npc_type = new NPCType; memset(npc_type, 0, sizeof(NPCType)); @@ -1235,6 +1235,8 @@ void NPC::SpawnZonePointNodeNPC(std::string name, const glm::vec4 &position) npc_type->show_name = true; npc_type->findable = true; + strcpy(npc_type->special_abilities, "12,1^13,1^14,1^15,1^16,1^17,1^19,1^22,1^24,1^25,1^28,1^31,1^35,1^39,1^42,1"); + auto node_position = glm::vec4(position.x, position.y, position.z, position.w); auto npc = new NPC(npc_type, nullptr, node_position, GravityBehavior::Flying); @@ -1243,14 +1245,15 @@ void NPC::SpawnZonePointNodeNPC(std::string name, const glm::vec4 &position) npc->GiveNPCTypeData(npc_type); entity_list.AddNPC(npc); + + return npc; } NPC * NPC::SpawnNodeNPC(std::string name, std::string last_name, const glm::vec4 &position) { auto npc_type = new NPCType; memset(npc_type, 0, sizeof(NPCType)); - sprintf(npc_type->name, "%s", name.c_str()); - sprintf(npc_type->lastname, "%s", last_name.c_str()); + strncpy(npc_type->name, name.c_str(), 60); npc_type->current_hp = 4000000; npc_type->max_hp = 4000000; @@ -1272,9 +1275,13 @@ NPC * NPC::SpawnNodeNPC(std::string name, std::string last_name, const glm::vec4 npc_type->findable = true; npc_type->runspeed = 1.25; + strcpy(npc_type->special_abilities, "12,1^13,1^14,1^15,1^16,1^17,1^19,1^22,1^24,1^25,1^28,1^31,1^35,1^39,1^42,1"); + auto node_position = glm::vec4(position.x, position.y, position.z, position.w); auto npc = new NPC(npc_type, nullptr, node_position, GravityBehavior::Flying); + npc->name[strlen(npc->name) - 3] = (char) NULL; + npc->GiveNPCTypeData(npc_type); entity_list.AddNPC(npc, true, true); diff --git a/zone/npc.h b/zone/npc.h index 1521eb4de..c758ee19f 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -115,7 +115,7 @@ public: static NPC *SpawnNodeNPC(std::string name, std::string last_name, const glm::vec4 &position); static void SpawnGridNodeNPC(const glm::vec4 &position, int32 grid_id, int32 grid_number, int32 zoffset); - static void SpawnZonePointNodeNPC(std::string name, const glm::vec4 &position); + static NPC * SpawnZonePointNodeNPC(std::string name, const glm::vec4 &position); //abstract virtual function implementations requird by base abstract class virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, EQ::skills::SkillType attack_skill); From 6b93130c1335942fdd8f1fdfb98b57906aa6c83c Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 12 Sep 2021 22:08:30 -0500 Subject: [PATCH 183/624] [Saylinks] Implement Auto Saylink Injection (#1525) * Implement auto saylink injection * Cover Lua say since it takes a different code path --- common/ruletypes.h | 2 ++ common/say_link.cpp | 50 +++++++++++++++++++++++++++++++++++++++--- common/say_link.h | 7 +++--- zone/embparser_api.cpp | 10 ++++----- zone/entity.cpp | 16 +++++++++++--- zone/lua_mob.cpp | 12 ++++++++-- zone/mob.cpp | 17 ++++++++++---- zone/perl_mob.cpp | 28 ++++++++++++++--------- 8 files changed, 112 insertions(+), 30 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 37bf96711..9457313ec 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -598,6 +598,8 @@ RULE_INT(Chat, IntervalDurationMS, 60000, "Interval length in milliseconds") RULE_INT(Chat, KarmaUpdateIntervalMS, 1200000, "Karma update interval in milliseconds") RULE_INT(Chat, KarmaGlobalChatLimit, 72, "Amount of karma you need to be able to talk in ooc/auction/chat below the level limit") RULE_INT(Chat, GlobalChatLevelLimit, 8, "Level limit you need to of reached to talk in ooc/auction/chat if your karma is too low") +RULE_BOOL(Chat, AutoInjectSaylinksToSay, true, "Automatically injects saylinks into dialogue that has [brackets in them]") +RULE_BOOL(Chat, AutoInjectSaylinksToClientMessage, true, "Automatically injects saylinks into dialogue that has [brackets in them]") RULE_CATEGORY_END() RULE_CATEGORY(Merchant) diff --git a/common/say_link.cpp b/common/say_link.cpp index b47897112..d5083384b 100644 --- a/common/say_link.cpp +++ b/common/say_link.cpp @@ -1,5 +1,5 @@ /* EQEMu: Everquest Server Emulator - + Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net) This program is free software; you can redistribute it and/or modify @@ -11,7 +11,7 @@ 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 @@ -339,4 +339,48 @@ std::string EQ::SayLinkEngine::GenerateQuestSaylink(std::string saylink_text, bo linker.SetProxyText(link_name.c_str()); return linker.GenerateLink(); -} \ No newline at end of file +} + +std::string EQ::SayLinkEngine::InjectSaylinksIfNotExist(const char *message) +{ + std::string new_message = message; + + int link_index = 0; + std::vector links = {}; + + // loop through brackets until none exist + while (new_message.find('[') != std::string::npos && new_message.find(']') != std::string::npos) { + std::string bracket_message = get_between(new_message, "[", "]"); + + // already a saylink + // todo: improve this later + if (!bracket_message.empty() && bracket_message.length() > 50) { + links.emplace_back(bracket_message); + } + else { + links.emplace_back(EQ::SayLinkEngine::GenerateQuestSaylink(bracket_message, false, bracket_message)); + } + + // replace with anchor + find_replace( + new_message, + fmt::format("[{}]", bracket_message), + fmt::format("", link_index) + ); + + link_index++; + } + + // pop links onto anchors + link_index = 0; + for (auto &link: links) { + find_replace( + new_message, + fmt::format("", link_index), + fmt::format("[{}]", link) + ); + link_index++; + } + + return new_message; +} diff --git a/common/say_link.h b/common/say_link.h index c0ea5a235..982b9ec58 100644 --- a/common/say_link.h +++ b/common/say_link.h @@ -1,17 +1,17 @@ /* EQEMu: Everquest Server Emulator - + Copyright (C) 2001-2016 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 @@ -105,6 +105,7 @@ namespace EQ void Reset(); + static std::string InjectSaylinksIfNotExist(const char *message); private: void generate_body(); void generate_text(); diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 89125d713..a8c14d669 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -1593,7 +1593,7 @@ XS(XS__addldonpoints) { dXSARGS; if (items != 2) Perl_croak(aTHX_ "Usage: quest::addldonpoints(uint32 theme_id, int points)"); - + uint32 theme_id = (uint32) SvUV(ST(0)); int points = (int) SvIV(ST(1)); quest_manager.addldonpoints(theme_id, points); @@ -6483,7 +6483,7 @@ XS(XS__gethexcolorcode) { sv_setpv(TARG, hex_color_code.c_str()); XSprePUSH; PUSHTARG; - XSRETURN(1); + XSRETURN(1); } XS(XS__getaaexpmodifierbycharid); @@ -6491,7 +6491,7 @@ XS(XS__getaaexpmodifierbycharid) { dXSARGS; if (items != 2) Perl_croak(aTHX_ "Usage: quest::getaaexpmodifierbycharid(uint32 character_id, uint32 zone_id)"); - + dXSTARG; double aa_modifier; uint32 character_id = (uint32) SvUV(ST(0)); @@ -6507,7 +6507,7 @@ XS(XS__getexpmodifierbycharid) { dXSARGS; if (items != 2) Perl_croak(aTHX_ "Usage: quest::getexpmodifierbycharid(uint32 character_id, uint32 zone_id)"); - + dXSTARG; double exp_modifier; uint32 character_id = (uint32) SvUV(ST(0)); @@ -6870,7 +6870,7 @@ XS(XS__getspellstat) { uint8 slot = 0; if (items == 3) slot = (uint8) SvUV(ST(2)); - + stat_value = quest_manager.getspellstat(spell_id, stat_identifier, slot); XSprePUSH; diff --git a/zone/entity.cpp b/zone/entity.cpp index 53477e5d7..12898f0ea 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -4185,8 +4185,10 @@ bool Entity::CheckCoordLosNoZLeaps(float cur_x, float cur_y, float cur_z, return false; } -void EntityList::QuestJournalledSayClose(Mob *sender, float dist, const char *mobname, const char *message, - Journal::Options &opts) +void EntityList::QuestJournalledSayClose( + Mob *sender, float dist, const char *mobname, const char *message, + Journal::Options &opts +) { SerializeBuffer buf(sizeof(SpecialMesgHeader_Struct) + 12 + 64 + 64); @@ -4199,7 +4201,15 @@ void EntityList::QuestJournalledSayClose(Mob *sender, float dist, const char *mo buf.WriteInt32(0); // location, client doesn't seem to do anything with this buf.WriteInt32(0); buf.WriteInt32(0); - buf.WriteString(message); + + // auto inject saylinks (say) + if (RuleB(Chat, AutoInjectSaylinksToSay)) { + std::string new_message = EQ::SayLinkEngine::InjectSaylinksIfNotExist(message); + buf.WriteString(new_message); + } + else { + buf.WriteString(message); + } auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf); diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index 6eff81a0c..b37841e73 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -755,7 +755,15 @@ double Lua_Mob::GetSize() { void Lua_Mob::Message(int type, const char *message) { Lua_Safe_Call_Void(); - self->Message(type, message); + + // auto inject saylinks + if (RuleB(Chat, AutoInjectSaylinksToClientMessage)) { + std::string new_message = EQ::SayLinkEngine::InjectSaylinksIfNotExist(message); + self->Message(type, new_message.c_str()); + } + else { + self->Message(type, message); + } } void Lua_Mob::MessageString(int type, int string_id, uint32 distance) { @@ -2747,7 +2755,7 @@ luabind::scope lua_register_mob() { .def("GetNimbusEffect2", (uint8(Lua_Mob::*)(void))&Lua_Mob::GetNimbusEffect2) .def("GetNimbusEffect3", (uint8(Lua_Mob::*)(void))&Lua_Mob::GetNimbusEffect3) .def("IsTargetable", (bool(Lua_Mob::*)(void))&Lua_Mob::IsTargetable) - .def("HasShieldEquiped", (bool(Lua_Mob::*)(void))&Lua_Mob::HasShieldEquiped) + .def("HasShieldEquiped", (bool(Lua_Mob::*)(void))&Lua_Mob::HasShieldEquiped) .def("HasTwoHandBluntEquiped", (bool(Lua_Mob::*)(void))&Lua_Mob::HasTwoHandBluntEquiped) .def("HasTwoHanderEquipped", (bool(Lua_Mob::*)(void))&Lua_Mob::HasTwoHanderEquipped) .def("GetHerosForgeModel", (int32(Lua_Mob::*)(uint8))&Lua_Mob::GetHerosForgeModel) diff --git a/zone/mob.cpp b/zone/mob.cpp index 287bdad92..902c19140 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -2923,10 +2923,19 @@ void Mob::Say(const char *format, ...) talker = this; } - entity_list.MessageCloseString( - talker, false, 200, 10, - GENERIC_SAY, GetCleanName(), buf - ); + if (RuleB(Chat, AutoInjectSaylinksToSay)) { + std::string new_message = EQ::SayLinkEngine::InjectSaylinksIfNotExist(buf); + entity_list.MessageCloseString( + talker, false, 200, 10, + GENERIC_SAY, GetCleanName(), new_message.c_str() + ); + } + else { + entity_list.MessageCloseString( + talker, false, 200, 10, + GENERIC_SAY, GetCleanName(), buf + ); + } } // diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index 24a9ec3b9..6f48c4cd8 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -989,7 +989,7 @@ XS(XS_Mob_BuffCount) { VALIDATE_THIS_IS_MOB; RETVAL = THIS->BuffCount(); XSprePUSH; - PUSHu((UV) RETVAL); + PUSHu((UV) RETVAL); } XSRETURN(1); } @@ -2620,7 +2620,15 @@ XS(XS_Mob_Message) { uint32 type = (uint32) SvUV(ST(1)); char *message = (char *) SvPV_nolen(ST(2)); VALIDATE_THIS_IS_MOB; - THIS->Message(type, message); + + // auto inject saylinks + if (RuleB(Chat, AutoInjectSaylinksToClientMessage)) { + std::string new_message = EQ::SayLinkEngine::InjectSaylinksIfNotExist(message); + THIS->Message(type, new_message.c_str()); + } + else { + THIS->Message(type, message); + } } XSRETURN_EMPTY; } @@ -6016,7 +6024,7 @@ XS(XS_Mob_GetClassName) { XSprePUSH; PUSHTARG; } - XSRETURN(1); + XSRETURN(1); } XS(XS_Mob_GetRaceName); @@ -6039,7 +6047,7 @@ XS(XS_Mob_GetRaceName) { XS(XS_Mob_DeleteBucket); XS(XS_Mob_DeleteBucket) { - dXSARGS; + dXSARGS; if (items != 2) Perl_croak(aTHX_ "Usage: Mob::DeleteBucket(THIS, std::string bucket_name)"); // @categories Script Utility { @@ -6053,7 +6061,7 @@ XS(XS_Mob_DeleteBucket) { XS(XS_Mob_GetBucket); XS(XS_Mob_GetBucket) { - dXSARGS; + dXSARGS; if (items != 2) Perl_croak(aTHX_ "Usage: Mob::GetBucket(THIS, std::string bucket_name)"); // @categories Script Utility { @@ -6072,7 +6080,7 @@ XS(XS_Mob_GetBucket) { XS(XS_Mob_GetBucketExpires); XS(XS_Mob_GetBucketExpires) { - dXSARGS; + dXSARGS; if (items != 2) Perl_croak(aTHX_ "Usage: Mob::GetBucketExpires(THIS, std::string bucket_name)"); // @categories Script Utility { @@ -6091,7 +6099,7 @@ XS(XS_Mob_GetBucketExpires) { XS(XS_Mob_GetBucketKey); XS(XS_Mob_GetBucketKey) { - dXSARGS; + dXSARGS; if (items != 1) Perl_croak(aTHX_ "Usage: Mob::GetBucketKey(THIS)"); // @categories Script Utility { @@ -6109,7 +6117,7 @@ XS(XS_Mob_GetBucketKey) { XS(XS_Mob_GetBucketRemaining); XS(XS_Mob_GetBucketRemaining) { - dXSARGS; + dXSARGS; if (items != 2) Perl_croak(aTHX_ "Usage: Mob::GetBucketRemaining(THIS, std::string bucket_name)"); // @categories Script Utility { @@ -6128,7 +6136,7 @@ XS(XS_Mob_GetBucketRemaining) { XS(XS_Mob_SetBucket); XS(XS_Mob_SetBucket) { - dXSARGS; + dXSARGS; if (items < 3 || items > 4) Perl_croak(aTHX_ "Usage: Mob::SetBucket(THIS, std::string bucket_name, std::string bucket_value, [std::string expiration])"); // @categories Script Utility { @@ -6146,7 +6154,7 @@ XS(XS_Mob_SetBucket) { } XS(XS_Mob_IsHorse); -XS(XS_Mob_IsHorse) { +XS(XS_Mob_IsHorse) { dXSARGS; if (items != 1) Perl_croak(aTHX_ "Usage: Mob::IsHorse(THIS)"); // @categories Script Utility From 97dcba70cfc444a00d11a2e56c9d36867c172f39 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 12 Sep 2021 23:37:39 -0400 Subject: [PATCH 184/624] [Bots] Fix for Bot Pets Taunting (#1519) Currently bot pets will taunt and there's no way to turn it off. This makes it so pets follow their owner's taunt settings. --- zone/bot_command.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 65c2cc70a..1a922379e 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -4789,12 +4789,25 @@ void bot_command_taunt(Client *c, const Seperator *sep) ++taunting_count; } + for (auto bot_iter : sbl) { + if (!bot_iter->HasPet()) + continue; + if (!bot_iter->GetPet()->GetSkill(EQ::skills::SkillTaunt)) + continue; + if (toggle_taunt) + bot_iter->GetPet()->CastToNPC()->SetTaunting(!bot_iter->GetPet()->CastToNPC()->IsTaunting()); + else + bot_iter->GetPet()->CastToNPC()->SetTaunting(taunt_state); + if (sbl.size() == 1) + Bot::BotGroupSay(bot_iter, "My Pet is %s taunting", bot_iter->GetPet()->CastToNPC()->IsTaunting() ? "now" : "no longer"); + ++taunting_count; + } if (taunting_count) { if (toggle_taunt) - c->Message(m_action, "%i of your bots %s toggled their taunting state", taunting_count, ((taunting_count != 1) ? ("have") : ("has"))); + c->Message(m_action, "%i of your bots and their pets %s toggled their taunting state", taunting_count, ((taunting_count != 1) ? ("have") : ("has"))); else - c->Message(m_action, "%i of your bots %s %s taunting", taunting_count, ((taunting_count != 1) ? ("have") : ("has")), ((taunt_state) ? ("started") : ("stopped"))); + c->Message(m_action, "%i of your bots and their pets %s %s taunting", taunting_count, ((taunting_count != 1) ? ("have") : ("has")), ((taunt_state) ? ("started") : ("stopped"))); } else { c->Message(m_fail, "None of your bots are capable of taunting"); From 56b9b6f2c4f8781d7293cd5532f51b792ab3422f Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 12 Sep 2021 23:38:38 -0400 Subject: [PATCH 185/624] [Quest API] Add corpse->GetLootList() and npc->GetLootList() to Perl and Lua. (#1529) * [Quest API] Add corpse->GetLootList() and npc->GetLootList() to Perl and Lua. - Add $corpse->GetLootList() to Perl. - Add $npc->GetLootList() to Perl. - Add corpse:GetLootList() to Lua. - Add npc:GetLootList() to Lua. Returns an array of item IDs for use with corpse and NPC methods such as HasItem(item_id), CountItem(item_id), and GetFirstSlotByItemID(item_id). * Categories. * Modify Lua to use classes. --- zone/corpse.cpp | 26 ++++++++++++++++++++++---- zone/corpse.h | 1 + zone/lua_corpse.cpp | 25 ++++++++++++++++++++++++- zone/lua_corpse.h | 3 +++ zone/lua_npc.cpp | 25 ++++++++++++++++++++++++- zone/lua_npc.h | 3 +++ zone/lua_parser.cpp | 2 ++ zone/npc.cpp | 22 ++++++++++++++++++++-- zone/npc.h | 1 + zone/perl_npc.cpp | 24 ++++++++++++++++++++++++ zone/perl_player_corpse.cpp | 24 ++++++++++++++++++++++++ 11 files changed, 148 insertions(+), 8 deletions(-) diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 498780e1f..1e655a6bc 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -1462,12 +1462,12 @@ bool Corpse::HasItem(uint32 item_id) { for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { ServerLootItem_Struct* loot_item = *current_item; if (!loot_item) { - LogError("NPC::CountItem() - ItemList error, null item"); + LogError("Corpse::HasItem() - ItemList error, null item"); continue; } if (!loot_item->item_id || !database.GetItem(loot_item->item_id)) { - LogError("NPC::CountItem() - Database error, invalid item"); + LogError("Corpse::HasItem() - Database error, invalid item"); continue; } @@ -1487,12 +1487,12 @@ uint16 Corpse::CountItem(uint32 item_id) { for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { ServerLootItem_Struct* loot_item = *current_item; if (!loot_item) { - LogError("NPC::CountItem() - ItemList error, null item"); + LogError("Corpse::CountItem() - ItemList error, null item"); continue; } if (!loot_item->item_id || !database.GetItem(loot_item->item_id)) { - LogError("NPC::CountItem() - Database error, invalid item"); + LogError("Corpse::CountItem() - Database error, invalid item"); continue; } @@ -1747,3 +1747,21 @@ bool Corpse::MovePlayerCorpseToNonInstance() return false; } + +std::vector Corpse::GetLootList() { + std::vector corpse_items; + for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { + ServerLootItem_Struct* loot_item = *current_item; + if (!loot_item) { + LogError("Corpse::GetLootList() - ItemList error, null item"); + continue; + } + + if (std::find(corpse_items.begin(), corpse_items.end(), loot_item->item_id) != corpse_items.end()) { + continue; + } + + corpse_items.push_back(loot_item->item_id); + } + return corpse_items; +} diff --git a/zone/corpse.h b/zone/corpse.h index 80d3b4e8f..acbd63ac5 100644 --- a/zone/corpse.h +++ b/zone/corpse.h @@ -117,6 +117,7 @@ class Corpse : public Mob { uint16 CountItem(uint32 item_id); uint32 GetItemIDBySlot(uint16 loot_slot); uint16 GetFirstSlotByItemID(uint32 item_id); + std::vector GetLootList(); void LootItem(Client* client, const EQApplicationPacket* app); void EndLoot(Client* client, const EQApplicationPacket* app); void MakeLootRequestPackets(Client* client, const EQApplicationPacket* app); diff --git a/zone/lua_corpse.cpp b/zone/lua_corpse.cpp index 85e4150f7..200e13ce3 100644 --- a/zone/lua_corpse.cpp +++ b/zone/lua_corpse.cpp @@ -2,11 +2,16 @@ #include "lua.hpp" #include +#include #include "corpse.h" #include "lua_corpse.h" #include "lua_client.h" +struct Lua_Corpse_Loot_List { + std::vector entries; +}; + uint32 Lua_Corpse::GetCharID() { Lua_Safe_Call_Int(); return self->GetCharID(); @@ -172,6 +177,18 @@ uint16 Lua_Corpse::GetFirstSlotByItemID(uint32 item_id) { return self->GetFirstSlotByItemID(item_id); } +Lua_Corpse_Loot_List Lua_Corpse::GetLootList(lua_State* L) { + Lua_Safe_Call_Class(Lua_Corpse_Loot_List); + Lua_Corpse_Loot_List ret; + auto loot_list = self->GetLootList(); + + for (auto item_id : loot_list) { + ret.entries.push_back(item_id); + } + + return ret; +} + luabind::scope lua_register_corpse() { return luabind::class_("Corpse") .def(luabind::constructor<>()) @@ -209,7 +226,13 @@ luabind::scope lua_register_corpse() { .def("HasItem", (bool(Lua_Corpse::*)(uint32))&Lua_Corpse::HasItem) .def("CountItem", (uint16(Lua_Corpse::*)(uint32))&Lua_Corpse::CountItem) .def("GetItemIDBySlot", (uint32(Lua_Corpse::*)(uint16))&Lua_Corpse::GetItemIDBySlot) - .def("GetFirstSlotByItemID", (uint16(Lua_Corpse::*)(uint32))&Lua_Corpse::GetFirstSlotByItemID); + .def("GetFirstSlotByItemID", (uint16(Lua_Corpse::*)(uint32))&Lua_Corpse::GetFirstSlotByItemID) + .def("GetLootList", (Lua_Corpse_Loot_List(Lua_Corpse::*)(lua_State* L))&Lua_Corpse::GetLootList); +} + +luabind::scope lua_register_corpse_loot_list() { + return luabind::class_("CorpseLootList") + .def_readwrite("entries", &Lua_Corpse_Loot_List::entries, luabind::return_stl_iterator); } #endif diff --git a/zone/lua_corpse.h b/zone/lua_corpse.h index 679fe4948..c8c9bf470 100644 --- a/zone/lua_corpse.h +++ b/zone/lua_corpse.h @@ -6,12 +6,14 @@ class Corpse; class Lua_Client; +struct Lua_Corpse_Loot_List; namespace luabind { struct scope; } luabind::scope lua_register_corpse(); +luabind::scope lua_register_corpse_loot_list(); class Lua_Corpse : public Lua_Mob { @@ -58,6 +60,7 @@ public: uint16 CountItem(uint32 item_id); uint32 GetItemIDBySlot(uint16 loot_slot); uint16 GetFirstSlotByItemID(uint32 item_id); + Lua_Corpse_Loot_List GetLootList(lua_State* L); }; #endif diff --git a/zone/lua_npc.cpp b/zone/lua_npc.cpp index c8bc012c9..8d9fe87ed 100644 --- a/zone/lua_npc.cpp +++ b/zone/lua_npc.cpp @@ -2,11 +2,16 @@ #include "lua.hpp" #include +#include #include "npc.h" #include "lua_npc.h" #include "lua_client.h" +struct Lua_NPC_Loot_List { + std::vector entries; +}; + void Lua_NPC::Signal(int id) { Lua_Safe_Call_Void(); self->SignalNPC(id); @@ -618,6 +623,18 @@ float Lua_NPC::GetSpellScale() return self->GetSpellScale(); } +Lua_NPC_Loot_List Lua_NPC::GetLootList(lua_State* L) { + Lua_Safe_Call_Class(Lua_NPC_Loot_List); + Lua_NPC_Loot_List ret; + auto loot_list = self->GetLootList(); + + for (auto item_id : loot_list) { + ret.entries.push_back(item_id); + } + + return ret; +} + luabind::scope lua_register_npc() { return luabind::class_("NPC") .def(luabind::constructor<>()) @@ -740,7 +757,13 @@ luabind::scope lua_register_npc() { .def("GetItemIDBySlot", (uint32(Lua_NPC::*)(uint16))&Lua_NPC::GetItemIDBySlot) .def("GetFirstSlotByItemID", (uint16(Lua_NPC::*)(uint32))&Lua_NPC::GetFirstSlotByItemID) .def("GetHealScale", (float(Lua_NPC::*)(void))&Lua_NPC::GetHealScale) - .def("GetSpellScale", (float(Lua_NPC::*)(void))&Lua_NPC::GetSpellScale); + .def("GetSpellScale", (float(Lua_NPC::*)(void))&Lua_NPC::GetSpellScale) + .def("GetLootList", (Lua_NPC_Loot_List(Lua_NPC::*)(lua_State* L))&Lua_NPC::GetLootList); +} + +luabind::scope lua_register_npc_loot_list() { + return luabind::class_("NPCLootList") + .def_readwrite("entries", &Lua_NPC_Loot_List::entries, luabind::return_stl_iterator); } #endif diff --git a/zone/lua_npc.h b/zone/lua_npc.h index 1577a1e20..6c329907b 100644 --- a/zone/lua_npc.h +++ b/zone/lua_npc.h @@ -8,12 +8,14 @@ class NPC; class Lua_Mob; class Lua_NPC; class Lua_Client; +struct Lua_NPC_Loot_List; namespace luabind { struct scope; } luabind::scope lua_register_npc(); +luabind::scope lua_register_npc_loot_list(); class Lua_NPC : public Lua_Mob { @@ -146,6 +148,7 @@ public: uint16 GetFirstSlotByItemID(uint32 item_id); float GetHealScale(); float GetSpellScale(); + Lua_NPC_Loot_List GetLootList(lua_State* L); }; #endif diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index 5a0601658..11106a254 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -1121,6 +1121,8 @@ void LuaParser::MapFunctions(lua_State *L) { lua_register_object_list(), lua_register_door_list(), lua_register_spawn_list(), + lua_register_corpse_loot_list(), + lua_register_npc_loot_list(), lua_register_group(), lua_register_raid(), lua_register_corpse(), diff --git a/zone/npc.cpp b/zone/npc.cpp index 85a843ca0..e402c89ab 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -682,12 +682,12 @@ bool NPC::HasItem(uint32 item_id) { for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { ServerLootItem_Struct* loot_item = *current_item; if (!loot_item) { - LogError("NPC::CountItem() - ItemList error, null item"); + LogError("NPC::HasItem() - ItemList error, null item"); continue; } if (!loot_item->item_id || !database.GetItem(loot_item->item_id)) { - LogError("NPC::CountItem() - Database error, invalid item"); + LogError("NPC::HasItem() - Database error, invalid item"); continue; } @@ -3474,3 +3474,21 @@ bool NPC::IsGuard() } return false; } + +std::vector NPC::GetLootList() { + std::vector npc_items; + for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { + ServerLootItem_Struct* loot_item = *current_item; + if (!loot_item) { + LogError("NPC::GetLootList() - ItemList error, null item"); + continue; + } + + if (std::find(npc_items.begin(), npc_items.end(), loot_item->item_id) != npc_items.end()) { + continue; + } + + npc_items.push_back(loot_item->item_id); + } + return npc_items; +} diff --git a/zone/npc.h b/zone/npc.h index c758ee19f..af07b396c 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -208,6 +208,7 @@ public: uint16 CountItem(uint32 item_id); uint32 GetItemIDBySlot(uint16 loot_slot); uint16 GetFirstSlotByItemID(uint32 item_id); + std::vector GetLootList(); uint32 CountLoot(); inline uint32 GetLoottableID() const { return loottable_id; } virtual void UpdateEquipmentLight(); diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index 6496e5863..d22962fbb 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -1846,6 +1846,29 @@ XS(XS_NPC_GetSpellScale) { XSRETURN(1); } +XS(XS_NPC_GetLootList); +XS(XS_NPC_GetLootList) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetLootList(THIS)"); // @categories Script Utility + { + NPC *THIS; + VALIDATE_THIS_IS_NPC; + auto npc_items = THIS->GetLootList(); + auto item_count = npc_items.size(); + if (item_count > 0) { + EXTEND(sp, item_count); + for (int index = 0; index < item_count; ++index) { + ST(index) = sv_2mortal(newSVuv(npc_items[index])); + } + XSRETURN(item_count); + } + SV* return_value = &PL_sv_undef; + ST(0) = return_value; + XSRETURN(1); + } +} + #ifdef __cplusplus extern "C" #endif @@ -1970,6 +1993,7 @@ XS(boot_NPC) { newXSproto(strcpy(buf, "GetFirstSlotByItemID"), XS_NPC_GetFirstSlotByItemID, file, "$$"); newXSproto(strcpy(buf, "GetHealScale"), XS_NPC_GetHealScale, file, "$"); newXSproto(strcpy(buf, "GetSpellScale"), XS_NPC_GetSpellScale, file, "$"); + newXSproto(strcpy(buf, "GetLootList"), XS_NPC_GetLootList, file, "$"); XSRETURN_YES; } diff --git a/zone/perl_player_corpse.cpp b/zone/perl_player_corpse.cpp index dc6aaeb8b..dd01acda4 100644 --- a/zone/perl_player_corpse.cpp +++ b/zone/perl_player_corpse.cpp @@ -599,6 +599,29 @@ XS(XS_Corpse_GetFirstSlotByItemID) { XSRETURN(1); } +XS(XS_Corpse_GetLootList); +XS(XS_Corpse_GetLootList) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Corpse::GetLootList(THIS)"); // @categories Script Utility + { + Corpse *THIS; + VALIDATE_THIS_IS_CORPSE; + auto corpse_items = THIS->GetLootList(); + auto item_count = corpse_items.size(); + if (item_count > 0) { + EXTEND(sp, item_count); + for (int index = 0; index < item_count; ++index) { + ST(index) = sv_2mortal(newSVuv(corpse_items[index])); + } + XSRETURN(item_count); + } + SV* return_value = &PL_sv_undef; + ST(0) = return_value; + XSRETURN(1); + } +} + #ifdef __cplusplus extern "C" #endif @@ -649,6 +672,7 @@ XS(boot_Corpse) { newXSproto(strcpy(buf, "CountItem"), XS_Corpse_CountItem, file, "$$"); newXSproto(strcpy(buf, "GetItemIDBySlot"), XS_Corpse_GetItemIDBySlot, file, "$$"); newXSproto(strcpy(buf, "GetFirstSlotByItemID"), XS_Corpse_GetFirstSlotByItemID, file, "$$"); + newXSproto(strcpy(buf, "GetLootList"), XS_Corpse_GetLootList, file, "$"); XSRETURN_YES; } From 38a86edc7055a8bb2b8285773e3485b959caf69f Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 12 Sep 2021 23:39:09 -0400 Subject: [PATCH 186/624] [Quest API] Add EVENT_CONSIDER_CORPSE to Perl and Lua. (#1530) - Exports $corpse_entity_id in Perl. - Exports e.corpse_entity_id in Lua. Allows you to perform events on corpse consider for server operators. --- zone/client_packet.cpp | 18 ++---------------- zone/embparser.cpp | 7 ++++++- zone/event_codes.h | 1 + zone/lua_general.cpp | 3 ++- zone/lua_parser.cpp | 4 +++- zone/lua_parser_events.cpp | 5 +++++ zone/lua_parser_events.h | 2 ++ 7 files changed, 21 insertions(+), 19 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 0a05d7580..d09e6e683 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -4977,6 +4977,7 @@ void Client::Handle_OP_ConsiderCorpse(const EQApplicationPacket *app) Consider_Struct* conin = (Consider_Struct*)app->pBuffer; Corpse* tcorpse = entity_list.GetCorpseByID(conin->targetid); if (tcorpse && tcorpse->IsNPCCorpse()) { + parse->EventPlayer(EVENT_CONSIDER_CORPSE, this, fmt::format("{}", conin->targetid), 0); uint32 min; uint32 sec; uint32 ttime; if ((ttime = tcorpse->GetDecayTime()) != 0) { sec = (ttime / 1000) % 60; // Total seconds @@ -4990,6 +4991,7 @@ void Client::Handle_OP_ConsiderCorpse(const EQApplicationPacket *app) } } else if (tcorpse && tcorpse->IsPlayerCorpse()) { + parse->EventPlayer(EVENT_CONSIDER_CORPSE, this, fmt::format("{}", conin->targetid), 0); uint32 day, hour, min, sec, ttime; if ((ttime = tcorpse->GetDecayTime()) != 0) { sec = (ttime / 1000) % 60; // Total seconds @@ -5004,22 +5006,6 @@ void Client::Handle_OP_ConsiderCorpse(const EQApplicationPacket *app) Message(0, "This corpse will decay in %i minutes and %i seconds.", min, sec); Message(0, "This corpse %s be resurrected.", tcorpse->IsRezzed() ? "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 { - MessageString(Chat::White, CORPSE_TOO_OLD); - } - */ } else { MessageString(Chat::NPCQuestSay, CORPSE_DECAY_NOW); diff --git a/zone/embparser.cpp b/zone/embparser.cpp index f34208eff..56f21444e 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -121,7 +121,8 @@ const char *QuestEventSubroutines[_LargestEventID] = { "EVENT_COMBINE_VALIDATE", "EVENT_BOT_COMMAND", "EVENT_WARP", - "EVENT_TEST_BUFF" + "EVENT_TEST_BUFF", + "EVENT_CONSIDER_CORPSE" }; PerlembParser::PerlembParser() : perl(nullptr) @@ -1644,6 +1645,10 @@ void PerlembParser::ExportEventVariables( ExportVar(package_name.c_str(), "from_z", sep.arg[2]); break; } + case EVENT_CONSIDER_CORPSE: { + ExportVar(package_name.c_str(), "corpse_entity_id", std::stoi(data)); + break; + } default: { break; diff --git a/zone/event_codes.h b/zone/event_codes.h index 186b4cc80..7d9de29ef 100644 --- a/zone/event_codes.h +++ b/zone/event_codes.h @@ -90,6 +90,7 @@ typedef enum { EVENT_BOT_COMMAND, EVENT_WARP, EVENT_TEST_BUFF, + EVENT_CONSIDER_CORPSE, _LargestEventID } QuestEventID; diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 756907fd2..dc574be27 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -3241,7 +3241,8 @@ luabind::scope lua_register_events() { luabind::value("death_zone", static_cast(EVENT_DEATH_ZONE)), luabind::value("use_skill", static_cast(EVENT_USE_SKILL)), luabind::value("warp", static_cast(EVENT_WARP)), - luabind::value("test_buff", static_cast(EVENT_TEST_BUFF)) + luabind::value("test_buff", static_cast(EVENT_TEST_BUFF)), + luabind::value("consider_corpse", static_cast(EVENT_CONSIDER_CORPSE)) ]; } diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index 11106a254..060e53c1d 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -132,7 +132,8 @@ const char *LuaEvents[_LargestEventID] = { "event_combine_validate", "event_bot_command", "event_warp", - "event_test_buff" + "event_test_buff", + "event_consider_corpse" }; extern Zone *zone; @@ -220,6 +221,7 @@ LuaParser::LuaParser() { PlayerArgumentDispatch[EVENT_COMBINE_VALIDATE] = handle_player_combine_validate; PlayerArgumentDispatch[EVENT_BOT_COMMAND] = handle_player_bot_command; PlayerArgumentDispatch[EVENT_WARP] = handle_player_warp; + PlayerArgumentDispatch[EVENT_CONSIDER_CORPSE] = handle_player_consider_corpse; ItemArgumentDispatch[EVENT_ITEM_CLICK] = handle_item_click; ItemArgumentDispatch[EVENT_ITEM_CLICK_CAST] = handle_item_click; diff --git a/zone/lua_parser_events.cpp b/zone/lua_parser_events.cpp index f0940b761..6ec7ed35a 100644 --- a/zone/lua_parser_events.cpp +++ b/zone/lua_parser_events.cpp @@ -573,6 +573,11 @@ void handle_player_warp(QuestInterface* parse, lua_State* L, Client* client, std lua_setfield(L, -2, "from_z"); } +void handle_player_consider_corpse(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector* extra_pointers) { + lua_pushinteger(L, std::stoi(data)); + lua_setfield(L, -2, "corpse_entity_id"); +} + //Item void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQ::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data, std::vector *extra_pointers) { diff --git a/zone/lua_parser_events.h b/zone/lua_parser_events.h index 64dfdb5d9..aac4ea3d1 100644 --- a/zone/lua_parser_events.h +++ b/zone/lua_parser_events.h @@ -105,6 +105,8 @@ void handle_player_bot_command(QuestInterface *parse, lua_State* L, Client* clie std::vector *extra_pointers); void handle_player_warp(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector* extra_pointers); +void handle_player_consider_corpse(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, + std::vector* extra_pointers); //Item void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQ::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data, From 7b1b05a35cccf4ab8c1e8fe1b8040de556f28df3 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 12 Sep 2021 23:40:07 -0400 Subject: [PATCH 187/624] [Bug Fix] Resolves issues with improper genders and textures on spells. (#1533) * [Bug Fix] Resolves issues with improper genders and textures on spells. Spells will now properly understand their expected gender and texture. Logic is based on what I saw in a stock PEQ database, can be adjusted if need be. Any feedback is helpful. * Made use of GetRaceGenderDefaultHeight() and added all races to their proper conditions. * Formatting. --- zone/mob.cpp | 76 +++++++++++++++++++++++++++++++++--------- zone/spell_effects.cpp | 74 ++++++++++++++++++++++++---------------- 2 files changed, 105 insertions(+), 45 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index 902c19140..07d4cff7e 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -2123,26 +2123,70 @@ uint16 Mob::GetFactionRace() { } uint8 Mob::GetDefaultGender(uint16 in_race, uint8 in_gender) { - if (Mob::IsPlayerRace(in_race) || in_race == 15 || in_race == 50 || in_race == 57 || in_race == 70 || in_race == 98 || in_race == 118 || in_race == 562) { - if (in_gender >= 2) { - // Male default for PC Races + if ( + Mob::IsPlayerRace(in_race) || + in_race == RACE_BROWNIE_15 || + in_race == RACE_KERRAN_23 || + in_race == RACE_LION_50 || + in_race == RACE_DRACNID_57 || + in_race == RACE_ZOMBIE_70 || + in_race == RACE_QEYNOS_CITIZEN_71 || + in_race == RACE_RIVERVALE_CITIZEN_81 || + in_race == RACE_HALAS_CITIZEN_90 || + in_race == RACE_GROBB_CITIZEN_92 || + in_race == RACE_OGGOK_CITIZEN_93 || + in_race == RACE_KALADIM_CITIZEN_94 || + in_race == RACE_ELF_VAMPIRE_98 || + in_race == RACE_FELGUARD_106 || + in_race == RACE_FAYGUARD_112 || + in_race == RACE_ERUDITE_GHOST_118 || + in_race == RACE_IKSAR_CITIZEN_139 || + in_race == RACE_TROLL_CREW_MEMBER_331 || + in_race == RACE_PIRATE_DECKHAND_332 || + in_race == RACE_GNOME_PIRATE_338 || + in_race == RACE_DARK_ELF_PIRATE_339 || + in_race == RACE_OGRE_PIRATE_340 || + in_race == RACE_HUMAN_PIRATE_341 || + in_race == RACE_ERUDITE_PIRATE_342 || + in_race == RACE_UNDEAD_PIRATE_344 || + in_race == RACE_KNIGHT_OF_HATE_351 || + in_race == RACE_WARLOCK_OF_HATE_352 || + in_race == RACE_UNDEAD_VAMPIRE_359 || + in_race == RACE_VAMPIRE_360 || + in_race == RACE_ZOMBIE_471 || + in_race == RACE_VAMPIRE_497 || + in_race == RACE_KERRAN_562 || + in_race == RACE_BROWNIE_568 || + in_race == RACE_HUMAN_566 || + in_race == RACE_ELVEN_GHOST_587 || + in_race == RACE_HUMAN_GHOST_588 || + in_race == RACE_COLDAIN_645 + ) { + if (in_gender >= 2) { // Male default for PC Races return 0; - } - else + } else { return in_gender; - } - else if (in_race == 44 || in_race == 52 || in_race == 55 || in_race == 65 || in_race == 67 || in_race == 88 || in_race == 117 || in_race == 127 || - in_race == 77 || in_race == 78 || in_race == 81 || in_race == 90 || in_race == 92 || in_race == 93 || in_race == 94 || in_race == 106 || in_race == 112 || in_race == 471) { - // Male only races + } + } else if ( + in_race == RACE_FREEPORT_GUARD_44 || + in_race == RACE_MIMIC_52 || + in_race == RACE_HUMAN_BEGGAR_55 || + in_race == RACE_VAMPIRE_65 || + in_race == RACE_HIGHPASS_CITIZEN_67 || + in_race == RACE_NERIAK_CITIZEN_77 || + in_race == RACE_ERUDITE_CITIZEN_78 || + in_race == RACE_CLOCKWORK_GNOME_88 || + in_race == RACE_DWARF_GHOST_117 || + in_race == RACE_SPECTRAL_IKSAR_147 || + in_race == RACE_INVISIBLE_MAN_127 || + in_race == RACE_VAMPYRE_208 || + in_race == RACE_BROKEN_SKULL_PIRATE_333 || + in_race == RACE_ERUDITE_678 + ) { // Male only races return 0; - - } - else if (in_race == 25 || in_race == 56) { - // Female only races + } else if (in_race == RACE_FAIRY_25 || in_race == RACE_PIXIE_56) { // Female only races return 1; - } - else { - // Neutral default for NPC Races + } else { // Neutral default for NPC Races return 2; } } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 848edd656..21a3a5f10 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1445,37 +1445,53 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove } // Racial Illusions else { - SendIllusionPacket - ( - spell.base[i], - Mob::GetDefaultGender(spell.base[i], GetGender()), - spell.base2[i], - spell.max[i] + auto gender_id = ( + spell.max[i] > 0 && + ( + spell.max[i] != 3 || + spell.base2[i] == 0 + ) ? + (spell.max[i] - 1) : + Mob::GetDefaultGender(spell.base[i], GetGender()) ); - if(spell.base[i] == OGRE){ - SendAppearancePacket(AT_Size, 9); - } - else if(spell.base[i] == TROLL){ - SendAppearancePacket(AT_Size, 8); - } - else if(spell.base[i] == VAHSHIR || spell.base[i] == BARBARIAN){ - SendAppearancePacket(AT_Size, 7); - } - else if(spell.base[i] == HALF_ELF || spell.base[i] == WOOD_ELF || spell.base[i] == DARK_ELF || spell.base[i] == FROGLOK){ - SendAppearancePacket(AT_Size, 5); - } - else if(spell.base[i] == DWARF){ - SendAppearancePacket(AT_Size, 4); - } - else if(spell.base[i] == HALFLING || spell.base[i] == GNOME){ - SendAppearancePacket(AT_Size, 3); - } - else if(spell.base[i] == WOLF) { - SendAppearancePacket(AT_Size, 2); - } - else{ - SendAppearancePacket(AT_Size, 6); + + auto race_size = GetRaceGenderDefaultHeight( + spell.base[i], + gender_id + ); + + if (spell.max[i] > 0) { + if (spell.base2[i] == 0) { + SendIllusionPacket( + spell.base[i], + gender_id + ); + } else { + if (spell.max[i] != 3) { + SendIllusionPacket( + spell.base[i], + gender_id, + spell.base2[i], + spell.max[i] + ); + } else { + SendIllusionPacket( + spell.base[i], + gender_id, + spell.base2[i], + spell.base2[i] + ); + } + } + } else { + SendIllusionPacket( + spell.base[i], + gender_id, + spell.base2[i], + spell.max[i] + ); } + SendAppearancePacket(AT_Size, race_size); } for (int x = EQ::textures::textureBegin; x <= EQ::textures::LastTintableTexture; x++) { From ce5fa9502fcfb63909ddc97423bb122ee75c7892 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 12 Sep 2021 23:40:43 -0400 Subject: [PATCH 188/624] [Commands] Adds #dye command. (#1537) * [Commands] Adds #dye command. * Fix use tint. --- common/ruletypes.h | 4 +++ zone/command.cpp | 86 ++++++++++++++++++++++++++++++++++++++++++++++ zone/command.h | 1 + 3 files changed, 91 insertions(+) diff --git a/common/ruletypes.h b/common/ruletypes.h index 9457313ec..b0c8074b1 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -781,6 +781,10 @@ RULE_BOOL(Cheat, EnableMQFastMemDetector, true, "Enable the MQFastMem Detector. RULE_BOOL(Cheat, MarkMQWarpLT, false, "Mark clients makeing smaller warps") RULE_CATEGORY_END() +RULE_CATEGORY(Command) +RULE_BOOL(Command, DyeCommandRequiresDyes, false, "Enable this to require a Prismatic Dye (32557) each time someone uses #dye.") +RULE_CATEGORY_END() + #undef RULE_CATEGORY #undef RULE_INT #undef RULE_REAL diff --git a/zone/command.cpp b/zone/command.cpp index 4db232d80..7fc61208b 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -205,6 +205,7 @@ int command_init(void) command_add("distance", "- Reports the distance between you and your target.", 80, command_distance) || command_add("door", "Door editing command", 80, command_door) || command_add("doanim", "[animnum] [type] - Send an EmoteAnim for you or your target", 50, command_doanim) || + command_add("dye", "[slot|'help'] [red] [green] [blue] [use_tint] - Dyes the specified armor slot to Red, Green, and Blue provided, allows you to bypass darkness limits.", 20, command_dye) || command_add("dz", "Manage expeditions and dynamic zone instances", 80, command_dz) || command_add("dzkickplayers", "Removes all players from current expedition. (/kickplayers alternative for pre-RoF clients)", 0, command_dzkickplayers) || command_add("editmassrespawn", "[name-search] [second-value] - Mass (Zone wide) NPC respawn timer editing command", 100, command_editmassrespawn) || @@ -14677,6 +14678,91 @@ void command_viewzoneloot(Client *c, const Seperator *sep) ); } } + +void command_dye(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + + if (arguments == 0) { + c->Message(Chat::White, "Command Syntax: #dye help | #dye [slot] [red] [green] [blue] [use_tint]"); + return; + } + + uint8 slot = 0; + uint8 red = 255; + uint8 green = 255; + uint8 blue = 255; + uint8 use_tint = 255; + + std::vector dye_slots = { + "Helmet", + "Chest", + "Arms", + "Wrist", + "Hands", + "Legs", + "Feet" + }; + + if (arguments == 1 && !strcasecmp(sep->arg[1], "help")) { + int slot_id = 0; + std::vector slot_messages; + c->Message(Chat::White, "Command Syntax: #dye help | #dye [slot] [red] [green] [blue] [use_tint]"); + c->Message(Chat::White, "Red, Green, and Blue go from 0 to 255."); + + for (const auto& slot : dye_slots) { + slot_messages.push_back(fmt::format("({}) {}", slot_id, slot)); + slot_id++; + } + + c->Message( + Chat::White, + fmt::format( + "{} {}", + "Slots are as follows:", + implode(", ", slot_messages) + ).c_str() + ); + return; + } + + if (arguments >= 1 && sep->IsNumber(1)) { + slot = atoi(sep->arg[1]); + } + + if (arguments >= 2 && sep->IsNumber(2)) { + red = atoi(sep->arg[2]); + } + + if (arguments >= 3 && sep->IsNumber(3)) { + green = atoi(sep->arg[3]); + } + + if (arguments >= 4 && sep->IsNumber(4)) { + blue = atoi(sep->arg[4]); + } + + if (arguments >= 5 && sep->IsNumber(5)) { + use_tint = atoi(sep->arg[5]); + } + + if (RuleB(Command, DyeCommandRequiresDyes)) { + uint32 dye_item_id = 32557; + if (c->CountItem(dye_item_id) >= 1) { + c->RemoveItem(dye_item_id); + } else { + EQ::SayLinkEngine linker; + linker.SetLinkType(EQ::saylink::SayLinkItemData); + const EQ::ItemData *dye_item = database.GetItem(dye_item_id); + linker.SetItemData(dye_item); + c->Message(Chat::White, fmt::format("This command requires a {} to use.", linker.GenerateLink()).c_str()); + return; + } + } + + c->DyeArmorBySlot(slot, red, green, blue, use_tint); +} + // All new code added to command.cpp should be BEFORE this comment line. Do no append code to this file below the BOTS code block. #ifdef BOTS #include "bot_command.h" diff --git a/zone/command.h b/zone/command.h index 9c2aef51b..f2e8922d1 100644 --- a/zone/command.h +++ b/zone/command.h @@ -93,6 +93,7 @@ void command_disarmtrap(Client *c, const Seperator *sep); void command_door(Client *c, const Seperator *sep); void command_distance(Client *c, const Seperator *sep); void command_doanim(Client *c, const Seperator *sep); +void command_dye(Client *c, const Seperator *sep); void command_dz(Client *c, const Seperator *sep); void command_dzkickplayers(Client *c, const Seperator *sep); void command_editmassrespawn(Client* c, const Seperator* sep); From 9589bf6bf84abc826d0fb389bd4fed96b6ce2763 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 13 Sep 2021 14:15:08 -0500 Subject: [PATCH 189/624] [Hotfix] Crash fix that apparently didn't make it in another PR --- zone/client_packet.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index d09e6e683..7c770d87a 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -11146,9 +11146,11 @@ void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app) break; case POPUPID_DIAWIND: - response = GetEntityVariable(DIAWIND_RESPONSE_KEY.c_str()); - if (!response.empty()) { - ChannelMessageReceived(8, 0, 100, response.c_str()); + if (EntityVariableExists(DIAWIND_RESPONSE_KEY.c_str())) { + response = GetEntityVariable(DIAWIND_RESPONSE_KEY.c_str()); + if (!response.empty()) { + ChannelMessageReceived(8, 0, 100, response.c_str()); + } } break; From 6e76f89ca2146e4249ea0a2832d3a8b6809c2693 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Mon, 13 Sep 2021 15:30:17 -0400 Subject: [PATCH 190/624] [Quest API] Add EVENT_CONSIDER to Perl and Lua. (#1531) * [Quest API] Add EVENT_CONSIDER to Perl and Lua. - Exports $entity_id in Perl. - Exports e.entity_id in Lua. Allows you to perform events on consider for server operators. * Missing comma. * Formatting. * Add return capability to EVENT_CONSIDER and EVENT_CONSIDER_CORPSE so operators can break out of consider functions. --- zone/client_packet.cpp | 14 ++++++++++++-- zone/embparser.cpp | 7 +++++++ zone/event_codes.h | 1 + zone/lua_general.cpp | 1 + zone/lua_parser.cpp | 2 ++ zone/lua_parser_events.cpp | 5 +++++ zone/lua_parser_events.h | 2 ++ 7 files changed, 30 insertions(+), 2 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 7c770d87a..418544abc 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -4854,6 +4854,10 @@ void Client::Handle_OP_Consider(const EQApplicationPacket *app) if (tmob == 0) return; + if (parse->EventPlayer(EVENT_CONSIDER, this, fmt::format("{}", conin->targetid), 0) == 1) { + return; + } + if (tmob->GetClass() == LDON_TREASURE) { Message(Chat::Yellow, "%s", tmob->GetCleanName()); @@ -4977,7 +4981,10 @@ void Client::Handle_OP_ConsiderCorpse(const EQApplicationPacket *app) Consider_Struct* conin = (Consider_Struct*)app->pBuffer; Corpse* tcorpse = entity_list.GetCorpseByID(conin->targetid); if (tcorpse && tcorpse->IsNPCCorpse()) { - parse->EventPlayer(EVENT_CONSIDER_CORPSE, this, fmt::format("{}", conin->targetid), 0); + if (parse->EventPlayer(EVENT_CONSIDER_CORPSE, this, fmt::format("{}", conin->targetid), 0) == 1) { + return; + } + uint32 min; uint32 sec; uint32 ttime; if ((ttime = tcorpse->GetDecayTime()) != 0) { sec = (ttime / 1000) % 60; // Total seconds @@ -4991,7 +4998,10 @@ void Client::Handle_OP_ConsiderCorpse(const EQApplicationPacket *app) } } else if (tcorpse && tcorpse->IsPlayerCorpse()) { - parse->EventPlayer(EVENT_CONSIDER_CORPSE, this, fmt::format("{}", conin->targetid), 0); + if (parse->EventPlayer(EVENT_CONSIDER_CORPSE, this, fmt::format("{}", conin->targetid), 0) == 1) { + return; + } + uint32 day, hour, min, sec, ttime; if ((ttime = tcorpse->GetDecayTime()) != 0) { sec = (ttime / 1000) % 60; // Total seconds diff --git a/zone/embparser.cpp b/zone/embparser.cpp index 56f21444e..535e3c356 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -122,6 +122,7 @@ const char *QuestEventSubroutines[_LargestEventID] = { "EVENT_BOT_COMMAND", "EVENT_WARP", "EVENT_TEST_BUFF", + "EVENT_CONSIDER", "EVENT_CONSIDER_CORPSE" }; @@ -1645,6 +1646,12 @@ void PerlembParser::ExportEventVariables( ExportVar(package_name.c_str(), "from_z", sep.arg[2]); break; } + + case EVENT_CONSIDER: { + ExportVar(package_name.c_str(), "entity_id", std::stoi(data)); + break; + } + case EVENT_CONSIDER_CORPSE: { ExportVar(package_name.c_str(), "corpse_entity_id", std::stoi(data)); break; diff --git a/zone/event_codes.h b/zone/event_codes.h index 7d9de29ef..87143d11e 100644 --- a/zone/event_codes.h +++ b/zone/event_codes.h @@ -90,6 +90,7 @@ typedef enum { EVENT_BOT_COMMAND, EVENT_WARP, EVENT_TEST_BUFF, + EVENT_CONSIDER, EVENT_CONSIDER_CORPSE, _LargestEventID } QuestEventID; diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index dc574be27..1992b78eb 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -3242,6 +3242,7 @@ luabind::scope lua_register_events() { luabind::value("use_skill", static_cast(EVENT_USE_SKILL)), luabind::value("warp", static_cast(EVENT_WARP)), luabind::value("test_buff", static_cast(EVENT_TEST_BUFF)), + luabind::value("consider", static_cast(EVENT_CONSIDER)), luabind::value("consider_corpse", static_cast(EVENT_CONSIDER_CORPSE)) ]; } diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index 060e53c1d..3a01c643f 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -133,6 +133,7 @@ const char *LuaEvents[_LargestEventID] = { "event_bot_command", "event_warp", "event_test_buff", + "event_consider", "event_consider_corpse" }; @@ -221,6 +222,7 @@ LuaParser::LuaParser() { PlayerArgumentDispatch[EVENT_COMBINE_VALIDATE] = handle_player_combine_validate; PlayerArgumentDispatch[EVENT_BOT_COMMAND] = handle_player_bot_command; PlayerArgumentDispatch[EVENT_WARP] = handle_player_warp; + PlayerArgumentDispatch[EVENT_CONSIDER] = handle_player_consider; PlayerArgumentDispatch[EVENT_CONSIDER_CORPSE] = handle_player_consider_corpse; ItemArgumentDispatch[EVENT_ITEM_CLICK] = handle_item_click; diff --git a/zone/lua_parser_events.cpp b/zone/lua_parser_events.cpp index 6ec7ed35a..4a0c154ca 100644 --- a/zone/lua_parser_events.cpp +++ b/zone/lua_parser_events.cpp @@ -573,6 +573,11 @@ void handle_player_warp(QuestInterface* parse, lua_State* L, Client* client, std lua_setfield(L, -2, "from_z"); } +void handle_player_consider(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector* extra_pointers) { + lua_pushinteger(L, std::stoi(data)); + lua_setfield(L, -2, "entity_id"); +} + void handle_player_consider_corpse(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector* extra_pointers) { lua_pushinteger(L, std::stoi(data)); lua_setfield(L, -2, "corpse_entity_id"); diff --git a/zone/lua_parser_events.h b/zone/lua_parser_events.h index aac4ea3d1..2b6512c15 100644 --- a/zone/lua_parser_events.h +++ b/zone/lua_parser_events.h @@ -105,6 +105,8 @@ void handle_player_bot_command(QuestInterface *parse, lua_State* L, Client* clie std::vector *extra_pointers); void handle_player_warp(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector* extra_pointers); +void handle_player_consider(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, + std::vector* extra_pointers); void handle_player_consider_corpse(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector* extra_pointers); From fa8d8eccc240bb0961fe0d2f27af38837647e455 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Mon, 13 Sep 2021 15:42:04 -0400 Subject: [PATCH 191/624] [Quest API] Add corpse->RemoveItemByID(item_id, quantity) to Perl and Lua. (#1535) * [Quest API] Add corpse->RemoveItemByID(item_id, quantity) to Perl and Lua. - Add $corpse->RemoveItemByID(item_id, quantity) to Perl. - Add corpse:RemoveItemByID(item_id, quantity) to Lua. * Formatting. --- zone/corpse.cpp | 39 +++++++++++++++++++++++++++++++++++++ zone/corpse.h | 1 + zone/lua_corpse.cpp | 12 ++++++++++++ zone/lua_corpse.h | 2 ++ zone/perl_player_corpse.cpp | 19 ++++++++++++++++++ 5 files changed, 73 insertions(+) diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 1e655a6bc..5ccdc8672 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -791,6 +791,45 @@ void Corpse::RemoveItem(ServerLootItem_Struct* item_data) } } +void Corpse::RemoveItemByID(uint32 item_id, int quantity) { + if (!database.GetItem(item_id)) { + return; + } + + if (!HasItem(item_id)) { + return; + } + + int removed_count = 0; + for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { + ServerLootItem_Struct* sitem = *current_item; + if (removed_count == quantity) { + break; + } + + if (sitem && sitem->item_id == item_id) { + int stack_size = sitem->charges > 1 ? sitem->charges : 1; + if ((removed_count + stack_size) <= quantity) { + removed_count += stack_size; + is_corpse_changed = true; + itemlist.erase(current_item); + } else { + int amount_left = (quantity - removed_count); + if (amount_left > 0) { + if (stack_size > amount_left) { + removed_count += amount_left; + sitem->charges -= amount_left; + is_corpse_changed = true; + } else if (stack_size == amount_left) { + removed_count += amount_left; + itemlist.erase(current_item); + } + } + } + } + } +} + void Corpse::SetCash(uint32 in_copper, uint32 in_silver, uint32 in_gold, uint32 in_platinum) { this->copper = in_copper; this->silver = in_silver; diff --git a/zone/corpse.h b/zone/corpse.h index acbd63ac5..a395c1956 100644 --- a/zone/corpse.h +++ b/zone/corpse.h @@ -95,6 +95,7 @@ class Corpse : public Mob { int32 GetPlayerKillItem() { return player_kill_item; } void RemoveItem(uint16 lootslot); void RemoveItem(ServerLootItem_Struct* item_data); + void RemoveItemByID(uint32 item_id, int quantity = 1); void AddItem(uint32 itemnum, uint16 charges, int16 slot = 0, uint32 aug1 = 0, uint32 aug2 = 0, uint32 aug3 = 0, uint32 aug4 = 0, uint32 aug5 = 0, uint32 aug6 = 0, uint8 attuned = 0); /* Corpse: Coin */ diff --git a/zone/lua_corpse.cpp b/zone/lua_corpse.cpp index 200e13ce3..29a9eb31c 100644 --- a/zone/lua_corpse.cpp +++ b/zone/lua_corpse.cpp @@ -177,6 +177,16 @@ uint16 Lua_Corpse::GetFirstSlotByItemID(uint32 item_id) { return self->GetFirstSlotByItemID(item_id); } +void Lua_Corpse::RemoveItemByID(uint32 item_id) { + Lua_Safe_Call_Void(); + self->RemoveItemByID(item_id); +} + +void Lua_Corpse::RemoveItemByID(uint32 item_id, int quantity) { + Lua_Safe_Call_Void(); + self->RemoveItemByID(item_id, quantity); +} + Lua_Corpse_Loot_List Lua_Corpse::GetLootList(lua_State* L) { Lua_Safe_Call_Class(Lua_Corpse_Loot_List); Lua_Corpse_Loot_List ret; @@ -227,6 +237,8 @@ luabind::scope lua_register_corpse() { .def("CountItem", (uint16(Lua_Corpse::*)(uint32))&Lua_Corpse::CountItem) .def("GetItemIDBySlot", (uint32(Lua_Corpse::*)(uint16))&Lua_Corpse::GetItemIDBySlot) .def("GetFirstSlotByItemID", (uint16(Lua_Corpse::*)(uint32))&Lua_Corpse::GetFirstSlotByItemID) + .def("RemoveItemByID", (void(Lua_Corpse::*)(uint32))&Lua_Corpse::RemoveItemByID) + .def("RemoveItemByID", (void(Lua_Corpse::*)(uint32,int))&Lua_Corpse::RemoveItemByID) .def("GetLootList", (Lua_Corpse_Loot_List(Lua_Corpse::*)(lua_State* L))&Lua_Corpse::GetLootList); } diff --git a/zone/lua_corpse.h b/zone/lua_corpse.h index c8c9bf470..0df694b51 100644 --- a/zone/lua_corpse.h +++ b/zone/lua_corpse.h @@ -44,6 +44,8 @@ public: void AddItem(uint32 itemnum, uint16 charges, int16 slot, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5); uint32 GetWornItem(int16 equipSlot); void RemoveItem(uint16 lootslot); + void RemoveItemByID(uint32 item_id); + void RemoveItemByID(uint32 item_id, int quantity); void SetCash(uint32 copper, uint32 silver, uint32 gold, uint32 platinum); void RemoveCash(); bool IsEmpty(); diff --git a/zone/perl_player_corpse.cpp b/zone/perl_player_corpse.cpp index dd01acda4..f4e85c214 100644 --- a/zone/perl_player_corpse.cpp +++ b/zone/perl_player_corpse.cpp @@ -599,6 +599,24 @@ XS(XS_Corpse_GetFirstSlotByItemID) { XSRETURN(1); } +XS(XS_Corpse_RemoveItemByID); +XS(XS_Corpse_RemoveItemByID) { + dXSARGS; + if (items != 2 && items != 3) + Perl_croak(aTHX_ "Usage: Corpse::RemoveItemByID(THIS, uint32 item_id, [int quantity = 1])"); // @categories Script Utility + { + Corpse *THIS; + uint32 item_id = (uint32) SvUV(ST(1)); + int quantity = 1; + VALIDATE_THIS_IS_CORPSE; + if (items == 3) + quantity = (int) SvIV(ST(2)); + + THIS->RemoveItemByID(item_id, quantity); + } + XSRETURN_EMPTY; +} + XS(XS_Corpse_GetLootList); XS(XS_Corpse_GetLootList) { dXSARGS; @@ -672,6 +690,7 @@ XS(boot_Corpse) { newXSproto(strcpy(buf, "CountItem"), XS_Corpse_CountItem, file, "$$"); newXSproto(strcpy(buf, "GetItemIDBySlot"), XS_Corpse_GetItemIDBySlot, file, "$$"); newXSproto(strcpy(buf, "GetFirstSlotByItemID"), XS_Corpse_GetFirstSlotByItemID, file, "$$"); + newXSproto(strcpy(buf, "RemoveItemByID"), XS_Corpse_RemoveItemByID, file, "$$;$"); newXSproto(strcpy(buf, "GetLootList"), XS_Corpse_GetLootList, file, "$"); XSRETURN_YES; } From 9c6a85ff16ac820b9c5972b66db9e58665f48cbc Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 17 Sep 2021 22:27:45 -0400 Subject: [PATCH 192/624] heal code updates --- common/ruletypes.h | 1 + zone/effects.cpp | 118 ++++++++++++++++++++++++++++----------------- zone/mob.cpp | 14 ------ zone/mob.h | 1 - 4 files changed, 74 insertions(+), 60 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index b0c8074b1..533567b0e 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -380,6 +380,7 @@ RULE_BOOL(Spells, PreventFactionWarOnCharmBreak, false, "Enable spell interupts RULE_BOOL(Spells, AllowDoubleInvis, false, "Allows you to cast invisibility spells on a player that is already invisible") RULE_BOOL(Spells, AllowSpellMemorizeFromItem, false, "Allows players to memorize spells by right-clicking spell scrolls") RULE_BOOL(Spells, InvisRequiresGroup, false, "Invis requires the the target to be in group.") +RULE_INT(Spells, ClericInnateHealFocus, 5, "Clerics on live get a 5 pct innate heal focus") RULE_CATEGORY_END() RULE_CATEGORY(Combat) diff --git a/zone/effects.cpp b/zone/effects.cpp index 37726df12..7dc2c9efa 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -267,63 +267,98 @@ int32 Mob::GetExtraSpellAmt(uint16 spell_id, int32 extra_spell_amt, int32 base_s int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { - if (target == nullptr) - target = this; - if (IsNPC()) - value += value*CastToNPC()->GetSpellFocusHeal()/100; + if (IsNPC()) { + value += value * CastToNPC()->GetSpellFocusHeal() / 100; + } int32 value_BaseEffect = 0; - int16 chance = 0; - int8 modifier = 1; - bool Critical = false; + int16 critical_chance = 0; + int8 critical_modifier = 1; - value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100); + if (spells[spell_id].buffduration < 1) { + critical_chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance; + + if (spellbonuses.CriticalHealDecay) { + critical_chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay); + } + } + else { + critical_chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime; + + if (spellbonuses.CriticalRegenDecay) { + critical_chance += GetDecayEffectValue(spell_id, SE_CriticalRegenDecay); + } + } + + if (critical_chance) { + + if (spells[spell_id].override_crit_chance > 0 && critical_chance > spells[spell_id].override_crit_chance) { + critical_chance = spells[spell_id].override_crit_chance; + } + + if (zone->random.Roll(critical_chance)) { + critical_modifier = 2; //At present time no critical heal amount modifier SPA exists. + } + } + + value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id) / 100); value = value_BaseEffect; - value += int(value_BaseEffect*GetFocusEffect(focusImprovedHeal, spell_id)/100); + if (GetClass() == CLERIC) { + value += int(value_BaseEffect*RuleI(Spells, ClericInnateHealFocus) / 100); //confirmed on live parsing clerics get an innate 5 pct heal focus + } + value += int(value_BaseEffect*GetFocusEffect(focusImprovedHeal, spell_id) / 100); value += int(value_BaseEffect*GetFocusEffect(focusFcAmplifyMod, spell_id) / 100); // Instant Heals - if(spells[spell_id].buffduration < 1) { + if (spells[spell_id].buffduration < 1) { - chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance; - - chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); - - if (spellbonuses.CriticalHealDecay) - chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay); - - if (spells[spell_id].override_crit_chance > 0 && chance > spells[spell_id].override_crit_chance) - chance = spells[spell_id].override_crit_chance; - - if(chance && (zone->random.Roll(chance))) { - Critical = true; - modifier = 2; //At present time no critical heal amount modifier SPA exists. + if (target) { + value += int(value_BaseEffect + target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id) / 100); //SPA 395 ? Add before critical + value += int(value_BaseEffect + target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctIncoming, this, spell_id) / 100); //SPA 393 Add before critical } - value *= modifier; - value += GetFocusEffect(focusFcHealAmtCrit, spell_id) * modifier; - value += GetFocusEffect(focusFcHealAmt, spell_id); - value += GetFocusEffect(focusFcAmplifyAmt, spell_id); - value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); + value += GetFocusEffect(focusFcHealAmtCrit, spell_id); //SPA 396 Add before critical + + if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) { + value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value); //Item Heal Amt Add before critical + } - if(!spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt && spells[spell_id].classes[(GetClass()%17) - 1] >= GetLevel() - 5) - value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value) * modifier; + if (target) { + int incoming_heal_mod_percent = target->itembonuses.HealRate + target->spellbonuses.HealRate + target->aabonuses.HealRate; //SPA 120 modifies value after Focus Applied but before critical + incoming_heal_mod_percent = std::min(incoming_heal_mod_percent, -100); + value += value * incoming_heal_mod_percent / 100; + } - value += value*target->GetHealRate(spell_id, this)/100; + //value += value * target->GetHealRate(spell_id, this) / 100; //SPA 120 modifies value after Focus Applied but before critical + + /* + Apply critical hit modifier + */ - if (IsNPC() && CastToNPC()->GetHealScale()) + value *= critical_modifier; + value += GetFocusEffect(focusFcHealAmt, spell_id); //SPA 392 Add after critical + value += GetFocusEffect(focusFcAmplifyAmt, spell_id); //SPA 508 ? Add after critical + + if (target) { + value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); //SPA 394 Add after critical + } + + + if (IsNPC() && CastToNPC()->GetHealScale()) { value = int(static_cast(value) * CastToNPC()->GetHealScale() / 100.0f); + } - if (Critical) { + if (critical_modifier > 1) { entity_list.MessageCloseString( this, true, 100, Chat::SpellCrit, OTHER_CRIT_HEAL, GetName(), itoa(value)); - if (IsClient()) + if (IsClient()) { MessageString(Chat::SpellCrit, YOU_CRIT_HEAL, itoa(value)); + } } return value; @@ -331,20 +366,13 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { //Heal over time spells. [Heal Rate and Additional Healing effects do not increase this value] else { - - chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime; - - chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); - - if (spellbonuses.CriticalRegenDecay) - chance += GetDecayEffectValue(spell_id, SE_CriticalRegenDecay); - - if(chance && zone->random.Roll(chance)) - value *= 2; + if (critical_chance && zone->random.Roll(critical_chance)) + value *= critical_modifier; } - if (IsNPC() && CastToNPC()->GetHealScale()) + if (IsNPC() && CastToNPC()->GetHealScale()) { value = int(static_cast(value) * CastToNPC()->GetHealScale() / 100.0f); + } return value; } diff --git a/zone/mob.cpp b/zone/mob.cpp index 07d4cff7e..4f6673cb6 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3933,20 +3933,6 @@ int32 Mob::GetPositionalDmgTakenAmt(Mob *attacker) return total_amt; } - -int16 Mob::GetHealRate(uint16 spell_id, Mob* caster) { - - int16 heal_rate = 0; - - heal_rate += itembonuses.HealRate + spellbonuses.HealRate + aabonuses.HealRate; - heal_rate += GetFocusIncoming(focusFcHealPctIncoming, SE_FcHealPctIncoming, caster, spell_id); - - if(heal_rate < -99) - heal_rate = -99; - - return heal_rate; -} - void Mob::SetBottomRampageList() { auto &mob_list = entity_list.GetCloseMobList(this); diff --git a/zone/mob.h b/zone/mob.h index 53170b958..3b07bfcdf 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -801,7 +801,6 @@ public: void TrySympatheticProc(Mob *target, uint32 spell_id); bool TryFadeEffect(int slot); uint16 GetSpellEffectResistChance(uint16 spell_id); - int16 GetHealRate(uint16 spell_id, Mob* caster = nullptr); int32 GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining); int32 GetFcDamageAmtIncoming(Mob *caster, uint32 spell_id, bool use_skill = false, uint16 skill=0); int32 GetFocusIncoming(focusType type, int effect, Mob *caster, uint32 spell_id); From d4e752987e7d9ab569ba75cd3611db35120671b5 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 17 Sep 2021 23:21:03 -0400 Subject: [PATCH 193/624] fixes --- zone/common.h | 4 ++-- zone/effects.cpp | 6 ++--- zone/spell_effects.cpp | 54 +++++++++++++++++++++++------------------- 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/zone/common.h b/zone/common.h index 772441025..1f478c750 100644 --- a/zone/common.h +++ b/zone/common.h @@ -134,7 +134,7 @@ typedef enum { //focus types focusSwarmPetDuration, //@Fc, SPA: 398, SE_SwarmPetDuration, On Caster, swarm pet duration mod, base: milliseconds focusReduceRecastTime, //@Fc, SPA: 310, SE_ReduceReuseTimer, On Caster, disc reuse time mod, base: milliseconds focusBlockNextSpell, //@Fc, SPA: 335, SE_BlockNextSpellFocus, On Caster, chance to block next spell, base: chance - focusFcHealPctIncoming, //@Fc, SPA: 395, SE_FcHealPctCritIncoming, On Target, heal received mod pct, base: pct + focusFcHealPctIncoming, //@Fc, SPA: 393, SE_FcHealPctIncoming, On Target, heal received mod pct, base: pct focusFcDamageAmtIncoming, //@Fc, SPA: 297, SE_FcDamageAmtIncoming, On Target, damage taken flat amt, base: amt focusFcSpellDamageAmtIncomingPC, //@Fc, SPA: 484, SE_Fc_Spell_Damage_Amt_IncomingPC, On Target, damage taken flat amt, base: amt focusFcCastSpellOnLand, //@Fc, SPA: 481, SE_Fc_Cast_Spell_On_Land, On Target, cast spell if hit by spell, base: chance pct, limit: spellid @@ -151,7 +151,7 @@ typedef enum { //focus types focusFcAmplifyAmt, //@Fc, SPA: 508, SE_Fc_Amplify_Amt, On Caster, damage-heal-dot mod flat amt, base: amt focusFcCastTimeMod2, //@Fc, SPA: 500, SE_Fc_CastTimeMod2, On Caster, cast time mod pct, base: pct focusFcCastTimeAmt, //@Fc, SPA: 501, SE_Fc_CastTimeAmt, On Caster, cast time mod flat amt, base: milliseconds - focusFcHealPctCritIncoming, //@Fc, SPA: 393, SE_FcHealPctCritIncoming, On Target, heal received critical chance mod, base: chance pct + focusFcHealPctCritIncoming, //@Fc, SPA: 395, SE_FcHealPctCritIncoming, On Target, spell healing mod pct, base: pct focusFcHealAmt, //@Fc, SPA: 392, SE_FcHealAmt, On Caster, spell healing mod flat amt, base: amt focusFcHealAmtCrit, //@Fc, SPA: 396, SE_FcHealAmtCrit, On Caster, spell healing mod flat amt, base: amt } focusType; //Any new FocusType needs to be added to the Mob::IsFocus function diff --git a/zone/effects.cpp b/zone/effects.cpp index 7dc2c9efa..fcc4d8f1c 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -316,8 +316,8 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { if (spells[spell_id].buffduration < 1) { if (target) { - value += int(value_BaseEffect + target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id) / 100); //SPA 395 ? Add before critical - value += int(value_BaseEffect + target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctIncoming, this, spell_id) / 100); //SPA 393 Add before critical + value += int(value_BaseEffect + target->GetFocusIncoming(focusFcHealPctIncoming, SE_FcHealPctIncoming, this, spell_id) / 100); //SPA 393 Add before critical + value += int(value_BaseEffect + target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id) / 100); //SPA 395 Add before critical (?) } value += GetFocusEffect(focusFcHealAmtCrit, spell_id); //SPA 396 Add before critical @@ -331,8 +331,6 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { incoming_heal_mod_percent = std::min(incoming_heal_mod_percent, -100); value += value * incoming_heal_mod_percent / 100; } - - //value += value * target->GetHealRate(spell_id, this) / 100; //SPA 120 modifies value after Focus Applied but before critical /* Apply critical hit modifier diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 21a3a5f10..b8d9ec55b 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -5741,7 +5741,7 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_FcHealPctCritIncoming: if (type == focusFcHealPctCritIncoming) { - value = focus_spell.base[i]; + value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); } break; @@ -5759,7 +5759,7 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_FcHealPctIncoming: if (type == focusFcHealPctIncoming) { - value = focus_spell.base[i]; + value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); } break; @@ -5923,7 +5923,7 @@ void Mob::TryTriggerOnCastFocusEffect(focusType type, uint16 spell_id) } } - // Only use of this focus per AA effect. + // Only use one of this focus per AA effect. if (IsClient() && aabonuses.FocusEffects[type]) { for (const auto &aa : aa_ranks) { auto ability_rank = zone->GetAlternateAdvancementAbilityAndRank(aa.first, aa.second.first); @@ -6937,44 +6937,46 @@ int32 Mob::GetFocusIncoming(focusType type, int effect, Mob *caster, uint32 spel Example: When your target has a focus limited buff that increases amount of healing on them. */ - if (!caster) + if (!caster) { return 0; + } int value = 0; if (spellbonuses.FocusEffects[type]){ - int32 tmp_focus = 0; - int tmp_buffslot = -1; + int32 tmp_focus = 0; + int tmp_buffslot = -1; - int buff_count = GetMaxTotalSlots(); - for(int i = 0; i < buff_count; i++) { + int buff_count = GetMaxTotalSlots(); + for(int i = 0; i < buff_count; i++) { - if((IsValidSpell(buffs[i].spellid) && IsEffectInSpell(buffs[i].spellid, effect))){ + if((IsValidSpell(buffs[i].spellid) && IsEffectInSpell(buffs[i].spellid, effect))){ - int32 focus = caster->CalcFocusEffect(type, buffs[i].spellid, spell_id); + int32 focus = caster->CalcFocusEffect(type, buffs[i].spellid, spell_id); - if (!focus) - continue; + if (!focus) { + continue; + } - if (tmp_focus && focus > tmp_focus){ - tmp_focus = focus; - tmp_buffslot = i; - } + if (tmp_focus && focus > tmp_focus){ + tmp_focus = focus; + tmp_buffslot = i; + } - else if (!tmp_focus){ - tmp_focus = focus; - tmp_buffslot = i; - } + else if (!tmp_focus){ + tmp_focus = focus; + tmp_buffslot = i; } } - - value = tmp_focus; - - if (tmp_buffslot >= 0) - CheckNumHitsRemaining(NumHit::MatchingSpells, tmp_buffslot); } + value = tmp_focus; + + if (tmp_buffslot >= 0) + CheckNumHitsRemaining(NumHit::MatchingSpells, tmp_buffslot); + } + return value; } @@ -8496,6 +8498,8 @@ bool Mob::CanFocusUseRandomEffectivenessByType(focusType type) case focusSpellHateMod: case focusSpellVulnerability: case focusFcSpellDamagePctIncomingPC: + case focusFcHealPctIncoming: + case focusFcHealPctCritIncoming: return true; } From 442850aebb935e2b0fc6611755e68bfc978aabdc Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 19 Sep 2021 16:16:02 -0400 Subject: [PATCH 194/624] [Spells] Update to SPA305 (#1545) minor fix to allow for effects with negative values. --- zone/bonuses.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 19ae9aac1..280726ffa 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1256,8 +1256,11 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) } case SE_MitigateDamageShield: { - if (base1 < 0) + + //AA that increase mitigation are set to negative. + if (base1 < 0) { base1 = base1 * (-1); + } newbon->DSMitigationOffHand += base1; break; @@ -2663,8 +2666,15 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_MitigateDamageShield: { - if (effect_value < 0) - effect_value = effect_value*-1; + /* + Bard songs have identical negative base value and positive max + The effect for the songs should increase mitigation. There are + spells that do decrease the mitigation with just negative base values. + To be consistent all values that increase mitigation will be set to positives + */ + if (max > 0 && effect_value < 0) { + effect_value = max; + } new_bonus->DSMitigationOffHand += effect_value; break; From 46edd56accc2bf68ee1c8fd5370bb45ff9e8cc49 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 19 Sep 2021 16:16:21 -0400 Subject: [PATCH 195/624] [Spells] Update SPA 101 SE_CompleteHeal (#1544) Fixed buff stacking issue --- zone/bonuses.cpp | 4 ++++ zone/common.h | 1 + zone/spell_effects.cpp | 28 +++++----------------------- zone/spells.cpp | 5 +++++ 4 files changed, 15 insertions(+), 23 deletions(-) diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 280726ffa..2bde70e63 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -3671,6 +3671,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne new_bonus->ZoneSuspendMinion = effect_value; break; + case SE_CompleteHeal: + new_bonus->CompleteHealBuffBlocker = true; + break; + //Special custom cases for loading effects on to NPC from 'npc_spels_effects' table if (IsAISpellEffect) { diff --git a/zone/common.h b/zone/common.h index 772441025..4fb9e82c7 100644 --- a/zone/common.h +++ b/zone/common.h @@ -554,6 +554,7 @@ struct StatBonuses { int32 ItemEnduranceRegenCap; // modify endurance regen cap int32 WeaponStance[WEAPON_STANCE_TYPE_MAX +1];// base = trigger spell id, base2 = 0 is 2h, 1 is shield, 2 is dual wield, [0]spid 2h, [1]spid shield, [2]spid DW bool ZoneSuspendMinion; // base 1 allows suspended minions to zone + bool CompleteHealBuffBlocker; // Use in SPA 101 to prevent recast of complete heal from this effect till blocker buff is removed. // AAs uint16 SecondaryForte; // allow a second skill to be specialized with a cap of this value. diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 21a3a5f10..bb2b3ca76 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -343,31 +343,13 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Complete Heal"); #endif - //make sure they are not allready affected by this... - //I think that is the point of making this a buff. - //this is in the wrong spot, it should be in the immune - //section so the buff timer does not get refreshed! - - int i; - bool inuse = false; - int buff_count = GetMaxTotalSlots(); - for(i = 0; i < buff_count; i++) { - if(buffs[i].spellid == spell_id && i != buffslot) { - Message(0, "You must wait before you can be affected by this spell again."); - inuse = true; - break; - } - } - if(inuse) - break; - - int32 val = 0; - val = 7500 * effect_value; - if (caster) + int val = 7500 * effect_value; + if (caster) { val = caster->GetActSpellHealing(spell_id, val, this); - - if (val > 0) + } + if (val > 0) { HealDamage(val, caster); + } break; } diff --git a/zone/spells.cpp b/zone/spells.cpp index c098060a0..713f70d58 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2931,6 +2931,11 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, LogSpells("Check Stacking on old [{}] ([{}]) @ lvl [{}] (by [{}]) vs. new [{}] ([{}]) @ lvl [{}] (by [{}])", sp1.name, spellid1, caster_level1, (caster1==nullptr)?"Nobody":caster1->GetName(), sp2.name, spellid2, caster_level2, (caster2==nullptr)?"Nobody":caster2->GetName()); + if (spellbonuses.CompleteHealBuffBlocker && IsEffectInSpell(spellid2, SE_CompleteHeal)) { + Message(0, "You must wait before you can be affected by this spell again."); + return -1; + } + if (spellid1 == spellid2 ) { if (!IsStackableDot(spellid1) && !IsEffectInSpell(spellid1, SE_ManaBurn)) { // mana burn spells we need to use the stacking command blocks live actually checks those first, we should probably rework to that too if (caster_level1 > caster_level2) { // cur buff higher level than new From f715ccd368ef75e73f48270724b94edf38b1cb6c Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 19 Sep 2021 16:16:38 -0400 Subject: [PATCH 196/624] [Bug Fix] Fixes EVENT_DISCONNECT for /quit and /exit. (#1542) /quit and /exit will now properly parse to EVENT_DISCONNECT so operators can do things on disconnect to these players, previously it only functioned for /camp. --- zone/client_process.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 0fd418131..34f2bb877 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -183,6 +183,8 @@ bool Client::Process() { SetDynamicZoneMemberStatus(DynamicZoneMemberStatus::Offline); + parse->EventPlayer(EVENT_DISCONNECT, this, "", 0); + return false; //delete client } From 71870cbd1ce2d5c17f1a5d8c572c75958424b475 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 19 Sep 2021 16:16:56 -0400 Subject: [PATCH 197/624] [Spells] Update to SPA 442 and 443 (SE_TriggerOnReqTarget, SE_TriggerOnReqCaster) (#1543) * Update to SPA 442 and 443 Use SpellRestriction Id's and updated PassCastRestriction code * Update mob.cpp --- common/spdat.h | 4 +- zone/attack.cpp | 11 +++--- zone/bonuses.cpp | 2 +- zone/client_process.cpp | 2 +- zone/common.h | 2 +- zone/lua_stat_bonuses.cpp | 2 +- zone/mob.cpp | 80 +++++---------------------------------- zone/mob.h | 2 +- zone/spell_effects.cpp | 22 ++++++----- zone/spells.cpp | 6 +-- 10 files changed, 37 insertions(+), 96 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 81cb122eb..65a146a82 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -1125,8 +1125,8 @@ typedef enum { #define SE_Assassinate 439 // implemented[AA] - Assassinate damage #define SE_FinishingBlowLvl 440 // implemented[AA] - Sets the level Finishing blow can be triggered on an NPC #define SE_DistanceRemoval 441 // implemented - Buff is removed from target when target moves X amount of distance away from where initially hit. -#define SE_TriggerOnReqTarget 442 // implemented - triggers a spell which a certain criteria are met (below X amount of hp,mana,end, number of pets on hatelist) -#define SE_TriggerOnReqCaster 443 // implemented - triggers a spell which a certain criteria are met (below X amount of hp,mana,end, number of pets on hatelist) +#define SE_TriggerOnReqTarget 442 // implemented, @SpellTrigger, triggers a spell when Target Requirement conditions are met (see enum SpellRestriction for IDs), base: spellid, limit: SpellRestriction ID, max: none, Note: Usually cast on a target +#define SE_TriggerOnReqCaster 443 // implemented, @SpellTrigger, triggers a spell when Caster Requirement conditions are met (see enum SpellRestriction for IDs), base: spellid, limit: SpellRestriction ID, max: none, Note: Usually self only #define SE_ImprovedTaunt 444 // implemented - Locks Aggro On Caster and Decrease other Players Aggro by X% on NPC targets below level Y //#define SE_AddMercSlot 445 // *not implemented[AA] - [Hero's Barracks] Allows you to conscript additional mercs. #define SE_AStacker 446 // implementet - bufff stacking blocker (26219 | Qirik's Watch) diff --git a/zone/attack.cpp b/zone/attack.cpp index 16ee8e69e..a41f0911a 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2701,8 +2701,9 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b } } - if (other->IsNPC() && (other->IsPet() || other->CastToNPC()->GetSwarmOwner() > 0)) - TryTriggerOnValueAmount(false, false, false, true); + if (other->IsNPC() && (other->IsPet() || other->CastToNPC()->GetSwarmOwner() > 0)) { + TryTriggerOnCastRequirement(); + } if (IsClient() && !IsAIControlled()) return; @@ -3343,7 +3344,7 @@ int32 Mob::ReduceAllDamage(int32 damage) if (GetMana() >= mana_reduced) { damage -= mana_reduced; SetMana(GetMana() - mana_reduced); - TryTriggerOnValueAmount(false, true); + TryTriggerOnCastRequirement(); } } @@ -3356,7 +3357,7 @@ int32 Mob::ReduceAllDamage(int32 damage) if (IsClient() && CastToClient()->GetEndurance() >= endurance_drain) { damage -= damage_reduced; CastToClient()->SetEndurance(CastToClient()->GetEndurance() - endurance_drain); - TryTriggerOnValueAmount(false, false, true); + TryTriggerOnCastRequirement(); } } @@ -3646,7 +3647,7 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const TryDeathSave(); } - TryTriggerOnValueAmount(true); + TryTriggerOnCastRequirement(); //fade mez if we are mezzed if (IsMezzed() && attacker) { diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 2bde70e63..390917ff7 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -3208,7 +3208,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_TriggerOnReqTarget: case SE_TriggerOnReqCaster: - new_bonus->TriggerOnValueAmount = true; + new_bonus->TriggerOnCastRequirement = true; break; case SE_DivineAura: diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 34f2bb877..a2c34a007 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -1866,7 +1866,7 @@ void Client::DoEnduranceUpkeep() { if(upkeep_sum != 0){ SetEndurance(GetEndurance() - upkeep_sum); - TryTriggerOnValueAmount(false, false, true); + TryTriggerOnCastRequirement(); } if (!has_effect) diff --git a/zone/common.h b/zone/common.h index 4fb9e82c7..ba2f1eac9 100644 --- a/zone/common.h +++ b/zone/common.h @@ -603,7 +603,7 @@ struct StatBonuses { int32 FinishingBlow[2]; // Chance to do a finishing blow for specified damage amount. uint32 FinishingBlowLvl[2]; // Sets max level an NPC can be affected by FB. (base1 = lv, base2= ???) int32 ShieldEquipDmgMod; // Increases weapon's base damage by base1 % when shield is equipped (indirectly increasing hate) - bool TriggerOnValueAmount; // Triggers off various different conditions, bool to check if client has effect. + bool TriggerOnCastRequirement; // Triggers off various different conditions defined as emum SpellRestrictions int8 StunBashChance; // chance to stun with bash. int8 IncreaseChanceMemwipe; // increases chance to memory wipe int8 CriticalMend; // chance critical monk mend diff --git a/zone/lua_stat_bonuses.cpp b/zone/lua_stat_bonuses.cpp index ef012c6d7..40805d648 100644 --- a/zone/lua_stat_bonuses.cpp +++ b/zone/lua_stat_bonuses.cpp @@ -987,7 +987,7 @@ int32 Lua_StatBonuses::GetShieldEquipDmgMod() const { bool Lua_StatBonuses::GetTriggerOnValueAmount() const { Lua_Safe_Call_Bool(); - return self->TriggerOnValueAmount; + return self->TriggerOnCastRequirement; } int8 Lua_StatBonuses::GetStunBashChance() const { diff --git a/zone/mob.cpp b/zone/mob.cpp index 07d4cff7e..9686ff29f 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3602,81 +3602,20 @@ bool Mob::TrySpellTrigger(Mob *target, uint32 spell_id, int effect) return false; } - - -void Mob::TryTriggerOnValueAmount(bool IsHP, bool IsMana, bool IsEndur, bool IsPet) +void Mob::TryTriggerOnCastRequirement() { - /* - At present time there is no obvious difference between ReqTarget and ReqCaster - ReqTarget is typically used in spells cast on a target where the trigger occurs on that target. - ReqCaster is typically self only spells where the triggers on self. - Regardless both trigger on the owner of the buff. - */ - - /* - Base2 Range: 1004 = Below < 80% HP - Base2 Range: 500-520 = Below (base2 - 500)*5 HP - Base2 Range: 521 = Below (?) Mana UKNOWN - Will assume its 20% unless proven otherwise - Base2 Range: 522 = Below (40%) Endurance - Base2 Range: 523 = Below (40%) Mana - Base2 Range: 220-? = Number of pets on hatelist to trigger (base2 - 220) (Set at 30 pets max for now) - 38311 = < 10% mana; - */ - - if (!spellbonuses.TriggerOnValueAmount) - return; - - if (spellbonuses.TriggerOnValueAmount){ - + if (spellbonuses.TriggerOnCastRequirement) { int buff_count = GetMaxTotalSlots(); - - for(int e = 0; e < buff_count; e++){ - - uint32 spell_id = buffs[e].spellid; - - if (IsValidSpell(spell_id)){ - - for(int i = 0; i < EFFECT_COUNT; i++){ - + for (int e = 0; e < buff_count; e++) { + int spell_id = buffs[e].spellid; + if (IsValidSpell(spell_id)) { + for (int i = 0; i < EFFECT_COUNT; i++) { if ((spells[spell_id].effectid[i] == SE_TriggerOnReqTarget) || (spells[spell_id].effectid[i] == SE_TriggerOnReqCaster)) { - - int base2 = spells[spell_id].base2[i]; - bool use_spell = false; - - if (IsHP){ - if ((base2 >= 500 && base2 <= 520) && GetHPRatio() < (base2 - 500)*5) - use_spell = true; - - else if (base2 == 1004 && GetHPRatio() < 80) - use_spell = true; - } - - else if (IsMana){ - if ( (base2 = 521 && GetManaRatio() < 20) || (base2 = 523 && GetManaRatio() < 40)) - use_spell = true; - - else if (base2 == 38311 && GetManaRatio() < 10) - use_spell = true; - } - - else if (IsEndur){ - if (base2 == 522 && GetEndurancePercent() < 40){ - use_spell = true; - } - } - - else if (IsPet){ - int count = hate_list.GetSummonedPetCountOnHateList(this); - if ((base2 >= 220 && base2 <= 250) && count >= (base2 - 220)){ - use_spell = true; - } - } - - if (use_spell){ + if (PassCastRestriction(spells[spell_id].base2[i])) { SpellFinished(spells[spell_id].base[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff); - - if(!TryFadeEffect(e)) + if (!TryFadeEffect(e)) { BuffFadeBySlot(e); + } } } } @@ -3685,7 +3624,6 @@ void Mob::TryTriggerOnValueAmount(bool IsHP, bool IsMana, bool IsEndur, bool IsP } } - //Twincast Focus effects should stack across different types (Spell, AA - when implemented ect) void Mob::TryTwincast(Mob *caster, Mob *target, uint32 spell_id) { diff --git a/zone/mob.h b/zone/mob.h index 53170b958..2095d38fc 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -796,7 +796,7 @@ public: void TryTriggerOnCastFocusEffect(focusType type, uint16 spell_id); bool TryTriggerOnCastProc(uint16 focusspellid, uint16 spell_id, uint16 proc_spellid); bool TrySpellTrigger(Mob *target, uint32 spell_id, int effect); - void TryTriggerOnValueAmount(bool IsHP = false, bool IsMana = false, bool IsEndur = false, bool IsPet = false); + void TryTriggerOnCastRequirement(); void TryTwincast(Mob *caster, Mob *target, uint32 spell_id); void TrySympatheticProc(Mob *target, uint32 spell_id); bool TryFadeEffect(int slot); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index bb2b3ca76..d5b7a8702 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -369,7 +369,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove caster->SetMana(caster->GetMana() + std::abs(effect_value)); if (effect_value < 0) - TryTriggerOnValueAmount(false, true); + TryTriggerOnCastRequirement(); #ifdef SPELL_EFFECT_SPAM if (caster) caster->Message(Chat::White, "You have gained %+i mana!", effect_value); @@ -385,7 +385,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove SetMana(GetMana() + effect_value); if (effect_value < 0) - TryTriggerOnValueAmount(false, true); + TryTriggerOnCastRequirement(); } break; @@ -2440,8 +2440,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #endif if(IsClient()) { CastToClient()->SetEndurance(CastToClient()->GetEndurance() + effect_value); - if (effect_value < 0) - TryTriggerOnValueAmount(false, false, true); + if (effect_value < 0) { + TryTriggerOnCastRequirement(); + } } break; } @@ -2452,10 +2453,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove snprintf(effect_desc, _EDLEN, "Current Endurance Once: %+i", effect_value); #endif - if(IsClient()) { + if (IsClient()) { CastToClient()->SetEndurance(CastToClient()->GetEndurance() + effect_value); - if (effect_value < 0) - TryTriggerOnValueAmount(false, false, true); + if (effect_value < 0) { + TryTriggerOnCastRequirement(); + } } break; } @@ -2634,7 +2636,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove int32 mana_to_use = GetMana() - spell.base[i]; if(mana_to_use > -1) { SetMana(GetMana() - spell.base[i]); - TryTriggerOnValueAmount(false, true); + TryTriggerOnCastRequirement(); // we take full dmg(-10 to make the damage the right sign) mana_damage = spell.base[i] / -10 * spell.base2[i]; Damage(caster, mana_damage, spell_id, spell.skill, false, i, true); @@ -2654,7 +2656,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove int32 end_to_use = CastToClient()->GetEndurance() - spell.base[i]; if(end_to_use > -1) { CastToClient()->SetEndurance(CastToClient()->GetEndurance() - spell.base[i]); - TryTriggerOnValueAmount(false, false, true); + TryTriggerOnCastRequirement(); // we take full dmg(-10 to make the damage the right sign) end_damage = spell.base[i] / -10 * spell.base2[i]; Damage(caster, end_damage, spell_id, spell.skill, false, i, true); @@ -2736,7 +2738,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove else { dmg = ratio*max_mana/10; caster->SetMana(caster->GetMana() - max_mana); - TryTriggerOnValueAmount(false, true); + TryTriggerOnCastRequirement(); } if(IsDetrimentalSpell(spell_id)) { diff --git a/zone/spells.cpp b/zone/spells.cpp index 713f70d58..9a3270d12 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -383,7 +383,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, GetName() ); - TryTriggerOnValueAmount(false, true); + TryTriggerOnCastRequirement(); return(false); } @@ -2467,7 +2467,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui LogSpells("Spell [{}]: consuming [{}] mana", spell_id, mana_used); if (!DoHPToManaCovert(mana_used)) { SetMana(GetMana() - mana_used); - TryTriggerOnValueAmount(false, true); + TryTriggerOnCastRequirement(); } } // one may want to check if this is a disc or not, but we actually don't, there are non disc stuff that have end cost @@ -2478,7 +2478,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui if (mgb) end_cost *= 2; SetEndurance(GetEndurance() - EQ::ClampUpper(end_cost, GetEndurance())); - TryTriggerOnValueAmount(false, false, true); + TryTriggerOnCastRequirement(); } if (mgb) SetMGB(false); From df9d6bc50636550767b2f41c04e08288b8d8fcd0 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 19 Sep 2021 16:17:10 -0400 Subject: [PATCH 198/624] [Spells] Corrected implementation of SE_Purify 291 (#1541) * Correct implementation of spa291 * debug removal --- common/spdat.h | 4 +++- zone/spell_effects.cpp | 24 +++++++++++++++++------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 65a146a82..64e831516 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -153,8 +153,10 @@ #define SPELL_ACTING_SPIRIT_I 1921 #define SPELL_ACTING_SPIRIT_II 1922 #define SPELL_RESURRECTION_SICKNESS 756 +#define SPELL_RESURRECTION_SICKNESS4 757 #define SPELL_RESURRECTION_SICKNESS2 5249 #define SPELL_REVIVAL_SICKNESS 13087 +#define SPELL_RESURRECTION_SICKNESS3 37624 #define SPELL_PACT_OF_HATE_RECOURSE 40375 #define SPELL_INCENDIARY_OOZE_BUFF 32513 @@ -974,7 +976,7 @@ typedef enum { #define SE_SkillAttackProc 288 // implemented[AA] - Chance to proc spell on skill attack usage (ex. Dragon Punch) #define SE_CastOnFadeEffect 289 // implemented - Triggers only if fades after natural duration. #define SE_IncreaseRunSpeedCap 290 // implemented[AA] - increases run speed over the hard cap -#define SE_Purify 291 // implemented - Removes determental effects +#define SE_Purify 291 // implemented, @Dispel, remove up specified amount of detiremental spells, base: amt removed, limit: none, max: none, Note: excluding charm, fear, resurrection, and revival sickness #define SE_StrikeThrough2 292 // implemented[AA] - increasing chance of bypassing an opponent's special defenses, such as dodge, block, parry, and riposte. #define SE_FrontalStunResist 293 // implemented[AA] - Reduce chance to be stunned from front. -- live descriptions sounds like this isn't limited to frontal anymore #define SE_CriticalSpellChance 294 // implemented - increase chance to critical hit and critical damage modifier. diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index d5b7a8702..ea9c879b0 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1118,13 +1118,23 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_Purify: { - //Attempt to remove all Deterimental buffs. - int buff_count = GetMaxTotalSlots(); - for(int slot = 0; slot < buff_count; slot++) { - if (buffs[slot].spellid != SPELL_UNKNOWN && - IsDetrimentalSpell(buffs[slot].spellid) && spells[buffs[slot].spellid].dispel_flag == 0) - { - if (caster && TryDispel(caster->GetLevel(),buffs[slot].casterlevel, effect_value)){ + //Attempt to remove up to base amount of detrimental effects (excluding charm, fear, resurrection, and revival sickness). + int purify_count = spells[spell_id].base[i]; + if (purify_count > GetMaxTotalSlots()) { + purify_count = GetMaxTotalSlots(); + } + + for(int slot = 0; slot < purify_count; slot++) { + if (IsValidSpell(buffs[slot].spellid) && IsDetrimentalSpell(buffs[slot].spellid)){ + + if (!IsEffectInSpell(buffs[slot].spellid, SE_Charm) && + !IsEffectInSpell(buffs[slot].spellid, SE_Fear) && + buffs[slot].spellid != SPELL_RESURRECTION_SICKNESS && + buffs[slot].spellid != SPELL_RESURRECTION_SICKNESS2 && + buffs[slot].spellid != SPELL_RESURRECTION_SICKNESS3 && + buffs[slot].spellid != SPELL_RESURRECTION_SICKNESS4 && + buffs[slot].spellid != SPELL_REVIVAL_SICKNESS) + { BuffFadeBySlot(slot); } } From 80493719f22ada2d332a88072927b71e963ba258 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 19 Sep 2021 16:19:29 -0400 Subject: [PATCH 199/624] [Summoning] Make Summon a bit more live like (#1539) Pretty sure the distance should probably be melee range / 2 but ahh yeah. Can't do that. Hopefully 5 units isn't too far. --- zone/mob.cpp | 11 +++++++++-- zone/mob.h | 1 + zone/waypoints.cpp | 26 ++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index 9686ff29f..69bf30531 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -2838,10 +2838,17 @@ bool Mob::HateSummon() { if(summon_level == 1) { entity_list.MessageClose(this, true, 500, Chat::Say, "%s says 'You will not evade me, %s!' ", GetCleanName(), target->GetCleanName() ); + auto new_pos = m_Position; + float angle = new_pos.w - target->GetHeading(); + new_pos.w = target->GetHeading(); + + // probably should be like half melee range, but we can't get melee range nicely because reasons :) + new_pos = target->TryMoveAlong(new_pos, 5.0f, angle); + if (target->IsClient()) - target->CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), m_Position.x, m_Position.y, m_Position.z, target->GetHeading(), 0, SummonPC); + target->CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), new_pos.x, new_pos.y, new_pos.z, new_pos.w, 0, SummonPC); else - target->GMMove(m_Position.x, m_Position.y, m_Position.z, target->GetHeading()); + target->GMMove(new_pos.x, new_pos.y, new_pos.z, new_pos.w); return true; } else if(summon_level == 2) { diff --git a/zone/mob.h b/zone/mob.h index 2095d38fc..9ccf846c9 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -623,6 +623,7 @@ public: void Teleport(const glm::vec3 &pos); void Teleport(const glm::vec4 &pos); void TryMoveAlong(float distance, float angle, bool send = true); + glm::vec4 TryMoveAlong(const glm::vec4 &start, float distance, float angle); void ProcessForcedMovement(); inline void IncDeltaX(float in) { m_Delta.x += in; } inline void IncDeltaY(float in) { m_Delta.y += in; } diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index 76db92dd6..97e9d6091 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -935,6 +935,32 @@ void Mob::TryMoveAlong(float distance, float angle, bool send) Teleport(new_pos); } +// like above, but takes a starting position and returns a new location instead of actually moving +glm::vec4 Mob::TryMoveAlong(const glm::vec4 &start, float distance, float angle) +{ + angle += start.w; + angle = FixHeading(angle); + + glm::vec3 tmp_pos; + glm::vec3 new_pos = start; + new_pos.x += distance * g_Math.FastSin(angle); + new_pos.y += distance * g_Math.FastCos(angle); + new_pos.z += GetZOffset(); + + if (zone->HasMap()) { + auto new_z = zone->zonemap->FindClosestZ(new_pos, nullptr); + if (new_z != BEST_Z_INVALID) + new_pos.z = new_z; + + if (zone->zonemap->LineIntersectsZone(start, new_pos, 0.0f, &tmp_pos)) + new_pos = tmp_pos; + } + + new_pos.z = GetFixedZ(new_pos); + + return {new_pos.x, new_pos.y, new_pos.z, start.w}; +} + int ZoneDatabase::GetHighestGrid(uint32 zoneid) { std::string query = StringFormat("SELECT COALESCE(MAX(id), 0) FROM grid WHERE zoneid = %i", zoneid); From 8eef7bb283d428f6f789c0ff14ef647a5265a821 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 19 Sep 2021 16:22:51 -0400 Subject: [PATCH 200/624] [Quest API] Add EVENT_COMBINE to Perl and Lua. (#1536) - Exports $container_slot in Perl. - Exports e.container_slot in Lua. Allows you to perform events when clicking combine in a tradeskill container. --- zone/embparser.cpp | 6 ++++++ zone/event_codes.h | 1 + zone/lua_parser.cpp | 2 ++ zone/lua_parser_events.cpp | 5 +++++ zone/lua_parser_events.h | 2 ++ zone/tradeskills.cpp | 8 ++++++++ 6 files changed, 24 insertions(+) diff --git a/zone/embparser.cpp b/zone/embparser.cpp index 535e3c356..f4ea70356 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -122,6 +122,7 @@ const char *QuestEventSubroutines[_LargestEventID] = { "EVENT_BOT_COMMAND", "EVENT_WARP", "EVENT_TEST_BUFF", + "EVENT_COMBINE", "EVENT_CONSIDER", "EVENT_CONSIDER_CORPSE" }; @@ -1657,6 +1658,11 @@ void PerlembParser::ExportEventVariables( break; } + case EVENT_COMBINE: { + ExportVar(package_name.c_str(), "container_slot", std::stoi(data)); + break; + } + default: { break; } diff --git a/zone/event_codes.h b/zone/event_codes.h index 87143d11e..4a915ce9c 100644 --- a/zone/event_codes.h +++ b/zone/event_codes.h @@ -90,6 +90,7 @@ typedef enum { EVENT_BOT_COMMAND, EVENT_WARP, EVENT_TEST_BUFF, + EVENT_COMBINE, EVENT_CONSIDER, EVENT_CONSIDER_CORPSE, _LargestEventID diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index 3a01c643f..641f24040 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -133,6 +133,7 @@ const char *LuaEvents[_LargestEventID] = { "event_bot_command", "event_warp", "event_test_buff", + "event_combine", "event_consider", "event_consider_corpse" }; @@ -222,6 +223,7 @@ LuaParser::LuaParser() { PlayerArgumentDispatch[EVENT_COMBINE_VALIDATE] = handle_player_combine_validate; PlayerArgumentDispatch[EVENT_BOT_COMMAND] = handle_player_bot_command; PlayerArgumentDispatch[EVENT_WARP] = handle_player_warp; + PlayerArgumentDispatch[EVENT_COMBINE] = handle_player_quest_combine; PlayerArgumentDispatch[EVENT_CONSIDER] = handle_player_consider; PlayerArgumentDispatch[EVENT_CONSIDER_CORPSE] = handle_player_consider_corpse; diff --git a/zone/lua_parser_events.cpp b/zone/lua_parser_events.cpp index 4a0c154ca..0734c9e19 100644 --- a/zone/lua_parser_events.cpp +++ b/zone/lua_parser_events.cpp @@ -573,6 +573,11 @@ void handle_player_warp(QuestInterface* parse, lua_State* L, Client* client, std lua_setfield(L, -2, "from_z"); } +void handle_player_quest_combine(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector* extra_pointers) { + lua_pushinteger(L, std::stoi(data)); + lua_setfield(L, -2, "container_slot"); + } + void handle_player_consider(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector* extra_pointers) { lua_pushinteger(L, std::stoi(data)); lua_setfield(L, -2, "entity_id"); diff --git a/zone/lua_parser_events.h b/zone/lua_parser_events.h index 2b6512c15..850116075 100644 --- a/zone/lua_parser_events.h +++ b/zone/lua_parser_events.h @@ -105,6 +105,8 @@ void handle_player_bot_command(QuestInterface *parse, lua_State* L, Client* clie std::vector *extra_pointers); void handle_player_warp(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector* extra_pointers); +void handle_player_quest_combine(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, + std::vector* extra_pointers); void handle_player_consider(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector* extra_pointers); void handle_player_consider_corpse(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index 4beaedf52..7e0ce5857 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -375,6 +375,14 @@ void Object::HandleCombine(Client* user, const NewCombine_Struct* in_combine, Ob } DBTradeskillRecipe_Struct spec; + + if (parse->EventPlayer(EVENT_COMBINE, user, std::to_string(in_combine->container_slot), 0) == 1) { + auto outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0); + user->QueuePacket(outapp); + safe_delete(outapp); + return; + } + if (!content_db.GetTradeRecipe(container, c_type, some_id, user->CharacterID(), &spec)) { LogTradeskillsDetail("[HandleCombine] Check 2"); From 24c079dca4dca2c1d5ece5cac410ef279c4bd1d2 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 19 Sep 2021 15:25:52 -0500 Subject: [PATCH 201/624] [Hotfix] Fix freeze formatting for Quest API parsing (Spire) (#1547) --- zone/perl_client.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 8cb3d56e9..2030ea061 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -4016,7 +4016,7 @@ XS(XS_Client_Freeze); XS(XS_Client_Freeze) { dXSARGS; if (items != 1) - Perl_croak(aTHX_ "Usage: Client:Freeze(THIS)"); + Perl_croak(aTHX_ "Usage: Client::Freeze(THIS)"); { Client *THIS; VALIDATE_THIS_IS_CLIENT; @@ -4029,7 +4029,7 @@ XS(XS_Client_UnFreeze); XS(XS_Client_UnFreeze) { dXSARGS; if (items != 1) - Perl_croak(aTHX_ "Usage: Client:UnFreeze(THIS)"); + Perl_croak(aTHX_ "Usage: Client::UnFreeze(THIS)"); { Client *THIS; VALIDATE_THIS_IS_CLIENT; From c15c54e9209c37a0fd4660a48b1cdd8aef03bddf Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 19 Sep 2021 19:15:14 -0400 Subject: [PATCH 202/624] [Quest API] Cross zone and world wide method overhaul. (#1520) * [Quest API] Cross zone and world wide method overhaul. - Adds support for Character ID, Character Name, and Expedition ID to all cross zone methods that did not have a method. - Adds worldwide LDoN Updates. - Shrinks the number of packets and structs from 83 to 17. No quest functionality will be affected by this, as the only changes are the underlying method used to send the cross zone and world wide data. * Formatting, organization, and fixing of improper exports. * Finalize comb through of variable types, update types, etc. * Merge fixes. --- common/servertalk.h | 773 ++----- world/clientlist.cpp | 12 +- world/console.cpp | 17 +- world/shared_task_manager.cpp | 2 +- world/zoneserver.cpp | 93 +- zone/client.cpp | 14 +- zone/command.cpp | 3 +- zone/embparser_api.cpp | 3936 ++++++++++++++++++++------------- zone/lua_general.cpp | 2083 +++++++++++------ zone/perl_raids.cpp | 2 +- zone/questmgr.cpp | 1080 ++------- zone/questmgr.h | 97 +- zone/worldserver.cpp | 1590 +++++++------ 13 files changed, 4916 insertions(+), 4786 deletions(-) diff --git a/common/servertalk.h b/common/servertalk.h index 3774a07cc..00510d60b 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -227,90 +227,24 @@ #define ServerOP_HotReloadQuests 0x4011 #define ServerOP_UpdateSchedulerEvents 0x4012 -#define ServerOP_CZCastSpellPlayer 0x4500 -#define ServerOP_CZCastSpellGroup 0x4501 -#define ServerOP_CZCastSpellRaid 0x4502 -#define ServerOP_CZCastSpellGuild 0x4503 -#define ServerOP_CZMarqueePlayer 0x4504 -#define ServerOP_CZMarqueeGroup 0x4505 -#define ServerOP_CZMarqueeRaid 0x4506 -#define ServerOP_CZMarqueeGuild 0x4507 -#define ServerOP_CZMessagePlayer 0x4508 -#define ServerOP_CZMessageGroup 0x4509 -#define ServerOP_CZMessageRaid 0x4510 -#define ServerOP_CZMessageGuild 0x4511 -#define ServerOP_CZMovePlayer 0x4512 -#define ServerOP_CZMoveGroup 0x4513 -#define ServerOP_CZMoveRaid 0x4514 -#define ServerOP_CZMoveGuild 0x4515 -#define ServerOP_CZMoveInstancePlayer 0x4516 -#define ServerOP_CZMoveInstanceGroup 0x4517 -#define ServerOP_CZMoveInstanceRaid 0x4518 -#define ServerOP_CZMoveInstanceGuild 0x4519 -#define ServerOP_CZRemoveSpellPlayer 0x4520 -#define ServerOP_CZRemoveSpellGroup 0x4521 -#define ServerOP_CZRemoveSpellRaid 0x4522 -#define ServerOP_CZRemoveSpellGuild 0x4523 -#define ServerOP_CZSetEntityVariableByClientName 0x4524 -#define ServerOP_CZSetEntityVariableByNPCTypeID 0x4525 -#define ServerOP_CZSetEntityVariableByGroupID 0x4526 -#define ServerOP_CZSetEntityVariableByRaidID 0x4527 -#define ServerOP_CZSetEntityVariableByGuildID 0x4528 -#define ServerOP_CZSignalClient 0x4529 -#define ServerOP_CZSignalClientByName 0x4530 -#define ServerOP_CZSignalNPC 0x4531 -#define ServerOP_CZSignalGroup 0x4532 -#define ServerOP_CZSignalRaid 0x4533 -#define ServerOP_CZSignalGuild 0x4534 -#define ServerOP_CZTaskActivityResetPlayer 0x4535 -#define ServerOP_CZTaskActivityResetGroup 0x4536 -#define ServerOP_CZTaskActivityResetRaid 0x4537 -#define ServerOP_CZTaskActivityResetGuild 0x4538 -#define ServerOP_CZTaskActivityUpdatePlayer 0x4539 -#define ServerOP_CZTaskActivityUpdateGroup 0x4540 -#define ServerOP_CZTaskActivityUpdateRaid 0x4541 -#define ServerOP_CZTaskActivityUpdateGuild 0x4542 -#define ServerOP_CZTaskAssignPlayer 0x4543 -#define ServerOP_CZTaskAssignGroup 0x4544 -#define ServerOP_CZTaskAssignRaid 0x4545 -#define ServerOP_CZTaskAssignGuild 0x4546 -#define ServerOP_CZTaskDisablePlayer 0x4547 -#define ServerOP_CZTaskDisableGroup 0x4548 -#define ServerOP_CZTaskDisableRaid 0x4549 -#define ServerOP_CZTaskDisableGuild 0x4550 -#define ServerOP_CZTaskEnablePlayer 0x4551 -#define ServerOP_CZTaskEnableGroup 0x4552 -#define ServerOP_CZTaskEnableRaid 0x4553 -#define ServerOP_CZTaskEnableGuild 0x4554 -#define ServerOP_CZTaskFailPlayer 0x4555 -#define ServerOP_CZTaskFailGroup 0x4556 -#define ServerOP_CZTaskFailRaid 0x4557 -#define ServerOP_CZTaskFailGuild 0x4558 -#define ServerOP_CZTaskRemovePlayer 0x4559 -#define ServerOP_CZTaskRemoveGroup 0x4560 -#define ServerOP_CZTaskRemoveRaid 0x4561 -#define ServerOP_CZTaskRemoveGuild 0x4562 -#define ServerOP_CZClientMessageString 0x4563 -#define ServerOP_CZLDoNUpdate 0x4564 +#define ServerOP_CZLDoNUpdate 0x4500 +#define ServerOP_CZMarquee 0x4501 +#define ServerOP_CZMessage 0x4502 +#define ServerOP_CZMove 0x4503 +#define ServerOP_CZSetEntityVariable 0x4504 +#define ServerOP_CZSignal 0x4505 +#define ServerOP_CZSpell 0x4506 +#define ServerOP_CZTaskUpdate 0x4507 +#define ServerOP_CZClientMessageString 0x4508 -#define ServerOP_WWAssignTask 0x4750 -#define ServerOP_WWCastSpell 0x4751 -#define ServerOP_WWCompleteActivity 0x4752 -#define ServerOP_WWDisableTask 0x4753 -#define ServerOP_WWEnableTask 0x4754 -#define ServerOP_WWFailTask 0x4755 -#define ServerOP_WWMarquee 0x4756 -#define ServerOP_WWMessage 0x4757 -#define ServerOP_WWMove 0x4758 -#define ServerOP_WWMoveInstance 0x4759 -#define ServerOP_WWRemoveSpell 0x4760 -#define ServerOP_WWRemoveTask 0x4761 -#define ServerOP_WWResetActivity 0x4762 -#define ServerOP_WWSetEntityVariableClient 0x4763 -#define ServerOP_WWSetEntityVariableNPC 0x4764 -#define ServerOP_WWSignalClient 0x4765 -#define ServerOP_WWSignalNPC 0x4766 -#define ServerOP_WWUpdateActivity 0x4767 +#define ServerOP_WWLDoNUpdate 0x4750 +#define ServerOP_WWMarquee 0x4751 +#define ServerOP_WWMessage 0x4752 +#define ServerOP_WWMove 0x4753 +#define ServerOP_WWSetEntityVariable 0x4754 +#define ServerOP_WWSignal 0x4755 +#define ServerOP_WWSpell 0x4756 +#define ServerOP_WWTaskUpdate 0x4757 /** * QueryServer @@ -325,17 +259,75 @@ #define ServerOP_QSPlayerDropItem 0x5007 enum { - CZLDoNUpdateType_Character = 0, - CZLDoNUpdateType_Group, - CZLDoNUpdateType_Raid, - CZLDoNUpdateType_Guild, - CZLDoNUpdateType_Expedition + CZUpdateType_Character, + CZUpdateType_Group, + CZUpdateType_Raid, + CZUpdateType_Guild, + CZUpdateType_Expedition, + CZUpdateType_ClientName, + CZUpdateType_NPC }; enum { - CZLDoNUpdateSubtype_Win = 0, CZLDoNUpdateSubtype_Loss, - CZLDoNUpdateSubtype_Points + CZLDoNUpdateSubtype_Points, + CZLDoNUpdateSubtype_Win +}; + +enum { + CZMoveUpdateSubtype_MoveZone, + CZMoveUpdateSubtype_MoveZoneInstance +}; + +enum { + CZSpellUpdateSubtype_Cast, + CZSpellUpdateSubtype_Remove +}; + +enum { + CZTaskUpdateSubtype_ActivityReset, + CZTaskUpdateSubtype_ActivityUpdate, + CZTaskUpdateSubtype_AssignTask, + CZTaskUpdateSubtype_DisableTask, + CZTaskUpdateSubtype_EnableTask, + CZTaskUpdateSubtype_FailTask, + CZTaskUpdateSubtype_RemoveTask +}; + +enum { + WWLDoNUpdateType_Loss, + WWLDoNUpdateType_Points, + WWLDoNUpdateType_Win +}; + +enum { + WWMoveUpdateType_MoveZone, + WWMoveUpdateType_MoveZoneInstance +}; + +enum { + WWSetEntityVariableUpdateType_Character, + WWSetEntityVariableUpdateType_NPC +}; + +enum { + WWSignalUpdateType_Character, + WWSignalUpdateType_NPC +}; + +enum { + WWSpellUpdateType_Cast, + WWSpellUpdateType_Remove +}; + +enum { + WWTaskUpdateType_ActivityReset, + WWTaskUpdateType_ActivityUpdate, + WWTaskUpdateType_AssignTask, + WWTaskUpdateType_DisableTask, + WWTaskUpdateType_EnableTask, + WWTaskUpdateType_FailTask, + WWTaskUpdateType_RemoveTask }; /* Query Serv Generic Packet Flag/Type Enumeration */ @@ -1440,489 +1432,94 @@ struct QSGeneralQuery_Struct { char QueryString[0]; }; -struct CZCastSpellPlayer_Struct { - int character_id; - uint32 spell_id; -}; - -struct CZCastSpellGroup_Struct { - int group_id; - uint32 spell_id; -}; - -struct CZCastSpellRaid_Struct { - int raid_id; - uint32 spell_id; -}; - -struct CZCastSpellGuild_Struct { - int guild_id; - uint32 spell_id; -}; - -struct CZClientSignal_Struct { - int character_id; - uint32 signal; -}; - -struct CZGroupSignal_Struct { - int group_id; - uint32 signal; -}; - -struct CZRaidSignal_Struct { - int raid_id; - uint32 signal; -}; - -struct CZGuildSignal_Struct { - int guild_id; - uint32 signal; -}; - -struct CZNPCSignal_Struct { - uint32 npctype_id; - uint32 signal; -}; - struct CZClientMessageString_Struct { uint32 string_id; uint16 chat_type; - char character_name[64]; + char client_name[64]; uint32 args_size; char args[1]; // null delimited }; -struct CZClientSignalByName_Struct { - char character_name[64]; - uint32 signal; -}; - -struct CZCompleteActivityPlayer_Struct { - int character_id; - uint32 task_id; - int activity_id; -}; - -struct CZCompleteActivityGroup_Struct { - int group_id; - uint32 task_id; - int activity_id; -}; - -struct CZCompleteActivityRaid_Struct { - int raid_id; - uint32 task_id; - int activity_id; -}; - -struct CZCompleteActivityGuild_Struct { - int guild_id; - uint32 task_id; - int activity_id; -}; - -struct CZMovePlayer_Struct { - int character_id; - char zone_short_name[32]; -}; - -struct CZMarqueePlayer_Struct { - int character_id; - uint32 type; - uint32 priority; - uint32 fade_in; - uint32 fade_out; - uint32 duration; - char message[512]; -}; - -struct CZMarqueeGroup_Struct { - int group_id; - uint32 type; - uint32 priority; - uint32 fade_in; - uint32 fade_out; - uint32 duration; - char message[512]; -}; - -struct CZMarqueeRaid_Struct { - int raid_id; - uint32 type; - uint32 priority; - uint32 fade_in; - uint32 fade_out; - uint32 duration; - char message[512]; -}; - -struct CZMarqueeGuild_Struct { - int guild_id; - uint32 type; - uint32 priority; - uint32 fade_in; - uint32 fade_out; - uint32 duration; - char message[512]; -}; - -struct CZMessagePlayer_Struct { - uint32 type; - char character_name[64]; - char message[512]; -}; - -struct CZMessageGroup_Struct { - uint32 type; - int group_id; - char message[512]; -}; - -struct CZMessageRaid_Struct { - uint32 type; - int raid_id; - char message[512]; -}; - -struct CZMessageGuild_Struct { - uint32 type; - int guild_id; - char message[512]; -}; - -struct CZMoveGroup_Struct { - int group_id; - char zone_short_name[32]; -}; - -struct CZMoveRaid_Struct { - int raid_id; - char zone_short_name[32]; -}; - -struct CZMoveGuild_Struct { - int guild_id; - char zone_short_name[32]; -}; - -struct CZMoveInstancePlayer_Struct { - int character_id; - uint16 instance_id; -}; - -struct CZMoveInstanceGroup_Struct { - int group_id; - uint16 instance_id; -}; - -struct CZMoveInstanceRaid_Struct { - int raid_id; - uint16 instance_id; -}; - -struct CZMoveInstanceGuild_Struct { - int guild_id; - uint16 instance_id; -}; - -struct CZRemoveSpellPlayer_Struct { - int character_id; - uint32 spell_id; -}; - -struct CZRemoveSpellGroup_Struct { - int group_id; - uint32 spell_id; -}; - -struct CZRemoveSpellRaid_Struct { - int raid_id; - uint32 spell_id; -}; - -struct CZRemoveSpellGuild_Struct { - int guild_id; - uint32 spell_id; -}; - -struct CZRemoveTaskPlayer_Struct { - int character_id; - uint32 task_id; -}; - -struct CZRemoveTaskGroup_Struct { - int group_id; - uint32 task_id; -}; - -struct CZRemoveTaskRaid_Struct { - int raid_id; - uint32 task_id; -}; - -struct CZRemoveTaskGuild_Struct { - int guild_id; - uint32 task_id; -}; - -struct CZResetActivityPlayer_Struct { - int character_id; - uint32 task_id; - int activity_id; -}; - -struct CZResetActivityGroup_Struct { - int group_id; - uint32 task_id; - int activity_id; -}; - -struct CZResetActivityRaid_Struct { - int raid_id; - uint32 task_id; - int activity_id; -}; - -struct CZResetActivityGuild_Struct { - int guild_id; - uint32 task_id; - int activity_id; -}; - -struct CZSetEntVarByNPCTypeID_Struct { - uint32 npctype_id; - char variable_name[256]; - char variable_value[256]; -}; - -struct CZSetEntVarByClientName_Struct { - char character_name[64]; - char variable_name[256]; - char variable_value[256]; -}; - -struct CZSetEntVarByGroupID_Struct { - int group_id; - char variable_name[256]; - char variable_value[256]; -}; - -struct CZSetEntVarByRaidID_Struct { - int raid_id; - char variable_name[256]; - char variable_value[256]; -}; - -struct CZSetEntVarByGuildID_Struct { - int guild_id; - char variable_name[256]; - char variable_value[256]; -}; - -struct CZTaskActivityResetPlayer_Struct { - int character_id; - uint32 task_id; - int activity_id; -}; - -struct CZTaskActivityResetGroup_Struct { - int group_id; - uint32 task_id; - int activity_id; -}; - -struct CZTaskActivityResetRaid_Struct { - int raid_id; - uint32 task_id; - int activity_id; -}; - -struct CZTaskActivityResetGuild_Struct { - int guild_id; - uint32 task_id; - int activity_id; -}; - -struct CZTaskActivityUpdatePlayer_Struct { - int character_id; - uint32 task_id; - int activity_id; - int activity_count; -}; - -struct CZTaskActivityUpdateGroup_Struct { - int group_id; - uint32 task_id; - int activity_id; - int activity_count; -}; - -struct CZTaskActivityUpdateRaid_Struct { - int raid_id; - uint32 task_id; - int activity_id; - int activity_count; -}; - -struct CZTaskActivityUpdateGuild_Struct { - int guild_id; - uint32 task_id; - int activity_id; - int activity_count; -}; - -struct CZTaskAssignPlayer_Struct { - uint16 npc_entity_id; - int character_id; - uint32 task_id; - bool enforce_level_requirement; -}; - -struct CZTaskAssignGroup_Struct { - uint16 npc_entity_id; - int group_id; - uint32 task_id; - bool enforce_level_requirement; -}; - -struct CZTaskAssignRaid_Struct { - uint16 npc_entity_id; - int raid_id; - uint32 task_id; - bool enforce_level_requirement; -}; - -struct CZTaskAssignGuild_Struct { - uint16 npc_entity_id; - int guild_id; - uint32 task_id; - bool enforce_level_requirement; -}; - -struct CZTaskDisablePlayer_Struct { - int character_id; - uint32 task_id; -}; - -struct CZTaskDisableGroup_Struct { - int group_id; - uint32 task_id; -}; - -struct CZTaskDisableRaid_Struct { - int raid_id; - uint32 task_id; -}; - -struct CZTaskDisableGuild_Struct { - int guild_id; - uint32 task_id; -}; - -struct CZTaskEnablePlayer_Struct { - int character_id; - uint32 task_id; -}; - -struct CZTaskEnableGroup_Struct { - int group_id; - uint32 task_id; -}; - -struct CZTaskEnableRaid_Struct { - int raid_id; - uint32 task_id; -}; - -struct CZTaskEnableGuild_Struct { - int guild_id; - uint32 task_id; -}; - -struct CZTaskFailPlayer_Struct { - int character_id; - uint32 task_id; -}; - -struct CZTaskFailGroup_Struct { - int group_id; - uint32 task_id; -}; - -struct CZTaskFailRaid_Struct { - int raid_id; - uint32 task_id; -}; - -struct CZTaskFailGuild_Struct { - int guild_id; - uint32 task_id; -}; - -struct CZTaskRemovePlayer_Struct { - uint16 npc_entity_id; - int character_id; - uint32 task_id; -}; - -struct CZTaskRemoveGroup_Struct { - uint16 npc_entity_id; - int group_id; - uint32 task_id; -}; - -struct CZTaskRemoveRaid_Struct { - uint16 npc_entity_id; - int raid_id; - uint32 task_id; -}; - -struct CZTaskRemoveGuild_Struct { - uint16 npc_entity_id; - int guild_id; - uint32 task_id; -}; - struct CZLDoNUpdate_Struct { - uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition - uint8 update_subtype; // 0 - Win, 1 - Loss, 2 - Points - int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, or Expedition ID based on update type + uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name + uint8 update_subtype; // 0 - Loss, 1 - Points, 2 - Win + int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, or Expedition ID based on update type, 0 for Character Name uint32 theme_id; - int points; // Always 1, except for when Points are used + int points; // Only used in Points Subtype, else 1 + char client_name[64]; // Only used by Character Name Type, else empty }; -struct WWAssignTask_Struct { - uint16 npc_entity_id; - uint32 task_id; - bool enforce_level_requirement; - uint8 min_status; - uint8 max_status; +struct CZMarquee_Struct { + uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name + int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, or Expedition ID based on update type, 0 for Character Name + uint32 type; + uint32 priority; + uint32 fade_in; + uint32 fade_out; + uint32 duration; + char message[512]; + char client_name[64]; // Only used by Character Name Type, else empty }; -struct WWCastSpell_Struct { +struct CZMessage_Struct { + uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name + int update_identifier; // Group ID, Raid ID, Guild ID, or Expedition ID based on update type, 0 for Character Name + uint32 type; + char message[512]; + char client_name[64]; // Only used by Character Name Type, else empty +}; + +struct CZMove_Struct { + uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name + uint8 update_subtype; // 0 - Move Zone, 1 - Move Zone Instance + int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, or Expedition ID based on update type, 0 for Character Name + uint16 instance_id; // Only used by Move Zone Instance, else 0 + char zone_short_name[32]; // Only by with Move Zone, else empty + char client_name[64]; // Only used by Character Name Type, else empty +}; + +struct CZSetEntityVariable_Struct { + uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name, 6 - NPC + int update_identifier; // Group ID, Raid ID, Guild ID, Expedition ID, or NPC ID based on update type, 0 for Character Name + char variable_name[256]; + char variable_value[256]; + char client_name[64]; // Only used by Character Type, else empty +}; + +struct CZSignal_Struct { + uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name, 6 - NPC + int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, Expedition ID, or NPC ID based on update type, 0 for Character Name + uint32 signal; + char client_name[64]; // Only used by Character Name Type, else empty +}; + +struct CZSpell_Struct { + uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name + uint8 update_subtype; // 0 - Cast Spell, 1 - Remove Spell + int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, or Expedition ID based on update type, 0 for Character Name uint32 spell_id; + char client_name[64]; // Only used by Character Name Type, else empty +}; + +struct CZTaskUpdate_Struct { + uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name + uint8 update_subtype; // 0 - Activity Reset, 1 - Activity Update, 2 - Assign Task, 3 - Disable Task, 4 - Enable Task, 5 - Fail Task, 6 - Remove Task + int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, or Expedition ID based on update type, 0 for Character Name + uint32 task_identifier; + int task_subidentifier; // Activity ID for Activity Reset and Activity Update, NPC Entity ID for Assign Task, else -1 + int update_count; // Only used by Activity Update, else 1 + bool enforce_level_requirement; // Only used by Assign Task + char client_name[64]; // Only used by Character Name Type, else empty +}; + +struct WWLDoNUpdate_Struct { + uint8 update_type; // 0 - Loss, 1 - Points, 2 - Win + uint32 theme_id; + int points; // Only used in Points Subtype, else 1 uint8 min_status; uint8 max_status; }; -struct WWDisableTask_Struct { - uint32 task_id; - uint8 min_status; - uint8 max_status; -}; - -struct WWEnableTask_Struct { - uint32 task_id; - uint8 min_status; - uint8 max_status; -}; - -struct WWFailTask_Struct { - uint32 task_id; - uint8 min_status; - uint8 max_status; -}; struct WWMarquee_Struct { uint32 type; uint32 priority; @@ -1942,63 +1539,41 @@ struct WWMessage_Struct { }; struct WWMove_Struct { - char zone_short_name[32]; + uint8 update_type; // 0 - Move Zone, 1 - Move Zone Instance + char zone_short_name[32]; // Used with Move Zone + uint16 instance_id; // Used with Move Zone Instance uint8 min_status; uint8 max_status; }; -struct WWMoveInstance_Struct { - uint16 instance_id; +struct WWSetEntityVariable_Struct { + uint8 update_type; // 0 - Character, 1 - NPC + char variable_name[256]; + char variable_value[256]; uint8 min_status; uint8 max_status; }; -struct WWRemoveSpell_Struct { +struct WWSignal_Struct { + uint8 update_type; // 0 - Character, 1 - NPC + uint32 signal; + uint8 min_status; + uint8 max_status; +}; + +struct WWSpell_Struct { + uint8 update_type; // 0 - Cast Spell, 1 - Remove Spell uint32 spell_id; uint8 min_status; uint8 max_status; }; -struct WWRemoveTask_Struct { - uint32 task_id; - uint8 min_status; - uint8 max_status; - -}; - -struct WWResetActivity_Struct { - uint32 task_id; - int activity_id; - uint8 min_status; - uint8 max_status; -}; - -struct WWSetEntVarClient_Struct { - char variable_name[256]; - char variable_value[256]; - uint8 min_status; - uint8 max_status; -}; - -struct WWSetEntVarNPC_Struct { - char variable_name[256]; - char variable_value[256]; -}; - -struct WWSignalClient_Struct { - uint32 signal; - uint8 min_status; - uint8 max_status; -}; - -struct WWSignalNPC_Struct { - uint32 signal; -}; - -struct WWUpdateActivity_Struct { - uint32 task_id; - int activity_id; - int activity_count; +struct WWTaskUpdate_Struct { + uint8 update_type; // 0 - Activity Reset, 1 - Activity Update, 2 - Assign Task, 3 - Disable Task, 4 - Enable Task, 5 - Fail Task, 6 - Remove Task + uint32 task_identifier; + int task_subidentifier; // Activity ID for Activity Reset and Activity Update, NPC Entity ID for Assign Task, else -1 + int update_count; // Update Count for Activity Update, else 1 + bool enforce_level_requirement; // Only used by Assign Task, else false uint8 min_status; uint8 max_status; }; diff --git a/world/clientlist.cpp b/world/clientlist.cpp index ac3c2c82f..7a7dfe408 100644 --- a/world/clientlist.cpp +++ b/world/clientlist.cpp @@ -1589,12 +1589,14 @@ void ClientList::SendCharacterMessage(ClientListEntry* character, int chat_type, return; } - uint32_t pack_size = sizeof(CZMessagePlayer_Struct); - auto pack = std::make_unique(ServerOP_CZMessagePlayer, pack_size); - auto buf = reinterpret_cast(pack->pBuffer); + uint32_t pack_size = sizeof(CZMessage_Struct); + auto pack = std::make_unique(ServerOP_CZMessage, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + buf->update_type = CZUpdateType_ClientName; + buf->update_identifier = 0; buf->type = chat_type; - strn0cpy(buf->character_name, character->name(), sizeof(buf->character_name)); strn0cpy(buf->message, message.c_str(), sizeof(buf->message)); + strn0cpy(buf->client_name, character->name(), sizeof(buf->client_name)); character->Server()->SendPacket(pack.get()); } @@ -1633,7 +1635,7 @@ void ClientList::SendCharacterMessageID(ClientListEntry* character, auto buf = reinterpret_cast(pack->pBuffer); buf->string_id = eqstr_id; buf->chat_type = chat_type; - strn0cpy(buf->character_name, character->name(), sizeof(buf->character_name)); + strn0cpy(buf->client_name, character->name(), sizeof(buf->client_name)); buf->args_size = args_size; memcpy(buf->args, serialized_args.buffer(), serialized_args.size()); diff --git a/world/console.cpp b/world/console.cpp index 6a4ffeff4..e25be5050 100644 --- a/world/console.cpp +++ b/world/console.cpp @@ -825,14 +825,15 @@ void ConsoleSignalCharByName( } connection->SendLine(StringFormat("Signal Sent to %s with ID %i", (char *) args[0].c_str(), atoi(args[1].c_str()))); - uint32 message_len = strlen((char *) args[0].c_str()) + 1; - auto pack = new ServerPacket( - ServerOP_CZSignalClientByName, - sizeof(CZClientSignalByName_Struct) + message_len - ); - CZClientSignalByName_Struct *CZSC = (CZClientSignalByName_Struct *) pack->pBuffer; - strn0cpy(CZSC->character_name, (char *) args[0].c_str(), 64); - CZSC->signal = atoi(args[1].c_str()); + uint32 message_len = strlen((char *) args[0].c_str()) + 1; + auto pack = new ServerPacket(ServerOP_CZSignal, sizeof(CZSignal_Struct) + message_len); + CZSignal_Struct* CZS = (CZSignal_Struct*) pack->pBuffer; + uint8 update_type = CZUpdateType_ClientName; + int update_identifier = 0; + CZS->update_type = update_type; + CZS->update_identifier = update_identifier; + CZS->signal = atoi(args[1].c_str()); + strn0cpy(CZS->client_name, (char *) args[0].c_str(), 64); zoneserver_list.SendPacket(pack); safe_delete(pack); } diff --git a/world/shared_task_manager.cpp b/world/shared_task_manager.cpp index d28559f90..541f35712 100644 --- a/world/shared_task_manager.cpp +++ b/world/shared_task_manager.cpp @@ -1311,7 +1311,7 @@ void SharedTaskManager::SendMembersMessageID( for (const auto &member : shared_task->GetMembers()) { auto character = client_list.FindCLEByCharacterID(member.character_id); if (character && character->Server()) { - strn0cpy(buf->character_name, character->name(), sizeof(buf->character_name)); + strn0cpy(buf->client_name, character->name(), sizeof(buf->client_name)); character->Server()->SendPacket(pack.get()); } } diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index d6f4c1888..ee0c9e8da 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -1239,88 +1239,23 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { { QSLink.SendPacket(pack); break; - } - case ServerOP_CZCastSpellPlayer: - case ServerOP_CZCastSpellGroup: - case ServerOP_CZCastSpellRaid: - case ServerOP_CZCastSpellGuild: - case ServerOP_CZMarqueePlayer: - case ServerOP_CZMarqueeGroup: - case ServerOP_CZMarqueeRaid: - case ServerOP_CZMarqueeGuild: - case ServerOP_CZMessagePlayer: - case ServerOP_CZMessageGroup: - case ServerOP_CZMessageRaid: - case ServerOP_CZMessageGuild: - case ServerOP_CZMovePlayer: - case ServerOP_CZMoveGroup: - case ServerOP_CZMoveRaid: - case ServerOP_CZMoveGuild: - case ServerOP_CZMoveInstancePlayer: - case ServerOP_CZMoveInstanceGroup: - case ServerOP_CZMoveInstanceRaid: - case ServerOP_CZMoveInstanceGuild: - case ServerOP_CZRemoveSpellPlayer: - case ServerOP_CZRemoveSpellGroup: - case ServerOP_CZRemoveSpellRaid: - case ServerOP_CZRemoveSpellGuild: - case ServerOP_CZSetEntityVariableByClientName: - case ServerOP_CZSetEntityVariableByNPCTypeID: - case ServerOP_CZSetEntityVariableByGroupID: - case ServerOP_CZSetEntityVariableByRaidID: - case ServerOP_CZSetEntityVariableByGuildID: - case ServerOP_CZSignalNPC: - case ServerOP_CZSignalClient: - case ServerOP_CZSignalClientByName: - case ServerOP_CZSignalGroup: - case ServerOP_CZSignalRaid: - case ServerOP_CZSignalGuild: - case ServerOP_CZTaskActivityResetPlayer: - case ServerOP_CZTaskActivityResetGroup: - case ServerOP_CZTaskActivityResetRaid: - case ServerOP_CZTaskActivityResetGuild: - case ServerOP_CZTaskActivityUpdatePlayer: - case ServerOP_CZTaskActivityUpdateGroup: - case ServerOP_CZTaskActivityUpdateRaid: - case ServerOP_CZTaskActivityUpdateGuild: - case ServerOP_CZTaskAssignPlayer: - case ServerOP_CZTaskAssignGroup: - case ServerOP_CZTaskAssignRaid: - case ServerOP_CZTaskAssignGuild: - case ServerOP_CZTaskDisablePlayer: - case ServerOP_CZTaskDisableGroup: - case ServerOP_CZTaskDisableRaid: - case ServerOP_CZTaskDisableGuild: - case ServerOP_CZTaskEnablePlayer: - case ServerOP_CZTaskEnableGroup: - case ServerOP_CZTaskEnableRaid: - case ServerOP_CZTaskEnableGuild: - case ServerOP_CZTaskFailPlayer: - case ServerOP_CZTaskFailGroup: - case ServerOP_CZTaskFailRaid: - case ServerOP_CZTaskFailGuild: - case ServerOP_CZTaskRemovePlayer: - case ServerOP_CZTaskRemoveGroup: - case ServerOP_CZTaskRemoveRaid: - case ServerOP_CZTaskRemoveGuild: + } case ServerOP_CZLDoNUpdate: - case ServerOP_WWAssignTask: - case ServerOP_WWCastSpell: - case ServerOP_WWDisableTask: - case ServerOP_WWEnableTask: - case ServerOP_WWFailTask: + case ServerOP_CZMarquee: + case ServerOP_CZMessage: + case ServerOP_CZMove: + case ServerOP_CZSetEntityVariable: + case ServerOP_CZSignal: + case ServerOP_CZSpell: + case ServerOP_CZTaskUpdate: + case ServerOP_WWLDoNUpdate: case ServerOP_WWMarquee: case ServerOP_WWMessage: case ServerOP_WWMove: - case ServerOP_WWMoveInstance: - case ServerOP_WWRemoveSpell: - case ServerOP_WWRemoveTask: - case ServerOP_WWResetActivity: - case ServerOP_WWSetEntityVariableClient: - case ServerOP_WWSetEntityVariableNPC: - case ServerOP_WWSignalClient: - case ServerOP_WWSignalNPC: - case ServerOP_WWUpdateActivity: + case ServerOP_WWSetEntityVariable: + case ServerOP_WWSignal: + case ServerOP_WWSpell: + case ServerOP_WWTaskUpdate: case ServerOP_DepopAllPlayersCorpses: case ServerOP_DepopPlayerCorpse: case ServerOP_ReloadTitles: @@ -1362,7 +1297,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { case ServerOP_CZClientMessageString: { auto buf = reinterpret_cast(pack->pBuffer); - client_list.SendPacket(buf->character_name, pack); + client_list.SendPacket(buf->client_name, pack); break; } case ServerOP_ExpeditionLockout: diff --git a/zone/client.cpp b/zone/client.cpp index 2ad737405..34a9a31de 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -9481,12 +9481,16 @@ void Client::SendCrossZoneMessage( } else if (!character_name.empty() && !message.empty()) { - uint32_t pack_size = sizeof(CZMessagePlayer_Struct); - auto pack = std::make_unique(ServerOP_CZMessagePlayer, pack_size); - auto buf = reinterpret_cast(pack->pBuffer); + uint32_t pack_size = sizeof(CZMessage_Struct); + auto pack = std::make_unique(ServerOP_CZMessage, pack_size); + auto buf = reinterpret_cast(pack->pBuffer); + uint8 update_type = CZUpdateType_Character; + int update_identifier = 0; + buf->update_type = update_type; + buf->update_identifier = update_identifier; buf->type = chat_type; - strn0cpy(buf->character_name, character_name.c_str(), sizeof(buf->character_name)); strn0cpy(buf->message, message.c_str(), sizeof(buf->message)); + strn0cpy(buf->client_name, character_name.c_str(), sizeof(buf->client_name)); worldserver.SendPacket(pack.get()); } @@ -9519,7 +9523,7 @@ void Client::SendCrossZoneMessageString( auto buf = reinterpret_cast(pack->pBuffer); buf->string_id = string_id; buf->chat_type = chat_type; - strn0cpy(buf->character_name, character_name.c_str(), sizeof(buf->character_name)); + strn0cpy(buf->client_name, character_name.c_str(), sizeof(buf->client_name)); buf->args_size = args_size; memcpy(buf->args, argument_buffer.buffer(), argument_buffer.size()); diff --git a/zone/command.cpp b/zone/command.cpp index 7fc61208b..1f7ec6b82 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -758,8 +758,9 @@ void command_worldwide(Client *c, const Seperator *sep) if (sub_command == "cast") { if (sep->arg[2][0] && Seperator::IsNumber(sep->arg[2])) { + uint8 update_type = WWSpellUpdateType_Cast; int spell_id = atoi(sep->arg[2]); - quest_manager.WorldWideCastSpell(spell_id, 0, 0); + quest_manager.WorldWideSpell(update_type, spell_id); worldserver.SendEmoteMessage(0, 0, 15, fmt::format(" A GM has cast [{}] world-wide!", GetSpellName(spell_id)).c_str()); } else { diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index a8c14d669..6796fc0a6 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -3861,1363 +3861,6 @@ XS(XS__GetTimeSeconds) { XSRETURN_UV(seconds); } -XS(XS__crosszoneassigntaskbycharid); -XS(XS__crosszoneassigntaskbycharid) { - dXSARGS; - if (items < 2 || items > 3) - Perl_croak(aTHX_ "Usage: quest::crosszoneassigntaskbycharid(int character_id, uint32 task_id, [bool enforce_level_requirement = false])"); - { - int character_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvIV(ST(1)); - bool enforce_level_requirement = false; - - if (items == 3) { - enforce_level_requirement = (bool) SvTRUE(ST(2)); - } - quest_manager.CrossZoneAssignTaskByCharID(character_id, task_id, enforce_level_requirement); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszoneassigntaskbygroupid); -XS(XS__crosszoneassigntaskbygroupid) { - dXSARGS; - if (items < 2 || items > 3) - Perl_croak(aTHX_ "Usage: quest::crosszoneassigntaskbygroupid(int group_id, uint32 task_id, [bool enforce_level_requirement = false])"); - { - int group_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvIV(ST(1)); - bool enforce_level_requirement = false; - - if (items == 3) { - enforce_level_requirement = (bool) SvTRUE(ST(2)); - } - quest_manager.CrossZoneAssignTaskByGroupID(group_id, task_id, enforce_level_requirement); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszoneassigntaskbyraidid); -XS(XS__crosszoneassigntaskbyraidid) { - dXSARGS; - if (items < 2 || items > 3) - Perl_croak(aTHX_ "Usage: quest::crosszoneassigntaskbyraidid(int raid_id, uint32 task_id, [bool enforce_level_requirement = false])");\ - { - int raid_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvIV(ST(1)); - bool enforce_level_requirement = false; - - if (items == 3) { - enforce_level_requirement = (bool) SvTRUE(ST(2)); - } - quest_manager.CrossZoneAssignTaskByRaidID(raid_id, task_id, enforce_level_requirement); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszoneassigntaskbyguildid); -XS(XS__crosszoneassigntaskbyguildid) { - dXSARGS; - if (items < 2 || items > 3) - Perl_croak(aTHX_ "Usage: quest::crosszoneassigntaskbyguildid(int guild_id, uint32 task_id, [bool enforce_level_requirement = false])"); - { - int guild_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvIV(ST(1)); - bool enforce_level_requirement = false; - - if (items == 3) { - enforce_level_requirement = (bool) SvTRUE(ST(2)); - } - quest_manager.CrossZoneAssignTaskByGuildID(guild_id, task_id, enforce_level_requirement); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszonecastspellbycharid); -XS(XS__crosszonecastspellbycharid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonecastspellbycharid(int character_id, uint32 spell_id)"); - { - int character_id = (int) SvIV(ST(0)); - uint32 spell_id = (uint32) SvIV(ST(1)); - quest_manager.CrossZoneCastSpellByCharID(character_id, spell_id); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszonecastspellbygroupid); -XS(XS__crosszonecastspellbygroupid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonecastspellbygroupid(int group_id, uint32 spell_id)"); - { - int group_id = (int) SvIV(ST(0)); - uint32 spell_id = (uint32) SvIV(ST(1)); - quest_manager.CrossZoneCastSpellByGroupID(group_id, spell_id); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszonecastspellbyraidid); -XS(XS__crosszonecastspellbyraidid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonecastspellbyraidid(int raid_id, uint32 spell_id)"); - { - int raid_id = (int) SvIV(ST(0)); - uint32 spell_id = (uint32) SvIV(ST(1)); - quest_manager.CrossZoneCastSpellByRaidID(raid_id, spell_id); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszonecastspellbyguildid); -XS(XS__crosszonecastspellbyguildid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonecastspellbyguildid(int guild_id, uint32 spell_id)"); - { - int guild_id = (int) SvIV(ST(0)); - uint32 spell_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneCastSpellByGuildID(guild_id, spell_id); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszonedisabletaskbycharid); -XS(XS__crosszonedisabletaskbycharid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonedisabletaskbycharid(int character_id, uint32 task_id)"); - { - int char_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneDisableTaskByCharID(char_id, task_id); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszonedisabletaskbygroupid); -XS(XS__crosszonedisabletaskbygroupid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonedisabletaskbygroupid(int group_id, uint32 task_id)"); - { - int group_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneDisableTaskByGroupID(group_id, task_id); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszonedisabletaskbyraidid); -XS(XS__crosszonedisabletaskbyraidid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonedisabletaskbyraidid(int raid_id, uint32 task_id)"); - { - int raid_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneDisableTaskByRaidID(raid_id, task_id); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszonedisabletaskbyguildid); -XS(XS__crosszonedisabletaskbyguildid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonedisabletaskbyguildid(int guild_id, uint32 task_id)"); - { - int guild_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneDisableTaskByGuildID(guild_id, task_id); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszoneenabletaskbycharid); -XS(XS__crosszoneenabletaskbycharid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszoneenabletaskbycharid(int character_id, uint32 task_id)"); - { - int char_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneEnableTaskByCharID(char_id, task_id); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszoneenabletaskbygroupid); -XS(XS__crosszoneenabletaskbygroupid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszoneenabletaskbygroupid(int group_id, uint32 task_id)"); - { - int group_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneEnableTaskByGroupID(group_id, task_id); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszoneenabletaskbyraidid); -XS(XS__crosszoneenabletaskbyraidid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszoneenabletaskbyraidid(int raid_id, uint32 task_id)"); - { - int raid_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneEnableTaskByRaidID(raid_id, task_id); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszoneenabletaskbyguildid); -XS(XS__crosszoneenabletaskbyguildid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszoneenabletaskbyguildid(int guild_id, uint32 task_id)"); - { - int guild_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneEnableTaskByGuildID(guild_id, task_id); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszonefailtaskbycharid); -XS(XS__crosszonefailtaskbycharid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonefailtaskbycharid(int character_id, uint32 task_id)"); - { - int char_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneFailTaskByCharID(char_id, task_id); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszonefailtaskbygroupid); -XS(XS__crosszonefailtaskbygroupid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonefailtaskbygroupid(int group_id, uint32 task_id)"); - { - int group_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneFailTaskByGroupID(group_id, task_id); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszonefailtaskbyraidid); -XS(XS__crosszonefailtaskbyraidid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonefailtaskbyraidid(int raid_id, uint32 task_id)"); - { - int raid_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneFailTaskByRaidID(raid_id, task_id); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszonefailtaskbyguildid); -XS(XS__crosszonefailtaskbyguildid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonefailtaskbyguildid(int guild_id, uint32 task_id)"); - { - int guild_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneFailTaskByGuildID(guild_id, task_id); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszonemarqueebycharid); -XS(XS__crosszonemarqueebycharid) { - dXSARGS; - - if (items != 7) - Perl_croak(aTHX_ "Usage: quest::crosszonemarqueebycharid(int character_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message)"); - - if (items == 7) { - int character_id = (int) SvIV(ST(0)); - int type = (int) SvIV(ST(1)); - int priority = (int) SvIV(ST(2)); - int fade_in = (int) SvIV(ST(3)); - int fade_out = (int) SvIV(ST(4)); - int duration = (int) SvIV(ST(5)); - char *message = (char *) SvPV_nolen(ST(6)); - quest_manager.CrossZoneMarqueeByCharID(character_id, type, priority, fade_in, fade_out, duration, message); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszonemarqueebygroupid); -XS(XS__crosszonemarqueebygroupid) { - dXSARGS; - - if (items != 7) - Perl_croak(aTHX_ "Usage: quest::crosszonemarqueebygroupid(int group_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message)"); - - if (items == 7) { - int group_id = (int) SvIV(ST(0)); - int type = (int) SvIV(ST(1)); - int priority = (int) SvIV(ST(2)); - int fade_in = (int) SvIV(ST(3)); - int fade_out = (int) SvIV(ST(4)); - int duration = (int) SvIV(ST(5)); - char *message = (char *) SvPV_nolen(ST(6)); - quest_manager.CrossZoneMarqueeByGroupID(group_id, type, priority, fade_in, fade_out, duration, message); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszonemarqueebyraidid); -XS(XS__crosszonemarqueebyraidid) { - dXSARGS; - - if (items != 7) - Perl_croak(aTHX_ "Usage: quest::crosszonemarqueebyraidid(int raid_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message)"); - - if (items == 7) { - int raid_id = (int) SvIV(ST(0)); - int type = (int) SvIV(ST(1)); - int priority = (int) SvIV(ST(2)); - int fade_in = (int) SvIV(ST(3)); - int fade_out = (int) SvIV(ST(4)); - int duration = (int) SvIV(ST(5)); - char *message = (char *) SvPV_nolen(ST(6)); - quest_manager.CrossZoneMarqueeByRaidID(raid_id, type, priority, fade_in, fade_out, duration, message); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszonemarqueebyguildid); -XS(XS__crosszonemarqueebyguildid) { - dXSARGS; - - if (items != 7) - Perl_croak(aTHX_ "Usage: quest::crosszonemarqueebyguildid(int guild_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message)"); - - if (items == 7) { - int guild_id = (int) SvIV(ST(0)); - int type = (int) SvIV(ST(1)); - int priority = (int) SvIV(ST(2)); - int fade_in = (int) SvIV(ST(3)); - int fade_out = (int) SvIV(ST(4)); - int duration = (int) SvIV(ST(5)); - char *message = (char *) SvPV_nolen(ST(6)); - quest_manager.CrossZoneMarqueeByGuildID(guild_id, type, priority, fade_in, fade_out, duration, message); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszonemessageplayerbyname); -XS(XS__crosszonemessageplayerbyname) { - dXSARGS; - - if (items != 3) - Perl_croak(aTHX_ "Usage: quest::crosszonemessageplayerbyname(uint32 type, string name, string message)"); - - if (items == 3) { - uint32 type = (uint32) SvUV(ST(0)); - char *name = (char *) SvPV_nolen(ST(1)); - char *message = (char *) SvPV_nolen(ST(2)); - quest_manager.CrossZoneMessagePlayerByName(type, name, message); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszonemessageplayerbygroupid); -XS(XS__crosszonemessageplayerbygroupid) { - dXSARGS; - - if (items != 3) - Perl_croak(aTHX_ "Usage: quest::crosszonemessageplayerbygroupid(uint32 type, int group_id, string message)"); - - if (items == 3) { - uint32 type = (uint32) SvUV(ST(0)); - int group_id = (int) SvIV(ST(1)); - char *message = (char *) SvPV_nolen(ST(2)); - quest_manager.CrossZoneMessagePlayerByGroupID(type, group_id, message); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszonemessageplayerbyraidid); -XS(XS__crosszonemessageplayerbyraidid) { - dXSARGS; - - if (items != 3) - Perl_croak(aTHX_ "Usage: quest::crosszonemessageplayerbyraidid(uint32 type, int raid_id, string message)"); - - if (items == 3) { - uint32 type = (uint32) SvUV(ST(0)); - int raid_id = (int) SvIV(ST(1)); - char *message = (char *) SvPV_nolen(ST(2)); - quest_manager.CrossZoneMessagePlayerByRaidID(type, raid_id, message); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszonemessageplayerbyguildid); -XS(XS__crosszonemessageplayerbyguildid) { - dXSARGS; - - if (items != 3) - Perl_croak(aTHX_ "Usage: quest::crosszonemessageplayerbyguildid(uint32 type, int guild_id, string message)"); - - if (items == 3) { - uint32 type = (uint32) SvUV(ST(0)); - int guild_id = (int) SvIV(ST(1)); - char *message = (char *) SvPV_nolen(ST(2)); - quest_manager.CrossZoneMessagePlayerByGuildID(type, guild_id, message); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszonemoveplayerbycharid); -XS(XS__crosszonemoveplayerbycharid) { - dXSARGS; - - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonemoveplayerbycharid(int character_id, string zone_short_name)"); - - if (items == 2) { - int character_id = (int) SvIV(ST(0)); - char *zone_short_name = (char *) SvPV_nolen(ST(1)); - quest_manager.CrossZoneMovePlayerByCharID(character_id, zone_short_name); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszonemoveplayerbygroupid); -XS(XS__crosszonemoveplayerbygroupid) { - dXSARGS; - - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonemoveplayerbygroupid(int group_id, string zone_short_name)"); - - if (items == 2) { - int group_id = (int) SvIV(ST(0)); - char *zone_short_name = (char *) SvPV_nolen(ST(1)); - quest_manager.CrossZoneMovePlayerByGroupID(group_id, zone_short_name); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszonemoveplayerbyraidid); -XS(XS__crosszonemoveplayerbyraidid) { - dXSARGS; - - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonemoveplayerbyraidid(int raid_id, string zone_short_name)"); - - if (items == 2) { - int raid_id = (int) SvIV(ST(0)); - char *zone_short_name = (char *) SvPV_nolen(ST(1)); - quest_manager.CrossZoneMovePlayerByRaidID(raid_id, zone_short_name); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszonemoveplayerbyguildid); -XS(XS__crosszonemoveplayerbyguildid) { - dXSARGS; - - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonemoveplayerbyguildid(int guild_id, string zone_short_name)"); - - if (items == 2) { - int guild_id = (int) SvIV(ST(0)); - char *zone_short_name = (char *) SvPV_nolen(ST(1)); - quest_manager.CrossZoneMovePlayerByGuildID(guild_id, zone_short_name); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszonemoveinstancebycharid); -XS(XS__crosszonemoveinstancebycharid) { - dXSARGS; - - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonemoveinstancebycharid(int character_id, uint16 instance_id)"); - - if (items == 2) { - int character_id = (int) SvIV(ST(0)); - uint16 instance_id = (uint16) SvUV(ST(1)); - quest_manager.CrossZoneMoveInstanceByCharID(character_id, instance_id); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszonemoveinstancebygroupid); -XS(XS__crosszonemoveinstancebygroupid) { - dXSARGS; - - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonemoveinstancebygroupid(int group_id, uint16 instance_id)"); - - if (items == 2) { - int group_id = (int) SvIV(ST(0)); - uint16 instance_id = (uint16) SvUV(ST(1)); - quest_manager.CrossZoneMoveInstanceByGroupID(group_id, instance_id); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszonemoveinstancebyraidid); -XS(XS__crosszonemoveinstancebyraidid) { - dXSARGS; - - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonemoveinstancebyraidid(int raid_id, uint16 instance_id)"); - - if (items == 2) { - int raid_id = (int) SvIV(ST(0)); - uint16 instance_id = (uint16) SvUV(ST(1)); - quest_manager.CrossZoneMoveInstanceByRaidID(raid_id, instance_id); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszonemoveinstancebyguildid); -XS(XS__crosszonemoveinstancebyguildid) { - dXSARGS; - - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonemoveinstancebyguildid(int guild_id, uint16 instance_id)"); - - if (items == 2) { - int guild_id = (int) SvIV(ST(0)); - uint16 instance_id = (uint16) SvUV(ST(1)); - quest_manager.CrossZoneMoveInstanceByGuildID(guild_id, instance_id); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszoneremovespellbycharid); -XS(XS__crosszoneremovespellbycharid) { - dXSARGS; - - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszoneremovespellbycharid(int character_id, uint32 spell_id)"); - - if (items == 2) { - int character_id = (int) SvIV(ST(0)); - uint32 spell_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneRemoveSpellByCharID(character_id, spell_id); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszoneremovespellbygroupid); -XS(XS__crosszoneremovespellbygroupid) { - dXSARGS; - - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszoneremovespellbygroupid(int group_id, uint32 spell_id)"); - - if (items == 2) { - int group_id = (int) SvIV(ST(0)); - uint32 spell_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneRemoveSpellByGroupID(group_id, spell_id); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszoneremovespellbyraidid); -XS(XS__crosszoneremovespellbyraidid) { - dXSARGS; - - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszoneremovespellbyraidid(int raid_id, uint32 spell_id)"); - - if (items == 2) { - int raid_id = (int) SvIV(ST(0)); - uint32 spell_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneRemoveSpellByRaidID(raid_id, spell_id); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszoneremovespellbyguildid); -XS(XS__crosszoneremovespellbyguildid) { - dXSARGS; - - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszoneremovespellbyguildid(int guild_id, uint32 spell_id)"); - - if (items == 2) { - int guild_id = (int) SvIV(ST(0)); - uint32 spell_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneRemoveSpellByGuildID(guild_id, spell_id); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszoneremovetaskbycharid); -XS(XS__crosszoneremovetaskbycharid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszoneremovetaskbycharid(int character_id, uint32 task_id)"); - { - int char_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneRemoveTaskByCharID(char_id, task_id); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszoneremovetaskbygroupid); -XS(XS__crosszoneremovetaskbygroupid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszoneremovetaskbygroupid(int group_id, uint32 task_id)"); - { - int group_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneRemoveTaskByGroupID(group_id, task_id); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszoneremovetaskbyraidid); -XS(XS__crosszoneremovetaskbyraidid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszoneremovetaskbyraidid(int raid_id, uint32 task_id)"); - { - int raid_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneRemoveTaskByRaidID(raid_id, task_id); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszoneremovetaskbyguildid); -XS(XS__crosszoneremovetaskbyguildid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszoneremovetaskbyguildid(int guild_id, uint32 task_id)"); - { - int guild_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneRemoveTaskByGuildID(guild_id, task_id); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszoneresetactivitybycharid); -XS(XS__crosszoneresetactivitybycharid) { - dXSARGS; - if (items != 3) - Perl_croak(aTHX_ "Usage: quest::crosszoneresetactivitybycharid(int char_id, uint32 task_id, int activity_id)"); - { - int char_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvUV(ST(1)); - int activity_id = (int) SvIV(ST(2)); - quest_manager.CrossZoneResetActivityByCharID(char_id, task_id, activity_id); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszoneresetactivitybygroupid); -XS(XS__crosszoneresetactivitybygroupid) { - dXSARGS; - if (items != 3) - Perl_croak(aTHX_ "Usage: quest::crosszoneresetactivitybygroupid(int group_id, uint32 task_id, int activity_id)"); - { - int group_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvUV(ST(1)); - int activity_id = (int) SvIV(ST(2)); - quest_manager.CrossZoneResetActivityByGroupID(group_id, task_id, activity_id); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszoneresetactivitybyraidid); -XS(XS__crosszoneresetactivitybyraidid) { - dXSARGS; - if (items != 3) - Perl_croak(aTHX_ "Usage: quest::crosszoneresetactivitybyraidid(int raid_id, uint32 task_id, int activity_id)"); - { - int raid_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvUV(ST(1)); - int activity_id = (int) SvIV(ST(2)); - quest_manager.CrossZoneResetActivityByRaidID(raid_id, task_id, activity_id); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszoneresetactivitybyguildid); -XS(XS__crosszoneresetactivitybyguildid) { - dXSARGS; - if (items != 3) - Perl_croak(aTHX_ "Usage: quest::crosszoneresetactivitybyguildid(int guild_id, uint32 task_id, int activity_id)"); - { - int guild_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvUV(ST(1)); - int activity_id = (int) SvIV(ST(2)); - quest_manager.CrossZoneResetActivityByGuildID(guild_id, task_id, activity_id); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszonesetentityvariablebynpctypeid); -XS(XS__crosszonesetentityvariablebynpctypeid) { - dXSARGS; - - if (items != 3) - Perl_croak(aTHX_ "Usage: quest::crosszonesetentityvariablebynpctypeid(int npc_type_id, string key, string value)"); - - if (items == 3) { - uint32 npc_type_id = (uint32) SvUV(ST(0)); - const char *key = (const char *) SvPV_nolen(ST(1)); - const char *str_value = (const char *) SvPV_nolen(ST(2)); - quest_manager.CrossZoneSetEntityVariableByNPCTypeID(npc_type_id, key, str_value); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszonesetentityvariablebyclientname); -XS(XS__crosszonesetentityvariablebyclientname) { - dXSARGS; - - if (items != 3) - Perl_croak(aTHX_ "Usage: quest::crosszonesetentityvariablebyclientname(string client_name, string key, string value)"); - - if (items == 3) { - const char *client_name = (const char *) SvPV_nolen(ST(0)); - const char *key = (const char *) SvPV_nolen(ST(1)); - const char *str_value = (const char *) SvPV_nolen(ST(2)); - quest_manager.CrossZoneSetEntityVariableByClientName(client_name, key, str_value); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszonesetentityvariablebygroupid); -XS(XS__crosszonesetentityvariablebygroupid) { - dXSARGS; - - if (items != 3) - Perl_croak(aTHX_ "Usage: quest::crosszonesetentityvariablebygroupid(int group_id, string key, string value)"); - - if (items == 3) { - int group_id = SvIV(ST(0)); - const char *key = (const char *) SvPV_nolen(ST(1)); - const char *str_value = (const char *) SvPV_nolen(ST(2)); - quest_manager.CrossZoneSetEntityVariableByGroupID(group_id, key, str_value); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszonesetentityvariablebyraidid); -XS(XS__crosszonesetentityvariablebyraidid) { - dXSARGS; - - if (items != 3) - Perl_croak(aTHX_ "Usage: quest::crosszonesetentityvariablebyraidid(int raid_id, string key, string value)"); - - if (items == 3) { - int raid_id = SvIV(ST(0)); - const char *key = (const char *) SvPV_nolen(ST(1)); - const char *str_value = (const char *) SvPV_nolen(ST(2)); - quest_manager.CrossZoneSetEntityVariableByRaidID(raid_id, key, str_value); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszonesetentityvariablebyguildid); -XS(XS__crosszonesetentityvariablebyguildid) { - dXSARGS; - - if (items != 3) - Perl_croak(aTHX_ "Usage: quest::crosszonesetentityvariablebyguildid(int guild_id, string key, string value)"); - - if (items == 3) { - int guild_id = SvIV(ST(0)); - const char *key = (const char *) SvPV_nolen(ST(1)); - const char *str_value = (const char *) SvPV_nolen(ST(2)); - quest_manager.CrossZoneSetEntityVariableByGuildID(guild_id, key, str_value); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszonesignalclientbycharid); -XS(XS__crosszonesignalclientbycharid) { - dXSARGS; - - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonesignalclientbycharid(int character_id, uint32 signal)"); - - if (items == 2) { - int char_id = (int) SvIV(ST(0)); - uint32 signal = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneSignalPlayerByCharID(char_id, signal); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszonesignalclientbygroupid); -XS(XS__crosszonesignalclientbygroupid) { - dXSARGS; - - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonesignalclientbygroupid(int group_id, uint32 signal)"); - - if (items == 2) { - int group_id = (int) SvIV(ST(0)); - uint32 signal = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneSignalPlayerByGroupID(group_id, signal); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszonesignalclientbyraidid); -XS(XS__crosszonesignalclientbyraidid) { - dXSARGS; - - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonesignalclientbyraidid(int raid_id, uint32 signal)"); - - if (items == 2) { - int raid_id = (int) SvIV(ST(0)); - uint32 signal = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneSignalPlayerByRaidID(raid_id, signal); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszonesignalclientbyguildid); -XS(XS__crosszonesignalclientbyguildid) { - dXSARGS; - - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonesignalclientbyguildid(int guild_id, uint32 signal)"); - - if (items == 2) { - int guild_id = (int) SvIV(ST(0)); - uint32 signal = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneSignalPlayerByGuildID(guild_id, signal); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszonesignalclientbyname); -XS(XS__crosszonesignalclientbyname) { - dXSARGS; - - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonesignalclientbyname(string name, uint32 signal)"); - - if (items == 2) { - char *name = (char *) SvPV_nolen(ST(0)); - uint32 signal = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneSignalPlayerByName(name, signal); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszonesignalnpcbynpctypeid); -XS(XS__crosszonesignalnpcbynpctypeid) { - dXSARGS; - - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszonesignalnpcbynpctypeid(uint32 npc_type_id, uint32 value)"); - - if (items == 2) { - uint32 npc_type_id = (uint32) SvUV(ST(0)); - uint32 int_value = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneSignalNPCByNPCTypeID(npc_type_id, int_value); - } - - XSRETURN_EMPTY; -} - -XS(XS__crosszoneupdateactivitybycharid); -XS(XS__crosszoneupdateactivitybycharid) { - dXSARGS; - if (items < 3 || items > 4) - Perl_croak(aTHX_ "Usage: quest::crosszoneupdateactivitybycharid(int char_id, uint32 task_id, int activity_id, [int activity_count = 1])"); - { - int char_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvUV(ST(1)); - int activity_id = (int) SvIV(ST(2)); - int activity_count = 1; - if (items == 4) { - activity_count = (int) SvIV(ST(3)); - } - quest_manager.CrossZoneUpdateActivityByCharID(char_id, task_id, activity_id, activity_count); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszoneupdateactivitybygroupid); -XS(XS__crosszoneupdateactivitybygroupid) { - dXSARGS; - if (items < 3 || items > 4) - Perl_croak(aTHX_ "Usage: quest::crosszoneupdateactivitybygroupid(int group_id, uint32 task_id, int activity_id, [int activity_count = 1])"); - { - int group_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvUV(ST(1)); - int activity_id = (int) SvIV(ST(2)); - int activity_count = 1; - if (items == 4) { - activity_count = (int) SvIV(ST(3)); - } - quest_manager.CrossZoneUpdateActivityByGroupID(group_id, task_id, activity_id, activity_count); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszoneupdateactivitybyraidid); -XS(XS__crosszoneupdateactivitybyraidid) { - dXSARGS; - if (items < 3 || items > 4) - Perl_croak(aTHX_ "Usage: quest::crosszoneupdateactivitybyraidid(int raid_id, uint32 task_id, int activity_id, [int activity_count = 1])"); - { - int raid_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvUV(ST(1)); - int activity_id = (int) SvIV(ST(2)); - int activity_count = 1; - if (items == 4) { - activity_count = (int) SvIV(ST(3)); - } - quest_manager.CrossZoneUpdateActivityByRaidID(raid_id, task_id, activity_id, activity_count); - } - XSRETURN_EMPTY; -} - -XS(XS__crosszoneupdateactivitybyguildid); -XS(XS__crosszoneupdateactivitybyguildid) { - dXSARGS; - if (items < 3 || items > 4) - Perl_croak(aTHX_ "Usage: quest::crosszoneupdateactivitybyguildid(int guild_id, uint32 task_id, int activity_id, [int activity_count = 1])"); - { - int guild_id = (int) SvIV(ST(0)); - uint32 task_id = (uint32) SvUV(ST(1)); - int activity_id = (int) SvIV(ST(2)); - int activity_count = 1; - if (items == 4) { - activity_count = (int) SvIV(ST(3)); - } - quest_manager.CrossZoneUpdateActivityByGuildID(guild_id, task_id, activity_id, activity_count); - } - XSRETURN_EMPTY; -} - -XS(XS__worldwideassigntask); -XS(XS__worldwideassigntask) { - dXSARGS; - if (items < 1 || items > 3) - Perl_croak(aTHX_ "Usage: quest::worldwideassigntask(uint32 task_id, [uint8 min_status = 0, uint8 max_status = 0])"); - { - uint32 task_id = (uint32) SvUV(ST(0)); - uint8 min_status = 0; - uint8 max_status = 0; - if (items == 2) { - min_status = (uint8) SvUV(ST(1)); - } - - if (items == 3) { - max_status = (uint8) SvUV(ST(2)); - } - quest_manager.WorldWideAssignTask(task_id, min_status, max_status); - } - - XSRETURN_EMPTY; -} - -XS(XS__worldwidecastspell); -XS(XS__worldwidecastspell) { - dXSARGS; - if (items < 1 || items > 3) - Perl_croak(aTHX_ "Usage: quest::worldwidecastspell(uint32 spell_id, [uint8 min_status = 0, uint8 max_status = 0])"); - { - uint32 spell_id = (uint32) SvUV(ST(0)); - uint8 min_status = 0; - uint8 max_status = 0; - if (items == 2) { - min_status = (uint8) SvUV(ST(1)); - } - - if (items == 3) { - max_status = (uint8) SvUV(ST(2)); - } - quest_manager.WorldWideCastSpell(spell_id, min_status, max_status); - } - - XSRETURN_EMPTY; -} - -XS(XS__worldwidedisabletask); -XS(XS__worldwidedisabletask) { - dXSARGS; - if (items < 1 || items > 3) - Perl_croak(aTHX_ "Usage: quest::worldwidedisabletask(uint32 task_id, [uint8 min_status = 0, uint8 max_status = 0])"); - { - uint32 task_id = (uint32) SvUV(ST(0)); - uint8 min_status = 0; - uint8 max_status = 0; - if (items == 2) { - min_status = (uint8) SvUV(ST(1)); - } - - if (items == 3) { - max_status = (uint8) SvUV(ST(2)); - } - quest_manager.WorldWideDisableTask(task_id, min_status, max_status); - } - - XSRETURN_EMPTY; -} - -XS(XS__worldwideenabletask); -XS(XS__worldwideenabletask) { - dXSARGS; - if (items < 1 || items > 3) - Perl_croak(aTHX_ "Usage: quest::worldwideenabletask(uint32 task_id, [uint8 min_status = 0, uint8 max_status = 0])"); - { - uint32 task_id = (uint32) SvUV(ST(0)); - uint8 min_status = 0; - uint8 max_status = 0; - if (items == 2) { - min_status = (uint8) SvUV(ST(1)); - } - - if (items == 3) { - max_status = (uint8) SvUV(ST(2)); - } - quest_manager.WorldWideEnableTask(task_id, min_status, max_status); - } - - XSRETURN_EMPTY; -} - -XS(XS__worldwidefailtask); -XS(XS__worldwidefailtask) { - dXSARGS; - if (items < 1 || items > 3) - Perl_croak(aTHX_ "Usage: quest::worldwidefailtask(uint32 task_id, [uint8 min_status = 0, uint8 max_status = 0])"); - { - uint32 task_id = (uint32) SvUV(ST(0)); - uint8 min_status = 0; - uint8 max_status = 0; - if (items == 2) { - min_status = (uint8) SvUV(ST(1)); - } - - if (items == 3) { - max_status = (uint8) SvUV(ST(2)); - } - quest_manager.WorldWideFailTask(task_id, min_status, max_status); - } - - XSRETURN_EMPTY; -} - -XS(XS__worldwidemarquee); -XS(XS__worldwidemarquee) { - dXSARGS; - if (items < 6 || items > 8) - Perl_croak(aTHX_ "Usage: quest::worldwidemarquee(uint32 color_id, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, string message, [uint8 min_status = 0, uint8 max_status = 0])"); - { - uint32 color_id = (uint32) SvUV(ST(0)); - uint32 priority = (uint32) SvUV(ST(1)); - uint32 fade_in = (uint32) SvUV(ST(2)); - uint32 fade_out = (uint32) SvUV(ST(3)); - uint32 duration = (uint32) SvUV(ST(4)); - char *message = (char *) SvPV_nolen(ST(5)); - uint8 min_status = 0; - uint8 max_status = 0; - if (items == 7) { - min_status = (uint8) SvUV(ST(6)); - } - - if (items == 8) { - max_status = (uint8) SvUV(ST(7)); - } - quest_manager.WorldWideMarquee(color_id, priority, fade_in, fade_out, duration, message, min_status, max_status); - } - - XSRETURN_EMPTY; -} - -XS(XS__worldwidemessage); -XS(XS__worldwidemessage) { - dXSARGS; - if (items < 2 || items > 4) - Perl_croak(aTHX_ "Usage: quest::worldwidemessage(uint32 type, string message, [uint8 min_status = 0, uint8 max_status = 0])"); - { - uint32 type = (uint32)SvUV(ST(0)); - const char *message = (const char*) SvPV_nolen(ST(1)); - uint8 min_status = 0; - uint8 max_status = 0; - if (items == 3) { - min_status = (uint8) SvUV(ST(2)); - } - - if (items == 4) { - max_status = (uint8) SvUV(ST(3)); - } - quest_manager.WorldWideMessage(type, message, min_status, max_status); - } - - XSRETURN_EMPTY; -} - -XS(XS__worldwidemove); -XS(XS__worldwidemove) { - dXSARGS; - if (items < 1 || items > 3) - Perl_croak(aTHX_ "Usage: quest::worldwidemove(string zone_short_name, [uint8 min_status = 0, uint8 max_status = 0])"); - - if (items == 1) { - const char *zone_short_name = (const char*) SvPV_nolen(ST(0)); - uint8 min_status = 0; - uint8 max_status = 0; - if (items == 2) { - min_status = (uint8) SvUV(ST(1)); - } - - if (items == 3) { - max_status = (uint8) SvUV(ST(2)); - } - quest_manager.WorldWideMove(zone_short_name, min_status, max_status); - } - - XSRETURN_EMPTY; -} - -XS(XS__worldwidemoveinstance); -XS(XS__worldwidemoveinstance) { - dXSARGS; - if (items < 1 || items > 3) - Perl_croak(aTHX_ "Usage: quest::worldwidemoveinstance(uint16 instance_id, [uint8 min_status = 0, uint max_status = 0])"); - { - uint16 instance_id = (uint16) SvUV(ST(0)); - uint8 min_status = 0; - uint8 max_status = 0; - if (items == 2) { - min_status = (uint8) SvUV(ST(1)); - } - - if (items == 3) { - max_status = (uint8) SvUV(ST(2)); - } - quest_manager.WorldWideMoveInstance(instance_id, min_status, max_status); - } - - XSRETURN_EMPTY; -} - -XS(XS__worldwideremovespell); -XS(XS__worldwideremovespell) { - dXSARGS; - if (items < 1 || items > 3) - Perl_croak(aTHX_ "Usage: quest::worldwideremovespell(uint32 spell_id, [uint8 min_status = 0, uint max_status = 0])"); - { - uint32 spell_id = (uint32) SvUV(ST(0)); - uint8 min_status = 0; - uint8 max_status = 0; - if (items == 2) { - min_status = (uint8) SvUV(ST(1)); - } - - if (items == 3) { - max_status = (uint8) SvUV(ST(2)); - } - quest_manager.WorldWideRemoveSpell(spell_id, min_status, max_status); - } - - XSRETURN_EMPTY; -} - -XS(XS__worldwideremovetask); -XS(XS__worldwideremovetask) { - dXSARGS; - if (items < 1 || items > 3) - Perl_croak(aTHX_ "Usage: quest::worldwideremovetask(uint32 task_id, [uint8 min_status = 0, uint max_status = 0])"); - { - uint32 task_id = (uint32) SvUV(ST(0)); - uint8 min_status = 0; - uint8 max_status = 0; - if (items == 2) { - min_status = (uint8) SvUV(ST(1)); - } - - if (items == 3) { - max_status = (uint8) SvUV(ST(2)); - } - quest_manager.WorldWideRemoveTask(task_id, min_status, max_status); - } - - XSRETURN_EMPTY; -} - -XS(XS__worldwideresetactivity); -XS(XS__worldwideresetactivity) { - dXSARGS; - if (items < 2 || items > 4) - Perl_croak(aTHX_ "Usage: quest::worldwideresetactivity(uint32 task_id, int activity_id, [uint8 min_status = 0, uint max_status = 0])"); - { - uint32 task_id = (uint32) SvUV(ST(0)); - int activity_id = (int) SvIV(ST(1)); - uint8 min_status = 0; - uint8 max_status = 0; - if (items == 3) { - min_status = (uint8) SvUV(ST(2)); - } - - if (items == 4) { - max_status = (uint8) SvUV(ST(3)); - } - quest_manager.WorldWideResetActivity(task_id, activity_id, min_status, max_status); - } - - XSRETURN_EMPTY; -} - -XS(XS__worldwidesetentityvariableclient); -XS(XS__worldwidesetentityvariableclient) { - dXSARGS; - if (items < 2 || items > 4) - Perl_croak(aTHX_ "Usage: quest::worldwidesetentityvariableclient(string variable_name, string variable_value, [uint8 min_status = 0, uint max_status = 0])"); - { - const char *variable_name = (const char*) SvPV_nolen(ST(0)); - const char *variable_value = (const char*) SvPV_nolen(ST(1)); - uint8 min_status = 0; - uint8 max_status = 0; - if (items == 3) { - min_status = (uint8) SvUV(ST(2)); - } - - if (items == 4) { - max_status = (uint8) SvUV(ST(3)); - } - quest_manager.WorldWideSetEntityVariableClient(variable_name, variable_value, min_status, max_status); - } - - XSRETURN_EMPTY; -} - -XS(XS__worldwidesetentityvariablenpc); -XS(XS__worldwidesetentityvariablenpc) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::worldwidesetentityvariablenpc(string variable_name, string variable_value)"); - { - const char *variable_name = (const char*) SvPV_nolen(ST(0)); - const char *variable_value = (const char*) SvPV_nolen(ST(1)); - quest_manager.WorldWideSetEntityVariableNPC(variable_name, variable_value); - } - - XSRETURN_EMPTY; -} - -XS(XS__worldwidesignalnpc); -XS(XS__worldwidesignalnpc) { - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: quest::worldwidesignalnpc(uint32 signal)"); - { - uint32 signal = (uint32) SvUV(ST(0)); - quest_manager.WorldWideSignalNPC(signal); - } - - XSRETURN_EMPTY; -} - -XS(XS__worldwidesignalclient); -XS(XS__worldwidesignalclient) { - dXSARGS; - if (items < 1 || items > 3) - Perl_croak(aTHX_ "Usage: quest::worldwidesignalclient(uint32 signal, [uint8 min_status = 0, uint max_status = 0])"); - { - uint32 signal = (uint32) SvUV(ST(0)); - uint8 min_status = 0; - uint8 max_status = 0; - if (items == 2) { - min_status = (uint8) SvUV(ST(1)); - } - - if (items == 3) { - max_status = (uint8) SvUV(ST(1)); - } - quest_manager.WorldWideSignalClient(signal, min_status, max_status); - } - - XSRETURN_EMPTY; -} -XS(XS__worldwideupdateactivity); -XS(XS__worldwideupdateactivity) { - dXSARGS; - if (items < 2 || items > 5) - Perl_croak(aTHX_ "Usage: quest::worldwideupdateactivity(uint32 task_id, int activity_id, [int activity_count = 1, uint8 min_status = 0, uint max_status = 0])"); - { - uint32 task_id = (uint32) SvUV(ST(0)); - int activity_id = (int) SvIV(ST(1)); - int activity_count = 1; - uint8 min_status = 0; - uint8 max_status = 0; - if (items == 3) { - activity_count = (int) SvIV(ST(2)); - } - - if (items == 4) { - min_status = (uint8) SvUV(ST(3)); - } - - if (items == 5) { - max_status = (uint8) SvUV(ST(4)); - } - quest_manager.WorldWideUpdateActivity(task_id, activity_id, activity_count, min_status, max_status); - } - - XSRETURN_EMPTY; -} - XS(XS__enablerecipe); XS(XS__enablerecipe) { dXSARGS; @@ -6483,7 +5126,7 @@ XS(XS__gethexcolorcode) { sv_setpv(TARG, hex_color_code.c_str()); XSprePUSH; PUSHTARG; - XSRETURN(1); + XSRETURN(1); } XS(XS__getaaexpmodifierbycharid); @@ -6491,7 +5134,7 @@ XS(XS__getaaexpmodifierbycharid) { dXSARGS; if (items != 2) Perl_croak(aTHX_ "Usage: quest::getaaexpmodifierbycharid(uint32 character_id, uint32 zone_id)"); - + dXSTARG; double aa_modifier; uint32 character_id = (uint32) SvUV(ST(0)); @@ -6507,7 +5150,7 @@ XS(XS__getexpmodifierbycharid) { dXSARGS; if (items != 2) Perl_croak(aTHX_ "Usage: quest::getexpmodifierbycharid(uint32 character_id, uint32 zone_id)"); - + dXSTARG; double exp_modifier; uint32 character_id = (uint32) SvUV(ST(0)); @@ -6544,221 +5187,6 @@ XS(XS__setexpmodifierbycharid) { XSRETURN_EMPTY; } -XS(XS__crosszoneaddldonlossbycharid); -XS(XS__crosszoneaddldonlossbycharid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonlossbycharid(int character_id, uint32 theme_id)"); - - uint8 update_type = CZLDoNUpdateType_Character; - uint8 update_subtype = CZLDoNUpdateSubtype_Loss; - int character_id = (int) SvIV(ST(0)); - uint32 theme_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id); - XSRETURN_EMPTY; -} - -XS(XS__crosszoneaddldonpointsbycharid); -XS(XS__crosszoneaddldonpointsbycharid) { - dXSARGS; - if (items != 3) - Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonpointsbycharid(int character_id, uint32 theme_id, int points)"); - - uint8 update_type = CZLDoNUpdateType_Character; - uint8 update_subtype = CZLDoNUpdateSubtype_Points; - int character_id = (int) SvIV(ST(0)); - uint32 theme_id = (uint32) SvUV(ST(1)); - int points = (int) SvIV(ST(2)); - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id, points); - XSRETURN_EMPTY; -} - -XS(XS__crosszoneaddldonwinbycharid); -XS(XS__crosszoneaddldonwinbycharid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonwinbycharid(int character_id, uint32 theme_id)"); - - uint8 update_type = CZLDoNUpdateType_Character; - uint8 update_subtype = CZLDoNUpdateSubtype_Win; - int character_id = (int) SvIV(ST(0)); - uint32 theme_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id); - XSRETURN_EMPTY; -} - -XS(XS__crosszoneaddldonlossbygroupid); -XS(XS__crosszoneaddldonlossbygroupid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonlossbygroupid(int group_id, uint32 theme_id)"); - - uint8 update_type = CZLDoNUpdateType_Group; - uint8 update_subtype = CZLDoNUpdateSubtype_Loss; - int group_id = (int) SvIV(ST(0)); - uint32 theme_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id); - XSRETURN_EMPTY; -} - -XS(XS__crosszoneaddldonpointsbygroupid); -XS(XS__crosszoneaddldonpointsbygroupid) { - dXSARGS; - if (items != 3) - Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonpointsbygroupid(int group_id, uint32 theme_id, int points)"); - - uint8 update_type = CZLDoNUpdateType_Group; - uint8 update_subtype = CZLDoNUpdateSubtype_Points; - int group_id = (int) SvIV(ST(0)); - uint32 theme_id = (uint32) SvUV(ST(1)); - int points = (int) SvIV(ST(2)); - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id, points); - XSRETURN_EMPTY; -} - -XS(XS__crosszoneaddldonwinbygroupid); -XS(XS__crosszoneaddldonwinbygroupid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonwinbygroupid(int group_id, uint32 theme_id)"); - - uint8 update_type = CZLDoNUpdateType_Group; - uint8 update_subtype = CZLDoNUpdateSubtype_Win; - int group_id = (int) SvIV(ST(0)); - uint32 theme_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id); - XSRETURN_EMPTY; -} - -XS(XS__crosszoneaddldonlossbyraidid); -XS(XS__crosszoneaddldonlossbyraidid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonlossbyraidid(int raid_id, uint32 theme_id)"); - - uint8 update_type = CZLDoNUpdateType_Raid; - uint8 update_subtype = CZLDoNUpdateSubtype_Loss; - int raid_id = (int) SvIV(ST(0)); - uint32 theme_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id); - XSRETURN_EMPTY; -} - -XS(XS__crosszoneaddldonpointsbyraidid); -XS(XS__crosszoneaddldonpointsbyraidid) { - dXSARGS; - if (items != 3) - Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonpointsbyraidid(int raid_id, uint32 theme_id, int points)"); - - uint8 update_type = CZLDoNUpdateType_Raid; - uint8 update_subtype = CZLDoNUpdateSubtype_Points; - int raid_id = (int) SvIV(ST(0)); - uint32 theme_id = (uint32) SvUV(ST(1)); - int points = (int) SvIV(ST(2)); - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id, points); - XSRETURN_EMPTY; -} - -XS(XS__crosszoneaddldonwinbyraidid); -XS(XS__crosszoneaddldonwinbyraidid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonwinbyraidid(int raid_id, uint32 theme_id)"); - - uint8 update_type = CZLDoNUpdateType_Raid; - uint8 update_subtype = CZLDoNUpdateSubtype_Win; - int raid_id = (int) SvIV(ST(0)); - uint32 theme_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id); - XSRETURN_EMPTY; -} - -XS(XS__crosszoneaddldonlossbyguildid); -XS(XS__crosszoneaddldonlossbyguildid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonlossbyguildid(int guild_id, uint32 theme_id)"); - - uint8 update_type = CZLDoNUpdateType_Guild; - uint8 update_subtype = CZLDoNUpdateSubtype_Loss; - int guild_id = (int) SvIV(ST(0)); - uint32 theme_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id); - XSRETURN_EMPTY; -} - -XS(XS__crosszoneaddldonpointsbyguildid); -XS(XS__crosszoneaddldonpointsbyguildid) { - dXSARGS; - if (items != 3) - Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonpointsbyguildid(int guild_id, uint32 theme_id, int points)"); - - uint8 update_type = CZLDoNUpdateType_Guild; - uint8 update_subtype = CZLDoNUpdateSubtype_Points; - int guild_id = (int) SvIV(ST(0)); - uint32 theme_id = (uint32) SvUV(ST(1)); - int points = (int) SvIV(ST(2)); - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id, points); - XSRETURN_EMPTY; -} - -XS(XS__crosszoneaddldonwinbyguildid); -XS(XS__crosszoneaddldonwinbyguildid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonwinbyguildid(int guild_id, uint32 theme_id)"); - - uint8 update_type = CZLDoNUpdateType_Guild; - uint8 update_subtype = CZLDoNUpdateSubtype_Win; - int guild_id = (int) SvIV(ST(0)); - uint32 theme_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id); - XSRETURN_EMPTY; -} - -XS(XS__crosszoneaddldonlossbyexpeditionid); -XS(XS__crosszoneaddldonlossbyexpeditionid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonlossbyexpeditionid(uint32 expedition_id, uint32 theme_id)"); - - uint8 update_type = CZLDoNUpdateType_Expedition; - uint8 update_subtype = CZLDoNUpdateSubtype_Loss; - uint32 expedition_id = (uint32) SvUV(ST(0)); - uint32 theme_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id); - XSRETURN_EMPTY; -} - -XS(XS__crosszoneaddldonpointsbyexpeditionid); -XS(XS__crosszoneaddldonpointsbyexpeditionid) { - dXSARGS; - if (items != 3) - Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonpointsbyexpeditionid(uint32 expedition_id, uint32 theme_id, int points)"); - - uint8 update_type = CZLDoNUpdateType_Expedition; - uint8 update_subtype = CZLDoNUpdateSubtype_Points; - uint32 expedition_id = (uint32) SvUV(ST(0)); - uint32 theme_id = (uint32) SvUV(ST(1)); - int points = (int) SvIV(ST(2)); - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id, points); - XSRETURN_EMPTY; -} - -XS(XS__crosszoneaddldonwinbyexpeditionid); -XS(XS__crosszoneaddldonwinbyexpeditionid) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonwinbyexpeditionid(uint32 expedition_id, uint32 theme_id)"); - - uint8 update_type = CZLDoNUpdateType_Expedition; - uint8 update_subtype = CZLDoNUpdateSubtype_Win; - uint32 expedition_id = (uint32) SvUV(ST(0)); - uint32 theme_id = (uint32) SvUV(ST(1)); - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id); - XSRETURN_EMPTY; -} - XS(XS__getcleannpcnamebyid); XS(XS__getcleannpcnamebyid) { dXSARGS; @@ -6870,7 +5298,7 @@ XS(XS__getspellstat) { uint8 slot = 0; if (items == 3) slot = (uint8) SvUV(ST(2)); - + stat_value = quest_manager.getspellstat(spell_id, stat_identifier, slot); XSprePUSH; @@ -6879,6 +5307,2321 @@ XS(XS__getspellstat) { XSRETURN(1); } +XS(XS__crosszoneaddldonlossbycharid); +XS(XS__crosszoneaddldonlossbycharid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonlossbycharid(int character_id, uint32 theme_id)"); + { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + int character_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonlossbygroupid); +XS(XS__crosszoneaddldonlossbygroupid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonlossbygroupid(int group_id, uint32 theme_id)"); + { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + int group_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonlossbyraidid); +XS(XS__crosszoneaddldonlossbyraidid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonlossbyraidid(int raid_id, uint32 theme_id)"); + { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + int raid_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonlossbyguildid); +XS(XS__crosszoneaddldonlossbyguildid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonlossbyguildid(int guild_id, uint32 theme_id)"); + { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + int guild_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonlossbyexpeditionid); +XS(XS__crosszoneaddldonlossbyexpeditionid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonlossbyexpeditionid(uint32 expedition_id, uint32 theme_id)"); + { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + uint32 expedition_id = (uint32) SvUV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonlossbyclientname); +XS(XS__crosszoneaddldonlossbyclientname) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonlossbyclientname(const char* client_name, uint32 theme_id)"); + { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + int update_identifier = 0; + int points = 1; + const char* client_name = (const char*) SvPV_nolen(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, update_identifier, theme_id, points, client_name); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonpointsbycharid); +XS(XS__crosszoneaddldonpointsbycharid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonpointsbycharid(int character_id, uint32 theme_id, int points)"); + { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZLDoNUpdateSubtype_Points; + int character_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + int points = (int) SvIV(ST(2)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id, points); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonpointsbygroupid); +XS(XS__crosszoneaddldonpointsbygroupid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonpointsbygroupid(int group_id, uint32 theme_id, int points)"); + { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZLDoNUpdateSubtype_Points; + int group_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + int points = (int) SvIV(ST(2)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id, points); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonpointsbyraidid); +XS(XS__crosszoneaddldonpointsbyraidid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonpointsbyraidid(int raid_id, uint32 theme_id, int points)"); + { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZLDoNUpdateSubtype_Points; + int raid_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + int points = (int) SvIV(ST(2)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id, points); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonpointsbyguildid); +XS(XS__crosszoneaddldonpointsbyguildid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonpointsbyguildid(int guild_id, uint32 theme_id, int points)"); + { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZLDoNUpdateSubtype_Points; + int guild_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + int points = (int) SvIV(ST(2)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id, points); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonpointsbyexpeditionid); +XS(XS__crosszoneaddldonpointsbyexpeditionid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonpointsbyexpeditionid(uint32 expedition_id, uint32 theme_id, int points)"); + { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZLDoNUpdateSubtype_Points; + uint32 expedition_id = (uint32) SvUV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + int points = (int) SvIV(ST(2)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id, points); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonpointsbyclientname); +XS(XS__crosszoneaddldonpointsbyclientname) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonpointsbyclientname(const char* client_name, uint32 theme_id, int points)"); + { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZLDoNUpdateSubtype_Points; + int update_identifier = 0; + const char* client_name = (const char*) SvPV_nolen(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + int points = (int) SvIV(ST(2)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, update_identifier, theme_id, points, client_name); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonwinbycharid); +XS(XS__crosszoneaddldonwinbycharid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonwinbycharid(int character_id, uint32 theme_id)"); + { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZLDoNUpdateSubtype_Win; + int character_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonwinbygroupid); +XS(XS__crosszoneaddldonwinbygroupid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonwinbygroupid(int group_id, uint32 theme_id)"); + { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZLDoNUpdateSubtype_Win; + int group_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonwinbyraidid); +XS(XS__crosszoneaddldonwinbyraidid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonwinbyraidid(int raid_id, uint32 theme_id)"); + { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZLDoNUpdateSubtype_Win; + int raid_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonwinbyguildid); +XS(XS__crosszoneaddldonwinbyguildid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonwinbyguildid(int guild_id, uint32 theme_id)"); + { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZLDoNUpdateSubtype_Win; + int guild_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonwinbyexpeditionid); +XS(XS__crosszoneaddldonwinbyexpeditionid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonwinbyexpeditionid(uint32 expedition_id, uint32 theme_id)"); + { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZLDoNUpdateSubtype_Win; + uint32 expedition_id = (uint32) SvUV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneaddldonwinbyclientname); +XS(XS__crosszoneaddldonwinbyclientname) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonwinbyclientname(const char* client_name, uint32 theme_id)"); + { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZLDoNUpdateSubtype_Win; + int update_identifier = 0; + int points = 1; + const char* client_name = (const char*) SvPV_nolen(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, update_identifier, theme_id, points, client_name); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneassigntaskbycharid); +XS(XS__crosszoneassigntaskbycharid) { + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: quest::crosszoneassigntaskbycharid(int character_id, uint32 task_identifier, [bool enforce_level_requirement = false])"); + { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZTaskUpdateSubtype_AssignTask; + int character_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvIV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + if (items == 3) + enforce_level_requirement = (bool) SvTRUE(ST(2)); + + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, character_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneassigntaskbygroupid); +XS(XS__crosszoneassigntaskbygroupid) { + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: quest::crosszoneassigntaskbygroupid(int group_id, uint32 task_identifier, [bool enforce_level_requirement = false])"); + { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZTaskUpdateSubtype_AssignTask; + int group_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvIV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + if (items == 3) + enforce_level_requirement = (bool) SvTRUE(ST(2)); + + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, group_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneassigntaskbyraidid); +XS(XS__crosszoneassigntaskbyraidid) { + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: quest::crosszoneassigntaskbyraidid(int raid_id, uint32 task_identifier, [bool enforce_level_requirement = false])");\ + { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZTaskUpdateSubtype_AssignTask; + int raid_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvIV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + if (items == 3) + enforce_level_requirement = (bool) SvTRUE(ST(2)); + + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, raid_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneassigntaskbyguildid); +XS(XS__crosszoneassigntaskbyguildid) { + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: quest::crosszoneassigntaskbyguildid(int guild_id, uint32 task_identifier, [bool enforce_level_requirement = false])"); + { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZTaskUpdateSubtype_AssignTask; + int guild_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvIV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + if (items == 3) + enforce_level_requirement = (bool) SvTRUE(ST(2)); + + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, guild_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneassigntaskbyexpeditionid); +XS(XS__crosszoneassigntaskbyexpeditionid) { + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: quest::crosszoneassigntaskbyexpeditionid(uint32 expedition_id, uint32 task_identifier, [bool enforce_level_requirement = false])"); + { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZTaskUpdateSubtype_AssignTask; + uint32 expedition_id = (uint32) SvUV(ST(0)); + uint32 task_identifier = (uint32) SvIV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + if (items == 3) + enforce_level_requirement = (bool) SvTRUE(ST(2)); + + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, expedition_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneassigntaskbyclientname); +XS(XS__crosszoneassigntaskbyclientname) { + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: quest::crosszoneassigntaskbyclientname(const char* client_name, uint32 task_identifier, [bool enforce_level_requirement = false])"); + { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZTaskUpdateSubtype_AssignTask; + int update_identifier = 0; + const char* client_name = (const char*) SvPV_nolen(ST(0)); + uint32 task_identifier = (uint32) SvIV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + if (items == 3) + enforce_level_requirement = (bool) SvTRUE(ST(2)); + + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, update_identifier, task_identifier, task_subidentifier, update_count, enforce_level_requirement, client_name); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonecastspellbycharid); +XS(XS__crosszonecastspellbycharid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonecastspellbycharid(int character_id, uint32 spell_id)"); + { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZSpellUpdateSubtype_Cast; + int character_id = (int) SvIV(ST(0)); + uint32 spell_id = (uint32) SvIV(ST(1)); + quest_manager.CrossZoneSpell(update_type, update_subtype, character_id, spell_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonecastspellbygroupid); +XS(XS__crosszonecastspellbygroupid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonecastspellbygroupid(int group_id, uint32 spell_id)"); + { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZSpellUpdateSubtype_Cast; + int group_id = (int) SvIV(ST(0)); + uint32 spell_id = (uint32) SvIV(ST(1)); + quest_manager.CrossZoneSpell(update_type, update_subtype, group_id, spell_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonecastspellbyraidid); +XS(XS__crosszonecastspellbyraidid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonecastspellbyraidid(int raid_id, uint32 spell_id)"); + { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZSpellUpdateSubtype_Cast; + int raid_id = (int) SvIV(ST(0)); + uint32 spell_id = (uint32) SvIV(ST(1)); + quest_manager.CrossZoneSpell(update_type, update_subtype, raid_id, spell_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonecastspellbyguildid); +XS(XS__crosszonecastspellbyguildid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonecastspellbyguildid(int guild_id, uint32 spell_id)"); + { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZSpellUpdateSubtype_Cast; + int guild_id = (int) SvIV(ST(0)); + uint32 spell_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneSpell(update_type, update_subtype, guild_id, spell_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonecastspellbyexpeditionid); +XS(XS__crosszonecastspellbyexpeditionid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonecastspellbyexpeditionid(uint32 expedition_id, uint32 spell_id)"); + { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZSpellUpdateSubtype_Cast; + uint32 expedition_id = (uint32) SvUV(ST(0)); + uint32 spell_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneSpell(update_type, update_subtype, expedition_id, spell_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonecastspellbyclientname); +XS(XS__crosszonecastspellbyclientname) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonecastspellbyclientname(const char* client_name, uint32 spell_id)"); + { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZSpellUpdateSubtype_Cast; + int update_identifier = 0; + const char* client_name = (const char*) SvPV_nolen(ST(0)); + uint32 spell_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneSpell(update_type, update_subtype, update_identifier, spell_id, client_name); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonedisabletaskbycharid); +XS(XS__crosszonedisabletaskbycharid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonedisabletaskbycharid(int character_id, uint32 task_identifier)"); + { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZTaskUpdateSubtype_DisableTask; + int character_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, character_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonedisabletaskbygroupid); +XS(XS__crosszonedisabletaskbygroupid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonedisabletaskbygroupid(int group_id, uint32 task_identifier)"); + { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZTaskUpdateSubtype_DisableTask; + int group_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, group_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonedisabletaskbyraidid); +XS(XS__crosszonedisabletaskbyraidid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonedisabletaskbyraidid(int raid_id, uint32 task_identifier)"); + { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZTaskUpdateSubtype_DisableTask; + int raid_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, raid_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonedisabletaskbyguildid); +XS(XS__crosszonedisabletaskbyguildid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonedisabletaskbyguildid(int guild_id, uint32 task_identifier)"); + { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZTaskUpdateSubtype_DisableTask; + int guild_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, guild_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonedisabletaskbyexpeditionid); +XS(XS__crosszonedisabletaskbyexpeditionid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonedisabletaskbyexpeditionid(uint32 expedition_id, uint32 task_identifier)"); + { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZTaskUpdateSubtype_DisableTask; + uint32 expedition_id = (uint32) SvUV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, expedition_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonedisabletaskbyclientname); +XS(XS__crosszonedisabletaskbyclientname) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonedisabletaskbyclientname(const char* client_name, uint32 task_identifier)"); + { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZTaskUpdateSubtype_DisableTask; + int update_identifier = 0; + const char* client_name = (const char*) SvPV_nolen(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, update_identifier, task_identifier, task_subidentifier, update_count, enforce_level_requirement, client_name); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneenabletaskbycharid); +XS(XS__crosszoneenabletaskbycharid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneenabletaskbycharid(int character_id, uint32 task_identifier)"); + { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZTaskUpdateSubtype_EnableTask; + int character_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, character_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneenabletaskbygroupid); +XS(XS__crosszoneenabletaskbygroupid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneenabletaskbygroupid(int group_id, uint32 task_identifier)"); + { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZTaskUpdateSubtype_EnableTask; + int group_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, group_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneenabletaskbyraidid); +XS(XS__crosszoneenabletaskbyraidid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneenabletaskbyraidid(int raid_id, uint32 task_identifier)"); + { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZTaskUpdateSubtype_EnableTask; + int raid_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, raid_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneenabletaskbyguildid); +XS(XS__crosszoneenabletaskbyguildid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneenabletaskbyguildid(int guild_id, uint32 task_identifier)"); + { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZTaskUpdateSubtype_EnableTask; + int guild_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, guild_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneenabletaskbyexpeditionid); +XS(XS__crosszoneenabletaskbyexpeditionid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneenabletaskbyexpeditionid(uint32 expedition_id, uint32 task_identifier)"); + { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZTaskUpdateSubtype_EnableTask; + uint32 expedition_id = (uint32) SvUV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, expedition_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneenabletaskbyclientname); +XS(XS__crosszoneenabletaskbyclientname) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneenabletaskbyclientname(const char* client_name, uint32 task_identifier)"); + { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZTaskUpdateSubtype_EnableTask; + int update_identifier = 0; + const char* client_name = (const char*) SvPV_nolen(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, update_identifier, task_identifier, task_subidentifier, update_count, enforce_level_requirement, client_name); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonefailtaskbycharid); +XS(XS__crosszonefailtaskbycharid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonefailtaskbycharid(int character_id, uint32 task_identifier)"); + { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZTaskUpdateSubtype_FailTask; + int character_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, character_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonefailtaskbygroupid); +XS(XS__crosszonefailtaskbygroupid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonefailtaskbygroupid(int group_id, uint32 task_identifier)"); + { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZTaskUpdateSubtype_FailTask; + int group_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, group_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonefailtaskbyraidid); +XS(XS__crosszonefailtaskbyraidid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonefailtaskbyraidid(int raid_id, uint32 task_identifier)"); + { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZTaskUpdateSubtype_FailTask; + int raid_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, raid_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonefailtaskbyguildid); +XS(XS__crosszonefailtaskbyguildid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonefailtaskbyguildid(int guild_id, uint32 task_identifier)"); + { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZTaskUpdateSubtype_FailTask; + int guild_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, guild_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonefailtaskbyexpeditionid); +XS(XS__crosszonefailtaskbyexpeditionid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonefailtaskbyexpeditionid(uint32 expedition_id, uint32 task_identifier)"); + { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZTaskUpdateSubtype_FailTask; + uint32 expedition_id = (uint32) SvUV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, expedition_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonefailtaskbyclientname); +XS(XS__crosszonefailtaskbyclientname) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonefailtaskbyclientname(const char* client_name, uint32 task_identifier)"); + { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZTaskUpdateSubtype_FailTask; + int update_identifier = 0; + const char* client_name = (const char*) SvPV_nolen(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, update_identifier, task_identifier, task_subidentifier, update_count, enforce_level_requirement, client_name); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonemarqueebycharid); +XS(XS__crosszonemarqueebycharid) { + dXSARGS; + if (items != 7) + Perl_croak(aTHX_ "Usage: quest::crosszonemarqueebycharid(int character_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message)"); + { + uint8 update_type = CZUpdateType_Character; + int character_id = (int) SvIV(ST(0)); + uint32 type = (uint32) SvUV(ST(1)); + int priority = (int) SvIV(ST(2)); + int fade_in = (int) SvIV(ST(3)); + int fade_out = (int) SvIV(ST(4)); + int duration = (int) SvIV(ST(5)); + char *message = (char *) SvPV_nolen(ST(6)); + quest_manager.CrossZoneMarquee(update_type, character_id, type, priority, fade_in, fade_out, duration, message); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonemarqueebygroupid); +XS(XS__crosszonemarqueebygroupid) { + dXSARGS; + if (items != 7) + Perl_croak(aTHX_ "Usage: quest::crosszonemarqueebygroupid(int group_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message)"); + { + uint8 update_type = CZUpdateType_Group; + int group_id = (int) SvIV(ST(0)); + uint32 type = (uint32) SvUV(ST(1)); + int priority = (int) SvIV(ST(2)); + int fade_in = (int) SvIV(ST(3)); + int fade_out = (int) SvIV(ST(4)); + int duration = (int) SvIV(ST(5)); + char *message = (char *) SvPV_nolen(ST(6)); + quest_manager.CrossZoneMarquee(update_type, group_id, type, priority, fade_in, fade_out, duration, message); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonemarqueebyraidid); +XS(XS__crosszonemarqueebyraidid) { + dXSARGS; + if (items != 7) + Perl_croak(aTHX_ "Usage: quest::crosszonemarqueebyraidid(int raid_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message)"); + { + uint8 update_type = CZUpdateType_Raid; + int raid_id = (int) SvIV(ST(0)); + uint32 type = (uint32) SvUV(ST(1)); + int priority = (int) SvIV(ST(2)); + int fade_in = (int) SvIV(ST(3)); + int fade_out = (int) SvIV(ST(4)); + int duration = (int) SvIV(ST(5)); + char *message = (char *) SvPV_nolen(ST(6)); + quest_manager.CrossZoneMarquee(update_type, raid_id, type, priority, fade_in, fade_out, duration, message); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonemarqueebyguildid); +XS(XS__crosszonemarqueebyguildid) { + dXSARGS; + if (items != 7) + Perl_croak(aTHX_ "Usage: quest::crosszonemarqueebyguildid(int guild_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message)"); + { + uint8 update_type = CZUpdateType_Guild; + int guild_id = (int) SvIV(ST(0)); + uint32 type = (uint32) SvUV(ST(1)); + int priority = (int) SvIV(ST(2)); + int fade_in = (int) SvIV(ST(3)); + int fade_out = (int) SvIV(ST(4)); + int duration = (int) SvIV(ST(5)); + char *message = (char *) SvPV_nolen(ST(6)); + quest_manager.CrossZoneMarquee(update_type, guild_id, type, priority, fade_in, fade_out, duration, message); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonemarqueebyexpeditionid); +XS(XS__crosszonemarqueebyexpeditionid) { + dXSARGS; + if (items != 7) + Perl_croak(aTHX_ "Usage: quest::crosszonemarqueebyexpeditionid(uint32 expedition_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message)"); + { + uint8 update_type = CZUpdateType_Expedition; + uint32 expedition_id = (uint32) SvUV(ST(0)); + uint32 type = (uint32) SvUV(ST(1)); + int priority = (int) SvIV(ST(2)); + int fade_in = (int) SvIV(ST(3)); + int fade_out = (int) SvIV(ST(4)); + int duration = (int) SvIV(ST(5)); + char *message = (char *) SvPV_nolen(ST(6)); + quest_manager.CrossZoneMarquee(update_type, expedition_id, type, priority, fade_in, fade_out, duration, message); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonemarqueebyclientname); +XS(XS__crosszonemarqueebyclientname) { + dXSARGS; + if (items != 7) + Perl_croak(aTHX_ "Usage: quest::crosszonemarqueebyclientname(const char* client_name, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message)"); + { + uint8 update_type = CZUpdateType_ClientName; + int update_identifier = 0; + const char* client_name = (const char*) SvPV_nolen(ST(0)); + uint32 type = (uint32) SvUV(ST(1)); + int priority = (int) SvIV(ST(2)); + int fade_in = (int) SvIV(ST(3)); + int fade_out = (int) SvIV(ST(4)); + int duration = (int) SvIV(ST(5)); + char *message = (char *) SvPV_nolen(ST(6)); + quest_manager.CrossZoneMarquee(update_type, update_identifier, type, priority, fade_in, fade_out, duration, message, client_name); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonemessageplayerbycharid); +XS(XS__crosszonemessageplayerbycharid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszonemessageplayerbycharid(int character_id, uint32 type, const char* message)"); + { + uint8 update_type = CZUpdateType_Character; + int character_id = (int) SvIV(ST(0)); + uint32 type = (uint32) SvUV(ST(1)); + const char* message = (const char*) SvPV_nolen(ST(2)); + quest_manager.CrossZoneMessage(update_type, character_id, type, message); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonemessageplayerbygroupid); +XS(XS__crosszonemessageplayerbygroupid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszonemessageplayerbygroupid(int group_id, uint32 type, const char* message)"); + { + uint8 update_type = CZUpdateType_Group; + int group_id = (int) SvIV(ST(0)); + uint32 type = (uint32) SvUV(ST(1)); + const char* message = (const char*) SvPV_nolen(ST(2)); + quest_manager.CrossZoneMessage(update_type, group_id, type, message); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonemessageplayerbyraidid); +XS(XS__crosszonemessageplayerbyraidid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszonemessageplayerbyraidid(int raid_id, uint32 type, const char* message)"); + { + uint8 update_type = CZUpdateType_Raid; + int raid_id = (int) SvIV(ST(0)); + uint32 type = (uint32) SvUV(ST(1)); + const char* message = (const char*) SvPV_nolen(ST(2)); + quest_manager.CrossZoneMessage(update_type, raid_id, type, message); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonemessageplayerbyguildid); +XS(XS__crosszonemessageplayerbyguildid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszonemessageplayerbyguildid(int guild_id, uint32 type, const char* message)"); + { + uint8 update_type = CZUpdateType_Guild; + int guild_id = (int) SvIV(ST(0)); + uint32 type = (uint32) SvUV(ST(1)); + const char* message = (const char*) SvPV_nolen(ST(2)); + quest_manager.CrossZoneMessage(update_type, guild_id, type, message); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonemessageplayerbyexpeditionid); +XS(XS__crosszonemessageplayerbyexpeditionid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszonemessageplayerbyexpeditionid(uint32 expedition_id, uint32 type, const char* message)"); + { + uint8 update_type = CZUpdateType_Expedition; + uint32 expedition_id = (uint32) SvUV(ST(0)); + uint32 type = (uint32) SvUV(ST(1)); + const char* message = (const char*) SvPV_nolen(ST(2)); + quest_manager.CrossZoneMessage(update_type, expedition_id, type, message); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonemessageplayerbyname); +XS(XS__crosszonemessageplayerbyname) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszonemessageplayerbyname(const char* client_name, uint32 type, const char* message)"); + { + uint8 update_type = CZUpdateType_ClientName; + int update_identifier = 0; + const char* client_name = (const char*) SvPV_nolen(ST(0)); + uint32 type = (uint32) SvUV(ST(1)); + const char* message = (const char*) SvPV_nolen(ST(2)); + quest_manager.CrossZoneMessage(update_type, update_identifier, type, message, client_name); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonemoveplayerbycharid); +XS(XS__crosszonemoveplayerbycharid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonemoveplayerbycharid(int character_id, string zone_short_name)"); + { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZMoveUpdateSubtype_MoveZone; + int character_id = (int) SvIV(ST(0)); + const char* zone_short_name = (const char*) SvPV_nolen(ST(1)); + uint16 instance_id = 0; + quest_manager.CrossZoneMove(update_type, update_subtype, character_id, zone_short_name, instance_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonemoveplayerbygroupid); +XS(XS__crosszonemoveplayerbygroupid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonemoveplayerbygroupid(int group_id, string zone_short_name)"); + { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZMoveUpdateSubtype_MoveZone; + int group_id = (int) SvIV(ST(0)); + const char* zone_short_name = (const char*) SvPV_nolen(ST(1)); + uint16 instance_id = 0; + quest_manager.CrossZoneMove(update_type, update_subtype, group_id, zone_short_name, instance_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonemoveplayerbyraidid); +XS(XS__crosszonemoveplayerbyraidid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonemoveplayerbyraidid(int raid_id, string zone_short_name)"); + { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZMoveUpdateSubtype_MoveZone; + int raid_id = (int) SvIV(ST(0)); + const char* zone_short_name = (const char*) SvPV_nolen(ST(1)); + uint16 instance_id = 0; + quest_manager.CrossZoneMove(update_type, update_subtype, raid_id, zone_short_name, instance_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonemoveplayerbyguildid); +XS(XS__crosszonemoveplayerbyguildid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonemoveplayerbyguildid(int guild_id, string zone_short_name)"); + { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZMoveUpdateSubtype_MoveZone; + int guild_id = (int) SvIV(ST(0)); + const char* zone_short_name = (const char*) SvPV_nolen(ST(1)); + uint16 instance_id = 0; + quest_manager.CrossZoneMove(update_type, update_subtype, guild_id, zone_short_name, instance_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonemoveplayerbyexpeditionid); +XS(XS__crosszonemoveplayerbyexpeditionid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonemoveplayerbyexpeditionid(uint32 expedition_id, string zone_short_name)"); + { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZMoveUpdateSubtype_MoveZone; + uint32 expedition_id = (uint32) SvUV(ST(0)); + const char* zone_short_name = (const char*) SvPV_nolen(ST(1)); + uint16 instance_id = 0; + quest_manager.CrossZoneMove(update_type, update_subtype, expedition_id, zone_short_name, instance_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonemoveplayerbyname); +XS(XS__crosszonemoveplayerbyname) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonemoveplayerbyname(const char* client_name, string zone_short_name)"); + { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZMoveUpdateSubtype_MoveZone; + int update_identifier = 0; + const char* client_name = (const char*) SvPV_nolen(ST(0)); + const char* zone_short_name = (const char*) SvPV_nolen(ST(1)); + uint16 instance_id = 0; + quest_manager.CrossZoneMove(update_type, update_subtype, update_identifier, zone_short_name, instance_id, client_name); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonemoveinstancebycharid); +XS(XS__crosszonemoveinstancebycharid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonemoveinstancebycharid(int character_id, uint16 instance_id)"); + { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZMoveUpdateSubtype_MoveZoneInstance; + const char* zone_short_name = ""; + int character_id = (int) SvIV(ST(0)); + uint16 instance_id = (uint16) SvUV(ST(1)); + quest_manager.CrossZoneMove(update_type, update_subtype, character_id, zone_short_name, instance_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonemoveinstancebygroupid); +XS(XS__crosszonemoveinstancebygroupid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonemoveinstancebygroupid(int group_id, uint16 instance_id)"); + { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZMoveUpdateSubtype_MoveZoneInstance; + const char* zone_short_name = ""; + int group_id = (int) SvIV(ST(0)); + uint16 instance_id = (uint16) SvUV(ST(1)); + quest_manager.CrossZoneMove(update_type, update_subtype, group_id, zone_short_name, instance_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonemoveinstancebyraidid); +XS(XS__crosszonemoveinstancebyraidid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonemoveinstancebyraidid(int raid_id, uint16 instance_id)"); + { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZMoveUpdateSubtype_MoveZoneInstance; + const char* zone_short_name = ""; + int raid_id = (int) SvIV(ST(0)); + uint16 instance_id = (uint16) SvUV(ST(1)); + quest_manager.CrossZoneMove(update_type, update_subtype, raid_id, zone_short_name, instance_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonemoveinstancebyguildid); +XS(XS__crosszonemoveinstancebyguildid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonemoveinstancebyguildid(int guild_id, uint16 instance_id)"); + { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZMoveUpdateSubtype_MoveZoneInstance; + const char* zone_short_name = ""; + int guild_id = (int) SvIV(ST(0)); + uint16 instance_id = (uint16) SvUV(ST(1)); + quest_manager.CrossZoneMove(update_type, update_subtype, guild_id, zone_short_name, instance_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonemoveinstancebyexpeditionid); +XS(XS__crosszonemoveinstancebyexpeditionid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonemoveinstancebyexpeditionid(uint32 expedition_id, uint16 instance_id)"); + { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZMoveUpdateSubtype_MoveZoneInstance; + const char* zone_short_name = ""; + uint32 expedition_id = (uint32) SvUV(ST(0)); + uint16 instance_id = (uint16) SvUV(ST(1)); + quest_manager.CrossZoneMove(update_type, update_subtype, expedition_id, zone_short_name, instance_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonemoveinstancebyclientname); +XS(XS__crosszonemoveinstancebyclientname) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonemoveinstancebyclientname(const char* client_name, uint16 instance_id)"); + { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZMoveUpdateSubtype_MoveZoneInstance; + const char* zone_short_name = ""; + int update_identifier = 0; + const char* client_name = (const char*) SvPV_nolen(ST(0)); + uint16 instance_id = (uint16) SvUV(ST(1)); + quest_manager.CrossZoneMove(update_type, update_subtype, update_identifier, zone_short_name, instance_id, client_name); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneremovespellbycharid); +XS(XS__crosszoneremovespellbycharid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneremovespellbycharid(int character_id, uint32 spell_id)"); + { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZSpellUpdateSubtype_Remove; + int character_id = (int) SvIV(ST(0)); + uint32 spell_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneSpell(update_type, update_subtype, character_id, spell_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneremovespellbygroupid); +XS(XS__crosszoneremovespellbygroupid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneremovespellbygroupid(int group_id, uint32 spell_id)"); + { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZSpellUpdateSubtype_Remove; + int group_id = (int) SvIV(ST(0)); + uint32 spell_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneSpell(update_type, update_subtype, group_id, spell_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneremovespellbyraidid); +XS(XS__crosszoneremovespellbyraidid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneremovespellbyraidid(int raid_id, uint32 spell_id)"); + { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZSpellUpdateSubtype_Remove; + int raid_id = (int) SvIV(ST(0)); + uint32 spell_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneSpell(update_type, update_subtype, raid_id, spell_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneremovespellbyguildid); +XS(XS__crosszoneremovespellbyguildid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneremovespellbyguildid(int guild_id, uint32 spell_id)"); + { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZSpellUpdateSubtype_Remove; + int guild_id = (int) SvIV(ST(0)); + uint32 spell_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneSpell(update_type, update_subtype, guild_id, spell_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneremovespellbyexpeditionid); +XS(XS__crosszoneremovespellbyexpeditionid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneremovespellbyexpeditionid(uint32 expedition_id, uint32 spell_id)"); + { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZSpellUpdateSubtype_Remove; + uint32 expedition_id = (uint32) SvUV(ST(0)); + uint32 spell_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneSpell(update_type, update_subtype, expedition_id, spell_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneremovespellbyclientname); +XS(XS__crosszoneremovespellbyclientname) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneremovespellbyclientname(const char* client_name, uint32 spell_id)"); + { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZSpellUpdateSubtype_Remove; + int update_identifier = 0; + const char* client_name = (const char*) SvPV_nolen(ST(0)); + uint32 spell_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneSpell(update_type, update_subtype, update_identifier, spell_id, client_name); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneremovetaskbycharid); +XS(XS__crosszoneremovetaskbycharid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneremovetaskbycharid(int character_id, uint32 task_identifier)"); + { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZTaskUpdateSubtype_RemoveTask; + int character_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, character_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneremovetaskbygroupid); +XS(XS__crosszoneremovetaskbygroupid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneremovetaskbygroupid(int group_id, uint32 task_identifier)"); + { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZTaskUpdateSubtype_RemoveTask; + int group_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, group_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneremovetaskbyraidid); +XS(XS__crosszoneremovetaskbyraidid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneremovetaskbyraidid(int raid_id, uint32 task_identifier)"); + { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZTaskUpdateSubtype_RemoveTask; + int raid_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, raid_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneremovetaskbyguildid); +XS(XS__crosszoneremovetaskbyguildid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneremovetaskbyguildid(int guild_id, uint32 task_identifier)"); + { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZTaskUpdateSubtype_RemoveTask; + int guild_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, guild_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneremovetaskbyexpeditionid); +XS(XS__crosszoneremovetaskbyexpeditionid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneremovetaskbyexpeditionid(uint32 expedition_id, uint32 task_identifier)"); + { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZTaskUpdateSubtype_RemoveTask; + uint32 expedition_id = (uint32) SvUV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, expedition_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneremovetaskbyclientname); +XS(XS__crosszoneremovetaskbyclientname) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneremovetaskbyclientname(const char* client_name, uint32 task_identifier)"); + { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZTaskUpdateSubtype_RemoveTask; + int update_identifier = 0; + const char* client_name = (const char*) SvPV_nolen(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, update_identifier, task_identifier, task_subidentifier, update_count, enforce_level_requirement, client_name); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneresetactivitybycharid); +XS(XS__crosszoneresetactivitybycharid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszoneresetactivitybycharid(int character_id, uint32 task_identifier, int activity_id)"); + { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityReset; + int character_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int activity_id = (int) SvIV(ST(2)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, character_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneresetactivitybygroupid); +XS(XS__crosszoneresetactivitybygroupid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszoneresetactivitybygroupid(int group_id, uint32 task_identifier, int activity_id)"); + { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityReset; + int group_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int activity_id = (int) SvIV(ST(2)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, group_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneresetactivitybyraidid); +XS(XS__crosszoneresetactivitybyraidid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszoneresetactivitybyraidid(int raid_id, uint32 task_identifier, int activity_id)"); + { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityReset; + int raid_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int activity_id = (int) SvIV(ST(2)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, raid_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneresetactivitybyguildid); +XS(XS__crosszoneresetactivitybyguildid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszoneresetactivitybyguildid(int guild_id, uint32 task_identifier, int activity_id)"); + { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityReset; + int guild_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int activity_id = (int) SvIV(ST(2)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, guild_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneresetactivitybyexpeditionid); +XS(XS__crosszoneresetactivitybyexpeditionid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszoneresetactivitybyexpeditionid(uint32 expedition_id, uint32 task_identifier, int activity_id)"); + { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityReset; + uint32 expedition_id = (uint32) SvUV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int activity_id = (int) SvIV(ST(2)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, expedition_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneresetactivitybyclientname); +XS(XS__crosszoneresetactivitybyclientname) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszoneresetactivitybyclientname(const char* client_name, uint32 task_identifier, int activity_id)"); + { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityReset; + int update_identifier = 0; + const char* client_name = (const char*) SvPV_nolen(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int activity_id = (int) SvIV(ST(2)); + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, update_identifier, task_identifier, task_subidentifier, update_count, enforce_level_requirement, client_name); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonesetentityvariablebycharid); +XS(XS__crosszonesetentityvariablebycharid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszonesetentityvariablebycharid(int character_id, const char* variable_name, const char* variable_value)"); + { + uint8 update_type = CZUpdateType_Character; + int character_id = (int) SvIV(ST(0)); + const char* variable_name = (const char*) SvPV_nolen(ST(1)); + const char* variable_value = (const char*) SvPV_nolen(ST(2)); + quest_manager.CrossZoneSetEntityVariable(update_type, character_id, variable_name, variable_value); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonesetentityvariablebygroupid); +XS(XS__crosszonesetentityvariablebygroupid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszonesetentityvariablebygroupid(int group_id, const char* variable_name, const char* variable_value)"); + { + uint8 update_type = CZUpdateType_Group; + int group_id = (int) SvIV(ST(0)); + const char* variable_name = (const char*) SvPV_nolen(ST(1)); + const char* variable_value = (const char*) SvPV_nolen(ST(2)); + quest_manager.CrossZoneSetEntityVariable(update_type, group_id, variable_name, variable_value); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonesetentityvariablebyraidid); +XS(XS__crosszonesetentityvariablebyraidid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszonesetentityvariablebyraidid(int raid_id, const char* variable_name, const char* variable_value)"); + { + uint8 update_type = CZUpdateType_Raid; + int raid_id = (int) SvIV(ST(0)); + const char* variable_name = (const char*) SvPV_nolen(ST(1)); + const char* variable_value = (const char*) SvPV_nolen(ST(2)); + quest_manager.CrossZoneSetEntityVariable(update_type, raid_id, variable_name, variable_value); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonesetentityvariablebyguildid); +XS(XS__crosszonesetentityvariablebyguildid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszonesetentityvariablebyguildid(int guild_id, const char* variable_name, const char* variable_value)"); + { + uint8 update_type = CZUpdateType_Guild; + int guild_id = (int) SvIV(ST(0)); + const char* variable_name = (const char*) SvPV_nolen(ST(1)); + const char* variable_value = (const char*) SvPV_nolen(ST(2)); + quest_manager.CrossZoneSetEntityVariable(update_type, guild_id, variable_name, variable_value); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonesetentityvariablebyexpeditionid); +XS(XS__crosszonesetentityvariablebyexpeditionid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszonesetentityvariablebyexpeditionid(uint32 expedition_id, const char* variable_name, const char* variable_value)"); + { + uint8 update_type = CZUpdateType_Expedition; + uint32 expedition_id = (uint32) SvUV(ST(0)); + const char* variable_name = (const char*) SvPV_nolen(ST(1)); + const char* variable_value = (const char*) SvPV_nolen(ST(2)); + quest_manager.CrossZoneSetEntityVariable(update_type, expedition_id, variable_name, variable_value); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonesetentityvariablebyclientname); +XS(XS__crosszonesetentityvariablebyclientname) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszonesetentityvariablebyclientname(const char* client_name, const char* variable_name, const char* variable_value)"); + { + uint8 update_type = CZUpdateType_ClientName; + int update_identifier = 0; + const char* client_name = (const char*) SvPV_nolen(ST(0)); + const char* variable_name = (const char*) SvPV_nolen(ST(1)); + const char* variable_value = (const char*) SvPV_nolen(ST(2)); + quest_manager.CrossZoneSetEntityVariable(update_type, update_identifier, variable_name, variable_value, client_name); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonesetentityvariablebynpctypeid); +XS(XS__crosszonesetentityvariablebynpctypeid) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: quest::crosszonesetentityvariablebynpctypeid(int npc_id, const char* variable_name, const char* variable_value)"); + { + uint8 update_type = CZUpdateType_NPC; + int npc_id = (int) SvIV(ST(0)); + const char* variable_name = (const char*) SvPV_nolen(ST(1)); + const char* variable_value = (const char*) SvPV_nolen(ST(2)); + quest_manager.CrossZoneSetEntityVariable(update_type, npc_id, variable_name, variable_value); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonesignalclientbycharid); +XS(XS__crosszonesignalclientbycharid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonesignalclientbycharid(int character_id, uint32 signal)"); + { + uint8 update_type = CZUpdateType_Character; + int character_id = (int) SvIV(ST(0)); + uint32 signal = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneSignal(update_type, character_id, signal); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonesignalclientbygroupid); +XS(XS__crosszonesignalclientbygroupid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonesignalclientbygroupid(int group_id, uint32 signal)"); + { + uint8 update_type = CZUpdateType_Group; + int group_id = (int) SvIV(ST(0)); + uint32 signal = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneSignal(update_type, group_id, signal); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonesignalclientbyraidid); +XS(XS__crosszonesignalclientbyraidid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonesignalclientbyraidid(int raid_id, uint32 signal)"); + { + uint8 update_type = CZUpdateType_Raid; + int raid_id = (int) SvIV(ST(0)); + uint32 signal = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneSignal(update_type, raid_id, signal); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonesignalclientbyguildid); +XS(XS__crosszonesignalclientbyguildid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonesignalclientbyguildid(int guild_id, uint32 signal)"); + { + uint8 update_type = CZUpdateType_Guild; + int guild_id = (int) SvIV(ST(0)); + uint32 signal = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneSignal(update_type, guild_id, signal); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonesignalclientbyexpeditionid); +XS(XS__crosszonesignalclientbyexpeditionid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonesignalclientbyexpeditionid(uint32 expedition_id, uint32 signal)"); + { + uint8 update_type = CZUpdateType_Expedition; + uint32 expedition_id = (uint32) SvUV(ST(0)); + uint32 signal = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneSignal(update_type, expedition_id, signal); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonesignalclientbyname); +XS(XS__crosszonesignalclientbyname) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonesignalclientbyname(const char* client_name, uint32 signal)"); + { + uint8 update_type = CZUpdateType_Expedition; + int update_identifier = 0; + const char* client_name = (const char*) SvPV_nolen(ST(0)); + uint32 signal = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneSignal(update_type, update_identifier, signal, client_name); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonesignalnpcbynpctypeid); +XS(XS__crosszonesignalnpcbynpctypeid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonesignalnpcbynpctypeid(uint32 npc_id, uint32 signal)"); + { + uint8 update_type = CZUpdateType_NPC; + uint32 npc_id = (uint32) SvUV(ST(0)); + uint32 signal = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneSignal(update_type, npc_id, signal); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneupdateactivitybycharid); +XS(XS__crosszoneupdateactivitybycharid) { + dXSARGS; + if (items < 3 || items > 4) + Perl_croak(aTHX_ "Usage: quest::crosszoneupdateactivitybycharid(int character_id, uint32 task_identifier, int activity_id, [int update_count = 1])"); + { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityUpdate; + int character_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = (int) SvIV(ST(2)); + int update_count = 1; + bool enforce_level_requirement = false; + if (items == 4) + update_count = (int) SvIV(ST(3)); + + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, character_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneupdateactivitybygroupid); +XS(XS__crosszoneupdateactivitybygroupid) { + dXSARGS; + if (items < 3 || items > 4) + Perl_croak(aTHX_ "Usage: quest::crosszoneupdateactivitybygroupid(int group_id, uint32 task_identifier, int activity_id, [int update_count = 1])"); + { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityUpdate; + int group_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = (int) SvIV(ST(2)); + int update_count = 1; + bool enforce_level_requirement = false; + if (items == 4) + update_count = (int) SvIV(ST(3)); + + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, group_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneupdateactivitybyraidid); +XS(XS__crosszoneupdateactivitybyraidid) { + dXSARGS; + if (items < 3 || items > 4) + Perl_croak(aTHX_ "Usage: quest::crosszoneupdateactivitybyraidid(int raid_id, uint32 task_identifier, int activity_id, [int update_count = 1])"); + { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityUpdate; + int raid_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = (int) SvIV(ST(2)); + int update_count = 1; + bool enforce_level_requirement = false; + if (items == 4) + update_count = (int) SvIV(ST(3)); + + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, raid_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneupdateactivitybyguildid); +XS(XS__crosszoneupdateactivitybyguildid) { + dXSARGS; + if (items < 3 || items > 4) + Perl_croak(aTHX_ "Usage: quest::crosszoneupdateactivitybyguildid(int guild_id, uint32 task_identifier, int activity_id, [int update_count = 1])"); + { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityUpdate; + int guild_id = (int) SvIV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = (int) SvIV(ST(2)); + int update_count = 1; + bool enforce_level_requirement = false; + if (items == 4) + update_count = (int) SvIV(ST(3)); + + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, guild_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneupdateactivitybyexpeditionid); +XS(XS__crosszoneupdateactivitybyexpeditionid) { + dXSARGS; + if (items < 3 || items > 4) + Perl_croak(aTHX_ "Usage: quest::crosszoneupdateactivitybyexpeditionid(uint32 expedition_id, uint32 task_identifier, int activity_id, [int update_count = 1])"); + { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityUpdate; + uint32 expedition_id = (uint32) SvUV(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = (int) SvIV(ST(2)); + int update_count = 1; + bool enforce_level_requirement = false; + if (items == 4) + update_count = (int) SvIV(ST(3)); + + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, expedition_id, task_identifier, task_subidentifier, update_count, enforce_level_requirement); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneupdateactivitybyclientname); +XS(XS__crosszoneupdateactivitybyclientname) { + dXSARGS; + if (items < 3 || items > 4) + Perl_croak(aTHX_ "Usage: quest::crosszoneupdateactivitybyclientname(const char* client_name, uint32 task_identifier, int activity_id, [int update_count = 1])"); + { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityUpdate; + int update_identifier = 0; + const char* client_name = (const char*) SvPV_nolen(ST(0)); + uint32 task_identifier = (uint32) SvUV(ST(1)); + int task_subidentifier = (int) SvIV(ST(2)); + int update_count = 1; + bool enforce_level_requirement = false; + if (items == 4) + update_count = (int) SvIV(ST(3)); + + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, update_identifier, task_identifier, task_subidentifier, update_count, enforce_level_requirement, client_name); + } + XSRETURN_EMPTY; +} + +XS(XS__worldwideassigntask); +XS(XS__worldwideassigntask) { + dXSARGS; + if (items < 1 || items > 3) + Perl_croak(aTHX_ "Usage: quest::worldwideassigntask(uint32 task_identifier, [uint8 min_status = 0, uint8 max_status = 0])"); + { + uint8 update_type = WWTaskUpdateType_AssignTask; + uint32 task_identifier = (uint32) SvUV(ST(0)); + uint8 min_status = 0; + uint8 max_status = 0; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + if (items == 2) + min_status = (uint8) SvUV(ST(1)); + + if (items == 3) + max_status = (uint8) SvUV(ST(2)); + + quest_manager.WorldWideTaskUpdate(update_type, task_identifier, task_subidentifier, update_count, enforce_level_requirement, min_status, max_status); + } + XSRETURN_EMPTY; +} + +XS(XS__worldwidecastspell); +XS(XS__worldwidecastspell) { + dXSARGS; + if (items < 1 || items > 3) + Perl_croak(aTHX_ "Usage: quest::worldwidecastspell(uint32 spell_id, [uint8 min_status = 0, uint8 max_status = 0])"); + { + uint8 update_type = WWSpellUpdateType_Cast; + uint32 spell_id = (uint32) SvUV(ST(0)); + uint8 min_status = 0; + uint8 max_status = 0; + if (items == 2) + min_status = (uint8) SvUV(ST(1)); + + if (items == 3) + max_status = (uint8) SvUV(ST(2)); + + quest_manager.WorldWideSpell(update_type, spell_id, min_status, max_status); + } + XSRETURN_EMPTY; +} + +XS(XS__worldwidedisabletask); +XS(XS__worldwidedisabletask) { + dXSARGS; + if (items < 1 || items > 3) + Perl_croak(aTHX_ "Usage: quest::worldwidedisabletask(uint32 task_id, [uint8 min_status = 0, uint8 max_status = 0])"); + { + uint8 update_type = WWTaskUpdateType_DisableTask; + uint32 task_identifier = (uint32) SvUV(ST(0)); + uint8 min_status = 0; + uint8 max_status = 0; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + if (items == 2) + min_status = (uint8) SvUV(ST(1)); + + if (items == 3) + max_status = (uint8) SvUV(ST(2)); + + quest_manager.WorldWideTaskUpdate(update_type, task_identifier, task_subidentifier, update_count, enforce_level_requirement, min_status, max_status); + } + XSRETURN_EMPTY; +} + +XS(XS__worldwideenabletask); +XS(XS__worldwideenabletask) { + dXSARGS; + if (items < 1 || items > 3) + Perl_croak(aTHX_ "Usage: quest::worldwideenabletask(uint32 task_id, [uint8 min_status = 0, uint8 max_status = 0])"); + { + uint8 update_type = WWTaskUpdateType_EnableTask; + uint32 task_identifier = (uint32) SvUV(ST(0)); + uint8 min_status = 0; + uint8 max_status = 0; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + if (items == 2) + min_status = (uint8) SvUV(ST(1)); + + if (items == 3) + max_status = (uint8) SvUV(ST(2)); + + quest_manager.WorldWideTaskUpdate(update_type, task_identifier, task_subidentifier, update_count, enforce_level_requirement, min_status, max_status); + } + XSRETURN_EMPTY; +} + +XS(XS__worldwidefailtask); +XS(XS__worldwidefailtask) { + dXSARGS; + if (items < 1 || items > 3) + Perl_croak(aTHX_ "Usage: quest::worldwidefailtask(uint32 task_id, [uint8 min_status = 0, uint8 max_status = 0])"); + { + uint8 update_type = WWTaskUpdateType_FailTask; + uint32 task_identifier = (uint32) SvUV(ST(0)); + uint8 min_status = 0; + uint8 max_status = 0; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + if (items == 2) + min_status = (uint8) SvUV(ST(1)); + + if (items == 3) + max_status = (uint8) SvUV(ST(2)); + + quest_manager.WorldWideTaskUpdate(update_type, task_identifier, task_subidentifier, update_count, enforce_level_requirement, min_status, max_status); + } + XSRETURN_EMPTY; +} + +XS(XS__worldwidemarquee); +XS(XS__worldwidemarquee) { + dXSARGS; + if (items < 6 || items > 8) + Perl_croak(aTHX_ "Usage: quest::worldwidemarquee(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char* message, [uint8 min_status = 0, uint8 max_status = 0])"); + { + uint32 type = (uint32) SvUV(ST(0)); + uint32 priority = (uint32) SvUV(ST(1)); + uint32 fade_in = (uint32) SvUV(ST(2)); + uint32 fade_out = (uint32) SvUV(ST(3)); + uint32 duration = (uint32) SvUV(ST(4)); + const char* message = (const char*) SvPV_nolen(ST(5)); + uint8 min_status = 0; + uint8 max_status = 0; + if (items == 7) + min_status = (uint8) SvUV(ST(6)); + + if (items == 8) + max_status = (uint8) SvUV(ST(7)); + + quest_manager.WorldWideMarquee(type, priority, fade_in, fade_out, duration, message, min_status, max_status); + } + XSRETURN_EMPTY; +} + +XS(XS__worldwidemessage); +XS(XS__worldwidemessage) { + dXSARGS; + if (items < 2 || items > 4) + Perl_croak(aTHX_ "Usage: quest::worldwidemessage(uint32 type, const char* message, [uint8 min_status = 0, uint8 max_status = 0])"); + { + uint32 type = (uint32) SvUV(ST(0)); + const char* message = (const char*) SvPV_nolen(ST(1)); + uint8 min_status = 0; + uint8 max_status = 0; + if (items == 3) + min_status = (uint8) SvUV(ST(2)); + + if (items == 4) + max_status = (uint8) SvUV(ST(3)); + + quest_manager.WorldWideMessage(type, message, min_status, max_status); + } + XSRETURN_EMPTY; +} + +XS(XS__worldwidemove); +XS(XS__worldwidemove) { + dXSARGS; + if (items < 1 || items > 3) + Perl_croak(aTHX_ "Usage: quest::worldwidemove(const char* zone_short_name, [uint8 min_status = 0, uint8 max_status = 0])"); + { + uint8 update_type = WWMoveUpdateType_MoveZone; + const char* zone_short_name = (const char*) SvPV_nolen(ST(0)); + uint16 instance_id = 0; + uint8 min_status = 0; + uint8 max_status = 0; + if (items == 2) + min_status = (uint8) SvUV(ST(1)); + + if (items == 3) + max_status = (uint8) SvUV(ST(2)); + + quest_manager.WorldWideMove(update_type, zone_short_name, instance_id, min_status, max_status); + } + XSRETURN_EMPTY; +} + +XS(XS__worldwidemoveinstance); +XS(XS__worldwidemoveinstance) { + dXSARGS; + if (items < 1 || items > 3) + Perl_croak(aTHX_ "Usage: quest::worldwidemoveinstance(uint16 instance_id, [uint8 min_status = 0, uint max_status = 0])"); + { + uint8 update_type = WWMoveUpdateType_MoveZoneInstance; + const char* zone_short_name = ""; + uint16 instance_id = (uint16) SvUV(ST(0)); + uint8 min_status = 0; + uint8 max_status = 0; + if (items == 2) + min_status = (uint8) SvUV(ST(1)); + + if (items == 3) + max_status = (uint8) SvUV(ST(2)); + + quest_manager.WorldWideMove(update_type, zone_short_name, instance_id, min_status, max_status); + } + XSRETURN_EMPTY; +} + +XS(XS__worldwideremovespell); +XS(XS__worldwideremovespell) { + dXSARGS; + if (items < 1 || items > 3) + Perl_croak(aTHX_ "Usage: quest::worldwideremovespell(uint32 spell_id, [uint8 min_status = 0, uint max_status = 0])"); + { + uint8 update_type = WWSpellUpdateType_Remove; + uint32 spell_id = (uint32) SvUV(ST(0)); + uint8 min_status = 0; + uint8 max_status = 0; + if (items == 2) + min_status = (uint8) SvUV(ST(1)); + + if (items == 3) + max_status = (uint8) SvUV(ST(2)); + + quest_manager.WorldWideSpell(update_type, spell_id, min_status, max_status); + } + XSRETURN_EMPTY; +} + +XS(XS__worldwideremovetask); +XS(XS__worldwideremovetask) { + dXSARGS; + if (items < 1 || items > 3) + Perl_croak(aTHX_ "Usage: quest::worldwideremovetask(uint32 task_identifier, [uint8 min_status = 0, uint max_status = 0])"); + { + uint8 update_type = WWTaskUpdateType_RemoveTask; + uint32 task_identifier = (uint32) SvUV(ST(0)); + uint8 min_status = 0; + uint8 max_status = 0; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + if (items == 2) + min_status = (uint8) SvUV(ST(1)); + + if (items == 3) + max_status = (uint8) SvUV(ST(2)); + + quest_manager.WorldWideTaskUpdate(update_type, task_identifier, task_subidentifier, update_count, enforce_level_requirement, min_status, max_status); + } + + XSRETURN_EMPTY; +} + +XS(XS__worldwideresetactivity); +XS(XS__worldwideresetactivity) { + dXSARGS; + if (items < 2 || items > 4) + Perl_croak(aTHX_ "Usage: quest::worldwideresetactivity(uint32 task_identifier, int activity_id, [uint8 min_status = 0, uint max_status = 0])"); + { + uint8 update_type = WWTaskUpdateType_ActivityReset; + uint32 task_identifier = (uint32) SvUV(ST(0)); + int task_subidentifier = (int) SvIV(ST(1)); + uint8 min_status = 0; + uint8 max_status = 0; + int update_count = 1; + bool enforce_level_requirement = false; + if (items == 3) + min_status = (uint8) SvUV(ST(2)); + + if (items == 4) + max_status = (uint8) SvUV(ST(3)); + + quest_manager.WorldWideTaskUpdate(update_type, task_identifier, task_subidentifier, update_count, enforce_level_requirement, min_status, max_status); + } + XSRETURN_EMPTY; +} + +XS(XS__worldwidesetentityvariableclient); +XS(XS__worldwidesetentityvariableclient) { + dXSARGS; + if (items < 2 || items > 4) + Perl_croak(aTHX_ "Usage: quest::worldwidesetentityvariableclient(const char* variable_name, const char* variable_value, [uint8 min_status = 0, uint max_status = 0])"); + { + uint8 update_type = WWSetEntityVariableUpdateType_Character; + const char* variable_name = (const char*) SvPV_nolen(ST(0)); + const char* variable_value = (const char*) SvPV_nolen(ST(1)); + uint8 min_status = 0; + uint8 max_status = 0; + if (items == 3) + min_status = (uint8) SvUV(ST(2)); + + if (items == 4) + max_status = (uint8) SvUV(ST(3)); + + quest_manager.WorldWideSetEntityVariable(update_type, variable_name, variable_value, min_status, max_status); + } + XSRETURN_EMPTY; +} + +XS(XS__worldwidesetentityvariablenpc); +XS(XS__worldwidesetentityvariablenpc) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::worldwidesetentityvariablenpc(const char* variable_name, const char* variable_value)"); + { + uint8 update_type = WWSetEntityVariableUpdateType_NPC; + const char* variable_name = (const char*) SvPV_nolen(ST(0)); + const char* variable_value = (const char*) SvPV_nolen(ST(1)); + quest_manager.WorldWideSetEntityVariable(update_type, variable_name, variable_value); + } + XSRETURN_EMPTY; +} + +XS(XS__worldwidesignalnpc); +XS(XS__worldwidesignalnpc) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::worldwidesignalnpc(uint32 signal)"); + { + uint8 update_type = WWSignalUpdateType_NPC; + uint32 signal = (uint32) SvUV(ST(0)); + quest_manager.WorldWideSignal(update_type, signal); + } + XSRETURN_EMPTY; +} + +XS(XS__worldwidesignalclient); +XS(XS__worldwidesignalclient) { + dXSARGS; + if (items < 1 || items > 3) + Perl_croak(aTHX_ "Usage: quest::worldwidesignalclient(uint32 signal, [uint8 min_status = 0, uint max_status = 0])"); + { + uint8 update_type = WWSignalUpdateType_Character; + uint32 signal = (uint32) SvUV(ST(0)); + uint8 min_status = 0; + uint8 max_status = 0; + if (items == 2) + min_status = (uint8) SvUV(ST(1)); + + if (items == 3) + max_status = (uint8) SvUV(ST(2)); + + quest_manager.WorldWideSignal(update_type, signal, min_status, max_status); + } + XSRETURN_EMPTY; +} + +XS(XS__worldwideupdateactivity); +XS(XS__worldwideupdateactivity) { + dXSARGS; + if (items < 2 || items > 5) + Perl_croak(aTHX_ "Usage: quest::worldwideupdateactivity(uint32 task_identifier, int activity_id, [int update_count = 1, uint8 min_status = 0, uint max_status = 0])"); + { + uint8 update_type = WWTaskUpdateType_ActivityUpdate; + uint32 task_identifier = (uint32) SvUV(ST(0)); + int task_subidentifier = (int) SvIV(ST(1)); + uint8 min_status = 0; + uint8 max_status = 0; + int update_count = 1; + bool enforce_level_requirement = false; + if (items == 3) + update_count = (int) SvIV(ST(2)); + + if (items == 4) + min_status = (uint8) SvUV(ST(3)); + + if (items == 5) + max_status = (uint8) SvUV(ST(4)); + + quest_manager.WorldWideTaskUpdate(update_type, task_identifier, task_subidentifier, update_count, enforce_level_requirement, min_status, max_status); + } + XSRETURN_EMPTY; +} + +XS(XS__worldwideaddldonloss); +XS(XS__worldwideaddldonloss) { + dXSARGS; + if (items < 1 || items > 3) + Perl_croak(aTHX_ "Usage: quest::worldwideaddldonloss(uint32 theme_id, [min_status = 0, max_status = 0])"); + { + uint8 update_type = CZLDoNUpdateSubtype_Loss; + uint32 theme_id = (uint32) SvUV(ST(0)); + int points = 1; + uint8 min_status = 0; + uint8 max_status = 0; + if (items == 2) + min_status = (uint8) SvUV(ST(1)); + + if (items == 3) + max_status = (uint8) SvUV(ST(2)); + + quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status, max_status); + } + XSRETURN_EMPTY; +} + +XS(XS__worldwideaddldonpoints); +XS(XS__worldwideaddldonpoints) { + dXSARGS; + if (items < 1 || items > 4) + Perl_croak(aTHX_ "Usage: quest::worldwideaddldonpoints(uint32 theme_id. [int points = 1, min_status = 0, max_status = 0])"); + { + uint8 update_type = CZLDoNUpdateSubtype_Points; + uint32 theme_id = (uint32) SvUV(ST(0)); + int points = 1; + uint8 min_status = 0; + uint8 max_status = 0; + if (items == 2) + points = (int) SvIV(ST(1)); + + if (items == 3) + min_status = (uint8) SvUV(ST(2)); + + if (items == 4) + max_status = (uint8) SvUV(ST(3)); + + quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status, max_status); + } + XSRETURN_EMPTY; +} + +XS(XS__worldwideaddldonwin); +XS(XS__worldwideaddldonwin) { + dXSARGS; + if (items < 1 || items > 3) + Perl_croak(aTHX_ "Usage: quest::worldwideaddldonwin(uint32 theme_id, [min_status = 0, max_status = 0])"); + { + uint8 update_type = CZLDoNUpdateSubtype_Win; + uint32 theme_id = (uint32) SvUV(ST(0)); + int points = 1; + uint8 min_status = 0; + uint8 max_status = 0; + if (items == 2) + min_status = (uint8) SvUV(ST(1)); + + if (items == 3) + max_status = (uint8) SvUV(ST(2)); + + quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status, max_status); + } + XSRETURN_EMPTY; +} /* This is the callback perl will look for to setup the @@ -6985,79 +7728,114 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "crosszoneaddldonlossbyraidid"), XS__crosszoneaddldonlossbyraidid, file); newXS(strcpy(buf, "crosszoneaddldonlossbyguildid"), XS__crosszoneaddldonlossbyguildid, file); newXS(strcpy(buf, "crosszoneaddldonlossbyexpeditionid"), XS__crosszoneaddldonlossbyexpeditionid, file); + newXS(strcpy(buf, "crosszoneaddldonlossbyclientname"), XS__crosszoneaddldonlossbyclientname, file); newXS(strcpy(buf, "crosszoneaddldonpointsbycharid"), XS__crosszoneaddldonpointsbycharid, file); newXS(strcpy(buf, "crosszoneaddldonpointsbygroupid"), XS__crosszoneaddldonpointsbygroupid, file); newXS(strcpy(buf, "crosszoneaddldonpointsbyraidid"), XS__crosszoneaddldonpointsbyraidid, file); newXS(strcpy(buf, "crosszoneaddldonpointsbyguildid"), XS__crosszoneaddldonpointsbyguildid, file); newXS(strcpy(buf, "crosszoneaddldonpointsbyexpeditionid"), XS__crosszoneaddldonpointsbyexpeditionid, file); + newXS(strcpy(buf, "crosszoneaddldonpointsbyclientname"), XS__crosszoneaddldonpointsbyclientname, file); newXS(strcpy(buf, "crosszoneaddldonwinbycharid"), XS__crosszoneaddldonwinbycharid, file); newXS(strcpy(buf, "crosszoneaddldonwinbygroupid"), XS__crosszoneaddldonwinbygroupid, file); newXS(strcpy(buf, "crosszoneaddldonwinbyraidid"), XS__crosszoneaddldonwinbyraidid, file); newXS(strcpy(buf, "crosszoneaddldonwinbyguildid"), XS__crosszoneaddldonwinbyguildid, file); newXS(strcpy(buf, "crosszoneaddldonwinbyexpeditionid"), XS__crosszoneaddldonwinbyexpeditionid, file); + newXS(strcpy(buf, "crosszoneaddldonwinbyclientname"), XS__crosszoneaddldonwinbyclientname, file); newXS(strcpy(buf, "crosszoneassigntaskbycharid"), XS__crosszoneassigntaskbycharid, file); newXS(strcpy(buf, "crosszoneassigntaskbygroupid"), XS__crosszoneassigntaskbygroupid, file); newXS(strcpy(buf, "crosszoneassigntaskbyraidid"), XS__crosszoneassigntaskbyraidid, file); newXS(strcpy(buf, "crosszoneassigntaskbyguildid"), XS__crosszoneassigntaskbyguildid, file); + newXS(strcpy(buf, "crosszoneassigntaskbyexpeditionid"), XS__crosszoneassigntaskbyexpeditionid, file); + newXS(strcpy(buf, "crosszoneassigntaskbyclientname"), XS__crosszoneassigntaskbyclientname, file); newXS(strcpy(buf, "crosszonecastspellbycharid"), XS__crosszonecastspellbycharid, file); newXS(strcpy(buf, "crosszonecastspellbygroupid"), XS__crosszonecastspellbygroupid, file); newXS(strcpy(buf, "crosszonecastspellbyraidid"), XS__crosszonecastspellbyraidid, file); newXS(strcpy(buf, "crosszonecastspellbyguildid"), XS__crosszonecastspellbyguildid, file); + newXS(strcpy(buf, "crosszonecastspellbyexpeditionid"), XS__crosszonecastspellbyexpeditionid, file); + newXS(strcpy(buf, "crosszonecastspellbyclientname"), XS__crosszonecastspellbyclientname, file); newXS(strcpy(buf, "crosszonedisabletaskbycharid"), XS__crosszonedisabletaskbycharid, file); newXS(strcpy(buf, "crosszonedisabletaskbygroupid"), XS__crosszonedisabletaskbygroupid, file); newXS(strcpy(buf, "crosszonedisabletaskbyraidid"), XS__crosszonedisabletaskbyraidid, file); newXS(strcpy(buf, "crosszonedisabletaskbyguildid"), XS__crosszonedisabletaskbyguildid, file); + newXS(strcpy(buf, "crosszonedisabletaskbyexpeditionid"), XS__crosszonedisabletaskbyexpeditionid, file); + newXS(strcpy(buf, "crosszonedisabletaskbyclientname"), XS__crosszonedisabletaskbyclientname, file); newXS(strcpy(buf, "crosszoneenabletaskbycharid"), XS__crosszoneenabletaskbycharid, file); newXS(strcpy(buf, "crosszoneenabletaskbygroupid"), XS__crosszoneenabletaskbygroupid, file); newXS(strcpy(buf, "crosszoneenabletaskbyraidid"), XS__crosszoneenabletaskbyraidid, file); newXS(strcpy(buf, "crosszoneenabletaskbyguildid"), XS__crosszoneenabletaskbyguildid, file); + newXS(strcpy(buf, "crosszoneenabletaskbyexpeditionid"), XS__crosszoneenabletaskbyexpeditionid, file); + newXS(strcpy(buf, "crosszoneenabletaskbyclientname"), XS__crosszoneenabletaskbyclientname, file); newXS(strcpy(buf, "crosszonefailtaskbycharid"), XS__crosszonefailtaskbycharid, file); newXS(strcpy(buf, "crosszonefailtaskbygroupid"), XS__crosszonefailtaskbygroupid, file); newXS(strcpy(buf, "crosszonefailtaskbyraidid"), XS__crosszonefailtaskbyraidid, file); newXS(strcpy(buf, "crosszonefailtaskbyguildid"), XS__crosszonefailtaskbyguildid, file); + newXS(strcpy(buf, "crosszonefailtaskbyexpeditionid"), XS__crosszonefailtaskbyexpeditionid, file); + newXS(strcpy(buf, "crosszonefailtaskbyclientname"), XS__crosszonefailtaskbyclientname, file); newXS(strcpy(buf, "crosszonemarqueebycharid"), XS__crosszonemarqueebycharid, file); newXS(strcpy(buf, "crosszonemarqueebygroupid"), XS__crosszonemarqueebygroupid, file); newXS(strcpy(buf, "crosszonemarqueebyraidid"), XS__crosszonemarqueebyraidid, file); newXS(strcpy(buf, "crosszonemarqueebyguildid"), XS__crosszonemarqueebyguildid, file); - newXS(strcpy(buf, "crosszonemessageplayerbyname"), XS__crosszonemessageplayerbyname, file); + newXS(strcpy(buf, "crosszonemarqueebyexpeditionid"), XS__crosszonemarqueebyexpeditionid, file); + newXS(strcpy(buf, "crosszonemarqueebyclientname"), XS__crosszonemarqueebyclientname, file); + newXS(strcpy(buf, "crosszonemessageplayerbycharid"), XS__crosszonemessageplayerbycharid, file); newXS(strcpy(buf, "crosszonemessageplayerbygroupid"), XS__crosszonemessageplayerbygroupid, file); newXS(strcpy(buf, "crosszonemessageplayerbyraidid"), XS__crosszonemessageplayerbyraidid, file); newXS(strcpy(buf, "crosszonemessageplayerbyguildid"), XS__crosszonemessageplayerbyguildid, file); + newXS(strcpy(buf, "crosszonemessageplayerbyexpeditionid"), XS__crosszonemessageplayerbyexpeditionid, file); + newXS(strcpy(buf, "crosszonemessageplayerbyname"), XS__crosszonemessageplayerbyname, file); newXS(strcpy(buf, "crosszonemoveplayerbycharid"), XS__crosszonemoveplayerbycharid, file); newXS(strcpy(buf, "crosszonemoveplayerbygroupid"), XS__crosszonemoveplayerbygroupid, file); newXS(strcpy(buf, "crosszonemoveplayerbyraidid"), XS__crosszonemoveplayerbyraidid, file); newXS(strcpy(buf, "crosszonemoveplayerbyguildid"), XS__crosszonemoveplayerbyguildid, file); + newXS(strcpy(buf, "crosszonemoveplayerbyexpeditionid"), XS__crosszonemoveplayerbyexpeditionid, file); + newXS(strcpy(buf, "crosszonemoveplayerbyname"), XS__crosszonemoveplayerbyname, file); newXS(strcpy(buf, "crosszonemoveinstancebycharid"), XS__crosszonemoveinstancebycharid, file); newXS(strcpy(buf, "crosszonemoveinstancebygroupid"), XS__crosszonemoveinstancebygroupid, file); newXS(strcpy(buf, "crosszonemoveinstancebyraidid"), XS__crosszonemoveinstancebyraidid, file); newXS(strcpy(buf, "crosszonemoveinstancebyguildid"), XS__crosszonemoveinstancebyguildid, file); + newXS(strcpy(buf, "crosszonemoveinstancebyexpeditionid"), XS__crosszonemoveinstancebyexpeditionid, file); + newXS(strcpy(buf, "crosszonemoveinstancebyclientname"), XS__crosszonemoveinstancebyclientname, file); newXS(strcpy(buf, "crosszoneremovespellbycharid"), XS__crosszoneremovespellbycharid, file); newXS(strcpy(buf, "crosszoneremovespellbygroupid"), XS__crosszoneremovespellbygroupid, file); newXS(strcpy(buf, "crosszoneremovespellbyraidid"), XS__crosszoneremovespellbyraidid, file); newXS(strcpy(buf, "crosszoneremovespellbyguildid"), XS__crosszoneremovespellbyguildid, file); + newXS(strcpy(buf, "crosszoneremovespellbyexpeditionid"), XS__crosszoneremovespellbyexpeditionid, file); + newXS(strcpy(buf, "crosszoneremovespellbyclientname"), XS__crosszoneremovespellbyclientname, file); newXS(strcpy(buf, "crosszoneremovetaskbycharid"), XS__crosszoneremovetaskbycharid, file); newXS(strcpy(buf, "crosszoneremovetaskbygroupid"), XS__crosszoneremovetaskbygroupid, file); newXS(strcpy(buf, "crosszoneremovetaskbyraidid"), XS__crosszoneremovetaskbyraidid, file); newXS(strcpy(buf, "crosszoneremovetaskbyguildid"), XS__crosszoneremovetaskbyguildid, file); + newXS(strcpy(buf, "crosszoneremovetaskbyexpeditionid"), XS__crosszoneremovetaskbyexpeditionid, file); + newXS(strcpy(buf, "crosszoneremovetaskbyclientname"), XS__crosszoneremovetaskbyclientname, file); newXS(strcpy(buf, "crosszoneresetactivitybycharid"), XS__crosszoneresetactivitybycharid, file); newXS(strcpy(buf, "crosszoneresetactivitybygroupid"), XS__crosszoneresetactivitybygroupid, file); newXS(strcpy(buf, "crosszoneresetactivitybyraidid"), XS__crosszoneresetactivitybyraidid, file); newXS(strcpy(buf, "crosszoneresetactivitybyguildid"), XS__crosszoneresetactivitybyguildid, file); - newXS(strcpy(buf, "crosszonesetentityvariablebynpctypeid"), XS__crosszonesetentityvariablebynpctypeid, file); - newXS(strcpy(buf, "crosszonesetentityvariablebyclientname"), XS__crosszonesetentityvariablebyclientname, file); + newXS(strcpy(buf, "crosszoneresetactivitybyexpeditionid"), XS__crosszoneresetactivitybyexpeditionid, file); + newXS(strcpy(buf, "crosszoneresetactivitybyclientname"), XS__crosszoneresetactivitybyclientname, file); + newXS(strcpy(buf, "crosszonesetentityvariablebycharid"), XS__crosszonesetentityvariablebycharid, file); newXS(strcpy(buf, "crosszonesetentityvariablebygroupid"), XS__crosszonesetentityvariablebygroupid, file); newXS(strcpy(buf, "crosszonesetentityvariablebyraidid"), XS__crosszonesetentityvariablebyraidid, file); newXS(strcpy(buf, "crosszonesetentityvariablebyguildid"), XS__crosszonesetentityvariablebyguildid, file); + newXS(strcpy(buf, "crosszonesetentityvariablebyexpeditionid"), XS__crosszonesetentityvariablebyexpeditionid, file); + newXS(strcpy(buf, "crosszonesetentityvariablebyclientname"), XS__crosszonesetentityvariablebyclientname, file); + newXS(strcpy(buf, "crosszonesetentityvariablebynpctypeid"), XS__crosszonesetentityvariablebynpctypeid, file); newXS(strcpy(buf, "crosszonesignalclientbycharid"), XS__crosszonesignalclientbycharid, file); newXS(strcpy(buf, "crosszonesignalclientbygroupid"), XS__crosszonesignalclientbygroupid, file); newXS(strcpy(buf, "crosszonesignalclientbyraidid"), XS__crosszonesignalclientbyraidid, file); newXS(strcpy(buf, "crosszonesignalclientbyguildid"), XS__crosszonesignalclientbyguildid, file); + newXS(strcpy(buf, "crosszonesignalclientbyexpeditionid"), XS__crosszonesignalclientbyexpeditionid, file); newXS(strcpy(buf, "crosszonesignalclientbyname"), XS__crosszonesignalclientbyname, file); newXS(strcpy(buf, "crosszonesignalnpcbynpctypeid"), XS__crosszonesignalnpcbynpctypeid, file); newXS(strcpy(buf, "crosszoneupdateactivitybycharid"), XS__crosszoneupdateactivitybycharid, file); newXS(strcpy(buf, "crosszoneupdateactivitybygroupid"), XS__crosszoneupdateactivitybygroupid, file); newXS(strcpy(buf, "crosszoneupdateactivitybyraidid"), XS__crosszoneupdateactivitybyraidid, file); newXS(strcpy(buf, "crosszoneupdateactivitybyguildid"), XS__crosszoneupdateactivitybyguildid, file); + newXS(strcpy(buf, "crosszoneupdateactivitybyexpeditionid"), XS__crosszoneupdateactivitybyexpeditionid, file); + newXS(strcpy(buf, "crosszoneupdateactivitybyclientname"), XS__crosszoneupdateactivitybyclientname, file); + newXS(strcpy(buf, "worldwideaddldonloss"), XS__worldwideaddldonloss, file); + newXS(strcpy(buf, "worldwideaddldonpoints"), XS__worldwideaddldonpoints, file); + newXS(strcpy(buf, "worldwideaddldonwin"), XS__worldwideaddldonwin, file); newXS(strcpy(buf, "worldwidecastspell"), XS__worldwidecastspell, file); newXS(strcpy(buf, "worldwidedisabletask"), XS__worldwidedisabletask, file); newXS(strcpy(buf, "worldwideenabletask"), XS__worldwideenabletask, file); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 1992b78eb..3338bea5c 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -1045,482 +1045,6 @@ void lua_send_mail(const char *to, const char *from, const char *subject, const quest_manager.SendMail(to, from, subject, message); } -void lua_cross_zone_assign_task_by_char_id(int character_id, uint32 task_id) { - quest_manager.CrossZoneAssignTaskByCharID(character_id, task_id); -} - -void lua_cross_zone_assign_task_by_char_id(int character_id, uint32 task_id, bool enforce_level_requirement) { - quest_manager.CrossZoneAssignTaskByCharID(character_id, task_id, enforce_level_requirement); -} - -void lua_cross_zone_assign_task_by_group_id(int group_id, uint32 task_id) { - quest_manager.CrossZoneAssignTaskByGroupID(group_id, task_id); -} - -void lua_cross_zone_assign_task_by_group_id(int group_id, uint32 task_id, bool enforce_level_requirement) { - quest_manager.CrossZoneAssignTaskByGroupID(group_id, task_id, enforce_level_requirement); -} - -void lua_cross_zone_assign_task_by_raid_id(int raid_id, uint32 task_id) { - quest_manager.CrossZoneAssignTaskByRaidID(raid_id, task_id); -} - -void lua_cross_zone_assign_task_by_raid_id(int raid_id, uint32 task_id, bool enforce_level_requirement) { - quest_manager.CrossZoneAssignTaskByRaidID(raid_id, task_id, enforce_level_requirement); -} - -void lua_cross_zone_assign_task_by_guild_id(int guild_id, uint32 task_id) { - quest_manager.CrossZoneAssignTaskByGuildID(guild_id, task_id); -} - -void lua_cross_zone_assign_task_by_guild_id(int guild_id, uint32 task_id, bool enforce_level_requirement) { - quest_manager.CrossZoneAssignTaskByGuildID(guild_id, task_id, enforce_level_requirement); -} - -void lua_cross_zone_cast_spell_by_char_id(int character_id, uint32 spell_id) { - quest_manager.CrossZoneCastSpellByCharID(character_id, spell_id); -} - -void lua_cross_zone_cast_spell_by_group_id(int group_id, uint32 spell_id) { - quest_manager.CrossZoneCastSpellByGroupID(group_id, spell_id); -} - -void lua_cross_zone_cast_spell_by_raid_id(int raid_id, uint32 spell_id) { - quest_manager.CrossZoneCastSpellByRaidID(raid_id, spell_id); -} - -void lua_cross_zone_cast_spell_by_guild_id(int guild_id, uint32 spell_id) { - quest_manager.CrossZoneCastSpellByGuildID(guild_id, spell_id); -} - -void lua_cross_zone_disable_task_by_char_id(int character_id, uint32 task_id) { - quest_manager.CrossZoneDisableTaskByCharID(character_id, task_id); -} - -void lua_cross_zone_disable_task_by_group_id(int group_id, uint32 task_id) { - quest_manager.CrossZoneDisableTaskByGroupID(group_id, task_id); -} - -void lua_cross_zone_disable_task_by_raid_id(int raid_id, uint32 task_id) { - quest_manager.CrossZoneDisableTaskByRaidID(raid_id, task_id); -} - -void lua_cross_zone_disable_task_by_guild_id(int guild_id, uint32 task_id) { - quest_manager.CrossZoneDisableTaskByGuildID(guild_id, task_id); -} - -void lua_cross_zone_enable_task_by_char_id(int character_id, uint32 task_id) { - quest_manager.CrossZoneEnableTaskByCharID(character_id, task_id); -} - -void lua_cross_zone_enable_task_by_group_id(int group_id, uint32 task_id) { - quest_manager.CrossZoneEnableTaskByGroupID(group_id, task_id); -} - -void lua_cross_zone_enable_task_by_raid_id(int raid_id, uint32 task_id) { - quest_manager.CrossZoneEnableTaskByRaidID(raid_id, task_id); -} - -void lua_cross_zone_enable_task_by_guild_id(int guild_id, uint32 task_id) { - quest_manager.CrossZoneEnableTaskByGuildID(guild_id, task_id); -} - -void lua_cross_zone_fail_task_by_char_id(int character_id, uint32 task_id) { - quest_manager.CrossZoneFailTaskByCharID(character_id, task_id); -} - -void lua_cross_zone_fail_task_by_group_id(int group_id, uint32 task_id) { - quest_manager.CrossZoneFailTaskByGroupID(group_id, task_id); -} - -void lua_cross_zone_fail_task_by_raid_id(int raid_id, uint32 task_id) { - quest_manager.CrossZoneFailTaskByRaidID(raid_id, task_id); -} - -void lua_cross_zone_fail_task_by_guild_id(int guild_id, uint32 task_id) { - quest_manager.CrossZoneFailTaskByGuildID(guild_id, task_id); -} - -void lua_cross_zone_marquee_by_char_id(int character_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message) { - quest_manager.CrossZoneMarqueeByCharID(character_id, type, priority, fade_in, fade_out, duration, message); -} - -void lua_cross_zone_marquee_by_group_id(int group_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message) { - quest_manager.CrossZoneMarqueeByGroupID(group_id, type, priority, fade_in, fade_out, duration, message); -} - -void lua_cross_zone_marquee_by_raid_id(int raid_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message) { - quest_manager.CrossZoneMarqueeByRaidID(raid_id, type, priority, fade_in, fade_out, duration, message); -} - -void lua_cross_zone_marquee_by_guild_id(int guild_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message) { - quest_manager.CrossZoneMarqueeByGuildID(guild_id, type, priority, fade_in, fade_out, duration, message); -} - -void lua_cross_zone_message_player_by_name(uint32 type, const char *character_name, const char *message) { - quest_manager.CrossZoneMessagePlayerByName(type, character_name, message); -} - -void lua_cross_zone_message_player_by_group_id(uint32 type, int group_id, const char *message) { - quest_manager.CrossZoneMessagePlayerByGroupID(type, group_id, message); -} - -void lua_cross_zone_message_player_by_raid_id(uint32 type, int raid_id, const char *message) { - quest_manager.CrossZoneMessagePlayerByRaidID(type, raid_id, message); -} - -void lua_cross_zone_message_player_by_guild_id(uint32 type, int guild_id, const char *message) { - quest_manager.CrossZoneMessagePlayerByGuildID(type, guild_id, message); -} - -void lua_cross_zone_move_player_by_char_id(int character_id, const char *zone_short_name) { - quest_manager.CrossZoneMovePlayerByCharID(character_id, zone_short_name); -} - -void lua_cross_zone_move_player_by_group_id(int group_id, const char *zone_short_name) { - quest_manager.CrossZoneMovePlayerByGroupID(group_id, zone_short_name); -} - -void lua_cross_zone_move_player_by_raid_id(int raid_id, const char *zone_short_name) { - quest_manager.CrossZoneMovePlayerByRaidID(raid_id, zone_short_name); -} - -void lua_cross_zone_move_player_by_guild_id(int guild_id, const char *zone_short_name) { - quest_manager.CrossZoneMovePlayerByGuildID(guild_id, zone_short_name); -} - -void lua_cross_zone_move_instance_by_char_id(int character_id, uint16 instance_id) { - quest_manager.CrossZoneMoveInstanceByCharID(character_id, instance_id); -} - -void lua_cross_zone_move_instance_by_group_id(int group_id, uint16 instance_id) { - quest_manager.CrossZoneMoveInstanceByGroupID(group_id, instance_id); -} - -void lua_cross_zone_move_instance_by_raid_id(int raid_id, uint16 instance_id) { - quest_manager.CrossZoneMoveInstanceByRaidID(raid_id, instance_id); -} - -void lua_cross_zone_move_instance_by_guild_id(int guild_id, uint16 instance_id) { - quest_manager.CrossZoneMoveInstanceByGuildID(guild_id, instance_id); -} - -void lua_cross_zone_remove_spell_by_char_id(int character_id, uint32 spell_id) { - quest_manager.CrossZoneRemoveSpellByCharID(character_id, spell_id); -} - -void lua_cross_zone_remove_spell_by_group_id(int group_id, uint32 spell_id) { - quest_manager.CrossZoneRemoveSpellByGroupID(group_id, spell_id); -} - -void lua_cross_zone_remove_spell_by_raid_id(int raid_id, uint32 spell_id) { - quest_manager.CrossZoneRemoveSpellByRaidID(raid_id, spell_id); -} - -void lua_cross_zone_remove_spell_by_guild_id(int guild_id, uint32 spell_id) { - quest_manager.CrossZoneRemoveSpellByGuildID(guild_id, spell_id); -} - -void lua_cross_zone_remove_task_by_char_id(int character_id, uint32 task_id) { - quest_manager.CrossZoneRemoveTaskByCharID(character_id, task_id); -} - -void lua_cross_zone_remove_task_by_group_id(int group_id, uint32 task_id) { - quest_manager.CrossZoneRemoveTaskByGroupID(group_id, task_id); -} - -void lua_cross_zone_remove_task_by_raid_id(int raid_id, uint32 task_id) { - quest_manager.CrossZoneRemoveTaskByRaidID(raid_id, task_id); -} - -void lua_cross_zone_remove_task_by_guild_id(int guild_id, uint32 task_id) { - quest_manager.CrossZoneRemoveTaskByGuildID(guild_id, task_id); -} - -void lua_cross_zone_reset_activity_by_char_id(int character_id, uint32 task_id, int activity_id) { - quest_manager.CrossZoneResetActivityByCharID(character_id, task_id, activity_id); -} - -void lua_cross_zone_reset_activity_by_group_id(int group_id, uint32 task_id, int activity_id) { - quest_manager.CrossZoneResetActivityByGroupID(group_id, task_id, activity_id); -} - -void lua_cross_zone_reset_activity_by_raid_id(int raid_id, uint32 task_id, int activity_id) { - quest_manager.CrossZoneResetActivityByRaidID(raid_id, task_id, activity_id); -} - -void lua_cross_zone_reset_activity_by_guild_id(int guild_id, uint32 task_id, int activity_id) { - quest_manager.CrossZoneResetActivityByGuildID(guild_id, task_id, activity_id); -} - -void lua_cross_zone_set_entity_variable_by_client_name(const char *character_name, const char *variable_name, const char *variable_value) { - quest_manager.CrossZoneSetEntityVariableByClientName(character_name, variable_name, variable_value); -} - -void lua_cross_zone_set_entity_variable_by_group_id(int group_id, const char *variable_name, const char *variable_value) { - quest_manager.CrossZoneSetEntityVariableByGroupID(group_id, variable_name, variable_value); -} - -void lua_cross_zone_set_entity_variable_by_raid_id(int raid_id, const char *variable_name, const char *variable_value) { - quest_manager.CrossZoneSetEntityVariableByRaidID(raid_id, variable_name, variable_value); -} - -void lua_cross_zone_set_entity_variable_by_guild_id(int guild_id, const char *variable_name, const char *variable_value) { - quest_manager.CrossZoneSetEntityVariableByGuildID(guild_id, variable_name, variable_value); -} - -void lua_cross_zone_signal_client_by_char_id(uint32 character_id, int signal) { - quest_manager.CrossZoneSignalPlayerByCharID(character_id, signal); -} - -void lua_cross_zone_signal_client_by_group_id(uint32 group_id, int signal) { - quest_manager.CrossZoneSignalPlayerByGroupID(group_id, signal); -} - -void lua_cross_zone_signal_client_by_raid_id(uint32 raid_id, int signal) { - quest_manager.CrossZoneSignalPlayerByRaidID(raid_id, signal); -} - -void lua_cross_zone_signal_client_by_guild_id(uint32 guild_id, int signal) { - quest_manager.CrossZoneSignalPlayerByGuildID(guild_id, signal); -} - -void lua_cross_zone_signal_client_by_name(const char *character_name, int signal) { - quest_manager.CrossZoneSignalPlayerByName(character_name, signal); -} - -void lua_cross_zone_signal_npc_by_npctype_id(uint32 npctype_id, int signal) { - quest_manager.CrossZoneSignalNPCByNPCTypeID(npctype_id, signal); -} - -void lua_cross_zone_update_activity_by_char_id(int character_id, uint32 task_id, int activity_id) { - quest_manager.CrossZoneUpdateActivityByCharID(character_id, task_id, activity_id); -} - -void lua_cross_zone_update_activity_by_char_id(int character_id, uint32 task_id, int activity_id, int activity_count) { - quest_manager.CrossZoneUpdateActivityByCharID(character_id, task_id, activity_id, activity_count); -} - -void lua_cross_zone_update_activity_by_group_id(int group_id, uint32 task_id, int activity_id) { - quest_manager.CrossZoneUpdateActivityByGroupID(group_id, task_id, activity_id); -} - -void lua_cross_zone_update_activity_by_group_id(int group_id, uint32 task_id, int activity_id, int activity_count) { - quest_manager.CrossZoneUpdateActivityByGroupID(group_id, task_id, activity_id, activity_count); -} - -void lua_cross_zone_update_activity_by_raid_id(int raid_id, uint32 task_id, int activity_id) { - quest_manager.CrossZoneUpdateActivityByRaidID(raid_id, task_id, activity_id); -} - -void lua_cross_zone_update_activity_by_raid_id(int raid_id, uint32 task_id, int activity_id, int activity_count) { - quest_manager.CrossZoneUpdateActivityByRaidID(raid_id, task_id, activity_id, activity_count); -} - -void lua_cross_zone_update_activity_by_guild_id(int guild_id, uint32 task_id, int activity_id) { - quest_manager.CrossZoneUpdateActivityByGuildID(guild_id, task_id, activity_id); -} - -void lua_cross_zone_update_activity_by_guild_id(int guild_id, uint32 task_id, int activity_id, int activity_count) { - quest_manager.CrossZoneUpdateActivityByGuildID(guild_id, task_id, activity_id, activity_count); -} - -void lua_world_wide_assign_task(uint32 task_id) { - quest_manager.WorldWideAssignTask(task_id); -} - -void lua_world_wide_assign_task(uint32 task_id, bool enforce_level_requirement) { - quest_manager.WorldWideAssignTask(task_id, enforce_level_requirement); -} - -void lua_world_wide_assign_task(uint32 task_id, bool enforce_level_requirement, uint8 min_status) { - quest_manager.WorldWideAssignTask(task_id, enforce_level_requirement, min_status); -} - -void lua_world_wide_assign_task(uint32 task_id, bool enforce_level_requirement, uint8 min_status, uint8 max_status) { - quest_manager.WorldWideAssignTask(task_id, enforce_level_requirement, min_status, max_status); -} - -void lua_world_wide_cast_spell(uint32 spell_id) { - quest_manager.WorldWideCastSpell(spell_id); -} - -void lua_world_wide_cast_spell(uint32 spell_id, uint8 min_status) { - quest_manager.WorldWideCastSpell(spell_id, min_status); -} - -void lua_world_wide_cast_spell(uint32 spell_id, uint8 min_status, uint8 max_status) { - quest_manager.WorldWideCastSpell(spell_id, min_status, max_status); -} - -void lua_world_wide_disable_task(uint32 task_id) { - quest_manager.WorldWideDisableTask(task_id); -} - -void lua_world_wide_disable_task(uint32 task_id, uint8 min_status) { - quest_manager.WorldWideDisableTask(task_id, min_status); -} - -void lua_world_wide_disable_task(uint32 task_id, uint8 min_status, uint8 max_status) { - quest_manager.WorldWideDisableTask(task_id, min_status, max_status); -} - -void lua_world_wide_enable_task(uint32 task_id) { - quest_manager.WorldWideEnableTask(task_id); -} - -void lua_world_wide_enable_task(uint32 task_id, uint8 min_status) { - quest_manager.WorldWideEnableTask(task_id, min_status); -} - -void lua_world_wide_enable_task(uint32 task_id, uint8 min_status, uint8 max_status) { - quest_manager.WorldWideEnableTask(task_id, min_status, max_status); -} - -void lua_world_wide_fail_task(uint32 task_id) { - quest_manager.WorldWideFailTask(task_id); -} - -void lua_world_wide_fail_task(uint32 task_id, uint8 min_status) { - quest_manager.WorldWideFailTask(task_id, min_status); -} - -void lua_world_wide_fail_task(uint32 task_id, uint8 min_status, uint8 max_status) { - quest_manager.WorldWideFailTask(task_id, min_status, max_status); -} - -void lua_world_wide_marquee(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message) { - quest_manager.WorldWideMarquee(type, priority, fade_in, fade_out, duration, message); -} - -void lua_world_wide_marquee(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message, uint8 min_status) { - quest_manager.WorldWideMarquee(type, priority, fade_in, fade_out, duration, message, min_status); -} - -void lua_world_wide_marquee(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message, uint8 min_status, uint8 max_status) { - quest_manager.WorldWideMarquee(type, priority, fade_in, fade_out, duration, message, min_status, max_status); -} - -void lua_world_wide_message(uint32 type, const char *message) { - quest_manager.WorldWideMessage(type, message); -} - -void lua_world_wide_message(uint32 type, const char *message, uint8 min_status) { - quest_manager.WorldWideMessage(type, message, min_status); -} - -void lua_world_wide_message(uint32 type, const char *message, uint8 min_status, uint8 max_status) { - quest_manager.WorldWideMessage(type, message, min_status, max_status); -} - -void lua_world_wide_move(const char *zone_short_name) { - quest_manager.WorldWideMove(zone_short_name); -} - -void lua_world_wide_move(const char *zone_short_name, uint8 min_status) { - quest_manager.WorldWideMove(zone_short_name, min_status); -} - -void lua_world_wide_move(const char *zone_short_name, uint8 min_status, uint8 max_status) { - quest_manager.WorldWideMove(zone_short_name, min_status, max_status); -} - -void lua_world_wide_move_instance(uint16 instance_id) { - quest_manager.WorldWideMoveInstance(instance_id); -} - -void lua_world_wide_move_instance(uint16 instance_id, uint8 min_status) { - quest_manager.WorldWideMoveInstance(instance_id, min_status); -} - -void lua_world_wide_move_instance(uint16 instance_id, uint8 min_status, uint8 max_status) { - quest_manager.WorldWideMoveInstance(instance_id, min_status, max_status); -} - -void lua_world_wide_remove_spell(uint32 spell_id) { - quest_manager.WorldWideRemoveSpell(spell_id); -} - -void lua_world_wide_remove_spell(uint32 spell_id, uint8 min_status) { - quest_manager.WorldWideRemoveSpell(spell_id, min_status); -} - -void lua_world_wide_remove_spell(uint32 spell_id, uint8 min_status, uint8 max_status) { - quest_manager.WorldWideRemoveSpell(spell_id, min_status, max_status); -} - -void lua_world_wide_remove_task(uint32 task_id) { - quest_manager.WorldWideRemoveTask(task_id); -} - -void lua_world_wide_remove_task(uint32 task_id, uint8 min_status) { - quest_manager.WorldWideRemoveTask(task_id, min_status); -} - -void lua_world_wide_remove_task(uint32 task_id, uint8 min_status, uint8 max_status) { - quest_manager.WorldWideRemoveTask(task_id, min_status, max_status); -} - -void lua_world_wide_reset_activity(uint32 task_id, int activity_id) { - quest_manager.WorldWideResetActivity(task_id, activity_id); -} - -void lua_world_wide_reset_activity(uint32 task_id, int activity_id, uint8 min_status) { - quest_manager.WorldWideResetActivity(task_id, activity_id, min_status); -} - -void lua_world_wide_reset_activity(uint32 task_id, int activity_id, uint8 min_status, uint8 max_status) { - quest_manager.WorldWideResetActivity(task_id, activity_id, min_status, max_status); -} - -void lua_world_wide_set_entity_variable_client(const char *variable_name, const char *variable_value) { - quest_manager.WorldWideSetEntityVariableClient(variable_name, variable_value); -} - -void lua_world_wide_set_entity_variable_client(const char *variable_name, const char *variable_value, uint8 min_status) { - quest_manager.WorldWideSetEntityVariableClient(variable_name, variable_value, min_status); -} - -void lua_world_wide_set_entity_variable_client(const char *variable_name, const char *variable_value, uint8 min_status, uint8 max_status) { - quest_manager.WorldWideSetEntityVariableClient(variable_name, variable_value, min_status, max_status); -} - -void lua_world_wide_set_entity_variable_npc(const char *variable_name, const char *variable_value) { - quest_manager.WorldWideSetEntityVariableNPC(variable_name, variable_value); -} - -void lua_world_wide_signal_client(uint32 signal) { - quest_manager.WorldWideSignalClient(signal); -} - -void lua_world_wide_signal_client(uint32 signal, uint8 min_status) { - quest_manager.WorldWideSignalClient(signal, min_status); -} - -void lua_world_wide_signal_client(uint32 signal, uint8 min_status, uint8 max_status) { - quest_manager.WorldWideSignalClient(signal, min_status, max_status); -} - -void lua_world_wide_signal_npc(uint32 signal) { - quest_manager.WorldWideSignalNPC(signal); -} - -void lua_world_wide_update_activity(uint32 task_id, int activity_id) { - quest_manager.WorldWideUpdateActivity(task_id, activity_id); -} - -void lua_world_wide_update_activity(uint32 task_id, int activity_id, int activity_count) { - quest_manager.WorldWideUpdateActivity(task_id, activity_id, activity_count); -} - -void lua_world_wide_update_activity(uint32 task_id, int activity_id, int activity_count, uint8 min_status) { - quest_manager.WorldWideUpdateActivity(task_id, activity_id, activity_count, min_status); -} - -void lua_world_wide_update_activity(uint32 task_id, int activity_id, int activity_count, uint8 min_status, uint8 max_status) { - quest_manager.WorldWideUpdateActivity(task_id, activity_id, activity_count, min_status, max_status); -} - luabind::adl::object lua_get_qglobals(lua_State *L, Lua_NPC npc, Lua_Client client) { luabind::adl::object ret = luabind::newtable(L); @@ -2365,96 +1889,6 @@ void lua_add_ldon_win(uint32 theme_id) { quest_manager.addldonwin(theme_id); } -void lua_cross_zone_add_ldon_loss_by_char_id(int character_id, uint32 theme_id) { - uint8 update_type = CZLDoNUpdateType_Character; - uint8 update_subtype = CZLDoNUpdateSubtype_Loss; - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id); -} - -void lua_cross_zone_add_ldon_points_by_char_id(int character_id, uint32 theme_id, int points) { - uint8 update_type = CZLDoNUpdateType_Character; - uint8 update_subtype = CZLDoNUpdateSubtype_Points; - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id, points); -} - -void lua_cross_zone_add_ldon_win_by_char_id(int character_id, uint32 theme_id) { - uint8 update_type = CZLDoNUpdateType_Character; - uint8 update_subtype = CZLDoNUpdateSubtype_Win; - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id); -} - -void lua_cross_zone_add_ldon_loss_by_group_id(int group_id, uint32 theme_id) { - uint8 update_type = CZLDoNUpdateType_Group; - uint8 update_subtype = CZLDoNUpdateSubtype_Loss; - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id); -} - -void lua_cross_zone_add_ldon_points_by_group_id(int group_id, uint32 theme_id, int points) { - uint8 update_type = CZLDoNUpdateType_Group; - uint8 update_subtype = CZLDoNUpdateSubtype_Points; - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id, points); -} - -void lua_cross_zone_add_ldon_win_by_group_id(int group_id, uint32 theme_id) { - uint8 update_type = CZLDoNUpdateType_Group; - uint8 update_subtype = CZLDoNUpdateSubtype_Win; - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id); -} - -void lua_cross_zone_add_ldon_loss_by_raid_id(int raid_id, uint32 theme_id) { - uint8 update_type = CZLDoNUpdateType_Raid; - uint8 update_subtype = CZLDoNUpdateSubtype_Loss; - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id); -} - -void lua_cross_zone_add_ldon_points_by_raid_id(int raid_id, uint32 theme_id, int points) { - uint8 update_type = CZLDoNUpdateType_Raid; - uint8 update_subtype = CZLDoNUpdateSubtype_Points; - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id, points); -} - -void lua_cross_zone_add_ldon_win_by_raid_id(int raid_id, uint32 theme_id) { - uint8 update_type = CZLDoNUpdateType_Raid; - uint8 update_subtype = CZLDoNUpdateSubtype_Win; - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id); -} - -void lua_cross_zone_add_ldon_loss_by_guild_id(int guild_id, uint32 theme_id) { - uint8 update_type = CZLDoNUpdateType_Guild; - uint8 update_subtype = CZLDoNUpdateSubtype_Loss; - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id); -} - -void lua_cross_zone_add_ldon_points_by_guild_id(int guild_id, uint32 theme_id, int points) { - uint8 update_type = CZLDoNUpdateType_Guild; - uint8 update_subtype = CZLDoNUpdateSubtype_Points; - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id, points); -} - -void lua_cross_zone_add_ldon_win_by_guild_id(int guild_id, uint32 theme_id) { - uint8 update_type = CZLDoNUpdateType_Guild; - uint8 update_subtype = CZLDoNUpdateSubtype_Win; - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id); -} - -void lua_cross_zone_add_ldon_loss_by_expedition_id(uint32 expedition_id, uint32 theme_id) { - uint8 update_type = CZLDoNUpdateType_Expedition; - uint8 update_subtype = CZLDoNUpdateSubtype_Loss; - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id); -} - -void lua_cross_zone_add_ldon_points_by_expedition_id(uint32 expedition_id, uint32 theme_id, int points) { - uint8 update_type = CZLDoNUpdateType_Expedition; - uint8 update_subtype = CZLDoNUpdateSubtype_Points; - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id, points); -} - -void lua_cross_zone_add_ldon_win_by_expedition_id(uint32 expedition_id, uint32 theme_id) { - uint8 update_type = CZLDoNUpdateType_Expedition; - uint8 update_subtype = CZLDoNUpdateSubtype_Win; - quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id); -} - std::string lua_get_clean_npc_name_by_id(uint32 npc_id) { return quest_manager.getcleannpcnamebyid(npc_id); } @@ -2491,6 +1925,1202 @@ int lua_get_spell_stat(uint32 spell_id, std::string stat_identifier, uint8 slot) return quest_manager.getspellstat(spell_id, stat_identifier, slot); } +void lua_cross_zone_add_ldon_loss_by_char_id(int character_id, uint32 theme_id) { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id); +} + +void lua_cross_zone_add_ldon_loss_by_group_id(int group_id, uint32 theme_id) { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id); +} + +void lua_cross_zone_add_ldon_loss_by_raid_id(int raid_id, uint32 theme_id) { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id); +} + +void lua_cross_zone_add_ldon_loss_by_guild_id(int guild_id, uint32 theme_id) { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id); +} + +void lua_cross_zone_add_ldon_loss_by_expedition_id(uint32 expedition_id, uint32 theme_id) { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id); +} + +void lua_cross_zone_add_ldon_loss_by_client_name(const char* client_name, uint32 theme_id) { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + int update_identifier = 0; + int points = 1; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, update_identifier, theme_id, points, client_name); +} + +void lua_cross_zone_add_ldon_points_by_char_id(int character_id, uint32 theme_id, int points) { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZLDoNUpdateSubtype_Points; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id, points); +} + +void lua_cross_zone_add_ldon_points_by_group_id(int group_id, uint32 theme_id, int points) { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZLDoNUpdateSubtype_Points; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id, points); +} + +void lua_cross_zone_add_ldon_points_by_raid_id(int raid_id, uint32 theme_id, int points) { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZLDoNUpdateSubtype_Points; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id, points); +} + +void lua_cross_zone_add_ldon_points_by_guild_id(int guild_id, uint32 theme_id, int points) { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZLDoNUpdateSubtype_Points; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id, points); +} + +void lua_cross_zone_add_ldon_points_by_expedition_id(uint32 expedition_id, uint32 theme_id, int points) { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZLDoNUpdateSubtype_Points; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id, points); +} + +void lua_cross_zone_add_ldon_points_by_client_name(const char* client_name, uint32 theme_id, int points) { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZLDoNUpdateSubtype_Points; + int update_identifier = 0; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, update_identifier, theme_id, points, client_name); +} + +void lua_cross_zone_add_ldon_win_by_char_id(int character_id, uint32 theme_id) { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZLDoNUpdateSubtype_Win; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id); +} + +void lua_cross_zone_add_ldon_win_by_group_id(int group_id, uint32 theme_id) { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZLDoNUpdateSubtype_Win; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id); +} + +void lua_cross_zone_add_ldon_win_by_raid_id(int raid_id, uint32 theme_id) { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZLDoNUpdateSubtype_Win; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id); +} + +void lua_cross_zone_add_ldon_win_by_guild_id(int guild_id, uint32 theme_id) { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZLDoNUpdateSubtype_Win; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id); +} + +void lua_cross_zone_add_ldon_win_by_expedition_id(uint32 expedition_id, uint32 theme_id) { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZLDoNUpdateSubtype_Win; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id); +} + +void lua_cross_zone_add_ldon_win_by_client_name(const char* client_name, uint32 theme_id) { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZLDoNUpdateSubtype_Win; + int update_identifier = 0; + int points = 1; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, update_identifier, theme_id, points, client_name); +} + +void lua_cross_zone_assign_task_by_char_id(int character_id, uint32 task_id) { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZTaskUpdateSubtype_AssignTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, character_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_assign_task_by_char_id(int character_id, uint32 task_id, bool enforce_level_requirement) { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZTaskUpdateSubtype_AssignTask; + int task_subidentifier = -1; + int update_count = 1; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, character_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_assign_task_by_group_id(int group_id, uint32 task_id) { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZTaskUpdateSubtype_AssignTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, group_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_assign_task_by_group_id(int group_id, uint32 task_id, bool enforce_level_requirement) { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZTaskUpdateSubtype_AssignTask; + int task_subidentifier = -1; + int update_count = 1; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, group_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_assign_task_by_raid_id(int raid_id, uint32 task_id) { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZTaskUpdateSubtype_AssignTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, raid_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_assign_task_by_raid_id(int raid_id, uint32 task_id, bool enforce_level_requirement) { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZTaskUpdateSubtype_AssignTask; + int task_subidentifier = -1; + int update_count = 1; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, raid_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_assign_task_by_guild_id(int guild_id, uint32 task_id) { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZTaskUpdateSubtype_AssignTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, guild_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_assign_task_by_guild_id(int guild_id, uint32 task_id, bool enforce_level_requirement) { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZTaskUpdateSubtype_AssignTask; + int task_subidentifier = -1; + int update_count = 1; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, guild_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_assign_task_by_expedition_id(uint32 expedition_id, uint32 task_id) { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZTaskUpdateSubtype_AssignTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, expedition_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_assign_task_by_expedition_id(uint32 expedition_id, uint32 task_id, bool enforce_level_requirement) { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZTaskUpdateSubtype_AssignTask; + int task_subidentifier = -1; + int update_count = 1; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, expedition_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_assign_task_by_client_name(const char* client_name, uint32 task_id) { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZTaskUpdateSubtype_AssignTask; + int update_identifier = 0; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, update_identifier, task_id, task_subidentifier, update_count, enforce_level_requirement, client_name); +} + +void lua_cross_zone_assign_task_by_client_name(const char* client_name, uint32 task_id, bool enforce_level_requirement) { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZTaskUpdateSubtype_AssignTask; + int update_identifier = 0; + int task_subidentifier = -1; + int update_count = 1; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, update_identifier, task_id, task_subidentifier, update_count, enforce_level_requirement, client_name); +} + +void lua_cross_zone_cast_spell_by_char_id(int character_id, uint32 spell_id) { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZSpellUpdateSubtype_Cast; + quest_manager.CrossZoneSpell(update_type, update_subtype, character_id, spell_id); +} + +void lua_cross_zone_cast_spell_by_group_id(int group_id, uint32 spell_id) { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZSpellUpdateSubtype_Cast; + quest_manager.CrossZoneSpell(update_type, update_subtype, group_id, spell_id); +} + +void lua_cross_zone_cast_spell_by_raid_id(int raid_id, uint32 spell_id) { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZSpellUpdateSubtype_Cast; + quest_manager.CrossZoneSpell(update_type, update_subtype, raid_id, spell_id); +} + +void lua_cross_zone_cast_spell_by_guild_id(int guild_id, uint32 spell_id) { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZSpellUpdateSubtype_Cast; + quest_manager.CrossZoneSpell(update_type, update_subtype, guild_id, spell_id); +} + +void lua_cross_zone_cast_spell_by_expedition_id(uint32 expedition_id, uint32 spell_id) { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZSpellUpdateSubtype_Cast; + quest_manager.CrossZoneSpell(update_type, update_subtype, expedition_id, spell_id); +} + +void lua_cross_zone_cast_spell_by_client_name(const char* client_name, uint32 spell_id) { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZSpellUpdateSubtype_Cast; + int update_identifier = 0; + quest_manager.CrossZoneSpell(update_type, update_subtype, update_identifier, spell_id, client_name); +} + +void lua_cross_zone_disable_task_by_char_id(int character_id, uint32 task_id) { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZTaskUpdateSubtype_DisableTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, character_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_disable_task_by_group_id(int group_id, uint32 task_id) { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZTaskUpdateSubtype_DisableTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, group_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_disable_task_by_raid_id(int raid_id, uint32 task_id) { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZTaskUpdateSubtype_DisableTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, raid_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_disable_task_by_guild_id(int guild_id, uint32 task_id) { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZTaskUpdateSubtype_DisableTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, guild_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_disable_task_by_expedition_id(uint32 expedition_id, uint32 task_id) { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZTaskUpdateSubtype_DisableTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, expedition_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_disable_task_by_client_name(const char* client_name, uint32 task_id) { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZTaskUpdateSubtype_DisableTask; + int update_identifier = 0; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, update_identifier, task_id, task_subidentifier, update_count, enforce_level_requirement, client_name); +} + +void lua_cross_zone_enable_task_by_char_id(int character_id, uint32 task_id) { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZTaskUpdateSubtype_EnableTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, character_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_enable_task_by_group_id(int group_id, uint32 task_id) { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZTaskUpdateSubtype_EnableTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, group_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_enable_task_by_raid_id(int raid_id, uint32 task_id) { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZTaskUpdateSubtype_EnableTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, raid_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_enable_task_by_guild_id(int guild_id, uint32 task_id) { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZTaskUpdateSubtype_EnableTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, guild_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_enable_task_by_expedition_id(uint32 expedition_id, uint32 task_id) { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZTaskUpdateSubtype_EnableTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, expedition_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_enable_task_by_client_name(const char* client_name, uint32 task_id) { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZTaskUpdateSubtype_EnableTask; + int update_identifier = 0; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, update_identifier, task_id, task_subidentifier, update_count, enforce_level_requirement, client_name); +} + +void lua_cross_zone_fail_task_by_char_id(int character_id, uint32 task_id) { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZTaskUpdateSubtype_FailTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, character_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_fail_task_by_group_id(int group_id, uint32 task_id) { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZTaskUpdateSubtype_FailTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, group_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_fail_task_by_raid_id(int raid_id, uint32 task_id) { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZTaskUpdateSubtype_FailTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, raid_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_fail_task_by_guild_id(int guild_id, uint32 task_id) { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZTaskUpdateSubtype_FailTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, guild_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_fail_task_by_expedition_id(uint32 expedition_id, uint32 task_id) { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZTaskUpdateSubtype_FailTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, expedition_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_fail_task_by_client_name(const char* client_name, uint32 task_id) { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZTaskUpdateSubtype_FailTask; + int update_identifier = 0; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, update_identifier, task_id, task_subidentifier, update_count, enforce_level_requirement, client_name); +} + +void lua_cross_zone_marquee_by_char_id(int character_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char* message) { + uint8 update_type = CZUpdateType_Character; + quest_manager.CrossZoneMarquee(update_type, character_id, type, priority, fade_in, fade_out, duration, message); +} + +void lua_cross_zone_marquee_by_group_id(int group_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char* message) { + uint8 update_type = CZUpdateType_Group; + quest_manager.CrossZoneMarquee(update_type, group_id, type, priority, fade_in, fade_out, duration, message); +} + +void lua_cross_zone_marquee_by_raid_id(int raid_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char* message) { + uint8 update_type = CZUpdateType_Raid; + quest_manager.CrossZoneMarquee(update_type, raid_id, type, priority, fade_in, fade_out, duration, message); +} + +void lua_cross_zone_marquee_by_guild_id(int guild_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char* message) { + uint8 update_type = CZUpdateType_Guild; + quest_manager.CrossZoneMarquee(update_type, guild_id, type, priority, fade_in, fade_out, duration, message); +} + +void lua_cross_zone_marquee_by_expedition_id(uint32 expedition_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char* message) { + uint8 update_type = CZUpdateType_Expedition; + quest_manager.CrossZoneMarquee(update_type, expedition_id, type, priority, fade_in, fade_out, duration, message); +} + +void lua_cross_zone_marquee_by_client_name(const char* client_name, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char* message) { + uint8 update_type = CZUpdateType_ClientName; + int update_identifier = 0; + quest_manager.CrossZoneMarquee(update_type, update_identifier, type, priority, fade_in, fade_out, duration, message, client_name); +} + +void lua_cross_zone_message_player_by_char_id(uint32 type, int character_id, const char* message) { + uint8 update_type = CZUpdateType_Character; + quest_manager.CrossZoneMessage(update_type, character_id, type, message); +} + +void lua_cross_zone_message_player_by_group_id(uint32 type, int group_id, const char* message) { + uint8 update_type = CZUpdateType_Group; + quest_manager.CrossZoneMessage(update_type, group_id, type, message); +} + +void lua_cross_zone_message_player_by_raid_id(uint32 type, int raid_id, const char* message) { + uint8 update_type = CZUpdateType_Raid; + quest_manager.CrossZoneMessage(update_type, raid_id, type, message); +} + +void lua_cross_zone_message_player_by_guild_id(uint32 type, int guild_id, const char* message) { + uint8 update_type = CZUpdateType_Guild; + quest_manager.CrossZoneMessage(update_type, guild_id, type, message); +} + +void lua_cross_zone_message_player_by_expedition_id(uint32 type, int expedition_id, const char* message) { + uint8 update_type = CZUpdateType_Expedition; + quest_manager.CrossZoneMessage(update_type, expedition_id, type, message); +} + +void lua_cross_zone_message_player_by_name(uint32 type, const char* client_name, const char* message) { + uint8 update_type = CZUpdateType_ClientName; + int update_identifier = 0; + quest_manager.CrossZoneMessage(update_type, update_identifier, type, message, client_name); +} + +void lua_cross_zone_move_player_by_char_id(int character_id, const char* zone_short_name) { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZMoveUpdateSubtype_MoveZone; + uint16 instance_id = 0; + quest_manager.CrossZoneMove(update_type, update_subtype, character_id, zone_short_name, instance_id); +} + +void lua_cross_zone_move_player_by_group_id(int group_id, const char* zone_short_name) { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZMoveUpdateSubtype_MoveZone; + uint16 instance_id = 0; + quest_manager.CrossZoneMove(update_type, update_subtype, group_id, zone_short_name, instance_id); +} + +void lua_cross_zone_move_player_by_raid_id(int raid_id, const char* zone_short_name) { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZMoveUpdateSubtype_MoveZone; + uint16 instance_id = 0; + quest_manager.CrossZoneMove(update_type, update_subtype, raid_id, zone_short_name, instance_id); +} + +void lua_cross_zone_move_player_by_guild_id(int guild_id, const char* zone_short_name) { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZMoveUpdateSubtype_MoveZone; + uint16 instance_id = 0; + quest_manager.CrossZoneMove(update_type, update_subtype, guild_id, zone_short_name, instance_id); +} + +void lua_cross_zone_move_player_by_expedition_id(int expedition_id, const char* zone_short_name) { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZMoveUpdateSubtype_MoveZone; + uint16 instance_id = 0; + quest_manager.CrossZoneMove(update_type, update_subtype, expedition_id, zone_short_name, instance_id); +} + +void lua_cross_zone_move_player_by_client_name(const char* client_name, const char* zone_short_name) { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZMoveUpdateSubtype_MoveZone; + int update_identifier = 0; + uint16 instance_id = 0; + quest_manager.CrossZoneMove(update_type, update_subtype, update_identifier, zone_short_name, instance_id, client_name); +} + +void lua_cross_zone_move_instance_by_char_id(int character_id, uint16 instance_id) { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZMoveUpdateSubtype_MoveZoneInstance; + const char* zone_short_name = ""; + quest_manager.CrossZoneMove(update_type, update_subtype, character_id, zone_short_name, instance_id); +} + +void lua_cross_zone_move_instance_by_group_id(int group_id, uint16 instance_id) { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZMoveUpdateSubtype_MoveZoneInstance; + const char* zone_short_name = ""; + quest_manager.CrossZoneMove(update_type, update_subtype, group_id, zone_short_name, instance_id); +} + +void lua_cross_zone_move_instance_by_raid_id(int raid_id, uint16 instance_id) { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZMoveUpdateSubtype_MoveZoneInstance; + const char* zone_short_name = ""; + quest_manager.CrossZoneMove(update_type, update_subtype, raid_id, zone_short_name, instance_id); +} + +void lua_cross_zone_move_instance_by_guild_id(int guild_id, uint16 instance_id) { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZMoveUpdateSubtype_MoveZoneInstance; + const char* zone_short_name = ""; + quest_manager.CrossZoneMove(update_type, update_subtype, guild_id, zone_short_name, instance_id); +} + +void lua_cross_zone_move_instance_by_expedition_id(uint32 expedition_id, uint16 instance_id) { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZMoveUpdateSubtype_MoveZoneInstance; + const char* zone_short_name = ""; + quest_manager.CrossZoneMove(update_type, update_subtype, expedition_id, zone_short_name, instance_id); +} + +void lua_cross_zone_move_instance_by_client_name(const char* client_name, uint16 instance_id) { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZMoveUpdateSubtype_MoveZoneInstance; + int update_identifier = 0; + const char* zone_short_name = ""; + quest_manager.CrossZoneMove(update_type, update_subtype, update_identifier, zone_short_name, instance_id, client_name); +} + +void lua_cross_zone_remove_spell_by_char_id(int character_id, uint32 spell_id) { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZSpellUpdateSubtype_Remove; + quest_manager.CrossZoneSpell(update_type, update_subtype, character_id, spell_id); +} + +void lua_cross_zone_remove_spell_by_group_id(int group_id, uint32 spell_id) { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZSpellUpdateSubtype_Remove; + quest_manager.CrossZoneSpell(update_type, update_subtype, group_id, spell_id); +} + +void lua_cross_zone_remove_spell_by_raid_id(int raid_id, uint32 spell_id) { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZSpellUpdateSubtype_Remove; + quest_manager.CrossZoneSpell(update_type, update_subtype, raid_id, spell_id); +} + +void lua_cross_zone_remove_spell_by_guild_id(int guild_id, uint32 spell_id) { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZSpellUpdateSubtype_Remove; + quest_manager.CrossZoneSpell(update_type, update_subtype, guild_id, spell_id); +} + +void lua_cross_zone_remove_spell_by_expedition_id(uint32 expedition_id, uint32 spell_id) { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZSpellUpdateSubtype_Remove; + quest_manager.CrossZoneSpell(update_type, update_subtype, expedition_id, spell_id); +} + +void lua_cross_zone_remove_spell_by_client_name(const char* client_name, uint32 spell_id) { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZSpellUpdateSubtype_Remove; + int update_identifier = 0; + quest_manager.CrossZoneSpell(update_type, update_subtype, update_identifier, spell_id, client_name); +} + +void lua_cross_zone_remove_task_by_char_id(int character_id, uint32 task_id) { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZTaskUpdateSubtype_RemoveTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, character_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_remove_task_by_group_id(int group_id, uint32 task_id) { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZTaskUpdateSubtype_RemoveTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, group_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_remove_task_by_raid_id(int raid_id, uint32 task_id) { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZTaskUpdateSubtype_RemoveTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, raid_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_remove_task_by_guild_id(int guild_id, uint32 task_id) { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZTaskUpdateSubtype_RemoveTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, guild_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_remove_task_by_expedition_id(uint32 expedition_id, uint32 task_id) { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZTaskUpdateSubtype_RemoveTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, expedition_id, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_cross_zone_remove_task_by_client_name(const char* client_name, uint32 task_id) { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZTaskUpdateSubtype_RemoveTask; + int update_identifier = 0; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, update_identifier, task_id, task_subidentifier, update_count, enforce_level_requirement, client_name); +} + +void lua_cross_zone_reset_activity_by_char_id(int character_id, uint32 task_id, int activity_id) { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityReset; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, character_id, task_id, activity_id, update_count, enforce_level_requirement); +} + +void lua_cross_zone_reset_activity_by_group_id(int group_id, uint32 task_id, int activity_id) { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityReset; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, group_id, task_id, activity_id, update_count, enforce_level_requirement); +} + +void lua_cross_zone_reset_activity_by_raid_id(int raid_id, uint32 task_id, int activity_id) { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityReset; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, raid_id, task_id, activity_id, update_count, enforce_level_requirement); +} + +void lua_cross_zone_reset_activity_by_guild_id(int guild_id, uint32 task_id, int activity_id) { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityReset; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, guild_id, task_id, activity_id, update_count, enforce_level_requirement); +} + +void lua_cross_zone_reset_activity_by_expedition_id(uint32 expedition_id, uint32 task_id, int activity_id) { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityReset; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, expedition_id, task_id, activity_id, update_count, enforce_level_requirement); +} + +void lua_cross_zone_reset_activity_by_client_name(const char* client_name, uint32 task_id, int activity_id) { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityReset; + int update_identifier = 0; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, update_identifier, task_id, activity_id, update_count, enforce_level_requirement, client_name); +} + +void lua_cross_zone_set_entity_variable_by_char_id(int character_id, const char* variable_name, const char* variable_value) { + uint8 update_type = CZUpdateType_Character; + quest_manager.CrossZoneSetEntityVariable(update_type, character_id, variable_name, variable_value); +} + +void lua_cross_zone_set_entity_variable_by_group_id(int group_id, const char* variable_name, const char* variable_value) { + uint8 update_type = CZUpdateType_Group; + quest_manager.CrossZoneSetEntityVariable(update_type, group_id, variable_name, variable_value); +} + +void lua_cross_zone_set_entity_variable_by_raid_id(int raid_id, const char* variable_name, const char* variable_value) { + uint8 update_type = CZUpdateType_Raid; + quest_manager.CrossZoneSetEntityVariable(update_type, raid_id, variable_name, variable_value); +} + +void lua_cross_zone_set_entity_variable_by_guild_id(int guild_id, const char* variable_name, const char* variable_value) { + uint8 update_type = CZUpdateType_Guild; + quest_manager.CrossZoneSetEntityVariable(update_type, guild_id, variable_name, variable_value); +} + +void lua_cross_zone_set_entity_variable_by_expedition_id(uint32 expedition_id, const char* variable_name, const char* variable_value) { + uint8 update_type = CZUpdateType_Expedition; + quest_manager.CrossZoneSetEntityVariable(update_type, expedition_id, variable_name, variable_value); +} + +void lua_cross_zone_set_entity_variable_by_client_name(const char* character_name, const char* variable_name, const char* variable_value) { + uint8 update_type = CZUpdateType_ClientName; + int update_identifier = 0; + quest_manager.CrossZoneSetEntityVariable(update_type, update_identifier, variable_name, variable_value, character_name); +} + +void lua_cross_zone_signal_client_by_char_id(uint32 character_id, int signal) { + uint8 update_type = CZUpdateType_Character; + quest_manager.CrossZoneSignal(update_type, character_id, signal); +} + +void lua_cross_zone_signal_client_by_group_id(uint32 group_id, int signal) { + uint8 update_type = CZUpdateType_Group; + quest_manager.CrossZoneSignal(update_type, group_id, signal); +} + +void lua_cross_zone_signal_client_by_raid_id(uint32 raid_id, int signal) { + uint8 update_type = CZUpdateType_Raid; + quest_manager.CrossZoneSignal(update_type, raid_id, signal); +} + +void lua_cross_zone_signal_client_by_guild_id(uint32 guild_id, int signal) { + uint8 update_type = CZUpdateType_Guild; + quest_manager.CrossZoneSignal(update_type, guild_id, signal); +} + +void lua_cross_zone_signal_client_by_expedition_id(uint32 expedition_id, int signal) { + uint8 update_type = CZUpdateType_Expedition; + quest_manager.CrossZoneSignal(update_type, expedition_id, signal); +} + +void lua_cross_zone_signal_client_by_name(const char* client_name, int signal) { + uint8 update_type = CZUpdateType_ClientName; + int update_identifier = 0; + quest_manager.CrossZoneSignal(update_type, update_identifier, signal, client_name); +} + +void lua_cross_zone_signal_npc_by_npctype_id(uint32 npctype_id, int signal) { + uint8 update_type = CZUpdateType_NPC; + quest_manager.CrossZoneSignal(update_type, npctype_id, signal); +} + +void lua_cross_zone_update_activity_by_char_id(int character_id, uint32 task_id, int activity_id) { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityUpdate; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, character_id, task_id, activity_id, update_count, enforce_level_requirement); +} + +void lua_cross_zone_update_activity_by_char_id(int character_id, uint32 task_id, int activity_id, int activity_count) { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityUpdate; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, character_id, task_id, activity_id, activity_count, enforce_level_requirement); +} + +void lua_cross_zone_update_activity_by_group_id(int group_id, uint32 task_id, int activity_id) { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityUpdate; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, group_id, task_id, activity_id, update_count, enforce_level_requirement); +} + +void lua_cross_zone_update_activity_by_group_id(int group_id, uint32 task_id, int activity_id, int activity_count) { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityUpdate; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, group_id, task_id, activity_id, activity_count, enforce_level_requirement); +} + +void lua_cross_zone_update_activity_by_raid_id(int raid_id, uint32 task_id, int activity_id) { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityUpdate; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, raid_id, task_id, activity_id, update_count, enforce_level_requirement); +} + +void lua_cross_zone_update_activity_by_raid_id(int raid_id, uint32 task_id, int activity_id, int activity_count) { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityUpdate; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, raid_id, task_id, activity_id, activity_count, enforce_level_requirement); +} + +void lua_cross_zone_update_activity_by_guild_id(int guild_id, uint32 task_id, int activity_id) { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityUpdate; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, guild_id, task_id, activity_id, update_count, enforce_level_requirement); +} + +void lua_cross_zone_update_activity_by_guild_id(int guild_id, uint32 task_id, int activity_id, int activity_count) { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityUpdate; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, guild_id, task_id, activity_id, activity_count, enforce_level_requirement); +} + +void lua_cross_zone_update_activity_by_expedition_id(uint32 expedition_id, uint32 task_id, int activity_id) { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityUpdate; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, expedition_id, task_id, activity_id, update_count, enforce_level_requirement); +} + +void lua_cross_zone_update_activity_by_expedition_id(uint32 expedition_id, uint32 task_id, int activity_id, int activity_count) { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityUpdate; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, expedition_id, task_id, activity_id, activity_count, enforce_level_requirement); +} + +void lua_cross_zone_update_activity_by_client_name(const char* client_name, uint32 task_id, int activity_id) { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityUpdate; + int update_identifier = 0; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, update_identifier, task_id, activity_id, update_count, enforce_level_requirement, client_name); +} + +void lua_cross_zone_update_activity_by_client_name(const char* client_name, uint32 task_id, int activity_id, int activity_count) { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZTaskUpdateSubtype_ActivityUpdate; + int update_identifier = 0; + bool enforce_level_requirement = false; + quest_manager.CrossZoneTaskUpdate(update_type, update_subtype, update_identifier, task_id, activity_id, activity_count, enforce_level_requirement, client_name); +} + +void lua_world_wide_add_ldon_loss(uint32 theme_id) { + uint8 update_type = WWLDoNUpdateType_Loss; + quest_manager.WorldWideLDoNUpdate(update_type, theme_id); +} + +void lua_world_wide_add_ldon_loss(uint32 theme_id, uint8 min_status) { + uint8 update_type = WWLDoNUpdateType_Loss; + int points = 1; + quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status); +} + +void lua_world_wide_add_ldon_loss(uint32 theme_id, uint8 min_status, uint8 max_status) { + uint8 update_type = WWLDoNUpdateType_Loss; + int points = 1; + quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status, max_status); +} + +void lua_world_wide_add_ldon_points(uint32 theme_id, int points) { + uint8 update_type = WWLDoNUpdateType_Points; + quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points); +} + +void lua_world_wide_add_ldon_points(uint32 theme_id, int points, uint8 min_status) { + uint8 update_type = WWLDoNUpdateType_Points; + quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status); +} + +void lua_world_wide_add_ldon_points(uint32 theme_id, int points, uint8 min_status, uint8 max_status) { + uint8 update_type = WWLDoNUpdateType_Points; + quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status, max_status); +} + +void lua_world_wide_add_ldon_win(uint32 theme_id) { + uint8 update_type = WWLDoNUpdateType_Win; + quest_manager.WorldWideLDoNUpdate(update_type, theme_id); +} + +void lua_world_wide_add_ldon_win(uint32 theme_id, uint8 min_status) { + uint8 update_type = WWLDoNUpdateType_Win; + int points = 1; + quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status); +} + +void lua_world_wide_add_ldon_win(uint32 theme_id, uint8 min_status, uint8 max_status) { + uint8 update_type = WWLDoNUpdateType_Win; + int points = 1; + quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status, max_status); +} + +void lua_world_wide_assign_task(uint32 task_id) { + uint8 update_type = WWTaskUpdateType_AssignTask; + quest_manager.WorldWideTaskUpdate(update_type, task_id); +} + +void lua_world_wide_assign_task(uint32 task_id, bool enforce_level_requirement) { + uint8 update_type = WWTaskUpdateType_AssignTask; + int task_subidentifier = -1; + int update_count = 1; + quest_manager.WorldWideTaskUpdate(update_type, task_id, task_subidentifier, update_count, enforce_level_requirement); +} + +void lua_world_wide_assign_task(uint32 task_id, bool enforce_level_requirement, uint8 min_status) { + uint8 update_type = WWTaskUpdateType_AssignTask; + int task_subidentifier = -1; + int update_count = 1; + quest_manager.WorldWideTaskUpdate(update_type, task_id, task_subidentifier, update_count, enforce_level_requirement, min_status); +} + +void lua_world_wide_assign_task(uint32 task_id, bool enforce_level_requirement, uint8 min_status, uint8 max_status) { + uint8 update_type = WWTaskUpdateType_AssignTask; + int task_subidentifier = -1; + int update_count = 1; + quest_manager.WorldWideTaskUpdate(update_type, task_id, task_subidentifier, update_count, enforce_level_requirement, min_status, max_status); +} + +void lua_world_wide_cast_spell(uint32 spell_id) { + uint8 update_type = WWSpellUpdateType_Cast; + quest_manager.WorldWideSpell(update_type, spell_id); +} + +void lua_world_wide_cast_spell(uint32 spell_id, uint8 min_status) { + uint8 update_type = WWSpellUpdateType_Cast; + quest_manager.WorldWideSpell(update_type, spell_id, min_status); +} + +void lua_world_wide_cast_spell(uint32 spell_id, uint8 min_status, uint8 max_status) { + uint8 update_type = WWSpellUpdateType_Cast; + quest_manager.WorldWideSpell(update_type, spell_id, min_status, max_status); +} + +void lua_world_wide_disable_task(uint32 task_id) { + uint8 update_type = WWTaskUpdateType_DisableTask; + quest_manager.WorldWideTaskUpdate(update_type, task_id); +} + +void lua_world_wide_disable_task(uint32 task_id, uint8 min_status) { + uint8 update_type = WWTaskUpdateType_DisableTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.WorldWideTaskUpdate(update_type, task_id, task_subidentifier, update_count, enforce_level_requirement, min_status); +} + +void lua_world_wide_disable_task(uint32 task_id, uint8 min_status, uint8 max_status) { + uint8 update_type = WWTaskUpdateType_DisableTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.WorldWideTaskUpdate(update_type, task_id, task_subidentifier, update_count, enforce_level_requirement, min_status, max_status); +} + +void lua_world_wide_enable_task(uint32 task_id) { + uint8 update_type = WWTaskUpdateType_EnableTask; + quest_manager.WorldWideTaskUpdate(update_type, task_id); +} + +void lua_world_wide_enable_task(uint32 task_id, uint8 min_status) { + uint8 update_type = WWTaskUpdateType_EnableTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.WorldWideTaskUpdate(update_type, task_id, task_subidentifier, update_count, enforce_level_requirement, min_status); +} + +void lua_world_wide_enable_task(uint32 task_id, uint8 min_status, uint8 max_status) { + uint8 update_type = WWTaskUpdateType_EnableTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.WorldWideTaskUpdate(update_type, task_id, task_subidentifier, update_count, enforce_level_requirement, min_status, max_status); +} + +void lua_world_wide_fail_task(uint32 task_id) { + uint8 update_type = WWTaskUpdateType_FailTask; + quest_manager.WorldWideTaskUpdate(update_type, task_id); +} + +void lua_world_wide_fail_task(uint32 task_id, uint8 min_status) { + uint8 update_type = WWTaskUpdateType_FailTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.WorldWideTaskUpdate(update_type, task_id, task_subidentifier, update_count, enforce_level_requirement, min_status); +} + +void lua_world_wide_fail_task(uint32 task_id, uint8 min_status, uint8 max_status) { + uint8 update_type = WWTaskUpdateType_FailTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.WorldWideTaskUpdate(update_type, task_id, task_subidentifier, update_count, enforce_level_requirement, min_status, max_status); +} + +void lua_world_wide_marquee(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char* message) { + quest_manager.WorldWideMarquee(type, priority, fade_in, fade_out, duration, message); +} + +void lua_world_wide_marquee(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char* message, uint8 min_status) { + quest_manager.WorldWideMarquee(type, priority, fade_in, fade_out, duration, message, min_status); +} + +void lua_world_wide_marquee(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char* message, uint8 min_status, uint8 max_status) { + quest_manager.WorldWideMarquee(type, priority, fade_in, fade_out, duration, message, min_status, max_status); +} + +void lua_world_wide_message(uint32 type, const char* message) { + quest_manager.WorldWideMessage(type, message); +} + +void lua_world_wide_message(uint32 type, const char* message, uint8 min_status) { + quest_manager.WorldWideMessage(type, message, min_status); +} + +void lua_world_wide_message(uint32 type, const char* message, uint8 min_status, uint8 max_status) { + quest_manager.WorldWideMessage(type, message, min_status, max_status); +} + +void lua_world_wide_move(const char* zone_short_name) { + uint8 update_type = WWMoveUpdateType_MoveZone; + quest_manager.WorldWideMove(update_type, zone_short_name); +} + +void lua_world_wide_move(const char* zone_short_name, uint8 min_status) { + uint8 update_type = WWMoveUpdateType_MoveZone; + uint16 instance_id = 0; + quest_manager.WorldWideMove(update_type, zone_short_name, instance_id, min_status); +} + +void lua_world_wide_move(const char* zone_short_name, uint8 min_status, uint8 max_status) { + uint8 update_type = WWMoveUpdateType_MoveZone; + uint16 instance_id = 0; + quest_manager.WorldWideMove(update_type, zone_short_name, instance_id, min_status, max_status); +} + +void lua_world_wide_move_instance(uint16 instance_id) { + uint8 update_type = WWMoveUpdateType_MoveZoneInstance; + const char* zone_short_name = ""; + quest_manager.WorldWideMove(update_type, zone_short_name, instance_id); +} + +void lua_world_wide_move_instance(uint16 instance_id, uint8 min_status) { + uint8 update_type = WWMoveUpdateType_MoveZoneInstance; + const char* zone_short_name = ""; + quest_manager.WorldWideMove(update_type, zone_short_name, instance_id, min_status); +} + +void lua_world_wide_move_instance(uint16 instance_id, uint8 min_status, uint8 max_status) { + uint8 update_type = WWMoveUpdateType_MoveZoneInstance; + const char* zone_short_name = ""; + quest_manager.WorldWideMove(update_type, zone_short_name, instance_id, min_status, max_status); +} + +void lua_world_wide_remove_spell(uint32 spell_id) { + uint8 update_type = WWSpellUpdateType_Remove; + quest_manager.WorldWideSpell(update_type, spell_id); +} + +void lua_world_wide_remove_spell(uint32 spell_id, uint8 min_status) { + uint8 update_type = WWSpellUpdateType_Remove; + quest_manager.WorldWideSpell(update_type, spell_id, min_status); +} + +void lua_world_wide_remove_spell(uint32 spell_id, uint8 min_status, uint8 max_status) { + uint8 update_type = WWSpellUpdateType_Remove; + quest_manager.WorldWideSpell(update_type, spell_id, min_status, max_status); +} + +void lua_world_wide_remove_task(uint32 task_id) { + uint8 update_type = WWTaskUpdateType_RemoveTask; + quest_manager.WorldWideTaskUpdate(update_type, task_id); +} + +void lua_world_wide_remove_task(uint32 task_id, uint8 min_status) { + uint8 update_type = WWTaskUpdateType_RemoveTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.WorldWideTaskUpdate(update_type, task_id, task_subidentifier, update_count, enforce_level_requirement, min_status); +} + +void lua_world_wide_remove_task(uint32 task_id, uint8 min_status, uint8 max_status) { + uint8 update_type = WWTaskUpdateType_RemoveTask; + int task_subidentifier = -1; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.WorldWideTaskUpdate(update_type, task_id, task_subidentifier, update_count, enforce_level_requirement, min_status, max_status); +} + +void lua_world_wide_reset_activity(uint32 task_id, int activity_id) { + uint8 update_type = WWTaskUpdateType_ActivityReset; + quest_manager.WorldWideTaskUpdate(update_type, task_id, activity_id); +} + +void lua_world_wide_reset_activity(uint32 task_id, int activity_id, uint8 min_status) { + uint8 update_type = WWTaskUpdateType_ActivityReset; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.WorldWideTaskUpdate(update_type, task_id, activity_id, update_count, enforce_level_requirement, min_status); +} + +void lua_world_wide_reset_activity(uint32 task_id, int activity_id, uint8 min_status, uint8 max_status) { + uint8 update_type = WWTaskUpdateType_ActivityReset; + int update_count = 1; + bool enforce_level_requirement = false; + quest_manager.WorldWideTaskUpdate(update_type, task_id, activity_id, update_count, enforce_level_requirement, min_status, max_status); +} + +void lua_world_wide_set_entity_variable_client(const char* variable_name, const char* variable_value) { + uint8 update_type = WWSetEntityVariableUpdateType_Character; + quest_manager.WorldWideSetEntityVariable(update_type, variable_name, variable_value); +} + +void lua_world_wide_set_entity_variable_client(const char* variable_name, const char* variable_value, uint8 min_status) { + uint8 update_type = WWSetEntityVariableUpdateType_Character; + quest_manager.WorldWideSetEntityVariable(update_type, variable_name, variable_value, min_status); +} + +void lua_world_wide_set_entity_variable_client(const char* variable_name, const char* variable_value, uint8 min_status, uint8 max_status) { + uint8 update_type = WWSetEntityVariableUpdateType_Character; + quest_manager.WorldWideSetEntityVariable(update_type, variable_name, variable_value, min_status, max_status); +} + +void lua_world_wide_set_entity_variable_npc(const char* variable_name, const char* variable_value) { + uint8 update_type = WWSetEntityVariableUpdateType_NPC; + quest_manager.WorldWideSetEntityVariable(update_type, variable_name, variable_value); +} + +void lua_world_wide_signal_client(uint32 signal) { + uint8 update_type = WWSignalUpdateType_Character; + quest_manager.WorldWideSignal(update_type, signal); +} + +void lua_world_wide_signal_client(uint32 signal, uint8 min_status) { + uint8 update_type = WWSignalUpdateType_Character; + quest_manager.WorldWideSignal(update_type, signal, min_status); +} + +void lua_world_wide_signal_client(uint32 signal, uint8 min_status, uint8 max_status) { + uint8 update_type = WWSignalUpdateType_Character; + quest_manager.WorldWideSignal(update_type, signal, min_status, max_status); +} + +void lua_world_wide_signal_npc(uint32 signal) { + uint8 update_type = WWSignalUpdateType_NPC; + quest_manager.WorldWideSignal(update_type, signal); +} + +void lua_world_wide_update_activity(uint32 task_id, int activity_id) { + uint8 update_type = WWTaskUpdateType_ActivityUpdate; + quest_manager.WorldWideTaskUpdate(update_type, task_id, activity_id); +} + +void lua_world_wide_update_activity(uint32 task_id, int activity_id, int activity_count) { + uint8 update_type = WWTaskUpdateType_ActivityUpdate; + quest_manager.WorldWideTaskUpdate(update_type, task_id, activity_id, activity_count); +} + +void lua_world_wide_update_activity(uint32 task_id, int activity_id, int activity_count, uint8 min_status) { + uint8 update_type = WWTaskUpdateType_ActivityUpdate; + bool enforce_level_requirement = false; + quest_manager.WorldWideTaskUpdate(update_type, task_id, activity_id, activity_count, enforce_level_requirement, min_status); +} + +void lua_world_wide_update_activity(uint32 task_id, int activity_id, int activity_count, uint8 min_status, uint8 max_status) { + uint8 update_type = WWTaskUpdateType_ActivityUpdate; + bool enforce_level_requirement = false; + quest_manager.WorldWideTaskUpdate(update_type, task_id, activity_id, activity_count, enforce_level_requirement, min_status, max_status); +} + #define LuaCreateNPCParse(name, c_type, default_value) do { \ cur = table[#name]; \ if(luabind::type(cur) != LUA_TNIL) { \ @@ -2866,125 +3496,6 @@ luabind::scope lua_register_general() { luabind::def("wear_change", &lua_wear_change), luabind::def("voice_tell", &lua_voice_tell), luabind::def("send_mail", &lua_send_mail), - luabind::def("cross_zone_assign_task_by_char_id", (void(*)(int,uint32))&lua_cross_zone_assign_task_by_char_id), - luabind::def("cross_zone_assign_task_by_char_id", (void(*)(int,uint32,bool))&lua_cross_zone_assign_task_by_char_id), - luabind::def("cross_zone_assign_task_by_group_id", (void(*)(int,uint32))&lua_cross_zone_assign_task_by_group_id), - luabind::def("cross_zone_assign_task_by_group_id", (void(*)(int,uint32,bool))&lua_cross_zone_assign_task_by_group_id), - luabind::def("cross_zone_assign_task_by_raid_id", (void(*)(int,uint32))&lua_cross_zone_assign_task_by_raid_id), - luabind::def("cross_zone_assign_task_by_raid_id", (void(*)(int,uint32,bool))&lua_cross_zone_assign_task_by_raid_id), - luabind::def("cross_zone_assign_task_by_guild_id", (void(*)(int,uint32))&lua_cross_zone_assign_task_by_guild_id), - luabind::def("cross_zone_assign_task_by_guild_id", (void(*)(int,uint32,bool))&lua_cross_zone_assign_task_by_guild_id), - luabind::def("cross_zone_cast_spell_by_char_id", &lua_cross_zone_cast_spell_by_char_id), - luabind::def("cross_zone_cast_spell_by_group_id", &lua_cross_zone_cast_spell_by_group_id), - luabind::def("cross_zone_cast_spell_by_raid_id", &lua_cross_zone_cast_spell_by_raid_id), - luabind::def("cross_zone_cast_spell_by_guild_id", &lua_cross_zone_cast_spell_by_guild_id), - luabind::def("cross_zone_disable_task_by_char_id", &lua_cross_zone_disable_task_by_char_id), - luabind::def("cross_zone_disable_task_by_group_id", &lua_cross_zone_disable_task_by_group_id), - luabind::def("cross_zone_disable_task_by_raid_id", &lua_cross_zone_disable_task_by_raid_id), - luabind::def("cross_zone_disable_task_by_guild_id", &lua_cross_zone_disable_task_by_guild_id), - luabind::def("cross_zone_enable_task_by_char_id", &lua_cross_zone_enable_task_by_char_id), - luabind::def("cross_zone_enable_task_by_group_id", &lua_cross_zone_enable_task_by_group_id), - luabind::def("cross_zone_enable_task_by_raid_id", &lua_cross_zone_enable_task_by_raid_id), - luabind::def("cross_zone_enable_task_by_guild_id", &lua_cross_zone_enable_task_by_guild_id), - luabind::def("cross_zone_fail_task_by_char_id", &lua_cross_zone_fail_task_by_char_id), - luabind::def("cross_zone_fail_task_by_group_id", &lua_cross_zone_fail_task_by_group_id), - luabind::def("cross_zone_fail_task_by_raid_id", &lua_cross_zone_fail_task_by_raid_id), - luabind::def("cross_zone_fail_task_by_guild_id", &lua_cross_zone_fail_task_by_guild_id), - luabind::def("cross_zone_marquee_by_char_id", &lua_cross_zone_marquee_by_char_id), - luabind::def("cross_zone_marquee_by_group_id", &lua_cross_zone_marquee_by_group_id), - luabind::def("cross_zone_marquee_by_raid_id", &lua_cross_zone_marquee_by_raid_id), - luabind::def("cross_zone_marquee_by_guild_id", &lua_cross_zone_marquee_by_guild_id), - luabind::def("cross_zone_message_player_by_name", &lua_cross_zone_message_player_by_name), - luabind::def("cross_zone_message_player_by_group_id", &lua_cross_zone_message_player_by_group_id), - luabind::def("cross_zone_message_player_by_raid_id", &lua_cross_zone_message_player_by_raid_id), - luabind::def("cross_zone_message_player_by_guild_id", &lua_cross_zone_message_player_by_guild_id), - luabind::def("cross_zone_move_player_by_char_id", &lua_cross_zone_move_player_by_char_id), - luabind::def("cross_zone_move_player_by_group_id", &lua_cross_zone_move_player_by_group_id), - luabind::def("cross_zone_move_player_by_raid_id", &lua_cross_zone_move_player_by_raid_id), - luabind::def("cross_zone_move_player_by_guild_id", &lua_cross_zone_move_player_by_guild_id), - luabind::def("cross_zone_move_instance_by_char_id", &lua_cross_zone_move_instance_by_char_id), - luabind::def("cross_zone_move_instance_by_group_id", &lua_cross_zone_move_instance_by_group_id), - luabind::def("cross_zone_move_instance_by_raid_id", &lua_cross_zone_move_instance_by_raid_id), - luabind::def("cross_zone_move_instance_by_guild_id", &lua_cross_zone_move_instance_by_guild_id), - luabind::def("cross_zone_remove_spell_by_char_id", &lua_cross_zone_remove_spell_by_char_id), - luabind::def("cross_zone_remove_spell_by_group_id", &lua_cross_zone_remove_spell_by_group_id), - luabind::def("cross_zone_remove_spell_by_raid_id", &lua_cross_zone_remove_spell_by_raid_id), - luabind::def("cross_zone_remove_spell_by_guild_id", &lua_cross_zone_remove_spell_by_guild_id), - luabind::def("cross_zone_remove_task_by_char_id", &lua_cross_zone_remove_task_by_char_id), - luabind::def("cross_zone_remove_task_by_group_id", &lua_cross_zone_remove_task_by_group_id), - luabind::def("cross_zone_remove_task_by_raid_id", &lua_cross_zone_remove_task_by_raid_id), - luabind::def("cross_zone_remove_task_by_guild_id", &lua_cross_zone_remove_task_by_guild_id), - luabind::def("cross_zone_reset_activity_by_char_id", &lua_cross_zone_reset_activity_by_char_id), - luabind::def("cross_zone_reset_activity_by_group_id", &lua_cross_zone_reset_activity_by_group_id), - luabind::def("cross_zone_reset_activity_by_raid_id", &lua_cross_zone_reset_activity_by_raid_id), - luabind::def("cross_zone_reset_activity_by_guild_id", &lua_cross_zone_reset_activity_by_guild_id), - luabind::def("cross_zone_set_entity_variable_by_client_name", &lua_cross_zone_set_entity_variable_by_client_name), - luabind::def("cross_zone_set_entity_variable_by_group_id", &lua_cross_zone_set_entity_variable_by_group_id), - luabind::def("cross_zone_set_entity_variable_by_raid_id", &lua_cross_zone_set_entity_variable_by_raid_id), - luabind::def("cross_zone_set_entity_variable_by_guild_id", &lua_cross_zone_set_entity_variable_by_guild_id), - luabind::def("cross_zone_signal_client_by_char_id", &lua_cross_zone_signal_client_by_char_id), - luabind::def("cross_zone_signal_client_by_group_id", &lua_cross_zone_signal_client_by_group_id), - luabind::def("cross_zone_signal_client_by_raid_id", &lua_cross_zone_signal_client_by_raid_id), - luabind::def("cross_zone_signal_client_by_guild_id", &lua_cross_zone_signal_client_by_guild_id), - luabind::def("cross_zone_signal_client_by_name", &lua_cross_zone_signal_client_by_name), - luabind::def("cross_zone_signal_npc_by_npctype_id", &lua_cross_zone_signal_npc_by_npctype_id), - luabind::def("cross_zone_update_activity_by_char_id", (void(*)(int,uint32,int))&lua_cross_zone_update_activity_by_char_id), - luabind::def("cross_zone_update_activity_by_char_id", (void(*)(int,uint32,int,int))&lua_cross_zone_update_activity_by_char_id), - luabind::def("cross_zone_update_activity_by_group_id", (void(*)(int,uint32,int))&lua_cross_zone_update_activity_by_group_id), - luabind::def("cross_zone_update_activity_by_group_id", (void(*)(int,uint32,int,int))&lua_cross_zone_update_activity_by_group_id), - luabind::def("cross_zone_update_activity_by_raid_id", (void(*)(int,uint32,int))&lua_cross_zone_update_activity_by_raid_id), - luabind::def("cross_zone_update_activity_by_raid_id", (void(*)(int,uint32,int,int))&lua_cross_zone_update_activity_by_raid_id), - luabind::def("cross_zone_update_activity_by_guild_id", (void(*)(int,uint32,int))&lua_cross_zone_update_activity_by_guild_id), - luabind::def("cross_zone_update_activity_by_guild_id", (void(*)(int,uint32,int,int))&lua_cross_zone_update_activity_by_guild_id), - luabind::def("world_wide_assign_task", (void(*)(uint32))&lua_world_wide_assign_task), - luabind::def("world_wide_assign_task", (void(*)(uint32,bool))&lua_world_wide_assign_task), - luabind::def("world_wide_assign_task", (void(*)(uint32,bool,uint8))&lua_world_wide_assign_task), - luabind::def("world_wide_assign_task", (void(*)(uint32,bool,uint8,uint8))&lua_world_wide_assign_task), - luabind::def("world_wide_cast_spell", (void(*)(uint32))&lua_world_wide_cast_spell), - luabind::def("world_wide_cast_spell", (void(*)(uint32,uint8))&lua_world_wide_cast_spell), - luabind::def("world_wide_cast_spell", (void(*)(uint32,uint8,uint8))&lua_world_wide_cast_spell), - luabind::def("world_wide_disable_task", (void(*)(uint32))&lua_world_wide_disable_task), - luabind::def("world_wide_disable_task", (void(*)(uint32,uint8))&lua_world_wide_disable_task), - luabind::def("world_wide_disable_task", (void(*)(uint32,uint8,uint8))&lua_world_wide_disable_task), - luabind::def("world_wide_enable_task", (void(*)(uint32))&lua_world_wide_enable_task), - luabind::def("world_wide_enable_task", (void(*)(uint32,uint8))&lua_world_wide_enable_task), - luabind::def("world_wide_enable_task", (void(*)(uint32,uint8,uint8))&lua_world_wide_enable_task), - luabind::def("world_wide_fail_task", (void(*)(uint32))&lua_world_wide_fail_task), - luabind::def("world_wide_fail_task", (void(*)(uint32,uint8))&lua_world_wide_fail_task), - luabind::def("world_wide_fail_task", (void(*)(uint32,uint8,uint8))&lua_world_wide_fail_task), - luabind::def("world_wide_marquee", (void(*)(uint32,uint32,uint32,uint32,uint32,const char*))&lua_world_wide_marquee), - luabind::def("world_wide_marquee", (void(*)(uint32,uint32,uint32,uint32,uint32,const char*,uint8))&lua_world_wide_marquee), - luabind::def("world_wide_marquee", (void(*)(uint32,uint32,uint32,uint32,uint32,const char*,uint8,uint8))&lua_world_wide_marquee), - luabind::def("world_wide_message", (void(*)(uint32,const char*))&lua_world_wide_message), - luabind::def("world_wide_message", (void(*)(uint32,const char*,uint8))&lua_world_wide_message), - luabind::def("world_wide_message", (void(*)(uint32,const char*,uint8,uint8))&lua_world_wide_message), - luabind::def("world_wide_move", (void(*)(const char*))&lua_world_wide_move), - luabind::def("world_wide_move", (void(*)(const char*,uint8))&lua_world_wide_move), - luabind::def("world_wide_move", (void(*)(const char*,uint8,uint8))&lua_world_wide_move), - luabind::def("world_wide_move_instance", (void(*)(uint16))&lua_world_wide_move_instance), - luabind::def("world_wide_move_instance", (void(*)(uint16,uint8))&lua_world_wide_move_instance), - luabind::def("world_wide_move_instance", (void(*)(uint16,uint8,uint8))&lua_world_wide_move_instance), - luabind::def("world_wide_remove_spell", (void(*)(uint32))&lua_world_wide_remove_spell), - luabind::def("world_wide_remove_spell", (void(*)(uint32,uint8))&lua_world_wide_remove_spell), - luabind::def("world_wide_remove_spell", (void(*)(uint32,uint8,uint8))&lua_world_wide_remove_spell), - luabind::def("world_wide_remove_task", (void(*)(uint32))&lua_world_wide_remove_task), - luabind::def("world_wide_remove_task", (void(*)(uint32,uint8))&lua_world_wide_remove_task), - luabind::def("world_wide_remove_task", (void(*)(uint32,uint8,uint8))&lua_world_wide_remove_task), - luabind::def("world_wide_reset_activity", (void(*)(uint32,int))&lua_world_wide_reset_activity), - luabind::def("world_wide_reset_activity", (void(*)(uint32,int,uint8))&lua_world_wide_reset_activity), - luabind::def("world_wide_reset_activity", (void(*)(uint32,int,uint8,uint8))&lua_world_wide_reset_activity), - luabind::def("world_wide_set_entity_variable_client", (void(*)(const char*,const char*))&lua_world_wide_set_entity_variable_client), - luabind::def("world_wide_set_entity_variable_client", (void(*)(const char*,const char*,uint8))&lua_world_wide_set_entity_variable_client), - luabind::def("world_wide_set_entity_variable_client", (void(*)(const char*,const char*,uint8,uint8))&lua_world_wide_set_entity_variable_client), - luabind::def("world_wide_set_entity_variable_npc", &lua_world_wide_set_entity_variable_npc), - luabind::def("world_wide_signal_client", (void(*)(uint32))&lua_world_wide_signal_client), - luabind::def("world_wide_signal_client", (void(*)(uint32,uint8))&lua_world_wide_signal_client), - luabind::def("world_wide_signal_client", (void(*)(uint32,uint8,uint8))&lua_world_wide_signal_client), - luabind::def("world_wide_signal_npc", &lua_world_wide_signal_npc), - luabind::def("world_wide_update_activity", (void(*)(uint32,int))&lua_world_wide_update_activity), - luabind::def("world_wide_update_activity", (void(*)(uint32,int,int))&lua_world_wide_update_activity), - luabind::def("world_wide_update_activity", (void(*)(uint32,int,int,uint8))&lua_world_wide_update_activity), - luabind::def("world_wide_update_activity", (void(*)(uint32,int,int,uint8,uint8))&lua_world_wide_update_activity), luabind::def("get_qglobals", (luabind::adl::object(*)(lua_State*,Lua_NPC,Lua_Client))&lua_get_qglobals), luabind::def("get_qglobals", (luabind::adl::object(*)(lua_State*,Lua_Client))&lua_get_qglobals), luabind::def("get_qglobals", (luabind::adl::object(*)(lua_State*,Lua_NPC))&lua_get_qglobals), @@ -3041,21 +3552,6 @@ luabind::scope lua_register_general() { luabind::def("add_ldon_loss", &lua_add_ldon_loss), luabind::def("add_ldon_points", &lua_add_ldon_points), luabind::def("add_ldon_win", &lua_add_ldon_win), - luabind::def("cross_zone_add_ldon_loss_by_char_id", &lua_cross_zone_add_ldon_loss_by_char_id), - luabind::def("cross_zone_add_ldon_points_by_char_id", &lua_cross_zone_add_ldon_points_by_char_id), - luabind::def("cross_zone_add_ldon_win_by_char_id", &lua_cross_zone_add_ldon_win_by_char_id), - luabind::def("cross_zone_add_ldon_loss_by_group_id", &lua_cross_zone_add_ldon_loss_by_group_id), - luabind::def("cross_zone_add_ldon_points_by_group_id", &lua_cross_zone_add_ldon_points_by_group_id), - luabind::def("cross_zone_add_ldon_win_by_group_id", &lua_cross_zone_add_ldon_win_by_group_id), - luabind::def("cross_zone_add_ldon_loss_by_raid_id", &lua_cross_zone_add_ldon_loss_by_raid_id), - luabind::def("cross_zone_add_ldon_points_by_raid_id", &lua_cross_zone_add_ldon_points_by_raid_id), - luabind::def("cross_zone_add_ldon_win_by_raid_id", &lua_cross_zone_add_ldon_win_by_raid_id), - luabind::def("cross_zone_add_ldon_loss_by_guild_id", &lua_cross_zone_add_ldon_loss_by_guild_id), - luabind::def("cross_zone_add_ldon_points_by_guild_id", &lua_cross_zone_add_ldon_points_by_guild_id), - luabind::def("cross_zone_add_ldon_win_by_guild_id", &lua_cross_zone_add_ldon_win_by_guild_id), - luabind::def("cross_zone_add_ldon_loss_by_expedition_id", &lua_cross_zone_add_ldon_loss_by_expedition_id), - luabind::def("cross_zone_add_ldon_points_by_expedition_id", &lua_cross_zone_add_ldon_points_by_expedition_id), - luabind::def("cross_zone_add_ldon_win_by_expedition_id", &lua_cross_zone_add_ldon_win_by_expedition_id), luabind::def("get_gender_name", &lua_get_gender_name), luabind::def("get_deity_name", &lua_get_deity_name), luabind::def("get_inventory_slot_name", &lua_get_inventory_slot_name), @@ -3065,6 +3561,193 @@ luabind::scope lua_register_general() { luabind::def("get_spell_stat", (int(*)(uint32,std::string))&lua_get_spell_stat), luabind::def("get_spell_stat", (int(*)(uint32,std::string,uint8))&lua_get_spell_stat), + /* + Cross Zone + */ + luabind::def("cross_zone_add_ldon_loss_by_char_id", &lua_cross_zone_add_ldon_loss_by_char_id), + luabind::def("cross_zone_add_ldon_loss_by_group_id", &lua_cross_zone_add_ldon_loss_by_group_id), + luabind::def("cross_zone_add_ldon_loss_by_raid_id", &lua_cross_zone_add_ldon_loss_by_raid_id), + luabind::def("cross_zone_add_ldon_loss_by_guild_id", &lua_cross_zone_add_ldon_loss_by_guild_id), + luabind::def("cross_zone_add_ldon_loss_by_expedition_id", &lua_cross_zone_add_ldon_loss_by_expedition_id), + luabind::def("cross_zone_add_ldon_loss_by_client_name", &lua_cross_zone_add_ldon_loss_by_client_name), + luabind::def("cross_zone_add_ldon_points_by_char_id", &lua_cross_zone_add_ldon_points_by_char_id), + luabind::def("cross_zone_add_ldon_points_by_group_id", &lua_cross_zone_add_ldon_points_by_group_id), + luabind::def("cross_zone_add_ldon_points_by_raid_id", &lua_cross_zone_add_ldon_points_by_raid_id), + luabind::def("cross_zone_add_ldon_points_by_guild_id", &lua_cross_zone_add_ldon_points_by_guild_id), + luabind::def("cross_zone_add_ldon_points_by_expedition_id", &lua_cross_zone_add_ldon_points_by_expedition_id), + luabind::def("cross_zone_add_ldon_points_by_client_name", &lua_cross_zone_add_ldon_points_by_client_name), + luabind::def("cross_zone_add_ldon_win_by_char_id", &lua_cross_zone_add_ldon_win_by_char_id), + luabind::def("cross_zone_add_ldon_win_by_group_id", &lua_cross_zone_add_ldon_win_by_group_id), + luabind::def("cross_zone_add_ldon_win_by_raid_id", &lua_cross_zone_add_ldon_win_by_raid_id), + luabind::def("cross_zone_add_ldon_win_by_guild_id", &lua_cross_zone_add_ldon_win_by_guild_id), + luabind::def("cross_zone_add_ldon_win_by_expedition_id", &lua_cross_zone_add_ldon_win_by_expedition_id), + luabind::def("cross_zone_add_ldon_win_by_client_name", &lua_cross_zone_add_ldon_win_by_client_name), + luabind::def("cross_zone_assign_task_by_char_id", (void(*)(int,uint32))&lua_cross_zone_assign_task_by_char_id), + luabind::def("cross_zone_assign_task_by_char_id", (void(*)(int,uint32,bool))&lua_cross_zone_assign_task_by_char_id), + luabind::def("cross_zone_assign_task_by_group_id", (void(*)(int,uint32))&lua_cross_zone_assign_task_by_group_id), + luabind::def("cross_zone_assign_task_by_group_id", (void(*)(int,uint32,bool))&lua_cross_zone_assign_task_by_group_id), + luabind::def("cross_zone_assign_task_by_raid_id", (void(*)(int,uint32))&lua_cross_zone_assign_task_by_raid_id), + luabind::def("cross_zone_assign_task_by_raid_id", (void(*)(int,uint32,bool))&lua_cross_zone_assign_task_by_raid_id), + luabind::def("cross_zone_assign_task_by_guild_id", (void(*)(int,uint32))&lua_cross_zone_assign_task_by_guild_id), + luabind::def("cross_zone_assign_task_by_guild_id", (void(*)(int,uint32,bool))&lua_cross_zone_assign_task_by_guild_id), + luabind::def("cross_zone_assign_task_by_expedition_id", (void(*)(uint32,uint32))&lua_cross_zone_assign_task_by_expedition_id), + luabind::def("cross_zone_assign_task_by_expedition_id", (void(*)(uint32,uint32,bool))&lua_cross_zone_assign_task_by_expedition_id), + luabind::def("cross_zone_assign_task_by_client_name", (void(*)(const char*,uint32))&lua_cross_zone_assign_task_by_client_name), + luabind::def("cross_zone_assign_task_by_client_name", (void(*)(const char*,uint32,bool))&lua_cross_zone_assign_task_by_client_name), + luabind::def("cross_zone_cast_spell_by_char_id", &lua_cross_zone_cast_spell_by_char_id), + luabind::def("cross_zone_cast_spell_by_group_id", &lua_cross_zone_cast_spell_by_group_id), + luabind::def("cross_zone_cast_spell_by_raid_id", &lua_cross_zone_cast_spell_by_raid_id), + luabind::def("cross_zone_cast_spell_by_guild_id", &lua_cross_zone_cast_spell_by_guild_id), + luabind::def("cross_zone_cast_spell_by_expedition_id", &lua_cross_zone_cast_spell_by_expedition_id), + luabind::def("cross_zone_cast_spell_by_client_name", &lua_cross_zone_cast_spell_by_client_name), + luabind::def("cross_zone_disable_task_by_char_id", &lua_cross_zone_disable_task_by_char_id), + luabind::def("cross_zone_disable_task_by_group_id", &lua_cross_zone_disable_task_by_group_id), + luabind::def("cross_zone_disable_task_by_raid_id", &lua_cross_zone_disable_task_by_raid_id), + luabind::def("cross_zone_disable_task_by_guild_id", &lua_cross_zone_disable_task_by_guild_id), + luabind::def("cross_zone_disable_task_by_expedition_id", &lua_cross_zone_disable_task_by_expedition_id), + luabind::def("cross_zone_disable_task_by_client_name", &lua_cross_zone_disable_task_by_client_name), + luabind::def("cross_zone_enable_task_by_char_id", &lua_cross_zone_enable_task_by_char_id), + luabind::def("cross_zone_enable_task_by_group_id", &lua_cross_zone_enable_task_by_group_id), + luabind::def("cross_zone_enable_task_by_raid_id", &lua_cross_zone_enable_task_by_raid_id), + luabind::def("cross_zone_enable_task_by_guild_id", &lua_cross_zone_enable_task_by_guild_id), + luabind::def("cross_zone_enable_task_by_expedition_id", &lua_cross_zone_enable_task_by_expedition_id), + luabind::def("cross_zone_enable_task_by_client_name", &lua_cross_zone_enable_task_by_client_name), + luabind::def("cross_zone_fail_task_by_char_id", &lua_cross_zone_fail_task_by_char_id), + luabind::def("cross_zone_fail_task_by_group_id", &lua_cross_zone_fail_task_by_group_id), + luabind::def("cross_zone_fail_task_by_raid_id", &lua_cross_zone_fail_task_by_raid_id), + luabind::def("cross_zone_fail_task_by_guild_id", &lua_cross_zone_fail_task_by_guild_id), + luabind::def("cross_zone_fail_task_by_expedition_id", &lua_cross_zone_fail_task_by_expedition_id), + luabind::def("cross_zone_fail_task_by_client_name", &lua_cross_zone_fail_task_by_client_name), + luabind::def("cross_zone_marquee_by_char_id", &lua_cross_zone_marquee_by_char_id), + luabind::def("cross_zone_marquee_by_group_id", &lua_cross_zone_marquee_by_group_id), + luabind::def("cross_zone_marquee_by_raid_id", &lua_cross_zone_marquee_by_raid_id), + luabind::def("cross_zone_marquee_by_guild_id", &lua_cross_zone_marquee_by_guild_id), + luabind::def("cross_zone_marquee_by_expedition_id", &lua_cross_zone_marquee_by_expedition_id), + luabind::def("cross_zone_marquee_by_client_name", &lua_cross_zone_marquee_by_client_name), + luabind::def("cross_zone_message_player_by_char_id", &lua_cross_zone_message_player_by_char_id), + luabind::def("cross_zone_message_player_by_group_id", &lua_cross_zone_message_player_by_group_id), + luabind::def("cross_zone_message_player_by_raid_id", &lua_cross_zone_message_player_by_raid_id), + luabind::def("cross_zone_message_player_by_guild_id", &lua_cross_zone_message_player_by_guild_id), + luabind::def("cross_zone_message_player_by_expedition_id", &lua_cross_zone_message_player_by_expedition_id), + luabind::def("cross_zone_message_player_by_name", &lua_cross_zone_message_player_by_name), + luabind::def("cross_zone_move_player_by_char_id", &lua_cross_zone_move_player_by_char_id), + luabind::def("cross_zone_move_player_by_group_id", &lua_cross_zone_move_player_by_group_id), + luabind::def("cross_zone_move_player_by_raid_id", &lua_cross_zone_move_player_by_raid_id), + luabind::def("cross_zone_move_player_by_guild_id", &lua_cross_zone_move_player_by_guild_id), + luabind::def("cross_zone_move_player_by_expedition_id", &lua_cross_zone_move_player_by_expedition_id), + luabind::def("cross_zone_move_player_by_client_name", &lua_cross_zone_move_player_by_client_name), + luabind::def("cross_zone_move_instance_by_char_id", &lua_cross_zone_move_instance_by_char_id), + luabind::def("cross_zone_move_instance_by_group_id", &lua_cross_zone_move_instance_by_group_id), + luabind::def("cross_zone_move_instance_by_raid_id", &lua_cross_zone_move_instance_by_raid_id), + luabind::def("cross_zone_move_instance_by_guild_id", &lua_cross_zone_move_instance_by_guild_id), + luabind::def("cross_zone_move_instance_by_expedition_id", &lua_cross_zone_move_instance_by_expedition_id), + luabind::def("cross_zone_move_instance_by_client_name", &lua_cross_zone_move_instance_by_client_name), + luabind::def("cross_zone_remove_spell_by_char_id", &lua_cross_zone_remove_spell_by_char_id), + luabind::def("cross_zone_remove_spell_by_group_id", &lua_cross_zone_remove_spell_by_group_id), + luabind::def("cross_zone_remove_spell_by_raid_id", &lua_cross_zone_remove_spell_by_raid_id), + luabind::def("cross_zone_remove_spell_by_guild_id", &lua_cross_zone_remove_spell_by_guild_id), + luabind::def("cross_zone_remove_spell_by_expedition_id", &lua_cross_zone_remove_spell_by_expedition_id), + luabind::def("cross_zone_remove_spell_by_client_name", &lua_cross_zone_remove_spell_by_client_name), + luabind::def("cross_zone_remove_task_by_char_id", &lua_cross_zone_remove_task_by_char_id), + luabind::def("cross_zone_remove_task_by_group_id", &lua_cross_zone_remove_task_by_group_id), + luabind::def("cross_zone_remove_task_by_raid_id", &lua_cross_zone_remove_task_by_raid_id), + luabind::def("cross_zone_remove_task_by_guild_id", &lua_cross_zone_remove_task_by_guild_id), + luabind::def("cross_zone_remove_task_by_expedition_id", &lua_cross_zone_remove_task_by_expedition_id), + luabind::def("cross_zone_remove_task_by_client_name", &lua_cross_zone_remove_task_by_client_name), + luabind::def("cross_zone_reset_activity_by_char_id", &lua_cross_zone_reset_activity_by_char_id), + luabind::def("cross_zone_reset_activity_by_group_id", &lua_cross_zone_reset_activity_by_group_id), + luabind::def("cross_zone_reset_activity_by_raid_id", &lua_cross_zone_reset_activity_by_raid_id), + luabind::def("cross_zone_reset_activity_by_guild_id", &lua_cross_zone_reset_activity_by_guild_id), + luabind::def("cross_zone_reset_activity_by_expedition_id", &lua_cross_zone_reset_activity_by_expedition_id), + luabind::def("cross_zone_reset_activity_by_client_name", &lua_cross_zone_reset_activity_by_client_name), + luabind::def("cross_zone_set_entity_variable_by_client_name", &lua_cross_zone_set_entity_variable_by_client_name), + luabind::def("cross_zone_set_entity_variable_by_group_id", &lua_cross_zone_set_entity_variable_by_group_id), + luabind::def("cross_zone_set_entity_variable_by_raid_id", &lua_cross_zone_set_entity_variable_by_raid_id), + luabind::def("cross_zone_set_entity_variable_by_guild_id", &lua_cross_zone_set_entity_variable_by_guild_id), + luabind::def("cross_zone_set_entity_variable_by_expedition_id", &lua_cross_zone_set_entity_variable_by_expedition_id), + luabind::def("cross_zone_set_entity_variable_by_client_name", &lua_cross_zone_set_entity_variable_by_client_name), + luabind::def("cross_zone_signal_client_by_char_id", &lua_cross_zone_signal_client_by_char_id), + luabind::def("cross_zone_signal_client_by_group_id", &lua_cross_zone_signal_client_by_group_id), + luabind::def("cross_zone_signal_client_by_raid_id", &lua_cross_zone_signal_client_by_raid_id), + luabind::def("cross_zone_signal_client_by_guild_id", &lua_cross_zone_signal_client_by_guild_id), + luabind::def("cross_zone_signal_client_by_expedition_id", &lua_cross_zone_signal_client_by_expedition_id), + luabind::def("cross_zone_signal_client_by_name", &lua_cross_zone_signal_client_by_name), + luabind::def("cross_zone_signal_npc_by_npctype_id", &lua_cross_zone_signal_npc_by_npctype_id), + luabind::def("cross_zone_update_activity_by_char_id", (void(*)(int,uint32,int))&lua_cross_zone_update_activity_by_char_id), + luabind::def("cross_zone_update_activity_by_char_id", (void(*)(int,uint32,int,int))&lua_cross_zone_update_activity_by_char_id), + luabind::def("cross_zone_update_activity_by_group_id", (void(*)(int,uint32,int))&lua_cross_zone_update_activity_by_group_id), + luabind::def("cross_zone_update_activity_by_group_id", (void(*)(int,uint32,int,int))&lua_cross_zone_update_activity_by_group_id), + luabind::def("cross_zone_update_activity_by_raid_id", (void(*)(int,uint32,int))&lua_cross_zone_update_activity_by_raid_id), + luabind::def("cross_zone_update_activity_by_raid_id", (void(*)(int,uint32,int,int))&lua_cross_zone_update_activity_by_raid_id), + luabind::def("cross_zone_update_activity_by_guild_id", (void(*)(int,uint32,int))&lua_cross_zone_update_activity_by_guild_id), + luabind::def("cross_zone_update_activity_by_guild_id", (void(*)(int,uint32,int,int))&lua_cross_zone_update_activity_by_guild_id), + luabind::def("cross_zone_update_activity_by_expedition_id", (void(*)(uint32,uint32,int))&lua_cross_zone_update_activity_by_expedition_id), + luabind::def("cross_zone_update_activity_by_expedition_id", (void(*)(uint32,uint32,int,int))&lua_cross_zone_update_activity_by_expedition_id), + luabind::def("cross_zone_update_activity_by_client_name", (void(*)(const char*,uint32,int))&lua_cross_zone_update_activity_by_client_name), + luabind::def("cross_zone_update_activity_by_client_name", (void(*)(const char*,uint32,int,int))&lua_cross_zone_update_activity_by_client_name), + + /* + World Wide + */ + luabind::def("world_wide_add_ldon_loss", (void(*)(uint32))&lua_world_wide_add_ldon_loss), + luabind::def("world_wide_add_ldon_loss", (void(*)(uint32,uint8))&lua_world_wide_add_ldon_loss), + luabind::def("world_wide_add_ldon_loss", (void(*)(uint32,uint8,uint8))&lua_world_wide_add_ldon_loss), + luabind::def("world_wide_add_ldon_points", (void(*)(uint32,int))&lua_world_wide_add_ldon_points), + luabind::def("world_wide_add_ldon_points", (void(*)(uint32,int,uint8))&lua_world_wide_add_ldon_points), + luabind::def("world_wide_add_ldon_points", (void(*)(uint32,int,uint8,uint8))&lua_world_wide_add_ldon_points), + luabind::def("world_wide_add_ldon_loss", (void(*)(uint32))&lua_world_wide_add_ldon_win), + luabind::def("world_wide_add_ldon_loss", (void(*)(uint32,uint8))&lua_world_wide_add_ldon_win), + luabind::def("world_wide_add_ldon_loss", (void(*)(uint32,uint8,uint8))&lua_world_wide_add_ldon_win), + luabind::def("world_wide_assign_task", (void(*)(uint32))&lua_world_wide_assign_task), + luabind::def("world_wide_assign_task", (void(*)(uint32,bool))&lua_world_wide_assign_task), + luabind::def("world_wide_assign_task", (void(*)(uint32,bool,uint8))&lua_world_wide_assign_task), + luabind::def("world_wide_assign_task", (void(*)(uint32,bool,uint8,uint8))&lua_world_wide_assign_task), + luabind::def("world_wide_cast_spell", (void(*)(uint32))&lua_world_wide_cast_spell), + luabind::def("world_wide_cast_spell", (void(*)(uint32,uint8))&lua_world_wide_cast_spell), + luabind::def("world_wide_cast_spell", (void(*)(uint32,uint8,uint8))&lua_world_wide_cast_spell), + luabind::def("world_wide_disable_task", (void(*)(uint32))&lua_world_wide_disable_task), + luabind::def("world_wide_disable_task", (void(*)(uint32,uint8))&lua_world_wide_disable_task), + luabind::def("world_wide_disable_task", (void(*)(uint32,uint8,uint8))&lua_world_wide_disable_task), + luabind::def("world_wide_enable_task", (void(*)(uint32))&lua_world_wide_enable_task), + luabind::def("world_wide_enable_task", (void(*)(uint32,uint8))&lua_world_wide_enable_task), + luabind::def("world_wide_enable_task", (void(*)(uint32,uint8,uint8))&lua_world_wide_enable_task), + luabind::def("world_wide_fail_task", (void(*)(uint32))&lua_world_wide_fail_task), + luabind::def("world_wide_fail_task", (void(*)(uint32,uint8))&lua_world_wide_fail_task), + luabind::def("world_wide_fail_task", (void(*)(uint32,uint8,uint8))&lua_world_wide_fail_task), + luabind::def("world_wide_marquee", (void(*)(uint32,uint32,uint32,uint32,uint32,const char*))&lua_world_wide_marquee), + luabind::def("world_wide_marquee", (void(*)(uint32,uint32,uint32,uint32,uint32,const char*,uint8))&lua_world_wide_marquee), + luabind::def("world_wide_marquee", (void(*)(uint32,uint32,uint32,uint32,uint32,const char*,uint8,uint8))&lua_world_wide_marquee), + luabind::def("world_wide_message", (void(*)(uint32,const char*))&lua_world_wide_message), + luabind::def("world_wide_message", (void(*)(uint32,const char*,uint8))&lua_world_wide_message), + luabind::def("world_wide_message", (void(*)(uint32,const char*,uint8,uint8))&lua_world_wide_message), + luabind::def("world_wide_move", (void(*)(const char*))&lua_world_wide_move), + luabind::def("world_wide_move", (void(*)(const char*,uint8))&lua_world_wide_move), + luabind::def("world_wide_move", (void(*)(const char*,uint8,uint8))&lua_world_wide_move), + luabind::def("world_wide_move_instance", (void(*)(uint16))&lua_world_wide_move_instance), + luabind::def("world_wide_move_instance", (void(*)(uint16,uint8))&lua_world_wide_move_instance), + luabind::def("world_wide_move_instance", (void(*)(uint16,uint8,uint8))&lua_world_wide_move_instance), + luabind::def("world_wide_remove_spell", (void(*)(uint32))&lua_world_wide_remove_spell), + luabind::def("world_wide_remove_spell", (void(*)(uint32,uint8))&lua_world_wide_remove_spell), + luabind::def("world_wide_remove_spell", (void(*)(uint32,uint8,uint8))&lua_world_wide_remove_spell), + luabind::def("world_wide_remove_task", (void(*)(uint32))&lua_world_wide_remove_task), + luabind::def("world_wide_remove_task", (void(*)(uint32,uint8))&lua_world_wide_remove_task), + luabind::def("world_wide_remove_task", (void(*)(uint32,uint8,uint8))&lua_world_wide_remove_task), + luabind::def("world_wide_reset_activity", (void(*)(uint32,int))&lua_world_wide_reset_activity), + luabind::def("world_wide_reset_activity", (void(*)(uint32,int,uint8))&lua_world_wide_reset_activity), + luabind::def("world_wide_reset_activity", (void(*)(uint32,int,uint8,uint8))&lua_world_wide_reset_activity), + luabind::def("world_wide_set_entity_variable_client", (void(*)(const char*,const char*))&lua_world_wide_set_entity_variable_client), + luabind::def("world_wide_set_entity_variable_client", (void(*)(const char*,const char*,uint8))&lua_world_wide_set_entity_variable_client), + luabind::def("world_wide_set_entity_variable_client", (void(*)(const char*,const char*,uint8,uint8))&lua_world_wide_set_entity_variable_client), + luabind::def("world_wide_set_entity_variable_npc", &lua_world_wide_set_entity_variable_npc), + luabind::def("world_wide_signal_client", (void(*)(uint32))&lua_world_wide_signal_client), + luabind::def("world_wide_signal_client", (void(*)(uint32,uint8))&lua_world_wide_signal_client), + luabind::def("world_wide_signal_client", (void(*)(uint32,uint8,uint8))&lua_world_wide_signal_client), + luabind::def("world_wide_signal_npc", &lua_world_wide_signal_npc), + luabind::def("world_wide_update_activity", (void(*)(uint32,int))&lua_world_wide_update_activity), + luabind::def("world_wide_update_activity", (void(*)(uint32,int,int))&lua_world_wide_update_activity), + luabind::def("world_wide_update_activity", (void(*)(uint32,int,int,uint8))&lua_world_wide_update_activity), + luabind::def("world_wide_update_activity", (void(*)(uint32,int,int,uint8,uint8))&lua_world_wide_update_activity), + /** * Expansions */ diff --git a/zone/perl_raids.cpp b/zone/perl_raids.cpp index 452741be6..bc739a8d6 100644 --- a/zone/perl_raids.cpp +++ b/zone/perl_raids.cpp @@ -304,7 +304,7 @@ XS(XS_Raid_GetClientByIndex); /* prototype to pass -Wmissing-prototypes */ XS(XS_Raid_GetClientByIndex) { dXSARGS; if (items != 2) - Perl_croak(aTHX_ "Usage: Raid::GetClientByIndex(THIS, uint16 raid_indez)"); // @categories Raid + Perl_croak(aTHX_ "Usage: Raid::GetClientByIndex(THIS, uint16 raid_index)"); // @categories Raid { Raid *THIS; Client *RETVAL; diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index cac4c34a8..0bf867a87 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -3220,872 +3220,6 @@ std::string QuestManager::GetZoneShortName(uint32 zone_id) { return ZoneName(zone_id); } -void QuestManager::CrossZoneAssignTaskByCharID(int character_id, uint32 task_id, bool enforce_level_requirement) { - QuestManagerCurrentQuestVars(); - if (initiator && owner) { - auto pack = new ServerPacket(ServerOP_CZTaskAssignPlayer, sizeof(CZTaskAssignPlayer_Struct)); - CZTaskAssignPlayer_Struct* CZTA = (CZTaskAssignPlayer_Struct*) pack->pBuffer; - CZTA->npc_entity_id = owner->GetID(); - CZTA->character_id = character_id; - CZTA->task_id = task_id; - CZTA->enforce_level_requirement = enforce_level_requirement; - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - -void QuestManager::CrossZoneAssignTaskByGroupID(int group_id, uint32 task_id, bool enforce_level_requirement) { - QuestManagerCurrentQuestVars(); - if (initiator && owner) { - auto pack = new ServerPacket(ServerOP_CZTaskAssignGroup, sizeof(CZTaskAssignGroup_Struct)); - CZTaskAssignGroup_Struct* CZTA = (CZTaskAssignGroup_Struct*) pack->pBuffer; - CZTA->npc_entity_id = owner->GetID(); - CZTA->group_id = group_id; - CZTA->task_id = task_id; - CZTA->enforce_level_requirement = enforce_level_requirement; - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - -void QuestManager::CrossZoneAssignTaskByRaidID(int raid_id, uint32 task_id, bool enforce_level_requirement) { - QuestManagerCurrentQuestVars(); - if (initiator && owner) { - auto pack = new ServerPacket(ServerOP_CZTaskAssignRaid, sizeof(CZTaskAssignRaid_Struct)); - CZTaskAssignRaid_Struct* CZTA = (CZTaskAssignRaid_Struct*) pack->pBuffer; - CZTA->npc_entity_id = owner->GetID(); - CZTA->raid_id = raid_id; - CZTA->task_id = task_id; - CZTA->enforce_level_requirement = enforce_level_requirement; - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - -void QuestManager::CrossZoneAssignTaskByGuildID(int guild_id, uint32 task_id, bool enforce_level_requirement) { - QuestManagerCurrentQuestVars(); - if (initiator && owner) { - auto pack = new ServerPacket(ServerOP_CZTaskAssignGuild, sizeof(CZTaskAssignGuild_Struct)); - CZTaskAssignGuild_Struct* CZTA = (CZTaskAssignGuild_Struct*) pack->pBuffer; - CZTA->npc_entity_id = owner->GetID(); - CZTA->guild_id = guild_id; - CZTA->task_id = task_id; - CZTA->enforce_level_requirement = enforce_level_requirement; - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - -void QuestManager::CrossZoneCastSpellByCharID(int character_id, uint32 spell_id) { - auto pack = new ServerPacket(ServerOP_CZCastSpellPlayer, sizeof(CZCastSpellPlayer_Struct)); - CZCastSpellPlayer_Struct* CZCS = (CZCastSpellPlayer_Struct*) pack->pBuffer; - CZCS->character_id = character_id; - CZCS->spell_id = spell_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneCastSpellByGroupID(int group_id, uint32 spell_id) { - auto pack = new ServerPacket(ServerOP_CZCastSpellGroup, sizeof(CZCastSpellGroup_Struct)); - CZCastSpellGroup_Struct* CZCS = (CZCastSpellGroup_Struct*) pack->pBuffer; - CZCS->group_id = group_id; - CZCS->spell_id = spell_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneCastSpellByRaidID(int raid_id, uint32 spell_id) { - auto pack = new ServerPacket(ServerOP_CZCastSpellRaid, sizeof(CZCastSpellRaid_Struct)); - CZCastSpellRaid_Struct* CZCS = (CZCastSpellRaid_Struct*) pack->pBuffer; - CZCS->raid_id = raid_id; - CZCS->spell_id = spell_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneCastSpellByGuildID(int guild_id, uint32 spell_id) { - auto pack = new ServerPacket(ServerOP_CZCastSpellGuild, sizeof(CZCastSpellGuild_Struct)); - CZCastSpellGuild_Struct* CZCS = (CZCastSpellGuild_Struct*) pack->pBuffer; - CZCS->guild_id = guild_id; - CZCS->spell_id = spell_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneDisableTaskByCharID(int character_id, uint32 task_id) { - auto pack = new ServerPacket(ServerOP_CZTaskDisablePlayer, sizeof(CZTaskDisablePlayer_Struct)); - CZTaskDisablePlayer_Struct* CZTD = (CZTaskDisablePlayer_Struct*) pack->pBuffer; - CZTD->character_id = character_id; - CZTD->task_id = task_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneDisableTaskByGroupID(int group_id, uint32 task_id) { - auto pack = new ServerPacket(ServerOP_CZTaskDisableGroup, sizeof(CZTaskDisableGroup_Struct)); - CZTaskDisableGroup_Struct* CZTD = (CZTaskDisableGroup_Struct*) pack->pBuffer; - CZTD->group_id = group_id; - CZTD->task_id = task_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneDisableTaskByRaidID(int raid_id, uint32 task_id) { - auto pack = new ServerPacket(ServerOP_CZTaskDisableRaid, sizeof(CZTaskDisableRaid_Struct)); - CZTaskDisableRaid_Struct* CZTD = (CZTaskDisableRaid_Struct*) pack->pBuffer; - CZTD->raid_id = raid_id; - CZTD->task_id = task_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneDisableTaskByGuildID(int guild_id, uint32 task_id) { - auto pack = new ServerPacket(ServerOP_CZTaskDisableGuild, sizeof(CZTaskDisableGuild_Struct)); - CZTaskDisableGuild_Struct* CZTD = (CZTaskDisableGuild_Struct*) pack->pBuffer; - CZTD->guild_id = guild_id; - CZTD->task_id = task_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneEnableTaskByCharID(int character_id, uint32 task_id) { - auto pack = new ServerPacket(ServerOP_CZTaskEnablePlayer, sizeof(CZTaskEnablePlayer_Struct)); - CZTaskEnablePlayer_Struct* CZTE = (CZTaskEnablePlayer_Struct*) pack->pBuffer; - CZTE->character_id = character_id; - CZTE->task_id = task_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneEnableTaskByGroupID(int group_id, uint32 task_id) { - auto pack = new ServerPacket(ServerOP_CZTaskEnableGroup, sizeof(CZTaskEnableGroup_Struct)); - CZTaskEnableGroup_Struct* CZTE = (CZTaskEnableGroup_Struct*) pack->pBuffer; - CZTE->group_id = group_id; - CZTE->task_id = task_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneEnableTaskByRaidID(int raid_id, uint32 task_id) { - auto pack = new ServerPacket(ServerOP_CZTaskEnableRaid, sizeof(CZTaskEnableRaid_Struct)); - CZTaskEnableRaid_Struct* CZTE = (CZTaskEnableRaid_Struct*) pack->pBuffer; - CZTE->raid_id = raid_id; - CZTE->task_id = task_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneEnableTaskByGuildID(int guild_id, uint32 task_id) { - auto pack = new ServerPacket(ServerOP_CZTaskEnableGuild, sizeof(CZTaskEnableGuild_Struct)); - CZTaskEnableGuild_Struct* CZTE = (CZTaskEnableGuild_Struct*) pack->pBuffer; - CZTE->guild_id = guild_id; - CZTE->task_id = task_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneFailTaskByCharID(int character_id, uint32 task_id) { - auto pack = new ServerPacket(ServerOP_CZTaskFailPlayer, sizeof(CZTaskFailPlayer_Struct)); - CZTaskFailPlayer_Struct* CZTF = (CZTaskFailPlayer_Struct*) pack->pBuffer; - CZTF->character_id = character_id; - CZTF->task_id = task_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneFailTaskByGroupID(int group_id, uint32 task_id) { - auto pack = new ServerPacket(ServerOP_CZTaskFailGroup, sizeof(CZTaskFailGroup_Struct)); - CZTaskFailGroup_Struct* CZTF = (CZTaskFailGroup_Struct*) pack->pBuffer; - CZTF->group_id = group_id; - CZTF->task_id = task_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneFailTaskByRaidID(int raid_id, uint32 task_id) { - auto pack = new ServerPacket(ServerOP_CZTaskFailRaid, sizeof(CZTaskFailRaid_Struct)); - CZTaskFailRaid_Struct* CZTF = (CZTaskFailRaid_Struct*) pack->pBuffer; - CZTF->raid_id = raid_id; - CZTF->task_id = task_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneFailTaskByGuildID(int guild_id, uint32 task_id) { - auto pack = new ServerPacket(ServerOP_CZTaskFailGuild, sizeof(CZTaskFailGuild_Struct)); - CZTaskFailGuild_Struct* CZTF = (CZTaskFailGuild_Struct*) pack->pBuffer; - CZTF->guild_id = guild_id; - CZTF->task_id = task_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneMarqueeByCharID(int character_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message) { - uint32 message_len = strlen(message) + 1; - auto pack = new ServerPacket(ServerOP_CZMarqueePlayer, sizeof(CZMarqueePlayer_Struct) + message_len); - CZMarqueePlayer_Struct* CZMS = (CZMarqueePlayer_Struct*) pack->pBuffer; - CZMS->character_id = character_id; - CZMS->type = type; - CZMS->priority = priority; - CZMS->fade_in = fade_in; - CZMS->fade_out = fade_out; - CZMS->duration = duration; - strn0cpy(CZMS->message, message, 512); - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneMarqueeByGroupID(int group_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message) { - uint32 message_len = strlen(message) + 1; - auto pack = new ServerPacket(ServerOP_CZMarqueeGroup, sizeof(CZMarqueeGroup_Struct) + message_len); - CZMarqueeGroup_Struct* CZMS = (CZMarqueeGroup_Struct*) pack->pBuffer; - CZMS->group_id = group_id; - CZMS->type = type; - CZMS->priority = priority; - CZMS->fade_in = fade_in; - CZMS->fade_out = fade_out; - CZMS->duration = duration; - strn0cpy(CZMS->message, message, 512); - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneMarqueeByRaidID(int raid_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message) { - uint32 message_len = strlen(message) + 1; - auto pack = new ServerPacket(ServerOP_CZMarqueeRaid, sizeof(CZMarqueeRaid_Struct) + message_len); - CZMarqueeRaid_Struct* CZMS = (CZMarqueeRaid_Struct*) pack->pBuffer; - CZMS->raid_id = raid_id; - CZMS->type = type; - CZMS->priority = priority; - CZMS->fade_in = fade_in; - CZMS->fade_out = fade_out; - CZMS->duration = duration; - strn0cpy(CZMS->message, message, 512); - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneMarqueeByGuildID(int guild_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message) { - uint32 message_len = strlen(message) + 1; - auto pack = new ServerPacket(ServerOP_CZMarqueeGuild, sizeof(CZMarqueeGuild_Struct) + message_len); - CZMarqueeGuild_Struct* CZMS = (CZMarqueeGuild_Struct*) pack->pBuffer; - CZMS->guild_id = guild_id; - CZMS->type = type; - CZMS->priority = priority; - CZMS->fade_in = fade_in; - CZMS->fade_out = fade_out; - CZMS->duration = duration; - strn0cpy(CZMS->message, message, 512); - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneMessagePlayerByName(uint32 type, const char *character_name, const char *message) { - uint32 message_len = strlen(character_name) + 1; - uint32 message_len2 = strlen(message) + 1; - auto pack = new ServerPacket(ServerOP_CZMessagePlayer, sizeof(CZMessagePlayer_Struct) + message_len + message_len2); - CZMessagePlayer_Struct* CZSC = (CZMessagePlayer_Struct*) pack->pBuffer; - CZSC->type = type; - strn0cpy(CZSC->character_name, character_name, 64); - strn0cpy(CZSC->message, message, 512); - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneMessagePlayerByGroupID(uint32 type, int group_id, const char *message) { - uint32 message_len = strlen(message) + 1; - auto pack = new ServerPacket(ServerOP_CZMessageGroup, sizeof(CZMessageGroup_Struct) + message_len); - CZMessageGroup_Struct* CZGM = (CZMessageGroup_Struct*) pack->pBuffer; - CZGM->type = type; - CZGM->group_id = group_id; - strn0cpy(CZGM->message, message, 512); - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneMessagePlayerByRaidID(uint32 type, int raid_id, const char *message) { - uint32 message_len = strlen(message) + 1; - auto pack = new ServerPacket(ServerOP_CZMessageRaid, sizeof(CZMessageRaid_Struct) + message_len); - CZMessageRaid_Struct* CZRM = (CZMessageRaid_Struct*) pack->pBuffer; - CZRM->type = type; - CZRM->raid_id = raid_id; - strn0cpy(CZRM->message, message, 512); - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneMessagePlayerByGuildID(uint32 type, int guild_id, const char *message) { - uint32 message_len = strlen(message) + 1; - auto pack = new ServerPacket(ServerOP_CZMessageGuild, sizeof(CZMessageGuild_Struct) + message_len); - CZMessageGuild_Struct* CZGM = (CZMessageGuild_Struct*) pack->pBuffer; - CZGM->type = type; - CZGM->guild_id = guild_id; - strn0cpy(CZGM->message, message, 512); - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneMovePlayerByCharID(int character_id, const char *zone_short_name) { - uint32 message_len = strlen(zone_short_name) + 1; - auto pack = new ServerPacket(ServerOP_CZMovePlayer, sizeof(CZMovePlayer_Struct) + message_len); - CZMovePlayer_Struct* CZGM = (CZMovePlayer_Struct*) pack->pBuffer; - CZGM->character_id = character_id; - strn0cpy(CZGM->zone_short_name, zone_short_name, 32); - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneMovePlayerByGroupID(int group_id, const char *zone_short_name) { - uint32 message_len = strlen(zone_short_name) + 1; - auto pack = new ServerPacket(ServerOP_CZMoveGroup, sizeof(CZMoveGroup_Struct) + message_len); - CZMoveGroup_Struct* CZGM = (CZMoveGroup_Struct*) pack->pBuffer; - CZGM->group_id = group_id; - strn0cpy(CZGM->zone_short_name, zone_short_name, 32); - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneMovePlayerByRaidID(int raid_id, const char *zone_short_name) { - uint32 message_len = strlen(zone_short_name) + 1; - auto pack = new ServerPacket(ServerOP_CZMoveRaid, sizeof(CZMoveRaid_Struct) + message_len); - CZMoveRaid_Struct* CZRM = (CZMoveRaid_Struct*) pack->pBuffer; - CZRM->raid_id = raid_id; - strn0cpy(CZRM->zone_short_name, zone_short_name, 32); - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneMovePlayerByGuildID(int guild_id, const char *zone_short_name) { - uint32 message_len = strlen(zone_short_name) + 1; - auto pack = new ServerPacket(ServerOP_CZMoveGuild, sizeof(CZMoveGuild_Struct) + message_len); - CZMoveGuild_Struct* CZGM = (CZMoveGuild_Struct*) pack->pBuffer; - CZGM->guild_id = guild_id; - strn0cpy(CZGM->zone_short_name, zone_short_name, 32); - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneMoveInstanceByCharID(int character_id, uint16 instance_id) { - auto pack = new ServerPacket(ServerOP_CZMoveInstancePlayer, sizeof(CZMoveInstancePlayer_Struct)); - CZMoveInstancePlayer_Struct* CZMS = (CZMoveInstancePlayer_Struct*) pack->pBuffer; - CZMS->character_id = character_id; - CZMS->instance_id = instance_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneMoveInstanceByGroupID(int group_id, uint16 instance_id) { - auto pack = new ServerPacket(ServerOP_CZMoveInstanceGroup, sizeof(CZMoveInstanceGroup_Struct)); - CZMoveInstanceGroup_Struct* CZMS = (CZMoveInstanceGroup_Struct*) pack->pBuffer; - CZMS->group_id = group_id; - CZMS->instance_id = instance_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneMoveInstanceByRaidID(int raid_id, uint16 instance_id) { - auto pack = new ServerPacket(ServerOP_CZMoveInstanceRaid, sizeof(CZMoveInstanceRaid_Struct)); - CZMoveInstanceRaid_Struct* CZMS = (CZMoveInstanceRaid_Struct*) pack->pBuffer; - CZMS->raid_id = raid_id; - CZMS->instance_id = instance_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneMoveInstanceByGuildID(int guild_id, uint16 instance_id) { - auto pack = new ServerPacket(ServerOP_CZMoveInstanceGuild, sizeof(CZMoveInstanceGuild_Struct)); - CZMoveInstanceGuild_Struct* CZMS = (CZMoveInstanceGuild_Struct*) pack->pBuffer; - CZMS->guild_id = guild_id; - CZMS->instance_id = instance_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneRemoveSpellByCharID(int character_id, uint32 spell_id) { - auto pack = new ServerPacket(ServerOP_CZRemoveSpellPlayer, sizeof(CZRemoveSpellPlayer_Struct)); - CZRemoveSpellPlayer_Struct* CZCS = (CZRemoveSpellPlayer_Struct*) pack->pBuffer; - CZCS->character_id = character_id; - CZCS->spell_id = spell_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneRemoveSpellByGroupID(int group_id, uint32 spell_id) { - auto pack = new ServerPacket(ServerOP_CZRemoveSpellGroup, sizeof(CZRemoveSpellGroup_Struct)); - CZRemoveSpellGroup_Struct* CZCS = (CZRemoveSpellGroup_Struct*) pack->pBuffer; - CZCS->group_id = group_id; - CZCS->spell_id = spell_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneRemoveSpellByRaidID(int raid_id, uint32 spell_id) { - auto pack = new ServerPacket(ServerOP_CZRemoveSpellRaid, sizeof(CZRemoveSpellRaid_Struct)); - CZRemoveSpellRaid_Struct* CZCS = (CZRemoveSpellRaid_Struct*) pack->pBuffer; - CZCS->raid_id = raid_id; - CZCS->spell_id = spell_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneRemoveSpellByGuildID(int guild_id, uint32 spell_id) { - auto pack = new ServerPacket(ServerOP_CZRemoveSpellGuild, sizeof(CZRemoveSpellGuild_Struct)); - CZRemoveSpellGuild_Struct* CZCS = (CZRemoveSpellGuild_Struct*) pack->pBuffer; - CZCS->guild_id = guild_id; - CZCS->spell_id = spell_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneRemoveTaskByCharID(int character_id, uint32 task_id) { - auto pack = new ServerPacket(ServerOP_CZTaskRemovePlayer, sizeof(CZTaskRemovePlayer_Struct)); - CZTaskRemovePlayer_Struct* CZCS = (CZTaskRemovePlayer_Struct*) pack->pBuffer; - CZCS->character_id = character_id; - CZCS->task_id = task_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneRemoveTaskByGroupID(int group_id, uint32 task_id) { - auto pack = new ServerPacket(ServerOP_CZTaskRemoveGroup, sizeof(CZTaskRemoveGroup_Struct)); - CZTaskRemoveGroup_Struct* CZCS = (CZTaskRemoveGroup_Struct*) pack->pBuffer; - CZCS->group_id = group_id; - CZCS->task_id = task_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneRemoveTaskByRaidID(int raid_id, uint32 task_id) { - auto pack = new ServerPacket(ServerOP_CZTaskRemoveRaid, sizeof(CZTaskRemoveRaid_Struct)); - CZTaskRemoveRaid_Struct* CZCS = (CZTaskRemoveRaid_Struct*) pack->pBuffer; - CZCS->raid_id = raid_id; - CZCS->task_id = task_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneRemoveTaskByGuildID(int guild_id, uint32 task_id) { - auto pack = new ServerPacket(ServerOP_CZTaskRemoveGuild, sizeof(CZTaskRemoveGuild_Struct)); - CZTaskRemoveGuild_Struct* CZCS = (CZTaskRemoveGuild_Struct*) pack->pBuffer; - CZCS->guild_id = guild_id; - CZCS->task_id = task_id; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneResetActivityByCharID(int character_id, uint32 task_id, int activity_id) { - QuestManagerCurrentQuestVars(); - if (initiator && owner) { - auto pack = new ServerPacket(ServerOP_CZTaskActivityResetPlayer, sizeof(CZResetActivityPlayer_Struct)); - CZResetActivityPlayer_Struct* CZCA = (CZResetActivityPlayer_Struct*) pack->pBuffer; - CZCA->character_id = character_id; - CZCA->task_id = task_id; - CZCA->activity_id = activity_id; - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - -void QuestManager::CrossZoneResetActivityByGroupID(int group_id, uint32 task_id, int activity_id) { - QuestManagerCurrentQuestVars(); - if (initiator && owner) { - auto pack = new ServerPacket(ServerOP_CZTaskActivityResetGroup, sizeof(CZResetActivityGroup_Struct)); - CZResetActivityGroup_Struct* CZCA = (CZResetActivityGroup_Struct*) pack->pBuffer; - CZCA->group_id = group_id; - CZCA->task_id = task_id; - CZCA->activity_id = activity_id; - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - -void QuestManager::CrossZoneResetActivityByRaidID(int raid_id, uint32 task_id, int activity_id) { - QuestManagerCurrentQuestVars(); - if (initiator && owner) { - auto pack = new ServerPacket(ServerOP_CZTaskActivityResetRaid, sizeof(CZResetActivityRaid_Struct)); - CZResetActivityRaid_Struct* CZCA = (CZResetActivityRaid_Struct*) pack->pBuffer; - CZCA->raid_id = raid_id; - CZCA->task_id = task_id; - CZCA->activity_id = activity_id; - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - -void QuestManager::CrossZoneResetActivityByGuildID(int guild_id, uint32 task_id, int activity_id) { - QuestManagerCurrentQuestVars(); - if (initiator && owner) { - auto pack = new ServerPacket(ServerOP_CZTaskActivityResetGuild, sizeof(CZResetActivityGuild_Struct)); - CZResetActivityGuild_Struct* CZCA = (CZResetActivityGuild_Struct*) pack->pBuffer; - CZCA->guild_id = guild_id; - CZCA->task_id = task_id; - CZCA->activity_id = activity_id; - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - -void QuestManager::CrossZoneSignalNPCByNPCTypeID(uint32 npctype_id, uint32 signal) { - auto pack = new ServerPacket(ServerOP_CZSignalNPC, sizeof(CZNPCSignal_Struct)); - CZNPCSignal_Struct* CZSN = (CZNPCSignal_Struct*) pack->pBuffer; - CZSN->npctype_id = npctype_id; - CZSN->signal = signal; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneSignalPlayerByCharID(int character_id, uint32 signal) { - auto pack = new ServerPacket(ServerOP_CZSignalClient, sizeof(CZClientSignal_Struct)); - CZClientSignal_Struct* CZSC = (CZClientSignal_Struct*) pack->pBuffer; - CZSC->character_id = character_id; - CZSC->signal = signal; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneSetEntityVariableByClientName(const char *character_name, const char *variable_name, const char *variable_value) { - uint32 message_len = strlen(variable_name) + 1; - uint32 message_len2 = strlen(variable_value) + 1; - uint32 message_len3 = strlen(character_name) + 1; - auto pack = new ServerPacket(ServerOP_CZSetEntityVariableByClientName, sizeof(CZSetEntVarByClientName_Struct) + message_len + message_len2 + message_len3); - CZSetEntVarByClientName_Struct* CZ = (CZSetEntVarByClientName_Struct*) pack->pBuffer; - strn0cpy(CZ->character_name, character_name, 64); - strn0cpy(CZ->variable_name, variable_name, 256); - strn0cpy(CZ->variable_value, variable_value, 256); - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneSetEntityVariableByGroupID(int group_id, const char *variable_name, const char *variable_value) { - uint32 message_len = strlen(variable_name) + 1; - uint32 message_len2 = strlen(variable_value) + 1; - auto pack = new ServerPacket(ServerOP_CZSetEntityVariableByGroupID, sizeof(CZSetEntVarByGroupID_Struct) + message_len + message_len2); - CZSetEntVarByGroupID_Struct* CZ = (CZSetEntVarByGroupID_Struct*) pack->pBuffer; - CZ->group_id = group_id; - strn0cpy(CZ->variable_name, variable_name, 256); - strn0cpy(CZ->variable_value, variable_value, 256); - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneSetEntityVariableByRaidID(int raid_id, const char *variable_name, const char *variable_value) { - uint32 message_len = strlen(variable_name) + 1; - uint32 message_len2 = strlen(variable_value) + 1; - auto pack = new ServerPacket(ServerOP_CZSetEntityVariableByRaidID, sizeof(CZSetEntVarByRaidID_Struct) + message_len + message_len2); - CZSetEntVarByRaidID_Struct* CZ = (CZSetEntVarByRaidID_Struct*) pack->pBuffer; - CZ->raid_id = raid_id; - strn0cpy(CZ->variable_name, variable_name, 256); - strn0cpy(CZ->variable_value, variable_value, 256); - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneSetEntityVariableByGuildID(int guild_id, const char *variable_name, const char *variable_value) { - uint32 message_len = strlen(variable_name) + 1; - uint32 message_len2 = strlen(variable_value) + 1; - auto pack = new ServerPacket(ServerOP_CZSetEntityVariableByGuildID, sizeof(CZSetEntVarByGuildID_Struct) + message_len + message_len2); - CZSetEntVarByGuildID_Struct* CZ = (CZSetEntVarByGuildID_Struct*) pack->pBuffer; - CZ->guild_id = guild_id; - strn0cpy(CZ->variable_name, variable_name, 256); - strn0cpy(CZ->variable_value, variable_value, 256); - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneSetEntityVariableByNPCTypeID(uint32 npctype_id, const char *variable_name, const char *variable_value) { - uint32 message_len = strlen(variable_name) + 1; - uint32 message_len2 = strlen(variable_value) + 1; - auto pack = new ServerPacket(ServerOP_CZSetEntityVariableByNPCTypeID, sizeof(CZSetEntVarByNPCTypeID_Struct) + message_len + message_len2); - CZSetEntVarByNPCTypeID_Struct* CZSNBYNID = (CZSetEntVarByNPCTypeID_Struct*) pack->pBuffer; - CZSNBYNID->npctype_id = npctype_id; - strn0cpy(CZSNBYNID->variable_name, variable_name, 256); - strn0cpy(CZSNBYNID->variable_value, variable_value, 256); - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneSignalPlayerByGroupID(int group_id, uint32 signal) { - auto pack = new ServerPacket(ServerOP_CZSignalGroup, sizeof(CZGroupSignal_Struct)); - CZGroupSignal_Struct* CZGS = (CZGroupSignal_Struct*) pack->pBuffer; - CZGS->group_id = group_id; - CZGS->signal = signal; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneSignalPlayerByRaidID(int raid_id, uint32 signal) { - auto pack = new ServerPacket(ServerOP_CZSignalRaid, sizeof(CZRaidSignal_Struct)); - CZRaidSignal_Struct* CZRS = (CZRaidSignal_Struct*) pack->pBuffer; - CZRS->raid_id = raid_id; - CZRS->signal = signal; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneSignalPlayerByGuildID(int guild_id, uint32 signal) { - auto pack = new ServerPacket(ServerOP_CZSignalGuild, sizeof(CZGuildSignal_Struct)); - CZGuildSignal_Struct* CZGS = (CZGuildSignal_Struct*) pack->pBuffer; - CZGS->guild_id = guild_id; - CZGS->signal = signal; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneSignalPlayerByName(const char *character_name, uint32 signal) { - uint32 message_len = strlen(character_name) + 1; - auto pack = new ServerPacket(ServerOP_CZSignalClientByName, sizeof(CZClientSignalByName_Struct) + message_len); - CZClientSignalByName_Struct* CZSC = (CZClientSignalByName_Struct*) pack->pBuffer; - strn0cpy(CZSC->character_name, character_name, 64); - CZSC->signal = signal; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::CrossZoneUpdateActivityByCharID(int character_id, uint32 task_id, int activity_id, int activity_count) { - QuestManagerCurrentQuestVars(); - if (initiator && owner) { - auto pack = new ServerPacket(ServerOP_CZTaskActivityUpdatePlayer, sizeof(CZTaskActivityUpdatePlayer_Struct)); - CZTaskActivityUpdatePlayer_Struct* CZCA = (CZTaskActivityUpdatePlayer_Struct*) pack->pBuffer; - CZCA->character_id = character_id; - CZCA->task_id = task_id; - CZCA->activity_id = activity_id; - CZCA->activity_count = activity_count; - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - -void QuestManager::CrossZoneUpdateActivityByGroupID(int group_id, uint32 task_id, int activity_id, int activity_count) { - QuestManagerCurrentQuestVars(); - if (initiator && owner) { - auto pack = new ServerPacket(ServerOP_CZTaskActivityUpdateGroup, sizeof(CZTaskActivityUpdateGroup_Struct)); - CZTaskActivityUpdateGroup_Struct* CZCA = (CZTaskActivityUpdateGroup_Struct*) pack->pBuffer; - CZCA->group_id = group_id; - CZCA->task_id = task_id; - CZCA->activity_id = activity_id; - CZCA->activity_count = activity_count; - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - -void QuestManager::CrossZoneUpdateActivityByRaidID(int raid_id, uint32 task_id, int activity_id, int activity_count) { - QuestManagerCurrentQuestVars(); - if (initiator && owner) { - auto pack = new ServerPacket(ServerOP_CZTaskActivityUpdateRaid, sizeof(CZTaskActivityUpdateRaid_Struct)); - CZTaskActivityUpdateRaid_Struct* CZCA = (CZTaskActivityUpdateRaid_Struct*) pack->pBuffer; - CZCA->raid_id = raid_id; - CZCA->task_id = task_id; - CZCA->activity_id = activity_id; - CZCA->activity_count = activity_count; - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - -void QuestManager::CrossZoneUpdateActivityByGuildID(int guild_id, uint32 task_id, int activity_id, int activity_count) { - QuestManagerCurrentQuestVars(); - if (initiator && owner) { - auto pack = new ServerPacket(ServerOP_CZTaskActivityUpdateGuild, sizeof(CZTaskActivityUpdateGuild_Struct)); - CZTaskActivityUpdateGuild_Struct* CZCA = (CZTaskActivityUpdateGuild_Struct*) pack->pBuffer; - CZCA->guild_id = guild_id; - CZCA->task_id = task_id; - CZCA->activity_id = activity_id; - CZCA->activity_count = activity_count; - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - -void QuestManager::WorldWideAssignTask(uint32 task_id, bool enforce_level_requirement, uint8 min_status, uint8 max_status) { - QuestManagerCurrentQuestVars(); - if (initiator && owner) { - auto pack = new ServerPacket(ServerOP_WWAssignTask, sizeof(WWAssignTask_Struct)); - WWAssignTask_Struct* WWTA = (WWAssignTask_Struct*) pack->pBuffer; - WWTA->npc_entity_id = owner->GetID(); - WWTA->task_id = task_id; - WWTA->enforce_level_requirement = enforce_level_requirement; - WWTA->min_status = min_status; - WWTA->max_status = max_status; - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - -void QuestManager::WorldWideCastSpell(uint32 spell_id, uint8 min_status, uint8 max_status) { - auto pack = new ServerPacket(ServerOP_WWCastSpell, sizeof(WWCastSpell_Struct)); - WWCastSpell_Struct* WWCS = (WWCastSpell_Struct*) pack->pBuffer; - WWCS->spell_id = spell_id; - WWCS->min_status = min_status; - WWCS->max_status = max_status; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::WorldWideDisableTask(uint32 task_id, uint8 min_status, uint8 max_status) { - auto pack = new ServerPacket(ServerOP_WWDisableTask, sizeof(WWDisableTask_Struct)); - WWDisableTask_Struct* WWDT = (WWDisableTask_Struct*) pack->pBuffer; - WWDT->task_id = task_id; - WWDT->min_status = min_status; - WWDT->max_status = max_status; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::WorldWideEnableTask(uint32 task_id, uint8 min_status, uint8 max_status) { - auto pack = new ServerPacket(ServerOP_WWEnableTask, sizeof(WWEnableTask_Struct)); - WWEnableTask_Struct* WWET = (WWEnableTask_Struct*) pack->pBuffer; - WWET->task_id = task_id; - WWET->min_status = min_status; - WWET->max_status = max_status; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::WorldWideFailTask(uint32 task_id, uint8 min_status, uint8 max_status) { - auto pack = new ServerPacket(ServerOP_WWFailTask, sizeof(WWFailTask_Struct)); - WWFailTask_Struct* WWFT = (WWFailTask_Struct*) pack->pBuffer; - WWFT->task_id = task_id; - WWFT->min_status = min_status; - WWFT->max_status = max_status; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::WorldWideMarquee(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message, uint8 min_status, uint8 max_status) { - uint32 message_len = strlen(message) + 1; - auto pack = new ServerPacket(ServerOP_WWMarquee, sizeof(WWMarquee_Struct) + message_len); - WWMarquee_Struct* WWMS = (WWMarquee_Struct*) pack->pBuffer; - WWMS->type = type; - WWMS->priority = priority; - WWMS->fade_in = fade_in; - WWMS->fade_out = fade_out; - WWMS->duration = duration; - strn0cpy(WWMS->message, message, 512); - WWMS->min_status = min_status; - WWMS->max_status = max_status; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::WorldWideMessage(uint32 type, const char *message, uint8 min_status, uint8 max_status) { - uint32 message_len = strlen(message) + 1; - auto pack = new ServerPacket(ServerOP_WWMessage, sizeof(WWMessage_Struct) + message_len); - WWMessage_Struct* WWMS = (WWMessage_Struct*) pack->pBuffer; - WWMS->type = type; - strn0cpy(WWMS->message, message, 512); - WWMS->min_status = min_status; - WWMS->max_status = max_status; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::WorldWideMove(const char *zone_short_name, uint8 min_status, uint8 max_status) { - uint32 message_len = strlen(zone_short_name) + 1; - auto pack = new ServerPacket(ServerOP_WWMove, sizeof(WWMove_Struct) + message_len); - WWMove_Struct* WWMS = (WWMove_Struct*) pack->pBuffer;; - strn0cpy(WWMS->zone_short_name, zone_short_name, 32); - WWMS->min_status = min_status; - WWMS->max_status = max_status; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::WorldWideMoveInstance(uint16 instance_id, uint8 min_status, uint8 max_status) { - auto pack = new ServerPacket(ServerOP_WWMoveInstance, sizeof(WWMoveInstance_Struct)); - WWMoveInstance_Struct* WWMS = (WWMoveInstance_Struct*) pack->pBuffer; - WWMS->instance_id = instance_id; - WWMS->min_status = min_status; - WWMS->max_status = max_status; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::WorldWideRemoveSpell(uint32 spell_id, uint8 min_status, uint8 max_status) { - auto pack = new ServerPacket(ServerOP_WWRemoveSpell, sizeof(WWRemoveSpell_Struct)); - WWRemoveSpell_Struct* WWRS = (WWRemoveSpell_Struct*) pack->pBuffer; - WWRS->spell_id = spell_id; - WWRS->min_status = min_status; - WWRS->max_status = max_status; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::WorldWideRemoveTask(uint32 task_id, uint8 min_status, uint8 max_status) { - auto pack = new ServerPacket(ServerOP_WWRemoveTask, sizeof(WWRemoveTask_Struct)); - WWRemoveTask_Struct* WWRT = (WWRemoveTask_Struct*) pack->pBuffer; - WWRT->task_id = task_id; - WWRT->min_status = min_status; - WWRT->max_status = max_status; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::WorldWideResetActivity(uint32 task_id, int activity_id, uint8 min_status, uint8 max_status) { - auto pack = new ServerPacket(ServerOP_WWResetActivity, sizeof(WWResetActivity_Struct)); - WWResetActivity_Struct* WWRA = (WWResetActivity_Struct*) pack->pBuffer; - WWRA->task_id = task_id; - WWRA->activity_id = activity_id; - WWRA->min_status = min_status; - WWRA->max_status = max_status; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::WorldWideSetEntityVariableClient(const char *variable_name, const char *variable_value, uint8 min_status, uint8 max_status) { - uint32 message_len = strlen(variable_name) + 1; - uint32 message_len2 = strlen(variable_value) + 1; - auto pack = new ServerPacket(ServerOP_WWSetEntityVariableClient, sizeof(WWSetEntVarClient_Struct) + message_len + message_len2); - WWSetEntVarClient_Struct* WWSC = (WWSetEntVarClient_Struct*) pack->pBuffer; - strn0cpy(WWSC->variable_name, variable_name, 256); - strn0cpy(WWSC->variable_value, variable_value, 256); - WWSC->min_status = min_status; - WWSC->max_status = max_status; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::WorldWideSetEntityVariableNPC(const char *variable_name, const char *variable_value) { - uint32 message_len = strlen(variable_name) + 1; - uint32 message_len2 = strlen(variable_value) + 1; - auto pack = new ServerPacket(ServerOP_WWSetEntityVariableNPC, sizeof(WWSetEntVarNPC_Struct) + message_len + message_len2); - WWSetEntVarNPC_Struct* WWSN = (WWSetEntVarNPC_Struct*) pack->pBuffer; - strn0cpy(WWSN->variable_name, variable_name, 256); - strn0cpy(WWSN->variable_value, variable_value, 256); - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::WorldWideSignalClient(uint32 signal, uint8 min_status, uint8 max_status) { - auto pack = new ServerPacket(ServerOP_WWSignalClient, sizeof(WWSignalClient_Struct)); - WWSignalClient_Struct* WWSC = (WWSignalClient_Struct*) pack->pBuffer; - WWSC->signal = signal; - WWSC->min_status = min_status; - WWSC->max_status = max_status; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::WorldWideSignalNPC(uint32 signal) { - auto pack = new ServerPacket(ServerOP_WWSignalNPC, sizeof(WWSignalNPC_Struct)); - WWSignalNPC_Struct* WWSN = (WWSignalNPC_Struct*) pack->pBuffer; - WWSN->signal = signal; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void QuestManager::WorldWideUpdateActivity(uint32 task_id, int activity_id, int activity_count, uint8 min_status, uint8 max_status) { - auto pack = new ServerPacket(ServerOP_WWUpdateActivity, sizeof(WWUpdateActivity_Struct)); - WWUpdateActivity_Struct* WWUA = (WWUpdateActivity_Struct*) pack->pBuffer; - WWUA->task_id = task_id; - WWUA->activity_id = activity_id; - WWUA->activity_count = activity_count; - WWUA->min_status = min_status; - WWUA->max_status = max_status; - worldserver.SendPacket(pack); - safe_delete(pack); -} - bool QuestManager::EnableRecipe(uint32 recipe_id) { bool success = false; @@ -4304,18 +3438,6 @@ void QuestManager::SetEXPModifierByCharID(uint32 character_id, uint32 zone_id, d database.SetEXPModifier(character_id, zone_id, exp_modifier); } -void QuestManager::CrossZoneLDoNUpdate(uint8 type, uint8 subtype, int identifier, uint32 theme_id, int points) { - auto pack = new ServerPacket(ServerOP_CZLDoNUpdate, sizeof(CZLDoNUpdate_Struct)); - CZLDoNUpdate_Struct* CZLU = (CZLDoNUpdate_Struct*)pack->pBuffer; - CZLU->update_type = type; - CZLU->update_subtype = subtype; - CZLU->update_identifier = identifier; - CZLU->theme_id = theme_id; - CZLU->points = points; - worldserver.SendPacket(pack); - safe_delete(pack); -} - std::string QuestManager::getgendername(uint32 gender_id) { auto gender_name = "Unknown"; if (gender_id == MALE) { @@ -4345,3 +3467,205 @@ int QuestManager::getspellstat(uint32 spell_id, std::string stat_identifier, uin QuestManagerCurrentQuestVars(); return GetSpellStatValue(spell_id, stat_identifier.c_str(), slot); } + +void QuestManager::CrossZoneLDoNUpdate(uint8 update_type, uint8 update_subtype, int update_identifier, uint32 theme_id, int points, const char* client_name) { + auto pack = new ServerPacket(ServerOP_CZLDoNUpdate, sizeof(CZLDoNUpdate_Struct)); + CZLDoNUpdate_Struct* CZLU = (CZLDoNUpdate_Struct*)pack->pBuffer; + CZLU->update_type = update_type; + CZLU->update_subtype = update_subtype; + CZLU->update_identifier = update_identifier; + CZLU->theme_id = theme_id; + CZLU->points = points; + strn0cpy(CZLU->client_name, client_name, 64); + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void QuestManager::CrossZoneMarquee(uint8 update_type, int update_identifier, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char* message, const char* client_name) { + auto pack = new ServerPacket(ServerOP_CZMarquee, sizeof(CZMarquee_Struct)); + CZMarquee_Struct* CZM = (CZMarquee_Struct*)pack->pBuffer; + CZM->update_type = update_type; + CZM->update_identifier = update_identifier; + CZM->type = type; + CZM->priority = priority; + CZM->fade_in = fade_in; + CZM->fade_out = fade_out; + CZM->duration = duration; + strn0cpy(CZM->message, message, 512); + strn0cpy(CZM->client_name, client_name, 64); + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void QuestManager::CrossZoneMessage(uint8 update_type, int update_identifier, uint32 type, const char* message, const char* client_name) { + auto pack = new ServerPacket(ServerOP_CZMessage, sizeof(CZMarquee_Struct)); + CZMessage_Struct* CZM = (CZMessage_Struct*)pack->pBuffer; + CZM->update_type = update_type; + CZM->update_identifier = update_identifier; + CZM->type = type; + strn0cpy(CZM->message, message, 512); + strn0cpy(CZM->client_name, client_name, 64); + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void QuestManager::CrossZoneMove(uint8 update_type, uint8 update_subtype, int update_identifier, const char* zone_short_name, uint16 instance_id, const char* client_name) { + auto pack = new ServerPacket(ServerOP_CZMove, sizeof(CZMove_Struct)); + CZMove_Struct* CZM = (CZMove_Struct*)pack->pBuffer; + CZM->update_type = update_type; + CZM->update_subtype = update_subtype; + CZM->update_identifier = update_identifier; + strn0cpy(CZM->zone_short_name, zone_short_name, 32); + CZM->instance_id = instance_id; + strn0cpy(CZM->client_name, client_name, 64); + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void QuestManager::CrossZoneSetEntityVariable(uint8 update_type, int update_identifier, const char* variable_name, const char* variable_value, const char* client_name) { + auto pack = new ServerPacket(ServerOP_CZSetEntityVariable, sizeof(CZSetEntityVariable_Struct)); + CZSetEntityVariable_Struct* CZM = (CZSetEntityVariable_Struct*)pack->pBuffer; + CZM->update_type = update_type; + CZM->update_identifier = update_identifier; + strn0cpy(CZM->variable_name, variable_name, 256); + strn0cpy(CZM->variable_value, variable_value, 256); + strn0cpy(CZM->client_name, client_name, 64); + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void QuestManager::CrossZoneSignal(uint8 update_type, int update_identifier, uint32 signal, const char* client_name) { + auto pack = new ServerPacket(ServerOP_CZSignal, sizeof(CZSignal_Struct)); + CZSignal_Struct* CZS = (CZSignal_Struct*)pack->pBuffer; + CZS->update_type = update_type; + CZS->update_identifier = update_identifier; + CZS->signal = signal; + strn0cpy(CZS->client_name, client_name, 64); + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void QuestManager::CrossZoneSpell(uint8 update_type, uint8 update_subtype, int update_identifier, uint32 spell_id, const char* client_name) { + auto pack = new ServerPacket(ServerOP_CZSpell, sizeof(CZSpell_Struct)); + CZSpell_Struct* CZS = (CZSpell_Struct*)pack->pBuffer; + CZS->update_type = update_type; + CZS->update_subtype = update_subtype; + CZS->update_identifier = update_identifier; + CZS->spell_id = spell_id; + strn0cpy(CZS->client_name, client_name, 64); + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void QuestManager::CrossZoneTaskUpdate(uint8 update_type, uint8 update_subtype, int update_identifier, uint32 task_identifier, int task_subidentifier, int update_count, bool enforce_level_requirement, const char* client_name) { + auto pack = new ServerPacket(ServerOP_CZTaskUpdate, sizeof(CZTaskUpdate_Struct)); + CZTaskUpdate_Struct* CZTU = (CZTaskUpdate_Struct*)pack->pBuffer; + CZTU->update_type = update_type; + CZTU->update_subtype = update_subtype; + CZTU->update_identifier = update_identifier; + CZTU->task_identifier = task_identifier; + CZTU->task_subidentifier = task_subidentifier; + CZTU->update_count = update_count; + CZTU->enforce_level_requirement = enforce_level_requirement; + strn0cpy(CZTU->client_name, client_name, 64); + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void QuestManager::WorldWideLDoNUpdate(uint8 update_type, uint32 theme_id, int points, uint8 min_status, uint8 max_status) { + auto pack = new ServerPacket(ServerOP_WWLDoNUpdate, sizeof(WWLDoNUpdate_Struct)); + WWLDoNUpdate_Struct* WWLU = (WWLDoNUpdate_Struct*)pack->pBuffer; + WWLU->update_type = update_type; + WWLU->theme_id = theme_id; + WWLU->points = points; + WWLU->min_status = min_status; + WWLU->max_status = max_status; + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void QuestManager::WorldWideMarquee(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char* message, uint8 min_status, uint8 max_status) { + auto pack = new ServerPacket(ServerOP_WWMarquee, sizeof(WWMarquee_Struct)); + WWMarquee_Struct* WWM = (WWMarquee_Struct*)pack->pBuffer; + WWM->type = type; + WWM->priority = priority; + WWM->fade_in = fade_in; + WWM->fade_out = fade_out; + WWM->duration = duration; + strn0cpy(WWM->message, message, 512); + WWM->min_status = min_status; + WWM->max_status = max_status; + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void QuestManager::WorldWideMessage(uint32 type, const char* message, uint8 min_status, uint8 max_status) { + auto pack = new ServerPacket(ServerOP_WWMessage, sizeof(WWMarquee_Struct)); + WWMessage_Struct* WWM = (WWMessage_Struct*)pack->pBuffer; + WWM->type = type; + strn0cpy(WWM->message, message, 512); + WWM->min_status = min_status; + WWM->max_status = max_status; + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void QuestManager::WorldWideMove(uint8 update_type, const char* zone_short_name, uint16 instance_id, uint8 min_status, uint8 max_status) { + auto pack = new ServerPacket(ServerOP_WWMove, sizeof(WWMove_Struct)); + WWMove_Struct* WWM = (WWMove_Struct*)pack->pBuffer; + WWM->update_type = update_type; + strn0cpy(WWM->zone_short_name, zone_short_name, 32); + WWM->instance_id = instance_id; + WWM->min_status = min_status; + WWM->max_status = max_status; + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void QuestManager::WorldWideSetEntityVariable(uint8 update_type, const char* variable_name, const char* variable_value, uint8 min_status, uint8 max_status) { + auto pack = new ServerPacket(ServerOP_WWSetEntityVariable, sizeof(WWSetEntityVariable_Struct)); + WWSetEntityVariable_Struct* WWSEV = (WWSetEntityVariable_Struct*)pack->pBuffer; + WWSEV->update_type = update_type; + strn0cpy(WWSEV->variable_name, variable_name, 256); + strn0cpy(WWSEV->variable_value, variable_value, 256); + WWSEV->min_status = min_status; + WWSEV->max_status = max_status; + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void QuestManager::WorldWideSignal(uint8 update_type, uint32 signal, uint8 min_status, uint8 max_status) { + auto pack = new ServerPacket(ServerOP_WWSignal, sizeof(WWSignal_Struct)); + WWSignal_Struct* WWS = (WWSignal_Struct*)pack->pBuffer; + WWS->update_type = update_type; + WWS->signal = signal; + WWS->min_status = min_status; + WWS->max_status = max_status; + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void QuestManager::WorldWideSpell(uint8 update_type, uint32 spell_id, uint8 min_status, uint8 max_status) { + auto pack = new ServerPacket(ServerOP_WWSpell, sizeof(WWSpell_Struct)); + WWSpell_Struct* WWS = (WWSpell_Struct*)pack->pBuffer; + WWS->update_type = update_type; + WWS->spell_id = spell_id; + WWS->min_status = min_status; + WWS->max_status = max_status; + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void QuestManager::WorldWideTaskUpdate(uint8 update_type, uint32 task_identifier, int task_subidentifier, int update_count, bool enforce_level_requirement, uint8 min_status, uint8 max_status) { + auto pack = new ServerPacket(ServerOP_WWTaskUpdate, sizeof(WWTaskUpdate_Struct)); + WWTaskUpdate_Struct* WWTU = (WWTaskUpdate_Struct*)pack->pBuffer; + WWTU->update_type = update_type; + WWTU->task_identifier = task_identifier; + WWTU->task_subidentifier = task_subidentifier; + WWTU->update_count = update_count; + WWTU->enforce_level_requirement = enforce_level_requirement; + WWTU->min_status = min_status; + WWTU->max_status = max_status; + worldserver.SendPacket(pack); + safe_delete(pack); +} diff --git a/zone/questmgr.h b/zone/questmgr.h index 5d0c735ba..fa56f8dc2 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -290,87 +290,22 @@ public: static std::string GetZoneLongName(std::string zone_short_name); static std::string GetZoneLongNameByID(uint32 zone_id); static std::string GetZoneShortName(uint32 zone_id); - void CrossZoneAssignTaskByCharID(int character_id, uint32 task_id, bool enforce_level_requirement = false); - void CrossZoneAssignTaskByGroupID(int group_id, uint32 task_id, bool enforce_level_requirement = false); - void CrossZoneAssignTaskByRaidID(int raid_id, uint32 task_id, bool enforce_level_requirement = false); - void CrossZoneAssignTaskByGuildID(int guild_id, uint32 task_id, bool enforce_level_requirement = false); - void CrossZoneCastSpellByCharID(int character_id, uint32 spell_id); - void CrossZoneCastSpellByGroupID(int group_id, uint32 spell_id); - void CrossZoneCastSpellByRaidID(int raid_id, uint32 spell_id); - void CrossZoneCastSpellByGuildID(int guild_id, uint32 spell_id); - void CrossZoneDisableTaskByCharID(int character_id, uint32 task_id); - void CrossZoneDisableTaskByGroupID(int group_id, uint32 task_id); - void CrossZoneDisableTaskByRaidID(int raid_id, uint32 task_id); - void CrossZoneDisableTaskByGuildID(int guild_id, uint32 task_id); - void CrossZoneEnableTaskByCharID(int character_id, uint32 task_id); - void CrossZoneEnableTaskByGroupID(int group_id, uint32 task_id); - void CrossZoneEnableTaskByRaidID(int raid_id, uint32 task_id); - void CrossZoneEnableTaskByGuildID(int guild_id, uint32 task_id); - void CrossZoneFailTaskByCharID(int character_id, uint32 task_id); - void CrossZoneFailTaskByGroupID(int group_id, uint32 task_id); - void CrossZoneFailTaskByRaidID(int raid_id, uint32 task_id); - void CrossZoneFailTaskByGuildID(int guild_id, uint32 task_id); - void CrossZoneLDoNUpdate(uint8 type, uint8 subtype, int identifier, uint32 theme_id, int points = 1); - void CrossZoneMarqueeByCharID(int character_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message); - void CrossZoneMarqueeByGroupID(int group_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message); - void CrossZoneMarqueeByRaidID(int raid_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message); - void CrossZoneMarqueeByGuildID(int guild_id, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message); - void CrossZoneMessagePlayerByName(uint32 type, const char *character_name, const char *message); - void CrossZoneMessagePlayerByGroupID(uint32 type, int group_id, const char *message); - void CrossZoneMessagePlayerByRaidID(uint32 type, int raid_id, const char *message); - void CrossZoneMessagePlayerByGuildID(uint32 type, int guild_id, const char *message); - void CrossZoneMovePlayerByCharID(int character_id, const char *zone_short_name); - void CrossZoneMovePlayerByGroupID(int group_id, const char *zone_short_name); - void CrossZoneMovePlayerByRaidID(int raid_id, const char *zone_short_name); - void CrossZoneMovePlayerByGuildID(int guild_id, const char *zone_short_name); - void CrossZoneMoveInstanceByCharID(int character_id, uint16 instance_id); - void CrossZoneMoveInstanceByGroupID(int group_id, uint16 instance_id); - void CrossZoneMoveInstanceByRaidID(int raid_id, uint16 instance_id); - void CrossZoneMoveInstanceByGuildID(int guild_id, uint16 instance_id); - void CrossZoneRemoveSpellByCharID(int character_id, uint32 spell_id); - void CrossZoneRemoveSpellByGroupID(int group_id, uint32 spell_id); - void CrossZoneRemoveSpellByRaidID(int raid_id, uint32 spell_id); - void CrossZoneRemoveSpellByGuildID(int guild_id, uint32 spell_id); - void CrossZoneRemoveTaskByCharID(int character_id, uint32 task_id); - void CrossZoneRemoveTaskByGroupID(int group_id, uint32 task_id); - void CrossZoneRemoveTaskByRaidID(int raid_id, uint32 task_id); - void CrossZoneRemoveTaskByGuildID(int guild_id, uint32 task_id); - void CrossZoneResetActivityByCharID(int character_id, uint32 task_id, int activity_id); - void CrossZoneResetActivityByGroupID(int group_id, uint32 task_id, int activity_id); - void CrossZoneResetActivityByRaidID(int raid_id, uint32 task_id, int activity_id); - void CrossZoneResetActivityByGuildID(int guild_id, uint32 task_id, int activity_id); - void CrossZoneSetEntityVariableByNPCTypeID(uint32 npctype_id, const char *variable_name, const char *variable_value); - void CrossZoneSetEntityVariableByClientName(const char *character_name, const char *variable_name, const char *variable_value); - void CrossZoneSetEntityVariableByGroupID(int group_id, const char *variable_name, const char *variable_value); - void CrossZoneSetEntityVariableByRaidID(int raid_id, const char *variable_name, const char *variable_value); - void CrossZoneSetEntityVariableByGuildID(int guild_id, const char *variable_name, const char *variable_value); - void CrossZoneSignalPlayerByCharID(int charid, uint32 signal); - void CrossZoneSignalPlayerByGroupID(int group_id, uint32 signal); - void CrossZoneSignalPlayerByRaidID(int raid_id, uint32 signal); - void CrossZoneSignalPlayerByGuildID(int guild_id, uint32 signal); - void CrossZoneSignalNPCByNPCTypeID(uint32 npctype_id, uint32 signal); - void CrossZoneSignalPlayerByName(const char *character_name, uint32 signal); - void CrossZoneUpdateActivityByCharID(int character_id, uint32 task_id, int activity_id, int activity_count = 1); - void CrossZoneUpdateActivityByGroupID(int group_id, uint32 task_id, int activity_id, int activity_count = 1); - void CrossZoneUpdateActivityByRaidID(int raid_id, uint32 task_id, int activity_id, int activity_count = 1); - void CrossZoneUpdateActivityByGuildID(int guild_id, uint32 task_id, int activity_id, int activity_count = 1); - void WorldWideAssignTask(uint32 task_id, bool enforce_level_requirement = false, uint8 min_status = 0, uint8 max_status = 0); - void WorldWideCastSpell(uint32 spell_id, uint8 min_status = 0, uint8 max_status = 0); - void WorldWideDisableTask(uint32 task_id, uint8 min_status = 0, uint8 max_status = 0); - void WorldWideEnableTask(uint32 task_id, uint8 min_status = 0, uint8 max_status = 0); - void WorldWideFailTask(uint32 task_id, uint8 min_status = 0, uint8 max_status = 0); - void WorldWideMarquee(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char *message, uint8 min_status = 0, uint8 max_status = 0); - void WorldWideMessage(uint32 type, const char *message, uint8 min_status = 0, uint8 max_status = 0); - void WorldWideMove(const char *zone_short_name, uint8 min_status = 0, uint8 max_status = 0); - void WorldWideMoveInstance(uint16 instance_id, uint8 min_status = 0, uint8 max_status = 0); - void WorldWideRemoveSpell(uint32 spell_id, uint8 min_status = 0, uint8 max_status = 0); - void WorldWideRemoveTask(uint32 task_id, uint8 min_status = 0, uint8 max_status = 0); - void WorldWideResetActivity(uint32 task_id, int activity_id, uint8 min_status = 0, uint8 max_status = 0); - void WorldWideSetEntityVariableClient(const char *variable_name, const char *variable_value, uint8 min_status = 0, uint8 max_status = 0); - void WorldWideSetEntityVariableNPC(const char *variable_name, const char *variable_value); - void WorldWideSignalClient(uint32 signal, uint8 min_status = 0, uint8 max_status = 0); - void WorldWideSignalNPC(uint32 signal); - void WorldWideUpdateActivity(uint32 task_id, int activity_id, int activity_count = 1, uint8 min_status = 0, uint8 max_status = 0); + void CrossZoneLDoNUpdate(uint8 update_type, uint8 update_subtype, int update_identifier, uint32 theme_id, int points = 1, const char* client_name = ""); + void CrossZoneMarquee(uint8 update_type, int update_identifier, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char* message, const char* client_name = ""); + void CrossZoneMessage(uint8 update_type, int update_identifier, uint32 type, const char* message, const char* client_name = ""); + void CrossZoneMove(uint8 update_type, uint8 update_subtype, int update_identifier, const char* zone_short_name, uint16 instance_id, const char* client_name = ""); + void CrossZoneSetEntityVariable(uint8 update_type, int update_identifier, const char* variable_name, const char* variable_value, const char* client_name = ""); + void CrossZoneSignal(uint8 update_type, int update_identifier, uint32 signal, const char* client_name = ""); + void CrossZoneSpell(uint8 update_type, uint8 update_subtype, int update_identifier, uint32 spell_id, const char* client_name = ""); + void CrossZoneTaskUpdate(uint8 update_type, uint8 update_subtype, int update_identifier, uint32 task_identifier, int task_subidentifier = -1, int update_count = 1, bool enforce_level_requirement = false, const char* client_name = ""); + void WorldWideLDoNUpdate(uint8 update_type, uint32 theme_id, int points = 1, uint8 min_status = 0, uint8 max_status = 0); + void WorldWideMarquee(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char* message, uint8 min_status = 0, uint8 max_status = 0); + void WorldWideMessage(uint32 type, const char* message, uint8 min_status = 0, uint8 max_status = 0); + void WorldWideMove(uint8 update_type, const char* zone_short_name, uint16 instance_id = 0, uint8 min_status = 0, uint8 max_status = 0); + void WorldWideSetEntityVariable(uint8 update_type, const char* variable_name, const char* variable_value, uint8 min_status = 0, uint8 max_status = 0); + void WorldWideSignal(uint8 update_type, uint32 signal, uint8 min_status = 0, uint8 max_status = 0); + void WorldWideSpell(uint8 update_type, uint32 spell_id, uint8 min_status = 0, uint8 max_status = 0); + void WorldWideTaskUpdate(uint8 update_type, uint32 task_identifier, int task_subidentifier = -1, int update_count = 1, bool enforce_level_requirement = false, uint8 min_status = 0, uint8 max_status = 0); bool EnableRecipe(uint32 recipe_id); bool DisableRecipe(uint32 recipe_id); void ClearNPCTypeCache(int npctype_id); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index fb4d0d4bb..98fe2a0f3 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1887,49 +1887,286 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } break; } - case ServerOP_CZCastSpellPlayer: + case ServerOP_CZSpell: { - CZCastSpellPlayer_Struct* CZSC = (CZCastSpellPlayer_Struct*) pack->pBuffer; - Client* client = entity_list.GetClientByCharID(CZSC->character_id); - if (client) { - client->SpellFinished(CZSC->spell_id, client); - } - break; - } - case ServerOP_CZCastSpellGroup: - { - CZCastSpellGroup_Struct* CZSC = (CZCastSpellGroup_Struct*) pack->pBuffer; - auto client_group = entity_list.GetGroupByID(CZSC->group_id); - if (client_group) { - for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { - if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { - auto group_member = client_group->members[member_index]->CastToClient(); - group_member->SpellFinished(CZSC->spell_id, group_member); + CZSpell_Struct* CZS = (CZSpell_Struct*) pack->pBuffer; + uint8 update_type = CZS->update_type; + uint8 update_subtype = CZS->update_subtype; + int update_identifier = CZS->update_identifier; + uint32 spell_id = CZS->spell_id; + const char* client_name = CZS->client_name; + if (update_type == CZUpdateType_Character) { + auto client = entity_list.GetClientByCharID(update_identifier); + if (client) { + switch (update_subtype) { + case CZSpellUpdateSubtype_Cast: + client->SpellFinished(spell_id, client); + break; + case CZSpellUpdateSubtype_Remove: + client->BuffFadeBySpellID(spell_id); + break; + } + } + } else if (update_type == CZUpdateType_Group) { + auto client_group = entity_list.GetGroupByID(update_identifier); + if (client_group) { + for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { + if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { + auto group_member = client_group->members[member_index]->CastToClient(); + switch (update_subtype) { + case CZSpellUpdateSubtype_Cast: + group_member->SpellFinished(spell_id, group_member); + break; + case CZSpellUpdateSubtype_Remove: + group_member->BuffFadeBySpellID(spell_id); + break; + } + } + } + } + } else if (update_type == CZUpdateType_Raid) { + auto client_raid = entity_list.GetRaidByID(update_identifier); + if (client_raid) { + for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { + if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { + auto raid_member = client_raid->members[member_index].member->CastToClient(); + switch (update_subtype) { + case CZSpellUpdateSubtype_Cast: + raid_member->SpellFinished(spell_id, raid_member); + break; + case CZSpellUpdateSubtype_Remove: + raid_member->BuffFadeBySpellID(spell_id); + break; + } + } + } + } + } else if (update_type == CZUpdateType_Guild) { + for (auto &client: entity_list.GetClientList()) { + if (client.second->GuildID() > 0 && client.second->GuildID() == update_identifier) { + switch (update_subtype) { + case CZSpellUpdateSubtype_Cast: + client.second->SpellFinished(spell_id, client.second); + break; + case CZSpellUpdateSubtype_Remove: + client.second->BuffFadeBySpellID(spell_id); + break; + } + } + } + } else if (update_type == CZUpdateType_Expedition) { + for (auto &client: entity_list.GetClientList()) { + if (client.second->GetExpedition() && client.second->GetExpedition()->GetID() == update_identifier) { + switch (update_subtype) { + case CZSpellUpdateSubtype_Cast: + client.second->SpellFinished(spell_id, client.second); + break; + case CZSpellUpdateSubtype_Remove: + client.second->BuffFadeBySpellID(spell_id); + break; + } + } + } + } else if (update_type == CZUpdateType_ClientName) { + auto client = entity_list.GetClientByName(client_name); + if (client) { + switch (update_subtype) { + case CZSpellUpdateSubtype_Cast: + client->SpellFinished(spell_id, client); + break; + case CZSpellUpdateSubtype_Remove: + client->BuffFadeBySpellID(spell_id); + break; } } } break; } - case ServerOP_CZCastSpellRaid: + case ServerOP_CZTaskUpdate: { - CZCastSpellRaid_Struct* CZSC = (CZCastSpellRaid_Struct*) pack->pBuffer; - auto client_raid = entity_list.GetRaidByID(CZSC->raid_id); - if (client_raid) { - for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { - if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { - auto raid_member = client_raid->members[member_index].member->CastToClient(); - raid_member->SpellFinished(CZSC->spell_id, raid_member); + CZTaskUpdate_Struct* CZTU = (CZTaskUpdate_Struct*) pack->pBuffer; + uint8 update_type = CZTU->update_type; + uint8 update_subtype = CZTU->update_subtype; + int update_identifier = CZTU->update_identifier; + uint32 task_identifier = CZTU->task_identifier; + int task_subidentifier = CZTU->task_subidentifier; + int update_count = CZTU->update_count; + bool enforce_level_requirement = CZTU->enforce_level_requirement; + const char* client_name = CZTU->client_name; + if (update_type == CZUpdateType_Character) { + auto client = entity_list.GetClientByCharID(update_identifier); + if (client) { + switch (update_subtype) { + case CZTaskUpdateSubtype_ActivityReset: + client->ResetTaskActivity(task_identifier, task_subidentifier); + break; + case CZTaskUpdateSubtype_ActivityUpdate: + client->UpdateTaskActivity(task_identifier, task_subidentifier, update_count); + break; + case CZTaskUpdateSubtype_AssignTask: + client->AssignTask(task_identifier, task_subidentifier, enforce_level_requirement); + break; + case CZTaskUpdateSubtype_DisableTask: + client->DisableTask(1, reinterpret_cast(task_identifier)); + break; + case CZTaskUpdateSubtype_EnableTask: + client->EnableTask(1, reinterpret_cast(task_identifier)); + break; + case CZTaskUpdateSubtype_FailTask: + client->FailTask(task_identifier); + break; + case CZTaskUpdateSubtype_RemoveTask: + client->RemoveTaskByTaskID(task_identifier); + break; } } - } - break; - } - case ServerOP_CZCastSpellGuild: - { - CZCastSpellGuild_Struct* CZSC = (CZCastSpellGuild_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - if (client.second->GuildID() > 0 && client.second->GuildID() == CZSC->guild_id) { - client.second->SpellFinished(CZSC->spell_id, client.second); + break; + } else if (update_type == CZUpdateType_Group) { + auto client_group = entity_list.GetGroupByID(update_identifier); + if (client_group) { + for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { + if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { + auto group_member = client_group->members[member_index]->CastToClient(); + switch (update_subtype) { + case CZTaskUpdateSubtype_ActivityReset: + group_member->ResetTaskActivity(task_identifier, task_subidentifier); + break; + case CZTaskUpdateSubtype_ActivityUpdate: + group_member->UpdateTaskActivity(task_identifier, task_subidentifier, update_count); + break; + case CZTaskUpdateSubtype_AssignTask: + group_member->AssignTask(task_identifier, task_subidentifier, enforce_level_requirement); + break; + case CZTaskUpdateSubtype_DisableTask: + group_member->DisableTask(1, reinterpret_cast(task_identifier)); + break; + case CZTaskUpdateSubtype_EnableTask: + group_member->EnableTask(1, reinterpret_cast(task_identifier)); + break; + case CZTaskUpdateSubtype_FailTask: + group_member->FailTask(task_identifier); + break; + case CZTaskUpdateSubtype_RemoveTask: + group_member->RemoveTaskByTaskID(task_identifier); + break; + } + } + } + } + } else if (update_type == CZUpdateType_Raid) { + auto client_raid = entity_list.GetRaidByID(update_identifier); + if (client_raid) { + for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { + if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { + auto raid_member = client_raid->members[member_index].member->CastToClient(); + switch (update_subtype) { + case CZTaskUpdateSubtype_ActivityReset: + raid_member->ResetTaskActivity(task_identifier, task_subidentifier); + break; + case CZTaskUpdateSubtype_ActivityUpdate: + raid_member->UpdateTaskActivity(task_identifier, task_subidentifier, update_count); + break; + case CZTaskUpdateSubtype_AssignTask: + raid_member->AssignTask(task_identifier, task_subidentifier, enforce_level_requirement); + break; + case CZTaskUpdateSubtype_DisableTask: + raid_member->DisableTask(1, reinterpret_cast(task_identifier)); + break; + case CZTaskUpdateSubtype_EnableTask: + raid_member->EnableTask(1, reinterpret_cast(task_identifier)); + break; + case CZTaskUpdateSubtype_FailTask: + raid_member->FailTask(task_identifier); + break; + case CZTaskUpdateSubtype_RemoveTask: + raid_member->RemoveTaskByTaskID(task_identifier); + break; + } + } + } + } + } else if (update_type == CZUpdateType_Guild) { + for (auto &client: entity_list.GetClientList()) { + if (client.second->GuildID() > 0 && client.second->GuildID() == update_identifier) { + switch (update_subtype) { + case CZTaskUpdateSubtype_ActivityReset: + client.second->ResetTaskActivity(task_identifier, task_subidentifier); + break; + case CZTaskUpdateSubtype_ActivityUpdate: + client.second->UpdateTaskActivity(task_identifier, task_subidentifier, update_count); + break; + case CZTaskUpdateSubtype_AssignTask: + client.second->AssignTask(task_identifier, task_subidentifier, enforce_level_requirement); + break; + case CZTaskUpdateSubtype_DisableTask: + client.second->DisableTask(1, reinterpret_cast(task_identifier)); + break; + case CZTaskUpdateSubtype_EnableTask: + client.second->EnableTask(1, reinterpret_cast(task_identifier)); + break; + case CZTaskUpdateSubtype_FailTask: + client.second->FailTask(task_identifier); + break; + case CZTaskUpdateSubtype_RemoveTask: + client.second->RemoveTaskByTaskID(task_identifier); + break; + } + } + } + } else if (update_type == CZUpdateType_Expedition) { + for (auto &client: entity_list.GetClientList()) { + if (client.second->GetExpedition() && client.second->GetExpedition()->GetID() == update_identifier) { + switch (update_subtype) { + case CZTaskUpdateSubtype_ActivityReset: + client.second->ResetTaskActivity(task_identifier, task_subidentifier); + break; + case CZTaskUpdateSubtype_ActivityUpdate: + client.second->UpdateTaskActivity(task_identifier, task_subidentifier, update_count); + break; + case CZTaskUpdateSubtype_AssignTask: + client.second->AssignTask(task_identifier, task_subidentifier, enforce_level_requirement); + break; + case CZTaskUpdateSubtype_DisableTask: + client.second->DisableTask(1, reinterpret_cast(task_identifier)); + break; + case CZTaskUpdateSubtype_EnableTask: + client.second->EnableTask(1, reinterpret_cast(task_identifier)); + break; + case CZTaskUpdateSubtype_FailTask: + client.second->FailTask(task_identifier); + break; + case CZTaskUpdateSubtype_RemoveTask: + client.second->RemoveTaskByTaskID(task_identifier); + break; + } + } + } + } else if (update_type == CZUpdateType_ClientName) { + auto client = entity_list.GetClientByName(client_name); + if (client) { + switch (update_subtype) { + case CZTaskUpdateSubtype_ActivityReset: + client->ResetTaskActivity(task_identifier, task_subidentifier); + break; + case CZTaskUpdateSubtype_ActivityUpdate: + client->UpdateTaskActivity(task_identifier, task_subidentifier, update_count); + break; + case CZTaskUpdateSubtype_AssignTask: + client->AssignTask(task_identifier, task_subidentifier, enforce_level_requirement); + break; + case CZTaskUpdateSubtype_DisableTask: + client->DisableTask(1, reinterpret_cast(task_identifier)); + break; + case CZTaskUpdateSubtype_EnableTask: + client->EnableTask(1, reinterpret_cast(task_identifier)); + break; + case CZTaskUpdateSubtype_FailTask: + client->FailTask(task_identifier); + break; + case CZTaskUpdateSubtype_RemoveTask: + client->RemoveTaskByTaskID(task_identifier); + break; + } } } break; @@ -1942,7 +2179,8 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) int update_identifier = CZLU->update_identifier; uint32 theme_id = CZLU->theme_id; int points = CZLU->points; - if (update_type == CZLDoNUpdateType_Character) { + const char* client_name = CZLU->client_name; + if (update_type == CZUpdateType_Character) { auto client = entity_list.GetClientByCharID(update_identifier); if (client) { switch (update_subtype) { @@ -1960,7 +2198,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } } break; - } else if (update_type == CZLDoNUpdateType_Group) { + } else if (update_type == CZUpdateType_Group) { auto client_group = entity_list.GetGroupByID(update_identifier); if (client_group) { for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { @@ -1982,7 +2220,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } } } - } else if (update_type == CZLDoNUpdateType_Raid) { + } else if (update_type == CZUpdateType_Raid) { auto client_raid = entity_list.GetRaidByID(update_identifier); if (client_raid) { for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { @@ -2004,7 +2242,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } } } - } else if (update_type == CZLDoNUpdateType_Guild) { + } else if (update_type == CZUpdateType_Guild) { for (auto &client : entity_list.GetClientList()) { if (client.second->GuildID() > 0 && client.second->GuildID() == update_identifier) { switch (update_subtype) { @@ -2022,7 +2260,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } } } - } else if (update_type == CZLDoNUpdateType_Expedition) { + } else if (update_type == CZUpdateType_Expedition) { for (auto &client : entity_list.GetClientList()) { if (client.second->GetExpedition() && client.second->GetExpedition()->GetID() == update_identifier) { switch (update_subtype) { @@ -2040,877 +2278,531 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } } } + } else if (update_type == CZUpdateType_ClientName) { + auto client = entity_list.GetClientByName(client_name); + if (client) { + switch (update_subtype) { + case CZLDoNUpdateSubtype_Loss: + client->AddLDoNLoss(theme_id); + break; + case CZLDoNUpdateSubtype_Points: + client->UpdateLDoNPoints(theme_id, points); + break; + case CZLDoNUpdateSubtype_Win: + client->AddLDoNWin(theme_id); + break; + default: + break; + } + } + break; + } + break; + } + case ServerOP_CZMarquee: + { + CZMarquee_Struct* CZM = (CZMarquee_Struct*) pack->pBuffer; + uint8 update_type = CZM->update_type; + int update_identifier = CZM->update_identifier; + uint32 type = CZM->type; + uint32 priority = CZM->priority; + uint32 fade_in = CZM->fade_in; + uint32 fade_out = CZM->fade_out; + uint32 duration = CZM->duration; + const char* message = CZM->message; + const char* client_name = CZM->client_name; + if (update_type == CZUpdateType_Character) { + auto client = entity_list.GetClientByCharID(update_identifier); + if (client) { + client->SendMarqueeMessage(type, priority, fade_in, fade_out, duration, message); + } + } else if (update_type == CZUpdateType_Group) { + auto client_group = entity_list.GetGroupByID(update_identifier); + if (client_group) { + for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { + if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { + auto group_member = client_group->members[member_index]->CastToClient(); + group_member->SendMarqueeMessage(type, priority, fade_in, fade_out, duration, message); + } + } + } + } else if (update_type == CZUpdateType_Raid) { + auto client_raid = entity_list.GetRaidByID(update_identifier); + if (client_raid) { + for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { + if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { + auto raid_member = client_raid->members[member_index].member->CastToClient(); + raid_member->SendMarqueeMessage(type, priority, fade_in, fade_out, duration, message); + } + } + } + } else if (update_type == CZUpdateType_Guild) { + for (auto &client: entity_list.GetClientList()) { + if (client.second->GuildID() > 0 && client.second->GuildID() == update_identifier) { + client.second->SendMarqueeMessage(type, priority, fade_in, fade_out, duration, message); + } + } + } else if (update_type == CZUpdateType_Expedition) { + for (auto &client: entity_list.GetClientList()) { + if (client.second->GetExpedition() && client.second->GetExpedition()->GetID() == update_identifier) { + client.second->SendMarqueeMessage(type, priority, fade_in, fade_out, duration, message); + } + } + } else if (update_type == CZUpdateType_ClientName) { + auto client = entity_list.GetClientByName(client_name); + if (client) { + client->SendMarqueeMessage(type, priority, fade_in, fade_out, duration, message); + } } break; } - case ServerOP_CZMarqueePlayer: + case ServerOP_CZMessage: { - CZMarqueePlayer_Struct* CZMS = (CZMarqueePlayer_Struct*) pack->pBuffer; - auto client = entity_list.GetClientByCharID(CZMS->character_id); - std::string message = CZMS->message; - if (client) { - client->SendMarqueeMessage(CZMS->type, CZMS->priority, CZMS->fade_in, CZMS->fade_out, CZMS->duration, message); + CZMessage_Struct* CZM = (CZMessage_Struct*) pack->pBuffer; + uint8 update_type = CZM->update_type; + int update_identifier = CZM->update_identifier; + uint32 type = CZM->type; + const char* message = CZM->message; + const char* client_name = CZM->client_name; + if (update_type == CZUpdateType_Character) { + auto client = entity_list.GetClientByCharID(update_identifier); + if (client) { + client->Message(type, message); + } + } else if (update_type == CZUpdateType_Group) { + auto client_group = entity_list.GetGroupByID(update_identifier); + if (client_group) { + for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { + if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { + auto group_member = client_group->members[member_index]->CastToClient(); + group_member->Message(type, message); + } + } + } + } else if (update_type == CZUpdateType_Raid) { + auto client_raid = entity_list.GetRaidByID(update_identifier); + if (client_raid) { + for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { + if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { + auto raid_member = client_raid->members[member_index].member->CastToClient(); + raid_member->Message(type, message); + } + } + } + } else if (update_type == CZUpdateType_Guild) { + for (auto &client: entity_list.GetClientList()) { + if (client.second->GuildID() > 0 && client.second->GuildID() == update_identifier) { + client.second->Message(type, message); + } + } + } else if (update_type == CZUpdateType_Expedition) { + for (auto &client: entity_list.GetClientList()) { + if (client.second->GetExpedition() && client.second->GetExpedition()->GetID() == update_identifier) { + client.second->Message(type, message); + } + } + } else if (update_type == CZUpdateType_ClientName) { + auto client = entity_list.GetClientByName(client_name); + if (client) { + client->Message(type, message); + } } break; } - case ServerOP_CZMarqueeGroup: + case ServerOP_CZMove: { - CZMarqueeGroup_Struct* CZMS = (CZMarqueeGroup_Struct*) pack->pBuffer; - auto client_group = entity_list.GetGroupByID(CZMS->group_id); - std::string message = CZMS->message; - if (client_group) { - for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { - if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { - auto group_member = client_group->members[member_index]->CastToClient(); - group_member->SendMarqueeMessage(CZMS->type, CZMS->priority, CZMS->fade_in, CZMS->fade_out, CZMS->duration, message); + CZMove_Struct* CZM = (CZMove_Struct*) pack->pBuffer; + uint8 update_type = CZM->update_type; + uint8 update_subtype = CZM->update_subtype; + int update_identifier = CZM->update_identifier; + const char* zone_short_name = CZM->zone_short_name; + uint16 instance_id = CZM->instance_id; + const char* client_name = CZM->client_name; + if (update_type == CZUpdateType_Character) { + auto client = entity_list.GetClientByCharID(update_identifier); + if (client) { + switch (update_subtype) { + case CZMoveUpdateSubtype_MoveZone: + client->MoveZone(zone_short_name); + break; + case CZMoveUpdateSubtype_MoveZoneInstance: + client->MoveZoneInstance(instance_id); + break; + } + } + } else if (update_type == CZUpdateType_Group) { + auto client_group = entity_list.GetGroupByID(update_identifier); + if (client_group) { + for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { + if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { + auto group_member = client_group->members[member_index]->CastToClient(); + switch (update_subtype) { + case CZMoveUpdateSubtype_MoveZone: + group_member->MoveZone(zone_short_name); + break; + case CZMoveUpdateSubtype_MoveZoneInstance: + group_member->MoveZoneInstance(instance_id); + break; + } + } + } + } + } else if (update_type == CZUpdateType_Raid) { + auto client_raid = entity_list.GetRaidByID(update_identifier); + if (client_raid) { + for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { + if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { + auto raid_member = client_raid->members[member_index].member->CastToClient(); + switch (update_subtype) { + case CZMoveUpdateSubtype_MoveZone: + raid_member->MoveZone(zone_short_name); + break; + case CZMoveUpdateSubtype_MoveZoneInstance: + raid_member->MoveZoneInstance(instance_id); + break; + } + } + } + } + } else if (update_type == CZUpdateType_Guild) { + for (auto &client: entity_list.GetClientList()) { + if (client.second->GuildID() > 0 && client.second->GuildID() == update_identifier) { + switch (update_subtype) { + case CZMoveUpdateSubtype_MoveZone: + client.second->MoveZone(zone_short_name); + break; + case CZMoveUpdateSubtype_MoveZoneInstance: + client.second->MoveZoneInstance(instance_id); + break; + } + } + } + } else if (update_type == CZUpdateType_Expedition) { + for (auto &client: entity_list.GetClientList()) { + if (client.second->GetExpedition() && client.second->GetExpedition()->GetID() == update_identifier) { + switch (update_subtype) { + case CZMoveUpdateSubtype_MoveZone: + client.second->MoveZone(zone_short_name); + break; + case CZMoveUpdateSubtype_MoveZoneInstance: + client.second->MoveZoneInstance(instance_id); + break; + } + } + } + } else if (update_type == CZUpdateType_ClientName) { + auto client = entity_list.GetClientByName(client_name); + if (client) { + switch (update_subtype) { + case CZMoveUpdateSubtype_MoveZone: + client->MoveZone(zone_short_name); + break; + case CZMoveUpdateSubtype_MoveZoneInstance: + client->MoveZoneInstance(instance_id); + break; } } } break; } - case ServerOP_CZMarqueeRaid: + case ServerOP_CZSetEntityVariable: { - CZMarqueeRaid_Struct* CZMS = (CZMarqueeRaid_Struct*) pack->pBuffer; - auto client_raid = entity_list.GetRaidByID(CZMS->raid_id); - std::string message = CZMS->message; - if (client_raid) { - for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { - if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { - auto raid_member = client_raid->members[member_index].member->CastToClient(); - raid_member->SendMarqueeMessage(CZMS->type, CZMS->priority, CZMS->fade_in, CZMS->fade_out, CZMS->duration, message); + CZSetEntityVariable_Struct* CZSEV = (CZSetEntityVariable_Struct*) pack->pBuffer; + uint8 update_type = CZSEV->update_type; + int update_identifier = CZSEV->update_identifier; + const char* variable_name = CZSEV->variable_name; + const char* variable_value = CZSEV->variable_value; + const char* client_name = CZSEV->client_name; + if (update_type == CZUpdateType_Character) { + auto client = entity_list.GetClientByCharID(update_identifier); + if (client) { + client->SetEntityVariable(variable_name, variable_value); + } + } else if (update_type == CZUpdateType_Group) { + auto client_group = entity_list.GetGroupByID(update_identifier); + if (client_group) { + for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { + if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { + auto group_member = client_group->members[member_index]->CastToClient(); + group_member->SetEntityVariable(variable_name, variable_value); + } } } + } else if (update_type == CZUpdateType_Raid) { + auto client_raid = entity_list.GetRaidByID(update_identifier); + if (client_raid) { + for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { + if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { + auto raid_member = client_raid->members[member_index].member->CastToClient(); + raid_member->SetEntityVariable(variable_name, variable_value); + } + } + } + } else if (update_type == CZUpdateType_Guild) { + for (auto &client: entity_list.GetClientList()) { + if (client.second->GuildID() > 0 && client.second->GuildID() == update_identifier) { + client.second->SetEntityVariable(variable_name, variable_value); + } + } + } else if (update_type == CZUpdateType_Expedition) { + for (auto &client: entity_list.GetClientList()) { + if (client.second->GetExpedition() && client.second->GetExpedition()->GetID() == update_identifier) { + client.second->SetEntityVariable(variable_name, variable_value); + } + } + } else if (update_type == CZUpdateType_ClientName) { + auto client = entity_list.GetClientByName(client_name); + if (client) { + client->SetEntityVariable(variable_name, variable_value); + } + } else if (update_type == CZUpdateType_NPC) { + auto npc = entity_list.GetNPCByNPCTypeID(update_identifier); + if (npc) { + npc->SetEntityVariable(variable_name, variable_value); + } } break; } - case ServerOP_CZMarqueeGuild: + case ServerOP_CZSignal: { - CZMarqueeGuild_Struct* CZMS = (CZMarqueeGuild_Struct*) pack->pBuffer; - std::string message = CZMS->message; + CZSignal_Struct* CZS = (CZSignal_Struct*) pack->pBuffer; + uint8 update_type = CZS->update_type; + int update_identifier = CZS->update_identifier; + uint32 signal = CZS->signal; + const char* client_name = CZS->client_name; + if (update_type == CZUpdateType_Character) { + auto client = entity_list.GetClientByCharID(update_identifier); + if (client) { + client->Signal(signal); + } + } else if (update_type == CZUpdateType_Group) { + auto client_group = entity_list.GetGroupByID(update_identifier); + if (client_group) { + for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { + if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { + auto group_member = client_group->members[member_index]->CastToClient(); + group_member->Signal(signal); + } + } + } + } else if (update_type == CZUpdateType_Raid) { + auto client_raid = entity_list.GetRaidByID(update_identifier); + if (client_raid) { + for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { + if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { + auto raid_member = client_raid->members[member_index].member->CastToClient(); + raid_member->Signal(signal); + } + } + } + } else if (update_type == CZUpdateType_Guild) { + for (auto &client: entity_list.GetClientList()) { + if (client.second->GuildID() > 0 && client.second->GuildID() == update_identifier) { + client.second->Signal(signal); + } + } + } else if (update_type == CZUpdateType_Expedition) { + for (auto &client: entity_list.GetClientList()) { + if (client.second->GetExpedition() && client.second->GetExpedition()->GetID() == update_identifier) { + client.second->Signal(signal); + } + } + } else if (update_type == CZUpdateType_ClientName) { + auto client = entity_list.GetClientByName(client_name); + if (client) { + client->Signal(signal); + } + } else if (update_type = CZUpdateType_NPC) { + auto npc = entity_list.GetNPCByNPCTypeID(update_identifier); + if (npc) { + npc->SignalNPC(signal); + } + } + break; + } + case ServerOP_WWLDoNUpdate: + { + WWLDoNUpdate_Struct* WWLU = (WWLDoNUpdate_Struct*) pack->pBuffer; + uint8 update_type = WWLU->update_type; + uint32 theme_id = WWLU->theme_id; + int points = WWLU->points; + uint8 min_status = WWLU->min_status; + uint8 max_status = WWLU->max_status; for (auto &client : entity_list.GetClientList()) { - if (client.second->GuildID() > 0 && client.second->GuildID() == CZMS->guild_id) { - client.second->SendMarqueeMessage(CZMS->type, CZMS->priority, CZMS->fade_in, CZMS->fade_out, CZMS->duration, message); - } - } - break; - } - case ServerOP_CZMessagePlayer: - { - CZMessagePlayer_Struct* CZCS = (CZMessagePlayer_Struct*) pack->pBuffer; - auto client = entity_list.GetClientByName(CZCS->character_name); - if (client) { - client->Message(CZCS->type, CZCS->message); - } - break; - } - case ServerOP_CZMessageGroup: - { - CZMessageGroup_Struct* CZGM = (CZMessageGroup_Struct*) pack->pBuffer; - auto client_group = entity_list.GetGroupByID(CZGM->group_id); - if (client_group) { - for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { - if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { - auto group_member = client_group->members[member_index]->CastToClient(); - group_member->Message(CZGM->type, CZGM->message); - } - } - } - break; - } - case ServerOP_CZMessageRaid: - { - CZMessageRaid_Struct* CZRM = (CZMessageRaid_Struct*) pack->pBuffer; - auto client_raid = entity_list.GetRaidByID(CZRM->raid_id); - if (client_raid) { - for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { - if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { - auto raid_member = client_raid->members[member_index].member->CastToClient(); - raid_member->Message(CZRM->type, CZRM->message); - } - } - } - break; - } - case ServerOP_CZMessageGuild: - { - CZMessageGuild_Struct* CZGM = (CZMessageGuild_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - if (client.second->GuildID() > 0 && client.second->GuildID() == CZGM->guild_id) { - client.second->Message(CZGM->type, CZGM->message); - } - } - break; - } - case ServerOP_CZMovePlayer: - { - CZMovePlayer_Struct* CZMP = (CZMovePlayer_Struct*) pack->pBuffer; - auto client = entity_list.GetClientByCharID(CZMP->character_id); - if (client) { - client->MoveZone(CZMP->zone_short_name); - } - break; - } - case ServerOP_CZMoveGroup: - { - CZMoveGroup_Struct* CZMG = (CZMoveGroup_Struct*) pack->pBuffer; - auto client_group = entity_list.GetGroupByID(CZMG->group_id); - if (client_group) { - for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { - if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { - auto group_member = client_group->members[member_index]->CastToClient(); - group_member->MoveZone(CZMG->zone_short_name); - } - } - } - break; - } - case ServerOP_CZMoveRaid: - { - CZMoveRaid_Struct* CZMR = (CZMoveRaid_Struct*) pack->pBuffer; - auto client_raid = entity_list.GetRaidByID(CZMR->raid_id); - if (client_raid) { - for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { - if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { - auto raid_member = client_raid->members[member_index].member->CastToClient(); - raid_member->MoveZone(CZMR->zone_short_name); - } - } - } - break; - } - case ServerOP_CZMoveGuild: - { - CZMoveGuild_Struct* CZMG = (CZMoveGuild_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - if (client.second->GuildID() > 0 && client.second->GuildID() == CZMG->guild_id) { - client.second->MoveZone(CZMG->zone_short_name); - } - } - break; - } - - case ServerOP_CZMoveInstancePlayer: - { - CZMoveInstancePlayer_Struct* CZMP = (CZMoveInstancePlayer_Struct*) pack->pBuffer; - auto client = entity_list.GetClientByCharID(CZMP->character_id); - if (client) { - client->MoveZoneInstance(CZMP->instance_id); - } - break; - } - case ServerOP_CZMoveInstanceGroup: - { - CZMoveInstanceGroup_Struct* CZMG = (CZMoveInstanceGroup_Struct*) pack->pBuffer; - auto client_group = entity_list.GetGroupByID(CZMG->group_id); - if (client_group) { - for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { - if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { - auto group_member = client_group->members[member_index]->CastToClient(); - group_member->MoveZoneInstance(CZMG->instance_id); - } - } - } - break; - } - case ServerOP_CZMoveInstanceRaid: - { - CZMoveInstanceRaid_Struct* CZMR = (CZMoveInstanceRaid_Struct*) pack->pBuffer; - auto client_raid = entity_list.GetRaidByID(CZMR->raid_id); - if (client_raid) { - for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { - if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { - auto raid_member = client_raid->members[member_index].member->CastToClient(); - raid_member->MoveZoneInstance(CZMR->instance_id); - } - } - } - break; - } - case ServerOP_CZMoveInstanceGuild: - { - CZMoveInstanceGuild_Struct* CZMG = (CZMoveInstanceGuild_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - if (client.second->GuildID() > 0 && client.second->GuildID() == CZMG->guild_id) { - client.second->MoveZoneInstance(CZMG->instance_id); - } - } - break; - } - case ServerOP_CZRemoveSpellPlayer: - { - CZRemoveSpellPlayer_Struct* CZRS = (CZRemoveSpellPlayer_Struct*) pack->pBuffer; - auto client = entity_list.GetClientByCharID(CZRS->character_id); - if (client) { - client->BuffFadeBySpellID(CZRS->spell_id); - } - break; - } - case ServerOP_CZRemoveSpellGroup: - { - CZRemoveSpellGroup_Struct* CZRS = (CZRemoveSpellGroup_Struct*) pack->pBuffer; - auto client_group = entity_list.GetGroupByID(CZRS->group_id); - if (client_group) { - for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { - if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { - auto group_member = client_group->members[member_index]->CastToClient(); - group_member->BuffFadeBySpellID(CZRS->spell_id); - } - } - } - break; - } - case ServerOP_CZRemoveSpellRaid: - { - CZRemoveSpellRaid_Struct* CZRS = (CZRemoveSpellRaid_Struct*) pack->pBuffer; - auto client_raid = entity_list.GetRaidByID(CZRS->raid_id); - if (client_raid) { - for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { - if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { - auto raid_member = client_raid->members[member_index].member->CastToClient(); - raid_member->BuffFadeBySpellID(CZRS->spell_id); - } - } - } - break; - } - case ServerOP_CZRemoveSpellGuild: - { - CZRemoveSpellGuild_Struct* CZRS = (CZRemoveSpellGuild_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - if (client.second->GuildID() > 0 && client.second->GuildID() == CZRS->guild_id) { - client.second->BuffFadeBySpellID(CZRS->spell_id); - } - } - break; - } - case ServerOP_CZSetEntityVariableByClientName: - { - CZSetEntVarByClientName_Struct* CZCS = (CZSetEntVarByClientName_Struct*) pack->pBuffer; - auto client = entity_list.GetClientByName(CZCS->character_name); - if (client) { - client->SetEntityVariable(CZCS->variable_name, CZCS->variable_value); - } - break; - } - case ServerOP_CZSetEntityVariableByGroupID: - { - CZSetEntVarByGroupID_Struct* CZCS = (CZSetEntVarByGroupID_Struct*) pack->pBuffer; - auto client_group = entity_list.GetGroupByID(CZCS->group_id); - if (client_group) { - for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { - if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { - auto group_member = client_group->members[member_index]->CastToClient(); - group_member->SetEntityVariable(CZCS->variable_name, CZCS->variable_value); - } - } - } - break; - } - case ServerOP_CZSetEntityVariableByRaidID: - { - CZSetEntVarByRaidID_Struct* CZCS = (CZSetEntVarByRaidID_Struct*) pack->pBuffer; - auto client_raid = entity_list.GetRaidByID(CZCS->raid_id); - if (client_raid) { - for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { - if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { - auto raid_member = client_raid->members[member_index].member->CastToClient(); - raid_member->SetEntityVariable(CZCS->variable_name, CZCS->variable_value); - } - } - } - break; - } - case ServerOP_CZSetEntityVariableByGuildID: - { - CZSetEntVarByGuildID_Struct* CZCS = (CZSetEntVarByGuildID_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - if (client.second->GuildID() > 0 && client.second->GuildID() == CZCS->guild_id) { - client.second->SetEntityVariable(CZCS->variable_name, CZCS->variable_value); - } - } - break; - } - case ServerOP_CZSetEntityVariableByNPCTypeID: - { - CZSetEntVarByNPCTypeID_Struct* CZM = (CZSetEntVarByNPCTypeID_Struct*) pack->pBuffer; - auto npc = entity_list.GetNPCByNPCTypeID(CZM->npctype_id); - if (npc != 0) { - npc->SetEntityVariable(CZM->variable_name, CZM->variable_value); - } - break; - } - case ServerOP_CZSignalNPC: - { - CZNPCSignal_Struct* CZCN = (CZNPCSignal_Struct*) pack->pBuffer; - auto npc = entity_list.GetNPCByNPCTypeID(CZCN->npctype_id); - if (npc != 0) { - npc->SignalNPC(CZCN->signal); - } - break; - } - case ServerOP_CZSignalClient: - { - CZClientSignal_Struct* CZCS = (CZClientSignal_Struct*) pack->pBuffer; - auto client = entity_list.GetClientByCharID(CZCS->character_id); - if (client) { - client->Signal(CZCS->signal); - } - break; - } - case ServerOP_CZSignalGroup: - { - CZGroupSignal_Struct* CZGS = (CZGroupSignal_Struct*) pack->pBuffer; - auto client_group = entity_list.GetGroupByID(CZGS->group_id); - if (client_group) { - for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { - if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { - auto group_member = client_group->members[member_index]->CastToClient(); - group_member->Signal(CZGS->signal); - } - } - } - break; - } - case ServerOP_CZSignalRaid: - { - CZRaidSignal_Struct* CZRS = (CZRaidSignal_Struct*) pack->pBuffer; - auto client_raid = entity_list.GetRaidByID(CZRS->raid_id); - if (client_raid) { - for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { - if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { - auto raid_member = client_raid->members[member_index].member->CastToClient(); - raid_member->Signal(CZRS->signal); - } - } - } - break; - } - case ServerOP_CZSignalGuild: - { - CZGuildSignal_Struct* CZGS = (CZGuildSignal_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - if (client.second->GuildID() > 0 && client.second->GuildID() == CZGS->guild_id) { - client.second->Signal(CZGS->signal); - } - } - break; - } - case ServerOP_CZSignalClientByName: - { - CZClientSignalByName_Struct* CZCS = (CZClientSignalByName_Struct*) pack->pBuffer; - auto client = entity_list.GetClientByName(CZCS->character_name); - if (client) { - client->Signal(CZCS->signal); - } - break; - } - case ServerOP_CZTaskAssignPlayer: - { - CZTaskAssignPlayer_Struct* CZTA = (CZTaskAssignPlayer_Struct*) pack->pBuffer; - auto client = entity_list.GetClientByCharID(CZTA->character_id); - if (client) { - client->AssignTask(CZTA->task_id, CZTA->npc_entity_id, CZTA->enforce_level_requirement); - } - break; - } - case ServerOP_CZTaskAssignGroup: - { - CZTaskAssignGroup_Struct* CZTA = (CZTaskAssignGroup_Struct*) pack->pBuffer; - auto client_group = entity_list.GetGroupByID(CZTA->group_id); - if (client_group) { - for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { - if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { - auto group_member = client_group->members[member_index]->CastToClient(); - group_member->AssignTask(CZTA->task_id, CZTA->npc_entity_id, CZTA->enforce_level_requirement); - } - } - } - break; - } - case ServerOP_CZTaskAssignRaid: - { - CZTaskAssignRaid_Struct* CZTA = (CZTaskAssignRaid_Struct*) pack->pBuffer; - auto client_raid = entity_list.GetRaidByID(CZTA->raid_id); - if (client_raid) { - for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { - if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { - auto raid_member = client_raid->members[member_index].member->CastToClient(); - raid_member->AssignTask(CZTA->task_id, CZTA->npc_entity_id, CZTA->enforce_level_requirement); - } - } - } - break; - } - case ServerOP_CZTaskAssignGuild: - { - CZTaskAssignGuild_Struct* CZTA = (CZTaskAssignGuild_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - if (client.second->GuildID() > 0 && client.second->GuildID() == CZTA->guild_id) { - client.second->AssignTask(CZTA->task_id, CZTA->npc_entity_id, CZTA->enforce_level_requirement); - } - } - break; - } - case ServerOP_CZTaskActivityResetPlayer: - { - CZTaskActivityResetPlayer_Struct* CZRA = (CZTaskActivityResetPlayer_Struct*) pack->pBuffer; - auto client = entity_list.GetClientByCharID(CZRA->character_id); - if (client) { - client->ResetTaskActivity(CZRA->task_id, CZRA->activity_id); - } - break; - } - case ServerOP_CZTaskActivityResetGroup: - { - CZTaskActivityResetGroup_Struct* CZRA = (CZTaskActivityResetGroup_Struct*) pack->pBuffer; - auto client_group = entity_list.GetGroupByID(CZRA->group_id); - if (client_group) { - for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { - if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { - auto group_member = client_group->members[member_index]->CastToClient(); - group_member->ResetTaskActivity(CZRA->task_id, CZRA->activity_id); - } - } - } - break; - } - case ServerOP_CZTaskActivityResetRaid: - { - CZTaskActivityResetRaid_Struct* CZRA = (CZTaskActivityResetRaid_Struct*) pack->pBuffer; - auto client_raid = entity_list.GetRaidByID(CZRA->raid_id); - if (client_raid) { - for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { - if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { - auto raid_member = client_raid->members[member_index].member->CastToClient(); - raid_member->ResetTaskActivity(CZRA->task_id, CZRA->activity_id); - } - } - } - break; - } - case ServerOP_CZTaskActivityResetGuild: - { - CZTaskActivityResetGuild_Struct* CZRA = (CZTaskActivityResetGuild_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - if (client.second->GuildID() > 0 && client.second->GuildID() == CZRA->guild_id) { - client.second->ResetTaskActivity(CZRA->task_id, CZRA->activity_id); - } - } - break; - } - case ServerOP_CZTaskActivityUpdatePlayer: - { - CZTaskActivityUpdatePlayer_Struct* CZUA = (CZTaskActivityUpdatePlayer_Struct*) pack->pBuffer; - auto client = entity_list.GetClientByCharID(CZUA->character_id); - if (client) { - client->UpdateTaskActivity(CZUA->task_id, CZUA->activity_id, CZUA->activity_count); - } - break; - } - case ServerOP_CZTaskActivityUpdateGroup: - { - CZTaskActivityUpdateGroup_Struct* CZUA = (CZTaskActivityUpdateGroup_Struct*) pack->pBuffer; - auto client_group = entity_list.GetGroupByID(CZUA->group_id); - if (client_group) { - for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { - if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { - auto group_member = client_group->members[member_index]->CastToClient(); - group_member->UpdateTaskActivity(CZUA->task_id, CZUA->activity_id, CZUA->activity_count); - } - } - } - break; - } - case ServerOP_CZTaskActivityUpdateRaid: - { - CZTaskActivityUpdateRaid_Struct* CZUA = (CZTaskActivityUpdateRaid_Struct*) pack->pBuffer; - auto client_raid = entity_list.GetRaidByID(CZUA->raid_id); - if (client_raid) { - for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { - if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { - auto raid_member = client_raid->members[member_index].member->CastToClient(); - raid_member->UpdateTaskActivity(CZUA->task_id, CZUA->activity_id, CZUA->activity_count); - } - } - } - break; - } - case ServerOP_CZTaskActivityUpdateGuild: - { - CZTaskActivityUpdateGuild_Struct* CZUA = (CZTaskActivityUpdateGuild_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - if (client.second->GuildID() > 0 && client.second->GuildID() == CZUA->guild_id) { - client.second->UpdateTaskActivity(CZUA->task_id, CZUA->activity_id, CZUA->activity_count); - } - } - break; - } - case ServerOP_CZTaskDisablePlayer: - { - CZTaskDisablePlayer_Struct* CZUA = (CZTaskDisablePlayer_Struct*) pack->pBuffer; - auto client = entity_list.GetClientByCharID(CZUA->character_id); - if (client) { - client->DisableTask(1, reinterpret_cast(CZUA->task_id)); - } - break; - } - case ServerOP_CZTaskDisableGroup: - { - CZTaskDisableGroup_Struct* CZUA = (CZTaskDisableGroup_Struct*) pack->pBuffer; - auto client_group = entity_list.GetGroupByID(CZUA->group_id); - if (client_group) { - for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { - if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { - auto group_member = client_group->members[member_index]->CastToClient(); - group_member->DisableTask(1, reinterpret_cast(CZUA->task_id)); - } - } - } - break; - } - case ServerOP_CZTaskDisableRaid: - { - CZTaskDisableRaid_Struct* CZUA = (CZTaskDisableRaid_Struct*) pack->pBuffer; - auto client_raid = entity_list.GetRaidByID(CZUA->raid_id); - if (client_raid) { - for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { - if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { - auto raid_member = client_raid->members[member_index].member->CastToClient(); - raid_member->DisableTask(1, reinterpret_cast(CZUA->task_id)); - } - } - } - break; - } - case ServerOP_CZTaskDisableGuild: - { - CZTaskDisableGuild_Struct* CZUA = (CZTaskDisableGuild_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - if (client.second->GuildID() > 0 && client.second->GuildID() == CZUA->guild_id) { - client.second->DisableTask(1, reinterpret_cast(CZUA->task_id)); - } - } - break; - } - case ServerOP_CZTaskEnablePlayer: - { - CZTaskEnablePlayer_Struct* CZUA = (CZTaskEnablePlayer_Struct*) pack->pBuffer; - auto client = entity_list.GetClientByCharID(CZUA->character_id); - if (client) { - client->EnableTask(1, reinterpret_cast(CZUA->task_id)); - } - break; - } - case ServerOP_CZTaskEnableGroup: - { - CZTaskEnableGroup_Struct* CZUA = (CZTaskEnableGroup_Struct*) pack->pBuffer; - auto client_group = entity_list.GetGroupByID(CZUA->group_id); - if (client_group) { - for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { - if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { - auto group_member = client_group->members[member_index]->CastToClient(); - group_member->EnableTask(1, reinterpret_cast(CZUA->task_id)); - } - } - } - break; - } - case ServerOP_CZTaskEnableRaid: - { - CZTaskEnableRaid_Struct* CZUA = (CZTaskEnableRaid_Struct*) pack->pBuffer; - auto client_raid = entity_list.GetRaidByID(CZUA->raid_id); - if (client_raid) { - for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { - if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { - auto raid_member = client_raid->members[member_index].member->CastToClient(); - raid_member->EnableTask(1, reinterpret_cast(CZUA->task_id)); - } - } - } - break; - } - case ServerOP_CZTaskEnableGuild: - { - CZTaskEnableGuild_Struct* CZUA = (CZTaskEnableGuild_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - if (client.second->GuildID() > 0 && client.second->GuildID() == CZUA->guild_id) { - client.second->EnableTask(1, reinterpret_cast(CZUA->task_id)); - } - } - break; - } - case ServerOP_CZTaskFailPlayer: - { - CZTaskFailPlayer_Struct* CZUA = (CZTaskFailPlayer_Struct*) pack->pBuffer; - auto client = entity_list.GetClientByCharID(CZUA->character_id); - if (client) { - client->FailTask(CZUA->task_id); - } - break; - } - case ServerOP_CZTaskFailGroup: - { - CZTaskFailGroup_Struct* CZUA = (CZTaskFailGroup_Struct*) pack->pBuffer; - auto client_group = entity_list.GetGroupByID(CZUA->group_id); - if (client_group) { - for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { - if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { - auto group_member = client_group->members[member_index]->CastToClient(); - group_member->FailTask(CZUA->task_id); - } - } - } - break; - } - case ServerOP_CZTaskFailRaid: - { - CZTaskFailRaid_Struct* CZUA = (CZTaskFailRaid_Struct*) pack->pBuffer; - auto client_raid = entity_list.GetRaidByID(CZUA->raid_id); - if (client_raid) { - for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { - if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { - auto raid_member = client_raid->members[member_index].member->CastToClient(); - raid_member->FailTask(CZUA->task_id); - } - } - } - break; - } - case ServerOP_CZTaskFailGuild: - { - CZTaskFailGuild_Struct* CZUA = (CZTaskFailGuild_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - if (client.second->GuildID() > 0 && client.second->GuildID() == CZUA->guild_id) { - client.second->FailTask(CZUA->task_id); - } - } - break; - } - case ServerOP_CZTaskRemovePlayer: - { - CZTaskRemovePlayer_Struct* CZTR = (CZTaskRemovePlayer_Struct*) pack->pBuffer; - auto client = entity_list.GetClientByCharID(CZTR->character_id); - if (client) { - client->RemoveTaskByTaskID(CZTR->task_id); - } - break; - } - case ServerOP_CZTaskRemoveGroup: - { - CZTaskRemoveGroup_Struct* CZTR = (CZTaskRemoveGroup_Struct*) pack->pBuffer; - auto client_group = entity_list.GetGroupByID(CZTR->group_id); - if (client_group) { - for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { - if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { - auto group_member = client_group->members[member_index]->CastToClient(); - group_member->RemoveTaskByTaskID(CZTR->task_id); - } - } - } - break; - } - case ServerOP_CZTaskRemoveRaid: - { - CZTaskRemoveRaid_Struct* CZTR = (CZTaskRemoveRaid_Struct*) pack->pBuffer; - auto client_raid = entity_list.GetRaidByID(CZTR->raid_id); - if (client_raid) { - for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { - if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { - auto raid_member = client_raid->members[member_index].member->CastToClient(); - raid_member->RemoveTaskByTaskID(CZTR->task_id); - } - } - } - break; - } - case ServerOP_CZTaskRemoveGuild: - { - CZTaskRemoveGuild_Struct* CZTR = (CZTaskRemoveGuild_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - if (client.second->GuildID() > 0 && client.second->GuildID() == CZTR->guild_id) { - client.second->RemoveTaskByTaskID(CZTR->task_id); - } - } - break; - } - case ServerOP_WWAssignTask: - { - WWAssignTask_Struct* WWAT = (WWAssignTask_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - auto client_status = client.second->Admin(); - if (client_status >= WWAT->min_status && (client_status <= WWAT->max_status || WWAT->max_status == 0)) { - client.second->AssignTask(WWAT->task_id, WWAT->npc_entity_id, WWAT->enforce_level_requirement); - } - } - break; - } - case ServerOP_WWCastSpell: - { - WWCastSpell_Struct* WWCS = (WWCastSpell_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - auto client_status = client.second->Admin(); - if (client_status >= WWCS->min_status && (client_status <= WWCS->max_status || WWCS->max_status == 0)) { - client.second->SpellFinished(WWCS->spell_id, client.second); - } - } - break; - } - case ServerOP_WWDisableTask: - { - WWDisableTask_Struct* WWDT = (WWDisableTask_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - auto client_status = client.second->Admin(); - if (client_status >= WWDT->min_status && (client_status <= WWDT->max_status || WWDT->max_status == 0)) { - client.second->DisableTask(1, reinterpret_cast(WWDT->task_id)); - } - } - break; - } - case ServerOP_WWEnableTask: - { - WWEnableTask_Struct* WWET = (WWEnableTask_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - auto client_status = client.second->Admin(); - if (client_status >= WWET->min_status && (client_status <= WWET->max_status || WWET->max_status == 0)) { - client.second->EnableTask(1, reinterpret_cast(WWET->task_id)); - } - } - break; - } - case ServerOP_WWFailTask: - { - WWFailTask_Struct* WWFT = (WWFailTask_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - auto client_status = client.second->Admin(); - if (client_status >= WWFT->min_status && (client_status <= WWFT->max_status || WWFT->max_status == 0)) { - client.second->FailTask(WWFT->task_id); + switch (update_type) { + case WWLDoNUpdateType_Loss: + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + client.second->AddLDoNLoss(theme_id); + } + break; + case WWLDoNUpdateType_Points: + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + client.second->UpdateLDoNPoints(theme_id, points); + } + break; + case WWLDoNUpdateType_Win: + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + client.second->AddLDoNWin(theme_id); + } + break; } } break; } case ServerOP_WWMarquee: { - WWMarquee_Struct* WWMS = (WWMarquee_Struct*) pack->pBuffer; - std::string message = WWMS->message; + WWMarquee_Struct* WWM = (WWMarquee_Struct*) pack->pBuffer; + uint32 type = WWM->type; + uint32 priority = WWM->priority; + uint32 fade_in = WWM->fade_in; + uint32 fade_out = WWM->fade_out; + uint32 duration = WWM->duration; + const char* message = WWM->message; + uint8 min_status = WWM->min_status; + uint8 max_status = WWM->max_status; for (auto &client : entity_list.GetClientList()) { - auto client_status = client.second->Admin(); - if (client_status >= WWMS->min_status && (client_status <= WWMS->max_status || WWMS->max_status == 0)) { - client.second->SendMarqueeMessage(WWMS->type, WWMS->priority, WWMS->fade_in, WWMS->fade_out, WWMS->duration, message); + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + client.second->SendMarqueeMessage(type, priority, fade_in, fade_out, duration, message); } } break; } case ServerOP_WWMessage: { - WWMessage_Struct* WWMS = (WWMessage_Struct*) pack->pBuffer; + WWMessage_Struct* WWM = (WWMessage_Struct*) pack->pBuffer; + uint32 type = WWM->type; + const char* message = WWM->message; + uint8 min_status = WWM->min_status; + uint8 max_status = WWM->max_status; for (auto &client : entity_list.GetClientList()) { - auto client_status = client.second->Admin(); - if (client_status >= WWMS->min_status && (client_status <= WWMS->max_status || WWMS->max_status == 0)) { - client.second->Message(WWMS->type, WWMS->message); + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + client.second->Message(type, message); } } break; } case ServerOP_WWMove: { - WWMove_Struct* WWMS = (WWMove_Struct*) pack->pBuffer; + WWMove_Struct* WWM = (WWMove_Struct*) pack->pBuffer; + uint8 update_type = WWM->update_type; + uint16 instance_id = WWM->instance_id; + const char* zone_short_name = WWM->zone_short_name; + uint8 min_status = WWM->min_status; + uint8 max_status = WWM->max_status; for (auto &client : entity_list.GetClientList()) { - auto client_status = client.second->Admin(); - if (client_status >= WWMS->min_status && (client_status <= WWMS->max_status || WWMS->max_status == 0)) { - client.second->MoveZone(WWMS->zone_short_name); + switch (update_type) { + case WWMoveUpdateType_MoveZone: + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + client.second->MoveZone(zone_short_name); + } + break; + case WWMoveUpdateType_MoveZoneInstance: + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + client.second->MoveZoneInstance(instance_id); + } + break; } } break; } - case ServerOP_WWMoveInstance: + case ServerOP_WWSetEntityVariable: { - WWMoveInstance_Struct* WWMS = (WWMoveInstance_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - auto client_status = client.second->Admin(); - if (client_status >= WWMS->min_status && (client_status <= WWMS->max_status || WWMS->max_status == 0)) { - client.second->MoveZoneInstance(WWMS->instance_id); + WWSetEntityVariable_Struct* WWSEV = (WWSetEntityVariable_Struct*) pack->pBuffer; + uint8 update_type = WWSEV->update_type; + const char* variable_name = WWSEV->variable_name; + const char* variable_value = WWSEV->variable_value; + uint8 min_status = WWSEV->min_status; + uint8 max_status = WWSEV->max_status; + if (update_type == WWSetEntityVariableUpdateType_Character) { + for (auto &client : entity_list.GetClientList()) { + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + client.second->SetEntityVariable(variable_name, variable_value); + } + } + } else if (update_type == WWSetEntityVariableUpdateType_NPC) { + for (auto &npc : entity_list.GetNPCList()) { + npc.second->SetEntityVariable(variable_name, variable_value); } } break; } - case ServerOP_WWRemoveSpell: + case ServerOP_WWSignal: { - WWRemoveSpell_Struct* WWRS = (WWRemoveSpell_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - auto client_status = client.second->Admin(); - if (client_status >= WWRS->min_status && (client_status <= WWRS->max_status || WWRS->max_status == 0)) { - client.second->BuffFadeBySpellID(WWRS->spell_id); + WWSignal_Struct* WWS = (WWSignal_Struct*) pack->pBuffer; + uint8 update_type = WWS->update_type; + uint32 signal = WWS->signal; + uint8 min_status = WWS->min_status; + uint8 max_status = WWS->max_status; + if (update_type == WWSignalUpdateType_Character) { + for (auto &client : entity_list.GetClientList()) { + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + client.second->Signal(signal); + } + } + } else if (update_type == WWSignalUpdateType_NPC) { + for (auto &npc : entity_list.GetNPCList()) { + npc.second->SignalNPC(signal); } } break; } - case ServerOP_WWRemoveTask: + case ServerOP_WWSpell: { - WWRemoveTask_Struct* WWRT = (WWRemoveTask_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - auto client_status = client.second->Admin(); - if (client_status >= WWRT->min_status && (client_status <= WWRT->max_status || WWRT->max_status == 0)) { - client.second->RemoveTaskByTaskID(WWRT->task_id); + WWSpell_Struct* WWS = (WWSpell_Struct*) pack->pBuffer; + uint8 update_type = WWS->update_type; + uint32 spell_id = WWS->spell_id; + uint8 min_status = WWS->min_status; + uint8 max_status = WWS->max_status; + if (update_type == WWSpellUpdateType_Cast) { + for (auto &client : entity_list.GetClientList()) { + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + client.second->SpellFinished(spell_id, client.second); + } + } + } else if (update_type == WWSpellUpdateType_Remove) { + for (auto &client : entity_list.GetClientList()) { + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + client.second->BuffFadeBySpellID(spell_id); + } } } break; } - case ServerOP_WWResetActivity: + case ServerOP_WWTaskUpdate: { - WWResetActivity_Struct* WWRA = (WWResetActivity_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - auto client_status = client.second->Admin(); - if (client_status >= WWRA->min_status && (client_status <= WWRA->max_status || WWRA->max_status == 0)) { - client.second->ResetTaskActivity(WWRA->task_id, WWRA->activity_id); - } - } - break; - } - case ServerOP_WWSetEntityVariableClient: - { - WWSetEntVarClient_Struct* WWSC = (WWSetEntVarClient_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - auto client_status = client.second->Admin(); - if (client_status >= WWSC->min_status && (client_status <= WWSC->max_status || WWSC->max_status == 0)) { - client.second->SetEntityVariable(WWSC->variable_name, WWSC->variable_value); - } - } - break; - } - case ServerOP_WWSetEntityVariableNPC: - { - WWSetEntVarNPC_Struct* WWSN = (WWSetEntVarNPC_Struct*) pack->pBuffer; - for (auto &npc : entity_list.GetNPCList()) { - npc.second->SetEntityVariable(WWSN->variable_name, WWSN->variable_value); - } - break; - } - case ServerOP_WWSignalClient: - { - WWSignalClient_Struct* WWSC = (WWSignalClient_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - auto client_status = client.second->Admin(); - if (client_status >= WWSC->min_status && (client_status <= WWSC->max_status || WWSC->max_status == 0)) { - client.second->Signal(WWSC->signal); - } - } - break; - } - case ServerOP_WWSignalNPC: - { - WWSignalNPC_Struct* WWSN = (WWSignalNPC_Struct*) pack->pBuffer; - for (auto &npc : entity_list.GetNPCList()) { - npc.second->SignalNPC(WWSN->signal); - } - break; - } - case ServerOP_WWUpdateActivity: - { - WWUpdateActivity_Struct* WWUA = (WWUpdateActivity_Struct*) pack->pBuffer; - for (auto &client : entity_list.GetClientList()) { - auto client_status = client.second->Admin(); - if (client_status >= WWUA->min_status && (client_status <= WWUA->max_status || WWUA->max_status == 0)) { - client.second->UpdateTaskActivity(WWUA->task_id, WWUA->activity_id, WWUA->activity_count); + WWTaskUpdate_Struct* WWTU = (WWTaskUpdate_Struct*) pack->pBuffer; + uint8 update_type = WWTU->update_type; + uint32 task_identifier = WWTU->task_identifier; + int task_subidentifier = WWTU->task_subidentifier; + int update_count = WWTU->update_count; + bool enforce_level_requirement = WWTU->enforce_level_requirement; + uint8 min_status = WWTU->min_status; + uint8 max_status = WWTU->max_status; + for (auto &client : entity_list.GetClientList()) { + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + switch (update_type) { + case WWTaskUpdateType_ActivityReset: + client.second->ResetTaskActivity(task_identifier, task_subidentifier); + break; + case WWTaskUpdateType_ActivityUpdate: + client.second->UpdateTaskActivity(task_identifier, task_subidentifier, update_count); + break; + case WWTaskUpdateType_AssignTask: + client.second->AssignTask(task_identifier, task_subidentifier, enforce_level_requirement); + break; + case WWTaskUpdateType_DisableTask: + client.second->DisableTask(1, reinterpret_cast(task_identifier)); + break; + case WWTaskUpdateType_EnableTask: + client.second->EnableTask(1, reinterpret_cast(task_identifier)); + break; + case WWTaskUpdateType_FailTask: + client.second->FailTask(task_identifier); + break; + case WWTaskUpdateType_RemoveTask: + client.second->RemoveTaskByTaskID(task_identifier); + break; + } } } break; @@ -2999,7 +2891,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) case ServerOP_CZClientMessageString: { auto buf = reinterpret_cast(pack->pBuffer); - Client* client = entity_list.GetClientByName(buf->character_name); + Client* client = entity_list.GetClientByName(buf->client_name); if (client) { client->MessageString(buf); } From 6a5face0aa75032a0fbdc7be9d5797c3328bb5b9 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 19 Sep 2021 19:24:04 -0400 Subject: [PATCH 203/624] [Dialogue] Add support for Dialogue Window buttons. (#1546) * [Dialogue] Add support for Dialogue Window buttons. - Also changes "mysterious" identifier to "{mysterious}". - Both button names are required for anything to show up, otherwise it defaults to Yes/No similar to Client::SendFullPopup. * Move SetEntityVariable so responses can override default button response. * Add negativeid support so you can override button two popup ID. * Fix log. * Update dialogue_window.cpp Convert button names to strings and negativeid to secondrespondid. --- zone/client.h | 6 +- zone/client_packet.cpp | 15 ++++- zone/dialogue_window.cpp | 136 +++++++++++++++++++++++++++++++++++---- 3 files changed, 141 insertions(+), 16 deletions(-) diff --git a/zone/client.h b/zone/client.h index 966d7976c..c53fbb21b 100644 --- a/zone/client.h +++ b/zone/client.h @@ -203,8 +203,10 @@ enum eInnateSkill { InnateDisabled = 255 }; -const std::string DIAWIND_RESPONSE_KEY = "diawind_npcresponse"; -const uint32 POPUPID_DIAWIND = 999; +const std::string DIAWIND_RESPONSE_ONE_KEY = "diawind_npc_response_one"; +const std::string DIAWIND_RESPONSE_TWO_KEY = "diawind_npc_response_two"; +const uint32 POPUPID_DIAWIND_ONE = 99999; +const uint32 POPUPID_DIAWIND_TWO = 100000; const uint32 POPUPID_UPDATE_SHOWSTATSWINDOW = 1000000; struct ClientReward diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 418544abc..6d5dd2314 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -11155,9 +11155,18 @@ void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app) return; break; - case POPUPID_DIAWIND: - if (EntityVariableExists(DIAWIND_RESPONSE_KEY.c_str())) { - response = GetEntityVariable(DIAWIND_RESPONSE_KEY.c_str()); + case POPUPID_DIAWIND_ONE: + if (EntityVariableExists(DIAWIND_RESPONSE_ONE_KEY.c_str())) { + response = GetEntityVariable(DIAWIND_RESPONSE_ONE_KEY.c_str()); + if (!response.empty()) { + ChannelMessageReceived(8, 0, 100, response.c_str()); + } + } + break; + + case POPUPID_DIAWIND_TWO: + if (EntityVariableExists(DIAWIND_RESPONSE_TWO_KEY.c_str())) { + response = GetEntityVariable(DIAWIND_RESPONSE_TWO_KEY.c_str()); if (!response.empty()) { ChannelMessageReceived(8, 0, 100, response.c_str()); } diff --git a/zone/dialogue_window.cpp b/zone/dialogue_window.cpp index 99c8944da..941bcbc82 100644 --- a/zone/dialogue_window.cpp +++ b/zone/dialogue_window.cpp @@ -11,7 +11,8 @@ void DialogueWindow::Render(Client *c, std::string markdown) } // zero this out - c->SetEntityVariable(DIAWIND_RESPONSE_KEY.c_str(), ""); + c->SetEntityVariable(DIAWIND_RESPONSE_ONE_KEY.c_str(), ""); + c->SetEntityVariable(DIAWIND_RESPONSE_TWO_KEY.c_str(), ""); // simple find and replace for the markdown find_replace(output, "~", "
"); @@ -32,10 +33,10 @@ void DialogueWindow::Render(Client *c, std::string markdown) // mysterious voice bool render_mysterious_voice = false; - if (markdown.find("mysterious") != std::string::npos) { + if (markdown.find("{mysterious}") != std::string::npos) { render_mysterious_voice = true; LogDiaWind("Client [{}] Rendering mysterious voice", c->GetCleanName()); - find_replace(output, "mysterious", ""); + find_replace(output, "{mysterious}", ""); } // noquotes @@ -98,10 +99,10 @@ void DialogueWindow::Render(Client *c, std::string markdown) } } - uint32 popup_id = POPUPID_DIAWIND; - uint32 negative_id = 0; - char *button_name_0 = nullptr; - char *button_name_1 = nullptr; + uint32 popup_id = POPUPID_DIAWIND_ONE; + uint32 negative_id = POPUPID_DIAWIND_TWO; + std::string button_one_name; + std::string button_two_name; uint32 sound_controls = 0; // window type @@ -171,6 +172,96 @@ void DialogueWindow::Render(Client *c, std::string markdown) } } + // secondresponseid + std::string secondresponseid; + if (markdown.find("secondresponseid") != std::string::npos) { + LogDiaWind("Client [{}] Rendering secondresponseid option", c->GetCleanName()); + + auto first_split = split_string(output, "secondresponseid:"); + if (!first_split.empty()) { + auto second_split = split_string(first_split[1], " "); + if (!second_split.empty()) { + secondresponseid = second_split[0]; + LogDiaWindDetail("Client [{}] Rendering secondresponseid option secondresponseid [{}]", c->GetCleanName(), secondresponseid); + } + + if (first_split[1].length() == 1) { + secondresponseid = first_split[1]; + LogDiaWindDetail( + "Client [{}] Rendering secondresponseid (end) option secondresponseid [{}]", + c->GetCleanName(), + secondresponseid + ); + } + + find_replace(output, fmt::format("secondresponseid:{}", secondresponseid), ""); + + if (!secondresponseid.empty()) { + negative_id = (StringIsNumber(secondresponseid) ? std::atoi(secondresponseid.c_str()) : 0); + } + } + } + + // Buttons Text + std::string button_one; + std::string button_two; + if ( + markdown.find("button_one") != std::string::npos && + markdown.find("button_two") != std::string::npos + ) { + LogDiaWind("Client [{}] Rendering button_one option.", c->GetCleanName()); + + auto one_first_split = split_string(output, "button_one:"); + if (!one_first_split.empty()) { + auto one_second_split = split_string(one_first_split[1], " "); + if (!one_second_split.empty()) { + button_one = one_second_split[0]; + LogDiaWindDetail("Client [{}] Rendering button_one option button_one [{}]", c->GetCleanName(), button_one); + } + + if (one_first_split[1].length() == 1) { + button_one = one_first_split[1]; + LogDiaWindDetail( + "Client [{}] Rendering button_one (end) option button_one [{}]", + c->GetCleanName(), + button_one + ); + } + + find_replace(output, fmt::format("button_one:{}", button_one), ""); + + if (!button_one.empty()) { + button_one_name = button_one.c_str(); + } + } + + LogDiaWind("Client [{}] Rendering button_two option.", c->GetCleanName()); + + auto two_first_split = split_string(output, "button_two:"); + if (!two_first_split.empty()) { + auto two_second_split = split_string(two_first_split[1], " "); + if (!two_second_split.empty()) { + button_two = two_second_split[0]; + LogDiaWindDetail("Client [{}] Rendering button_two option button_two [{}]", c->GetCleanName(), button_two); + } + + if (two_first_split[1].length() == 1) { + button_two = two_first_split[1]; + LogDiaWindDetail( + "Client [{}] Rendering button_two (end) option button_two [{}]", + c->GetCleanName(), + button_two + ); + } + + find_replace(output, fmt::format("button_two:{}", button_two), ""); + + if (!button_two.empty()) { + button_two_name = button_two.c_str(); + } + } + } + // bracket responses std::vector responses; std::vector bracket_responses; @@ -209,13 +300,25 @@ void DialogueWindow::Render(Client *c, std::string markdown) } } + // Placed here to allow silent message or other message to override default for custom values. + if (!button_one_name.empty() && !button_two_name.empty()) { + c->SetEntityVariable( + DIAWIND_RESPONSE_ONE_KEY.c_str(), + button_one_name.c_str() + ); + c->SetEntityVariable( + DIAWIND_RESPONSE_TWO_KEY.c_str(), + button_two_name.c_str() + ); + } + // handle silent prompts from the [> silent syntax std::string silent_message; if (responses.empty() && markdown.find('[') != std::string::npos && markdown.find('>') != std::string::npos) { silent_message = get_between(output, "[", ">"); // temporary and used during the response - c->SetEntityVariable(DIAWIND_RESPONSE_KEY.c_str(), silent_message.c_str()); + c->SetEntityVariable(DIAWIND_RESPONSE_ONE_KEY.c_str(), silent_message.c_str()); // pop the silent message off find_replace(output, fmt::format("[{}>", silent_message), ""); @@ -225,7 +328,7 @@ void DialogueWindow::Render(Client *c, std::string markdown) silent_message = responses[0]; // temporary and used during the response - c->SetEntityVariable(DIAWIND_RESPONSE_KEY.c_str(), silent_message.c_str()); + c->SetEntityVariable(DIAWIND_RESPONSE_ONE_KEY.c_str(), silent_message.c_str()); // pop the silent message off find_replace(output, fmt::format("[{}]", silent_message), ""); @@ -282,6 +385,17 @@ void DialogueWindow::Render(Client *c, std::string markdown) ); } + if (!button_one_name.empty() && !button_two_name.empty()) { + click_response = fmt::format( + "Click [{}] to respond with [{}]...
" + "Click [{}] to respond with [{}]...
", + button_one_name, + button_one_name, + button_two_name, + button_two_name + ); + } + // post processing of color markdowns // {spring_green_1} = if (markdown.find('{') != std::string::npos && markdown.find('}') != std::string::npos) { @@ -325,8 +439,8 @@ void DialogueWindow::Render(Client *c, std::string markdown) negative_id, window_type, window_expire_seconds, - button_name_0, - button_name_1, + button_one_name.c_str(), + button_two_name.c_str(), sound_controls ); From c0de17817358fa8668665c3e67b5cda92a36d57a Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 19 Sep 2021 19:32:21 -0400 Subject: [PATCH 204/624] [Commands] Overhauled #npcedit. (#1538) * [Commands] Overhauled #npcedit. - Added missing columns like untargetable, show_name, exp_mod, etc. - Put stats in order of column appearance in table within help message and within code. - Converted StringFormat to fmt::format. - Added a GetGenderName() helper method. - Prettified response messages of nearly every #npcedit option. All tested and ready to go. Would like input about possibly changing some of the command arguments to match the table column names more closely, example being "spell" should be "npc_spells_id". * Cleanup. * Fix indentation. --- common/races.cpp | 12 + common/races.h | 1 + zone/command.cpp | 1432 +++++++++++++++++++++++++-------------------- zone/questmgr.cpp | 9 +- 4 files changed, 808 insertions(+), 646 deletions(-) diff --git a/common/races.cpp b/common/races.cpp index 68f4717b6..bb7cce9c0 100644 --- a/common/races.cpp +++ b/common/races.cpp @@ -2232,3 +2232,15 @@ bool PlayerAppearance::IsValidWoad(uint16 race_id, uint8 gender_id, uint8 woad_v } return false; } + +const char* GetGenderName(uint32 gender_id) { + const char* gender_name = "Unknown"; + if (gender_id == MALE) { + gender_name = "Male"; + } else if (gender_id == FEMALE) { + gender_name = "Female"; + } else if (gender_id == NEUTER) { + gender_name = "Neuter"; + } + return gender_name; +} \ No newline at end of file diff --git a/common/races.h b/common/races.h index b492fb03a..73c6ba634 100644 --- a/common/races.h +++ b/common/races.h @@ -851,6 +851,7 @@ const char* GetRaceIDName(uint16 race_id); const char* GetPlayerRaceName(uint32 player_race_value); +const char* GetGenderName(uint32 gender_id); uint32 GetPlayerRaceValue(uint16 race_id); uint32 GetPlayerRaceBit(uint16 race_id); diff --git a/zone/command.cpp b/zone/command.cpp index 1f7ec6b82..ab18d63ac 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -1087,7 +1087,7 @@ void command_npcloot(Client *c, const Seperator *sep) if ((atoi(sep->arg[2]) < 34465 && atoi(sep->arg[2]) >= 0) && (atoi(sep->arg[3]) < 34465 && atoi(sep->arg[3]) >= 0) && (atoi(sep->arg[4]) < 34465 && atoi(sep->arg[4]) >= 0) && (atoi(sep->arg[5]) < 34465 && atoi(sep->arg[5]) >= 0)) { c->GetTarget()->CastToNPC()->AddCash(atoi(sep->arg[5]), atoi(sep->arg[4]), atoi(sep->arg[3]), atoi(sep->arg[2])); - c->Message(Chat::White, "Set %i Platinum, %i Gold, %i Silver, and %i Copper as %s's money.", atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), atoi(sep->arg[5]), c->GetTarget()->GetName()); + c->Message(Chat::White, "Set %i Platinum, %i Gold, %i Silver, and %i Copper as %s's money.", atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), atoi(sep->arg[5]), c->GetTarget()->GetName()); } else c->Message(Chat::White, "Error: #npcloot money: Values must be between 0-34465."); @@ -1545,7 +1545,7 @@ void command_delpetition(Client *c, const Seperator *sep) } c->Message(Chat::Red,"Attempting to delete petition number: %i", atoi(sep->argplus[1])); - std::string query = StringFormat("DELETE FROM petitions WHERE petid = %i", atoi(sep->argplus[1])); + std::string query = StringFormat("DELETE FROM petitions WHERE petid = %i", atoi(sep->argplus[1])); auto results = database.QueryDatabase(query); if (!results.Success()) return; @@ -2006,7 +2006,7 @@ void command_npccast(Client *c, const Seperator *sep) if (spelltar) c->GetTarget()->CastSpell(atoi(sep->arg[2]), spelltar->GetID()); else - c->Message(Chat::White, "Error: target ID %i not found", atoi(sep->arg[1])); + c->Message(Chat::White, "Error: target ID %i not found", atoi(sep->arg[1])); } else c->Message(Chat::White, "Usage: (needs NPC targeted) #npccast targetname/entityid spellid"); @@ -3592,7 +3592,7 @@ void command_npctypespawn(Client *c, const Seperator *sep) entity_list.AddNPC(npc); } else - c->Message(Chat::White, "NPC Type %i not found", atoi(sep->arg[1])); + c->Message(Chat::White, "NPC Type %i not found", atoi(sep->arg[1])); } else c->Message(Chat::White, "Usage: #npctypespawn npctypeid factionid"); @@ -5959,7 +5959,7 @@ void command_repop(Client *c, const Seperator *sep) return; } - c->Message(Chat::White, "Zone depoped. Repop in %i seconds", atoi(sep->arg[timearg])); + c->Message(Chat::White, "Zone depoped. Repop in %i seconds", atoi(sep->arg[timearg])); zone->Repop(atoi(sep->arg[timearg]) * 1000); zone->spawn2_timer.Trigger(); @@ -7562,7 +7562,7 @@ void command_face(Client *c, const Seperator *sep) EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, DrakkinHeritage, DrakkinTattoo, DrakkinDetails); - c->Message(Chat::White,"Face = %i", atoi(sep->arg[1])); + c->Message(Chat::White,"Face = %i", atoi(sep->arg[1])); } } @@ -7625,7 +7625,7 @@ void command_details(Client *c, const Seperator *sep) EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, DrakkinHeritage, DrakkinTattoo, DrakkinDetails); - c->Message(Chat::White,"Details = %i", atoi(sep->arg[1])); + c->Message(Chat::White,"Details = %i", atoi(sep->arg[1])); } } @@ -7656,7 +7656,7 @@ void command_heritage(Client *c, const Seperator *sep) EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, DrakkinHeritage, DrakkinTattoo, DrakkinDetails); - c->Message(Chat::White,"Heritage = %i", atoi(sep->arg[1])); + c->Message(Chat::White,"Heritage = %i", atoi(sep->arg[1])); } } @@ -7687,7 +7687,7 @@ void command_tattoo(Client *c, const Seperator *sep) EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, DrakkinHeritage, DrakkinTattoo, DrakkinDetails); - c->Message(Chat::White,"Tattoo = %i", atoi(sep->arg[1])); + c->Message(Chat::White,"Tattoo = %i", atoi(sep->arg[1])); } } @@ -7718,7 +7718,7 @@ void command_helm(Client *c, const Seperator *sep) EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, DrakkinHeritage, DrakkinTattoo, DrakkinDetails); - c->Message(Chat::White,"Helm = %i", atoi(sep->arg[1])); + c->Message(Chat::White,"Helm = %i", atoi(sep->arg[1])); } } @@ -7749,7 +7749,7 @@ void command_hair(Client *c, const Seperator *sep) EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, DrakkinHeritage, DrakkinTattoo, DrakkinDetails); - c->Message(Chat::White,"Hair = %i", atoi(sep->arg[1])); + c->Message(Chat::White,"Hair = %i", atoi(sep->arg[1])); } } @@ -7780,7 +7780,7 @@ void command_haircolor(Client *c, const Seperator *sep) EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, DrakkinHeritage, DrakkinTattoo, DrakkinDetails); - c->Message(Chat::White,"Hair Color = %i", atoi(sep->arg[1])); + c->Message(Chat::White,"Hair Color = %i", atoi(sep->arg[1])); } } @@ -7811,7 +7811,7 @@ void command_beard(Client *c, const Seperator *sep) EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, DrakkinHeritage, DrakkinTattoo, DrakkinDetails); - c->Message(Chat::White,"Beard = %i", atoi(sep->arg[1])); + c->Message(Chat::White,"Beard = %i", atoi(sep->arg[1])); } } @@ -7842,7 +7842,7 @@ void command_beardcolor(Client *c, const Seperator *sep) EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, DrakkinHeritage, DrakkinTattoo, DrakkinDetails); - c->Message(Chat::White,"Beard Color = %i", atoi(sep->arg[1])); + c->Message(Chat::White,"Beard Color = %i", atoi(sep->arg[1])); } } @@ -8324,7 +8324,7 @@ void command_givemoney(Client *c, const Seperator *sep) else { //TODO: update this to the client, otherwise the client doesn't show any weight change until you zone, move an item, etc c->GetTarget()->CastToClient()->AddMoneyToPP(atoi(sep->arg[4]), atoi(sep->arg[3]), atoi(sep->arg[2]), atoi(sep->arg[1]), true); - c->Message(Chat::White, "Added %i Platinum, %i Gold, %i Silver, and %i Copper to %s's inventory.", atoi(sep->arg[1]), atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), c->GetTarget()->GetName()); + c->Message(Chat::White, "Added %i Platinum, %i Gold, %i Silver, and %i Copper to %s's inventory.", atoi(sep->arg[1]), atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), c->GetTarget()->GetName()); } } @@ -9081,709 +9081,865 @@ void command_npcedit(Client *c, const Seperator *sep) if (strcasecmp(sep->arg[1], "help") == 0) { c->Message(Chat::White, "Help File for #npcedit. Syntax for commands are:"); - c->Message(Chat::White, "#npcedit Name - Sets an NPC's name"); - c->Message(Chat::White, "#npcedit Lastname - Sets an NPC's lastname"); - c->Message(Chat::White, "#npcedit Level - Sets an NPC's level"); - c->Message(Chat::White, "#npcedit Maxlevel - Sets an NPC's maximum level"); - c->Message(Chat::White, "#npcedit Race - Sets an NPC's race"); - c->Message(Chat::White, "#npcedit Class - Sets an NPC's class"); - c->Message(Chat::White, "#npcedit Bodytype - Sets an NPC's bodytype"); - c->Message(Chat::White, "#npcedit HP - Sets an NPC's hitpoints"); - c->Message(Chat::White, "#npcedit Gender - Sets an NPC's gender"); - c->Message(Chat::White, "#npcedit Texture - Sets an NPC's texture"); - c->Message(Chat::White, "#npcedit Helmtexture - Sets an NPC's helmet texture"); - c->Message(Chat::White, "#npcedit Armtexture - Sets an NPC's arm texture"); - c->Message(Chat::White, "#npcedit Bracertexture - Sets an NPC's bracer texture"); - c->Message(Chat::White, "#npcedit Handtexture - Sets an NPC's hand texture"); - c->Message(Chat::White, "#npcedit Legtexture - Sets an NPC's leg texture"); - c->Message(Chat::White, "#npcedit Feettexture - Sets an NPC's feettexture"); - c->Message(Chat::White, "#npcedit Herosforgemodel - Sets an NPC's Hero's Forge Model"); - c->Message(Chat::White, "#npcedit Size - Sets an NPC's size"); - c->Message(Chat::White, "#npcedit Hpregen - Sets an NPC's hitpoint regen rate per tick"); - c->Message(Chat::White, "#npcedit Manaregen - Sets an NPC's mana regen rate per tick"); - c->Message(Chat::White, "#npcedit Loottable - Sets the loottable ID for an NPC "); - c->Message(Chat::White, "#npcedit Merchantid - Sets the merchant ID for an NPC"); - c->Message(Chat::White, "#npcedit alt_currency_id - Sets the Alternate Currency ID for an alterative currency Merchant"); - c->Message(Chat::White, "#npcedit npc_spells_effects_id - Sets the NPC Spell Effects ID"); - c->Message(Chat::White, "#npcedit adventure_template_id - Sets the NPC's Adventure Template ID"); - c->Message(Chat::White, "#npcedit trap_template - Sets the NPC's Trap Template ID"); - c->Message(Chat::White, "#npcedit special_abilities - Sets the NPC's Special Abilities"); - c->Message(Chat::White, "#npcedit Spell - Sets the npc spells list ID for an NPC"); - c->Message(Chat::White, "#npcedit Faction - Sets the NPC's faction id"); - c->Message(Chat::White, "#npcedit Damage - Sets an NPC's damage"); - c->Message(Chat::White, "#npcedit Meleetype - Sets an NPC's melee types"); - c->Message(Chat::White, "#npcedit Rangedtype - Sets an NPC's ranged type"); - c->Message(Chat::White, "#npcedit Ammoidfile - Sets an NPC's ammo id file"); - c->Message(Chat::White, "#npcedit Aggroradius - Sets an NPC's aggro radius"); - c->Message(Chat::White, "#npcedit Assistradius - Sets an NPC's assist radius"); - c->Message(Chat::White, "#npcedit Social - Set to 1 if an NPC should assist others on its faction"); - c->Message(Chat::White, "#npcedit Runspeed - Sets an NPC's run speed"); - c->Message(Chat::White, "#npcedit Walkspeed - Sets an NPC's walk speed"); - c->Message(Chat::White, "#npcedit AGI - Sets an NPC's Agility"); - c->Message(Chat::White, "#npcedit CHA - Sets an NPC's Charisma"); - c->Message(Chat::White, "#npcedit DEX - Sets an NPC's Dexterity"); - c->Message(Chat::White, "#npcedit INT - Sets an NPC's Intelligence"); - c->Message(Chat::White, "#npcedit STA - Sets an NPC's Stamina"); - c->Message(Chat::White, "#npcedit STR - Sets an NPC's Strength"); - c->Message(Chat::White, "#npcedit WIS - Sets an NPC's Wisdom"); - c->Message(Chat::White, "#npcedit MR - Sets an NPC's Magic Resistance"); - c->Message(Chat::White, "#npcedit PR - Sets an NPC's Poison Resistance"); - c->Message(Chat::White, "#npcedit DR - Sets an NPC's Disease Resistance"); - c->Message(Chat::White, "#npcedit FR - Sets an NPC's Fire Resistance"); - c->Message(Chat::White, "#npcedit CR - Sets an NPC's Cold Resistance"); - c->Message(Chat::White, "#npcedit Corrup - Sets an NPC's Corruption Resistance"); - c->Message(Chat::White, "#npcedit PhR - Sets and NPC's Physical Resistance"); - c->Message(Chat::White, "#npcedit Seeinvis - Sets an NPC's ability to see invis"); - c->Message(Chat::White, "#npcedit Seeinvisundead - Sets an NPC's ability to see through invis vs. undead"); - c->Message(Chat::White, "#npcedit Seehide - Sets an NPC's ability to see through hide"); - c->Message(Chat::White, "#npcedit Seeimprovedhide - Sets an NPC's ability to see through improved hide"); - c->Message(Chat::White, "#npcedit AC - Sets an NPC's Armor Class"); - c->Message(Chat::White, "#npcedit ATK - Sets an NPC's Attack"); - c->Message(Chat::White, "#npcedit Accuracy - Sets an NPC's Accuracy"); - c->Message(Chat::White, "#npcedit Avoidance - Sets an NPC's Avoidance"); - c->Message(Chat::White, "#npcedit npcaggro - Sets an NPC's npc_aggro flag"); - c->Message(Chat::White, "#npcedit qglobal - Sets an NPC's quest global flag"); - c->Message(Chat::White, "#npcedit spawn_limit - Sets an NPC's spawn limit counter"); - c->Message(Chat::White, "#npcedit Attackspeed - Sets an NPC's attack speed modifier"); - c->Message(Chat::White, "#npcedit Attackdelay - Sets an NPC's attack delay"); - c->Message(Chat::White, "#npcedit Attackcount - Sets an NPC's attack count"); - c->Message(Chat::White, "#npcedit findable - Sets an NPC's findable flag"); - c->Message(Chat::White, "#npcedit trackable - Sets an NPC's trackable flag"); - c->Message(Chat::White, "#npcedit weapon - Sets an NPC's primary and secondary weapon model"); - c->Message(Chat::White, "#npcedit featuresave - Saves all current facial features to the database"); - c->Message(Chat::White, "#npcedit color - Sets an NPC's red, green, and blue armor tint"); - c->Message(Chat::White, "#npcedit armortint_id - Set an NPC's Armor tint ID"); - c->Message(Chat::White, "#npcedit setanimation - Set an NPC's animation on spawn (Stored in spawn2 table)"); - c->Message(Chat::White, "#npcedit scalerate - Set an NPC's scaling rate"); - c->Message(Chat::White, "#npcedit healscale - Set an NPC's heal scaling rate"); - c->Message(Chat::White, "#npcedit spellscale - Set an NPC's spell scaling rate"); - c->Message(Chat::White, "#npcedit no_target - Set an NPC's ability to be targeted with the target hotkey"); - c->Message(Chat::White, "#npcedit version - Set an NPC's version"); - c->Message(Chat::White, "#npcedit slow_mitigation - Set an NPC's slow mitigation"); - c->Message(Chat::White, "#npcedit flymode - Set an NPC's flymode [0 = ground, 1 = flying, 2 = levitate, 3 = water, 4 = floating]"); - c->Message(Chat::White, "#npcedit raidtarget - Set an NPCs raid_target field"); - c->Message(Chat::White, "#npcedit rarespawn - Set an NPCs rare flag"); - c->Message(Chat::White, "#npcedit respawntime - Set an NPCs respawn timer in seconds"); - + c->Message(Chat::White, "#npcedit name - Sets an NPC's Name"); + c->Message(Chat::White, "#npcedit lastname - Sets an NPC's Lastname"); + c->Message(Chat::White, "#npcedit level - Sets an NPC's Level"); + c->Message(Chat::White, "#npcedit race - Sets an NPC's Race"); + c->Message(Chat::White, "#npcedit class - Sets an NPC's Class"); + c->Message(Chat::White, "#npcedit bodytype - Sets an NPC's Bodytype"); + c->Message(Chat::White, "#npcedit hp - Sets an NPC's Hitpoints"); + c->Message(Chat::White, "#npcedit mana - Sets an NPC's Mana"); + c->Message(Chat::White, "#npcedit gender - Sets an NPC's Gender"); + c->Message(Chat::White, "#npcedit texture - Sets an NPC's Texture"); + c->Message(Chat::White, "#npcedit helmtexture - Sets an NPC's Helmet Texture"); + c->Message(Chat::White, "#npcedit herosforgemodel - Sets an NPC's Hero's Forge Model"); + c->Message(Chat::White, "#npcedit size - Sets an NPC's Size"); + c->Message(Chat::White, "#npcedit hpregen - Sets an NPC's Hitpoints Regeneration Rate Per Tick"); + c->Message(Chat::White, "#npcedit manaregen - Sets an NPC's Mana Regeneration Rate Per Tick"); + c->Message(Chat::White, "#npcedit loottable - Sets an NPC's Loottable ID"); + c->Message(Chat::White, "#npcedit merchantid - Sets an NPC's Merchant ID"); + c->Message(Chat::White, "#npcedit alt_currency_id - Sets an NPC's Alternate Currency ID"); + c->Message(Chat::White, "#npcedit spell - Sets an NPC's Spells List ID"); + c->Message(Chat::White, "#npcedit npc_spells_effects_id - Sets an NPC's Spell Effects ID"); + c->Message(Chat::White, "#npcedit faction - Sets an NPC's Faction ID"); + c->Message(Chat::White, "#npcedit adventure_template_id - Sets an NPC's Adventure Template ID"); + c->Message(Chat::White, "#npcedit trap_template - Sets an NPC's Trap Template ID"); + c->Message(Chat::White, "#npcedit damage [minimum] [maximum] - Sets an NPC's Damage"); + c->Message(Chat::White, "#npcedit attackcount - Sets an NPC's Attack Count"); + c->Message(Chat::White, "#npcedit special_attacks - Sets an NPC's Special Attacks"); + c->Message(Chat::White, "#npcedit special_abilities - Sets an NPC's Special Abilities"); + c->Message(Chat::White, "#npcedit aggroradius - Sets an NPC's Aggro Radius"); + c->Message(Chat::White, "#npcedit assistradius - Sets an NPC's Assist Radius"); + c->Message(Chat::White, "#npcedit featuresave - Saves an NPC's current facial features to the database"); + c->Message(Chat::White, "#npcedit armortint_id - Sets an NPC's Armor Tint ID"); + c->Message(Chat::White, "#npcedit color [red] [green] [blue] - Sets an NPC's Red, Green, and Blue armor tint"); + c->Message(Chat::White, "#npcedit ammoidfile - Sets an NPC's Ammo ID File"); + c->Message(Chat::White, "#npcedit weapon [primary_model] [secondary_model] - Sets an NPC's Primary and Secondary Weapon Model"); + c->Message(Chat::White, "#npcedit meleetype [primary_type] [secondary_type] - Sets an NPC's Melee Types"); + c->Message(Chat::White, "#npcedit rangedtype - Sets an NPC's Ranged Type"); + c->Message(Chat::White, "#npcedit runspeed - Sets an NPC's Run Speed"); + c->Message(Chat::White, "#npcedit mr - Sets an NPC's Magic Resistance"); + c->Message(Chat::White, "#npcedit pr - Sets an NPC's Poison Resistance"); + c->Message(Chat::White, "#npcedit dr - Sets an NPC's Disease Resistance"); + c->Message(Chat::White, "#npcedit fr - Sets an NPC's Fire Resistance"); + c->Message(Chat::White, "#npcedit cr - Sets an NPC's Cold Resistance"); + c->Message(Chat::White, "#npcedit corrup - Sets an NPC's Corruption Resistance"); + c->Message(Chat::White, "#npcedit phr - Sets and NPC's Physical Resistance"); + c->Message(Chat::White, "#npcedit seeinvis - Sets an NPC's See Invisible Flag [0 = Cannot See Invisible, 1 = Can See Invisible]"); + c->Message(Chat::White, "#npcedit seeinvisundead - Sets an NPC's See Invisible vs. Undead Flag [0 = Cannot See Invisible vs. Undead, 1 = Can See Invisible vs. Undead]"); + c->Message(Chat::White, "#npcedit qglobal - Sets an NPC's Quest Global Flag [0 = Quest Globals Off, 1 = Quest Globals On]"); + c->Message(Chat::White, "#npcedit ac - Sets an NPC's Armor Class"); + c->Message(Chat::White, "#npcedit npcaggro - Sets an NPC's NPC Aggro Flag [0 = Aggro NPCs Off, 1 = Aggro NPCs On]"); + c->Message(Chat::White, "#npcedit spawn_limit - Sets an NPC's Spawn Limit Counter"); + c->Message(Chat::White, "#npcedit attackspeed - Sets an NPC's Attack Speed Modifier"); + c->Message(Chat::White, "#npcedit attackdelay - Sets an NPC's Attack Delay"); + c->Message(Chat::White, "#npcedit findable - Sets an NPC's Findable Flag [0 = Not Findable, 1 = Findable]"); + c->Message(Chat::White, "#npcedit str - Sets an NPC's Strength"); + c->Message(Chat::White, "#npcedit sta - Sets an NPC's Stamina"); + c->Message(Chat::White, "#npcedit dex - Sets an NPC's Dexterity"); + c->Message(Chat::White, "#npcedit agi - Sets an NPC's Agility"); + c->Message(Chat::White, "#npcedit int - Sets an NPC's Intelligence"); + c->Message(Chat::White, "#npcedit wis - Sets an NPC's Wisdom"); + c->Message(Chat::White, "#npcedit cha - Sets an NPC's Charisma"); + c->Message(Chat::White, "#npcedit seehide - Sets an NPC's See Hide Flag [0 = Cannot See Hide, 1 = Can See Hide]"); + c->Message(Chat::White, "#npcedit seeimprovedhide - Sets an NPC's See Improved Hide Flag [0 = Cannot See Improved Hide, 1 = Can See Improved Hide]"); + c->Message(Chat::White, "#npcedit trackable - Sets an NPC's Trackable Flag [0 = Not Trackable, 1 = Trackable]"); + c->Message(Chat::White, "#npcedit atk - Sets an NPC's Attack"); + c->Message(Chat::White, "#npcedit accuracy - Sets an NPC's Accuracy"); + c->Message(Chat::White, "#npcedit avoidance - Sets an NPC's Avoidance"); + c->Message(Chat::White, "#npcedit slow_mitigation - Sets an NPC's Slow Mitigation"); + c->Message(Chat::White, "#npcedit version - Sets an NPC's Version"); + c->Message(Chat::White, "#npcedit maxlevel - Sets an NPC's Maximum Level"); + c->Message(Chat::White, "#npcedit scalerate - Sets an NPC's Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]"); + c->Message(Chat::White, "#npcedit spellscale - Sets an NPC's Spell Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]"); + c->Message(Chat::White, "#npcedit healscale - Sets an NPC's Heal Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]"); + c->Message(Chat::White, "#npcedit no_target - Sets an NPC's No Target Hotkey Flag [0 = Not Targetable with Target Hotkey, 1 = Targetable with Target Hotkey]"); + c->Message(Chat::White, "#npcedit raidtarget - Sets an NPC's Raid Target Flag [0 = Not a Raid Target, 1 = Raid Target]"); + c->Message(Chat::White, "#npcedit armtexture - Sets an NPC's Arm Texture"); + c->Message(Chat::White, "#npcedit bracertexture - Sets an NPC's Bracer Texture"); + c->Message(Chat::White, "#npcedit handtexture - Sets an NPC's Hand Texture"); + c->Message(Chat::White, "#npcedit legtexture - Sets an NPC's Leg Texture"); + c->Message(Chat::White, "#npcedit feettexture - Sets an NPC's Feet Texture"); + c->Message(Chat::White, "#npcedit walkspeed - Sets an NPC's Walk Speed"); + c->Message(Chat::White, "#npcedit show_name - Sets an NPC's Show Name Flag [0 = Hidden, 1 = Shown]"); + c->Message(Chat::White, "#npcedit untargetable - Sets an NPC's Untargetable Flag [0 = Targetable, 1 = Untargetable]"); + c->Message(Chat::White, "#npcedit charm_ac - Sets an NPC's Armor Class while Charmed"); + c->Message(Chat::White, "#npcedit charm_min_dmg - Sets an NPC's Minimum Damage while Charmed"); + c->Message(Chat::White, "#npcedit charm_max_dmg - Sets an NPC's Max Damage while Charmed"); + c->Message(Chat::White, "#npcedit charm_attack_delay - Sets an NPC's Attack Delay while Charmed"); + c->Message(Chat::White, "#npcedit charm_accuracy_rating - Sets an NPC's Accuracy Rating while Charmed"); + c->Message(Chat::White, "#npcedit charm_avoidance_rating - Sets an NPC's Avoidance Rating while Charmed"); + c->Message(Chat::White, "#npcedit charm_atk - Sets an NPC's Attack while Charmed"); + c->Message(Chat::White, "#npcedit skip_global_loot - Sets an NPC's Skip Global Loot Flag [0 = Don't Skip, 1 = Skip"); + c->Message(Chat::White, "#npcedit rarespawn - Sets an NPC's Rare Spawn Flag [0 = Not a Rare Spawn, 1 = Rare Spawn]"); + c->Message(Chat::White, "#npcedit stuck_behavior - Sets an NPC's Stuck Behavior [0 = Run to Target, 1 = Warp to Target, 2 = Take No Action, 3 = Evade Combat]"); + c->Message(Chat::White, "#npcedit flymode - Sets an NPC's flymode [0 = Ground, 1 = Flying, 2 = Levitating, 3 = Water, 4 = Floating, 5 = Levitating While Running]"); + c->Message(Chat::White, "#npcedit always_aggro - Sets an NPC's Always Aggro Flag [0 = Does not Always Aggro, 1 = Always Aggro]"); + c->Message(Chat::White, "#npcedit exp_mod - Sets an NPC's Experience Modifier [50 = 50%, 100 = 100%, 200 = 200%]"); + c->Message(Chat::White, "#npcedit setanimation - Sets an NPC's Animation on Spawn (Stored in spawn2 table)"); + c->Message(Chat::White, "#npcedit respawntime - Sets an NPC's Respawn Timer in Seconds (Stored in spawn2 table)"); } - uint32 npcTypeID = c->GetTarget()->CastToNPC()->GetNPCTypeID(); + uint32 npc_id = c->GetTarget()->CastToNPC()->GetNPCTypeID(); if (strcasecmp(sep->arg[1], "name") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has the name %s.", npcTypeID, sep->argplus[2]); - std::string query = StringFormat("UPDATE npc_types SET name = '%s' WHERE id = %i", sep->argplus[2],npcTypeID); + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has the name '{}'.", npc_id, sep->arg[2]).c_str()); + std::string query = fmt::format("UPDATE npc_types SET name = '{}' WHERE id = {}", sep->arg[2], npc_id); content_db.QueryDatabase(query); return; } if (strcasecmp(sep->arg[1], "lastname") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has the lastname %s.", npcTypeID, sep->argplus[2]); - std::string query = StringFormat("UPDATE npc_types SET lastname = '%s' WHERE id = %i", sep->argplus[2],npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "flymode") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has flymode [%s]", npcTypeID, sep->argplus[2]); - std::string query = StringFormat("UPDATE npc_types SET flymode = '%s' WHERE id = %i", sep->argplus[2],npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "race") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has the race %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET race = %i WHERE id = %i", atoi(sep->argplus[2]),npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "class") == 0) { - c->Message(Chat::Yellow,"NPCID %u is now class %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET class = %i WHERE id = %i", atoi(sep->argplus[2]),npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "bodytype") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has type %i bodytype.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET bodytype = %i WHERE id = %i", atoi(sep->argplus[2]),npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "hp") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has %i Hitpoints.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET hp = %i WHERE id = %i", atoi(sep->argplus[2]),npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "gender") == 0) { - c->Message(Chat::Yellow,"NPCID %u is now gender %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET gender = %i WHERE id = %i", atoi(sep->argplus[2]),npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "texture") == 0) { - c->Message(Chat::Yellow,"NPCID %u now uses texture %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET texture = %i WHERE id = %i", atoi(sep->argplus[2]),npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "helmtexture") == 0) { - c->Message(Chat::Yellow,"NPCID %u now uses helmtexture %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET helmtexture = %i WHERE id = %i", atoi(sep->argplus[2]),npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "armtexture") == 0) { - c->Message(Chat::Yellow,"NPCID %u now uses armtexture %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET armtexture = %i WHERE id = %i", atoi(sep->argplus[2]),npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "bracertexture") == 0) { - c->Message(Chat::Yellow,"NPCID %u now uses bracertexture %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET bracertexture = %i WHERE id = %i", atoi(sep->argplus[2]),npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "handtexture") == 0) { - c->Message(Chat::Yellow,"NPCID %u now uses handtexture %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET handtexture = %i WHERE id = %i", atoi(sep->argplus[2]),npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "legtexture") == 0) { - c->Message(Chat::Yellow,"NPCID %u now uses legtexture %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET legtexture = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "feettexture") == 0) { - c->Message(Chat::Yellow,"NPCID %u now uses feettexture %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET feettexture = %i WHERE id = %i", atoi(sep->argplus[2]),npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "herosforgemodel") == 0) { - c->Message(Chat::Yellow,"NPCID %u now uses herosforgemodel %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET herosforgemodel = %i WHERE id = %i", atoi(sep->argplus[2]),npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "size") == 0) { - c->Message(Chat::Yellow,"NPCID %u is now size %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET size = %i WHERE id = %i", atoi(sep->argplus[2]),npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "hpregen") == 0) { - c->Message(Chat::Yellow,"NPCID %u now regens %i hitpoints per tick.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET hp_regen_rate = %i WHERE id = %i", atoi(sep->argplus[2]),npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "manaregen") == 0) { - c->Message(Chat::Yellow,"NPCID %u now regens %i mana per tick.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET mana_regen_rate = %i WHERE id = %i", atoi(sep->argplus[2]),npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "loottable") == 0) { - c->Message(Chat::Yellow,"NPCID %u is now on loottable_id %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET loottable_id = %i WHERE id = %i", atoi(sep->argplus[2]),npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "merchantid") == 0) { - c->Message(Chat::Yellow,"NPCID %u is now merchant_id %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET merchant_id = %i WHERE id = %i", atoi(sep->argplus[2]),npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "alt_currency_id") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has field 'alt_currency_id' set to %s.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET alt_currency_id = '%s' WHERE id = %i", sep->argplus[2],npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "npc_spells_effects_id") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has field 'npc_spells_effects_id' set to %s.", npcTypeID, sep->argplus[2]); - std::string query = StringFormat("UPDATE npc_types SET npc_spells_effects_id = '%s' WHERE id = %i", sep->argplus[2],npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "adventure_template_id") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has field 'adventure_template_id' set to %s.", npcTypeID, sep->argplus[2]); - std::string query = StringFormat("UPDATE npc_types SET adventure_template_id = '%s' WHERE id = %i", sep->argplus[2],npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "trap_template") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has field 'trap_template' set to %s.", npcTypeID, sep->argplus[2]); - std::string query = StringFormat("UPDATE npc_types SET trap_template = '%s' WHERE id = %i", sep->argplus[2],npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "special_abilities") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has field 'special_abilities' set to %s.", npcTypeID, sep->argplus[2]); - std::string query = StringFormat("UPDATE npc_types SET special_abilities = '%s' WHERE id = %i", sep->argplus[2],npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "spell") == 0) { - c->Message(Chat::Yellow,"NPCID %u now uses spell list %i", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET npc_spells_id = %i WHERE id = %i", atoi(sep->argplus[2]),npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "faction") == 0) { - c->Message(Chat::Yellow,"NPCID %u is now faction %i", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET npc_faction_id = %i WHERE id = %i", atoi(sep->argplus[2]),npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "damage") == 0) { - c->Message(Chat::Yellow,"NPCID %u now hits from %i to %i", npcTypeID, atoi(sep->arg[2]), atoi(sep->arg[3])); - std::string query = StringFormat("UPDATE npc_types SET mindmg = %i, maxdmg = %i WHERE id = %i", atoi(sep->arg[2]), atoi(sep->arg[3]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "meleetype") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has a primary melee type of %i and a secondary melee type of %i.", npcTypeID, atoi(sep->arg[2]), atoi(sep->arg[3])); - std::string query = StringFormat("UPDATE npc_types SET prim_melee_type = %i, sec_melee_type = %i WHERE id = %i", atoi(sep->arg[2]), atoi(sep->arg[3]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "rangedtype") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has a ranged type of %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET ranged_type = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "ammoidfile") == 0) { - c->Message(Chat::Yellow,"NPCID %u's ammo id file is now %i", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET ammoidfile = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "aggroradius") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has an aggro radius of %i", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET aggroradius = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "assistradius") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has an assist radius of %i", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET assistradius = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "social") == 0) { - c->Message(Chat::Yellow,"NPCID %u social status is now %i", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET social = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "runspeed") == 0) { - c->Message(Chat::Yellow,"NPCID %u now runs at %f", npcTypeID, atof(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET runspeed = %f WHERE id = %i", atof(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "walkspeed") == 0) { - c->Message(Chat::Yellow,"NPCID %u now walks at %f", npcTypeID, atof(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET walkspeed = %f WHERE id = %i", atof(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "AGI") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has %i Agility.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET AGI = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "CHA") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has %i Charisma.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET CHA = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "DEX") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has %i Dexterity.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET DEX = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "INT") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has %i Intelligence.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET _INT = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "STA") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has %i Stamina.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET STA = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "STR") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has %i Strength.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET STR = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "WIS") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has a Magic Resistance of %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET WIS = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "MR") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has a Magic Resistance of %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET MR = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "DR") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has a Disease Resistance of %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET DR = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "CR") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has a Cold Resistance of %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET CR = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "FR") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has a Fire Resistance of %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET FR = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "PR") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has a Poison Resistance of %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET PR = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "Corrup") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has a Corruption Resistance of %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET corrup = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "PhR") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has a Physical Resistance of %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET PhR = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "seeinvis") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has seeinvis set to %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET see_invis = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "seeinvisundead") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has seeinvisundead set to %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET see_invis_undead = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "seehide") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has seehide set to %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET see_hide = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "seeimprovedhide") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has seeimprovedhide set to %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET see_improved_hide = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "AC") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has %i Armor Class.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET ac = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "ATK") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has %i Attack.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET atk = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "Accuracy") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has %i Accuracy.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET accuracy = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "Avoidance") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has %i Avoidance.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET avoidance = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has the lastname '{}'.", npc_id, sep->arg[2]).c_str()); + std::string query = fmt::format("UPDATE npc_types SET lastname = '{}' WHERE id = {}", sep->arg[2], npc_id); content_db.QueryDatabase(query); return; } if (strcasecmp(sep->arg[1], "level") == 0) { - c->Message(Chat::Yellow,"NPCID %u is now level %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET level = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now level {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET level = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); content_db.QueryDatabase(query); return; } - if (strcasecmp(sep->arg[1], "maxlevel") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has a maximum level of %i.", npcTypeID, atoi(sep->argplus[2])); - std::string query = StringFormat("UPDATE npc_types SET maxlevel = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); + if (strcasecmp(sep->arg[1], "race") == 0) { + auto race_id = atoi(sep->arg[2]); + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now a {} ({}).", npc_id, GetRaceIDName(race_id), race_id).c_str()); + std::string query = fmt::format("UPDATE npc_types SET race = {} WHERE id = {}", race_id, npc_id); content_db.QueryDatabase(query); return; } - if (strcasecmp(sep->arg[1], "qglobal") == 0) { - c->Message(Chat::Yellow,"Quest globals have been %s for NPCID %u", atoi(sep->arg[2]) == 0 ? "disabled" : "enabled", npcTypeID); - std::string query = StringFormat("UPDATE npc_types SET qglobal = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); + if (strcasecmp(sep->arg[1], "class") == 0) { + auto class_id = atoi(sep->arg[2]); + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now a {} ({}).", npc_id, GetClassIDName(class_id), class_id).c_str()); + std::string query = fmt::format("UPDATE npc_types SET class = {} WHERE id = {}", class_id, npc_id); content_db.QueryDatabase(query); return; } - if (strcasecmp(sep->arg[1], "npcaggro") == 0) { - c->Message(Chat::Yellow,"NPCID %u will now %s other NPCs with negative faction npc_value", npcTypeID, atoi(sep->arg[2]) == 0? "not aggro": "aggro"); - std::string query = StringFormat("UPDATE npc_types SET npc_aggro = %i WHERE id = %i", atoi(sep->argplus[2]) == 0? 0: 1, npcTypeID); + if (strcasecmp(sep->arg[1], "bodytype") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Bodytype {} .", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET bodytype = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); content_db.QueryDatabase(query); return; } - if (strcasecmp(sep->arg[1], "spawn_limit") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has a spawn limit of %i", npcTypeID, atoi(sep->arg[2])); - std::string query = StringFormat("UPDATE npc_types SET spawn_limit = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); + if (strcasecmp(sep->arg[1], "hp") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Health.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET hp = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); content_db.QueryDatabase(query); return; } - if (strcasecmp(sep->arg[1], "Attackspeed") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has attack_speed set to %f", npcTypeID, atof(sep->arg[2])); - std::string query = StringFormat("UPDATE npc_types SET attack_speed = %f WHERE id = %i", atof(sep->argplus[2]), npcTypeID); + if (strcasecmp(sep->arg[1], "mana") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Mana.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET mana = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); content_db.QueryDatabase(query); return; } - if (strcasecmp(sep->arg[1], "Attackdelay") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has attack_delay set to %i", npcTypeID,atoi(sep->arg[2])); - std::string query = StringFormat("UPDATE npc_types SET attack_delay = %i WHERE id = %i", atoi(sep->argplus[2]),npcTypeID); + if (strcasecmp(sep->arg[1], "gender") == 0) { + auto gender_id = atoi(sep->arg[2]); + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now a {} ({}).", npc_id, gender_id, GetGenderName(gender_id)).c_str()); + std::string query = fmt::format("UPDATE npc_types SET gender = {} WHERE id = {}", gender_id, npc_id); content_db.QueryDatabase(query); return; } - if (strcasecmp(sep->arg[1], "Attackcount") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has attack_count set to %i", npcTypeID,atoi(sep->arg[2])); - std::string query = StringFormat("UPDATE npc_types SET attack_count = %i WHERE id = %i", atoi(sep->argplus[2]),npcTypeID); + if (strcasecmp(sep->arg[1], "texture") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET texture = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); content_db.QueryDatabase(query); return; } - if (strcasecmp(sep->arg[1], "findable") == 0) { - c->Message(Chat::Yellow,"NPCID %u is now %s", npcTypeID, atoi(sep->arg[2]) == 0? "not findable": "findable"); - std::string query = StringFormat("UPDATE npc_types SET findable = %i WHERE id = %i", atoi(sep->argplus[2]) == 0? 0: 1, npcTypeID); + if (strcasecmp(sep->arg[1], "helmtexture") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Helmet Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET helmtexture = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); content_db.QueryDatabase(query); return; } - if (strcasecmp(sep->arg[1], "trackable") == 0) { - c->Message(Chat::Yellow,"NPCID %u is now %s", npcTypeID, atoi(sep->arg[2]) == 0? "not trackable": "trackable"); - std::string query = StringFormat("UPDATE npc_types SET trackable = %i WHERE id = %i", atoi(sep->argplus[2]) == 0? 0: 1, npcTypeID); + if (strcasecmp(sep->arg[1], "herosforgemodel") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Hero's Forge Model {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET herosforgemodel = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); content_db.QueryDatabase(query); return; } - if (strcasecmp(sep->arg[1], "weapon") == 0) { - c->Message(Chat::Yellow,"NPCID %u will have item graphic %i set to his primary and item graphic %i set to his secondary on repop.", npcTypeID, atoi(sep->arg[2]), atoi(sep->arg[3])); - std::string query = StringFormat("UPDATE npc_types SET d_melee_texture1 = %i, d_melee_texture2 = %i WHERE id = %i", atoi(sep->arg[2]), atoi(sep->arg[3]), npcTypeID); + if (strcasecmp(sep->arg[1], "size") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now Size {:.2f}.", npc_id, atof(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET size = {:.2f} WHERE id = {}", atof(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "hpregen") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now regenerates {} Health per Tick.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET hp_regen_rate = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "manaregen") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now regenerates {} Mana per Tick.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET mana_regen_rate = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "loottable") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using loottable ID {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET loottable_id = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "merchantid") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using merchant ID {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET merchant_id = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "alt_currency_id") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Alternate Currency ID {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET alt_currency_id = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "spell") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Spell List ID {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET npc_spells_id = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "npc_spells_effects_id") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using NPC Spells Effects ID {}.", npc_id, sep->arg[2]).c_str()); + std::string query = fmt::format("UPDATE npc_types SET npc_spells_effects_id = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "faction") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Faction ID {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET npc_faction_id = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "adventure_template_id") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Adventure Template ID {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET adventure_template_id = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "trap_template") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Trap Template ID {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET trap_template = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "damage") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now hits from {} to {} damage.", npc_id, atoi(sep->arg[2]), atoi(sep->arg[3])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET mindmg = {}, maxdmg = {} WHERE id = {}", atoi(sep->arg[2]), atoi(sep->arg[3]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "attackcount") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has an Attack Count of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET attack_count = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "special_attacks") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using the following Special Attacks '{}'.", npc_id, sep->arg[2]).c_str()); + std::string query = fmt::format("UPDATE npc_types SET npcspecialattks = '{}' WHERE id = {}", sep->arg[2], npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "special_abilities") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using the following Special Abilities '{}'.", npc_id, sep->arg[2]).c_str()); + std::string query = fmt::format("UPDATE npc_types SET special_abilities = '{}' WHERE id = {}", sep->arg[2], npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "aggroradius") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has an Aggro Radius of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET aggroradius = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "assistradius") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has an Assist Radius of {}", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET assistradius = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); content_db.QueryDatabase(query); return; } if (strcasecmp(sep->arg[1], "featuresave") == 0) { - c->Message(Chat::Yellow,"NPCID %u saved with all current facial feature settings", npcTypeID); - Mob* target = c->GetTarget(); - std::string query = StringFormat("UPDATE npc_types " - "SET luclin_haircolor = %i, luclin_beardcolor = %i, " - "luclin_hairstyle = %i, luclin_beard = %i, " - "face = %i, drakkin_heritage = %i, " - "drakkin_tattoo = %i, drakkin_details = %i " - "WHERE id = %i", - target->GetHairColor(), target->GetBeardColor(), - target->GetHairStyle(), target->GetBeard(), - target->GetLuclinFace(), target->GetDrakkinHeritage(), - target->GetDrakkinTattoo(), target->GetDrakkinDetails(), - npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "color") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has %i red, %i green, and %i blue tinting on their armor.", npcTypeID, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4])); - std::string query = StringFormat("UPDATE npc_types SET armortint_red = %i, armortint_green = %i, armortint_blue = %i WHERE id = %i", atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), npcTypeID); + c->Message(Chat::Yellow, fmt::format("NPC ID {} saved with all current facial feature settings.", npc_id).c_str()); + Mob* target = c->GetTarget(); + std::string query = fmt::format( + "UPDATE npc_types " + "SET luclin_haircolor = {}, luclin_beardcolor = {}, " + "luclin_hairstyle = {}, luclin_beard = {}, " + "face = {}, drakkin_heritage = {}, " + "drakkin_tattoo = {}, drakkin_details = {} " + "WHERE id = {}", + target->GetHairColor(), target->GetBeardColor(), + target->GetHairStyle(), target->GetBeard(), + target->GetLuclinFace(), target->GetDrakkinHeritage(), + target->GetDrakkinTattoo(), target->GetDrakkinDetails(), + npc_id + ); content_db.QueryDatabase(query); return; } if (strcasecmp(sep->arg[1], "armortint_id") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has field 'armortint_id' set to %s", npcTypeID, sep->arg[2]); - std::string query = StringFormat("UPDATE npc_types SET armortint_id = '%s' WHERE id = %i", sep->argplus[2], npcTypeID); + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Armor Tint ID {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET armortint_id = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "color") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Red, {} Green, and {} Blue tinting on their armor.", npc_id, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET armortint_red = {}, armortint_green = {}, armortint_blue = {} WHERE id = {}", atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "ammoidfile") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Ammo ID File {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET ammo_idfile = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "weapon") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} will have Model {} set to their Primary and Model {} set to their Secondary on repop.", npc_id, atoi(sep->arg[2]), atoi(sep->arg[3])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET d_melee_texture1 = {}, d_melee_texture2 = {} WHERE id = {}", atoi(sep->arg[2]), atoi(sep->arg[3]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "meleetype") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Primary Melee Type of {} and a Secondary Melee Type of {}.", npc_id, atoi(sep->arg[2]), atoi(sep->arg[3])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET prim_melee_type = {}, sec_melee_type = {} WHERE id = {}", atoi(sep->arg[2]), atoi(sep->arg[3]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "rangedtype") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Ranged Type of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET ranged_type = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "runspeed") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now runs at {:.2f}.", npc_id, atof(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET runspeed = {:.2f} WHERE id = {}", atof(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "mr") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Magic Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET MR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "pr") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Poison Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET PR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "dr") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Disease Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET DR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "fr") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Fire Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET FR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "cr") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Cold Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET CR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "corrup") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Corruption Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET corrup = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "phr") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Physical Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET PhR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "seeinvis") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} can {} See Invisible.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET see_invis = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "seeinvisundead") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} can {} See Invisible vs. Undead.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET see_invis_undead = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "qglobal") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} can {} use Quest Globals.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET qglobal = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "ac") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Armor Class.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET ac = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "npcaggro") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} will {} aggro other NPCs that have a hostile faction.", npc_id, (atoi(sep->arg[2]) == 1 ? "now": "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET npc_aggro = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "spawn_limit") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Spawn Limit of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET spawn_limit = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "attackspeed") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has an Attack Speed of {:.2f}.", npc_id, atof(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET attack_speed = {:.2f} WHERE id = {}", atof(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "attackdelay") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has an Attack Delay of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET attack_delay = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "findable") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is {} Findable.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET findable = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "str") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Strength.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET STR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "sta") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Stamina.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET STA = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "agi") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Agility.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET AGI = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "dex") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Dexterity.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET DEX = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "int") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Intelligence.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET _INT = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "wis") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Magic Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET WIS = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "cha") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Charisma.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET CHA = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "seehide") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} can {} See Hide.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET see_hide = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "seeimprovedhide") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} can {} See Improved Hide.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET see_improved_hide = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "trackable") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is {} Trackable.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET trackable = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "atk") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Attack.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET atk = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "accuracy") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Accuracy.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET accuracy = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "avoidance") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Avoidance.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET avoidance = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "slow_mitigation") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Slow Mitigation.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET slow_mitigation = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "version") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Version {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET version = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "maxlevel") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Maximum Level of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET maxlevel = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "scalerate") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Scaling Rate of {}%%.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET scalerate = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "spellscale") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Spell Scaling Rate of {}%%.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET spellscale = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "healscale") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Heal Scaling Rate of {}%%.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET healscale = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "no_target") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is {} Targetable with Target Hotkey.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET no_target_hotkey = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "raidtarget") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is {} designated as a Raid Target.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET raid_target = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "armtexture") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Arm Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET armtexture = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "bracertexture") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Bracer Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET bracertexture = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "handtexture") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Hand Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET handtexture = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "legtexture") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Leg Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET legtexture = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "feettexture") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Feet Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET feettexture = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "walkspeed") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now walks at {:.2f}.", npc_id, atof(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET walkspeed = {:.2f} WHERE id = {}", atof(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "show_name") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} will {} show their name.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET show_name = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "untargetable") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} will {} be untargetable.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET untargetable = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "charm_ac") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Armor Class while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET charm_ac = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "charm_min_dmg") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now does {} Minimum Damage while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET charm_min_dmg = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "charm_max_dmg") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now does {} Maximum Damage while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET charm_max_dmg = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "charm_attack_delay") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Attack Delay while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET charm_attack_delay = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "charm_accuracy_rating") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Accuracy Rating while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET charm_accuracy_rating = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "charm_avoidance_rating") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Avoidance Rating while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET charm_avoidance_rating = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "charm_atk") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Attack while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET charm_atk = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "skip_global_loot") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} will {} skip Global Loot.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET skip_global_loot = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "rarespawn") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is {} designated as a Rare Spawn.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET rare_spawn = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "stuck_behavior") == 0) { + auto behavior_id = atoi(sep->arg[2]); + std::string behavior_name = "Unknown"; + if (behavior_id == MobStuckBehavior::RunToTarget) { + behavior_name = "Run To Target"; + } else if (behavior_id == MobStuckBehavior::WarpToTarget) { + behavior_name = "Warp To Target"; + } else if (behavior_id == MobStuckBehavior::TakeNoAction) { + behavior_name = "Take No Action"; + } else if (behavior_id == MobStuckBehavior::EvadeCombat) { + behavior_name = "Evade Combat"; + } + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Stuck Behavior {} ({}).", npc_id, behavior_name, behavior_id).c_str()); + std::string query = fmt::format("UPDATE npc_types SET stuck_behavior = {} WHERE id = {}", behavior_id, npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "flymode") == 0) { + auto flymode_id = atoi(sep->arg[2]); + std::string flymode_name = "Unknown"; + if (flymode_id == GravityBehavior::Ground) { + flymode_name = "Ground"; + } else if (flymode_id == GravityBehavior::Flying) { + flymode_name = "Flying"; + } else if (flymode_id == GravityBehavior::Levitating) { + flymode_name = "Levitating"; + } else if (flymode_id == GravityBehavior::Water) { + flymode_name = "Water"; + } else if (flymode_id == GravityBehavior::Floating) { + flymode_name = "Floating"; + } else if (flymode_id == GravityBehavior::LevitateWhileRunning) { + flymode_name = "Levitating While Running"; + } + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Fly Mode {} ({}).", npc_id, flymode_name, flymode_id).c_str()); + std::string query = fmt::format("UPDATE npc_types SET flymode = {} WHERE id = {}", flymode_id, npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "always_aggro") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} will {} Always Aggro.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET always_aggro = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "exp_mod") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has an Experience Modifier of {}%%.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET exp_mod = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); content_db.QueryDatabase(query); return; } if (strcasecmp(sep->arg[1], "setanimation") == 0) { int animation = 0; + std::string animation_name = "Unknown"; if(sep->arg[2] && atoi(sep->arg[2]) <= 4) { - if((strcasecmp(sep->arg[2], "stand" ) == 0) || atoi(sep->arg[2]) == 0) - animation = 0; //Stand - if((strcasecmp(sep->arg[2], "sit" ) == 0) || atoi(sep->arg[2]) == 1) - animation = 1; //Sit - if((strcasecmp(sep->arg[2], "crouch" ) == 0) || atoi(sep->arg[2]) == 2) - animation = 2; //Crouch - if((strcasecmp(sep->arg[2], "dead" ) == 0) || atoi(sep->arg[2]) == 3) - animation = 3; //Dead - if((strcasecmp(sep->arg[2], "loot" ) == 0) || atoi(sep->arg[2]) == 4) - animation = 4; //Looting Animation + if(strcasecmp(sep->arg[2], "stand") == 0 || atoi(sep->arg[2]) == 0) { // Stand + animation = 0; + animation_name = "Standing"; + } else if(strcasecmp(sep->arg[2], "sit") == 0 || atoi(sep->arg[2]) == 1) { // Sit + animation = 1; + animation_name = "Sitting"; + } else if(strcasecmp(sep->arg[2], "crouch") == 0 || atoi(sep->arg[2]) == 2) { // Crouch + animation = 2; + animation_name = "Crouching"; + } else if(strcasecmp(sep->arg[2], "dead") == 0 || atoi(sep->arg[2]) == 3) { // Dead + animation = 3; + animation_name = "Dead"; + } else if(strcasecmp(sep->arg[2], "loot") == 0 || atoi(sep->arg[2]) == 4) { // Looting Animation + animation = 4; + animation_name = "Looting"; + } } else { - c->Message(Chat::White, "You must specifiy an animation stand, sit, crouch, dead, loot (0-4)"); + c->Message(Chat::White, "You must specify an Animation (0 = Stand, 1 = Sit, 2 = Crouch, 3 = Dead, 4 = Loot)"); c->Message(Chat::White, "Example: #npcedit setanimation sit"); c->Message(Chat::White, "Example: #npcedit setanimation 0"); return; } - c->Message(Chat::Yellow,"NPCID %u now has the animation set to %i on spawn with spawngroup %i", npcTypeID, animation, - c->GetTarget()->CastToNPC()->GetSpawnGroupId() ); - std::string query = StringFormat("UPDATE spawn2 SET animation = %i " "WHERE spawngroupID = %i", animation, - c->GetTarget()->CastToNPC()->GetSpawnGroupId()); + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} now has their Spawn Animation set to {} ({}) on Spawn Group ID {}.", + npc_id, + animation_name, + animation, + c->GetTarget()->CastToNPC()->GetSpawnGroupId() + ).c_str() + ); + std::string query = fmt::format( + "UPDATE spawn2 SET animation = {} WHERE spawngroupID = {}", + animation, + c->GetTarget()->CastToNPC()->GetSpawnGroupId() + ); content_db.QueryDatabase(query); c->GetTarget()->SetAppearance(EmuAppearance(animation)); return; } - if (strcasecmp(sep->arg[1], "scalerate") == 0) { - c->Message(Chat::Yellow,"NPCID %u now has a scaling rate of %i.", npcTypeID, atoi(sep->arg[2])); - std::string query = StringFormat("UPDATE npc_types SET scalerate = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "healscale") == 0) { - c->Message(Chat::Yellow, "NPCID %u now has a heal scaling rate of %i.", npcTypeID, atoi(sep->arg[2])); - std::string query = StringFormat("UPDATE npc_types SET healscale = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "spellscale") == 0) { - c->Message(Chat::Yellow, "NPCID %u now has a spell scaling rate of %i.", npcTypeID, atoi(sep->arg[2])); - std::string query = StringFormat("UPDATE npc_types SET spellscale = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "no_target") == 0) { - c->Message(Chat::Yellow, "NPCID %u is now %s.", npcTypeID, atoi(sep->arg[2]) == 0? "targetable": "untargetable"); - std::string query = StringFormat("UPDATE npc_types SET no_target_hotkey = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "version") == 0) { - c->Message(Chat::Yellow, "NPCID %u is now version %i.", npcTypeID, atoi(sep->arg[2])); - std::string query = StringFormat("UPDATE npc_types SET version = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "slow_mitigation") == 0) { - c->Message(Chat::Yellow, "NPCID %u's slow mitigation limit is now %i.", npcTypeID, atoi(sep->arg[2])); - std::string query = StringFormat("UPDATE npc_types SET slow_mitigation = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "raidtarget") == 0) { - if (sep->arg[2][0] && sep->IsNumber(sep->arg[2]) && atoi(sep->arg[2]) >= 0) { - c->Message(Chat::Yellow, "NPCID %u is %s as a raid target.", npcTypeID, atoi(sep->arg[2]) == 0 ? "no longer designated" : "now designated"); - std::string query = StringFormat("UPDATE npc_types SET raid_target = %i WHERE id = %i", atoi(sep->arg[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - } - - if (strcasecmp(sep->arg[1], "rarespawn") == 0) { - if (sep->arg[2][0] && sep->IsNumber(sep->arg[2]) && atoi(sep->arg[2]) >= 0) { - c->Message(Chat::Yellow, "NPCID %u is %s as a rare spawn.", npcTypeID, atoi(sep->arg[2]) == 0 ? "no longer designated" : "now designated"); - std::string query = StringFormat("UPDATE npc_types SET rare_spawn = %i WHERE id = %i", atoi(sep->arg[2]), npcTypeID); - content_db.QueryDatabase(query); - return; - } - } - if (strcasecmp(sep->arg[1], "respawntime") == 0) { if (sep->arg[2][0] && sep->IsNumber(sep->arg[2]) && atoi(sep->arg[2]) > 0) { - c->Message(Chat::Yellow, "NPCID %u (spawngroup %i) respawn time set to %i.", npcTypeID, c->GetTarget()->CastToNPC()->GetSpawnGroupId(), atoi(sep->arg[2])); - std::string query = StringFormat("UPDATE spawn2 SET respawntime = %i WHERE spawngroupID = %i AND version = %i", atoi(sep->arg[2]), c->GetTarget()->CastToNPC()->GetSpawnGroupId(), zone->GetInstanceVersion()); + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Respawn Timer of {} Seconds on Spawn Group ID {}.", npc_id, atoi(sep->arg[2]), c->GetTarget()->CastToNPC()->GetSpawnGroupId()).c_str()); + std::string query = fmt::format("UPDATE spawn2 SET respawntime = {} WHERE spawngroupID = {} AND version = {}", atoi(sep->arg[2]), c->GetTarget()->CastToNPC()->GetSpawnGroupId(), zone->GetInstanceVersion()); content_db.QueryDatabase(query); return; } @@ -11015,7 +11171,7 @@ void command_advnpcspawn(Client *c, const Seperator *sep) return; } - c->Message(Chat::White, "NPC %i added to group %i with %i chance!", atoi(sep->arg[3]), atoi(sep->arg[2]), atoi(sep->arg[4]) ); + c->Message(Chat::White, "NPC %i added to group %i with %i chance!", atoi(sep->arg[3]), atoi(sep->arg[2]), atoi(sep->arg[4]) ); return; } @@ -11052,7 +11208,7 @@ void command_advnpcspawn(Client *c, const Seperator *sep) std::string query = StringFormat("UPDATE spawngroup " "SET dist = '0', max_x = '0', min_x = '0', " "max_y = '0', min_y = '0', delay = '0' " - "WHERE id = '%i' ", atoi(sep->arg[2])); + "WHERE id = '%i' ", atoi(sep->arg[2])); auto results = content_db.QueryDatabase(query); if (!results.Success()) { c->Message(Chat::White, "Invalid Arguments -- MySQL gave the following error:"); @@ -11067,7 +11223,7 @@ void command_advnpcspawn(Client *c, const Seperator *sep) if (strcasecmp(sep->arg[1], "addgroupspawn") == 0 && atoi(sep->arg[2])!=0) { content_db.NPCSpawnDB(5, zone->GetShortName(), zone->GetInstanceVersion(), c, 0, atoi(sep->arg[2])); - c->Message(Chat::White, "Mob of group %i added successfully!", atoi(sep->arg[2])); + c->Message(Chat::White, "Mob of group %i added successfully!", atoi(sep->arg[2])); return; } @@ -11199,7 +11355,7 @@ void command_advnpcspawn(Client *c, const Seperator *sep) if (strcasecmp(sep->arg[1], "testload") == 0 && atoi(sep->arg[2])!=0) { content_db.LoadSpawnGroupsByID(atoi(sep->arg[2]),&zone->spawn_group_list); - c->Message(Chat::White, "Group %i loaded successfully!", atoi(sep->arg[2])); + c->Message(Chat::White, "Group %i loaded successfully!", atoi(sep->arg[2])); return; } diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 0bf867a87..df3d846e3 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -3439,14 +3439,7 @@ void QuestManager::SetEXPModifierByCharID(uint32 character_id, uint32 zone_id, d } std::string QuestManager::getgendername(uint32 gender_id) { - auto gender_name = "Unknown"; - if (gender_id == MALE) { - gender_name = "Male"; - } else if (gender_id == FEMALE) { - gender_name = "Female"; - } else if (gender_id == NEUTER) { - gender_name = "Neuter"; - } + std::string gender_name = GetGenderName(gender_id); return gender_name; } From 9b06221be015f3a363b6dca53e137f2f48d8120a Mon Sep 17 00:00:00 2001 From: Noudess Date: Mon, 20 Sep 2021 11:30:33 -0400 Subject: [PATCH 205/624] [Bug Fix] Fix bug where IVU could not be cast on char with Invis --- zone/spell_effects.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index ea9c879b0..68fa5e897 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -582,7 +582,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove snprintf(effect_desc, _EDLEN, "Invisibility to Animals"); #endif invisible_animals = true; - SetInvisible(0); break; } @@ -593,7 +592,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove snprintf(effect_desc, _EDLEN, "Invisibility to Undead"); #endif invisible_undead = true; - SetInvisible(0); break; } case SE_SeeInvis: From ca77d22035c6733f13c366266cd77385f4e25cd2 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 21 Sep 2021 21:08:16 -0400 Subject: [PATCH 206/624] [Bug Fix] GetSpellStat() Identifiers were comparing improperly. (#1552) - GetSpellStat() converts identifiers to lowercase and they were being checked against mixed case strings, causing certain identifiers to always fail. --- common/spdat.cpp | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index a95088a71..581575aa4 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1390,7 +1390,7 @@ int GetSpellStatValue(uint32 spell_id, const char* stat_identifier, uint8 slot) if (slot < 4) { if (id == "components") { return spells[spell_id].components[slot]; } else if (id == "component_counts") { return spells[spell_id].component_counts[slot]; } - else if (id == "NoexpendReagent") { return spells[spell_id].NoexpendReagent[slot]; } + else if (id == "noexpendreagent") { return spells[spell_id].NoexpendReagent[slot]; } } if (id == "range") { return static_cast(spells[spell_id].range); } @@ -1402,26 +1402,26 @@ int GetSpellStatValue(uint32 spell_id, const char* stat_identifier, uint8 slot) else if (id == "recast_time") { return spells[spell_id].recast_time; } else if (id == "buffdurationformula") { return spells[spell_id].buffdurationformula; } else if (id == "buffduration") { return spells[spell_id].buffduration; } - else if (id == "AEDuration") { return spells[spell_id].AEDuration; } + else if (id == "aeduration") { return spells[spell_id].AEDuration; } else if (id == "mana") { return spells[spell_id].mana; } //else if (id == "LightType") {stat = spells[spell_id].LightType; } - Not implemented - else if (id == "goodEffect") { return spells[spell_id].goodEffect; } - else if (id == "Activated") { return spells[spell_id].Activated; } + else if (id == "goodeffect") { return spells[spell_id].goodEffect; } + else if (id == "activated") { return spells[spell_id].Activated; } else if (id == "resisttype") { return spells[spell_id].resisttype; } else if (id == "targettype") { return spells[spell_id].targettype; } else if (id == "basediff") { return spells[spell_id].basediff; } else if (id == "skill") { return spells[spell_id].skill; } else if (id == "zonetype") { return spells[spell_id].zonetype; } - else if (id == "EnvironmentType") { return spells[spell_id].EnvironmentType; } - else if (id == "TimeOfDay") { return spells[spell_id].TimeOfDay; } - else if (id == "CastingAnim") { return spells[spell_id].CastingAnim; } - else if (id == "SpellAffectIndex") { return spells[spell_id].SpellAffectIndex; } + else if (id == "environmenttype") { return spells[spell_id].EnvironmentType; } + else if (id == "timeofday") { return spells[spell_id].TimeOfDay; } + else if (id == "castinganim") { return spells[spell_id].CastingAnim; } + else if (id == "spellaffectindex") { return spells[spell_id].SpellAffectIndex; } else if (id == "disallow_sit") { return spells[spell_id].disallow_sit; } //else if (id == "spellanim") {stat = spells[spell_id].spellanim; } - Not implemented else if (id == "uninterruptable") { return spells[spell_id].uninterruptable; } - else if (id == "ResistDiff") { return spells[spell_id].ResistDiff; } + else if (id == "resistdiff") { return spells[spell_id].ResistDiff; } else if (id == "dot_stacking_exempt") { return spells[spell_id].dot_stacking_exempt; } - else if (id == "RecourseLink") { return spells[spell_id].RecourseLink; } + else if (id == "recourselink") { return spells[spell_id].RecourseLink; } else if (id == "no_partial_resist") { return spells[spell_id].no_partial_resist; } else if (id == "short_buff_box") { return spells[spell_id].short_buff_box; } else if (id == "descnum") { return spells[spell_id].descnum; } @@ -1429,11 +1429,11 @@ int GetSpellStatValue(uint32 spell_id, const char* stat_identifier, uint8 slot) else if (id == "npc_no_los") { return spells[spell_id].npc_no_los; } else if (id == "reflectable") { return spells[spell_id].reflectable; } else if (id == "bonushate") { return spells[spell_id].bonushate; } - else if (id == "EndurCost") { return spells[spell_id].EndurCost; } - else if (id == "EndurTimerIndex") { return spells[spell_id].EndurTimerIndex; } - else if (id == "IsDisciplineBuff") { return spells[spell_id].IsDisciplineBuff; } - else if (id == "HateAdded") { return spells[spell_id].HateAdded; } - else if (id == "EndurUpkeep") { return spells[spell_id].EndurUpkeep; } + else if (id == "endurcost") { return spells[spell_id].EndurCost; } + else if (id == "endurtimerindex") { return spells[spell_id].EndurTimerIndex; } + else if (id == "isdisciplinebuff") { return spells[spell_id].IsDisciplineBuff; } + else if (id == "hateadded") { return spells[spell_id].HateAdded; } + else if (id == "endurupkeep") { return spells[spell_id].EndurUpkeep; } else if (id == "numhitstype") { return spells[spell_id].numhitstype; } else if (id == "numhits") { return spells[spell_id].numhits; } else if (id == "pvpresistbase") { return spells[spell_id].pvpresistbase; } @@ -1442,11 +1442,11 @@ int GetSpellStatValue(uint32 spell_id, const char* stat_identifier, uint8 slot) else if (id == "spell_category") { return spells[spell_id].spell_category; } else if (id == "can_mgb") { return spells[spell_id].can_mgb; } else if (id == "dispel_flag") { return spells[spell_id].dispel_flag; } - else if (id == "MinResist") { return spells[spell_id].MinResist; } - else if (id == "MaxResist") { return spells[spell_id].MaxResist; } + else if (id == "minresist") { return spells[spell_id].MinResist; } + else if (id == "maxresist") { return spells[spell_id].MaxResist; } else if (id == "viral_targets") { return spells[spell_id].viral_targets; } else if (id == "viral_timer") { return spells[spell_id].viral_timer; } - else if (id == "NimbusEffect") { return spells[spell_id].NimbusEffect; } + else if (id == "nimbuseffect") { return spells[spell_id].NimbusEffect; } else if (id == "directional_start") { return static_cast(spells[spell_id].directional_start); } else if (id == "directional_end") { return static_cast(spells[spell_id].directional_end); } else if (id == "not_focusable") { return spells[spell_id].not_focusable; } @@ -1455,10 +1455,10 @@ int GetSpellStatValue(uint32 spell_id, const char* stat_identifier, uint8 slot) else if (id == "spellgroup") { return spells[spell_id].spellgroup; } else if (id == "rank") { return spells[spell_id].rank; } else if (id == "no_resist") { return spells[spell_id].no_resist; } - else if (id == "CastRestriction") { return spells[spell_id].CastRestriction; } - else if (id == "AllowRest") { return spells[spell_id].AllowRest; } - else if (id == "InCombat") { return spells[spell_id].InCombat; } - else if (id == "OutofCombat") { return spells[spell_id].OutofCombat; } + else if (id == "castrestriction") { return spells[spell_id].CastRestriction; } + else if (id == "allowrest") { return spells[spell_id].AllowRest; } + else if (id == "incombat") { return spells[spell_id].InCombat; } + else if (id == "outofcombat") { return spells[spell_id].OutofCombat; } else if (id == "aemaxtargets") { return spells[spell_id].aemaxtargets; } else if (id == "no_heal_damage_item_mod") { return spells[spell_id].no_heal_damage_item_mod; } else if (id == "persistdeath") { return spells[spell_id].persistdeath; } @@ -1466,7 +1466,7 @@ int GetSpellStatValue(uint32 spell_id, const char* stat_identifier, uint8 slot) else if (id == "min_dist_mod") { return static_cast(spells[spell_id].min_dist_mod); } else if (id == "max_dist") { return static_cast(spells[spell_id].max_dist); } else if (id == "min_range") { return static_cast(spells[spell_id].min_range); } - else if (id == "DamageShieldType") { return spells[spell_id].DamageShieldType; } + else if (id == "damageshieldtype") { return spells[spell_id].DamageShieldType; } return 0; } \ No newline at end of file From 7b969173f45197dddb8c66a1d743cfc6ea88ef69 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Wed, 22 Sep 2021 17:43:01 -0400 Subject: [PATCH 207/624] [Door Manipulation] Resolve some typos and add a GM check. (#1550) * [Door Manipulation] Resolve some typos and add a status check. * Remove Status check and use GetGM() inside devtools check instead. --- common/shareddb.cpp | 2 +- zone/client.cpp | 2 +- zone/gm_commands/door_manipulation.cpp | 40 ++++++++++++++------------ 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 7c86067bc..2a52ebe0e 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -2287,4 +2287,4 @@ void SharedDatabase::LoadCharacterInspectMessage(uint32 character_id, InspectMes void SharedDatabase::SaveCharacterInspectMessage(uint32 character_id, const InspectMessage_Struct* message) { std::string query = StringFormat("REPLACE INTO `character_inspect_messages` (id, inspect_message) VALUES (%u, '%s')", character_id, EscapeString(message->text).c_str()); auto results = QueryDatabase(query); -} +} \ No newline at end of file diff --git a/zone/client.cpp b/zone/client.cpp index 34a9a31de..8d64cd019 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -9191,7 +9191,7 @@ void Client::SetDisplayMobInfoWindow(bool display_mob_info_window) bool Client::IsDevToolsEnabled() const { - return dev_tools_enabled && RuleB(World, EnableDevTools); + return dev_tools_enabled && GetGM() && RuleB(World, EnableDevTools); } void Client::SetDevToolsEnabled(bool in_dev_tools_enabled) diff --git a/zone/gm_commands/door_manipulation.cpp b/zone/gm_commands/door_manipulation.cpp index c9a950e52..afb91b07a 100644 --- a/zone/gm_commands/door_manipulation.cpp +++ b/zone/gm_commands/door_manipulation.cpp @@ -129,7 +129,7 @@ void DoorManipulation::CommandHandler(Client *c, const Seperator *sep) std::vector move_h_options_negative; std::vector set_size_options_positive; std::vector set_size_options_negative; - for (const auto &move_option : move_options) { + for (const auto &move_option : move_options) { if (move_option == move_x_action) { move_x_options_positive.emplace_back( EQ::SayLinkEngine::GenerateQuestSaylink( @@ -165,7 +165,7 @@ void DoorManipulation::CommandHandler(Client *c, const Seperator *sep) EQ::SayLinkEngine::GenerateQuestSaylink( fmt::format("#door edit {} -.25", move_option), false, - "-.25" + ".25" ) ); } @@ -190,7 +190,7 @@ void DoorManipulation::CommandHandler(Client *c, const Seperator *sep) } for (int move_index = -15; move_index <= 0; move_index += 5) { - int value = (move_index == 0 ? 1 : move_index); + int value = (move_index == 0 ? -1 : move_index); move_y_options_negative.emplace_back( EQ::SayLinkEngine::GenerateQuestSaylink( fmt::format("#door edit {} {}", move_option, value), @@ -204,7 +204,7 @@ void DoorManipulation::CommandHandler(Client *c, const Seperator *sep) EQ::SayLinkEngine::GenerateQuestSaylink( fmt::format("#door edit {} -.25", move_option), false, - "-.25" + ".25" ) ); } @@ -229,7 +229,7 @@ void DoorManipulation::CommandHandler(Client *c, const Seperator *sep) } for (int move_index = -15; move_index <= 0; move_index += 5) { - int value = (move_index == 0 ? 1 : move_index); + int value = (move_index == 0 ? -1 : move_index); move_z_options_negative.emplace_back( EQ::SayLinkEngine::GenerateQuestSaylink( fmt::format("#door edit {} {}", move_option, value), @@ -243,7 +243,7 @@ void DoorManipulation::CommandHandler(Client *c, const Seperator *sep) EQ::SayLinkEngine::GenerateQuestSaylink( fmt::format("#door edit {} -.25", move_option), false, - "-.25" + ".25" ) ); } @@ -260,7 +260,7 @@ void DoorManipulation::CommandHandler(Client *c, const Seperator *sep) } for (int move_index = -50; move_index <= 0; move_index += 5) { - int value = (move_index == 0 ? 1 : move_index); + int value = (move_index == 0 ? -1 : move_index); move_h_options_negative.emplace_back( EQ::SayLinkEngine::GenerateQuestSaylink( fmt::format("#door edit {} {}", move_option, value), @@ -283,7 +283,7 @@ void DoorManipulation::CommandHandler(Client *c, const Seperator *sep) } for (int move_index = -100; move_index <= 0; move_index += 10) { - int value = (move_index == 0 ? 1 : move_index); + int value = (move_index == 0 ? -1 : move_index); set_size_options_negative.emplace_back( EQ::SayLinkEngine::GenerateQuestSaylink( fmt::format("#door edit {} {}", move_option, value), @@ -297,26 +297,30 @@ void DoorManipulation::CommandHandler(Client *c, const Seperator *sep) // we're passing a move action here if (!arg3.empty() && StringIsNumber(arg3)) { - int x_move = 0; - int y_move = 0; - int z_move = 0; - int h_move = 0; - int set_size = 0; + float x_move = 0.0f; + float y_move = 0.0f; + float z_move = 0.0f; + float h_move = 0.0f; + float set_size = 0.0f; if (arg2 == move_x_action) { - x_move = std::atoi(arg3.c_str()); + x_move = std::atof(arg3.c_str()); } + if (arg2 == move_y_action) { - y_move = std::atoi(arg3.c_str()); + y_move = std::atof(arg3.c_str()); } + if (arg2 == move_z_action) { - z_move = std::atoi(arg3.c_str()); + z_move = std::atof(arg3.c_str()); } + if (arg2 == move_h_action) { - h_move = std::atoi(arg3.c_str()); + h_move = std::atof(arg3.c_str()); } + if (arg2 == set_size_action) { - set_size = std::atoi(arg3.c_str()); + set_size = std::atof(arg3.c_str()); } door->SetLocation( From 9aac12f517a8cd17086b963b2c3ff1392d74e268 Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Wed, 22 Sep 2021 18:21:53 -0500 Subject: [PATCH 208/624] Hide tradeskill recipes that require being learned before crafting them, as well as fix how learned recipes are checked. --- zone/tradeskills.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index 7e0ce5857..950ddb06d 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -810,6 +810,14 @@ void Client::SendTradeskillSearchResults( continue; } } + + //Check if we need to learn it before sending them the recipe.. + DBTradeskillRecipe_Struct spec; + if (content_db.GetTradeRecipe(recipe_id, objtype, someid, this->CharacterID(), &spec)) { + if ((spec.must_learn & 0xf) && !spec.has_learnt) { + continue; + } + } auto outapp = new EQApplicationPacket(OP_RecipeReply, sizeof(RecipeReply_Struct)); RecipeReply_Struct *reply = (RecipeReply_Struct *) outapp->pBuffer; @@ -1489,7 +1497,7 @@ bool ZoneDatabase::GetTradeRecipe( recipe_id ); - if (character_learned_recipe.made_count > 0) { + if (character_learned_recipe.recipe_id) { //If this exists we learned it LogTradeskills("[GetTradeRecipe] made_count [{}]", character_learned_recipe.made_count); spec->has_learnt = true; From bf8d94eb35fdb1ba53065042299bdcf5cbc2466b Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Wed, 22 Sep 2021 21:43:47 -0500 Subject: [PATCH 209/624] Fix SendTradeskillSearchResults row count was incorrect format. Remove extra database hits from last commit. --- zone/client_packet.cpp | 6 ++++-- zone/tradeskills.cpp | 29 +++++++++++++++-------------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 6d5dd2314..a74f65180 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -12198,7 +12198,8 @@ void Client::Handle_OP_RecipesFavorite(const EQApplicationPacket *app) tr.name, tr.trivial, SUM(tre.componentcount), - tr.tradeskill + tr.tradeskill, + tr.must_learn FROM tradeskill_recipe AS tr LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id = tre.recipe_id @@ -12298,7 +12299,8 @@ void Client::Handle_OP_RecipesSearch(const EQApplicationPacket *app) tr.name, tr.trivial, SUM(tre.componentcount), - tr.tradeskill + tr.tradeskill, + tr.must_learn FROM tradeskill_recipe AS tr LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id = tre.recipe_id diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index 950ddb06d..ea46fae9a 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -782,7 +782,7 @@ void Client::SendTradeskillSearchResults( for (auto row = results.begin(); row != results.end(); ++row) { if (row == nullptr || row[0] == nullptr || row[1] == nullptr || row[2] == nullptr || row[3] == nullptr || - row[5] == nullptr) { + row[4] == nullptr || row[5] == nullptr) { continue; } @@ -790,35 +790,36 @@ void Client::SendTradeskillSearchResults( const char *name = row[1]; uint32 trivial = (uint32) atoi(row[2]); uint32 comp_count = (uint32) atoi(row[3]); - uint32 tradeskill = (uint16) atoi(row[5]); + uint32 tradeskill = (uint16) atoi(row[4]); + uint32 must_learn = (uint16) atoi(row[5]); + // Skip the recipes that exceed the threshold in skill difference // Recipes that have either been made before or were // explicitly learned are excempt from that limit + + auto character_learned_recipe = CharacterRecipeListRepository::GetRecipe( + character_learned_recipe_list, + recipe_id + ); + if (RuleB(Skills, UseLimitTradeskillSearchSkillDiff) && ((int32) trivial - (int32) GetSkill((EQ::skills::SkillType) tradeskill)) > RuleI(Skills, MaxTradeskillSearchSkillDiff)) { LogTradeskills("Checking limit recipe_id [{}] name [{}]", recipe_id, name); - auto character_learned_recipe = CharacterRecipeListRepository::GetRecipe( - character_learned_recipe_list, - recipe_id - ); - if (character_learned_recipe.made_count == 0) { continue; } } - - //Check if we need to learn it before sending them the recipe.. - DBTradeskillRecipe_Struct spec; - if (content_db.GetTradeRecipe(recipe_id, objtype, someid, this->CharacterID(), &spec)) { - if ((spec.must_learn & 0xf) && !spec.has_learnt) { - continue; - } + + //Skip recipes that must be learned + if ((must_learn & 0xf) && !character_learned_recipe.recipe_id) { + continue; } + auto outapp = new EQApplicationPacket(OP_RecipeReply, sizeof(RecipeReply_Struct)); RecipeReply_Struct *reply = (RecipeReply_Struct *) outapp->pBuffer; From 1ce5087e2a90599735e1a412066939f8b3236aa2 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 23 Sep 2021 09:35:09 -0400 Subject: [PATCH 210/624] Update effects.cpp --- zone/effects.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/zone/effects.cpp b/zone/effects.cpp index fcc4d8f1c..5fb9216c0 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -318,6 +318,15 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { if (target) { value += int(value_BaseEffect + target->GetFocusIncoming(focusFcHealPctIncoming, SE_FcHealPctIncoming, this, spell_id) / 100); //SPA 393 Add before critical value += int(value_BaseEffect + target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id) / 100); //SPA 395 Add before critical (?) + + int test1 = 0; + int test2 = 0; + + test1 += target->GetFocusIncoming(focusFcHealPctIncoming, SE_FcHealPctIncoming, this, spell_id); //SPA 393 Add before critical + test2 = target->GetFocusEffect(focusFcHealPctIncoming, spell_id); //SPA 393 Add before critical + + Shout("Test GetFocusIncomming %i", test1); + Shout("Test GetFocusEffect %i", test2); } value += GetFocusEffect(focusFcHealAmtCrit, spell_id); //SPA 396 Add before critical From 3faa0d2603e75a1e7d3897d20c8dd3da3c37a73a Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 23 Sep 2021 11:41:36 -0400 Subject: [PATCH 211/624] update --- zone/effects.cpp | 18 ++++----------- zone/mob.h | 1 - zone/spell_effects.cpp | 52 ------------------------------------------ 3 files changed, 4 insertions(+), 67 deletions(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index 5fb9216c0..d6b1b7b51 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -316,17 +316,8 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { if (spells[spell_id].buffduration < 1) { if (target) { - value += int(value_BaseEffect + target->GetFocusIncoming(focusFcHealPctIncoming, SE_FcHealPctIncoming, this, spell_id) / 100); //SPA 393 Add before critical - value += int(value_BaseEffect + target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id) / 100); //SPA 395 Add before critical (?) - - int test1 = 0; - int test2 = 0; - - test1 += target->GetFocusIncoming(focusFcHealPctIncoming, SE_FcHealPctIncoming, this, spell_id); //SPA 393 Add before critical - test2 = target->GetFocusEffect(focusFcHealPctIncoming, spell_id); //SPA 393 Add before critical - - Shout("Test GetFocusIncomming %i", test1); - Shout("Test GetFocusEffect %i", test2); + value += int(value_BaseEffect + target->GetFocusEffect(focusFcHealPctIncoming, spell_id)/100); //SPA 393 Add before critical + value += int(value_BaseEffect + target->GetFocusEffect(focusFcHealPctCritIncoming, spell_id)/100); //SPA 395 Add before critical (?) } value += GetFocusEffect(focusFcHealAmtCrit, spell_id); //SPA 396 Add before critical @@ -350,10 +341,9 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { value += GetFocusEffect(focusFcAmplifyAmt, spell_id); //SPA 508 ? Add after critical if (target) { - value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); //SPA 394 Add after critical + value += target->GetFocusEffect(focusFcHealAmtIncoming, spell_id); //SPA 394 Add after critical } - - + if (IsNPC() && CastToNPC()->GetHealScale()) { value = int(static_cast(value) * CastToNPC()->GetHealScale() / 100.0f); } diff --git a/zone/mob.h b/zone/mob.h index 3b07bfcdf..cda5285d0 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -803,7 +803,6 @@ public: uint16 GetSpellEffectResistChance(uint16 spell_id); int32 GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining); int32 GetFcDamageAmtIncoming(Mob *caster, uint32 spell_id, bool use_skill = false, uint16 skill=0); - int32 GetFocusIncoming(focusType type, int effect, Mob *caster, uint32 spell_id); int32 GetSkillDmgTaken(const EQ::skills::SkillType skill_used, ExtraAttackOptions *opts = nullptr); int32 GetPositionalDmgTaken(Mob *attacker); int32 GetPositionalDmgTakenAmt(Mob *attacker); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index b8d9ec55b..2cf808487 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -6929,58 +6929,6 @@ int32 Mob::GetFcDamageAmtIncoming(Mob *caster, uint32 spell_id, bool use_skill, return dmg; } -int32 Mob::GetFocusIncoming(focusType type, int effect, Mob *caster, uint32 spell_id) { - - /* - This is a general function for calculating best focus effect values for focus effects that exist on targets but modify incoming spells. - Should be used when checking for foci that can exist on clients or npcs ect. - Example: When your target has a focus limited buff that increases amount of healing on them. - */ - - if (!caster) { - return 0; - } - - int value = 0; - - if (spellbonuses.FocusEffects[type]){ - - int32 tmp_focus = 0; - int tmp_buffslot = -1; - - int buff_count = GetMaxTotalSlots(); - for(int i = 0; i < buff_count; i++) { - - if((IsValidSpell(buffs[i].spellid) && IsEffectInSpell(buffs[i].spellid, effect))){ - - int32 focus = caster->CalcFocusEffect(type, buffs[i].spellid, spell_id); - - if (!focus) { - continue; - } - - if (tmp_focus && focus > tmp_focus){ - tmp_focus = focus; - tmp_buffslot = i; - } - - else if (!tmp_focus){ - tmp_focus = focus; - tmp_buffslot = i; - } - } - } - - value = tmp_focus; - - if (tmp_buffslot >= 0) - CheckNumHitsRemaining(NumHit::MatchingSpells, tmp_buffslot); - } - - - return value; -} - int32 Mob::ApplySpellEffectiveness(int16 spell_id, int32 value, bool IsBard, uint16 caster_id) { // 9-17-12: This is likely causing crashes, disabled till can resolve. From 881dc33c9b01b2e8336e3c99211938bd21043443 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 23 Sep 2021 12:00:16 -0400 Subject: [PATCH 212/624] update --- common/spdat.h | 4 ++-- zone/spell_effects.cpp | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 81cb122eb..d626219d5 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -1076,9 +1076,9 @@ typedef enum { #define SE_FcTimerLockout 390 // implemented, @Fc, On Caster, set a spell to be on recast timer, base: recast duration milliseconds, Note: Applied from casted spells only #define SE_LimitManaMax 391 // implemented, @Ff, Mininum mana of spell that can be focused, base1: mana amt #define SE_FcHealAmt 392 // implemented, @Fc, On Caster, spell healing mod flat amt, base: amt -#define SE_FcHealPctIncoming 393 // implemented, @Fc, On Target, heal received critical chance mod, base: chance pct +#define SE_FcHealPctIncoming 393 // implemented, @Fc, On Target, heal received mod pct, base: pct, limit: random max pct #define SE_FcHealAmtIncoming 394 // implemented, @Fc, On Target, heal received mod flat amt, base: amt -#define SE_FcHealPctCritIncoming 395 // implemented, @Fc, On Target, heal received mod pct, base: pct +#define SE_FcHealPctCritIncoming 395 // implemented, @Fc, On Target, heal received mod pct, base: pct, limit: random max pct #define SE_FcHealAmtCrit 396 // implemented, @Fc, On Caster, spell healing mod flat amt, base: amt #define SE_PetMeleeMitigation 397 // implemented[AA] - additional mitigation to your pets. Adds AC #define SE_SwarmPetDuration 398 // implemented - Affects the duration of swarm pets diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 2cf808487..a268327d9 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -6370,8 +6370,9 @@ int32 NPC::GetFocusEffect(focusType type, uint16 spell_id) { //Improved Healing, Damage & Mana Reduction are handled differently in that some are random percentages //In these cases we need to find the most powerful effect, so that each piece of gear wont get its own chance - if(RuleB(Spells, LiveLikeFocusEffects) && (type == focusManaCost || type == focusImprovedHeal || type == focusImprovedDamage || type == focusImprovedDamage2)) + if (RuleB(Spells, LiveLikeFocusEffects) && CanFocusUseRandomEffectivenessByType(type)) { rand_effectiveness = true; + } if (RuleB(Spells, NPC_UseFocusFromItems) && itembonuses.FocusEffects[type]){ From 933ede40f96f21e6a8081cc11c44c55263d340d3 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 23 Sep 2021 12:13:29 -0400 Subject: [PATCH 213/624] Update bot.cpp --- zone/bot.cpp | 116 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 84 insertions(+), 32 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 0cb352a18..106961b95 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -6747,49 +6747,101 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { } int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { - if (target == nullptr) - target = this; + + + if (IsNPC()) { + value += value * CastToNPC()->GetSpellFocusHeal() / 100; + } int32 value_BaseEffect = 0; - int32 chance = 0; - int8 modifier = 1; - bool Critical = false; - value_BaseEffect = (value + (value*GetBotFocusEffect(focusFcBaseEffects, spell_id) / 100)); - value = value_BaseEffect; - value += int(value_BaseEffect*GetBotFocusEffect(focusImprovedHeal, spell_id) / 100); - if(spells[spell_id].buffduration < 1) { - chance += (itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance); - chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); - if (spellbonuses.CriticalHealDecay) - chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay); + int16 critical_chance = 0; + int8 critical_modifier = 1; - if(chance && (zone->random.Int(0, 99) < chance)) { - Critical = true; - modifier = 2; + if (spells[spell_id].buffduration < 1) { + critical_chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance; + + if (spellbonuses.CriticalHealDecay) { + critical_chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay); + } + } + else { + critical_chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime; + + if (spellbonuses.CriticalRegenDecay) { + critical_chance += GetDecayEffectValue(spell_id, SE_CriticalRegenDecay); + } + } + + if (critical_chance) { + + if (spells[spell_id].override_crit_chance > 0 && critical_chance > spells[spell_id].override_crit_chance) { + critical_chance = spells[spell_id].override_crit_chance; } - value *= modifier; - value += (GetBotFocusEffect(focusFcHealAmtCrit, spell_id) * modifier); - value += GetBotFocusEffect(focusFcHealAmt, spell_id); - value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); + if (zone->random.Roll(critical_chance)) { + critical_modifier = 2; //At present time no critical heal amount modifier SPA exists. + } + } - if(itembonuses.HealAmt && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) - value += (GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value) * modifier); + value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id) / 100); - value += (value * target->GetHealRate(spell_id, this) / 100); - if (Critical) + value = value_BaseEffect; + + if (GetClass() == CLERIC) { + value += int(value_BaseEffect*RuleI(Spells, ClericInnateHealFocus) / 100); //confirmed on live parsing clerics get an innate 5 pct heal focus + } + value += int(value_BaseEffect*GetFocusEffect(focusImprovedHeal, spell_id) / 100); + value += int(value_BaseEffect*GetFocusEffect(focusFcAmplifyMod, spell_id) / 100); + + // Instant Heals + if (spells[spell_id].buffduration < 1) { + + if (target) { + value += int(value_BaseEffect + target->GetFocusEffect(focusFcHealPctIncoming, spell_id) / 100); //SPA 393 Add before critical + value += int(value_BaseEffect + target->GetFocusEffect(focusFcHealPctCritIncoming, spell_id) / 100); //SPA 395 Add before critical (?) + } + + value += GetFocusEffect(focusFcHealAmtCrit, spell_id); //SPA 396 Add before critical + + if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) { + value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value); //Item Heal Amt Add before critical + } + + if (target) { + int incoming_heal_mod_percent = target->itembonuses.HealRate + target->spellbonuses.HealRate + target->aabonuses.HealRate; //SPA 120 modifies value after Focus Applied but before critical + incoming_heal_mod_percent = std::min(incoming_heal_mod_percent, -100); + value += value * incoming_heal_mod_percent / 100; + } + + /* + Apply critical hit modifier + */ + + value *= critical_modifier; + value += GetFocusEffect(focusFcHealAmt, spell_id); //SPA 392 Add after critical + value += GetFocusEffect(focusFcAmplifyAmt, spell_id); //SPA 508 ? Add after critical + + if (target) { + value += target->GetFocusEffect(focusFcHealAmtIncoming, spell_id); //SPA 394 Add after critical + } + + if (critical_modifier > 1) { entity_list.MessageClose(this, false, 100, Chat::SpellCrit, "%s performs an exceptional heal! (%d)", GetName(), value); + } return value; - } else { - chance = (itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime); - chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); - if (spellbonuses.CriticalRegenDecay) - chance += GetDecayEffectValue(spell_id, SE_CriticalRegenDecay); - - if(chance && (zone->random.Int(0,99) < chance)) - return (value * 2); } + + //Heal over time spells. [Heal Rate and Additional Healing effects do not increase this value] + else { + if (critical_chance && zone->random.Roll(critical_chance)) + value *= critical_modifier; + } + + if (IsNPC() && CastToNPC()->GetHealScale()) { + value = int(static_cast(value) * CastToNPC()->GetHealScale() / 100.0f); + } + return value; } From 34b2264d5d9c3554952557284e343cf6984cdf12 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 23 Sep 2021 12:21:53 -0400 Subject: [PATCH 214/624] bots... --- zone/bot.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 106961b95..1f8f21f44 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -6790,15 +6790,15 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { if (GetClass() == CLERIC) { value += int(value_BaseEffect*RuleI(Spells, ClericInnateHealFocus) / 100); //confirmed on live parsing clerics get an innate 5 pct heal focus } - value += int(value_BaseEffect*GetFocusEffect(focusImprovedHeal, spell_id) / 100); - value += int(value_BaseEffect*GetFocusEffect(focusFcAmplifyMod, spell_id) / 100); + value += int(value_BaseEffect*GetBotFocusEffect(focusImprovedHeal, spell_id) / 100); + value += int(value_BaseEffect*GetBotFocusEffect(focusFcAmplifyMod, spell_id) / 100); // Instant Heals if (spells[spell_id].buffduration < 1) { if (target) { - value += int(value_BaseEffect + target->GetFocusEffect(focusFcHealPctIncoming, spell_id) / 100); //SPA 393 Add before critical - value += int(value_BaseEffect + target->GetFocusEffect(focusFcHealPctCritIncoming, spell_id) / 100); //SPA 395 Add before critical (?) + value += int(value_BaseEffect + target->GetBotFocusEffect(focusFcHealPctIncoming, spell_id) / 100); //SPA 393 Add before critical + value += int(value_BaseEffect + target->GetBotFocusEffect(focusFcHealPctCritIncoming, spell_id) / 100); //SPA 395 Add before critical (?) } value += GetFocusEffect(focusFcHealAmtCrit, spell_id); //SPA 396 Add before critical @@ -6818,11 +6818,11 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { */ value *= critical_modifier; - value += GetFocusEffect(focusFcHealAmt, spell_id); //SPA 392 Add after critical - value += GetFocusEffect(focusFcAmplifyAmt, spell_id); //SPA 508 ? Add after critical + value += GetBotFocusEffect(focusFcHealAmt, spell_id); //SPA 392 Add after critical + value += GetBotFocusEffect(focusFcAmplifyAmt, spell_id); //SPA 508 ? Add after critical if (target) { - value += target->GetFocusEffect(focusFcHealAmtIncoming, spell_id); //SPA 394 Add after critical + value += target->GetBotFocusEffect(focusFcHealAmtIncoming, spell_id); //SPA 394 Add after critical } if (critical_modifier > 1) { @@ -6838,10 +6838,6 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { value *= critical_modifier; } - if (IsNPC() && CastToNPC()->GetHealScale()) { - value = int(static_cast(value) * CastToNPC()->GetHealScale() / 100.0f); - } - return value; } From 03ac8281345852c0f0889e1b3dd8a063708e6e45 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 23 Sep 2021 12:28:34 -0400 Subject: [PATCH 215/624] Update bot.cpp --- zone/bot.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 1f8f21f44..3308a343c 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -6797,8 +6797,8 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { if (spells[spell_id].buffduration < 1) { if (target) { - value += int(value_BaseEffect + target->GetBotFocusEffect(focusFcHealPctIncoming, spell_id) / 100); //SPA 393 Add before critical - value += int(value_BaseEffect + target->GetBotFocusEffect(focusFcHealPctCritIncoming, spell_id) / 100); //SPA 395 Add before critical (?) + value += int(value_BaseEffect + target->GetFocusEffect(focusFcHealPctIncoming, spell_id) / 100); //SPA 393 Add before critical + value += int(value_BaseEffect + target->GetFocusEffect(focusFcHealPctCritIncoming, spell_id) / 100); //SPA 395 Add before critical (?) } value += GetFocusEffect(focusFcHealAmtCrit, spell_id); //SPA 396 Add before critical @@ -6822,7 +6822,7 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { value += GetBotFocusEffect(focusFcAmplifyAmt, spell_id); //SPA 508 ? Add after critical if (target) { - value += target->GetBotFocusEffect(focusFcHealAmtIncoming, spell_id); //SPA 394 Add after critical + value += target->GetFocusEffect(focusFcHealAmtIncoming, spell_id); //SPA 394 Add after critical } if (critical_modifier > 1) { From 456fb56e826320d91a47595801945d155d4ed83f Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 23 Sep 2021 13:42:36 -0400 Subject: [PATCH 216/624] revert for bots --- zone/bot.cpp | 106 +++++++++++------------------------------ zone/mob.h | 2 + zone/spell_effects.cpp | 52 ++++++++++++++++++++ 3 files changed, 83 insertions(+), 77 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 3308a343c..0cb352a18 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -6747,97 +6747,49 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { } int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { - - - if (IsNPC()) { - value += value * CastToNPC()->GetSpellFocusHeal() / 100; - } + if (target == nullptr) + target = this; int32 value_BaseEffect = 0; - int16 critical_chance = 0; - int8 critical_modifier = 1; - - if (spells[spell_id].buffduration < 1) { - critical_chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance; - - if (spellbonuses.CriticalHealDecay) { - critical_chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay); - } - } - else { - critical_chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime; - - if (spellbonuses.CriticalRegenDecay) { - critical_chance += GetDecayEffectValue(spell_id, SE_CriticalRegenDecay); - } - } - - if (critical_chance) { - - if (spells[spell_id].override_crit_chance > 0 && critical_chance > spells[spell_id].override_crit_chance) { - critical_chance = spells[spell_id].override_crit_chance; - } - - if (zone->random.Roll(critical_chance)) { - critical_modifier = 2; //At present time no critical heal amount modifier SPA exists. - } - } - - value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id) / 100); - + int32 chance = 0; + int8 modifier = 1; + bool Critical = false; + value_BaseEffect = (value + (value*GetBotFocusEffect(focusFcBaseEffects, spell_id) / 100)); value = value_BaseEffect; - - if (GetClass() == CLERIC) { - value += int(value_BaseEffect*RuleI(Spells, ClericInnateHealFocus) / 100); //confirmed on live parsing clerics get an innate 5 pct heal focus - } value += int(value_BaseEffect*GetBotFocusEffect(focusImprovedHeal, spell_id) / 100); - value += int(value_BaseEffect*GetBotFocusEffect(focusFcAmplifyMod, spell_id) / 100); + if(spells[spell_id].buffduration < 1) { + chance += (itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance); + chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); + if (spellbonuses.CriticalHealDecay) + chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay); - // Instant Heals - if (spells[spell_id].buffduration < 1) { - - if (target) { - value += int(value_BaseEffect + target->GetFocusEffect(focusFcHealPctIncoming, spell_id) / 100); //SPA 393 Add before critical - value += int(value_BaseEffect + target->GetFocusEffect(focusFcHealPctCritIncoming, spell_id) / 100); //SPA 395 Add before critical (?) + if(chance && (zone->random.Int(0, 99) < chance)) { + Critical = true; + modifier = 2; } - value += GetFocusEffect(focusFcHealAmtCrit, spell_id); //SPA 396 Add before critical + value *= modifier; + value += (GetBotFocusEffect(focusFcHealAmtCrit, spell_id) * modifier); + value += GetBotFocusEffect(focusFcHealAmt, spell_id); + value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); - if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) { - value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value); //Item Heal Amt Add before critical - } + if(itembonuses.HealAmt && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) + value += (GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value) * modifier); - if (target) { - int incoming_heal_mod_percent = target->itembonuses.HealRate + target->spellbonuses.HealRate + target->aabonuses.HealRate; //SPA 120 modifies value after Focus Applied but before critical - incoming_heal_mod_percent = std::min(incoming_heal_mod_percent, -100); - value += value * incoming_heal_mod_percent / 100; - } - - /* - Apply critical hit modifier - */ - - value *= critical_modifier; - value += GetBotFocusEffect(focusFcHealAmt, spell_id); //SPA 392 Add after critical - value += GetBotFocusEffect(focusFcAmplifyAmt, spell_id); //SPA 508 ? Add after critical - - if (target) { - value += target->GetFocusEffect(focusFcHealAmtIncoming, spell_id); //SPA 394 Add after critical - } - - if (critical_modifier > 1) { + value += (value * target->GetHealRate(spell_id, this) / 100); + if (Critical) entity_list.MessageClose(this, false, 100, Chat::SpellCrit, "%s performs an exceptional heal! (%d)", GetName(), value); - } return value; - } + } else { + chance = (itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime); + chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); + if (spellbonuses.CriticalRegenDecay) + chance += GetDecayEffectValue(spell_id, SE_CriticalRegenDecay); - //Heal over time spells. [Heal Rate and Additional Healing effects do not increase this value] - else { - if (critical_chance && zone->random.Roll(critical_chance)) - value *= critical_modifier; + if(chance && (zone->random.Int(0,99) < chance)) + return (value * 2); } - return value; } diff --git a/zone/mob.h b/zone/mob.h index cda5285d0..3975efb90 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -803,6 +803,7 @@ public: uint16 GetSpellEffectResistChance(uint16 spell_id); int32 GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining); int32 GetFcDamageAmtIncoming(Mob *caster, uint32 spell_id, bool use_skill = false, uint16 skill=0); + int32 GetFocusIncoming(focusType type, int effect, Mob *caster, uint32 spell_id); int32 GetSkillDmgTaken(const EQ::skills::SkillType skill_used, ExtraAttackOptions *opts = nullptr); int32 GetPositionalDmgTaken(Mob *attacker); int32 GetPositionalDmgTakenAmt(Mob *attacker); @@ -842,6 +843,7 @@ public: bool HarmonySpellLevelCheck(int32 spell_id, Mob* target = nullptr); bool CanFocusUseRandomEffectivenessByType(focusType type); int GetFocusRandomEffectivenessValue(int focus_base, int focus_base2, bool best_focus = 0); + int GetHealRate() const { return itembonuses.HealRate + spellbonuses.HealRate + aabonuses.HealRate; } bool TryDoubleMeleeRoundEffect(); bool GetUseDoubleMeleeRoundDmgBonus() const { return use_double_melee_round_dmg_bonus; } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index a268327d9..5218afa69 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -6930,6 +6930,58 @@ int32 Mob::GetFcDamageAmtIncoming(Mob *caster, uint32 spell_id, bool use_skill, return dmg; } +int32 Mob::GetFocusIncoming(focusType type, int effect, Mob *caster, uint32 spell_id) { + + /* + This is a general function for calculating best focus effect values for focus effects that exist on targets but modify incoming spells. + Should be used when checking for foci that can exist on clients or npcs ect. + Example: When your target has a focus limited buff that increases amount of healing on them. + */ + + if (!caster) { + return 0; + } + + int value = 0; + + if (spellbonuses.FocusEffects[type]){ + + int32 tmp_focus = 0; + int tmp_buffslot = -1; + + int buff_count = GetMaxTotalSlots(); + for(int i = 0; i < buff_count; i++) { + + if((IsValidSpell(buffs[i].spellid) && IsEffectInSpell(buffs[i].spellid, effect))){ + + int32 focus = caster->CalcFocusEffect(type, buffs[i].spellid, spell_id); + + if (!focus) { + continue; + } + + if (tmp_focus && focus > tmp_focus){ + tmp_focus = focus; + tmp_buffslot = i; + } + + else if (!tmp_focus){ + tmp_focus = focus; + tmp_buffslot = i; + } + } + } + + value = tmp_focus; + + if (tmp_buffslot >= 0) + CheckNumHitsRemaining(NumHit::MatchingSpells, tmp_buffslot); + } + + + return value; +} + int32 Mob::ApplySpellEffectiveness(int16 spell_id, int32 value, bool IsBard, uint16 caster_id) { // 9-17-12: This is likely causing crashes, disabled till can resolve. From e89c2aec4adb3cf3805d0bb62be59a230b0b667e Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 23 Sep 2021 13:48:15 -0400 Subject: [PATCH 217/624] Update bot.cpp --- zone/bot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 0cb352a18..9f9f723b9 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -6776,7 +6776,7 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { if(itembonuses.HealAmt && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) value += (GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value) * modifier); - value += (value * target->GetHealRate(spell_id, this) / 100); + value += (value * target->GetHealRate() / 100); if (Critical) entity_list.MessageClose(this, false, 100, Chat::SpellCrit, "%s performs an exceptional heal! (%d)", GetName(), value); From b6991962993aa941d8adc44871bcf2cf831ecef1 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 23 Sep 2021 14:14:46 -0400 Subject: [PATCH 218/624] Update effects.cpp --- zone/effects.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index d6b1b7b51..571abe6da 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -327,9 +327,7 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { } if (target) { - int incoming_heal_mod_percent = target->itembonuses.HealRate + target->spellbonuses.HealRate + target->aabonuses.HealRate; //SPA 120 modifies value after Focus Applied but before critical - incoming_heal_mod_percent = std::min(incoming_heal_mod_percent, -100); - value += value * incoming_heal_mod_percent / 100; + value += value * target->GetHealRate() / 100; //SPA 120 modifies value after Focus Applied but before critical } /* From 5cd9bfeb70879d21af3dfed36ec90dfdbdbfd1f4 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 23 Sep 2021 14:23:17 -0400 Subject: [PATCH 219/624] reminder bot code needs to updated, then old function can be removed --- zone/mob.h | 2 +- zone/spell_effects.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/zone/mob.h b/zone/mob.h index 3975efb90..7d07c3f7f 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -803,7 +803,7 @@ public: uint16 GetSpellEffectResistChance(uint16 spell_id); int32 GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining); int32 GetFcDamageAmtIncoming(Mob *caster, uint32 spell_id, bool use_skill = false, uint16 skill=0); - int32 GetFocusIncoming(focusType type, int effect, Mob *caster, uint32 spell_id); + int32 GetFocusIncoming(focusType type, int effect, Mob *caster, uint32 spell_id); //**** This can be removed when bot healing focus code is updated **** int32 GetSkillDmgTaken(const EQ::skills::SkillType skill_used, ExtraAttackOptions *opts = nullptr); int32 GetPositionalDmgTaken(Mob *attacker); int32 GetPositionalDmgTakenAmt(Mob *attacker); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 5218afa69..aaa6f38ad 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -6932,6 +6932,8 @@ int32 Mob::GetFcDamageAmtIncoming(Mob *caster, uint32 spell_id, bool use_skill, int32 Mob::GetFocusIncoming(focusType type, int effect, Mob *caster, uint32 spell_id) { + //**** This can be removed when bot healing focus code is updated **** + /* This is a general function for calculating best focus effect values for focus effects that exist on targets but modify incoming spells. Should be used when checking for foci that can exist on clients or npcs ect. From ea9c07aa98f9fcd375d7539dc3335c22544cfdae Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 23 Sep 2021 16:43:07 -0400 Subject: [PATCH 220/624] 393 NegateEffect updates --- common/spdat.h | 2 +- zone/mob.h | 1 + zone/spell_effects.cpp | 30 +++++++++++++++++++++++++++++- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 64e831516..98423f9a7 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -1067,7 +1067,7 @@ typedef enum { #define SE_ShadowStepDirectional 379 // implemented - handled by client #define SE_Knockdown 380 // implemented - small knock back(handled by client) //#define SE_KnockTowardCaster 381 // *not implemented (Call of Hither) knocks you back to caster (value) distance units infront -#define SE_NegateSpellEffect 382 // implemented, @Debuff, negates specific spell effect benefits for the duration of the debuff, base: see NegateSpellEffecttype Enum, limit: SPA id, max: none +#define SE_NegateSpellEffect 382 // implemented, @Debuff, negates specific spell effect benefits for the duration of the debuff and prevent non-duration spell effect from working, base: see NegateSpellEffecttype Enum, limit: SPA id, max: none #define SE_SympatheticProc 383 // implemented, @Fc, On Caster, cast on spell use, base: variable proc chance on cast time, limit: spellid #define SE_Leap 384 // implemented - Leap effect, ie stomping leap #define SE_LimitSpellGroup 385 // implemented, @Ff, Spell group(s) that a spell focus can require or exclude, base1: spellgroup id, Include: Positive Exclude: Negative diff --git a/zone/mob.h b/zone/mob.h index 9ccf846c9..f44c715ce 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -298,6 +298,7 @@ public: uint8 WornType = 0, int32 ticsremaining = 0, int buffslot = -1, int instrument_mod = 10, bool IsAISpellEffect = false, uint16 effect_id = 0, int32 se_base = 0, int32 se_limit = 0, int32 se_max = 0); void NegateSpellEffectBonuses(uint16 spell_id); + bool NegateSpellEffect(uint16 spell_id, int effect_id); virtual float GetActSpellRange(uint16 spell_id, float range, bool IsBard = false); virtual int32 GetActSpellDamage(uint16 spell_id, int32 value, Mob* target = nullptr); virtual int32 GetActDoTDamage(uint16 spell_id, int32 value, Mob* target); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 68fa5e897..6fb4ce5e1 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -216,10 +216,19 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if (GetSpellPowerDistanceMod()) effect_value = effect_value*GetSpellPowerDistanceMod()/100; + //Prevents effect from being applied + if (spellbonuses.NegateEffects) { + if (effect != SE_NegateSpellEffect && NegateSpellEffect(spell_id, effect)) { + if (caster) { + caster->Message(Chat::Red, "Part or all of this spell has lost its effectiveness."); //Placeholder msg, until live one is obtained. + } + continue; + } + } + #ifdef SPELL_EFFECT_SPAM effect_desc[0] = 0; #endif - switch(effect) { case SE_CurrentHP: // nukes, heals; also regen/dot if a buff @@ -8513,3 +8522,22 @@ int Mob::GetFocusRandomEffectivenessValue(int focus_base, int focus_base2, bool return zone->random.Int(focus_base, focus_base2); } + +bool Mob::NegateSpellEffect(uint16 spell_id, int effect_id) +{ + //*This works for most effects, anything handled purely by the client will bypass this (ie Gate, Shadowstep) + + for (int i = 0; i < GetMaxTotalSlots(); i++) { + //Check for any buffs containing NegateEffect + if (IsValidSpell(buffs[i].spellid) && IsEffectInSpell(buffs[i].spellid, SE_NegateSpellEffect) && spell_id != buffs[i].spellid) { + //Match each of the negate effects with the current spell effect, if found, that effect will not be applied. + for (int j = 0; j < EFFECT_COUNT; j++) + { + if (spells[buffs[i].spellid].base2[j] == effect_id) { + return true; + } + } + } + } + return false; +} From 27787c247b6db379d2f8719d3a90d1b5beca9453 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 23 Sep 2021 18:01:08 -0400 Subject: [PATCH 221/624] Update spell_effects.cpp --- zone/spell_effects.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 6fb4ce5e1..c8ed3929c 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -8525,7 +8525,10 @@ int Mob::GetFocusRandomEffectivenessValue(int focus_base, int focus_base2, bool bool Mob::NegateSpellEffect(uint16 spell_id, int effect_id) { - //*This works for most effects, anything handled purely by the client will bypass this (ie Gate, Shadowstep) + /* + This works for most effects, anything handled purely by the client will bypass this (ie Gate, Shadowstep) + Seen with resurrection effects, likely blocks the client from accepting a ressurection request. *Not implement at this time. + */ for (int i = 0; i < GetMaxTotalSlots(); i++) { //Check for any buffs containing NegateEffect From d4f14efaa080ef8acbe9e9f91faf7a389f49be64 Mon Sep 17 00:00:00 2001 From: Noudess Date: Mon, 27 Sep 2021 10:04:02 -0400 Subject: [PATCH 222/624] Fix TimeSync to work with new Servertalk connection order --- zone/zone.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/zone.cpp b/zone/zone.cpp index 98fb346aa..e5b810a1f 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -153,7 +153,6 @@ bool Zone::Bootup(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone) { LogInfo("Zone Bootup: [{}] ([{}]: [{}])", zonename, iZoneID, iInstanceID); parse->Init(); UpdateWindowTitle(nullptr); - zone->GetTimeSync(); zone->RequestUCSServerStatus(); @@ -1818,7 +1817,8 @@ void Zone::Repop(uint32 delay) void Zone::GetTimeSync() { - if (worldserver.Connected() && !zone_has_current_time) { + if (!zone_has_current_time) { + LogInfo("Requesting world time"); auto pack = new ServerPacket(ServerOP_GetWorldTime, 1); worldserver.SendPacket(pack); safe_delete(pack); From 7fcea371c2d11bb4dc81f5f383c0f3a81966170a Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 30 Sep 2021 12:43:05 -0400 Subject: [PATCH 223/624] [Spells] Updated Memory Blur SPA 63 - Implemented Live Mechanics (#1559) * memory blur updated * Update spdat.h --- common/spdat.h | 4 +-- zone/mob.h | 2 ++ zone/spell_effects.cpp | 78 ++++++++++++++++++++++++++++++++---------- 3 files changed, 63 insertions(+), 21 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 64e831516..55af388b5 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -748,7 +748,7 @@ typedef enum { //#define SE_TransferItem 60 // not used #define SE_Identify 61 // implemented //#define SE_ItemID 62 // not used -#define SE_WipeHateList 63 // implemented +#define SE_WipeHateList 63 // implemented, @Memblur, chance to wipe hate list of target, base: pct chance, limit: none, max: ? (not implemented), Note: caster level and CHA add to pct chance #define SE_SpinTarget 64 // implemented - TO DO: Not sure stun portion is working correctly #define SE_InfraVision 65 // implemented #define SE_UltraVision 66 // implemented @@ -927,7 +927,7 @@ typedef enum { #define SE_FeignedCastOnChance 239 // implemented - ability gives you an increasing chance for your feigned deaths to not be revealed by spells cast upon you. //#define SE_StringUnbreakable 240 // not used [Likely related to above - you become immune to feign breaking on a resisted spell and have a good chance of feigning through a spell that successfully lands upon you.] #define SE_ImprovedReclaimEnergy 241 // implemented - increase the amount of mana returned to you when reclaiming your pet. -#define SE_IncreaseChanceMemwipe 242 // implemented - increases the chance to wipe hate with memory blurr +#define SE_IncreaseChanceMemwipe 242 // implemented - @Memblur, increases the chance to wipe hate with memory blurr, base: chance pct, limit: none, max: none, Note: Mods final blur chance after other bonuses added. #define SE_CharmBreakChance 243 // implemented - Total Domination #define SE_RootBreakChance 244 // implemented[AA] reduce the chance that your root will break. #define SE_TrapCircumvention 245 // *not implemented[AA] - decreases the chance that you will set off a trap when opening a chest diff --git a/zone/mob.h b/zone/mob.h index 9ccf846c9..098d19dac 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -845,6 +845,7 @@ public: bool HarmonySpellLevelCheck(int32 spell_id, Mob* target = nullptr); bool CanFocusUseRandomEffectivenessByType(focusType type); int GetFocusRandomEffectivenessValue(int focus_base, int focus_base2, bool best_focus = 0); + int GetMemoryBlurChance(int base_chance); bool TryDoubleMeleeRoundEffect(); bool GetUseDoubleMeleeRoundDmgBonus() const { return use_double_melee_round_dmg_bonus; } @@ -1533,6 +1534,7 @@ protected: bool endur_upkeep; bool degenerating_effects; // true if we have a buff that needs to be recalced every tick bool spawned_in_water; + public: bool GetWasSpawnedInWater() const; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 68fa5e897..e7e054302 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -63,6 +63,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if (spell.disallow_sit && IsBuffSpell(spell_id) && IsClient() && (CastToClient()->IsSitting() || CastToClient()->GetHorseId() != 0)) return false; + bool CanMemoryBlurFromMez = true; + if (IsMezzed()) { //Check for special memory blur behavior when on mez, this needs to be before buff override. + CanMemoryBlurFromMez = false; + } + bool c_override = false; if (caster && caster->IsClient() && GetCastedSpellInvSlot() > 0) { const EQ::ItemInstance *inst = caster->CastToClient()->GetInv().GetItem(GetCastedSpellInvSlot()); @@ -1521,16 +1526,16 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Memory Blur: %d", effect_value); #endif - int wipechance = spells[spell_id].base[i]; - int bonus = 0; - - if (caster){ - bonus = caster->spellbonuses.IncreaseChanceMemwipe + - caster->itembonuses.IncreaseChanceMemwipe + - caster->aabonuses.IncreaseChanceMemwipe; + //Memory blur component of Mez spells is not checked again if Mez is recast on a target that is already mezed + if (!CanMemoryBlurFromMez && IsEffectInSpell(spell_id, SE_Mez)) { + break; + } + + int wipechance = 0; + + if (caster) { + wipechance = caster->GetMemoryBlurChance(effect_value); } - - wipechance += wipechance*bonus/100; if(zone->random.Roll(wipechance)) { @@ -3850,19 +3855,15 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) } case SE_WipeHateList: { - if (IsMezSpell(buff.spellid)) + if (IsMezSpell(buff.spellid)) { break; - - int wipechance = spells[buff.spellid].base[i]; - int bonus = 0; - - if (caster) { - bonus = caster->spellbonuses.IncreaseChanceMemwipe + - caster->itembonuses.IncreaseChanceMemwipe + - caster->aabonuses.IncreaseChanceMemwipe; } - wipechance += wipechance * bonus / 100; + int wipechance = 0; + + if (caster) { + wipechance = caster->GetMemoryBlurChance(effect_value); + } if (zone->random.Roll(wipechance)) { if (IsAIControlled()) { @@ -8513,3 +8514,42 @@ int Mob::GetFocusRandomEffectivenessValue(int focus_base, int focus_base2, bool return zone->random.Int(focus_base, focus_base2); } + +int Mob::GetMemoryBlurChance(int base_chance) +{ + /* + Memory Blur mechanic for SPA 62 + Chance formula is effect chance + charisma modifer + caster level modifier + Effect chance is base value of spell + Charisma modifier is CHA/10 = %, with MAX of 15% (thus 150 cha gives you max bonus) + Caster level modifier. +100% if caster < level 17 which scales down to 25% at > 53. ** + (Yes the above gets worse as you level. Behavior was confirmed on live.) + Memory blur is applied to mez on initial cast using same formula. However, recasting on a target that + is already mezed will not give a chance to memory blur. The blur is not checked on buff ticks. + + SPA 242 SE_IncreaseChanceMemwipe modifies the final chance after all bonuses are applied. + This is also applied to memory blur from mez spells. + + this = caster + */ + int cha_mod = int(GetCHA() / 10); + cha_mod = std::min(cha_mod, 15); + + int lvl_mod = 0; + if (GetLevel() < 17) { + lvl_mod = 100; + } + else if (GetLevel() > 53) { + lvl_mod = 25; + } + else { + lvl_mod = 100 + ((GetLevel() - 16)*-2);//Derived from above range of values.** + } + + int chance = cha_mod + lvl_mod + base_chance; + + int chance_mod = spellbonuses.IncreaseChanceMemwipe + itembonuses.IncreaseChanceMemwipe + aabonuses.IncreaseChanceMemwipe; + + chance += chance * chance_mod / 100; + return chance; +} From c04bcef273e1e4a5b305ade2925069d8be84d444 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 30 Sep 2021 12:43:36 -0400 Subject: [PATCH 224/624] Update spells.cpp (#1554) --- zone/spells.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 9a3270d12..dd101ba51 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3406,7 +3406,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid buffs[emptyslot].focusproclimit_procamt = 0; buffs[emptyslot].instrument_mod = caster ? caster->GetInstrumentMod(spell_id) : 10; - if (level_override > 0) { + if (level_override > 0 || buffs[emptyslot].numhits > 0) { buffs[emptyslot].UpdateClient = true; } else { if (buffs[emptyslot].ticsremaining > (1 + CalcBuffDuration_formula(caster_level, spells[spell_id].buffdurationformula, spells[spell_id].buffduration))) From 0aeaf7c3b73c0c54dece7943881c81f1bd02751d Mon Sep 17 00:00:00 2001 From: Cole-SoD <90719745+Cole-SoD@users.noreply.github.com> Date: Thu, 30 Sep 2021 12:44:22 -0400 Subject: [PATCH 225/624] [Zone] Add LavaDamage and MinLavaDamage support to ZoneHeader (#1540) * Add LavaDamage and MinLavaDamage support to ZoneHeader * Add lava_damage and min_lava_damage to base_zone_repository.h * Update version.h and utils/sql/git/required/ file * Correct SQL Query, adjust utils/sql/db_update_manifest.txt to check one column * Correct manifest https://github.com/EQEmu/Server/pull/1540#discussion_r714330945 --- common/eq_packet_structs.h | 4 +++- common/patches/rof.cpp | 4 ++-- common/patches/rof2.cpp | 4 ++-- common/patches/rof_structs.h | 4 ++-- common/patches/sod.cpp | 4 ++-- common/patches/sod_structs.h | 4 ++-- common/patches/sof.cpp | 4 ++-- common/patches/sof_structs.h | 4 ++-- common/patches/uf.cpp | 4 ++-- common/patches/uf_structs.h | 4 ++-- .../repositories/base/base_zone_repository.h | 18 ++++++++++++++++++ common/version.h | 2 +- utils/sql/db_update_manifest.txt | 1 + .../required/2021_09_14_zone_lava_damage.sql | 1 + zone/zonedb.cpp | 6 +++++- 15 files changed, 47 insertions(+), 21 deletions(-) create mode 100644 utils/sql/git/required/2021_09_14_zone_lava_damage.sql diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 47f14bb4e..6f1a437a8 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -384,7 +384,9 @@ struct NewZone_Struct { /*0716*/ uint32 FastRegenEndurance; /*0720*/ uint32 NPCAggroMaxDist; /*0724*/ uint32 underworld_teleport_index; // > 0 teleports w/ zone point index, invalid succors, if this value is 0, it prevents you from running off edges that would end up underworld -/*0728*/ +/*0728*/ uint32 LavaDamage; // Seen 50 +/*0732*/ uint32 MinLavaDamage; // Seen 10 +/*0736*/ }; /* diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 38b109677..8e40e43ce 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -1863,8 +1863,8 @@ namespace RoF /*fill in some unknowns with observed values, hopefully it will help */ eq->unknown800 = -1; eq->unknown844 = 600; - eq->unknown880 = 50; - eq->unknown884 = 10; + OUT(LavaDamage); + OUT(MinLavaDamage); eq->unknown888 = 1; eq->unknown889 = 0; eq->unknown890 = 1; diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 55fbfe2c8..03f636560 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -1919,8 +1919,8 @@ namespace RoF2 eq->SkyRelated2 = -1; eq->NPCAggroMaxDist = 600; eq->FilterID = 2008; // Guild Lobby observed value - eq->LavaDamage = 50; - eq->MinLavaDamage = 10; + OUT(LavaDamage); + OUT(MinLavaDamage); eq->bDisallowManaStone = 1; eq->bNoBind = 0; eq->bNoAttack = 0; diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index a8297b2ea..d98827ce9 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -581,8 +581,8 @@ struct NewZone_Struct { /*0868*/ uint32 underworld_teleport_index; // > 0 teleports w/ zone point index, invalid succors, -1 affects some collisions /*0872*/ uint32 scriptIDSomething3; /*0876*/ uint32 SuspendBuffs; -/*0880*/ uint32 unknown880; // Seen 50 -/*0884*/ uint32 unknown884; // Seen 10 +/*0880*/ uint32 LavaDamage; // Seen 50 +/*0884*/ uint32 MinLavaDamage; // Seen 10 /*0888*/ uint8 unknown888; // Seen 1 /*0889*/ uint8 unknown889; // Seen 0 (POK) or 1 (rujj) /*0890*/ uint8 unknown890; // Seen 1 diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index 404190d09..8470b7296 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -1388,8 +1388,8 @@ namespace SoD /*fill in some unknowns with observed values, hopefully it will help */ eq->unknown800 = -1; eq->unknown844 = 600; - eq->unknown880 = 50; - eq->unknown884 = 10; + OUT(LavaDamage); + OUT(MinLavaDamage); eq->unknown888 = 1; eq->unknown889 = 0; eq->unknown890 = 1; diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 70866db8f..12bfacac7 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -450,8 +450,8 @@ struct NewZone_Struct { /*0868*/ uint32 underworld_teleport_index; // > 0 teleports w/ zone point index, invalid succors, -1 affects some collisions /*0872*/ uint32 scriptIDSomething3; /*0876*/ uint32 SuspendBuffs; -/*0880*/ uint32 unknown880; //seen 50 -/*0884*/ uint32 unknown884; //seen 10 +/*0880*/ uint32 LavaDamage; //seen 50 +/*0884*/ uint32 MinLavaDamage; //seen 10 /*0888*/ uint8 unknown888; //seen 1 /*0889*/ uint8 unknown889; //seen 0 (POK) or 1 (rujj) /*0890*/ uint8 unknown890; //seen 1 diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index 07758bba6..760a9008b 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -1066,8 +1066,8 @@ namespace SoF /*fill in some unknowns with observed values, hopefully it will help */ eq->unknown796 = -1; eq->unknown840 = 600; - eq->unknown876 = 50; - eq->unknown880 = 10; + OUT(LavaDamage); + OUT(MinLavaDamage); eq->unknown884 = 1; eq->unknown885 = 0; eq->unknown886 = 1; diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index 97b200a91..60542db38 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -454,8 +454,8 @@ struct NewZone_Struct { /*0864*/ uint32 underworld_teleport_index; // > 0 teleports w/ zone point index, invalid succors, -1 affects some collisions /*0868*/ uint32 scriptIDSomething3; /*0872*/ uint32 SuspendBuffs; -/*0876*/ uint32 unknown876; //seen 50 -/*0880*/ uint32 unknown880; //seen 10 +/*0876*/ uint32 LavaDamage; //seen 50 +/*0880*/ uint32 MinLavaDamage; //seen 10 /*0884*/ uint8 unknown884; //seen 1 /*0885*/ uint8 unknown885; //seen 0 (POK) or 1 (rujj) /*0886*/ uint8 unknown886; //seen 1 diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index c23415737..f1bf60e6a 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -1614,8 +1614,8 @@ namespace UF /*fill in some unknowns with observed values, hopefully it will help */ eq->unknown800 = -1; eq->unknown844 = 600; - eq->unknown880 = 50; - eq->unknown884 = 10; + OUT(LavaDamage); + OUT(MinLavaDamage); eq->unknown888 = 1; eq->unknown889 = 0; eq->unknown890 = 1; diff --git a/common/patches/uf_structs.h b/common/patches/uf_structs.h index 6c1b4a4ed..f3d7f0e2c 100644 --- a/common/patches/uf_structs.h +++ b/common/patches/uf_structs.h @@ -450,8 +450,8 @@ struct NewZone_Struct { /*0868*/ uint32 underworld_teleport_index; // > 0 teleports w/ zone point index, invalid succors, -1 affects some collisions /*0872*/ uint32 scriptIDSomething3; /*0876*/ uint32 SuspendBuffs; -/*0880*/ uint32 unknown880; //seen 50 -/*0884*/ uint32 unknown884; //seen 10 +/*0880*/ uint32 LavaDamage; //seen 50 +/*0884*/ uint32 MinLavaDamage; //seen 10 /*0888*/ uint8 unknown888; //seen 1 /*0889*/ uint8 unknown889; //seen 0 (POK) or 1 (rujj) /*0890*/ uint8 unknown890; //seen 1 diff --git a/common/repositories/base/base_zone_repository.h b/common/repositories/base/base_zone_repository.h index 04188ac93..3478c9fa1 100644 --- a/common/repositories/base/base_zone_repository.h +++ b/common/repositories/base/base_zone_repository.h @@ -110,6 +110,8 @@ public: std::string content_flags; std::string content_flags_disabled; int underworld_teleport_index; + int lava_damage; + int min_lava_damage; }; static std::string PrimaryKey() @@ -212,6 +214,8 @@ public: "content_flags", "content_flags_disabled", "underworld_teleport_index", + "lava_damage", + "min_lava_damage", }; } @@ -339,6 +343,8 @@ public: entry.content_flags = ""; entry.content_flags_disabled = ""; entry.underworld_teleport_index = 0; + entry.lava_damage = 50; + entry.min_lava_damage = 10; return entry; } @@ -466,6 +472,8 @@ public: entry.content_flags = row[89] ? row[89] : ""; entry.content_flags_disabled = row[90] ? row[90] : ""; entry.underworld_teleport_index = atoi(row[91]); + entry.lava_damage = atoi(row[92]); + entry.min_lava_damage = atoi(row[93]); return entry; } @@ -590,6 +598,8 @@ public: update_values.push_back(columns[89] + " = '" + EscapeString(zone_entry.content_flags) + "'"); update_values.push_back(columns[90] + " = '" + EscapeString(zone_entry.content_flags_disabled) + "'"); update_values.push_back(columns[91] + " = " + std::to_string(zone_entry.underworld_teleport_index)); + update_values.push_back(columns[92] + " = " + std::to_string(zone_entry.lava_damage)); + update_values.push_back(columns[93] + " = " + std::to_string(zone_entry.min_lava_damage)); auto results = db.QueryDatabase( fmt::format( @@ -703,6 +713,8 @@ public: insert_values.push_back("'" + EscapeString(zone_entry.content_flags) + "'"); insert_values.push_back("'" + EscapeString(zone_entry.content_flags_disabled) + "'"); insert_values.push_back(std::to_string(zone_entry.underworld_teleport_index)); + insert_values.push_back(std::to_string(zone_entry.lava_damage)); + insert_values.push_back(std::to_string(zone_entry.min_lava_damage)); auto results = db.QueryDatabase( fmt::format( @@ -824,6 +836,8 @@ public: insert_values.push_back("'" + EscapeString(zone_entry.content_flags) + "'"); insert_values.push_back("'" + EscapeString(zone_entry.content_flags_disabled) + "'"); insert_values.push_back(std::to_string(zone_entry.underworld_teleport_index)); + insert_values.push_back(std::to_string(zone_entry.lava_damage)); + insert_values.push_back(std::to_string(zone_entry.min_lava_damage)); insert_chunks.push_back("(" + implode(",", insert_values) + ")"); } @@ -949,6 +963,8 @@ public: entry.content_flags = row[89] ? row[89] : ""; entry.content_flags_disabled = row[90] ? row[90] : ""; entry.underworld_teleport_index = atoi(row[91]); + entry.lava_damage = atoi(row[92]); + entry.min_lava_damage = atoi(row[93]); all_entries.push_back(entry); } @@ -1065,6 +1081,8 @@ public: entry.content_flags = row[89] ? row[89] : ""; entry.content_flags_disabled = row[90] ? row[90] : ""; entry.underworld_teleport_index = atoi(row[91]); + entry.lava_damage = atoi(row[92]); + entry.min_lava_damage = atoi(row[93]); all_entries.push_back(entry); } diff --git a/common/version.h b/common/version.h index bd84935ae..1d5eec0f8 100644 --- a/common/version.h +++ b/common/version.h @@ -34,7 +34,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9172 +#define CURRENT_BINARY_DATABASE_VERSION 9173 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9028 diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index cddfb7717..076e6fdf4 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -426,6 +426,7 @@ 9170|2021_03_03_instance_safereturns.sql|SHOW TABLES LIKE 'character_instance_safereturns'|empty| 9171|2021_03_30_remove_dz_is_current_member.sql|SHOW COLUMNS FROM `dynamic_zone_members` LIKE 'is_current_member'|not_empty| 9172|2021_05_21_shared_tasks.sql|SHOW TABLES LIKE 'shared_tasks'|empty| +9173|2021_09_14_zone_lava_damage.sql|SHOW COLUMNS FROM `zone` LIKE 'lava_damage'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2021_09_14_zone_lava_damage.sql b/utils/sql/git/required/2021_09_14_zone_lava_damage.sql new file mode 100644 index 000000000..f6a5d00d1 --- /dev/null +++ b/utils/sql/git/required/2021_09_14_zone_lava_damage.sql @@ -0,0 +1 @@ +ALTER TABLE zone ADD lava_damage INT(11) NULL DEFAULT '50' AFTER underworld_teleport_index, ADD min_lava_damage INT(11) NOT NULL DEFAULT '10' AFTER lava_damage; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 7e195ab14..4b33dcfad 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -167,7 +167,9 @@ bool ZoneDatabase::GetZoneCFG( "fast_regen_endurance, " // 59 "npc_max_aggro_dist, " // 60 "max_movement_update_range, " // 61 - "underworld_teleport_index " // 62 + "underworld_teleport_index, " // 62 + "lava_damage, " // 63 + "min_lava_damage " // 64 "FROM zone WHERE zoneidnumber = %i AND version = %i %s", zoneid, instance_id, @@ -220,6 +222,8 @@ bool ZoneDatabase::GetZoneCFG( zone_data->FastRegenEndurance = atoi(row[59]); zone_data->NPCAggroMaxDist = atoi(row[60]); zone_data->underworld_teleport_index = atoi(row[62]); + zone_data->LavaDamage = atoi(row[63]); + zone_data->MinLavaDamage = atoi(row[64]); int bindable = 0; bindable = atoi(row[31]); From 558bebe71005f94abc819e0317d80f7f9c25d1f9 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 1 Oct 2021 15:50:26 -0400 Subject: [PATCH 226/624] updates --- common/spdat.h | 4 ++-- zone/mob.h | 1 + zone/spell_effects.cpp | 35 ++++++++++++++++++++++++++--------- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 55af388b5..9061e8725 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -839,7 +839,7 @@ typedef enum { #define SE_SuspendPet 151 // implemented, @Pet, allow caster to have an extra suspended pet, base: 0=no buffs/items 1=buffs+items, limit: none, max: none #define SE_TemporaryPets 152 // implemented #define SE_BalanceHP 153 // implemented -#define SE_DispelDetrimental 154 // implemented +#define SE_DispelDetrimental 154 // implemented, @Dispel only beneficial effects on a target, base: pct chance (950=95%), limit:none, max:none #define SE_SpellCritDmgIncrease 155 // implemented - no known live spells use this currently #define SE_IllusionCopy 156 // implemented - Deception #define SE_SpellDamageShield 157 // implemented - Petrad's Protection @@ -894,7 +894,7 @@ typedef enum { #define SE_AETaunt 206 // implemented #define SE_FleshToBone 207 // implemented //#define SE_PurgePoison 208 // not used -#define SE_DispelBeneficial 209 // implemented +#define SE_DispelBeneficial 209 // implemented, @Dispel only beneficial effects on a target, base: pct chance (950=95%), limit:none, max:none #define SE_PetShield 210 // implmented, @ShieldAbility, allows pet to 'shield' owner for 50 pct of damage taken for a duration, base: Time multiplier 1=12 seconds, 2=24 ect, limit: mitigation on pet owner override (not on live), max: mitigation on pet overide (not on live) #define SE_AEMelee 211 // implemented TO DO: Implement to allow NPC use (client only atm). #define SE_FrenziedDevastation 212 // implemented - increase spell criticals + all DD spells cast 2x mana. diff --git a/zone/mob.h b/zone/mob.h index 098d19dac..40967aa72 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -346,6 +346,7 @@ public: uint16 CastingSpellID() const { return casting_spell_id; } bool DoCastingChecks(); bool TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier); + bool TryDispelBeneficialOrDetrimental(uint8 caster_level, uint8 buff_level, int chance); bool TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed = 1.5f); void ResourceTap(int32 damage, uint16 spell_id); void TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index e7e054302..6abccbfa7 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -7057,15 +7057,6 @@ uint16 Mob::GetSpellEffectResistChance(uint16 spell_id) bool Mob::TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier){ - /*Live 5-20-14 Patch Note: Updated all spells which use Remove Detrimental and - Cancel Beneficial spell effects to use a new method. The chances for those spells to - affect their targets have not changed unless otherwise noted.*/ - - /*This should provide a somewhat accurate conversion between pre 5/14 base values and post. - until more information is avialble - Kayen*/ - if (level_modifier >= 100) - level_modifier = level_modifier/100; - //Dispels - Check level of caster agianst buffs level (level of the caster who cast the buff) //Effect value of dispels are treated as a level modifier. //Values for scaling were obtain from live parses, best estimates. @@ -7092,6 +7083,32 @@ bool Mob::TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier){ return false; } +bool Mob::TryDispelBeneficialOrDetrimental(uint8 caster_level, uint8 buff_level, int chance) { + + /* + Used in SPA 154 and SPA 209 to specifically dispel only beneficial or only detrimental spells. + Formula derived from live parsing. + Baseline chance is 'base' / 10, (ie. 950/10 = 95%) + Chance receives a penality of 0.5% per level, for each level the 'caster of the buff/debuff' is above the caster of the dispel. + There is no bonus percent chance for trying to dispel buff/debuffs cast by mobs lower level than you. + + Ie. Lv 69 Shaman Casts 'Pure Spirit' which has 95% chance to remove deterimental on a debuff cast by a level 85 raid mob. 85-69= 16, + then 16 x 0.5 = 8% penality, thus actual chance to remove is going to be 95-8 = 87% + */ + + int dispel_chance = chance; + int level_diff = caster_level - buff_level; + + if (level_diff < 0) { + dispel_chance += level_diff * 5; + } + + if (zone->random.Int(1,1000) <= dispel_chance) + return true; + else + return false; +} + bool Mob::ImprovedTaunt(){ From 91c451b6c588338357e86b85f003401b336dcca6 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Fri, 1 Oct 2021 18:42:02 -0500 Subject: [PATCH 227/624] Shared task kill update fix --- zone/task_client_state.cpp | 178 ++++++++++++++++++++----------------- 1 file changed, 98 insertions(+), 80 deletions(-) diff --git a/zone/task_client_state.cpp b/zone/task_client_state.cpp index e00960238..e3ed80637 100644 --- a/zone/task_client_state.cpp +++ b/zone/task_client_state.cpp @@ -2739,110 +2739,128 @@ void ClientTaskState::SyncSharedTaskZoneClientDoneCountState( void ClientTaskState::HandleUpdateTasksOnKill(Client *client, uint32 npc_type_id) { - if (!HasActiveTasks()) { - return; + + // get clients to update + std::vector clients_to_update = {}; + + // raid + Raid *raid = entity_list.GetRaidByClient(client); + if (raid) { + for (auto &e : raid->members) { + if (e.member && e.member->IsClient()) { + clients_to_update.push_back(e.member->CastToClient()); + } + } } - // loop over the union of tasks and quests - for (auto &active_task : m_active_tasks) { - auto current_task = &active_task; - if (current_task->task_id == TASKSLOTEMPTY) { + // group + if (!clients_to_update.empty()) { + Group *group = entity_list.GetGroupByClient(client); + if (group) { + for (auto &m : group->members) { + if (m && m->IsClient()) { + clients_to_update.push_back(m->CastToClient()); + } + } + } + } + + // solo + if (!clients_to_update.empty()) { + clients_to_update.push_back(client); + } + + for (auto &c: clients_to_update) { + if (c->ClientDataLoaded()) { continue; } - // Check if there are any active kill activities for this p_task_data - auto p_task_data = task_manager->m_task_data[current_task->task_id]; - if (p_task_data == nullptr) { - return; - } + LogTasksDetail("[HandleUpdateTasksOnKill] Looping through client [{}]", c->GetCleanName()); - for (int activity_id = 0; activity_id < p_task_data->activity_count; activity_id++) { - ClientActivityInformation *client_activity = ¤t_task->activity[activity_id]; - ActivityInformation *activity_info = &p_task_data->activity_information[activity_id]; - - // We are not interested in completed or hidden activities - if (client_activity->activity_state != ActivityActive) { + // loop over the union of tasks and quests + for (auto &active_task : c->GetTaskState()->m_active_tasks) { + auto current_task = &active_task; + if (current_task->task_id == TASKSLOTEMPTY) { continue; } - // We are only interested in Kill activities - if (activity_info->activity_type != TaskActivityType::Kill) { - continue; + // Check if there are any active kill activities for this p_task_data + auto p_task_data = task_manager->m_task_data[current_task->task_id]; + if (p_task_data == nullptr) { + return; } - // Is there a zone restriction on the activity_information ? - if (!activity_info->CheckZone(zone->GetZoneID())) { - LogTasks( - "[HandleUpdateTasksOnKill] character [{}] task_id [{}] activity_id [{}] activity_type [{}] for NPC [{}] failed zone check", - client->GetName(), - current_task->task_id, - activity_id, - static_cast(TaskActivityType::Kill), - npc_type_id - ); - continue; - } - // Is the activity_information to kill this type of NPC ? - switch (activity_info->goal_method) { - case METHODSINGLEID: - if (activity_info->goal_id != npc_type_id) { - LogTasksDetail("[HandleUpdateTasksOnKill] Matched single goal"); - continue; - } - break; + for (int activity_id = 0; activity_id < p_task_data->activity_count; activity_id++) { + ClientActivityInformation *client_activity = ¤t_task->activity[activity_id]; + ActivityInformation *activity_info = &p_task_data->activity_information[activity_id]; - case METHODLIST: - if (!task_manager->m_goal_list_manager.IsInList( - activity_info->goal_id, - (int) npc_type_id - )) { - LogTasksDetail("[HandleUpdateTasksOnKill] Matched list goal"); - continue; - } - break; - - default: - // If METHODQUEST, don't updated the activity_information here + // We are not interested in completed or hidden activities + if (client_activity->activity_state != ActivityActive) { continue; - } + } - LogTasksDetail("[HandleUpdateTasksOnKill] passed checks"); + // We are only interested in Kill activities + if (activity_info->activity_type != TaskActivityType::Kill) { + continue; + } - // handle actual update - // legacy eqemu task update logic loops through group on kill of npc to update a single task - if (p_task_data->type != TaskType::Shared) { - LogTasksDetail("[HandleUpdateTasksOnKill] Non-Shared Update"); - - Raid *raid = entity_list.GetRaidByClient(client); - if (raid) { - for (auto &e : raid->members) { - if (e.member && e.member->IsClient()) { - Client *c = e.member->CastToClient(); - c->UpdateTasksOnKill(npc_type_id); + // Is there a zone restriction on the activity_information ? + if (!activity_info->CheckZone(zone->GetZoneID())) { + LogTasks( + "[HandleUpdateTasksOnKill] character [{}] task_id [{}] activity_id [{}] activity_type [{}] for NPC [{}] failed zone check", + client->GetName(), + current_task->task_id, + activity_id, + static_cast(TaskActivityType::Kill), + npc_type_id + ); + continue; + } + // Is the activity_information to kill this type of NPC ? + switch (activity_info->goal_method) { + case METHODSINGLEID: + if (activity_info->goal_id != npc_type_id) { + LogTasksDetail("[HandleUpdateTasksOnKill] Matched single goal"); + continue; } - } + break; + + case METHODLIST: + if (!task_manager->m_goal_list_manager.IsInList( + activity_info->goal_id, + (int) npc_type_id + )) { + LogTasksDetail("[HandleUpdateTasksOnKill] Matched list goal"); + continue; + } + break; + + default: + // If METHODQUEST, don't updated the activity_information here + continue; + } + + LogTasksDetail("[HandleUpdateTasksOnKill] passed checks"); + + // handle actual update + // legacy eqemu task update logic loops through group on kill of npc to update a single task + if (p_task_data->type != TaskType::Shared) { + LogTasksDetail("[HandleUpdateTasksOnKill] Non-Shared Update"); + IncrementDoneCount(c, p_task_data, current_task->slot, activity_id); return; } - Group *group = entity_list.GetGroupByClient(client); - if (group) { - for (auto &m : group->members) { - if (m && m->IsClient()) { - Client *c = m->CastToClient(); - c->UpdateTasksOnKill(npc_type_id); - } - } - return; + LogTasksDetail("[HandleUpdateTasksOnKill] Shared update"); + + // shared tasks only require one client to receive an update to propagate + if (c == client) { + IncrementDoneCount(c, p_task_data, current_task->slot, activity_id); } } - - LogTasksDetail("[HandleUpdateTasksOnKill] Shared update"); - - // shared tasks only require one client to receive an update to propagate - client->UpdateTasksOnKill(npc_type_id); } } } + bool ClientTaskState::HasActiveTasks() { if (!task_manager) { From 859751f74d671ecaa36d777450803ece85a72bf4 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Fri, 1 Oct 2021 18:42:36 -0500 Subject: [PATCH 228/624] Revert "Shared task kill update fix" This reverts commit 91c451b6c588338357e86b85f003401b336dcca6. --- zone/task_client_state.cpp | 182 +++++++++++++++++-------------------- 1 file changed, 82 insertions(+), 100 deletions(-) diff --git a/zone/task_client_state.cpp b/zone/task_client_state.cpp index e3ed80637..e00960238 100644 --- a/zone/task_client_state.cpp +++ b/zone/task_client_state.cpp @@ -2739,128 +2739,110 @@ void ClientTaskState::SyncSharedTaskZoneClientDoneCountState( void ClientTaskState::HandleUpdateTasksOnKill(Client *client, uint32 npc_type_id) { - - // get clients to update - std::vector clients_to_update = {}; - - // raid - Raid *raid = entity_list.GetRaidByClient(client); - if (raid) { - for (auto &e : raid->members) { - if (e.member && e.member->IsClient()) { - clients_to_update.push_back(e.member->CastToClient()); - } - } + if (!HasActiveTasks()) { + return; } - // group - if (!clients_to_update.empty()) { - Group *group = entity_list.GetGroupByClient(client); - if (group) { - for (auto &m : group->members) { - if (m && m->IsClient()) { - clients_to_update.push_back(m->CastToClient()); - } - } - } - } - - // solo - if (!clients_to_update.empty()) { - clients_to_update.push_back(client); - } - - for (auto &c: clients_to_update) { - if (c->ClientDataLoaded()) { + // loop over the union of tasks and quests + for (auto &active_task : m_active_tasks) { + auto current_task = &active_task; + if (current_task->task_id == TASKSLOTEMPTY) { continue; } - LogTasksDetail("[HandleUpdateTasksOnKill] Looping through client [{}]", c->GetCleanName()); + // Check if there are any active kill activities for this p_task_data + auto p_task_data = task_manager->m_task_data[current_task->task_id]; + if (p_task_data == nullptr) { + return; + } - // loop over the union of tasks and quests - for (auto &active_task : c->GetTaskState()->m_active_tasks) { - auto current_task = &active_task; - if (current_task->task_id == TASKSLOTEMPTY) { + for (int activity_id = 0; activity_id < p_task_data->activity_count; activity_id++) { + ClientActivityInformation *client_activity = ¤t_task->activity[activity_id]; + ActivityInformation *activity_info = &p_task_data->activity_information[activity_id]; + + // We are not interested in completed or hidden activities + if (client_activity->activity_state != ActivityActive) { continue; } - // Check if there are any active kill activities for this p_task_data - auto p_task_data = task_manager->m_task_data[current_task->task_id]; - if (p_task_data == nullptr) { - return; + // We are only interested in Kill activities + if (activity_info->activity_type != TaskActivityType::Kill) { + continue; } - for (int activity_id = 0; activity_id < p_task_data->activity_count; activity_id++) { - ClientActivityInformation *client_activity = ¤t_task->activity[activity_id]; - ActivityInformation *activity_info = &p_task_data->activity_information[activity_id]; - - // We are not interested in completed or hidden activities - if (client_activity->activity_state != ActivityActive) { - continue; - } - - // We are only interested in Kill activities - if (activity_info->activity_type != TaskActivityType::Kill) { - continue; - } - - // Is there a zone restriction on the activity_information ? - if (!activity_info->CheckZone(zone->GetZoneID())) { - LogTasks( - "[HandleUpdateTasksOnKill] character [{}] task_id [{}] activity_id [{}] activity_type [{}] for NPC [{}] failed zone check", - client->GetName(), - current_task->task_id, - activity_id, - static_cast(TaskActivityType::Kill), - npc_type_id - ); - continue; - } - // Is the activity_information to kill this type of NPC ? - switch (activity_info->goal_method) { - case METHODSINGLEID: - if (activity_info->goal_id != npc_type_id) { - LogTasksDetail("[HandleUpdateTasksOnKill] Matched single goal"); - continue; - } - break; - - case METHODLIST: - if (!task_manager->m_goal_list_manager.IsInList( - activity_info->goal_id, - (int) npc_type_id - )) { - LogTasksDetail("[HandleUpdateTasksOnKill] Matched list goal"); - continue; - } - break; - - default: - // If METHODQUEST, don't updated the activity_information here + // Is there a zone restriction on the activity_information ? + if (!activity_info->CheckZone(zone->GetZoneID())) { + LogTasks( + "[HandleUpdateTasksOnKill] character [{}] task_id [{}] activity_id [{}] activity_type [{}] for NPC [{}] failed zone check", + client->GetName(), + current_task->task_id, + activity_id, + static_cast(TaskActivityType::Kill), + npc_type_id + ); + continue; + } + // Is the activity_information to kill this type of NPC ? + switch (activity_info->goal_method) { + case METHODSINGLEID: + if (activity_info->goal_id != npc_type_id) { + LogTasksDetail("[HandleUpdateTasksOnKill] Matched single goal"); continue; - } + } + break; - LogTasksDetail("[HandleUpdateTasksOnKill] passed checks"); + case METHODLIST: + if (!task_manager->m_goal_list_manager.IsInList( + activity_info->goal_id, + (int) npc_type_id + )) { + LogTasksDetail("[HandleUpdateTasksOnKill] Matched list goal"); + continue; + } + break; - // handle actual update - // legacy eqemu task update logic loops through group on kill of npc to update a single task - if (p_task_data->type != TaskType::Shared) { - LogTasksDetail("[HandleUpdateTasksOnKill] Non-Shared Update"); - IncrementDoneCount(c, p_task_data, current_task->slot, activity_id); + default: + // If METHODQUEST, don't updated the activity_information here + continue; + } + + LogTasksDetail("[HandleUpdateTasksOnKill] passed checks"); + + // handle actual update + // legacy eqemu task update logic loops through group on kill of npc to update a single task + if (p_task_data->type != TaskType::Shared) { + LogTasksDetail("[HandleUpdateTasksOnKill] Non-Shared Update"); + + Raid *raid = entity_list.GetRaidByClient(client); + if (raid) { + for (auto &e : raid->members) { + if (e.member && e.member->IsClient()) { + Client *c = e.member->CastToClient(); + c->UpdateTasksOnKill(npc_type_id); + } + } return; } - LogTasksDetail("[HandleUpdateTasksOnKill] Shared update"); - - // shared tasks only require one client to receive an update to propagate - if (c == client) { - IncrementDoneCount(c, p_task_data, current_task->slot, activity_id); + Group *group = entity_list.GetGroupByClient(client); + if (group) { + for (auto &m : group->members) { + if (m && m->IsClient()) { + Client *c = m->CastToClient(); + c->UpdateTasksOnKill(npc_type_id); + } + } + return; } } + + LogTasksDetail("[HandleUpdateTasksOnKill] Shared update"); + + // shared tasks only require one client to receive an update to propagate + client->UpdateTasksOnKill(npc_type_id); } } } - bool ClientTaskState::HasActiveTasks() { if (!task_manager) { From b70dc64d96006c809b5f62c1c1acfc86a8cd70ef Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 1 Oct 2021 20:36:54 -0400 Subject: [PATCH 229/624] Update spell_effects.cpp --- zone/spell_effects.cpp | 35 ++++------------------------------- 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 6abccbfa7..61ff24b0b 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1077,14 +1077,14 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove caster->MessageString(Chat::SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name); break; } - + int chance = spells[spell_id].base[i] - 20; //Baseline 2% negative modifer derived from parsing. int buff_count = GetMaxTotalSlots(); for(int slot = 0; slot < buff_count; slot++) { if (buffs[slot].spellid != SPELL_UNKNOWN && IsDetrimentalSpell(buffs[slot].spellid) && spells[buffs[slot].spellid].dispel_flag == 0) { - if (caster && TryDispel(caster->GetLevel(),buffs[slot].casterlevel, effect_value)){ + if (zone->random.Int(1, 1000) <= chance){ BuffFadeBySlot(slot); slot = buff_count; } @@ -1103,14 +1103,14 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove caster->MessageString(Chat::SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name); break; } - + int chance = spells[spell_id].base[i] - 20; //Baseline 2% negative modifer derived from parsing. int buff_count = GetMaxTotalSlots(); for(int slot = 0; slot < buff_count; slot++) { if (buffs[slot].spellid != SPELL_UNKNOWN && IsBeneficialSpell(buffs[slot].spellid) && spells[buffs[slot].spellid].dispel_flag == 0) { - if (caster && TryDispel(caster->GetLevel(),buffs[slot].casterlevel, effect_value)){ + if (zone->random.Int(1, 1000) <= chance) { BuffFadeBySlot(slot); slot = buff_count; } @@ -7083,33 +7083,6 @@ bool Mob::TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier){ return false; } -bool Mob::TryDispelBeneficialOrDetrimental(uint8 caster_level, uint8 buff_level, int chance) { - - /* - Used in SPA 154 and SPA 209 to specifically dispel only beneficial or only detrimental spells. - Formula derived from live parsing. - Baseline chance is 'base' / 10, (ie. 950/10 = 95%) - Chance receives a penality of 0.5% per level, for each level the 'caster of the buff/debuff' is above the caster of the dispel. - There is no bonus percent chance for trying to dispel buff/debuffs cast by mobs lower level than you. - - Ie. Lv 69 Shaman Casts 'Pure Spirit' which has 95% chance to remove deterimental on a debuff cast by a level 85 raid mob. 85-69= 16, - then 16 x 0.5 = 8% penality, thus actual chance to remove is going to be 95-8 = 87% - */ - - int dispel_chance = chance; - int level_diff = caster_level - buff_level; - - if (level_diff < 0) { - dispel_chance += level_diff * 5; - } - - if (zone->random.Int(1,1000) <= dispel_chance) - return true; - else - return false; -} - - bool Mob::ImprovedTaunt(){ if (spellbonuses.ImprovedTaunt[SBIndex::IMPROVED_TAUNT_MAX_LVL]){ From 0762ffa3dcd6591b51d22051b6fe36668ff37c0f Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 1 Oct 2021 21:19:26 -0400 Subject: [PATCH 230/624] [Quest API] Typo in Perl $entity_lsit->IsMobSpawnedByNpcTypeID(). (#1576) This causes the wrong name to show up on Spire. --- zone/perl_entity.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/perl_entity.cpp b/zone/perl_entity.cpp index ec1e855cd..d45190d32 100644 --- a/zone/perl_entity.cpp +++ b/zone/perl_entity.cpp @@ -128,7 +128,7 @@ XS(XS_EntityList_IsMobSpawnedByNpcTypeID); /* prototype pass -Wmissing-prototype XS(XS_EntityList_IsMobSpawnedByNpcTypeID) { dXSARGS; if (items != 2) - Perl_croak(aTHX_ "Usage: EntityList::ValidMobByNpcTypeID(THIS, get_id)"); // @categories Script Utility + Perl_croak(aTHX_ "Usage: EntityList::IsMobSpawnedByNpcTypeID(THIS, get_id)"); // @categories Script Utility { EntityList *THIS; bool RETVAL; From 3883adcefc4ac2cc7562fa3dcfe59532442e60e7 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Fri, 1 Oct 2021 22:09:21 -0500 Subject: [PATCH 231/624] [Dialogue Window / Saylinks] Missing Changes (#1574) * Implement auto saylink injection * Cover Lua say since it takes a different code path * [Dialogue] Dialogue Window Middleware (#1526) * Dialogue window quest dialogue work * Add rest of DialogueWindow hooks * Remove spacing --- common/ruletypes.h | 2 ++ zone/client_packet.cpp | 2 +- zone/dialogue_window.cpp | 53 +++++++++++++++++++++++++++++++++++++--- zone/entity.cpp | 22 +++++++++++++++-- zone/lua_mob.cpp | 7 +++++- zone/mob.cpp | 26 +++++++++++++++++--- zone/perl_mob.cpp | 8 ++++-- 7 files changed, 107 insertions(+), 13 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 533567b0e..7f463e733 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -601,6 +601,8 @@ RULE_INT(Chat, KarmaGlobalChatLimit, 72, "Amount of karma you need to be able to RULE_INT(Chat, GlobalChatLevelLimit, 8, "Level limit you need to of reached to talk in ooc/auction/chat if your karma is too low") RULE_BOOL(Chat, AutoInjectSaylinksToSay, true, "Automatically injects saylinks into dialogue that has [brackets in them]") RULE_BOOL(Chat, AutoInjectSaylinksToClientMessage, true, "Automatically injects saylinks into dialogue that has [brackets in them]") +RULE_BOOL(Chat, QuestDialogueUsesDialogueWindow, false, "Pipes all quest dialogue to dialogue window") +RULE_BOOL(Chat, DialogueWindowAnimatesNPCsIfNoneSet, true, "If there is no animation specified in the dialogue window markdown then it will choose a random greet animation such as wave or salute") RULE_CATEGORY_END() RULE_CATEGORY(Merchant) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index a74f65180..513cd5942 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -5001,7 +5001,7 @@ void Client::Handle_OP_ConsiderCorpse(const EQApplicationPacket *app) if (parse->EventPlayer(EVENT_CONSIDER_CORPSE, this, fmt::format("{}", conin->targetid), 0) == 1) { return; } - + uint32 day, hour, min, sec, ttime; if ((ttime = tcorpse->GetDecayTime()) != 0) { sec = (ttime / 1000) % 60; // Total seconds diff --git a/zone/dialogue_window.cpp b/zone/dialogue_window.cpp index 941bcbc82..bae0d8533 100644 --- a/zone/dialogue_window.cpp +++ b/zone/dialogue_window.cpp @@ -4,6 +4,10 @@ void DialogueWindow::Render(Client *c, std::string markdown) { std::string output = markdown; + if (!c->ClientDataLoaded()) { + return; + } + // this is the NPC that the client is interacting with if there is dialogue going on Mob *target; if (c->GetTarget()) { @@ -81,6 +85,21 @@ void DialogueWindow::Render(Client *c, std::string markdown) } } + if (animation.empty() && RuleB(Chat, DialogueWindowAnimatesNPCsIfNoneSet)) { + std::vector greet_animations = { + 29, // wave + 48, // nodyes + 64, // point + 67, // salute + 69, // tapfoot + 70, // bowto + }; + + int random_animation = rand() % (greet_animations.size() - 1) + 0; + + target->DoAnim(greet_animations[random_animation]); + } + // window expire time std::string expire_time = get_between(output, "=", "="); uint32 window_expire_seconds = 0; @@ -234,7 +253,7 @@ void DialogueWindow::Render(Client *c, std::string markdown) button_one_name = button_one.c_str(); } } - + LogDiaWind("Client [{}] Rendering button_two option.", c->GetCleanName()); auto two_first_split = split_string(output, "button_two:"); @@ -266,6 +285,33 @@ void DialogueWindow::Render(Client *c, std::string markdown) std::vector responses; std::vector bracket_responses; if (markdown.find('[') != std::string::npos && markdown.find(']') != std::string::npos) { + + // record any saylinks that may be in saylink form + std::string strip_saylinks = output; + std::map replacements = {}; + while (strip_saylinks.find('[') != std::string::npos && strip_saylinks.find(']') != std::string::npos) { + std::string bracket_message = get_between(strip_saylinks, "[", "]"); + + // strip saylinks and normalize to [regular message] + size_t link_open = bracket_message.find('\x12'); + size_t link_close = bracket_message.find_last_of('\x12'); + if (link_open != link_close && (bracket_message.length() - link_open) > EQ::constants::SAY_LINK_BODY_SIZE) { + replacements.insert( + std::pair( + bracket_message, + bracket_message.substr(EQ::constants::SAY_LINK_BODY_SIZE + 1) + ) + ); + } + + find_replace(strip_saylinks, fmt::format("[{}]", bracket_message), ""); + } + + // write replacement strips + for (auto &replacement: replacements) { + find_replace(output, replacement.first, replacement.second.substr(0, replacement.second.size() - 1)); + } + // copy std::string content = output; @@ -412,8 +458,8 @@ void DialogueWindow::Render(Client *c, std::string markdown) color_tag ); - std::string html_tag; - for (const auto& color : html_colors) { + std::string html_tag; + for (const auto &color : html_colors) { if (color_tag.find(color.first) != std::string::npos) { // build html tag html_tag = fmt::format("", color.second); @@ -426,7 +472,6 @@ void DialogueWindow::Render(Client *c, std::string markdown) } } - // build the final output string std::string final_output; final_output = fmt::format("{}{}{}

{}", quote_string, output, quote_string, click_response); diff --git a/zone/entity.cpp b/zone/entity.cpp index 12898f0ea..47e03afba 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -43,6 +43,7 @@ #include "water_map.h" #include "npc_scale_manager.h" #include "../common/say_link.h" +#include "dialogue_window.h" #ifdef _WINDOWS #define snprintf _snprintf @@ -4202,8 +4203,25 @@ void EntityList::QuestJournalledSayClose( buf.WriteInt32(0); buf.WriteInt32(0); - // auto inject saylinks (say) - if (RuleB(Chat, AutoInjectSaylinksToSay)) { + if (RuleB(Chat, QuestDialogueUsesDialogueWindow)) { + for (auto &e : GetCloseMobList(sender, (dist * dist))) { + Mob *mob = e.second; + + if (!mob->IsClient()) { + continue; + } + + Client *client = mob->CastToClient(); + + if (client->GetTarget() && client->GetTarget()->IsMob() && client->GetTarget()->CastToMob() == sender) { + std::string window_markdown = message; + DialogueWindow::Render(client, window_markdown); + } + } + + return; + } + else if (RuleB(Chat, AutoInjectSaylinksToSay)) { std::string new_message = EQ::SayLinkEngine::InjectSaylinksIfNotExist(message); buf.WriteString(new_message); } diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index b37841e73..72f10be76 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -11,6 +11,7 @@ #include "lua_hate_list.h" #include "lua_client.h" #include "lua_stat_bonuses.h" +#include "dialogue_window.h" struct SpecialAbilities { }; @@ -757,7 +758,11 @@ void Lua_Mob::Message(int type, const char *message) { Lua_Safe_Call_Void(); // auto inject saylinks - if (RuleB(Chat, AutoInjectSaylinksToClientMessage)) { + if (RuleB(Chat, QuestDialogueUsesDialogueWindow) && self->IsClient()) { + std::string window_markdown = message; + DialogueWindow::Render(self->CastToClient(), window_markdown); + } + else if (RuleB(Chat, AutoInjectSaylinksToClientMessage)) { std::string new_message = EQ::SayLinkEngine::InjectSaylinksIfNotExist(message); self->Message(type, new_message.c_str()); } diff --git a/zone/mob.cpp b/zone/mob.cpp index 96591b9e1..240496ef4 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -26,6 +26,7 @@ #include "worldserver.h" #include "mob_movement_manager.h" #include "water_map.h" +#include "dialogue_window.h" #include #include @@ -2974,16 +2975,35 @@ void Mob::Say(const char *format, ...) talker = this; } - if (RuleB(Chat, AutoInjectSaylinksToSay)) { + int16 distance = 200; + + if (RuleB(Chat, QuestDialogueUsesDialogueWindow)) { + for (auto &e : entity_list.GetCloseMobList(talker, (distance * distance))) { + Mob *mob = e.second; + + if (!mob->IsClient()) { + continue; + } + + Client *client = mob->CastToClient(); + if (client->GetTarget() && client->GetTarget()->IsMob() && client->GetTarget()->CastToMob() == talker) { + std::string window_markdown = buf; + DialogueWindow::Render(client, window_markdown); + } + } + + return; + } + else if (RuleB(Chat, AutoInjectSaylinksToSay)) { std::string new_message = EQ::SayLinkEngine::InjectSaylinksIfNotExist(buf); entity_list.MessageCloseString( - talker, false, 200, 10, + talker, false, distance, Chat::NPCQuestSay, GENERIC_SAY, GetCleanName(), new_message.c_str() ); } else { entity_list.MessageCloseString( - talker, false, 200, 10, + talker, false, distance, Chat::NPCQuestSay, GENERIC_SAY, GetCleanName(), buf ); } diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index 6f48c4cd8..aa7a2eaab 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -41,6 +41,7 @@ typedef const char Const_char; #include "mob.h" #include "client.h" #include "../common/spdat.h" +#include "dialogue_window.h" #ifdef BOTS #include "bot.h" @@ -2621,8 +2622,11 @@ XS(XS_Mob_Message) { char *message = (char *) SvPV_nolen(ST(2)); VALIDATE_THIS_IS_MOB; - // auto inject saylinks - if (RuleB(Chat, AutoInjectSaylinksToClientMessage)) { + if (RuleB(Chat, QuestDialogueUsesDialogueWindow) && THIS->IsClient()) { + std::string window_markdown = message; + DialogueWindow::Render(THIS->CastToClient(), window_markdown); + } + else if (RuleB(Chat, AutoInjectSaylinksToClientMessage)) { std::string new_message = EQ::SayLinkEngine::InjectSaylinksIfNotExist(message); THIS->Message(type, new_message.c_str()); } From 00a22ca12ec9bd3d946f26e156fa782f43d705e2 Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Fri, 1 Oct 2021 23:09:40 -0400 Subject: [PATCH 232/624] [Repositories] Use repositories to load doors (#1572) Remove Door struct that was being used to map db columns --- zone/command.cpp | 43 +++++++------- zone/doors.cpp | 151 ++++++++++------------------------------------- zone/doors.h | 3 +- zone/zone.cpp | 51 +++++++--------- zone/zonedb.h | 3 +- zone/zonedump.h | 31 ---------- 6 files changed, 80 insertions(+), 202 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index ab18d63ac..884d43330 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -11708,8 +11708,6 @@ void command_object(Client *c, const Seperator *sep) Object *o = nullptr; Object_Struct od; - Door door; - Doors *doors; Door_Struct *ds; uint32 id = 0; uint32 itemid = 0; @@ -12531,32 +12529,34 @@ void command_object(Client *c, const Seperator *sep) entity_list.RemoveObject(o->GetID()); - memset(&door, 0, sizeof(door)); + auto door = DoorsRepository::NewEntity(); - strn0cpy(door.zone_name, zone->GetShortName(), sizeof(door.zone_name)); + door.zone = zone->GetShortName(); - door.db_id = 1000000000 + id; // Out of range of normal use for doors.id - door.door_id = -1; // Client doesn't care if these are all the same door_id - door.pos_x = od.x; // xpos - door.pos_y = od.y; // ypos - door.pos_z = od.z; // zpos - door.heading = od.heading; // heading + door.id = 1000000000 + id; // Out of range of normal use for doors.id + door.doorid = -1; // Client doesn't care if these are all the same door_id + door.pos_x = od.x; + door.pos_y = od.y; + door.pos_z = od.z; + door.heading = od.heading; - strn0cpy(door.door_name, od.object_name, sizeof(door.door_name)); // objectname + door.name = od.object_name; // Strip trailing "_ACTORDEF" if present. Client won't accept it for doors. - uint32 len = strlen(door.door_name); - if ((len > 9) && (memcmp(&door.door_name[len - 9], "_ACTORDEF", 10) == 0)) - door.door_name[len - 9] = '\0'; + int pos = door.name.size() - strlen("_ACTORDEF"); + if (pos > 0 && door.name.compare(pos, std::string::npos, "_ACTORDEF") == 0) + { + door.name.erase(pos); + } - memcpy(door.dest_zone, "NONE", 5); + door.dest_zone = "NONE"; if ((door.size = od.size) == 0) // unknown08 = optional size percentage door.size = 100; - switch ( - door.opentype = - od.solidtype) // unknown10 = optional request_nonsolid (0 or 1 or experimental number) + door.opentype = od.solidtype; + + switch (door.opentype) // unknown10 = optional request_nonsolid (0 or 1 or experimental number) { case 0: door.opentype = 31; @@ -12570,21 +12570,22 @@ void command_object(Client *c, const Seperator *sep) door.incline = od.unknown020; // unknown20 = optional incline value door.client_version_mask = 0xFFFFFFFF; - doors = new Doors(&door); + Doors* doors = new Doors(door); + entity_list.AddDoor(doors); app = new EQApplicationPacket(OP_SpawnDoor, sizeof(Door_Struct)); ds = (Door_Struct *)app->pBuffer; memset(ds, 0, sizeof(Door_Struct)); - memcpy(ds->name, door.door_name, 32); + memcpy(ds->name, door.name.c_str(), 32); ds->xPos = door.pos_x; ds->yPos = door.pos_y; ds->zPos = door.pos_z; ds->heading = door.heading; ds->incline = door.incline; ds->size = door.size; - ds->doorId = door.door_id; + ds->doorId = door.doorid; ds->opentype = door.opentype; ds->unknown0052[9] = 1; // *ptr-1 and *ptr-3 from EntityList::MakeDoorSpawnPacket() ds->unknown0052[11] = 1; diff --git a/zone/doors.cpp b/zone/doors.cpp index 2458e7f69..83f8dcb8a 100644 --- a/zone/doors.cpp +++ b/zone/doors.cpp @@ -42,38 +42,38 @@ extern EntityList entity_list; extern WorldServer worldserver; -Doors::Doors(const Door *door) : +Doors::Doors(const DoorsRepository::Doors& door) : close_timer(5000), - m_Position(door->pos_x, door->pos_y, door->pos_z, door->heading), - m_Destination(door->dest_x, door->dest_y, door->dest_z, door->dest_heading) { + m_Position(door.pos_x, door.pos_y, door.pos_z, door.heading), + m_Destination(door.dest_x, door.dest_y, door.dest_z, door.dest_heading) +{ + strn0cpy(zone_name, door.zone.c_str(), sizeof(zone_name)); + strn0cpy(door_name, door.name.c_str(), sizeof(door_name)); + strn0cpy(destination_zone_name, door.dest_zone.c_str(), sizeof(destination_zone_name)); - strn0cpy(zone_name, door->zone_name, 32); - strn0cpy(door_name, door->door_name, 32); - strn0cpy(destination_zone_name, door->dest_zone, 16); - - this->database_id = door->db_id; - this->door_id = door->door_id; - this->incline = door->incline; - this->open_type = door->opentype; - this->guild_id = door->guild_id; - this->lockpick = door->lock_pick; - this->key_item_id = door->keyitem; - this->no_key_ring = door->nokeyring; - this->trigger_door = door->trigger_door; - this->trigger_type = door->trigger_type; - this->triggered = false; - this->door_param = door->door_param; - this->size = door->size; - this->invert_state = door->invert_state; - this->destination_instance_id = door->dest_instance_id; - this->is_ldon_door = door->is_ldon_door; - this->client_version_mask = door->client_version_mask; + database_id = door.id; + door_id = door.doorid; + incline = door.incline; + open_type = door.opentype; + guild_id = door.guild; + lockpick = door.lockpick; + key_item_id = door.keyitem; + no_key_ring = door.nokeyring; + trigger_door = door.triggerdoor; + trigger_type = door.triggertype; + triggered = false; + door_param = door.door_param; + size = door.size; + invert_state = door.invert_state; + destination_instance_id = door.dest_instance; + is_ldon_door = door.is_ldon_door; + client_version_mask = door.client_version_mask; SetOpenState(false); close_timer.Disable(); - disable_timer = (door->disable_timer == 1 ? true : false); + disable_timer = (door.disable_timer == 1 ? true : false); } Doors::Doors(const char *model, const glm::vec4 &position, uint8 open_type, uint16 size) : @@ -682,106 +682,19 @@ int32 ZoneDatabase::GetDoorsDBCountPlusOne(const char *zone_name, int16 version) return atoi(row[0]) + 1; } -bool ZoneDatabase::LoadDoors(int32 door_count, Door *into, const char *zone_name, int16 version) { +std::vector ZoneDatabase::LoadDoors(const std::string& zone_name, int16 version) +{ LogInfo("Loading Doors from database"); - std::string query = StringFormat( - " SELECT " - " id, " - " doorid, " - " zone, " - " NAME, " - " pos_x, " - " pos_y, " - " pos_z, " - " heading, " - " opentype, " - " guild, " - " lockpick, " - " keyitem, " - " nokeyring, " - " triggerdoor, " - " triggertype, " - " dest_zone, " - " dest_instance, " - " dest_x, " - " dest_y, " - " dest_z, " - " dest_heading, " - " door_param, " - " invert_state, " - " incline, " - " size, " - " is_ldon_door, " - " client_version_mask, " - " disable_timer " - " FROM " - " doors " - " WHERE " - " zone = '%s' " - " AND ( version = % u OR version = - 1 ) " - " %s " - " ORDER BY " - " doorid ASC ", - zone_name, - version, - ContentFilterCriteria::apply().c_str() - ); - auto results = QueryDatabase(query); - if (!results.Success()) { - return false; - } + auto door_entries = DoorsRepository::GetWhere(*this, fmt::format( + "zone = '{}' AND (version = {} OR version = -1) {} ORDER BY doorid ASC", + zone_name, version, ContentFilterCriteria::apply())); - int32 row_index = 0; - for (auto row = results.begin(); row != results.end(); ++row, ++row_index) { - if (row_index >= door_count) { - std::cerr << "Error, Door Count of " << door_count << " exceeded." << std::endl; - break; - } + LogDoors("Loaded [{}] doors for [{}] version [{}]", door_entries.size(), zone_name, version); - memset(&into[row_index], 0, sizeof(Door)); - - strn0cpy(into[row_index].zone_name, row[2], 32); - strn0cpy(into[row_index].door_name, row[3], 32); - strn0cpy(into[row_index].dest_zone, row[15], 32); - - into[row_index].db_id = static_cast(atoi(row[0])); - into[row_index].door_id = static_cast(atoi(row[1])); - into[row_index].pos_x = (float) atof(row[4]); - into[row_index].pos_y = (float) atof(row[5]); - into[row_index].pos_z = (float) atof(row[6]); - into[row_index].heading = (float) atof(row[7]); - into[row_index].opentype = static_cast(atoi(row[8])); - into[row_index].guild_id = static_cast(atoi(row[9])); - into[row_index].lock_pick = static_cast(atoi(row[10])); - into[row_index].keyitem = static_cast(atoi(row[11])); - into[row_index].nokeyring = static_cast(atoi(row[12])); - into[row_index].trigger_door = static_cast(atoi(row[13])); - into[row_index].trigger_type = static_cast(atoi(row[14])); - into[row_index].dest_instance_id = static_cast(atoi(row[16])); - into[row_index].dest_x = (float) atof(row[17]); - into[row_index].dest_y = (float) atof(row[18]); - into[row_index].dest_z = (float) atof(row[19]); - into[row_index].dest_heading = (float) atof(row[20]); - into[row_index].door_param = static_cast(atoi(row[21])); - into[row_index].invert_state = atoi(row[22]); - into[row_index].incline = atoi(row[23]); - into[row_index].size = static_cast(atoi(row[24])); - into[row_index].is_ldon_door = static_cast(atoi(row[25])); - into[row_index].client_version_mask = (uint32) strtoul(row[26], nullptr, 10); - into[row_index].disable_timer = static_cast(atoi(row[27])); - - Log(Logs::Detail, Logs::Doors, "Door Load: db id: %u, door_id %u disable_timer: %i", - into[row_index].db_id, - into[row_index].door_id, - into[row_index].disable_timer - ); - } - - return true; + return door_entries; } - void Doors::SetLocation(float x, float y, float z) { entity_list.DespawnAllDoors(); diff --git a/zone/doors.h b/zone/doors.h index 455dfd03f..009041805 100644 --- a/zone/doors.h +++ b/zone/doors.h @@ -1,6 +1,7 @@ #ifndef DOORS_H #define DOORS_H +#include "../common/repositories/doors_repository.h" #include "../common/emu_opcodes.h" #include "../common/eq_packet_structs.h" #include "../common/linked_list.h" @@ -19,7 +20,7 @@ public: ~Doors(); Doors(const char *model, const glm::vec4& position, uint8 open_type = 58, uint16 size = 100); - Doors(const Door* door); + Doors(const DoorsRepository::Doors& door); bool GetDisableTimer() { return disable_timer; } bool IsDoor() const { return true; } diff --git a/zone/zone.cpp b/zone/zone.cpp index e5b810a1f..a5be7d140 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -197,24 +197,27 @@ bool Zone::LoadZoneObjects() if (!shortname) continue; - Door d; - memset(&d, 0, sizeof(d)); + // todo: clean up duplicate code with command_object + auto d = DoorsRepository::NewEntity(); - strn0cpy(d.zone_name, shortname, sizeof(d.zone_name)); - d.db_id = 1000000000 + atoi(row[0]); // Out of range of normal use for doors.id - d.door_id = -1; // Client doesn't care if these are all the same door_id + d.zone = shortname; + d.id = 1000000000 + atoi(row[0]); // Out of range of normal use for doors.id + d.doorid = -1; // Client doesn't care if these are all the same door_id d.pos_x = atof(row[2]); // xpos d.pos_y = atof(row[3]); // ypos d.pos_z = atof(row[4]); // zpos d.heading = atof(row[5]); // heading - strn0cpy(d.door_name, row[8], sizeof(d.door_name)); // objectname - // Strip trailing "_ACTORDEF" if present. Client won't accept it for doors. - int len = strlen(d.door_name); - if ((len > 9) && (memcmp(&d.door_name[len - 9], "_ACTORDEF", 10) == 0)) - d.door_name[len - 9] = '\0'; + d.name = row[8]; // objectname - memcpy(d.dest_zone, "NONE", 5); + // Strip trailing "_ACTORDEF" if present. Client won't accept it for doors. + int pos = d.name.size() - strlen("_ACTORDEF"); + if (pos > 0 && d.name.compare(pos, std::string::npos, "_ACTORDEF") == 0) + { + d.name.erase(pos); + } + + d.dest_zone = "NONE"; if ((d.size = atoi(row[11])) == 0) // unknown08 = optional size percentage d.size = 100; @@ -232,7 +235,7 @@ bool Zone::LoadZoneObjects() d.incline = atoi(row[13]); // unknown20 = optional model incline value d.client_version_mask = 0xFFFFFFFF; // We should load the mask from the zone. - auto door = new Doors(&d); + auto door = new Doors(d); entity_list.AddDoor(door); } @@ -911,29 +914,19 @@ void Zone::LoadZoneDoors(const char* zone, int16 version) { LogInfo("Loading doors for [{}] ", zone); - uint32 maxid; - int32 count = content_db.GetDoorsCount(&maxid, zone, version); - if(count < 1) { + auto door_entries = content_db.LoadDoors(zone, version); + if (door_entries.empty()) + { LogInfo("No doors loaded"); return; } - auto dlist = new Door[count]; - - if(!content_db.LoadDoors(count, dlist, zone, version)) { - LogError("Failed to load doors"); - delete[] dlist; - return; - } - - int r; - Door *d = dlist; - for(r = 0; r < count; r++, d++) { - auto newdoor = new Doors(d); + for (const auto& entry : door_entries) + { + auto newdoor = new Doors(entry); entity_list.AddDoor(newdoor); - Log(Logs::Detail, Logs::Doors, "Door Add to Entity List, index: %u db id: %u, door_id %u", r, dlist[r].db_id, dlist[r].door_id); + LogDoorsDetail("Door added to entity list, db id: [{}], door_id: [{}]", entry.id, entry.doorid); } - delete[] dlist; } Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) diff --git a/zone/zonedb.h b/zone/zonedb.h index 61eb97ae1..03e69c242 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -10,6 +10,7 @@ #include "../common/eqemu_logsys.h" #include "aa_ability.h" #include "event_codes.h" +#include "../common/repositories/doors_repository.h" #ifdef BOTS #include "bot_database.h" @@ -526,7 +527,7 @@ public: /* Doors */ bool DoorIsOpen(uint8 door_id,const char* zone_name); void SetDoorPlace(uint8 value,uint8 door_id,const char* zone_name); - bool LoadDoors(int32 door_count, Door *into, const char *zone_name, int16 version); + std::vector LoadDoors(const std::string& zone_name, int16 version); uint32 GetGuildEQID(uint32 guilddbid); void UpdateDoorGuildID(int doorid, int guild_id); int32 GetDoorsCount(uint32* oMaxID, const char *zone_name, int16 version); diff --git a/zone/zonedump.h b/zone/zonedump.h index 09f11b65b..4bbdf47c8 100644 --- a/zone/zonedump.h +++ b/zone/zonedump.h @@ -201,37 +201,6 @@ struct PlayerCorpse_Struct { //std::list items; }; -struct Door { - uint32 db_id; - uint8 door_id; - char zone_name[32]; - char door_name[32]; - float pos_x; - float pos_y; - float pos_z; - float heading; - int incline; - uint8 opentype; - uint32 guild_id; - uint16 lock_pick; - uint32 keyitem; - uint8 nokeyring; - uint8 trigger_door; - uint8 trigger_type; - uint8 disable_timer; - uint32 door_param; - int invert_state; - uint16 size; - char dest_zone[16]; - uint32 dest_instance_id; - float dest_x; - float dest_y; - float dest_z; - float dest_heading; - uint8 is_ldon_door; - uint32 client_version_mask; -}; - #pragma pack() #endif From fb98349bbd25b965e57688070a11b4788f9d26cf Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Fri, 1 Oct 2021 23:11:16 -0400 Subject: [PATCH 233/624] [Quest API] Add mob SetPet and RemovePet quest apis (#1569) Will be required for tutoriala script and other similar events --- zone/lua_mob.cpp | 12 ++++++++++++ zone/lua_mob.h | 2 ++ zone/perl_mob.cpp | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index 72f10be76..3c2781c28 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -2194,6 +2194,16 @@ bool Lua_Mob::HasPet() { return self->HasPet(); } +void Lua_Mob::RemovePet() { + Lua_Safe_Call_Void(); + return self->SetPet(nullptr); +} + +void Lua_Mob::SetPet(Lua_Mob new_pet) { + Lua_Safe_Call_Void(); + return self->SetPet(new_pet); +} + bool Lua_Mob::IsSilenced() { Lua_Safe_Call_Bool(); return self->IsSilenced(); @@ -2769,6 +2779,8 @@ luabind::scope lua_register_mob() { .def("HasOwner", (bool(Lua_Mob::*)(void))&Lua_Mob::HasOwner) .def("IsPet", (bool(Lua_Mob::*)(void))&Lua_Mob::IsPet) .def("HasPet", (bool(Lua_Mob::*)(void))&Lua_Mob::HasPet) + .def("RemovePet", &Lua_Mob::RemovePet) + .def("SetPet", &Lua_Mob::SetPet) .def("IsSilenced", (bool(Lua_Mob::*)(void))&Lua_Mob::IsSilenced) .def("IsAmnesiad", (bool(Lua_Mob::*)(void))&Lua_Mob::IsAmnesiad) .def("GetMeleeMitigation", (int32(Lua_Mob::*)(void))&Lua_Mob::GetMeleeMitigation) diff --git a/zone/lua_mob.h b/zone/lua_mob.h index ebe4a59d0..cd3e524cb 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -412,6 +412,8 @@ public: bool HasOwner(); bool IsPet(); bool HasPet(); + void RemovePet(); + void SetPet(Lua_Mob new_pet); bool IsSilenced(); bool IsAmnesiad(); int32 GetMeleeMitigation(); diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index aa7a2eaab..ff8bc48b9 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -5945,6 +5945,43 @@ XS(XS_Mob_HasPet) { XSRETURN(1); } +XS(XS_Mob_RemovePet); +XS(XS_Mob_RemovePet) { + dXSARGS; + if (items != 1) { + Perl_croak(aTHX_ "Usage: Mob::RemovePet(THIS)"); // @categories Pet + } + + Mob* THIS; + VALIDATE_THIS_IS_MOB; + + THIS->SetPet(nullptr); + + XSRETURN_EMPTY; +} + +XS(XS_Mob_SetPet); +XS(XS_Mob_SetPet) { + dXSARGS; + if (items != 2) { + Perl_croak(aTHX_ "Usage: Mob::SetPet(THIS, Mob* new_pet)"); // @categories Pet + } + + Mob* THIS; + VALIDATE_THIS_IS_MOB; + + Mob* new_pet = nullptr; // passing null or invalid new_pet removes pet + if (sv_derived_from(ST(1), "Mob")) + { + IV tmp = SvIV((SV*)SvRV(ST(1))); + new_pet = INT2PTR(Mob*, tmp); + } + + THIS->SetPet(new_pet); + + XSRETURN_EMPTY; +} + XS(XS_Mob_IsSilenced); XS(XS_Mob_IsSilenced) { dXSARGS; @@ -6673,6 +6710,8 @@ XS(boot_Mob) { newXSproto(strcpy(buf, "HasOwner"), XS_Mob_HasOwner, file, "$"); newXSproto(strcpy(buf, "IsPet"), XS_Mob_IsPet, file, "$"); newXSproto(strcpy(buf, "HasPet"), XS_Mob_HasPet, file, "$"); + newXSproto(strcpy(buf, "RemovePet"), XS_Mob_RemovePet, file, "$"); + newXSproto(strcpy(buf, "SetPet"), XS_Mob_SetPet, file, "$$"); newXSproto(strcpy(buf, "IsSilenced"), XS_Mob_IsSilenced, file, "$"); newXSproto(strcpy(buf, "IsAmnesiad"), XS_Mob_IsAmnesiad, file, "$"); newXSproto(strcpy(buf, "GetMeleeMitigation"), XS_Mob_GetMeleeMitigation, file, "$"); From 5ffe6284ca6a2e7bf830ab81e87b052528bdd2a8 Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Fri, 1 Oct 2021 23:11:57 -0400 Subject: [PATCH 234/624] [Shared Tasks] Start solo task replay timers from completion time (#1568) Shared tasks start replay timers based on accept time but solo tasks should start from completion time. Solo tasks on live that have a non-unlimited duration may require further investigation --- zone/task_client_state.cpp | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/zone/task_client_state.cpp b/zone/task_client_state.cpp index e00960238..293bd5aca 100644 --- a/zone/task_client_state.cpp +++ b/zone/task_client_state.cpp @@ -2668,29 +2668,26 @@ void ClientTaskState::ListTaskTimers(Client* client) void ClientTaskState::AddReplayTimer(Client* client, ClientTaskInformation& client_task, TaskInformation& task) { // world adds timers for shared tasks and handles messages - if (task.type != TaskType::Shared && task.replay_timer_seconds > 0 && client) + if (task.type != TaskType::Shared && task.replay_timer_seconds > 0) { - int expire_time = client_task.accepted_time + task.replay_timer_seconds; + // solo task replay timers are based on completion time + auto expire_time = std::time(nullptr) + task.replay_timer_seconds; - auto seconds = expire_time - std::time(nullptr); - if (seconds > 0) // not already expired - { - auto timer = CharacterTaskTimersRepository::NewEntity(); - timer.character_id = client->CharacterID(); - timer.task_id = client_task.task_id; - timer.expire_time = expire_time; - timer.timer_type = static_cast(TaskTimerType::Replay); + auto timer = CharacterTaskTimersRepository::NewEntity(); + timer.character_id = client->CharacterID(); + timer.task_id = client_task.task_id; + timer.expire_time = expire_time; + timer.timer_type = static_cast(TaskTimerType::Replay); - CharacterTaskTimersRepository::InsertOne(database, timer); + CharacterTaskTimersRepository::InsertOne(database, timer); - client->Message(Chat::Yellow, fmt::format( - SharedTaskMessage::GetEQStr(SharedTaskMessage::RECEIVED_REPLAY_TIMER), - task.title, - fmt::format_int(seconds / 86400).c_str(), // days - fmt::format_int((seconds / 3600) % 24).c_str(), // hours - fmt::format_int((seconds / 60) % 60).c_str() // minutes - ).c_str()); - } + client->Message(Chat::Yellow, fmt::format( + SharedTaskMessage::GetEQStr(SharedTaskMessage::RECEIVED_REPLAY_TIMER), + task.title, + fmt::format_int(task.replay_timer_seconds / 86400).c_str(), // days + fmt::format_int((task.replay_timer_seconds / 3600) % 24).c_str(), // hours + fmt::format_int((task.replay_timer_seconds / 60) % 60).c_str() // minutes + ).c_str()); } } From 92e03dccb9e87dd2b27d8a3ece2cb9034e725d70 Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Fri, 1 Oct 2021 23:12:45 -0400 Subject: [PATCH 235/624] [Quest API] Add perl hash apis for dz creation (#1571) Add hash overload to perl CreateExpedition api This adds an api to perl similar to the Lua api that accepts a reference to a hash table with expedition creation info Usage example: my $expedition_info = { expedition => { name => "Perl expedition", min_players => 2, max_players => 6 }, instance => { zone => "crushbone", version => 0, duration => 3600 }, compass => { zone => "gfaydark", x => 238, y => 987, z => -24.90 }, safereturn => { zone => "gfaydark", x => 245.84, y => 987.93, z => -27.6, h => 484.0 }, zonein => { x => 479.44, y => -500.18, z => 5.75, h => 421.8 } }; $client->CreateExpedition($expedition_info); Syntax for passing directly from a hash: my %expedition_info = (...); $client->CreateExpedition(\%expedition_info); Add CreateTaskDynamicZone api to perl Usage example: sub EVENT_TASKACCEPTED { if ($task_id == 4795) { my %dz_hash = ( "instance", { zone=>"thundercrest", version => 11 }, "compass", { zone=>"broodlands", x=>1241.88, y=>511.147, z=>23.4192 }, "safereturn", { zone=>"broodlands", x=>1242.0, y=>526.0, z=>27.0, h=>0.0 } ); $client->CreateTaskDynamicZone($task_id, \%dz_hash) } } --- zone/perl_client.cpp | 250 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 239 insertions(+), 11 deletions(-) diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 2030ea061..764002223 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -4759,26 +4759,165 @@ XS(XS_Client_GetClientMaxLevel) { XSRETURN(1); } +DynamicZoneLocation GetDynamicZoneLocationFromHash(HV* hash) +{ + // dynamic zone helper method (caller must validate hash) + SV** zone_ptr = hv_fetchs(hash, "zone", false); + SV** x_ptr = hv_fetchs(hash, "x", false); + SV** y_ptr = hv_fetchs(hash, "y", false); + SV** z_ptr = hv_fetchs(hash, "z", false); + SV** h_ptr = hv_fetchs(hash, "h", false); + + uint32_t zone_id = 0; + if (zone_ptr && SvIOK(*zone_ptr)) + { + zone_id = static_cast(SvIV(*zone_ptr)); + } + else if (zone_ptr && SvPOK(*zone_ptr)) + { + zone_id = ZoneID(SvPV_nolen(*zone_ptr)); + } + + // SvNIOK checks for number, integer or double + float x = (x_ptr && SvNIOK(*x_ptr)) ? static_cast(SvNV(*x_ptr)) : 0.0f; + float y = (y_ptr && SvNIOK(*y_ptr)) ? static_cast(SvNV(*y_ptr)) : 0.0f; + float z = (z_ptr && SvNIOK(*z_ptr)) ? static_cast(SvNV(*z_ptr)) : 0.0f; + float h = (h_ptr && SvNIOK(*h_ptr)) ? static_cast(SvNV(*h_ptr)) : 0.0f; + + return { zone_id, x, y, z, h }; +} + +Expedition* CreateExpeditionFromHash(Client* client, SV* hash_ref) +{ + if (!hash_ref || !SvROK(hash_ref)) // verify valid reference type + { + Perl_croak(aTHX_ "Client::CreateExpedition argument is not a reference type"); + } + + HV* hash = (HV*)SvRV(hash_ref); // dereference and verify type is hash + if (SvTYPE(hash) != SVt_PVHV) + { + Perl_croak(aTHX_ "Client::CreateExpedition reference argument is not to a hash type"); + } + + SV** expedition_info_ptr = hv_fetchs(hash, "expedition", false); + if (!expedition_info_ptr) + { + Perl_croak(aTHX_ "Client::CreateExpedition required 'expedition' key missing from hash"); + } + + if (!SvROK(*expedition_info_ptr) || SvTYPE(SvRV(*expedition_info_ptr)) != SVt_PVHV) + { + Perl_croak(aTHX_ "Client::CreateExpedition 'expedition' entry must have a hash table value"); + } + + SV** instance_info_ptr = hv_fetchs(hash, "instance", false); + if (!instance_info_ptr) + { + Perl_croak(aTHX_ "Client::CreateExpedition required 'instance' key missing from hash"); + } + + if (!SvROK(*instance_info_ptr) || SvTYPE(SvRV(*instance_info_ptr)) != SVt_PVHV) + { + Perl_croak(aTHX_ "Client::CreateExpedition 'instance' entry must have a hash table value"); + } + + // dereference the nested hash tables and validate required keys + HV* expedition_hash = (HV*)SvRV(*expedition_info_ptr); + SV** name_ptr = hv_fetchs(expedition_hash, "name", false); + SV** min_players_ptr = hv_fetchs(expedition_hash, "min_players", false); + SV** max_players_ptr = hv_fetchs(expedition_hash, "max_players", false); + SV** disable_msg_ptr = hv_fetchs(expedition_hash, "disable_messages", false); + if (!name_ptr || !min_players_ptr || !max_players_ptr) + { + Perl_croak(aTHX_ "Client::CreateExpedition 'expedition' hash table missing required keys (name, min_players, max_players)"); + } + + HV* instance_hash = (HV*)SvRV(*instance_info_ptr); + SV** instance_zone_ptr = hv_fetchs(instance_hash, "zone", false); + SV** version_ptr = hv_fetchs(instance_hash, "version", false); + SV** duration_ptr = hv_fetchs(instance_hash, "duration", false); + if (!instance_zone_ptr || !version_ptr || !duration_ptr) + { + Perl_croak(aTHX_ "Client::CreateExpedition 'instance' hash table missing required keys (zone, version, duration)"); + } + + uint32_t zone_id = 0; + if (SvIOK(*instance_zone_ptr)) + { + zone_id = static_cast(SvIV(*instance_zone_ptr)); + } + else if (SvPOK(*instance_zone_ptr)) + { + zone_id = ZoneID(SvPV_nolen(*instance_zone_ptr)); + } + else + { + Perl_croak(aTHX_ "Client::CreateExpedition zone value in 'instance' table must be int or string"); + } + + uint32_t zone_version = SvIOK(*version_ptr) ? static_cast(SvIV(*version_ptr)) : 0; + uint32_t zone_duration = SvIOK(*duration_ptr) ? static_cast(SvIV(*duration_ptr)) : 0; + + DynamicZone dz{ zone_id, zone_version, zone_duration, DynamicZoneType::Expedition }; + dz.SetName(SvPOK(*name_ptr) ? SvPV_nolen(*name_ptr) : ""); + dz.SetMinPlayers(SvIOK(*min_players_ptr) ? static_cast(SvIV(*min_players_ptr)) : 0); + dz.SetMaxPlayers(SvIOK(*max_players_ptr) ? static_cast(SvIV(*max_players_ptr)) : 0); + + SV** compass_ptr = hv_fetchs(hash, "compass", false); + if (compass_ptr && SvROK(*compass_ptr) && SvTYPE(SvRV(*compass_ptr)) == SVt_PVHV) + { + auto compass_loc = GetDynamicZoneLocationFromHash((HV*)SvRV(*compass_ptr)); + dz.SetCompass(compass_loc); + } + + SV** safereturn_ptr = hv_fetchs(hash, "safereturn", false); + if (safereturn_ptr && SvROK(*safereturn_ptr) && SvTYPE(SvRV(*safereturn_ptr)) == SVt_PVHV) + { + auto safereturn_loc = GetDynamicZoneLocationFromHash((HV*)SvRV(*safereturn_ptr)); + dz.SetSafeReturn(safereturn_loc); + } + + SV** zonein_ptr = hv_fetchs(hash, "zonein", false); + if (zonein_ptr && SvROK(*zonein_ptr) && SvTYPE(SvRV(*zonein_ptr)) == SVt_PVHV) + { + auto zonein_loc = GetDynamicZoneLocationFromHash((HV*)SvRV(*zonein_ptr)); + dz.SetZoneInLocation(zonein_loc); + } + + bool disable_messages = (disable_msg_ptr && SvIOK(*disable_msg_ptr)) ? SvTRUE(*disable_msg_ptr) : false; + + return client->CreateExpedition(dz, disable_messages); +} + XS(XS_Client_CreateExpedition); XS(XS_Client_CreateExpedition) { dXSARGS; - if (items != 7 && items != 8) { - Perl_croak(aTHX_ "Usage: Client::CreateExpedition(THIS, string zone_name, uint32 zone_version, uint32 duration, string expedition_name, uint32 min_players, uint32 max_players, [bool disable_messages = false])"); + if (items != 2 && items != 7 && items != 8) { + Perl_croak(aTHX_ "Usage: Client::CreateExpedition(THIS, HASHREF expedition_info | string zone_name, uint32 zone_version, uint32 duration, string expedition_name, uint32 min_players, uint32 max_players, [bool disable_messages = false])"); } Client* THIS = nullptr; VALIDATE_THIS_IS_CLIENT; - std::string zone_name(SvPV_nolen(ST(1))); - uint32 zone_version = (uint32)SvUV(ST(2)); - uint32 duration = (uint32)SvUV(ST(3)); - std::string expedition_name(SvPV_nolen(ST(4))); - uint32 min_players = (uint32)SvUV(ST(5)); - uint32 max_players = (uint32)SvUV(ST(6)); - bool disable_messages = (items > 7) ? (bool)SvTRUE(ST(7)) : false; + Expedition* RETVAL = nullptr; + if (items == 2) + { + RETVAL = CreateExpeditionFromHash(THIS, ST(1)); + } + else + { + std::string zone_name(SvPV_nolen(ST(1))); + uint32 zone_version = (uint32)SvUV(ST(2)); + uint32 duration = (uint32)SvUV(ST(3)); + std::string expedition_name(SvPV_nolen(ST(4))); + uint32 min_players = (uint32)SvUV(ST(5)); + uint32 max_players = (uint32)SvUV(ST(6)); + bool disable_messages = (items > 7) ? (bool)SvTRUE(ST(7)) : false; - Expedition* RETVAL = THIS->CreateExpedition(zone_name, zone_version, duration, - expedition_name, min_players, max_players, disable_messages); + RETVAL = THIS->CreateExpedition(zone_name, zone_version, duration, + expedition_name, min_players, max_players, disable_messages); + } ST(0) = sv_newmortal(); sv_setref_pv(ST(0), "Expedition", (void*)RETVAL); @@ -4786,6 +4925,94 @@ XS(XS_Client_CreateExpedition) { XSRETURN(1); } +XS(XS_Client_CreateTaskDynamicZone); +XS(XS_Client_CreateTaskDynamicZone) { + dXSARGS; + if (items != 3) { + Perl_croak(aTHX_ "Usage: Client::CreateTaskDynamicZone(THIS, int task_id, HASHREF dz_info)"); + } + + Client* THIS = nullptr; + VALIDATE_THIS_IS_CLIENT; + + SV* hash_ref = ST(2); + if (!hash_ref || !SvROK(hash_ref)) + { + Perl_croak(aTHX_ "Client::CreateTaskDynamicZone argument is not a reference type"); + } + + HV* hash = (HV*)SvRV(hash_ref); // dereference into hash + if (SvTYPE(hash) != SVt_PVHV) + { + Perl_croak(aTHX_ "Client::CreateTaskDynamicZone reference argument is not to a hash type"); + } + + SV** instance_info_ptr = hv_fetchs(hash, "instance", false); + if (!instance_info_ptr) + { + Perl_croak(aTHX_ "Client::CreateTaskDynamicZone required 'instance' key missing from hash"); + } + + if (!SvROK(*instance_info_ptr) || SvTYPE(SvRV(*instance_info_ptr)) != SVt_PVHV) + { + Perl_croak(aTHX_ "Client::CreateTaskDynamicZone 'instance' entry must have a hash table value"); + } + + HV* instance_hash = (HV*)SvRV(*instance_info_ptr); + SV** instance_zone_ptr = hv_fetchs(instance_hash, "zone", false); + SV** version_ptr = hv_fetchs(instance_hash, "version", false); + SV** duration_ptr = hv_fetchs(instance_hash, "duration", false); + if (!instance_zone_ptr || !version_ptr) + { + Perl_croak(aTHX_ "Client::CreateTaskDynamicZone 'instance' hash table missing required keys (zone, version, duration)"); + } + + uint32_t zone_id = 0; + SV* instance_zone = *instance_zone_ptr; + if (SvIOK(instance_zone)) + { + zone_id = static_cast(SvIV(instance_zone)); + } + else if (SvPOK(instance_zone)) + { + zone_id = ZoneID(SvPV_nolen(instance_zone)); + } + else + { + Perl_croak(aTHX_ "Client::CreateTaskDynamicZone zone value in 'instance' table must be int or string"); + } + + uint32_t zone_version = SvIOK(*version_ptr) ? static_cast(SvIV(*version_ptr)) : 0; + + // tasks override dz duration so duration is ignored here + DynamicZone dz{ zone_id, zone_version, 0, DynamicZoneType::None }; + + SV** compass_ptr = hv_fetchs(hash, "compass", false); + if (compass_ptr && SvROK(*compass_ptr) && SvTYPE(SvRV(*compass_ptr)) == SVt_PVHV) + { + auto compass_loc = GetDynamicZoneLocationFromHash((HV*)SvRV(*compass_ptr)); + dz.SetCompass(compass_loc); + } + + SV** safereturn_ptr = hv_fetchs(hash, "safereturn", false); + if (safereturn_ptr && SvROK(*safereturn_ptr) && SvTYPE(SvRV(*safereturn_ptr)) == SVt_PVHV) + { + auto safereturn_loc = GetDynamicZoneLocationFromHash((HV*)SvRV(*safereturn_ptr)); + dz.SetSafeReturn(safereturn_loc); + } + + SV** zonein_ptr = hv_fetchs(hash, "zonein", false); + if (zonein_ptr && SvROK(*zonein_ptr) && SvTYPE(SvRV(*zonein_ptr)) == SVt_PVHV) + { + auto zonein_loc = GetDynamicZoneLocationFromHash((HV*)SvRV(*zonein_ptr)); + dz.SetZoneInLocation(zonein_loc); + } + + uint32_t task_id = static_cast(SvUV(ST(1))); + + THIS->CreateTaskDynamicZone(task_id, dz); +} + XS(XS_Client_GetExpedition); XS(XS_Client_GetExpedition) { dXSARGS; @@ -5515,6 +5742,7 @@ XS(boot_Client) { newXSproto(strcpy(buf, "ClearCompassMark"), XS_Client_ClearCompassMark, file, "$"); newXSproto(strcpy(buf, "ClearZoneFlag"), XS_Client_ClearZoneFlag, file, "$$"); newXSproto(strcpy(buf, "CreateExpedition"), XS_Client_CreateExpedition, file, "$$$$$$$;$"); + newXSproto(strcpy(buf, "CreateTaskDynamicZone"), XS_Client_CreateTaskDynamicZone, file, "$$"); newXSproto(strcpy(buf, "Connected"), XS_Client_Connected, file, "$"); newXSproto(strcpy(buf, "CountItem"), XS_Client_CountItem, file, "$$"); newXSproto(strcpy(buf, "DecreaseByID"), XS_Client_DecreaseByID, file, "$$$"); From 2f5d360e53294eb96afd8178162288dec0427ce8 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 1 Oct 2021 23:14:56 -0400 Subject: [PATCH 236/624] [Quest API] Add UntrainDiscBySpellID(spell_id, update_client) to Perl/Lua. (#1565) - Add $client->UntrainDiscBySpellID(spell_id, update_client) to Perl. - Add client:UntrainDiscBySpellID(spell_id, update_client) to Lua. --- zone/client.h | 1 + zone/lua_client.cpp | 34 +++++++++++++++++++++++----------- zone/lua_client.h | 2 ++ zone/perl_client.cpp | 20 ++++++++++++++++++++ zone/spells.cpp | 10 ++++++++++ 5 files changed, 56 insertions(+), 11 deletions(-) diff --git a/zone/client.h b/zone/client.h index c53fbb21b..5e84cf96b 100644 --- a/zone/client.h +++ b/zone/client.h @@ -800,6 +800,7 @@ public: void UnscribeSpellAll(bool update_client = true); void UntrainDisc(int slot, bool update_client = true); void UntrainDiscAll(bool update_client = true); + void UntrainDiscBySpellID(uint16 spell_id, bool update_client = true); bool SpellGlobalCheck(uint16 spell_id, uint32 char_id); bool SpellBucketCheck(uint16 spell_id, uint32 char_id); uint32 GetCharMaxLevelFromQGlobal(); diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index d758dc6c8..fee64d493 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -2206,17 +2206,27 @@ int Lua_Client::CountItem(uint32 item_id) { void Lua_Client::RemoveItem(uint32 item_id) { Lua_Safe_Call_Void(); - return self->RemoveItem(item_id); + self->RemoveItem(item_id); } void Lua_Client::RemoveItem(uint32 item_id, uint32 quantity) { Lua_Safe_Call_Void(); - return self->RemoveItem(item_id, quantity); + self->RemoveItem(item_id, quantity); } void Lua_Client::SetGMStatus(uint32 newStatus) { Lua_Safe_Call_Void(); - return self->SetGMStatus(newStatus); + self->SetGMStatus(newStatus); +} + +void Lua_Client::UntrainDiscBySpellID(uint16 spell_id) { + Lua_Safe_Call_Void(); + self->UntrainDiscBySpellID(spell_id); +} + +void Lua_Client::UntrainDiscBySpellID(uint16 spell_id, bool update_client) { + Lua_Safe_Call_Void(); + self->UntrainDiscBySpellID(spell_id, update_client); } luabind::scope lua_register_client() { @@ -2475,7 +2485,7 @@ luabind::scope lua_register_client() { .def("ClearCompassMark",(void(Lua_Client::*)(void))&Lua_Client::ClearCompassMark) .def("GetNextAvailableSpellBookSlot", (int(Lua_Client::*)(void))&Lua_Client::GetNextAvailableSpellBookSlot) .def("GetNextAvailableSpellBookSlot", (int(Lua_Client::*)(int))&Lua_Client::GetNextAvailableSpellBookSlot) - .def("GetSpellIDByBookSlot", (uint32(Lua_Client::*)(int))& Lua_Client::GetSpellIDByBookSlot) + .def("GetSpellIDByBookSlot", (uint32(Lua_Client::*)(int))&Lua_Client::GetSpellIDByBookSlot) .def("FindSpellBookSlotBySpellID", (int(Lua_Client::*)(int))&Lua_Client::FindSpellBookSlotBySpellID) .def("UpdateTaskActivity", (void(Lua_Client::*)(int,int,int))&Lua_Client::UpdateTaskActivity) .def("AssignTask", (void(Lua_Client::*)(int,int))&Lua_Client::AssignTask) @@ -2580,19 +2590,21 @@ luabind::scope lua_register_client() { .def("AddLDoNLoss", (void(Lua_Client::*)(uint32))&Lua_Client::AddLDoNLoss) .def("AddLDoNWin", (void(Lua_Client::*)(uint32))&Lua_Client::AddLDoNWin) .def("SetHideMe", (void(Lua_Client::*)(bool))&Lua_Client::SetHideMe) - .def("Popup", (void(Lua_Client::*)(const char*,const char*))& Lua_Client::Popup) - .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32))& Lua_Client::Popup) - .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32))& Lua_Client::Popup) - .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32,uint32))& Lua_Client::Popup) - .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32,uint32,uint32))& Lua_Client::Popup) - .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32,uint32,uint32,const char*,const char*))& Lua_Client::Popup) + .def("Popup", (void(Lua_Client::*)(const char*,const char*))&Lua_Client::Popup) + .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32))&Lua_Client::Popup) + .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32))&Lua_Client::Popup) + .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32,uint32))&Lua_Client::Popup) + .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32,uint32,uint32))&Lua_Client::Popup) + .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32,uint32,uint32,const char*,const char*))&Lua_Client::Popup) .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32,uint32,uint32,const char*,const char*,uint32))&Lua_Client::Popup) .def("ResetAllDisciplineTimers", (void(Lua_Client::*)(void))&Lua_Client::ResetAllDisciplineTimers) .def("SendToInstance", (void(Lua_Client::*)(std::string,std::string,uint32,float,float,float,float,std::string,uint32))&Lua_Client::SendToInstance) .def("CountItem", (int(Lua_Client::*)(uint32))&Lua_Client::CountItem) .def("RemoveItem", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveItem) .def("RemoveItem", (void(Lua_Client::*)(uint32,uint32))&Lua_Client::RemoveItem) - .def("SetGMStatus", (void(Lua_Client::*)(int32))& Lua_Client::SetGMStatus); + .def("SetGMStatus", (void(Lua_Client::*)(int32))&Lua_Client::SetGMStatus) + .def("UntrainDiscBySpellID", (void(Lua_Client::*)(uint16))&Lua_Client::UntrainDiscBySpellID) + .def("UntrainDiscBySpellID", (void(Lua_Client::*)(uint16,bool))&Lua_Client::UntrainDiscBySpellID); } luabind::scope lua_register_inventory_where() { diff --git a/zone/lua_client.h b/zone/lua_client.h index 985939834..b49cb9b31 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -183,6 +183,8 @@ public: int GetDiscSlotBySpellID(int32 spell_id); void UntrainDisc(int slot); void UntrainDisc(int slot, bool update_client); + void UntrainDiscBySpellID(uint16 spell_id); + void UntrainDiscBySpellID(uint16 spell_id, bool update_client); void UntrainDiscAll(); void UntrainDiscAll(bool update_client); bool IsStanding(); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 764002223..9c810c7ef 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -5693,6 +5693,25 @@ XS(XS_Client_DiaWind) { XSRETURN_EMPTY; } +XS(XS_Client_UntrainDiscBySpellID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_UntrainDiscBySpellID) { + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: Client::UntrainDiscBySpellID(THIS, uint16 spell_id, [bool update_client = true])"); // @categories Spells and Disciplines + { + Client *THIS; + uint16 spell_id = (uint16) SvUV(ST(1)); + bool update_client = true; + VALIDATE_THIS_IS_CLIENT; + if (items == 3) { + update_client = (bool) SvTRUE(ST(2)); + } + + THIS->UntrainDiscBySpellID(spell_id, update_client); + } + XSRETURN_EMPTY; +} + #ifdef __cplusplus extern "C" #endif @@ -5998,6 +6017,7 @@ XS(boot_Client) { newXSproto(strcpy(buf, "UnscribeSpellAll"), XS_Client_UnscribeSpellAll, file, "$;$"); newXSproto(strcpy(buf, "UntrainDisc"), XS_Client_UntrainDisc, file, "$$;$"); newXSproto(strcpy(buf, "UntrainDiscAll"), XS_Client_UntrainDiscAll, file, "$;$"); + newXSproto(strcpy(buf, "UntrainDiscBySpellID"), XS_Client_UntrainDiscBySpellID, file, "$$;$"); newXSproto(strcpy(buf, "UpdateAdmin"), XS_Client_UpdateAdmin, file, "$;$"); newXSproto(strcpy(buf, "SetGMStatus"), XS_Client_SetGMStatus, file, "$$"); newXSproto(strcpy(buf, "UpdateGroupAAs"), XS_Client_UpdateGroupAAs, file, "$$$"); diff --git a/zone/spells.cpp b/zone/spells.cpp index dd101ba51..2553b5e10 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -5335,6 +5335,16 @@ void Client::UntrainDiscAll(bool update_client) } } +void Client::UntrainDiscBySpellID(uint16 spell_id, bool update_client) +{ + for (int slot = 0; slot < MAX_PP_DISCIPLINES; slot++) { + if (m_pp.disciplines.values[slot] == spell_id) { + UntrainDisc(slot, update_client); + return; + } + } +} + int Client::GetNextAvailableSpellBookSlot(int starting_slot) { for (int i = starting_slot; i < EQ::spells::SPELLBOOK_SIZE; i++) { //using starting_slot should help speed this up when we're iterating through a bunch of spells if (!IsValidSpell(GetSpellByBookSlot(i))) From bb5c49179443159e8ea9201fb1581640ebff8f85 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 1 Oct 2021 23:20:15 -0400 Subject: [PATCH 237/624] [Dialogue] Add support for Dialogue Window titles. (#1563) * [Dialogue] Add support for Dialogue Window titles. - Custom title allows defaults to be overridden where necessary, like a leaderboard or something. - Default target to client in case people want to send Dialogue Windows from current client. * Fix possible issue with markdown. - Example: Using the word "title" or using any identifier and forgetting the colon. --- zone/dialogue_window.cpp | 49 ++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/zone/dialogue_window.cpp b/zone/dialogue_window.cpp index bae0d8533..f9a42d26c 100644 --- a/zone/dialogue_window.cpp +++ b/zone/dialogue_window.cpp @@ -9,10 +9,7 @@ void DialogueWindow::Render(Client *c, std::string markdown) } // this is the NPC that the client is interacting with if there is dialogue going on - Mob *target; - if (c->GetTarget()) { - target = c->GetTarget(); - } + Mob* target = c->GetTarget() ? c->GetTarget() : c; // zero this out c->SetEntityVariable(DIAWIND_RESPONSE_ONE_KEY.c_str(), ""); @@ -126,7 +123,7 @@ void DialogueWindow::Render(Client *c, std::string markdown) // window type std::string wintype; - if (markdown.find("wintype") != std::string::npos) { + if (markdown.find("wintype:") != std::string::npos) { LogDiaWind("Client [{}] Rendering wintype option", c->GetCleanName()); auto first_split = split_string(output, "wintype:"); @@ -157,7 +154,7 @@ void DialogueWindow::Render(Client *c, std::string markdown) // popupid std::string popupid; - if (markdown.find("popupid") != std::string::npos) { + if (markdown.find("popupid:") != std::string::npos) { LogDiaWind("Client [{}] Rendering popupid option", c->GetCleanName()); auto first_split = split_string(output, "popupid:"); @@ -193,7 +190,7 @@ void DialogueWindow::Render(Client *c, std::string markdown) // secondresponseid std::string secondresponseid; - if (markdown.find("secondresponseid") != std::string::npos) { + if (markdown.find("secondresponseid:") != std::string::npos) { LogDiaWind("Client [{}] Rendering secondresponseid option", c->GetCleanName()); auto first_split = split_string(output, "secondresponseid:"); @@ -225,8 +222,8 @@ void DialogueWindow::Render(Client *c, std::string markdown) std::string button_one; std::string button_two; if ( - markdown.find("button_one") != std::string::npos && - markdown.find("button_two") != std::string::npos + markdown.find("button_one:") != std::string::npos && + markdown.find("button_two:") != std::string::npos ) { LogDiaWind("Client [{}] Rendering button_one option.", c->GetCleanName()); @@ -397,7 +394,39 @@ void DialogueWindow::Render(Client *c, std::string markdown) speaking = "A Mysterious Voice says"; } - title = fmt::format("Dialogue [{}]", speaking); + // title + std::string popup_title; + if (markdown.find("title:") != std::string::npos) { + LogDiaWind("Client [{}] Rendering title option", c->GetCleanName()); + + auto first_split = split_string(output, "title:"); + if (!first_split.empty()) { + auto second_split = split_string(first_split[1], " "); + if (!second_split.empty()) { + popup_title = second_split[0]; + LogDiaWindDetail("Client [{}] Rendering title option title [{}]", c->GetCleanName(), popup_title); + } + + if (first_split[1].length() == 1) { + popup_title = first_split[1]; + LogDiaWindDetail( + "Client [{}] Rendering title (end) option title [{}]", + c->GetCleanName(), + popup_title + ); + } + + find_replace(output, fmt::format("title:{}", popup_title), ""); + + if (!popup_title.empty()) { + title = popup_title; + } + } + } + + if (title.empty()) { + title = fmt::format("Dialogue [{}]", speaking); + } // render quotes std::string quote_string = "'"; From 9a413cf5537896f5d154a17487c6b2a6aabf2691 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Fri, 1 Oct 2021 22:57:00 -0500 Subject: [PATCH 238/624] [Shared Tasks] Task Kill Update Fix (#1573) * Revert "Revert "Shared task kill update fix"" This reverts commit 859751f74d671ecaa36d777450803ece85a72bf4. * Swap return for continue in this context * Slight tweak * Slight tweak * Remove no longer needed task methods * Update scope for IncrementDoneCount * Create helper method Client::GetPartyMembers() and add client->HasTaskState() * Move HandleUpdateTasksOnKill responsibility to TaskManager * Remove unnecessary pointer --- zone/attack.cpp | 4 +- zone/client.cpp | 38 +++++++++++++ zone/client.h | 13 ++--- zone/client_packet.cpp | 2 + zone/task_client_state.cpp | 111 ------------------------------------- zone/task_client_state.h | 3 - zone/task_manager.cpp | 93 +++++++++++++++++++++++++++++++ zone/task_manager.h | 2 + 8 files changed, 140 insertions(+), 126 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index a41f0911a..d5cd9005c 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2357,9 +2357,7 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy give_exp_client->GetCleanName(), GetNPCTypeID() ); - give_exp_client - ->GetTaskState() - ->HandleUpdateTasksOnKill(give_exp_client, GetNPCTypeID()); + task_manager->HandleUpdateTasksOnKill(give_exp_client, GetNPCTypeID()); } if (kr) { diff --git a/zone/client.cpp b/zone/client.cpp index 8d64cd019..3845357a6 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -10529,3 +10529,41 @@ void Client::SetDoorToolEntityId(uint16 door_tool_entity_id) { Client::m_door_tool_entity_id = door_tool_entity_id; } + +// this will fetch raid clients if exists +// fallback to group if raid doesn't exist +// fallback to self if group doesn't exist +std::vector Client::GetPartyMembers() +{ + // get clients to update + std::vector clients_to_update = {}; + + // raid + Raid *raid = entity_list.GetRaidByClient(this); + if (raid) { + for (auto &e : raid->members) { + if (e.member && e.member->IsClient()) { + clients_to_update.push_back(e.member->CastToClient()); + } + } + } + + // group + if (clients_to_update.empty()) { + Group *group = entity_list.GetGroupByClient(this); + if (group) { + for (auto &m : group->members) { + if (m && m->IsClient()) { + clients_to_update.push_back(m->CastToClient()); + } + } + } + } + + // solo + if (clients_to_update.empty()) { + clients_to_update.push_back(this); + } + + return clients_to_update; +} diff --git a/zone/client.h b/zone/client.h index 5e84cf96b..aa703f050 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1040,6 +1040,7 @@ public: void SendTaskRequestCooldownTimerMessage(); void StartTaskRequestCooldownTimer(); inline ClientTaskState *GetTaskState() const { return task_state; } + inline bool HasTaskState() { if (task_state) { return true; } return false; } inline void CancelTask(int task_index, TaskType task_type) { if (task_state) { @@ -1094,15 +1095,6 @@ public: ); } } - inline void UpdateTasksOnKill(int npc_type_id) - { - if (task_state) { - task_state->UpdateTasksOnKill( - this, - npc_type_id - ); - } - } inline void UpdateTasksForItem( TaskActivityType activity_type, int item_id, @@ -1281,6 +1273,9 @@ public: bool m_shared_task_update = false; bool m_requested_shared_task_removal = false; + std::vector GetPartyMembers(); + void HandleUpdateTasksOnKill(uint32 npc_type_id); + inline const EQ::versions::ClientVersion ClientVersion() const { return m_ClientVersion; } inline const uint32 ClientVersionBit() const { return m_ClientVersionBit; } inline void SetClientVersion(EQ::versions::ClientVersion client_version) { m_ClientVersion = client_version; } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 513cd5942..d751fad8b 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -67,6 +67,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "../common/repositories/criteria/content_filter_criteria.h" #include "../common/shared_tasks.h" #include "gm_commands/door_manipulation.h" +#include "client.h" + #ifdef BOTS #include "bot.h" diff --git a/zone/task_client_state.cpp b/zone/task_client_state.cpp index 293bd5aca..d995044a7 100644 --- a/zone/task_client_state.cpp +++ b/zone/task_client_state.cpp @@ -581,11 +581,6 @@ bool ClientTaskState::UnlockActivities(int character_id, ClientTaskInformation & return false; } -void ClientTaskState::UpdateTasksOnKill(Client *client, int npc_type_id) -{ - UpdateTasksByNPC(client, TaskActivityType::Kill, npc_type_id); -} - bool ClientTaskState::UpdateTasksOnSpeakWith(Client *client, int npc_type_id) { return UpdateTasksByNPC(client, TaskActivityType::SpeakWith, npc_type_id); @@ -2734,112 +2729,6 @@ void ClientTaskState::SyncSharedTaskZoneClientDoneCountState( } } -void ClientTaskState::HandleUpdateTasksOnKill(Client *client, uint32 npc_type_id) -{ - if (!HasActiveTasks()) { - return; - } - - // loop over the union of tasks and quests - for (auto &active_task : m_active_tasks) { - auto current_task = &active_task; - if (current_task->task_id == TASKSLOTEMPTY) { - continue; - } - - // Check if there are any active kill activities for this p_task_data - auto p_task_data = task_manager->m_task_data[current_task->task_id]; - if (p_task_data == nullptr) { - return; - } - - for (int activity_id = 0; activity_id < p_task_data->activity_count; activity_id++) { - ClientActivityInformation *client_activity = ¤t_task->activity[activity_id]; - ActivityInformation *activity_info = &p_task_data->activity_information[activity_id]; - - // We are not interested in completed or hidden activities - if (client_activity->activity_state != ActivityActive) { - continue; - } - - // We are only interested in Kill activities - if (activity_info->activity_type != TaskActivityType::Kill) { - continue; - } - - // Is there a zone restriction on the activity_information ? - if (!activity_info->CheckZone(zone->GetZoneID())) { - LogTasks( - "[HandleUpdateTasksOnKill] character [{}] task_id [{}] activity_id [{}] activity_type [{}] for NPC [{}] failed zone check", - client->GetName(), - current_task->task_id, - activity_id, - static_cast(TaskActivityType::Kill), - npc_type_id - ); - continue; - } - // Is the activity_information to kill this type of NPC ? - switch (activity_info->goal_method) { - case METHODSINGLEID: - if (activity_info->goal_id != npc_type_id) { - LogTasksDetail("[HandleUpdateTasksOnKill] Matched single goal"); - continue; - } - break; - - case METHODLIST: - if (!task_manager->m_goal_list_manager.IsInList( - activity_info->goal_id, - (int) npc_type_id - )) { - LogTasksDetail("[HandleUpdateTasksOnKill] Matched list goal"); - continue; - } - break; - - default: - // If METHODQUEST, don't updated the activity_information here - continue; - } - - LogTasksDetail("[HandleUpdateTasksOnKill] passed checks"); - - // handle actual update - // legacy eqemu task update logic loops through group on kill of npc to update a single task - if (p_task_data->type != TaskType::Shared) { - LogTasksDetail("[HandleUpdateTasksOnKill] Non-Shared Update"); - - Raid *raid = entity_list.GetRaidByClient(client); - if (raid) { - for (auto &e : raid->members) { - if (e.member && e.member->IsClient()) { - Client *c = e.member->CastToClient(); - c->UpdateTasksOnKill(npc_type_id); - } - } - return; - } - - Group *group = entity_list.GetGroupByClient(client); - if (group) { - for (auto &m : group->members) { - if (m && m->IsClient()) { - Client *c = m->CastToClient(); - c->UpdateTasksOnKill(npc_type_id); - } - } - return; - } - } - - LogTasksDetail("[HandleUpdateTasksOnKill] Shared update"); - - // shared tasks only require one client to receive an update to propagate - client->UpdateTasksOnKill(npc_type_id); - } - } -} bool ClientTaskState::HasActiveTasks() { if (!task_manager) { diff --git a/zone/task_client_state.h b/zone/task_client_state.h index f45a522c9..ab3ad1044 100644 --- a/zone/task_client_state.h +++ b/zone/task_client_state.h @@ -34,7 +34,6 @@ public: void RemoveTask(Client *client, int sequence_number, TaskType task_type); void RemoveTaskByTaskID(Client *client, uint32 task_id); bool UpdateTasksByNPC(Client *client, TaskActivityType activity_type, int npc_type_id); - void UpdateTasksOnKill(Client *client, int npc_type_id); void UpdateTasksForItem(Client *client, TaskActivityType activity_type, int item_id, int count = 1); void UpdateTasksOnExplore(Client *client, int explore_id); bool UpdateTasksOnSpeakWith(Client *client, int npc_type_id); @@ -74,8 +73,6 @@ public: const ClientTaskInformation &GetActiveSharedTask() const; bool HasActiveSharedTask(); - - void HandleUpdateTasksOnKill(Client *client, uint32 npc_type_id); private: void AddReplayTimer(Client *client, ClientTaskInformation& client_task, TaskInformation& task); diff --git a/zone/task_manager.cpp b/zone/task_manager.cpp index 7616753e8..9204f4ee6 100644 --- a/zone/task_manager.cpp +++ b/zone/task_manager.cpp @@ -1814,3 +1814,96 @@ void TaskManager::SyncClientSharedTaskStateToLocal( } } } + +void TaskManager::HandleUpdateTasksOnKill(Client *client, uint32 npc_type_id) +{ + for (auto &c: client->GetPartyMembers()) { + if (!c->ClientDataLoaded() || !c->HasTaskState()) { + continue; + } + + LogTasksDetail("[HandleUpdateTasksOnKill] Looping through client [{}]", c->GetCleanName()); + + // loop over the union of tasks and quests + for (auto &active_task : c->GetTaskState()->m_active_tasks) { + auto current_task = &active_task; + if (current_task->task_id == TASKSLOTEMPTY) { + continue; + } + + // Check if there are any active kill activities for this p_task_data + auto p_task_data = m_task_data[current_task->task_id]; + if (p_task_data == nullptr) { + return; + } + + for (int activity_id = 0; activity_id < p_task_data->activity_count; activity_id++) { + ClientActivityInformation *client_activity = ¤t_task->activity[activity_id]; + ActivityInformation *activity_info = &p_task_data->activity_information[activity_id]; + + // We are not interested in completed or hidden activities + if (client_activity->activity_state != ActivityActive) { + continue; + } + + // We are only interested in Kill activities + if (activity_info->activity_type != TaskActivityType::Kill) { + continue; + } + + // Is there a zone restriction on the activity_information ? + if (!activity_info->CheckZone(zone->GetZoneID())) { + LogTasks( + "[HandleUpdateTasksOnKill] character [{}] task_id [{}] activity_id [{}] activity_type [{}] for NPC [{}] failed zone check", + client->GetName(), + current_task->task_id, + activity_id, + static_cast(TaskActivityType::Kill), + npc_type_id + ); + continue; + } + // Is the activity_information to kill this type of NPC ? + switch (activity_info->goal_method) { + case METHODSINGLEID: + if (activity_info->goal_id != npc_type_id) { + LogTasksDetail("[HandleUpdateTasksOnKill] Matched single goal"); + continue; + } + break; + + case METHODLIST: + if (!m_goal_list_manager.IsInList( + activity_info->goal_id, + (int) npc_type_id + )) { + LogTasksDetail("[HandleUpdateTasksOnKill] Matched list goal"); + continue; + } + break; + + default: + // If METHODQUEST, don't updated the activity_information here + continue; + } + + LogTasksDetail("[HandleUpdateTasksOnKill] passed checks"); + + // handle actual update + // legacy eqemu task update logic loops through group on kill of npc to update a single task + if (p_task_data->type != TaskType::Shared) { + LogTasksDetail("[HandleUpdateTasksOnKill] Non-Shared Update"); + c->GetTaskState()->IncrementDoneCount(c, p_task_data, current_task->slot, activity_id); + continue; + } + + LogTasksDetail("[HandleUpdateTasksOnKill] Shared update"); + + // shared tasks only require one client to receive an update to propagate + if (c == client) { + c->GetTaskState()->IncrementDoneCount(c, p_task_data, current_task->slot, activity_id); + } + } + } + } +} diff --git a/zone/task_manager.h b/zone/task_manager.h index 0914fe888..16eb51fcd 100644 --- a/zone/task_manager.h +++ b/zone/task_manager.h @@ -72,6 +72,8 @@ public: // shared tasks void SyncClientSharedTaskState(Client *c, ClientTaskState *cts); + void HandleUpdateTasksOnKill(Client *client, uint32 npc_type_id); + private: TaskGoalListManager m_goal_list_manager; TaskProximityManager m_proximity_manager; From 5560b198ca7da5d714bc7760af8dece865b3d6b2 Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Sat, 2 Oct 2021 12:00:00 -0400 Subject: [PATCH 239/624] [Quest API] Add client->SummonBaggedItems(bag_item_id, bag_items_ref) to Perl/Lua. Alternative apis using arrays of hash items for EQEmu/Server#1575 Perl usage: ```pl # create as an array, pass as reference my @bag_items = ( { item_id => 1001, charges => 1 }, { item_id => 1002, charges => 1 }, { item_id => 10037, charges => 10 }, ); $client->SummonBaggedItems(17403, \@bag_items); # create directly as an array reference my $bag_items = [ { item_id => 1001, charges => 1 }, { item_id => 1002, charges => 1 }, { item_id => 10037, charges => 10 }, ]; $client->SummonBaggedItems(17403, $bag_items); ``` Lua Usage: ```lua local bag_items = { { item_id = 1001, charges = 1 }, { item_id = 1002, charges = 1 }, { item_id = 10037, charges = 10 } } e.other:SummonBaggedItems(17403, bag_items); --- zone/client.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++++ zone/client.h | 1 + zone/lua_client.cpp | 28 +++++++++++++++++++- zone/lua_client.h | 1 + zone/perl_client.cpp | 52 +++++++++++++++++++++++++++++++++++++ 5 files changed, 143 insertions(+), 1 deletion(-) diff --git a/zone/client.cpp b/zone/client.cpp index 3845357a6..cc8b9f8cb 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -10567,3 +10567,65 @@ std::vector Client::GetPartyMembers() return clients_to_update; } + +void Client::SummonBaggedItems(uint32 bag_item_id, const std::vector& bag_items) +{ + if (bag_items.empty()) + { + return; + } + + // todo: maybe some common functions for SE_SummonItem and SE_SummonItemIntoBag + + const EQ::ItemData* bag_item = database.GetItem(bag_item_id); + if (!bag_item) + { + Message(Chat::Red, fmt::format("Unable to summon item [{}]. Item not found.", bag_item_id).c_str()); + return; + } + + if (CheckLoreConflict(bag_item)) + { + DuplicateLoreMessage(bag_item_id); + return; + } + + int bag_item_charges = 1; // just summoning a single bag + EQ::ItemInstance* summoned_bag = database.CreateItem(bag_item_id, bag_item_charges); + if (!summoned_bag || !summoned_bag->IsClassBag()) + { + Message(Chat::Red, fmt::format("Failed to summon bag item [{}]", bag_item_id).c_str()); + safe_delete(summoned_bag); + return; + } + + for (const auto& item : bag_items) + { + uint8 open_slot = summoned_bag->FirstOpenSlot(); + if (open_slot == 0xff) + { + Message(Chat::Red, "Attempting to summon item in to bag, but there is no room in the summoned bag!"); + break; + } + + const EQ::ItemData* current_item = database.GetItem(item.item_id); + + if (CheckLoreConflict(current_item)) + { + DuplicateLoreMessage(item.item_id); + } + else + { + EQ::ItemInstance* summoned_bag_item = database.CreateItem(item.item_id, item.charges); + if (summoned_bag_item) + { + summoned_bag->PutItem(open_slot, *summoned_bag_item); + safe_delete(summoned_bag_item); + } + } + } + + PushItemOnCursor(*summoned_bag); + SendItemPacket(EQ::invslot::slotCursor, summoned_bag, ItemPacketLimbo); + safe_delete(summoned_bag); +} diff --git a/zone/client.h b/zone/client.h index aa703f050..952c52fca 100644 --- a/zone/client.h +++ b/zone/client.h @@ -913,6 +913,7 @@ public: void PutLootInInventory(int16 slot_id, const EQ::ItemInstance &inst, ServerLootItem_Struct** bag_item_data = 0); bool AutoPutLootInInventory(EQ::ItemInstance& inst, bool try_worn = false, bool try_cursor = true, ServerLootItem_Struct** bag_item_data = 0); bool SummonItem(uint32 item_id, int16 charges = -1, uint32 aug1 = 0, uint32 aug2 = 0, uint32 aug3 = 0, uint32 aug4 = 0, uint32 aug5 = 0, uint32 aug6 = 0, bool attuned = false, uint16 to_slot = EQ::invslot::slotCursor, uint32 ornament_icon = 0, uint32 ornament_idfile = 0, uint32 ornament_hero_model = 0); + void SummonBaggedItems(uint32 bag_item_id, const std::vector& bag_items); void SetStats(uint8 type,int16 set_val); void IncStats(uint8 type,int16 increase_val); void DropItem(int16 slot_id, bool recurse = true); diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index fee64d493..66fb2f47d 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -2229,6 +2229,31 @@ void Lua_Client::UntrainDiscBySpellID(uint16 spell_id, bool update_client) { self->UntrainDiscBySpellID(spell_id, update_client); } +void Lua_Client::SummonBaggedItems(uint32 bag_item_id, luabind::adl::object bag_items_table) { + Lua_Safe_Call_Void(); + if (luabind::type(bag_items_table) != LUA_TTABLE) { + return; + } + + std::vector bagged_items; + + luabind::raw_iterator end; // raw_iterator uses lua_rawget + for (luabind::raw_iterator it(bag_items_table); it != end; ++it) + { + // verify array element is a table for item details + if (luabind::type(*it) == LUA_TTABLE) + { + // no need to try/catch, quest lua parser already catches exceptions + ServerLootItem_Struct item{}; + item.item_id = luabind::object_cast((*it)["item_id"]); + item.charges = luabind::object_cast((*it)["charges"]); + bagged_items.emplace_back(item); + } + } + + self->SummonBaggedItems(bag_item_id, bagged_items); +} + luabind::scope lua_register_client() { return luabind::class_("Client") .def(luabind::constructor<>()) @@ -2604,7 +2629,8 @@ luabind::scope lua_register_client() { .def("RemoveItem", (void(Lua_Client::*)(uint32,uint32))&Lua_Client::RemoveItem) .def("SetGMStatus", (void(Lua_Client::*)(int32))&Lua_Client::SetGMStatus) .def("UntrainDiscBySpellID", (void(Lua_Client::*)(uint16))&Lua_Client::UntrainDiscBySpellID) - .def("UntrainDiscBySpellID", (void(Lua_Client::*)(uint16,bool))&Lua_Client::UntrainDiscBySpellID); + .def("UntrainDiscBySpellID", (void(Lua_Client::*)(uint16,bool))&Lua_Client::UntrainDiscBySpellID) + .def("SummonBaggedItems", (void(Lua_Client::*)(uint32,luabind::adl::object))&Lua_Client::SummonBaggedItems); } luabind::scope lua_register_inventory_where() { diff --git a/zone/lua_client.h b/zone/lua_client.h index b49cb9b31..0e1f1be9e 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -215,6 +215,7 @@ public: bool attuned); void SummonItem(uint32 item_id, int charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5, bool attuned, int to_slot); + void SummonBaggedItems(uint32 bag_item_id, luabind::adl::object bag_items_table); void SetStats(int type, int value); void IncStats(int type, int value); void DropItem(int slot_id); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 9c810c7ef..81c20917e 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -5712,6 +5712,57 @@ XS(XS_Client_UntrainDiscBySpellID) { XSRETURN_EMPTY; } +XS(XS_Client_SummonBaggedItems); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SummonBaggedItems) { + dXSARGS; + if (items != 3) { + Perl_croak(aTHX_ "Usage: Client::SummonBaggedItems(THIS, uint32 bag_item_id, ARRAYREF bag_items_array)"); // @categories Inventory and Items, Script Utility + } + + Client* THIS; + VALIDATE_THIS_IS_CLIENT; + + uint32 bag_item_id = (uint32) SvUV(ST(1)); + + // verify we're receiving a reference to an array type + SV* bag_items_avref = ST(2); + if (!bag_items_avref || !SvROK(bag_items_avref) || SvTYPE(SvRV(bag_items_avref)) != SVt_PVAV) + { + Perl_croak(aTHX_ "Client::SummonBaggedItems second argument is not a reference to an array"); + } + + // dereference into the array + AV* bag_items_av = (AV*)SvRV(bag_items_avref); + + std::vector bagged_items; + + auto count = av_len(bag_items_av) + 1; + for (int i = 0; i < count; ++i) + { + SV** element = av_fetch(bag_items_av, i, 0); + + // verify array element is a hash reference containing item details + if (element && SvROK(*element) && SvTYPE(SvRV(*element)) == SVt_PVHV) + { + HV* hash = (HV*)SvRV(*element); // dereference + + SV** item_id_ptr = hv_fetchs(hash, "item_id", false); + SV** item_charges_ptr = hv_fetchs(hash, "charges", false); + if (item_id_ptr && item_charges_ptr) + { + ServerLootItem_Struct item{}; + item.item_id = static_cast(SvUV(*item_id_ptr)); + item.charges = static_cast(SvIV(*item_charges_ptr)); + bagged_items.emplace_back(item); + } + } + } + + THIS->SummonBaggedItems(bag_item_id, bagged_items); + + XSRETURN_EMPTY; +} + #ifdef __cplusplus extern "C" #endif @@ -6001,6 +6052,7 @@ XS(boot_Client) { newXSproto(strcpy(buf, "Sit"), XS_Client_Sit, file, "$"); newXSproto(strcpy(buf, "SlotConvert2"), XS_Client_SlotConvert2, file, "$$"); newXSproto(strcpy(buf, "Stand"), XS_Client_Stand, file, "$"); + newXSproto(strcpy(buf, "SummonBaggedItems"), XS_Client_SummonBaggedItems, file, "$$$"); newXSproto(strcpy(buf, "SummonItem"), XS_Client_SummonItem, file, "$$;$$$$$$$$"); newXSproto(strcpy(buf, "TakeMoneyFromPP"), XS_Client_TakeMoneyFromPP, file, "$$;$"); newXSproto(strcpy(buf, "TGB"), XS_Client_TGB, file, "$"); From 8c5f26ca5edfdc2b443700195268f6834bada387 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 2 Oct 2021 12:01:39 -0400 Subject: [PATCH 240/624] [Quest API] Add IsNPCSpawned(npc_ids) and CountSpawnedNPCs(npc_ids) to Perl/Lua. (#1570) - Add quest::isnpcspawned(npc_ids) to Perl. - Add quest::countspawnednpcs(npc_ids) to Perl. - Add eq.is_npc_spawned(npc_ids) to Lua. - Add eq.count_spawned_npcs(npc_ids) to Lua. --- zone/embparser_api.cpp | 39 ++++++++++++++++++++++++++ zone/entity.cpp | 28 +++++++++++++++++++ zone/entity.h | 2 ++ zone/lua_general.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 131 insertions(+) diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 6796fc0a6..164021bf6 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -7623,6 +7623,43 @@ XS(XS__worldwideaddldonwin) { XSRETURN_EMPTY; } +XS(XS__isnpcspawned); +XS(XS__isnpcspawned) { + dXSARGS; + if (items < 1) + Perl_croak(aTHX_ "Usage: quest::isnpcspawned(npc_id_one, npc_id_two, npc_idthree, npc_id_four, npc_id_five...[no limit])"); + { + std::vector npc_ids; + bool is_spawned = false; + for (int index = 0; index < items; index++) { + npc_ids.push_back((uint32)SvUV(ST(index))); + } + is_spawned = entity_list.IsNPCSpawned(npc_ids); + ST(0) = boolSV(is_spawned); + sv_2mortal(ST(0)); + XSRETURN(1); + } +} + +XS(XS__countspawnednpcs); +XS(XS__countspawnednpcs) { + dXSARGS; + if (items < 1) + Perl_croak(aTHX_ "Usage: quest::countspawnednpcs(npc_id_one, npc_id_two, npc_idthree, npc_id_four, npc_id_five...[no limit])"); + { + dXSTARG; + std::vector npc_ids; + uint32 npc_count = 0; + for (int index = 0; index < items; index++) { + npc_ids.push_back((uint32)SvUV(ST(index))); + } + npc_count = entity_list.CountSpawnedNPCs(npc_ids); + XSprePUSH; + PUSHu((UV)npc_count); + XSRETURN(1); + } +} + /* This is the callback perl will look for to setup the quest package's XSUBs @@ -7718,6 +7755,7 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "collectitems"), XS__collectitems, file); newXS(strcpy(buf, "completedtasksinset"), XS__completedtasksinset, file); newXS(strcpy(buf, "countitem"), XS__countitem, file); + newXS(strcpy(buf, "countspawnednpcs"), XS__countspawnednpcs, file); newXS(strcpy(buf, "createdoor"), XS__CreateDoor, file); newXS(strcpy(buf, "creategroundobject"), XS__CreateGroundObject, file); newXS(strcpy(buf, "creategroundobjectfrommodel"), XS__CreateGroundObjectFromModel, file); @@ -7926,6 +7964,7 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "incstat"), XS__incstat, file); newXS(strcpy(buf, "isdisctome"), XS__isdisctome, file); newXS(strcpy(buf, "isdooropen"), XS__isdooropen, file); + newXS(strcpy(buf, "isnpcspawned"), XS__isnpcspawned, file); newXS(strcpy(buf, "istaskactive"), XS__istaskactive, file); newXS(strcpy(buf, "istaskactivityactive"), XS__istaskactivityactive, file); newXS(strcpy(buf, "istaskappropriate"), XS__istaskappropriate, file); diff --git a/zone/entity.cpp b/zone/entity.cpp index 47e03afba..b506ca01a 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1201,6 +1201,34 @@ bool EntityList::IsMobSpawnedByNpcTypeID(uint32 get_id) return false; } +bool EntityList::IsNPCSpawned(std::vector npc_ids) +{ + return CountSpawnedNPCs(npc_ids) != 0; +} + +uint32 EntityList::CountSpawnedNPCs(std::vector npc_ids) +{ + uint32 npc_count = 0; + if (npc_list.empty() || npc_ids.empty()) { + return npc_count; + } + + for (auto current_npc : npc_list) { + if ( + std::find( + npc_ids.begin(), + npc_ids.end(), + current_npc.second->GetNPCTypeID() + ) != npc_ids.end() && + current_npc.second->GetID() != 0 + ) { + npc_count++; + } + } + + return npc_count; +} + Object *EntityList::GetObjectByDBID(uint32 id) { if (id == 0 || object_list.empty()) diff --git a/zone/entity.h b/zone/entity.h index 1690799a1..2890c18f5 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -158,6 +158,8 @@ public: Mob *GetMob(const char* name); Mob *GetMobByNpcTypeID(uint32 get_id); bool IsMobSpawnedByNpcTypeID(uint32 get_id); + bool IsNPCSpawned(std::vector npc_ids); + uint32 CountSpawnedNPCs(std::vector npc_ids); Mob *GetTargetForVirus(Mob* spreader, int range); inline NPC *GetNPCByID(uint16 id) { diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 3338bea5c..404a074ef 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -3121,6 +3121,66 @@ void lua_world_wide_update_activity(uint32 task_id, int activity_id, int activit quest_manager.WorldWideTaskUpdate(update_type, task_id, activity_id, activity_count, enforce_level_requirement, min_status, max_status); } +bool lua_is_npc_spawned(luabind::adl::object table) { + if(luabind::type(table) != LUA_TTABLE) { + return false; + } + + std::vector npc_ids; + int index = 1; + while (luabind::type(table[index]) != LUA_TNIL) { + auto current_id = table[index]; + uint32 npc_id = 0; + if(luabind::type(current_id) != LUA_TNIL) { + try { + npc_id = luabind::object_cast(current_id); + } catch(luabind::cast_failed &) { + } + } else { + break; + } + + npc_ids.push_back(npc_id); + ++index; + } + + if (npc_ids.empty()) { + return false; + } + + return entity_list.IsNPCSpawned(npc_ids); +} + +uint32 lua_count_spawned_npcs(luabind::adl::object table) { + if(luabind::type(table) != LUA_TTABLE) { + return 0; + } + + std::vector npc_ids; + int index = 1; + while (luabind::type(table[index]) != LUA_TNIL) { + auto current_id = table[index]; + uint32 npc_id = 0; + if(luabind::type(current_id) != LUA_TNIL) { + try { + npc_id = luabind::object_cast(current_id); + } catch(luabind::cast_failed &) { + } + } else { + break; + } + + npc_ids.push_back(npc_id); + ++index; + } + + if (npc_ids.empty()) { + return 0; + } + + return entity_list.CountSpawnedNPCs(npc_ids); +} + #define LuaCreateNPCParse(name, c_type, default_value) do { \ cur = table[#name]; \ if(luabind::type(cur) != LUA_TNIL) { \ @@ -3560,6 +3620,8 @@ luabind::scope lua_register_general() { luabind::def("get_item_stat", &lua_get_item_stat), luabind::def("get_spell_stat", (int(*)(uint32,std::string))&lua_get_spell_stat), luabind::def("get_spell_stat", (int(*)(uint32,std::string,uint8))&lua_get_spell_stat), + luabind::def("is_npc_spawned", &lua_is_npc_spawned), + luabind::def("count_spawned_npcs", &lua_count_spawned_npcs), /* Cross Zone From ff46a854f9d66079145a9d5299857cb37d1d03d3 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 2 Oct 2021 12:01:54 -0400 Subject: [PATCH 241/624] [Quest API] Add LavaDamage and MinLavaDamage to UpdateZoneHeader in Perl/Lua. (#1578) Allows operators to modify lava damage dynamically. --- zone/questmgr.cpp | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index df3d846e3..350a8a6ef 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -3326,12 +3326,11 @@ void QuestManager::UpdateZoneHeader(std::string type, std::string value) { for (int i = 0; i < 4; i++) { zone->newzone_data.fog_maxclip[i] = atof(value.c_str()); } - } - else if (strcasecmp(type.c_str(), "gravity") == 0) + } else if (strcasecmp(type.c_str(), "gravity") == 0) { zone->newzone_data.gravity = atof(value.c_str()); - else if (strcasecmp(type.c_str(), "time_type") == 0) + } else if (strcasecmp(type.c_str(), "time_type") == 0) { zone->newzone_data.time_type = atoi(value.c_str()); - else if (strcasecmp(type.c_str(), "rain_chance") == 0) { + } else if (strcasecmp(type.c_str(), "rain_chance") == 0) { for (int i = 0; i < 4; i++) { zone->newzone_data.rain_chance[i] = atoi(value.c_str()); } @@ -3347,27 +3346,31 @@ void QuestManager::UpdateZoneHeader(std::string type, std::string value) { for (int i = 0; i < 4; i++) { zone->newzone_data.snow_duration[i] = atoi(value.c_str()); } - } - else if (strcasecmp(type.c_str(), "sky") == 0) + } else if (strcasecmp(type.c_str(), "sky") == 0) { zone->newzone_data.sky = atoi(value.c_str()); - else if (strcasecmp(type.c_str(), "safe_x") == 0) + } else if (strcasecmp(type.c_str(), "safe_x") == 0) { zone->newzone_data.safe_x = atof(value.c_str()); - else if (strcasecmp(type.c_str(), "safe_y") == 0) + } else if (strcasecmp(type.c_str(), "safe_y") == 0) { zone->newzone_data.safe_y = atof(value.c_str()); - else if (strcasecmp(type.c_str(), "safe_z") == 0) + } else if (strcasecmp(type.c_str(), "safe_z") == 0) { zone->newzone_data.safe_z = atof(value.c_str()); - else if (strcasecmp(type.c_str(), "max_z") == 0) + } else if (strcasecmp(type.c_str(), "max_z") == 0) { zone->newzone_data.max_z = atof(value.c_str()); - else if (strcasecmp(type.c_str(), "underworld") == 0) + } else if (strcasecmp(type.c_str(), "underworld") == 0) { zone->newzone_data.underworld = atof(value.c_str()); - else if (strcasecmp(type.c_str(), "minclip") == 0) + } else if (strcasecmp(type.c_str(), "minclip") == 0) { zone->newzone_data.minclip = atof(value.c_str()); - else if (strcasecmp(type.c_str(), "maxclip") == 0) + } else if (strcasecmp(type.c_str(), "maxclip") == 0) { zone->newzone_data.maxclip = atof(value.c_str()); - else if (strcasecmp(type.c_str(), "fog_density") == 0) + } else if (strcasecmp(type.c_str(), "fog_density") == 0) { zone->newzone_data.fog_density = atof(value.c_str()); - else if (strcasecmp(type.c_str(), "suspendbuffs") == 0) + } else if (strcasecmp(type.c_str(), "suspendbuffs") == 0) { zone->newzone_data.SuspendBuffs = atoi(value.c_str()); + } else if (strcasecmp(type.c_str(), "lavadamage") == 0) { + zone->newzone_data.LavaDamage = atoi(value.c_str()); + } else if (strcasecmp(type.c_str(), "minlavadamage") == 0) { + zone->newzone_data.MinLavaDamage = atoi(value.c_str()); + } auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); From 93acf50bb4ff2f9c72d82ca7d64c85bd0e8db81f Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 2 Oct 2021 13:09:30 -0400 Subject: [PATCH 242/624] [Quest API] Add client->ReadBookByName(book_name, book_type) to Perl/Lua. - Add $client->ReadBookByName(booK_name, book_type) to Perl. - Add client:ReadBookByName(booK_name, book_type) to Lua. - Allows server operators to put books in to their database and read from their database instead of storing the values in a script, also allows them to read pre-existing books using a script. --- zone/client.cpp | 27 +++++++++++++++++++++++++++ zone/client.h | 1 + zone/lua_client.cpp | 6 ++++++ zone/lua_client.h | 1 + zone/perl_client.cpp | 16 ++++++++++++++++ 5 files changed, 51 insertions(+) diff --git a/zone/client.cpp b/zone/client.cpp index cc8b9f8cb..49b9090da 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -10530,6 +10530,33 @@ void Client::SetDoorToolEntityId(uint16 door_tool_entity_id) Client::m_door_tool_entity_id = door_tool_entity_id; } +void Client::ReadBookByName(std::string book_name, uint8 book_type) +{ + int16 book_language = 0; + std::string book_text = content_db.GetBook(book_name.c_str(), &book_language); + int length = book_text.length(); + + if (book_text[0] != '\0') { + LogDebug("Client::ReadBookByName() Book Name: [{}] Text: [{}]", book_name, book_text.c_str()); + auto outapp = new EQApplicationPacket(OP_ReadBook, length + sizeof(BookText_Struct)); + BookText_Struct *out = (BookText_Struct *) outapp->pBuffer; + out->window = 0xFF; + out->type = book_type; + out->invslot = 0; + + memcpy(out->booktext, book_text.c_str(), length); + + if (book_language > 0 && book_language < MAX_PP_LANGUAGE) { + if (m_pp.languages[book_language] < 100) { + GarbleMessage(out->booktext, (100 - m_pp.languages[book_language])); + } + } + + QueuePacket(outapp); + safe_delete(outapp); + } +} + // this will fetch raid clients if exists // fallback to group if raid doesn't exist // fallback to self if group doesn't exist diff --git a/zone/client.h b/zone/client.h index 952c52fca..34249cbbe 100644 --- a/zone/client.h +++ b/zone/client.h @@ -728,6 +728,7 @@ public: void Stun(int duration); void UnStun(); void ReadBook(BookRequest_Struct *book); + void ReadBookByName(std::string book_name, uint8 book_type); void QuestReadBook(const char* text, uint8 type); void SendClientMoneyUpdate(uint8 type,uint32 amount); void SendMoneyUpdate(); diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 66fb2f47d..a44a3f1f7 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -2229,6 +2229,11 @@ void Lua_Client::UntrainDiscBySpellID(uint16 spell_id, bool update_client) { self->UntrainDiscBySpellID(spell_id, update_client); } +void Lua_Client::ReadBookByName(std::string book_name, uint8 book_type) { + Lua_Safe_Call_Void(); + self->ReadBookByName(book_name, book_type); +} + void Lua_Client::SummonBaggedItems(uint32 bag_item_id, luabind::adl::object bag_items_table) { Lua_Safe_Call_Void(); if (luabind::type(bag_items_table) != LUA_TTABLE) { @@ -2627,6 +2632,7 @@ luabind::scope lua_register_client() { .def("CountItem", (int(Lua_Client::*)(uint32))&Lua_Client::CountItem) .def("RemoveItem", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveItem) .def("RemoveItem", (void(Lua_Client::*)(uint32,uint32))&Lua_Client::RemoveItem) + .def("ReadBookByName", (void(Lua_Client::*)(std::string,uint8))&Lua_Client::ReadBookByName) .def("SetGMStatus", (void(Lua_Client::*)(int32))&Lua_Client::SetGMStatus) .def("UntrainDiscBySpellID", (void(Lua_Client::*)(uint16))&Lua_Client::UntrainDiscBySpellID) .def("UntrainDiscBySpellID", (void(Lua_Client::*)(uint16,bool))&Lua_Client::UntrainDiscBySpellID) diff --git a/zone/lua_client.h b/zone/lua_client.h index 0e1f1be9e..9efb6c00b 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -273,6 +273,7 @@ public: uint32 GetRadiantCrystals(); uint32 GetEbonCrystals(); void QuestReadBook(const char *text, int type); + void ReadBookByName(std::string book_name, uint8 book_type); void UpdateGroupAAs(int points, uint32 type); uint32 GetGroupPoints(); uint32 GetRaidPoints(); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 81c20917e..be530ff5f 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -5693,6 +5693,21 @@ XS(XS_Client_DiaWind) { XSRETURN_EMPTY; } +XS(XS_Client_ReadBookByName); +XS(XS_Client_ReadBookByName) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::ReadBookByName(THIS, string book_name, uint8 book_type)"); // @categories Script Utility + { + Client *THIS; + std::string book_name(SvPV_nolen(ST(1))); + uint8 book_type = (uint8) SvUV(ST(2)); + VALIDATE_THIS_IS_CLIENT; + THIS->ReadBookByName(book_name, book_type); + } + XSRETURN_EMPTY; +} + XS(XS_Client_UntrainDiscBySpellID); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_UntrainDiscBySpellID) { dXSARGS; @@ -6078,6 +6093,7 @@ XS(boot_Client) { newXSproto(strcpy(buf, "UpdateWho"), XS_Client_UpdateWho, file, "$;$"); newXSproto(strcpy(buf, "UseDiscipline"), XS_Client_UseDiscipline, file, "$$$"); newXSproto(strcpy(buf, "WorldKick"), XS_Client_WorldKick, file, "$"); + newXSproto(strcpy(buf, "ReadBookByName"), XS_Client_ReadBookByName, file, "$$$"); XSRETURN_YES; } From b3e9e2099a1515f9d7992d98e4619be45031c22d Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 2 Oct 2021 13:39:32 -0400 Subject: [PATCH 243/624] [Quest API] Add GetIPExemption(), GetIPString(), and SetIPExemption(exemption_amount) to Perl/Lua. - Add $client->GetIPExemption() to Perl. - Add $client->GetIPString() to Perl. - Add $client->SetIPExemption(exemption_amount) to Perl. - Add client:GetIPExemption() to Lua. - Add client:GetIPString() to Lua. - Add client:SetIPExemption(exemption_amount) to Lua. Will make plugin::IP unnecessary and allow people to get readable IP string easier, as well as set/get IP exemptions from Perl and Lua. --- common/database.cpp | 29 ++++++++++++++++++++++++ common/database.h | 3 ++- zone/client.cpp | 17 +++++++++++++++ zone/client.h | 3 +++ zone/lua_client.cpp | 18 +++++++++++++++ zone/lua_client.h | 3 +++ zone/perl_client.cpp | 52 ++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 124 insertions(+), 1 deletion(-) diff --git a/common/database.cpp b/common/database.cpp index 0a5678b2c..e694e7801 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -2271,6 +2271,35 @@ int Database::GetIPExemption(std::string account_ip) { return RuleI(World, MaxClientsPerIP); } +void Database::SetIPExemption(std::string account_ip, int exemption_amount) { + std::string query = fmt::format( + "SELECT `exemption_id` FROM `ip_exemptions` WHERE `exemption_ip` = '{}'", + account_ip + ); + + auto results = QueryDatabase(query); + uint32 exemption_id = 0; + if (results.Success() && results.RowCount() > 0) { + auto row = results.begin(); + exemption_id = atoi(row[0]); + } + + query = fmt::format( + "INSERT INTO `ip_exemptions` (`exemption_ip`, `exemption_amount`) VALUES ('{}', {})", + account_ip, + exemption_amount + ); + + if (exemption_id != 0) { + query = fmt::format( + "UPDATE `ip_exemptions` SET `exemption_amount` = {} WHERE `exemption_ip` = '{}'", + exemption_amount, + account_ip + ); + } + QueryDatabase(query); +} + int Database::GetInstanceID(uint32 char_id, uint32 zone_id) { std::string query = StringFormat("SELECT instance_list.id FROM instance_list INNER JOIN instance_list_player ON instance_list.id = instance_list_player.id WHERE instance_list.zone = '%i' AND instance_list_player.charid = '%i'", zone_id, char_id); auto results = QueryDatabase(query); diff --git a/common/database.h b/common/database.h index e0eff4e8e..16506dfae 100644 --- a/common/database.h +++ b/common/database.h @@ -198,7 +198,8 @@ public: void GetAccountFromID(uint32 id, char* oAccountName, int16* oStatus); void SetAgreementFlag(uint32 acctid); - int GetIPExemption(std::string account_ip); + int GetIPExemption(std::string account_ip); + void SetIPExemption(std::string account_ip, int exemption_amount); int GetInstanceID(uint32 char_id, uint32 zone_id); diff --git a/zone/client.cpp b/zone/client.cpp index 49b9090da..0470713cf 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -10530,6 +10530,23 @@ void Client::SetDoorToolEntityId(uint16 door_tool_entity_id) Client::m_door_tool_entity_id = door_tool_entity_id; } +int Client::GetIPExemption() +{ + return database.GetIPExemption(GetIPString()); +} + +std::string Client::GetIPString() +{ + in_addr client_ip{}; + client_ip.s_addr = GetIP(); + return inet_ntoa(client_ip); +} + +void Client::SetIPExemption(int exemption_amount) +{ + database.SetIPExemption(GetIPString(), exemption_amount); +} + void Client::ReadBookByName(std::string book_name, uint8 book_type) { int16 book_language = 0; diff --git a/zone/client.h b/zone/client.h index 34249cbbe..da77e2b25 100644 --- a/zone/client.h +++ b/zone/client.h @@ -340,6 +340,9 @@ public: bool GetRevoked() const { return revoked; } void SetRevoked(bool rev) { revoked = rev; } inline uint32 GetIP() const { return ip; } + std::string GetIPString(); + int GetIPExemption(); + void SetIPExemption(int exemption_amount); inline bool GetHideMe() const { return gm_hide_me; } void SetHideMe(bool hm); inline uint16 GetPort() const { return port; } diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index a44a3f1f7..3161aa0c3 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -2229,6 +2229,21 @@ void Lua_Client::UntrainDiscBySpellID(uint16 spell_id, bool update_client) { self->UntrainDiscBySpellID(spell_id, update_client); } +int Lua_Client::GetIPExemption() { + Lua_Safe_Call_Int(); + return self->GetIPExemption(); +} + +std::string Lua_Client::GetIPString() { + Lua_Safe_Call_String(); + return self->GetIPString(); +} + +void Lua_Client::SetIPExemption(int exemption_amount) { + Lua_Safe_Call_Void(); + self->SetIPExemption(exemption_amount); +} + void Lua_Client::ReadBookByName(std::string book_name, uint8 book_type) { Lua_Safe_Call_Void(); self->ReadBookByName(book_name, book_type); @@ -2632,6 +2647,9 @@ luabind::scope lua_register_client() { .def("CountItem", (int(Lua_Client::*)(uint32))&Lua_Client::CountItem) .def("RemoveItem", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveItem) .def("RemoveItem", (void(Lua_Client::*)(uint32,uint32))&Lua_Client::RemoveItem) + .def("GetIPExemption", (int(Lua_Client::*)(void))&Lua_Client::GetIPExemption) + .def("GetIPString", (std::string(Lua_Client::*)(void))&Lua_Client::GetIPString) + .def("SetIPExemption", (void(Lua_Client::*)(int))&Lua_Client::SetIPExemption) .def("ReadBookByName", (void(Lua_Client::*)(std::string,uint8))&Lua_Client::ReadBookByName) .def("SetGMStatus", (void(Lua_Client::*)(int32))&Lua_Client::SetGMStatus) .def("UntrainDiscBySpellID", (void(Lua_Client::*)(uint16))&Lua_Client::UntrainDiscBySpellID) diff --git a/zone/lua_client.h b/zone/lua_client.h index 9efb6c00b..add5089b7 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -284,6 +284,9 @@ public: void SetEndurance(int endur); void SendOPTranslocateConfirm(Lua_Mob caster, int spell_id); uint32 GetIP(); + std::string GetIPString(); + int GetIPExemption(); + void SetIPExemption(int exemption_amount); void AddLevelBasedExp(int exp_pct); void AddLevelBasedExp(int exp_pct, int max_level); void AddLevelBasedExp(int exp_pct, int max_level, bool ignore_mods); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index be530ff5f..3e2018f72 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -5693,6 +5693,55 @@ XS(XS_Client_DiaWind) { XSRETURN_EMPTY; } +XS(XS_Client_GetIPExemption); +XS(XS_Client_GetIPExemption) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetIPExemption(THIS)"); // @categories Account and Character + { + Client* THIS; + int exemption_amount = 0; + dXSTARG; + VALIDATE_THIS_IS_CLIENT; + exemption_amount = THIS->GetIPExemption(); + XSprePUSH; + PUSHi((IV) exemption_amount); + } + XSRETURN(1); +} + +XS(XS_Client_GetIPString); +XS(XS_Client_GetIPString) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetIPString(THIS)"); // @categories Account and Character + { + Client *THIS; + dXSTARG; + VALIDATE_THIS_IS_CLIENT; + std::string ip_string = THIS->GetIPString(); + sv_setpv(TARG, ip_string.c_str()); + XSprePUSH; + PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_Client_SetIPExemption); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetIPExemption) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::SetIPExemption(THIS, int exemption_amount)"); // @categories Account and Character + { + Client *THIS; + int exemption_amount = (int) SvIV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_CLIENT; + THIS->SetIPExemption(exemption_amount); + } + XSRETURN_EMPTY; +} + XS(XS_Client_ReadBookByName); XS(XS_Client_ReadBookByName) { dXSARGS; @@ -5905,6 +5954,8 @@ XS(boot_Client) { newXSproto(strcpy(buf, "GetInstrumentMod"), XS_Client_GetInstrumentMod, file, "$$"); newXSproto(strcpy(buf, "GetInventory"), XS_Client_GetInventory, file, "$"); newXSproto(strcpy(buf, "GetIP"), XS_Client_GetIP, file, "$"); + newXSproto(strcpy(buf, "GetIPExemption"), XS_Client_GetIPExemption, file, "$"); + newXSproto(strcpy(buf, "GetIPString"), XS_Client_GetIPString, file, "$"); newXSproto(strcpy(buf, "GetItemAt"), XS_Client_GetItemAt, file, "$$"); newXSproto(strcpy(buf, "GetItemIDAt"), XS_Client_GetItemIDAt, file, "$$"); newXSproto(strcpy(buf, "GetItemInInventory"), XS_Client_GetItemInInventory, file, "$$"); @@ -6049,6 +6100,7 @@ XS(boot_Client) { newXSproto(strcpy(buf, "SetHideMe"), XS_Client_SetHideMe, file, "$$"); newXSproto(strcpy(buf, "SetHorseId"), XS_Client_SetHorseId, file, "$$"); newXSproto(strcpy(buf, "SetHunger"), XS_Client_SetHunger, file, "$$"); + newXSproto(strcpy(buf, "SetIPExemption"), XS_Client_SetIPExemption, file, "$$"); newXSproto(strcpy(buf, "SetLanguageSkill"), XS_Client_SetLanguageSkill, file, "$$$"); newXSproto(strcpy(buf, "SetMaterial"), XS_Client_SetMaterial, file, "$$$"); newXSproto(strcpy(buf, "SetPrimaryWeaponOrnamentation"), XS_Client_SetPrimaryWeaponOrnamentation, file, "$$"); From 5720a5020d6a3873240ae5219d130fce2dbb3d73 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 2 Oct 2021 19:35:35 -0400 Subject: [PATCH 244/624] [Quest API] Add attuned/augment support to client->SummonBaggedItems() in Perl/Lua. (#1580) Perl Example: ```pl my @bag_items = ( { item_id => 33649, charges => 1, attuned => 1, augment_one => 32940 } ); ``` Lua Example: ```lua local bag_items = { { item_id = 33649, charges = 1, attuned = 1, augment_one = 32940 } } --- zone/client.cpp | 12 +++++++++++- zone/lua_client.cpp | 7 +++++++ zone/perl_client.cpp | 26 ++++++++++++++++++-------- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 0470713cf..215ba2480 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -10660,7 +10660,17 @@ void Client::SummonBaggedItems(uint32 bag_item_id, const std::vectorPutItem(open_slot, *summoned_bag_item); diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 3161aa0c3..eb9bef15d 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -2267,6 +2267,13 @@ void Lua_Client::SummonBaggedItems(uint32 bag_item_id, luabind::adl::object bag_ ServerLootItem_Struct item{}; item.item_id = luabind::object_cast((*it)["item_id"]); item.charges = luabind::object_cast((*it)["charges"]); + item.attuned = luabind::type((*it)["attuned"]) != LUA_TNIL ? luabind::object_cast((*it)["attuned"]) : 0; + item.aug_1 = luabind::type((*it)["augment_one"]) != LUA_TNIL ? luabind::object_cast((*it)["augment_one"]) : 0; + item.aug_2 = luabind::type((*it)["augment_two"]) != LUA_TNIL ? luabind::object_cast((*it)["augment_two"]) : 0; + item.aug_3 = luabind::type((*it)["augment_three"]) != LUA_TNIL ? luabind::object_cast((*it)["augment_three"]) : 0; + item.aug_4 = luabind::type((*it)["augment_four"]) != LUA_TNIL ? luabind::object_cast((*it)["augment_four"]) : 0; + item.aug_5 = luabind::type((*it)["augment_five"]) != LUA_TNIL ? luabind::object_cast((*it)["augment_five"]) : 0; + item.aug_6 = luabind::type((*it)["augment_six"]) != LUA_TNIL ? luabind::object_cast((*it)["augment_six"]) : 0; bagged_items.emplace_back(item); } } diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 3e2018f72..de16ec1dd 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -5790,8 +5790,7 @@ XS(XS_Client_SummonBaggedItems) { // verify we're receiving a reference to an array type SV* bag_items_avref = ST(2); - if (!bag_items_avref || !SvROK(bag_items_avref) || SvTYPE(SvRV(bag_items_avref)) != SVt_PVAV) - { + if (!bag_items_avref || !SvROK(bag_items_avref) || SvTYPE(SvRV(bag_items_avref)) != SVt_PVAV) { Perl_croak(aTHX_ "Client::SummonBaggedItems second argument is not a reference to an array"); } @@ -5801,22 +5800,33 @@ XS(XS_Client_SummonBaggedItems) { std::vector bagged_items; auto count = av_len(bag_items_av) + 1; - for (int i = 0; i < count; ++i) - { + for (int i = 0; i < count; ++i) { SV** element = av_fetch(bag_items_av, i, 0); // verify array element is a hash reference containing item details - if (element && SvROK(*element) && SvTYPE(SvRV(*element)) == SVt_PVHV) - { + if (element && SvROK(*element) && SvTYPE(SvRV(*element)) == SVt_PVHV) { HV* hash = (HV*)SvRV(*element); // dereference SV** item_id_ptr = hv_fetchs(hash, "item_id", false); SV** item_charges_ptr = hv_fetchs(hash, "charges", false); - if (item_id_ptr && item_charges_ptr) - { + SV** attuned_ptr = hv_fetchs(hash, "attuned", false); + SV** augment_one_ptr = hv_fetchs(hash, "augment_one", false); + SV** augment_two_ptr = hv_fetchs(hash, "augment_two", false); + SV** augment_three_ptr = hv_fetchs(hash, "augment_three", false); + SV** augment_four_ptr = hv_fetchs(hash, "augment_four", false); + SV** augment_five_ptr = hv_fetchs(hash, "augment_five", false); + SV** augment_six_ptr = hv_fetchs(hash, "augment_six", false); + if (item_id_ptr && item_charges_ptr) { ServerLootItem_Struct item{}; item.item_id = static_cast(SvUV(*item_id_ptr)); item.charges = static_cast(SvIV(*item_charges_ptr)); + item.attuned = attuned_ptr ? static_cast(SvUV(*attuned_ptr)) : 0; + item.aug_1 = augment_one_ptr ? static_cast(SvUV(*augment_one_ptr)) : 0; + item.aug_2 = augment_two_ptr ? static_cast(SvUV(*augment_two_ptr)) : 0; + item.aug_3 = augment_three_ptr ? static_cast(SvUV(*augment_three_ptr)) : 0; + item.aug_4 = augment_four_ptr ? static_cast(SvUV(*augment_four_ptr)) : 0; + item.aug_5 = augment_five_ptr ? static_cast(SvUV(*augment_five_ptr)) : 0; + item.aug_6 = augment_six_ptr ? static_cast(SvUV(*augment_six_ptr)) : 0; bagged_items.emplace_back(item); } } From 3a76d9a28e83b7571261348a7342276d0e52f3f0 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 3 Oct 2021 13:06:31 -0400 Subject: [PATCH 245/624] [Bug Fix] Dialogue Window Name replace. (#1581) {name} was being replaced with the string "$name" because Perl would parse it properly, but when used here it doesn't return the client's name. --- zone/dialogue_window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/dialogue_window.cpp b/zone/dialogue_window.cpp index f9a42d26c..38c530494 100644 --- a/zone/dialogue_window.cpp +++ b/zone/dialogue_window.cpp @@ -26,7 +26,7 @@ void DialogueWindow::Render(Client *c, std::string markdown) find_replace(output, "{gray}", ""); find_replace(output, "{tan}", ""); find_replace(output, "{bullet}", "•"); - find_replace(output, "{name}", "$name"); + find_replace(output, "{name}", fmt::format("{}", c->GetCleanName())); find_replace(output, "{linebreak}", "--------------------------------------------------------------------"); find_replace(output, "{rowpad}", R"({tdpad}<"td>{tdpad}<"td><"tr>)"); find_replace(output, "{tdpad}", "----------------------"); From ccab07bd662742b32584d8dfa19157f0c4f19291 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 3 Oct 2021 13:06:39 -0400 Subject: [PATCH 246/624] [Dialogue] Add hidden response support. (#1583) Allows operators to hide responses. Syntax is `hiddenresponse`. --- zone/dialogue_window.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/zone/dialogue_window.cpp b/zone/dialogue_window.cpp index 38c530494..b9ce924d0 100644 --- a/zone/dialogue_window.cpp +++ b/zone/dialogue_window.cpp @@ -56,6 +56,13 @@ void DialogueWindow::Render(Client *c, std::string markdown) find_replace(output, "nobracket", ""); } + bool render_hiddenresponse = false; + if (markdown.find("hiddenresponse") != std::string::npos) { + render_hiddenresponse = true; + LogDiaWind("Client [{}] Rendering hiddenresponse", c->GetCleanName()); + find_replace(output, "hiddenresponse", ""); + } + // animations std::string animation = get_between(output, "+", "+"); if (!animation.empty()) { @@ -504,6 +511,9 @@ void DialogueWindow::Render(Client *c, std::string markdown) // build the final output string std::string final_output; final_output = fmt::format("{}{}{}

{}", quote_string, output, quote_string, click_response); + if (render_hiddenresponse) { + final_output = fmt::format("{}{}{}", quote_string, output, quote_string); + } // send popup c->SendFullPopup( From 64b8d7c8746a8e748f8ab1c8166b2945abf292cc Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Sun, 3 Oct 2021 13:25:49 -0400 Subject: [PATCH 247/624] Remove unnecessary includes (#1585) The include order here was causing a compile error when building with perl 5.12 due to a bad interaction with the older fmt submodule version being used --- zone/doors.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/zone/doors.h b/zone/doors.h index 009041805..a0d9c6364 100644 --- a/zone/doors.h +++ b/zone/doors.h @@ -1,13 +1,8 @@ #ifndef DOORS_H #define DOORS_H -#include "../common/repositories/doors_repository.h" -#include "../common/emu_opcodes.h" -#include "../common/eq_packet_structs.h" -#include "../common/linked_list.h" - #include "mob.h" -#include "zonedump.h" +#include "../common/repositories/doors_repository.h" class Client; class Mob; From 07664eedc0b2823c2ad5b5bbb46db5c55afa93bb Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 3 Oct 2021 14:51:12 -0400 Subject: [PATCH 248/624] [Bug Fix] Trim trailing whitespace off output in Popup. (#1584) --- zone/dialogue_window.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/dialogue_window.cpp b/zone/dialogue_window.cpp index b9ce924d0..24fe0e568 100644 --- a/zone/dialogue_window.cpp +++ b/zone/dialogue_window.cpp @@ -510,11 +510,11 @@ void DialogueWindow::Render(Client *c, std::string markdown) // build the final output string std::string final_output; - final_output = fmt::format("{}{}{}

{}", quote_string, output, quote_string, click_response); + final_output = fmt::format("{}{}{}

{}", quote_string, trim(output), quote_string, click_response); if (render_hiddenresponse) { final_output = fmt::format("{}{}{}", quote_string, output, quote_string); } - + // send popup c->SendFullPopup( title.c_str(), From f1d267bb2df9b9fe8bdb099f8897bfb9ed2bf823 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Mon, 4 Oct 2021 08:31:52 -0400 Subject: [PATCH 249/624] Update spell_effects.cpp --- zone/spell_effects.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 61ff24b0b..33e1fdfeb 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1077,7 +1077,12 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove caster->MessageString(Chat::SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name); break; } - int chance = spells[spell_id].base[i] - 20; //Baseline 2% negative modifer derived from parsing. + /* + TODO: Parsing shows there is no level modifier. However, a consistent -2% modifer was + found on spell with value 950 (95% spells would have 7% failure rates). + Further investigation is needed. ~ Kayen + */ + int chance = spells[spell_id].base[i]; int buff_count = GetMaxTotalSlots(); for(int slot = 0; slot < buff_count; slot++) { if (buffs[slot].spellid != SPELL_UNKNOWN && @@ -1103,7 +1108,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove caster->MessageString(Chat::SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name); break; } - int chance = spells[spell_id].base[i] - 20; //Baseline 2% negative modifer derived from parsing. + + int chance = spells[spell_id].base[i]; int buff_count = GetMaxTotalSlots(); for(int slot = 0; slot < buff_count; slot++) { if (buffs[slot].spellid != SPELL_UNKNOWN && From fc7c99fb0a7572b41cf67ce01a33494c6bb8b3e2 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Mon, 4 Oct 2021 11:14:56 -0400 Subject: [PATCH 250/624] Update spdat.h --- common/spdat.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 9061e8725..7d6dec102 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -839,7 +839,7 @@ typedef enum { #define SE_SuspendPet 151 // implemented, @Pet, allow caster to have an extra suspended pet, base: 0=no buffs/items 1=buffs+items, limit: none, max: none #define SE_TemporaryPets 152 // implemented #define SE_BalanceHP 153 // implemented -#define SE_DispelDetrimental 154 // implemented, @Dispel only beneficial effects on a target, base: pct chance (950=95%), limit:none, max:none +#define SE_DispelDetrimental 154 // implemented, @Dispel, removes only detrimental effects on a target, base: pct chance (950=95%), limit: none, max: none #define SE_SpellCritDmgIncrease 155 // implemented - no known live spells use this currently #define SE_IllusionCopy 156 // implemented - Deception #define SE_SpellDamageShield 157 // implemented - Petrad's Protection @@ -894,7 +894,7 @@ typedef enum { #define SE_AETaunt 206 // implemented #define SE_FleshToBone 207 // implemented //#define SE_PurgePoison 208 // not used -#define SE_DispelBeneficial 209 // implemented, @Dispel only beneficial effects on a target, base: pct chance (950=95%), limit:none, max:none +#define SE_DispelBeneficial 209 // implemented, @Dispel, removes only beneficial effects on a target, base: pct chance (950=95%), limit: none, max: none #define SE_PetShield 210 // implmented, @ShieldAbility, allows pet to 'shield' owner for 50 pct of damage taken for a duration, base: Time multiplier 1=12 seconds, 2=24 ect, limit: mitigation on pet owner override (not on live), max: mitigation on pet overide (not on live) #define SE_AEMelee 211 // implemented TO DO: Implement to allow NPC use (client only atm). #define SE_FrenziedDevastation 212 // implemented - increase spell criticals + all DD spells cast 2x mana. From 8b08e22dbc0e27bf0c90eda201bd7108b0657ea8 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Mon, 4 Oct 2021 14:33:20 -0400 Subject: [PATCH 251/624] removed unused function --- zone/mob.h | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/mob.h b/zone/mob.h index 40967aa72..098d19dac 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -346,7 +346,6 @@ public: uint16 CastingSpellID() const { return casting_spell_id; } bool DoCastingChecks(); bool TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier); - bool TryDispelBeneficialOrDetrimental(uint8 caster_level, uint8 buff_level, int chance); bool TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed = 1.5f); void ResourceTap(int32 damage, uint16 spell_id); void TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker); From b7304618940515251a973bd2e6024d2120a3d34c Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Mon, 4 Oct 2021 17:25:21 -0400 Subject: [PATCH 252/624] [Bug Fix] Trim output in hidden dialogue response. (#1587) --- zone/dialogue_window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/dialogue_window.cpp b/zone/dialogue_window.cpp index 24fe0e568..a2a874291 100644 --- a/zone/dialogue_window.cpp +++ b/zone/dialogue_window.cpp @@ -512,7 +512,7 @@ void DialogueWindow::Render(Client *c, std::string markdown) std::string final_output; final_output = fmt::format("{}{}{}

{}", quote_string, trim(output), quote_string, click_response); if (render_hiddenresponse) { - final_output = fmt::format("{}{}{}", quote_string, output, quote_string); + final_output = fmt::format("{}{}{}", quote_string, trim(output), quote_string); } // send popup From 133c1e866c4abc339e4d23d75d2bae172fbc8eb0 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Mon, 4 Oct 2021 17:25:51 -0400 Subject: [PATCH 253/624] [Bug Fix] Send appearance wasn't setting size properly when changing races. (#1586) --- zone/mob.cpp | 38 +++++--------------------------------- 1 file changed, 5 insertions(+), 33 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index 240496ef4..b81eb6146 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1766,21 +1766,21 @@ void Mob::SendIllusionPacket( race = in_race; if (race == 0) { - race = (use_model) ? use_model : GetBaseRace(); + race = use_model ? use_model : GetBaseRace(); } if (in_gender != 0xFF) { gender = in_gender; } else { - gender = (in_race) ? GetDefaultGender(race, gender) : GetBaseGender(); + gender = in_race ? GetDefaultGender(race, gender) : GetBaseGender(); } - if (in_texture == 0xFF && !IsPlayerRace(in_race)) { + if (in_texture == 0xFF && !IsPlayerRace(race)) { new_texture = GetTexture(); } - if (in_helmtexture == 0xFF && !IsPlayerRace(in_race)) { + if (in_helmtexture == 0xFF && !IsPlayerRace(race)) { new_helmtexture = GetHelmTexture(); } @@ -1813,38 +1813,10 @@ void Mob::SendIllusionPacket( new_drakkin_heritage = drakkin_heritage = CastToClient()->GetBaseHeritage(); new_drakkin_tattoo = drakkin_tattoo = CastToClient()->GetBaseTattoo(); new_drakkin_details = drakkin_details = CastToClient()->GetBaseDetails(); - switch (race) { - case OGRE: - size = 9; - break; - case TROLL: - size = 8; - break; - case VAHSHIR: - case BARBARIAN: - size = 7; - break; - case HALF_ELF: - case WOOD_ELF: - case DARK_ELF: - case FROGLOK: - size = 5; - break; - case DWARF: - size = 4; - break; - case HALFLING: - case GNOME: - size = 3; - break; - default: - size = 6; - break; - } } // update internal values for mob - size = (in_size <= 0.0f) ? GetSize() : in_size; + size = (in_size <= 0.0f) ? GetRaceGenderDefaultHeight(race, gender) : in_size; texture = new_texture; helmtexture = new_helmtexture; haircolor = new_haircolor; From 61d1eeab6f3410fed837fd7f51ea043be062582b Mon Sep 17 00:00:00 2001 From: Cole-SoD <90719745+Cole-SoD@users.noreply.github.com> Date: Mon, 4 Oct 2021 17:26:02 -0400 Subject: [PATCH 254/624] Minor corrections (#1582) --- common/inventory_profile.cpp | 2 +- zone/client_packet.cpp | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/common/inventory_profile.cpp b/common/inventory_profile.cpp index e93146598..2f0fc98a0 100644 --- a/common/inventory_profile.cpp +++ b/common/inventory_profile.cpp @@ -224,7 +224,7 @@ EQ::ItemInstance* EQ::InventoryProfile::GetItem(int16 slot_id, uint8 bagidx) con return GetItem(InventoryProfile::CalcSlotId(slot_id, bagidx)); } -// Put an item snto specified slot +// Put an item into specified slot int16 EQ::InventoryProfile::PutItem(int16 slot_id, const ItemInstance& inst) { if (slot_id <= EQ::invslot::POSSESSIONS_END && slot_id >= EQ::invslot::POSSESSIONS_BEGIN) { diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index d751fad8b..73802cac4 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1258,11 +1258,8 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) uint32 pplen = 0; EQApplicationPacket* outapp = nullptr; - MYSQL_RES* result = nullptr; bool loaditems = 0; - uint32 i; std::string query; - unsigned long* lengths = nullptr; uint32 cid = CharacterID(); character_id = cid; /* Global character_id reference */ @@ -1305,7 +1302,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) m_pp.platinum_shared = database.GetSharedPlatinum(this->AccountID()); database.ClearOldRecastTimestamps(cid); /* Clear out our old recast timestamps to keep the DB clean */ - // set to full support in case they're a gm with items in disabled expansion slots..but, have their gm flag off... + // set to full support in case they're a gm with items in disabled expansion slots...but, have their gm flag off... // item loss will occur when they use the 'empty' slots, if this is not done m_inv.SetGMInventory(true); loaditems = database.GetInventory(cid, &m_inv); /* Load Character Inventory */ @@ -1735,12 +1732,12 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) Character Inventory Packet this is not quite where live sends inventory, they do it after tribute */ - if (loaditems) { /* Dont load if a length error occurs */ + if (loaditems) { /* Don't load if a length error occurs */ if (admin >= minStatusToBeGM) m_inv.SetGMInventory(true); // set to true to allow expansion-restricted packets through BulkSendInventoryItems(); - /* Send stuff on the cursor which isnt sent in bulk */ + /* Send stuff on the cursor which isn't sent in bulk */ for (auto iter = m_inv.cursor_cbegin(); iter != m_inv.cursor_cend(); ++iter) { /* First item cursor is sent in bulk inventory packet */ if (iter == m_inv.cursor_cbegin()) @@ -1807,7 +1804,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) /* Weather Packet - This shouldent be moved, this seems to be what the client + This shouldn't be moved, this seems to be what the client uses to advance to the next state (sending ReqNewZone) */ outapp = new EQApplicationPacket(OP_Weather, 12); From 55d45f9a9895401cc25ccb7640fe2d5459c83715 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 5 Oct 2021 15:50:26 -0400 Subject: [PATCH 255/624] updates --- common/shareddb.cpp | 3 ++- common/spdat.cpp | 3 ++- common/spdat.h | 2 +- zone/attack.cpp | 8 +++++++- zone/bonuses.cpp | 6 +++++- zone/mob.h | 1 - zone/spells.cpp | 3 ++- 7 files changed, 19 insertions(+), 7 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 2a52ebe0e..18a2bed3d 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1840,6 +1840,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { sp[tempid].effectdescnum = atoi(row[157]); sp[tempid].npc_no_los = atoi(row[159]) != 0; + sp[tempid].feedbackable = atoi(row[160]) != 0; sp[tempid].reflectable = atoi(row[161]) != 0; sp[tempid].bonushate=atoi(row[162]); @@ -2287,4 +2288,4 @@ void SharedDatabase::LoadCharacterInspectMessage(uint32 character_id, InspectMes void SharedDatabase::SaveCharacterInspectMessage(uint32 character_id, const InspectMessage_Struct* message) { std::string query = StringFormat("REPLACE INTO `character_inspect_messages` (id, inspect_message) VALUES (%u, '%s')", character_id, EscapeString(message->text).c_str()); auto results = QueryDatabase(query); -} \ No newline at end of file +} diff --git a/common/spdat.cpp b/common/spdat.cpp index 581575aa4..839b9bbf2 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1427,6 +1427,7 @@ int GetSpellStatValue(uint32 spell_id, const char* stat_identifier, uint8 slot) else if (id == "descnum") { return spells[spell_id].descnum; } else if (id == "effectdescnum") { return spells[spell_id].effectdescnum; } else if (id == "npc_no_los") { return spells[spell_id].npc_no_los; } + else if (id == "feedbackable") { return spells[spell_id].reflectable; } else if (id == "reflectable") { return spells[spell_id].reflectable; } else if (id == "bonushate") { return spells[spell_id].bonushate; } else if (id == "endurcost") { return spells[spell_id].EndurCost; } @@ -1469,4 +1470,4 @@ int GetSpellStatValue(uint32 spell_id, const char* stat_identifier, uint8 slot) else if (id == "damageshieldtype") { return spells[spell_id].DamageShieldType; } return 0; -} \ No newline at end of file +} diff --git a/common/spdat.h b/common/spdat.h index 863281e4b..42f7f7ed0 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -1302,7 +1302,7 @@ struct SPDat_Spell_Struct /* 157 */ int effectdescnum; // eqstr of effect description -- SECONDARY_CATEGORY_1 /* 158 */ //int secondary_category_2; //Category Desc ID 3 -- SECONDARY_CATEGORY_2 /* 159 */ bool npc_no_los; // -- NO_NPC_LOS -/* 160 */ //bool feedbackable; // -- FEEDBACKABLE +/* 160 */ bool feedbackable; // -- FEEDBACKABLE /* 161 */ bool reflectable; // -- REFLECTABLE /* 162 */ int bonushate; // -- HATE_MOD /* 163 */ //int resist_per_level; // -- RESIST_PER_LEVEL diff --git a/zone/attack.cpp b/zone/attack.cpp index d5cd9005c..dbfee1e3a 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2875,10 +2875,15 @@ void Mob::DamageShield(Mob* attacker, bool spell_ds) { spellid = spellbonuses.DamageShieldSpellID; } else { - DS = spellbonuses.SpellDamageShield; + DS = spellbonuses.SpellDamageShield + itembonuses.SpellDamageShield + aabonuses.SpellDamageShield; rev_ds = 0; // This ID returns "you are burned", seemed most appropriate for spell DS spellid = 2166; + /* + Live Message - not yet used on emu + Feedback onto you "YOUR mind burns from TARGETS NAME's feedback for %i points of non-melee damage." + Feedback onto other "TARGETS NAME's mind burns from YOUR feedback for %i points of non-melee damage." + */ } if (DS == 0 && rev_ds == 0) @@ -2912,6 +2917,7 @@ void Mob::DamageShield(Mob* attacker, bool spell_ds) { DS -= DS * ds_mitigation / 100; } + attacker->Damage(this, -DS, spellid, EQ::skills::SkillAbjuration/*hackish*/, false); //we can assume there is a spell now auto outapp = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct)); diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 390917ff7..9a640196a 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1672,6 +1672,10 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) newbon->ZoneSuspendMinion = base1; break; + case SE_SpellDamageShield: + newbon->SpellDamageShield += base1; + break; + // to do case SE_PetDiscipline: break; @@ -3727,7 +3731,7 @@ void NPC::CalcItemBonuses(StatBonuses *newbon) newbon->DamageShield += cur->DamageShield; } if(cur->SpellShield > 0) { - newbon->SpellDamageShield += cur->SpellShield; + newbon->SpellShield += cur->SpellShield; } if(cur->Shielding > 0) { newbon->MeleeMitigation += cur->Shielding; diff --git a/zone/mob.h b/zone/mob.h index ac222a3dd..b9e3bfd85 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -848,7 +848,6 @@ public: int GetHealRate() const { return itembonuses.HealRate + spellbonuses.HealRate + aabonuses.HealRate; } int GetMemoryBlurChance(int base_chance); - bool TryDoubleMeleeRoundEffect(); bool GetUseDoubleMeleeRoundDmgBonus() const { return use_double_melee_round_dmg_bonus; } inline void SetUseDoubleMeleeRoundDmgBonus(bool val) { use_double_melee_round_dmg_bonus = val; } diff --git a/zone/spells.cpp b/zone/spells.cpp index 2553b5e10..97caa49c7 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3989,8 +3989,9 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r spell_effectiveness = 100; } - if(spelltar->spellbonuses.SpellDamageShield && IsDetrimentalSpell(spell_id)) + if (spells[spell_id].feedbackable && (spelltar->spellbonuses.SpellDamageShield || spelltar->itembonuses.SpellDamageShield || spelltar->aabonuses.SpellDamageShield)) { spelltar->DamageShield(this, true); + } if (spelltar->IsAIControlled() && IsDetrimentalSpell(spell_id) && !IsHarmonySpell(spell_id)) { int32 aggro_amount = CheckAggroAmount(spell_id, spelltar, isproc); From 740f84dc22253ea807286d6300ff37ed22e997a2 Mon Sep 17 00:00:00 2001 From: Noudess Date: Tue, 5 Oct 2021 15:51:22 -0400 Subject: [PATCH 256/624] always_aggro flag needed to be checked on assist --- zone/npc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/npc.cpp b/zone/npc.cpp index e402c89ab..d5c89d185 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -3379,7 +3379,7 @@ void NPC::AIYellForHelp(Mob *sender, Mob *attacker) * if they are in range, make sure we are not green... * then jump in if they are our friend */ - if (mob->GetLevel() >= 50 || attacker->GetLevelCon(mob->GetLevel()) != CON_GRAY) { + if (mob->GetLevel() >= 50 || mob->AlwaysAggro() || attacker->GetLevelCon(mob->GetLevel()) != CON_GRAY) { if (mob->GetPrimaryFaction() == sender->CastToNPC()->GetPrimaryFaction()) { const NPCFactionList *cf = content_db.GetNPCFactionEntry(mob->CastToNPC()->GetNPCFactionID()); if (cf) { From 3b9574af14caf7c1010ef0e9bde50d7244e0855b Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 5 Oct 2021 16:59:07 -0400 Subject: [PATCH 257/624] fix --- common/spdat.cpp | 2 +- common/spdat.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 839b9bbf2..8721078d7 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1427,7 +1427,7 @@ int GetSpellStatValue(uint32 spell_id, const char* stat_identifier, uint8 slot) else if (id == "descnum") { return spells[spell_id].descnum; } else if (id == "effectdescnum") { return spells[spell_id].effectdescnum; } else if (id == "npc_no_los") { return spells[spell_id].npc_no_los; } - else if (id == "feedbackable") { return spells[spell_id].reflectable; } + else if (id == "feedbackable") { return spells[spell_id].feedbackable; } else if (id == "reflectable") { return spells[spell_id].reflectable; } else if (id == "bonushate") { return spells[spell_id].bonushate; } else if (id == "endurcost") { return spells[spell_id].EndurCost; } diff --git a/common/spdat.h b/common/spdat.h index 42f7f7ed0..81f068fec 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -842,7 +842,7 @@ typedef enum { #define SE_DispelDetrimental 154 // implemented #define SE_SpellCritDmgIncrease 155 // implemented - no known live spells use this currently #define SE_IllusionCopy 156 // implemented - Deception -#define SE_SpellDamageShield 157 // implemented - Petrad's Protection +#define SE_SpellDamageShield 157 // implemented, @DS, causes non-melee damage on caster of a spell, base: Amt DS (negative), limit: none, max: unknown (same as base but +) #define SE_Reflect 158 // implemented #define SE_AllStats 159 // implemented //#define SE_MakeDrunk 160 // *not implemented - Effect works entirely client side (Should check against tolerance) From 6689b57a527db37adf8244a2ee584aa7ab54cca4 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 8 Oct 2021 05:41:37 -0400 Subject: [PATCH 258/624] [Commands] Convert item ID search to use saylinks similar to name search. (#1589) --- zone/command.cpp | 59 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 12 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 884d43330..3cad3be78 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -8344,11 +8344,46 @@ void command_itemsearch(Client *c, const Seperator *sep) item = database.GetItem(atoi(search_criteria)); if (item) { linker.SetItemData(item); + std::string item_id = std::to_string(item->ID); + std::string saylink_commands = + "[" + + EQ::SayLinkEngine::GenerateQuestSaylink( + "#si " + item_id, + false, + "X" + ) + + "] "; - c->Message(Chat::White, "%u: %s", item->ID, linker.GenerateLink().c_str()); + if (item->Stackable && item->StackSize > 1) { + std::string stack_size = std::to_string(item->StackSize); + saylink_commands += + "[" + + EQ::SayLinkEngine::GenerateQuestSaylink( + "#si " + item_id + " " + stack_size, + false, + stack_size + ) + + "]"; + } + + c->Message( + Chat::White, + fmt::format( + " Summon {} [{}] [{}]", + saylink_commands, + linker.GenerateLink(), + item->ID + ).c_str() + ); } else { - c->Message(Chat::White, "Item #%s not found", search_criteria); + c->Message( + Chat::White, + fmt::format( + "Item {} not found", + search_criteria + ).c_str() + ); } return; @@ -8370,21 +8405,21 @@ void command_itemsearch(Client *c, const Seperator *sep) std::string item_id = std::to_string(item->ID); std::string saylink_commands = "[" + - EQ::SayLinkEngine::GenerateQuestSaylink( - "#si " + item_id, - false, - "X" - ) + + EQ::SayLinkEngine::GenerateQuestSaylink( + "#si " + item_id, + false, + "X" + ) + "] "; if (item->Stackable && item->StackSize > 1) { std::string stack_size = std::to_string(item->StackSize); saylink_commands += "[" + - EQ::SayLinkEngine::GenerateQuestSaylink( - "#si " + item_id + " " + stack_size, - false, - stack_size - ) + + EQ::SayLinkEngine::GenerateQuestSaylink( + "#si " + item_id + " " + stack_size, + false, + stack_size + ) + "]"; } From 783c12590ef30783744bd4e0bfdb2a32a1715fd2 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 8 Oct 2021 13:14:39 -0400 Subject: [PATCH 259/624] minor fix was not correct, was comparing negative to a positive --- zone/effects.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index 571abe6da..bdf230490 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -259,10 +259,11 @@ int32 Mob::GetExtraSpellAmt(uint16 spell_id, int32 extra_spell_amt, int32 base_s else extra_spell_amt = extra_spell_amt * total_cast_time / 7000; - if(extra_spell_amt*2 < base_spell_dmg) - return 0; + if (extra_spell_amt * 2 > abs(base_spell_dmg)) { + return 0; + } - return extra_spell_amt; + return extra_spell_amt; } int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { From dd1a869531d20864cc006d5788ee631449162187 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 8 Oct 2021 18:43:47 -0400 Subject: [PATCH 260/624] hotfix --- zone/effects.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index bdf230490..b7319e793 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -317,8 +317,8 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { if (spells[spell_id].buffduration < 1) { if (target) { - value += int(value_BaseEffect + target->GetFocusEffect(focusFcHealPctIncoming, spell_id)/100); //SPA 393 Add before critical - value += int(value_BaseEffect + target->GetFocusEffect(focusFcHealPctCritIncoming, spell_id)/100); //SPA 395 Add before critical (?) + value += int(value_BaseEffect * target->GetFocusEffect(focusFcHealPctIncoming, spell_id)/100); //SPA 393 Add before critical + value += int(value_BaseEffect * target->GetFocusEffect(focusFcHealPctCritIncoming, spell_id)/100); //SPA 395 Add before critical (?) } value += GetFocusEffect(focusFcHealAmtCrit, spell_id); //SPA 396 Add before critical From db369c98c8ac59023abc6ce52e0bf11d5001a10d Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Fri, 8 Oct 2021 23:04:19 -0500 Subject: [PATCH 261/624] [HP Updates] Fix for Titanium clients not being updated properly by removing client version check (#1596) --- zone/mob.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index b81eb6146..d1c6ced3d 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1370,20 +1370,18 @@ void Mob::SendHPUpdate(bool force_update_all) last_hp ); - if (CastToClient()->ClientVersion() >= EQ::versions::ClientVersion::SoD) { - auto client_packet = new EQApplicationPacket(OP_HPUpdate, sizeof(SpawnHPUpdate_Struct)); - auto *hp_packet_client = (SpawnHPUpdate_Struct *) client_packet->pBuffer; + auto client_packet = new EQApplicationPacket(OP_HPUpdate, sizeof(SpawnHPUpdate_Struct)); + auto *hp_packet_client = (SpawnHPUpdate_Struct *) client_packet->pBuffer; - hp_packet_client->cur_hp = static_cast(CastToClient()->GetHP() - itembonuses.HP); - hp_packet_client->spawn_id = GetID(); - hp_packet_client->max_hp = CastToClient()->GetMaxHP() - itembonuses.HP; + hp_packet_client->cur_hp = static_cast(CastToClient()->GetHP() - itembonuses.HP); + hp_packet_client->spawn_id = GetID(); + hp_packet_client->max_hp = CastToClient()->GetMaxHP() - itembonuses.HP; - CastToClient()->QueuePacket(client_packet); + CastToClient()->QueuePacket(client_packet); - safe_delete(client_packet); + safe_delete(client_packet); - ResetHPUpdateTimer(); - } + ResetHPUpdateTimer(); // Used to check if HP has changed to update self next round last_hp = current_hp; From 9887580f9a250223ad2bf8bcbe66a50d307c4415 Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Sat, 9 Oct 2021 13:45:38 -0400 Subject: [PATCH 262/624] Make columns in doors table not nullable (#1597) This makes the float and integer fields in the doors table not nullable. The only column this should affect is the buffer column which wasn't being loaded in the old doors loading query. The other columns weren't validated but they should still be made not nullable to avoid issues. This will fix a crash in potimeb which is the only zone that had NULL values in the buffer column with the current peq database. This column can be removed in a future followup since it isn't being used anyway. --- common/version.h | 2 +- utils/sql/db_update_manifest.txt | 1 + .../2021_10_09_not_null_door_columns.sql | 18 ++++++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 utils/sql/git/required/2021_10_09_not_null_door_columns.sql diff --git a/common/version.h b/common/version.h index 1d5eec0f8..f8eb10b8c 100644 --- a/common/version.h +++ b/common/version.h @@ -34,7 +34,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9173 +#define CURRENT_BINARY_DATABASE_VERSION 9174 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9028 diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 076e6fdf4..153155c5d 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -427,6 +427,7 @@ 9171|2021_03_30_remove_dz_is_current_member.sql|SHOW COLUMNS FROM `dynamic_zone_members` LIKE 'is_current_member'|not_empty| 9172|2021_05_21_shared_tasks.sql|SHOW TABLES LIKE 'shared_tasks'|empty| 9173|2021_09_14_zone_lava_damage.sql|SHOW COLUMNS FROM `zone` LIKE 'lava_damage'|empty| +9174|2021_10_09_not_null_door_columns.sql|SELECT * FROM db_version WHERE version >= 9174|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2021_10_09_not_null_door_columns.sql b/utils/sql/git/required/2021_10_09_not_null_door_columns.sql new file mode 100644 index 000000000..7aed58b6f --- /dev/null +++ b/utils/sql/git/required/2021_10_09_not_null_door_columns.sql @@ -0,0 +1,18 @@ +-- update any null columns to non-null value first to avoid data truncation errors +-- this will likely only affect the buffer column +update `doors` set `doors`.`dest_x` = 0 where `doors`.`dest_x` is null; +update `doors` set `doors`.`dest_y` = 0 where `doors`.`dest_y` is null; +update `doors` set `doors`.`dest_z` = 0 where `doors`.`dest_z` is null; +update `doors` set `doors`.`dest_heading` = 0 where `doors`.`dest_heading` is null; +update `doors` set `doors`.`invert_state` = 0 where `doors`.`invert_state` is null; +update `doors` set `doors`.`incline` = 0 where `doors`.`incline` is null; +update `doors` set `doors`.`buffer` = 0 where `doors`.`buffer` is null; + +ALTER TABLE `doors` + CHANGE COLUMN `dest_x` `dest_x` FLOAT NOT NULL DEFAULT '0' AFTER `dest_instance`, + CHANGE COLUMN `dest_y` `dest_y` FLOAT NOT NULL DEFAULT '0' AFTER `dest_x`, + CHANGE COLUMN `dest_z` `dest_z` FLOAT NOT NULL DEFAULT '0' AFTER `dest_y`, + CHANGE COLUMN `dest_heading` `dest_heading` FLOAT NOT NULL DEFAULT '0' AFTER `dest_z`, + CHANGE COLUMN `invert_state` `invert_state` INT(11) NOT NULL DEFAULT '0' AFTER `dest_heading`, + CHANGE COLUMN `incline` `incline` INT(11) NOT NULL DEFAULT '0' AFTER `invert_state`, + CHANGE COLUMN `buffer` `buffer` FLOAT NOT NULL DEFAULT '0' AFTER `size`; From 91adf9c0ebd4c94fc94f84ab08f83b1c4dd14903 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Mon, 11 Oct 2021 16:33:18 -0400 Subject: [PATCH 263/624] [Quest API] Add cross zone and world wide dialogue windows to Perl/Lua. (#1599) * [Quest API] Add cross zone and world wide dialogue windows to Perl/Lua. - Add quest::crosszonedialoguewindowbycharid(character_id, message) to Perl. - Add quest::crosszonedialoguewindowbygroupid(group_id, message) to Perl. - Add quest::crosszonedialoguewindowbyraidid(raid_id, message) to Perl. - Add quest::crosszonedialoguewindowbyguildid(guild_id, message) to Perl. - Add quest::crosszonedialoguewindowbyexpeditionid(expedition_id, message) to Perl. - Add quest::crosszonedialoguewindowbyclientname(client_name, message) to Perl. - Add quest::worldwidedialoguewindow(message, min_status, max_status) to Perl. - Add eq.cross_zone_dialogue_window_by_char_id(character_id, message) to Lua. - Add eq.cross_zone_dialogue_window_by_group_id(group_id, message) to Lua. - Add eq.cross_zone_dialogue_window_by_raid_id(raid_id, message) to Lua. - Add eq.cross_zone_dialogue_window_by_guild_id(guild_id, message) to Lua. - Add eq.cross_zone_dialogue_window_by_expedition_id(expedition_id, message) to Lua. - Add eq.cross_zone_dialogue_window_by_client_name(client_name, message) to Lua. - Add eq.world_wide_dialogue_window(message, min_status, max_status) to Lua. * Use string instead. --- common/servertalk.h | 49 ++-- world/zoneserver.cpp | 4 +- zone/embparser_api.cpp | 112 +++++++++ zone/lua_general.cpp | 52 ++++ zone/questmgr.cpp | 21 ++ zone/questmgr.h | 4 +- zone/worldserver.cpp | 556 +++++++++++++++++++++++------------------ 7 files changed, 534 insertions(+), 264 deletions(-) diff --git a/common/servertalk.h b/common/servertalk.h index 00510d60b..fd91f591e 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -227,24 +227,26 @@ #define ServerOP_HotReloadQuests 0x4011 #define ServerOP_UpdateSchedulerEvents 0x4012 -#define ServerOP_CZLDoNUpdate 0x4500 -#define ServerOP_CZMarquee 0x4501 -#define ServerOP_CZMessage 0x4502 -#define ServerOP_CZMove 0x4503 -#define ServerOP_CZSetEntityVariable 0x4504 -#define ServerOP_CZSignal 0x4505 -#define ServerOP_CZSpell 0x4506 -#define ServerOP_CZTaskUpdate 0x4507 -#define ServerOP_CZClientMessageString 0x4508 +#define ServerOP_CZDialogueWindow 0x4500 +#define ServerOP_CZLDoNUpdate 0x4501 +#define ServerOP_CZMarquee 0x4502 +#define ServerOP_CZMessage 0x4503 +#define ServerOP_CZMove 0x4504 +#define ServerOP_CZSetEntityVariable 0x4505 +#define ServerOP_CZSignal 0x4506 +#define ServerOP_CZSpell 0x4507 +#define ServerOP_CZTaskUpdate 0x4508 +#define ServerOP_CZClientMessageString 0x4509 -#define ServerOP_WWLDoNUpdate 0x4750 -#define ServerOP_WWMarquee 0x4751 -#define ServerOP_WWMessage 0x4752 -#define ServerOP_WWMove 0x4753 -#define ServerOP_WWSetEntityVariable 0x4754 -#define ServerOP_WWSignal 0x4755 -#define ServerOP_WWSpell 0x4756 -#define ServerOP_WWTaskUpdate 0x4757 +#define ServerOP_WWDialogueWindow 0x4750 +#define ServerOP_WWLDoNUpdate 0x4751 +#define ServerOP_WWMarquee 0x4752 +#define ServerOP_WWMessage 0x4753 +#define ServerOP_WWMove 0x4754 +#define ServerOP_WWSetEntityVariable 0x4755 +#define ServerOP_WWSignal 0x4756 +#define ServerOP_WWSpell 0x4757 +#define ServerOP_WWTaskUpdate 0x4758 /** * QueryServer @@ -1440,6 +1442,13 @@ struct CZClientMessageString_Struct { char args[1]; // null delimited }; +struct CZDialogueWindow_Struct { + uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name + int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, or Expedition ID based on update type, 0 for Character Name + char message[4096]; + char client_name[64]; // Only used by Character Name Type, else empty +}; + struct CZLDoNUpdate_Struct { uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name uint8 update_subtype; // 0 - Loss, 1 - Points, 2 - Win @@ -1512,6 +1521,12 @@ struct CZTaskUpdate_Struct { char client_name[64]; // Only used by Character Name Type, else empty }; +struct WWDialogueWindow_Struct { + char message[4096]; + uint8 min_status; + uint8 max_status; +}; + struct WWLDoNUpdate_Struct { uint8 update_type; // 0 - Loss, 1 - Points, 2 - Win uint32 theme_id; diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index ee0c9e8da..03b5e8dd2 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -1239,7 +1239,8 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { { QSLink.SendPacket(pack); break; - } + } + case ServerOP_CZDialogueWindow: case ServerOP_CZLDoNUpdate: case ServerOP_CZMarquee: case ServerOP_CZMessage: @@ -1248,6 +1249,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { case ServerOP_CZSignal: case ServerOP_CZSpell: case ServerOP_CZTaskUpdate: + case ServerOP_WWDialogueWindow: case ServerOP_WWLDoNUpdate: case ServerOP_WWMarquee: case ServerOP_WWMessage: diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 164021bf6..98e061c8c 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -5806,6 +5806,91 @@ XS(XS__crosszonecastspellbyclientname) { XSRETURN_EMPTY; } +XS(XS__crosszonedialoguewindowbycharid); +XS(XS__crosszonedialoguewindowbycharid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonedialoguewindowbycharid(int character_id, string message)"); + { + uint8 update_type = CZUpdateType_Character; + int character_id = (int) SvIV(ST(0)); + const char* message = (const char*) SvPV_nolen(ST(1)); + quest_manager.CrossZoneDialogueWindow(update_type, character_id, message); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonedialoguewindowbygroupid); +XS(XS__crosszonedialoguewindowbygroupid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonedialoguewindowbygroupid(int group_id, string message)"); + { + uint8 update_type = CZUpdateType_Group; + int group_id = (int) SvIV(ST(0)); + const char* message = (const char*) SvPV_nolen(ST(1)); + quest_manager.CrossZoneDialogueWindow(update_type, group_id, message); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonedialoguewindowbyraidid); +XS(XS__crosszonedialoguewindowbyraidid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonedialoguewindowbyraidid(int raid_id, string message)"); + { + uint8 update_type = CZUpdateType_Raid; + int raid_id = (int) SvIV(ST(0)); + const char* message = (const char*) SvPV_nolen(ST(1)); + quest_manager.CrossZoneDialogueWindow(update_type, raid_id, message); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonedialoguewindowbyguildid); +XS(XS__crosszonedialoguewindowbyguildid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonedialoguewindowbyguildid(int guild_id, string message)"); + { + uint8 update_type = CZUpdateType_Guild; + int guild_id = (int) SvIV(ST(0)); + const char* message = (const char*) SvPV_nolen(ST(1)); + quest_manager.CrossZoneDialogueWindow(update_type, guild_id, message); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonedialoguewindowbyexpeditionid); +XS(XS__crosszonedialoguewindowbyexpeditionid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonedialoguewindowbyexpeditionid(uint32 expedition_id, string message)"); + { + uint8 update_type = CZUpdateType_Expedition; + uint32 expedition_id = (uint32) SvUV(ST(0)); + const char* message = (const char*) SvPV_nolen(ST(1)); + quest_manager.CrossZoneDialogueWindow(update_type, expedition_id, message); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszonedialoguewindowbyclientname); +XS(XS__crosszonedialoguewindowbyclientname) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszonedialoguewindowbyclientname(const char* client_name, string message)"); + { + uint8 update_type = CZUpdateType_ClientName; + int update_identifier = 0; + const char* client_name = (const char*) SvPV_nolen(ST(0)); + const char* message = (const char*) SvPV_nolen(ST(1)); + quest_manager.CrossZoneDialogueWindow(update_type, update_identifier, message, client_name); + } + XSRETURN_EMPTY; +} + XS(XS__crosszonedisabletaskbycharid); XS(XS__crosszonedisabletaskbycharid) { dXSARGS; @@ -7225,6 +7310,26 @@ XS(XS__worldwidecastspell) { XSRETURN_EMPTY; } +XS(XS__worldwidedialoguewindow); +XS(XS__worldwidedialoguewindow) { + dXSARGS; + if (items < 1 || items > 3) + Perl_croak(aTHX_ "Usage: quest::worldwidedialoguewindow(string message, [uint8 min_status = 0, uint8 max_status = 0])"); + { + const char* message = (const char*) SvPV_nolen(ST(0)); + uint8 min_status = 0; + uint8 max_status = 0; + if (items == 2) + min_status = (uint8)SvUV(ST(1)); + + if (items == 3) + max_status = (uint8)SvUV(ST(2)); + + quest_manager.WorldWideDialogueWindow(message, min_status, max_status); + } + XSRETURN_EMPTY; +} + XS(XS__worldwidedisabletask); XS(XS__worldwidedisabletask) { dXSARGS; @@ -7791,6 +7896,12 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "crosszonecastspellbyguildid"), XS__crosszonecastspellbyguildid, file); newXS(strcpy(buf, "crosszonecastspellbyexpeditionid"), XS__crosszonecastspellbyexpeditionid, file); newXS(strcpy(buf, "crosszonecastspellbyclientname"), XS__crosszonecastspellbyclientname, file); + newXS(strcpy(buf, "crosszonedialoguewindowbycharid"), XS__crosszonedialoguewindowbycharid, file); + newXS(strcpy(buf, "crosszonedialoguewindowbygroupid"), XS__crosszonedialoguewindowbygroupid, file); + newXS(strcpy(buf, "crosszonedialoguewindowbyraidid"), XS__crosszonedialoguewindowbyraidid, file); + newXS(strcpy(buf, "crosszonedialoguewindowbyguildid"), XS__crosszonedialoguewindowbyguildid, file); + newXS(strcpy(buf, "crosszonedialoguewindowbyexpeditionid"), XS__crosszonedialoguewindowbyexpeditionid, file); + newXS(strcpy(buf, "crosszonedialoguewindowbyclientname"), XS__crosszonedialoguewindowbyclientname, file); newXS(strcpy(buf, "crosszonedisabletaskbycharid"), XS__crosszonedisabletaskbycharid, file); newXS(strcpy(buf, "crosszonedisabletaskbygroupid"), XS__crosszonedisabletaskbygroupid, file); newXS(strcpy(buf, "crosszonedisabletaskbyraidid"), XS__crosszonedisabletaskbyraidid, file); @@ -7875,6 +7986,7 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "worldwideaddldonpoints"), XS__worldwideaddldonpoints, file); newXS(strcpy(buf, "worldwideaddldonwin"), XS__worldwideaddldonwin, file); newXS(strcpy(buf, "worldwidecastspell"), XS__worldwidecastspell, file); + newXS(strcpy(buf, "worldwidedialoguewindow"), XS__worldwidedialoguewindow, file); newXS(strcpy(buf, "worldwidedisabletask"), XS__worldwidedisabletask, file); newXS(strcpy(buf, "worldwideenabletask"), XS__worldwideenabletask, file); newXS(strcpy(buf, "worldwidefailtask"), XS__worldwidefailtask, file); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 404a074ef..a9d724138 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -2179,6 +2179,37 @@ void lua_cross_zone_cast_spell_by_client_name(const char* client_name, uint32 sp quest_manager.CrossZoneSpell(update_type, update_subtype, update_identifier, spell_id, client_name); } +void lua_cross_zone_dialogue_window_by_char_id(int character_id, const char* message) { + uint8 update_type = CZUpdateType_Character; + quest_manager.CrossZoneDialogueWindow(update_type, character_id, message); +} + +void lua_cross_zone_dialogue_window_by_group_id(int group_id, const char* message) { + uint8 update_type = CZUpdateType_Group; + quest_manager.CrossZoneDialogueWindow(update_type, group_id, message); +} + +void lua_cross_zone_dialogue_window_by_raid_id(int raid_id, const char* message) { + uint8 update_type = CZUpdateType_Raid; + quest_manager.CrossZoneDialogueWindow(update_type, raid_id, message); +} + +void lua_cross_zone_dialogue_window_by_guild_id(int guild_id, const char* message) { + uint8 update_type = CZUpdateType_Guild; + quest_manager.CrossZoneDialogueWindow(update_type, guild_id, message); +} + +void lua_cross_zone_dialogue_window_by_expedition_id(uint32 expedition_id, const char* message) { + uint8 update_type = CZUpdateType_Expedition; + quest_manager.CrossZoneDialogueWindow(update_type, expedition_id, message); +} + +void lua_cross_zone_dialogue_window_by_client_name(const char* client_name, const char* message) { + uint8 update_type = CZUpdateType_ClientName; + int update_identifier = 0; + quest_manager.CrossZoneDialogueWindow(update_type, update_identifier, message, client_name); +} + void lua_cross_zone_disable_task_by_char_id(int character_id, uint32 task_id) { uint8 update_type = CZUpdateType_Character; uint8 update_subtype = CZTaskUpdateSubtype_DisableTask; @@ -2882,6 +2913,18 @@ void lua_world_wide_cast_spell(uint32 spell_id, uint8 min_status, uint8 max_stat quest_manager.WorldWideSpell(update_type, spell_id, min_status, max_status); } +void lua_world_wide_dialogue_window(const char* message) { + quest_manager.WorldWideDialogueWindow(message); +} + +void lua_world_wide_dialogue_window(const char* message, uint8 min_status) { + quest_manager.WorldWideDialogueWindow(message, min_status); +} + +void lua_world_wide_dialogue_window(const char* message, uint8 min_status, uint8 max_status) { + quest_manager.WorldWideDialogueWindow(message, min_status, max_status); +} + void lua_world_wide_disable_task(uint32 task_id) { uint8 update_type = WWTaskUpdateType_DisableTask; quest_manager.WorldWideTaskUpdate(update_type, task_id); @@ -3662,6 +3705,12 @@ luabind::scope lua_register_general() { luabind::def("cross_zone_cast_spell_by_guild_id", &lua_cross_zone_cast_spell_by_guild_id), luabind::def("cross_zone_cast_spell_by_expedition_id", &lua_cross_zone_cast_spell_by_expedition_id), luabind::def("cross_zone_cast_spell_by_client_name", &lua_cross_zone_cast_spell_by_client_name), + luabind::def("cross_zone_dialogue_window_by_char_id", &lua_cross_zone_dialogue_window_by_char_id), + luabind::def("cross_zone_dialogue_window_by_group_id", &lua_cross_zone_dialogue_window_by_group_id), + luabind::def("cross_zone_dialogue_window_by_raid_id", &lua_cross_zone_dialogue_window_by_raid_id), + luabind::def("cross_zone_dialogue_window_by_guild_id", &lua_cross_zone_dialogue_window_by_guild_id), + luabind::def("cross_zone_dialogue_window_by_expedition_id", &lua_cross_zone_dialogue_window_by_expedition_id), + luabind::def("cross_zone_dialogue_window_by_client_name", &lua_cross_zone_dialogue_window_by_client_name), luabind::def("cross_zone_disable_task_by_char_id", &lua_cross_zone_disable_task_by_char_id), luabind::def("cross_zone_disable_task_by_group_id", &lua_cross_zone_disable_task_by_group_id), luabind::def("cross_zone_disable_task_by_raid_id", &lua_cross_zone_disable_task_by_raid_id), @@ -3767,6 +3816,9 @@ luabind::scope lua_register_general() { luabind::def("world_wide_cast_spell", (void(*)(uint32))&lua_world_wide_cast_spell), luabind::def("world_wide_cast_spell", (void(*)(uint32,uint8))&lua_world_wide_cast_spell), luabind::def("world_wide_cast_spell", (void(*)(uint32,uint8,uint8))&lua_world_wide_cast_spell), + luabind::def("world_wide_dialogue_window", (void(*)(const char*))&lua_world_wide_dialogue_window), + luabind::def("world_wide_dialogue_window", (void(*)(const char*,uint8))&lua_world_wide_dialogue_window), + luabind::def("world_wide_dialogue_window", (void(*)(const char*,uint8,uint8))&lua_world_wide_dialogue_window), luabind::def("world_wide_disable_task", (void(*)(uint32))&lua_world_wide_disable_task), luabind::def("world_wide_disable_task", (void(*)(uint32,uint8))&lua_world_wide_disable_task), luabind::def("world_wide_disable_task", (void(*)(uint32,uint8,uint8))&lua_world_wide_disable_task), diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 350a8a6ef..e2fa0617b 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -3464,6 +3464,17 @@ int QuestManager::getspellstat(uint32 spell_id, std::string stat_identifier, uin return GetSpellStatValue(spell_id, stat_identifier.c_str(), slot); } +void QuestManager::CrossZoneDialogueWindow(uint8 update_type, int update_identifier, const char* message, const char* client_name) { + auto pack = new ServerPacket(ServerOP_CZDialogueWindow, sizeof(CZDialogueWindow_Struct)); + CZDialogueWindow_Struct* CZDW = (CZDialogueWindow_Struct*)pack->pBuffer; + CZDW->update_type = update_type; + CZDW->update_identifier = update_identifier; + strn0cpy(CZDW->message, message, 4096); + strn0cpy(CZDW->client_name, client_name, 64); + worldserver.SendPacket(pack); + safe_delete(pack); +} + void QuestManager::CrossZoneLDoNUpdate(uint8 update_type, uint8 update_subtype, int update_identifier, uint32 theme_id, int points, const char* client_name) { auto pack = new ServerPacket(ServerOP_CZLDoNUpdate, sizeof(CZLDoNUpdate_Struct)); CZLDoNUpdate_Struct* CZLU = (CZLDoNUpdate_Struct*)pack->pBuffer; @@ -3568,6 +3579,16 @@ void QuestManager::CrossZoneTaskUpdate(uint8 update_type, uint8 update_subtype, safe_delete(pack); } +void QuestManager::WorldWideDialogueWindow(const char* message, uint8 min_status, uint8 max_status) { + auto pack = new ServerPacket(ServerOP_WWDialogueWindow, sizeof(WWDialogueWindow_Struct)); + WWDialogueWindow_Struct* WWDW = (WWDialogueWindow_Struct*)pack->pBuffer; + strn0cpy(WWDW->message, message, 4096); + WWDW->min_status = min_status; + WWDW->max_status = max_status; + worldserver.SendPacket(pack); + safe_delete(pack); +} + void QuestManager::WorldWideLDoNUpdate(uint8 update_type, uint32 theme_id, int points, uint8 min_status, uint8 max_status) { auto pack = new ServerPacket(ServerOP_WWLDoNUpdate, sizeof(WWLDoNUpdate_Struct)); WWLDoNUpdate_Struct* WWLU = (WWLDoNUpdate_Struct*)pack->pBuffer; diff --git a/zone/questmgr.h b/zone/questmgr.h index fa56f8dc2..00221c965 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -290,6 +290,7 @@ public: static std::string GetZoneLongName(std::string zone_short_name); static std::string GetZoneLongNameByID(uint32 zone_id); static std::string GetZoneShortName(uint32 zone_id); + void CrossZoneDialogueWindow(uint8 update_type, int update_identifier, const char* message, const char* client_name = ""); void CrossZoneLDoNUpdate(uint8 update_type, uint8 update_subtype, int update_identifier, uint32 theme_id, int points = 1, const char* client_name = ""); void CrossZoneMarquee(uint8 update_type, int update_identifier, uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char* message, const char* client_name = ""); void CrossZoneMessage(uint8 update_type, int update_identifier, uint32 type, const char* message, const char* client_name = ""); @@ -298,7 +299,8 @@ public: void CrossZoneSignal(uint8 update_type, int update_identifier, uint32 signal, const char* client_name = ""); void CrossZoneSpell(uint8 update_type, uint8 update_subtype, int update_identifier, uint32 spell_id, const char* client_name = ""); void CrossZoneTaskUpdate(uint8 update_type, uint8 update_subtype, int update_identifier, uint32 task_identifier, int task_subidentifier = -1, int update_count = 1, bool enforce_level_requirement = false, const char* client_name = ""); - void WorldWideLDoNUpdate(uint8 update_type, uint32 theme_id, int points = 1, uint8 min_status = 0, uint8 max_status = 0); + void WorldWideDialogueWindow(const char* message, uint8 min_status = 0, uint8 max_status = 0); + void WorldWideLDoNUpdate(uint8 update_type, uint32 theme_id, int points = 1, uint8 min_status = 0, uint8 max_status = 0); void WorldWideMarquee(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char* message, uint8 min_status = 0, uint8 max_status = 0); void WorldWideMessage(uint32 type, const char* message, uint8 min_status = 0, uint8 max_status = 0); void WorldWideMove(uint8 update_type, const char* zone_short_name, uint16 instance_id = 0, uint8 min_status = 0, uint8 max_status = 0); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 98fe2a0f3..36d1b7c22 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -56,6 +56,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "zone_reload.h" #include "../common/shared_tasks.h" #include "shared_task_zone_messaging.h" +#include "dialogue_window.h" extern EntityList entity_list; extern Zone* zone; @@ -1887,25 +1888,17 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } break; } - case ServerOP_CZSpell: + case ServerOP_CZDialogueWindow: { - CZSpell_Struct* CZS = (CZSpell_Struct*) pack->pBuffer; - uint8 update_type = CZS->update_type; - uint8 update_subtype = CZS->update_subtype; - int update_identifier = CZS->update_identifier; - uint32 spell_id = CZS->spell_id; - const char* client_name = CZS->client_name; + CZDialogueWindow_Struct* CZDW = (CZDialogueWindow_Struct*) pack->pBuffer; + uint8 update_type = CZDW->update_type; + int update_identifier = CZDW->update_identifier; + std::string message = CZDW->message; + const char* client_name = CZDW->client_name; if (update_type == CZUpdateType_Character) { auto client = entity_list.GetClientByCharID(update_identifier); if (client) { - switch (update_subtype) { - case CZSpellUpdateSubtype_Cast: - client->SpellFinished(spell_id, client); - break; - case CZSpellUpdateSubtype_Remove: - client->BuffFadeBySpellID(spell_id); - break; - } + DialogueWindow::Render(client, message); } } else if (update_type == CZUpdateType_Group) { auto client_group = entity_list.GetGroupByID(update_identifier); @@ -1913,143 +1906,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { auto group_member = client_group->members[member_index]->CastToClient(); - switch (update_subtype) { - case CZSpellUpdateSubtype_Cast: - group_member->SpellFinished(spell_id, group_member); - break; - case CZSpellUpdateSubtype_Remove: - group_member->BuffFadeBySpellID(spell_id); - break; - } - } - } - } - } else if (update_type == CZUpdateType_Raid) { - auto client_raid = entity_list.GetRaidByID(update_identifier); - if (client_raid) { - for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { - if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { - auto raid_member = client_raid->members[member_index].member->CastToClient(); - switch (update_subtype) { - case CZSpellUpdateSubtype_Cast: - raid_member->SpellFinished(spell_id, raid_member); - break; - case CZSpellUpdateSubtype_Remove: - raid_member->BuffFadeBySpellID(spell_id); - break; - } - } - } - } - } else if (update_type == CZUpdateType_Guild) { - for (auto &client: entity_list.GetClientList()) { - if (client.second->GuildID() > 0 && client.second->GuildID() == update_identifier) { - switch (update_subtype) { - case CZSpellUpdateSubtype_Cast: - client.second->SpellFinished(spell_id, client.second); - break; - case CZSpellUpdateSubtype_Remove: - client.second->BuffFadeBySpellID(spell_id); - break; - } - } - } - } else if (update_type == CZUpdateType_Expedition) { - for (auto &client: entity_list.GetClientList()) { - if (client.second->GetExpedition() && client.second->GetExpedition()->GetID() == update_identifier) { - switch (update_subtype) { - case CZSpellUpdateSubtype_Cast: - client.second->SpellFinished(spell_id, client.second); - break; - case CZSpellUpdateSubtype_Remove: - client.second->BuffFadeBySpellID(spell_id); - break; - } - } - } - } else if (update_type == CZUpdateType_ClientName) { - auto client = entity_list.GetClientByName(client_name); - if (client) { - switch (update_subtype) { - case CZSpellUpdateSubtype_Cast: - client->SpellFinished(spell_id, client); - break; - case CZSpellUpdateSubtype_Remove: - client->BuffFadeBySpellID(spell_id); - break; - } - } - } - break; - } - case ServerOP_CZTaskUpdate: - { - CZTaskUpdate_Struct* CZTU = (CZTaskUpdate_Struct*) pack->pBuffer; - uint8 update_type = CZTU->update_type; - uint8 update_subtype = CZTU->update_subtype; - int update_identifier = CZTU->update_identifier; - uint32 task_identifier = CZTU->task_identifier; - int task_subidentifier = CZTU->task_subidentifier; - int update_count = CZTU->update_count; - bool enforce_level_requirement = CZTU->enforce_level_requirement; - const char* client_name = CZTU->client_name; - if (update_type == CZUpdateType_Character) { - auto client = entity_list.GetClientByCharID(update_identifier); - if (client) { - switch (update_subtype) { - case CZTaskUpdateSubtype_ActivityReset: - client->ResetTaskActivity(task_identifier, task_subidentifier); - break; - case CZTaskUpdateSubtype_ActivityUpdate: - client->UpdateTaskActivity(task_identifier, task_subidentifier, update_count); - break; - case CZTaskUpdateSubtype_AssignTask: - client->AssignTask(task_identifier, task_subidentifier, enforce_level_requirement); - break; - case CZTaskUpdateSubtype_DisableTask: - client->DisableTask(1, reinterpret_cast(task_identifier)); - break; - case CZTaskUpdateSubtype_EnableTask: - client->EnableTask(1, reinterpret_cast(task_identifier)); - break; - case CZTaskUpdateSubtype_FailTask: - client->FailTask(task_identifier); - break; - case CZTaskUpdateSubtype_RemoveTask: - client->RemoveTaskByTaskID(task_identifier); - break; - } - } - break; - } else if (update_type == CZUpdateType_Group) { - auto client_group = entity_list.GetGroupByID(update_identifier); - if (client_group) { - for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { - if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { - auto group_member = client_group->members[member_index]->CastToClient(); - switch (update_subtype) { - case CZTaskUpdateSubtype_ActivityReset: - group_member->ResetTaskActivity(task_identifier, task_subidentifier); - break; - case CZTaskUpdateSubtype_ActivityUpdate: - group_member->UpdateTaskActivity(task_identifier, task_subidentifier, update_count); - break; - case CZTaskUpdateSubtype_AssignTask: - group_member->AssignTask(task_identifier, task_subidentifier, enforce_level_requirement); - break; - case CZTaskUpdateSubtype_DisableTask: - group_member->DisableTask(1, reinterpret_cast(task_identifier)); - break; - case CZTaskUpdateSubtype_EnableTask: - group_member->EnableTask(1, reinterpret_cast(task_identifier)); - break; - case CZTaskUpdateSubtype_FailTask: - group_member->FailTask(task_identifier); - break; - case CZTaskUpdateSubtype_RemoveTask: - group_member->RemoveTaskByTaskID(task_identifier); - break; - } + DialogueWindow::Render(group_member, message); } } } @@ -2059,114 +1916,26 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { auto raid_member = client_raid->members[member_index].member->CastToClient(); - switch (update_subtype) { - case CZTaskUpdateSubtype_ActivityReset: - raid_member->ResetTaskActivity(task_identifier, task_subidentifier); - break; - case CZTaskUpdateSubtype_ActivityUpdate: - raid_member->UpdateTaskActivity(task_identifier, task_subidentifier, update_count); - break; - case CZTaskUpdateSubtype_AssignTask: - raid_member->AssignTask(task_identifier, task_subidentifier, enforce_level_requirement); - break; - case CZTaskUpdateSubtype_DisableTask: - raid_member->DisableTask(1, reinterpret_cast(task_identifier)); - break; - case CZTaskUpdateSubtype_EnableTask: - raid_member->EnableTask(1, reinterpret_cast(task_identifier)); - break; - case CZTaskUpdateSubtype_FailTask: - raid_member->FailTask(task_identifier); - break; - case CZTaskUpdateSubtype_RemoveTask: - raid_member->RemoveTaskByTaskID(task_identifier); - break; - } + DialogueWindow::Render(raid_member, message); } } } } else if (update_type == CZUpdateType_Guild) { for (auto &client: entity_list.GetClientList()) { if (client.second->GuildID() > 0 && client.second->GuildID() == update_identifier) { - switch (update_subtype) { - case CZTaskUpdateSubtype_ActivityReset: - client.second->ResetTaskActivity(task_identifier, task_subidentifier); - break; - case CZTaskUpdateSubtype_ActivityUpdate: - client.second->UpdateTaskActivity(task_identifier, task_subidentifier, update_count); - break; - case CZTaskUpdateSubtype_AssignTask: - client.second->AssignTask(task_identifier, task_subidentifier, enforce_level_requirement); - break; - case CZTaskUpdateSubtype_DisableTask: - client.second->DisableTask(1, reinterpret_cast(task_identifier)); - break; - case CZTaskUpdateSubtype_EnableTask: - client.second->EnableTask(1, reinterpret_cast(task_identifier)); - break; - case CZTaskUpdateSubtype_FailTask: - client.second->FailTask(task_identifier); - break; - case CZTaskUpdateSubtype_RemoveTask: - client.second->RemoveTaskByTaskID(task_identifier); - break; - } + DialogueWindow::Render(client.second, message); } - } + } } else if (update_type == CZUpdateType_Expedition) { for (auto &client: entity_list.GetClientList()) { if (client.second->GetExpedition() && client.second->GetExpedition()->GetID() == update_identifier) { - switch (update_subtype) { - case CZTaskUpdateSubtype_ActivityReset: - client.second->ResetTaskActivity(task_identifier, task_subidentifier); - break; - case CZTaskUpdateSubtype_ActivityUpdate: - client.second->UpdateTaskActivity(task_identifier, task_subidentifier, update_count); - break; - case CZTaskUpdateSubtype_AssignTask: - client.second->AssignTask(task_identifier, task_subidentifier, enforce_level_requirement); - break; - case CZTaskUpdateSubtype_DisableTask: - client.second->DisableTask(1, reinterpret_cast(task_identifier)); - break; - case CZTaskUpdateSubtype_EnableTask: - client.second->EnableTask(1, reinterpret_cast(task_identifier)); - break; - case CZTaskUpdateSubtype_FailTask: - client.second->FailTask(task_identifier); - break; - case CZTaskUpdateSubtype_RemoveTask: - client.second->RemoveTaskByTaskID(task_identifier); - break; - } + DialogueWindow::Render(client.second, message); } } } else if (update_type == CZUpdateType_ClientName) { auto client = entity_list.GetClientByName(client_name); if (client) { - switch (update_subtype) { - case CZTaskUpdateSubtype_ActivityReset: - client->ResetTaskActivity(task_identifier, task_subidentifier); - break; - case CZTaskUpdateSubtype_ActivityUpdate: - client->UpdateTaskActivity(task_identifier, task_subidentifier, update_count); - break; - case CZTaskUpdateSubtype_AssignTask: - client->AssignTask(task_identifier, task_subidentifier, enforce_level_requirement); - break; - case CZTaskUpdateSubtype_DisableTask: - client->DisableTask(1, reinterpret_cast(task_identifier)); - break; - case CZTaskUpdateSubtype_EnableTask: - client->EnableTask(1, reinterpret_cast(task_identifier)); - break; - case CZTaskUpdateSubtype_FailTask: - client->FailTask(task_identifier); - break; - case CZTaskUpdateSubtype_RemoveTask: - client->RemoveTaskByTaskID(task_identifier); - break; - } + DialogueWindow::Render(client, message); } } break; @@ -2620,6 +2389,303 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } break; } + case ServerOP_CZSpell: + { + CZSpell_Struct* CZS = (CZSpell_Struct*) pack->pBuffer; + uint8 update_type = CZS->update_type; + uint8 update_subtype = CZS->update_subtype; + int update_identifier = CZS->update_identifier; + uint32 spell_id = CZS->spell_id; + const char* client_name = CZS->client_name; + if (update_type == CZUpdateType_Character) { + auto client = entity_list.GetClientByCharID(update_identifier); + if (client) { + switch (update_subtype) { + case CZSpellUpdateSubtype_Cast: + client->SpellFinished(spell_id, client); + break; + case CZSpellUpdateSubtype_Remove: + client->BuffFadeBySpellID(spell_id); + break; + } + } + } else if (update_type == CZUpdateType_Group) { + auto client_group = entity_list.GetGroupByID(update_identifier); + if (client_group) { + for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { + if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { + auto group_member = client_group->members[member_index]->CastToClient(); + switch (update_subtype) { + case CZSpellUpdateSubtype_Cast: + group_member->SpellFinished(spell_id, group_member); + break; + case CZSpellUpdateSubtype_Remove: + group_member->BuffFadeBySpellID(spell_id); + break; + } + } + } + } + } else if (update_type == CZUpdateType_Raid) { + auto client_raid = entity_list.GetRaidByID(update_identifier); + if (client_raid) { + for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { + if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { + auto raid_member = client_raid->members[member_index].member->CastToClient(); + switch (update_subtype) { + case CZSpellUpdateSubtype_Cast: + raid_member->SpellFinished(spell_id, raid_member); + break; + case CZSpellUpdateSubtype_Remove: + raid_member->BuffFadeBySpellID(spell_id); + break; + } + } + } + } + } else if (update_type == CZUpdateType_Guild) { + for (auto &client: entity_list.GetClientList()) { + if (client.second->GuildID() > 0 && client.second->GuildID() == update_identifier) { + switch (update_subtype) { + case CZSpellUpdateSubtype_Cast: + client.second->SpellFinished(spell_id, client.second); + break; + case CZSpellUpdateSubtype_Remove: + client.second->BuffFadeBySpellID(spell_id); + break; + } + } + } + } else if (update_type == CZUpdateType_Expedition) { + for (auto &client: entity_list.GetClientList()) { + if (client.second->GetExpedition() && client.second->GetExpedition()->GetID() == update_identifier) { + switch (update_subtype) { + case CZSpellUpdateSubtype_Cast: + client.second->SpellFinished(spell_id, client.second); + break; + case CZSpellUpdateSubtype_Remove: + client.second->BuffFadeBySpellID(spell_id); + break; + } + } + } + } else if (update_type == CZUpdateType_ClientName) { + auto client = entity_list.GetClientByName(client_name); + if (client) { + switch (update_subtype) { + case CZSpellUpdateSubtype_Cast: + client->SpellFinished(spell_id, client); + break; + case CZSpellUpdateSubtype_Remove: + client->BuffFadeBySpellID(spell_id); + break; + } + } + } + break; + } + case ServerOP_CZTaskUpdate: + { + CZTaskUpdate_Struct* CZTU = (CZTaskUpdate_Struct*) pack->pBuffer; + uint8 update_type = CZTU->update_type; + uint8 update_subtype = CZTU->update_subtype; + int update_identifier = CZTU->update_identifier; + uint32 task_identifier = CZTU->task_identifier; + int task_subidentifier = CZTU->task_subidentifier; + int update_count = CZTU->update_count; + bool enforce_level_requirement = CZTU->enforce_level_requirement; + const char* client_name = CZTU->client_name; + if (update_type == CZUpdateType_Character) { + auto client = entity_list.GetClientByCharID(update_identifier); + if (client) { + switch (update_subtype) { + case CZTaskUpdateSubtype_ActivityReset: + client->ResetTaskActivity(task_identifier, task_subidentifier); + break; + case CZTaskUpdateSubtype_ActivityUpdate: + client->UpdateTaskActivity(task_identifier, task_subidentifier, update_count); + break; + case CZTaskUpdateSubtype_AssignTask: + client->AssignTask(task_identifier, task_subidentifier, enforce_level_requirement); + break; + case CZTaskUpdateSubtype_DisableTask: + client->DisableTask(1, reinterpret_cast(task_identifier)); + break; + case CZTaskUpdateSubtype_EnableTask: + client->EnableTask(1, reinterpret_cast(task_identifier)); + break; + case CZTaskUpdateSubtype_FailTask: + client->FailTask(task_identifier); + break; + case CZTaskUpdateSubtype_RemoveTask: + client->RemoveTaskByTaskID(task_identifier); + break; + } + } + break; + } else if (update_type == CZUpdateType_Group) { + auto client_group = entity_list.GetGroupByID(update_identifier); + if (client_group) { + for (int member_index = 0; member_index < MAX_GROUP_MEMBERS; member_index++) { + if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { + auto group_member = client_group->members[member_index]->CastToClient(); + switch (update_subtype) { + case CZTaskUpdateSubtype_ActivityReset: + group_member->ResetTaskActivity(task_identifier, task_subidentifier); + break; + case CZTaskUpdateSubtype_ActivityUpdate: + group_member->UpdateTaskActivity(task_identifier, task_subidentifier, update_count); + break; + case CZTaskUpdateSubtype_AssignTask: + group_member->AssignTask(task_identifier, task_subidentifier, enforce_level_requirement); + break; + case CZTaskUpdateSubtype_DisableTask: + group_member->DisableTask(1, reinterpret_cast(task_identifier)); + break; + case CZTaskUpdateSubtype_EnableTask: + group_member->EnableTask(1, reinterpret_cast(task_identifier)); + break; + case CZTaskUpdateSubtype_FailTask: + group_member->FailTask(task_identifier); + break; + case CZTaskUpdateSubtype_RemoveTask: + group_member->RemoveTaskByTaskID(task_identifier); + break; + } + } + } + } + } else if (update_type == CZUpdateType_Raid) { + auto client_raid = entity_list.GetRaidByID(update_identifier); + if (client_raid) { + for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { + if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { + auto raid_member = client_raid->members[member_index].member->CastToClient(); + switch (update_subtype) { + case CZTaskUpdateSubtype_ActivityReset: + raid_member->ResetTaskActivity(task_identifier, task_subidentifier); + break; + case CZTaskUpdateSubtype_ActivityUpdate: + raid_member->UpdateTaskActivity(task_identifier, task_subidentifier, update_count); + break; + case CZTaskUpdateSubtype_AssignTask: + raid_member->AssignTask(task_identifier, task_subidentifier, enforce_level_requirement); + break; + case CZTaskUpdateSubtype_DisableTask: + raid_member->DisableTask(1, reinterpret_cast(task_identifier)); + break; + case CZTaskUpdateSubtype_EnableTask: + raid_member->EnableTask(1, reinterpret_cast(task_identifier)); + break; + case CZTaskUpdateSubtype_FailTask: + raid_member->FailTask(task_identifier); + break; + case CZTaskUpdateSubtype_RemoveTask: + raid_member->RemoveTaskByTaskID(task_identifier); + break; + } + } + } + } + } else if (update_type == CZUpdateType_Guild) { + for (auto &client: entity_list.GetClientList()) { + if (client.second->GuildID() > 0 && client.second->GuildID() == update_identifier) { + switch (update_subtype) { + case CZTaskUpdateSubtype_ActivityReset: + client.second->ResetTaskActivity(task_identifier, task_subidentifier); + break; + case CZTaskUpdateSubtype_ActivityUpdate: + client.second->UpdateTaskActivity(task_identifier, task_subidentifier, update_count); + break; + case CZTaskUpdateSubtype_AssignTask: + client.second->AssignTask(task_identifier, task_subidentifier, enforce_level_requirement); + break; + case CZTaskUpdateSubtype_DisableTask: + client.second->DisableTask(1, reinterpret_cast(task_identifier)); + break; + case CZTaskUpdateSubtype_EnableTask: + client.second->EnableTask(1, reinterpret_cast(task_identifier)); + break; + case CZTaskUpdateSubtype_FailTask: + client.second->FailTask(task_identifier); + break; + case CZTaskUpdateSubtype_RemoveTask: + client.second->RemoveTaskByTaskID(task_identifier); + break; + } + } + } + } else if (update_type == CZUpdateType_Expedition) { + for (auto &client: entity_list.GetClientList()) { + if (client.second->GetExpedition() && client.second->GetExpedition()->GetID() == update_identifier) { + switch (update_subtype) { + case CZTaskUpdateSubtype_ActivityReset: + client.second->ResetTaskActivity(task_identifier, task_subidentifier); + break; + case CZTaskUpdateSubtype_ActivityUpdate: + client.second->UpdateTaskActivity(task_identifier, task_subidentifier, update_count); + break; + case CZTaskUpdateSubtype_AssignTask: + client.second->AssignTask(task_identifier, task_subidentifier, enforce_level_requirement); + break; + case CZTaskUpdateSubtype_DisableTask: + client.second->DisableTask(1, reinterpret_cast(task_identifier)); + break; + case CZTaskUpdateSubtype_EnableTask: + client.second->EnableTask(1, reinterpret_cast(task_identifier)); + break; + case CZTaskUpdateSubtype_FailTask: + client.second->FailTask(task_identifier); + break; + case CZTaskUpdateSubtype_RemoveTask: + client.second->RemoveTaskByTaskID(task_identifier); + break; + } + } + } + } else if (update_type == CZUpdateType_ClientName) { + auto client = entity_list.GetClientByName(client_name); + if (client) { + switch (update_subtype) { + case CZTaskUpdateSubtype_ActivityReset: + client->ResetTaskActivity(task_identifier, task_subidentifier); + break; + case CZTaskUpdateSubtype_ActivityUpdate: + client->UpdateTaskActivity(task_identifier, task_subidentifier, update_count); + break; + case CZTaskUpdateSubtype_AssignTask: + client->AssignTask(task_identifier, task_subidentifier, enforce_level_requirement); + break; + case CZTaskUpdateSubtype_DisableTask: + client->DisableTask(1, reinterpret_cast(task_identifier)); + break; + case CZTaskUpdateSubtype_EnableTask: + client->EnableTask(1, reinterpret_cast(task_identifier)); + break; + case CZTaskUpdateSubtype_FailTask: + client->FailTask(task_identifier); + break; + case CZTaskUpdateSubtype_RemoveTask: + client->RemoveTaskByTaskID(task_identifier); + break; + } + } + } + break; + } + case ServerOP_WWDialogueWindow: + { + WWDialogueWindow_Struct* WWDW = (WWDialogueWindow_Struct*) pack->pBuffer; + std::string message = WWDW->message; + uint8 min_status = WWDW->min_status; + uint8 max_status = WWDW->max_status; + for (auto &client : entity_list.GetClientList()) { + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + DialogueWindow::Render(client.second, message); + } + } + break; + } case ServerOP_WWLDoNUpdate: { WWLDoNUpdate_Struct* WWLU = (WWLDoNUpdate_Struct*) pack->pBuffer; From 6a962f25915ef1473729a5cbca76e4de5f30bfd7 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 12 Oct 2021 15:30:36 -0400 Subject: [PATCH 264/624] [Spells] Update SPA158 Reflect (#1590) * update * updates * updates * update * update * Update ruletypes.h * Apply extra spell dmg Mob with the reflect effect apply its Extra Spell Damage from item stat to the reflected spell. Updated portion of formula for extra damage based on live parsing. * correct formula --- common/ruletypes.h | 2 +- common/spdat.h | 11 +++- zone/bonuses.cpp | 28 +++++++-- zone/common.h | 5 +- zone/effects.cpp | 42 ++++++++++--- zone/lua_stat_bonuses.cpp | 2 +- zone/mob.cpp | 17 +----- zone/mob.h | 6 +- zone/special_attacks.cpp | 2 +- zone/spell_effects.cpp | 9 ++- zone/spells.cpp | 120 ++++++++++++++++++++++++-------------- 11 files changed, 164 insertions(+), 80 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 7f463e733..40682abcd 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -319,7 +319,7 @@ RULE_INT(Spells, MaxDiscSlotsNPC, 0, "Maximum number of NPC disc slots. NPC don' RULE_INT(Spells, MaxTotalSlotsNPC, 60, "Maximum total of NPC slots. The default value is the limit of the Titanium client") RULE_INT(Spells, MaxTotalSlotsPET, 30, "Maximum total of pet slots. The default value is the limit of the Titanium client") RULE_BOOL (Spells, EnableBlockedBuffs, true, "Allow blocked spells") -RULE_INT(Spells, ReflectType, 3, "Reflect type. 0=disabled, 1=single target player spells only, 2=all player spells, 3=all single target spells, 4=all spells") +RULE_INT(Spells, ReflectType, 4, "Reflect type. 0=disabled, 1=single target player spells only, 2=all player spells, 3=all single target spells, 4=all spells") RULE_BOOL(Spells, ReflectMessagesClose, true, "True (Live functionality) is for Reflect messages to show to players within close proximity. False shows just player reflecting") RULE_INT(Spells, VirusSpreadDistance, 30, "The distance a viral spell will jump to its next victim") RULE_BOOL(Spells, LiveLikeFocusEffects, true, "Determines whether specific healing, dmg and mana reduction focuses are randomized") diff --git a/common/spdat.h b/common/spdat.h index 509170000..ebd39eed4 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -511,6 +511,15 @@ enum NegateSpellEffectType NEGATE_SPA_SPELLBONUS_AND_AABONUS = 5, NEGATE_SPA_ITEMBONUS_AND_AABONUS = 6, }; +//Used for rule RuleI(Spells, ReflectType)) +enum ReflectSpellType +{ + REFLECT_DISABLED = 0, + REFLECT_SINGLE_TARGET_SPELLS_ONLY = 1, + REFLECT_ALL_PLAYER_SPELLS = 2, + RELFECT_ALL_SINGLE_TARGET_SPELLS = 3, + REFLECT_ALL_SPELLS = 4, +}; enum SpellTypes : uint32 { @@ -843,7 +852,7 @@ typedef enum { #define SE_SpellCritDmgIncrease 155 // implemented - no known live spells use this currently #define SE_IllusionCopy 156 // implemented - Deception #define SE_SpellDamageShield 157 // implemented, @DS, causes non-melee damage on caster of a spell, base: Amt DS (negative), limit: none, max: unknown (same as base but +) -#define SE_Reflect 158 // implemented +#define SE_Reflect 158 // implemented, @SpellMisc, reflect casted detrimental spell back at caster, base: chance pct, limit: resist modifier (positive value reduces resists), max: pct of base dmg mod (50=50pct of base) #define SE_AllStats 159 // implemented //#define SE_MakeDrunk 160 // *not implemented - Effect works entirely client side (Should check against tolerance) #define SE_MitigateSpellDamage 161 // implemented - rune with max value diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 9a640196a..e56de4daf 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1672,6 +1672,17 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) newbon->ZoneSuspendMinion = base1; break; + + case SE_Reflect: + + if (newbon->reflect[SBIndex::REFLECT_CHANCE] < base1) { + newbon->reflect[SBIndex::REFLECT_CHANCE] = base1; + } + if (newbon->reflect[SBIndex::REFLECT_RESISTANCE_MOD] < base2) { + newbon->reflect[SBIndex::REFLECT_RESISTANCE_MOD] = base2; + } + break; + case SE_SpellDamageShield: newbon->SpellDamageShield += base1; break; @@ -2152,7 +2163,16 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne } case SE_Reflect: - new_bonus->reflect_chance += effect_value; + + if (AdditiveWornBonus) { + new_bonus->reflect[SBIndex::REFLECT_CHANCE] += effect_value; + } + + else if (new_bonus->reflect[SBIndex::REFLECT_CHANCE] < effect_value) { + new_bonus->reflect[SBIndex::REFLECT_CHANCE] = effect_value; + new_bonus->reflect[SBIndex::REFLECT_RESISTANCE_MOD] = base2; + new_bonus->reflect[SBIndex::REFLECT_DMG_EFFECTIVENESS] = max; + } break; case SE_Amplification: @@ -4381,9 +4401,9 @@ void Mob::NegateSpellEffectBonuses(uint16 spell_id) break; case SE_Reflect: - if (negate_spellbonus) { spellbonuses.reflect_chance = effect_value; } - if (negate_aabonus) { aabonuses.reflect_chance = effect_value; } - if (negate_itembonus) { itembonuses.reflect_chance = effect_value; } + if (negate_spellbonus) { spellbonuses.reflect[SBIndex::REFLECT_CHANCE] = effect_value; } + if (negate_aabonus) { aabonuses.reflect[SBIndex::REFLECT_CHANCE] = effect_value; } + if (negate_itembonus) { itembonuses.reflect[SBIndex::REFLECT_CHANCE] = effect_value; } break; case SE_Amplification: diff --git a/zone/common.h b/zone/common.h index 4c5bfa4df..937cffc51 100644 --- a/zone/common.h +++ b/zone/common.h @@ -404,7 +404,7 @@ struct StatBonuses { int32 skillmodmax[EQ::skills::HIGHEST_SKILL + 1]; int effective_casting_level; int adjusted_casting_skill; // SPA 112 for fizzles - int reflect_chance; // chance to reflect incoming spell + int reflect[3]; // chance to reflect incoming spell [0]=Chance [1]=Resist Mod [2]= % of Base Dmg uint32 singingMod; uint32 Amplification; // stacks with singingMod uint32 brassMod; @@ -680,6 +680,9 @@ namespace SBIndex { constexpr uint16 FINISHING_EFFECT_LEVEL_CHANCE_BONUS = 1; // SPA 440, 345, 346 constexpr uint16 DOUBLE_MELEE_ROUND_CHANCE = 0; // SPA 471 constexpr uint16 DOUBLE_MELEE_ROUND_DMG_BONUS = 1; // SPA 471 + constexpr uint16 REFLECT_CHANCE = 0; // SPA 158 + constexpr uint16 REFLECT_RESISTANCE_MOD = 1; // SPA 158 + constexpr uint16 REFLECT_DMG_EFFECTIVENESS = 2; // SPA 158 }; diff --git a/zone/effects.cpp b/zone/effects.cpp index b7319e793..4180d774c 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -117,10 +117,10 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { value -= GetFocusEffect(focusFcAmplifyAmt, spell_id); if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg) - value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value)*ratio / 100; + value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value_BaseEffect)*ratio / 100; else if(!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) - value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value)*ratio/100; + value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value_BaseEffect)*ratio/100; else if (IsNPC() && CastToNPC()->GetSpellScale()) value = int(static_cast(value) * CastToNPC()->GetSpellScale() / 100.0f); @@ -156,10 +156,10 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { value -= GetFocusEffect(focusFcAmplifyAmt, spell_id); if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg) - value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); + value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value_BaseEffect); else if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) - value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); + value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value_BaseEffect); if (IsNPC() && CastToNPC()->GetSpellScale()) value = int(static_cast(value) * CastToNPC()->GetSpellScale() / 100.0f); @@ -167,6 +167,33 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { return value; } +int32 Mob::GetActReflectedSpellDamage(int32 spell_id, int32 value, int effectiveness) { + /* + Reflected spells use the spells base damage before any modifiers or formulas applied. + That value can then be modifier by the reflect spells 'max' value, defined here as effectiveness + Default effectiveness is set at 100. + Extra Spell Damage stat from the with the reflect effect will be applied to reflected damage + with no level limitation, this was confirmed with extensive parsing ~Kayen + */ + if (IsNPC()) { + value += value * CastToNPC()->GetSpellFocusDMG() / 100; + + if (CastToNPC()->GetSpellScale()) { + value = int(static_cast(value) * CastToNPC()->GetSpellScale() / 100.0f); + } + } + + int32 base_spell_dmg = value; + + value = value * effectiveness / 100; + + if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg) { + value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_spell_dmg); + } + + return value; +} + int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) { if (target == nullptr) @@ -259,8 +286,9 @@ int32 Mob::GetExtraSpellAmt(uint16 spell_id, int32 extra_spell_amt, int32 base_s else extra_spell_amt = extra_spell_amt * total_cast_time / 7000; + //Confirmed with parsing 10/9/21 ~Kayen if (extra_spell_amt * 2 > abs(base_spell_dmg)) { - return 0; + extra_spell_amt = abs(base_spell_dmg) / 2; } return extra_spell_amt; @@ -324,7 +352,7 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { value += GetFocusEffect(focusFcHealAmtCrit, spell_id); //SPA 396 Add before critical if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) { - value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value); //Item Heal Amt Add before critical + value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value_BaseEffect); //Item Heal Amt Add before critical } if (target) { @@ -1020,7 +1048,7 @@ void EntityList::AESpell( } current_mob->CalcSpellPowerDistanceMod(spell_id, distance_to_target); - caster_mob->SpellOnTarget(spell_id, current_mob, false, true, resist_adjust); + caster_mob->SpellOnTarget(spell_id, current_mob, 0, true, resist_adjust); /** * Increment hit count if max targets diff --git a/zone/lua_stat_bonuses.cpp b/zone/lua_stat_bonuses.cpp index 40805d648..156a320f9 100644 --- a/zone/lua_stat_bonuses.cpp +++ b/zone/lua_stat_bonuses.cpp @@ -342,7 +342,7 @@ int Lua_StatBonuses::Getadjusted_casting_skill() const { int Lua_StatBonuses::Getreflect_chance() const { Lua_Safe_Call_Int(); - return self->reflect_chance; + return self->reflect[SBIndex::REFLECT_CHANCE]; } uint32 Lua_StatBonuses::GetsingingMod() const { diff --git a/zone/mob.cpp b/zone/mob.cpp index d1c6ced3d..f0982115c 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3214,12 +3214,12 @@ void Mob::ExecWeaponProc(const EQ::ItemInstance *inst, uint16 spell_id, Mob *on, if (IsBeneficialSpell(spell_id) && (!IsNPC() || (IsNPC() && CastToNPC()->GetInnateProcSpellID() != spell_id))) { // NPC innate procs don't take this path ever SpellFinished(spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff, true, level_override); if(twinproc) - SpellOnTarget(spell_id, this, false, false, 0, true, level_override); + SpellOnTarget(spell_id, this, 0, false, 0, true, level_override); } else if(!(on->IsClient() && on->CastToClient()->dead)) { //dont proc on dead clients SpellFinished(spell_id, on, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff, true, level_override); if(twinproc) - SpellOnTarget(spell_id, on, false, false, 0, true, level_override); + SpellOnTarget(spell_id, on, 0, false, 0, true, level_override); } return; } @@ -4596,19 +4596,6 @@ bool Mob::TryDoubleMeleeRoundEffect() { return false; } -bool Mob::TryReflectSpell(uint32 spell_id) -{ - if (!spells[spell_id].reflectable) - return false; - - int chance = itembonuses.reflect_chance + spellbonuses.reflect_chance + aabonuses.reflect_chance; - - if(chance && zone->random.Roll(chance)) - return true; - - return false; -} - void Mob::DoGravityEffect() { Mob *caster = nullptr; diff --git a/zone/mob.h b/zone/mob.h index b9e3bfd85..3d207ee26 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -306,6 +306,7 @@ public: virtual int32 GetActSpellCost(uint16 spell_id, int32 cost){ return cost;} virtual int32 GetActSpellDuration(uint16 spell_id, int32 duration); virtual int32 GetActSpellCasttime(uint16 spell_id, int32 casttime); + virtual int32 GetActReflectedSpellDamage(int32 spell_id, int32 value, int effectiveness); float ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override = false, int resist_override = 0, bool CharismaCheck = false, bool CharmTick = false, bool IsRoot = false, int level_override = -1); @@ -331,9 +332,9 @@ public: bool SpellFinished(uint16 spell_id, Mob *target, EQ::spells::CastingSlot slot = EQ::spells::CastingSlot::Item, uint16 mana_used = 0, uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false, int level_override = -1); void SendBeginCast(uint16 spell_id, uint32 casttime); - virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect = false, + virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar, int reflect_effectiveness = 0, bool use_resist_adjust = false, int16 resist_adjust = 0, bool isproc = false, int level_override = -1); - virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100, int level_override = -1); + virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100, int level_override = -1, int reflect_effectiveness = 0); virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, EQ::spells::CastingSlot slot, bool isproc = false); virtual bool CheckFizzle(uint16 spell_id); @@ -827,7 +828,6 @@ public: int GetCriticalChanceBonus(uint16 skill); int16 GetSkillDmgAmt(uint16 skill); int16 GetPositionalDmgAmt(Mob* defender); - bool TryReflectSpell(uint32 spell_id); inline bool CanBlockSpell() const { return(spellbonuses.FocusEffects[focusBlockNextSpell]); } bool DoHPToManaCovert(uint16 mana_cost = 0); int32 ApplySpellEffectiveness(int16 spell_id, int32 value, bool IsBard = false, uint16 caster_id=0); diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 4d614731a..1aa87288c 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -2043,7 +2043,7 @@ void Mob::InstillDoubt(Mob *who) { //temporary hack... //cast fear on them... should prolly be a different spell //and should be un-resistable. - SpellOnTarget(229, who, false, true, -2000); + SpellOnTarget(229, who, 0, true, -2000); //is there a success message? } else { MessageString(Chat::LightBlue,NOT_SCARING); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 088d4b2a2..5caab6d0d 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -46,7 +46,7 @@ extern WorldServer worldserver; // the spell can still fail here, if the buff can't stack // in this case false will be returned, true otherwise -bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_override) +bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_override, int reflect_effectiveness) { int caster_level, buffslot, effect, effect_value, i; EQ::ItemInstance *SummonedItem=nullptr; @@ -259,7 +259,12 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove //handles AAs and what not... if(caster) { - dmg = caster->GetActSpellDamage(spell_id, dmg, this); + if (reflect_effectiveness) { + dmg = caster->GetActReflectedSpellDamage(spell_id, (int32)(spells[spell_id].base[i] * partial / 100), reflect_effectiveness); + } + else { + dmg = caster->GetActSpellDamage(spell_id, dmg, this); + } caster->ResourceTap(-dmg, spell_id); } diff --git a/zone/spells.cpp b/zone/spells.cpp index 97caa49c7..b7affc35f 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2274,14 +2274,14 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui return(false); } if (isproc) { - SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, true, level_override); + SpellOnTarget(spell_id, spell_target, 0, true, resist_adjust, true, level_override); } else { if (spells[spell_id].targettype == ST_TargetOptional){ if (!TrySpellProjectile(spell_target, spell_id)) return false; } - else if(!SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, false, level_override)) { + else if(!SpellOnTarget(spell_id, spell_target, 0, true, resist_adjust, false, level_override)) { if(IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id)) { // Prevent mana usage/timers being set for beneficial buffs if(casting_spell_aa_id) @@ -3513,7 +3513,7 @@ int Mob::CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite) // and if you don't want effects just return false. interrupting here will // break stuff // -bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_resist_adjust, int16 resist_adjust, +bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectiveness, bool use_resist_adjust, int16 resist_adjust, bool isproc, int level_override) { @@ -3641,7 +3641,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r parse->EventPlayer(EVENT_CAST_ON, spelltar->CastToClient(),temp1, 0); } - mod_spell_cast(spell_id, spelltar, reflect, use_resist_adjust, resist_adjust, isproc); + mod_spell_cast(spell_id, spelltar, reflect_effectiveness, use_resist_adjust, resist_adjust, isproc); // now check if the spell is allowed to land if (RuleB(Spells, EnableBlockedBuffs)) { @@ -3860,69 +3860,101 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r } } } - // Reflect - if(spelltar && spelltar->TryReflectSpell(spell_id) && !reflect && IsDetrimentalSpell(spell_id) && this != spelltar) { - int reflect_chance = 0; + /* + Reflect + base= % Chance to Reflect + Limit= Resist Modifier (+Value for decrease chance to resist) + Max= % of base spell damage (this is the base before any formula or focus is applied) + On live any type of detrimental spell can be reflected as long as the Reflectable spell field is set, this includes AOE. + The 'caster' of the reflected spell is owner of the reflect effect. Caster's focus effects are NOT applied to reflected spell. + + reflect_effectiveness is applied to damage spells, a value of 100 is no change to base damage. Other values change by percent. (50=50% of damage) + we this variable to both check if a spell being applied is from a reflection and for the damage modifier. + + There are a few spells in database that are not detrimental that have Reflectable field set, however from testing, they do not actually reflect. + */ + if(spells[spell_id].reflectable && !reflect_effectiveness && spelltar && this != spelltar && IsDetrimentalSpell(spell_id) && + (spelltar->spellbonuses.reflect[SBIndex::REFLECT_CHANCE] || spelltar->aabonuses.reflect[SBIndex::REFLECT_CHANCE] || spelltar->itembonuses.reflect[SBIndex::REFLECT_CHANCE])) { + bool can_spell_reflect = false; switch(RuleI(Spells, ReflectType)) { - case 0: + case REFLECT_DISABLED: break; - case 1: + case REFLECT_SINGLE_TARGET_SPELLS_ONLY: { if(spells[spell_id].targettype == ST_Target) { for(int y = 0; y < 16; y++) { - if(spells[spell_id].classes[y] < 255) - reflect_chance = 1; + if (spells[spell_id].classes[y] < 255) { + can_spell_reflect = true; + } } } break; } - case 2: + case REFLECT_ALL_PLAYER_SPELLS: { for(int y = 0; y < 16; y++) { - if(spells[spell_id].classes[y] < 255) - reflect_chance = 1; + if (spells[spell_id].classes[y] < 255) { + can_spell_reflect = true; + } } break; } - case 3: + case RELFECT_ALL_SINGLE_TARGET_SPELLS: { - if(spells[spell_id].targettype == ST_Target) - reflect_chance = 1; - + if (spells[spell_id].targettype == ST_Target) { + can_spell_reflect = true; + } break; } - case 4: - reflect_chance = 1; + case REFLECT_ALL_SPELLS: //This is live like behavior + can_spell_reflect = true; default: break; } - if (reflect_chance) { + if (can_spell_reflect) { - if (RuleB(Spells, ReflectMessagesClose)) { - entity_list.MessageCloseString( - this, /* Sender */ - false, /* Skip Sender */ - RuleI(Range, SpellMessages), /* Range */ - Chat::Spells, /* Type */ - SPELL_REFLECT, /* String ID */ - GetCleanName(), /* Message 1 */ - spelltar->GetCleanName() /* Message 2 */ - ); + int reflect_resist_adjust = 0; + int reflect_effectiveness_mod = 0; //Need value of 100 to do baseline unmodified damage. + + if (spelltar->spellbonuses.reflect[SBIndex::REFLECT_CHANCE] && zone->random.Roll(spelltar->spellbonuses.reflect[SBIndex::REFLECT_CHANCE])) { + reflect_resist_adjust = spelltar->spellbonuses.reflect[SBIndex::REFLECT_RESISTANCE_MOD]; + reflect_effectiveness_mod = spelltar->spellbonuses.reflect[SBIndex::REFLECT_DMG_EFFECTIVENESS] ? spelltar->spellbonuses.reflect[SBIndex::REFLECT_DMG_EFFECTIVENESS] : 100; } - else { - MessageString(Chat::Spells, SPELL_REFLECT, GetCleanName(), spelltar->GetCleanName()); + else if (spelltar->aabonuses.reflect[SBIndex::REFLECT_CHANCE] && zone->random.Roll(spelltar->aabonuses.reflect[SBIndex::REFLECT_CHANCE])) { + reflect_effectiveness_mod = 100; + reflect_resist_adjust = spelltar->aabonuses.reflect[SBIndex::REFLECT_RESISTANCE_MOD]; + } + else if (spelltar->itembonuses.reflect[SBIndex::REFLECT_CHANCE] && zone->random.Roll(spelltar->itembonuses.reflect[SBIndex::REFLECT_CHANCE])) { + reflect_resist_adjust = spelltar->itembonuses.reflect[SBIndex::REFLECT_RESISTANCE_MOD]; + reflect_effectiveness_mod = spelltar->itembonuses.reflect[SBIndex::REFLECT_DMG_EFFECTIVENESS] ? spelltar->itembonuses.reflect[SBIndex::REFLECT_DMG_EFFECTIVENESS] : 100; } - CheckNumHitsRemaining(NumHit::ReflectSpell); - // caster actually appears to change - // ex. During OMM fight you click your reflect mask and you get the recourse from the reflected - // spell - spelltar->SpellOnTarget(spell_id, this, true, use_resist_adjust, resist_adjust); - safe_delete(action_packet); - return false; + if (reflect_effectiveness_mod) { + + if (RuleB(Spells, ReflectMessagesClose)) { + entity_list.MessageCloseString( + this, /* Sender */ + false, /* Skip Sender */ + RuleI(Range, SpellMessages), /* Range */ + Chat::Spells, /* Type */ + SPELL_REFLECT, /* String ID */ + GetCleanName(), /* Message 1 */ + spelltar->GetCleanName() /* Message 2 */ + ); + } + else { + MessageString(Chat::Spells, SPELL_REFLECT, GetCleanName(), spelltar->GetCleanName()); + } + + CheckNumHitsRemaining(NumHit::ReflectSpell); + + spelltar->SpellOnTarget(spell_id, this, reflect_effectiveness_mod, use_resist_adjust, (resist_adjust - reflect_resist_adjust)); + safe_delete(action_packet); + return false; + } } } @@ -4022,7 +4054,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r } // cause the effects to the target - if(!spelltar->SpellEffect(this, spell_id, spell_effectiveness, level_override)) + if(!spelltar->SpellEffect(this, spell_id, spell_effectiveness, level_override, reflect_effectiveness)) { // if SpellEffect returned false there's a problem applying the // spell. It's most likely a buff that can't stack. @@ -6071,7 +6103,7 @@ void Mob::BeamDirectional(uint16 spell_id, int16 resist_adjust) if (d <= spells[spell_id].aoerange) { if (CheckLosFN((*iter)) || spells[spell_id].npc_no_los) { (*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this); - SpellOnTarget(spell_id, (*iter), false, true, resist_adjust); + SpellOnTarget(spell_id, (*iter), 0, true, resist_adjust); maxtarget_count++; } @@ -6145,7 +6177,7 @@ void Mob::ConeDirectional(uint16 spell_id, int16 resist_adjust) (heading_to_target >= 0.0f && heading_to_target <= angle_end)) { if (CheckLosFN((*iter)) || spells[spell_id].npc_no_los) { (*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this); - SpellOnTarget(spell_id, (*iter), false, true, resist_adjust); + SpellOnTarget(spell_id, (*iter), 0, true, resist_adjust); maxtarget_count++; } } @@ -6153,7 +6185,7 @@ void Mob::ConeDirectional(uint16 spell_id, int16 resist_adjust) if (heading_to_target >= angle_start && heading_to_target <= angle_end) { if (CheckLosFN((*iter)) || spells[spell_id].npc_no_los) { (*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this); - SpellOnTarget(spell_id, (*iter), false, true, resist_adjust); + SpellOnTarget(spell_id, (*iter), 0, true, resist_adjust); maxtarget_count++; } } From cef873f7934bfb3047ddde5c462b215e1e20f2a4 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Thu, 14 Oct 2021 10:52:29 -0400 Subject: [PATCH 265/624] [BugFix] Remove detection of client pets from Sense[Summoned|Undead|Animal] spells (#1601) * Remove detection of client pets from Sense[Summoned|Undead|Animal] * Use IsPetOwnerClient() function instead of individual checks * Add option to exclude client pets from GetClosestMobByBodyType * Add parameter Co-authored-by: Noudess --- zone/entity.cpp | 6 +++++- zone/entity.h | 2 +- zone/spell_effects.cpp | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/zone/entity.cpp b/zone/entity.cpp index b506ca01a..c80d13ba8 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -5146,7 +5146,7 @@ void EntityList::ExpeditionWarning(uint32 minutes_left) safe_delete(outapp); } -Mob *EntityList::GetClosestMobByBodyType(Mob *sender, bodyType BodyType) +Mob *EntityList::GetClosestMobByBodyType(Mob *sender, bodyType BodyType, bool skip_client_pets) { if (!sender) @@ -5163,6 +5163,10 @@ Mob *EntityList::GetClosestMobByBodyType(Mob *sender, bodyType BodyType) if (CurrentMob->GetBodyType() != BodyType) continue; + // Do not detect client pets + if (skip_client_pets && CurrentMob->IsPet() && CurrentMob->IsPetOwnerClient()) + continue; + CurrentDistance = ((CurrentMob->GetY() - sender->GetY()) * (CurrentMob->GetY() - sender->GetY())) + ((CurrentMob->GetX() - sender->GetX()) * (CurrentMob->GetX() - sender->GetX())); diff --git a/zone/entity.h b/zone/entity.h index 2890c18f5..20c5be842 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -479,7 +479,7 @@ public: Corpse* GetClosestCorpse(Mob* sender, const char *Name); NPC* GetClosestBanker(Mob* sender, uint32 &distance); void CameraEffect(uint32 duration, uint32 intensity); - Mob* GetClosestMobByBodyType(Mob* sender, bodyType BodyType); + Mob* GetClosestMobByBodyType(Mob* sender, bodyType BodyType, bool skip_client_pets=false); void ForceGroupUpdate(uint32 gid); void SendGroupLeave(uint32 gid, const char *name); void SendGroupJoin(uint32 gid, const char *name); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 5caab6d0d..f5ca54794 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -854,7 +854,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove MessageID = SENSE_ANIMAL; } - Mob *ClosestMob = entity_list.GetClosestMobByBodyType(this, bt); + Mob *ClosestMob = entity_list.GetClosestMobByBodyType(this, bt, true); if(ClosestMob) { From 6669fc8214fe3d189137ac7406c859981dcf2ae5 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 14 Oct 2021 23:30:34 -0400 Subject: [PATCH 266/624] [Bug Fix] Healing pets not correctly dropping out of combat status (#1603) * Fix for pets not correctly triggering in combat timers * Update spells.cpp --- zone/spells.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index b7affc35f..49b430c6f 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4036,11 +4036,21 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes spelltar->SetHateAmountOnEnt(this, std::max(newhate, 1)); } } else if (IsBeneficialSpell(spell_id) && !IsSummonPCSpell(spell_id)) { - if (this != spelltar && spelltar->IsClient() && IsClient()) - CastToClient()->UpdateRestTimer(spelltar->CastToClient()->GetRestTimer()); + if (this != spelltar && IsClient()){ + if (spelltar->IsClient()) { + CastToClient()->UpdateRestTimer(spelltar->CastToClient()->GetRestTimer()); + } + else if (spelltar->IsPet()) { + Mob *owner = spelltar->GetOwner(); + if (owner && owner != this && owner->IsClient()) { + CastToClient()->UpdateRestTimer(owner->CastToClient()->GetRestTimer()); + } + } + } + entity_list.AddHealAggro( - spelltar, this, - CheckHealAggroAmount(spell_id, spelltar, (spelltar->GetMaxHP() - spelltar->GetHP()))); + spelltar, this, + CheckHealAggroAmount(spell_id, spelltar, (spelltar->GetMaxHP() - spelltar->GetHP()))); } // make sure spelltar is high enough level for the buff From 9c67421cccbb1f5d8fba2017d57dbe99ca45fd72 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 15 Oct 2021 09:04:14 -0400 Subject: [PATCH 267/624] quick fix for persistent effects fading (#1604) --- zone/mob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index f0982115c..f58b6a92a 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4372,7 +4372,7 @@ bool Mob::TrySpellOnDeath() } } - BuffFadeAll(); + BuffFadeNonPersistDeath(); return false; //You should not be able to use this effect and survive (ALWAYS return false), //attempting to place a heal in these effects will still result From 203ba2d340f7e184713608bca2d6c6965e692667 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Fri, 15 Oct 2021 13:17:51 -0400 Subject: [PATCH 268/624] [Bug Fix] Urgent - Previous fix for TimeSync on static zones broke dynamic zones. (#1605) * [BugFix] Urgent - Previous fix for TimeSync on static zones broke dynamic zones * Use local variable instead of inline accessor for consistancy. Co-authored-by: Noudess --- zone/zone.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/zone/zone.cpp b/zone/zone.cpp index a5be7d140..56a93bd96 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -150,10 +150,19 @@ bool Zone::Bootup(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone) { } LogInfo("---- Zone server [{}], listening on port:[{}] ----", zonename, ZoneConfig::get()->ZonePort); - LogInfo("Zone Bootup: [{}] ([{}]: [{}])", zonename, iZoneID, iInstanceID); + LogInfo("Zone Bootup: [{}] [{}] ([{}]: [{}])", + (iStaticZone) ? "Static" : "Dynamic", zonename, iZoneID, iInstanceID); parse->Init(); UpdateWindowTitle(nullptr); + // Dynamic zones need to Sync here. + // Static zones sync when they connect in worldserver.cpp. + // Static zones cannot sync here as request is ignored by worldserver. + if (!iStaticZone) + { + zone->GetTimeSync(); + } + zone->RequestUCSServerStatus(); /** From 5235dcee95b0b7d451a6ca05526763cbb6dd5197 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 15 Oct 2021 20:46:57 -0400 Subject: [PATCH 269/624] Fix Immune Melee Nonmagical logic (#1606) --- zone/attack.cpp | 33 ++++++++++++++++----------------- zone/special_attacks.cpp | 6 +++++- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index dbfee1e3a..2d4278046 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -990,32 +990,31 @@ int Mob::GetWeaponDamage(Mob *against, const EQ::ItemData *weapon_item) { //check to see if our weapons or fists are magical. if (against->GetSpecialAbility(IMMUNE_MELEE_NONMAGICAL)) { - if (weapon_item) { + if (GetSpecialAbility(SPECATK_MAGICAL)) { + dmg = 1; + } + //On live this occurs for pets and charmed pet >= level 10 + else if (GetOwner() && GetLevel() >= RuleI(Combat, PetAttackMagicLevel)) { + //pets wouldn't actually use this but... + //it gives us an idea if we can hit due to the dual nature of this function + dmg = 1; + } + else if (weapon_item) { if (weapon_item->Magic) { dmg = weapon_item->Damage; - //this is more for non weapon items, ex: boots for kick //they don't have a dmg but we should be able to hit magical dmg = dmg <= 0 ? 1 : dmg; } - else + else { return 0; + } + } + else if ((GetClass() == MONK || GetClass() == BEASTLORD) && GetLevel() >= 30) { + dmg = GetHandToHandDamage(); } else { - if ((GetClass() == MONK || GetClass() == BEASTLORD) && GetLevel() >= 30) { - dmg = GetHandToHandDamage(); - } - else if (GetOwner() && GetLevel() >= RuleI(Combat, PetAttackMagicLevel)) { - //pets wouldn't actually use this but... - //it gives us an idea if we can hit due to the dual nature of this function - dmg = 1; - } - else if (GetSpecialAbility(SPECATK_MAGICAL)) - { - dmg = 1; - } - else - return 0; + return 0; } } else { diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 1aa87288c..953f6f997 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -156,6 +156,9 @@ void Mob::DoSpecialAttackDamage(Mob *who, EQ::skills::SkillType skill, int32 bas if (my_hit.base_damage == 0) my_hit.base_damage = GetBaseSkillDamage(my_hit.skill); + if (base_damage = DMG_INVULNERABLE) + my_hit.damage_done = DMG_INVULNERABLE; + if (who->GetInvul() || who->GetSpecialAbility(IMMUNE_MELEE)) my_hit.damage_done = DMG_INVULNERABLE; @@ -1649,8 +1652,9 @@ void NPC::DoClassAttacks(Mob *target) { DoAnim(animKick, 0, false); int32 dmg = GetBaseSkillDamage(EQ::skills::SkillKick); - if (GetWeaponDamage(target, (const EQ::ItemData*)nullptr) <= 0) + if (GetWeaponDamage(target, (const EQ::ItemData*)nullptr) <= 0) { dmg = DMG_INVULNERABLE; + } reuse = (KickReuseTime + 3) * 1000; DoSpecialAttackDamage(target, EQ::skills::SkillKick, dmg, GetMinDamage(), -1, reuse); From 426f9c337bfa37b4ef23c07567497d9ee328ef80 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 16 Oct 2021 00:10:54 -0400 Subject: [PATCH 270/624] hotfix --- zone/special_attacks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 953f6f997..a7eeb023d 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -156,7 +156,7 @@ void Mob::DoSpecialAttackDamage(Mob *who, EQ::skills::SkillType skill, int32 bas if (my_hit.base_damage == 0) my_hit.base_damage = GetBaseSkillDamage(my_hit.skill); - if (base_damage = DMG_INVULNERABLE) + if (base_damage == DMG_INVULNERABLE) my_hit.damage_done = DMG_INVULNERABLE; if (who->GetInvul() || who->GetSpecialAbility(IMMUNE_MELEE)) From af5cfb9bed7c4fdfbef3dcf42bb39b349cfb3888 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 16 Oct 2021 00:22:07 -0400 Subject: [PATCH 271/624] [Spells] Fix to prevent Charmed Pets from continuing fight target if owner is dead. (#1600) * Fix for charm break if pet owner dead * fix, can't check hatelist it is already wiped. * Update spell_effects.cpp --- zone/attack.cpp | 6 ++++ zone/spell_effects.cpp | 66 ++++++++++++++++++++++++++++++------------ 2 files changed, 53 insertions(+), 19 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 2d4278046..39c613cc6 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1684,11 +1684,17 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQ::skills::Skill // #2: figure out things that affect the player dying and mark them dead InterruptSpell(); + + Mob* m_pet = GetPet(); SetPet(0); SetHorseId(0); ShieldAbilityClearVariables(); dead = true; + if (m_pet && m_pet->IsCharmed()) { + m_pet->BuffFadeByEffect(SE_Charm); + } + if (GetMerc()) { GetMerc()->Suspend(); } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index f5ca54794..db2e78ecc 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -4291,7 +4291,7 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) } SendAppearancePacket(AT_Pet, 0, true, true); - Mob* tempmob = GetOwner(); + Mob* owner = GetOwner(); SetOwnerID(0); SetPetType(petNone); SetHeld(false); @@ -4300,25 +4300,27 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) SetFocused(false); SetPetStop(false); SetPetRegroup(false); - if(tempmob) + if(owner) { - tempmob->SetPet(0); + owner->SetPet(0); } if (IsAIControlled()) { - // clear the hate list of the mobs + //Remove damage over time effects on charmed pet and those applied by charmed pet. if (RuleB(Spells, PreventFactionWarOnCharmBreak)) { for (auto mob : hate_list.GetHateList()) { auto tar = mob->entity_on_hatelist; - if (tar->IsCasting()) { - tar->InterruptSpell(tar->CastingSpellID()); - } - uint32 buff_count = tar->GetMaxTotalSlots(); - for (unsigned int j = 0; j < buff_count; j++) { - if (tar->GetBuffs()[j].spellid != SPELL_UNKNOWN) { - auto spell = spells[tar->GetBuffs()[j].spellid]; - if (spell.goodEffect == 0 && IsEffectInSpell(spell.id, SE_CurrentHP) && tar->GetBuffs()[j].casterid == GetID()) { - tar->BuffFadeBySpellID(spell.id); + if (tar) { + if (tar->IsCasting()) { + tar->InterruptSpell(tar->CastingSpellID()); + } + uint32 buff_count = tar->GetMaxTotalSlots(); + for (unsigned int j = 0; j < buff_count; j++) { + if (IsValidSpell(tar->GetBuffs()[j].spellid)) { + auto spell = spells[tar->GetBuffs()[j].spellid]; + if (spell.goodEffect == 0 && IsEffectInSpell(spell.id, SE_CurrentHP) && tar->GetBuffs()[j].casterid == GetID()) { + tar->BuffFadeBySpellID(spell.id); + } } } } @@ -4328,7 +4330,7 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) } uint32 buff_count = GetMaxTotalSlots(); for (unsigned int j = 0; j < buff_count; j++) { - if (GetBuffs()[j].spellid != SPELL_UNKNOWN) { + if (IsValidSpell(GetBuffs()[j].spellid )) { auto spell = spells[this->GetBuffs()[j].spellid]; if (spell.goodEffect == 0 && IsEffectInSpell(spell.id, SE_CurrentHP)) { BuffFadeBySpellID(spell.id); @@ -4336,17 +4338,43 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) } } } - entity_list.ReplaceWithTarget(this, tempmob); + + // clear the hate list of the mobs + entity_list.ReplaceWithTarget(this, owner); WipeHateList(); - if(tempmob) - AddToHateList(tempmob, 1, 0); + if (owner) { + AddToHateList(owner, 1, 0); + } + //If owner dead, briefly setting Immmune Aggro while hatelists wipe for both pet and targets is needed to ensure no reaggroing. + else if (IsNPC()){ + bool immune_aggro = GetSpecialAbility(IMMUNE_AGGRO); //check if already immune aggro + SetSpecialAbility(IMMUNE_AGGRO, 1); + WipeHateList(); + if (IsCasting()) { + InterruptSpell(CastingSpellID()); + } + entity_list.RemoveFromHateLists(this); + //If NPC targeting charmed pet are in process of casting on it after it is removed from hatelist, stop the cast to prevent reaggroing. + Mob *current_npc = nullptr; + for (auto &it : entity_list.GetNPCList()) { + current_npc = it.second; + + if (current_npc && current_npc->IsCasting() && current_npc->GetTarget() == this) { + current_npc->InterruptSpell(current_npc->CastingSpellID()); + } + } + + if (!immune_aggro) { + SetSpecialAbility(IMMUNE_AGGRO, 0); + } + } SendAppearancePacket(AT_Anim, ANIM_STAND); } - if(tempmob && tempmob->IsClient()) + if(owner && owner->IsClient()) { auto app = new EQApplicationPacket(OP_Charm, sizeof(Charm_Struct)); Charm_Struct *ps = (Charm_Struct*)app->pBuffer; - ps->owner_id = tempmob->GetID(); + ps->owner_id = owner->GetID(); ps->pet_id = GetID(); ps->command = 0; entity_list.QueueClients(this, app); From 5d522b149ba410c0babf8b8706db902618d2f5fe Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 16 Oct 2021 08:56:38 -0400 Subject: [PATCH 272/624] [Bug Fix] Allow invisible to be cast on Raid Group members. (#1607) When `Spells:InvisRequiresGroup` was true, you could only cast on Group members, intended functionality is to cast on Group members and/or people in your Raid Group. --- zone/spells.cpp | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 49b430c6f..f379dbc8c 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -432,22 +432,37 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, casting_spell_targetid = target_id; if (RuleB(Spells, InvisRequiresGroup) && IsInvisSpell(spell_id)) { - if (GetTarget() && GetTarget()->IsClient()) { - Client *spell_target = entity_list.GetClientByID(target_id); + if (IsClient() && GetTarget() && GetTarget()->IsClient()) { + Client* spell_caster = this->CastToClient(); + Client* spell_target = entity_list.GetClientByID(target_id); if (spell_target && spell_target->GetID() != GetID()) { - if (!spell_target->IsGrouped()) { - InterruptSpell(spell_id); - Message(Chat::Red, "You cannot invis someone who is not in your group."); - return false; - } - else if (spell_target->IsGrouped()) { + bool cast_failed = true; + if (spell_target->IsGrouped()) { Group *target_group = spell_target->GetGroup(); - Group *my_group = GetGroup(); - if (target_group && my_group && (target_group->GetID() != my_group->GetID())) { - InterruptSpell(spell_id); - Message(Chat::Red, "You cannot invis someone who is not in your group."); - return false; + Group *my_group = GetGroup(); + if ( + target_group && + my_group && + (target_group->GetID() == my_group->GetID()) + ) { + cast_failed = false; } + } else if (spell_target->IsRaidGrouped()) { + Raid *target_raid = spell_target->GetRaid(); + Raid *my_raid = GetRaid(); + if ( + target_raid && + my_raid && + (target_raid->GetGroup(spell_target) == my_raid->GetGroup(spell_caster)) + ) { + cast_failed = false; + } + } + + if (cast_failed) { + InterruptSpell(spell_id); + MessageString(Chat::Red, TARGET_GROUP_MEMBER); + return false; } } } From 07d96ad9218f5c12395c39b7cf98c5b5206d54ea Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 16 Oct 2021 15:10:42 -0400 Subject: [PATCH 273/624] [Bug Fix] Fix Character Recast Type -1 saving to database. (#1598) --- common/item_data.h | 2 +- common/shareddb.cpp | 2 +- zone/spells.cpp | 21 +++++++++++++++------ 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/common/item_data.h b/common/item_data.h index d3ae5f928..e3123e22f 100644 --- a/common/item_data.h +++ b/common/item_data.h @@ -482,7 +482,7 @@ namespace EQ uint32 Haste; uint32 DamageShield; uint32 RecastDelay; - uint32 RecastType; + int RecastType; uint32 AugDistiller; bool Attuneable; bool NoPet; diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 18a2bed3d..18619ee6e 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1118,7 +1118,7 @@ void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_ item.Haste = (uint32)atoul(row[ItemField::haste]); item.DamageShield = (uint32)atoul(row[ItemField::damageshield]); item.RecastDelay = (uint32)atoul(row[ItemField::recastdelay]); - item.RecastType = (uint32)atoul(row[ItemField::recasttype]); + item.RecastType = (int)atoi(row[ItemField::recasttype]); item.GuildFavor = (uint32)atoul(row[ItemField::guildfavor]); item.AugDistiller = (uint32)atoul(row[ItemField::augdistiller]); item.Attuneable = (atoi(row[ItemField::attuneable]) == 0) ? false : true; diff --git a/zone/spells.cpp b/zone/spells.cpp index f379dbc8c..36202f12c 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1338,7 +1338,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo bool fromaug = false; EQ::ItemData* augitem = nullptr; uint32 recastdelay = 0; - uint32 recasttype = 0; + int recasttype = 0; while (true) { if (item == nullptr) @@ -1378,8 +1378,13 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo { //Can we start the timer here? I don't see why not. CastToClient()->GetPTimers().Start((pTimerItemStart + recasttype), recastdelay); - database.UpdateItemRecastTimestamps(CastToClient()->CharacterID(), recasttype, - CastToClient()->GetPTimers().Get(pTimerItemStart + recasttype)->GetReadyTimestamp()); + if (recasttype != -1) { + database.UpdateItemRecastTimestamps( + CastToClient()->CharacterID(), + recasttype, + CastToClient()->GetPTimers().Get(pTimerItemStart + recasttype)->GetReadyTimestamp() + ); + } } } @@ -2539,9 +2544,13 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui if(itm && itm->GetItem()->RecastDelay > 0){ auto recast_type = itm->GetItem()->RecastType; CastToClient()->GetPTimers().Start((pTimerItemStart + recast_type), itm->GetItem()->RecastDelay); - database.UpdateItemRecastTimestamps( - CastToClient()->CharacterID(), recast_type, - CastToClient()->GetPTimers().Get(pTimerItemStart + recast_type)->GetReadyTimestamp()); + if (recast_type != -1) { + database.UpdateItemRecastTimestamps( + CastToClient()->CharacterID(), + recast_type, + CastToClient()->GetPTimers().Get(pTimerItemStart + recast_type)->GetReadyTimestamp() + ); + } auto outapp = new EQApplicationPacket(OP_ItemRecastDelay, sizeof(ItemRecastDelay_Struct)); ItemRecastDelay_Struct *ird = (ItemRecastDelay_Struct *)outapp->pBuffer; ird->recast_delay = itm->GetItem()->RecastDelay; From 11c335a015b2fecefe70b5e550e469e5a1935ba2 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sat, 16 Oct 2021 21:35:03 -0500 Subject: [PATCH 274/624] [DiaWind] Tag Adjustments for title, button_one, button_two (#1610) * Add a consistent way to handle a few different tags * Simplify logic further --- zone/dialogue_window.cpp | 123 +++++++++++++++------------------------ 1 file changed, 46 insertions(+), 77 deletions(-) diff --git a/zone/dialogue_window.cpp b/zone/dialogue_window.cpp index a2a874291..d719f72a8 100644 --- a/zone/dialogue_window.cpp +++ b/zone/dialogue_window.cpp @@ -9,7 +9,7 @@ void DialogueWindow::Render(Client *c, std::string markdown) } // this is the NPC that the client is interacting with if there is dialogue going on - Mob* target = c->GetTarget() ? c->GetTarget() : c; + Mob *target = c->GetTarget() ? c->GetTarget() : c; // zero this out c->SetEntityVariable(DIAWIND_RESPONSE_ONE_KEY.c_str(), ""); @@ -57,7 +57,7 @@ void DialogueWindow::Render(Client *c, std::string markdown) } bool render_hiddenresponse = false; - if (markdown.find("hiddenresponse") != std::string::npos) { + if (markdown.find("hiddenresponse") != std::string::npos) { render_hiddenresponse = true; LogDiaWind("Client [{}] Rendering hiddenresponse", c->GetCleanName()); find_replace(output, "hiddenresponse", ""); @@ -122,11 +122,11 @@ void DialogueWindow::Render(Client *c, std::string markdown) } } - uint32 popup_id = POPUPID_DIAWIND_ONE; - uint32 negative_id = POPUPID_DIAWIND_TWO; + uint32 popup_id = POPUPID_DIAWIND_ONE; + uint32 negative_id = POPUPID_DIAWIND_TWO; std::string button_one_name; std::string button_two_name; - uint32 sound_controls = 0; + uint32 sound_controls = 0; // window type std::string wintype; @@ -205,7 +205,9 @@ void DialogueWindow::Render(Client *c, std::string markdown) auto second_split = split_string(first_split[1], " "); if (!second_split.empty()) { secondresponseid = second_split[0]; - LogDiaWindDetail("Client [{}] Rendering secondresponseid option secondresponseid [{}]", c->GetCleanName(), secondresponseid); + LogDiaWindDetail("Client [{}] Rendering secondresponseid option secondresponseid [{}]", + c->GetCleanName(), + secondresponseid); } if (first_split[1].length() == 1) { @@ -229,60 +231,34 @@ void DialogueWindow::Render(Client *c, std::string markdown) std::string button_one; std::string button_two; if ( - markdown.find("button_one:") != std::string::npos && - markdown.find("button_two:") != std::string::npos - ) { + markdown.find("{button_one:") != std::string::npos && + markdown.find("{button_two:") != std::string::npos + ) { + LogDiaWind("Client [{}] Rendering button_one option.", c->GetCleanName()); - auto one_first_split = split_string(output, "button_one:"); - if (!one_first_split.empty()) { - auto one_second_split = split_string(one_first_split[1], " "); - if (!one_second_split.empty()) { - button_one = one_second_split[0]; - LogDiaWindDetail("Client [{}] Rendering button_one option button_one [{}]", c->GetCleanName(), button_one); - } + button_one = get_between(output, "{button_one:", "}"); + LogDiaWind("Client [{}] button_one [{}]", c->GetCleanName(), button_one); - if (one_first_split[1].length() == 1) { - button_one = one_first_split[1]; - LogDiaWindDetail( - "Client [{}] Rendering button_one (end) option button_one [{}]", - c->GetCleanName(), - button_one - ); - } - - find_replace(output, fmt::format("button_one:{}", button_one), ""); - - if (!button_one.empty()) { - button_one_name = button_one.c_str(); - } + if (!button_one.empty()) { + find_replace(output, fmt::format("{{button_one:{}}}", button_one), ""); + button_one_name = trim(button_one); } - LogDiaWind("Client [{}] Rendering button_two option.", c->GetCleanName()); + button_two = get_between(output, "{button_two:", "}"); + LogDiaWind("Client [{}] button_two [{}]", c->GetCleanName(), button_two); - auto two_first_split = split_string(output, "button_two:"); - if (!two_first_split.empty()) { - auto two_second_split = split_string(two_first_split[1], " "); - if (!two_second_split.empty()) { - button_two = two_second_split[0]; - LogDiaWindDetail("Client [{}] Rendering button_two option button_two [{}]", c->GetCleanName(), button_two); - } - - if (two_first_split[1].length() == 1) { - button_two = two_first_split[1]; - LogDiaWindDetail( - "Client [{}] Rendering button_two (end) option button_two [{}]", - c->GetCleanName(), - button_two - ); - } - - find_replace(output, fmt::format("button_two:{}", button_two), ""); - - if (!button_two.empty()) { - button_two_name = button_two.c_str(); - } + if (!button_two.empty()) { + find_replace(output, fmt::format("{{button_two:{}}}", button_two), ""); + button_two_name = trim(button_two); } + + LogDiaWind( + "Client [{}] Rendering buttons button_one [{}] button_two [{}]", + c->GetCleanName(), + button_one, + button_two + ); } // bracket responses @@ -337,6 +313,11 @@ void DialogueWindow::Render(Client *c, std::string markdown) // pop the response off of the message find_replace(content, fmt::format("[{}]", bracket_message), ""); + // too many iterations / safety net + if (response_index > 100) { + break; + } + response_index++; } } @@ -403,31 +384,14 @@ void DialogueWindow::Render(Client *c, std::string markdown) // title std::string popup_title; - if (markdown.find("title:") != std::string::npos) { - LogDiaWind("Client [{}] Rendering title option", c->GetCleanName()); + if (markdown.find("{title:") != std::string::npos) { + popup_title = get_between(output, "{title:", "}"); - auto first_split = split_string(output, "title:"); - if (!first_split.empty()) { - auto second_split = split_string(first_split[1], " "); - if (!second_split.empty()) { - popup_title = second_split[0]; - LogDiaWindDetail("Client [{}] Rendering title option title [{}]", c->GetCleanName(), popup_title); - } + LogDiaWind("Client [{}] Rendering title option title [{}]", c->GetCleanName(), popup_title); - if (first_split[1].length() == 1) { - popup_title = first_split[1]; - LogDiaWindDetail( - "Client [{}] Rendering title (end) option title [{}]", - c->GetCleanName(), - popup_title - ); - } - - find_replace(output, fmt::format("title:{}", popup_title), ""); - - if (!popup_title.empty()) { - title = popup_title; - } + if (!popup_title.empty()) { + find_replace(output, fmt::format("{{title:{}}}", popup_title), ""); + title = trim(popup_title); } } @@ -504,6 +468,11 @@ void DialogueWindow::Render(Client *c, std::string markdown) } } + // too many iterations / safety net + if (tag_index > 100) { + break; + } + tag_index++; } } @@ -514,7 +483,7 @@ void DialogueWindow::Render(Client *c, std::string markdown) if (render_hiddenresponse) { final_output = fmt::format("{}{}{}", quote_string, trim(output), quote_string); } - + // send popup c->SendFullPopup( title.c_str(), From 7823ff533692ad3da48ac22737e3394e99fd07af Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Sat, 16 Oct 2021 23:19:19 -0500 Subject: [PATCH 275/624] [Quest API] Add EVENT_LOOT_ZONE to zone_controller (#1608) * Add EVENT_LOOT_ZONE to zone_controller * Fix porting event_loot_zone to lua API * Remove extra spacing and remove forced message to allow for scripted responses. * Allow all script parsing to fire before sending a failed lootitem, add corpse_id * Only search for zone_controller once --- zone/corpse.cpp | 42 +++++++++++++++++++++++--------------- zone/embparser.cpp | 7 +++++-- zone/event_codes.h | 1 + zone/lua_general.cpp | 3 ++- zone/lua_parser.cpp | 4 +++- zone/lua_parser_events.cpp | 18 ++++++++++++++++ zone/lua_parser_events.h | 2 ++ 7 files changed, 57 insertions(+), 20 deletions(-) diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 5ccdc8672..2053e559d 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -1283,21 +1283,24 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) } } - char buf[88]; char q_corpse_name[64]; strcpy(q_corpse_name, corpse_name); - snprintf(buf, 87, "%d %d %s", inst->GetItem()->ID, inst->GetCharges(), - EntityList::RemoveNumbers(q_corpse_name)); - buf[87] = '\0'; + std::string buf = fmt::format("{} {} {} {}", inst->GetItem()->ID, inst->GetCharges(), EntityList::RemoveNumbers(q_corpse_name), GetID()); std::vector args; args.push_back(inst); args.push_back(this); - if (parse->EventPlayer(EVENT_LOOT, client, buf, 0, &args) != 0) { - lootitem->auto_loot = -1; - client->MessageString(Chat::Red, LOOT_NOT_ALLOWED, inst->GetItem()->Name); - client->QueuePacket(app); - delete inst; - return; + bool prevent_loot = false; + if (RuleB(Zone, UseZoneController)) { + auto controller = entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID); + if (controller){ + if (parse->EventNPC(EVENT_LOOT_ZONE, controller, client, buf.c_str(), 0, &args) != 0) { + prevent_loot = true; + } + } + } + + if (parse->EventPlayer(EVENT_LOOT, client, buf.c_str(), 0, &args) != 0) { + prevent_loot = true; } if (!IsPlayerCorpse()) @@ -1306,17 +1309,24 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) auto dz = zone->GetDynamicZone(); if (dz && !dz->CanClientLootCorpse(client, GetNPCTypeID(), GetID())) { + prevent_loot = true; // note on live this message is only sent once on the first loot attempt of an open corpse client->MessageString(Chat::Loot, LOOT_NOT_ALLOWED, inst->GetItem()->Name); - lootitem->auto_loot = -1; // generates client eqstr 1370 "You may not loot that item from this corpse." - client->QueuePacket(app); - delete inst; - return; } } - // do we want this to have a fail option too? - parse->EventItem(EVENT_LOOT, client, inst, this, buf, 0); + // do we want this to have a fail option too? Sure? + if (parse->EventItem(EVENT_LOOT, client, inst, this, buf.c_str(), 0) != 0) { + prevent_loot = true; + } + + if (prevent_loot) { + lootitem->auto_loot = -1; + client->QueuePacket(app); + safe_delete(inst); + return; + } + // safe to ACK now client->QueuePacket(app); diff --git a/zone/embparser.cpp b/zone/embparser.cpp index f4ea70356..43bcebe5c 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -124,7 +124,8 @@ const char *QuestEventSubroutines[_LargestEventID] = { "EVENT_TEST_BUFF", "EVENT_COMBINE", "EVENT_CONSIDER", - "EVENT_CONSIDER_CORPSE" + "EVENT_CONSIDER_CORPSE", + "EVENT_LOOT_ZONE" }; PerlembParser::PerlembParser() : perl(nullptr) @@ -1411,12 +1412,14 @@ void PerlembParser::ExportEventVariables( ExportVar(package_name.c_str(), "version", zone->GetInstanceVersion()); break; } - + + case EVENT_LOOT_ZONE: case EVENT_LOOT: { Seperator sep(data); ExportVar(package_name.c_str(), "looted_id", sep.arg[0]); ExportVar(package_name.c_str(), "looted_charges", sep.arg[1]); ExportVar(package_name.c_str(), "corpse", sep.arg[2]); + ExportVar(package_name.c_str(), "corpse_id", sep.arg[3]); break; } diff --git a/zone/event_codes.h b/zone/event_codes.h index 4a915ce9c..0fd9d94b3 100644 --- a/zone/event_codes.h +++ b/zone/event_codes.h @@ -93,6 +93,7 @@ typedef enum { EVENT_COMBINE, EVENT_CONSIDER, EVENT_CONSIDER_CORPSE, + EVENT_LOOT_ZONE, _LargestEventID } QuestEventID; diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index a9d724138..b6652302b 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -4040,7 +4040,8 @@ luabind::scope lua_register_events() { luabind::value("warp", static_cast(EVENT_WARP)), luabind::value("test_buff", static_cast(EVENT_TEST_BUFF)), luabind::value("consider", static_cast(EVENT_CONSIDER)), - luabind::value("consider_corpse", static_cast(EVENT_CONSIDER_CORPSE)) + luabind::value("consider_corpse", static_cast(EVENT_CONSIDER_CORPSE)), + luabind::value("loot_zone", static_cast(EVENT_LOOT_ZONE)) ]; } diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index 641f24040..093f11c62 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -135,7 +135,8 @@ const char *LuaEvents[_LargestEventID] = { "event_test_buff", "event_combine", "event_consider", - "event_consider_corpse" + "event_consider_corpse", + "event_loot_zone" }; extern Zone *zone; @@ -185,6 +186,7 @@ LuaParser::LuaParser() { NPCArgumentDispatch[EVENT_FEIGN_DEATH] = handle_npc_single_client; NPCArgumentDispatch[EVENT_ENTER_AREA] = handle_npc_area; NPCArgumentDispatch[EVENT_LEAVE_AREA] = handle_npc_area; + NPCArgumentDispatch[EVENT_LOOT_ZONE] = handle_npc_loot_zone; PlayerArgumentDispatch[EVENT_SAY] = handle_player_say; PlayerArgumentDispatch[EVENT_ENVIRONMENTAL_DAMAGE] = handle_player_environmental_damage; diff --git a/zone/lua_parser_events.cpp b/zone/lua_parser_events.cpp index 0734c9e19..e7711b36c 100644 --- a/zone/lua_parser_events.cpp +++ b/zone/lua_parser_events.cpp @@ -237,6 +237,24 @@ void handle_npc_null(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, s std::vector *extra_pointers) { } +void handle_npc_loot_zone(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, + std::vector *extra_pointers) { + Lua_Client l_client(reinterpret_cast(init)); + luabind::adl::object l_client_o = luabind::adl::object(L, l_client); + l_client_o.push(L); + lua_setfield(L, -2, "other"); + + Lua_ItemInst l_item(EQ::any_cast(extra_pointers->at(0))); + luabind::adl::object l_item_o = luabind::adl::object(L, l_item); + l_item_o.push(L); + lua_setfield(L, -2, "item"); + + Lua_Corpse l_corpse(EQ::any_cast(extra_pointers->at(1))); + luabind::adl::object l_corpse_o = luabind::adl::object(L, l_corpse); + l_corpse_o.push(L); + lua_setfield(L, -2, "corpse"); +} + //Player void handle_player_say(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector *extra_pointers) { diff --git a/zone/lua_parser_events.h b/zone/lua_parser_events.h index 850116075..1d9d4b22c 100644 --- a/zone/lua_parser_events.h +++ b/zone/lua_parser_events.h @@ -41,6 +41,8 @@ void handle_npc_area(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, s std::vector *extra_pointers); void handle_npc_null(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, std::vector *extra_pointers); +void handle_npc_loot_zone(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, + std::vector *extra_pointers); //Player void handle_player_say(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, From 3dcddcba04cb48d2eab588c59eb8b77a78c31bde Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 17 Oct 2021 23:41:10 -0400 Subject: [PATCH 276/624] [Quest API] Add GetHateRandomBot(), GetHateRandomClient(), and GetHateRandomNPC() to Perl/Lua. (#1613) - Add $mob->GetHateRandomBot() to Perl. - Add $mob->GetHateRandomClient() to Perl. - Add $mob->GetHateRandomNPC() to Perl. - Add mob:GetHateRandomBot() to Lua. - Add mob:GetHateRandomClient() to Lua. - Add mob:GetHateRandomNPC() to Lua. --- zone/hate_list.cpp | 149 +++++++++++++++++++++++++++++++++++++++++++++ zone/hate_list.h | 6 ++ zone/lua_mob.cpp | 28 ++++++++- zone/lua_mob.h | 10 +++ zone/mob.h | 5 ++ zone/perl_mob.cpp | 51 ++++++++++++++++ 6 files changed, 248 insertions(+), 1 deletion(-) diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index b4b6725bb..5e13f3ed5 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -797,3 +797,152 @@ std::list HateList::GetHateListByDistance(int distance) } return hate_list; } + +#ifdef BOTS +Bot* HateList::GetRandomBotOnHateList(bool skip_mezzed) +{ + int count = list.size(); + if (count <= 0) { //If we don't have any entries it'll crash getting a random 0, -1 position. + return nullptr; + } + + if (count == 1) { //No need to do all that extra work if we only have one hate entry + if (*list.begin() && (*list.begin())->entity_on_hatelist->IsBot() && (!skip_mezzed || !(*list.begin())->entity_on_hatelist->IsMezzed())) { + return (*list.begin())->entity_on_hatelist->CastToBot(); + } + return nullptr; + } + + if (skip_mezzed) { + for (auto iter : list) { + if (iter->entity_on_hatelist->IsMezzed()) { + --count; + } + } + + if (count <= 0) { + return nullptr; + } + } + + int random = zone->random.Int(0, count - 1); + int counter = 0; + + for (auto iter : list) { + if (!iter->entity_on_hatelist->IsBot()) { + continue; + } + + if (skip_mezzed && iter->entity_on_hatelist->IsMezzed()) { + continue; + } + + if (counter < random) { + ++counter; + continue; + } + + return iter->entity_on_hatelist->CastToBot(); + } + + return nullptr; +} +#endif + +Client* HateList::GetRandomClientOnHateList(bool skip_mezzed) +{ + int count = list.size(); + if (count <= 0) { //If we don't have any entries it'll crash getting a random 0, -1 position. + return nullptr; + } + + if (count == 1) { //No need to do all that extra work if we only have one hate entry + if (*list.begin() && (*list.begin())->entity_on_hatelist->IsClient() && (!skip_mezzed || !(*list.begin())->entity_on_hatelist->IsMezzed())) { + return (*list.begin())->entity_on_hatelist->CastToClient(); + } + return nullptr; + } + + if (skip_mezzed) { + for (auto iter : list) { + if (iter->entity_on_hatelist->IsMezzed()) { + --count; + } + } + + if (count <= 0) { + return nullptr; + } + } + + int random = zone->random.Int(0, count - 1); + int counter = 0; + + for (auto iter : list) { + if (!iter->entity_on_hatelist->IsClient()) { + continue; + } + + if (skip_mezzed && iter->entity_on_hatelist->IsMezzed()) { + continue; + } + + if (counter < random) { + ++counter; + continue; + } + + return iter->entity_on_hatelist->CastToClient(); + } + + return nullptr; +} + +NPC* HateList::GetRandomNPCOnHateList(bool skip_mezzed) +{ + int count = list.size(); + if (count <= 0) { //If we don't have any entries it'll crash getting a random 0, -1 position. + return nullptr; + } + + if (count == 1) { //No need to do all that extra work if we only have one hate entry + if (*list.begin() && (*list.begin())->entity_on_hatelist->IsNPC() && (!skip_mezzed || !(*list.begin())->entity_on_hatelist->IsMezzed())) { + return (*list.begin())->entity_on_hatelist->CastToNPC(); + } + return nullptr; + } + + if (skip_mezzed) { + for (auto iter : list) { + if (iter->entity_on_hatelist->IsMezzed()) { + --count; + } + } + + if (count <= 0) { + return nullptr; + } + } + + int random = zone->random.Int(0, count - 1); + int counter = 0; + + for (auto iter : list) { + if (!iter->entity_on_hatelist->IsNPC()) { + continue; + } + + if (skip_mezzed && iter->entity_on_hatelist->IsMezzed()) { + continue; + } + + if (counter < random) { + ++counter; + continue; + } + + return iter->entity_on_hatelist->CastToNPC(); + } + + return nullptr; +} diff --git a/zone/hate_list.h b/zone/hate_list.h index 71c1f31cf..22971aa2e 100644 --- a/zone/hate_list.h +++ b/zone/hate_list.h @@ -49,6 +49,12 @@ public: Mob *GetEscapingEntOnHateList(); // returns first eligble entity Mob *GetEscapingEntOnHateList(Mob *center, float range = 0.0f, bool first = false); +#ifdef BOTS + Bot* GetRandomBotOnHateList(bool skip_mezzed = false); +#endif + Client* GetRandomClientOnHateList(bool skip_mezzed = false); + NPC* GetRandomNPCOnHateList(bool skip_mezzed = false); + bool IsEntOnHateList(Mob *mob); bool IsHateListEmpty(); bool RemoveEntFromHateList(Mob *ent); diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index 3c2781c28..181a7dde5 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -5,9 +5,13 @@ #include "client.h" #include "npc.h" +#ifdef BOTS +#include "lua_bot.h" +#endif #include "lua_item.h" #include "lua_iteminst.h" #include "lua_mob.h" +#include "lua_npc.h" #include "lua_hate_list.h" #include "lua_client.h" #include "lua_stat_bonuses.h" @@ -2402,6 +2406,23 @@ void Lua_Mob::RemoveAllNimbusEffects() { self->RemoveAllNimbusEffects(); } +#ifdef BOTS +Lua_Bot Lua_Mob::GetHateRandomBot() { + Lua_Safe_Call_Class(Lua_Bot); + return Lua_Bot(self->GetHateRandomBot()); +} +#endif + +Lua_Client Lua_Mob::GetHateRandomClient() { + Lua_Safe_Call_Class(Lua_Client); + return Lua_Client(self->GetHateRandomClient()); +} + +Lua_NPC Lua_Mob::GetHateRandomNPC() { + Lua_Safe_Call_Class(Lua_NPC); + return Lua_NPC(self->GetHateRandomNPC()); +} + luabind::scope lua_register_mob() { return luabind::class_("Mob") .def(luabind::constructor<>()) @@ -2810,7 +2831,12 @@ luabind::scope lua_register_mob() { .def("GetLastName", &Lua_Mob::GetLastName) .def("CanClassEquipItem", &Lua_Mob::CanClassEquipItem) .def("CanRaceEquipItem", &Lua_Mob::CanRaceEquipItem) - .def("RemoveAllNimbusEffects", &Lua_Mob::RemoveAllNimbusEffects); + .def("RemoveAllNimbusEffects", &Lua_Mob::RemoveAllNimbusEffects) +#ifdef BOTS + .def("GetHateRandomBot", (Lua_Bot(Lua_Mob::*)(void))&Lua_Mob::GetHateRandomBot) +#endif + .def("GetHateRandomClient", (Lua_Client(Lua_Mob::*)(void))&Lua_Mob::GetHateRandomClient) + .def("GetHateRandomNPC", (Lua_NPC(Lua_Mob::*)(void))&Lua_Mob::GetHateRandomNPC); } luabind::scope lua_register_special_abilities() { diff --git a/zone/lua_mob.h b/zone/lua_mob.h index cd3e524cb..144d9dbb5 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -9,6 +9,11 @@ struct Lua_HateList; class Lua_Item; class Lua_ItemInst; class Lua_StatBonuses; +#ifdef BOTS +class Lua_Bot; +#endif +class Lua_NPC; +class Lua_Client; namespace luabind { struct scope; @@ -205,6 +210,11 @@ public: Lua_Mob GetHateTop(); Lua_Mob GetHateDamageTop(Lua_Mob other); Lua_Mob GetHateRandom(); +#ifdef BOTS + Lua_Bot GetHateRandomBot(); +#endif + Lua_Client GetHateRandomClient(); + Lua_NPC GetHateRandomNPC(); Lua_Mob GetHateClosest(); void AddToHateList(Lua_Mob other); void AddToHateList(Lua_Mob other, int hate); diff --git a/zone/mob.h b/zone/mob.h index 3d207ee26..c2e49f757 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -649,6 +649,11 @@ public: Mob* GetSecondaryHate(Mob *skip) { return hate_list.GetEntWithMostHateOnList(this, skip); } Mob* GetHateDamageTop(Mob* other) { return hate_list.GetDamageTopOnHateList(other);} Mob* GetHateRandom() { return hate_list.GetRandomEntOnHateList();} + Client* GetHateRandomClient() { return hate_list.GetRandomClientOnHateList(); } + NPC* GetHateRandomNPC() { return hate_list.GetRandomNPCOnHateList(); } +#ifdef BOTS + Bot* GetHateRandomBot() { return hate_list.GetRandomBotOnHateList(); } +#endif Mob* GetHateMost() { return hate_list.GetEntWithMostHateOnList();} Mob* GetHateClosest() { return hate_list.GetClosestEntOnHateList(this); } bool IsEngaged() { return(!hate_list.IsHateListEmpty()); } diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index ff8bc48b9..4c32c5a87 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -6357,6 +6357,38 @@ XS(XS_Mob_ShieldAbility) { XSRETURN_EMPTY; } +XS(XS_Mob_GetHateRandomClient); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetHateRandomClient) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetHateRandomClient(THIS)"); // @categories Hate and Aggro + { + Mob* THIS; + Client* RETVAL; + VALIDATE_THIS_IS_MOB; + RETVAL = THIS->GetHateRandomClient(); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Client", (void *) RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetHateRandomNPC); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetHateRandomNPC) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetHateRandomNPC(THIS)"); // @categories Hate and Aggro + { + Mob* THIS; + NPC* RETVAL; + VALIDATE_THIS_IS_MOB; + RETVAL = THIS->GetHateRandomNPC(); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "NPC", (void *) RETVAL); + } + XSRETURN(1); +} + #ifdef BOTS XS(XS_Mob_CastToBot); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_CastToBot) @@ -6374,6 +6406,22 @@ XS(XS_Mob_CastToBot) } XSRETURN(1); } + +XS(XS_Mob_GetHateRandomBot); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetHateRandomBot) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetHateRandomBot(THIS)"); // @categories Hate and Aggro + { + Mob* THIS; + Bot* RETVAL; + VALIDATE_THIS_IS_MOB; + RETVAL = THIS->GetHateRandomBot(); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Bot", (void *) RETVAL); + } + XSRETURN(1); +} #endif #ifdef __cplusplus @@ -6729,8 +6777,11 @@ XS(boot_Mob) { newXSproto(strcpy(buf, "RemoveAllNimbusEffects"), XS_Mob_RemoveAllNimbusEffects, file, "$"); newXSproto(strcpy(buf, "AddNimbusEffect"), XS_Mob_AddNimbusEffect, file, "$$"); newXSproto(strcpy(buf, "ShieldAbility"), XS_Mob_ShieldAbility, file, "$$$$$$$$"); + newXSproto(strcpy(buf, "GetHateRandomClient"), XS_Mob_GetHateRandomClient, file, "$"); + newXSproto(strcpy(buf, "GetHateRandomNPC"), XS_Mob_GetHateRandomNPC, file, "$"); #ifdef BOTS newXSproto(strcpy(buf, "CastToBot"), XS_Mob_CastToBot, file, "$"); + newXSproto(strcpy(buf, "GetHateRandomBot"), XS_Mob_GetHateRandomBot, file, "$"); #endif XSRETURN_YES; } From d197ee631e768e45beb3fa019e7d735b56eb6779 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Tue, 19 Oct 2021 22:25:13 -0500 Subject: [PATCH 277/624] [Saylinks] Fix auto saylink injection edge cases (#1620) * Fix auto saylink injection edge cases * Add even more resiliency to edge cases * Move to split based injection * Add some constants --- common/eqemu_logsys.h | 2 + common/eqemu_logsys_log_aliases.h | 10 ++ common/say_link.cpp | 153 ++++++++++++++++++++++++------ 3 files changed, 135 insertions(+), 30 deletions(-) diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 3ec495413..11808d891 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -126,6 +126,7 @@ namespace Logs { ClientList, DiaWind, HTTP, + Saylink, MaxCategoryID /* Don't Remove this */ }; @@ -210,6 +211,7 @@ namespace Logs { "ClientList", "DialogueWindow", "HTTP", + "Saylink", }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index a949f63a8..753417d81 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -686,6 +686,16 @@ OutF(LogSys, Logs::Detail, Logs::HTTP, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogSaylink(message, ...) do {\ + if (LogSys.log_settings[Logs::Saylink].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Saylink, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogSaylinkDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Saylink].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Saylink, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + #define Log(debug_level, log_category, message, ...) do {\ if (LogSys.log_settings[log_category].is_category_enabled == 1)\ LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ diff --git a/common/say_link.cpp b/common/say_link.cpp index d5083384b..c6347d87c 100644 --- a/common/say_link.cpp +++ b/common/say_link.cpp @@ -102,13 +102,13 @@ const std::string &EQ::SayLinkEngine::GenerateLink() m_Link = ""; LogError("SayLinkEngine::GenerateLink() failed to generate a useable say link"); LogError(">> LinkType: {}, Lengths: [link: {}({}), body: {}({}), text: {}({})]", - m_LinkType, - m_Link.length(), - EQ::constants::SAY_LINK_MAXIMUM_SIZE, - m_LinkBody.length(), - EQ::constants::SAY_LINK_BODY_SIZE, - m_LinkText.length(), - EQ::constants::SAY_LINK_TEXT_SIZE + m_LinkType, + m_Link.length(), + EQ::constants::SAY_LINK_MAXIMUM_SIZE, + m_LinkBody.length(), + EQ::constants::SAY_LINK_BODY_SIZE, + m_LinkText.length(), + EQ::constants::SAY_LINK_TEXT_SIZE ); LogError(">> LinkBody: {}", m_LinkBody.c_str()); LogError(">> LinkText: {}", m_LinkText.c_str()); @@ -343,44 +343,137 @@ std::string EQ::SayLinkEngine::GenerateQuestSaylink(std::string saylink_text, bo std::string EQ::SayLinkEngine::InjectSaylinksIfNotExist(const char *message) { - std::string new_message = message; + std::string new_message = message; + int link_index = 0; + int saylink_index = 0; + std::vector links = {}; + std::vector saylinks = {}; + int saylink_length = 50; + std::string saylink_separator = "\u0012"; + std::string saylink_partial = "000"; - int link_index = 0; - std::vector links = {}; + LogSaylinkDetail("new_message pre pass 1 [{}]", new_message); + + // first pass - strip existing saylinks by putting placeholder anchors on them + for (auto &saylink: split_string(new_message, saylink_separator)) { + if (!saylink.empty() && saylink.length() > saylink_length && + saylink.find(saylink_partial) != std::string::npos) { + saylinks.emplace_back(saylink); + + LogSaylinkDetail("Found saylink [{}]", saylink); + + // replace with anchor + find_replace( + new_message, + fmt::format("{}", saylink), + fmt::format("", saylink_index) + ); + + saylink_index++; + } + } + + LogSaylinkDetail("new_message post pass 1 [{}]", new_message); // loop through brackets until none exist - while (new_message.find('[') != std::string::npos && new_message.find(']') != std::string::npos) { - std::string bracket_message = get_between(new_message, "[", "]"); + if (new_message.find('[') != std::string::npos) { + for (auto &b: split_string(new_message, "[")) { + if (!b.empty() && b.find(']') != std::string::npos) { + std::vector right_split = split_string(b, "]"); + if (!right_split.empty()) { + std::string bracket_message = trim(right_split[0]); - // already a saylink - // todo: improve this later - if (!bracket_message.empty() && bracket_message.length() > 50) { - links.emplace_back(bracket_message); + // we shouldn't see a saylink fragment here, ignore this bracket + if (bracket_message.find(saylink_partial) != std::string::npos) { + continue; + } + + // if non empty bracket contents + if (!bracket_message.empty()) { + LogSaylinkDetail("Found bracket_message [{}]", bracket_message); + + // already a saylink + // todo: improve this later + if (!bracket_message.empty() && + (bracket_message.length() > saylink_length || + bracket_message.find(saylink_separator) != std::string::npos)) { + links.emplace_back(bracket_message); + } + else { + links.emplace_back( + EQ::SayLinkEngine::GenerateQuestSaylink( + bracket_message, + false, + bracket_message + ) + ); + } + + // replace with anchor + find_replace( + new_message, + fmt::format("[{}]", bracket_message), + fmt::format("", link_index) + ); + + link_index++; + } + } + } } - else { - links.emplace_back(EQ::SayLinkEngine::GenerateQuestSaylink(bracket_message, false, bracket_message)); - } - - // replace with anchor - find_replace( - new_message, - fmt::format("[{}]", bracket_message), - fmt::format("", link_index) - ); - - link_index++; } + LogSaylinkDetail("new_message post pass 2 (post brackets) [{}]", new_message); + + // strip any current delimiters of saylinks + find_replace(new_message, saylink_separator, ""); + // pop links onto anchors link_index = 0; for (auto &link: links) { + + // strip any current delimiters of saylinks + find_replace(link, saylink_separator, ""); + find_replace( new_message, - fmt::format("", link_index), - fmt::format("[{}]", link) + fmt::format("", link_index), + fmt::format("[\u0012{}\u0012]", link) ); link_index++; } + LogSaylinkDetail("new_message post pass 3 (post prelink anchor pop) [{}]", new_message); + + // pop links onto anchors + saylink_index = 0; + for (auto &link: saylinks) { + // strip any current delimiters of saylinks + find_replace(link, saylink_separator, ""); + + // check to see if we did a double anchor pass (existing saylink that was also inside brackets) + // this means we found a saylink and we're checking to see if we're already encoded before double encoding + if (new_message.find(fmt::format("\u0012\u0012", saylink_index)) != std::string::npos) { + LogSaylinkDetail("Found encoded saylink at index [{}]", saylink_index); + + find_replace( + new_message, + fmt::format("\u0012\u0012", saylink_index), + fmt::format("\u0012{}\u0012", link) + ); + saylink_index++; + continue; + } + + find_replace( + new_message, + fmt::format("", saylink_index), + fmt::format("\u0012{}\u0012", link) + ); + saylink_index++; + } + + LogSaylinkDetail("new_message post pass 4 (post saylink anchor pop) [{}]", new_message); + return new_message; } From c8385640231d13004bf5aed71a172278e6050e48 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 19 Oct 2021 23:28:10 -0400 Subject: [PATCH 278/624] [Bug Fix] Fix OpenSSL Support for Windows (#1625) --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9022ea306..407b1a221 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -224,6 +224,9 @@ IF(OpenSSL_FOUND AND MBEDTLS_FOUND) SET(TLS_LIBRARY_LIBS ${OPENSSL_LIBRARIES}) SET(TLS_LIBRARY_INCLUDE ${OPENSSL_INCLUDE_DIR}) ADD_DEFINITIONS(-DEQEMU_USE_OPENSSL) + IF(${OPENSSL_VERSION} VERSION_GREATER_EQUAL "1.1.1") + ADD_DEFINITIONS(-DCPPHTTPLIB_OPENSSL_SUPPORT) + ENDIF() ELSEIF(TLS_LIBRARY_SELECTION STREQUAL "mbedTLS") SET(TLS_LIBRARY_TYPE " mbedTLS") SET(TLS_LIBRARY_ENABLED ON) From efab0c4b6bfce37c8563cf4fd57b73fc5b20c371 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Wed, 20 Oct 2021 15:11:14 -0400 Subject: [PATCH 279/624] [Quest API] Add remove LDoN Win/Loss to Perl and Lua. (#1611) * [Quest API] Add remove LDoN Win/Loss to Perl and Lua. - Add $client->RemoveLDoNLoss(theme_id) to Perl. - Add $client->RemoveLDoNWin(theme_id) to Perl. - Add quest::removeldonloss(theme_id) to Perl. - Add quest::removeldonwin(theme_id) to Perl. - Add quest::crosszoneremoveldonlossbycharid(character_id, theme_id) to Perl. - Add quest::crosszoneremoveldonlossbygroupid(group_id, theme_id) to Perl. - Add quest::crosszoneremoveldonlossbyraidid(raid_id, theme_id) to Perl. - Add quest::crosszoneremoveldonlossbyguildid(guild_id, theme_id) to Perl. - Add quest::crosszoneremoveldonlossbyexpeditionid(expedition_id, theme_id) to Perl. - Add quest::crosszoneremoveldonlossbyclientname(client_name, theme_id) to Perl. - Add quest::crosszoneremoveldonwinbycharid(character_id, theme_id) to Perl. - Add quest::crosszoneremoveldonwinbygroupid(group_id, theme_id) to Perl. - Add quest::crosszoneremoveldonwinbyraidid(raid_id, theme_id) to Perl. - Add quest::crosszoneremoveldonwinbyguildid(guild_id, theme_id) to Perl. - Add quest::crosszoneremoveldonwinbyexpeditionid(expedition_id, theme_id) to Perl. - Add quest::crosszoneremoveldonwinbyclientname(client_name, theme_id) to Perl. - Add quest::worldwideaddldonloss(theme_id, min_status, max_status) to Perl. - Add quest::worldwideaddldonwin(theme_id, min_status, max_status) to Perl. - Add client:RemoveLDoNLoss(theme_id) to Lua. - Add client:RemoveLDoNWin(theme_id) to Lua. - Add eq.remove_ldon_loss(theme_id) to Lua. - Add eq.remove_ldon_win(theme_id) to Lua. - Add eq.cross_zone_remove_ldon_loss_by_char_id(character_id, theme_id) to Lua. - Add eq.cross_zone_remove_ldon_loss_by_group_id(group_id, theme_id) to Lua. - Add eq.cross_zone_remove_ldon_loss_by_raid_id(raid_id, theme_id) to Lua. - Add eq.cross_zone_remove_ldon_loss_by_guild_id(guild_id, theme_id) to Lua. - Add eq.cross_zone_remove_ldon_loss_by_expedition_id(expedition_id, theme_id) to Lua. - Add eq.cross_zone_remove_ldon_loss_by_client_name(client_name, theme_id) to Lua. - Add eq.cross_zone_remove_ldon_win_by_char_id(character_id, theme_id) to Lua. - Add eq.cross_zone_remove_ldon_win_by_group_id(group_id, theme_id) to Lua. - Add eq.cross_zone_remove_ldon_win_by_raid_id(raid_id, theme_id) to Lua. - Add eq.cross_zone_remove_ldon_win_by_guild_id(guild_id, theme_id) to Lua. - Add eq.cross_zone_remove_ldon_win_by_expedition_id(expedition_id, theme_id) to Lua. - Add eq.cross_zone_remove_ldon_win_by_client_name(client_name, theme_id) to Lua. - Add eq.world_wide_add_ldon_loss(theme_id, min_status, max_status) to Lua. - Add eq.world_wide_add_ldon_win(theme_id, min_status, max_status) to Lua. Adds enum for LDoN Themes and Theme Bitmasks so we're not using magic numbers. Adds item links to item messages and augment messages on rejection/restriction/Lore. * Update client_packet.cpp * Update client_packet.cpp * Update servertalk.h Alphabetical. --- common/database.cpp | 56 +- common/database.h | 2 +- common/eq_constants.h | 18 + .../base/base_adventure_template_repository.h | 2 +- .../repositories/base/base_items_repository.h | 2 +- common/servertalk.h | 16 +- zone/client.cpp | 151 ++-- zone/client.h | 3 +- zone/client_packet.cpp | 242 +++---- zone/embparser_api.cpp | 416 ++++++++--- zone/inventory.cpp | 684 ++++++++++++------ zone/lua_client.cpp | 18 +- zone/lua_client.h | 2 + zone/lua_general.cpp | 184 ++++- zone/perl_client.cpp | 34 +- zone/questmgr.cpp | 24 +- zone/questmgr.h | 2 + zone/worldserver.cpp | 116 ++- 18 files changed, 1337 insertions(+), 635 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index e694e7801..9a0ea375d 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -2042,62 +2042,64 @@ void Database::ClearRaidLeader(uint32 gid, uint32 rid) QueryDatabase(query); } -void Database::UpdateAdventureStatsEntry(uint32 char_id, uint8 theme, bool win) +void Database::UpdateAdventureStatsEntry(uint32 char_id, uint8 theme, bool win, bool remove) { - std::string field; - - switch(theme) - { - case 1: - { + switch(theme) { + case LDoNThemes::GUK: { field = "guk_"; break; } - case 2: - { + case LDoNThemes::MIR: { field = "mir_"; break; } - case 3: - { + case LDoNThemes::MMC: { field = "mmc_"; break; } - case 4: - { + case LDoNThemes::RUJ: { field = "ruj_"; break; } - case 5: - { + case LDoNThemes::TAK: { field = "tak_"; break; } - default: - { + default: { return; } } - if (win) - field += "wins"; - else - field += "losses"; + field += win ? "wins" : "losses"; + std::string field_operation = remove ? "-" : "+"; - std::string query = StringFormat("UPDATE `adventure_stats` SET %s=%s+1 WHERE player_id=%u",field.c_str(), field.c_str(), char_id); + std::string query = fmt::format( + "UPDATE `adventure_stats` SET {} = {} {} 1 WHERE player_id = {}", + field, + field, + field_operation, + char_id + ); auto results = QueryDatabase(query); - if (results.RowsAffected() != 0) + if (results.RowsAffected() != 0) { return; + } - query = StringFormat("INSERT INTO `adventure_stats` SET %s=1, player_id=%u", field.c_str(), char_id); - QueryDatabase(query); + if (!remove) { + query = fmt::format( + "INSERT INTO `adventure_stats` SET {} = 1, player_id = {}", + field, + char_id + ); + QueryDatabase(query); + } } bool Database::GetAdventureStats(uint32 char_id, AdventureStats_Struct *as) { - std::string query = StringFormat( + std::string query = fmt::format( "SELECT " "`guk_wins`, " "`mir_wins`, " @@ -2112,7 +2114,7 @@ bool Database::GetAdventureStats(uint32 char_id, AdventureStats_Struct *as) "FROM " "`adventure_stats` " "WHERE " - "player_id = %u ", + "player_id = {}", char_id ); auto results = QueryDatabase(query); diff --git a/common/database.h b/common/database.h index 16506dfae..64856ef80 100644 --- a/common/database.h +++ b/common/database.h @@ -176,7 +176,7 @@ public: /* Adventure related. */ - void UpdateAdventureStatsEntry(uint32 char_id, uint8 theme, bool win); + void UpdateAdventureStatsEntry(uint32 char_id, uint8 theme, bool win = false, bool remove = false); bool GetAdventureStats(uint32 char_id, AdventureStats_Struct *as); /* Account Related */ diff --git a/common/eq_constants.h b/common/eq_constants.h index fb6f0c557..6321b2d9b 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -974,4 +974,22 @@ enum class DynamicZoneMemberStatus : uint8_t LinkDead }; +enum LDoNThemes { + Unused = 0, + GUK, + MIR, + MMC, + RUJ, + TAK +}; + +enum LDoNThemeBits { + UnusedBit = 0, + GUKBit = 1, + MIRBit = 2, + MMCBit = 4, + RUJBit = 8, + TAKBit = 16 +}; + #endif /*COMMON_EQ_CONSTANTS_H*/ diff --git a/common/repositories/base/base_adventure_template_repository.h b/common/repositories/base/base_adventure_template_repository.h index 4509268ad..b54e4fa89 100644 --- a/common/repositories/base/base_adventure_template_repository.h +++ b/common/repositories/base/base_adventure_template_repository.h @@ -148,7 +148,7 @@ public: entry.zone_in_time = 1800; entry.win_points = 0; entry.lose_points = 0; - entry.theme = 1; + entry.theme = LDoNThemes::GUK; entry.zone_in_zone_id = 0; entry.zone_in_x = 0; entry.zone_in_y = 0; diff --git a/common/repositories/base/base_items_repository.h b/common/repositories/base/base_items_repository.h index 06a7797b8..8bbbd979e 100644 --- a/common/repositories/base/base_items_repository.h +++ b/common/repositories/base/base_items_repository.h @@ -717,7 +717,7 @@ public: entry.itemclass = 0; entry.itemtype = 0; entry.ldonprice = 0; - entry.ldontheme = 0; + entry.ldontheme = LDoNThemes::Unused; entry.ldonsold = 0; entry.light = 0; entry.lore = ""; diff --git a/common/servertalk.h b/common/servertalk.h index fd91f591e..86d4b08a6 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -271,9 +271,11 @@ enum { }; enum { - CZLDoNUpdateSubtype_Loss, - CZLDoNUpdateSubtype_Points, - CZLDoNUpdateSubtype_Win + CZLDoNUpdateSubtype_AddLoss, + CZLDoNUpdateSubtype_AddPoints, + CZLDoNUpdateSubtype_AddWin, + CZLDoNUpdateSubtype_RemoveLoss, + CZLDoNUpdateSubtype_RemoveWin, }; enum { @@ -297,9 +299,11 @@ enum { }; enum { - WWLDoNUpdateType_Loss, - WWLDoNUpdateType_Points, - WWLDoNUpdateType_Win + WWLDoNUpdateType_AddLoss, + WWLDoNUpdateType_AddPoints, + WWLDoNUpdateType_AddWin, + WWLDoNUpdateType_RemoveLoss, + WWLDoNUpdateType_RemoveWin }; enum { diff --git a/zone/client.cpp b/zone/client.cpp index 215ba2480..aceb82b20 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -318,7 +318,7 @@ Client::Client(EQStreamInterface* ieqs) adventure_stats_timer = nullptr; adventure_leaderboard_timer = nullptr; adv_data = nullptr; - adv_requested_theme = 0; + adv_requested_theme = LDoNThemes::Unused; adv_requested_id = 0; adv_requested_member_count = 0; @@ -1375,9 +1375,8 @@ bool Client::UpdateLDoNPoints(uint32 theme_id, int points) { return false; } - switch (theme_id) - { - case 0: { // No theme, so distribute evenly across all + switch (theme_id) { + case LDoNThemes::Unused: { // No theme, so distribute evenly across all int split_points = (points / 5); int guk_points = (split_points + (points % 5)); int mir_points = split_points; @@ -1421,47 +1420,52 @@ bool Client::UpdateLDoNPoints(uint32 theme_id, int points) { m_pp.ldon_points_ruj += ruj_points; m_pp.ldon_points_tak += tak_points; points -= split_points; - if (split_points != 0) // if anything left, recursively loop thru again + if (split_points != 0) { // if anything left, recursively loop thru again UpdateLDoNPoints(0, split_points); - + } break; } - case 1: { + case LDoNThemes::GUK: { if(points < 0) { - if(m_pp.ldon_points_guk < (0 - points)) + if(m_pp.ldon_points_guk < (0 - points)) { return false; + } } m_pp.ldon_points_guk += points; break; } - case 2: { + case LDoNThemes::MIR: { if(points < 0) { - if(m_pp.ldon_points_mir < (0 - points)) + if(m_pp.ldon_points_mir < (0 - points)) { return false; + } } m_pp.ldon_points_mir += points; break; } - case 3: { + case LDoNThemes::MMC: { if(points < 0) { - if(m_pp.ldon_points_mmc < (0 - points)) + if(m_pp.ldon_points_mmc < (0 - points)) { return false; + } } m_pp.ldon_points_mmc += points; break; } - case 4: { + case LDoNThemes::RUJ: { if(points < 0) { - if(m_pp.ldon_points_ruj < (0 - points)) + if(m_pp.ldon_points_ruj < (0 - points)) { return false; + } } m_pp.ldon_points_ruj += points; break; } - case 5: { + case LDoNThemes::TAK: { if(points < 0) { - if(m_pp.ldon_points_tak < (0 - points)) + if(m_pp.ldon_points_tak < (0 - points)) { return false; + } } m_pp.ldon_points_tak += points; break; @@ -5490,15 +5494,15 @@ uint32 Client::GetLDoNPointsTheme(uint32 t) { switch(t) { - case 1: + case LDoNThemes::GUK: return m_pp.ldon_points_guk; - case 2: + case LDoNThemes::MIR: return m_pp.ldon_points_mir; - case 3: + case LDoNThemes::MMC: return m_pp.ldon_points_mmc; - case 4: + case LDoNThemes::RUJ: return m_pp.ldon_points_ruj; - case 5: + case LDoNThemes::TAK: return m_pp.ldon_points_tak; default: return 0; @@ -5509,15 +5513,15 @@ uint32 Client::GetLDoNWinsTheme(uint32 t) { switch(t) { - case 1: + case LDoNThemes::GUK: return m_pp.ldon_wins_guk; - case 2: + case LDoNThemes::MIR: return m_pp.ldon_wins_mir; - case 3: + case LDoNThemes::MMC: return m_pp.ldon_wins_mmc; - case 4: + case LDoNThemes::RUJ: return m_pp.ldon_wins_ruj; - case 5: + case LDoNThemes::TAK: return m_pp.ldon_wins_tak; default: return 0; @@ -5528,77 +5532,62 @@ uint32 Client::GetLDoNLossesTheme(uint32 t) { switch(t) { - case 1: + case LDoNThemes::GUK: return m_pp.ldon_losses_guk; - case 2: + case LDoNThemes::MIR: return m_pp.ldon_losses_mir; - case 3: + case LDoNThemes::MMC: return m_pp.ldon_losses_mmc; - case 4: + case LDoNThemes::RUJ: return m_pp.ldon_losses_ruj; - case 5: + case LDoNThemes::TAK: return m_pp.ldon_losses_tak; default: return 0; } } -void Client::AddLDoNLoss(uint32 theme_id) -{ - switch (theme_id) - { - case 1: - m_pp.ldon_losses_guk += 1; - database.UpdateAdventureStatsEntry(CharacterID(), theme_id, false); +void Client::UpdateLDoNWinLoss(uint32 theme_id, bool win, bool remove) { + switch (theme_id) { + case LDoNThemes::GUK: + if (win) { + m_pp.ldon_wins_guk += (remove ? -1 : 1); + } else { + m_pp.ldon_losses_guk += (remove ? -1 : 1); + } break; - case 2: - m_pp.ldon_losses_mir += 1; - database.UpdateAdventureStatsEntry(CharacterID(), theme_id, false); + case LDoNThemes::MIR: + if (win) { + m_pp.ldon_wins_mir += (remove ? -1 : 1); + } else { + m_pp.ldon_losses_mir += (remove ? -1 : 1); + } break; - case 3: - m_pp.ldon_losses_mmc += 1; - database.UpdateAdventureStatsEntry(CharacterID(), theme_id, false); + case LDoNThemes::MMC: + if (win) { + m_pp.ldon_wins_mmc += (remove ? -1 : 1); + } else { + m_pp.ldon_losses_mmc += (remove ? -1 : 1); + } break; - case 4: - m_pp.ldon_losses_ruj += 1; - database.UpdateAdventureStatsEntry(CharacterID(), theme_id, false); + case LDoNThemes::RUJ: + if (win) { + m_pp.ldon_wins_ruj += (remove ? -1 : 1); + } else { + m_pp.ldon_losses_ruj += (remove ? -1 : 1); + } break; - case 5: - m_pp.ldon_losses_tak += 1; - database.UpdateAdventureStatsEntry(CharacterID(), theme_id, false); + case LDoNThemes::TAK: + if (win) { + m_pp.ldon_wins_tak += (remove ? -1 : 1); + } else { + m_pp.ldon_losses_tak += (remove ? -1 : 1); + } break; default: return; - } -} - -void Client::AddLDoNWin(uint32 theme_id) -{ - switch (theme_id) - { - case 1: - m_pp.ldon_wins_guk += 1; - database.UpdateAdventureStatsEntry(CharacterID(), theme_id, true); - break; - case 2: - m_pp.ldon_wins_mir += 1; - database.UpdateAdventureStatsEntry(CharacterID(), theme_id, true); - break; - case 3: - m_pp.ldon_wins_mmc += 1; - database.UpdateAdventureStatsEntry(CharacterID(), theme_id, true); - break; - case 4: - m_pp.ldon_wins_ruj += 1; - database.UpdateAdventureStatsEntry(CharacterID(), theme_id, true); - break; - case 5: - m_pp.ldon_wins_tak += 1; - database.UpdateAdventureStatsEntry(CharacterID(), theme_id, true); - break; - default: - return; - } + } + database.UpdateAdventureStatsEntry(CharacterID(), theme_id, win, remove); } @@ -5981,7 +5970,7 @@ void Client::NewAdventure(int id, int theme, const char *text, int member_count, void Client::ClearPendingAdventureData() { adv_requested_id = 0; - adv_requested_theme = 0; + adv_requested_theme = LDoNThemes::Unused; safe_delete_array(adv_requested_data); adv_requested_member_count = 0; } diff --git a/zone/client.h b/zone/client.h index da77e2b25..550be9620 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1323,8 +1323,7 @@ public: uint32 GetLDoNWinsTheme(uint32 t); uint32 GetLDoNLossesTheme(uint32 t); uint32 GetLDoNPointsTheme(uint32 t); - void AddLDoNWin(uint32 theme_id); - void AddLDoNLoss(uint32 theme_id); + void UpdateLDoNWinLoss(uint32 theme_id, bool win = false, bool remove = false); void CheckLDoNHail(Mob *target); void CheckEmoteHail(Mob *target, const char* message); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 73802cac4..f1c47a4d1 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1979,136 +1979,136 @@ void Client::Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app) const EQ::ItemData* 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) { + std::list merchantlists = zone->merchanttable[merchantid]; + for (auto merchantlist : merchantlists) { + if (GetLevel() < merchantlist.level_required) { continue; } - int32 fac = tmp->GetPrimaryFaction(); - if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + int faction_id = tmp->GetPrimaryFaction(); + if (faction_id != 0 && GetModCharacterFactionLevel(faction_id) < merchantlist.faction_required) { continue; } - item = database.GetItem(ml.item); - if (!item) + item = database.GetItem(merchantlist.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(Chat::Red, "Error: The item you purchased does not exist!"); return; } - if (aps->Type == LDoNMerchant) - { - if (m_pp.ldon_points_available < int32(item->LDoNPrice)) { + auto item_cost = item->LDoNPrice; + bool cannot_afford = false; + std::string merchant_type; + if (item_cost < 0) { + Message(Chat::Red, "This item cannot be bought."); + return; + } + + if (aps->Type == LDoNMerchant) { + if (m_pp.ldon_points_available < item_cost) { Message(Chat::Red, "You cannot afford that item."); return; } - if (item->LDoNTheme <= 16) - { - if (item->LDoNTheme & 16) - { - if (m_pp.ldon_points_tak < int32(item->LDoNPrice)) - { - Message(Chat::Red, "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(Chat::Red, "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(Chat::Red, "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(Chat::Red, "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(Chat::Red, "You need at least %u points in guk to purchase this item.", int32(item->LDoNPrice)); - return; + if (item->LDoNTheme <= LDoNThemeBits::TAKBit) { + if (item->LDoNTheme & LDoNThemeBits::TAKBit) { + if (m_pp.ldon_points_tak < item_cost) { + cannot_afford = true; + merchant_type = fmt::format("Deepest Guk Point{}", item_cost != 1 ? "s" : ""); + } + } else if (item->LDoNTheme & LDoNThemeBits::RUJBit) { + if (m_pp.ldon_points_ruj < item_cost) { + cannot_afford = true; + merchant_type = fmt::format("Miragul's Menagerie Point{}", item_cost != 1 ? "s" : ""); + } + } else if (item->LDoNTheme & LDoNThemeBits::MMCBit) { + if (m_pp.ldon_points_mmc < item_cost) { + cannot_afford = true; + merchant_type = fmt::format("Mistmoore Catacombs Point{}", item_cost != 1 ? "s" : ""); + } + } else if (item->LDoNTheme & LDoNThemeBits::MIRBit) { + if (m_pp.ldon_points_mir < item_cost) { + cannot_afford = true; + merchant_type = fmt::format("Rujarkian Hills Point{}", item_cost != 1 ? "s" : ""); + } + } else if (item->LDoNTheme & LDoNThemeBits::GUKBit) { + if (m_pp.ldon_points_guk < item_cost) { + cannot_afford = true; + merchant_type = fmt::format("Takish-Hiz Point{}", item_cost != 1 ? "s" : ""); } } + + + } + } else if (aps->Type == DiscordMerchant) { + if (GetPVPPoints() < item_cost) { + cannot_afford = true; + merchant_type = fmt::format("PVP Point{}", item_cost != 1 ? "s" : ""); + } + } else if (aps->Type == NorrathsKeepersMerchant) { + if (GetRadiantCrystals() < item_cost) { + cannot_afford = true; + merchant_type = database.CreateItemLink(RuleI(Zone, RadiantCrystalItemID)); + } + } else if (aps->Type == DarkReignMerchant) { + if (GetEbonCrystals() < item_cost) { + cannot_afford = true; + merchant_type = database.CreateItemLink(RuleI(Zone, EbonCrystalItemID)); + } + } else { + Message(Chat::Red, "Unknown Adventure Merchant Type."); + return; + } + + if (cannot_afford && !merchant_type.empty()) { + Message( + Chat::Red, + fmt::format( + "You need at least {} {} to purchase this item.", + item_cost, + merchant_type + ).c_str() + ); + } + + + if (CheckLoreConflict(item)) { + Message( + Chat::Yellow, + fmt::format( + "You already have a lore {} ({}) in your inventory.", + database.CreateItemLink(item->ID), + item->ID + ).c_str() + ); + return; + } + + if (aps->Type == LDoNMerchant) { + int required_points = item_cost * -1; + + if (!UpdateLDoNPoints(6, required_points)) { + return; } } else if (aps->Type == DiscordMerchant) { - if (GetPVPPoints() < item->LDoNPrice) - { - Message(Chat::Red, "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(Chat::Red, "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(Chat::Red, "You need at least %u Ebon Crystals to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else - { - Message(Chat::Red, "Unknown Adventure Merchant type."); - return; - } - - - if (CheckLoreConflict(item)) - { - Message(Chat::Yellow, "You can only have one of a lore item."); - return; - } - - if (aps->Type == LDoNMerchant) - { - int32 requiredpts = (int32)item->LDoNPrice*-1; - - if (!UpdateLDoNPoints(6, requiredpts)) - return; - } - else if (aps->Type == DiscordMerchant) - { - SetPVPPoints(GetPVPPoints() - (int32)item->LDoNPrice); + SetPVPPoints(GetPVPPoints() - item_cost); SendPVPStats(); } else if (aps->Type == NorrathsKeepersMerchant) { - SetRadiantCrystals(GetRadiantCrystals() - (int32)item->LDoNPrice); + SetRadiantCrystals(GetRadiantCrystals() - item_cost); } else if (aps->Type == DarkReignMerchant) { @@ -2165,36 +2165,20 @@ void Client::Handle_OP_AdventureMerchantRequest(const EQApplicationPacket *app) } 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; + if (item) { + uint32 theme = LDoNThemes::Unused; + if (item->LDoNTheme > LDoNThemeBits::TAKBit) { + theme = LDoNThemes::Unused; + } else if (item->LDoNTheme & LDoNThemeBits::TAKBit) { + theme = LDoNThemes::TAK; + } else if (item->LDoNTheme & LDoNThemeBits::RUJBit) { + theme = LDoNThemes::RUJ; + } else if (item->LDoNTheme & LDoNThemeBits::MMCBit) { + theme = LDoNThemes::MMC; + } else if (item->LDoNTheme & LDoNThemeBits::RUJBit) { + theme = LDoNThemes::MIR; + } else if (item->LDoNTheme & LDoNThemeBits::GUKBit) { + theme = LDoNThemes::GUK; } ss << "^" << item->Name << "|"; ss << item->ID << "|"; diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 98e061c8c..fda5cd9b1 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -5314,7 +5314,7 @@ XS(XS__crosszoneaddldonlossbycharid) { Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonlossbycharid(int character_id, uint32 theme_id)"); { uint8 update_type = CZUpdateType_Character; - uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + uint8 update_subtype = CZLDoNUpdateSubtype_AddLoss; int character_id = (int) SvIV(ST(0)); uint32 theme_id = (uint32) SvUV(ST(1)); quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id); @@ -5329,7 +5329,7 @@ XS(XS__crosszoneaddldonlossbygroupid) { Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonlossbygroupid(int group_id, uint32 theme_id)"); { uint8 update_type = CZUpdateType_Group; - uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + uint8 update_subtype = CZLDoNUpdateSubtype_AddLoss; int group_id = (int) SvIV(ST(0)); uint32 theme_id = (uint32) SvUV(ST(1)); quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id); @@ -5344,7 +5344,7 @@ XS(XS__crosszoneaddldonlossbyraidid) { Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonlossbyraidid(int raid_id, uint32 theme_id)"); { uint8 update_type = CZUpdateType_Raid; - uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + uint8 update_subtype = CZLDoNUpdateSubtype_AddLoss; int raid_id = (int) SvIV(ST(0)); uint32 theme_id = (uint32) SvUV(ST(1)); quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id); @@ -5359,7 +5359,7 @@ XS(XS__crosszoneaddldonlossbyguildid) { Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonlossbyguildid(int guild_id, uint32 theme_id)"); { uint8 update_type = CZUpdateType_Guild; - uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + uint8 update_subtype = CZLDoNUpdateSubtype_AddLoss; int guild_id = (int) SvIV(ST(0)); uint32 theme_id = (uint32) SvUV(ST(1)); quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id); @@ -5374,7 +5374,7 @@ XS(XS__crosszoneaddldonlossbyexpeditionid) { Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonlossbyexpeditionid(uint32 expedition_id, uint32 theme_id)"); { uint8 update_type = CZUpdateType_Expedition; - uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + uint8 update_subtype = CZLDoNUpdateSubtype_AddLoss; uint32 expedition_id = (uint32) SvUV(ST(0)); uint32 theme_id = (uint32) SvUV(ST(1)); quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id); @@ -5389,7 +5389,7 @@ XS(XS__crosszoneaddldonlossbyclientname) { Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonlossbyclientname(const char* client_name, uint32 theme_id)"); { uint8 update_type = CZUpdateType_ClientName; - uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + uint8 update_subtype = CZLDoNUpdateSubtype_AddLoss; int update_identifier = 0; int points = 1; const char* client_name = (const char*) SvPV_nolen(ST(0)); @@ -5406,7 +5406,7 @@ XS(XS__crosszoneaddldonpointsbycharid) { Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonpointsbycharid(int character_id, uint32 theme_id, int points)"); { uint8 update_type = CZUpdateType_Character; - uint8 update_subtype = CZLDoNUpdateSubtype_Points; + uint8 update_subtype = CZLDoNUpdateSubtype_AddPoints; int character_id = (int) SvIV(ST(0)); uint32 theme_id = (uint32) SvUV(ST(1)); int points = (int) SvIV(ST(2)); @@ -5422,7 +5422,7 @@ XS(XS__crosszoneaddldonpointsbygroupid) { Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonpointsbygroupid(int group_id, uint32 theme_id, int points)"); { uint8 update_type = CZUpdateType_Group; - uint8 update_subtype = CZLDoNUpdateSubtype_Points; + uint8 update_subtype = CZLDoNUpdateSubtype_AddPoints; int group_id = (int) SvIV(ST(0)); uint32 theme_id = (uint32) SvUV(ST(1)); int points = (int) SvIV(ST(2)); @@ -5438,7 +5438,7 @@ XS(XS__crosszoneaddldonpointsbyraidid) { Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonpointsbyraidid(int raid_id, uint32 theme_id, int points)"); { uint8 update_type = CZUpdateType_Raid; - uint8 update_subtype = CZLDoNUpdateSubtype_Points; + uint8 update_subtype = CZLDoNUpdateSubtype_AddPoints; int raid_id = (int) SvIV(ST(0)); uint32 theme_id = (uint32) SvUV(ST(1)); int points = (int) SvIV(ST(2)); @@ -5454,7 +5454,7 @@ XS(XS__crosszoneaddldonpointsbyguildid) { Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonpointsbyguildid(int guild_id, uint32 theme_id, int points)"); { uint8 update_type = CZUpdateType_Guild; - uint8 update_subtype = CZLDoNUpdateSubtype_Points; + uint8 update_subtype = CZLDoNUpdateSubtype_AddPoints; int guild_id = (int) SvIV(ST(0)); uint32 theme_id = (uint32) SvUV(ST(1)); int points = (int) SvIV(ST(2)); @@ -5470,7 +5470,7 @@ XS(XS__crosszoneaddldonpointsbyexpeditionid) { Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonpointsbyexpeditionid(uint32 expedition_id, uint32 theme_id, int points)"); { uint8 update_type = CZUpdateType_Expedition; - uint8 update_subtype = CZLDoNUpdateSubtype_Points; + uint8 update_subtype = CZLDoNUpdateSubtype_AddPoints; uint32 expedition_id = (uint32) SvUV(ST(0)); uint32 theme_id = (uint32) SvUV(ST(1)); int points = (int) SvIV(ST(2)); @@ -5486,7 +5486,7 @@ XS(XS__crosszoneaddldonpointsbyclientname) { Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonpointsbyclientname(const char* client_name, uint32 theme_id, int points)"); { uint8 update_type = CZUpdateType_ClientName; - uint8 update_subtype = CZLDoNUpdateSubtype_Points; + uint8 update_subtype = CZLDoNUpdateSubtype_AddPoints; int update_identifier = 0; const char* client_name = (const char*) SvPV_nolen(ST(0)); uint32 theme_id = (uint32) SvUV(ST(1)); @@ -5503,7 +5503,7 @@ XS(XS__crosszoneaddldonwinbycharid) { Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonwinbycharid(int character_id, uint32 theme_id)"); { uint8 update_type = CZUpdateType_Character; - uint8 update_subtype = CZLDoNUpdateSubtype_Win; + uint8 update_subtype = CZLDoNUpdateSubtype_AddWin; int character_id = (int) SvIV(ST(0)); uint32 theme_id = (uint32) SvUV(ST(1)); quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id); @@ -5518,7 +5518,7 @@ XS(XS__crosszoneaddldonwinbygroupid) { Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonwinbygroupid(int group_id, uint32 theme_id)"); { uint8 update_type = CZUpdateType_Group; - uint8 update_subtype = CZLDoNUpdateSubtype_Win; + uint8 update_subtype = CZLDoNUpdateSubtype_AddWin; int group_id = (int) SvIV(ST(0)); uint32 theme_id = (uint32) SvUV(ST(1)); quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id); @@ -5533,7 +5533,7 @@ XS(XS__crosszoneaddldonwinbyraidid) { Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonwinbyraidid(int raid_id, uint32 theme_id)"); { uint8 update_type = CZUpdateType_Raid; - uint8 update_subtype = CZLDoNUpdateSubtype_Win; + uint8 update_subtype = CZLDoNUpdateSubtype_AddWin; int raid_id = (int) SvIV(ST(0)); uint32 theme_id = (uint32) SvUV(ST(1)); quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id); @@ -5548,7 +5548,7 @@ XS(XS__crosszoneaddldonwinbyguildid) { Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonwinbyguildid(int guild_id, uint32 theme_id)"); { uint8 update_type = CZUpdateType_Guild; - uint8 update_subtype = CZLDoNUpdateSubtype_Win; + uint8 update_subtype = CZLDoNUpdateSubtype_AddWin; int guild_id = (int) SvIV(ST(0)); uint32 theme_id = (uint32) SvUV(ST(1)); quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id); @@ -5563,7 +5563,7 @@ XS(XS__crosszoneaddldonwinbyexpeditionid) { Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonwinbyexpeditionid(uint32 expedition_id, uint32 theme_id)"); { uint8 update_type = CZUpdateType_Expedition; - uint8 update_subtype = CZLDoNUpdateSubtype_Win; + uint8 update_subtype = CZLDoNUpdateSubtype_AddWin; uint32 expedition_id = (uint32) SvUV(ST(0)); uint32 theme_id = (uint32) SvUV(ST(1)); quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id); @@ -5578,7 +5578,7 @@ XS(XS__crosszoneaddldonwinbyclientname) { Perl_croak(aTHX_ "Usage: quest::crosszoneaddldonwinbyclientname(const char* client_name, uint32 theme_id)"); { uint8 update_type = CZUpdateType_ClientName; - uint8 update_subtype = CZLDoNUpdateSubtype_Win; + uint8 update_subtype = CZLDoNUpdateSubtype_AddWin; int update_identifier = 0; int points = 1; const char* client_name = (const char*) SvPV_nolen(ST(0)); @@ -6618,6 +6618,190 @@ XS(XS__crosszonemoveinstancebyclientname) { XSRETURN_EMPTY; } +XS(XS__crosszoneremoveldonlossbycharid); +XS(XS__crosszoneremoveldonlossbycharid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneremoveldonlossbycharid(int character_id, uint32 theme_id)"); + { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZLDoNUpdateSubtype_RemoveLoss; + int character_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneremoveldonlossbygroupid); +XS(XS__crosszoneremoveldonlossbygroupid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneremoveldonlossbygroupid(int group_id, uint32 theme_id)"); + { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZLDoNUpdateSubtype_RemoveLoss; + int group_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneremoveldonlossbyraidid); +XS(XS__crosszoneremoveldonlossbyraidid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneremoveldonlossbyraidid(int raid_id, uint32 theme_id)"); + { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZLDoNUpdateSubtype_RemoveLoss; + int raid_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneremoveldonlossbyguildid); +XS(XS__crosszoneremoveldonlossbyguildid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneremoveldonlossbyguildid(int guild_id, uint32 theme_id)"); + { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZLDoNUpdateSubtype_RemoveLoss; + int guild_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneremoveldonlossbyexpeditionid); +XS(XS__crosszoneremoveldonlossbyexpeditionid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneremoveldonlossbyexpeditionid(uint32 expedition_id, uint32 theme_id)"); + { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZLDoNUpdateSubtype_RemoveLoss; + uint32 expedition_id = (uint32) SvUV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneremoveldonlossbyclientname); +XS(XS__crosszoneremoveldonlossbyclientname) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneremoveldonlossbyclientname(const char* client_name, uint32 theme_id)"); + { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZLDoNUpdateSubtype_RemoveLoss; + int update_identifier = 0; + int points = 1; + const char* client_name = (const char*) SvPV_nolen(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, update_identifier, theme_id, points, client_name); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneremoveldonwinbycharid); +XS(XS__crosszoneremoveldonwinbycharid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneremoveldonwinbycharid(int character_id, uint32 theme_id)"); + { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZLDoNUpdateSubtype_RemoveWin; + int character_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneremoveldonwinbygroupid); +XS(XS__crosszoneremoveldonwinbygroupid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneremoveldonwinbygroupid(int group_id, uint32 theme_id)"); + { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZLDoNUpdateSubtype_RemoveWin; + int group_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneremoveldonwinbyraidid); +XS(XS__crosszoneremoveldonwinbyraidid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneremoveldonwinbyraidid(int raid_id, uint32 theme_id)"); + { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZLDoNUpdateSubtype_RemoveWin; + int raid_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneremoveldonwinbyguildid); +XS(XS__crosszoneremoveldonwinbyguildid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneremoveldonwinbyguildid(int guild_id, uint32 theme_id)"); + { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZLDoNUpdateSubtype_RemoveWin; + int guild_id = (int) SvIV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneremoveldonwinbyexpeditionid); +XS(XS__crosszoneremoveldonwinbyexpeditionid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneremoveldonwinbyexpeditionid(uint32 expedition_id, uint32 theme_id)"); + { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZLDoNUpdateSubtype_RemoveWin; + uint32 expedition_id = (uint32) SvUV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id); + } + XSRETURN_EMPTY; +} + +XS(XS__crosszoneremoveldonwinbyclientname); +XS(XS__crosszoneremoveldonwinbyclientname) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::crosszoneremoveldonwinbyclientname(const char* client_name, uint32 theme_id)"); + { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZLDoNUpdateSubtype_RemoveWin; + int update_identifier = 0; + int points = 1; + const char* client_name = (const char*) SvPV_nolen(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(1)); + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, update_identifier, theme_id, points, client_name); + } + XSRETURN_EMPTY; +} + XS(XS__crosszoneremovespellbycharid); XS(XS__crosszoneremovespellbycharid) { dXSARGS; @@ -7265,6 +7449,75 @@ XS(XS__crosszoneupdateactivitybyclientname) { XSRETURN_EMPTY; } +XS(XS__worldwideaddldonloss); +XS(XS__worldwideaddldonloss) { + dXSARGS; + if (items < 1 || items > 3) + Perl_croak(aTHX_ "Usage: quest::worldwideaddldonloss(uint32 theme_id, [min_status = 0, max_status = 0])"); + { + uint8 update_type = CZLDoNUpdateSubtype_AddLoss; + uint32 theme_id = (uint32)SvUV(ST(0)); + int points = 1; + uint8 min_status = 0; + uint8 max_status = 0; + if (items == 2) + min_status = (uint8)SvUV(ST(1)); + + if (items == 3) + max_status = (uint8)SvUV(ST(2)); + + quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status, max_status); + } + XSRETURN_EMPTY; +} + +XS(XS__worldwideaddldonpoints); +XS(XS__worldwideaddldonpoints) { + dXSARGS; + if (items < 1 || items > 4) + Perl_croak(aTHX_ "Usage: quest::worldwideaddldonpoints(uint32 theme_id. [int points = 1, min_status = 0, max_status = 0])"); + { + uint8 update_type = CZLDoNUpdateSubtype_AddPoints; + uint32 theme_id = (uint32)SvUV(ST(0)); + int points = 1; + uint8 min_status = 0; + uint8 max_status = 0; + if (items == 2) + points = (int)SvIV(ST(1)); + + if (items == 3) + min_status = (uint8)SvUV(ST(2)); + + if (items == 4) + max_status = (uint8)SvUV(ST(3)); + + quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status, max_status); + } + XSRETURN_EMPTY; +} + +XS(XS__worldwideaddldonwin); +XS(XS__worldwideaddldonwin) { + dXSARGS; + if (items < 1 || items > 3) + Perl_croak(aTHX_ "Usage: quest::worldwideaddldonwin(uint32 theme_id, [min_status = 0, max_status = 0])"); + { + uint8 update_type = CZLDoNUpdateSubtype_AddWin; + uint32 theme_id = (uint32)SvUV(ST(0)); + int points = 1; + uint8 min_status = 0; + uint8 max_status = 0; + if (items == 2) + min_status = (uint8)SvUV(ST(1)); + + if (items == 3) + max_status = (uint8)SvUV(ST(2)); + + quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status, max_status); + } + XSRETURN_EMPTY; +} + XS(XS__worldwideassigntask); XS(XS__worldwideassigntask) { dXSARGS; @@ -7492,6 +7745,50 @@ XS(XS__worldwidemoveinstance) { XSRETURN_EMPTY; } +XS(XS__worldwideremoveldonloss); +XS(XS__worldwideremoveldonloss) { + dXSARGS; + if (items < 1 || items > 3) + Perl_croak(aTHX_ "Usage: quest::worldwideremoveldonloss(uint32 theme_id, [min_status = 0, max_status = 0])"); + { + uint8 update_type = CZLDoNUpdateSubtype_RemoveLoss; + uint32 theme_id = (uint32)SvUV(ST(0)); + int points = 1; + uint8 min_status = 0; + uint8 max_status = 0; + if (items == 2) + min_status = (uint8)SvUV(ST(1)); + + if (items == 3) + max_status = (uint8)SvUV(ST(2)); + + quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status, max_status); + } + XSRETURN_EMPTY; +} + +XS(XS__worldwideremoveldonwin); +XS(XS__worldwideremoveldonwin) { + dXSARGS; + if (items < 1 || items > 3) + Perl_croak(aTHX_ "Usage: quest::worldwideremoveldonwin(uint32 theme_id, [min_status = 0, max_status = 0])"); + { + uint8 update_type = CZLDoNUpdateSubtype_RemoveWin; + uint32 theme_id = (uint32)SvUV(ST(0)); + int points = 1; + uint8 min_status = 0; + uint8 max_status = 0; + if (items == 2) + min_status = (uint8)SvUV(ST(1)); + + if (items == 3) + max_status = (uint8)SvUV(ST(2)); + + quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status, max_status); + } + XSRETURN_EMPTY; +} + XS(XS__worldwideremovespell); XS(XS__worldwideremovespell) { dXSARGS; @@ -7659,75 +7956,6 @@ XS(XS__worldwideupdateactivity) { XSRETURN_EMPTY; } -XS(XS__worldwideaddldonloss); -XS(XS__worldwideaddldonloss) { - dXSARGS; - if (items < 1 || items > 3) - Perl_croak(aTHX_ "Usage: quest::worldwideaddldonloss(uint32 theme_id, [min_status = 0, max_status = 0])"); - { - uint8 update_type = CZLDoNUpdateSubtype_Loss; - uint32 theme_id = (uint32) SvUV(ST(0)); - int points = 1; - uint8 min_status = 0; - uint8 max_status = 0; - if (items == 2) - min_status = (uint8) SvUV(ST(1)); - - if (items == 3) - max_status = (uint8) SvUV(ST(2)); - - quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status, max_status); - } - XSRETURN_EMPTY; -} - -XS(XS__worldwideaddldonpoints); -XS(XS__worldwideaddldonpoints) { - dXSARGS; - if (items < 1 || items > 4) - Perl_croak(aTHX_ "Usage: quest::worldwideaddldonpoints(uint32 theme_id. [int points = 1, min_status = 0, max_status = 0])"); - { - uint8 update_type = CZLDoNUpdateSubtype_Points; - uint32 theme_id = (uint32) SvUV(ST(0)); - int points = 1; - uint8 min_status = 0; - uint8 max_status = 0; - if (items == 2) - points = (int) SvIV(ST(1)); - - if (items == 3) - min_status = (uint8) SvUV(ST(2)); - - if (items == 4) - max_status = (uint8) SvUV(ST(3)); - - quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status, max_status); - } - XSRETURN_EMPTY; -} - -XS(XS__worldwideaddldonwin); -XS(XS__worldwideaddldonwin) { - dXSARGS; - if (items < 1 || items > 3) - Perl_croak(aTHX_ "Usage: quest::worldwideaddldonwin(uint32 theme_id, [min_status = 0, max_status = 0])"); - { - uint8 update_type = CZLDoNUpdateSubtype_Win; - uint32 theme_id = (uint32) SvUV(ST(0)); - int points = 1; - uint8 min_status = 0; - uint8 max_status = 0; - if (items == 2) - min_status = (uint8) SvUV(ST(1)); - - if (items == 3) - max_status = (uint8) SvUV(ST(2)); - - quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status, max_status); - } - XSRETURN_EMPTY; -} - XS(XS__isnpcspawned); XS(XS__isnpcspawned) { dXSARGS; @@ -7944,6 +8172,18 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "crosszonemoveinstancebyguildid"), XS__crosszonemoveinstancebyguildid, file); newXS(strcpy(buf, "crosszonemoveinstancebyexpeditionid"), XS__crosszonemoveinstancebyexpeditionid, file); newXS(strcpy(buf, "crosszonemoveinstancebyclientname"), XS__crosszonemoveinstancebyclientname, file); + newXS(strcpy(buf, "crosszoneremoveldonlossbycharid"), XS__crosszoneremoveldonlossbycharid, file); + newXS(strcpy(buf, "crosszoneremoveldonlossbygroupid"), XS__crosszoneremoveldonlossbygroupid, file); + newXS(strcpy(buf, "crosszoneremoveldonlossbyraidid"), XS__crosszoneremoveldonlossbyraidid, file); + newXS(strcpy(buf, "crosszoneremoveldonlossbyguildid"), XS__crosszoneremoveldonlossbyguildid, file); + newXS(strcpy(buf, "crosszoneremoveldonlossbyexpeditionid"), XS__crosszoneremoveldonlossbyexpeditionid, file); + newXS(strcpy(buf, "crosszoneremoveldonlossbyclientname"), XS__crosszoneremoveldonlossbyclientname, file); + newXS(strcpy(buf, "crosszoneremoveldonwinbycharid"), XS__crosszoneremoveldonwinbycharid, file); + newXS(strcpy(buf, "crosszoneremoveldonwinbygroupid"), XS__crosszoneremoveldonwinbygroupid, file); + newXS(strcpy(buf, "crosszoneremoveldonwinbyraidid"), XS__crosszoneremoveldonwinbyraidid, file); + newXS(strcpy(buf, "crosszoneremoveldonwinbyguildid"), XS__crosszoneremoveldonwinbyguildid, file); + newXS(strcpy(buf, "crosszoneremoveldonwinbyexpeditionid"), XS__crosszoneremoveldonwinbyexpeditionid, file); + newXS(strcpy(buf, "crosszoneremoveldonwinbyclientname"), XS__crosszoneremoveldonwinbyclientname, file); newXS(strcpy(buf, "crosszoneremovespellbycharid"), XS__crosszoneremovespellbycharid, file); newXS(strcpy(buf, "crosszoneremovespellbygroupid"), XS__crosszoneremovespellbygroupid, file); newXS(strcpy(buf, "crosszoneremovespellbyraidid"), XS__crosszoneremovespellbyraidid, file); @@ -7994,6 +8234,8 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "worldwidemessage"), XS__worldwidemessage, file); newXS(strcpy(buf, "worldwidemove"), XS__worldwidemove, file); newXS(strcpy(buf, "worldwidemoveinstance"), XS__worldwidemoveinstance, file); + newXS(strcpy(buf, "worldwideremoveldonloss"), XS__worldwideremoveldonloss, file); + newXS(strcpy(buf, "worldwideremoveldonwin"), XS__worldwideremoveldonwin, file); newXS(strcpy(buf, "worldwideremovespell"), XS__worldwideremovespell, file); newXS(strcpy(buf, "worldwideremovetask"), XS__worldwideremovetask, file); newXS(strcpy(buf, "worldwideresetactivity"), XS__worldwideresetactivity, file); diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 0a805ad07..d6c3c5e79 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -186,25 +186,57 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, // make sure the item exists if(item == nullptr) { - Message(Chat::Red, "Item %u does not exist.", item_id); - LogInventory("Player [{}] on account [{}] attempted to create an item with an invalid id.\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", - GetName(), account_name, item_id, aug1, aug2, aug3, aug4, aug5, aug6); - + Message( + Chat::Red, + fmt::format( + "Item {} does not exist.", + item_id + ).c_str() + ); + LogInventory( + "Player [{}] on account [{}] attempted to create an item with an invalid id.\n" + "Item [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", + GetName(), + account_name, + item_id, + aug1, + aug2, + aug3, + aug4, + aug5, + aug6 + ); return false; } // check that there is not a lore conflict between base item and existing inventory else if(CheckLoreConflict(item)) { // DuplicateLoreMessage(item_id); - Message(Chat::Red, "You already have a lore %s (%i) in your inventory.", item->Name, item_id); - + Message( + Chat::Red, + fmt::format( + "You already have a lore {} ({}) in your inventory.", + database.CreateItemLink(item_id), + item_id + ).c_str() + ); return false; } // check to make sure we are augmenting an augmentable item else if (((!item->IsClassCommon()) || (item->AugType > 0)) && (aug1 | aug2 | aug3 | aug4 | aug5 | aug6)) { Message(Chat::Red, "You can not augment an augment or a non-common class item."); - LogInventory("Player [{}] on account [{}] attempted to augment an augment or a non-common class item.\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug5: [{}])\n", - GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5, aug6); - + LogInventory( + "Player [{}] on account [{}] attempted to augment an augment or a non-common class item.\n" + "Item [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug5: [{}])\n", + GetName(), + account_name, + item->ID, + aug1, + aug2, + aug3, + aug4, + aug5, + aug6 + ); return false; } @@ -216,7 +248,7 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, /* else if(item->MinStatus && ((this->Admin() < item->MinStatus) || (this->Admin() < RuleI(GM, MinStatusToSummonItem)))) { Message(Chat::Red, "You are not a GM or do not have the status to summon this item."); - LogInventory("Player [{}] on account [{}] attempted to create a GM-only item with a status of [{}].\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}], MinStatus: [{}])\n", + LogInventory("Player [{}] on account [{}] attempted to create a GM-only item with a status of [{}].\n"Item [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}], MinStatus: [{}])\n", GetName(), account_name, this->Admin(), item->ID, aug1, aug2, aug3, aug4, aug5, aug6, item->MinStatus); return false; @@ -224,23 +256,39 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, */ uint32 augments[EQ::invaug::SOCKET_COUNT] = { aug1, aug2, aug3, aug4, aug5, aug6 }; - - uint32 classes = item->Classes; - uint32 races = item->Races; - uint32 slots = item->Slots; - - bool enforcewear = RuleB(Inventory, EnforceAugmentWear); - bool enforcerestr = RuleB(Inventory, EnforceAugmentRestriction); - bool enforceusable = RuleB(Inventory, EnforceAugmentUsability); - + uint32 classes = item->Classes; + uint32 races = item->Races; + uint32 slots = item->Slots; + bool enforce_wearable = RuleB(Inventory, EnforceAugmentWear); + bool enforce_restrictions = RuleB(Inventory, EnforceAugmentRestriction); + bool enforce_usable = RuleB(Inventory, EnforceAugmentUsability); for (int iter = EQ::invaug::SOCKET_BEGIN; iter <= EQ::invaug::SOCKET_END; ++iter) { + int augment_slot = iter + 1; const EQ::ItemData* augtest = database.GetItem(augments[iter]); - if(augtest == nullptr) { if(augments[iter]) { - Message(Chat::Red, "Augment %u (Aug%i) does not exist.", augments[iter], iter + 1); - LogInventory("Player [{}] on account [{}] attempted to create an augment (Aug[{}]) with an invalid id.\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", - GetName(), account_name, (iter + 1), item->ID, aug1, aug2, aug3, aug4, aug5, aug6); + Message( + Chat::Red, + fmt::format( + "Augment {} in Augment Slot {} does not exist.", + augments[iter], + augment_slot + ).c_str() + ); + LogInventory( + "Player [{}] on account [{}] attempted to create an augment (Aug[{}]) with an invalid id.\n" + "Item [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", + GetName(), + account_name, + augment_slot, + item->ID, + aug1, + aug2, + aug3, + aug4, + aug5, + aug6 + ); return false; } @@ -249,15 +297,42 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, // check that there is not a lore conflict between augment and existing inventory if(CheckLoreConflict(augtest)) { // DuplicateLoreMessage(augtest->ID); - Message(Chat::Red, "You already have a lore %s (%u) in your inventory.", augtest->Name, augtest->ID); + Message( + Chat::Red, + fmt::format( + "You already have a lore {} ({}) in your inventory.", + database.CreateItemLink(augtest->ID), + augtest->ID + ).c_str() + ); return false; } // check that augment is an actual augment else if(augtest->AugType == 0) { - Message(Chat::Red, "%s (%u) (Aug%i) is not an actual augment.", augtest->Name, augtest->ID, iter + 1); - LogInventory("Player [{}] on account [{}] attempted to use a non-augment item (Aug[{}]) as an augment.\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", - GetName(), account_name, item->ID, (iter + 1), aug1, aug2, aug3, aug4, aug5, aug6); + Message( + Chat::Red, + fmt::format( + "{} ({}) in Augment Slot {} is not an actual augment.", + database.CreateItemLink(augtest->ID), + augtest->ID, + augment_slot + ).c_str() + ); + LogInventory( + "Player [{}] on account [{}] attempted to use a non-augment item (Augment Slot [{}]) as an augment.\n" + "Item [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", + GetName(), + account_name, + item->ID, + augment_slot, + aug1, + aug2, + aug3, + aug4, + aug5, + aug6 + ); return false; } @@ -269,232 +344,352 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, else if(augtest->MinStatus && ((this->Admin() < augtest->MinStatus) || (this->Admin() < RuleI(GM, MinStatusToSummonItem)))) { Message(Chat::Red, "You are not a GM or do not have the status to summon this augment."); LogInventory("Player [{}] on account [{}] attempted to create a GM-only augment (Aug[{}]) with a status of [{}].\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], MinStatus: [{}])\n", - GetName(), account_name, (iter + 1), this->Admin(), item->ID, aug1, aug2, aug3, aug4, aug5, aug6, item->MinStatus); + GetName(), account_name, augment_slot, this->Admin(), item->ID, aug1, aug2, aug3, aug4, aug5, aug6, item->MinStatus); return false; } */ // check for augment type allowance - if(enforcewear) { + if(enforce_wearable) { if ((item->AugSlotType[iter] == EQ::item::AugTypeNone) || !(((uint32)1 << (item->AugSlotType[iter] - 1)) & augtest->AugType)) { - Message(Chat::Red, "Augment %u (Aug%i) is not acceptable wear on Item %u.", augments[iter], iter + 1, item->ID); - LogInventory("Player [{}] on account [{}] attempted to augment an item with an unacceptable augment type (Aug[{}]).\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", - GetName(), account_name, (iter + 1), item->ID, aug1, aug2, aug3, aug4, aug5, aug6); + Message( + Chat::Red, + fmt::format( + "Augment {} ({}) in Augment Slot {} is not capable of being socketed in to {} ({}).", + database.CreateItemLink(augments[iter]), + augments[iter], + augment_slot, + database.CreateItemLink(item->ID), + item->ID + ).c_str() + ); + LogInventory( + "Player [{}] on account [{}] attempted to augment an item with an unacceptable augment type (Aug[{}]).\n" + "Item [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", + GetName(), + account_name, + augment_slot, + item->ID, + aug1, + aug2, + aug3, + aug4, + aug5, + aug6 + ); return false; } if(item->AugSlotVisible[iter] == 0) { - Message(Chat::Red, "Item %u has not evolved enough to accept Augment %u (Aug%i).", item->ID, augments[iter], iter + 1); - LogInventory("Player [{}] on account [{}] attempted to augment an unevolved item with augment type (Aug[{}]).\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", - GetName(), account_name, (iter + 1), item->ID, aug1, aug2, aug3, aug4, aug5, aug6); + Message( + Chat::Red, + "{} ({}) has not evolved enough to accept {} ({}) in Augment Slot {}.", + database.CreateItemLink(item->ID), + item->ID, + database.CreateItemLink(augments[iter]), + augments[iter], + augment_slot + ); + LogInventory( + "Player [{}] on account [{}] attempted to augment an unevolved item with augment type (Aug[{}]).\n" + "Item [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", + GetName(), + account_name, + augment_slot, + item->ID, + aug1, + aug2, + aug3, + aug4, + aug5, + aug6 + ); return false; } } // check for augment to item restriction - if(enforcerestr) { - bool restrictfail = false; - uint8 it = item->ItemType; - - switch(augtest->AugRestrict) { - case EQ::item::AugRestrictionAny: - break; - case EQ::item::AugRestrictionArmor: - switch(it) { - case EQ::item::ItemTypeArmor: + if(enforce_restrictions) { + bool is_restricted = false; + uint8 item_type = item->ItemType; + switch (augtest->AugRestrict) { + case EQ::item::AugRestrictionAny: break; + case EQ::item::AugRestrictionArmor: + switch (item_type) { + case EQ::item::ItemTypeArmor: + break; + default: + is_restricted = true; + break; + } + break; + case EQ::item::AugRestrictionWeapons: + switch (item_type) { + case EQ::item::ItemType1HSlash: + case EQ::item::ItemType1HBlunt: + case EQ::item::ItemType1HPiercing: + case EQ::item::ItemTypeMartial: + case EQ::item::ItemType2HSlash: + case EQ::item::ItemType2HBlunt: + case EQ::item::ItemType2HPiercing: + case EQ::item::ItemTypeBow: + break; + default: + is_restricted = true; + break; + } + break; + case EQ::item::AugRestriction1HWeapons: + switch (item_type) { + case EQ::item::ItemType1HSlash: + case EQ::item::ItemType1HBlunt: + case EQ::item::ItemType1HPiercing: + case EQ::item::ItemTypeMartial: + break; + default: + is_restricted = true; + break; + } + break; + case EQ::item::AugRestriction2HWeapons: + switch (item_type) { + case EQ::item::ItemType2HSlash: + case EQ::item::ItemType2HBlunt: + case EQ::item::ItemType2HPiercing: + case EQ::item::ItemTypeBow: + break; + default: + is_restricted = true; + break; + } + break; + case EQ::item::AugRestriction1HSlash: + switch (item_type) { + case EQ::item::ItemType1HSlash: + break; + default: + is_restricted = true; + break; + } + break; + case EQ::item::AugRestriction1HBlunt: + switch (item_type) { + case EQ::item::ItemType1HBlunt: + break; + default: + is_restricted = true; + break; + } + break; + case EQ::item::AugRestrictionPiercing: + switch (item_type) { + case EQ::item::ItemType1HPiercing: + break; + default: + is_restricted = true; + break; + } + break; + case EQ::item::AugRestrictionHandToHand: + switch (item_type) { + case EQ::item::ItemTypeMartial: + break; + default: + is_restricted = true; + break; + } + break; + case EQ::item::AugRestriction2HSlash: + switch (item_type) { + case EQ::item::ItemType2HSlash: + break; + default: + is_restricted = true; + break; + } + break; + case EQ::item::AugRestriction2HBlunt: + switch (item_type) { + case EQ::item::ItemType2HBlunt: + break; + default: + is_restricted = true; + break; + } + break; + case EQ::item::AugRestriction2HPierce: + switch (item_type) { + case EQ::item::ItemType2HPiercing: + break; + default: + is_restricted = true; + break; + } + break; + case EQ::item::AugRestrictionBows: + switch (item_type) { + case EQ::item::ItemTypeBow: + break; + default: + is_restricted = true; + break; + } + break; + case EQ::item::AugRestrictionShields: + switch (item_type) { + case EQ::item::ItemTypeShield: + break; + default: + is_restricted = true; + break; + } + break; + case EQ::item::AugRestriction1HSlash1HBluntOrHandToHand: + switch (item_type) { + case EQ::item::ItemType1HSlash: + case EQ::item::ItemType1HBlunt: + case EQ::item::ItemTypeMartial: + break; + default: + is_restricted = true; + break; + } + break; + case EQ::item::AugRestriction1HBluntOrHandToHand: + switch (item_type) { + case EQ::item::ItemType1HBlunt: + case EQ::item::ItemTypeMartial: + break; + default: + is_restricted = true; + break; + } + break; + // These 3 are in-work + case EQ::item::AugRestrictionUnknown1: + case EQ::item::AugRestrictionUnknown2: + case EQ::item::AugRestrictionUnknown3: default: - restrictfail = true; + is_restricted = true; break; - } - break; - case EQ::item::AugRestrictionWeapons: - switch(it) { - case EQ::item::ItemType1HSlash: - case EQ::item::ItemType1HBlunt: - case EQ::item::ItemType1HPiercing: - case EQ::item::ItemTypeMartial: - case EQ::item::ItemType2HSlash: - case EQ::item::ItemType2HBlunt: - case EQ::item::ItemType2HPiercing: - case EQ::item::ItemTypeBow: - break; - default: - restrictfail = true; - break; - } - break; - case EQ::item::AugRestriction1HWeapons: - switch(it) { - case EQ::item::ItemType1HSlash: - case EQ::item::ItemType1HBlunt: - case EQ::item::ItemType1HPiercing: - case EQ::item::ItemTypeMartial: - break; - default: - restrictfail = true; - break; - } - break; - case EQ::item::AugRestriction2HWeapons: - switch(it) { - case EQ::item::ItemType2HSlash: - case EQ::item::ItemType2HBlunt: - case EQ::item::ItemType2HPiercing: - case EQ::item::ItemTypeBow: - break; - default: - restrictfail = true; - break; - } - break; - case EQ::item::AugRestriction1HSlash: - switch(it) { - case EQ::item::ItemType1HSlash: - break; - default: - restrictfail = true; - break; - } - break; - case EQ::item::AugRestriction1HBlunt: - switch(it) { - case EQ::item::ItemType1HBlunt: - break; - default: - restrictfail = true; - break; - } - break; - case EQ::item::AugRestrictionPiercing: - switch(it) { - case EQ::item::ItemType1HPiercing: - break; - default: - restrictfail = true; - break; - } - break; - case EQ::item::AugRestrictionHandToHand: - switch(it) { - case EQ::item::ItemTypeMartial: - break; - default: - restrictfail = true; - break; - } - break; - case EQ::item::AugRestriction2HSlash: - switch(it) { - case EQ::item::ItemType2HSlash: - break; - default: - restrictfail = true; - break; - } - break; - case EQ::item::AugRestriction2HBlunt: - switch(it) { - case EQ::item::ItemType2HBlunt: - break; - default: - restrictfail = true; - break; - } - break; - case EQ::item::AugRestriction2HPierce: - switch(it) { - case EQ::item::ItemType2HPiercing: - break; - default: - restrictfail = true; - break; - } - break; - case EQ::item::AugRestrictionBows: - switch(it) { - case EQ::item::ItemTypeBow: - break; - default: - restrictfail = true; - break; - } - break; - case EQ::item::AugRestrictionShields: - switch(it) { - case EQ::item::ItemTypeShield: - break; - default: - restrictfail = true; - break; - } - break; - case EQ::item::AugRestriction1HSlash1HBluntOrHandToHand: - switch(it) { - case EQ::item::ItemType1HSlash: - case EQ::item::ItemType1HBlunt: - case EQ::item::ItemTypeMartial: - break; - default: - restrictfail = true; - break; - } - break; - case EQ::item::AugRestriction1HBluntOrHandToHand: - switch(it) { - case EQ::item::ItemType1HBlunt: - case EQ::item::ItemTypeMartial: - break; - default: - restrictfail = true; - break; - } - break; - // These 3 are in-work - case EQ::item::AugRestrictionUnknown1: - case EQ::item::AugRestrictionUnknown2: - case EQ::item::AugRestrictionUnknown3: - default: - restrictfail = true; - break; } - if(restrictfail) { - Message(Chat::Red, "Augment %u (Aug%i) is restricted from wear on Item %u.", augments[iter], (iter + 1), item->ID); - LogInventory("Player [{}] on account [{}] attempted to augment an item with a restricted augment (Aug[{}]).\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", - GetName(), account_name, (iter + 1), item->ID, aug1, aug2, aug3, aug4, aug5, aug6); + if(is_restricted) { + Message( + Chat::Red, + fmt::format( + "{} ({}) in Augment Slot {} is restricted from being augmented in to {} ({}).", + database.CreateItemLink(augments[iter]), + augments[iter], + augment_slot, + database.CreateItemLink(item->ID), + item->ID + ).c_str() + ); + LogInventory( + "Player [{}] on account [{}] attempted to augment an item with a restricted augment (Aug[{}]).\n" + "Item [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", + GetName(), + account_name, + augment_slot, + item->ID, + aug1, + aug2, + aug3, + aug4, + aug5, + aug6 + ); return false; } } - if(enforceusable) { + if(enforce_usable) { // check for class usability if(item->Classes && !(classes &= augtest->Classes)) { - Message(Chat::Red, "Augment %u (Aug%i) will result in an item not usable by any class.", augments[iter], (iter + 1)); - LogInventory("Player [{}] on account [{}] attempted to create an item unusable by any class.\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", - GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5, aug6); + Message( + Chat::Red, + fmt::format( + "{} ({}) in Augment Slot {} will result in an item unusable by any class.", + database.CreateItemLink(augments[iter]), + augments[iter], + augment_slot + ).c_str() + ); + LogInventory( + "Player [{}] on account [{}] attempted to create an item unusable by any class.\n" + "Item [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", + GetName(), + account_name, + item->ID, + aug1, + aug2, + aug3, + aug4, + aug5, + aug6 + ); return false; } // check for race usability if(item->Races && !(races &= augtest->Races)) { - Message(Chat::Red, "Augment %u (Aug%i) will result in an item not usable by any race.", augments[iter], (iter + 1)); - LogInventory("Player [{}] on account [{}] attempted to create an item unusable by any race.\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", - GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5, aug6); + Message( + Chat::Red, + fmt::format( + "{} ({}) in Augment Slot {} will result in an item unusable by any race.", + database.CreateItemLink(augments[iter]), + augments[iter], + augment_slot + ).c_str() + ); + LogInventory( + "Player [{}] on account [{}] attempted to create an item unusable by any race.\n" + "Item [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", + GetName(), + account_name, + item->ID, + aug1, + aug2, + aug3, + aug4, + aug5, + aug6 + ); return false; } // check for slot usability if(item->Slots && !(slots &= augtest->Slots)) { - Message(Chat::Red, "Augment %u (Aug%i) will result in an item not usable in any slot.", augments[iter], (iter + 1)); - LogInventory("Player [{}] on account [{}] attempted to create an item unusable in any slot.\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", - GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5, aug6); + Message( + Chat::Red, + fmt::format( + "{} ({}) in Augment Slot {} will result in an item unusable in any slot.", + database.CreateItemLink(augments[iter]), + augments[iter], + augment_slot + ).c_str() + ); + LogInventory( + "Player [{}] on account [{}] attempted to create an item unusable in any slot.\n" + "Item [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", + GetName(), + account_name, + item->ID, + aug1, + aug2, + aug3, + aug4, + aug5, + aug6 + ); return false; } @@ -506,12 +701,11 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, // if the item is stackable and the charge amount is -1 or 0 then set to 1 charge. // removed && item->MaxCharges == 0 if -1 or 0 was passed max charges is irrelevant - if(charges <= 0 && item->Stackable) + if(charges <= 0 && item->Stackable) { charges = 1; - - // if the charges is -1, then no charge value was passed in set to max charges - else if(charges == -1) + } else if(charges == -1) { // if the charges is -1, then no charge value was passed in set to max charges charges = item->MaxCharges; + } // in any other situation just use charges as passed @@ -520,35 +714,67 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, if(inst == nullptr) { Message(Chat::Red, "An unknown server error has occurred and your item was not created."); // this goes to logfile since this is a major error - LogError("Player [{}] on account [{}] encountered an unknown item creation error.\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", - GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5, aug6); + LogError( + "Player [{}] on account [{}] encountered an unknown item creation error.\n" + "Item [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", + GetName(), + account_name, + item->ID, + aug1, + aug2, + aug3, + aug4, + aug5, + aug6 + ); return false; } // add any validated augments for (int iter = EQ::invaug::SOCKET_BEGIN; iter <= EQ::invaug::SOCKET_END; ++iter) { - if(augments[iter]) + if(augments[iter]) { inst->PutAugment(&database, iter, augments[iter]); + } } // attune item - if(attuned && inst->GetItem()->Attuneable) + if(attuned && inst->GetItem()->Attuneable) { inst->SetAttuned(true); + } inst->SetOrnamentIcon(ornament_icon); inst->SetOrnamentationIDFile(ornament_idfile); inst->SetOrnamentHeroModel(ornament_hero_model); // check to see if item is usable in requested slot - if (enforceusable && (to_slot >= EQ::invslot::EQUIPMENT_BEGIN && to_slot <= EQ::invslot::EQUIPMENT_END)) { + if (enforce_usable && (to_slot >= EQ::invslot::EQUIPMENT_BEGIN && to_slot <= EQ::invslot::EQUIPMENT_END)) { uint32 slottest = to_slot; - if(!(slots & ((uint32)1 << slottest))) { - Message(0, "This item is not equipable at slot %u - moving to cursor.", to_slot); - LogInventory("Player [{}] on account [{}] attempted to equip an item unusable in slot [{}] - moved to cursor.\n(Item: [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", - GetName(), account_name, to_slot, item->ID, aug1, aug2, aug3, aug4, aug5, aug6); - + Message( + Chat::White, + fmt::format( + "{} ({}) cannot be equipped in {} ({}), moving to cursor.", + database.CreateItemLink(item->ID), + item->ID, + EQ::invslot::GetInvPossessionsSlotName(to_slot), + to_slot + ).c_str() + ); + LogInventory( + "Player [{}] on account [{}] attempted to equip an item unusable in slot [{}] - moved to cursor.\n" + "Item [{}], Aug1: [{}], Aug2: [{}], Aug3: [{}], Aug4: [{}], Aug5: [{}], Aug6: [{}])\n", + GetName(), + account_name, + to_slot, + item->ID, + aug1, + aug2, + aug3, + aug4, + aug5, + aug6 + ); to_slot = EQ::invslot::slotCursor; } } @@ -557,8 +783,7 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, if (to_slot == EQ::invslot::slotCursor) { PushItemOnCursor(*inst); SendItemPacket(EQ::invslot::slotCursor, inst, ItemPacketLimbo); - } - else { + } else { PutItemInInventory(to_slot, *inst, true); } @@ -566,8 +791,9 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, // discover item and any augments if((RuleB(Character, EnableDiscoveredItems)) && !GetGM()) { - if(!IsDiscovered(item_id)) + if(!IsDiscovered(item_id)) { DiscoverItem(item_id); + } /* // Augments should have been discovered prior to being placed on an item. for (int iter = AUG_BEGIN; iter < EQ::constants::ITEM_COMMON_SIZE; ++iter) { @@ -2522,7 +2748,7 @@ void Client::DisenchantSummonedBags(bool client_update) { for (auto slot_id = EQ::invslot::GENERAL_BEGIN; slot_id <= EQ::invslot::GENERAL_END; ++slot_id) { if (((uint64)1 << slot_id) & GetInv().GetLookup()->PossessionsBitmask == 0) - continue; // not useable this session - will be disenchanted once player logs in on client that doesn't exclude affected slots + continue; // not usable this session - will be disenchanted once player logs in on client that doesn't exclude affected slots auto inst = m_inv[slot_id]; if (!inst) { continue; } diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index eb9bef15d..441b490a1 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -2141,12 +2141,12 @@ void Lua_Client::SetEXPModifier(uint32 zone_id, double exp_modifier) { void Lua_Client::AddLDoNLoss(uint32 theme_id) { Lua_Safe_Call_Void(); - self->AddLDoNLoss(theme_id); + self->UpdateLDoNWinLoss(theme_id); } void Lua_Client::AddLDoNWin(uint32 theme_id) { Lua_Safe_Call_Void(); - self->AddLDoNWin(theme_id); + self->UpdateLDoNWinLoss(theme_id, true); } void Lua_Client::SetHideMe(bool hide_me_state) { @@ -2281,6 +2281,16 @@ void Lua_Client::SummonBaggedItems(uint32 bag_item_id, luabind::adl::object bag_ self->SummonBaggedItems(bag_item_id, bagged_items); } +void Lua_Client::RemoveLDoNLoss(uint32 theme_id) { + Lua_Safe_Call_Void(); + self->UpdateLDoNWinLoss(theme_id, false, true); +} + +void Lua_Client::RemoveLDoNWin(uint32 theme_id) { + Lua_Safe_Call_Void(); + self->UpdateLDoNWinLoss(theme_id, true, true); +} + luabind::scope lua_register_client() { return luabind::class_("Client") .def(luabind::constructor<>()) @@ -2661,7 +2671,9 @@ luabind::scope lua_register_client() { .def("SetGMStatus", (void(Lua_Client::*)(int32))&Lua_Client::SetGMStatus) .def("UntrainDiscBySpellID", (void(Lua_Client::*)(uint16))&Lua_Client::UntrainDiscBySpellID) .def("UntrainDiscBySpellID", (void(Lua_Client::*)(uint16,bool))&Lua_Client::UntrainDiscBySpellID) - .def("SummonBaggedItems", (void(Lua_Client::*)(uint32,luabind::adl::object))&Lua_Client::SummonBaggedItems); + .def("SummonBaggedItems", (void(Lua_Client::*)(uint32,luabind::adl::object))&Lua_Client::SummonBaggedItems) + .def("RemoveLDoNLoss", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveLDoNLoss) + .def("RemoveLDoNWin", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveLDoNWin); } luabind::scope lua_register_inventory_where() { diff --git a/zone/lua_client.h b/zone/lua_client.h index add5089b7..a5fbd0834 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -81,6 +81,8 @@ public: uint32 GetTotalSecondsPlayed(); void AddLDoNLoss(uint32 theme_id); void AddLDoNWin(uint32 theme_id); + void RemoveLDoNLoss(uint32 theme_id); + void RemoveLDoNWin(uint32 theme_id); void UpdateLDoNPoints(uint32 theme_id, int points); void SetDeity(int v); void AddEXP(uint32 add_exp); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index b6652302b..283ed5db8 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -1889,6 +1889,14 @@ void lua_add_ldon_win(uint32 theme_id) { quest_manager.addldonwin(theme_id); } +void lua_remove_ldon_loss(uint32 theme_id) { + quest_manager.removeldonloss(theme_id); +} + +void lua_remove_ldon_win(uint32 theme_id) { + quest_manager.addldonwin(theme_id); +} + std::string lua_get_clean_npc_name_by_id(uint32 npc_id) { return quest_manager.getcleannpcnamebyid(npc_id); } @@ -1927,37 +1935,37 @@ int lua_get_spell_stat(uint32 spell_id, std::string stat_identifier, uint8 slot) void lua_cross_zone_add_ldon_loss_by_char_id(int character_id, uint32 theme_id) { uint8 update_type = CZUpdateType_Character; - uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + uint8 update_subtype = CZLDoNUpdateSubtype_AddLoss; quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id); } void lua_cross_zone_add_ldon_loss_by_group_id(int group_id, uint32 theme_id) { uint8 update_type = CZUpdateType_Group; - uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + uint8 update_subtype = CZLDoNUpdateSubtype_AddLoss; quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id); } void lua_cross_zone_add_ldon_loss_by_raid_id(int raid_id, uint32 theme_id) { uint8 update_type = CZUpdateType_Raid; - uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + uint8 update_subtype = CZLDoNUpdateSubtype_AddLoss; quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id); } void lua_cross_zone_add_ldon_loss_by_guild_id(int guild_id, uint32 theme_id) { uint8 update_type = CZUpdateType_Guild; - uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + uint8 update_subtype = CZLDoNUpdateSubtype_AddLoss; quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id); } void lua_cross_zone_add_ldon_loss_by_expedition_id(uint32 expedition_id, uint32 theme_id) { uint8 update_type = CZUpdateType_Expedition; - uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + uint8 update_subtype = CZLDoNUpdateSubtype_AddLoss; quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id); } void lua_cross_zone_add_ldon_loss_by_client_name(const char* client_name, uint32 theme_id) { uint8 update_type = CZUpdateType_ClientName; - uint8 update_subtype = CZLDoNUpdateSubtype_Loss; + uint8 update_subtype = CZLDoNUpdateSubtype_AddLoss; int update_identifier = 0; int points = 1; quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, update_identifier, theme_id, points, client_name); @@ -1965,74 +1973,74 @@ void lua_cross_zone_add_ldon_loss_by_client_name(const char* client_name, uint32 void lua_cross_zone_add_ldon_points_by_char_id(int character_id, uint32 theme_id, int points) { uint8 update_type = CZUpdateType_Character; - uint8 update_subtype = CZLDoNUpdateSubtype_Points; + uint8 update_subtype = CZLDoNUpdateSubtype_AddPoints; quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id, points); } void lua_cross_zone_add_ldon_points_by_group_id(int group_id, uint32 theme_id, int points) { uint8 update_type = CZUpdateType_Group; - uint8 update_subtype = CZLDoNUpdateSubtype_Points; + uint8 update_subtype = CZLDoNUpdateSubtype_AddPoints; quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id, points); } void lua_cross_zone_add_ldon_points_by_raid_id(int raid_id, uint32 theme_id, int points) { uint8 update_type = CZUpdateType_Raid; - uint8 update_subtype = CZLDoNUpdateSubtype_Points; + uint8 update_subtype = CZLDoNUpdateSubtype_AddPoints; quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id, points); } void lua_cross_zone_add_ldon_points_by_guild_id(int guild_id, uint32 theme_id, int points) { uint8 update_type = CZUpdateType_Guild; - uint8 update_subtype = CZLDoNUpdateSubtype_Points; + uint8 update_subtype = CZLDoNUpdateSubtype_AddPoints; quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id, points); } void lua_cross_zone_add_ldon_points_by_expedition_id(uint32 expedition_id, uint32 theme_id, int points) { uint8 update_type = CZUpdateType_Expedition; - uint8 update_subtype = CZLDoNUpdateSubtype_Points; + uint8 update_subtype = CZLDoNUpdateSubtype_AddPoints; quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id, points); } void lua_cross_zone_add_ldon_points_by_client_name(const char* client_name, uint32 theme_id, int points) { uint8 update_type = CZUpdateType_ClientName; - uint8 update_subtype = CZLDoNUpdateSubtype_Points; + uint8 update_subtype = CZLDoNUpdateSubtype_AddPoints; int update_identifier = 0; quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, update_identifier, theme_id, points, client_name); } void lua_cross_zone_add_ldon_win_by_char_id(int character_id, uint32 theme_id) { uint8 update_type = CZUpdateType_Character; - uint8 update_subtype = CZLDoNUpdateSubtype_Win; + uint8 update_subtype = CZLDoNUpdateSubtype_AddWin; quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id); } void lua_cross_zone_add_ldon_win_by_group_id(int group_id, uint32 theme_id) { uint8 update_type = CZUpdateType_Group; - uint8 update_subtype = CZLDoNUpdateSubtype_Win; + uint8 update_subtype = CZLDoNUpdateSubtype_AddWin; quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id); } void lua_cross_zone_add_ldon_win_by_raid_id(int raid_id, uint32 theme_id) { uint8 update_type = CZUpdateType_Raid; - uint8 update_subtype = CZLDoNUpdateSubtype_Win; + uint8 update_subtype = CZLDoNUpdateSubtype_AddWin; quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id); } void lua_cross_zone_add_ldon_win_by_guild_id(int guild_id, uint32 theme_id) { uint8 update_type = CZUpdateType_Guild; - uint8 update_subtype = CZLDoNUpdateSubtype_Win; + uint8 update_subtype = CZLDoNUpdateSubtype_AddWin; quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id); } void lua_cross_zone_add_ldon_win_by_expedition_id(uint32 expedition_id, uint32 theme_id) { uint8 update_type = CZUpdateType_Expedition; - uint8 update_subtype = CZLDoNUpdateSubtype_Win; + uint8 update_subtype = CZLDoNUpdateSubtype_AddWin; quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id); } void lua_cross_zone_add_ldon_win_by_client_name(const char* client_name, uint32 theme_id) { uint8 update_type = CZUpdateType_ClientName; - uint8 update_subtype = CZLDoNUpdateSubtype_Win; + uint8 update_subtype = CZLDoNUpdateSubtype_AddWin; int update_identifier = 0; int points = 1; quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, update_identifier, theme_id, points, client_name); @@ -2523,6 +2531,82 @@ void lua_cross_zone_move_instance_by_client_name(const char* client_name, uint16 quest_manager.CrossZoneMove(update_type, update_subtype, update_identifier, zone_short_name, instance_id, client_name); } +void lua_cross_zone_remove_ldon_loss_by_char_id(int character_id, uint32 theme_id) { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZLDoNUpdateSubtype_RemoveLoss; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id); +} + +void lua_cross_zone_remove_ldon_loss_by_group_id(int group_id, uint32 theme_id) { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZLDoNUpdateSubtype_RemoveLoss; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id); +} + +void lua_cross_zone_remove_ldon_loss_by_raid_id(int raid_id, uint32 theme_id) { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZLDoNUpdateSubtype_RemoveLoss; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id); +} + +void lua_cross_zone_remove_ldon_loss_by_guild_id(int guild_id, uint32 theme_id) { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZLDoNUpdateSubtype_RemoveLoss; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id); +} + +void lua_cross_zone_remove_ldon_loss_by_expedition_id(uint32 expedition_id, uint32 theme_id) { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZLDoNUpdateSubtype_RemoveLoss; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id); +} + +void lua_cross_zone_remove_ldon_loss_by_client_name(const char* client_name, uint32 theme_id) { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZLDoNUpdateSubtype_RemoveLoss; + int update_identifier = 0; + int points = 1; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, update_identifier, theme_id, points, client_name); +} + +void lua_cross_zone_remove_ldon_win_by_char_id(int character_id, uint32 theme_id) { + uint8 update_type = CZUpdateType_Character; + uint8 update_subtype = CZLDoNUpdateSubtype_RemoveWin; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, character_id, theme_id); +} + +void lua_cross_zone_remove_ldon_win_by_group_id(int group_id, uint32 theme_id) { + uint8 update_type = CZUpdateType_Group; + uint8 update_subtype = CZLDoNUpdateSubtype_RemoveWin; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, group_id, theme_id); +} + +void lua_cross_zone_remove_ldon_win_by_raid_id(int raid_id, uint32 theme_id) { + uint8 update_type = CZUpdateType_Raid; + uint8 update_subtype = CZLDoNUpdateSubtype_RemoveWin; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, raid_id, theme_id); +} + +void lua_cross_zone_remove_ldon_win_by_guild_id(int guild_id, uint32 theme_id) { + uint8 update_type = CZUpdateType_Guild; + uint8 update_subtype = CZLDoNUpdateSubtype_RemoveWin; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, guild_id, theme_id); +} + +void lua_cross_zone_remove_ldon_win_by_expedition_id(uint32 expedition_id, uint32 theme_id) { + uint8 update_type = CZUpdateType_Expedition; + uint8 update_subtype = CZLDoNUpdateSubtype_RemoveWin; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, expedition_id, theme_id); +} + +void lua_cross_zone_remove_ldon_win_by_client_name(const char* client_name, uint32 theme_id) { + uint8 update_type = CZUpdateType_ClientName; + uint8 update_subtype = CZLDoNUpdateSubtype_RemoveWin; + int update_identifier = 0; + int points = 1; + quest_manager.CrossZoneLDoNUpdate(update_type, update_subtype, update_identifier, theme_id, points, client_name); +} + void lua_cross_zone_remove_spell_by_char_id(int character_id, uint32 spell_id) { uint8 update_type = CZUpdateType_Character; uint8 update_subtype = CZSpellUpdateSubtype_Remove; @@ -2824,50 +2908,50 @@ void lua_cross_zone_update_activity_by_client_name(const char* client_name, uint } void lua_world_wide_add_ldon_loss(uint32 theme_id) { - uint8 update_type = WWLDoNUpdateType_Loss; + uint8 update_type = WWLDoNUpdateType_AddLoss; quest_manager.WorldWideLDoNUpdate(update_type, theme_id); } void lua_world_wide_add_ldon_loss(uint32 theme_id, uint8 min_status) { - uint8 update_type = WWLDoNUpdateType_Loss; + uint8 update_type = WWLDoNUpdateType_AddLoss; int points = 1; quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status); } void lua_world_wide_add_ldon_loss(uint32 theme_id, uint8 min_status, uint8 max_status) { - uint8 update_type = WWLDoNUpdateType_Loss; + uint8 update_type = WWLDoNUpdateType_AddLoss; int points = 1; quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status, max_status); } void lua_world_wide_add_ldon_points(uint32 theme_id, int points) { - uint8 update_type = WWLDoNUpdateType_Points; + uint8 update_type = WWLDoNUpdateType_AddPoints; quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points); } void lua_world_wide_add_ldon_points(uint32 theme_id, int points, uint8 min_status) { - uint8 update_type = WWLDoNUpdateType_Points; + uint8 update_type = WWLDoNUpdateType_AddPoints; quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status); } void lua_world_wide_add_ldon_points(uint32 theme_id, int points, uint8 min_status, uint8 max_status) { - uint8 update_type = WWLDoNUpdateType_Points; + uint8 update_type = WWLDoNUpdateType_AddPoints; quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status, max_status); } void lua_world_wide_add_ldon_win(uint32 theme_id) { - uint8 update_type = WWLDoNUpdateType_Win; + uint8 update_type = WWLDoNUpdateType_AddWin; quest_manager.WorldWideLDoNUpdate(update_type, theme_id); } void lua_world_wide_add_ldon_win(uint32 theme_id, uint8 min_status) { - uint8 update_type = WWLDoNUpdateType_Win; + uint8 update_type = WWLDoNUpdateType_AddWin; int points = 1; quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status); } void lua_world_wide_add_ldon_win(uint32 theme_id, uint8 min_status, uint8 max_status) { - uint8 update_type = WWLDoNUpdateType_Win; + uint8 update_type = WWLDoNUpdateType_AddWin; int points = 1; quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status, max_status); } @@ -3047,6 +3131,40 @@ void lua_world_wide_move_instance(uint16 instance_id, uint8 min_status, uint8 ma quest_manager.WorldWideMove(update_type, zone_short_name, instance_id, min_status, max_status); } +void lua_world_wide_remove_ldon_loss(uint32 theme_id) { + uint8 update_type = WWLDoNUpdateType_RemoveLoss; + quest_manager.WorldWideLDoNUpdate(update_type, theme_id); +} + +void lua_world_wide_remove_ldon_loss(uint32 theme_id, uint8 min_status) { + uint8 update_type = WWLDoNUpdateType_RemoveLoss; + int points = 1; + quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status); +} + +void lua_world_wide_remove_ldon_loss(uint32 theme_id, uint8 min_status, uint8 max_status) { + uint8 update_type = WWLDoNUpdateType_RemoveLoss; + int points = 1; + quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status, max_status); +} + +void lua_world_wide_remove_ldon_win(uint32 theme_id) { + uint8 update_type = WWLDoNUpdateType_RemoveWin; + quest_manager.WorldWideLDoNUpdate(update_type, theme_id); +} + +void lua_world_wide_remove_ldon_win(uint32 theme_id, uint8 min_status) { + uint8 update_type = WWLDoNUpdateType_RemoveWin; + int points = 1; + quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status); +} + +void lua_world_wide_remove_ldon_win(uint32 theme_id, uint8 min_status, uint8 max_status) { + uint8 update_type = WWLDoNUpdateType_RemoveWin; + int points = 1; + quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status, max_status); +} + void lua_world_wide_remove_spell(uint32 spell_id) { uint8 update_type = WWSpellUpdateType_Remove; quest_manager.WorldWideSpell(update_type, spell_id); @@ -3753,6 +3871,18 @@ luabind::scope lua_register_general() { luabind::def("cross_zone_move_instance_by_guild_id", &lua_cross_zone_move_instance_by_guild_id), luabind::def("cross_zone_move_instance_by_expedition_id", &lua_cross_zone_move_instance_by_expedition_id), luabind::def("cross_zone_move_instance_by_client_name", &lua_cross_zone_move_instance_by_client_name), + luabind::def("cross_zone_remove_ldon_loss_by_char_id", &lua_cross_zone_remove_ldon_loss_by_char_id), + luabind::def("cross_zone_remove_ldon_loss_by_group_id", &lua_cross_zone_remove_ldon_loss_by_group_id), + luabind::def("cross_zone_remove_ldon_loss_by_raid_id", &lua_cross_zone_remove_ldon_loss_by_raid_id), + luabind::def("cross_zone_remove_ldon_loss_by_guild_id", &lua_cross_zone_remove_ldon_loss_by_guild_id), + luabind::def("cross_zone_remove_ldon_loss_by_expedition_id", &lua_cross_zone_remove_ldon_loss_by_expedition_id), + luabind::def("cross_zone_remove_ldon_loss_by_client_name", &lua_cross_zone_remove_ldon_loss_by_client_name), + luabind::def("cross_zone_remove_ldon_win_by_char_id", &lua_cross_zone_remove_ldon_win_by_char_id), + luabind::def("cross_zone_remove_ldon_win_by_group_id", &lua_cross_zone_remove_ldon_win_by_group_id), + luabind::def("cross_zone_remove_ldon_win_by_raid_id", &lua_cross_zone_remove_ldon_win_by_raid_id), + luabind::def("cross_zone_remove_ldon_win_by_guild_id", &lua_cross_zone_remove_ldon_win_by_guild_id), + luabind::def("cross_zone_remove_ldon_win_by_expedition_id", &lua_cross_zone_remove_ldon_win_by_expedition_id), + luabind::def("cross_zone_remove_ldon_win_by_client_name", &lua_cross_zone_remove_ldon_win_by_client_name), luabind::def("cross_zone_remove_spell_by_char_id", &lua_cross_zone_remove_spell_by_char_id), luabind::def("cross_zone_remove_spell_by_group_id", &lua_cross_zone_remove_spell_by_group_id), luabind::def("cross_zone_remove_spell_by_raid_id", &lua_cross_zone_remove_spell_by_raid_id), diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index de16ec1dd..264b3b09b 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -5555,7 +5555,7 @@ XS(XS_Client_AddLDoNLoss) { Client* THIS; uint32 theme_id = (uint32) SvUV(ST(1)); VALIDATE_THIS_IS_CLIENT; - THIS->AddLDoNLoss(theme_id); + THIS->UpdateLDoNWinLoss(theme_id); } XSRETURN_EMPTY; } @@ -5569,7 +5569,7 @@ XS(XS_Client_AddLDoNWin) { Client* THIS; uint32 theme_id = (uint32) SvUV(ST(1)); VALIDATE_THIS_IS_CLIENT; - THIS->AddLDoNWin(theme_id); + THIS->UpdateLDoNWinLoss(theme_id, true); } XSRETURN_EMPTY; } @@ -5837,6 +5837,34 @@ XS(XS_Client_SummonBaggedItems) { XSRETURN_EMPTY; } +XS(XS_Client_RemoveLDoNLoss); +XS(XS_Client_RemoveLDoNLoss) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::RemoveLDoNLoss(THIS, uint32 theme_id)"); + { + Client* THIS; + uint32 theme_id = (uint32) SvUV(ST(1)); + VALIDATE_THIS_IS_CLIENT; + THIS->UpdateLDoNWinLoss(theme_id, false, true); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_RemoveLDoNWin); +XS(XS_Client_RemoveLDoNWin) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::RemoveLDoNWin(THIS, uint32 theme_id)"); + { + Client* THIS; + uint32 theme_id = (uint32) SvUV(ST(1)); + VALIDATE_THIS_IS_CLIENT; + THIS->UpdateLDoNWinLoss(theme_id, true, true); + } + XSRETURN_EMPTY; +} + #ifdef __cplusplus extern "C" #endif @@ -6062,6 +6090,8 @@ XS(boot_Client) { newXSproto(strcpy(buf, "RemoveAllExpeditionLockouts"), XS_Client_RemoveAllExpeditionLockouts, file, "$;$"); newXSproto(strcpy(buf, "RemoveExpeditionLockout"), XS_Client_RemoveExpeditionLockout, file, "$$$"); newXSproto(strcpy(buf, "RemoveItem"), XS_Client_RemoveItem, file, "$$;$"); + newXSproto(strcpy(buf, "RemoveLDoNLoss"), XS_Client_RemoveLDoNLoss, file, "$$"); + newXSproto(strcpy(buf, "RemoveLDoNWin"), XS_Client_RemoveLDoNWin, file, "$$"); newXSproto(strcpy(buf, "RemoveNoRent"), XS_Client_RemoveNoRent, file, "$"); newXSproto(strcpy(buf, "ResetAA"), XS_Client_ResetAA, file, "$"); newXSproto(strcpy(buf, "ResetAllDisciplineTimers"), XS_Client_ResetAllDisciplineTimers, file, "$"); diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index e2fa0617b..89b47aea8 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1761,14 +1761,30 @@ void QuestManager::addldonpoints(uint32 theme_id, int points) { void QuestManager::addldonloss(uint32 theme_id) { QuestManagerCurrentQuestVars(); - if(initiator) - initiator->AddLDoNLoss(theme_id); + if(initiator) { + initiator->UpdateLDoNWinLoss(theme_id); + } } void QuestManager::addldonwin(uint32 theme_id) { QuestManagerCurrentQuestVars(); - if(initiator) - initiator->AddLDoNWin(theme_id); + if(initiator) { + initiator->UpdateLDoNWinLoss(theme_id, true); + } +} + +void QuestManager::removeldonloss(uint32 theme_id) { + QuestManagerCurrentQuestVars(); + if(initiator) { + initiator->UpdateLDoNWinLoss(theme_id, false, true); + } +} + +void QuestManager::removeldonwin(uint32 theme_id) { + QuestManagerCurrentQuestVars(); + if(initiator) { + initiator->UpdateLDoNWinLoss(theme_id, true, true); + } } void QuestManager::setnexthpevent(int at) { diff --git a/zone/questmgr.h b/zone/questmgr.h index 00221c965..4098fd75a 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -162,6 +162,8 @@ public: void addldonpoints(uint32 theme_id, int points); void addldonloss(uint32 theme_id); void addldonwin(uint32 theme_id); + void removeldonloss(uint32 theme_id); + void removeldonwin(uint32 theme_id); void setnexthpevent(int at); void setnextinchpevent(int at); void respawn(int npc_type, int grid); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 36d1b7c22..a77eb4a17 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1953,14 +1953,20 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) auto client = entity_list.GetClientByCharID(update_identifier); if (client) { switch (update_subtype) { - case CZLDoNUpdateSubtype_Loss: - client->AddLDoNLoss(theme_id); + case CZLDoNUpdateSubtype_AddLoss: + client->UpdateLDoNWinLoss(theme_id, false); break; - case CZLDoNUpdateSubtype_Points: + case CZLDoNUpdateSubtype_AddPoints: client->UpdateLDoNPoints(theme_id, points); break; - case CZLDoNUpdateSubtype_Win: - client->AddLDoNWin(theme_id); + case CZLDoNUpdateSubtype_AddWin: + client->UpdateLDoNWinLoss(theme_id, true); + break; + case CZLDoNUpdateSubtype_RemoveLoss: + client->UpdateLDoNWinLoss(theme_id, false, true); + break; + case CZLDoNUpdateSubtype_RemoveWin: + client->UpdateLDoNWinLoss(theme_id, true, true); break; default: break; @@ -1974,14 +1980,20 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) if (client_group->members[member_index] && client_group->members[member_index]->IsClient()) { auto client_group_member = client_group->members[member_index]->CastToClient(); switch (update_subtype) { - case CZLDoNUpdateSubtype_Loss: - client_group_member->AddLDoNLoss(theme_id); + case CZLDoNUpdateSubtype_AddLoss: + client_group_member->UpdateLDoNWinLoss(theme_id, false); break; - case CZLDoNUpdateSubtype_Points: + case CZLDoNUpdateSubtype_AddPoints: client_group_member->UpdateLDoNPoints(theme_id, points); break; - case CZLDoNUpdateSubtype_Win: - client_group_member->AddLDoNWin(theme_id); + case CZLDoNUpdateSubtype_AddWin: + client_group_member->UpdateLDoNWinLoss(theme_id, true); + break; + case CZLDoNUpdateSubtype_RemoveLoss: + client_group_member->UpdateLDoNWinLoss(theme_id, false, true); + break; + case CZLDoNUpdateSubtype_RemoveWin: + client_group_member->UpdateLDoNWinLoss(theme_id, true, true); break; default: break; @@ -1996,14 +2008,20 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) auto client_raid_member = client_raid->members[member_index].member; if (client_raid_member && client_raid_member->IsClient()) { switch (update_subtype) { - case CZLDoNUpdateSubtype_Loss: - client_raid_member->AddLDoNLoss(theme_id); + case CZLDoNUpdateSubtype_AddLoss: + client_raid_member->UpdateLDoNWinLoss(theme_id, false); break; - case CZLDoNUpdateSubtype_Points: + case CZLDoNUpdateSubtype_AddPoints: client_raid_member->UpdateLDoNPoints(theme_id, points); break; - case CZLDoNUpdateSubtype_Win: - client_raid_member->AddLDoNWin(theme_id); + case CZLDoNUpdateSubtype_AddWin: + client_raid_member->UpdateLDoNWinLoss(theme_id, true); + break; + case CZLDoNUpdateSubtype_RemoveLoss: + client_raid_member->UpdateLDoNWinLoss(theme_id, false, true); + break; + case CZLDoNUpdateSubtype_RemoveWin: + client_raid_member->UpdateLDoNWinLoss(theme_id, true, true); break; default: break; @@ -2015,14 +2033,20 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) for (auto &client : entity_list.GetClientList()) { if (client.second->GuildID() > 0 && client.second->GuildID() == update_identifier) { switch (update_subtype) { - case CZLDoNUpdateSubtype_Loss: - client.second->AddLDoNLoss(theme_id); + case CZLDoNUpdateSubtype_AddLoss: + client.second->UpdateLDoNWinLoss(theme_id, false); break; - case CZLDoNUpdateSubtype_Points: + case CZLDoNUpdateSubtype_AddPoints: client.second->UpdateLDoNPoints(theme_id, points); break; - case CZLDoNUpdateSubtype_Win: - client.second->AddLDoNWin(theme_id); + case CZLDoNUpdateSubtype_AddWin: + client.second->UpdateLDoNWinLoss(theme_id, true); + break; + case CZLDoNUpdateSubtype_RemoveLoss: + client.second->UpdateLDoNWinLoss(theme_id, false, true); + break; + case CZLDoNUpdateSubtype_RemoveWin: + client.second->UpdateLDoNWinLoss(theme_id, true, true); break; default: break; @@ -2033,14 +2057,20 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) for (auto &client : entity_list.GetClientList()) { if (client.second->GetExpedition() && client.second->GetExpedition()->GetID() == update_identifier) { switch (update_subtype) { - case CZLDoNUpdateSubtype_Loss: - client.second->AddLDoNLoss(theme_id); + case CZLDoNUpdateSubtype_AddLoss: + client.second->UpdateLDoNWinLoss(theme_id, false); break; - case CZLDoNUpdateSubtype_Points: + case CZLDoNUpdateSubtype_AddPoints: client.second->UpdateLDoNPoints(theme_id, points); break; - case CZLDoNUpdateSubtype_Win: - client.second->AddLDoNWin(theme_id); + case CZLDoNUpdateSubtype_AddWin: + client.second->UpdateLDoNWinLoss(theme_id, true); + break; + case CZLDoNUpdateSubtype_RemoveLoss: + client.second->UpdateLDoNWinLoss(theme_id, false, true); + break; + case CZLDoNUpdateSubtype_RemoveWin: + client.second->UpdateLDoNWinLoss(theme_id, true, true); break; default: break; @@ -2051,14 +2081,20 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) auto client = entity_list.GetClientByName(client_name); if (client) { switch (update_subtype) { - case CZLDoNUpdateSubtype_Loss: - client->AddLDoNLoss(theme_id); + case CZLDoNUpdateSubtype_AddLoss: + client->UpdateLDoNWinLoss(theme_id, false); break; - case CZLDoNUpdateSubtype_Points: + case CZLDoNUpdateSubtype_AddPoints: client->UpdateLDoNPoints(theme_id, points); break; - case CZLDoNUpdateSubtype_Win: - client->AddLDoNWin(theme_id); + case CZLDoNUpdateSubtype_AddWin: + client->UpdateLDoNWinLoss(theme_id, true); + break; + case CZLDoNUpdateSubtype_RemoveLoss: + client->UpdateLDoNWinLoss(theme_id, false, true); + break; + case CZLDoNUpdateSubtype_RemoveWin: + client->UpdateLDoNWinLoss(theme_id, true, true); break; default: break; @@ -2696,19 +2732,29 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) uint8 max_status = WWLU->max_status; for (auto &client : entity_list.GetClientList()) { switch (update_type) { - case WWLDoNUpdateType_Loss: + case WWLDoNUpdateType_AddLoss: if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { - client.second->AddLDoNLoss(theme_id); + client.second->UpdateLDoNWinLoss(theme_id, false); } break; - case WWLDoNUpdateType_Points: + case WWLDoNUpdateType_AddPoints: if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { client.second->UpdateLDoNPoints(theme_id, points); } break; - case WWLDoNUpdateType_Win: + case WWLDoNUpdateType_AddWin: if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { - client.second->AddLDoNWin(theme_id); + client.second->UpdateLDoNWinLoss(theme_id, true); + } + break; + case WWLDoNUpdateType_RemoveLoss: + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + client.second->UpdateLDoNWinLoss(theme_id, false, true); + } + break; + case WWLDoNUpdateType_RemoveWin: + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + client.second->UpdateLDoNWinLoss(theme_id, true, true); } break; } From edf298685e8b59cd7415fe8bb5ff23767a0c5d2f Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Wed, 20 Oct 2021 15:59:28 -0400 Subject: [PATCH 280/624] [Quest API] Convert all char arrays to strings. (#1612) * [Quest API] Convert all char arrays to strings. Also change multiple loops for zone controller to one loop. * Remove 'this' keyword' --- zone/attack.cpp | 26 ++++++++++---------------- zone/client_packet.cpp | 7 +++---- zone/entity.cpp | 8 ++++---- zone/mob.cpp | 12 ++++-------- zone/mob_ai.cpp | 16 ++++++---------- zone/spells.cpp | 30 ++++++++++++------------------ zone/waypoints.cpp | 5 ++--- 7 files changed, 41 insertions(+), 63 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 39c613cc6..9b835afb3 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2211,11 +2211,8 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy Mob *oos = nullptr; if (killer_mob) { oos = killer_mob->GetOwnerOrSelf(); - - char buffer[48] = { 0 }; - snprintf(buffer, 47, "%d %d %d %d", killer_mob->GetID(), damage, spell, static_cast(attack_skill)); - - if (parse->EventNPC(EVENT_DEATH, this, oos, buffer, 0) != 0) { + std::string buffer = fmt::format("{} {} {} {}", killer_mob->GetID(), damage, spell, static_cast(attack_skill)); + if (parse->EventNPC(EVENT_DEATH, this, oos, buffer.c_str(), 0) != 0) { if (GetHP() < 0) { SetHP(0); } @@ -2238,10 +2235,8 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy } } else { - char buffer[48] = { 0 }; - snprintf(buffer, 47, "%d %d %d %d", 0, damage, spell, static_cast(attack_skill)); - - if (parse->EventNPC(EVENT_DEATH, this, nullptr, buffer, 0) != 0) { + std::string buffer = fmt::format("{} {} {} {}", 0, damage, spell, static_cast(attack_skill)); + if (parse->EventNPC(EVENT_DEATH, this, nullptr, buffer.c_str(), 0) != 0) { if (GetHP() < 0) { SetHP(0); } @@ -2632,16 +2627,15 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy entity_list.UpdateFindableNPCState(this, true); - char buffer[48] = { 0 }; - snprintf(buffer, 47, "%d %d %d %d", killer_mob ? killer_mob->GetID() : 0, damage, spell, static_cast(attack_skill)); - parse->EventNPC(EVENT_DEATH_COMPLETE, this, oos, buffer, 0); + std::string buffer = fmt::format("{} {} {} {}", killer_mob ? killer_mob->GetID() : 0, damage, spell, static_cast(attack_skill)); + parse->EventNPC(EVENT_DEATH_COMPLETE, this, oos, buffer.c_str(), 0); /* Zone controller process EVENT_DEATH_ZONE (Death events) */ if (RuleB(Zone, UseZoneController)) { - if (entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID) && this->GetNPCTypeID() != ZONE_CONTROLLER_NPC_ID) { - char data_pass[100] = { 0 }; - snprintf(data_pass, 99, "%d %d %d %d %d", killer_mob ? killer_mob->GetID() : 0, damage, spell, static_cast(attack_skill), this->GetNPCTypeID()); - parse->EventNPC(EVENT_DEATH_ZONE, entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID)->CastToNPC(), nullptr, data_pass, 0); + auto controller = entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID); + if (controller && GetNPCTypeID() != ZONE_CONTROLLER_NPC_ID) { + std::string data_pass = fmt::format("{} {} {} {} {}", killer_mob ? killer_mob->GetID() : 0, damage, spell, static_cast(attack_skill), GetNPCTypeID()); + parse->EventNPC(EVENT_DEATH_ZONE, controller, nullptr, data_pass.c_str(), 0); } } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index f1c47a4d1..8fec728e8 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -11164,14 +11164,13 @@ void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app) break; } - char buf[16]; - sprintf(buf, "%d", popup_response->popupid); + std::string buf = fmt::format("{}", popup_response->popupid); - parse->EventPlayer(EVENT_POPUP_RESPONSE, this, buf, 0); + parse->EventPlayer(EVENT_POPUP_RESPONSE, this, buf.c_str(), 0); Mob *Target = GetTarget(); if (Target && Target->IsNPC()) { - parse->EventNPC(EVENT_POPUP_RESPONSE, Target->CastToNPC(), this, buf, 0); + parse->EventNPC(EVENT_POPUP_RESPONSE, Target->CastToNPC(), this, buf.c_str(), 0); } } diff --git a/zone/entity.cpp b/zone/entity.cpp index c80d13ba8..8a15d10a4 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -723,10 +723,10 @@ void EntityList::AddNPC(NPC *npc, bool SendSpawnPacket, bool dontqueue) /* Zone controller process EVENT_SPAWN_ZONE */ if (RuleB(Zone, UseZoneController)) { - if (entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID) && npc->GetNPCTypeID() != ZONE_CONTROLLER_NPC_ID){ - char data_pass[100] = { 0 }; - snprintf(data_pass, 99, "%d %d", npc->GetID(), npc->GetNPCTypeID()); - parse->EventNPC(EVENT_SPAWN_ZONE, entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID)->CastToNPC(), nullptr, data_pass, 0); + auto controller = entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID); + if (controller && npc->GetNPCTypeID() != ZONE_CONTROLLER_NPC_ID){ + std::string data_pass = fmt::format("{} {}", npc->GetID(), npc->GetNPCTypeID()); + parse->EventNPC(EVENT_SPAWN_ZONE, controller, nullptr, data_pass.c_str(), 0); } } diff --git a/zone/mob.cpp b/zone/mob.cpp index f58b6a92a..c5d8d24c0 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1334,11 +1334,9 @@ void Mob::CreateHPPacket(EQApplicationPacket* app) { if (ds->hp < GetNextHPEvent()) { - char buf[10]; - snprintf(buf, 9, "%i", GetNextHPEvent()); - buf[9] = '\0'; + std::string buf = fmt::format("{}", GetNextHPEvent()); SetNextHPEvent(-1); - parse->EventNPC(EVENT_HP, CastToNPC(), nullptr, buf, 0); + parse->EventNPC(EVENT_HP, CastToNPC(), nullptr, buf.c_str(), 0); } } @@ -1346,11 +1344,9 @@ void Mob::CreateHPPacket(EQApplicationPacket* app) { if (ds->hp > GetNextIncHPEvent()) { - char buf[10]; - snprintf(buf, 9, "%i", GetNextIncHPEvent()); - buf[9] = '\0'; + std::string buf = fmt::format("{}", GetNextIncHPEvent()); SetNextIncHPEvent(-1); - parse->EventNPC(EVENT_HP, CastToNPC(), nullptr, buf, 1); + parse->EventNPC(EVENT_HP, CastToNPC(), nullptr, buf.c_str(), 1); } } } diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 962d63f3d..af74f3510 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1766,9 +1766,8 @@ void NPC::AI_DoMovement() { } //kick off event_waypoint arrive - char temp[16]; - sprintf(temp, "%d", cur_wp); - parse->EventNPC(EVENT_WAYPOINT_ARRIVE, CastToNPC(), nullptr, temp, 0); + std::string buf = fmt::format("{}", cur_wp); + parse->EventNPC(EVENT_WAYPOINT_ARRIVE, CastToNPC(), nullptr, buf.c_str(), 0); // No need to move as we are there. Next loop will // take care of normal grids, even at pause 0. // We do need to call and setup a wp if we're cur_wp=-2 @@ -1885,9 +1884,8 @@ void NPC::AI_SetupNextWaypoint() { if (!DistractedFromGrid) { //kick off event_waypoint depart - char temp[16]; - sprintf(temp, "%d", cur_wp); - parse->EventNPC(EVENT_WAYPOINT_DEPART, CastToNPC(), nullptr, temp, 0); + std::string buf = fmt::format("{}", cur_wp); + parse->EventNPC(EVENT_WAYPOINT_DEPART, CastToNPC(), nullptr, buf.c_str(), 0); //setup our next waypoint, if we are still on our normal grid //remember that the quest event above could have done anything it wanted with our grid @@ -2470,10 +2468,8 @@ void NPC::CheckSignal() { if (!signal_q.empty()) { int signal_id = signal_q.front(); signal_q.pop_front(); - char buf[32]; - snprintf(buf, 31, "%d", signal_id); - buf[31] = '\0'; - parse->EventNPC(EVENT_SIGNAL, this, nullptr, buf, 0); + std::string buf = fmt::format("{}", signal_id); + parse->EventNPC(EVENT_SIGNAL, this, nullptr, buf.c_str(), 0); } } diff --git a/zone/spells.cpp b/zone/spells.cpp index 36202f12c..dd7442911 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -289,14 +289,12 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, } if(IsClient()) { - char temp[64]; - sprintf(temp, "%d", spell_id); - if (parse->EventPlayer(EVENT_CAST_BEGIN, CastToClient(), temp, 0) != 0) + std::string buf = fmt::format("{}", spell_id); + if (parse->EventPlayer(EVENT_CAST_BEGIN, CastToClient(), buf.c_str(), 0) != 0) return false; } else if(IsNPC()) { - char temp[64]; - sprintf(temp, "%d", spell_id); - parse->EventNPC(EVENT_CAST_BEGIN, CastToNPC(), nullptr, temp, 0); + std::string buf = fmt::format("{}", spell_id); + parse->EventNPC(EVENT_CAST_BEGIN, CastToNPC(), nullptr, buf.c_str(), 0); } //To prevent NPC ghosting when spells are cast from scripts @@ -1440,13 +1438,11 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo // if(IsClient()) { - char temp[64]; - sprintf(temp, "%d", spell_id); - parse->EventPlayer(EVENT_CAST, CastToClient(), temp, 0); + std::string buf = fmt::format("{}", spell_id); + parse->EventPlayer(EVENT_CAST, CastToClient(), buf.c_str(), 0); } else if(IsNPC()) { - char temp[64]; - sprintf(temp, "%d", spell_id); - parse->EventNPC(EVENT_CAST, CastToNPC(), nullptr, temp, 0); + std::string buf = fmt::format("{}", spell_id); + parse->EventNPC(EVENT_CAST, CastToNPC(), nullptr, buf.c_str(), 0); } if(bard_song_mode) @@ -3654,15 +3650,13 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes /* Send the EVENT_CAST_ON event */ if(spelltar->IsNPC()) { - char temp1[100]; - sprintf(temp1, "%d", spell_id); - parse->EventNPC(EVENT_CAST_ON, spelltar->CastToNPC(), this, temp1, 0); + std::string buf = fmt::format("{}", spell_id); + parse->EventNPC(EVENT_CAST_ON, spelltar->CastToNPC(), this, buf.c_str(), 0); } else if (spelltar->IsClient()) { - char temp1[100]; - sprintf(temp1, "%d", spell_id); - parse->EventPlayer(EVENT_CAST_ON, spelltar->CastToClient(),temp1, 0); + std::string buf = fmt::format("{}", spell_id); + parse->EventPlayer(EVENT_CAST_ON, spelltar->CastToClient(), buf.c_str(), 0); } mod_spell_cast(spell_id, spelltar, reflect_effectiveness, use_resist_adjust, resist_adjust, isproc); diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index 97e9d6091..72bdf14d5 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -138,11 +138,10 @@ void NPC::ResumeWandering() if (m_CurrentWayPoint.x == GetX() && m_CurrentWayPoint.y == GetY()) { // are we we at a waypoint? if so, trigger event and start to next - char temp[100]; - itoa(cur_wp, temp, 10); //do this before updating to next waypoint + std::string buf = fmt::format("{}", cur_wp); CalculateNewWaypoint(); SetAppearance(eaStanding, false); - parse->EventNPC(EVENT_WAYPOINT_DEPART, this, nullptr, temp, 0); + parse->EventNPC(EVENT_WAYPOINT_DEPART, this, nullptr, buf.c_str(), 0); } // if not currently at a waypoint, we continue on to the one we were headed to before the stop } else From 81e7cf5a32d901100c0e85e28a289afacff6bc2d Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Wed, 20 Oct 2021 16:02:12 -0400 Subject: [PATCH 281/624] [Quest API] Convert Spell Events to similar formats and exports. (#1618) * [Quest API] Convert Spell Events to similar formats and exports. Export spell ID, caster ID, caster level, tics remaining, and buff slot to Perl/Lua spell events. - Export e.buff_slot, e.caster_id, e.caster_level, e.spell_id, and e.tics_remaining to `event_spell_buff_tic`, `event_spell_effect`, and `event_spell_fade` in Lua. - Export $buff_slot, $caster_id, $caster_level, $spell_id, $tics_remaining to `EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT`, `EVENT_SPELL_EFFECT_BUFF_TIC_NPC`, `EVENT_SPELL_EFFECT_CLIENT`, `EVENT_SPELL_EFFECT_NPC`, and `EVENT_SPELL_FADE` in Perl. * Formatting. * Remove debug variable. --- common/spdat.cpp | 8 ++++ common/spdat.h | 4 ++ zone/client_packet.cpp | 45 ++++++++++++------- zone/embparser.cpp | 60 +++++++++++++------------ zone/embparser.h | 2 +- zone/event_codes.h | 4 +- zone/lua_general.cpp | 2 +- zone/lua_parser.cpp | 26 +++++------ zone/lua_parser.h | 6 +-- zone/lua_parser_events.cpp | 76 +++++++------------------------ zone/lua_parser_events.h | 12 ++--- zone/quest_interface.h | 4 +- zone/quest_parser_collection.cpp | 18 ++++---- zone/quest_parser_collection.h | 4 +- zone/spell_effects.cpp | 77 ++++++++++++++++---------------- 15 files changed, 164 insertions(+), 184 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 8721078d7..7c3476505 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -833,6 +833,14 @@ bool IsTeleportSpell(uint16 spell_id) return false; } +bool IsTranslocateSpell(uint16 spell_id) +{ + if (IsEffectInSpell(spell_id, SE_Translocate)) + return true; + + return false; +} + bool IsGateSpell(uint16 spell_id) { if (IsEffectInSpell(spell_id, SE_Gate)) diff --git a/common/spdat.h b/common/spdat.h index ebd39eed4..c6c9df888 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -129,6 +129,8 @@ #define SPELL_SPIRITUAL_ECHO 1248 #define SPELL_BRISTLING_ARMAMENT 1249 #define SPELL_WATON_DESTRUCTION 1250 +#define SPELL_TRANSLOCATE_GROUP 1334 +#define SPELL_TRANSLOCATE 1422 #define SPELL_ACTING_MAGIC_RESIST_I 1900 #define SPELL_ACTING_FIRE_RESIST_I 1901 #define SPELL_ACTING_COLD_RESIST_I 1902 @@ -154,6 +156,7 @@ #define SPELL_ACTING_SPIRIT_II 1922 #define SPELL_RESURRECTION_SICKNESS 756 #define SPELL_RESURRECTION_SICKNESS4 757 +#define SPELL_TELEPORT 3243 #define SPELL_RESURRECTION_SICKNESS2 5249 #define SPELL_REVIVAL_SICKNESS 13087 #define SPELL_RESURRECTION_SICKNESS3 37624 @@ -1472,6 +1475,7 @@ bool IsPartialDeathSaveSpell(uint16 spell_id); bool IsShadowStepSpell(uint16 spell_id); bool IsSuccorSpell(uint16 spell_id); bool IsTeleportSpell(uint16 spell_id); +bool IsTranslocateSpell(uint16 spell_id); bool IsGateSpell(uint16 spell_id); bool IsPlayerIllusionSpell(uint16 spell_id); // seveian 2008-09-23 bool IsLDoNObjectSpell(uint16 spell_id); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 8fec728e8..c925d90db 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -14761,38 +14761,44 @@ void Client::Handle_OP_TradeSkillCombine(const EQApplicationPacket *app) void Client::Handle_OP_Translocate(const EQApplicationPacket *app) { - if (app->size != sizeof(Translocate_Struct)) { LogDebug("Size mismatch in OP_Translocate expected [{}] got [{}]", sizeof(Translocate_Struct), app->size); DumpPacket(app); return; } + Translocate_Struct *its = (Translocate_Struct*)app->pBuffer; - if (!PendingTranslocate) + if (!PendingTranslocate) { return; + } - if ((RuleI(Spells, TranslocateTimeLimit) > 0) && (time(nullptr) > (TranslocateTime + RuleI(Spells, TranslocateTimeLimit)))) { + auto translocate_time_limit = RuleI(Spells, TranslocateTimeLimit); + if ( + translocate_time_limit && + time(nullptr) > (TranslocateTime + translocate_time_limit) + ) { Message(Chat::Red, "You did not accept the Translocate within the required time limit."); PendingTranslocate = false; return; } if (its->Complete == 1) { - - int SpellID = PendingTranslocateData.spell_id; - int i = parse->EventSpell(EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE, nullptr, this, SpellID, 0); - - if (i == 0) - { + uint32 spell_id = PendingTranslocateData.spell_id; + bool in_translocate_zone = ( + zone->GetZoneID() == PendingTranslocateData.zone_id && + zone->GetInstanceID() == PendingTranslocateData.instance_id + ); + + if (parse->EventSpell(EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE, nullptr, this, spell_id, "", 0) == 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.zone_id && - zone->GetInstanceID() == PendingTranslocateData.instance_id)) - { + if ( + IsTranslocateSpell(spell_id) && + in_translocate_zone + ) { PendingTranslocate = false; GoToBind(); return; @@ -14800,9 +14806,16 @@ void Client::Handle_OP_Translocate(const EQApplicationPacket *app) ////Was sending the packet back to initiate client zone... ////but that could be abusable, so lets go through proper channels - MovePC(PendingTranslocateData.zone_id, PendingTranslocateData.instance_id, - PendingTranslocateData.x, PendingTranslocateData.y, - PendingTranslocateData.z, PendingTranslocateData.heading, 0, ZoneSolicited); + MovePC( + PendingTranslocateData.zone_id, + PendingTranslocateData.instance_id, + PendingTranslocateData.x, + PendingTranslocateData.y, + PendingTranslocateData.z, + PendingTranslocateData.heading, + 0, + ZoneSolicited + ); } } diff --git a/zone/embparser.cpp b/zone/embparser.cpp index 43bcebe5c..635a48b4c 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -253,19 +253,15 @@ int PerlembParser::EventCommon( if (isPlayerQuest || isGlobalPlayerQuest) { return SendCommands(package_name.c_str(), sub_name, 0, mob, mob, nullptr); - } - else if (isItemQuest) { + } else if (isItemQuest) { return SendCommands(package_name.c_str(), sub_name, 0, mob, mob, item_inst); - } - else if (isSpellQuest) { + } else if (isSpellQuest) { if (mob) { return SendCommands(package_name.c_str(), sub_name, 0, mob, mob, nullptr); - } - else { + } else { return SendCommands(package_name.c_str(), sub_name, 0, npcmob, mob, nullptr); } - } - else { + } else { return SendCommands(package_name.c_str(), sub_name, objid, npcmob, mob, nullptr); } } @@ -312,11 +308,11 @@ int PerlembParser::EventItem( } int PerlembParser::EventSpell( - QuestEventID evt, NPC *npc, Client *client, uint32 spell_id, uint32 extra_data, + QuestEventID evt, NPC *npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, std::vector *extra_pointers ) { - return EventCommon(evt, 0, itoa(spell_id), npc, nullptr, client, extra_data, false, extra_pointers); + return EventCommon(evt, spell_id, data.c_str(), npc, nullptr, client, extra_data, false, extra_pointers); } bool PerlembParser::HasQuestSub(uint32 npcid, QuestEventID evt) @@ -1004,8 +1000,8 @@ void PerlembParser::GetQuestTypes( { if (event == EVENT_SPELL_EFFECT_CLIENT || event == EVENT_SPELL_EFFECT_NPC || - event == EVENT_SPELL_BUFF_TIC_CLIENT || - event == EVENT_SPELL_BUFF_TIC_NPC || + event == EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT || + event == EVENT_SPELL_EFFECT_BUFF_TIC_NPC || event == EVENT_SPELL_FADE || event == EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE) { isSpellQuest = true; @@ -1042,31 +1038,31 @@ void PerlembParser::GetQuestPackageName( bool global ) { - if (!isPlayerQuest && !isGlobalPlayerQuest && !isItemQuest && !isSpellQuest) { + if ( + !isPlayerQuest && + !isGlobalPlayerQuest && + !isItemQuest && + !isSpellQuest + ) { if (global) { isGlobalNPC = true; package_name = "qst_global_npc"; - } - else { + } else { package_name = "qst_npc_"; - package_name += itoa(npcmob->GetNPCTypeID()); + package_name += std::to_string(npcmob->GetNPCTypeID()); } - } - else if (isItemQuest) { + } else if (isItemQuest) { // need a valid EQ::ItemInstance pointer check here..unsure how to cancel this process const EQ::ItemData *item = item_inst->GetItem(); package_name = "qst_item_"; - package_name += itoa(item->ID); - } - else if (isPlayerQuest) { + package_name += std::to_string(item->ID); + } else if (isPlayerQuest) { package_name = "qst_player"; - } - else if (isGlobalPlayerQuest) { + } else if (isGlobalPlayerQuest) { package_name = "qst_global_player"; - } - else { + } else { package_name = "qst_spell_"; - package_name += data; + package_name += std::to_string(objid); } } @@ -1525,11 +1521,17 @@ void PerlembParser::ExportEventVariables( break; } + case EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT: + case EVENT_SPELL_EFFECT_BUFF_TIC_NPC: case EVENT_SPELL_EFFECT_CLIENT: case EVENT_SPELL_EFFECT_NPC: - case EVENT_SPELL_BUFF_TIC_CLIENT: - case EVENT_SPELL_BUFF_TIC_NPC: { - ExportVar(package_name.c_str(), "caster_id", extradata); + case EVENT_SPELL_FADE: { + Seperator sep(data); + ExportVar(package_name.c_str(), "spell_id", objid); + ExportVar(package_name.c_str(), "caster_id", sep.arg[0]); + ExportVar(package_name.c_str(), "tics_remaining", sep.arg[1]); + ExportVar(package_name.c_str(), "caster_level", sep.arg[2]); + ExportVar(package_name.c_str(), "buff_slot", sep.arg[3]); break; } diff --git a/zone/embparser.h b/zone/embparser.h index ed7989a99..76d6d9717 100644 --- a/zone/embparser.h +++ b/zone/embparser.h @@ -58,7 +58,7 @@ public: std::vector *extra_pointers); virtual int EventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data, std::vector *extra_pointers); - virtual int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, + virtual int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, std::vector *extra_pointers); virtual bool HasQuestSub(uint32 npcid, QuestEventID evt); diff --git a/zone/event_codes.h b/zone/event_codes.h index 0fd9d94b3..8aebee1a7 100644 --- a/zone/event_codes.h +++ b/zone/event_codes.h @@ -43,8 +43,8 @@ typedef enum { EVENT_HATE_LIST, EVENT_SPELL_EFFECT_CLIENT, EVENT_SPELL_EFFECT_NPC, - EVENT_SPELL_BUFF_TIC_CLIENT, - EVENT_SPELL_BUFF_TIC_NPC, + EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT, + EVENT_SPELL_EFFECT_BUFF_TIC_NPC, EVENT_SPELL_FADE, EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE, EVENT_COMBINE_SUCCESS, //PC successfully combined a recipe diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 283ed5db8..57a426516 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -4126,7 +4126,7 @@ luabind::scope lua_register_events() { luabind::value("target_change", static_cast(EVENT_TARGET_CHANGE)), luabind::value("hate_list", static_cast(EVENT_HATE_LIST)), luabind::value("spell_effect", static_cast(EVENT_SPELL_EFFECT_CLIENT)), - luabind::value("spell_buff_tic", static_cast(EVENT_SPELL_BUFF_TIC_CLIENT)), + luabind::value("spell_buff_tic", static_cast(EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT)), luabind::value("spell_fade", static_cast(EVENT_SPELL_FADE)), luabind::value("spell_effect_translocate_complete", static_cast(EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE)), luabind::value("combine_success ", static_cast(EVENT_COMBINE_SUCCESS )), diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index 093f11c62..a1aee9a41 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -241,9 +241,9 @@ LuaParser::LuaParser() { ItemArgumentDispatch[EVENT_AUGMENT_INSERT] = handle_item_augment_insert; ItemArgumentDispatch[EVENT_AUGMENT_REMOVE] = handle_item_augment_remove; - SpellArgumentDispatch[EVENT_SPELL_EFFECT_CLIENT] = handle_spell_effect; - SpellArgumentDispatch[EVENT_SPELL_BUFF_TIC_CLIENT] = handle_spell_tic; - SpellArgumentDispatch[EVENT_SPELL_FADE] = handle_spell_fade; + SpellArgumentDispatch[EVENT_SPELL_EFFECT_CLIENT] = handle_spell_event; + SpellArgumentDispatch[EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT] = handle_spell_event; + SpellArgumentDispatch[EVENT_SPELL_FADE] = handle_spell_event; SpellArgumentDispatch[EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE] = handle_translocate_finish; EncounterArgumentDispatch[EVENT_TIMER] = handle_encounter_timer; @@ -535,7 +535,7 @@ int LuaParser::_EventItem(std::string package_name, QuestEventID evt, Client *cl return 0; } -int LuaParser::EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, +int LuaParser::EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, std::vector *extra_pointers) { evt = ConvertLuaEvent(evt); if(evt >= _LargestEventID) { @@ -548,10 +548,10 @@ int LuaParser::EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spe return 0; } - return _EventSpell(package_name, evt, npc, client, spell_id, extra_data, extra_pointers); + return _EventSpell(package_name, evt, npc, client, spell_id, data, extra_data, extra_pointers); } -int LuaParser::_EventSpell(std::string package_name, QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, +int LuaParser::_EventSpell(std::string package_name, QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, std::vector *extra_pointers, luabind::adl::object *l_func) { const char *sub_name = LuaEvents[evt]; @@ -582,7 +582,7 @@ int LuaParser::_EventSpell(std::string package_name, QuestEventID evt, NPC* npc, lua_setfield(L, -2, "self"); auto arg_function = SpellArgumentDispatch[evt]; - arg_function(this, L, npc, client, spell_id, extra_data, extra_pointers); + arg_function(this, L, npc, client, spell_id, data, extra_data, extra_pointers); quest_manager.StartQuest(npc, client, nullptr); if(lua_pcall(L, 1, 1, 0)) { @@ -1276,7 +1276,7 @@ int LuaParser::DispatchEventItem(QuestEventID evt, Client *client, EQ::ItemInsta return ret; } -int LuaParser::DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, +int LuaParser::DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, std::vector *extra_pointers) { evt = ConvertLuaEvent(evt); if(evt >= _LargestEventID) { @@ -1292,7 +1292,7 @@ int LuaParser::DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, ui while(riter != iter->second.end()) { if(riter->event_id == evt) { std::string package_name = "encounter_" + riter->encounter_name; - int i = _EventSpell(package_name, evt, npc, client, spell_id, extra_data, extra_pointers, &riter->lua_reference); + int i = _EventSpell(package_name, evt, npc, client, spell_id, data, extra_data, extra_pointers, &riter->lua_reference); if(i != 0) { ret = i; } @@ -1310,7 +1310,7 @@ int LuaParser::DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, ui while(riter != iter->second.end()) { if(riter->event_id == evt) { std::string package_name = "encounter_" + riter->encounter_name; - int i = _EventSpell(package_name, evt, npc, client, spell_id, extra_data, extra_pointers, &riter->lua_reference); + int i = _EventSpell(package_name, evt, npc, client, spell_id, data, extra_data, extra_pointers, &riter->lua_reference); if(i != 0) ret = i; } @@ -1329,9 +1329,9 @@ QuestEventID LuaParser::ConvertLuaEvent(QuestEventID evt) { case EVENT_SPELL_EFFECT_NPC: return EVENT_SPELL_EFFECT_CLIENT; break; - case EVENT_SPELL_BUFF_TIC_CLIENT: - case EVENT_SPELL_BUFF_TIC_NPC: - return EVENT_SPELL_BUFF_TIC_CLIENT; + case EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT: + case EVENT_SPELL_EFFECT_BUFF_TIC_NPC: + return EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT; break; case EVENT_AGGRO: case EVENT_ATTACK: diff --git a/zone/lua_parser.h b/zone/lua_parser.h index d058daf13..831f22061 100644 --- a/zone/lua_parser.h +++ b/zone/lua_parser.h @@ -46,7 +46,7 @@ public: std::vector *extra_pointers); virtual int EventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data, std::vector *extra_pointers); - virtual int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, + virtual int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, std::vector *extra_pointers); virtual int EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, std::vector *extra_pointers); @@ -80,7 +80,7 @@ public: std::vector *extra_pointers); virtual int DispatchEventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data, std::vector *extra_pointers); - virtual int DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, + virtual int DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, std::vector *extra_pointers); static LuaParser* Instance() { @@ -112,7 +112,7 @@ private: std::vector *extra_pointers, luabind::adl::object *l_func = nullptr); int _EventItem(std::string package_name, QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data, std::vector *extra_pointers, luabind::adl::object *l_func = nullptr); - int _EventSpell(std::string package_name, QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, + int _EventSpell(std::string package_name, QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, std::vector *extra_pointers, luabind::adl::object *l_func = nullptr); int _EventEncounter(std::string package_name, QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, std::vector *extra_pointers); diff --git a/zone/lua_parser_events.cpp b/zone/lua_parser_events.cpp index e7711b36c..9adc0981e 100644 --- a/zone/lua_parser_events.cpp +++ b/zone/lua_parser_events.cpp @@ -702,8 +702,7 @@ void handle_item_null(QuestInterface *parse, lua_State* L, Client* client, EQ::I } //Spell -void handle_spell_effect(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers) { +void handle_spell_event(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, std::string data, uint32 extra_data, std::vector *extra_pointers) { if(npc) { Lua_Mob l_npc(npc); luabind::adl::object l_npc_o = luabind::adl::object(L, l_npc); @@ -720,71 +719,30 @@ void handle_spell_effect(QuestInterface *parse, lua_State* L, NPC* npc, Client* lua_setfield(L, -2, "target"); - lua_pushinteger(L, *EQ::any_cast(extra_pointers->at(0))); - lua_setfield(L, -2, "buff_slot"); + lua_pushinteger(L, spell_id); + lua_setfield(L, -2, "spell_id"); - lua_pushinteger(L, extra_data); + Seperator sep(data.c_str()); + + lua_pushinteger(L, std::stoi(sep.arg[0])); lua_setfield(L, -2, "caster_id"); -} -void handle_spell_tic(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers) { - if(npc) { - Lua_Mob l_npc(npc); - luabind::adl::object l_npc_o = luabind::adl::object(L, l_npc); - l_npc_o.push(L); - } else if(client) { - Lua_Mob l_client(client); - luabind::adl::object l_client_o = luabind::adl::object(L, l_client); - l_client_o.push(L); - } else { - Lua_Mob l_mob(nullptr); - luabind::adl::object l_mob_o = luabind::adl::object(L, l_mob); - l_mob_o.push(L); - } - - lua_setfield(L, -2, "target"); - - lua_pushinteger(L, *EQ::any_cast(extra_pointers->at(0))); + lua_pushinteger(L, std::stoi(sep.arg[1])); lua_setfield(L, -2, "tics_remaining"); - lua_pushinteger(L, *EQ::any_cast(extra_pointers->at(1))); + lua_pushinteger(L, std::stoi(sep.arg[2])); lua_setfield(L, -2, "caster_level"); - lua_pushinteger(L, *EQ::any_cast(extra_pointers->at(2))); + lua_pushinteger(L, std::stoi(sep.arg[3])); lua_setfield(L, -2, "buff_slot"); - - lua_pushinteger(L, extra_data); - lua_setfield(L, -2, "caster_id"); + + Lua_Spell l_spell(spell_id); + luabind::adl::object l_spell_o = luabind::adl::object(L, l_spell); + l_spell_o.push(L); + lua_setfield(L, -2, "spell"); } -void handle_spell_fade(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers) { - if(npc) { - Lua_Mob l_npc(npc); - luabind::adl::object l_npc_o = luabind::adl::object(L, l_npc); - l_npc_o.push(L); - } else if(client) { - Lua_Mob l_client(client); - luabind::adl::object l_client_o = luabind::adl::object(L, l_client); - l_client_o.push(L); - } else { - Lua_Mob l_mob(nullptr); - luabind::adl::object l_mob_o = luabind::adl::object(L, l_mob); - l_mob_o.push(L); - } - - lua_setfield(L, -2, "target"); - - lua_pushinteger(L, extra_data); - lua_setfield(L, -2, "buff_slot"); - - lua_pushinteger(L, *EQ::any_cast(extra_pointers->at(0))); - lua_setfield(L, -2, "caster_id"); -} - -void handle_translocate_finish(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers) { +void handle_translocate_finish(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, std::string data, uint32 extra_data, std::vector *extra_pointers) { if(npc) { Lua_Mob l_npc(npc); luabind::adl::object l_npc_o = luabind::adl::object(L, l_npc); @@ -802,9 +760,7 @@ void handle_translocate_finish(QuestInterface *parse, lua_State* L, NPC* npc, Cl lua_setfield(L, -2, "target"); } -void handle_spell_null(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers) { -} +void handle_spell_null(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, std::string data, uint32 extra_data, std::vector *extra_pointers) { } void handle_encounter_timer(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data, std::vector *extra_pointers) { diff --git a/zone/lua_parser_events.h b/zone/lua_parser_events.h index 1d9d4b22c..5eced1243 100644 --- a/zone/lua_parser_events.h +++ b/zone/lua_parser_events.h @@ -5,7 +5,7 @@ typedef void(*NPCArgumentHandler)(QuestInterface*, lua_State*, NPC*, Mob*, std::string, uint32, std::vector*); typedef void(*PlayerArgumentHandler)(QuestInterface*, lua_State*, Client*, std::string, uint32, std::vector*); typedef void(*ItemArgumentHandler)(QuestInterface*, lua_State*, Client*, EQ::ItemInstance*, Mob*, std::string, uint32, std::vector*); -typedef void(*SpellArgumentHandler)(QuestInterface*, lua_State*, NPC*, Client*, uint32, uint32, std::vector*); +typedef void(*SpellArgumentHandler)(QuestInterface*, lua_State*, NPC*, Client*, uint32, std::string, uint32, std::vector*); typedef void(*EncounterArgumentHandler)(QuestInterface*, lua_State*, Encounter* encounter, std::string, uint32, std::vector*); //NPC @@ -135,15 +135,11 @@ void handle_item_null(QuestInterface *parse, lua_State* L, Client* client, EQ::I std::vector *extra_pointers); //Spell -void handle_spell_effect(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, uint32 extra_data, +void handle_spell_event(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, std::string data, uint32 extra_data, std::vector *extra_pointers); -void handle_spell_tic(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, uint32 extra_data, +void handle_translocate_finish(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, std::string data, uint32 extra_data, std::vector *extra_pointers); -void handle_spell_fade(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers); -void handle_translocate_finish(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers); -void handle_spell_null(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, uint32 extra_data, +void handle_spell_null(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, std::string data, uint32 extra_data, std::vector *extra_pointers); diff --git a/zone/quest_interface.h b/zone/quest_interface.h index d0f121cb6..794251e36 100644 --- a/zone/quest_interface.h +++ b/zone/quest_interface.h @@ -43,7 +43,7 @@ public: std::vector *extra_pointers) { return 0; } virtual int EventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data, std::vector *extra_pointers) { return 0; } - virtual int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, + virtual int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, std::vector *extra_pointers) { return 0; } virtual int EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, std::vector *extra_pointers) { return 0; } @@ -70,7 +70,7 @@ public: std::vector *extra_pointers) { return 0; } virtual int DispatchEventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data, std::vector *extra_pointers) { return 0; } - virtual int DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, + virtual int DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, std::vector *extra_pointers) { return 0; } virtual void AddVar(std::string name, std::string val) { } diff --git a/zone/quest_parser_collection.cpp b/zone/quest_parser_collection.cpp index 625425839..8c5185ad6 100644 --- a/zone/quest_parser_collection.cpp +++ b/zone/quest_parser_collection.cpp @@ -410,21 +410,21 @@ int QuestParserCollection::EventItem(QuestEventID evt, Client *client, EQ::ItemI return 0; } -int QuestParserCollection::EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, +int QuestParserCollection::EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, std::vector *extra_pointers) { auto iter = _spell_quest_status.find(spell_id); if(iter != _spell_quest_status.end()) { //loaded or failed to load if(iter->second != QuestFailedToLoad) { auto qiter = _interfaces.find(iter->second); - int ret = DispatchEventSpell(evt, npc, client, spell_id, extra_data, extra_pointers); - int i = qiter->second->EventSpell(evt, npc, client, spell_id, extra_data, extra_pointers); + int ret = DispatchEventSpell(evt, npc, client, spell_id, data, extra_data, extra_pointers); + int i = qiter->second->EventSpell(evt, npc, client, spell_id, data, extra_data, extra_pointers); if(i != 0) { ret = i; } return ret; } - return DispatchEventSpell(evt, npc, client, spell_id, extra_data, extra_pointers); + return DispatchEventSpell(evt, npc, client, spell_id, data, extra_data, extra_pointers); } else if (_spell_quest_status[spell_id] != QuestFailedToLoad) { std::string filename; @@ -432,8 +432,8 @@ int QuestParserCollection::EventSpell(QuestEventID evt, NPC* npc, Client *client if (qi) { _spell_quest_status[spell_id] = qi->GetIdentifier(); qi->LoadSpellScript(filename, spell_id); - int ret = DispatchEventSpell(evt, npc, client, spell_id, extra_data, extra_pointers); - int i = qi->EventSpell(evt, npc, client, spell_id, extra_data, extra_pointers); + int ret = DispatchEventSpell(evt, npc, client, spell_id, data, extra_data, extra_pointers); + int i = qi->EventSpell(evt, npc, client, spell_id, data, extra_data, extra_pointers); if (i != 0) { ret = i; } @@ -441,7 +441,7 @@ int QuestParserCollection::EventSpell(QuestEventID evt, NPC* npc, Client *client } else { _spell_quest_status[spell_id] = QuestFailedToLoad; - return DispatchEventSpell(evt, npc, client, spell_id, extra_data, extra_pointers); + return DispatchEventSpell(evt, npc, client, spell_id, data, extra_data, extra_pointers); } } return 0; @@ -1042,12 +1042,12 @@ int QuestParserCollection::DispatchEventItem(QuestEventID evt, Client *client, E return ret; } -int QuestParserCollection::DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, +int QuestParserCollection::DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, std::vector *extra_pointers) { int ret = 0; auto iter = _load_precedence.begin(); while(iter != _load_precedence.end()) { - int i = (*iter)->DispatchEventSpell(evt, npc, client, spell_id, extra_data, extra_pointers); + int i = (*iter)->DispatchEventSpell(evt, npc, client, spell_id, data, extra_data, extra_pointers); if(i != 0) { ret = i; } diff --git a/zone/quest_parser_collection.h b/zone/quest_parser_collection.h index 082b5b6f4..c8ebc2427 100644 --- a/zone/quest_parser_collection.h +++ b/zone/quest_parser_collection.h @@ -78,7 +78,7 @@ public: std::vector *extra_pointers = nullptr); int EventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data, std::vector *extra_pointers = nullptr); - int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, + int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, std::vector *extra_pointers = nullptr); int EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, std::vector *extra_pointers = nullptr); @@ -131,7 +131,7 @@ private: std::vector *extra_pointers); int DispatchEventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data, std::vector *extra_pointers); - int DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, + int DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data, std::vector *extra_pointers); std::map _interfaces; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index db2e78ecc..926cfff6d 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -161,22 +161,21 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove } } - if(IsNPC()) - { - std::vector args; - args.push_back(&buffslot); - int i = parse->EventSpell(EVENT_SPELL_EFFECT_NPC, CastToNPC(), nullptr, spell_id, caster ? caster->GetID() : 0, &args); - if(i != 0){ + std::string buf = fmt::format( + "{} {} {} {}", + caster->GetID(), + buffs[buffslot].ticsremaining, + caster->GetLevel(), + buffslot + ); + + if (IsClient()) { + if (parse->EventSpell(EVENT_SPELL_EFFECT_CLIENT, nullptr, CastToClient(), spell_id, buf, 0) != 0) { CalcBonuses(); return true; } - } - else if(IsClient()) - { - std::vector args; - args.push_back(&buffslot); - int i = parse->EventSpell(EVENT_SPELL_EFFECT_CLIENT, nullptr, CastToClient(), spell_id, caster ? caster->GetID() : 0, &args); - if(i != 0){ + } else if (IsNPC()) { + if (parse->EventSpell(EVENT_SPELL_EFFECT_NPC, CastToNPC(), nullptr, spell_id, buf, 0) != 0) { CalcBonuses(); return true; } @@ -3750,24 +3749,20 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) const SPDat_Spell_Struct &spell = spells[buff.spellid]; - if (IsNPC()) { - std::vector args; - args.push_back(&buff.ticsremaining); - args.push_back(&buff.casterlevel); - args.push_back(&slot); - int i = parse->EventSpell(EVENT_SPELL_BUFF_TIC_NPC, CastToNPC(), nullptr, buff.spellid, - caster ? caster->GetID() : 0, &args); - if (i != 0) { + std::string buf = fmt::format( + "{} {} {} {}", + caster->GetID(), + buffs[slot].ticsremaining, + caster->GetLevel(), + slot + ); + + if (IsClient()) { + if (parse->EventSpell(EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT, nullptr, CastToClient(), buff.spellid, buf, 0) != 0) { return; } - } else { - std::vector args; - args.push_back(&buff.ticsremaining); - args.push_back(&buff.casterlevel); - args.push_back(&slot); - int i = parse->EventSpell(EVENT_SPELL_BUFF_TIC_CLIENT, nullptr, CastToClient(), buff.spellid, - caster ? caster->GetID() : 0, &args); - if (i != 0) { + } else if (IsNPC()) { + if (parse->EventSpell(EVENT_SPELL_EFFECT_BUFF_TIC_NPC, CastToNPC(), nullptr, buff.spellid, buf, 0) != 0) { return; } } @@ -4115,16 +4110,22 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) } } - if(IsClient()) { - std::vector args; - args.push_back(&buffs[slot].casterid); + std::string buf = fmt::format( + "{} {} {} {}", + buffs[slot].casterid, + buffs[slot].ticsremaining, + buffs[slot].casterlevel, + slot + ); - parse->EventSpell(EVENT_SPELL_FADE, nullptr, CastToClient(), buffs[slot].spellid, slot, &args); - } else if(IsNPC()) { - std::vector args; - args.push_back(&buffs[slot].casterid); - - parse->EventSpell(EVENT_SPELL_FADE, CastToNPC(), nullptr, buffs[slot].spellid, slot, &args); + if (IsClient()) { + if (parse->EventSpell(EVENT_SPELL_FADE, nullptr, CastToClient(), buffs[slot].spellid, buf, 0) != 0) { + return; + } + } else if (IsNPC()) { + if (parse->EventSpell(EVENT_SPELL_FADE, CastToNPC(), nullptr, buffs[slot].spellid, buf, 0) != 0) { + return; + } } for (int i=0; i < EFFECT_COUNT; i++) From 657cbbcabe697c0dc7c5e90dc86d52d1d6c4d525 Mon Sep 17 00:00:00 2001 From: splose Date: Fri, 22 Oct 2021 13:48:15 -0400 Subject: [PATCH 282/624] define caster to fix a crash from #1618 (#1632) --- zone/spell_effects.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 926cfff6d..8af4e5324 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -163,9 +163,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove std::string buf = fmt::format( "{} {} {} {}", - caster->GetID(), + caster ? caster->GetID() : 0, buffs[buffslot].ticsremaining, - caster->GetLevel(), + caster ? caster->GetLevel() : 0, buffslot ); @@ -3751,9 +3751,9 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) std::string buf = fmt::format( "{} {} {} {}", - caster->GetID(), + caster ? caster->GetID() : 0, buffs[slot].ticsremaining, - caster->GetLevel(), + caster ? caster->GetLevel() : 0, slot ); From c30dbf66285acf802966d0327fa2d37e0730d971 Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Fri, 22 Oct 2021 16:16:56 -0500 Subject: [PATCH 283/624] [Bug Fix] Do not check tics remaining on non-buff spells (#1633) --- zone/spell_effects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 8af4e5324..68c04432b 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -164,7 +164,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove std::string buf = fmt::format( "{} {} {} {}", caster ? caster->GetID() : 0, - buffs[buffslot].ticsremaining, + buffslot >= 0 ? buffs[buffslot].ticsremaining : 0, caster ? caster->GetLevel() : 0, buffslot ); From 36d10462f7d34e624e263e44b7277e09b78fa10f Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 22 Oct 2021 22:39:37 -0400 Subject: [PATCH 284/624] [Combat] Updates to IMMUNE_MELEE_NONMAGICAL mechanics (#1616) * pre remove debug * Update attack.cpp * Update attack.cpp * Update attack.cpp * Update attack.cpp * apply to temp pets * format fix * changed to just use one rule Merged into NPC's and Pet's into one rule. --- common/ruletypes.h | 3 ++- zone/attack.cpp | 20 ++++++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 40682abcd..c432455e3 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -392,7 +392,8 @@ RULE_INT(Combat, ArcheryCritDifficulty, 3400, "Value against which is rolled to RULE_INT(Combat, ThrowingCritDifficulty, 1100, "Value against which is rolled to check if a throwing crit is triggered. Lower is easier") RULE_BOOL(Combat, NPCCanCrit, false, "Setting whether an NPC can land critical hits") RULE_BOOL(Combat, UseIntervalAC, true, "Switch whether bonuses, armour class, multipliers, classes and caps should be considered in the calculation of damage values") -RULE_INT(Combat, PetAttackMagicLevel, 30, "Level at which pets can cause magic damage") +RULE_INT(Combat, PetAttackMagicLevel, 10, "Level at which pets can cause magic damage, no longer used") +RULE_INT(Combat, NPCAttackMagicLevel, 10, "Level at which NPC and pets can cause magic damage") RULE_BOOL(Combat, EnableFearPathing, true, "Setting whether to use pathing during fear") RULE_BOOL(Combat, FleeGray, true, "If true FleeGrayHPRatio will be used") RULE_INT(Combat, FleeGrayHPRatio, 50, "HP percentage when a Gray NPC begins to flee") diff --git a/zone/attack.cpp b/zone/attack.cpp index 9b835afb3..f70bb6f1d 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -993,18 +993,22 @@ int Mob::GetWeaponDamage(Mob *against, const EQ::ItemData *weapon_item) { if (GetSpecialAbility(SPECATK_MAGICAL)) { dmg = 1; } - //On live this occurs for pets and charmed pet >= level 10 - else if (GetOwner() && GetLevel() >= RuleI(Combat, PetAttackMagicLevel)) { - //pets wouldn't actually use this but... - //it gives us an idea if we can hit due to the dual nature of this function + //On live this occurs for ALL NPC's >= 10 + else if (IsNPC() && GetLevel() >= RuleI(Combat, NPCAttackMagicLevel)) { dmg = 1; } else if (weapon_item) { if (weapon_item->Magic) { - dmg = weapon_item->Damage; - //this is more for non weapon items, ex: boots for kick - //they don't have a dmg but we should be able to hit magical - dmg = dmg <= 0 ? 1 : dmg; + if (weapon_item->Damage && (weapon_item->IsType1HWeapon() || weapon_item->IsType2HWeapon())) { + dmg = weapon_item->Damage; + } + //Non weapon items, ie. boots for kick. + else if (weapon_item->ItemType == EQ::item::ItemTypeArmor) { + dmg = 1; + } + else { + return 0; + } } else { return 0; From 6a244f16e1ca134307e1efc33ea339f103605d3e Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 24 Oct 2021 11:08:21 -0400 Subject: [PATCH 285/624] Update spells.cpp (#1635) --- zone/spells.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/spells.cpp b/zone/spells.cpp index dd7442911..c302160e7 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3859,6 +3859,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes //Need this to account for special AOE cases. if (IsClient() && IsHarmonySpell(spell_id) && !HarmonySpellLevelCheck(spell_id, spelltar)) { MessageString(Chat::SpellFailure, SPELL_NO_EFFECT); + safe_delete(action_packet); return false; } From da01156673cab9aac1519b462fff02291fb17e55 Mon Sep 17 00:00:00 2001 From: JJ <3617814+joligario@users.noreply.github.com> Date: Sun, 24 Oct 2021 11:53:23 -0400 Subject: [PATCH 286/624] Update 2021_03_03_instance_safereturns.sql (#1636) --- utils/sql/git/required/2021_03_03_instance_safereturns.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/sql/git/required/2021_03_03_instance_safereturns.sql b/utils/sql/git/required/2021_03_03_instance_safereturns.sql index 6687b17a7..73c522234 100644 --- a/utils/sql/git/required/2021_03_03_instance_safereturns.sql +++ b/utils/sql/git/required/2021_03_03_instance_safereturns.sql @@ -10,4 +10,4 @@ CREATE TABLE `character_instance_safereturns` ( `safe_heading` float NOT NULL DEFAULT 0, PRIMARY KEY (`id`), UNIQUE KEY `character_id` (`character_id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 +) ENGINE=InnoDB DEFAULT CHARSET=latin1; From 5eb95a95d00e23c921af3c7f6079dc8d7504e0ab Mon Sep 17 00:00:00 2001 From: Logan Date: Sun, 24 Oct 2021 13:53:49 -0700 Subject: [PATCH 287/624] [Rules] Added rule to extend max race id (#1630) * Added rule to extend max race id * Cleaned fmt of MaxRaceID * Added format command * Updated MaxRaceID default to be 732 --- common/ruletypes.h | 1 + zone/command.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index c432455e3..956ac43d3 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -510,6 +510,7 @@ RULE_BOOL(NPC, NPCHealOnGate, true, "Will the NPC Heal on Gate") RULE_BOOL(NPC, UseMeditateBasedManaRegen, false, "Based NPC ooc regen on Meditate skill") RULE_REAL(NPC, NPCHealOnGateAmount, 25, "How much the NPC will heal on gate if enabled") RULE_BOOL(NPC, AnimalsOpenDoors, true, "Determines or not whether animals open doors or not when they approach them") +RULE_INT(NPC, MaxRaceID, 732, "Maximum Race ID, RoF2 by default supports up to 732") RULE_CATEGORY_END() RULE_CATEGORY(Aggro) diff --git a/zone/command.cpp b/zone/command.cpp index 3cad3be78..be06bd9b6 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -3123,18 +3123,18 @@ void command_race(Client *c, const Seperator *sep) if (sep->IsNumber(1)) { auto race = atoi(sep->arg[1]); - if ((race >= 0 && race <= 732) || (race >= 2253 && race <= 2259)) { + if ((race >= 0 && race <= RuleI(NPC, MaxRaceID)) || (race >= 2253 && race <= 2259)) { if ((c->GetTarget()) && c->Admin() >= commandRaceOthers) { target = c->GetTarget(); } target->SendIllusionPacket(race); } else { - c->Message(Chat::White, "Usage: #race [0-732, 2253-2259] (0 for back to normal)"); + c->Message(Chat::White, fmt::format("Usage: #race [0-{}, 2253-2259] (0 for back to normal)", RuleI(NPC, MaxRaceID)).c_str()); } } else { - c->Message(Chat::White, "Usage: #race [0-732, 2253-2259] (0 for back to normal)"); + c->Message(Chat::White, fmt::format("Usage: #race [0-{}, 2253-2259] (0 for back to normal)", RuleI(NPC, MaxRaceID)).c_str()); } } @@ -5223,8 +5223,8 @@ void command_fixmob(Client *c, const Seperator *sep) if (strcasecmp(command, "race") == 0) { if (Race == 1 && codeMove == 'p') - Race = 724; - else if (Race >= 724 && codeMove != 'p') + Race = RuleI(NPC, MaxRaceID); + else if (Race >= RuleI(NPC, MaxRaceID) && codeMove != 'p') Race = 1; else Race += Adjustment; From 624d11de4ef4b88f6ac2d8310b875092830e3997 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 24 Oct 2021 17:03:24 -0400 Subject: [PATCH 288/624] [Bug Fix] Fix missing format in client message. (#1637) --- zone/inventory.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/zone/inventory.cpp b/zone/inventory.cpp index d6c3c5e79..bc79d7c43 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -385,12 +385,14 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, if(item->AugSlotVisible[iter] == 0) { Message( Chat::Red, - "{} ({}) has not evolved enough to accept {} ({}) in Augment Slot {}.", - database.CreateItemLink(item->ID), - item->ID, - database.CreateItemLink(augments[iter]), - augments[iter], - augment_slot + fmt::format( + "{} ({}) has not evolved enough to accept {} ({}) in Augment Slot {}.", + database.CreateItemLink(item->ID), + item->ID, + database.CreateItemLink(augments[iter]), + augments[iter], + augment_slot + ).c_str() ); LogInventory( "Player [{}] on account [{}] attempted to augment an unevolved item with augment type (Aug[{}]).\n" From c98f3cfb4c35a99c0416610a70f5acc6b17072c4 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 24 Oct 2021 17:06:22 -0400 Subject: [PATCH 289/624] [Quest API] Further char array cleanup. (#1634) - Cleans up the rest of the char arrays used when exporting to events. - Converts all events to use a similar variable name for export `export_string`. - Needless calls to .c_str() removed. --- zone/attack.cpp | 13 +++++++--- zone/cheat_manager.cpp | 14 +++++++++-- zone/client.cpp | 15 ++++++------ zone/client_packet.cpp | 50 ++++++++++++++++++++------------------ zone/client_process.cpp | 3 ++- zone/corpse.cpp | 16 +++++++----- zone/object.cpp | 6 ++--- zone/spells.cpp | 29 +++++++++------------- zone/task_client_state.cpp | 30 +++++++++-------------- zone/tasks.cpp | 6 ++--- zone/tradeskills.cpp | 18 ++++++++------ zone/zoning.cpp | 6 ++--- 12 files changed, 107 insertions(+), 99 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index f70bb6f1d..c78615ac8 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1631,9 +1631,14 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQ::skills::Skill if (!spell) spell = SPELL_UNKNOWN; - char buffer[48] = { 0 }; - snprintf(buffer, 47, "%d %d %d %d", killerMob ? killerMob->GetID() : 0, damage, spell, static_cast(attack_skill)); - if (parse->EventPlayer(EVENT_DEATH, this, buffer, 0) != 0) { + std::string export_string = fmt::format( + "{} {} {} {}", + killerMob ? killerMob->GetID() : 0, + damage, + spell, + static_cast(attack_skill) + ); + if (parse->EventPlayer(EVENT_DEATH, this, export_string, 0) != 0) { if (GetHP() < 0) { SetHP(0); } @@ -1935,7 +1940,7 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQ::skills::Skill QServ->PlayerLogEvent(Player_Log_Deaths, this->CharacterID(), event_desc); } - parse->EventPlayer(EVENT_DEATH_COMPLETE, this, buffer, 0); + parse->EventPlayer(EVENT_DEATH_COMPLETE, this, export_string, 0); return true; } diff --git a/zone/cheat_manager.cpp b/zone/cheat_manager.cpp index f0115e017..30ec922be 100644 --- a/zone/cheat_manager.cpp +++ b/zone/cheat_manager.cpp @@ -43,7 +43,12 @@ void CheatManager::CheatDetected(CheatTypes type, glm::vec3 position1, glm::vec3 zone->GetShortName() ); LogCheat(message); - std::string export_string = fmt::format("{} {} {}", position1.x, position1.y, position1.z); + std::string export_string = fmt::format( + "{} {} {}", + position1.x, + position1.y, + position1.z + ); parse->EventPlayer(EVENT_WARP, m_target, export_string, 0); } break; @@ -67,7 +72,12 @@ void CheatManager::CheatDetected(CheatTypes type, glm::vec3 position1, glm::vec3 zone->GetShortName() ); LogCheat(message); - std::string export_string = fmt::format("{} {} {}", position1.x, position1.y, position1.z); + std::string export_string = fmt::format( + "{} {} {}", + position1.x, + position1.y, + position1.z + ); parse->EventPlayer(EVENT_WARP, m_target, export_string, 0); m_time_since_last_warp_detection.Start(2500); } diff --git a/zone/client.cpp b/zone/client.cpp index aceb82b20..15a7e870d 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -2415,9 +2415,12 @@ bool Client::CheckIncreaseSkill(EQ::skills::SkillType skillid, Mob *against_who, return false; int skillval = GetRawSkill(skillid); int maxskill = GetMaxSkillAfterSpecializationRules(skillid, MaxSkill(skillid)); - char buffer[24] = { 0 }; - snprintf(buffer, 23, "%d %d", skillid, skillval); - parse->EventPlayer(EVENT_USE_SKILL, this, buffer, 0); + std::string export_string = fmt::format( + "{} {}", + skillid, + skillval + ); + parse->EventPlayer(EVENT_USE_SKILL, this, export_string, 0); if (against_who) { if ( against_who->GetSpecialAbility(IMMUNE_AGGRO) || @@ -5343,10 +5346,8 @@ void Client::ShowSkillsWindow() void Client::Signal(uint32 data) { - char buf[32]; - snprintf(buf, 31, "%d", data); - buf[31] = '\0'; - parse->EventPlayer(EVENT_SIGNAL, this, buf, 0); + std::string export_string = fmt::format("{}", data); + parse->EventPlayer(EVENT_SIGNAL, this, export_string, 0); } void Client::SendRewards() diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index c925d90db..f10e23a73 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -4354,12 +4354,10 @@ void Client::Handle_OP_ClickDoor(const EQApplicationPacket *app) ); } - char buf[20]; - snprintf(buf, 19, "%u", cd->doorid); - buf[19] = '\0'; + std::string export_string = fmt::format("{}", cd->doorid); std::vector args; args.push_back(currentdoor); - parse->EventPlayer(EVENT_CLICK_DOOR, this, buf, 0, &args); + parse->EventPlayer(EVENT_CLICK_DOOR, this, export_string, 0, &args); currentdoor->HandleClick(this, 0); return; @@ -4383,10 +4381,8 @@ void Client::Handle_OP_ClickObject(const EQApplicationPacket *app) 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, GetID(), &args); + std::string export_string = fmt::format("{}", click_object->drop_id); + parse->EventPlayer(EVENT_CLICK_OBJECT, this, export_string, GetID(), &args); } // Observed in RoF after OP_ClickObjectAction: @@ -4837,7 +4833,8 @@ void Client::Handle_OP_Consider(const EQApplicationPacket *app) if (tmob == 0) return; - if (parse->EventPlayer(EVENT_CONSIDER, this, fmt::format("{}", conin->targetid), 0) == 1) { + std::string export_string = fmt::format("{}", conin->targetid); + if (parse->EventPlayer(EVENT_CONSIDER, this, export_string, 0) == 1) { return; } @@ -4963,8 +4960,9 @@ void Client::Handle_OP_ConsiderCorpse(const EQApplicationPacket *app) } Consider_Struct* conin = (Consider_Struct*)app->pBuffer; Corpse* tcorpse = entity_list.GetCorpseByID(conin->targetid); + std::string export_string = fmt::format("{}", conin->targetid); if (tcorpse && tcorpse->IsNPCCorpse()) { - if (parse->EventPlayer(EVENT_CONSIDER_CORPSE, this, fmt::format("{}", conin->targetid), 0) == 1) { + if (parse->EventPlayer(EVENT_CONSIDER_CORPSE, this, export_string, 0) == 1) { return; } @@ -4981,7 +4979,7 @@ void Client::Handle_OP_ConsiderCorpse(const EQApplicationPacket *app) } } else if (tcorpse && tcorpse->IsPlayerCorpse()) { - if (parse->EventPlayer(EVENT_CONSIDER_CORPSE, this, fmt::format("{}", conin->targetid), 0) == 1) { + if (parse->EventPlayer(EVENT_CONSIDER_CORPSE, this, export_string, 0) == 1) { return; } @@ -5928,9 +5926,13 @@ void Client::Handle_OP_EnvDamage(const EQApplicationPacket *app) /* EVENT_ENVIRONMENTAL_DAMAGE */ int final_damage = (damage * RuleR(Character, EnvironmentDamageMulipliter)); - char buf[24]; - snprintf(buf, 23, "%u %u %i", ed->damage, ed->dmgtype, final_damage); - parse->EventPlayer(EVENT_ENVIRONMENTAL_DAMAGE, this, buf, 0); + std::string export_string = fmt::format( + "{} {} {}", + ed->damage, + ed->dmgtype, + final_damage + ); + parse->EventPlayer(EVENT_ENVIRONMENTAL_DAMAGE, this, export_string, 0); } if (GetHP() <= 0) { @@ -8539,18 +8541,18 @@ void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app) if (GetTarget() && GetTarget()->IsNPC()) { if (silentsaylink) { - parse->EventNPC(EVENT_SAY, GetTarget()->CastToNPC(), this, response.c_str(), 0); + parse->EventNPC(EVENT_SAY, GetTarget()->CastToNPC(), this, response, 0); if (response[0] == '#' && parse->PlayerHasQuestSub(EVENT_COMMAND)) { - parse->EventPlayer(EVENT_COMMAND, this, response.c_str(), 0); + parse->EventPlayer(EVENT_COMMAND, this, response, 0); } #ifdef BOTS else if (response[0] == '^' && parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) { - parse->EventPlayer(EVENT_BOT_COMMAND, this, response.c_str(), 0); + parse->EventPlayer(EVENT_BOT_COMMAND, this, response, 0); } #endif else { - parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); + parse->EventPlayer(EVENT_SAY, this, response, 0); } } else { @@ -8562,15 +8564,15 @@ void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app) else { if (silentsaylink) { if (response[0] == '#' && parse->PlayerHasQuestSub(EVENT_COMMAND)) { - parse->EventPlayer(EVENT_COMMAND, this, response.c_str(), 0); + parse->EventPlayer(EVENT_COMMAND, this, response, 0); } #ifdef BOTS else if (response[0] == '^' && parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) { - parse->EventPlayer(EVENT_BOT_COMMAND, this, response.c_str(), 0); + parse->EventPlayer(EVENT_BOT_COMMAND, this, response, 0); } #endif else { - parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); + parse->EventPlayer(EVENT_SAY, this, response, 0); } } else { @@ -11164,13 +11166,13 @@ void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app) break; } - std::string buf = fmt::format("{}", popup_response->popupid); + std::string export_string = fmt::format("{}", popup_response->popupid); - parse->EventPlayer(EVENT_POPUP_RESPONSE, this, buf.c_str(), 0); + parse->EventPlayer(EVENT_POPUP_RESPONSE, this, export_string, 0); Mob *Target = GetTarget(); if (Target && Target->IsNPC()) { - parse->EventNPC(EVENT_POPUP_RESPONSE, Target->CastToNPC(), this, buf.c_str(), 0); + parse->EventNPC(EVENT_POPUP_RESPONSE, Target->CastToNPC(), this, export_string, 0); } } diff --git a/zone/client_process.cpp b/zone/client_process.cpp index a2c34a007..98d862d4e 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -2074,7 +2074,8 @@ void Client::HandleRespawnFromHover(uint32 Option) } //After they've respawned into the same zone, trigger EVENT_RESPAWN - parse->EventPlayer(EVENT_RESPAWN, this, static_cast(itoa(Option)), is_rez ? 1 : 0); + std::string export_string = fmt::format("{}", Option); + parse->EventPlayer(EVENT_RESPAWN, this, export_string, is_rez ? 1 : 0); //Pop Rez option from the respawn options list; //easiest way to make sure it stays at the end and diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 2053e559d..bd8a36418 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -1283,9 +1283,13 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) } } - char q_corpse_name[64]; - strcpy(q_corpse_name, corpse_name); - std::string buf = fmt::format("{} {} {} {}", inst->GetItem()->ID, inst->GetCharges(), EntityList::RemoveNumbers(q_corpse_name), GetID()); + std::string export_string = fmt::format( + "{} {} {} {}", + inst->GetItem()->ID, + inst->GetCharges(), + EntityList::RemoveNumbers(corpse_name), + GetID() + ); std::vector args; args.push_back(inst); args.push_back(this); @@ -1293,13 +1297,13 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) if (RuleB(Zone, UseZoneController)) { auto controller = entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID); if (controller){ - if (parse->EventNPC(EVENT_LOOT_ZONE, controller, client, buf.c_str(), 0, &args) != 0) { + if (parse->EventNPC(EVENT_LOOT_ZONE, controller, client, export_string, 0, &args) != 0) { prevent_loot = true; } } } - if (parse->EventPlayer(EVENT_LOOT, client, buf.c_str(), 0, &args) != 0) { + if (parse->EventPlayer(EVENT_LOOT, client, export_string, 0, &args) != 0) { prevent_loot = true; } @@ -1316,7 +1320,7 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) } // do we want this to have a fail option too? Sure? - if (parse->EventItem(EVENT_LOOT, client, inst, this, buf.c_str(), 0) != 0) { + if (parse->EventItem(EVENT_LOOT, client, inst, this, export_string, 0) != 0) { prevent_loot = true; } diff --git a/zone/object.cpp b/zone/object.cpp index 88038bbe8..30222c404 100644 --- a/zone/object.cpp +++ b/zone/object.cpp @@ -509,12 +509,10 @@ bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object) m_inst->SetRecastTimestamp( database.GetItemRecastTimestamp(sender->CharacterID(), item->RecastType)); - char buf[10]; - snprintf(buf, 9, "%u", item->ID); - buf[9] = '\0'; + std::string export_string = fmt::format("{}", item->ID); std::vector args; args.push_back(m_inst); - if(parse->EventPlayer(EVENT_PLAYER_PICKUP, sender, buf, this->GetID(), &args)) + if(parse->EventPlayer(EVENT_PLAYER_PICKUP, sender, export_string, this->GetID(), &args)) { auto outapp = new EQApplicationPacket(OP_ClickObject, sizeof(ClickObject_Struct)); memcpy(outapp->pBuffer, click_object, sizeof(ClickObject_Struct)); diff --git a/zone/spells.cpp b/zone/spells.cpp index c302160e7..bd9442646 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -288,13 +288,13 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, } } + std::string export_string = fmt::format("{}", spell_id); if(IsClient()) { - std::string buf = fmt::format("{}", spell_id); - if (parse->EventPlayer(EVENT_CAST_BEGIN, CastToClient(), buf.c_str(), 0) != 0) + if (parse->EventPlayer(EVENT_CAST_BEGIN, CastToClient(), export_string, 0) != 0) { return false; + } } else if(IsNPC()) { - std::string buf = fmt::format("{}", spell_id); - parse->EventNPC(EVENT_CAST_BEGIN, CastToNPC(), nullptr, buf.c_str(), 0); + parse->EventNPC(EVENT_CAST_BEGIN, CastToNPC(), nullptr, export_string, 0); } //To prevent NPC ghosting when spells are cast from scripts @@ -1437,12 +1437,11 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo // at this point the spell has successfully been cast // + std::string export_string = fmt::format("{}", spell_id); if(IsClient()) { - std::string buf = fmt::format("{}", spell_id); - parse->EventPlayer(EVENT_CAST, CastToClient(), buf.c_str(), 0); + parse->EventPlayer(EVENT_CAST, CastToClient(), export_string, 0); } else if(IsNPC()) { - std::string buf = fmt::format("{}", spell_id); - parse->EventNPC(EVENT_CAST, CastToNPC(), nullptr, buf.c_str(), 0); + parse->EventNPC(EVENT_CAST, CastToNPC(), nullptr, export_string, 0); } if(bard_song_mode) @@ -3648,15 +3647,11 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes ); /* Send the EVENT_CAST_ON event */ - if(spelltar->IsNPC()) - { - std::string buf = fmt::format("{}", spell_id); - parse->EventNPC(EVENT_CAST_ON, spelltar->CastToNPC(), this, buf.c_str(), 0); - } - else if (spelltar->IsClient()) - { - std::string buf = fmt::format("{}", spell_id); - parse->EventPlayer(EVENT_CAST_ON, spelltar->CastToClient(), buf.c_str(), 0); + std::string export_string = fmt::format("{}", spell_id); + if(spelltar->IsNPC()) { + parse->EventNPC(EVENT_CAST_ON, spelltar->CastToNPC(), this, export_string, 0); + } else if (spelltar->IsClient()) { + parse->EventPlayer(EVENT_CAST_ON, spelltar->CastToClient(), export_string, 0); } mod_spell_cast(spell_id, spelltar, reflect_effectiveness, use_resist_adjust, resist_adjust, isproc); diff --git a/zone/task_client_state.cpp b/zone/task_client_state.cpp index d995044a7..8017a0fec 100644 --- a/zone/task_client_state.cpp +++ b/zone/task_client_state.cpp @@ -1154,17 +1154,13 @@ void ClientTaskState::IncrementDoneCount( } if (!ignore_quest_update) { - char buf[24]; - snprintf( - buf, - 23, - "%d %d %d", + std::string export_string = fmt::format( + "{} {} {}", info->activity[activity_id].done_count, info->activity[activity_id].activity_id, info->task_id ); - buf[23] = '\0'; - parse->EventPlayer(EVENT_TASK_UPDATE, client, buf, 0); + parse->EventPlayer(EVENT_TASK_UPDATE, client, export_string, 0); } info->activity[activity_id].updated = true; @@ -1189,10 +1185,12 @@ void ClientTaskState::IncrementDoneCount( client->MessageString(Chat::White, TASK_UPDATED, task_information->title.c_str()); if (!ignore_quest_update) { - char buf[24]; - snprintf(buf, 23, "%d %d", info->task_id, info->activity[activity_id].activity_id); - buf[23] = '\0'; - parse->EventPlayer(EVENT_TASK_STAGE_COMPLETE, client, buf, 0); + std::string export_string = fmt::format( + "{} {}", + info->task_id, + info->activity[activity_id].activity_id + ); + parse->EventPlayer(EVENT_TASK_STAGE_COMPLETE, client, export_string, 0); } /* QS: PlayerLogTaskUpdates :: Update */ if (RuleB(QueryServ, PlayerLogTaskUpdates)) { @@ -1210,17 +1208,13 @@ void ClientTaskState::IncrementDoneCount( // updated in UnlockActivities. Send the completed task list to the // client. This is the same sequence the packets are sent on live. if (task_complete) { - char buf[24]; - snprintf( - buf, - 23, - "%d %d %d", + std::string export_string = fmt::format( + "{} {} {}", info->activity[activity_id].done_count, info->activity[activity_id].activity_id, info->task_id ); - buf[23] = '\0'; - parse->EventPlayer(EVENT_TASK_COMPLETE, client, buf, 0); + parse->EventPlayer(EVENT_TASK_COMPLETE, client, export_string, 0); /* QS: PlayerLogTaskUpdates :: Complete */ if (RuleB(QueryServ, PlayerLogTaskUpdates)) { diff --git a/zone/tasks.cpp b/zone/tasks.cpp index be8a6ca58..a524ebd5f 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -97,10 +97,8 @@ void Client::SendTaskActivityComplete( void Client::SendTaskFailed(int task_id, int task_index, TaskType task_type) { // 0x54eb - char buf[24]; - snprintf(buf, 23, "%d", task_id); - buf[23] = '\0'; - parse->EventPlayer(EVENT_TASK_FAIL, this, buf, 0); + std::string export_string = fmt::format("{}", task_id); + parse->EventPlayer(EVENT_TASK_FAIL, this, export_string, 0); TaskActivityComplete_Struct *task_activity_complete; diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index ea46fae9a..22903ebf5 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -504,11 +504,11 @@ void Object::HandleCombine(Client* user, const NewCombine_Struct* in_combine, Ob user->DeleteItemInInventory(in_combine->container_slot, 0, true); } } + if (success) { - parse->EventPlayer(EVENT_COMBINE_SUCCESS, user, spec.name.c_str(), spec.recipe_id); - } - else { - parse->EventPlayer(EVENT_COMBINE_FAILURE, user, spec.name.c_str(), spec.recipe_id); + parse->EventPlayer(EVENT_COMBINE_SUCCESS, user, spec.name, spec.recipe_id); + } else { + parse->EventPlayer(EVENT_COMBINE_FAILURE, user, spec.name, spec.recipe_id); } } @@ -672,10 +672,12 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac if(success && spec.replace_container) { // user->DeleteItemInInventory(in_combine->container_slot, 0, true); } - if (success) - parse->EventPlayer(EVENT_COMBINE_SUCCESS, user, spec.name.c_str(), spec.recipe_id); - else - parse->EventPlayer(EVENT_COMBINE_FAILURE, user, spec.name.c_str(), spec.recipe_id); + + if (success) { + parse->EventPlayer(EVENT_COMBINE_SUCCESS, user, spec.name, spec.recipe_id); + } else { + parse->EventPlayer(EVENT_COMBINE_FAILURE, user, spec.name, spec.recipe_id); + } } EQ::skills::SkillType Object::TypeToSkill(uint32 type) diff --git a/zone/zoning.cpp b/zone/zoning.cpp index 300e9d1bf..7ca47aa7b 100644 --- a/zone/zoning.cpp +++ b/zone/zoning.cpp @@ -202,10 +202,8 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { return; } - char buf[10]; - snprintf(buf, 9, "%d", target_zone_id); - buf[9] = '\0'; - parse->EventPlayer(EVENT_ZONE, this, buf, 0); + std::string export_string = fmt::format("{}", target_zone_id); + parse->EventPlayer(EVENT_ZONE, this, export_string, 0); //handle circumvention of zone restrictions //we need the value when creating the outgoing packet as well. From 0b18671e91dae712046a4ec9b021724b5804455e Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 24 Oct 2021 17:07:25 -0400 Subject: [PATCH 290/624] [Spells] Update to how Bard Instrument mods are applied to spell effects (#1628) * new instrument mod spell effect checks PR split * format * Update spdat.cpp correction, all direct damage spells get modifiers. Made a mistake with the parse, was using wrong mod. * restriction changes cure effects can be modified. decided to keep a list of known effects that are not modified to return false. and will keep the default to be true for anything as to not inhibit custom bard song development * SE_ProcChance is modified * Update spdat.cpp * update * Update spell_effects.cpp --- common/spdat.cpp | 93 ++++++++++++++++++++++++++++++++++++++++++ common/spdat.h | 1 + zone/spell_effects.cpp | 13 ++---- 3 files changed, 97 insertions(+), 10 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 7c3476505..1fb54b0dd 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1366,6 +1366,99 @@ bool SpellRequiresTarget(int spell_id) return true; } +bool IsInstrumentModAppliedToSpellEffect(int32 spell_id, int effect) +{ + + //Effects that are verified modifiable by bard instrument/singing mods, or highly likely due to similiar type of effect. + switch (effect) { + + //Only modify instant endurance or mana effects (Ie. Mana drain, Crescendo line) + case SE_CurrentEndurance: + case SE_CurrentMana: { + if (spells[spell_id].buffduration == 0) { + return true; + } + //Mana regen is not modified. + return false; + } + + case SE_CurrentHP: + case SE_ArmorClass: + case SE_ACv2: + case SE_MovementSpeed: + case SE_ATK: + case SE_STR: + case SE_DEX: + case SE_AGI: + case SE_STA: + case SE_INT: + case SE_WIS: + case SE_CHA: + case SE_AllStats: + case SE_ResistFire: + case SE_ResistCold: + case SE_ResistPoison: + case SE_ResistDisease: + case SE_ResistMagic: + case SE_ResistAll: + case SE_ResistCorruption: + case SE_Rune: + case SE_AbsorbMagicAtt: + case SE_DamageShield: + case SE_MitigateDamageShield: + case SE_Amplification: //On live Amplification is modified by singing mods, including itself. + case SE_TripleAttackChance: + case SE_Flurry: + case SE_DamageModifier: + case SE_DamageModifier2: + case SE_MinDamageModifier: + case SE_ProcChance: + case SE_PetFlurry: // ? Need verified + case SE_DiseaseCounter: + case SE_PoisonCounter: + case SE_CurseCounter: + case SE_CorruptionCounter: + return true; + + /* + Following are confirmed NOT modifiable by instrument/singing mods. + Focus Effects, Proc Effects, Spell Triggers are not modified but handled elsewhere, not neccessary to checked here. + */ + + case SE_AttackSpeed: //(Haste AND Slow not modifiable) + case SE_AttackSpeed2: + case SE_AttackSpeed3: + case SE_Lull: + case SE_ChangeFrenzyRad: + case SE_Harmony: + case SE_AddFaction: + //case SE_CurrentMana: // duration only + case SE_ManaRegen_v2: + //case SE_CurrentEndurance: // duration only + case SE_PersistentEffect: + case SE_ReduceReuseTimer: + case SE_Stun: + case SE_Mez: + case SE_WipeHateList: //? + case SE_CancelMagic: + case SE_ManaAbsorbPercentDamage: + case SE_ResistSpellChance: + case SE_Reflect: + case SE_MitigateSpellDamage: + case SE_MitigateMeleeDamage: + case SE_AllInstrumentMod: + case SE_AddSingingMod: + case SE_SongModCap: + case SE_BardSongRange: + case SE_TemporaryPets: + case SE_SpellOnDeath: + return false; + default: + return true; + } + //Allowing anything not confirmed to be restricted / allowed to receive modifiers, as to not inhbit anyone making custom bard songs. +} + int GetSpellStatValue(uint32 spell_id, const char* stat_identifier, uint8 slot) { if (!IsValidSpell(spell_id)) diff --git a/common/spdat.h b/common/spdat.h index c6c9df888..d4b858ee0 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -1509,6 +1509,7 @@ bool IsCastWhileInvis(uint16 spell_id); bool IsEffectIgnoredInStacking(int spa); bool IsFocusLimit(int spa); bool SpellRequiresTarget(int targettype); +bool IsInstrumentModAppliedToSpellEffect(int32 spell_id, int effect); int CalcPetHp(int levelb, int classb, int STA = 75); int GetSpellEffectDescNum(uint16 spell_id); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 68c04432b..14385dff3 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3360,16 +3360,9 @@ int Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level, effect_value = CalcSpellEffectValue_formula(formula, base, max, caster_level, spell_id, ticsremaining); // this doesn't actually need to be a song to get mods, just the right skill - if (EQ::skills::IsBardInstrumentSkill(spells[spell_id].skill) && - spells[spell_id].effectid[effect_id] != SE_AttackSpeed && - spells[spell_id].effectid[effect_id] != SE_AttackSpeed2 && - spells[spell_id].effectid[effect_id] != SE_AttackSpeed3 && - spells[spell_id].effectid[effect_id] != SE_Lull && - spells[spell_id].effectid[effect_id] != SE_ChangeFrenzyRad && - spells[spell_id].effectid[effect_id] != SE_Harmony && - spells[spell_id].effectid[effect_id] != SE_CurrentMana && - spells[spell_id].effectid[effect_id] != SE_ManaRegen_v2 && - spells[spell_id].effectid[effect_id] != SE_AddFaction) { + if (EQ::skills::IsBardInstrumentSkill(spells[spell_id].skill) + && IsInstrumentModAppliedToSpellEffect(spell_id, spells[spell_id].effectid[effect_id])){ + int oval = effect_value; int mod = ApplySpellEffectiveness(spell_id, instrument_mod, true, caster_id); From 62253cc016c0f3ee2d980d2ab316933a57faef40 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Sun, 24 Oct 2021 17:17:42 -0400 Subject: [PATCH 291/624] [Bug Fix] Edge cases where min_drop 1 not honored with valid choices (#1617) * [Bug Fix] Edge cases where min_drop 1 not honored with valid choices * Forgot header file change. * Remove verbose option from MeetsLootDropLevelRequirements per @akka * Fix spacing * Restore verbose mode after further consideration * Remove logging on counting of valid items Co-authored-by: Noudess --- zone/loottables.cpp | 56 ++++++++++++++++++++++++++------------------- zone/npc.h | 2 +- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/zone/loottables.cpp b/zone/loottables.cpp index b4e0f00f0..4bcfc81e2 100644 --- a/zone/loottables.cpp +++ b/zone/loottables.cpp @@ -133,7 +133,7 @@ void ZoneDatabase::AddLootDropToNPC(NPC *npc, uint32 lootdrop_id, ItemList *item int charges = loot_drop->Entries[i].multiplier; for (int j = 0; j < charges; ++j) { if (zone->random.Real(0.0, 100.0) <= loot_drop->Entries[i].chance && - npc->MeetsLootDropLevelRequirements(loot_drop->Entries[i])) { + npc->MeetsLootDropLevelRequirements(loot_drop->Entries[i], true)) { const EQ::ItemData *database_item = GetItem(loot_drop->Entries[i].item_id); npc->AddLootDrop( database_item, @@ -155,29 +155,29 @@ void ZoneDatabase::AddLootDropToNPC(NPC *npc, uint32 lootdrop_id, ItemList *item } float roll_t = 0.0f; - float roll_t_min = 0.0f; bool active_item_list = false; for (uint32 i = 0; i < loot_drop->NumEntries; ++i) { const EQ::ItemData *db_item = GetItem(loot_drop->Entries[i].item_id); - if (db_item) { + if (db_item && npc->MeetsLootDropLevelRequirements(loot_drop->Entries[i])) { roll_t += loot_drop->Entries[i].chance; active_item_list = true; } } - roll_t_min = roll_t; - roll_t = EQ::ClampLower(roll_t, 100.0f); - if (!active_item_list) { return; } for (int i = 0; i < mindrop; ++i) { - float roll = (float) zone->random.Real(0.0, roll_t_min); + float roll = (float) zone->random.Real(0.0, roll_t); for (uint32 j = 0; j < loot_drop->NumEntries; ++j) { const EQ::ItemData *db_item = GetItem(loot_drop->Entries[j].item_id); if (db_item) { - if (roll < loot_drop->Entries[j].chance && npc->MeetsLootDropLevelRequirements(loot_drop->Entries[j])) { + // if it doesn't meet the requirements do nothing + if (!npc->MeetsLootDropLevelRequirements(loot_drop->Entries[j])) + continue; + + if (roll < loot_drop->Entries[j].chance) { npc->AddLootDrop( db_item, item_list, @@ -213,7 +213,11 @@ void ZoneDatabase::AddLootDropToNPC(NPC *npc, uint32 lootdrop_id, ItemList *item for (uint32 j = 0; j < loot_drop->NumEntries; ++j) { const EQ::ItemData *db_item = GetItem(loot_drop->Entries[j].item_id); if (db_item) { - if (roll < loot_drop->Entries[j].chance && npc->MeetsLootDropLevelRequirements(loot_drop->Entries[j])) { + // if it doesn't meet the requirements do nothing + if (!npc->MeetsLootDropLevelRequirements(loot_drop->Entries[j])) + continue; + + if (roll < loot_drop->Entries[j].chance) { npc->AddLootDrop( db_item, item_list, @@ -250,27 +254,31 @@ void ZoneDatabase::AddLootDropToNPC(NPC *npc, uint32 lootdrop_id, ItemList *item // npc->SendAppearancePacket(AT_Light, npc->GetActiveLightValue()); } -bool NPC::MeetsLootDropLevelRequirements(LootDropEntries_Struct loot_drop) +bool NPC::MeetsLootDropLevelRequirements(LootDropEntries_Struct loot_drop, bool verbose) { if (loot_drop.npc_min_level > 0 && GetLevel() < loot_drop.npc_min_level) { - LogLootDetail( - "NPC [{}] does not meet loot_drop level requirements (min_level) level [{}] current [{}] for item [{}]", - GetCleanName(), - loot_drop.npc_min_level, - GetLevel(), - database.CreateItemLink(loot_drop.item_id) - ); + if (verbose) { + LogLootDetail( + "NPC [{}] does not meet loot_drop level requirements (min_level) level [{}] current [{}] for item [{}]", + GetCleanName(), + loot_drop.npc_min_level, + GetLevel(), + database.CreateItemLink(loot_drop.item_id) + ); + } return false; } if (loot_drop.npc_max_level > 0 && GetLevel() > loot_drop.npc_max_level) { - LogLootDetail( - "NPC [{}] does not meet loot_drop level requirements (max_level) level [{}] current [{}] for item [{}]", - GetCleanName(), - loot_drop.npc_max_level, - GetLevel(), - database.CreateItemLink(loot_drop.item_id) - ); + if (verbose) { + LogLootDetail( + "NPC [{}] does not meet loot_drop level requirements (max_level) level [{}] current [{}] for item [{}]", + GetCleanName(), + loot_drop.npc_max_level, + GetLevel(), + database.CreateItemLink(loot_drop.item_id) + ); + } return false; } diff --git a/zone/npc.h b/zone/npc.h index af07b396c..2c43b79c1 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -314,7 +314,7 @@ public: uint32 aug6 = 0 ); - bool MeetsLootDropLevelRequirements(LootDropEntries_Struct loot_drop); + bool MeetsLootDropLevelRequirements(LootDropEntries_Struct loot_drop, bool verbose=false); virtual void DoClassAttacks(Mob *target); void CheckSignal(); From 1c5f9f2e0f1f866deb42fea3ca2453bfab4edcdf Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 24 Oct 2021 17:50:43 -0400 Subject: [PATCH 292/624] [Bug Fix] Fix possible #proximity crash. (#1639) --- zone/command.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/command.cpp b/zone/command.cpp index be06bd9b6..723e96333 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -6097,7 +6097,7 @@ void command_push(Client *c, const Seperator *sep) void command_proximity(Client *c, const Seperator *sep) { - if (!c->GetTarget() && !c->GetTarget()->IsNPC()) { + if (!c->GetTarget() || (c->GetTarget() && !c->GetTarget()->IsNPC())) { c->Message(Chat::White, "You must target an NPC"); return; } From 060be606e730d62e370366fdfb959e07cdfdfa8d Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 24 Oct 2021 19:27:51 -0400 Subject: [PATCH 293/624] [Spells] Rework of Virus Effect code (#1593) * start of rework * functional * virus updates * Update npc.cpp * updates * updates * update v2 * pre remove old code * removed old code1 * remove debugs * description * Update spell_effects.cpp * changed function name * remove unused var * merge error fix * fix formating issue * Update spdat.cpp * Update spell_effects.cpp * Convert virus entity range code to use vectors and GetCloseMobList * Formatting [skip ci] Co-authored-by: Akkadius --- common/spdat.cpp | 23 ++++++ common/spdat.h | 8 +- zone/api_service.cpp | 1 - zone/client_process.cpp | 15 +--- zone/common.h | 1 + zone/entity.cpp | 111 +++++++++++++++++--------- zone/entity.h | 2 +- zone/mob.cpp | 27 +------ zone/mob.h | 16 ++-- zone/npc.cpp | 26 +++--- zone/spell_effects.cpp | 170 ++++++++++++++++++++++++++-------------- zone/spells.cpp | 6 +- zone/zonedb.cpp | 1 + 13 files changed, 242 insertions(+), 165 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 1fb54b0dd..432b903b5 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1572,3 +1572,26 @@ int GetSpellStatValue(uint32 spell_id, const char* stat_identifier, uint8 slot) return 0; } + +bool IsVirusSpell(int32 spell_id) +{ + if (GetViralMinSpreadTime(spell_id) && GetViralMaxSpreadTime(spell_id) && GetViralSpreadRange(spell_id)){ + return true; + } + return false; +} + +int32 GetViralMinSpreadTime(int32 spell_id) +{ + return spells[spell_id].viral_targets; +} + +int32 GetViralMaxSpreadTime(int32 spell_id) +{ + return spells[spell_id].viral_timer; +} + +int32 GetViralSpreadRange(int32 spell_id) +{ + return spells[spell_id].viral_range; +} diff --git a/common/spdat.h b/common/spdat.h index d4b858ee0..7a078903a 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -1342,8 +1342,8 @@ struct SPDat_Spell_Struct /* 188 */ //int npc_usefulness; // -- NPC_USEFULNESS /* 189 */ int MinResist; // -- MIN_RESIST /* 190 */ int MaxResist; // -- MAX_RESIST -/* 191 */ uint8 viral_targets; // -- MIN_SPREAD_TIME -/* 192 */ uint8 viral_timer; // -- MAX_SPREAD_TIME +/* 191 */ int viral_targets; // -- MIN_SPREAD_TIME +/* 192 */ int viral_timer; // -- MAX_SPREAD_TIME /* 193 */ int NimbusEffect; // -- DURATION_PARTICLE_EFFECT /* 194 */ float directional_start; //Cone Start Angle: -- CONE_START_ANGLE /* 195 */ float directional_end; // Cone End Angle: -- CONE_END_ANGLE @@ -1509,6 +1509,10 @@ bool IsCastWhileInvis(uint16 spell_id); bool IsEffectIgnoredInStacking(int spa); bool IsFocusLimit(int spa); bool SpellRequiresTarget(int targettype); +bool IsVirusSpell(int32 spell_id); +int GetViralMinSpreadTime(int32 spell_id); +int GetViralMaxSpreadTime(int32 spell_id); +int GetViralSpreadRange(int32 spell_id); bool IsInstrumentModAppliedToSpellEffect(int32 spell_id, int effect); int CalcPetHp(int levelb, int classb, int STA = 75); diff --git a/zone/api_service.cpp b/zone/api_service.cpp index f97c451b1..60f36418b 100644 --- a/zone/api_service.cpp +++ b/zone/api_service.cpp @@ -478,7 +478,6 @@ Json::Value ApiGetMobListDetail(EQ::Net::WebsocketServerConnection *connection, row["has_temp_pets_active"] = mob->HasTempPetsActive(); row["has_two_hand_blunt_equiped"] = mob->HasTwoHandBluntEquiped(); row["has_two_hander_equipped"] = mob->HasTwoHanderEquipped(); - row["has_virus"] = mob->HasVirus(); row["hate_summon"] = mob->HateSummon(); row["helm_texture"] = mob->GetHelmTexture(); row["hp"] = mob->GetHP(); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 98d862d4e..b7aad858b 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -444,19 +444,8 @@ bool Client::Process() { } } - if (HasVirus()) { - if (viral_timer.Check()) { - viral_timer_counter++; - for (int i = 0; i < MAX_SPELL_TRIGGER * 2; i += 2) { - if (viral_spells[i]) { - if (viral_timer_counter % spells[viral_spells[i]].viral_timer == 0) { - SpreadVirus(viral_spells[i], viral_spells[i + 1]); - } - } - } - } - if (viral_timer_counter > 999) - viral_timer_counter = 0; + if (viral_timer.Check() && !dead) { + VirusEffectProcess(); } ProjectileAttack(); diff --git a/zone/common.h b/zone/common.h index 937cffc51..8678aec64 100644 --- a/zone/common.h +++ b/zone/common.h @@ -330,6 +330,7 @@ struct Buffs_Struct { uint32 instrument_mod; int16 focusproclimit_time; //timer to limit number of procs from focus effects int16 focusproclimit_procamt; //amount of procs that can be cast before timer limiter is set + int32 virus_spread_time; //time till next attempted viral spread bool persistant_buff; bool client; //True if the caster is a client bool UpdateClient; diff --git a/zone/entity.cpp b/zone/entity.cpp index 8a15d10a4..28bb5e144 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1219,7 +1219,7 @@ uint32 EntityList::CountSpawnedNPCs(std::vector npc_ids) npc_ids.begin(), npc_ids.end(), current_npc.second->GetNPCTypeID() - ) != npc_ids.end() && + ) != npc_ids.end() && current_npc.second->GetID() != 0 ) { npc_count++; @@ -5222,59 +5222,94 @@ Client *EntityList::FindCorpseDragger(uint16 CorpseID) return nullptr; } -Mob *EntityList::GetTargetForVirus(Mob *spreader, int range) +std::vector EntityList::GetTargetsForVirusEffect(Mob *spreader, Mob *original_caster, int range, int pcnpc, int32 spell_id) { - int max_spread_range = RuleI(Spells, VirusSpreadDistance); + /* + Live Mechanics + Virus spreader does NOT need LOS + There is no max target limit + */ + if (!spreader) { + return {}; + } - if (range) - max_spread_range = range; + std::vector spreader_list = {}; + bool is_detrimental_spell = IsDetrimentalSpell(spell_id); + for (auto &it : entity_list.GetCloseMobList(spreader, range)) { + Mob *mob = it.second; + if (mob == spreader) { + continue; + } - std::vector TargetsInRange; + // check PC/NPC only flag 1 = PCs, 2 = NPCs + if (pcnpc == 1 && !mob->IsClient() && !mob->IsMerc() && !mob->IsBot()) { + continue; + } + else if (pcnpc == 2 && (mob->IsClient() || mob->IsMerc() || mob->IsBot())) { + continue; + } + if (mob->IsClient() && !mob->CastToClient()->ClientFinishedLoading()) { + continue; + } - auto it = mob_list.begin(); - while (it != mob_list.end()) { - Mob *cur = it->second; - // Make sure the target is in range, has los and is not the mob doing the spreading - if ((cur->GetID() != spreader->GetID()) && - (cur->CalculateDistance(spreader->GetX(), spreader->GetY(), - spreader->GetZ()) <= max_spread_range) && - (spreader->CheckLosFN(cur))) { - // If the spreader is an npc it can only spread to other npc controlled mobs - if (spreader->IsNPC() && !spreader->IsPet() && !spreader->CastToNPC()->GetSwarmOwner() && cur->IsNPC()) { - TargetsInRange.push_back(cur); + if (mob->IsAura() || mob->IsTrap()) { + continue; + } + + // Make sure the target is in range + if (mob->CalculateDistance(spreader->GetX(), spreader->GetY(), spreader->GetZ()) <= range) { + + //Do not allow detrimental spread to anything the original caster couldn't normally attack. + if (is_detrimental_spell && !original_caster->IsAttackAllowed(mob, true)) { + continue; } - // If the spreader is an npc controlled pet it can spread to any other npc or an npc controlled pet - else if (spreader->IsNPC() && spreader->IsPet() && spreader->GetOwner()->IsNPC()) { - if (cur->IsNPC() && !cur->IsPet()) { - TargetsInRange.push_back(cur); - } else if (cur->IsNPC() && cur->IsPet() && cur->GetOwner()->IsNPC()) { - TargetsInRange.push_back(cur); + + //For non-NPCs, do not allow beneficial spread to anything the original caster could normally attack. + if (!is_detrimental_spell && !original_caster->IsNPC() && original_caster->IsAttackAllowed(mob, true)) { + continue; + } + + // If the spreader is an npc and NOT a PET, then spread to other npc controlled mobs that are not pets + if (spreader->IsNPC() && !spreader->IsPet() && !spreader->IsTempPet() && mob->IsNPC() && !mob->IsPet() && !mob->IsTempPet()) { + spreader_list.push_back(mob); + } + // If the spreader is an npc and NOT a PET, then spread to npc controlled pet + else if (spreader->IsNPC() && !spreader->IsPet() && !spreader->IsTempPet() && mob->IsNPC() && (mob->IsPet() || mob->IsTempPet()) && mob->IsPetOwnerNPC()) { + spreader_list.push_back(mob); + } + // If the spreader is an npc controlled PET it can spread to any other npc or an npc controlled pet + else if (spreader->IsNPC() && (spreader->IsPet() || spreader->IsTempPet()) && spreader->IsPetOwnerNPC()) { + if (mob->IsNPC() && (!mob->IsPet() || !mob->IsTempPet())) { + spreader_list.push_back(mob); } - else if (cur->IsNPC() && cur->CastToNPC()->GetSwarmOwner() && cur->GetOwner()->IsNPC()) { - TargetsInRange.push_back(cur); + else if (mob->IsNPC() && (mob->IsPet() || mob->IsTempPet()) && mob->IsPetOwnerNPC()) { + spreader_list.push_back(mob); } } // if the spreader is anything else(bot, pet, etc) then it should spread to everything but non client controlled npcs - else if (!spreader->IsNPC() && !cur->IsNPC()) { - TargetsInRange.push_back(cur); + else if (!spreader->IsNPC() && !mob->IsNPC()) { + spreader_list.push_back(mob); } - // if its a pet we need to determine appropriate targets(pet to client, pet to pet, pet to bot, etc) - else if (spreader->IsNPC() && (spreader->IsPet() || spreader->CastToNPC()->GetSwarmOwner()) && !spreader->GetOwner()->IsNPC()) { - if (!cur->IsNPC()) { - TargetsInRange.push_back(cur); + // if spreader is not an NPC, and Target is an NPC, then spread to non-NPC controlled pets + else if (!spreader->IsNPC() && mob->IsNPC() && (mob->IsPet() || mob->IsTempPet()) && !mob->IsPetOwnerNPC()) { + spreader_list.push_back(mob); + } + + // if spreader is a non-NPC controlled pet we need to determine appropriate targets(pet to client, pet to pet, pet to bot, etc) + else if (spreader->IsNPC() && (spreader->IsPet() || spreader->IsTempPet()) && !spreader->IsPetOwnerNPC()) { + //Spread to non-NPCs + if (!mob->IsNPC()) { + spreader_list.push_back(mob); } - else if (cur->IsNPC() && (cur->IsPet() || cur->CastToNPC()->GetSwarmOwner()) && !cur->GetOwner()->IsNPC()) { - TargetsInRange.push_back(cur); + //Spread to other non-NPC Pets + else if (mob->IsNPC() && (mob->IsPet() || mob->IsTempPet()) && !mob->IsPetOwnerNPC()) { + spreader_list.push_back(mob); } } } - ++it; } - if(TargetsInRange.empty()) - return nullptr; - - return TargetsInRange[zone->random.Int(0, TargetsInRange.size() - 1)]; + return spreader_list; } void EntityList::StopMobAI() diff --git a/zone/entity.h b/zone/entity.h index 20c5be842..b7f65978a 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -160,7 +160,6 @@ public: bool IsMobSpawnedByNpcTypeID(uint32 get_id); bool IsNPCSpawned(std::vector npc_ids); uint32 CountSpawnedNPCs(std::vector npc_ids); - Mob *GetTargetForVirus(Mob* spreader, int range); inline NPC *GetNPCByID(uint16 id) { auto it = npc_list.find(id); @@ -512,6 +511,7 @@ public: void GetDoorsList(std::list &d_list); void GetSpawnList(std::list &d_list); void GetTargetsForConeArea(Mob *start, float min_radius, float radius, float height, int pcnpc, std::list &m_list); + std::vector GetTargetsForVirusEffect(Mob *spreader, Mob *orginal_caster, int range, int pcnpc, int32 spell_id); inline const std::unordered_map &GetMobList() { return mob_list; } inline const std::unordered_map &GetNPCList() { return npc_list; } diff --git a/zone/mob.cpp b/zone/mob.cpp index c5d8d24c0..a138668eb 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -367,6 +367,7 @@ Mob::Mob( pet_regroup = false; _IsTempPet = false; pet_owner_client = false; + pet_owner_npc = false; pet_targetlock_id = 0; attacked_count = 0; @@ -405,10 +406,6 @@ Mob::Mob( roamer = false; rooted = false; charmed = false; - has_virus = false; - for (int i = 0; i < MAX_SPELL_TRIGGER * 2; i++) { - viral_spells[i] = 0; - } weaponstance.enabled = false; weaponstance.spellbonus_enabled = false; //Set when bonus is applied @@ -4682,28 +4679,6 @@ void Mob::DoGravityEffect() } } -void Mob::SpreadVirus(uint16 spell_id, uint16 casterID) -{ - int num_targs = spells[spell_id].viral_targets; - - Mob* caster = entity_list.GetMob(casterID); - Mob* target = nullptr; - // Only spread in zones without perm buffs - if(!zone->BuffTimersSuspended()) { - for(int i = 0; i < num_targs; i++) { - target = entity_list.GetTargetForVirus(this, spells[spell_id].viral_range); - if(target) { - // Only spreads to the uninfected - if(!target->FindBuff(spell_id)) { - if(caster) - caster->SpellOnTarget(spell_id, target); - - } - } - } - } -} - void Mob::AddNimbusEffect(int effectid) { SetNimbusEffect(effectid); diff --git a/zone/mob.h b/zone/mob.h index c2e49f757..2442dfb15 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -333,8 +333,8 @@ public: uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false, int level_override = -1); void SendBeginCast(uint16 spell_id, uint32 casttime); virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar, int reflect_effectiveness = 0, - bool use_resist_adjust = false, int16 resist_adjust = 0, bool isproc = false, int level_override = -1); - virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100, int level_override = -1, int reflect_effectiveness = 0); + bool use_resist_adjust = false, int16 resist_adjust = 0, bool isproc = false, int level_override = -1, int32 duration_override = 0); + virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100, int level_override = -1, int reflect_effectiveness = 0, int32 duration_override = 0); virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, EQ::spells::CastingSlot slot, bool isproc = false); virtual bool CheckFizzle(uint16 spell_id); @@ -407,7 +407,6 @@ public: inline void SetMGB(bool val) { has_MGB = val; } bool HasProjectIllusion() const { return has_ProjectIllusion ; } inline void SetProjectIllusion(bool val) { has_ProjectIllusion = val; } - void SpreadVirus(uint16 spell_id, uint16 casterID); bool IsNimbusEffectActive(uint32 nimbus_effect); void SetNimbusEffect(uint32 nimbus_effect); inline virtual uint32 GetNimbusEffect1() const { return nimbus_effect1; } @@ -861,6 +860,9 @@ public: void FocusProcLimitProcess(); bool ApplyFocusProcLimiter(int32 spell_id, int buffslot = -1); + void VirusEffectProcess(); + void SpreadVirusEffect(int32 spell_id, uint32 caster_id, int32 buff_tics_remaining); + void ModSkillDmgTaken(EQ::skills::SkillType skill_num, int value); int16 GetModSkillDmgTaken(const EQ::skills::SkillType skill_num); void ModVulnerability(uint8 resist, int16 value); @@ -929,6 +931,8 @@ public: bool HasPetAffinity() { if (aabonuses.GivePetGroupTarget || itembonuses.GivePetGroupTarget || spellbonuses.GivePetGroupTarget) return true; return false; } inline bool IsPetOwnerClient() const { return pet_owner_client; } inline void SetPetOwnerClient(bool value) { pet_owner_client = value; } + inline bool IsPetOwnerNPC() const { return pet_owner_npc; } + inline void SetPetOwnerNPC(bool value) { pet_owner_npc = value; } inline bool IsTempPet() const { return _IsTempPet; } inline void SetTempPet(bool value) { _IsTempPet = value; } inline bool IsHorse() { return is_horse; } @@ -1038,7 +1042,6 @@ public: inline const bool IsRoamer() const { return roamer; } inline const int GetWanderType() const { return wandertype; } inline const bool IsRooted() const { return rooted || permarooted; } - inline const bool HasVirus() const { return has_virus; } int GetSnaredAmount(); inline const bool IsPseudoRooted() const { return pseudo_rooted; } inline void SetPseudoRoot(bool prState) { pseudo_rooted = prState; } @@ -1122,7 +1125,7 @@ public: void SendItemAnimation(Mob *to, const EQ::ItemData *item, EQ::skills::SkillType skillInUse, float velocity = 4.0); inline int& GetNextIncHPEvent() { return nextinchpevent; } void SetNextIncHPEvent( int inchpevent ); - + inline bool DivineAura() const { return spellbonuses.DivineAura; } inline bool Sanctuary() const { return spellbonuses.Sanctuary; } @@ -1522,8 +1525,6 @@ protected: bool silenced; bool amnesiad; bool inWater; // Set to true or false by Water Detection code if enabled by rules - bool has_virus; // whether this mob has a viral spell on them - uint16 viral_spells[MAX_SPELL_TRIGGER*2]; // Stores the spell ids of the viruses on target and caster ids bool offhand; bool has_shieldequiped; bool has_twohandbluntequiped; @@ -1636,6 +1637,7 @@ protected: bool _IsTempPet; int16 count_TempPet; bool pet_owner_client; //Flags regular and pets as belonging to a client + bool pet_owner_npc; //Flags regular and pets as belonging to a npc uint32 pet_targetlock_id; glm::vec3 m_TargetRing; diff --git a/zone/npc.cpp b/zone/npc.cpp index d5c89d185..3b635ca57 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -928,19 +928,8 @@ bool NPC::Process() SendHPUpdate(); } - if(HasVirus()) { - if(viral_timer.Check()) { - viral_timer_counter++; - for(int i = 0; i < MAX_SPELL_TRIGGER*2; i+=2) { - if(viral_spells[i] && spells[viral_spells[i]].viral_timer > 0) { - if(viral_timer_counter % spells[viral_spells[i]].viral_timer == 0) { - SpreadVirus(viral_spells[i], viral_spells[i+1]); - } - } - } - } - if(viral_timer_counter > 999) - viral_timer_counter = 0; + if (viral_timer.Check()) { + VirusEffectProcess(); } if(spellbonuses.GravityEffect == 1) { @@ -2339,6 +2328,10 @@ void NPC::PetOnSpawn(NewSpawn_Struct* ns) strn0cpy(ns->spawn.lastName, tmp_lastname.c_str(), sizeof(ns->spawn.lastName)); } } + + if (swarmOwner->IsNPC()) { + SetPetOwnerNPC(true); + } } else if(GetOwnerID()) { @@ -2354,6 +2347,13 @@ void NPC::PetOnSpawn(NewSpawn_Struct* ns) if (tmp_lastname.size() < sizeof(ns->spawn.lastName)) strn0cpy(ns->spawn.lastName, tmp_lastname.c_str(), sizeof(ns->spawn.lastName)); } + else + { + if (entity_list.GetNPCByID(GetOwnerID())) + { + SetPetOwnerNPC(true); + } + } } } else diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 14385dff3..0de22519d 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -46,7 +46,7 @@ extern WorldServer worldserver; // the spell can still fail here, if the buff can't stack // in this case false will be returned, true otherwise -bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_override, int reflect_effectiveness) +bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_override, int reflect_effectiveness, int32 duration_override) { int caster_level, buffslot, effect, effect_value, i; EQ::ItemInstance *SummonedItem=nullptr; @@ -119,7 +119,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove } else { - buffslot = AddBuff(caster, spell_id); + buffslot = AddBuff(caster, spell_id, duration_override); } if(buffslot == -1) // stacking failure return false; @@ -168,7 +168,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove caster ? caster->GetLevel() : 0, buffslot ); - + if (IsClient()) { if (parse->EventSpell(EVENT_SPELL_EFFECT_CLIENT, nullptr, CastToClient(), spell_id, buf, 0) != 0) { CalcBonuses(); @@ -181,20 +181,12 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove } } - if(spells[spell_id].viral_targets > 0) { - if(!viral_timer.Enabled()) - viral_timer.Start(1000); + if(IsVirusSpell(spell_id)) { - has_virus = true; - for(int i = 0; i < MAX_SPELL_TRIGGER*2; i+=2) - { - if(!viral_spells[i]) - { - viral_spells[i] = spell_id; - viral_spells[i+1] = caster->GetID(); - break; - } + if (!viral_timer.Enabled()) { + viral_timer.Start(1000); } + buffs[buffslot].virus_spread_time = zone->random.Int(GetViralMinSpreadTime(spell_id), GetViralMaxSpreadTime(spell_id)); } @@ -1091,8 +1083,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove break; } /* - TODO: Parsing shows there is no level modifier. However, a consistent -2% modifer was - found on spell with value 950 (95% spells would have 7% failure rates). + TODO: Parsing shows there is no level modifier. However, a consistent -2% modifer was + found on spell with value 950 (95% spells would have 7% failure rates). Further investigation is needed. ~ Kayen */ int chance = spells[spell_id].base[i]; @@ -1121,7 +1113,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove caster->MessageString(Chat::SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name); break; } - + int chance = spells[spell_id].base[i]; int buff_count = GetMaxTotalSlots(); for(int slot = 0; slot < buff_count; slot++) { @@ -1473,7 +1465,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove spell.base[i], gender_id ); - + if (spell.max[i] > 0) { if (spell.base2[i] == 0) { SendIllusionPacket( @@ -1505,7 +1497,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove spell.max[i] ); } - SendAppearancePacket(AT_Size, race_size); + SendAppearancePacket(AT_Size, race_size); } for (int x = EQ::textures::textureBegin; x <= EQ::textures::LastTintableTexture; x++) { @@ -1549,9 +1541,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if (!CanMemoryBlurFromMez && IsEffectInSpell(spell_id, SE_Mez)) { break; } - + int wipechance = 0; - + if (caster) { wipechance = caster->GetMemoryBlurChance(effect_value); } @@ -3360,7 +3352,7 @@ int Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level, effect_value = CalcSpellEffectValue_formula(formula, base, max, caster_level, spell_id, ticsremaining); // this doesn't actually need to be a song to get mods, just the right skill - if (EQ::skills::IsBardInstrumentSkill(spells[spell_id].skill) + if (EQ::skills::IsBardInstrumentSkill(spells[spell_id].skill) && IsInstrumentModAppliedToSpellEffect(spell_id, spells[spell_id].effectid[effect_id])){ @@ -3770,7 +3762,7 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) switch (effect) { case SE_CurrentHP: { - + if (spells[buff.spellid].base2[i] && !PassCastRestriction(spells[buff.spellid].base2[i])) { break; } @@ -4086,23 +4078,6 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) LogSpells("Fading buff [{}] from slot [{}]", buffs[slot].spellid, slot); - if(spells[buffs[slot].spellid].viral_targets > 0) { - bool last_virus = true; - for(int i = 0; i < MAX_SPELL_TRIGGER*2; i+=2) - { - if(viral_spells[i] && viral_spells[i] != buffs[slot].spellid) - { - // If we have a virus that doesn't match this one then don't stop the viral timer - last_virus = false; - } - } - // This is the last virus on us so lets stop timer - if(last_virus) { - viral_timer.Disable(); - has_virus = false; - } - } - std::string buf = fmt::format( "{} {} {} {}", buffs[slot].casterid, @@ -7168,7 +7143,7 @@ bool Mob::PassCastRestriction(int value) Note: (ID 221 - 249) For effect seen in mage spell 'Shock of Many' which increases damage based on number of pets on targets hatelist. The way it is implemented works for how our ROF2 spell file handles the effect where each slot fires individually, while on live it only takes the highest - value. In the future the way check is done will need to be adjusted to check a defined range instead of just great than. + value. In the future the way check is done will need to be adjusted to check a defined range instead of just great than. */ if (value <= 0) { @@ -7202,8 +7177,8 @@ bool Mob::PassCastRestriction(int value) break; case IS_BODY_TYPE_MISC: - if ((GetBodyType() == BT_Humanoid) || (GetBodyType() == BT_Lycanthrope) || (GetBodyType() == BT_Giant) || - (GetBodyType() == BT_RaidGiant) || (GetBodyType() == BT_RaidColdain) || (GetBodyType() == BT_Animal)|| + if ((GetBodyType() == BT_Humanoid) || (GetBodyType() == BT_Lycanthrope) || (GetBodyType() == BT_Giant) || + (GetBodyType() == BT_RaidGiant) || (GetBodyType() == BT_RaidColdain) || (GetBodyType() == BT_Animal)|| (GetBodyType() == BT_Construct) || (GetBodyType() == BT_Dragon) || (GetBodyType() == BT_Insect)|| (GetBodyType() == BT_VeliousDragon) || (GetBodyType() == BT_Muramite) || (GetBodyType() == BT_Magical)) return true; @@ -7342,7 +7317,7 @@ bool Mob::PassCastRestriction(int value) if (IsHybridClass(GetClass())) return true; break; - + case IS_CLASS_WARRIOR: if (GetClass() == WARRIOR) return true; @@ -7449,7 +7424,7 @@ bool Mob::PassCastRestriction(int value) return true; break; - case FRENZIED_BURNOUT_NOT_ACTIVE: + case FRENZIED_BURNOUT_NOT_ACTIVE: if (!HasBuffWithSpellGroup(SPELLGROUP_FRENZIED_BURNOUT)) return true; break; @@ -7458,7 +7433,7 @@ bool Mob::PassCastRestriction(int value) if (GetHPRatio() > 75) return true; break; - + case IS_HP_LESS_THAN_20_PCT: if (GetHPRatio() <= 20) return true; @@ -7605,27 +7580,27 @@ bool Mob::PassCastRestriction(int value) if (GetHPRatio() > 25 && GetHPRatio() <= 35) return true; break; - + case IS_HP_BETWEEN_35_AND_45_PCT: if (GetHPRatio() > 35 && GetHPRatio() <= 45) return true; break; - + case IS_HP_BETWEEN_45_AND_55_PCT: if (GetHPRatio() > 45 && GetHPRatio() <= 55) return true; break; - + case IS_HP_BETWEEN_55_AND_65_PCT: if (GetHPRatio() > 55 && GetHPRatio() <= 65) return true; break; - + case IS_HP_BETWEEN_65_AND_75_PCT: if (GetHPRatio() > 65 && GetHPRatio() <= 75) return true; break; - + case IS_HP_BETWEEN_75_AND_85_PCT: if (GetHPRatio() > 75 && GetHPRatio() <= 85) return true; @@ -7635,7 +7610,7 @@ bool Mob::PassCastRestriction(int value) if (GetHPRatio() > 85 && GetHPRatio() <= 95) return true; break; - + case IS_HP_ABOVE_45_PCT: if (GetHPRatio() > 45) return true; @@ -7675,7 +7650,7 @@ bool Mob::PassCastRestriction(int value) if (GetBodyType() != BT_Plant) return true; break; - + case IS_NOT_CLIENT: if (!IsClient()) return true; @@ -7689,8 +7664,8 @@ bool Mob::PassCastRestriction(int value) case IS_LEVEL_ABOVE_42_AND_IS_CLIENT: if (IsClient() && GetLevel() > 42) return true; - break; - + break; + case IS_TREANT: if (GetRace() == RT_TREANT || GetRace() == RT_TREANT_2 || GetRace() == RT_TREANT_3) return true; @@ -7878,7 +7853,7 @@ bool Mob::PassCastRestriction(int value) } break; } - + case IS_CLIENT_AND_MALE_PLATE_USER: if (IsClient() && GetGender() == MALE && IsPlateClass(GetClass())) return true; @@ -7890,7 +7865,7 @@ bool Mob::PassCastRestriction(int value) break; case IS_CLIENT_AND_MALE_BEASTLORD_BERSERKER_MONK_RANGER_OR_ROGUE: - if (IsClient() && GetGender() == MALE && + if (IsClient() && GetGender() == MALE && (GetClass() == BEASTLORD || GetClass() == BERSERKER || GetClass() == MONK || GetClass() == RANGER || GetClass() == ROGUE)) return true; break; @@ -7967,7 +7942,7 @@ bool Mob::PassCastRestriction(int value) } break; } - + case IS_NOT_CLASS_BARD: if (GetClass() != BARD) return true; @@ -8012,7 +7987,7 @@ bool Mob::PassCastRestriction(int value) if (FindBuff(SPELL_INCENDIARY_OOZE_BUFF)) return true; break; - + //Not handled, just allow them to pass for now. case UNKNOWN_3: case HAS_CRYSTALLIZED_FLAME_BUFF: @@ -8595,7 +8570,7 @@ int Mob::GetMemoryBlurChance(int base_chance) */ int cha_mod = int(GetCHA() / 10); cha_mod = std::min(cha_mod, 15); - + int lvl_mod = 0; if (GetLevel() < 17) { lvl_mod = 100; @@ -8614,3 +8589,76 @@ int Mob::GetMemoryBlurChance(int base_chance) chance += chance * chance_mod / 100; return chance; } + +void Mob::VirusEffectProcess() +{ + /* + Virus Mechanics + To qualify as a virus effect buff, all of the following spell table need to be set. (At some point will correct names) + viral_targets = MIN_SPREAD_TIME + viral_timer = MAX_SPREAD_TIME + viral_range = SPREAD_RADIUS + Once a buff with a viral effect is applied, a 1000 ms timer will begin. + The time at which the virus will attempt to spread is determined by a random value between MIN_SPREAD_TIME and MAX_SPREAD_TIME + Each time the virus attempts to spread the next time interval will be chosen at random again. + If a spreader finds a target for viral buff, when the viral buff spreads the duration on the new target will be the time remaining on the spreaders buff. + Spreaders DOES NOT need LOS to spread. There is no max amount of targets the virus can spread to. + When the spreader no longer has any viral buffs the timer stops. + The current code supports spreading for both detrimental and beneficial spells. + */ + + // Only spread in zones without perm buffs + if (zone->BuffTimersSuspended()) { + viral_timer.Disable(); + return; + } + + bool stop_timer = true; + for (int buffs_i = 0; buffs_i < GetMaxTotalSlots(); ++buffs_i) + { + if (IsValidSpell(buffs[buffs_i].spellid) && IsVirusSpell(buffs[buffs_i].spellid)) + { + if (buffs[buffs_i].virus_spread_time > 0) { + buffs[buffs_i].virus_spread_time -= 1; + stop_timer = false; + } + + if (buffs[buffs_i].virus_spread_time <= 0) { + buffs[buffs_i].virus_spread_time = zone->random.Int(GetViralMinSpreadTime(buffs[buffs_i].spellid), GetViralMaxSpreadTime(buffs[buffs_i].spellid)); + SpreadVirusEffect(buffs[buffs_i].spellid, buffs[buffs_i].casterid, buffs[buffs_i].ticsremaining); + stop_timer = false; + } + } + } + + if (stop_timer) { + viral_timer.Disable(); + } +} + +void Mob::SpreadVirusEffect(int32 spell_id, uint32 caster_id, int32 buff_tics_remaining) +{ + Mob *caster = entity_list.GetMob(caster_id); + std::vector targets_in_range = entity_list.GetTargetsForVirusEffect( + this, + caster, + GetViralSpreadRange(spell_id), + spells[spell_id].pcnpc_only_flag, + spell_id + ); + + for (auto &mob: targets_in_range) { + if (!mob) { + continue; + } + + if (!mob->FindBuff(spell_id)) { + if (caster) { + if (buff_tics_remaining) { + //When virus is spread, the buff on new target is applied with the amount of time remaining on the spreaders buff. + caster->SpellOnTarget(spell_id, mob, 0, false, 0, false, -1, buff_tics_remaining); + } + } + } + } +} diff --git a/zone/spells.cpp b/zone/spells.cpp index bd9442646..f4d0a0647 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3423,6 +3423,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid buffs[emptyslot].RootBreakChance = 0; buffs[emptyslot].focusproclimit_time = 0; buffs[emptyslot].focusproclimit_procamt = 0; + buffs[emptyslot].virus_spread_time = 0; buffs[emptyslot].instrument_mod = caster ? caster->GetInstrumentMod(spell_id) : 10; if (level_override > 0 || buffs[emptyslot].numhits > 0) { @@ -3533,9 +3534,8 @@ int Mob::CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite) // break stuff // bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectiveness, bool use_resist_adjust, int16 resist_adjust, - bool isproc, int level_override) + bool isproc, int level_override, int32 duration_override) { - bool is_damage_or_lifetap_spell = IsDamageSpell(spell_id) || IsLifetapSpell(spell_id); // well we can't cast a spell on target without a target @@ -4078,7 +4078,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes } // cause the effects to the target - if(!spelltar->SpellEffect(this, spell_id, spell_effectiveness, level_override, reflect_effectiveness)) + if(!spelltar->SpellEffect(this, spell_id, spell_effectiveness, level_override, reflect_effectiveness, duration_override)) { // if SpellEffect returned false there's a problem applying the // spell. It's most likely a buff that can't stack. diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 4b33dcfad..c5f3ce418 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -3672,6 +3672,7 @@ void ZoneDatabase::LoadBuffs(Client *client) buffs[slot_id].RootBreakChance = 0; buffs[slot_id].focusproclimit_time = 0; buffs[slot_id].focusproclimit_procamt = 0; + buffs[slot_id].virus_spread_time = 0; buffs[slot_id].UpdateClient = false; buffs[slot_id].instrument_mod = instrument_mod; } From 987de17e93f7040ff08a3dd6faf5e0a4682a0c67 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 24 Oct 2021 19:38:28 -0400 Subject: [PATCH 294/624] [Spells] Rework for SPA 413 SE_FcBaseEffects and Bard updates (#1629) * baseline start * update1 * updates * base effect implemented for bard * instrument mod updates amplification amps itself * updates * updates * debug * base effect updates * baseeffects for spell focus updated * update skill attack baseeffects * focus will remain for quest functions * song cap mod added back in * remove debugs1 * fix cr * base effects functionalish * remove debug * Update client_mods.cpp * spdat instrumentmod * Update spell_effects.cpp * Update spdat.h * remove new instrument mod check split PR --- common/spdat.h | 10 ++--- zone/bonuses.cpp | 15 ++++--- zone/client_mods.cpp | 84 +++++++++++++++++++++++++++++------- zone/effects.cpp | 87 +++++++++++++++++-------------------- zone/mob.h | 5 ++- zone/special_attacks.cpp | 7 +-- zone/spell_effects.cpp | 92 +++++++++++++++++++++------------------- 7 files changed, 179 insertions(+), 121 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 7a078903a..3f3349186 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -815,7 +815,7 @@ typedef enum { #define SE_Hunger 115 // implemented - Song of Sustenance #define SE_CurseCounter 116 // implemented #define SE_MagicWeapon 117 // implemented - makes weapon magical -#define SE_Amplification 118 // implemented - Harmonize/Amplification (stacks with other singing mods) +#define SE_Amplification 118 // implemented, @Song, stackable singing mod, base: mod%, limit: none, max: none, Note: Can focus itself. #define SE_AttackSpeed3 119 // implemented #define SE_HealRate 120 // implemented - reduces healing by a % #define SE_ReverseDS 121 // implemented @@ -876,7 +876,7 @@ typedef enum { #define SE_DualWieldChance 176 // implemented #define SE_DoubleAttackChance 177 // implemented #define SE_MeleeLifetap 178 // implemented -#define SE_AllInstrumentMod 179 // implemented +#define SE_AllInstrumentMod 179 // implemented, @Song, set mod for ALL instrument/singing skills that will be used if higher then item mods, base: mod%, limit: none, max: none #define SE_ResistSpellChance 180 // implemented #define SE_ResistFearChance 181 // implemented #define SE_HundredHands 182 // implemented @@ -957,8 +957,8 @@ typedef enum { #define SE_PetDiscipline 257 // not implemented as bonus - /pet hold - official name is GivePetHold #define SE_TripleBackstab 258 // implemented[AA] - chance to perform a triple backstab #define SE_CombatStability 259 // implemented[AA] - damage mitigation -#define SE_AddSingingMod 260 // implemented[AA] - Instrument/Singing Mastery, base1 is the mod, base2 is the ItemType -#define SE_SongModCap 261 // implemented[AA] - Song Mod cap increase (no longer used on live) +#define SE_AddSingingMod 260 // implemented, @Song, set mod for specific instrument/singing skills that will be used if higher then item mods, base: mod%, limit: ItemType ID, max: none +#define SE_SongModCap 261 // implemented, @Song, raise max song modifier cap, base: amt, limit: none, max: none, Note: No longer used on live #define SE_RaiseStatCap 262 // implemented #define SE_TradeSkillMastery 263 // implemented - lets you raise more than one tradeskill above master. #define SE_HastenedAASkill 264 // implemented @@ -967,7 +967,7 @@ typedef enum { #define SE_AddPetCommand 267 // implemented - sets command base2 to base1 #define SE_ReduceTradeskillFail 268 // implemented - reduces chance to fail with given tradeskill by a percent chance #define SE_MaxBindWound 269 // implemented[AA] - Increase max HP you can bind wound. -#define SE_BardSongRange 270 // implemented[AA] - increase range of beneficial bard songs (Sionachie's Crescendo) +#define SE_BardSongRange 270 // implemented, @Song, increase range of beneficial bard songs, base: mod%, limit: none, max: none , Note: example Sionachie's Crescendo #define SE_BaseMovementSpeed 271 // implemented[AA] - mods basemove speed, doesn't stack with other move mods #define SE_CastingLevel2 272 // implemented #define SE_CriticalDoTChance 273 // implemented diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index e56de4daf..4a251b136 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1687,6 +1687,10 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) newbon->SpellDamageShield += base1; break; + case SE_Amplification: + newbon->Amplification += base1; + break; + // to do case SE_PetDiscipline: break; @@ -1795,18 +1799,19 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne if (focus) { if (WornType){ - if (RuleB(Spells, UseAdditiveFocusFromWornSlot)) + if (RuleB(Spells, UseAdditiveFocusFromWornSlot)) { new_bonus->FocusEffectsWorn[focus] += spells[spell_id].base[i]; + } } - - else + else { new_bonus->FocusEffects[focus] = static_cast(spells[spell_id].effectid[i]); - + } continue; } - if (WornType && (RuleI(Spells, AdditiveBonusWornType) == WornType)) + if (WornType && (RuleI(Spells, AdditiveBonusWornType) == WornType)) { AdditiveWornBonus = true; + } effectid = spells[spell_id].effectid[i]; effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, instrument_mod, nullptr, ticsremaining, casterId); diff --git a/zone/client_mods.cpp b/zone/client_mods.cpp index b415b6938..e124daf9e 100644 --- a/zone/client_mods.cpp +++ b/zone/client_mods.cpp @@ -1508,22 +1508,25 @@ int32 Client::CalcATK() return (ATK); } -uint32 Mob::GetInstrumentMod(uint16 spell_id) const +uint32 Mob::GetInstrumentMod(uint16 spell_id) { - if (GetClass() != BARD || spells[spell_id].IsDisciplineBuff) // Puretone is Singing but doesn't get any mod + if (GetClass() != BARD) { + //Other classes can get a base effects mod using SPA 413 + if (HasBaseEffectFocus()) { + return (10 + (GetFocusEffect(focusFcBaseEffects, spell_id) / 10));//TODO: change action->instrument mod to float to support < 10% focus values + } return 10; - + } + uint32 effectmod = 10; int effectmodcap = 0; - bool nocap = false; if (RuleB(Character, UseSpellFileSongCap)) { effectmodcap = spells[spell_id].songcap / 10; - // this looks a bit weird, but easiest way I could think to keep both systems working - if (effectmodcap == 0) - nocap = true; - else - effectmodcap += 10; - } else { + if (effectmodcap) { + effectmodcap += 10; //Actual calculated cap is 100 greater than songcap value. + } + } + else { effectmodcap = RuleI(Character, BaseInstrumentSoftCap); } // this should never use spell modifiers... @@ -1532,6 +1535,39 @@ uint32 Mob::GetInstrumentMod(uint16 spell_id) const // item mods are in 10ths of percent increases // clickies (Symphony of Battle) that have a song skill don't get AA bonus for some reason // but clickies that are songs (selo's on Composers Greaves) do get AA mod as well + + /*Mechanics: updated 10/19/21 ~Kayen + Bard Spell Effects + + Mod uses the highest bonus from either of these for each instrument + SPA 179 SE_AllInstrumentMod is used for instrument spellbonus.______Mod. This applies to ALL instrument mods (Puretones Discipline) + SPA 260 SE_AddSingingMod is used for instrument spellbonus.______Mod. This applies to indiviual instrument mods. (Instrument mastery AA) + -Example usage: From AA a value of 4 = 40% + + SPA 118 SE_Amplification is a stackable singing mod, on live it exists as both spell and AA bonus (stackable) + - Live Behavior: Amplifcation can be modified by singing mods and amplification itself, thus on the second cast of Amplification you will recieve + the mod from the first cast, this continues until you reach the song mod cap. + + SPA 261 SE_SongModCap raises song focus cap (No longer used on live) + SPA 270 SE_BardSongRange increase range of beneficial bard songs (Sionachie's Crescendo) + + SPA 413 SE_FcBaseEffects focus effect that replaced item instrument mods + + Issues 10-15-21: + Bonuses are not applied, unless song is stopped and restarted due to pulse keeping it continues. -> Need to recode songs to recast when duration ends. + + Formula Live Bards: + mod = (10 + (aabonus.____Mod [SPA 260 AA Instrument Mastery]) + (SE_FcBaseEffect[SPA 413])/10 + (spellbonus.______Mod [SPA 179 Puretone Disc]) + (Amplication [SPA 118])/10 + + TODO: Spell Table Fields that need to be implemented + Field 225 //float base_effects_focus_slope; // -- BASE_EFFECTS_FOCUS_SLOPE + Field 226 //float base_effects_focus_offset; // -- BASE_EFFECTS_FOCUS_OFFSET (35161 Ruaabri's Reckless Renewal -120) + Based on description possibly works as a way to quickly balance instrument mods to a song. + Using a standard slope formula: y = mx + b + modified_base_value = (base_effects_focus_slope x effectmod)(base_value) + (base_effects_focus_offset) + Will need to confirm on live before implementing. + */ + switch (spells[spell_id].skill) { case EQ::skills::SkillPercussionInstruments: if (itembonuses.percussionMod == 0 && spellbonuses.percussionMod == 0) @@ -1589,18 +1625,34 @@ uint32 Mob::GetInstrumentMod(uint16 spell_id) const else effectmod = spellbonuses.singingMod; if (IsBardSong(spell_id)) - effectmod += aabonuses.singingMod + spellbonuses.Amplification; + effectmod += aabonuses.singingMod + (spellbonuses.Amplification + itembonuses.Amplification + aabonuses.Amplification); //SPA 118 SE_Amplification break; default: effectmod = 10; return effectmod; } - if (!RuleB(Character, UseSpellFileSongCap)) - effectmodcap += aabonuses.songModCap + spellbonuses.songModCap + itembonuses.songModCap; - if (effectmod < 10) + + if (HasBaseEffectFocus()) { + effectmod += (GetFocusEffect(focusFcBaseEffects, spell_id) / 10); + } + + if (effectmod < 10) { effectmod = 10; - if (!nocap && effectmod > effectmodcap) // if the cap is calculated to be 0 using new rules, no cap. - effectmod = effectmodcap; + } + + if (effectmodcap) { + + effectmodcap += aabonuses.songModCap + spellbonuses.songModCap + itembonuses.songModCap; //SPA 261 SE_SongModCap (not used on live) + + //Incase a negative modifier is used. + if (effectmodcap <= 0) { + effectmodcap = 10; + } + + if (effectmod > effectmodcap) { // if the cap is calculated to be 0 using new rules, no cap. + effectmod = effectmodcap; + } + } LogSpells("[{}]::GetInstrumentMod() spell=[{}] mod=[{}] modcap=[{}]\n", GetName(), spell_id, effectmod, effectmodcap); diff --git a/zone/effects.cpp b/zone/effects.cpp index 4180d774c..601018148 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -49,11 +49,9 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { value += value*CastToNPC()->GetSpellFocusDMG()/100; bool Critical = false; - int32 value_BaseEffect = 0; + int32 base_value = value; int chance = 0; - value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100); - // Need to scale HT damage differently after level 40! It no longer scales by the constant value in the spell file. It scales differently, instead of 10 more damage per level, it does 30 more damage per level. So we multiply the level minus 40 times 20 if they are over level 40. if ((spell_id == SPELL_HARM_TOUCH || spell_id == SPELL_HARM_TOUCH2 || spell_id == SPELL_IMP_HARM_TOUCH ) && GetLevel() > 40) value -= (GetLevel() - 40) * 20; @@ -97,16 +95,16 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { if (Critical){ - value = value_BaseEffect*ratio/100; + value = base_value*ratio/100; - value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100; - value += value_BaseEffect*GetFocusEffect(focusImprovedDamage2, spell_id)/100; + value += base_value*GetFocusEffect(focusImprovedDamage, spell_id)/100; + value += base_value*GetFocusEffect(focusImprovedDamage2, spell_id)/100; - value += int(value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100; - value += int(value_BaseEffect*GetFocusEffect(focusFcAmplifyMod, spell_id) / 100)*ratio / 100; + value += int(base_value*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100; + value += int(base_value*GetFocusEffect(focusFcAmplifyMod, spell_id) / 100)*ratio / 100; if (target) { - value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100; + value += int(base_value*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100; value -= target->GetFcDamageAmtIncoming(this, spell_id); } @@ -117,10 +115,10 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { value -= GetFocusEffect(focusFcAmplifyAmt, spell_id); if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg) - value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value_BaseEffect)*ratio / 100; + value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value)*ratio / 100; else if(!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) - value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value_BaseEffect)*ratio/100; + value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value)*ratio/100; else if (IsNPC() && CastToNPC()->GetSpellScale()) value = int(static_cast(value) * CastToNPC()->GetSpellScale() / 100.0f); @@ -136,16 +134,16 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { } } //Non Crtical Hit Calculation pathway - value = value_BaseEffect; + value = base_value; - value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100; - value += value_BaseEffect*GetFocusEffect(focusImprovedDamage2, spell_id)/100; + value += base_value*GetFocusEffect(focusImprovedDamage, spell_id)/100; + value += base_value*GetFocusEffect(focusImprovedDamage2, spell_id)/100; - value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100; - value += value_BaseEffect*GetFocusEffect(focusFcAmplifyMod, spell_id)/100; + value += base_value*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100; + value += base_value*GetFocusEffect(focusFcAmplifyMod, spell_id)/100; if (target) { - value += value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100; + value += base_value*target->GetVulnerability(this, spell_id, 0)/100; value -= target->GetFcDamageAmtIncoming(this, spell_id); } @@ -156,10 +154,10 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { value -= GetFocusEffect(focusFcAmplifyAmt, spell_id); if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg) - value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value_BaseEffect); + value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value); else if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) - value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value_BaseEffect); + value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value); if (IsNPC() && CastToNPC()->GetSpellScale()) value = int(static_cast(value) * CastToNPC()->GetSpellScale() / 100.0f); @@ -199,10 +197,11 @@ int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) { if (target == nullptr) return value; - if (IsNPC()) - value += value*CastToNPC()->GetSpellFocusDMG()/100; + if (IsNPC()) { + value += value * CastToNPC()->GetSpellFocusDMG() / 100; + } - int32 value_BaseEffect = 0; + int32 base_value = value; int32 extra_dmg = 0; int16 chance = 0; chance += itembonuses.CriticalDoTChance + spellbonuses.CriticalDoTChance + aabonuses.CriticalDoTChance; @@ -213,17 +212,15 @@ int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) { if (spells[spell_id].override_crit_chance > 0 && chance > spells[spell_id].override_crit_chance) chance = spells[spell_id].override_crit_chance; - value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100); - if (chance > 0 && (zone->random.Roll(chance))) { int32 ratio = 200; ratio += itembonuses.DotCritDmgIncrease + spellbonuses.DotCritDmgIncrease + aabonuses.DotCritDmgIncrease; - value = value_BaseEffect*ratio/100; - value += int(value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100)*ratio/100; - value += int(value_BaseEffect*GetFocusEffect(focusImprovedDamage2, spell_id)/100)*ratio/100; - value += int(value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100; - value += int(value_BaseEffect*GetFocusEffect(focusFcAmplifyMod, spell_id) / 100)*ratio/100; - value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100; + value = base_value*ratio/100; + value += int(base_value*GetFocusEffect(focusImprovedDamage, spell_id)/100)*ratio/100; + value += int(base_value*GetFocusEffect(focusImprovedDamage2, spell_id)/100)*ratio/100; + value += int(base_value*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100; + value += int(base_value*GetFocusEffect(focusFcAmplifyMod, spell_id) / 100)*ratio/100; + value += int(base_value*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100; extra_dmg = target->GetFcDamageAmtIncoming(this, spell_id) + int(GetFocusEffect(focusFcDamageAmtCrit, spell_id)*ratio/100) + GetFocusEffect(focusFcDamageAmt, spell_id) + @@ -240,12 +237,12 @@ int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) { } else { - value = value_BaseEffect; - value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100; - value += value_BaseEffect*GetFocusEffect(focusImprovedDamage2, spell_id)/100; - value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100; - value += value_BaseEffect*GetFocusEffect(focusFcAmplifyMod, spell_id)/100; - value += value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100; + value = base_value; + value += base_value*GetFocusEffect(focusImprovedDamage, spell_id)/100; + value += base_value*GetFocusEffect(focusImprovedDamage2, spell_id)/100; + value += base_value*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100; + value += base_value*GetFocusEffect(focusFcAmplifyMod, spell_id)/100; + value += base_value*target->GetVulnerability(this, spell_id, 0)/100; extra_dmg = target->GetFcDamageAmtIncoming(this, spell_id) + GetFocusEffect(focusFcDamageAmtCrit, spell_id) + GetFocusEffect(focusFcDamageAmt, spell_id) + @@ -301,7 +298,7 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { value += value * CastToNPC()->GetSpellFocusHeal() / 100; } - int32 value_BaseEffect = 0; + int32 base_value = value; int16 critical_chance = 0; int8 critical_modifier = 1; @@ -331,28 +328,24 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { } } - value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id) / 100); - - value = value_BaseEffect; - if (GetClass() == CLERIC) { - value += int(value_BaseEffect*RuleI(Spells, ClericInnateHealFocus) / 100); //confirmed on live parsing clerics get an innate 5 pct heal focus + value += int(base_value*RuleI(Spells, ClericInnateHealFocus) / 100); //confirmed on live parsing clerics get an innate 5 pct heal focus } - value += int(value_BaseEffect*GetFocusEffect(focusImprovedHeal, spell_id) / 100); - value += int(value_BaseEffect*GetFocusEffect(focusFcAmplifyMod, spell_id) / 100); + value += int(base_value*GetFocusEffect(focusImprovedHeal, spell_id) / 100); + value += int(base_value*GetFocusEffect(focusFcAmplifyMod, spell_id) / 100); // Instant Heals if (spells[spell_id].buffduration < 1) { if (target) { - value += int(value_BaseEffect * target->GetFocusEffect(focusFcHealPctIncoming, spell_id)/100); //SPA 393 Add before critical - value += int(value_BaseEffect * target->GetFocusEffect(focusFcHealPctCritIncoming, spell_id)/100); //SPA 395 Add before critical (?) + value += int(base_value * target->GetFocusEffect(focusFcHealPctIncoming, spell_id)/100); //SPA 393 Add before critical + value += int(base_value * target->GetFocusEffect(focusFcHealPctCritIncoming, spell_id)/100); //SPA 395 Add before critical (?) } value += GetFocusEffect(focusFcHealAmtCrit, spell_id); //SPA 396 Add before critical if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) { - value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value_BaseEffect); //Item Heal Amt Add before critical + value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, base_value); //Item Heal Amt Add before critical } if (target) { diff --git a/zone/mob.h b/zone/mob.h index 2442dfb15..fefeafff9 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -834,7 +834,6 @@ public: int16 GetPositionalDmgAmt(Mob* defender); inline bool CanBlockSpell() const { return(spellbonuses.FocusEffects[focusBlockNextSpell]); } bool DoHPToManaCovert(uint16 mana_cost = 0); - int32 ApplySpellEffectiveness(int16 spell_id, int32 value, bool IsBard = false, uint16 caster_id=0); int8 GetDecayEffectValue(uint16 spell_id, uint16 spelleffect); int32 GetExtraSpellAmt(uint16 spell_id, int32 extra_spell_amt, int32 base_spell_dmg); void MeleeLifeTap(int32 damage); @@ -851,6 +850,7 @@ public: int GetFocusRandomEffectivenessValue(int focus_base, int focus_base2, bool best_focus = 0); int GetHealRate() const { return itembonuses.HealRate + spellbonuses.HealRate + aabonuses.HealRate; } int GetMemoryBlurChance(int base_chance); + inline bool HasBaseEffectFocus() const { return (spellbonuses.FocusEffects[focusFcBaseEffects] || aabonuses.FocusEffects[focusFcBaseEffects] || itembonuses.FocusEffects[focusFcBaseEffects]); } bool TryDoubleMeleeRoundEffect(); bool GetUseDoubleMeleeRoundDmgBonus() const { return use_double_melee_round_dmg_bonus; } @@ -1113,7 +1113,8 @@ public: virtual int32 CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc = false); virtual int32 CheckHealAggroAmount(uint16 spell_id, Mob *target, uint32 heal_possible = 0); - uint32 GetInstrumentMod(uint16 spell_id) const; + //uint32 GetInstrumentMod(uint16 spell_id) const; + uint32 GetInstrumentMod(uint16 spell_id); int CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level = 1, uint32 instrument_mod = 10, Mob *caster = nullptr, int ticsremaining = 0,uint16 casterid=0); int CalcSpellEffectValue_formula(int formula, int base, int max, int caster_level, uint16 spell_id, int ticsremaining = 0); virtual int CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, int caster_level2, Mob* caster1 = nullptr, Mob* caster2 = nullptr, int buffslot = -1); diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index a7eeb023d..6e4b4b9a7 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -838,9 +838,9 @@ void Mob::DoArcheryAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, co return; } - // unsure when this should happen - if (focus) // From FcBaseEffects + if (focus) { WDmg += WDmg * focus / 100; + } if (WDmg > 0 || ADmg > 0) { if (WDmg < 0) @@ -2158,8 +2158,9 @@ void Mob::DoMeleeSkillAttackDmg(Mob *other, uint16 weapon_damage, EQ::skills::Sk hate = weapon_damage; if (weapon_damage > 0) { - if (focus) // From FcBaseEffects + if (focus) { weapon_damage += weapon_damage * focus / 100; + } if (skillinuse == EQ::skills::SkillBash) { if (IsClient()) { diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 0de22519d..cefcd2123 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -197,6 +197,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove // if buff slot, use instrument mod there, otherwise calc it uint32 instrument_mod = buffslot > -1 ? buffs[buffslot].instrument_mod : caster ? caster->GetInstrumentMod(spell_id) : 10; + // iterate through the effects in the spell for (i = 0; i < EFFECT_COUNT; i++) { @@ -1325,9 +1326,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Melee Absorb Rune: %+i", effect_value); #endif - if (caster) - effect_value = caster->ApplySpellEffectiveness(spell_id, effect_value); - buffs[buffslot].melee_rune = effect_value; break; } @@ -2356,22 +2354,20 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove ???? = spells[spell_id].max[i] - MOST of the effects have this value. *Max is lower value then Weapon base, possibly min hit vs Weapon Damage range ie. MakeRandInt(max,base) */ - int16 focus = 0; int ReuseTime = spells[spell_id].recast_time + spells[spell_id].recovery_time; - if (!caster) + if (!caster) { break; - - focus = caster->GetFocusEffect(focusFcBaseEffects, spell_id); - + } + switch(spells[spell_id].skill) { case EQ::skills::SkillThrowing: - caster->DoThrowingAttackDmg(this, nullptr, nullptr, spells[spell_id].base[i],spells[spell_id].base2[i], focus, ReuseTime); + caster->DoThrowingAttackDmg(this, nullptr, nullptr, effect_value,spells[spell_id].base2[i], 0, ReuseTime); break; case EQ::skills::SkillArchery: - caster->DoArcheryAttackDmg(this, nullptr, nullptr, spells[spell_id].base[i],spells[spell_id].base2[i],focus, ReuseTime); + caster->DoArcheryAttackDmg(this, nullptr, nullptr, effect_value,spells[spell_id].base2[i], 0, ReuseTime); break; default: - caster->DoMeleeSkillAttackDmg(this, spells[spell_id].base[i], spells[spell_id].skill, spells[spell_id].base2[i], focus, false, ReuseTime); + caster->DoMeleeSkillAttackDmg(this, effect_value, spells[spell_id].skill, spells[spell_id].base2[i], 0, false, ReuseTime); break; } break; @@ -3335,9 +3331,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove } int Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level, uint32 instrument_mod, Mob *caster, - int ticsremaining, uint16 caster_id) + int ticsremaining, uint16 caster_id) { - int formula, base, max, effect_value; + int formula, base, max, effect_value, oval; if (!IsValidSpell(spell_id) || effect_id < 0 || effect_id >= EFFECT_COUNT) return 0; @@ -3355,16 +3351,49 @@ int Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level, if (EQ::skills::IsBardInstrumentSkill(spells[spell_id].skill) && IsInstrumentModAppliedToSpellEffect(spell_id, spells[spell_id].effectid[effect_id])){ + oval = effect_value; + effect_value = effect_value * instrument_mod / 10; - int oval = effect_value; - int mod = ApplySpellEffectiveness(spell_id, instrument_mod, true, caster_id); - effect_value = effect_value * mod / 10; LogSpells("Effect value [{}] altered with bard modifier of [{}] to yeild [{}]", - oval, mod, effect_value); + oval, instrument_mod, effect_value); + } + /* + SPA 413 SE_FcBaseEffects, modifies base value of a spell effect after formula calcultion, but before other focuses. + This is applied to non-Bards in Mob::GetInstrumentMod + Like bard modifiers, this is sent in the action_struct using action->instrument_mod (which is a base effect modifier) + + Issue: value sent with action->instrument_mod needs to be 10 or higher. Therefore lowest possible percent chance would be 11 (calculated to 10%) + there are modern spells that use less than 10% but we send as a uint where lowest value has to be 10, where it should be a float for current clients. + Though not ideal, at the moment for spells that are instant effects, the action packet doesn't matter and we will calculate the actual percent here correctly. + Logic here is, caster_id is only sent from ApplySpellBonuses. Thus if it is a buff a long as the base effects is set to over 10% and at +10% intervals + it will focus the base value correctly. + + */ + if (GetClass() != BARD) { + + if (caster_id && instrument_mod > 10) { + //This is checked from Mob::ApplySpellBonuses, applied to buffs that receive bonuses. See above, must be in 10% intervals to work. + oval = effect_value; + effect_value = effect_value * instrument_mod / 10; + + LogSpells("Bonus Effect value [{}] altered with base effects modifier of [{}] to yeild [{}]", + oval, instrument_mod, effect_value); + } + else if (!caster_id) { + //This is checked from Mob::SpellEffects and applied to instant spells and runes. + if (caster && caster->HasBaseEffectFocus()) { + oval = effect_value; + int mod = caster->GetFocusEffect(focusFcBaseEffects, spell_id); + effect_value += effect_value * mod / 100; + + LogSpells("Instant Effect value [{}] altered with base effects modifier of [{}] to yeild [{}]", + oval, mod, effect_value); + } + } } effect_value = mod_effect_value(effect_value, spell_id, spells[spell_id].effectid[effect_id], caster, caster_id); - + return effect_value; } @@ -3505,7 +3534,8 @@ snare has both of them negative, yet their range should work the same: break; case 119: // confirmed 2/6/04 - result = ubase + (caster_level / 8); break; + result = ubase + (caster_level / 8); + break; case 120: { int ticdif = CalcBuffDuration_formula(caster_level, spells[spell_id].buffdurationformula, spells[spell_id].buffduration) - std::max((ticsremaining - 1), 0); @@ -6994,30 +7024,6 @@ int32 Mob::GetFocusIncoming(focusType type, int effect, Mob *caster, uint32 spel return value; } -int32 Mob::ApplySpellEffectiveness(int16 spell_id, int32 value, bool IsBard, uint16 caster_id) { - - // 9-17-12: This is likely causing crashes, disabled till can resolve. - if (IsBard) - return value; - - Mob* caster = this; - - if (caster_id && caster_id != GetID())//Make sure we are checking the casters focus - caster = entity_list.GetMob(caster_id); - - if (!caster) - return value; - - int16 focus = caster->GetFocusEffect(focusFcBaseEffects, spell_id); - - if (IsBard) - value += focus; - else - value += value*focus/100; - - return value; -} - bool Mob::PassLimitClass(uint32 Classes_, uint16 Class_) { //The class value for SE_LimitClass is +1 to its equivelent value in item dbase From ef5124d7564511a6585df4635712cc29e8251341 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 24 Oct 2021 21:53:29 -0500 Subject: [PATCH 295/624] [Shared Tasks] World Reload Task Data on #task reloadall (#1641) --- world/zoneserver.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 03b5e8dd2..191b42396 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -40,6 +40,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "expedition_message.h" #include "shared_task_world_messaging.h" #include "../common/shared_tasks.h" +#include "shared_task_manager.h" extern ClientList client_list; extern GroupLFPList LFPGroupList; @@ -50,6 +51,8 @@ extern volatile bool UCSServerAvailable_; extern AdventureManager adventure_manager; extern UCSConnection UCSLink; extern QueryServConnection QSLink; +extern SharedTaskManager shared_task_manager; + void CatchSignal(int sig_num); ZoneServer::ZoneServer(std::shared_ptr connection, EQ::Net::ConsoleServer *console) @@ -1262,13 +1265,20 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { case ServerOP_DepopPlayerCorpse: case ServerOP_ReloadTitles: case ServerOP_SpawnStatusChange: - case ServerOP_ReloadTasks: case ServerOP_ReloadWorld: case ServerOP_UpdateSpawn: { zoneserver_list.SendPacket(pack); break; } + case ServerOP_ReloadTasks: + { + // world needs to update its copy of task data as well + shared_task_manager.LoadTaskData(); + + zoneserver_list.SendPacket(pack); + break; + } case ServerOP_ChangeSharedMem: { std::string hotfix_name = std::string((char*)pack->pBuffer); From fb66afd5652101a63bd88dcadd6190d294b313d7 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 26 Oct 2021 21:36:10 -0400 Subject: [PATCH 296/624] [Spells] Implemented SPA 511 SE_Ff_FocusTimerMin (#1645) * update for SPA 511 * remove debugs, AA implemented * update * format update * rename function renamed function only check for buffs value > 0, don't need to check for AA's which are negative ID's * var rename update var name to better represent its function. --- common/spdat.cpp | 2 + common/spdat.h | 6 +- zone/client_process.cpp | 3 - zone/common.h | 2 - zone/mob.cpp | 6 +- zone/mob.h | 10 ++- zone/mob_ai.cpp | 3 - zone/spell_effects.cpp | 180 ++++++++++++++++++---------------------- zone/spells.cpp | 2 - zone/zonedb.cpp | 2 - 10 files changed, 99 insertions(+), 117 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 432b903b5..719af4069 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1255,6 +1255,7 @@ bool IsEffectIgnoredInStacking(int spa) case SE_Ff_ReuseTimeMax: case SE_Ff_Value_Min: case SE_Ff_Value_Max: + case SE_Ff_FocusTimerMin: return true; default: return false; @@ -1296,6 +1297,7 @@ bool IsFocusLimit(int spa) case SE_Ff_ReuseTimeMax: case SE_Ff_Value_Min: case SE_Ff_Value_Max: + case SE_Ff_FocusTimerMin: return true; default: return false; diff --git a/common/spdat.h b/common/spdat.h index 3f3349186..b4535cf1d 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -180,7 +180,7 @@ #define MaxLimitInclude 16 //Number(x 0.5) of focus Limiters that have inclusive checks used when calcing focus effects #define MAX_SKILL_PROCS 4 //Number of spells to check skill procs from. (This is arbitrary) [Single spell can have multiple proc checks] #define MAX_SYMPATHETIC_PROCS 10 // Number of sympathetic procs a client can have (This is arbitrary) - +#define MAX_FOCUS_PROC_LIMIT_TIMERS 20 //Number of proc limiting timers that can be going at same time (This is arbitrary) const int Z_AGGRO=10; @@ -1208,8 +1208,8 @@ typedef enum { #define SE_Fc_Amplify_Amt 508 // implemented, @Fc, On Caster, damage-heal-dot mod flat amt, base: amt #define SE_Health_Transfer 509 // implemented - exchange health for damage or healing on a target. ie Lifeburn/Act of Valor #define SE_Fc_ResistIncoming 510 // implemented, @Fc, On Target, resist modifier, base: amt -//#define SE_Ff_FocusTimerMin 511 // -#define SE_Proc_Timer_Modifier 512 // implemented - spell trigger limiter used currently with SPA 481, ie. limit to 1 proc every 1.5 seconds (base=1 base2=1500). +#define SE_Ff_FocusTimerMin 511 // implemented, @Ff, sets a recast time until focus can be used again, base: 1, limit: time ms, Note: ie. limit to 1 trigger every 1.5 seconds +#define SE_Proc_Timer_Modifier 512 // not implemented - limits procs per amount of a time based on timer value (ie limit to 1 proc every 55 seconds) //#define SE_Mana_Max_Percent 513 // //#define SE_Endurance_Max_Percent 514 // #define SE_AC_Avoidance_Max_Percent 515 // implemented - stackable avoidance modifier diff --git a/zone/client_process.cpp b/zone/client_process.cpp index b7aad858b..ea11764bf 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -509,9 +509,6 @@ bool Client::Process() { } } - if (focus_proc_limit_timer.Check() && !dead) - FocusProcLimitProcess(); - if (client_state == CLIENT_KICKED) { Save(); OnDisconnect(true); diff --git a/zone/common.h b/zone/common.h index 8678aec64..82b17470e 100644 --- a/zone/common.h +++ b/zone/common.h @@ -328,8 +328,6 @@ struct Buffs_Struct { int32 ExtraDIChance; int16 RootBreakChance; //Not saved to dbase uint32 instrument_mod; - int16 focusproclimit_time; //timer to limit number of procs from focus effects - int16 focusproclimit_procamt; //amount of procs that can be cast before timer limiter is set int32 virus_spread_time; //time till next attempted viral spread bool persistant_buff; bool client; //True if the caster is a client diff --git a/zone/mob.cpp b/zone/mob.cpp index a138668eb..a60a9f563 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -103,7 +103,6 @@ Mob::Mob( ranged_timer(2000), tic_timer(6000), mana_timer(2000), - focus_proc_limit_timer(250), spellend_timer(0), rewind_timer(30000), bindwound_timer(10000), @@ -350,6 +349,11 @@ Mob::Mob( ProjectileAtk[i].speed_mod = 0.0f; } + for (int i = 0; i < MAX_FOCUS_PROC_LIMIT_TIMERS; i++) { + focusproclimit_spellid[i] = 0; + focusproclimit_timer[i].Disable(); + } + memset(&itembonuses, 0, sizeof(StatBonuses)); memset(&spellbonuses, 0, sizeof(StatBonuses)); memset(&aabonuses, 0, sizeof(StatBonuses)); diff --git a/zone/mob.h b/zone/mob.h index fefeafff9..72ccb8447 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -857,8 +857,9 @@ public: inline void SetUseDoubleMeleeRoundDmgBonus(bool val) { use_double_melee_round_dmg_bonus = val; } void CastSpellOnLand(Mob* caster, int32 spell_id); - void FocusProcLimitProcess(); - bool ApplyFocusProcLimiter(int32 spell_id, int buffslot = -1); + + bool IsFocusProcLimitTimerActive(int32 focus_spell_id); + void SetFocusProcLimitTimer(int32 focus_spell_id, uint32 focus_reuse_time); void VirusEffectProcess(); void SpreadVirusEffect(int32 spell_id, uint32 caster_id, int32 buff_tics_remaining); @@ -1463,7 +1464,9 @@ protected: int16 slow_mitigation; // Allows for a slow mitigation (100 = 100%, 50% = 50%) Timer tic_timer; Timer mana_timer; - Timer focus_proc_limit_timer; + + Timer focusproclimit_timer[MAX_FOCUS_PROC_LIMIT_TIMERS]; //SPA 511 + int32 focusproclimit_spellid[MAX_FOCUS_PROC_LIMIT_TIMERS]; //SPA 511 Timer shield_timer; uint32 m_shield_target_id; @@ -1560,7 +1563,6 @@ protected: Timer bardsong_timer; Timer gravity_timer; Timer viral_timer; - uint8 viral_timer_counter; // MobAI stuff eStandingPetOrder pStandingPetOrder; diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index af74f3510..8e7b2bcd5 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1125,9 +1125,6 @@ void Mob::AI_Process() { ProjectileAttack(); - if (focus_proc_limit_timer.Check()) - FocusProcLimitProcess(); - if (shield_timer.Check()) { ShieldAbilityFinish(); } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index cefcd2123..23cc70991 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2992,11 +2992,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove break; } - case SE_Proc_Timer_Modifier:{ - buffs[buffslot].focusproclimit_procamt = spells[spell_id].base[i]; //Set max amount of procs before lockout timer - break; - } - case SE_PetShield: { if (IsPet()) { Mob* petowner = GetOwner(); @@ -3300,6 +3295,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_Skill_Base_Damage_Mod: case SE_Worn_Endurance_Regen_Cap: case SE_Buy_AA_Rank: + case SE_Ff_FocusTimerMin: { break; } @@ -4568,8 +4564,9 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) int32 base1 = 0; int32 base2 = 0; uint32 slot = 0; - + int index_id = -1; + uint32 focus_reuse_time = 0; bool LimitFailure = false; bool LimitInclude[MaxLimitInclude] = {false}; @@ -4917,6 +4914,15 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) } break; + case SE_Ff_FocusTimerMin: + if (IsFocusProcLimitTimerActive(-rank.id)) { + LimitFailure = true; + } + else { + focus_reuse_time = base2; + } + break; + /* These are not applicable to AA's because there is never a 'caster' of the 'buff' with the focus effect. case SE_Ff_Same_Caster: @@ -5217,6 +5223,10 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) return 0; } + if (focus_reuse_time) { + SetFocusProcLimitTimer(-rank.id, focus_reuse_time); + } + return (value * lvlModifier / 100); } @@ -5247,6 +5257,7 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo int lvldiff = 0; uint32 Caston_spell_id = 0; int index_id = -1; + uint32 focus_reuse_time = 0; //If this is set and all limits pass, start timer at end of script. bool LimitInclude[MaxLimitInclude] = {false}; /* Certain limits require only one of several Include conditions to be true. Determined by limits being negative or positive @@ -5579,7 +5590,16 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo } break; - // handle effects + case SE_Ff_FocusTimerMin: + if (IsFocusProcLimitTimerActive(focus_spell.id)) { + return 0; + } + else { + focus_reuse_time = focus_spell.base2[i]; + } + break; + + // handle effects case SE_ImprovedDamage: if (type == focusImprovedDamage) { value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); @@ -5882,6 +5902,10 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo } } + if (focus_reuse_time) { + SetFocusProcLimitTimer(focus_spell.id, focus_reuse_time); + } + return (value * lvlModifier / 100); } @@ -8293,102 +8317,22 @@ void Mob::CastSpellOnLand(Mob* caster, int32 spell_id) if (IsValidSpell(trigger_spell_id) && (trigger_spell_id != spell_id)) { - //Step 3: Check if SE_Proc_Time_Modifier is present and if so apply it. - if (ApplyFocusProcLimiter(buffs[i].spellid, i)) { - //Step 4: Cast spells - if (IsBeneficialSpell(trigger_spell_id)) { - SpellFinished(trigger_spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[trigger_spell_id].ResistDiff); - } - else { - Mob* current_target = GetTarget(); - //For now don't let players cast detrimental effects on themselves if they are targeting themselves. Need to confirm behavior. - if (current_target && current_target->GetID() != GetID()) - SpellFinished(trigger_spell_id, current_target, EQ::spells::CastingSlot::Item, 0, -1, spells[trigger_spell_id].ResistDiff); - } + //Step 3: Cast spells + if (IsBeneficialSpell(trigger_spell_id)) { + SpellFinished(trigger_spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[trigger_spell_id].ResistDiff); } - - if (i >= 0) - CheckNumHitsRemaining(NumHit::MatchingSpells, i); - } - } - } - } -} - -bool Mob::ApplyFocusProcLimiter(int32 spell_id, int buffslot) -{ - if (buffslot < 0) - return false; - - //Do not allow spell cast if timer is active. - if (buffs[buffslot].focusproclimit_time > 0) - return false; - - /* - SE_Proc_Timer_Modifier - base1= amount of total procs allowed until lock out timer is triggered, should be set to at least 1 in any spell for the effect to function. - base2= lock out timer, which prevents any more procs set in ms 1500 = 1.5 seconds - This system allows easy scaling for multiple different buffs with same effects each having seperate active individual timer checks. Ie. - */ - - if (IsValidSpell(spell_id)) { - - for (int i = 0; i < EFFECT_COUNT; i++) { - - //Step 1: Find which slot the spell effect is in. - if (spells[spell_id].effectid[i] == SE_Proc_Timer_Modifier) { - - //Step 2: Check if you still have procs left to trigger, and if so reduce available procs - if (buffs[buffslot].focusproclimit_procamt > 0) { - --buffs[buffslot].focusproclimit_procamt; //Reduce total amount of triggers possible. - } - - //Step 3: If you used all the procs in the time frame then set proc amount back to max - if (buffs[buffslot].focusproclimit_procamt == 0 && spells[spell_id].base[i] > 0) { - buffs[buffslot].focusproclimit_procamt = spells[spell_id].base[i];//reset to max - - //Step 4: Check if timer exists on this spell, and then set it, and activiate global timer if not active - if (buffs[buffslot].focusproclimit_time ==0 && spells[spell_id].base2[i] > 0) { - buffs[buffslot].focusproclimit_time = spells[spell_id].base2[i];//set time - - //Step 5: If timer is not already running, then start it. - if (!focus_proc_limit_timer.Enabled()) { - focus_proc_limit_timer.Start(250); - } - - return true; + else { + Mob* current_target = GetTarget(); + //For now don't let players cast detrimental effects on themselves if they are targeting themselves. Need to confirm behavior. + if (current_target && current_target->GetID() != GetID()) + SpellFinished(trigger_spell_id, current_target, EQ::spells::CastingSlot::Item, 0, -1, spells[trigger_spell_id].ResistDiff); } } + if (i >= 0) + CheckNumHitsRemaining(NumHit::MatchingSpells, i); } } } - return true; -} - -void Mob::FocusProcLimitProcess() -{ - /* - Fast 250 ms uinversal timer for checking Focus effects that have a proc rate limiter set in actual time. - */ - bool stop_timer = true; - int buff_count = GetMaxTotalSlots(); - for (int buffs_i = 0; buffs_i < buff_count; ++buffs_i) - { - if (IsValidSpell(buffs[buffs_i].spellid)) - { - if (buffs[buffs_i].focusproclimit_time > 0) { - buffs[buffs_i].focusproclimit_time -= 250; - stop_timer = false; - } - - if (buffs[buffs_i].focusproclimit_time < 0) - buffs[buffs_i].focusproclimit_time = 0; - } - } - - if (stop_timer) { - focus_proc_limit_timer.Disable(); - } } void Mob::CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster) @@ -8668,3 +8612,45 @@ void Mob::SpreadVirusEffect(int32 spell_id, uint32 caster_id, int32 buff_tics_re } } } + +bool Mob::IsFocusProcLimitTimerActive(int32 focus_spell_id) { + /* + Used with SPA SE_Ff_FocusTimerMin to limit how often a focus effect can be applied. + Ie. Can only have a spell trigger once every 15 seconds, or to be more creative can only + have the fire spells received a very high special focused once every 30 seconds. + Note, this stores timers for both spell, item and AA related focuses For AA the focus_spell_id + is saved as the the negative value of the rank.id (to avoid conflicting with spell_ids) + */ + for (int i = 0; i < MAX_FOCUS_PROC_LIMIT_TIMERS; i++) { + if (focusproclimit_spellid[i] == focus_spell_id) { + if (focusproclimit_timer[i].Enabled()) { + if (focusproclimit_timer[i].GetRemainingTime() > 0) { + return true; + } + else { + focusproclimit_timer[i].Disable(); + focusproclimit_spellid[i] = 0; + } + } + } + } + return false; +} + +void Mob::SetFocusProcLimitTimer(int32 focus_spell_id, uint32 focus_reuse_time) { + + bool is_set = false; + + for (int i = 0; i < MAX_FOCUS_PROC_LIMIT_TIMERS; i++) { + if (!focusproclimit_spellid[i] && !is_set) { + focusproclimit_spellid[i] = focus_spell_id; + focusproclimit_timer[i].SetTimer(focus_reuse_time); + is_set = true; + } + //Remove old temporary focus if was from a buff you no longer have. + else if (focusproclimit_spellid[i] > 0 && !FindBuff(focus_spell_id)) { + focusproclimit_spellid[i] = 0; + focusproclimit_timer[i].Disable(); + } + } +} diff --git a/zone/spells.cpp b/zone/spells.cpp index f4d0a0647..2ca332100 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3421,8 +3421,6 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid buffs[emptyslot].dot_rune = 0; buffs[emptyslot].ExtraDIChance = 0; buffs[emptyslot].RootBreakChance = 0; - buffs[emptyslot].focusproclimit_time = 0; - buffs[emptyslot].focusproclimit_procamt = 0; buffs[emptyslot].virus_spread_time = 0; buffs[emptyslot].instrument_mod = caster ? caster->GetInstrumentMod(spell_id) : 10; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index c5f3ce418..bc463b99b 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -3670,8 +3670,6 @@ void ZoneDatabase::LoadBuffs(Client *client) buffs[slot_id].caston_z = caston_z; buffs[slot_id].ExtraDIChance = ExtraDIChance; buffs[slot_id].RootBreakChance = 0; - buffs[slot_id].focusproclimit_time = 0; - buffs[slot_id].focusproclimit_procamt = 0; buffs[slot_id].virus_spread_time = 0; buffs[slot_id].UpdateClient = false; buffs[slot_id].instrument_mod = instrument_mod; From 6e5bf4b9418905d757d4077613c1e1dbc2e5b80f Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Wed, 27 Oct 2021 00:01:37 -0500 Subject: [PATCH 297/624] [Saylinks] Multiple saylinks in brackets (#1643) * Saylink edge case where multiple saylinks show up within a bracket * Update partial --- common/say_link.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/common/say_link.cpp b/common/say_link.cpp index c6347d87c..aa4c4e3ac 100644 --- a/common/say_link.cpp +++ b/common/say_link.cpp @@ -24,7 +24,7 @@ #include "item_instance.h" #include "item_data.h" #include "../zone/zonedb.h" - +#include bool EQ::saylink::DegenerateLinkBody(SayLinkBody_Struct &say_link_body_struct, const std::string &say_link_body) { @@ -350,7 +350,7 @@ std::string EQ::SayLinkEngine::InjectSaylinksIfNotExist(const char *message) std::vector saylinks = {}; int saylink_length = 50; std::string saylink_separator = "\u0012"; - std::string saylink_partial = "000"; + std::string saylink_partial = "00000"; LogSaylinkDetail("new_message pre pass 1 [{}]", new_message); @@ -375,6 +375,8 @@ std::string EQ::SayLinkEngine::InjectSaylinksIfNotExist(const char *message) LogSaylinkDetail("new_message post pass 1 [{}]", new_message); + LogSaylinkDetail("saylink separator count [{}]", std::count(new_message.begin(), new_message.end(), '\u0012')); + // loop through brackets until none exist if (new_message.find('[') != std::string::npos) { for (auto &b: split_string(new_message, "[")) { @@ -388,6 +390,12 @@ std::string EQ::SayLinkEngine::InjectSaylinksIfNotExist(const char *message) continue; } + // skip where multiple saylinks are within brackets + if (bracket_message.find(saylink_separator) != std::string::npos && + std::count(bracket_message.begin(), bracket_message.end(), '\u0012') > 1) { + continue; + } + // if non empty bracket contents if (!bracket_message.empty()) { LogSaylinkDetail("Found bracket_message [{}]", bracket_message); From 7230714cbc2017a513f24568c0849ba757e7afff Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Wed, 27 Oct 2021 20:45:27 -0500 Subject: [PATCH 298/624] [Spells/Disciplines] Bulk Train / Scribe (#1640) * Bulk scribe spells * Add bulk disc training * Remove bulk from non bulk method * PR adjustments --- zone/client.cpp | 50 ++++++++++++++++++++++- zone/client.h | 11 +++-- zone/client_packet.cpp | 6 +-- zone/command.cpp | 51 +++++++++++++---------- zone/effects.cpp | 17 ++++---- zone/questmgr.cpp | 6 ++- zone/spells.cpp | 91 ++++++++++++++++++++++++------------------ 7 files changed, 157 insertions(+), 75 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 15a7e870d..ed29a3981 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -63,6 +63,10 @@ extern volatile bool RunLoops; #include "../common/expedition_lockout_timer.h" #include "cheat_manager.h" +#include "../common/repositories/character_spells_repository.h" +#include "../common/repositories/character_disciplines_repository.h" + + extern QueryServ* QServ; extern EntityList entity_list; extern Zone* zone; @@ -5587,7 +5591,7 @@ void Client::UpdateLDoNWinLoss(uint32 theme_id, bool win, bool remove) { break; default: return; - } + } database.UpdateAdventureStatsEntry(CharacterID(), theme_id, win, remove); } @@ -10550,7 +10554,7 @@ void Client::ReadBookByName(std::string book_name, uint8 book_type) out->window = 0xFF; out->type = book_type; out->invslot = 0; - + memcpy(out->booktext, book_text.c_str(), length); if (book_language > 0 && book_language < MAX_PP_LANGUAGE) { @@ -10673,3 +10677,45 @@ void Client::SummonBaggedItems(uint32 bag_item_id, const std::vector character_spells = {}; + + for (int index = 0; index < EQ::spells::SPELLBOOK_SIZE; index++) { + if (IsValidSpell(m_pp.spell_book[index])) { + auto spell = CharacterSpellsRepository::NewEntity(); + spell.id = CharacterID(); + spell.slot_id = index; + spell.spell_id = m_pp.spell_book[index]; + character_spells.emplace_back(spell); + } + } + + CharacterSpellsRepository::DeleteWhere(database, fmt::format("id = {}", CharacterID())); + + if (!character_spells.empty()) { + CharacterSpellsRepository::InsertMany(database, character_spells); + } +} + +void Client::SaveDisciplines() +{ + std::vector character_discs = {}; + + for (int index = 0; index < MAX_PP_DISCIPLINES; index++) { + if (IsValidSpell(m_pp.disciplines.values[index])) { + auto discipline = CharacterDisciplinesRepository::NewEntity(); + discipline.id = CharacterID(); + discipline.slot_id = index; + discipline.disc_id = m_pp.disciplines.values[index]; + character_discs.emplace_back(discipline); + } + } + + CharacterDisciplinesRepository::DeleteWhere(database, fmt::format("id = {}", CharacterID())); + + if (!character_discs.empty()) { + CharacterDisciplinesRepository::InsertMany(database, character_discs); + } +} diff --git a/zone/client.h b/zone/client.h index 550be9620..41fc994ee 100644 --- a/zone/client.h +++ b/zone/client.h @@ -799,10 +799,15 @@ public: std::vector GetMemmedSpells(); std::vector GetScribeableSpells(uint8 min_level = 1, uint8 max_level = 0); std::vector GetScribedSpells(); - void ScribeSpell(uint16 spell_id, int slot, bool update_client = true); - void UnscribeSpell(int slot, bool update_client = true); + // defer save used when bulk saving + void ScribeSpell(uint16 spell_id, int slot, bool update_client = true, bool defer_save = false); + void SaveSpells(); + void SaveDisciplines(); + + // defer save used when bulk saving + void UnscribeSpell(int slot, bool update_client = true, bool defer_save = false); void UnscribeSpellAll(bool update_client = true); - void UntrainDisc(int slot, bool update_client = true); + void UntrainDisc(int slot, bool update_client = true, bool defer_save = false); void UntrainDiscAll(bool update_client = true); void UntrainDiscBySpellID(uint16 spell_id, bool update_client = true); bool SpellGlobalCheck(uint16 spell_id, uint32 char_id); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index f10e23a73..d04db5849 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -2048,7 +2048,7 @@ void Client::Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app) } } - + } } else if (aps->Type == DiscordMerchant) { if (GetPVPPoints() < item_cost) { @@ -14791,8 +14791,8 @@ void Client::Handle_OP_Translocate(const EQApplicationPacket *app) zone->GetZoneID() == PendingTranslocateData.zone_id && zone->GetInstanceID() == PendingTranslocateData.instance_id ); - - if (parse->EventSpell(EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE, nullptr, this, spell_id, "", 0) == 0) { + + if (parse->EventSpell(EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE, nullptr, this, spell_id, "", 0) == 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 diff --git a/zone/command.cpp b/zone/command.cpp index 723e96333..c48bbd685 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -7928,7 +7928,7 @@ void command_scribespells(Client *c, const Seperator *sep) } if (!IsDiscipline(spell_id_) && !t->HasSpellScribed(spell_id)) { // isn't a discipline & we don't already have it scribed - t->ScribeSpell(spell_id_, book_slot); + t->ScribeSpell(spell_id_, book_slot, true, true); ++count; } @@ -7939,14 +7939,18 @@ void command_scribespells(Client *c, const Seperator *sep) } if (count > 0) { - t->Message(Chat::White, "Successfully scribed %i spells.", count); - if (t != c) - c->Message(Chat::White, "Successfully scribed %i spells for %s.", count, t->GetName()); + t->Message(Chat::White, "Successfully scribed %i spells.", count); + if (t != c) { + c->Message(Chat::White, "Successfully scribed %i spells for %s.", count, t->GetName()); + } + + t->SaveSpells(); } else { t->Message(Chat::White, "No spells scribed."); - if (t != c) - c->Message(Chat::White, "No spells scribed for %s.", t->GetName()); + if (t != c) { + c->Message(Chat::White, "No spells scribed for %s.", t->GetName()); + } } } @@ -8058,6 +8062,7 @@ void command_untraindiscs(Client *c, const Seperator *sep) { t = c->GetTarget()->CastToClient(); t->UntrainDiscAll(); + t->Message(Chat::Yellow, "All disciplines removed."); } void command_wpinfo(Client *c, const Seperator *sep) @@ -9275,7 +9280,7 @@ void command_npcedit(Client *c, const Seperator *sep) } if (strcasecmp(sep->arg[1], "gender") == 0) { - auto gender_id = atoi(sep->arg[2]); + auto gender_id = atoi(sep->arg[2]); c->Message(Chat::Yellow, fmt::format("NPC ID {} is now a {} ({}).", npc_id, gender_id, GetGenderName(gender_id)).c_str()); std::string query = fmt::format("UPDATE npc_types SET gender = {} WHERE id = {}", gender_id, npc_id); content_db.QueryDatabase(query); @@ -9461,7 +9466,7 @@ void command_npcedit(Client *c, const Seperator *sep) std::string query = fmt::format("UPDATE npc_types SET ammo_idfile = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); content_db.QueryDatabase(query); return; - } + } if (strcasecmp(sep->arg[1], "weapon") == 0) { c->Message(Chat::Yellow, fmt::format("NPC ID {} will have Model {} set to their Primary and Model {} set to their Secondary on repop.", npc_id, atoi(sep->arg[2]), atoi(sep->arg[3])).c_str()); @@ -9679,7 +9684,7 @@ void command_npcedit(Client *c, const Seperator *sep) content_db.QueryDatabase(query); return; } - + if (strcasecmp(sep->arg[1], "accuracy") == 0) { c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Accuracy.", npc_id, atoi(sep->arg[2])).c_str()); std::string query = fmt::format("UPDATE npc_types SET accuracy = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); @@ -9749,7 +9754,7 @@ void command_npcedit(Client *c, const Seperator *sep) content_db.QueryDatabase(query); return; } - + if (strcasecmp(sep->arg[1], "armtexture") == 0) { c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Arm Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); std::string query = fmt::format("UPDATE npc_types SET armtexture = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); @@ -9934,7 +9939,7 @@ void command_npcedit(Client *c, const Seperator *sep) animation = 1; animation_name = "Sitting"; } else if(strcasecmp(sep->arg[2], "crouch") == 0 || atoi(sep->arg[2]) == 2) { // Crouch - animation = 2; + animation = 2; animation_name = "Crouching"; } else if(strcasecmp(sep->arg[2], "dead") == 0 || atoi(sep->arg[2]) == 3) { // Dead animation = 3; @@ -10999,7 +11004,6 @@ void command_traindisc(Client *c, const Seperator *sep) } else if (t->GetPP().disciplines.values[r] == 0) { t->GetPP().disciplines.values[r] = spell_id_; - database.SaveCharacterDisc(t->CharacterID(), r, spell_id_); change = true; t->Message(Chat::White, "You have learned a new discipline!"); ++count; // success counter @@ -11011,17 +11015,22 @@ void command_traindisc(Client *c, const Seperator *sep) } } - if (change) + if (change) { t->SendDisciplineUpdate(); + t->SaveDisciplines(); + } if (count > 0) { - t->Message(Chat::White, "Successfully trained %u disciplines.", count); - if (t != c) - c->Message(Chat::White, "Successfully trained %u disciplines for %s.", count, t->GetName()); - } else { + t->Message(Chat::White, "Successfully trained %u disciplines.", count); + if (t != c) { + c->Message(Chat::White, "Successfully trained %u disciplines for %s.", count, t->GetName()); + } + } + else { t->Message(Chat::White, "No disciplines trained."); - if (t != c) - c->Message(Chat::White, "No disciplines trained for %s.", t->GetName()); + if (t != c) { + c->Message(Chat::White, "No disciplines trained for %s.", t->GetName()); + } } } @@ -14880,7 +14889,7 @@ void command_dye(Client *c, const Seperator *sep) c->Message(Chat::White, "Command Syntax: #dye help | #dye [slot] [red] [green] [blue] [use_tint]"); return; } - + uint8 slot = 0; uint8 red = 255; uint8 green = 255; @@ -14902,7 +14911,7 @@ void command_dye(Client *c, const Seperator *sep) std::vector slot_messages; c->Message(Chat::White, "Command Syntax: #dye help | #dye [slot] [red] [green] [blue] [use_tint]"); c->Message(Chat::White, "Red, Green, and Blue go from 0 to 255."); - + for (const auto& slot : dye_slots) { slot_messages.push_back(fmt::format("({}) {}", slot_id, slot)); slot_id++; diff --git a/zone/effects.cpp b/zone/effects.cpp index 601018148..67efa4fe1 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -180,7 +180,7 @@ int32 Mob::GetActReflectedSpellDamage(int32 spell_id, int32 value, int effective value = int(static_cast(value) * CastToNPC()->GetSpellScale() / 100.0f); } } - + int32 base_spell_dmg = value; value = value * effectiveness / 100; @@ -343,7 +343,7 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { } value += GetFocusEffect(focusFcHealAmtCrit, spell_id); //SPA 396 Add before critical - + if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) { value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, base_value); //Item Heal Amt Add before critical } @@ -351,7 +351,7 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { if (target) { value += value * target->GetHealRate() / 100; //SPA 120 modifies value after Focus Applied but before critical } - + /* Apply critical hit modifier */ @@ -363,7 +363,7 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { if (target) { value += target->GetFocusEffect(focusFcHealAmtIncoming, spell_id); //SPA 394 Add after critical } - + if (IsNPC() && CastToNPC()->GetHealScale()) { value = int(static_cast(value) * CastToNPC()->GetHealScale() / 100.0f); } @@ -619,13 +619,14 @@ bool Client::MemorizeSpellFromItem(uint32 item_id) { return false; } - for(int index = 0; index < EQ::spells::SPELLBOOK_SIZE; index++) { + for (int index = 0; index < EQ::spells::SPELLBOOK_SIZE; index++) { if (!HasSpellScribed(spell_id)) { auto next_slot = GetNextAvailableSpellBookSlot(); if (next_slot != -1) { ScribeSpell(spell_id, next_slot); return true; - } else { + } + else { Message( Chat::Red, "Unable to scribe spell %s (%i) to spellbook: no more spell book slots available.", @@ -635,12 +636,14 @@ bool Client::MemorizeSpellFromItem(uint32 item_id) { SummonItem(item_id); return false; } - } else { + } + else { Message(Chat::Red, "You already know this spell."); SummonItem(item_id); return false; } } + Message(Chat::Red, "You have learned too many spells and can learn no more."); return false; } diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 89b47aea8..54a2524ab 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1130,7 +1130,8 @@ uint16 QuestManager::scribespells(uint8 max_level, uint8 min_level) { if (initiator->HasSpellScribed(spell_id)) continue; - initiator->ScribeSpell(spell_id, book_slot); + // defer saving per spell and bulk save at the end + initiator->ScribeSpell(spell_id, book_slot, true, true); book_slot = initiator->GetNextAvailableSpellBookSlot(book_slot); spells_learned++; } @@ -1139,6 +1140,9 @@ uint16 QuestManager::scribespells(uint8 max_level, uint8 min_level) { if (spells_learned > 0) { std::string spell_message = (spells_learned == 1 ? "a new spell" : fmt::format("{} new spells", spells_learned)); initiator->Message(Chat::White, fmt::format("You have learned {}!", spell_message).c_str()); + + // bulk insert spells + initiator->SaveSpells(); } return spells_learned; } diff --git a/zone/spells.cpp b/zone/spells.cpp index 2ca332100..9efb8736b 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -445,7 +445,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, ) { cast_failed = false; } - } else if (spell_target->IsRaidGrouped()) { + } else if (spell_target->IsRaidGrouped()) { Raid *target_raid = spell_target->GetRaid(); Raid *my_raid = GetRaid(); if ( @@ -3874,15 +3874,15 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes } /* Reflect - base= % Chance to Reflect - Limit= Resist Modifier (+Value for decrease chance to resist) + base= % Chance to Reflect + Limit= Resist Modifier (+Value for decrease chance to resist) Max= % of base spell damage (this is the base before any formula or focus is applied) On live any type of detrimental spell can be reflected as long as the Reflectable spell field is set, this includes AOE. The 'caster' of the reflected spell is owner of the reflect effect. Caster's focus effects are NOT applied to reflected spell. - + reflect_effectiveness is applied to damage spells, a value of 100 is no change to base damage. Other values change by percent. (50=50% of damage) we this variable to both check if a spell being applied is from a reflection and for the damage modifier. - + There are a few spells in database that are not detrimental that have Reflectable field set, however from testing, they do not actually reflect. */ if(spells[spell_id].reflectable && !reflect_effectiveness && spelltar && this != spelltar && IsDetrimentalSpell(spell_id) && @@ -4059,7 +4059,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes } } } - + entity_list.AddHealAggro( spelltar, this, CheckHealAggroAmount(spell_id, spelltar, (spelltar->GetMaxHP() - spelltar->GetHP()))); @@ -5314,42 +5314,49 @@ int Client::MemmedCount() { } -void Client::ScribeSpell(uint16 spell_id, int slot, bool update_client) +void Client::ScribeSpell(uint16 spell_id, int slot, bool update_client, bool defer_save) { - if(slot >= EQ::spells::SPELLBOOK_SIZE || slot < 0) + if (slot >= EQ::spells::SPELLBOOK_SIZE || slot < 0) { return; + } - if(update_client) - { - if(m_pp.spell_book[slot] != 0xFFFFFFFF) - UnscribeSpell(slot, update_client); + if (update_client) { + if (m_pp.spell_book[slot] != 0xFFFFFFFF) { + UnscribeSpell(slot, update_client, defer_save); + } } m_pp.spell_book[slot] = spell_id; - database.SaveCharacterSpell(this->CharacterID(), spell_id, slot); + + // defer save if we're bulk saving elsewhere + if (!defer_save) { + database.SaveCharacterSpell(this->CharacterID(), spell_id, slot); + } LogSpells("Spell [{}] scribed into spell book slot [{}]", spell_id, slot); - if(update_client) - { + if (update_client) { MemorizeSpell(slot, spell_id, memSpellScribing); } } -void Client::UnscribeSpell(int slot, bool update_client) +void Client::UnscribeSpell(int slot, bool update_client, bool defer_save) { - if(slot >= EQ::spells::SPELLBOOK_SIZE || slot < 0) + if (slot >= EQ::spells::SPELLBOOK_SIZE || slot < 0) { return; + } LogSpells("Spell [{}] erased from spell book slot [{}]", m_pp.spell_book[slot], slot); m_pp.spell_book[slot] = 0xFFFFFFFF; - database.DeleteCharacterSpell(this->CharacterID(), m_pp.spell_book[slot], slot); - if(update_client && slot < EQ::spells::DynamicLookup(ClientVersion(), GetGM())->SpellbookSize) - { - auto outapp = new EQApplicationPacket(OP_DeleteSpell, sizeof(DeleteSpell_Struct)); - DeleteSpell_Struct* del = (DeleteSpell_Struct*)outapp->pBuffer; + if (!defer_save) { + database.DeleteCharacterSpell(this->CharacterID(), m_pp.spell_book[slot], slot); + } + + if (update_client && slot < EQ::spells::DynamicLookup(ClientVersion(), GetGM())->SpellbookSize) { + auto outapp = new EQApplicationPacket(OP_DeleteSpell, sizeof(DeleteSpell_Struct)); + DeleteSpell_Struct *del = (DeleteSpell_Struct *) outapp->pBuffer; del->spell_slot = slot; - del->success = 1; + del->success = 1; QueuePacket(outapp); safe_delete(outapp); } @@ -5357,37 +5364,44 @@ void Client::UnscribeSpell(int slot, bool update_client) void Client::UnscribeSpellAll(bool update_client) { - for(int i = 0; i < EQ::spells::SPELLBOOK_SIZE; i++) - { - if(m_pp.spell_book[i] != 0xFFFFFFFF) - UnscribeSpell(i, update_client); + for (int i = 0; i < EQ::spells::SPELLBOOK_SIZE; i++) { + if (m_pp.spell_book[i] != 0xFFFFFFFF) { + UnscribeSpell(i, update_client, true); + } } + + // bulk save at end (this will only delete) + SaveSpells(); } -void Client::UntrainDisc(int slot, bool update_client) +void Client::UntrainDisc(int slot, bool update_client, bool defer_save) { - if(slot >= MAX_PP_DISCIPLINES || slot < 0) + if (slot >= MAX_PP_DISCIPLINES || slot < 0) { return; + } LogSpells("Discipline [{}] untrained from slot [{}]", m_pp.disciplines.values[slot], slot); m_pp.disciplines.values[slot] = 0; - database.DeleteCharacterDisc(this->CharacterID(), slot); - if(update_client) - { + if (!defer_save) { + database.DeleteCharacterDisc(this->CharacterID(), slot); + } + + if (update_client) { SendDisciplineUpdate(); } } void Client::UntrainDiscAll(bool update_client) { - int i; - - for(i = 0; i < MAX_PP_DISCIPLINES; i++) - { - if(m_pp.disciplines.values[i] != 0) - UntrainDisc(i, update_client); + for (int i = 0; i < MAX_PP_DISCIPLINES; i++) { + if (m_pp.disciplines.values[i] != 0) { + UntrainDisc(i, update_client, true); + } } + + // bulk delete / save + SaveDisciplines(); } void Client::UntrainDiscBySpellID(uint16 spell_id, bool update_client) @@ -6244,3 +6258,4 @@ bool Client::IsLinkedSpellReuseTimerReady(uint32 timer_id) } + From d36d11653a892939554983e0c7266f2d29a92b00 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 27 Oct 2021 23:42:31 -0400 Subject: [PATCH 299/624] Fix issue with new summmon method putting players OOB (#1649) The FindClosestZ was finding the Z above them ... lets try just not doing that for now :) --- zone/waypoints.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index 72bdf14d5..2c8338e53 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -947,10 +947,6 @@ glm::vec4 Mob::TryMoveAlong(const glm::vec4 &start, float distance, float angle) new_pos.z += GetZOffset(); if (zone->HasMap()) { - auto new_z = zone->zonemap->FindClosestZ(new_pos, nullptr); - if (new_z != BEST_Z_INVALID) - new_pos.z = new_z; - if (zone->zonemap->LineIntersectsZone(start, new_pos, 0.0f, &tmp_pos)) new_pos = tmp_pos; } From 5738958a2a6b12ef98ff10a4cdd86caba9d2d4ff Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Thu, 28 Oct 2021 14:43:40 -0400 Subject: [PATCH 300/624] Fix issue with droplimit code (#1650) --- zone/loottables.cpp | 88 ++++++++++++++++++++++++++++----------------- 1 file changed, 56 insertions(+), 32 deletions(-) diff --git a/zone/loottables.cpp b/zone/loottables.cpp index 4bcfc81e2..5c3259502 100644 --- a/zone/loottables.cpp +++ b/zone/loottables.cpp @@ -128,6 +128,7 @@ void ZoneDatabase::AddLootDropToNPC(NPC *npc, uint32 lootdrop_id, ItemList *item return; } + // if this lootdrop is droplimit=0 and mindrop 0, scan list once and return if (droplimit == 0 && mindrop == 0) { for (uint32 i = 0; i < loot_drop->NumEntries; ++i) { int charges = loot_drop->Entries[i].multiplier; @@ -168,6 +169,11 @@ void ZoneDatabase::AddLootDropToNPC(NPC *npc, uint32 lootdrop_id, ItemList *item return; } + // This will pick one item per iteration until mindrop. + // Don't let the compare against chance fool you. + // The roll isn't 0-100, its 0-total and it picks the item, we're just + // looping to find the lucky item, descremening otherwise. This is ok, + // items with chance 60 are 6 times more likely than items chance 10. for (int i = 0; i < mindrop; ++i) { float roll = (float) zone->random.Real(0.0, roll_t); for (uint32 j = 0; j < loot_drop->NumEntries; ++j) { @@ -208,45 +214,63 @@ void ZoneDatabase::AddLootDropToNPC(NPC *npc, uint32 lootdrop_id, ItemList *item } } - for (int i = mindrop; i < droplimit; ++i) { - float roll = (float) zone->random.Real(0.0, roll_t); - for (uint32 j = 0; j < loot_drop->NumEntries; ++j) { - const EQ::ItemData *db_item = GetItem(loot_drop->Entries[j].item_id); - if (db_item) { - // if it doesn't meet the requirements do nothing - if (!npc->MeetsLootDropLevelRequirements(loot_drop->Entries[j])) - continue; + // Now that mindrop has been established see if we get any more based + // on item odds. Pick a random entry to start at so no one entry is favored. + // Look at all items until we hit droplimit or look at all items. - if (roll < loot_drop->Entries[j].chance) { - npc->AddLootDrop( - db_item, - item_list, - loot_drop->Entries[j] - ); + if (droplimit <= mindrop) { + return; + } - int charges = (int) loot_drop->Entries[i].multiplier; - charges = EQ::ClampLower(charges, 1); + int start_index = zone->random.Int(0,loot_drop->NumEntries-1); + int j = start_index; + int dropped = mindrop; - for (int k = 1; k < charges; ++k) { - float c_roll = (float) zone->random.Real(0.0, 100.0); - if (c_roll <= loot_drop->Entries[i].chance) { - npc->AddLootDrop( - db_item, - item_list, - loot_drop->Entries[i] - ); - } + do { + + LogLootDetail("DropLimit Starting at [{}] out of [{}]", j, + loot_drop->NumEntries); + + const EQ::ItemData *db_item = GetItem(loot_drop->Entries[j].item_id); + if (db_item && + npc->MeetsLootDropLevelRequirements(loot_drop->Entries[j])) { + + float iroll = (float) zone->random.Real(0.0, 100.0); + + LogLootDetail("Rolled [{}] Needed [{}]", iroll, loot_drop->Entries[j].chance); + // If this item succeeds the chance roll + if (iroll < loot_drop->Entries[j].chance) { + + ++dropped; + LogLootDetail("Dropping item [{}]", loot_drop->Entries[j].item_id); + npc->AddLootDrop( + db_item, + item_list, + loot_drop->Entries[j] + ); + + int charges = (int) loot_drop->Entries[j].multiplier; + charges = EQ::ClampLower(charges, 1); + + for (int k = 1; k < charges; ++k) { + float c_roll = (float) zone->random.Real(0.0, 100.0); + if (c_roll <= loot_drop->Entries[j].chance) { + npc->AddLootDrop( + db_item, + item_list, + loot_drop->Entries[j] + ); } - - j = loot_drop->NumEntries; - break; - } - else { - roll -= loot_drop->Entries[j].chance; } } } - } // We either ran out of items or reached our limit. + + // Wrap the loop back to catch the start of loop + if (++j >= loot_drop->NumEntries) { + j = 0; + } + + } while (dropped < droplimit && j != start_index); npc->UpdateEquipmentLight(); // no wearchange associated with this function..so, this should not be needed From f912814e136688ba9d84c42dbc2dd5f13f547143 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sat, 30 Oct 2021 00:54:33 -0500 Subject: [PATCH 301/624] [Commands] Fix Z on #spawnfix (#1647) * Fix Z on spawnfix * Slight adjustment --- zone/command.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index c48bbd685..c973f6cb4 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -6469,21 +6469,27 @@ void command_npcspawn(Client *c, const Seperator *sep) } void command_spawnfix(Client *c, const Seperator *sep) { - Mob *targetMob = c->GetTarget(); - if (!targetMob || !targetMob->IsNPC()) { + Mob *target_mob = c->GetTarget(); + if (!target_mob || !target_mob->IsNPC()) { c->Message(Chat::White, "Error: #spawnfix: Need an NPC target."); return; } - Spawn2* s2 = targetMob->CastToNPC()->respawn2; + Spawn2* s2 = target_mob->CastToNPC()->respawn2; if(!s2) { c->Message(Chat::White, "#spawnfix FAILED -- cannot determine which spawn entry in the database this mob came from."); return; } - std::string query = StringFormat("UPDATE spawn2 SET x = '%f', y = '%f', z = '%f', heading = '%f' WHERE id = '%i'", - c->GetX(), c->GetY(), c->GetZ(), c->GetHeading(),s2->GetID()); + std::string query = StringFormat( + "UPDATE spawn2 SET x = '%f', y = '%f', z = '%f', heading = '%f' WHERE id = '%i'", + c->GetX(), + c->GetY(), + target_mob->GetFixedZ(c->GetPosition()), + c->GetHeading(), + s2->GetID() + ); auto results = content_db.QueryDatabase(query); if (!results.Success()) { c->Message(Chat::Red, "Update failed! MySQL gave the following error:"); @@ -6492,7 +6498,7 @@ void command_spawnfix(Client *c, const Seperator *sep) { } c->Message(Chat::White, "Updating coordinates successful."); - targetMob->Depop(false); + target_mob->Depop(false); } void command_loc(Client *c, const Seperator *sep) From f9855fd0976a4dd34640491df26abc5eec3a8403 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sat, 30 Oct 2021 00:54:44 -0500 Subject: [PATCH 302/624] [Rez] Fix Z during Resurrection (#1648) --- zone/spells.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 9efb8736b..f50acd1f1 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4205,9 +4205,9 @@ void Corpse::CastRezz(uint16 spellid, Mob* Caster) rezz->zone_id = zone->GetZoneID(); rezz->instance_id = zone->GetInstanceID(); rezz->spellid = spellid; - rezz->x = this->m_Position.x; - rezz->y = this->m_Position.y; - rezz->z = this->m_Position.z; + rezz->x = m_Position.x; + rezz->y = m_Position.y; + rezz->z = GetFixedZ(m_Position); rezz->unknown000 = 0x00000000; rezz->unknown020 = 0x00000000; rezz->unknown088 = 0x00000000; From 4389f84ea590dd8a03ee1e7ef154713b65c7d5aa Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 30 Oct 2021 08:50:15 -0400 Subject: [PATCH 303/624] [BugFix] Fix for bard song instrument mod formula from recent update (#1654) * Update spell_effects.cpp * Update spell_effects.cpp * Update spell_effects.cpp --- zone/spell_effects.cpp | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 23cc70991..2aa4e92da 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3329,14 +3329,14 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove int Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level, uint32 instrument_mod, Mob *caster, int ticsremaining, uint16 caster_id) { - int formula, base, max, effect_value, oval; - if (!IsValidSpell(spell_id) || effect_id < 0 || effect_id >= EFFECT_COUNT) return 0; - formula = spells[spell_id].formula[effect_id]; - base = spells[spell_id].base[effect_id]; - max = spells[spell_id].max[effect_id]; + int formula = spells[spell_id].formula[effect_id]; + int base = spells[spell_id].base[effect_id]; + int max = spells[spell_id].max[effect_id]; + int effect_value = 0; + int oval = 0; if (IsBlankSpellEffect(spell_id, effect_id)) return 0; @@ -3345,32 +3345,31 @@ int Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level, // this doesn't actually need to be a song to get mods, just the right skill if (EQ::skills::IsBardInstrumentSkill(spells[spell_id].skill) - && IsInstrumentModAppliedToSpellEffect(spell_id, spells[spell_id].effectid[effect_id])){ + && IsInstrumentModAppliedToSpellEffect(spell_id, spells[spell_id].effectid[effect_id])) { - oval = effect_value; - effect_value = effect_value * instrument_mod / 10; - - LogSpells("Effect value [{}] altered with bard modifier of [{}] to yeild [{}]", - oval, instrument_mod, effect_value); + oval = effect_value; + effect_value = effect_value * static_cast(instrument_mod) / 10; + LogSpells("Effect value [{}] altered with bard modifier of [{}] to yeild [{}]", + oval, instrument_mod, effect_value); } /* SPA 413 SE_FcBaseEffects, modifies base value of a spell effect after formula calcultion, but before other focuses. This is applied to non-Bards in Mob::GetInstrumentMod Like bard modifiers, this is sent in the action_struct using action->instrument_mod (which is a base effect modifier) - + Issue: value sent with action->instrument_mod needs to be 10 or higher. Therefore lowest possible percent chance would be 11 (calculated to 10%) - there are modern spells that use less than 10% but we send as a uint where lowest value has to be 10, where it should be a float for current clients. + there are modern spells that use less than 10% but we send as a uint where lowest value has to be 10, where it should be a float for current clients. Though not ideal, at the moment for spells that are instant effects, the action packet doesn't matter and we will calculate the actual percent here correctly. Logic here is, caster_id is only sent from ApplySpellBonuses. Thus if it is a buff a long as the base effects is set to over 10% and at +10% intervals it will focus the base value correctly. - + */ if (GetClass() != BARD) { - + if (caster_id && instrument_mod > 10) { //This is checked from Mob::ApplySpellBonuses, applied to buffs that receive bonuses. See above, must be in 10% intervals to work. oval = effect_value; - effect_value = effect_value * instrument_mod / 10; + effect_value = effect_value * static_cast(instrument_mod) / 10; LogSpells("Bonus Effect value [{}] altered with base effects modifier of [{}] to yeild [{}]", oval, instrument_mod, effect_value); @@ -3389,7 +3388,7 @@ int Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level, } effect_value = mod_effect_value(effect_value, spell_id, spells[spell_id].effectid[effect_id], caster, caster_id); - + return effect_value; } From df3161455a2314e0e8b774bbbe328ee5d0a41fe9 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 30 Oct 2021 17:48:55 -0400 Subject: [PATCH 304/624] [BugFix] Charm Targeting and other issues. (#1655) * fix for target change bug on client * Update spell_effects.cpp --- zone/entity.cpp | 4 +++- zone/mob.cpp | 2 +- zone/spell_effects.cpp | 41 ++++++++++++++++++++++------------------- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/zone/entity.cpp b/zone/entity.cpp index 28bb5e144..8630cbfcf 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1514,7 +1514,9 @@ void EntityList::RemoveFromTargets(Mob *mob, bool RemoveFromXTargets) mob->CastToClient()->RemoveXTarget(m, false); } - m->RemoveFromHateList(mob); + if (m->IsAIControlled()) { + m->RemoveFromHateList(mob); + } } } diff --git a/zone/mob.cpp b/zone/mob.cpp index a60a9f563..51fa71d4a 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -2875,7 +2875,7 @@ bool Mob::RemoveFromHateList(Mob* mob) ResetAssistCap(); } } - if(GetTarget() == mob) + if(IsAIControlled() && GetTarget() == mob) { SetTarget(hate_list.GetEntWithMostHateOnList(this)); } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 2aa4e92da..3a4a59490 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -731,6 +731,27 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if (!caster) // can't be someone's pet unless we know who that someone is break; + if (IsClient() && caster->IsClient()) { + caster->Message(Chat::White, "Unable to cast charm on a fellow player."); + BuffFadeByEffect(SE_Charm); + break; + } + else if (IsCorpse()) { + caster->Message(Chat::White, "Unable to cast charm on a corpse."); + BuffFadeByEffect(SE_Charm); + break; + } + else if (caster->GetPet() != nullptr && caster->IsClient()) { + caster->Message(Chat::White, "You cannot charm something when you already have a pet."); + BuffFadeByEffect(SE_Charm); + break; + } + else if (GetOwner()) { + caster->Message(Chat::White, "You cannot charm someone else's pet!"); + BuffFadeByEffect(SE_Charm); + break; + } + if(IsNPC()) { CastToNPC()->SaveGuardSpotCharm(); @@ -739,25 +760,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove entity_list.RemoveDebuffs(this); entity_list.RemoveFromTargets(this); WipeHateList(); - - if (IsClient() && caster->IsClient()) { - caster->Message(Chat::White, "Unable to cast charm on a fellow player."); - BuffFadeByEffect(SE_Charm); - break; - } else if(IsCorpse()) { - caster->Message(Chat::White, "Unable to cast charm on a corpse."); - BuffFadeByEffect(SE_Charm); - break; - } else if(caster->GetPet() != nullptr && caster->IsClient()) { - caster->Message(Chat::White, "You cannot charm something when you already have a pet."); - BuffFadeByEffect(SE_Charm); - break; - } else if(GetOwner()) { - caster->Message(Chat::White, "You cannot charm someone else's pet!"); - BuffFadeByEffect(SE_Charm); - break; - } - + Mob *my_pet = GetPet(); if(my_pet) { From 3cda32c21385d5c88ae3a75e07c974098b8bf0d5 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sat, 30 Oct 2021 17:32:59 -0500 Subject: [PATCH 305/624] [Saylinks] In-Memory Saylink Lookups (#1644) * Implement saylink memory lookups (performance) * Ignore commands --- common/say_link.cpp | 77 +++++++++++++++++++++++++++++---------------- common/say_link.h | 4 ++- zone/main.cpp | 2 ++ 3 files changed, 55 insertions(+), 28 deletions(-) diff --git a/common/say_link.cpp b/common/say_link.cpp index aa4c4e3ac..a10dc4b2d 100644 --- a/common/say_link.cpp +++ b/common/say_link.cpp @@ -26,6 +26,9 @@ #include "../zone/zonedb.h" #include +// static bucket global +std::vector g_cached_saylinks = {}; + bool EQ::saylink::DegenerateLinkBody(SayLinkBody_Struct &say_link_body_struct, const std::string &say_link_body) { memset(&say_link_body_struct, 0, sizeof(say_link_body_struct)); @@ -295,33 +298,9 @@ std::string EQ::SayLinkEngine::GenerateQuestSaylink(std::string saylink_text, bo { uint32 saylink_id = 0; - /** - * Query for an existing phrase and id in the saylink table - */ - std::string query = StringFormat( - "SELECT `id` FROM `saylink` WHERE `phrase` = '%s' LIMIT 1", - EscapeString(saylink_text).c_str()); - - auto results = database.QueryDatabase(query); - - if (results.Success()) { - if (results.RowCount() >= 1) { - for (auto row = results.begin(); row != results.end(); ++row) - saylink_id = static_cast(atoi(row[0])); - } - else { - std::string insert_query = StringFormat( - "INSERT INTO `saylink` (`phrase`) VALUES ('%s')", - EscapeString(saylink_text).c_str()); - - results = database.QueryDatabase(insert_query); - if (!results.Success()) { - LogError("Error in saylink phrase queries {}", results.ErrorMessage().c_str()); - } - else { - saylink_id = results.LastInsertedID(); - } - } + SaylinkRepository::Saylink saylink = GetOrSaveSaylink(saylink_text); + if (saylink.id > 0) { + saylink_id = saylink.id; } /** @@ -485,3 +464,47 @@ std::string EQ::SayLinkEngine::InjectSaylinksIfNotExist(const char *message) return new_message; } + +void EQ::SayLinkEngine::LoadCachedSaylinks() +{ + auto saylinks = SaylinkRepository::GetWhere(database, "phrase not like '%#%'"); + LogSaylink("Loaded [{}] saylinks into cache", saylinks.size()); + g_cached_saylinks = saylinks; +} + +SaylinkRepository::Saylink EQ::SayLinkEngine::GetOrSaveSaylink(std::string saylink_text) +{ + // return cached saylink if exist + if (!g_cached_saylinks.empty()) { + for (auto &s: g_cached_saylinks) { + if (s.phrase == saylink_text) { + return s; + } + } + } + + auto saylinks = SaylinkRepository::GetWhere( + database, + fmt::format("phrase = '{}'", EscapeString(saylink_text)) + ); + + // return if found from the database + if (!saylinks.empty()) { + return saylinks[0]; + } + + // if not found in database - save + if (saylinks.empty()) { + auto new_saylink = SaylinkRepository::NewEntity(); + new_saylink.phrase = saylink_text; + + // persist to database + auto link = SaylinkRepository::InsertOne(database, new_saylink); + if (link.id > 0) { + g_cached_saylinks.emplace_back(link); + return link; + } + } + + return {}; +} diff --git a/common/say_link.h b/common/say_link.h index 982b9ec58..aec8d8879 100644 --- a/common/say_link.h +++ b/common/say_link.h @@ -23,7 +23,7 @@ #include "types.h" #include - +#include "repositories/saylink_repository.h" struct ServerLootItem_Struct; @@ -106,6 +106,7 @@ namespace EQ void Reset(); static std::string InjectSaylinksIfNotExist(const char *message); + static void LoadCachedSaylinks(); private: void generate_body(); void generate_text(); @@ -121,6 +122,7 @@ namespace EQ std::string m_LinkBody; std::string m_LinkText; bool m_Error; + static SaylinkRepository::Saylink GetOrSaveSaylink(std::string saylink_text); }; } /*EQEmu*/ diff --git a/zone/main.cpp b/zone/main.cpp index a152e9945..eab175639 100644 --- a/zone/main.cpp +++ b/zone/main.cpp @@ -392,6 +392,8 @@ int main(int argc, char** argv) { event_scheduler.SetDatabase(&database)->LoadScheduledEvents(); + EQ::SayLinkEngine::LoadCachedSaylinks(); + #ifdef BOTS LogInfo("Loading bot commands"); int botretval = bot_command_init(); From 119c3d14b7a6b8e9e6d9badd7b0b957de34a9500 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sat, 30 Oct 2021 19:06:38 -0500 Subject: [PATCH 306/624] [Hotfix] Gate some new shared task logic behind task rule (#1656) --- zone/client_packet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index d04db5849..6f4b630f0 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -959,7 +959,7 @@ void Client::CompleteConnect() } // shared tasks memberlist - if (GetTaskState()->HasActiveSharedTask()) { + if (RuleB(TaskSystem, EnableTaskSystem) && GetTaskState()->HasActiveSharedTask()) { // struct auto p = new ServerPacket( From d87db648c37b3a729de8278a660d5839b5e480ba Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sat, 30 Oct 2021 19:09:42 -0500 Subject: [PATCH 307/624] [Loginserver] Code Cleanup and Tweaks (#1653) * if for whatever reason the world server is not sending an address, use the local address it sends * Log when world is sending loginserver info * Force legacy mode when login host is login.eqemulator.net to avoid misconfigurations at least until things change * Add human IP translation to log messages * Sanitize world server name * Code cleanup and renaming member variables * More cleanup * Remove this-> * Validation constants * Key worldserver lookups by both longname and shortname both * Update allowed character list * Fix short_name API response field; add world_id to response * Shorten receiver verbosity * Remove unnecessary member variables from database and rename database to m_database * Adjust MAX_SERVER_VERSION_LENGTH * Fix indents --- common/eqemu_config.cpp | 15 +- common/string_util.cpp | 28 +++ common/string_util.h | 1 + loginserver/account_management.cpp | 22 +- loginserver/account_management.h | 19 -- loginserver/client.cpp | 164 +++++++------- loginserver/client.h | 64 ++---- loginserver/client_manager.cpp | 31 +-- loginserver/client_manager.h | 20 -- loginserver/database.cpp | 110 ++++------ loginserver/database.h | 29 +-- loginserver/encryption.h | 20 -- loginserver/eq_crypto_api.h | 20 -- loginserver/login_server.h | 24 +-- loginserver/login_structures.h | 20 -- loginserver/loginserver_command_handler.cpp | 20 -- loginserver/loginserver_command_handler.h | 20 -- loginserver/loginserver_webserver.cpp | 39 +--- loginserver/loginserver_webserver.h | 20 -- loginserver/main.cpp | 20 -- loginserver/options.h | 20 -- loginserver/server_manager.cpp | 72 +++---- loginserver/server_manager.h | 24 +-- loginserver/world_server.cpp | 225 +++++++++----------- loginserver/world_server.h | 85 +++----- world/login_server.cpp | 185 +++++++++------- world/login_server.h | 49 ++--- 27 files changed, 496 insertions(+), 870 deletions(-) diff --git a/common/eqemu_config.cpp b/common/eqemu_config.cpp index 4f29bb901..1790e7788 100644 --- a/common/eqemu_config.cpp +++ b/common/eqemu_config.cpp @@ -44,6 +44,12 @@ void EQEmuConfig::parse_config() if (_root["server"]["world"]["loginserver"].get("legacy", "0").asString() == "1") { LoginLegacy = true; } LoginAccount = _root["server"]["world"]["loginserver"].get("account", "").asString(); LoginPassword = _root["server"]["world"]["loginserver"].get("password", "").asString(); + + // at least today, this is wrong a majority of the time + // remove this if eqemulator ever upgrades its loginserver + if (LoginHost.find("login.eqemulator.net") != std::string::npos) { + LoginLegacy = true; + } } else { char str[32]; @@ -62,12 +68,19 @@ void EQEmuConfig::parse_config() loginconfig->LoginLegacy = false; if (_root["server"]["world"][str].get("legacy", "0").asString() == "1") { loginconfig->LoginLegacy = true; } + + // at least today, this is wrong a majority of the time + // remove this if eqemulator ever upgrades its loginserver + if (loginconfig->LoginHost.find("login.eqemulator.net") != std::string::npos) { + loginconfig->LoginLegacy = true; + } + loginlist.Insert(loginconfig); } while (LoginCount < 100); } - // from xml converts to json as locked: "", so i default to "false". + // from xml converts to json as locked: "", so i default to "false". //The only way to enable locked is by switching to true, meaning this value is always false until manually set true Locked = false; if (_root["server"]["world"].get("locked", "false").asString() == "true") { Locked = true; } diff --git a/common/string_util.cpp b/common/string_util.cpp index 65a35a0d7..305db7e48 100644 --- a/common/string_util.cpp +++ b/common/string_util.cpp @@ -605,3 +605,31 @@ std::string FormatName(const std::string& char_name) } return formatted; } + +bool IsAllowedWorldServerCharacterList(char c) +{ + const char *valid_characters = ":[](){}.!@#$%^&*-=+<>/\\|'\""; + if (strchr(valid_characters, c)) { + return true; + } + + return false; +} + +void SanitizeWorldServerName(char *name) +{ + std::string server_long_name = name; + server_long_name.erase( + std::remove_if( + server_long_name.begin(), + server_long_name.end(), + [](char c) { + return !(std::isalpha(c) || std::isalnum(c) || std::isspace(c) || IsAllowedWorldServerCharacterList(c)); + } + ), server_long_name.end() + ); + + server_long_name = trim(server_long_name); + + strcpy(name, server_long_name.c_str()); +} diff --git a/common/string_util.h b/common/string_util.h index b6718172a..9402bfcb6 100644 --- a/common/string_util.h +++ b/common/string_util.h @@ -208,6 +208,7 @@ void RemoveApostrophes(std::string &s); std::string convert2digit(int n, std::string suffix); std::string numberToWords(unsigned long long int n); std::string FormatName(const std::string& char_name); +void SanitizeWorldServerName(char *name); template auto CleanMobName(InputIterator first, InputIterator last, OutputIterator result) diff --git a/loginserver/account_management.cpp b/loginserver/account_management.cpp index 0ed766c9f..18b685d64 100644 --- a/loginserver/account_management.cpp +++ b/loginserver/account_management.cpp @@ -1,23 +1,3 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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 - * - */ - #include "account_management.h" #include "login_server.h" #include "../common/event/task_scheduler.h" @@ -416,4 +396,4 @@ uint32 AccountManagement::CheckExternalLoginserverUserCredentials( ); return res.get(); -} \ No newline at end of file +} diff --git a/loginserver/account_management.h b/loginserver/account_management.h index 052f2619f..e342ec172 100644 --- a/loginserver/account_management.h +++ b/loginserver/account_management.h @@ -1,22 +1,3 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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_ACCOUNT_MANAGEMENT_H #define EQEMU_ACCOUNT_MANAGEMENT_H diff --git a/loginserver/client.cpp b/loginserver/client.cpp index 4b116fc1c..d62b6c1fa 100644 --- a/loginserver/client.cpp +++ b/loginserver/client.cpp @@ -1,23 +1,3 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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 - * - */ - #include "client.h" #include "login_server.h" #include "../common/misc_functions.h" @@ -33,17 +13,17 @@ extern LoginServer server; */ Client::Client(std::shared_ptr c, LSClientVersion v) { - connection = c; - version = v; - status = cs_not_sent_session_ready; - account_id = 0; - play_server_id = 0; - play_sequence_id = 0; + m_connection = c; + m_client_version = v; + m_client_status = cs_not_sent_session_ready; + m_account_id = 0; + m_play_server_id = 0; + m_play_sequence_id = 0; } bool Client::Process() { - EQApplicationPacket *app = connection->PopPacket(); + EQApplicationPacket *app = m_connection->PopPacket(); while (app) { if (server.options.IsTraceOn()) { LogDebug("Application packet received from client (size {0})", app->Size()); @@ -53,9 +33,9 @@ bool Client::Process() DumpPacket(app); } - if (status == cs_failed_to_login) { + if (m_client_status == cs_failed_to_login) { delete app; - app = connection->PopPacket(); + app = m_connection->PopPacket(); continue; } @@ -112,7 +92,7 @@ bool Client::Process() } delete app; - app = connection->PopPacket(); + app = m_connection->PopPacket(); } return true; @@ -126,7 +106,7 @@ bool Client::Process() */ void Client::Handle_SessionReady(const char *data, unsigned int size) { - if (status != cs_not_sent_session_ready) { + if (m_client_status != cs_not_sent_session_ready) { LogError("Session ready received again after already being received"); return; } @@ -136,12 +116,12 @@ void Client::Handle_SessionReady(const char *data, unsigned int size) return; } - status = cs_waiting_for_login; + m_client_status = cs_waiting_for_login; /** * The packets are mostly the same but slightly different between the two versions */ - if (version == cv_sod) { + if (m_client_version == cv_sod) { auto *outapp = new EQApplicationPacket(OP_ChatMessage, 17); outapp->pBuffer[0] = 0x02; outapp->pBuffer[10] = 0x01; @@ -151,7 +131,7 @@ void Client::Handle_SessionReady(const char *data, unsigned int size) DumpPacket(outapp); } - connection->QueuePacket(outapp); + m_connection->QueuePacket(outapp); delete outapp; } else { @@ -166,7 +146,7 @@ void Client::Handle_SessionReady(const char *data, unsigned int size) DumpPacket(outapp); } - connection->QueuePacket(outapp); + m_connection->QueuePacket(outapp); delete outapp; } } @@ -179,7 +159,7 @@ void Client::Handle_SessionReady(const char *data, unsigned int size) */ void Client::Handle_Login(const char *data, unsigned int size) { - if (status != cs_waiting_for_login) { + if (m_client_status != cs_waiting_for_login) { LogError("Login received after already having logged in"); return; } @@ -228,7 +208,7 @@ void Client::Handle_Login(const char *data, unsigned int size) return; } - memcpy(&llrs, data, sizeof(LoginLoginRequest_Struct)); + memcpy(&m_llrs, data, sizeof(LoginLoginRequest_Struct)); bool result = false; if (outbuffer[0] == 0 && outbuffer[1] == 0) { @@ -236,7 +216,7 @@ void Client::Handle_Login(const char *data, unsigned int size) cred = (&outbuffer[2 + user.length()]); result = server.db->GetLoginTokenDataFromToken( cred, - connection->GetRemoteAddr(), + m_connection->GetRemoteAddr(), db_account_id, db_loginserver, user @@ -267,7 +247,7 @@ void Client::Handle_Login(const char *data, unsigned int size) LogDebug("[VerifyLoginHash] Success [{0}]", (result ? "true" : "false")); } else { - status = cs_creating_account; + m_client_status = cs_creating_account; AttemptLoginAccountCreation(user, cred, db_loginserver); return; @@ -305,7 +285,7 @@ void Client::Handle_Login(const char *data, unsigned int size) */ void Client::Handle_Play(const char *data) { - if (status != cs_logged_in) { + if (m_client_status != cs_logged_in) { LogError("Client sent a play request when they were not logged in, discarding"); return; } @@ -323,10 +303,10 @@ void Client::Handle_Play(const char *data) ); } - this->play_server_id = (unsigned int) play->ServerNumber; - play_sequence_id = sequence_in; - play_server_id = server_id_in; - server.server_manager->SendUserToWorldRequest(server_id_in, account_id, loginserver_name); + m_play_server_id = (unsigned int) play->ServerNumber; + m_play_sequence_id = sequence_in; + m_play_server_id = server_id_in; + server.server_manager->SendUserToWorldRequest(server_id_in, m_account_id, m_loginserver_name); } /** @@ -340,7 +320,7 @@ void Client::SendServerListPacket(uint32 seq) DumpPacket(outapp); } - connection->QueuePacket(outapp); + m_connection->QueuePacket(outapp); delete outapp; } @@ -350,12 +330,12 @@ void Client::SendPlayResponse(EQApplicationPacket *outapp) LogDebug("Sending play response for {0}", GetAccountName()); // server_log->LogPacket(log_network_trace, (const char*)outapp->pBuffer, outapp->size); } - connection->QueuePacket(outapp); + m_connection->QueuePacket(outapp); } void Client::GenerateKey() { - key.clear(); + m_key.clear(); int count = 0; while (count < 10) { static const char key_selection[] = @@ -367,7 +347,7 @@ void Client::GenerateKey() '6', '7', '8', '9' }; - key.append((const char *) &key_selection[random.Int(0, 35)], 1); + m_key.append((const char *) &key_selection[m_random.Int(0, 35)], 1); count++; } } @@ -383,6 +363,8 @@ void Client::AttemptLoginAccountCreation( const std::string &loginserver ) { + LogInfo("[AttemptLoginAccountCreation] user [{}] loginserver [{}]", user, loginserver); + #ifdef LSPX if (loginserver == "eqemu") { LogInfo("Attempting login account creation via '{0}'", loginserver); @@ -404,8 +386,8 @@ void Client::AttemptLoginAccountCreation( return; } - stored_user = user; - stored_pass = pass; + m_stored_user = user; + m_stored_pass = pass; auto address = addr_components[0]; auto port = std::stoi(addr_components[1]); @@ -416,15 +398,15 @@ void Client::AttemptLoginAccountCreation( return; } - login_connection_manager.reset(new EQ::Net::DaybreakConnectionManager()); - login_connection_manager->OnNewConnection( + m_login_connection_manager.reset(new EQ::Net::DaybreakConnectionManager()); + m_login_connection_manager->OnNewConnection( std::bind( &Client::LoginOnNewConnection, this, std::placeholders::_1 ) ); - login_connection_manager->OnConnectionStateChange( + m_login_connection_manager->OnConnectionStateChange( std::bind( &Client::LoginOnStatusChange, this, @@ -433,7 +415,7 @@ void Client::AttemptLoginAccountCreation( std::placeholders::_3 ) ); - login_connection_manager->OnPacketRecv( + m_login_connection_manager->OnPacketRecv( std::bind( &Client::LoginOnPacketRecv, this, @@ -442,7 +424,7 @@ void Client::AttemptLoginAccountCreation( ) ); - login_connection_manager->Connect(addr, port); + m_login_connection_manager->Connect(addr, port); } ); @@ -461,17 +443,17 @@ void Client::AttemptLoginAccountCreation( void Client::DoFailedLogin() { - stored_user.clear(); - stored_pass.clear(); + m_stored_user.clear(); + m_stored_pass.clear(); EQApplicationPacket outapp(OP_LoginAccepted, sizeof(LoginLoginFailed_Struct)); auto *login_failed = (LoginLoginFailed_Struct *) outapp.pBuffer; - login_failed->unknown1 = llrs.unknown1; - login_failed->unknown2 = llrs.unknown2; - login_failed->unknown3 = llrs.unknown3; - login_failed->unknown4 = llrs.unknown4; - login_failed->unknown5 = llrs.unknown5; + login_failed->unknown1 = m_llrs.unknown1; + login_failed->unknown2 = m_llrs.unknown2; + login_failed->unknown3 = m_llrs.unknown3; + login_failed->unknown4 = m_llrs.unknown4; + login_failed->unknown5 = m_llrs.unknown5; memcpy(login_failed->unknown6, FailedLoginResponseData, sizeof(FailedLoginResponseData)); @@ -479,8 +461,8 @@ void Client::DoFailedLogin() DumpPacket(&outapp); } - connection->QueuePacket(&outapp); - status = cs_failed_to_login; + m_connection->QueuePacket(&outapp); + m_client_status = cs_failed_to_login; } /** @@ -576,28 +558,28 @@ void Client::DoSuccessfulLogin( const std::string &db_loginserver ) { - stored_user.clear(); - stored_pass.clear(); + m_stored_user.clear(); + m_stored_pass.clear(); server.client_manager->RemoveExistingClient(db_account_id, db_loginserver); in_addr in{}; - in.s_addr = connection->GetRemoteIP(); + in.s_addr = m_connection->GetRemoteIP(); server.db->UpdateLSAccountData(db_account_id, std::string(inet_ntoa(in))); GenerateKey(); - account_id = db_account_id; - account_name = in_account_name; - loginserver_name = db_loginserver; + m_account_id = db_account_id; + m_account_name = in_account_name; + m_loginserver_name = db_loginserver; auto *outapp = new EQApplicationPacket(OP_LoginAccepted, 10 + 80); auto *login_accepted = (LoginAccepted_Struct *) outapp->pBuffer; - login_accepted->unknown1 = llrs.unknown1; - login_accepted->unknown2 = llrs.unknown2; - login_accepted->unknown3 = llrs.unknown3; - login_accepted->unknown4 = llrs.unknown4; - login_accepted->unknown5 = llrs.unknown5; + login_accepted->unknown1 = m_llrs.unknown1; + login_accepted->unknown2 = m_llrs.unknown2; + login_accepted->unknown3 = m_llrs.unknown3; + login_accepted->unknown4 = m_llrs.unknown4; + login_accepted->unknown5 = m_llrs.unknown5; auto *login_failed_attempts = new LoginFailedAttempts_Struct; memset(login_failed_attempts, 0, sizeof(LoginFailedAttempts_Struct)); @@ -620,7 +602,7 @@ void Client::DoSuccessfulLogin( login_failed_attempts->unknown9[1] = 0x03; login_failed_attempts->unknown11[0] = 0x63; login_failed_attempts->unknown12[0] = 0x01; - memcpy(login_failed_attempts->key, key.c_str(), key.size()); + memcpy(login_failed_attempts->key, m_key.c_str(), m_key.size()); char encrypted_buffer[80] = {0}; auto rc = eqcrypt_block((const char *) login_failed_attempts, 75, encrypted_buffer, 1); @@ -634,10 +616,10 @@ void Client::DoSuccessfulLogin( DumpPacket(outapp); } - connection->QueuePacket(outapp); + m_connection->QueuePacket(outapp); delete outapp; - status = cs_logged_in; + m_client_status = cs_logged_in; } /** @@ -689,7 +671,7 @@ void Client::CreateEQEmuAccount( */ void Client::LoginOnNewConnection(std::shared_ptr connection) { - login_connection = connection; + m_login_connection = connection; } /** @@ -751,16 +733,16 @@ void Client::LoginSendSessionReady() p.PutUInt16(0, 1); //OP_SessionReady p.PutUInt32(2, 2); - login_connection->QueuePacket(p); + m_login_connection->QueuePacket(p); } void Client::LoginSendLogin() { - size_t buffer_len = stored_user.length() + stored_pass.length() + 2; + size_t buffer_len = m_stored_user.length() + m_stored_pass.length() + 2; std::unique_ptr buffer(new char[buffer_len]); - strcpy(&buffer[0], stored_user.c_str()); - strcpy(&buffer[stored_user.length() + 1], stored_pass.c_str()); + strcpy(&buffer[0], m_stored_user.c_str()); + strcpy(&buffer[m_stored_user.length() + 1], m_stored_pass.c_str()); size_t encrypted_len = buffer_len; @@ -775,7 +757,7 @@ void Client::LoginSendLogin() eqcrypt_block(&buffer[0], buffer_len, (char *) p.Data() + 12, true); - login_connection->QueuePacket(p); + m_login_connection->QueuePacket(p); } /** @@ -796,7 +778,7 @@ void Client::LoginProcessLoginResponse(const EQ::Net::Packet &p) EQ::Net::StaticPacket sp(&decrypted[0], encrypt_size); auto response_error = sp.GetUInt16(1); - login_connection_manager->OnConnectionStateChange( + m_login_connection_manager->OnConnectionStateChange( std::bind( &Client::LoginOnStatusChangeIgnored, this, @@ -809,18 +791,18 @@ void Client::LoginProcessLoginResponse(const EQ::Net::Packet &p) if (response_error > 101) { LogDebug("response [{0}] failed login", response_error); DoFailedLogin(); - login_connection->Close(); + m_login_connection->Close(); } else { LogDebug( - "response [{0}] login succeeded user [{1}]", - response_error, - stored_user + "response [{0}] login succeeded user [{1}]", + response_error, + m_stored_user ); auto m_dbid = sp.GetUInt32(8); - CreateEQEmuAccount(stored_user, stored_pass, m_dbid); - login_connection->Close(); + CreateEQEmuAccount(m_stored_user, m_stored_pass, m_dbid); + m_login_connection->Close(); } } diff --git a/loginserver/client.h b/loginserver/client.h index 9d76e7e78..67c2f8e1f 100644 --- a/loginserver/client.h +++ b/loginserver/client.h @@ -1,23 +1,3 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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_CLIENT_H #define EQEMU_CLIENT_H @@ -116,49 +96,49 @@ public: * * @return */ - unsigned int GetAccountID() const { return account_id; } + unsigned int GetAccountID() const { return m_account_id; } /** * Gets the loginserver name of this client * * @return */ - std::string GetLoginServerName() const { return loginserver_name; } + std::string GetLoginServerName() const { return m_loginserver_name; } /** * Gets the account name of this client * * @return */ - std::string GetAccountName() const { return account_name; } + std::string GetAccountName() const { return m_account_name; } /** * Gets the key generated at login for this client * * @return */ - std::string GetKey() const { return key; } + std::string GetKey() const { return m_key; } /** * Gets the server selected to be played on for this client * * @return */ - unsigned int GetPlayServerID() const { return play_server_id; } + unsigned int GetPlayServerID() const { return m_play_server_id; } /** * Gets the play sequence state for this client * * @return */ - unsigned int GetPlaySequence() const { return play_sequence_id; } + unsigned int GetPlaySequence() const { return m_play_sequence_id; } /** * Gets the connection for this client * * @return */ - std::shared_ptr GetConnection() { return connection; } + std::shared_ptr GetConnection() { return m_connection; } /** * Attempts to create a login account @@ -195,24 +175,24 @@ public: void CreateEQEmuAccount(const std::string &in_account_name, const std::string &in_account_password, unsigned int loginserver_account_id); private: - EQ::Random random; - std::shared_ptr connection; - LSClientVersion version; - LSClientStatus status; + EQ::Random m_random; + std::shared_ptr m_connection; + LSClientVersion m_client_version; + LSClientStatus m_client_status; - std::string account_name; - unsigned int account_id; - std::string loginserver_name; - unsigned int play_server_id; - unsigned int play_sequence_id; - std::string key; + std::string m_account_name; + unsigned int m_account_id; + std::string m_loginserver_name; + unsigned int m_play_server_id; + unsigned int m_play_sequence_id; + std::string m_key; - std::unique_ptr login_connection_manager; - std::shared_ptr login_connection; - LoginLoginRequest_Struct llrs; + std::unique_ptr m_login_connection_manager; + std::shared_ptr m_login_connection; + LoginLoginRequest_Struct m_llrs; - std::string stored_user; - std::string stored_pass; + std::string m_stored_user; + std::string m_stored_pass; void LoginOnNewConnection(std::shared_ptr connection); void LoginOnStatusChange( std::shared_ptr conn, diff --git a/loginserver/client_manager.cpp b/loginserver/client_manager.cpp index d37ec545f..6b3fc0bce 100644 --- a/loginserver/client_manager.cpp +++ b/loginserver/client_manager.cpp @@ -1,23 +1,3 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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 - * - */ - #include "client_manager.h" #include "login_server.h" @@ -25,6 +5,7 @@ extern LoginServer server; extern bool run_server; #include "../common/eqemu_logsys.h" +#include "../common/misc.h" ClientManager::ClientManager() { @@ -52,8 +33,8 @@ ClientManager::ClientManager() titanium_stream->OnNewConnection( [this](std::shared_ptr stream) { LogInfo( - "New Titanium client connection from {0}:{1}", - stream->GetRemoteIP(), + "New Titanium client connection from [{0}:{1}]", + long2ip(stream->GetRemoteIP()), stream->GetRemotePort() ); @@ -87,13 +68,13 @@ ClientManager::ClientManager() sod_stream->OnNewConnection( [this](std::shared_ptr stream) { LogInfo( - "New SoD client connection from {0}:{1}", - stream->GetRemoteIP(), + "New SoD+ client connection from [{0}:{1}]", + long2ip(stream->GetRemoteIP()), stream->GetRemotePort() ); stream->SetOpcodeManager(&sod_ops); - Client *c = new Client(stream, cv_sod); + auto *c = new Client(stream, cv_sod); clients.push_back(c); } ); diff --git a/loginserver/client_manager.h b/loginserver/client_manager.h index 4de290210..6a8c31164 100644 --- a/loginserver/client_manager.h +++ b/loginserver/client_manager.h @@ -1,23 +1,3 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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_CLIENTMANAGER_H #define EQEMU_CLIENTMANAGER_H diff --git a/loginserver/database.cpp b/loginserver/database.cpp index 8bf481d20..18612dac6 100644 --- a/loginserver/database.cpp +++ b/loginserver/database.cpp @@ -1,23 +1,3 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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 - * - */ - #include "../common/global_define.h" #include "database.h" @@ -45,11 +25,6 @@ Database::Database( std::string name ) { - this->user = user; - this->pass = pass; - this->host = host; - this->name = name; - uint32 errnum = 0; char errbuf[MYSQL_ERRMSG_SIZE]; if (!Open( @@ -75,8 +50,8 @@ Database::Database( */ Database::~Database() { - if (database) { - mysql_close(database); + if (m_database) { + mysql_close(m_database); } } @@ -355,11 +330,13 @@ void Database::UpdateLoginserverAccountPasswordHash( /** * @param short_name + * @param long_name * @param login_world_server_admin_id * @return */ Database::DbWorldRegistration Database::GetWorldRegistration( const std::string &short_name, + const std::string &long_name, uint32 login_world_server_admin_id ) { @@ -375,45 +352,46 @@ Database::DbWorldRegistration Database::GetWorldRegistration( " login_world_servers AS WSR\n" " JOIN login_server_list_types AS SLT ON WSR.login_server_list_type_id = SLT.id\n" "WHERE\n" - " WSR.short_name = '{0}' AND WSR.login_server_admin_id = {1} LIMIT 1", + " WSR.short_name = '{}' AND WSR.long_name = '{}' AND WSR.login_server_admin_id = {} LIMIT 1", EscapeString(short_name), + EscapeString(long_name), login_world_server_admin_id ); - Database::DbWorldRegistration world_registration{}; + Database::DbWorldRegistration r{}; auto results = QueryDatabase(query); if (!results.Success() || results.RowCount() != 1) { - return world_registration; + return r; } auto row = results.begin(); - world_registration.loaded = true; - world_registration.server_id = std::stoi(row[0]); - world_registration.server_description = row[1]; - world_registration.server_list_type = std::stoi(row[3]); - world_registration.is_server_trusted = std::stoi(row[2]) > 0; - world_registration.server_list_description = row[4]; - world_registration.server_admin_id = std::stoi(row[5]); + r.loaded = true; + r.server_id = std::stoi(row[0]); + r.server_description = row[1]; + r.server_list_type = std::stoi(row[3]); + r.is_server_trusted = std::stoi(row[2]) > 0; + r.server_list_description = row[4]; + r.server_admin_id = std::stoi(row[5]); - if (world_registration.server_admin_id <= 0) { - return world_registration; + if (r.server_admin_id <= 0) { + return r; } auto world_registration_query = fmt::format( "SELECT account_name, account_password FROM login_server_admins WHERE id = {0} LIMIT 1", - world_registration.server_admin_id + r.server_admin_id ); auto world_registration_results = QueryDatabase(world_registration_query); if (world_registration_results.Success() && world_registration_results.RowCount() == 1) { auto world_registration_row = world_registration_results.begin(); - world_registration.server_admin_account_name = world_registration_row[0]; - world_registration.server_admin_account_password = world_registration_row[1]; + r.server_admin_account_name = world_registration_row[0]; + r.server_admin_account_password = world_registration_row[1]; } - return world_registration; + return r; } /** @@ -665,21 +643,21 @@ Database::DbLoginServerAdmin Database::GetLoginServerAdmin(const std::string &ac auto results = QueryDatabase(query); - Database::DbLoginServerAdmin login_server_admin{}; + Database::DbLoginServerAdmin r{}; if (results.RowCount() == 1) { auto row = results.begin(); - login_server_admin.loaded = true; - login_server_admin.id = std::stoi(row[0]); - login_server_admin.account_name = row[1]; - login_server_admin.account_password = row[2]; - login_server_admin.first_name = row[3]; - login_server_admin.last_name = row[4]; - login_server_admin.email = row[5]; - login_server_admin.registration_date = row[7]; - login_server_admin.registration_ip_address = row[8]; + r.loaded = true; + r.id = std::stoi(row[0]); + r.account_name = row[1]; + r.account_password = row[2]; + r.first_name = row[3]; + r.last_name = row[4]; + r.email = row[5]; + r.registration_date = row[7]; + r.registration_ip_address = row[8]; } - return login_server_admin; + return r; } /** @@ -701,20 +679,20 @@ Database::DbLoginServerAccount Database::GetLoginServerAccountByAccountName( auto results = QueryDatabase(query); - Database::DbLoginServerAccount login_server_account{}; + Database::DbLoginServerAccount r{}; if (results.RowCount() == 1) { auto row = results.begin(); - login_server_account.loaded = true; - login_server_account.id = std::stoi(row[0]); - login_server_account.account_name = row[1]; - login_server_account.account_password = row[2]; - login_server_account.account_email = row[3]; - login_server_account.source_loginserver = row[4]; - login_server_account.last_ip_address = row[5]; - login_server_account.last_login_date = row[6]; - login_server_account.created_at = row[7]; - login_server_account.updated_at = row[8]; + r.loaded = true; + r.id = std::stoi(row[0]); + r.account_name = row[1]; + r.account_password = row[2]; + r.account_email = row[3]; + r.source_loginserver = row[4]; + r.last_ip_address = row[5]; + r.last_login_date = row[6]; + r.created_at = row[7]; + r.updated_at = row[8]; } - return login_server_account; + return r; } diff --git a/loginserver/database.h b/loginserver/database.h index ecb809ee9..b53af1de9 100644 --- a/loginserver/database.h +++ b/loginserver/database.h @@ -1,23 +1,3 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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_DATABASEMYSQL_H #define EQEMU_DATABASEMYSQL_H @@ -32,7 +12,7 @@ class Database : public DBcore { public: - Database() { database = nullptr; } + Database() { m_database = nullptr; } /** * Constructor, tries to set our database to connect to the supplied options. @@ -49,7 +29,7 @@ public: * Destructor, frees our database if needed. */ ~Database(); - bool IsConnected() { return (database != nullptr); } + bool IsConnected() { return (m_database != nullptr); } /** * Retrieves the login data (password hash and account id) from the account name provided needed for client login procedure. @@ -158,11 +138,13 @@ public: * Returns true if the record was found, false otherwise * * @param short_name + * @param long_name * @param login_world_server_admin_id * @return */ Database::DbWorldRegistration GetWorldRegistration( const std::string &short_name, + const std::string &long_name, uint32 login_world_server_admin_id ); @@ -298,8 +280,7 @@ public: ); protected: - std::string user, pass, host, port, name; - MYSQL *database{}; + MYSQL *m_database{}; }; #endif diff --git a/loginserver/encryption.h b/loginserver/encryption.h index 7db61197e..d8337c25b 100644 --- a/loginserver/encryption.h +++ b/loginserver/encryption.h @@ -1,23 +1,3 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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 - * - */ - #pragma once #include diff --git a/loginserver/eq_crypto_api.h b/loginserver/eq_crypto_api.h index bff7b8444..89d9cbe3c 100644 --- a/loginserver/eq_crypto_api.h +++ b/loginserver/eq_crypto_api.h @@ -1,23 +1,3 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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 EQEMUCAPI__H #define EQEMUCAPI__H diff --git a/loginserver/login_server.h b/loginserver/login_server.h index c130c41d2..015a35fcc 100644 --- a/loginserver/login_server.h +++ b/loginserver/login_server.h @@ -1,29 +1,7 @@ -#include - -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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_LOGINSERVER_H #define EQEMU_LOGINSERVER_H +#include #include "../common/json_config.h" #include "database.h" #include "encryption.h" diff --git a/loginserver/login_structures.h b/loginserver/login_structures.h index 099b4b4f0..35564cff9 100644 --- a/loginserver/login_structures.h +++ b/loginserver/login_structures.h @@ -1,23 +1,3 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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_LOGINSTRUCTURES_H #define EQEMU_LOGINSTRUCTURES_H diff --git a/loginserver/loginserver_command_handler.cpp b/loginserver/loginserver_command_handler.cpp index 35599e299..eed4e6058 100644 --- a/loginserver/loginserver_command_handler.cpp +++ b/loginserver/loginserver_command_handler.cpp @@ -1,23 +1,3 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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 - * - */ - #include #include #include "loginserver_command_handler.h" diff --git a/loginserver/loginserver_command_handler.h b/loginserver/loginserver_command_handler.h index 2f890f956..e47382022 100644 --- a/loginserver/loginserver_command_handler.h +++ b/loginserver/loginserver_command_handler.h @@ -1,23 +1,3 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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 - * - */ - #include "iostream" #include "../common/cli/eqemu_command_handler.h" diff --git a/loginserver/loginserver_webserver.cpp b/loginserver/loginserver_webserver.cpp index 38a3508f6..8fc7780bf 100644 --- a/loginserver/loginserver_webserver.cpp +++ b/loginserver/loginserver_webserver.cpp @@ -1,23 +1,3 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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 - * - */ - #include "loginserver_webserver.h" #include "server_manager.h" #include "login_server.h" @@ -48,17 +28,18 @@ namespace LoginserverWebserver { } Json::Value response; - auto iter = server.server_manager->getWorldServers().begin(); + auto iter = server.server_manager->getWorldServers().begin(); while (iter != server.server_manager->getWorldServers().end()) { Json::Value row; - row["server_long_name"] = (*iter)->GetServerLongName(); - row["server_short_name"] = (*iter)->GetServerLongName(); - row["server_list_id"] = (*iter)->GetServerListID(); - row["server_status"] = (*iter)->GetStatus(); - row["zones_booted"] = (*iter)->GetZonesBooted(); - row["local_ip"] = (*iter)->GetLocalIP(); - row["remote_ip"] = (*iter)->GetRemoteIP(); - row["players_online"] = (*iter)->GetPlayersOnline(); + row["server_long_name"] = (*iter)->GetServerLongName(); + row["server_short_name"] = (*iter)->GetServerShortName(); + row["server_list_type_id"] = (*iter)->GetServerListID(); + row["server_status"] = (*iter)->GetStatus(); + row["zones_booted"] = (*iter)->GetZonesBooted(); + row["local_ip"] = (*iter)->GetLocalIP(); + row["remote_ip"] = (*iter)->GetRemoteIP(); + row["players_online"] = (*iter)->GetPlayersOnline(); + row["world_id"] = (*iter)->GetServerId(); response.append(row); ++iter; } diff --git a/loginserver/loginserver_webserver.h b/loginserver/loginserver_webserver.h index b0cdb88c2..7eb852892 100644 --- a/loginserver/loginserver_webserver.h +++ b/loginserver/loginserver_webserver.h @@ -1,23 +1,3 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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_LOGINSERVER_WEBSERVER_H #define EQEMU_LOGINSERVER_WEBSERVER_H diff --git a/loginserver/main.cpp b/loginserver/main.cpp index ec2cb616d..d505ffc5d 100644 --- a/loginserver/main.cpp +++ b/loginserver/main.cpp @@ -1,23 +1,3 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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 - * - */ - #include "../common/global_define.h" #include "../common/types.h" #include "../common/opcodemgr.h" diff --git a/loginserver/options.h b/loginserver/options.h index b774a88a0..e38114500 100644 --- a/loginserver/options.h +++ b/loginserver/options.h @@ -1,23 +1,3 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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_OPTIONS_H #define EQEMU_OPTIONS_H diff --git a/loginserver/server_manager.cpp b/loginserver/server_manager.cpp index 1761e1b15..e3b839189 100644 --- a/loginserver/server_manager.cpp +++ b/loginserver/server_manager.cpp @@ -1,23 +1,3 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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 - * - */ - #include "server_manager.h" #include "login_server.h" #include "login_structures.h" @@ -33,15 +13,15 @@ ServerManager::ServerManager() { int listen_port = server.config.GetVariableInt("general", "listen_port", 5998); - server_connection = std::make_unique(); + m_server_connection = std::make_unique(); EQ::Net::ServertalkServerOptions opts; opts.port = listen_port; - opts.ipv6 = false; - server_connection->Listen(opts); + opts.ipv6 = false; + m_server_connection->Listen(opts); LogInfo("Loginserver now listening on port [{0}]", listen_port); - server_connection->OnConnectionIdentified( + m_server_connection->OnConnectionIdentified( "World", [this](std::shared_ptr world_connection) { LogInfo( "New World Server connection from {0}:{1}", @@ -49,8 +29,8 @@ ServerManager::ServerManager() world_connection->Handle()->RemotePort() ); - auto iter = world_servers.begin(); - while (iter != world_servers.end()) { + auto iter = m_world_servers.begin(); + while (iter != m_world_servers.end()) { if ((*iter)->GetConnection()->Handle()->RemoteIP().compare(world_connection->Handle()->RemoteIP()) == 0 && (*iter)->GetConnection()->Handle()->RemotePort() == world_connection->Handle()->RemotePort()) { @@ -61,27 +41,27 @@ ServerManager::ServerManager() world_connection->Handle()->RemotePort() ); - world_servers.erase(iter); + m_world_servers.erase(iter); break; } ++iter; } - world_servers.push_back(std::make_unique(world_connection)); + m_world_servers.push_back(std::make_unique(world_connection)); } ); - server_connection->OnConnectionRemoved( + m_server_connection->OnConnectionRemoved( "World", [this](std::shared_ptr c) { - auto iter = world_servers.begin(); - while (iter != world_servers.end()) { + auto iter = m_world_servers.begin(); + while (iter != m_world_servers.end()) { if ((*iter)->GetConnection()->GetUUID() == c->GetUUID()) { LogInfo( "World server {0} has been disconnected, removing.", (*iter)->GetServerLongName() ); - world_servers.erase(iter); + m_world_servers.erase(iter); return; } @@ -100,8 +80,8 @@ ServerManager::~ServerManager() = default; */ WorldServer *ServerManager::GetServerByAddress(const std::string &ip_address, int port) { - auto iter = world_servers.begin(); - while (iter != world_servers.end()) { + auto iter = m_world_servers.begin(); + while (iter != m_world_servers.end()) { if ((*iter)->GetConnection()->Handle()->RemoteIP() == ip_address && (*iter)->GetConnection()->Handle()->RemotePort()) { return (*iter).get(); @@ -127,8 +107,8 @@ EQApplicationPacket *ServerManager::CreateServerListPacket(Client *client, uint3 LogDebug("ServerManager::CreateServerListPacket via client address [{0}]", client_ip); - auto iter = world_servers.begin(); - while (iter != world_servers.end()) { + auto iter = m_world_servers.begin(); + while (iter != m_world_servers.end()) { if (!(*iter)->IsAuthorized()) { LogDebug( "ServerManager::CreateServerListPacket | Server [{0}] via IP [{1}] is not authorized to be listed", @@ -195,8 +175,8 @@ EQApplicationPacket *ServerManager::CreateServerListPacket(Client *client, uint3 unsigned char *data_pointer = outapp->pBuffer; data_pointer += sizeof(ServerListHeader_Struct); - iter = world_servers.begin(); - while (iter != world_servers.end()) { + iter = m_world_servers.begin(); + while (iter != m_world_servers.end()) { if (!(*iter)->IsAuthorized()) { ++iter; continue; @@ -278,9 +258,9 @@ void ServerManager::SendUserToWorldRequest( const std::string &client_loginserver ) { - auto iter = world_servers.begin(); + auto iter = m_world_servers.begin(); bool found = false; - while (iter != world_servers.end()) { + while (iter != m_world_servers.end()) { if ((*iter)->GetServerId() == server_id) { EQ::Net::DynamicPacket outapp; outapp.Resize(sizeof(UsertoWorldRequest_Struct)); @@ -316,8 +296,8 @@ bool ServerManager::ServerExists( WorldServer *ignore ) { - auto iter = world_servers.begin(); - while (iter != world_servers.end()) { + auto iter = m_world_servers.begin(); + while (iter != m_world_servers.end()) { if ((*iter).get() == ignore) { ++iter; continue; @@ -343,8 +323,8 @@ void ServerManager::DestroyServerByName( WorldServer *ignore ) { - auto iter = world_servers.begin(); - while (iter != world_servers.end()) { + auto iter = m_world_servers.begin(); + while (iter != m_world_servers.end()) { if ((*iter).get() == ignore) { ++iter; continue; @@ -353,7 +333,7 @@ void ServerManager::DestroyServerByName( if ((*iter)->GetServerLongName().compare(server_long_name) == 0 && (*iter)->GetServerShortName().compare(server_short_name) == 0) { (*iter)->GetConnection()->Handle()->Disconnect(); - iter = world_servers.erase(iter); + iter = m_world_servers.erase(iter); continue; } @@ -366,5 +346,5 @@ void ServerManager::DestroyServerByName( */ const std::list> &ServerManager::getWorldServers() const { - return world_servers; + return m_world_servers; } diff --git a/loginserver/server_manager.h b/loginserver/server_manager.h index 8babf8a56..c89c7c85e 100644 --- a/loginserver/server_manager.h +++ b/loginserver/server_manager.h @@ -1,23 +1,3 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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_SERVERMANAGER_H #define EQEMU_SERVERMANAGER_H @@ -103,8 +83,8 @@ private: */ WorldServer *GetServerByAddress(const std::string &ip_address, int port); - std::unique_ptr server_connection; - std::list> world_servers; + std::unique_ptr m_server_connection; + std::list> m_world_servers; }; diff --git a/loginserver/world_server.cpp b/loginserver/world_server.cpp index 90b4ea160..d6917c845 100644 --- a/loginserver/world_server.cpp +++ b/loginserver/world_server.cpp @@ -1,28 +1,8 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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 - * - */ - #include "world_server.h" #include "login_server.h" #include "login_structures.h" -#include "../common/eqemu_logsys.h" #include "../common/ip_util.h" +#include "../common/string_util.h" extern LoginServer server; @@ -31,16 +11,16 @@ extern LoginServer server; */ WorldServer::WorldServer(std::shared_ptr worldserver_connection) { - connection = worldserver_connection; - zones_booted = 0; - players_online = 0; - server_status = 0; - server_id = 0; - server_list_type_id = 0; - server_process_type = 0; - is_server_authorized = false; - is_server_trusted = false; - is_server_logged_in = false; + m_connection = worldserver_connection; + m_zones_booted = 0; + m_players_online = 0; + m_server_status = 0; + m_server_id = 0; + m_server_list_type_id = 0; + m_server_process_type = 0; + m_is_server_authorized = false; + m_is_server_trusted = false; + m_is_server_logged_in = false; worldserver_connection->OnMessage( ServerOP_NewLSInfo, @@ -72,21 +52,25 @@ WorldServer::WorldServer(std::shared_ptr wo std::bind(&WorldServer::ProcessLSAccountUpdate, this, std::placeholders::_1, std::placeholders::_2) ); - m_keepalive = std::make_unique(1000, true, std::bind(&WorldServer::OnKeepAlive, this, std::placeholders::_1)); + m_keepalive = std::make_unique( + 1000, + true, + std::bind(&WorldServer::OnKeepAlive, this, std::placeholders::_1) + ); } WorldServer::~WorldServer() = default; void WorldServer::Reset() { - server_id; - zones_booted = 0; - players_online = 0; - server_status = 0; - server_list_type_id = 0; - server_process_type = 0; - is_server_authorized = false; - is_server_logged_in = false; + m_server_id; + m_zones_booted = 0; + m_players_online = 0; + m_server_status = 0; + m_server_list_type_id = 0; + m_server_process_type = 0; + m_is_server_authorized = false; + m_is_server_logged_in = false; } /** @@ -116,9 +100,15 @@ void WorldServer::ProcessNewLSInfo(uint16_t opcode, const EQ::Net::Packet &packe return; } - auto *info = (ServerNewLSInfo_Struct *) packet.Data(); + // if for whatever reason the world server is not sending an address, use the local address it sends + std::string remote_ip_addr = info->remote_ip_address; + std::string local_ip_addr = info->local_ip_address; + if (remote_ip_addr.empty() && !local_ip_addr.empty() && local_ip_addr != "127.0.0.1") { + strcpy(info->remote_ip_address, local_ip_addr.c_str()); + } + LogInfo( "New World Server Info | name [{0}] shortname [{1}] remote_address [{2}] local_address [{3}] account [{4}] password [{5}] server_type [{6}]", info->server_long_name, @@ -443,7 +433,7 @@ void WorldServer::ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet } if (server.options.IsWorldTraceOn()) { - LogDebug("ServerOP_LSAccountUpdate packet received from [{0}]", short_name); + LogDebug("ServerOP_LSAccountUpdate packet received from [{0}]", m_short_name); } auto *loginserver_update = (ServerLSAccountUpdate_Struct *) packet.Data(); @@ -486,6 +476,8 @@ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info return; } + SanitizeWorldServerName(new_world_server_info_packet->server_long_name); + SetAccountPassword(new_world_server_info_packet->account_password) ->SetLongName(new_world_server_info_packet->server_long_name) ->SetShortName(new_world_server_info_packet->server_short_name) @@ -506,8 +498,8 @@ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info else { if (server.server_manager->ServerExists(GetServerLongName(), GetServerShortName(), this)) { LogInfo("World tried to login but there already exists a server that has that name, destroying [{}]", - long_name); - server.server_manager->DestroyServerByName(long_name, short_name, this); + m_long_name); + server.server_manager->DestroyServerByName(m_long_name, m_short_name, this); } } @@ -551,6 +543,7 @@ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info Database::DbWorldRegistration world_registration = server.db->GetWorldRegistration( GetServerShortName(), + GetServerLongName(), world_server_admin_id ); @@ -619,7 +612,7 @@ void WorldServer::SendClientAuth( strncpy(client_auth.loginserver_name, &loginserver_name[0], 64); const std::string &client_address(ip); - std::string world_address(connection->Handle()->RemoteIP()); + std::string world_address(m_connection->Handle()->RemoteIP()); if (client_address == world_address) { client_auth.is_client_from_local_network = 1; @@ -655,13 +648,22 @@ void WorldServer::SendClientAuth( ); outapp.PutSerialize(0, client_auth); - connection->Send(ServerOP_LSClientAuth, outapp); + m_connection->Send(ServerOP_LSClientAuth, outapp); if (server.options.IsDumpInPacketsOn()) { DumpPacket(ServerOP_LSClientAuth, outapp); } } +constexpr static int MAX_ACCOUNT_NAME_LENGTH = 30; +constexpr static int MAX_ACCOUNT_PASSWORD_LENGTH = 30; +constexpr static int MAX_SERVER_LONG_NAME_LENGTH = 200; +constexpr static int MAX_SERVER_SHORT_NAME_LENGTH = 50; +constexpr static int MAX_SERVER_LOCAL_ADDRESS_LENGTH = 125; +constexpr static int MAX_SERVER_REMOTE_ADDRESS_LENGTH = 125; +constexpr static int MAX_SERVER_VERSION_LENGTH = 64; +constexpr static int MAX_SERVER_PROTOCOL_VERSION = 25; + /** * @param new_world_server_info_packet * @return @@ -670,41 +672,32 @@ bool WorldServer::HandleNewLoginserverInfoValidation( ServerNewLSInfo_Struct *new_world_server_info_packet ) { - const int max_account_name_length = 30; - const int max_account_password_length = 30; - const int max_server_long_name_length = 200; - const int max_server_short_name_length = 50; - const int max_server_local_address_length = 125; - const int max_server_remote_address_length = 125; - const int max_server_version_length = 64; - const int max_server_protocol_version = 25; - - if (strlen(new_world_server_info_packet->account_name) >= max_account_name_length) { - LogError("Handle_NewLSInfo error [account_name] was too long | max [{0}]", max_account_name_length); + if (strlen(new_world_server_info_packet->account_name) >= MAX_ACCOUNT_NAME_LENGTH) { + LogError("Handle_NewLSInfo error [account_name] was too long | max [{0}]", MAX_ACCOUNT_NAME_LENGTH); return false; } - else if (strlen(new_world_server_info_packet->account_password) >= max_account_password_length) { - LogError("Handle_NewLSInfo error [account_password] was too long | max [{0}]", max_account_password_length); + else if (strlen(new_world_server_info_packet->account_password) >= MAX_ACCOUNT_PASSWORD_LENGTH) { + LogError("Handle_NewLSInfo error [account_password] was too long | max [{0}]", MAX_ACCOUNT_PASSWORD_LENGTH); return false; } - else if (strlen(new_world_server_info_packet->server_long_name) >= max_server_long_name_length) { - LogError("Handle_NewLSInfo error [server_long_name] was too long | max [{0}]", max_server_long_name_length); + else if (strlen(new_world_server_info_packet->server_long_name) >= MAX_SERVER_LONG_NAME_LENGTH) { + LogError("Handle_NewLSInfo error [server_long_name] was too long | max [{0}]", MAX_SERVER_LONG_NAME_LENGTH); return false; } - else if (strlen(new_world_server_info_packet->server_short_name) >= max_server_short_name_length) { - LogError("Handle_NewLSInfo error [server_short_name] was too long | max [{0}]", max_server_short_name_length); + else if (strlen(new_world_server_info_packet->server_short_name) >= MAX_SERVER_SHORT_NAME_LENGTH) { + LogError("Handle_NewLSInfo error [server_short_name] was too long | max [{0}]", MAX_SERVER_SHORT_NAME_LENGTH); return false; } - else if (strlen(new_world_server_info_packet->server_version) >= max_server_short_name_length) { - LogError("Handle_NewLSInfo error [server_version] was too long | max [{0}]", max_server_version_length); + else if (strlen(new_world_server_info_packet->server_version) >= MAX_SERVER_VERSION_LENGTH) { + LogError("Handle_NewLSInfo error [server_version] was too long | max [{0}]", MAX_SERVER_VERSION_LENGTH); return false; } - else if (strlen(new_world_server_info_packet->protocol_version) >= max_server_protocol_version) { - LogError("Handle_NewLSInfo error [protocol_version] was too long | max [{0}]", max_server_protocol_version); + else if (strlen(new_world_server_info_packet->protocol_version) >= MAX_SERVER_PROTOCOL_VERSION) { + LogError("Handle_NewLSInfo error [protocol_version] was too long | max [{0}]", MAX_SERVER_PROTOCOL_VERSION); return false; } - if (strlen(new_world_server_info_packet->local_ip_address) <= max_server_local_address_length) { + if (strlen(new_world_server_info_packet->local_ip_address) <= MAX_SERVER_LOCAL_ADDRESS_LENGTH) { if (strlen(new_world_server_info_packet->local_ip_address) == 0) { LogError("Handle_NewLSInfo error, local address was null, defaulting to localhost"); SetLocalIp("127.0.0.1"); @@ -714,17 +707,17 @@ bool WorldServer::HandleNewLoginserverInfoValidation( } } else { - LogError("Handle_NewLSInfo error, local address was too long | max [{0}]", max_server_local_address_length); + LogError("Handle_NewLSInfo error, local address was too long | max [{0}]", MAX_SERVER_LOCAL_ADDRESS_LENGTH); return false; } - if (strlen(new_world_server_info_packet->remote_ip_address) <= max_server_remote_address_length) { + if (strlen(new_world_server_info_packet->remote_ip_address) <= MAX_SERVER_REMOTE_ADDRESS_LENGTH) { if (strlen(new_world_server_info_packet->remote_ip_address) == 0) { SetRemoteIp(GetConnection()->Handle()->RemoteIP()); LogWarning( "Remote address was null, defaulting to stream address [{0}]", - remote_ip_address + m_remote_ip_address ); } else { @@ -736,7 +729,7 @@ bool WorldServer::HandleNewLoginserverInfoValidation( LogWarning( "Handle_NewLSInfo remote address was too long, defaulting to stream address [{0}]", - remote_ip_address + m_remote_ip_address ); } @@ -796,7 +789,7 @@ bool WorldServer::HandleNewLoginserverRegisteredOnly( if (IsServerTrusted()) { LogDebug("WorldServer::HandleNewLoginserverRegisteredOnly | ServerOP_LSAccountUpdate sent to world"); EQ::Net::DynamicPacket outapp; - connection->Send(ServerOP_LSAccountUpdate, outapp); + m_connection->Send(ServerOP_LSAccountUpdate, outapp); } } else { @@ -847,6 +840,8 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed( ->SetIsServerTrusted(world_registration.is_server_trusted) ->SetServerListTypeId(world_registration.server_list_type); + SetIsServerAuthorized(true); + bool does_world_server_pass_authentication_check = ( world_registration.server_admin_account_name == GetAccountName() && WorldServer::ValidateWorldServerAdminLogin( @@ -864,7 +859,7 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed( if (does_world_server_have_non_empty_credentials) { if (does_world_server_pass_authentication_check) { - SetIsServerAuthorized(true); + SetIsServerLoggedIn(true); LogInfo( "Server long_name [{0}] short_name [{1}] successfully logged in", @@ -875,16 +870,13 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed( if (IsServerTrusted()) { LogDebug("WorldServer::HandleNewLoginserverRegisteredOnly | ServerOP_LSAccountUpdate sent to world"); EQ::Net::DynamicPacket outapp; - connection->Send(ServerOP_LSAccountUpdate, outapp); + m_connection->Send(ServerOP_LSAccountUpdate, outapp); } } else { - - /** - * this is the first of two cases where we should deny access even if unregistered is allowed - */ + // server is authorized to be on the loginserver list but didn't pass login check LogInfo( - "Server long_name [{0}] short_name [{1}] attempted to log in but account and password did not match the entry in the database.", + "Server long_name [{0}] short_name [{1}] attempted to log in but account and password did not match the entry in the database. Unregistered still allowed", GetServerLongName(), GetServerShortName() ); @@ -892,9 +884,7 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed( } else { - /** - * this is the second of two cases where we should deny access even if unregistered is allowed - */ + // server is authorized to be on the loginserver list but didn't pass login check if (!GetAccountName().empty() || !GetAccountPassword().empty()) { LogInfo( "Server [{0}] [{1}] did not login but this server required a password to login", @@ -903,7 +893,6 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed( ); } else { - SetIsServerAuthorized(true); LogInfo( "Server [{0}] [{1}] did not login but unregistered servers are allowed", GetServerLongName(), @@ -939,14 +928,12 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed( } } - /** - * Auto create a registration - */ + // Auto create a registration if (!server.db->CreateWorldRegistration( GetServerLongName(), GetServerShortName(), GetRemoteIp(), - server_id, + m_server_id, server_admin_id )) { return false; @@ -1044,7 +1031,7 @@ bool WorldServer::ValidateWorldServerAdminLogin( */ WorldServer *WorldServer::SetServerListTypeId(unsigned int in_server_list_id) { - server_list_type_id = in_server_list_id; + m_server_list_type_id = in_server_list_id; return this; } @@ -1054,7 +1041,7 @@ WorldServer *WorldServer::SetServerListTypeId(unsigned int in_server_list_id) */ const std::string &WorldServer::GetServerDescription() const { - return server_description; + return m_server_description; } /** @@ -1062,7 +1049,7 @@ const std::string &WorldServer::GetServerDescription() const */ WorldServer *WorldServer::SetServerDescription(const std::string &in_server_description) { - WorldServer::server_description = in_server_description; + WorldServer::m_server_description = in_server_description; return this; } @@ -1072,7 +1059,7 @@ WorldServer *WorldServer::SetServerDescription(const std::string &in_server_desc */ bool WorldServer::IsServerAuthorized() const { - return is_server_authorized; + return m_is_server_authorized; } /** @@ -1080,7 +1067,7 @@ bool WorldServer::IsServerAuthorized() const */ WorldServer *WorldServer::SetIsServerAuthorized(bool in_is_server_authorized) { - WorldServer::is_server_authorized = in_is_server_authorized; + WorldServer::m_is_server_authorized = in_is_server_authorized; return this; } @@ -1090,7 +1077,7 @@ WorldServer *WorldServer::SetIsServerAuthorized(bool in_is_server_authorized) */ bool WorldServer::IsServerLoggedIn() const { - return is_server_logged_in; + return m_is_server_logged_in; } /** @@ -1098,7 +1085,7 @@ bool WorldServer::IsServerLoggedIn() const */ WorldServer *WorldServer::SetIsServerLoggedIn(bool in_is_server_logged_in) { - WorldServer::is_server_logged_in = in_is_server_logged_in; + WorldServer::m_is_server_logged_in = in_is_server_logged_in; return this; } @@ -1108,7 +1095,7 @@ WorldServer *WorldServer::SetIsServerLoggedIn(bool in_is_server_logged_in) */ bool WorldServer::IsServerTrusted() const { - return is_server_trusted; + return m_is_server_trusted; } /** @@ -1116,7 +1103,7 @@ bool WorldServer::IsServerTrusted() const */ WorldServer *WorldServer::SetIsServerTrusted(bool in_is_server_trusted) { - WorldServer::is_server_trusted = in_is_server_trusted; + WorldServer::m_is_server_trusted = in_is_server_trusted; return this; } @@ -1126,7 +1113,7 @@ WorldServer *WorldServer::SetIsServerTrusted(bool in_is_server_trusted) */ WorldServer *WorldServer::SetZonesBooted(unsigned int in_zones_booted) { - WorldServer::zones_booted = in_zones_booted; + WorldServer::m_zones_booted = in_zones_booted; return this; } @@ -1136,7 +1123,7 @@ WorldServer *WorldServer::SetZonesBooted(unsigned int in_zones_booted) */ WorldServer *WorldServer::SetPlayersOnline(unsigned int in_players_online) { - WorldServer::players_online = in_players_online; + WorldServer::m_players_online = in_players_online; return this; } @@ -1146,7 +1133,7 @@ WorldServer *WorldServer::SetPlayersOnline(unsigned int in_players_online) */ WorldServer *WorldServer::SetServerStatus(int in_server_status) { - WorldServer::server_status = in_server_status; + WorldServer::m_server_status = in_server_status; return this; } @@ -1156,7 +1143,7 @@ WorldServer *WorldServer::SetServerStatus(int in_server_status) */ WorldServer *WorldServer::SetServerProcessType(unsigned int in_server_process_type) { - WorldServer::server_process_type = in_server_process_type; + WorldServer::m_server_process_type = in_server_process_type; return this; } @@ -1166,7 +1153,7 @@ WorldServer *WorldServer::SetServerProcessType(unsigned int in_server_process_ty */ WorldServer *WorldServer::SetLongName(const std::string &in_long_name) { - WorldServer::long_name = in_long_name; + WorldServer::m_long_name = in_long_name; return this; } @@ -1176,7 +1163,7 @@ WorldServer *WorldServer::SetLongName(const std::string &in_long_name) */ WorldServer *WorldServer::SetShortName(const std::string &in_short_name) { - WorldServer::short_name = in_short_name; + WorldServer::m_short_name = in_short_name; return this; } @@ -1186,7 +1173,7 @@ WorldServer *WorldServer::SetShortName(const std::string &in_short_name) */ WorldServer *WorldServer::SetAccountName(const std::string &in_account_name) { - WorldServer::account_name = in_account_name; + WorldServer::m_account_name = in_account_name; return this; } @@ -1196,7 +1183,7 @@ WorldServer *WorldServer::SetAccountName(const std::string &in_account_name) */ WorldServer *WorldServer::SetAccountPassword(const std::string &in_account_password) { - WorldServer::account_password = in_account_password; + WorldServer::m_account_password = in_account_password; return this; } @@ -1206,7 +1193,7 @@ WorldServer *WorldServer::SetAccountPassword(const std::string &in_account_passw */ WorldServer *WorldServer::SetRemoteIp(const std::string &in_remote_ip) { - WorldServer::remote_ip_address = in_remote_ip; + WorldServer::m_remote_ip_address = in_remote_ip; return this; } @@ -1216,7 +1203,7 @@ WorldServer *WorldServer::SetRemoteIp(const std::string &in_remote_ip) */ WorldServer *WorldServer::SetLocalIp(const std::string &in_local_ip) { - WorldServer::local_ip = in_local_ip; + WorldServer::m_local_ip = in_local_ip; return this; } @@ -1226,7 +1213,7 @@ WorldServer *WorldServer::SetLocalIp(const std::string &in_local_ip) */ WorldServer *WorldServer::SetProtocol(const std::string &in_protocol) { - WorldServer::protocol = in_protocol; + WorldServer::m_protocol = in_protocol; return this; } @@ -1236,7 +1223,7 @@ WorldServer *WorldServer::SetProtocol(const std::string &in_protocol) */ WorldServer *WorldServer::SetVersion(const std::string &in_version) { - WorldServer::version = in_version; + WorldServer::m_version = in_version; return this; } @@ -1246,7 +1233,7 @@ WorldServer *WorldServer::SetVersion(const std::string &in_version) */ int WorldServer::GetServerStatus() const { - return server_status; + return m_server_status; } /** @@ -1254,7 +1241,7 @@ int WorldServer::GetServerStatus() const */ unsigned int WorldServer::GetServerListTypeId() const { - return server_list_type_id; + return m_server_list_type_id; } /** @@ -1262,7 +1249,7 @@ unsigned int WorldServer::GetServerListTypeId() const */ unsigned int WorldServer::GetServerProcessType() const { - return server_process_type; + return m_server_process_type; } /** @@ -1270,7 +1257,7 @@ unsigned int WorldServer::GetServerProcessType() const */ const std::string &WorldServer::GetAccountName() const { - return account_name; + return m_account_name; } /** @@ -1278,7 +1265,7 @@ const std::string &WorldServer::GetAccountName() const */ const std::string &WorldServer::GetAccountPassword() const { - return account_password; + return m_account_password; } /** @@ -1286,7 +1273,7 @@ const std::string &WorldServer::GetAccountPassword() const */ const std::string &WorldServer::GetRemoteIp() const { - return remote_ip_address; + return m_remote_ip_address; } /** @@ -1294,7 +1281,7 @@ const std::string &WorldServer::GetRemoteIp() const */ const std::string &WorldServer::GetLocalIp() const { - return local_ip; + return m_local_ip; } /** @@ -1302,7 +1289,7 @@ const std::string &WorldServer::GetLocalIp() const */ const std::string &WorldServer::GetProtocol() const { - return protocol; + return m_protocol; } /** @@ -1310,11 +1297,11 @@ const std::string &WorldServer::GetProtocol() const */ const std::string &WorldServer::GetVersion() const { - return version; + return m_version; } void WorldServer::OnKeepAlive(EQ::Timer *t) { ServerPacket pack(ServerOP_KeepAlive, 0); - connection->SendPacket(&pack); + m_connection->SendPacket(&pack); } diff --git a/loginserver/world_server.h b/loginserver/world_server.h index 04db63924..f3e533b91 100644 --- a/loginserver/world_server.h +++ b/loginserver/world_server.h @@ -1,23 +1,3 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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_WORLDSERVER_H #define EQEMU_WORLDSERVER_H @@ -50,44 +30,44 @@ public: /** * Accesses connection, it is intentional that this is not const (trust me). */ - std::shared_ptr GetConnection() { return connection; } - void SetConnection(std::shared_ptr c) { connection = c; } + std::shared_ptr GetConnection() { return m_connection; } + void SetConnection(std::shared_ptr c) { m_connection = c; } /** * @return */ - unsigned int GetServerId() const { return server_id; } + unsigned int GetServerId() const { return m_server_id; } WorldServer *SetServerId(unsigned int id) { - server_id = id; + m_server_id = id; return this; } /** * @return */ - std::string GetServerLongName() const { return long_name; } - std::string GetServerShortName() const { return short_name; } + std::string GetServerLongName() const { return m_long_name; } + std::string GetServerShortName() const { return m_short_name; } /** * Gets whether the server is authorized to show up on the server list or not * @return */ - bool IsAuthorized() const { return is_server_authorized; } - std::string GetLocalIP() const { return local_ip; } - std::string GetRemoteIP() const { return remote_ip_address; } + bool IsAuthorized() const { return m_is_server_authorized; } + std::string GetLocalIP() const { return m_local_ip; } + std::string GetRemoteIP() const { return m_remote_ip_address; } /** * Gets what kind of server this server is (legends, preferred, normal) * * @return */ - unsigned int GetServerListID() const { return server_list_type_id; } + unsigned int GetServerListID() const { return m_server_list_type_id; } WorldServer *SetServerListTypeId(unsigned int in_server_list_id); - int GetStatus() const { return server_status; } - unsigned int GetZonesBooted() const { return zones_booted; } - unsigned int GetPlayersOnline() const { return players_online; } + int GetStatus() const { return m_server_status; } + unsigned int GetZonesBooted() const { return m_zones_booted; } + unsigned int GetPlayersOnline() const { return m_players_online; } /** * Takes the info struct we received from world and processes it @@ -184,27 +164,26 @@ private: void ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Packet &packet); void ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet &packet); - std::shared_ptr connection; + std::shared_ptr m_connection; - unsigned int zones_booted; - unsigned int players_online; - int server_status; - unsigned int server_id; - unsigned int server_list_type_id; - unsigned int server_process_type; - std::string server_description; - std::string long_name; - std::string short_name; - std::string account_name; - std::string account_password; - std::string remote_ip_address; - std::string local_ip; - std::string protocol; - std::string version; - - bool is_server_authorized; - bool is_server_logged_in; - bool is_server_trusted; + unsigned int m_zones_booted; + unsigned int m_players_online; + int m_server_status; + unsigned int m_server_id; + unsigned int m_server_list_type_id; + unsigned int m_server_process_type; + std::string m_server_description; + std::string m_long_name; + std::string m_short_name; + std::string m_account_name; + std::string m_account_password; + std::string m_remote_ip_address; + std::string m_local_ip; + std::string m_protocol; + std::string m_version; + bool m_is_server_authorized; + bool m_is_server_logged_in; + bool m_is_server_trusted; /** * Keepalive diff --git a/world/login_server.cpp b/world/login_server.cpp index 8d93a6128..0dc665f36 100644 --- a/world/login_server.cpp +++ b/world/login_server.cpp @@ -46,12 +46,12 @@ extern volatile bool RunLoops; LoginServer::LoginServer(const char *iAddress, uint16 iPort, const char *Account, const char *Password, bool legacy) { - strn0cpy(LoginServerAddress, iAddress, 256); - LoginServerPort = iPort; - LoginAccount = Account; - LoginPassword = Password; - CanAccountUpdate = false; - IsLegacy = legacy; + strn0cpy(m_loginserver_address, iAddress, 256); + m_loginserver_port = iPort; + m_login_account = Account; + m_login_password = Password; + m_can_account_update = false; + m_is_legacy = legacy; Connect(); } @@ -93,7 +93,10 @@ void LoginServer::ProcessUsertoWorldReqLeg(uint16_t opcode, EQ::Net::Packet &p) if (Config->Locked) { if (status < (RuleI(GM, MinStatusToBypassLockedServer))) { - LogDebug("[ProcessUsertoWorldReqLeg] Server locked and status is not high enough for account_id [{0}]", utwr->lsaccountid); + LogDebug( + "[ProcessUsertoWorldReqLeg] Server locked and status is not high enough for account_id [{0}]", + utwr->lsaccountid + ); utwrs->response = UserToWorldStatusWorldUnavail; SendPacket(&outpack); return; @@ -171,7 +174,10 @@ void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p) if (Config->Locked == true) { if (status < (RuleI(GM, MinStatusToBypassLockedServer))) { - LogDebug("[ProcessUsertoWorldReq] Server locked and status is not high enough for account_id [{0}]", utwr->lsaccountid); + LogDebug( + "[ProcessUsertoWorldReq] Server locked and status is not high enough for account_id [{0}]", + utwr->lsaccountid + ); utwrs->response = UserToWorldStatusWorldUnavail; SendPacket(&outpack); return; @@ -320,61 +326,63 @@ void LoginServer::ProcessLSAccountUpdate(uint16_t opcode, EQ::Net::Packet &p) LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode); LogNetcode("Received ServerOP_LSAccountUpdate packet from loginserver"); - CanAccountUpdate = true; + m_can_account_update = true; } bool LoginServer::Connect() { char errbuf[1024]; - if ((LoginServerIP = ResolveIP(LoginServerAddress, errbuf)) == 0) { - LogInfo("Unable to resolve [{}] to an IP", LoginServerAddress); + if ((m_loginserver_ip = ResolveIP(m_loginserver_address, errbuf)) == 0) { + LogInfo("Unable to resolve [{}] to an IP", m_loginserver_address); return false; } - if (LoginServerIP == 0 || LoginServerPort == 0) { + if (m_loginserver_ip == 0 || m_loginserver_port == 0) { LogInfo( "Connect info incomplete, cannot connect: [{0}:{1}]", - LoginServerAddress, - LoginServerPort + m_loginserver_address, + m_loginserver_port ); return false; } - if (IsLegacy) { - legacy_client = std::make_unique(LoginServerAddress, LoginServerPort, false); - legacy_client->OnConnect( + if (m_is_legacy) { + m_legacy_client = std::make_unique( + m_loginserver_address, + m_loginserver_port, + false + ); + m_legacy_client->OnConnect( [this](EQ::Net::ServertalkLegacyClient *client) { if (client) { LogInfo( "Connected to Legacy Loginserver: [{0}:{1}]", - LoginServerAddress, - LoginServerPort + m_loginserver_address, + m_loginserver_port ); SendInfo(); SendStatus(); zoneserver_list.SendLSZones(); - statusupdate_timer = std::make_unique( - - LoginServer_StatusUpdateInterval, true, [this](EQ::Timer *t) { - SendStatus(); - } - + m_statusupdate_timer = std::make_unique( + LoginServer_StatusUpdateInterval, true, [this](EQ::Timer *t) { + SendStatus(); + } ); } else { LogInfo( "Could not connect to Legacy Loginserver: [{0}:{1}]", - LoginServerAddress, - LoginServerPort + m_loginserver_address, + m_loginserver_port ); } } ); - legacy_client->OnMessage( + m_legacy_client->OnMessage( ServerOP_UsertoWorldReqLeg, std::bind( &LoginServer::ProcessUsertoWorldReqLeg, @@ -383,7 +391,7 @@ bool LoginServer::Connect() std::placeholders::_2 ) ); - legacy_client->OnMessage( + m_legacy_client->OnMessage( ServerOP_UsertoWorldReq, std::bind( &LoginServer::ProcessUsertoWorldReq, @@ -392,7 +400,7 @@ bool LoginServer::Connect() std::placeholders::_2 ) ); - legacy_client->OnMessage( + m_legacy_client->OnMessage( ServerOP_LSClientAuthLeg, std::bind( &LoginServer::ProcessLSClientAuthLegacy, @@ -401,7 +409,7 @@ bool LoginServer::Connect() std::placeholders::_2 ) ); - legacy_client->OnMessage( + m_legacy_client->OnMessage( ServerOP_LSClientAuth, std::bind( &LoginServer::ProcessLSClientAuth, @@ -410,7 +418,7 @@ bool LoginServer::Connect() std::placeholders::_2 ) ); - legacy_client->OnMessage( + m_legacy_client->OnMessage( ServerOP_LSFatalError, std::bind( &LoginServer::ProcessLSFatalError, @@ -419,7 +427,7 @@ bool LoginServer::Connect() std::placeholders::_2 ) ); - legacy_client->OnMessage( + m_legacy_client->OnMessage( ServerOP_SystemwideMessage, std::bind( &LoginServer::ProcessSystemwideMessage, @@ -428,7 +436,7 @@ bool LoginServer::Connect() std::placeholders::_2 ) ); - legacy_client->OnMessage( + m_legacy_client->OnMessage( ServerOP_LSRemoteAddr, std::bind( &LoginServer::ProcessLSRemoteAddr, @@ -437,7 +445,7 @@ bool LoginServer::Connect() std::placeholders::_2 ) ); - legacy_client->OnMessage( + m_legacy_client->OnMessage( ServerOP_LSAccountUpdate, std::bind( &LoginServer::ProcessLSAccountUpdate, @@ -448,37 +456,43 @@ bool LoginServer::Connect() ); } else { - client = std::make_unique(LoginServerAddress, LoginServerPort, false, "World", ""); - client->OnConnect( + m_client = std::make_unique( + m_loginserver_address, + m_loginserver_port, + false, + "World", + "" + ); + m_client->OnConnect( [this](EQ::Net::ServertalkClient *client) { if (client) { LogInfo( "Connected to Loginserver: [{0}:{1}]", - LoginServerAddress, - LoginServerPort + m_loginserver_address, + m_loginserver_port ); SendInfo(); SendStatus(); zoneserver_list.SendLSZones(); - statusupdate_timer = std::make_unique( + m_statusupdate_timer = std::make_unique( - LoginServer_StatusUpdateInterval, true, [this](EQ::Timer *t) { - SendStatus(); - } - ); + LoginServer_StatusUpdateInterval, true, [this](EQ::Timer *t) { + SendStatus(); + } + ); } else { LogInfo( "Could not connect to Loginserver: [{0}:{1}]", - LoginServerAddress, - LoginServerPort + m_loginserver_address, + m_loginserver_port ); } } ); - client->OnMessage( + m_client->OnMessage( ServerOP_UsertoWorldReqLeg, std::bind( &LoginServer::ProcessUsertoWorldReqLeg, @@ -487,7 +501,7 @@ bool LoginServer::Connect() std::placeholders::_2 ) ); - client->OnMessage( + m_client->OnMessage( ServerOP_UsertoWorldReq, std::bind( &LoginServer::ProcessUsertoWorldReq, @@ -496,7 +510,7 @@ bool LoginServer::Connect() std::placeholders::_2 ) ); - client->OnMessage( + m_client->OnMessage( ServerOP_LSClientAuthLeg, std::bind( &LoginServer::ProcessLSClientAuthLegacy, @@ -505,7 +519,7 @@ bool LoginServer::Connect() std::placeholders::_2 ) ); - client->OnMessage( + m_client->OnMessage( ServerOP_LSClientAuth, std::bind( &LoginServer::ProcessLSClientAuth, @@ -514,7 +528,7 @@ bool LoginServer::Connect() std::placeholders::_2 ) ); - client->OnMessage( + m_client->OnMessage( ServerOP_LSFatalError, std::bind( &LoginServer::ProcessLSFatalError, @@ -523,7 +537,7 @@ bool LoginServer::Connect() std::placeholders::_2 ) ); - client->OnMessage( + m_client->OnMessage( ServerOP_SystemwideMessage, std::bind( &LoginServer::ProcessSystemwideMessage, @@ -532,7 +546,7 @@ bool LoginServer::Connect() std::placeholders::_2 ) ); - client->OnMessage( + m_client->OnMessage( ServerOP_LSRemoteAddr, std::bind( &LoginServer::ProcessLSRemoteAddr, @@ -541,7 +555,7 @@ bool LoginServer::Connect() std::placeholders::_2 ) ); - client->OnMessage( + m_client->OnMessage( ServerOP_LSAccountUpdate, std::bind( &LoginServer::ProcessLSAccountUpdate, @@ -552,7 +566,10 @@ bool LoginServer::Connect() ); } - m_keepalive = std::make_unique(1000, true, std::bind(&LoginServer::OnKeepAlive, this, std::placeholders::_1)); + m_keepalive = std::make_unique( + 1000, + true, + std::bind(&LoginServer::OnKeepAlive, this, std::placeholders::_1)); return true; } @@ -566,28 +583,44 @@ void LoginServer::SendInfo() pack->size = sizeof(ServerNewLSInfo_Struct); pack->pBuffer = new uchar[pack->size]; memset(pack->pBuffer, 0, pack->size); - ServerNewLSInfo_Struct *lsi = (ServerNewLSInfo_Struct *) pack->pBuffer; - strcpy(lsi->protocol_version, EQEMU_PROTOCOL_VERSION); - strcpy(lsi->server_version, LOGIN_VERSION); - strcpy(lsi->server_long_name, Config->LongName.c_str()); - strcpy(lsi->server_short_name, Config->ShortName.c_str()); - strn0cpy(lsi->account_name, LoginAccount.c_str(), 30); - strn0cpy(lsi->account_password, LoginPassword.c_str(), 30); + + auto *l = (ServerNewLSInfo_Struct *) pack->pBuffer; + strcpy(l->protocol_version, EQEMU_PROTOCOL_VERSION); + strcpy(l->server_version, LOGIN_VERSION); + strcpy(l->server_long_name, Config->LongName.c_str()); + strcpy(l->server_short_name, Config->ShortName.c_str()); + strn0cpy(l->account_name, m_login_account.c_str(), 30); + strn0cpy(l->account_password, m_login_password.c_str(), 30); if (Config->WorldAddress.length()) { - strcpy(lsi->remote_ip_address, Config->WorldAddress.c_str()); + strcpy(l->remote_ip_address, Config->WorldAddress.c_str()); } if (Config->LocalAddress.length()) { - strcpy(lsi->local_ip_address, Config->LocalAddress.c_str()); + strcpy(l->local_ip_address, Config->LocalAddress.c_str()); } else { - auto local_addr = IsLegacy ? legacy_client->Handle()->LocalIP() : client->Handle()->LocalIP(); - strcpy(lsi->local_ip_address, local_addr.c_str()); - WorldConfig::SetLocalAddress(lsi->local_ip_address); + auto local_addr = m_is_legacy ? m_legacy_client->Handle()->LocalIP() : m_client->Handle()->LocalIP(); + strcpy(l->local_ip_address, local_addr.c_str()); + WorldConfig::SetLocalAddress(l->local_ip_address); } + + SanitizeWorldServerName(l->server_long_name); + + LogInfo( + "[LoginServer::SendInfo] protocol_version [{}] server_version [{}] long_name [{}] short_name [{}] account_name [{}] remote_ip_address [{}] local_ip [{}]", + l->protocol_version, + l->server_version, + l->server_long_name, + l->server_short_name, + l->account_name, + l->remote_ip_address, + l->local_ip_address + ); + SendPacket(pack); delete pack; } + void LoginServer::SendStatus() { auto pack = new ServerPacket; @@ -618,14 +651,14 @@ void LoginServer::SendStatus() */ void LoginServer::SendPacket(ServerPacket *pack) { - if (IsLegacy) { - if (legacy_client) { - legacy_client->SendPacket(pack); + if (m_is_legacy) { + if (m_legacy_client) { + m_legacy_client->SendPacket(pack); } } else { - if (client) { - client->SendPacket(pack); + if (m_client) { + m_client->SendPacket(pack); } } } @@ -636,11 +669,11 @@ void LoginServer::SendAccountUpdate(ServerPacket *pack) if (CanUpdate()) { LogInfo( "Sending ServerOP_LSAccountUpdate packet to loginserver: [{0}]:[{1}]", - LoginServerAddress, - LoginServerPort + m_loginserver_address, + m_loginserver_port ); - strn0cpy(ls_account_update->worldaccount, LoginAccount.c_str(), 30); - strn0cpy(ls_account_update->worldpassword, LoginPassword.c_str(), 30); + strn0cpy(ls_account_update->worldaccount, m_login_account.c_str(), 30); + strn0cpy(ls_account_update->worldpassword, m_login_password.c_str(), 30); SendPacket(pack); } } diff --git a/world/login_server.h b/world/login_server.h index 16fde8e65..21e25870f 100644 --- a/world/login_server.h +++ b/world/login_server.h @@ -1,20 +1,3 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) - - 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 LOGINSERVER_H #define LOGINSERVER_H @@ -42,20 +25,20 @@ public: void SendAccountUpdate(ServerPacket *pack); bool Connected() { - if (IsLegacy) { - if (legacy_client) { - return legacy_client->Connected(); + if (m_is_legacy) { + if (m_legacy_client) { + return m_legacy_client->Connected(); } } else { - if (client) { - return client->Connected(); + if (m_client) { + return m_client->Connected(); } } return false; } - bool CanUpdate() { return CanAccountUpdate; } + bool CanUpdate() { return m_can_account_update; } private: void ProcessUsertoWorldReqLeg(uint16_t opcode, EQ::Net::Packet &p); @@ -70,15 +53,15 @@ private: void OnKeepAlive(EQ::Timer *t); std::unique_ptr m_keepalive; - std::unique_ptr client; - std::unique_ptr legacy_client; - std::unique_ptr statusupdate_timer; - char LoginServerAddress[256]; - uint32 LoginServerIP; - uint16 LoginServerPort; - std::string LoginAccount; - std::string LoginPassword; - bool CanAccountUpdate; - bool IsLegacy; + std::unique_ptr m_client; + std::unique_ptr m_legacy_client; + std::unique_ptr m_statusupdate_timer; + char m_loginserver_address[256]; + uint32 m_loginserver_ip; + uint16 m_loginserver_port; + std::string m_login_account; + std::string m_login_password; + bool m_can_account_update; + bool m_is_legacy; }; #endif From 873f1f7f343b872c1cd6fc43b28502d66632204f Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Sat, 30 Oct 2021 22:11:04 -0400 Subject: [PATCH 308/624] [Hotfix] Add include to fix windows build (#1657) --- common/string_util.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/common/string_util.cpp b/common/string_util.cpp index 305db7e48..77961dfc5 100644 --- a/common/string_util.cpp +++ b/common/string_util.cpp @@ -16,6 +16,7 @@ #include "string_util.h" #include +#include #ifdef _WINDOWS #include From 1231d44b5548b65f48c2cfcfd8ad9f606fe46561 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 30 Oct 2021 22:18:40 -0500 Subject: [PATCH 309/624] Revert "[BugFix] Charm Targeting and other issues. (#1655)" This reverts commit df3161455a2314e0e8b774bbbe328ee5d0a41fe9. --- zone/entity.cpp | 4 +--- zone/mob.cpp | 2 +- zone/spell_effects.cpp | 41 +++++++++++++++++++---------------------- 3 files changed, 21 insertions(+), 26 deletions(-) diff --git a/zone/entity.cpp b/zone/entity.cpp index 8630cbfcf..28bb5e144 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1514,9 +1514,7 @@ void EntityList::RemoveFromTargets(Mob *mob, bool RemoveFromXTargets) mob->CastToClient()->RemoveXTarget(m, false); } - if (m->IsAIControlled()) { - m->RemoveFromHateList(mob); - } + m->RemoveFromHateList(mob); } } diff --git a/zone/mob.cpp b/zone/mob.cpp index 51fa71d4a..a60a9f563 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -2875,7 +2875,7 @@ bool Mob::RemoveFromHateList(Mob* mob) ResetAssistCap(); } } - if(IsAIControlled() && GetTarget() == mob) + if(GetTarget() == mob) { SetTarget(hate_list.GetEntWithMostHateOnList(this)); } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 3a4a59490..2aa4e92da 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -731,27 +731,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if (!caster) // can't be someone's pet unless we know who that someone is break; - if (IsClient() && caster->IsClient()) { - caster->Message(Chat::White, "Unable to cast charm on a fellow player."); - BuffFadeByEffect(SE_Charm); - break; - } - else if (IsCorpse()) { - caster->Message(Chat::White, "Unable to cast charm on a corpse."); - BuffFadeByEffect(SE_Charm); - break; - } - else if (caster->GetPet() != nullptr && caster->IsClient()) { - caster->Message(Chat::White, "You cannot charm something when you already have a pet."); - BuffFadeByEffect(SE_Charm); - break; - } - else if (GetOwner()) { - caster->Message(Chat::White, "You cannot charm someone else's pet!"); - BuffFadeByEffect(SE_Charm); - break; - } - if(IsNPC()) { CastToNPC()->SaveGuardSpotCharm(); @@ -760,7 +739,25 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove entity_list.RemoveDebuffs(this); entity_list.RemoveFromTargets(this); WipeHateList(); - + + if (IsClient() && caster->IsClient()) { + caster->Message(Chat::White, "Unable to cast charm on a fellow player."); + BuffFadeByEffect(SE_Charm); + break; + } else if(IsCorpse()) { + caster->Message(Chat::White, "Unable to cast charm on a corpse."); + BuffFadeByEffect(SE_Charm); + break; + } else if(caster->GetPet() != nullptr && caster->IsClient()) { + caster->Message(Chat::White, "You cannot charm something when you already have a pet."); + BuffFadeByEffect(SE_Charm); + break; + } else if(GetOwner()) { + caster->Message(Chat::White, "You cannot charm someone else's pet!"); + BuffFadeByEffect(SE_Charm); + break; + } + Mob *my_pet = GetPet(); if(my_pet) { From 9e7a7634820c373e9c1c17ad63345a07bbc1a8a2 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 31 Oct 2021 00:04:48 -0500 Subject: [PATCH 310/624] [Charm] Push up fragments of Kayen's PR back up (#1659) --- zone/spell_effects.cpp | 57 ++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 2aa4e92da..44a02fbdd 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -197,7 +197,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove // if buff slot, use instrument mod there, otherwise calc it uint32 instrument_mod = buffslot > -1 ? buffs[buffslot].instrument_mod : caster ? caster->GetInstrumentMod(spell_id) : 10; - + // iterate through the effects in the spell for (i = 0; i < EFFECT_COUNT; i++) { @@ -728,11 +728,32 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove snprintf(effect_desc, _EDLEN, "Charm: %+i (up to lvl %d)", effect_value, spell.max[i]); #endif - if (!caster) // can't be someone's pet unless we know who that someone is + if (!caster) { // can't be someone's pet unless we know who that someone is break; + } - if(IsNPC()) - { + if (IsClient() && caster->IsClient()) { + caster->Message(Chat::White, "Unable to cast charm on a fellow player."); + BuffFadeByEffect(SE_Charm); + break; + } + else if (IsCorpse()) { + caster->Message(Chat::White, "Unable to cast charm on a corpse."); + BuffFadeByEffect(SE_Charm); + break; + } + else if (caster->GetPet() != nullptr && caster->IsClient()) { + caster->Message(Chat::White, "You cannot charm something when you already have a pet."); + BuffFadeByEffect(SE_Charm); + break; + } + else if (GetOwner()) { + caster->Message(Chat::White, "You cannot charm someone else's pet!"); + BuffFadeByEffect(SE_Charm); + break; + } + + if (IsNPC()) { CastToNPC()->SaveGuardSpotCharm(); } InterruptSpell(); @@ -740,24 +761,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove entity_list.RemoveFromTargets(this); WipeHateList(); - if (IsClient() && caster->IsClient()) { - caster->Message(Chat::White, "Unable to cast charm on a fellow player."); - BuffFadeByEffect(SE_Charm); - break; - } else if(IsCorpse()) { - caster->Message(Chat::White, "Unable to cast charm on a corpse."); - BuffFadeByEffect(SE_Charm); - break; - } else if(caster->GetPet() != nullptr && caster->IsClient()) { - caster->Message(Chat::White, "You cannot charm something when you already have a pet."); - BuffFadeByEffect(SE_Charm); - break; - } else if(GetOwner()) { - caster->Message(Chat::White, "You cannot charm someone else's pet!"); - BuffFadeByEffect(SE_Charm); - break; - } - Mob *my_pet = GetPet(); if(my_pet) { @@ -2358,7 +2361,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if (!caster) { break; } - + switch(spells[spell_id].skill) { case EQ::skills::SkillThrowing: caster->DoThrowingAttackDmg(this, nullptr, nullptr, effect_value,spells[spell_id].base2[i], 0, ReuseTime); @@ -3529,7 +3532,7 @@ snare has both of them negative, yet their range should work the same: break; case 119: // confirmed 2/6/04 - result = ubase + (caster_level / 8); + result = ubase + (caster_level / 8); break; case 120: { @@ -4563,7 +4566,7 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) int32 base1 = 0; int32 base2 = 0; uint32 slot = 0; - + int index_id = -1; uint32 focus_reuse_time = 0; @@ -8614,7 +8617,7 @@ void Mob::SpreadVirusEffect(int32 spell_id, uint32 caster_id, int32 buff_tics_re bool Mob::IsFocusProcLimitTimerActive(int32 focus_spell_id) { /* - Used with SPA SE_Ff_FocusTimerMin to limit how often a focus effect can be applied. + Used with SPA SE_Ff_FocusTimerMin to limit how often a focus effect can be applied. Ie. Can only have a spell trigger once every 15 seconds, or to be more creative can only have the fire spells received a very high special focused once every 30 seconds. Note, this stores timers for both spell, item and AA related focuses For AA the focus_spell_id @@ -8637,7 +8640,7 @@ bool Mob::IsFocusProcLimitTimerActive(int32 focus_spell_id) { } void Mob::SetFocusProcLimitTimer(int32 focus_spell_id, uint32 focus_reuse_time) { - + bool is_set = false; for (int i = 0; i < MAX_FOCUS_PROC_LIMIT_TIMERS; i++) { From 9af7122b1df1864e9067772952724a3f6b57df3d Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 31 Oct 2021 01:06:32 -0400 Subject: [PATCH 311/624] [BugFix] Remove potential nullptrs in Virus Code (#1658) --- zone/entity.cpp | 9 +++++++++ zone/spell_effects.cpp | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/zone/entity.cpp b/zone/entity.cpp index 28bb5e144..8d182fff5 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -5237,6 +5237,11 @@ std::vector EntityList::GetTargetsForVirusEffect(Mob *spreader, Mob *origi bool is_detrimental_spell = IsDetrimentalSpell(spell_id); for (auto &it : entity_list.GetCloseMobList(spreader, range)) { Mob *mob = it.second; + + if (!mob) { + continue; + } + if (mob == spreader) { continue; } @@ -5259,6 +5264,10 @@ std::vector EntityList::GetTargetsForVirusEffect(Mob *spreader, Mob *origi // Make sure the target is in range if (mob->CalculateDistance(spreader->GetX(), spreader->GetY(), spreader->GetZ()) <= range) { + if (!original_caster) { + continue; + } + //Do not allow detrimental spread to anything the original caster couldn't normally attack. if (is_detrimental_spell && !original_caster->IsAttackAllowed(mob, true)) { continue; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 44a02fbdd..da3b45fe1 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -8591,6 +8591,11 @@ void Mob::VirusEffectProcess() void Mob::SpreadVirusEffect(int32 spell_id, uint32 caster_id, int32 buff_tics_remaining) { Mob *caster = entity_list.GetMob(caster_id); + + if (!caster) { + return; + } + std::vector targets_in_range = entity_list.GetTargetsForVirusEffect( this, caster, From 05782433b8e75b9f2fe2b542e2b7dddeb1ca43b6 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Tue, 2 Nov 2021 00:19:13 -0500 Subject: [PATCH 312/624] [Loginserver] Add some resiliency to LS requests (#1663) --- loginserver/account_management.cpp | 10 ++++ loginserver/client.cpp | 80 ++++++++---------------------- 2 files changed, 30 insertions(+), 60 deletions(-) diff --git a/loginserver/account_management.cpp b/loginserver/account_management.cpp index 18b685d64..d068d916e 100644 --- a/loginserver/account_management.cpp +++ b/loginserver/account_management.cpp @@ -280,6 +280,8 @@ bool AccountManagement::UpdateLoginserverWorldAdminAccountPasswordById( return updated_account; } +constexpr int REQUEST_TIMEOUT_MS = 1500; + /** * @param in_account_username * @param in_account_password @@ -386,8 +388,16 @@ uint32 AccountManagement::CheckExternalLoginserverUserCredentials( } ); + std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); + auto &loop = EQ::EventLoop::Get(); while (running) { + std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); + if (std::chrono::duration_cast(end - begin).count() > REQUEST_TIMEOUT_MS) { + LogInfo("[CheckExternalLoginserverUserCredentials] Deadline exceeded [{}]", REQUEST_TIMEOUT_MS); + running = false; + } + loop.Process(); } diff --git a/loginserver/client.cpp b/loginserver/client.cpp index d62b6c1fa..6eb5fea13 100644 --- a/loginserver/client.cpp +++ b/loginserver/client.cpp @@ -4,6 +4,7 @@ #include "../common/eqemu_logsys.h" #include "../common/string_util.h" #include "encryption.h" +#include "account_management.h" extern LoginServer server; @@ -13,10 +14,10 @@ extern LoginServer server; */ Client::Client(std::shared_ptr c, LSClientVersion v) { - m_connection = c; - m_client_version = v; - m_client_status = cs_not_sent_session_ready; - m_account_id = 0; + m_connection = c; + m_client_version = v; + m_client_status = cs_not_sent_session_ready; + m_account_id = 0; m_play_server_id = 0; m_play_sequence_id = 0; } @@ -303,7 +304,7 @@ void Client::Handle_Play(const char *data) ); } - m_play_server_id = (unsigned int) play->ServerNumber; + m_play_server_id = (unsigned int) play->ServerNumber; m_play_sequence_id = sequence_in; m_play_server_id = server_id_in; server.server_manager->SendUserToWorldRequest(server_id_in, m_account_id, m_loginserver_name); @@ -375,59 +376,18 @@ void Client::AttemptLoginAccountCreation( return; } - if (server.options.GetEQEmuLoginServerAddress().length() == 0) { - DoFailedLogin(); - return; - } - - auto addr_components = SplitString(server.options.GetEQEmuLoginServerAddress(), ':'); - if (addr_components.size() != 2) { - DoFailedLogin(); - return; - } - - m_stored_user = user; - m_stored_pass = pass; - - auto address = addr_components[0]; - auto port = std::stoi(addr_components[1]); - EQ::Net::DNSLookup( - address, port, false, [=](const std::string &addr) { - if (addr.empty()) { - DoFailedLogin(); - return; - } - - m_login_connection_manager.reset(new EQ::Net::DaybreakConnectionManager()); - m_login_connection_manager->OnNewConnection( - std::bind( - &Client::LoginOnNewConnection, - this, - std::placeholders::_1 - ) - ); - m_login_connection_manager->OnConnectionStateChange( - std::bind( - &Client::LoginOnStatusChange, - this, - std::placeholders::_1, - std::placeholders::_2, - std::placeholders::_3 - ) - ); - m_login_connection_manager->OnPacketRecv( - std::bind( - &Client::LoginOnPacketRecv, - this, - std::placeholders::_1, - std::placeholders::_2 - ) - ); - - m_login_connection_manager->Connect(addr, port); - } + uint32 account_id = AccountManagement::CheckExternalLoginserverUserCredentials( + user, + pass ); + if (account_id > 0) { + LogInfo("[AttemptLoginAccountCreation] Found and creating eqemu account [{}]", account_id); + CreateEQEmuAccount(user, pass, account_id); + return; + } + + DoFailedLogin(); return; } #endif @@ -569,7 +529,7 @@ void Client::DoSuccessfulLogin( server.db->UpdateLSAccountData(db_account_id, std::string(inet_ntoa(in))); GenerateKey(); - m_account_id = db_account_id; + m_account_id = db_account_id; m_account_name = in_account_name; m_loginserver_name = db_loginserver; @@ -795,9 +755,9 @@ void Client::LoginProcessLoginResponse(const EQ::Net::Packet &p) } else { LogDebug( - "response [{0}] login succeeded user [{1}]", - response_error, - m_stored_user + "response [{0}] login succeeded user [{1}]", + response_error, + m_stored_user ); auto m_dbid = sp.GetUInt32(8); From 4ac525afc2d0c764d40c9900d2130b3f81e9d57f Mon Sep 17 00:00:00 2001 From: splose Date: Tue, 2 Nov 2021 13:57:16 -0400 Subject: [PATCH 313/624] [Rules] Missing Character:TradeskillUpTailoring (#1667) --- common/ruletypes.h | 1 + zone/tradeskills.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/common/ruletypes.h b/common/ruletypes.h index 956ac43d3..9b944b2ec 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -139,6 +139,7 @@ RULE_INT(Character, TradeskillUpMakePoison, 2, "Make Poison skillup rate adjustm RULE_INT(Character, TradeskillUpPottery, 4, "Pottery skillup rate adjustment. Lower is faster") RULE_INT(Character, TradeskillUpResearch, 1, "Research skillup rate adjustment. Lower is faster") RULE_INT(Character, TradeskillUpTinkering, 2, "Tinkering skillup rate adjustment. Lower is faster") +RULE_INT(Character, TradeskillUpTailoring, 2, "Tailoring skillup rate adjustment. Lower is faster") RULE_BOOL(Character, MarqueeHPUpdates, false, "Will show health percentage in center of screen if health lesser than 100%") RULE_INT(Character, IksarCommonTongue, 95, "Starting value for Common Tongue for Iksars") RULE_INT(Character, OgreCommonTongue, 95, "Starting value for Common Tongue for Ogres") diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index 22903ebf5..82d29e7f4 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -995,6 +995,9 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { case EQ::skills::SkillTinkering: skillup_modifier = RuleI(Character, TradeskillUpTinkering); break; + case EQ::skills::SkillTailoring: + skillup_modifier = RuleI(Character, TradeskillUpTailoring); + break; default: skillup_modifier = 2; break; From e4138b871b36768c9500e0b940325618a2a63236 Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Tue, 2 Nov 2021 15:12:07 -0500 Subject: [PATCH 314/624] [Rules] Add optional rules for HealAmt and SpellAmt to scale DoTs/HoTs. (#1661) * Add optional rules for itembonuses HealAmt and SpellAmt to scale for DoTs/HoTs * Fix typo * Only 1 rulecheck * Apply +healingitems and +dmgitems after focus effects so they scale properly * Fix dots / hots to not always use PVPScaling for extra_dmg / extra_healing Adjust +healamt and +spelldmg to scale over the full duration of the spell, Thanks Kayen --- common/ruletypes.h | 2 ++ zone/effects.cpp | 43 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 9b944b2ec..ae0f0a272 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -382,6 +382,8 @@ RULE_BOOL(Spells, AllowDoubleInvis, false, "Allows you to cast invisibility spel RULE_BOOL(Spells, AllowSpellMemorizeFromItem, false, "Allows players to memorize spells by right-clicking spell scrolls") RULE_BOOL(Spells, InvisRequiresGroup, false, "Invis requires the the target to be in group.") RULE_INT(Spells, ClericInnateHealFocus, 5, "Clerics on live get a 5 pct innate heal focus") +RULE_BOOL(Spells, DOTsScaleWithSpellDmg, false, "Allow SpellDmg stat to affect DoT spells") +RULE_BOOL(Spells, HOTsScaleWithHealAmt, false, "Allow HealAmt stat to affect HoT spells") RULE_CATEGORY_END() RULE_CATEGORY(Combat) diff --git a/zone/effects.cpp b/zone/effects.cpp index 67efa4fe1..849038ab1 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -196,7 +196,7 @@ int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) { if (target == nullptr) return value; - + if (IsNPC()) { value += value * CastToNPC()->GetSpellFocusDMG() / 100; } @@ -226,9 +226,18 @@ int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) { GetFocusEffect(focusFcDamageAmt, spell_id) + GetFocusEffect(focusFcDamageAmt2, spell_id) + GetFocusEffect(focusFcAmplifyAmt, spell_id); + + if (RuleB(Spells, DOTsScaleWithSpellDmg)) { + if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg) { + extra_dmg += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value)*ratio/100; + } + else if(!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) { + extra_dmg += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value)*ratio/100; + } + } if (extra_dmg) { - int duration = CalcBuffDuration(this, this, spell_id); + int duration = CalcBuffDuration(this, target, spell_id); if (duration > 0) extra_dmg /= duration; } @@ -248,9 +257,18 @@ int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) { GetFocusEffect(focusFcDamageAmt, spell_id) + GetFocusEffect(focusFcDamageAmt2, spell_id) + GetFocusEffect(focusFcAmplifyAmt, spell_id); + + if (RuleB(Spells, DOTsScaleWithSpellDmg)) { + if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg) { + extra_dmg += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value); + } + else if(!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) { + extra_dmg += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value); + } + } if (extra_dmg) { - int duration = CalcBuffDuration(this, this, spell_id); + int duration = CalcBuffDuration(this, target, spell_id); if (duration > 0) extra_dmg /= duration; } @@ -383,6 +401,25 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { //Heal over time spells. [Heal Rate and Additional Healing effects do not increase this value] else { + //Using IgnoreSpellDmgLvlRestriction to also allow healing to scale + int32 extra_heal = 0; + if (RuleB(Spells, HOTsScaleWithHealAmt)) { + if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt) { + extra_heal += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, base_value); + } + else if(!spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) { + extra_heal += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, base_value); + } + } + + if (extra_heal) { + int duration = CalcBuffDuration(this, target, spell_id); + if (duration > 0) { + extra_heal /= duration; + value += extra_heal; + } + } + if (critical_chance && zone->random.Roll(critical_chance)) value *= critical_modifier; } From 6e26e8953c38ad91a6ed9d74ed76fa7b959ee78f Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Wed, 3 Nov 2021 14:39:51 -0500 Subject: [PATCH 315/624] [Loginserver] Health Checks (#1665) * Health checks stash * Healthcheck work --- loginserver/account_management.cpp | 136 ++++++++++++++++++-- loginserver/account_management.h | 2 + loginserver/client.cpp | 20 ++- loginserver/client.h | 1 + loginserver/loginserver_command_handler.cpp | 21 +++ loginserver/loginserver_command_handler.h | 1 + loginserver/loginserver_webserver.cpp | 20 ++- loginserver/main.cpp | 12 +- 8 files changed, 196 insertions(+), 17 deletions(-) diff --git a/loginserver/account_management.cpp b/loginserver/account_management.cpp index d068d916e..2c3506db0 100644 --- a/loginserver/account_management.cpp +++ b/loginserver/account_management.cpp @@ -3,6 +3,7 @@ #include "../common/event/task_scheduler.h" #include "../common/event/event_loop.h" #include "../common/net/dns.h" +#include "../common/string_util.h" extern LoginServer server; EQ::Event::TaskScheduler task_runner; @@ -377,16 +378,22 @@ uint32 AccountManagement::CheckExternalLoginserverUserCredentials( } ); - EQ::Net::DNSLookup( - "login.eqemulator.net", 5999, false, [&](const std::string &addr) { - if (addr.empty()) { - ret = 0; - running = false; - } + auto s = SplitString(server.options.GetEQEmuLoginServerAddress(), ':'); + if (s.size() == 2) { + auto address = s[0]; + auto port = std::stoi(s[1]); - mgr.Connect(addr, 5999); - } - ); + EQ::Net::DNSLookup( + address, port, false, [&](const std::string &addr) { + if (addr.empty()) { + ret = 0; + running = false; + } + + mgr.Connect(addr, port); + } + ); + } std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); @@ -407,3 +414,114 @@ uint32 AccountManagement::CheckExternalLoginserverUserCredentials( return res.get(); } + +uint32 AccountManagement::HealthCheckUserLogin() +{ + std::string in_account_username = "healthcheckuser"; + std::string in_account_password = "healthcheckpassword"; + + auto res = task_runner.Enqueue( + [&]() -> uint32 { + bool running = true; + uint32 ret = 0; + + EQ::Net::DaybreakConnectionManager mgr; + std::shared_ptr c; + + mgr.OnNewConnection( + [&](std::shared_ptr connection) { + c = connection; + } + ); + + mgr.OnConnectionStateChange( + [&]( + std::shared_ptr conn, + EQ::Net::DbProtocolStatus from, + EQ::Net::DbProtocolStatus to + ) { + if (EQ::Net::StatusConnected == to) { + EQ::Net::DynamicPacket p; + p.PutUInt16(0, 1); //OP_SessionReady + p.PutUInt32(2, 2); + c->QueuePacket(p); + } + else if (EQ::Net::StatusDisconnected == to) { + running = false; + } + } + ); + + mgr.OnPacketRecv( + [&](std::shared_ptr conn, const EQ::Net::Packet &p) { + auto opcode = p.GetUInt16(0); + switch (opcode) { + case 0x0017: //OP_ChatMessage + { + size_t buffer_len = + in_account_username.length() + in_account_password.length() + 2; + + std::unique_ptr buffer(new char[buffer_len]); + + strcpy(&buffer[0], in_account_username.c_str()); + strcpy(&buffer[in_account_username.length() + 1], in_account_password.c_str()); + + size_t encrypted_len = buffer_len; + + if (encrypted_len % 8 > 0) { + encrypted_len = ((encrypted_len / 8) + 1) * 8; + } + + EQ::Net::DynamicPacket p; + p.Resize(12 + encrypted_len); + p.PutUInt16(0, 2); //OP_Login + p.PutUInt32(2, 3); + + eqcrypt_block(&buffer[0], buffer_len, (char *) p.Data() + 12, true); + c->QueuePacket(p); + break; + } + case 0x0018: { + auto encrypt_size = p.Length() - 12; + if (encrypt_size % 8 > 0) { + encrypt_size = (encrypt_size / 8) * 8; + } + + std::unique_ptr decrypted(new char[encrypt_size]); + + eqcrypt_block((char *) p.Data() + 12, encrypt_size, &decrypted[0], false); + + EQ::Net::StaticPacket sp(&decrypted[0], encrypt_size); + auto response_error = sp.GetUInt16(1); + + { + // we only care to see the response code + ret = response_error; + running = false; + } + break; + } + } + } + ); + + mgr.Connect("127.0.0.1", 5999); + + std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); + auto &loop = EQ::EventLoop::Get(); + while (running) { + std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); + if (std::chrono::duration_cast(end - begin).count() > 2000) { + ret = 0; + running = false; + } + + loop.Process(); + } + + return ret; + } + ); + + return res.get(); +} diff --git a/loginserver/account_management.h b/loginserver/account_management.h index e342ec172..2c92af69d 100644 --- a/loginserver/account_management.h +++ b/loginserver/account_management.h @@ -89,6 +89,8 @@ public: uint32 in_account_id, const std::string &in_account_password_hash ); + + static uint32 HealthCheckUserLogin(); }; diff --git a/loginserver/client.cpp b/loginserver/client.cpp index 6eb5fea13..e92d408dc 100644 --- a/loginserver/client.cpp +++ b/loginserver/client.cpp @@ -233,11 +233,16 @@ void Client::Handle_Login(const char *data, unsigned int size) user = components[1]; } + // health checks + if (ProcessHealthCheck(user)) { + DoFailedLogin(); + return; + } + LogInfo( - "Attempting password based login [{0}] login [{1}] user [{2}]", + "Attempting password based login [{0}] login [{1}]", user, - db_loginserver, - user + db_loginserver ); ParseAccountString(user, user, db_loginserver); @@ -376,6 +381,7 @@ void Client::AttemptLoginAccountCreation( return; } + uint32 account_id = AccountManagement::CheckExternalLoginserverUserCredentials( user, pass @@ -766,3 +772,11 @@ void Client::LoginProcessLoginResponse(const EQ::Net::Packet &p) m_login_connection->Close(); } } +bool Client::ProcessHealthCheck(std::string username) +{ + if (username == "healthcheckuser") { + return true; + } + + return false; +} diff --git a/loginserver/client.h b/loginserver/client.h index 67c2f8e1f..4089e3d29 100644 --- a/loginserver/client.h +++ b/loginserver/client.h @@ -208,6 +208,7 @@ private: void LoginSendSessionReady(); void LoginSendLogin(); void LoginProcessLoginResponse(const EQ::Net::Packet &p); + static bool ProcessHealthCheck(std::string username); }; #endif diff --git a/loginserver/loginserver_command_handler.cpp b/loginserver/loginserver_command_handler.cpp index eed4e6058..152266902 100644 --- a/loginserver/loginserver_command_handler.cpp +++ b/loginserver/loginserver_command_handler.cpp @@ -38,6 +38,7 @@ namespace LoginserverCommandHandler { function_map["web-api-token:list"] = &LoginserverCommandHandler::ListLoginserverApiTokens; function_map["world-admin:create"] = &LoginserverCommandHandler::CreateLoginserverWorldAdminAccount; function_map["world-admin:update"] = &LoginserverCommandHandler::UpdateLoginserverWorldAdminAccountPassword; + function_map["health:check-login"] = &LoginserverCommandHandler::HealthCheckLogin; EQEmuCommand::HandleMenu(function_map, cmd, argc, argv); } @@ -281,4 +282,24 @@ namespace LoginserverCommandHandler { cmd(3).str() ); } + + /** + * @param argc + * @param argv + * @param cmd + * @param description + */ + void HealthCheckLogin(int argc, char **argv, argh::parser &cmd, std::string &description) + { + description = "Checks login health using a test user"; + + std::vector arguments = {}; + std::vector options = {}; + + if (cmd[{"-h", "--help"}]) { + return; + } + + LogInfo("[CLI] [HealthCheck] Response code [{}]", AccountManagement::HealthCheckUserLogin()); + } } diff --git a/loginserver/loginserver_command_handler.h b/loginserver/loginserver_command_handler.h index e47382022..fc2f2cbeb 100644 --- a/loginserver/loginserver_command_handler.h +++ b/loginserver/loginserver_command_handler.h @@ -14,6 +14,7 @@ namespace LoginserverCommandHandler { void UpdateLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description); void CheckExternalLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description); void UpdateLoginserverWorldAdminAccountPassword(int argc, char **argv, argh::parser &cmd, std::string &description); + void HealthCheckLogin(int argc, char **argv, argh::parser &cmd, std::string &description); }; diff --git a/loginserver/loginserver_webserver.cpp b/loginserver/loginserver_webserver.cpp index 8fc7780bf..bea7dc8ae 100644 --- a/loginserver/loginserver_webserver.cpp +++ b/loginserver/loginserver_webserver.cpp @@ -28,7 +28,7 @@ namespace LoginserverWebserver { } Json::Value response; - auto iter = server.server_manager->getWorldServers().begin(); + auto iter = server.server_manager->getWorldServers().begin(); while (iter != server.server_manager->getWorldServers().end()) { Json::Value row; row["server_long_name"] = (*iter)->GetServerLongName(); @@ -297,6 +297,24 @@ namespace LoginserverWebserver { LoginserverWebserver::SendResponse(response, res); } ); + + api.Get( + "/probes/healthcheck", [](const httplib::Request &request, httplib::Response &res) { + Json::Value response; + uint32 login_response = AccountManagement::HealthCheckUserLogin(); + + response["status"] = login_response; + if (login_response == 0) { + response["message"] = "Process unresponsive, exiting..."; + LogInfo("Probes healthcheck unresponsive, exiting..."); + } + + LoginserverWebserver::SendResponse(response, res); + if (login_response == 0) { + std::exit(0); + } + } + ); } /** diff --git a/loginserver/main.cpp b/loginserver/main.cpp index d505ffc5d..50f86d673 100644 --- a/loginserver/main.cpp +++ b/loginserver/main.cpp @@ -10,6 +10,7 @@ #include "login_server.h" #include "loginserver_webserver.h" #include "loginserver_command_handler.h" +#include "../common/string_util.h" #include #include #include @@ -20,6 +21,7 @@ LoginServer server; EQEmuLogSys LogSys; bool run_server = true; +void ResolveAddresses(); void CatchSignal(int sig_num) { } @@ -118,11 +120,13 @@ void start_web_server() httplib::Server api; - api.set_logger([](const auto& req, const auto& res) { - if (!req.path.empty()) { - LogInfo("[API] Request [{}] via [{}:{}]", req.path, req.remote_addr, req.remote_port); + api.set_logger( + [](const auto &req, const auto &res) { + if (!req.path.empty()) { + LogInfo("[API] Request [{}] via [{}:{}]", req.path, req.remote_addr, req.remote_port); + } } - }); + ); LoginserverWebserver::RegisterRoutes(api); api.listen("0.0.0.0", web_api_port); From 17aaab1f9d4208dd5a5c64bc67189ed471651372 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Wed, 3 Nov 2021 17:47:15 -0400 Subject: [PATCH 316/624] [Quest API] Add Spell methods to Perl. (#1631) * [Quest API] Add Spell methods to Perl. - Add quest::getspell(spell_id) to Perl. - Add eq.get_spell(spell_id) to Lua. These methods return a spell object. Exports $spell object references to spell events. * Formatting. * Remove comment. * Update spdat.cpp * Amplication typo. * Fix conflicts. * Remove repository changes. * Fix typing. * Update spell_effects.cpp --- common/eq_packet_structs.h | 8 +- common/patches/rof2_structs.h | 4 +- common/patches/rof_structs.h | 4 +- common/patches/sod.cpp | 4 +- common/patches/sod_structs.h | 4 +- common/patches/sof.cpp | 4 +- common/patches/sof_structs.h | 4 +- common/patches/titanium.cpp | 4 +- common/patches/titanium_structs.h | 4 +- common/patches/uf.cpp | 4 +- common/patches/uf_structs.h | 4 +- common/shareddb.cpp | 114 +- common/spdat.cpp | 324 ++--- common/spdat.h | 120 +- zone/CMakeLists.txt | 1 + zone/aa.cpp | 24 +- zone/aa_rank_effects.h | 4 +- zone/aggro.cpp | 52 +- zone/attack.cpp | 44 +- zone/beacon.cpp | 8 +- zone/bonuses.cpp | 940 ++++++------- zone/bot.cpp | 394 +++--- zone/bot_command.cpp | 190 +-- zone/bot_database.cpp | 6 +- zone/botspellsai.cpp | 72 +- zone/client.cpp | 20 +- zone/client_mods.cpp | 2 +- zone/client_packet.cpp | 30 +- zone/client_process.cpp | 10 +- zone/command.cpp | 52 +- zone/common.h | 2 +- zone/effects.cpp | 28 +- zone/embparser.cpp | 53 +- zone/embparser.h | 4 +- zone/embparser_api.cpp | 31 + zone/embperl.cpp | 2 + zone/groups.cpp | 4 +- zone/lua_general.cpp | 11 + zone/lua_mob.cpp | 4 +- zone/lua_mob.h | 2 +- zone/lua_parser.cpp | 8 +- zone/lua_spell.cpp | 102 +- zone/merc.cpp | 66 +- zone/mob.cpp | 158 +-- zone/mob.h | 14 +- zone/mob_ai.cpp | 44 +- zone/npc.cpp | 6 +- zone/npc.h | 6 +- zone/perl_mob.cpp | 16 +- zone/perl_spell.cpp | 2138 +++++++++++++++++++++++++++++ zone/perlparser.h | 2 +- zone/pets.cpp | 8 +- zone/questmgr.cpp | 25 +- zone/questmgr.h | 5 +- zone/raids.cpp | 4 +- zone/special_attacks.cpp | 8 +- zone/spell_effects.cpp | 997 +++++++------- zone/spells.cpp | 234 ++-- zone/trap.cpp | 2 +- zone/zonedb.cpp | 16 +- zone/zonedb.h | 4 +- 61 files changed, 4342 insertions(+), 2117 deletions(-) create mode 100644 zone/perl_spell.cpp diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 6f1a437a8..304411d03 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -4342,8 +4342,8 @@ struct AARankPrereq_Struct struct AARankEffect_Struct { int32 effect_id; - int32 base1; - int32 base2; + int32 base_value; + int32 limit_value; int32 slot; }; @@ -4351,8 +4351,8 @@ struct AARankEffect_Struct struct AA_Ability { /*00*/ uint32 skill_id; -/*04*/ uint32 base1; -/*08*/ uint32 base2; +/*04*/ uint32 base_value; +/*08*/ uint32 limit_value; /*12*/ uint32 slot; }; diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index a37e25d61..229a3b3e7 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -4361,8 +4361,8 @@ struct UseAA_Struct { struct AA_Ability { /*00*/ uint32 skill_id; -/*04*/ uint32 base1; -/*08*/ uint32 base2; +/*04*/ uint32 base_value; +/*08*/ uint32 limit_value; /*12*/ uint32 slot; /*16*/ }; diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index d98827ce9..27365a36d 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -4305,8 +4305,8 @@ struct UseAA_Struct { struct AA_Ability { /*00*/ uint32 skill_id; -/*04*/ uint32 base1; -/*08*/ uint32 base2; +/*04*/ uint32 base_value; +/*08*/ uint32 limit_value; /*12*/ uint32 slot; /*16*/ }; diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index 8470b7296..034679bd5 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -1874,8 +1874,8 @@ namespace SoD for(auto i = 0; i < eq->total_abilities; ++i) { eq->abilities[i].skill_id = inapp->ReadUInt32(); - eq->abilities[i].base1 = inapp->ReadUInt32(); - eq->abilities[i].base2 = inapp->ReadUInt32(); + eq->abilities[i].base_value = inapp->ReadUInt32(); + eq->abilities[i].limit_value = inapp->ReadUInt32(); eq->abilities[i].slot = inapp->ReadUInt32(); } diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 12bfacac7..37392fc18 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -3748,8 +3748,8 @@ struct UseAA_Struct { struct AA_Ability { /*00*/ uint32 skill_id; -/*04*/ uint32 base1; -/*08*/ uint32 base2; +/*04*/ uint32 base_value; +/*08*/ uint32 limit_value; /*12*/ uint32 slot; /*16*/ }; diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index 760a9008b..56fbba8f4 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -1545,8 +1545,8 @@ namespace SoF for(auto i = 0; i < eq->total_abilities; ++i) { eq->abilities[i].skill_id = inapp->ReadUInt32(); - eq->abilities[i].base1 = inapp->ReadUInt32(); - eq->abilities[i].base2 = inapp->ReadUInt32(); + eq->abilities[i].base_value = inapp->ReadUInt32(); + eq->abilities[i].limit_value = inapp->ReadUInt32(); eq->abilities[i].slot = inapp->ReadUInt32(); } diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index 60542db38..3129e3b0b 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -3673,8 +3673,8 @@ struct UseAA_Struct { struct AA_Ability { /*00*/ uint32 skill_id; -/*04*/ uint32 base1; -/*08*/ uint32 base2; +/*04*/ uint32 base_value; +/*08*/ uint32 limit_value; /*12*/ uint32 slot; /*16*/ }; diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index bb35d9f87..200beebf5 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -1338,8 +1338,8 @@ namespace Titanium for(auto i = 0; i < eq->total_abilities; ++i) { eq->abilities[i].skill_id = inapp->ReadUInt32(); - eq->abilities[i].base1 = inapp->ReadUInt32(); - eq->abilities[i].base2 = inapp->ReadUInt32(); + eq->abilities[i].base_value = inapp->ReadUInt32(); + eq->abilities[i].limit_value = inapp->ReadUInt32(); eq->abilities[i].slot = inapp->ReadUInt32(); } diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index 2846288bc..cf918717a 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -3180,8 +3180,8 @@ struct UseAA_Struct { struct AA_Ability { /*00*/ uint32 skill_id; -/*04*/ uint32 base1; -/*08*/ uint32 base2; +/*04*/ uint32 base_value; +/*08*/ uint32 limit_value; /*12*/ uint32 slot; }; diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index f1bf60e6a..81c76aa62 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -2139,8 +2139,8 @@ namespace UF for(auto i = 0; i < eq->total_abilities; ++i) { eq->abilities[i].skill_id = inapp->ReadUInt32(); - eq->abilities[i].base1 = inapp->ReadUInt32(); - eq->abilities[i].base2 = inapp->ReadUInt32(); + eq->abilities[i].base_value = inapp->ReadUInt32(); + eq->abilities[i].limit_value = inapp->ReadUInt32(); eq->abilities[i].slot = inapp->ReadUInt32(); } diff --git a/common/patches/uf_structs.h b/common/patches/uf_structs.h index f3d7f0e2c..a0ee2ba8d 100644 --- a/common/patches/uf_structs.h +++ b/common/patches/uf_structs.h @@ -3803,8 +3803,8 @@ struct UseAA_Struct { struct AA_Ability { /*00*/ uint32 skill_id; -/*04*/ uint32 base1; -/*08*/ uint32 base2; +/*04*/ uint32 base_value; +/*08*/ uint32 limit_value; /*12*/ uint32 slot; /*16*/ }; diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 18619ee6e..7f913edf3 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1682,7 +1682,7 @@ void SharedDatabase::LoadDamageShieldTypes(SPDat_Spell_Struct* sp, int32 iMaxSpe for(auto row = results.begin(); row != results.end(); ++row) { int spellID = atoi(row[0]); if((spellID > 0) && (spellID <= iMaxSpellID)) - sp[spellID].DamageShieldType = atoi(row[1]); + sp[spellID].damage_shield_type = atoi(row[1]); } } @@ -1762,48 +1762,48 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { strn0cpy(sp[tempid].spell_fades, row[8], sizeof(sp[tempid].spell_fades)); sp[tempid].range=static_cast(atof(row[9])); - sp[tempid].aoerange=static_cast(atof(row[10])); - sp[tempid].pushback=static_cast(atof(row[11])); - sp[tempid].pushup=static_cast(atof(row[12])); + sp[tempid].aoe_range=static_cast(atof(row[10])); + sp[tempid].push_back=static_cast(atof(row[11])); + sp[tempid].push_up=static_cast(atof(row[12])); sp[tempid].cast_time=atoi(row[13]); sp[tempid].recovery_time=atoi(row[14]); sp[tempid].recast_time=atoi(row[15]); - sp[tempid].buffdurationformula=atoi(row[16]); - sp[tempid].buffduration=atoi(row[17]); - sp[tempid].AEDuration=atoi(row[18]); + sp[tempid].buff_duration_formula=atoi(row[16]); + sp[tempid].buff_duration=atoi(row[17]); + sp[tempid].aoe_duration=atoi(row[18]); sp[tempid].mana=atoi(row[19]); int y=0; for(y=0; y< EFFECT_COUNT;y++) - sp[tempid].base[y]=atoi(row[20+y]); // effect_base_value + sp[tempid].base_value[y]=atoi(row[20+y]); // effect_base_value for(y=0; y < EFFECT_COUNT; y++) - sp[tempid].base2[y]=atoi(row[32+y]); // effect_limit_value + sp[tempid].limit_value[y]=atoi(row[32+y]); // effect_limit_value for(y=0; y< EFFECT_COUNT;y++) - sp[tempid].max[y]=atoi(row[44+y]); + sp[tempid].max_value[y]=atoi(row[44+y]); for(y=0; y< 4;y++) - sp[tempid].components[y]=atoi(row[58+y]); + sp[tempid].component[y]=atoi(row[58+y]); for(y=0; y< 4;y++) - sp[tempid].component_counts[y]=atoi(row[62+y]); + sp[tempid].component_count[y]=atoi(row[62+y]); for(y=0; y< 4;y++) - sp[tempid].NoexpendReagent[y]=atoi(row[66+y]); + sp[tempid].no_expend_reagent[y]=atoi(row[66+y]); for(y=0; y< EFFECT_COUNT;y++) sp[tempid].formula[y]=atoi(row[70+y]); - sp[tempid].goodEffect=atoi(row[83]); - sp[tempid].Activated=atoi(row[84]); - sp[tempid].resisttype=atoi(row[85]); + sp[tempid].good_effect=atoi(row[83]); + sp[tempid].activated=atoi(row[84]); + sp[tempid].resist_type=atoi(row[85]); for(y=0; y< EFFECT_COUNT;y++) - sp[tempid].effectid[y]=atoi(row[86+y]); + sp[tempid].effect_id[y]=atoi(row[86+y]); - sp[tempid].targettype = (SpellTargetType) atoi(row[98]); - sp[tempid].basediff=atoi(row[99]); + sp[tempid].target_type = (SpellTargetType) atoi(row[98]); + sp[tempid].base_difficulty=atoi(row[99]); int tmp_skill = atoi(row[100]);; @@ -1812,15 +1812,15 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { else sp[tempid].skill = (EQ::skills::SkillType) tmp_skill; - sp[tempid].zonetype=atoi(row[101]); - sp[tempid].EnvironmentType=atoi(row[102]); - sp[tempid].TimeOfDay=atoi(row[103]); + sp[tempid].zone_type=atoi(row[101]); + sp[tempid].environment_type=atoi(row[102]); + sp[tempid].time_of_day=atoi(row[103]); for(y=0; y < PLAYER_CLASS_COUNT;y++) sp[tempid].classes[y]=atoi(row[104+y]); - sp[tempid].CastingAnim=atoi(row[120]); - sp[tempid].SpellAffectIndex=atoi(row[123]); + sp[tempid].casting_animation=atoi(row[120]); + sp[tempid].spell_affect_index=atoi(row[123]); sp[tempid].disallow_sit=atoi(row[124]); sp[tempid].deity_agnostic=atoi(row[125]); @@ -1829,32 +1829,32 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { sp[tempid].new_icon=atoi(row[144]); sp[tempid].uninterruptable=atoi(row[146]) != 0; - sp[tempid].ResistDiff=atoi(row[147]); - sp[tempid].dot_stacking_exempt = atoi(row[148]) != 0; - sp[tempid].RecourseLink = atoi(row[150]); + sp[tempid].resist_difficulty=atoi(row[147]); + sp[tempid].unstackable_dot = atoi(row[148]) != 0; + sp[tempid].recourse_link = atoi(row[150]); sp[tempid].no_partial_resist = atoi(row[151]) != 0; sp[tempid].short_buff_box = atoi(row[154]); - sp[tempid].descnum = atoi(row[155]); - sp[tempid].typedescnum = atoi(row[156]); - sp[tempid].effectdescnum = atoi(row[157]); + sp[tempid].description_id = atoi(row[155]); + sp[tempid].type_description_id = atoi(row[156]); + sp[tempid].effect_description_id = atoi(row[157]); sp[tempid].npc_no_los = atoi(row[159]) != 0; sp[tempid].feedbackable = atoi(row[160]) != 0; sp[tempid].reflectable = atoi(row[161]) != 0; - sp[tempid].bonushate=atoi(row[162]); + sp[tempid].bonus_hate=atoi(row[162]); sp[tempid].ldon_trap = atoi(row[165]) != 0; - sp[tempid].EndurCost=atoi(row[166]); - sp[tempid].EndurTimerIndex=atoi(row[167]); - sp[tempid].IsDisciplineBuff = atoi(row[168]) != 0; - sp[tempid].HateAdded=atoi(row[173]); - sp[tempid].EndurUpkeep=atoi(row[174]); - sp[tempid].numhitstype = atoi(row[175]); - sp[tempid].numhits = atoi(row[176]); - sp[tempid].pvpresistbase=atoi(row[177]); - sp[tempid].pvpresistcalc=atoi(row[178]); - sp[tempid].pvpresistcap=atoi(row[179]); + sp[tempid].endurance_cost=atoi(row[166]); + sp[tempid].timer_id=atoi(row[167]); + sp[tempid].is_discipline = atoi(row[168]) != 0; + sp[tempid].hate_added=atoi(row[173]); + sp[tempid].endurance_upkeep=atoi(row[174]); + sp[tempid].hit_number_type = atoi(row[175]); + sp[tempid].hit_number = atoi(row[176]); + sp[tempid].pvp_resist_base=atoi(row[177]); + sp[tempid].pvp_resist_per_level=atoi(row[178]); + sp[tempid].pvp_resist_cap=atoi(row[179]); sp[tempid].spell_category=atoi(row[180]); sp[tempid].pvp_duration = atoi(row[181]); sp[tempid].pvp_duration_cap = atoi(row[182]); @@ -1862,11 +1862,11 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { sp[tempid].cast_not_standing = atoi(row[184]) != 0; sp[tempid].can_mgb=atoi(row[185]); sp[tempid].dispel_flag = atoi(row[186]); - sp[tempid].MinResist = atoi(row[189]); - sp[tempid].MaxResist = atoi(row[190]); + sp[tempid].min_resist = atoi(row[189]); + sp[tempid].max_resist = atoi(row[190]); sp[tempid].viral_targets = atoi(row[191]); sp[tempid].viral_timer = atoi(row[192]); - sp[tempid].NimbusEffect = atoi(row[193]); + sp[tempid].nimbus_effect = atoi(row[193]); sp[tempid].directional_start = static_cast(atoi(row[194])); sp[tempid].directional_end = static_cast(atoi(row[195])); sp[tempid].sneak = atoi(row[196]) != 0; @@ -1874,29 +1874,29 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { sp[tempid].no_detrimental_spell_aggro = atoi(row[198]) != 0; sp[tempid].suspendable = atoi(row[200]) != 0; sp[tempid].viral_range = atoi(row[201]); - sp[tempid].songcap = atoi(row[202]); + sp[tempid].song_cap = atoi(row[202]); sp[tempid].no_block = atoi(row[205]); - sp[tempid].spellgroup=atoi(row[207]); + sp[tempid].spell_group=atoi(row[207]); sp[tempid].rank = atoi(row[208]); sp[tempid].no_resist=atoi(row[209]); - sp[tempid].CastRestriction = atoi(row[211]); - sp[tempid].AllowRest = atoi(row[212]) != 0; - sp[tempid].InCombat = atoi(row[213]) != 0; - sp[tempid].OutofCombat = atoi(row[214]) != 0; + sp[tempid].cast_restriction = atoi(row[211]); + sp[tempid].allow_rest = atoi(row[212]) != 0; + sp[tempid].can_cast_in_combat = atoi(row[213]) != 0; + sp[tempid].can_cast_out_of_combat = atoi(row[214]) != 0; sp[tempid].override_crit_chance = atoi(row[217]); - sp[tempid].aemaxtargets = atoi(row[218]); + sp[tempid].aoe_max_targets = atoi(row[218]); sp[tempid].no_heal_damage_item_mod = atoi(row[219]); sp[tempid].caster_requirement_id = atoi(row[220]); sp[tempid].spell_class = atoi(row[221]); sp[tempid].spell_subclass = atoi(row[222]); - sp[tempid].persistdeath = atoi(row[224]) != 0; - sp[tempid].min_dist = atof(row[227]); - sp[tempid].min_dist_mod = atof(row[228]); - sp[tempid].max_dist = atof(row[229]); - sp[tempid].max_dist_mod = atof(row[230]); + sp[tempid].persist_death = atoi(row[224]) != 0; + sp[tempid].min_distance = atof(row[227]); + sp[tempid].min_distance_mod = atof(row[228]); + sp[tempid].max_distance = atof(row[229]); + sp[tempid].max_distance_mod = atof(row[230]); sp[tempid].min_range = static_cast(atoi(row[231])); sp[tempid].no_remove = atoi(row[232]) != 0; - sp[tempid].DamageShieldType = 0; + sp[tempid].damage_shield_type = 0; } LoadDamageShieldTypes(sp, max_spells); diff --git a/common/spdat.cpp b/common/spdat.cpp index 719af4069..e52da4da1 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -87,7 +87,7 @@ bool IsTargetableAESpell(uint16 spell_id) { - if (IsValidSpell(spell_id) && spells[spell_id].targettype == ST_AETarget) { + if (IsValidSpell(spell_id) && spells[spell_id].target_type == ST_AETarget) { return true; } @@ -103,8 +103,8 @@ bool IsLifetapSpell(uint16 spell_id) { // Ancient Lifebane: 2115 if (IsValidSpell(spell_id) && - (spells[spell_id].targettype == ST_Tap || - spells[spell_id].targettype == ST_TargetAETap || + (spells[spell_id].target_type == ST_Tap || + spells[spell_id].target_type == ST_TargetAETap || spell_id == 2115)) return true; @@ -124,7 +124,7 @@ bool IsStunSpell(uint16 spell_id) bool IsSummonSpell(uint16 spellid) { for (int o = 0; o < EFFECT_COUNT; o++) { - uint32 tid = spells[spellid].effectid[o]; + uint32 tid = spells[spellid].effect_id[o]; if (tid == SE_SummonPet || tid == SE_SummonItem || tid == SE_SummonPC) return true; } @@ -140,10 +140,10 @@ bool IsEvacSpell(uint16 spellid) bool IsDamageSpell(uint16 spellid) { for (int o = 0; o < EFFECT_COUNT; o++) { - uint32 tid = spells[spellid].effectid[o]; + uint32 tid = spells[spellid].effect_id[o]; if ((tid == SE_CurrentHPOnce || tid == SE_CurrentHP) && - spells[spellid].targettype != ST_Tap && spells[spellid].buffduration < 1 && - spells[spellid].base[o] < 0) + spells[spellid].target_type != ST_Tap && spells[spellid].buff_duration < 1 && + spells[spellid].base_value[o] < 0) return true; } @@ -163,8 +163,8 @@ bool IsCureSpell(uint16 spell_id) bool CureEffect = false; for(int i = 0; i < EFFECT_COUNT; i++){ - if (sp.effectid[i] == SE_DiseaseCounter || sp.effectid[i] == SE_PoisonCounter - || sp.effectid[i] == SE_CurseCounter || sp.effectid[i] == SE_CorruptionCounter) + if (sp.effect_id[i] == SE_DiseaseCounter || sp.effect_id[i] == SE_PoisonCounter + || sp.effect_id[i] == SE_CurseCounter || sp.effect_id[i] == SE_CorruptionCounter) CureEffect = true; } @@ -179,8 +179,8 @@ bool IsSlowSpell(uint16 spell_id) const SPDat_Spell_Struct &sp = spells[spell_id]; for(int i = 0; i < EFFECT_COUNT; i++) - if ((sp.effectid[i] == SE_AttackSpeed && sp.base[i] < 100) || - (sp.effectid[i] == SE_AttackSpeed4)) + if ((sp.effect_id[i] == SE_AttackSpeed && sp.base_value[i] < 100) || + (sp.effect_id[i] == SE_AttackSpeed4)) return true; return false; @@ -191,8 +191,8 @@ bool IsHasteSpell(uint16 spell_id) const SPDat_Spell_Struct &sp = spells[spell_id]; for(int i = 0; i < EFFECT_COUNT; i++) - if(sp.effectid[i] == SE_AttackSpeed) - return (sp.base[i] < 100); + if(sp.effect_id[i] == SE_AttackSpeed) + return (sp.base_value[i] < 100); return false; } @@ -210,7 +210,7 @@ bool IsPercentalHealSpell(uint16 spell_id) bool IsGroupOnlySpell(uint16 spell_id) { - return IsValidSpell(spell_id) && spells[spell_id].goodEffect == 2; + return IsValidSpell(spell_id) && spells[spell_id].good_effect == 2; } bool IsBeneficialSpell(uint16 spell_id) @@ -219,10 +219,10 @@ bool IsBeneficialSpell(uint16 spell_id) return false; // You'd think just checking goodEffect flag would be enough? - if (spells[spell_id].goodEffect == 1) { + if (spells[spell_id].good_effect == 1) { // If the target type is ST_Self or ST_Pet and is a SE_CancleMagic spell // it is not Beneficial - SpellTargetType tt = spells[spell_id].targettype; + SpellTargetType tt = spells[spell_id].target_type; if (tt != ST_Self && tt != ST_Pet && IsEffectInSpell(spell_id, SE_CancelMagic)) return false; @@ -231,14 +231,14 @@ bool IsBeneficialSpell(uint16 spell_id) // We need to check more things! if (tt == ST_Target || tt == ST_AETarget || tt == ST_Animal || tt == ST_Undead || tt == ST_Pet) { - uint16 sai = spells[spell_id].SpellAffectIndex; + uint16 sai = spells[spell_id].spell_affect_index; // If the resisttype is magic and SpellAffectIndex is Calm/memblur/dispell sight // it's not beneficial - if (spells[spell_id].resisttype == RESIST_MAGIC) { + if (spells[spell_id].resist_type == RESIST_MAGIC) { // checking these SAI cause issues with the rng defensive proc line // So I guess instead of fixing it for real, just a quick hack :P - if (spells[spell_id].effectid[0] != SE_DefensiveProc && + if (spells[spell_id].effect_id[0] != SE_DefensiveProc && (sai == SAI_Calm || sai == SAI_Dispell_Sight || sai == SAI_Memory_Blur || sai == SAI_Calm_Song)) return false; @@ -252,7 +252,7 @@ bool IsBeneficialSpell(uint16 spell_id) } // And finally, if goodEffect is not 0 or if it's a group spell it's beneficial - return spells[spell_id].goodEffect != 0 || IsGroupSpell(spell_id); + return spells[spell_id].good_effect != 0 || IsGroupSpell(spell_id); } bool IsDetrimentalSpell(uint16 spell_id) @@ -364,8 +364,8 @@ bool IsImprovedDamageSpell(uint16 spell_id) bool IsAEDurationSpell(uint16 spell_id) { if (IsValidSpell(spell_id) && - (spells[spell_id].targettype == ST_AETarget || spells[spell_id].targettype == ST_UndeadAE) && - spells[spell_id].AEDuration != 0) + (spells[spell_id].target_type == ST_AETarget || spells[spell_id].target_type == ST_UndeadAE) && + spells[spell_id].aoe_duration != 0) return true; return false; @@ -383,7 +383,7 @@ bool IsPureNukeSpell(uint16 spell_id) effect_count++; if (effect_count == 1 && IsEffectInSpell(spell_id, SE_CurrentHP) && - spells[spell_id].buffduration == 0 && IsDamageSpell(spell_id)) + spells[spell_id].buff_duration == 0 && IsDamageSpell(spell_id)) return true; return false; @@ -392,7 +392,7 @@ bool IsPureNukeSpell(uint16 spell_id) bool IsAENukeSpell(uint16 spell_id) { if (IsValidSpell(spell_id) && IsPureNukeSpell(spell_id) && - spells[spell_id].aoerange > 0) + spells[spell_id].aoe_range > 0) return true; return false; @@ -401,7 +401,7 @@ bool IsAENukeSpell(uint16 spell_id) bool IsPBAENukeSpell(uint16 spell_id) { if (IsValidSpell(spell_id) && IsPureNukeSpell(spell_id) && - spells[spell_id].aoerange > 0 && spells[spell_id].targettype == ST_AECaster) + spells[spell_id].aoe_range > 0 && spells[spell_id].target_type == ST_AECaster) return true; return false; @@ -410,7 +410,7 @@ bool IsPBAENukeSpell(uint16 spell_id) bool IsAERainNukeSpell(uint16 spell_id) { if (IsValidSpell(spell_id) && IsPureNukeSpell(spell_id) && - spells[spell_id].aoerange > 0 && spells[spell_id].AEDuration > 1000) + spells[spell_id].aoe_range > 0 && spells[spell_id].aoe_duration > 1000) return true; return false; @@ -424,12 +424,12 @@ bool IsPartialCapableSpell(uint16 spell_id) // spell uses 600 (partial) scale if first effect is damage, else it uses 200 scale. // this includes DoTs. no_partial_resist excludes spells like necro snares for (int o = 0; o < EFFECT_COUNT; o++) { - auto tid = spells[spell_id].effectid[o]; + auto tid = spells[spell_id].effect_id[o]; if (IsBlankSpellEffect(spell_id, o)) continue; - if ((tid == SE_CurrentHPOnce || tid == SE_CurrentHP) && spells[spell_id].base[o] < 0) + if ((tid == SE_CurrentHPOnce || tid == SE_CurrentHP) && spells[spell_id].base_value[o] < 0) return true; return false; @@ -452,9 +452,9 @@ bool IsResistableSpell(uint16 spell_id) bool IsGroupSpell(uint16 spell_id) { if (IsValidSpell(spell_id) && - (spells[spell_id].targettype == ST_AEBard || - spells[spell_id].targettype == ST_Group || - spells[spell_id].targettype == ST_GroupTeleport)) + (spells[spell_id].target_type == ST_AEBard || + spells[spell_id].target_type == ST_Group || + spells[spell_id].target_type == ST_GroupTeleport)) return true; return false; @@ -464,7 +464,7 @@ bool IsGroupSpell(uint16 spell_id) bool IsTGBCompatibleSpell(uint16 spell_id) { if (IsValidSpell(spell_id) && - (!IsDetrimentalSpell(spell_id) && spells[spell_id].buffduration != 0 && + (!IsDetrimentalSpell(spell_id) && spells[spell_id].buff_duration != 0 && !IsBardSong(spell_id) && !IsEffectInSpell(spell_id, SE_Illusion))) return true; @@ -473,7 +473,7 @@ bool IsTGBCompatibleSpell(uint16 spell_id) bool IsBardSong(uint16 spell_id) { - if (IsValidSpell(spell_id) && spells[spell_id].classes[BARD - 1] < 255 && !spells[spell_id].IsDisciplineBuff) + if (IsValidSpell(spell_id) && spells[spell_id].classes[BARD - 1] < 255 && !spells[spell_id].is_discipline) return true; return false; @@ -487,7 +487,7 @@ bool IsEffectInSpell(uint16 spellid, int effect) return false; for (j = 0; j < EFFECT_COUNT; j++) - if (spells[spellid].effectid[j] == effect) + if (spells[spellid].effect_id[j] == effect) return true; return false; @@ -498,15 +498,15 @@ bool IsEffectInSpell(uint16 spellid, int effect) // the blanks bool IsBlankSpellEffect(uint16 spellid, int effect_index) { - int effect, base, formula; + int effect, base_value, formula; - effect = spells[spellid].effectid[effect_index]; - base = spells[spellid].base[effect_index]; + effect = spells[spellid].effect_id[effect_index]; + base_value = spells[spellid].base_value[effect_index]; formula = spells[spellid].formula[effect_index]; // SE_CHA is "spacer" // SE_Stacking* are also considered blank where this is used - if (effect == SE_Blank || (effect == SE_CHA && base == 0 && formula == 100) || + if (effect == SE_Blank || (effect == SE_CHA && base_value == 0 && formula == 100) || effect == SE_StackingCommand_Block || effect == SE_StackingCommand_Overwrite) return true; @@ -561,7 +561,7 @@ int GetSpellEffectIndex(uint16 spell_id, int effect) return -1; for (i = 0; i < EFFECT_COUNT; i++) - if (spells[spell_id].effectid[i] == effect) + if (spells[spell_id].effect_id[i] == effect) return i; return -1; @@ -591,7 +591,7 @@ bool BeneficialSpell(uint16 spell_id) /*|| spells[spell_id].stacking == 27*/ ) return true; - switch (spells[spell_id].goodEffect) { + switch (spells[spell_id].good_effect) { case 1: case 3: return true; @@ -602,7 +602,7 @@ bool BeneficialSpell(uint16 spell_id) bool GroupOnlySpell(uint16 spell_id) { - switch (spells[spell_id].goodEffect) { + switch (spells[spell_id].good_effect) { case 2: case 3: return true; @@ -623,9 +623,9 @@ int32 CalculatePoisonCounters(uint16 spell_id) int32 Counters = 0; for (int i = 0; i < EFFECT_COUNT; i++) - if (spells[spell_id].effectid[i] == SE_PoisonCounter && - spells[spell_id].base[i] > 0) - Counters += spells[spell_id].base[i]; + if (spells[spell_id].effect_id[i] == SE_PoisonCounter && + spells[spell_id].base_value[i] > 0) + Counters += spells[spell_id].base_value[i]; return Counters; } @@ -637,9 +637,9 @@ int32 CalculateDiseaseCounters(uint16 spell_id) int32 Counters = 0; for (int i = 0; i < EFFECT_COUNT; i++) - if(spells[spell_id].effectid[i] == SE_DiseaseCounter && - spells[spell_id].base[i] > 0) - Counters += spells[spell_id].base[i]; + if(spells[spell_id].effect_id[i] == SE_DiseaseCounter && + spells[spell_id].base_value[i] > 0) + Counters += spells[spell_id].base_value[i]; return Counters; } @@ -651,9 +651,9 @@ int32 CalculateCurseCounters(uint16 spell_id) int32 Counters = 0; for (int i = 0; i < EFFECT_COUNT; i++) - if(spells[spell_id].effectid[i] == SE_CurseCounter && - spells[spell_id].base[i] > 0) - Counters += spells[spell_id].base[i]; + if(spells[spell_id].effect_id[i] == SE_CurseCounter && + spells[spell_id].base_value[i] > 0) + Counters += spells[spell_id].base_value[i]; return Counters; } @@ -665,9 +665,9 @@ int32 CalculateCorruptionCounters(uint16 spell_id) int32 Counters = 0; for (int i = 0; i < EFFECT_COUNT; i++) - if (spells[spell_id].effectid[i] == SE_CorruptionCounter && - spells[spell_id].base[i] > 0) - Counters += spells[spell_id].base[i]; + if (spells[spell_id].effect_id[i] == SE_CorruptionCounter && + spells[spell_id].base_value[i] > 0) + Counters += spells[spell_id].base_value[i]; return Counters; } @@ -695,7 +695,7 @@ bool IsDisciplineBuff(uint16 spell_id) if (!IsValidSpell(spell_id)) return false; - if (spells[spell_id].IsDisciplineBuff && spells[spell_id].targettype == ST_Self) + if (spells[spell_id].is_discipline && spells[spell_id].target_type == ST_Self) return true; return false; @@ -707,7 +707,7 @@ bool IsDiscipline(uint16 spell_id) return false; if (spells[spell_id].mana == 0 && - (spells[spell_id].EndurCost || spells[spell_id].EndurUpkeep)) + (spells[spell_id].endurance_cost || spells[spell_id].endurance_upkeep)) return true; return false; @@ -719,7 +719,7 @@ bool IsCombatSkill(uint16 spell_id) return false; //Check if Discipline - if ((spells[spell_id].mana == 0 && (spells[spell_id].EndurCost || spells[spell_id].EndurUpkeep))) + if ((spells[spell_id].mana == 0 && (spells[spell_id].endurance_cost || spells[spell_id].endurance_upkeep))) return true; return false; @@ -738,7 +738,7 @@ bool IsRuneSpell(uint16 spell_id) { if (IsValidSpell(spell_id)) for (int i = 0; i < EFFECT_COUNT; i++) - if (spells[spell_id].effectid[i] == SE_Rune) + if (spells[spell_id].effect_id[i] == SE_Rune) return true; return false; @@ -748,7 +748,7 @@ bool IsMagicRuneSpell(uint16 spell_id) { if(IsValidSpell(spell_id)) for(int i = 0; i < EFFECT_COUNT; i++) - if(spells[spell_id].effectid[i] == SE_AbsorbMagicAtt) + if(spells[spell_id].effect_id[i] == SE_AbsorbMagicAtt) return true; return false; @@ -758,8 +758,8 @@ bool IsManaTapSpell(uint16 spell_id) { if (IsValidSpell(spell_id)) for (int i = 0; i < EFFECT_COUNT; i++) - if (spells[spell_id].effectid[i] == SE_CurrentMana && - spells[spell_id].targettype == ST_Tap) + if (spells[spell_id].effect_id[i] == SE_CurrentMana && + spells[spell_id].target_type == ST_Tap) return true; return false; @@ -788,8 +788,8 @@ bool IsPartialDeathSaveSpell(uint16 spell_id) return false; for (int i = 0; i < EFFECT_COUNT; i++) - if (spells[spell_id].effectid[i] == SE_DeathSave && - spells[spell_id].base[i] == 1) + if (spells[spell_id].effect_id[i] == SE_DeathSave && + spells[spell_id].base_value[i] == 1) return true; return false; @@ -802,8 +802,8 @@ bool IsFullDeathSaveSpell(uint16 spell_id) return false; for (int i = 0; i < EFFECT_COUNT; i++) - if (spells[spell_id].effectid[i] == SE_DeathSave && - spells[spell_id].base[i] == 2) + if (spells[spell_id].effect_id[i] == SE_DeathSave && + spells[spell_id].base_value[i] == 2) return true; return false; @@ -852,7 +852,7 @@ bool IsGateSpell(uint16 spell_id) bool IsPlayerIllusionSpell(uint16 spell_id) { if (IsEffectInSpell(spell_id, SE_Illusion) && - spells[spell_id].targettype == ST_Self) + spells[spell_id].target_type == ST_Self) return true; return false; @@ -861,7 +861,7 @@ bool IsPlayerIllusionSpell(uint16 spell_id) int GetSpellEffectDescNum(uint16 spell_id) { if (IsValidSpell(spell_id)) - return spells[spell_id].effectdescnum; + return spells[spell_id].effect_description_id; return -1; } @@ -872,12 +872,12 @@ DmgShieldType GetDamageShieldType(uint16 spell_id, int32 DSType) // else, make a guess, based on the resist type. Default return value is DS_THORNS if (IsValidSpell(spell_id)) { LogSpells("DamageShieldType for spell [{}] ([{}]) is [{}]", spell_id, - spells[spell_id].name, spells[spell_id].DamageShieldType); + spells[spell_id].name, spells[spell_id].damage_shield_type); - if (spells[spell_id].DamageShieldType) - return (DmgShieldType) spells[spell_id].DamageShieldType; + if (spells[spell_id].damage_shield_type) + return (DmgShieldType) spells[spell_id].damage_shield_type; - switch (spells[spell_id].resisttype) { + switch (spells[spell_id].resist_type) { case RESIST_COLD: return DS_TORMENT; case RESIST_FIRE: @@ -907,12 +907,12 @@ bool IsLDoNObjectSpell(uint16 spell_id) int32 GetSpellResistType(uint16 spell_id) { - return spells[spell_id].resisttype; + return spells[spell_id].resist_type; } int32 GetSpellTargetType(uint16 spell_id) { - return (int32)spells[spell_id].targettype; + return (int32)spells[spell_id].target_type; } bool IsHealOverTimeSpell(uint16 spell_id) @@ -937,7 +937,7 @@ bool IsFastHealSpell(uint16 spell_id) const int MaxFastHealCastingTime = 2000; if (spells[spell_id].cast_time <= MaxFastHealCastingTime && - spells[spell_id].effectid[0] == 0 && spells[spell_id].base[0] > 0 && + spells[spell_id].effect_id[0] == 0 && spells[spell_id].base_value[0] > 0 && !IsGroupSpell(spell_id)) return true; @@ -949,7 +949,7 @@ bool IsVeryFastHealSpell(uint16 spell_id) const int MaxFastHealCastingTime = 1000; if (spells[spell_id].cast_time <= MaxFastHealCastingTime && - spells[spell_id].effectid[0] == 0 && spells[spell_id].base[0] > 0 && + spells[spell_id].effect_id[0] == 0 && spells[spell_id].base_value[0] > 0 && !IsGroupSpell(spell_id)) return true; @@ -958,8 +958,8 @@ bool IsVeryFastHealSpell(uint16 spell_id) bool IsRegularSingleTargetHealSpell(uint16 spell_id) { - if(spells[spell_id].effectid[0] == 0 && spells[spell_id].base[0] > 0 && - spells[spell_id].targettype == ST_Target && spells[spell_id].buffduration == 0 && + if(spells[spell_id].effect_id[0] == 0 && spells[spell_id].base_value[0] > 0 && + spells[spell_id].target_type == ST_Target && spells[spell_id].buff_duration == 0 && !IsCompleteHealSpell(spell_id) && !IsHealOverTimeSpell(spell_id) && !IsGroupSpell(spell_id)) return true; @@ -985,7 +985,7 @@ bool IsGroupCompleteHealSpell(uint16 spell_id) bool IsGroupHealOverTimeSpell(uint16 spell_id) { - if( IsGroupSpell(spell_id) && IsHealOverTimeSpell(spell_id) && spells[spell_id].buffduration < 10) + if( IsGroupSpell(spell_id) && IsHealOverTimeSpell(spell_id) && spells[spell_id].buff_duration < 10) return true; return false; @@ -1016,8 +1016,8 @@ bool IsResistDebuffSpell(uint16 spell_id) bool IsSelfConversionSpell(uint16 spell_id) { if (GetSpellTargetType(spell_id) == ST_Self && IsEffectInSpell(spell_id, SE_CurrentMana) && - IsEffectInSpell(spell_id, SE_CurrentHP) && spells[spell_id].base[GetSpellEffectIndex(spell_id, SE_CurrentMana)] > 0 && - spells[spell_id].base[GetSpellEffectIndex(spell_id, SE_CurrentHP)] < 0) + IsEffectInSpell(spell_id, SE_CurrentHP) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_CurrentMana)] > 0 && + spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_CurrentHP)] < 0) return true; else return false; @@ -1026,7 +1026,7 @@ bool IsSelfConversionSpell(uint16 spell_id) // returns true for both detrimental and beneficial buffs bool IsBuffSpell(uint16 spell_id) { - if (IsValidSpell(spell_id) && (spells[spell_id].buffduration || spells[spell_id].buffdurationformula)) + if (IsValidSpell(spell_id) && (spells[spell_id].buff_duration || spells[spell_id].buff_duration_formula)) return true; return false; @@ -1034,7 +1034,7 @@ bool IsBuffSpell(uint16 spell_id) bool IsPersistDeathSpell(uint16 spell_id) { - if (IsValidSpell(spell_id) && spells[spell_id].persistdeath) + if (IsValidSpell(spell_id) && spells[spell_id].persist_death) return true; return false; @@ -1051,8 +1051,8 @@ bool IsSuspendableSpell(uint16 spell_id) uint32 GetMorphTrigger(uint32 spell_id) { for (int i = 0; i < EFFECT_COUNT; ++i) - if (spells[spell_id].effectid[i] == SE_CastOnFadeEffect) - return spells[spell_id].base[i]; + if (spells[spell_id].effect_id[i] == SE_CastOnFadeEffect) + return spells[spell_id].base_value[i]; return 0; } @@ -1060,9 +1060,9 @@ uint32 GetMorphTrigger(uint32 spell_id) bool IsCastonFadeDurationSpell(uint16 spell_id) { for (int i = 0; i < EFFECT_COUNT; ++i) { - if (spells[spell_id].effectid[i] == SE_CastOnFadeEffect - || spells[spell_id].effectid[i] == SE_CastOnFadeEffectNPC - || spells[spell_id].effectid[i] == SE_CastOnFadeEffectAlways){ + if (spells[spell_id].effect_id[i] == SE_CastOnFadeEffect + || spells[spell_id].effect_id[i] == SE_CastOnFadeEffectNPC + || spells[spell_id].effect_id[i] == SE_CastOnFadeEffectAlways){ return true; } @@ -1073,7 +1073,7 @@ bool IsCastonFadeDurationSpell(uint16 spell_id) bool IsPowerDistModSpell(uint16 spell_id) { if (IsValidSpell(spell_id) && - (spells[spell_id].max_dist_mod || spells[spell_id].min_dist_mod) && spells[spell_id].max_dist > spells[spell_id].min_dist) + (spells[spell_id].max_distance_mod || spells[spell_id].min_distance_mod) && spells[spell_id].max_distance > spells[spell_id].min_distance) return true; return false; @@ -1082,8 +1082,8 @@ bool IsPowerDistModSpell(uint16 spell_id) uint32 GetPartialMeleeRuneReduction(uint32 spell_id) { for (int i = 0; i < EFFECT_COUNT; ++i) - if (spells[spell_id].effectid[i] == SE_MitigateMeleeDamage) - return spells[spell_id].base[i]; + if (spells[spell_id].effect_id[i] == SE_MitigateMeleeDamage) + return spells[spell_id].base_value[i]; return 0; } @@ -1091,8 +1091,8 @@ uint32 GetPartialMeleeRuneReduction(uint32 spell_id) uint32 GetPartialMagicRuneReduction(uint32 spell_id) { for (int i = 0; i < EFFECT_COUNT; ++i) - if (spells[spell_id].effectid[i] == SE_MitigateSpellDamage) - return spells[spell_id].base[i]; + if (spells[spell_id].effect_id[i] == SE_MitigateSpellDamage) + return spells[spell_id].base_value[i]; return 0; } @@ -1100,8 +1100,8 @@ uint32 GetPartialMagicRuneReduction(uint32 spell_id) uint32 GetPartialMeleeRuneAmount(uint32 spell_id) { for (int i = 0; i < EFFECT_COUNT; ++i) - if (spells[spell_id].effectid[i] == SE_MitigateMeleeDamage) - return spells[spell_id].max[i]; + if (spells[spell_id].effect_id[i] == SE_MitigateMeleeDamage) + return spells[spell_id].max_value[i]; return 0; } @@ -1109,8 +1109,8 @@ uint32 GetPartialMeleeRuneAmount(uint32 spell_id) uint32 GetPartialMagicRuneAmount(uint32 spell_id) { for (int i = 0; i < EFFECT_COUNT; ++i) - if (spells[spell_id].effectid[i] == SE_MitigateSpellDamage) - return spells[spell_id].max[i]; + if (spells[spell_id].effect_id[i] == SE_MitigateSpellDamage) + return spells[spell_id].max_value[i]; return 0; } @@ -1119,7 +1119,7 @@ uint32 GetPartialMagicRuneAmount(uint32 spell_id) bool DetrimentalSpellAllowsRest(uint16 spell_id) { if (IsValidSpell(spell_id)) - return spells[spell_id].AllowRest; + return spells[spell_id].allow_rest; return false; } @@ -1138,7 +1138,7 @@ bool IsStackableDot(uint16 spell_id) if (!IsValidSpell(spell_id)) return false; const auto &spell = spells[spell_id]; - if (spell.dot_stacking_exempt || spell.goodEffect || !spell.buffdurationformula) + if (spell.unstackable_dot || spell.good_effect || !spell.buff_duration_formula) return false; return IsEffectInSpell(spell_id, SE_CurrentHP) || IsEffectInSpell(spell_id, SE_GravityEffect); } @@ -1307,7 +1307,7 @@ bool IsFocusLimit(int spa) uint32 GetNimbusEffect(uint16 spell_id) { if (IsValidSpell(spell_id)) - return (int32)spells[spell_id].NimbusEffect; + return (int32)spells[spell_id].nimbus_effect; return 0; } @@ -1321,9 +1321,9 @@ int32 GetFuriousBash(uint16 spell_id) int32 mod = 0; for (int i = 0; i < EFFECT_COUNT; ++i) - if (spells[spell_id].effectid[i] == SE_SpellHateMod) - mod = spells[spell_id].base[i]; - else if (spells[spell_id].effectid[i] == SE_LimitEffect && spells[spell_id].base[i] == 999) + if (spells[spell_id].effect_id[i] == SE_SpellHateMod) + mod = spells[spell_id].base_value[i]; + else if (spells[spell_id].effect_id[i] == SE_LimitEffect && spells[spell_id].base_value[i] == 999) found_effect_limit = true; if (found_effect_limit) @@ -1344,8 +1344,8 @@ bool IsSpellUsableThisZoneType(uint16 spell_id, uint8 zone_type) { //check if spell can be cast in any zone (-1 or 255), then if spell zonetype matches zone's zonetype // || spells[spell_id].zonetype == 255 comparing signed 8 bit int to 255 is always false - if (IsValidSpell(spell_id) && (spells[spell_id].zonetype == -1 || - spells[spell_id].zonetype == zone_type)) + if (IsValidSpell(spell_id) && (spells[spell_id].zone_type == -1 || + spells[spell_id].zone_type == zone_type)) return true; return false; @@ -1358,11 +1358,11 @@ const char* GetSpellName(uint16 spell_id) bool SpellRequiresTarget(int spell_id) { - if (spells[spell_id].targettype == ST_AEClientV1 || - spells[spell_id].targettype == ST_Self || - spells[spell_id].targettype == ST_AECaster || - spells[spell_id].targettype == ST_Ring || - spells[spell_id].targettype == ST_Beam) { + if (spells[spell_id].target_type == ST_AEClientV1 || + spells[spell_id].target_type == ST_Self || + spells[spell_id].target_type == ST_AECaster || + spells[spell_id].target_type == ST_Ring || + spells[spell_id].target_type == ST_Beam) { return false; } return true; @@ -1377,7 +1377,7 @@ bool IsInstrumentModAppliedToSpellEffect(int32 spell_id, int effect) //Only modify instant endurance or mana effects (Ie. Mana drain, Crescendo line) case SE_CurrentEndurance: case SE_CurrentMana: { - if (spells[spell_id].buffduration == 0) { + if (spells[spell_id].buff_duration == 0) { return true; } //Mana regen is not modified. @@ -1483,94 +1483,94 @@ int GetSpellStatValue(uint32 spell_id, const char* stat_identifier, uint8 slot) } if (slot < 12) { - if (id == "base") { return spells[spell_id].base[slot]; } - else if (id == "base2") { return spells[spell_id].base2[slot]; } - else if (id == "max") { return spells[spell_id].max[slot]; } + if (id == "base") { return spells[spell_id].base_value[slot]; } + else if (id == "base2") { return spells[spell_id].limit_value[slot]; } + else if (id == "max") { return spells[spell_id].max_value[slot]; } else if (id == "formula") { return spells[spell_id].formula[slot]; } - else if (id == "effectid") { return spells[spell_id].effectid[slot]; } + else if (id == "effectid") { return spells[spell_id].effect_id[slot]; } } if (slot < 4) { - if (id == "components") { return spells[spell_id].components[slot]; } - else if (id == "component_counts") { return spells[spell_id].component_counts[slot]; } - else if (id == "noexpendreagent") { return spells[spell_id].NoexpendReagent[slot]; } + if (id == "components") { return spells[spell_id].component[slot]; } + else if (id == "component_counts") { return spells[spell_id].component_count[slot]; } + else if (id == "noexpendreagent") { return spells[spell_id].no_expend_reagent[slot]; } } if (id == "range") { return static_cast(spells[spell_id].range); } - else if (id == "aoerange") { return static_cast(spells[spell_id].aoerange); } - else if (id == "pushback") { return static_cast(spells[spell_id].pushback); } - else if (id == "pushup") { return static_cast(spells[spell_id].pushup); } + else if (id == "aoe_range") { return static_cast(spells[spell_id].aoe_range); } + else if (id == "push_back") { return static_cast(spells[spell_id].push_back); } + else if (id == "push_up") { return static_cast(spells[spell_id].push_up); } else if (id == "cast_time") { return spells[spell_id].cast_time; } else if (id == "recovery_time") { return spells[spell_id].recovery_time; } else if (id == "recast_time") { return spells[spell_id].recast_time; } - else if (id == "buffdurationformula") { return spells[spell_id].buffdurationformula; } - else if (id == "buffduration") { return spells[spell_id].buffduration; } - else if (id == "aeduration") { return spells[spell_id].AEDuration; } + else if (id == "buff_duration_formula") { return spells[spell_id].buff_duration_formula; } + else if (id == "buff_duration") { return spells[spell_id].buff_duration; } + else if (id == "aeduration") { return spells[spell_id].aoe_duration; } else if (id == "mana") { return spells[spell_id].mana; } //else if (id == "LightType") {stat = spells[spell_id].LightType; } - Not implemented - else if (id == "goodeffect") { return spells[spell_id].goodEffect; } - else if (id == "activated") { return spells[spell_id].Activated; } - else if (id == "resisttype") { return spells[spell_id].resisttype; } - else if (id == "targettype") { return spells[spell_id].targettype; } - else if (id == "basediff") { return spells[spell_id].basediff; } + else if (id == "goodeffect") { return spells[spell_id].good_effect; } + else if (id == "activated") { return spells[spell_id].activated; } + else if (id == "resisttype") { return spells[spell_id].resist_type; } + else if (id == "targettype") { return spells[spell_id].target_type; } + else if (id == "basediff") { return spells[spell_id].base_difficulty; } else if (id == "skill") { return spells[spell_id].skill; } - else if (id == "zonetype") { return spells[spell_id].zonetype; } - else if (id == "environmenttype") { return spells[spell_id].EnvironmentType; } - else if (id == "timeofday") { return spells[spell_id].TimeOfDay; } - else if (id == "castinganim") { return spells[spell_id].CastingAnim; } - else if (id == "spellaffectindex") { return spells[spell_id].SpellAffectIndex; } + else if (id == "zonetype") { return spells[spell_id].zone_type; } + else if (id == "environmenttype") { return spells[spell_id].environment_type; } + else if (id == "timeofday") { return spells[spell_id].time_of_day; } + else if (id == "castinganim") { return spells[spell_id].casting_animation; } + else if (id == "spellaffectindex") { return spells[spell_id].spell_affect_index; } else if (id == "disallow_sit") { return spells[spell_id].disallow_sit; } //else if (id == "spellanim") {stat = spells[spell_id].spellanim; } - Not implemented else if (id == "uninterruptable") { return spells[spell_id].uninterruptable; } - else if (id == "resistdiff") { return spells[spell_id].ResistDiff; } - else if (id == "dot_stacking_exempt") { return spells[spell_id].dot_stacking_exempt; } - else if (id == "recourselink") { return spells[spell_id].RecourseLink; } + else if (id == "resistdiff") { return spells[spell_id].resist_difficulty; } + else if (id == "dot_stacking_exempt") { return spells[spell_id].unstackable_dot; } + else if (id == "recourselink") { return spells[spell_id].recourse_link; } else if (id == "no_partial_resist") { return spells[spell_id].no_partial_resist; } else if (id == "short_buff_box") { return spells[spell_id].short_buff_box; } - else if (id == "descnum") { return spells[spell_id].descnum; } - else if (id == "effectdescnum") { return spells[spell_id].effectdescnum; } + else if (id == "descnum") { return spells[spell_id].description_id; } + else if (id == "effectdescnum") { return spells[spell_id].effect_description_id; } else if (id == "npc_no_los") { return spells[spell_id].npc_no_los; } else if (id == "feedbackable") { return spells[spell_id].feedbackable; } else if (id == "reflectable") { return spells[spell_id].reflectable; } - else if (id == "bonushate") { return spells[spell_id].bonushate; } - else if (id == "endurcost") { return spells[spell_id].EndurCost; } - else if (id == "endurtimerindex") { return spells[spell_id].EndurTimerIndex; } - else if (id == "isdisciplinebuff") { return spells[spell_id].IsDisciplineBuff; } - else if (id == "hateadded") { return spells[spell_id].HateAdded; } - else if (id == "endurupkeep") { return spells[spell_id].EndurUpkeep; } - else if (id == "numhitstype") { return spells[spell_id].numhitstype; } - else if (id == "numhits") { return spells[spell_id].numhits; } - else if (id == "pvpresistbase") { return spells[spell_id].pvpresistbase; } - else if (id == "pvpresistcalc") { return spells[spell_id].pvpresistcalc; } - else if (id == "pvpresistcap") { return spells[spell_id].pvpresistcap; } + else if (id == "bonushate") { return spells[spell_id].bonus_hate; } + else if (id == "endurcost") { return spells[spell_id].endurance_cost; } + else if (id == "endurtimerindex") { return spells[spell_id].timer_id; } + else if (id == "isdisciplinebuff") { return spells[spell_id].is_discipline; } + else if (id == "hateadded") { return spells[spell_id].hate_added; } + else if (id == "endurupkeep") { return spells[spell_id].endurance_upkeep; } + else if (id == "numhitstype") { return spells[spell_id].hit_number_type; } + else if (id == "numhits") { return spells[spell_id].hit_number; } + else if (id == "pvpresistbase") { return spells[spell_id].pvp_resist_base; } + else if (id == "pvpresistcalc") { return spells[spell_id].pvp_resist_per_level; } + else if (id == "pvpresistcap") { return spells[spell_id].pvp_resist_cap; } else if (id == "spell_category") { return spells[spell_id].spell_category; } else if (id == "can_mgb") { return spells[spell_id].can_mgb; } else if (id == "dispel_flag") { return spells[spell_id].dispel_flag; } - else if (id == "minresist") { return spells[spell_id].MinResist; } - else if (id == "maxresist") { return spells[spell_id].MaxResist; } + else if (id == "minresist") { return spells[spell_id].min_resist; } + else if (id == "maxresist") { return spells[spell_id].max_resist; } else if (id == "viral_targets") { return spells[spell_id].viral_targets; } else if (id == "viral_timer") { return spells[spell_id].viral_timer; } - else if (id == "nimbuseffect") { return spells[spell_id].NimbusEffect; } + else if (id == "nimbuseffect") { return spells[spell_id].nimbus_effect; } else if (id == "directional_start") { return static_cast(spells[spell_id].directional_start); } else if (id == "directional_end") { return static_cast(spells[spell_id].directional_end); } else if (id == "not_focusable") { return spells[spell_id].not_focusable; } else if (id == "suspendable") { return spells[spell_id].suspendable; } else if (id == "viral_range") { return spells[spell_id].viral_range; } - else if (id == "spellgroup") { return spells[spell_id].spellgroup; } + else if (id == "spellgroup") { return spells[spell_id].spell_group; } else if (id == "rank") { return spells[spell_id].rank; } else if (id == "no_resist") { return spells[spell_id].no_resist; } - else if (id == "castrestriction") { return spells[spell_id].CastRestriction; } - else if (id == "allowrest") { return spells[spell_id].AllowRest; } - else if (id == "incombat") { return spells[spell_id].InCombat; } - else if (id == "outofcombat") { return spells[spell_id].OutofCombat; } - else if (id == "aemaxtargets") { return spells[spell_id].aemaxtargets; } + else if (id == "castrestriction") { return spells[spell_id].cast_restriction; } + else if (id == "allowrest") { return spells[spell_id].allow_rest; } + else if (id == "incombat") { return spells[spell_id].can_cast_in_combat; } + else if (id == "outofcombat") { return spells[spell_id].can_cast_out_of_combat; } + else if (id == "aemaxtargets") { return spells[spell_id].aoe_max_targets; } else if (id == "no_heal_damage_item_mod") { return spells[spell_id].no_heal_damage_item_mod; } - else if (id == "persistdeath") { return spells[spell_id].persistdeath; } - else if (id == "min_dist") { return static_cast(spells[spell_id].min_dist); } - else if (id == "min_dist_mod") { return static_cast(spells[spell_id].min_dist_mod); } - else if (id == "max_dist") { return static_cast(spells[spell_id].max_dist); } + else if (id == "persistdeath") { return spells[spell_id].persist_death; } + else if (id == "min_dist") { return static_cast(spells[spell_id].min_distance); } + else if (id == "min_dist_mod") { return static_cast(spells[spell_id].min_distance_mod); } + else if (id == "max_dist") { return static_cast(spells[spell_id].max_distance); } else if (id == "min_range") { return static_cast(spells[spell_id].min_range); } - else if (id == "damageshieldtype") { return spells[spell_id].DamageShieldType; } + else if (id == "damageshieldtype") { return spells[spell_id].damage_shield_type; } return 0; } diff --git a/common/spdat.h b/common/spdat.h index b4535cf1d..9710e1825 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -561,7 +561,7 @@ const uint32 SPELL_TYPES_INNATE = (SpellType_Nuke | SpellType_Lifetap | SpellTyp // These should not be used to determine spell category.. // They are a graphical affects (effects?) index only // TODO: import sai list -enum SpellAffectIndex { +enum spell_affect_index { SAI_Summon_Mount_Unclass = -1, SAI_Direct_Damage = 0, SAI_Heal_Cure = 1, @@ -1252,44 +1252,44 @@ struct SPDat_Spell_Struct /* 007 */ char cast_on_other[64]; // Message when spell is cast on someone else -- CASTEDOTHERTXT /* 008 */ char spell_fades[64]; // Spell fades -- SPELLGONE /* 009 */ float range; // -- RANGE -/* 010 */ float aoerange; // -- IMPACTRANGE -/* 011 */ float pushback; // -- OUTFORCE -/* 012 */ float pushup; // -- UPFORCE +/* 010 */ float aoe_range; // -- IMPACTRANGE +/* 011 */ float push_back; // -- OUTFORCE +/* 012 */ float push_up; // -- UPFORCE /* 013 */ uint32 cast_time; // Cast time -- CASTINGTIME /* 014 */ uint32 recovery_time; // Recovery time -- RECOVERYDELAY /* 015 */ uint32 recast_time; // Recast same spell time -- SPELLDELAY -/* 016 */ uint32 buffdurationformula; // -- DURATIONBASE -/* 017 */ uint32 buffduration; // -- DURATIONCAP -/* 018 */ uint32 AEDuration; // sentinel, rain of something -- IMPACTDURATION +/* 016 */ uint32 buff_duration_formula; // -- DURATIONBASE +/* 017 */ uint32 buff_duration; // -- DURATIONCAP +/* 018 */ uint32 aoe_duration; // sentinel, rain of something -- IMPACTDURATION /* 019 */ uint16 mana; // Mana Used -- MANACOST -/* 020 */ int base[EFFECT_COUNT]; //various purposes -- BASEAFFECT1 .. BASEAFFECT12 -/* 032 */ int base2[EFFECT_COUNT]; //various purposes -- BASE_EFFECT2_1 ... BASE_EFFECT2_12 -/* 044 */ int32 max[EFFECT_COUNT]; // -- AFFECT1CAP ... AFFECT12CAP +/* 020 */ int base_value[EFFECT_COUNT]; //various purposes -- BASEAFFECT1 .. BASEAFFECT12 +/* 032 */ int limit_value[EFFECT_COUNT]; //various purposes -- BASE_EFFECT2_1 ... BASE_EFFECT2_12 +/* 044 */ int32 max_value[EFFECT_COUNT]; // -- AFFECT1CAP ... AFFECT12CAP /* 056 */ //uint16 icon; // Spell icon -- IMAGENUMBER /* 057 */ //uint16 memicon; // Icon on membarthing -- MEMIMAGENUMBER -/* 058 */ int32 components[4]; // reagents -- EXPENDREAGENT1 ... EXPENDREAGENT4 -/* 062 */ int component_counts[4]; // amount of regents used -- EXPENDQTY1 ... EXPENDQTY4 -/* 066 */ int NoexpendReagent[4]; // focus items (Need but not used; Flame Lick has a Fire Beetle Eye focus.) +/* 058 */ int32 component[4]; // reagents -- EXPENDREAGENT1 ... EXPENDREAGENT4 +/* 062 */ int component_count[4]; // amount of regents used -- EXPENDQTY1 ... EXPENDQTY4 +/* 066 */ int no_expend_reagent[4]; // focus items (Need but not used; Flame Lick has a Fire Beetle Eye focus.) // If it is a number between 1-4 it means components[number] is a focus and not to expend it // If it is a valid itemid it means this item is a focus as well // -- NOEXPENDREAGENT1 ... NOEXPENDREAGENT4 /* 070 */ uint16 formula[EFFECT_COUNT]; // Spell's value formula -- LEVELAFFECT1MOD ... LEVELAFFECT12MOD /* 082 */ //int LightType; // probaly another effecttype flag -- LIGHTTYPE -/* 083 */ int8 goodEffect; //0=detrimental, 1=Beneficial, 2=Beneficial, Group Only -- BENEFICIAL -/* 084 */ int Activated; // probably another effecttype flag -- ACTIVATED -/* 085 */ int resisttype; // -- RESISTTYPE -/* 086 */ int effectid[EFFECT_COUNT]; // Spell's effects -- SPELLAFFECT1 ... SPELLAFFECT12 -/* 098 */ SpellTargetType targettype; // Spell's Target -- TYPENUMBER -/* 099 */ int basediff; // base difficulty fizzle adjustment -- BASEDIFFICULTY +/* 083 */ int8 good_effect; //0=detrimental, 1=Beneficial, 2=Beneficial, Group Only -- BENEFICIAL +/* 084 */ int activated; // probably another effecttype flag -- ACTIVATED +/* 085 */ int resist_type; // -- RESISTTYPE +/* 086 */ int effect_id[EFFECT_COUNT]; // Spell's effects -- SPELLAFFECT1 ... SPELLAFFECT12 +/* 098 */ SpellTargetType target_type; // Spell's Target -- TYPENUMBER +/* 099 */ int base_difficulty; // base difficulty fizzle adjustment -- BASEDIFFICULTY /* 100 */ EQ::skills::SkillType skill; // -- CASTINGSKILL -/* 101 */ int8 zonetype; // 01=Outdoors, 02=dungeons, ff=Any -- ZONETYPE -/* 102 */ int8 EnvironmentType; // -- ENVIRONMENTTYPE -/* 103 */ int8 TimeOfDay; // -- TIMEOFDAY +/* 101 */ int8 zone_type; // 01=Outdoors, 02=dungeons, ff=Any -- ZONETYPE +/* 102 */ int8 environment_type; // -- ENVIRONMENTTYPE +/* 103 */ int8 time_of_day; // -- TIMEOFDAY /* 104 */ uint8 classes[PLAYER_CLASS_COUNT]; // Classes, and their min levels -- WARRIORMIN ... BERSERKERMIN -/* 120 */ uint8 CastingAnim; // -- CASTINGANIM +/* 120 */ uint8 casting_animation; // -- CASTINGANIM /* 121 */ //uint8 TargetAnim; // -- TARGETANIM /* 122 */ //uint32 TravelType; // -- TRAVELTYPE -/* 123 */ uint16 SpellAffectIndex; // -- SPELLAFFECTINDEX +/* 123 */ uint16 spell_affect_index; // -- SPELLAFFECTINDEX /* 124 */ int8 disallow_sit; // 124: high-end Yaulp spells (V, VI, VII, VIII [Rk 1, 2, & 3], & Gallenite's Bark of Fury -- CANCELONSIT /* 125 */ int8 deity_agnostic;// 125: Words of the Skeptic -- DEITY_AGNOSTIC /* 126 */ int8 deities[16]; // Deity check. 201 - 216 per http://www.eqemulator.net/wiki/wikka.php?wakka=DeityList @@ -1301,36 +1301,36 @@ struct SPDat_Spell_Struct /* 144 */ int16 new_icon; // Spell icon used by the client in uifiles/default/spells??.tga, both for spell gems & buff window. Looks to depreciate icon & memicon -- NEW_ICON /* 145 */ //int16 spellanim; // Doesn't look like it's the same as #doanim, so not sure what this is, particles I think -- SPELL_EFFECT_INDEX /* 146 */ bool uninterruptable; // Looks like anything != 0 is uninterruptable. Values are mostly -1, 0, & 1 (Fetid Breath = 90?) -- NO_INTERRUPT -/* 147 */ int16 ResistDiff; // -- RESIST_MOD -/* 148 */ bool dot_stacking_exempt; // -- NOT_STACKABLE_DOT +/* 147 */ int16 resist_difficulty; // -- RESIST_MOD +/* 148 */ bool unstackable_dot; // -- NOT_STACKABLE_DOT /* 149 */ //int deletable; // -- DELETE_OK -/* 150 */ uint16 RecourseLink; // -- REFLECT_SPELLINDEX +/* 150 */ uint16 recourse_link; // -- REFLECT_SPELLINDEX /* 151 */ bool no_partial_resist; // 151: -1, 0, or 1 -- NO_PARTIAL_SAVE /* 152 */ //bool small_targets_only; // -- SMALL_TARGETS_ONLY /* 153 */ //bool uses_persistent_particles; // -- USES_PERSISTENT_PARTICLES /* 154 */ int8 short_buff_box; // != 0, goes to short buff box. -- BARD_BUFF_BOX -/* 155 */ int descnum; // eqstr of description of spell -- DESCRIPTION_INDEX -/* 156 */ int typedescnum; // eqstr of type description -- PRIMARY_CATEGORY -/* 157 */ int effectdescnum; // eqstr of effect description -- SECONDARY_CATEGORY_1 +/* 155 */ int description_id; // eqstr of description of spell -- DESCRIPTION_INDEX +/* 156 */ int type_description_id; // eqstr of type description -- PRIMARY_CATEGORY +/* 157 */ int effect_description_id; // eqstr of effect description -- SECONDARY_CATEGORY_1 /* 158 */ //int secondary_category_2; //Category Desc ID 3 -- SECONDARY_CATEGORY_2 /* 159 */ bool npc_no_los; // -- NO_NPC_LOS /* 160 */ bool feedbackable; // -- FEEDBACKABLE /* 161 */ bool reflectable; // -- REFLECTABLE -/* 162 */ int bonushate; // -- HATE_MOD +/* 162 */ int bonus_hate; // -- HATE_MOD /* 163 */ //int resist_per_level; // -- RESIST_PER_LEVEL /* 164 */ //int resist_cap; // for most spells this appears to mimic ResistDiff -- RESIST_CAP /* 165 */ bool ldon_trap; //Flag found on all LDON trap / chest related spells. -- AFFECT_INANIMATE -/* 166 */ int EndurCost; // -- STAMINA_COST -/* 167 */ int8 EndurTimerIndex; // bad name, used for all spells -- TIMER_INDEX -/* 168 */ bool IsDisciplineBuff; //Will goto the combat window when cast -- IS_SKILL +/* 166 */ int endurance_cost; // -- STAMINA_COST +/* 167 */ int8 timer_id; // bad name, used for all spells -- TIMER_INDEX +/* 168 */ bool is_discipline; //Will goto the combat window when cast -- IS_SKILL /* 169 - 172*/ //These are zero for ALL spells, also removed from live -- ATTACK_OPENING, DEFENSE_OPENING, SKILL_OPENING, NPC_ERROR_OPENING -/* 173 */ int HateAdded; // -- SPELL_HATE_GIVEN -/* 174 */ int EndurUpkeep; // -- ENDUR_UPKEEP -/* 175 */ int numhitstype; // defines which type of behavior will tick down the numhit counter. -- LIMITED_USE_TYPE -/* 176 */ int numhits; // -- LIMITED_USE_COUNT -/* 177 */ int pvpresistbase; // -- PVP_RESIST_MOD -/* 178 */ int pvpresistcalc; // -- PVP_RESIST_PER_LEVEL -/* 179 */ int pvpresistcap; // -- PVP_RESIST_CAP +/* 173 */ int hate_added; // -- SPELL_HATE_GIVEN +/* 174 */ int endurance_upkeep; // -- ENDUR_UPKEEP +/* 175 */ int hit_number_type; // defines which type of behavior will tick down the numhit counter. -- LIMITED_USE_TYPE +/* 176 */ int hit_number; // -- LIMITED_USE_COUNT +/* 177 */ int pvp_resist_base; // -- PVP_RESIST_MOD +/* 178 */ int pvp_resist_per_level; // -- PVP_RESIST_PER_LEVEL +/* 179 */ int pvp_resist_cap; // -- PVP_RESIST_CAP /* 180 */ int spell_category; // -- GLOBAL_GROUP /* 181 */ int pvp_duration; // buffdurationformula for PvP -- PVP_DURATION /* 182 */ int pvp_duration_cap; // buffduration for PvP -- PVP_DURATION_CAP @@ -1340,11 +1340,11 @@ struct SPDat_Spell_Struct /* 186 */ int dispel_flag; // -- NO_DISPELL /* 187 */ //int npc_category; // -- NPC_MEM_CATEGORY /* 188 */ //int npc_usefulness; // -- NPC_USEFULNESS -/* 189 */ int MinResist; // -- MIN_RESIST -/* 190 */ int MaxResist; // -- MAX_RESIST -/* 191 */ int viral_targets; // -- MIN_SPREAD_TIME -/* 192 */ int viral_timer; // -- MAX_SPREAD_TIME -/* 193 */ int NimbusEffect; // -- DURATION_PARTICLE_EFFECT +/* 189 */ int min_resist; // -- MIN_RESIST +/* 190 */ int max_resist; // -- MAX_RESIST +/* 191 */ uint8 viral_targets; // -- MIN_SPREAD_TIME +/* 192 */ uint8 viral_timer; // -- MAX_SPREAD_TIME +/* 193 */ int nimbus_effect; // -- DURATION_PARTICLE_EFFECT /* 194 */ float directional_start; //Cone Start Angle: -- CONE_START_ANGLE /* 195 */ float directional_end; // Cone End Angle: -- CONE_END_ANGLE /* 196 */ bool sneak; // effect can only be used if sneaking (rogue 'Daggerfall' ect) -- SNEAK_ATTACK @@ -1353,35 +1353,35 @@ struct SPDat_Spell_Struct /* 199 */ //bool show_wear_off_message; // -- SHOW_WEAR_OFF_MESSAGE /* 200 */ bool suspendable; // buff is suspended in suspended buff zones -- IS_COUNTDOWN_HELD /* 201 */ int viral_range; // -- SPREAD_RADIUS -/* 202 */ int songcap; // individual song cap -- BASE_EFFECTS_FOCUS_CAP +/* 202 */ int song_cap; // individual song cap -- BASE_EFFECTS_FOCUS_CAP /* 203 */ //bool stacks_with_self; // -- STACKS_WITH_SELF /* 204 */ //int not_shown_to_player; // client skips this -- NOT_SHOWN_TO_PLAYER /* 205 */ bool no_block; // -- NO_BUFF_BLOCK /* 206 */ //int8 anim_variation; // -- ANIM_VARIATION -/* 207 */ int spellgroup; // -- SPELL_GROUP +/* 207 */ int spell_group; // -- SPELL_GROUP /* 208 */ int rank; //increments AA effects with same name -- SPELL_GROUP_RANK /* 209 */ int no_resist; //makes spells unresistable, which makes charms unbreakable as well. -- NO_RESIST /* 210 */ // bool allow_spellscribe; // -- ALLOW_SPELLSCRIBE -/* 211 */ int CastRestriction; //Various restriction categories for spells most seem targetable race related but have also seen others for instance only castable if target hp 20% or lower or only if target out of combat -- SPELL_REQ_ASSOCIATION_ID -/* 212 */ bool AllowRest; // -- BYPASS_REGEN_CHECK -/* 213 */ bool InCombat; //Allow spell if target is in combat -- CAN_CAST_IN_COMBAT -/* 214 */ bool OutofCombat; //Allow spell if target is out of combat -- CAN_CAST_OUT_OF_COMBAT +/* 211 */ int cast_restriction; //Various restriction categories for spells most seem targetable race related but have also seen others for instance only castable if target hp 20% or lower or only if target out of combat -- SPELL_REQ_ASSOCIATION_ID +/* 212 */ bool allow_rest; // -- BYPASS_REGEN_CHECK +/* 213 */ bool can_cast_in_combat; //Allow spell if target is in combat -- CAN_CAST_IN_COMBAT +/* 214 */ bool can_cast_out_of_combat; //Allow spell if target is out of combat -- CAN_CAST_OUT_OF_COMBAT /* 215 */ //bool show_dot_message; // -- SHOW_DOT_MESSAGE /* 216 */ //bool invalid; // -- INVALID /* 217 */ int override_crit_chance; //Places a cap on the max chance to critical -- OVERRIDE_CRIT_CHANCE -/* 218 */ int aemaxtargets; //Is used for various AE effects -- MAX_TARGETS +/* 218 */ int aoe_max_targets; //Is used for various AE effects -- MAX_TARGETS /* 219 */ int no_heal_damage_item_mod; // -- NO_HEAL_DAMAGE_ITEM_MOD /* 220 */ int caster_requirement_id; // -- CASTER_REQUIREMENT_ID /* 221 */ int spell_class; // -- SPELL_CLASS /* 222 */ int spell_subclass; // -- SPELL_SUBCLASS /* 223 */ //int ai_valid_targets; // -- AI_VALID_TARGETS -/* 224 */ bool persistdeath; // buff doesn't get stripped on death -- NO_STRIP_ON_DEATH +/* 224 */ bool persist_death; // buff doesn't get stripped on death -- NO_STRIP_ON_DEATH /* 225 */ //float base_effects_focus_slope; // -- BASE_EFFECTS_FOCUS_SLOPE /* 226 */ //float base_effects_focus_offset; // -- BASE_EFFECTS_FOCUS_OFFSET -/* 227 */ float min_dist; //spell power modified by distance from caster (Min Distance) -- DISTANCE_MOD_CLOSE_DIST -/* 228 */ float min_dist_mod; //spell power modified by distance from caster (Modifier at Min Distance) -- DISTANCE_MOD_CLOSE_MULT -/* 229 */ float max_dist; //spell power modified by distance from caster (Max Distance) -- DISTANCE_MOD_FAR_DIST -/* 230 */ float max_dist_mod; //spell power modified by distance from caster (Modifier at Max Distance) -- DISTANCE_MOD_FAR_MULT +/* 227 */ float min_distance; //spell power modified by distance from caster (Min Distance) -- DISTANCE_MOD_CLOSE_DIST +/* 228 */ float min_distance_mod; //spell power modified by distance from caster (Modifier at Min Distance) -- DISTANCE_MOD_CLOSE_MULT +/* 229 */ float max_distance; //spell power modified by distance from caster (Max Distance) -- DISTANCE_MOD_FAR_DIST +/* 230 */ float max_distance_mod; //spell power modified by distance from caster (Modifier at Max Distance) -- DISTANCE_MOD_FAR_MULT /* The client also does this * v26 = *(float *)&v4->DistanceModFarDist - *(float *)&v4->DistanceModCloseDist; * if ( v26 > -0.00000011920929 && v26 < 0.00000011920929 ) @@ -1395,7 +1395,7 @@ struct SPDat_Spell_Struct /* 234 */ //bool only_during_fast_regen; // -- ONLY_DURING_FAST_REGEN /* 235 */ //bool is_beta_only; // -- IS_BETA_ONLY /* 236 */ //int spell_subgroup; // -- SPELL_SUBGROUP - uint8 DamageShieldType; // This field does not exist in spells_us.txt + uint8 damage_shield_type; // This field does not exist in spells_us.txt }; extern const SPDat_Spell_Struct* spells; @@ -1508,7 +1508,7 @@ bool IsBardOnlyStackEffect(int effect); bool IsCastWhileInvis(uint16 spell_id); bool IsEffectIgnoredInStacking(int spa); bool IsFocusLimit(int spa); -bool SpellRequiresTarget(int targettype); +bool SpellRequiresTarget(int target_type); bool IsVirusSpell(int32 spell_id); int GetViralMinSpreadTime(int32 spell_id); int GetViralMaxSpreadTime(int32 spell_id); diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 9f519b498..dd4188c25 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -121,6 +121,7 @@ SET(zone_sources perl_player_corpse.cpp perl_questitem.cpp perl_raids.cpp + perl_spell.cpp perlpacket.cpp petitions.cpp pets.cpp diff --git a/zone/aa.cpp b/zone/aa.cpp index fff2ae727..f114abb93 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -69,10 +69,10 @@ void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, u for (int x = 0; x < MAX_SWARM_PETS; x++) { - if (spells[spell_id].effectid[x] == SE_TemporaryPets) + if (spells[spell_id].effect_id[x] == SE_TemporaryPets) { - pet.count = spells[spell_id].base[x]; - pet.duration = spells[spell_id].max[x]; + pet.count = spells[spell_id].base_value[x]; + pet.duration = spells[spell_id].max_value[x]; } } @@ -764,7 +764,7 @@ void Client::InspectBuffs(Client* Inspector, int Rank) continue; ib->spell_id[packet_index] = buffs[i].spellid; if (Rank > 1) - ib->tics_remaining[packet_index] = spells[buffs[i].spellid].buffdurationformula == DF_Permanent ? 0xFFFFFFFF : buffs[i].ticsremaining; + ib->tics_remaining[packet_index] = spells[buffs[i].spellid].buff_duration_formula == DF_Permanent ? 0xFFFFFFFF : buffs[i].ticsremaining; packet_index++; } @@ -904,8 +904,8 @@ void Client::SendAlternateAdvancementRank(int aa_id, int level) { outapp->SetWritePosition(sizeof(AARankInfo_Struct)); for(auto &effect : rank->effects) { outapp->WriteSInt32(effect.effect_id); - outapp->WriteSInt32(effect.base1); - outapp->WriteSInt32(effect.base2); + outapp->WriteSInt32(effect.base_value); + outapp->WriteSInt32(effect.limit_value); outapp->WriteSInt32(effect.slot); } @@ -1229,7 +1229,7 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { } // // Modern clients don't require pet targeted for AA casts that are ST_Pet - if (spells[rank->spell].targettype == ST_Pet || spells[rank->spell].targettype == ST_SummonedPet) + if (spells[rank->spell].target_type == ST_Pet || spells[rank->spell].target_type == ST_SummonedPet) target_id = GetPetID(); // extra handling for cast_not_standing spells @@ -1249,7 +1249,7 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { else { // Bards can cast instant cast AAs while they are casting another song if (spells[rank->spell].cast_time == 0 && GetClass() == BARD && IsBardSong(casting_spell_id)) { - if (!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQ::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].ResistDiff, false)) { + if (!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQ::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].resist_difficulty, false)) { return; } ExpendAlternateAdvancementCharge(ability->id); @@ -1285,8 +1285,8 @@ int Mob::GetAlternateAdvancementCooldownReduction(AA::Rank *rank_in) { } for(auto &effect : rank->effects) { - if(effect.effect_id == SE_HastenedAASkill && effect.base2 == ability_in->id) { - return effect.base1; + if(effect.effect_id == SE_HastenedAASkill && effect.limit_value == ability_in->id) { + return effect.base_value; } } } @@ -1744,8 +1744,8 @@ bool ZoneDatabase::LoadAlternateAdvancementAbilities(std::unordered_map(GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 0)); - float min_dist = static_cast(GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 1)); + float min_distance = static_cast(GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 1)); if (GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 2)) DoLoSCheck = false; //Ignore line of sight check @@ -884,12 +884,12 @@ bool Mob::CombatRange(Mob* other, float fixed_size_mod, bool aeRampage) max_dist = max_dist * max_dist; - if (!min_dist) - min_dist = size_mod; //Default to melee range + if (!min_distance) + min_distance = size_mod; //Default to melee range else - min_dist = min_dist * min_dist; + min_distance = min_distance * min_distance; - if ((DoLoSCheck && CheckLastLosState()) && (_DistNoRoot >= min_dist && _DistNoRoot <= max_dist)) + if ((DoLoSCheck && CheckLastLosState()) && (_DistNoRoot >= min_distance && _DistNoRoot <= max_dist)) SetPseudoRoot(true); else SetPseudoRoot(false); @@ -1003,16 +1003,16 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc) default_aggro = target_hp / 15; for (int o = 0; o < EFFECT_COUNT; o++) { - switch (spells[spell_id].effectid[o]) { + switch (spells[spell_id].effect_id[o]) { case SE_CurrentHPOnce: case SE_CurrentHP: { - int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); + int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id); if(val < 0) AggroAmount -= val; break; } case SE_MovementSpeed: { - int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); + int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id); if (val < 0) AggroAmount += default_aggro; break; @@ -1020,7 +1020,7 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc) case SE_AttackSpeed: case SE_AttackSpeed2: case SE_AttackSpeed3: { - int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); + int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id); if (val < 100) AggroAmount += default_aggro; break; @@ -1038,7 +1038,7 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc) break; case SE_ACv2: case SE_ArmorClass: { - int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); + int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id); if (val < 0) AggroAmount += default_aggro; break; @@ -1056,19 +1056,19 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc) case SE_INT: case SE_WIS: case SE_CHA: { - int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); + int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id); if (val < 0) AggroAmount += 10; break; } case SE_ResistAll: { - int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); + int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id); if (val < 0) AggroAmount += 50; break; } case SE_AllStats: { - int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); + int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id); if (val < 0) AggroAmount += 70; break; @@ -1110,7 +1110,7 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc) case SE_ManaRegen_v2: case SE_ManaPool: case SE_CurrentEndurance: { - int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); + int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id); if (val < 0) AggroAmount -= val * 2; break; @@ -1122,7 +1122,7 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc) break; case SE_ReduceHate: case SE_InstantHate: - nonModifiedAggro = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); + nonModifiedAggro = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id); break; } } @@ -1133,8 +1133,8 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc) if (dispel && target && target->GetHateAmount(this) < 100) AggroAmount += 50; - if (spells[spell_id].HateAdded > 0) // overrides the hate (ex. tash) - AggroAmount = spells[spell_id].HateAdded; + if (spells[spell_id].hate_added > 0) // overrides the hate (ex. tash) + AggroAmount = spells[spell_id].hate_added; if (GetOwner() && IsPet()) AggroAmount = AggroAmount * RuleI(Aggro, PetSpellAggroMod) / 100; @@ -1149,10 +1149,10 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc) // initial aggro gets a bonus 100 besides for dispel or hate override // We add this 100 in AddToHateList so we need to account for the oddities here - if (dispel && spells[spell_id].HateAdded > 0 && !on_hatelist) + if (dispel && spells[spell_id].hate_added > 0 && !on_hatelist) AggroAmount -= 100; - return AggroAmount + spells[spell_id].bonushate + nonModifiedAggro; + return AggroAmount + spells[spell_id].bonus_hate + nonModifiedAggro; } //healing and buffing aggro @@ -1163,7 +1163,7 @@ int32 Mob::CheckHealAggroAmount(uint16 spell_id, Mob *target, uint32 heal_possib bool ignore_default_buff = false; // rune/hot don't use the default 9, HP buffs that heal (virtue) do use the default for (int o = 0; o < EFFECT_COUNT; o++) { - switch (spells[spell_id].effectid[o]) { + switch (spells[spell_id].effect_id[o]) { case SE_CurrentHP: case SE_PercentalHeal: { @@ -1173,7 +1173,7 @@ int32 Mob::CheckHealAggroAmount(uint16 spell_id, Mob *target, uint32 heal_possib } // hate based on base healing power of the spell int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], - spells[spell_id].base[o], spells[spell_id].max[o], GetLevel(), spell_id); + spells[spell_id].base_value[o], spells[spell_id].max_value[o], GetLevel(), spell_id); if (val > 0) { if (heal_possible < val) val = heal_possible; // capped to amount healed @@ -1189,7 +1189,7 @@ int32 Mob::CheckHealAggroAmount(uint16 spell_id, Mob *target, uint32 heal_possib } case SE_Rune: AggroAmount += CalcSpellEffectValue_formula(spells[spell_id].formula[o], - spells[spell_id].base[o], spells[spell_id].max[o], GetLevel(), spell_id) * 2; + spells[spell_id].base_value[o], spells[spell_id].max_value[o], GetLevel(), spell_id) * 2; ignore_default_buff = true; break; case SE_HealOverTime: @@ -1269,7 +1269,7 @@ bool Mob::PassCharismaCheck(Mob* caster, uint16 spell_id) { if(!caster) return false; - if(spells[spell_id].ResistDiff <= -600) + if(spells[spell_id].resist_difficulty <= -600) return true; float resist_check = 0; @@ -1284,9 +1284,9 @@ bool Mob::PassCharismaCheck(Mob* caster, uint16 spell_id) { return true; if (RuleB(Spells, CharismaCharmDuration)) - resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster,false,0,true,true); + resist_check = ResistSpell(spells[spell_id].resist_type, spell_id, caster,false,0,true,true); else - resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster, false,0, false, true); + resist_check = ResistSpell(spells[spell_id].resist_type, spell_id, caster, false,0, false, true); //2: The mob makes a resistance check against the charm if (resist_check == 100) @@ -1310,7 +1310,7 @@ bool Mob::PassCharismaCheck(Mob* caster, uint16 spell_id) { { // Assume this is a harmony/pacify spell // If 'Lull' spell resists, do a second resist check with a charisma modifier AND regular resist checks. If resists agian you gain aggro. - resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster, false,0,true); + resist_check = ResistSpell(spells[spell_id].resist_type, spell_id, caster, false,0,true); if (resist_check == 100) return true; } diff --git a/zone/attack.cpp b/zone/attack.cpp index c78615ac8..f9f086f7f 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1555,7 +1555,7 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b float chance = aabonuses.SkillAttackProc[SBIndex::SKILLPROC_CHANCE] / 1000.0f; if (zone->random.Roll(chance)) SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID], other, EQ::spells::CastingSlot::Item, 0, -1, - spells[aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID]].ResistDiff); + spells[aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID]].resist_difficulty); } other->Damage(this, my_hit.damage_done, SPELL_UNKNOWN, my_hit.skill, true, -1, false, m_specialattacks); @@ -2195,7 +2195,7 @@ void NPC::Damage(Mob* other, int32 damage, uint16 spell_id, EQ::skills::SkillTyp if (IsLDoNTrapped()) { MessageString(Chat::Red, LDON_ACCIDENT_SETOFF2); - SpellFinished(GetLDoNTrapSpellID(), other, EQ::spells::CastingSlot::Item, 0, -1, spells[GetLDoNTrapSpellID()].ResistDiff, false); + SpellFinished(GetLDoNTrapSpellID(), other, EQ::spells::CastingSlot::Item, 0, -1, spells[GetLDoNTrapSpellID()].resist_difficulty, false); SetLDoNTrapSpellID(0); SetLDoNTrapped(false); SetLDoNTrapDetected(false); @@ -3141,7 +3141,7 @@ int32 Mob::ReduceDamage(int32 damage) if (spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_EXISTS]) { slot = spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_BUFFSLOT]; if (slot >= 0) { - if (--buffs[slot].numhits == 0) { + if (--buffs[slot].hit_number == 0) { if (!TryFadeEffect(slot)) BuffFadeBySlot(slot, true); @@ -3230,7 +3230,7 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi if (!iBuffTic && spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_EXISTS]) { slot = spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_BUFFSLOT]; if (slot >= 0) { - if (--buffs[slot].numhits == 0) { + if (--buffs[slot].hit_number == 0) { if (!TryFadeEffect(slot)) BuffFadeBySlot(slot, true); @@ -4918,14 +4918,14 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui for (int i = 0; i < EFFECT_COUNT; i++) { - if (spells[base_spell_id].effectid[i] == SE_SkillProc || spells[base_spell_id].effectid[i] == SE_SkillProcSuccess) { - proc_spell_id = spells[base_spell_id].base[i]; - ProcMod = static_cast(spells[base_spell_id].base2[i]); + if (spells[base_spell_id].effect_id[i] == SE_SkillProc || spells[base_spell_id].effect_id[i] == SE_SkillProcSuccess) { + proc_spell_id = spells[base_spell_id].base_value[i]; + ProcMod = static_cast(spells[base_spell_id].limit_value[i]); } - else if (spells[base_spell_id].effectid[i] == SE_LimitToSkill && spells[base_spell_id].base[i] <= EQ::skills::HIGHEST_SKILL) { + else if (spells[base_spell_id].effect_id[i] == SE_LimitToSkill && spells[base_spell_id].base_value[i] <= EQ::skills::HIGHEST_SKILL) { - if (CanProc && spells[base_spell_id].base[i] == skill && IsValidSpell(proc_spell_id)) { + if (CanProc && spells[base_spell_id].base_value[i] == skill && IsValidSpell(proc_spell_id)) { float final_chance = chance * (ProcMod / 100.0f); if (zone->random.Roll(final_chance)) { ExecWeaponProc(nullptr, proc_spell_id, on); @@ -4962,14 +4962,14 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui ProcMod = 0; for (int i = 0; i < EFFECT_COUNT; i++) { - if (spells[base_spell_id].effectid[i] == SE_SkillProc || spells[base_spell_id].effectid[i] == SE_SkillProcSuccess) { - proc_spell_id = spells[base_spell_id].base[i]; - ProcMod = static_cast(spells[base_spell_id].base2[i]); + if (spells[base_spell_id].effect_id[i] == SE_SkillProc || spells[base_spell_id].effect_id[i] == SE_SkillProcSuccess) { + proc_spell_id = spells[base_spell_id].base_value[i]; + ProcMod = static_cast(spells[base_spell_id].limit_value[i]); } - else if (spells[base_spell_id].effectid[i] == SE_LimitToSkill && spells[base_spell_id].base[i] <= EQ::skills::HIGHEST_SKILL) { + else if (spells[base_spell_id].effect_id[i] == SE_LimitToSkill && spells[base_spell_id].base_value[i] <= EQ::skills::HIGHEST_SKILL) { - if (CanProc && spells[base_spell_id].base[i] == skill && IsValidSpell(proc_spell_id)) { + if (CanProc && spells[base_spell_id].base_value[i] == skill && IsValidSpell(proc_spell_id)) { float final_chance = chance * (ProcMod / 100.0f); if (zone->random.Roll(final_chance)) { ExecWeaponProc(nullptr, proc_spell_id, on); @@ -4991,8 +4991,8 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui CanProc = true; uint32 effect_id = 0; - int32 base1 = 0; - int32 base2 = 0; + int32 base_value = 0; + int32 limit_value = 0; uint32 slot = 0; for (int e = 0; e < MAX_SKILL_PROCS; e++) { @@ -5020,17 +5020,17 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui for (auto &effect : rank->effects) { effect_id = effect.effect_id; - base1 = effect.base1; - base2 = effect.base2; + base_value = effect.base_value; + limit_value = effect.limit_value; slot = effect.slot; if (effect_id == SE_SkillProc || effect_id == SE_SkillProcSuccess) { - proc_spell_id = base1; - ProcMod = static_cast(base2); + proc_spell_id = base_value; + ProcMod = static_cast(limit_value); } - else if (effect_id == SE_LimitToSkill && base1 <= EQ::skills::HIGHEST_SKILL) { + else if (effect_id == SE_LimitToSkill && base_value <= EQ::skills::HIGHEST_SKILL) { - if (CanProc && base1 == skill && IsValidSpell(proc_spell_id)) { + if (CanProc && base_value == skill && IsValidSpell(proc_spell_id)) { float final_chance = chance * (ProcMod / 100.0f); if (zone->random.Roll(final_chance)) { diff --git a/zone/beacon.cpp b/zone/beacon.cpp index 11b0de0fd..60b572dd2 100644 --- a/zone/beacon.cpp +++ b/zone/beacon.cpp @@ -98,7 +98,7 @@ bool Beacon::Process() { // NPCs should never be affected by an AE they cast. PB AEs shouldn't affect caster either // I don't think any other cases that get here matter - bool affect_caster = (!caster->IsNPC() && !caster->IsAIControlled()) && spells[spell_id].targettype != ST_AECaster; + bool affect_caster = (!caster->IsNPC() && !caster->IsAIControlled()) && spells[spell_id].target_type != ST_AECaster; entity_list.AESpell(caster, this, spell_id, affect_caster, resist_adjust, &max_targets); } else @@ -127,10 +127,10 @@ void Beacon::AELocationSpell(Mob *caster, uint16 cast_spell_id, int16 resist_adj caster_id = caster->GetID(); spell_id = cast_spell_id; this->resist_adjust = resist_adjust; - spell_iterations = spells[spell_id].AEDuration / 2500; + spell_iterations = spells[spell_id].aoe_duration / 2500; spell_iterations = spell_iterations < 1 ? 1 : spell_iterations; // at least 1 - if (spells[spell_id].aemaxtargets) - max_targets = spells[spell_id].aemaxtargets; + if (spells[spell_id].aoe_max_targets) + max_targets = spells[spell_id].aoe_max_targets; spell_timer.Start(2500); spell_timer.Trigger(); } diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 4a251b136..bdc02009c 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -650,27 +650,27 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) return; uint32 effect = 0; - int32 base1 = 0; - int32 base2 = 0; // only really used for SE_RaiseStatCap & SE_ReduceSkillTimer in aa_effects table + int32 base_value = 0; + int32 limit_value = 0; // only really used for SE_RaiseStatCap & SE_ReduceSkillTimer in aa_effects table uint32 slot = 0; for (const auto &e : rank.effects) { effect = e.effect_id; - base1 = e.base1; - base2 = e.base2; + base_value = e.base_value; + limit_value = e.limit_value; slot = e.slot; // we default to 0 (SE_CurrentHP) for the effect, so if there aren't any base1/2 values, we'll just skip it - if (effect == 0 && base1 == 0 && base2 == 0) + if (effect == 0 && base_value == 0 && limit_value == 0) continue; // IsBlankSpellEffect() - if (effect == SE_Blank || (effect == SE_CHA && base1 == 0) || effect == SE_StackingCommand_Block || + if (effect == SE_Blank || (effect == SE_CHA && base_value == 0) || effect == SE_StackingCommand_Block || effect == SE_StackingCommand_Overwrite) continue; LogAA("Applying Effect [{}] from AA [{}] in slot [{}] (base1: [{}], base2: [{}]) on [{}]", - effect, rank.id, slot, base1, base2, GetCleanName()); + effect, rank.id, slot, base_value, limit_value, GetCleanName()); uint8 focus = IsFocusEffect(0, 0, true, effect); if (focus) { @@ -681,373 +681,373 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) switch (effect) { case SE_ACv2: case SE_ArmorClass: - newbon->AC += base1; + newbon->AC += base_value; break; // Note: AA effects that use accuracy are skill limited, while spell effect is not. case SE_Accuracy: // Bad data or unsupported new skill - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; - if ((base2 == ALL_SKILLS) && (newbon->Accuracy[EQ::skills::HIGHEST_SKILL + 1] < base1)) - newbon->Accuracy[EQ::skills::HIGHEST_SKILL + 1] = base1; - else if (newbon->Accuracy[base2] < base1) - newbon->Accuracy[base2] += base1; + if ((limit_value == ALL_SKILLS) && (newbon->Accuracy[EQ::skills::HIGHEST_SKILL + 1] < base_value)) + newbon->Accuracy[EQ::skills::HIGHEST_SKILL + 1] = base_value; + else if (newbon->Accuracy[limit_value] < base_value) + newbon->Accuracy[limit_value] += base_value; break; case SE_CurrentHP: // regens - newbon->HPRegen += base1; + newbon->HPRegen += base_value; break; case SE_CurrentEndurance: - newbon->EnduranceRegen += base1; + newbon->EnduranceRegen += base_value; break; case SE_MovementSpeed: - newbon->movementspeed += base1; // should we let these stack? + newbon->movementspeed += base_value; // should we let these stack? /*if (base1 > newbon->movementspeed) //or should we use a total value? newbon->movementspeed = base1;*/ break; case SE_STR: - newbon->STR += base1; + newbon->STR += base_value; break; case SE_DEX: - newbon->DEX += base1; + newbon->DEX += base_value; break; case SE_AGI: - newbon->AGI += base1; + newbon->AGI += base_value; break; case SE_STA: - newbon->STA += base1; + newbon->STA += base_value; break; case SE_INT: - newbon->INT += base1; + newbon->INT += base_value; break; case SE_WIS: - newbon->WIS += base1; + newbon->WIS += base_value; break; case SE_CHA: - newbon->CHA += base1; + newbon->CHA += base_value; break; case SE_WaterBreathing: // handled by client break; case SE_CurrentMana: - newbon->ManaRegen += base1; + newbon->ManaRegen += base_value; break; case SE_ManaPool: - newbon->Mana += base1; + newbon->Mana += base_value; break; case SE_ItemManaRegenCapIncrease: - newbon->ItemManaRegenCap += base1; + newbon->ItemManaRegenCap += base_value; break; case SE_ResistFire: - newbon->FR += base1; + newbon->FR += base_value; break; case SE_ResistCold: - newbon->CR += base1; + newbon->CR += base_value; break; case SE_ResistPoison: - newbon->PR += base1; + newbon->PR += base_value; break; case SE_ResistDisease: - newbon->DR += base1; + newbon->DR += base_value; break; case SE_ResistMagic: - newbon->MR += base1; + newbon->MR += base_value; break; case SE_ResistCorruption: - newbon->Corrup += base1; + newbon->Corrup += base_value; break; case SE_IncreaseSpellHaste: break; case SE_IncreaseRange: break; case SE_MaxHPChange: - newbon->MaxHP += base1; + newbon->MaxHP += base_value; break; case SE_Packrat: - newbon->Packrat += base1; + newbon->Packrat += base_value; break; case SE_TwoHandBash: break; case SE_SetBreathLevel: break; case SE_RaiseStatCap: - switch (base2) { + switch (limit_value) { // are these #define'd somewhere? case 0: // str - newbon->STRCapMod += base1; + newbon->STRCapMod += base_value; break; case 1: // sta - newbon->STACapMod += base1; + newbon->STACapMod += base_value; break; case 2: // agi - newbon->AGICapMod += base1; + newbon->AGICapMod += base_value; break; case 3: // dex - newbon->DEXCapMod += base1; + newbon->DEXCapMod += base_value; break; case 4: // wis - newbon->WISCapMod += base1; + newbon->WISCapMod += base_value; break; case 5: // int - newbon->INTCapMod += base1; + newbon->INTCapMod += base_value; break; case 6: // cha - newbon->CHACapMod += base1; + newbon->CHACapMod += base_value; break; case 7: // mr - newbon->MRCapMod += base1; + newbon->MRCapMod += base_value; break; case 8: // cr - newbon->CRCapMod += base1; + newbon->CRCapMod += base_value; break; case 9: // fr - newbon->FRCapMod += base1; + newbon->FRCapMod += base_value; break; case 10: // pr - newbon->PRCapMod += base1; + newbon->PRCapMod += base_value; break; case 11: // dr - newbon->DRCapMod += base1; + newbon->DRCapMod += base_value; break; case 12: // corruption - newbon->CorrupCapMod += base1; + newbon->CorrupCapMod += base_value; break; } break; case SE_SpellSlotIncrease: break; case SE_MysticalAttune: - newbon->BuffSlotIncrease += base1; + newbon->BuffSlotIncrease += base_value; break; case SE_TotalHP: - newbon->HP += base1; + newbon->HP += base_value; break; case SE_StunResist: - newbon->StunResist += base1; + newbon->StunResist += base_value; break; case SE_SpellCritChance: - newbon->CriticalSpellChance += base1; + newbon->CriticalSpellChance += base_value; break; case SE_SpellCritDmgIncrease: - newbon->SpellCritDmgIncrease += base1; + newbon->SpellCritDmgIncrease += base_value; break; case SE_DotCritDmgIncrease: - newbon->DotCritDmgIncrease += base1; + newbon->DotCritDmgIncrease += base_value; break; case SE_ResistSpellChance: - newbon->ResistSpellChance += base1; + newbon->ResistSpellChance += base_value; break; case SE_CriticalHealChance: - newbon->CriticalHealChance += base1; + newbon->CriticalHealChance += base_value; break; case SE_CriticalHealOverTime: - newbon->CriticalHealOverTime += base1; + newbon->CriticalHealOverTime += base_value; break; case SE_CriticalDoTChance: - newbon->CriticalDoTChance += base1; + newbon->CriticalDoTChance += base_value; break; case SE_ReduceSkillTimer: - newbon->SkillReuseTime[base2] += base1; + newbon->SkillReuseTime[limit_value] += base_value; break; case SE_Fearless: newbon->Fearless = true; break; case SE_PersistantCasting: - newbon->PersistantCasting += base1; + newbon->PersistantCasting += base_value; break; case SE_DelayDeath: - newbon->DelayDeath += base1; + newbon->DelayDeath += base_value; break; case SE_FrontalStunResist: - newbon->FrontalStunResist += base1; + newbon->FrontalStunResist += base_value; break; case SE_ImprovedBindWound: - newbon->BindWound += base1; + newbon->BindWound += base_value; break; case SE_MaxBindWound: - newbon->MaxBindWound += base1; + newbon->MaxBindWound += base_value; break; case SE_SeeInvis: - newbon->SeeInvis = base1; + newbon->SeeInvis = base_value; break; case SE_BaseMovementSpeed: - newbon->BaseMovementSpeed += base1; + newbon->BaseMovementSpeed += base_value; break; case SE_IncreaseRunSpeedCap: - newbon->IncreaseRunSpeedCap += base1; + newbon->IncreaseRunSpeedCap += base_value; break; case SE_ConsumeProjectile: - newbon->ConsumeProjectile += base1; + newbon->ConsumeProjectile += base_value; break; case SE_ForageAdditionalItems: - newbon->ForageAdditionalItems += base1; + newbon->ForageAdditionalItems += base_value; break; case SE_Salvage: - newbon->SalvageChance += base1; + newbon->SalvageChance += base_value; break; case SE_ArcheryDamageModifier: - newbon->ArcheryDamageModifier += base1; + newbon->ArcheryDamageModifier += base_value; break; case SE_DoubleRangedAttack: - newbon->DoubleRangedAttack += base1; + newbon->DoubleRangedAttack += base_value; break; case SE_DamageShield: - newbon->DamageShield += base1; + newbon->DamageShield += base_value; break; case SE_CharmBreakChance: - newbon->CharmBreakChance += base1; + newbon->CharmBreakChance += base_value; break; case SE_OffhandRiposteFail: - newbon->OffhandRiposteFail += base1; + newbon->OffhandRiposteFail += base_value; break; case SE_ItemAttackCapIncrease: - newbon->ItemATKCap += base1; + newbon->ItemATKCap += base_value; break; case SE_GivePetGroupTarget: newbon->GivePetGroupTarget = true; break; case SE_ItemHPRegenCapIncrease: - newbon->ItemHPRegenCap += base1; + newbon->ItemHPRegenCap += base_value; break; case SE_Ambidexterity: - newbon->Ambidexterity += base1; + newbon->Ambidexterity += base_value; break; case SE_PetMaxHP: - newbon->PetMaxHP += base1; + newbon->PetMaxHP += base_value; break; case SE_AvoidMeleeChance: - newbon->AvoidMeleeChanceEffect += base1; + newbon->AvoidMeleeChanceEffect += base_value; break; case SE_CombatStability: - newbon->CombatStability += base1; + newbon->CombatStability += base_value; break; case SE_AddSingingMod: - switch (base2) { + switch (limit_value) { case EQ::item::ItemTypeWindInstrument: - newbon->windMod += base1; + newbon->windMod += base_value; break; case EQ::item::ItemTypeStringedInstrument: - newbon->stringedMod += base1; + newbon->stringedMod += base_value; break; case EQ::item::ItemTypeBrassInstrument: - newbon->brassMod += base1; + newbon->brassMod += base_value; break; case EQ::item::ItemTypePercussionInstrument: - newbon->percussionMod += base1; + newbon->percussionMod += base_value; break; case EQ::item::ItemTypeSinging: - newbon->singingMod += base1; + newbon->singingMod += base_value; break; } break; case SE_SongModCap: - newbon->songModCap += base1; + newbon->songModCap += base_value; break; case SE_PetCriticalHit: - newbon->PetCriticalHit += base1; + newbon->PetCriticalHit += base_value; break; case SE_PetAvoidance: - newbon->PetAvoidance += base1; + newbon->PetAvoidance += base_value; break; case SE_ShieldBlock: - newbon->ShieldBlock += base1; + newbon->ShieldBlock += base_value; break; case SE_ShieldEquipDmgMod: - newbon->ShieldEquipDmgMod += base1; + newbon->ShieldEquipDmgMod += base_value; break; case SE_SecondaryDmgInc: newbon->SecondaryDmgInc = true; break; case SE_ChangeAggro: - newbon->hatemod += base1; + newbon->hatemod += base_value; break; case SE_EndurancePool: - newbon->Endurance += base1; + newbon->Endurance += base_value; break; case SE_ChannelChanceItems: - newbon->ChannelChanceItems += base1; + newbon->ChannelChanceItems += base_value; break; case SE_ChannelChanceSpells: - newbon->ChannelChanceSpells += base1; + newbon->ChannelChanceSpells += base_value; break; case SE_DoubleSpecialAttack: - newbon->DoubleSpecialAttack += base1; + newbon->DoubleSpecialAttack += base_value; break; case SE_TripleBackstab: - newbon->TripleBackstab += base1; + newbon->TripleBackstab += base_value; break; case SE_FrontalBackstabMinDmg: newbon->FrontalBackstabMinDmg = true; break; case SE_FrontalBackstabChance: - newbon->FrontalBackstabChance += base1; + newbon->FrontalBackstabChance += base_value; break; case SE_Double_Backstab_Front: - newbon->Double_Backstab_Front += base1; + newbon->Double_Backstab_Front += base_value; break; case SE_BlockBehind: - newbon->BlockBehind += base1; + newbon->BlockBehind += base_value; break; case SE_StrikeThrough: case SE_StrikeThrough2: - newbon->StrikeThrough += base1; + newbon->StrikeThrough += base_value; break; case SE_DoubleAttackChance: - newbon->DoubleAttackChance += base1; + newbon->DoubleAttackChance += base_value; break; case SE_GiveDoubleAttack: - newbon->GiveDoubleAttack += base1; + newbon->GiveDoubleAttack += base_value; break; case SE_ProcChance: - newbon->ProcChanceSPA += base1; + newbon->ProcChanceSPA += base_value; break; case SE_RiposteChance: - newbon->RiposteChance += base1; + newbon->RiposteChance += base_value; break; case SE_DodgeChance: - newbon->DodgeChance += base1; + newbon->DodgeChance += base_value; break; case SE_ParryChance: - newbon->ParryChance += base1; + newbon->ParryChance += base_value; break; case SE_IncreaseBlockChance: - newbon->IncreaseBlockChance += base1; + newbon->IncreaseBlockChance += base_value; break; case SE_Flurry: - newbon->FlurryChance += base1; + newbon->FlurryChance += base_value; break; case SE_PetFlurry: - newbon->PetFlurry += base1; + newbon->PetFlurry += base_value; break; case SE_BardSongRange: - newbon->SongRange += base1; + newbon->SongRange += base_value; break; case SE_RootBreakChance: - newbon->RootBreakChance += base1; + newbon->RootBreakChance += base_value; break; case SE_UnfailingDivinity: - newbon->UnfailingDivinity += base1; + newbon->UnfailingDivinity += base_value; break; case SE_CrippBlowChance: - newbon->CrippBlowChance += base1; + newbon->CrippBlowChance += base_value; break; case SE_HitChance: { // Bad data or unsupported new skill - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; - if (base2 == ALL_SKILLS) - newbon->HitChanceEffect[EQ::skills::HIGHEST_SKILL + 1] += base1; + if (limit_value == ALL_SKILLS) + newbon->HitChanceEffect[EQ::skills::HIGHEST_SKILL + 1] += base_value; else - newbon->HitChanceEffect[base2] += base1; + newbon->HitChanceEffect[limit_value] += base_value; } case SE_ProcOnKillShot: for (int i = 0; i < MAX_SPELL_TRIGGER * 3; i += 3) { if (!newbon->SpellOnKill[i] || - ((newbon->SpellOnKill[i] == base2) && (newbon->SpellOnKill[i + 1] < base1))) { + ((newbon->SpellOnKill[i] == limit_value) && (newbon->SpellOnKill[i + 1] < base_value))) { // base1 = chance, base2 = SpellID to be triggered, base3 = min npc level - newbon->SpellOnKill[i] = base2; - newbon->SpellOnKill[i + 1] = base1; + newbon->SpellOnKill[i] = limit_value; + newbon->SpellOnKill[i + 1] = base_value; if (GetLevel() > 15) newbon->SpellOnKill[i + 2] = @@ -1064,8 +1064,8 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) for (int i = 0; i < MAX_SPELL_TRIGGER * 2; i += 2) { if (!newbon->SpellOnDeath[i]) { // base1 = SpellID to be triggered, base2 = chance to fire - newbon->SpellOnDeath[i] = base1; - newbon->SpellOnDeath[i + 1] = base2; + newbon->SpellOnDeath[i] = base_value; + newbon->SpellOnDeath[i + 1] = limit_value; break; } } @@ -1074,131 +1074,131 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) case SE_CriticalHitChance: { // Bad data or unsupported new skill - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; - if (base2 == ALL_SKILLS) - newbon->CriticalHitChance[EQ::skills::HIGHEST_SKILL + 1] += base1; + if (limit_value == ALL_SKILLS) + newbon->CriticalHitChance[EQ::skills::HIGHEST_SKILL + 1] += base_value; else - newbon->CriticalHitChance[base2] += base1; + newbon->CriticalHitChance[limit_value] += base_value; } break; case SE_CriticalDamageMob: { // Bad data or unsupported new skill - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; // base1 = effect value, base2 = skill restrictions(-1 for all) - if (base2 == ALL_SKILLS) - newbon->CritDmgMod[EQ::skills::HIGHEST_SKILL + 1] += base1; + if (limit_value == ALL_SKILLS) + newbon->CritDmgMod[EQ::skills::HIGHEST_SKILL + 1] += base_value; else - newbon->CritDmgMod[base2] += base1; + newbon->CritDmgMod[limit_value] += base_value; break; } case SE_Critical_Melee_Damage_Mod_Max: { // Bad data or unsupported new skill - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; - int skill = base2 == ALL_SKILLS ? EQ::skills::HIGHEST_SKILL + 1 : base2; - if (base1 < 0 && newbon->CritDmgModNoStack[skill] > base1) - newbon->CritDmgModNoStack[skill] = base1; - else if (base1 > 0 && newbon->CritDmgModNoStack[skill] < base1) - newbon->CritDmgModNoStack[skill] = base1; + int skill = limit_value == ALL_SKILLS ? EQ::skills::HIGHEST_SKILL + 1 : limit_value; + if (base_value < 0 && newbon->CritDmgModNoStack[skill] > base_value) + newbon->CritDmgModNoStack[skill] = base_value; + else if (base_value > 0 && newbon->CritDmgModNoStack[skill] < base_value) + newbon->CritDmgModNoStack[skill] = base_value; break; } case SE_CriticalSpellChance: { - newbon->CriticalSpellChance += base1; + newbon->CriticalSpellChance += base_value; - if (base2 > newbon->SpellCritDmgIncNoStack) - newbon->SpellCritDmgIncNoStack = base2; + if (limit_value > newbon->SpellCritDmgIncNoStack) + newbon->SpellCritDmgIncNoStack = limit_value; break; } case SE_ResistFearChance: { - if (base1 == 100) // If we reach 100% in a single spell/item then we should be immune to + if (base_value == 100) // If we reach 100% in a single spell/item then we should be immune to // negative fear resist effects until our immunity is over newbon->Fearless = true; - newbon->ResistFearChance += base1; // these should stack + newbon->ResistFearChance += base_value; // these should stack break; } case SE_SkillDamageAmount: { // Bad data or unsupported new skill - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; - if (base2 == ALL_SKILLS) - newbon->SkillDamageAmount[EQ::skills::HIGHEST_SKILL + 1] += base1; + if (limit_value == ALL_SKILLS) + newbon->SkillDamageAmount[EQ::skills::HIGHEST_SKILL + 1] += base_value; else - newbon->SkillDamageAmount[base2] += base1; + newbon->SkillDamageAmount[limit_value] += base_value; break; } case SE_SkillAttackProc: { // You can only have one of these per client. [AA Dragon Punch] - newbon->SkillAttackProc[SBIndex::SKILLPROC_CHANCE] = base1; // Chance base 1000 = 100% proc rate - newbon->SkillAttackProc[SBIndex::SKILLPROC_SKILL] = base2; // Skill to Proc Off + newbon->SkillAttackProc[SBIndex::SKILLPROC_CHANCE] = base_value; // Chance base 1000 = 100% proc rate + newbon->SkillAttackProc[SBIndex::SKILLPROC_SKILL] = limit_value; // Skill to Proc Off newbon->SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID] = rank.spell; // spell to proc break; } case SE_DamageModifier: { // Bad data or unsupported new skill - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; - if (base2 == ALL_SKILLS) - newbon->DamageModifier[EQ::skills::HIGHEST_SKILL + 1] += base1; + if (limit_value == ALL_SKILLS) + newbon->DamageModifier[EQ::skills::HIGHEST_SKILL + 1] += base_value; else - newbon->DamageModifier[base2] += base1; + newbon->DamageModifier[limit_value] += base_value; break; } case SE_DamageModifier2: { // Bad data or unsupported new skill - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; - if (base2 == ALL_SKILLS) - newbon->DamageModifier2[EQ::skills::HIGHEST_SKILL + 1] += base1; + if (limit_value == ALL_SKILLS) + newbon->DamageModifier2[EQ::skills::HIGHEST_SKILL + 1] += base_value; else - newbon->DamageModifier2[base2] += base1; + newbon->DamageModifier2[limit_value] += base_value; break; } case SE_Skill_Base_Damage_Mod: { // Bad data or unsupported new skill - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; - if (base2 == ALL_SKILLS) - newbon->DamageModifier3[EQ::skills::HIGHEST_SKILL + 1] += base1; + if (limit_value == ALL_SKILLS) + newbon->DamageModifier3[EQ::skills::HIGHEST_SKILL + 1] += base_value; else - newbon->DamageModifier3[base2] += base1; + newbon->DamageModifier3[limit_value] += base_value; break; } case SE_SlayUndead: { - if (newbon->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] < base1) - newbon->SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] = base1; // Rate - newbon->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = base2; // Damage Modifier + if (newbon->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] < base_value) + newbon->SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] = base_value; // Rate + newbon->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = limit_value; // Damage Modifier break; } case SE_DoubleRiposte: { - newbon->DoubleRiposte += base1; + newbon->DoubleRiposte += base_value; break; } case SE_GiveDoubleRiposte: { // 0=Regular Riposte 1=Skill Attack Riposte 2=Skill - if (base2 == 0) { - if (newbon->GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_CHANCE] < base1) - newbon->GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_CHANCE] = base1; + if (limit_value == 0) { + if (newbon->GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_CHANCE] < base_value) + newbon->GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_CHANCE] = base_value; } // Only for special attacks. - else if (base2 > 0 && (newbon->GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_SKILL_ATK_CHANCE] < base1)) { - newbon->GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_SKILL_ATK_CHANCE] = base1; - newbon->GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_SKILL] = base2; + else if (limit_value > 0 && (newbon->GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_SKILL_ATK_CHANCE] < base_value)) { + newbon->GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_SKILL_ATK_CHANCE] = base_value; + newbon->GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_SKILL] = limit_value; } break; @@ -1207,48 +1207,48 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) // Physically raises skill cap ie if 55/55 it will raise to 55/60 case SE_RaiseSkillCap: { - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; - if (newbon->RaiseSkillCap[base2] < base1) - newbon->RaiseSkillCap[base2] = base1; + if (newbon->RaiseSkillCap[limit_value] < base_value) + newbon->RaiseSkillCap[limit_value] = base_value; break; } case SE_MasteryofPast: { - if (newbon->MasteryofPast < base1) - newbon->MasteryofPast = base1; + if (newbon->MasteryofPast < base_value) + newbon->MasteryofPast = base_value; break; } case SE_CastingLevel: { - newbon->adjusted_casting_skill += base1; + newbon->adjusted_casting_skill += base_value; break; } case SE_CastingLevel2: { - newbon->effective_casting_level += base1; + newbon->effective_casting_level += base_value; break; } case SE_DivineSave: { - if (newbon->DivineSaveChance[SBIndex::DIVINE_SAVE_CHANCE] < base1) { - newbon->DivineSaveChance[SBIndex::DIVINE_SAVE_CHANCE] = base1; - newbon->DivineSaveChance[SBIndex::DIVINE_SAVE_SPELL_TRIGGER_ID] = base2; + if (newbon->DivineSaveChance[SBIndex::DIVINE_SAVE_CHANCE] < base_value) { + newbon->DivineSaveChance[SBIndex::DIVINE_SAVE_CHANCE] = base_value; + newbon->DivineSaveChance[SBIndex::DIVINE_SAVE_SPELL_TRIGGER_ID] = limit_value; } break; } case SE_SpellEffectResistChance: { for (int e = 0; e < MAX_RESISTABLE_EFFECTS * 2; e += 2) { - if (newbon->SEResist[e + 1] && (newbon->SEResist[e] == base2) && - (newbon->SEResist[e + 1] < base1)) { - newbon->SEResist[e] = base2; // Spell Effect ID - newbon->SEResist[e + 1] = base1; // Resist Chance + if (newbon->SEResist[e + 1] && (newbon->SEResist[e] == limit_value) && + (newbon->SEResist[e + 1] < base_value)) { + newbon->SEResist[e] = limit_value; // Spell Effect ID + newbon->SEResist[e + 1] = base_value; // Resist Chance break; } else if (!newbon->SEResist[e + 1]) { - newbon->SEResist[e] = base2; // Spell Effect ID - newbon->SEResist[e + 1] = base1; // Resist Chance + newbon->SEResist[e] = limit_value; // Spell Effect ID + newbon->SEResist[e + 1] = base_value; // Resist Chance break; } } @@ -1258,68 +1258,68 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) case SE_MitigateDamageShield: { //AA that increase mitigation are set to negative. - if (base1 < 0) { - base1 = base1 * (-1); + if (base_value < 0) { + base_value = base_value * (-1); } - newbon->DSMitigationOffHand += base1; + newbon->DSMitigationOffHand += base_value; break; } case SE_FinishingBlow: { // base1 = chance, base2 = damage - if (newbon->FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] < base2) { - newbon->FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = base1; - newbon->FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] = base2; + if (newbon->FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] < limit_value) { + newbon->FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = base_value; + newbon->FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] = limit_value; } break; } case SE_FinishingBlowLvl: { // base1 = level, base2 = ??? (Set to 200 in AA data, possible proc rate mod?) - if (newbon->FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] < base1) { - newbon->FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = base1; - newbon->FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = base2; + if (newbon->FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] < base_value) { + newbon->FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = base_value; + newbon->FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = limit_value; } break; } case SE_StunBashChance: - newbon->StunBashChance += base1; + newbon->StunBashChance += base_value; break; case SE_IncreaseChanceMemwipe: - newbon->IncreaseChanceMemwipe += base1; + newbon->IncreaseChanceMemwipe += base_value; break; case SE_CriticalMend: - newbon->CriticalMend += base1; + newbon->CriticalMend += base_value; break; case SE_HealRate: - newbon->HealRate += base1; + newbon->HealRate += base_value; break; case SE_MeleeLifetap: { - if ((base1 < 0) && (newbon->MeleeLifetap > base1)) - newbon->MeleeLifetap = base1; + if ((base_value < 0) && (newbon->MeleeLifetap > base_value)) + newbon->MeleeLifetap = base_value; - else if (newbon->MeleeLifetap < base1) - newbon->MeleeLifetap = base1; + else if (newbon->MeleeLifetap < base_value) + newbon->MeleeLifetap = base_value; break; } case SE_Vampirism: - newbon->Vampirism += base1; + newbon->Vampirism += base_value; break; case SE_FrenziedDevastation: - newbon->FrenziedDevastation += base2; + newbon->FrenziedDevastation += limit_value; break; case SE_SpellProcChance: - newbon->SpellProcChance += base1; + newbon->SpellProcChance += base_value; break; case SE_Berserk: @@ -1327,59 +1327,59 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) break; case SE_Metabolism: - newbon->Metabolism += base1; + newbon->Metabolism += base_value; break; case SE_ImprovedReclaimEnergy: { - if ((base1 < 0) && (newbon->ImprovedReclaimEnergy > base1)) - newbon->ImprovedReclaimEnergy = base1; + if ((base_value < 0) && (newbon->ImprovedReclaimEnergy > base_value)) + newbon->ImprovedReclaimEnergy = base_value; - else if (newbon->ImprovedReclaimEnergy < base1) - newbon->ImprovedReclaimEnergy = base1; + else if (newbon->ImprovedReclaimEnergy < base_value) + newbon->ImprovedReclaimEnergy = base_value; break; } case SE_HeadShot: { - if (newbon->HeadShot[SBIndex::FINISHING_EFFECT_DMG] < base2) { - newbon->HeadShot[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = base1; - newbon->HeadShot[SBIndex::FINISHING_EFFECT_DMG] = base2; + if (newbon->HeadShot[SBIndex::FINISHING_EFFECT_DMG] < limit_value) { + newbon->HeadShot[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = base_value; + newbon->HeadShot[SBIndex::FINISHING_EFFECT_DMG] = limit_value; } break; } case SE_HeadShotLevel: { - if (newbon->HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] < base1) - newbon->HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = base1; - newbon->HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = base2; + if (newbon->HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] < base_value) + newbon->HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = base_value; + newbon->HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = limit_value; break; } case SE_Assassinate: { - if (newbon->Assassinate[SBIndex::FINISHING_EFFECT_DMG] < base2) { - newbon->Assassinate[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = base1; - newbon->Assassinate[SBIndex::FINISHING_EFFECT_DMG] = base2; + if (newbon->Assassinate[SBIndex::FINISHING_EFFECT_DMG] < limit_value) { + newbon->Assassinate[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = base_value; + newbon->Assassinate[SBIndex::FINISHING_EFFECT_DMG] = limit_value; } break; } case SE_AssassinateLevel: { - if (newbon->AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] < base1) { - newbon->AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = base1; - newbon->AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = base2; + if (newbon->AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] < base_value) { + newbon->AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = base_value; + newbon->AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = limit_value; } break; } case SE_PetMeleeMitigation: - newbon->PetMeleeMitigation += base1; + newbon->PetMeleeMitigation += base_value; break; case SE_FactionModPct: { - if ((base1 < 0) && (newbon->FactionModPct > base1)) - newbon->FactionModPct = base1; + if ((base_value < 0) && (newbon->FactionModPct > base_value)) + newbon->FactionModPct = base_value; - else if (newbon->FactionModPct < base1) - newbon->FactionModPct = base1; + else if (newbon->FactionModPct < base_value) + newbon->FactionModPct = base_value; break; } @@ -1389,10 +1389,10 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) case SE_LimitToSkill: { // Bad data or unsupported new skill - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; - if (base1 <= EQ::skills::HIGHEST_SKILL) - newbon->LimitToSkill[base1] = true; + if (base_value <= EQ::skills::HIGHEST_SKILL) + newbon->LimitToSkill[base_value] = true; break; } @@ -1424,32 +1424,32 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) } case SE_MeleeMitigation: - newbon->MeleeMitigationEffect += base1; + newbon->MeleeMitigationEffect += base_value; break; case SE_ATK: - newbon->ATK += base1; + newbon->ATK += base_value; break; case SE_IncreaseExtTargetWindow: - newbon->extra_xtargets += base1; + newbon->extra_xtargets += base_value; break; case SE_PC_Pet_Rampage: { - newbon->PC_Pet_Rampage[SBIndex::PET_RAMPAGE_CHANCE] += base1; //Chance to rampage - if (newbon->PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] < base2) - newbon->PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = base2; //Damage modifer - take highest + newbon->PC_Pet_Rampage[SBIndex::PET_RAMPAGE_CHANCE] += base_value; //Chance to rampage + if (newbon->PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] < limit_value) + newbon->PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = limit_value; //Damage modifer - take highest break; } case SE_PC_Pet_AE_Rampage: { - newbon->PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_CHANCE] += base1; //Chance to rampage - if (newbon->PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] < base2) - newbon->PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = base2; //Damage modifer - take highest + newbon->PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_CHANCE] += base_value; //Chance to rampage + if (newbon->PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] < limit_value) + newbon->PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = limit_value; //Damage modifer - take highest break; } case SE_PC_Pet_Flurry_Chance: - newbon->PC_Pet_Flurry += base1; //Chance to Flurry + newbon->PC_Pet_Flurry += base_value; //Chance to Flurry break; case SE_ShroudofStealth: @@ -1457,53 +1457,53 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) break; case SE_ReduceFallDamage: - newbon->ReduceFallDamage += base1; + newbon->ReduceFallDamage += base_value; break; case SE_ReduceTradeskillFail:{ - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; - newbon->ReduceTradeskillFail[base2] += base1; + newbon->ReduceTradeskillFail[limit_value] += base_value; break; } case SE_TradeSkillMastery: - if (newbon->TradeSkillMastery < base1) - newbon->TradeSkillMastery = base1; + if (newbon->TradeSkillMastery < base_value) + newbon->TradeSkillMastery = base_value; break; case SE_NoBreakAESneak: - if (newbon->NoBreakAESneak < base1) - newbon->NoBreakAESneak = base1; + if (newbon->NoBreakAESneak < base_value) + newbon->NoBreakAESneak = base_value; break; case SE_FeignedCastOnChance: - if (newbon->FeignedCastOnChance < base1) - newbon->FeignedCastOnChance = base1; + if (newbon->FeignedCastOnChance < base_value) + newbon->FeignedCastOnChance = base_value; break; case SE_AddPetCommand: - if (base1 && base2 < PET_MAXCOMMANDS) - newbon->PetCommands[base2] = true; + if (base_value && limit_value < PET_MAXCOMMANDS) + newbon->PetCommands[limit_value] = true; break; case SE_FeignedMinion: - if (newbon->FeignedMinionChance < base1) - newbon->FeignedMinionChance = base1; + if (newbon->FeignedMinionChance < base_value) + newbon->FeignedMinionChance = base_value; break; case SE_AdditionalAura: - newbon->aura_slots += base1; + newbon->aura_slots += base_value; break; case SE_IncreaseTrapCount: - newbon->trap_slots += base1; + newbon->trap_slots += base_value; break; case SE_ForageSkill: - newbon->GrantForage += base1; + newbon->GrantForage += base_value; // we need to grant a skill point here // I'd rather not do this here, but whatever, probably fine if (IsClient()) { @@ -1514,88 +1514,88 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) break; case SE_Attack_Accuracy_Max_Percent: - newbon->Attack_Accuracy_Max_Percent += base1; + newbon->Attack_Accuracy_Max_Percent += base_value; break; case SE_AC_Mitigation_Max_Percent: - newbon->AC_Mitigation_Max_Percent += base1; + newbon->AC_Mitigation_Max_Percent += base_value; break; case SE_AC_Avoidance_Max_Percent: - newbon->AC_Avoidance_Max_Percent += base1; + newbon->AC_Avoidance_Max_Percent += base_value; break; case SE_Damage_Taken_Position_Mod: { //Mitigate if damage taken from behind base2 = 0, from front base2 = 1 - if (base2 < 0 || base2 > 2) + if (limit_value < 0 || limit_value > 2) break; - else if (base1 < 0 && newbon->Damage_Taken_Position_Mod[base2] > base1) - newbon->Damage_Taken_Position_Mod[base2] = base1; - else if (base1 > 0 && newbon->Damage_Taken_Position_Mod[base2] < base1) - newbon->Damage_Taken_Position_Mod[base2] = base1; + else if (base_value < 0 && newbon->Damage_Taken_Position_Mod[limit_value] > base_value) + newbon->Damage_Taken_Position_Mod[limit_value] = base_value; + else if (base_value > 0 && newbon->Damage_Taken_Position_Mod[limit_value] < base_value) + newbon->Damage_Taken_Position_Mod[limit_value] = base_value; break; } case SE_Melee_Damage_Position_Mod: { - if (base2 < 0 || base2 > 2) + if (limit_value < 0 || limit_value > 2) break; - else if (base1 < 0 && newbon->Melee_Damage_Position_Mod[base2] > base1) - newbon->Melee_Damage_Position_Mod[base2] = base1; - else if (base1 > 0 && newbon->Melee_Damage_Position_Mod[base2] < base1) - newbon->Melee_Damage_Position_Mod[base2] = base1; + else if (base_value < 0 && newbon->Melee_Damage_Position_Mod[limit_value] > base_value) + newbon->Melee_Damage_Position_Mod[limit_value] = base_value; + else if (base_value > 0 && newbon->Melee_Damage_Position_Mod[limit_value] < base_value) + newbon->Melee_Damage_Position_Mod[limit_value] = base_value; break; } case SE_Damage_Taken_Position_Amt: { //Mitigate if damage taken from behind base2 = 0, from front base2 = 1 - if (base2 < 0 || base2 > 2) + if (limit_value < 0 || limit_value > 2) break; - newbon->Damage_Taken_Position_Amt[base2] += base1; + newbon->Damage_Taken_Position_Amt[limit_value] += base_value; break; } case SE_Melee_Damage_Position_Amt: { //Mitigate if damage taken from behind base2 = 0, from front base2 = 1 - if (base2 < 0 || base2 > 2) + if (limit_value < 0 || limit_value > 2) break; - newbon->Melee_Damage_Position_Amt[base2] += base1; + newbon->Melee_Damage_Position_Amt[limit_value] += base_value; break; } case SE_DS_Mitigation_Amount: - newbon->DS_Mitigation_Amount += base1; + newbon->DS_Mitigation_Amount += base_value; break; case SE_DS_Mitigation_Percentage: - newbon->DS_Mitigation_Percentage += base1; + newbon->DS_Mitigation_Percentage += base_value; break; case SE_Pet_Crit_Melee_Damage_Pct_Owner: - newbon->Pet_Crit_Melee_Damage_Pct_Owner += base1; + newbon->Pet_Crit_Melee_Damage_Pct_Owner += base_value; break; case SE_Pet_Add_Atk: - newbon->Pet_Add_Atk += base1; + newbon->Pet_Add_Atk += base_value; break; case SE_Weapon_Stance: { - if (IsValidSpell(base1)) { //base1 is the spell_id of buff - if (base2 <= WEAPON_STANCE_TYPE_MAX) { //0=2H, 1=Shield, 2=DW - if (IsValidSpell(newbon->WeaponStance[base2])) { //Check if we already a spell_id saved for this effect - if (spells[newbon->WeaponStance[base2]].rank < spells[base1].rank) { //If so, check if any new spellids with higher rank exist (live spells for this are ranked). - newbon->WeaponStance[base2] = base1; //Overwrite with new effect + if (IsValidSpell(base_value)) { //base1 is the spell_id of buff + if (limit_value <= WEAPON_STANCE_TYPE_MAX) { //0=2H, 1=Shield, 2=DW + if (IsValidSpell(newbon->WeaponStance[limit_value])) { //Check if we already a spell_id saved for this effect + if (spells[newbon->WeaponStance[limit_value]].rank < spells[base_value].rank) { //If so, check if any new spellids with higher rank exist (live spells for this are ranked). + newbon->WeaponStance[limit_value] = base_value; //Overwrite with new effect SetWeaponStanceEnabled(true); } } else { - newbon->WeaponStance[base2] = base1; //If no prior effect exists, then apply + newbon->WeaponStance[limit_value] = base_value; //If no prior effect exists, then apply SetWeaponStanceEnabled(true); } } @@ -1605,18 +1605,18 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) case SE_ExtraAttackChance: { - if (newbon->ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] < base1) { - newbon->ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] = base1; - newbon->ExtraAttackChance[SBIndex::EXTRA_ATTACK_NUM_ATKS] = base2 ? base2 : 1; + if (newbon->ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] < base_value) { + newbon->ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] = base_value; + newbon->ExtraAttackChance[SBIndex::EXTRA_ATTACK_NUM_ATKS] = limit_value ? limit_value : 1; } break; } case SE_AddExtraAttackPct_1h_Primary: { - if (newbon->ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] < base1) { - newbon->ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] = base1; - newbon->ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_NUM_ATKS] = base2 ? base2 : 1; + if (newbon->ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] < base_value) { + newbon->ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] = base_value; + newbon->ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_NUM_ATKS] = limit_value ? limit_value : 1; } break; } @@ -1624,18 +1624,18 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) case SE_AddExtraAttackPct_1h_Secondary: { - if (newbon->ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] < base1) { - newbon->ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] = base1; - newbon->ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_NUM_ATKS] = base2 ? base2 : 1; + if (newbon->ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] < base_value) { + newbon->ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] = base_value; + newbon->ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_NUM_ATKS] = limit_value ? limit_value : 1; } break; } case SE_Double_Melee_Round: { - if (newbon->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] < base1) { - newbon->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] = base1; - newbon->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_DMG_BONUS] = base2; + if (newbon->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] < base_value) { + newbon->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] = base_value; + newbon->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_DMG_BONUS] = limit_value; } break; @@ -1643,52 +1643,52 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) case SE_ExtendedShielding: { - if (newbon->ExtendedShielding < base1) { - newbon->ExtendedShielding = base1; + if (newbon->ExtendedShielding < base_value) { + newbon->ExtendedShielding = base_value; } break; } case SE_ShieldDuration: { - if (newbon->ShieldDuration < base1) { - newbon->ShieldDuration = base1; + if (newbon->ShieldDuration < base_value) { + newbon->ShieldDuration = base_value; } break; } case SE_Worn_Endurance_Regen_Cap: - newbon->ItemEnduranceRegenCap += base1; + newbon->ItemEnduranceRegenCap += base_value; break; case SE_SecondaryForte: - if (newbon->SecondaryForte < base1) { - newbon->SecondaryForte = base1; + if (newbon->SecondaryForte < base_value) { + newbon->SecondaryForte = base_value; } break; case SE_ZoneSuspendMinion: - newbon->ZoneSuspendMinion = base1; + newbon->ZoneSuspendMinion = base_value; break; case SE_Reflect: - if (newbon->reflect[SBIndex::REFLECT_CHANCE] < base1) { - newbon->reflect[SBIndex::REFLECT_CHANCE] = base1; + if (newbon->reflect[SBIndex::REFLECT_CHANCE] < base_value) { + newbon->reflect[SBIndex::REFLECT_CHANCE] = base_value; } - if (newbon->reflect[SBIndex::REFLECT_RESISTANCE_MOD] < base2) { - newbon->reflect[SBIndex::REFLECT_RESISTANCE_MOD] = base2; + if (newbon->reflect[SBIndex::REFLECT_RESISTANCE_MOD] < limit_value) { + newbon->reflect[SBIndex::REFLECT_RESISTANCE_MOD] = limit_value; } break; case SE_SpellDamageShield: - newbon->SpellDamageShield += base1; + newbon->SpellDamageShield += base_value; break; case SE_Amplification: - newbon->Amplification += base1; + newbon->Amplification += base_value; break; // to do @@ -1756,7 +1756,7 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon) if(buffs[i].spellid != SPELL_UNKNOWN){ ApplySpellsBonuses(buffs[i].spellid, buffs[i].casterlevel, newbon, buffs[i].casterid, 0, buffs[i].ticsremaining, i, buffs[i].instrument_mod); - if (buffs[i].numhits > 0) + if (buffs[i].hit_number > 0) Numhits(true); } } @@ -1781,7 +1781,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne uint8 WornType, int32 ticsremaining, int buffslot, int instrument_mod, bool IsAISpellEffect, uint16 effect_id, int32 se_base, int32 se_limit, int32 se_max) { - int i, effect_value, base2, max, effectid; + int i, effect_value, limit_value, max_value, spell_effect_id; bool AdditiveWornBonus = false; if(!IsAISpellEffect && !IsValidSpell(spell_id)) @@ -1800,11 +1800,11 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne { if (WornType){ if (RuleB(Spells, UseAdditiveFocusFromWornSlot)) { - new_bonus->FocusEffectsWorn[focus] += spells[spell_id].base[i]; + new_bonus->FocusEffectsWorn[focus] += spells[spell_id].base_value[i]; } } else { - new_bonus->FocusEffects[focus] = static_cast(spells[spell_id].effectid[i]); + new_bonus->FocusEffects[focus] = static_cast(spells[spell_id].effect_id[i]); } continue; } @@ -1813,21 +1813,21 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne AdditiveWornBonus = true; } - effectid = spells[spell_id].effectid[i]; + spell_effect_id = spells[spell_id].effect_id[i]; effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, instrument_mod, nullptr, ticsremaining, casterId); - base2 = spells[spell_id].base2[i]; - max = spells[spell_id].max[i]; + limit_value = spells[spell_id].limit_value[i]; + max_value = spells[spell_id].max_value[i]; } //Use AISpellEffects else { - effectid = effect_id; + spell_effect_id = effect_id; effect_value = se_base; - base2 = se_limit; - max = se_max; + limit_value = se_limit; + max_value = se_max; i = EFFECT_COUNT; //End the loop } - switch (effectid) + switch (spell_effect_id) { case SE_CurrentHP: //regens if(effect_value > 0) { @@ -1841,7 +1841,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_ChangeFrenzyRad: { - if (max != 0 && GetLevel() > max) + if (max_value != 0 && GetLevel() > max_value) break; if(new_bonus->AggroRange == -1 || effect_value < new_bonus->AggroRange) @@ -1853,7 +1853,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_Harmony: { - if (max != 0 && GetLevel() > max) + if (max_value != 0 && GetLevel() > max_value) break; // Harmony effect as buff - kinda tricky // harmony could stack with a lull spell, which has better aggro range @@ -2010,7 +2010,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_CHA: { - if (spells[spell_id].base[i] != 0) { + if (spells[spell_id].base_value[i] != 0) { new_bonus->CHA += effect_value; } break; @@ -2076,7 +2076,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_RaiseStatCap: { - switch(spells[spell_id].base2[i]) + switch(spells[spell_id].limit_value[i]) { //are these #define'd somewhere? case 0: //str @@ -2147,8 +2147,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne new_bonus->DamageShield += effect_value; new_bonus->DamageShieldSpellID = spell_id; //When using npc_spells_effects MAX value can be set to determine DS Type - if (IsAISpellEffect && max) - new_bonus->DamageShieldType = GetDamageShieldType(spell_id, max); + if (IsAISpellEffect && max_value) + new_bonus->DamageShieldType = GetDamageShieldType(spell_id, max_value); else new_bonus->DamageShieldType = GetDamageShieldType(spell_id); @@ -2160,8 +2160,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne new_bonus->ReverseDamageShield += effect_value; new_bonus->ReverseDamageShieldSpellID = spell_id; - if (IsAISpellEffect && max) - new_bonus->ReverseDamageShieldType = GetDamageShieldType(spell_id, max); + if (IsAISpellEffect && max_value) + new_bonus->ReverseDamageShieldType = GetDamageShieldType(spell_id, max_value); else new_bonus->ReverseDamageShieldType = GetDamageShieldType(spell_id); break; @@ -2175,8 +2175,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne else if (new_bonus->reflect[SBIndex::REFLECT_CHANCE] < effect_value) { new_bonus->reflect[SBIndex::REFLECT_CHANCE] = effect_value; - new_bonus->reflect[SBIndex::REFLECT_RESISTANCE_MOD] = base2; - new_bonus->reflect[SBIndex::REFLECT_DMG_EFFECTIVENESS] = max; + new_bonus->reflect[SBIndex::REFLECT_RESISTANCE_MOD] = limit_value; + new_bonus->reflect[SBIndex::REFLECT_DMG_EFFECTIVENESS] = max_value; } break; @@ -2196,28 +2196,28 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_CriticalHitChance: { // Bad data or unsupported new skill - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; if (AdditiveWornBonus) { - if(base2 == ALL_SKILLS) + if(limit_value == ALL_SKILLS) new_bonus->CriticalHitChance[EQ::skills::HIGHEST_SKILL + 1] += effect_value; else - new_bonus->CriticalHitChance[base2] += effect_value; + new_bonus->CriticalHitChance[limit_value] += effect_value; } else if(effect_value < 0) { - if (base2 == ALL_SKILLS && new_bonus->CriticalHitChance[EQ::skills::HIGHEST_SKILL + 1] > effect_value) + if (limit_value == ALL_SKILLS && new_bonus->CriticalHitChance[EQ::skills::HIGHEST_SKILL + 1] > effect_value) new_bonus->CriticalHitChance[EQ::skills::HIGHEST_SKILL + 1] = effect_value; - else if(base2 != ALL_SKILLS && new_bonus->CriticalHitChance[base2] > effect_value) - new_bonus->CriticalHitChance[base2] = effect_value; + else if(limit_value != ALL_SKILLS && new_bonus->CriticalHitChance[limit_value] > effect_value) + new_bonus->CriticalHitChance[limit_value] = effect_value; } - else if (base2 == ALL_SKILLS && new_bonus->CriticalHitChance[EQ::skills::HIGHEST_SKILL + 1] < effect_value) + else if (limit_value == ALL_SKILLS && new_bonus->CriticalHitChance[EQ::skills::HIGHEST_SKILL + 1] < effect_value) new_bonus->CriticalHitChance[EQ::skills::HIGHEST_SKILL + 1] = effect_value; - else if(base2 != ALL_SKILLS && new_bonus->CriticalHitChance[base2] < effect_value) - new_bonus->CriticalHitChance[base2] = effect_value; + else if(limit_value != ALL_SKILLS && new_bonus->CriticalHitChance[limit_value] < effect_value) + new_bonus->CriticalHitChance[limit_value] = effect_value; break; } @@ -2332,7 +2332,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_MeleeLifetap: { if (AdditiveWornBonus) - new_bonus->MeleeLifetap += spells[spell_id].base[i]; + new_bonus->MeleeLifetap += spells[spell_id].base_value[i]; else if((effect_value < 0) && (new_bonus->MeleeLifetap > effect_value)) new_bonus->MeleeLifetap = effect_value; @@ -2394,7 +2394,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne { if(new_bonus->MeleeSkillCheck < effect_value) { new_bonus->MeleeSkillCheck = effect_value; - new_bonus->MeleeSkillCheckSkill = base2==ALL_SKILLS?255:base2; + new_bonus->MeleeSkillCheckSkill = limit_value==ALL_SKILLS?255:limit_value; } break; } @@ -2402,17 +2402,17 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_HitChance: { // Bad data or unsupported new skill - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; if (AdditiveWornBonus){ - if(base2 == ALL_SKILLS) + if(limit_value == ALL_SKILLS) new_bonus->HitChanceEffect[EQ::skills::HIGHEST_SKILL + 1] += effect_value; else - new_bonus->HitChanceEffect[base2] += effect_value; + new_bonus->HitChanceEffect[limit_value] += effect_value; } - else if(base2 == ALL_SKILLS){ + else if(limit_value == ALL_SKILLS){ if ((effect_value < 0) && (new_bonus->HitChanceEffect[EQ::skills::HIGHEST_SKILL + 1] > effect_value)) new_bonus->HitChanceEffect[EQ::skills::HIGHEST_SKILL + 1] = effect_value; @@ -2424,12 +2424,12 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne else { - if ((effect_value < 0) && (new_bonus->HitChanceEffect[base2] > effect_value)) - new_bonus->HitChanceEffect[base2] = effect_value; + if ((effect_value < 0) && (new_bonus->HitChanceEffect[limit_value] > effect_value)) + new_bonus->HitChanceEffect[limit_value] = effect_value; - else if (!new_bonus->HitChanceEffect[base2] || - ((new_bonus->HitChanceEffect[base2] > 0) && (new_bonus->HitChanceEffect[base2] < effect_value))) - new_bonus->HitChanceEffect[base2] = effect_value; + else if (!new_bonus->HitChanceEffect[limit_value] || + ((new_bonus->HitChanceEffect[limit_value] > 0) && (new_bonus->HitChanceEffect[limit_value] < effect_value))) + new_bonus->HitChanceEffect[limit_value] = effect_value; } break; @@ -2439,9 +2439,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_DamageModifier: { // Bad data or unsupported new skill - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; - int skill = base2 == ALL_SKILLS ? EQ::skills::HIGHEST_SKILL + 1 : base2; + int skill = limit_value == ALL_SKILLS ? EQ::skills::HIGHEST_SKILL + 1 : limit_value; if (effect_value < 0 && new_bonus->DamageModifier[skill] > effect_value) new_bonus->DamageModifier[skill] = effect_value; else if (effect_value > 0 && new_bonus->DamageModifier[skill] < effect_value) @@ -2452,9 +2452,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_DamageModifier2: { // Bad data or unsupported new skill - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; - int skill = base2 == ALL_SKILLS ? EQ::skills::HIGHEST_SKILL + 1 : base2; + int skill = limit_value == ALL_SKILLS ? EQ::skills::HIGHEST_SKILL + 1 : limit_value; if (effect_value < 0 && new_bonus->DamageModifier2[skill] > effect_value) new_bonus->DamageModifier2[skill] = effect_value; else if (effect_value > 0 && new_bonus->DamageModifier2[skill] < effect_value) @@ -2465,9 +2465,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_Skill_Base_Damage_Mod: { // Bad data or unsupported new skill - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; - int skill = base2 == ALL_SKILLS ? EQ::skills::HIGHEST_SKILL + 1 : base2; + int skill = limit_value == ALL_SKILLS ? EQ::skills::HIGHEST_SKILL + 1 : limit_value; if (effect_value < 0 && new_bonus->DamageModifier3[skill] > effect_value) new_bonus->DamageModifier3[skill] = effect_value; else if (effect_value > 0 && new_bonus->DamageModifier3[skill] < effect_value) @@ -2478,9 +2478,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_MinDamageModifier: { // Bad data or unsupported new skill - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; - int skill = base2 == ALL_SKILLS ? EQ::skills::HIGHEST_SKILL + 1 : base2; + int skill = limit_value == ALL_SKILLS ? EQ::skills::HIGHEST_SKILL + 1 : limit_value; if (effect_value < 0 && new_bonus->MinDamageModifier[skill] > effect_value) new_bonus->MinDamageModifier[skill] = effect_value; else if (effect_value > 0 && new_bonus->MinDamageModifier[skill] < effect_value) @@ -2513,11 +2513,11 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne { if (AdditiveWornBonus) { new_bonus->ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] += effect_value; - new_bonus->ExtraAttackChance[SBIndex::EXTRA_ATTACK_NUM_ATKS] = base2 ? base2 : 1; + new_bonus->ExtraAttackChance[SBIndex::EXTRA_ATTACK_NUM_ATKS] = limit_value ? limit_value : 1; } if (new_bonus->ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] < effect_value) { new_bonus->ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; - new_bonus->ExtraAttackChance[SBIndex::EXTRA_ATTACK_NUM_ATKS] = base2 ? base2 : 1; + new_bonus->ExtraAttackChance[SBIndex::EXTRA_ATTACK_NUM_ATKS] = limit_value ? limit_value : 1; } break; } @@ -2526,12 +2526,12 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne { if (AdditiveWornBonus) { new_bonus->ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] += effect_value; - new_bonus->ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_NUM_ATKS] = base2 ? base2 : 1; + new_bonus->ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_NUM_ATKS] = limit_value ? limit_value : 1; } if (new_bonus->ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] < effect_value) { new_bonus->ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; - new_bonus->ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_NUM_ATKS] = base2 ? base2 : 1; + new_bonus->ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_NUM_ATKS] = limit_value ? limit_value : 1; } break; } @@ -2540,12 +2540,12 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne { if (AdditiveWornBonus) { new_bonus->ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] += effect_value; - new_bonus->ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_NUM_ATKS] = base2 ? base2 : 1; + new_bonus->ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_NUM_ATKS] = limit_value ? limit_value : 1; } if (new_bonus->ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] < effect_value) { new_bonus->ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] = effect_value; - new_bonus->ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_NUM_ATKS] = base2 ? base2 : 1; + new_bonus->ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_NUM_ATKS] = limit_value ? limit_value : 1; } break; } @@ -2554,12 +2554,12 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne { if (AdditiveWornBonus) { new_bonus->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] += effect_value; - new_bonus->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_DMG_BONUS] += base2; + new_bonus->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_DMG_BONUS] += limit_value; } if (new_bonus->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] < effect_value) { new_bonus->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_CHANCE] = effect_value; - new_bonus->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_DMG_BONUS] = base2; + new_bonus->DoubleMeleeRound[SBIndex::DOUBLE_MELEE_ROUND_DMG_BONUS] = limit_value; } break; } @@ -2578,8 +2578,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne new_bonus->DeathSave[SBIndex::DEATH_SAVE_TYPE] = effect_value; //1='Partial' 2='Full' new_bonus->DeathSave[SBIndex::DEATH_SAVE_BUFFSLOT] = buffslot; //These are used in later expansion spell effects. - new_bonus->DeathSave[SBIndex::DEATH_SAVE_MIN_LEVEL_FOR_HEAL] = base2;//Min level for HealAmt - new_bonus->DeathSave[SBIndex::DEATH_SAVE_HEAL_AMT] = max;//HealAmt + new_bonus->DeathSave[SBIndex::DEATH_SAVE_MIN_LEVEL_FOR_HEAL] = limit_value;//Min level for HealAmt + new_bonus->DeathSave[SBIndex::DEATH_SAVE_HEAL_AMT] = max_value;//HealAmt } break; } @@ -2594,7 +2594,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne else if(new_bonus->DivineSaveChance[SBIndex::DIVINE_SAVE_CHANCE] < effect_value) { new_bonus->DivineSaveChance[SBIndex::DIVINE_SAVE_CHANCE] = effect_value; - new_bonus->DivineSaveChance[SBIndex::DIVINE_SAVE_SPELL_TRIGGER_ID] = base2; + new_bonus->DivineSaveChance[SBIndex::DIVINE_SAVE_SPELL_TRIGGER_ID] = limit_value; } break; } @@ -2606,7 +2606,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_Accuracy: { // Bad data or unsupported new skill - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; if ((effect_value < 0) && (new_bonus->Accuracy[EQ::skills::HIGHEST_SKILL + 1] > effect_value)) new_bonus->Accuracy[EQ::skills::HIGHEST_SKILL + 1] = effect_value; @@ -2632,21 +2632,21 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_SkillDamageTaken: { // Bad data or unsupported new skill - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; //When using npc_spells_effects if MAX value set, use stackable quest based modifier. - if (IsAISpellEffect && max){ - if(base2 == ALL_SKILLS) + if (IsAISpellEffect && max_value){ + if(limit_value == ALL_SKILLS) SkillDmgTaken_Mod[EQ::skills::HIGHEST_SKILL + 1] = effect_value; else - SkillDmgTaken_Mod[base2] = effect_value; + SkillDmgTaken_Mod[limit_value] = effect_value; } else { - if(base2 == ALL_SKILLS) + if(limit_value == ALL_SKILLS) new_bonus->SkillDmgTaken[EQ::skills::HIGHEST_SKILL + 1] += effect_value; else - new_bonus->SkillDmgTaken[base2] += effect_value; + new_bonus->SkillDmgTaken[limit_value] += effect_value; } break; @@ -2660,8 +2660,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne { new_bonus->CriticalSpellChance += effect_value; - if (base2 > new_bonus->SpellCritDmgIncNoStack) - new_bonus->SpellCritDmgIncNoStack = base2; + if (limit_value > new_bonus->SpellCritDmgIncNoStack) + new_bonus->SpellCritDmgIncNoStack = limit_value; break; } @@ -2701,8 +2701,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne spells that do decrease the mitigation with just negative base values. To be consistent all values that increase mitigation will be set to positives */ - if (max > 0 && effect_value < 0) { - effect_value = max; + if (max_value > 0 && effect_value < 0) { + effect_value = max_value; } new_bonus->DSMitigationOffHand += effect_value; @@ -2720,9 +2720,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne if(!new_bonus->SpellOnKill[e]) { // Base2 = Spell to fire | Base1 = % chance | Base3 = min level - new_bonus->SpellOnKill[e] = base2; + new_bonus->SpellOnKill[e] = limit_value; new_bonus->SpellOnKill[e+1] = effect_value; - new_bonus->SpellOnKill[e+2] = max; + new_bonus->SpellOnKill[e+2] = max_value; break; } } @@ -2736,7 +2736,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne if(!new_bonus->SpellOnDeath[e]) { // Base2 = Spell to fire | Base1 = % chance - new_bonus->SpellOnDeath[e] = base2; + new_bonus->SpellOnDeath[e] = limit_value; new_bonus->SpellOnDeath[e+1] = effect_value; break; } @@ -2747,21 +2747,21 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_CriticalDamageMob: { // Bad data or unsupported new skill - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; - if(base2 == ALL_SKILLS) + if(limit_value == ALL_SKILLS) new_bonus->CritDmgMod[EQ::skills::HIGHEST_SKILL + 1] += effect_value; else - new_bonus->CritDmgMod[base2] += effect_value; + new_bonus->CritDmgMod[limit_value] += effect_value; break; } case SE_Critical_Melee_Damage_Mod_Max: { // Bad data or unsupported new skill - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; - int skill = base2 == ALL_SKILLS ? EQ::skills::HIGHEST_SKILL + 1 : base2; + int skill = limit_value == ALL_SKILLS ? EQ::skills::HIGHEST_SKILL + 1 : limit_value; if (effect_value < 0 && new_bonus->CritDmgModNoStack[skill] > effect_value) new_bonus->CritDmgModNoStack[skill] = effect_value; else if (effect_value > 0 && new_bonus->CritDmgModNoStack[skill] < effect_value) { @@ -2772,20 +2772,20 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_ReduceSkillTimer: { - if(new_bonus->SkillReuseTime[base2] < effect_value) - new_bonus->SkillReuseTime[base2] = effect_value; + if(new_bonus->SkillReuseTime[limit_value] < effect_value) + new_bonus->SkillReuseTime[limit_value] = effect_value; break; } case SE_SkillDamageAmount: { // Bad data or unsupported new skill - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; - if(base2 == ALL_SKILLS) + if(limit_value == ALL_SKILLS) new_bonus->SkillDamageAmount[EQ::skills::HIGHEST_SKILL + 1] += effect_value; else - new_bonus->SkillDamageAmount[base2] += effect_value; + new_bonus->SkillDamageAmount[limit_value] += effect_value; break; } @@ -2822,11 +2822,11 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne { if(new_bonus->HPPercCap[SBIndex::RESOURCE_PERCENT_CAP] != 0 && new_bonus->HPPercCap[SBIndex::RESOURCE_PERCENT_CAP] > effect_value){ new_bonus->HPPercCap[SBIndex::RESOURCE_PERCENT_CAP] = effect_value; - new_bonus->HPPercCap[SBIndex::RESOURCE_AMOUNT_CAP] = base2; + new_bonus->HPPercCap[SBIndex::RESOURCE_AMOUNT_CAP] = limit_value; } else if(new_bonus->HPPercCap[SBIndex::RESOURCE_PERCENT_CAP] == 0){ new_bonus->HPPercCap[SBIndex::RESOURCE_PERCENT_CAP] = effect_value; - new_bonus->HPPercCap[SBIndex::RESOURCE_AMOUNT_CAP] = base2; + new_bonus->HPPercCap[SBIndex::RESOURCE_AMOUNT_CAP] = limit_value; } break; } @@ -2834,11 +2834,11 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne { if(new_bonus->ManaPercCap[SBIndex::RESOURCE_PERCENT_CAP] != 0 && new_bonus->ManaPercCap[SBIndex::RESOURCE_PERCENT_CAP] > effect_value){ new_bonus->ManaPercCap[SBIndex::RESOURCE_PERCENT_CAP] = effect_value; - new_bonus->ManaPercCap[SBIndex::RESOURCE_AMOUNT_CAP] = base2; + new_bonus->ManaPercCap[SBIndex::RESOURCE_AMOUNT_CAP] = limit_value; } else if(new_bonus->ManaPercCap[SBIndex::RESOURCE_PERCENT_CAP] == 0) { new_bonus->ManaPercCap[SBIndex::RESOURCE_PERCENT_CAP] = effect_value; - new_bonus->ManaPercCap[SBIndex::RESOURCE_AMOUNT_CAP] = base2; + new_bonus->ManaPercCap[SBIndex::RESOURCE_AMOUNT_CAP] = limit_value; } break; @@ -2847,12 +2847,12 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne { if(new_bonus->EndPercCap[SBIndex::RESOURCE_PERCENT_CAP] != 0 && new_bonus->EndPercCap[SBIndex::RESOURCE_PERCENT_CAP] > effect_value) { new_bonus->EndPercCap[SBIndex::RESOURCE_PERCENT_CAP] = effect_value; - new_bonus->EndPercCap[SBIndex::RESOURCE_AMOUNT_CAP] = base2; + new_bonus->EndPercCap[SBIndex::RESOURCE_AMOUNT_CAP] = limit_value; } else if(new_bonus->EndPercCap[SBIndex::RESOURCE_PERCENT_CAP] == 0){ new_bonus->EndPercCap[SBIndex::RESOURCE_PERCENT_CAP] = effect_value; - new_bonus->EndPercCap[SBIndex::RESOURCE_AMOUNT_CAP] = base2; + new_bonus->EndPercCap[SBIndex::RESOURCE_AMOUNT_CAP] = limit_value; } break; @@ -2888,29 +2888,29 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne { //Lower the ratio the more favorable if((!new_bonus->HPToManaConvert) || (new_bonus->HPToManaConvert >= effect_value)) - new_bonus->HPToManaConvert = spells[spell_id].base[i]; + new_bonus->HPToManaConvert = spells[spell_id].base_value[i]; break; } case SE_SkillDamageAmount2: { // Bad data or unsupported new skill - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; - if(base2 == ALL_SKILLS) + if(limit_value == ALL_SKILLS) new_bonus->SkillDamageAmount2[EQ::skills::HIGHEST_SKILL + 1] += effect_value; else - new_bonus->SkillDamageAmount2[base2] += effect_value; + new_bonus->SkillDamageAmount2[limit_value] += effect_value; break; } case SE_NegateAttacks: { if (!new_bonus->NegateAttacks[SBIndex::NEGATE_ATK_EXISTS] || - ((new_bonus->NegateAttacks[SBIndex::NEGATE_ATK_EXISTS] && new_bonus->NegateAttacks[SBIndex::NEGATE_ATK_MAX_DMG_ABSORB_PER_HIT]) && (new_bonus->NegateAttacks[SBIndex::NEGATE_ATK_MAX_DMG_ABSORB_PER_HIT] < max))){ + ((new_bonus->NegateAttacks[SBIndex::NEGATE_ATK_EXISTS] && new_bonus->NegateAttacks[SBIndex::NEGATE_ATK_MAX_DMG_ABSORB_PER_HIT]) && (new_bonus->NegateAttacks[SBIndex::NEGATE_ATK_MAX_DMG_ABSORB_PER_HIT] < max_value))){ new_bonus->NegateAttacks[SBIndex::NEGATE_ATK_EXISTS] = 1; new_bonus->NegateAttacks[SBIndex::NEGATE_ATK_BUFFSLOT] = buffslot; - new_bonus->NegateAttacks[SBIndex::NEGATE_ATK_MAX_DMG_ABSORB_PER_HIT] = max; + new_bonus->NegateAttacks[SBIndex::NEGATE_ATK_MAX_DMG_ABSORB_PER_HIT] = max_value; } break; } @@ -2920,8 +2920,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne if (new_bonus->MitigateMeleeRune[SBIndex::MITIGATION_RUNE_PERCENT] < effect_value){ new_bonus->MitigateMeleeRune[SBIndex::MITIGATION_RUNE_PERCENT] = effect_value; new_bonus->MitigateMeleeRune[SBIndex::MITIGATION_RUNE_BUFFSLOT] = buffslot; - new_bonus->MitigateMeleeRune[SBIndex::MITIGATION_RUNE_MAX_DMG_ABSORB_PER_HIT] = base2; - new_bonus->MitigateMeleeRune[SBIndex::MITIGATION_RUNE_MAX_HP_AMT] = max; + new_bonus->MitigateMeleeRune[SBIndex::MITIGATION_RUNE_MAX_DMG_ABSORB_PER_HIT] = limit_value; + new_bonus->MitigateMeleeRune[SBIndex::MITIGATION_RUNE_MAX_HP_AMT] = max_value; } break; } @@ -2932,7 +2932,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne if (new_bonus->MeleeThresholdGuard[SBIndex::THRESHOLDGUARD_MITIGATION_PERCENT] < effect_value){ new_bonus->MeleeThresholdGuard[SBIndex::THRESHOLDGUARD_MITIGATION_PERCENT] = effect_value; new_bonus->MeleeThresholdGuard[SBIndex::THRESHOLDGUARD_BUFFSLOT] = buffslot; - new_bonus->MeleeThresholdGuard[SBIndex::THRESHOLDGUARD_MIN_DMG_TO_TRIGGER] = base2; + new_bonus->MeleeThresholdGuard[SBIndex::THRESHOLDGUARD_MIN_DMG_TO_TRIGGER] = limit_value; } break; } @@ -2942,7 +2942,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne if (new_bonus->SpellThresholdGuard[SBIndex::THRESHOLDGUARD_MITIGATION_PERCENT] < effect_value){ new_bonus->SpellThresholdGuard[SBIndex::THRESHOLDGUARD_MITIGATION_PERCENT] = effect_value; new_bonus->SpellThresholdGuard[SBIndex::THRESHOLDGUARD_BUFFSLOT] = buffslot; - new_bonus->SpellThresholdGuard[SBIndex::THRESHOLDGUARD_MIN_DMG_TO_TRIGGER] = base2; + new_bonus->SpellThresholdGuard[SBIndex::THRESHOLDGUARD_MIN_DMG_TO_TRIGGER] = limit_value; } break; } @@ -2952,8 +2952,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne if (new_bonus->MitigateSpellRune[SBIndex::MITIGATION_RUNE_PERCENT] < effect_value){ new_bonus->MitigateSpellRune[SBIndex::MITIGATION_RUNE_PERCENT] = effect_value; new_bonus->MitigateSpellRune[SBIndex::MITIGATION_RUNE_BUFFSLOT] = buffslot; - new_bonus->MitigateSpellRune[SBIndex::MITIGATION_RUNE_MAX_DMG_ABSORB_PER_HIT] = base2; - new_bonus->MitigateSpellRune[SBIndex::MITIGATION_RUNE_MAX_HP_AMT] = max; + new_bonus->MitigateSpellRune[SBIndex::MITIGATION_RUNE_MAX_DMG_ABSORB_PER_HIT] = limit_value; + new_bonus->MitigateSpellRune[SBIndex::MITIGATION_RUNE_MAX_HP_AMT] = max_value; } break; } @@ -2963,8 +2963,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne if (new_bonus->MitigateDotRune[SBIndex::MITIGATION_RUNE_PERCENT] < effect_value){ new_bonus->MitigateDotRune[SBIndex::MITIGATION_RUNE_PERCENT] = effect_value; new_bonus->MitigateDotRune[SBIndex::MITIGATION_RUNE_BUFFSLOT] = buffslot; - new_bonus->MitigateDotRune[SBIndex::MITIGATION_RUNE_MAX_DMG_ABSORB_PER_HIT] = base2; - new_bonus->MitigateDotRune[SBIndex::MITIGATION_RUNE_MAX_HP_AMT] = max; + new_bonus->MitigateDotRune[SBIndex::MITIGATION_RUNE_MAX_DMG_ABSORB_PER_HIT] = limit_value; + new_bonus->MitigateDotRune[SBIndex::MITIGATION_RUNE_MAX_HP_AMT] = max_value; } break; } @@ -2981,7 +2981,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne { if (new_bonus->EnduranceAbsorbPercentDamage[SBIndex::ENDURANCE_ABSORD_MITIGIATION] < effect_value) { new_bonus->EnduranceAbsorbPercentDamage[SBIndex::ENDURANCE_ABSORD_MITIGIATION] = effect_value; - new_bonus->EnduranceAbsorbPercentDamage[SBIndex::ENDURANCE_ABSORD_DRAIN_PER_HP] = base2; + new_bonus->EnduranceAbsorbPercentDamage[SBIndex::ENDURANCE_ABSORD_DRAIN_PER_HP] = limit_value; } break; } @@ -3097,7 +3097,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne break; case SE_AddSingingMod: - switch (base2) { + switch (limit_value) { case EQ::item::ItemTypeWindInstrument: new_bonus->windMod += effect_value; break; @@ -3191,13 +3191,13 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne { for(int e = 0; e < MAX_RESISTABLE_EFFECTS*2; e+=2) { - if(new_bonus->SEResist[e+1] && (new_bonus->SEResist[e] == base2) && (new_bonus->SEResist[e+1] < effect_value)){ - new_bonus->SEResist[e] = base2; //Spell Effect ID + if(new_bonus->SEResist[e+1] && (new_bonus->SEResist[e] == limit_value) && (new_bonus->SEResist[e+1] < effect_value)){ + new_bonus->SEResist[e] = limit_value; //Spell Effect ID new_bonus->SEResist[e+1] = effect_value; //Resist Chance break; } else if (!new_bonus->SEResist[e+1]){ - new_bonus->SEResist[e] = base2; //Spell Effect ID + new_bonus->SEResist[e] = limit_value; //Spell Effect ID new_bonus->SEResist[e+1] = effect_value; //Resist Chance break; } @@ -3220,7 +3220,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_GiveDoubleRiposte: { //Only allow for regular double riposte chance. - if(new_bonus->GiveDoubleRiposte[base2] == 0){ + if(new_bonus->GiveDoubleRiposte[limit_value] == 0){ if(new_bonus->GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_CHANCE] < effect_value) new_bonus->GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_CHANCE] = effect_value; } @@ -3231,7 +3231,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne { if(new_bonus->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] < effect_value) new_bonus->SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] = effect_value; // Rate - new_bonus->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = base2; // Damage Modifier + new_bonus->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = limit_value; // Damage Modifier break; } @@ -3247,7 +3247,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_ImprovedTaunt: if (new_bonus->ImprovedTaunt[SBIndex::IMPROVED_TAUNT_MAX_LVL] < effect_value) { new_bonus->ImprovedTaunt[SBIndex::IMPROVED_TAUNT_MAX_LVL] = effect_value; - new_bonus->ImprovedTaunt[SBIndex::IMPROVED_TAUNT_AGGRO_MOD] = base2; + new_bonus->ImprovedTaunt[SBIndex::IMPROVED_TAUNT_AGGRO_MOD] = limit_value; new_bonus->ImprovedTaunt[SBIndex::IMPROVED_TAUNT_BUFFSLOT] = buffslot; } break; @@ -3258,7 +3258,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne break; case SE_FrenziedDevastation: - new_bonus->FrenziedDevastation += base2; + new_bonus->FrenziedDevastation += limit_value; break; case SE_Root: @@ -3366,9 +3366,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_HeadShot: { - if(new_bonus->HeadShot[SBIndex::FINISHING_EFFECT_DMG] < base2){ + if(new_bonus->HeadShot[SBIndex::FINISHING_EFFECT_DMG] < limit_value){ new_bonus->HeadShot[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; - new_bonus->HeadShot[SBIndex::FINISHING_EFFECT_DMG] = base2; + new_bonus->HeadShot[SBIndex::FINISHING_EFFECT_DMG] = limit_value; } break; } @@ -3377,16 +3377,16 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne { if(new_bonus->HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] < effect_value) { new_bonus->HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; - new_bonus->HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = base2; + new_bonus->HSLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = limit_value; } break; } case SE_Assassinate: { - if(new_bonus->Assassinate[SBIndex::FINISHING_EFFECT_DMG] < base2){ + if(new_bonus->Assassinate[SBIndex::FINISHING_EFFECT_DMG] < limit_value){ new_bonus->Assassinate[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; - new_bonus->Assassinate[SBIndex::FINISHING_EFFECT_DMG] = base2; + new_bonus->Assassinate[SBIndex::FINISHING_EFFECT_DMG] = limit_value; } break; } @@ -3395,7 +3395,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne { if(new_bonus->AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] < effect_value) { new_bonus->AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; - new_bonus->AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = base2; + new_bonus->AssassinateLevel[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = limit_value; } break; } @@ -3403,9 +3403,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_FinishingBlow: { //base1 = chance, base2 = damage - if (new_bonus->FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] < base2){ + if (new_bonus->FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] < limit_value){ new_bonus->FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE] = effect_value; - new_bonus->FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] = base2; + new_bonus->FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] = limit_value; } break; } @@ -3415,7 +3415,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne //base1 = level, base2 = ??? (Set to 200 in AA data, possible proc rate mod?) if (new_bonus->FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] < effect_value){ new_bonus->FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; - new_bonus->FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = base2; + new_bonus->FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = limit_value; } break; } @@ -3444,7 +3444,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_LimitToSkill:{ // Bad data or unsupported new skill - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; if (effect_value <= EQ::skills::HIGHEST_SKILL){ new_bonus->LimitToSkill[effect_value] = true; @@ -3484,15 +3484,15 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_PC_Pet_Rampage: { new_bonus->PC_Pet_Rampage[SBIndex::PET_RAMPAGE_CHANCE] += effect_value; //Chance to rampage - if (new_bonus->PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] < base2) - new_bonus->PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = base2; //Damage modifer - take highest + if (new_bonus->PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] < limit_value) + new_bonus->PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = limit_value; //Damage modifer - take highest break; } case SE_PC_Pet_AE_Rampage: { new_bonus->PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_CHANCE] += effect_value; //Chance to rampage - if (new_bonus->PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] < base2) - new_bonus->PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = base2; //Damage modifer - take highest + if (new_bonus->PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] < limit_value) + new_bonus->PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] = limit_value; //Damage modifer - take highest break; } @@ -3510,10 +3510,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_ReduceTradeskillFail:{ - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; - new_bonus->ReduceTradeskillFail[base2] += effect_value; + new_bonus->ReduceTradeskillFail[limit_value] += effect_value; break; } @@ -3523,11 +3523,11 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne break; case SE_RaiseSkillCap: { - if (base2 > EQ::skills::HIGHEST_SKILL) + if (limit_value > EQ::skills::HIGHEST_SKILL) break; - if (new_bonus->RaiseSkillCap[base2] < effect_value) - new_bonus->RaiseSkillCap[base2] = effect_value; + if (new_bonus->RaiseSkillCap[limit_value] < effect_value) + new_bonus->RaiseSkillCap[limit_value] = effect_value; break; } @@ -3567,48 +3567,48 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_Damage_Taken_Position_Mod: { //Mitigate if damage taken from behind base2 = 0, from front base2 = 1 - if (base2 < 0 || base2 > 2) + if (limit_value < 0 || limit_value > 2) break; if (AdditiveWornBonus) - new_bonus->Damage_Taken_Position_Mod[base2] += effect_value; - else if (effect_value < 0 && new_bonus->Damage_Taken_Position_Mod[base2] > effect_value) - new_bonus->Damage_Taken_Position_Mod[base2] = effect_value; - else if (effect_value > 0 && new_bonus->Damage_Taken_Position_Mod[base2] < effect_value) - new_bonus->Damage_Taken_Position_Mod[base2] = effect_value; + new_bonus->Damage_Taken_Position_Mod[limit_value] += effect_value; + else if (effect_value < 0 && new_bonus->Damage_Taken_Position_Mod[limit_value] > effect_value) + new_bonus->Damage_Taken_Position_Mod[limit_value] = effect_value; + else if (effect_value > 0 && new_bonus->Damage_Taken_Position_Mod[limit_value] < effect_value) + new_bonus->Damage_Taken_Position_Mod[limit_value] = effect_value; break; } case SE_Melee_Damage_Position_Mod: { //Increase damage by percent from behind base2 = 0, from front base2 = 1 - if (base2 < 0 || base2 > 2) + if (limit_value < 0 || limit_value > 2) break; if (AdditiveWornBonus) - new_bonus->Melee_Damage_Position_Mod[base2] += effect_value; - else if (effect_value < 0 && new_bonus->Melee_Damage_Position_Mod[base2] > effect_value) - new_bonus->Melee_Damage_Position_Mod[base2] = effect_value; - else if (effect_value > 0 && new_bonus->Melee_Damage_Position_Mod[base2] < effect_value) - new_bonus->Melee_Damage_Position_Mod[base2] = effect_value; + new_bonus->Melee_Damage_Position_Mod[limit_value] += effect_value; + else if (effect_value < 0 && new_bonus->Melee_Damage_Position_Mod[limit_value] > effect_value) + new_bonus->Melee_Damage_Position_Mod[limit_value] = effect_value; + else if (effect_value > 0 && new_bonus->Melee_Damage_Position_Mod[limit_value] < effect_value) + new_bonus->Melee_Damage_Position_Mod[limit_value] = effect_value; break; } case SE_Damage_Taken_Position_Amt: { //Mitigate if damage taken from behind base2 = 0, from front base2 = 1 - if (base2 < 0 || base2 > 2) + if (limit_value < 0 || limit_value > 2) break; - new_bonus->Damage_Taken_Position_Amt[base2] += effect_value; + new_bonus->Damage_Taken_Position_Amt[limit_value] += effect_value; break; } case SE_Melee_Damage_Position_Amt: { //Mitigate if damage taken from behind base2 = 0, from front base2 = 1 - if (base2 < 0 || base2 > 2) + if (limit_value < 0 || limit_value > 2) break; - new_bonus->Melee_Damage_Position_Amt[base2] += effect_value; + new_bonus->Melee_Damage_Position_Amt[limit_value] += effect_value; break; } @@ -3666,10 +3666,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_Weapon_Stance: { if (IsValidSpell(effect_value)) { //base1 is the spell_id of buff - if (base2 <= WEAPON_STANCE_TYPE_MAX) { //0=2H, 1=Shield, 2=DW - if (IsValidSpell(new_bonus->WeaponStance[base2])) { //Check if we already a spell_id saved for this effect - if (spells[new_bonus->WeaponStance[base2]].rank < spells[effect_value].rank) { //If so, check if any new spellids with higher rank exist (live spells for this are ranked). - new_bonus->WeaponStance[base2] = effect_value; //Overwrite with new effect + if (limit_value <= WEAPON_STANCE_TYPE_MAX) { //0=2H, 1=Shield, 2=DW + if (IsValidSpell(new_bonus->WeaponStance[limit_value])) { //Check if we already a spell_id saved for this effect + if (spells[new_bonus->WeaponStance[limit_value]].rank < spells[effect_value].rank) { //If so, check if any new spellids with higher rank exist (live spells for this are ranked). + new_bonus->WeaponStance[limit_value] = effect_value; //Overwrite with new effect SetWeaponStanceEnabled(true); if (WornType) { @@ -3681,7 +3681,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne } } else { - new_bonus->WeaponStance[base2] = effect_value; //If no prior effect exists, then apply + new_bonus->WeaponStance[limit_value] = effect_value; //If no prior effect exists, then apply SetWeaponStanceEnabled(true); if (WornType) { @@ -3709,7 +3709,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne //Non-Focused Effect to modify incoming spell damage by resist type. case SE_FcSpellVulnerability: - ModVulnerability(base2, effect_value); + ModVulnerability(limit_value, effect_value); break; } } @@ -3985,7 +3985,7 @@ uint8 Mob::IsFocusEffect(uint16 spell_id,int effect_index, bool AA,uint32 aa_eff uint16 effect = 0; if (!AA) - effect = spells[spell_id].effectid[effect_index]; + effect = spells[spell_id].effect_id[effect_index]; else effect = aa_effect; @@ -4097,10 +4097,10 @@ void Mob::NegateSpellEffectBonuses(uint16 spell_id) bool negate_aabonus = false; bool negate_itembonus = false; - if (spells[spell_id].effectid[i] == SE_NegateSpellEffect) { + if (spells[spell_id].effect_id[i] == SE_NegateSpellEffect) { //Set negate types - switch (spells[spell_id].base[i]) + switch (spells[spell_id].base_value[i]) { case NEGATE_SPA_ALL_BONUSES: negate_spellbonus = true; @@ -4139,7 +4139,7 @@ void Mob::NegateSpellEffectBonuses(uint16 spell_id) //Negate focus effects for (int e = 0; e < HIGHEST_FOCUS + 1; e++) { - if (spellbonuses.FocusEffects[e] == spells[spell_id].base2[i]) + if (spellbonuses.FocusEffects[e] == spells[spell_id].limit_value[i]) { if (negate_spellbonus) { spellbonuses.FocusEffects[e] = effect_value; } continue; @@ -4147,7 +4147,7 @@ void Mob::NegateSpellEffectBonuses(uint16 spell_id) } //Negate bonuses - switch (spells[spell_id].base2[i]) + switch (spells[spell_id].limit_value[i]) { case SE_CurrentHP: if (negate_spellbonus) { spellbonuses.HPRegen = effect_value; } diff --git a/zone/bot.cpp b/zone/bot.cpp index 9f9f723b9..e86bf579a 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -257,30 +257,30 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to } for (int x1 = 0; x1 < EFFECT_COUNT; x1++) { - switch (spell.effectid[x1]) { + switch (spell.effect_id[x1]) { case SE_IllusionCopy: case SE_Illusion: { - if (spell.base[x1] == -1) { + if (spell.base_value[x1] == -1) { if (gender == 1) gender = 0; else if (gender == 0) gender = 1; SendIllusionPacket(GetRace(), gender, 0xFF, 0xFF); } - else if (spell.base[x1] == -2) // WTF IS THIS + else if (spell.base_value[x1] == -2) // WTF IS THIS { if (GetRace() == 128 || GetRace() == 130 || GetRace() <= 12) - SendIllusionPacket(GetRace(), GetGender(), spell.base2[x1], spell.max[x1]); + SendIllusionPacket(GetRace(), GetGender(), spell.limit_value[x1], spell.max_value[x1]); } - else if (spell.max[x1] > 0) + else if (spell.max_value[x1] > 0) { - SendIllusionPacket(spell.base[x1], 0xFF, spell.base2[x1], spell.max[x1]); + SendIllusionPacket(spell.base_value[x1], 0xFF, spell.limit_value[x1], spell.max_value[x1]); } else { - SendIllusionPacket(spell.base[x1], 0xFF, 0xFF, 0xFF); + SendIllusionPacket(spell.base_value[x1], 0xFF, 0xFF, 0xFF); } - switch (spell.base[x1]) { + switch (spell.base_value[x1]) { case OGRE: SendAppearancePacket(AT_Size, 9); break; @@ -367,17 +367,17 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to case SE_AddMeleeProc: case SE_WeaponProc: { - AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid, buffs[j1].casterlevel); + AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, buffs[j1].casterlevel); break; } case SE_DefensiveProc: { - AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); + AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid); break; } case SE_RangedProc: { - AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); + AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid); break; } } @@ -4937,8 +4937,8 @@ int32 Bot::CalcBotAAFocus(focusType type, uint32 aa_ID, uint32 points, uint16 sp bool LimitSpellSkill = false; bool SpellSkill_Found = false; uint32 effect = 0; - int32 base1 = 0; - int32 base2 = 0; + int32 base_value = 0; + int32 limit_value = 0; uint32 slot = 0; bool LimitFound = false; int FocusCount = 0; @@ -4953,8 +4953,8 @@ int32 Bot::CalcBotAAFocus(focusType type, uint32 aa_ID, uint32 points, uint16 sp for(auto &eff : rank->effects) { effect = eff.effect_id; - base1 = eff.base1; - base2 = eff.base2; + base_value = eff.base_value; + limit_value = eff.limit_value; slot = eff.slot; //AA Foci's can contain multiple focus effects within the same AA. @@ -4979,22 +4979,22 @@ int32 Bot::CalcBotAAFocus(focusType type, uint32 aa_ID, uint32 points, uint16 sp case SE_Blank: break; case SE_LimitResist: - if(base1) { - if(spell.resisttype != base1) + if(base_value) { + if(spell.resist_type != base_value) LimitFound = true; } break; case SE_LimitInstant: - if(spell.buffduration) + if(spell.buff_duration) LimitFound = true; break; case SE_LimitMaxLevel: spell_level = spell.classes[(GetClass() % 17) - 1]; - lvldiff = spell_level - base1; + lvldiff = spell_level - base_value; //every level over cap reduces the effect by base2 percent unless from a clicky when ItemCastsUseFocus is true if(lvldiff > 0 && (spell_level <= RuleI(Character, MaxLevel) || RuleB(Character, ItemCastsUseFocus) == false)) { - if(base2 > 0) { - lvlModifier -= (base2 * lvldiff); + if(limit_value > 0) { + lvlModifier -= (limit_value * lvldiff); if(lvlModifier < 1) LimitFound = true; } @@ -5003,37 +5003,37 @@ int32 Bot::CalcBotAAFocus(focusType type, uint32 aa_ID, uint32 points, uint16 sp } break; case SE_LimitMinLevel: - if((spell.classes[(GetClass() % 17) - 1]) < base1) + if((spell.classes[(GetClass() % 17) - 1]) < base_value) LimitFound = true; break; case SE_LimitCastTimeMin: - if (spell.cast_time < base1) + if (spell.cast_time < base_value) LimitFound = true; break; case SE_LimitSpell: - if(base1 < 0) { - if (spell_id == (base1*-1)) + if(base_value < 0) { + if (spell_id == (base_value*-1)) LimitFound = true; } else { - if (spell_id != base1) + if (spell_id != base_value) LimitFound = true; } break; case SE_LimitMinDur: - if (base1 > CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration)) + if (base_value > CalcBuffDuration_formula(GetLevel(), spell.buff_duration_formula, spell.buff_duration)) LimitFound = true; break; case SE_LimitEffect: - if(base1 < 0) { - if(IsEffectInSpell(spell_id,(base1*-1))) + if(base_value < 0) { + if(IsEffectInSpell(spell_id,(base_value*-1))) LimitFound = true; } else { - if(!IsEffectInSpell(spell_id,base1)) + if(!IsEffectInSpell(spell_id,base_value)) LimitFound = true; } break; case SE_LimitSpellType: - switch(base1) { + switch(base_value) { case 0: if (!IsDetrimentalSpell(spell_id)) LimitFound = true; @@ -5046,110 +5046,110 @@ int32 Bot::CalcBotAAFocus(focusType type, uint32 aa_ID, uint32 points, uint16 sp break; case SE_LimitManaMin: - if(spell.mana < base1) + if(spell.mana < base_value) LimitFound = true; break; case SE_LimitTarget: - if(base1 < 0) { - if(-base1 == spell.targettype) + if(base_value < 0) { + if(-base_value == spell.target_type) LimitFound = true; } else { - if(base1 != spell.targettype) + if(base_value != spell.target_type) LimitFound = true; } break; case SE_LimitCombatSkills: - if((base1 == 1 && !IsDiscipline(spell_id)) || (base1 == 0 && IsDiscipline(spell_id))) + if((base_value == 1 && !IsDiscipline(spell_id)) || (base_value == 0 && IsDiscipline(spell_id))) LimitFound = true; break; case SE_LimitSpellGroup: - if((base1 > 0 && base1 != spell.spellgroup) || (base1 < 0 && base1 == spell.spellgroup)) + if((base_value > 0 && base_value != spell.spell_group) || (base_value < 0 && base_value == spell.spell_group)) LimitFound = true; break; case SE_LimitCastingSkill: LimitSpellSkill = true; - if(base1 == spell.skill) + if(base_value == spell.skill) SpellSkill_Found = true; break; case SE_LimitClass: //Do not use this limit more then once per spell. If multiple class, treat value like items would. - if (!PassLimitClass(base1, GetClass())) + if (!PassLimitClass(base_value, GetClass())) LimitFound = true; break; //Handle Focus Effects case SE_ImprovedDamage: - if (type == focusImprovedDamage && base1 > value) - value = base1; + if (type == focusImprovedDamage && base_value > value) + value = base_value; break; case SE_ImprovedDamage2: - if (type == focusImprovedDamage2 && base1 > value) - value = base1; + if (type == focusImprovedDamage2 && base_value > value) + value = base_value; break; case SE_ImprovedHeal: - if (type == focusImprovedHeal && base1 > value) - value = base1; + if (type == focusImprovedHeal && base_value > value) + value = base_value; break; case SE_ReduceManaCost: if (type == focusManaCost) - value = base1; + value = base_value; break; case SE_IncreaseSpellHaste: - if (type == focusSpellHaste && base1 > value) - value = base1; + if (type == focusSpellHaste && base_value > value) + value = base_value; break; case SE_IncreaseSpellDuration: - if (type == focusSpellDuration && base1 > value) - value = base1; + if (type == focusSpellDuration && base_value > value) + value = base_value; break; case SE_SpellDurationIncByTic: - if (type == focusSpellDurByTic && base1 > value) - value = base1; + if (type == focusSpellDurByTic && base_value > value) + value = base_value; break; case SE_SwarmPetDuration: - if (type == focusSwarmPetDuration && base1 > value) - value = base1; + if (type == focusSwarmPetDuration && base_value > value) + value = base_value; break; case SE_IncreaseRange: - if (type == focusRange && base1 > value) - value = base1; + if (type == focusRange && base_value > value) + value = base_value; break; case SE_ReduceReagentCost: - if (type == focusReagentCost && base1 > value) - value = base1; + if (type == focusReagentCost && base_value > value) + value = base_value; break; case SE_PetPowerIncrease: - if (type == focusPetPower && base1 > value) - value = base1; + if (type == focusPetPower && base_value > value) + value = base_value; break; case SE_SpellResistReduction: - if (type == focusResistRate && base1 > value) - value = base1; + if (type == focusResistRate && base_value > value) + value = base_value; break; case SE_SpellHateMod: if (type == focusSpellHateMod) { if(value != 0) { if(value > 0) { - if(base1 > value) - value = base1; + if(base_value > value) + value = base_value; } else { - if(base1 < value) - value = base1; + if(base_value < value) + value = base_value; } } else - value = base1; + value = base_value; } break; case SE_ReduceReuseTimer: { if(type == focusReduceRecastTime) - value = (base1 / 1000); + value = (base_value / 1000); break; } case SE_TriggerOnCast: { if(type == focusTriggerOnCast) { - if(zone->random.Int(0, 100) <= base1) - value = base2; + if(zone->random.Int(0, 100) <= base_value) + value = limit_value; else { value = 0; LimitFound = true; @@ -5159,19 +5159,19 @@ int32 Bot::CalcBotAAFocus(focusType type, uint32 aa_ID, uint32 points, uint16 sp } case SE_FcSpellVulnerability: { if(type == focusSpellVulnerability) - value = base1; + value = base_value; break; } case SE_BlockNextSpellFocus: { if(type == focusBlockNextSpell) { - if(zone->random.Int(1, 100) <= base1) + if(zone->random.Int(1, 100) <= base_value) value = 1; } break; } case SE_FcTwincast: { if(type == focusTwincast) - value = base1; + value = base_value; break; } //case SE_SympatheticProc: @@ -5193,57 +5193,57 @@ int32 Bot::CalcBotAAFocus(focusType type, uint32 aa_ID, uint32 points, uint16 sp //} case SE_FcDamageAmt: { if(type == focusFcDamageAmt) - value = base1; + value = base_value; break; } case SE_FcDamageAmt2: { if(type == focusFcDamageAmt2) - value = base1; + value = base_value; break; } case SE_FcDamageAmtCrit: { if(type == focusFcDamageAmtCrit) - value = base1; + value = base_value; break; } case SE_FcDamageAmtIncoming: { if(type == focusFcDamageAmtIncoming) - value = base1; + value = base_value; break; } case SE_FcHealAmtIncoming: if(type == focusFcHealAmtIncoming) - value = base1; + value = base_value; break; case SE_FcHealPctCritIncoming: if (type == focusFcHealPctCritIncoming) - value = base1; + value = base_value; break; case SE_FcHealAmtCrit: if(type == focusFcHealAmtCrit) - value = base1; + value = base_value; break; case SE_FcHealAmt: if(type == focusFcHealAmt) - value = base1; + value = base_value; break; case SE_FcHealPctIncoming: if(type == focusFcHealPctIncoming) - value = base1; + value = base_value; break; case SE_FcBaseEffects: { if (type == focusFcBaseEffects) - value = base1; + value = base_value; break; } case SE_FcDamagePctCrit: { if(type == focusFcDamagePctCrit) - value = base1; + value = base_value; break; } case SE_FcIncreaseNumHits: { if(type == focusIncreaseNumHits) - value = base1; + value = base_value; break; } @@ -5375,7 +5375,7 @@ int32 Bot::GetBotFocusEffect(focusType bottype, uint16 spell_id) { realTotal2 = CalcBotFocusEffect(bottype, focusspell_tracker, spell_id); // For effects like gift of mana that only fire once, save the spellid into an array that consists of all available buff slots. - if(buff_tracker >= 0 && buffs[buff_tracker].numhits > 0) + if(buff_tracker >= 0 && buffs[buff_tracker].hit_number > 0) m_spellHitsLeft[buff_tracker] = focusspell_tracker; } @@ -5432,18 +5432,18 @@ int32 Bot::CalcBotFocusEffect(focusType bottype, uint16 focus_id, uint16 spell_i bool LimitSpellSkill = false; bool SpellSkill_Found = false; for (int i = 0; i < EFFECT_COUNT; i++) { - switch (focus_spell.effectid[i]) { + switch (focus_spell.effect_id[i]) { case SE_Blank: break; case SE_LimitResist:{ - if(focus_spell.base[i]) { - if(spell.resisttype != focus_spell.base[i]) + if(focus_spell.base_value[i]) { + if(spell.resist_type != focus_spell.base_value[i]) return 0; } break; } case SE_LimitInstant: { - if(spell.buffduration) + if(spell.buff_duration) return 0; break; } @@ -5451,10 +5451,10 @@ int32 Bot::CalcBotFocusEffect(focusType bottype, uint16 focus_id, uint16 spell_i if (IsNPC()) break; spell_level = spell.classes[(GetClass() % 17) - 1]; - lvldiff = (spell_level - focus_spell.base[i]); + lvldiff = (spell_level - focus_spell.base_value[i]); if(lvldiff > 0 && (spell_level <= RuleI(Character, MaxLevel) || RuleB(Character, ItemCastsUseFocus) == false)) { - if(focus_spell.base2[i] > 0) { - lvlModifier -= (focus_spell.base2[i] * lvldiff); + if(focus_spell.limit_value[i] > 0) { + lvlModifier -= (focus_spell.limit_value[i] * lvldiff); if(lvlModifier < 1) return 0; } @@ -5466,44 +5466,44 @@ int32 Bot::CalcBotFocusEffect(focusType bottype, uint16 focus_id, uint16 spell_i case SE_LimitMinLevel: if (IsNPC()) break; - if (spell.classes[(GetClass() % 17) - 1] < focus_spell.base[i]) + if (spell.classes[(GetClass() % 17) - 1] < focus_spell.base_value[i]) return 0; break; case SE_LimitCastTimeMin: - if (spells[spell_id].cast_time < (uint32)focus_spell.base[i]) + if (spells[spell_id].cast_time < (uint32)focus_spell.base_value[i]) return 0; break; case SE_LimitSpell: - if(focus_spell.base[i] < 0) { - if (spell_id == (focus_spell.base[i] * -1)) + if(focus_spell.base_value[i] < 0) { + if (spell_id == (focus_spell.base_value[i] * -1)) return 0; } else { - if (spell_id != focus_spell.base[i]) + if (spell_id != focus_spell.base_value[i]) return 0; } break; case SE_LimitMinDur: - if (focus_spell.base[i] > CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration)) + if (focus_spell.base_value[i] > CalcBuffDuration_formula(GetLevel(), spell.buff_duration_formula, spell.buff_duration)) return 0; break; case SE_LimitEffect: - if(focus_spell.base[i] < 0) { - if(IsEffectInSpell(spell_id,focus_spell.base[i])) + if(focus_spell.base_value[i] < 0) { + if(IsEffectInSpell(spell_id,focus_spell.base_value[i])) return 0; } else { - if(focus_spell.base[i] == SE_SummonPet) { + if(focus_spell.base_value[i] == SE_SummonPet) { if(!IsEffectInSpell(spell_id, SE_SummonPet) && !IsEffectInSpell(spell_id, SE_NecPet) && !IsEffectInSpell(spell_id, SE_SummonBSTPet)) { return 0; } - } else if(!IsEffectInSpell(spell_id,focus_spell.base[i])) + } else if(!IsEffectInSpell(spell_id,focus_spell.base_value[i])) return 0; } break; case SE_LimitSpellType: - switch(focus_spell.base[i]) { + switch(focus_spell.base_value[i]) { case 0: if (!IsDetrimentalSpell(spell_id)) return 0; @@ -5513,153 +5513,153 @@ int32 Bot::CalcBotFocusEffect(focusType bottype, uint16 focus_id, uint16 spell_i return 0; break; default: - LogInfo("CalcFocusEffect: unknown limit spelltype [{}]", focus_spell.base[i]); + LogInfo("CalcFocusEffect: unknown limit spelltype [{}]", focus_spell.base_value[i]); } break; case SE_LimitManaMin: - if(spell.mana < focus_spell.base[i]) + if(spell.mana < focus_spell.base_value[i]) return 0; break; case SE_LimitTarget: - if((focus_spell.base[i] < 0) && -focus_spell.base[i] == spell.targettype) + if((focus_spell.base_value[i] < 0) && -focus_spell.base_value[i] == spell.target_type) return 0; - else if (focus_spell.base[i] > 0 && focus_spell.base[i] != spell.targettype) + else if (focus_spell.base_value[i] > 0 && focus_spell.base_value[i] != spell.target_type) return 0; break; case SE_LimitCombatSkills: - if(focus_spell.base[i] == 1 && !IsDiscipline(spell_id)) + if(focus_spell.base_value[i] == 1 && !IsDiscipline(spell_id)) return 0; - else if(focus_spell.base[i] == 0 && IsDiscipline(spell_id)) + else if(focus_spell.base_value[i] == 0 && IsDiscipline(spell_id)) return 0; break; case SE_LimitSpellGroup: - if(focus_spell.base[i] > 0 && focus_spell.base[i] != spell.spellgroup) + if(focus_spell.base_value[i] > 0 && focus_spell.base_value[i] != spell.spell_group) return 0; - else if(focus_spell.base[i] < 0 && focus_spell.base[i] == spell.spellgroup) + else if(focus_spell.base_value[i] < 0 && focus_spell.base_value[i] == spell.spell_group) return 0; break; case SE_LimitCastingSkill: LimitSpellSkill = true; - if(focus_spell.base[i] == spell.skill) + if(focus_spell.base_value[i] == spell.skill) SpellSkill_Found = true; break; case SE_LimitClass: - if (!PassLimitClass(focus_spell.base[i], GetClass())) + if (!PassLimitClass(focus_spell.base_value[i], GetClass())) return 0; break; case SE_ImprovedDamage: if (bottype == focusImprovedDamage) { if(best_focus) { - if (focus_spell.base2[i] != 0) - value = focus_spell.base2[i]; + if (focus_spell.limit_value[i] != 0) + value = focus_spell.limit_value[i]; else - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; } - else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) - value = focus_spell.base[i]; + else if (focus_spell.limit_value[i] == 0 || focus_spell.base_value[i] == focus_spell.limit_value[i]) + value = focus_spell.base_value[i]; else - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); + value = zone->random.Int(focus_spell.base_value[i], focus_spell.limit_value[i]); } break; case SE_ImprovedDamage2: if (bottype == focusImprovedDamage2) { if(best_focus) { - if (focus_spell.base2[i] != 0) - value = focus_spell.base2[i]; + if (focus_spell.limit_value[i] != 0) + value = focus_spell.limit_value[i]; else - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; } - else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) - value = focus_spell.base[i]; + else if (focus_spell.limit_value[i] == 0 || focus_spell.base_value[i] == focus_spell.limit_value[i]) + value = focus_spell.base_value[i]; else - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); + value = zone->random.Int(focus_spell.base_value[i], focus_spell.limit_value[i]); } break; case SE_ImprovedHeal: if (bottype == focusImprovedHeal) { if(best_focus) { - if (focus_spell.base2[i] != 0) - value = focus_spell.base2[i]; + if (focus_spell.limit_value[i] != 0) + value = focus_spell.limit_value[i]; else - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; } - else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) - value = focus_spell.base[i]; + else if (focus_spell.limit_value[i] == 0 || focus_spell.base_value[i] == focus_spell.limit_value[i]) + value = focus_spell.base_value[i]; else - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); + value = zone->random.Int(focus_spell.base_value[i], focus_spell.limit_value[i]); } break; case SE_ReduceManaCost: if (bottype == focusManaCost) { if(best_focus) { - if (focus_spell.base2[i] != 0) - value = focus_spell.base2[i]; + if (focus_spell.limit_value[i] != 0) + value = focus_spell.limit_value[i]; else - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; } - else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) - value = focus_spell.base[i]; + else if (focus_spell.limit_value[i] == 0 || focus_spell.base_value[i] == focus_spell.limit_value[i]) + value = focus_spell.base_value[i]; else - value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); + value = zone->random.Int(focus_spell.base_value[i], focus_spell.limit_value[i]); } break; case SE_IncreaseSpellHaste: - if (bottype == focusSpellHaste && focus_spell.base[i] > value) - value = focus_spell.base[i]; + if (bottype == focusSpellHaste && focus_spell.base_value[i] > value) + value = focus_spell.base_value[i]; break; case SE_IncreaseSpellDuration: - if (bottype == focusSpellDuration && focus_spell.base[i] > value) - value = focus_spell.base[i]; + if (bottype == focusSpellDuration && focus_spell.base_value[i] > value) + value = focus_spell.base_value[i]; break; case SE_SpellDurationIncByTic: - if (bottype == focusSpellDurByTic && focus_spell.base[i] > value) - value = focus_spell.base[i]; + if (bottype == focusSpellDurByTic && focus_spell.base_value[i] > value) + value = focus_spell.base_value[i]; break; case SE_SwarmPetDuration: - if (bottype == focusSwarmPetDuration && focus_spell.base[i] > value) - value = focus_spell.base[i]; + if (bottype == focusSwarmPetDuration && focus_spell.base_value[i] > value) + value = focus_spell.base_value[i]; break; case SE_IncreaseRange: - if (bottype == focusRange && focus_spell.base[i] > value) - value = focus_spell.base[i]; + if (bottype == focusRange && focus_spell.base_value[i] > value) + value = focus_spell.base_value[i]; break; case SE_ReduceReagentCost: - if (bottype == focusReagentCost && focus_spell.base[i] > value) - value = focus_spell.base[i]; + if (bottype == focusReagentCost && focus_spell.base_value[i] > value) + value = focus_spell.base_value[i]; break; case SE_PetPowerIncrease: - if (bottype == focusPetPower && focus_spell.base[i] > value) - value = focus_spell.base[i]; + if (bottype == focusPetPower && focus_spell.base_value[i] > value) + value = focus_spell.base_value[i]; break; case SE_SpellResistReduction: - if (bottype == focusResistRate && focus_spell.base[i] > value) - value = focus_spell.base[i]; + if (bottype == focusResistRate && focus_spell.base_value[i] > value) + value = focus_spell.base_value[i]; break; case SE_SpellHateMod: if (bottype == focusSpellHateMod) { if(value != 0) { if(value > 0) { - if(focus_spell.base[i] > value) - value = focus_spell.base[i]; + if(focus_spell.base_value[i] > value) + value = focus_spell.base_value[i]; } else { - if(focus_spell.base[i] < value) - value = focus_spell.base[i]; + if(focus_spell.base_value[i] < value) + value = focus_spell.base_value[i]; } } else - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; } break; case SE_ReduceReuseTimer: { if(bottype == focusReduceRecastTime) - value = (focus_spell.base[i] / 1000); + value = (focus_spell.base_value[i] / 1000); break; } case SE_TriggerOnCast: { if(bottype == focusTriggerOnCast) { - if(zone->random.Int(0, 100) <= focus_spell.base[i]) - value = focus_spell.base2[i]; + if(zone->random.Int(0, 100) <= focus_spell.base_value[i]) + value = focus_spell.limit_value[i]; else value = 0; } @@ -5667,24 +5667,24 @@ int32 Bot::CalcBotFocusEffect(focusType bottype, uint16 focus_id, uint16 spell_i } case SE_FcSpellVulnerability: { if(bottype == focusSpellVulnerability) - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; break; } case SE_BlockNextSpellFocus: { if(bottype == focusBlockNextSpell) { - if(zone->random.Int(1, 100) <= focus_spell.base[i]) + if(zone->random.Int(1, 100) <= focus_spell.base_value[i]) value = 1; } break; } case SE_FcTwincast: { if(bottype == focusTwincast) - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; break; } case SE_SympatheticProc: { if(bottype == focusSympatheticProc) { - float ProcChance = GetSympatheticProcChances(spell_id, focus_spell.base[i]); + float ProcChance = GetSympatheticProcChances(spell_id, focus_spell.base_value[i]); if(zone->random.Real(0, 1) <= ProcChance) value = focus_id; else @@ -5694,59 +5694,59 @@ int32 Bot::CalcBotFocusEffect(focusType bottype, uint16 focus_id, uint16 spell_i } case SE_FcDamageAmt: { if(bottype == focusFcDamageAmt) - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; break; } case SE_FcDamageAmt2: { if(bottype == focusFcDamageAmt2) - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; break; } case SE_FcDamageAmtCrit: { if(bottype == focusFcDamageAmtCrit) - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; break; } case SE_FcHealAmtIncoming: if(bottype == focusFcHealAmtIncoming) - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; break; case SE_FcHealPctCritIncoming: if (bottype == focusFcHealPctCritIncoming) - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; break; case SE_FcHealAmtCrit: if(bottype == focusFcHealAmtCrit) - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; break; case SE_FcHealAmt: if(bottype == focusFcHealAmt) - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; break; case SE_FcHealPctIncoming: if(bottype == focusFcHealPctIncoming) - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; break; case SE_FcBaseEffects: { if (bottype == focusFcBaseEffects) - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; break; } case SE_FcDamagePctCrit: { if(bottype == focusFcDamagePctCrit) - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; break; } case SE_FcIncreaseNumHits: { if(bottype == focusIncreaseNumHits) - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; break; } default: - LogSpells("CalcFocusEffect: unknown effectid [{}]", focus_spell.effectid[i]); + LogSpells("CalcFocusEffect: unknown effectid [{}]", focus_spell.effect_id[i]); break; } } @@ -6673,7 +6673,7 @@ void Bot::SetAttackTimer() { } int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { - if (spells[spell_id].targettype == ST_Self) + if (spells[spell_id].target_type == ST_Self) return value; bool Critical = false; @@ -6757,7 +6757,7 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { value_BaseEffect = (value + (value*GetBotFocusEffect(focusFcBaseEffects, spell_id) / 100)); value = value_BaseEffect; value += int(value_BaseEffect*GetBotFocusEffect(focusImprovedHeal, spell_id) / 100); - if(spells[spell_id].buffduration < 1) { + if(spells[spell_id].buff_duration < 1) { chance += (itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance); chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); if (spellbonuses.CriticalHealDecay) @@ -6798,7 +6798,7 @@ int32 Bot::GetActSpellCasttime(uint16 spell_id, int32 casttime) { auto min_cap = casttime / 2; uint8 botlevel = GetLevel(); uint8 botclass = GetClass(); - if (botlevel >= 51 && casttime >= 3000 && !spells[spell_id].goodEffect && + if (botlevel >= 51 && casttime >= 3000 && !spells[spell_id].good_effect && (botclass == SHADOWKNIGHT || botclass == RANGER || botclass == PALADIN || botclass == BEASTLORD)) { int level_mod = std::min(15, botlevel - 50); cast_reducer += level_mod * 3; @@ -6994,7 +6994,7 @@ int32 Bot::GetActSpellDuration(uint16 spell_id, int32 duration) { float Bot::GetAOERange(uint16 spell_id) { float range; - range = spells[spell_id].aoerange; + range = spells[spell_id].aoe_range; if(range == 0) range = spells[spell_id].range; @@ -7099,7 +7099,7 @@ bool Bot::SpellOnTarget(uint16 spell_id, Mob* spelltar) { return false; if(spelltar) { - if(spelltar->IsBot() && (spells[spell_id].targettype == ST_GroupTeleport)) { + if(spelltar->IsBot() && (spells[spell_id].target_type == ST_GroupTeleport)) { switch(spell_id) { // Paladin case 3577: // Wave of Life @@ -7175,7 +7175,7 @@ bool Bot::SpellOnTarget(uint16 spell_id, Mob* spelltar) { if(spelltar->IsPet()) { for(int i= 0; i < EFFECT_COUNT; ++i) { - if(spells[spell_id].effectid[i] == SE_Illusion) + if(spells[spell_id].effect_id[i] == SE_Illusion) return false; } } @@ -7193,13 +7193,13 @@ bool Bot::IsImmuneToSpell(uint16 spell_id, Mob *caster) { Result = Mob::IsImmuneToSpell(spell_id, caster); if(!Result) { if(caster->IsBot()) { - if(spells[spell_id].targettype == ST_Undead) { + if(spells[spell_id].target_type == ST_Undead) { if((GetBodyType() != BT_SummonedUndead) && (GetBodyType() != BT_Undead) && (GetBodyType() != BT_Vampire)) { LogSpells("Bot's target is not an undead"); return true; } } - if(spells[spell_id].targettype == ST_Summoned) { + if(spells[spell_id].target_type == ST_Summoned) { if((GetBodyType() != BT_SummonedUndead) && (GetBodyType() != BT_Summoned) && (GetBodyType() != BT_Summoned2) && (GetBodyType() != BT_Summoned3)) { LogSpells("Bot's target is not a summoned creature"); return true; @@ -7216,7 +7216,7 @@ bool Bot::IsImmuneToSpell(uint16 spell_id, Mob *caster) { bool Bot::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, EQ::spells::CastingSlot slot) { bool Result = false; - SpellTargetType targetType = spells[spell_id].targettype; + SpellTargetType targetType = spells[spell_id].target_type; if(targetType == ST_GroupClientAndPet) { if((spell_id == 1768 && zone->GetZoneID() == 202) || (!IsDetrimentalSpell(spell_id))) { CastAction = SingleTarget; @@ -7351,13 +7351,13 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQ::spe int spelltype = BotGetSpellType(i); bool spellequal = (j == thespell); bool spelltypeequal = ((spelltype == 2) || (spelltype == 16) || (spelltype == 32)); - bool spelltypetargetequal = ((spelltype == 8) && (spells[thespell].targettype == ST_Self)); + bool spelltypetargetequal = ((spelltype == 8) && (spells[thespell].target_type == ST_Self)); bool spelltypeclassequal = ((spelltype == 1024) && (GetClass() == SHAMAN)); bool slotequal = (slot == EQ::spells::CastingSlot::Item); if(spellequal || slotequal) { if((spelltypeequal || spelltypetargetequal) || spelltypeclassequal || slotequal) { - if(((spells[thespell].effectid[0] == 0) && (spells[thespell].base[0] < 0)) && - (spellTarget->GetHP() < ((spells[thespell].base[0] * (-1)) + 100))) { + if(((spells[thespell].effect_id[0] == 0) && (spells[thespell].base_value[0] < 0)) && + (spellTarget->GetHP() < ((spells[thespell].base_value[0] * (-1)) + 100))) { LogSpells("Bot::DoFinishedSpellSingleTarget - GroupBuffing failure"); return false; } @@ -8040,7 +8040,7 @@ void Bot::DoEnduranceUpkeep() { uint32 buff_count = GetMaxTotalSlots(); for (buffs_i = 0; buffs_i < buff_count; buffs_i++) { if (buffs[buffs_i].spellid != SPELL_UNKNOWN) { - int upkeep = spells[buffs[buffs_i].spellid].EndurUpkeep; + int upkeep = spells[buffs[buffs_i].spellid].endurance_upkeep; if(upkeep > 0) { if(cost_redux > 0) { if(upkeep <= cost_redux) @@ -9523,17 +9523,17 @@ bool Bot::UseDiscipline(uint32 spell_id, uint32 target) { return false; } - if(GetEndurance() > spell.EndurCost) - SetEndurance(GetEndurance() - spell.EndurCost); + if(GetEndurance() > spell.endurance_cost) + SetEndurance(GetEndurance() - spell.endurance_cost); else return false; if(spell.recast_time > 0) { - if(CheckDisciplineRecastTimers(this, spells[spell_id].EndurTimerIndex)) { - if(spells[spell_id].EndurTimerIndex > 0 && spells[spell_id].EndurTimerIndex < MAX_DISCIPLINE_TIMERS) - SetDisciplineRecastTimer(spells[spell_id].EndurTimerIndex, spell.recast_time); + if(CheckDisciplineRecastTimers(this, spells[spell_id].timer_id)) { + if(spells[spell_id].timer_id > 0 && spells[spell_id].timer_id < MAX_DISCIPLINE_TIMERS) + SetDisciplineRecastTimer(spells[spell_id].timer_id, spell.recast_time); } else { - uint32 remain = (GetDisciplineRemainingTime(this, spells[spell_id].EndurTimerIndex) / 1000); + uint32 remain = (GetDisciplineRemainingTime(this, spells[spell_id].timer_id) / 1000); GetOwner()->Message(Chat::White, "%s can use this discipline in %d minutes %d seconds.", GetCleanName(), (remain / 60), (remain % 60)); return false; } diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 1a922379e..bd826078b 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -130,11 +130,11 @@ public: for (int spell_id = 2; spell_id < SPDAT_RECORDS; ++spell_id) { if (spells[spell_id].player_1[0] == '\0') continue; - if (spells[spell_id].targettype != ST_Target && spells[spell_id].CastRestriction != 0) // watch + if (spells[spell_id].target_type != ST_Target && spells[spell_id].cast_restriction != 0) // watch continue; auto target_type = BCEnum::TT_None; - switch (spells[spell_id].targettype) { + switch (spells[spell_id].target_type) { case ST_GroupTeleport: target_type = BCEnum::TT_GroupV1; break; @@ -147,7 +147,7 @@ public: //target_type = BCEnum::TT_AEBard; break; case ST_Target: - switch (spells[spell_id].CastRestriction) { + switch (spells[spell_id].cast_restriction) { case 0: target_type = BCEnum::TT_Single; break; @@ -213,15 +213,15 @@ public: STBaseEntry* entry_prototype = nullptr; while (true) { - switch (spells[spell_id].effectid[EFFECTIDTOINDEX(1)]) { + switch (spells[spell_id].effect_id[EFFECTIDTOINDEX(1)]) { case SE_BindAffinity: entry_prototype = new STBaseEntry(BCEnum::SpT_BindAffinity); break; case SE_Charm: - if (spells[spell_id].SpellAffectIndex != 12) + if (spells[spell_id].spell_affect_index != 12) break; entry_prototype = new STCharmEntry(); - if (spells[spell_id].ResistDiff <= -1000) + if (spells[spell_id].resist_difficulty <= -1000) entry_prototype->SafeCastToCharm()->dire = true; break; case SE_Teleport: @@ -248,11 +248,11 @@ public: } break; case SE_ModelSize: - if (spells[spell_id].base[EFFECTIDTOINDEX(1)] > 100) { + if (spells[spell_id].base_value[EFFECTIDTOINDEX(1)] > 100) { entry_prototype = new STSizeEntry; entry_prototype->SafeCastToSize()->size_type = BCEnum::SzT_Enlarge; } - else if (spells[spell_id].base[EFFECTIDTOINDEX(1)] > 0 && spells[spell_id].base[EFFECTIDTOINDEX(1)] < 100) { + else if (spells[spell_id].base_value[EFFECTIDTOINDEX(1)] > 0 && spells[spell_id].base_value[EFFECTIDTOINDEX(1)] < 100) { entry_prototype = new STSizeEntry; entry_prototype->SafeCastToSize()->size_type = BCEnum::SzT_Reduce; } @@ -261,42 +261,42 @@ public: entry_prototype = new STBaseEntry(BCEnum::SpT_Identify); break; case SE_Invisibility: - if (spells[spell_id].SpellAffectIndex != 9) + if (spells[spell_id].spell_affect_index != 9) break; entry_prototype = new STInvisibilityEntry; entry_prototype->SafeCastToInvisibility()->invis_type = BCEnum::IT_Living; break; case SE_SeeInvis: - if (spells[spell_id].SpellAffectIndex != 5) + if (spells[spell_id].spell_affect_index != 5) break; entry_prototype = new STInvisibilityEntry; entry_prototype->SafeCastToInvisibility()->invis_type = BCEnum::IT_See; break; case SE_InvisVsUndead: - if (spells[spell_id].SpellAffectIndex != 9) + if (spells[spell_id].spell_affect_index != 9) break; entry_prototype = new STInvisibilityEntry; entry_prototype->SafeCastToInvisibility()->invis_type = BCEnum::IT_Undead; break; case SE_InvisVsAnimals: - if (spells[spell_id].SpellAffectIndex != 9) + if (spells[spell_id].spell_affect_index != 9) break; entry_prototype = new STInvisibilityEntry; entry_prototype->SafeCastToInvisibility()->invis_type = BCEnum::IT_Animal; break; case SE_Mez: - if (spells[spell_id].SpellAffectIndex != 12) + if (spells[spell_id].spell_affect_index != 12) break; entry_prototype = new STBaseEntry(BCEnum::SpT_Mesmerize); break; case SE_Revive: - if (spells[spell_id].SpellAffectIndex != 1) + if (spells[spell_id].spell_affect_index != 1) break; entry_prototype = new STResurrectEntry(); entry_prototype->SafeCastToResurrect()->aoe = BCSpells::IsCasterCentered(target_type); break; case SE_Rune: - if (spells[spell_id].SpellAffectIndex != 2) + if (spells[spell_id].spell_affect_index != 2) break; entry_prototype = new STBaseEntry(BCEnum::SpT_Rune); break; @@ -312,7 +312,7 @@ public: if (entry_prototype) break; - switch (spells[spell_id].effectid[EFFECTIDTOINDEX(2)]) { + switch (spells[spell_id].effect_id[EFFECTIDTOINDEX(2)]) { case SE_Succor: entry_prototype = new STEscapeEntry; std::string is_lesser = spells[spell_id].name; @@ -323,7 +323,7 @@ public: if (entry_prototype) break; - switch (spells[spell_id].effectid[EFFECTIDTOINDEX(3)]) { + switch (spells[spell_id].effect_id[EFFECTIDTOINDEX(3)]) { case SE_Lull: entry_prototype = new STBaseEntry(BCEnum::SpT_Lull); break; @@ -336,8 +336,8 @@ public: if (entry_prototype) break; - while (spells[spell_id].typedescnum == 27) { - if (!spells[spell_id].goodEffect) + while (spells[spell_id].type_description_id == 27) { + if (!spells[spell_id].good_effect) break; if (spells[spell_id].skill != EQ::skills::SkillOffense && spells[spell_id].skill != EQ::skills::SkillDefense) break; @@ -353,38 +353,38 @@ public: if (entry_prototype) break; - switch (spells[spell_id].SpellAffectIndex) { + switch (spells[spell_id].spell_affect_index) { case 1: { bool valid_spell = false; entry_prototype = new STCureEntry; for (int i = EffectIDFirst; i <= EffectIDLast; ++i) { int effect_index = EFFECTIDTOINDEX(i); - if (spells[spell_id].effectid[effect_index] != SE_Blind && spells[spell_id].base[effect_index] >= 0) + if (spells[spell_id].effect_id[effect_index] != SE_Blind && spells[spell_id].base_value[effect_index] >= 0) continue; - else if (spells[spell_id].effectid[effect_index] == SE_Blind && !spells[spell_id].goodEffect) + else if (spells[spell_id].effect_id[effect_index] == SE_Blind && !spells[spell_id].good_effect) continue; - switch (spells[spell_id].effectid[effect_index]) { + switch (spells[spell_id].effect_id[effect_index]) { case SE_Blind: - entry_prototype->SafeCastToCure()->cure_value[AILMENTIDTOINDEX(BCEnum::AT_Blindness)] += spells[spell_id].base[effect_index]; + entry_prototype->SafeCastToCure()->cure_value[AILMENTIDTOINDEX(BCEnum::AT_Blindness)] += spells[spell_id].base_value[effect_index]; break; case SE_DiseaseCounter: - entry_prototype->SafeCastToCure()->cure_value[AILMENTIDTOINDEX(BCEnum::AT_Disease)] += spells[spell_id].base[effect_index]; + entry_prototype->SafeCastToCure()->cure_value[AILMENTIDTOINDEX(BCEnum::AT_Disease)] += spells[spell_id].base_value[effect_index]; break; case SE_PoisonCounter: - entry_prototype->SafeCastToCure()->cure_value[AILMENTIDTOINDEX(BCEnum::AT_Poison)] += spells[spell_id].base[effect_index]; + entry_prototype->SafeCastToCure()->cure_value[AILMENTIDTOINDEX(BCEnum::AT_Poison)] += spells[spell_id].base_value[effect_index]; break; case SE_CurseCounter: - entry_prototype->SafeCastToCure()->cure_value[AILMENTIDTOINDEX(BCEnum::AT_Curse)] += spells[spell_id].base[effect_index]; + entry_prototype->SafeCastToCure()->cure_value[AILMENTIDTOINDEX(BCEnum::AT_Curse)] += spells[spell_id].base_value[effect_index]; break; case SE_CorruptionCounter: - entry_prototype->SafeCastToCure()->cure_value[AILMENTIDTOINDEX(BCEnum::AT_Corruption)] += spells[spell_id].base[effect_index]; + entry_prototype->SafeCastToCure()->cure_value[AILMENTIDTOINDEX(BCEnum::AT_Corruption)] += spells[spell_id].base_value[effect_index]; break; default: continue; } - entry_prototype->SafeCastToCure()->cure_total += spells[spell_id].base[effect_index]; + entry_prototype->SafeCastToCure()->cure_total += spells[spell_id].base_value[effect_index]; valid_spell = true; } if (!valid_spell) { @@ -400,32 +400,32 @@ public: for (int i = EffectIDFirst; i <= EffectIDLast; ++i) { int effect_index = EFFECTIDTOINDEX(i); - if (spells[spell_id].base[effect_index] <= 0) + if (spells[spell_id].base_value[effect_index] <= 0) continue; - switch (spells[spell_id].effectid[effect_index]) { + switch (spells[spell_id].effect_id[effect_index]) { case SE_ResistFire: - entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(BCEnum::RT_Fire)] += spells[spell_id].base[effect_index]; + entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(BCEnum::RT_Fire)] += spells[spell_id].base_value[effect_index]; break; case SE_ResistCold: - entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(BCEnum::RT_Cold)] += spells[spell_id].base[effect_index]; + entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(BCEnum::RT_Cold)] += spells[spell_id].base_value[effect_index]; break; case SE_ResistPoison: - entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(BCEnum::RT_Poison)] += spells[spell_id].base[effect_index]; + entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(BCEnum::RT_Poison)] += spells[spell_id].base_value[effect_index]; break; case SE_ResistDisease: - entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(BCEnum::RT_Disease)] += spells[spell_id].base[effect_index]; + entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(BCEnum::RT_Disease)] += spells[spell_id].base_value[effect_index]; break; case SE_ResistMagic: - entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(BCEnum::RT_Magic)] += spells[spell_id].base[effect_index]; + entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(BCEnum::RT_Magic)] += spells[spell_id].base_value[effect_index]; break; case SE_ResistCorruption: - entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(BCEnum::RT_Corruption)] += spells[spell_id].base[effect_index]; + entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(BCEnum::RT_Corruption)] += spells[spell_id].base_value[effect_index]; break; default: continue; } - entry_prototype->SafeCastToResistance()->resist_total += spells[spell_id].base[effect_index]; + entry_prototype->SafeCastToResistance()->resist_total += spells[spell_id].base_value[effect_index]; valid_spell = true; } if (!valid_spell) { @@ -437,7 +437,7 @@ public: } case 7: case 10: - if (spells[spell_id].effectdescnum != 65) + if (spells[spell_id].effect_description_id != 65) break; if (IsEffectInSpell(spell_id, SE_NegateIfCombat)) break; @@ -622,18 +622,18 @@ private: if (RuleI(Bots, CommandSpellRank) == 1) { spells_list->sort([](STBaseEntry* l, STBaseEntry* r) { - if (spells[l->spell_id].spellgroup < spells[r->spell_id].spellgroup) + if (spells[l->spell_id].spell_group < spells[r->spell_id].spell_group) return true; - if (spells[l->spell_id].spellgroup == spells[r->spell_id].spellgroup && l->caster_class < r->caster_class) + if (spells[l->spell_id].spell_group == spells[r->spell_id].spell_group && l->caster_class < r->caster_class) return true; - if (spells[l->spell_id].spellgroup == spells[r->spell_id].spellgroup && l->caster_class == r->caster_class && spells[l->spell_id].rank < spells[r->spell_id].rank) + if (spells[l->spell_id].spell_group == spells[r->spell_id].spell_group && l->caster_class == r->caster_class && spells[l->spell_id].rank < spells[r->spell_id].rank) return true; return false; }); spells_list->unique([removed_spells_list](STBaseEntry* l, STBaseEntry* r) { std::string r_name = spells[r->spell_id].name; - if (spells[l->spell_id].spellgroup == spells[r->spell_id].spellgroup && l->caster_class == r->caster_class && spells[l->spell_id].rank < spells[r->spell_id].rank) { + if (spells[l->spell_id].spell_group == spells[r->spell_id].spell_group && l->caster_class == r->caster_class && spells[l->spell_id].rank < spells[r->spell_id].rank) { removed_spells_list->push_back(r); return true; } @@ -673,18 +673,18 @@ private: // needs rework if (RuleI(Bots, CommandSpellRank) == 2 || RuleI(Bots, CommandSpellRank) == 3) { spells_list->sort([](STBaseEntry* l, STBaseEntry* r) { - if (spells[l->spell_id].spellgroup < spells[r->spell_id].spellgroup) + if (spells[l->spell_id].spell_group < spells[r->spell_id].spell_group) return true; - if (spells[l->spell_id].spellgroup == spells[r->spell_id].spellgroup && l->caster_class < r->caster_class) + if (spells[l->spell_id].spell_group == spells[r->spell_id].spell_group && l->caster_class < r->caster_class) return true; - if (spells[l->spell_id].spellgroup == spells[r->spell_id].spellgroup && l->caster_class == r->caster_class && spells[l->spell_id].rank > spells[r->spell_id].rank) + if (spells[l->spell_id].spell_group == spells[r->spell_id].spell_group && l->caster_class == r->caster_class && spells[l->spell_id].rank > spells[r->spell_id].rank) return true; return false; }); spells_list->unique([removed_spells_list](STBaseEntry* l, STBaseEntry* r) { std::string l_name = spells[l->spell_id].name; - if (spells[l->spell_id].spellgroup == spells[r->spell_id].spellgroup && l->caster_class == r->caster_class && spells[l->spell_id].rank > spells[r->spell_id].rank) { + if (spells[l->spell_id].spell_group == spells[r->spell_id].spell_group && l->caster_class == r->caster_class && spells[l->spell_id].rank > spells[r->spell_id].rank) { removed_spells_list->push_back(r); return true; } @@ -786,15 +786,15 @@ private: continue; case BCEnum::SpT_Charm: spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) { - if (LT_SPELLS(l, r, ResistDiff)) + if (LT_SPELLS(l, r, resist_difficulty)) return true; - if (EQ_SPELLS(l, r, ResistDiff) && LT_STBASE(l, r, target_type)) + if (EQ_SPELLS(l, r, resist_difficulty) && LT_STBASE(l, r, target_type)) return true; - if (EQ_SPELLS(l, r, ResistDiff) && EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, max, 1)) + if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, max_value, 1)) return true; - if (EQ_SPELLS(l, r, ResistDiff) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max, 1) && LT_STBASE(l, r, spell_level)) + if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max_value, 1) && LT_STBASE(l, r, spell_level)) return true; - if (EQ_SPELLS(l, r, ResistDiff) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max, 1) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) + if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max_value, 1) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) return true; return false; @@ -883,11 +883,11 @@ private: spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) { if (LT_STBASE(l, r, target_type)) return true; - if (EQ_STBASE(l, r, target_type) && LT_SPELLS(l, r, zonetype)) + if (EQ_STBASE(l, r, target_type) && LT_SPELLS(l, r, zone_type)) return true; - if (EQ_STBASE(l, r, target_type) && EQ_SPELLS(l, r, zonetype) && GT_STBASE(l, r, spell_level)) + if (EQ_STBASE(l, r, target_type) && EQ_SPELLS(l, r, zone_type) && GT_STBASE(l, r, spell_level)) return true; - if (EQ_STBASE(l, r, target_type) && EQ_SPELLS(l, r, zonetype) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) + if (EQ_STBASE(l, r, target_type) && EQ_SPELLS(l, r, zone_type) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) return true; return false; @@ -895,15 +895,15 @@ private: continue; case BCEnum::SpT_Lull: spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) { - if (LT_SPELLS(l, r, ResistDiff)) + if (LT_SPELLS(l, r, resist_difficulty)) return true; - if (EQ_SPELLS(l, r, ResistDiff) && LT_STBASE(l, r, target_type)) + if (EQ_SPELLS(l, r, resist_difficulty) && LT_STBASE(l, r, target_type)) return true; - if (EQ_SPELLS(l, r, ResistDiff) && EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, max, 3)) + if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, max_value, 3)) return true; - if (EQ_SPELLS(l, r, ResistDiff) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max, 3) && LT_STBASE(l, r, spell_level)) + if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max_value, 3) && LT_STBASE(l, r, spell_level)) return true; - if (EQ_SPELLS(l, r, ResistDiff) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max, 3) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) + if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max_value, 3) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) return true; return false; @@ -911,15 +911,15 @@ private: continue; case BCEnum::SpT_Mesmerize: spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) { - if (GT_SPELLS(l, r, ResistDiff)) + if (GT_SPELLS(l, r, resist_difficulty)) return true; - if (EQ_SPELLS(l, r, ResistDiff) && LT_STBASE(l, r, target_type)) + if (EQ_SPELLS(l, r, resist_difficulty) && LT_STBASE(l, r, target_type)) return true; - if (EQ_SPELLS(l, r, ResistDiff) && EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, max, 1)) + if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, max_value, 1)) return true; - if (EQ_SPELLS(l, r, ResistDiff) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max, 1) && GT_STBASE(l, r, spell_level)) + if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max_value, 1) && GT_STBASE(l, r, spell_level)) return true; - if (EQ_SPELLS(l, r, ResistDiff) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max, 1) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) + if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max_value, 1) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) return true; return false; @@ -929,11 +929,11 @@ private: spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) { if (LT_STBASE(l, r, target_type)) return true; - if (EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, base, 2)) + if (EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, base_value, 2)) return true; - if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base, 2) && LT_STBASE(l, r, spell_level)) + if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base_value, 2) && LT_STBASE(l, r, spell_level)) return true; - if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base, 2) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) + if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base_value, 2) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) return true; return false; @@ -951,13 +951,13 @@ private: continue; case BCEnum::SpT_Resurrect: spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) { - if (GT_SPELLS_EFFECT_ID(l, r, base, 1)) + if (GT_SPELLS_EFFECT_ID(l, r, base_value, 1)) return true; - if (EQ_SPELLS_EFFECT_ID(l, r, base, 1) && LT_STBASE(l, r, target_type)) + if (EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && LT_STBASE(l, r, target_type)) return true; - if (EQ_SPELLS_EFFECT_ID(l, r, base, 1) && EQ_STBASE(l, r, target_type) && LT_STBASE(l, r, spell_level)) + if (EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && EQ_STBASE(l, r, target_type) && LT_STBASE(l, r, spell_level)) return true; - if (EQ_SPELLS_EFFECT_ID(l, r, base, 1) && EQ_STBASE(l, r, target_type) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) + if (EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && EQ_STBASE(l, r, target_type) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) return true; return false; @@ -967,11 +967,11 @@ private: spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) { if (LT_STBASE(l, r, target_type)) return true; - if (EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, max, 1)) + if (EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, max_value, 1)) return true; - if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max, 1) && LT_STBASE(l, r, spell_level)) + if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max_value, 1) && LT_STBASE(l, r, spell_level)) return true; - if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max, 1) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) + if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max_value, 1) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) return true; return false; @@ -999,19 +999,19 @@ private: if (l_size_type < r_size_type) return true; if (l_size_type == BCEnum::SzT_Enlarge && r_size_type == BCEnum::SzT_Enlarge) { - if (EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, base, 1)) + if (EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, base_value, 1)) return true; - if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base, 1) && GT_STBASE(l, r, spell_level)) + if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && GT_STBASE(l, r, spell_level)) return true; - if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base, 1) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) + if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) return true; } if (l_size_type == BCEnum::SzT_Reduce && r_size_type == BCEnum::SzT_Reduce) { - if (EQ_STBASE(l, r, target_type) && LT_SPELLS_EFFECT_ID(l, r, base, 1)) + if (EQ_STBASE(l, r, target_type) && LT_SPELLS_EFFECT_ID(l, r, base_value, 1)) return true; - if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base, 1) && GT_STBASE(l, r, spell_level)) + if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && GT_STBASE(l, r, spell_level)) return true; - if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base, 1) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) + if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) return true; } @@ -1028,11 +1028,11 @@ private: continue; case BCEnum::SpT_SummonCorpse: spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) { - if (GT_SPELLS_EFFECT_ID(l, r, base, 1)) + if (GT_SPELLS_EFFECT_ID(l, r, base_value, 1)) return true; - if (EQ_SPELLS_EFFECT_ID(l, r, base, 1) && LT_STBASE(l, r, spell_level)) + if (EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && LT_STBASE(l, r, spell_level)) return true; - if (EQ_SPELLS_EFFECT_ID(l, r, base, 1) && EQ_STBASE(l, r, spell_level) && EQ_STBASE(l, r, caster_class)) + if (EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && EQ_STBASE(l, r, spell_level) && EQ_STBASE(l, r, caster_class)) return true; return false; @@ -1167,18 +1167,18 @@ private: spell_dump << StringFormat(" /mn:%05u/RD:%06i/zt:%02i/d#:%06i/td#:%05i/ed#:%05i/SAI:%03u", spells[spell_id].mana, - spells[spell_id].ResistDiff, - spells[spell_id].zonetype, - spells[spell_id].descnum, - spells[spell_id].typedescnum, - spells[spell_id].effectdescnum, - spells[spell_id].SpellAffectIndex + spells[spell_id].resist_difficulty, + spells[spell_id].zone_type, + spells[spell_id].description_id, + spells[spell_id].type_description_id, + spells[spell_id].effect_description_id, + spells[spell_id].spell_affect_index ); for (int i = EffectIDFirst; i <= 3/*EffectIDLast*/; ++i) { int effect_index = EFFECTIDTOINDEX(i); spell_dump << StringFormat(" /e%02i:%04i/b%02i:%06i/m%02i:%06i", - i, spells[spell_id].effectid[effect_index], i, spells[spell_id].base[effect_index], i, spells[spell_id].max[effect_index]); + i, spells[spell_id].effect_id[effect_index], i, spells[spell_id].base_value[effect_index], i, spells[spell_id].max_value[effect_index]); } switch (list_entry->BCST()) { @@ -2935,7 +2935,7 @@ void bot_command_charm(Client *c, const Seperator *sep) return; } - if (spells[local_entry->spell_id].max[EFFECTIDTOINDEX(1)] < target_mob->GetLevel()) + if (spells[local_entry->spell_id].max_value[EFFECTIDTOINDEX(1)] < target_mob->GetLevel()) continue; my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob, true); @@ -3790,7 +3790,7 @@ void bot_command_mesmerize(Client *c, const Seperator *sep) if (!target_mob) continue; - if (spells[local_entry->spell_id].max[EFFECTIDTOINDEX(1)] < target_mob->GetLevel()) + if (spells[local_entry->spell_id].max_value[EFFECTIDTOINDEX(1)] < target_mob->GetLevel()) continue; my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); @@ -4705,7 +4705,7 @@ void bot_command_summon_corpse(Client *c, const Seperator *sep) if (!target_mob) continue; - if (spells[local_entry->spell_id].base[EFFECTIDTOINDEX(1)] < target_mob->GetLevel()) + if (spells[local_entry->spell_id].base_value[EFFECTIDTOINDEX(1)] < target_mob->GetLevel()) continue; my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); @@ -9029,7 +9029,7 @@ bool helper_spell_check_fail(STBaseEntry* local_entry) { if (!local_entry) return true; - if (spells[local_entry->spell_id].zonetype && zone->GetZoneType() && !(spells[local_entry->spell_id].zonetype & zone->GetZoneType())) + if (spells[local_entry->spell_id].zone_type && zone->GetZoneType() && !(spells[local_entry->spell_id].zone_type & zone->GetZoneType())) return true; return false; diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index b9d0bd6cd..4b8d602a6 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -766,7 +766,7 @@ bool BotDatabase::LoadBuffs(Bot* bot_inst) else if (CalculateCorruptionCounters(bot_buffs[buff_count].spellid) > 0) bot_buffs[buff_count].counters = atoi(row[7]); - bot_buffs[buff_count].numhits = atoi(row[8]); + bot_buffs[buff_count].hit_number = atoi(row[8]); bot_buffs[buff_count].melee_rune = atoi(row[9]); bot_buffs[buff_count].magic_rune = atoi(row[10]); bot_buffs[buff_count].dot_rune = atoi(row[11]); @@ -843,13 +843,13 @@ bool BotDatabase::SaveBuffs(Bot* bot_inst) bot_inst->GetBotID(), bot_buffs[buff_index].spellid, bot_buffs[buff_index].casterlevel, - spells[bot_buffs[buff_index].spellid].buffdurationformula, + spells[bot_buffs[buff_index].spellid].buff_duration_formula, bot_buffs[buff_index].ticsremaining, ((CalculatePoisonCounters(bot_buffs[buff_index].spellid) > 0) ? (bot_buffs[buff_index].counters) : (0)), ((CalculateDiseaseCounters(bot_buffs[buff_index].spellid) > 0) ? (bot_buffs[buff_index].counters) : (0)), ((CalculateCurseCounters(bot_buffs[buff_index].spellid) > 0) ? (bot_buffs[buff_index].counters) : (0)), ((CalculateCorruptionCounters(bot_buffs[buff_index].spellid) > 0) ? (bot_buffs[buff_index].counters) : (0)), - bot_buffs[buff_index].numhits, + bot_buffs[buff_index].hit_number, bot_buffs[buff_index].melee_rune, bot_buffs[buff_index].magic_rune, bot_buffs[buff_index].dot_rune, diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index e03c996fd..22eb103c5 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -232,7 +232,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) { break; // Can we cast this spell on this target? - if(!(spells[botSpell.SpellId].targettype==ST_GroupTeleport || spells[botSpell.SpellId].targettype == ST_Target || tar == this) + if(!(spells[botSpell.SpellId].target_type==ST_GroupTeleport || spells[botSpell.SpellId].target_type == ST_Target || tar == this) && !(tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0)) break; @@ -331,14 +331,14 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) { continue; // can not cast buffs for your own pet only on another pet that isn't yours - if((spells[selectedBotSpell.SpellId].targettype == ST_Pet) && (tar != this->GetPet())) + if((spells[selectedBotSpell.SpellId].target_type == ST_Pet) && (tar != this->GetPet())) continue; // Validate target - if(!((spells[selectedBotSpell.SpellId].targettype == ST_Target || spells[selectedBotSpell.SpellId].targettype == ST_Pet || tar == this || - spells[selectedBotSpell.SpellId].targettype == ST_Group || spells[selectedBotSpell.SpellId].targettype == ST_GroupTeleport || - (botClass == BARD && spells[selectedBotSpell.SpellId].targettype == ST_AEBard)) + if(!((spells[selectedBotSpell.SpellId].target_type == ST_Target || spells[selectedBotSpell.SpellId].target_type == ST_Pet || tar == this || + spells[selectedBotSpell.SpellId].target_type == ST_Group || spells[selectedBotSpell.SpellId].target_type == ST_GroupTeleport || + (botClass == BARD && spells[selectedBotSpell.SpellId].target_type == ST_AEBard)) && !tar->IsImmuneToSpell(selectedBotSpell.SpellId, this) && (tar->CanBuffStack(selectedBotSpell.SpellId, botLevel, true) >= 0))) { continue; @@ -606,7 +606,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) { if(CheckSpellRecastTimers(this, itr->SpellIndex)) { - if(!(!tar->IsImmuneToSpell(selectedBotSpell.SpellId, this) && (spells[selectedBotSpell.SpellId].buffduration < 1 || tar->CanBuffStack(selectedBotSpell.SpellId, botLevel, true) >= 0))) + if(!(!tar->IsImmuneToSpell(selectedBotSpell.SpellId, this) && (spells[selectedBotSpell.SpellId].buff_duration < 1 || tar->CanBuffStack(selectedBotSpell.SpellId, botLevel, true) >= 0))) continue; //short duration buffs or other buffs only to be cast during combat. @@ -644,14 +644,14 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) { continue; // can not cast buffs for your own pet only on another pet that isn't yours - if((spells[selectedBotSpell.SpellId].targettype == ST_Pet) && (tar != this->GetPet())) + if((spells[selectedBotSpell.SpellId].target_type == ST_Pet) && (tar != this->GetPet())) continue; // Validate target - if(!((spells[selectedBotSpell.SpellId].targettype == ST_Target || spells[selectedBotSpell.SpellId].targettype == ST_Pet || tar == this || - spells[selectedBotSpell.SpellId].targettype == ST_Group || spells[selectedBotSpell.SpellId].targettype == ST_GroupTeleport || - (botClass == BARD && spells[selectedBotSpell.SpellId].targettype == ST_AEBard)) + if(!((spells[selectedBotSpell.SpellId].target_type == ST_Target || spells[selectedBotSpell.SpellId].target_type == ST_Pet || tar == this || + spells[selectedBotSpell.SpellId].target_type == ST_Group || spells[selectedBotSpell.SpellId].target_type == ST_GroupTeleport || + (botClass == BARD && spells[selectedBotSpell.SpellId].target_type == ST_AEBard)) && !tar->IsImmuneToSpell(selectedBotSpell.SpellId, this) && (tar->CanBuffStack(selectedBotSpell.SpellId, botLevel, true) >= 0))) { continue; @@ -846,9 +846,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) { continue; if (!CheckSpellRecastTimers(this, iter.SpellIndex)) continue; - if (spells[iter.SpellId].zonetype != -1 && zone->GetZoneType() != -1 && spells[iter.SpellId].zonetype != zone->GetZoneType()) // is this bit or index? + if (spells[iter.SpellId].zone_type != -1 && zone->GetZoneType() != -1 && spells[iter.SpellId].zone_type != zone->GetZoneType()) // is this bit or index? continue; - if (spells[iter.SpellId].targettype != ST_Target) + if (spells[iter.SpellId].target_type != ST_Target) continue; if (tar->CanBuffStack(iter.SpellId, botLevel, true) < 0) continue; @@ -868,7 +868,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) { case BEASTLORD: { botSpell = GetBestBotSpellForDiseaseBasedSlow(this); - if(botSpell.SpellId == 0 || ((tar->GetMR() - 50) < (tar->GetDR() + spells[botSpell.SpellId].ResistDiff))) + if(botSpell.SpellId == 0 || ((tar->GetMR() - 50) < (tar->GetDR() + spells[botSpell.SpellId].resist_difficulty))) botSpell = GetBestBotSpellForMagicBasedSlow(this); break; } @@ -962,9 +962,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) { continue; if (!CheckSpellRecastTimers(this, iter.SpellIndex)) continue; - if (spells[iter.SpellId].zonetype != -1 && zone->GetZoneType() != -1 && spells[iter.SpellId].zonetype != zone->GetZoneType()) // is this bit or index? + if (spells[iter.SpellId].zone_type != -1 && zone->GetZoneType() != -1 && spells[iter.SpellId].zone_type != zone->GetZoneType()) // is this bit or index? continue; - if (spells[iter.SpellId].targettype != ST_Target) + if (spells[iter.SpellId].target_type != ST_Target) continue; if (tar->CanBuffStack(iter.SpellId, botLevel, true) < 0) continue; @@ -989,9 +989,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) { continue; if (!CheckSpellRecastTimers(this, iter.SpellIndex)) continue; - if (spells[iter.SpellId].zonetype != -1 && zone->GetZoneType() != -1 && spells[iter.SpellId].zonetype != zone->GetZoneType()) // is this bit or index? + if (spells[iter.SpellId].zone_type != -1 && zone->GetZoneType() != -1 && spells[iter.SpellId].zone_type != zone->GetZoneType()) // is this bit or index? continue; - switch (spells[iter.SpellId].targettype) { + switch (spells[iter.SpellId].target_type) { case ST_AEBard: case ST_AECaster: case ST_GroupTeleport: @@ -1021,9 +1021,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) { continue; if (!CheckSpellRecastTimers(this, iter.SpellIndex)) continue; - if (spells[iter.SpellId].zonetype != -1 && zone->GetZoneType() != -1 && spells[iter.SpellId].zonetype != zone->GetZoneType()) // is this bit or index? + if (spells[iter.SpellId].zone_type != -1 && zone->GetZoneType() != -1 && spells[iter.SpellId].zone_type != zone->GetZoneType()) // is this bit or index? continue; - switch (spells[iter.SpellId].targettype) { + switch (spells[iter.SpellId].target_type) { case ST_AEBard: case ST_AECaster: case ST_GroupTeleport: @@ -1088,11 +1088,11 @@ bool Bot::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain } else dist2 = DistanceSquared(m_Position, tar->GetPosition()); - if (((((spells[AIspells[i].spellid].targettype==ST_GroupTeleport && AIspells[i].type==2) - || spells[AIspells[i].spellid].targettype==ST_AECaster - || spells[AIspells[i].spellid].targettype==ST_Group - || spells[AIspells[i].spellid].targettype==ST_AEBard) - && dist2 <= spells[AIspells[i].spellid].aoerange*spells[AIspells[i].spellid].aoerange) + if (((((spells[AIspells[i].spellid].target_type==ST_GroupTeleport && AIspells[i].type==2) + || spells[AIspells[i].spellid].target_type==ST_AECaster + || spells[AIspells[i].spellid].target_type==ST_Group + || spells[AIspells[i].spellid].target_type==ST_AEBard) + && dist2 <= spells[AIspells[i].spellid].aoe_range*spells[AIspells[i].spellid].aoe_range) || dist2 <= GetActSpellRange(AIspells[i].spellid, spells[AIspells[i].spellid].range)*GetActSpellRange(AIspells[i].spellid, spells[AIspells[i].spellid].range)) && (mana_cost <= GetMana() || GetMana() == GetMaxMana())) { result = NPC::AIDoSpellCast(i, tar, mana_cost, oDontDoAgainBefore); @@ -1119,8 +1119,8 @@ bool Bot::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain AIspells[i].time_cancast = Timer::GetCurrentTime() + spells[AIspells[i].spellid].recast_time; - if(spells[AIspells[i].spellid].EndurTimerIndex > 0) { - SetSpellRecastTimer(spells[AIspells[i].spellid].EndurTimerIndex, spells[AIspells[i].spellid].recast_time); + if(spells[AIspells[i].spellid].timer_id > 0) { + SetSpellRecastTimer(spells[AIspells[i].spellid].timer_id, spells[AIspells[i].spellid].recast_time); } } @@ -1575,7 +1575,7 @@ bool Bot::AIHealRotation(Mob* tar, bool useFastHeals) { return false; // Can we cast this spell on this target? - if (!(spells[botSpell.SpellId].targettype == ST_GroupTeleport || spells[botSpell.SpellId].targettype == ST_Target || tar == this) + if (!(spells[botSpell.SpellId].target_type == ST_GroupTeleport || spells[botSpell.SpellId].target_type == ST_Target || tar == this) && !(tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0)) return false; @@ -1630,7 +1630,7 @@ std::list Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, } if(IsEffectInSpell(botSpellList[i].spellid, spellEffect)) { - if(spells[botSpellList[i].spellid].targettype == targetType) { + if(spells[botSpellList[i].spellid].target_type == targetType) { BotSpell botSpell; botSpell.SpellId = botSpellList[i].spellid; botSpell.SpellIndex = i; @@ -1999,7 +1999,7 @@ BotSpell Bot::GetBestBotSpellForMagicBasedSlow(Bot* botCaster) { for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order - if (IsSlowSpell(botSpellListItr->SpellId) && spells[botSpellListItr->SpellId].resisttype == RESIST_MAGIC && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) { + if (IsSlowSpell(botSpellListItr->SpellId) && spells[botSpellListItr->SpellId].resist_type == RESIST_MAGIC && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) { result.SpellId = botSpellListItr->SpellId; result.SpellIndex = botSpellListItr->SpellIndex; result.ManaCost = botSpellListItr->ManaCost; @@ -2024,7 +2024,7 @@ BotSpell Bot::GetBestBotSpellForDiseaseBasedSlow(Bot* botCaster) { for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order - if(IsSlowSpell(botSpellListItr->SpellId) && spells[botSpellListItr->SpellId].resisttype == RESIST_DISEASE && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) { + if(IsSlowSpell(botSpellListItr->SpellId) && spells[botSpellListItr->SpellId].resist_type == RESIST_DISEASE && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) { result.SpellId = botSpellListItr->SpellId; result.SpellIndex = botSpellListItr->SpellIndex; result.ManaCost = botSpellListItr->ManaCost; @@ -2265,26 +2265,26 @@ BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* targ bool spellSelected = false; if(CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) { - if(selectLureNuke && (spells[botSpellListItr->SpellId].ResistDiff < lureResisValue)) { + if(selectLureNuke && (spells[botSpellListItr->SpellId].resist_difficulty < lureResisValue)) { spellSelected = true; } else if(IsPureNukeSpell(botSpellListItr->SpellId)) { if(((target->GetMR() < target->GetCR()) || (target->GetMR() < target->GetFR())) && (GetSpellResistType(botSpellListItr->SpellId) == RESIST_MAGIC) - && (spells[botSpellListItr->SpellId].ResistDiff > lureResisValue)) + && (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue)) { spellSelected = true; } else if(((target->GetCR() < target->GetMR()) || (target->GetCR() < target->GetFR())) && (GetSpellResistType(botSpellListItr->SpellId) == RESIST_COLD) - && (spells[botSpellListItr->SpellId].ResistDiff > lureResisValue)) + && (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue)) { spellSelected = true; } else if(((target->GetFR() < target->GetCR()) || (target->GetFR() < target->GetMR())) && (GetSpellResistType(botSpellListItr->SpellId) == RESIST_FIRE) - && (spells[botSpellListItr->SpellId].ResistDiff > lureResisValue)) + && (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue)) { spellSelected = true; } - else if((GetSpellResistType(botSpellListItr->SpellId) == RESIST_MAGIC) && (spells[botSpellListItr->SpellId].ResistDiff > lureResisValue) && !IsStunSpell(botSpellListItr->SpellId)) { + else if((GetSpellResistType(botSpellListItr->SpellId) == RESIST_MAGIC) && (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue) && !IsStunSpell(botSpellListItr->SpellId)) { firstWizardMagicNukeSpellFound.SpellId = botSpellListItr->SpellId; firstWizardMagicNukeSpellFound.SpellIndex = botSpellListItr->SpellIndex; firstWizardMagicNukeSpellFound.ManaCost = botSpellListItr->ManaCost; @@ -2527,7 +2527,7 @@ int32 Bot::GetSpellRecastTimer(Bot *caster, int timer_index) { bool Bot::CheckSpellRecastTimers(Bot *caster, int SpellIndex) { if(caster) { if(caster->AIspells[SpellIndex].time_cancast < Timer::GetCurrentTime()) { //checks spell recast - if(GetSpellRecastTimer(caster, spells[caster->AIspells[SpellIndex].spellid].EndurTimerIndex) < Timer::GetCurrentTime()) { //checks for spells on the same timer + if(GetSpellRecastTimer(caster, spells[caster->AIspells[SpellIndex].spellid].timer_id) < Timer::GetCurrentTime()) { //checks for spells on the same timer return true; //can cast spell } } diff --git a/zone/client.cpp b/zone/client.cpp index ed29a3981..a1afb485f 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -4005,9 +4005,9 @@ void Client::SendOPTranslocateConfirm(Mob *Caster, uint16 SpellID) { else { PendingTranslocateData.zone_id = ts->ZoneID = ZoneID(Spell.teleport_zone); PendingTranslocateData.instance_id = 0; - PendingTranslocateData.y = ts->y = Spell.base[0]; - PendingTranslocateData.x = ts->x = Spell.base[1]; - PendingTranslocateData.z = ts->z = Spell.base[2]; + PendingTranslocateData.y = ts->y = Spell.base_value[0]; + PendingTranslocateData.x = ts->x = Spell.base_value[1]; + PendingTranslocateData.z = ts->z = Spell.base_value[2]; PendingTranslocateData.heading = 0.0; } @@ -4965,7 +4965,7 @@ void Client::HandleLDoNOpen(NPC *target) if(target->GetLDoNTrapSpellID() != 0) { MessageString(Chat::Red, LDON_ACCIDENT_SETOFF2); - target->SpellFinished(target->GetLDoNTrapSpellID(), this, EQ::spells::CastingSlot::Item, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff); + target->SpellFinished(target->GetLDoNTrapSpellID(), this, EQ::spells::CastingSlot::Item, 0, -1, spells[target->GetLDoNTrapSpellID()].resist_difficulty); target->SetLDoNTrapSpellID(0); target->SetLDoNTrapped(false); target->SetLDoNTrapDetected(false); @@ -5087,7 +5087,7 @@ void Client::HandleLDoNDisarm(NPC *target, uint16 skill, uint8 type) break; case -1: MessageString(Chat::Red, LDON_ACCIDENT_SETOFF2); - target->SpellFinished(target->GetLDoNTrapSpellID(), this, EQ::spells::CastingSlot::Item, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff); + target->SpellFinished(target->GetLDoNTrapSpellID(), this, EQ::spells::CastingSlot::Item, 0, -1, spells[target->GetLDoNTrapSpellID()].resist_difficulty); target->SetLDoNTrapSpellID(0); target->SetLDoNTrapped(false); target->SetLDoNTrapDetected(false); @@ -5106,7 +5106,7 @@ void Client::HandleLDoNPickLock(NPC *target, uint16 skill, uint8 type) if(target->IsLDoNTrapped()) { MessageString(Chat::Red, LDON_ACCIDENT_SETOFF2); - target->SpellFinished(target->GetLDoNTrapSpellID(), this, EQ::spells::CastingSlot::Item, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff); + target->SpellFinished(target->GetLDoNTrapSpellID(), this, EQ::spells::CastingSlot::Item, 0, -1, spells[target->GetLDoNTrapSpellID()].resist_difficulty); target->SetLDoNTrapSpellID(0); target->SetLDoNTrapped(false); target->SetLDoNTrapDetected(false); @@ -8577,8 +8577,8 @@ void Client::ShowNumHits() uint32 buffcount = GetMaxTotalSlots(); for (uint32 buffslot = 0; buffslot < buffcount; buffslot++) { const Buffs_Struct &curbuff = buffs[buffslot]; - if (curbuff.spellid != SPELL_UNKNOWN && curbuff.numhits) - Message(0, "You have %d hits left on %s", curbuff.numhits, GetSpellName(curbuff.spellid)); + if (curbuff.spellid != SPELL_UNKNOWN && curbuff.hit_number) + Message(0, "You have %d hits left on %s", curbuff.hit_number, GetSpellName(curbuff.spellid)); } return; } @@ -10055,7 +10055,7 @@ std::vector Client::GetLearnableDisciplines(uint8 min_level, uint8 max_leve continue; if (spells[spell_id].skill == 52) continue; - if (RuleB(Spells, UseCHAScribeHack) && spells[spell_id].effectid[EFFECT_COUNT - 1] == 10) + if (RuleB(Spells, UseCHAScribeHack) && spells[spell_id].effect_id[EFFECT_COUNT - 1] == 10) continue; if (HasDisciplineLearned(spell_id)) continue; @@ -10121,7 +10121,7 @@ std::vector Client::GetScribeableSpells(uint8 min_level, uint8 max_level) { continue; if (spells[spell_id].skill == 52) continue; - if (RuleB(Spells, UseCHAScribeHack) && spells[spell_id].effectid[EFFECT_COUNT - 1] == 10) + if (RuleB(Spells, UseCHAScribeHack) && spells[spell_id].effect_id[EFFECT_COUNT - 1] == 10) continue; if (HasSpellScribed(spell_id)) continue; diff --git a/zone/client_mods.cpp b/zone/client_mods.cpp index e124daf9e..ae2e6487a 100644 --- a/zone/client_mods.cpp +++ b/zone/client_mods.cpp @@ -1521,7 +1521,7 @@ uint32 Mob::GetInstrumentMod(uint16 spell_id) uint32 effectmod = 10; int effectmodcap = 0; if (RuleB(Character, UseSpellFileSongCap)) { - effectmodcap = spells[spell_id].songcap / 10; + effectmodcap = spells[spell_id].song_cap / 10; if (effectmodcap) { effectmodcap += 10; //Actual calculated cap is 100 greater than songcap value. } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 6f4b630f0..9273290b3 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -651,30 +651,30 @@ void Client::CompleteConnect() } for (int x1 = 0; x1 < EFFECT_COUNT; x1++) { - switch (spell.effectid[x1]) { + switch (spell.effect_id[x1]) { case SE_IllusionCopy: case SE_Illusion: { - if (spell.base[x1] == -1) { + if (spell.base_value[x1] == -1) { if (gender == 1) gender = 0; else if (gender == 0) gender = 1; SendIllusionPacket(GetRace(), gender, 0xFF, 0xFF); } - else if (spell.base[x1] == -2) // WTF IS THIS + else if (spell.base_value[x1] == -2) // WTF IS THIS { if (GetRace() == 128 || GetRace() == 130 || GetRace() <= 12) - SendIllusionPacket(GetRace(), GetGender(), spell.base2[x1], spell.max[x1]); + SendIllusionPacket(GetRace(), GetGender(), spell.limit_value[x1], spell.max_value[x1]); } - else if (spell.max[x1] > 0) + else if (spell.max_value[x1] > 0) { - SendIllusionPacket(spell.base[x1], 0xFF, spell.base2[x1], spell.max[x1]); + SendIllusionPacket(spell.base_value[x1], 0xFF, spell.limit_value[x1], spell.max_value[x1]); } else { - SendIllusionPacket(spell.base[x1], 0xFF, 0xFF, 0xFF); + SendIllusionPacket(spell.base_value[x1], 0xFF, 0xFF, 0xFF); } - switch (spell.base[x1]) { + switch (spell.base_value[x1]) { case OGRE: SendAppearancePacket(AT_Size, 9); break; @@ -761,17 +761,17 @@ void Client::CompleteConnect() case SE_AddMeleeProc: case SE_WeaponProc: { - AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid, buffs[j1].casterlevel); + AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, buffs[j1].casterlevel); break; } case SE_DefensiveProc: { - AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); + AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid); break; } case SE_RangedProc: { - AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); + AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid); break; } } @@ -1510,7 +1510,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) m_pp.buffs[i].unknown003 = 0; m_pp.buffs[i].duration = buffs[i].ticsremaining; m_pp.buffs[i].counters = buffs[i].counters; - m_pp.buffs[i].num_hits = buffs[i].numhits; + m_pp.buffs[i].num_hits = buffs[i].hit_number; } else { m_pp.buffs[i].spellid = SPELLBOOK_UNKNOWN; @@ -3956,7 +3956,7 @@ void Client::Handle_OP_Buff(const EQApplicationPacket *app) //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))) + if (spid == 0xFFFF || (IsValidSpell(spid) && (spells[spid].good_effect == 0))) QueuePacket(app); else BuffFadeBySpellID(spid); @@ -8877,7 +8877,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) IsFeared() || IsMezzed() || DivineAura() || - (spells[spell_id].targettype == ST_Ring) || + (spells[spell_id].target_type == ST_Ring) || (IsSilenced() && !IsDiscipline(spell_id)) || (IsAmnesiad() && IsDiscipline(spell_id)) || (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) || @@ -8890,7 +8890,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) } // Modern clients don't require pet targeted for item clicks that are ST_Pet - if (spell_id > 0 && (spells[spell_id].targettype == ST_Pet || spells[spell_id].targettype == ST_SummonedPet)) + if (spell_id > 0 && (spells[spell_id].target_type == ST_Pet || spells[spell_id].target_type == ST_SummonedPet)) target_id = GetPetID(); LogDebug("OP ItemVerifyRequest: spell=[{}], target=[{}], inv=[{}]", spell_id, target_id, slot_id); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index ea11764bf..d82bfa10a 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -978,7 +978,7 @@ void Client::OPRezzAnswer(uint32 Action, uint32 SpellID, uint16 ZoneID, uint16 I // corpse is in has shutdown since the rez spell was cast. database.MarkCorpseAsRezzed(PendingRezzDBID); LogSpells("Player [{}] got a [{}] Rezz, spellid [{}] in zone[{}], instance id [{}]", - this->name, (uint16)spells[SpellID].base[0], + this->name, (uint16)spells[SpellID].base_value[0], SpellID, ZoneID, InstanceID); this->BuffFadeNonPersistDeath(); @@ -997,12 +997,12 @@ void Client::OPRezzAnswer(uint32 Action, uint32 SpellID, uint16 ZoneID, uint16 I SetMana(GetMaxMana()); SetHP(GetMaxHP()); } - if(spells[SpellID].base[0] < 100 && spells[SpellID].base[0] > 0 && PendingRezzXP > 0) + if(spells[SpellID].base_value[0] < 100 && spells[SpellID].base_value[0] > 0 && PendingRezzXP > 0) { - SetEXP(((int)(GetEXP()+((float)((PendingRezzXP / 100) * spells[SpellID].base[0])))), + SetEXP(((int)(GetEXP()+((float)((PendingRezzXP / 100) * spells[SpellID].base_value[0])))), GetAAXP(),true); } - else if (spells[SpellID].base[0] == 100 && PendingRezzXP > 0) { + else if (spells[SpellID].base_value[0] == 100 && PendingRezzXP > 0) { SetEXP((GetEXP() + PendingRezzXP), GetAAXP(), true); } @@ -1832,7 +1832,7 @@ void Client::DoEnduranceUpkeep() { uint32 buff_count = GetMaxTotalSlots(); for (buffs_i = 0; buffs_i < buff_count; buffs_i++) { if (buffs[buffs_i].spellid != SPELL_UNKNOWN) { - int upkeep = spells[buffs[buffs_i].spellid].EndurUpkeep; + int upkeep = spells[buffs[buffs_i].spellid].endurance_upkeep; if(upkeep > 0) { has_effect = true; if(cost_redux > 0) { diff --git a/zone/command.cpp b/zone/command.cpp index c973f6cb4..45024464b 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -2998,12 +2998,12 @@ void command_castspell(Client *c, const Seperator *sep) else if (c->GetTarget() == 0) if(c->Admin() >= commandInstacast) - c->SpellFinished(spellid, 0, EQ::spells::CastingSlot::Item, 0, -1, spells[spellid].ResistDiff); + c->SpellFinished(spellid, 0, EQ::spells::CastingSlot::Item, 0, -1, spells[spellid].resist_difficulty); else c->CastSpell(spellid, 0, EQ::spells::CastingSlot::Item, 0); else if(c->Admin() >= commandInstacast) - c->SpellFinished(spellid, c->GetTarget(), EQ::spells::CastingSlot::Item, 0, -1, spells[spellid].ResistDiff); + c->SpellFinished(spellid, c->GetTarget(), EQ::spells::CastingSlot::Item, 0, -1, spells[spellid].resist_difficulty); else c->CastSpell(spellid, c->GetTarget()->GetID(), EQ::spells::CastingSlot::Item, 0); } @@ -5608,40 +5608,40 @@ void command_spellinfo(Client *c, const Seperator *sep) c->Message(Chat::White, " cast_on_you: %s", s->cast_on_you); c->Message(Chat::White, " spell_fades: %s", s->spell_fades); c->Message(Chat::White, " range: %f", s->range); - c->Message(Chat::White, " aoerange: %f", s->aoerange); - c->Message(Chat::White, " pushback: %f", s->pushback); - c->Message(Chat::White, " pushup: %f", s->pushup); + c->Message(Chat::White, " aoe_range: %f", s->aoe_range); + c->Message(Chat::White, " push_back: %f", s->push_back); + c->Message(Chat::White, " push_up: %f", s->push_up); c->Message(Chat::White, " cast_time: %d", s->cast_time); c->Message(Chat::White, " recovery_time: %d", s->recovery_time); c->Message(Chat::White, " recast_time: %d", s->recast_time); - c->Message(Chat::White, " buffdurationformula: %d", s->buffdurationformula); - c->Message(Chat::White, " buffduration: %d", s->buffduration); - c->Message(Chat::White, " AEDuration: %d", s->AEDuration); + c->Message(Chat::White, " buff_duration_formula: %d", s->buff_duration_formula); + c->Message(Chat::White, " buff_duration: %d", s->buff_duration); + c->Message(Chat::White, " AEDuration: %d", s->aoe_duration); c->Message(Chat::White, " mana: %d", s->mana); - c->Message(Chat::White, " base[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", s->base[0], s->base[1], s->base[2], s->base[3], s->base[4], s->base[5], s->base[6], s->base[7], s->base[8], s->base[9], s->base[10], s->base[11]); - c->Message(Chat::White, " base22[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", s->base2[0], s->base2[1], s->base2[2], s->base2[3], s->base2[4], s->base2[5], s->base2[6], s->base2[7], s->base2[8], s->base2[9], s->base2[10], s->base2[11]); - c->Message(Chat::White, " max[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", s->max[0], s->max[1], s->max[2], s->max[3], s->max[4], s->max[5], s->max[6], s->max[7], s->max[8], s->max[9], s->max[10], s->max[11]); - c->Message(Chat::White, " components[4]: %d, %d, %d, %d", s->components[0], s->components[1], s->components[2], s->components[3]); - c->Message(Chat::White, " component_counts[4]: %d, %d, %d, %d", s->component_counts[0], s->component_counts[1], s->component_counts[2], s->component_counts[3]); - c->Message(Chat::White, " NoexpendReagent[4]: %d, %d, %d, %d", s->NoexpendReagent[0], s->NoexpendReagent[1], s->NoexpendReagent[2], s->NoexpendReagent[3]); + c->Message(Chat::White, " base[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", s->base_value[0], s->base_value[1], s->base_value[2], s->base_value[3], s->base_value[4], s->base_value[5], s->base_value[6], s->base_value[7], s->base_value[8], s->base_value[9], s->base_value[10], s->base_value[11]); + c->Message(Chat::White, " base22[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", s->limit_value[0], s->limit_value[1], s->limit_value[2], s->limit_value[3], s->limit_value[4], s->limit_value[5], s->limit_value[6], s->limit_value[7], s->limit_value[8], s->limit_value[9], s->limit_value[10], s->limit_value[11]); + c->Message(Chat::White, " max[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", s->max_value[0], s->max_value[1], s->max_value[2], s->max_value[3], s->max_value[4], s->max_value[5], s->max_value[6], s->max_value[7], s->max_value[8], s->max_value[9], s->max_value[10], s->max_value[11]); + c->Message(Chat::White, " components[4]: %d, %d, %d, %d", s->component[0], s->component[1], s->component[2], s->component[3]); + c->Message(Chat::White, " component_counts[4]: %d, %d, %d, %d", s->component_count[0], s->component_count[1], s->component_count[2], s->component_count[3]); + c->Message(Chat::White, " NoexpendReagent[4]: %d, %d, %d, %d", s->no_expend_reagent[0], s->no_expend_reagent[1], s->no_expend_reagent[2], s->no_expend_reagent[3]); c->Message(Chat::White, " formula[12]: 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x", s->formula[0], s->formula[1], s->formula[2], s->formula[3], s->formula[4], s->formula[5], s->formula[6], s->formula[7], s->formula[8], s->formula[9], s->formula[10], s->formula[11]); - c->Message(Chat::White, " goodEffect: %d", s->goodEffect); - c->Message(Chat::White, " Activated: %d", s->Activated); - c->Message(Chat::White, " resisttype: %d", s->resisttype); - c->Message(Chat::White, " effectid[12]: 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x", s->effectid[0], s->effectid[1], s->effectid[2], s->effectid[3], s->effectid[4], s->effectid[5], s->effectid[6], s->effectid[7], s->effectid[8], s->effectid[9], s->effectid[10], s->effectid[11]); - c->Message(Chat::White, " targettype: %d", s->targettype); - c->Message(Chat::White, " basediff: %d", s->basediff); + c->Message(Chat::White, " goodEffect: %d", s->good_effect); + c->Message(Chat::White, " Activated: %d", s->activated); + c->Message(Chat::White, " resisttype: %d", s->resist_type); + c->Message(Chat::White, " effectid[12]: 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x", s->effect_id[0], s->effect_id[1], s->effect_id[2], s->effect_id[3], s->effect_id[4], s->effect_id[5], s->effect_id[6], s->effect_id[7], s->effect_id[8], s->effect_id[9], s->effect_id[10], s->effect_id[11]); + c->Message(Chat::White, " targettype: %d", s->target_type); + c->Message(Chat::White, " basediff: %d", s->base_difficulty); c->Message(Chat::White, " skill: %d", s->skill); - c->Message(Chat::White, " zonetype: %d", s->zonetype); - c->Message(Chat::White, " EnvironmentType: %d", s->EnvironmentType); - c->Message(Chat::White, " TimeOfDay: %d", s->TimeOfDay); + c->Message(Chat::White, " zonetype: %d", s->zone_type); + c->Message(Chat::White, " EnvironmentType: %d", s->environment_type); + c->Message(Chat::White, " TimeOfDay: %d", s->time_of_day); c->Message(Chat::White, " classes[15]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", s->classes[0], s->classes[1], s->classes[2], s->classes[3], s->classes[4], s->classes[5], s->classes[6], s->classes[7], s->classes[8], s->classes[9], s->classes[10], s->classes[11], s->classes[12], s->classes[13], s->classes[14]); - c->Message(Chat::White, " CastingAnim: %d", s->CastingAnim); - c->Message(Chat::White, " SpellAffectIndex: %d", s->SpellAffectIndex); - c->Message(Chat::White, " RecourseLink: %d", s->RecourseLink); + c->Message(Chat::White, " CastingAnim: %d", s->casting_animation); + c->Message(Chat::White, " SpellAffectIndex: %d", s->spell_affect_index); + c->Message(Chat::White, " RecourseLink: %d", s->recourse_link); } } diff --git a/zone/common.h b/zone/common.h index 82b17470e..733a4e3fd 100644 --- a/zone/common.h +++ b/zone/common.h @@ -318,7 +318,7 @@ struct Buffs_Struct { char caster_name[64]; int32 ticsremaining; uint32 counters; - uint32 numhits; //the number of physical hits this buff can take before it fades away, lots of druid armor spells take advantage of this mixed with powerful effects + uint32 hit_number; //the number of physical hits this buff can take before it fades away, lots of druid armor spells take advantage of this mixed with powerful effects uint32 melee_rune; uint32 magic_rune; uint32 dot_rune; diff --git a/zone/effects.cpp b/zone/effects.cpp index 849038ab1..0d9c412d0 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -42,7 +42,7 @@ float Mob::GetActSpellRange(uint16 spell_id, float range, bool IsBard) int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { - if (spells[spell_id].targettype == ST_Self) + if (spells[spell_id].target_type == ST_Self) return value; if (IsNPC()) @@ -320,7 +320,7 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { int16 critical_chance = 0; int8 critical_modifier = 1; - if (spells[spell_id].buffduration < 1) { + if (spells[spell_id].buff_duration < 1) { critical_chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance; if (spellbonuses.CriticalHealDecay) { @@ -353,7 +353,7 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { value += int(base_value*GetFocusEffect(focusFcAmplifyMod, spell_id) / 100); // Instant Heals - if (spells[spell_id].buffduration < 1) { + if (spells[spell_id].buff_duration < 1) { if (target) { value += int(base_value * target->GetFocusEffect(focusFcHealPctIncoming, spell_id)/100); //SPA 393 Add before critical @@ -756,7 +756,7 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) { return(false); } - if(GetEndurance() < spell.EndurCost) { + if(GetEndurance() < spell.endurance_cost) { Message(11, "You are too fatigued to use this skill right now."); return(false); } @@ -768,11 +768,11 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) { } // the client does this check before calling CastSpell, should prevent discs being eaten - if (spell.buffdurationformula != 0 && spell.targettype == ST_Self && HasDiscBuff()) + if (spell.buff_duration_formula != 0 && spell.target_type == ST_Self && HasDiscBuff()) return false; //Check the disc timer - pTimerType DiscTimer = pTimerDisciplineReuseStart + spell.EndurTimerIndex; + pTimerType DiscTimer = pTimerDisciplineReuseStart + spell.timer_id; if(!p_timers.Expired(&database, DiscTimer, false)) { // lets not set the reuse timer in case CastSpell fails (or we would have to turn off the timer, but CastSpell will set it as well) /*char val1[20]={0};*/ //unused /*char val2[20]={0};*/ //unused @@ -805,7 +805,7 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) { return true; } - SendDisciplineTimer(spells[spell_id].EndurTimerIndex, reduced_recast); + SendDisciplineTimer(spells[spell_id].timer_id, reduced_recast); } else { @@ -924,7 +924,7 @@ void EntityList::AESpell( ) { const auto &cast_target_position = - spells[spell_id].targettype == ST_Ring ? + spells[spell_id].target_type == ST_Ring ? caster_mob->GetTargetRingLocation() : static_cast(center_mob->GetPosition()); @@ -951,8 +951,8 @@ void EntityList::AESpell( if (max_targets) { // rains pass this in since they need to preserve the count through waves max_targets_allowed = *max_targets; } - else if (spells[spell_id].aemaxtargets) { - max_targets_allowed = spells[spell_id].aemaxtargets; + else if (spells[spell_id].aoe_max_targets) { + max_targets_allowed = spells[spell_id].aoe_max_targets; } else if (IsTargetableAESpell(spell_id) && is_detrimental_spell && !is_npc && !IsEffectInSpell(spell_id, SE_Lull) && !IsEffectInSpell(spell_id, SE_Mez)) { max_targets_allowed = 4; @@ -984,15 +984,15 @@ void EntityList::AESpell( continue; } - if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && current_mob->IsPetOwnerClient()) { + if (spells[spell_id].target_type == ST_TargetAENoPlayersPets && current_mob->IsPetOwnerClient()) { continue; } - if (spells[spell_id].targettype == ST_AreaClientOnly && !current_mob->IsClient()) { + if (spells[spell_id].target_type == ST_AreaClientOnly && !current_mob->IsClient()) { continue; } - if (spells[spell_id].targettype == ST_AreaNPCOnly && !current_mob->IsNPC()) { + if (spells[spell_id].target_type == ST_AreaNPCOnly && !current_mob->IsNPC()) { continue; } @@ -1026,7 +1026,7 @@ void EntityList::AESpell( } if (is_npc && current_mob->IsNPC() && - spells[spell_id].targettype != ST_AreaNPCOnly) { //check npc->npc casting + spells[spell_id].target_type != ST_AreaNPCOnly) { //check npc->npc casting FACTION_VALUE faction_value = current_mob->GetReverseFactionCon(caster_mob); if (is_detrimental_spell) { //affect mobs that are on our hate list, or diff --git a/zone/embparser.cpp b/zone/embparser.cpp index 635a48b4c..c74b7de42 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -171,7 +171,7 @@ void PerlembParser::ReloadQuests() } int PerlembParser::EventCommon( - QuestEventID event, uint32 objid, const char *data, NPC *npcmob, EQ::ItemInstance *item_inst, Mob *mob, + QuestEventID event, uint32 objid, const char *data, NPC *npcmob, EQ::ItemInstance *item_inst, const SPDat_Spell_Struct* spell, Mob *mob, uint32 extradata, bool global, std::vector *extra_pointers ) { @@ -252,17 +252,17 @@ int PerlembParser::EventCommon( } if (isPlayerQuest || isGlobalPlayerQuest) { - return SendCommands(package_name.c_str(), sub_name, 0, mob, mob, nullptr); + return SendCommands(package_name.c_str(), sub_name, 0, mob, mob, nullptr, nullptr); } else if (isItemQuest) { - return SendCommands(package_name.c_str(), sub_name, 0, mob, mob, item_inst); + return SendCommands(package_name.c_str(), sub_name, 0, mob, mob, item_inst, nullptr); } else if (isSpellQuest) { if (mob) { - return SendCommands(package_name.c_str(), sub_name, 0, mob, mob, nullptr); + return SendCommands(package_name.c_str(), sub_name, 0, mob, mob, nullptr, spell); } else { - return SendCommands(package_name.c_str(), sub_name, 0, npcmob, mob, nullptr); + return SendCommands(package_name.c_str(), sub_name, 0, npcmob, mob, nullptr, spell); } } else { - return SendCommands(package_name.c_str(), sub_name, objid, npcmob, mob, nullptr); + return SendCommands(package_name.c_str(), sub_name, objid, npcmob, mob, nullptr, nullptr); } } @@ -271,7 +271,7 @@ int PerlembParser::EventNPC( std::vector *extra_pointers ) { - return EventCommon(evt, npc->GetNPCTypeID(), data.c_str(), npc, nullptr, init, extra_data, false, extra_pointers); + return EventCommon(evt, npc->GetNPCTypeID(), data.c_str(), npc, nullptr, nullptr, init, extra_data, false, extra_pointers); } int PerlembParser::EventGlobalNPC( @@ -279,7 +279,7 @@ int PerlembParser::EventGlobalNPC( std::vector *extra_pointers ) { - return EventCommon(evt, npc->GetNPCTypeID(), data.c_str(), npc, nullptr, init, extra_data, true, extra_pointers); + return EventCommon(evt, npc->GetNPCTypeID(), data.c_str(), npc, nullptr, nullptr, init, extra_data, true, extra_pointers); } int PerlembParser::EventPlayer( @@ -287,7 +287,7 @@ int PerlembParser::EventPlayer( std::vector *extra_pointers ) { - return EventCommon(evt, 0, data.c_str(), nullptr, nullptr, client, extra_data, false, extra_pointers); + return EventCommon(evt, 0, data.c_str(), nullptr, nullptr, nullptr, client, extra_data, false, extra_pointers); } int PerlembParser::EventGlobalPlayer( @@ -295,7 +295,7 @@ int PerlembParser::EventGlobalPlayer( std::vector *extra_pointers ) { - return EventCommon(evt, 0, data.c_str(), nullptr, nullptr, client, extra_data, true, extra_pointers); + return EventCommon(evt, 0, data.c_str(), nullptr, nullptr, nullptr, client, extra_data, true, extra_pointers); } int PerlembParser::EventItem( @@ -304,7 +304,7 @@ int PerlembParser::EventItem( ) { // needs pointer validation on 'item' argument - return EventCommon(evt, item->GetID(), nullptr, nullptr, item, client, extra_data, false, extra_pointers); + return EventCommon(evt, item->GetID(), nullptr, nullptr, item, nullptr, client, extra_data, false, extra_pointers); } int PerlembParser::EventSpell( @@ -312,7 +312,7 @@ int PerlembParser::EventSpell( std::vector *extra_pointers ) { - return EventCommon(evt, spell_id, data.c_str(), npc, nullptr, client, extra_data, false, extra_pointers); + return EventCommon(evt, spell_id, data.c_str(), npc, nullptr, &spells[spell_id], client, extra_data, false, extra_pointers); } bool PerlembParser::HasQuestSub(uint32 npcid, QuestEventID evt) @@ -775,10 +775,11 @@ void PerlembParser::ExportVar(const char *pkgprefix, const char *varname, const int PerlembParser::SendCommands( const char *pkgprefix, const char *event, - uint32 npcid, + uint32 object_id, Mob *other, Mob *mob, - EQ::ItemInstance *item_inst + EQ::ItemInstance *item_inst, + const SPDat_Spell_Struct *spell ) { if (!perl) { @@ -787,10 +788,10 @@ int PerlembParser::SendCommands( int ret_value = 0; if (mob && mob->IsClient()) { - quest_manager.StartQuest(other, mob->CastToClient(), item_inst); + quest_manager.StartQuest(other, mob->CastToClient(), item_inst, spell); } else { - quest_manager.StartQuest(other, nullptr, nullptr); + quest_manager.StartQuest(other); } try { @@ -804,6 +805,7 @@ int PerlembParser::SendCommands( std::string cl = (std::string) "$" + (std::string) pkgprefix + (std::string) "::client"; std::string np = (std::string) "$" + (std::string) pkgprefix + (std::string) "::npc"; std::string qi = (std::string) "$" + (std::string) pkgprefix + (std::string) "::questitem"; + std::string sp = (std::string) "$" + (std::string) pkgprefix + (std::string) "::spell"; std::string enl = (std::string) "$" + (std::string) pkgprefix + (std::string) "::entity_list"; if (clear_vars_.find(cl) != clear_vars_.end()) { std::string eval_str = cl; @@ -823,6 +825,12 @@ int PerlembParser::SendCommands( perl->eval(eval_str.c_str()); } + if (clear_vars_.find(sp) != clear_vars_.end()) { + std::string eval_str = sp; + eval_str += " = undef;"; + perl->eval(eval_str.c_str()); + } + if (clear_vars_.find(enl) != clear_vars_.end()) { std::string eval_str = enl; eval_str += " = undef;"; @@ -860,6 +868,14 @@ int PerlembParser::SendCommands( sv_setref_pv(questitem, "QuestItem", curi); } + if (spell) { + const SPDat_Spell_Struct* current_spell = quest_manager.GetQuestSpell(); + SPDat_Spell_Struct* real_spell = const_cast(current_spell); + snprintf(namebuf, 64, "%s::spell", pkgprefix); + SV *spell = get_sv(namebuf, true); + sv_setref_pv(spell, "Spell", (void *) real_spell); + } + snprintf(namebuf, 64, "%s::entity_list", pkgprefix); SV *el = get_sv(namebuf, true); sv_setref_pv(el, "EntityList", &entity_list); @@ -873,10 +889,12 @@ int PerlembParser::SendCommands( std::string cl = (std::string) "$" + (std::string) pkgprefix + (std::string) "::client"; std::string np = (std::string) "$" + (std::string) pkgprefix + (std::string) "::npc"; std::string qi = (std::string) "$" + (std::string) pkgprefix + (std::string) "::questitem"; + std::string sp = (std::string) "$" + (std::string) pkgprefix + (std::string) "::spell"; std::string enl = (std::string) "$" + (std::string) pkgprefix + (std::string) "::entity_list"; clear_vars_[cl] = 1; clear_vars_[np] = 1; clear_vars_[qi] = 1; + clear_vars_[sp] = 1; clear_vars_[enl] = 1; } #endif @@ -967,6 +985,9 @@ void PerlembParser::MapFunctions() "package QuestItem;" "&boot_QuestItem;" // load quest Item XS + "package Spell;" + "&boot_Spell;" // load quest Spell XS + "package HateEntry;" "&boot_HateEntry;" // load quest Hate XS diff --git a/zone/embparser.h b/zone/embparser.h index 76d6d9717..52bba1589 100644 --- a/zone/embparser.h +++ b/zone/embparser.h @@ -90,9 +90,9 @@ private: void ExportVar(const char *pkgprefix, const char *varname, float value); void ExportVarComplex(const char *pkgprefix, const char *varname, const char *value); - int EventCommon(QuestEventID event, uint32 objid, const char * data, NPC* npcmob, EQ::ItemInstance* item_inst, Mob* mob, + int EventCommon(QuestEventID event, uint32 objid, const char * data, NPC* npcmob, EQ::ItemInstance* item_inst, const SPDat_Spell_Struct* spell, Mob* mob, uint32 extradata, bool global, std::vector *extra_pointers); - int SendCommands(const char *pkgprefix, const char *event, uint32 npcid, Mob* other, Mob* mob, EQ::ItemInstance *item_inst); + int SendCommands(const char *pkgprefix, const char *event, uint32 spell_id, Mob* other, Mob* mob, EQ::ItemInstance *item_inst, const SPDat_Spell_Struct *spell); void MapFunctions(); void GetQuestTypes(bool &isPlayerQuest, bool &isGlobalPlayerQuest, bool &isGlobalNPC, bool &isItemQuest, diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index fda5cd9b1..b0b1a312e 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -138,6 +138,21 @@ XS(XS_QuestItem_new) { XSRETURN(1); } +//Any creation of new Spells gets the current Spell +XS(XS_Spell_new); +XS(XS_Spell_new) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::Spell::new()"); + + const SPDat_Spell_Struct* spell = quest_manager.GetQuestSpell(); + ST(0) = sv_newmortal(); + if (spell) + sv_setref_pv(ST(0), "Spell", (void *) spell); + + XSRETURN(1); +} + //Any creation of new quest items gets the current quest item XS(XS_MobList_new); XS(XS_MobList_new) { @@ -7993,6 +8008,21 @@ XS(XS__countspawnednpcs) { } } +XS(XS__getspell); +XS(XS__getspell) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::getspell(uint32 spell_id)"); + { + dXSTARG; + uint32 spell_id = (uint32) SvUV(ST(0)); + const SPDat_Spell_Struct* spell = quest_manager.getspell(spell_id); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Spell", (void *) spell); + XSRETURN(1); + } +} + /* This is the callback perl will look for to setup the quest package's XSUBs @@ -8301,6 +8331,7 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "getinventoryslotname"), XS__getinventoryslotname, file); newXS(strcpy(buf, "getraididbycharid"), XS__getraididbycharid, file); newXS(strcpy(buf, "getracename"), XS__getracename, file); + newXS(strcpy(buf, "getspell"), XS__getspell, file); newXS(strcpy(buf, "getspellname"), XS__getspellname, file); newXS(strcpy(buf, "get_spell_level"), XS__get_spell_level, file); newXS(strcpy(buf, "getspellstat"), XS__getspellstat, file); diff --git a/zone/embperl.cpp b/zone/embperl.cpp index 1300df0b2..9be785566 100644 --- a/zone/embperl.cpp +++ b/zone/embperl.cpp @@ -34,6 +34,7 @@ EXTERN_C XS(boot_Group); EXTERN_C XS(boot_Raid); EXTERN_C XS(boot_Inventory); EXTERN_C XS(boot_QuestItem); +EXTERN_C XS(boot_Spell); EXTERN_C XS(boot_HateEntry); EXTERN_C XS(boot_Object); EXTERN_C XS(boot_Doors); @@ -90,6 +91,7 @@ EXTERN_C void xs_init(pTHX) newXS(strcpy(buf, "Raid::boot_Raid"), boot_Raid, file); newXS(strcpy(buf, "Inventory::boot_Inventory"), boot_Inventory, file); newXS(strcpy(buf, "QuestItem::boot_QuestItem"), boot_QuestItem, file); + newXS(strcpy(buf, "Spell::boot_Spell"), boot_Spell, file); newXS(strcpy(buf, "HateEntry::boot_HateEntry"), boot_HateEntry, file); newXS(strcpy(buf, "Object::boot_Object"), boot_Object, file); newXS(strcpy(buf, "Doors::boot_Doors"), boot_Doors, file); diff --git a/zone/groups.cpp b/zone/groups.cpp index 13188dcb9..aa4fc857a 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -824,7 +824,7 @@ void Group::CastGroupSpell(Mob* caster, uint16 spell_id) { if(members[z] == caster) { caster->SpellOnTarget(spell_id, caster); #ifdef GROUP_BUFF_PETS - if(spells[spell_id].targettype != ST_GroupNoPets && caster->GetPet() && caster->HasPetAffinity() && !caster->GetPet()->IsCharmed()) + if(spells[spell_id].target_type != ST_GroupNoPets && caster->GetPet() && caster->HasPetAffinity() && !caster->GetPet()->IsCharmed()) caster->SpellOnTarget(spell_id, caster->GetPet()); #endif } @@ -835,7 +835,7 @@ void Group::CastGroupSpell(Mob* caster, uint16 spell_id) { members[z]->CalcSpellPowerDistanceMod(spell_id, distance); caster->SpellOnTarget(spell_id, members[z]); #ifdef GROUP_BUFF_PETS - if(spells[spell_id].targettype != ST_GroupNoPets && members[z]->GetPet() && members[z]->HasPetAffinity() && !members[z]->GetPet()->IsCharmed()) + if(spells[spell_id].target_type != ST_GroupNoPets && members[z]->GetPet() && members[z]->HasPetAffinity() && !members[z]->GetPet()->IsCharmed()) caster->SpellOnTarget(spell_id, members[z]->GetPet()); #endif } else diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 57a426516..af3cc0e4b 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -19,6 +19,7 @@ #include "lua_npc.h" #include "lua_entity_list.h" #include "lua_expedition.h" +#include "lua_spell.h" #include "quest_parser_collection.h" #include "questmgr.h" #include "qglobals.h" @@ -1452,6 +1453,10 @@ Lua_ItemInst lua_get_quest_item() { return quest_manager.GetQuestItem(); } +Lua_Spell lua_get_quest_spell() { + return quest_manager.GetQuestSpell(); +} + std::string lua_get_encounter() { return quest_manager.GetEncounter(); } @@ -3342,6 +3347,10 @@ uint32 lua_count_spawned_npcs(luabind::adl::object table) { return entity_list.CountSpawnedNPCs(npc_ids); } +Lua_Spell lua_get_spell(uint32 spell_id) { + return Lua_Spell(spell_id); +} + #define LuaCreateNPCParse(name, c_type, default_value) do { \ cur = table[#name]; \ if(luabind::type(cur) != LUA_TNIL) { \ @@ -3750,6 +3759,7 @@ luabind::scope lua_register_general() { luabind::def("get_initiator", &lua_get_initiator), luabind::def("get_owner", &lua_get_owner), luabind::def("get_quest_item", &lua_get_quest_item), + luabind::def("get_quest_spell", &lua_get_quest_spell), luabind::def("get_encounter", &lua_get_encounter), luabind::def("map_opcodes", &lua_map_opcodes), luabind::def("clear_opcode", &lua_clear_opcode), @@ -3783,6 +3793,7 @@ luabind::scope lua_register_general() { luabind::def("get_spell_stat", (int(*)(uint32,std::string,uint8))&lua_get_spell_stat), luabind::def("is_npc_spawned", &lua_is_npc_spawned), luabind::def("count_spawned_npcs", &lua_count_spawned_npcs), + luabind::def("get_spell", &lua_get_spell), /* Cross Zone diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index 181a7dde5..ed201ec9c 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -1891,9 +1891,9 @@ void Lua_Mob::WearChange(int material_slot, int texture, uint32 color) { self->WearChange(material_slot, texture, color); } -void Lua_Mob::DoKnockback(Lua_Mob caster, uint32 pushback, uint32 pushup) { +void Lua_Mob::DoKnockback(Lua_Mob caster, uint32 push_back, uint32 push_up) { Lua_Safe_Call_Void(); - self->DoKnockback(caster, pushback, pushup); + self->DoKnockback(caster, push_back, push_up); } void Lua_Mob::AddNimbusEffect(int effect_id) { diff --git a/zone/lua_mob.h b/zone/lua_mob.h index 144d9dbb5..e2e964b3b 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -362,7 +362,7 @@ public: void DelGlobal(const char *varname); void SetSlotTint(int material_slot, int red_tint, int green_tint, int blue_tint); void WearChange(int material_slot, int texture, uint32 color); - void DoKnockback(Lua_Mob caster, uint32 pushback, uint32 pushup); + void DoKnockback(Lua_Mob caster, uint32 push_back, uint32 push_up); void AddNimbusEffect(int effect_id); void RemoveNimbusEffect(int effect_id); void RemoveAllNimbusEffects(); diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index a1aee9a41..2b1f89574 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -327,7 +327,7 @@ int LuaParser::_EventNPC(std::string package_name, QuestEventID evt, NPC* npc, M arg_function(this, L, npc, init, data, extra_data, extra_pointers); Client *c = (init && init->IsClient()) ? init->CastToClient() : nullptr; - quest_manager.StartQuest(npc, c, nullptr); + quest_manager.StartQuest(npc, c); if(lua_pcall(L, 1, 1, 0)) { std::string error = lua_tostring(L, -1); AddError(error); @@ -420,7 +420,7 @@ int LuaParser::_EventPlayer(std::string package_name, QuestEventID evt, Client * auto arg_function = PlayerArgumentDispatch[evt]; arg_function(this, L, client, data, extra_data, extra_pointers); - quest_manager.StartQuest(client, client, nullptr); + quest_manager.StartQuest(client, client); if(lua_pcall(L, 1, 1, 0)) { std::string error = lua_tostring(L, -1); AddError(error); @@ -584,7 +584,7 @@ int LuaParser::_EventSpell(std::string package_name, QuestEventID evt, NPC* npc, auto arg_function = SpellArgumentDispatch[evt]; arg_function(this, L, npc, client, spell_id, data, extra_data, extra_pointers); - quest_manager.StartQuest(npc, client, nullptr); + quest_manager.StartQuest(npc, client, nullptr, const_cast(&spells[spell_id])); if(lua_pcall(L, 1, 1, 0)) { std::string error = lua_tostring(L, -1); AddError(error); @@ -650,7 +650,7 @@ int LuaParser::_EventEncounter(std::string package_name, QuestEventID evt, std:: auto arg_function = EncounterArgumentDispatch[evt]; arg_function(this, L, enc, data, extra_data, extra_pointers); - quest_manager.StartQuest(enc, nullptr, nullptr, encounter_name); + quest_manager.StartQuest(enc, nullptr, nullptr, nullptr, encounter_name); if(lua_pcall(L, 1, 1, 0)) { std::string error = lua_tostring(L, -1); AddError(error); diff --git a/zone/lua_spell.cpp b/zone/lua_spell.cpp index 5e7dcff73..4675f4415 100644 --- a/zone/lua_spell.cpp +++ b/zone/lua_spell.cpp @@ -66,17 +66,17 @@ float Lua_Spell::GetRange() { float Lua_Spell::GetAoeRange() { Lua_Safe_Call_Real(); - return self->aoerange; + return self->aoe_range; } float Lua_Spell::GetPushBack() { Lua_Safe_Call_Real(); - return self->pushback; + return self->push_back; } float Lua_Spell::GetPushUp() { Lua_Safe_Call_Real(); - return self->pushup; + return self->push_up; } uint32 Lua_Spell::GetCastTime() { @@ -96,17 +96,17 @@ uint32 Lua_Spell::GetRecastTime() { uint32 Lua_Spell::GetBuffdurationFormula() { Lua_Safe_Call_Int(); - return self->buffdurationformula; + return self->buff_duration_formula; } uint32 Lua_Spell::GetBuffDuration() { Lua_Safe_Call_Int(); - return self->buffduration; + return self->buff_duration; } uint32 Lua_Spell::GetAEDuration() { Lua_Safe_Call_Int(); - return self->AEDuration; + return self->aoe_duration; } int Lua_Spell::GetMana() { @@ -121,7 +121,7 @@ int Lua_Spell::GetBase(int i) { return 0; } - return self->base[i]; + return self->base_value[i]; } int Lua_Spell::GetBase2(int i) { @@ -131,7 +131,7 @@ int Lua_Spell::GetBase2(int i) { return 0; } - return self->base2[i]; + return self->limit_value[i]; } int Lua_Spell::GetMax(int i) { @@ -141,7 +141,7 @@ int Lua_Spell::GetMax(int i) { return 0; } - return self->max[i]; + return self->max_value[i]; } int Lua_Spell::GetComponents(int i) { @@ -151,7 +151,7 @@ int Lua_Spell::GetComponents(int i) { return 0; } - return self->components[i]; + return self->component[i]; } int Lua_Spell::GetComponentCounts(int i) { @@ -161,7 +161,7 @@ int Lua_Spell::GetComponentCounts(int i) { return 0; } - return self->component_counts[i]; + return self->component_count[i]; } int Lua_Spell::GetNoexpendReagent(int i) { @@ -171,7 +171,7 @@ int Lua_Spell::GetNoexpendReagent(int i) { return 0; } - return self->NoexpendReagent[i]; + return self->no_expend_reagent[i]; } int Lua_Spell::GetFormula(int i) { @@ -186,17 +186,17 @@ int Lua_Spell::GetFormula(int i) { int Lua_Spell::GetGoodEffect() { Lua_Safe_Call_Int(); - return self->goodEffect; + return self->good_effect; } int Lua_Spell::GetActivated() { Lua_Safe_Call_Int(); - return self->Activated; + return self->activated; } int Lua_Spell::GetResistType() { Lua_Safe_Call_Int(); - return self->resisttype; + return self->resist_type; } int Lua_Spell::GetEffectID(int i) { @@ -206,17 +206,17 @@ int Lua_Spell::GetEffectID(int i) { return 0; } - return self->effectid[i]; + return self->effect_id[i]; } int Lua_Spell::GetTargetType() { Lua_Safe_Call_Int(); - return self->targettype; + return self->target_type; } int Lua_Spell::GetBaseDiff() { Lua_Safe_Call_Int(); - return self->basediff; + return self->base_difficulty; } int Lua_Spell::GetSkill() { @@ -226,17 +226,17 @@ int Lua_Spell::GetSkill() { int Lua_Spell::GetZoneType() { Lua_Safe_Call_Int(); - return self->zonetype; + return self->zone_type; } int Lua_Spell::GetEnvironmentType() { Lua_Safe_Call_Int(); - return self->EnvironmentType; + return self->environment_type; } int Lua_Spell::GetTimeOfDay() { Lua_Safe_Call_Int(); - return self->TimeOfDay; + return self->time_of_day; } int Lua_Spell::GetClasses(int i) { @@ -251,12 +251,12 @@ int Lua_Spell::GetClasses(int i) { int Lua_Spell::GetCastingAnim() { Lua_Safe_Call_Int(); - return self->CastingAnim; + return self->casting_animation; } int Lua_Spell::GetSpellAffectIndex() { Lua_Safe_Call_Int(); - return self->SpellAffectIndex; + return self->spell_affect_index; } int Lua_Spell::GetDisallowSit() { @@ -281,12 +281,12 @@ int Lua_Spell::GetUninterruptable() { int Lua_Spell::GetResistDiff() { Lua_Safe_Call_Int(); - return self->ResistDiff; + return self->resist_difficulty; } int Lua_Spell::GetRecourseLink() { Lua_Safe_Call_Int(); - return self->RecourseLink; + return self->recourse_link; } int Lua_Spell::GetShortBuffBox() { @@ -296,57 +296,57 @@ int Lua_Spell::GetShortBuffBox() { int Lua_Spell::GetDescNum() { Lua_Safe_Call_Int(); - return self->descnum; + return self->description_id; } int Lua_Spell::GetEffectDescNum() { Lua_Safe_Call_Int(); - return self->effectdescnum; + return self->effect_description_id; } int Lua_Spell::GetBonusHate() { Lua_Safe_Call_Int(); - return self->bonushate; + return self->bonus_hate; } int Lua_Spell::GetEndurCost() { Lua_Safe_Call_Int(); - return self->EndurCost; + return self->endurance_cost; } int Lua_Spell::GetEndurTimerIndex() { Lua_Safe_Call_Int(); - return self->EndurUpkeep; + return self->endurance_upkeep; } int Lua_Spell::GetHateAdded() { Lua_Safe_Call_Int(); - return self->HateAdded; + return self->hate_added; } int Lua_Spell::GetEndurUpkeep() { Lua_Safe_Call_Int(); - return self->EndurUpkeep; + return self->endurance_upkeep; } int Lua_Spell::GetNumHits() { Lua_Safe_Call_Int(); - return self->numhits; + return self->hit_number; } int Lua_Spell::GetPVPResistBase() { Lua_Safe_Call_Int(); - return self->pvpresistbase; + return self->pvp_resist_base; } int Lua_Spell::GetPVPResistCalc() { Lua_Safe_Call_Int(); - return self->pvpresistcalc; + return self->pvp_resist_per_level; } int Lua_Spell::GetPVPResistCap() { Lua_Safe_Call_Int(); - return self->pvpresistcap; + return self->pvp_resist_cap; } int Lua_Spell::GetSpellCategory() { @@ -376,12 +376,12 @@ int Lua_Spell::GetDispelFlag() { int Lua_Spell::GetMinResist() { Lua_Safe_Call_Int(); - return self->MinResist; + return self->min_resist; } int Lua_Spell::GetMaxResist() { Lua_Safe_Call_Int(); - return self->MaxResist; + return self->max_resist; } int Lua_Spell::GetViralTargets() { @@ -396,7 +396,7 @@ int Lua_Spell::GetViralTimer() { int Lua_Spell::GetNimbusEffect() { Lua_Safe_Call_Int(); - return self->NimbusEffect; + return self->nimbus_effect; } float Lua_Spell::GetDirectionalStart() { @@ -411,7 +411,7 @@ float Lua_Spell::GetDirectionalEnd() { int Lua_Spell::GetSpellGroup() { Lua_Safe_Call_Int(); - return self->spellgroup; + return self->spell_group; } int Lua_Spell::GetPowerfulFlag() { @@ -421,27 +421,27 @@ int Lua_Spell::GetPowerfulFlag() { int Lua_Spell::GetCastRestriction() { Lua_Safe_Call_Int(); - return self->CastRestriction; + return self->cast_restriction; } bool Lua_Spell::GetAllowRest() { Lua_Safe_Call_Bool(); - return self->AllowRest; + return self->allow_rest; } bool Lua_Spell::GetInCombat() { Lua_Safe_Call_Bool(); - return self->InCombat; + return self->can_cast_in_combat; } bool Lua_Spell::GetOutOfCombat() { Lua_Safe_Call_Bool(); - return self->OutofCombat; + return self->can_cast_out_of_combat; } int Lua_Spell::GetAEMaxTargets() { Lua_Safe_Call_Int(); - return self->aemaxtargets; + return self->aoe_max_targets; } int Lua_Spell::GetMaxTargets() { @@ -451,27 +451,27 @@ int Lua_Spell::GetMaxTargets() { bool Lua_Spell::GetPersistDeath() { Lua_Safe_Call_Bool(); - return self->persistdeath; + return self->persist_death; } float Lua_Spell::GetMinDist() { Lua_Safe_Call_Real(); - return self->min_dist; + return self->min_distance; } float Lua_Spell::GetMinDistMod() { Lua_Safe_Call_Real(); - return self->min_dist_mod; + return self->min_distance_mod; } float Lua_Spell::GetMaxDist() { Lua_Safe_Call_Real(); - return self->max_dist; + return self->max_distance; } float Lua_Spell::GetMaxDistMod() { Lua_Safe_Call_Real(); - return self->max_dist_mod; + return self->max_distance_mod; } float Lua_Spell::GetMinRange() { @@ -481,7 +481,7 @@ float Lua_Spell::GetMinRange() { int Lua_Spell::GetDamageShieldType() { Lua_Safe_Call_Int(); - return self->DamageShieldType; + return self->damage_shield_type; } int Lua_Spell::GetRank() { diff --git a/zone/merc.cpp b/zone/merc.cpp index 2a68e8907..306c1aeec 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -1117,7 +1117,7 @@ void Merc::DoEnduranceUpkeep() { uint32 buff_count = GetMaxTotalSlots(); for (buffs_i = 0; buffs_i < buff_count; buffs_i++) { if (buffs[buffs_i].spellid != SPELL_UNKNOWN) { - int upkeep = spells[buffs[buffs_i].spellid].EndurUpkeep; + int upkeep = spells[buffs[buffs_i].spellid].endurance_upkeep; if(upkeep > 0) { has_effect = true; if(cost_redux > 0) { @@ -1982,11 +1982,11 @@ bool Merc::AIDoSpellCast(uint16 spellid, Mob* tar, int32 mana_cost, uint32* oDon } else dist2 = DistanceSquared(m_Position, tar->GetPosition()); - if (((((spells[spellid].targettype==ST_GroupTeleport && mercSpell.type==SpellType_Heal) - || spells[spellid].targettype==ST_AECaster - || spells[spellid].targettype==ST_Group - || spells[spellid].targettype==ST_AEBard) - && dist2 <= spells[spellid].aoerange*spells[spellid].aoerange) + if (((((spells[spellid].target_type==ST_GroupTeleport && mercSpell.type==SpellType_Heal) + || spells[spellid].target_type==ST_AECaster + || spells[spellid].target_type==ST_Group + || spells[spellid].target_type==ST_AEBard) + && dist2 <= spells[spellid].aoe_range*spells[spellid].aoe_range) || dist2 <= GetActSpellRange(spellid, spells[spellid].range)*GetActSpellRange(spellid, spells[spellid].range)) && (mana_cost <= GetMana() || GetMana() == GetMaxMana())) { SetRunAnimSpeed(0); @@ -2007,8 +2007,8 @@ bool Merc::AIDoSpellCast(uint16 spellid, Mob* tar, int32 mana_cost, uint32* oDon else { //handle spell recast and recast timers SetSpellTimeCanCast(mercSpell.spellid, spells[spellid].recast_time); - if(spells[spellid].EndurTimerIndex > 0) { - SetSpellRecastTimer(spells[spellid].EndurTimerIndex, spellid, spells[spellid].recast_time); + if(spells[spellid].timer_id > 0) { + SetSpellRecastTimer(spells[spellid].timer_id, spellid, spells[spellid].recast_time); } } @@ -2177,13 +2177,13 @@ bool Merc::AICastSpell(int8 iChance, uint32 iSpellTypes) { itr != buffSpellList.end(); ++itr) { MercSpell selectedMercSpell = *itr; - if(!((spells[selectedMercSpell.spellid].targettype == ST_Target || spells[selectedMercSpell.spellid].targettype == ST_Pet || - spells[selectedMercSpell.spellid].targettype == ST_Group || spells[selectedMercSpell.spellid].targettype == ST_GroupTeleport || - spells[selectedMercSpell.spellid].targettype == ST_Self))) { + if(!((spells[selectedMercSpell.spellid].target_type == ST_Target || spells[selectedMercSpell.spellid].target_type == ST_Pet || + spells[selectedMercSpell.spellid].target_type == ST_Group || spells[selectedMercSpell.spellid].target_type == ST_GroupTeleport || + spells[selectedMercSpell.spellid].target_type == ST_Self))) { continue; } - if(spells[selectedMercSpell.spellid].targettype == ST_Self) { + if(spells[selectedMercSpell.spellid].target_type == ST_Self) { if( !this->IsImmuneToSpell(selectedMercSpell.spellid, this) && (this->CanBuffStack(selectedMercSpell.spellid, mercLevel, true) >= 0)) { @@ -2237,8 +2237,8 @@ bool Merc::AICastSpell(int8 iChance, uint32 iSpellTypes) { //don't cast group spells on pets if(IsGroupSpell(selectedMercSpell.spellid) - || spells[selectedMercSpell.spellid].targettype == ST_Group - || spells[selectedMercSpell.spellid].targettype == ST_GroupTeleport ) { + || spells[selectedMercSpell.spellid].target_type == ST_Group + || spells[selectedMercSpell.spellid].target_type == ST_GroupTeleport ) { continue; } @@ -2337,11 +2337,11 @@ bool Merc::AICastSpell(int8 iChance, uint32 iSpellTypes) { itr != buffSpellList.end(); ++itr) { MercSpell selectedMercSpell = *itr; - if(!(spells[selectedMercSpell.spellid].targettype == ST_Self)) { + if(!(spells[selectedMercSpell.spellid].target_type == ST_Self)) { continue; } - if (spells[selectedMercSpell.spellid].skill == EQ::skills::SkillBackstab && spells[selectedMercSpell.spellid].targettype == ST_Self) { + if (spells[selectedMercSpell.spellid].skill == EQ::skills::SkillBackstab && spells[selectedMercSpell.spellid].target_type == ST_Self) { if(!hidden) { continue; } @@ -2535,7 +2535,7 @@ bool Merc::CheckAENuke(Merc* caster, Mob* tar, uint16 spell_id, uint8 &numTarget for (auto itr = npc_list.begin(); itr != npc_list.end(); ++itr) { NPC* npc = *itr; - if(DistanceSquaredNoZ(npc->GetPosition(), tar->GetPosition()) <= spells[spell_id].aoerange * spells[spell_id].aoerange) { + if(DistanceSquaredNoZ(npc->GetPosition(), tar->GetPosition()) <= spells[spell_id].aoe_range * spells[spell_id].aoe_range) { if(!npc->IsMezzed()) { numTargets++; } @@ -2667,7 +2667,7 @@ int32 Merc::GetFocusEffect(focusType type, uint16 spell_id) { realTotal2 = CalcFocusEffect(type, focusspell_tracker, spell_id); // For effects like gift of mana that only fire once, save the spellid into an array that consists of all available buff slots. - if(buff_tracker >= 0 && buffs[buff_tracker].numhits > 0) { + if(buff_tracker >= 0 && buffs[buff_tracker].hit_number > 0) { m_spellHitsLeft[buff_tracker] = focusspell_tracker; } } @@ -3047,7 +3047,7 @@ std::list Merc::GetMercSpellsForSpellEffectAndTargetType(Merc* caster } if(IsEffectInSpell(mercSpellList[i].spellid, spellEffect) && caster->CheckStance(mercSpellList[i].stance)) { - if(spells[mercSpellList[i].spellid].targettype == targetType) { + if(spells[mercSpellList[i].spellid].target_type == targetType) { MercSpell MercSpell; MercSpell.spellid = mercSpellList[i].spellid; MercSpell.stance = mercSpellList[i].stance; @@ -3395,9 +3395,9 @@ MercSpell Merc::GetBestMercSpellForAETaunt(Merc* caster) { for (auto mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); ++mercSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order - if((spells[mercSpellListItr->spellid].targettype == ST_AECaster - || spells[mercSpellListItr->spellid].targettype == ST_AETarget - || spells[mercSpellListItr->spellid].targettype == ST_UndeadAE) + if((spells[mercSpellListItr->spellid].target_type == ST_AECaster + || spells[mercSpellListItr->spellid].target_type == ST_AETarget + || spells[mercSpellListItr->spellid].target_type == ST_UndeadAE) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { result.spellid = mercSpellListItr->spellid; result.stance = mercSpellListItr->stance; @@ -3430,7 +3430,7 @@ MercSpell Merc::GetBestMercSpellForTaunt(Merc* caster) { for (auto mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); ++mercSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order - if((spells[mercSpellListItr->spellid].targettype == ST_Target) + if((spells[mercSpellListItr->spellid].target_type == ST_Target) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { result.spellid = mercSpellListItr->spellid; result.stance = mercSpellListItr->stance; @@ -3900,22 +3900,22 @@ MercSpell Merc::GetBestMercSpellForNukeByTargetResists(Merc* caster, Mob* target // Assuming all the spells have been loaded into this list by level and in descending order if(IsPureNukeSpell(mercSpellListItr->spellid) && !IsAENukeSpell(mercSpellListItr->spellid) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - if(selectLureNuke && (spells[mercSpellListItr->spellid].ResistDiff < lureResisValue)) { + if(selectLureNuke && (spells[mercSpellListItr->spellid].resist_difficulty < lureResisValue)) { spellSelected = true; } else { if(((target->GetMR() < target->GetCR()) || (target->GetMR() < target->GetFR())) && (GetSpellResistType(mercSpellListItr->spellid) == RESIST_MAGIC) - && (spells[mercSpellListItr->spellid].ResistDiff > lureResisValue)) + && (spells[mercSpellListItr->spellid].resist_difficulty > lureResisValue)) { spellSelected = true; } else if(((target->GetCR() < target->GetMR()) || (target->GetCR() < target->GetFR())) && (GetSpellResistType(mercSpellListItr->spellid) == RESIST_COLD) - && (spells[mercSpellListItr->spellid].ResistDiff > lureResisValue)) + && (spells[mercSpellListItr->spellid].resist_difficulty > lureResisValue)) { spellSelected = true; } else if(((target->GetFR() < target->GetCR()) || (target->GetFR() < target->GetMR())) && (GetSpellResistType(mercSpellListItr->spellid) == RESIST_FIRE) - && (spells[mercSpellListItr->spellid].ResistDiff > lureResisValue)) + && (spells[mercSpellListItr->spellid].resist_difficulty > lureResisValue)) { spellSelected = true; } @@ -4003,9 +4003,9 @@ bool Merc::UseDiscipline(int32 spell_id, int32 target) { if(spell.recast_time > 0) { - if(CheckDisciplineRecastTimers(this, spell_id, spells[spell_id].EndurTimerIndex)) { - if(spells[spell_id].EndurTimerIndex > 0) { - SetDisciplineRecastTimer(spells[spell_id].EndurTimerIndex, spell_id, spell.recast_time); + if(CheckDisciplineRecastTimers(this, spell_id, spells[spell_id].timer_id)) { + if(spells[spell_id].timer_id > 0) { + SetDisciplineRecastTimer(spells[spell_id].timer_id, spell_id, spell.recast_time); } SetSpellTimeCanCast(spell_id, spells[spell_id].recast_time); @@ -4015,8 +4015,8 @@ bool Merc::UseDiscipline(int32 spell_id, int32 target) { } } - if(GetEndurance() > spell.EndurCost) { - SetEndurance(GetEndurance() - spell.EndurCost); + if(GetEndurance() > spell.endurance_cost) { + SetEndurance(GetEndurance() - spell.endurance_cost); } else { //too fatigued to use this skill right now. return(false); @@ -4055,7 +4055,7 @@ bool Merc::CheckSpellRecastTimers(Merc *caster, uint16 spell_id) { if(caster) { MercSpell mercSpell = GetMercSpellBySpellID(caster, spell_id); if(mercSpell.spellid > 0 && mercSpell.time_cancast < Timer::GetCurrentTime()) { //checks spell recast - if(GetSpellRecastTimer(caster, spells[spell_id].EndurTimerIndex) < Timer::GetCurrentTime()) { //checks for spells on the same timer + if(GetSpellRecastTimer(caster, spells[spell_id].timer_id) < Timer::GetCurrentTime()) { //checks for spells on the same timer return true; //can cast spell } } diff --git a/zone/mob.cpp b/zone/mob.cpp index a60a9f563..1c5752d9a 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1672,7 +1672,7 @@ void Mob::ShowBuffs(Client* client) { uint32 buff_count = GetMaxTotalSlots(); for (i=0; i < buff_count; i++) { if (buffs[i].spellid != SPELL_UNKNOWN) { - if (spells[buffs[i].spellid].buffdurationformula == DF_Permanent) + if (spells[buffs[i].spellid].buff_duration_formula == DF_Permanent) client->Message(Chat::White, " %i: %s: Permanent", i, spells[buffs[i].spellid].name); else client->Message(Chat::White, " %i: %s: %i tics left", i, spells[buffs[i].spellid].name, buffs[i].ticsremaining); @@ -1706,7 +1706,7 @@ void Mob::ShowBuffList(Client* client) { uint32 buff_count = GetMaxTotalSlots(); for (i = 0; i < buff_count; i++) { if (buffs[i].spellid != SPELL_UNKNOWN) { - if (spells[buffs[i].spellid].buffdurationformula == DF_Permanent) + if (spells[buffs[i].spellid].buff_duration_formula == DF_Permanent) client->Message(Chat::White, " %i: %s: Permanent", i, spells[buffs[i].spellid].name); else client->Message(Chat::White, " %i: %s: %i tics left", i, spells[buffs[i].spellid].name, buffs[i].ticsremaining); @@ -2283,11 +2283,11 @@ void Mob::CameraEffect(uint32 duration, uint32 intensity, Client *c, bool global safe_delete(outapp); } -void Mob::SendSpellEffect(uint32 effectid, uint32 duration, uint32 finish_delay, bool zone_wide, uint32 unk020, bool perm_effect, Client *c) { +void Mob::SendSpellEffect(uint32 effect_id, uint32 duration, uint32 finish_delay, bool zone_wide, uint32 unk020, bool perm_effect, Client *c) { auto outapp = new EQApplicationPacket(OP_SpellEffect, sizeof(SpellEffect_Struct)); SpellEffect_Struct* se = (SpellEffect_Struct*) outapp->pBuffer; - se->EffectID = effectid; // ID of the Particle Effect + se->EffectID = effect_id; // ID of the Particle Effect se->EntityID = GetID(); se->EntityID2 = GetID(); // EntityID again se->Duration = duration; // In Milliseconds @@ -2307,8 +2307,8 @@ void Mob::SendSpellEffect(uint32 effectid, uint32 duration, uint32 finish_delay, safe_delete(outapp); if (perm_effect) { - if(!IsNimbusEffectActive(effectid)) { - SetNimbusEffect(effectid); + if(!IsNimbusEffectActive(effect_id)) { + SetNimbusEffect(effect_id); } } @@ -3149,7 +3149,7 @@ int32 Mob::GetActSpellCasttime(uint16 spell_id, int32 casttime) int32 cast_reducer_amt = GetFocusEffect(focusFcCastTimeAmt, spell_id); int32 cast_reducer_no_limit = GetFocusEffect(focusFcCastTimeMod2, spell_id); - if (level > 50 && casttime >= 3000 && !spells[spell_id].goodEffect && + if (level > 50 && casttime >= 3000 && !spells[spell_id].good_effect && (GetClass() == RANGER || GetClass() == SHADOWKNIGHT || GetClass() == PALADIN || GetClass() == BEASTLORD)) { int level_mod = std::min(15, GetLevel() - 50); cast_reducer += level_mod * 3; @@ -3209,12 +3209,12 @@ void Mob::ExecWeaponProc(const EQ::ItemInstance *inst, uint16 spell_id, Mob *on, twinproc = true; if (IsBeneficialSpell(spell_id) && (!IsNPC() || (IsNPC() && CastToNPC()->GetInnateProcSpellID() != spell_id))) { // NPC innate procs don't take this path ever - SpellFinished(spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff, true, level_override); + SpellFinished(spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty, true, level_override); if(twinproc) SpellOnTarget(spell_id, this, 0, false, 0, true, level_override); } else if(!(on->IsClient() && on->CastToClient()->dead)) { //dont proc on dead clients - SpellFinished(spell_id, on, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff, true, level_override); + SpellFinished(spell_id, on, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty, true, level_override); if(twinproc) SpellOnTarget(spell_id, on, 0, false, 0, true, level_override); } @@ -3384,10 +3384,10 @@ int Mob::CountDispellableBuffs() if(buffs[x].counters) continue; - if(spells[buffs[x].spellid].goodEffect == 0) + if(spells[buffs[x].spellid].good_effect == 0) continue; - if(buffs[x].spellid != SPELL_UNKNOWN && spells[buffs[x].spellid].buffdurationformula != DF_Permanent) + if(buffs[x].spellid != SPELL_UNKNOWN && spells[buffs[x].spellid].buff_duration_formula != DF_Permanent) val++; } return val; @@ -3406,9 +3406,9 @@ int Mob::GetSnaredAmount() for(int j = 0; j < EFFECT_COUNT; j++) { - if (spells[buffs[i].spellid].effectid[j] == SE_MovementSpeed) + if (spells[buffs[i].spellid].effect_id[j] == SE_MovementSpeed) { - int val = CalcSpellEffectValue_formula(spells[buffs[i].spellid].formula[j], spells[buffs[i].spellid].base[j], spells[buffs[i].spellid].max[j], buffs[i].casterlevel, buffs[i].spellid); + int val = CalcSpellEffectValue_formula(spells[buffs[i].spellid].formula[j], spells[buffs[i].spellid].base_value[j], spells[buffs[i].spellid].max_value[j], buffs[i].casterlevel, buffs[i].spellid); //int effect = CalcSpellEffectValue(buffs[i].spellid, spells[buffs[i].spellid].effectid[j], buffs[i].casterlevel); if (val < 0 && std::abs(val) > worst_snare) worst_snare = std::abs(val); @@ -3545,8 +3545,8 @@ bool Mob::TrySpellTrigger(Mob *target, uint32 spell_id, int effect) for (int i = 0; i < EFFECT_COUNT; i++) { - if (spells[spell_id].effectid[i] == SE_SpellTrigger || spells[spell_id].effectid[i] == SE_Chance_Best_in_Spell_Grp) - total_chance += spells[spell_id].base[i]; + if (spells[spell_id].effect_id[i] == SE_SpellTrigger || spells[spell_id].effect_id[i] == SE_Chance_Best_in_Spell_Grp) + total_chance += spells[spell_id].base_value[i]; } if (total_chance == 100) @@ -3556,9 +3556,9 @@ bool Mob::TrySpellTrigger(Mob *target, uint32 spell_id, int effect) for (int i = 0; i < EFFECT_COUNT; i++){ //Find spells with SPA 340 and add the cummulative percent chances to the roll array - if ((spells[spell_id].effectid[i] == SE_SpellTrigger) || (spells[spell_id].effectid[i] == SE_Chance_Best_in_Spell_Grp)){ + if ((spells[spell_id].effect_id[i] == SE_SpellTrigger) || (spells[spell_id].effect_id[i] == SE_Chance_Best_in_Spell_Grp)){ - cummulative_chance = current_chance + spells[spell_id].base[i]; + cummulative_chance = current_chance + spells[spell_id].base_value[i]; chance_array[i] = cummulative_chance; current_chance = cummulative_chance; } @@ -3575,19 +3575,19 @@ bool Mob::TrySpellTrigger(Mob *target, uint32 spell_id, int effect) } //If the chances don't add to 100, then each effect gets a chance to fire, chance for no trigger as well. - else if (zone->random.Roll(spells[spell_id].base[effect])) { + else if (zone->random.Roll(spells[spell_id].base_value[effect])) { CastSpell = true; //In this case effect_slot is what was passed into function. } if (CastSpell) { - if (spells[spell_id].effectid[effect_slot] == SE_SpellTrigger && IsValidSpell(spells[spell_id].base2[effect_slot])) { - SpellFinished(spells[spell_id].base2[effect_slot], target, EQ::spells::CastingSlot::Item, 0, -1, spells[spells[spell_id].base2[effect_slot]].ResistDiff); + if (spells[spell_id].effect_id[effect_slot] == SE_SpellTrigger && IsValidSpell(spells[spell_id].limit_value[effect_slot])) { + SpellFinished(spells[spell_id].limit_value[effect_slot], target, EQ::spells::CastingSlot::Item, 0, -1, spells[spells[spell_id].limit_value[effect_slot]].resist_difficulty); return true; } - else if (IsClient() & spells[spell_id].effectid[effect_slot] == SE_Chance_Best_in_Spell_Grp) { - uint32 best_spell_id = CastToClient()->GetHighestScribedSpellinSpellGroup(spells[spell_id].base2[effect_slot]); + else if (IsClient() & spells[spell_id].effect_id[effect_slot] == SE_Chance_Best_in_Spell_Grp) { + uint32 best_spell_id = CastToClient()->GetHighestScribedSpellinSpellGroup(spells[spell_id].limit_value[effect_slot]); if (IsValidSpell(best_spell_id)) { - SpellFinished(best_spell_id, target, EQ::spells::CastingSlot::Item, 0, -1, spells[best_spell_id].ResistDiff); + SpellFinished(best_spell_id, target, EQ::spells::CastingSlot::Item, 0, -1, spells[best_spell_id].resist_difficulty); } return true;//Do nothing if you don't have the any spell in spell group scribed. } @@ -3604,9 +3604,9 @@ void Mob::TryTriggerOnCastRequirement() int spell_id = buffs[e].spellid; if (IsValidSpell(spell_id)) { for (int i = 0; i < EFFECT_COUNT; i++) { - if ((spells[spell_id].effectid[i] == SE_TriggerOnReqTarget) || (spells[spell_id].effectid[i] == SE_TriggerOnReqCaster)) { - if (PassCastRestriction(spells[spell_id].base2[i])) { - SpellFinished(spells[spell_id].base[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff); + if ((spells[spell_id].effect_id[i] == SE_TriggerOnReqTarget) || (spells[spell_id].effect_id[i] == SE_TriggerOnReqCaster)) { + if (PassCastRestriction(spells[spell_id].limit_value[i])) { + SpellFinished(spells[spell_id].base_value[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty); if (!TryFadeEffect(e)) { BuffFadeBySlot(e); } @@ -3633,7 +3633,7 @@ void Mob::TryTwincast(Mob *caster, Mob *target, uint32 spell_id) if(zone->random.Roll(focus)) { Message(Chat::Spells,"You twincast %s!", spells[spell_id].name); - SpellFinished(spell_id, target, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff); + SpellFinished(spell_id, target, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty); } } } @@ -3651,7 +3651,7 @@ void Mob::TryTwincast(Mob *caster, Mob *target, uint32 spell_id) { if(zone->random.Roll(focus)) { - SpellFinished(spell_id, target, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff); + SpellFinished(spell_id, target, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty); } } } @@ -3672,9 +3672,9 @@ void Mob::TryOnSpellFinished(Mob *caster, Mob *target, uint16 spell_id) if (IsEffectInSpell(spell_id, SE_Health_Transfer)){ for (int i = 0; i < EFFECT_COUNT; i++) { - if (spells[spell_id].effectid[i] == SE_Health_Transfer) { + if (spells[spell_id].effect_id[i] == SE_Health_Transfer) { int new_hp = GetMaxHP(); - new_hp -= GetMaxHP() * spells[spell_id].base[i] / 1000; + new_hp -= GetMaxHP() * spells[spell_id].base_value[i] / 1000; if (new_hp > 0) SetHP(new_hp); @@ -3930,13 +3930,13 @@ bool Mob::TryFadeEffect(int slot) for(int i = 0; i < EFFECT_COUNT; i++) { - if (!spells[buffs[slot].spellid].effectid[i]) + if (!spells[buffs[slot].spellid].effect_id[i]) continue; - if (spells[buffs[slot].spellid].effectid[i] == SE_CastOnFadeEffectAlways || - spells[buffs[slot].spellid].effectid[i] == SE_CastOnRuneFadeEffect) + if (spells[buffs[slot].spellid].effect_id[i] == SE_CastOnFadeEffectAlways || + spells[buffs[slot].spellid].effect_id[i] == SE_CastOnRuneFadeEffect) { - uint16 spell_id = spells[buffs[slot].spellid].base[i]; + uint16 spell_id = spells[buffs[slot].spellid].base_value[i]; BuffFadeBySlot(slot); if(spell_id) @@ -3948,10 +3948,10 @@ bool Mob::TryFadeEffect(int slot) if(IsValidSpell(spell_id)) { if (IsBeneficialSpell(spell_id)) { - SpellFinished(spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff); + SpellFinished(spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty); } else if(!(IsClient() && CastToClient()->dead)) { - SpellFinished(spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff); + SpellFinished(spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty); } return true; } @@ -3985,7 +3985,7 @@ void Mob::TrySympatheticProc(Mob *target, uint32 spell_id) SpellFinished(focus_trigger, target); else - SpellFinished(focus_trigger, this, EQ::spells::CastingSlot::Item, 0, -1, spells[focus_trigger].ResistDiff); + SpellFinished(focus_trigger, this, EQ::spells::CastingSlot::Item, 0, -1, spells[focus_trigger].resist_difficulty); } // For detrimental spells, if the triggered spell is beneficial, then it will land on the caster // if the triggered spell is also detrimental, then it will land on the target @@ -3995,7 +3995,7 @@ void Mob::TrySympatheticProc(Mob *target, uint32 spell_id) SpellFinished(focus_trigger, this); else - SpellFinished(focus_trigger, target, EQ::spells::CastingSlot::Item, 0, -1, spells[focus_trigger].ResistDiff); + SpellFinished(focus_trigger, target, EQ::spells::CastingSlot::Item, 0, -1, spells[focus_trigger].resist_difficulty); } CheckNumHitsRemaining(NumHit::MatchingSpells, -1, focus_spell); @@ -4260,7 +4260,7 @@ int Mob::QGVarDuration(const char *fmt) return duration; } -void Mob::DoKnockback(Mob *caster, uint32 pushback, uint32 pushup) +void Mob::DoKnockback(Mob *caster, uint32 push_back, uint32 push_up) { if(IsClient()) { @@ -4275,8 +4275,8 @@ void Mob::DoKnockback(Mob *caster, uint32 pushback, uint32 pushup) look_heading -= 360; //x and y are crossed mkay - double new_x = pushback * sin(double(look_heading * 3.141592 / 180.0)); - double new_y = pushback * cos(double(look_heading * 3.141592 / 180.0)); + double new_x = push_back * sin(double(look_heading * 3.141592 / 180.0)); + double new_y = push_back * cos(double(look_heading * 3.141592 / 180.0)); spu->spawn_id = GetID(); spu->x_pos = FloatToEQ19(GetX()); @@ -4284,7 +4284,7 @@ void Mob::DoKnockback(Mob *caster, uint32 pushback, uint32 pushup) spu->z_pos = FloatToEQ19(GetZ()); spu->delta_x = FloatToEQ13(static_cast(new_x)); spu->delta_y = FloatToEQ13(static_cast(new_y)); - spu->delta_z = FloatToEQ13(static_cast(pushup)); + spu->delta_z = FloatToEQ13(static_cast(push_up)); spu->heading = FloatToEQ12(GetHeading()); // for ref: these were not passed on to other 5 clients while on Titanium standard (change to RoF2 standard: 11/16/2019) //eq->padding0002 = 0; @@ -4305,12 +4305,12 @@ void Mob::TrySpellOnKill(uint8 level, uint16 spell_id) { if(IsEffectInSpell(spell_id, SE_ProcOnSpellKillShot)) { for (int i = 0; i < EFFECT_COUNT; i++) { - if (spells[spell_id].effectid[i] == SE_ProcOnSpellKillShot) + if (spells[spell_id].effect_id[i] == SE_ProcOnSpellKillShot) { - if (IsValidSpell(spells[spell_id].base2[i]) && spells[spell_id].max[i] <= level) + if (IsValidSpell(spells[spell_id].limit_value[i]) && spells[spell_id].max_value[i] <= level) { - if(zone->random.Roll(spells[spell_id].base[i])) - SpellFinished(spells[spell_id].base2[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff); + if(zone->random.Roll(spells[spell_id].base_value[i])) + SpellFinished(spells[spell_id].limit_value[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[spells[spell_id].limit_value[i]].resist_difficulty); } } } @@ -4325,17 +4325,17 @@ void Mob::TrySpellOnKill(uint8 level, uint16 spell_id) if(aabonuses.SpellOnKill[i] && IsValidSpell(aabonuses.SpellOnKill[i]) && (level >= aabonuses.SpellOnKill[i + 2])) { if(zone->random.Roll(static_cast(aabonuses.SpellOnKill[i + 1]))) - SpellFinished(aabonuses.SpellOnKill[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[aabonuses.SpellOnKill[i]].ResistDiff); + SpellFinished(aabonuses.SpellOnKill[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[aabonuses.SpellOnKill[i]].resist_difficulty); } if(itembonuses.SpellOnKill[i] && IsValidSpell(itembonuses.SpellOnKill[i]) && (level >= itembonuses.SpellOnKill[i + 2])){ if(zone->random.Roll(static_cast(itembonuses.SpellOnKill[i + 1]))) - SpellFinished(itembonuses.SpellOnKill[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[aabonuses.SpellOnKill[i]].ResistDiff); + SpellFinished(itembonuses.SpellOnKill[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[aabonuses.SpellOnKill[i]].resist_difficulty); } if(spellbonuses.SpellOnKill[i] && IsValidSpell(spellbonuses.SpellOnKill[i]) && (level >= spellbonuses.SpellOnKill[i + 2])) { if(zone->random.Roll(static_cast(spellbonuses.SpellOnKill[i + 1]))) - SpellFinished(spellbonuses.SpellOnKill[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[aabonuses.SpellOnKill[i]].ResistDiff); + SpellFinished(spellbonuses.SpellOnKill[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[aabonuses.SpellOnKill[i]].resist_difficulty); } } @@ -4352,19 +4352,19 @@ bool Mob::TrySpellOnDeath() for(int i = 0; i < MAX_SPELL_TRIGGER*2; i+=2) { if(IsClient() && aabonuses.SpellOnDeath[i] && IsValidSpell(aabonuses.SpellOnDeath[i])) { if(zone->random.Roll(static_cast(aabonuses.SpellOnDeath[i + 1]))) { - SpellFinished(aabonuses.SpellOnDeath[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[aabonuses.SpellOnDeath[i]].ResistDiff); + SpellFinished(aabonuses.SpellOnDeath[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[aabonuses.SpellOnDeath[i]].resist_difficulty); } } if(itembonuses.SpellOnDeath[i] && IsValidSpell(itembonuses.SpellOnDeath[i])) { if(zone->random.Roll(static_cast(itembonuses.SpellOnDeath[i + 1]))) { - SpellFinished(itembonuses.SpellOnDeath[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[itembonuses.SpellOnDeath[i]].ResistDiff); + SpellFinished(itembonuses.SpellOnDeath[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[itembonuses.SpellOnDeath[i]].resist_difficulty); } } if(spellbonuses.SpellOnDeath[i] && IsValidSpell(spellbonuses.SpellOnDeath[i])) { if(zone->random.Roll(static_cast(spellbonuses.SpellOnDeath[i + 1]))) { - SpellFinished(spellbonuses.SpellOnDeath[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[spellbonuses.SpellOnDeath[i]].ResistDiff); + SpellFinished(spellbonuses.SpellOnDeath[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[spellbonuses.SpellOnDeath[i]].resist_difficulty); } } } @@ -4611,7 +4611,7 @@ void Mob::DoGravityEffect() { for (int i = 0; i < EFFECT_COUNT; i++) { - if(spells[buffs[slot].spellid].effectid[i] == SE_GravityEffect) { + if(spells[buffs[slot].spellid].effect_id[i] == SE_GravityEffect) { int casterId = buffs[slot].casterid; if(casterId) @@ -4623,7 +4623,7 @@ void Mob::DoGravityEffect() caster_x = caster->GetX(); caster_y = caster->GetY(); - value = static_cast(spells[buffs[slot].spellid].base[i]); + value = static_cast(spells[buffs[slot].spellid].base_value[i]); if(value == 0) continue; @@ -4683,33 +4683,33 @@ void Mob::DoGravityEffect() } } -void Mob::AddNimbusEffect(int effectid) +void Mob::AddNimbusEffect(int effect_id) { - SetNimbusEffect(effectid); + SetNimbusEffect(effect_id); auto outapp = new EQApplicationPacket(OP_AddNimbusEffect, sizeof(RemoveNimbusEffect_Struct)); auto ane = (RemoveNimbusEffect_Struct *)outapp->pBuffer; ane->spawnid = GetID(); - ane->nimbus_effect = effectid; + ane->nimbus_effect = effect_id; entity_list.QueueClients(this, outapp); safe_delete(outapp); } -void Mob::RemoveNimbusEffect(int effectid) +void Mob::RemoveNimbusEffect(int effect_id) { - if (effectid == nimbus_effect1) + if (effect_id == nimbus_effect1) nimbus_effect1 = 0; - else if (effectid == nimbus_effect2) + else if (effect_id == nimbus_effect2) nimbus_effect2 = 0; - else if (effectid == nimbus_effect3) + else if (effect_id == nimbus_effect3) nimbus_effect3 = 0; auto outapp = new EQApplicationPacket(OP_RemoveNimbusEffect, sizeof(RemoveNimbusEffect_Struct)); RemoveNimbusEffect_Struct* rne = (RemoveNimbusEffect_Struct*)outapp->pBuffer; rne->spawnid = GetID(); - rne->nimbus_effect = effectid; + rne->nimbus_effect = effect_id; entity_list.QueueClients(this, outapp); safe_delete(outapp); } @@ -4818,11 +4818,11 @@ void Mob::CastOnCurer(uint32 spell_id) { for(int i = 0; i < EFFECT_COUNT; i++) { - if (spells[spell_id].effectid[i] == SE_CastOnCurer) + if (spells[spell_id].effect_id[i] == SE_CastOnCurer) { - if(IsValidSpell(spells[spell_id].base[i])) + if(IsValidSpell(spells[spell_id].base_value[i])) { - SpellFinished(spells[spell_id].base[i], this); + SpellFinished(spells[spell_id].base_value[i], this); } } } @@ -4832,11 +4832,11 @@ void Mob::CastOnCure(uint32 spell_id) { for(int i = 0; i < EFFECT_COUNT; i++) { - if (spells[spell_id].effectid[i] == SE_CastOnCure) + if (spells[spell_id].effect_id[i] == SE_CastOnCure) { - if(IsValidSpell(spells[spell_id].base[i])) + if(IsValidSpell(spells[spell_id].base_value[i])) { - SpellFinished(spells[spell_id].base[i], this); + SpellFinished(spells[spell_id].base_value[i], this); } } } @@ -4849,11 +4849,11 @@ void Mob::CastOnNumHitFade(uint32 spell_id) for(int i = 0; i < EFFECT_COUNT; i++) { - if (spells[spell_id].effectid[i] == SE_CastonNumHitFade) + if (spells[spell_id].effect_id[i] == SE_CastonNumHitFade) { - if(IsValidSpell(spells[spell_id].base[i])) + if(IsValidSpell(spells[spell_id].base_value[i])) { - SpellFinished(spells[spell_id].base[i], this); + SpellFinished(spells[spell_id].base_value[i], this); } } } @@ -4940,8 +4940,8 @@ bool Mob::PassLimitToSkill(uint16 spell_id, uint16 skill) { return false; for (int i = 0; i < EFFECT_COUNT; i++) { - if (spells[spell_id].effectid[i] == SE_LimitToSkill){ - if (spells[spell_id].base[i] == skill){ + if (spells[spell_id].effect_id[i] == SE_LimitToSkill){ + if (spells[spell_id].base_value[i] == skill){ return true; } } @@ -4984,11 +4984,11 @@ int8 Mob::GetDecayEffectValue(uint16 spell_id, uint16 spelleffect) { for (int slot = 0; slot < buff_count; slot++){ if (IsValidSpell(buffs[slot].spellid)){ for (int i = 0; i < EFFECT_COUNT; i++){ - if(spells[buffs[slot].spellid].effectid[i] == spelleffect) { + if(spells[buffs[slot].spellid].effect_id[i] == spelleffect) { - int critchance = spells[buffs[slot].spellid].base[i]; - int decay = spells[buffs[slot].spellid].base2[i]; - int lvldiff = spell_level - spells[buffs[slot].spellid].max[i]; + int critchance = spells[buffs[slot].spellid].base_value[i]; + int decay = spells[buffs[slot].spellid].limit_value[i]; + int lvldiff = spell_level - spells[buffs[slot].spellid].max_value[i]; if(lvldiff > 0 && decay > 0) { @@ -5192,7 +5192,7 @@ FACTION_VALUE Mob::GetSpecialFactionCon(Mob* iOther) { } } -bool Mob::HasSpellEffect(int effectid) +bool Mob::HasSpellEffect(int effect_id) { int i; @@ -5201,7 +5201,7 @@ bool Mob::HasSpellEffect(int effectid) { if(buffs[i].spellid == SPELL_UNKNOWN) { continue; } - if(IsEffectInSpell(buffs[i].spellid, effectid)) + if(IsEffectInSpell(buffs[i].spellid, effect_id)) { return(1); } diff --git a/zone/mob.h b/zone/mob.h index 72ccb8447..c3da39e27 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -290,7 +290,7 @@ public: void BardPulse(uint16 spell_id, Mob *caster); //Spell - void SendSpellEffect(uint32 effectid, uint32 duration, uint32 finish_delay, bool zone_wide, + void SendSpellEffect(uint32 effect_id, uint32 duration, uint32 finish_delay, bool zone_wide, uint32 unk020, bool perm_effect = false, Client *c = nullptr); bool IsBeneficialAllowed(Mob *target); virtual int GetCasterLevel(uint16 spell_id); @@ -361,7 +361,7 @@ public: virtual void DoBuffTic(const Buffs_Struct &buff, int slot, Mob* caster = nullptr); void BuffFadeBySpellID(uint16 spell_id); void BuffFadeBySpellIDAndCaster(uint16 spell_id, uint16 caster_id); - void BuffFadeByEffect(int effectid, int skipslot = -1); + void BuffFadeByEffect(int effect_id, int skipslot = -1); void BuffFadeAll(); void BuffFadeNonPersistDeath(); void BuffFadeDetrimental(); @@ -412,8 +412,8 @@ public: inline virtual uint32 GetNimbusEffect1() const { return nimbus_effect1; } inline virtual uint32 GetNimbusEffect2() const { return nimbus_effect2; } inline virtual uint32 GetNimbusEffect3() const { return nimbus_effect3; } - void AddNimbusEffect(int effectid); - void RemoveNimbusEffect(int effectid); + void AddNimbusEffect(int effect_id); + void RemoveNimbusEffect(int effect_id); void RemoveAllNimbusEffects(); inline const glm::vec3& GetTargetRingLocation() const { return m_TargetRing; } inline float GetTargetRingX() const { return m_TargetRing.x; } @@ -814,7 +814,7 @@ public: int32 GetSkillDmgTaken(const EQ::skills::SkillType skill_used, ExtraAttackOptions *opts = nullptr); int32 GetPositionalDmgTaken(Mob *attacker); int32 GetPositionalDmgTakenAmt(Mob *attacker); - void DoKnockback(Mob *caster, uint32 pushback, uint32 pushup); + void DoKnockback(Mob *caster, uint32 push_back, uint32 push_up); int16 CalcResistChanceBonus(); int16 CalcFearResistChance(); void TrySpellOnKill(uint8 level, uint16 spell_id); @@ -1117,7 +1117,7 @@ public: //uint32 GetInstrumentMod(uint16 spell_id) const; uint32 GetInstrumentMod(uint16 spell_id); int CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level = 1, uint32 instrument_mod = 10, Mob *caster = nullptr, int ticsremaining = 0,uint16 casterid=0); - int CalcSpellEffectValue_formula(int formula, int base, int max, int caster_level, uint16 spell_id, int ticsremaining = 0); + int CalcSpellEffectValue_formula(int formula, int base_value, int max_value, int caster_level, uint16 spell_id, int ticsremaining = 0); virtual int CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, int caster_level2, Mob* caster1 = nullptr, Mob* caster2 = nullptr, int buffslot = -1); uint32 GetCastedSpellInvSlot() const { return casting_spell_inventory_slot; } @@ -1194,7 +1194,7 @@ public: inline void SetEmoteID(uint16 emote) { emoteid = emote; } inline uint16 GetEmoteID() { return emoteid; } - bool HasSpellEffect(int effectid); + bool HasSpellEffect(int effect_id); int mod_effect_value(int effect_value, uint16 spell_id, int effect_type, Mob* caster, uint16 caster_id); float mod_hit_chance(float chancetohit, EQ::skills::SkillType skillinuse, Mob* attacker); float mod_riposte_chance(float ripostchance, Mob* attacker); diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 8e7b2bcd5..a7cb29947 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -115,11 +115,11 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes, bool bInnates // should probably match that eventually. This should be good enough for now I guess .... if ( ( - (spells[AIspells[i].spellid].targettype == ST_HateList || spells[AIspells[i].spellid].targettype == ST_AETargetHateList) || + (spells[AIspells[i].spellid].target_type == ST_HateList || spells[AIspells[i].spellid].target_type == ST_AETargetHateList) || ( // note: I think this check is actually wrong and we should be checking range instead in all cases, BUT if range is 0, range check is skipped? Works for now - (spells[AIspells[i].spellid].targettype==ST_AECaster || spells[AIspells[i].spellid].targettype==ST_AEBard || spells[AIspells[i].spellid].targettype==ST_AEClientV1) - && dist2 <= spells[AIspells[i].spellid].aoerange*spells[AIspells[i].spellid].aoerange + (spells[AIspells[i].spellid].target_type==ST_AECaster || spells[AIspells[i].spellid].target_type==ST_AEBard || spells[AIspells[i].spellid].target_type==ST_AEClientV1) + && dist2 <= spells[AIspells[i].spellid].aoe_range*spells[AIspells[i].spellid].aoe_range ) || dist2 <= spells[AIspells[i].spellid].range*spells[AIspells[i].spellid].range ) @@ -135,7 +135,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes, bool bInnates switch (AIspells[i].type) { case SpellType_Heal: { if ( - (spells[AIspells[i].spellid].targettype == ST_Target || tar == this) + (spells[AIspells[i].spellid].target_type == ST_Target || tar == this) && tar->DontHealMeBefore() < Timer::GetCurrentTime() && !(tar->IsPet() && tar->GetOwner()->IsClient()) //no buffing PC's pets ) { @@ -174,7 +174,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes, bool bInnates } case SpellType_Buff: { if ( - (spells[AIspells[i].spellid].targettype == ST_Target || tar == this) + (spells[AIspells[i].spellid].target_type == ST_Target || tar == this) && tar->DontBuffMeBefore() < Timer::GetCurrentTime() && !tar->IsImmuneToSpell(AIspells[i].spellid, this) && tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0 @@ -2497,7 +2497,7 @@ create table npc_spells_entries ( */ bool IsSpellInList(DBnpcspells_Struct* spell_list, uint16 iSpellID); -bool IsSpellEffectInList(DBnpcspellseffects_Struct* spelleffect_list, uint16 iSpellEffectID, int32 base, int32 limit, int32 max); +bool IsSpellEffectInList(DBnpcspellseffects_Struct* spelleffect_list, uint16 iSpellEffectID, int32 base_value, int32 limit, int32 max_value); bool NPC::AI_AddNPCSpells(uint32 iDBSpellsID) { // ok, this function should load the list, and the parent list then shove them into the struct and sort @@ -2708,12 +2708,12 @@ bool NPC::AI_AddNPCSpellsEffects(uint32 iDBSpellsEffectsID) { if (parentlist) { for (i=0; inumentries; i++) { if (GetLevel() >= parentlist->entries[i].minlevel && GetLevel() <= parentlist->entries[i].maxlevel && parentlist->entries[i].spelleffectid > 0) { - if (!IsSpellEffectInList(spell_effects_list, parentlist->entries[i].spelleffectid, parentlist->entries[i].base, - parentlist->entries[i].limit, parentlist->entries[i].max)) + if (!IsSpellEffectInList(spell_effects_list, parentlist->entries[i].spelleffectid, parentlist->entries[i].base_value, + parentlist->entries[i].limit, parentlist->entries[i].max_value)) { AddSpellEffectToNPCList(parentlist->entries[i].spelleffectid, - parentlist->entries[i].base, parentlist->entries[i].limit, - parentlist->entries[i].max); + parentlist->entries[i].base_value, parentlist->entries[i].limit, + parentlist->entries[i].max_value); } } } @@ -2722,8 +2722,8 @@ bool NPC::AI_AddNPCSpellsEffects(uint32 iDBSpellsEffectsID) { for (i=0; inumentries; i++) { if (GetLevel() >= spell_effects_list->entries[i].minlevel && GetLevel() <= spell_effects_list->entries[i].maxlevel && spell_effects_list->entries[i].spelleffectid > 0) { AddSpellEffectToNPCList(spell_effects_list->entries[i].spelleffectid, - spell_effects_list->entries[i].base, spell_effects_list->entries[i].limit, - spell_effects_list->entries[i].max); + spell_effects_list->entries[i].base_value, spell_effects_list->entries[i].limit, + spell_effects_list->entries[i].max_value); } } @@ -2737,13 +2737,13 @@ void NPC::ApplyAISpellEffects(StatBonuses* newbon) for (int i = 0; i < AIspellsEffects.size(); i++) ApplySpellsBonuses(0, 0, newbon, 0, 0, 0, -1, 10, true, AIspellsEffects[i].spelleffectid, - AIspellsEffects[i].base, AIspellsEffects[i].limit, AIspellsEffects[i].max); + AIspellsEffects[i].base_value, AIspellsEffects[i].limit, AIspellsEffects[i].max_value); return; } // adds a spell to the list, taking into account priority and resorting list as needed. -void NPC::AddSpellEffectToNPCList(uint16 iSpellEffectID, int32 base, int32 limit, int32 max) +void NPC::AddSpellEffectToNPCList(uint16 iSpellEffectID, int32 base_value, int32 limit, int32 max_value) { if(!iSpellEffectID) @@ -2753,16 +2753,16 @@ void NPC::AddSpellEffectToNPCList(uint16 iSpellEffectID, int32 base, int32 limit AISpellsEffects_Struct t; t.spelleffectid = iSpellEffectID; - t.base = base; + t.base_value = base_value; t.limit = limit; - t.max = max; + t.max_value = max_value; AIspellsEffects.push_back(t); } -bool IsSpellEffectInList(DBnpcspellseffects_Struct* spelleffect_list, uint16 iSpellEffectID, int32 base, int32 limit, int32 max) { +bool IsSpellEffectInList(DBnpcspellseffects_Struct* spelleffect_list, uint16 iSpellEffectID, int32 base_value, int32 limit, int32 max_value) { for (uint32 i=0; i < spelleffect_list->numentries; i++) { - if (spelleffect_list->entries[i].spelleffectid == iSpellEffectID && spelleffect_list->entries[i].base == base - && spelleffect_list->entries[i].limit == limit && spelleffect_list->entries[i].max == max) + if (spelleffect_list->entries[i].spelleffectid == iSpellEffectID && spelleffect_list->entries[i].base_value == base_value + && spelleffect_list->entries[i].limit == limit && spelleffect_list->entries[i].max_value == max_value) return true; } return false; @@ -2922,7 +2922,7 @@ DBnpcspells_Struct *ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID) if (row[9]) entry.resist_adjust = atoi(row[9]); else if (IsValidSpell(spell_id)) - entry.resist_adjust = spells[spell_id].ResistDiff; + entry.resist_adjust = spells[spell_id].resist_difficulty; spell_set.entries.push_back(entry); } @@ -3015,9 +3015,9 @@ DBnpcspellseffects_Struct *ZoneDatabase::GetNPCSpellsEffects(uint32 iDBSpellsEff npc_spellseffects_cache[iDBSpellsEffectsID]->entries[entryIndex].spelleffectid = spell_effect_id; npc_spellseffects_cache[iDBSpellsEffectsID]->entries[entryIndex].minlevel = atoi(row[1]); npc_spellseffects_cache[iDBSpellsEffectsID]->entries[entryIndex].maxlevel = atoi(row[2]); - npc_spellseffects_cache[iDBSpellsEffectsID]->entries[entryIndex].base = atoi(row[3]); + npc_spellseffects_cache[iDBSpellsEffectsID]->entries[entryIndex].base_value = atoi(row[3]); npc_spellseffects_cache[iDBSpellsEffectsID]->entries[entryIndex].limit = atoi(row[4]); - npc_spellseffects_cache[iDBSpellsEffectsID]->entries[entryIndex].max = atoi(row[5]); + npc_spellseffects_cache[iDBSpellsEffectsID]->entries[entryIndex].max_value = atoi(row[5]); } return npc_spellseffects_cache[iDBSpellsEffectsID]; diff --git a/zone/npc.cpp b/zone/npc.cpp index 3b635ca57..ef9932ba3 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -1052,7 +1052,7 @@ void NPC::Depop(bool StartSpawnTimer) { bool NPC::DatabaseCastAccepted(int spell_id) { for (int i=0; i < EFFECT_COUNT; i++) { - switch(spells[spell_id].effectid[i]) { + switch(spells[spell_id].effect_id[i]) { case SE_Stamina: { if(IsEngaged() && GetHPRatio() < 100) return true; @@ -1062,7 +1062,7 @@ bool NPC::DatabaseCastAccepted(int spell_id) { } case SE_CurrentHPOnce: case SE_CurrentHP: { - if(this->GetHPRatio() < 100 && spells[spell_id].buffduration == 0) + if(this->GetHPRatio() < 100 && spells[spell_id].buff_duration == 0) return true; else return false; @@ -1095,7 +1095,7 @@ bool NPC::DatabaseCastAccepted(int spell_id) { break; } default: - if(spells[spell_id].goodEffect == 1 && !(spells[spell_id].buffduration == 0 && this->GetHPRatio() == 100) && !IsEngaged()) + if(spells[spell_id].good_effect == 1 && !(spells[spell_id].buff_duration == 0 && this->GetHPRatio() == 100) && !IsEngaged()) return true; return false; } diff --git a/zone/npc.h b/zone/npc.h index 2c43b79c1..6f0c221f4 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -70,9 +70,9 @@ struct AISpells_Struct { struct AISpellsEffects_Struct { uint16 spelleffectid; - int32 base; + int32 base_value; int32 limit; - int32 max; + int32 max_value; }; struct AISpellsVar_Struct { @@ -445,7 +445,7 @@ public: uint32 GetAdventureTemplate() const { return adventure_template_id; } void AddSpellToNPCList(int16 iPriority, uint16 iSpellID, uint32 iType, int16 iManaCost, int32 iRecastDelay, int16 iResistAdjust, int8 min_hp, int8 max_hp); - void AddSpellEffectToNPCList(uint16 iSpellEffectID, int32 base, int32 limit, int32 max); + void AddSpellEffectToNPCList(uint16 iSpellEffectID, int32 base_value, int32 limit, int32 max_value); void RemoveSpellFromNPCList(uint16 spell_id); Timer *GetRefaceTimer() const { return reface_timer; } const uint32 GetAltCurrencyType() const { return NPCTypedata->alt_currency_type; } diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index 4c32c5a87..f207b47b8 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -2800,7 +2800,7 @@ XS(XS_Mob_SpellFinished) { if (items > 4) { resist_diff = (int16) SvUV(ST(4)); } else { - resist_diff = spells[spell_id].ResistDiff; + resist_diff = spells[spell_id].resist_difficulty; } THIS->SpellFinished(spell_id, spell_target, EQ::spells::CastingSlot::Item, mana_cost, -1, resist_diff); @@ -2855,7 +2855,7 @@ XS(XS_Mob_BuffFadeByEffect) { Perl_croak(aTHX_ "Usage: Mob::BuffFadeByEffect(THIS, int effect_id, int skip_slot = -1)"); // @categories Script Utility, Spells and Disciplines { Mob *THIS; - int effectid = (int) SvIV(ST(1)); + int effect_id = (int) SvIV(ST(1)); int skipslot; VALIDATE_THIS_IS_MOB; if (items < 3) @@ -2864,7 +2864,7 @@ XS(XS_Mob_BuffFadeByEffect) { skipslot = (int) SvIV(ST(2)); } - THIS->BuffFadeByEffect(effectid, skipslot); + THIS->BuffFadeByEffect(effect_id, skipslot); } XSRETURN_EMPTY; } @@ -5093,8 +5093,8 @@ XS(XS_Mob_DoKnockback) { { Mob *THIS; Mob *caster; - uint32 pushback = (uint16) SvUV(ST(2)); - uint32 pushup = (uint16) SvUV(ST(2)); + uint32 push_back = (uint16) SvUV(ST(2)); + uint32 push_up = (uint16) SvUV(ST(2)); VALIDATE_THIS_IS_MOB; if (sv_derived_from(ST(1), "Mob")) { IV tmp = SvIV((SV *) SvRV(ST(1))); @@ -5104,7 +5104,7 @@ XS(XS_Mob_DoKnockback) { if (caster == nullptr) Perl_croak(aTHX_ "caster is nullptr, avoiding crash."); - THIS->DoKnockback(caster, pushback, pushup); + THIS->DoKnockback(caster, push_back, push_up); } XSRETURN_EMPTY; } @@ -5116,9 +5116,9 @@ XS(XS_Mob_RemoveNimbusEffect) { Perl_croak(aTHX_ "Usage: Mob::RemoveNimbusEffect(THIS, int32 effect_id)"); // @categories Script Utility { Mob *THIS; - int32 effectid = (int32) SvIV(ST(1)); + int32 effect_id = (int32) SvIV(ST(1)); VALIDATE_THIS_IS_MOB; - THIS->RemoveNimbusEffect(effectid); + THIS->RemoveNimbusEffect(effect_id); } XSRETURN_EMPTY; } diff --git a/zone/perl_spell.cpp b/zone/perl_spell.cpp new file mode 100644 index 000000000..85aa27d05 --- /dev/null +++ b/zone/perl_spell.cpp @@ -0,0 +1,2138 @@ +#include "../common/features.h" + +#ifdef EMBPERL_XS_CLASSES + +#include "../common/global_define.h" +#include "embperl.h" + +#ifdef seed +#undef seed +#endif + +#include "../common/spdat.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + +#define VALIDATE_THIS_IS_SPELL \ + do { \ + if (sv_derived_from(ST(0), "Spell")) { \ + IV tmp = SvIV((SV*)SvRV(ST(0))); \ + THIS = INT2PTR(SPDat_Spell_Struct*, tmp); \ + } else { \ + Perl_croak(aTHX_ "THIS is not of type SPDat_Spell_Struct"); \ + } \ + if (THIS == nullptr) { \ + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); \ + } \ + } while (0); + +XS(XS_Spell_GetActivated); +XS(XS_Spell_GetActivated) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetActivated(THIS)"); + { + SPDat_Spell_Struct* THIS; + int activated; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + activated = THIS->activated; + XSprePUSH; + PUSHi((IV) activated); + } + XSRETURN(1); +} + +XS(XS_Spell_GetAllowRest); +XS(XS_Spell_GetAllowRest) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetAllowRest(THIS)"); + { + SPDat_Spell_Struct* THIS; + bool allow_rest; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + allow_rest = THIS->allow_rest; + ST(0) = boolSV(allow_rest); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Spell_GetAOEDuration); +XS(XS_Spell_GetAOEDuration) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetAOEDuration(THIS)"); + { + SPDat_Spell_Struct* THIS; + uint32 aoe_duration; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + aoe_duration = THIS->aoe_duration; + XSprePUSH; + PUSHu((UV) aoe_duration); + } + XSRETURN(1); +} + +XS(XS_Spell_GetAOEMaxTargets); +XS(XS_Spell_GetAOEMaxTargets) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetAOEMaxTargets(THIS)"); + { + SPDat_Spell_Struct* THIS; + int aoe_max_targets; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + aoe_max_targets = THIS->aoe_max_targets; + XSprePUSH; + PUSHi((IV) aoe_max_targets); + } + XSRETURN(1); +} + +XS(XS_Spell_GetAOERange); +XS(XS_Spell_GetAOERange) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetAOERange(THIS)"); + { + SPDat_Spell_Struct* THIS; + float aoe_range; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + aoe_range = THIS->aoe_range; + XSprePUSH; + PUSHn((double) aoe_range); + } + XSRETURN(1); +} + +XS(XS_Spell_GetBaseDifficulty); +XS(XS_Spell_GetBaseDifficulty) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetBaseDifficulty(THIS)"); + { + SPDat_Spell_Struct* THIS; + int base_difficulty; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + base_difficulty = THIS->base_difficulty; + XSprePUSH; + PUSHi((IV) base_difficulty); + } + XSRETURN(1); +} + +XS(XS_Spell_GetBaseValue); +XS(XS_Spell_GetBaseValue) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Spell::GetBaseValue(THIS, uint8 slot)"); + { + SPDat_Spell_Struct* THIS; + int base_value; + uint8 slot = (uint8) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + base_value = THIS->base_value[slot]; + XSprePUSH; + PUSHi((IV) base_value); + } + XSRETURN(1); +} + +XS(XS_Spell_GetBonusHate); +XS(XS_Spell_GetBonusHate) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetBonusHate(THIS)"); + { + SPDat_Spell_Struct* THIS; + int bonus_hate; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + bonus_hate = THIS->bonus_hate; + XSprePUSH; + PUSHi((IV) bonus_hate); + } + XSRETURN(1); +} + +XS(XS_Spell_GetBuffDuration); +XS(XS_Spell_GetBuffDuration) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetBuffDuration(THIS)"); + { + SPDat_Spell_Struct* THIS; + uint32 buff_duration; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + buff_duration = THIS->buff_duration; + XSprePUSH; + PUSHu((UV) buff_duration); + } + XSRETURN(1); +} + +XS(XS_Spell_GetBuffDurationFormula); +XS(XS_Spell_GetBuffDurationFormula) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetBuffDurationFormula(THIS)"); + { + SPDat_Spell_Struct* THIS; + uint32 buff_duration_formula; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + buff_duration_formula = THIS->buff_duration_formula; + XSprePUSH; + PUSHu((UV) buff_duration_formula); + } + XSRETURN(1); +} + +XS(XS_Spell_GetCanCastInCombat); +XS(XS_Spell_GetCanCastInCombat) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetCanCastInCombat(THIS)"); + { + SPDat_Spell_Struct* THIS; + bool can_cast_in_combat; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + can_cast_in_combat = THIS->can_cast_in_combat; + ST(0) = boolSV(can_cast_in_combat); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Spell_GetCanCastOutOfCombat); +XS(XS_Spell_GetCanCastOutOfCombat) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetCanCastOutOfCombat(THIS)"); + { + SPDat_Spell_Struct* THIS; + bool can_cast_out_of_combat; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + can_cast_out_of_combat = THIS->can_cast_out_of_combat; + ST(0) = boolSV(can_cast_out_of_combat); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Spell_GetCanMGB); +XS(XS_Spell_GetCanMGB) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetCanMGB(THIS)"); + { + SPDat_Spell_Struct* THIS; + bool can_mgb; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + can_mgb = THIS->can_mgb; + ST(0) = boolSV(can_mgb); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Spell_GetCastNotStanding); +XS(XS_Spell_GetCastNotStanding) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetCastNotStanding(THIS)"); + { + SPDat_Spell_Struct* THIS; + bool cast_not_standing; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + cast_not_standing = THIS->cast_not_standing; + ST(0) = boolSV(cast_not_standing); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Spell_GetCastOnOther); +XS(XS_Spell_GetCastOnOther) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetCastOnOther(THIS)"); + { + SPDat_Spell_Struct* THIS; + std::string cast_on_other; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + cast_on_other = THIS->cast_on_other; + sv_setpv(TARG, cast_on_other.c_str()); + XSprePUSH; + PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_Spell_GetCastOnYou); +XS(XS_Spell_GetCastOnYou) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetCastOnYou(THIS)"); + { + SPDat_Spell_Struct* THIS; + std::string cast_on_you; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + cast_on_you = THIS->cast_on_you; + sv_setpv(TARG, cast_on_you.c_str()); + XSprePUSH; + PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_Spell_GetCastRestriction); +XS(XS_Spell_GetCastRestriction) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetCastRestriction(THIS)"); + { + SPDat_Spell_Struct* THIS; + int cast_restriction; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + cast_restriction = THIS->cast_restriction; + XSprePUSH; + PUSHi((IV) cast_restriction); + } + XSRETURN(1); +} + +XS(XS_Spell_GetCastTime); +XS(XS_Spell_GetCastTime) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetCastTime(THIS)"); + { + SPDat_Spell_Struct* THIS; + uint32 cast_time; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + cast_time = THIS->cast_time; + XSprePUSH; + PUSHu((UV) cast_time); + } + XSRETURN(1); +} + +XS(XS_Spell_GetCasterRequirementID); +XS(XS_Spell_GetCasterRequirementID) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetCasterRequirementID(THIS)"); + { + SPDat_Spell_Struct* THIS; + int caster_requirement_id; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + caster_requirement_id = THIS->caster_requirement_id; + XSprePUSH; + PUSHi((IV) caster_requirement_id); + } + XSRETURN(1); +} + +XS(XS_Spell_GetCastingAnimation); +XS(XS_Spell_GetCastingAnimation) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetCastingAnimation(THIS)"); + { + SPDat_Spell_Struct* THIS; + uint8 casting_animation; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + casting_animation = THIS->casting_animation; + XSprePUSH; + PUSHu((UV) casting_animation); + } + XSRETURN(1); +} + +XS(XS_Spell_GetClasses); +XS(XS_Spell_GetClasses) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Spell::GetClasses(THIS, uint8 slot)"); + { + SPDat_Spell_Struct* THIS; + uint8 classes; + uint8 slot = (uint8) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + classes = THIS->classes[slot]; + XSprePUSH; + PUSHu((UV) classes); + } + XSRETURN(1); +} + +XS(XS_Spell_GetComponent); +XS(XS_Spell_GetComponent) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Spell::GetComponent(THIS, uint8 slot)"); + { + SPDat_Spell_Struct* THIS; + int component; + uint8 slot = (uint8) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + component = THIS->component[slot]; + XSprePUSH; + PUSHi((IV) component); + } + XSRETURN(1); +} + +XS(XS_Spell_GetComponentCount); +XS(XS_Spell_GetComponentCount) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Spell::GetComponentCount(THIS, uint8 slot)"); + { + SPDat_Spell_Struct* THIS; + int component_count; + uint8 slot = (uint8) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + component_count = THIS->component_count[slot]; + XSprePUSH; + PUSHi((IV) component_count); + } + XSRETURN(1); +} + +XS(XS_Spell_GetDeities); +XS(XS_Spell_GetDeities) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Spell::GetDeities(THIS, uint8 slot)"); + { + SPDat_Spell_Struct* THIS; + int8 deities; + uint8 slot = (uint8) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + deities = THIS->deities[slot]; + XSprePUSH; + PUSHi((IV) deities); + } + XSRETURN(1); +} + +XS(XS_Spell_GetDeityAgnostic); +XS(XS_Spell_GetDeityAgnostic) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetDeityAgnostic(THIS)"); + { + SPDat_Spell_Struct* THIS; + int8 deity_agnostic; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + deity_agnostic = THIS->deity_agnostic; + XSprePUSH; + PUSHi((IV) deity_agnostic); + } + XSRETURN(1); +} + +XS(XS_Spell_GetDescriptionID); +XS(XS_Spell_GetDescriptionID) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetDescriptionID(THIS)"); + { + SPDat_Spell_Struct* THIS; + int description_id; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + description_id = THIS->description_id; + XSprePUSH; + PUSHi((IV) description_id); + } + XSRETURN(1); +} + +XS(XS_Spell_GetDirectionalEnd); +XS(XS_Spell_GetDirectionalEnd) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetDirectionalEnd(THIS)"); + { + SPDat_Spell_Struct* THIS; + float directional_end; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + directional_end = THIS->directional_end; + XSprePUSH; + PUSHn((double) directional_end); + } + XSRETURN(1); +} + +XS(XS_Spell_GetDirectionalStart); +XS(XS_Spell_GetDirectionalStart) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetDirectionalStart(THIS)"); + { + SPDat_Spell_Struct* THIS; + float directional_start; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + directional_start = THIS->directional_start; + XSprePUSH; + PUSHn((double) directional_start); + } + XSRETURN(1); +} + +XS(XS_Spell_GetDisallowSit); +XS(XS_Spell_GetDisallowSit) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetDisallowSit(THIS)"); + { + SPDat_Spell_Struct* THIS; + int8 disallow_sit; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + disallow_sit = THIS->disallow_sit; + XSprePUSH; + PUSHi((IV) disallow_sit); + } + XSRETURN(1); +} + +XS(XS_Spell_GetDispelFlag); +XS(XS_Spell_GetDispelFlag) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetDispelFlag(THIS)"); + { + SPDat_Spell_Struct* THIS; + int dispel_flag; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + dispel_flag = THIS->dispel_flag; + XSprePUSH; + PUSHi((IV) dispel_flag); + } + XSRETURN(1); +} + +XS(XS_Spell_GetEffectDescriptionID); +XS(XS_Spell_GetEffectDescriptionID) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetEffectDescriptionID(THIS)"); + { + SPDat_Spell_Struct* THIS; + int effect_description_id; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + effect_description_id = THIS->effect_description_id; + XSprePUSH; + PUSHi((IV) effect_description_id); + } + XSRETURN(1); +} + +XS(XS_Spell_GetEffectID); +XS(XS_Spell_GetEffectID) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Spell::GetEffectID(THIS, uint8 slot)"); + { + SPDat_Spell_Struct* THIS; + int effect_id; + uint8 slot = (uint8) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + effect_id = THIS->effect_id[slot]; + XSprePUSH; + PUSHi((IV) effect_id); + } + XSRETURN(1); +} + +XS(XS_Spell_GetEnduranceCost); +XS(XS_Spell_GetEnduranceCost) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetEnduranceCost(THIS)"); + { + SPDat_Spell_Struct* THIS; + int endurance_cost; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + endurance_cost = THIS->endurance_cost; + XSprePUSH; + PUSHi((IV) endurance_cost); + } + XSRETURN(1); +} + +XS(XS_Spell_GetEnduranceUpkeep); +XS(XS_Spell_GetEnduranceUpkeep) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetEnduranceUpkeep(THIS)"); + { + SPDat_Spell_Struct* THIS; + int endurance_upkeep; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + endurance_upkeep = THIS->endurance_upkeep; + XSprePUSH; + PUSHi((IV) endurance_upkeep); + } + XSRETURN(1); +} + +XS(XS_Spell_GetEnvironmentType); +XS(XS_Spell_GetEnvironmentType) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetEnvironmentType(THIS)"); + { + SPDat_Spell_Struct* THIS; + int8 environment_type; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + environment_type = THIS->environment_type; + XSprePUSH; + PUSHi((IV) environment_type); + } + XSRETURN(1); +} + +XS(XS_Spell_GetFeedbackable); +XS(XS_Spell_GetFeedbackable) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetFeedbackable(THIS)"); + { + SPDat_Spell_Struct* THIS; + bool feedbackable; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + feedbackable = THIS->feedbackable; + ST(0) = boolSV(feedbackable); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Spell_GetFormula); +XS(XS_Spell_GetFormula) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Spell::GetFormula(THIS, uint8 slot)"); + { + SPDat_Spell_Struct* THIS; + uint16 formula; + uint8 slot = (uint8) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + formula = THIS->formula[slot]; + XSprePUSH; + PUSHu((UV) formula); + } + XSRETURN(1); +} + +XS(XS_Spell_GetGoodEffect); +XS(XS_Spell_GetGoodEffect) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetGoodEffect(THIS)"); + { + SPDat_Spell_Struct* THIS; + int8 good_effect; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + good_effect = THIS->good_effect; + XSprePUSH; + PUSHi((IV) good_effect); + } + XSRETURN(1); +} + +XS(XS_Spell_GetHateAdded); +XS(XS_Spell_GetHateAdded) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetHateAdded(THIS)"); + { + SPDat_Spell_Struct* THIS; + int hate_added; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + hate_added = THIS->hate_added; + XSprePUSH; + PUSHi((IV) hate_added); + } + XSRETURN(1); +} + +XS(XS_Spell_GetHitNumber); +XS(XS_Spell_GetHitNumber) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetHitNumber(THIS)"); + { + SPDat_Spell_Struct* THIS; + int hit_number; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + hit_number = THIS->hit_number; + XSprePUSH; + PUSHi((IV) hit_number); + } + XSRETURN(1); +} + +XS(XS_Spell_GetHitNumberType); +XS(XS_Spell_GetHitNumberType) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetHitNumberType(THIS)"); + { + SPDat_Spell_Struct* THIS; + int hit_number_type; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + hit_number_type = THIS->hit_number_type; + XSprePUSH; + PUSHi((IV) hit_number_type); + } + XSRETURN(1); +} + +XS(XS_Spell_GetID); +XS(XS_Spell_GetID) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetID(THIS)"); + { + SPDat_Spell_Struct* THIS; + int id; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + id = THIS->id; + XSprePUSH; + PUSHi((IV) id); + } + XSRETURN(1); +} + +XS(XS_Spell_GetIsDiscipline); +XS(XS_Spell_GetIsDiscipline) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetIsDiscipline(THIS)"); + { + SPDat_Spell_Struct* THIS; + bool is_discipline; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + is_discipline = THIS->is_discipline; + ST(0) = boolSV(is_discipline); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Spell_GetLDoNTrap); +XS(XS_Spell_GetLDoNTrap) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetLDoNTrap(THIS)"); + { + SPDat_Spell_Struct* THIS; + bool ldon_trap; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + ldon_trap = THIS->ldon_trap; + ST(0) = boolSV(ldon_trap); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Spell_GetLimitValue); +XS(XS_Spell_GetLimitValue) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Spell::GetLimitValue(THIS, uint8 slot)"); + { + SPDat_Spell_Struct* THIS; + int limit_value; + uint8 slot = (uint8) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + limit_value = THIS->limit_value[slot]; + XSprePUSH; + PUSHi((IV) limit_value); + } + XSRETURN(1); +} + +XS(XS_Spell_GetMana); +XS(XS_Spell_GetMana) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetMana(THIS)"); + { + SPDat_Spell_Struct* THIS; + uint16 mana; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + mana = THIS->mana; + XSprePUSH; + PUSHu((UV) mana); + } + XSRETURN(1); +} + +XS(XS_Spell_GetMaxDistance); +XS(XS_Spell_GetMaxDistance) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetMaxDistance(THIS)"); + { + SPDat_Spell_Struct* THIS; + float max_distance; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + max_distance = THIS->max_distance; + XSprePUSH; + PUSHn((double) max_distance); + } + XSRETURN(1); +} + +XS(XS_Spell_GetMaxDistanceMod); +XS(XS_Spell_GetMaxDistanceMod) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetMaxDistanceMod(THIS)"); + { + SPDat_Spell_Struct* THIS; + float max_distance_mod; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + max_distance_mod = THIS->max_distance_mod; + XSprePUSH; + PUSHn((double) max_distance_mod); + } + XSRETURN(1); +} + +XS(XS_Spell_GetMaxResist); +XS(XS_Spell_GetMaxResist) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetMaxResist(THIS)"); + { + SPDat_Spell_Struct* THIS; + int max_resist; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + max_resist = THIS->max_resist; + XSprePUSH; + PUSHi((IV) max_resist); + } + XSRETURN(1); +} + +XS(XS_Spell_GetMaxValue); +XS(XS_Spell_GetMaxValue) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Spell::GetMaxValue(THIS, uint8 slot)"); + { + SPDat_Spell_Struct* THIS; + int max_value; + uint8 slot = (uint8) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + max_value = THIS->max_value[slot]; + XSprePUSH; + PUSHi((IV) max_value); + } + XSRETURN(1); +} + +XS(XS_Spell_GetMinDistance); +XS(XS_Spell_GetMinDistance) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetMinDistance(THIS)"); + { + SPDat_Spell_Struct* THIS; + float min_distance; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + min_distance = THIS->min_distance; + XSprePUSH; + PUSHn((double) min_distance); + } + XSRETURN(1); +} + +XS(XS_Spell_GetMinDistanceMod); +XS(XS_Spell_GetMinDistanceMod) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetMinDistanceMod(THIS)"); + { + SPDat_Spell_Struct* THIS; + float min_distance_mod; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + min_distance_mod = THIS->min_distance_mod; + XSprePUSH; + PUSHn((double) min_distance_mod); + } + XSRETURN(1); +} + +XS(XS_Spell_GetMinRange); +XS(XS_Spell_GetMinRange) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetMinRange(THIS)"); + { + SPDat_Spell_Struct* THIS; + float min_range; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + min_range = THIS->min_range; + XSprePUSH; + PUSHn((double) min_range); + } + XSRETURN(1); +} + +XS(XS_Spell_GetMinResist); +XS(XS_Spell_GetMinResist) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetMinResist(THIS)"); + { + SPDat_Spell_Struct* THIS; + int min_resist; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + min_resist = THIS->min_resist; + XSprePUSH; + PUSHi((IV) min_resist); + } + XSRETURN(1); +} + +XS(XS_Spell_GetName); +XS(XS_Spell_GetName) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetName(THIS)"); + { + SPDat_Spell_Struct* THIS; + std::string name; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + name = THIS->name; + sv_setpv(TARG, name.c_str()); + XSprePUSH; + PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_Spell_GetNewIcon); +XS(XS_Spell_GetNewIcon) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetNewIcon(THIS)"); + { + SPDat_Spell_Struct* THIS; + int16 new_icon; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + new_icon = THIS->new_icon; + XSprePUSH; + PUSHi((IV) new_icon); + } + XSRETURN(1); +} + +XS(XS_Spell_GetNimbusEffect); +XS(XS_Spell_GetNimbusEffect) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetNimbusEffect(THIS)"); + { + SPDat_Spell_Struct* THIS; + int nimbus_effect; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + nimbus_effect = THIS->nimbus_effect; + XSprePUSH; + PUSHi((IV) nimbus_effect); + } + XSRETURN(1); +} + +XS(XS_Spell_GetNoBlock); +XS(XS_Spell_GetNoBlock) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetNoBlock(THIS)"); + { + SPDat_Spell_Struct* THIS; + bool no_block; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + no_block = THIS->no_block; + ST(0) = boolSV(no_block); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Spell_GetNoDetrimentalSpellAggro); +XS(XS_Spell_GetNoDetrimentalSpellAggro) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetNoDetrimentalSpellAggro(THIS)"); + { + SPDat_Spell_Struct* THIS; + bool no_detrimental_spell_aggro; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + no_detrimental_spell_aggro = THIS->no_detrimental_spell_aggro; + ST(0) = boolSV(no_detrimental_spell_aggro); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Spell_GetNoExpendReagent); +XS(XS_Spell_GetNoExpendReagent) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Spell::GetNoExpendReagent(THIS, uint8 slot)"); + { + SPDat_Spell_Struct* THIS; + int no_expend_reagent; + uint8 slot = (uint8) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + no_expend_reagent = THIS->no_expend_reagent[slot]; + XSprePUSH; + PUSHi((IV) no_expend_reagent); + } + XSRETURN(1); +} + +XS(XS_Spell_GetNoHealDamageItemMod); +XS(XS_Spell_GetNoHealDamageItemMod) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetNoHealDamageItemMod(THIS)"); + { + SPDat_Spell_Struct* THIS; + int no_heal_damage_item_mod; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + no_heal_damage_item_mod = THIS->no_heal_damage_item_mod; + XSprePUSH; + PUSHi((IV) no_heal_damage_item_mod); + } + XSRETURN(1); +} + +XS(XS_Spell_GetNoPartialResist); +XS(XS_Spell_GetNoPartialResist) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetNoPartialResist(THIS)"); + { + SPDat_Spell_Struct* THIS; + bool no_partial_resist; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + no_partial_resist = THIS->no_partial_resist; + ST(0) = boolSV(no_partial_resist); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Spell_GetNoRemove); +XS(XS_Spell_GetNoRemove) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetNoRemove(THIS)"); + { + SPDat_Spell_Struct* THIS; + bool no_remove; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + no_remove = THIS->no_remove; + ST(0) = boolSV(no_remove); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Spell_GetNoResist); +XS(XS_Spell_GetNoResist) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetNoResist(THIS)"); + { + SPDat_Spell_Struct* THIS; + int no_resist; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + no_resist = THIS->no_resist; + XSprePUSH; + PUSHi((IV) no_resist); + } + XSRETURN(1); +} + +XS(XS_Spell_GetNotFocusable); +XS(XS_Spell_GetNotFocusable) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetNotFocusable(THIS)"); + { + SPDat_Spell_Struct* THIS; + bool not_focusable; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + not_focusable = THIS->not_focusable; + ST(0) = boolSV(not_focusable); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Spell_GetNPCNoLOS); +XS(XS_Spell_GetNPCNoLOS) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetNPCNoLOS(THIS)"); + { + SPDat_Spell_Struct* THIS; + bool npc_no_los; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + npc_no_los = THIS->npc_no_los; + ST(0) = boolSV(npc_no_los); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Spell_GetOtherCasts); +XS(XS_Spell_GetOtherCasts) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetOtherCasts(THIS)"); + { + SPDat_Spell_Struct* THIS; + std::string other_casts; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + other_casts = THIS->other_casts; + sv_setpv(TARG, other_casts.c_str()); + XSprePUSH; + PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_Spell_GetOverrideCritChance); +XS(XS_Spell_GetOverrideCritChance) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetOverrideCritChance(THIS)"); + { + SPDat_Spell_Struct* THIS; + int override_crit_chance; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + override_crit_chance = THIS->override_crit_chance; + XSprePUSH; + PUSHi((IV) override_crit_chance); + } + XSRETURN(1); +} + +XS(XS_Spell_GetPCNPCOnlyFlag); +XS(XS_Spell_GetPCNPCOnlyFlag) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetPCNPCOnlyFlag(THIS)"); + { + SPDat_Spell_Struct* THIS; + int pcnpc_only_flag; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + pcnpc_only_flag = THIS->pcnpc_only_flag; + XSprePUSH; + PUSHi((IV) pcnpc_only_flag); + } + XSRETURN(1); +} + +XS(XS_Spell_GetPersistDeath); +XS(XS_Spell_GetPersistDeath) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetPersistDeath(THIS)"); + { + SPDat_Spell_Struct* THIS; + bool persist_death; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + persist_death = THIS->persist_death; + ST(0) = boolSV(persist_death); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Spell_GetPlayer_1); +XS(XS_Spell_GetPlayer_1) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetPlayer_1(THIS)"); + { + SPDat_Spell_Struct* THIS; + std::string player_1; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + player_1 = THIS->player_1; + sv_setpv(TARG, player_1.c_str()); + XSprePUSH; + PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_Spell_GetPushBack); +XS(XS_Spell_GetPushBack) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetPushBack(THIS)"); + { + SPDat_Spell_Struct* THIS; + float push_back; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + push_back = THIS->push_back; + XSprePUSH; + PUSHn((double) push_back); + } + XSRETURN(1); +} + +XS(XS_Spell_GetPushUp); +XS(XS_Spell_GetPushUp) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetPushUp(THIS)"); + { + SPDat_Spell_Struct* THIS; + float push_up; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + push_up = THIS->push_up; + XSprePUSH; + PUSHn((double) push_up); + } + XSRETURN(1); +} + +XS(XS_Spell_GetPVPDuration); +XS(XS_Spell_GetPVPDuration) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetPVPDuration(THIS)"); + { + SPDat_Spell_Struct* THIS; + int pvp_duration; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + pvp_duration = THIS->pvp_duration; + XSprePUSH; + PUSHi((IV) pvp_duration); + } + XSRETURN(1); +} + +XS(XS_Spell_GetPVPDurationCap); +XS(XS_Spell_GetPVPDurationCap) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetPVPDurationCap(THIS)"); + { + SPDat_Spell_Struct* THIS; + int pvp_duration_cap; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + pvp_duration_cap = THIS->pvp_duration_cap; + XSprePUSH; + PUSHi((IV) pvp_duration_cap); + } + XSRETURN(1); +} + +XS(XS_Spell_GetPVPResistBase); +XS(XS_Spell_GetPVPResistBase) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetPVPResistBase(THIS)"); + { + SPDat_Spell_Struct* THIS; + int pvp_resist_base; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + pvp_resist_base = THIS->pvp_resist_base; + XSprePUSH; + PUSHi((IV) pvp_resist_base); + } + XSRETURN(1); +} + +XS(XS_Spell_GetPVPResistCap); +XS(XS_Spell_GetPVPResistCap) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetPVPResistCap(THIS)"); + { + SPDat_Spell_Struct* THIS; + int pvp_resist_cap; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + pvp_resist_cap = THIS->pvp_resist_cap; + XSprePUSH; + PUSHi((IV) pvp_resist_cap); + } + XSRETURN(1); +} + +XS(XS_Spell_GetPVPResistPerLevel); +XS(XS_Spell_GetPVPResistPerLevel) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetPVPResistPerLevel(THIS)"); + { + SPDat_Spell_Struct* THIS; + int pvp_resist_per_level; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + pvp_resist_per_level = THIS->pvp_resist_per_level; + XSprePUSH; + PUSHi((IV) pvp_resist_per_level); + } + XSRETURN(1); +} + +XS(XS_Spell_GetRange); +XS(XS_Spell_GetRange) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetRange(THIS)"); + { + SPDat_Spell_Struct* THIS; + float range; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + range = THIS->range; + XSprePUSH; + PUSHn((double) range); + } + XSRETURN(1); +} + +XS(XS_Spell_GetRank); +XS(XS_Spell_GetRank) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetRank(THIS)"); + { + SPDat_Spell_Struct* THIS; + int rank; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + rank = THIS->rank; + XSprePUSH; + PUSHi((IV) rank); + } + XSRETURN(1); +} + +XS(XS_Spell_GetRecastTime); +XS(XS_Spell_GetRecastTime) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetRecastTime(THIS)"); + { + SPDat_Spell_Struct* THIS; + uint32 recast_time; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + recast_time = THIS->recast_time; + XSprePUSH; + PUSHu((UV) recast_time); + } + XSRETURN(1); +} + +XS(XS_Spell_GetRecourseLink); +XS(XS_Spell_GetRecourseLink) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetRecourseLink(THIS)"); + { + SPDat_Spell_Struct* THIS; + uint16 recourse_link; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + recourse_link = THIS->recourse_link; + XSprePUSH; + PUSHu((UV) recourse_link); + } + XSRETURN(1); +} + +XS(XS_Spell_GetRecoveryTime); +XS(XS_Spell_GetRecoveryTime) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetRecoveryTime(THIS)"); + { + SPDat_Spell_Struct* THIS; + uint32 recovery_time; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + recovery_time = THIS->recovery_time; + XSprePUSH; + PUSHu((UV) recovery_time); + } + XSRETURN(1); +} + +XS(XS_Spell_GetReflectable); +XS(XS_Spell_GetReflectable) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetReflectable(THIS)"); + { + SPDat_Spell_Struct* THIS; + bool reflectable; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + reflectable = THIS->reflectable; + ST(0) = boolSV(reflectable); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Spell_GetResistDifficulty); +XS(XS_Spell_GetResistDifficulty) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetResistDifficulty(THIS)"); + { + SPDat_Spell_Struct* THIS; + int16 resist_difficulty; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + resist_difficulty = THIS->resist_difficulty; + XSprePUSH; + PUSHi((IV) resist_difficulty); + } + XSRETURN(1); +} + +XS(XS_Spell_GetResistType); +XS(XS_Spell_GetResistType) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetResistType(THIS)"); + { + SPDat_Spell_Struct* THIS; + int resist_type; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + resist_type = THIS->resist_type; + XSprePUSH; + PUSHi((IV) resist_type); + } + XSRETURN(1); +} + +XS(XS_Spell_GetShortBuffBox); +XS(XS_Spell_GetShortBuffBox) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetShortBuffBox(THIS)"); + { + SPDat_Spell_Struct* THIS; + int8 short_buff_box; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + short_buff_box = THIS->short_buff_box; + XSprePUSH; + PUSHi((IV) short_buff_box); + } + XSRETURN(1); +} + +XS(XS_Spell_GetSkill); +XS(XS_Spell_GetSkill) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetSkill(THIS)"); + { + SPDat_Spell_Struct* THIS; + EQ::skills::SkillType skill; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + skill = THIS->skill; + XSprePUSH; + PUSHi((IV) skill); + } + XSRETURN(1); +} + +XS(XS_Spell_GetSneak); +XS(XS_Spell_GetSneak) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetSneak(THIS)"); + { + SPDat_Spell_Struct* THIS; + bool sneak; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + sneak = THIS->sneak; + ST(0) = boolSV(sneak); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Spell_GetSongCap); +XS(XS_Spell_GetSongCap) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetSongCap(THIS)"); + { + SPDat_Spell_Struct* THIS; + int song_cap; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + song_cap = THIS->song_cap; + XSprePUSH; + PUSHi((IV) song_cap); + } + XSRETURN(1); +} + +XS(XS_Spell_GetSpellAffectIndex); +XS(XS_Spell_GetSpellAffectIndex) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetSpellAffectIndex(THIS)"); + { + SPDat_Spell_Struct* THIS; + uint16 spell_affect_index; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + spell_affect_index = THIS->spell_affect_index; + XSprePUSH; + PUSHu((UV) spell_affect_index); + } + XSRETURN(1); +} + +XS(XS_Spell_GetSpellCategory); +XS(XS_Spell_GetSpellCategory) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetSpellCategory(THIS)"); + { + SPDat_Spell_Struct* THIS; + int spell_category; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + spell_category = THIS->spell_category; + XSprePUSH; + PUSHi((IV) spell_category); + } + XSRETURN(1); +} + +XS(XS_Spell_GetSpellClass); +XS(XS_Spell_GetSpellClass) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetSpellClass(THIS)"); + { + SPDat_Spell_Struct* THIS; + int spell_class; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + spell_class = THIS->spell_class; + XSprePUSH; + PUSHi((IV) spell_class); + } + XSRETURN(1); +} + +XS(XS_Spell_GetSpellFades); +XS(XS_Spell_GetSpellFades) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetSpellFades(THIS)"); + { + SPDat_Spell_Struct* THIS; + std::string spell_fades; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + spell_fades = THIS->spell_fades; + sv_setpv(TARG, spell_fades.c_str()); + XSprePUSH; + PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_Spell_GetSpellGroup); +XS(XS_Spell_GetSpellGroup) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetSpellGroup(THIS)"); + { + SPDat_Spell_Struct* THIS; + int spell_group; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + spell_group = THIS->spell_group; + XSprePUSH; + PUSHi((IV) spell_group); + } + XSRETURN(1); +} + +XS(XS_Spell_GetSpellSubclass); +XS(XS_Spell_GetSpellSubclass) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetSpellSubclass(THIS)"); + { + SPDat_Spell_Struct* THIS; + int spell_subclass; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + spell_subclass = THIS->spell_subclass; + XSprePUSH; + PUSHi((IV) spell_subclass); + } + XSRETURN(1); +} + +XS(XS_Spell_GetSuspendable); +XS(XS_Spell_GetSuspendable) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetSuspendable(THIS)"); + { + SPDat_Spell_Struct* THIS; + bool suspendable; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + suspendable = THIS->suspendable; + ST(0) = boolSV(suspendable); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Spell_GetTargetType); +XS(XS_Spell_GetTargetType) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetTargetType(THIS)"); + { + SPDat_Spell_Struct* THIS; + SpellTargetType target_type; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + target_type = THIS->target_type; + XSprePUSH; + PUSHi((IV) target_type); + } + XSRETURN(1); +} + +XS(XS_Spell_GetTeleportZone); +XS(XS_Spell_GetTeleportZone) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetTeleportZone(THIS)"); + { + SPDat_Spell_Struct* THIS; + std::string teleport_zone; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + teleport_zone = THIS->teleport_zone; + sv_setpv(TARG, teleport_zone.c_str()); + XSprePUSH; + PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_Spell_GetTimeOfDay); +XS(XS_Spell_GetTimeOfDay) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetTimeOfDay(THIS)"); + { + SPDat_Spell_Struct* THIS; + int8 time_of_day; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + time_of_day = THIS->time_of_day; + XSprePUSH; + PUSHi((IV) time_of_day); + } + XSRETURN(1); +} + +XS(XS_Spell_GetTimerID); +XS(XS_Spell_GetTimerID) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetTimerID(THIS)"); + { + SPDat_Spell_Struct* THIS; + int8 timer_id; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + timer_id = THIS->timer_id; + XSprePUSH; + PUSHi((IV) timer_id); + } + XSRETURN(1); +} + +XS(XS_Spell_GetTypeDescriptionID); +XS(XS_Spell_GetTypeDescriptionID) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetTypeDescriptionID(THIS)"); + { + SPDat_Spell_Struct* THIS; + int type_description_id; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + type_description_id = THIS->type_description_id; + XSprePUSH; + PUSHi((IV) type_description_id); + } + XSRETURN(1); +} + +XS(XS_Spell_GetUninterruptable); +XS(XS_Spell_GetUninterruptable) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetUninterruptable(THIS)"); + { + SPDat_Spell_Struct* THIS; + bool uninterruptable; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + uninterruptable = THIS->uninterruptable; + ST(0) = boolSV(uninterruptable); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Spell_GetUnstackableDOT); +XS(XS_Spell_GetUnstackableDOT) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetUnstackableDOT(THIS)"); + { + SPDat_Spell_Struct* THIS; + bool unstackable_dot; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + unstackable_dot = THIS->unstackable_dot; + ST(0) = boolSV(unstackable_dot); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Spell_GetViralRange); +XS(XS_Spell_GetViralRange) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetViralRange(THIS)"); + { + SPDat_Spell_Struct* THIS; + int viral_range; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + viral_range = THIS->viral_range; + XSprePUSH; + PUSHi((IV) viral_range); + } + XSRETURN(1); +} + +XS(XS_Spell_GetViralTargets); +XS(XS_Spell_GetViralTargets) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetViralTargets(THIS)"); + { + SPDat_Spell_Struct* THIS; + uint8 viral_targets; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + viral_targets = THIS->viral_targets; + XSprePUSH; + PUSHu((UV) viral_targets); + } + XSRETURN(1); +} + +XS(XS_Spell_GetViralTimer); +XS(XS_Spell_GetViralTimer) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetViralTimer(THIS)"); + { + SPDat_Spell_Struct* THIS; + uint8 viral_timer; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + viral_timer = THIS->viral_timer; + XSprePUSH; + PUSHu((UV) viral_timer); + } + XSRETURN(1); +} + +XS(XS_Spell_GetYouCast); +XS(XS_Spell_GetYouCast) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetYouCast(THIS)"); + { + SPDat_Spell_Struct* THIS; + std::string you_cast; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + you_cast = THIS->you_cast; + sv_setpv(TARG, you_cast.c_str()); + XSprePUSH; + PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_Spell_GetZoneType); +XS(XS_Spell_GetZoneType) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Spell::GetZoneType(THIS)"); + { + SPDat_Spell_Struct* THIS; + int8 zone_type; + dXSTARG; + VALIDATE_THIS_IS_SPELL; + + zone_type = THIS->zone_type; + XSprePUSH; + PUSHi((IV) zone_type); + } + XSRETURN(1); +} + +#ifdef __cplusplus +extern "C" +#endif + +XS(boot_Spell); +XS(boot_Spell) { + dXSARGS; + char file[256]; + strncpy(file, __FILE__, 256); + file[255] = 0; + if (items != 1) + fprintf(stderr, "boot_Spell does not take any arguments."); + + char buf[128]; + XS_VERSION_BOOTCHECK; + newXSproto(strcpy(buf, "GetActivated"), XS_Spell_GetActivated, file, "$"); + newXSproto(strcpy(buf, "GetAllowRest"), XS_Spell_GetAllowRest, file, "$"); + newXSproto(strcpy(buf, "GetAOEDuration"), XS_Spell_GetAOEDuration, file, "$"); + newXSproto(strcpy(buf, "GetAOEMaxTargets"), XS_Spell_GetAOEMaxTargets, file, "$"); + newXSproto(strcpy(buf, "GetAOERange"), XS_Spell_GetAOERange, file, "$"); + newXSproto(strcpy(buf, "GetBaseDifficulty"), XS_Spell_GetBaseDifficulty, file, "$"); + newXSproto(strcpy(buf, "GetBaseValue"), XS_Spell_GetBaseValue, file, "$$"); + newXSproto(strcpy(buf, "GetBonusHate"), XS_Spell_GetBonusHate, file, "$"); + newXSproto(strcpy(buf, "GetBuffDuration"), XS_Spell_GetBuffDuration, file, "$"); + newXSproto(strcpy(buf, "GetBuffDurationFormula"), XS_Spell_GetBuffDurationFormula, file, "$"); + newXSproto(strcpy(buf, "GetCanCastInCombat"), XS_Spell_GetCanCastInCombat, file, "$"); + newXSproto(strcpy(buf, "GetCanCastOutOfCombat"), XS_Spell_GetCanCastOutOfCombat, file, "$"); + newXSproto(strcpy(buf, "GetCanMGB"), XS_Spell_GetCanMGB, file, "$"); + newXSproto(strcpy(buf, "GetCastNotStanding"), XS_Spell_GetCastNotStanding, file, "$"); + newXSproto(strcpy(buf, "GetCastOnOther"), XS_Spell_GetCastOnOther, file, "$"); + newXSproto(strcpy(buf, "GetCastOnYou"), XS_Spell_GetCastOnYou, file, "$"); + newXSproto(strcpy(buf, "GetCastRestriction"), XS_Spell_GetCastRestriction, file, "$"); + newXSproto(strcpy(buf, "GetCastTime"), XS_Spell_GetCastTime, file, "$"); + newXSproto(strcpy(buf, "GetCasterRequirementID"), XS_Spell_GetCasterRequirementID, file, "$"); + newXSproto(strcpy(buf, "GetCastingAnimation"), XS_Spell_GetCastingAnimation, file, "$"); + newXSproto(strcpy(buf, "GetClasses"), XS_Spell_GetClasses, file, "$$"); + newXSproto(strcpy(buf, "GetComponent"), XS_Spell_GetComponent, file, "$$"); + newXSproto(strcpy(buf, "GetComponentCount"), XS_Spell_GetComponentCount, file, "$$"); + newXSproto(strcpy(buf, "GetDeities"), XS_Spell_GetDeities, file, "$$"); + newXSproto(strcpy(buf, "GetDeityAgnostic"), XS_Spell_GetDeityAgnostic, file, "$"); + newXSproto(strcpy(buf, "GetDescriptionID"), XS_Spell_GetDescriptionID, file, "$"); + newXSproto(strcpy(buf, "GetDirectionalEnd"), XS_Spell_GetDirectionalEnd, file, "$"); + newXSproto(strcpy(buf, "GetDirectionalStart"), XS_Spell_GetDirectionalStart, file, "$"); + newXSproto(strcpy(buf, "GetDisallowSit"), XS_Spell_GetDisallowSit, file, "$"); + newXSproto(strcpy(buf, "GetDispelFlag"), XS_Spell_GetDispelFlag, file, "$"); + newXSproto(strcpy(buf, "GetEffectDescriptionID"), XS_Spell_GetEffectDescriptionID, file, "$"); + newXSproto(strcpy(buf, "GetEffectID"), XS_Spell_GetEffectID, file, "$$"); + newXSproto(strcpy(buf, "GetEnduranceCost"), XS_Spell_GetEnduranceCost, file, "$"); + newXSproto(strcpy(buf, "GetEnduranceUpkeep"), XS_Spell_GetEnduranceUpkeep, file, "$"); + newXSproto(strcpy(buf, "GetEnvironmentType"), XS_Spell_GetEnvironmentType, file, "$"); + newXSproto(strcpy(buf, "GetFeedbackable"), XS_Spell_GetFeedbackable, file, "$"); + newXSproto(strcpy(buf, "GetFormula"), XS_Spell_GetFormula, file, "$$"); + newXSproto(strcpy(buf, "GetGoodEffect"), XS_Spell_GetGoodEffect, file, "$"); + newXSproto(strcpy(buf, "GetHateAdded"), XS_Spell_GetHateAdded, file, "$"); + newXSproto(strcpy(buf, "GetHitNumber"), XS_Spell_GetHitNumber, file, "$"); + newXSproto(strcpy(buf, "GetHitNumberType"), XS_Spell_GetHitNumberType, file, "$"); + newXSproto(strcpy(buf, "GetID"), XS_Spell_GetID, file, "$"); + newXSproto(strcpy(buf, "GetIsDiscipline"), XS_Spell_GetIsDiscipline, file, "$"); + newXSproto(strcpy(buf, "GetLDoNTrap"), XS_Spell_GetLDoNTrap, file, "$"); + newXSproto(strcpy(buf, "GetLimitValue"), XS_Spell_GetLimitValue, file, "$$"); + newXSproto(strcpy(buf, "GetMana"), XS_Spell_GetMana, file, "$"); + newXSproto(strcpy(buf, "GetMaxDistance"), XS_Spell_GetMaxDistance, file, "$"); + newXSproto(strcpy(buf, "GetMaxDistanceMod"), XS_Spell_GetMaxDistanceMod, file, "$"); + newXSproto(strcpy(buf, "GetMaxResist"), XS_Spell_GetMaxResist, file, "$"); + newXSproto(strcpy(buf, "GetMaxValue"), XS_Spell_GetMaxValue, file, "$$"); + newXSproto(strcpy(buf, "GetMinDistance"), XS_Spell_GetMinDistance, file, "$"); + newXSproto(strcpy(buf, "GetMinDistanceMod"), XS_Spell_GetMinDistanceMod, file, "$"); + newXSproto(strcpy(buf, "GetMinRange"), XS_Spell_GetMinRange, file, "$"); + newXSproto(strcpy(buf, "GetMinResist"), XS_Spell_GetMinResist, file, "$"); + newXSproto(strcpy(buf, "GetName"), XS_Spell_GetName, file, "$"); + newXSproto(strcpy(buf, "GetNewIcon"), XS_Spell_GetNewIcon, file, "$"); + newXSproto(strcpy(buf, "GetNimbusEffect"), XS_Spell_GetNimbusEffect, file, "$"); + newXSproto(strcpy(buf, "GetNoBlock"), XS_Spell_GetNoBlock, file, "$"); + newXSproto(strcpy(buf, "GetNoDetrimentalSpellAggro"), XS_Spell_GetNoDetrimentalSpellAggro, file, "$"); + newXSproto(strcpy(buf, "GetNoExpendReagent"), XS_Spell_GetNoExpendReagent, file, "$$"); + newXSproto(strcpy(buf, "GetNoHealDamageItemMod"), XS_Spell_GetNoHealDamageItemMod, file, "$"); + newXSproto(strcpy(buf, "GetNoPartialResist"), XS_Spell_GetNoPartialResist, file, "$"); + newXSproto(strcpy(buf, "GetNoRemove"), XS_Spell_GetNoRemove, file, "$"); + newXSproto(strcpy(buf, "GetNoResist"), XS_Spell_GetNoResist, file, "$"); + newXSproto(strcpy(buf, "GetNotFocusable"), XS_Spell_GetNotFocusable, file, "$"); + newXSproto(strcpy(buf, "GetNPCNoLOS"), XS_Spell_GetNPCNoLOS, file, "$"); + newXSproto(strcpy(buf, "GetOtherCasts"), XS_Spell_GetOtherCasts, file, "$"); + newXSproto(strcpy(buf, "GetOverrideCritChance"), XS_Spell_GetOverrideCritChance, file, "$"); + newXSproto(strcpy(buf, "GetPCNPCOnlyFlag"), XS_Spell_GetPCNPCOnlyFlag, file, "$"); + newXSproto(strcpy(buf, "GetPersistDeath"), XS_Spell_GetPersistDeath, file, "$"); + newXSproto(strcpy(buf, "GetPlayer_1"), XS_Spell_GetPlayer_1, file, "$"); + newXSproto(strcpy(buf, "GetPushBack"), XS_Spell_GetPushBack, file, "$"); + newXSproto(strcpy(buf, "GetPushUp"), XS_Spell_GetPushUp, file, "$"); + newXSproto(strcpy(buf, "GetPVPDuration"), XS_Spell_GetPVPDuration, file, "$"); + newXSproto(strcpy(buf, "GetPVPDurationCap"), XS_Spell_GetPVPDurationCap, file, "$"); + newXSproto(strcpy(buf, "GetPVPResistBase"), XS_Spell_GetPVPResistBase, file, "$"); + newXSproto(strcpy(buf, "GetPVPResistCap"), XS_Spell_GetPVPResistCap, file, "$"); + newXSproto(strcpy(buf, "GetPVPResistPerLevel"), XS_Spell_GetPVPResistPerLevel, file, "$"); + newXSproto(strcpy(buf, "GetRange"), XS_Spell_GetRange, file, "$"); + newXSproto(strcpy(buf, "GetRank"), XS_Spell_GetRank, file, "$"); + newXSproto(strcpy(buf, "GetRecastTime"), XS_Spell_GetRecastTime, file, "$"); + newXSproto(strcpy(buf, "GetRecourseLink"), XS_Spell_GetRecourseLink, file, "$"); + newXSproto(strcpy(buf, "GetRecoveryTime"), XS_Spell_GetRecoveryTime, file, "$"); + newXSproto(strcpy(buf, "GetReflectable"), XS_Spell_GetReflectable, file, "$"); + newXSproto(strcpy(buf, "GetResistDifficulty"), XS_Spell_GetResistDifficulty, file, "$"); + newXSproto(strcpy(buf, "GetResistType"), XS_Spell_GetResistType, file, "$"); + newXSproto(strcpy(buf, "GetShortBuffBox"), XS_Spell_GetShortBuffBox, file, "$"); + newXSproto(strcpy(buf, "GetSkill"), XS_Spell_GetSkill, file, "$"); + newXSproto(strcpy(buf, "GetSneak"), XS_Spell_GetSneak, file, "$"); + newXSproto(strcpy(buf, "GetSongCap"), XS_Spell_GetSongCap, file, "$"); + newXSproto(strcpy(buf, "GetSpellAffectIndex"), XS_Spell_GetSpellAffectIndex, file, "$"); + newXSproto(strcpy(buf, "GetSpellCategory"), XS_Spell_GetSpellCategory, file, "$"); + newXSproto(strcpy(buf, "GetSpellClass"), XS_Spell_GetSpellClass, file, "$"); + newXSproto(strcpy(buf, "GetSpellFades"), XS_Spell_GetSpellFades, file, "$"); + newXSproto(strcpy(buf, "GetSpellGroup"), XS_Spell_GetSpellGroup, file, "$"); + newXSproto(strcpy(buf, "GetSpellSubclass"), XS_Spell_GetSpellSubclass, file, "$"); + newXSproto(strcpy(buf, "GetSuspendable"), XS_Spell_GetSuspendable, file, "$"); + newXSproto(strcpy(buf, "GetTargetType"), XS_Spell_GetTargetType, file, "$"); + newXSproto(strcpy(buf, "GetTeleportZone"), XS_Spell_GetTeleportZone, file, "$"); + newXSproto(strcpy(buf, "GetTimeOfDay"), XS_Spell_GetTimeOfDay, file, "$"); + newXSproto(strcpy(buf, "GetTimerID"), XS_Spell_GetTimerID, file, "$"); + newXSproto(strcpy(buf, "GetTypeDescriptionID"), XS_Spell_GetTypeDescriptionID, file, "$"); + newXSproto(strcpy(buf, "GetUninterruptable"), XS_Spell_GetUninterruptable, file, "$"); + newXSproto(strcpy(buf, "GetUnstackableDOT"), XS_Spell_GetUnstackableDOT, file, "$"); + newXSproto(strcpy(buf, "GetViralRange"), XS_Spell_GetViralRange, file, "$"); + newXSproto(strcpy(buf, "GetViralTargets"), XS_Spell_GetViralTargets, file, "$"); + newXSproto(strcpy(buf, "GetViralTimer"), XS_Spell_GetViralTimer, file, "$"); + newXSproto(strcpy(buf, "GetYouCast"), XS_Spell_GetYouCast, file, "$"); + newXSproto(strcpy(buf, "GetZoneType"), XS_Spell_GetZoneType, file, "$"); + XSRETURN_YES; +} + +#endif //EMBPERL_XS_CLASSES diff --git a/zone/perlparser.h b/zone/perlparser.h index 60020306f..686018543 100644 --- a/zone/perlparser.h +++ b/zone/perlparser.h @@ -32,7 +32,7 @@ public: PerlXSParser(); // ~PerlXSParser(); - virtual void SendCommands(const char * pkgprefix, const char *event, uint32 npcid, Mob* other, Mob* mob, ItemInst* iteminst); + virtual void SendCommands(const char * pkgprefix, const char *event, uint32 object_id, Mob* other, Mob* mob, ItemInst* iteminst, const SPDat_Spell_Struct *spell); protected: void map_funs(); diff --git a/zone/pets.cpp b/zone/pets.cpp index 1c833f088..4ca6b7752 100644 --- a/zone/pets.cpp +++ b/zone/pets.cpp @@ -560,7 +560,7 @@ void NPC::SetPetState(SpellBuff_Struct *pet_buffs, uint32 *items) { buffs[i].casterlevel = pet_buffs[i].level; buffs[i].casterid = 0; buffs[i].counters = pet_buffs[i].counters; - buffs[i].numhits = spells[pet_buffs[i].spellid].numhits; + buffs[i].hit_number = spells[pet_buffs[i].spellid].hit_number; buffs[i].instrument_mod = pet_buffs[i].bard_modifier; } else { @@ -575,14 +575,14 @@ void NPC::SetPetState(SpellBuff_Struct *pet_buffs, uint32 *items) { for (int j1=0; j1 < GetPetMaxTotalSlots(); j1++) { if (buffs[j1].spellid <= (uint32)SPDAT_RECORDS) { for (int x1=0; x1 < EFFECT_COUNT; x1++) { - switch (spells[buffs[j1].spellid].effectid[x1]) { + switch (spells[buffs[j1].spellid].effect_id[x1]) { case SE_WeaponProc: // We need to reapply buff based procs // We need to do this here so suspended pets also regain their procs. - if (spells[buffs[j1].spellid].base2[x1] == 0) { + if (spells[buffs[j1].spellid].limit_value[x1] == 0) { AddProcToWeapon(GetProcID(buffs[j1].spellid,x1), false, 100, buffs[j1].spellid); } else { - AddProcToWeapon(GetProcID(buffs[j1].spellid,x1), false, 100+spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); + AddProcToWeapon(GetProcID(buffs[j1].spellid,x1), false, 100+spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid); } break; case SE_Charm: diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 54a2524ab..4da5c2b1d 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -57,6 +57,7 @@ QuestManager quest_manager; Mob *owner = nullptr; \ Client *initiator = nullptr; \ EQ::ItemInstance* questitem = nullptr; \ + const SPDat_Spell_Struct* questspell = nullptr; \ bool depop_npc = false; \ std::string encounter; \ do { \ @@ -65,6 +66,7 @@ QuestManager quest_manager; owner = e.owner; \ initiator = e.initiator; \ questitem = e.questitem; \ + questspell = e.questspell; \ depop_npc = e.depop_npc; \ encounter = e.encounter; \ } \ @@ -118,11 +120,12 @@ void QuestManager::Process() { } } -void QuestManager::StartQuest(Mob *_owner, Client *_initiator, EQ::ItemInstance* _questitem, std::string encounter) { +void QuestManager::StartQuest(Mob *_owner, Client *_initiator, EQ::ItemInstance* _questitem, const SPDat_Spell_Struct* _questspell, std::string encounter) { running_quest run; run.owner = _owner; run.initiator = _initiator; run.questitem = _questitem; + run.questspell = _questspell; run.depop_npc = false; run.encounter = encounter; quests_running_.push(run); @@ -372,14 +375,14 @@ void QuestManager::castspell(int spell_id, int target_id) { if (owner) { Mob *tgt = entity_list.GetMob(target_id); if(tgt != nullptr) - owner->SpellFinished(spell_id, tgt, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff); + owner->SpellFinished(spell_id, tgt, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty); } } void QuestManager::selfcast(int spell_id) { QuestManagerCurrentQuestVars(); if (initiator) - initiator->SpellFinished(spell_id, initiator, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff); + initiator->SpellFinished(spell_id, initiator, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty); } void QuestManager::addloot(int item_id, int charges, bool equipitem, int aug1, int aug2, int aug3, int aug4, int aug5, int aug6) { @@ -3314,6 +3317,15 @@ EQ::ItemInstance *QuestManager::GetQuestItem() const { return nullptr; } +const SPDat_Spell_Struct *QuestManager::GetQuestSpell() { + if(!quests_running_.empty()) { + running_quest e = quests_running_.top(); + return e.questspell; + } + + return nullptr; +} + std::string QuestManager::GetEncounter() const { if(!quests_running_.empty()) { running_quest e = quests_running_.top(); @@ -3706,3 +3718,10 @@ void QuestManager::WorldWideTaskUpdate(uint8 update_type, uint32 task_identifier worldserver.SendPacket(pack); safe_delete(pack); } + +const SPDat_Spell_Struct* QuestManager::getspell(uint32 spell_id) { + if (spells[spell_id].id) { + return &spells[spell_id]; + } + return nullptr; +} diff --git a/zone/questmgr.h b/zone/questmgr.h index 4098fd75a..2b14e4fe6 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -38,6 +38,7 @@ class QuestManager { Mob *owner; Client *initiator; EQ::ItemInstance* questitem; + const SPDat_Spell_Struct* questspell; bool depop_npc; std::string encounter; }; @@ -51,7 +52,7 @@ public: QuestManager(); virtual ~QuestManager(); - void StartQuest(Mob *_owner, Client *_initiator = nullptr, EQ::ItemInstance* _questitem = nullptr, std::string encounter = ""); + void StartQuest(Mob *_owner, Client *_initiator = nullptr, EQ::ItemInstance* _questitem = nullptr, const SPDat_Spell_Struct* _questspell = nullptr, std::string encounter = ""); void EndQuest(); bool QuestsRunning() { return !quests_running_.empty(); } @@ -325,12 +326,14 @@ public: std::string getinventoryslotname(int16 slot_id); int getitemstat(uint32 item_id, std::string stat_identifier); int getspellstat(uint32 spell_id, std::string stat_identifier, uint8 slot = 0); + const SPDat_Spell_Struct *getspell(uint32 spell_id); Client *GetInitiator() const; NPC *GetNPC() const; Mob *GetOwner() const; EQ::InventoryProfile* GetInventory() const; EQ::ItemInstance *GetQuestItem() const; + const SPDat_Spell_Struct *GetQuestSpell(); std::string GetEncounter() const; inline bool ProximitySayInUse() { return HaveProximitySays; } diff --git a/zone/raids.cpp b/zone/raids.cpp index 55e6d9e55..ef883b917 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -535,7 +535,7 @@ void Raid::CastGroupSpell(Mob* caster, uint16 spellid, uint32 gid) if(members[x].member == caster) { caster->SpellOnTarget(spellid, caster); #ifdef GROUP_BUFF_PETS - if(spells[spellid].targettype != ST_GroupNoPets && caster->GetPet() && caster->HasPetAffinity() && !caster->GetPet()->IsCharmed()) + if(spells[spellid].target_type != ST_GroupNoPets && caster->GetPet() && caster->HasPetAffinity() && !caster->GetPet()->IsCharmed()) caster->SpellOnTarget(spellid, caster->GetPet()); #endif } @@ -546,7 +546,7 @@ void Raid::CastGroupSpell(Mob* caster, uint16 spellid, uint32 gid) if(distance <= range2){ caster->SpellOnTarget(spellid, members[x].member); #ifdef GROUP_BUFF_PETS - if(spells[spellid].targettype != ST_GroupNoPets && members[x].member->GetPet() && members[x].member->HasPetAffinity() && !members[x].member->GetPet()->IsCharmed()) + if(spells[spellid].target_type != ST_GroupNoPets && members[x].member->GetPet() && members[x].member->HasPetAffinity() && !members[x].member->GetPet()->IsCharmed()) caster->SpellOnTarget(spellid, members[x].member->GetPet()); #endif } diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 6e4b4b9a7..db3efff05 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -201,7 +201,7 @@ void Mob::DoSpecialAttackDamage(Mob *who, EQ::skills::SkillType skill, int32 bas float chance = aabonuses.SkillAttackProc[SBIndex::SKILLPROC_CHANCE] / 1000.0f; if (zone->random.Roll(chance)) SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID], who, EQ::spells::CastingSlot::Item, 0, -1, - spells[aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID]].ResistDiff); + spells[aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID]].resist_difficulty); } who->Damage(this, my_hit.damage_done, SPELL_UNKNOWN, skill, false); @@ -1039,7 +1039,7 @@ void Mob::ProjectileAttack() if (ProjectileAtk[i].skill == EQ::skills::SkillConjuration) { if (IsValidSpell(ProjectileAtk[i].wpn_dmg)) SpellOnTarget(ProjectileAtk[i].wpn_dmg, target, false, true, - spells[ProjectileAtk[i].wpn_dmg].ResistDiff, + spells[ProjectileAtk[i].wpn_dmg].resist_difficulty, true); } else { CastToNPC()->DoRangedAttackDmg( @@ -1059,7 +1059,7 @@ void Mob::ProjectileAttack() else if (ProjectileAtk[i].skill == EQ::skills::SkillConjuration && IsValidSpell(ProjectileAtk[i].wpn_dmg)) SpellOnTarget(ProjectileAtk[i].wpn_dmg, target, false, true, - spells[ProjectileAtk[i].wpn_dmg].ResistDiff, true); + spells[ProjectileAtk[i].wpn_dmg].resist_difficulty, true); } } @@ -2208,7 +2208,7 @@ void Mob::DoMeleeSkillAttackDmg(Mob *other, uint16 weapon_damage, EQ::skills::Sk float chance = aabonuses.SkillAttackProc[SBIndex::SKILLPROC_CHANCE] / 1000.0f; if (zone->random.Roll(chance)) SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID], other, EQ::spells::CastingSlot::Item, 0, -1, - spells[aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID]].ResistDiff); + spells[aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID]].resist_difficulty); } other->Damage(this, damage, SPELL_UNKNOWN, skillinuse); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index da3b45fe1..7f7cb7759 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -140,18 +140,18 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove { buffs[buffslot].melee_rune = 0; buffs[buffslot].magic_rune = 0; - buffs[buffslot].numhits = 0; + buffs[buffslot].hit_number = 0; - if (spells[spell_id].numhits > 0) { + if (spells[spell_id].hit_number > 0) { - int numhit = spells[spell_id].numhits; + int numhit = spells[spell_id].hit_number; numhit += numhit * caster->GetFocusEffect(focusFcLimitUse, spell_id) / 100; numhit += caster->GetFocusEffect(focusIncreaseNumHits, spell_id); - buffs[buffslot].numhits = numhit; + buffs[buffslot].hit_number = numhit; } - if (spells[spell_id].EndurUpkeep > 0) + if (spells[spell_id].endurance_upkeep > 0) SetEndurUpkeep(true); if (IsClient() && CastToClient()->ClientVersionBit() & EQ::versions::maskUFAndLater) @@ -204,7 +204,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if(IsBlankSpellEffect(spell_id, i)) continue; - effect = spell.effectid[i]; + effect = spell.effect_id[i]; effect_value = CalcSpellEffectValue(spell_id, i, caster_level, instrument_mod, caster ? caster : this); if(spell_id == SPELL_LAY_ON_HANDS && caster && caster->GetAA(aaImprovedLayOnHands)) @@ -237,7 +237,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if (buffslot >= 0) break; - if (spells[spell_id].base2[i] && !PassCastRestriction(spells[spell_id].base2[i])) { + if (spells[spell_id].limit_value[i] && !PassCastRestriction(spells[spell_id].limit_value[i])) { break; //no messages are given on live if this fails. } @@ -252,7 +252,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove //handles AAs and what not... if(caster) { if (reflect_effectiveness) { - dmg = caster->GetActReflectedSpellDamage(spell_id, (int32)(spells[spell_id].base[i] * partial / 100), reflect_effectiveness); + dmg = caster->GetActReflectedSpellDamage(spell_id, (int32)(spells[spell_id].base_value[i] * partial / 100), reflect_effectiveness); } else { dmg = caster->GetActSpellDamage(spell_id, dmg, this); @@ -299,7 +299,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove // hack fix for client health not reflecting server value last_hp = 0; - if (spells[spell_id].base2[i] && !PassCastRestriction(spells[spell_id].base2[i])) { + if (spells[spell_id].limit_value[i] && !PassCastRestriction(spells[spell_id].limit_value[i])) { break; } @@ -317,15 +317,15 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_PercentalHeal: { #ifdef SPELL_EFFECT_SPAM - snprintf(effect_desc, _EDLEN, "Percental Heal: %+i (%d%% max)", spell.max[i], effect_value); + snprintf(effect_desc, _EDLEN, "Percental Heal: %+i (%d%% max)", spell.max_value[i], effect_value); #endif - int32 val = GetMaxHP() * spell.base[i] / 100; + int32 val = GetMaxHP() * spell.base_value[i] / 100; //This effect can also do damage by percent. if (val < 0) { - if (spell.max[i] && -val > spell.max[i]) - val = -spell.max[i]; + if (spell.max_value[i] && -val > spell.max_value[i]) + val = -spell.max_value[i]; if (caster) val = caster->GetActSpellDamage(spell_id, val, this); @@ -334,8 +334,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove else { - if (spell.max[i] && val > spell.max[i]) - val = spell.max[i]; + if (spell.max_value[i] && val > spell.max_value[i]) + val = spell.max_value[i]; if(caster) val = caster->GetActSpellHealing(spell_id, val, this); @@ -418,8 +418,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove { #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Translocate: %s %d %d %d heading %d", - spell.teleport_zone, spell.base[1], spell.base[0], - spell.base[2], spell.base[3] + spell.teleport_zone, spell.base_value[1], spell.base_value[0], + spell.base_value[2], spell.base_value[3] ); #endif if(IsClient()) @@ -438,10 +438,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove float x, y, z, heading; const char *target_zone = nullptr; - x = static_cast(spell.base[1]); - y = static_cast(spell.base[0]); - z = static_cast(spell.base[2]); - heading = static_cast(spell.base[3]); + x = static_cast(spell.base_value[1]); + y = static_cast(spell.base_value[0]); + z = static_cast(spell.base_value[2]); + heading = static_cast(spell.base_value[3]); if(!strcmp(spell.teleport_zone, "same")) { @@ -510,10 +510,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove float x, y, z, heading; const char *target_zone = nullptr; - x = static_cast(spell.base[1]); - y = static_cast(spell.base[0]); - z = static_cast(spell.base[2]); - heading = static_cast(spell.base[3]); + x = static_cast(spell.base_value[1]); + y = static_cast(spell.base_value[0]); + z = static_cast(spell.base_value[2]); + heading = static_cast(spell.base_value[3]); if(!strcmp(spell.teleport_zone, "same")) { @@ -537,7 +537,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if (effect == SE_GateCastersBindpoint && caster && caster->IsClient()) { // Teleport Bind uses caster's bind point - int index = spells[spell_id].base[i] - 1; + int index = spells[spell_id].base_value[i] - 1; if (index < 0 || index > 4) index = 0; x = caster->CastToClient()->GetBindX(index); @@ -583,7 +583,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Invisibility"); #endif - SetInvisible(spell.base[i]); + SetInvisible(spell.base_value[i]); break; } @@ -610,7 +610,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "See Invisible"); #endif - see_invis = spell.base[i]; + see_invis = spell.base_value[i]; break; } @@ -650,7 +650,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #endif //Added client messages to give some indication this effect is active. // Is there a message generated? Too disgusted by raids. - uint32 time = spell.base[i] * 10 * 1000; + uint32 time = spell.base_value[i] * 10 * 1000; if (caster && caster->IsClient()) { if (caster->IsGrouped()) { auto group = caster->GetGroup(); @@ -688,7 +688,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #endif //Typically we check for immunities else where but since stun immunities are different and only //Block the stun part and not the whole spell, we do it here, also do the message here so we wont get the message on a resist - int max_level = spell.max[i]; + int max_level = spell.max_value[i]; //max_level of 0 means we assume a default of 55. if (max_level == 0) max_level = RuleI(Spells, BaseImmunityLevel); @@ -725,7 +725,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_Charm: { #ifdef SPELL_EFFECT_SPAM - snprintf(effect_desc, _EDLEN, "Charm: %+i (up to lvl %d)", effect_value, spell.max[i]); + snprintf(effect_desc, _EDLEN, "Charm: %+i (up to lvl %d)", effect_value, spell.max_value[i]); #endif if (!caster) { // can't be someone's pet unless we know who that someone is @@ -936,7 +936,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if(caster && caster->IsClient() && caster != this) caster->CastToClient()->QueuePacket(message_packet); - CastToClient()->SetBindPoint(spells[spell_id].base[i] - 1); + CastToClient()->SetBindPoint(spells[spell_id].base_value[i] - 1); Save(); safe_delete(action_packet); safe_delete(message_packet); @@ -987,7 +987,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if(caster->IsClient() && caster != this) caster->CastToClient()->QueuePacket(message_packet); - CastToClient()->SetBindPoint(spells[spell_id].base[i] - 1); + CastToClient()->SetBindPoint(spells[spell_id].base_value[i] - 1); Save(); safe_delete(action_packet); safe_delete(message_packet); @@ -1025,7 +1025,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if(caster->IsClient() && caster != this) caster->CastToClient()->QueuePacket(message_packet); - CastToClient()->SetBindPoint(spells[spell_id].base[i] - 1); + CastToClient()->SetBindPoint(spells[spell_id].base_value[i] - 1); Save(); safe_delete(action_packet); safe_delete(message_packet); @@ -1043,7 +1043,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if(!spellbonuses.AntiGate){ if(zone->random.Roll(effect_value)) - Gate(spells[spell_id].base2[i] - 1); + Gate(spells[spell_id].limit_value[i] - 1); else if (caster) caster->MessageString(Chat::SpellFailure,GATE_FAIL); } @@ -1091,7 +1091,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove found on spell with value 950 (95% spells would have 7% failure rates). Further investigation is needed. ~ Kayen */ - int chance = spells[spell_id].base[i]; + int chance = spells[spell_id].base_value[i]; int buff_count = GetMaxTotalSlots(); for(int slot = 0; slot < buff_count; slot++) { if (buffs[slot].spellid != SPELL_UNKNOWN && @@ -1117,8 +1117,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove caster->MessageString(Chat::SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name); break; } - - int chance = spells[spell_id].base[i]; + + int chance = spells[spell_id].base_value[i]; int buff_count = GetMaxTotalSlots(); for(int slot = 0; slot < buff_count; slot++) { if (buffs[slot].spellid != SPELL_UNKNOWN && @@ -1137,7 +1137,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_Purify: { //Attempt to remove up to base amount of detrimental effects (excluding charm, fear, resurrection, and revival sickness). - int purify_count = spells[spell_id].base[i]; + int purify_count = spells[spell_id].base_value[i]; if (purify_count > GetMaxTotalSlots()) { purify_count = GetMaxTotalSlots(); } @@ -1171,17 +1171,17 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_SummonItem: { - const EQ::ItemData *item = database.GetItem(spell.base[i]); + const EQ::ItemData *item = database.GetItem(spell.base_value[i]); #ifdef SPELL_EFFECT_SPAM const char *itemname = item ? item->Name : "*Unknown Item*"; - snprintf(effect_desc, _EDLEN, "Summon Item: %s (id %d)", itemname, spell.base[i]); + snprintf(effect_desc, _EDLEN, "Summon Item: %s (id %d)", itemname, spell.base_value[i]); #endif if (!item) { - Message(Chat::Red, "Unable to summon item %d. Item not found.", spell.base[i]); + Message(Chat::Red, "Unable to summon item %d. Item not found.", spell.base_value[i]); } else if (IsClient()) { Client *c = CastToClient(); if (c->CheckLoreConflict(item)) { - c->DuplicateLoreMessage(spell.base[i]); + c->DuplicateLoreMessage(spell.base_value[i]); } else { int charges; if (item->Stackable) @@ -1199,7 +1199,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove c->SendItemPacket(EQ::invslot::slotCursor, SummonedItem, ItemPacketLimbo); safe_delete(SummonedItem); } - SummonedItem = database.CreateItem(spell.base[i], charges); + SummonedItem = database.CreateItem(spell.base_value[i], charges); } } @@ -1207,10 +1207,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove } case SE_SummonItemIntoBag: { - const EQ::ItemData *item = database.GetItem(spell.base[i]); + const EQ::ItemData *item = database.GetItem(spell.base_value[i]); #ifdef SPELL_EFFECT_SPAM const char *itemname = item ? item->Name : "*Unknown Item*"; - snprintf(effect_desc, _EDLEN, "Summon Item In Bag: %s (id %d)", itemname, spell.base[i]); + snprintf(effect_desc, _EDLEN, "Summon Item In Bag: %s (id %d)", itemname, spell.base_value[i]); #endif uint8 slot; @@ -1222,7 +1222,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove caster->Message(Chat::Red, "SE_SummonItemIntoBag but no room in summoned bag!"); } else if (IsClient()) { if (CastToClient()->CheckLoreConflict(item)) { - CastToClient()->DuplicateLoreMessage(spell.base[i]); + CastToClient()->DuplicateLoreMessage(spell.base_value[i]); } else { int charges; @@ -1236,7 +1236,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if (charges < 1) charges = 1; - EQ::ItemInstance *SubItem = database.CreateItem(spell.base[i], charges); + EQ::ItemInstance *SubItem = database.CreateItem(spell.base_value[i], charges); if (SubItem != nullptr) { SummonedItem->PutItem(slot, *SubItem); safe_delete(SubItem); @@ -1292,7 +1292,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove snprintf(effect_desc, _EDLEN, "Invulnerability"); #endif if(spell_id==4789) // Touch of the Divine - Divine Save - buffs[buffslot].ticsremaining = spells[spell_id].buffduration; // Prevent focus/aa buff extension + buffs[buffslot].ticsremaining = spells[spell_id].buff_duration; // Prevent focus/aa buff extension SetInvul(true); break; @@ -1317,7 +1317,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove snprintf(effect_desc, _EDLEN, "Blind: %+i", effect_value); #endif // this should catch the cures - if (BeneficialSpell(spell_id) && spells[spell_id].buffduration == 0) + if (BeneficialSpell(spell_id) && spells[spell_id].buff_duration == 0) BuffFadeByEffect(SE_Blind); else if (!IsClient()) CalculateNewFearpoint(); @@ -1346,31 +1346,31 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_MitigateMeleeDamage: { - buffs[buffslot].melee_rune = spells[spell_id].max[i]; + buffs[buffslot].melee_rune = spells[spell_id].max_value[i]; break; } case SE_MeleeThresholdGuard: { - buffs[buffslot].melee_rune = spells[spell_id].max[i]; + buffs[buffslot].melee_rune = spells[spell_id].max_value[i]; break; } case SE_SpellThresholdGuard: { - buffs[buffslot].magic_rune = spells[spell_id].max[i]; + buffs[buffslot].magic_rune = spells[spell_id].max_value[i]; break; } case SE_MitigateSpellDamage: { - buffs[buffslot].magic_rune = spells[spell_id].max[i]; + buffs[buffslot].magic_rune = spells[spell_id].max_value[i]; break; } case SE_MitigateDotDamage: { - buffs[buffslot].dot_rune = spells[spell_id].max[i]; + buffs[buffslot].dot_rune = spells[spell_id].max_value[i]; break; } @@ -1413,7 +1413,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove snprintf(effect_desc, _EDLEN, "Illusion: race %d", effect_value); #endif // Gender Illusions - if(spell.base[i] == -1) { + if(spell.base_value[i] == -1) { // Specific Gender Illusions if(spell_id == 1732 || spell_id == 1731) { int specific_gender = -1; @@ -1453,49 +1453,49 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove // Racial Illusions else { auto gender_id = ( - spell.max[i] > 0 && + spell.max_value[i] > 0 && ( - spell.max[i] != 3 || - spell.base2[i] == 0 + spell.max_value[i] != 3 || + spell.limit_value[i] == 0 ) ? - (spell.max[i] - 1) : - Mob::GetDefaultGender(spell.base[i], GetGender()) + (spell.max_value[i] - 1) : + Mob::GetDefaultGender(spell.base_value[i], GetGender()) ); auto race_size = GetRaceGenderDefaultHeight( - spell.base[i], + spell.base_value[i], gender_id ); - - if (spell.max[i] > 0) { - if (spell.base2[i] == 0) { + + if (spell.max_value[i] > 0) { + if (spell.limit_value[i] == 0) { SendIllusionPacket( - spell.base[i], + spell.base_value[i], gender_id ); } else { - if (spell.max[i] != 3) { + if (spell.max_value[i] != 3) { SendIllusionPacket( - spell.base[i], + spell.base_value[i], gender_id, - spell.base2[i], - spell.max[i] + spell.limit_value[i], + spell.max_value[i] ); } else { SendIllusionPacket( - spell.base[i], + spell.base_value[i], gender_id, - spell.base2[i], - spell.base2[i] + spell.limit_value[i], + spell.limit_value[i] ); } } } else { SendIllusionPacket( - spell.base[i], + spell.base_value[i], gender_id, - spell.base2[i], - spell.max[i] + spell.limit_value[i], + spell.max_value[i] ); } SendAppearancePacket(AT_Size, race_size); @@ -1566,7 +1566,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove snprintf(effect_desc, _EDLEN, "Spin: %d", effect_value); #endif // the spinning is handled by the client - int max_level = spells[spell_id].max[i]; + int max_level = spells[spell_id].max_value[i]; if(max_level == 0) max_level = RuleI(Spells, BaseImmunityLevel); // Default max is 55 level limit @@ -1668,7 +1668,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if(IsClient()) { CastToClient()->SetHorseId(0); // dismount if have horse - if (zone->random.Int(0, 99) > spells[spell_id].base[i]) { + if (zone->random.Int(0, 99) > spells[spell_id].base_value[i]) { CastToClient()->SetFeigned(false); entity_list.MessageCloseString(this, false, 200, 10, STRING_FEIGNFAILED, GetName()); } else { @@ -1919,10 +1919,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove snprintf(effect_desc, _EDLEN, "Weapon Proc: %s (id %d)", spells[effect_value].name, procid); #endif - if(spells[spell_id].base2[i] == 0) + if(spells[spell_id].limit_value[i] == 0) AddProcToWeapon(procid, false, 100, spell_id, caster_level); else - AddProcToWeapon(procid, false, spells[spell_id].base2[i]+100, spell_id, caster_level); + AddProcToWeapon(procid, false, spells[spell_id].limit_value[i]+100, spell_id, caster_level); break; } @@ -1932,15 +1932,15 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove snprintf(effect_desc, _EDLEN, "Melee Negate Attack Rune: %+i", effect_value); #endif if(buffslot >= 0) - buffs[buffslot].numhits = effect_value; + buffs[buffslot].hit_number = effect_value; break; } case SE_AppraiseLDonChest: { if(IsNPC()) { - int check = spell.max[0]; - int target = spell.targettype; + int check = spell.max_value[0]; + int target = spell.target_type; if(target == ST_LDoNChest_Cursed) { if(caster && caster->IsClient()) @@ -1963,8 +1963,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove { if(IsNPC()) { - int check = spell.max[0]; - int target = spell.targettype; + int check = spell.max_value[0]; + int target = spell.target_type; if(target == ST_LDoNChest_Cursed) { if(caster && caster->IsClient()) @@ -1987,8 +1987,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove { if(IsNPC()) { - int check = spell.max[0]; - int target = spell.targettype; + int check = spell.max_value[0]; + int target = spell.target_type; if(target == ST_LDoNChest_Cursed) { if(caster && caster->IsClient()) @@ -2281,7 +2281,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Fading Memories"); #endif - if(zone->random.Roll(spells[spell_id].base[i])) { + if(zone->random.Roll(spells[spell_id].base_value[i])) { if(caster && caster->IsClient()) caster->CastToClient()->Escape(); @@ -2301,10 +2301,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #endif uint16 procid = GetProcID(spell_id, i); - if(spells[spell_id].base2[i] == 0) + if(spells[spell_id].limit_value[i] == 0) AddRangedProc(procid, 100, spell_id); else - AddRangedProc(procid, spells[spell_id].base2[i]+100, spell_id); + AddRangedProc(procid, spells[spell_id].limit_value[i]+100, spell_id); break; } @@ -2338,10 +2338,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if(caster && caster->IsClient()){ //Live AE Taunt range is hardcoded at 40 (Spells for AE taunt all use zero range) Target type should be self only. float range = 40; - if (spells[spell_id].max[i])//custom support if you want to alter range of AE Taunt. - range = spells[spell_id].max[i]; + if (spells[spell_id].max_value[i])//custom support if you want to alter range of AE Taunt. + range = spells[spell_id].max_value[i]; - entity_list.AETaunt(caster->CastToClient(), range, spells[spell_id].base[i]); + entity_list.AETaunt(caster->CastToClient(), range, spells[spell_id].base_value[i]); } break; } @@ -2364,13 +2364,13 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove switch(spells[spell_id].skill) { case EQ::skills::SkillThrowing: - caster->DoThrowingAttackDmg(this, nullptr, nullptr, effect_value,spells[spell_id].base2[i], 0, ReuseTime); + caster->DoThrowingAttackDmg(this, nullptr, nullptr, spells[spell_id].base_value[i],spells[spell_id].limit_value[i], 0, ReuseTime); break; case EQ::skills::SkillArchery: - caster->DoArcheryAttackDmg(this, nullptr, nullptr, effect_value,spells[spell_id].base2[i], 0, ReuseTime); + caster->DoArcheryAttackDmg(this, nullptr, nullptr, spells[spell_id].base_value[i],spells[spell_id].limit_value[i], 0, ReuseTime); break; default: - caster->DoMeleeSkillAttackDmg(this, effect_value, spells[spell_id].skill, spells[spell_id].base2[i], 0, false, ReuseTime); + caster->DoMeleeSkillAttackDmg(this, spells[spell_id].base_value[i], spells[spell_id].skill, spells[spell_id].limit_value[i], 0, false, ReuseTime); break; } break; @@ -2383,7 +2383,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #endif //meh dupe issue with npc casting this if(caster && caster->IsClient()){ - int dur = spells[spell_id].max[i]; + int dur = spells[spell_id].max_value[i]; if (!dur) dur = 60; @@ -2397,8 +2397,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if(caster && caster->IsClient()) { char pet_name[64]; snprintf(pet_name, sizeof(pet_name), "%s`s doppelganger", caster->GetCleanName()); - int pet_count = spells[spell_id].base[i]; - int pet_duration = spells[spell_id].max[i]; + int pet_count = spells[spell_id].base_value[i]; + int pet_duration = spells[spell_id].max_value[i]; caster->CastToClient()->Doppelganger(spell_id, this, pet_name, pet_count, pet_duration); } break; @@ -2410,10 +2410,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Defensive Proc: %s (id %d)", spells[effect_value].name, procid); #endif - if(spells[spell_id].base2[i] == 0) + if(spells[spell_id].limit_value[i] == 0) AddDefensiveProc(procid, 100,spell_id); else - AddDefensiveProc(procid, spells[spell_id].base2[i]+100,spell_id); + AddDefensiveProc(procid, spells[spell_id].limit_value[i]+100,spell_id); break; break; @@ -2499,7 +2499,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove gid = r->GetGroup(caster->GetName()); if(gid < 11) { - r->BalanceHP(spell.base[i], gid, spell.range, caster, spell.base2[i]); + r->BalanceHP(spell.base_value[i], gid, spell.range, caster, spell.limit_value[i]); break; } } @@ -2509,7 +2509,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if(!g) break; - g->BalanceHP(spell.base[i], spell.range, caster, spell.base2[i]); + g->BalanceHP(spell.base_value[i], spell.range, caster, spell.limit_value[i]); break; } @@ -2527,7 +2527,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove gid = r->GetGroup(caster->GetName()); if(gid < 11) { - r->BalanceMana(spell.base[i], gid, spell.range, caster, spell.base2[i]); + r->BalanceMana(spell.base_value[i], gid, spell.range, caster, spell.limit_value[i]); break; } } @@ -2537,7 +2537,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if(!g) break; - g->BalanceMana(spell.base[i], spell.range, caster, spell.base2[i]); + g->BalanceMana(spell.base_value[i], spell.range, caster, spell.limit_value[i]); break; } @@ -2559,7 +2559,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_SuspendPet: { if(IsClient()) - CastToClient()->SuspendMinion(spell.base[i]); + CastToClient()->SuspendMinion(spell.base_value[i]); break; } @@ -2616,8 +2616,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if(!caster->IsClient()) break; - int32 max_mana = spell.base[i]; - int ratio = spell.base2[i]; + int32 max_mana = spell.base_value[i]; + int ratio = spell.limit_value[i]; uint32 heal_amt = 0; if (caster->GetMana() <= max_mana){ @@ -2656,16 +2656,16 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_ManaDrainWithDmg: { int mana_damage = 0; - int32 mana_to_use = GetMana() - spell.base[i]; + int32 mana_to_use = GetMana() - spell.base_value[i]; if(mana_to_use > -1) { - SetMana(GetMana() - spell.base[i]); + SetMana(GetMana() - spell.base_value[i]); TryTriggerOnCastRequirement(); // we take full dmg(-10 to make the damage the right sign) - mana_damage = spell.base[i] / -10 * spell.base2[i]; + mana_damage = spell.base_value[i] / -10 * spell.limit_value[i]; Damage(caster, mana_damage, spell_id, spell.skill, false, i, true); } else { - mana_damage = GetMana() / -10 * spell.base2[i]; + mana_damage = GetMana() / -10 * spell.limit_value[i]; SetMana(0); Damage(caster, mana_damage, spell_id, spell.skill, false, i, true); } @@ -2676,16 +2676,16 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove { if(IsClient()) { int end_damage = 0; - int32 end_to_use = CastToClient()->GetEndurance() - spell.base[i]; + int32 end_to_use = CastToClient()->GetEndurance() - spell.base_value[i]; if(end_to_use > -1) { - CastToClient()->SetEndurance(CastToClient()->GetEndurance() - spell.base[i]); + CastToClient()->SetEndurance(CastToClient()->GetEndurance() - spell.base_value[i]); TryTriggerOnCastRequirement(); // we take full dmg(-10 to make the damage the right sign) - end_damage = spell.base[i] / -10 * spell.base2[i]; + end_damage = spell.base_value[i] / -10 * spell.limit_value[i]; Damage(caster, end_damage, spell_id, spell.skill, false, i, true); } else { - end_damage = CastToClient()->GetEndurance() / -10 * spell.base2[i]; + end_damage = CastToClient()->GetEndurance() / -10 * spell.limit_value[i]; CastToClient()->SetEndurance(0); Damage(caster, end_damage, spell_id, spell.skill, false, i, true); } @@ -2695,7 +2695,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_SetBodyType: { - SetBodyType((bodyType)spell.base[i], false); + SetBodyType((bodyType)spell.base_value[i], false); break; } @@ -2716,7 +2716,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove { float value, x_vector, y_vector, hypot; - value = (float)spell.base[i]; // distance away from target + value = (float)spell.base_value[i]; // distance away from target x_vector = target_x - my_x; y_vector = target_y - my_y; @@ -2748,8 +2748,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_ManaBurn: { - int32 max_mana = spell.base[i]; - int ratio = spell.base2[i]; + int32 max_mana = spell.base_value[i]; + int ratio = spell.limit_value[i]; int32 dmg = 0; if (caster){ @@ -2778,23 +2778,23 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_Taunt: { if (caster && IsNPC()){ - caster->Taunt(this->CastToNPC(), false, spell.base[i], true, spell.base2[i]); + caster->Taunt(this->CastToNPC(), false, spell.base_value[i], true, spell.limit_value[i]); } break; } case SE_AttackSpeed: - if (spell.base[i] < 100) + if (spell.base_value[i] < 100) SlowMitigation(caster); break; case SE_AttackSpeed2: - if (spell.base[i] < 100) + if (spell.base_value[i] < 100) SlowMitigation(caster); break; case SE_AttackSpeed3: - if (spell.base[i] < 0) + if (spell.base_value[i] < 0) SlowMitigation(caster); break; @@ -2805,7 +2805,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_AddHatePct: { if (IsNPC()){ - int32 new_hate = CastToNPC()->GetHateAmount(caster) * (100 + spell.base[i]) / 100; + int32 new_hate = CastToNPC()->GetHateAmount(caster) * (100 + spell.base_value[i]) / 100; if (new_hate <= 0) new_hate = 1; @@ -2842,7 +2842,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if (buffslot >= 0) break; - if(!spells[spell_id].uninterruptable && IsCasting() && zone->random.Roll(spells[spell_id].base[i])) + if(!spells[spell_id].uninterruptable && IsCasting() && zone->random.Roll(spells[spell_id].base_value[i])) InterruptSpell(); break; @@ -2863,9 +2863,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_ApplyEffect: { - if (caster && IsValidSpell(spells[spell_id].base2[i])){ - if(zone->random.Roll(spells[spell_id].base[i])) - caster->SpellFinished(spells[spell_id].base2[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff); + if (caster && IsValidSpell(spells[spell_id].limit_value[i])){ + if(zone->random.Roll(spells[spell_id].base_value[i])) + caster->SpellFinished(spells[spell_id].limit_value[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[spells[spell_id].limit_value[i]].resist_difficulty); } break; } @@ -2881,10 +2881,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_Instant_Mana_Pct: { - effect_value = spells[spell_id].base[i]; + effect_value = spells[spell_id].base_value[i]; int32 amt = abs(GetMaxMana() * effect_value / 10000); - if (spells[spell_id].max[i] && amt > spells[spell_id].max[i]) - amt = spells[spell_id].max[i]; + if (spells[spell_id].max_value[i] && amt > spells[spell_id].max_value[i]) + amt = spells[spell_id].max_value[i]; if (effect_value < 0) { SetMana(GetMana() - amt); @@ -2896,11 +2896,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove } case SE_Instant_Endurance_Pct: { - effect_value = spells[spell_id].base[i]; + effect_value = spells[spell_id].base_value[i]; if (IsClient()) { int32 amt = abs(CastToClient()->GetMaxEndurance() * effect_value / 10000); - if (spells[spell_id].max[i] && amt > spells[spell_id].max[i]) - amt = spells[spell_id].max[i]; + if (spells[spell_id].max_value[i] && amt > spells[spell_id].max_value[i]) + amt = spells[spell_id].max_value[i]; if (effect_value < 0) { CastToClient()->SetEndurance(CastToClient()->GetEndurance() - amt); @@ -2914,7 +2914,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove /*Calc for base1 is found in TryOnSpellFinished() due to needing to account for AOE functionality since effect can potentially kill caster*/ case SE_Health_Transfer: { - effect_value = spells[spell_id].base2[i]; + effect_value = spells[spell_id].limit_value[i]; int32 amt = abs(caster->GetMaxHP() * effect_value / 1000); if (effect_value < 0) @@ -2937,11 +2937,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if (caster && !caster->IsClient()) break; - if (zone->random.Roll(spells[spell_id].base[i])) { - uint32 best_spell_id = caster->CastToClient()->GetHighestScribedSpellinSpellGroup(spells[spell_id].base2[i]); + if (zone->random.Roll(spells[spell_id].base_value[i])) { + uint32 best_spell_id = caster->CastToClient()->GetHighestScribedSpellinSpellGroup(spells[spell_id].limit_value[i]); if (caster && IsValidSpell(best_spell_id)) - caster->SpellFinished(best_spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[best_spell_id].ResistDiff); + caster->SpellFinished(best_spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[best_spell_id].resist_difficulty); } break; } @@ -2951,20 +2951,20 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if (caster && caster->IsClient() && GetCastedSpellInvSlot() > 0) break; - if (zone->random.Roll(spells[spell_id].base[i]) && IsValidSpell(spells[spell_id].base2[i])) - caster->SpellFinished(spells[spell_id].base2[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff); + if (zone->random.Roll(spells[spell_id].base_value[i]) && IsValidSpell(spells[spell_id].limit_value[i])) + caster->SpellFinished(spells[spell_id].limit_value[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[spells[spell_id].limit_value[i]].resist_difficulty); break; } case SE_Hatelist_To_Tail_Index: { - if (caster && zone->random.Roll(spells[spell_id].base[i])) + if (caster && zone->random.Roll(spells[spell_id].base_value[i])) caster->SetBottomRampageList(); break; } case SE_Hatelist_To_Top_Index: { - if (caster && zone->random.Roll(spells[spell_id].base[i])) + if (caster && zone->random.Roll(spells[spell_id].base_value[i])) caster->SetTopRampageList(); break; } @@ -2984,11 +2984,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove break; } - if (spells[spell_id].max[i] == 0 || GetLevel() <= spells[spell_id].max[i]) { - if (IsClient() && spells[spell_id].base2[i]) - Stun(spells[spell_id].base2[i]); + if (spells[spell_id].max_value[i] == 0 || GetLevel() <= spells[spell_id].max_value[i]) { + if (IsClient() && spells[spell_id].limit_value[i]) + Stun(spells[spell_id].limit_value[i]); else - Stun(spells[spell_id].base[i]); + Stun(spells[spell_id].base_value[i]); } else caster->MessageString(Chat::SpellFailure, FEAR_TOO_HIGH); @@ -2999,9 +2999,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if (IsPet()) { Mob* petowner = GetOwner(); if (petowner) { - int shield_duration = spells[spell_id].base[i] * 12 * 1000; - int shield_target_mitigation = spells[spell_id].base2[i] ? spells[spell_id].base2[i] : 50; - int shielder_mitigation = spells[spell_id].max[i] ? spells[spell_id].base2[i] : 50; + int shield_duration = spells[spell_id].base_value[i] * 12 * 1000; + int shield_target_mitigation = spells[spell_id].limit_value[i] ? spells[spell_id].limit_value[i] : 50; + int shielder_mitigation = spells[spell_id].max_value[i] ? spells[spell_id].limit_value[i] : 50; ShieldAbility(petowner->GetID(), 25, shield_duration, shield_target_mitigation, shielder_mitigation); break; } @@ -3336,20 +3336,20 @@ int Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level, return 0; int formula = spells[spell_id].formula[effect_id]; - int base = spells[spell_id].base[effect_id]; - int max = spells[spell_id].max[effect_id]; + int base_value = spells[spell_id].base_value[effect_id]; + int max_value = spells[spell_id].max_value[effect_id]; int effect_value = 0; int oval = 0; if (IsBlankSpellEffect(spell_id, effect_id)) return 0; - effect_value = CalcSpellEffectValue_formula(formula, base, max, caster_level, spell_id, ticsremaining); + effect_value = CalcSpellEffectValue_formula(formula, base_value, max_value, caster_level, spell_id, ticsremaining); // this doesn't actually need to be a song to get mods, just the right skill - if (EQ::skills::IsBardInstrumentSkill(spells[spell_id].skill) - && IsInstrumentModAppliedToSpellEffect(spell_id, spells[spell_id].effectid[effect_id])) { + if (EQ::skills::IsBardInstrumentSkill(spells[spell_id].skill) + && IsInstrumentModAppliedToSpellEffect(spell_id, spells[spell_id].effect_id[effect_id])) { oval = effect_value; effect_value = effect_value * static_cast(instrument_mod) / 10; LogSpells("Effect value [{}] altered with bard modifier of [{}] to yeild [{}]", @@ -3390,13 +3390,13 @@ int Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level, } } - effect_value = mod_effect_value(effect_value, spell_id, spells[spell_id].effectid[effect_id], caster, caster_id); + effect_value = mod_effect_value(effect_value, spell_id, spells[spell_id].effect_id[effect_id], caster, caster_id); return effect_value; } // generic formula calculations -int Mob::CalcSpellEffectValue_formula(int formula, int base, int max, int caster_level, uint16 spell_id, int ticsremaining) +int Mob::CalcSpellEffectValue_formula(int formula, int base_value, int max_value, int caster_level, uint16 spell_id, int ticsremaining) { /* i need those formulas checked!!!! @@ -3425,7 +3425,7 @@ i need those formulas checked!!!! 0x77 = min + level / 8 */ - int result = 0, updownsign = 1, ubase = base; + int result = 0, updownsign = 1, ubase = base_value; if(ubase < 0) ubase = 0 - ubase; @@ -3438,7 +3438,7 @@ Strangely, damage spells have a negative base and positive max, but snare has both of them negative, yet their range should work the same: (meaning they both start at a negative value and the value gets lower) */ - if (max < base && max != 0) + if (max_value < base_value && max_value != 0) { // values are calculated down updownsign = -1; @@ -3450,7 +3450,7 @@ snare has both of them negative, yet their range should work the same: } LogSpells("CSEV: spell [{}], formula [{}], base [{}], max [{}], lvl [{}]. Up/Down [{}]", - spell_id, formula, base, max, caster_level, updownsign); + spell_id, formula, base_value, max_value, caster_level, updownsign); switch(formula) { @@ -3473,7 +3473,7 @@ snare has both of them negative, yet their range should work the same: case 107: { - int ticdif = CalcBuffDuration_formula(caster_level, spells[spell_id].buffdurationformula, spells[spell_id].buffduration) - std::max((ticsremaining - 1), 0); + int ticdif = CalcBuffDuration_formula(caster_level, spells[spell_id].buff_duration_formula, spells[spell_id].buff_duration) - std::max((ticsremaining - 1), 0); if (ticdif < 0) ticdif = 0; result = updownsign * (ubase - ticdif); @@ -3482,7 +3482,7 @@ snare has both of them negative, yet their range should work the same: } case 108: { - int ticdif = CalcBuffDuration_formula(caster_level, spells[spell_id].buffdurationformula, spells[spell_id].buffduration) - std::max((ticsremaining - 1), 0); + int ticdif = CalcBuffDuration_formula(caster_level, spells[spell_id].buff_duration_formula, spells[spell_id].buff_duration) - std::max((ticsremaining - 1), 0); if (ticdif < 0) ticdif = 0; result = updownsign * (ubase - (2 * ticdif)); @@ -3536,7 +3536,7 @@ snare has both of them negative, yet their range should work the same: break; case 120: { - int ticdif = CalcBuffDuration_formula(caster_level, spells[spell_id].buffdurationformula, spells[spell_id].buffduration) - std::max((ticsremaining - 1), 0); + int ticdif = CalcBuffDuration_formula(caster_level, spells[spell_id].buff_duration_formula, spells[spell_id].buff_duration) - std::max((ticsremaining - 1), 0); if (ticdif < 0) ticdif = 0; result = updownsign * (ubase - (5 * ticdif)); @@ -3547,7 +3547,7 @@ snare has both of them negative, yet their range should work the same: result = ubase + (caster_level / 3); break; case 122: { - int ticdif = CalcBuffDuration_formula(caster_level, spells[spell_id].buffdurationformula, spells[spell_id].buffduration) - std::max((ticsremaining - 1), 0); + int ticdif = CalcBuffDuration_formula(caster_level, spells[spell_id].buff_duration_formula, spells[spell_id].buff_duration) - std::max((ticsremaining - 1), 0); if(ticdif < 0) ticdif = 0; @@ -3556,7 +3556,7 @@ snare has both of them negative, yet their range should work the same: break; } case 123: // added 2/6/04 - result = zone->random.Int(ubase, std::abs(max)); + result = zone->random.Int(ubase, std::abs(max_value)); break; case 124: // check sign @@ -3649,7 +3649,7 @@ snare has both of them negative, yet their range should work the same: //these are used in stacking effects... formula unknown case 201: case 203: - result = max; + result = max_value; break; default: { @@ -3659,7 +3659,7 @@ snare has both of them negative, yet their range should work the same: { // These work like splurt, accept instead of being hard coded to 12, it is formula - 1000. // Formula 1999 seems to have a slightly different effect, so is not included here - int ticdif = CalcBuffDuration_formula(caster_level, spells[spell_id].buffdurationformula, spells[spell_id].buffduration) - std::max((ticsremaining - 1), 0); + int ticdif = CalcBuffDuration_formula(caster_level, spells[spell_id].buff_duration_formula, spells[spell_id].buff_duration) - std::max((ticsremaining - 1), 0); if(ticdif < 0) ticdif = 0; @@ -3679,25 +3679,25 @@ snare has both of them negative, yet their range should work the same: int oresult = result; // now check result against the allowed maximum - if (max != 0) + if (max_value != 0) { if (updownsign == 1) { - if (result > max) - result = max; + if (result > max_value) + result = max_value; } else { - if (result < max) - result = max; + if (result < max_value) + result = max_value; } } // if base is less than zero, then the result need to be negative too - if (base < 0 && result > 0) + if (base_value < 0 && result > 0) result *= -1; - LogSpells("Result: [{}] (orig [{}]), cap [{}] [{}]", result, oresult, max, (base < 0 && result > 0)?"Inverted due to negative base":""); + LogSpells("Result: [{}] (orig [{}]), cap [{}] [{}]", result, oresult, max_value, (base_value < 0 && result > 0)?"Inverted due to negative base":""); return result; } @@ -3717,8 +3717,8 @@ void Mob::BuffProcess() continue; // DF_Permanent uses -1 DF_Aura uses -4 but we need to check negatives for some spells for some reason? - if (spells[buffs[buffs_i].spellid].buffdurationformula != DF_Permanent && - spells[buffs[buffs_i].spellid].buffdurationformula != DF_Aura) { + if (spells[buffs[buffs_i].spellid].buff_duration_formula != DF_Permanent && + spells[buffs[buffs_i].spellid].buff_duration_formula != DF_Aura) { if(!zone->BuffTimersSuspended() || !IsSuspendableSpell(buffs[buffs_i].spellid)) { --buffs[buffs_i].ticsremaining; @@ -3744,7 +3744,7 @@ void Mob::BuffProcess() { CastToClient()->SendBuffDurationPacket(buffs[buffs_i], buffs_i); // Hack to get UF to play nicer, RoF seems fine without it - if (CastToClient()->ClientVersion() == EQ::versions::ClientVersion::UF && buffs[buffs_i].numhits > 0) + if (CastToClient()->ClientVersion() == EQ::versions::ClientVersion::UF && buffs[buffs_i].hit_number > 0) CastToClient()->SendBuffNumHitPacket(buffs[buffs_i], buffs_i); buffs[buffs_i].UpdateClient = false; } @@ -3784,14 +3784,13 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) if (IsBlankSpellEffect(buff.spellid, i)) continue; - effect = spell.effectid[i]; + effect = spell.effect_id[i]; // I copied the calculation into each case which needed it instead of // doing it every time up here, since most buff effects dont need it switch (effect) { - case SE_CurrentHP: { - - if (spells[buff.spellid].base2[i] && !PassCastRestriction(spells[buff.spellid].base2[i])) { + case SE_CurrentHP: { + if (spells[buff.spellid].limit_value[i] && !PassCastRestriction(spells[buff.spellid].limit_value[i])) { break; } @@ -3918,7 +3917,7 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) if (zone->random.Roll(RuleI(Spells, RootBreakCheckChance))) { float resist_check = - ResistSpell(spells[buff.spellid].resisttype, buff.spellid, caster, 0, 0, 0, 0, true); + ResistSpell(spells[buff.spellid].resist_type, buff.spellid, caster, 0, 0, 0, 0, true); if (resist_check == 100) break; @@ -3931,7 +3930,7 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) case SE_Fear: { if (zone->random.Roll(RuleI(Spells, FearBreakCheckChance))) { - float resist_check = ResistSpell(spells[buff.spellid].resisttype, buff.spellid, caster,0,0,true); + float resist_check = ResistSpell(spells[buff.spellid].resist_type, buff.spellid, caster,0,0,true); if (resist_check == 100) break; @@ -3975,7 +3974,7 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) case SE_InterruptCasting: { if (IsCasting()) { const auto &spell = spells[casting_spell_id]; - if (!spell.cast_not_standing && zone->random.Roll(spells[buff.spellid].base[i])) { + if (!spell.cast_not_standing && zone->random.Roll(spells[buff.spellid].base_value[i])) { InterruptSpell(); } } @@ -3987,7 +3986,7 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) case SE_CastOnFadeEffectNPC: case SE_CastOnFadeEffectAlways: { if (buff.ticsremaining == 0) { - SpellFinished(spells[buff.spellid].base[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[spells[buff.spellid].base[i]].ResistDiff); + SpellFinished(spells[buff.spellid].base_value[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[spells[buff.spellid].base_value[i]].resist_difficulty); } break; } @@ -4006,7 +4005,7 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) ((int(GetY()) - buff.caston_y) * (int(GetY()) - buff.caston_y)) + ((int(GetZ()) - buff.caston_z) * (int(GetZ()) - buff.caston_z)); - if (distance > (spells[buff.spellid].base[i] * spells[buff.spellid].base[i])) { + if (distance > (spells[buff.spellid].base_value[i] * spells[buff.spellid].base_value[i])) { if (!TryFadeEffect(slot)) BuffFadeBySlot(slot, true); @@ -4017,7 +4016,7 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) case SE_AddHateOverTimePct: { if (IsNPC()) { - uint32 new_hate = CastToNPC()->GetHateAmount(caster) * (100 + spell.base[i]) / 100; + uint32 new_hate = CastToNPC()->GetHateAmount(caster) * (100 + spell.base_value[i]) / 100; if (new_hate <= 0) new_hate = 1; @@ -4027,10 +4026,10 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) } case SE_Duration_HP_Pct: { - effect_value = spells[buff.spellid].base[i]; + effect_value = spells[buff.spellid].base_value[i]; int32 amt = abs(GetMaxHP() * effect_value / 100); - if (spells[buff.spellid].max[i] && amt > spells[buff.spellid].max[i]) - amt = spells[buff.spellid].max[i]; + if (spells[buff.spellid].max_value[i] && amt > spells[buff.spellid].max_value[i]) + amt = spells[buff.spellid].max_value[i]; if (effect_value < 0) { Damage(this, amt, 0, EQ::skills::SkillEvocation, false); @@ -4042,10 +4041,10 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) } case SE_Duration_Mana_Pct: { - effect_value = spells[buff.spellid].base[i]; + effect_value = spells[buff.spellid].base_value[i]; int32 amt = abs(GetMaxMana() * effect_value / 100); - if (spells[buff.spellid].max[i] && amt > spells[buff.spellid].max[i]) - amt = spells[buff.spellid].max[i]; + if (spells[buff.spellid].max_value[i] && amt > spells[buff.spellid].max_value[i]) + amt = spells[buff.spellid].max_value[i]; if (effect_value < 0) { @@ -4058,12 +4057,12 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) } case SE_Duration_Endurance_Pct: { - effect_value = spells[buff.spellid].base[i]; + effect_value = spells[buff.spellid].base_value[i]; if (IsClient()) { int32 amt = abs(CastToClient()->GetMaxEndurance() * effect_value / 100); - if (spells[buff.spellid].max[i] && amt > spells[buff.spellid].max[i]) - amt = spells[buff.spellid].max[i]; + if (spells[buff.spellid].max_value[i] && amt > spells[buff.spellid].max_value[i]) + amt = spells[buff.spellid].max_value[i]; if (effect_value < 0) { CastToClient()->SetEndurance(CastToClient()->GetEndurance() - amt); @@ -4129,7 +4128,7 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) if(IsBlankSpellEffect(buffs[slot].spellid, i)) continue; - switch (spells[buffs[slot].spellid].effectid[i]) + switch (spells[buffs[slot].spellid].effect_id[i]) { case SE_AddMeleeProc: case SE_WeaponProc: @@ -4315,7 +4314,7 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) for (unsigned int j = 0; j < buff_count; j++) { if (IsValidSpell(tar->GetBuffs()[j].spellid)) { auto spell = spells[tar->GetBuffs()[j].spellid]; - if (spell.goodEffect == 0 && IsEffectInSpell(spell.id, SE_CurrentHP) && tar->GetBuffs()[j].casterid == GetID()) { + if (spell.good_effect == 0 && IsEffectInSpell(spell.id, SE_CurrentHP) && tar->GetBuffs()[j].casterid == GetID()) { tar->BuffFadeBySpellID(spell.id); } } @@ -4329,7 +4328,7 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) for (unsigned int j = 0; j < buff_count; j++) { if (IsValidSpell(GetBuffs()[j].spellid )) { auto spell = spells[this->GetBuffs()[j].spellid]; - if (spell.goodEffect == 0 && IsEffectInSpell(spell.id, SE_CurrentHP)) { + if (spell.good_effect == 0 && IsEffectInSpell(spell.id, SE_CurrentHP)) { BuffFadeBySpellID(spell.id); } } @@ -4500,7 +4499,7 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) for(uint32 d = 0; d < buff_max; d++) { - if(IsValidSpell(buffs[d].spellid) && (buffs[d].numhits > 0)) { + if(IsValidSpell(buffs[d].spellid) && (buffs[d].hit_number > 0)) { Numhits(true); found_numhits = true; } @@ -4510,8 +4509,8 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) Numhits(false); } - if (spells[buffs[slot].spellid].NimbusEffect > 0) - RemoveNimbusEffect(spells[buffs[slot].spellid].NimbusEffect); + if (spells[buffs[slot].spellid].nimbus_effect > 0) + RemoveNimbusEffect(spells[buffs[slot].spellid].nimbus_effect); buffs[slot].spellid = SPELL_UNKNOWN; if(IsPet() && GetOwner() && GetOwner()->IsClient()) { @@ -4563,8 +4562,8 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) int spell_level = 0; int lvldiff = 0; uint32 effect = 0; - int32 base1 = 0; - int32 base2 = 0; + int32 base_value = 0; + int32 limit_value = 0; uint32 slot = 0; int index_id = -1; @@ -4589,8 +4588,8 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) for (const auto &e : rank.effects) { effect = e.effect_id; - base1 = e.base1; - base2 = e.base2; + base_value = e.base_value; + limit_value = e.limit_value; slot = e.slot; /* @@ -4632,24 +4631,24 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) // Handle Focus Limits case SE_LimitResist: - if (base1 < 0) { - if (spell.resisttype == -base1) { // Exclude + if (base_value < 0) { + if (spell.resist_type == -base_value) { // Exclude LimitFailure = true; } } else { LimitInclude[IncludeExistsSELimitResist] = true; - if (spell.resisttype == base1) { // Include + if (spell.resist_type == base_value) { // Include LimitInclude[IncludeFoundSELimitResist] = true; } } break; case SE_LimitInstant: - if (base1 == 1 && spell.buffduration) { // Fail if not instant + if (base_value == 1 && spell.buff_duration) { // Fail if not instant LimitFailure = true; } - if (base1 == 0 && (spell.buffduration == 0)) { // Fail if instant + if (base_value == 0 && (spell.buff_duration == 0)) { // Fail if instant LimitFailure = true; } @@ -4657,12 +4656,12 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) case SE_LimitMaxLevel: spell_level = spell.classes[(GetClass() % 17) - 1]; - lvldiff = spell_level - base1; + lvldiff = spell_level - base_value; // every level over cap reduces the effect by base2 percent unless from a clicky when // ItemCastsUseFocus is true if (lvldiff > 0 && (spell_level <= RuleI(Character, MaxLevel) || RuleB(Character, ItemCastsUseFocus) == false)) { - if (base2 > 0) { - lvlModifier -= base2 * lvldiff; + if (limit_value > 0) { + lvlModifier -= limit_value * lvldiff; if (lvlModifier < 1) { LimitFailure = true; } @@ -4674,59 +4673,59 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) break; case SE_LimitMinLevel: - if ((spell.classes[(GetClass() % 17) - 1]) < base1) { + if ((spell.classes[(GetClass() % 17) - 1]) < base_value) { LimitFailure = true; } break; case SE_LimitCastTimeMin: - if (static_cast(spell.cast_time) < base1) { + if (static_cast(spell.cast_time) < base_value) { LimitFailure = true; } break; case SE_LimitCastTimeMax: - if (static_cast(spell.cast_time) > base1) { + if (static_cast(spell.cast_time) > base_value) { LimitFailure = true; } break; case SE_LimitSpell: - if (base1 < 0) { // Exclude - if (spell_id == -base1) { + if (base_value < 0) { // Exclude + if (spell_id == -base_value) { LimitFailure = true; } } else { LimitInclude[IncludeExistsSELimitSpell] = true; - if (spell_id == base1) { // Include + if (spell_id == base_value) { // Include LimitInclude[IncludeFoundSELimitSpell] = true; } } break; case SE_LimitMinDur: - if (base1 > CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration)) { + if (base_value > CalcBuffDuration_formula(GetLevel(), spell.buff_duration_formula, spell.buff_duration)) { LimitFailure = true; } break; case SE_LimitEffect: - if (base1 < 0) { - if (IsEffectInSpell(spell_id, -base1)) { // Exclude + if (base_value < 0) { + if (IsEffectInSpell(spell_id, -base_value)) { // Exclude LimitFailure = true; } } else { LimitInclude[IncludeExistsSELimitEffect] = true; // they use 33 here for all classes ... unsure if the type check is really needed - if (base1 == SE_SummonPet && type == focusReagentCost) { + if (base_value == SE_SummonPet && type == focusReagentCost) { if (IsSummonPetSpell(spell_id) || IsSummonSkeletonSpell(spell_id)) { LimitInclude[IncludeFoundSELimitEffect] = true; } } else { - if (IsEffectInSpell(spell_id, base1)) { // Include + if (IsEffectInSpell(spell_id, base_value)) { // Include LimitInclude[IncludeFoundSELimitEffect] = true; } } @@ -4734,7 +4733,7 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) break; case SE_LimitSpellType: - switch (base1) { + switch (base_value) { case 0: if (!IsDetrimentalSpell(spell_id)) { LimitFailure = true; @@ -4749,92 +4748,92 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) break; case SE_LimitManaMin: - if (spell.mana < base1) { + if (spell.mana < base_value) { LimitFailure = true; } break; case SE_LimitManaMax: - if (spell.mana > base1) { + if (spell.mana > base_value) { LimitFailure = true; } break; case SE_LimitTarget: - if (base1 < 0) { - if (-base1 == spell.targettype) { // Exclude + if (base_value < 0) { + if (-base_value == spell.target_type) { // Exclude LimitFailure = true; } } else { LimitInclude[IncludeExistsSELimitTarget] = true; - if (base1 == spell.targettype) { // Include + if (base_value == spell.target_type) { // Include LimitInclude[IncludeFoundSELimitTarget] = true; } } break; case SE_LimitCombatSkills: - if (base1 == 0 && (IsCombatSkill(spell_id) || IsCombatProc(spell_id))) { // Exclude Discs / Procs + if (base_value == 0 && (IsCombatSkill(spell_id) || IsCombatProc(spell_id))) { // Exclude Discs / Procs LimitFailure = true; } - else if (base1 == 1 && (!IsCombatSkill(spell_id) || !IsCombatProc(spell_id))) { // Exclude Spells + else if (base_value == 1 && (!IsCombatSkill(spell_id) || !IsCombatProc(spell_id))) { // Exclude Spells LimitFailure = true; } break; case SE_LimitSpellGroup: - if (base1 < 0) { - if (-base1 == spell.spellgroup) { // Exclude + if (base_value < 0) { + if (-base_value == spell.spell_group) { // Exclude LimitFailure = true; } } else { LimitInclude[IncludeExistsSELimitSpellGroup] = true; - if (base1 == spell.spellgroup) { // Include + if (base_value == spell.spell_group) { // Include LimitInclude[IncludeFoundSELimitSpellGroup] = true; } } break; case SE_LimitCastingSkill: - if (base1 < 0) { - if (-base1 == spell.skill) { + if (base_value < 0) { + if (-base_value == spell.skill) { LimitFailure = true; } } else { LimitInclude[IncludeExistsSELimitCastingSkill] = true; - if (base1 == spell.skill) { + if (base_value == spell.skill) { LimitInclude[IncludeFoundSELimitCastingSkill] = true; } } break; case SE_LimitSpellClass: - if (base1 < 0) { // Exclude - if (-base1 == spell.spell_class) { + if (base_value < 0) { // Exclude + if (-base_value == spell.spell_class) { LimitFailure = true; } } else { LimitInclude[IncludeExistsSELimitSpellClass] = true; - if (base1 == spell.spell_class) { // Include + if (base_value == spell.spell_class) { // Include LimitInclude[IncludeFoundSELimitSpellClass] = true; } } break; case SE_LimitSpellSubclass: - if (base1 < 0) { // Exclude - if (-base1 == spell.spell_subclass) { + if (base_value < 0) { // Exclude + if (-base_value == spell.spell_subclass) { LimitFailure = true; } } else { LimitInclude[IncludeExistsSELimitSpellSubclass] = true; - if (base1 == spell.spell_subclass) { // Include + if (base_value == spell.spell_subclass) { // Include LimitInclude[IncludeFoundSELimitSpellSubclass] = true; } } @@ -4843,75 +4842,75 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) case SE_LimitClass: // Do not use this limit more then once per spell. If multiple class, treat value like items // would. - if (!PassLimitClass(base1, GetClass())) { + if (!PassLimitClass(base_value, GetClass())) { LimitFailure = true; } break; case SE_LimitRace: - if (base1 != GetRace()) { + if (base_value != GetRace()) { LimitFailure = true; } break; case SE_LimitUseMin: - if (base1 > spell.numhits) { + if (base_value > spell.hit_number) { LimitFailure = true; } break; case SE_LimitUseType: - if (base1 != spell.numhitstype) { + if (base_value != spell.hit_number_type) { LimitFailure = true; } break; case SE_Ff_DurationMax: - if (base1 > spell.buffduration) { + if (base_value > spell.buff_duration) { LimitFailure = true; } break; case SE_Ff_Endurance_Min: - if (spell.EndurCost < base1) { + if (spell.endurance_cost < base_value) { LimitFailure = true; } break; case SE_Ff_Endurance_Max: - if (spell.EndurCost > base1) { + if (spell.endurance_cost > base_value) { LimitFailure = true; } break; case SE_Ff_ReuseTimeMin: - if (spell.recast_time < base1) { + if (spell.recast_time < base_value) { LimitFailure = true; } break; case SE_Ff_ReuseTimeMax: - if (spell.recast_time > base1) { + if (spell.recast_time > base_value) { LimitFailure = true; } break; case SE_Ff_Value_Min: - index_id = GetSpellEffectIndex(spell_id, base2); - if (index_id >= 0 && spell.base[index_id] < base1) { + index_id = GetSpellEffectIndex(spell_id, limit_value); + if (index_id >= 0 && spell.base_value[index_id] < base_value) { LimitFailure = true; } break; case SE_Ff_Value_Max: - index_id = GetSpellEffectIndex(spell_id, base2); - if (index_id >= 0 && spell.base[index_id] > base1) { + index_id = GetSpellEffectIndex(spell_id, limit_value); + if (index_id >= 0 && spell.base_value[index_id] > base_value) { LimitFailure = true; } break; case SE_Ff_Override_NotFocusable: - if (base1 == 1) { + if (base_value == 1) { not_focusable = false; } break; @@ -4921,7 +4920,7 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) LimitFailure = true; } else { - focus_reuse_time = base2; + focus_reuse_time = limit_value; } break; @@ -4933,98 +4932,98 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) // Handle Focus Effects case SE_ImprovedDamage: - if (type == focusImprovedDamage && base1 > value) { - value = base1; + if (type == focusImprovedDamage && base_value > value) { + value = base_value; } break; case SE_ImprovedDamage2: - if (type == focusImprovedDamage2 && base1 > value) { - value = base1; + if (type == focusImprovedDamage2 && base_value > value) { + value = base_value; } break; case SE_Fc_Amplify_Mod: - if (type == focusFcAmplifyMod && base1 > value) { - value = base1; + if (type == focusFcAmplifyMod && base_value > value) { + value = base_value; } break; case SE_ImprovedHeal: - if (type == focusImprovedHeal && base1 > value) { - value = base1; + if (type == focusImprovedHeal && base_value > value) { + value = base_value; } break; case SE_ReduceManaCost: if (type == focusManaCost) { - value = base1; + value = base_value; } break; case SE_IncreaseSpellHaste: - if (type == focusSpellHaste && base1 > value) { - value = base1; + if (type == focusSpellHaste && base_value > value) { + value = base_value; } break; case SE_Fc_CastTimeMod2: - if (type == focusFcCastTimeMod2 && base1 > value) { - value = base1; + if (type == focusFcCastTimeMod2 && base_value > value) { + value = base_value; } break; case SE_Fc_CastTimeAmt: - if (type == focusFcCastTimeAmt && base1 > value) { - value = base1; + if (type == focusFcCastTimeAmt && base_value > value) { + value = base_value; } break; case SE_IncreaseSpellDuration: - if (type == focusSpellDuration && base1 > value) { - value = base1; + if (type == focusSpellDuration && base_value > value) { + value = base_value; } break; case SE_SpellDurationIncByTic: - if (type == focusSpellDurByTic && base1 > value) { - value = base1; + if (type == focusSpellDurByTic && base_value > value) { + value = base_value; } break; case SE_SwarmPetDuration: - if (type == focusSwarmPetDuration && base1 > value) { - value = base1; + if (type == focusSwarmPetDuration && base_value > value) { + value = base_value; } break; case SE_IncreaseRange: - if (type == focusRange && base1 > value) { - value = base1; + if (type == focusRange && base_value > value) { + value = base_value; } break; case SE_ReduceReagentCost: - if (type == focusReagentCost && base1 > value) { - value = base1; + if (type == focusReagentCost && base_value > value) { + value = base_value; } break; case SE_PetPowerIncrease: - if (type == focusPetPower && base1 > value) { - value = base1; + if (type == focusPetPower && base_value > value) { + value = base_value; } break; case SE_SpellResistReduction: - if (type == focusResistRate && base1 > value) { - value = base1; + if (type == focusResistRate && base_value > value) { + value = base_value; } break; case SE_Fc_ResistIncoming: - if (type == focusFcResistIncoming && base1 > value) { - value = base1; + if (type == focusFcResistIncoming && base_value > value) { + value = base_value; } break; @@ -5032,32 +5031,32 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) if (type == focusSpellHateMod) { if (value != 0) { if (value > 0) { - if (base1 > value) { - value = base1; + if (base_value > value) { + value = base_value; } } else { - if (base1 < value) { - value = base1; + if (base_value < value) { + value = base_value; } } } else { - value = base1; + value = base_value; } } break; case SE_ReduceReuseTimer: if (type == focusReduceRecastTime) { - value = base1 / 1000; + value = base_value / 1000; } break; case SE_TriggerOnCast: if (type == focusTriggerOnCast) { - if (zone->random.Roll(base1)) { - value = base2; + if (zone->random.Roll(base_value)) { + value = limit_value; } else { value = 0; @@ -5068,19 +5067,19 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) case SE_FcSpellVulnerability: if (type == focusSpellVulnerability) { - value = base1; + value = base_value; } break; case SE_Fc_Spell_Damage_Pct_IncomingPC: if (type == focusFcSpellDamagePctIncomingPC) { - value = base1; + value = base_value; } break; case SE_BlockNextSpellFocus: if (type == focusBlockNextSpell) { - if (zone->random.Roll(base1)) { + if (zone->random.Roll(base_value)) { value = 1; } } @@ -5088,123 +5087,123 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) case SE_FcTwincast: if (type == focusTwincast && !IsEffectInSpell(spell_id, SE_TwinCastBlocker)) { - value = base1; + value = base_value; } break; // Note if using these as AA, make sure this is first focus used. case SE_SympatheticProc: if (type == focusSympatheticProc) { - value = base2; + value = limit_value; } break; case SE_FcDamageAmt: if (type == focusFcDamageAmt) { - value = base1; + value = base_value; } break; case SE_FcDamageAmt2: if (type == focusFcDamageAmt2) { - value = base1; + value = base_value; } break; case SE_Fc_Amplify_Amt: if (type == focusFcAmplifyAmt) { - value = base1; + value = base_value; } break; case SE_FcDamageAmtCrit: if (type == focusFcDamageAmtCrit) { - value = base1; + value = base_value; } break; case SE_FcDamageAmtIncoming: if (type == focusFcDamageAmtIncoming) { - value = base1; + value = base_value; } break; case SE_Fc_Spell_Damage_Amt_IncomingPC: if (type == focusFcSpellDamageAmtIncomingPC) { - value = base1; + value = base_value; } break; case SE_FcHealAmtIncoming: if (type == focusFcHealAmtIncoming) { - value = base1; + value = base_value; } break; case SE_FcHealPctCritIncoming: if (type == focusFcHealPctCritIncoming) { - value = base1; + value = base_value; } break; case SE_FcHealAmtCrit: if (type == focusFcHealAmtCrit) { - value = base1; + value = base_value; } break; case SE_FcHealAmt: if (type == focusFcHealAmt) { - value = base1; + value = base_value; } break; case SE_FcHealPctIncoming: if (type == focusFcHealPctIncoming) { - value = base1; + value = base_value; } break; case SE_FcBaseEffects: if (type == focusFcBaseEffects) { - value = base1; + value = base_value; } break; case SE_FcDamagePctCrit: if (type == focusFcDamagePctCrit) { - value = base1; + value = base_value; } break; case SE_FcIncreaseNumHits: if (type == focusIncreaseNumHits) { - value = base1; + value = base_value; } break; case SE_FcLimitUse: if (type == focusFcLimitUse) { - value = base1; + value = base_value; } break; case SE_FcMute: if (type == focusFcMute) { - value = base1; + value = base_value; } break; case SE_FcStunTimeMod: if (type == focusFcStunTimeMod) { - value = base1; + value = base_value; } break; case SE_Fc_Cast_Spell_On_Land: if (type == focusFcCastSpellOnLand) { - if (zone->random.Roll(base1)) { - value = base2; + if (zone->random.Roll(base_value)) { + value = limit_value; } break; } @@ -5278,30 +5277,30 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo for (int i = 0; i < EFFECT_COUNT; i++) { - switch (focus_spell.effectid[i]) { + switch (focus_spell.effect_id[i]) { case SE_Blank: break; case SE_LimitResist: - if (focus_spell.base[i] < 0) { - if (spell.resisttype == -focus_spell.base[i]) { // Exclude + if (focus_spell.base_value[i] < 0) { + if (spell.resist_type == -focus_spell.base_value[i]) { // Exclude return 0; } } else { LimitInclude[IncludeExistsSELimitResist] = true; - if (spell.resisttype == focus_spell.base[i]) { // Include + if (spell.resist_type == focus_spell.base_value[i]) { // Include LimitInclude[IncludeFoundSELimitResist] = true; } } break; case SE_LimitInstant: - if (focus_spell.base[i] == 1 && spell.buffduration) { // Fail if not instant + if (focus_spell.base_value[i] == 1 && spell.buff_duration) { // Fail if not instant return 0; } - if (focus_spell.base[i] == 0 && (spell.buffduration == 0)) { // Fail if instant + if (focus_spell.base_value[i] == 0 && (spell.buff_duration == 0)) { // Fail if instant return 0; } @@ -5312,13 +5311,13 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo break; } spell_level = spell.classes[(GetClass() % 17) - 1]; - lvldiff = spell_level - focus_spell.base[i]; + lvldiff = spell_level - focus_spell.base_value[i]; // every level over cap reduces the effect by focus_spell.base2[i] percent unless from a clicky // when ItemCastsUseFocus is true if (lvldiff > 0 && (spell_level <= RuleI(Character, MaxLevel) || RuleB(Character, ItemCastsUseFocus) == false)) { - if (focus_spell.base2[i] > 0) { - lvlModifier -= focus_spell.base2[i] * lvldiff; + if (focus_spell.limit_value[i] > 0) { + lvlModifier -= focus_spell.limit_value[i] * lvldiff; if (lvlModifier < 1) { return 0; } @@ -5333,60 +5332,60 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo if (IsNPC()) { break; } - if (spell.classes[(GetClass() % 17) - 1] < focus_spell.base[i]) { + if (spell.classes[(GetClass() % 17) - 1] < focus_spell.base_value[i]) { return (0); } break; case SE_LimitCastTimeMin: - if (spells[spell_id].cast_time < (uint16) focus_spell.base[i]) { + if (spells[spell_id].cast_time < (uint16) focus_spell.base_value[i]) { return (0); } break; case SE_LimitCastTimeMax: - if (spells[spell_id].cast_time > (uint16) focus_spell.base[i]) { + if (spells[spell_id].cast_time > (uint16) focus_spell.base_value[i]) { return (0); } break; case SE_LimitSpell: - if (focus_spell.base[i] < 0) { // Exclude - if (spell_id == -focus_spell.base[i]) { + if (focus_spell.base_value[i] < 0) { // Exclude + if (spell_id == -focus_spell.base_value[i]) { return (0); } } else { LimitInclude[IncludeExistsSELimitSpell] = true; - if (spell_id == focus_spell.base[i]) { // Include + if (spell_id == focus_spell.base_value[i]) { // Include LimitInclude[IncludeFoundSELimitSpell] = true; } } break; case SE_LimitMinDur: - if (focus_spell.base[i] > - CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration)) { + if (focus_spell.base_value[i] > + CalcBuffDuration_formula(GetLevel(), spell.buff_duration_formula, spell.buff_duration)) { return (0); } break; case SE_LimitEffect: - if (focus_spell.base[i] < 0) { - if (IsEffectInSpell(spell_id, -focus_spell.base[i])) { // Exclude + if (focus_spell.base_value[i] < 0) { + if (IsEffectInSpell(spell_id, -focus_spell.base_value[i])) { // Exclude return 0; } } else { LimitInclude[IncludeExistsSELimitEffect] = true; - if (IsEffectInSpell(spell_id, focus_spell.base[i])) { // Include + if (IsEffectInSpell(spell_id, focus_spell.base_value[i])) { // Include LimitInclude[IncludeFoundSELimitEffect] = true; } } break; case SE_LimitSpellType: - switch (focus_spell.base[i]) { + switch (focus_spell.base_value[i]) { case 0: if (!IsDetrimentalSpell(spell_id)) { return 0; @@ -5398,71 +5397,71 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo } break; default: - LogInfo("CalcFocusEffect: unknown limit spelltype [{}]", focus_spell.base[i]); + LogInfo("CalcFocusEffect: unknown limit spelltype [{}]", focus_spell.base_value[i]); break; } break; case SE_LimitManaMin: - if (spell.mana < focus_spell.base[i]) { + if (spell.mana < focus_spell.base_value[i]) { return 0; } break; case SE_LimitManaMax: - if (spell.mana > focus_spell.base[i]) { + if (spell.mana > focus_spell.base_value[i]) { return 0; } break; case SE_LimitTarget: - if (focus_spell.base[i] < 0) { - if (-focus_spell.base[i] == spell.targettype) { // Exclude + if (focus_spell.base_value[i] < 0) { + if (-focus_spell.base_value[i] == spell.target_type) { // Exclude return 0; } } else { LimitInclude[IncludeExistsSELimitTarget] = true; - if (focus_spell.base[i] == spell.targettype) { // Include + if (focus_spell.base_value[i] == spell.target_type) { // Include LimitInclude[IncludeFoundSELimitTarget] = true; } } break; case SE_LimitCombatSkills: - if (focus_spell.base[i] == 0 && + if (focus_spell.base_value[i] == 0 && (IsCombatSkill(spell_id) || IsCombatProc(spell_id))) { // Exclude Discs / Procs return 0; } - else if (focus_spell.base[i] == 1 && (!IsCombatSkill(spell_id) || !IsCombatProc(spell_id))) { // Exclude Spells + else if (focus_spell.base_value[i] == 1 && (!IsCombatSkill(spell_id) || !IsCombatProc(spell_id))) { // Exclude Spells return 0; } break; case SE_LimitSpellGroup: - if (focus_spell.base[i] < 0) { - if (-focus_spell.base[i] == spell.spellgroup) { // Exclude + if (focus_spell.base_value[i] < 0) { + if (-focus_spell.base_value[i] == spell.spell_group) { // Exclude return 0; } } else { LimitInclude[IncludeExistsSELimitSpellGroup] = true; - if (focus_spell.base[i] == spell.spellgroup) { // Include + if (focus_spell.base_value[i] == spell.spell_group) { // Include LimitInclude[IncludeFoundSELimitSpellGroup] = true; } } break; case SE_LimitCastingSkill: - if (focus_spell.base[i] < 0) { - if (-focus_spell.base[i] == spell.skill) { + if (focus_spell.base_value[i] < 0) { + if (-focus_spell.base_value[i] == spell.skill) { return 0; } } else { LimitInclude[IncludeExistsSELimitCastingSkill] = true; - if (focus_spell.base[i] == spell.skill) { + if (focus_spell.base_value[i] == spell.skill) { LimitInclude[IncludeFoundSELimitCastingSkill] = true; } } @@ -5471,70 +5470,70 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_LimitClass: // Do not use this limit more then once per spell. If multiple class, treat value like items // would. - if (!PassLimitClass(focus_spell.base[i], GetClass())) { + if (!PassLimitClass(focus_spell.base_value[i], GetClass())) { return 0; } break; case SE_LimitRace: - if (focus_spell.base[i] != GetRace()) { + if (focus_spell.base_value[i] != GetRace()) { return 0; } break; case SE_LimitUseMin: - if (focus_spell.base[i] > spell.numhits) { + if (focus_spell.base_value[i] > spell.hit_number) { return 0; } break; case SE_LimitUseType: - if (focus_spell.base[i] != spell.numhitstype) { + if (focus_spell.base_value[i] != spell.hit_number_type) { return 0; } break; case SE_CastonFocusEffect: - if (focus_spell.base[i] > 0) { - Caston_spell_id = focus_spell.base[i]; + if (focus_spell.base_value[i] > 0) { + Caston_spell_id = focus_spell.base_value[i]; } break; case SE_LimitSpellClass: - if (focus_spell.base[i] < 0) { // Exclude - if (-focus_spell.base[i] == spell.spell_class) { + if (focus_spell.base_value[i] < 0) { // Exclude + if (-focus_spell.base_value[i] == spell.spell_class) { return 0; } } else { LimitInclude[IncludeExistsSELimitSpellClass] = true; - if (focus_spell.base[i] == spell.spell_class) { // Include + if (focus_spell.base_value[i] == spell.spell_class) { // Include LimitInclude[IncludeFoundSELimitSpellClass] = true; } } break; case SE_LimitSpellSubclass: - if (focus_spell.base[i] < 0) { // Exclude - if (-focus_spell.base[i] == spell.spell_subclass) { + if (focus_spell.base_value[i] < 0) { // Exclude + if (-focus_spell.base_value[i] == spell.spell_subclass) { return 0; } } else { LimitInclude[IncludeExistsSELimitSpellSubclass] = true; - if (focus_spell.base[i] == spell.spell_subclass) { // Include + if (focus_spell.base_value[i] == spell.spell_subclass) { // Include LimitInclude[IncludeFoundSELimitSpellSubclass] = true; } } break; case SE_Ff_Same_Caster://hmm do i need to pass casterid from buff slot here - if (focus_spell.base[i] == 0) { + if (focus_spell.base_value[i] == 0) { if (casterid == GetID()) { return 0; }//Mob casting is same as target, fail if you are casting on yourself. } - else if (focus_spell.base[i] == 1) { + else if (focus_spell.base_value[i] == 1) { if (casterid != GetID()) { return 0; }//Mob casting is not same as target, fail if you are not casting on yourself. @@ -5543,51 +5542,51 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_Ff_CasterClass: // Do not use this limit more then once per spell. If multiple class, treat value like items would. - if (!PassLimitClass(focus_spell.base[i], GetClass())) { + if (!PassLimitClass(focus_spell.base_value[i], GetClass())) { return 0; } break; case SE_Ff_DurationMax: - if (focus_spell.base[i] > spell.buffduration) { + if (focus_spell.base_value[i] > spell.buff_duration) { return 0; } break; case SE_Ff_Endurance_Min: - if (spell.EndurCost < focus_spell.base[i]) { + if (spell.endurance_cost < focus_spell.base_value[i]) { return 0; } break; case SE_Ff_Endurance_Max: - if (spell.EndurCost > focus_spell.base[i]) { + if (spell.endurance_cost > focus_spell.base_value[i]) { return 0; } break; case SE_Ff_ReuseTimeMin: - if (spell.recast_time < focus_spell.base[i]) { + if (spell.recast_time < focus_spell.base_value[i]) { return 0; } break; case SE_Ff_ReuseTimeMax: - if (spell.recast_time > focus_spell.base[i]) { + if (spell.recast_time > focus_spell.base_value[i]) { return 0; } break; case SE_Ff_Value_Min: - index_id = GetSpellEffectIndex(spell_id, focus_spell.base2[i]); - if (index_id >= 0 && spell.base[index_id] < focus_spell.base[i]) { + index_id = GetSpellEffectIndex(spell_id, focus_spell.limit_value[i]); + if (index_id >= 0 && spell.base_value[index_id] < focus_spell.base_value[i]) { return 0; } break; case SE_Ff_Value_Max: - index_id = GetSpellEffectIndex(spell_id, focus_spell.base2[i]); - if (index_id >= 0 && spell.base[index_id] > focus_spell.base[i]) { + index_id = GetSpellEffectIndex(spell_id, focus_spell.limit_value[i]); + if (index_id >= 0 && spell.base_value[index_id] > focus_spell.base_value[i]) { return 0; } break; @@ -5597,123 +5596,123 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo return 0; } else { - focus_reuse_time = focus_spell.base2[i]; + focus_reuse_time = focus_spell.limit_value[i]; } break; // handle effects case SE_ImprovedDamage: if (type == focusImprovedDamage) { - value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); + value = GetFocusRandomEffectivenessValue(focus_spell.base_value[i], focus_spell.limit_value[i], best_focus); } break; case SE_ImprovedDamage2: if (type == focusImprovedDamage2) { - value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); + value = GetFocusRandomEffectivenessValue(focus_spell.base_value[i], focus_spell.limit_value[i], best_focus); } break; case SE_Fc_Amplify_Mod: - if (type == focusFcAmplifyMod && focus_spell.base[i] > value) { - value = focus_spell.base[i]; + if (type == focusFcAmplifyMod && focus_spell.base_value[i] > value) { + value = focus_spell.base_value[i]; } break; case SE_ImprovedHeal: if (type == focusImprovedHeal) { - value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); + value = GetFocusRandomEffectivenessValue(focus_spell.base_value[i], focus_spell.limit_value[i], best_focus); } break; case SE_ReduceManaCost: if (type == focusManaCost) { - value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); + value = GetFocusRandomEffectivenessValue(focus_spell.base_value[i], focus_spell.limit_value[i], best_focus); } break; case SE_IncreaseSpellHaste: - if (type == focusSpellHaste && focus_spell.base[i] > value) { - value = focus_spell.base[i]; + if (type == focusSpellHaste && focus_spell.base_value[i] > value) { + value = focus_spell.base_value[i]; } break; case SE_Fc_CastTimeMod2: - if (type == focusFcCastTimeMod2 && focus_spell.base[i] > value) { - value = focus_spell.base[i]; + if (type == focusFcCastTimeMod2 && focus_spell.base_value[i] > value) { + value = focus_spell.base_value[i]; } break; case SE_Fc_CastTimeAmt: - if (type == focusFcCastTimeAmt && focus_spell.base[i] > value) { - value = focus_spell.base[i]; + if (type == focusFcCastTimeAmt && focus_spell.base_value[i] > value) { + value = focus_spell.base_value[i]; } break; case SE_IncreaseSpellDuration: - if (type == focusSpellDuration && focus_spell.base[i] > value) { - value = focus_spell.base[i]; + if (type == focusSpellDuration && focus_spell.base_value[i] > value) { + value = focus_spell.base_value[i]; } break; case SE_SpellDurationIncByTic: - if (type == focusSpellDurByTic && focus_spell.base[i] > value) { - value = focus_spell.base[i]; + if (type == focusSpellDurByTic && focus_spell.base_value[i] > value) { + value = focus_spell.base_value[i]; } break; case SE_SwarmPetDuration: - if (type == focusSwarmPetDuration && focus_spell.base[i] > value) { - value = focus_spell.base[i]; + if (type == focusSwarmPetDuration && focus_spell.base_value[i] > value) { + value = focus_spell.base_value[i]; } break; case SE_IncreaseRange: - if (type == focusRange && focus_spell.base[i] > value) { - value = focus_spell.base[i]; + if (type == focusRange && focus_spell.base_value[i] > value) { + value = focus_spell.base_value[i]; } break; case SE_ReduceReagentCost: if (type == focusReagentCost) { - value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); + value = GetFocusRandomEffectivenessValue(focus_spell.base_value[i], focus_spell.limit_value[i], best_focus); } break; case SE_PetPowerIncrease: - if (type == focusPetPower && focus_spell.base[i] > value) { - value = focus_spell.base[i]; + if (type == focusPetPower && focus_spell.base_value[i] > value) { + value = focus_spell.base_value[i]; } break; case SE_SpellResistReduction: if (type == focusResistRate) { - value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); + value = GetFocusRandomEffectivenessValue(focus_spell.base_value[i], focus_spell.limit_value[i], best_focus); } break; case SE_Fc_ResistIncoming: - if (type == focusFcResistIncoming && focus_spell.base[i] > value) { - value = focus_spell.base[i]; + if (type == focusFcResistIncoming && focus_spell.base_value[i] > value) { + value = focus_spell.base_value[i]; } break; case SE_SpellHateMod: if (type == focusSpellHateMod) { - value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); + value = GetFocusRandomEffectivenessValue(focus_spell.base_value[i], focus_spell.limit_value[i], best_focus); } break; case SE_ReduceReuseTimer: if (type == focusReduceRecastTime) { - value = focus_spell.base[i] / 1000; + value = focus_spell.base_value[i] / 1000; } break; case SE_TriggerOnCast: if (type == focusTriggerOnCast) { - if (zone->random.Roll(focus_spell.base[i])) { - value = focus_spell.base2[i]; + if (zone->random.Roll(focus_spell.base_value[i])) { + value = focus_spell.limit_value[i]; } else { value = 0; @@ -5723,7 +5722,7 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_BlockNextSpellFocus: if (type == focusBlockNextSpell) { - if (zone->random.Roll(focus_spell.base[i])) { + if (zone->random.Roll(focus_spell.base_value[i])) { value = 1; } } @@ -5737,140 +5736,140 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_FcSpellVulnerability: if (type == focusSpellVulnerability) { - value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); + value = GetFocusRandomEffectivenessValue(focus_spell.base_value[i], focus_spell.limit_value[i], best_focus); } break; case SE_Fc_Spell_Damage_Pct_IncomingPC: if (type == focusFcSpellDamagePctIncomingPC) { - value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); + value = GetFocusRandomEffectivenessValue(focus_spell.base_value[i], focus_spell.limit_value[i], best_focus); } break; case SE_FcTwincast: if (type == focusTwincast && !IsEffectInSpell(spell_id, SE_TwinCastBlocker)) { - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; } break; case SE_FcDamageAmt: if (type == focusFcDamageAmt) { - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; } break; case SE_FcDamageAmt2: if (type == focusFcDamageAmt2) { - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; } break; case SE_Fc_Amplify_Amt: if (type == focusFcAmplifyAmt) { - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; } break; case SE_FcDamageAmtCrit: if (type == focusFcDamageAmtCrit) { - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; } break; case SE_FcDamageAmtIncoming: if (type == focusFcDamageAmtIncoming) { - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; } break; case SE_Fc_Spell_Damage_Amt_IncomingPC: if (type == focusFcSpellDamageAmtIncomingPC) { - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; } break; case SE_FcHealAmtIncoming: if (type == focusFcHealAmtIncoming) { - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; } break; case SE_FcDamagePctCrit: if (type == focusFcDamagePctCrit) { - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; } break; case SE_FcHealPctCritIncoming: if (type == focusFcHealPctCritIncoming) { - value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); + value = GetFocusRandomEffectivenessValue(focus_spell.base_value[i], focus_spell.limit_value[i], best_focus); } break; case SE_FcHealAmtCrit: if (type == focusFcHealAmtCrit) { - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; } break; case SE_FcHealAmt: if (type == focusFcHealAmt) { - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; } break; case SE_FcHealPctIncoming: if (type == focusFcHealPctIncoming) { - value = GetFocusRandomEffectivenessValue(focus_spell.base[i], focus_spell.base2[i], best_focus); + value = GetFocusRandomEffectivenessValue(focus_spell.base_value[i], focus_spell.limit_value[i], best_focus); } break; case SE_FcBaseEffects: if (type == focusFcBaseEffects) { - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; } break; case SE_FcIncreaseNumHits: if (type == focusIncreaseNumHits) { - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; } break; case SE_FcLimitUse: if (type == focusFcLimitUse) { - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; } break; case SE_FcMute: if (type == focusFcMute) { - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; } break; case SE_FcStunTimeMod: if (type == focusFcStunTimeMod) { - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; } break; case SE_FcTimerRefresh: if (type == focusFcTimerRefresh) { - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; } break; case SE_FcTimerLockout: if (type == focusFcTimerLockout) { - value = focus_spell.base[i]; + value = focus_spell.base_value[i]; } break; case SE_Fc_Cast_Spell_On_Land: if (type == focusFcCastSpellOnLand) { - if (zone->random.Roll(focus_spell.base[i])) { - value = focus_spell.base2[i]; + if (zone->random.Roll(focus_spell.base_value[i])) { + value = focus_spell.limit_value[i]; } break; } @@ -5880,7 +5879,7 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo // since they have all kinds of extra effects on them. default: LogInfo("CalcFocusEffect: unknown effectid [{}]", - focus_spell.effectid[i]); + focus_spell.effect_id[i]); #endif } } @@ -5899,7 +5898,7 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo EQ::spells::CastingSlot::Item, 0, -1, - spells[Caston_spell_id].ResistDiff + spells[Caston_spell_id].resist_difficulty ); } } @@ -6018,12 +6017,12 @@ bool Mob::TryTriggerOnCastProc(uint16 focusspellid, uint16 spell_id, uint16 proc if (IsValidSpell(proc_spellid) && spell_id != focusspellid && spell_id != proc_spellid) { Mob* proc_target = GetTarget(); if (proc_target) { - SpellFinished(proc_spellid, proc_target, EQ::spells::CastingSlot::Item, 0, -1, spells[proc_spellid].ResistDiff); + SpellFinished(proc_spellid, proc_target, EQ::spells::CastingSlot::Item, 0, -1, spells[proc_spellid].resist_difficulty); return true; } // Edge cases where proc spell does not require a target such as PBAE, allows proc to still occur even if target potentially dead. Live spells exist with PBAE procs. else if (!SpellRequiresTarget(proc_spellid)) { - SpellFinished(proc_spellid, this, EQ::spells::CastingSlot::Item, 0, -1, spells[proc_spellid].ResistDiff); + SpellFinished(proc_spellid, this, EQ::spells::CastingSlot::Item, 0, -1, spells[proc_spellid].resist_difficulty); return true; } } @@ -6059,7 +6058,7 @@ uint16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) { proc_spellid = CalcFocusEffect(type, TempItem->Focus.Effect, spell_id); if (IsValidSpell(proc_spellid)){ - ProcChance = GetSympatheticProcChances(spell_id, spells[TempItem->Focus.Effect].base[0], TempItem->ProcRate); + ProcChance = GetSympatheticProcChances(spell_id, spells[TempItem->Focus.Effect].base_value[0], TempItem->ProcRate); if(zone->random.Roll(ProcChance)) SympatheticProcList.push_back(proc_spellid); } @@ -6078,7 +6077,7 @@ uint16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) { if (TempItemAug && TempItemAug->Focus.Effect > 0 && IsValidSpell(TempItemAug->Focus.Effect)) { proc_spellid = CalcFocusEffect(type, TempItemAug->Focus.Effect, spell_id); if (IsValidSpell(proc_spellid)){ - ProcChance = GetSympatheticProcChances(spell_id, spells[TempItemAug->Focus.Effect].base[0], TempItemAug->ProcRate); + ProcChance = GetSympatheticProcChances(spell_id, spells[TempItemAug->Focus.Effect].base_value[0], TempItemAug->ProcRate); if(zone->random.Roll(ProcChance)) SympatheticProcList.push_back(proc_spellid); } @@ -6133,7 +6132,7 @@ uint16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) { proc_spellid = CalcAAFocus(type, *rank, spell_id); if (IsValidSpell(proc_spellid)) { - ProcChance = GetSympatheticProcChances(spell_id, rank->effects[0].base1); + ProcChance = GetSympatheticProcChances(spell_id, rank->effects[0].base_value); if (zone->random.Roll(ProcChance)) SympatheticProcList.push_back(proc_spellid); } @@ -6381,7 +6380,7 @@ int32 Client::GetFocusEffect(focusType type, uint16 spell_id) realTotal2 = CalcFocusEffect(type, focusspell_tracker, spell_id); // For effects like gift of mana that only fire once, save the spellid into an array that consists of all available buff slots. - if(buff_tracker >= 0 && buffs[buff_tracker].numhits > 0) { + if(buff_tracker >= 0 && buffs[buff_tracker].hit_number > 0) { m_spellHitsLeft[buff_tracker] = focusspell_tracker; } } @@ -6537,7 +6536,7 @@ int32 NPC::GetFocusEffect(focusType type, uint16 spell_id) { realTotal2 = CalcFocusEffect(type, focusspell_tracker, spell_id); // For effects like gift of mana that only fire once, save the spellid into an array that consists of all available buff slots. - if(buff_tracker >= 0 && buffs[buff_tracker].numhits > 0) { + if(buff_tracker >= 0 && buffs[buff_tracker].hit_number > 0) { m_spellHitsLeft[buff_tracker] = focusspell_tracker; } } @@ -6577,16 +6576,16 @@ void Mob::CheckNumHitsRemaining(NumHit type, int32 buff_slot, uint16 spell_id) //Spell specific procs [Type 7,10,11] if (IsValidSpell(spell_id)) { for (int d = 0; d < buff_max; d++) { - if (buffs[d].spellid == spell_id && buffs[d].numhits > 0 && - spells[buffs[d].spellid].numhitstype == static_cast(type)) { + if (buffs[d].spellid == spell_id && buffs[d].hit_number > 0 && + spells[buffs[d].spellid].hit_number_type == static_cast(type)) { #ifdef BOTS buff_name = spells[buffs[d].spellid].name; - buff_counter = (buffs[d].numhits - 1); + buff_counter = (buffs[d].hit_number - 1); buff_update = true; #endif - if (--buffs[d].numhits == 0) { + if (--buffs[d].hit_number == 0) { CastOnNumHitFade(buffs[d].spellid); if (!TryFadeEffect(d)) BuffFadeBySlot(d, true); @@ -6597,11 +6596,11 @@ void Mob::CheckNumHitsRemaining(NumHit type, int32 buff_slot, uint16 spell_id) } } else if (type == NumHit::MatchingSpells) { if (buff_slot >= 0) { - if (--buffs[buff_slot].numhits == 0) { + if (--buffs[buff_slot].hit_number == 0) { #ifdef BOTS buff_name = spells[buffs[buff_slot].spellid].name; - buff_counter = (buffs[buff_slot].numhits - 1); + buff_counter = (buffs[buff_slot].hit_number - 1); buff_update = true; #endif @@ -6620,11 +6619,11 @@ void Mob::CheckNumHitsRemaining(NumHit type, int32 buff_slot, uint16 spell_id) #ifdef BOTS buff_name = spells[buffs[d].spellid].name; - buff_counter = (buffs[d].numhits - 1); + buff_counter = (buffs[d].hit_number - 1); buff_update = true; #endif - if (--buffs[d].numhits == 0) { + if (--buffs[d].hit_number == 0) { CastOnNumHitFade(buffs[d].spellid); m_spellHitsLeft[d] = 0; if (!TryFadeEffect(d)) @@ -6637,16 +6636,16 @@ void Mob::CheckNumHitsRemaining(NumHit type, int32 buff_slot, uint16 spell_id) } } else { for (int d = 0; d < buff_max; d++) { - if (IsValidSpell(buffs[d].spellid) && buffs[d].numhits > 0 && - spells[buffs[d].spellid].numhitstype == static_cast(type)) { + if (IsValidSpell(buffs[d].spellid) && buffs[d].hit_number > 0 && + spells[buffs[d].spellid].hit_number_type == static_cast(type)) { #ifdef BOTS buff_name = spells[buffs[d].spellid].name; - buff_counter = (buffs[d].numhits - 1); + buff_counter = (buffs[d].hit_number - 1); buff_update = true; #endif - if (--buffs[d].numhits == 0) { + if (--buffs[d].hit_number == 0) { CastOnNumHitFade(buffs[d].spellid); if (!TryFadeEffect(d)) BuffFadeBySlot(d, true); @@ -6684,7 +6683,7 @@ void Mob::CheckNumHitsRemaining(NumHit type, int32 buff_slot, uint16 spell_id) uint16 Mob::GetProcID(uint16 spell_id, uint8 effect_index) { if (!RuleB(Spells, SHDProcIDOffByOne)) // UF+ spell files - return spells[spell_id].base[effect_index]; + return spells[spell_id].base_value[effect_index]; // We should actually just be checking if the mob is SHD, but to not force // custom servers to create new spells, we will still do this @@ -6701,9 +6700,9 @@ uint16 Mob::GetProcID(uint16 spell_id, uint8 effect_index) } if (sk && !other) - return spells[spell_id].base[effect_index] + 1; + return spells[spell_id].base_value[effect_index] + 1; else - return spells[spell_id].base[effect_index]; + return spells[spell_id].base_value[effect_index]; } bool Mob::TryDivineSave() @@ -6896,8 +6895,8 @@ float Mob::GetSympatheticProcChances(uint16 spell_id, int16 ProcRateMod, int32 I int16 Mob::GetSympatheticSpellProcRate(uint16 spell_id) { for (int i = 0; i < EFFECT_COUNT; i++){ - if (spells[spell_id].effectid[i] == SE_SympatheticProc) - return spells[spell_id].base[i]; + if (spells[spell_id].effect_id[i] == SE_SympatheticProc) + return spells[spell_id].base_value[i]; } return 0; @@ -6906,8 +6905,8 @@ int16 Mob::GetSympatheticSpellProcRate(uint16 spell_id) uint16 Mob::GetSympatheticSpellProcID(uint16 spell_id) { for (int i = 0; i < EFFECT_COUNT; i++){ - if (spells[spell_id].effectid[i] == SE_SympatheticProc) - return spells[spell_id].base2[i]; + if (spells[spell_id].effect_id[i] == SE_SympatheticProc) + return spells[spell_id].limit_value[i]; } return 0; @@ -6948,17 +6947,17 @@ int32 Mob::GetFcDamageAmtIncoming(Mob *caster, uint32 spell_id, bool use_skill, int32 temp_dmg = 0; for (int e = 0; e < EFFECT_COUNT; e++) { - if (spells[buffs[i].spellid].effectid[e] == SE_FcDamageAmtIncoming){ - temp_dmg += spells[buffs[i].spellid].base[e]; + if (spells[buffs[i].spellid].effect_id[e] == SE_FcDamageAmtIncoming){ + temp_dmg += spells[buffs[i].spellid].base_value[e]; continue; } if (!skill_found){ - if ((spells[buffs[i].spellid].effectid[e] == SE_LimitToSkill) || - (spells[buffs[i].spellid].effectid[e] == SE_LimitCastingSkill)){ + if ((spells[buffs[i].spellid].effect_id[e] == SE_LimitToSkill) || + (spells[buffs[i].spellid].effect_id[e] == SE_LimitCastingSkill)){ limit_exists = true; - if (spells[buffs[i].spellid].base[e] == skill) + if (spells[buffs[i].spellid].base_value[e] == skill) skill_found = true; } } @@ -7086,18 +7085,18 @@ uint16 Mob::GetSpellEffectResistChance(uint16 spell_id) for(int d = 0; d < MAX_RESISTABLE_EFFECTS*2; d+=2) { - if (spells[spell_id].effectid[i] == aabonuses.SEResist[d]){ + if (spells[spell_id].effect_id[i] == aabonuses.SEResist[d]){ resist_chance += aabonuses.SEResist[d+1]; found = true; } - if (spells[spell_id].effectid[i] == itembonuses.SEResist[d]){ + if (spells[spell_id].effect_id[i] == itembonuses.SEResist[d]){ resist_chance += itembonuses.SEResist[d+1]; found = true; } - if (spells[spell_id].effectid[i] == spellbonuses.SEResist[d]){ + if (spells[spell_id].effect_id[i] == spellbonuses.SEResist[d]){ resist_chance += spellbonuses.SEResist[d+1]; found = true; } @@ -8129,7 +8128,7 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed) { if (!spell_target) return false; - uint8 anim = spells[spell_id].CastingAnim; + uint8 anim = spells[spell_id].casting_animation; int slot = -1; //Make sure there is an avialable bolt to be cast. @@ -8193,7 +8192,7 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed) { //This allows limited support for server using older spell files that do not contain data for bolt graphics. else { //Only use fire graphic for fire spells. - if (spells[spell_id].resisttype == RESIST_FIRE) { + if (spells[spell_id].resist_type == RESIST_FIRE) { if (IsClient()) { if (CastToClient()->ClientVersionBit() <= 4) //Titanium needs alternate graphic. @@ -8210,7 +8209,7 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed) { ProjectileAnimation(spell_target, 0, 1, speed, 0.0f, 0.0f, arc); } - if (spells[spell_id].CastingAnim == 64) + if (spells[spell_id].casting_animation == 64) anim = 44; //Corrects for animation error. DoAnim(anim, 0, true, IsClient() ? FilterPCSpells : FilterNPCSpells); //Override the default projectile animation. @@ -8224,24 +8223,24 @@ void Mob::ResourceTap(int32 damage, uint16 spellid) return; for (int i = 0; i < EFFECT_COUNT; i++) { - if (spells[spellid].effectid[i] == SE_ResourceTap) { - damage = (damage * spells[spellid].base[i]) / 1000; + if (spells[spellid].effect_id[i] == SE_ResourceTap) { + damage = (damage * spells[spellid].base_value[i]) / 1000; if (damage) { - if (spells[spellid].max[i] && (damage > spells[spellid].max[i])) - damage = spells[spellid].max[i]; + if (spells[spellid].max_value[i] && (damage > spells[spellid].max_value[i])) + damage = spells[spellid].max_value[i]; - if (spells[spellid].base2[i] == 0) { // HP Tap + if (spells[spellid].limit_value[i] == 0) { // HP Tap if (damage > 0) HealDamage(damage); else Damage(this, -damage, 0, EQ::skills::SkillEvocation, false); } - if (spells[spellid].base2[i] == 1) // Mana Tap + if (spells[spellid].limit_value[i] == 1) // Mana Tap SetMana(GetMana() + damage); - if (spells[spellid].base2[i] == 2 && IsClient()) // Endurance Tap + if (spells[spellid].limit_value[i] == 2 && IsClient()) // Endurance Tap CastToClient()->SetEndurance(CastToClient()->GetEndurance() + damage); } @@ -8267,21 +8266,21 @@ void Mob::TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker){ for(int i = 0; i < EFFECT_COUNT; i++){ - if (spells[buffs[slot].spellid].effectid[i] == effect_id){ + if (spells[buffs[slot].spellid].effect_id[i] == effect_id){ - uint16 spell_id = spells[buffs[slot].spellid].base[i]; + uint16 spell_id = spells[buffs[slot].spellid].base_value[i]; - if (damage > spells[buffs[slot].spellid].base2[i]){ + if (damage > spells[buffs[slot].spellid].limit_value[i]){ BuffFadeBySlot(slot); if (IsValidSpell(spell_id)) { if (IsBeneficialSpell(spell_id)) - SpellFinished(spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff); + SpellFinished(spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty); else if(attacker) - SpellFinished(spell_id, attacker, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff); + SpellFinished(spell_id, attacker, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty); } } } @@ -8321,13 +8320,13 @@ void Mob::CastSpellOnLand(Mob* caster, int32 spell_id) //Step 3: Cast spells if (IsBeneficialSpell(trigger_spell_id)) { - SpellFinished(trigger_spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[trigger_spell_id].ResistDiff); + SpellFinished(trigger_spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[trigger_spell_id].resist_difficulty); } else { Mob* current_target = GetTarget(); //For now don't let players cast detrimental effects on themselves if they are targeting themselves. Need to confirm behavior. if (current_target && current_target->GetID() != GetID()) - SpellFinished(trigger_spell_id, current_target, EQ::spells::CastingSlot::Item, 0, -1, spells[trigger_spell_id].ResistDiff); + SpellFinished(trigger_spell_id, current_target, EQ::spells::CastingSlot::Item, 0, -1, spells[trigger_spell_id].resist_difficulty); } } if (i >= 0) @@ -8348,12 +8347,12 @@ void Mob::CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster) else distance = sqrt(range); - distance = EQ::Clamp(distance, spells[spell_id].min_dist, spells[spell_id].max_dist); + distance = EQ::Clamp(distance, spells[spell_id].min_distance, spells[spell_id].max_distance); - float dm_range = spells[spell_id].max_dist - spells[spell_id].min_dist; - float dm_mod_interval = spells[spell_id].max_dist_mod - spells[spell_id].min_dist_mod; - float dist_from_min = distance - spells[spell_id].min_dist; - float mod = spells[spell_id].min_dist_mod + (dist_from_min * (dm_mod_interval/dm_range)); + float dm_range = spells[spell_id].max_distance - spells[spell_id].min_distance; + float dm_mod_interval = spells[spell_id].max_distance_mod - spells[spell_id].min_distance_mod; + float dist_from_min = distance - spells[spell_id].min_distance; + float mod = spells[spell_id].min_distance_mod + (dist_from_min * (dm_mod_interval/dm_range)); mod *= 100.0f; SetSpellPowerDistanceMod(static_cast(mod)); @@ -8431,8 +8430,8 @@ bool Mob::HarmonySpellLevelCheck(int32 spell_id, Mob *target) for (int i = 0; i < EFFECT_COUNT; i++) { // not important to check limit on SE_Lull as it doesnt have one and if the other components won't land, then SE_Lull wont either - if (spells[spell_id].effectid[i] == SE_ChangeFrenzyRad || spells[spell_id].effectid[i] == SE_Harmony) { - if ((spells[spell_id].max[i] != 0 && target->GetLevel() > spells[spell_id].max[i]) || target->GetSpecialAbility(IMMUNE_PACIFY)) { + if (spells[spell_id].effect_id[i] == SE_ChangeFrenzyRad || spells[spell_id].effect_id[i] == SE_Harmony) { + if ((spells[spell_id].max_value[i] != 0 && target->GetLevel() > spells[spell_id].max_value[i]) || target->GetSpecialAbility(IMMUNE_PACIFY)) { return false; } } @@ -8494,7 +8493,7 @@ bool Mob::NegateSpellEffect(uint16 spell_id, int effect_id) //Match each of the negate effects with the current spell effect, if found, that effect will not be applied. for (int j = 0; j < EFFECT_COUNT; j++) { - if (spells[buffs[i].spellid].base2[j] == effect_id) { + if (spells[buffs[i].spellid].limit_value[j] == effect_id) { return true; } } diff --git a/zone/spells.cpp b/zone/spells.cpp index f50acd1f1..9a7ca6647 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -191,7 +191,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, return(false); } //It appears that the Sanctuary effect is removed by a check on the client side (keep this however for redundancy) - if (spellbonuses.Sanctuary && (spells[spell_id].targettype != ST_Self && GetTarget() != this) || IsDetrimentalSpell(spell_id)) + if (spellbonuses.Sanctuary && (spells[spell_id].target_type != ST_Self && GetTarget() != this) || IsDetrimentalSpell(spell_id)) BuffFadeByEffect(SE_Sanctuary); if(IsClient()){ @@ -308,7 +308,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, } else { - return(DoCastSpell(spell_id, target_id, slot, cast_time, mana_cost, oSpellWillFinish, item_slot, timer, timer_duration, spells[spell_id].ResistDiff, aa_id)); + return(DoCastSpell(spell_id, target_id, slot, cast_time, mana_cost, oSpellWillFinish, item_slot, timer, timer_duration, spells[spell_id].resist_difficulty, aa_id)); } } @@ -392,13 +392,13 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, // and a target wasn't provided, then it's us; unless TGB is on and this // is a TGB compatible spell. if((IsGroupSpell(spell_id) || - spell.targettype == ST_AEClientV1 || - spell.targettype == ST_Self || - spell.targettype == ST_AECaster || - spell.targettype == ST_Ring || - spell.targettype == ST_Beam) && target_id == 0) + spell.target_type == ST_AEClientV1 || + spell.target_type == ST_Self || + spell.target_type == ST_AECaster || + spell.target_type == ST_Ring || + spell.target_type == ST_Beam) && target_id == 0) { - LogSpells("Spell [{}] auto-targeted the caster. Group? [{}], target type [{}]", spell_id, IsGroupSpell(spell_id), spell.targettype); + LogSpells("Spell [{}] auto-targeted the caster. Group? [{}], target type [{}]", spell_id, IsGroupSpell(spell_id), spell.target_type); target_id = GetID(); } @@ -599,7 +599,7 @@ bool Mob::DoCastingChecks() if (RuleB(Spells, BuffLevelRestrictions)) { // casting_spell_targetid is guaranteed to be what we went, check for ST_Self for now should work though - if (spell_target && spells[spell_id].targettype != ST_Self && !spell_target->CheckSpellLevelRestriction(spell_id)) { + if (spell_target && spells[spell_id].target_type != ST_Self && !spell_target->CheckSpellLevelRestriction(spell_id)) { LogSpells("Spell [{}] failed: recipient did not meet the level restrictions", spell_id); if (!IsBardSong(spell_id)) MessageString(Chat::SpellFailure, SPELL_TOO_POWERFUL); @@ -607,7 +607,7 @@ bool Mob::DoCastingChecks() } } - if (spells[spell_id].zonetype == 1 && !zone->CanCastOutdoor()) { + if (spells[spell_id].zone_type == 1 && !zone->CanCastOutdoor()) { MessageString(Chat::Red, CAST_OUTDOORS); return false; } @@ -628,8 +628,8 @@ bool Mob::DoCastingChecks() } } - if (IsClient() && spells[spell_id].EndurTimerIndex > 0 && casting_spell_slot < CastingSlot::MaxGems) - if (!CastToClient()->IsLinkedSpellReuseTimerReady(spells[spell_id].EndurTimerIndex)) + if (IsClient() && spells[spell_id].timer_id > 0 && casting_spell_slot < CastingSlot::MaxGems) + if (!CastToClient()->IsLinkedSpellReuseTimerReady(spells[spell_id].timer_id)) return false; casting_spell_checks = true; @@ -812,7 +812,7 @@ bool Client::CheckFizzle(uint16 spell_id) // > 0 --> skill is lower, higher chance of fizzle // < 0 --> skill is better, lower chance of fizzle // the max that diff can be is +- 235 - float diff = par_skill + static_cast(spells[spell_id].basediff) - act_skill; + float diff = par_skill + static_cast(spells[spell_id].base_difficulty) - act_skill; // if you have high int/wis you fizzle less, you fizzle more if you are stupid if(GetClass() == BARD) @@ -1058,8 +1058,8 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo if(GetClass() == BARD) // bard's can move when casting any spell... { if (IsBardSong(spell_id)) { - if(spells[spell_id].buffduration == 0xFFFF) { - LogSpells("Bard song [{}] not applying bard logic because duration. dur=[{}], recast=[{}]", spells[spell_id].buffduration); + if(spells[spell_id].buff_duration == 0xFFFF) { + LogSpells("Bard song [{}] not applying bard logic because duration. dur=[{}], recast=[{}]", spells[spell_id].buff_duration); } else { // So long recast bard songs need special bard logic, although the effects don't repulse like other songs // This is basically a hack to get that effect @@ -1071,7 +1071,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo bardsong = spell_id; bardsong_slot = slot; //NOTE: theres a lot more target types than this to think about... - if (spell_target == nullptr || (spells[spell_id].targettype != ST_Target && spells[spell_id].targettype != ST_AETarget)) + if (spell_target == nullptr || (spells[spell_id].target_type != ST_Target && spells[spell_id].target_type != ST_AETarget)) bardsong_target_id = GetID(); else bardsong_target_id = spell_target->GetID(); @@ -1195,8 +1195,8 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo int component, component_count, inv_slot_id; bool missingreags = false; for(int t_count = 0; t_count < 4; t_count++) { - component = spells[spell_id].components[t_count]; - component_count = spells[spell_id].component_counts[t_count]; + component = spells[spell_id].component[t_count]; + component_count = spells[spell_id].component_count[t_count]; if (component == -1) continue; @@ -1204,7 +1204,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo // bard components are requirements for a certain instrument type, not a specific item if(bard_song_mode) { bool HasInstrument = true; - int InstComponent = spells[spell_id].NoexpendReagent[0]; + int InstComponent = spells[spell_id].no_expend_reagent[0]; switch (InstComponent) { case -1: @@ -1299,11 +1299,11 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo { int noexpend; for(int t_count = 0; t_count < 4; t_count++) { - component = spells[spell_id].components[t_count]; - noexpend = spells[spell_id].NoexpendReagent[t_count]; + component = spells[spell_id].component[t_count]; + noexpend = spells[spell_id].no_expend_reagent[t_count]; if (component == -1 || noexpend == component) continue; - component_count = spells[spell_id].component_counts[t_count]; + component_count = spells[spell_id].component_count[t_count]; LogSpells("Spell [{}]: Consuming [{}] of spell component item id [{}]", spell_id, component_count, component); // Components found, Deleting // now we go looking for and deleting the items one by one @@ -1452,8 +1452,8 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo if((IsFromItem && RuleB(Character, SkillUpFromItems)) || !IsFromItem) { c->CheckSongSkillIncrease(spell_id); } - if (spells[spell_id].EndurTimerIndex > 0 && slot < CastingSlot::MaxGems) - c->SetLinkedSpellReuseTimer(spells[spell_id].EndurTimerIndex, spells[spell_id].recast_time / 1000); + if (spells[spell_id].timer_id > 0 && slot < CastingSlot::MaxGems) + c->SetLinkedSpellReuseTimer(spells[spell_id].timer_id, spells[spell_id].recast_time / 1000); c->MemorizeSpell(static_cast(slot), spell_id, memSpellSpellbar); } LogSpells("Bard song [{}] should be started", spell_id); @@ -1466,8 +1466,8 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo SendSpellBarEnable(spell_id); // this causes the delayed refresh of the spell bar gems - if (spells[spell_id].EndurTimerIndex > 0 && slot < CastingSlot::MaxGems) - c->SetLinkedSpellReuseTimer(spells[spell_id].EndurTimerIndex, spells[spell_id].recast_time / 1000); + if (spells[spell_id].timer_id > 0 && slot < CastingSlot::MaxGems) + c->SetLinkedSpellReuseTimer(spells[spell_id].timer_id, spells[spell_id].recast_time / 1000); c->MemorizeSpell(static_cast(slot), spell_id, memSpellSpellbar); // this tells the client that casting may happen again @@ -1531,7 +1531,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce // and that causes the spell to be executed differently bodyType target_bt = BT_Humanoid; - SpellTargetType targetType = spells[spell_id].targettype; + SpellTargetType targetType = spells[spell_id].target_type; bodyType mob_body = spell_target ? spell_target->GetBodyType() : BT_Humanoid; if(IsPlayerIllusionSpell(spell_id) @@ -1550,7 +1550,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce if (isproc && IsNPC() && CastToNPC()->GetInnateProcSpellID() == spell_id) targetType = ST_Target; - if (spell_target && spells[spell_id].CastRestriction && !spell_target->PassCastRestriction(spells[spell_id].CastRestriction)){ + if (spell_target && spells[spell_id].cast_restriction && !spell_target->PassCastRestriction(spells[spell_id].cast_restriction)){ Message(Chat::Red, "Your target does not meet the spell requirements."); //Current live also adds description after this from dbstr_us type 39 return false; } @@ -1561,7 +1561,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce } //Must be out of combat. (If Beneficial checks casters combat state, Deterimental checks targets) - if (!spells[spell_id].InCombat && spells[spell_id].OutofCombat) { + if (!spells[spell_id].can_cast_in_combat && spells[spell_id].can_cast_out_of_combat) { if (IsDetrimentalSpell(spell_id)) { if (spell_target && ((spell_target->IsNPC() && spell_target->IsEngaged()) || @@ -1584,7 +1584,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce } // Must be in combat. (If Beneficial checks casters combat state, Deterimental checks targets) - else if (spells[spell_id].InCombat && !spells[spell_id].OutofCombat) { + else if (spells[spell_id].can_cast_in_combat && !spells[spell_id].can_cast_out_of_combat) { if (IsDetrimentalSpell(spell_id)) { if (spell_target && ((spell_target->IsNPC() && !spell_target->IsEngaged()) || @@ -1857,7 +1857,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce spell_target = this; } - if (spell_target && spell_target->IsPet() && spells[spell_id].targettype == ST_GroupNoPets){ + if (spell_target && spell_target->IsPet() && spells[spell_id].target_type == ST_GroupNoPets){ MessageString(Chat::Red,NO_CAST_ON_PET); return false; } @@ -2064,8 +2064,8 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce default: { - LogSpells("I dont know Target Type: [{}] Spell: ([{}]) [{}]", spells[spell_id].targettype, spell_id, spells[spell_id].name); - Message(0, "I dont know Target Type: %d Spell: (%d) %s", spells[spell_id].targettype, spell_id, spells[spell_id].name); + LogSpells("I dont know Target Type: [{}] Spell: ([{}]) [{}]", spells[spell_id].target_type, spell_id, spells[spell_id].name); + Message(0, "I dont know Target Type: %d Spell: (%d) %s", spells[spell_id].target_type, spell_id, spells[spell_id].name); CastAction = CastActUnknown; break; } @@ -2113,7 +2113,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui } } - if( spells[spell_id].zonetype == 1 && !zone->CanCastOutdoor()){ + if( spells[spell_id].zone_type == 1 && !zone->CanCastOutdoor()){ if(IsClient()){ if(!CastToClient()->GetGM()){ MessageString(Chat::Red, CAST_OUTDOORS); @@ -2186,7 +2186,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui if(IsAEDurationSpell(spell_id)) { // the spells are AE target, but we aim them on a beacon Mob *beacon_loc = spell_target ? spell_target : this; - auto beacon = new Beacon(beacon_loc, spells[spell_id].AEDuration); + auto beacon = new Beacon(beacon_loc, spells[spell_id].aoe_duration); entity_list.AddBeacon(beacon); LogSpells("Spell [{}]: AE duration beacon created, entity id [{}]", spell_id, beacon->GetName()); spell_target = nullptr; @@ -2195,7 +2195,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui } // check line of sight to target if it's a detrimental spell - if(!spells[spell_id].npc_no_los && spell_target && IsDetrimentalSpell(spell_id) && !CheckLosFN(spell_target) && !IsHarmonySpell(spell_id) && spells[spell_id].targettype != ST_TargetOptional) + if(!spells[spell_id].npc_no_los && spell_target && IsDetrimentalSpell(spell_id) && !CheckLosFN(spell_target) && !IsHarmonySpell(spell_id) && spells[spell_id].target_type != ST_TargetOptional) { LogSpells("Spell [{}]: cannot see target [{}]", spell_id, spell_target->GetName()); MessageString(Chat::Red,CANT_SEE_TARGET); @@ -2213,7 +2213,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui //range check our target, if we have one and it is not us float range = spells[spell_id].range + GetRangeDistTargetSizeMod(spell_target); if(IsClient() && CastToClient()->TGB() && IsTGBCompatibleSpell(spell_id) && IsGroupSpell(spell_id)) - range = spells[spell_id].aoerange; + range = spells[spell_id].aoe_range; range = GetActSpellRange(spell_id, range); if(IsPlayerIllusionSpell(spell_id) @@ -2291,7 +2291,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui if (isproc) { SpellOnTarget(spell_id, spell_target, 0, true, resist_adjust, true, level_override); } else { - if (spells[spell_id].targettype == ST_TargetOptional){ + if (spells[spell_id].target_type == ST_TargetOptional){ if (!TrySpellProjectile(spell_target, spell_id)) return false; } @@ -2344,10 +2344,10 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui // NPCs should never be affected by an AE they cast. PB AEs shouldn't affect caster either // I don't think any other cases that get here matter - bool affect_caster = !IsNPC() && spells[spell_id].targettype != ST_AECaster; + bool affect_caster = !IsNPC() && spells[spell_id].target_type != ST_AECaster; - if (spells[spell_id].targettype == ST_AETargetHateList) - hate_list.SpellCast(this, spell_id, spells[spell_id].aoerange, ae_center); + if (spells[spell_id].target_type == ST_AETargetHateList) + hate_list.SpellCast(this, spell_id, spells[spell_id].aoe_range, ae_center); else entity_list.AESpell(this, ae_center, spell_id, affect_caster, resist_adjust); } @@ -2416,7 +2416,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui SpellOnTarget(spell_id, this); #ifdef GROUP_BUFF_PETS //pet too - if (spells[spell_id].targettype != ST_GroupNoPets && GetPet() && HasPetAffinity() && !GetPet()->IsCharmed()) + if (spells[spell_id].target_type != ST_GroupNoPets && GetPet() && HasPetAffinity() && !GetPet()->IsCharmed()) SpellOnTarget(spell_id, GetPet()); #endif } @@ -2424,7 +2424,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui SpellOnTarget(spell_id, spell_target); #ifdef GROUP_BUFF_PETS //pet too - if (spells[spell_id].targettype != ST_GroupNoPets && spell_target->GetPet() && spell_target->HasPetAffinity() && !spell_target->GetPet()->IsCharmed()) + if (spells[spell_id].target_type != ST_GroupNoPets && spell_target->GetPet() && spell_target->HasPetAffinity() && !spell_target->GetPet()->IsCharmed()) SpellOnTarget(spell_id, spell_target->GetPet()); #endif } @@ -2436,7 +2436,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui { if(!IsClient()) { - hate_list.SpellCast(this, spell_id, spells[spell_id].range > spells[spell_id].aoerange ? spells[spell_id].range : spells[spell_id].aoerange); + hate_list.SpellCast(this, spell_id, spells[spell_id].range > spells[spell_id].aoe_range ? spells[spell_id].range : spells[spell_id].aoe_range); } break; } @@ -2488,8 +2488,8 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui // one may want to check if this is a disc or not, but we actually don't, there are non disc stuff that have end cost // lets not consume end for custom items that have disc procs. // One might also want to filter out USE_ITEM_SPELL_SLOT, but DISCIPLINE_SPELL_SLOT are both #defined to the same thing ... - if (spells[spell_id].EndurCost && !isproc) { - auto end_cost = spells[spell_id].EndurCost; + if (spells[spell_id].endurance_cost && !isproc) { + auto end_cost = spells[spell_id].endurance_cost; if (mgb) end_cost *= 2; SetEndurance(GetEndurance() - EQ::ClampUpper(end_cost, GetEndurance())); @@ -2514,7 +2514,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui CastToClient()->GetPTimers().Start(casting_spell_timer, casting_spell_timer_duration); LogSpells("Spell [{}]: Setting custom reuse timer [{}] to [{}]", spell_id, casting_spell_timer, casting_spell_timer_duration); } - else if(spells[spell_id].recast_time > 1000 && !spells[spell_id].IsDisciplineBuff) { + else if(spells[spell_id].recast_time > 1000 && !spells[spell_id].is_discipline) { int recast = spells[spell_id].recast_time/1000; if (spell_id == SPELL_LAY_ON_HANDS) //lay on hands { @@ -2751,9 +2751,9 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) { action->source = caster->GetID(); action->target = GetID(); action->spell = spell_id; - action->force = spells[spell_id].pushback; + action->force = spells[spell_id].push_back; action->hit_heading = GetHeading(); - action->hit_pitch = spells[spell_id].pushup; + action->hit_pitch = spells[spell_id].push_up; action->instrument_mod = caster->GetInstrumentMod(spell_id); action->effect_flag = 0; action->spell_level = action->level = buffs[buffs_i].casterlevel; @@ -2762,7 +2762,7 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) { action->effect_flag = 4; - if (spells[spell_id].pushback != 0.0f || spells[spell_id].pushup != 0.0f) + if (spells[spell_id].push_back != 0.0f || spells[spell_id].push_up != 0.0f) { if (IsClient()) { @@ -2840,8 +2840,8 @@ int Mob::CalcBuffDuration(Mob *caster, Mob *target, uint16 spell_id, int32 caste formula = spells[spell_id].pvp_duration; duration = spells[spell_id].pvp_duration_cap; } else { - formula = spells[spell_id].buffdurationformula; - duration = spells[spell_id].buffduration; + formula = spells[spell_id].buff_duration_formula; + duration = spells[spell_id].buff_duration; } int castlevel = caster->GetCasterLevel(spell_id); @@ -2994,7 +2994,7 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, if (spellid1 != spellid2) { for (i = 0; i < EFFECT_COUNT; i++) { // we don't want this optimization for mana burns - if (sp1.effectid[i] != sp2.effectid[i] || sp1.effectid[i] == SE_ManaBurn) { + if (sp1.effect_id[i] != sp2.effect_id[i] || sp1.effect_id[i] == SE_ManaBurn) { effect_match = false; break; } @@ -3009,11 +3009,11 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, if (!effect_match) { for(i = 0; i < EFFECT_COUNT; i++) { - effect1 = sp1.effectid[i]; - effect2 = sp2.effectid[i]; + effect1 = sp1.effect_id[i]; + effect2 = sp2.effect_id[i]; if (spellbonuses.Screech == 1) { - if (effect2 == SE_Screech && sp2.base[i] == -1) { + if (effect2 == SE_Screech && sp2.base_value[i] == -1) { MessageString(Chat::SpellFailure, SCREECH_BUFF_BLOCK, sp2.name); return -1; } @@ -3026,26 +3026,26 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, is not fully removed at the time of the trigger */ if (spellbonuses.AStacker[SBIndex::BUFFSTACKER_EXISTS]) { - if ((effect2 == SE_AStacker) && (sp2.effectid[i] <= spellbonuses.AStacker[SBIndex::BUFFSTACKER_VALUE])) + if ((effect2 == SE_AStacker) && (sp2.effect_id[i] <= spellbonuses.AStacker[SBIndex::BUFFSTACKER_VALUE])) return -1; } if (spellbonuses.BStacker[SBIndex::BUFFSTACKER_EXISTS]) { - if ((effect2 == SE_BStacker) && (sp2.effectid[i] <= spellbonuses.BStacker[SBIndex::BUFFSTACKER_VALUE])) + if ((effect2 == SE_BStacker) && (sp2.effect_id[i] <= spellbonuses.BStacker[SBIndex::BUFFSTACKER_VALUE])) return -1; if ((effect2 == SE_AStacker) && (!IsCastonFadeDurationSpell(spellid1) && buffs[buffslot].ticsremaining != 1 && IsEffectInSpell(spellid1, SE_BStacker))) return -1; } if (spellbonuses.CStacker[SBIndex::BUFFSTACKER_EXISTS]) { - if ((effect2 == SE_CStacker) && (sp2.effectid[i] <= spellbonuses.CStacker[SBIndex::BUFFSTACKER_VALUE])) + if ((effect2 == SE_CStacker) && (sp2.effect_id[i] <= spellbonuses.CStacker[SBIndex::BUFFSTACKER_VALUE])) return -1; if ((effect2 == SE_BStacker) && (!IsCastonFadeDurationSpell(spellid1) && buffs[buffslot].ticsremaining != 1 && IsEffectInSpell(spellid1, SE_CStacker))) return -1; } if (spellbonuses.DStacker[SBIndex::BUFFSTACKER_EXISTS]) { - if ((effect2 == SE_DStacker) && (sp2.effectid[i] <= spellbonuses.DStacker[SBIndex::BUFFSTACKER_VALUE])) + if ((effect2 == SE_DStacker) && (sp2.effect_id[i] <= spellbonuses.DStacker[SBIndex::BUFFSTACKER_VALUE])) return -1; if ((effect2 == SE_CStacker) && (!IsCastonFadeDurationSpell(spellid1) && buffs[buffslot].ticsremaining != 1 && IsEffectInSpell(spellid1, SE_DStacker))) return -1; @@ -3053,10 +3053,10 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, if(effect2 == SE_StackingCommand_Overwrite) { - overwrite_effect = sp2.base[i]; + overwrite_effect = sp2.base_value[i]; overwrite_slot = sp2.formula[i] - 201; //they use base 1 for slots, we use base 0 - overwrite_below_value = sp2.max[i]; - if(sp1.effectid[overwrite_slot] == overwrite_effect) + overwrite_below_value = sp2.max_value[i]; + if(sp1.effect_id[overwrite_slot] == overwrite_effect) { sp1_value = CalcSpellEffectValue(spellid1, overwrite_slot, caster_level1); @@ -3075,11 +3075,11 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, } } else if (effect1 == SE_StackingCommand_Block) { - blocked_effect = sp1.base[i]; + blocked_effect = sp1.base_value[i]; blocked_slot = sp1.formula[i] - 201; - blocked_below_value = sp1.max[i]; + blocked_below_value = sp1.max_value[i]; - if (sp2.effectid[blocked_slot] == blocked_effect) + if (sp2.effect_id[blocked_slot] == blocked_effect) { sp2_value = CalcSpellEffectValue(spellid2, blocked_slot, caster_level2); @@ -3121,8 +3121,8 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, if(IsBlankSpellEffect(spellid1, i) || IsBlankSpellEffect(spellid2, i)) continue; - effect1 = sp1.effectid[i]; - effect2 = sp2.effectid[i]; + effect1 = sp1.effect_id[i]; + effect2 = sp2.effect_id[i]; /* Quick check, are the effects the same, if so then @@ -3142,7 +3142,7 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, // negative AC affects are skipped. Ex. Sun's Corona and Glacier Breath should stack // There may be more SPAs we need to add here .... // The client does just check base rather than calculating the affect change value. - if ((effect1 == SE_ArmorClass || effect1 == SE_ACv2) && sp2.base[i] < 0) + if ((effect1 == SE_ArmorClass || effect1 == SE_ACv2) && sp2.base_value[i] < 0) continue; /* @@ -3412,7 +3412,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid buffs[emptyslot].casterid = caster ? caster->GetID() : 0; buffs[emptyslot].ticsremaining = duration; buffs[emptyslot].counters = CalculateCounters(spell_id); - buffs[emptyslot].numhits = spells[spell_id].numhits; + buffs[emptyslot].hit_number = spells[spell_id].hit_number; buffs[emptyslot].client = caster ? caster->IsClient() : 0; buffs[emptyslot].persistant_buff = 0; buffs[emptyslot].caston_x = 0; @@ -3424,10 +3424,10 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid buffs[emptyslot].virus_spread_time = 0; buffs[emptyslot].instrument_mod = caster ? caster->GetInstrumentMod(spell_id) : 10; - if (level_override > 0 || buffs[emptyslot].numhits > 0) { + if (level_override > 0 || buffs[emptyslot].hit_number > 0) { buffs[emptyslot].UpdateClient = true; } else { - if (buffs[emptyslot].ticsremaining > (1 + CalcBuffDuration_formula(caster_level, spells[spell_id].buffdurationformula, spells[spell_id].buffduration))) + if (buffs[emptyslot].ticsremaining > (1 + CalcBuffDuration_formula(caster_level, spells[spell_id].buff_duration_formula, spells[spell_id].buff_duration))) buffs[emptyslot].UpdateClient = true; } @@ -3563,8 +3563,8 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes // these target types skip pcnpc only check (according to dev quotes) // other AE spells this is redundant, oh well // 1 = PCs, 2 = NPCs - if (spells[spell_id].pcnpc_only_flag && spells[spell_id].targettype != ST_AETargetHateList && - spells[spell_id].targettype != ST_HateList) { + if (spells[spell_id].pcnpc_only_flag && spells[spell_id].target_type != ST_AETargetHateList && + spells[spell_id].target_type != ST_HateList) { if (spells[spell_id].pcnpc_only_flag == 1 && !spelltar->IsClient() && !spelltar->IsMerc() && !spelltar->IsBot()) return false; else if (spells[spell_id].pcnpc_only_flag == 2 && (spelltar->IsClient() || spelltar->IsMerc() || spelltar->IsBot())) @@ -3622,9 +3622,9 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes action->spell_level = action->level = caster_level; // caster level, for animation only action->type = 231; // 231 means a spell action->spell = spell_id; - action->force = spells[spell_id].pushback; + action->force = spells[spell_id].push_back; action->hit_heading = GetHeading(); - action->hit_pitch = spells[spell_id].pushup; + action->hit_pitch = spells[spell_id].push_up; action->instrument_mod = GetInstrumentMod(spell_id); action->effect_flag = 0; @@ -3806,7 +3806,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes ) ) { - if(spells[spell_id].targettype == ST_AEBard) { + if(spells[spell_id].target_type == ST_AEBard) { //if it was a beneficial AE bard song don't spam the window that it would not hold LogSpells("Beneficial ae bard song [{}] can't take hold [{}] -> [{}], IBA? [{}]", spell_id, GetName(), spelltar->GetName(), IsBeneficialAllowed(spelltar)); } else { @@ -3840,7 +3840,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes } //check for AE_Undead - if(spells[spell_id].targettype == ST_UndeadAE){ + if(spells[spell_id].target_type == ST_UndeadAE){ if(spelltar->GetBodyType() != BT_SummonedUndead && spelltar->GetBodyType() != BT_Undead && spelltar->GetBodyType() != BT_Vampire) @@ -3895,7 +3895,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes case REFLECT_SINGLE_TARGET_SPELLS_ONLY: { - if(spells[spell_id].targettype == ST_Target) { + if(spells[spell_id].target_type == ST_Target) { for(int y = 0; y < 16; y++) { if (spells[spell_id].classes[y] < 255) { can_spell_reflect = true; @@ -3915,7 +3915,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes } case RELFECT_ALL_SINGLE_TARGET_SPELLS: { - if (spells[spell_id].targettype == ST_Target) { + if (spells[spell_id].target_type == ST_Target) { can_spell_reflect = true; } break; @@ -3978,9 +3978,9 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes spelltar->BreakInvisibleSpells(); //Any detrimental spell cast on you will drop invisible (can be AOE, non damage ect). if (IsCharmSpell(spell_id) || IsMezSpell(spell_id) || IsFearSpell(spell_id)) - spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust, true, false, false, level_override); + spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resist_type, spell_id, this, use_resist_adjust, resist_adjust, true, false, false, level_override); else - spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust, false, false, false, level_override); + spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resist_type, spell_id, this, use_resist_adjust, resist_adjust, false, false, false, level_override); if(spell_effectiveness < 100) { @@ -3988,7 +3988,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes { LogSpells("Spell [{}] was completely resisted by [{}]", spell_id, spelltar->GetName()); - if (spells[spell_id].resisttype == RESIST_PHYSICAL){ + if (spells[spell_id].resist_type == RESIST_PHYSICAL){ MessageString(Chat::SpellFailure, PHYSICAL_RESIST_FAIL,spells[spell_id].name); spelltar->MessageString(Chat::SpellFailure, YOU_RESIST, spells[spell_id].name); } @@ -4091,8 +4091,8 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes if (spelltar) spelltar->CastSpellOnLand(this, spell_id); - if (IsValidSpell(spells[spell_id].RecourseLink) && spells[spell_id].RecourseLink != spell_id) - SpellFinished(spells[spell_id].RecourseLink, this, CastingSlot::Item, 0, -1, spells[spells[spell_id].RecourseLink].ResistDiff); + if (IsValidSpell(spells[spell_id].recourse_link) && spells[spell_id].recourse_link != spell_id) + SpellFinished(spells[spell_id].recourse_link, this, CastingSlot::Item, 0, -1, spells[spells[spell_id].recourse_link].resist_difficulty); if (IsDetrimentalSpell(spell_id)) { @@ -4108,7 +4108,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes // the complete sequence is 2 actions and 1 damage message action->effect_flag = 0x04; // this is a success flag - if(spells[spell_id].pushback != 0.0f || spells[spell_id].pushup != 0.0f) + if(spells[spell_id].push_back != 0.0f || spells[spell_id].push_up != 0.0f) { if (spelltar->IsClient()) { @@ -4248,7 +4248,7 @@ uint32 Mob::BuffCount() { bool Mob::HasBuffWithSpellGroup(int spellgroup) { for (int i = 0; i < GetMaxTotalSlots(); i++) { - if (IsValidSpell(buffs[i].spellid) && spells[buffs[i].spellid].spellgroup == spellgroup) { + if (IsValidSpell(buffs[i].spellid) && spells[buffs[i].spellid].spell_group == spellgroup) { return true; } } @@ -4364,7 +4364,7 @@ void Mob::BuffFadeBySpellIDAndCaster(uint16 spell_id, uint16 caster_id) } // removes buffs containing effectid, skipping skipslot -void Mob::BuffFadeByEffect(int effectid, int skipslot) +void Mob::BuffFadeByEffect(int effect_id, int skipslot) { int i; @@ -4373,7 +4373,7 @@ void Mob::BuffFadeByEffect(int effectid, int skipslot) { if(buffs[i].spellid == SPELL_UNKNOWN) continue; - if(IsEffectInSpell(buffs[i].spellid, effectid) && i != skipslot) + if(IsEffectInSpell(buffs[i].spellid, effect_id) && i != skipslot) BuffFadeBySlot(i, false); } @@ -4443,10 +4443,10 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) effect_index = GetSpellEffectIndex(spell_id, SE_Mez); assert(effect_index >= 0); // NPCs get to ignore the max level - if((GetLevel() > spells[spell_id].max[effect_index]) && + if((GetLevel() > spells[spell_id].max_value[effect_index]) && (!caster->IsNPC() || (caster->IsNPC() && !RuleB(Spells, NPCIgnoreBaseImmunity)))) { - LogSpells("Our level ([{}]) is higher than the limit of this Mez spell ([{}])", GetLevel(), spells[spell_id].max[effect_index]); + LogSpells("Our level ([{}]) is higher than the limit of this Mez spell ([{}])", GetLevel(), spells[spell_id].max_value[effect_index]); caster->MessageString(Chat::SpellFailure, CANNOT_MEZ_WITH_SPELL); AddToHateList(caster, 1,0,true,false,false,spell_id); return true; @@ -4487,7 +4487,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) caster->MessageString(Chat::Red, IMMUNE_FEAR); // need to verify message type, not in MQ2Cast for easy look up return true; } - else if(GetLevel() > spells[spell_id].max[effect_index] && spells[spell_id].max[effect_index] != 0) + else if(GetLevel() > spells[spell_id].max_value[effect_index] && spells[spell_id].max_value[effect_index] != 0) { LogSpells("Level is [{}], cannot be feared by this spell", GetLevel()); caster->MessageString(Chat::Shout, FEAR_TOO_HIGH); @@ -4536,9 +4536,9 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) // check level limit of charm spell effect_index = GetSpellEffectIndex(spell_id, SE_Charm); assert(effect_index >= 0); - if(GetLevel() > spells[spell_id].max[effect_index] && spells[spell_id].max[effect_index] != 0) + if(GetLevel() > spells[spell_id].max_value[effect_index] && spells[spell_id].max_value[effect_index] != 0) { - LogSpells("Our level ([{}]) is higher than the limit of this Charm spell ([{}])", GetLevel(), spells[spell_id].max[effect_index]); + LogSpells("Our level ([{}]) is higher than the limit of this Charm spell ([{}])", GetLevel(), spells[spell_id].max_value[effect_index]); caster->MessageString(Chat::Red, CANNOT_CHARM_YET); // need to verify message type, not in MQ2Cast for easy look up AddToHateList(caster, 1,0,true,false,false,spell_id); return true; @@ -4664,9 +4664,9 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use // PVP, we don't have the normal per_level or cap stuff implemented ... so ahh do that // and make sure the PVP versions are also handled. if (IsClient() && caster->IsClient()) { - resist_modifier = spells[spell_id].pvpresistbase; + resist_modifier = spells[spell_id].pvp_resist_base; } else { - resist_modifier = spells[spell_id].ResistDiff; + resist_modifier = spells[spell_id].resist_difficulty; } } @@ -4813,7 +4813,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use */ int16 charisma = caster->GetCHA(); - if (IsFear && (spells[spell_id].targettype != ST_Undead)){ + if (IsFear && (spells[spell_id].target_type != ST_Undead)){ if (charisma < 100) resist_modifier -= 20; @@ -4851,14 +4851,14 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use resist_chance = mod_spell_resist(resist_chance, level_mod, resist_modifier, target_resist, resist_type, spell_id, caster); //Do our min and max resist checks. - if(resist_chance > spells[spell_id].MaxResist && spells[spell_id].MaxResist != 0) + if(resist_chance > spells[spell_id].max_resist && spells[spell_id].max_resist != 0) { - resist_chance = spells[spell_id].MaxResist; + resist_chance = spells[spell_id].max_resist; } - if(resist_chance < spells[spell_id].MinResist && spells[spell_id].MinResist != 0) + if(resist_chance < spells[spell_id].min_resist && spells[spell_id].min_resist != 0) { - resist_chance = spells[spell_id].MinResist; + resist_chance = spells[spell_id].min_resist; } //Average charm duration agianst mobs with 0% chance to resist on LIVE is ~ 68 ticks. @@ -5010,7 +5010,7 @@ int16 Mob::CalcFearResistChance() */ float Mob::GetAOERange(uint16 spell_id) { - float range = spells[spell_id].aoerange; + float range = spells[spell_id].aoe_range; /** * For TGB @@ -5442,7 +5442,7 @@ uint32 Client::GetHighestScribedSpellinSpellGroup(uint32 spell_group) for (int i = 0; i < EQ::spells::SPELLBOOK_SIZE; i++) { if (IsValidSpell(m_pp.spell_book[i])) { - if (spells[m_pp.spell_book[i]].spellgroup == spell_group) { + if (spells[m_pp.spell_book[i]].spell_group == spell_group) { if (highest_rank < spells[m_pp.spell_book[i]].rank) { highest_rank = spells[m_pp.spell_book[i]].rank; highest_spell_id = m_pp.spell_book[i]; @@ -5583,7 +5583,7 @@ int16 Mob::GetBuffSlotFromType(uint16 type) { for (int i = 0; i < buff_count; i++) { if (buffs[i].spellid != SPELL_UNKNOWN) { for (int j = 0; j < EFFECT_COUNT; j++) { - if (spells[buffs[i].spellid].effectid[j] == type ) + if (spells[buffs[i].spellid].effect_id[j] == type ) return i; } } @@ -5606,11 +5606,11 @@ bool Mob::FindType(uint16 type, bool bOffensive, uint16 threshold) { for (int j = 0; j < EFFECT_COUNT; j++) { // adjustments necessary for offensive npc casting behavior if (bOffensive) { - if (spells[buffs[i].spellid].effectid[j] == type) { + if (spells[buffs[i].spellid].effect_id[j] == type) { int16 value = - CalcSpellEffectValue_formula(spells[buffs[i].spellid].buffdurationformula, - spells[buffs[i].spellid].base[j], - spells[buffs[i].spellid].max[j], + CalcSpellEffectValue_formula(spells[buffs[i].spellid].buff_duration_formula, + spells[buffs[i].spellid].base_value[j], + spells[buffs[i].spellid].max_value[j], buffs[i].casterlevel, buffs[i].spellid); Log(Logs::General, Logs::Normal, "FindType: type = %d; value = %d; threshold = %d", @@ -5619,7 +5619,7 @@ bool Mob::FindType(uint16 type, bool bOffensive, uint16 threshold) { return true; } } else { - if (spells[buffs[i].spellid].effectid[j] == type ) + if (spells[buffs[i].spellid].effect_id[j] == type ) return true; } } @@ -5847,7 +5847,7 @@ void Client::SendBuffDurationPacket(Buffs_Struct &buff, int slot) else if (buff.counters) sbf->buff.counters = buff.counters; sbf->buff.player_id = buff.casterid; - sbf->buff.num_hits = buff.numhits; + sbf->buff.num_hits = buff.hit_number; sbf->buff.y = buff.caston_y; sbf->buff.x = buff.caston_x; sbf->buff.z = buff.caston_z; @@ -5873,7 +5873,7 @@ void Client::SendBuffNumHitPacket(Buffs_Struct &buff, int slot) bi->entries[0].buff_slot = slot; bi->entries[0].spell_id = buff.spellid; bi->entries[0].tics_remaining = buff.ticsremaining; - bi->entries[0].num_hits = buff.numhits; + bi->entries[0].num_hits = buff.hit_number; strn0cpy(bi->entries[0].caster, buff.caster_name, 64); bi->name_lengths = strlen(bi->entries[0].caster); FastQueuePacket(&outapp); @@ -5970,7 +5970,7 @@ EQApplicationPacket *Mob::MakeBuffsPacket(bool for_target) buff->entries[index].buff_slot = i; buff->entries[index].spell_id = buffs[i].spellid; buff->entries[index].tics_remaining = buffs[i].ticsremaining; - buff->entries[index].num_hits = buffs[i].numhits; + buff->entries[index].num_hits = buffs[i].hit_number; strn0cpy(buff->entries[index].caster, buffs[i].caster_name, 64); buff->name_lengths += strlen(buff->entries[index].caster); ++index; @@ -6136,7 +6136,7 @@ void Mob::BeamDirectional(uint16 spell_id, int16 resist_adjust) //# shortest distance from line to target point float d = std::abs((*iter)->GetY() - m * (*iter)->GetX() - b) / sqrt(m * m + 1); - if (d <= spells[spell_id].aoerange) { + if (d <= spells[spell_id].aoe_range) { if (CheckLosFN((*iter)) || spells[spell_id].npc_no_los) { (*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this); SpellOnTarget(spell_id, (*iter), 0, true, resist_adjust); @@ -6144,7 +6144,7 @@ void Mob::BeamDirectional(uint16 spell_id, int16 resist_adjust) } // not sure if we need this check, but probably do, need to check if it should be default limited or not - if (spells[spell_id].aemaxtargets && maxtarget_count >= spells[spell_id].aemaxtargets) + if (spells[spell_id].aoe_max_targets && maxtarget_count >= spells[spell_id].aoe_max_targets) return; } ++iter; @@ -6172,8 +6172,8 @@ void Mob::ConeDirectional(uint16 spell_id, int16 resist_adjust) std::list targets_in_range; - entity_list.GetTargetsForConeArea(this, spells[spell_id].min_range, spells[spell_id].aoerange, - spells[spell_id].aoerange / 2, spells[spell_id].pcnpc_only_flag, targets_in_range); + entity_list.GetTargetsForConeArea(this, spells[spell_id].min_range, spells[spell_id].aoe_range, + spells[spell_id].aoe_range / 2, spells[spell_id].pcnpc_only_flag, targets_in_range); auto iter = targets_in_range.begin(); while (iter != targets_in_range.end()) { @@ -6228,7 +6228,7 @@ void Mob::ConeDirectional(uint16 spell_id, int16 resist_adjust) } // my SHM breath could hit all 5 dummies I could summon in arena - if (spells[spell_id].aemaxtargets && maxtarget_count >= spells[spell_id].aemaxtargets) + if (spells[spell_id].aoe_max_targets && maxtarget_count >= spells[spell_id].aoe_max_targets) return; ++iter; diff --git a/zone/trap.cpp b/zone/trap.cpp index 327f0ef80..f3fc3d22e 100644 --- a/zone/trap.cpp +++ b/zone/trap.cpp @@ -136,7 +136,7 @@ void Trap::Trigger(Mob* trigger) entity_list.MessageClose(trigger,false,100,13,"%s",message.c_str()); } if(hiddenTrigger){ - hiddenTrigger->SpellFinished(effectvalue, trigger, EQ::spells::CastingSlot::Item, 0, -1, spells[effectvalue].ResistDiff); + hiddenTrigger->SpellFinished(effectvalue, trigger, EQ::spells::CastingSlot::Item, 0, -1, spells[effectvalue].resist_difficulty); } break; case trapTypeAlarm: diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index bc463b99b..04b4a9326 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -3113,12 +3113,12 @@ void ZoneDatabase::SaveMercBuffs(Merc *merc) { "caston_x, Persistent, caston_y, caston_z, ExtraDIChance) " "VALUES (%u, %u, %u, %u, %u, %d, %u, %u, %u, %u, %u, %u, %u, %i, %u, %i, %i, %i);", merc->GetMercID(), buffs[buffCount].spellid, buffs[buffCount].casterlevel, - spells[buffs[buffCount].spellid].buffdurationformula, buffs[buffCount].ticsremaining, + spells[buffs[buffCount].spellid].buff_duration_formula, buffs[buffCount].ticsremaining, CalculatePoisonCounters(buffs[buffCount].spellid) > 0 ? buffs[buffCount].counters : 0, CalculateDiseaseCounters(buffs[buffCount].spellid) > 0 ? buffs[buffCount].counters : 0, CalculateCurseCounters(buffs[buffCount].spellid) > 0 ? buffs[buffCount].counters : 0, CalculateCorruptionCounters(buffs[buffCount].spellid) > 0 ? buffs[buffCount].counters : 0, - buffs[buffCount].numhits, buffs[buffCount].melee_rune, buffs[buffCount].magic_rune, + buffs[buffCount].hit_number, buffs[buffCount].melee_rune, buffs[buffCount].magic_rune, buffs[buffCount].dot_rune, buffs[buffCount].caston_x, IsPersistent, buffs[buffCount].caston_y, buffs[buffCount].caston_z, buffs[buffCount].ExtraDIChance); results = database.QueryDatabase(query); @@ -3167,7 +3167,7 @@ void ZoneDatabase::LoadMercBuffs(Merc *merc) { if(CalculateCorruptionCounters(buffs[buffCount].spellid) > 0) buffs[buffCount].counters = atoi(row[7]); - buffs[buffCount].numhits = atoi(row[8]); + buffs[buffCount].hit_number = atoi(row[8]); buffs[buffCount].melee_rune = atoi(row[9]); buffs[buffCount].magic_rune = atoi(row[10]); buffs[buffCount].dot_rune = atoi(row[11]); @@ -3594,7 +3594,7 @@ void ZoneDatabase::SaveBuffs(Client *client) { "VALUES('%u', '%u', '%u', '%u', '%s', '%d', '%u', '%u', '%u', '%u', '%u', '%u', " "'%i', '%i', '%i', '%i', '%i')", client->CharacterID(), index, buffs[index].spellid, buffs[index].casterlevel, buffs[index].caster_name, buffs[index].ticsremaining, - buffs[index].counters, buffs[index].numhits, buffs[index].melee_rune, + buffs[index].counters, buffs[index].hit_number, buffs[index].melee_rune, buffs[index].magic_rune, buffs[index].persistant_buff, buffs[index].dot_rune, buffs[index].caston_x, buffs[index].caston_y, buffs[index].caston_z, buffs[index].ExtraDIChance, buffs[index].instrument_mod); @@ -3634,7 +3634,7 @@ void ZoneDatabase::LoadBuffs(Client *client) uint32 caster_level = atoi(row[2]); int32 ticsremaining = atoi(row[4]); uint32 counters = atoul(row[5]); - uint32 numhits = atoul(row[6]); + uint32 hit_number = atoul(row[6]); uint32 melee_rune = atoul(row[7]); uint32 magic_rune = atoul(row[8]); uint8 persistent = atoul(row[9]); @@ -3660,7 +3660,7 @@ void ZoneDatabase::LoadBuffs(Client *client) buffs[slot_id].ticsremaining = ticsremaining; buffs[slot_id].counters = counters; - buffs[slot_id].numhits = numhits; + buffs[slot_id].hit_number = hit_number; buffs[slot_id].melee_rune = melee_rune; buffs[slot_id].magic_rune = magic_rune; buffs[slot_id].persistant_buff = persistent ? true : false; @@ -3683,12 +3683,12 @@ void ZoneDatabase::LoadBuffs(Client *client) for (int effectIndex = 0; effectIndex < EFFECT_COUNT; ++effectIndex) { - if (spells[buffs[index].spellid].effectid[effectIndex] == SE_Charm) { + if (spells[buffs[index].spellid].effect_id[effectIndex] == SE_Charm) { buffs[index].spellid = SPELL_UNKNOWN; break; } - if (spells[buffs[index].spellid].effectid[effectIndex] == SE_Illusion) { + if (spells[buffs[index].spellid].effect_id[effectIndex] == SE_Illusion) { if (buffs[index].persistant_buff) break; diff --git a/zone/zonedb.h b/zone/zonedb.h index 03e69c242..83148ba7b 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -72,9 +72,9 @@ struct DBnpcspellseffects_entries_Struct { int16 spelleffectid; uint8 minlevel; uint8 maxlevel; - int32 base; + int32 base_value; int32 limit; - int32 max; + int32 max_value; }; #pragma pack() From 9d515b20f29168520294a72d0402b45fc8e25171 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Wed, 3 Nov 2021 18:31:13 -0400 Subject: [PATCH 317/624] [Quest API] Simplify bulk Scribe and Train logic. (#1660) * [Quest API] Simplify bulk Scribe and Train logic. - Add $client->GetFreeDisciplineSlot(starting_slot) to Perl. - Add $client->ScribeSpells(min_level, max_level) to Perl. - Add $client->LearnDisciplines(min_level, max_level) to Perl. - Add client:GetNextAvailableDisciplineSlot(starting_slot) to Lua. - Add client:ScribeSpells(min_level, max_level) to Lua. - Add client:LearnDisciplines(min_level, max_level) to Lua. Convert quest::scribespells() and quest::traindisc() to use new ScribeSpells and LearnDisciplines methods for consistency. * Update command.cpp --- zone/client.cpp | 90 +++++++++++++++ zone/client.h | 5 + zone/command.cpp | 256 +++++++++++++++---------------------------- zone/lua_client.cpp | 26 ++++- zone/lua_client.h | 4 + zone/perl_client.cpp | 65 +++++++++++ zone/questmgr.cpp | 61 +---------- zone/spells.cpp | 9 +- 8 files changed, 286 insertions(+), 230 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index a1afb485f..dc6b04307 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -10719,3 +10719,93 @@ void Client::SaveDisciplines() CharacterDisciplinesRepository::InsertMany(database, character_discs); } } + +uint16 Client::ScribeSpells(uint8 min_level, uint8 max_level) +{ + int available_book_slot = GetNextAvailableSpellBookSlot(); + std::vector spell_ids = GetScribeableSpells(min_level, max_level); + uint16 spell_count = spell_ids.size(); + uint16 scribed_spells = 0; + if (spell_count > 0) { + for (auto spell_id : spell_ids) { + if (available_book_slot == -1) { + Message( + Chat::Red, + fmt::format( + "Unable to scribe {} ({}) to Spell Book because your Spell Book is full.", + spells[spell_id].name, + spell_id + ).c_str() + ); + break; + } + + if (HasSpellScribed(spell_id)) { + continue; + } + + // defer saving per spell and bulk save at the end + ScribeSpell(spell_id, available_book_slot, true, true); + available_book_slot = GetNextAvailableSpellBookSlot(available_book_slot); + scribed_spells++; + } + } + + if (scribed_spells > 0) { + std::string spell_message = ( + scribed_spells == 1 ? + "a new spell" : + fmt::format("{} new spells", scribed_spells) + ); + Message(Chat::White, fmt::format("You have learned {}!", spell_message).c_str()); + + // bulk insert spells + SaveSpells(); + } + return scribed_spells; +} + +uint16 Client::LearnDisciplines(uint8 min_level, uint8 max_level) +{ + int available_discipline_slot = GetNextAvailableDisciplineSlot(); + int character_id = CharacterID(); + std::vector spell_ids = GetLearnableDisciplines(min_level, max_level); + uint16 discipline_count = spell_ids.size(); + uint16 learned_disciplines = 0; + if (discipline_count > 0) { + for (auto spell_id : spell_ids) { + if (available_discipline_slot == -1) { + Message( + Chat::Red, + fmt::format( + "Unable to learn {} ({}) because your Discipline slots are full.", + spells[spell_id].name, + spell_id + ).c_str() + ); + break; + } + + if (HasDisciplineLearned(spell_id)) { + continue; + } + + GetPP().disciplines.values[available_discipline_slot] = spell_id; + available_discipline_slot = GetNextAvailableDisciplineSlot(available_discipline_slot); + learned_disciplines++; + } + } + + if (learned_disciplines > 0) { + std::string discipline_message = ( + learned_disciplines == 1 ? + "a new discipline" : + fmt::format("{} new disciplines", learned_disciplines) + ); + Message(Chat::White, fmt::format("You have learned {}!", discipline_message).c_str()); + SendDisciplineUpdate(); + SaveDisciplines(); + } + + return learned_disciplines; +} \ No newline at end of file diff --git a/zone/client.h b/zone/client.h index 41fc994ee..7df7bb143 100644 --- a/zone/client.h +++ b/zone/client.h @@ -804,6 +804,10 @@ public: void SaveSpells(); void SaveDisciplines(); + // Bulk Scribe/Learn + uint16 ScribeSpells(uint8 min_level, uint8 max_level); + uint16 LearnDisciplines(uint8 min_level, uint8 max_level); + // defer save used when bulk saving void UnscribeSpell(int slot, bool update_client = true, bool defer_save = false); void UnscribeSpellAll(bool update_client = true); @@ -1026,6 +1030,7 @@ public: int FindSpellBookSlotBySpellID(uint16 spellid); uint32 GetSpellIDByBookSlot(int book_slot); int GetNextAvailableSpellBookSlot(int starting_slot = 0); + int GetNextAvailableDisciplineSlot(int starting_slot = 0); inline uint32 GetSpellByBookSlot(int book_slot) { return m_pp.spell_book[book_slot]; } inline bool HasSpellScribed(int spellid) { return FindSpellBookSlotBySpellID(spellid) != -1; } uint32 GetHighestScribedSpellinSpellGroup(uint32 spell_group); diff --git a/zone/command.cpp b/zone/command.cpp index 45024464b..aea1e96fc 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -7854,109 +7854,64 @@ void command_beardcolor(Client *c, const Seperator *sep) void command_scribespells(Client *c, const Seperator *sep) { - Client *t = c; - if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) - t = c->GetTarget()->CastToClient(); + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + target = c->GetTarget()->CastToClient(); + } if(sep->argnum < 1 || !sep->IsNumber(1)) { c->Message(Chat::White, "FORMAT: #scribespells "); return; } - uint8 max_level = (uint8)atol(sep->arg[1]); - if (!c->GetGM() && max_level > (uint8)RuleI(Character, MaxLevel)) - max_level = (uint8)RuleI(Character, MaxLevel); // default to Character:MaxLevel if we're not a GM & it's higher than the max level + uint8 rule_max_level = (uint8) RuleI(Character, MaxLevel); + uint8 max_level = (uint8) std::stoi(sep->arg[1]); + uint8 min_level = ( + sep->IsNumber(2) ? + (uint8) + std::stoi(sep->arg[2]) : + 1 + ); // Default to Level 1 if there isn't a 2nd argument - uint8 min_level = (sep->IsNumber(2) ? (uint8)atol(sep->arg[2]) : 1); // default to 1 if there isn't a 2nd argument - if (!c->GetGM() && min_level > (uint8)RuleI(Character, MaxLevel)) - min_level = (uint8)RuleI(Character, MaxLevel); // default to Character:MaxLevel if we're not a GM & it's higher than the max level + if (!c->GetGM()) { // Default to Character:MaxLevel if we're not a GM and Level is higher than the max level + if (max_level > rule_max_level) { + max_level = rule_max_level; + } + + if (min_level > rule_max_level) { + min_level = rule_max_level; + } + } if(max_level < 1 || min_level < 1) { - c->Message(Chat::White, "ERROR: Level must be greater than 1."); + c->Message(Chat::White, "ERROR: Level must be greater than or equal to 1."); return; } + if (min_level > max_level) { - c->Message(Chat::White, "ERROR: Min Level must be less than or equal to Max Level."); + c->Message(Chat::White, "ERROR: Minimum Level must be less than or equal to Maximum Level."); return; } - t->Message(Chat::White, "Scribing spells to spellbook."); - if(t != c) - c->Message(Chat::White, "Scribing spells for %s.", t->GetName()); - LogInfo("Scribe spells request for [{}] from [{}], levels: [{}] -> [{}]", t->GetName(), c->GetName(), min_level, max_level); - - int book_slot = t->GetNextAvailableSpellBookSlot(); - int spell_id = 0; - int count = 0; - - for ( ; spell_id < SPDAT_RECORDS && book_slot < EQ::spells::SPELLBOOK_SIZE; ++spell_id) { - if (book_slot == -1) { - t->Message( - 13, - "Unable to scribe spell %s (%i) to spellbook: no more spell book slots available.", - ((spell_id >= 0 && spell_id < SPDAT_RECORDS) ? spells[spell_id].name : "Out-of-range"), - spell_id - ); - if (t != c) - c->Message( - 13, - "Error scribing spells: %s ran out of spell book slots on spell %s (%i)", - t->GetName(), - ((spell_id >= 0 && spell_id < SPDAT_RECORDS) ? spells[spell_id].name : "Out-of-range"), - spell_id - ); - - break; - } - if (spell_id < 0 || spell_id >= SPDAT_RECORDS) { - c->Message(Chat::Red, "FATAL ERROR: Spell id out-of-range (id: %i, min: 0, max: %i)", spell_id, SPDAT_RECORDS); - return; - } - if (book_slot < 0 || book_slot >= EQ::spells::SPELLBOOK_SIZE) { - c->Message(Chat::Red, "FATAL ERROR: Book slot out-of-range (slot: %i, min: 0, max: %i)", book_slot, EQ::spells::SPELLBOOK_SIZE); - return; - } - - while (true) { - if (spells[spell_id].classes[WARRIOR] == 0) // check if spell exists - break; - if (spells[spell_id].classes[t->GetPP().class_ - 1] > max_level) // maximum level - break; - if (spells[spell_id].classes[t->GetPP().class_ - 1] < min_level) // minimum level - break; - if (spells[spell_id].skill == 52) - break; - - uint16 spell_id_ = (uint16)spell_id; - if ((spell_id_ != spell_id) || (spell_id != spell_id_)) { - c->Message(Chat::Red, "FATAL ERROR: Type conversion data loss with spell_id (%i != %u)", spell_id, spell_id_); - return; - } - - if (!IsDiscipline(spell_id_) && !t->HasSpellScribed(spell_id)) { // isn't a discipline & we don't already have it scribed - t->ScribeSpell(spell_id_, book_slot, true, true); - ++count; - } - - break; - } - - book_slot = t->GetNextAvailableSpellBookSlot(book_slot); - } - - if (count > 0) { - t->Message(Chat::White, "Successfully scribed %i spells.", count); - if (t != c) { - c->Message(Chat::White, "Successfully scribed %i spells for %s.", count, t->GetName()); - } - - t->SaveSpells(); - } - else { - t->Message(Chat::White, "No spells scribed."); - if (t != c) { - c->Message(Chat::White, "No spells scribed for %s.", t->GetName()); - } + uint16 scribed_spells = target->ScribeSpells(min_level, max_level); + if (target != c) { + std::string spell_message = ( + scribed_spells > 0 ? + ( + scribed_spells == 1 ? + "A new spell" : + fmt::format("{} New spells", scribed_spells) + ) : + "No new spells" + ); + c->Message( + Chat::White, + fmt::format( + "{} scribed for {}.", + spell_message, + target->GetCleanName() + ).c_str() + ); } } @@ -10942,101 +10897,64 @@ void command_reloadtitles(Client *c, const Seperator *sep) void command_traindisc(Client *c, const Seperator *sep) { - Client *t = c; - if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) - t = c->GetTarget()->CastToClient(); + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + target = c->GetTarget()->CastToClient(); + } - if (sep->argnum < 1 || !sep->IsNumber(1)) { + if(sep->argnum < 1 || !sep->IsNumber(1)) { c->Message(Chat::White, "FORMAT: #traindisc "); return; } - uint8 max_level = (uint8)atol(sep->arg[1]); - if (!c->GetGM() && max_level >(uint8)RuleI(Character, MaxLevel)) - max_level = (uint8)RuleI(Character, MaxLevel); // default to Character:MaxLevel if we're not a GM & it's higher than the max level + uint8 rule_max_level = (uint8) RuleI(Character, MaxLevel); + uint8 max_level = (uint8) std::stoi(sep->arg[1]); + uint8 min_level = ( + sep->IsNumber(2) ? + (uint8) + std::stoi(sep->arg[2]) : + 1 + ); // Default to Level 1 if there isn't a 2nd argument - uint8 min_level = (sep->IsNumber(2) ? (uint8)atol(sep->arg[2]) : 1); // default to 1 if there isn't a 2nd argument - if (!c->GetGM() && min_level > (uint8)RuleI(Character, MaxLevel)) - min_level = (uint8)RuleI(Character, MaxLevel); // default to Character:MaxLevel if we're not a GM & it's higher than the max level + if (!c->GetGM()) { // Default to Character:MaxLevel if we're not a GM and Level is higher than the max level + if (max_level > rule_max_level) { + max_level = rule_max_level; + } + + if (min_level > rule_max_level) { + min_level = rule_max_level; + } + } if(max_level < 1 || min_level < 1) { - c->Message(Chat::White, "ERROR: Level must be greater than 1."); + c->Message(Chat::White, "ERROR: Level must be greater than or equal to 1."); return; } + if (min_level > max_level) { - c->Message(Chat::White, "Error: Min Level must be less than or equal to Max Level."); + c->Message(Chat::White, "ERROR: Minimum Level must be less than or equal to Maximum Level."); return; } - t->Message(Chat::White, "Training disciplines"); - if(t != c) - c->Message(Chat::White, "Training disciplines for %s.", t->GetName()); - LogInfo("Train disciplines request for [{}] from [{}], levels: [{}] -> [{}]", t->GetName(), c->GetName(), min_level, max_level); - - int spell_id = 0; - int count = 0; - - bool change = false; - - for( ; spell_id < SPDAT_RECORDS; ++spell_id) { - if (spell_id < 0 || spell_id >= SPDAT_RECORDS) { - c->Message(Chat::Red, "FATAL ERROR: Spell id out-of-range (id: %i, min: 0, max: %i)", spell_id, SPDAT_RECORDS); - return; - } - - while (true) { - if (spells[spell_id].classes[WARRIOR] == 0) // check if spell exists - break; - if (spells[spell_id].classes[t->GetPP().class_ - 1] > max_level) // maximum level - break; - if (spells[spell_id].classes[t->GetPP().class_ - 1] < min_level) // minimum level - break; - if (spells[spell_id].skill == 52) - break; - - uint16 spell_id_ = (uint16)spell_id; - if ((spell_id_ != spell_id) || (spell_id != spell_id_)) { - c->Message(Chat::Red, "FATAL ERROR: Type conversion data loss with spell_id (%i != %u)", spell_id, spell_id_); - return; - } - - if (!IsDiscipline(spell_id_)) - break; - - for (uint32 r = 0; r < MAX_PP_DISCIPLINES; ++r) { - if (t->GetPP().disciplines.values[r] == spell_id_) { - t->Message(Chat::Red, "You already know this discipline."); - break; // continue the 1st loop - } - else if (t->GetPP().disciplines.values[r] == 0) { - t->GetPP().disciplines.values[r] = spell_id_; - change = true; - t->Message(Chat::White, "You have learned a new discipline!"); - ++count; // success counter - break; // continue the 1st loop - } // if we get to this point, there's already a discipline in this slot, so we continue onto the next slot - } - - break; - } - } - - if (change) { - t->SendDisciplineUpdate(); - t->SaveDisciplines(); - } - - if (count > 0) { - t->Message(Chat::White, "Successfully trained %u disciplines.", count); - if (t != c) { - c->Message(Chat::White, "Successfully trained %u disciplines for %s.", count, t->GetName()); - } - } - else { - t->Message(Chat::White, "No disciplines trained."); - if (t != c) { - c->Message(Chat::White, "No disciplines trained for %s.", t->GetName()); - } + uint16 learned_disciplines = target->LearnDisciplines(min_level, max_level); + if (target != c) { + std::string discipline_message = ( + learned_disciplines > 0 ? + ( + learned_disciplines == 1 ? + "A new discipline" : + fmt::format("{} New disciplines", learned_disciplines) + ) : + "No new disciplines" + ); + c->Message( + Chat::White, + fmt::format( + "{} learned for {}.", + discipline_message, + target->GetCleanName() + ).c_str() + ); } } diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 441b490a1..002004274 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -2291,6 +2291,26 @@ void Lua_Client::RemoveLDoNWin(uint32 theme_id) { self->UpdateLDoNWinLoss(theme_id, true, true); } +uint16 Lua_Client::ScribeSpells(uint8 min_level, uint8 max_level) { + Lua_Safe_Call_Int(); + return self->ScribeSpells(min_level, max_level); +} + +uint16 Lua_Client::LearnDisciplines(uint8 min_level, uint8 max_level) { + Lua_Safe_Call_Int(); + return self->LearnDisciplines(min_level, max_level); +} + +int Lua_Client::GetNextAvailableDisciplineSlot() { + Lua_Safe_Call_Int(); + return self->GetNextAvailableDisciplineSlot(); +} + +int Lua_Client::GetNextAvailableDisciplineSlot(int starting_slot) { + Lua_Safe_Call_Int(); + return self->GetNextAvailableDisciplineSlot(starting_slot); +} + luabind::scope lua_register_client() { return luabind::class_("Client") .def(luabind::constructor<>()) @@ -2673,7 +2693,11 @@ luabind::scope lua_register_client() { .def("UntrainDiscBySpellID", (void(Lua_Client::*)(uint16,bool))&Lua_Client::UntrainDiscBySpellID) .def("SummonBaggedItems", (void(Lua_Client::*)(uint32,luabind::adl::object))&Lua_Client::SummonBaggedItems) .def("RemoveLDoNLoss", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveLDoNLoss) - .def("RemoveLDoNWin", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveLDoNWin); + .def("RemoveLDoNWin", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveLDoNWin) + .def("ScribeSpells", (uint16(Lua_Client::*)(uint8,uint8))&Lua_Client::ScribeSpells) + .def("LearnDisciplines", (uint16(Lua_Client::*)(uint8,uint8))&Lua_Client::LearnDisciplines) + .def("GetNextAvailableDisciplineSlot", (int(Lua_Client::*)(void))&Lua_Client::GetNextAvailableDisciplineSlot) + .def("GetNextAvailableDisciplineSlot", (int(Lua_Client::*)(int))&Lua_Client::GetNextAvailableDisciplineSlot); } luabind::scope lua_register_inventory_where() { diff --git a/zone/lua_client.h b/zone/lua_client.h index a5fbd0834..5bab5c219 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -176,11 +176,13 @@ public: luabind::object GetScribeableSpells(lua_State* L, uint8 min_level, uint8 max_level); void ScribeSpell(int spell_id, int slot); void ScribeSpell(int spell_id, int slot, bool update_client); + uint16 ScribeSpells(uint8 min_level, uint8 max_level); void UnscribeSpell(int slot); void UnscribeSpell(int slot, bool update_client); void UnscribeSpellAll(); void UnscribeSpellAll(bool update_client); void TrainDisc(int itemid); + uint16 LearnDisciplines(uint8 min_level, uint8 max_level); void TrainDiscBySpellID(int32 spell_id); int GetDiscSlotBySpellID(int32 spell_id); void UntrainDisc(int slot); @@ -300,6 +302,8 @@ public: void ClearCompassMark(); int GetNextAvailableSpellBookSlot(); int GetNextAvailableSpellBookSlot(int start); + int GetNextAvailableDisciplineSlot(); + int GetNextAvailableDisciplineSlot(int starting_slot); uint32 GetSpellIDByBookSlot(int book_slot); int FindSpellBookSlotBySpellID(int spell_id); void UpdateTaskActivity(int task, int activity, int count); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 264b3b09b..3d3aa96b3 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -5865,6 +5865,68 @@ XS(XS_Client_RemoveLDoNWin) { XSRETURN_EMPTY; } +XS(XS_Client_GetFreeDisciplineSlot); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetFreeDisciplineSlot) { + dXSARGS; + if (items != 1 || items != 2) + Perl_croak(aTHX_ "Usage: Client::GetFreeDisciplineSlot(THIS, [int starting_slot = 0])"); // @categories Spells and Disciplines + { + Client *THIS; + int free_discipline_slot; + int starting_slot = 0; + dXSTARG; + VALIDATE_THIS_IS_CLIENT; + if (items == 2) { + starting_slot = SvIV(ST(1)); + } + + free_discipline_slot = THIS->GetNextAvailableDisciplineSlot(starting_slot); + XSprePUSH; + PUSHi((IV) free_discipline_slot); + } + XSRETURN(1); +} + +XS(XS_Client_ScribeSpells); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_ScribeSpells) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::ScribeSpells(THIS, uint8 min_level, uint8 max_level)"); // @categories Spells and Disciplines + { + Client *THIS; + uint8 min_level = (uint8) SvUV(ST(1)); + uint8 max_level = (uint8) SvUV(ST(2)); + uint16 scribed_spells = 0; + dXSTARG; + VALIDATE_THIS_IS_CLIENT; + + scribed_spells = THIS->ScribeSpells(min_level, max_level); + XSprePUSH; + PUSHu((UV) scribed_spells); + } + XSRETURN(1); +} + +XS(XS_Client_LearnDisciplines); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_LearnDisciplines) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::LearnDisciplines(THIS, uint8 min_level, uint8 max_level)"); // @categories Spells and Disciplines + { + Client *THIS; + uint8 min_level = (uint8) SvUV(ST(1)); + uint8 max_level = (uint8) SvUV(ST(2)); + uint16 learned_disciplines = 0; + dXSTARG; + VALIDATE_THIS_IS_CLIENT; + + learned_disciplines = THIS->LearnDisciplines(min_level, max_level); + XSprePUSH; + PUSHu((UV) learned_disciplines); + } + XSRETURN(1); +} + #ifdef __cplusplus extern "C" #endif @@ -6186,6 +6248,9 @@ XS(boot_Client) { newXSproto(strcpy(buf, "UseDiscipline"), XS_Client_UseDiscipline, file, "$$$"); newXSproto(strcpy(buf, "WorldKick"), XS_Client_WorldKick, file, "$"); newXSproto(strcpy(buf, "ReadBookByName"), XS_Client_ReadBookByName, file, "$$$"); + newXSproto(strcpy(buf, "GetFreeDisciplineSlot"), XS_Client_GetFreeDisciplineSlot, file, "$;$"); + newXSproto(strcpy(buf, "ScribeSpells"), XS_Client_ScribeSpells, file, "$$$"); + newXSproto(strcpy(buf, "LearnDisciplines"), XS_Client_LearnDisciplines, file, "$$$"); XSRETURN_YES; } diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 4da5c2b1d..956b1882b 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1116,69 +1116,12 @@ void QuestManager::permagender(int gender_id) { uint16 QuestManager::scribespells(uint8 max_level, uint8 min_level) { QuestManagerCurrentQuestVars(); - int book_slot = initiator->GetNextAvailableSpellBookSlot(); - std::vector spell_ids = initiator->GetScribeableSpells(min_level, max_level); - int spell_count = spell_ids.size(); - int spells_learned = 0; - if (spell_count > 0) { - for (auto spell_id : spell_ids) { - if (book_slot == -1) { - initiator->Message( - Chat::Red, - "Unable to scribe spell %s (%i) to Spell Book: Spell Book is Full.", spells[spell_id].name, spell_id - ); - break; - } - - if (initiator->HasSpellScribed(spell_id)) - continue; - - // defer saving per spell and bulk save at the end - initiator->ScribeSpell(spell_id, book_slot, true, true); - book_slot = initiator->GetNextAvailableSpellBookSlot(book_slot); - spells_learned++; - } - } - - if (spells_learned > 0) { - std::string spell_message = (spells_learned == 1 ? "a new spell" : fmt::format("{} new spells", spells_learned)); - initiator->Message(Chat::White, fmt::format("You have learned {}!", spell_message).c_str()); - - // bulk insert spells - initiator->SaveSpells(); - } - return spells_learned; + return initiator->ScribeSpells(min_level, max_level); } uint16 QuestManager::traindiscs(uint8 max_level, uint8 min_level) { QuestManagerCurrentQuestVars(); - int character_id = initiator->CharacterID(); - std::vector spell_ids = initiator->GetLearnableDisciplines(min_level, max_level); - int discipline_count = spell_ids.size(); - int disciplines_learned = 0; - if (discipline_count > 0) { - for (auto spell_id : spell_ids) { - if (initiator->HasDisciplineLearned(spell_id)) - continue; - - for (uint32 index = 0; index < MAX_PP_DISCIPLINES; index++) { - if (initiator->GetPP().disciplines.values[index] == 0) { - initiator->GetPP().disciplines.values[index] = spell_id; - database.SaveCharacterDisc(character_id, index, spell_id); - disciplines_learned++; - break; - } - } - } - } - - if (disciplines_learned > 0) { - std::string discipline_message = (disciplines_learned == 1 ? "a new discipline" : fmt::format("{} new disciplines", disciplines_learned)); - initiator->SendDisciplineUpdate(); - initiator->Message(Chat::White, fmt::format("You have learned {}!", discipline_message).c_str()); - } - - return disciplines_learned; + return initiator->LearnDisciplines(min_level, max_level); } void QuestManager::unscribespells() { diff --git a/zone/spells.cpp b/zone/spells.cpp index 9a7ca6647..5def206bc 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -6257,5 +6257,12 @@ bool Client::IsLinkedSpellReuseTimerReady(uint32 timer_id) return GetPTimers().Expired(&database, pTimerLinkedSpellReuseStart + timer_id, false); } +int Client::GetNextAvailableDisciplineSlot(int starting_slot) { + for (uint32 index = starting_slot; index < MAX_PP_DISCIPLINES; index++) { + if (!IsValidSpell(GetPP().disciplines.values[index])) { + return index; + } + } - + return -1; // Return -1 if No Slots open +} \ No newline at end of file From 18cc648c8d4aaf440a459ba5ec46363636e59687 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 3 Nov 2021 19:01:08 -0400 Subject: [PATCH 318/624] Update spell_effects.cpp (#1668) --- zone/spell_effects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 7f7cb7759..a26f370fc 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -758,7 +758,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove } InterruptSpell(); entity_list.RemoveDebuffs(this); - entity_list.RemoveFromTargets(this); + entity_list.RemoveFromHateLists(this); WipeHateList(); Mob *my_pet = GetPet(); From 5874deeffc0bb5833213540338408d6c31300c50 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 3 Nov 2021 21:07:45 -0400 Subject: [PATCH 319/624] Update spell_effects.cpp (#1670) --- zone/spell_effects.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index a26f370fc..c9538523b 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -8493,8 +8493,13 @@ bool Mob::NegateSpellEffect(uint16 spell_id, int effect_id) //Match each of the negate effects with the current spell effect, if found, that effect will not be applied. for (int j = 0; j < EFFECT_COUNT; j++) { - if (spells[buffs[i].spellid].limit_value[j] == effect_id) { - return true; + if (spells[buffs[i].spellid].effect_id[j] == SE_NegateSpellEffect && + spells[buffs[i].spellid].limit_value[j] == effect_id && + (spells[buffs[i].spellid].base_value[j] == NEGATE_SPA_ALL_BONUSES || + spells[buffs[i].spellid].base_value[j] == NEGATE_SPA_SPELLBONUS || + spells[buffs[i].spellid].base_value[j] == NEGATE_SPA_SPELLBONUS_AND_ITEMBONUS || + spells[buffs[i].spellid].base_value[j] == NEGATE_SPA_SPELLBONUS_AND_AABONUS)) { + return true; } } } From 8c95323728dc5815b087a3873180e0b0c521af84 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 5 Nov 2021 10:39:17 -0400 Subject: [PATCH 320/624] [Spells] Update to Charm target restriction code (#1666) * charm target restrictions * fixed * Update spells.cpp * Update spells.cpp * Update spells.cpp only send spell bar when we have to, avoid potential exploit. * logs --- zone/mob.h | 1 + zone/spell_effects.cpp | 50 ++++++++++++++++++++++++------------------ zone/spells.cpp | 15 +++++++++++++ 3 files changed, 45 insertions(+), 21 deletions(-) diff --git a/zone/mob.h b/zone/mob.h index c3da39e27..9b8361b9c 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -846,6 +846,7 @@ public: inline void SetSpellPowerDistanceMod(int16 value) { SpellPowerDistanceMod = value; }; int32 GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot = 0); bool HarmonySpellLevelCheck(int32 spell_id, Mob* target = nullptr); + bool PassCharmTargetRestriction(Mob *target); bool CanFocusUseRandomEffectivenessByType(focusType type); int GetFocusRandomEffectivenessValue(int focus_base, int focus_base2, bool best_focus = 0); int GetHealRate() const { return itembonuses.HealRate + spellbonuses.HealRate + aabonuses.HealRate; } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index c9538523b..44d07f957 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -732,27 +732,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove break; } - if (IsClient() && caster->IsClient()) { - caster->Message(Chat::White, "Unable to cast charm on a fellow player."); - BuffFadeByEffect(SE_Charm); - break; - } - else if (IsCorpse()) { - caster->Message(Chat::White, "Unable to cast charm on a corpse."); - BuffFadeByEffect(SE_Charm); - break; - } - else if (caster->GetPet() != nullptr && caster->IsClient()) { - caster->Message(Chat::White, "You cannot charm something when you already have a pet."); - BuffFadeByEffect(SE_Charm); - break; - } - else if (GetOwner()) { - caster->Message(Chat::White, "You cannot charm someone else's pet!"); - BuffFadeByEffect(SE_Charm); - break; - } - if (IsNPC()) { CastToNPC()->SaveGuardSpotCharm(); } @@ -8439,6 +8418,35 @@ bool Mob::HarmonySpellLevelCheck(int32 spell_id, Mob *target) return true; } +bool Mob::PassCharmTargetRestriction(Mob *target) { + + //Level restriction check should not go here. + if (!target) { + return false; + } + + if (target->IsClient() && IsClient()) { + MessageString(Chat::Red, CANNOT_AFFECT_PC); + LogSpells("Spell casting canceled: Can not cast charm on a client."); + return false; + } + else if (target->IsCorpse()) { + LogSpells("Spell casting canceled: Can not cast charm on a corpse."); + return false; + } + else if (GetPet() && IsClient()) { + MessageString(Chat::Red, ONLY_ONE_PET); + LogSpells("Spell casting canceled: Can not cast charm if you have a pet."); + return false; + } + else if (target->GetOwner()) { + MessageString(Chat::Red, CANNOT_CHARM); + LogSpells("Spell casting canceled: Can not cast charm on a pet."); + return false; + } + return true; +} + bool Mob::CanFocusUseRandomEffectivenessByType(focusType type) { switch (type) { diff --git a/zone/spells.cpp b/zone/spells.cpp index 5def206bc..6cf9837c9 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -229,6 +229,21 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, return false; } + if (IsEffectInSpell(spell_id, SE_Charm) && !PassCharmTargetRestriction(entity_list.GetMobID(target_id))) { + bool can_send_spellbar_enable = true; + if ((item_slot != -1 && cast_time == 0) || aa_id) { + can_send_spellbar_enable = false; + } + + if (can_send_spellbar_enable) { + SendSpellBarEnable(spell_id); + } + if (casting_spell_id && IsNPC()) { + CastToNPC()->AI_Event_SpellCastFinished(false, static_cast(casting_spell_slot)); + } + return false; + } + if (HasActiveSong() && IsBardSong(spell_id)) { LogSpells("Casting a new song while singing a song. Killing old song [{}]", bardsong); //Note: this does NOT tell the client From f1bfd6bc2a84fc44b1a1127d7a5d9e32588122cc Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 5 Nov 2021 14:14:11 -0400 Subject: [PATCH 321/624] [Spells] Implemented SPA 512 SE_Proc_Timer_Modifier, Fixed AA procs not working (#1646) * update for SPA 511 * remove debugs, AA implemented * update * twinprocfix * AA procs added * format update * update * proctimer limits * update * rename function renamed function only check for buffs value > 0, don't need to check for AA's which are negative ID's * pre merge * variable updates * Update spell_effects.cpp * var rename update var name to better represent its function. * updated proc struct added reuse timer * reuse timer to spell procs * updates * debug remove * Update mob.cpp * fix * merge --- common/spdat.cpp | 35 ++++++++ common/spdat.h | 8 +- zone/attack.cpp | 181 +++++++++++++++++++++++++++++++++-------- zone/bonuses.cpp | 70 ++++++++++++++++ zone/client_packet.cpp | 6 +- zone/common.h | 5 ++ zone/mob.cpp | 65 ++++++++++----- zone/mob.h | 15 +++- zone/pets.cpp | 13 +-- zone/spell_effects.cpp | 163 +++++++++++++++++++++++++++++-------- zone/spells.cpp | 17 ++-- 11 files changed, 469 insertions(+), 109 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index e52da4da1..42b2fe26d 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1597,3 +1597,38 @@ int32 GetViralSpreadRange(int32 spell_id) { return spells[spell_id].viral_range; } + +uint32 GetProcLimitTimer(int32 spell_id, int proc_type) { + + //This allows for support for effects that may have multiple different proc types and timers. + if (!IsValidSpell(spell_id)) { + return 0; + } + + bool use_next_timer = false; + for (int i = 0; i < EFFECT_COUNT; ++i) { + + if (proc_type == SE_WeaponProc) { + if (spells[spell_id].effect_id[i] == SE_WeaponProc || spells[spell_id].effect_id[i] == SE_AddMeleeProc) { + use_next_timer = true; + } + } + + if (proc_type == SE_RangedProc) { + if (spells[spell_id].effect_id[i] == SE_RangedProc) { + use_next_timer = true; + } + } + + if (proc_type == SE_DefensiveProc) { + if (spells[spell_id].effect_id[i] == SE_DefensiveProc) { + use_next_timer = true; + } + } + + if (use_next_timer && spells[spell_id].effect_id[i] == SE_Proc_Timer_Modifier) { + return spells[spell_id].limit_value[i]; + } + } + return 0; +} diff --git a/common/spdat.h b/common/spdat.h index 9710e1825..fd9413444 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -179,8 +179,11 @@ #define MAX_RESISTABLE_EFFECTS 12 // Number of effects that are typcially checked agianst resists. #define MaxLimitInclude 16 //Number(x 0.5) of focus Limiters that have inclusive checks used when calcing focus effects #define MAX_SKILL_PROCS 4 //Number of spells to check skill procs from. (This is arbitrary) [Single spell can have multiple proc checks] +#define MAX_AA_PROCS 16 //(Actual Proc Amount is MAX_AA_PROCS/4) Number of spells to check AA procs from. (This is arbitrary) #define MAX_SYMPATHETIC_PROCS 10 // Number of sympathetic procs a client can have (This is arbitrary) -#define MAX_FOCUS_PROC_LIMIT_TIMERS 20 //Number of proc limiting timers that can be going at same time (This is arbitrary) +#define MAX_FOCUS_PROC_LIMIT_TIMERS 20 //Number of focus recast timers that can be going at same time (This is arbitrary) +#define MAX_PROC_LIMIT_TIMERS 8 //Number of proc delay timers that can be going at same time, different proc types get their own timer array. (This is arbitrary) + const int Z_AGGRO=10; @@ -1209,7 +1212,7 @@ typedef enum { #define SE_Health_Transfer 509 // implemented - exchange health for damage or healing on a target. ie Lifeburn/Act of Valor #define SE_Fc_ResistIncoming 510 // implemented, @Fc, On Target, resist modifier, base: amt #define SE_Ff_FocusTimerMin 511 // implemented, @Ff, sets a recast time until focus can be used again, base: 1, limit: time ms, Note: ie. limit to 1 trigger every 1.5 seconds -#define SE_Proc_Timer_Modifier 512 // not implemented - limits procs per amount of a time based on timer value (ie limit to 1 proc every 55 seconds) +#define SE_Proc_Timer_Modifier 512 // implemented - limits procs per amount of a time based on timer value, base: 1, limit: time ms, Note:, ie limit to 1 proc every 55 seconds) //#define SE_Mana_Max_Percent 513 // //#define SE_Endurance_Max_Percent 514 // #define SE_AC_Avoidance_Max_Percent 515 // implemented - stackable avoidance modifier @@ -1514,6 +1517,7 @@ int GetViralMinSpreadTime(int32 spell_id); int GetViralMaxSpreadTime(int32 spell_id); int GetViralSpreadRange(int32 spell_id); bool IsInstrumentModAppliedToSpellEffect(int32 spell_id, int effect); +uint32 GetProcLimitTimer(int32 spell_id, int proc_type); int CalcPetHp(int levelb, int classb, int STA = 75); int GetSpellEffectDescNum(uint16 spell_id); diff --git a/zone/attack.cpp b/zone/attack.cpp index f9f086f7f..28a6f995e 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3380,17 +3380,37 @@ int32 Mob::ReduceAllDamage(int32 damage) bool Mob::HasProcs() const { - for (int i = 0; i < MAX_PROCS; i++) - if (PermaProcs[i].spellID != SPELL_UNKNOWN || SpellProcs[i].spellID != SPELL_UNKNOWN) + for (int i = 0; i < MAX_PROCS; i++) { + if (PermaProcs[i].spellID != SPELL_UNKNOWN || SpellProcs[i].spellID != SPELL_UNKNOWN) { return true; + } + } + + if (IsClient()) { + for (int i = 0; i < MAX_AA_PROCS; i += 4) { + if (aabonuses.SpellProc[i]) { + return true; + } + } + } return false; } bool Mob::HasDefensiveProcs() const { - for (int i = 0; i < MAX_PROCS; i++) - if (DefensiveProcs[i].spellID != SPELL_UNKNOWN) + for (int i = 0; i < MAX_PROCS; i++) { + if (DefensiveProcs[i].spellID != SPELL_UNKNOWN) { return true; + } + } + + if (IsClient()) { + for (int i = 0; i < MAX_AA_PROCS; i += 4) { + if (aabonuses.DefensiveProc[i]) { + return true; + } + } + } return false; } @@ -3415,9 +3435,19 @@ bool Mob::HasSkillProcSuccess() const bool Mob::HasRangedProcs() const { - for (int i = 0; i < MAX_PROCS; i++) - if (RangedProcs[i].spellID != SPELL_UNKNOWN) + for (int i = 0; i < MAX_PROCS; i++){ + if (RangedProcs[i].spellID != SPELL_UNKNOWN) { return true; + } + } + + if (IsClient()) { + for (int i = 0; i < MAX_AA_PROCS; i += 4) { + if (aabonuses.RangedProc[i]) { + return true; + } + } + } return false; } @@ -4051,33 +4081,61 @@ void Mob::TryDefensiveProc(Mob *on, uint16 hand) { return; } - if (!HasDefensiveProcs()) + if (!HasDefensiveProcs()) { return; + } if (!on->HasDied() && on->GetHP() > 0) { float ProcChance, ProcBonus; on->GetDefensiveProcChances(ProcBonus, ProcChance, hand, this); - if (hand != EQ::invslot::slotPrimary) + if (hand != EQ::invslot::slotPrimary) { ProcChance /= 2; + } int level_penalty = 0; int level_diff = GetLevel() - on->GetLevel(); - if (level_diff > 6)//10% penalty per level if > 6 levels over target. + if (level_diff > 6) {//10% penalty per level if > 6 levels over target. level_penalty = (level_diff - 6) * 10; + } ProcChance -= ProcChance*level_penalty / 100; - if (ProcChance < 0) + if (ProcChance < 0) { return; + } + //Spell Procs and Quest added procs for (int i = 0; i < MAX_PROCS; i++) { if (IsValidSpell(DefensiveProcs[i].spellID)) { - float chance = ProcChance * (static_cast(DefensiveProcs[i].chance) / 100.0f); - if (zone->random.Roll(chance)) { - ExecWeaponProc(nullptr, DefensiveProcs[i].spellID, on); - CheckNumHitsRemaining(NumHit::DefensiveSpellProcs, 0, DefensiveProcs[i].base_spellID); + if (!IsProcLimitTimerActive(DefensiveProcs[i].base_spellID, DefensiveProcs[i].proc_reuse_time, SE_DefensiveProc)) { + float chance = ProcChance * (static_cast(DefensiveProcs[i].chance) / 100.0f); + if (zone->random.Roll(chance)) { + ExecWeaponProc(nullptr, DefensiveProcs[i].spellID, on); + CheckNumHitsRemaining(NumHit::DefensiveSpellProcs, 0, DefensiveProcs[i].base_spellID); + SetProcLimitTimer(DefensiveProcs[i].base_spellID, DefensiveProcs[i].proc_reuse_time, SE_DefensiveProc); + } + } + } + } + + //AA Procs + if (IsClient()){ + for (int i = 0; i < MAX_AA_PROCS; i += 4) { + int32 aa_rank_id = aabonuses.DefensiveProc[i]; + int32 aa_spell_id = aabonuses.DefensiveProc[i + 1]; + int32 aa_proc_chance = 100 + aabonuses.DefensiveProc[i + 2]; + uint32 aa_proc_reuse_timer = aabonuses.DefensiveProc[i + 3]; + + if (aa_rank_id) { + if (!IsProcLimitTimerActive(-aa_rank_id, aa_proc_reuse_timer, SE_DefensiveProc)) { + float chance = ProcChance * (static_cast(aa_proc_chance) / 100.0f); + if (zone->random.Roll(chance) && IsValidSpell(aa_spell_id)) { + ExecWeaponProc(nullptr, aa_spell_id, on); + SetProcLimitTimer(-aa_rank_id, aa_proc_reuse_timer, SE_DefensiveProc); + } + } } } } @@ -4122,7 +4180,9 @@ void Mob::TryWeaponProc(const EQ::ItemInstance* weapon_g, Mob *on, uint16 hand) void Mob::TryWeaponProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon, Mob *on, uint16 hand) { - + if (!on) { + return; + } if (!weapon) return; uint16 skillinuse = 28; @@ -4206,6 +4266,10 @@ void Mob::TryWeaponProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon, Mob *on, uint16 hand) { + if (!on) { + return; + } + float ProcBonus = static_cast(spellbonuses.SpellProcChance + itembonuses.SpellProcChance + aabonuses.SpellProcChance); float ProcChance = 0.0f; @@ -4239,7 +4303,7 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon, // Not ranged if (!rangedattk) { - // Perma procs (AAs) + // Perma procs (Not used for AA, they are handled below) if (PermaProcs[i].spellID != SPELL_UNKNOWN) { if (zone->random.Roll(PermaProcs[i].chance)) { // TODO: Do these get spell bonus? LogCombat("Permanent proc [{}] procing spell [{}] ([{}] percent chance)", i, PermaProcs[i].spellID, PermaProcs[i].chance); @@ -4256,32 +4320,79 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon, poison_slot=i; continue; // Process the poison proc last per @mackal } - - float chance = ProcChance * (static_cast(SpellProcs[i].chance) / 100.0f); - if (zone->random.Roll(chance)) { - LogCombat("Spell proc [{}] procing spell [{}] ([{}] percent chance)", i, SpellProcs[i].spellID, chance); - SendBeginCast(SpellProcs[i].spellID, 0); - ExecWeaponProc(nullptr, SpellProcs[i].spellID, on, SpellProcs[i].level_override); - CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0, - SpellProcs[i].base_spellID); - } - else { - LogCombat("Spell proc [{}] failed to proc [{}] ([{}] percent chance)", i, SpellProcs[i].spellID, chance); + + if (!IsProcLimitTimerActive(SpellProcs[i].base_spellID, SpellProcs[i].proc_reuse_time, SE_WeaponProc)) { + float chance = ProcChance * (static_cast(SpellProcs[i].chance) / 100.0f); + if (zone->random.Roll(chance)) { + LogCombat("Spell proc [{}] procing spell [{}] ([{}] percent chance)", i, SpellProcs[i].spellID, chance); + SendBeginCast(SpellProcs[i].spellID, 0); + ExecWeaponProc(nullptr, SpellProcs[i].spellID, on, SpellProcs[i].level_override); + SetProcLimitTimer(SpellProcs[i].base_spellID, SpellProcs[i].proc_reuse_time, SE_WeaponProc); + CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0, SpellProcs[i].base_spellID); + } + else { + LogCombat("Spell proc [{}] failed to proc [{}] ([{}] percent chance)", i, SpellProcs[i].spellID, chance); + } } } } else if (rangedattk) { // ranged only // ranged spell procs (buffs) if (RangedProcs[i].spellID != SPELL_UNKNOWN) { - float chance = ProcChance * (static_cast(RangedProcs[i].chance) / 100.0f); - if (zone->random.Roll(chance)) { - LogCombat("Ranged proc [{}] procing spell [{}] ([{}] percent chance)", i, RangedProcs[i].spellID, chance); - ExecWeaponProc(nullptr, RangedProcs[i].spellID, on); - CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0, - RangedProcs[i].base_spellID); + + if (!IsProcLimitTimerActive(RangedProcs[i].base_spellID, RangedProcs[i].proc_reuse_time, SE_RangedProc)) { + float chance = ProcChance * (static_cast(RangedProcs[i].chance) / 100.0f); + if (zone->random.Roll(chance)) { + LogCombat("Ranged proc [{}] procing spell [{}] ([{}] percent chance)", i, RangedProcs[i].spellID, chance); + ExecWeaponProc(nullptr, RangedProcs[i].spellID, on); + CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0, RangedProcs[i].base_spellID); + SetProcLimitTimer(RangedProcs[i].base_spellID, RangedProcs[i].proc_reuse_time, SE_RangedProc); + } + else { + LogCombat("Ranged proc [{}] failed to proc [{}] ([{}] percent chance)", i, RangedProcs[i].spellID, chance); + } } - else { - LogCombat("Ranged proc [{}] failed to proc [{}] ([{}] percent chance)", i, RangedProcs[i].spellID, chance); + } + } + } + + //AA Procs + if (IsClient()) { + for (int i = 0; i < MAX_AA_PROCS; i += 4) { + + int32 aa_rank_id = 0; + int32 aa_spell_id = SPELL_UNKNOWN; + int32 aa_proc_chance = 100; + uint32 aa_proc_reuse_timer = 0; + int proc_type = 0; //used to deterimne which timer array is used. + + if (!rangedattk) { + + aa_rank_id = aabonuses.SpellProc[i]; + aa_spell_id = aabonuses.SpellProc[i + 1]; + aa_proc_chance += aabonuses.SpellProc[i + 2]; + aa_proc_reuse_timer = aabonuses.SpellProc[i + 3]; + proc_type = SE_WeaponProc; + } + else { + aa_rank_id = aabonuses.RangedProc[i]; + aa_spell_id = aabonuses.RangedProc[i + 1]; + aa_proc_chance += aabonuses.RangedProc[i + 2]; + aa_proc_reuse_timer = aabonuses.RangedProc[i + 3]; + proc_type = SE_RangedProc; + } + + if (aa_rank_id) { + if (!IsProcLimitTimerActive(-aa_rank_id, aa_proc_reuse_timer, proc_type)) { + float chance = ProcChance * (static_cast(aa_proc_chance) / 100.0f); + if (zone->random.Roll(chance) && IsValidSpell(aa_spell_id)) { + LogCombat("AA proc [{}] procing spell [{}] ([{}] percent chance)", aa_rank_id, aa_spell_id, chance); + ExecWeaponProc(nullptr, aa_spell_id, on); + SetProcLimitTimer(-aa_rank_id, aa_proc_reuse_timer, proc_type); + } + else { + LogCombat("AA proc [{}] failed to proc [{}] ([{}] percent chance)", aa_rank_id, aa_spell_id, chance); + } } } } diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index bdc02009c..e15175cad 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1071,6 +1071,76 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) } break; + case SE_WeaponProc: + case SE_AddMeleeProc: + for (int i = 0; i < MAX_AA_PROCS; i += 4) { + if (!newbon->SpellProc[i]) { + newbon->SpellProc[i] = rank.id; //aa rank id + newbon->SpellProc[i + 1] = base_value; //proc spell id + newbon->SpellProc[i + 2] = limit_value; //proc rate modifer + newbon->SpellProc[i + 3] = 0; //Lock out Timer + break; + } + } + break; + + case SE_RangedProc: + for (int i = 0; i < MAX_AA_PROCS; i += 4) { + if (!newbon->RangedProc[i]) { + newbon->RangedProc[i] = rank.id; //aa rank id + newbon->RangedProc[i + 1] = base_value; //proc spell id + newbon->RangedProc[i + 2] = limit_value; //proc rate modifer + newbon->RangedProc[i + 3] = 0; //Lock out Timer + break; + } + } + break; + + case SE_DefensiveProc: + for (int i = 0; i < MAX_AA_PROCS; i += 4) { + if (!newbon->DefensiveProc[i]) { + newbon->DefensiveProc[i] = rank.id; //aa rank id + newbon->DefensiveProc[i + 1] = base_value; //proc spell id + newbon->DefensiveProc[i + 2] = limit_value; //proc rate modifer + newbon->DefensiveProc[i + 3] = 0; //Lock out Timer + break; + } + } + break; + + case SE_Proc_Timer_Modifier: { + /* + AA can multiples of this in a single effect, proc should use the timer + that comes after the respective proc spell effect, thus rank.id will be already set + when this is checked. + */ + + newbon->Proc_Timer_Modifier = true; + + for (int i = 0; i < MAX_AA_PROCS; i += 4) { + if (newbon->SpellProc[i] == rank.id) { + if (!newbon->SpellProc[i + 3]) { + newbon->SpellProc[i + 3] = limit_value;//Lock out Timer + break; + } + } + + if (newbon->RangedProc[i] == rank.id) { + if (!newbon->RangedProc[i + 3]) { + newbon->RangedProc[i + 3] = limit_value;//Lock out Timer + break; + } + } + + if (newbon->DefensiveProc[i] == rank.id) { + if (!newbon->DefensiveProc[i + 3]) { + newbon->DefensiveProc[i + 3] = limit_value;//Lock out Timer + break; + } + } + } + break; + } case SE_CriticalHitChance: { // Bad data or unsupported new skill diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 9273290b3..ab8fa7b0f 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -761,17 +761,17 @@ void Client::CompleteConnect() case SE_AddMeleeProc: case SE_WeaponProc: { - AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, buffs[j1].casterlevel); + AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, buffs[j1].casterlevel, GetProcLimitTimer(buffs[j1].spellid, SE_WeaponProc)); break; } case SE_DefensiveProc: { - AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid); + AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, GetProcLimitTimer(buffs[j1].spellid, SE_DefensiveProc)); break; } case SE_RangedProc: { - AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid); + AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, GetProcLimitTimer(buffs[j1].spellid, SE_RangedProc)); break; } } diff --git a/zone/common.h b/zone/common.h index 733a4e3fd..85f2d3cbc 100644 --- a/zone/common.h +++ b/zone/common.h @@ -535,6 +535,10 @@ struct StatBonuses { bool LimitToSkill[EQ::skills::HIGHEST_SKILL + 2]; // Determines if we need to search for a skill proc. uint32 SkillProc[MAX_SKILL_PROCS]; // Max number of spells containing skill_procs. uint32 SkillProcSuccess[MAX_SKILL_PROCS]; // Max number of spells containing skill_procs_success. + int32 SpellProc[MAX_AA_PROCS]; // Max number of spells containing melee spell procs. + int32 RangedProc[MAX_AA_PROCS]; // Max number of spells containing ranged spell procs. + int32 DefensiveProc[MAX_AA_PROCS]; // Max number of spells containing defensive spell procs. + bool Proc_Timer_Modifier; // Used to check if this exists, to avoid any further unnncessary checks. uint32 PC_Pet_Rampage[2]; // 0= % chance to rampage, 1=damage modifier uint32 PC_Pet_AE_Rampage[2]; // 0= % chance to AE rampage, 1=damage modifier uint32 PC_Pet_Flurry; // Percent chance flurry from double attack @@ -691,6 +695,7 @@ typedef struct uint16 chance; uint16 base_spellID; int level_override; + uint32 proc_reuse_time; } tProc; diff --git a/zone/mob.cpp b/zone/mob.cpp index 1c5752d9a..0ac852b56 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -284,22 +284,26 @@ Mob::Mob( // clear the proc arrays for (int j = 0; j < MAX_PROCS; j++) { - PermaProcs[j].spellID = SPELL_UNKNOWN; - PermaProcs[j].chance = 0; - PermaProcs[j].base_spellID = SPELL_UNKNOWN; - PermaProcs[j].level_override = -1; - SpellProcs[j].spellID = SPELL_UNKNOWN; - SpellProcs[j].chance = 0; - SpellProcs[j].base_spellID = SPELL_UNKNOWN; - SpellProcs[j].level_override = -1; - DefensiveProcs[j].spellID = SPELL_UNKNOWN; - DefensiveProcs[j].chance = 0; - DefensiveProcs[j].base_spellID = SPELL_UNKNOWN; - DefensiveProcs[j].level_override = -1; - RangedProcs[j].spellID = SPELL_UNKNOWN; - RangedProcs[j].chance = 0; - RangedProcs[j].base_spellID = SPELL_UNKNOWN; - RangedProcs[j].level_override = -1; + PermaProcs[j].spellID = SPELL_UNKNOWN; + PermaProcs[j].chance = 0; + PermaProcs[j].base_spellID = SPELL_UNKNOWN; + PermaProcs[j].level_override = -1; + PermaProcs[j].proc_reuse_time = 0; + SpellProcs[j].spellID = SPELL_UNKNOWN; + SpellProcs[j].chance = 0; + SpellProcs[j].base_spellID = SPELL_UNKNOWN; + SpellProcs[j].proc_reuse_time = 0; + SpellProcs[j].level_override = -1; + DefensiveProcs[j].spellID = SPELL_UNKNOWN; + DefensiveProcs[j].chance = 0; + DefensiveProcs[j].base_spellID = SPELL_UNKNOWN; + DefensiveProcs[j].level_override = -1; + DefensiveProcs[j].proc_reuse_time = 0; + RangedProcs[j].spellID = SPELL_UNKNOWN; + RangedProcs[j].chance = 0; + RangedProcs[j].base_spellID = SPELL_UNKNOWN; + RangedProcs[j].level_override = -1; + RangedProcs[j].proc_reuse_time = 0; } for (int i = EQ::textures::textureBegin; i < EQ::textures::materialCount; i++) { @@ -354,6 +358,15 @@ Mob::Mob( focusproclimit_timer[i].Disable(); } + for (int i = 0; i < MAX_PROC_LIMIT_TIMERS; i++) { + spell_proclimit_spellid[i] = 0; + spell_proclimit_timer[i].Disable(); + ranged_proclimit_spellid[i] = 0; + ranged_proclimit_timer[i].Disable(); + def_proclimit_spellid[i] = 0; + def_proclimit_timer[i].Disable(); + } + memset(&itembonuses, 0, sizeof(StatBonuses)); memset(&spellbonuses, 0, sizeof(StatBonuses)); memset(&aabonuses, 0, sizeof(StatBonuses)); @@ -3167,6 +3180,10 @@ int32 Mob::GetActSpellCasttime(uint16 spell_id, int32 casttime) void Mob::ExecWeaponProc(const EQ::ItemInstance *inst, uint16 spell_id, Mob *on, int level_override) { // Changed proc targets to look up based on the spells goodEffect flag. // This should work for the majority of weapons. + if (!on) { + return; + } + if(spell_id == SPELL_UNKNOWN || on->GetSpecialAbility(NO_HARM_FROM_CLIENT)) { //This is so 65535 doesn't get passed to the client message and to logs because it is not relavant information for debugging. return; @@ -3202,21 +3219,25 @@ void Mob::ExecWeaponProc(const EQ::ItemInstance *inst, uint16 spell_id, Mob *on, bool twinproc = false; int32 twinproc_chance = 0; - if(IsClient()) + if (IsClient()) { twinproc_chance = CastToClient()->GetFocusEffect(focusTwincast, spell_id); + } - if(twinproc_chance && zone->random.Roll(twinproc_chance)) + if (twinproc_chance && zone->random.Roll(twinproc_chance)) { twinproc = true; + } if (IsBeneficialSpell(spell_id) && (!IsNPC() || (IsNPC() && CastToNPC()->GetInnateProcSpellID() != spell_id))) { // NPC innate procs don't take this path ever SpellFinished(spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty, true, level_override); - if(twinproc) - SpellOnTarget(spell_id, this, 0, false, 0, true, level_override); + if (twinproc) { + SpellFinished(spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty, true, level_override); + } } else if(!(on->IsClient() && on->CastToClient()->dead)) { //dont proc on dead clients SpellFinished(spell_id, on, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty, true, level_override); - if(twinproc) - SpellOnTarget(spell_id, on, 0, false, 0, true, level_override); + if (twinproc && (!(on->IsClient() && on->CastToClient()->dead))) { + SpellFinished(spell_id, on, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty, true, level_override); + } } return; } diff --git a/zone/mob.h b/zone/mob.h index 9b8361b9c..ec7a84700 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -726,15 +726,15 @@ public: //Procs void TriggerDefensiveProcs(Mob *on, uint16 hand = EQ::invslot::slotPrimary, bool FromSkillProc = false, int damage = 0); - bool AddRangedProc(uint16 spell_id, uint16 iChance = 3, uint16 base_spell_id = SPELL_UNKNOWN); + bool AddRangedProc(uint16 spell_id, uint16 iChance = 3, uint16 base_spell_id = SPELL_UNKNOWN, uint32 proc_reuse_time = 0); bool RemoveRangedProc(uint16 spell_id, bool bAll = false); bool HasRangedProcs() const; - bool AddDefensiveProc(uint16 spell_id, uint16 iChance = 3, uint16 base_spell_id = SPELL_UNKNOWN); + bool AddDefensiveProc(uint16 spell_id, uint16 iChance = 3, uint16 base_spell_id = SPELL_UNKNOWN, uint32 proc_reuse_time = 0); bool RemoveDefensiveProc(uint16 spell_id, bool bAll = false); bool HasDefensiveProcs() const; bool HasSkillProcs() const; bool HasSkillProcSuccess() const; - bool AddProcToWeapon(uint16 spell_id, bool bPerma = false, uint16 iChance = 3, uint16 base_spell_id = SPELL_UNKNOWN, int level_override = -1); + bool AddProcToWeapon(uint16 spell_id, bool bPerma = false, uint16 iChance = 3, uint16 base_spell_id = SPELL_UNKNOWN, int level_override = -1, uint32 proc_reuse_time = 0); bool RemoveProcFromWeapon(uint16 spell_id, bool bAll = false); bool HasProcs() const; bool IsCombatProc(uint16 spell_id); @@ -861,6 +861,8 @@ public: bool IsFocusProcLimitTimerActive(int32 focus_spell_id); void SetFocusProcLimitTimer(int32 focus_spell_id, uint32 focus_reuse_time); + bool IsProcLimitTimerActive(int32 base_spell_id, uint32 proc_reuse_time, int proc_type); + void SetProcLimitTimer(int32 base_spell_id, uint32 proc_reuse_time, int proc_type); void VirusEffectProcess(); void SpreadVirusEffect(int32 spell_id, uint32 caster_id, int32 buff_tics_remaining); @@ -1469,6 +1471,13 @@ protected: Timer focusproclimit_timer[MAX_FOCUS_PROC_LIMIT_TIMERS]; //SPA 511 int32 focusproclimit_spellid[MAX_FOCUS_PROC_LIMIT_TIMERS]; //SPA 511 + Timer spell_proclimit_timer[MAX_PROC_LIMIT_TIMERS]; //SPA 512 + int32 spell_proclimit_spellid[MAX_PROC_LIMIT_TIMERS]; //SPA 512 + Timer ranged_proclimit_timer[MAX_PROC_LIMIT_TIMERS]; //SPA 512 + int32 ranged_proclimit_spellid[MAX_PROC_LIMIT_TIMERS]; //SPA 512 + Timer def_proclimit_timer[MAX_PROC_LIMIT_TIMERS]; //SPA 512 + int32 def_proclimit_spellid[MAX_PROC_LIMIT_TIMERS]; //SPA 512 + Timer shield_timer; uint32 m_shield_target_id; uint32 m_shielder_id; diff --git a/zone/pets.cpp b/zone/pets.cpp index 4ca6b7752..8aeb1f5f2 100644 --- a/zone/pets.cpp +++ b/zone/pets.cpp @@ -576,14 +576,17 @@ void NPC::SetPetState(SpellBuff_Struct *pet_buffs, uint32 *items) { if (buffs[j1].spellid <= (uint32)SPDAT_RECORDS) { for (int x1=0; x1 < EFFECT_COUNT; x1++) { switch (spells[buffs[j1].spellid].effect_id[x1]) { + case SE_AddMeleeProc: case SE_WeaponProc: // We need to reapply buff based procs // We need to do this here so suspended pets also regain their procs. - if (spells[buffs[j1].spellid].limit_value[x1] == 0) { - AddProcToWeapon(GetProcID(buffs[j1].spellid,x1), false, 100, buffs[j1].spellid); - } else { - AddProcToWeapon(GetProcID(buffs[j1].spellid,x1), false, 100+spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid); - } + AddProcToWeapon(GetProcID(buffs[j1].spellid,x1), false, 100+spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, buffs[j1].casterlevel, GetProcLimitTimer(buffs[j1].spellid, SE_WeaponProc)); + break; + case SE_DefensiveProc: + AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, GetProcLimitTimer(buffs[j1].spellid, SE_DefensiveProc)); + break; + case SE_RangedProc: + AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, GetProcLimitTimer(buffs[j1].spellid, SE_RangedProc)); break; case SE_Charm: case SE_Rune: diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 44d07f957..163aa18c6 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1897,11 +1897,27 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Weapon Proc: %s (id %d)", spells[effect_value].name, procid); #endif + AddProcToWeapon(procid, false, 100 + spells[spell_id].limit_value[i], spell_id, caster_level, GetProcLimitTimer(spell_id, SE_WeaponProc)); + break; + } - if(spells[spell_id].limit_value[i] == 0) - AddProcToWeapon(procid, false, 100, spell_id, caster_level); - else - AddProcToWeapon(procid, false, spells[spell_id].limit_value[i]+100, spell_id, caster_level); + case SE_RangedProc: + { + uint16 procid = GetProcID(spell_id, i); +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Ranged Proc: %+i", effect_value); +#endif + AddRangedProc(procid, 100 + spells[spell_id].limit_value[i], spell_id, GetProcLimitTimer(spell_id, SE_RangedProc)); + break; + } + + case SE_DefensiveProc: + { + uint16 procid = GetProcID(spell_id, i); +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Defensive Proc: %s (id %d)", spells[effect_value].name, procid); +#endif + AddDefensiveProc(procid, 100 + spells[spell_id].limit_value[i], spell_id, GetProcLimitTimer(spell_id, SE_DefensiveProc)); break; } @@ -2273,20 +2289,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove break; } - case SE_RangedProc: - { -#ifdef SPELL_EFFECT_SPAM - snprintf(effect_desc, _EDLEN, "Ranged Proc: %+i", effect_value); -#endif - uint16 procid = GetProcID(spell_id, i); - - if(spells[spell_id].limit_value[i] == 0) - AddRangedProc(procid, 100, spell_id); - else - AddRangedProc(procid, spells[spell_id].limit_value[i]+100, spell_id); - break; - } - case SE_Rampage: { #ifdef SPELL_EFFECT_SPAM @@ -2383,21 +2385,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove break; } - case SE_DefensiveProc: - { - uint16 procid = GetProcID(spell_id, i); -#ifdef SPELL_EFFECT_SPAM - snprintf(effect_desc, _EDLEN, "Defensive Proc: %s (id %d)", spells[effect_value].name, procid); -#endif - if(spells[spell_id].limit_value[i] == 0) - AddDefensiveProc(procid, 100,spell_id); - else - AddDefensiveProc(procid, spells[spell_id].limit_value[i]+100,spell_id); - break; - - break; - } - case SE_BardAEDot: { #ifdef SPELL_EFFECT_SPAM @@ -3278,6 +3265,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_Worn_Endurance_Regen_Cap: case SE_Buy_AA_Rank: case SE_Ff_FocusTimerMin: + case SE_Proc_Timer_Modifier: { break; } @@ -8634,7 +8622,7 @@ void Mob::SpreadVirusEffect(int32 spell_id, uint32 caster_id, int32 buff_tics_re bool Mob::IsFocusProcLimitTimerActive(int32 focus_spell_id) { /* - Used with SPA SE_Ff_FocusTimerMin to limit how often a focus effect can be applied. + Used with SPA 511 SE_Ff_FocusTimerMin to limit how often a focus effect can be applied. Ie. Can only have a spell trigger once every 15 seconds, or to be more creative can only have the fire spells received a very high special focused once every 30 seconds. Note, this stores timers for both spell, item and AA related focuses For AA the focus_spell_id @@ -8673,3 +8661,110 @@ void Mob::SetFocusProcLimitTimer(int32 focus_spell_id, uint32 focus_reuse_time) } } } + +bool Mob::IsProcLimitTimerActive(int32 base_spell_id, uint32 proc_reuse_time, int proc_type) { + /* + Used with SPA 512 SE_Proc_Timer_Modifier to limit how often a proc can be cast. + If this effect exists it will prevent the next proc from firing until the timer + defined in SPA 512 is finished. Ie. 1 proc every 55 seconds. + Spell, Ranged, and Defensive procs all have their own timer array, therefore + you can stack multiple different types of effects in the same spell. Make sure + SPA 512 goes directly after each proc you want to have the timer. + */ + if (!proc_reuse_time) { + return false; + } + + for (int i = 0; i < MAX_PROC_LIMIT_TIMERS; i++) { + + if (proc_type == SE_WeaponProc) { + if (spell_proclimit_spellid[i] == base_spell_id) { + if (spell_proclimit_timer[i].Enabled()) { + if (spell_proclimit_timer[i].GetRemainingTime() > 0) { + return true; + } + else { + spell_proclimit_timer[i].Disable(); + spell_proclimit_spellid[i] = 0; + } + } + } + } + else if (proc_type == SE_RangedProc) { + if (ranged_proclimit_spellid[i] == base_spell_id) { + if (ranged_proclimit_timer[i].Enabled()) { + if (ranged_proclimit_timer[i].GetRemainingTime() > 0) { + return true; + } + else { + ranged_proclimit_timer[i].Disable(); + ranged_proclimit_spellid[i] = 0; + } + } + } + } + else if (proc_type == SE_DefensiveProc) { + if (def_proclimit_spellid[i] == base_spell_id) { + if (def_proclimit_timer[i].Enabled()) { + if (def_proclimit_timer[i].GetRemainingTime() > 0) { + return true; + } + else { + def_proclimit_timer[i].Disable(); + def_proclimit_spellid[i] = 0; + } + } + } + } + } + return false; +} + +void Mob::SetProcLimitTimer(int32 base_spell_id, uint32 proc_reuse_time, int proc_type) { + + if (!proc_reuse_time) { + return; + } + + bool is_set = false; + + for (int i = 0; i < MAX_PROC_LIMIT_TIMERS; i++) { + + if (proc_type == SE_WeaponProc) { + if (!spell_proclimit_spellid[i] && !is_set) { + spell_proclimit_spellid[i] = base_spell_id; + spell_proclimit_timer[i].SetTimer(proc_reuse_time); + is_set = true; + } + else if (spell_proclimit_spellid[i] > 0 && !FindBuff(base_spell_id)) { + spell_proclimit_spellid[i] = 0; + spell_proclimit_timer[i].Disable(); + } + } + + if (proc_type == SE_RangedProc) { + if (!ranged_proclimit_spellid[i] && !is_set) { + ranged_proclimit_spellid[i] = base_spell_id; + ranged_proclimit_timer[i].SetTimer(proc_reuse_time); + is_set = true; + } + else if (ranged_proclimit_spellid[i] > 0 && !FindBuff(base_spell_id)) { + ranged_proclimit_spellid[i] = 0; + ranged_proclimit_timer[i].Disable(); + } + } + + if (proc_type == SE_DefensiveProc) { + if (!def_proclimit_spellid[i] && !is_set) { + def_proclimit_spellid[i] = base_spell_id; + def_proclimit_timer[i].SetTimer(proc_reuse_time); + is_set = true; + } + else if (def_proclimit_spellid[i] > 0 && !FindBuff(base_spell_id)) { + def_proclimit_spellid[i] = 0; + def_proclimit_timer[i].Disable(); + } + } + } +} + diff --git a/zone/spells.cpp b/zone/spells.cpp index 6cf9837c9..76769da93 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -5665,7 +5665,7 @@ bool Mob::IsCombatProc(uint16 spell_id) { return false; } -bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 base_spell_id, int level_override) { +bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 base_spell_id, int level_override, uint32 proc_reuse_time) { if(spell_id == SPELL_UNKNOWN) return(false); @@ -5677,8 +5677,8 @@ bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 b PermaProcs[i].chance = iChance; PermaProcs[i].base_spellID = base_spell_id; PermaProcs[i].level_override = level_override; + PermaProcs[i].proc_reuse_time = proc_reuse_time; LogSpells("Added permanent proc spell [{}] with chance [{}] to slot [{}]", spell_id, iChance, i); - return true; } } @@ -5692,6 +5692,7 @@ bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 b SpellProcs[i].spellID = spell_id; SpellProcs[i].chance = iChance; SpellProcs[i].level_override = level_override; + SpellProcs[i].proc_reuse_time = proc_reuse_time; Log(Logs::Detail, Logs::Spells, "Replaced poison-granted proc spell %d with chance %d to slot %d", spell_id, iChance, i); return true; } @@ -5708,6 +5709,7 @@ bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 b SpellProcs[i].chance = iChance; SpellProcs[i].base_spellID = base_spell_id;; SpellProcs[i].level_override = level_override; + SpellProcs[i].proc_reuse_time = proc_reuse_time; LogSpells("Added [{}]-granted proc spell [{}] with chance [{}] to slot [{}]", (base_spell_id == POISON_PROC) ? "poison" : "spell", spell_id, iChance, i); return true; } @@ -5724,13 +5726,14 @@ bool Mob::RemoveProcFromWeapon(uint16 spell_id, bool bAll) { SpellProcs[i].chance = 0; SpellProcs[i].base_spellID = SPELL_UNKNOWN; SpellProcs[i].level_override = -1; + SpellProcs[i].proc_reuse_time = 0; LogSpells("Removed proc [{}] from slot [{}]", spell_id, i); } } return true; } -bool Mob::AddDefensiveProc(uint16 spell_id, uint16 iChance, uint16 base_spell_id) +bool Mob::AddDefensiveProc(uint16 spell_id, uint16 iChance, uint16 base_spell_id, uint32 proc_reuse_time) { if(spell_id == SPELL_UNKNOWN) return(false); @@ -5741,6 +5744,7 @@ bool Mob::AddDefensiveProc(uint16 spell_id, uint16 iChance, uint16 base_spell_id DefensiveProcs[i].spellID = spell_id; DefensiveProcs[i].chance = iChance; DefensiveProcs[i].base_spellID = base_spell_id; + DefensiveProcs[i].proc_reuse_time = proc_reuse_time; LogSpells("Added spell-granted defensive proc spell [{}] with chance [{}] to slot [{}]", spell_id, iChance, i); return true; } @@ -5756,13 +5760,14 @@ bool Mob::RemoveDefensiveProc(uint16 spell_id, bool bAll) DefensiveProcs[i].spellID = SPELL_UNKNOWN; DefensiveProcs[i].chance = 0; DefensiveProcs[i].base_spellID = SPELL_UNKNOWN; + DefensiveProcs[i].proc_reuse_time = 0; LogSpells("Removed defensive proc [{}] from slot [{}]", spell_id, i); } } return true; } -bool Mob::AddRangedProc(uint16 spell_id, uint16 iChance, uint16 base_spell_id) +bool Mob::AddRangedProc(uint16 spell_id, uint16 iChance, uint16 base_spell_id, uint32 proc_reuse_time) { if(spell_id == SPELL_UNKNOWN) return(false); @@ -5773,6 +5778,7 @@ bool Mob::AddRangedProc(uint16 spell_id, uint16 iChance, uint16 base_spell_id) RangedProcs[i].spellID = spell_id; RangedProcs[i].chance = iChance; RangedProcs[i].base_spellID = base_spell_id; + RangedProcs[i].proc_reuse_time = proc_reuse_time; LogSpells("Added spell-granted ranged proc spell [{}] with chance [{}] to slot [{}]", spell_id, iChance, i); return true; } @@ -5787,7 +5793,8 @@ bool Mob::RemoveRangedProc(uint16 spell_id, bool bAll) if (bAll || RangedProcs[i].spellID == spell_id) { RangedProcs[i].spellID = SPELL_UNKNOWN; RangedProcs[i].chance = 0; - RangedProcs[i].base_spellID = SPELL_UNKNOWN;; + RangedProcs[i].base_spellID = SPELL_UNKNOWN; + RangedProcs[i].proc_reuse_time = 0; LogSpells("Removed ranged proc [{}] from slot [{}]", spell_id, i); } } From b4aa401210561b3fcf86d5c152af6e5fede283a0 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 6 Nov 2021 17:35:37 -0400 Subject: [PATCH 322/624] [Commands] Add #findtask [search criteria] Command. (#1675) * [Commands] Add #findtask [search criteria] Command. - Allows you to search for Tasks by ID or partial name. * Update command.cpp --- zone/command.cpp | 85 ++++++++++++++++++++++++++++++++++++++++++++++++ zone/command.h | 1 + 2 files changed, 86 insertions(+) diff --git a/zone/command.cpp b/zone/command.cpp index aea1e96fc..5f22ab2fa 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -222,6 +222,7 @@ int command_init(void) command_add("findnpctype", "[search criteria] - Search database NPC types", 100, command_findnpctype) || command_add("findrace", "[search criteria] - Search for a race", 50, command_findrace) || command_add("findspell", "[search criteria] - Search for a spell", 50, command_findspell) || + command_add("findtask", "[search criteria] - Search for a task", 50, command_findtask) || command_add("findzone", "[search criteria] - Search database zones", 100, command_findzone) || command_add("fixmob", "[race|gender|texture|helm|face|hair|haircolor|beard|beardcolor|heritage|tattoo|detail] [next|prev] - Manipulate appearance of your target", 80, command_fixmob) || command_add("flag", "[status] [acctname] - Refresh your admin status, or set an account's admin status if arguments provided", 0, command_flag) || @@ -14889,6 +14890,90 @@ void command_dye(Client *c, const Seperator *sep) c->DyeArmorBySlot(slot, red, green, blue, use_tint); } +void command_findtask(Client *c, const Seperator *sep) +{ + if (RuleB(TaskSystem, EnableTaskSystem)) { + int arguments = sep->argnum; + + if (arguments == 0) { + c->Message(Chat::White, "Command Syntax: #findtask [search criteria]"); + return; + } + + if (sep->IsNumber(1)) { + auto task_id = atoul(sep->argplus[1]); + auto task_name = task_manager->GetTaskName(task_id); + auto task_message = ( + !task_name.empty() ? + fmt::format( + "{}: {}", + task_id, + task_name + ).c_str() : + fmt::format( + "Task ID {} was not found.", + task_id + ).c_str() + ); + + c->Message( + Chat::White, + task_message + ); + } else { + std::string search_criteria = str_tolower(sep->argplus[1]); + if (!search_criteria.empty()) { + int found_count = 0; + for (uint32 task_id = 1; task_id <= MAXTASKS; task_id++) { + auto task_name = task_manager->GetTaskName(task_id); + std::string task_name_lower = str_tolower(task_name); + if (task_name_lower.find(search_criteria) == std::string::npos) { + continue; + } + + c->Message( + Chat::White, + fmt::format( + "{}: {}", + task_id, + task_name + ).c_str() + ); + found_count++; + + if (found_count == 20) { + break; + } + } + + if (found_count == 20) { + c->Message(Chat::White, "20 Tasks were found, max reached."); + } else { + auto task_message = ( + found_count > 0 ? + ( + found_count == 1 ? + "A Task was" : + fmt::format("{} Tasks were", found_count) + ) : + "No Tasks were" + ); + + c->Message( + Chat::White, + fmt::format( + "{} found.", + task_message + ).c_str() + ); + } + } + } + } else { + c->Message(Chat::White, "This command cannot be used while the Task system is disabled."); + } +} + // All new code added to command.cpp should be BEFORE this comment line. Do no append code to this file below the BOTS code block. #ifdef BOTS #include "bot_command.h" diff --git a/zone/command.h b/zone/command.h index f2e8922d1..7bac28308 100644 --- a/zone/command.h +++ b/zone/command.h @@ -110,6 +110,7 @@ void command_findclass(Client *c, const Seperator *sep); void command_findnpctype(Client *c, const Seperator *sep); void command_findrace(Client *c, const Seperator *sep); void command_findspell(Client *c, const Seperator *sep); +void command_findtask(Client *c, const Seperator *sep); void command_findzone(Client *c, const Seperator *sep); void command_fixmob(Client *c, const Seperator *sep); void command_flag(Client *c, const Seperator *sep); From 8d8301fbd7a844c25aab9ea8f9a7dc52266e5101 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 6 Nov 2021 17:35:43 -0400 Subject: [PATCH 323/624] [Commands] Add #findskill [search criteria] Command. (#1674) * [Commands] Add #findskill [search criteria] Command. - Allows you to search for skills by ID or partial name. * Add error message. * Update command.cpp * Update command.cpp * Update command.cpp --- zone/command.cpp | 86 ++++++++++++++++++++++++++++++++++++++++++++++++ zone/command.h | 1 + 2 files changed, 87 insertions(+) diff --git a/zone/command.cpp b/zone/command.cpp index 5f22ab2fa..cb084020f 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -221,6 +221,7 @@ int command_init(void) command_add("findclass", "[search criteria] - Search for a class", 50, command_findclass) || command_add("findnpctype", "[search criteria] - Search database NPC types", 100, command_findnpctype) || command_add("findrace", "[search criteria] - Search for a race", 50, command_findrace) || + command_add("findskill", "[search criteria] - Search for a skill", 50, command_findskill) || command_add("findspell", "[search criteria] - Search for a spell", 50, command_findspell) || command_add("findtask", "[search criteria] - Search for a task", 50, command_findtask) || command_add("findzone", "[search criteria] - Search database zones", 100, command_findzone) || @@ -2882,6 +2883,91 @@ void command_findrace(Client *c, const Seperator *sep) } } +void command_findskill(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + + if (arguments == 0) { + c->Message(Chat::White, "Command Syntax: #findskill [search criteria]"); + return; + } + + std::map skills = EQ::skills::GetSkillTypeMap(); + if (sep->IsNumber(1)) { + int skill_id = atoi(sep->argplus[1]); + if (skill_id >= EQ::skills::Skill1HBlunt && skill_id < EQ::skills::SkillCount) { + for (auto skills_iter : skills) { + if (skill_id == skills_iter.first) { + c->Message( + Chat::White, + fmt::format( + "{}: {}", + skills_iter.first, + skills_iter.second + ).c_str() + ); + break; + } + } + } else { + c->Message( + Chat::White, + fmt::format( + "Skill ID {} was not found.", + skill_id + ).c_str() + ); + } + } else { + std::string search_criteria = str_tolower(sep->argplus[1]); + if (!search_criteria.empty()) { + int found_count = 0; + for (auto skills_iter : skills) { + std::string skill_name_lower = str_tolower(skills_iter.second); + if (skill_name_lower.find(search_criteria) == std::string::npos) { + continue; + } + + c->Message( + Chat::White, + fmt::format( + "{}: {}", + skills_iter.first, + skills_iter.second + ).c_str() + ); + found_count++; + + if (found_count == 20) { + break; + } + } + + if (found_count == 20) { + c->Message(Chat::White, "20 Skills were found, max reached."); + } else { + auto skill_message = ( + found_count > 0 ? + ( + found_count == 1 ? + "A skill was" : + fmt::format("{} skills were", found_count) + ) : + "No skills were" + ); + + c->Message( + Chat::White, + fmt::format( + "{} found.", + skill_message + ).c_str() + ); + } + } + } +} + void command_findspell(Client *c, const Seperator *sep) { if (sep->arg[1][0] == 0) { diff --git a/zone/command.h b/zone/command.h index 7bac28308..dec5cd076 100644 --- a/zone/command.h +++ b/zone/command.h @@ -109,6 +109,7 @@ void command_findaliases(Client *c, const Seperator *sep); void command_findclass(Client *c, const Seperator *sep); void command_findnpctype(Client *c, const Seperator *sep); void command_findrace(Client *c, const Seperator *sep); +void command_findskill(Client *c, const Seperator *sep); void command_findspell(Client *c, const Seperator *sep); void command_findtask(Client *c, const Seperator *sep); void command_findzone(Client *c, const Seperator *sep); From 7b6decaef3ca658ebb9f8970f2296559db21e512 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 6 Nov 2021 17:36:00 -0400 Subject: [PATCH 324/624] [Quest API] Alphabetize Lua method exports. (#1673) - Keeps things tidier. - Removes unnecessary/outdated comments at the top of files. --- zone/lua_client.cpp | 770 ++++++++++++------------ zone/lua_corpse.cpp | 81 +-- zone/lua_door.cpp | 70 +-- zone/lua_entity.cpp | 48 +- zone/lua_entity_list.cpp | 154 ++--- zone/lua_expedition.cpp | 118 ++-- zone/lua_group.cpp | 46 +- zone/lua_hate_entry.cpp | 14 +- zone/lua_hate_list.cpp | 15 +- zone/lua_inventory.cpp | 60 +- zone/lua_item.cpp | 358 ++++++------ zone/lua_iteminst.cpp | 100 ++-- zone/lua_mob.cpp | 924 ++++++++++++++--------------- zone/lua_npc.cpp | 248 ++++---- zone/lua_object.cpp | 74 +-- zone/lua_packet.cpp | 1166 ++++++++++++++++++------------------- zone/lua_raid.cpp | 50 +- zone/lua_spawn.cpp | 56 +- zone/lua_spell.cpp | 176 +++--- zone/lua_stat_bonuses.cpp | 514 ++++++++-------- 20 files changed, 2506 insertions(+), 2536 deletions(-) diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 002004274..a7459cff3 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -2313,391 +2313,391 @@ int Lua_Client::GetNextAvailableDisciplineSlot(int starting_slot) { luabind::scope lua_register_client() { return luabind::class_("Client") - .def(luabind::constructor<>()) - .def("SendSound", (void(Lua_Client::*)(void))&Lua_Client::SendSound) - .def("Save", (void(Lua_Client::*)(void))&Lua_Client::Save) - .def("Save", (void(Lua_Client::*)(int))&Lua_Client::Save) - .def("SaveBackup", (void(Lua_Client::*)(void))&Lua_Client::SaveBackup) - .def("Connected", (bool(Lua_Client::*)(void))&Lua_Client::Connected) - .def("InZone", (bool(Lua_Client::*)(void))&Lua_Client::InZone) - .def("Kick", (void(Lua_Client::*)(void))&Lua_Client::Kick) - .def("Disconnect", (void(Lua_Client::*)(void))&Lua_Client::Disconnect) - .def("IsLD", (bool(Lua_Client::*)(void))&Lua_Client::IsLD) - .def("WorldKick", (void(Lua_Client::*)(void))&Lua_Client::WorldKick) - .def("SendToGuildHall", (void(Lua_Client::*)(void))&Lua_Client::SendToGuildHall) - .def("GetAFK", (int(Lua_Client::*)(void))&Lua_Client::GetAFK) - .def("SetAFK", (void(Lua_Client::*)(uint8))&Lua_Client::SetAFK) - .def("GetAnon", (int(Lua_Client::*)(void))&Lua_Client::GetAnon) - .def("SetAnon", (void(Lua_Client::*)(uint8))&Lua_Client::SetAnon) - .def("Sit", (void(Lua_Client::*)(void))&Lua_Client::Sit) - .def("Duck", (void(Lua_Client::*)(void))&Lua_Client::Duck) - .def("DyeArmorBySlot", (void(Lua_Client::*)(uint8,uint8,uint8,uint8))&Lua_Client::DyeArmorBySlot) - .def("DyeArmorBySlot", (void(Lua_Client::*)(uint8,uint8,uint8,uint8,uint8))&Lua_Client::DyeArmorBySlot) - .def("Stand", (void(Lua_Client::*)(void))&Lua_Client::Stand) - .def("SetGM", (void(Lua_Client::*)(bool))&Lua_Client::SetGM) - .def("SetPVP", (void(Lua_Client::*)(bool))&Lua_Client::SetPVP) - .def("GetPVP", (bool(Lua_Client::*)(void))&Lua_Client::GetPVP) - .def("GetGM", (bool(Lua_Client::*)(void))&Lua_Client::GetGM) - .def("SetBaseClass", (void(Lua_Client::*)(int))&Lua_Client::SetBaseClass) - .def("SetBaseRace", (void(Lua_Client::*)(int))&Lua_Client::SetBaseRace) - .def("SetBaseGender", (void(Lua_Client::*)(int))&Lua_Client::SetBaseGender) - .def("GetClassBitmask", (int(Lua_Client::*)(void))&Lua_Client::GetClassBitmask) - .def("GetRaceBitmask", (int(Lua_Client::*)(void))&Lua_Client::GetRaceBitmask) - .def("GetBaseFace", (int(Lua_Client::*)(void))&Lua_Client::GetBaseFace) - .def("GetLanguageSkill", (int(Lua_Client::*)(int))&Lua_Client::GetLanguageSkill) - .def("GetLDoNPointsTheme", (int(Lua_Client::*)(int))&Lua_Client::GetLDoNPointsTheme) - .def("GetBaseSTR", (int(Lua_Client::*)(void))&Lua_Client::GetBaseSTR) - .def("GetBaseSTA", (int(Lua_Client::*)(void))&Lua_Client::GetBaseSTA) - .def("GetBaseCHA", (int(Lua_Client::*)(void))&Lua_Client::GetBaseCHA) - .def("GetBaseDEX", (int(Lua_Client::*)(void))&Lua_Client::GetBaseDEX) - .def("GetBaseINT", (int(Lua_Client::*)(void))&Lua_Client::GetBaseINT) - .def("GetBaseAGI", (int(Lua_Client::*)(void))&Lua_Client::GetBaseAGI) - .def("GetBaseWIS", (int(Lua_Client::*)(void))&Lua_Client::GetBaseWIS) - .def("GetWeight", (int(Lua_Client::*)(void))&Lua_Client::GetWeight) - .def("GetEXP", (uint32(Lua_Client::*)(void))&Lua_Client::GetEXP) - .def("GetAAExp", (uint32(Lua_Client::*)(void))&Lua_Client::GetAAExp) - .def("GetAAPercent", (uint32(Lua_Client::*)(void))&Lua_Client::GetAAPercent) - .def("GetTotalSecondsPlayed", (uint32(Lua_Client::*)(void))&Lua_Client::GetTotalSecondsPlayed) - .def("UpdateLDoNPoints", (void(Lua_Client::*)(uint32,int))&Lua_Client::UpdateLDoNPoints) - .def("SetDeity", (void(Lua_Client::*)(int))&Lua_Client::SetDeity) - .def("AddEXP", (void(Lua_Client::*)(uint32))&Lua_Client::AddEXP) - .def("AddEXP", (void(Lua_Client::*)(uint32,int))&Lua_Client::AddEXP) - .def("AddEXP", (void(Lua_Client::*)(uint32,int,bool))&Lua_Client::AddEXP) - .def("SetEXP", (void(Lua_Client::*)(uint32,uint32))&Lua_Client::SetEXP) - .def("SetEXP", (void(Lua_Client::*)(uint32,uint32,bool))&Lua_Client::SetEXP) - .def("SetBindPoint", (void(Lua_Client::*)(void))&Lua_Client::SetBindPoint) - .def("SetBindPoint", (void(Lua_Client::*)(int))&Lua_Client::SetBindPoint) - .def("SetBindPoint", (void(Lua_Client::*)(int,int))&Lua_Client::SetBindPoint) - .def("SetBindPoint", (void(Lua_Client::*)(int,int,float))&Lua_Client::SetBindPoint) - .def("SetBindPoint", (void(Lua_Client::*)(int,int,float,float))&Lua_Client::SetBindPoint) - .def("SetBindPoint", (void(Lua_Client::*)(int,int,float,float,float))&Lua_Client::SetBindPoint) - .def("SetBindPoint", (void(Lua_Client::*)(int,int,float,float,float,float))&Lua_Client::SetBindPoint) - .def("GetBindX", (float(Lua_Client::*)(void))&Lua_Client::GetBindX) - .def("GetBindX", (float(Lua_Client::*)(int))&Lua_Client::GetBindX) - .def("GetBindY", (float(Lua_Client::*)(void))&Lua_Client::GetBindY) - .def("GetBindY", (float(Lua_Client::*)(int))&Lua_Client::GetBindY) - .def("GetBindZ", (float(Lua_Client::*)(void))&Lua_Client::GetBindZ) - .def("GetBindZ", (float(Lua_Client::*)(int))&Lua_Client::GetBindZ) - .def("GetBindHeading", (float(Lua_Client::*)(void))&Lua_Client::GetBindHeading) - .def("GetBindHeading", (float(Lua_Client::*)(int))&Lua_Client::GetBindHeading) - .def("GetBindZoneID", (uint32(Lua_Client::*)(void))&Lua_Client::GetBindZoneID) - .def("GetBindZoneID", (uint32(Lua_Client::*)(int))&Lua_Client::GetBindZoneID) - .def("GetTargetRingX", (float(Lua_Client::*)(void))&Lua_Client::GetTargetRingX) - .def("GetTargetRingY", (float(Lua_Client::*)(void))&Lua_Client::GetTargetRingY) - .def("GetTargetRingZ", (float(Lua_Client::*)(void))&Lua_Client::GetTargetRingZ) - .def("SetPrimaryWeaponOrnamentation", (void(Lua_Client::*)(uint32))&Lua_Client::SetPrimaryWeaponOrnamentation) - .def("SetSecondaryWeaponOrnamentation", (void(Lua_Client::*)(uint32))&Lua_Client::SetSecondaryWeaponOrnamentation) - .def("MovePC", (void(Lua_Client::*)(int,float,float,float,float))&Lua_Client::MovePC) - .def("MovePCInstance", (void(Lua_Client::*)(int,int,float,float,float,float))&Lua_Client::MovePCInstance) - .def("MoveZone", (void(Lua_Client::*)(const char*))&Lua_Client::MoveZone) - .def("MoveZoneGroup", (void(Lua_Client::*)(const char*))&Lua_Client::MoveZoneGroup) - .def("MoveZoneRaid", (void(Lua_Client::*)(const char*))&Lua_Client::MoveZoneRaid) - .def("MoveZoneInstance", (void(Lua_Client::*)(uint16))&Lua_Client::MoveZoneInstance) - .def("MoveZoneInstanceGroup", (void(Lua_Client::*)(uint16))&Lua_Client::MoveZoneInstanceGroup) - .def("MoveZoneInstanceRaid", (void(Lua_Client::*)(uint16))&Lua_Client::MoveZoneInstanceRaid) - .def("ChangeLastName", (void(Lua_Client::*)(const char *in))&Lua_Client::ChangeLastName) - .def("GetFactionLevel", (int(Lua_Client::*)(uint32,uint32,uint32,uint32,uint32,uint32,Lua_NPC))&Lua_Client::GetFactionLevel) - .def("SetFactionLevel", (void(Lua_Client::*)(uint32,uint32,int,int,int))&Lua_Client::SetFactionLevel) - .def("SetFactionLevel2", (void(Lua_Client::*)(uint32,int,int,int,int,int,int))&Lua_Client::SetFactionLevel2) - .def("GetRawItemAC", (int(Lua_Client::*)(void))&Lua_Client::GetRawItemAC) - .def("AccountID", (uint32(Lua_Client::*)(void))&Lua_Client::AccountID) - .def("AccountName", (const char *(Lua_Client::*)(void))&Lua_Client::AccountName) - .def("GetAccountAge", (int(Lua_Client::*)(void))&Lua_Client::GetAccountAge) - .def("Admin", (int(Lua_Client::*)(void))&Lua_Client::Admin) - .def("CharacterID", (uint32(Lua_Client::*)(void))&Lua_Client::CharacterID) - .def("GuildRank", (int(Lua_Client::*)(void))&Lua_Client::GuildRank) - .def("GuildID", (uint32(Lua_Client::*)(void))&Lua_Client::GuildID) - .def("GetFace", (int(Lua_Client::*)(void))&Lua_Client::GetFace) - .def("TakeMoneyFromPP", (bool(Lua_Client::*)(uint64))&Lua_Client::TakeMoneyFromPP) - .def("TakeMoneyFromPP", (bool(Lua_Client::*)(uint64,bool))&Lua_Client::TakeMoneyFromPP) - .def("AddMoneyToPP", (void(Lua_Client::*)(uint32,uint32,uint32,uint32,bool))&Lua_Client::AddMoneyToPP) - .def("TGB", (bool(Lua_Client::*)(void))&Lua_Client::TGB) - .def("GetSkillPoints", (int(Lua_Client::*)(void))&Lua_Client::GetSkillPoints) - .def("SetSkillPoints", (void(Lua_Client::*)(int))&Lua_Client::SetSkillPoints) - .def("IncreaseSkill", (void(Lua_Client::*)(int))&Lua_Client::IncreaseSkill) - .def("IncreaseSkill", (void(Lua_Client::*)(int,int))&Lua_Client::IncreaseSkill) - .def("IncreaseLanguageSkill", (void(Lua_Client::*)(int))&Lua_Client::IncreaseLanguageSkill) - .def("IncreaseLanguageSkill", (void(Lua_Client::*)(int,int))&Lua_Client::IncreaseLanguageSkill) - .def("GetRawSkill", (int(Lua_Client::*)(int))&Lua_Client::GetRawSkill) - .def("HasSkill", (bool(Lua_Client::*)(int))&Lua_Client::HasSkill) - .def("CanHaveSkill", (bool(Lua_Client::*)(int))&Lua_Client::CanHaveSkill) - .def("SetSkill", (void(Lua_Client::*)(int,int))&Lua_Client::SetSkill) - .def("AddSkill", (void(Lua_Client::*)(int,int))&Lua_Client::AddSkill) - .def("CheckSpecializeIncrease", (void(Lua_Client::*)(int))&Lua_Client::CheckSpecializeIncrease) - .def("CheckIncreaseSkill", (void(Lua_Client::*)(int,Lua_Mob))&Lua_Client::CheckIncreaseSkill) - .def("CheckIncreaseSkill", (void(Lua_Client::*)(int,Lua_Mob,int))&Lua_Client::CheckIncreaseSkill) - .def("SetLanguageSkill", (void(Lua_Client::*)(int,int))&Lua_Client::SetLanguageSkill) - .def("MaxSkill", (int(Lua_Client::*)(int))&Lua_Client::MaxSkill) - .def("IsMedding", (bool(Lua_Client::*)(void))&Lua_Client::IsMedding) - .def("GetDuelTarget", (int(Lua_Client::*)(void))&Lua_Client::GetDuelTarget) - .def("IsDueling", (bool(Lua_Client::*)(void))&Lua_Client::IsDueling) - .def("SetDuelTarget", (void(Lua_Client::*)(int))&Lua_Client::SetDuelTarget) - .def("SetDueling", (void(Lua_Client::*)(bool))&Lua_Client::SetDueling) - .def("ResetAA", (void(Lua_Client::*)(void))&Lua_Client::ResetAA) - .def("MemSpell", (void(Lua_Client::*)(int,int))&Lua_Client::MemSpell) - .def("MemSpell", (void(Lua_Client::*)(int,int,bool))&Lua_Client::MemSpell) - .def("UnmemSpell", (void(Lua_Client::*)(int))&Lua_Client::UnmemSpell) - .def("UnmemSpell", (void(Lua_Client::*)(int,bool))&Lua_Client::UnmemSpell) - .def("UnmemSpellBySpellID", (void(Lua_Client::*)(int32))&Lua_Client::UnmemSpellBySpellID) - .def("UnmemSpellAll", (void(Lua_Client::*)(void))&Lua_Client::UnmemSpellAll) - .def("UnmemSpellAll", (void(Lua_Client::*)(bool))&Lua_Client::UnmemSpellAll) - .def("FindMemmedSpellBySlot", (uint16(Lua_Client::*)(int))&Lua_Client::FindMemmedSpellBySlot) - .def("MemmedCount", (int(Lua_Client::*)(void))&Lua_Client::MemmedCount) - .def("GetLearnableDisciplines", (luabind::object(Lua_Client::*)(lua_State* L))&Lua_Client::GetLearnableDisciplines) - .def("GetLearnableDisciplines", (luabind::object(Lua_Client::*)(lua_State* L,uint8))&Lua_Client::GetLearnableDisciplines) - .def("GetLearnableDisciplines", (luabind::object(Lua_Client::*)(lua_State* L,uint8,uint8))&Lua_Client::GetLearnableDisciplines) - .def("GetLearnedDisciplines", (luabind::object(Lua_Client::*)(lua_State* L))&Lua_Client::GetLearnedDisciplines) - .def("GetMemmedSpells", (luabind::object(Lua_Client::*)(lua_State* L))&Lua_Client::GetMemmedSpells) - .def("GetScribedSpells", (luabind::object(Lua_Client::*)(lua_State* L))&Lua_Client::GetScribedSpells) - .def("GetScribeableSpells", (luabind::object(Lua_Client::*)(lua_State* L))&Lua_Client::GetScribeableSpells) - .def("GetScribeableSpells", (luabind::object(Lua_Client::*)(lua_State* L,uint8))&Lua_Client::GetScribeableSpells) - .def("GetScribeableSpells", (luabind::object(Lua_Client::*)(lua_State* L,uint8,uint8))&Lua_Client::GetScribeableSpells) - .def("ScribeSpell", (void(Lua_Client::*)(int,int))&Lua_Client::ScribeSpell) - .def("ScribeSpell", (void(Lua_Client::*)(int,int,bool))&Lua_Client::ScribeSpell) - .def("UnscribeSpell", (void(Lua_Client::*)(int))&Lua_Client::UnscribeSpell) - .def("UnscribeSpell", (void(Lua_Client::*)(int,bool))&Lua_Client::UnscribeSpell) - .def("UnscribeSpellAll", (void(Lua_Client::*)(void))&Lua_Client::UnscribeSpellAll) - .def("UnscribeSpellAll", (void(Lua_Client::*)(bool))&Lua_Client::UnscribeSpellAll) - .def("TrainDisc", (void(Lua_Client::*)(int))&Lua_Client::TrainDisc) - .def("TrainDiscBySpellID", (void(Lua_Client::*)(int32))&Lua_Client::TrainDiscBySpellID) - .def("GetDiscSlotBySpellID", (int(Lua_Client::*)(int32))&Lua_Client::GetDiscSlotBySpellID) - .def("UntrainDisc", (void(Lua_Client::*)(int))&Lua_Client::UntrainDisc) - .def("UntrainDisc", (void(Lua_Client::*)(int,bool))&Lua_Client::UntrainDisc) - .def("UntrainDiscAll", (void(Lua_Client::*)(void))&Lua_Client::UntrainDiscAll) - .def("UntrainDiscAll", (void(Lua_Client::*)(bool))&Lua_Client::UntrainDiscAll) - .def("IsStanding", (bool(Lua_Client::*)(void))&Lua_Client::IsStanding) - .def("IsSitting", (bool(Lua_Client::*)(void))&Lua_Client::IsSitting) - .def("IsCrouching", (bool(Lua_Client::*)(void))&Lua_Client::IsCrouching) - .def("SetFeigned", (void(Lua_Client::*)(bool))&Lua_Client::SetFeigned) - .def("GetFeigned", (bool(Lua_Client::*)(void))&Lua_Client::GetFeigned) - .def("AutoSplitEnabled", (bool(Lua_Client::*)(void))&Lua_Client::AutoSplitEnabled) - .def("SetHorseId", (void(Lua_Client::*)(int))&Lua_Client::SetHorseId) - .def("GetHorseId", (int(Lua_Client::*)(void))&Lua_Client::GetHorseId) - .def("NukeItem", (void(Lua_Client::*)(uint32))&Lua_Client::NukeItem) - .def("NukeItem", (void(Lua_Client::*)(uint32,int))&Lua_Client::NukeItem) - .def("SetTint", (void(Lua_Client::*)(int,uint32))&Lua_Client::SetTint) - .def("SetMaterial", (void(Lua_Client::*)(int,uint32))&Lua_Client::SetMaterial) - .def("Undye", (void(Lua_Client::*)(void))&Lua_Client::Undye) - .def("GetItemIDAt", (int(Lua_Client::*)(int))&Lua_Client::GetItemIDAt) - .def("GetAugmentIDAt", (int(Lua_Client::*)(int,int))&Lua_Client::GetAugmentIDAt) - .def("DeleteItemInInventory", (void(Lua_Client::*)(int,int))&Lua_Client::DeleteItemInInventory) - .def("DeleteItemInInventory", (void(Lua_Client::*)(int,int,bool))&Lua_Client::DeleteItemInInventory) - .def("SummonItem", (void(Lua_Client::*)(uint32))&Lua_Client::SummonItem) - .def("SummonItem", (void(Lua_Client::*)(uint32,int))&Lua_Client::SummonItem) - .def("SummonItem", (void(Lua_Client::*)(uint32,int,uint32))&Lua_Client::SummonItem) - .def("SummonItem", (void(Lua_Client::*)(uint32,int,uint32,uint32))&Lua_Client::SummonItem) - .def("SummonItem", (void(Lua_Client::*)(uint32,int,uint32,uint32,uint32))&Lua_Client::SummonItem) - .def("SummonItem", (void(Lua_Client::*)(uint32,int,uint32,uint32,uint32,uint32))&Lua_Client::SummonItem) - .def("SummonItem", (void(Lua_Client::*)(uint32,int,uint32,uint32,uint32,uint32,uint32))&Lua_Client::SummonItem) - .def("SummonItem", (void(Lua_Client::*)(uint32,int,uint32,uint32,uint32,uint32,uint32,bool))&Lua_Client::SummonItem) - .def("SummonItem", (void(Lua_Client::*)(uint32,int,uint32,uint32,uint32,uint32,uint32,bool,int))&Lua_Client::SummonItem) - .def("SetStats", (void(Lua_Client::*)(int,int))&Lua_Client::SetStats) - .def("IncStats", (void(Lua_Client::*)(int,int))&Lua_Client::IncStats) - .def("DropItem", (void(Lua_Client::*)(int))&Lua_Client::DropItem) - .def("BreakInvis", (void(Lua_Client::*)(void))&Lua_Client::BreakInvis) - .def("LeaveGroup", (void(Lua_Client::*)(void))&Lua_Client::LeaveGroup) - .def("IsGrouped", (bool(Lua_Client::*)(void))&Lua_Client::IsGrouped) - .def("IsRaidGrouped", (bool(Lua_Client::*)(void))&Lua_Client::IsRaidGrouped) - .def("Hungry", (bool(Lua_Client::*)(void))&Lua_Client::Hungry) - .def("Thirsty", (bool(Lua_Client::*)(void))&Lua_Client::Thirsty) - .def("GetInstrumentMod", (int(Lua_Client::*)(int))&Lua_Client::GetInstrumentMod) - .def("DecreaseByID", (bool(Lua_Client::*)(uint32,int))&Lua_Client::DecreaseByID) - .def("Escape", (void(Lua_Client::*)(void))&Lua_Client::Escape) - .def("GoFish", (void(Lua_Client::*)(void))&Lua_Client::GoFish) - .def("ForageItem", (void(Lua_Client::*)(void))&Lua_Client::ForageItem) - .def("ForageItem", (void(Lua_Client::*)(bool))&Lua_Client::ForageItem) - .def("CalcPriceMod", (float(Lua_Client::*)(Lua_Mob,bool))&Lua_Client::CalcPriceMod) - .def("ResetTrade", (void(Lua_Client::*)(void))&Lua_Client::ResetTrade) - .def("GetDisciplineTimer", (uint32(Lua_Client::*)(uint32))&Lua_Client::GetDisciplineTimer) - .def("ResetDisciplineTimer", (void(Lua_Client::*)(uint32))&Lua_Client::ResetDisciplineTimer) - .def("UseDiscipline", (bool(Lua_Client::*)(int,int))&Lua_Client::UseDiscipline) - .def("HasDisciplineLearned", (bool(Lua_Client::*)(uint16))&Lua_Client::HasDisciplineLearned) - .def("GetCharacterFactionLevel", (int(Lua_Client::*)(int))&Lua_Client::GetCharacterFactionLevel) - .def("SetZoneFlag", (void(Lua_Client::*)(int))&Lua_Client::SetZoneFlag) - .def("ClearZoneFlag", (void(Lua_Client::*)(int))&Lua_Client::ClearZoneFlag) - .def("HasZoneFlag", (bool(Lua_Client::*)(int))&Lua_Client::HasZoneFlag) - .def("SendZoneFlagInfo", (void(Lua_Client::*)(Lua_Client))&Lua_Client::SendZoneFlagInfo) - .def("SetAATitle", (void(Lua_Client::*)(const char *))&Lua_Client::SetAATitle) - .def("GetClientVersion", (int(Lua_Client::*)(void))&Lua_Client::GetClientVersion) - .def("GetClientVersionBit", (uint32(Lua_Client::*)(void))&Lua_Client::GetClientVersionBit) - .def("SetTitleSuffix", (void(Lua_Client::*)(const char *))&Lua_Client::SetTitleSuffix) - .def("SetAAPoints", (void(Lua_Client::*)(int))&Lua_Client::SetAAPoints) - .def("GetAAPoints", (int(Lua_Client::*)(void))&Lua_Client::GetAAPoints) - .def("GetSpentAA", (int(Lua_Client::*)(void))&Lua_Client::GetSpentAA) - .def("AddAAPoints", (void(Lua_Client::*)(int))&Lua_Client::AddAAPoints) - .def("RefundAA", (void(Lua_Client::*)(void))&Lua_Client::RefundAA) - .def("GetModCharacterFactionLevel", (int(Lua_Client::*)(int))&Lua_Client::GetModCharacterFactionLevel) - .def("GetLDoNWins", (int(Lua_Client::*)(void))&Lua_Client::GetLDoNWins) - .def("GetLDoNLosses", (int(Lua_Client::*)(void))&Lua_Client::GetLDoNLosses) - .def("GetLDoNWinsTheme", (int(Lua_Client::*)(int))&Lua_Client::GetLDoNWinsTheme) - .def("GetLDoNLossesTheme", (int(Lua_Client::*)(int))&Lua_Client::GetLDoNLossesTheme) - .def("GetStartZone", (int(Lua_Client::*)(void))&Lua_Client::GetStartZone) - .def("SetStartZone", (void(Lua_Client::*)(int))&Lua_Client::SetStartZone) - .def("SetStartZone", (void(Lua_Client::*)(int,float))&Lua_Client::SetStartZone) - .def("SetStartZone", (void(Lua_Client::*)(int,float,float))&Lua_Client::SetStartZone) - .def("SetStartZone", (void(Lua_Client::*)(int,float,float,float))&Lua_Client::SetStartZone) - .def("KeyRingAdd", (void(Lua_Client::*)(uint32))&Lua_Client::KeyRingAdd) - .def("KeyRingCheck", (bool(Lua_Client::*)(uint32))&Lua_Client::KeyRingCheck) - .def("AddPVPPoints", (void(Lua_Client::*)(uint32))&Lua_Client::AddPVPPoints) - .def("AddCrystals", (void(Lua_Client::*)(uint32,uint32))&Lua_Client::AddCrystals) - .def("SetEbonCrystals", (void(Lua_Client::*)(uint32))&Lua_Client::SetEbonCrystals) - .def("SetRadiantCrystals", (void(Lua_Client::*)(uint32))&Lua_Client::SetRadiantCrystals) - .def("GetPVPPoints", (uint32(Lua_Client::*)(void))&Lua_Client::GetPVPPoints) - .def("GetRadiantCrystals", (uint32(Lua_Client::*)(void))&Lua_Client::GetRadiantCrystals) - .def("GetEbonCrystals", (uint32(Lua_Client::*)(void))&Lua_Client::GetEbonCrystals) - .def("QuestReadBook", (void(Lua_Client::*)(const char *,int))&Lua_Client::QuestReadBook) - .def("UpdateGroupAAs", (void(Lua_Client::*)(int,uint32))&Lua_Client::UpdateGroupAAs) - .def("GetGroupPoints", (uint32(Lua_Client::*)(void))&Lua_Client::GetGroupPoints) - .def("GetRaidPoints", (uint32(Lua_Client::*)(void))&Lua_Client::GetRaidPoints) - .def("LearnRecipe", (void(Lua_Client::*)(uint32))&Lua_Client::LearnRecipe) - .def("GetEndurance", (int(Lua_Client::*)(void))&Lua_Client::GetEndurance) - .def("GetMaxEndurance", (int(Lua_Client::*)(void))&Lua_Client::GetMaxEndurance) - .def("GetEndurancePercent", (int(Lua_Client::*)(void))&Lua_Client::GetEndurancePercent) - .def("SetEndurance", (void(Lua_Client::*)(int))&Lua_Client::SetEndurance) - .def("SendOPTranslocateConfirm", (void(Lua_Client::*)(Lua_Mob,int))&Lua_Client::SendOPTranslocateConfirm) - .def("GetIP", (uint32(Lua_Client::*)(void))&Lua_Client::GetIP) - .def("AddLevelBasedExp", (void(Lua_Client::*)(int))&Lua_Client::AddLevelBasedExp) - .def("AddLevelBasedExp", (void(Lua_Client::*)(int,int))&Lua_Client::AddLevelBasedExp) - .def("AddLevelBasedExp", (void(Lua_Client::*)(int,int,bool))&Lua_Client::AddLevelBasedExp) - .def("IncrementAA", (void(Lua_Client::*)(int))&Lua_Client::IncrementAA) - .def("GrantAlternateAdvancementAbility", (bool(Lua_Client::*)(int, int))&Lua_Client::GrantAlternateAdvancementAbility) - .def("GrantAlternateAdvancementAbility", (bool(Lua_Client::*)(int, int, bool))&Lua_Client::GrantAlternateAdvancementAbility) - .def("MarkSingleCompassLoc", (void(Lua_Client::*)(float,float,float))&Lua_Client::MarkSingleCompassLoc) - .def("MarkSingleCompassLoc", (void(Lua_Client::*)(float,float,float,int))&Lua_Client::MarkSingleCompassLoc) - .def("ClearCompassMark",(void(Lua_Client::*)(void))&Lua_Client::ClearCompassMark) - .def("GetNextAvailableSpellBookSlot", (int(Lua_Client::*)(void))&Lua_Client::GetNextAvailableSpellBookSlot) - .def("GetNextAvailableSpellBookSlot", (int(Lua_Client::*)(int))&Lua_Client::GetNextAvailableSpellBookSlot) - .def("GetSpellIDByBookSlot", (uint32(Lua_Client::*)(int))&Lua_Client::GetSpellIDByBookSlot) - .def("FindSpellBookSlotBySpellID", (int(Lua_Client::*)(int))&Lua_Client::FindSpellBookSlotBySpellID) - .def("UpdateTaskActivity", (void(Lua_Client::*)(int,int,int))&Lua_Client::UpdateTaskActivity) - .def("AssignTask", (void(Lua_Client::*)(int,int))&Lua_Client::AssignTask) - .def("AssignTask", (void(Lua_Client::*)(int,int,bool))&Lua_Client::AssignTask) - .def("FailTask", (void(Lua_Client::*)(int))&Lua_Client::FailTask) - .def("IsTaskCompleted", (bool(Lua_Client::*)(int))&Lua_Client::IsTaskCompleted) - .def("IsTaskActive", (bool(Lua_Client::*)(int))&Lua_Client::IsTaskActive) - .def("IsTaskActivityActive", (bool(Lua_Client::*)(int,int))&Lua_Client::IsTaskActivityActive) - .def("GetCorpseCount", (int(Lua_Client::*)(void))&Lua_Client::GetCorpseCount) - .def("GetCorpseID", (int(Lua_Client::*)(int))&Lua_Client::GetCorpseID) - .def("GetCorpseItemAt", (int(Lua_Client::*)(int,int))&Lua_Client::GetCorpseItemAt) - .def("AssignToInstance", (void(Lua_Client::*)(int))&Lua_Client::AssignToInstance) - .def("Freeze", (void(Lua_Client::*)(void))&Lua_Client::Freeze) - .def("UnFreeze", (void(Lua_Client::*)(void))&Lua_Client::UnFreeze) - .def("GetAggroCount", (int(Lua_Client::*)(void))&Lua_Client::GetAggroCount) - .def("GetCarriedMoney", (uint64(Lua_Client::*)(void))&Lua_Client::GetCarriedMoney) - .def("GetAllMoney", (uint64(Lua_Client::*)(void))&Lua_Client::GetAllMoney) - .def("GetMoney", (uint32(Lua_Client::*)(uint8, uint8))&Lua_Client::GetMoney) - .def("OpenLFGuildWindow", (void(Lua_Client::*)(void))&Lua_Client::OpenLFGuildWindow) - .def("NotifyNewTitlesAvailable", (void(Lua_Client::*)(void))&Lua_Client::NotifyNewTitlesAvailable) - .def("Signal", (void(Lua_Client::*)(uint32))&Lua_Client::Signal) - .def("AddAlternateCurrencyValue", (void(Lua_Client::*)(uint32,int))&Lua_Client::AddAlternateCurrencyValue) - .def("SetAlternateCurrencyValue", (void(Lua_Client::*)(uint32,int))&Lua_Client::SetAlternateCurrencyValue) - .def("GetAlternateCurrencyValue", (int(Lua_Client::*)(uint32))&Lua_Client::GetAlternateCurrencyValue) - .def("SendWebLink", (void(Lua_Client::*)(const char *))&Lua_Client::SendWebLink) - .def("HasSpellScribed", (bool(Lua_Client::*)(int))&Lua_Client::HasSpellScribed) - .def("DiaWind", (void(Lua_Client::*)(std::string))&Lua_Client::DialogueWindow) - .def("DialogueWindow", (void(Lua_Client::*)(std::string))&Lua_Client::DialogueWindow) - .def("SetAccountFlag", (void(Lua_Client::*)(std::string,std::string))&Lua_Client::SetAccountFlag) - .def("SetAccountFlag", (void(Lua_Client::*)(std::string,std::string))&Lua_Client::SetAccountFlag) - .def("GetAccountFlag", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetAccountFlag) - .def("GetGroup", (Lua_Group(Lua_Client::*)(void))&Lua_Client::GetGroup) - .def("GetRaid", (Lua_Raid(Lua_Client::*)(void))&Lua_Client::GetRaid) - .def("PutItemInInventory", (bool(Lua_Client::*)(int,Lua_ItemInst))&Lua_Client::PutItemInInventory) - .def("PushItemOnCursor", (bool(Lua_Client::*)(Lua_ItemInst))&Lua_Client::PushItemOnCursor) - .def("GetInventory", (Lua_Inventory(Lua_Client::*)(void))&Lua_Client::GetInventory) - .def("SendItemScale", (void(Lua_Client::*)(Lua_ItemInst))&Lua_Client::SendItemScale) - .def("QueuePacket", (void(Lua_Client::*)(Lua_Packet))&Lua_Client::QueuePacket) - .def("QueuePacket", (void(Lua_Client::*)(Lua_Packet,bool))&Lua_Client::QueuePacket) - .def("QueuePacket", (void(Lua_Client::*)(Lua_Packet,bool,int))&Lua_Client::QueuePacket) - .def("QueuePacket", (void(Lua_Client::*)(Lua_Packet,bool,int,int))&Lua_Client::QueuePacket) - .def("GetHunger", (int(Lua_Client::*)(void))&Lua_Client::GetHunger) - .def("GetThirst", (int(Lua_Client::*)(void))&Lua_Client::GetThirst) - .def("SetHunger", (void(Lua_Client::*)(int))&Lua_Client::SetHunger) - .def("SetThirst", (void(Lua_Client::*)(int))&Lua_Client::SetThirst) - .def("SetConsumption", (void(Lua_Client::*)(int, int))&Lua_Client::SetConsumption) - .def("SendMarqueeMessage", (void(Lua_Client::*)(uint32, uint32, uint32, uint32, uint32, std::string))&Lua_Client::SendMarqueeMessage) - .def("SendColoredText", (void(Lua_Client::*)(uint32, std::string))&Lua_Client::SendColoredText) - .def("PlayMP3", (void(Lua_Client::*)(std::string))&Lua_Client::PlayMP3) - .def("QuestReward", (void(Lua_Client::*)(Lua_Mob))&Lua_Client::QuestReward) - .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32))&Lua_Client::QuestReward) - .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32))&Lua_Client::QuestReward) - .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32))&Lua_Client::QuestReward) - .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32, uint32))&Lua_Client::QuestReward) - .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32, uint32, uint32))&Lua_Client::QuestReward) - .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32, uint32, uint32, uint32))&Lua_Client::QuestReward) - .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32, uint32, uint32, uint32, bool))&Lua_Client::QuestReward) - .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, luabind::adl::object))&Lua_Client::QuestReward) - .def("IsDead", &Lua_Client::IsDead) - .def("CalcCurrentWeight", &Lua_Client::CalcCurrentWeight) - .def("CalcATK", &Lua_Client::CalcATK) - .def("FilteredMessage", &Lua_Client::FilteredMessage) - .def("EnableAreaHPRegen", &Lua_Client::EnableAreaHPRegen) - .def("DisableAreaHPRegen", &Lua_Client::DisableAreaHPRegen) - .def("EnableAreaManaRegen", &Lua_Client::EnableAreaManaRegen) - .def("DisableAreaManaRegen", &Lua_Client::DisableAreaManaRegen) - .def("EnableAreaEndRegen", &Lua_Client::EnableAreaEndRegen) - .def("DisableAreaEndRegen", &Lua_Client::DisableAreaEndRegen) - .def("EnableAreaRegens", &Lua_Client::EnableAreaRegens) - .def("DisableAreaRegens", &Lua_Client::DisableAreaRegens) - .def("SetClientMaxLevel", (void(Lua_Client::*)(int))&Lua_Client::SetClientMaxLevel) - .def("GetClientMaxLevel", (int(Lua_Client::*)(void))&Lua_Client::GetClientMaxLevel) - .def("CreateExpedition", (Lua_Expedition(Lua_Client::*)(luabind::object))&Lua_Client::CreateExpedition) - .def("CreateExpedition", (Lua_Expedition(Lua_Client::*)(std::string, uint32, uint32, std::string, uint32, uint32))&Lua_Client::CreateExpedition) - .def("CreateExpedition", (Lua_Expedition(Lua_Client::*)(std::string, uint32, uint32, std::string, uint32, uint32, bool))&Lua_Client::CreateExpedition) - .def("GetExpedition", (Lua_Expedition(Lua_Client::*)(void))&Lua_Client::GetExpedition) - .def("GetExpeditionLockouts", (luabind::object(Lua_Client::*)(lua_State* L))&Lua_Client::GetExpeditionLockouts) - .def("GetExpeditionLockouts", (luabind::object(Lua_Client::*)(lua_State* L, std::string))&Lua_Client::GetExpeditionLockouts) - .def("GetLockoutExpeditionUUID", (std::string(Lua_Client::*)(std::string, std::string))&Lua_Client::GetLockoutExpeditionUUID) - .def("AddExpeditionLockout", (void(Lua_Client::*)(std::string, std::string, uint32))&Lua_Client::AddExpeditionLockout) - .def("AddExpeditionLockout", (void(Lua_Client::*)(std::string, std::string, uint32, std::string))&Lua_Client::AddExpeditionLockout) - .def("AddExpeditionLockoutDuration", (void(Lua_Client::*)(std::string, std::string, int))&Lua_Client::AddExpeditionLockoutDuration) - .def("AddExpeditionLockoutDuration", (void(Lua_Client::*)(std::string, std::string, int, std::string))&Lua_Client::AddExpeditionLockoutDuration) - .def("RemoveAllExpeditionLockouts", (void(Lua_Client::*)(void))&Lua_Client::RemoveAllExpeditionLockouts) - .def("RemoveAllExpeditionLockouts", (void(Lua_Client::*)(std::string))&Lua_Client::RemoveAllExpeditionLockouts) - .def("RemoveExpeditionLockout", (void(Lua_Client::*)(std::string, std::string))&Lua_Client::RemoveExpeditionLockout) - .def("HasExpeditionLockout", (bool(Lua_Client::*)(std::string, std::string))&Lua_Client::HasExpeditionLockout) - .def("MovePCDynamicZone", (void(Lua_Client::*)(uint32))&Lua_Client::MovePCDynamicZone) - .def("MovePCDynamicZone", (void(Lua_Client::*)(uint32, int))&Lua_Client::MovePCDynamicZone) - .def("MovePCDynamicZone", (void(Lua_Client::*)(uint32, int, bool))&Lua_Client::MovePCDynamicZone) - .def("MovePCDynamicZone", (void(Lua_Client::*)(std::string))&Lua_Client::MovePCDynamicZone) - .def("MovePCDynamicZone", (void(Lua_Client::*)(std::string, int))&Lua_Client::MovePCDynamicZone) - .def("MovePCDynamicZone", (void(Lua_Client::*)(std::string, int, bool))&Lua_Client::MovePCDynamicZone) - .def("CreateTaskDynamicZone", &Lua_Client::CreateTaskDynamicZone) - .def("Fling", (void(Lua_Client::*)(float,float,float,float))&Lua_Client::Fling) - .def("Fling", (void(Lua_Client::*)(float,float,float,float,bool))&Lua_Client::Fling) - .def("Fling", (void(Lua_Client::*)(float,float,float,float,bool,bool))&Lua_Client::Fling) - .def("GetAAEXPModifier", (double(Lua_Client::*)(uint32))&Lua_Client::GetAAEXPModifier) - .def("GetEXPModifier", (double(Lua_Client::*)(uint32))&Lua_Client::GetEXPModifier) - .def("SetAAEXPModifier", (void(Lua_Client::*)(uint32,double))&Lua_Client::SetAAEXPModifier) - .def("SetEXPModifier", (void(Lua_Client::*)(uint32,double))&Lua_Client::SetEXPModifier) - .def("AddLDoNLoss", (void(Lua_Client::*)(uint32))&Lua_Client::AddLDoNLoss) - .def("AddLDoNWin", (void(Lua_Client::*)(uint32))&Lua_Client::AddLDoNWin) - .def("SetHideMe", (void(Lua_Client::*)(bool))&Lua_Client::SetHideMe) - .def("Popup", (void(Lua_Client::*)(const char*,const char*))&Lua_Client::Popup) - .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32))&Lua_Client::Popup) - .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32))&Lua_Client::Popup) - .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32,uint32))&Lua_Client::Popup) - .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32,uint32,uint32))&Lua_Client::Popup) - .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32,uint32,uint32,const char*,const char*))&Lua_Client::Popup) - .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32,uint32,uint32,const char*,const char*,uint32))&Lua_Client::Popup) - .def("ResetAllDisciplineTimers", (void(Lua_Client::*)(void))&Lua_Client::ResetAllDisciplineTimers) - .def("SendToInstance", (void(Lua_Client::*)(std::string,std::string,uint32,float,float,float,float,std::string,uint32))&Lua_Client::SendToInstance) - .def("CountItem", (int(Lua_Client::*)(uint32))&Lua_Client::CountItem) - .def("RemoveItem", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveItem) - .def("RemoveItem", (void(Lua_Client::*)(uint32,uint32))&Lua_Client::RemoveItem) - .def("GetIPExemption", (int(Lua_Client::*)(void))&Lua_Client::GetIPExemption) - .def("GetIPString", (std::string(Lua_Client::*)(void))&Lua_Client::GetIPString) - .def("SetIPExemption", (void(Lua_Client::*)(int))&Lua_Client::SetIPExemption) - .def("ReadBookByName", (void(Lua_Client::*)(std::string,uint8))&Lua_Client::ReadBookByName) - .def("SetGMStatus", (void(Lua_Client::*)(int32))&Lua_Client::SetGMStatus) - .def("UntrainDiscBySpellID", (void(Lua_Client::*)(uint16))&Lua_Client::UntrainDiscBySpellID) - .def("UntrainDiscBySpellID", (void(Lua_Client::*)(uint16,bool))&Lua_Client::UntrainDiscBySpellID) - .def("SummonBaggedItems", (void(Lua_Client::*)(uint32,luabind::adl::object))&Lua_Client::SummonBaggedItems) - .def("RemoveLDoNLoss", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveLDoNLoss) - .def("RemoveLDoNWin", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveLDoNWin) - .def("ScribeSpells", (uint16(Lua_Client::*)(uint8,uint8))&Lua_Client::ScribeSpells) - .def("LearnDisciplines", (uint16(Lua_Client::*)(uint8,uint8))&Lua_Client::LearnDisciplines) - .def("GetNextAvailableDisciplineSlot", (int(Lua_Client::*)(void))&Lua_Client::GetNextAvailableDisciplineSlot) - .def("GetNextAvailableDisciplineSlot", (int(Lua_Client::*)(int))&Lua_Client::GetNextAvailableDisciplineSlot); + .def(luabind::constructor<>()) + .def("AccountID", (uint32(Lua_Client::*)(void))&Lua_Client::AccountID) + .def("AccountName", (const char *(Lua_Client::*)(void))&Lua_Client::AccountName) + .def("AddAAPoints", (void(Lua_Client::*)(int))&Lua_Client::AddAAPoints) + .def("AddAlternateCurrencyValue", (void(Lua_Client::*)(uint32,int))&Lua_Client::AddAlternateCurrencyValue) + .def("AddCrystals", (void(Lua_Client::*)(uint32,uint32))&Lua_Client::AddCrystals) + .def("AddEXP", (void(Lua_Client::*)(uint32))&Lua_Client::AddEXP) + .def("AddEXP", (void(Lua_Client::*)(uint32,int))&Lua_Client::AddEXP) + .def("AddEXP", (void(Lua_Client::*)(uint32,int,bool))&Lua_Client::AddEXP) + .def("AddExpeditionLockout", (void(Lua_Client::*)(std::string, std::string, uint32))&Lua_Client::AddExpeditionLockout) + .def("AddExpeditionLockout", (void(Lua_Client::*)(std::string, std::string, uint32, std::string))&Lua_Client::AddExpeditionLockout) + .def("AddExpeditionLockoutDuration", (void(Lua_Client::*)(std::string, std::string, int))&Lua_Client::AddExpeditionLockoutDuration) + .def("AddExpeditionLockoutDuration", (void(Lua_Client::*)(std::string, std::string, int, std::string))&Lua_Client::AddExpeditionLockoutDuration) + .def("AddLDoNLoss", (void(Lua_Client::*)(uint32))&Lua_Client::AddLDoNLoss) + .def("AddLDoNWin", (void(Lua_Client::*)(uint32))&Lua_Client::AddLDoNWin) + .def("AddLevelBasedExp", (void(Lua_Client::*)(int))&Lua_Client::AddLevelBasedExp) + .def("AddLevelBasedExp", (void(Lua_Client::*)(int,int))&Lua_Client::AddLevelBasedExp) + .def("AddLevelBasedExp", (void(Lua_Client::*)(int,int,bool))&Lua_Client::AddLevelBasedExp) + .def("AddMoneyToPP", (void(Lua_Client::*)(uint32,uint32,uint32,uint32,bool))&Lua_Client::AddMoneyToPP) + .def("AddPVPPoints", (void(Lua_Client::*)(uint32))&Lua_Client::AddPVPPoints) + .def("AddSkill", (void(Lua_Client::*)(int,int))&Lua_Client::AddSkill) + .def("Admin", (int(Lua_Client::*)(void))&Lua_Client::Admin) + .def("AssignTask", (void(Lua_Client::*)(int,int))&Lua_Client::AssignTask) + .def("AssignTask", (void(Lua_Client::*)(int,int,bool))&Lua_Client::AssignTask) + .def("AssignToInstance", (void(Lua_Client::*)(int))&Lua_Client::AssignToInstance) + .def("AutoSplitEnabled", (bool(Lua_Client::*)(void))&Lua_Client::AutoSplitEnabled) + .def("BreakInvis", (void(Lua_Client::*)(void))&Lua_Client::BreakInvis) + .def("CalcATK", &Lua_Client::CalcATK) + .def("CalcCurrentWeight", &Lua_Client::CalcCurrentWeight) + .def("CalcPriceMod", (float(Lua_Client::*)(Lua_Mob,bool))&Lua_Client::CalcPriceMod) + .def("CanHaveSkill", (bool(Lua_Client::*)(int))&Lua_Client::CanHaveSkill) + .def("ChangeLastName", (void(Lua_Client::*)(const char *in))&Lua_Client::ChangeLastName) + .def("CharacterID", (uint32(Lua_Client::*)(void))&Lua_Client::CharacterID) + .def("CheckIncreaseSkill", (void(Lua_Client::*)(int,Lua_Mob))&Lua_Client::CheckIncreaseSkill) + .def("CheckIncreaseSkill", (void(Lua_Client::*)(int,Lua_Mob,int))&Lua_Client::CheckIncreaseSkill) + .def("CheckSpecializeIncrease", (void(Lua_Client::*)(int))&Lua_Client::CheckSpecializeIncrease) + .def("ClearCompassMark",(void(Lua_Client::*)(void))&Lua_Client::ClearCompassMark) + .def("ClearZoneFlag", (void(Lua_Client::*)(int))&Lua_Client::ClearZoneFlag) + .def("Connected", (bool(Lua_Client::*)(void))&Lua_Client::Connected) + .def("CountItem", (int(Lua_Client::*)(uint32))&Lua_Client::CountItem) + .def("CreateExpedition", (Lua_Expedition(Lua_Client::*)(luabind::object))&Lua_Client::CreateExpedition) + .def("CreateExpedition", (Lua_Expedition(Lua_Client::*)(std::string, uint32, uint32, std::string, uint32, uint32))&Lua_Client::CreateExpedition) + .def("CreateExpedition", (Lua_Expedition(Lua_Client::*)(std::string, uint32, uint32, std::string, uint32, uint32, bool))&Lua_Client::CreateExpedition) + .def("CreateTaskDynamicZone", &Lua_Client::CreateTaskDynamicZone) + .def("DecreaseByID", (bool(Lua_Client::*)(uint32,int))&Lua_Client::DecreaseByID) + .def("DeleteItemInInventory", (void(Lua_Client::*)(int,int))&Lua_Client::DeleteItemInInventory) + .def("DeleteItemInInventory", (void(Lua_Client::*)(int,int,bool))&Lua_Client::DeleteItemInInventory) + .def("DiaWind", (void(Lua_Client::*)(std::string))&Lua_Client::DialogueWindow) + .def("DialogueWindow", (void(Lua_Client::*)(std::string))&Lua_Client::DialogueWindow) + .def("DisableAreaEndRegen", &Lua_Client::DisableAreaEndRegen) + .def("DisableAreaHPRegen", &Lua_Client::DisableAreaHPRegen) + .def("DisableAreaManaRegen", &Lua_Client::DisableAreaManaRegen) + .def("DisableAreaRegens", &Lua_Client::DisableAreaRegens) + .def("Disconnect", (void(Lua_Client::*)(void))&Lua_Client::Disconnect) + .def("DropItem", (void(Lua_Client::*)(int))&Lua_Client::DropItem) + .def("Duck", (void(Lua_Client::*)(void))&Lua_Client::Duck) + .def("DyeArmorBySlot", (void(Lua_Client::*)(uint8,uint8,uint8,uint8))&Lua_Client::DyeArmorBySlot) + .def("DyeArmorBySlot", (void(Lua_Client::*)(uint8,uint8,uint8,uint8,uint8))&Lua_Client::DyeArmorBySlot) + .def("EnableAreaEndRegen", &Lua_Client::EnableAreaEndRegen) + .def("EnableAreaHPRegen", &Lua_Client::EnableAreaHPRegen) + .def("EnableAreaManaRegen", &Lua_Client::EnableAreaManaRegen) + .def("EnableAreaRegens", &Lua_Client::EnableAreaRegens) + .def("Escape", (void(Lua_Client::*)(void))&Lua_Client::Escape) + .def("FailTask", (void(Lua_Client::*)(int))&Lua_Client::FailTask) + .def("FilteredMessage", &Lua_Client::FilteredMessage) + .def("FindMemmedSpellBySlot", (uint16(Lua_Client::*)(int))&Lua_Client::FindMemmedSpellBySlot) + .def("FindSpellBookSlotBySpellID", (int(Lua_Client::*)(int))&Lua_Client::FindSpellBookSlotBySpellID) + .def("Fling", (void(Lua_Client::*)(float,float,float,float))&Lua_Client::Fling) + .def("Fling", (void(Lua_Client::*)(float,float,float,float,bool))&Lua_Client::Fling) + .def("Fling", (void(Lua_Client::*)(float,float,float,float,bool,bool))&Lua_Client::Fling) + .def("ForageItem", (void(Lua_Client::*)(bool))&Lua_Client::ForageItem) + .def("ForageItem", (void(Lua_Client::*)(void))&Lua_Client::ForageItem) + .def("Freeze", (void(Lua_Client::*)(void))&Lua_Client::Freeze) + .def("GetAAEXPModifier", (double(Lua_Client::*)(uint32))&Lua_Client::GetAAEXPModifier) + .def("GetAAExp", (uint32(Lua_Client::*)(void))&Lua_Client::GetAAExp) + .def("GetAAPercent", (uint32(Lua_Client::*)(void))&Lua_Client::GetAAPercent) + .def("GetAAPoints", (int(Lua_Client::*)(void))&Lua_Client::GetAAPoints) + .def("GetAFK", (int(Lua_Client::*)(void))&Lua_Client::GetAFK) + .def("GetAccountAge", (int(Lua_Client::*)(void))&Lua_Client::GetAccountAge) + .def("GetAccountFlag", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetAccountFlag) + .def("GetAggroCount", (int(Lua_Client::*)(void))&Lua_Client::GetAggroCount) + .def("GetAllMoney", (uint64(Lua_Client::*)(void))&Lua_Client::GetAllMoney) + .def("GetAlternateCurrencyValue", (int(Lua_Client::*)(uint32))&Lua_Client::GetAlternateCurrencyValue) + .def("GetAnon", (int(Lua_Client::*)(void))&Lua_Client::GetAnon) + .def("GetAugmentIDAt", (int(Lua_Client::*)(int,int))&Lua_Client::GetAugmentIDAt) + .def("GetBaseAGI", (int(Lua_Client::*)(void))&Lua_Client::GetBaseAGI) + .def("GetBaseCHA", (int(Lua_Client::*)(void))&Lua_Client::GetBaseCHA) + .def("GetBaseDEX", (int(Lua_Client::*)(void))&Lua_Client::GetBaseDEX) + .def("GetBaseFace", (int(Lua_Client::*)(void))&Lua_Client::GetBaseFace) + .def("GetBaseINT", (int(Lua_Client::*)(void))&Lua_Client::GetBaseINT) + .def("GetBaseSTA", (int(Lua_Client::*)(void))&Lua_Client::GetBaseSTA) + .def("GetBaseSTR", (int(Lua_Client::*)(void))&Lua_Client::GetBaseSTR) + .def("GetBaseWIS", (int(Lua_Client::*)(void))&Lua_Client::GetBaseWIS) + .def("GetBindHeading", (float(Lua_Client::*)(int))&Lua_Client::GetBindHeading) + .def("GetBindHeading", (float(Lua_Client::*)(void))&Lua_Client::GetBindHeading) + .def("GetBindX", (float(Lua_Client::*)(int))&Lua_Client::GetBindX) + .def("GetBindX", (float(Lua_Client::*)(void))&Lua_Client::GetBindX) + .def("GetBindY", (float(Lua_Client::*)(int))&Lua_Client::GetBindY) + .def("GetBindY", (float(Lua_Client::*)(void))&Lua_Client::GetBindY) + .def("GetBindZ", (float(Lua_Client::*)(int))&Lua_Client::GetBindZ) + .def("GetBindZ", (float(Lua_Client::*)(void))&Lua_Client::GetBindZ) + .def("GetBindZoneID", (uint32(Lua_Client::*)(int))&Lua_Client::GetBindZoneID) + .def("GetBindZoneID", (uint32(Lua_Client::*)(void))&Lua_Client::GetBindZoneID) + .def("GetCarriedMoney", (uint64(Lua_Client::*)(void))&Lua_Client::GetCarriedMoney) + .def("GetCharacterFactionLevel", (int(Lua_Client::*)(int))&Lua_Client::GetCharacterFactionLevel) + .def("GetClassBitmask", (int(Lua_Client::*)(void))&Lua_Client::GetClassBitmask) + .def("GetClientMaxLevel", (int(Lua_Client::*)(void))&Lua_Client::GetClientMaxLevel) + .def("GetClientVersion", (int(Lua_Client::*)(void))&Lua_Client::GetClientVersion) + .def("GetClientVersionBit", (uint32(Lua_Client::*)(void))&Lua_Client::GetClientVersionBit) + .def("GetCorpseCount", (int(Lua_Client::*)(void))&Lua_Client::GetCorpseCount) + .def("GetCorpseID", (int(Lua_Client::*)(int))&Lua_Client::GetCorpseID) + .def("GetCorpseItemAt", (int(Lua_Client::*)(int,int))&Lua_Client::GetCorpseItemAt) + .def("GetDiscSlotBySpellID", (int(Lua_Client::*)(int32))&Lua_Client::GetDiscSlotBySpellID) + .def("GetDisciplineTimer", (uint32(Lua_Client::*)(uint32))&Lua_Client::GetDisciplineTimer) + .def("GetDuelTarget", (int(Lua_Client::*)(void))&Lua_Client::GetDuelTarget) + .def("GetEXP", (uint32(Lua_Client::*)(void))&Lua_Client::GetEXP) + .def("GetEXPModifier", (double(Lua_Client::*)(uint32))&Lua_Client::GetEXPModifier) + .def("GetEbonCrystals", (uint32(Lua_Client::*)(void))&Lua_Client::GetEbonCrystals) + .def("GetEndurance", (int(Lua_Client::*)(void))&Lua_Client::GetEndurance) + .def("GetEndurancePercent", (int(Lua_Client::*)(void))&Lua_Client::GetEndurancePercent) + .def("GetExpedition", (Lua_Expedition(Lua_Client::*)(void))&Lua_Client::GetExpedition) + .def("GetExpeditionLockouts", (luabind::object(Lua_Client::*)(lua_State* L))&Lua_Client::GetExpeditionLockouts) + .def("GetExpeditionLockouts", (luabind::object(Lua_Client::*)(lua_State* L, std::string))&Lua_Client::GetExpeditionLockouts) + .def("GetFace", (int(Lua_Client::*)(void))&Lua_Client::GetFace) + .def("GetFactionLevel", (int(Lua_Client::*)(uint32,uint32,uint32,uint32,uint32,uint32,Lua_NPC))&Lua_Client::GetFactionLevel) + .def("GetFeigned", (bool(Lua_Client::*)(void))&Lua_Client::GetFeigned) + .def("GetGM", (bool(Lua_Client::*)(void))&Lua_Client::GetGM) + .def("GetGroup", (Lua_Group(Lua_Client::*)(void))&Lua_Client::GetGroup) + .def("GetGroupPoints", (uint32(Lua_Client::*)(void))&Lua_Client::GetGroupPoints) + .def("GetHorseId", (int(Lua_Client::*)(void))&Lua_Client::GetHorseId) + .def("GetHunger", (int(Lua_Client::*)(void))&Lua_Client::GetHunger) + .def("GetIP", (uint32(Lua_Client::*)(void))&Lua_Client::GetIP) + .def("GetIPExemption", (int(Lua_Client::*)(void))&Lua_Client::GetIPExemption) + .def("GetIPString", (std::string(Lua_Client::*)(void))&Lua_Client::GetIPString) + .def("GetInstrumentMod", (int(Lua_Client::*)(int))&Lua_Client::GetInstrumentMod) + .def("GetInventory", (Lua_Inventory(Lua_Client::*)(void))&Lua_Client::GetInventory) + .def("GetItemIDAt", (int(Lua_Client::*)(int))&Lua_Client::GetItemIDAt) + .def("GetLDoNLosses", (int(Lua_Client::*)(void))&Lua_Client::GetLDoNLosses) + .def("GetLDoNLossesTheme", (int(Lua_Client::*)(int))&Lua_Client::GetLDoNLossesTheme) + .def("GetLDoNPointsTheme", (int(Lua_Client::*)(int))&Lua_Client::GetLDoNPointsTheme) + .def("GetLDoNWins", (int(Lua_Client::*)(void))&Lua_Client::GetLDoNWins) + .def("GetLDoNWinsTheme", (int(Lua_Client::*)(int))&Lua_Client::GetLDoNWinsTheme) + .def("GetLanguageSkill", (int(Lua_Client::*)(int))&Lua_Client::GetLanguageSkill) + .def("GetLearnableDisciplines", (luabind::object(Lua_Client::*)(lua_State* L))&Lua_Client::GetLearnableDisciplines) + .def("GetLearnableDisciplines", (luabind::object(Lua_Client::*)(lua_State* L,uint8))&Lua_Client::GetLearnableDisciplines) + .def("GetLearnableDisciplines", (luabind::object(Lua_Client::*)(lua_State* L,uint8,uint8))&Lua_Client::GetLearnableDisciplines) + .def("GetLearnedDisciplines", (luabind::object(Lua_Client::*)(lua_State* L))&Lua_Client::GetLearnedDisciplines) + .def("GetLockoutExpeditionUUID", (std::string(Lua_Client::*)(std::string, std::string))&Lua_Client::GetLockoutExpeditionUUID) + .def("GetMaxEndurance", (int(Lua_Client::*)(void))&Lua_Client::GetMaxEndurance) + .def("GetMemmedSpells", (luabind::object(Lua_Client::*)(lua_State* L))&Lua_Client::GetMemmedSpells) + .def("GetModCharacterFactionLevel", (int(Lua_Client::*)(int))&Lua_Client::GetModCharacterFactionLevel) + .def("GetMoney", (uint32(Lua_Client::*)(uint8, uint8))&Lua_Client::GetMoney) + .def("GetNextAvailableDisciplineSlot", (int(Lua_Client::*)(int))&Lua_Client::GetNextAvailableDisciplineSlot) + .def("GetNextAvailableDisciplineSlot", (int(Lua_Client::*)(void))&Lua_Client::GetNextAvailableDisciplineSlot) + .def("GetNextAvailableSpellBookSlot", (int(Lua_Client::*)(int))&Lua_Client::GetNextAvailableSpellBookSlot) + .def("GetNextAvailableSpellBookSlot", (int(Lua_Client::*)(void))&Lua_Client::GetNextAvailableSpellBookSlot) + .def("GetPVP", (bool(Lua_Client::*)(void))&Lua_Client::GetPVP) + .def("GetPVPPoints", (uint32(Lua_Client::*)(void))&Lua_Client::GetPVPPoints) + .def("GetRaceBitmask", (int(Lua_Client::*)(void))&Lua_Client::GetRaceBitmask) + .def("GetRadiantCrystals", (uint32(Lua_Client::*)(void))&Lua_Client::GetRadiantCrystals) + .def("GetRaid", (Lua_Raid(Lua_Client::*)(void))&Lua_Client::GetRaid) + .def("GetRaidPoints", (uint32(Lua_Client::*)(void))&Lua_Client::GetRaidPoints) + .def("GetRawItemAC", (int(Lua_Client::*)(void))&Lua_Client::GetRawItemAC) + .def("GetRawSkill", (int(Lua_Client::*)(int))&Lua_Client::GetRawSkill) + .def("GetScribeableSpells", (luabind::object(Lua_Client::*)(lua_State* L))&Lua_Client::GetScribeableSpells) + .def("GetScribeableSpells", (luabind::object(Lua_Client::*)(lua_State* L,uint8))&Lua_Client::GetScribeableSpells) + .def("GetScribeableSpells", (luabind::object(Lua_Client::*)(lua_State* L,uint8,uint8))&Lua_Client::GetScribeableSpells) + .def("GetScribedSpells", (luabind::object(Lua_Client::*)(lua_State* L))&Lua_Client::GetScribedSpells) + .def("GetSkillPoints", (int(Lua_Client::*)(void))&Lua_Client::GetSkillPoints) + .def("GetSpellIDByBookSlot", (uint32(Lua_Client::*)(int))&Lua_Client::GetSpellIDByBookSlot) + .def("GetSpentAA", (int(Lua_Client::*)(void))&Lua_Client::GetSpentAA) + .def("GetStartZone", (int(Lua_Client::*)(void))&Lua_Client::GetStartZone) + .def("GetTargetRingX", (float(Lua_Client::*)(void))&Lua_Client::GetTargetRingX) + .def("GetTargetRingY", (float(Lua_Client::*)(void))&Lua_Client::GetTargetRingY) + .def("GetTargetRingZ", (float(Lua_Client::*)(void))&Lua_Client::GetTargetRingZ) + .def("GetThirst", (int(Lua_Client::*)(void))&Lua_Client::GetThirst) + .def("GetTotalSecondsPlayed", (uint32(Lua_Client::*)(void))&Lua_Client::GetTotalSecondsPlayed) + .def("GetWeight", (int(Lua_Client::*)(void))&Lua_Client::GetWeight) + .def("GoFish", (void(Lua_Client::*)(void))&Lua_Client::GoFish) + .def("GrantAlternateAdvancementAbility", (bool(Lua_Client::*)(int, int))&Lua_Client::GrantAlternateAdvancementAbility) + .def("GrantAlternateAdvancementAbility", (bool(Lua_Client::*)(int, int, bool))&Lua_Client::GrantAlternateAdvancementAbility) + .def("GuildID", (uint32(Lua_Client::*)(void))&Lua_Client::GuildID) + .def("GuildRank", (int(Lua_Client::*)(void))&Lua_Client::GuildRank) + .def("HasDisciplineLearned", (bool(Lua_Client::*)(uint16))&Lua_Client::HasDisciplineLearned) + .def("HasExpeditionLockout", (bool(Lua_Client::*)(std::string, std::string))&Lua_Client::HasExpeditionLockout) + .def("HasSkill", (bool(Lua_Client::*)(int))&Lua_Client::HasSkill) + .def("HasSpellScribed", (bool(Lua_Client::*)(int))&Lua_Client::HasSpellScribed) + .def("HasZoneFlag", (bool(Lua_Client::*)(int))&Lua_Client::HasZoneFlag) + .def("Hungry", (bool(Lua_Client::*)(void))&Lua_Client::Hungry) + .def("InZone", (bool(Lua_Client::*)(void))&Lua_Client::InZone) + .def("IncStats", (void(Lua_Client::*)(int,int))&Lua_Client::IncStats) + .def("IncreaseLanguageSkill", (void(Lua_Client::*)(int))&Lua_Client::IncreaseLanguageSkill) + .def("IncreaseLanguageSkill", (void(Lua_Client::*)(int,int))&Lua_Client::IncreaseLanguageSkill) + .def("IncreaseSkill", (void(Lua_Client::*)(int))&Lua_Client::IncreaseSkill) + .def("IncreaseSkill", (void(Lua_Client::*)(int,int))&Lua_Client::IncreaseSkill) + .def("IncrementAA", (void(Lua_Client::*)(int))&Lua_Client::IncrementAA) + .def("IsCrouching", (bool(Lua_Client::*)(void))&Lua_Client::IsCrouching) + .def("IsDead", &Lua_Client::IsDead) + .def("IsDueling", (bool(Lua_Client::*)(void))&Lua_Client::IsDueling) + .def("IsGrouped", (bool(Lua_Client::*)(void))&Lua_Client::IsGrouped) + .def("IsLD", (bool(Lua_Client::*)(void))&Lua_Client::IsLD) + .def("IsMedding", (bool(Lua_Client::*)(void))&Lua_Client::IsMedding) + .def("IsRaidGrouped", (bool(Lua_Client::*)(void))&Lua_Client::IsRaidGrouped) + .def("IsSitting", (bool(Lua_Client::*)(void))&Lua_Client::IsSitting) + .def("IsStanding", (bool(Lua_Client::*)(void))&Lua_Client::IsStanding) + .def("IsTaskActive", (bool(Lua_Client::*)(int))&Lua_Client::IsTaskActive) + .def("IsTaskActivityActive", (bool(Lua_Client::*)(int,int))&Lua_Client::IsTaskActivityActive) + .def("IsTaskCompleted", (bool(Lua_Client::*)(int))&Lua_Client::IsTaskCompleted) + .def("KeyRingAdd", (void(Lua_Client::*)(uint32))&Lua_Client::KeyRingAdd) + .def("KeyRingCheck", (bool(Lua_Client::*)(uint32))&Lua_Client::KeyRingCheck) + .def("Kick", (void(Lua_Client::*)(void))&Lua_Client::Kick) + .def("LearnDisciplines", (uint16(Lua_Client::*)(uint8,uint8))&Lua_Client::LearnDisciplines) + .def("LearnRecipe", (void(Lua_Client::*)(uint32))&Lua_Client::LearnRecipe) + .def("LeaveGroup", (void(Lua_Client::*)(void))&Lua_Client::LeaveGroup) + .def("MarkSingleCompassLoc", (void(Lua_Client::*)(float,float,float))&Lua_Client::MarkSingleCompassLoc) + .def("MarkSingleCompassLoc", (void(Lua_Client::*)(float,float,float,int))&Lua_Client::MarkSingleCompassLoc) + .def("MaxSkill", (int(Lua_Client::*)(int))&Lua_Client::MaxSkill) + .def("MemSpell", (void(Lua_Client::*)(int,int))&Lua_Client::MemSpell) + .def("MemSpell", (void(Lua_Client::*)(int,int,bool))&Lua_Client::MemSpell) + .def("MemmedCount", (int(Lua_Client::*)(void))&Lua_Client::MemmedCount) + .def("MovePC", (void(Lua_Client::*)(int,float,float,float,float))&Lua_Client::MovePC) + .def("MovePCDynamicZone", (void(Lua_Client::*)(std::string))&Lua_Client::MovePCDynamicZone) + .def("MovePCDynamicZone", (void(Lua_Client::*)(std::string, int))&Lua_Client::MovePCDynamicZone) + .def("MovePCDynamicZone", (void(Lua_Client::*)(std::string, int, bool))&Lua_Client::MovePCDynamicZone) + .def("MovePCDynamicZone", (void(Lua_Client::*)(uint32))&Lua_Client::MovePCDynamicZone) + .def("MovePCDynamicZone", (void(Lua_Client::*)(uint32, int))&Lua_Client::MovePCDynamicZone) + .def("MovePCDynamicZone", (void(Lua_Client::*)(uint32, int, bool))&Lua_Client::MovePCDynamicZone) + .def("MovePCInstance", (void(Lua_Client::*)(int,int,float,float,float,float))&Lua_Client::MovePCInstance) + .def("MoveZone", (void(Lua_Client::*)(const char*))&Lua_Client::MoveZone) + .def("MoveZoneGroup", (void(Lua_Client::*)(const char*))&Lua_Client::MoveZoneGroup) + .def("MoveZoneInstance", (void(Lua_Client::*)(uint16))&Lua_Client::MoveZoneInstance) + .def("MoveZoneInstanceGroup", (void(Lua_Client::*)(uint16))&Lua_Client::MoveZoneInstanceGroup) + .def("MoveZoneInstanceRaid", (void(Lua_Client::*)(uint16))&Lua_Client::MoveZoneInstanceRaid) + .def("MoveZoneRaid", (void(Lua_Client::*)(const char*))&Lua_Client::MoveZoneRaid) + .def("NotifyNewTitlesAvailable", (void(Lua_Client::*)(void))&Lua_Client::NotifyNewTitlesAvailable) + .def("NukeItem", (void(Lua_Client::*)(uint32))&Lua_Client::NukeItem) + .def("NukeItem", (void(Lua_Client::*)(uint32,int))&Lua_Client::NukeItem) + .def("OpenLFGuildWindow", (void(Lua_Client::*)(void))&Lua_Client::OpenLFGuildWindow) + .def("PlayMP3", (void(Lua_Client::*)(std::string))&Lua_Client::PlayMP3) + .def("Popup", (void(Lua_Client::*)(const char*,const char*))&Lua_Client::Popup) + .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32))&Lua_Client::Popup) + .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32))&Lua_Client::Popup) + .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32,uint32))&Lua_Client::Popup) + .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32,uint32,uint32))&Lua_Client::Popup) + .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32,uint32,uint32,const char*,const char*))&Lua_Client::Popup) + .def("Popup", (void(Lua_Client::*)(const char*,const char*,uint32,uint32,uint32,uint32,const char*,const char*,uint32))&Lua_Client::Popup) + .def("PushItemOnCursor", (bool(Lua_Client::*)(Lua_ItemInst))&Lua_Client::PushItemOnCursor) + .def("PutItemInInventory", (bool(Lua_Client::*)(int,Lua_ItemInst))&Lua_Client::PutItemInInventory) + .def("QuestReadBook", (void(Lua_Client::*)(const char *,int))&Lua_Client::QuestReadBook) + .def("QuestReward", (void(Lua_Client::*)(Lua_Mob))&Lua_Client::QuestReward) + .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, luabind::adl::object))&Lua_Client::QuestReward) + .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32))&Lua_Client::QuestReward) + .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32))&Lua_Client::QuestReward) + .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32))&Lua_Client::QuestReward) + .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32, uint32))&Lua_Client::QuestReward) + .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32, uint32, uint32))&Lua_Client::QuestReward) + .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32, uint32, uint32, uint32))&Lua_Client::QuestReward) + .def("QuestReward", (void(Lua_Client::*)(Lua_Mob, uint32, uint32, uint32, uint32, uint32, uint32, bool))&Lua_Client::QuestReward) + .def("QueuePacket", (void(Lua_Client::*)(Lua_Packet))&Lua_Client::QueuePacket) + .def("QueuePacket", (void(Lua_Client::*)(Lua_Packet,bool))&Lua_Client::QueuePacket) + .def("QueuePacket", (void(Lua_Client::*)(Lua_Packet,bool,int))&Lua_Client::QueuePacket) + .def("QueuePacket", (void(Lua_Client::*)(Lua_Packet,bool,int,int))&Lua_Client::QueuePacket) + .def("ReadBookByName", (void(Lua_Client::*)(std::string,uint8))&Lua_Client::ReadBookByName) + .def("RefundAA", (void(Lua_Client::*)(void))&Lua_Client::RefundAA) + .def("RemoveAllExpeditionLockouts", (void(Lua_Client::*)(std::string))&Lua_Client::RemoveAllExpeditionLockouts) + .def("RemoveAllExpeditionLockouts", (void(Lua_Client::*)(void))&Lua_Client::RemoveAllExpeditionLockouts) + .def("RemoveExpeditionLockout", (void(Lua_Client::*)(std::string, std::string))&Lua_Client::RemoveExpeditionLockout) + .def("RemoveItem", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveItem) + .def("RemoveItem", (void(Lua_Client::*)(uint32,uint32))&Lua_Client::RemoveItem) + .def("RemoveLDoNLoss", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveLDoNLoss) + .def("RemoveLDoNWin", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveLDoNWin) + .def("ResetAA", (void(Lua_Client::*)(void))&Lua_Client::ResetAA) + .def("ResetAllDisciplineTimers", (void(Lua_Client::*)(void))&Lua_Client::ResetAllDisciplineTimers) + .def("ResetDisciplineTimer", (void(Lua_Client::*)(uint32))&Lua_Client::ResetDisciplineTimer) + .def("ResetTrade", (void(Lua_Client::*)(void))&Lua_Client::ResetTrade) + .def("Save", (void(Lua_Client::*)(int))&Lua_Client::Save) + .def("Save", (void(Lua_Client::*)(void))&Lua_Client::Save) + .def("SaveBackup", (void(Lua_Client::*)(void))&Lua_Client::SaveBackup) + .def("ScribeSpell", (void(Lua_Client::*)(int,int))&Lua_Client::ScribeSpell) + .def("ScribeSpell", (void(Lua_Client::*)(int,int,bool))&Lua_Client::ScribeSpell) + .def("ScribeSpells", (uint16(Lua_Client::*)(uint8,uint8))&Lua_Client::ScribeSpells) + .def("SendColoredText", (void(Lua_Client::*)(uint32, std::string))&Lua_Client::SendColoredText) + .def("SendItemScale", (void(Lua_Client::*)(Lua_ItemInst))&Lua_Client::SendItemScale) + .def("SendMarqueeMessage", (void(Lua_Client::*)(uint32, uint32, uint32, uint32, uint32, std::string))&Lua_Client::SendMarqueeMessage) + .def("SendOPTranslocateConfirm", (void(Lua_Client::*)(Lua_Mob,int))&Lua_Client::SendOPTranslocateConfirm) + .def("SendSound", (void(Lua_Client::*)(void))&Lua_Client::SendSound) + .def("SendToGuildHall", (void(Lua_Client::*)(void))&Lua_Client::SendToGuildHall) + .def("SendToInstance", (void(Lua_Client::*)(std::string,std::string,uint32,float,float,float,float,std::string,uint32))&Lua_Client::SendToInstance) + .def("SendWebLink", (void(Lua_Client::*)(const char *))&Lua_Client::SendWebLink) + .def("SendZoneFlagInfo", (void(Lua_Client::*)(Lua_Client))&Lua_Client::SendZoneFlagInfo) + .def("SetAAEXPModifier", (void(Lua_Client::*)(uint32,double))&Lua_Client::SetAAEXPModifier) + .def("SetAAPoints", (void(Lua_Client::*)(int))&Lua_Client::SetAAPoints) + .def("SetAATitle", (void(Lua_Client::*)(const char *))&Lua_Client::SetAATitle) + .def("SetAFK", (void(Lua_Client::*)(uint8))&Lua_Client::SetAFK) + .def("SetAccountFlag", (void(Lua_Client::*)(std::string,std::string))&Lua_Client::SetAccountFlag) + .def("SetAccountFlag", (void(Lua_Client::*)(std::string,std::string))&Lua_Client::SetAccountFlag) + .def("SetAlternateCurrencyValue", (void(Lua_Client::*)(uint32,int))&Lua_Client::SetAlternateCurrencyValue) + .def("SetAnon", (void(Lua_Client::*)(uint8))&Lua_Client::SetAnon) + .def("SetBaseClass", (void(Lua_Client::*)(int))&Lua_Client::SetBaseClass) + .def("SetBaseGender", (void(Lua_Client::*)(int))&Lua_Client::SetBaseGender) + .def("SetBaseRace", (void(Lua_Client::*)(int))&Lua_Client::SetBaseRace) + .def("SetBindPoint", (void(Lua_Client::*)(int))&Lua_Client::SetBindPoint) + .def("SetBindPoint", (void(Lua_Client::*)(int,int))&Lua_Client::SetBindPoint) + .def("SetBindPoint", (void(Lua_Client::*)(int,int,float))&Lua_Client::SetBindPoint) + .def("SetBindPoint", (void(Lua_Client::*)(int,int,float,float))&Lua_Client::SetBindPoint) + .def("SetBindPoint", (void(Lua_Client::*)(int,int,float,float,float))&Lua_Client::SetBindPoint) + .def("SetBindPoint", (void(Lua_Client::*)(int,int,float,float,float,float))&Lua_Client::SetBindPoint) + .def("SetBindPoint", (void(Lua_Client::*)(void))&Lua_Client::SetBindPoint) + .def("SetClientMaxLevel", (void(Lua_Client::*)(int))&Lua_Client::SetClientMaxLevel) + .def("SetConsumption", (void(Lua_Client::*)(int, int))&Lua_Client::SetConsumption) + .def("SetDeity", (void(Lua_Client::*)(int))&Lua_Client::SetDeity) + .def("SetDuelTarget", (void(Lua_Client::*)(int))&Lua_Client::SetDuelTarget) + .def("SetDueling", (void(Lua_Client::*)(bool))&Lua_Client::SetDueling) + .def("SetEXP", (void(Lua_Client::*)(uint32,uint32))&Lua_Client::SetEXP) + .def("SetEXP", (void(Lua_Client::*)(uint32,uint32,bool))&Lua_Client::SetEXP) + .def("SetEXPModifier", (void(Lua_Client::*)(uint32,double))&Lua_Client::SetEXPModifier) + .def("SetEbonCrystals", (void(Lua_Client::*)(uint32))&Lua_Client::SetEbonCrystals) + .def("SetEndurance", (void(Lua_Client::*)(int))&Lua_Client::SetEndurance) + .def("SetFactionLevel", (void(Lua_Client::*)(uint32,uint32,int,int,int))&Lua_Client::SetFactionLevel) + .def("SetFactionLevel2", (void(Lua_Client::*)(uint32,int,int,int,int,int,int))&Lua_Client::SetFactionLevel2) + .def("SetFeigned", (void(Lua_Client::*)(bool))&Lua_Client::SetFeigned) + .def("SetGM", (void(Lua_Client::*)(bool))&Lua_Client::SetGM) + .def("SetGMStatus", (void(Lua_Client::*)(int32))&Lua_Client::SetGMStatus) + .def("SetHideMe", (void(Lua_Client::*)(bool))&Lua_Client::SetHideMe) + .def("SetHorseId", (void(Lua_Client::*)(int))&Lua_Client::SetHorseId) + .def("SetHunger", (void(Lua_Client::*)(int))&Lua_Client::SetHunger) + .def("SetIPExemption", (void(Lua_Client::*)(int))&Lua_Client::SetIPExemption) + .def("SetLanguageSkill", (void(Lua_Client::*)(int,int))&Lua_Client::SetLanguageSkill) + .def("SetMaterial", (void(Lua_Client::*)(int,uint32))&Lua_Client::SetMaterial) + .def("SetPVP", (void(Lua_Client::*)(bool))&Lua_Client::SetPVP) + .def("SetPrimaryWeaponOrnamentation", (void(Lua_Client::*)(uint32))&Lua_Client::SetPrimaryWeaponOrnamentation) + .def("SetRadiantCrystals", (void(Lua_Client::*)(uint32))&Lua_Client::SetRadiantCrystals) + .def("SetSecondaryWeaponOrnamentation", (void(Lua_Client::*)(uint32))&Lua_Client::SetSecondaryWeaponOrnamentation) + .def("SetSkill", (void(Lua_Client::*)(int,int))&Lua_Client::SetSkill) + .def("SetSkillPoints", (void(Lua_Client::*)(int))&Lua_Client::SetSkillPoints) + .def("SetStartZone", (void(Lua_Client::*)(int))&Lua_Client::SetStartZone) + .def("SetStartZone", (void(Lua_Client::*)(int,float))&Lua_Client::SetStartZone) + .def("SetStartZone", (void(Lua_Client::*)(int,float,float))&Lua_Client::SetStartZone) + .def("SetStartZone", (void(Lua_Client::*)(int,float,float,float))&Lua_Client::SetStartZone) + .def("SetStats", (void(Lua_Client::*)(int,int))&Lua_Client::SetStats) + .def("SetThirst", (void(Lua_Client::*)(int))&Lua_Client::SetThirst) + .def("SetTint", (void(Lua_Client::*)(int,uint32))&Lua_Client::SetTint) + .def("SetTitleSuffix", (void(Lua_Client::*)(const char *))&Lua_Client::SetTitleSuffix) + .def("SetZoneFlag", (void(Lua_Client::*)(int))&Lua_Client::SetZoneFlag) + .def("Signal", (void(Lua_Client::*)(uint32))&Lua_Client::Signal) + .def("Sit", (void(Lua_Client::*)(void))&Lua_Client::Sit) + .def("Stand", (void(Lua_Client::*)(void))&Lua_Client::Stand) + .def("SummonBaggedItems", (void(Lua_Client::*)(uint32,luabind::adl::object))&Lua_Client::SummonBaggedItems) + .def("SummonItem", (void(Lua_Client::*)(uint32))&Lua_Client::SummonItem) + .def("SummonItem", (void(Lua_Client::*)(uint32,int))&Lua_Client::SummonItem) + .def("SummonItem", (void(Lua_Client::*)(uint32,int,uint32))&Lua_Client::SummonItem) + .def("SummonItem", (void(Lua_Client::*)(uint32,int,uint32,uint32))&Lua_Client::SummonItem) + .def("SummonItem", (void(Lua_Client::*)(uint32,int,uint32,uint32,uint32))&Lua_Client::SummonItem) + .def("SummonItem", (void(Lua_Client::*)(uint32,int,uint32,uint32,uint32,uint32))&Lua_Client::SummonItem) + .def("SummonItem", (void(Lua_Client::*)(uint32,int,uint32,uint32,uint32,uint32,uint32))&Lua_Client::SummonItem) + .def("SummonItem", (void(Lua_Client::*)(uint32,int,uint32,uint32,uint32,uint32,uint32,bool))&Lua_Client::SummonItem) + .def("SummonItem", (void(Lua_Client::*)(uint32,int,uint32,uint32,uint32,uint32,uint32,bool,int))&Lua_Client::SummonItem) + .def("TGB", (bool(Lua_Client::*)(void))&Lua_Client::TGB) + .def("TakeMoneyFromPP", (bool(Lua_Client::*)(uint64))&Lua_Client::TakeMoneyFromPP) + .def("TakeMoneyFromPP", (bool(Lua_Client::*)(uint64,bool))&Lua_Client::TakeMoneyFromPP) + .def("Thirsty", (bool(Lua_Client::*)(void))&Lua_Client::Thirsty) + .def("TrainDisc", (void(Lua_Client::*)(int))&Lua_Client::TrainDisc) + .def("TrainDiscBySpellID", (void(Lua_Client::*)(int32))&Lua_Client::TrainDiscBySpellID) + .def("UnFreeze", (void(Lua_Client::*)(void))&Lua_Client::UnFreeze) + .def("Undye", (void(Lua_Client::*)(void))&Lua_Client::Undye) + .def("UnmemSpell", (void(Lua_Client::*)(int))&Lua_Client::UnmemSpell) + .def("UnmemSpell", (void(Lua_Client::*)(int,bool))&Lua_Client::UnmemSpell) + .def("UnmemSpellAll", (void(Lua_Client::*)(bool))&Lua_Client::UnmemSpellAll) + .def("UnmemSpellAll", (void(Lua_Client::*)(void))&Lua_Client::UnmemSpellAll) + .def("UnmemSpellBySpellID", (void(Lua_Client::*)(int32))&Lua_Client::UnmemSpellBySpellID) + .def("UnscribeSpell", (void(Lua_Client::*)(int))&Lua_Client::UnscribeSpell) + .def("UnscribeSpell", (void(Lua_Client::*)(int,bool))&Lua_Client::UnscribeSpell) + .def("UnscribeSpellAll", (void(Lua_Client::*)(bool))&Lua_Client::UnscribeSpellAll) + .def("UnscribeSpellAll", (void(Lua_Client::*)(void))&Lua_Client::UnscribeSpellAll) + .def("UntrainDisc", (void(Lua_Client::*)(int))&Lua_Client::UntrainDisc) + .def("UntrainDisc", (void(Lua_Client::*)(int,bool))&Lua_Client::UntrainDisc) + .def("UntrainDiscAll", (void(Lua_Client::*)(bool))&Lua_Client::UntrainDiscAll) + .def("UntrainDiscAll", (void(Lua_Client::*)(void))&Lua_Client::UntrainDiscAll) + .def("UntrainDiscBySpellID", (void(Lua_Client::*)(uint16))&Lua_Client::UntrainDiscBySpellID) + .def("UntrainDiscBySpellID", (void(Lua_Client::*)(uint16,bool))&Lua_Client::UntrainDiscBySpellID) + .def("UpdateGroupAAs", (void(Lua_Client::*)(int,uint32))&Lua_Client::UpdateGroupAAs) + .def("UpdateLDoNPoints", (void(Lua_Client::*)(uint32,int))&Lua_Client::UpdateLDoNPoints) + .def("UpdateTaskActivity", (void(Lua_Client::*)(int,int,int))&Lua_Client::UpdateTaskActivity) + .def("UseDiscipline", (bool(Lua_Client::*)(int,int))&Lua_Client::UseDiscipline) + .def("WorldKick", (void(Lua_Client::*)(void))&Lua_Client::WorldKick); } luabind::scope lua_register_inventory_where() { diff --git a/zone/lua_corpse.cpp b/zone/lua_corpse.cpp index 29a9eb31c..d1f5039c7 100644 --- a/zone/lua_corpse.cpp +++ b/zone/lua_corpse.cpp @@ -201,50 +201,51 @@ Lua_Corpse_Loot_List Lua_Corpse::GetLootList(lua_State* L) { luabind::scope lua_register_corpse() { return luabind::class_("Corpse") - .def(luabind::constructor<>()) - .property("null", &Lua_Corpse::Null) - .property("valid", &Lua_Corpse::Valid) - .def("GetCharID", (uint32(Lua_Corpse::*)(void))&Lua_Corpse::GetCharID) - .def("GetDecayTime", (uint32(Lua_Corpse::*)(void))&Lua_Corpse::GetDecayTime) - .def("Lock", (void(Lua_Corpse::*)(void))&Lua_Corpse::Lock) - .def("UnLock", (void(Lua_Corpse::*)(void))&Lua_Corpse::UnLock) - .def("IsLocked", (bool(Lua_Corpse::*)(void))&Lua_Corpse::IsLocked) - .def("ResetLooter", (void(Lua_Corpse::*)(void))&Lua_Corpse::ResetLooter) - .def("GetDBID", (uint32(Lua_Corpse::*)(void))&Lua_Corpse::GetDBID) - .def("IsRezzed", (bool(Lua_Corpse::*)(void))&Lua_Corpse::IsRezzed) - .def("GetOwnerName", (const char *(Lua_Corpse::*)(void))&Lua_Corpse::GetOwnerName) - .def("Save", (bool(Lua_Corpse::*)(void))&Lua_Corpse::Save) - .def("Delete", (void(Lua_Corpse::*)(void))&Lua_Corpse::Delete) - .def("Bury", (void(Lua_Corpse::*)(void))&Lua_Corpse::Bury) - .def("Depop", (void(Lua_Corpse::*)(void))&Lua_Corpse::Depop) - .def("CountItems", (uint32(Lua_Corpse::*)(void))&Lua_Corpse::CountItems) - .def("AddItem", (void(Lua_Corpse::*)(uint32, uint16, int16, uint32, uint32, uint32, uint32, uint32))&Lua_Corpse::AddItem) - .def("GetWornItem", (uint32(Lua_Corpse::*)(int16))&Lua_Corpse::GetWornItem) - .def("RemoveItem", (void(Lua_Corpse::*)(uint16))&Lua_Corpse::RemoveItem) - .def("SetCash", (void(Lua_Corpse::*)(uint32, uint32, uint32, uint32))&Lua_Corpse::SetCash) - .def("RemoveCash", (void(Lua_Corpse::*)(void))&Lua_Corpse::RemoveCash) - .def("IsEmpty", (bool(Lua_Corpse::*)(void))&Lua_Corpse::IsEmpty) - .def("SetDecayTimer", (void(Lua_Corpse::*)(uint32))&Lua_Corpse::SetDecayTimer) - .def("CanMobLoot", (bool(Lua_Corpse::*)(int))&Lua_Corpse::CanMobLoot) - .def("AllowMobLoot", (void(Lua_Corpse::*)(Lua_Mob, uint8))&Lua_Corpse::AllowMobLoot) - .def("Summon", (bool(Lua_Corpse::*)(Lua_Client, bool, bool))&Lua_Corpse::Summon) - .def("GetCopper", (uint32(Lua_Corpse::*)(void))&Lua_Corpse::GetCopper) - .def("GetSilver", (uint32(Lua_Corpse::*)(void))&Lua_Corpse::GetSilver) - .def("GetGold", (uint32(Lua_Corpse::*)(void))&Lua_Corpse::GetGold) - .def("GetPlatinum", (uint32(Lua_Corpse::*)(void))&Lua_Corpse::GetPlatinum) - .def("AddLooter", (void(Lua_Corpse::*)(Lua_Mob))&Lua_Corpse::AddLooter) - .def("HasItem", (bool(Lua_Corpse::*)(uint32))&Lua_Corpse::HasItem) - .def("CountItem", (uint16(Lua_Corpse::*)(uint32))&Lua_Corpse::CountItem) - .def("GetItemIDBySlot", (uint32(Lua_Corpse::*)(uint16))&Lua_Corpse::GetItemIDBySlot) - .def("GetFirstSlotByItemID", (uint16(Lua_Corpse::*)(uint32))&Lua_Corpse::GetFirstSlotByItemID) - .def("RemoveItemByID", (void(Lua_Corpse::*)(uint32))&Lua_Corpse::RemoveItemByID) - .def("RemoveItemByID", (void(Lua_Corpse::*)(uint32,int))&Lua_Corpse::RemoveItemByID) - .def("GetLootList", (Lua_Corpse_Loot_List(Lua_Corpse::*)(lua_State* L))&Lua_Corpse::GetLootList); + .def(luabind::constructor<>()) + .property("null", &Lua_Corpse::Null) + .property("valid", &Lua_Corpse::Valid) + .def("AddItem", (void(Lua_Corpse::*)(uint32, uint16, int16, uint32, uint32, uint32, uint32, uint32))&Lua_Corpse::AddItem) + .def("AddLooter", (void(Lua_Corpse::*)(Lua_Mob))&Lua_Corpse::AddLooter) + .def("AllowMobLoot", (void(Lua_Corpse::*)(Lua_Mob, uint8))&Lua_Corpse::AllowMobLoot) + .def("Bury", (void(Lua_Corpse::*)(void))&Lua_Corpse::Bury) + .def("CanMobLoot", (bool(Lua_Corpse::*)(int))&Lua_Corpse::CanMobLoot) + .def("CountItem", (uint16(Lua_Corpse::*)(uint32))&Lua_Corpse::CountItem) + .def("CountItems", (uint32(Lua_Corpse::*)(void))&Lua_Corpse::CountItems) + .def("Delete", (void(Lua_Corpse::*)(void))&Lua_Corpse::Delete) + .def("Depop", (void(Lua_Corpse::*)(void))&Lua_Corpse::Depop) + .def("GetCharID", (uint32(Lua_Corpse::*)(void))&Lua_Corpse::GetCharID) + .def("GetCopper", (uint32(Lua_Corpse::*)(void))&Lua_Corpse::GetCopper) + .def("GetDBID", (uint32(Lua_Corpse::*)(void))&Lua_Corpse::GetDBID) + .def("GetDecayTime", (uint32(Lua_Corpse::*)(void))&Lua_Corpse::GetDecayTime) + .def("GetFirstSlotByItemID", (uint16(Lua_Corpse::*)(uint32))&Lua_Corpse::GetFirstSlotByItemID) + .def("GetGold", (uint32(Lua_Corpse::*)(void))&Lua_Corpse::GetGold) + .def("GetItemIDBySlot", (uint32(Lua_Corpse::*)(uint16))&Lua_Corpse::GetItemIDBySlot) + .def("GetLootList", (Lua_Corpse_Loot_List(Lua_Corpse::*)(lua_State* L))&Lua_Corpse::GetLootList) + .def("GetOwnerName", (const char *(Lua_Corpse::*)(void))&Lua_Corpse::GetOwnerName) + .def("GetPlatinum", (uint32(Lua_Corpse::*)(void))&Lua_Corpse::GetPlatinum) + .def("GetSilver", (uint32(Lua_Corpse::*)(void))&Lua_Corpse::GetSilver) + .def("GetWornItem", (uint32(Lua_Corpse::*)(int16))&Lua_Corpse::GetWornItem) + .def("HasItem", (bool(Lua_Corpse::*)(uint32))&Lua_Corpse::HasItem) + .def("IsEmpty", (bool(Lua_Corpse::*)(void))&Lua_Corpse::IsEmpty) + .def("IsLocked", (bool(Lua_Corpse::*)(void))&Lua_Corpse::IsLocked) + .def("IsRezzed", (bool(Lua_Corpse::*)(void))&Lua_Corpse::IsRezzed) + .def("Lock", (void(Lua_Corpse::*)(void))&Lua_Corpse::Lock) + .def("RemoveCash", (void(Lua_Corpse::*)(void))&Lua_Corpse::RemoveCash) + .def("RemoveItem", (void(Lua_Corpse::*)(uint16))&Lua_Corpse::RemoveItem) + .def("RemoveItemByID", (void(Lua_Corpse::*)(uint32))&Lua_Corpse::RemoveItemByID) + .def("RemoveItemByID", (void(Lua_Corpse::*)(uint32,int))&Lua_Corpse::RemoveItemByID) + .def("ResetLooter", (void(Lua_Corpse::*)(void))&Lua_Corpse::ResetLooter) + .def("Save", (bool(Lua_Corpse::*)(void))&Lua_Corpse::Save) + .def("SetCash", (void(Lua_Corpse::*)(uint32, uint32, uint32, uint32))&Lua_Corpse::SetCash) + .def("SetDecayTimer", (void(Lua_Corpse::*)(uint32))&Lua_Corpse::SetDecayTimer) + .def("Summon", (bool(Lua_Corpse::*)(Lua_Client, bool, bool))&Lua_Corpse::Summon) + .def("UnLock", (void(Lua_Corpse::*)(void))&Lua_Corpse::UnLock); + } luabind::scope lua_register_corpse_loot_list() { return luabind::class_("CorpseLootList") - .def_readwrite("entries", &Lua_Corpse_Loot_List::entries, luabind::return_stl_iterator); + .def_readwrite("entries", &Lua_Corpse_Loot_List::entries, luabind::return_stl_iterator); } #endif diff --git a/zone/lua_door.cpp b/zone/lua_door.cpp index 1b39d3e54..bb10e4035 100644 --- a/zone/lua_door.cpp +++ b/zone/lua_door.cpp @@ -178,41 +178,41 @@ void Lua_Door::ForceClose(Lua_Mob sender, bool alt_mode) { luabind::scope lua_register_door() { return luabind::class_("Door") - .def(luabind::constructor<>()) - .property("null", &Lua_Door::Null) - .property("valid", &Lua_Door::Valid) - .def("SetDoorName", (void(Lua_Door::*)(const char*))&Lua_Door::SetDoorName) - .def("GetDoorName", (const char*(Lua_Door::*)(void))&Lua_Door::GetDoorName) - .def("GetX", (float(Lua_Door::*)(void))&Lua_Door::GetX) - .def("GetY", (float(Lua_Door::*)(void))&Lua_Door::GetY) - .def("GetZ", (float(Lua_Door::*)(void))&Lua_Door::GetZ) - .def("GetHeading", (float(Lua_Door::*)(void))&Lua_Door::GetHeading) - .def("SetX", (void(Lua_Door::*)(float))&Lua_Door::SetX) - .def("SetY", (void(Lua_Door::*)(float))&Lua_Door::SetY) - .def("SetZ", (void(Lua_Door::*)(float))&Lua_Door::SetZ) - .def("SetHeading", (void(Lua_Door::*)(float))&Lua_Door::SetHeading) - .def("SetLocation", (void(Lua_Door::*)(float,float,float))&Lua_Door::SetLocation) - .def("GetDoorDBID", (uint32(Lua_Door::*)(void))&Lua_Door::GetDoorDBID) - .def("GetDoorID", (uint32(Lua_Door::*)(void))&Lua_Door::GetDoorID) - .def("SetSize", (void(Lua_Door::*)(uint32))&Lua_Door::SetSize) - .def("GetSize", (uint32(Lua_Door::*)(void))&Lua_Door::GetSize) - .def("SetIncline", (void(Lua_Door::*)(uint32))&Lua_Door::SetIncline) - .def("GetIncline", (uint32(Lua_Door::*)(void))&Lua_Door::GetIncline) - .def("SetOpenType", (void(Lua_Door::*)(uint32))&Lua_Door::SetOpenType) - .def("GetOpenType", (uint32(Lua_Door::*)(void))&Lua_Door::GetOpenType) - .def("SetDisableTimer", (void(Lua_Door::*)(bool))&Lua_Door::SetDisableTimer) - .def("GetDisableTimer", (bool(Lua_Door::*)(void))&Lua_Door::GetDisableTimer) - .def("SetLockPick", (void(Lua_Door::*)(uint32))&Lua_Door::SetLockPick) - .def("GetLockPick", (uint32(Lua_Door::*)(void))&Lua_Door::GetLockPick) - .def("SetKeyItem", (void(Lua_Door::*)(uint32))&Lua_Door::SetKeyItem) - .def("GetKeyItem", (uint32(Lua_Door::*)(void))&Lua_Door::GetKeyItem) - .def("SetNoKeyring", (void(Lua_Door::*)(int))&Lua_Door::SetNoKeyring) - .def("GetNoKeyring", (int(Lua_Door::*)(void))&Lua_Door::GetNoKeyring) - .def("CreateDatabaseEntry", (void(Lua_Door::*)(void))&Lua_Door::CreateDatabaseEntry) - .def("ForceOpen", (void(Lua_Door::*)(Lua_Mob))&Lua_Door::ForceOpen) - .def("ForceOpen", (void(Lua_Door::*)(Lua_Mob,bool))&Lua_Door::ForceOpen) - .def("ForceClose", (void(Lua_Door::*)(Lua_Mob))&Lua_Door::ForceClose) - .def("ForceClose", (void(Lua_Door::*)(Lua_Mob,bool))&Lua_Door::ForceClose); + .def(luabind::constructor<>()) + .property("null", &Lua_Door::Null) + .property("valid", &Lua_Door::Valid) + .def("CreateDatabaseEntry", (void(Lua_Door::*)(void))&Lua_Door::CreateDatabaseEntry) + .def("ForceClose", (void(Lua_Door::*)(Lua_Mob))&Lua_Door::ForceClose) + .def("ForceClose", (void(Lua_Door::*)(Lua_Mob,bool))&Lua_Door::ForceClose) + .def("ForceOpen", (void(Lua_Door::*)(Lua_Mob))&Lua_Door::ForceOpen) + .def("ForceOpen", (void(Lua_Door::*)(Lua_Mob,bool))&Lua_Door::ForceOpen) + .def("GetDisableTimer", (bool(Lua_Door::*)(void))&Lua_Door::GetDisableTimer) + .def("GetDoorDBID", (uint32(Lua_Door::*)(void))&Lua_Door::GetDoorDBID) + .def("GetDoorID", (uint32(Lua_Door::*)(void))&Lua_Door::GetDoorID) + .def("GetDoorName", (const char*(Lua_Door::*)(void))&Lua_Door::GetDoorName) + .def("GetHeading", (float(Lua_Door::*)(void))&Lua_Door::GetHeading) + .def("GetIncline", (uint32(Lua_Door::*)(void))&Lua_Door::GetIncline) + .def("GetKeyItem", (uint32(Lua_Door::*)(void))&Lua_Door::GetKeyItem) + .def("GetLockPick", (uint32(Lua_Door::*)(void))&Lua_Door::GetLockPick) + .def("GetNoKeyring", (int(Lua_Door::*)(void))&Lua_Door::GetNoKeyring) + .def("GetOpenType", (uint32(Lua_Door::*)(void))&Lua_Door::GetOpenType) + .def("GetSize", (uint32(Lua_Door::*)(void))&Lua_Door::GetSize) + .def("GetX", (float(Lua_Door::*)(void))&Lua_Door::GetX) + .def("GetY", (float(Lua_Door::*)(void))&Lua_Door::GetY) + .def("GetZ", (float(Lua_Door::*)(void))&Lua_Door::GetZ) + .def("SetDisableTimer", (void(Lua_Door::*)(bool))&Lua_Door::SetDisableTimer) + .def("SetDoorName", (void(Lua_Door::*)(const char*))&Lua_Door::SetDoorName) + .def("SetHeading", (void(Lua_Door::*)(float))&Lua_Door::SetHeading) + .def("SetIncline", (void(Lua_Door::*)(uint32))&Lua_Door::SetIncline) + .def("SetKeyItem", (void(Lua_Door::*)(uint32))&Lua_Door::SetKeyItem) + .def("SetLocation", (void(Lua_Door::*)(float,float,float))&Lua_Door::SetLocation) + .def("SetLockPick", (void(Lua_Door::*)(uint32))&Lua_Door::SetLockPick) + .def("SetNoKeyring", (void(Lua_Door::*)(int))&Lua_Door::SetNoKeyring) + .def("SetOpenType", (void(Lua_Door::*)(uint32))&Lua_Door::SetOpenType) + .def("SetSize", (void(Lua_Door::*)(uint32))&Lua_Door::SetSize) + .def("SetX", (void(Lua_Door::*)(float))&Lua_Door::SetX) + .def("SetY", (void(Lua_Door::*)(float))&Lua_Door::SetY) + .def("SetZ", (void(Lua_Door::*)(float))&Lua_Door::SetZ); } #endif diff --git a/zone/lua_entity.cpp b/zone/lua_entity.cpp index 11ac06199..045a7e6a7 100644 --- a/zone/lua_entity.cpp +++ b/zone/lua_entity.cpp @@ -132,32 +132,32 @@ Lua_Bot Lua_Entity::CastToBot() { luabind::scope lua_register_entity() { return luabind::class_("Entity") - .def(luabind::constructor<>()) - .property("null", &Lua_Entity::Null) - .property("valid", &Lua_Entity::Valid) - .def("IsClient", &Lua_Entity::IsClient) - .def("IsNPC", &Lua_Entity::IsNPC) - .def("IsMob", &Lua_Entity::IsMob) - .def("IsMerc", &Lua_Entity::IsMerc) - .def("IsCorpse", &Lua_Entity::IsCorpse) - .def("IsPlayerCorpse", &Lua_Entity::IsPlayerCorpse) - .def("IsNPCCorpse", &Lua_Entity::IsNPCCorpse) - .def("IsObject", &Lua_Entity::IsObject) - .def("IsDoor", &Lua_Entity::IsDoor) - .def("IsTrap", &Lua_Entity::IsTrap) - .def("IsBeacon", &Lua_Entity::IsBeacon) - .def("IsEncounter", &Lua_Entity::IsEncounter) - .def("IsBot", &Lua_Entity::IsBot) - .def("GetID", &Lua_Entity::GetID) - .def("CastToClient", &Lua_Entity::CastToClient) + .def(luabind::constructor<>()) + .property("null", &Lua_Entity::Null) + .property("valid", &Lua_Entity::Valid) #ifdef BOTS - .def("CastToBot", &Lua_Entity::CastToBot) + .def("CastToBot", &Lua_Entity::CastToBot) #endif - .def("CastToNPC", &Lua_Entity::CastToNPC) - .def("CastToMob", &Lua_Entity::CastToMob) - .def("CastToCorpse", &Lua_Entity::CastToCorpse) - .def("CastToObject", &Lua_Entity::CastToObject) - .def("CastToDoor", &Lua_Entity::CastToDoor); + .def("CastToClient", &Lua_Entity::CastToClient) + .def("CastToCorpse", &Lua_Entity::CastToCorpse) + .def("CastToDoor", &Lua_Entity::CastToDoor) + .def("CastToMob", &Lua_Entity::CastToMob) + .def("CastToNPC", &Lua_Entity::CastToNPC) + .def("CastToObject", &Lua_Entity::CastToObject) + .def("GetID", &Lua_Entity::GetID) + .def("IsBeacon", &Lua_Entity::IsBeacon) + .def("IsBot", &Lua_Entity::IsBot) + .def("IsClient", &Lua_Entity::IsClient) + .def("IsCorpse", &Lua_Entity::IsCorpse) + .def("IsDoor", &Lua_Entity::IsDoor) + .def("IsEncounter", &Lua_Entity::IsEncounter) + .def("IsMerc", &Lua_Entity::IsMerc) + .def("IsMob", &Lua_Entity::IsMob) + .def("IsNPC", &Lua_Entity::IsNPC) + .def("IsNPCCorpse", &Lua_Entity::IsNPCCorpse) + .def("IsObject", &Lua_Entity::IsObject) + .def("IsPlayerCorpse", &Lua_Entity::IsPlayerCorpse) + .def("IsTrap", &Lua_Entity::IsTrap); } #endif diff --git a/zone/lua_entity_list.cpp b/zone/lua_entity_list.cpp index 9d6a812ed..9a9996d96 100644 --- a/zone/lua_entity_list.cpp +++ b/zone/lua_entity_list.cpp @@ -477,119 +477,119 @@ void Lua_EntityList::ChannelMessage(Lua_Mob from, int channel_num, int language, luabind::scope lua_register_entity_list() { return luabind::class_("EntityList") - .def(luabind::constructor<>()) - .property("null", &Lua_EntityList::Null) - .property("valid", &Lua_EntityList::Valid) - .def("GetMobID", (Lua_Mob(Lua_EntityList::*)(int))&Lua_EntityList::GetMobID) - .def("GetMob", (Lua_Mob(Lua_EntityList::*)(const char*))&Lua_EntityList::GetMob) - .def("GetMob", (Lua_Mob(Lua_EntityList::*)(int))&Lua_EntityList::GetMob) - .def("GetMobByNpcTypeID", (Lua_Mob(Lua_EntityList::*)(int))&Lua_EntityList::GetMobByNpcTypeID) - .def("IsMobSpawnedByNpcTypeID", (bool(Lua_EntityList::*)(int))&Lua_EntityList::IsMobSpawnedByNpcTypeID) - .def("GetNPCByID", (Lua_NPC(Lua_EntityList::*)(int))&Lua_EntityList::GetNPCByID) - .def("GetNPCByNPCTypeID", (Lua_NPC(Lua_EntityList::*)(int))&Lua_EntityList::GetNPCByNPCTypeID) - .def("GetNPCBySpawnID", (Lua_NPC(Lua_EntityList::*)(int))&Lua_EntityList::GetNPCBySpawnID) - .def("GetClientByName", (Lua_Client(Lua_EntityList::*)(const char*))&Lua_EntityList::GetClientByName) - .def("GetClientByAccID", (Lua_Client(Lua_EntityList::*)(uint32))&Lua_EntityList::GetClientByAccID) - .def("GetClientByID", (Lua_Client(Lua_EntityList::*)(int))&Lua_EntityList::GetClientByID) - .def("GetClientByCharID", (Lua_Client(Lua_EntityList::*)(uint32))&Lua_EntityList::GetClientByCharID) - .def("GetClientByWID", (Lua_Client(Lua_EntityList::*)(uint32))&Lua_EntityList::GetClientByWID) - .def("GetObjectByID", (Lua_Object(Lua_EntityList::*)(int))&Lua_EntityList::GetObjectByID) - .def("GetObjectByDBID", (Lua_Object(Lua_EntityList::*)(uint32))&Lua_EntityList::GetObjectByDBID) - .def("GetDoorsByID", (Lua_Door(Lua_EntityList::*)(int))&Lua_EntityList::GetDoorsByID) - .def("GetDoorsByDBID", (Lua_Door(Lua_EntityList::*)(uint32))&Lua_EntityList::GetDoorsByDBID) - .def("GetDoorsByDoorID", (Lua_Door(Lua_EntityList::*)(uint32))&Lua_EntityList::GetDoorsByDoorID) - .def("FindDoor", (Lua_Door(Lua_EntityList::*)(uint32))&Lua_EntityList::FindDoor) - .def("GetGroupByMob", (Lua_Group(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::GetGroupByMob) - .def("GetGroupByClient", (Lua_Group(Lua_EntityList::*)(Lua_Client))&Lua_EntityList::GetGroupByClient) - .def("GetGroupByID", (Lua_Group(Lua_EntityList::*)(int))&Lua_EntityList::GetGroupByID) - .def("GetGroupByLeaderName", (Lua_Group(Lua_EntityList::*)(const char*))&Lua_EntityList::GetGroupByLeaderName) - .def("GetRaidByID", (Lua_Raid(Lua_EntityList::*)(int))&Lua_EntityList::GetRaidByID) - .def("GetRaidByClient", (Lua_Raid(Lua_EntityList::*)(Lua_Client))&Lua_EntityList::GetRaidByClient) - .def("GetCorpseByOwner", (Lua_Corpse(Lua_EntityList::*)(Lua_Client))&Lua_EntityList::GetCorpseByOwner) - .def("GetCorpseByID", (Lua_Corpse(Lua_EntityList::*)(int))&Lua_EntityList::GetCorpseByID) - .def("GetCorpseByName", (Lua_Corpse(Lua_EntityList::*)(const char*))&Lua_EntityList::GetCorpseByName) - .def("GetSpawnByID", (Lua_Spawn(Lua_EntityList::*)(uint32))&Lua_EntityList::GetSpawnByID) - .def("ClearClientPetitionQueue", (void(Lua_EntityList::*)(void))&Lua_EntityList::ClearClientPetitionQueue) - .def("CanAddHateForMob", (bool(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::CanAddHateForMob) - .def("Message", (void(Lua_EntityList::*)(uint32, uint32, const char*))&Lua_EntityList::Message) - .def("MessageStatus", (void(Lua_EntityList::*)(uint32, uint32, uint32, const char*))&Lua_EntityList::MessageStatus) - .def("MessageClose", (void(Lua_EntityList::*)(Lua_Mob, bool, float, uint32, const char*))&Lua_EntityList::MessageClose) - .def("FilteredMessageClose", &Lua_EntityList::FilteredMessageClose) - .def("RemoveFromTargets", (void(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::RemoveFromTargets) - .def("RemoveFromTargets", (void(Lua_EntityList::*)(Lua_Mob, bool))&Lua_EntityList::RemoveFromTargets) - .def("ReplaceWithTarget", (void(Lua_EntityList::*)(Lua_Mob, Lua_Mob))&Lua_EntityList::ReplaceWithTarget) - .def("OpenDoorsNear", (void(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::OpenDoorsNear) - .def("MakeNameUnique", (std::string(Lua_EntityList::*)(const char*))&Lua_EntityList::MakeNameUnique) - .def("RemoveNumbers", (std::string(Lua_EntityList::*)(const char*))&Lua_EntityList::RemoveNumbers) - .def("SignalMobsByNPCID", (void(Lua_EntityList::*)(uint32, int))&Lua_EntityList::SignalMobsByNPCID) - .def("DeleteNPCCorpses", (int(Lua_EntityList::*)(void))&Lua_EntityList::DeleteNPCCorpses) - .def("DeletePlayerCorpses", (int(Lua_EntityList::*)(void))&Lua_EntityList::DeletePlayerCorpses) - .def("HalveAggro", (void(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::HalveAggro) - .def("DoubleAggro", (void(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::DoubleAggro) - .def("ClearFeignAggro", (void(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::ClearFeignAggro) - .def("Fighting", (bool(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::Fighting) - .def("RemoveFromHateLists", (void(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::RemoveFromHateLists) - .def("RemoveFromHateLists", (void(Lua_EntityList::*)(Lua_Mob, bool))&Lua_EntityList::RemoveFromHateLists) - .def("MessageGroup", (void(Lua_EntityList::*)(Lua_Mob, bool, uint32, const char*))&Lua_EntityList::MessageGroup) - .def("GetRandomClient", (Lua_Client(Lua_EntityList::*)(float, float, float, float))&Lua_EntityList::GetRandomClient) - .def("GetRandomClient", (Lua_Client(Lua_EntityList::*)(float, float, float, float, Lua_Client))&Lua_EntityList::GetRandomClient) - .def("GetMobList", (Lua_Mob_List(Lua_EntityList::*)(void))&Lua_EntityList::GetMobList) - .def("GetClientList", (Lua_Client_List(Lua_EntityList::*)(void))&Lua_EntityList::GetClientList) + .def(luabind::constructor<>()) + .property("null", &Lua_EntityList::Null) + .property("valid", &Lua_EntityList::Valid) + .def("CanAddHateForMob", (bool(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::CanAddHateForMob) + .def("ChannelMessage", (void(Lua_EntityList::*)(Lua_Mob, int, int, const char*))&Lua_EntityList::ChannelMessage) + .def("ClearClientPetitionQueue", (void(Lua_EntityList::*)(void))&Lua_EntityList::ClearClientPetitionQueue) + .def("ClearFeignAggro", (void(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::ClearFeignAggro) + .def("DeleteNPCCorpses", (int(Lua_EntityList::*)(void))&Lua_EntityList::DeleteNPCCorpses) + .def("DeletePlayerCorpses", (int(Lua_EntityList::*)(void))&Lua_EntityList::DeletePlayerCorpses) + .def("DoubleAggro", (void(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::DoubleAggro) + .def("Fighting", (bool(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::Fighting) + .def("FilteredMessageClose", &Lua_EntityList::FilteredMessageClose) + .def("FindDoor", (Lua_Door(Lua_EntityList::*)(uint32))&Lua_EntityList::FindDoor) #ifdef BOTS - .def("GetBotByID", (Lua_Bot(Lua_EntityList::*)(uint32))&Lua_EntityList::GetBotByID) - .def("GetBotByName", (Lua_Bot(Lua_EntityList::*)(std::string))&Lua_EntityList::GetBotByName) - .def("GetBotList", (Lua_Bot_List(Lua_EntityList::*)(void))&Lua_EntityList::GetBotList) + .def("GetBotByID", (Lua_Bot(Lua_EntityList::*)(uint32))&Lua_EntityList::GetBotByID) + .def("GetBotByName", (Lua_Bot(Lua_EntityList::*)(std::string))&Lua_EntityList::GetBotByName) + .def("GetBotList", (Lua_Bot_List(Lua_EntityList::*)(void))&Lua_EntityList::GetBotList) #endif - .def("GetShuffledClientList", (Lua_Client_List(Lua_EntityList::*)(void))&Lua_EntityList::GetShuffledClientList) - .def("GetNPCList", (Lua_NPC_List(Lua_EntityList::*)(void))&Lua_EntityList::GetNPCList) - .def("GetCorpseList", (Lua_Corpse_List(Lua_EntityList::*)(void))&Lua_EntityList::GetCorpseList) - .def("GetObjectList", (Lua_Object_List(Lua_EntityList::*)(void))&Lua_EntityList::GetObjectList) - .def("GetDoorsList", (Lua_Doors_List(Lua_EntityList::*)(void))&Lua_EntityList::GetDoorsList) - .def("GetSpawnList", (Lua_Spawn_List(Lua_EntityList::*)(void))&Lua_EntityList::GetSpawnList) - .def("SignalAllClients", (void(Lua_EntityList::*)(int))&Lua_EntityList::SignalAllClients) - .def("ChannelMessage", (void(Lua_EntityList::*)(Lua_Mob, int, int, const char*))&Lua_EntityList::ChannelMessage); + .def("GetClientByAccID", (Lua_Client(Lua_EntityList::*)(uint32))&Lua_EntityList::GetClientByAccID) + .def("GetClientByCharID", (Lua_Client(Lua_EntityList::*)(uint32))&Lua_EntityList::GetClientByCharID) + .def("GetClientByID", (Lua_Client(Lua_EntityList::*)(int))&Lua_EntityList::GetClientByID) + .def("GetClientByName", (Lua_Client(Lua_EntityList::*)(const char*))&Lua_EntityList::GetClientByName) + .def("GetClientByWID", (Lua_Client(Lua_EntityList::*)(uint32))&Lua_EntityList::GetClientByWID) + .def("GetClientList", (Lua_Client_List(Lua_EntityList::*)(void))&Lua_EntityList::GetClientList) + .def("GetCorpseByID", (Lua_Corpse(Lua_EntityList::*)(int))&Lua_EntityList::GetCorpseByID) + .def("GetCorpseByName", (Lua_Corpse(Lua_EntityList::*)(const char*))&Lua_EntityList::GetCorpseByName) + .def("GetCorpseByOwner", (Lua_Corpse(Lua_EntityList::*)(Lua_Client))&Lua_EntityList::GetCorpseByOwner) + .def("GetCorpseList", (Lua_Corpse_List(Lua_EntityList::*)(void))&Lua_EntityList::GetCorpseList) + .def("GetDoorsByDBID", (Lua_Door(Lua_EntityList::*)(uint32))&Lua_EntityList::GetDoorsByDBID) + .def("GetDoorsByDoorID", (Lua_Door(Lua_EntityList::*)(uint32))&Lua_EntityList::GetDoorsByDoorID) + .def("GetDoorsByID", (Lua_Door(Lua_EntityList::*)(int))&Lua_EntityList::GetDoorsByID) + .def("GetDoorsList", (Lua_Doors_List(Lua_EntityList::*)(void))&Lua_EntityList::GetDoorsList) + .def("GetGroupByClient", (Lua_Group(Lua_EntityList::*)(Lua_Client))&Lua_EntityList::GetGroupByClient) + .def("GetGroupByID", (Lua_Group(Lua_EntityList::*)(int))&Lua_EntityList::GetGroupByID) + .def("GetGroupByLeaderName", (Lua_Group(Lua_EntityList::*)(const char*))&Lua_EntityList::GetGroupByLeaderName) + .def("GetGroupByMob", (Lua_Group(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::GetGroupByMob) + .def("GetMob", (Lua_Mob(Lua_EntityList::*)(const char*))&Lua_EntityList::GetMob) + .def("GetMob", (Lua_Mob(Lua_EntityList::*)(int))&Lua_EntityList::GetMob) + .def("GetMobByNpcTypeID", (Lua_Mob(Lua_EntityList::*)(int))&Lua_EntityList::GetMobByNpcTypeID) + .def("GetMobID", (Lua_Mob(Lua_EntityList::*)(int))&Lua_EntityList::GetMobID) + .def("GetMobList", (Lua_Mob_List(Lua_EntityList::*)(void))&Lua_EntityList::GetMobList) + .def("GetNPCByID", (Lua_NPC(Lua_EntityList::*)(int))&Lua_EntityList::GetNPCByID) + .def("GetNPCByNPCTypeID", (Lua_NPC(Lua_EntityList::*)(int))&Lua_EntityList::GetNPCByNPCTypeID) + .def("GetNPCBySpawnID", (Lua_NPC(Lua_EntityList::*)(int))&Lua_EntityList::GetNPCBySpawnID) + .def("GetNPCList", (Lua_NPC_List(Lua_EntityList::*)(void))&Lua_EntityList::GetNPCList) + .def("GetObjectByDBID", (Lua_Object(Lua_EntityList::*)(uint32))&Lua_EntityList::GetObjectByDBID) + .def("GetObjectByID", (Lua_Object(Lua_EntityList::*)(int))&Lua_EntityList::GetObjectByID) + .def("GetObjectList", (Lua_Object_List(Lua_EntityList::*)(void))&Lua_EntityList::GetObjectList) + .def("GetRaidByClient", (Lua_Raid(Lua_EntityList::*)(Lua_Client))&Lua_EntityList::GetRaidByClient) + .def("GetRaidByID", (Lua_Raid(Lua_EntityList::*)(int))&Lua_EntityList::GetRaidByID) + .def("GetRandomClient", (Lua_Client(Lua_EntityList::*)(float, float, float, float))&Lua_EntityList::GetRandomClient) + .def("GetRandomClient", (Lua_Client(Lua_EntityList::*)(float, float, float, float, Lua_Client))&Lua_EntityList::GetRandomClient) + .def("GetShuffledClientList", (Lua_Client_List(Lua_EntityList::*)(void))&Lua_EntityList::GetShuffledClientList) + .def("GetSpawnByID", (Lua_Spawn(Lua_EntityList::*)(uint32))&Lua_EntityList::GetSpawnByID) + .def("GetSpawnList", (Lua_Spawn_List(Lua_EntityList::*)(void))&Lua_EntityList::GetSpawnList) + .def("HalveAggro", (void(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::HalveAggro) + .def("IsMobSpawnedByNpcTypeID", (bool(Lua_EntityList::*)(int))&Lua_EntityList::IsMobSpawnedByNpcTypeID) + .def("MakeNameUnique", (std::string(Lua_EntityList::*)(const char*))&Lua_EntityList::MakeNameUnique) + .def("Message", (void(Lua_EntityList::*)(uint32, uint32, const char*))&Lua_EntityList::Message) + .def("MessageClose", (void(Lua_EntityList::*)(Lua_Mob, bool, float, uint32, const char*))&Lua_EntityList::MessageClose) + .def("MessageGroup", (void(Lua_EntityList::*)(Lua_Mob, bool, uint32, const char*))&Lua_EntityList::MessageGroup) + .def("MessageStatus", (void(Lua_EntityList::*)(uint32, uint32, uint32, const char*))&Lua_EntityList::MessageStatus) + .def("OpenDoorsNear", (void(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::OpenDoorsNear) + .def("RemoveFromHateLists", (void(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::RemoveFromHateLists) + .def("RemoveFromHateLists", (void(Lua_EntityList::*)(Lua_Mob, bool))&Lua_EntityList::RemoveFromHateLists) + .def("RemoveFromTargets", (void(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::RemoveFromTargets) + .def("RemoveFromTargets", (void(Lua_EntityList::*)(Lua_Mob, bool))&Lua_EntityList::RemoveFromTargets) + .def("RemoveNumbers", (std::string(Lua_EntityList::*)(const char*))&Lua_EntityList::RemoveNumbers) + .def("ReplaceWithTarget", (void(Lua_EntityList::*)(Lua_Mob, Lua_Mob))&Lua_EntityList::ReplaceWithTarget) + .def("SignalAllClients", (void(Lua_EntityList::*)(int))&Lua_EntityList::SignalAllClients) + .def("SignalMobsByNPCID", (void(Lua_EntityList::*)(uint32, int))&Lua_EntityList::SignalMobsByNPCID); } luabind::scope lua_register_mob_list() { return luabind::class_("MobList") - .def_readwrite("entries", &Lua_Mob_List::entries, luabind::return_stl_iterator); + .def_readwrite("entries", &Lua_Mob_List::entries, luabind::return_stl_iterator); } luabind::scope lua_register_client_list() { return luabind::class_("ClientList") - .def_readwrite("entries", &Lua_Client_List::entries, luabind::return_stl_iterator); + .def_readwrite("entries", &Lua_Client_List::entries, luabind::return_stl_iterator); } #ifdef BOTS luabind::scope lua_register_bot_list() { return luabind::class_("BotList") - .def_readwrite("entries", &Lua_Bot_List::entries, luabind::return_stl_iterator); + .def_readwrite("entries", &Lua_Bot_List::entries, luabind::return_stl_iterator); } #endif luabind::scope lua_register_npc_list() { return luabind::class_("NPCList") - .def_readwrite("entries", &Lua_NPC_List::entries, luabind::return_stl_iterator); + .def_readwrite("entries", &Lua_NPC_List::entries, luabind::return_stl_iterator); } luabind::scope lua_register_corpse_list() { return luabind::class_("CorpseList") - .def_readwrite("entries", &Lua_Corpse_List::entries, luabind::return_stl_iterator); + .def_readwrite("entries", &Lua_Corpse_List::entries, luabind::return_stl_iterator); } luabind::scope lua_register_object_list() { return luabind::class_("ObjectList") - .def_readwrite("entries", &Lua_Object_List::entries, luabind::return_stl_iterator); + .def_readwrite("entries", &Lua_Object_List::entries, luabind::return_stl_iterator); } luabind::scope lua_register_door_list() { return luabind::class_("DoorList") - .def_readwrite("entries", &Lua_Doors_List::entries, luabind::return_stl_iterator); + .def_readwrite("entries", &Lua_Doors_List::entries, luabind::return_stl_iterator); } luabind::scope lua_register_spawn_list() { return luabind::class_("SpawnList") - .def_readwrite("entries", &Lua_Spawn_List::entries, luabind::return_stl_iterator); + .def_readwrite("entries", &Lua_Spawn_List::entries, luabind::return_stl_iterator); } #endif diff --git a/zone/lua_expedition.cpp b/zone/lua_expedition.cpp index 063a68ce9..0ed63782b 100644 --- a/zone/lua_expedition.cpp +++ b/zone/lua_expedition.cpp @@ -1,23 +1,3 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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 - * - */ - #ifdef LUA_EQEMU #include "lua_expedition.h" @@ -249,59 +229,59 @@ void Lua_Expedition::UpdateLockoutDuration(std::string event_name, uint32_t dura luabind::scope lua_register_expedition() { return luabind::class_("Expedition") - .def(luabind::constructor<>()) - .property("null", &Lua_Expedition::Null) - .property("valid", &Lua_Expedition::Valid) - .def("AddLockout", (void(Lua_Expedition::*)(std::string, uint32_t))&Lua_Expedition::AddLockout) - .def("AddLockoutDuration", (void(Lua_Expedition::*)(std::string, int))&Lua_Expedition::AddLockoutDuration) - .def("AddLockoutDuration", (void(Lua_Expedition::*)(std::string, int, bool))&Lua_Expedition::AddLockoutDuration) - .def("AddReplayLockout", (void(Lua_Expedition::*)(uint32_t))&Lua_Expedition::AddReplayLockout) - .def("AddReplayLockoutDuration", (void(Lua_Expedition::*)(int))&Lua_Expedition::AddReplayLockoutDuration) - .def("AddReplayLockoutDuration", (void(Lua_Expedition::*)(int, bool))&Lua_Expedition::AddReplayLockoutDuration) - .def("GetDynamicZoneID", &Lua_Expedition::GetDynamicZoneID) - .def("GetID", (uint32_t(Lua_Expedition::*)(void))&Lua_Expedition::GetID) - .def("GetInstanceID", (int(Lua_Expedition::*)(void))&Lua_Expedition::GetInstanceID) - .def("GetLeaderName", (std::string(Lua_Expedition::*)(void))&Lua_Expedition::GetLeaderName) - .def("GetLockouts", &Lua_Expedition::GetLockouts) - .def("GetLootEventByNPCTypeID", (std::string(Lua_Expedition::*)(uint32_t))&Lua_Expedition::GetLootEventByNPCTypeID) - .def("GetLootEventBySpawnID", (std::string(Lua_Expedition::*)(uint32_t))&Lua_Expedition::GetLootEventBySpawnID) - .def("GetMemberCount", (uint32_t(Lua_Expedition::*)(void))&Lua_Expedition::GetMemberCount) - .def("GetMembers", &Lua_Expedition::GetMembers) - .def("GetName", (std::string(Lua_Expedition::*)(void))&Lua_Expedition::GetName) - .def("GetSecondsRemaining", (int(Lua_Expedition::*)(void))&Lua_Expedition::GetSecondsRemaining) - .def("GetUUID", (std::string(Lua_Expedition::*)(void))&Lua_Expedition::GetUUID) - .def("GetZoneID", (int(Lua_Expedition::*)(void))&Lua_Expedition::GetZoneID) - .def("GetZoneName", &Lua_Expedition::GetZoneName) - .def("GetZoneVersion", &Lua_Expedition::GetZoneVersion) - .def("HasLockout", (bool(Lua_Expedition::*)(std::string))&Lua_Expedition::HasLockout) - .def("HasReplayLockout", (bool(Lua_Expedition::*)(void))&Lua_Expedition::HasReplayLockout) - .def("IsLocked", &Lua_Expedition::IsLocked) - .def("RemoveCompass", (void(Lua_Expedition::*)(void))&Lua_Expedition::RemoveCompass) - .def("RemoveLockout", (void(Lua_Expedition::*)(std::string))&Lua_Expedition::RemoveLockout) - .def("SetCompass", (void(Lua_Expedition::*)(uint32_t, float, float, float))&Lua_Expedition::SetCompass) - .def("SetCompass", (void(Lua_Expedition::*)(std::string, float, float, float))&Lua_Expedition::SetCompass) - .def("SetLocked", (void(Lua_Expedition::*)(bool))&Lua_Expedition::SetLocked) - .def("SetLocked", (void(Lua_Expedition::*)(bool, int))&Lua_Expedition::SetLocked) - .def("SetLocked", (void(Lua_Expedition::*)(bool, int, uint32_t))&Lua_Expedition::SetLocked) - .def("SetLootEventByNPCTypeID", (void(Lua_Expedition::*)(uint32_t, std::string))&Lua_Expedition::SetLootEventByNPCTypeID) - .def("SetLootEventBySpawnID", (void(Lua_Expedition::*)(uint32_t, std::string))&Lua_Expedition::SetLootEventBySpawnID) - .def("SetReplayLockoutOnMemberJoin", (void(Lua_Expedition::*)(bool))&Lua_Expedition::SetReplayLockoutOnMemberJoin) - .def("SetSafeReturn", (void(Lua_Expedition::*)(uint32_t, float, float, float, float))&Lua_Expedition::SetSafeReturn) - .def("SetSafeReturn", (void(Lua_Expedition::*)(std::string, float, float, float, float))&Lua_Expedition::SetSafeReturn) - .def("SetSecondsRemaining", &Lua_Expedition::SetSecondsRemaining) - .def("SetZoneInLocation", (void(Lua_Expedition::*)(float, float, float, float))&Lua_Expedition::SetZoneInLocation) - .def("UpdateLockoutDuration", (void(Lua_Expedition::*)(std::string, uint32_t))&Lua_Expedition::UpdateLockoutDuration) - .def("UpdateLockoutDuration", (void(Lua_Expedition::*)(std::string, uint32_t, bool))&Lua_Expedition::UpdateLockoutDuration); + .def(luabind::constructor<>()) + .property("null", &Lua_Expedition::Null) + .property("valid", &Lua_Expedition::Valid) + .def("AddLockout", (void(Lua_Expedition::*)(std::string, uint32_t))&Lua_Expedition::AddLockout) + .def("AddLockoutDuration", (void(Lua_Expedition::*)(std::string, int))&Lua_Expedition::AddLockoutDuration) + .def("AddLockoutDuration", (void(Lua_Expedition::*)(std::string, int, bool))&Lua_Expedition::AddLockoutDuration) + .def("AddReplayLockout", (void(Lua_Expedition::*)(uint32_t))&Lua_Expedition::AddReplayLockout) + .def("AddReplayLockoutDuration", (void(Lua_Expedition::*)(int))&Lua_Expedition::AddReplayLockoutDuration) + .def("AddReplayLockoutDuration", (void(Lua_Expedition::*)(int, bool))&Lua_Expedition::AddReplayLockoutDuration) + .def("GetDynamicZoneID", &Lua_Expedition::GetDynamicZoneID) + .def("GetID", (uint32_t(Lua_Expedition::*)(void))&Lua_Expedition::GetID) + .def("GetInstanceID", (int(Lua_Expedition::*)(void))&Lua_Expedition::GetInstanceID) + .def("GetLeaderName", (std::string(Lua_Expedition::*)(void))&Lua_Expedition::GetLeaderName) + .def("GetLockouts", &Lua_Expedition::GetLockouts) + .def("GetLootEventByNPCTypeID", (std::string(Lua_Expedition::*)(uint32_t))&Lua_Expedition::GetLootEventByNPCTypeID) + .def("GetLootEventBySpawnID", (std::string(Lua_Expedition::*)(uint32_t))&Lua_Expedition::GetLootEventBySpawnID) + .def("GetMemberCount", (uint32_t(Lua_Expedition::*)(void))&Lua_Expedition::GetMemberCount) + .def("GetMembers", &Lua_Expedition::GetMembers) + .def("GetName", (std::string(Lua_Expedition::*)(void))&Lua_Expedition::GetName) + .def("GetSecondsRemaining", (int(Lua_Expedition::*)(void))&Lua_Expedition::GetSecondsRemaining) + .def("GetUUID", (std::string(Lua_Expedition::*)(void))&Lua_Expedition::GetUUID) + .def("GetZoneID", (int(Lua_Expedition::*)(void))&Lua_Expedition::GetZoneID) + .def("GetZoneName", &Lua_Expedition::GetZoneName) + .def("GetZoneVersion", &Lua_Expedition::GetZoneVersion) + .def("HasLockout", (bool(Lua_Expedition::*)(std::string))&Lua_Expedition::HasLockout) + .def("HasReplayLockout", (bool(Lua_Expedition::*)(void))&Lua_Expedition::HasReplayLockout) + .def("IsLocked", &Lua_Expedition::IsLocked) + .def("RemoveCompass", (void(Lua_Expedition::*)(void))&Lua_Expedition::RemoveCompass) + .def("RemoveLockout", (void(Lua_Expedition::*)(std::string))&Lua_Expedition::RemoveLockout) + .def("SetCompass", (void(Lua_Expedition::*)(uint32_t, float, float, float))&Lua_Expedition::SetCompass) + .def("SetCompass", (void(Lua_Expedition::*)(std::string, float, float, float))&Lua_Expedition::SetCompass) + .def("SetLocked", (void(Lua_Expedition::*)(bool))&Lua_Expedition::SetLocked) + .def("SetLocked", (void(Lua_Expedition::*)(bool, int))&Lua_Expedition::SetLocked) + .def("SetLocked", (void(Lua_Expedition::*)(bool, int, uint32_t))&Lua_Expedition::SetLocked) + .def("SetLootEventByNPCTypeID", (void(Lua_Expedition::*)(uint32_t, std::string))&Lua_Expedition::SetLootEventByNPCTypeID) + .def("SetLootEventBySpawnID", (void(Lua_Expedition::*)(uint32_t, std::string))&Lua_Expedition::SetLootEventBySpawnID) + .def("SetReplayLockoutOnMemberJoin", (void(Lua_Expedition::*)(bool))&Lua_Expedition::SetReplayLockoutOnMemberJoin) + .def("SetSafeReturn", (void(Lua_Expedition::*)(uint32_t, float, float, float, float))&Lua_Expedition::SetSafeReturn) + .def("SetSafeReturn", (void(Lua_Expedition::*)(std::string, float, float, float, float))&Lua_Expedition::SetSafeReturn) + .def("SetSecondsRemaining", &Lua_Expedition::SetSecondsRemaining) + .def("SetZoneInLocation", (void(Lua_Expedition::*)(float, float, float, float))&Lua_Expedition::SetZoneInLocation) + .def("UpdateLockoutDuration", (void(Lua_Expedition::*)(std::string, uint32_t))&Lua_Expedition::UpdateLockoutDuration) + .def("UpdateLockoutDuration", (void(Lua_Expedition::*)(std::string, uint32_t, bool))&Lua_Expedition::UpdateLockoutDuration); } luabind::scope lua_register_expedition_lock_messages() { return luabind::class_("ExpeditionLockMessage") - .enum_("constants") - [ - luabind::value("None", static_cast(ExpeditionLockMessage::None)), - luabind::value("Close", static_cast(ExpeditionLockMessage::Close)), - luabind::value("Begin", static_cast(ExpeditionLockMessage::Begin)) - ]; + .enum_("constants") + [ + luabind::value("None", static_cast(ExpeditionLockMessage::None)), + luabind::value("Close", static_cast(ExpeditionLockMessage::Close)), + luabind::value("Begin", static_cast(ExpeditionLockMessage::Begin)) + ]; } #endif // LUA_EQEMU diff --git a/zone/lua_group.cpp b/zone/lua_group.cpp index 2cac41418..668067b59 100644 --- a/zone/lua_group.cpp +++ b/zone/lua_group.cpp @@ -121,29 +121,29 @@ bool Lua_Group::DoesAnyMemberHaveExpeditionLockout(std::string expedition_name, luabind::scope lua_register_group() { return luabind::class_("Group") - .def(luabind::constructor<>()) - .property("null", &Lua_Group::Null) - .property("valid", &Lua_Group::Valid) - .def("DisbandGroup", (void(Lua_Group::*)(void))&Lua_Group::DisbandGroup) - .def("IsGroupMember", (bool(Lua_Group::*)(Lua_Mob))&Lua_Group::IsGroupMember) - .def("CastGroupSpell", (void(Lua_Group::*)(Lua_Mob,int))&Lua_Group::CastGroupSpell) - .def("SplitExp", (void(Lua_Group::*)(uint32,Lua_Mob))&Lua_Group::SplitExp) - .def("GroupMessage", (void(Lua_Group::*)(Lua_Mob,int,const char* message))&Lua_Group::GroupMessage) - .def("GetTotalGroupDamage", (uint32(Lua_Group::*)(Lua_Mob))&Lua_Group::GetTotalGroupDamage) - .def("SplitMoney", (void(Lua_Group::*)(uint32,uint32,uint32,uint32))&Lua_Group::SplitMoney) - .def("SplitMoney", (void(Lua_Group::*)(uint32,uint32,uint32,uint32,Lua_Client))&Lua_Group::SplitMoney) - .def("SetLeader", (void(Lua_Group::*)(Lua_Mob))&Lua_Group::SetLeader) - .def("GetLeader", (Lua_Mob(Lua_Group::*)(void))&Lua_Group::GetLeader) - .def("GetLeaderName", (const char*(Lua_Group::*)(void))&Lua_Group::GetLeaderName) - .def("IsLeader", (bool(Lua_Group::*)(Lua_Mob))&Lua_Group::IsLeader) - .def("GroupCount", (int(Lua_Group::*)(void))&Lua_Group::GroupCount) - .def("GetHighestLevel", (int(Lua_Group::*)(void))&Lua_Group::GetHighestLevel) - .def("GetLowestLevel", (int(Lua_Group::*)(void))&Lua_Group::GetLowestLevel) - .def("TeleportGroup", (void(Lua_Group::*)(Lua_Mob,uint32,uint32,float,float,float,float))&Lua_Group::TeleportGroup) - .def("GetID", (int(Lua_Group::*)(void))&Lua_Group::GetID) - .def("GetMember", (Lua_Mob(Lua_Group::*)(int))&Lua_Group::GetMember) - .def("DoesAnyMemberHaveExpeditionLockout", (bool(Lua_Group::*)(std::string, std::string))&Lua_Group::DoesAnyMemberHaveExpeditionLockout) - .def("DoesAnyMemberHaveExpeditionLockout", (bool(Lua_Group::*)(std::string, std::string, int))&Lua_Group::DoesAnyMemberHaveExpeditionLockout); + .def(luabind::constructor<>()) + .property("null", &Lua_Group::Null) + .property("valid", &Lua_Group::Valid) + .def("CastGroupSpell", (void(Lua_Group::*)(Lua_Mob,int))&Lua_Group::CastGroupSpell) + .def("DisbandGroup", (void(Lua_Group::*)(void))&Lua_Group::DisbandGroup) + .def("DoesAnyMemberHaveExpeditionLockout", (bool(Lua_Group::*)(std::string, std::string))&Lua_Group::DoesAnyMemberHaveExpeditionLockout) + .def("DoesAnyMemberHaveExpeditionLockout", (bool(Lua_Group::*)(std::string, std::string, int))&Lua_Group::DoesAnyMemberHaveExpeditionLockout) + .def("GetHighestLevel", (int(Lua_Group::*)(void))&Lua_Group::GetHighestLevel) + .def("GetID", (int(Lua_Group::*)(void))&Lua_Group::GetID) + .def("GetLeader", (Lua_Mob(Lua_Group::*)(void))&Lua_Group::GetLeader) + .def("GetLeaderName", (const char*(Lua_Group::*)(void))&Lua_Group::GetLeaderName) + .def("GetLowestLevel", (int(Lua_Group::*)(void))&Lua_Group::GetLowestLevel) + .def("GetMember", (Lua_Mob(Lua_Group::*)(int))&Lua_Group::GetMember) + .def("GetTotalGroupDamage", (uint32(Lua_Group::*)(Lua_Mob))&Lua_Group::GetTotalGroupDamage) + .def("GroupCount", (int(Lua_Group::*)(void))&Lua_Group::GroupCount) + .def("GroupMessage", (void(Lua_Group::*)(Lua_Mob,int,const char* message))&Lua_Group::GroupMessage) + .def("IsGroupMember", (bool(Lua_Group::*)(Lua_Mob))&Lua_Group::IsGroupMember) + .def("IsLeader", (bool(Lua_Group::*)(Lua_Mob))&Lua_Group::IsLeader) + .def("SetLeader", (void(Lua_Group::*)(Lua_Mob))&Lua_Group::SetLeader) + .def("SplitExp", (void(Lua_Group::*)(uint32,Lua_Mob))&Lua_Group::SplitExp) + .def("SplitMoney", (void(Lua_Group::*)(uint32,uint32,uint32,uint32))&Lua_Group::SplitMoney) + .def("SplitMoney", (void(Lua_Group::*)(uint32,uint32,uint32,uint32,Lua_Client))&Lua_Group::SplitMoney) + .def("TeleportGroup", (void(Lua_Group::*)(Lua_Mob,uint32,uint32,float,float,float,float))&Lua_Group::TeleportGroup); } #endif diff --git a/zone/lua_hate_entry.cpp b/zone/lua_hate_entry.cpp index 14eed4a7f..7d209bd43 100644 --- a/zone/lua_hate_entry.cpp +++ b/zone/lua_hate_entry.cpp @@ -58,17 +58,17 @@ void Lua_HateEntry::SetFrenzy(bool value) { luabind::scope lua_register_hate_entry() { return luabind::class_("HateEntry") - .property("null", &Lua_HateEntry::Null) - .property("valid", &Lua_HateEntry::Valid) - .property("ent", &Lua_HateEntry::GetEnt, &Lua_HateEntry::SetEnt) - .property("damage", &Lua_HateEntry::GetDamage, &Lua_HateEntry::SetDamage) - .property("hate", &Lua_HateEntry::GetHate, &Lua_HateEntry::SetHate) - .property("frenzy", &Lua_HateEntry::GetFrenzy, &Lua_HateEntry::SetFrenzy); + .property("null", &Lua_HateEntry::Null) + .property("valid", &Lua_HateEntry::Valid) + .property("damage", &Lua_HateEntry::GetDamage, &Lua_HateEntry::SetDamage) + .property("ent", &Lua_HateEntry::GetEnt, &Lua_HateEntry::SetEnt) + .property("frenzy", &Lua_HateEntry::GetFrenzy, &Lua_HateEntry::SetFrenzy) + .property("hate", &Lua_HateEntry::GetHate, &Lua_HateEntry::SetHate); } luabind::scope lua_register_hate_list() { return luabind::class_("HateList") - .def_readwrite("entries", &Lua_HateList::entries, luabind::return_stl_iterator); + .def_readwrite("entries", &Lua_HateList::entries, luabind::return_stl_iterator); } #endif diff --git a/zone/lua_hate_list.cpp b/zone/lua_hate_list.cpp index e8c6a52df..ca1fd6eaf 100644 --- a/zone/lua_hate_list.cpp +++ b/zone/lua_hate_list.cpp @@ -51,19 +51,18 @@ void Lua_HateEntry::SetFrenzy(bool value) { } luabind::scope lua_register_hate_entry() { - return luabind::class_("HateEntry") - .property("null", &Lua_HateEntry::Null) - .property("valid", &Lua_HateEntry::Valid) - .property("ent", &Lua_HateEntry::GetEnt, &Lua_HateEntry::SetEnt) - .property("damage", &Lua_HateEntry::GetDamage, &Lua_HateEntry::SetDamage) - .property("hate", &Lua_HateEntry::GetHate, &Lua_HateEntry::SetHate) - .property("frenzy", &Lua_HateEntry::GetFrenzy, &Lua_HateEntry::SetFrenzy); + .property("null", &Lua_HateEntry::Null) + .property("valid", &Lua_HateEntry::Valid) + .property("damage", &Lua_HateEntry::GetDamage, &Lua_HateEntry::SetDamage) + .property("ent", &Lua_HateEntry::GetEnt, &Lua_HateEntry::SetEnt) + .property("frenzy", &Lua_HateEntry::GetFrenzy, &Lua_HateEntry::SetFrenzy) + .property("hate", &Lua_HateEntry::GetHate, &Lua_HateEntry::SetHate); } luabind::scope lua_register_hate_list() { return luabind::class_("HateList") - .def_readwrite("entries", &Lua_HateList::entries, luabind::return_stl_iterator); + .def_readwrite("entries", &Lua_HateList::entries, luabind::return_stl_iterator); } #endif diff --git a/zone/lua_inventory.cpp b/zone/lua_inventory.cpp index 1eba6e6f6..3f9af6947 100644 --- a/zone/lua_inventory.cpp +++ b/zone/lua_inventory.cpp @@ -166,36 +166,36 @@ int Lua_Inventory::GetSlotByItemInst(Lua_ItemInst inst) { luabind::scope lua_register_inventory() { return luabind::class_("Inventory") - .def(luabind::constructor<>()) - .def("GetItem", (Lua_ItemInst(Lua_Inventory::*)(int))&Lua_Inventory::GetItem) - .def("GetItem", (Lua_ItemInst(Lua_Inventory::*)(int,int))&Lua_Inventory::GetItem) - .def("PutItem", (int(Lua_Inventory::*)(int,Lua_ItemInst))&Lua_Inventory::PutItem) - .def("PushCursor", (int(Lua_Inventory::*)(Lua_ItemInst))&Lua_Inventory::PushCursor) - .def("SwapItem", (bool(Lua_Inventory::*)(int,int))&Lua_Inventory::SwapItem) - .def("DeleteItem", (bool(Lua_Inventory::*)(int))&Lua_Inventory::DeleteItem) - .def("DeleteItem", (bool(Lua_Inventory::*)(int,int))&Lua_Inventory::DeleteItem) - .def("CheckNoDrop", (bool(Lua_Inventory::*)(int))&Lua_Inventory::CheckNoDrop) - .def("PopItem", (Lua_ItemInst(Lua_Inventory::*)(int))&Lua_Inventory::PopItem) - .def("HasItem", (int(Lua_Inventory::*)(int))&Lua_Inventory::HasItem) - .def("HasItem", (int(Lua_Inventory::*)(int,int))&Lua_Inventory::HasItem) - .def("HasItem", (int(Lua_Inventory::*)(int,int,int))&Lua_Inventory::HasItem) - .def("HasSpaceForItem", (bool(Lua_Inventory::*)(Lua_Item,int))&Lua_Inventory::HasSpaceForItem) - .def("HasItemByUse", (int(Lua_Inventory::*)(int))&Lua_Inventory::HasItemByUse) - .def("HasItemByUse", (int(Lua_Inventory::*)(int,uint8))&Lua_Inventory::HasItemByUse) - .def("HasItemByUse", (int(Lua_Inventory::*)(int,uint8,uint8))&Lua_Inventory::HasItemByUse) - .def("HasItemByLoreGroup", (int(Lua_Inventory::*)(uint32))&Lua_Inventory::HasItemByLoreGroup) - .def("HasItemByLoreGroup", (int(Lua_Inventory::*)(uint32,int))&Lua_Inventory::HasItemByLoreGroup) - .def("FindFreeSlot", (int(Lua_Inventory::*)(bool,bool))&Lua_Inventory::FindFreeSlot) - .def("FindFreeSlot", (int(Lua_Inventory::*)(bool,bool,int))&Lua_Inventory::FindFreeSlot) - .def("FindFreeSlot", (int(Lua_Inventory::*)(bool,bool,int,bool))&Lua_Inventory::FindFreeSlot) - .def("CalcSlotId", (int(Lua_Inventory::*)(int))&Lua_Inventory::CalcSlotId) - .def("CalcSlotId", (int(Lua_Inventory::*)(int,int))&Lua_Inventory::CalcSlotId) - .def("CalcBagIdx", (int(Lua_Inventory::*)(int))&Lua_Inventory::CalcBagIdx) - .def("CalcSlotFromMaterial", (int(Lua_Inventory::*)(int))&Lua_Inventory::CalcSlotFromMaterial) - .def("CalcMaterialFromSlot", (int(Lua_Inventory::*)(int))&Lua_Inventory::CalcMaterialFromSlot) - .def("CanItemFitInContainer", (bool(Lua_Inventory::*)(Lua_Item,Lua_Item))&Lua_Inventory::CanItemFitInContainer) - .def("SupportsContainers", (bool(Lua_Inventory::*)(int))&Lua_Inventory::SupportsContainers) - .def("GetSlotByItemInst", (int(Lua_Inventory::*)(Lua_ItemInst))&Lua_Inventory::GetSlotByItemInst); + .def(luabind::constructor<>()) + .def("CalcBagIdx", (int(Lua_Inventory::*)(int))&Lua_Inventory::CalcBagIdx) + .def("CalcMaterialFromSlot", (int(Lua_Inventory::*)(int))&Lua_Inventory::CalcMaterialFromSlot) + .def("CalcSlotFromMaterial", (int(Lua_Inventory::*)(int))&Lua_Inventory::CalcSlotFromMaterial) + .def("CalcSlotId", (int(Lua_Inventory::*)(int))&Lua_Inventory::CalcSlotId) + .def("CalcSlotId", (int(Lua_Inventory::*)(int,int))&Lua_Inventory::CalcSlotId) + .def("CanItemFitInContainer", (bool(Lua_Inventory::*)(Lua_Item,Lua_Item))&Lua_Inventory::CanItemFitInContainer) + .def("CheckNoDrop", (bool(Lua_Inventory::*)(int))&Lua_Inventory::CheckNoDrop) + .def("DeleteItem", (bool(Lua_Inventory::*)(int))&Lua_Inventory::DeleteItem) + .def("DeleteItem", (bool(Lua_Inventory::*)(int,int))&Lua_Inventory::DeleteItem) + .def("FindFreeSlot", (int(Lua_Inventory::*)(bool,bool))&Lua_Inventory::FindFreeSlot) + .def("FindFreeSlot", (int(Lua_Inventory::*)(bool,bool,int))&Lua_Inventory::FindFreeSlot) + .def("FindFreeSlot", (int(Lua_Inventory::*)(bool,bool,int,bool))&Lua_Inventory::FindFreeSlot) + .def("GetItem", (Lua_ItemInst(Lua_Inventory::*)(int))&Lua_Inventory::GetItem) + .def("GetItem", (Lua_ItemInst(Lua_Inventory::*)(int,int))&Lua_Inventory::GetItem) + .def("GetSlotByItemInst", (int(Lua_Inventory::*)(Lua_ItemInst))&Lua_Inventory::GetSlotByItemInst) + .def("HasItem", (int(Lua_Inventory::*)(int))&Lua_Inventory::HasItem) + .def("HasItem", (int(Lua_Inventory::*)(int,int))&Lua_Inventory::HasItem) + .def("HasItem", (int(Lua_Inventory::*)(int,int,int))&Lua_Inventory::HasItem) + .def("HasItemByLoreGroup", (int(Lua_Inventory::*)(uint32))&Lua_Inventory::HasItemByLoreGroup) + .def("HasItemByLoreGroup", (int(Lua_Inventory::*)(uint32,int))&Lua_Inventory::HasItemByLoreGroup) + .def("HasItemByUse", (int(Lua_Inventory::*)(int))&Lua_Inventory::HasItemByUse) + .def("HasItemByUse", (int(Lua_Inventory::*)(int,uint8))&Lua_Inventory::HasItemByUse) + .def("HasItemByUse", (int(Lua_Inventory::*)(int,uint8,uint8))&Lua_Inventory::HasItemByUse) + .def("HasSpaceForItem", (bool(Lua_Inventory::*)(Lua_Item,int))&Lua_Inventory::HasSpaceForItem) + .def("PopItem", (Lua_ItemInst(Lua_Inventory::*)(int))&Lua_Inventory::PopItem) + .def("PushCursor", (int(Lua_Inventory::*)(Lua_ItemInst))&Lua_Inventory::PushCursor) + .def("PutItem", (int(Lua_Inventory::*)(int,Lua_ItemInst))&Lua_Inventory::PutItem) + .def("SupportsContainers", (bool(Lua_Inventory::*)(int))&Lua_Inventory::SupportsContainers) + .def("SwapItem", (bool(Lua_Inventory::*)(int,int))&Lua_Inventory::SwapItem); } #endif diff --git a/zone/lua_item.cpp b/zone/lua_item.cpp index 5ee5f5d69..f693407d9 100644 --- a/zone/lua_item.cpp +++ b/zone/lua_item.cpp @@ -903,185 +903,185 @@ const char *Lua_Item::GetScrollName() { luabind::scope lua_register_item() { return luabind::class_("Item") - .def(luabind::constructor<>()) - .def(luabind::constructor()) - .def("null", &Lua_Item::Null) - .def("valid", &Lua_Item::Valid) - .def("MinStatus", &Lua_Item::GetMinStatus) - .def("ItemClass", &Lua_Item::GetItemClass) - .def("Name", &Lua_Item::GetName) - .def("Lore", &Lua_Item::GetLore) - .def("IDFile", &Lua_Item::GetIDFile) - .def("ID", &Lua_Item::GetID) - .def("Weight", &Lua_Item::GetWeight) - .def("NoRent", &Lua_Item::GetNoRent) - .def("NoDrop", &Lua_Item::GetNoDrop) - .def("Size", &Lua_Item::GetSize) - .def("Slots", &Lua_Item::GetSlots) - .def("Price", &Lua_Item::GetPrice) - .def("Icon", &Lua_Item::GetIcon) - .def("LoreGroup", &Lua_Item::GetLoreGroup) - .def("LoreFlag", &Lua_Item::GetLoreFlag) - .def("PendingLoreFlag", &Lua_Item::GetPendingLoreFlag) - .def("ArtifactFlag", &Lua_Item::GetArtifactFlag) - .def("SummonedFlag", &Lua_Item::GetSummonedFlag) - .def("FVNoDrop", &Lua_Item::GetFVNoDrop) - .def("Favor", &Lua_Item::GetFavor) - .def("GuildFavor", &Lua_Item::GetGuildFavor) - .def("PointType", &Lua_Item::GetPointType) - .def("BagType", &Lua_Item::GetBagType) - .def("BagSlots", &Lua_Item::GetBagSlots) - .def("BagSize", &Lua_Item::GetBagSize) - .def("BagWR", &Lua_Item::GetBagWR) - .def("BenefitFlag", &Lua_Item::GetBenefitFlag) - .def("Tradeskills", &Lua_Item::GetTradeskills) - .def("CR", &Lua_Item::GetCR) - .def("DR", &Lua_Item::GetDR) - .def("PR", &Lua_Item::GetPR) - .def("MR", &Lua_Item::GetMR) - .def("FR", &Lua_Item::GetFR) - .def("AStr", &Lua_Item::GetAStr) - .def("ASta", &Lua_Item::GetASta) - .def("AAgi", &Lua_Item::GetAAgi) - .def("ADex", &Lua_Item::GetADex) - .def("ACha", &Lua_Item::GetACha) - .def("AInt", &Lua_Item::GetAInt) - .def("AWis", &Lua_Item::GetAWis) - .def("HP", &Lua_Item::GetHP) - .def("Mana", &Lua_Item::GetMana) - .def("AC", &Lua_Item::GetAC) - .def("Deity", &Lua_Item::GetDeity) - .def("SkillModValue", &Lua_Item::GetSkillModValue) - .def("SkillModType", &Lua_Item::GetSkillModType) - .def("BaneDmgRace", &Lua_Item::GetBaneDmgRace) - .def("BaneDmgAmt", &Lua_Item::GetBaneDmgAmt) - .def("BaneDmgBody", &Lua_Item::GetBaneDmgBody) - .def("Magic", &Lua_Item::GetMagic) - .def("CastTime_", &Lua_Item::GetCastTime_) - .def("ReqLevel", &Lua_Item::GetReqLevel) - .def("BardType", &Lua_Item::GetBardType) - .def("BardValue", &Lua_Item::GetBardValue) - .def("Light", &Lua_Item::GetLight) - .def("Delay", &Lua_Item::GetDelay) - .def("RecLevel", &Lua_Item::GetRecLevel) - .def("RecSkill", &Lua_Item::GetRecSkill) - .def("ElemDmgType", &Lua_Item::GetElemDmgType) - .def("ElemDmgAmt", &Lua_Item::GetElemDmgAmt) - .def("Range", &Lua_Item::GetRange) - .def("Damage", &Lua_Item::GetDamage) - .def("Color", &Lua_Item::GetColor) - .def("Classes", &Lua_Item::GetClasses) - .def("Races", &Lua_Item::GetRaces) - .def("MaxCharges", &Lua_Item::GetMaxCharges) - .def("ItemType", &Lua_Item::GetItemType) - .def("Material", &Lua_Item::GetMaterial) - .def("SellRate", &Lua_Item::GetSellRate) - .def("Fulfilment", &Lua_Item::GetFulfilment) - .def("CastTime", &Lua_Item::GetCastTime) - .def("EliteMaterial", &Lua_Item::GetEliteMaterial) - .def("ProcRate", &Lua_Item::GetProcRate) - .def("CombatEffects", &Lua_Item::GetCombatEffects) - .def("Shielding", &Lua_Item::GetShielding) - .def("StunResist", &Lua_Item::GetStunResist) - .def("StrikeThrough", &Lua_Item::GetStrikeThrough) - .def("ExtraDmgSkill", &Lua_Item::GetExtraDmgSkill) - .def("ExtraDmgAmt", &Lua_Item::GetExtraDmgAmt) - .def("SpellShield", &Lua_Item::GetSpellShield) - .def("Avoidance", &Lua_Item::GetAvoidance) - .def("Accuracy", &Lua_Item::GetAccuracy) - .def("CharmFileID", &Lua_Item::GetCharmFileID) - .def("FactionMod1", &Lua_Item::GetFactionMod1) - .def("FactionMod2", &Lua_Item::GetFactionMod2) - .def("FactionMod3", &Lua_Item::GetFactionMod3) - .def("FactionMod4", &Lua_Item::GetFactionMod4) - .def("FactionAmt1", &Lua_Item::GetFactionAmt1) - .def("FactionAmt2", &Lua_Item::GetFactionAmt2) - .def("FactionAmt3", &Lua_Item::GetFactionAmt3) - .def("FactionAmt4", &Lua_Item::GetFactionAmt4) - .def("CharmFile", &Lua_Item::GetCharmFile) - .def("AugType", &Lua_Item::GetAugType) - .def("AugSlotType", &Lua_Item::GetAugSlotType) - .def("AugSlotVisible", &Lua_Item::GetAugSlotVisible) - .def("AugSlotUnk2", &Lua_Item::GetAugSlotUnk2) - .def("LDoNTheme", &Lua_Item::GetLDoNTheme) - .def("LDoNPrice", &Lua_Item::GetLDoNPrice) - .def("LDoNSold", &Lua_Item::GetLDoNSold) - .def("BaneDmgRaceAmt", &Lua_Item::GetBaneDmgRaceAmt) - .def("AugRestrict", &Lua_Item::GetAugRestrict) - .def("Endur", &Lua_Item::GetEndur) - .def("DotShielding", &Lua_Item::GetDotShielding) - .def("Attack", &Lua_Item::GetAttack) - .def("Regen", &Lua_Item::GetRegen) - .def("ManaRegen", &Lua_Item::GetManaRegen) - .def("EnduranceRegen", &Lua_Item::GetEnduranceRegen) - .def("Haste", &Lua_Item::GetHaste) - .def("DamageShield", &Lua_Item::GetDamageShield) - .def("RecastDelay", &Lua_Item::GetRecastDelay) - .def("RecastType", &Lua_Item::GetRecastType) - .def("AugDistiller", &Lua_Item::GetAugDistiller) - .def("Attuneable", &Lua_Item::GetAttuneable) - .def("NoPet", &Lua_Item::GetNoPet) - .def("PotionBelt", &Lua_Item::GetPotionBelt) - .def("Stackable", &Lua_Item::GetStackable) - .def("NoTransfer", &Lua_Item::GetNoTransfer) - .def("QuestItemFlag", &Lua_Item::GetQuestItemFlag) - .def("StackSize", &Lua_Item::GetStackSize) - .def("PotionBeltSlots", &Lua_Item::GetPotionBeltSlots) - .def("Click_Effect", &Lua_Item::GetClick_Effect) - .def("Click_Type", &Lua_Item::GetClick_Type) - .def("Click_Level", &Lua_Item::GetClick_Level) - .def("Click_Level2", &Lua_Item::GetClick_Level2) - .def("Proc_Effect", &Lua_Item::GetProc_Effect) - .def("Proc_Type", &Lua_Item::GetProc_Type) - .def("Proc_Level", &Lua_Item::GetProc_Level) - .def("Proc_Level2", &Lua_Item::GetProc_Level2) - .def("Worn_Effect", &Lua_Item::GetWorn_Effect) - .def("Worn_Type", &Lua_Item::GetWorn_Type) - .def("Worn_Level", &Lua_Item::GetWorn_Level) - .def("Worn_Level2", &Lua_Item::GetWorn_Level2) - .def("Focus_Effect", &Lua_Item::GetFocus_Effect) - .def("Focus_Type", &Lua_Item::GetFocus_Type) - .def("Focus_Level", &Lua_Item::GetFocus_Level) - .def("Focus_Level2", &Lua_Item::GetFocus_Level2) - .def("Scroll_Effect", &Lua_Item::GetScroll_Effect) - .def("Scroll_Type", &Lua_Item::GetScroll_Type) - .def("Scroll_Level", &Lua_Item::GetScroll_Level) - .def("Scroll_Level2", &Lua_Item::GetScroll_Level2) - .def("Bard_Effect", &Lua_Item::GetBard_Effect) - .def("Bard_Type", &Lua_Item::GetBard_Type) - .def("Bard_Level", &Lua_Item::GetBard_Level) - .def("Bard_Level2", &Lua_Item::GetBard_Level2) - .def("Book", &Lua_Item::GetBook) - .def("BookType", &Lua_Item::GetBookType) - .def("Filename", &Lua_Item::GetFilename) - .def("SVCorruption", &Lua_Item::GetSVCorruption) - .def("Purity", &Lua_Item::GetPurity) - .def("BackstabDmg", &Lua_Item::GetBackstabDmg) - .def("DSMitigation", &Lua_Item::GetDSMitigation) - .def("HeroicStr", &Lua_Item::GetHeroicStr) - .def("HeroicInt", &Lua_Item::GetHeroicInt) - .def("HeroicWis", &Lua_Item::GetHeroicWis) - .def("HeroicAgi", &Lua_Item::GetHeroicAgi) - .def("HeroicDex", &Lua_Item::GetHeroicDex) - .def("HeroicSta", &Lua_Item::GetHeroicSta) - .def("HeroicCha", &Lua_Item::GetHeroicCha) - .def("HeroicMR", &Lua_Item::GetHeroicMR) - .def("HeroicFR", &Lua_Item::GetHeroicFR) - .def("HeroicCR", &Lua_Item::GetHeroicCR) - .def("HeroicDR", &Lua_Item::GetHeroicDR) - .def("HeroicPR", &Lua_Item::GetHeroicPR) - .def("HeroicSVCorrup", &Lua_Item::GetHeroicSVCorrup) - .def("HealAmt", &Lua_Item::GetHealAmt) - .def("SpellDmg", &Lua_Item::GetSpellDmg) - .def("LDoNSellBackRate", &Lua_Item::GetLDoNSellBackRate) - .def("ScriptFileID", &Lua_Item::GetScriptFileID) - .def("ExpendableArrow", &Lua_Item::GetExpendableArrow) - .def("Clairvoyance", &Lua_Item::GetClairvoyance) - .def("ClickName", &Lua_Item::GetClickName) - .def("ProcName", &Lua_Item::GetProcName) - .def("WornName", &Lua_Item::GetWornName) - .def("FocusName", &Lua_Item::GetFocusName) - .def("ScrollName", &Lua_Item::GetScrollName); + .def(luabind::constructor<>()) + .def(luabind::constructor()) + .def("null", &Lua_Item::Null) + .def("valid", &Lua_Item::Valid) + .def("AAgi", &Lua_Item::GetAAgi) + .def("AC", &Lua_Item::GetAC) + .def("ACha", &Lua_Item::GetACha) + .def("ADex", &Lua_Item::GetADex) + .def("AInt", &Lua_Item::GetAInt) + .def("ASta", &Lua_Item::GetASta) + .def("AStr", &Lua_Item::GetAStr) + .def("AWis", &Lua_Item::GetAWis) + .def("Accuracy", &Lua_Item::GetAccuracy) + .def("ArtifactFlag", &Lua_Item::GetArtifactFlag) + .def("Attack", &Lua_Item::GetAttack) + .def("Attuneable", &Lua_Item::GetAttuneable) + .def("AugDistiller", &Lua_Item::GetAugDistiller) + .def("AugRestrict", &Lua_Item::GetAugRestrict) + .def("AugSlotType", &Lua_Item::GetAugSlotType) + .def("AugSlotUnk2", &Lua_Item::GetAugSlotUnk2) + .def("AugSlotVisible", &Lua_Item::GetAugSlotVisible) + .def("AugType", &Lua_Item::GetAugType) + .def("Avoidance", &Lua_Item::GetAvoidance) + .def("BackstabDmg", &Lua_Item::GetBackstabDmg) + .def("BagSize", &Lua_Item::GetBagSize) + .def("BagSlots", &Lua_Item::GetBagSlots) + .def("BagType", &Lua_Item::GetBagType) + .def("BagWR", &Lua_Item::GetBagWR) + .def("BaneDmgAmt", &Lua_Item::GetBaneDmgAmt) + .def("BaneDmgBody", &Lua_Item::GetBaneDmgBody) + .def("BaneDmgRace", &Lua_Item::GetBaneDmgRace) + .def("BaneDmgRaceAmt", &Lua_Item::GetBaneDmgRaceAmt) + .def("BardType", &Lua_Item::GetBardType) + .def("BardValue", &Lua_Item::GetBardValue) + .def("Bard_Effect", &Lua_Item::GetBard_Effect) + .def("Bard_Level", &Lua_Item::GetBard_Level) + .def("Bard_Level2", &Lua_Item::GetBard_Level2) + .def("Bard_Type", &Lua_Item::GetBard_Type) + .def("BenefitFlag", &Lua_Item::GetBenefitFlag) + .def("Book", &Lua_Item::GetBook) + .def("BookType", &Lua_Item::GetBookType) + .def("CR", &Lua_Item::GetCR) + .def("CastTime", &Lua_Item::GetCastTime) + .def("CastTime_", &Lua_Item::GetCastTime_) + .def("CharmFile", &Lua_Item::GetCharmFile) + .def("CharmFileID", &Lua_Item::GetCharmFileID) + .def("Clairvoyance", &Lua_Item::GetClairvoyance) + .def("Classes", &Lua_Item::GetClasses) + .def("ClickName", &Lua_Item::GetClickName) + .def("Click_Effect", &Lua_Item::GetClick_Effect) + .def("Click_Level", &Lua_Item::GetClick_Level) + .def("Click_Level2", &Lua_Item::GetClick_Level2) + .def("Click_Type", &Lua_Item::GetClick_Type) + .def("Color", &Lua_Item::GetColor) + .def("CombatEffects", &Lua_Item::GetCombatEffects) + .def("DR", &Lua_Item::GetDR) + .def("DSMitigation", &Lua_Item::GetDSMitigation) + .def("Damage", &Lua_Item::GetDamage) + .def("DamageShield", &Lua_Item::GetDamageShield) + .def("Deity", &Lua_Item::GetDeity) + .def("Delay", &Lua_Item::GetDelay) + .def("DotShielding", &Lua_Item::GetDotShielding) + .def("ElemDmgAmt", &Lua_Item::GetElemDmgAmt) + .def("ElemDmgType", &Lua_Item::GetElemDmgType) + .def("EliteMaterial", &Lua_Item::GetEliteMaterial) + .def("Endur", &Lua_Item::GetEndur) + .def("EnduranceRegen", &Lua_Item::GetEnduranceRegen) + .def("ExpendableArrow", &Lua_Item::GetExpendableArrow) + .def("ExtraDmgAmt", &Lua_Item::GetExtraDmgAmt) + .def("ExtraDmgSkill", &Lua_Item::GetExtraDmgSkill) + .def("FR", &Lua_Item::GetFR) + .def("FVNoDrop", &Lua_Item::GetFVNoDrop) + .def("FactionAmt1", &Lua_Item::GetFactionAmt1) + .def("FactionAmt2", &Lua_Item::GetFactionAmt2) + .def("FactionAmt3", &Lua_Item::GetFactionAmt3) + .def("FactionAmt4", &Lua_Item::GetFactionAmt4) + .def("FactionMod1", &Lua_Item::GetFactionMod1) + .def("FactionMod2", &Lua_Item::GetFactionMod2) + .def("FactionMod3", &Lua_Item::GetFactionMod3) + .def("FactionMod4", &Lua_Item::GetFactionMod4) + .def("Favor", &Lua_Item::GetFavor) + .def("Filename", &Lua_Item::GetFilename) + .def("FocusName", &Lua_Item::GetFocusName) + .def("Focus_Effect", &Lua_Item::GetFocus_Effect) + .def("Focus_Level", &Lua_Item::GetFocus_Level) + .def("Focus_Level2", &Lua_Item::GetFocus_Level2) + .def("Focus_Type", &Lua_Item::GetFocus_Type) + .def("Fulfilment", &Lua_Item::GetFulfilment) + .def("GuildFavor", &Lua_Item::GetGuildFavor) + .def("HP", &Lua_Item::GetHP) + .def("Haste", &Lua_Item::GetHaste) + .def("HealAmt", &Lua_Item::GetHealAmt) + .def("HeroicAgi", &Lua_Item::GetHeroicAgi) + .def("HeroicCR", &Lua_Item::GetHeroicCR) + .def("HeroicCha", &Lua_Item::GetHeroicCha) + .def("HeroicDR", &Lua_Item::GetHeroicDR) + .def("HeroicDex", &Lua_Item::GetHeroicDex) + .def("HeroicFR", &Lua_Item::GetHeroicFR) + .def("HeroicInt", &Lua_Item::GetHeroicInt) + .def("HeroicMR", &Lua_Item::GetHeroicMR) + .def("HeroicPR", &Lua_Item::GetHeroicPR) + .def("HeroicSVCorrup", &Lua_Item::GetHeroicSVCorrup) + .def("HeroicSta", &Lua_Item::GetHeroicSta) + .def("HeroicStr", &Lua_Item::GetHeroicStr) + .def("HeroicWis", &Lua_Item::GetHeroicWis) + .def("ID", &Lua_Item::GetID) + .def("IDFile", &Lua_Item::GetIDFile) + .def("Icon", &Lua_Item::GetIcon) + .def("ItemClass", &Lua_Item::GetItemClass) + .def("ItemType", &Lua_Item::GetItemType) + .def("LDoNPrice", &Lua_Item::GetLDoNPrice) + .def("LDoNSellBackRate", &Lua_Item::GetLDoNSellBackRate) + .def("LDoNSold", &Lua_Item::GetLDoNSold) + .def("LDoNTheme", &Lua_Item::GetLDoNTheme) + .def("Light", &Lua_Item::GetLight) + .def("Lore", &Lua_Item::GetLore) + .def("LoreFlag", &Lua_Item::GetLoreFlag) + .def("LoreGroup", &Lua_Item::GetLoreGroup) + .def("MR", &Lua_Item::GetMR) + .def("Magic", &Lua_Item::GetMagic) + .def("Mana", &Lua_Item::GetMana) + .def("ManaRegen", &Lua_Item::GetManaRegen) + .def("Material", &Lua_Item::GetMaterial) + .def("MaxCharges", &Lua_Item::GetMaxCharges) + .def("MinStatus", &Lua_Item::GetMinStatus) + .def("Name", &Lua_Item::GetName) + .def("NoDrop", &Lua_Item::GetNoDrop) + .def("NoPet", &Lua_Item::GetNoPet) + .def("NoRent", &Lua_Item::GetNoRent) + .def("NoTransfer", &Lua_Item::GetNoTransfer) + .def("PR", &Lua_Item::GetPR) + .def("PendingLoreFlag", &Lua_Item::GetPendingLoreFlag) + .def("PointType", &Lua_Item::GetPointType) + .def("PotionBelt", &Lua_Item::GetPotionBelt) + .def("PotionBeltSlots", &Lua_Item::GetPotionBeltSlots) + .def("Price", &Lua_Item::GetPrice) + .def("ProcName", &Lua_Item::GetProcName) + .def("ProcRate", &Lua_Item::GetProcRate) + .def("Proc_Effect", &Lua_Item::GetProc_Effect) + .def("Proc_Level", &Lua_Item::GetProc_Level) + .def("Proc_Level2", &Lua_Item::GetProc_Level2) + .def("Proc_Type", &Lua_Item::GetProc_Type) + .def("Purity", &Lua_Item::GetPurity) + .def("QuestItemFlag", &Lua_Item::GetQuestItemFlag) + .def("Races", &Lua_Item::GetRaces) + .def("Range", &Lua_Item::GetRange) + .def("RecLevel", &Lua_Item::GetRecLevel) + .def("RecSkill", &Lua_Item::GetRecSkill) + .def("RecastDelay", &Lua_Item::GetRecastDelay) + .def("RecastType", &Lua_Item::GetRecastType) + .def("Regen", &Lua_Item::GetRegen) + .def("ReqLevel", &Lua_Item::GetReqLevel) + .def("SVCorruption", &Lua_Item::GetSVCorruption) + .def("ScriptFileID", &Lua_Item::GetScriptFileID) + .def("ScrollName", &Lua_Item::GetScrollName) + .def("Scroll_Effect", &Lua_Item::GetScroll_Effect) + .def("Scroll_Level", &Lua_Item::GetScroll_Level) + .def("Scroll_Level2", &Lua_Item::GetScroll_Level2) + .def("Scroll_Type", &Lua_Item::GetScroll_Type) + .def("SellRate", &Lua_Item::GetSellRate) + .def("Shielding", &Lua_Item::GetShielding) + .def("Size", &Lua_Item::GetSize) + .def("SkillModType", &Lua_Item::GetSkillModType) + .def("SkillModValue", &Lua_Item::GetSkillModValue) + .def("Slots", &Lua_Item::GetSlots) + .def("SpellDmg", &Lua_Item::GetSpellDmg) + .def("SpellShield", &Lua_Item::GetSpellShield) + .def("StackSize", &Lua_Item::GetStackSize) + .def("Stackable", &Lua_Item::GetStackable) + .def("StrikeThrough", &Lua_Item::GetStrikeThrough) + .def("StunResist", &Lua_Item::GetStunResist) + .def("SummonedFlag", &Lua_Item::GetSummonedFlag) + .def("Tradeskills", &Lua_Item::GetTradeskills) + .def("Weight", &Lua_Item::GetWeight) + .def("WornName", &Lua_Item::GetWornName) + .def("Worn_Effect", &Lua_Item::GetWorn_Effect) + .def("Worn_Level", &Lua_Item::GetWorn_Level) + .def("Worn_Level2", &Lua_Item::GetWorn_Level2) + .def("Worn_Type", &Lua_Item::GetWorn_Type); } #endif diff --git a/zone/lua_iteminst.cpp b/zone/lua_iteminst.cpp index ba3c8a2c6..ee164ded1 100644 --- a/zone/lua_iteminst.cpp +++ b/zone/lua_iteminst.cpp @@ -265,57 +265,55 @@ void Lua_ItemInst::ClearTimers() { } luabind::scope lua_register_iteminst() { - return luabind::class_("ItemInst") - .def(luabind::constructor<>()) - .def(luabind::constructor()) - .def(luabind::constructor()) - .property("null", &Lua_ItemInst::Null) - .property("valid", &Lua_ItemInst::Valid) - .def("IsType", (bool(Lua_ItemInst::*)(int))&Lua_ItemInst::IsType) - .def("IsStackable", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsStackable) - .def("IsEquipable", (bool(Lua_ItemInst::*)(int,int))&Lua_ItemInst::IsEquipable) - .def("IsEquipable", (bool(Lua_ItemInst::*)(int))&Lua_ItemInst::IsEquipable) - .def("IsAugmentable", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsAugmentable) - .def("GetAugmentType", (int(Lua_ItemInst::*)(void))&Lua_ItemInst::GetAugmentType) - .def("IsExpendable", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsExpendable) - .def("GetItem", (Lua_ItemInst(Lua_ItemInst::*)(int))&Lua_ItemInst::GetItem) - .def("GetUnscaledItem", (Lua_ItemInst(Lua_ItemInst::*)(int))&Lua_ItemInst::GetUnscaledItem) - .def("GetItemID", (uint32(Lua_ItemInst::*)(int))&Lua_ItemInst::GetItemID) - .def("GetTotalItemCount", (int(Lua_ItemInst::*)(void))&Lua_ItemInst::GetTotalItemCount) - .def("GetAugment", (Lua_ItemInst(Lua_ItemInst::*)(int))&Lua_ItemInst::GetAugment) - .def("GetAugmentItemID", (uint32(Lua_ItemInst::*)(int))&Lua_ItemInst::GetAugmentItemID) - .def("IsAugmented", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsAugmented) - .def("IsWeapon", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsWeapon) - .def("IsAmmo", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsAmmo) - .def("GetID", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetID) - .def("GetItemScriptID", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetItemScriptID) - .def("GetItem", (Lua_Item(Lua_ItemInst::*)(void))&Lua_ItemInst::GetItem) - .def("GetCharges", (int(Lua_ItemInst::*)(void))&Lua_ItemInst::GetCharges) - .def("SetCharges", (void(Lua_ItemInst::*)(int))&Lua_ItemInst::SetCharges) - .def("GetPrice", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetPrice) - .def("SetPrice", (void(Lua_ItemInst::*)(uint32))&Lua_ItemInst::SetPrice) - .def("SetColor", (void(Lua_ItemInst::*)(uint32))&Lua_ItemInst::SetColor) - .def("GetColor", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetColor) - .def("IsInstNoDrop", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsInstNoDrop) - .def("SetInstNoDrop", (void(Lua_ItemInst::*)(bool))&Lua_ItemInst::SetInstNoDrop) - .def("GetCustomDataString", (std::string(Lua_ItemInst::*)(void))&Lua_ItemInst::GetCustomDataString) - .def("SetCustomData", (void(Lua_ItemInst::*)(std::string,std::string))&Lua_ItemInst::SetCustomData) - .def("SetCustomData", (void(Lua_ItemInst::*)(std::string,int))&Lua_ItemInst::SetCustomData) - .def("SetCustomData", (void(Lua_ItemInst::*)(std::string,float))&Lua_ItemInst::SetCustomData) - .def("SetCustomData", (void(Lua_ItemInst::*)(std::string,bool))&Lua_ItemInst::SetCustomData) - .def("GetCustomData", (std::string(Lua_ItemInst::*)(std::string))&Lua_ItemInst::GetCustomData) - .def("DeleteCustomData", (void(Lua_ItemInst::*)(std::string))&Lua_ItemInst::DeleteCustomData) - .def("SetScaling", (void(Lua_ItemInst::*)(bool))&Lua_ItemInst::SetScaling) - .def("SetScale", (void(Lua_ItemInst::*)(double))&Lua_ItemInst::SetScale) - .def("GetExp", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetExp) - .def("SetExp", (void(Lua_ItemInst::*)(uint32))&Lua_ItemInst::SetExp) - .def("AddExp", (void(Lua_ItemInst::*)(uint32))&Lua_ItemInst::AddExp) - .def("GetMaxEvolveLvl", (int(Lua_ItemInst::*)(void))&Lua_ItemInst::GetMaxEvolveLvl) - .def("GetKillsNeeded", (uint32(Lua_ItemInst::*)(int))&Lua_ItemInst::GetKillsNeeded) - .def("Clone", (Lua_ItemInst(Lua_ItemInst::*)(void))&Lua_ItemInst::Clone) - .def("SetTimer", (void(Lua_ItemInst::*)(std::string,uint32))&Lua_ItemInst::SetTimer) - .def("StopTimer", (void(Lua_ItemInst::*)(std::string))&Lua_ItemInst::StopTimer) - .def("ClearTimers", (void(Lua_ItemInst::*)(void))&Lua_ItemInst::ClearTimers); + return luabind::class_("ItemInst") + .def(luabind::constructor<>()) + .def(luabind::constructor()) + .def(luabind::constructor()) + .def("AddExp", (void(Lua_ItemInst::*)(uint32))&Lua_ItemInst::AddExp) + .def("ClearTimers", (void(Lua_ItemInst::*)(void))&Lua_ItemInst::ClearTimers) + .def("Clone", (Lua_ItemInst(Lua_ItemInst::*)(void))&Lua_ItemInst::Clone) + .def("DeleteCustomData", (void(Lua_ItemInst::*)(std::string))&Lua_ItemInst::DeleteCustomData) + .def("GetAugment", (Lua_ItemInst(Lua_ItemInst::*)(int))&Lua_ItemInst::GetAugment) + .def("GetAugmentItemID", (uint32(Lua_ItemInst::*)(int))&Lua_ItemInst::GetAugmentItemID) + .def("GetAugmentType", (int(Lua_ItemInst::*)(void))&Lua_ItemInst::GetAugmentType) + .def("GetCharges", (int(Lua_ItemInst::*)(void))&Lua_ItemInst::GetCharges) + .def("GetColor", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetColor) + .def("GetCustomData", (std::string(Lua_ItemInst::*)(std::string))&Lua_ItemInst::GetCustomData) + .def("GetCustomDataString", (std::string(Lua_ItemInst::*)(void))&Lua_ItemInst::GetCustomDataString) + .def("GetExp", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetExp) + .def("GetID", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetID) + .def("GetItem", (Lua_Item(Lua_ItemInst::*)(void))&Lua_ItemInst::GetItem) + .def("GetItem", (Lua_ItemInst(Lua_ItemInst::*)(int))&Lua_ItemInst::GetItem) + .def("GetItemID", (uint32(Lua_ItemInst::*)(int))&Lua_ItemInst::GetItemID) + .def("GetItemScriptID", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetItemScriptID) + .def("GetKillsNeeded", (uint32(Lua_ItemInst::*)(int))&Lua_ItemInst::GetKillsNeeded) + .def("GetMaxEvolveLvl", (int(Lua_ItemInst::*)(void))&Lua_ItemInst::GetMaxEvolveLvl) + .def("GetPrice", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetPrice) + .def("GetTotalItemCount", (int(Lua_ItemInst::*)(void))&Lua_ItemInst::GetTotalItemCount) + .def("GetUnscaledItem", (Lua_ItemInst(Lua_ItemInst::*)(int))&Lua_ItemInst::GetUnscaledItem) + .def("IsAmmo", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsAmmo) + .def("IsAugmentable", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsAugmentable) + .def("IsAugmented", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsAugmented) + .def("IsEquipable", (bool(Lua_ItemInst::*)(int))&Lua_ItemInst::IsEquipable) + .def("IsEquipable", (bool(Lua_ItemInst::*)(int,int))&Lua_ItemInst::IsEquipable) + .def("IsExpendable", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsExpendable) + .def("IsInstNoDrop", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsInstNoDrop) + .def("IsStackable", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsStackable) + .def("IsType", (bool(Lua_ItemInst::*)(int))&Lua_ItemInst::IsType) + .def("IsWeapon", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsWeapon) + .def("SetCharges", (void(Lua_ItemInst::*)(int))&Lua_ItemInst::SetCharges) + .def("SetColor", (void(Lua_ItemInst::*)(uint32))&Lua_ItemInst::SetColor) + .def("SetCustomData", (void(Lua_ItemInst::*)(std::string,bool))&Lua_ItemInst::SetCustomData) + .def("SetCustomData", (void(Lua_ItemInst::*)(std::string,float))&Lua_ItemInst::SetCustomData) + .def("SetCustomData", (void(Lua_ItemInst::*)(std::string,int))&Lua_ItemInst::SetCustomData) + .def("SetCustomData", (void(Lua_ItemInst::*)(std::string,std::string))&Lua_ItemInst::SetCustomData) + .def("SetExp", (void(Lua_ItemInst::*)(uint32))&Lua_ItemInst::SetExp) + .def("SetInstNoDrop", (void(Lua_ItemInst::*)(bool))&Lua_ItemInst::SetInstNoDrop) + .def("SetPrice", (void(Lua_ItemInst::*)(uint32))&Lua_ItemInst::SetPrice) + .def("SetScale", (void(Lua_ItemInst::*)(double))&Lua_ItemInst::SetScale) + .def("SetScaling", (void(Lua_ItemInst::*)(bool))&Lua_ItemInst::SetScaling) + .def("SetTimer", (void(Lua_ItemInst::*)(std::string,uint32))&Lua_ItemInst::SetTimer) + .def("StopTimer", (void(Lua_ItemInst::*)(std::string))&Lua_ItemInst::StopTimer); } #endif diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index ed201ec9c..4b50c2b78 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -2425,474 +2425,474 @@ Lua_NPC Lua_Mob::GetHateRandomNPC() { luabind::scope lua_register_mob() { return luabind::class_("Mob") - .def(luabind::constructor<>()) - .def("GetName", &Lua_Mob::GetName) - .def("Depop", (void(Lua_Mob::*)(void))&Lua_Mob::Depop) - .def("Depop", (void(Lua_Mob::*)(bool))&Lua_Mob::Depop) - .def("BehindMob", (bool(Lua_Mob::*)(void))&Lua_Mob::BehindMob) - .def("BehindMob", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::BehindMob) - .def("BehindMob", (bool(Lua_Mob::*)(Lua_Mob,float))&Lua_Mob::BehindMob) - .def("BehindMob", (bool(Lua_Mob::*)(Lua_Mob,float,float))&Lua_Mob::BehindMob) - .def("SetLevel", (void(Lua_Mob::*)(int))&Lua_Mob::SetLevel) - .def("SetLevel", (void(Lua_Mob::*)(int,bool))&Lua_Mob::SetLevel) - .def("IsMoving", &Lua_Mob::IsMoving) - .def("GotoBind", &Lua_Mob::GotoBind) - .def("Attack", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::Attack) - .def("Attack", (bool(Lua_Mob::*)(Lua_Mob,int))&Lua_Mob::Attack) - .def("Attack", (bool(Lua_Mob::*)(Lua_Mob,int,bool))&Lua_Mob::Attack) - .def("Attack", (bool(Lua_Mob::*)(Lua_Mob,int,bool,bool))&Lua_Mob::Attack) - .def("Attack", (bool(Lua_Mob::*)(Lua_Mob,int,bool,bool,bool))&Lua_Mob::Attack) - .def("Attack", (bool(Lua_Mob::*)(Lua_Mob,int,bool,bool,bool,luabind::adl::object))&Lua_Mob::Attack) - .def("Damage", (void(Lua_Mob::*)(Lua_Mob,int,int,int))&Lua_Mob::Damage) - .def("Damage", (void(Lua_Mob::*)(Lua_Mob,int,int,int,bool))&Lua_Mob::Damage) - .def("Damage", (void(Lua_Mob::*)(Lua_Mob,int,int,int,bool,int))&Lua_Mob::Damage) - .def("Damage", (void(Lua_Mob::*)(Lua_Mob,int,int,int,bool,int,bool))&Lua_Mob::Damage) - .def("RangedAttack", &Lua_Mob::RangedAttack) - .def("ThrowingAttack", &Lua_Mob::ThrowingAttack) - .def("Heal", &Lua_Mob::Heal) - .def("HealDamage", (void(Lua_Mob::*)(uint32))&Lua_Mob::HealDamage) - .def("HealDamage", (void(Lua_Mob::*)(uint32,Lua_Mob))&Lua_Mob::HealDamage) - .def("GetLevelCon", (uint32(Lua_Mob::*)(int))&Lua_Mob::GetLevelCon) - .def("GetLevelCon", (uint32(Lua_Mob::*)(int,int))&Lua_Mob::GetLevelCon) - .def("SetHP", &Lua_Mob::SetHP) - .def("DoAnim", (void(Lua_Mob::*)(int))&Lua_Mob::DoAnim) - .def("DoAnim", (void(Lua_Mob::*)(int,int))&Lua_Mob::DoAnim) - .def("DoAnim", (void(Lua_Mob::*)(int,int,bool))&Lua_Mob::DoAnim) - .def("DoAnim", (void(Lua_Mob::*)(int,int,bool,int))&Lua_Mob::DoAnim) - .def("ChangeSize", (void(Lua_Mob::*)(double))&Lua_Mob::ChangeSize) - .def("ChangeSize", (void(Lua_Mob::*)(double,bool))&Lua_Mob::ChangeSize) - .def("RandomizeFeatures", (void(Lua_Mob::*)(bool,bool))&Lua_Mob::RandomizeFeatures) - .def("GMMove", (void(Lua_Mob::*)(double,double,double))&Lua_Mob::GMMove) - .def("GMMove", (void(Lua_Mob::*)(double,double,double,double))&Lua_Mob::GMMove) - .def("GMMove", (void(Lua_Mob::*)(double,double,double,double,bool))&Lua_Mob::GMMove) - .def("TryMoveAlong", (void(Lua_Mob::*)(float,float))&Lua_Mob::TryMoveAlong) - .def("TryMoveAlong", (void(Lua_Mob::*)(float,float,bool))&Lua_Mob::TryMoveAlong) - .def("HasProcs", &Lua_Mob::HasProcs) - .def("IsInvisible", (bool(Lua_Mob::*)(void))&Lua_Mob::IsInvisible) - .def("IsInvisible", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::IsInvisible) - .def("SetInvisible", &Lua_Mob::SetInvisible) - .def("FindBuff", &Lua_Mob::FindBuff) - .def("FindBuffBySlot", (uint16(Lua_Mob::*)(int))&Lua_Mob::FindBuffBySlot) - .def("BuffCount", &Lua_Mob::BuffCount) - .def("FindType", (bool(Lua_Mob::*)(int))&Lua_Mob::FindType) - .def("FindType", (bool(Lua_Mob::*)(int,bool))&Lua_Mob::FindType) - .def("FindType", (bool(Lua_Mob::*)(int,bool,int))&Lua_Mob::FindType) - .def("GetBuffSlotFromType", &Lua_Mob::GetBuffSlotFromType) - .def("GetBaseRace", &Lua_Mob::GetBaseRace) - .def("GetBaseGender", &Lua_Mob::GetBaseGender) - .def("GetDeity", &Lua_Mob::GetDeity) - .def("GetRace", &Lua_Mob::GetRace) - .def("GetRaceName", &Lua_Mob::GetRaceName) - .def("GetGender", &Lua_Mob::GetGender) - .def("GetTexture", &Lua_Mob::GetTexture) - .def("GetHelmTexture", &Lua_Mob::GetHelmTexture) - .def("GetHairColor", &Lua_Mob::GetHairColor) - .def("GetBeardColor", &Lua_Mob::GetBeardColor) - .def("GetEyeColor1", &Lua_Mob::GetEyeColor1) - .def("GetEyeColor2", &Lua_Mob::GetEyeColor2) - .def("GetHairStyle", &Lua_Mob::GetHairStyle) - .def("GetLuclinFace", &Lua_Mob::GetLuclinFace) - .def("GetBeard", &Lua_Mob::GetBeard) - .def("GetDrakkinHeritage", &Lua_Mob::GetDrakkinHeritage) - .def("GetDrakkinTattoo", &Lua_Mob::GetDrakkinTattoo) - .def("GetDrakkinDetails", &Lua_Mob::GetDrakkinDetails) - .def("GetClass", &Lua_Mob::GetClass) - .def("GetClassName", &Lua_Mob::GetClassName) - .def("GetLevel", &Lua_Mob::GetLevel) - .def("GetCleanName", &Lua_Mob::GetCleanName) - .def("GetTarget", &Lua_Mob::GetTarget) - .def("SetTarget", &Lua_Mob::SetTarget) - .def("GetHPRatio", &Lua_Mob::GetHPRatio) - .def("IsWarriorClass", &Lua_Mob::IsWarriorClass) - .def("GetHP", &Lua_Mob::GetHP) - .def("GetMaxHP", &Lua_Mob::GetMaxHP) - .def("GetItemStat", (int(Lua_Mob::*)(uint32,const char*))&Lua_Mob::GetItemStat) - .def("GetItemHPBonuses", &Lua_Mob::GetItemHPBonuses) - .def("GetSpellHPBonuses", &Lua_Mob::GetSpellHPBonuses) - .def("GetWalkspeed", &Lua_Mob::GetWalkspeed) - .def("GetRunspeed", &Lua_Mob::GetRunspeed) - .def("GetCasterLevel", &Lua_Mob::GetCasterLevel) - .def("GetMaxMana", &Lua_Mob::GetMaxMana) - .def("GetMana", &Lua_Mob::GetMana) - .def("SetMana", &Lua_Mob::SetMana) - .def("GetManaRatio", &Lua_Mob::GetManaRatio) - .def("GetAC", &Lua_Mob::GetAC) - .def("GetDisplayAC", &Lua_Mob::GetDisplayAC) - .def("GetATK", &Lua_Mob::GetATK) - .def("GetSTR", &Lua_Mob::GetSTR) - .def("GetSTA", &Lua_Mob::GetSTA) - .def("GetDEX", &Lua_Mob::GetDEX) - .def("GetAGI", &Lua_Mob::GetAGI) - .def("GetINT", &Lua_Mob::GetINT) - .def("GetWIS", &Lua_Mob::GetWIS) - .def("GetCHA", &Lua_Mob::GetCHA) - .def("GetMR", &Lua_Mob::GetMR) - .def("GetFR", &Lua_Mob::GetFR) - .def("GetDR", &Lua_Mob::GetDR) - .def("GetPR", &Lua_Mob::GetPR) - .def("GetCR", &Lua_Mob::GetCR) - .def("GetCorruption", &Lua_Mob::GetCorruption) - .def("GetPhR", &Lua_Mob::GetPhR) - .def("GetMaxSTR", &Lua_Mob::GetMaxSTR) - .def("GetMaxSTA", &Lua_Mob::GetMaxSTA) - .def("GetMaxDEX", &Lua_Mob::GetMaxDEX) - .def("GetMaxAGI", &Lua_Mob::GetMaxAGI) - .def("GetMaxINT", &Lua_Mob::GetMaxINT) - .def("GetMaxWIS", &Lua_Mob::GetMaxWIS) - .def("GetMaxCHA", &Lua_Mob::GetMaxCHA) - .def("ResistSpell", (double(Lua_Mob::*)(int,int,Lua_Mob))&Lua_Mob::ResistSpell) - .def("ResistSpell", (double(Lua_Mob::*)(int,int,Lua_Mob,bool))&Lua_Mob::ResistSpell) - .def("ResistSpell", (double(Lua_Mob::*)(int,int,Lua_Mob,bool,int))&Lua_Mob::ResistSpell) - .def("ResistSpell", (double(Lua_Mob::*)(int,int,Lua_Mob,bool,int,bool))&Lua_Mob::ResistSpell) - .def("GetSpecializeSkillValue", &Lua_Mob::GetSpecializeSkillValue) - .def("GetNPCTypeID", &Lua_Mob::GetNPCTypeID) - .def("IsTargeted", &Lua_Mob::IsTargeted) - .def("GetX", &Lua_Mob::GetX) - .def("GetY", &Lua_Mob::GetY) - .def("GetZ", &Lua_Mob::GetZ) - .def("GetHeading", &Lua_Mob::GetHeading) - .def("GetWaypointX", &Lua_Mob::GetWaypointX) - .def("GetWaypointY", &Lua_Mob::GetWaypointY) - .def("GetWaypointZ", &Lua_Mob::GetWaypointZ) - .def("GetWaypointH", &Lua_Mob::GetWaypointH) - .def("GetWaypointPause", &Lua_Mob::GetWaypointPause) - .def("GetWaypointID", &Lua_Mob::GetWaypointID) - .def("SetCurrentWP", &Lua_Mob::SetCurrentWP) - .def("GetSize", &Lua_Mob::GetSize) - .def("Message", &Lua_Mob::Message) - .def("MessageString", &Lua_Mob::MessageString) - .def("Message_StringID", &Lua_Mob::MessageString) - .def("Say", (void(Lua_Mob::*)(const char*))& Lua_Mob::Say) - .def("Say", (void(Lua_Mob::*)(const char*, int))& Lua_Mob::Say) - .def("QuestSay", (void(Lua_Mob::*)(Lua_Client,const char *))&Lua_Mob::QuestSay) - .def("QuestSay", (void(Lua_Mob::*)(Lua_Client,const char *,luabind::adl::object))&Lua_Mob::QuestSay) - .def("Shout", (void(Lua_Mob::*)(const char*))& Lua_Mob::Shout) - .def("Shout", (void(Lua_Mob::*)(const char*, int))& Lua_Mob::Shout) - .def("Emote", &Lua_Mob::Emote) - .def("InterruptSpell", (void(Lua_Mob::*)(void))&Lua_Mob::InterruptSpell) - .def("InterruptSpell", (void(Lua_Mob::*)(int))&Lua_Mob::InterruptSpell) - .def("CastSpell", (bool(Lua_Mob::*)(int,int))&Lua_Mob::CastSpell) - .def("CastSpell", (bool(Lua_Mob::*)(int,int,int))&Lua_Mob::CastSpell) - .def("CastSpell", (bool(Lua_Mob::*)(int,int,int,int))&Lua_Mob::CastSpell) - .def("CastSpell", (bool(Lua_Mob::*)(int,int,int,int,int))&Lua_Mob::CastSpell) - .def("CastSpell", (bool(Lua_Mob::*)(int,int,int,int,int,int))&Lua_Mob::CastSpell) - .def("CastSpell", (bool(Lua_Mob::*)(int,int,int,int,int,int,int,int))&Lua_Mob::CastSpell) - .def("CastSpell", (bool(Lua_Mob::*)(int,int,int,int,int,int,int,int,int))&Lua_Mob::CastSpell) - .def("SpellFinished", (bool(Lua_Mob::*)(int,Lua_Mob))&Lua_Mob::SpellFinished) - .def("SpellFinished", (bool(Lua_Mob::*)(int,Lua_Mob,int))&Lua_Mob::SpellFinished) - .def("SpellFinished", (bool(Lua_Mob::*)(int,Lua_Mob,int,int))&Lua_Mob::SpellFinished) - .def("SpellFinished", (bool(Lua_Mob::*)(int,Lua_Mob,int,int,uint32))&Lua_Mob::SpellFinished) - .def("SpellFinished", (bool(Lua_Mob::*)(int,Lua_Mob,int,int,uint32,int))&Lua_Mob::SpellFinished) - .def("SpellFinished", (bool(Lua_Mob::*)(int,Lua_Mob,int,int,uint32,int,bool))&Lua_Mob::SpellFinished) - .def("SendBeginCast", &Lua_Mob::SendBeginCast) - .def("SpellEffect", &Lua_Mob::SpellEffect) - .def("GetPet", &Lua_Mob::GetPet) - .def("GetOwner", &Lua_Mob::GetOwner) - .def("GetHateList", &Lua_Mob::GetHateList) - .def("GetShuffledHateList", &Lua_Mob::GetShuffledHateList) - .def("GetHateListByDistance", (Lua_HateList(Lua_Mob::*)(void))&Lua_Mob::GetHateListByDistance) - .def("GetHateListByDistance", (Lua_HateList(Lua_Mob::*)(int))&Lua_Mob::GetHateListByDistance) - .def("GetHateTop", (Lua_Mob(Lua_Mob::*)(void))&Lua_Mob::GetHateTop) - .def("GetHateDamageTop", (Lua_Mob(Lua_Mob::*)(Lua_Mob))&Lua_Mob::GetHateDamageTop) - .def("GetHateRandom", (Lua_Mob(Lua_Mob::*)(void))&Lua_Mob::GetHateRandom) - .def("GetHateClosest", &Lua_Mob::GetHateClosest) - .def("AddToHateList", (void(Lua_Mob::*)(Lua_Mob))&Lua_Mob::AddToHateList) - .def("AddToHateList", (void(Lua_Mob::*)(Lua_Mob,int))&Lua_Mob::AddToHateList) - .def("AddToHateList", (void(Lua_Mob::*)(Lua_Mob,int,int))&Lua_Mob::AddToHateList) - .def("AddToHateList", (void(Lua_Mob::*)(Lua_Mob,int,int,bool))&Lua_Mob::AddToHateList) - .def("AddToHateList", (void(Lua_Mob::*)(Lua_Mob,int,int,bool,bool))&Lua_Mob::AddToHateList) - .def("AddToHateList", (void(Lua_Mob::*)(Lua_Mob,int,int,bool,bool,bool))&Lua_Mob::AddToHateList) - .def("SetHate", (void(Lua_Mob::*)(Lua_Mob))&Lua_Mob::SetHate) - .def("SetHate", (void(Lua_Mob::*)(Lua_Mob,int))&Lua_Mob::SetHate) - .def("SetHate", (void(Lua_Mob::*)(Lua_Mob,int,int))&Lua_Mob::SetHate) - .def("HalveAggro", &Lua_Mob::HalveAggro) - .def("DoubleAggro", &Lua_Mob::DoubleAggro) - .def("GetHateAmount", (uint32(Lua_Mob::*)(Lua_Mob))&Lua_Mob::GetHateAmount) - .def("GetHateAmount", (uint32(Lua_Mob::*)(Lua_Mob,bool))&Lua_Mob::GetHateAmount) - .def("GetDamageAmount", (uint32(Lua_Mob::*)(Lua_Mob))&Lua_Mob::GetDamageAmount) - .def("WipeHateList", (void(Lua_Mob::*)(void))&Lua_Mob::WipeHateList) - .def("CheckAggro", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::CheckAggro) - .def("Stun", (void(Lua_Mob::*)(int))&Lua_Mob::Stun) - .def("UnStun", (void(Lua_Mob::*)(void))&Lua_Mob::UnStun) - .def("IsStunned", (bool(Lua_Mob::*)(void))&Lua_Mob::IsStunned) - .def("Spin", (void(Lua_Mob::*)(void))&Lua_Mob::Spin) - .def("Kill", (void(Lua_Mob::*)(void))&Lua_Mob::Kill) - .def("CanThisClassDoubleAttack", (bool(Lua_Mob::*)(void))&Lua_Mob::CanThisClassDoubleAttack) - .def("CanThisClassDualWield", (bool(Lua_Mob::*)(void))&Lua_Mob::CanThisClassDualWield) - .def("CanThisClassRiposte", (bool(Lua_Mob::*)(void))&Lua_Mob::CanThisClassRiposte) - .def("CanThisClassDodge", (bool(Lua_Mob::*)(void))&Lua_Mob::CanThisClassDodge) - .def("CanThisClassParry", (bool(Lua_Mob::*)(void))&Lua_Mob::CanThisClassParry) - .def("CanThisClassBlock", (bool(Lua_Mob::*)(void))&Lua_Mob::CanThisClassBlock) - .def("SetInvul", (void(Lua_Mob::*)(bool))&Lua_Mob::SetInvul) - .def("GetInvul", (bool(Lua_Mob::*)(void))&Lua_Mob::GetInvul) - .def("SetExtraHaste", (void(Lua_Mob::*)(int))&Lua_Mob::SetExtraHaste) - .def("GetHaste", (int(Lua_Mob::*)(void))&Lua_Mob::GetHaste) - .def("GetHandToHandDamage", (int(Lua_Mob::*)(void))&Lua_Mob::GetHandToHandDamage) - .def("GetHandToHandDelay", (int(Lua_Mob::*)(void))&Lua_Mob::GetHandToHandDelay) - .def("Mesmerize", (void(Lua_Mob::*)(void))&Lua_Mob::Mesmerize) - .def("IsMezzed", (bool(Lua_Mob::*)(void))&Lua_Mob::IsMezzed) - .def("IsEnraged", (bool(Lua_Mob::*)(void))&Lua_Mob::IsEnraged) - .def("GetReverseFactionCon", (int(Lua_Mob::*)(Lua_Mob))&Lua_Mob::GetReverseFactionCon) - .def("IsAIControlled", (bool(Lua_Mob::*)(void))&Lua_Mob::IsAIControlled) - .def("GetAggroRange", (float(Lua_Mob::*)(void))&Lua_Mob::GetAggroRange) - .def("GetAssistRange", (float(Lua_Mob::*)(void))&Lua_Mob::GetAssistRange) - .def("SetPetOrder", (void(Lua_Mob::*)(int))&Lua_Mob::SetPetOrder) - .def("GetPetOrder", (int(Lua_Mob::*)(void))&Lua_Mob::GetPetOrder) - .def("IsRoamer", (bool(Lua_Mob::*)(void))&Lua_Mob::IsRoamer) - .def("IsRooted", (bool(Lua_Mob::*)(void))&Lua_Mob::IsRooted) - .def("IsEngaged", (bool(Lua_Mob::*)(void))&Lua_Mob::IsEngaged) - .def("FaceTarget", (void(Lua_Mob::*)(Lua_Mob))&Lua_Mob::FaceTarget) - .def("SetHeading", (void(Lua_Mob::*)(double))&Lua_Mob::SetHeading) - .def("CalculateHeadingToTarget", (double(Lua_Mob::*)(double,double))&Lua_Mob::CalculateHeadingToTarget) - .def("RunTo", (void(Lua_Mob::*)(double, double, double))&Lua_Mob::RunTo) - .def("WalkTo", (void(Lua_Mob::*)(double, double, double))&Lua_Mob::WalkTo) - .def("NavigateTo", (void(Lua_Mob::*)(double,double,double))&Lua_Mob::NavigateTo) - .def("StopNavigation", (void(Lua_Mob::*)(void))&Lua_Mob::StopNavigation) - .def("CalculateDistance", (float(Lua_Mob::*)(double,double,double))&Lua_Mob::CalculateDistance) - .def("SendTo", (void(Lua_Mob::*)(double,double,double))&Lua_Mob::SendTo) - .def("SendToFixZ", (void(Lua_Mob::*)(double,double,double))&Lua_Mob::SendToFixZ) - .def("NPCSpecialAttacks", (void(Lua_Mob::*)(const char*,int))&Lua_Mob::NPCSpecialAttacks) - .def("NPCSpecialAttacks", (void(Lua_Mob::*)(const char*,int,bool))&Lua_Mob::NPCSpecialAttacks) - .def("NPCSpecialAttacks", (void(Lua_Mob::*)(const char*,int,bool,bool))&Lua_Mob::NPCSpecialAttacks) - .def("GetResist", (int(Lua_Mob::*)(int))&Lua_Mob::GetResist) - .def("Charmed", (bool(Lua_Mob::*)(void))&Lua_Mob::Charmed) - .def("CheckAggroAmount", (int(Lua_Mob::*)(int))&Lua_Mob::CheckAggroAmount) - .def("CheckAggroAmount", (int(Lua_Mob::*)(int,bool))&Lua_Mob::CheckAggroAmount) - .def("CheckHealAggroAmount", (int(Lua_Mob::*)(int))&Lua_Mob::CheckHealAggroAmount) - .def("CheckHealAggroAmount", (int(Lua_Mob::*)(int,uint32))&Lua_Mob::CheckHealAggroAmount) - .def("GetAA", (int(Lua_Mob::*)(int))&Lua_Mob::GetAA) - .def("GetAAByAAID", (int(Lua_Mob::*)(int))&Lua_Mob::GetAAByAAID) - .def("SetAA", (bool(Lua_Mob::*)(int,int))&Lua_Mob::SetAA) - .def("SetAA", (bool(Lua_Mob::*)(int,int,int))&Lua_Mob::SetAA) - .def("DivineAura", (bool(Lua_Mob::*)(void))&Lua_Mob::DivineAura) - .def("SetOOCRegen", (void(Lua_Mob::*)(int))&Lua_Mob::SetOOCRegen) - .def("GetEntityVariable", (const char*(Lua_Mob::*)(const char*))&Lua_Mob::GetEntityVariable) - .def("SetEntityVariable", (void(Lua_Mob::*)(const char*,const char*))&Lua_Mob::SetEntityVariable) - .def("EntityVariableExists", (bool(Lua_Mob::*)(const char*))&Lua_Mob::EntityVariableExists) - .def("Signal", (void(Lua_Mob::*)(uint32))&Lua_Mob::Signal) - .def("CombatRange", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::CombatRange) - .def("DoSpecialAttackDamage", (void(Lua_Mob::*)(Lua_Mob,int,int))&Lua_Mob::DoSpecialAttackDamage) - .def("DoSpecialAttackDamage", (void(Lua_Mob::*)(Lua_Mob,int,int,int))&Lua_Mob::DoSpecialAttackDamage) - .def("DoSpecialAttackDamage", (void(Lua_Mob::*)(Lua_Mob,int,int,int,int))&Lua_Mob::DoSpecialAttackDamage) - .def("DoSpecialAttackDamage", (void(Lua_Mob::*)(Lua_Mob,int,int,int,int,int))&Lua_Mob::DoSpecialAttackDamage) - .def("DoThrowingAttackDmg", (void(Lua_Mob::*)(Lua_Mob))&Lua_Mob::DoThrowingAttackDmg) - .def("DoThrowingAttackDmg", (void(Lua_Mob::*)(Lua_Mob,Lua_ItemInst))&Lua_Mob::DoThrowingAttackDmg) - .def("DoThrowingAttackDmg", (void(Lua_Mob::*)(Lua_Mob,Lua_ItemInst,Lua_Item))&Lua_Mob::DoThrowingAttackDmg) - .def("DoThrowingAttackDmg", (void(Lua_Mob::*)(Lua_Mob,Lua_ItemInst,Lua_Item,int))&Lua_Mob::DoThrowingAttackDmg) - .def("DoThrowingAttackDmg", (void(Lua_Mob::*)(Lua_Mob,Lua_ItemInst,Lua_Item,int,int))&Lua_Mob::DoThrowingAttackDmg) - .def("DoThrowingAttackDmg", (void(Lua_Mob::*)(Lua_Mob,Lua_ItemInst,Lua_Item,int,int,int))&Lua_Mob::DoThrowingAttackDmg) - .def("DoMeleeSkillAttackDmg", (void(Lua_Mob::*)(Lua_Mob,int,int))&Lua_Mob::DoMeleeSkillAttackDmg) - .def("DoMeleeSkillAttackDmg", (void(Lua_Mob::*)(Lua_Mob,int,int,int))&Lua_Mob::DoMeleeSkillAttackDmg) - .def("DoMeleeSkillAttackDmg", (void(Lua_Mob::*)(Lua_Mob,int,int,int,int))&Lua_Mob::DoMeleeSkillAttackDmg) - .def("DoMeleeSkillAttackDmg", (void(Lua_Mob::*)(Lua_Mob,int,int,int,int,bool))&Lua_Mob::DoMeleeSkillAttackDmg) - .def("DoArcheryAttackDmg", (void(Lua_Mob::*)(Lua_Mob))&Lua_Mob::DoArcheryAttackDmg) - .def("DoArcheryAttackDmg", (void(Lua_Mob::*)(Lua_Mob,Lua_ItemInst))&Lua_Mob::DoArcheryAttackDmg) - .def("DoArcheryAttackDmg", (void(Lua_Mob::*)(Lua_Mob,Lua_ItemInst,Lua_ItemInst))&Lua_Mob::DoArcheryAttackDmg) - .def("DoArcheryAttackDmg", (void(Lua_Mob::*)(Lua_Mob,Lua_ItemInst,Lua_ItemInst,int))&Lua_Mob::DoArcheryAttackDmg) - .def("DoArcheryAttackDmg", (void(Lua_Mob::*)(Lua_Mob,Lua_ItemInst,Lua_ItemInst,int,int))&Lua_Mob::DoArcheryAttackDmg) - .def("DoArcheryAttackDmg", (void(Lua_Mob::*)(Lua_Mob,Lua_ItemInst,Lua_ItemInst,int,int,int))&Lua_Mob::DoArcheryAttackDmg) - .def("CheckLoS", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::CheckLoS) - .def("CheckLoSToLoc", (bool(Lua_Mob::*)(double,double,double))&Lua_Mob::CheckLoSToLoc) - .def("CheckLoSToLoc", (bool(Lua_Mob::*)(double,double,double,double))&Lua_Mob::CheckLoSToLoc) - .def("FindGroundZ", (double(Lua_Mob::*)(double,double))&Lua_Mob::FindGroundZ) - .def("FindGroundZ", (double(Lua_Mob::*)(double,double,double))&Lua_Mob::FindGroundZ) - .def("ProjectileAnimation", (void(Lua_Mob::*)(Lua_Mob,int))&Lua_Mob::ProjectileAnimation) - .def("ProjectileAnimation", (void(Lua_Mob::*)(Lua_Mob,int,bool))&Lua_Mob::ProjectileAnimation) - .def("ProjectileAnimation", (void(Lua_Mob::*)(Lua_Mob,int,bool,double))&Lua_Mob::ProjectileAnimation) - .def("ProjectileAnimation", (void(Lua_Mob::*)(Lua_Mob,int,bool,double,double))&Lua_Mob::ProjectileAnimation) - .def("ProjectileAnimation", (void(Lua_Mob::*)(Lua_Mob,int,bool,double,double,double))&Lua_Mob::ProjectileAnimation) - .def("ProjectileAnimation", (void(Lua_Mob::*)(Lua_Mob,int,bool,double,double,double,double))&Lua_Mob::ProjectileAnimation) - .def("HasNPCSpecialAtk", (bool(Lua_Mob::*)(const char*))&Lua_Mob::HasNPCSpecialAtk) - .def("SendAppearanceEffect", (void(Lua_Mob::*)(uint32,uint32,uint32,uint32,uint32))&Lua_Mob::SendAppearanceEffect) - .def("SendAppearanceEffect", (void(Lua_Mob::*)(uint32,uint32,uint32,uint32,uint32,Lua_Client))&Lua_Mob::SendAppearanceEffect) - .def("SetFlyMode", (void(Lua_Mob::*)(int))&Lua_Mob::SetFlyMode) - .def("SetTexture", (void(Lua_Mob::*)(int))&Lua_Mob::SetTexture) - .def("SetRace", (void(Lua_Mob::*)(int))&Lua_Mob::SetRace) - .def("SetGender", (void(Lua_Mob::*)(int))&Lua_Mob::SetGender) - .def("SendIllusionPacket", (void(Lua_Mob::*)(luabind::adl::object))&Lua_Mob::SendIllusionPacket) - .def("ChangeRace", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeRace) - .def("ChangeGender", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeGender) - .def("ChangeTexture", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeTexture) - .def("ChangeHelmTexture", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeHelmTexture) - .def("ChangeHairColor", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeHairColor) - .def("ChangeBeardColor", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeBeardColor) - .def("ChangeEyeColor1", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeEyeColor1) - .def("ChangeEyeColor2", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeEyeColor2) - .def("ChangeHairStyle", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeHairStyle) - .def("ChangeLuclinFace", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeLuclinFace) - .def("ChangeBeard", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeBeard) - .def("ChangeDrakkinHeritage", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeDrakkinHeritage) - .def("ChangeDrakkinTattoo", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeDrakkinTattoo) - .def("ChangeDrakkinDetails", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeDrakkinDetails) - .def("CameraEffect", (void(Lua_Mob::*)(uint32,uint32))&Lua_Mob::CameraEffect) - .def("CameraEffect", (void(Lua_Mob::*)(uint32,uint32,Lua_Client))&Lua_Mob::CameraEffect) - .def("CameraEffect", (void(Lua_Mob::*)(uint32,uint32,Lua_Client,bool))&Lua_Mob::CameraEffect) - .def("SendSpellEffect", (void(Lua_Mob::*)(uint32,uint32,uint32,bool,uint32))&Lua_Mob::SendSpellEffect) - .def("SendSpellEffect", (void(Lua_Mob::*)(uint32,uint32,uint32,bool,uint32,bool))&Lua_Mob::SendSpellEffect) - .def("SendSpellEffect", (void(Lua_Mob::*)(uint32,uint32,uint32,bool,uint32,bool,Lua_Client))&Lua_Mob::SendSpellEffect) - .def("TempName", (void(Lua_Mob::*)(void))&Lua_Mob::TempName) - .def("TempName", (void(Lua_Mob::*)(const char*))&Lua_Mob::TempName) - .def("GetGlobal", (std::string(Lua_Mob::*)(const char*))&Lua_Mob::GetGlobal) - .def("SetGlobal", (void(Lua_Mob::*)(const char*,const char*,int,const char*))&Lua_Mob::SetGlobal) - .def("SetGlobal", (void(Lua_Mob::*)(const char*,const char*,int,const char*,Lua_Mob))&Lua_Mob::SetGlobal) - .def("TarGlobal", (void(Lua_Mob::*)(const char*,const char*,const char*,int,int,int))&Lua_Mob::TarGlobal) - .def("DelGlobal", (void(Lua_Mob::*)(const char*))&Lua_Mob::DelGlobal) - .def("SetSlotTint", (void(Lua_Mob::*)(int,int,int,int))&Lua_Mob::SetSlotTint) - .def("WearChange", (void(Lua_Mob::*)(int,int,uint32))&Lua_Mob::WearChange) - .def("DoKnockback", (void(Lua_Mob::*)(Lua_Mob,uint32,uint32))&Lua_Mob::DoKnockback) - .def("AddNimbusEffect", (void(Lua_Mob::*)(int))&Lua_Mob::AddNimbusEffect) - .def("RemoveNimbusEffect", (void(Lua_Mob::*)(int))&Lua_Mob::RemoveNimbusEffect) - .def("IsFeared", (bool(Lua_Mob::*)(void))&Lua_Mob::IsFeared) - .def("IsBlind", (bool(Lua_Mob::*)(void))&Lua_Mob::IsBlind) - .def("IsRunning", (bool(Lua_Mob::*)(void))&Lua_Mob::IsRunning) - .def("SetRunning", (void(Lua_Mob::*)(bool))&Lua_Mob::SetRunning) - .def("SetBodyType", (void(Lua_Mob::*)(int,bool))&Lua_Mob::SetBodyType) - .def("SetTargetable", (void(Lua_Mob::*)(bool))&Lua_Mob::SetTargetable) - .def("ModSkillDmgTaken", (void(Lua_Mob::*)(int,int))&Lua_Mob::ModSkillDmgTaken) - .def("GetModSkillDmgTaken", (int(Lua_Mob::*)(int))&Lua_Mob::GetModSkillDmgTaken) - .def("GetSkillDmgTaken", (int(Lua_Mob::*)(int))&Lua_Mob::GetSkillDmgTaken) - .def("GetFcDamageAmtIncoming", &Lua_Mob::GetFcDamageAmtIncoming) - .def("GetSkillDmgAmt", (int(Lua_Mob::*)(int))&Lua_Mob::GetSkillDmgAmt) - .def("SetAllowBeneficial", (void(Lua_Mob::*)(bool))&Lua_Mob::SetAllowBeneficial) - .def("GetAllowBeneficial", (bool(Lua_Mob::*)(void))&Lua_Mob::GetAllowBeneficial) - .def("IsBeneficialAllowed", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::IsBeneficialAllowed) - .def("ModVulnerability", (void(Lua_Mob::*)(int,int))&Lua_Mob::ModVulnerability) - .def("GetModVulnerability", (int(Lua_Mob::*)(int))&Lua_Mob::GetModVulnerability) - .def("SetDisableMelee", (void(Lua_Mob::*)(bool))&Lua_Mob::SetDisableMelee) - .def("IsMeleeDisabled", (bool(Lua_Mob::*)(void))&Lua_Mob::IsMeleeDisabled) - .def("SetFlurryChance", (void(Lua_Mob::*)(int))&Lua_Mob::SetFlurryChance) - .def("GetFlurryChance", (int(Lua_Mob::*)(void))&Lua_Mob::GetFlurryChance) - .def("GetSkill", (int(Lua_Mob::*)(int))&Lua_Mob::GetSkill) - .def("GetSpecialAbility", (int(Lua_Mob::*)(int))&Lua_Mob::GetSpecialAbility) - .def("GetSpecialAbilityParam", (int(Lua_Mob::*)(int,int))&Lua_Mob::GetSpecialAbilityParam) - .def("SetSpecialAbility", (void(Lua_Mob::*)(int,int))&Lua_Mob::SetSpecialAbility) - .def("SetSpecialAbilityParam", (void(Lua_Mob::*)(int,int,int))&Lua_Mob::SetSpecialAbilityParam) - .def("ClearSpecialAbilities", (void(Lua_Mob::*)(void))&Lua_Mob::ClearSpecialAbilities) - .def("ProcessSpecialAbilities", (void(Lua_Mob::*)(std::string))&Lua_Mob::ProcessSpecialAbilities) - .def("GetAppearance", (uint32(Lua_Mob::*)(void))&Lua_Mob::GetAppearance) - .def("SetAppearance", (void(Lua_Mob::*)(int))&Lua_Mob::SetAppearance) - .def("SetAppearance", (void(Lua_Mob::*)(int,bool))&Lua_Mob::SetAppearance) - .def("SetDestructibleObject", (void(Lua_Mob::*)(bool))&Lua_Mob::SetDestructibleObject) - .def("IsImmuneToSpell", (bool(Lua_Mob::*)(int,Lua_Mob))&Lua_Mob::IsImmuneToSpell) - .def("BuffFadeBySpellID", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeBySpellID) - .def("BuffFadeByEffect", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeByEffect) - .def("BuffFadeByEffect", (void(Lua_Mob::*)(int,int))&Lua_Mob::BuffFadeByEffect) - .def("BuffFadeAll", (void(Lua_Mob::*)(void))&Lua_Mob::BuffFadeAll) - .def("BuffFadeBySlot", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeBySlot) - .def("BuffFadeBySlot", (void(Lua_Mob::*)(int,bool))&Lua_Mob::BuffFadeBySlot) - .def("CanBuffStack", (int(Lua_Mob::*)(int,int))&Lua_Mob::CanBuffStack) - .def("CanBuffStack", (int(Lua_Mob::*)(int,int,bool))&Lua_Mob::CanBuffStack) - .def("SetPseudoRoot", (void(Lua_Mob::*)(bool))&Lua_Mob::SetPseudoRoot) - .def("SeeInvisible", (uint8(Lua_Mob::*)(void))&Lua_Mob::SeeInvisible) - .def("SeeInvisibleUndead", (bool(Lua_Mob::*)(void))&Lua_Mob::SeeInvisibleUndead) - .def("SeeHide", (bool(Lua_Mob::*)(void))&Lua_Mob::SeeHide) - .def("SeeImprovedHide", (bool(Lua_Mob::*)(bool))&Lua_Mob::SeeImprovedHide) - .def("GetNimbusEffect1", (uint8(Lua_Mob::*)(void))&Lua_Mob::GetNimbusEffect1) - .def("GetNimbusEffect2", (uint8(Lua_Mob::*)(void))&Lua_Mob::GetNimbusEffect2) - .def("GetNimbusEffect3", (uint8(Lua_Mob::*)(void))&Lua_Mob::GetNimbusEffect3) - .def("IsTargetable", (bool(Lua_Mob::*)(void))&Lua_Mob::IsTargetable) - .def("HasShieldEquiped", (bool(Lua_Mob::*)(void))&Lua_Mob::HasShieldEquiped) - .def("HasTwoHandBluntEquiped", (bool(Lua_Mob::*)(void))&Lua_Mob::HasTwoHandBluntEquiped) - .def("HasTwoHanderEquipped", (bool(Lua_Mob::*)(void))&Lua_Mob::HasTwoHanderEquipped) - .def("GetHerosForgeModel", (int32(Lua_Mob::*)(uint8))&Lua_Mob::GetHerosForgeModel) - .def("IsEliteMaterialItem", (uint32(Lua_Mob::*)(uint8))&Lua_Mob::IsEliteMaterialItem) - .def("GetBaseSize", (double(Lua_Mob::*)(void))&Lua_Mob::GetBaseSize) - .def("HasOwner", (bool(Lua_Mob::*)(void))&Lua_Mob::HasOwner) - .def("IsPet", (bool(Lua_Mob::*)(void))&Lua_Mob::IsPet) - .def("HasPet", (bool(Lua_Mob::*)(void))&Lua_Mob::HasPet) - .def("RemovePet", &Lua_Mob::RemovePet) - .def("SetPet", &Lua_Mob::SetPet) - .def("IsSilenced", (bool(Lua_Mob::*)(void))&Lua_Mob::IsSilenced) - .def("IsAmnesiad", (bool(Lua_Mob::*)(void))&Lua_Mob::IsAmnesiad) - .def("GetMeleeMitigation", (int32(Lua_Mob::*)(void))&Lua_Mob::GetMeleeMitigation) - .def("GetWeaponDamageBonus", &Lua_Mob::GetWeaponDamageBonus) - .def("GetItemBonuses", &Lua_Mob::GetItemBonuses) - .def("GetSpellBonuses", &Lua_Mob::GetSpellBonuses) - .def("GetAABonuses", &Lua_Mob::GetAABonuses) - .def("GetMeleeDamageMod_SE", &Lua_Mob::GetMeleeDamageMod_SE) - .def("GetMeleeMinDamageMod_SE", &Lua_Mob::GetMeleeMinDamageMod_SE) - .def("IsAttackAllowed", &Lua_Mob::IsAttackAllowed) - .def("IsCasting", &Lua_Mob::IsCasting) - .def("AttackAnimation", &Lua_Mob::AttackAnimation) - .def("GetWeaponDamage", &Lua_Mob::GetWeaponDamage) - .def("IsBerserk", &Lua_Mob::IsBerserk) - .def("TryFinishingBlow", &Lua_Mob::TryFinishingBlow) - .def("GetBodyType", &Lua_Mob::GetBodyType) - .def("GetOrigBodyType", &Lua_Mob::GetOrigBodyType) - .def("CheckNumHitsRemaining", &Lua_Mob::CheckNumHitsRemaining) - .def("DeleteBucket", (void(Lua_Mob::*)(std::string))&Lua_Mob::DeleteBucket) - .def("GetBucket", (std::string(Lua_Mob::*)(std::string))&Lua_Mob::GetBucket) - .def("GetBucketExpires", (std::string(Lua_Mob::*)(std::string))&Lua_Mob::GetBucketExpires) - .def("GetBucketKey", (std::string(Lua_Mob::*)(void))&Lua_Mob::GetBucketKey) - .def("GetBucketRemaining", (std::string(Lua_Mob::*)(std::string))&Lua_Mob::GetBucketRemaining) - .def("SetBucket", (void(Lua_Mob::*)(std::string,std::string))&Lua_Mob::SetBucket) - .def("SetBucket", (void(Lua_Mob::*)(std::string,std::string,std::string))&Lua_Mob::SetBucket) - .def("IsHorse", &Lua_Mob::IsHorse) - .def("GetLastName", &Lua_Mob::GetLastName) - .def("CanClassEquipItem", &Lua_Mob::CanClassEquipItem) - .def("CanRaceEquipItem", &Lua_Mob::CanRaceEquipItem) - .def("RemoveAllNimbusEffects", &Lua_Mob::RemoveAllNimbusEffects) + .def(luabind::constructor<>()) + .def("AddNimbusEffect", (void(Lua_Mob::*)(int))&Lua_Mob::AddNimbusEffect) + .def("AddToHateList", (void(Lua_Mob::*)(Lua_Mob))&Lua_Mob::AddToHateList) + .def("AddToHateList", (void(Lua_Mob::*)(Lua_Mob,int))&Lua_Mob::AddToHateList) + .def("AddToHateList", (void(Lua_Mob::*)(Lua_Mob,int,int))&Lua_Mob::AddToHateList) + .def("AddToHateList", (void(Lua_Mob::*)(Lua_Mob,int,int,bool))&Lua_Mob::AddToHateList) + .def("AddToHateList", (void(Lua_Mob::*)(Lua_Mob,int,int,bool,bool))&Lua_Mob::AddToHateList) + .def("AddToHateList", (void(Lua_Mob::*)(Lua_Mob,int,int,bool,bool,bool))&Lua_Mob::AddToHateList) + .def("Attack", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::Attack) + .def("Attack", (bool(Lua_Mob::*)(Lua_Mob,int))&Lua_Mob::Attack) + .def("Attack", (bool(Lua_Mob::*)(Lua_Mob,int,bool))&Lua_Mob::Attack) + .def("Attack", (bool(Lua_Mob::*)(Lua_Mob,int,bool,bool))&Lua_Mob::Attack) + .def("Attack", (bool(Lua_Mob::*)(Lua_Mob,int,bool,bool,bool))&Lua_Mob::Attack) + .def("Attack", (bool(Lua_Mob::*)(Lua_Mob,int,bool,bool,bool,luabind::adl::object))&Lua_Mob::Attack) + .def("AttackAnimation", &Lua_Mob::AttackAnimation) + .def("BehindMob", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::BehindMob) + .def("BehindMob", (bool(Lua_Mob::*)(Lua_Mob,float))&Lua_Mob::BehindMob) + .def("BehindMob", (bool(Lua_Mob::*)(Lua_Mob,float,float))&Lua_Mob::BehindMob) + .def("BehindMob", (bool(Lua_Mob::*)(void))&Lua_Mob::BehindMob) + .def("BuffCount", &Lua_Mob::BuffCount) + .def("BuffFadeAll", (void(Lua_Mob::*)(void))&Lua_Mob::BuffFadeAll) + .def("BuffFadeByEffect", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeByEffect) + .def("BuffFadeByEffect", (void(Lua_Mob::*)(int,int))&Lua_Mob::BuffFadeByEffect) + .def("BuffFadeBySlot", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeBySlot) + .def("BuffFadeBySlot", (void(Lua_Mob::*)(int,bool))&Lua_Mob::BuffFadeBySlot) + .def("BuffFadeBySpellID", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeBySpellID) + .def("CalculateDistance", (float(Lua_Mob::*)(double,double,double))&Lua_Mob::CalculateDistance) + .def("CalculateHeadingToTarget", (double(Lua_Mob::*)(double,double))&Lua_Mob::CalculateHeadingToTarget) + .def("CameraEffect", (void(Lua_Mob::*)(uint32,uint32))&Lua_Mob::CameraEffect) + .def("CameraEffect", (void(Lua_Mob::*)(uint32,uint32,Lua_Client))&Lua_Mob::CameraEffect) + .def("CameraEffect", (void(Lua_Mob::*)(uint32,uint32,Lua_Client,bool))&Lua_Mob::CameraEffect) + .def("CanBuffStack", (int(Lua_Mob::*)(int,int))&Lua_Mob::CanBuffStack) + .def("CanBuffStack", (int(Lua_Mob::*)(int,int,bool))&Lua_Mob::CanBuffStack) + .def("CanClassEquipItem", &Lua_Mob::CanClassEquipItem) + .def("CanRaceEquipItem", &Lua_Mob::CanRaceEquipItem) + .def("CanThisClassBlock", (bool(Lua_Mob::*)(void))&Lua_Mob::CanThisClassBlock) + .def("CanThisClassDodge", (bool(Lua_Mob::*)(void))&Lua_Mob::CanThisClassDodge) + .def("CanThisClassDoubleAttack", (bool(Lua_Mob::*)(void))&Lua_Mob::CanThisClassDoubleAttack) + .def("CanThisClassDualWield", (bool(Lua_Mob::*)(void))&Lua_Mob::CanThisClassDualWield) + .def("CanThisClassParry", (bool(Lua_Mob::*)(void))&Lua_Mob::CanThisClassParry) + .def("CanThisClassRiposte", (bool(Lua_Mob::*)(void))&Lua_Mob::CanThisClassRiposte) + .def("CastSpell", (bool(Lua_Mob::*)(int,int))&Lua_Mob::CastSpell) + .def("CastSpell", (bool(Lua_Mob::*)(int,int,int))&Lua_Mob::CastSpell) + .def("CastSpell", (bool(Lua_Mob::*)(int,int,int,int))&Lua_Mob::CastSpell) + .def("CastSpell", (bool(Lua_Mob::*)(int,int,int,int,int))&Lua_Mob::CastSpell) + .def("CastSpell", (bool(Lua_Mob::*)(int,int,int,int,int,int))&Lua_Mob::CastSpell) + .def("CastSpell", (bool(Lua_Mob::*)(int,int,int,int,int,int,int,int))&Lua_Mob::CastSpell) + .def("CastSpell", (bool(Lua_Mob::*)(int,int,int,int,int,int,int,int,int))&Lua_Mob::CastSpell) + .def("ChangeBeard", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeBeard) + .def("ChangeBeardColor", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeBeardColor) + .def("ChangeDrakkinDetails", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeDrakkinDetails) + .def("ChangeDrakkinHeritage", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeDrakkinHeritage) + .def("ChangeDrakkinTattoo", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeDrakkinTattoo) + .def("ChangeEyeColor1", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeEyeColor1) + .def("ChangeEyeColor2", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeEyeColor2) + .def("ChangeGender", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeGender) + .def("ChangeHairColor", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeHairColor) + .def("ChangeHairStyle", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeHairStyle) + .def("ChangeHelmTexture", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeHelmTexture) + .def("ChangeLuclinFace", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeLuclinFace) + .def("ChangeRace", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeRace) + .def("ChangeSize", (void(Lua_Mob::*)(double))&Lua_Mob::ChangeSize) + .def("ChangeSize", (void(Lua_Mob::*)(double,bool))&Lua_Mob::ChangeSize) + .def("ChangeTexture", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeTexture) + .def("Charmed", (bool(Lua_Mob::*)(void))&Lua_Mob::Charmed) + .def("CheckAggro", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::CheckAggro) + .def("CheckAggroAmount", (int(Lua_Mob::*)(int))&Lua_Mob::CheckAggroAmount) + .def("CheckAggroAmount", (int(Lua_Mob::*)(int,bool))&Lua_Mob::CheckAggroAmount) + .def("CheckHealAggroAmount", (int(Lua_Mob::*)(int))&Lua_Mob::CheckHealAggroAmount) + .def("CheckHealAggroAmount", (int(Lua_Mob::*)(int,uint32))&Lua_Mob::CheckHealAggroAmount) + .def("CheckLoS", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::CheckLoS) + .def("CheckLoSToLoc", (bool(Lua_Mob::*)(double,double,double))&Lua_Mob::CheckLoSToLoc) + .def("CheckLoSToLoc", (bool(Lua_Mob::*)(double,double,double,double))&Lua_Mob::CheckLoSToLoc) + .def("CheckNumHitsRemaining", &Lua_Mob::CheckNumHitsRemaining) + .def("ClearSpecialAbilities", (void(Lua_Mob::*)(void))&Lua_Mob::ClearSpecialAbilities) + .def("CombatRange", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::CombatRange) + .def("Damage", (void(Lua_Mob::*)(Lua_Mob,int,int,int))&Lua_Mob::Damage) + .def("Damage", (void(Lua_Mob::*)(Lua_Mob,int,int,int,bool))&Lua_Mob::Damage) + .def("Damage", (void(Lua_Mob::*)(Lua_Mob,int,int,int,bool,int))&Lua_Mob::Damage) + .def("Damage", (void(Lua_Mob::*)(Lua_Mob,int,int,int,bool,int,bool))&Lua_Mob::Damage) + .def("DelGlobal", (void(Lua_Mob::*)(const char*))&Lua_Mob::DelGlobal) + .def("DeleteBucket", (void(Lua_Mob::*)(std::string))&Lua_Mob::DeleteBucket) + .def("Depop", (void(Lua_Mob::*)(bool))&Lua_Mob::Depop) + .def("Depop", (void(Lua_Mob::*)(void))&Lua_Mob::Depop) + .def("DivineAura", (bool(Lua_Mob::*)(void))&Lua_Mob::DivineAura) + .def("DoAnim", (void(Lua_Mob::*)(int))&Lua_Mob::DoAnim) + .def("DoAnim", (void(Lua_Mob::*)(int,int))&Lua_Mob::DoAnim) + .def("DoAnim", (void(Lua_Mob::*)(int,int,bool))&Lua_Mob::DoAnim) + .def("DoAnim", (void(Lua_Mob::*)(int,int,bool,int))&Lua_Mob::DoAnim) + .def("DoArcheryAttackDmg", (void(Lua_Mob::*)(Lua_Mob))&Lua_Mob::DoArcheryAttackDmg) + .def("DoArcheryAttackDmg", (void(Lua_Mob::*)(Lua_Mob,Lua_ItemInst))&Lua_Mob::DoArcheryAttackDmg) + .def("DoArcheryAttackDmg", (void(Lua_Mob::*)(Lua_Mob,Lua_ItemInst,Lua_ItemInst))&Lua_Mob::DoArcheryAttackDmg) + .def("DoArcheryAttackDmg", (void(Lua_Mob::*)(Lua_Mob,Lua_ItemInst,Lua_ItemInst,int))&Lua_Mob::DoArcheryAttackDmg) + .def("DoArcheryAttackDmg", (void(Lua_Mob::*)(Lua_Mob,Lua_ItemInst,Lua_ItemInst,int,int))&Lua_Mob::DoArcheryAttackDmg) + .def("DoArcheryAttackDmg", (void(Lua_Mob::*)(Lua_Mob,Lua_ItemInst,Lua_ItemInst,int,int,int))&Lua_Mob::DoArcheryAttackDmg) + .def("DoKnockback", (void(Lua_Mob::*)(Lua_Mob,uint32,uint32))&Lua_Mob::DoKnockback) + .def("DoMeleeSkillAttackDmg", (void(Lua_Mob::*)(Lua_Mob,int,int))&Lua_Mob::DoMeleeSkillAttackDmg) + .def("DoMeleeSkillAttackDmg", (void(Lua_Mob::*)(Lua_Mob,int,int,int))&Lua_Mob::DoMeleeSkillAttackDmg) + .def("DoMeleeSkillAttackDmg", (void(Lua_Mob::*)(Lua_Mob,int,int,int,int))&Lua_Mob::DoMeleeSkillAttackDmg) + .def("DoMeleeSkillAttackDmg", (void(Lua_Mob::*)(Lua_Mob,int,int,int,int,bool))&Lua_Mob::DoMeleeSkillAttackDmg) + .def("DoSpecialAttackDamage", (void(Lua_Mob::*)(Lua_Mob,int,int))&Lua_Mob::DoSpecialAttackDamage) + .def("DoSpecialAttackDamage", (void(Lua_Mob::*)(Lua_Mob,int,int,int))&Lua_Mob::DoSpecialAttackDamage) + .def("DoSpecialAttackDamage", (void(Lua_Mob::*)(Lua_Mob,int,int,int,int))&Lua_Mob::DoSpecialAttackDamage) + .def("DoSpecialAttackDamage", (void(Lua_Mob::*)(Lua_Mob,int,int,int,int,int))&Lua_Mob::DoSpecialAttackDamage) + .def("DoThrowingAttackDmg", (void(Lua_Mob::*)(Lua_Mob))&Lua_Mob::DoThrowingAttackDmg) + .def("DoThrowingAttackDmg", (void(Lua_Mob::*)(Lua_Mob,Lua_ItemInst))&Lua_Mob::DoThrowingAttackDmg) + .def("DoThrowingAttackDmg", (void(Lua_Mob::*)(Lua_Mob,Lua_ItemInst,Lua_Item))&Lua_Mob::DoThrowingAttackDmg) + .def("DoThrowingAttackDmg", (void(Lua_Mob::*)(Lua_Mob,Lua_ItemInst,Lua_Item,int))&Lua_Mob::DoThrowingAttackDmg) + .def("DoThrowingAttackDmg", (void(Lua_Mob::*)(Lua_Mob,Lua_ItemInst,Lua_Item,int,int))&Lua_Mob::DoThrowingAttackDmg) + .def("DoThrowingAttackDmg", (void(Lua_Mob::*)(Lua_Mob,Lua_ItemInst,Lua_Item,int,int,int))&Lua_Mob::DoThrowingAttackDmg) + .def("DoubleAggro", &Lua_Mob::DoubleAggro) + .def("Emote", &Lua_Mob::Emote) + .def("EntityVariableExists", (bool(Lua_Mob::*)(const char*))&Lua_Mob::EntityVariableExists) + .def("FaceTarget", (void(Lua_Mob::*)(Lua_Mob))&Lua_Mob::FaceTarget) + .def("FindBuff", &Lua_Mob::FindBuff) + .def("FindBuffBySlot", (uint16(Lua_Mob::*)(int))&Lua_Mob::FindBuffBySlot) + .def("FindGroundZ", (double(Lua_Mob::*)(double,double))&Lua_Mob::FindGroundZ) + .def("FindGroundZ", (double(Lua_Mob::*)(double,double,double))&Lua_Mob::FindGroundZ) + .def("FindType", (bool(Lua_Mob::*)(int))&Lua_Mob::FindType) + .def("FindType", (bool(Lua_Mob::*)(int,bool))&Lua_Mob::FindType) + .def("FindType", (bool(Lua_Mob::*)(int,bool,int))&Lua_Mob::FindType) + .def("GMMove", (void(Lua_Mob::*)(double,double,double))&Lua_Mob::GMMove) + .def("GMMove", (void(Lua_Mob::*)(double,double,double,double))&Lua_Mob::GMMove) + .def("GMMove", (void(Lua_Mob::*)(double,double,double,double,bool))&Lua_Mob::GMMove) + .def("GetAA", (int(Lua_Mob::*)(int))&Lua_Mob::GetAA) + .def("GetAABonuses", &Lua_Mob::GetAABonuses) + .def("GetAAByAAID", (int(Lua_Mob::*)(int))&Lua_Mob::GetAAByAAID) + .def("GetAC", &Lua_Mob::GetAC) + .def("GetAGI", &Lua_Mob::GetAGI) + .def("GetATK", &Lua_Mob::GetATK) + .def("GetAggroRange", (float(Lua_Mob::*)(void))&Lua_Mob::GetAggroRange) + .def("GetAllowBeneficial", (bool(Lua_Mob::*)(void))&Lua_Mob::GetAllowBeneficial) + .def("GetAppearance", (uint32(Lua_Mob::*)(void))&Lua_Mob::GetAppearance) + .def("GetAssistRange", (float(Lua_Mob::*)(void))&Lua_Mob::GetAssistRange) + .def("GetBaseGender", &Lua_Mob::GetBaseGender) + .def("GetBaseRace", &Lua_Mob::GetBaseRace) + .def("GetBaseSize", (double(Lua_Mob::*)(void))&Lua_Mob::GetBaseSize) + .def("GetBeard", &Lua_Mob::GetBeard) + .def("GetBeardColor", &Lua_Mob::GetBeardColor) + .def("GetBodyType", &Lua_Mob::GetBodyType) + .def("GetBucket", (std::string(Lua_Mob::*)(std::string))&Lua_Mob::GetBucket) + .def("GetBucketExpires", (std::string(Lua_Mob::*)(std::string))&Lua_Mob::GetBucketExpires) + .def("GetBucketKey", (std::string(Lua_Mob::*)(void))&Lua_Mob::GetBucketKey) + .def("GetBucketRemaining", (std::string(Lua_Mob::*)(std::string))&Lua_Mob::GetBucketRemaining) + .def("GetBuffSlotFromType", &Lua_Mob::GetBuffSlotFromType) + .def("GetCHA", &Lua_Mob::GetCHA) + .def("GetCR", &Lua_Mob::GetCR) + .def("GetCasterLevel", &Lua_Mob::GetCasterLevel) + .def("GetClass", &Lua_Mob::GetClass) + .def("GetClassName", &Lua_Mob::GetClassName) + .def("GetCleanName", &Lua_Mob::GetCleanName) + .def("GetCorruption", &Lua_Mob::GetCorruption) + .def("GetDEX", &Lua_Mob::GetDEX) + .def("GetDR", &Lua_Mob::GetDR) + .def("GetDamageAmount", (uint32(Lua_Mob::*)(Lua_Mob))&Lua_Mob::GetDamageAmount) + .def("GetDeity", &Lua_Mob::GetDeity) + .def("GetDisplayAC", &Lua_Mob::GetDisplayAC) + .def("GetDrakkinDetails", &Lua_Mob::GetDrakkinDetails) + .def("GetDrakkinHeritage", &Lua_Mob::GetDrakkinHeritage) + .def("GetDrakkinTattoo", &Lua_Mob::GetDrakkinTattoo) + .def("GetEntityVariable", (const char*(Lua_Mob::*)(const char*))&Lua_Mob::GetEntityVariable) + .def("GetEyeColor1", &Lua_Mob::GetEyeColor1) + .def("GetEyeColor2", &Lua_Mob::GetEyeColor2) + .def("GetFR", &Lua_Mob::GetFR) + .def("GetFcDamageAmtIncoming", &Lua_Mob::GetFcDamageAmtIncoming) + .def("GetFlurryChance", (int(Lua_Mob::*)(void))&Lua_Mob::GetFlurryChance) + .def("GetGender", &Lua_Mob::GetGender) + .def("GetGlobal", (std::string(Lua_Mob::*)(const char*))&Lua_Mob::GetGlobal) + .def("GetHP", &Lua_Mob::GetHP) + .def("GetHPRatio", &Lua_Mob::GetHPRatio) + .def("GetHairColor", &Lua_Mob::GetHairColor) + .def("GetHairStyle", &Lua_Mob::GetHairStyle) + .def("GetHandToHandDamage", (int(Lua_Mob::*)(void))&Lua_Mob::GetHandToHandDamage) + .def("GetHandToHandDelay", (int(Lua_Mob::*)(void))&Lua_Mob::GetHandToHandDelay) + .def("GetHaste", (int(Lua_Mob::*)(void))&Lua_Mob::GetHaste) + .def("GetHateAmount", (uint32(Lua_Mob::*)(Lua_Mob))&Lua_Mob::GetHateAmount) + .def("GetHateAmount", (uint32(Lua_Mob::*)(Lua_Mob,bool))&Lua_Mob::GetHateAmount) + .def("GetHateClosest", &Lua_Mob::GetHateClosest) + .def("GetHateDamageTop", (Lua_Mob(Lua_Mob::*)(Lua_Mob))&Lua_Mob::GetHateDamageTop) + .def("GetHateList", &Lua_Mob::GetHateList) + .def("GetHateListByDistance", (Lua_HateList(Lua_Mob::*)(int))&Lua_Mob::GetHateListByDistance) + .def("GetHateListByDistance", (Lua_HateList(Lua_Mob::*)(void))&Lua_Mob::GetHateListByDistance) + .def("GetHateRandom", (Lua_Mob(Lua_Mob::*)(void))&Lua_Mob::GetHateRandom) #ifdef BOTS - .def("GetHateRandomBot", (Lua_Bot(Lua_Mob::*)(void))&Lua_Mob::GetHateRandomBot) + .def("GetHateRandomBot", (Lua_Bot(Lua_Mob::*)(void))&Lua_Mob::GetHateRandomBot) #endif - .def("GetHateRandomClient", (Lua_Client(Lua_Mob::*)(void))&Lua_Mob::GetHateRandomClient) - .def("GetHateRandomNPC", (Lua_NPC(Lua_Mob::*)(void))&Lua_Mob::GetHateRandomNPC); + .def("GetHateRandomClient", (Lua_Client(Lua_Mob::*)(void))&Lua_Mob::GetHateRandomClient) + .def("GetHateRandomNPC", (Lua_NPC(Lua_Mob::*)(void))&Lua_Mob::GetHateRandomNPC) + .def("GetHateTop", (Lua_Mob(Lua_Mob::*)(void))&Lua_Mob::GetHateTop) + .def("GetHeading", &Lua_Mob::GetHeading) + .def("GetHelmTexture", &Lua_Mob::GetHelmTexture) + .def("GetHerosForgeModel", (int32(Lua_Mob::*)(uint8))&Lua_Mob::GetHerosForgeModel) + .def("GetINT", &Lua_Mob::GetINT) + .def("GetInvul", (bool(Lua_Mob::*)(void))&Lua_Mob::GetInvul) + .def("GetItemBonuses", &Lua_Mob::GetItemBonuses) + .def("GetItemHPBonuses", &Lua_Mob::GetItemHPBonuses) + .def("GetItemStat", (int(Lua_Mob::*)(uint32,const char*))&Lua_Mob::GetItemStat) + .def("GetLastName", &Lua_Mob::GetLastName) + .def("GetLevel", &Lua_Mob::GetLevel) + .def("GetLevelCon", (uint32(Lua_Mob::*)(int))&Lua_Mob::GetLevelCon) + .def("GetLevelCon", (uint32(Lua_Mob::*)(int,int))&Lua_Mob::GetLevelCon) + .def("GetLuclinFace", &Lua_Mob::GetLuclinFace) + .def("GetMR", &Lua_Mob::GetMR) + .def("GetMana", &Lua_Mob::GetMana) + .def("GetManaRatio", &Lua_Mob::GetManaRatio) + .def("GetMaxAGI", &Lua_Mob::GetMaxAGI) + .def("GetMaxCHA", &Lua_Mob::GetMaxCHA) + .def("GetMaxDEX", &Lua_Mob::GetMaxDEX) + .def("GetMaxHP", &Lua_Mob::GetMaxHP) + .def("GetMaxINT", &Lua_Mob::GetMaxINT) + .def("GetMaxMana", &Lua_Mob::GetMaxMana) + .def("GetMaxSTA", &Lua_Mob::GetMaxSTA) + .def("GetMaxSTR", &Lua_Mob::GetMaxSTR) + .def("GetMaxWIS", &Lua_Mob::GetMaxWIS) + .def("GetMeleeDamageMod_SE", &Lua_Mob::GetMeleeDamageMod_SE) + .def("GetMeleeMinDamageMod_SE", &Lua_Mob::GetMeleeMinDamageMod_SE) + .def("GetMeleeMitigation", (int32(Lua_Mob::*)(void))&Lua_Mob::GetMeleeMitigation) + .def("GetModSkillDmgTaken", (int(Lua_Mob::*)(int))&Lua_Mob::GetModSkillDmgTaken) + .def("GetModVulnerability", (int(Lua_Mob::*)(int))&Lua_Mob::GetModVulnerability) + .def("GetNPCTypeID", &Lua_Mob::GetNPCTypeID) + .def("GetName", &Lua_Mob::GetName) + .def("GetNimbusEffect1", (uint8(Lua_Mob::*)(void))&Lua_Mob::GetNimbusEffect1) + .def("GetNimbusEffect2", (uint8(Lua_Mob::*)(void))&Lua_Mob::GetNimbusEffect2) + .def("GetNimbusEffect3", (uint8(Lua_Mob::*)(void))&Lua_Mob::GetNimbusEffect3) + .def("GetOrigBodyType", &Lua_Mob::GetOrigBodyType) + .def("GetOwner", &Lua_Mob::GetOwner) + .def("GetPR", &Lua_Mob::GetPR) + .def("GetPet", &Lua_Mob::GetPet) + .def("GetPetOrder", (int(Lua_Mob::*)(void))&Lua_Mob::GetPetOrder) + .def("GetPhR", &Lua_Mob::GetPhR) + .def("GetRace", &Lua_Mob::GetRace) + .def("GetRaceName", &Lua_Mob::GetRaceName) + .def("GetResist", (int(Lua_Mob::*)(int))&Lua_Mob::GetResist) + .def("GetReverseFactionCon", (int(Lua_Mob::*)(Lua_Mob))&Lua_Mob::GetReverseFactionCon) + .def("GetRunspeed", &Lua_Mob::GetRunspeed) + .def("GetSTA", &Lua_Mob::GetSTA) + .def("GetSTR", &Lua_Mob::GetSTR) + .def("GetShuffledHateList", &Lua_Mob::GetShuffledHateList) + .def("GetSize", &Lua_Mob::GetSize) + .def("GetSkill", (int(Lua_Mob::*)(int))&Lua_Mob::GetSkill) + .def("GetSkillDmgAmt", (int(Lua_Mob::*)(int))&Lua_Mob::GetSkillDmgAmt) + .def("GetSkillDmgTaken", (int(Lua_Mob::*)(int))&Lua_Mob::GetSkillDmgTaken) + .def("GetSpecialAbility", (int(Lua_Mob::*)(int))&Lua_Mob::GetSpecialAbility) + .def("GetSpecialAbilityParam", (int(Lua_Mob::*)(int,int))&Lua_Mob::GetSpecialAbilityParam) + .def("GetSpecializeSkillValue", &Lua_Mob::GetSpecializeSkillValue) + .def("GetSpellBonuses", &Lua_Mob::GetSpellBonuses) + .def("GetSpellHPBonuses", &Lua_Mob::GetSpellHPBonuses) + .def("GetTarget", &Lua_Mob::GetTarget) + .def("GetTexture", &Lua_Mob::GetTexture) + .def("GetWIS", &Lua_Mob::GetWIS) + .def("GetWalkspeed", &Lua_Mob::GetWalkspeed) + .def("GetWaypointH", &Lua_Mob::GetWaypointH) + .def("GetWaypointID", &Lua_Mob::GetWaypointID) + .def("GetWaypointPause", &Lua_Mob::GetWaypointPause) + .def("GetWaypointX", &Lua_Mob::GetWaypointX) + .def("GetWaypointY", &Lua_Mob::GetWaypointY) + .def("GetWaypointZ", &Lua_Mob::GetWaypointZ) + .def("GetWeaponDamage", &Lua_Mob::GetWeaponDamage) + .def("GetWeaponDamageBonus", &Lua_Mob::GetWeaponDamageBonus) + .def("GetX", &Lua_Mob::GetX) + .def("GetY", &Lua_Mob::GetY) + .def("GetZ", &Lua_Mob::GetZ) + .def("GotoBind", &Lua_Mob::GotoBind) + .def("HalveAggro", &Lua_Mob::HalveAggro) + .def("HasNPCSpecialAtk", (bool(Lua_Mob::*)(const char*))&Lua_Mob::HasNPCSpecialAtk) + .def("HasOwner", (bool(Lua_Mob::*)(void))&Lua_Mob::HasOwner) + .def("HasPet", (bool(Lua_Mob::*)(void))&Lua_Mob::HasPet) + .def("HasProcs", &Lua_Mob::HasProcs) + .def("HasShieldEquiped", (bool(Lua_Mob::*)(void))&Lua_Mob::HasShieldEquiped) + .def("HasTwoHandBluntEquiped", (bool(Lua_Mob::*)(void))&Lua_Mob::HasTwoHandBluntEquiped) + .def("HasTwoHanderEquipped", (bool(Lua_Mob::*)(void))&Lua_Mob::HasTwoHanderEquipped) + .def("Heal", &Lua_Mob::Heal) + .def("HealDamage", (void(Lua_Mob::*)(uint32))&Lua_Mob::HealDamage) + .def("HealDamage", (void(Lua_Mob::*)(uint32,Lua_Mob))&Lua_Mob::HealDamage) + .def("InterruptSpell", (void(Lua_Mob::*)(int))&Lua_Mob::InterruptSpell) + .def("InterruptSpell", (void(Lua_Mob::*)(void))&Lua_Mob::InterruptSpell) + .def("IsAIControlled", (bool(Lua_Mob::*)(void))&Lua_Mob::IsAIControlled) + .def("IsAmnesiad", (bool(Lua_Mob::*)(void))&Lua_Mob::IsAmnesiad) + .def("IsAttackAllowed", &Lua_Mob::IsAttackAllowed) + .def("IsBeneficialAllowed", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::IsBeneficialAllowed) + .def("IsBerserk", &Lua_Mob::IsBerserk) + .def("IsBlind", (bool(Lua_Mob::*)(void))&Lua_Mob::IsBlind) + .def("IsCasting", &Lua_Mob::IsCasting) + .def("IsEliteMaterialItem", (uint32(Lua_Mob::*)(uint8))&Lua_Mob::IsEliteMaterialItem) + .def("IsEngaged", (bool(Lua_Mob::*)(void))&Lua_Mob::IsEngaged) + .def("IsEnraged", (bool(Lua_Mob::*)(void))&Lua_Mob::IsEnraged) + .def("IsFeared", (bool(Lua_Mob::*)(void))&Lua_Mob::IsFeared) + .def("IsHorse", &Lua_Mob::IsHorse) + .def("IsImmuneToSpell", (bool(Lua_Mob::*)(int,Lua_Mob))&Lua_Mob::IsImmuneToSpell) + .def("IsInvisible", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::IsInvisible) + .def("IsInvisible", (bool(Lua_Mob::*)(void))&Lua_Mob::IsInvisible) + .def("IsMeleeDisabled", (bool(Lua_Mob::*)(void))&Lua_Mob::IsMeleeDisabled) + .def("IsMezzed", (bool(Lua_Mob::*)(void))&Lua_Mob::IsMezzed) + .def("IsMoving", &Lua_Mob::IsMoving) + .def("IsPet", (bool(Lua_Mob::*)(void))&Lua_Mob::IsPet) + .def("IsRoamer", (bool(Lua_Mob::*)(void))&Lua_Mob::IsRoamer) + .def("IsRooted", (bool(Lua_Mob::*)(void))&Lua_Mob::IsRooted) + .def("IsRunning", (bool(Lua_Mob::*)(void))&Lua_Mob::IsRunning) + .def("IsSilenced", (bool(Lua_Mob::*)(void))&Lua_Mob::IsSilenced) + .def("IsStunned", (bool(Lua_Mob::*)(void))&Lua_Mob::IsStunned) + .def("IsTargetable", (bool(Lua_Mob::*)(void))&Lua_Mob::IsTargetable) + .def("IsTargeted", &Lua_Mob::IsTargeted) + .def("IsWarriorClass", &Lua_Mob::IsWarriorClass) + .def("Kill", (void(Lua_Mob::*)(void))&Lua_Mob::Kill) + .def("Mesmerize", (void(Lua_Mob::*)(void))&Lua_Mob::Mesmerize) + .def("Message", &Lua_Mob::Message) + .def("MessageString", &Lua_Mob::MessageString) + .def("Message_StringID", &Lua_Mob::MessageString) + .def("ModSkillDmgTaken", (void(Lua_Mob::*)(int,int))&Lua_Mob::ModSkillDmgTaken) + .def("ModVulnerability", (void(Lua_Mob::*)(int,int))&Lua_Mob::ModVulnerability) + .def("NPCSpecialAttacks", (void(Lua_Mob::*)(const char*,int))&Lua_Mob::NPCSpecialAttacks) + .def("NPCSpecialAttacks", (void(Lua_Mob::*)(const char*,int,bool))&Lua_Mob::NPCSpecialAttacks) + .def("NPCSpecialAttacks", (void(Lua_Mob::*)(const char*,int,bool,bool))&Lua_Mob::NPCSpecialAttacks) + .def("NavigateTo", (void(Lua_Mob::*)(double,double,double))&Lua_Mob::NavigateTo) + .def("ProcessSpecialAbilities", (void(Lua_Mob::*)(std::string))&Lua_Mob::ProcessSpecialAbilities) + .def("ProjectileAnimation", (void(Lua_Mob::*)(Lua_Mob,int))&Lua_Mob::ProjectileAnimation) + .def("ProjectileAnimation", (void(Lua_Mob::*)(Lua_Mob,int,bool))&Lua_Mob::ProjectileAnimation) + .def("ProjectileAnimation", (void(Lua_Mob::*)(Lua_Mob,int,bool,double))&Lua_Mob::ProjectileAnimation) + .def("ProjectileAnimation", (void(Lua_Mob::*)(Lua_Mob,int,bool,double,double))&Lua_Mob::ProjectileAnimation) + .def("ProjectileAnimation", (void(Lua_Mob::*)(Lua_Mob,int,bool,double,double,double))&Lua_Mob::ProjectileAnimation) + .def("ProjectileAnimation", (void(Lua_Mob::*)(Lua_Mob,int,bool,double,double,double,double))&Lua_Mob::ProjectileAnimation) + .def("QuestSay", (void(Lua_Mob::*)(Lua_Client,const char *))&Lua_Mob::QuestSay) + .def("QuestSay", (void(Lua_Mob::*)(Lua_Client,const char *,luabind::adl::object))&Lua_Mob::QuestSay) + .def("RandomizeFeatures", (void(Lua_Mob::*)(bool,bool))&Lua_Mob::RandomizeFeatures) + .def("RangedAttack", &Lua_Mob::RangedAttack) + .def("RemoveAllNimbusEffects", &Lua_Mob::RemoveAllNimbusEffects) + .def("RemoveNimbusEffect", (void(Lua_Mob::*)(int))&Lua_Mob::RemoveNimbusEffect) + .def("RemovePet", &Lua_Mob::RemovePet) + .def("ResistSpell", (double(Lua_Mob::*)(int,int,Lua_Mob))&Lua_Mob::ResistSpell) + .def("ResistSpell", (double(Lua_Mob::*)(int,int,Lua_Mob,bool))&Lua_Mob::ResistSpell) + .def("ResistSpell", (double(Lua_Mob::*)(int,int,Lua_Mob,bool,int))&Lua_Mob::ResistSpell) + .def("ResistSpell", (double(Lua_Mob::*)(int,int,Lua_Mob,bool,int,bool))&Lua_Mob::ResistSpell) + .def("RunTo", (void(Lua_Mob::*)(double, double, double))&Lua_Mob::RunTo) + .def("Say", (void(Lua_Mob::*)(const char*))& Lua_Mob::Say) + .def("Say", (void(Lua_Mob::*)(const char*, int))& Lua_Mob::Say) + .def("SeeHide", (bool(Lua_Mob::*)(void))&Lua_Mob::SeeHide) + .def("SeeImprovedHide", (bool(Lua_Mob::*)(bool))&Lua_Mob::SeeImprovedHide) + .def("SeeInvisible", (uint8(Lua_Mob::*)(void))&Lua_Mob::SeeInvisible) + .def("SeeInvisibleUndead", (bool(Lua_Mob::*)(void))&Lua_Mob::SeeInvisibleUndead) + .def("SendAppearanceEffect", (void(Lua_Mob::*)(uint32,uint32,uint32,uint32,uint32))&Lua_Mob::SendAppearanceEffect) + .def("SendAppearanceEffect", (void(Lua_Mob::*)(uint32,uint32,uint32,uint32,uint32,Lua_Client))&Lua_Mob::SendAppearanceEffect) + .def("SendBeginCast", &Lua_Mob::SendBeginCast) + .def("SendIllusionPacket", (void(Lua_Mob::*)(luabind::adl::object))&Lua_Mob::SendIllusionPacket) + .def("SendSpellEffect", (void(Lua_Mob::*)(uint32,uint32,uint32,bool,uint32))&Lua_Mob::SendSpellEffect) + .def("SendSpellEffect", (void(Lua_Mob::*)(uint32,uint32,uint32,bool,uint32,bool))&Lua_Mob::SendSpellEffect) + .def("SendSpellEffect", (void(Lua_Mob::*)(uint32,uint32,uint32,bool,uint32,bool,Lua_Client))&Lua_Mob::SendSpellEffect) + .def("SendTo", (void(Lua_Mob::*)(double,double,double))&Lua_Mob::SendTo) + .def("SendToFixZ", (void(Lua_Mob::*)(double,double,double))&Lua_Mob::SendToFixZ) + .def("SetAA", (bool(Lua_Mob::*)(int,int))&Lua_Mob::SetAA) + .def("SetAA", (bool(Lua_Mob::*)(int,int,int))&Lua_Mob::SetAA) + .def("SetAllowBeneficial", (void(Lua_Mob::*)(bool))&Lua_Mob::SetAllowBeneficial) + .def("SetAppearance", (void(Lua_Mob::*)(int))&Lua_Mob::SetAppearance) + .def("SetAppearance", (void(Lua_Mob::*)(int,bool))&Lua_Mob::SetAppearance) + .def("SetBodyType", (void(Lua_Mob::*)(int,bool))&Lua_Mob::SetBodyType) + .def("SetBucket", (void(Lua_Mob::*)(std::string,std::string))&Lua_Mob::SetBucket) + .def("SetBucket", (void(Lua_Mob::*)(std::string,std::string,std::string))&Lua_Mob::SetBucket) + .def("SetCurrentWP", &Lua_Mob::SetCurrentWP) + .def("SetDestructibleObject", (void(Lua_Mob::*)(bool))&Lua_Mob::SetDestructibleObject) + .def("SetDisableMelee", (void(Lua_Mob::*)(bool))&Lua_Mob::SetDisableMelee) + .def("SetEntityVariable", (void(Lua_Mob::*)(const char*,const char*))&Lua_Mob::SetEntityVariable) + .def("SetExtraHaste", (void(Lua_Mob::*)(int))&Lua_Mob::SetExtraHaste) + .def("SetFlurryChance", (void(Lua_Mob::*)(int))&Lua_Mob::SetFlurryChance) + .def("SetFlyMode", (void(Lua_Mob::*)(int))&Lua_Mob::SetFlyMode) + .def("SetGender", (void(Lua_Mob::*)(int))&Lua_Mob::SetGender) + .def("SetGlobal", (void(Lua_Mob::*)(const char*,const char*,int,const char*))&Lua_Mob::SetGlobal) + .def("SetGlobal", (void(Lua_Mob::*)(const char*,const char*,int,const char*,Lua_Mob))&Lua_Mob::SetGlobal) + .def("SetHP", &Lua_Mob::SetHP) + .def("SetHate", (void(Lua_Mob::*)(Lua_Mob))&Lua_Mob::SetHate) + .def("SetHate", (void(Lua_Mob::*)(Lua_Mob,int))&Lua_Mob::SetHate) + .def("SetHate", (void(Lua_Mob::*)(Lua_Mob,int,int))&Lua_Mob::SetHate) + .def("SetHeading", (void(Lua_Mob::*)(double))&Lua_Mob::SetHeading) + .def("SetInvisible", &Lua_Mob::SetInvisible) + .def("SetInvul", (void(Lua_Mob::*)(bool))&Lua_Mob::SetInvul) + .def("SetLevel", (void(Lua_Mob::*)(int))&Lua_Mob::SetLevel) + .def("SetLevel", (void(Lua_Mob::*)(int,bool))&Lua_Mob::SetLevel) + .def("SetMana", &Lua_Mob::SetMana) + .def("SetOOCRegen", (void(Lua_Mob::*)(int))&Lua_Mob::SetOOCRegen) + .def("SetPet", &Lua_Mob::SetPet) + .def("SetPetOrder", (void(Lua_Mob::*)(int))&Lua_Mob::SetPetOrder) + .def("SetPseudoRoot", (void(Lua_Mob::*)(bool))&Lua_Mob::SetPseudoRoot) + .def("SetRace", (void(Lua_Mob::*)(int))&Lua_Mob::SetRace) + .def("SetRunning", (void(Lua_Mob::*)(bool))&Lua_Mob::SetRunning) + .def("SetSlotTint", (void(Lua_Mob::*)(int,int,int,int))&Lua_Mob::SetSlotTint) + .def("SetSpecialAbility", (void(Lua_Mob::*)(int,int))&Lua_Mob::SetSpecialAbility) + .def("SetSpecialAbilityParam", (void(Lua_Mob::*)(int,int,int))&Lua_Mob::SetSpecialAbilityParam) + .def("SetTarget", &Lua_Mob::SetTarget) + .def("SetTargetable", (void(Lua_Mob::*)(bool))&Lua_Mob::SetTargetable) + .def("SetTexture", (void(Lua_Mob::*)(int))&Lua_Mob::SetTexture) + .def("Shout", (void(Lua_Mob::*)(const char*))& Lua_Mob::Shout) + .def("Shout", (void(Lua_Mob::*)(const char*, int))& Lua_Mob::Shout) + .def("Signal", (void(Lua_Mob::*)(uint32))&Lua_Mob::Signal) + .def("SpellEffect", &Lua_Mob::SpellEffect) + .def("SpellFinished", (bool(Lua_Mob::*)(int,Lua_Mob))&Lua_Mob::SpellFinished) + .def("SpellFinished", (bool(Lua_Mob::*)(int,Lua_Mob,int))&Lua_Mob::SpellFinished) + .def("SpellFinished", (bool(Lua_Mob::*)(int,Lua_Mob,int,int))&Lua_Mob::SpellFinished) + .def("SpellFinished", (bool(Lua_Mob::*)(int,Lua_Mob,int,int,uint32))&Lua_Mob::SpellFinished) + .def("SpellFinished", (bool(Lua_Mob::*)(int,Lua_Mob,int,int,uint32,int))&Lua_Mob::SpellFinished) + .def("SpellFinished", (bool(Lua_Mob::*)(int,Lua_Mob,int,int,uint32,int,bool))&Lua_Mob::SpellFinished) + .def("Spin", (void(Lua_Mob::*)(void))&Lua_Mob::Spin) + .def("StopNavigation", (void(Lua_Mob::*)(void))&Lua_Mob::StopNavigation) + .def("Stun", (void(Lua_Mob::*)(int))&Lua_Mob::Stun) + .def("TarGlobal", (void(Lua_Mob::*)(const char*,const char*,const char*,int,int,int))&Lua_Mob::TarGlobal) + .def("TempName", (void(Lua_Mob::*)(const char*))&Lua_Mob::TempName) + .def("TempName", (void(Lua_Mob::*)(void))&Lua_Mob::TempName) + .def("ThrowingAttack", &Lua_Mob::ThrowingAttack) + .def("TryFinishingBlow", &Lua_Mob::TryFinishingBlow) + .def("TryMoveAlong", (void(Lua_Mob::*)(float,float))&Lua_Mob::TryMoveAlong) + .def("TryMoveAlong", (void(Lua_Mob::*)(float,float,bool))&Lua_Mob::TryMoveAlong) + .def("UnStun", (void(Lua_Mob::*)(void))&Lua_Mob::UnStun) + .def("WalkTo", (void(Lua_Mob::*)(double, double, double))&Lua_Mob::WalkTo) + .def("WearChange", (void(Lua_Mob::*)(int,int,uint32))&Lua_Mob::WearChange) + .def("WipeHateList", (void(Lua_Mob::*)(void))&Lua_Mob::WipeHateList); } luabind::scope lua_register_special_abilities() { return luabind::class_("SpecialAbility") - .enum_("constants") - [ - luabind::value("summon", static_cast(SPECATK_SUMMON)), - luabind::value("enrage", static_cast(SPECATK_ENRAGE)), - luabind::value("rampage", static_cast(SPECATK_RAMPAGE)), - luabind::value("area_rampage", static_cast(SPECATK_AREA_RAMPAGE)), - luabind::value("flurry", static_cast(SPECATK_FLURRY)), - luabind::value("triple_attack", static_cast(SPECATK_TRIPLE)), - luabind::value("quad_attack", static_cast(SPECATK_QUAD)), - luabind::value("innate_dual_wield", static_cast(SPECATK_INNATE_DW)), - luabind::value("bane_attack", static_cast(SPECATK_BANE)), - luabind::value("magical_attack", static_cast(SPECATK_MAGICAL)), - luabind::value("ranged_attack", static_cast(SPECATK_RANGED_ATK)), - luabind::value("unslowable", static_cast(UNSLOWABLE)), - luabind::value("unmezable", static_cast(UNMEZABLE)), - luabind::value("uncharmable", static_cast(UNCHARMABLE)), - luabind::value("unstunable", static_cast(UNSTUNABLE)), - luabind::value("unsnareable", static_cast(UNSNAREABLE)), - luabind::value("unfearable", static_cast(UNFEARABLE)), - luabind::value("undispellable", static_cast(UNDISPELLABLE)), - luabind::value("immune_melee", static_cast(IMMUNE_MELEE)), - luabind::value("immune_magic", static_cast(IMMUNE_MAGIC)), - luabind::value("immune_fleeing", static_cast(IMMUNE_FLEEING)), - luabind::value("immune_melee_except_bane", static_cast(IMMUNE_MELEE_EXCEPT_BANE)), - luabind::value("immune_melee_except_magical", static_cast(IMMUNE_MELEE_NONMAGICAL)), - luabind::value("immune_aggro", static_cast(IMMUNE_AGGRO)), - luabind::value("immune_aggro_on", static_cast(IMMUNE_AGGRO_ON)), - luabind::value("immune_casting_from_range", static_cast(IMMUNE_CASTING_FROM_RANGE)), - luabind::value("immune_feign_death", static_cast(IMMUNE_FEIGN_DEATH)), - luabind::value("immune_taunt", static_cast(IMMUNE_TAUNT)), - luabind::value("tunnelvision", static_cast(NPC_TUNNELVISION)), - luabind::value("dont_buff_friends", static_cast(NPC_NO_BUFFHEAL_FRIENDS)), - luabind::value("immune_pacify", static_cast(IMMUNE_PACIFY)), - luabind::value("leash", static_cast(LEASH)), - luabind::value("tether", static_cast(TETHER)), - luabind::value("destructible_object", static_cast(DESTRUCTIBLE_OBJECT)), - luabind::value("no_harm_from_client", static_cast(NO_HARM_FROM_CLIENT)), - luabind::value("always_flee", static_cast(ALWAYS_FLEE)), - luabind::value("flee_percent", static_cast(FLEE_PERCENT)), - luabind::value("allow_beneficial", static_cast(ALLOW_BENEFICIAL)), - luabind::value("disable_melee", static_cast(DISABLE_MELEE)), - luabind::value("npc_chase_distance", static_cast(NPC_CHASE_DISTANCE)), - luabind::value("allow_to_tank", static_cast(ALLOW_TO_TANK)), - luabind::value("ignore_root_aggro_rules", static_cast(IGNORE_ROOT_AGGRO_RULES)), - luabind::value("casting_resist_diff", static_cast(CASTING_RESIST_DIFF)), - luabind::value("counter_avoid_damage", static_cast(COUNTER_AVOID_DAMAGE)), - luabind::value("immune_ranged_attacks", static_cast(IMMUNE_RANGED_ATTACKS)), - luabind::value("immune_damage_client", static_cast(IMMUNE_DAMAGE_CLIENT)), - luabind::value("immune_damage_npc", static_cast(IMMUNE_DAMAGE_NPC)), - luabind::value("immune_aggro_client", static_cast(IMMUNE_AGGRO_CLIENT)), - luabind::value("immune_aggro_npc", static_cast(IMMUNE_AGGRO_NPC)) - ]; + .enum_("constants") + [ + luabind::value("summon", static_cast(SPECATK_SUMMON)), + luabind::value("enrage", static_cast(SPECATK_ENRAGE)), + luabind::value("rampage", static_cast(SPECATK_RAMPAGE)), + luabind::value("area_rampage", static_cast(SPECATK_AREA_RAMPAGE)), + luabind::value("flurry", static_cast(SPECATK_FLURRY)), + luabind::value("triple_attack", static_cast(SPECATK_TRIPLE)), + luabind::value("quad_attack", static_cast(SPECATK_QUAD)), + luabind::value("innate_dual_wield", static_cast(SPECATK_INNATE_DW)), + luabind::value("bane_attack", static_cast(SPECATK_BANE)), + luabind::value("magical_attack", static_cast(SPECATK_MAGICAL)), + luabind::value("ranged_attack", static_cast(SPECATK_RANGED_ATK)), + luabind::value("unslowable", static_cast(UNSLOWABLE)), + luabind::value("unmezable", static_cast(UNMEZABLE)), + luabind::value("uncharmable", static_cast(UNCHARMABLE)), + luabind::value("unstunable", static_cast(UNSTUNABLE)), + luabind::value("unsnareable", static_cast(UNSNAREABLE)), + luabind::value("unfearable", static_cast(UNFEARABLE)), + luabind::value("undispellable", static_cast(UNDISPELLABLE)), + luabind::value("immune_melee", static_cast(IMMUNE_MELEE)), + luabind::value("immune_magic", static_cast(IMMUNE_MAGIC)), + luabind::value("immune_fleeing", static_cast(IMMUNE_FLEEING)), + luabind::value("immune_melee_except_bane", static_cast(IMMUNE_MELEE_EXCEPT_BANE)), + luabind::value("immune_melee_except_magical", static_cast(IMMUNE_MELEE_NONMAGICAL)), + luabind::value("immune_aggro", static_cast(IMMUNE_AGGRO)), + luabind::value("immune_aggro_on", static_cast(IMMUNE_AGGRO_ON)), + luabind::value("immune_casting_from_range", static_cast(IMMUNE_CASTING_FROM_RANGE)), + luabind::value("immune_feign_death", static_cast(IMMUNE_FEIGN_DEATH)), + luabind::value("immune_taunt", static_cast(IMMUNE_TAUNT)), + luabind::value("tunnelvision", static_cast(NPC_TUNNELVISION)), + luabind::value("dont_buff_friends", static_cast(NPC_NO_BUFFHEAL_FRIENDS)), + luabind::value("immune_pacify", static_cast(IMMUNE_PACIFY)), + luabind::value("leash", static_cast(LEASH)), + luabind::value("tether", static_cast(TETHER)), + luabind::value("destructible_object", static_cast(DESTRUCTIBLE_OBJECT)), + luabind::value("no_harm_from_client", static_cast(NO_HARM_FROM_CLIENT)), + luabind::value("always_flee", static_cast(ALWAYS_FLEE)), + luabind::value("flee_percent", static_cast(FLEE_PERCENT)), + luabind::value("allow_beneficial", static_cast(ALLOW_BENEFICIAL)), + luabind::value("disable_melee", static_cast(DISABLE_MELEE)), + luabind::value("npc_chase_distance", static_cast(NPC_CHASE_DISTANCE)), + luabind::value("allow_to_tank", static_cast(ALLOW_TO_TANK)), + luabind::value("ignore_root_aggro_rules", static_cast(IGNORE_ROOT_AGGRO_RULES)), + luabind::value("casting_resist_diff", static_cast(CASTING_RESIST_DIFF)), + luabind::value("counter_avoid_damage", static_cast(COUNTER_AVOID_DAMAGE)), + luabind::value("immune_ranged_attacks", static_cast(IMMUNE_RANGED_ATTACKS)), + luabind::value("immune_damage_client", static_cast(IMMUNE_DAMAGE_CLIENT)), + luabind::value("immune_damage_npc", static_cast(IMMUNE_DAMAGE_NPC)), + luabind::value("immune_aggro_client", static_cast(IMMUNE_AGGRO_CLIENT)), + luabind::value("immune_aggro_npc", static_cast(IMMUNE_AGGRO_NPC)) + ]; } #endif diff --git a/zone/lua_npc.cpp b/zone/lua_npc.cpp index 8d9fe87ed..74cd4f3c2 100644 --- a/zone/lua_npc.cpp +++ b/zone/lua_npc.cpp @@ -636,134 +636,134 @@ Lua_NPC_Loot_List Lua_NPC::GetLootList(lua_State* L) { } luabind::scope lua_register_npc() { - return luabind::class_("NPC") - .def(luabind::constructor<>()) - .def("Signal", (void(Lua_NPC::*)(int))&Lua_NPC::Signal) - .def("CheckNPCFactionAlly", (int(Lua_NPC::*)(int))&Lua_NPC::CheckNPCFactionAlly) - .def("AddItem", (void(Lua_NPC::*)(int,int))&Lua_NPC::AddItem) - .def("AddItem", (void(Lua_NPC::*)(int,int,bool))&Lua_NPC::AddItem) - .def("AddItem", (void(Lua_NPC::*)(int,int,bool,int))&Lua_NPC::AddItem) - .def("AddItem", (void(Lua_NPC::*)(int,int,bool,int,int))&Lua_NPC::AddItem) - .def("AddItem", (void(Lua_NPC::*)(int,int,bool,int,int,int))&Lua_NPC::AddItem) - .def("AddItem", (void(Lua_NPC::*)(int,int,bool,int,int,int,int))&Lua_NPC::AddItem) - .def("AddItem", (void(Lua_NPC::*)(int,int,bool,int,int,int,int,int))&Lua_NPC::AddItem) - .def("AddItem", (void(Lua_NPC::*)(int,int,bool,int,int,int,int,int,int))&Lua_NPC::AddItem) - .def("AddLootTable", (void(Lua_NPC::*)(void))&Lua_NPC::AddLootTable) - .def("AddLootTable", (void(Lua_NPC::*)(int))&Lua_NPC::AddLootTable) - .def("RemoveItem", (void(Lua_NPC::*)(int))&Lua_NPC::RemoveItem) - .def("RemoveItem", (void(Lua_NPC::*)(int,int))&Lua_NPC::RemoveItem) - .def("RemoveItem", (void(Lua_NPC::*)(int,int,int))&Lua_NPC::RemoveItem) - .def("ClearItemList", (void(Lua_NPC::*)(void))&Lua_NPC::ClearItemList) - .def("AddCash", (void(Lua_NPC::*)(int,int,int,int))&Lua_NPC::AddCash) - .def("RemoveCash", (void(Lua_NPC::*)(void))&Lua_NPC::RemoveCash) - .def("CountLoot", (int(Lua_NPC::*)(void))&Lua_NPC::CountLoot) - .def("GetLoottableID", (int(Lua_NPC::*)(void))&Lua_NPC::GetLoottableID) - .def("GetCopper", (uint32(Lua_NPC::*)(void))&Lua_NPC::GetCopper) - .def("GetSilver", (uint32(Lua_NPC::*)(void))&Lua_NPC::GetSilver) - .def("GetGold", (uint32(Lua_NPC::*)(void))&Lua_NPC::GetGold) - .def("GetPlatinum", (uint32(Lua_NPC::*)(void))&Lua_NPC::GetPlatinum) - .def("SetCopper", (void(Lua_NPC::*)(uint32))&Lua_NPC::SetCopper) - .def("SetSilver", (void(Lua_NPC::*)(uint32))&Lua_NPC::SetSilver) - .def("SetGold", (void(Lua_NPC::*)(uint32))&Lua_NPC::SetGold) - .def("SetPlatinum", (void(Lua_NPC::*)(uint32))&Lua_NPC::SetPlatinum) - .def("SetGrid", (void(Lua_NPC::*)(int))&Lua_NPC::SetGrid) - .def("SetSaveWaypoint", (void(Lua_NPC::*)(int))&Lua_NPC::SetSaveWaypoint) - .def("SetSp2", (void(Lua_NPC::*)(int))&Lua_NPC::SetSp2) - .def("GetWaypointMax", (int(Lua_NPC::*)(void))&Lua_NPC::GetWaypointMax) - .def("GetGrid", (int(Lua_NPC::*)(void))&Lua_NPC::GetGrid) - .def("GetSp2", (uint32(Lua_NPC::*)(void))&Lua_NPC::GetSp2) - .def("GetNPCFactionID", (int(Lua_NPC::*)(void))&Lua_NPC::GetNPCFactionID) - .def("GetPrimaryFaction", (int(Lua_NPC::*)(void))&Lua_NPC::GetPrimaryFaction) - .def("GetNPCHate", (int(Lua_NPC::*)(Lua_Mob))&Lua_NPC::GetNPCHate) - .def("IsOnHatelist", (bool(Lua_NPC::*)(Lua_Mob))&Lua_NPC::IsOnHatelist) - .def("SetNPCFactionID", (void(Lua_NPC::*)(int))&Lua_NPC::SetNPCFactionID) - .def("GetMaxDMG", (uint32(Lua_NPC::*)(void))&Lua_NPC::GetMaxDMG) - .def("GetMinDMG", (uint32(Lua_NPC::*)(void))&Lua_NPC::GetMinDMG) - .def("IsAnimal", (bool(Lua_NPC::*)(void))&Lua_NPC::IsAnimal) - .def("GetPetSpellID", (int(Lua_NPC::*)(void))&Lua_NPC::GetPetSpellID) - .def("SetPetSpellID", (void(Lua_NPC::*)(int))&Lua_NPC::SetPetSpellID) - .def("GetMaxDamage", (uint32(Lua_NPC::*)(int))&Lua_NPC::GetMaxDamage) - .def("SetTaunting", (void(Lua_NPC::*)(bool))&Lua_NPC::SetTaunting) - .def("IsTaunting", (bool(Lua_NPC::*)(void))&Lua_NPC::IsTaunting) - .def("PickPocket", (void(Lua_NPC::*)(Lua_Client))&Lua_NPC::PickPocket) - .def("StartSwarmTimer", (void(Lua_NPC::*)(uint32))&Lua_NPC::StartSwarmTimer) - .def("DoClassAttacks", (void(Lua_NPC::*)(Lua_Mob))&Lua_NPC::DoClassAttacks) - .def("GetMaxWp", (int(Lua_NPC::*)(void))&Lua_NPC::GetMaxWp) - .def("DisplayWaypointInfo", (void(Lua_NPC::*)(Lua_Client))&Lua_NPC::DisplayWaypointInfo) - .def("CalculateNewWaypoint", (void(Lua_NPC::*)(void))&Lua_NPC::CalculateNewWaypoint) - .def("AssignWaypoints", (void(Lua_NPC::*)(int))&Lua_NPC::AssignWaypoints) - .def("SetWaypointPause", (void(Lua_NPC::*)(void))&Lua_NPC::SetWaypointPause) - .def("UpdateWaypoint", (void(Lua_NPC::*)(int))&Lua_NPC::UpdateWaypoint) - .def("StopWandering", (void(Lua_NPC::*)(void))&Lua_NPC::StopWandering) - .def("ResumeWandering", (void(Lua_NPC::*)(void))&Lua_NPC::ResumeWandering) - .def("PauseWandering", (void(Lua_NPC::*)(int))&Lua_NPC::PauseWandering) - .def("MoveTo", (void(Lua_NPC::*)(float,float,float,float,bool))&Lua_NPC::MoveTo) - .def("NextGuardPosition", (void(Lua_NPC::*)(void))&Lua_NPC::NextGuardPosition) - .def("SaveGuardSpot", (void(Lua_NPC::*)(float,float,float,float))&Lua_NPC::SaveGuardSpot) - .def("IsGuarding", (bool(Lua_NPC::*)(void))&Lua_NPC::IsGuarding) - .def("AI_SetRoambox", (void(Lua_NPC::*)(float,float,float,float,float))&Lua_NPC::AI_SetRoambox) - .def("AI_SetRoambox", (void(Lua_NPC::*)(float,float,float,float,float,uint32,uint32))&Lua_NPC::AI_SetRoambox) - .def("SetFollowID", (void(Lua_NPC::*)(int))&Lua_NPC::SetFollowID) - .def("SetFollowDistance", (void(Lua_NPC::*)(int))&Lua_NPC::SetFollowDistance) - .def("SetFollowCanRun", (void(Lua_NPC::*)(bool))&Lua_NPC::SetFollowCanRun) - .def("GetFollowID", (int(Lua_NPC::*)(void))&Lua_NPC::GetFollowID) - .def("GetFollowDistance", (int(Lua_NPC::*)(void))&Lua_NPC::GetFollowDistance) - .def("GetFollowCanRun", (bool(Lua_NPC::*)(void))&Lua_NPC::GetFollowCanRun) - .def("GetNPCSpellsID", (int(Lua_NPC::*)(void))&Lua_NPC::GetNPCSpellsID) - .def("GetNPCSpellsID", (int(Lua_NPC::*)(void))&Lua_NPC::GetNPCSpellsID) - .def("GetSpawnPointID", (int(Lua_NPC::*)(void))&Lua_NPC::GetSpawnPointID) - .def("GetSpawnPointX", (float(Lua_NPC::*)(void))&Lua_NPC::GetSpawnPointX) - .def("GetSpawnPointY", (float(Lua_NPC::*)(void))&Lua_NPC::GetSpawnPointY) - .def("GetSpawnPointZ", (float(Lua_NPC::*)(void))&Lua_NPC::GetSpawnPointZ) - .def("GetSpawnPointH", (float(Lua_NPC::*)(void))&Lua_NPC::GetSpawnPointH) - .def("GetGuardPointX", (float(Lua_NPC::*)(void))&Lua_NPC::GetGuardPointX) - .def("GetGuardPointY", (float(Lua_NPC::*)(void))&Lua_NPC::GetGuardPointY) - .def("GetGuardPointZ", (float(Lua_NPC::*)(void))&Lua_NPC::GetGuardPointZ) - .def("SetPrimSkill", (void(Lua_NPC::*)(int))&Lua_NPC::SetPrimSkill) - .def("SetSecSkill", (void(Lua_NPC::*)(int))&Lua_NPC::SetSecSkill) - .def("SetSimpleRoamBox", (void(Lua_NPC::*)(float))&Lua_NPC::SetSimpleRoamBox) - .def("SetSimpleRoamBox", (void(Lua_NPC::*)(float, float))&Lua_NPC::SetSimpleRoamBox) - .def("SetSimpleRoamBox", (void(Lua_NPC::*)(float, float, int))&Lua_NPC::SetSimpleRoamBox) - .def("GetPrimSkill", (int(Lua_NPC::*)(void))&Lua_NPC::GetPrimSkill) - .def("GetSecSkill", (int(Lua_NPC::*)(void))&Lua_NPC::GetSecSkill) - .def("GetSwarmOwner", (int(Lua_NPC::*)(void))&Lua_NPC::GetSwarmOwner) - .def("GetSwarmTarget", (int(Lua_NPC::*)(void))&Lua_NPC::GetSwarmTarget) - .def("SetSwarmTarget", (void(Lua_NPC::*)(int))&Lua_NPC::SetSwarmTarget) - .def("ModifyNPCStat", (void(Lua_NPC::*)(const char*,const char*))&Lua_NPC::ModifyNPCStat) - .def("AddAISpell", (void(Lua_NPC::*)(int,int,int,int,int,int))&Lua_NPC::AddAISpell) - .def("AddAISpell", (void(Lua_NPC::*)(int,int,int,int,int,int,int,int))&Lua_NPC::AddAISpell) - .def("RemoveAISpell", (void(Lua_NPC::*)(int))&Lua_NPC::RemoveAISpell) - .def("SetSpellFocusDMG", (void(Lua_NPC::*)(int))&Lua_NPC::SetSpellFocusDMG) - .def("SetSpellFocusHeal", (void(Lua_NPC::*)(int))&Lua_NPC::SetSpellFocusHeal) - .def("GetSpellFocusDMG", (void(Lua_NPC::*)(int))&Lua_NPC::GetSpellFocusDMG) - .def("GetSpellFocusHeal", (void(Lua_NPC::*)(int))&Lua_NPC::GetSpellFocusHeal) - .def("GetSlowMitigation", (int(Lua_NPC::*)(void))&Lua_NPC::GetSlowMitigation) - .def("GetAttackSpeed", (float(Lua_NPC::*)(void))&Lua_NPC::GetAttackSpeed) - .def("GetAttackDelay", (int(Lua_NPC::*)(void))&Lua_NPC::GetAttackDelay) - .def("GetAccuracyRating", (int(Lua_NPC::*)(void))&Lua_NPC::GetAccuracyRating) - .def("GetSpawnKillCount", (int(Lua_NPC::*)(void))&Lua_NPC::GetSpawnKillCount) - .def("GetScore", (int(Lua_NPC::*)(void))&Lua_NPC::GetScore) - .def("MerchantOpenShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantOpenShop) - .def("MerchantCloseShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantCloseShop) - .def("GetRawAC", (int(Lua_NPC::*)(void))&Lua_NPC::GetRawAC) - .def("GetAvoidanceRating", &Lua_NPC::GetAvoidanceRating) - .def("RecalculateSkills", (void(Lua_NPC::*)(void))&Lua_NPC::RecalculateSkills) - .def("ScaleNPC", (void(Lua_NPC::*)(uint8))&Lua_NPC::ScaleNPC) - .def("IsRaidTarget", (bool(Lua_NPC::*)(void))&Lua_NPC::IsRaidTarget) - .def("ChangeLastName", (void(Lua_NPC::*)(const char*))&Lua_NPC::ChangeLastName) - .def("ClearLastName", (void(Lua_NPC::*)(void))&Lua_NPC::ClearLastName) - .def("HasItem", (bool(Lua_NPC::*)(uint32))&Lua_NPC::HasItem) - .def("CountItem", (uint16(Lua_NPC::*)(uint32))&Lua_NPC::CountItem) - .def("GetItemIDBySlot", (uint32(Lua_NPC::*)(uint16))&Lua_NPC::GetItemIDBySlot) - .def("GetFirstSlotByItemID", (uint16(Lua_NPC::*)(uint32))&Lua_NPC::GetFirstSlotByItemID) - .def("GetHealScale", (float(Lua_NPC::*)(void))&Lua_NPC::GetHealScale) - .def("GetSpellScale", (float(Lua_NPC::*)(void))&Lua_NPC::GetSpellScale) - .def("GetLootList", (Lua_NPC_Loot_List(Lua_NPC::*)(lua_State* L))&Lua_NPC::GetLootList); + return luabind::class_("NPC") + .def(luabind::constructor<>()) + .def("AI_SetRoambox", (void(Lua_NPC::*)(float,float,float,float,float))&Lua_NPC::AI_SetRoambox) + .def("AI_SetRoambox", (void(Lua_NPC::*)(float,float,float,float,float,uint32,uint32))&Lua_NPC::AI_SetRoambox) + .def("AddAISpell", (void(Lua_NPC::*)(int,int,int,int,int,int))&Lua_NPC::AddAISpell) + .def("AddAISpell", (void(Lua_NPC::*)(int,int,int,int,int,int,int,int))&Lua_NPC::AddAISpell) + .def("AddCash", (void(Lua_NPC::*)(int,int,int,int))&Lua_NPC::AddCash) + .def("AddItem", (void(Lua_NPC::*)(int,int))&Lua_NPC::AddItem) + .def("AddItem", (void(Lua_NPC::*)(int,int,bool))&Lua_NPC::AddItem) + .def("AddItem", (void(Lua_NPC::*)(int,int,bool,int))&Lua_NPC::AddItem) + .def("AddItem", (void(Lua_NPC::*)(int,int,bool,int,int))&Lua_NPC::AddItem) + .def("AddItem", (void(Lua_NPC::*)(int,int,bool,int,int,int))&Lua_NPC::AddItem) + .def("AddItem", (void(Lua_NPC::*)(int,int,bool,int,int,int,int))&Lua_NPC::AddItem) + .def("AddItem", (void(Lua_NPC::*)(int,int,bool,int,int,int,int,int))&Lua_NPC::AddItem) + .def("AddItem", (void(Lua_NPC::*)(int,int,bool,int,int,int,int,int,int))&Lua_NPC::AddItem) + .def("AddLootTable", (void(Lua_NPC::*)(int))&Lua_NPC::AddLootTable) + .def("AddLootTable", (void(Lua_NPC::*)(void))&Lua_NPC::AddLootTable) + .def("AssignWaypoints", (void(Lua_NPC::*)(int))&Lua_NPC::AssignWaypoints) + .def("CalculateNewWaypoint", (void(Lua_NPC::*)(void))&Lua_NPC::CalculateNewWaypoint) + .def("ChangeLastName", (void(Lua_NPC::*)(const char*))&Lua_NPC::ChangeLastName) + .def("CheckNPCFactionAlly", (int(Lua_NPC::*)(int))&Lua_NPC::CheckNPCFactionAlly) + .def("ClearItemList", (void(Lua_NPC::*)(void))&Lua_NPC::ClearItemList) + .def("ClearLastName", (void(Lua_NPC::*)(void))&Lua_NPC::ClearLastName) + .def("CountItem", (uint16(Lua_NPC::*)(uint32))&Lua_NPC::CountItem) + .def("CountLoot", (int(Lua_NPC::*)(void))&Lua_NPC::CountLoot) + .def("DisplayWaypointInfo", (void(Lua_NPC::*)(Lua_Client))&Lua_NPC::DisplayWaypointInfo) + .def("DoClassAttacks", (void(Lua_NPC::*)(Lua_Mob))&Lua_NPC::DoClassAttacks) + .def("GetAccuracyRating", (int(Lua_NPC::*)(void))&Lua_NPC::GetAccuracyRating) + .def("GetAttackDelay", (int(Lua_NPC::*)(void))&Lua_NPC::GetAttackDelay) + .def("GetAttackSpeed", (float(Lua_NPC::*)(void))&Lua_NPC::GetAttackSpeed) + .def("GetAvoidanceRating", &Lua_NPC::GetAvoidanceRating) + .def("GetCopper", (uint32(Lua_NPC::*)(void))&Lua_NPC::GetCopper) + .def("GetFirstSlotByItemID", (uint16(Lua_NPC::*)(uint32))&Lua_NPC::GetFirstSlotByItemID) + .def("GetFollowCanRun", (bool(Lua_NPC::*)(void))&Lua_NPC::GetFollowCanRun) + .def("GetFollowDistance", (int(Lua_NPC::*)(void))&Lua_NPC::GetFollowDistance) + .def("GetFollowID", (int(Lua_NPC::*)(void))&Lua_NPC::GetFollowID) + .def("GetGold", (uint32(Lua_NPC::*)(void))&Lua_NPC::GetGold) + .def("GetGrid", (int(Lua_NPC::*)(void))&Lua_NPC::GetGrid) + .def("GetGuardPointX", (float(Lua_NPC::*)(void))&Lua_NPC::GetGuardPointX) + .def("GetGuardPointY", (float(Lua_NPC::*)(void))&Lua_NPC::GetGuardPointY) + .def("GetGuardPointZ", (float(Lua_NPC::*)(void))&Lua_NPC::GetGuardPointZ) + .def("GetHealScale", (float(Lua_NPC::*)(void))&Lua_NPC::GetHealScale) + .def("GetItemIDBySlot", (uint32(Lua_NPC::*)(uint16))&Lua_NPC::GetItemIDBySlot) + .def("GetLootList", (Lua_NPC_Loot_List(Lua_NPC::*)(lua_State* L))&Lua_NPC::GetLootList) + .def("GetLoottableID", (int(Lua_NPC::*)(void))&Lua_NPC::GetLoottableID) + .def("GetMaxDMG", (uint32(Lua_NPC::*)(void))&Lua_NPC::GetMaxDMG) + .def("GetMaxDamage", (uint32(Lua_NPC::*)(int))&Lua_NPC::GetMaxDamage) + .def("GetMaxWp", (int(Lua_NPC::*)(void))&Lua_NPC::GetMaxWp) + .def("GetMinDMG", (uint32(Lua_NPC::*)(void))&Lua_NPC::GetMinDMG) + .def("GetNPCFactionID", (int(Lua_NPC::*)(void))&Lua_NPC::GetNPCFactionID) + .def("GetNPCHate", (int(Lua_NPC::*)(Lua_Mob))&Lua_NPC::GetNPCHate) + .def("GetNPCSpellsID", (int(Lua_NPC::*)(void))&Lua_NPC::GetNPCSpellsID) + .def("GetNPCSpellsID", (int(Lua_NPC::*)(void))&Lua_NPC::GetNPCSpellsID) + .def("GetPetSpellID", (int(Lua_NPC::*)(void))&Lua_NPC::GetPetSpellID) + .def("GetPlatinum", (uint32(Lua_NPC::*)(void))&Lua_NPC::GetPlatinum) + .def("GetPrimSkill", (int(Lua_NPC::*)(void))&Lua_NPC::GetPrimSkill) + .def("GetPrimaryFaction", (int(Lua_NPC::*)(void))&Lua_NPC::GetPrimaryFaction) + .def("GetRawAC", (int(Lua_NPC::*)(void))&Lua_NPC::GetRawAC) + .def("GetScore", (int(Lua_NPC::*)(void))&Lua_NPC::GetScore) + .def("GetSecSkill", (int(Lua_NPC::*)(void))&Lua_NPC::GetSecSkill) + .def("GetSilver", (uint32(Lua_NPC::*)(void))&Lua_NPC::GetSilver) + .def("GetSlowMitigation", (int(Lua_NPC::*)(void))&Lua_NPC::GetSlowMitigation) + .def("GetSp2", (uint32(Lua_NPC::*)(void))&Lua_NPC::GetSp2) + .def("GetSpawnKillCount", (int(Lua_NPC::*)(void))&Lua_NPC::GetSpawnKillCount) + .def("GetSpawnPointH", (float(Lua_NPC::*)(void))&Lua_NPC::GetSpawnPointH) + .def("GetSpawnPointID", (int(Lua_NPC::*)(void))&Lua_NPC::GetSpawnPointID) + .def("GetSpawnPointX", (float(Lua_NPC::*)(void))&Lua_NPC::GetSpawnPointX) + .def("GetSpawnPointY", (float(Lua_NPC::*)(void))&Lua_NPC::GetSpawnPointY) + .def("GetSpawnPointZ", (float(Lua_NPC::*)(void))&Lua_NPC::GetSpawnPointZ) + .def("GetSpellFocusDMG", (void(Lua_NPC::*)(int))&Lua_NPC::GetSpellFocusDMG) + .def("GetSpellFocusHeal", (void(Lua_NPC::*)(int))&Lua_NPC::GetSpellFocusHeal) + .def("GetSpellScale", (float(Lua_NPC::*)(void))&Lua_NPC::GetSpellScale) + .def("GetSwarmOwner", (int(Lua_NPC::*)(void))&Lua_NPC::GetSwarmOwner) + .def("GetSwarmTarget", (int(Lua_NPC::*)(void))&Lua_NPC::GetSwarmTarget) + .def("GetWaypointMax", (int(Lua_NPC::*)(void))&Lua_NPC::GetWaypointMax) + .def("HasItem", (bool(Lua_NPC::*)(uint32))&Lua_NPC::HasItem) + .def("IsAnimal", (bool(Lua_NPC::*)(void))&Lua_NPC::IsAnimal) + .def("IsGuarding", (bool(Lua_NPC::*)(void))&Lua_NPC::IsGuarding) + .def("IsOnHatelist", (bool(Lua_NPC::*)(Lua_Mob))&Lua_NPC::IsOnHatelist) + .def("IsRaidTarget", (bool(Lua_NPC::*)(void))&Lua_NPC::IsRaidTarget) + .def("IsTaunting", (bool(Lua_NPC::*)(void))&Lua_NPC::IsTaunting) + .def("MerchantCloseShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantCloseShop) + .def("MerchantOpenShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantOpenShop) + .def("ModifyNPCStat", (void(Lua_NPC::*)(const char*,const char*))&Lua_NPC::ModifyNPCStat) + .def("MoveTo", (void(Lua_NPC::*)(float,float,float,float,bool))&Lua_NPC::MoveTo) + .def("NextGuardPosition", (void(Lua_NPC::*)(void))&Lua_NPC::NextGuardPosition) + .def("PauseWandering", (void(Lua_NPC::*)(int))&Lua_NPC::PauseWandering) + .def("PickPocket", (void(Lua_NPC::*)(Lua_Client))&Lua_NPC::PickPocket) + .def("RecalculateSkills", (void(Lua_NPC::*)(void))&Lua_NPC::RecalculateSkills) + .def("RemoveAISpell", (void(Lua_NPC::*)(int))&Lua_NPC::RemoveAISpell) + .def("RemoveCash", (void(Lua_NPC::*)(void))&Lua_NPC::RemoveCash) + .def("RemoveItem", (void(Lua_NPC::*)(int))&Lua_NPC::RemoveItem) + .def("RemoveItem", (void(Lua_NPC::*)(int,int))&Lua_NPC::RemoveItem) + .def("RemoveItem", (void(Lua_NPC::*)(int,int,int))&Lua_NPC::RemoveItem) + .def("ResumeWandering", (void(Lua_NPC::*)(void))&Lua_NPC::ResumeWandering) + .def("SaveGuardSpot", (void(Lua_NPC::*)(float,float,float,float))&Lua_NPC::SaveGuardSpot) + .def("ScaleNPC", (void(Lua_NPC::*)(uint8))&Lua_NPC::ScaleNPC) + .def("SetCopper", (void(Lua_NPC::*)(uint32))&Lua_NPC::SetCopper) + .def("SetFollowCanRun", (void(Lua_NPC::*)(bool))&Lua_NPC::SetFollowCanRun) + .def("SetFollowDistance", (void(Lua_NPC::*)(int))&Lua_NPC::SetFollowDistance) + .def("SetFollowID", (void(Lua_NPC::*)(int))&Lua_NPC::SetFollowID) + .def("SetGold", (void(Lua_NPC::*)(uint32))&Lua_NPC::SetGold) + .def("SetGrid", (void(Lua_NPC::*)(int))&Lua_NPC::SetGrid) + .def("SetNPCFactionID", (void(Lua_NPC::*)(int))&Lua_NPC::SetNPCFactionID) + .def("SetPetSpellID", (void(Lua_NPC::*)(int))&Lua_NPC::SetPetSpellID) + .def("SetPlatinum", (void(Lua_NPC::*)(uint32))&Lua_NPC::SetPlatinum) + .def("SetPrimSkill", (void(Lua_NPC::*)(int))&Lua_NPC::SetPrimSkill) + .def("SetSaveWaypoint", (void(Lua_NPC::*)(int))&Lua_NPC::SetSaveWaypoint) + .def("SetSecSkill", (void(Lua_NPC::*)(int))&Lua_NPC::SetSecSkill) + .def("SetSilver", (void(Lua_NPC::*)(uint32))&Lua_NPC::SetSilver) + .def("SetSimpleRoamBox", (void(Lua_NPC::*)(float))&Lua_NPC::SetSimpleRoamBox) + .def("SetSimpleRoamBox", (void(Lua_NPC::*)(float, float))&Lua_NPC::SetSimpleRoamBox) + .def("SetSimpleRoamBox", (void(Lua_NPC::*)(float, float, int))&Lua_NPC::SetSimpleRoamBox) + .def("SetSp2", (void(Lua_NPC::*)(int))&Lua_NPC::SetSp2) + .def("SetSpellFocusDMG", (void(Lua_NPC::*)(int))&Lua_NPC::SetSpellFocusDMG) + .def("SetSpellFocusHeal", (void(Lua_NPC::*)(int))&Lua_NPC::SetSpellFocusHeal) + .def("SetSwarmTarget", (void(Lua_NPC::*)(int))&Lua_NPC::SetSwarmTarget) + .def("SetTaunting", (void(Lua_NPC::*)(bool))&Lua_NPC::SetTaunting) + .def("SetWaypointPause", (void(Lua_NPC::*)(void))&Lua_NPC::SetWaypointPause) + .def("Signal", (void(Lua_NPC::*)(int))&Lua_NPC::Signal) + .def("StartSwarmTimer", (void(Lua_NPC::*)(uint32))&Lua_NPC::StartSwarmTimer) + .def("StopWandering", (void(Lua_NPC::*)(void))&Lua_NPC::StopWandering) + .def("UpdateWaypoint", (void(Lua_NPC::*)(int))&Lua_NPC::UpdateWaypoint); } luabind::scope lua_register_npc_loot_list() { return luabind::class_("NPCLootList") - .def_readwrite("entries", &Lua_NPC_Loot_List::entries, luabind::return_stl_iterator); + .def_readwrite("entries", &Lua_NPC_Loot_List::entries, luabind::return_stl_iterator); } #endif diff --git a/zone/lua_object.cpp b/zone/lua_object.cpp index eef878c66..09599ecac 100644 --- a/zone/lua_object.cpp +++ b/zone/lua_object.cpp @@ -180,43 +180,43 @@ bool Lua_Object::EntityVariableExists(const char *name) { luabind::scope lua_register_object() { return luabind::class_("Object") - .def(luabind::constructor<>()) - .property("null", &Lua_Object::Null) - .property("valid", &Lua_Object::Valid) - .def("Depop", (void(Lua_Object::*)(void))&Lua_Object::Depop) - .def("Repop", (void(Lua_Object::*)(void))&Lua_Object::Repop) - .def("SetModelName", (void(Lua_Object::*)(const char*))&Lua_Object::SetModelName) - .def("GetModelName", (const char*(Lua_Object::*)(void))&Lua_Object::GetModelName) - .def("GetX", (float(Lua_Object::*)(void))&Lua_Object::GetX) - .def("GetY", (float(Lua_Object::*)(void))&Lua_Object::GetY) - .def("GetZ", (float(Lua_Object::*)(void))&Lua_Object::GetZ) - .def("GetHeading", (float(Lua_Object::*)(void))&Lua_Object::GetHeading) - .def("SetX", (void(Lua_Object::*)(float))&Lua_Object::SetX) - .def("SetY", (void(Lua_Object::*)(float))&Lua_Object::SetY) - .def("SetZ", (void(Lua_Object::*)(float))&Lua_Object::SetZ) - .def("SetHeading", (void(Lua_Object::*)(float))&Lua_Object::SetHeading) - .def("SetLocation", (void(Lua_Object::*)(float,float,float))&Lua_Object::SetLocation) - .def("SetItemID", (void(Lua_Object::*)(uint32))&Lua_Object::SetItemID) - .def("GetItemID", (uint32(Lua_Object::*)(void))&Lua_Object::GetItemID) - .def("SetIcon", (void(Lua_Object::*)(uint32))&Lua_Object::SetIcon) - .def("GetIcon", (uint32(Lua_Object::*)(void))&Lua_Object::GetIcon) - .def("SetType", (void(Lua_Object::*)(uint32))&Lua_Object::SetType) - .def("GetType", (uint32(Lua_Object::*)(void))&Lua_Object::GetType) - .def("GetDBID", (uint32(Lua_Object::*)(void))&Lua_Object::GetDBID) - .def("ClearUser", (void(Lua_Object::*)(void))&Lua_Object::ClearUser) - .def("SetID", (void(Lua_Object::*)(int))&Lua_Object::SetID) - .def("GetID", (int(Lua_Object::*)(void))&Lua_Object::GetID) - .def("Save", (bool(Lua_Object::*)(void))&Lua_Object::Save) - .def("VarSave", (uint32(Lua_Object::*)(void))&Lua_Object::VarSave) - .def("DeleteItem", (void(Lua_Object::*)(int))&Lua_Object::DeleteItem) - .def("StartDecay", (void(Lua_Object::*)(void))&Lua_Object::StartDecay) - .def("Delete", (void(Lua_Object::*)(void))&Lua_Object::Delete) - .def("Delete", (void(Lua_Object::*)(bool))&Lua_Object::Delete) - .def("IsGroundSpawn", (bool(Lua_Object::*)(void))&Lua_Object::IsGroundSpawn) - .def("Close", (void(Lua_Object::*)(void))&Lua_Object::Close) - .def("GetEntityVariable", (const char*(Lua_Object::*)(const char*))&Lua_Object::GetEntityVariable) - .def("SetEntityVariable", (void(Lua_Object::*)(const char*,const char*))&Lua_Object::SetEntityVariable) - .def("EntityVariableExists", (bool(Lua_Object::*)(const char*))&Lua_Object::EntityVariableExists); + .def(luabind::constructor<>()) + .property("null", &Lua_Object::Null) + .property("valid", &Lua_Object::Valid) + .def("ClearUser", (void(Lua_Object::*)(void))&Lua_Object::ClearUser) + .def("Close", (void(Lua_Object::*)(void))&Lua_Object::Close) + .def("Delete", (void(Lua_Object::*)(bool))&Lua_Object::Delete) + .def("Delete", (void(Lua_Object::*)(void))&Lua_Object::Delete) + .def("DeleteItem", (void(Lua_Object::*)(int))&Lua_Object::DeleteItem) + .def("Depop", (void(Lua_Object::*)(void))&Lua_Object::Depop) + .def("EntityVariableExists", (bool(Lua_Object::*)(const char*))&Lua_Object::EntityVariableExists) + .def("GetDBID", (uint32(Lua_Object::*)(void))&Lua_Object::GetDBID) + .def("GetEntityVariable", (const char*(Lua_Object::*)(const char*))&Lua_Object::GetEntityVariable) + .def("GetHeading", (float(Lua_Object::*)(void))&Lua_Object::GetHeading) + .def("GetID", (int(Lua_Object::*)(void))&Lua_Object::GetID) + .def("GetIcon", (uint32(Lua_Object::*)(void))&Lua_Object::GetIcon) + .def("GetItemID", (uint32(Lua_Object::*)(void))&Lua_Object::GetItemID) + .def("GetModelName", (const char*(Lua_Object::*)(void))&Lua_Object::GetModelName) + .def("GetType", (uint32(Lua_Object::*)(void))&Lua_Object::GetType) + .def("GetX", (float(Lua_Object::*)(void))&Lua_Object::GetX) + .def("GetY", (float(Lua_Object::*)(void))&Lua_Object::GetY) + .def("GetZ", (float(Lua_Object::*)(void))&Lua_Object::GetZ) + .def("IsGroundSpawn", (bool(Lua_Object::*)(void))&Lua_Object::IsGroundSpawn) + .def("Repop", (void(Lua_Object::*)(void))&Lua_Object::Repop) + .def("Save", (bool(Lua_Object::*)(void))&Lua_Object::Save) + .def("SetEntityVariable", (void(Lua_Object::*)(const char*,const char*))&Lua_Object::SetEntityVariable) + .def("SetHeading", (void(Lua_Object::*)(float))&Lua_Object::SetHeading) + .def("SetID", (void(Lua_Object::*)(int))&Lua_Object::SetID) + .def("SetIcon", (void(Lua_Object::*)(uint32))&Lua_Object::SetIcon) + .def("SetItemID", (void(Lua_Object::*)(uint32))&Lua_Object::SetItemID) + .def("SetLocation", (void(Lua_Object::*)(float,float,float))&Lua_Object::SetLocation) + .def("SetModelName", (void(Lua_Object::*)(const char*))&Lua_Object::SetModelName) + .def("SetType", (void(Lua_Object::*)(uint32))&Lua_Object::SetType) + .def("SetX", (void(Lua_Object::*)(float))&Lua_Object::SetX) + .def("SetY", (void(Lua_Object::*)(float))&Lua_Object::SetY) + .def("SetZ", (void(Lua_Object::*)(float))&Lua_Object::SetZ) + .def("StartDecay", (void(Lua_Object::*)(void))&Lua_Object::StartDecay) + .def("VarSave", (uint32(Lua_Object::*)(void))&Lua_Object::VarSave); } #endif diff --git a/zone/lua_packet.cpp b/zone/lua_packet.cpp index 15211b097..bf246465b 100644 --- a/zone/lua_packet.cpp +++ b/zone/lua_packet.cpp @@ -323,595 +323,593 @@ std::string Lua_Packet::ReadFixedLengthString(int offset, int string_length) { 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("GetWritePosition", &Lua_Packet::GetWritePosition) - .def("SetWritePosition", &Lua_Packet::SetWritePosition) - .def("WriteInt8", (void(Lua_Packet::*)(int))&Lua_Packet::WriteInt8) - .def("WriteInt8", (void(Lua_Packet::*)(int, int))&Lua_Packet::WriteInt8) - .def("WriteInt16", (void(Lua_Packet::*)(int))&Lua_Packet::WriteInt16) - .def("WriteInt16", (void(Lua_Packet::*)(int, int))&Lua_Packet::WriteInt16) - .def("WriteInt32", (void(Lua_Packet::*)(int))&Lua_Packet::WriteInt32) - .def("WriteInt32", (void(Lua_Packet::*)(int, int))&Lua_Packet::WriteInt32) - .def("WriteInt64", (void(Lua_Packet::*)(int64))&Lua_Packet::WriteInt64) - .def("WriteInt64", (void(Lua_Packet::*)(int, int64))&Lua_Packet::WriteInt64) - .def("WriteFloat", (void(Lua_Packet::*)(float))&Lua_Packet::WriteFloat) - .def("WriteFloat", (void(Lua_Packet::*)(int, float))&Lua_Packet::WriteFloat) - .def("WriteDouble", (void(Lua_Packet::*)(double))&Lua_Packet::WriteDouble) - .def("WriteDouble", (void(Lua_Packet::*)(int, double))&Lua_Packet::WriteDouble) - .def("WriteString", (void(Lua_Packet::*)(std::string))&Lua_Packet::WriteString) - .def("WriteString", (void(Lua_Packet::*)(int, std::string))&Lua_Packet::WriteString) - .def("WriteFixedLengthString", (void(Lua_Packet::*)(std::string))&Lua_Packet::WriteFixedLengthString) - .def("WriteFixedLengthString", (void(Lua_Packet::*)(int, std::string, int))&Lua_Packet::WriteFixedLengthString) - .def("ReadInt8", &Lua_Packet::ReadInt8) - .def("ReadInt16", &Lua_Packet::ReadInt16) - .def("ReadInt32", &Lua_Packet::ReadInt32) - .def("ReadInt64", &Lua_Packet::ReadInt64) - .def("ReadFloat", &Lua_Packet::ReadFloat) - .def("ReadDouble", &Lua_Packet::ReadDouble) - .def("ReadString", &Lua_Packet::ReadString) - .def("ReadFixedLengthString", &Lua_Packet::ReadFixedLengthString); + .def(luabind::constructor<>()) + .def(luabind::constructor()) + .def(luabind::constructor()) + .def("GetOpcode", &Lua_Packet::GetOpcode) + .def("GetRawOpcode", &Lua_Packet::GetRawOpcode) + .def("GetSize", &Lua_Packet::GetSize) + .def("GetWritePosition", &Lua_Packet::GetWritePosition) + .def("ReadDouble", &Lua_Packet::ReadDouble) + .def("ReadFixedLengthString", &Lua_Packet::ReadFixedLengthString) + .def("ReadFloat", &Lua_Packet::ReadFloat) + .def("ReadInt16", &Lua_Packet::ReadInt16) + .def("ReadInt32", &Lua_Packet::ReadInt32) + .def("ReadInt64", &Lua_Packet::ReadInt64) + .def("ReadInt8", &Lua_Packet::ReadInt8) + .def("ReadString", &Lua_Packet::ReadString) + .def("SetOpcode", &Lua_Packet::SetOpcode) + .def("SetRawOpcode", &Lua_Packet::SetRawOpcode) + .def("SetWritePosition", &Lua_Packet::SetWritePosition) + .def("WriteDouble", (void(Lua_Packet::*)(double))&Lua_Packet::WriteDouble) + .def("WriteDouble", (void(Lua_Packet::*)(int, double))&Lua_Packet::WriteDouble) + .def("WriteFixedLengthString", (void(Lua_Packet::*)(int, std::string, int))&Lua_Packet::WriteFixedLengthString) + .def("WriteFixedLengthString", (void(Lua_Packet::*)(std::string))&Lua_Packet::WriteFixedLengthString) + .def("WriteFloat", (void(Lua_Packet::*)(float))&Lua_Packet::WriteFloat) + .def("WriteFloat", (void(Lua_Packet::*)(int, float))&Lua_Packet::WriteFloat) + .def("WriteInt16", (void(Lua_Packet::*)(int))&Lua_Packet::WriteInt16) + .def("WriteInt16", (void(Lua_Packet::*)(int, int))&Lua_Packet::WriteInt16) + .def("WriteInt32", (void(Lua_Packet::*)(int))&Lua_Packet::WriteInt32) + .def("WriteInt32", (void(Lua_Packet::*)(int, int))&Lua_Packet::WriteInt32) + .def("WriteInt64", (void(Lua_Packet::*)(int, int64))&Lua_Packet::WriteInt64) + .def("WriteInt64", (void(Lua_Packet::*)(int64))&Lua_Packet::WriteInt64) + .def("WriteInt8", (void(Lua_Packet::*)(int))&Lua_Packet::WriteInt8) + .def("WriteInt8", (void(Lua_Packet::*)(int, int))&Lua_Packet::WriteInt8) + .def("WriteString", (void(Lua_Packet::*)(int, std::string))&Lua_Packet::WriteString) + .def("WriteString", (void(Lua_Packet::*)(std::string))&Lua_Packet::WriteString); } //TODO: Reorder these to match emu_oplist.h again luabind::scope lua_register_packet_opcodes() { return luabind::class_("Opcode") - .enum_("constants") - [ - luabind::value("ExploreUnknown", static_cast(OP_ExploreUnknown)), - luabind::value("Heartbeat", static_cast(OP_Heartbeat)), - luabind::value("ReloadUI", static_cast(OP_ReloadUI)), - luabind::value("IncreaseStats", static_cast(OP_IncreaseStats)), - luabind::value("ApproveZone", static_cast(OP_ApproveZone)), - luabind::value("Dye", static_cast(OP_Dye)), - luabind::value("Stamina", static_cast(OP_Stamina)), - luabind::value("ControlBoat", static_cast(OP_ControlBoat)), - luabind::value("MobUpdate", static_cast(OP_MobUpdate)), - luabind::value("ClientUpdate", static_cast(OP_ClientUpdate)), - luabind::value("ChannelMessage", static_cast(OP_ChannelMessage)), - luabind::value("SimpleMessage", static_cast(OP_SimpleMessage)), - luabind::value("FormattedMessage", static_cast(OP_FormattedMessage)), - luabind::value("TGB", static_cast(OP_TGB)), - luabind::value("Bind_Wound", static_cast(OP_Bind_Wound)), - luabind::value("Charm", static_cast(OP_Charm)), - luabind::value("Begging", static_cast(OP_Begging)), - luabind::value("MoveCoin", static_cast(OP_MoveCoin)), - luabind::value("SpawnDoor", static_cast(OP_SpawnDoor)), - luabind::value("Sneak", static_cast(OP_Sneak)), - luabind::value("ExpUpdate", static_cast(OP_ExpUpdate)), - luabind::value("DumpName", static_cast(OP_DumpName)), - luabind::value("RespondAA", static_cast(OP_RespondAA)), - luabind::value("UpdateAA", static_cast(OP_UpdateAA)), - luabind::value("SendAAStats", static_cast(OP_SendAAStats)), - luabind::value("SendAATable", static_cast(OP_SendAATable)), - luabind::value("AAAction", static_cast(OP_AAAction)), - luabind::value("BoardBoat", static_cast(OP_BoardBoat)), - luabind::value("LeaveBoat", static_cast(OP_LeaveBoat)), - luabind::value("AdventureInfoRequest", static_cast(OP_AdventureInfoRequest)), - luabind::value("AdventureInfo", static_cast(OP_AdventureInfo)), - luabind::value("AdventureRequest", static_cast(OP_AdventureRequest)), - luabind::value("AdventureDetails", static_cast(OP_AdventureDetails)), - luabind::value("LDoNButton", static_cast(OP_LDoNButton)), - luabind::value("AdventureData", static_cast(OP_AdventureData)), - luabind::value("AdventureFinish", static_cast(OP_AdventureFinish)), - luabind::value("LeaveAdventure", static_cast(OP_LeaveAdventure)), - luabind::value("AdventureUpdate", static_cast(OP_AdventureUpdate)), - luabind::value("SendExpZonein", static_cast(OP_SendExpZonein)), - luabind::value("RaidUpdate", static_cast(OP_RaidUpdate)), - luabind::value("GuildLeader", static_cast(OP_GuildLeader)), - luabind::value("GuildPeace", static_cast(OP_GuildPeace)), - luabind::value("GuildRemove", static_cast(OP_GuildRemove)), - luabind::value("GuildMemberList", static_cast(OP_GuildMemberList)), - luabind::value("GuildMemberUpdate", static_cast(OP_GuildMemberUpdate)), - luabind::value("GuildMemberLevelUpdate", static_cast(OP_GuildMemberLevelUpdate)), - luabind::value("GuildInvite", static_cast(OP_GuildInvite)), - luabind::value("GuildMOTD", static_cast(OP_GuildMOTD)), - luabind::value("SetGuildMOTD", static_cast(OP_SetGuildMOTD)), - luabind::value("GuildPublicNote", static_cast(OP_GuildPublicNote)), - luabind::value("GetGuildsList", static_cast(OP_GetGuildsList)), - luabind::value("GuildDemote", static_cast(OP_GuildDemote)), - luabind::value("GuildInviteAccept", static_cast(OP_GuildInviteAccept)), - luabind::value("GuildWar", static_cast(OP_GuildWar)), - luabind::value("GuildDelete", static_cast(OP_GuildDelete)), - luabind::value("GuildManageRemove", static_cast(OP_GuildManageRemove)), - luabind::value("GuildManageAdd", static_cast(OP_GuildManageAdd)), - luabind::value("GuildManageStatus", static_cast(OP_GuildManageStatus)), - luabind::value("GuildManageBanker", static_cast(OP_GuildManageBanker)), - luabind::value("GetGuildMOTD", static_cast(OP_GetGuildMOTD)), - luabind::value("Trader", static_cast(OP_Trader)), - luabind::value("Bazaar", static_cast(OP_Bazaar)), - luabind::value("BecomeTrader", static_cast(OP_BecomeTrader)), - luabind::value("TraderItemUpdate", static_cast(OP_TraderItemUpdate)), - luabind::value("TraderShop", static_cast(OP_TraderShop)), - luabind::value("TraderBuy", static_cast(OP_TraderBuy)), - luabind::value("PetCommands", static_cast(OP_PetCommands)), - luabind::value("TradeSkillCombine", static_cast(OP_TradeSkillCombine)), - luabind::value("AugmentItem", static_cast(OP_AugmentItem)), - luabind::value("ItemName", static_cast(OP_ItemName)), - luabind::value("ShopItem", static_cast(OP_ShopItem)), - luabind::value("ShopPlayerBuy", static_cast(OP_ShopPlayerBuy)), - luabind::value("ShopPlayerSell", static_cast(OP_ShopPlayerSell)), - luabind::value("ShopDelItem", static_cast(OP_ShopDelItem)), - luabind::value("ShopRequest", static_cast(OP_ShopRequest)), - luabind::value("ShopEnd", static_cast(OP_ShopEnd)), - luabind::value("LFGCommand", static_cast(OP_LFGCommand)), - luabind::value("LFGAppearance", static_cast(OP_LFGAppearance)), - luabind::value("GroupUpdate", static_cast(OP_GroupUpdate)), - luabind::value("GroupInvite", static_cast(OP_GroupInvite)), - luabind::value("GroupDisband", static_cast(OP_GroupDisband)), - luabind::value("GroupInvite2", static_cast(OP_GroupInvite2)), - luabind::value("GroupFollow", static_cast(OP_GroupFollow)), - luabind::value("GroupFollow2", static_cast(OP_GroupFollow2)), - luabind::value("GroupCancelInvite", static_cast(OP_GroupCancelInvite)), - luabind::value("CustomTitles", static_cast(OP_CustomTitles)), - luabind::value("Split", static_cast(OP_Split)), - luabind::value("Jump", static_cast(OP_Jump)), - luabind::value("ConsiderCorpse", static_cast(OP_ConsiderCorpse)), - luabind::value("SkillUpdate", static_cast(OP_SkillUpdate)), - luabind::value("GMEndTrainingResponse", static_cast(OP_GMEndTrainingResponse)), - luabind::value("GMEndTraining", static_cast(OP_GMEndTraining)), - luabind::value("GMTrainSkill", static_cast(OP_GMTrainSkill)), - luabind::value("GMTraining", static_cast(OP_GMTraining)), - luabind::value("DeleteItem", static_cast(OP_DeleteItem)), - luabind::value("CombatAbility", static_cast(OP_CombatAbility)), - luabind::value("TrackUnknown", static_cast(OP_TrackUnknown)), - luabind::value("TrackTarget", static_cast(OP_TrackTarget)), - luabind::value("Track", static_cast(OP_Track)), - luabind::value("ItemLinkClick", static_cast(OP_ItemLinkClick)), - luabind::value("ItemLinkResponse", static_cast(OP_ItemLinkResponse)), - luabind::value("ItemLinkText", static_cast(OP_ItemLinkText)), - luabind::value("RezzAnswer", static_cast(OP_RezzAnswer)), - luabind::value("RezzComplete", static_cast(OP_RezzComplete)), - luabind::value("SendZonepoints", static_cast(OP_SendZonepoints)), - luabind::value("SetRunMode", static_cast(OP_SetRunMode)), - luabind::value("InspectRequest", static_cast(OP_InspectRequest)), - luabind::value("InspectAnswer", static_cast(OP_InspectAnswer)), - luabind::value("SenseTraps", static_cast(OP_SenseTraps)), - luabind::value("DisarmTraps", static_cast(OP_DisarmTraps)), - luabind::value("Assist", static_cast(OP_Assist)), - luabind::value("AssistGroup", static_cast(OP_AssistGroup)), - luabind::value("PickPocket", static_cast(OP_PickPocket)), - luabind::value("LootRequest", static_cast(OP_LootRequest)), - luabind::value("EndLootRequest", static_cast(OP_EndLootRequest)), - luabind::value("MoneyOnCorpse", static_cast(OP_MoneyOnCorpse)), - luabind::value("LootComplete", static_cast(OP_LootComplete)), - luabind::value("LootItem", static_cast(OP_LootItem)), - luabind::value("MoveItem", static_cast(OP_MoveItem)), - luabind::value("WhoAllRequest", static_cast(OP_WhoAllRequest)), - luabind::value("WhoAllResponse", static_cast(OP_WhoAllResponse)), - luabind::value("Consume", static_cast(OP_Consume)), - luabind::value("AutoAttack", static_cast(OP_AutoAttack)), - luabind::value("AutoAttack2", static_cast(OP_AutoAttack2)), - luabind::value("TargetMouse", static_cast(OP_TargetMouse)), - luabind::value("TargetCommand", static_cast(OP_TargetCommand)), - luabind::value("TargetReject", static_cast(OP_TargetReject)), - luabind::value("TargetHoTT", static_cast(OP_TargetHoTT)), - luabind::value("Hide", static_cast(OP_Hide)), - luabind::value("Forage", static_cast(OP_Forage)), - luabind::value("Fishing", static_cast(OP_Fishing)), - luabind::value("Bug", static_cast(OP_Bug)), - luabind::value("Emote", static_cast(OP_Emote)), - luabind::value("Consider", static_cast(OP_Consider)), - luabind::value("FaceChange", static_cast(OP_FaceChange)), - luabind::value("RandomReq", static_cast(OP_RandomReq)), - luabind::value("RandomReply", static_cast(OP_RandomReply)), - luabind::value("Camp", static_cast(OP_Camp)), - luabind::value("YellForHelp", static_cast(OP_YellForHelp)), - luabind::value("SafePoint", static_cast(OP_SafePoint)), - luabind::value("Buff", static_cast(OP_Buff)), - 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)), - luabind::value("Stun", static_cast(OP_Stun)), - luabind::value("BeginCast", static_cast(OP_BeginCast)), - luabind::value("CastSpell", static_cast(OP_CastSpell)), - luabind::value("InterruptCast", static_cast(OP_InterruptCast)), - luabind::value("Death", static_cast(OP_Death)), - luabind::value("FeignDeath", static_cast(OP_FeignDeath)), - luabind::value("Illusion", static_cast(OP_Illusion)), - luabind::value("LevelUpdate", static_cast(OP_LevelUpdate)), - luabind::value("LevelAppearance", static_cast(OP_LevelAppearance)), - luabind::value("MemorizeSpell", static_cast(OP_MemorizeSpell)), - luabind::value("HPUpdate", static_cast(OP_HPUpdate)), - luabind::value("Mend", static_cast(OP_Mend)), - luabind::value("Taunt", static_cast(OP_Taunt)), - luabind::value("GMDelCorpse", static_cast(OP_GMDelCorpse)), - luabind::value("GMFind", static_cast(OP_GMFind)), - luabind::value("GMServers", static_cast(OP_GMServers)), - luabind::value("GMGoto", static_cast(OP_GMGoto)), - luabind::value("GMSummon", static_cast(OP_GMSummon)), - luabind::value("GMKill", static_cast(OP_GMKill)), - luabind::value("GMLastName", static_cast(OP_GMLastName)), - luabind::value("GMToggle", static_cast(OP_GMToggle)), - luabind::value("GMEmoteZone", static_cast(OP_GMEmoteZone)), - luabind::value("GMBecomeNPC", static_cast(OP_GMBecomeNPC)), - luabind::value("GMHideMe", static_cast(OP_GMHideMe)), - luabind::value("GMZoneRequest", static_cast(OP_GMZoneRequest)), - luabind::value("GMZoneRequest2", static_cast(OP_GMZoneRequest2)), - luabind::value("Petition", static_cast(OP_Petition)), - luabind::value("PetitionRefresh", static_cast(OP_PetitionRefresh)), - luabind::value("PDeletePetition", static_cast(OP_PDeletePetition)), - luabind::value("PetitionBug", static_cast(OP_PetitionBug)), - luabind::value("PetitionUpdate", static_cast(OP_PetitionUpdate)), - luabind::value("PetitionCheckout", static_cast(OP_PetitionCheckout)), - luabind::value("PetitionCheckout2", static_cast(OP_PetitionCheckout2)), - luabind::value("PetitionDelete", static_cast(OP_PetitionDelete)), - luabind::value("PetitionResolve", static_cast(OP_PetitionResolve)), - luabind::value("PetitionCheckIn", static_cast(OP_PetitionCheckIn)), - luabind::value("PetitionUnCheckout", static_cast(OP_PetitionUnCheckout)), - luabind::value("PetitionQue", static_cast(OP_PetitionQue)), - luabind::value("SetServerFilter", static_cast(OP_SetServerFilter)), - luabind::value("NewSpawn", static_cast(OP_NewSpawn)), - luabind::value("Animation", static_cast(OP_Animation)), - luabind::value("ZoneChange", static_cast(OP_ZoneChange)), - luabind::value("DeleteSpawn", static_cast(OP_DeleteSpawn)), - luabind::value("EnvDamage", static_cast(OP_EnvDamage)), - luabind::value("Action", static_cast(OP_Action)), - luabind::value("Damage", static_cast(OP_Damage)), - luabind::value("ManaChange", static_cast(OP_ManaChange)), - luabind::value("ClientError", static_cast(OP_ClientError)), - luabind::value("Save", static_cast(OP_Save)), - luabind::value("LocInfo", static_cast(OP_LocInfo)), - luabind::value("Surname", static_cast(OP_Surname)), - luabind::value("ClearSurname", static_cast(OP_ClearSurname)), - luabind::value("SwapSpell", static_cast(OP_SwapSpell)), - luabind::value("DeleteSpell", static_cast(OP_DeleteSpell)), - luabind::value("CloseContainer", static_cast(OP_CloseContainer)), - luabind::value("ClickObjectAction", static_cast(OP_ClickObjectAction)), - luabind::value("GroundSpawn", static_cast(OP_GroundSpawn)), - luabind::value("ClearObject", static_cast(OP_ClearObject)), - luabind::value("ZoneUnavail", static_cast(OP_ZoneUnavail)), - luabind::value("ItemPacket", static_cast(OP_ItemPacket)), - luabind::value("TradeRequest", static_cast(OP_TradeRequest)), - luabind::value("TradeRequestAck", static_cast(OP_TradeRequestAck)), - luabind::value("TradeAcceptClick", static_cast(OP_TradeAcceptClick)), - luabind::value("TradeMoneyUpdate", static_cast(OP_TradeMoneyUpdate)), - luabind::value("TradeCoins", static_cast(OP_TradeCoins)), - luabind::value("CancelTrade", static_cast(OP_CancelTrade)), - luabind::value("FinishTrade", static_cast(OP_FinishTrade)), - luabind::value("SaveOnZoneReq", static_cast(OP_SaveOnZoneReq)), - luabind::value("Logout", static_cast(OP_Logout)), - luabind::value("LogoutReply", static_cast(OP_LogoutReply)), - luabind::value("PreLogoutReply", static_cast(OP_PreLogoutReply)), - luabind::value("DuelResponse2", static_cast(OP_DuelResponse2)), - luabind::value("InstillDoubt", static_cast(OP_InstillDoubt)), - luabind::value("SafeFallSuccess", static_cast(OP_SafeFallSuccess)), - luabind::value("DisciplineUpdate", static_cast(OP_DisciplineUpdate)), - luabind::value("SendGuildTributes", static_cast(OP_SendGuildTributes)), - luabind::value("SendTributes", static_cast(OP_SendTributes)), - luabind::value("TributeUpdate", static_cast(OP_TributeUpdate)), - luabind::value("TributeItem", static_cast(OP_TributeItem)), - luabind::value("TributePointUpdate", static_cast(OP_TributePointUpdate)), - luabind::value("TributeInfo", static_cast(OP_TributeInfo)), - luabind::value("GuildTributeInfo", static_cast(OP_GuildTributeInfo)), - luabind::value("OpenGuildTributeMaster", static_cast(OP_OpenGuildTributeMaster)), - luabind::value("OpenTributeMaster", static_cast(OP_OpenTributeMaster)), - luabind::value("TributeTimer", static_cast(OP_TributeTimer)), - luabind::value("SelectTribute", static_cast(OP_SelectTribute)), - luabind::value("TributeNPC", static_cast(OP_TributeNPC)), - luabind::value("TributeMoney", static_cast(OP_TributeMoney)), - luabind::value("TributeToggle", static_cast(OP_TributeToggle)), - luabind::value("CloseTributeMaster", static_cast(OP_CloseTributeMaster)), - luabind::value("RecipesFavorite", static_cast(OP_RecipesFavorite)), - luabind::value("RecipesSearch", static_cast(OP_RecipesSearch)), - luabind::value("RecipeReply", static_cast(OP_RecipeReply)), - luabind::value("RecipeDetails", static_cast(OP_RecipeDetails)), - luabind::value("RecipeAutoCombine", static_cast(OP_RecipeAutoCombine)), - luabind::value("Shielding", static_cast(OP_Shielding)), - luabind::value("FindPersonRequest", static_cast(OP_FindPersonRequest)), - luabind::value("FindPersonReply", static_cast(OP_FindPersonReply)), - luabind::value("ZoneEntry", static_cast(OP_ZoneEntry)), - luabind::value("PlayerProfile", static_cast(OP_PlayerProfile)), - luabind::value("CharInventory", static_cast(OP_CharInventory)), - luabind::value("ZoneSpawns", static_cast(OP_ZoneSpawns)), - luabind::value("Weather", static_cast(OP_Weather)), - luabind::value("ReqNewZone", static_cast(OP_ReqNewZone)), - luabind::value("NewZone", static_cast(OP_NewZone)), - luabind::value("ReqClientSpawn", static_cast(OP_ReqClientSpawn)), - luabind::value("SpawnAppearance", static_cast(OP_SpawnAppearance)), - luabind::value("ClientReady", static_cast(OP_ClientReady)), - luabind::value("ZoneComplete", static_cast(OP_ZoneComplete)), - luabind::value("ApproveWorld", static_cast(OP_ApproveWorld)), - luabind::value("LogServer", static_cast(OP_LogServer)), - luabind::value("MOTD", static_cast(OP_MOTD)), - luabind::value("SendLoginInfo", static_cast(OP_SendLoginInfo)), - luabind::value("DeleteCharacter", static_cast(OP_DeleteCharacter)), - luabind::value("SendCharInfo", static_cast(OP_SendCharInfo)), - luabind::value("ExpansionInfo", static_cast(OP_ExpansionInfo)), - luabind::value("CharacterCreate", static_cast(OP_CharacterCreate)), - luabind::value("CharacterCreateRequest", static_cast(OP_CharacterCreateRequest)), - luabind::value("RandomNameGenerator", static_cast(OP_RandomNameGenerator)), - luabind::value("GuildsList", static_cast(OP_GuildsList)), - luabind::value("ApproveName", static_cast(OP_ApproveName)), - luabind::value("EnterWorld", static_cast(OP_EnterWorld)), - luabind::value("PostEnterWorld ", static_cast(OP_PostEnterWorld )), - luabind::value("SendSystemStats", static_cast(OP_SendSystemStats)), - luabind::value("World_Client_CRC1", static_cast(OP_World_Client_CRC1)), - luabind::value("World_Client_CRC2", static_cast(OP_World_Client_CRC2)), - luabind::value("SetChatServer", static_cast(OP_SetChatServer)), - luabind::value("SetChatServer2", static_cast(OP_SetChatServer2)), - luabind::value("ZoneServerInfo", static_cast(OP_ZoneServerInfo)), - luabind::value("WorldClientReady", static_cast(OP_WorldClientReady)), - luabind::value("WorldUnknown001", static_cast(OP_WorldUnknown001)), - luabind::value("AckPacket", static_cast(OP_AckPacket)), - luabind::value("WearChange", static_cast(OP_WearChange)), - luabind::value("CrashDump", static_cast(OP_CrashDump)), - luabind::value("LoginComplete", static_cast(OP_LoginComplete)), - luabind::value("GMNameChange", static_cast(OP_GMNameChange)), - luabind::value("ReadBook", static_cast(OP_ReadBook)), - luabind::value("GMKick", static_cast(OP_GMKick)), - luabind::value("RezzRequest", static_cast(OP_RezzRequest)), - luabind::value("MultiLineMsg", static_cast(OP_MultiLineMsg)), - luabind::value("TimeOfDay", static_cast(OP_TimeOfDay)), - luabind::value("CompletedTasks", static_cast(OP_CompletedTasks)), - luabind::value("MoneyUpdate", static_cast(OP_MoneyUpdate)), - luabind::value("ClickObject", static_cast(OP_ClickObject)), - luabind::value("MoveDoor", static_cast(OP_MoveDoor)), - luabind::value("TraderDelItem", static_cast(OP_TraderDelItem)), - luabind::value("AdventureMerchantPurchase", static_cast(OP_AdventureMerchantPurchase)), - luabind::value("TestBuff", static_cast(OP_TestBuff)), - luabind::value("DuelResponse", static_cast(OP_DuelResponse)), - luabind::value("RequestDuel", static_cast(OP_RequestDuel)), - luabind::value("BazaarInspect", static_cast(OP_BazaarInspect)), - luabind::value("ClickDoor", static_cast(OP_ClickDoor)), - luabind::value("GroupAcknowledge", static_cast(OP_GroupAcknowledge)), - luabind::value("GroupDelete", static_cast(OP_GroupDelete)), - luabind::value("AdventureMerchantResponse", static_cast(OP_AdventureMerchantResponse)), - luabind::value("ShopEndConfirm", static_cast(OP_ShopEndConfirm)), - luabind::value("AdventureMerchantRequest", static_cast(OP_AdventureMerchantRequest)), - luabind::value("Sound", static_cast(OP_Sound)), - luabind::value("0x0193", static_cast(OP_0x0193)), - luabind::value("0x0347", static_cast(OP_0x0347)), - luabind::value("WorldComplete", static_cast(OP_WorldComplete)), - luabind::value("MobRename", static_cast(OP_MobRename)), - luabind::value("TaskDescription", static_cast(OP_TaskDescription)), - luabind::value("TaskActivity", static_cast(OP_TaskActivity)), - luabind::value("SharedTaskPlayerList", static_cast(OP_SharedTaskPlayerList)), - luabind::value("AnnoyingZoneUnknown", static_cast(OP_AnnoyingZoneUnknown)), - luabind::value("Some3ByteHPUpdate", static_cast(OP_Some3ByteHPUpdate)), - luabind::value("FloatListThing", static_cast(OP_FloatListThing)), - luabind::value("AAExpUpdate", static_cast(OP_AAExpUpdate)), - luabind::value("ForceFindPerson", static_cast(OP_ForceFindPerson)), - luabind::value("PlayMP3", static_cast(OP_PlayMP3)), - luabind::value("RequestClientZoneChange", static_cast(OP_RequestClientZoneChange)), - luabind::value("SomeItemPacketMaybe", static_cast(OP_SomeItemPacketMaybe)), - luabind::value("QueryResponseThing", static_cast(OP_QueryResponseThing)), - luabind::value("Some6ByteHPUpdate", static_cast(OP_Some6ByteHPUpdate)), - luabind::value("BankerChange", static_cast(OP_BankerChange)), - luabind::value("BecomeCorpse", static_cast(OP_BecomeCorpse)), - luabind::value("Action2", static_cast(OP_Action2)), - luabind::value("BazaarSearch", static_cast(OP_BazaarSearch)), - luabind::value("SetTitle", static_cast(OP_SetTitle)), - luabind::value("SetTitleReply", static_cast(OP_SetTitleReply)), - luabind::value("ConfirmDelete", static_cast(OP_ConfirmDelete)), - luabind::value("ConsentDeny", static_cast(OP_ConsentDeny)), - luabind::value("CrystalCountUpdate", static_cast(OP_CrystalCountUpdate)), - luabind::value("DeletePetition", static_cast(OP_DeletePetition)), - luabind::value("DenyResponse", static_cast(OP_DenyResponse)), - luabind::value("Disarm", static_cast(OP_Disarm)), - luabind::value("Feedback", static_cast(OP_Feedback)), - luabind::value("FriendsWho", static_cast(OP_FriendsWho)), - luabind::value("GMApproval", static_cast(OP_GMApproval)), - luabind::value("GMSearchCorpse", static_cast(OP_GMSearchCorpse)), - luabind::value("GuildBank", static_cast(OP_GuildBank)), - luabind::value("InitialHPUpdate", static_cast(OP_InitialHPUpdate)), - luabind::value("InitialMobHealth", static_cast(OP_InitialMobHealth)), - luabind::value("LFGGetMatchesRequest", static_cast(OP_LFGGetMatchesRequest)), - luabind::value("LFGGetMatchesResponse", static_cast(OP_LFGGetMatchesResponse)), - luabind::value("LFGResponse", static_cast(OP_LFGResponse)), - luabind::value("LFPCommand", static_cast(OP_LFPCommand)), - luabind::value("LFPGetMatchesRequest", static_cast(OP_LFPGetMatchesRequest)), - luabind::value("LFPGetMatchesResponse", static_cast(OP_LFPGetMatchesResponse)), - luabind::value("LeadershipExpToggle", static_cast(OP_LeadershipExpToggle)), - luabind::value("LeadershipExpUpdate", static_cast(OP_LeadershipExpUpdate)), - luabind::value("LoadSpellSet", static_cast(OP_LoadSpellSet)), - luabind::value("LockoutTimerInfo", static_cast(OP_LockoutTimerInfo)), - luabind::value("MendHPUpdate", static_cast(OP_MendHPUpdate)), - luabind::value("MobHealth", static_cast(OP_MobHealth)), - luabind::value("MoveLogDisregard", static_cast(OP_MoveLogDisregard)), - luabind::value("MoveLogRequest", static_cast(OP_MoveLogRequest)), - luabind::value("PetitionSearch", static_cast(OP_PetitionSearch)), - luabind::value("PetitionSearchResults", static_cast(OP_PetitionSearchResults)), - luabind::value("PetitionSearchText", static_cast(OP_PetitionSearchText)), - luabind::value("RaidInvite", static_cast(OP_RaidInvite)), - luabind::value("ReclaimCrystals", static_cast(OP_ReclaimCrystals)), - luabind::value("Report", static_cast(OP_Report)), - luabind::value("SenseHeading", static_cast(OP_SenseHeading)), - luabind::value("LDoNOpen", static_cast(OP_LDoNOpen)), - luabind::value("LDoNSenseTraps", static_cast(OP_LDoNSenseTraps)), - luabind::value("LDoNPickLock", static_cast(OP_LDoNPickLock)), - luabind::value("LDoNDisarmTraps", static_cast(OP_LDoNDisarmTraps)), - luabind::value("LDoNInspect", static_cast(OP_LDoNInspect)), - luabind::value("DynamicWall", static_cast(OP_DynamicWall)), - luabind::value("RequestTitles", static_cast(OP_RequestTitles)), - luabind::value("PurchaseLeadershipAA", static_cast(OP_PurchaseLeadershipAA)), - luabind::value("UpdateLeadershipAA", static_cast(OP_UpdateLeadershipAA)), - luabind::value("AdventurePointsUpdate", static_cast(OP_AdventurePointsUpdate)), - luabind::value("ZoneInUnknown", static_cast(OP_ZoneInUnknown)), - luabind::value("ZoneServerReady ", static_cast(OP_ZoneServerReady )), - luabind::value("ZoneGuildList", static_cast(OP_ZoneGuildList)), - luabind::value("SendTitleList", static_cast(OP_SendTitleList)), - luabind::value("NewTitlesAvailable", static_cast(OP_NewTitlesAvailable)), - luabind::value("Bandolier", static_cast(OP_Bandolier)), - luabind::value("OpenDiscordMerchant", static_cast(OP_OpenDiscordMerchant)), - luabind::value("DiscordMerchantInventory", static_cast(OP_DiscordMerchantInventory)), - luabind::value("GiveMoney", static_cast(OP_GiveMoney)), - luabind::value("OnLevelMessage", static_cast(OP_OnLevelMessage)), - luabind::value("RequestKnowledgeBase", static_cast(OP_RequestKnowledgeBase)), - luabind::value("KnowledgeBase", static_cast(OP_KnowledgeBase)), - luabind::value("VetRewardsAvaliable", static_cast(OP_VetRewardsAvaliable)), - luabind::value("VetClaimRequest", static_cast(OP_VetClaimRequest)), - luabind::value("VetClaimReply", static_cast(OP_VetClaimReply)), - luabind::value("WeaponEquip1", static_cast(OP_WeaponEquip1)), - luabind::value("PlayerStateAdd", static_cast(OP_PlayerStateAdd)), - luabind::value("PlayerStateRemove", static_cast(OP_PlayerStateRemove)), - luabind::value("WorldLogout", static_cast(OP_WorldLogout)), - luabind::value("SessionReady", static_cast(OP_SessionReady)), - luabind::value("Login", static_cast(OP_Login)), - luabind::value("ServerListRequest", static_cast(OP_ServerListRequest)), - luabind::value("PlayEverquestRequest", static_cast(OP_PlayEverquestRequest)), - luabind::value("ChatMessage", static_cast(OP_ChatMessage)), - luabind::value("LoginAccepted", static_cast(OP_LoginAccepted)), - luabind::value("ServerListResponse", static_cast(OP_ServerListResponse)), - luabind::value("Poll", static_cast(OP_Poll)), - luabind::value("PlayEverquestResponse", static_cast(OP_PlayEverquestResponse)), - luabind::value("EnterChat", static_cast(OP_EnterChat)), - luabind::value("PollResponse", static_cast(OP_PollResponse)), - luabind::value("Command", static_cast(OP_Command)), - luabind::value("ZonePlayerToBind", static_cast(OP_ZonePlayerToBind)), - luabind::value("AutoFire", static_cast(OP_AutoFire)), - luabind::value("Rewind", static_cast(OP_Rewind)), - luabind::value("TaskSelectWindow", static_cast(OP_TaskSelectWindow)), - luabind::value("TaskActivityComplete", static_cast(OP_TaskActivityComplete)), - luabind::value("AcceptNewTask", static_cast(OP_AcceptNewTask)), - luabind::value("CancelTask", static_cast(OP_CancelTask)), - luabind::value("TaskHistoryRequest", static_cast(OP_TaskHistoryRequest)), - luabind::value("TaskHistoryReply", static_cast(OP_TaskHistoryReply)), - luabind::value("PetBuffWindow", static_cast(OP_PetBuffWindow)), - luabind::value("RaidJoin", static_cast(OP_RaidJoin)), - luabind::value("Translocate", static_cast(OP_Translocate)), - luabind::value("Sacrifice", static_cast(OP_Sacrifice)), - luabind::value("KeyRing", static_cast(OP_KeyRing)), - luabind::value("PopupResponse", static_cast(OP_PopupResponse)), - luabind::value("DeleteCharge", static_cast(OP_DeleteCharge)), - luabind::value("PotionBelt", static_cast(OP_PotionBelt)), - luabind::value("Barter", static_cast(OP_Barter)), - luabind::value("VoiceMacroIn", static_cast(OP_VoiceMacroIn)), - luabind::value("VoiceMacroOut", static_cast(OP_VoiceMacroOut)), - luabind::value("WorldObjectsSent", static_cast(OP_WorldObjectsSent)), - luabind::value("BlockedBuffs", static_cast(OP_BlockedBuffs)), - luabind::value("RemoveBlockedBuffs", static_cast(OP_RemoveBlockedBuffs)), - luabind::value("ClearBlockedBuffs", static_cast(OP_ClearBlockedBuffs)), - luabind::value("GroupUpdateLeaderAA", static_cast(OP_GroupUpdateLeaderAA)), - luabind::value("MarkNPC", static_cast(OP_MarkNPC)), - luabind::value("ClearNPCMarks", static_cast(OP_ClearNPCMarks)), - luabind::value("DoGroupLeadershipAbility", static_cast(OP_DoGroupLeadershipAbility)), - luabind::value("DelegateAbility", static_cast(OP_DelegateAbility)), - luabind::value("SetGroupTarget", static_cast(OP_SetGroupTarget)), - luabind::value("ApplyPoison", static_cast(OP_ApplyPoison)), - luabind::value("FinishWindow", static_cast(OP_FinishWindow)), - luabind::value("FinishWindow2", static_cast(OP_FinishWindow2)), - luabind::value("ItemVerifyRequest", static_cast(OP_ItemVerifyRequest)), - luabind::value("ItemVerifyReply", static_cast(OP_ItemVerifyReply)), - luabind::value("GMTrainSkillConfirm", static_cast(OP_GMTrainSkillConfirm)), - luabind::value("RestState", static_cast(OP_RestState)), - luabind::value("AugmentInfo", static_cast(OP_AugmentInfo)), - luabind::value("PVPStats", static_cast(OP_PVPStats)), - luabind::value("PVPLeaderBoardRequest", static_cast(OP_PVPLeaderBoardRequest)), - luabind::value("PVPLeaderBoardReply", static_cast(OP_PVPLeaderBoardReply)), - luabind::value("PVPLeaderBoardDetailsRequest", static_cast(OP_PVPLeaderBoardDetailsRequest)), - luabind::value("PVPLeaderBoardDetailsReply", static_cast(OP_PVPLeaderBoardDetailsReply)), - luabind::value("DisciplineTimer", static_cast(OP_DisciplineTimer)), - luabind::value("RespawnWindow", static_cast(OP_RespawnWindow)), - luabind::value("AdventureMerchantSell", static_cast(OP_AdventureMerchantSell)), - luabind::value("AdventureStatsRequest", static_cast(OP_AdventureStatsRequest)), - luabind::value("AdventureStatsReply", static_cast(OP_AdventureStatsReply)), - luabind::value("AdventureLeaderboardRequest", static_cast(OP_AdventureLeaderboardRequest)), - luabind::value("AdventureLeaderboardReply", static_cast(OP_AdventureLeaderboardReply)), - luabind::value("SetStartCity", static_cast(OP_SetStartCity)), - luabind::value("LoginUnknown1", static_cast(OP_LoginUnknown1)), - luabind::value("LoginUnknown2", static_cast(OP_LoginUnknown2)), - luabind::value("ItemViewUnknown", static_cast(OP_ItemViewUnknown)), - luabind::value("GetGuildMOTDReply", static_cast(OP_GetGuildMOTDReply)), - luabind::value("SetGuildRank", static_cast(OP_SetGuildRank)), - luabind::value("SpawnPositionUpdate", static_cast(OP_SpawnPositionUpdate)), - luabind::value("ManaUpdate", static_cast(OP_ManaUpdate)), - luabind::value("EnduranceUpdate", static_cast(OP_EnduranceUpdate)), - luabind::value("MobManaUpdate", static_cast(OP_MobManaUpdate)), - luabind::value("MobEnduranceUpdate", static_cast(OP_MobEnduranceUpdate)), - luabind::value("GroupUpdateB", static_cast(OP_GroupUpdateB)), - luabind::value("GroupDisbandYou", static_cast(OP_GroupDisbandYou)), - luabind::value("GroupDisbandOther", static_cast(OP_GroupDisbandOther)), - luabind::value("GroupLeaderChange", static_cast(OP_GroupLeaderChange)), - luabind::value("GroupLeadershipAAUpdate", static_cast(OP_GroupLeadershipAAUpdate)), - luabind::value("GroupRoles", static_cast(OP_GroupRoles)), - luabind::value("SendFindableNPCs", static_cast(OP_SendFindableNPCs)), - luabind::value("HideCorpse", static_cast(OP_HideCorpse)), - luabind::value("TargetBuffs", static_cast(OP_TargetBuffs)), - luabind::value("TradeBusy", static_cast(OP_TradeBusy)), - luabind::value("GuildUpdateURLAndChannel", static_cast(OP_GuildUpdateURLAndChannel)), - luabind::value("CameraEffect", static_cast(OP_CameraEffect)), - luabind::value("SpellEffect", static_cast(OP_SpellEffect)), - luabind::value("DzQuit", static_cast(OP_DzQuit)), - luabind::value("DzListTimers", static_cast(OP_DzListTimers)), - luabind::value("DzPlayerList", static_cast(OP_DzPlayerList)), - luabind::value("DzAddPlayer", static_cast(OP_DzAddPlayer)), - luabind::value("DzRemovePlayer", static_cast(OP_DzRemovePlayer)), - luabind::value("DzSwapPlayer", static_cast(OP_DzSwapPlayer)), - luabind::value("DzMakeLeader", static_cast(OP_DzMakeLeader)), - luabind::value("DzExpeditionInvite", static_cast(OP_DzExpeditionInvite)), - luabind::value("DzExpeditionInviteResponse", static_cast(OP_DzExpeditionInviteResponse)), - luabind::value("DzExpeditionInfo", static_cast(OP_DzExpeditionInfo)), - luabind::value("DzMemberListName", static_cast(OP_DzMemberListName)), - luabind::value("DzMemberListStatus", static_cast(OP_DzMemberListStatus)), - luabind::value("DzSetLeaderName", static_cast(OP_DzSetLeaderName)), - luabind::value("DzExpeditionEndsWarning", static_cast(OP_DzExpeditionEndsWarning)), - luabind::value("DzExpeditionLockoutTimers", static_cast(OP_DzExpeditionLockoutTimers)), - luabind::value("DzMemberList", static_cast(OP_DzMemberList)), - luabind::value("DzCompass", static_cast(OP_DzCompass)), - luabind::value("DzChooseZone", static_cast(OP_DzChooseZone)), - luabind::value("DzChooseZoneReply", static_cast(OP_DzChooseZoneReply)), - luabind::value("BuffCreate", static_cast(OP_BuffCreate)), - luabind::value("GuildStatus", static_cast(OP_GuildStatus)), - luabind::value("BuffRemoveRequest", static_cast(OP_BuffRemoveRequest)), - luabind::value("CorpseDrag", static_cast(OP_CorpseDrag)), - luabind::value("CorpseDrop", static_cast(OP_CorpseDrop)), - luabind::value("ChangeSize", static_cast(OP_ChangeSize)), - luabind::value("GroupMakeLeader", static_cast(OP_GroupMakeLeader)), - luabind::value("RemoveAllDoors", static_cast(OP_RemoveAllDoors)), - luabind::value("RemoveNimbusEffect", static_cast(OP_RemoveNimbusEffect)), - luabind::value("GuildCreate", static_cast(OP_GuildCreate)), - luabind::value("AltCurrency", static_cast(OP_AltCurrency)), - luabind::value("FellowshipUpdate", static_cast(OP_FellowshipUpdate)), - luabind::value("AltCurrencyMerchantRequest", static_cast(OP_AltCurrencyMerchantRequest)), - luabind::value("AltCurrencyMerchantReply", static_cast(OP_AltCurrencyMerchantReply)), - luabind::value("AltCurrencyPurchase", static_cast(OP_AltCurrencyPurchase)), - luabind::value("AltCurrencySellSelection", static_cast(OP_AltCurrencySellSelection)), - luabind::value("AltCurrencyReclaim", static_cast(OP_AltCurrencyReclaim)), - luabind::value("AltCurrencySell", static_cast(OP_AltCurrencySell)), - luabind::value("Untargetable", static_cast(OP_Untargetable)), - luabind::value("CrystalReclaim", static_cast(OP_CrystalReclaim)), - luabind::value("CrystalCreate", static_cast(OP_CrystalCreate)), - luabind::value("SendMaxCharacters", static_cast(OP_SendMaxCharacters)), - luabind::value("SendMembership", static_cast(OP_SendMembership)), - luabind::value("SendMembershipDetails", static_cast(OP_SendMembershipDetails)), - luabind::value("LFGuild", static_cast(OP_LFGuild)), - luabind::value("XTargetRequest", static_cast(OP_XTargetRequest)), - luabind::value("XTargetResponse", static_cast(OP_XTargetResponse)), - luabind::value("XTargetAutoAddHaters", static_cast(OP_XTargetAutoAddHaters)), - luabind::value("Weblink", static_cast(OP_Weblink)), - luabind::value("InspectMessageUpdate", static_cast(OP_InspectMessageUpdate)), - luabind::value("ItemPreview", static_cast(OP_ItemPreview)), - luabind::value("MercenaryDataRequest", static_cast(OP_MercenaryDataRequest)), - luabind::value("MercenaryDataResponse", static_cast(OP_MercenaryDataResponse)), - luabind::value("MercenaryHire", static_cast(OP_MercenaryHire)), - luabind::value("MercenaryUnknown1", static_cast(OP_MercenaryUnknown1)), - luabind::value("MercenaryTimer", static_cast(OP_MercenaryTimer)), - luabind::value("MercenaryAssign", static_cast(OP_MercenaryAssign)), - luabind::value("MercenaryDataUpdate", static_cast(OP_MercenaryDataUpdate)), - luabind::value("MercenaryCommand", static_cast(OP_MercenaryCommand)), - luabind::value("MercenarySuspendRequest", static_cast(OP_MercenarySuspendRequest)), - luabind::value("MercenarySuspendResponse", static_cast(OP_MercenarySuspendResponse)), - luabind::value("MercenaryUnsuspendResponse", static_cast(OP_MercenaryUnsuspendResponse)), - luabind::value("MercenaryDataUpdateRequest", static_cast(OP_MercenaryDataUpdateRequest)), - 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("Marquee", static_cast(OP_Marquee)), - luabind::value("ClientTimeStamp", static_cast(OP_ClientTimeStamp)), - luabind::value("GuildPromote", static_cast(OP_GuildPromote)), - luabind::value("Fling", static_cast(OP_Fling)) - ]; + .enum_("constants") + [ + luabind::value("ExploreUnknown", static_cast(OP_ExploreUnknown)), + luabind::value("Heartbeat", static_cast(OP_Heartbeat)), + luabind::value("ReloadUI", static_cast(OP_ReloadUI)), + luabind::value("IncreaseStats", static_cast(OP_IncreaseStats)), + luabind::value("ApproveZone", static_cast(OP_ApproveZone)), + luabind::value("Dye", static_cast(OP_Dye)), + luabind::value("Stamina", static_cast(OP_Stamina)), + luabind::value("ControlBoat", static_cast(OP_ControlBoat)), + luabind::value("MobUpdate", static_cast(OP_MobUpdate)), + luabind::value("ClientUpdate", static_cast(OP_ClientUpdate)), + luabind::value("ChannelMessage", static_cast(OP_ChannelMessage)), + luabind::value("SimpleMessage", static_cast(OP_SimpleMessage)), + luabind::value("FormattedMessage", static_cast(OP_FormattedMessage)), + luabind::value("TGB", static_cast(OP_TGB)), + luabind::value("Bind_Wound", static_cast(OP_Bind_Wound)), + luabind::value("Charm", static_cast(OP_Charm)), + luabind::value("Begging", static_cast(OP_Begging)), + luabind::value("MoveCoin", static_cast(OP_MoveCoin)), + luabind::value("SpawnDoor", static_cast(OP_SpawnDoor)), + luabind::value("Sneak", static_cast(OP_Sneak)), + luabind::value("ExpUpdate", static_cast(OP_ExpUpdate)), + luabind::value("DumpName", static_cast(OP_DumpName)), + luabind::value("RespondAA", static_cast(OP_RespondAA)), + luabind::value("UpdateAA", static_cast(OP_UpdateAA)), + luabind::value("SendAAStats", static_cast(OP_SendAAStats)), + luabind::value("SendAATable", static_cast(OP_SendAATable)), + luabind::value("AAAction", static_cast(OP_AAAction)), + luabind::value("BoardBoat", static_cast(OP_BoardBoat)), + luabind::value("LeaveBoat", static_cast(OP_LeaveBoat)), + luabind::value("AdventureInfoRequest", static_cast(OP_AdventureInfoRequest)), + luabind::value("AdventureInfo", static_cast(OP_AdventureInfo)), + luabind::value("AdventureRequest", static_cast(OP_AdventureRequest)), + luabind::value("AdventureDetails", static_cast(OP_AdventureDetails)), + luabind::value("LDoNButton", static_cast(OP_LDoNButton)), + luabind::value("AdventureData", static_cast(OP_AdventureData)), + luabind::value("AdventureFinish", static_cast(OP_AdventureFinish)), + luabind::value("LeaveAdventure", static_cast(OP_LeaveAdventure)), + luabind::value("AdventureUpdate", static_cast(OP_AdventureUpdate)), + luabind::value("SendExpZonein", static_cast(OP_SendExpZonein)), + luabind::value("RaidUpdate", static_cast(OP_RaidUpdate)), + luabind::value("GuildLeader", static_cast(OP_GuildLeader)), + luabind::value("GuildPeace", static_cast(OP_GuildPeace)), + luabind::value("GuildRemove", static_cast(OP_GuildRemove)), + luabind::value("GuildMemberList", static_cast(OP_GuildMemberList)), + luabind::value("GuildMemberUpdate", static_cast(OP_GuildMemberUpdate)), + luabind::value("GuildMemberLevelUpdate", static_cast(OP_GuildMemberLevelUpdate)), + luabind::value("GuildInvite", static_cast(OP_GuildInvite)), + luabind::value("GuildMOTD", static_cast(OP_GuildMOTD)), + luabind::value("SetGuildMOTD", static_cast(OP_SetGuildMOTD)), + luabind::value("GuildPublicNote", static_cast(OP_GuildPublicNote)), + luabind::value("GetGuildsList", static_cast(OP_GetGuildsList)), + luabind::value("GuildDemote", static_cast(OP_GuildDemote)), + luabind::value("GuildInviteAccept", static_cast(OP_GuildInviteAccept)), + luabind::value("GuildWar", static_cast(OP_GuildWar)), + luabind::value("GuildDelete", static_cast(OP_GuildDelete)), + luabind::value("GuildManageRemove", static_cast(OP_GuildManageRemove)), + luabind::value("GuildManageAdd", static_cast(OP_GuildManageAdd)), + luabind::value("GuildManageStatus", static_cast(OP_GuildManageStatus)), + luabind::value("GuildManageBanker", static_cast(OP_GuildManageBanker)), + luabind::value("GetGuildMOTD", static_cast(OP_GetGuildMOTD)), + luabind::value("Trader", static_cast(OP_Trader)), + luabind::value("Bazaar", static_cast(OP_Bazaar)), + luabind::value("BecomeTrader", static_cast(OP_BecomeTrader)), + luabind::value("TraderItemUpdate", static_cast(OP_TraderItemUpdate)), + luabind::value("TraderShop", static_cast(OP_TraderShop)), + luabind::value("TraderBuy", static_cast(OP_TraderBuy)), + luabind::value("PetCommands", static_cast(OP_PetCommands)), + luabind::value("TradeSkillCombine", static_cast(OP_TradeSkillCombine)), + luabind::value("AugmentItem", static_cast(OP_AugmentItem)), + luabind::value("ItemName", static_cast(OP_ItemName)), + luabind::value("ShopItem", static_cast(OP_ShopItem)), + luabind::value("ShopPlayerBuy", static_cast(OP_ShopPlayerBuy)), + luabind::value("ShopPlayerSell", static_cast(OP_ShopPlayerSell)), + luabind::value("ShopDelItem", static_cast(OP_ShopDelItem)), + luabind::value("ShopRequest", static_cast(OP_ShopRequest)), + luabind::value("ShopEnd", static_cast(OP_ShopEnd)), + luabind::value("LFGCommand", static_cast(OP_LFGCommand)), + luabind::value("LFGAppearance", static_cast(OP_LFGAppearance)), + luabind::value("GroupUpdate", static_cast(OP_GroupUpdate)), + luabind::value("GroupInvite", static_cast(OP_GroupInvite)), + luabind::value("GroupDisband", static_cast(OP_GroupDisband)), + luabind::value("GroupInvite2", static_cast(OP_GroupInvite2)), + luabind::value("GroupFollow", static_cast(OP_GroupFollow)), + luabind::value("GroupFollow2", static_cast(OP_GroupFollow2)), + luabind::value("GroupCancelInvite", static_cast(OP_GroupCancelInvite)), + luabind::value("CustomTitles", static_cast(OP_CustomTitles)), + luabind::value("Split", static_cast(OP_Split)), + luabind::value("Jump", static_cast(OP_Jump)), + luabind::value("ConsiderCorpse", static_cast(OP_ConsiderCorpse)), + luabind::value("SkillUpdate", static_cast(OP_SkillUpdate)), + luabind::value("GMEndTrainingResponse", static_cast(OP_GMEndTrainingResponse)), + luabind::value("GMEndTraining", static_cast(OP_GMEndTraining)), + luabind::value("GMTrainSkill", static_cast(OP_GMTrainSkill)), + luabind::value("GMTraining", static_cast(OP_GMTraining)), + luabind::value("DeleteItem", static_cast(OP_DeleteItem)), + luabind::value("CombatAbility", static_cast(OP_CombatAbility)), + luabind::value("TrackUnknown", static_cast(OP_TrackUnknown)), + luabind::value("TrackTarget", static_cast(OP_TrackTarget)), + luabind::value("Track", static_cast(OP_Track)), + luabind::value("ItemLinkClick", static_cast(OP_ItemLinkClick)), + luabind::value("ItemLinkResponse", static_cast(OP_ItemLinkResponse)), + luabind::value("ItemLinkText", static_cast(OP_ItemLinkText)), + luabind::value("RezzAnswer", static_cast(OP_RezzAnswer)), + luabind::value("RezzComplete", static_cast(OP_RezzComplete)), + luabind::value("SendZonepoints", static_cast(OP_SendZonepoints)), + luabind::value("SetRunMode", static_cast(OP_SetRunMode)), + luabind::value("InspectRequest", static_cast(OP_InspectRequest)), + luabind::value("InspectAnswer", static_cast(OP_InspectAnswer)), + luabind::value("SenseTraps", static_cast(OP_SenseTraps)), + luabind::value("DisarmTraps", static_cast(OP_DisarmTraps)), + luabind::value("Assist", static_cast(OP_Assist)), + luabind::value("AssistGroup", static_cast(OP_AssistGroup)), + luabind::value("PickPocket", static_cast(OP_PickPocket)), + luabind::value("LootRequest", static_cast(OP_LootRequest)), + luabind::value("EndLootRequest", static_cast(OP_EndLootRequest)), + luabind::value("MoneyOnCorpse", static_cast(OP_MoneyOnCorpse)), + luabind::value("LootComplete", static_cast(OP_LootComplete)), + luabind::value("LootItem", static_cast(OP_LootItem)), + luabind::value("MoveItem", static_cast(OP_MoveItem)), + luabind::value("WhoAllRequest", static_cast(OP_WhoAllRequest)), + luabind::value("WhoAllResponse", static_cast(OP_WhoAllResponse)), + luabind::value("Consume", static_cast(OP_Consume)), + luabind::value("AutoAttack", static_cast(OP_AutoAttack)), + luabind::value("AutoAttack2", static_cast(OP_AutoAttack2)), + luabind::value("TargetMouse", static_cast(OP_TargetMouse)), + luabind::value("TargetCommand", static_cast(OP_TargetCommand)), + luabind::value("TargetReject", static_cast(OP_TargetReject)), + luabind::value("TargetHoTT", static_cast(OP_TargetHoTT)), + luabind::value("Hide", static_cast(OP_Hide)), + luabind::value("Forage", static_cast(OP_Forage)), + luabind::value("Fishing", static_cast(OP_Fishing)), + luabind::value("Bug", static_cast(OP_Bug)), + luabind::value("Emote", static_cast(OP_Emote)), + luabind::value("Consider", static_cast(OP_Consider)), + luabind::value("FaceChange", static_cast(OP_FaceChange)), + luabind::value("RandomReq", static_cast(OP_RandomReq)), + luabind::value("RandomReply", static_cast(OP_RandomReply)), + luabind::value("Camp", static_cast(OP_Camp)), + luabind::value("YellForHelp", static_cast(OP_YellForHelp)), + luabind::value("SafePoint", static_cast(OP_SafePoint)), + luabind::value("Buff", static_cast(OP_Buff)), + 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)), + luabind::value("Stun", static_cast(OP_Stun)), + luabind::value("BeginCast", static_cast(OP_BeginCast)), + luabind::value("CastSpell", static_cast(OP_CastSpell)), + luabind::value("InterruptCast", static_cast(OP_InterruptCast)), + luabind::value("Death", static_cast(OP_Death)), + luabind::value("FeignDeath", static_cast(OP_FeignDeath)), + luabind::value("Illusion", static_cast(OP_Illusion)), + luabind::value("LevelUpdate", static_cast(OP_LevelUpdate)), + luabind::value("LevelAppearance", static_cast(OP_LevelAppearance)), + luabind::value("MemorizeSpell", static_cast(OP_MemorizeSpell)), + luabind::value("HPUpdate", static_cast(OP_HPUpdate)), + luabind::value("Mend", static_cast(OP_Mend)), + luabind::value("Taunt", static_cast(OP_Taunt)), + luabind::value("GMDelCorpse", static_cast(OP_GMDelCorpse)), + luabind::value("GMFind", static_cast(OP_GMFind)), + luabind::value("GMServers", static_cast(OP_GMServers)), + luabind::value("GMGoto", static_cast(OP_GMGoto)), + luabind::value("GMSummon", static_cast(OP_GMSummon)), + luabind::value("GMKill", static_cast(OP_GMKill)), + luabind::value("GMLastName", static_cast(OP_GMLastName)), + luabind::value("GMToggle", static_cast(OP_GMToggle)), + luabind::value("GMEmoteZone", static_cast(OP_GMEmoteZone)), + luabind::value("GMBecomeNPC", static_cast(OP_GMBecomeNPC)), + luabind::value("GMHideMe", static_cast(OP_GMHideMe)), + luabind::value("GMZoneRequest", static_cast(OP_GMZoneRequest)), + luabind::value("GMZoneRequest2", static_cast(OP_GMZoneRequest2)), + luabind::value("Petition", static_cast(OP_Petition)), + luabind::value("PetitionRefresh", static_cast(OP_PetitionRefresh)), + luabind::value("PDeletePetition", static_cast(OP_PDeletePetition)), + luabind::value("PetitionBug", static_cast(OP_PetitionBug)), + luabind::value("PetitionUpdate", static_cast(OP_PetitionUpdate)), + luabind::value("PetitionCheckout", static_cast(OP_PetitionCheckout)), + luabind::value("PetitionCheckout2", static_cast(OP_PetitionCheckout2)), + luabind::value("PetitionDelete", static_cast(OP_PetitionDelete)), + luabind::value("PetitionResolve", static_cast(OP_PetitionResolve)), + luabind::value("PetitionCheckIn", static_cast(OP_PetitionCheckIn)), + luabind::value("PetitionUnCheckout", static_cast(OP_PetitionUnCheckout)), + luabind::value("PetitionQue", static_cast(OP_PetitionQue)), + luabind::value("SetServerFilter", static_cast(OP_SetServerFilter)), + luabind::value("NewSpawn", static_cast(OP_NewSpawn)), + luabind::value("Animation", static_cast(OP_Animation)), + luabind::value("ZoneChange", static_cast(OP_ZoneChange)), + luabind::value("DeleteSpawn", static_cast(OP_DeleteSpawn)), + luabind::value("EnvDamage", static_cast(OP_EnvDamage)), + luabind::value("Action", static_cast(OP_Action)), + luabind::value("Damage", static_cast(OP_Damage)), + luabind::value("ManaChange", static_cast(OP_ManaChange)), + luabind::value("ClientError", static_cast(OP_ClientError)), + luabind::value("Save", static_cast(OP_Save)), + luabind::value("LocInfo", static_cast(OP_LocInfo)), + luabind::value("Surname", static_cast(OP_Surname)), + luabind::value("ClearSurname", static_cast(OP_ClearSurname)), + luabind::value("SwapSpell", static_cast(OP_SwapSpell)), + luabind::value("DeleteSpell", static_cast(OP_DeleteSpell)), + luabind::value("CloseContainer", static_cast(OP_CloseContainer)), + luabind::value("ClickObjectAction", static_cast(OP_ClickObjectAction)), + luabind::value("GroundSpawn", static_cast(OP_GroundSpawn)), + luabind::value("ClearObject", static_cast(OP_ClearObject)), + luabind::value("ZoneUnavail", static_cast(OP_ZoneUnavail)), + luabind::value("ItemPacket", static_cast(OP_ItemPacket)), + luabind::value("TradeRequest", static_cast(OP_TradeRequest)), + luabind::value("TradeRequestAck", static_cast(OP_TradeRequestAck)), + luabind::value("TradeAcceptClick", static_cast(OP_TradeAcceptClick)), + luabind::value("TradeMoneyUpdate", static_cast(OP_TradeMoneyUpdate)), + luabind::value("TradeCoins", static_cast(OP_TradeCoins)), + luabind::value("CancelTrade", static_cast(OP_CancelTrade)), + luabind::value("FinishTrade", static_cast(OP_FinishTrade)), + luabind::value("SaveOnZoneReq", static_cast(OP_SaveOnZoneReq)), + luabind::value("Logout", static_cast(OP_Logout)), + luabind::value("LogoutReply", static_cast(OP_LogoutReply)), + luabind::value("PreLogoutReply", static_cast(OP_PreLogoutReply)), + luabind::value("DuelResponse2", static_cast(OP_DuelResponse2)), + luabind::value("InstillDoubt", static_cast(OP_InstillDoubt)), + luabind::value("SafeFallSuccess", static_cast(OP_SafeFallSuccess)), + luabind::value("DisciplineUpdate", static_cast(OP_DisciplineUpdate)), + luabind::value("SendGuildTributes", static_cast(OP_SendGuildTributes)), + luabind::value("SendTributes", static_cast(OP_SendTributes)), + luabind::value("TributeUpdate", static_cast(OP_TributeUpdate)), + luabind::value("TributeItem", static_cast(OP_TributeItem)), + luabind::value("TributePointUpdate", static_cast(OP_TributePointUpdate)), + luabind::value("TributeInfo", static_cast(OP_TributeInfo)), + luabind::value("GuildTributeInfo", static_cast(OP_GuildTributeInfo)), + luabind::value("OpenGuildTributeMaster", static_cast(OP_OpenGuildTributeMaster)), + luabind::value("OpenTributeMaster", static_cast(OP_OpenTributeMaster)), + luabind::value("TributeTimer", static_cast(OP_TributeTimer)), + luabind::value("SelectTribute", static_cast(OP_SelectTribute)), + luabind::value("TributeNPC", static_cast(OP_TributeNPC)), + luabind::value("TributeMoney", static_cast(OP_TributeMoney)), + luabind::value("TributeToggle", static_cast(OP_TributeToggle)), + luabind::value("CloseTributeMaster", static_cast(OP_CloseTributeMaster)), + luabind::value("RecipesFavorite", static_cast(OP_RecipesFavorite)), + luabind::value("RecipesSearch", static_cast(OP_RecipesSearch)), + luabind::value("RecipeReply", static_cast(OP_RecipeReply)), + luabind::value("RecipeDetails", static_cast(OP_RecipeDetails)), + luabind::value("RecipeAutoCombine", static_cast(OP_RecipeAutoCombine)), + luabind::value("Shielding", static_cast(OP_Shielding)), + luabind::value("FindPersonRequest", static_cast(OP_FindPersonRequest)), + luabind::value("FindPersonReply", static_cast(OP_FindPersonReply)), + luabind::value("ZoneEntry", static_cast(OP_ZoneEntry)), + luabind::value("PlayerProfile", static_cast(OP_PlayerProfile)), + luabind::value("CharInventory", static_cast(OP_CharInventory)), + luabind::value("ZoneSpawns", static_cast(OP_ZoneSpawns)), + luabind::value("Weather", static_cast(OP_Weather)), + luabind::value("ReqNewZone", static_cast(OP_ReqNewZone)), + luabind::value("NewZone", static_cast(OP_NewZone)), + luabind::value("ReqClientSpawn", static_cast(OP_ReqClientSpawn)), + luabind::value("SpawnAppearance", static_cast(OP_SpawnAppearance)), + luabind::value("ClientReady", static_cast(OP_ClientReady)), + luabind::value("ZoneComplete", static_cast(OP_ZoneComplete)), + luabind::value("ApproveWorld", static_cast(OP_ApproveWorld)), + luabind::value("LogServer", static_cast(OP_LogServer)), + luabind::value("MOTD", static_cast(OP_MOTD)), + luabind::value("SendLoginInfo", static_cast(OP_SendLoginInfo)), + luabind::value("DeleteCharacter", static_cast(OP_DeleteCharacter)), + luabind::value("SendCharInfo", static_cast(OP_SendCharInfo)), + luabind::value("ExpansionInfo", static_cast(OP_ExpansionInfo)), + luabind::value("CharacterCreate", static_cast(OP_CharacterCreate)), + luabind::value("CharacterCreateRequest", static_cast(OP_CharacterCreateRequest)), + luabind::value("RandomNameGenerator", static_cast(OP_RandomNameGenerator)), + luabind::value("GuildsList", static_cast(OP_GuildsList)), + luabind::value("ApproveName", static_cast(OP_ApproveName)), + luabind::value("EnterWorld", static_cast(OP_EnterWorld)), + luabind::value("PostEnterWorld ", static_cast(OP_PostEnterWorld )), + luabind::value("SendSystemStats", static_cast(OP_SendSystemStats)), + luabind::value("World_Client_CRC1", static_cast(OP_World_Client_CRC1)), + luabind::value("World_Client_CRC2", static_cast(OP_World_Client_CRC2)), + luabind::value("SetChatServer", static_cast(OP_SetChatServer)), + luabind::value("SetChatServer2", static_cast(OP_SetChatServer2)), + luabind::value("ZoneServerInfo", static_cast(OP_ZoneServerInfo)), + luabind::value("WorldClientReady", static_cast(OP_WorldClientReady)), + luabind::value("WorldUnknown001", static_cast(OP_WorldUnknown001)), + luabind::value("AckPacket", static_cast(OP_AckPacket)), + luabind::value("WearChange", static_cast(OP_WearChange)), + luabind::value("CrashDump", static_cast(OP_CrashDump)), + luabind::value("LoginComplete", static_cast(OP_LoginComplete)), + luabind::value("GMNameChange", static_cast(OP_GMNameChange)), + luabind::value("ReadBook", static_cast(OP_ReadBook)), + luabind::value("GMKick", static_cast(OP_GMKick)), + luabind::value("RezzRequest", static_cast(OP_RezzRequest)), + luabind::value("MultiLineMsg", static_cast(OP_MultiLineMsg)), + luabind::value("TimeOfDay", static_cast(OP_TimeOfDay)), + luabind::value("CompletedTasks", static_cast(OP_CompletedTasks)), + luabind::value("MoneyUpdate", static_cast(OP_MoneyUpdate)), + luabind::value("ClickObject", static_cast(OP_ClickObject)), + luabind::value("MoveDoor", static_cast(OP_MoveDoor)), + luabind::value("TraderDelItem", static_cast(OP_TraderDelItem)), + luabind::value("AdventureMerchantPurchase", static_cast(OP_AdventureMerchantPurchase)), + luabind::value("TestBuff", static_cast(OP_TestBuff)), + luabind::value("DuelResponse", static_cast(OP_DuelResponse)), + luabind::value("RequestDuel", static_cast(OP_RequestDuel)), + luabind::value("BazaarInspect", static_cast(OP_BazaarInspect)), + luabind::value("ClickDoor", static_cast(OP_ClickDoor)), + luabind::value("GroupAcknowledge", static_cast(OP_GroupAcknowledge)), + luabind::value("GroupDelete", static_cast(OP_GroupDelete)), + luabind::value("AdventureMerchantResponse", static_cast(OP_AdventureMerchantResponse)), + luabind::value("ShopEndConfirm", static_cast(OP_ShopEndConfirm)), + luabind::value("AdventureMerchantRequest", static_cast(OP_AdventureMerchantRequest)), + luabind::value("Sound", static_cast(OP_Sound)), + luabind::value("0x0193", static_cast(OP_0x0193)), + luabind::value("0x0347", static_cast(OP_0x0347)), + luabind::value("WorldComplete", static_cast(OP_WorldComplete)), + luabind::value("MobRename", static_cast(OP_MobRename)), + luabind::value("TaskDescription", static_cast(OP_TaskDescription)), + luabind::value("TaskActivity", static_cast(OP_TaskActivity)), + luabind::value("SharedTaskPlayerList", static_cast(OP_SharedTaskPlayerList)), + luabind::value("AnnoyingZoneUnknown", static_cast(OP_AnnoyingZoneUnknown)), + luabind::value("Some3ByteHPUpdate", static_cast(OP_Some3ByteHPUpdate)), + luabind::value("FloatListThing", static_cast(OP_FloatListThing)), + luabind::value("AAExpUpdate", static_cast(OP_AAExpUpdate)), + luabind::value("ForceFindPerson", static_cast(OP_ForceFindPerson)), + luabind::value("PlayMP3", static_cast(OP_PlayMP3)), + luabind::value("RequestClientZoneChange", static_cast(OP_RequestClientZoneChange)), + luabind::value("SomeItemPacketMaybe", static_cast(OP_SomeItemPacketMaybe)), + luabind::value("QueryResponseThing", static_cast(OP_QueryResponseThing)), + luabind::value("Some6ByteHPUpdate", static_cast(OP_Some6ByteHPUpdate)), + luabind::value("BankerChange", static_cast(OP_BankerChange)), + luabind::value("BecomeCorpse", static_cast(OP_BecomeCorpse)), + luabind::value("Action2", static_cast(OP_Action2)), + luabind::value("BazaarSearch", static_cast(OP_BazaarSearch)), + luabind::value("SetTitle", static_cast(OP_SetTitle)), + luabind::value("SetTitleReply", static_cast(OP_SetTitleReply)), + luabind::value("ConfirmDelete", static_cast(OP_ConfirmDelete)), + luabind::value("ConsentDeny", static_cast(OP_ConsentDeny)), + luabind::value("CrystalCountUpdate", static_cast(OP_CrystalCountUpdate)), + luabind::value("DeletePetition", static_cast(OP_DeletePetition)), + luabind::value("DenyResponse", static_cast(OP_DenyResponse)), + luabind::value("Disarm", static_cast(OP_Disarm)), + luabind::value("Feedback", static_cast(OP_Feedback)), + luabind::value("FriendsWho", static_cast(OP_FriendsWho)), + luabind::value("GMApproval", static_cast(OP_GMApproval)), + luabind::value("GMSearchCorpse", static_cast(OP_GMSearchCorpse)), + luabind::value("GuildBank", static_cast(OP_GuildBank)), + luabind::value("InitialHPUpdate", static_cast(OP_InitialHPUpdate)), + luabind::value("InitialMobHealth", static_cast(OP_InitialMobHealth)), + luabind::value("LFGGetMatchesRequest", static_cast(OP_LFGGetMatchesRequest)), + luabind::value("LFGGetMatchesResponse", static_cast(OP_LFGGetMatchesResponse)), + luabind::value("LFGResponse", static_cast(OP_LFGResponse)), + luabind::value("LFPCommand", static_cast(OP_LFPCommand)), + luabind::value("LFPGetMatchesRequest", static_cast(OP_LFPGetMatchesRequest)), + luabind::value("LFPGetMatchesResponse", static_cast(OP_LFPGetMatchesResponse)), + luabind::value("LeadershipExpToggle", static_cast(OP_LeadershipExpToggle)), + luabind::value("LeadershipExpUpdate", static_cast(OP_LeadershipExpUpdate)), + luabind::value("LoadSpellSet", static_cast(OP_LoadSpellSet)), + luabind::value("LockoutTimerInfo", static_cast(OP_LockoutTimerInfo)), + luabind::value("MendHPUpdate", static_cast(OP_MendHPUpdate)), + luabind::value("MobHealth", static_cast(OP_MobHealth)), + luabind::value("MoveLogDisregard", static_cast(OP_MoveLogDisregard)), + luabind::value("MoveLogRequest", static_cast(OP_MoveLogRequest)), + luabind::value("PetitionSearch", static_cast(OP_PetitionSearch)), + luabind::value("PetitionSearchResults", static_cast(OP_PetitionSearchResults)), + luabind::value("PetitionSearchText", static_cast(OP_PetitionSearchText)), + luabind::value("RaidInvite", static_cast(OP_RaidInvite)), + luabind::value("ReclaimCrystals", static_cast(OP_ReclaimCrystals)), + luabind::value("Report", static_cast(OP_Report)), + luabind::value("SenseHeading", static_cast(OP_SenseHeading)), + luabind::value("LDoNOpen", static_cast(OP_LDoNOpen)), + luabind::value("LDoNSenseTraps", static_cast(OP_LDoNSenseTraps)), + luabind::value("LDoNPickLock", static_cast(OP_LDoNPickLock)), + luabind::value("LDoNDisarmTraps", static_cast(OP_LDoNDisarmTraps)), + luabind::value("LDoNInspect", static_cast(OP_LDoNInspect)), + luabind::value("DynamicWall", static_cast(OP_DynamicWall)), + luabind::value("RequestTitles", static_cast(OP_RequestTitles)), + luabind::value("PurchaseLeadershipAA", static_cast(OP_PurchaseLeadershipAA)), + luabind::value("UpdateLeadershipAA", static_cast(OP_UpdateLeadershipAA)), + luabind::value("AdventurePointsUpdate", static_cast(OP_AdventurePointsUpdate)), + luabind::value("ZoneInUnknown", static_cast(OP_ZoneInUnknown)), + luabind::value("ZoneServerReady ", static_cast(OP_ZoneServerReady )), + luabind::value("ZoneGuildList", static_cast(OP_ZoneGuildList)), + luabind::value("SendTitleList", static_cast(OP_SendTitleList)), + luabind::value("NewTitlesAvailable", static_cast(OP_NewTitlesAvailable)), + luabind::value("Bandolier", static_cast(OP_Bandolier)), + luabind::value("OpenDiscordMerchant", static_cast(OP_OpenDiscordMerchant)), + luabind::value("DiscordMerchantInventory", static_cast(OP_DiscordMerchantInventory)), + luabind::value("GiveMoney", static_cast(OP_GiveMoney)), + luabind::value("OnLevelMessage", static_cast(OP_OnLevelMessage)), + luabind::value("RequestKnowledgeBase", static_cast(OP_RequestKnowledgeBase)), + luabind::value("KnowledgeBase", static_cast(OP_KnowledgeBase)), + luabind::value("VetRewardsAvaliable", static_cast(OP_VetRewardsAvaliable)), + luabind::value("VetClaimRequest", static_cast(OP_VetClaimRequest)), + luabind::value("VetClaimReply", static_cast(OP_VetClaimReply)), + luabind::value("WeaponEquip1", static_cast(OP_WeaponEquip1)), + luabind::value("PlayerStateAdd", static_cast(OP_PlayerStateAdd)), + luabind::value("PlayerStateRemove", static_cast(OP_PlayerStateRemove)), + luabind::value("WorldLogout", static_cast(OP_WorldLogout)), + luabind::value("SessionReady", static_cast(OP_SessionReady)), + luabind::value("Login", static_cast(OP_Login)), + luabind::value("ServerListRequest", static_cast(OP_ServerListRequest)), + luabind::value("PlayEverquestRequest", static_cast(OP_PlayEverquestRequest)), + luabind::value("ChatMessage", static_cast(OP_ChatMessage)), + luabind::value("LoginAccepted", static_cast(OP_LoginAccepted)), + luabind::value("ServerListResponse", static_cast(OP_ServerListResponse)), + luabind::value("Poll", static_cast(OP_Poll)), + luabind::value("PlayEverquestResponse", static_cast(OP_PlayEverquestResponse)), + luabind::value("EnterChat", static_cast(OP_EnterChat)), + luabind::value("PollResponse", static_cast(OP_PollResponse)), + luabind::value("Command", static_cast(OP_Command)), + luabind::value("ZonePlayerToBind", static_cast(OP_ZonePlayerToBind)), + luabind::value("AutoFire", static_cast(OP_AutoFire)), + luabind::value("Rewind", static_cast(OP_Rewind)), + luabind::value("TaskSelectWindow", static_cast(OP_TaskSelectWindow)), + luabind::value("TaskActivityComplete", static_cast(OP_TaskActivityComplete)), + luabind::value("AcceptNewTask", static_cast(OP_AcceptNewTask)), + luabind::value("CancelTask", static_cast(OP_CancelTask)), + luabind::value("TaskHistoryRequest", static_cast(OP_TaskHistoryRequest)), + luabind::value("TaskHistoryReply", static_cast(OP_TaskHistoryReply)), + luabind::value("PetBuffWindow", static_cast(OP_PetBuffWindow)), + luabind::value("RaidJoin", static_cast(OP_RaidJoin)), + luabind::value("Translocate", static_cast(OP_Translocate)), + luabind::value("Sacrifice", static_cast(OP_Sacrifice)), + luabind::value("KeyRing", static_cast(OP_KeyRing)), + luabind::value("PopupResponse", static_cast(OP_PopupResponse)), + luabind::value("DeleteCharge", static_cast(OP_DeleteCharge)), + luabind::value("PotionBelt", static_cast(OP_PotionBelt)), + luabind::value("Barter", static_cast(OP_Barter)), + luabind::value("VoiceMacroIn", static_cast(OP_VoiceMacroIn)), + luabind::value("VoiceMacroOut", static_cast(OP_VoiceMacroOut)), + luabind::value("WorldObjectsSent", static_cast(OP_WorldObjectsSent)), + luabind::value("BlockedBuffs", static_cast(OP_BlockedBuffs)), + luabind::value("RemoveBlockedBuffs", static_cast(OP_RemoveBlockedBuffs)), + luabind::value("ClearBlockedBuffs", static_cast(OP_ClearBlockedBuffs)), + luabind::value("GroupUpdateLeaderAA", static_cast(OP_GroupUpdateLeaderAA)), + luabind::value("MarkNPC", static_cast(OP_MarkNPC)), + luabind::value("ClearNPCMarks", static_cast(OP_ClearNPCMarks)), + luabind::value("DoGroupLeadershipAbility", static_cast(OP_DoGroupLeadershipAbility)), + luabind::value("DelegateAbility", static_cast(OP_DelegateAbility)), + luabind::value("SetGroupTarget", static_cast(OP_SetGroupTarget)), + luabind::value("ApplyPoison", static_cast(OP_ApplyPoison)), + luabind::value("FinishWindow", static_cast(OP_FinishWindow)), + luabind::value("FinishWindow2", static_cast(OP_FinishWindow2)), + luabind::value("ItemVerifyRequest", static_cast(OP_ItemVerifyRequest)), + luabind::value("ItemVerifyReply", static_cast(OP_ItemVerifyReply)), + luabind::value("GMTrainSkillConfirm", static_cast(OP_GMTrainSkillConfirm)), + luabind::value("RestState", static_cast(OP_RestState)), + luabind::value("AugmentInfo", static_cast(OP_AugmentInfo)), + luabind::value("PVPStats", static_cast(OP_PVPStats)), + luabind::value("PVPLeaderBoardRequest", static_cast(OP_PVPLeaderBoardRequest)), + luabind::value("PVPLeaderBoardReply", static_cast(OP_PVPLeaderBoardReply)), + luabind::value("PVPLeaderBoardDetailsRequest", static_cast(OP_PVPLeaderBoardDetailsRequest)), + luabind::value("PVPLeaderBoardDetailsReply", static_cast(OP_PVPLeaderBoardDetailsReply)), + luabind::value("DisciplineTimer", static_cast(OP_DisciplineTimer)), + luabind::value("RespawnWindow", static_cast(OP_RespawnWindow)), + luabind::value("AdventureMerchantSell", static_cast(OP_AdventureMerchantSell)), + luabind::value("AdventureStatsRequest", static_cast(OP_AdventureStatsRequest)), + luabind::value("AdventureStatsReply", static_cast(OP_AdventureStatsReply)), + luabind::value("AdventureLeaderboardRequest", static_cast(OP_AdventureLeaderboardRequest)), + luabind::value("AdventureLeaderboardReply", static_cast(OP_AdventureLeaderboardReply)), + luabind::value("SetStartCity", static_cast(OP_SetStartCity)), + luabind::value("LoginUnknown1", static_cast(OP_LoginUnknown1)), + luabind::value("LoginUnknown2", static_cast(OP_LoginUnknown2)), + luabind::value("ItemViewUnknown", static_cast(OP_ItemViewUnknown)), + luabind::value("GetGuildMOTDReply", static_cast(OP_GetGuildMOTDReply)), + luabind::value("SetGuildRank", static_cast(OP_SetGuildRank)), + luabind::value("SpawnPositionUpdate", static_cast(OP_SpawnPositionUpdate)), + luabind::value("ManaUpdate", static_cast(OP_ManaUpdate)), + luabind::value("EnduranceUpdate", static_cast(OP_EnduranceUpdate)), + luabind::value("MobManaUpdate", static_cast(OP_MobManaUpdate)), + luabind::value("MobEnduranceUpdate", static_cast(OP_MobEnduranceUpdate)), + luabind::value("GroupUpdateB", static_cast(OP_GroupUpdateB)), + luabind::value("GroupDisbandYou", static_cast(OP_GroupDisbandYou)), + luabind::value("GroupDisbandOther", static_cast(OP_GroupDisbandOther)), + luabind::value("GroupLeaderChange", static_cast(OP_GroupLeaderChange)), + luabind::value("GroupLeadershipAAUpdate", static_cast(OP_GroupLeadershipAAUpdate)), + luabind::value("GroupRoles", static_cast(OP_GroupRoles)), + luabind::value("SendFindableNPCs", static_cast(OP_SendFindableNPCs)), + luabind::value("HideCorpse", static_cast(OP_HideCorpse)), + luabind::value("TargetBuffs", static_cast(OP_TargetBuffs)), + luabind::value("TradeBusy", static_cast(OP_TradeBusy)), + luabind::value("GuildUpdateURLAndChannel", static_cast(OP_GuildUpdateURLAndChannel)), + luabind::value("CameraEffect", static_cast(OP_CameraEffect)), + luabind::value("SpellEffect", static_cast(OP_SpellEffect)), + luabind::value("DzQuit", static_cast(OP_DzQuit)), + luabind::value("DzListTimers", static_cast(OP_DzListTimers)), + luabind::value("DzPlayerList", static_cast(OP_DzPlayerList)), + luabind::value("DzAddPlayer", static_cast(OP_DzAddPlayer)), + luabind::value("DzRemovePlayer", static_cast(OP_DzRemovePlayer)), + luabind::value("DzSwapPlayer", static_cast(OP_DzSwapPlayer)), + luabind::value("DzMakeLeader", static_cast(OP_DzMakeLeader)), + luabind::value("DzExpeditionInvite", static_cast(OP_DzExpeditionInvite)), + luabind::value("DzExpeditionInviteResponse", static_cast(OP_DzExpeditionInviteResponse)), + luabind::value("DzExpeditionInfo", static_cast(OP_DzExpeditionInfo)), + luabind::value("DzMemberListName", static_cast(OP_DzMemberListName)), + luabind::value("DzMemberListStatus", static_cast(OP_DzMemberListStatus)), + luabind::value("DzSetLeaderName", static_cast(OP_DzSetLeaderName)), + luabind::value("DzExpeditionEndsWarning", static_cast(OP_DzExpeditionEndsWarning)), + luabind::value("DzExpeditionLockoutTimers", static_cast(OP_DzExpeditionLockoutTimers)), + luabind::value("DzMemberList", static_cast(OP_DzMemberList)), + luabind::value("DzCompass", static_cast(OP_DzCompass)), + luabind::value("DzChooseZone", static_cast(OP_DzChooseZone)), + luabind::value("DzChooseZoneReply", static_cast(OP_DzChooseZoneReply)), + luabind::value("BuffCreate", static_cast(OP_BuffCreate)), + luabind::value("GuildStatus", static_cast(OP_GuildStatus)), + luabind::value("BuffRemoveRequest", static_cast(OP_BuffRemoveRequest)), + luabind::value("CorpseDrag", static_cast(OP_CorpseDrag)), + luabind::value("CorpseDrop", static_cast(OP_CorpseDrop)), + luabind::value("ChangeSize", static_cast(OP_ChangeSize)), + luabind::value("GroupMakeLeader", static_cast(OP_GroupMakeLeader)), + luabind::value("RemoveAllDoors", static_cast(OP_RemoveAllDoors)), + luabind::value("RemoveNimbusEffect", static_cast(OP_RemoveNimbusEffect)), + luabind::value("GuildCreate", static_cast(OP_GuildCreate)), + luabind::value("AltCurrency", static_cast(OP_AltCurrency)), + luabind::value("FellowshipUpdate", static_cast(OP_FellowshipUpdate)), + luabind::value("AltCurrencyMerchantRequest", static_cast(OP_AltCurrencyMerchantRequest)), + luabind::value("AltCurrencyMerchantReply", static_cast(OP_AltCurrencyMerchantReply)), + luabind::value("AltCurrencyPurchase", static_cast(OP_AltCurrencyPurchase)), + luabind::value("AltCurrencySellSelection", static_cast(OP_AltCurrencySellSelection)), + luabind::value("AltCurrencyReclaim", static_cast(OP_AltCurrencyReclaim)), + luabind::value("AltCurrencySell", static_cast(OP_AltCurrencySell)), + luabind::value("Untargetable", static_cast(OP_Untargetable)), + luabind::value("CrystalReclaim", static_cast(OP_CrystalReclaim)), + luabind::value("CrystalCreate", static_cast(OP_CrystalCreate)), + luabind::value("SendMaxCharacters", static_cast(OP_SendMaxCharacters)), + luabind::value("SendMembership", static_cast(OP_SendMembership)), + luabind::value("SendMembershipDetails", static_cast(OP_SendMembershipDetails)), + luabind::value("LFGuild", static_cast(OP_LFGuild)), + luabind::value("XTargetRequest", static_cast(OP_XTargetRequest)), + luabind::value("XTargetResponse", static_cast(OP_XTargetResponse)), + luabind::value("XTargetAutoAddHaters", static_cast(OP_XTargetAutoAddHaters)), + luabind::value("Weblink", static_cast(OP_Weblink)), + luabind::value("InspectMessageUpdate", static_cast(OP_InspectMessageUpdate)), + luabind::value("ItemPreview", static_cast(OP_ItemPreview)), + luabind::value("MercenaryDataRequest", static_cast(OP_MercenaryDataRequest)), + luabind::value("MercenaryDataResponse", static_cast(OP_MercenaryDataResponse)), + luabind::value("MercenaryHire", static_cast(OP_MercenaryHire)), + luabind::value("MercenaryUnknown1", static_cast(OP_MercenaryUnknown1)), + luabind::value("MercenaryTimer", static_cast(OP_MercenaryTimer)), + luabind::value("MercenaryAssign", static_cast(OP_MercenaryAssign)), + luabind::value("MercenaryDataUpdate", static_cast(OP_MercenaryDataUpdate)), + luabind::value("MercenaryCommand", static_cast(OP_MercenaryCommand)), + luabind::value("MercenarySuspendRequest", static_cast(OP_MercenarySuspendRequest)), + luabind::value("MercenarySuspendResponse", static_cast(OP_MercenarySuspendResponse)), + luabind::value("MercenaryUnsuspendResponse", static_cast(OP_MercenaryUnsuspendResponse)), + luabind::value("MercenaryDataUpdateRequest", static_cast(OP_MercenaryDataUpdateRequest)), + 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("Marquee", static_cast(OP_Marquee)), + luabind::value("ClientTimeStamp", static_cast(OP_ClientTimeStamp)), + luabind::value("GuildPromote", static_cast(OP_GuildPromote)), + luabind::value("Fling", static_cast(OP_Fling)) + ]; } #endif diff --git a/zone/lua_raid.cpp b/zone/lua_raid.cpp index 78f6bb1b6..aa209fa7b 100644 --- a/zone/lua_raid.cpp +++ b/zone/lua_raid.cpp @@ -145,32 +145,30 @@ bool Lua_Raid::DoesAnyMemberHaveExpeditionLockout(std::string expedition_name, s luabind::scope lua_register_raid() { return luabind::class_("Raid") - .def(luabind::constructor<>()) - .property("null", &Lua_Raid::Null) - .property("valid", &Lua_Raid::Valid) - .def("IsRaidMember", (bool(Lua_Raid::*)(const char*))&Lua_Raid::IsRaidMember) - .def("CastGroupSpell", (void(Lua_Raid::*)(Lua_Mob,int,uint32))&Lua_Raid::CastGroupSpell) - .def("GroupCount", (int(Lua_Raid::*)(uint32))&Lua_Raid::GroupCount) - .def("RaidCount", (int(Lua_Raid::*)(void))&Lua_Raid::RaidCount) - .def("GetGroup", (int(Lua_Raid::*)(const char*))&Lua_Raid::GetGroup) - .def("GetGroup", (int(Lua_Raid::*)(Lua_Client))&Lua_Raid::GetGroup) - .def("SplitExp", (void(Lua_Raid::*)(uint32,Lua_Mob))&Lua_Raid::SplitExp) - .def("GetTotalRaidDamage", (uint32(Lua_Raid::*)(Lua_Mob))&Lua_Raid::GetTotalRaidDamage) - .def("SplitMoney", (void(Lua_Raid::*)(uint32,uint32,uint32,uint32,uint32))&Lua_Raid::SplitMoney) - .def("SplitMoney", (void(Lua_Raid::*)(uint32,uint32,uint32,uint32,uint32,Lua_Client))&Lua_Raid::SplitMoney) - .def("BalanceHP", (void(Lua_Raid::*)(int,uint32))&Lua_Raid::BalanceHP) - .def("IsLeader", (bool(Lua_Raid::*)(const char*))&Lua_Raid::IsLeader) - .def("IsGroupLeader", (bool(Lua_Raid::*)(const char*))&Lua_Raid::IsGroupLeader) - .def("GetHighestLevel", (int(Lua_Raid::*)(void))&Lua_Raid::GetHighestLevel) - .def("GetLowestLevel", (int(Lua_Raid::*)(void))&Lua_Raid::GetLowestLevel) - .def("GetClientByIndex", (Lua_Client(Lua_Raid::*)(int))&Lua_Raid::GetClientByIndex) - .def("TeleportGroup", (int(Lua_Raid::*)(Lua_Mob,uint32,uint32,float,float,float,float,uint32))&Lua_Raid::TeleportGroup) - .def("TeleportRaid", (int(Lua_Raid::*)(Lua_Mob,uint32,uint32,float,float,float,float))&Lua_Raid::TeleportRaid) - .def("GetID", (int(Lua_Raid::*)(void))&Lua_Raid::GetID) - .def("GetMember", (Lua_Client(Lua_Raid::*)(int))&Lua_Raid::GetMember) - .def("GetGroupNumber", (int(Lua_Raid::*)(int))&Lua_Raid::GetGroupNumber) - .def("DoesAnyMemberHaveExpeditionLockout", (bool(Lua_Raid::*)(std::string, std::string))&Lua_Raid::DoesAnyMemberHaveExpeditionLockout) - .def("DoesAnyMemberHaveExpeditionLockout", (bool(Lua_Raid::*)(std::string, std::string, int))&Lua_Raid::DoesAnyMemberHaveExpeditionLockout); + .def(luabind::constructor<>()) + .def("BalanceHP", (void(Lua_Raid::*)(int,uint32))&Lua_Raid::BalanceHP) + .def("CastGroupSpell", (void(Lua_Raid::*)(Lua_Mob,int,uint32))&Lua_Raid::CastGroupSpell) + .def("DoesAnyMemberHaveExpeditionLockout", (bool(Lua_Raid::*)(std::string, std::string))&Lua_Raid::DoesAnyMemberHaveExpeditionLockout) + .def("DoesAnyMemberHaveExpeditionLockout", (bool(Lua_Raid::*)(std::string, std::string, int))&Lua_Raid::DoesAnyMemberHaveExpeditionLockout) + .def("GetClientByIndex", (Lua_Client(Lua_Raid::*)(int))&Lua_Raid::GetClientByIndex) + .def("GetGroup", (int(Lua_Raid::*)(Lua_Client))&Lua_Raid::GetGroup) + .def("GetGroup", (int(Lua_Raid::*)(const char*))&Lua_Raid::GetGroup) + .def("GetGroupNumber", (int(Lua_Raid::*)(int))&Lua_Raid::GetGroupNumber) + .def("GetHighestLevel", (int(Lua_Raid::*)(void))&Lua_Raid::GetHighestLevel) + .def("GetID", (int(Lua_Raid::*)(void))&Lua_Raid::GetID) + .def("GetLowestLevel", (int(Lua_Raid::*)(void))&Lua_Raid::GetLowestLevel) + .def("GetMember", (Lua_Client(Lua_Raid::*)(int))&Lua_Raid::GetMember) + .def("GetTotalRaidDamage", (uint32(Lua_Raid::*)(Lua_Mob))&Lua_Raid::GetTotalRaidDamage) + .def("GroupCount", (int(Lua_Raid::*)(uint32))&Lua_Raid::GroupCount) + .def("IsGroupLeader", (bool(Lua_Raid::*)(const char*))&Lua_Raid::IsGroupLeader) + .def("IsLeader", (bool(Lua_Raid::*)(const char*))&Lua_Raid::IsLeader) + .def("IsRaidMember", (bool(Lua_Raid::*)(const char*))&Lua_Raid::IsRaidMember) + .def("RaidCount", (int(Lua_Raid::*)(void))&Lua_Raid::RaidCount) + .def("SplitExp", (void(Lua_Raid::*)(uint32,Lua_Mob))&Lua_Raid::SplitExp) + .def("SplitMoney", (void(Lua_Raid::*)(uint32,uint32,uint32,uint32,uint32))&Lua_Raid::SplitMoney) + .def("SplitMoney", (void(Lua_Raid::*)(uint32,uint32,uint32,uint32,uint32,Lua_Client))&Lua_Raid::SplitMoney) + .def("TeleportGroup", (int(Lua_Raid::*)(Lua_Mob,uint32,uint32,float,float,float,float,uint32))&Lua_Raid::TeleportGroup) + .def("TeleportRaid", (int(Lua_Raid::*)(Lua_Mob,uint32,uint32,float,float,float,float))&Lua_Raid::TeleportRaid); } #endif diff --git a/zone/lua_spawn.cpp b/zone/lua_spawn.cpp index be39bcf74..56fb94757 100644 --- a/zone/lua_spawn.cpp +++ b/zone/lua_spawn.cpp @@ -140,35 +140,33 @@ uint32 Lua_Spawn::GetKillCount() { luabind::scope lua_register_spawn() { return luabind::class_("Spawn") - .def(luabind::constructor<>()) - .property("null", &Lua_Spawn::Null) - .property("valid", &Lua_Spawn::Valid) - .def("LoadGrid", (void(Lua_Spawn::*)(void))&Lua_Spawn::LoadGrid) - .def("Enable", (void(Lua_Spawn::*)(void))&Lua_Spawn::Enable) - .def("Disable", (void(Lua_Spawn::*)(void))&Lua_Spawn::Disable) - .def("Enabled", (bool(Lua_Spawn::*)(void))&Lua_Spawn::Enabled) - .def("Reset", (void(Lua_Spawn::*)(void))&Lua_Spawn::Reset) - .def("Depop", (void(Lua_Spawn::*)(void))&Lua_Spawn::Depop) - .def("Repop", (void(Lua_Spawn::*)(void))&Lua_Spawn::Repop) - .def("Repop", (void(Lua_Spawn::*)(uint32))&Lua_Spawn::Repop) - .def("ForceDespawn", (void(Lua_Spawn::*)(void))&Lua_Spawn::ForceDespawn) - .def("GetID", (uint32(Lua_Spawn::*)(void))&Lua_Spawn::GetID) - .def("GetX", (float(Lua_Spawn::*)(void))&Lua_Spawn::GetX) - .def("GetY", (float(Lua_Spawn::*)(void))&Lua_Spawn::GetY) - .def("GetZ", (float(Lua_Spawn::*)(void))&Lua_Spawn::GetZ) - .def("GetHeading", (float(Lua_Spawn::*)(void))&Lua_Spawn::GetHeading) - .def("SetRespawnTimer", (void(Lua_Spawn::*)(uint32))&Lua_Spawn::SetRespawnTimer) - .def("SetVariance", (void(Lua_Spawn::*)(uint32))&Lua_Spawn::SetVariance) - .def("GetVariance", (uint32(Lua_Spawn::*)(void))&Lua_Spawn::GetVariance) - .def("RespawnTimer", (uint32(Lua_Spawn::*)(void))&Lua_Spawn::RespawnTimer) - .def("SpawnGroupID", (uint32(Lua_Spawn::*)(void))&Lua_Spawn::SpawnGroupID) - .def("CurrentNPCID", (uint32(Lua_Spawn::*)(void))&Lua_Spawn::CurrentNPCID) - .def("SetCurrentNPCID", (void(Lua_Spawn::*)(uint32))&Lua_Spawn::SetCurrentNPCID) - .def("GetSpawnCondition", (uint32(Lua_Spawn::*)(void))&Lua_Spawn::GetSpawnCondition) - .def("NPCPointerValid", (bool(Lua_Spawn::*)(void))&Lua_Spawn::NPCPointerValid) - .def("SetNPCPointer", (void(Lua_Spawn::*)(Lua_NPC))&Lua_Spawn::SetNPCPointer) - .def("SetTimer", (void(Lua_Spawn::*)(uint32))&Lua_Spawn::SetTimer) - .def("GetKillCount", (uint32(Lua_Spawn::*)(void))&Lua_Spawn::GetKillCount); + .def(luabind::constructor<>()) + .def("CurrentNPCID", (uint32(Lua_Spawn::*)(void))&Lua_Spawn::CurrentNPCID) + .def("Depop", (void(Lua_Spawn::*)(void))&Lua_Spawn::Depop) + .def("Disable", (void(Lua_Spawn::*)(void))&Lua_Spawn::Disable) + .def("Enable", (void(Lua_Spawn::*)(void))&Lua_Spawn::Enable) + .def("Enabled", (bool(Lua_Spawn::*)(void))&Lua_Spawn::Enabled) + .def("ForceDespawn", (void(Lua_Spawn::*)(void))&Lua_Spawn::ForceDespawn) + .def("GetHeading", (float(Lua_Spawn::*)(void))&Lua_Spawn::GetHeading) + .def("GetID", (uint32(Lua_Spawn::*)(void))&Lua_Spawn::GetID) + .def("GetKillCount", (uint32(Lua_Spawn::*)(void))&Lua_Spawn::GetKillCount) + .def("GetSpawnCondition", (uint32(Lua_Spawn::*)(void))&Lua_Spawn::GetSpawnCondition) + .def("GetVariance", (uint32(Lua_Spawn::*)(void))&Lua_Spawn::GetVariance) + .def("GetX", (float(Lua_Spawn::*)(void))&Lua_Spawn::GetX) + .def("GetY", (float(Lua_Spawn::*)(void))&Lua_Spawn::GetY) + .def("GetZ", (float(Lua_Spawn::*)(void))&Lua_Spawn::GetZ) + .def("LoadGrid", (void(Lua_Spawn::*)(void))&Lua_Spawn::LoadGrid) + .def("NPCPointerValid", (bool(Lua_Spawn::*)(void))&Lua_Spawn::NPCPointerValid) + .def("Repop", (void(Lua_Spawn::*)(uint32))&Lua_Spawn::Repop) + .def("Repop", (void(Lua_Spawn::*)(void))&Lua_Spawn::Repop) + .def("Reset", (void(Lua_Spawn::*)(void))&Lua_Spawn::Reset) + .def("RespawnTimer", (uint32(Lua_Spawn::*)(void))&Lua_Spawn::RespawnTimer) + .def("SetCurrentNPCID", (void(Lua_Spawn::*)(uint32))&Lua_Spawn::SetCurrentNPCID) + .def("SetNPCPointer", (void(Lua_Spawn::*)(Lua_NPC))&Lua_Spawn::SetNPCPointer) + .def("SetRespawnTimer", (void(Lua_Spawn::*)(uint32))&Lua_Spawn::SetRespawnTimer) + .def("SetTimer", (void(Lua_Spawn::*)(uint32))&Lua_Spawn::SetTimer) + .def("SetVariance", (void(Lua_Spawn::*)(uint32))&Lua_Spawn::SetVariance) + .def("SpawnGroupID", (uint32(Lua_Spawn::*)(void))&Lua_Spawn::SpawnGroupID); } #endif diff --git a/zone/lua_spell.cpp b/zone/lua_spell.cpp index 4675f4415..28c7e7cb5 100644 --- a/zone/lua_spell.cpp +++ b/zone/lua_spell.cpp @@ -491,95 +491,93 @@ int Lua_Spell::GetRank() { luabind::scope lua_register_spell() { return luabind::class_("Spell") - .def(luabind::constructor<>()) - .def(luabind::constructor()) - .property("null", &Lua_Spell::Null) - .property("valid", &Lua_Spell::Valid) - .def("ID", &Lua_Spell::GetID) - .def("Name", &Lua_Spell::GetName) - .def("Player1", &Lua_Spell::GetPlayer1) - .def("TeleportZone", &Lua_Spell::GetTeleportZone) - .def("YouCast", &Lua_Spell::GetYouCast) - .def("OtherCasts", &Lua_Spell::GetOtherCasts) - .def("CastOnYou", &Lua_Spell::GetCastOnYou) - .def("CastOnOther", &Lua_Spell::GetCastOnOther) - .def("SpellFades", &Lua_Spell::GetSpellFades) - .def("Range", &Lua_Spell::GetRange) - .def("AoeRange", &Lua_Spell::GetAoeRange) - .def("PushBack", &Lua_Spell::GetPushBack) - .def("PushUp", &Lua_Spell::GetPushUp) - .def("CastTime", &Lua_Spell::GetCastTime) - .def("RecoveryTime", &Lua_Spell::GetRecoveryTime) - .def("RecastTime", &Lua_Spell::GetRecastTime) - .def("BuffdurationFormula", &Lua_Spell::GetBuffdurationFormula) - .def("BuffDuration", &Lua_Spell::GetBuffDuration) - .def("AEDuration", &Lua_Spell::GetAEDuration) - .def("Mana", &Lua_Spell::GetMana) - .def("Base", &Lua_Spell::GetBase) - .def("Base2", &Lua_Spell::GetBase2) - .def("Max", &Lua_Spell::GetMax) - .def("Components", &Lua_Spell::GetComponents) - .def("ComponentCounts", &Lua_Spell::GetComponentCounts) - .def("NoexpendReagent", &Lua_Spell::GetNoexpendReagent) - .def("Formula", &Lua_Spell::GetFormula) - .def("GoodEffect", &Lua_Spell::GetGoodEffect) - .def("Activated", &Lua_Spell::GetActivated) - .def("ResistType", &Lua_Spell::GetResistType) - .def("EffectID", &Lua_Spell::GetEffectID) - .def("TargetType", &Lua_Spell::GetTargetType) - .def("BaseDiff", &Lua_Spell::GetBaseDiff) - .def("Skill", &Lua_Spell::GetSkill) - .def("ZoneType", &Lua_Spell::GetZoneType) - .def("EnvironmentType", &Lua_Spell::GetEnvironmentType) - .def("TimeOfDay", &Lua_Spell::GetTimeOfDay) - .def("Classes", &Lua_Spell::GetClasses) - .def("CastingAnim", &Lua_Spell::GetCastingAnim) - .def("SpellAffectIndex", &Lua_Spell::GetSpellAffectIndex) - .def("DisallowSit", &Lua_Spell::GetDisallowSit) - .def("Deities", &Lua_Spell::GetDeities) - .def("Uninterruptable", &Lua_Spell::GetUninterruptable) - .def("ResistDiff", &Lua_Spell::GetResistDiff) - .def("RecourseLink", &Lua_Spell::GetRecourseLink) - .def("ShortBuffBox", &Lua_Spell::GetShortBuffBox) - .def("DescNum", &Lua_Spell::GetDescNum) - .def("EffectDescNum", &Lua_Spell::GetEffectDescNum) - .def("BonusHate", &Lua_Spell::GetBonusHate) - .def("EndurCost", &Lua_Spell::GetEndurCost) - .def("EndurTimerIndex", &Lua_Spell::GetEndurTimerIndex) - .def("HateAdded", &Lua_Spell::GetHateAdded) - .def("EndurUpkeep", &Lua_Spell::GetEndurUpkeep) - .def("NumHits", &Lua_Spell::GetNumHits) - .def("PVPResistBase", &Lua_Spell::GetPVPResistBase) - .def("PVPResistCalc", &Lua_Spell::GetPVPResistCalc) - .def("PVPResistCap", &Lua_Spell::GetPVPResistCap) - .def("SpellCategory", &Lua_Spell::GetSpellCategory) - .def("PVPDuration", &Lua_Spell::GetPVPDuration) - .def("PVPDurationCap", &Lua_Spell::GetPVPDurationCap) - .def("CanMGB", &Lua_Spell::GetCanMGB) - .def("DispelFlag", &Lua_Spell::GetDispelFlag) - .def("MinResist", &Lua_Spell::GetMinResist) - .def("MaxResist", &Lua_Spell::GetMaxResist) - .def("ViralTargets", &Lua_Spell::GetViralTargets) - .def("ViralTimer", &Lua_Spell::GetViralTimer) - .def("NimbusEffect", &Lua_Spell::GetNimbusEffect) - .def("DirectionalStart", &Lua_Spell::GetDirectionalStart) - .def("DirectionalEnd", &Lua_Spell::GetDirectionalEnd) - .def("SpellGroup", &Lua_Spell::GetSpellGroup) - .def("PowerfulFlag", &Lua_Spell::GetPowerfulFlag) - .def("CastRestriction", &Lua_Spell::GetCastRestriction) - .def("AllowRest", &Lua_Spell::GetAllowRest) - .def("InCombat", &Lua_Spell::GetInCombat) - .def("OutOfCombat", &Lua_Spell::GetOutOfCombat) - .def("AEMaxTargets", &Lua_Spell::GetAEMaxTargets) - .def("MaxTargets", &Lua_Spell::GetMaxTargets) - .def("PersistDeath", &Lua_Spell::GetPersistDeath) - .def("MinDist", &Lua_Spell::GetMinDist) - .def("MinDistMod", &Lua_Spell::GetMinDistMod) - .def("MaxDist", &Lua_Spell::GetMaxDist) - .def("MaxDistMod", &Lua_Spell::GetMaxDistMod) - .def("MinRange", &Lua_Spell::GetMinRange) - .def("DamageShieldType", &Lua_Spell::GetDamageShieldType) - .def("Rank", &Lua_Spell::GetRank); + .def(luabind::constructor<>()) + .def(luabind::constructor()) + .def("AEDuration", &Lua_Spell::GetAEDuration) + .def("AEMaxTargets", &Lua_Spell::GetAEMaxTargets) + .def("Activated", &Lua_Spell::GetActivated) + .def("AllowRest", &Lua_Spell::GetAllowRest) + .def("AoeRange", &Lua_Spell::GetAoeRange) + .def("Base", &Lua_Spell::GetBase) + .def("Base2", &Lua_Spell::GetBase2) + .def("BaseDiff", &Lua_Spell::GetBaseDiff) + .def("BonusHate", &Lua_Spell::GetBonusHate) + .def("BuffDuration", &Lua_Spell::GetBuffDuration) + .def("BuffdurationFormula", &Lua_Spell::GetBuffdurationFormula) + .def("CanMGB", &Lua_Spell::GetCanMGB) + .def("CastOnOther", &Lua_Spell::GetCastOnOther) + .def("CastOnYou", &Lua_Spell::GetCastOnYou) + .def("CastRestriction", &Lua_Spell::GetCastRestriction) + .def("CastTime", &Lua_Spell::GetCastTime) + .def("CastingAnim", &Lua_Spell::GetCastingAnim) + .def("Classes", &Lua_Spell::GetClasses) + .def("ComponentCounts", &Lua_Spell::GetComponentCounts) + .def("Components", &Lua_Spell::GetComponents) + .def("DamageShieldType", &Lua_Spell::GetDamageShieldType) + .def("Deities", &Lua_Spell::GetDeities) + .def("DescNum", &Lua_Spell::GetDescNum) + .def("DirectionalEnd", &Lua_Spell::GetDirectionalEnd) + .def("DirectionalStart", &Lua_Spell::GetDirectionalStart) + .def("DisallowSit", &Lua_Spell::GetDisallowSit) + .def("DispelFlag", &Lua_Spell::GetDispelFlag) + .def("EffectDescNum", &Lua_Spell::GetEffectDescNum) + .def("EffectID", &Lua_Spell::GetEffectID) + .def("EndurCost", &Lua_Spell::GetEndurCost) + .def("EndurTimerIndex", &Lua_Spell::GetEndurTimerIndex) + .def("EndurUpkeep", &Lua_Spell::GetEndurUpkeep) + .def("EnvironmentType", &Lua_Spell::GetEnvironmentType) + .def("Formula", &Lua_Spell::GetFormula) + .def("GoodEffect", &Lua_Spell::GetGoodEffect) + .def("HateAdded", &Lua_Spell::GetHateAdded) + .def("ID", &Lua_Spell::GetID) + .def("InCombat", &Lua_Spell::GetInCombat) + .def("Mana", &Lua_Spell::GetMana) + .def("Max", &Lua_Spell::GetMax) + .def("MaxDist", &Lua_Spell::GetMaxDist) + .def("MaxDistMod", &Lua_Spell::GetMaxDistMod) + .def("MaxResist", &Lua_Spell::GetMaxResist) + .def("MaxTargets", &Lua_Spell::GetMaxTargets) + .def("MinDist", &Lua_Spell::GetMinDist) + .def("MinDistMod", &Lua_Spell::GetMinDistMod) + .def("MinRange", &Lua_Spell::GetMinRange) + .def("MinResist", &Lua_Spell::GetMinResist) + .def("Name", &Lua_Spell::GetName) + .def("NimbusEffect", &Lua_Spell::GetNimbusEffect) + .def("NoexpendReagent", &Lua_Spell::GetNoexpendReagent) + .def("NumHits", &Lua_Spell::GetNumHits) + .def("OtherCasts", &Lua_Spell::GetOtherCasts) + .def("OutOfCombat", &Lua_Spell::GetOutOfCombat) + .def("PVPDuration", &Lua_Spell::GetPVPDuration) + .def("PVPDurationCap", &Lua_Spell::GetPVPDurationCap) + .def("PVPResistBase", &Lua_Spell::GetPVPResistBase) + .def("PVPResistCalc", &Lua_Spell::GetPVPResistCalc) + .def("PVPResistCap", &Lua_Spell::GetPVPResistCap) + .def("PersistDeath", &Lua_Spell::GetPersistDeath) + .def("Player1", &Lua_Spell::GetPlayer1) + .def("PowerfulFlag", &Lua_Spell::GetPowerfulFlag) + .def("PushBack", &Lua_Spell::GetPushBack) + .def("PushUp", &Lua_Spell::GetPushUp) + .def("Range", &Lua_Spell::GetRange) + .def("Rank", &Lua_Spell::GetRank) + .def("RecastTime", &Lua_Spell::GetRecastTime) + .def("RecourseLink", &Lua_Spell::GetRecourseLink) + .def("RecoveryTime", &Lua_Spell::GetRecoveryTime) + .def("ResistDiff", &Lua_Spell::GetResistDiff) + .def("ResistType", &Lua_Spell::GetResistType) + .def("ShortBuffBox", &Lua_Spell::GetShortBuffBox) + .def("Skill", &Lua_Spell::GetSkill) + .def("SpellAffectIndex", &Lua_Spell::GetSpellAffectIndex) + .def("SpellCategory", &Lua_Spell::GetSpellCategory) + .def("SpellFades", &Lua_Spell::GetSpellFades) + .def("SpellGroup", &Lua_Spell::GetSpellGroup) + .def("TargetType", &Lua_Spell::GetTargetType) + .def("TeleportZone", &Lua_Spell::GetTeleportZone) + .def("TimeOfDay", &Lua_Spell::GetTimeOfDay) + .def("Uninterruptable", &Lua_Spell::GetUninterruptable) + .def("ViralTargets", &Lua_Spell::GetViralTargets) + .def("ViralTimer", &Lua_Spell::GetViralTimer) + .def("YouCast", &Lua_Spell::GetYouCast) + .def("ZoneType", &Lua_Spell::GetZoneType); } #endif diff --git a/zone/lua_stat_bonuses.cpp b/zone/lua_stat_bonuses.cpp index 156a320f9..9da4a64b4 100644 --- a/zone/lua_stat_bonuses.cpp +++ b/zone/lua_stat_bonuses.cpp @@ -1287,263 +1287,263 @@ int32 Lua_StatBonuses::GetReduceTradeskillFail(int idx) const { luabind::scope lua_register_stat_bonuses() { return luabind::class_("StatBonuses") - .def(luabind::constructor<>()) - .def("AC", &Lua_StatBonuses::GetAC) - .def("HP", &Lua_StatBonuses::GetHP) - .def("HPRegen", &Lua_StatBonuses::GetHPRegen) - .def("MaxHP", &Lua_StatBonuses::GetMaxHP) - .def("ManaRegen", &Lua_StatBonuses::GetManaRegen) - .def("EnduranceRegen", &Lua_StatBonuses::GetEnduranceRegen) - .def("Mana", &Lua_StatBonuses::GetMana) - .def("Endurance", &Lua_StatBonuses::GetEndurance) - .def("ATK", &Lua_StatBonuses::GetATK) - .def("STR", &Lua_StatBonuses::GetSTR) - .def("STRCapMod", &Lua_StatBonuses::GetSTRCapMod) - .def("HeroicSTR", &Lua_StatBonuses::GetHeroicSTR) - .def("STA", &Lua_StatBonuses::GetSTA) - .def("STACapMod", &Lua_StatBonuses::GetSTACapMod) - .def("HeroicSTA", &Lua_StatBonuses::GetHeroicSTA) - .def("DEX", &Lua_StatBonuses::GetDEX) - .def("DEXCapMod", &Lua_StatBonuses::GetDEXCapMod) - .def("HeroicDEX", &Lua_StatBonuses::GetHeroicDEX) - .def("AGI", &Lua_StatBonuses::GetAGI) - .def("AGICapMod", &Lua_StatBonuses::GetAGICapMod) - .def("HeroicAGI", &Lua_StatBonuses::GetHeroicAGI) - .def("INT", &Lua_StatBonuses::GetINT) - .def("INTCapMod", &Lua_StatBonuses::GetINTCapMod) - .def("HeroicINT", &Lua_StatBonuses::GetHeroicINT) - .def("WIS", &Lua_StatBonuses::GetWIS) - .def("WISCapMod", &Lua_StatBonuses::GetWISCapMod) - .def("HeroicWIS", &Lua_StatBonuses::GetHeroicWIS) - .def("CHA", &Lua_StatBonuses::GetCHA) - .def("CHACapMod", &Lua_StatBonuses::GetCHACapMod) - .def("HeroicCHA", &Lua_StatBonuses::GetHeroicCHA) - .def("MR", &Lua_StatBonuses::GetMR) - .def("MRCapMod", &Lua_StatBonuses::GetMRCapMod) - .def("HeroicMR", &Lua_StatBonuses::GetHeroicMR) - .def("FR", &Lua_StatBonuses::GetFR) - .def("FRCapMod", &Lua_StatBonuses::GetFRCapMod) - .def("HeroicFR", &Lua_StatBonuses::GetHeroicFR) - .def("CR", &Lua_StatBonuses::GetCR) - .def("CRCapMod", &Lua_StatBonuses::GetCRCapMod) - .def("HeroicCR", &Lua_StatBonuses::GetHeroicCR) - .def("PR", &Lua_StatBonuses::GetPR) - .def("PRCapMod", &Lua_StatBonuses::GetPRCapMod) - .def("HeroicPR", &Lua_StatBonuses::GetHeroicPR) - .def("DR", &Lua_StatBonuses::GetDR) - .def("DRCapMod", &Lua_StatBonuses::GetDRCapMod) - .def("HeroicDR", &Lua_StatBonuses::GetHeroicDR) - .def("Corrup", &Lua_StatBonuses::GetCorrup) - .def("CorrupCapMod", &Lua_StatBonuses::GetCorrupCapMod) - .def("HeroicCorrup", &Lua_StatBonuses::GetHeroicCorrup) - .def("DamageShieldSpellID", &Lua_StatBonuses::GetDamageShieldSpellID) - .def("DamageShield", &Lua_StatBonuses::GetDamageShield) - .def("DamageShieldType", &Lua_StatBonuses::GetDamageShieldType) - .def("SpellDamageShield", &Lua_StatBonuses::GetSpellDamageShield) - .def("SpellShield", &Lua_StatBonuses::GetSpellShield) - .def("ReverseDamageShield", &Lua_StatBonuses::GetReverseDamageShield) - .def("ReverseDamageShieldSpellID", &Lua_StatBonuses::GetReverseDamageShieldSpellID) - .def("ReverseDamageShieldType", &Lua_StatBonuses::GetReverseDamageShieldType) - .def("movementspeed", &Lua_StatBonuses::Getmovementspeed) - .def("haste", &Lua_StatBonuses::Gethaste) - .def("hastetype2", &Lua_StatBonuses::Gethastetype2) - .def("hastetype3", &Lua_StatBonuses::Gethastetype3) - .def("inhibitmelee", &Lua_StatBonuses::Getinhibitmelee) - .def("AggroRange", &Lua_StatBonuses::GetAggroRange) - .def("AssistRange", &Lua_StatBonuses::GetAssistRange) - .def("skillmod", &Lua_StatBonuses::Getskillmod) - .def("skillmodmax", &Lua_StatBonuses::Getskillmodmax) - .def("effective_casting_level", &Lua_StatBonuses::Geteffective_casting_level) - .def("adjusted_casting_skill", &Lua_StatBonuses::Getadjusted_casting_skill) - .def("reflect_chance", &Lua_StatBonuses::Getreflect_chance) - .def("singingMod", &Lua_StatBonuses::GetsingingMod) - .def("Amplification", &Lua_StatBonuses::GetAmplification) - .def("brassMod", &Lua_StatBonuses::GetbrassMod) - .def("percussionMod", &Lua_StatBonuses::GetpercussionMod) - .def("windMod", &Lua_StatBonuses::GetwindMod) - .def("stringedMod", &Lua_StatBonuses::GetstringedMod) - .def("songModCap", &Lua_StatBonuses::GetsongModCap) - .def("hatemod", &Lua_StatBonuses::Gethatemod) - .def("EnduranceReduction", &Lua_StatBonuses::GetEnduranceReduction) - .def("StrikeThrough", &Lua_StatBonuses::GetStrikeThrough) - .def("MeleeMitigation", &Lua_StatBonuses::GetMeleeMitigation) - .def("MeleeMitigationEffect", &Lua_StatBonuses::GetMeleeMitigationEffect) - .def("CriticalHitChance", &Lua_StatBonuses::GetCriticalHitChance) - .def("CriticalSpellChance", &Lua_StatBonuses::GetCriticalSpellChance) - .def("SpellCritDmgIncrease", &Lua_StatBonuses::GetSpellCritDmgIncrease) - .def("SpellCritDmgIncNoStack", &Lua_StatBonuses::GetSpellCritDmgIncNoStack) - .def("DotCritDmgIncrease", &Lua_StatBonuses::GetDotCritDmgIncrease) - .def("CriticalHealChance", &Lua_StatBonuses::GetCriticalHealChance) - .def("CriticalHealOverTime", &Lua_StatBonuses::GetCriticalHealOverTime) - .def("CriticalDoTChance", &Lua_StatBonuses::GetCriticalDoTChance) - .def("CrippBlowChance", &Lua_StatBonuses::GetCrippBlowChance) - .def("AvoidMeleeChance", &Lua_StatBonuses::GetAvoidMeleeChance) - .def("AvoidMeleeChanceEffect", &Lua_StatBonuses::GetAvoidMeleeChanceEffect) - .def("RiposteChance", &Lua_StatBonuses::GetRiposteChance) - .def("DodgeChance", &Lua_StatBonuses::GetDodgeChance) - .def("ParryChance", &Lua_StatBonuses::GetParryChance) - .def("DualWieldChance", &Lua_StatBonuses::GetDualWieldChance) - .def("DoubleAttackChance", &Lua_StatBonuses::GetDoubleAttackChance) - .def("TripleAttackChance", &Lua_StatBonuses::GetTripleAttackChance) - .def("DoubleRangedAttack", &Lua_StatBonuses::GetDoubleRangedAttack) - .def("ResistSpellChance", &Lua_StatBonuses::GetResistSpellChance) - .def("ResistFearChance", &Lua_StatBonuses::GetResistFearChance) - .def("Fearless", &Lua_StatBonuses::GetFearless) - .def("IsFeared", &Lua_StatBonuses::GetIsFeared) - .def("IsBlind", &Lua_StatBonuses::GetIsBlind) - .def("StunResist", &Lua_StatBonuses::GetStunResist) - .def("MeleeSkillCheck", &Lua_StatBonuses::GetMeleeSkillCheck) - .def("MeleeSkillCheckSkill", &Lua_StatBonuses::GetMeleeSkillCheckSkill) - .def("HitChance", &Lua_StatBonuses::GetHitChance) - .def("HitChanceEffect", &Lua_StatBonuses::GetHitChanceEffect) - .def("DamageModifier", &Lua_StatBonuses::GetDamageModifier) - .def("DamageModifier2", &Lua_StatBonuses::GetDamageModifier2) - .def("MinDamageModifier", &Lua_StatBonuses::GetMinDamageModifier) - .def("ProcChance", &Lua_StatBonuses::GetProcChance) - .def("ProcChanceSPA", &Lua_StatBonuses::GetProcChanceSPA) - .def("ExtraAttackChance", &Lua_StatBonuses::GetExtraAttackChance) - .def("DoTShielding", &Lua_StatBonuses::GetDoTShielding) - .def("FlurryChance", &Lua_StatBonuses::GetFlurryChance) - .def("HundredHands", &Lua_StatBonuses::GetHundredHands) - .def("MeleeLifetap", &Lua_StatBonuses::GetMeleeLifetap) - .def("Vampirism", &Lua_StatBonuses::GetVampirism) - .def("HealRate", &Lua_StatBonuses::GetHealRate) - .def("MaxHPChange", &Lua_StatBonuses::GetMaxHPChange) - .def("HealAmt", &Lua_StatBonuses::GetHealAmt) - .def("SpellDmg", &Lua_StatBonuses::GetSpellDmg) - .def("Clairvoyance", &Lua_StatBonuses::GetClairvoyance) - .def("DSMitigation", &Lua_StatBonuses::GetDSMitigation) - .def("DSMitigationOffHand", &Lua_StatBonuses::GetDSMitigationOffHand) - .def("TwoHandBluntBlock", &Lua_StatBonuses::GetTwoHandBluntBlock) - .def("ItemManaRegenCap", &Lua_StatBonuses::GetItemManaRegenCap) - .def("GravityEffect", &Lua_StatBonuses::GetGravityEffect) - .def("AntiGate", &Lua_StatBonuses::GetAntiGate) - .def("MagicWeapon", &Lua_StatBonuses::GetMagicWeapon) - .def("IncreaseBlockChance", &Lua_StatBonuses::GetIncreaseBlockChance) - .def("PersistantCasting", &Lua_StatBonuses::GetPersistantCasting) - .def("XPRateMod", &Lua_StatBonuses::GetXPRateMod) - .def("BlockNextSpell", &Lua_StatBonuses::GetBlockNextSpell) - .def("ImmuneToFlee", &Lua_StatBonuses::GetImmuneToFlee) - .def("VoiceGraft", &Lua_StatBonuses::GetVoiceGraft) - .def("SpellProcChance", &Lua_StatBonuses::GetSpellProcChance) - .def("CharmBreakChance", &Lua_StatBonuses::GetCharmBreakChance) - .def("SongRange", &Lua_StatBonuses::GetSongRange) - .def("HPToManaConvert", &Lua_StatBonuses::GetHPToManaConvert) - .def("NegateEffects", &Lua_StatBonuses::GetNegateEffects) - .def("TriggerMeleeThreshold", &Lua_StatBonuses::GetTriggerMeleeThreshold) - .def("TriggerSpellThreshold", &Lua_StatBonuses::GetTriggerSpellThreshold) - .def("ShieldBlock", &Lua_StatBonuses::GetShieldBlock) - .def("BlockBehind", &Lua_StatBonuses::GetBlockBehind) - .def("CriticalRegenDecay", &Lua_StatBonuses::GetCriticalRegenDecay) - .def("CriticalHealDecay", &Lua_StatBonuses::GetCriticalHealDecay) - .def("CriticalDotDecay", &Lua_StatBonuses::GetCriticalDotDecay) - .def("DivineAura", &Lua_StatBonuses::GetDivineAura) - .def("DistanceRemoval", &Lua_StatBonuses::GetDistanceRemoval) - .def("FrenziedDevastation", &Lua_StatBonuses::GetFrenziedDevastation) - .def("NegateIfCombat", &Lua_StatBonuses::GetNegateIfCombat) - .def("Screech", &Lua_StatBonuses::GetScreech) - .def("AlterNPCLevel", &Lua_StatBonuses::GetAlterNPCLevel) - .def("BerserkSPA", &Lua_StatBonuses::GetBerserkSPA) - .def("Metabolism", &Lua_StatBonuses::GetMetabolism) - .def("Sanctuary", &Lua_StatBonuses::GetSanctuary) - .def("FactionModPct", &Lua_StatBonuses::GetFactionModPct) - .def("PC_Pet_Flurry", &Lua_StatBonuses::GetPC_Pet_Flurry) - .def("Packrat", &Lua_StatBonuses::GetPackrat) - .def("BuffSlotIncrease", &Lua_StatBonuses::GetBuffSlotIncrease) - .def("DelayDeath", &Lua_StatBonuses::GetDelayDeath) - .def("BaseMovementSpeed", &Lua_StatBonuses::GetBaseMovementSpeed) - .def("IncreaseRunSpeedCap", &Lua_StatBonuses::GetIncreaseRunSpeedCap) - .def("DoubleSpecialAttack", &Lua_StatBonuses::GetDoubleSpecialAttack) - .def("FrontalStunResist", &Lua_StatBonuses::GetFrontalStunResist) - .def("BindWound", &Lua_StatBonuses::GetBindWound) - .def("MaxBindWound", &Lua_StatBonuses::GetMaxBindWound) - .def("ChannelChanceSpells", &Lua_StatBonuses::GetChannelChanceSpells) - .def("ChannelChanceItems", &Lua_StatBonuses::GetChannelChanceItems) - .def("SeeInvis", &Lua_StatBonuses::GetSeeInvis) - .def("TripleBackstab", &Lua_StatBonuses::GetTripleBackstab) - .def("FrontalBackstabMinDmg", &Lua_StatBonuses::GetFrontalBackstabMinDmg) - .def("FrontalBackstabChance", &Lua_StatBonuses::GetFrontalBackstabChance) - .def("ConsumeProjectile", &Lua_StatBonuses::GetConsumeProjectile) - .def("ForageAdditionalItems", &Lua_StatBonuses::GetForageAdditionalItems) - .def("SalvageChance", &Lua_StatBonuses::GetSalvageChance) - .def("ArcheryDamageModifier", &Lua_StatBonuses::GetArcheryDamageModifier) - .def("SecondaryDmgInc", &Lua_StatBonuses::GetSecondaryDmgInc) - .def("GiveDoubleAttack", &Lua_StatBonuses::GetGiveDoubleAttack) - .def("PetCriticalHit", &Lua_StatBonuses::GetPetCriticalHit) - .def("PetAvoidance", &Lua_StatBonuses::GetPetAvoidance) - .def("CombatStability", &Lua_StatBonuses::GetCombatStability) - .def("DoubleRiposte", &Lua_StatBonuses::GetDoubleRiposte) - .def("Ambidexterity", &Lua_StatBonuses::GetAmbidexterity) - .def("PetMaxHP", &Lua_StatBonuses::GetPetMaxHP) - .def("PetFlurry", &Lua_StatBonuses::GetPetFlurry) - .def("MasteryofPast", &Lua_StatBonuses::GetMasteryofPast) - .def("GivePetGroupTarget", &Lua_StatBonuses::GetGivePetGroupTarget) - .def("RootBreakChance", &Lua_StatBonuses::GetRootBreakChance) - .def("UnfailingDivinity", &Lua_StatBonuses::GetUnfailingDivinity) - .def("ItemHPRegenCap", &Lua_StatBonuses::GetItemHPRegenCap) - .def("OffhandRiposteFail", &Lua_StatBonuses::GetOffhandRiposteFail) - .def("ItemATKCap", &Lua_StatBonuses::GetItemATKCap) - .def("ShieldEquipDmgMod", &Lua_StatBonuses::GetShieldEquipDmgMod) - .def("TriggerOnValueAmount", &Lua_StatBonuses::GetTriggerOnValueAmount) - .def("StunBashChance", &Lua_StatBonuses::GetStunBashChance) - .def("IncreaseChanceMemwipe", &Lua_StatBonuses::GetIncreaseChanceMemwipe) - .def("CriticalMend", &Lua_StatBonuses::GetCriticalMend) - .def("ImprovedReclaimEnergy", &Lua_StatBonuses::GetImprovedReclaimEnergy) - .def("PetMeleeMitigation", &Lua_StatBonuses::GetPetMeleeMitigation) - .def("IllusionPersistence", &Lua_StatBonuses::GetIllusionPersistence) - .def("extra_xtargets", &Lua_StatBonuses::Getextra_xtargets) - .def("ShroudofStealth", &Lua_StatBonuses::GetShroudofStealth) - .def("ReduceFallDamage", &Lua_StatBonuses::GetReduceFallDamage) - .def("TradeSkillMastery", &Lua_StatBonuses::GetTradeSkillMastery) - .def("NoBreakAESneak", &Lua_StatBonuses::GetNoBreakAESneak) - .def("FeignedCastOnChance", &Lua_StatBonuses::GetFeignedCastOnChance) - .def("DivineSaveChance", &Lua_StatBonuses::GetDivineSaveChance) - .def("DeathSave", &Lua_StatBonuses::GetDeathSave) - .def("Accuracy", &Lua_StatBonuses::GetAccuracy) - .def("SkillDmgTaken", &Lua_StatBonuses::GetSkillDmgTaken) - .def("SpellTriggers", &Lua_StatBonuses::GetSpellTriggers) - .def("SpellOnKill", &Lua_StatBonuses::GetSpellOnKill) - .def("SpellOnDeath", &Lua_StatBonuses::GetSpellOnDeath) - .def("CritDmgMod", &Lua_StatBonuses::GetCritDmgMod) - .def("SkillReuseTime", &Lua_StatBonuses::GetSkillReuseTime) - .def("SkillDamageAmount", &Lua_StatBonuses::GetSkillDamageAmount) - .def("HPPercCap", &Lua_StatBonuses::GetHPPercCap) - .def("ManaPercCap", &Lua_StatBonuses::GetManaPercCap) - .def("EndPercCap", &Lua_StatBonuses::GetEndPercCap) - .def("FocusEffects", &Lua_StatBonuses::GetFocusEffects) - .def("FocusEffectsWorn", &Lua_StatBonuses::GetFocusEffectsWorn) - .def("SkillDamageAmount2", &Lua_StatBonuses::GetSkillDamageAmount2) - .def("NegateAttacks", &Lua_StatBonuses::GetNegateAttacks) - .def("MitigateMeleeRune", &Lua_StatBonuses::GetMitigateMeleeRune) - .def("MeleeThresholdGuard", &Lua_StatBonuses::GetMeleeThresholdGuard) - .def("SpellThresholdGuard", &Lua_StatBonuses::GetSpellThresholdGuard) - .def("MitigateSpellRune", &Lua_StatBonuses::GetMitigateSpellRune) - .def("MitigateDotRune", &Lua_StatBonuses::GetMitigateDotRune) - .def("ManaAbsorbPercentDamage", &Lua_StatBonuses::GetManaAbsorbPercentDamage) - .def("ImprovedTaunt", &Lua_StatBonuses::GetImprovedTaunt) - .def("Root", &Lua_StatBonuses::GetRoot) - .def("AbsorbMagicAtt", &Lua_StatBonuses::GetAbsorbMagicAtt) - .def("MeleeRune", &Lua_StatBonuses::GetMeleeRune) - .def("AStacker", &Lua_StatBonuses::GetAStacker) - .def("BStacker", &Lua_StatBonuses::GetBStacker) - .def("CStacker", &Lua_StatBonuses::GetCStacker) - .def("DStacker", &Lua_StatBonuses::GetDStacker) - .def("LimitToSkill", &Lua_StatBonuses::GetLimitToSkill) - .def("SkillProc", &Lua_StatBonuses::GetSkillProc) - .def("SkillProcSuccess", &Lua_StatBonuses::GetSkillProcSuccess) - .def("PC_Pet_Rampage", &Lua_StatBonuses::GetPC_Pet_Rampage) - .def("SkillAttackProc", &Lua_StatBonuses::GetSkillAttackProc) - .def("SlayUndead", &Lua_StatBonuses::GetSlayUndead) - .def("GiveDoubleRiposte", &Lua_StatBonuses::GetGiveDoubleRiposte) - .def("RaiseSkillCap", &Lua_StatBonuses::GetRaiseSkillCap) - .def("SEResist", &Lua_StatBonuses::GetSEResist) - .def("FinishingBlow", &Lua_StatBonuses::GetFinishingBlow) - .def("FinishingBlowLvl", &Lua_StatBonuses::GetFinishingBlowLvl) - .def("HeadShot", &Lua_StatBonuses::GetHeadShot) - .def("HSLevel", &Lua_StatBonuses::GetHSLevel) - .def("Assassinate", &Lua_StatBonuses::GetAssassinate) - .def("AssassinateLevel", &Lua_StatBonuses::GetAssassinateLevel) - .def("ReduceTradeskillFail", &Lua_StatBonuses::GetReduceTradeskillFail); + .def(luabind::constructor<>()) + .def("AbsorbMagicAtt", &Lua_StatBonuses::GetAbsorbMagicAtt) + .def("AC", &Lua_StatBonuses::GetAC) + .def("Accuracy", &Lua_StatBonuses::GetAccuracy) + .def("adjusted_casting_skill", &Lua_StatBonuses::Getadjusted_casting_skill) + .def("AggroRange", &Lua_StatBonuses::GetAggroRange) + .def("AGI", &Lua_StatBonuses::GetAGI) + .def("AGICapMod", &Lua_StatBonuses::GetAGICapMod) + .def("AlterNPCLevel", &Lua_StatBonuses::GetAlterNPCLevel) + .def("Ambidexterity", &Lua_StatBonuses::GetAmbidexterity) + .def("Amplification", &Lua_StatBonuses::GetAmplification) + .def("AntiGate", &Lua_StatBonuses::GetAntiGate) + .def("ArcheryDamageModifier", &Lua_StatBonuses::GetArcheryDamageModifier) + .def("Assassinate", &Lua_StatBonuses::GetAssassinate) + .def("AssassinateLevel", &Lua_StatBonuses::GetAssassinateLevel) + .def("AssistRange", &Lua_StatBonuses::GetAssistRange) + .def("AStacker", &Lua_StatBonuses::GetAStacker) + .def("ATK", &Lua_StatBonuses::GetATK) + .def("AvoidMeleeChance", &Lua_StatBonuses::GetAvoidMeleeChance) + .def("AvoidMeleeChanceEffect", &Lua_StatBonuses::GetAvoidMeleeChanceEffect) + .def("BaseMovementSpeed", &Lua_StatBonuses::GetBaseMovementSpeed) + .def("BerserkSPA", &Lua_StatBonuses::GetBerserkSPA) + .def("BindWound", &Lua_StatBonuses::GetBindWound) + .def("BlockBehind", &Lua_StatBonuses::GetBlockBehind) + .def("BlockNextSpell", &Lua_StatBonuses::GetBlockNextSpell) + .def("brassMod", &Lua_StatBonuses::GetbrassMod) + .def("BStacker", &Lua_StatBonuses::GetBStacker) + .def("BuffSlotIncrease", &Lua_StatBonuses::GetBuffSlotIncrease) + .def("CHA", &Lua_StatBonuses::GetCHA) + .def("CHACapMod", &Lua_StatBonuses::GetCHACapMod) + .def("ChannelChanceItems", &Lua_StatBonuses::GetChannelChanceItems) + .def("ChannelChanceSpells", &Lua_StatBonuses::GetChannelChanceSpells) + .def("CharmBreakChance", &Lua_StatBonuses::GetCharmBreakChance) + .def("Clairvoyance", &Lua_StatBonuses::GetClairvoyance) + .def("CombatStability", &Lua_StatBonuses::GetCombatStability) + .def("ConsumeProjectile", &Lua_StatBonuses::GetConsumeProjectile) + .def("Corrup", &Lua_StatBonuses::GetCorrup) + .def("CorrupCapMod", &Lua_StatBonuses::GetCorrupCapMod) + .def("CR", &Lua_StatBonuses::GetCR) + .def("CRCapMod", &Lua_StatBonuses::GetCRCapMod) + .def("CrippBlowChance", &Lua_StatBonuses::GetCrippBlowChance) + .def("CritDmgMod", &Lua_StatBonuses::GetCritDmgMod) + .def("CriticalDoTChance", &Lua_StatBonuses::GetCriticalDoTChance) + .def("CriticalDotDecay", &Lua_StatBonuses::GetCriticalDotDecay) + .def("CriticalHealChance", &Lua_StatBonuses::GetCriticalHealChance) + .def("CriticalHealDecay", &Lua_StatBonuses::GetCriticalHealDecay) + .def("CriticalHealOverTime", &Lua_StatBonuses::GetCriticalHealOverTime) + .def("CriticalHitChance", &Lua_StatBonuses::GetCriticalHitChance) + .def("CriticalMend", &Lua_StatBonuses::GetCriticalMend) + .def("CriticalRegenDecay", &Lua_StatBonuses::GetCriticalRegenDecay) + .def("CriticalSpellChance", &Lua_StatBonuses::GetCriticalSpellChance) + .def("CStacker", &Lua_StatBonuses::GetCStacker) + .def("DamageModifier", &Lua_StatBonuses::GetDamageModifier) + .def("DamageModifier2", &Lua_StatBonuses::GetDamageModifier2) + .def("DamageShield", &Lua_StatBonuses::GetDamageShield) + .def("DamageShieldSpellID", &Lua_StatBonuses::GetDamageShieldSpellID) + .def("DamageShieldType", &Lua_StatBonuses::GetDamageShieldType) + .def("DeathSave", &Lua_StatBonuses::GetDeathSave) + .def("DelayDeath", &Lua_StatBonuses::GetDelayDeath) + .def("DEX", &Lua_StatBonuses::GetDEX) + .def("DEXCapMod", &Lua_StatBonuses::GetDEXCapMod) + .def("DistanceRemoval", &Lua_StatBonuses::GetDistanceRemoval) + .def("DivineAura", &Lua_StatBonuses::GetDivineAura) + .def("DivineSaveChance", &Lua_StatBonuses::GetDivineSaveChance) + .def("DodgeChance", &Lua_StatBonuses::GetDodgeChance) + .def("DotCritDmgIncrease", &Lua_StatBonuses::GetDotCritDmgIncrease) + .def("DoTShielding", &Lua_StatBonuses::GetDoTShielding) + .def("DoubleAttackChance", &Lua_StatBonuses::GetDoubleAttackChance) + .def("DoubleRangedAttack", &Lua_StatBonuses::GetDoubleRangedAttack) + .def("DoubleRiposte", &Lua_StatBonuses::GetDoubleRiposte) + .def("DoubleSpecialAttack", &Lua_StatBonuses::GetDoubleSpecialAttack) + .def("DR", &Lua_StatBonuses::GetDR) + .def("DRCapMod", &Lua_StatBonuses::GetDRCapMod) + .def("DSMitigation", &Lua_StatBonuses::GetDSMitigation) + .def("DSMitigationOffHand", &Lua_StatBonuses::GetDSMitigationOffHand) + .def("DStacker", &Lua_StatBonuses::GetDStacker) + .def("DualWieldChance", &Lua_StatBonuses::GetDualWieldChance) + .def("effective_casting_level", &Lua_StatBonuses::Geteffective_casting_level) + .def("EndPercCap", &Lua_StatBonuses::GetEndPercCap) + .def("Endurance", &Lua_StatBonuses::GetEndurance) + .def("EnduranceReduction", &Lua_StatBonuses::GetEnduranceReduction) + .def("EnduranceRegen", &Lua_StatBonuses::GetEnduranceRegen) + .def("extra_xtargets", &Lua_StatBonuses::Getextra_xtargets) + .def("ExtraAttackChance", &Lua_StatBonuses::GetExtraAttackChance) + .def("FactionModPct", &Lua_StatBonuses::GetFactionModPct) + .def("Fearless", &Lua_StatBonuses::GetFearless) + .def("FeignedCastOnChance", &Lua_StatBonuses::GetFeignedCastOnChance) + .def("FinishingBlow", &Lua_StatBonuses::GetFinishingBlow) + .def("FinishingBlowLvl", &Lua_StatBonuses::GetFinishingBlowLvl) + .def("FlurryChance", &Lua_StatBonuses::GetFlurryChance) + .def("FocusEffects", &Lua_StatBonuses::GetFocusEffects) + .def("FocusEffectsWorn", &Lua_StatBonuses::GetFocusEffectsWorn) + .def("ForageAdditionalItems", &Lua_StatBonuses::GetForageAdditionalItems) + .def("FR", &Lua_StatBonuses::GetFR) + .def("FRCapMod", &Lua_StatBonuses::GetFRCapMod) + .def("FrenziedDevastation", &Lua_StatBonuses::GetFrenziedDevastation) + .def("FrontalBackstabChance", &Lua_StatBonuses::GetFrontalBackstabChance) + .def("FrontalBackstabMinDmg", &Lua_StatBonuses::GetFrontalBackstabMinDmg) + .def("FrontalStunResist", &Lua_StatBonuses::GetFrontalStunResist) + .def("GiveDoubleAttack", &Lua_StatBonuses::GetGiveDoubleAttack) + .def("GiveDoubleRiposte", &Lua_StatBonuses::GetGiveDoubleRiposte) + .def("GivePetGroupTarget", &Lua_StatBonuses::GetGivePetGroupTarget) + .def("GravityEffect", &Lua_StatBonuses::GetGravityEffect) + .def("haste", &Lua_StatBonuses::Gethaste) + .def("hastetype2", &Lua_StatBonuses::Gethastetype2) + .def("hastetype3", &Lua_StatBonuses::Gethastetype3) + .def("hatemod", &Lua_StatBonuses::Gethatemod) + .def("HeadShot", &Lua_StatBonuses::GetHeadShot) + .def("HealAmt", &Lua_StatBonuses::GetHealAmt) + .def("HealRate", &Lua_StatBonuses::GetHealRate) + .def("HeroicAGI", &Lua_StatBonuses::GetHeroicAGI) + .def("HeroicCHA", &Lua_StatBonuses::GetHeroicCHA) + .def("HeroicCorrup", &Lua_StatBonuses::GetHeroicCorrup) + .def("HeroicCR", &Lua_StatBonuses::GetHeroicCR) + .def("HeroicDEX", &Lua_StatBonuses::GetHeroicDEX) + .def("HeroicDR", &Lua_StatBonuses::GetHeroicDR) + .def("HeroicFR", &Lua_StatBonuses::GetHeroicFR) + .def("HeroicINT", &Lua_StatBonuses::GetHeroicINT) + .def("HeroicMR", &Lua_StatBonuses::GetHeroicMR) + .def("HeroicPR", &Lua_StatBonuses::GetHeroicPR) + .def("HeroicSTA", &Lua_StatBonuses::GetHeroicSTA) + .def("HeroicSTR", &Lua_StatBonuses::GetHeroicSTR) + .def("HeroicWIS", &Lua_StatBonuses::GetHeroicWIS) + .def("HitChance", &Lua_StatBonuses::GetHitChance) + .def("HitChanceEffect", &Lua_StatBonuses::GetHitChanceEffect) + .def("HP", &Lua_StatBonuses::GetHP) + .def("HPPercCap", &Lua_StatBonuses::GetHPPercCap) + .def("HPRegen", &Lua_StatBonuses::GetHPRegen) + .def("HPToManaConvert", &Lua_StatBonuses::GetHPToManaConvert) + .def("HSLevel", &Lua_StatBonuses::GetHSLevel) + .def("HundredHands", &Lua_StatBonuses::GetHundredHands) + .def("IllusionPersistence", &Lua_StatBonuses::GetIllusionPersistence) + .def("ImmuneToFlee", &Lua_StatBonuses::GetImmuneToFlee) + .def("ImprovedReclaimEnergy", &Lua_StatBonuses::GetImprovedReclaimEnergy) + .def("ImprovedTaunt", &Lua_StatBonuses::GetImprovedTaunt) + .def("IncreaseBlockChance", &Lua_StatBonuses::GetIncreaseBlockChance) + .def("IncreaseChanceMemwipe", &Lua_StatBonuses::GetIncreaseChanceMemwipe) + .def("IncreaseRunSpeedCap", &Lua_StatBonuses::GetIncreaseRunSpeedCap) + .def("inhibitmelee", &Lua_StatBonuses::Getinhibitmelee) + .def("INT", &Lua_StatBonuses::GetINT) + .def("INTCapMod", &Lua_StatBonuses::GetINTCapMod) + .def("IsBlind", &Lua_StatBonuses::GetIsBlind) + .def("IsFeared", &Lua_StatBonuses::GetIsFeared) + .def("ItemATKCap", &Lua_StatBonuses::GetItemATKCap) + .def("ItemHPRegenCap", &Lua_StatBonuses::GetItemHPRegenCap) + .def("ItemManaRegenCap", &Lua_StatBonuses::GetItemManaRegenCap) + .def("LimitToSkill", &Lua_StatBonuses::GetLimitToSkill) + .def("MagicWeapon", &Lua_StatBonuses::GetMagicWeapon) + .def("Mana", &Lua_StatBonuses::GetMana) + .def("ManaAbsorbPercentDamage", &Lua_StatBonuses::GetManaAbsorbPercentDamage) + .def("ManaPercCap", &Lua_StatBonuses::GetManaPercCap) + .def("ManaRegen", &Lua_StatBonuses::GetManaRegen) + .def("MasteryofPast", &Lua_StatBonuses::GetMasteryofPast) + .def("MaxBindWound", &Lua_StatBonuses::GetMaxBindWound) + .def("MaxHP", &Lua_StatBonuses::GetMaxHP) + .def("MaxHPChange", &Lua_StatBonuses::GetMaxHPChange) + .def("MeleeLifetap", &Lua_StatBonuses::GetMeleeLifetap) + .def("MeleeMitigation", &Lua_StatBonuses::GetMeleeMitigation) + .def("MeleeMitigationEffect", &Lua_StatBonuses::GetMeleeMitigationEffect) + .def("MeleeRune", &Lua_StatBonuses::GetMeleeRune) + .def("MeleeSkillCheck", &Lua_StatBonuses::GetMeleeSkillCheck) + .def("MeleeSkillCheckSkill", &Lua_StatBonuses::GetMeleeSkillCheckSkill) + .def("MeleeThresholdGuard", &Lua_StatBonuses::GetMeleeThresholdGuard) + .def("Metabolism", &Lua_StatBonuses::GetMetabolism) + .def("MinDamageModifier", &Lua_StatBonuses::GetMinDamageModifier) + .def("MitigateDotRune", &Lua_StatBonuses::GetMitigateDotRune) + .def("MitigateMeleeRune", &Lua_StatBonuses::GetMitigateMeleeRune) + .def("MitigateSpellRune", &Lua_StatBonuses::GetMitigateSpellRune) + .def("movementspeed", &Lua_StatBonuses::Getmovementspeed) + .def("MR", &Lua_StatBonuses::GetMR) + .def("MRCapMod", &Lua_StatBonuses::GetMRCapMod) + .def("NegateAttacks", &Lua_StatBonuses::GetNegateAttacks) + .def("NegateEffects", &Lua_StatBonuses::GetNegateEffects) + .def("NegateIfCombat", &Lua_StatBonuses::GetNegateIfCombat) + .def("NoBreakAESneak", &Lua_StatBonuses::GetNoBreakAESneak) + .def("OffhandRiposteFail", &Lua_StatBonuses::GetOffhandRiposteFail) + .def("Packrat", &Lua_StatBonuses::GetPackrat) + .def("ParryChance", &Lua_StatBonuses::GetParryChance) + .def("PC_Pet_Flurry", &Lua_StatBonuses::GetPC_Pet_Flurry) + .def("PC_Pet_Rampage", &Lua_StatBonuses::GetPC_Pet_Rampage) + .def("percussionMod", &Lua_StatBonuses::GetpercussionMod) + .def("PersistantCasting", &Lua_StatBonuses::GetPersistantCasting) + .def("PetAvoidance", &Lua_StatBonuses::GetPetAvoidance) + .def("PetCriticalHit", &Lua_StatBonuses::GetPetCriticalHit) + .def("PetFlurry", &Lua_StatBonuses::GetPetFlurry) + .def("PetMaxHP", &Lua_StatBonuses::GetPetMaxHP) + .def("PetMeleeMitigation", &Lua_StatBonuses::GetPetMeleeMitigation) + .def("PR", &Lua_StatBonuses::GetPR) + .def("PRCapMod", &Lua_StatBonuses::GetPRCapMod) + .def("ProcChance", &Lua_StatBonuses::GetProcChance) + .def("ProcChanceSPA", &Lua_StatBonuses::GetProcChanceSPA) + .def("RaiseSkillCap", &Lua_StatBonuses::GetRaiseSkillCap) + .def("ReduceFallDamage", &Lua_StatBonuses::GetReduceFallDamage) + .def("ReduceTradeskillFail", &Lua_StatBonuses::GetReduceTradeskillFail) + .def("reflect_chance", &Lua_StatBonuses::Getreflect_chance) + .def("ResistFearChance", &Lua_StatBonuses::GetResistFearChance) + .def("ResistSpellChance", &Lua_StatBonuses::GetResistSpellChance) + .def("ReverseDamageShield", &Lua_StatBonuses::GetReverseDamageShield) + .def("ReverseDamageShieldSpellID", &Lua_StatBonuses::GetReverseDamageShieldSpellID) + .def("ReverseDamageShieldType", &Lua_StatBonuses::GetReverseDamageShieldType) + .def("RiposteChance", &Lua_StatBonuses::GetRiposteChance) + .def("Root", &Lua_StatBonuses::GetRoot) + .def("RootBreakChance", &Lua_StatBonuses::GetRootBreakChance) + .def("SalvageChance", &Lua_StatBonuses::GetSalvageChance) + .def("Sanctuary", &Lua_StatBonuses::GetSanctuary) + .def("Screech", &Lua_StatBonuses::GetScreech) + .def("SecondaryDmgInc", &Lua_StatBonuses::GetSecondaryDmgInc) + .def("SeeInvis", &Lua_StatBonuses::GetSeeInvis) + .def("SEResist", &Lua_StatBonuses::GetSEResist) + .def("ShieldBlock", &Lua_StatBonuses::GetShieldBlock) + .def("ShieldEquipDmgMod", &Lua_StatBonuses::GetShieldEquipDmgMod) + .def("ShroudofStealth", &Lua_StatBonuses::GetShroudofStealth) + .def("singingMod", &Lua_StatBonuses::GetsingingMod) + .def("SkillAttackProc", &Lua_StatBonuses::GetSkillAttackProc) + .def("SkillDamageAmount", &Lua_StatBonuses::GetSkillDamageAmount) + .def("SkillDamageAmount2", &Lua_StatBonuses::GetSkillDamageAmount2) + .def("SkillDmgTaken", &Lua_StatBonuses::GetSkillDmgTaken) + .def("skillmod", &Lua_StatBonuses::Getskillmod) + .def("skillmodmax", &Lua_StatBonuses::Getskillmodmax) + .def("SkillProc", &Lua_StatBonuses::GetSkillProc) + .def("SkillProcSuccess", &Lua_StatBonuses::GetSkillProcSuccess) + .def("SkillReuseTime", &Lua_StatBonuses::GetSkillReuseTime) + .def("SlayUndead", &Lua_StatBonuses::GetSlayUndead) + .def("songModCap", &Lua_StatBonuses::GetsongModCap) + .def("SongRange", &Lua_StatBonuses::GetSongRange) + .def("SpellCritDmgIncNoStack", &Lua_StatBonuses::GetSpellCritDmgIncNoStack) + .def("SpellCritDmgIncrease", &Lua_StatBonuses::GetSpellCritDmgIncrease) + .def("SpellDamageShield", &Lua_StatBonuses::GetSpellDamageShield) + .def("SpellDmg", &Lua_StatBonuses::GetSpellDmg) + .def("SpellOnDeath", &Lua_StatBonuses::GetSpellOnDeath) + .def("SpellOnKill", &Lua_StatBonuses::GetSpellOnKill) + .def("SpellProcChance", &Lua_StatBonuses::GetSpellProcChance) + .def("SpellShield", &Lua_StatBonuses::GetSpellShield) + .def("SpellThresholdGuard", &Lua_StatBonuses::GetSpellThresholdGuard) + .def("SpellTriggers", &Lua_StatBonuses::GetSpellTriggers) + .def("STA", &Lua_StatBonuses::GetSTA) + .def("STACapMod", &Lua_StatBonuses::GetSTACapMod) + .def("STR", &Lua_StatBonuses::GetSTR) + .def("STRCapMod", &Lua_StatBonuses::GetSTRCapMod) + .def("StrikeThrough", &Lua_StatBonuses::GetStrikeThrough) + .def("stringedMod", &Lua_StatBonuses::GetstringedMod) + .def("StunBashChance", &Lua_StatBonuses::GetStunBashChance) + .def("StunResist", &Lua_StatBonuses::GetStunResist) + .def("TradeSkillMastery", &Lua_StatBonuses::GetTradeSkillMastery) + .def("TriggerMeleeThreshold", &Lua_StatBonuses::GetTriggerMeleeThreshold) + .def("TriggerOnValueAmount", &Lua_StatBonuses::GetTriggerOnValueAmount) + .def("TriggerSpellThreshold", &Lua_StatBonuses::GetTriggerSpellThreshold) + .def("TripleAttackChance", &Lua_StatBonuses::GetTripleAttackChance) + .def("TripleBackstab", &Lua_StatBonuses::GetTripleBackstab) + .def("TwoHandBluntBlock", &Lua_StatBonuses::GetTwoHandBluntBlock) + .def("UnfailingDivinity", &Lua_StatBonuses::GetUnfailingDivinity) + .def("Vampirism", &Lua_StatBonuses::GetVampirism) + .def("VoiceGraft", &Lua_StatBonuses::GetVoiceGraft) + .def("windMod", &Lua_StatBonuses::GetwindMod) + .def("WIS", &Lua_StatBonuses::GetWIS) + .def("WISCapMod", &Lua_StatBonuses::GetWISCapMod) + .def("XPRateMod", &Lua_StatBonuses::GetXPRateMod); } #endif From b983fac8608fda63bd471029c60ac9b4f4901e28 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 6 Nov 2021 17:36:06 -0400 Subject: [PATCH 325/624] [Quest API] Alphabetize Perl method exports. (#1672) - Keeps things tidier. Perl script was used to get this in order easily. ```pl my @perl_file_types = ( "bot", "client", "doors", "entity", "expedition", "groups", "hateentry", "inventory", "mob", "npc", "object", "perlpacket", "player_corpse", "questitem", "raids", "spell" ); foreach my $file_type (sort {$a cmp $b} @perl_file_types) { my $perl_file = "perl_$file_type.cpp"; open my $client_file, '<', $perl_file or die "Cannot open file_name $perl_file"; { local $/; $content = <$client_file>; } close $client_file; open my $perl_data_file, ">", "perl_$file_type\_data.cpp"; my @variables = (); foreach my $line (split("\n", $content)) { if ($line=~/newXSproto\(/i) { $line =~ s/\s+/ /g; my @line_data = split(/ /, $line); push(@variables, join(" ", @line_data)); } } foreach my $variable (sort {$a cmp $b} @variables) { $variable =~ s/^ //ig; print $perl_data_file "\t$variable\n"; } close $perl_data_file; }``` --- zone/perl_bot.cpp | 18 +- zone/perl_client.cpp | 104 ++---- zone/perl_doors.cpp | 65 ++-- zone/perl_entity.cpp | 164 ++++----- zone/perl_expedition.cpp | 20 -- zone/perl_groups.cpp | 58 +--- zone/perl_hateentry.cpp | 22 +- zone/perl_inventory.cpp | 27 -- zone/perl_mob.cpp | 664 +++++++++++++++++------------------- zone/perl_npc.cpp | 214 +++++------- zone/perl_object.cpp | 91 ++--- zone/perl_perlpacket.cpp | 68 ++-- zone/perl_player_corpse.cpp | 84 ++--- zone/perl_questitem.cpp | 32 +- zone/perl_raids.cpp | 58 +--- 15 files changed, 652 insertions(+), 1037 deletions(-) diff --git a/zone/perl_bot.cpp b/zone/perl_bot.cpp index d1a9e92a2..cb40bf013 100644 --- a/zone/perl_bot.cpp +++ b/zone/perl_bot.cpp @@ -1,18 +1,3 @@ -/* EQEMu: Everquest Server Emulator -Copyright (C) 2001-2004 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 -*/ - #ifdef BOTS #include "../common/features.h" #ifdef EMBPERL_XS_CLASSES @@ -75,10 +60,9 @@ XS(boot_Bot) fprintf(stderr, "boot_Bot does not take any arguments."); char buf[128]; + XS_VERSION_BOOTCHECK; - newXSproto(strcpy(buf, "GetOwner"), XS_Bot_GetOwner, file, "$"); - XSRETURN_YES; } diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 3d3aa96b3..5bceb1e5d 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -1,30 +1,3 @@ -/* -* This file was generated automatically by xsubpp version 1.9508 from the -* contents of tmp. Do not edit this file, edit tmp instead. -* -* ANY CHANGES MADE HERE WILL BE LOST! -* -*/ - - -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2004 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 -*/ - #include "../common/features.h" #ifdef EMBPERL_XS_CLASSES @@ -4429,7 +4402,7 @@ XS(XS_Client_PlayMP3); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_PlayMP3) { dXSARGS; if (items < 1 || items > 2) - Perl_croak(aTHX_ "Usage: Client::PlayMP3(THIS, string file_name)"); // @categories Script Utility + Perl_croak(aTHX_ "Usage: Client::PlayMP3(THIS, string file)"); // @categories Script Utility { Client *THIS; char *fname = nullptr; @@ -5943,10 +5916,7 @@ XS(boot_Client) { //add the strcpy stuff to get rid of const warnings.... - - XS_VERSION_BOOTCHECK; - newXSproto(strcpy(buf, "AccountID"), XS_Client_AccountID, file, "$"); newXSproto(strcpy(buf, "AccountName"), XS_Client_AccountName, file, "$"); newXSproto(strcpy(buf, "AddAAPoints"), XS_Client_AddAAPoints, file, "$$"); @@ -5975,32 +5945,34 @@ XS(boot_Client) { newXSproto(strcpy(buf, "CheckSpecializeIncrease"), XS_Client_CheckSpecializeIncrease, file, "$$"); newXSproto(strcpy(buf, "ClearCompassMark"), XS_Client_ClearCompassMark, file, "$"); newXSproto(strcpy(buf, "ClearZoneFlag"), XS_Client_ClearZoneFlag, file, "$$"); - newXSproto(strcpy(buf, "CreateExpedition"), XS_Client_CreateExpedition, file, "$$$$$$$;$"); - newXSproto(strcpy(buf, "CreateTaskDynamicZone"), XS_Client_CreateTaskDynamicZone, file, "$$"); newXSproto(strcpy(buf, "Connected"), XS_Client_Connected, file, "$"); newXSproto(strcpy(buf, "CountItem"), XS_Client_CountItem, file, "$$"); + newXSproto(strcpy(buf, "CreateExpedition"), XS_Client_CreateExpedition, file, "$$$$$$$;$"); + newXSproto(strcpy(buf, "CreateTaskDynamicZone"), XS_Client_CreateTaskDynamicZone, file, "$$"); newXSproto(strcpy(buf, "DecreaseByID"), XS_Client_DecreaseByID, file, "$$$"); newXSproto(strcpy(buf, "DeleteItemInInventory"), XS_Client_DeleteItemInInventory, file, "$$;$$"); - newXSproto(strcpy(buf, "Disconnect"), XS_Client_Disconnect, file, "$"); newXSproto(strcpy(buf, "DiaWind"), XS_Client_DiaWind, file, "$$"); newXSproto(strcpy(buf, "DialogueWindow"), XS_Client_DialogueWindow, file, "$$"); + newXSproto(strcpy(buf, "Disconnect"), XS_Client_Disconnect, file, "$"); newXSproto(strcpy(buf, "DropItem"), XS_Client_DropItem, file, "$$"); newXSproto(strcpy(buf, "Duck"), XS_Client_Duck, file, "$"); newXSproto(strcpy(buf, "DyeArmorBySlot"), XS_Client_DyeArmorBySlot, file, "$$$$$;$"); newXSproto(strcpy(buf, "Escape"), XS_Client_Escape, file, "$"); newXSproto(strcpy(buf, "ExpeditionMessage"), XS_Client_ExpeditionMessage, file, "$$$"); newXSproto(strcpy(buf, "FailTask"), XS_Client_FailTask, file, "$$"); + newXSproto(strcpy(buf, "FindMemmedSpellBySlot"), XS_Client_FindMemmedSpellBySlot, file, "$$"); newXSproto(strcpy(buf, "Fling"), XS_Client_Fling, file, "$$$$$;$$"); newXSproto(strcpy(buf, "ForageItem"), XS_Client_ForageItem, file, "$"); newXSproto(strcpy(buf, "Freeze"), XS_Client_Freeze, file, "$"); - newXSproto(strcpy(buf, "GetAAExp"), XS_Client_GetAAExp, file, "$"); + newXSproto(strcpy(buf, "GMKill"), XS_Client_GMKill, file, "$"); newXSproto(strcpy(buf, "GetAAEXPModifier"), XS_Client_GetAAEXPModifier, file, "$$"); + newXSproto(strcpy(buf, "GetAAExp"), XS_Client_GetAAExp, file, "$"); newXSproto(strcpy(buf, "GetAALevel"), XS_Client_GetAALevel, file, "$$"); newXSproto(strcpy(buf, "GetAAPercent"), XS_Client_GetAAPercent, file, "$"); newXSproto(strcpy(buf, "GetAAPoints"), XS_Client_GetAAPoints, file, "$$"); + newXSproto(strcpy(buf, "GetAFK"), XS_Client_GetAFK, file, "$"); newXSproto(strcpy(buf, "GetAccountAge"), XS_Client_GetAccountAge, file, "$"); newXSproto(strcpy(buf, "GetAccountFlag"), XS_Client_GetAccountFlag, file, "$$"); - newXSproto(strcpy(buf, "GetAFK"), XS_Client_GetAFK, file, "$"); newXSproto(strcpy(buf, "GetAggroCount"), XS_Client_GetAggroCount, file, "$"); newXSproto(strcpy(buf, "GetAllMoney"), XS_Client_GetAllMoney, file, "$"); newXSproto(strcpy(buf, "GetAlternateCurrencyValue"), XS_Client_GetAlternateCurrencyValue, file, "$$"); @@ -6031,40 +6003,41 @@ XS(boot_Client) { newXSproto(strcpy(buf, "GetCorpseID"), XS_Client_GetCorpseID, file, "$$"); newXSproto(strcpy(buf, "GetCorpseItemAt"), XS_Client_GetCorpseItemAt, file, "$$$"); newXSproto(strcpy(buf, "GetCustomItemData"), XS_Client_GetCustomItemData, file, "$$$"); - newXSproto(strcpy(buf, "GetDisciplineTimer"), XS_Client_GetDisciplineTimer, file, "$$"); newXSproto(strcpy(buf, "GetDiscSlotBySpellID"), XS_Client_GetDiscSlotBySpellID, file, "$$"); + newXSproto(strcpy(buf, "GetDisciplineTimer"), XS_Client_GetDisciplineTimer, file, "$$"); newXSproto(strcpy(buf, "GetDuelTarget"), XS_Client_GetDuelTarget, file, "$"); + newXSproto(strcpy(buf, "GetEXP"), XS_Client_GetEXP, file, "$"); + newXSproto(strcpy(buf, "GetEXPModifier"), XS_Client_GetEXPModifier, file, "$$"); newXSproto(strcpy(buf, "GetEbonCrystals"), XS_Client_GetEbonCrystals, file, "$"); newXSproto(strcpy(buf, "GetEndurance"), XS_Client_GetEndurance, file, "$"); newXSproto(strcpy(buf, "GetEnduranceRatio"), XS_Client_GetEnduranceRatio, file, "$"); - newXSproto(strcpy(buf, "GetEXP"), XS_Client_GetEXP, file, "$"); - newXSproto(strcpy(buf, "GetEXPModifier"), XS_Client_GetEXPModifier, file, "$$"); newXSproto(strcpy(buf, "GetExpedition"), XS_Client_GetExpedition, file, "$"); newXSproto(strcpy(buf, "GetExpeditionLockouts"), XS_Client_GetExpeditionLockouts, file, "$;$"); newXSproto(strcpy(buf, "GetFace"), XS_Client_GetFace, file, "$"); newXSproto(strcpy(buf, "GetFactionLevel"), XS_Client_GetFactionLevel, file, "$$$$$$$$"); newXSproto(strcpy(buf, "GetFeigned"), XS_Client_GetFeigned, file, "$"); + newXSproto(strcpy(buf, "GetFreeDisciplineSlot"), XS_Client_GetFreeDisciplineSlot, file, "$;$"); newXSproto(strcpy(buf, "GetFreeSpellBookSlot"), XS_Client_GetFreeSpellBookSlot, file, "$;$"); newXSproto(strcpy(buf, "GetGM"), XS_Client_GetGM, file, "$"); newXSproto(strcpy(buf, "GetGroup"), XS_Client_GetGroup, file, "$"); newXSproto(strcpy(buf, "GetGroupPoints"), XS_Client_GetGroupPoints, file, "$"); newXSproto(strcpy(buf, "GetHorseId"), XS_Client_GetHorseId, file, "$"); newXSproto(strcpy(buf, "GetHunger"), XS_Client_GetHunger, file, "$$"); - newXSproto(strcpy(buf, "GetInstanceID"), XS_Client_GetInstanceID, file, "$$"); - newXSproto(strcpy(buf, "GetInstrumentMod"), XS_Client_GetInstrumentMod, file, "$$"); - newXSproto(strcpy(buf, "GetInventory"), XS_Client_GetInventory, file, "$"); newXSproto(strcpy(buf, "GetIP"), XS_Client_GetIP, file, "$"); newXSproto(strcpy(buf, "GetIPExemption"), XS_Client_GetIPExemption, file, "$"); newXSproto(strcpy(buf, "GetIPString"), XS_Client_GetIPString, file, "$"); + newXSproto(strcpy(buf, "GetInstanceID"), XS_Client_GetInstanceID, file, "$$"); + newXSproto(strcpy(buf, "GetInstrumentMod"), XS_Client_GetInstrumentMod, file, "$$"); + newXSproto(strcpy(buf, "GetInventory"), XS_Client_GetInventory, file, "$"); newXSproto(strcpy(buf, "GetItemAt"), XS_Client_GetItemAt, file, "$$"); newXSproto(strcpy(buf, "GetItemIDAt"), XS_Client_GetItemIDAt, file, "$$"); newXSproto(strcpy(buf, "GetItemInInventory"), XS_Client_GetItemInInventory, file, "$$"); - newXSproto(strcpy(buf, "GetLanguageSkill"), XS_Client_GetLanguageSkill, file, "$$"); newXSproto(strcpy(buf, "GetLDoNLosses"), XS_Client_GetLDoNLosses, file, "$"); newXSproto(strcpy(buf, "GetLDoNLossesTheme"), XS_Client_GetLDoNLossesTheme, file, "$$"); newXSproto(strcpy(buf, "GetLDoNPointsTheme"), XS_Client_GetLDoNPointsTheme, file, "$"); newXSproto(strcpy(buf, "GetLDoNWins"), XS_Client_GetLDoNWins, file, "$"); newXSproto(strcpy(buf, "GetLDoNWinsTheme"), XS_Client_GetLDoNWinsTheme, file, "$$"); + newXSproto(strcpy(buf, "GetLanguageSkill"), XS_Client_GetLanguageSkill, file, "$$"); newXSproto(strcpy(buf, "GetLearnableDisciplines"), XS_Client_GetLearnableDisciplines, file, "$;$$"); newXSproto(strcpy(buf, "GetLearnedDisciplines"), XS_Client_GetLearnedDisciplines, file, "$"); newXSproto(strcpy(buf, "GetLockoutExpeditionUUID"), XS_Client_GetLockoutExpeditionUUID, file, "$$$"); @@ -6094,7 +6067,6 @@ XS(boot_Client) { newXSproto(strcpy(buf, "GetThirst"), XS_Client_GetThirst, file, "$$"); newXSproto(strcpy(buf, "GetTotalSecondsPlayed"), XS_Client_GetTotalSecondsPlayed, file, "$"); newXSproto(strcpy(buf, "GetWeight"), XS_Client_GetWeight, file, "$"); - newXSproto(strcpy(buf, "GMKill"), XS_Client_GMKill, file, "$"); newXSproto(strcpy(buf, "GoFish"), XS_Client_GoFish, file, "$"); newXSproto(strcpy(buf, "GrantAlternateAdvancementAbility"), XS_Client_GrantAlternateAdvancementAbility, file, "$$$;$"); newXSproto(strcpy(buf, "GuildID"), XS_Client_GuildID, file, "$"); @@ -6105,49 +6077,52 @@ XS(boot_Client) { newXSproto(strcpy(buf, "HasSpellScribed"), XS_Client_HasSkill, file, "$$"); newXSproto(strcpy(buf, "HasZoneFlag"), XS_Client_HasZoneFlag, file, "$$"); newXSproto(strcpy(buf, "Hungry"), XS_Client_Hungry, file, "$"); + newXSproto(strcpy(buf, "InZone"), XS_Client_InZone, file, "$"); + newXSproto(strcpy(buf, "IncStats"), XS_Client_IncStats, file, "$$$"); newXSproto(strcpy(buf, "IncreaseLanguageSkill"), XS_Client_IncreaseLanguageSkill, file, "$$;$"); newXSproto(strcpy(buf, "IncreaseSkill"), XS_Client_IncreaseSkill, file, "$$;$"); newXSproto(strcpy(buf, "IncrementAA"), XS_Client_IncrementAA, file, "$$"); - newXSproto(strcpy(buf, "IncStats"), XS_Client_IncStats, file, "$$$"); - newXSproto(strcpy(buf, "InZone"), XS_Client_InZone, file, "$"); newXSproto(strcpy(buf, "IsBecomeNPC"), XS_Client_IsBecomeNPC, file, "$"); + newXSproto(strcpy(buf, "IsCrouching"), XS_Client_IsCrouching, file, "$"); newXSproto(strcpy(buf, "IsDueling"), XS_Client_IsDueling, file, "$"); newXSproto(strcpy(buf, "IsGrouped"), XS_Client_IsGrouped, file, "$"); newXSproto(strcpy(buf, "IsLD"), XS_Client_IsLD, file, "$"); newXSproto(strcpy(buf, "IsMedding"), XS_Client_IsMedding, file, "$"); newXSproto(strcpy(buf, "IsRaidGrouped"), XS_Client_IsRaidGrouped, file, "$"); - newXSproto(strcpy(buf, "IsStanding"), XS_Client_IsStanding, file, "$"); newXSproto(strcpy(buf, "IsSitting"), XS_Client_IsSitting, file, "$"); - newXSproto(strcpy(buf, "IsCrouching"), XS_Client_IsCrouching, file, "$"); + newXSproto(strcpy(buf, "IsStanding"), XS_Client_IsStanding, file, "$"); newXSproto(strcpy(buf, "IsTaskActive"), XS_Client_IsTaskActive, file, "$$"); newXSproto(strcpy(buf, "IsTaskActivityActive"), XS_Client_IsTaskActivityActive, file, "$$$"); newXSproto(strcpy(buf, "IsTaskCompleted"), XS_Client_IsTaskCompleted, file, "$$"); newXSproto(strcpy(buf, "KeyRingAdd"), XS_Client_KeyRingAdd, file, "$$"); newXSproto(strcpy(buf, "KeyRingCheck"), XS_Client_KeyRingCheck, file, "$$"); newXSproto(strcpy(buf, "Kick"), XS_Client_Kick, file, "$"); + newXSproto(strcpy(buf, "LearnDisciplines"), XS_Client_LearnDisciplines, file, "$$$"); newXSproto(strcpy(buf, "LearnRecipe"), XS_Client_LearnRecipe, file, "$$"); newXSproto(strcpy(buf, "LeaveGroup"), XS_Client_LeaveGroup, file, "$"); newXSproto(strcpy(buf, "LoadZoneFlags"), XS_Client_LoadZoneFlags, file, "$"); newXSproto(strcpy(buf, "MarkCompassLoc"), XS_Client_MarkCompassLoc, file, "$$$$"); newXSproto(strcpy(buf, "MaxSkill"), XS_Client_MaxSkill, file, "$$;$$"); newXSproto(strcpy(buf, "MemSpell"), XS_Client_MemSpell, file, "$$$;$"); + newXSproto(strcpy(buf, "MemmedCount"), XS_Client_MemmedCount, file, "$"); newXSproto(strcpy(buf, "MovePC"), XS_Client_MovePC, file, "$$$$$$"); newXSproto(strcpy(buf, "MovePCDynamicZone"), XS_Client_MovePCDynamicZone, file, "$$;$$"); newXSproto(strcpy(buf, "MovePCInstance"), XS_Client_MovePCInstance, file, "$$$$$$$"); newXSproto(strcpy(buf, "MoveZone"), XS_Client_MoveZone, file, "$$"); newXSproto(strcpy(buf, "MoveZoneGroup"), XS_Client_MoveZoneGroup, file, "$$"); - newXSproto(strcpy(buf, "MoveZoneRaid"), XS_Client_MoveZoneRaid, file, "$$"); newXSproto(strcpy(buf, "MoveZoneInstance"), XS_Client_MoveZoneInstance, file, "$$"); newXSproto(strcpy(buf, "MoveZoneInstanceGroup"), XS_Client_MoveZoneInstanceGroup, file, "$$"); newXSproto(strcpy(buf, "MoveZoneInstanceRaid"), XS_Client_MoveZoneInstanceRaid, file, "$$"); + newXSproto(strcpy(buf, "MoveZoneRaid"), XS_Client_MoveZoneRaid, file, "$$"); newXSproto(strcpy(buf, "NPCSpawn"), XS_Client_NPCSpawn, file, "$$$;$"); + newXSproto(strcpy(buf, "NotifyNewTitlesAvailable"), XS_Client_NotifyNewTitlesAvailable, file, "$"); newXSproto(strcpy(buf, "NukeItem"), XS_Client_NukeItem, file, "$$;$"); newXSproto(strcpy(buf, "OpenLFGuildWindow"), XS_Client_OpenLFGuildWindow, file, "$"); - newXSproto(strcpy(buf, "NotifyNewTitlesAvailable"), XS_Client_NotifyNewTitlesAvailable, file, "$"); newXSproto(strcpy(buf, "PlayMP3"), XS_Client_PlayMP3, file, "$;$"); newXSproto(strcpy(buf, "Popup2"), XS_Client_Popup2, file, "$$$;$$$$$$$"); newXSproto(strcpy(buf, "QuestReward"), XS_Client_QuestReward, file, "$$;$$$$$$$"); newXSproto(strcpy(buf, "ReadBook"), XS_Client_ReadBook, file, "$$$"); + newXSproto(strcpy(buf, "ReadBookByName"), XS_Client_ReadBookByName, file, "$$$"); newXSproto(strcpy(buf, "RefundAA"), XS_Client_RefundAA, file, "$$"); newXSproto(strcpy(buf, "RemoveAllExpeditionLockouts"), XS_Client_RemoveAllExpeditionLockouts, file, "$;$"); newXSproto(strcpy(buf, "RemoveExpeditionLockout"), XS_Client_RemoveExpeditionLockout, file, "$$$"); @@ -6162,51 +6137,53 @@ XS(boot_Client) { newXSproto(strcpy(buf, "Save"), XS_Client_Save, file, "$$"); newXSproto(strcpy(buf, "SaveBackup"), XS_Client_SaveBackup, file, "$"); newXSproto(strcpy(buf, "ScribeSpell"), XS_Client_ScribeSpell, file, "$$$;$"); + newXSproto(strcpy(buf, "ScribeSpells"), XS_Client_ScribeSpells, file, "$$$"); newXSproto(strcpy(buf, "SendColoredText"), XS_Client_SendColoredText, file, "$$$"); newXSproto(strcpy(buf, "SendMarqueeMessage"), XS_Client_SendMarqueeMessage, file, "$$$$$$$"); newXSproto(strcpy(buf, "SendOPTranslocateConfirm"), XS_Client_SendOPTranslocateConfirm, file, "$$$"); newXSproto(strcpy(buf, "SendSound"), XS_Client_SendSound, file, "$"); newXSproto(strcpy(buf, "SendSpellAnim"), XS_Client_SendSpellAnim, file, "$$$"); newXSproto(strcpy(buf, "SendTargetCommand"), XS_Client_SendTargetCommand, file, "$$"); - newXSproto(strcpy(buf, "SendToInstance"), XS_Client_SendToInstance, file, "$$$$$$$$$$"); newXSproto(strcpy(buf, "SendToGuildHall"), XS_Client_SendToGuildHall, file, "$"); + newXSproto(strcpy(buf, "SendToInstance"), XS_Client_SendToInstance, file, "$$$$$$$$$$"); newXSproto(strcpy(buf, "SendWebLink"), XS_Client_SendWebLink, file, "$:$"); newXSproto(strcpy(buf, "SendZoneFlagInfo"), XS_Client_SendZoneFlagInfo, file, "$$"); newXSproto(strcpy(buf, "SetAAEXPModifier"), XS_Client_SetAAEXPModifier, file, "$$$"); newXSproto(strcpy(buf, "SetAAPoints"), XS_Client_SetAAPoints, file, "$$"); newXSproto(strcpy(buf, "SetAATitle"), XS_Client_SetAATitle, file, "$$;$"); - newXSproto(strcpy(buf, "SetAccountFlag"), XS_Client_SetAccountFlag, file, "$$"); newXSproto(strcpy(buf, "SetAFK"), XS_Client_SetAFK, file, "$$"); - newXSproto(strcpy(buf, "SetAnon"), XS_Client_SetAnon, file, "$$"); + newXSproto(strcpy(buf, "SetAccountFlag"), XS_Client_SetAccountFlag, file, "$$"); newXSproto(strcpy(buf, "SetAlternateCurrencyValue"), XS_Client_SetAlternateCurrencyValue, file, "$$$"); + newXSproto(strcpy(buf, "SetAnon"), XS_Client_SetAnon, file, "$$"); newXSproto(strcpy(buf, "SetBaseClass"), XS_Client_SetBaseClass, file, "$$"); newXSproto(strcpy(buf, "SetBaseGender"), XS_Client_SetBaseGender, file, "$$"); newXSproto(strcpy(buf, "SetBaseRace"), XS_Client_SetBaseRace, file, "$$"); newXSproto(strcpy(buf, "SetBecomeNPC"), XS_Client_SetBecomeNPC, file, "$$"); newXSproto(strcpy(buf, "SetBecomeNPCLevel"), XS_Client_SetBecomeNPCLevel, file, "$$"); newXSproto(strcpy(buf, "SetBindPoint"), XS_Client_SetBindPoint, file, "$;$$$$$$"); - newXSproto(strcpy(buf, "SetConsumption"), XS_Client_SetConsumption, file, "$$$"); newXSproto(strcpy(buf, "SetClientMaxLevel"), XS_Client_SetClientMaxLevel, file, "$$"); + newXSproto(strcpy(buf, "SetConsumption"), XS_Client_SetConsumption, file, "$$$"); newXSproto(strcpy(buf, "SetCustomItemData"), XS_Client_SetCustomItemData, file, "$$$$"); newXSproto(strcpy(buf, "SetDeity"), XS_Client_SetDeity, file, "$$"); - newXSproto(strcpy(buf, "SetDueling"), XS_Client_SetDueling, file, "$$"); newXSproto(strcpy(buf, "SetDuelTarget"), XS_Client_SetDuelTarget, file, "$$"); - newXSproto(strcpy(buf, "SetEbonCrystals"), XS_Client_SetEbonCrystals, file, "$$"); - newXSproto(strcpy(buf, "SetEndurance"), XS_Client_SetEndurance, file, "$$"); + newXSproto(strcpy(buf, "SetDueling"), XS_Client_SetDueling, file, "$$"); newXSproto(strcpy(buf, "SetEXP"), XS_Client_SetEXP, file, "$$$;$"); newXSproto(strcpy(buf, "SetEXPModifier"), XS_Client_SetEXPModifier, file, "$$$"); + newXSproto(strcpy(buf, "SetEbonCrystals"), XS_Client_SetEbonCrystals, file, "$$"); + newXSproto(strcpy(buf, "SetEndurance"), XS_Client_SetEndurance, file, "$$"); newXSproto(strcpy(buf, "SetFactionLevel"), XS_Client_SetFactionLevel, file, "$$$$$$"); newXSproto(strcpy(buf, "SetFactionLevel2"), XS_Client_SetFactionLevel2, file, "$$$$$$$"); newXSproto(strcpy(buf, "SetFeigned"), XS_Client_SetFeigned, file, "$$"); newXSproto(strcpy(buf, "SetGM"), XS_Client_SetGM, file, "$$"); + newXSproto(strcpy(buf, "SetGMStatus"), XS_Client_SetGMStatus, file, "$$"); newXSproto(strcpy(buf, "SetHideMe"), XS_Client_SetHideMe, file, "$$"); newXSproto(strcpy(buf, "SetHorseId"), XS_Client_SetHorseId, file, "$$"); newXSproto(strcpy(buf, "SetHunger"), XS_Client_SetHunger, file, "$$"); newXSproto(strcpy(buf, "SetIPExemption"), XS_Client_SetIPExemption, file, "$$"); newXSproto(strcpy(buf, "SetLanguageSkill"), XS_Client_SetLanguageSkill, file, "$$$"); newXSproto(strcpy(buf, "SetMaterial"), XS_Client_SetMaterial, file, "$$$"); - newXSproto(strcpy(buf, "SetPrimaryWeaponOrnamentation"), XS_Client_SetPrimaryWeaponOrnamentation, file, "$$"); newXSproto(strcpy(buf, "SetPVP"), XS_Client_SetPVP, file, "$$"); + newXSproto(strcpy(buf, "SetPrimaryWeaponOrnamentation"), XS_Client_SetPrimaryWeaponOrnamentation, file, "$$"); newXSproto(strcpy(buf, "SetRadiantCrystals"), XS_Client_SetRadiantCrystals, file, "$$"); newXSproto(strcpy(buf, "SetSecondaryWeaponOrnamentation"), XS_Client_SetSecondaryWeaponOrnamentation, file, "$$"); newXSproto(strcpy(buf, "SetSkill"), XS_Client_SetSkill, file, "$$$"); @@ -6223,34 +6200,27 @@ XS(boot_Client) { newXSproto(strcpy(buf, "Stand"), XS_Client_Stand, file, "$"); newXSproto(strcpy(buf, "SummonBaggedItems"), XS_Client_SummonBaggedItems, file, "$$$"); newXSproto(strcpy(buf, "SummonItem"), XS_Client_SummonItem, file, "$$;$$$$$$$$"); - newXSproto(strcpy(buf, "TakeMoneyFromPP"), XS_Client_TakeMoneyFromPP, file, "$$;$"); newXSproto(strcpy(buf, "TGB"), XS_Client_TGB, file, "$"); + newXSproto(strcpy(buf, "TakeMoneyFromPP"), XS_Client_TakeMoneyFromPP, file, "$$;$"); newXSproto(strcpy(buf, "Thirsty"), XS_Client_Thirsty, file, "$"); newXSproto(strcpy(buf, "TrainDiscBySpellID"), XS_Client_TrainDiscBySpellID, file, "$$"); - newXSproto(strcpy(buf, "Undye"), XS_Client_Undye, file, "$"); newXSproto(strcpy(buf, "UnFreeze"), XS_Client_UnFreeze, file, "$"); + newXSproto(strcpy(buf, "Undye"), XS_Client_Undye, file, "$"); newXSproto(strcpy(buf, "UnmemSpell"), XS_Client_UnmemSpell, file, "$$;$"); newXSproto(strcpy(buf, "UnmemSpellAll"), XS_Client_UnmemSpellAll, file, "$;$"); newXSproto(strcpy(buf, "UnmemSpellBySpellID"), XS_Client_UnmemSpellBySpellID, file, "$$"); - newXSproto(strcpy(buf, "FindMemmedSpellBySlot"), XS_Client_FindMemmedSpellBySlot, file, "$$"); - newXSproto(strcpy(buf, "MemmedCount"), XS_Client_MemmedCount, file, "$"); newXSproto(strcpy(buf, "UnscribeSpell"), XS_Client_UnscribeSpell, file, "$$;$"); newXSproto(strcpy(buf, "UnscribeSpellAll"), XS_Client_UnscribeSpellAll, file, "$;$"); newXSproto(strcpy(buf, "UntrainDisc"), XS_Client_UntrainDisc, file, "$$;$"); newXSproto(strcpy(buf, "UntrainDiscAll"), XS_Client_UntrainDiscAll, file, "$;$"); newXSproto(strcpy(buf, "UntrainDiscBySpellID"), XS_Client_UntrainDiscBySpellID, file, "$$;$"); newXSproto(strcpy(buf, "UpdateAdmin"), XS_Client_UpdateAdmin, file, "$;$"); - newXSproto(strcpy(buf, "SetGMStatus"), XS_Client_SetGMStatus, file, "$$"); newXSproto(strcpy(buf, "UpdateGroupAAs"), XS_Client_UpdateGroupAAs, file, "$$$"); newXSproto(strcpy(buf, "UpdateLDoNPoints"), XS_Client_UpdateLDoNPoints, file, "$$$"); newXSproto(strcpy(buf, "UpdateTaskActivity"), XS_Client_UpdateTaskActivity, file, "$$$$;$"); newXSproto(strcpy(buf, "UpdateWho"), XS_Client_UpdateWho, file, "$;$"); newXSproto(strcpy(buf, "UseDiscipline"), XS_Client_UseDiscipline, file, "$$$"); newXSproto(strcpy(buf, "WorldKick"), XS_Client_WorldKick, file, "$"); - newXSproto(strcpy(buf, "ReadBookByName"), XS_Client_ReadBookByName, file, "$$$"); - newXSproto(strcpy(buf, "GetFreeDisciplineSlot"), XS_Client_GetFreeDisciplineSlot, file, "$;$"); - newXSproto(strcpy(buf, "ScribeSpells"), XS_Client_ScribeSpells, file, "$$$"); - newXSproto(strcpy(buf, "LearnDisciplines"), XS_Client_LearnDisciplines, file, "$$$"); XSRETURN_YES; } diff --git a/zone/perl_doors.cpp b/zone/perl_doors.cpp index b922b5091..b92774edf 100644 --- a/zone/perl_doors.cpp +++ b/zone/perl_doors.cpp @@ -1,30 +1,3 @@ -/* -* This file was generated automatically by xsubpp version 1.9508 from the -* contents of tmp. Do not edit this file, edit tmp instead. -* -* ANY CHANGES MADE HERE WILL BE LOST! -* -*/ - - -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2004 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 -*/ - #include "../common/features.h" #ifdef EMBPERL_XS_CLASSES @@ -502,33 +475,33 @@ XS(boot_Doors) { //add the strcpy stuff to get rid of const warnings.... XS_VERSION_BOOTCHECK; + newXSproto(strcpy(buf, "CreateDatabaseEntry"), XS_Doors_CreateDatabaseEntry, file, "$"); + newXSproto(strcpy(buf, "GetDoorDBID"), XS_Doors_GetDoorDBID, file, "$"); + newXSproto(strcpy(buf, "GetDoorID"), XS_Doors_GetDoorID, file, "$"); + newXSproto(strcpy(buf, "GetHeading"), XS_Doors_GetHeading, file, "$"); newXSproto(strcpy(buf, "GetID"), XS_Doors_GetID, file, "$"); - newXSproto(strcpy(buf, "SetModelName"), XS_Doors_SetModelName, file, "$$"); + newXSproto(strcpy(buf, "GetIncline"), XS_Doors_GetIncline, file, "$"); + newXSproto(strcpy(buf, "GetKeyItem"), XS_Doors_GetKeyItem, file, "$"); + newXSproto(strcpy(buf, "GetLockPick"), XS_Doors_GetLockpick, file, "$"); newXSproto(strcpy(buf, "GetModelName"), XS_Doors_GetModelName, file, "$"); + newXSproto(strcpy(buf, "GetNoKeyring"), XS_Doors_GetNoKeyring, file, "$"); + newXSproto(strcpy(buf, "GetOpenType"), XS_Doors_GetOpenType, file, "$"); + newXSproto(strcpy(buf, "GetSize"), XS_Doors_GetSize, file, "$"); newXSproto(strcpy(buf, "GetX"), XS_Doors_GetX, file, "$"); newXSproto(strcpy(buf, "GetY"), XS_Doors_GetY, file, "$"); newXSproto(strcpy(buf, "GetZ"), XS_Doors_GetZ, file, "$"); - newXSproto(strcpy(buf, "GetHeading"), XS_Doors_GetHeading, file, "$"); + newXSproto(strcpy(buf, "SetHeading"), XS_Doors_SetHeading, file, "$$"); + newXSproto(strcpy(buf, "SetIncline"), XS_Doors_SetIncline, file, "$$"); + newXSproto(strcpy(buf, "SetKeyItem"), XS_Doors_SetKeyItem, file, "$$"); + newXSproto(strcpy(buf, "SetLocation"), XS_Doors_SetLocation, file, "$$$$"); + newXSproto(strcpy(buf, "SetLockPick"), XS_Doors_SetLockpick, file, "$$"); + newXSproto(strcpy(buf, "SetModelName"), XS_Doors_SetModelName, file, "$$"); + newXSproto(strcpy(buf, "SetNoKeyring"), XS_Doors_SetNoKeyring, file, "$$"); + newXSproto(strcpy(buf, "SetOpenType"), XS_Doors_SetOpenType, file, "$$"); + newXSproto(strcpy(buf, "SetSize"), XS_Doors_SetSize, file, "$$"); newXSproto(strcpy(buf, "SetX"), XS_Doors_SetX, file, "$$"); newXSproto(strcpy(buf, "SetY"), XS_Doors_SetY, file, "$$"); newXSproto(strcpy(buf, "SetZ"), XS_Doors_SetZ, file, "$$"); - newXSproto(strcpy(buf, "SetHeading"), XS_Doors_SetHeading, file, "$$"); - newXSproto(strcpy(buf, "SetLocation"), XS_Doors_SetLocation, file, "$$$$"); - newXSproto(strcpy(buf, "GetDoorDBID"), XS_Doors_GetDoorDBID, file, "$"); - newXSproto(strcpy(buf, "GetDoorID"), XS_Doors_GetDoorID, file, "$"); - newXSproto(strcpy(buf, "SetSize"), XS_Doors_SetSize, file, "$$"); - newXSproto(strcpy(buf, "GetSize"), XS_Doors_GetSize, file, "$"); - newXSproto(strcpy(buf, "SetIncline"), XS_Doors_SetIncline, file, "$$"); - newXSproto(strcpy(buf, "GetIncline"), XS_Doors_GetIncline, file, "$"); - newXSproto(strcpy(buf, "SetOpenType"), XS_Doors_SetOpenType, file, "$$"); - newXSproto(strcpy(buf, "GetOpenType"), XS_Doors_GetOpenType, file, "$"); - newXSproto(strcpy(buf, "SetLockPick"), XS_Doors_SetLockpick, file, "$$"); - newXSproto(strcpy(buf, "GetLockPick"), XS_Doors_GetLockpick, file, "$"); - newXSproto(strcpy(buf, "SetKeyItem"), XS_Doors_SetKeyItem, file, "$$"); - newXSproto(strcpy(buf, "GetKeyItem"), XS_Doors_GetKeyItem, file, "$"); - newXSproto(strcpy(buf, "SetNoKeyring"), XS_Doors_SetNoKeyring, file, "$$"); - newXSproto(strcpy(buf, "GetNoKeyring"), XS_Doors_GetNoKeyring, file, "$"); - newXSproto(strcpy(buf, "CreateDatabaseEntry"), XS_Doors_CreateDatabaseEntry, file, "$"); XSRETURN_YES; } #endif //EMBPERL_XS_CLASSES diff --git a/zone/perl_entity.cpp b/zone/perl_entity.cpp index d45190d32..835929ef9 100644 --- a/zone/perl_entity.cpp +++ b/zone/perl_entity.cpp @@ -1,30 +1,3 @@ -/* -* This file was generated automatically by xsubpp version 1.9508 from the -* contents of tmp. Do not edit this file, edit tmp instead. -* -* ANY CHANGES MADE HERE WILL BE LOST! -* -*/ - - -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2004 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 -*/ - #include "../common/features.h" #ifdef EMBPERL_XS_CLASSES @@ -1498,88 +1471,85 @@ XS(boot_EntityList) { //add the strcpy stuff to get rid of const warnings.... - - XS_VERSION_BOOTCHECK; - - newXSproto(strcpy(buf, "GetMobID"), XS_EntityList_GetMobID, file, "$$"); - newXSproto(strcpy(buf, "GetMob"), XS_EntityList_GetMob, file, "$$"); - newXSproto(strcpy(buf, "GetMobByID"), XS_EntityList_GetMobByID, file, "$$"); - newXSproto(strcpy(buf, "GetMobByNpcTypeID"), XS_EntityList_GetMobByNpcTypeID, file, "$$"); - newXSproto(strcpy(buf, "IsMobSpawnedByNpcTypeID"), XS_EntityList_IsMobSpawnedByNpcTypeID, file, "$$"); - newXSproto(strcpy(buf, "GetNPCByID"), XS_EntityList_GetNPCByID, file, "$$"); - newXSproto(strcpy(buf, "GetNPCByNPCTypeID"), XS_EntityList_GetNPCByNPCTypeID, file, "$$"); - newXSproto(strcpy(buf, "GetNPCBySpawnID"), XS_EntityList_GetNPCBySpawnID, file, "$$"); - newXSproto(strcpy(buf, "GetClientByName"), XS_EntityList_GetClientByName, file, "$$"); - newXSproto(strcpy(buf, "GetClientByAccID"), XS_EntityList_GetClientByAccID, file, "$$"); - newXSproto(strcpy(buf, "GetClientByID"), XS_EntityList_GetClientByID, file, "$$"); - newXSproto(strcpy(buf, "GetClientByCharID"), XS_EntityList_GetClientByCharID, file, "$$"); - newXSproto(strcpy(buf, "GetClientByWID"), XS_EntityList_GetClientByWID, file, "$$"); - newXSproto(strcpy(buf, "GetObjectByID"), XS_EntityList_GetObjectByID, file, "$"); - newXSproto(strcpy(buf, "GetObjectByDBID"), XS_EntityList_GetObjectByDBID, file, "$"); - newXSproto(strcpy(buf, "GetDoorsByID"), XS_EntityList_GetDoorsByID, file, "$$"); - newXSproto(strcpy(buf, "GetDoorsByDBID"), XS_EntityList_GetDoorsByDBID, file, "$$"); - newXSproto(strcpy(buf, "GetDoorsByDoorID"), XS_EntityList_GetDoorsByDoorID, file, "$$"); - newXSproto(strcpy(buf, "FindDoor"), XS_EntityList_FindDoor, file, "$$"); - newXSproto(strcpy(buf, "GetGroupByMob"), XS_EntityList_GetGroupByMob, file, "$$"); - newXSproto(strcpy(buf, "GetGroupByClient"), XS_EntityList_GetGroupByClient, file, "$$"); - newXSproto(strcpy(buf, "GetGroupByID"), XS_EntityList_GetGroupByID, file, "$$"); - newXSproto(strcpy(buf, "GetGroupByLeaderName"), XS_EntityList_GetGroupByLeaderName, file, "$$"); - newXSproto(strcpy(buf, "GetRaidByID"), XS_EntityList_GetRaidByID, file, "$$"); - newXSproto(strcpy(buf, "GetRaidByClient"), XS_EntityList_GetRaidByClient, file, "$$"); - newXSproto(strcpy(buf, "GetCorpseByOwner"), XS_EntityList_GetCorpseByOwner, file, "$$"); - newXSproto(strcpy(buf, "GetCorpseByID"), XS_EntityList_GetCorpseByID, file, "$$"); - newXSproto(strcpy(buf, "GetCorpseByName"), XS_EntityList_GetCorpseByName, file, "$$"); - newXSproto(strcpy(buf, "ClearClientPetitionQueue"), XS_EntityList_ClearClientPetitionQueue, file, "$"); newXSproto(strcpy(buf, "CanAddHateForMob"), XS_EntityList_CanAddHateForMob, file, "$$"); newXSproto(strcpy(buf, "Clear"), XS_EntityList_Clear, file, "$"); - newXSproto(strcpy(buf, "RemoveMob"), XS_EntityList_RemoveMob, file, "$$"); - newXSproto(strcpy(buf, "RemoveClient"), XS_EntityList_RemoveClient, file, "$$"); - newXSproto(strcpy(buf, "RemoveNPC"), XS_EntityList_RemoveNPC, file, "$$"); - newXSproto(strcpy(buf, "RemoveGroup"), XS_EntityList_RemoveGroup, file, "$$"); - newXSproto(strcpy(buf, "RemoveCorpse"), XS_EntityList_RemoveCorpse, file, "$$"); - newXSproto(strcpy(buf, "RemoveDoor"), XS_EntityList_RemoveDoor, file, "$$"); - newXSproto(strcpy(buf, "RemoveTrap"), XS_EntityList_RemoveTrap, file, "$$"); - newXSproto(strcpy(buf, "RemoveObject"), XS_EntityList_RemoveObject, file, "$$"); - newXSproto(strcpy(buf, "RemoveAllMobs"), XS_EntityList_RemoveAllMobs, file, "$"); - newXSproto(strcpy(buf, "RemoveAllClients"), XS_EntityList_RemoveAllClients, file, "$"); - newXSproto(strcpy(buf, "RemoveAllNPCs"), XS_EntityList_RemoveAllNPCs, file, "$"); - newXSproto(strcpy(buf, "RemoveAllGroups"), XS_EntityList_RemoveAllGroups, file, "$"); - newXSproto(strcpy(buf, "RemoveAllCorpses"), XS_EntityList_RemoveAllCorpses, file, "$"); - newXSproto(strcpy(buf, "RemoveAllDoors"), XS_EntityList_RemoveAllDoors, file, "$"); - newXSproto(strcpy(buf, "RemoveAllTraps"), XS_EntityList_RemoveAllTraps, file, "$"); - newXSproto(strcpy(buf, "RemoveAllObjects"), XS_EntityList_RemoveAllObjects, file, "$"); - newXSproto(strcpy(buf, "Message"), XS_EntityList_Message, file, "$$$$;@"); - newXSproto(strcpy(buf, "MessageStatus"), XS_EntityList_MessageStatus, file, "$$$$$;@"); - newXSproto(strcpy(buf, "MessageClose"), XS_EntityList_MessageClose, file, "$$$$$$;@"); - newXSproto(strcpy(buf, "RemoveFromTargets"), XS_EntityList_RemoveFromTargets, file, "$$"); - newXSproto(strcpy(buf, "ReplaceWithTarget"), XS_EntityList_ReplaceWithTarget, file, "$$$"); - newXSproto(strcpy(buf, "OpenDoorsNear"), XS_EntityList_OpenDoorsNear, file, "$$"); - newXSproto(strcpy(buf, "MakeNameUnique"), XS_EntityList_MakeNameUnique, file, "$$"); - newXSproto(strcpy(buf, "RemoveNumbers"), XS_EntityList_RemoveNumbers, file, "$$"); - newXSproto(strcpy(buf, "SignalMobsByNPCID"), XS_EntityList_SignalMobsByNPCID, file, "$$$"); - newXSproto(strcpy(buf, "RemoveEntity"), XS_EntityList_RemoveEntity, file, "$$"); + newXSproto(strcpy(buf, "ClearClientPetitionQueue"), XS_EntityList_ClearClientPetitionQueue, file, "$"); + newXSproto(strcpy(buf, "ClearFeignAggro"), XS_EntityList_ClearFeignAggro, file, "$$"); newXSproto(strcpy(buf, "DeleteNPCCorpses"), XS_EntityList_DeleteNPCCorpses, file, "$"); newXSproto(strcpy(buf, "DeletePlayerCorpses"), XS_EntityList_DeletePlayerCorpses, file, "$"); - newXSproto(strcpy(buf, "HalveAggro"), XS_EntityList_HalveAggro, file, "$$"); newXSproto(strcpy(buf, "DoubleAggro"), XS_EntityList_DoubleAggro, file, "$$"); - newXSproto(strcpy(buf, "ClearFeignAggro"), XS_EntityList_ClearFeignAggro, file, "$$"); newXSproto(strcpy(buf, "Fighting"), XS_EntityList_Fighting, file, "$$"); - newXSproto(strcpy(buf, "RemoveFromHateLists"), XS_EntityList_RemoveFromHateLists, file, "$$;$"); - newXSproto(strcpy(buf, "MessageGroup"), XS_EntityList_MessageGroup, file, "$$$$$;@"); - newXSproto(strcpy(buf, "GetRandomClient"), XS_EntityList_GetRandomClient, file, "$$$$$;$"); - newXSproto(strcpy(buf, "GetMobList"), XS_EntityList_GetMobList, file, "$"); - newXSproto(strcpy(buf, "GetClientList"), XS_EntityList_GetClientList, file, "$"); - newXSproto(strcpy(buf, "GetNPCList"), XS_EntityList_GetNPCList, file, "$"); - newXSproto(strcpy(buf, "GetCorpseList"), XS_EntityList_GetCorpseList, file, "$"); - newXSproto(strcpy(buf, "GetObjectList"), XS_EntityList_GetObjectList, file, "$"); - newXSproto(strcpy(buf, "GetDoorsList"), XS_EntityList_GetDoorsList, file, "$"); - newXSproto(strcpy(buf, "SignalAllClients"), XS_EntityList_SignalAllClients, file, "$$"); + newXSproto(strcpy(buf, "FindDoor"), XS_EntityList_FindDoor, file, "$$"); #ifdef BOTS newXSproto(strcpy(buf, "GetBotByID"), XS_EntityList_GetBotByID, file, "$$"); newXSproto(strcpy(buf, "GetBotByName"), XS_EntityList_GetBotByName, file, "$$"); newXSproto(strcpy(buf, "GetBotList"), XS_EntityList_GetBotList, file, "$"); #endif + newXSproto(strcpy(buf, "GetClientByAccID"), XS_EntityList_GetClientByAccID, file, "$$"); + newXSproto(strcpy(buf, "GetClientByCharID"), XS_EntityList_GetClientByCharID, file, "$$"); + newXSproto(strcpy(buf, "GetClientByID"), XS_EntityList_GetClientByID, file, "$$"); + newXSproto(strcpy(buf, "GetClientByName"), XS_EntityList_GetClientByName, file, "$$"); + newXSproto(strcpy(buf, "GetClientByWID"), XS_EntityList_GetClientByWID, file, "$$"); + newXSproto(strcpy(buf, "GetClientList"), XS_EntityList_GetClientList, file, "$"); + newXSproto(strcpy(buf, "GetCorpseByID"), XS_EntityList_GetCorpseByID, file, "$$"); + newXSproto(strcpy(buf, "GetCorpseByName"), XS_EntityList_GetCorpseByName, file, "$$"); + newXSproto(strcpy(buf, "GetCorpseByOwner"), XS_EntityList_GetCorpseByOwner, file, "$$"); + newXSproto(strcpy(buf, "GetCorpseList"), XS_EntityList_GetCorpseList, file, "$"); + newXSproto(strcpy(buf, "GetDoorsByDBID"), XS_EntityList_GetDoorsByDBID, file, "$$"); + newXSproto(strcpy(buf, "GetDoorsByDoorID"), XS_EntityList_GetDoorsByDoorID, file, "$$"); + newXSproto(strcpy(buf, "GetDoorsByID"), XS_EntityList_GetDoorsByID, file, "$$"); + newXSproto(strcpy(buf, "GetDoorsList"), XS_EntityList_GetDoorsList, file, "$"); + newXSproto(strcpy(buf, "GetGroupByClient"), XS_EntityList_GetGroupByClient, file, "$$"); + newXSproto(strcpy(buf, "GetGroupByID"), XS_EntityList_GetGroupByID, file, "$$"); + newXSproto(strcpy(buf, "GetGroupByLeaderName"), XS_EntityList_GetGroupByLeaderName, file, "$$"); + newXSproto(strcpy(buf, "GetGroupByMob"), XS_EntityList_GetGroupByMob, file, "$$"); + newXSproto(strcpy(buf, "GetMob"), XS_EntityList_GetMob, file, "$$"); + newXSproto(strcpy(buf, "GetMobByID"), XS_EntityList_GetMobByID, file, "$$"); + newXSproto(strcpy(buf, "GetMobByNpcTypeID"), XS_EntityList_GetMobByNpcTypeID, file, "$$"); + newXSproto(strcpy(buf, "GetMobID"), XS_EntityList_GetMobID, file, "$$"); + newXSproto(strcpy(buf, "GetMobList"), XS_EntityList_GetMobList, file, "$"); + newXSproto(strcpy(buf, "GetNPCByID"), XS_EntityList_GetNPCByID, file, "$$"); + newXSproto(strcpy(buf, "GetNPCByNPCTypeID"), XS_EntityList_GetNPCByNPCTypeID, file, "$$"); + newXSproto(strcpy(buf, "GetNPCBySpawnID"), XS_EntityList_GetNPCBySpawnID, file, "$$"); + newXSproto(strcpy(buf, "GetNPCList"), XS_EntityList_GetNPCList, file, "$"); + newXSproto(strcpy(buf, "GetObjectByDBID"), XS_EntityList_GetObjectByDBID, file, "$"); + newXSproto(strcpy(buf, "GetObjectByID"), XS_EntityList_GetObjectByID, file, "$"); + newXSproto(strcpy(buf, "GetObjectList"), XS_EntityList_GetObjectList, file, "$"); + newXSproto(strcpy(buf, "GetRaidByClient"), XS_EntityList_GetRaidByClient, file, "$$"); + newXSproto(strcpy(buf, "GetRaidByID"), XS_EntityList_GetRaidByID, file, "$$"); + newXSproto(strcpy(buf, "GetRandomClient"), XS_EntityList_GetRandomClient, file, "$$$$$;$"); + newXSproto(strcpy(buf, "HalveAggro"), XS_EntityList_HalveAggro, file, "$$"); + newXSproto(strcpy(buf, "IsMobSpawnedByNpcTypeID"), XS_EntityList_IsMobSpawnedByNpcTypeID, file, "$$"); + newXSproto(strcpy(buf, "MakeNameUnique"), XS_EntityList_MakeNameUnique, file, "$$"); + newXSproto(strcpy(buf, "Message"), XS_EntityList_Message, file, "$$$$;@"); + newXSproto(strcpy(buf, "MessageClose"), XS_EntityList_MessageClose, file, "$$$$$$;@"); + newXSproto(strcpy(buf, "MessageGroup"), XS_EntityList_MessageGroup, file, "$$$$$;@"); + newXSproto(strcpy(buf, "MessageStatus"), XS_EntityList_MessageStatus, file, "$$$$$;@"); + newXSproto(strcpy(buf, "OpenDoorsNear"), XS_EntityList_OpenDoorsNear, file, "$$"); + newXSproto(strcpy(buf, "RemoveAllClients"), XS_EntityList_RemoveAllClients, file, "$"); + newXSproto(strcpy(buf, "RemoveAllCorpses"), XS_EntityList_RemoveAllCorpses, file, "$"); + newXSproto(strcpy(buf, "RemoveAllDoors"), XS_EntityList_RemoveAllDoors, file, "$"); + newXSproto(strcpy(buf, "RemoveAllGroups"), XS_EntityList_RemoveAllGroups, file, "$"); + newXSproto(strcpy(buf, "RemoveAllMobs"), XS_EntityList_RemoveAllMobs, file, "$"); + newXSproto(strcpy(buf, "RemoveAllNPCs"), XS_EntityList_RemoveAllNPCs, file, "$"); + newXSproto(strcpy(buf, "RemoveAllObjects"), XS_EntityList_RemoveAllObjects, file, "$"); + newXSproto(strcpy(buf, "RemoveAllTraps"), XS_EntityList_RemoveAllTraps, file, "$"); + newXSproto(strcpy(buf, "RemoveClient"), XS_EntityList_RemoveClient, file, "$$"); + newXSproto(strcpy(buf, "RemoveCorpse"), XS_EntityList_RemoveCorpse, file, "$$"); + newXSproto(strcpy(buf, "RemoveDoor"), XS_EntityList_RemoveDoor, file, "$$"); + newXSproto(strcpy(buf, "RemoveEntity"), XS_EntityList_RemoveEntity, file, "$$"); + newXSproto(strcpy(buf, "RemoveFromHateLists"), XS_EntityList_RemoveFromHateLists, file, "$$;$"); + newXSproto(strcpy(buf, "RemoveFromTargets"), XS_EntityList_RemoveFromTargets, file, "$$"); + newXSproto(strcpy(buf, "RemoveGroup"), XS_EntityList_RemoveGroup, file, "$$"); + newXSproto(strcpy(buf, "RemoveMob"), XS_EntityList_RemoveMob, file, "$$"); + newXSproto(strcpy(buf, "RemoveNPC"), XS_EntityList_RemoveNPC, file, "$$"); + newXSproto(strcpy(buf, "RemoveNumbers"), XS_EntityList_RemoveNumbers, file, "$$"); + newXSproto(strcpy(buf, "RemoveObject"), XS_EntityList_RemoveObject, file, "$$"); + newXSproto(strcpy(buf, "RemoveTrap"), XS_EntityList_RemoveTrap, file, "$$"); + newXSproto(strcpy(buf, "ReplaceWithTarget"), XS_EntityList_ReplaceWithTarget, file, "$$$"); + newXSproto(strcpy(buf, "SignalAllClients"), XS_EntityList_SignalAllClients, file, "$$"); + newXSproto(strcpy(buf, "SignalMobsByNPCID"), XS_EntityList_SignalMobsByNPCID, file, "$$$"); XSRETURN_YES; } diff --git a/zone/perl_expedition.cpp b/zone/perl_expedition.cpp index 2ac7689fa..69cf7ac95 100644 --- a/zone/perl_expedition.cpp +++ b/zone/perl_expedition.cpp @@ -1,23 +1,3 @@ -/** - * EQEmulator: Everquest Server Emulator - * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) - * - * 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 - * - */ - #include "../common/features.h" #ifdef EMBPERL_XS_CLASSES diff --git a/zone/perl_groups.cpp b/zone/perl_groups.cpp index 70c321a21..d40f9578b 100644 --- a/zone/perl_groups.cpp +++ b/zone/perl_groups.cpp @@ -1,30 +1,3 @@ -/* -* This file was generated automatically by xsubpp version 1.9508 from the -* contents of tmp. Do not edit this file, edit tmp instead. -* -* ANY CHANGES MADE HERE WILL BE LOST! -* -*/ - - -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2004 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 -*/ - #include "../common/features.h" #ifdef EMBPERL_XS_CLASSES @@ -479,26 +452,25 @@ XS(boot_Group) { //add the strcpy stuff to get rid of const warnings.... XS_VERSION_BOOTCHECK; - - newXSproto(strcpy(buf, "DisbandGroup"), XS_Group_DisbandGroup, file, "$"); - newXSproto(strcpy(buf, "IsGroupMember"), XS_Group_IsGroupMember, file, "$$"); newXSproto(strcpy(buf, "CastGroupSpell"), XS_Group_CastGroupSpell, file, "$$$"); - newXSproto(strcpy(buf, "SplitExp"), XS_Group_SplitExp, file, "$$$"); - newXSproto(strcpy(buf, "GroupMessage"), XS_Group_GroupMessage, file, "$$$"); - newXSproto(strcpy(buf, "GetTotalGroupDamage"), XS_Group_GetTotalGroupDamage, file, "$$"); - newXSproto(strcpy(buf, "SplitMoney"), XS_Group_SplitMoney, file, "$$$$$"); - newXSproto(strcpy(buf, "SetLeader"), XS_Group_SetLeader, file, "$$"); + newXSproto(strcpy(buf, "DisbandGroup"), XS_Group_DisbandGroup, file, "$"); + newXSproto(strcpy(buf, "DoesAnyMemberHaveExpeditionLockout"), XS_Group_DoesAnyMemberHaveExpeditionLockout, file, "$$$;$"); + newXSproto(strcpy(buf, "GetHighestLevel"), XS_Group_GetHighestLevel, file, "$"); + newXSproto(strcpy(buf, "GetID"), XS_Group_GetID, file, "$"); newXSproto(strcpy(buf, "GetLeader"), XS_Group_GetLeader, file, "$"); newXSproto(strcpy(buf, "GetLeaderName"), XS_Group_GetLeaderName, file, "$"); - newXSproto(strcpy(buf, "SendHPPacketsTo"), XS_Group_SendHPPacketsTo, file, "$$"); - newXSproto(strcpy(buf, "SendHPPacketsFrom"), XS_Group_SendHPPacketsFrom, file, "$$"); - newXSproto(strcpy(buf, "IsLeader"), XS_Group_IsLeader, file, "$$"); - newXSproto(strcpy(buf, "GroupCount"), XS_Group_GroupCount, file, "$"); - newXSproto(strcpy(buf, "GetHighestLevel"), XS_Group_GetHighestLevel, file, "$"); - newXSproto(strcpy(buf, "TeleportGroup"), XS_Group_TeleportGroup, file, "$$$$$$$"); - newXSproto(strcpy(buf, "GetID"), XS_Group_GetID, file, "$"); newXSproto(strcpy(buf, "GetMember"), XS_Group_GetMember, file, "$$"); - newXSproto(strcpy(buf, "DoesAnyMemberHaveExpeditionLockout"), XS_Group_DoesAnyMemberHaveExpeditionLockout, file, "$$$;$"); + newXSproto(strcpy(buf, "GetTotalGroupDamage"), XS_Group_GetTotalGroupDamage, file, "$$"); + newXSproto(strcpy(buf, "GroupCount"), XS_Group_GroupCount, file, "$"); + newXSproto(strcpy(buf, "GroupMessage"), XS_Group_GroupMessage, file, "$$$"); + newXSproto(strcpy(buf, "IsGroupMember"), XS_Group_IsGroupMember, file, "$$"); + newXSproto(strcpy(buf, "IsLeader"), XS_Group_IsLeader, file, "$$"); + newXSproto(strcpy(buf, "SendHPPacketsFrom"), XS_Group_SendHPPacketsFrom, file, "$$"); + newXSproto(strcpy(buf, "SendHPPacketsTo"), XS_Group_SendHPPacketsTo, file, "$$"); + newXSproto(strcpy(buf, "SetLeader"), XS_Group_SetLeader, file, "$$"); + newXSproto(strcpy(buf, "SplitExp"), XS_Group_SplitExp, file, "$$$"); + newXSproto(strcpy(buf, "SplitMoney"), XS_Group_SplitMoney, file, "$$$$$"); + newXSproto(strcpy(buf, "TeleportGroup"), XS_Group_TeleportGroup, file, "$$$$$$$"); XSRETURN_YES; } diff --git a/zone/perl_hateentry.cpp b/zone/perl_hateentry.cpp index cd98648fa..291a4e3df 100644 --- a/zone/perl_hateentry.cpp +++ b/zone/perl_hateentry.cpp @@ -1,21 +1,3 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2009 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 -*/ - #include "../common/features.h" #include "client.h" @@ -116,11 +98,9 @@ XS(boot_HateEntry) { //add the strcpy stuff to get rid of const warnings.... XS_VERSION_BOOTCHECK; - - newXSproto(strcpy(buf, "GetEnt"), XS_HateEntry_GetEnt, file, "$"); newXSproto(strcpy(buf, "GetDamage"), XS_HateEntry_GetDamage, file, "$"); + newXSproto(strcpy(buf, "GetEnt"), XS_HateEntry_GetEnt, file, "$"); newXSproto(strcpy(buf, "GetHate"), XS_HateEntry_GetHate, file, "$"); - XSRETURN_YES; } diff --git a/zone/perl_inventory.cpp b/zone/perl_inventory.cpp index 56452873c..c2ea6679c 100644 --- a/zone/perl_inventory.cpp +++ b/zone/perl_inventory.cpp @@ -1,30 +1,3 @@ -/* -* This file was generated automatically by xsubpp version 1.9508 from the -* contents of tmp. Do not edit this file, edit tmp instead. -* -* ANY CHANGES MADE HERE WILL BE LOST! -* -*/ - - -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2004 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 -*/ - #include "../common/features.h" #include "client.h" diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index f207b47b8..2e64ec65f 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -1,30 +1,3 @@ -/* -* This file was generated automatically by xsubpp version 1.9508 from the -* contents of tmp. Do not edit this file, edit tmp instead. -* -* ANY CHANGES MADE HERE WILL BE LOST! -* -*/ - - -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2004 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 -*/ - #include "../common/features.h" #ifdef EMBPERL_XS_CLASSES @@ -6441,348 +6414,349 @@ XS(boot_Mob) { //add the strcpy stuff to get rid of const warnings.... XS_VERSION_BOOTCHECK; - - newXSproto(strcpy(buf, "IsClient"), XS_Mob_IsClient, file, "$"); - newXSproto(strcpy(buf, "IsNPC"), XS_Mob_IsNPC, file, "$"); - newXSproto(strcpy(buf, "IsBot"), XS_Mob_IsBot, file, "$"); - newXSproto(strcpy(buf, "IsMob"), XS_Mob_IsMob, file, "$"); - newXSproto(strcpy(buf, "IsCorpse"), XS_Mob_IsCorpse, file, "$"); - newXSproto(strcpy(buf, "IsPlayerCorpse"), XS_Mob_IsPlayerCorpse, file, "$"); - newXSproto(strcpy(buf, "IsNPCCorpse"), XS_Mob_IsNPCCorpse, file, "$"); - newXSproto(strcpy(buf, "IsObject"), XS_Mob_IsObject, file, "$"); - newXSproto(strcpy(buf, "IsDoor"), XS_Mob_IsDoor, file, "$"); - newXSproto(strcpy(buf, "IsTrap"), XS_Mob_IsTrap, file, "$"); - newXSproto(strcpy(buf, "IsBeacon"), XS_Mob_IsBeacon, file, "$"); - newXSproto(strcpy(buf, "IsHorse"), XS_Mob_IsHorse, file, "$"); - newXSproto(strcpy(buf, "CastToClient"), XS_Mob_CastToClient, file, "$"); - newXSproto(strcpy(buf, "CastToNPC"), XS_Mob_CastToNPC, file, "$"); - newXSproto(strcpy(buf, "CastToMob"), XS_Mob_CastToMob, file, "$"); - newXSproto(strcpy(buf, "CastToCorpse"), XS_Mob_CastToCorpse, file, "$"); - newXSproto(strcpy(buf, "GetID"), XS_Mob_GetID, file, "$"); - newXSproto(strcpy(buf, "GetName"), XS_Mob_GetName, file, "$"); - newXSproto(strcpy(buf, "Depop"), XS_Mob_Depop, file, "$;$"); - newXSproto(strcpy(buf, "RogueAssassinate"), XS_Mob_RogueAssassinate, file, "$$"); - newXSproto(strcpy(buf, "BehindMob"), XS_Mob_BehindMob, file, "$;$$$"); - newXSproto(strcpy(buf, "SetLevel"), XS_Mob_SetLevel, file, "$$;$"); - newXSproto(strcpy(buf, "GetSkill"), XS_Mob_GetSkill, file, "$$"); - newXSproto(strcpy(buf, "SendWearChange"), XS_Mob_SendWearChange, file, "$$"); - newXSproto(strcpy(buf, "GetEquipment"), XS_Mob_GetEquipment, file, "$$"); - newXSproto(strcpy(buf, "GetEquipmentMaterial"), XS_Mob_GetEquipmentMaterial, file, "$$"); - newXSproto(strcpy(buf, "GetEquipmentColor"), XS_Mob_GetEquipmentColor, file, "$$"); - newXSproto(strcpy(buf, "GetArmorTint"), XS_Mob_GetArmorTint, file, "$$"); - newXSproto(strcpy(buf, "IsMoving"), XS_Mob_IsMoving, file, "$"); - newXSproto(strcpy(buf, "GoToBind"), XS_Mob_GoToBind, file, "$"); - newXSproto(strcpy(buf, "Gate"), XS_Mob_Gate, file, "$"); + newXSproto(strcpy(buf, "AddFeignMemory"), XS_Mob_AddFeignMemory, file, "$$"); + newXSproto(strcpy(buf, "AddNimbusEffect"), XS_Mob_AddNimbusEffect, file, "$$"); + newXSproto(strcpy(buf, "AddToHateList"), XS_Mob_AddToHateList, file, "$$;$$$$$"); newXSproto(strcpy(buf, "Attack"), XS_Mob_Attack, file, "$$;$$"); - newXSproto(strcpy(buf, "Damage"), XS_Mob_Damage, file, "$$$$$;$$$"); - newXSproto(strcpy(buf, "RangedAttack"), XS_Mob_RangedAttack, file, "$$"); - newXSproto(strcpy(buf, "ThrowingAttack"), XS_Mob_ThrowingAttack, file, "$$"); - newXSproto(strcpy(buf, "Heal"), XS_Mob_Heal, file, "$"); - newXSproto(strcpy(buf, "HealDamage"), XS_Mob_HealDamage, file, "$$;$"); - newXSproto(strcpy(buf, "SetMaxHP"), XS_Mob_SetMaxHP, file, "$"); - newXSproto(strcpy(buf, "GetLevelCon"), XS_Mob_GetLevelCon, file, "$$"); - newXSproto(strcpy(buf, "SetHP"), XS_Mob_SetHP, file, "$$"); - newXSproto(strcpy(buf, "DoAnim"), XS_Mob_DoAnim, file, "$$;$"); - newXSproto(strcpy(buf, "ChangeSize"), XS_Mob_ChangeSize, file, "$$;$"); - newXSproto(strcpy(buf, "RandomizeFeatures"), XS_Mob_RandomizeFeatures, file, "$$;$"); - newXSproto(strcpy(buf, "GMMove"), XS_Mob_GMMove, file, "$$$$;$"); - newXSproto(strcpy(buf, "HasProcs"), XS_Mob_HasProcs, file, "$"); - newXSproto(strcpy(buf, "IsInvisible"), XS_Mob_IsInvisible, file, "$;$"); - newXSproto(strcpy(buf, "SetInvisible"), XS_Mob_SetInvisible, file, "$$"); - newXSproto(strcpy(buf, "FindBuff"), XS_Mob_FindBuff, file, "$$"); - newXSproto(strcpy(buf, "FindBuffBySlot"), XS_Mob_FindBuffBySlot, file, "$$"); + newXSproto(strcpy(buf, "BehindMob"), XS_Mob_BehindMob, file, "$;$$$"); newXSproto(strcpy(buf, "BuffCount"), XS_Mob_BuffCount, file, "$"); - newXSproto(strcpy(buf, "FindType"), XS_Mob_FindType, file, "$$;$$"); - newXSproto(strcpy(buf, "GetBuffSlotFromType"), XS_Mob_GetBuffSlotFromType, file, "$$"); - newXSproto(strcpy(buf, "MakePet"), XS_Mob_MakePet, file, "$$$;$"); - newXSproto(strcpy(buf, "GetBaseRace"), XS_Mob_GetBaseRace, file, "$"); - newXSproto(strcpy(buf, "GetBaseGender"), XS_Mob_GetBaseGender, file, "$"); - newXSproto(strcpy(buf, "GetDeity"), XS_Mob_GetDeity, file, "$"); - newXSproto(strcpy(buf, "GetRace"), XS_Mob_GetRace, file, "$"); - newXSproto(strcpy(buf, "GetRaceName"), XS_Mob_GetRaceName, file, "$"); - newXSproto(strcpy(buf, "GetGender"), XS_Mob_GetGender, file, "$"); - newXSproto(strcpy(buf, "GetTexture"), XS_Mob_GetTexture, file, "$"); - newXSproto(strcpy(buf, "GetHelmTexture"), XS_Mob_GetHelmTexture, file, "$"); - newXSproto(strcpy(buf, "GetHairColor"), XS_Mob_GetHairColor, file, "$"); - newXSproto(strcpy(buf, "GetBeardColor"), XS_Mob_GetBeardColor, file, "$"); - newXSproto(strcpy(buf, "GetEyeColor1"), XS_Mob_GetEyeColor1, file, "$"); - newXSproto(strcpy(buf, "GetEyeColor2"), XS_Mob_GetEyeColor2, file, "$"); - newXSproto(strcpy(buf, "GetHairStyle"), XS_Mob_GetHairStyle, file, "$"); - newXSproto(strcpy(buf, "GetLuclinFace"), XS_Mob_GetLuclinFace, file, "$"); - newXSproto(strcpy(buf, "GetBeard"), XS_Mob_GetBeard, file, "$"); - newXSproto(strcpy(buf, "GetDrakkinHeritage"), XS_Mob_GetDrakkinHeritage, file, "$"); - newXSproto(strcpy(buf, "GetDrakkinTattoo"), XS_Mob_GetDrakkinTattoo, file, "$"); - newXSproto(strcpy(buf, "GetDrakkinDetails"), XS_Mob_GetDrakkinDetails, file, "$"); - newXSproto(strcpy(buf, "GetClass"), XS_Mob_GetClass, file, "$"); - newXSproto(strcpy(buf, "GetClassName"), XS_Mob_GetClassName, file, "$"); - newXSproto(strcpy(buf, "GetLevel"), XS_Mob_GetLevel, file, "$"); - newXSproto(strcpy(buf, "GetCleanName"), XS_Mob_GetCleanName, file, "$"); - newXSproto(strcpy(buf, "GetTarget"), XS_Mob_GetTarget, file, "$"); - newXSproto(strcpy(buf, "SetTarget"), XS_Mob_SetTarget, file, "$$"); - newXSproto(strcpy(buf, "GetHPRatio"), XS_Mob_GetHPRatio, file, "$"); - newXSproto(strcpy(buf, "IsWarriorClass"), XS_Mob_IsWarriorClass, file, "$"); - newXSproto(strcpy(buf, "GetHP"), XS_Mob_GetHP, file, "$"); - newXSproto(strcpy(buf, "GetMaxHP"), XS_Mob_GetMaxHP, file, "$"); - newXSproto(strcpy(buf, "GetItemHPBonuses"), XS_Mob_GetItemHPBonuses, file, "$"); - newXSproto(strcpy(buf, "GetSpellHPBonuses"), XS_Mob_GetSpellHPBonuses, file, "$"); - newXSproto(strcpy(buf, "GetSpellIDFromSlot"), XS_Mob_GetSpellIDFromSlot, file, "$$"); - newXSproto(strcpy(buf, "GetWalkspeed"), XS_Mob_GetWalkspeed, file, "$"); - newXSproto(strcpy(buf, "GetRunspeed"), XS_Mob_GetRunspeed, file, "$"); - newXSproto(strcpy(buf, "GetCasterLevel"), XS_Mob_GetCasterLevel, file, "$$"); - newXSproto(strcpy(buf, "GetMaxMana"), XS_Mob_GetMaxMana, file, "$"); - newXSproto(strcpy(buf, "GetMana"), XS_Mob_GetMana, file, "$"); - newXSproto(strcpy(buf, "SetMana"), XS_Mob_SetMana, file, "$$"); - newXSproto(strcpy(buf, "GetManaRatio"), XS_Mob_GetManaRatio, file, "$"); - newXSproto(strcpy(buf, "GetAC"), XS_Mob_GetAC, file, "$"); - newXSproto(strcpy(buf, "GetDisplayAC"), XS_Mob_GetDisplayAC, file, "$"); - newXSproto(strcpy(buf, "GetATK"), XS_Mob_GetATK, file, "$"); - newXSproto(strcpy(buf, "GetSTR"), XS_Mob_GetSTR, file, "$"); - newXSproto(strcpy(buf, "GetSTA"), XS_Mob_GetSTA, file, "$"); - newXSproto(strcpy(buf, "GetDEX"), XS_Mob_GetDEX, file, "$"); - newXSproto(strcpy(buf, "GetAGI"), XS_Mob_GetAGI, file, "$"); - newXSproto(strcpy(buf, "GetINT"), XS_Mob_GetINT, file, "$"); - newXSproto(strcpy(buf, "GetWIS"), XS_Mob_GetWIS, file, "$"); - newXSproto(strcpy(buf, "GetCHA"), XS_Mob_GetCHA, file, "$"); - newXSproto(strcpy(buf, "GetMR"), XS_Mob_GetMR, file, "$"); - newXSproto(strcpy(buf, "GetFR"), XS_Mob_GetFR, file, "$"); - newXSproto(strcpy(buf, "GetDR"), XS_Mob_GetDR, file, "$"); - newXSproto(strcpy(buf, "GetPR"), XS_Mob_GetPR, file, "$"); - newXSproto(strcpy(buf, "GetCR"), XS_Mob_GetCR, file, "$"); - newXSproto(strcpy(buf, "GetCorruption"), XS_Mob_GetCorruption, file, "$"); - newXSproto(strcpy(buf, "GetPhR"), XS_Mob_GetPhR, file, "$"); - newXSproto(strcpy(buf, "GetMaxSTR"), XS_Mob_GetMaxSTR, file, "$"); - newXSproto(strcpy(buf, "GetMaxSTA"), XS_Mob_GetMaxSTA, file, "$"); - newXSproto(strcpy(buf, "GetMaxDEX"), XS_Mob_GetMaxDEX, file, "$"); - newXSproto(strcpy(buf, "GetMaxAGI"), XS_Mob_GetMaxAGI, file, "$"); - newXSproto(strcpy(buf, "GetMaxINT"), XS_Mob_GetMaxINT, file, "$"); - newXSproto(strcpy(buf, "GetMaxWIS"), XS_Mob_GetMaxWIS, file, "$"); - newXSproto(strcpy(buf, "GetMaxCHA"), XS_Mob_GetMaxCHA, file, "$"); - newXSproto(strcpy(buf, "GetActSpellRange"), XS_Mob_GetActSpellRange, file, "$$$"); - newXSproto(strcpy(buf, "GetActSpellDamage"), XS_Mob_GetActSpellDamage, file, "$$$"); - newXSproto(strcpy(buf, "GetActSpellHealing"), XS_Mob_GetActSpellHealing, file, "$$$"); - newXSproto(strcpy(buf, "GetActSpellCost"), XS_Mob_GetActSpellCost, file, "$$$"); - newXSproto(strcpy(buf, "GetActSpellDuration"), XS_Mob_GetActSpellDuration, file, "$$$"); - newXSproto(strcpy(buf, "GetActSpellCasttime"), XS_Mob_GetActSpellCasttime, file, "$$$"); - newXSproto(strcpy(buf, "ResistSpell"), XS_Mob_ResistSpell, file, "$$$$"); - newXSproto(strcpy(buf, "GetSpecializeSkillValue"), XS_Mob_GetSpecializeSkillValue, file, "$$"); - newXSproto(strcpy(buf, "GetNPCTypeID"), XS_Mob_GetNPCTypeID, file, "$"); - newXSproto(strcpy(buf, "IsTargeted"), XS_Mob_IsTargeted, file, "$"); - newXSproto(strcpy(buf, "GetX"), XS_Mob_GetX, file, "$"); - newXSproto(strcpy(buf, "GetY"), XS_Mob_GetY, file, "$"); - newXSproto(strcpy(buf, "GetZ"), XS_Mob_GetZ, file, "$"); - newXSproto(strcpy(buf, "GetHeading"), XS_Mob_GetHeading, file, "$"); - newXSproto(strcpy(buf, "GetWaypointX"), XS_Mob_GetWaypointX, file, "$"); - newXSproto(strcpy(buf, "GetWaypointY"), XS_Mob_GetWaypointY, file, "$"); - newXSproto(strcpy(buf, "GetWaypointZ"), XS_Mob_GetWaypointZ, file, "$"); - newXSproto(strcpy(buf, "GetWaypointH"), XS_Mob_GetWaypointH, file, "$"); - newXSproto(strcpy(buf, "GetWaypointPause"), XS_Mob_GetWaypointPause, file, "$"); - newXSproto(strcpy(buf, "GetWaypointID"), XS_Mob_GetWaypointID, file, "$"); - newXSproto(strcpy(buf, "SetCurrentWP"), XS_Mob_SetCurrentWP, file, "$$"); - newXSproto(strcpy(buf, "GetSize"), XS_Mob_GetSize, file, "$"); - newXSproto(strcpy(buf, "SetFollowID"), XS_Mob_SetFollowID, file, "$$"); - newXSproto(strcpy(buf, "GetFollowID"), XS_Mob_GetFollowID, file, "$"); - newXSproto(strcpy(buf, "Message"), XS_Mob_Message, file, "$$$;@"); - newXSproto(strcpy(buf, "Message_StringID"), XS_Mob_Message_StringID, file, "$$$;$"); - newXSproto(strcpy(buf, "Say"), XS_Mob_Say, file, "$$;@"); - newXSproto(strcpy(buf, "Shout"), XS_Mob_Shout, file, "$$;@"); - newXSproto(strcpy(buf, "Emote"), XS_Mob_Emote, file, "$$;@"); - newXSproto(strcpy(buf, "InterruptSpell"), XS_Mob_InterruptSpell, file, "$;$"); - newXSproto(strcpy(buf, "CastSpell"), XS_Mob_CastSpell, file, "$$$;$$$"); - newXSproto(strcpy(buf, "SpellFinished"), XS_Mob_SpellFinished, file, "$$;$$"); - newXSproto(strcpy(buf, "IsImmuneToSpell"), XS_Mob_IsImmuneToSpell, file, "$$$"); - newXSproto(strcpy(buf, "BuffFadeBySpellID"), XS_Mob_BuffFadeBySpellID, file, "$$"); - newXSproto(strcpy(buf, "BuffFadeByEffect"), XS_Mob_BuffFadeByEffect, file, "$$;$"); newXSproto(strcpy(buf, "BuffFadeAll"), XS_Mob_BuffFadeAll, file, "$"); + newXSproto(strcpy(buf, "BuffFadeByEffect"), XS_Mob_BuffFadeByEffect, file, "$$;$"); newXSproto(strcpy(buf, "BuffFadeBySlot"), XS_Mob_BuffFadeBySlot, file, "$$;$"); + newXSproto(strcpy(buf, "BuffFadeBySpellID"), XS_Mob_BuffFadeBySpellID, file, "$$"); + newXSproto(strcpy(buf, "CalculateDistance"), XS_Mob_CalculateDistance, file, "$$$$"); + newXSproto(strcpy(buf, "CalculateHeadingToTarget"), XS_Mob_CalculateHeadingToTarget, file, "$$$"); + newXSproto(strcpy(buf, "CameraEffect"), XS_Mob_CameraEffect, file, "$$;$$$"); newXSproto(strcpy(buf, "CanBuffStack"), XS_Mob_CanBuffStack, file, "$$$;$"); - newXSproto(strcpy(buf, "IsCasting"), XS_Mob_IsCasting, file, "$"); - newXSproto(strcpy(buf, "CastingSpellID"), XS_Mob_CastingSpellID, file, "$"); - newXSproto(strcpy(buf, "SetAppearance"), XS_Mob_SetAppearance, file, "$$;$"); - newXSproto(strcpy(buf, "GetAppearance"), XS_Mob_GetAppearance, file, "$"); - newXSproto(strcpy(buf, "GetRunAnimSpeed"), XS_Mob_GetRunAnimSpeed, file, "$"); - newXSproto(strcpy(buf, "SetRunAnimSpeed"), XS_Mob_SetRunAnimSpeed, file, "$$"); - newXSproto(strcpy(buf, "SetPetID"), XS_Mob_SetPetID, file, "$$"); - newXSproto(strcpy(buf, "GetPetID"), XS_Mob_GetPetID, file, "$"); - newXSproto(strcpy(buf, "SetOwnerID"), XS_Mob_SetOwnerID, file, "$$"); - newXSproto(strcpy(buf, "GetOwnerID"), XS_Mob_GetOwnerID, file, "$"); - newXSproto(strcpy(buf, "GetPetType"), XS_Mob_GetPetType, file, "$"); - newXSproto(strcpy(buf, "GetBodyType"), XS_Mob_GetBodyType, file, "$"); - newXSproto(strcpy(buf, "Stun"), XS_Mob_Stun, file, "$$"); - newXSproto(strcpy(buf, "Spin"), XS_Mob_Spin, file, "$"); - newXSproto(strcpy(buf, "Kill"), XS_Mob_Kill, file, "$"); - newXSproto(strcpy(buf, "SetInvul"), XS_Mob_SetInvul, file, "$$"); - newXSproto(strcpy(buf, "GetInvul"), XS_Mob_GetInvul, file, "$"); - newXSproto(strcpy(buf, "SetExtraHaste"), XS_Mob_SetExtraHaste, file, "$$"); - newXSproto(strcpy(buf, "GetHaste"), XS_Mob_GetHaste, file, "$"); - newXSproto(strcpy(buf, "GetHandToHandDamage"), XS_Mob_GetHandToHandDamage, file, "$"); + newXSproto(strcpy(buf, "CanClassEquipItem"), XS_Mob_CanClassEquipItem, file, "$$"); + newXSproto(strcpy(buf, "CanRaceEquipItem"), XS_Mob_CanRaceEquipItem, file, "$$"); + newXSproto(strcpy(buf, "CanThisClassDodge"), XS_Mob_CanThisClassDodge, file, "$"); newXSproto(strcpy(buf, "CanThisClassDoubleAttack"), XS_Mob_CanThisClassDoubleAttack, file, "$"); newXSproto(strcpy(buf, "CanThisClassDualWield"), XS_Mob_CanThisClassDualWield, file, "$"); - newXSproto(strcpy(buf, "CanThisClassRiposte"), XS_Mob_CanThisClassRiposte, file, "$"); - newXSproto(strcpy(buf, "CanThisClassDodge"), XS_Mob_CanThisClassDodge, file, "$"); newXSproto(strcpy(buf, "CanThisClassParry"), XS_Mob_CanThisClassParry, file, "$"); - newXSproto(strcpy(buf, "GetHandToHandDelay"), XS_Mob_GetHandToHandDelay, file, "$"); - newXSproto(strcpy(buf, "GetClassLevelFactor"), XS_Mob_GetClassLevelFactor, file, "$"); - newXSproto(strcpy(buf, "Mesmerize"), XS_Mob_Mesmerize, file, "$"); - newXSproto(strcpy(buf, "IsMezzed"), XS_Mob_IsMezzed, file, "$"); - newXSproto(strcpy(buf, "IsStunned"), XS_Mob_IsStunned, file, "$"); - newXSproto(strcpy(buf, "StartEnrage"), XS_Mob_StartEnrage, file, "$"); - newXSproto(strcpy(buf, "IsEnraged"), XS_Mob_IsEnraged, file, "$"); - newXSproto(strcpy(buf, "GetReverseFactionCon"), XS_Mob_GetReverseFactionCon, file, "$$"); - newXSproto(strcpy(buf, "IsAIControlled"), XS_Mob_IsAIControlled, file, "$"); - newXSproto(strcpy(buf, "GetAggroRange"), XS_Mob_GetAggroRange, file, "$"); - newXSproto(strcpy(buf, "GetAssistRange"), XS_Mob_GetAssistRange, file, "$"); - newXSproto(strcpy(buf, "SetPetOrder"), XS_Mob_SetPetOrder, file, "$$"); - newXSproto(strcpy(buf, "GetPetOrder"), XS_Mob_GetPetOrder, file, "$"); - newXSproto(strcpy(buf, "IsRoamer"), XS_Mob_IsRoamer, file, "$"); - newXSproto(strcpy(buf, "IsRooted"), XS_Mob_IsRooted, file, "$"); - newXSproto(strcpy(buf, "AddToHateList"), XS_Mob_AddToHateList, file, "$$;$$$$$"); - newXSproto(strcpy(buf, "SetHate"), XS_Mob_SetHate, file, "$$;$$"); - newXSproto(strcpy(buf, "HalveAggro"), XS_Mob_HalveAggro, file, "$$"); - newXSproto(strcpy(buf, "DoubleAggro"), XS_Mob_DoubleAggro, file, "$$"); - newXSproto(strcpy(buf, "GetHateAmount"), XS_Mob_GetHateAmount, file, "$$;$"); - newXSproto(strcpy(buf, "GetDamageAmount"), XS_Mob_GetDamageAmount, file, "$$"); - newXSproto(strcpy(buf, "GetHateTop"), XS_Mob_GetHateTop, file, "$"); - newXSproto(strcpy(buf, "GetHateDamageTop"), XS_Mob_GetHateDamageTop, file, "$$"); - newXSproto(strcpy(buf, "GetHateRandom"), XS_Mob_GetHateRandom, file, "$"); - newXSproto(strcpy(buf, "IsEngaged"), XS_Mob_IsEngaged, file, "$"); - newXSproto(strcpy(buf, "HateSummon"), XS_Mob_HateSummon, file, "$"); - newXSproto(strcpy(buf, "FaceTarget"), XS_Mob_FaceTarget, file, "$;$$"); - newXSproto(strcpy(buf, "SetHeading"), XS_Mob_SetHeading, file, "$$"); - newXSproto(strcpy(buf, "WipeHateList"), XS_Mob_WipeHateList, file, "$"); - newXSproto(strcpy(buf, "CheckAggro"), XS_Mob_CheckAggro, file, "$$"); - newXSproto(strcpy(buf, "CalculateHeadingToTarget"), XS_Mob_CalculateHeadingToTarget, file, "$$$"); - newXSproto(strcpy(buf, "RunTo"), XS_Mob_RunTo, file, "$$$$"); - newXSproto(strcpy(buf, "WalkTo"), XS_Mob_WalkTo, file, "$$$$"); - newXSproto(strcpy(buf, "NavigateTo"), XS_Mob_NavigateTo, file, "$$$$"); - newXSproto(strcpy(buf, "StopNavigation"), XS_Mob_StopNavigation, file, "$"); - newXSproto(strcpy(buf, "CalculateDistance"), XS_Mob_CalculateDistance, file, "$$$$"); - newXSproto(strcpy(buf, "SendTo"), XS_Mob_SendTo, file, "$$$$"); - newXSproto(strcpy(buf, "SendToFixZ"), XS_Mob_SendToFixZ, file, "$$$$"); - newXSproto(strcpy(buf, "NPCSpecialAttacks"), XS_Mob_NPCSpecialAttacks, file, "$$$;$$"); - newXSproto(strcpy(buf, "DontHealMeBefore"), XS_Mob_DontHealMeBefore, file, "$"); - newXSproto(strcpy(buf, "DontBuffMeBefore"), XS_Mob_DontBuffMeBefore, file, "$"); - newXSproto(strcpy(buf, "DontDotMeBefore"), XS_Mob_DontDotMeBefore, file, "$"); - newXSproto(strcpy(buf, "DontRootMeBefore"), XS_Mob_DontRootMeBefore, file, "$"); - newXSproto(strcpy(buf, "DontSnareMeBefore"), XS_Mob_DontSnareMeBefore, file, "$"); - newXSproto(strcpy(buf, "GetResist"), XS_Mob_GetResist, file, "$$"); + newXSproto(strcpy(buf, "CanThisClassRiposte"), XS_Mob_CanThisClassRiposte, file, "$"); + newXSproto(strcpy(buf, "CastSpell"), XS_Mob_CastSpell, file, "$$$;$$$"); +#ifdef BOTS + newXSproto(strcpy(buf, "CastToBot"), XS_Mob_CastToBot, file, "$"); +#endif + newXSproto(strcpy(buf, "CastToClient"), XS_Mob_CastToClient, file, "$"); + newXSproto(strcpy(buf, "CastToCorpse"), XS_Mob_CastToCorpse, file, "$"); + newXSproto(strcpy(buf, "CastToMob"), XS_Mob_CastToMob, file, "$"); + newXSproto(strcpy(buf, "CastToNPC"), XS_Mob_CastToNPC, file, "$"); + newXSproto(strcpy(buf, "CastingSpellID"), XS_Mob_CastingSpellID, file, "$"); + newXSproto(strcpy(buf, "ChangeSize"), XS_Mob_ChangeSize, file, "$$;$"); newXSproto(strcpy(buf, "Charmed"), XS_Mob_Charmed, file, "$"); - newXSproto(strcpy(buf, "GetLevelHP"), XS_Mob_GetLevelHP, file, "$$"); - newXSproto(strcpy(buf, "GetZoneID"), XS_Mob_GetZoneID, file, "$"); + newXSproto(strcpy(buf, "CheckAggro"), XS_Mob_CheckAggro, file, "$$"); newXSproto(strcpy(buf, "CheckAggroAmount"), XS_Mob_CheckAggroAmount, file, "$$"); newXSproto(strcpy(buf, "CheckHealAggroAmount"), XS_Mob_CheckHealAggroAmount, file, "$$"); - newXSproto(strcpy(buf, "GetAA"), XS_Mob_GetAA, file, "$$"); - newXSproto(strcpy(buf, "GetAAByAAID"), XS_Mob_GetAAByAAID, file, "$$"); - newXSproto(strcpy(buf, "SetAA"), XS_Mob_SetAA, file, "$$$;$"); - newXSproto(strcpy(buf, "DivineAura"), XS_Mob_DivineAura, file, "$"); - newXSproto(strcpy(buf, "AddFeignMemory"), XS_Mob_AddFeignMemory, file, "$$"); - newXSproto(strcpy(buf, "RemoveFromFeignMemory"), XS_Mob_RemoveFromFeignMemory, file, "$$"); - newXSproto(strcpy(buf, "ClearFeignMemory"), XS_Mob_ClearFeignMemory, file, "$"); - newXSproto(strcpy(buf, "SetOOCRegen"), XS_Mob_SetOOCRegen, file, "$$"); - newXSproto(strcpy(buf, "GetEntityVariable"), XS_Mob_GetEntityVariable, file, "$$"); - newXSproto(strcpy(buf, "SetEntityVariable"), XS_Mob_SetEntityVariable, file, "$$$"); - newXSproto(strcpy(buf, "EntityVariableExists"), XS_Mob_EntityVariableExists, file, "$$"); - newXSproto(strcpy(buf, "GetHateList"), XS_Mob_GetHateList, file, "$"); - newXSproto(strcpy(buf, "SignalClient"), XS_Mob_SignalClient, file, "$$$"); - newXSproto(strcpy(buf, "CombatRange"), XS_Mob_CombatRange, file, "$$"); - newXSproto(strcpy(buf, "DoSpecialAttackDamage"), XS_Mob_DoSpecialAttackDamage, file, "$$$$;$$"); newXSproto(strcpy(buf, "CheckLoS"), XS_Mob_CheckLoS, file, "$$"); newXSproto(strcpy(buf, "CheckLoSToLoc"), XS_Mob_CheckLoSToLoc, file, "$$$$;$"); - newXSproto(strcpy(buf, "FindGroundZ"), XS_Mob_FindGroundZ, file, "$$$;$"); - newXSproto(strcpy(buf, "ProjectileAnim"), XS_Mob_ProjectileAnim, file, "$$$;$$$$$$"); - newXSproto(strcpy(buf, "HasNPCSpecialAtk"), XS_Mob_HasNPCSpecialAtk, file, "$$"); - newXSproto(strcpy(buf, "SendAppearanceEffect"), XS_Mob_SendAppearanceEffect, file, "$$;$$$$"); - newXSproto(strcpy(buf, "SetFlyMode"), XS_Mob_SetFlyMode, file, "$$"); - newXSproto(strcpy(buf, "SetTexture"), XS_Mob_SetTexture, file, "$$"); - newXSproto(strcpy(buf, "SetRace"), XS_Mob_SetRace, file, "$$"); - newXSproto(strcpy(buf, "SetGender"), XS_Mob_SetGender, file, "$$"); - newXSproto(strcpy(buf, "SendIllusion"), XS_Mob_SendIllusion, file, "$$;$$$$$$$$$$$$"); - newXSproto(strcpy(buf, "MakeTempPet"), XS_Mob_MakeTempPet, file, "$$;$$$$"); - newXSproto(strcpy(buf, "TypesTempPet"), XS_Mob_TypesTempPet, file, "$$;$$$$$"); - newXSproto(strcpy(buf, "CameraEffect"), XS_Mob_CameraEffect, file, "$$;$$$"); - newXSproto(strcpy(buf, "SpellEffect"), XS_Mob_SpellEffect, file, "$$;$$$$$$"); - newXSproto(strcpy(buf, "TempName"), XS_Mob_TempName, file, "$:$"); - newXSproto(strcpy(buf, "GetItemStat"), XS_Mob_GetItemStat, file, "$$$"); - newXSproto(strcpy(buf, "GetGlobal"), XS_Mob_GetGlobal, file, "$$"); - newXSproto(strcpy(buf, "SetGlobal"), XS_Mob_SetGlobal, file, "$$$$$;$"); - newXSproto(strcpy(buf, "TarGlobal"), XS_Mob_TarGlobal, file, "$$$$$$$"); - newXSproto(strcpy(buf, "DelGlobal"), XS_Mob_DelGlobal, file, "$$"); - newXSproto(strcpy(buf, "SetSlotTint"), XS_Mob_SetSlotTint, file, "$$$$$"); - newXSproto(strcpy(buf, "WearChange"), XS_Mob_WearChange, file, "$$$;$$"); - newXSproto(strcpy(buf, "DoKnockback"), XS_Mob_DoKnockback, file, "$$$$"); - newXSproto(strcpy(buf, "RemoveNimbusEffect"), XS_Mob_RemoveNimbusEffect, file, "$$"); - newXSproto(strcpy(buf, "IsRunning"), XS_Mob_IsRunning, file, "$"); - newXSproto(strcpy(buf, "SetRunning"), XS_Mob_SetRunning, file, "$$"); - newXSproto(strcpy(buf, "SetBodyType"), XS_Mob_SetBodyType, file, "$$;$"); - newXSproto(strcpy(buf, "SetDeltas"), XS_Mob_SetDeltas, file, "$$$$$"); - newXSproto(strcpy(buf, "SetLD"), XS_Mob_SetLD, file, "$$"); - newXSproto(strcpy(buf, "SetTargetable"), XS_Mob_SetTargetable, file, "$$"); - newXSproto(strcpy(buf, "ModSkillDmgTaken"), XS_Mob_ModSkillDmgTaken, file, "$$$"); - newXSproto(strcpy(buf, "GetModSkillDmgTaken"), XS_Mob_GetModSkillDmgTaken, file, "$$"); - newXSproto(strcpy(buf, "GetSkillDmgTaken"), XS_Mob_GetSkillDmgTaken, file, "$$"); - newXSproto(strcpy(buf, "SetAllowBeneficial"), XS_Mob_SetAllowBeneficial, file, "$$"); - newXSproto(strcpy(buf, "GetAllowBeneficial"), XS_Mob_GetAllowBeneficial, file, "$$"); - newXSproto(strcpy(buf, "IsBeneficialAllowed"), XS_Mob_IsBeneficialAllowed, file, "$$"); - newXSproto(strcpy(buf, "ModVulnerability"), XS_Mob_ModVulnerability, file, "$$$"); - newXSproto(strcpy(buf, "GetModVulnerability"), XS_Mob_GetModVulnerability, file, "$$"); - newXSproto(strcpy(buf, "DoMeleeSkillAttackDmg"), XS_Mob_DoMeleeSkillAttackDmg, file, "$$$$$$$"); - newXSproto(strcpy(buf, "DoArcheryAttackDmg"), XS_Mob_DoArcheryAttackDmg, file, "$$$$$$$"); - newXSproto(strcpy(buf, "DoThrowingAttackDmg"), XS_Mob_DoThrowingAttackDmg, file, "$$$$$$$"); - newXSproto(strcpy(buf, "SetDisableMelee"), XS_Mob_SetDisableMelee, file, "$$"); - newXSproto(strcpy(buf, "IsMeleeDisabled"), XS_Mob_IsMeleeDisabled, file, "$"); - newXSproto(strcpy(buf, "SetFlurryChance"), XS_Mob_SetFlurryChance, file, "$$"); - newXSproto(strcpy(buf, "GetFlurryChance"), XS_Mob_GetFlurryChance, file, "$"); - newXSproto(strcpy(buf, "GetSpellStat"), XS_Mob_GetSpellStat, file, "$$$$"); - newXSproto(strcpy(buf, "GetSpecialAbility"), XS_Mob_GetSpecialAbility, file, "$$"); - newXSproto(strcpy(buf, "GetSpecialAbilityParam"), XS_Mob_GetSpecialAbilityParam, file, "$$$"); - newXSproto(strcpy(buf, "SetSpecialAbility"), XS_Mob_SetSpecialAbility, file, "$$$"); - newXSproto(strcpy(buf, "SetSpecialAbilityParam"), XS_Mob_SetSpecialAbilityParam, file, "$$$$"); + newXSproto(strcpy(buf, "ClearFeignMemory"), XS_Mob_ClearFeignMemory, file, "$"); newXSproto(strcpy(buf, "ClearSpecialAbilities"), XS_Mob_ClearSpecialAbilities, file, "$"); - newXSproto(strcpy(buf, "ProcessSpecialAbilities"), XS_Mob_ProcessSpecialAbilities, file, "$$"); - newXSproto(strcpy(buf, "CanClassEquipItem"), XS_Mob_CanClassEquipItem, file, "$$"); - newXSproto(strcpy(buf, "IsFeared"), XS_Mob_IsFeared, file, "$"); - newXSproto(strcpy(buf, "IsBlind"), XS_Mob_IsBlind, file, "$"); - newXSproto(strcpy(buf, "SeeInvisible"), XS_Mob_SeeInvisible, file, "$"); - newXSproto(strcpy(buf, "SeeInvisibleUndead"), XS_Mob_SeeInvisibleUndead, file, "$"); - newXSproto(strcpy(buf, "SeeHide"), XS_Mob_SeeHide, file, "$"); - newXSproto(strcpy(buf, "SeeImprovedHide"), XS_Mob_SeeImprovedHide, file, "$"); - newXSproto(strcpy(buf, "GetNimbusEffect1"), XS_Mob_GetNimbusEffect1, file, "$"); - newXSproto(strcpy(buf, "GetNimbusEffect2"), XS_Mob_GetNimbusEffect2, file, "$"); - newXSproto(strcpy(buf, "GetNimbusEffect3"), XS_Mob_GetNimbusEffect3, file, "$"); - newXSproto(strcpy(buf, "IsTargetable"), XS_Mob_IsTargetable, file, "$"); - newXSproto(strcpy(buf, "HasShieldEquiped"), XS_Mob_HasShieldEquiped, file, "$"); - newXSproto(strcpy(buf, "HasTwoHandBluntEquiped"), XS_Mob_HasTwoHandBluntEquiped, file, "$"); - newXSproto(strcpy(buf, "HasTwoHanderEquipped"), XS_Mob_HasTwoHanderEquipped, file, "$"); - newXSproto(strcpy(buf, "GetHerosForgeModel"), XS_Mob_GetHerosForgeModel, file, "$$"); - newXSproto(strcpy(buf, "IsEliteMaterialItem"), XS_Mob_IsEliteMaterialItem, file, "$$"); - newXSproto(strcpy(buf, "GetBaseSize"), XS_Mob_GetBaseSize, file, "$"); - newXSproto(strcpy(buf, "HasOwner"), XS_Mob_HasOwner, file, "$"); - newXSproto(strcpy(buf, "IsPet"), XS_Mob_IsPet, file, "$"); - newXSproto(strcpy(buf, "HasPet"), XS_Mob_HasPet, file, "$"); - newXSproto(strcpy(buf, "RemovePet"), XS_Mob_RemovePet, file, "$"); - newXSproto(strcpy(buf, "SetPet"), XS_Mob_SetPet, file, "$$"); - newXSproto(strcpy(buf, "IsSilenced"), XS_Mob_IsSilenced, file, "$"); - newXSproto(strcpy(buf, "IsAmnesiad"), XS_Mob_IsAmnesiad, file, "$"); - newXSproto(strcpy(buf, "GetMeleeMitigation"), XS_Mob_GetMeleeMitigation, file, "$"); - newXSproto(strcpy(buf, "TryMoveAlong"), XS_Mob_TryMoveAlong, file, "$$$;$"); + newXSproto(strcpy(buf, "CombatRange"), XS_Mob_CombatRange, file, "$$"); + newXSproto(strcpy(buf, "Damage"), XS_Mob_Damage, file, "$$$$$;$$$"); + newXSproto(strcpy(buf, "DelGlobal"), XS_Mob_DelGlobal, file, "$$"); newXSproto(strcpy(buf, "DeleteBucket"), XS_Mob_DeleteBucket, file, "$$"); + newXSproto(strcpy(buf, "Depop"), XS_Mob_Depop, file, "$;$"); + newXSproto(strcpy(buf, "DivineAura"), XS_Mob_DivineAura, file, "$"); + newXSproto(strcpy(buf, "DoAnim"), XS_Mob_DoAnim, file, "$$;$"); + newXSproto(strcpy(buf, "DoArcheryAttackDmg"), XS_Mob_DoArcheryAttackDmg, file, "$$$$$$$"); + newXSproto(strcpy(buf, "DoKnockback"), XS_Mob_DoKnockback, file, "$$$$"); + newXSproto(strcpy(buf, "DoMeleeSkillAttackDmg"), XS_Mob_DoMeleeSkillAttackDmg, file, "$$$$$$$"); + newXSproto(strcpy(buf, "DoSpecialAttackDamage"), XS_Mob_DoSpecialAttackDamage, file, "$$$$;$$"); + newXSproto(strcpy(buf, "DoThrowingAttackDmg"), XS_Mob_DoThrowingAttackDmg, file, "$$$$$$$"); + newXSproto(strcpy(buf, "DontBuffMeBefore"), XS_Mob_DontBuffMeBefore, file, "$"); + newXSproto(strcpy(buf, "DontDotMeBefore"), XS_Mob_DontDotMeBefore, file, "$"); + newXSproto(strcpy(buf, "DontHealMeBefore"), XS_Mob_DontHealMeBefore, file, "$"); + newXSproto(strcpy(buf, "DontRootMeBefore"), XS_Mob_DontRootMeBefore, file, "$"); + newXSproto(strcpy(buf, "DontSnareMeBefore"), XS_Mob_DontSnareMeBefore, file, "$"); + newXSproto(strcpy(buf, "DoubleAggro"), XS_Mob_DoubleAggro, file, "$$"); + newXSproto(strcpy(buf, "Emote"), XS_Mob_Emote, file, "$$;@"); + newXSproto(strcpy(buf, "EntityVariableExists"), XS_Mob_EntityVariableExists, file, "$$"); + newXSproto(strcpy(buf, "FaceTarget"), XS_Mob_FaceTarget, file, "$;$$"); + newXSproto(strcpy(buf, "FindBuff"), XS_Mob_FindBuff, file, "$$"); + newXSproto(strcpy(buf, "FindBuffBySlot"), XS_Mob_FindBuffBySlot, file, "$$"); + newXSproto(strcpy(buf, "FindGroundZ"), XS_Mob_FindGroundZ, file, "$$$;$"); + newXSproto(strcpy(buf, "FindType"), XS_Mob_FindType, file, "$$;$$"); + newXSproto(strcpy(buf, "GMMove"), XS_Mob_GMMove, file, "$$$$;$"); + newXSproto(strcpy(buf, "Gate"), XS_Mob_Gate, file, "$"); + newXSproto(strcpy(buf, "GetAA"), XS_Mob_GetAA, file, "$$"); + newXSproto(strcpy(buf, "GetAAByAAID"), XS_Mob_GetAAByAAID, file, "$$"); + newXSproto(strcpy(buf, "GetAC"), XS_Mob_GetAC, file, "$"); + newXSproto(strcpy(buf, "GetAGI"), XS_Mob_GetAGI, file, "$"); + newXSproto(strcpy(buf, "GetATK"), XS_Mob_GetATK, file, "$"); + newXSproto(strcpy(buf, "GetActSpellCasttime"), XS_Mob_GetActSpellCasttime, file, "$$$"); + newXSproto(strcpy(buf, "GetActSpellCost"), XS_Mob_GetActSpellCost, file, "$$$"); + newXSproto(strcpy(buf, "GetActSpellDamage"), XS_Mob_GetActSpellDamage, file, "$$$"); + newXSproto(strcpy(buf, "GetActSpellDuration"), XS_Mob_GetActSpellDuration, file, "$$$"); + newXSproto(strcpy(buf, "GetActSpellHealing"), XS_Mob_GetActSpellHealing, file, "$$$"); + newXSproto(strcpy(buf, "GetActSpellRange"), XS_Mob_GetActSpellRange, file, "$$$"); + newXSproto(strcpy(buf, "GetAggroRange"), XS_Mob_GetAggroRange, file, "$"); + newXSproto(strcpy(buf, "GetAllowBeneficial"), XS_Mob_GetAllowBeneficial, file, "$$"); + newXSproto(strcpy(buf, "GetAppearance"), XS_Mob_GetAppearance, file, "$"); + newXSproto(strcpy(buf, "GetArmorTint"), XS_Mob_GetArmorTint, file, "$$"); + newXSproto(strcpy(buf, "GetAssistRange"), XS_Mob_GetAssistRange, file, "$"); + newXSproto(strcpy(buf, "GetBaseGender"), XS_Mob_GetBaseGender, file, "$"); + newXSproto(strcpy(buf, "GetBaseRace"), XS_Mob_GetBaseRace, file, "$"); + newXSproto(strcpy(buf, "GetBaseSize"), XS_Mob_GetBaseSize, file, "$"); + newXSproto(strcpy(buf, "GetBeard"), XS_Mob_GetBeard, file, "$"); + newXSproto(strcpy(buf, "GetBeardColor"), XS_Mob_GetBeardColor, file, "$"); + newXSproto(strcpy(buf, "GetBodyType"), XS_Mob_GetBodyType, file, "$"); newXSproto(strcpy(buf, "GetBucket"), XS_Mob_GetBucket, file, "$$"); newXSproto(strcpy(buf, "GetBucketExpires"), XS_Mob_GetBucketExpires, file, "$$"); newXSproto(strcpy(buf, "GetBucketKey"), XS_Mob_GetBucketKey, file, "$"); newXSproto(strcpy(buf, "GetBucketRemaining"), XS_Mob_GetBucketRemaining, file, "$$"); - newXSproto(strcpy(buf, "SetBucket"), XS_Mob_SetBucket, file, "$$$;$"); + newXSproto(strcpy(buf, "GetBuffSlotFromType"), XS_Mob_GetBuffSlotFromType, file, "$$"); + newXSproto(strcpy(buf, "GetCHA"), XS_Mob_GetCHA, file, "$"); + newXSproto(strcpy(buf, "GetCR"), XS_Mob_GetCR, file, "$"); + newXSproto(strcpy(buf, "GetCasterLevel"), XS_Mob_GetCasterLevel, file, "$$"); + newXSproto(strcpy(buf, "GetClass"), XS_Mob_GetClass, file, "$"); + newXSproto(strcpy(buf, "GetClassLevelFactor"), XS_Mob_GetClassLevelFactor, file, "$"); + newXSproto(strcpy(buf, "GetClassName"), XS_Mob_GetClassName, file, "$"); + newXSproto(strcpy(buf, "GetCleanName"), XS_Mob_GetCleanName, file, "$"); + newXSproto(strcpy(buf, "GetCorruption"), XS_Mob_GetCorruption, file, "$"); + newXSproto(strcpy(buf, "GetDEX"), XS_Mob_GetDEX, file, "$"); + newXSproto(strcpy(buf, "GetDR"), XS_Mob_GetDR, file, "$"); + newXSproto(strcpy(buf, "GetDamageAmount"), XS_Mob_GetDamageAmount, file, "$$"); + newXSproto(strcpy(buf, "GetDeity"), XS_Mob_GetDeity, file, "$"); + newXSproto(strcpy(buf, "GetDisplayAC"), XS_Mob_GetDisplayAC, file, "$"); + newXSproto(strcpy(buf, "GetDrakkinDetails"), XS_Mob_GetDrakkinDetails, file, "$"); + newXSproto(strcpy(buf, "GetDrakkinHeritage"), XS_Mob_GetDrakkinHeritage, file, "$"); + newXSproto(strcpy(buf, "GetDrakkinTattoo"), XS_Mob_GetDrakkinTattoo, file, "$"); + newXSproto(strcpy(buf, "GetEntityVariable"), XS_Mob_GetEntityVariable, file, "$$"); + newXSproto(strcpy(buf, "GetEquipment"), XS_Mob_GetEquipment, file, "$$"); + newXSproto(strcpy(buf, "GetEquipmentColor"), XS_Mob_GetEquipmentColor, file, "$$"); + newXSproto(strcpy(buf, "GetEquipmentMaterial"), XS_Mob_GetEquipmentMaterial, file, "$$"); + newXSproto(strcpy(buf, "GetEyeColor1"), XS_Mob_GetEyeColor1, file, "$"); + newXSproto(strcpy(buf, "GetEyeColor2"), XS_Mob_GetEyeColor2, file, "$"); + newXSproto(strcpy(buf, "GetFR"), XS_Mob_GetFR, file, "$"); + newXSproto(strcpy(buf, "GetFlurryChance"), XS_Mob_GetFlurryChance, file, "$"); + newXSproto(strcpy(buf, "GetFollowID"), XS_Mob_GetFollowID, file, "$"); + newXSproto(strcpy(buf, "GetGender"), XS_Mob_GetGender, file, "$"); + newXSproto(strcpy(buf, "GetGlobal"), XS_Mob_GetGlobal, file, "$$"); + newXSproto(strcpy(buf, "GetHP"), XS_Mob_GetHP, file, "$"); + newXSproto(strcpy(buf, "GetHPRatio"), XS_Mob_GetHPRatio, file, "$"); + newXSproto(strcpy(buf, "GetHairColor"), XS_Mob_GetHairColor, file, "$"); + newXSproto(strcpy(buf, "GetHairStyle"), XS_Mob_GetHairStyle, file, "$"); + newXSproto(strcpy(buf, "GetHandToHandDamage"), XS_Mob_GetHandToHandDamage, file, "$"); + newXSproto(strcpy(buf, "GetHandToHandDelay"), XS_Mob_GetHandToHandDelay, file, "$"); + newXSproto(strcpy(buf, "GetHaste"), XS_Mob_GetHaste, file, "$"); + newXSproto(strcpy(buf, "GetHateAmount"), XS_Mob_GetHateAmount, file, "$$;$"); newXSproto(strcpy(buf, "GetHateClosest"), XS_Mob_GetHateClosest, file, "$"); + newXSproto(strcpy(buf, "GetHateDamageTop"), XS_Mob_GetHateDamageTop, file, "$$"); + newXSproto(strcpy(buf, "GetHateList"), XS_Mob_GetHateList, file, "$"); newXSproto(strcpy(buf, "GetHateListByDistance"), XS_Mob_GetHateListByDistance, file, "$;$"); - newXSproto(strcpy(buf, "GetLastName"), XS_Mob_GetLastName, file, "$"); - newXSproto(strcpy(buf, "CanRaceEquipItem"), XS_Mob_CanRaceEquipItem, file, "$$"); - newXSproto(strcpy(buf, "RemoveAllNimbusEffects"), XS_Mob_RemoveAllNimbusEffects, file, "$"); - newXSproto(strcpy(buf, "AddNimbusEffect"), XS_Mob_AddNimbusEffect, file, "$$"); - newXSproto(strcpy(buf, "ShieldAbility"), XS_Mob_ShieldAbility, file, "$$$$$$$$"); - newXSproto(strcpy(buf, "GetHateRandomClient"), XS_Mob_GetHateRandomClient, file, "$"); - newXSproto(strcpy(buf, "GetHateRandomNPC"), XS_Mob_GetHateRandomNPC, file, "$"); + newXSproto(strcpy(buf, "GetHateRandom"), XS_Mob_GetHateRandom, file, "$"); #ifdef BOTS - newXSproto(strcpy(buf, "CastToBot"), XS_Mob_CastToBot, file, "$"); newXSproto(strcpy(buf, "GetHateRandomBot"), XS_Mob_GetHateRandomBot, file, "$"); #endif + newXSproto(strcpy(buf, "GetHateRandomClient"), XS_Mob_GetHateRandomClient, file, "$"); + newXSproto(strcpy(buf, "GetHateRandomNPC"), XS_Mob_GetHateRandomNPC, file, "$"); + newXSproto(strcpy(buf, "GetHateTop"), XS_Mob_GetHateTop, file, "$"); + newXSproto(strcpy(buf, "GetHeading"), XS_Mob_GetHeading, file, "$"); + newXSproto(strcpy(buf, "GetHelmTexture"), XS_Mob_GetHelmTexture, file, "$"); + newXSproto(strcpy(buf, "GetHerosForgeModel"), XS_Mob_GetHerosForgeModel, file, "$$"); + newXSproto(strcpy(buf, "GetID"), XS_Mob_GetID, file, "$"); + newXSproto(strcpy(buf, "GetINT"), XS_Mob_GetINT, file, "$"); + newXSproto(strcpy(buf, "GetInvul"), XS_Mob_GetInvul, file, "$"); + newXSproto(strcpy(buf, "GetItemHPBonuses"), XS_Mob_GetItemHPBonuses, file, "$"); + newXSproto(strcpy(buf, "GetItemStat"), XS_Mob_GetItemStat, file, "$$$"); + newXSproto(strcpy(buf, "GetLastName"), XS_Mob_GetLastName, file, "$"); + newXSproto(strcpy(buf, "GetLevel"), XS_Mob_GetLevel, file, "$"); + newXSproto(strcpy(buf, "GetLevelCon"), XS_Mob_GetLevelCon, file, "$$"); + newXSproto(strcpy(buf, "GetLevelHP"), XS_Mob_GetLevelHP, file, "$$"); + newXSproto(strcpy(buf, "GetLuclinFace"), XS_Mob_GetLuclinFace, file, "$"); + newXSproto(strcpy(buf, "GetMR"), XS_Mob_GetMR, file, "$"); + newXSproto(strcpy(buf, "GetMana"), XS_Mob_GetMana, file, "$"); + newXSproto(strcpy(buf, "GetManaRatio"), XS_Mob_GetManaRatio, file, "$"); + newXSproto(strcpy(buf, "GetMaxAGI"), XS_Mob_GetMaxAGI, file, "$"); + newXSproto(strcpy(buf, "GetMaxCHA"), XS_Mob_GetMaxCHA, file, "$"); + newXSproto(strcpy(buf, "GetMaxDEX"), XS_Mob_GetMaxDEX, file, "$"); + newXSproto(strcpy(buf, "GetMaxHP"), XS_Mob_GetMaxHP, file, "$"); + newXSproto(strcpy(buf, "GetMaxINT"), XS_Mob_GetMaxINT, file, "$"); + newXSproto(strcpy(buf, "GetMaxMana"), XS_Mob_GetMaxMana, file, "$"); + newXSproto(strcpy(buf, "GetMaxSTA"), XS_Mob_GetMaxSTA, file, "$"); + newXSproto(strcpy(buf, "GetMaxSTR"), XS_Mob_GetMaxSTR, file, "$"); + newXSproto(strcpy(buf, "GetMaxWIS"), XS_Mob_GetMaxWIS, file, "$"); + newXSproto(strcpy(buf, "GetMeleeMitigation"), XS_Mob_GetMeleeMitigation, file, "$"); + newXSproto(strcpy(buf, "GetModSkillDmgTaken"), XS_Mob_GetModSkillDmgTaken, file, "$$"); + newXSproto(strcpy(buf, "GetModVulnerability"), XS_Mob_GetModVulnerability, file, "$$"); + newXSproto(strcpy(buf, "GetNPCTypeID"), XS_Mob_GetNPCTypeID, file, "$"); + newXSproto(strcpy(buf, "GetName"), XS_Mob_GetName, file, "$"); + newXSproto(strcpy(buf, "GetNimbusEffect1"), XS_Mob_GetNimbusEffect1, file, "$"); + newXSproto(strcpy(buf, "GetNimbusEffect2"), XS_Mob_GetNimbusEffect2, file, "$"); + newXSproto(strcpy(buf, "GetNimbusEffect3"), XS_Mob_GetNimbusEffect3, file, "$"); + newXSproto(strcpy(buf, "GetOwnerID"), XS_Mob_GetOwnerID, file, "$"); + newXSproto(strcpy(buf, "GetPR"), XS_Mob_GetPR, file, "$"); + newXSproto(strcpy(buf, "GetPetID"), XS_Mob_GetPetID, file, "$"); + newXSproto(strcpy(buf, "GetPetOrder"), XS_Mob_GetPetOrder, file, "$"); + newXSproto(strcpy(buf, "GetPetType"), XS_Mob_GetPetType, file, "$"); + newXSproto(strcpy(buf, "GetPhR"), XS_Mob_GetPhR, file, "$"); + newXSproto(strcpy(buf, "GetRace"), XS_Mob_GetRace, file, "$"); + newXSproto(strcpy(buf, "GetRaceName"), XS_Mob_GetRaceName, file, "$"); + newXSproto(strcpy(buf, "GetResist"), XS_Mob_GetResist, file, "$$"); + newXSproto(strcpy(buf, "GetReverseFactionCon"), XS_Mob_GetReverseFactionCon, file, "$$"); + newXSproto(strcpy(buf, "GetRunAnimSpeed"), XS_Mob_GetRunAnimSpeed, file, "$"); + newXSproto(strcpy(buf, "GetRunspeed"), XS_Mob_GetRunspeed, file, "$"); + newXSproto(strcpy(buf, "GetSTA"), XS_Mob_GetSTA, file, "$"); + newXSproto(strcpy(buf, "GetSTR"), XS_Mob_GetSTR, file, "$"); + newXSproto(strcpy(buf, "GetSize"), XS_Mob_GetSize, file, "$"); + newXSproto(strcpy(buf, "GetSkill"), XS_Mob_GetSkill, file, "$$"); + newXSproto(strcpy(buf, "GetSkillDmgTaken"), XS_Mob_GetSkillDmgTaken, file, "$$"); + newXSproto(strcpy(buf, "GetSpecialAbility"), XS_Mob_GetSpecialAbility, file, "$$"); + newXSproto(strcpy(buf, "GetSpecialAbilityParam"), XS_Mob_GetSpecialAbilityParam, file, "$$$"); + newXSproto(strcpy(buf, "GetSpecializeSkillValue"), XS_Mob_GetSpecializeSkillValue, file, "$$"); + newXSproto(strcpy(buf, "GetSpellHPBonuses"), XS_Mob_GetSpellHPBonuses, file, "$"); + newXSproto(strcpy(buf, "GetSpellIDFromSlot"), XS_Mob_GetSpellIDFromSlot, file, "$$"); + newXSproto(strcpy(buf, "GetSpellStat"), XS_Mob_GetSpellStat, file, "$$$$"); + newXSproto(strcpy(buf, "GetTarget"), XS_Mob_GetTarget, file, "$"); + newXSproto(strcpy(buf, "GetTexture"), XS_Mob_GetTexture, file, "$"); + newXSproto(strcpy(buf, "GetWIS"), XS_Mob_GetWIS, file, "$"); + newXSproto(strcpy(buf, "GetWalkspeed"), XS_Mob_GetWalkspeed, file, "$"); + newXSproto(strcpy(buf, "GetWaypointH"), XS_Mob_GetWaypointH, file, "$"); + newXSproto(strcpy(buf, "GetWaypointID"), XS_Mob_GetWaypointID, file, "$"); + newXSproto(strcpy(buf, "GetWaypointPause"), XS_Mob_GetWaypointPause, file, "$"); + newXSproto(strcpy(buf, "GetWaypointX"), XS_Mob_GetWaypointX, file, "$"); + newXSproto(strcpy(buf, "GetWaypointY"), XS_Mob_GetWaypointY, file, "$"); + newXSproto(strcpy(buf, "GetWaypointZ"), XS_Mob_GetWaypointZ, file, "$"); + newXSproto(strcpy(buf, "GetX"), XS_Mob_GetX, file, "$"); + newXSproto(strcpy(buf, "GetY"), XS_Mob_GetY, file, "$"); + newXSproto(strcpy(buf, "GetZ"), XS_Mob_GetZ, file, "$"); + newXSproto(strcpy(buf, "GetZoneID"), XS_Mob_GetZoneID, file, "$"); + newXSproto(strcpy(buf, "GoToBind"), XS_Mob_GoToBind, file, "$"); + newXSproto(strcpy(buf, "HalveAggro"), XS_Mob_HalveAggro, file, "$$"); + newXSproto(strcpy(buf, "HasNPCSpecialAtk"), XS_Mob_HasNPCSpecialAtk, file, "$$"); + newXSproto(strcpy(buf, "HasOwner"), XS_Mob_HasOwner, file, "$"); + newXSproto(strcpy(buf, "HasPet"), XS_Mob_HasPet, file, "$"); + newXSproto(strcpy(buf, "HasProcs"), XS_Mob_HasProcs, file, "$"); + newXSproto(strcpy(buf, "HasShieldEquiped"), XS_Mob_HasShieldEquiped, file, "$"); + newXSproto(strcpy(buf, "HasTwoHandBluntEquiped"), XS_Mob_HasTwoHandBluntEquiped, file, "$"); + newXSproto(strcpy(buf, "HasTwoHanderEquipped"), XS_Mob_HasTwoHanderEquipped, file, "$"); + newXSproto(strcpy(buf, "HateSummon"), XS_Mob_HateSummon, file, "$"); + newXSproto(strcpy(buf, "Heal"), XS_Mob_Heal, file, "$"); + newXSproto(strcpy(buf, "HealDamage"), XS_Mob_HealDamage, file, "$$;$"); + newXSproto(strcpy(buf, "InterruptSpell"), XS_Mob_InterruptSpell, file, "$;$"); + newXSproto(strcpy(buf, "IsAIControlled"), XS_Mob_IsAIControlled, file, "$"); + newXSproto(strcpy(buf, "IsAmnesiad"), XS_Mob_IsAmnesiad, file, "$"); + newXSproto(strcpy(buf, "IsBeacon"), XS_Mob_IsBeacon, file, "$"); + newXSproto(strcpy(buf, "IsBeneficialAllowed"), XS_Mob_IsBeneficialAllowed, file, "$$"); + newXSproto(strcpy(buf, "IsBlind"), XS_Mob_IsBlind, file, "$"); + newXSproto(strcpy(buf, "IsBot"), XS_Mob_IsBot, file, "$"); + newXSproto(strcpy(buf, "IsCasting"), XS_Mob_IsCasting, file, "$"); + newXSproto(strcpy(buf, "IsClient"), XS_Mob_IsClient, file, "$"); + newXSproto(strcpy(buf, "IsCorpse"), XS_Mob_IsCorpse, file, "$"); + newXSproto(strcpy(buf, "IsDoor"), XS_Mob_IsDoor, file, "$"); + newXSproto(strcpy(buf, "IsEliteMaterialItem"), XS_Mob_IsEliteMaterialItem, file, "$$"); + newXSproto(strcpy(buf, "IsEngaged"), XS_Mob_IsEngaged, file, "$"); + newXSproto(strcpy(buf, "IsEnraged"), XS_Mob_IsEnraged, file, "$"); + newXSproto(strcpy(buf, "IsFeared"), XS_Mob_IsFeared, file, "$"); + newXSproto(strcpy(buf, "IsHorse"), XS_Mob_IsHorse, file, "$"); + newXSproto(strcpy(buf, "IsImmuneToSpell"), XS_Mob_IsImmuneToSpell, file, "$$$"); + newXSproto(strcpy(buf, "IsInvisible"), XS_Mob_IsInvisible, file, "$;$"); + newXSproto(strcpy(buf, "IsMeleeDisabled"), XS_Mob_IsMeleeDisabled, file, "$"); + newXSproto(strcpy(buf, "IsMezzed"), XS_Mob_IsMezzed, file, "$"); + newXSproto(strcpy(buf, "IsMob"), XS_Mob_IsMob, file, "$"); + newXSproto(strcpy(buf, "IsMoving"), XS_Mob_IsMoving, file, "$"); + newXSproto(strcpy(buf, "IsNPC"), XS_Mob_IsNPC, file, "$"); + newXSproto(strcpy(buf, "IsNPCCorpse"), XS_Mob_IsNPCCorpse, file, "$"); + newXSproto(strcpy(buf, "IsObject"), XS_Mob_IsObject, file, "$"); + newXSproto(strcpy(buf, "IsPet"), XS_Mob_IsPet, file, "$"); + newXSproto(strcpy(buf, "IsPlayerCorpse"), XS_Mob_IsPlayerCorpse, file, "$"); + newXSproto(strcpy(buf, "IsRoamer"), XS_Mob_IsRoamer, file, "$"); + newXSproto(strcpy(buf, "IsRooted"), XS_Mob_IsRooted, file, "$"); + newXSproto(strcpy(buf, "IsRunning"), XS_Mob_IsRunning, file, "$"); + newXSproto(strcpy(buf, "IsSilenced"), XS_Mob_IsSilenced, file, "$"); + newXSproto(strcpy(buf, "IsStunned"), XS_Mob_IsStunned, file, "$"); + newXSproto(strcpy(buf, "IsTargetable"), XS_Mob_IsTargetable, file, "$"); + newXSproto(strcpy(buf, "IsTargeted"), XS_Mob_IsTargeted, file, "$"); + newXSproto(strcpy(buf, "IsTrap"), XS_Mob_IsTrap, file, "$"); + newXSproto(strcpy(buf, "IsWarriorClass"), XS_Mob_IsWarriorClass, file, "$"); + newXSproto(strcpy(buf, "Kill"), XS_Mob_Kill, file, "$"); + newXSproto(strcpy(buf, "MakePet"), XS_Mob_MakePet, file, "$$$;$"); + newXSproto(strcpy(buf, "MakeTempPet"), XS_Mob_MakeTempPet, file, "$$;$$$$"); + newXSproto(strcpy(buf, "Mesmerize"), XS_Mob_Mesmerize, file, "$"); + newXSproto(strcpy(buf, "Message"), XS_Mob_Message, file, "$$$;@"); + newXSproto(strcpy(buf, "Message_StringID"), XS_Mob_Message_StringID, file, "$$$;$"); + newXSproto(strcpy(buf, "ModSkillDmgTaken"), XS_Mob_ModSkillDmgTaken, file, "$$$"); + newXSproto(strcpy(buf, "ModVulnerability"), XS_Mob_ModVulnerability, file, "$$$"); + newXSproto(strcpy(buf, "NPCSpecialAttacks"), XS_Mob_NPCSpecialAttacks, file, "$$$;$$"); + newXSproto(strcpy(buf, "NavigateTo"), XS_Mob_NavigateTo, file, "$$$$"); + newXSproto(strcpy(buf, "ProcessSpecialAbilities"), XS_Mob_ProcessSpecialAbilities, file, "$$"); + newXSproto(strcpy(buf, "ProjectileAnim"), XS_Mob_ProjectileAnim, file, "$$$;$$$$$$"); + newXSproto(strcpy(buf, "RandomizeFeatures"), XS_Mob_RandomizeFeatures, file, "$$;$"); + newXSproto(strcpy(buf, "RangedAttack"), XS_Mob_RangedAttack, file, "$$"); + newXSproto(strcpy(buf, "RemoveAllNimbusEffects"), XS_Mob_RemoveAllNimbusEffects, file, "$"); + newXSproto(strcpy(buf, "RemoveFromFeignMemory"), XS_Mob_RemoveFromFeignMemory, file, "$$"); + newXSproto(strcpy(buf, "RemoveNimbusEffect"), XS_Mob_RemoveNimbusEffect, file, "$$"); + newXSproto(strcpy(buf, "RemovePet"), XS_Mob_RemovePet, file, "$"); + newXSproto(strcpy(buf, "ResistSpell"), XS_Mob_ResistSpell, file, "$$$$"); + newXSproto(strcpy(buf, "RogueAssassinate"), XS_Mob_RogueAssassinate, file, "$$"); + newXSproto(strcpy(buf, "RunTo"), XS_Mob_RunTo, file, "$$$$"); + newXSproto(strcpy(buf, "Say"), XS_Mob_Say, file, "$$;@"); + newXSproto(strcpy(buf, "SeeHide"), XS_Mob_SeeHide, file, "$"); + newXSproto(strcpy(buf, "SeeImprovedHide"), XS_Mob_SeeImprovedHide, file, "$"); + newXSproto(strcpy(buf, "SeeInvisible"), XS_Mob_SeeInvisible, file, "$"); + newXSproto(strcpy(buf, "SeeInvisibleUndead"), XS_Mob_SeeInvisibleUndead, file, "$"); + newXSproto(strcpy(buf, "SendAppearanceEffect"), XS_Mob_SendAppearanceEffect, file, "$$;$$$$"); + newXSproto(strcpy(buf, "SendIllusion"), XS_Mob_SendIllusion, file, "$$;$$$$$$$$$$$$"); + newXSproto(strcpy(buf, "SendTo"), XS_Mob_SendTo, file, "$$$$"); + newXSproto(strcpy(buf, "SendToFixZ"), XS_Mob_SendToFixZ, file, "$$$$"); + newXSproto(strcpy(buf, "SendWearChange"), XS_Mob_SendWearChange, file, "$$"); + newXSproto(strcpy(buf, "SetAA"), XS_Mob_SetAA, file, "$$$;$"); + newXSproto(strcpy(buf, "SetAllowBeneficial"), XS_Mob_SetAllowBeneficial, file, "$$"); + newXSproto(strcpy(buf, "SetAppearance"), XS_Mob_SetAppearance, file, "$$;$"); + newXSproto(strcpy(buf, "SetBodyType"), XS_Mob_SetBodyType, file, "$$;$"); + newXSproto(strcpy(buf, "SetBucket"), XS_Mob_SetBucket, file, "$$$;$"); + newXSproto(strcpy(buf, "SetCurrentWP"), XS_Mob_SetCurrentWP, file, "$$"); + newXSproto(strcpy(buf, "SetDeltas"), XS_Mob_SetDeltas, file, "$$$$$"); + newXSproto(strcpy(buf, "SetDisableMelee"), XS_Mob_SetDisableMelee, file, "$$"); + newXSproto(strcpy(buf, "SetEntityVariable"), XS_Mob_SetEntityVariable, file, "$$$"); + newXSproto(strcpy(buf, "SetExtraHaste"), XS_Mob_SetExtraHaste, file, "$$"); + newXSproto(strcpy(buf, "SetFlurryChance"), XS_Mob_SetFlurryChance, file, "$$"); + newXSproto(strcpy(buf, "SetFlyMode"), XS_Mob_SetFlyMode, file, "$$"); + newXSproto(strcpy(buf, "SetFollowID"), XS_Mob_SetFollowID, file, "$$"); + newXSproto(strcpy(buf, "SetGender"), XS_Mob_SetGender, file, "$$"); + newXSproto(strcpy(buf, "SetGlobal"), XS_Mob_SetGlobal, file, "$$$$$;$"); + newXSproto(strcpy(buf, "SetHP"), XS_Mob_SetHP, file, "$$"); + newXSproto(strcpy(buf, "SetHate"), XS_Mob_SetHate, file, "$$;$$"); + newXSproto(strcpy(buf, "SetHeading"), XS_Mob_SetHeading, file, "$$"); + newXSproto(strcpy(buf, "SetInvisible"), XS_Mob_SetInvisible, file, "$$"); + newXSproto(strcpy(buf, "SetInvul"), XS_Mob_SetInvul, file, "$$"); + newXSproto(strcpy(buf, "SetLD"), XS_Mob_SetLD, file, "$$"); + newXSproto(strcpy(buf, "SetLevel"), XS_Mob_SetLevel, file, "$$;$"); + newXSproto(strcpy(buf, "SetMana"), XS_Mob_SetMana, file, "$$"); + newXSproto(strcpy(buf, "SetMaxHP"), XS_Mob_SetMaxHP, file, "$"); + newXSproto(strcpy(buf, "SetOOCRegen"), XS_Mob_SetOOCRegen, file, "$$"); + newXSproto(strcpy(buf, "SetOwnerID"), XS_Mob_SetOwnerID, file, "$$"); + newXSproto(strcpy(buf, "SetPet"), XS_Mob_SetPet, file, "$$"); + newXSproto(strcpy(buf, "SetPetID"), XS_Mob_SetPetID, file, "$$"); + newXSproto(strcpy(buf, "SetPetOrder"), XS_Mob_SetPetOrder, file, "$$"); + newXSproto(strcpy(buf, "SetRace"), XS_Mob_SetRace, file, "$$"); + newXSproto(strcpy(buf, "SetRunAnimSpeed"), XS_Mob_SetRunAnimSpeed, file, "$$"); + newXSproto(strcpy(buf, "SetRunning"), XS_Mob_SetRunning, file, "$$"); + newXSproto(strcpy(buf, "SetSlotTint"), XS_Mob_SetSlotTint, file, "$$$$$"); + newXSproto(strcpy(buf, "SetSpecialAbility"), XS_Mob_SetSpecialAbility, file, "$$$"); + newXSproto(strcpy(buf, "SetSpecialAbilityParam"), XS_Mob_SetSpecialAbilityParam, file, "$$$$"); + newXSproto(strcpy(buf, "SetTarget"), XS_Mob_SetTarget, file, "$$"); + newXSproto(strcpy(buf, "SetTargetable"), XS_Mob_SetTargetable, file, "$$"); + newXSproto(strcpy(buf, "SetTexture"), XS_Mob_SetTexture, file, "$$"); + newXSproto(strcpy(buf, "ShieldAbility"), XS_Mob_ShieldAbility, file, "$$$$$$$$"); + newXSproto(strcpy(buf, "Shout"), XS_Mob_Shout, file, "$$;@"); + newXSproto(strcpy(buf, "SignalClient"), XS_Mob_SignalClient, file, "$$$"); + newXSproto(strcpy(buf, "SpellEffect"), XS_Mob_SpellEffect, file, "$$;$$$$$$"); + newXSproto(strcpy(buf, "SpellFinished"), XS_Mob_SpellFinished, file, "$$;$$"); + newXSproto(strcpy(buf, "Spin"), XS_Mob_Spin, file, "$"); + newXSproto(strcpy(buf, "StartEnrage"), XS_Mob_StartEnrage, file, "$"); + newXSproto(strcpy(buf, "StopNavigation"), XS_Mob_StopNavigation, file, "$"); + newXSproto(strcpy(buf, "Stun"), XS_Mob_Stun, file, "$$"); + newXSproto(strcpy(buf, "TarGlobal"), XS_Mob_TarGlobal, file, "$$$$$$$"); + newXSproto(strcpy(buf, "TempName"), XS_Mob_TempName, file, "$:$"); + newXSproto(strcpy(buf, "ThrowingAttack"), XS_Mob_ThrowingAttack, file, "$$"); + newXSproto(strcpy(buf, "TryMoveAlong"), XS_Mob_TryMoveAlong, file, "$$$;$"); + newXSproto(strcpy(buf, "TypesTempPet"), XS_Mob_TypesTempPet, file, "$$;$$$$$"); + newXSproto(strcpy(buf, "WalkTo"), XS_Mob_WalkTo, file, "$$$$"); + newXSproto(strcpy(buf, "WearChange"), XS_Mob_WearChange, file, "$$$;$$"); + newXSproto(strcpy(buf, "WipeHateList"), XS_Mob_WipeHateList, file, "$"); XSRETURN_YES; } diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index d22962fbb..a145bf0b0 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -1,30 +1,3 @@ -/* -* This file was generated automatically by xsubpp version 1.9508 from the -* contents of tmp. Do not edit this file, edit tmp instead. -* -* ANY CHANGES MADE HERE WILL BE LOST! -* -*/ - - -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2004 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 -*/ - #include "../common/features.h" #ifdef EMBPERL_XS_CLASSES @@ -1886,114 +1859,113 @@ XS(boot_NPC) { //add the strcpy stuff to get rid of const warnings.... XS_VERSION_BOOTCHECK; - - newXSproto(strcpy(buf, "SignalNPC"), XS_NPC_SignalNPC, file, "$$"); - newXSproto(strcpy(buf, "CheckNPCFactionAlly"), XS_NPC_CheckNPCFactionAlly, file, "$$"); + newXSproto(strcpy(buf, "AI_SetRoambox"), XS_NPC_AI_SetRoambox, file, "$$$$$$;$$"); + newXSproto(strcpy(buf, "AddAISpell"), XS_NPC_AddSpellToNPCList, file, "$$$$$$$"); + newXSproto(strcpy(buf, "AddCash"), XS_NPC_AddCash, file, "$$$$$"); + newXSproto(strcpy(buf, "AddDefensiveProc"), XS_NPC_AddDefensiveProc, file, "$$$"); newXSproto(strcpy(buf, "AddItem"), XS_NPC_AddItem, file, "$$;$$$$$$$$"); newXSproto(strcpy(buf, "AddLootTable"), XS_NPC_AddLootTable, file, "$"); - newXSproto(strcpy(buf, "RemoveItem"), XS_NPC_RemoveItem, file, "$$;$$"); - newXSproto(strcpy(buf, "ClearItemList"), XS_NPC_ClearItemList, file, "$"); - newXSproto(strcpy(buf, "AddCash"), XS_NPC_AddCash, file, "$$$$$"); - newXSproto(strcpy(buf, "RemoveCash"), XS_NPC_RemoveCash, file, "$"); - newXSproto(strcpy(buf, "CountLoot"), XS_NPC_CountLoot, file, "$"); - newXSproto(strcpy(buf, "GetLoottableID"), XS_NPC_GetLoottableID, file, "$"); - newXSproto(strcpy(buf, "GetCopper"), XS_NPC_GetCopper, file, "$"); - newXSproto(strcpy(buf, "GetSilver"), XS_NPC_GetSilver, file, "$"); - newXSproto(strcpy(buf, "GetGold"), XS_NPC_GetGold, file, "$"); - newXSproto(strcpy(buf, "GetPlatinum"), XS_NPC_GetPlatinum, file, "$"); - newXSproto(strcpy(buf, "SetCopper"), XS_NPC_SetCopper, file, "$$"); - newXSproto(strcpy(buf, "SetSilver"), XS_NPC_SetSilver, file, "$$"); - newXSproto(strcpy(buf, "SetGold"), XS_NPC_SetGold, file, "$$"); - newXSproto(strcpy(buf, "SetPlatinum"), XS_NPC_SetPlatinum, file, "$$"); - newXSproto(strcpy(buf, "SetGrid"), XS_NPC_SetGrid, file, "$$"); - newXSproto(strcpy(buf, "SetSaveWaypoint"), XS_NPC_SetSaveWaypoint, file, "$$"); - newXSproto(strcpy(buf, "SetSp2"), XS_NPC_SetSp2, file, "$$"); - newXSproto(strcpy(buf, "GetWaypointMax"), XS_NPC_GetWaypointMax, file, "$"); - newXSproto(strcpy(buf, "GetGrid"), XS_NPC_GetGrid, file, "$"); - newXSproto(strcpy(buf, "GetSp2"), XS_NPC_GetSp2, file, "$"); - newXSproto(strcpy(buf, "GetNPCFactionID"), XS_NPC_GetNPCFactionID, file, "$"); - newXSproto(strcpy(buf, "GetPrimaryFaction"), XS_NPC_GetPrimaryFaction, file, "$"); - newXSproto(strcpy(buf, "GetNPCHate"), XS_NPC_GetNPCHate, file, "$$"); - newXSproto(strcpy(buf, "IsOnHatelist"), XS_NPC_IsOnHatelist, file, "$$"); - newXSproto(strcpy(buf, "RemoveFromHateList"), XS_NPC_RemoveFromHateList, file, "$$"); - newXSproto(strcpy(buf, "SetNPCFactionID"), XS_NPC_SetNPCFactionID, file, "$$"); - newXSproto(strcpy(buf, "GetMaxDMG"), XS_NPC_GetMaxDMG, file, "$"); - newXSproto(strcpy(buf, "GetMinDMG"), XS_NPC_GetMinDMG, file, "$"); - newXSproto(strcpy(buf, "IsAnimal"), XS_NPC_IsAnimal, file, "$"); - newXSproto(strcpy(buf, "GetPetSpellID"), XS_NPC_GetPetSpellID, file, "$"); - newXSproto(strcpy(buf, "SetPetSpellID"), XS_NPC_SetPetSpellID, file, "$$"); - newXSproto(strcpy(buf, "GetMaxDamage"), XS_NPC_GetMaxDamage, file, "$$"); - newXSproto(strcpy(buf, "SetTaunting"), XS_NPC_SetTaunting, file, "$$"); - newXSproto(strcpy(buf, "IsTaunting"), XS_NPC_IsTaunting, file, "$"); - newXSproto(strcpy(buf, "PickPocket"), XS_NPC_PickPocket, file, "$$"); - newXSproto(strcpy(buf, "StartSwarmTimer"), XS_NPC_StartSwarmTimer, file, "$$"); - newXSproto(strcpy(buf, "DoClassAttacks"), XS_NPC_DoClassAttacks, file, "$$"); - newXSproto(strcpy(buf, "GetMaxWp"), XS_NPC_GetMaxWp, file, "$"); - newXSproto(strcpy(buf, "DisplayWaypointInfo"), XS_NPC_DisplayWaypointInfo, file, "$$"); - newXSproto(strcpy(buf, "CalculateNewWaypoint"), XS_NPC_CalculateNewWaypoint, file, "$"); + newXSproto(strcpy(buf, "AddMeleeProc"), XS_NPC_AddMeleeProc, file, "$$$"); + newXSproto(strcpy(buf, "AddRangedProc"), XS_NPC_AddRangedProc, file, "$$$"); newXSproto(strcpy(buf, "AssignWaypoints"), XS_NPC_AssignWaypoints, file, "$$"); - newXSproto(strcpy(buf, "SetWaypointPause"), XS_NPC_SetWaypointPause, file, "$"); - newXSproto(strcpy(buf, "UpdateWaypoint"), XS_NPC_UpdateWaypoint, file, "$$"); - newXSproto(strcpy(buf, "StopWandering"), XS_NPC_StopWandering, file, "$"); - newXSproto(strcpy(buf, "ResumeWandering"), XS_NPC_ResumeWandering, file, "$"); - newXSproto(strcpy(buf, "PauseWandering"), XS_NPC_PauseWandering, file, "$$"); - newXSproto(strcpy(buf, "MoveTo"), XS_NPC_MoveTo, file, "$$$$"); - newXSproto(strcpy(buf, "NextGuardPosition"), XS_NPC_NextGuardPosition, file, "$"); - newXSproto(strcpy(buf, "SaveGuardSpot"), XS_NPC_SaveGuardSpot, file, "$$$$$"); - newXSproto(strcpy(buf, "IsGuarding"), XS_NPC_IsGuarding, file, "$"); - newXSproto(strcpy(buf, "AI_SetRoambox"), XS_NPC_AI_SetRoambox, file, "$$$$$$;$$"); + newXSproto(strcpy(buf, "CalculateNewWaypoint"), XS_NPC_CalculateNewWaypoint, file, "$"); + newXSproto(strcpy(buf, "ChangeLastName"), XS_NPC_ChangeLastName, file, "$:$"); + newXSproto(strcpy(buf, "CheckNPCFactionAlly"), XS_NPC_CheckNPCFactionAlly, file, "$$"); + newXSproto(strcpy(buf, "ClearItemList"), XS_NPC_ClearItemList, file, "$"); + newXSproto(strcpy(buf, "ClearLastName"), XS_NPC_ClearLastName, file, "$"); + newXSproto(strcpy(buf, "CountItem"), XS_NPC_CountItem, file, "$$"); + newXSproto(strcpy(buf, "CountLoot"), XS_NPC_CountLoot, file, "$"); + newXSproto(strcpy(buf, "DisplayWaypointInfo"), XS_NPC_DisplayWaypointInfo, file, "$$"); + newXSproto(strcpy(buf, "DoClassAttacks"), XS_NPC_DoClassAttacks, file, "$$"); + newXSproto(strcpy(buf, "GetAccuracyRating"), XS_NPC_GetAccuracyRating, file, "$"); + newXSproto(strcpy(buf, "GetAttackDelay"), XS_NPC_GetAttackDelay, file, "$"); + newXSproto(strcpy(buf, "GetAttackSpeed"), XS_NPC_GetAttackSpeed, file, "$"); + newXSproto(strcpy(buf, "GetAvoidanceRating"), XS_NPC_GetAvoidanceRating, file, "$"); + newXSproto(strcpy(buf, "GetCombatState"), XS_NPC_GetCombatState, file, "$"); + newXSproto(strcpy(buf, "GetCopper"), XS_NPC_GetCopper, file, "$"); + newXSproto(strcpy(buf, "GetFirstSlotByItemID"), XS_NPC_GetFirstSlotByItemID, file, "$$"); + newXSproto(strcpy(buf, "GetGold"), XS_NPC_GetGold, file, "$"); + newXSproto(strcpy(buf, "GetGrid"), XS_NPC_GetGrid, file, "$"); + newXSproto(strcpy(buf, "GetGuardPointX"), XS_NPC_GetGuardPointX, file, "$"); + newXSproto(strcpy(buf, "GetGuardPointY"), XS_NPC_GetGuardPointY, file, "$"); + newXSproto(strcpy(buf, "GetGuardPointZ"), XS_NPC_GetGuardPointZ, file, "$"); + newXSproto(strcpy(buf, "GetHealScale"), XS_NPC_GetHealScale, file, "$"); + newXSproto(strcpy(buf, "GetItemIDBySlot"), XS_NPC_GetItemIDBySlot, file, "$$"); + newXSproto(strcpy(buf, "GetLootList"), XS_NPC_GetLootList, file, "$"); + newXSproto(strcpy(buf, "GetLoottableID"), XS_NPC_GetLoottableID, file, "$"); + newXSproto(strcpy(buf, "GetMaxDMG"), XS_NPC_GetMaxDMG, file, "$"); + newXSproto(strcpy(buf, "GetMaxDamage"), XS_NPC_GetMaxDamage, file, "$$"); + newXSproto(strcpy(buf, "GetMaxWp"), XS_NPC_GetMaxWp, file, "$"); + newXSproto(strcpy(buf, "GetMinDMG"), XS_NPC_GetMinDMG, file, "$"); + newXSproto(strcpy(buf, "GetNPCFactionID"), XS_NPC_GetNPCFactionID, file, "$"); + newXSproto(strcpy(buf, "GetNPCHate"), XS_NPC_GetNPCHate, file, "$$"); newXSproto(strcpy(buf, "GetNPCSpellsID"), XS_NPC_GetNPCSpellsID, file, "$"); + newXSproto(strcpy(buf, "GetPetSpellID"), XS_NPC_GetPetSpellID, file, "$"); + newXSproto(strcpy(buf, "GetPlatinum"), XS_NPC_GetPlatinum, file, "$"); + newXSproto(strcpy(buf, "GetPrimSkill"), XS_NPC_GetPrimSkill, file, "$"); + newXSproto(strcpy(buf, "GetPrimaryFaction"), XS_NPC_GetPrimaryFaction, file, "$"); + newXSproto(strcpy(buf, "GetScore"), XS_NPC_GetScore, file, "$"); + newXSproto(strcpy(buf, "GetSecSkill"), XS_NPC_GetSecSkill, file, "$"); + newXSproto(strcpy(buf, "GetSilver"), XS_NPC_GetSilver, file, "$"); + newXSproto(strcpy(buf, "GetSlowMitigation"), XS_NPC_GetSlowMitigation, file, "$"); + newXSproto(strcpy(buf, "GetSp2"), XS_NPC_GetSp2, file, "$"); + newXSproto(strcpy(buf, "GetSpawnKillCount"), XS_NPC_GetSpawnKillCount, file, "$"); + newXSproto(strcpy(buf, "GetSpawnPointH"), XS_NPC_GetSpawnPointH, file, "$"); newXSproto(strcpy(buf, "GetSpawnPointID"), XS_NPC_GetSpawnPointID, file, "$"); newXSproto(strcpy(buf, "GetSpawnPointX"), XS_NPC_GetSpawnPointX, file, "$"); newXSproto(strcpy(buf, "GetSpawnPointY"), XS_NPC_GetSpawnPointY, file, "$"); newXSproto(strcpy(buf, "GetSpawnPointZ"), XS_NPC_GetSpawnPointZ, file, "$"); - newXSproto(strcpy(buf, "GetSpawnPointH"), XS_NPC_GetSpawnPointH, file, "$"); - newXSproto(strcpy(buf, "GetGuardPointX"), XS_NPC_GetGuardPointX, file, "$"); - newXSproto(strcpy(buf, "GetGuardPointY"), XS_NPC_GetGuardPointY, file, "$"); - newXSproto(strcpy(buf, "GetGuardPointZ"), XS_NPC_GetGuardPointZ, file, "$"); - newXSproto(strcpy(buf, "SetPrimSkill"), XS_NPC_SetPrimSkill, file, "$$"); - newXSproto(strcpy(buf, "SetSecSkill"), XS_NPC_SetSecSkill, file, "$$"); - newXSproto(strcpy(buf, "GetPrimSkill"), XS_NPC_GetPrimSkill, file, "$"); - newXSproto(strcpy(buf, "GetSecSkill"), XS_NPC_GetSecSkill, file, "$"); - newXSproto(strcpy(buf, "GetSwarmOwner"), XS_NPC_GetSwarmOwner, file, "$"); - newXSproto(strcpy(buf, "GetSwarmTarget"), XS_NPC_GetSwarmTarget, file, "$"); - newXSproto(strcpy(buf, "SetSwarmTarget"), XS_NPC_SetSwarmTarget, file, "$$"); - newXSproto(strcpy(buf, "ModifyNPCStat"), XS_NPC_ModifyNPCStat, file, "$$$"); - newXSproto(strcpy(buf, "AddAISpell"), XS_NPC_AddSpellToNPCList, file, "$$$$$$$"); - newXSproto(strcpy(buf, "RemoveAISpell"), XS_NPC_RemoveSpellFromNPCList, file, "$$"); - newXSproto(strcpy(buf, "SetSpellFocusDMG"), XS_NPC_SetSpellFocusDMG, file, "$$"); - newXSproto(strcpy(buf, "SetSpellFocusHeal"), XS_NPC_SetSpellFocusHeal, file, "$$"); newXSproto(strcpy(buf, "GetSpellFocusDMG"), XS_NPC_GetSpellFocusDMG, file, "$"); newXSproto(strcpy(buf, "GetSpellFocusHeal"), XS_NPC_GetSpellFocusHeal, file, "$"); - newXSproto(strcpy(buf, "GetSlowMitigation"), XS_NPC_GetSlowMitigation, file, "$"); - newXSproto(strcpy(buf, "GetAttackSpeed"), XS_NPC_GetAttackSpeed, file, "$"); - newXSproto(strcpy(buf, "GetAttackDelay"), XS_NPC_GetAttackDelay, file, "$"); - newXSproto(strcpy(buf, "GetAccuracyRating"), XS_NPC_GetAccuracyRating, file, "$"); - newXSproto(strcpy(buf, "GetAvoidanceRating"), XS_NPC_GetAvoidanceRating, file, "$"); - newXSproto(strcpy(buf, "GetSpawnKillCount"), XS_NPC_GetSpawnKillCount, file, "$"); - newXSproto(strcpy(buf, "GetScore"), XS_NPC_GetScore, file, "$"); - newXSproto(strcpy(buf, "MerchantOpenShop"), XS_NPC_MerchantOpenShop, file, "$"); + newXSproto(strcpy(buf, "GetSpellScale"), XS_NPC_GetSpellScale, file, "$"); + newXSproto(strcpy(buf, "GetSwarmOwner"), XS_NPC_GetSwarmOwner, file, "$"); + newXSproto(strcpy(buf, "GetSwarmTarget"), XS_NPC_GetSwarmTarget, file, "$"); + newXSproto(strcpy(buf, "GetWaypointMax"), XS_NPC_GetWaypointMax, file, "$"); + newXSproto(strcpy(buf, "HasItem"), XS_NPC_HasItem, file, "$$"); + newXSproto(strcpy(buf, "IsAnimal"), XS_NPC_IsAnimal, file, "$"); + newXSproto(strcpy(buf, "IsGuarding"), XS_NPC_IsGuarding, file, "$"); + newXSproto(strcpy(buf, "IsOnHatelist"), XS_NPC_IsOnHatelist, file, "$$"); + newXSproto(strcpy(buf, "IsRaidTarget"), XS_NPC_IsRaidTarget, file, "$"); + newXSproto(strcpy(buf, "IsTaunting"), XS_NPC_IsTaunting, file, "$"); newXSproto(strcpy(buf, "MerchantCloseShop"), XS_NPC_MerchantCloseShop, file, "$"); - newXSproto(strcpy(buf, "AddMeleeProc"), XS_NPC_AddMeleeProc, file, "$$$"); - newXSproto(strcpy(buf, "AddRangedProc"), XS_NPC_AddRangedProc, file, "$$$"); - newXSproto(strcpy(buf, "AddDefensiveProc"), XS_NPC_AddDefensiveProc, file, "$$$"); + newXSproto(strcpy(buf, "MerchantOpenShop"), XS_NPC_MerchantOpenShop, file, "$"); + newXSproto(strcpy(buf, "ModifyNPCStat"), XS_NPC_ModifyNPCStat, file, "$$$"); + newXSproto(strcpy(buf, "MoveTo"), XS_NPC_MoveTo, file, "$$$$"); + newXSproto(strcpy(buf, "NextGuardPosition"), XS_NPC_NextGuardPosition, file, "$"); + newXSproto(strcpy(buf, "PauseWandering"), XS_NPC_PauseWandering, file, "$$"); + newXSproto(strcpy(buf, "PickPocket"), XS_NPC_PickPocket, file, "$$"); + newXSproto(strcpy(buf, "RecalculateSkills"), XS_NPC_RecalculateSkills, file, "$"); + newXSproto(strcpy(buf, "RemoveAISpell"), XS_NPC_RemoveSpellFromNPCList, file, "$$"); + newXSproto(strcpy(buf, "RemoveCash"), XS_NPC_RemoveCash, file, "$"); + newXSproto(strcpy(buf, "RemoveDefensiveProc"), XS_NPC_RemoveDefensiveProc, file, "$$"); + newXSproto(strcpy(buf, "RemoveFromHateList"), XS_NPC_RemoveFromHateList, file, "$$"); + newXSproto(strcpy(buf, "RemoveItem"), XS_NPC_RemoveItem, file, "$$;$$"); newXSproto(strcpy(buf, "RemoveMeleeProc"), XS_NPC_RemoveMeleeProc, file, "$$"); newXSproto(strcpy(buf, "RemoveRangedProc"), XS_NPC_RemoveRangedProc, file, "$$"); - newXSproto(strcpy(buf, "RemoveDefensiveProc"), XS_NPC_RemoveDefensiveProc, file, "$$"); - newXSproto(strcpy(buf, "ChangeLastName"), XS_NPC_ChangeLastName, file, "$:$"); - newXSproto(strcpy(buf, "ClearLastName"), XS_NPC_ClearLastName, file, "$"); - newXSproto(strcpy(buf, "GetCombatState"), XS_NPC_GetCombatState, file, "$"); - newXSproto(strcpy(buf, "SetSimpleRoamBox"), XS_NPC_SetSimpleRoamBox, file, "$$;$$"); - newXSproto(strcpy(buf, "RecalculateSkills"), XS_NPC_RecalculateSkills, file, "$"); + newXSproto(strcpy(buf, "ResumeWandering"), XS_NPC_ResumeWandering, file, "$"); + newXSproto(strcpy(buf, "SaveGuardSpot"), XS_NPC_SaveGuardSpot, file, "$$$$$"); newXSproto(strcpy(buf, "ScaleNPC"), XS_NPC_ScaleNPC, file, "$$"); - newXSproto(strcpy(buf, "IsRaidTarget"), XS_NPC_IsRaidTarget, file, "$"); - newXSproto(strcpy(buf, "HasItem"), XS_NPC_HasItem, file, "$$"); - newXSproto(strcpy(buf, "CountItem"), XS_NPC_CountItem, file, "$$"); - newXSproto(strcpy(buf, "GetItemIDBySlot"), XS_NPC_GetItemIDBySlot, file, "$$"); - newXSproto(strcpy(buf, "GetFirstSlotByItemID"), XS_NPC_GetFirstSlotByItemID, file, "$$"); - newXSproto(strcpy(buf, "GetHealScale"), XS_NPC_GetHealScale, file, "$"); - newXSproto(strcpy(buf, "GetSpellScale"), XS_NPC_GetSpellScale, file, "$"); - newXSproto(strcpy(buf, "GetLootList"), XS_NPC_GetLootList, file, "$"); + newXSproto(strcpy(buf, "SetCopper"), XS_NPC_SetCopper, file, "$$"); + newXSproto(strcpy(buf, "SetGold"), XS_NPC_SetGold, file, "$$"); + newXSproto(strcpy(buf, "SetGrid"), XS_NPC_SetGrid, file, "$$"); + newXSproto(strcpy(buf, "SetNPCFactionID"), XS_NPC_SetNPCFactionID, file, "$$"); + newXSproto(strcpy(buf, "SetPetSpellID"), XS_NPC_SetPetSpellID, file, "$$"); + newXSproto(strcpy(buf, "SetPlatinum"), XS_NPC_SetPlatinum, file, "$$"); + newXSproto(strcpy(buf, "SetPrimSkill"), XS_NPC_SetPrimSkill, file, "$$"); + newXSproto(strcpy(buf, "SetSaveWaypoint"), XS_NPC_SetSaveWaypoint, file, "$$"); + newXSproto(strcpy(buf, "SetSecSkill"), XS_NPC_SetSecSkill, file, "$$"); + newXSproto(strcpy(buf, "SetSilver"), XS_NPC_SetSilver, file, "$$"); + newXSproto(strcpy(buf, "SetSimpleRoamBox"), XS_NPC_SetSimpleRoamBox, file, "$$;$$"); + newXSproto(strcpy(buf, "SetSp2"), XS_NPC_SetSp2, file, "$$"); + newXSproto(strcpy(buf, "SetSpellFocusDMG"), XS_NPC_SetSpellFocusDMG, file, "$$"); + newXSproto(strcpy(buf, "SetSpellFocusHeal"), XS_NPC_SetSpellFocusHeal, file, "$$"); + newXSproto(strcpy(buf, "SetSwarmTarget"), XS_NPC_SetSwarmTarget, file, "$$"); + newXSproto(strcpy(buf, "SetTaunting"), XS_NPC_SetTaunting, file, "$$"); + newXSproto(strcpy(buf, "SetWaypointPause"), XS_NPC_SetWaypointPause, file, "$"); + newXSproto(strcpy(buf, "SignalNPC"), XS_NPC_SignalNPC, file, "$$"); + newXSproto(strcpy(buf, "StartSwarmTimer"), XS_NPC_StartSwarmTimer, file, "$$"); + newXSproto(strcpy(buf, "StopWandering"), XS_NPC_StopWandering, file, "$"); + newXSproto(strcpy(buf, "UpdateWaypoint"), XS_NPC_UpdateWaypoint, file, "$$"); XSRETURN_YES; } diff --git a/zone/perl_object.cpp b/zone/perl_object.cpp index c1e5d76db..fac167bc7 100644 --- a/zone/perl_object.cpp +++ b/zone/perl_object.cpp @@ -1,30 +1,3 @@ -/* -* This file was generated automatically by xsubpp version 1.9508 from the -* contents of tmp. Do not edit this file, edit tmp instead. -* -* ANY CHANGES MADE HERE WILL BE LOST! -* -*/ - - -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2004 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 -*/ - #include "../common/features.h" #ifdef EMBPERL_XS_CLASSES @@ -740,47 +713,47 @@ XS(boot_Object) { //add the strcpy stuff to get rid of const warnings.... XS_VERSION_BOOTCHECK; + newXSproto(strcpy(buf, "ClearUser"), XS_Object_ClearUser, file, "$"); + newXSproto(strcpy(buf, "Close"), XS_Object_Close, file, "$"); + newXSproto(strcpy(buf, "Delete"), XS_Object_Delete, file, "$$"); + newXSproto(strcpy(buf, "DeleteItem"), XS_Object_DeleteItem, file, "$$"); newXSproto(strcpy(buf, "Depop"), XS_Object_Depop, file, "$"); - newXSproto(strcpy(buf, "Repop"), XS_Object_Repop, file, "$"); - newXSproto(strcpy(buf, "SetModelName"), XS_Object_SetModelName, file, "$$"); + newXSproto(strcpy(buf, "EntityVariableExists"), XS_Object_EntityVariableExists, file, "$$"); + newXSproto(strcpy(buf, "GetDBID"), XS_Object_GetDBID, file, "$"); + newXSproto(strcpy(buf, "GetEntityVariable"), XS_Object_GetEntityVariable, file, "$$"); + newXSproto(strcpy(buf, "GetHeading"), XS_Object_GetHeading, file, "$"); + newXSproto(strcpy(buf, "GetID"), XS_Object_GetID, file, "$"); + newXSproto(strcpy(buf, "GetIcon"), XS_Object_GetIcon, file, "$"); + newXSproto(strcpy(buf, "GetItemID"), XS_Object_GetItemID, file, "$"); newXSproto(strcpy(buf, "GetModelName"), XS_Object_GetModelName, file, "$"); + newXSproto(strcpy(buf, "GetSize"), XS_Object_GetSize, file, "$"); + newXSproto(strcpy(buf, "GetSolidType"), XS_Object_GetSolidType, file, "$"); + newXSproto(strcpy(buf, "GetTiltX"), XS_Object_GetTiltX, file, "$$"); + newXSproto(strcpy(buf, "GetTiltY"), XS_Object_GetTiltY, file, "$"); + newXSproto(strcpy(buf, "GetType"), XS_Object_GetType, file, "$"); newXSproto(strcpy(buf, "GetX"), XS_Object_GetX, file, "$"); newXSproto(strcpy(buf, "GetY"), XS_Object_GetY, file, "$"); newXSproto(strcpy(buf, "GetZ"), XS_Object_GetZ, file, "$"); - newXSproto(strcpy(buf, "GetHeading"), XS_Object_GetHeading, file, "$"); + newXSproto(strcpy(buf, "IsGroundSpawn"), XS_Object_IsGroundSpawn, file, "$"); + newXSproto(strcpy(buf, "Repop"), XS_Object_Repop, file, "$"); + newXSproto(strcpy(buf, "Save"), XS_Object_Save, file, "$"); + newXSproto(strcpy(buf, "SetEntityVariable"), XS_Object_SetEntityVariable, file, "$$$"); + newXSproto(strcpy(buf, "SetHeading"), XS_Object_SetHeading, file, "$$"); + newXSproto(strcpy(buf, "SetID"), XS_Object_SetID, file, "$$"); + newXSproto(strcpy(buf, "SetIcon"), XS_Object_SetIcon, file, "$$"); + newXSproto(strcpy(buf, "SetItemID"), XS_Object_SetItemID, file, "$$"); + newXSproto(strcpy(buf, "SetLocation"), XS_Object_SetLocation, file, "$$$$"); + newXSproto(strcpy(buf, "SetModelName"), XS_Object_SetModelName, file, "$$"); + newXSproto(strcpy(buf, "SetSize"), XS_Object_SetSize, file, "$$"); + newXSproto(strcpy(buf, "SetSolidType"), XS_Object_SetSolidType, file, "$$"); + newXSproto(strcpy(buf, "SetTiltX"), XS_Object_SetTiltX, file, "$$"); + newXSproto(strcpy(buf, "SetTiltY"), XS_Object_SetTiltY, file, "$"); + newXSproto(strcpy(buf, "SetType"), XS_Object_SetType, file, "$$"); newXSproto(strcpy(buf, "SetX"), XS_Object_SetX, file, "$$"); newXSproto(strcpy(buf, "SetY"), XS_Object_SetY, file, "$$"); newXSproto(strcpy(buf, "SetZ"), XS_Object_SetZ, file, "$$"); - newXSproto(strcpy(buf, "SetHeading"), XS_Object_SetHeading, file, "$$"); - newXSproto(strcpy(buf, "SetLocation"), XS_Object_SetLocation, file, "$$$$"); - newXSproto(strcpy(buf, "SetItemID"), XS_Object_SetItemID, file, "$$"); - newXSproto(strcpy(buf, "GetItemID"), XS_Object_GetItemID, file, "$"); - newXSproto(strcpy(buf, "SetIcon"), XS_Object_SetIcon, file, "$$"); - newXSproto(strcpy(buf, "GetIcon"), XS_Object_GetIcon, file, "$"); - newXSproto(strcpy(buf, "SetType"), XS_Object_SetType, file, "$$"); - newXSproto(strcpy(buf, "GetType"), XS_Object_GetType, file, "$"); - newXSproto(strcpy(buf, "GetDBID"), XS_Object_GetDBID, file, "$"); - newXSproto(strcpy(buf, "ClearUser"), XS_Object_ClearUser, file, "$"); - newXSproto(strcpy(buf, "SetID"), XS_Object_SetID, file, "$$"); - newXSproto(strcpy(buf, "GetID"), XS_Object_GetID, file, "$"); - newXSproto(strcpy(buf, "Save"), XS_Object_Save, file, "$"); - newXSproto(strcpy(buf, "VarSave"), XS_Object_VarSave, file, "$"); - newXSproto(strcpy(buf, "DeleteItem"), XS_Object_DeleteItem, file, "$$"); newXSproto(strcpy(buf, "StartDecay"), XS_Object_StartDecay, file, "$$"); - newXSproto(strcpy(buf, "Delete"), XS_Object_Delete, file, "$$"); - newXSproto(strcpy(buf, "IsGroundSpawn"), XS_Object_IsGroundSpawn, file, "$"); - newXSproto(strcpy(buf, "Close"), XS_Object_Close, file, "$"); - newXSproto(strcpy(buf, "GetEntityVariable"), XS_Object_GetEntityVariable, file, "$$"); - newXSproto(strcpy(buf, "SetEntityVariable"), XS_Object_SetEntityVariable, file, "$$$"); - newXSproto(strcpy(buf, "EntityVariableExists"), XS_Object_EntityVariableExists, file, "$$"); - newXSproto(strcpy(buf, "SetSolidType"), XS_Object_SetSolidType, file, "$$"); - newXSproto(strcpy(buf, "GetSolidType"), XS_Object_GetSolidType, file, "$"); - newXSproto(strcpy(buf, "SetSize"), XS_Object_SetSize, file, "$$"); - newXSproto(strcpy(buf, "GetSize"), XS_Object_GetSize, file, "$"); - newXSproto(strcpy(buf, "SetTiltX"), XS_Object_SetTiltX, file, "$$"); - newXSproto(strcpy(buf, "SetTiltY"), XS_Object_SetTiltY, file, "$"); - newXSproto(strcpy(buf, "GetTiltX"), XS_Object_GetTiltX, file, "$$"); - newXSproto(strcpy(buf, "GetTiltY"), XS_Object_GetTiltY, file, "$"); + newXSproto(strcpy(buf, "VarSave"), XS_Object_VarSave, file, "$"); XSRETURN_YES; } #endif //EMBPERL_XS_CLASSES diff --git a/zone/perl_perlpacket.cpp b/zone/perl_perlpacket.cpp index 36fb97440..e4965d2a8 100644 --- a/zone/perl_perlpacket.cpp +++ b/zone/perl_perlpacket.cpp @@ -1,30 +1,3 @@ -/* -* This file was generated automatically by xsubpp version 1.9508 from the -* contents of tmp. Do not edit this file, edit tmp instead. -* -* ANY CHANGES MADE HERE WILL BE LOST! -* -*/ - - -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2004 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 -*/ - #include "../common/features.h" #ifdef EMBPERL_XS_CLASSES #include "../common/global_define.h" @@ -420,29 +393,26 @@ XS(boot_PerlPacket) //add the strcpy stuff to get rid of const warnings.... - - XS_VERSION_BOOTCHECK ; - - newXSproto(strcpy(buf, "new"), XS_PerlPacket_new, file, "$;$$"); - newXSproto(strcpy(buf, "DESTROY"), XS_PerlPacket_DESTROY, file, "$"); - newXSproto(strcpy(buf, "SetOpcode"), XS_PerlPacket_SetOpcode, file, "$$"); - newXSproto(strcpy(buf, "Resize"), XS_PerlPacket_Resize, file, "$$"); - newXSproto(strcpy(buf, "SendTo"), XS_PerlPacket_SendTo, file, "$$"); - newXSproto(strcpy(buf, "SendToAll"), XS_PerlPacket_SendToAll, file, "$"); - newXSproto(strcpy(buf, "Zero"), XS_PerlPacket_Zero, file, "$"); - newXSproto(strcpy(buf, "FromArray"), XS_PerlPacket_FromArray, file, "$$$"); - newXSproto(strcpy(buf, "SetByte"), XS_PerlPacket_SetByte, file, "$$$"); - newXSproto(strcpy(buf, "SetShort"), XS_PerlPacket_SetShort, file, "$$$"); - newXSproto(strcpy(buf, "SetLong"), XS_PerlPacket_SetLong, file, "$$$"); - newXSproto(strcpy(buf, "SetFloat"), XS_PerlPacket_SetFloat, file, "$$$"); - newXSproto(strcpy(buf, "SetString"), XS_PerlPacket_SetString, file, "$$$"); - newXSproto(strcpy(buf, "SetEQ1319"), XS_PerlPacket_SetEQ1319, file, "$$$$"); - newXSproto(strcpy(buf, "SetEQ1913"), XS_PerlPacket_SetEQ1913, file, "$$$$"); - newXSproto(strcpy(buf, "GetByte"), XS_PerlPacket_GetByte, file, "$$"); - newXSproto(strcpy(buf, "GetShort"), XS_PerlPacket_GetShort, file, "$$"); - newXSproto(strcpy(buf, "GetLong"), XS_PerlPacket_GetLong, file, "$$"); - newXSproto(strcpy(buf, "GetFloat"), XS_PerlPacket_GetFloat, file, "$$"); + newXSproto(strcpy(buf, "DESTROY"), XS_PerlPacket_DESTROY, file, "$"); + newXSproto(strcpy(buf, "FromArray"), XS_PerlPacket_FromArray, file, "$$$"); + newXSproto(strcpy(buf, "GetByte"), XS_PerlPacket_GetByte, file, "$$"); + newXSproto(strcpy(buf, "GetFloat"), XS_PerlPacket_GetFloat, file, "$$"); + newXSproto(strcpy(buf, "GetLong"), XS_PerlPacket_GetLong, file, "$$"); + newXSproto(strcpy(buf, "GetShort"), XS_PerlPacket_GetShort, file, "$$"); + newXSproto(strcpy(buf, "Resize"), XS_PerlPacket_Resize, file, "$$"); + newXSproto(strcpy(buf, "SendTo"), XS_PerlPacket_SendTo, file, "$$"); + newXSproto(strcpy(buf, "SendToAll"), XS_PerlPacket_SendToAll, file, "$"); + newXSproto(strcpy(buf, "SetByte"), XS_PerlPacket_SetByte, file, "$$$"); + newXSproto(strcpy(buf, "SetEQ1319"), XS_PerlPacket_SetEQ1319, file, "$$$$"); + newXSproto(strcpy(buf, "SetEQ1913"), XS_PerlPacket_SetEQ1913, file, "$$$$"); + newXSproto(strcpy(buf, "SetFloat"), XS_PerlPacket_SetFloat, file, "$$$"); + newXSproto(strcpy(buf, "SetLong"), XS_PerlPacket_SetLong, file, "$$$"); + newXSproto(strcpy(buf, "SetOpcode"), XS_PerlPacket_SetOpcode, file, "$$"); + newXSproto(strcpy(buf, "SetShort"), XS_PerlPacket_SetShort, file, "$$$"); + newXSproto(strcpy(buf, "SetString"), XS_PerlPacket_SetString, file, "$$$"); + newXSproto(strcpy(buf, "Zero"), XS_PerlPacket_Zero, file, "$"); + newXSproto(strcpy(buf, "new"), XS_PerlPacket_new, file, "$;$$"); XSRETURN_YES; } diff --git a/zone/perl_player_corpse.cpp b/zone/perl_player_corpse.cpp index f4e85c214..3d9e5b85a 100644 --- a/zone/perl_player_corpse.cpp +++ b/zone/perl_player_corpse.cpp @@ -1,30 +1,3 @@ -/* -* This file was generated automatically by xsubpp version 1.9508 from the -* contents of tmp. Do not edit this file, edit tmp instead. -* -* ANY CHANGES MADE HERE WILL BE LOST! -* -*/ - - -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2004 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 -*/ - #include "../common/features.h" #ifdef EMBPERL_XS_CLASSES @@ -657,41 +630,40 @@ XS(boot_Corpse) { //add the strcpy stuff to get rid of const warnings.... XS_VERSION_BOOTCHECK; - - newXSproto(strcpy(buf, "GetCharID"), XS_Corpse_GetCharID, file, "$"); - newXSproto(strcpy(buf, "GetDecayTime"), XS_Corpse_GetDecayTime, file, "$"); - newXSproto(strcpy(buf, "Lock"), XS_Corpse_Lock, file, "$"); - newXSproto(strcpy(buf, "UnLock"), XS_Corpse_UnLock, file, "$"); - newXSproto(strcpy(buf, "IsLocked"), XS_Corpse_IsLocked, file, "$"); - newXSproto(strcpy(buf, "ResetLooter"), XS_Corpse_ResetLooter, file, "$"); - newXSproto(strcpy(buf, "GetDBID"), XS_Corpse_GetDBID, file, "$"); - newXSproto(strcpy(buf, "GetOwnerName"), XS_Corpse_GetOwnerName, file, "$"); - newXSproto(strcpy(buf, "SetDecayTimer"), XS_Corpse_SetDecayTimer, file, "$$"); - newXSproto(strcpy(buf, "IsEmpty"), XS_Corpse_IsEmpty, file, "$"); newXSproto(strcpy(buf, "AddItem"), XS_Corpse_AddItem, file, "$$$;$"); - newXSproto(strcpy(buf, "GetWornItem"), XS_Corpse_GetWornItem, file, "$$"); - newXSproto(strcpy(buf, "RemoveItem"), XS_Corpse_RemoveItem, file, "$$"); - newXSproto(strcpy(buf, "SetCash"), XS_Corpse_SetCash, file, "$$$$$"); - newXSproto(strcpy(buf, "RemoveCash"), XS_Corpse_RemoveCash, file, "$"); - newXSproto(strcpy(buf, "CountItems"), XS_Corpse_CountItems, file, "$"); - newXSproto(strcpy(buf, "Delete"), XS_Corpse_Delete, file, "$"); - newXSproto(strcpy(buf, "GetCopper"), XS_Corpse_GetCopper, file, "$"); - newXSproto(strcpy(buf, "GetSilver"), XS_Corpse_GetSilver, file, "$"); - newXSproto(strcpy(buf, "GetGold"), XS_Corpse_GetGold, file, "$"); - newXSproto(strcpy(buf, "GetPlatinum"), XS_Corpse_GetPlatinum, file, "$"); - newXSproto(strcpy(buf, "Summon"), XS_Corpse_Summon, file, "$$$"); + newXSproto(strcpy(buf, "AddLooter"), XS_Corpse_AddLooter, file, "$$"); + newXSproto(strcpy(buf, "AllowMobLoot"), XS_Corpse_AllowMobLoot, file, "$$$"); + newXSproto(strcpy(buf, "CanMobLoot"), XS_Corpse_CanMobLoot, file, "$$"); newXSproto(strcpy(buf, "CastRezz"), XS_Corpse_CastRezz, file, "$$$"); newXSproto(strcpy(buf, "CompleteRezz"), XS_Corpse_CompleteRezz, file, "$"); - newXSproto(strcpy(buf, "CanMobLoot"), XS_Corpse_CanMobLoot, file, "$$"); - newXSproto(strcpy(buf, "AllowMobLoot"), XS_Corpse_AllowMobLoot, file, "$$$"); - newXSproto(strcpy(buf, "AddLooter"), XS_Corpse_AddLooter, file, "$$"); - newXSproto(strcpy(buf, "IsRezzed"), XS_Corpse_IsRezzed, file, "$"); - newXSproto(strcpy(buf, "HasItem"), XS_Corpse_HasItem, file, "$$"); newXSproto(strcpy(buf, "CountItem"), XS_Corpse_CountItem, file, "$$"); - newXSproto(strcpy(buf, "GetItemIDBySlot"), XS_Corpse_GetItemIDBySlot, file, "$$"); + newXSproto(strcpy(buf, "CountItems"), XS_Corpse_CountItems, file, "$"); + newXSproto(strcpy(buf, "Delete"), XS_Corpse_Delete, file, "$"); + newXSproto(strcpy(buf, "GetCharID"), XS_Corpse_GetCharID, file, "$"); + newXSproto(strcpy(buf, "GetCopper"), XS_Corpse_GetCopper, file, "$"); + newXSproto(strcpy(buf, "GetDBID"), XS_Corpse_GetDBID, file, "$"); + newXSproto(strcpy(buf, "GetDecayTime"), XS_Corpse_GetDecayTime, file, "$"); newXSproto(strcpy(buf, "GetFirstSlotByItemID"), XS_Corpse_GetFirstSlotByItemID, file, "$$"); - newXSproto(strcpy(buf, "RemoveItemByID"), XS_Corpse_RemoveItemByID, file, "$$;$"); + newXSproto(strcpy(buf, "GetGold"), XS_Corpse_GetGold, file, "$"); + newXSproto(strcpy(buf, "GetItemIDBySlot"), XS_Corpse_GetItemIDBySlot, file, "$$"); newXSproto(strcpy(buf, "GetLootList"), XS_Corpse_GetLootList, file, "$"); + newXSproto(strcpy(buf, "GetOwnerName"), XS_Corpse_GetOwnerName, file, "$"); + newXSproto(strcpy(buf, "GetPlatinum"), XS_Corpse_GetPlatinum, file, "$"); + newXSproto(strcpy(buf, "GetSilver"), XS_Corpse_GetSilver, file, "$"); + newXSproto(strcpy(buf, "GetWornItem"), XS_Corpse_GetWornItem, file, "$$"); + newXSproto(strcpy(buf, "HasItem"), XS_Corpse_HasItem, file, "$$"); + newXSproto(strcpy(buf, "IsEmpty"), XS_Corpse_IsEmpty, file, "$"); + newXSproto(strcpy(buf, "IsLocked"), XS_Corpse_IsLocked, file, "$"); + newXSproto(strcpy(buf, "IsRezzed"), XS_Corpse_IsRezzed, file, "$"); + newXSproto(strcpy(buf, "Lock"), XS_Corpse_Lock, file, "$"); + newXSproto(strcpy(buf, "RemoveCash"), XS_Corpse_RemoveCash, file, "$"); + newXSproto(strcpy(buf, "RemoveItem"), XS_Corpse_RemoveItem, file, "$$"); + newXSproto(strcpy(buf, "RemoveItemByID"), XS_Corpse_RemoveItemByID, file, "$$;$"); + newXSproto(strcpy(buf, "ResetLooter"), XS_Corpse_ResetLooter, file, "$"); + newXSproto(strcpy(buf, "SetCash"), XS_Corpse_SetCash, file, "$$$$$"); + newXSproto(strcpy(buf, "SetDecayTimer"), XS_Corpse_SetDecayTimer, file, "$$"); + newXSproto(strcpy(buf, "Summon"), XS_Corpse_Summon, file, "$$$"); + newXSproto(strcpy(buf, "UnLock"), XS_Corpse_UnLock, file, "$"); XSRETURN_YES; } diff --git a/zone/perl_questitem.cpp b/zone/perl_questitem.cpp index e1029b33a..30a5a7ca1 100644 --- a/zone/perl_questitem.cpp +++ b/zone/perl_questitem.cpp @@ -1,21 +1,3 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2004 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 -*/ - #include "../common/features.h" #include "client.h" @@ -204,16 +186,14 @@ XS(boot_QuestItem) { //add the strcpy stuff to get rid of const warnings.... XS_VERSION_BOOTCHECK; - - newXSproto(strcpy(buf, "GetName"), XS_QuestItem_GetName, file, "$"); - newXSproto(strcpy(buf, "SetScale"), XS_QuestItem_SetScale, file, "$"); - newXSproto(strcpy(buf, "ItemSay"), XS_QuestItem_ItemSay, file, "$"); - newXSproto(strcpy(buf, "IsType"), XS_QuestItem_IsType, file, "$$"); - newXSproto(strcpy(buf, "IsAttuned"), XS_QuestItem_IsAttuned, file, "$"); - newXSproto(strcpy(buf, "GetCharges"), XS_QuestItem_GetCharges, file, "$"); newXSproto(strcpy(buf, "GetAugment"), XS_QuestItem_GetAugment, file, "$$"); + newXSproto(strcpy(buf, "GetCharges"), XS_QuestItem_GetCharges, file, "$"); newXSproto(strcpy(buf, "GetID"), XS_QuestItem_GetID, file, "$"); - + newXSproto(strcpy(buf, "GetName"), XS_QuestItem_GetName, file, "$"); + newXSproto(strcpy(buf, "IsAttuned"), XS_QuestItem_IsAttuned, file, "$"); + newXSproto(strcpy(buf, "IsType"), XS_QuestItem_IsType, file, "$$"); + newXSproto(strcpy(buf, "ItemSay"), XS_QuestItem_ItemSay, file, "$"); + newXSproto(strcpy(buf, "SetScale"), XS_QuestItem_SetScale, file, "$"); XSRETURN_YES; } diff --git a/zone/perl_raids.cpp b/zone/perl_raids.cpp index bc739a8d6..07834de9f 100644 --- a/zone/perl_raids.cpp +++ b/zone/perl_raids.cpp @@ -1,30 +1,3 @@ -/* -* This file was generated automatically by ExtUtils::ParseXS version 2.18 from the -* contents of raids.h.xs. Do not edit this file, edit raids.h.xs instead. -* -* ANY CHANGES MADE HERE WILL BE LOST! -* -*/ - - -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2004 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 -*/ - #include "../common/features.h" #ifdef EMBPERL_XS_CLASSES @@ -448,26 +421,25 @@ XS(boot_Raid) { //add the strcpy stuff to get rid of const warnings.... XS_VERSION_BOOTCHECK; - - newXSproto(strcpy(buf, "IsRaidMember"), XS_Raid_IsRaidMember, file, "$$"); - newXSproto(strcpy(buf, "CastGroupSpell"), XS_Raid_CastGroupSpell, file, "$$$$"); - newXSproto(strcpy(buf, "GroupCount"), XS_Raid_GroupCount, file, "$$"); - newXSproto(strcpy(buf, "RaidCount"), XS_Raid_RaidCount, file, "$"); - newXSproto(strcpy(buf, "GetGroup"), XS_Raid_GetGroup, file, "$$"); - newXSproto(strcpy(buf, "SplitExp"), XS_Raid_SplitExp, file, "$$$"); - newXSproto(strcpy(buf, "GetTotalRaidDamage"), XS_Raid_GetTotalRaidDamage, file, "$$"); - newXSproto(strcpy(buf, "SplitMoney"), XS_Raid_SplitMoney, file, "$$$$$$"); newXSproto(strcpy(buf, "BalanceHP"), XS_Raid_BalanceHP, file, "$$$"); - newXSproto(strcpy(buf, "IsLeader"), XS_Raid_IsLeader, file, "$$"); - newXSproto(strcpy(buf, "IsGroupLeader"), XS_Raid_IsGroupLeader, file, "$$"); - newXSproto(strcpy(buf, "GetHighestLevel"), XS_Raid_GetHighestLevel, file, "$"); - newXSproto(strcpy(buf, "GetLowestLevel"), XS_Raid_GetLowestLevel, file, "$"); + newXSproto(strcpy(buf, "CastGroupSpell"), XS_Raid_CastGroupSpell, file, "$$$$"); + newXSproto(strcpy(buf, "DoesAnyMemberHaveExpeditionLockout"), XS_Raid_DoesAnyMemberHaveExpeditionLockout, file, "$$$;$"); newXSproto(strcpy(buf, "GetClientByIndex"), XS_Raid_GetClientByIndex, file, "$$"); + newXSproto(strcpy(buf, "GetGroup"), XS_Raid_GetGroup, file, "$$"); + newXSproto(strcpy(buf, "GetHighestLevel"), XS_Raid_GetHighestLevel, file, "$"); + newXSproto(strcpy(buf, "GetID"), XS_Raid_GetID, file, "$"); + newXSproto(strcpy(buf, "GetLowestLevel"), XS_Raid_GetLowestLevel, file, "$"); + newXSproto(strcpy(buf, "GetMember"), XS_Raid_GetMember, file, "$$"); + newXSproto(strcpy(buf, "GetTotalRaidDamage"), XS_Raid_GetTotalRaidDamage, file, "$$"); + newXSproto(strcpy(buf, "GroupCount"), XS_Raid_GroupCount, file, "$$"); + newXSproto(strcpy(buf, "IsGroupLeader"), XS_Raid_IsGroupLeader, file, "$$"); + newXSproto(strcpy(buf, "IsLeader"), XS_Raid_IsLeader, file, "$$"); + newXSproto(strcpy(buf, "IsRaidMember"), XS_Raid_IsRaidMember, file, "$$"); + newXSproto(strcpy(buf, "RaidCount"), XS_Raid_RaidCount, file, "$"); + newXSproto(strcpy(buf, "SplitExp"), XS_Raid_SplitExp, file, "$$$"); + newXSproto(strcpy(buf, "SplitMoney"), XS_Raid_SplitMoney, file, "$$$$$$"); newXSproto(strcpy(buf, "TeleportGroup"), XS_Raid_TeleportGroup, file, "$$$$$$$$"); newXSproto(strcpy(buf, "TeleportRaid"), XS_Raid_TeleportRaid, file, "$$$$$$$"); - newXSproto(strcpy(buf, "GetID"), XS_Raid_GetID, file, "$"); - newXSproto(strcpy(buf, "GetMember"), XS_Raid_GetMember, file, "$$"); - newXSproto(strcpy(buf, "DoesAnyMemberHaveExpeditionLockout"), XS_Raid_DoesAnyMemberHaveExpeditionLockout, file, "$$$;$"); XSRETURN_YES; } From 886f00ed50964a58040d4b5ee850a66f44d5f943 Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Sat, 6 Nov 2021 16:36:19 -0500 Subject: [PATCH 326/624] Fix resetAA to actually remove all AAs except granted AAs (#1681) --- zone/aa.cpp | 3 ++- zone/zonedb.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/zone/aa.cpp b/zone/aa.cpp index f114abb93..4945b2a94 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -492,7 +492,8 @@ void Client::ResetAA() { m_pp.raid_leadership_points = 0; m_pp.group_leadership_exp = 0; m_pp.raid_leadership_exp = 0; - + + database.DeleteCharacterAAs(CharacterID()); database.DeleteCharacterLeadershipAAs(CharacterID()); } diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 04b4a9326..f8e2b3a43 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1973,7 +1973,7 @@ bool ZoneDatabase::DeleteCharacterLeadershipAAs(uint32 character_id){ } bool ZoneDatabase::DeleteCharacterAAs(uint32 character_id){ - std::string query = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u", character_id); + std::string query = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u AND `aa_id` NOT IN(SELECT a.first_rank_id FROM aa_ability a WHERE a.grant_only != 0)", character_id); QueryDatabase(query); return true; } From 5c7972345a60d6bd31ff1d30a5ad24aeb09edf78 Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Sat, 6 Nov 2021 17:22:52 -0500 Subject: [PATCH 327/624] =?UTF-8?q?[Bug=20Fix]=20Fix=20startzone=20rule=20?= =?UTF-8?q?to=20default=20players=20to=20correct=20zone=20when=20not=20fou?= =?UTF-8?q?nd=20=E2=80=A6=20(#1669)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix startzone rule to default players to correct zone when not found in database * Formatting Co-authored-by: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> --- common/eq_constants.h | 17 ++++ world/worlddb.cpp | 191 ++++++++++++++++++++++-------------------- 2 files changed, 118 insertions(+), 90 deletions(-) diff --git a/common/eq_constants.h b/common/eq_constants.h index 6321b2d9b..8949ea109 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -992,4 +992,21 @@ enum LDoNThemeBits { TAKBit = 16 }; +enum StartZoneIndex { + Odus = 0, + Qeynos, + Halas, + Rivervale, + Freeport, + Neriak, + Grobb, + Oggok, + Kaladim, + GreaterFaydark, + Felwithe, + Akanon, + Cabilis, + SharVahl +}; + #endif /*COMMON_EQ_CONSTANTS_H*/ diff --git a/world/worlddb.cpp b/world/worlddb.cpp index c9cd497ee..7e6490d0b 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -645,7 +645,12 @@ bool WorldDatabase::GetStartZone( } void WorldDatabase::SetSoFDefaultStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc){ - if (in_cc->start_zone == RuleI(World, TutorialZoneID)) { + int sof_start_zone_id = RuleI(World, SoFStartZoneID); + if (sof_start_zone_id > 0) { + in_pp->zone_id = sof_start_zone_id; + in_cc->start_zone = in_pp->zone_id; + } + else if (in_cc->start_zone == RuleI(World, TutorialZoneID)) { in_pp->zone_id = in_cc->start_zone; } else { @@ -653,105 +658,111 @@ void WorldDatabase::SetSoFDefaultStartZone(PlayerProfile_Struct* in_pp, CharCrea in_pp->y = in_pp->binds[0].y = -20.0f; in_pp->z = in_pp->binds[0].z = 0.79f; in_pp->heading = in_pp->binds[0].heading = 0.0f; - in_pp->zone_id = in_pp->binds[0].zone_id = 394; // Crescent Reach. + in_pp->zone_id = in_pp->binds[0].zone_id = Zones::CRESCENT; // Crescent Reach. } } void WorldDatabase::SetTitaniumDefaultStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc) { - switch (in_cc->start_zone) - { - case 0: + int titanium_start_zone_id = RuleI(World, TitaniumStartZoneID); + if (titanium_start_zone_id > 0) { + in_pp->zone_id = titanium_start_zone_id; + in_pp->binds[0].zone_id = titanium_start_zone_id; + } else { + switch (in_cc->start_zone) { - if (in_cc->deity == 203) // Cazic-Thule Erudites go to Paineel + case StartZoneIndex::Odus: { - in_pp->zone_id = 75; // paineel - in_pp->binds[0].zone_id = 75; + if (in_cc->deity == EQ::deity::DeityCazicThule) // Cazic-Thule Erudites go to Paineel + { + in_pp->zone_id = Zones::PAINEEL; // paineel + in_pp->binds[0].zone_id = Zones::PAINEEL; + } + else + { + in_pp->zone_id = Zones::ERUDNEXT; // erudnext + in_pp->binds[0].zone_id = Zones::TOX; // tox + } + break; } - else + case StartZoneIndex::Qeynos: { - in_pp->zone_id = 24; // erudnext - in_pp->binds[0].zone_id = 38; // tox + in_pp->zone_id = Zones::QEYNOS2; // qeynos2 + in_pp->binds[0].zone_id = Zones::QEYNOS2; // qeynos2 + break; + } + case StartZoneIndex::Halas: + { + in_pp->zone_id = Zones::HALAS; // halas + in_pp->binds[0].zone_id = Zones::EVERFROST; // everfrost + break; + } + case StartZoneIndex::Rivervale: + { + in_pp->zone_id = Zones::RIVERVALE; // rivervale + in_pp->binds[0].zone_id = Zones::KITHICOR; // kithicor + break; + } + case StartZoneIndex::Freeport: + { + in_pp->zone_id = Zones::FREPORTW; // freportw + in_pp->binds[0].zone_id = Zones::FREPORTW; // freportw + break; + } + case StartZoneIndex::Neriak: + { + in_pp->zone_id = Zones::NERIAKA; // neriaka + in_pp->binds[0].zone_id = Zones::NEKTULOS; // nektulos + break; + } + case StartZoneIndex::Grobb: + { + in_pp->zone_id = Zones::GROBB; // grobb + in_pp->binds[0].zone_id = Zones::INNOTHULE; // innothule + break; + } + case StartZoneIndex::Oggok: + { + in_pp->zone_id = Zones::OGGOK; // oggok + in_pp->binds[0].zone_id = Zones::FEERROTT; // feerrott + break; + } + case StartZoneIndex::Kaladim: + { + in_pp->zone_id = Zones::KALADIMA; // kaladima + in_pp->binds[0].zone_id = Zones::BUTCHER; // butcher + break; + } + case StartZoneIndex::GreaterFaydark: + { + in_pp->zone_id = Zones::GFAYDARK; // gfaydark + in_pp->binds[0].zone_id = Zones::GFAYDARK; // gfaydark + break; + } + case StartZoneIndex::Felwithe: + { + in_pp->zone_id = Zones::FELWITHEA; // felwithea + in_pp->binds[0].zone_id = Zones::GFAYDARK; // gfaydark + break; + } + case StartZoneIndex::Akanon: + { + in_pp->zone_id = Zones::AKANON; // akanon + in_pp->binds[0].zone_id = Zones::STEAMFONT; // steamfont + break; + } + case StartZoneIndex::Cabilis: + { + in_pp->zone_id = Zones::CABWEST; // cabwest + in_pp->binds[0].zone_id = Zones::FIELDOFBONE; // fieldofbone + break; + } + case StartZoneIndex::SharVahl: + { + in_pp->zone_id = Zones::SHARVAHL; // sharvahl + in_pp->binds[0].zone_id = Zones::SHARVAHL; // sharvahl + break; } - break; - } - case 1: - { - in_pp->zone_id = 2; // qeynos2 - in_pp->binds[0].zone_id = 2; // qeynos2 - break; - } - case 2: - { - in_pp->zone_id = 29; // halas - in_pp->binds[0].zone_id = 30; // everfrost - break; - } - case 3: - { - in_pp->zone_id = 19; // rivervale - in_pp->binds[0].zone_id = 20; // kithicor - break; - } - case 4: - { - in_pp->zone_id = 9; // freportw - in_pp->binds[0].zone_id = 9; // freportw - break; - } - case 5: - { - in_pp->zone_id = 40; // neriaka - in_pp->binds[0].zone_id = 25; // nektulos - break; - } - case 6: - { - in_pp->zone_id = 52; // gukta - in_pp->binds[0].zone_id = 46; // innothule - break; - } - case 7: - { - in_pp->zone_id = 49; // oggok - in_pp->binds[0].zone_id = 47; // feerrott - break; - } - case 8: - { - in_pp->zone_id = 60; // kaladima - in_pp->binds[0].zone_id = 68; // butcher - break; - } - case 9: - { - in_pp->zone_id = 54; // gfaydark - in_pp->binds[0].zone_id = 54; // gfaydark - break; - } - case 10: - { - in_pp->zone_id = 61; // felwithea - in_pp->binds[0].zone_id = 54; // gfaydark - break; - } - case 11: - { - in_pp->zone_id = 55; // akanon - in_pp->binds[0].zone_id = 56; // steamfont - break; - } - case 12: - { - in_pp->zone_id = 82; // cabwest - in_pp->binds[0].zone_id = 78; // fieldofbone - break; - } - case 13: - { - in_pp->zone_id = 155; // sharvahl - in_pp->binds[0].zone_id = 155; // sharvahl - break; } } } From 785926a584a392ff36323329f2fa30abb4dd0073 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 6 Nov 2021 21:06:14 -0400 Subject: [PATCH 328/624] [Quest API] Added NPC special ability to modify Riposte/Dodge/Parry/Block chance (#1683) * Update attack.cpp * u * Update attack.cpp * spellchecked --- zone/attack.cpp | 46 ++++++++++++++++---- zone/common.h | 5 ++- zone/lua_mob.cpp | 106 ++++++++++++++++++++++++----------------------- 3 files changed, 95 insertions(+), 62 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 28a6f995e..c869792a7 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -382,18 +382,32 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit) ultimately end up being more useful as fields in npc_types. */ - int counter_all = 0; + int counter_all = 0; int counter_riposte = 0; - int counter_block = 0; - int counter_parry = 0; - int counter_dodge = 0; + int counter_block = 0; + int counter_parry = 0; + int counter_dodge = 0; if (attacker->GetSpecialAbility(COUNTER_AVOID_DAMAGE)) { - counter_all = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 0); + counter_all = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 0); counter_riposte = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 1); - counter_block = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 2); - counter_parry = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 3); - counter_dodge = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 4); + counter_block = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 2); + counter_parry = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 3); + counter_dodge = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 4); + } + + int modify_all = 0; + int modify_riposte = 0; + int modify_block = 0; + int modify_parry = 0; + int modify_dodge = 0; + + if (GetSpecialAbility(MODIFY_AVOID_DAMAGE)) { + modify_all = GetSpecialAbilityParam(MODIFY_AVOID_DAMAGE, 0); + modify_riposte = GetSpecialAbilityParam(MODIFY_AVOID_DAMAGE, 1); + modify_block = GetSpecialAbilityParam(MODIFY_AVOID_DAMAGE, 2); + modify_parry = GetSpecialAbilityParam(MODIFY_AVOID_DAMAGE, 3); + modify_dodge = GetSpecialAbilityParam(MODIFY_AVOID_DAMAGE, 4); } // riposte -- it may seem crazy, but if the attacker has SPA 173 on them, they are immune to Ripo @@ -420,6 +434,10 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit) float counter = (counter_riposte + counter_all) / 100.0f; chance -= chance * counter; } + if (modify_riposte || modify_all) { + float npc_modifier = (modify_riposte + modify_all) / 100.0f; + chance += chance * npc_modifier; + } // AA Slippery Attacks if (hit.hand == EQ::invslot::slotSecondary) { int slip = aabonuses.OffhandRiposteFail + itembonuses.OffhandRiposteFail + spellbonuses.OffhandRiposteFail; @@ -459,6 +477,10 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit) float counter = (counter_block + counter_all) / 100.0f; chance -= chance * counter; } + if (modify_block || modify_all) { + float npc_modifier = (modify_block + modify_all) / 100.0f; + chance += chance * npc_modifier; + } if (zone->random.Roll(chance)) { hit.damage_done = DMG_BLOCKED; return true; @@ -482,6 +504,10 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit) float counter = (counter_parry + counter_all) / 100.0f; chance -= chance * counter; } + if (modify_parry || modify_all) { + float npc_modifier = (modify_parry + modify_all) / 100.0f; + chance += chance * npc_modifier; + } if (zone->random.Roll(chance)) { hit.damage_done = DMG_PARRIED; return true; @@ -505,6 +531,10 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit) float counter = (counter_dodge + counter_all) / 100.0f; chance -= chance * counter; } + if (modify_dodge || modify_all) { + float npc_modifier = (modify_dodge + modify_all) / 100.0f; + chance += chance * npc_modifier; + } if (zone->random.Roll(chance)) { hit.damage_done = DMG_DODGED; return true; diff --git a/zone/common.h b/zone/common.h index 85f2d3cbc..c52dc74e1 100644 --- a/zone/common.h +++ b/zone/common.h @@ -201,14 +201,15 @@ enum { ALLOW_TO_TANK = 41, IGNORE_ROOT_AGGRO_RULES = 42, CASTING_RESIST_DIFF = 43, - COUNTER_AVOID_DAMAGE = 44, + COUNTER_AVOID_DAMAGE = 44, //Modify by percent NPC's opponents chance to riposte, block, parry or dodge individually, or for all skills PROX_AGGRO = 45, IMMUNE_RANGED_ATTACKS = 46, IMMUNE_DAMAGE_CLIENT = 47, IMMUNE_DAMAGE_NPC = 48, IMMUNE_AGGRO_CLIENT = 49, IMMUNE_AGGRO_NPC = 50, - MAX_SPECIAL_ATTACK = 51 + MODIFY_AVOID_DAMAGE = 51, //Modify by percent the NPCs chance to riposte, block, parry or dodge individually, or for all skills + MAX_SPECIAL_ATTACK = 52 }; typedef enum { //fear states diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index 4b50c2b78..ad39f9af5 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -2841,58 +2841,60 @@ luabind::scope lua_register_mob() { luabind::scope lua_register_special_abilities() { return luabind::class_("SpecialAbility") - .enum_("constants") - [ - luabind::value("summon", static_cast(SPECATK_SUMMON)), - luabind::value("enrage", static_cast(SPECATK_ENRAGE)), - luabind::value("rampage", static_cast(SPECATK_RAMPAGE)), - luabind::value("area_rampage", static_cast(SPECATK_AREA_RAMPAGE)), - luabind::value("flurry", static_cast(SPECATK_FLURRY)), - luabind::value("triple_attack", static_cast(SPECATK_TRIPLE)), - luabind::value("quad_attack", static_cast(SPECATK_QUAD)), - luabind::value("innate_dual_wield", static_cast(SPECATK_INNATE_DW)), - luabind::value("bane_attack", static_cast(SPECATK_BANE)), - luabind::value("magical_attack", static_cast(SPECATK_MAGICAL)), - luabind::value("ranged_attack", static_cast(SPECATK_RANGED_ATK)), - luabind::value("unslowable", static_cast(UNSLOWABLE)), - luabind::value("unmezable", static_cast(UNMEZABLE)), - luabind::value("uncharmable", static_cast(UNCHARMABLE)), - luabind::value("unstunable", static_cast(UNSTUNABLE)), - luabind::value("unsnareable", static_cast(UNSNAREABLE)), - luabind::value("unfearable", static_cast(UNFEARABLE)), - luabind::value("undispellable", static_cast(UNDISPELLABLE)), - luabind::value("immune_melee", static_cast(IMMUNE_MELEE)), - luabind::value("immune_magic", static_cast(IMMUNE_MAGIC)), - luabind::value("immune_fleeing", static_cast(IMMUNE_FLEEING)), - luabind::value("immune_melee_except_bane", static_cast(IMMUNE_MELEE_EXCEPT_BANE)), - luabind::value("immune_melee_except_magical", static_cast(IMMUNE_MELEE_NONMAGICAL)), - luabind::value("immune_aggro", static_cast(IMMUNE_AGGRO)), - luabind::value("immune_aggro_on", static_cast(IMMUNE_AGGRO_ON)), - luabind::value("immune_casting_from_range", static_cast(IMMUNE_CASTING_FROM_RANGE)), - luabind::value("immune_feign_death", static_cast(IMMUNE_FEIGN_DEATH)), - luabind::value("immune_taunt", static_cast(IMMUNE_TAUNT)), - luabind::value("tunnelvision", static_cast(NPC_TUNNELVISION)), - luabind::value("dont_buff_friends", static_cast(NPC_NO_BUFFHEAL_FRIENDS)), - luabind::value("immune_pacify", static_cast(IMMUNE_PACIFY)), - luabind::value("leash", static_cast(LEASH)), - luabind::value("tether", static_cast(TETHER)), - luabind::value("destructible_object", static_cast(DESTRUCTIBLE_OBJECT)), - luabind::value("no_harm_from_client", static_cast(NO_HARM_FROM_CLIENT)), - luabind::value("always_flee", static_cast(ALWAYS_FLEE)), - luabind::value("flee_percent", static_cast(FLEE_PERCENT)), - luabind::value("allow_beneficial", static_cast(ALLOW_BENEFICIAL)), - luabind::value("disable_melee", static_cast(DISABLE_MELEE)), - luabind::value("npc_chase_distance", static_cast(NPC_CHASE_DISTANCE)), - luabind::value("allow_to_tank", static_cast(ALLOW_TO_TANK)), - luabind::value("ignore_root_aggro_rules", static_cast(IGNORE_ROOT_AGGRO_RULES)), - luabind::value("casting_resist_diff", static_cast(CASTING_RESIST_DIFF)), - luabind::value("counter_avoid_damage", static_cast(COUNTER_AVOID_DAMAGE)), - luabind::value("immune_ranged_attacks", static_cast(IMMUNE_RANGED_ATTACKS)), - luabind::value("immune_damage_client", static_cast(IMMUNE_DAMAGE_CLIENT)), - luabind::value("immune_damage_npc", static_cast(IMMUNE_DAMAGE_NPC)), - luabind::value("immune_aggro_client", static_cast(IMMUNE_AGGRO_CLIENT)), - luabind::value("immune_aggro_npc", static_cast(IMMUNE_AGGRO_NPC)) - ]; + + .enum_("constants") + [ + luabind::value("summon", static_cast(SPECATK_SUMMON)), + luabind::value("enrage", static_cast(SPECATK_ENRAGE)), + luabind::value("rampage", static_cast(SPECATK_RAMPAGE)), + luabind::value("area_rampage", static_cast(SPECATK_AREA_RAMPAGE)), + luabind::value("flurry", static_cast(SPECATK_FLURRY)), + luabind::value("triple_attack", static_cast(SPECATK_TRIPLE)), + luabind::value("quad_attack", static_cast(SPECATK_QUAD)), + luabind::value("innate_dual_wield", static_cast(SPECATK_INNATE_DW)), + luabind::value("bane_attack", static_cast(SPECATK_BANE)), + luabind::value("magical_attack", static_cast(SPECATK_MAGICAL)), + luabind::value("ranged_attack", static_cast(SPECATK_RANGED_ATK)), + luabind::value("unslowable", static_cast(UNSLOWABLE)), + luabind::value("unmezable", static_cast(UNMEZABLE)), + luabind::value("uncharmable", static_cast(UNCHARMABLE)), + luabind::value("unstunable", static_cast(UNSTUNABLE)), + luabind::value("unsnareable", static_cast(UNSNAREABLE)), + luabind::value("unfearable", static_cast(UNFEARABLE)), + luabind::value("undispellable", static_cast(UNDISPELLABLE)), + luabind::value("immune_melee", static_cast(IMMUNE_MELEE)), + luabind::value("immune_magic", static_cast(IMMUNE_MAGIC)), + luabind::value("immune_fleeing", static_cast(IMMUNE_FLEEING)), + luabind::value("immune_melee_except_bane", static_cast(IMMUNE_MELEE_EXCEPT_BANE)), + luabind::value("immune_melee_except_magical", static_cast(IMMUNE_MELEE_NONMAGICAL)), + luabind::value("immune_aggro", static_cast(IMMUNE_AGGRO)), + luabind::value("immune_aggro_on", static_cast(IMMUNE_AGGRO_ON)), + luabind::value("immune_casting_from_range", static_cast(IMMUNE_CASTING_FROM_RANGE)), + luabind::value("immune_feign_death", static_cast(IMMUNE_FEIGN_DEATH)), + luabind::value("immune_taunt", static_cast(IMMUNE_TAUNT)), + luabind::value("tunnelvision", static_cast(NPC_TUNNELVISION)), + luabind::value("dont_buff_friends", static_cast(NPC_NO_BUFFHEAL_FRIENDS)), + luabind::value("immune_pacify", static_cast(IMMUNE_PACIFY)), + luabind::value("leash", static_cast(LEASH)), + luabind::value("tether", static_cast(TETHER)), + luabind::value("destructible_object", static_cast(DESTRUCTIBLE_OBJECT)), + luabind::value("no_harm_from_client", static_cast(NO_HARM_FROM_CLIENT)), + luabind::value("always_flee", static_cast(ALWAYS_FLEE)), + luabind::value("flee_percent", static_cast(FLEE_PERCENT)), + luabind::value("allow_beneficial", static_cast(ALLOW_BENEFICIAL)), + luabind::value("disable_melee", static_cast(DISABLE_MELEE)), + luabind::value("npc_chase_distance", static_cast(NPC_CHASE_DISTANCE)), + luabind::value("allow_to_tank", static_cast(ALLOW_TO_TANK)), + luabind::value("ignore_root_aggro_rules", static_cast(IGNORE_ROOT_AGGRO_RULES)), + luabind::value("casting_resist_diff", static_cast(CASTING_RESIST_DIFF)), + luabind::value("counter_avoid_damage", static_cast(COUNTER_AVOID_DAMAGE)), + luabind::value("immune_ranged_attacks", static_cast(IMMUNE_RANGED_ATTACKS)), + luabind::value("immune_damage_client", static_cast(IMMUNE_DAMAGE_CLIENT)), + luabind::value("immune_damage_npc", static_cast(IMMUNE_DAMAGE_NPC)), + luabind::value("immune_aggro_client", static_cast(IMMUNE_AGGRO_CLIENT)), + luabind::value("immune_aggro_npc", static_cast(IMMUNE_AGGRO_NPC)), + luabind::value("modify_avoid_damage", static_cast(MODIFY_AVOID_DAMAGE)) + ]; } #endif From beb4de0b45c461796ce0c660bb7e8f4ad7a45d87 Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Sat, 6 Nov 2021 21:57:05 -0400 Subject: [PATCH 329/624] [Cleanup] Remove unused door variable. (#1685) --- zone/zonedb.cpp | 18 ------------------ zone/zonedb.h | 3 --- 2 files changed, 21 deletions(-) diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index f8e2b3a43..10ff77325 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -37,7 +37,6 @@ ZoneDatabase::ZoneDatabase(const char* host, const char* user, const char* passw } void ZoneDatabase::ZDBInitVars() { - memset(door_isopen_array, 0, sizeof(door_isopen_array)); npc_spellseffects_cache = 0; npc_spellseffects_loadtried = 0; max_faction = 0; @@ -626,23 +625,6 @@ bool ZoneDatabase::SetSpecialAttkFlag(uint8 id, const char* flag) { return results.RowsAffected() != 0; } -bool ZoneDatabase::DoorIsOpen(uint8 door_id,const char* zone_name) -{ - if(door_isopen_array[door_id] == 0) { - SetDoorPlace(1,door_id,zone_name); - return false; - } - else { - SetDoorPlace(0,door_id,zone_name); - return true; - } -} - -void ZoneDatabase::SetDoorPlace(uint8 value,uint8 door_id,const char* zone_name) -{ - door_isopen_array[door_id] = value; -} - // Load child objects for a world container (i.e., forge, bag dropped to ground, etc) void ZoneDatabase::LoadWorldContainer(uint32 parentid, EQ::ItemInstance* container) { diff --git a/zone/zonedb.h b/zone/zonedb.h index 83148ba7b..536d3aa86 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -525,8 +525,6 @@ public: bool LoadTributes(); /* Doors */ - bool DoorIsOpen(uint8 door_id,const char* zone_name); - void SetDoorPlace(uint8 value,uint8 door_id,const char* zone_name); std::vector LoadDoors(const std::string& zone_name, int16 version); uint32 GetGuildEQID(uint32 guilddbid); void UpdateDoorGuildID(int doorid, int guild_id); @@ -595,7 +593,6 @@ protected: std::unordered_set npc_spells_loadtried; DBnpcspellseffects_Struct** npc_spellseffects_cache; bool* npc_spellseffects_loadtried; - uint8 door_isopen_array[255]; }; extern ZoneDatabase database; From bc82b897c5214d6edcf4b79cc9a464631c5c9263 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 6 Nov 2021 22:34:04 -0400 Subject: [PATCH 330/624] [Commands] Add #emptyinventory Command. (#1684) * [Commands] Add #emptyinventory Command. - Allows you empty you or your target's inventory completely. (Equipment, General, Bank, and Shared Bank) - Fixed an issue not allowing quest::removeitem(item_id, quanity) to remove 0 charge items. - Fixed an issue not allowing eq.remove_item(item_id, quanity) to remove 0 charge items. * Update command.cpp * Update client.cpp --- zone/client.cpp | 7 ++--- zone/command.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++++++++ zone/command.h | 1 + 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index dc6b04307..893a2079e 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -10276,10 +10276,11 @@ void Client::RemoveItem(uint32 item_id, uint32 quantity) item = GetInv().GetItem(slot_id); if (item && item->GetID() == item_id) { - int stack_size = item->IsStackable() ? item->GetCharges() : 1; + int charges = item->IsStackable() ? item->GetCharges() : 0; + int stack_size = std::max(charges, 1); if ((removed_count + stack_size) <= quantity) { removed_count += stack_size; - DeleteItemInInventory(slot_id, stack_size, true); + DeleteItemInInventory(slot_id, charges, true); } else { int amount_left = (quantity - removed_count); if (amount_left > 0 && stack_size >= amount_left) { @@ -10808,4 +10809,4 @@ uint16 Client::LearnDisciplines(uint8 min_level, uint8 max_level) } return learned_disciplines; -} \ No newline at end of file +} diff --git a/zone/command.cpp b/zone/command.cpp index cb084020f..2efed7ccd 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -212,6 +212,7 @@ int command_init(void) command_add("emote", "['name'/'world'/'zone'] [type] [message] - Send an emote message", 80, command_emote) || command_add("emotesearch", "Searches NPC Emotes", 80, command_emotesearch) || command_add("emoteview", "Lists all NPC Emotes", 80, command_emoteview) || + command_add("emptyinventory", "- Clears you or your target's entire inventory (Equipment, General, Bank, and Shared Bank)", 250, command_emptyinventory) || command_add("enablerecipe", "[recipe_id] - Enables a recipe using the recipe id.", 80, command_enablerecipe) || command_add("endurance", "Restores you or your target's endurance.", 50, command_endurance) || command_add("equipitem", "[slotid(0-21)] - Equip the item on your cursor into the specified slot", 50, command_equipitem) || @@ -15060,6 +15061,71 @@ void command_findtask(Client *c, const Seperator *sep) } } +void command_emptyinventory(Client *c, const Seperator *sep) +{ + Client *target = c; + if (c->GetGM() && c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + + EQ::ItemInstance *item = nullptr; + static const int16 slots[][2] = { + { EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END }, + { EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END }, + { EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END}, + { EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END }, + { EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END }, + { EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END }, + { EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END }, + }; + int removed_count = 0; + const size_t size = sizeof(slots) / sizeof(slots[0]); + for (int slot_index = 0; slot_index < size; ++slot_index) { + for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { + item = target->GetInv().GetItem(slot_id); + if (item) { + int stack_size = std::max(static_cast(item->GetCharges()), 1); + removed_count += stack_size; + target->DeleteItemInInventory(slot_id, 0, true); + } + } + } + + if (c != target) { + auto target_name = target->GetCleanName(); + if (removed_count) { + c->Message( + Chat::White, + fmt::format( + "Inventory cleared for {}, {} items deleted.", + target_name, + removed_count + ).c_str() + ); + } else { + c->Message( + Chat::White, + fmt::format( + "{} has no items to delete.", + target_name + ).c_str() + ); + } + } else { + if (removed_count) { + c->Message( + Chat::White, + fmt::format( + "Your inventory has been cleared, {} items deleted.", + removed_count + ).c_str() + ); + } else { + c->Message(Chat::White, "You have no items to delete."); + } + } +} + // All new code added to command.cpp should be BEFORE this comment line. Do no append code to this file below the BOTS code block. #ifdef BOTS #include "bot_command.h" diff --git a/zone/command.h b/zone/command.h index dec5cd076..59926536d 100644 --- a/zone/command.h +++ b/zone/command.h @@ -100,6 +100,7 @@ void command_editmassrespawn(Client* c, const Seperator* sep); void command_emote(Client *c, const Seperator *sep); void command_emotesearch(Client* c, const Seperator *sep); void command_emoteview(Client* c, const Seperator *sep); +void command_emptyinventory(Client *c, const Seperator *sep); void command_enablerecipe(Client *c, const Seperator *sep); void command_endurance(Client *c, const Seperator *sep); void command_equipitem(Client *c, const Seperator *sep); From 1cdb1816a2c34de9f6e0a8c2dc6b7eebc2120106 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 6 Nov 2021 23:14:36 -0400 Subject: [PATCH 331/624] [Bug Fix] SOF+ clients item click recast timer not met check (#1682) * Update client_packet.cpp * Update spells.cpp * augs working too --- zone/client_packet.cpp | 17 +++++++++++++++++ zone/spells.cpp | 7 +++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index ab8fa7b0f..6fa07623f 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -8959,6 +8959,14 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) } if (GetLevel() >= item->Click.Level2) { + if (item->RecastDelay > 0) + { + if (!GetPTimers().Expired(&database, (pTimerItemStart + item->RecastType), false)) { + LogSpells("Casting of [{}] canceled: item spell reuse timer not expired", spell_id); + return; + } + } + int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", slot_id); inst = m_inv[slot_id]; if (!inst) @@ -8988,6 +8996,15 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) } if (GetLevel() >= augitem->Click.Level2) { + if (augitem->RecastDelay > 0) + { + if (!GetPTimers().Expired(&database, (pTimerItemStart + augitem->RecastType), false)) { + LogSpells("Casting of [{}] canceled: item spell reuse timer from augment not expired", spell_id); + MessageString(Chat::Red, SPELL_RECAST); + return; + } + } + int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, clickaug, nullptr, "", slot_id); inst = m_inv[slot_id]; if (!inst) diff --git a/zone/spells.cpp b/zone/spells.cpp index 76769da93..4d6446fc5 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1020,7 +1020,10 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo return; } } - + /* + Titanium client will prevent item recast on its own. This is only used to enforce. Titanium items are cast from Handle_OP_CastSpell. + SOF+ client does not prevent item recast on its own. We enforce this in Handle_OP_ItemVerifyRequest where items are cast from. + */ if(IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt)) { IsFromItem = true; @@ -6287,4 +6290,4 @@ int Client::GetNextAvailableDisciplineSlot(int starting_slot) { } return -1; // Return -1 if No Slots open -} \ No newline at end of file +} From 7f497f9d324f7eb30cfdb208ebf16eda24165a5b Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 7 Nov 2021 16:35:30 -0500 Subject: [PATCH 332/624] [Spells] Implemented SPA 415 SE_FFItemClass (#1688) * prelim * Spell Focus implemented * AA implemented * Update spdat.h * Update spdat.h * prelim excludes * enum limit expansion * overhaul * v2 testing * updates * working * Fin * Update spell_effects.cpp * Update spell_effects.cpp * var fix * Update spell_effects.cpp make it not apply to casted spells... oops * Update spell_effects.cpp * Update spell_effects.cpp --- common/spdat.cpp | 3 +- common/spdat.h | 8 ++- zone/spell_effects.cpp | 148 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 154 insertions(+), 5 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 42b2fe26d..525c70c1c 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1230,7 +1230,7 @@ bool IsEffectIgnoredInStacking(int spa) case SE_LimitClass: case SE_LimitRace: case SE_FcBaseEffects: - case 415: + case SE_FFItemClass: case SE_SkillDamageAmount2: case SE_FcLimitUse: case SE_FcIncreaseNumHits: @@ -1298,6 +1298,7 @@ bool IsFocusLimit(int spa) case SE_Ff_Value_Min: case SE_Ff_Value_Max: case SE_Ff_FocusTimerMin: + case SE_FFItemClass: return true; default: return false; diff --git a/common/spdat.h b/common/spdat.h index fd9413444..2e6d59d5e 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -177,7 +177,7 @@ #define EFFECT_COUNT 12 #define MAX_SPELL_TRIGGER 12 // One for each slot(only 6 for AA since AA use 2) #define MAX_RESISTABLE_EFFECTS 12 // Number of effects that are typcially checked agianst resists. -#define MaxLimitInclude 16 //Number(x 0.5) of focus Limiters that have inclusive checks used when calcing focus effects +#define MaxLimitInclude 18 //Number(x 0.5) of focus Limiters that have inclusive checks used when calcing focus effects #define MAX_SKILL_PROCS 4 //Number of spells to check skill procs from. (This is arbitrary) [Single spell can have multiple proc checks] #define MAX_AA_PROCS 16 //(Actual Proc Amount is MAX_AA_PROCS/4) Number of spells to check AA procs from. (This is arbitrary) #define MAX_SYMPATHETIC_PROCS 10 // Number of sympathetic procs a client can have (This is arbitrary) @@ -206,7 +206,9 @@ enum FocusLimitIncludes { IncludeExistsSELimitSpellClass = 12, IncludeFoundSELimitSpellClass = 13, IncludeExistsSELimitSpellSubclass = 14, - IncludeFoundSELimitSpellSubclass = 15 + IncludeFoundSELimitSpellSubclass = 15, + IncludeExistsSEFFItemClass = 16, + IncludeFoundSEFFItemClass = 17 }; /* The id's correspond to 'type' 39 in live(2021) dbstr_us gives the message for target and caster restricted effects. These are not present in the ROF2 dbstr_us. @@ -1115,7 +1117,7 @@ typedef enum { #define SE_LimitRace 412 // implemented, @Ff, Race that can use the spell focus, base1: race, Note: not used in any known live spells. Use only single race at a time. #define SE_FcBaseEffects 413 // implemented, @Fc, On Caster, base spell effectiveness mod pct, base: pct #define SE_LimitCastingSkill 414 // implemented, @Ff, Spell and singing skills(s) that a spell focus can require or exclude, base1: skill id, Include: Positive Exclude: Negative -//#define SE_FFItemClass 415 // not used - base1 matches ItemType, base2 matches SubType, -1 ignored, max is bitmask of valid slots +#define SE_FFItemClass 415 // implemented, @Ff, Limits focuses to be applied only from item click. base1: item ItemType (-1 to include for all ItemTypes,-1000 to exclude clicks from getting the focus, or exclude specific SubTypes or Slots if set), limit: item SubType (-1 for all SubTypes), max: item Slots (bitmask of valid slots, -1 ALL slots), Note: not used on live. See comments in Mob::CalcFocusEffect for more details. #define SE_ACv2 416 // implemented - New AC spell effect #define SE_ManaRegen_v2 417 // implemented - New mana regen effect #define SE_SkillDamageAmount2 418 // implemented - adds skill damage directly to certain attacks diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 163aa18c6..19320af8f 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3266,6 +3266,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_Buy_AA_Rank: case SE_Ff_FocusTimerMin: case SE_Proc_Timer_Modifier: + case SE_FFItemClass: { break; } @@ -4548,6 +4549,7 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) 10/11 SE_LimitCastingSkill: 12/13 SE_LimitSpellClass: 14/15 SE_LimitSpellSubClass: + 16/17 SE_FFItemCLass: Remember: Update MaxLimitInclude in spdat.h if adding new limits that require Includes */ @@ -4891,6 +4893,59 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) } break; + case SE_FFItemClass: + if (casting_spell_inventory_slot && casting_spell_inventory_slot != -1) { + if (IsClient() && casting_spell_slot == EQ::spells::CastingSlot::Item && casting_spell_inventory_slot != 0xFFFFFFFF) { + auto item = CastToClient()->GetInv().GetItem(casting_spell_inventory_slot); + if (item && item->GetItem()) { + //If ItemType set to < -1, then we will exclude either all Subtypes (-1000), or specific items by ItemType, SubType or Slot. See above for rules. + if (base_value < -1) { //Excludes + bool exclude_this_item = true; + int tmp_itemtype = (item->GetItem()->ItemType + 100) * -1; + //ItemType (if set to -1000, ignore and exclude any ItemType) + if (base_value < -1 && base_value != -1000) { + if (base_value != tmp_itemtype) { + exclude_this_item = false; + } + } + //SubType (if set to -1, ignore and exclude all SubTypes) + if (limit_value >= 0) { + if (limit_value != item->GetItem()->SubType) { + exclude_this_item = false; + } + } + if (exclude_this_item) { + LimitFailure = true; + } + } + else {//Includes + LimitInclude[IncludeExistsSEFFItemClass] = true; + bool include_this_item = true; + //ItemType (if set to -1, ignore and include any ItemType) + if (base_value >= 0) { + if (base_value != item->GetItem()->ItemType) { + include_this_item = false; + } + } + //SubType (if set to -1, ignore and include any SubType) + if (limit_value >= 0) { + if (limit_value != item->GetItem()->SubType) { + include_this_item = false; + } + } + if (include_this_item) { + LimitInclude[IncludeFoundSEFFItemClass] = true; + } + } + } + } + } + //If this is checking that focus can only be cast from an item, then if its not cast from item fail. + else if (base_value >= -1) { + LimitFailure = true; + } + //If we are checking to exclude items from a focus then do not fail unless the above check fails. + break; /* These are not applicable to AA's because there is never a 'caster' of the 'buff' with the focus effect. case SE_Ff_Same_Caster: @@ -5239,13 +5294,14 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo 10/11 SE_LimitCastingSkill: 12/13 SE_LimitSpellClass: 14/15 SE_LimitSpellSubClass: + 16/17 SE_FFItemCLass: Remember: Update MaxLimitInclude in spdat.h if adding new limits that require Includes */ for (int i = 0; i < EFFECT_COUNT; i++) { switch (focus_spell.effect_id[i]) { - + case SE_Blank: break; @@ -5567,6 +5623,96 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo } break; + case SE_FFItemClass: + + /* + Limits focuses to check if cast from item clicks. Can be used to INCLUDE or EXCLUDE items by ItemType and/or SubType and/or Slots + Not used on live, going on information we have plus implemented as broadly as possible to allow all possible options. + base = item table field 'ItemType' Limit = item table field 'SubType' Max = item table field 'Slots' (this is slot bitmask) + + When including: Setting base, limit, max respectively to -1 will cause it to ignore that check, letting any type or slot ect be used. + + Special rules for excluding. base value needs to be negative < -1, if excluding all ItemTypes set to -1000. + For SubType and Slots set using same rules above as for includes. Ie. -1 for all, positive for specifics + To exclude a specific ItemType we have to do some math. The exclude value will be the negative value of (ItemType + 100). + If ItemType = 10, then SET ItemType= -110 to exclude. If its ItemType 0, then SET ItemType= -100 to exclude ect. Not ideal but it works. + + Usage example: [INCLUDE] Only focus spell if from click cast and is a 'defense armor' item type=10 [base= 10, limit= -1, max= -1] + Usage example: [INCLUDE] Only focus spell if from click cast and is from helmet slot' slots= 4 [base= -1, limit= -1, max= 4] + Usage example: [EXCLUDE] Do not focus spell if it is from an item click. [base= -1000, limit= -1, max= -1] + Usage example: [EXCLUDE] Do not focus spell if it is from an item click from a helmet slot. [base= -1000, limit= -1, max= 4] + Usage example: [EXCLUDE] Do not focus spell if it is from an item click and is a 'defense armor' item type=10. [base= -110, limit= -1, max= -1] + + Note: You can apply multiple includes or excludes to a single focus spell, using multiple SPA 415 limits in the spell. Ie. Check for clicks from ItemType 10 or 11. + + */ + + if (casting_spell_inventory_slot && casting_spell_inventory_slot != -1) { + if (IsClient() && casting_spell_slot == EQ::spells::CastingSlot::Item && casting_spell_inventory_slot != 0xFFFFFFFF) { + auto item = CastToClient()->GetInv().GetItem(casting_spell_inventory_slot); + if (item && item->GetItem()) { + //If ItemType set to < -1, then we will exclude either all Subtypes (-1000), or specific items by ItemType, SubType or Slot. See above for rules. + if (focus_spell.base_value[i] < -1) { //Excludes + bool exclude_this_item = true; + int tmp_itemtype = (item->GetItem()->ItemType + 100) * -1; + //ItemType (if set to -1000, ignore and exclude any ItemType) + if (focus_spell.base_value[i] < -1 && focus_spell.base_value[i] != -1000) { + if (focus_spell.base_value[i] != tmp_itemtype) { + exclude_this_item = false; + } + } + //SubType (if set to -1, ignore and exclude all SubTypes) + if (focus_spell.limit_value[i] >= 0) { + if (focus_spell.limit_value[i] != item->GetItem()->SubType) { + exclude_this_item = false; + } + } + //item slot bitmask (if set to -1, ignore and exclude all SubTypes) + if (focus_spell.max_value[i] >= 0) { + if (focus_spell.max_value[i] != item->GetItem()->Slots) { + exclude_this_item = false; + } + } + if (exclude_this_item) { + return 0; + } + } + else {//Includes + LimitInclude[IncludeExistsSEFFItemClass] = true; + bool include_this_item = true; + //ItemType (if set to -1, ignore and include any ItemType) + if (focus_spell.base_value[i] >= 0) { + if (focus_spell.base_value[i] != item->GetItem()->ItemType) { + include_this_item = false; + } + } + //SubType (if set to -1, ignore and include any SubType) + if (focus_spell.limit_value[i] >= 0) { + if (focus_spell.limit_value[i] != item->GetItem()->SubType) { + include_this_item = false; + } + } + //item slot bitmask (if set to -1, ignore and include any slot) + if (focus_spell.max_value[i] >= 0) { + if (focus_spell.max_value[i] != item->GetItem()->Slots) { + include_this_item = false; + } + } + + if (include_this_item) { + LimitInclude[IncludeFoundSEFFItemClass] = true; + } + } + } + } + } + //If this is checking that focus can only be cast from an item, then if its not cast from item fail. + else if (focus_spell.base_value[i] >= -1) { + return 0; + } + //If we are checking to exclude items from a focus then do not fail unless the above check fails. + break; + // handle effects case SE_ImprovedDamage: if (type == focusImprovedDamage) { From e1de3d2ae06661edea27465fbb4aa2de6d12addf Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 7 Nov 2021 17:15:03 -0500 Subject: [PATCH 333/624] [Commands] Cleanup #zstats Command. (#1687) - Add new data from NewZone_Struct to command and clean up display. --- zone/command.cpp | 208 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 202 insertions(+), 6 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 2efed7ccd..ef8198958 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -2017,12 +2017,208 @@ void command_npccast(Client *c, const Seperator *sep) void command_zstats(Client *c, const Seperator *sep) { - c->Message(Chat::White, "Zone Header Data:"); - c->Message(Chat::White, "Sky Type: %i", zone->newzone_data.sky); - c->Message(Chat::White, "Fog Colour: Red: %i; Blue: %i; Green %i", zone->newzone_data.fog_red[0], zone->newzone_data.fog_green[0], zone->newzone_data.fog_blue[0]); - c->Message(Chat::White, "Safe Coords: %f, %f, %f", zone->newzone_data.safe_x, zone->newzone_data.safe_y, zone->newzone_data.safe_z); - c->Message(Chat::White, "Underworld Coords: %f", zone->newzone_data.underworld); - c->Message(Chat::White, "Clip Plane: %f - %f", zone->newzone_data.minclip, zone->newzone_data.maxclip); + // Zone + c->Message( + Chat::White, + fmt::format( + "Zone | ID: {} Instance ID: {} Name: {} ({})", + zone->GetZoneID(), + zone->GetInstanceID(), + zone->GetLongName(), + zone->GetShortName() + ).c_str() + ); + + // Type + c->Message( + Chat::White, + fmt::format( + "Type: {}", + zone->newzone_data.ztype + ).c_str() + ); + + // Fog Data + for (int fog_index = 0; fog_index < 4; fog_index++) { + int fog_number = (fog_index + 1); + c->Message( + Chat::White, + fmt::format( + "Fog {} Colors | Red: {} Blue: {} Green: {} ", + fog_number, + zone->newzone_data.fog_red[fog_index], + zone->newzone_data.fog_green[fog_index], + zone->newzone_data.fog_blue[fog_index] + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Fog {} Clipping Distance | Min: {} Max: {}", + fog_number, + zone->newzone_data.fog_minclip[fog_index], + zone->newzone_data.fog_maxclip[fog_index] + ).c_str() + ); + } + + // Fog Density + c->Message( + Chat::White, + fmt::format( + "Fog Density: {}", + zone->newzone_data.fog_density + ).c_str() + ); + + + // Gravity + c->Message( + Chat::White, + fmt::format( + "Gravity: {}", + zone->newzone_data.gravity + ).c_str() + ); + + // Time Type + c->Message( + Chat::White, + fmt::format( + "Time Type: {}", + zone->newzone_data.time_type + ).c_str() + ); + + // Experience Multiplier + c->Message( + Chat::White, + fmt::format( + "Experience Multiplier: {}", + zone->newzone_data.zone_exp_multiplier + ).c_str() + ); + + // Safe Coordinates + c->Message( + Chat::White, + fmt::format( + "Safe Coordinates: {}, {}, {}", + zone->newzone_data.safe_x, + zone->newzone_data.safe_y, + zone->newzone_data.safe_z + ).c_str() + ); + + // Max Z + c->Message( + Chat::White, + fmt::format( + "Max Z: {}", + zone->newzone_data.max_z + ).c_str() + ); + + // Underworld Z + c->Message( + Chat::White, + fmt::format( + "Underworld Z: {}", + zone->newzone_data.underworld + ).c_str() + ); + + // Clipping Distance + c->Message( + Chat::White, + fmt::format( + "Clipping Distance | Min: {} Max: {}", + zone->newzone_data.minclip, + zone->newzone_data.maxclip + ).c_str() + ); + + // Weather Data + for (int weather_index = 0; weather_index < 4; weather_index++) { + int weather_number = (weather_index + 1); + c->Message( + Chat::White, + fmt::format( + "Rain {} | Chance: {} Duration: {} ", + weather_number, + zone->newzone_data.rain_chance[weather_index], + zone->newzone_data.rain_duration[weather_index] + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Snow {} | Chance: {} Duration: {}", + weather_number, + zone->newzone_data.snow_chance[weather_index], + zone->newzone_data.snow_duration[weather_index] + ).c_str() + ); + } + + // Sky Type + c->Message( + Chat::White, + fmt::format( + "Sky Type: {}", + zone->newzone_data.sky + ).c_str() + ); + + // Suspend Buffs + c->Message( + Chat::White, + fmt::format( + "Suspend Buffs: {}", + zone->newzone_data.SuspendBuffs + ).c_str() + ); + + // Regeneration Data + c->Message( + Chat::White, + fmt::format( + "Regen | Health: {} Mana: {} Endurance: {}", + zone->newzone_data.FastRegenHP, + zone->newzone_data.FastRegenMana, + zone->newzone_data.FastRegenEndurance + ).c_str() + ); + + // NPC Max Aggro Distance + c->Message( + Chat::White, + fmt::format( + "NPC Max Aggro Distance: {}", + zone->newzone_data.NPCAggroMaxDist + ).c_str() + ); + + // Underworld Teleport Index + c->Message( + Chat::White, + fmt::format( + "Underworld Teleport Index: {}", + zone->newzone_data.underworld_teleport_index + ).c_str() + ); + + // Lava Damage + c->Message( + Chat::White, + fmt::format( + "Lava Damage | Min: {} Max: {}", + zone->newzone_data.MinLavaDamage, + zone->newzone_data.LavaDamage + ).c_str() + ); } void command_permaclass(Client *c, const Seperator *sep) From 194c71727dbbd7c6e08ae6b19a42f80623e10398 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 7 Nov 2021 17:20:43 -0500 Subject: [PATCH 334/624] [Commands] Cleanup #npcstats Command. (#1690) - Cleanup menu and add stats that were not there before. - Only display some data if necessary (i.e only show loot/money if they have loot/money) - Add skill name helper method. - Add faction name helper method. - Add Charmed stats and other getter methods. - Cleanup QueryLoot() method. --- common/skills.cpp | 110 ++------- common/skills.h | 1 + zone/command.cpp | 614 ++++++++++++++++++++++++++++++++++++++++++++-- zone/npc.cpp | 82 +++++-- zone/npc.h | 19 ++ zone/questmgr.cpp | 10 +- zone/zonedb.cpp | 16 ++ zone/zonedb.h | 1 + 8 files changed, 697 insertions(+), 156 deletions(-) diff --git a/common/skills.cpp b/common/skills.cpp index 81b1a926c..0d423f556 100644 --- a/common/skills.cpp +++ b/common/skills.cpp @@ -177,8 +177,7 @@ bool EQ::skills::IsMeleeDmg(SkillType skill) const std::map& EQ::skills::GetSkillTypeMap() { - /* VS2013 code - static const std::map skill_use_types_map = { + static const std::map skill_type_map = { { Skill1HBlunt, "1H Blunt" }, { Skill1HSlashing, "1H Slashing" }, { Skill2HBlunt, "2H Blunt" }, @@ -258,103 +257,22 @@ const std::map& EQ::skills::GetSkillTypeMap( { SkillTripleAttack, "Triple Attack" }, { Skill2HPiercing, "2H Piercing" } }; - */ - - /* VS2012 code - begin */ - - static const char* skill_use_names[SkillCount] = { - "1H Blunt", - "1H Slashing", - "2H Blunt", - "2H Slashing", - "Abjuration", - "Alteration", - "Apply Poison", - "Archery", - "Backstab", - "Bind Wound", - "Bash", - "Block", - "Brass Instruments", - "Channeling", - "Conjuration", - "Defense", - "Disarm", - "Disarm Traps", - "Divination", - "Dodge", - "Double Attack", - "Dragon Punch", - "Dual Wield", - "Eagle Strike", - "Evocation", - "Feign Death", - "Flying Kick", - "Forage", - "Hand to Hand", - "Hide", - "Kick", - "Meditate", - "Mend", - "Offense", - "Parry", - "Pick Lock", - "1H Piercing", - "Riposte", - "Round Kick", - "Safe Fall", - "Sense Heading", - "Singing", - "Sneak", - "Specialize Abjuration", - "Specialize Alteration", - "Specialize Conjuration", - "Specialize Divination", - "Specialize Evocation", - "Pick Pockets", - "Stringed Instruments", - "Swimming", - "Throwing", - "Tiger Claw", - "Tracking", - "Wind Instruments", - "Fishing", - "Make Poison", - "Tinkering", - "Research", - "Alchemy", - "Baking", - "Tailoring", - "Sense Traps", - "Blacksmithing", - "Fletching", - "Brewing", - "Alcohol Tolerance", - "Begging", - "Jewelry Making", - "Pottery", - "Percussion Instruments", - "Intimidation", - "Berserking", - "Taunt", - "Frenzy", - "Remove Traps", - "Triple Attack", - "2H Piercing" - }; - - static std::map skill_type_map; - - skill_type_map.clear(); - - for (int i = Skill1HBlunt; i < SkillCount; ++i) - skill_type_map[(SkillType)i] = skill_use_names[i]; - - /* VS2012 code - end */ - return skill_type_map; } +std::string EQ::skills::GetSkillName(SkillType skill) +{ + if (skill >= Skill1HBlunt && skill <= Skill2HPiercing) { + std::map skills = GetSkillTypeMap(); + for (auto current_skill : skills) { + if (skill == current_skill.first) { + return current_skill.second; + } + } + } + return std::string(); +} + EQ::SkillProfile::SkillProfile() { memset(&Skill, 0, (sizeof(uint32) * PACKET_SKILL_ARRAY_SIZE)); diff --git a/common/skills.h b/common/skills.h index 81790ede1..38f549015 100644 --- a/common/skills.h +++ b/common/skills.h @@ -171,6 +171,7 @@ namespace EQ extern const std::map& GetSkillTypeMap(); + std::string GetSkillName(SkillType skill); } /*skills*/ struct SkillProfile { // prototype - not implemented diff --git a/zone/command.cpp b/zone/command.cpp index ef8198958..24f9924b0 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -1935,34 +1935,596 @@ void command_fov(Client *c, const Seperator *sep) void command_npcstats(Client *c, const Seperator *sep) { - if (c->GetTarget() == 0) - c->Message(Chat::White, "ERROR: No target!"); - else if (!c->GetTarget()->IsNPC()) - c->Message(Chat::White, "ERROR: Target is not a NPC!"); - else { - auto target_npc = c->GetTarget()->CastToNPC(); - c->Message(Chat::White, "# NPC Stats"); - c->Message(Chat::White, "- Name: %s NpcID: %u", target_npc->GetName(), target_npc->GetNPCTypeID()); - c->Message(Chat::White, "- Race: %i Level: %i Class: %i Material: %i", target_npc->GetRace(), target_npc->GetLevel(), target_npc->GetClass(), target_npc->GetTexture()); - c->Message(Chat::White, "- Current HP: %i Max HP: %i", target_npc->GetHP(), target_npc->GetMaxHP()); - //c->Message(Chat::White, "Weapon Item Number: %s", target_npc->GetWeapNo()); - c->Message(Chat::White, "- Gender: %i Size: %f Bodytype: %d", target_npc->GetGender(), target_npc->GetSize(), target_npc->GetBodyType()); - c->Message(Chat::White, "- Runspeed: %.3f Walkspeed: %.3f", static_cast(0.025f * target_npc->GetRunspeed()), static_cast(0.025f * target_npc->GetWalkspeed())); - c->Message(Chat::White, "- Spawn Group: %i Grid: %i", target_npc->GetSpawnGroupId(), target_npc->GetGrid()); - if (target_npc->proximity) { - c->Message(Chat::White, "- Proximity: Enabled"); - c->Message(Chat::White, "-- Cur_X: %1.3f, Cur_Y: %1.3f, Cur_Z: %1.3f", target_npc->GetX(), target_npc->GetY(), target_npc->GetZ()); - c->Message(Chat::White, "-- Min_X: %1.3f(%1.3f), Max_X: %1.3f(%1.3f), X_Range: %1.3f", target_npc->proximity->min_x, (target_npc->proximity->min_x - target_npc->GetX()), target_npc->proximity->max_x, (target_npc->proximity->max_x - target_npc->GetX()), (target_npc->proximity->max_x - target_npc->proximity->min_x)); - c->Message(Chat::White, "-- Min_Y: %1.3f(%1.3f), Max_Y: %1.3f(%1.3f), Y_Range: %1.3f", target_npc->proximity->min_y, (target_npc->proximity->min_y - target_npc->GetY()), target_npc->proximity->max_y, (target_npc->proximity->max_y - target_npc->GetY()), (target_npc->proximity->max_y - target_npc->proximity->min_y)); - c->Message(Chat::White, "-- Min_Z: %1.3f(%1.3f), Max_Z: %1.3f(%1.3f), Z_Range: %1.3f", target_npc->proximity->min_z, (target_npc->proximity->min_z - target_npc->GetZ()), target_npc->proximity->max_z, (target_npc->proximity->max_z - target_npc->GetZ()), (target_npc->proximity->max_z - target_npc->proximity->min_z)); - c->Message(Chat::White, "-- Say: %s", (target_npc->proximity->say ? "Enabled" : "Disabled")); + if (c->GetTarget() && c->GetTarget()->IsNPC()) { + auto target = c->GetTarget()->CastToNPC(); + std::string target_name = target->GetCleanName(); + std::string target_last_name = target->GetLastName(); + bool has_charmed_stats = ( + target->GetCharmedAccuracy() != 0 || + target->GetCharmedArmorClass() != 0 || + target->GetCharmedAttack() != 0 || + target->GetCharmedAttackDelay() != 0 || + target->GetCharmedAvoidance() != 0 || + target->GetCharmedMaxDamage() != 0 || + target->GetCharmedMinDamage() != 0 + ); + + // Spawn Data + c->Message( + Chat::White, + fmt::format( + "Spawn | Group: {} Point: {} Grid: {}", + target->GetSpawnGroupId(), + target->GetSpawnPointID(), + target->GetGrid() + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Spawn | Raid: {} Rare: {}", + target->IsRaidTarget() ? "Yes" : "No", + target->IsRareSpawn() ? "Yes" : "No", + target->GetSkipGlobalLoot() ? "Yes" : "No" + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Spawn | Skip Global Loot: {} Ignore Despawn: {}", + target->GetSkipGlobalLoot() ? "Yes" : "No", + target->GetIgnoreDespawn() ? "Yes" : "No" + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Spawn | Findable: {} Trackable: {} Underwater: {}", + target->IsFindable() ? "Yes" : "No", + target->IsTrackable() ? "Yes" : "No", + target->IsUnderwaterOnly() ? "Yes" : "No" + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Spawn | Stuck Behavior: {} Fly Mode: {}", + target->GetStuckBehavior(), + static_cast(target->GetFlyMode()) + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Spawn | Aggro NPCs: {} Always Aggro: {}", + target->GetNPCAggro() ? "Yes" : "No", + target->GetAlwaysAggro() ? "Yes" : "No" + ).c_str() + ); + + // NPC + c->Message( + Chat::White, + fmt::format( + "NPC | ID: {} Name: {}{} Level: {}", + target->GetNPCTypeID(), + target_name, + ( + !target_last_name.empty() ? + fmt::format(" ({})", target_last_name) : + "" + ), + target->GetLevel() + ).c_str() + ); + + // Race / Class / Gender + c->Message( + Chat::White, + fmt::format( + "Race: {} ({}) Class: {} ({}) Gender: {} ({})", + GetRaceIDName(target->GetRace()), + target->GetRace(), + GetClassIDName(target->GetClass()), + target->GetClass(), + GetGenderName(target->GetGender()), + target->GetGender() + ).c_str() + ); + + // Faction + if (target->GetNPCFactionID()) { + auto faction_id = target->GetNPCFactionID(); + auto faction_name = content_db.GetFactionName(faction_id); + c->Message( + Chat::White, + fmt::format( + "Faction: {} ({})", + faction_name, + faction_id + ).c_str() + ); } - else { - c->Message(Chat::White, "-Proximity: Disabled"); + + // Adventure Template + if (target->GetAdventureTemplate()) { + c->Message( + Chat::White, + fmt::format( + "Adventure Template: {}", + target->GetAdventureTemplate() + ).c_str() + ); } - c->Message(Chat::White, ""); - c->Message(Chat::White, "EmoteID: %i", target_npc->GetEmoteID()); - target_npc->QueryLoot(c); + + // Body + c->Message( + Chat::White, + fmt::format( + "Body | Size: {:.2f} Type: {}", + target->GetSize(), + target->GetBodyType() + ).c_str() + ); + + // Face + c->Message( + Chat::White, + fmt::format( + "Features | Face: {} Eye One: {} Eye Two: {}", + target->GetLuclinFace(), + target->GetEyeColor1(), + target->GetEyeColor2() + ).c_str() + ); + + // Hair + c->Message( + Chat::White, + fmt::format( + "Features | Hair: {} Hair Color: {}", + target->GetHairStyle(), + target->GetHairColor() + ).c_str() + ); + + // Beard + c->Message( + Chat::White, + fmt::format( + "Features | Beard: {} Beard Color: {}", + target->GetBeard(), + target->GetBeardColor() + ).c_str() + ); + + // Drakkin Features + if (target->GetRace() == RACE_DRAKKIN_522) { + c->Message( + Chat::White, + fmt::format( + "Drakkin Features | Heritage: {} Tattoo: {} Details: {}", + target->GetDrakkinHeritage(), + target->GetDrakkinTattoo(), + target->GetDrakkinDetails() + ).c_str() + ); + } + + // Textures + c->Message( + Chat::White, + fmt::format( + "Textures | Armor: {} Helmet: {}", + target->GetTexture(), + target->GetHelmTexture() + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Textures | Arms: {} Bracers: {} Hands: {}", + target->GetArmTexture(), + target->GetBracerTexture(), + target->GetHandTexture() + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Textures | Legs: {} Feet: {}", + target->GetLegTexture(), + target->GetFeetTexture() + ).c_str() + ); + + // Hero's Forge + if (target->GetHeroForgeModel()) { + c->Message( + Chat::White, + fmt::format( + "Hero's Forge: {}", + target->GetHeroForgeModel() + ).c_str() + ); + } + + // Owner Data + if (target->GetOwner()) { + auto owner_name = target->GetOwner()->GetCleanName(); + auto owner_type = ( + target->GetOwner()->IsNPC() ? + "NPC" : + ( + target->GetOwner()->IsClient() ? + "Client" : + "Other" + ) + ); + auto owner_id = target->GetOwnerID(); + c->Message( + Chat::White, + fmt::format( + "Owner | Name: {} ({}) Type: {}", + owner_name, + owner_id, + owner_type + ).c_str() + ); + } + + // Pet Data + if (target->GetPet()) { + auto pet_name = target->GetPet()->GetCleanName(); + auto pet_id = target->GetPetID(); + c->Message( + Chat::White, + fmt::format( + "Pet | Name: {} ({})", + pet_name, + pet_id + ).c_str() + ); + } + + // Merchant Data + if (target->MerchantType) { + c->Message( + Chat::White, + fmt::format( + "Merchant | ID: {} Currency Type: {}", + target->MerchantType, + target->GetAltCurrencyType() + ).c_str() + ); + } + + // Spell Data + if (target->AI_HasSpells() || target->AI_HasSpellsEffects()) { + c->Message( + Chat::White, + fmt::format( + "Spells | ID: {} Effects ID: {}", + target->GetNPCSpellsID(), + target->GetNPCSpellsEffectsID() + ).c_str() + ); + } + + // Health + c->Message( + Chat::White, + fmt::format( + "Health: {}/{} ({:.2f}%) Regen: {}", + target->GetHP(), + target->GetMaxHP(), + target->GetHPRatio(), + target->GetHPRegen() + ).c_str() + ); + + // Mana + if (target->GetMaxMana() > 0) { + c->Message( + Chat::White, + fmt::format( + "Mana: {}/{} ({:.2f}%) Regen: {}", + target->GetMana(), + target->GetMaxMana(), + target->GetManaRatio(), + target->GetManaRegen() + ).c_str() + ); + } + + // Damage + c->Message( + Chat::White, + fmt::format( + "Damage | Min: {} Max: {}", + target->GetMinDMG(), + target->GetMaxDMG() + ).c_str() + ); + + // Attack Count / Delay + c->Message( + Chat::White, + fmt::format( + "Attack | Count: {} Delay: {}", + target->GetNumberOfAttacks(), + target->GetAttackDelay() + ).c_str() + ); + + // Weapon Textures + c->Message( + Chat::White, + fmt::format( + "Weapon Textures | Primary: {} Secondary: {} Ammo: {}", + target->GetEquipmentMaterial(EQ::textures::weaponPrimary), + target->GetEquipmentMaterial(EQ::textures::weaponSecondary), + target->GetAmmoIDfile() + ).c_str() + ); + + // Weapon Types + c->Message( + Chat::White, + fmt::format( + "Weapon Types | Primary: {} ({}) Secondary: {} ({})", + EQ::skills::GetSkillName(static_cast(target->GetPrimSkill())), + target->GetPrimSkill(), + EQ::skills::GetSkillName(static_cast(target->GetSecSkill())), + target->GetSecSkill() + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Weapon Types | Ranged: {} ({})", + EQ::skills::GetSkillName(static_cast(target->GetRangedSkill())), + target->GetRangedSkill() + ).c_str() + ); + + // Combat Stats + c->Message( + Chat::White, + fmt::format( + "Combat Stats | Accuracy: {} Armor Class: {} Attack: {}", + target->GetAccuracyRating(), + target->GetAC(), + target->GetATK() + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Combat Stats | Avoidance: {} Slow Mitigation: {}", + target->GetAvoidanceRating(), + target->GetSlowMitigation() + ).c_str() + ); + + // Stats + c->Message( + Chat::White, + fmt::format( + "Stats | Agility: {} Charisma: {} Dexterity: {} Intelligence: {}", + target->GetAGI(), + target->GetCHA(), + target->GetDEX(), + target->GetINT() + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Stats | Stamina: {} Strength: {} Wisdom: {}", + target->GetSTA(), + target->GetSTR(), + target->GetWIS() + ).c_str() + ); + + // Charmed Stats + if (has_charmed_stats) { + c->Message( + Chat::White, + fmt::format( + "Charmed Stats | Attack: {} Attack Delay: {}", + target->GetCharmedAttack(), + target->GetCharmedAttackDelay() + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Charmed Stats | Accuracy: {} Avoidance: {}", + target->GetCharmedAccuracy(), + target->GetCharmedAvoidance() + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Charmed Stats | Min Damage: {} Max Damage: {}", + target->GetCharmedMinDamage(), + target->GetCharmedMaxDamage() + ).c_str() + ); + } + + // Resists + c->Message( + Chat::White, + fmt::format( + "Resists | Cold: {} Disease: {} Fire: {} Magic: {}", + target->GetCR(), + target->GetDR(), + target->GetFR(), + target->GetMR() + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Resists | Poison: {} Corruption: {} Physical: {}", + target->GetPR(), + target->GetCorrup(), + target->GetPhR() + ).c_str() + ); + + // Scaling + c->Message( + Chat::White, + fmt::format( + "Scaling | Heal: {} Spell: {}", + target->GetHealScale(), + target->GetSpellScale() + ).c_str() + ); + + // See Invisible / Invisible vs. Undead / Hide / Improved Hide + c->Message( + Chat::White, + fmt::format( + "Can See | Invisible: {} Invisible vs. Undead: {}", + target->SeeInvisible() ? "Yes" : "No", + target->SeeInvisibleUndead() ? "Yes" : "No" + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Can See | Hide: {} Improved Hide: {}", + target->SeeHide() ? "Yes" : "No", + target->SeeImprovedHide() ? "Yes" : "No" + ).c_str() + ); + + // Aggro / Assist Radius + c->Message( + Chat::White, + fmt::format( + "Radius | Aggro: {} Assist: {}", + target->GetAggroRange(), + target->GetAssistRange() + ).c_str() + ); + + // Emote + c->Message( + Chat::White, + fmt::format( + "Emote: {}", + target->GetEmoteID() + ).c_str() + ); + + // Run/Walk Speed + c->Message( + Chat::White, + fmt::format( + "Speed | Run: {} Walk: {}", + target->GetRunspeed(), + target->GetWalkspeed() + ).c_str() + ); + + // Position + c->Message( + Chat::White, + fmt::format( + "Position | {}, {}, {}, {}", + target->GetX(), + target->GetY(), + target->GetZ(), + target->GetHeading() + ).c_str() + ); + + // Experience Modifier + c->Message( + Chat::White, + fmt::format( + "Experience Modifier: {}", + target->GetKillExpMod() + ).c_str() + ); + + // Quest Globals + c->Message( + Chat::White, + fmt::format( + "Quest Globals: {}", + target->qglobal ? "Enabled" : "Disabled" + ).c_str() + ); + + // Proximity + if (target->IsProximitySet()) { + c->Message( + Chat::White, + fmt::format( + "Proximity | Say: {}", + target->proximity->say ? "Enabled" : "Disabled" + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Proximity X | Min: {} Max: {} Range: {}", + target->GetProximityMinX(), + target->GetProximityMinX(), + (target->GetProximityMinX() - target->GetProximityMinX()) + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Proximity Y | Min: {} Max: {} Range: {}", + target->GetProximityMinY(), + target->GetProximityMaxY(), + (target->GetProximityMaxY() - target->GetProximityMinY()) + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Proximity Z | Min: {} Max: {} Range: {}", + target->GetProximityMinZ(), + target->GetProximityMaxZ(), + (target->GetProximityMaxZ() - target->GetProximityMinZ()) + ).c_str() + ); + } + + // Loot Data + if (target->GetLoottableID()) { + target->QueryLoot(c); + } + } else { + c->Message(Chat::White, "You must target an NPC to use this command."); } } diff --git a/zone/npc.cpp b/zone/npc.cpp index ef9932ba3..c44bb15f6 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -643,35 +643,67 @@ void NPC::ClearItemList() { void NPC::QueryLoot(Client* to) { - to->Message(Chat::White, "| # Current Loot (%s) LootTableID: %i", GetName(), GetLoottableID()); - - int item_count = 0; - for (auto cur = itemlist.begin(); cur != itemlist.end(); ++cur, ++item_count) { - if (!(*cur)) { - LogError("NPC::QueryLoot() - ItemList error, null item"); - continue; - } - if (!(*cur)->item_id || !database.GetItem((*cur)->item_id)) { - LogError("NPC::QueryLoot() - Database error, invalid item"); - continue; - } - - EQ::SayLinkEngine linker; - linker.SetLinkType(EQ::saylink::SayLinkLootItem); - linker.SetLootData(*cur); - + if (itemlist.size() > 0) { to->Message( - 0, - "| -- Item %i: %s ID: %u min_level: %u max_level: %u", - item_count, - linker.GenerateLink().c_str(), - (*cur)->item_id, - (*cur)->trivial_min_level, - (*cur)->trivial_max_level + Chat::White, + fmt::format( + "Loot | Name: {} ID: {} Loottable ID: {}", + GetName(), + GetNPCTypeID(), + GetLoottableID() + ).c_str() ); + + int item_count = 0; + for (auto current_item : itemlist) { + int item_number = (item_count + 1); + if (!current_item) { + LogError("NPC::QueryLoot() - ItemList error, null item."); + continue; + } + + if (!current_item->item_id || !database.GetItem(current_item->item_id)) { + LogError("NPC::QueryLoot() - Database error, invalid item."); + continue; + } + + EQ::SayLinkEngine linker; + linker.SetLinkType(EQ::saylink::SayLinkLootItem); + linker.SetLootData(current_item); + + to->Message( + Chat::White, + fmt::format( + "Item {} | Name: {} ID: {} Min Level: {} Max Level: {}", + item_number, + linker.GenerateLink().c_str(), + current_item->item_id, + current_item->trivial_min_level, + current_item->trivial_max_level + ).c_str() + ); + item_count++; + } } - to->Message(Chat::White, "| %i Platinum %i Gold %i Silver %i Copper", platinum, gold, silver, copper); + bool has_money = ( + platinum > 0 || + gold > 0 || + silver > 0 || + copper > 0 + ); + if (has_money) { + to->Message( + Chat::White, + fmt::format( + "Money | Platinum: {} Gold: {} Silver: {} Copper: {}", + platinum, + gold, + silver, + copper + ).c_str() + ); + } } bool NPC::HasItem(uint32 item_id) { diff --git a/zone/npc.h b/zone/npc.h index 6f0c221f4..ad7d14c86 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -497,6 +497,25 @@ public: uint32 GetRoamboxDelay() const; uint32 GetRoamboxMinDelay() const; + inline uint8 GetArmTexture() { return armtexture; } + inline uint8 GetBracerTexture() { return bracertexture; } + inline uint8 GetHandTexture() { return handtexture; } + inline uint8 GetFeetTexture() { return feettexture; } + inline uint8 GetLegTexture() { return legtexture; } + + inline int GetCharmedAccuracy() { return charm_accuracy_rating; } + inline int GetCharmedArmorClass() { return charm_ac; } + inline int GetCharmedAttack() { return charm_atk; } + inline int GetCharmedAttackDelay() { return charm_attack_delay; } + inline int GetCharmedAvoidance() { return charm_avoidance_rating; } + inline int GetCharmedMaxDamage() { return charm_max_dmg; } + inline int GetCharmedMinDamage() { return charm_min_dmg; } + + inline bool GetAlwaysAggro() { return always_aggro; } + inline bool GetNPCAggro() { return npc_aggro; } + inline bool GetIgnoreDespawn() { return ignore_despawn; } + inline bool GetSkipGlobalLoot() { return skip_global_loot; } + std::unique_ptr AIautocastspell_timer; virtual int GetStuckBehavior() const { return NPCTypedata_ours ? NPCTypedata_ours->stuck_behavior : NPCTypedata->stuck_behavior; } diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 956b1882b..b7a8976fb 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1012,15 +1012,7 @@ std::string QuestManager::getspellname(uint32 spell_id) { } std::string QuestManager::getskillname(int skill_id) { - if (skill_id >= 0 && skill_id < EQ::skills::SkillCount) { - std::map Skills = EQ::skills::GetSkillTypeMap(); - for (auto skills_iter : Skills) { - if (skill_id == skills_iter.first) { - return skills_iter.second; - } - } - } - return std::string(); + return EQ::skills::GetSkillName(static_cast(skill_id)); } void QuestManager::safemove() { diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 10ff77325..f382c300e 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -3981,6 +3981,22 @@ bool ZoneDatabase::GetFactionName(int32 faction_id, char* name, uint32 buflen) { } +std::string ZoneDatabase::GetFactionName(int32 faction_id) +{ + std::string faction_name; + if ( + faction_id <= 0 || + faction_id > static_cast(max_faction) || + !faction_array[faction_id] + ) { + return faction_name; + } + + faction_name = faction_array[faction_id]->name; + + return faction_name; +} + //o-------------------------------------------------------------- //| Name: GetNPCFactionList; Dec. 16, 2001 //o-------------------------------------------------------------- diff --git a/zone/zonedb.h b/zone/zonedb.h index 536d3aa86..054317256 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -403,6 +403,7 @@ public: bool GetNPCFactionList(uint32 npcfaction_id, int32* faction_id, int32* value, uint8* temp, int32* primary_faction = 0); bool GetFactionData(FactionMods* fd, uint32 class_mod, uint32 race_mod, uint32 deity_mod, int32 faction_id); //needed for factions Dec, 16 2001 bool GetFactionName(int32 faction_id, char* name, uint32 buflen); // needed for factions Dec, 16 2001 + std::string GetFactionName(int32 faction_id); bool GetFactionIdsForNPC(uint32 nfl_id, std::list *faction_list, int32* primary_faction = 0); // improve faction handling bool SetCharacterFactionLevel(uint32 char_id, int32 faction_id, int32 value, uint8 temp, faction_map &val_list); // needed for factions Dec, 16 2001 bool LoadFactionData(); From 062fb73f032e27e6a9d93f43daece5b4cc367236 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 7 Nov 2021 17:20:55 -0500 Subject: [PATCH 335/624] [Commands] Remove #test, #spon, and #spoff Commands. (#1686) - These commands are unused or outdated. --- zone/command.cpp | 28 ---------------------------- zone/command.h | 3 --- 2 files changed, 31 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 24f9924b0..c537377bf 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -411,8 +411,6 @@ int command_init(void) command_add("spawnfix", "- Find targeted NPC in database based on its X/Y/heading and update the database to make it spawn at your current location/heading.", 170, command_spawnfix) || command_add("spawnstatus", "- Show respawn timer status", 100, command_spawnstatus) || command_add("spellinfo", "[spellid] - Get detailed info about a spell", 10, command_spellinfo) || - command_add("spoff", "- Sends OP_ManaChange", 80, command_spoff) || - command_add("spon", "- Sends OP_MemorizeSpell", 80, command_spon) || command_add("stun", "[duration] - Stuns you or your target for duration", 100, command_stun) || command_add("summon", "[charname] - Summons your player/npc/corpse target, or charname if specified", 80, command_summon) || command_add("summonburiedplayercorpse", "- Summons the target's oldest buried corpse, if any exist.", 100, command_summonburiedplayercorpse) || @@ -422,7 +420,6 @@ int command_init(void) command_add("tattoo", "- Change the tattoo of your target (Drakkin Only)", 80, command_tattoo) || command_add("tempname", "[newname] - Temporarily renames your target. Leave name blank to restore the original name.", 100, command_tempname) || command_add("petname", "[newname] - Temporarily renames your pet. Leave name blank to restore the original name.", 100, command_petname) || - command_add("test", "Test command", 200, command_test) || command_add("texture", "[texture] [helmtexture] - Change your or your target's appearance, use 255 to show equipment", 10, command_texture) || command_add("time", "[HH] [MM] - Set EQ time", 90, command_time) || command_add("timers", "- Display persistent timers for target", 200, command_timers) || @@ -2978,19 +2975,6 @@ void command_zcolor(Client *c, const Seperator *sep) } } -void command_spon(Client *c, const Seperator *sep) -{ - c->MemorizeSpell(0, SPELLBAR_UNLOCK, memSpellSpellbar); -} - -void command_spoff(Client *c, const Seperator *sep) -{ - auto outapp = new EQApplicationPacket(OP_ManaChange, 0); - outapp->priority = 5; - c->QueuePacket(outapp); - safe_delete(outapp); -} - void command_gassign(Client *c, const Seperator *sep) { if (sep->IsNumber(1) && c->GetTarget() && c->GetTarget()->IsNPC() && c->GetTarget()->CastToNPC()->GetSpawnPointID() > 0) { @@ -4364,18 +4348,6 @@ void command_spawn(Client *c, const Seperator *sep) } } -void command_test(Client *c, const Seperator *sep) -{ - c->Message(Chat::Yellow, "Triggering test command"); - - if (sep->arg[1]) { - c->SetPrimaryWeaponOrnamentation(atoi(sep->arg[1])); - } - if (sep->arg[2]) { - c->SetSecondaryWeaponOrnamentation(atoi(sep->arg[2])); - } -} - void command_texture(Client *c, const Seperator *sep) { diff --git a/zone/command.h b/zone/command.h index 59926536d..c2dee0251 100644 --- a/zone/command.h +++ b/zone/command.h @@ -308,8 +308,6 @@ void command_spawneditmass(Client *c, const Seperator *sep); void command_spawnfix(Client *c, const Seperator *sep); void command_spawnstatus(Client *c, const Seperator *sep); void command_spellinfo(Client *c, const Seperator *sep); -void command_spoff(Client *c, const Seperator *sep); -void command_spon(Client *c, const Seperator *sep); void command_stun(Client *c, const Seperator *sep); void command_summon(Client *c, const Seperator *sep); void command_summonburiedplayercorpse(Client *c, const Seperator *sep); @@ -320,7 +318,6 @@ void command_task(Client *c, const Seperator *sep); void command_tattoo(Client *c, const Seperator *sep); void command_tempname(Client *c, const Seperator *sep); void command_petname(Client *c, const Seperator *sep); -void command_test(Client *c, const Seperator *sep); void command_testspawn(Client *c, const Seperator *sep); void command_testspawnkill(Client *c, const Seperator *sep); void command_texture(Client *c, const Seperator *sep); From 30fdb189456669b05365d2b2feb32f7a28aac023 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 7 Nov 2021 17:21:04 -0500 Subject: [PATCH 336/624] [Bug Fix] Fix Elemental Illusion spells not using proper texture. (#1691) --- zone/spell_effects.cpp | 47 +++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 19320af8f..2fbbcb58c 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1446,35 +1446,44 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove gender_id ); - if (spell.max_value[i] > 0) { - if (spell.limit_value[i] == 0) { - SendIllusionPacket( - spell.base_value[i], - gender_id - ); - } else { - if (spell.max_value[i] != 3) { + if (spell.base_value[i] != RACE_ELEMENTAL_75) { + if (spell.max_value[i] > 0) { + if (spell.limit_value[i] == 0) { SendIllusionPacket( spell.base_value[i], - gender_id, - spell.limit_value[i], - spell.max_value[i] + gender_id ); } else { - SendIllusionPacket( - spell.base_value[i], - gender_id, - spell.limit_value[i], - spell.limit_value[i] - ); + if (spell.max_value[i] != 3) { + SendIllusionPacket( + spell.base_value[i], + gender_id, + spell.limit_value[i], + spell.max_value[i] + ); + } else { + SendIllusionPacket( + spell.base_value[i], + gender_id, + spell.limit_value[i], + spell.limit_value[i] + ); + } } + } else { + SendIllusionPacket( + spell.base_value[i], + gender_id, + spell.limit_value[i], + spell.max_value[i] + ); } + } else { SendIllusionPacket( spell.base_value[i], gender_id, - spell.limit_value[i], - spell.max_value[i] + spell.limit_value[i] ); } SendAppearancePacket(AT_Size, race_size); From f8cbc2faed1896f0af8aa60d77a81f68b27dc989 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 7 Nov 2021 17:21:22 -0500 Subject: [PATCH 337/624] [Commands] Further implement #worldwide functionality. (#1693) - Add #worldwide remove [Spell ID] - Removes a spell from player buffs worldwide. - Add #worldwide message [Message] - Sends a worldwide message in Chat::Yellow. - Add #worldwide move [Zone ID] or #worldwide move [Zone Short Name] - Moves every player in the game to the specified zone. - Add #worldwide moveinstance [Instance ID] - Moves every player in the game to the specified instance. - All but `#worldwide message` send a message to sender client. --- zone/command.cpp | 125 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 118 insertions(+), 7 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index c537377bf..272f1d4e6 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -757,20 +757,131 @@ void command_worldwide(Client *c, const Seperator *sep) } if (sub_command == "cast") { - if (sep->arg[2][0] && Seperator::IsNumber(sep->arg[2])) { + if (sep->arg[2] && Seperator::IsNumber(sep->arg[2])) { uint8 update_type = WWSpellUpdateType_Cast; - int spell_id = atoi(sep->arg[2]); + auto spell_id = std::stoul(sep->arg[2]); + bool disable_message = false; + if (sep->arg[3] && Seperator::IsNumber(sep->arg[3])) { + disable_message = std::stoi(sep->arg[3]) ? true : false; + } + + c->Message( + Chat::White, + fmt::format( + "World Wide Cast Spell | Spell: {} ({})", + GetSpellName(spell_id), + spell_id + ).c_str() + ); + quest_manager.WorldWideSpell(update_type, spell_id); - worldserver.SendEmoteMessage(0, 0, 15, fmt::format(" A GM has cast [{}] world-wide!", GetSpellName(spell_id)).c_str()); + if (!disable_message) { + quest_manager.WorldWideMessage( + Chat::Yellow, + fmt::format( + "[SYSTEM] A GM has cast [{}] world-wide!", + GetSpellName(spell_id) + ).c_str() + ); + } + } else { + c->Message(Chat::White, "Usage: #worldwide cast [Spell ID] [Disable Message]"); } - else { - c->Message(Chat::Yellow, "Usage: #worldwide cast [spellid]"); + } else if (sub_command == "remove") { + if (sep->arg[2] && Seperator::IsNumber(sep->arg[2])) { + uint8 update_type = WWSpellUpdateType_Remove; + auto spell_id = std::stoul(sep->arg[2]); + + c->Message( + Chat::White, + fmt::format( + "World Wide Remove Spell | Spell: {} ({})", + GetSpellName(spell_id), + spell_id + ).c_str() + ); + + quest_manager.WorldWideSpell(update_type, spell_id); + } else { + c->Message(Chat::White, "Usage: #worldwide remove [Spell ID]"); } + } else if (sub_command == "message") { + if (sep->arg[2]) { + std::string message = sep->arg[2]; + quest_manager.WorldWideMessage( + Chat::White, + fmt::format( + "{}", + message + ).c_str() + ); + } else { + c->Message(Chat::White, "Usage: #worldwide message [Message]"); + } + } else if (sub_command == "move") { + if (sep->arg[2]) { + uint8 update_type = WWMoveUpdateType_MoveZone; + uint32 zone_id = 0; + std::string zone_short_name; + if (Seperator::IsNumber(sep->arg[2])) { + zone_id = std::stoul(sep->arg[2]); + } + + if (zone_id) { + zone_short_name = ZoneName(zone_id); + } else { + zone_short_name = sep->arg[2]; + } + + c->Message( + Chat::White, + fmt::format( + "World Wide Zone | Zone: {} ({}) ID: {}", + ZoneLongName( + ZoneID(zone_short_name) + ), + zone_short_name, + ZoneID(zone_short_name) + ).c_str() + ); + + quest_manager.WorldWideMove(update_type, zone_short_name.c_str()); + } else { + c->Message( + Chat::White, + "Usage: #worldwide move [Zone ID] or #worldwide move [Zone Short Name]" + ); + } + } else if (sub_command == "moveinstance") { + if (Seperator::IsNumber(sep->arg[2])) { + uint8 update_type = WWMoveUpdateType_MoveZoneInstance; + const char* zone_short_name = ""; + uint16 instance_id = std::stoi(sep->arg[2]); + + c->Message( + Chat::White, + fmt::format( + "World Wide Zone Instance | Instance ID: {}", + instance_id + ).c_str() + ); + + quest_manager.WorldWideMove(update_type, zone_short_name, instance_id); + } else { + c->Message(Chat::White, "Usage: #worldwide moveinstance [Instance ID]"); + } } if (!sep->arg[1]) { - c->Message(Chat::White, "This command is used to perform world-wide tasks"); - c->Message(Chat::White, "Usage: #worldwide cast [spellid]"); + c->Message(Chat::White, "This command is used to perform world-wide tasks."); + c->Message(Chat::White, "Usage: #worldwide cast [Spell ID] [Disable Message]"); + c->Message(Chat::White, "Usage: #worldwide remove [Spell ID]"); + c->Message(Chat::White, "Usage: #worldwide message [Message]"); + c->Message( + Chat::White, + "Usage: #worldwide move [Zone ID] or #worldwide move [Zone Short Name]" + ); + c->Message(Chat::White, "Usage: #worldwide moveinstance [Instance ID]"); } } void command_endurance(Client *c, const Seperator *sep) From bf92845a4a35462973f9011130c89a7bd49be4ea Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 7 Nov 2021 17:21:34 -0500 Subject: [PATCH 338/624] [Rules] Add Resurrection Sickness rules for Characters/Bots. (#1692) * [Rules] Add Resurrection Sickness rule for Characters/Bots. - Add RULE_BOOL(Character, UseResurrectionSickness, true, "Use Resurrection Sickness based on Resurrection spell cast, set to false to disable Resurrection Sickness.") - Add RULE_BOOL(Bots, UseOldRaceRezEffects, false, "Older clients had ID 757 for races with high starting STR, but it doesn't seem used anymore") - Add RULE_BOOL(Bots, UseResurrectionSickness, true, "Use Resurrection Sickness based on Resurrection spell cast, set to false to disable Resurrection Sickness.") * Add rules for spell IDs. * Fix bot health on spawn when resurrection sickness is disabled. - Formatting. * Remove 'this' keyword. --- common/ruletypes.h | 7 +++++++ zone/bot.cpp | 24 ++++++++++++++++++++---- zone/client_process.cpp | 25 ++++++++++++++++--------- 3 files changed, 43 insertions(+), 13 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index ae0f0a272..5be339c17 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -168,6 +168,9 @@ RULE_BOOL(Character, EnableCharacterEXPMods, false, "Enables character zone-base RULE_BOOL(Character, PVPEnableGuardFactionAssist, true, "Enables faction based assisting against the aggresor in pvp.") RULE_BOOL(Character, SkillUpFromItems, true, "Allow Skill ups from clickable items") RULE_BOOL(Character, EnableTestBuff, false, "Allow the use of /testbuff") +RULE_BOOL(Character, UseResurrectionSickness, true, "Use Resurrection Sickness based on Resurrection spell cast, set to false to disable Resurrection Sickness.") +RULE_INT(Character, OldResurrectionSicknessSpellID, 757, "757 is Default Old Resurrection Sickness Spell ID") +RULE_INT(Character, ResurrectionSicknessSpellID, 756, "756 is Default Resurrection Sickness Spell ID") RULE_CATEGORY_END() RULE_CATEGORY(Mercs) @@ -586,6 +589,10 @@ RULE_REAL(Bots, LeashDistance, 562500.0f, "Distance a bot is allowed to travel f RULE_BOOL(Bots, AllowApplyPoisonCommand, true, "Allows the use of the bot command 'applypoison'") RULE_BOOL(Bots, AllowApplyPotionCommand, true, "Allows the use of the bot command 'applypotion'") RULE_BOOL(Bots, RestrictApplyPotionToRogue, true, "Restricts the bot command 'applypotion' to rogue-usable potions (i.e., poisons)") +RULE_BOOL(Bots, OldRaceRezEffects, false, "Older clients had ID 757 for races with high starting STR, but it doesn't seem used anymore") +RULE_BOOL(Bots, ResurrectionSickness, true, "Use Resurrection Sickness based on Resurrection spell cast, set to false to disable Resurrection Sickness.") +RULE_INT(Bots, OldResurrectionSicknessSpell, 757, "757 is Default Old Resurrection Sickness Spell") +RULE_INT(Bots, ResurrectionSicknessSpell, 756, "756 is Default Resurrection Sickness Spell") RULE_CATEGORY_END() #endif diff --git a/zone/bot.cpp b/zone/bot.cpp index e86bf579a..8765fbb75 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -398,10 +398,26 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to current_hp = max_hp; if(current_hp <= 0) { - SetHP(max_hp/5); - SetMana(0); - BuffFadeAll(); - SpellOnTarget(756, this); // Rezz effects + BuffFadeNonPersistDeath(); + if (RuleB(Bots, ResurrectionSickness)) { + int resurrection_sickness_spell_id = ( + RuleB(Bots, OldRaceRezEffects) && + ( + GetRace() == BARBARIAN || + GetRace() == DWARF || + GetRace() == TROLL || + GetRace() == OGRE + ) ? + RuleI(Bots, OldResurrectionSicknessSpell) : + RuleI(Bots, ResurrectionSicknessSpell) + ); + SetHP(max_hp / 5); + SetMana(0); + SpellOnTarget(resurrection_sickness_spell_id, this); // Rezz effects + } else { + SetHP(GetMaxHP()); + SetMana(GetMaxMana()); + } } if(current_mana > max_mana) diff --git a/zone/client_process.cpp b/zone/client_process.cpp index d82bfa10a..97dfdb012 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -981,21 +981,28 @@ void Client::OPRezzAnswer(uint32 Action, uint32 SpellID, uint16 ZoneID, uint16 I this->name, (uint16)spells[SpellID].base_value[0], SpellID, ZoneID, InstanceID); - this->BuffFadeNonPersistDeath(); + BuffFadeNonPersistDeath(); int SpellEffectDescNum = GetSpellEffectDescNum(SpellID); // Rez spells with Rez effects have this DescNum (first is Titanium, second is 6.2 Client) - if((SpellEffectDescNum == 82) || (SpellEffectDescNum == 39067)) { + if(RuleB(Character, UseResurrectionSickness) && SpellEffectDescNum == 82 || SpellEffectDescNum == 39067) { + SetHP(GetMaxHP() / 5); SetMana(0); - SetHP(GetMaxHP()/5); - int rez_eff = 756; - if (RuleB(Character, UseOldRaceRezEffects) && - (GetRace() == BARBARIAN || GetRace() == DWARF || GetRace() == TROLL || GetRace() == OGRE)) - rez_eff = 757; - SpellOnTarget(rez_eff, this); // Rezz effects + int resurrection_sickness_spell_id = ( + RuleB(Character, UseOldRaceRezEffects) && + ( + GetRace() == BARBARIAN || + GetRace() == DWARF || + GetRace() == TROLL || + GetRace() == OGRE + ) ? + RuleI(Character, OldResurrectionSicknessSpellID) : + RuleI(Character, ResurrectionSicknessSpellID) + ); + SpellOnTarget(resurrection_sickness_spell_id, this); // Rezz effects } else { - SetMana(GetMaxMana()); SetHP(GetMaxHP()); + SetMana(GetMaxMana()); } if(spells[SpellID].base_value[0] < 100 && spells[SpellID].base_value[0] > 0 && PendingRezzXP > 0) { From 90871cb3d9dad02b1490970e2f21dab8b6bf80ad Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 7 Nov 2021 17:21:42 -0500 Subject: [PATCH 339/624] [Commands] Cleanup #worldshutdown Command. (#1694) - Cleanup system messages and magic numbers. --- world/console.cpp | 10 +++++-- world/console.old.cpp | 16 +++++++++-- world/zonelist.cpp | 24 ++++++++++++++--- zone/command.cpp | 63 ++++++++++++++++++++++++++++--------------- 4 files changed, 84 insertions(+), 29 deletions(-) diff --git a/world/console.cpp b/world/console.cpp index e25be5050..f2cce0000 100644 --- a/world/console.cpp +++ b/world/console.cpp @@ -778,8 +778,14 @@ void ConsoleWorldShutdown( zoneserver_list.WorldShutDown(0, 0); } else if (strcasecmp(args[0].c_str(), "disable") == 0) { - connection->SendLine(":SYSTEM MSG:World shutdown aborted."); - zoneserver_list.SendEmoteMessage(0, 0, 0, 15, ":SYSTEM MSG:World shutdown aborted."); + connection->SendLine("[SYSTEM] World shutdown has been aborted."); + zoneserver_list.SendEmoteMessage( + 0, + 0, + 0, + Chat::Yellow, + "[SYSTEM] World shutdown has been aborted." + ); zoneserver_list.shutdowntimer->Disable(); zoneserver_list.reminder->Disable(); } diff --git a/world/console.old.cpp b/world/console.old.cpp index d7589d6eb..5917b506f 100644 --- a/world/console.old.cpp +++ b/world/console.old.cpp @@ -750,8 +750,20 @@ void Console::ProcessCommand(const char* command) { zoneserver_list.WorldShutDown(0, 0); } else if(strcasecmp(sep.arg[1], "disable") == 0) { - SendEmoteMessage(0,0,0,15,":SYSTEM MSG:World shutdown aborted."); - zoneserver_list.SendEmoteMessage(0,0,0,15,":SYSTEM MSG:World shutdown aborted."); + SendEmoteMessage( + 0, + 0, + 0, + Chat::Yellow, + "[SYSTEM] World shutdown has been aborted." + ); + zoneserver_list.SendEmoteMessage( + 0, + 0, + 0, + Chat::Yellow, + "[SYSTEM] World shutdown has been aborted." + ); zoneserver_list.shutdowntimer->Disable(); zoneserver_list.reminder->Disable(); } diff --git a/world/zonelist.cpp b/world/zonelist.cpp index 9e1239a6c..400c07854 100644 --- a/world/zonelist.cpp +++ b/world/zonelist.cpp @@ -110,7 +110,16 @@ void ZSList::Process() { } if (reminder && reminder->Check() && shutdowntimer) { - SendEmoteMessage(0, 0, 0, 15, ":SYSTEM MSG:World coming down, everyone log out now. World will shut down in %i minutes...", ((shutdowntimer->GetRemainingTime() / 1000) / 60)); + SendEmoteMessage( + 0, + 0, + 0, + Chat::Yellow, + fmt::format( + "[SYSTEM] World will be shutting down in {} minutes.", + ((shutdowntimer->GetRemainingTime() / 1000) / 60) + ).c_str() + ); } } @@ -677,7 +686,16 @@ void ZSList::UpdateUCSServerAvailable(bool ucss_available) { void ZSList::WorldShutDown(uint32 time, uint32 interval) { if (time > 0) { - SendEmoteMessage(0, 0, 0, 15, ":SYSTEM MSG:World coming down in %i minutes, everyone log out before this time.", (time / 60)); + SendEmoteMessage( + 0, + 0, + 0, + Chat::Yellow, + fmt::format( + "[SYSTEM] World will be shutting down in {} minutes.", + (time / 60) + ).c_str() + ); time *= 1000; interval *= 1000; @@ -690,7 +708,7 @@ void ZSList::WorldShutDown(uint32 time, uint32 interval) reminder->Start(); } else { - SendEmoteMessage(0, 0, 0, 15, ":SYSTEM MSG:World coming down, everyone log out now."); + SendEmoteMessage(0, 0, 0, 15, "[SYSTEM] World is shutting down."); auto pack = new ServerPacket; pack->opcode = ServerOP_ShutdownAll; pack->size = 0; diff --git a/zone/command.cpp b/zone/command.cpp index 272f1d4e6..400aee524 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -3197,46 +3197,65 @@ void command_ai(Client *c, const Seperator *sep) void command_worldshutdown(Client *c, const Seperator *sep) { // GM command to shutdown world server and all zone servers - uint32 time=0; - uint32 interval=0; + uint32 time = 0; + uint32 interval = 0; if (worldserver.Connected()) { - if(sep->IsNumber(1) && sep->IsNumber(2) && ((time=atoi(sep->arg[1]))>0) && ((interval=atoi(sep->arg[2]))>0)) { - worldserver.SendEmoteMessage(0,0,15,":SYSTEM MSG:World coming down in %i minutes, everyone log out before this time.", (time / 60 )); - c->Message(Chat::White, "Sending shutdown packet now, World will shutdown in: %i minutes with an interval of: %i seconds", (time / 60), interval); + if ( + sep->IsNumber(1) && + sep->IsNumber(2) && + (time = std::stoi(sep->arg[1]) > 0) && + (interval = std::stoi(sep->arg[2]) > 0) + ) { + int time_minutes = (time / 60); + quest_manager.WorldWideMessage( + Chat::Yellow, + fmt::format( + "[SYSTEM] World will be shutting down in {} minutes.", + time_minutes + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "World will be shutting down in {} minutes, notifying every {} seconds", + time_minutes, + interval + ).c_str() + ); auto pack = new ServerPacket(ServerOP_ShutdownAll, sizeof(WorldShutDown_Struct)); WorldShutDown_Struct* wsd = (WorldShutDown_Struct*)pack->pBuffer; - wsd->time=time*1000; - wsd->interval=(interval*1000); + wsd->time = (time * 1000); + wsd->interval = (interval * 1000); worldserver.SendPacket(pack); safe_delete(pack); - } - else if(strcasecmp(sep->arg[1], "now") == 0){ - worldserver.SendEmoteMessage(0,0,15,":SYSTEM MSG:World coming down, everyone log out now."); - c->Message(Chat::White, "Sending shutdown packet"); + } else if (!strcasecmp(sep->arg[1], "now")){ + quest_manager.WorldWideMessage( + Chat::Yellow, + "[SYSTEM] World is shutting down now." + ); + c->Message(Chat::White, "World is shutting down now."); auto pack = new ServerPacket; pack->opcode = ServerOP_ShutdownAll; - pack->size=0; + pack->size = 0; worldserver.SendPacket(pack); safe_delete(pack); - } - else if(strcasecmp(sep->arg[1], "disable") == 0){ - c->Message(Chat::White, "Shutdown prevented, next time I may not be so forgiving..."); + } else if (!strcasecmp(sep->arg[1], "disable")) { + c->Message(Chat::White, "World shutdown has been aborted."); auto pack = new ServerPacket(ServerOP_ShutdownAll, sizeof(WorldShutDown_Struct)); WorldShutDown_Struct* wsd = (WorldShutDown_Struct*)pack->pBuffer; - wsd->time=0; - wsd->interval=0; + wsd->time = 0; + wsd->interval = 0; worldserver.SendPacket(pack); safe_delete(pack); - } - else{ + } else { c->Message(Chat::White,"#worldshutdown - Shuts down the server and all zones."); c->Message(Chat::White,"Usage: #worldshutdown now - Shuts down the server and all zones immediately."); c->Message(Chat::White,"Usage: #worldshutdown disable - Stops the server from a previously scheduled shut down."); - c->Message(Chat::White,"Usage: #worldshutdown [timer] [interval] - Shuts down the server and all zones after [timer] seconds and sends warning every [interval] seconds."); + c->Message(Chat::White,"Usage: #worldshutdown [timer] [interval] - Shuts down the server and all zones after [timer] seconds and notifies players every [interval] seconds."); } + } else { + c->Message(Chat::White, "Error: World server is disconnected."); } - else - c->Message(Chat::White, "Error: World server disconnected"); } void command_sendzonespawns(Client *c, const Seperator *sep) From 0b283e60db9decfd93eb19cf62036b29eca0ff52 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 7 Nov 2021 18:32:33 -0500 Subject: [PATCH 340/624] [Commands] Remove #listnpcs Command. (#1696) - Unused command. --- zone/command.cpp | 6 ------ zone/command.h | 1 - 2 files changed, 7 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 400aee524..451507f9e 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -278,7 +278,6 @@ int command_init(void) command_add("killallnpcs", " [npc_name] Kills all npcs by search name, leave blank for all attackable NPC's", 200, command_killallnpcs) || command_add("lastname", "[new lastname] - Set your or your player target's lastname", 50, command_lastname) || command_add("level", "[level] - Set your or your target's level", 10, command_level) || - command_add("listnpcs", "[name/range] - Search NPCs", 20, command_listnpcs) || command_add("list", "[npcs|players|corpses|doors|objects] [search] - Search entities", 20, command_list) || command_add("listpetition", "- List petitions", 50, command_listpetition) || command_add("load_shared_memory", "[shared_memory_name] - Reloads shared memory and uses the input as output", 250, command_load_shared_memory) || @@ -1665,11 +1664,6 @@ void command_delpetition(Client *c, const Seperator *sep) } -void command_listnpcs(Client *c, const Seperator *sep) -{ - c->Message(Chat::White, "Deprecated, use the #list command (#list npcs )"); -} - void command_list(Client *c, const Seperator *sep) { std::string search_type; diff --git a/zone/command.h b/zone/command.h index c2dee0251..bebb82340 100644 --- a/zone/command.h +++ b/zone/command.h @@ -168,7 +168,6 @@ void command_killallnpcs(Client *c, const Seperator *sep); void command_kill(Client *c, const Seperator *sep); void command_lastname(Client *c, const Seperator *sep); void command_level(Client *c, const Seperator *sep); -void command_listnpcs(Client *c, const Seperator *sep); void command_list(Client *c, const Seperator *sep); void command_listpetition(Client *c, const Seperator *sep); void command_load_shared_memory(Client *c, const Seperator *sep); From 211196a72255f77be6b595522894cf52f78e92eb Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 9 Nov 2021 10:54:54 -0500 Subject: [PATCH 341/624] Fix Channel TellEcho issues (#1676) These were missed switching them to TellEcho from a previous change --- world/zoneserver.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 191b42396..7921c4466 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -453,6 +453,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { break; scm->noreply = true; scm->queued = 3; // offline + scm->chan_num = ChatChannel_TellEcho; strcpy(scm->deliverto, scm->from); // ideally this would be trimming off the message too, oh well sender->Server()->SendPacket(pack); @@ -466,6 +467,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { break; scm->noreply = true; scm->queued = 2; // queue full + scm->chan_num = ChatChannel_TellEcho; strcpy(scm->deliverto, scm->from); sender->Server()->SendPacket(pack); } @@ -481,6 +483,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { break; scm->noreply = true; scm->queued = 1; // queued + scm->chan_num = ChatChannel_TellEcho; strcpy(scm->deliverto, scm->from); sender->Server()->SendPacket(pack); } From 328a94e2d4e7d355c486ec5e59821cd18973591c Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 9 Nov 2021 21:23:38 -0500 Subject: [PATCH 342/624] [Commands] Add #findfaction Command. (#1697) - Add #findfaction [search criteria] command. - Cleanup other #find command messages/logic. - Add GetMaxFaction() helper method. - Add races.h defines for races 725-732. --- common/races.h | 8 ++ zone/command.cpp | 259 +++++++++++++++++++++++++++++++++++++---------- zone/command.h | 1 + zone/zonedb.h | 1 + 4 files changed, 216 insertions(+), 53 deletions(-) diff --git a/common/races.h b/common/races.h index 73c6ba634..68e6fa061 100644 --- a/common/races.h +++ b/common/races.h @@ -1603,6 +1603,14 @@ namespace PlayerAppearance #define RACE_FALLEN_KNIGHT_722 722 #define RACE_SERVANT_OF_SHADOW_723 723 #define RACE_LUCLIN_724 724 +#define RACE_XARIC_725 725 +#define RACE_DERVISH_726 726 +#define RACE_DERVISH_727 727 +#define RACE_LUCLIN_728 728 +#define RACE_LUCLIN_729 729 +#define RACE_ORB_730 730 +#define RACE_LUCLIN_731 731 +#define RACE_PEGASUS_732 732 #define RACE_INTERACTIVE_OBJECT_2250 2250 #endif diff --git a/zone/command.cpp b/zone/command.cpp index 451507f9e..96f65d363 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -220,6 +220,7 @@ int command_init(void) command_add("faction", "[Find (criteria | all ) | Review (criteria | all) | Reset (id)] - Resets Player's Faction", 80, command_faction) || command_add("findaliases", "[search criteria]- Searches for available command aliases, by alias or command", 0, command_findaliases) || command_add("findclass", "[search criteria] - Search for a class", 50, command_findclass) || + command_add("findfaction", "[search criteria] - Search for a faction", 50, command_findfaction) || command_add("findnpctype", "[search criteria] - Search database NPC types", 100, command_findnpctype) || command_add("findrace", "[search criteria] - Search for a race", 50, command_findrace) || command_add("findskill", "[search criteria] - Search for a skill", 50, command_findskill) || @@ -3639,21 +3640,33 @@ void command_showskills(Client *c, const Seperator *sep) void command_findclass(Client *c, const Seperator *sep) { - if (sep->arg[1][0] == 0) { - c->Message(Chat::White, "Usage: #findclass [search criteria]"); - } else if (Seperator::IsNumber(sep->argplus[1])) { - int search_id = atoi(sep->argplus[1]); - std::string class_name = GetClassIDName(search_id); - if (class_name.length() > 0) { + int arguments = sep->argnum; + + if (arguments == 0) { + c->Message(Chat::White, "Command Syntax: #findclass [search criteria]"); + return; + } + + if (sep->IsNumber(1)) { + int class_id = std::stoi(sep->arg[1]); + if (class_id >= WARRIOR && class_id <= MERCERNARY_MASTER) { + std::string class_name = GetClassIDName(class_id); c->Message( Chat::White, fmt::format( "Class {}: {}", - search_id, + class_id, class_name ).c_str() ); - return; + } else { + c->Message( + Chat::White, + fmt::format( + "Class ID {} was not found.", + class_id + ).c_str() + ); } } else { std::string search_criteria = str_tolower(sep->argplus[1]); @@ -3683,11 +3696,105 @@ void command_findclass(Client *c, const Seperator *sep) if (found_count == 20) { c->Message(Chat::White, "20 Classes found... max reached."); } else { + auto class_message = ( + found_count > 0 ? + ( + found_count == 1 ? + "A Class was" : + fmt::format("{} Classes were", found_count) + ) : + "No Classes were" + ); + c->Message( Chat::White, fmt::format( - "{} Class(es) found.", - found_count + "{} found.", + class_message + ).c_str() + ); + } + } +} + +void command_findfaction(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + + if (arguments == 0) { + c->Message(Chat::White, "Command Syntax: #findfaction [search criteria]"); + return; + } + + if (sep->IsNumber(1)) { + int faction_id = std::stoi(sep->arg[1]); + auto faction_name = content_db.GetFactionName(faction_id); + if (!faction_name.empty()) { + c->Message( + Chat::White, + fmt::format( + "Faction {}: {}", + faction_id, + faction_name + ).c_str() + ); + } else { + c->Message( + Chat::White, + fmt::format( + "Faction ID {} was not found.", + faction_id + ).c_str() + ); + } + } else { + std::string search_criteria = str_tolower(sep->argplus[1]); + int found_count = 0; + int max_faction_id = content_db.GetMaxFaction(); + for (int faction_id = 0; faction_id < max_faction_id; faction_id++) { + std::string faction_name = content_db.GetFactionName(faction_id); + std::string faction_name_lower = str_tolower(faction_name); + if (faction_name.empty()) { + continue; + } + + if (faction_name.find(search_criteria) == std::string::npos) { + continue; + } + + c->Message( + Chat::White, + fmt::format( + "Faction {}: {}", + faction_id, + faction_name + ).c_str() + ); + found_count++; + + if (found_count == 20) { + break; + } + } + + if (found_count == 20) { + c->Message(Chat::White, "20 Factions found... max reached."); + } else { + auto faction_message = ( + found_count > 0 ? + ( + found_count == 1 ? + "A Faction was" : + fmt::format("{} Factions were", found_count) + ) : + "No Factions were" + ); + + c->Message( + Chat::White, + fmt::format( + "{} found.", + faction_message ).c_str() ); } @@ -3696,26 +3803,38 @@ void command_findclass(Client *c, const Seperator *sep) void command_findrace(Client *c, const Seperator *sep) { - if (sep->arg[1][0] == 0) { - c->Message(Chat::White, "Usage: #findrace [search criteria]"); - } else if (Seperator::IsNumber(sep->argplus[1])) { - int search_id = atoi(sep->argplus[1]); - std::string race_name = GetRaceIDName(search_id); - if (race_name.length() > 0) { + int arguments = sep->argnum; + + if (arguments == 0) { + c->Message(Chat::White, "Command Syntax: #findrace [search criteria]"); + return; + } + + if (sep->IsNumber(1)) { + int race_id = std::stoi(sep->arg[1]); + std::string race_name = GetRaceIDName(race_id); + if (race_id >= RACE_HUMAN_1 && race_id <= RACE_PEGASUS_732) { c->Message( Chat::White, fmt::format( "Race {}: {}", - search_id, + race_id, race_name ).c_str() ); - return; + } else { + c->Message( + Chat::White, + fmt::format( + "Race ID {} was not found.", + race_id + ).c_str() + ); } } else { std::string search_criteria = str_tolower(sep->argplus[1]); int found_count = 0; - for (int race_id = RACE_HUMAN_1; race_id <= RT_PEGASUS_3; race_id++) { + for (int race_id = RACE_HUMAN_1; race_id <= RACE_PEGASUS_732; race_id++) { std::string race_name = GetRaceIDName(race_id); std::string race_name_lower = str_tolower(race_name); if (search_criteria.length() > 0 && race_name_lower.find(search_criteria) == std::string::npos) { @@ -3736,14 +3855,25 @@ void command_findrace(Client *c, const Seperator *sep) break; } } + if (found_count == 20) { c->Message(Chat::White, "20 Races found... max reached."); } else { + auto race_message = ( + found_count > 0 ? + ( + found_count == 1 ? + "A Race was" : + fmt::format("{} Races were", found_count) + ) : + "No Races were" + ); + c->Message( Chat::White, fmt::format( - "{} Race(s) found.", - found_count + "{} found.", + race_message ).c_str() ); } @@ -3761,16 +3891,16 @@ void command_findskill(Client *c, const Seperator *sep) std::map skills = EQ::skills::GetSkillTypeMap(); if (sep->IsNumber(1)) { - int skill_id = atoi(sep->argplus[1]); + int skill_id = std::stoi(sep->arg[1]); if (skill_id >= EQ::skills::Skill1HBlunt && skill_id < EQ::skills::SkillCount) { - for (auto skills_iter : skills) { - if (skill_id == skills_iter.first) { + for (auto skill : skills) { + if (skill_id == skill.first) { c->Message( Chat::White, fmt::format( - "{}: {}", - skills_iter.first, - skills_iter.second + "Skill {}: {}", + skill.first, + skill.second ).c_str() ); break; @@ -3789,8 +3919,8 @@ void command_findskill(Client *c, const Seperator *sep) std::string search_criteria = str_tolower(sep->argplus[1]); if (!search_criteria.empty()) { int found_count = 0; - for (auto skills_iter : skills) { - std::string skill_name_lower = str_tolower(skills_iter.second); + for (auto skill : skills) { + std::string skill_name_lower = str_tolower(skill.second); if (skill_name_lower.find(search_criteria) == std::string::npos) { continue; } @@ -3798,9 +3928,9 @@ void command_findskill(Client *c, const Seperator *sep) c->Message( Chat::White, fmt::format( - "{}: {}", - skills_iter.first, - skills_iter.second + "Skill {}: {}", + skill.first, + skill.second ).c_str() ); found_count++; @@ -3817,10 +3947,10 @@ void command_findskill(Client *c, const Seperator *sep) found_count > 0 ? ( found_count == 1 ? - "A skill was" : - fmt::format("{} skills were", found_count) + "A Skill was" : + fmt::format("{} Skills were", found_count) ) : - "No skills were" + "No Skills were" ); c->Message( @@ -3837,30 +3967,43 @@ void command_findskill(Client *c, const Seperator *sep) void command_findspell(Client *c, const Seperator *sep) { - if (sep->arg[1][0] == 0) { - c->Message(Chat::White, "Usage: #findspell [search criteria]"); - } else if (SPDAT_RECORDS <= 0) { + if (SPDAT_RECORDS <= 0) { c->Message(Chat::White, "Spells not loaded"); - } else if (Seperator::IsNumber(sep->argplus[1])) { - int spell_id = atoi(sep->argplus[1]); + return; + } + + int arguments = sep->argnum; + + if (arguments == 0) { + c->Message(Chat::White, "Command Syntax: #findspell [search criteria]"); + return; + } + + if (sep->IsNumber(1)) { + int spell_id = std::stoi(sep->arg[1]); if (!IsValidSpell(spell_id)) { - c->Message(Chat::White, "Error: Invalid Spell"); + c->Message( + Chat::White, + fmt::format( + "Spell ID {} was not found.", + spell_id + ).c_str() + ); } else { c->Message( Chat::White, fmt::format( - "{}: {}", + "Spell {}: {}", spell_id, spells[spell_id].name ).c_str() ); } - } - else { + } else { std::string search_criteria = str_tolower(sep->argplus[1]); int found_count = 0; - for (int i = 0; i < SPDAT_RECORDS; i++) { - auto current_spell = spells[i]; + for (int spell_id = 0; spell_id < SPDAT_RECORDS; spell_id++) { + auto current_spell = spells[spell_id]; if (current_spell.name[0] != 0) { std::string spell_name = current_spell.name; std::string spell_name_lower = str_tolower(spell_name); @@ -3871,8 +4014,8 @@ void command_findspell(Client *c, const Seperator *sep) c->Message( Chat::White, fmt::format( - "{}: {}", - i, + "Spell {}: {}", + spell_id, spell_name ).c_str() ); @@ -3887,11 +4030,21 @@ void command_findspell(Client *c, const Seperator *sep) if (found_count == 20) { c->Message(Chat::White, "20 Spells found... max reached."); } else { + auto spell_message = ( + found_count > 0 ? + ( + found_count == 1 ? + "A Spell was" : + fmt::format("{} Spells were", found_count) + ) : + "No Spells were" + ); + c->Message( Chat::White, fmt::format( - "{} Spell(s) found.", - found_count + "{} found.", + spell_message ).c_str() ); } @@ -15842,12 +15995,12 @@ void command_findtask(Client *c, const Seperator *sep) } if (sep->IsNumber(1)) { - auto task_id = atoul(sep->argplus[1]); + auto task_id = std::stoul(sep->arg[1]); auto task_name = task_manager->GetTaskName(task_id); auto task_message = ( !task_name.empty() ? fmt::format( - "{}: {}", + "Task {}: {}", task_id, task_name ).c_str() : @@ -15875,7 +16028,7 @@ void command_findtask(Client *c, const Seperator *sep) c->Message( Chat::White, fmt::format( - "{}: {}", + "Task {}: {}", task_id, task_name ).c_str() diff --git a/zone/command.h b/zone/command.h index bebb82340..d6464c8d1 100644 --- a/zone/command.h +++ b/zone/command.h @@ -108,6 +108,7 @@ void command_face(Client *c, const Seperator *sep); void command_faction(Client *c, const Seperator *sep); void command_findaliases(Client *c, const Seperator *sep); void command_findclass(Client *c, const Seperator *sep); +void command_findfaction(Client *c, const Seperator *sep); void command_findnpctype(Client *c, const Seperator *sep); void command_findrace(Client *c, const Seperator *sep); void command_findskill(Client *c, const Seperator *sep); diff --git a/zone/zonedb.h b/zone/zonedb.h index 054317256..074f983b6 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -407,6 +407,7 @@ public: bool GetFactionIdsForNPC(uint32 nfl_id, std::list *faction_list, int32* primary_faction = 0); // improve faction handling bool SetCharacterFactionLevel(uint32 char_id, int32 faction_id, int32 value, uint8 temp, faction_map &val_list); // needed for factions Dec, 16 2001 bool LoadFactionData(); + inline uint32 GetMaxFaction() { return max_faction; } /* AAs New */ bool LoadAlternateAdvancementAbilities(std::unordered_map> &abilities, From 248e6d44dba1ebf036a2a643b9e7f699885253ce Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 9 Nov 2021 21:23:48 -0500 Subject: [PATCH 343/624] [Commands] Cleanup #npccast Command. (#1699) - Cleanup messages and display. --- zone/command.cpp | 96 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 81 insertions(+), 15 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 96f65d363..cd2e69082 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -2662,22 +2662,88 @@ void command_zclip(Client *c, const Seperator *sep) void command_npccast(Client *c, const Seperator *sep) { - if (c->GetTarget() && c->GetTarget()->IsNPC() && !sep->IsNumber(1) && sep->arg[1] != 0 && sep->IsNumber(2)) { - Mob* spelltar = entity_list.GetMob(sep->arg[1]); - if (spelltar) - c->GetTarget()->CastSpell(atoi(sep->arg[2]), spelltar->GetID()); - else - c->Message(Chat::White, "Error: %s not found", sep->arg[1]); + if (c->GetTarget() && c->GetTarget()->IsNPC()) { + NPC* target = c->GetTarget()->CastToNPC(); + if (!sep->IsNumber(1) && sep->arg[1] && sep->IsNumber(2)) { + const char* entity_name = sep->arg[1] ? sep->arg[1] : 0; + auto spell_id = sep->arg[2] ? std::stoul(sep->arg[2]) : 0; + Mob* spell_target = entity_list.GetMob(entity_name); + if (spell_target && IsValidSpell(spell_id) && spell_id < SPDAT_RECORDS) { + c->Message( + Chat::White, + fmt::format( + "{} ({}) casting {} ({}) on {} ({}).", + target->GetCleanName(), + target->GetID(), + GetSpellName(static_cast(spell_id)), + spell_id, + spell_target->GetCleanName(), + spell_target->GetID() + ).c_str() + ); + + target->CastSpell(spell_id, spell_target->GetID()); + } else { + if (!spell_target) { + c->Message( + Chat::White, + fmt::format( + "Entity {} was not found", + entity_name + ).c_str() + ); + } else if (!spell_id || !IsValidSpell(spell_id)) { + c->Message( + Chat::White, + fmt::format( + "Spell ID {} was not found", + spell_id + ).c_str() + ); + } + } + } else if (sep->IsNumber(1) && sep->IsNumber(2) ) { + uint16 entity_id = sep->arg[1] ? std::stoul(sep->arg[1]) : 0; + auto spell_id = sep->arg[2] ? std::stoul(sep->arg[2]) : 0; + Mob* spell_target = entity_list.GetMob(entity_id); + if (spell_target && IsValidSpell(spell_id) && spell_id < SPDAT_RECORDS) { + c->Message( + Chat::White, + fmt::format( + "{} ({}) casting {} ({}) on {} ({}).", + target->GetCleanName(), + target->GetID(), + GetSpellName(static_cast(spell_id)), + spell_id, + spell_target->GetCleanName(), + spell_target->GetID() + ).c_str() + ); + + target->CastSpell(spell_id, spell_target->GetID()); + } else { + if (!spell_target) { + c->Message( + Chat::White, + fmt::format( + "Entity ID {} was not found", + entity_id + ).c_str() + ); + } else if (!spell_id || !IsValidSpell(spell_id)) { + c->Message( + Chat::White, + fmt::format( + "Spell ID {} was not found", + spell_id + ).c_str() + ); + } + } + } + } else { + c->Message(Chat::White, "You must target an NPC to use this command."); } - else if (c->GetTarget() && c->GetTarget()->IsNPC() && sep->IsNumber(1) && sep->IsNumber(2) ) { - Mob* spelltar = entity_list.GetMob(atoi(sep->arg[1])); - if (spelltar) - c->GetTarget()->CastSpell(atoi(sep->arg[2]), spelltar->GetID()); - else - c->Message(Chat::White, "Error: target ID %i not found", atoi(sep->arg[1])); - } - else - c->Message(Chat::White, "Usage: (needs NPC targeted) #npccast targetname/entityid spellid"); } void command_zstats(Client *c, const Seperator *sep) From 605b3d3a27bf28051b03824af7d9810d95130de5 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 9 Nov 2021 21:24:17 -0500 Subject: [PATCH 344/624] [Commands] Cleanup #fov Command. (#1701) - Cleanup message. --- zone/command.cpp | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index cd2e69082..a92b23ae0 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -2027,13 +2027,41 @@ void command_emote(Client *c, const Seperator *sep) void command_fov(Client *c, const Seperator *sep) { - if(c->GetTarget()) - if(c->BehindMob(c->GetTarget(), c->GetX(), c->GetY())) - c->Message(Chat::White, "You are behind mob %s, it is looking to %d", c->GetTarget()->GetName(), c->GetTarget()->GetHeading()); - else - c->Message(Chat::White, "You are NOT behind mob %s, it is looking to %d", c->GetTarget()->GetName(), c->GetTarget()->GetHeading()); - else - c->Message(Chat::White, "I Need a target!"); + if (c->GetTarget()) { + auto target = c->GetTarget(); + std::string behind_message = ( + c->BehindMob( + target, + c->GetX(), + c->GetY() + ) ? + "behind" : + "not behind" + ); + std::string gender_message = ( + target->GetGender() == MALE ? + "he" : + ( + target->GetGender() == FEMALE ? + "she" : + "it" + ) + ); + + c->Message( + Chat::White, + fmt::format( + "You are {} {} ({}), {} has a heading of {}.", + behind_message, + target->GetCleanName(), + target->GetID(), + gender_message, + target->GetHeading() + ).c_str() + ); + } else { + c->Message(Chat::White, "You must have a target to use this command."); + } } void command_npcstats(Client *c, const Seperator *sep) From a64e326c68dc6ab2931cd34542542fa0551a8336 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 9 Nov 2021 21:24:25 -0500 Subject: [PATCH 345/624] [Commands] Cleanup #viewzoneloot Command. (#1702) - Cleanup message logic. --- zone/command.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index a92b23ae0..e235b907f 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -15898,10 +15898,12 @@ void command_viewzoneloot(Client *c, const Seperator *sep) ); return; } + for (auto npc_entity : npc_list) { auto current_npc_item_list = npc_entity.second->GetItemList(); zone_loot_list.insert({ npc_entity.second->GetID(), current_npc_item_list }); } + for (auto loot_item : zone_loot_list) { uint32 current_entity_id = loot_item.first; auto current_item_list = loot_item.second; @@ -15963,6 +15965,7 @@ void command_viewzoneloot(Client *c, const Seperator *sep) ) : "not dropping" ); + c->Message( Chat::White, fmt::format( @@ -15976,13 +15979,13 @@ void command_viewzoneloot(Client *c, const Seperator *sep) std::string drop_string = ( loot_amount > 0 ? fmt::format( - "{} {} {}", + "{} {} dropping", (loot_amount > 1 ? "items" : "item"), - (loot_amount > 1 ? "are" : "is"), - (loot_amount > 1 ? "dropping" : "not dropping") + (loot_amount > 1 ? "are" : "is") ) : "items are dropping" ); + c->Message( Chat::White, fmt::format( From e306059f439f05346031751c8724714accf15cd3 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 9 Nov 2021 21:24:34 -0500 Subject: [PATCH 346/624] [Commands] Cleanup #showspellslist Command. (#1703) - Cleanup messages and display. --- zone/command.cpp | 12 ++----- zone/mob_ai.cpp | 86 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 84 insertions(+), 14 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index e235b907f..457d5b927 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -13991,19 +13991,11 @@ void command_object(Client *c, const Seperator *sep) void command_showspellslist(Client *c, const Seperator *sep) { Mob *target = c->GetTarget(); - - if (!target) { - c->Message(Chat::White, "Must target an NPC."); + if (!target || !target->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); return; } - - if (!target->IsNPC()) { - c->Message(Chat::White, "%s is not an NPC.", target->GetName()); - return; - } - target->CastToNPC()->AISpellsList(c); - return; } diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index a7cb29947..1c1be6b69 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -2818,12 +2818,90 @@ void NPC::RemoveSpellFromNPCList(uint16 spell_id) void NPC::AISpellsList(Client *c) { - if (!c) + if (!c) { return; + } - for (auto it = AIspells.begin(); it != AIspells.end(); ++it) - c->Message(Chat::White, "%s (%d): Type %d, Priority %d, Recast Delay %d, Resist Adjust %d, Min HP %d, Max HP %d", - spells[it->spellid].name, it->spellid, it->type, it->priority, it->recast_delay, it->resist_adjust, it->min_hp, it->max_hp); + if (AIspells.size() > 0) { + c->Message( + Chat::White, + fmt::format( + "{} has {} AI spells.", + GetCleanName(), + AIspells.size() + ).c_str() + ); + + int spell_slot = 1; + for (const auto& ai_spell : AIspells) { + c->Message( + Chat::White, + fmt::format( + "Spell {} | Name: {} ({}) Type: {} Mana Cost: {}", + spell_slot, + GetSpellName(ai_spell.spellid), + ai_spell.spellid, + ai_spell.type, + ai_spell.manacost + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Spell {} | Priority: {} Recast Delay: {}", + spell_slot, + ai_spell.priority, + ai_spell.recast_delay + ).c_str() + ); + + if (ai_spell.time_cancast) { + c->Message( + Chat::White, + fmt::format( + "Spell {} | Time Can Cast : {}", + spell_slot, + ai_spell.time_cancast + ).c_str() + ); + } + + if (ai_spell.resist_adjust) { + c->Message( + Chat::White, + fmt::format( + "Spell {} | Resist Adjust : {}", + spell_slot, + ai_spell.resist_adjust + ).c_str() + ); + } + + if (ai_spell.min_hp || ai_spell.max_hp) { + c->Message( + Chat::White, + fmt::format( + "Spell {} | Min HP: {} Max HP: {}", + spell_slot, + ai_spell.min_hp, + ai_spell.max_hp + ).c_str() + ); + } + + spell_slot++; + } + } + else { + c->Message( + Chat::White, + fmt::format( + "{} has no AI spells.", + GetCleanName() + ).c_str() + ); + } return; } From b5391b9110259a46e19870cb4e4f698c8dff183b Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 9 Nov 2021 21:25:42 -0500 Subject: [PATCH 347/624] [Commands] Cleanup #showstats Command. (#1700) - Convert Mob::ShowStats() to use the #npcstats code and make #npcstats use Mob::ShowStats(). --- zone/command.cpp | 582 +---------------------------------------- zone/mob.cpp | 655 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 626 insertions(+), 611 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 457d5b927..2dc790b87 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -2067,588 +2067,10 @@ void command_fov(Client *c, const Seperator *sep) void command_npcstats(Client *c, const Seperator *sep) { if (c->GetTarget() && c->GetTarget()->IsNPC()) { - auto target = c->GetTarget()->CastToNPC(); - std::string target_name = target->GetCleanName(); - std::string target_last_name = target->GetLastName(); - bool has_charmed_stats = ( - target->GetCharmedAccuracy() != 0 || - target->GetCharmedArmorClass() != 0 || - target->GetCharmedAttack() != 0 || - target->GetCharmedAttackDelay() != 0 || - target->GetCharmedAvoidance() != 0 || - target->GetCharmedMaxDamage() != 0 || - target->GetCharmedMinDamage() != 0 - ); - - // Spawn Data - c->Message( - Chat::White, - fmt::format( - "Spawn | Group: {} Point: {} Grid: {}", - target->GetSpawnGroupId(), - target->GetSpawnPointID(), - target->GetGrid() - ).c_str() - ); - - c->Message( - Chat::White, - fmt::format( - "Spawn | Raid: {} Rare: {}", - target->IsRaidTarget() ? "Yes" : "No", - target->IsRareSpawn() ? "Yes" : "No", - target->GetSkipGlobalLoot() ? "Yes" : "No" - ).c_str() - ); - - c->Message( - Chat::White, - fmt::format( - "Spawn | Skip Global Loot: {} Ignore Despawn: {}", - target->GetSkipGlobalLoot() ? "Yes" : "No", - target->GetIgnoreDespawn() ? "Yes" : "No" - ).c_str() - ); - - c->Message( - Chat::White, - fmt::format( - "Spawn | Findable: {} Trackable: {} Underwater: {}", - target->IsFindable() ? "Yes" : "No", - target->IsTrackable() ? "Yes" : "No", - target->IsUnderwaterOnly() ? "Yes" : "No" - ).c_str() - ); - - c->Message( - Chat::White, - fmt::format( - "Spawn | Stuck Behavior: {} Fly Mode: {}", - target->GetStuckBehavior(), - static_cast(target->GetFlyMode()) - ).c_str() - ); - - c->Message( - Chat::White, - fmt::format( - "Spawn | Aggro NPCs: {} Always Aggro: {}", - target->GetNPCAggro() ? "Yes" : "No", - target->GetAlwaysAggro() ? "Yes" : "No" - ).c_str() - ); - - // NPC - c->Message( - Chat::White, - fmt::format( - "NPC | ID: {} Name: {}{} Level: {}", - target->GetNPCTypeID(), - target_name, - ( - !target_last_name.empty() ? - fmt::format(" ({})", target_last_name) : - "" - ), - target->GetLevel() - ).c_str() - ); - - // Race / Class / Gender - c->Message( - Chat::White, - fmt::format( - "Race: {} ({}) Class: {} ({}) Gender: {} ({})", - GetRaceIDName(target->GetRace()), - target->GetRace(), - GetClassIDName(target->GetClass()), - target->GetClass(), - GetGenderName(target->GetGender()), - target->GetGender() - ).c_str() - ); - - // Faction - if (target->GetNPCFactionID()) { - auto faction_id = target->GetNPCFactionID(); - auto faction_name = content_db.GetFactionName(faction_id); - c->Message( - Chat::White, - fmt::format( - "Faction: {} ({})", - faction_name, - faction_id - ).c_str() - ); - } - - // Adventure Template - if (target->GetAdventureTemplate()) { - c->Message( - Chat::White, - fmt::format( - "Adventure Template: {}", - target->GetAdventureTemplate() - ).c_str() - ); - } - - // Body - c->Message( - Chat::White, - fmt::format( - "Body | Size: {:.2f} Type: {}", - target->GetSize(), - target->GetBodyType() - ).c_str() - ); - - // Face - c->Message( - Chat::White, - fmt::format( - "Features | Face: {} Eye One: {} Eye Two: {}", - target->GetLuclinFace(), - target->GetEyeColor1(), - target->GetEyeColor2() - ).c_str() - ); - - // Hair - c->Message( - Chat::White, - fmt::format( - "Features | Hair: {} Hair Color: {}", - target->GetHairStyle(), - target->GetHairColor() - ).c_str() - ); - - // Beard - c->Message( - Chat::White, - fmt::format( - "Features | Beard: {} Beard Color: {}", - target->GetBeard(), - target->GetBeardColor() - ).c_str() - ); - - // Drakkin Features - if (target->GetRace() == RACE_DRAKKIN_522) { - c->Message( - Chat::White, - fmt::format( - "Drakkin Features | Heritage: {} Tattoo: {} Details: {}", - target->GetDrakkinHeritage(), - target->GetDrakkinTattoo(), - target->GetDrakkinDetails() - ).c_str() - ); - } - - // Textures - c->Message( - Chat::White, - fmt::format( - "Textures | Armor: {} Helmet: {}", - target->GetTexture(), - target->GetHelmTexture() - ).c_str() - ); - - c->Message( - Chat::White, - fmt::format( - "Textures | Arms: {} Bracers: {} Hands: {}", - target->GetArmTexture(), - target->GetBracerTexture(), - target->GetHandTexture() - ).c_str() - ); - - c->Message( - Chat::White, - fmt::format( - "Textures | Legs: {} Feet: {}", - target->GetLegTexture(), - target->GetFeetTexture() - ).c_str() - ); - - // Hero's Forge - if (target->GetHeroForgeModel()) { - c->Message( - Chat::White, - fmt::format( - "Hero's Forge: {}", - target->GetHeroForgeModel() - ).c_str() - ); - } - - // Owner Data - if (target->GetOwner()) { - auto owner_name = target->GetOwner()->GetCleanName(); - auto owner_type = ( - target->GetOwner()->IsNPC() ? - "NPC" : - ( - target->GetOwner()->IsClient() ? - "Client" : - "Other" - ) - ); - auto owner_id = target->GetOwnerID(); - c->Message( - Chat::White, - fmt::format( - "Owner | Name: {} ({}) Type: {}", - owner_name, - owner_id, - owner_type - ).c_str() - ); - } - - // Pet Data - if (target->GetPet()) { - auto pet_name = target->GetPet()->GetCleanName(); - auto pet_id = target->GetPetID(); - c->Message( - Chat::White, - fmt::format( - "Pet | Name: {} ({})", - pet_name, - pet_id - ).c_str() - ); - } - - // Merchant Data - if (target->MerchantType) { - c->Message( - Chat::White, - fmt::format( - "Merchant | ID: {} Currency Type: {}", - target->MerchantType, - target->GetAltCurrencyType() - ).c_str() - ); - } - - // Spell Data - if (target->AI_HasSpells() || target->AI_HasSpellsEffects()) { - c->Message( - Chat::White, - fmt::format( - "Spells | ID: {} Effects ID: {}", - target->GetNPCSpellsID(), - target->GetNPCSpellsEffectsID() - ).c_str() - ); - } - - // Health - c->Message( - Chat::White, - fmt::format( - "Health: {}/{} ({:.2f}%) Regen: {}", - target->GetHP(), - target->GetMaxHP(), - target->GetHPRatio(), - target->GetHPRegen() - ).c_str() - ); - - // Mana - if (target->GetMaxMana() > 0) { - c->Message( - Chat::White, - fmt::format( - "Mana: {}/{} ({:.2f}%) Regen: {}", - target->GetMana(), - target->GetMaxMana(), - target->GetManaRatio(), - target->GetManaRegen() - ).c_str() - ); - } - - // Damage - c->Message( - Chat::White, - fmt::format( - "Damage | Min: {} Max: {}", - target->GetMinDMG(), - target->GetMaxDMG() - ).c_str() - ); - - // Attack Count / Delay - c->Message( - Chat::White, - fmt::format( - "Attack | Count: {} Delay: {}", - target->GetNumberOfAttacks(), - target->GetAttackDelay() - ).c_str() - ); - - // Weapon Textures - c->Message( - Chat::White, - fmt::format( - "Weapon Textures | Primary: {} Secondary: {} Ammo: {}", - target->GetEquipmentMaterial(EQ::textures::weaponPrimary), - target->GetEquipmentMaterial(EQ::textures::weaponSecondary), - target->GetAmmoIDfile() - ).c_str() - ); - - // Weapon Types - c->Message( - Chat::White, - fmt::format( - "Weapon Types | Primary: {} ({}) Secondary: {} ({})", - EQ::skills::GetSkillName(static_cast(target->GetPrimSkill())), - target->GetPrimSkill(), - EQ::skills::GetSkillName(static_cast(target->GetSecSkill())), - target->GetSecSkill() - ).c_str() - ); - - c->Message( - Chat::White, - fmt::format( - "Weapon Types | Ranged: {} ({})", - EQ::skills::GetSkillName(static_cast(target->GetRangedSkill())), - target->GetRangedSkill() - ).c_str() - ); - - // Combat Stats - c->Message( - Chat::White, - fmt::format( - "Combat Stats | Accuracy: {} Armor Class: {} Attack: {}", - target->GetAccuracyRating(), - target->GetAC(), - target->GetATK() - ).c_str() - ); - - c->Message( - Chat::White, - fmt::format( - "Combat Stats | Avoidance: {} Slow Mitigation: {}", - target->GetAvoidanceRating(), - target->GetSlowMitigation() - ).c_str() - ); + NPC* target = c->GetTarget()->CastToNPC(); // Stats - c->Message( - Chat::White, - fmt::format( - "Stats | Agility: {} Charisma: {} Dexterity: {} Intelligence: {}", - target->GetAGI(), - target->GetCHA(), - target->GetDEX(), - target->GetINT() - ).c_str() - ); - - c->Message( - Chat::White, - fmt::format( - "Stats | Stamina: {} Strength: {} Wisdom: {}", - target->GetSTA(), - target->GetSTR(), - target->GetWIS() - ).c_str() - ); - - // Charmed Stats - if (has_charmed_stats) { - c->Message( - Chat::White, - fmt::format( - "Charmed Stats | Attack: {} Attack Delay: {}", - target->GetCharmedAttack(), - target->GetCharmedAttackDelay() - ).c_str() - ); - - c->Message( - Chat::White, - fmt::format( - "Charmed Stats | Accuracy: {} Avoidance: {}", - target->GetCharmedAccuracy(), - target->GetCharmedAvoidance() - ).c_str() - ); - - c->Message( - Chat::White, - fmt::format( - "Charmed Stats | Min Damage: {} Max Damage: {}", - target->GetCharmedMinDamage(), - target->GetCharmedMaxDamage() - ).c_str() - ); - } - - // Resists - c->Message( - Chat::White, - fmt::format( - "Resists | Cold: {} Disease: {} Fire: {} Magic: {}", - target->GetCR(), - target->GetDR(), - target->GetFR(), - target->GetMR() - ).c_str() - ); - - c->Message( - Chat::White, - fmt::format( - "Resists | Poison: {} Corruption: {} Physical: {}", - target->GetPR(), - target->GetCorrup(), - target->GetPhR() - ).c_str() - ); - - // Scaling - c->Message( - Chat::White, - fmt::format( - "Scaling | Heal: {} Spell: {}", - target->GetHealScale(), - target->GetSpellScale() - ).c_str() - ); - - // See Invisible / Invisible vs. Undead / Hide / Improved Hide - c->Message( - Chat::White, - fmt::format( - "Can See | Invisible: {} Invisible vs. Undead: {}", - target->SeeInvisible() ? "Yes" : "No", - target->SeeInvisibleUndead() ? "Yes" : "No" - ).c_str() - ); - - c->Message( - Chat::White, - fmt::format( - "Can See | Hide: {} Improved Hide: {}", - target->SeeHide() ? "Yes" : "No", - target->SeeImprovedHide() ? "Yes" : "No" - ).c_str() - ); - - // Aggro / Assist Radius - c->Message( - Chat::White, - fmt::format( - "Radius | Aggro: {} Assist: {}", - target->GetAggroRange(), - target->GetAssistRange() - ).c_str() - ); - - // Emote - c->Message( - Chat::White, - fmt::format( - "Emote: {}", - target->GetEmoteID() - ).c_str() - ); - - // Run/Walk Speed - c->Message( - Chat::White, - fmt::format( - "Speed | Run: {} Walk: {}", - target->GetRunspeed(), - target->GetWalkspeed() - ).c_str() - ); - - // Position - c->Message( - Chat::White, - fmt::format( - "Position | {}, {}, {}, {}", - target->GetX(), - target->GetY(), - target->GetZ(), - target->GetHeading() - ).c_str() - ); - - // Experience Modifier - c->Message( - Chat::White, - fmt::format( - "Experience Modifier: {}", - target->GetKillExpMod() - ).c_str() - ); - - // Quest Globals - c->Message( - Chat::White, - fmt::format( - "Quest Globals: {}", - target->qglobal ? "Enabled" : "Disabled" - ).c_str() - ); - - // Proximity - if (target->IsProximitySet()) { - c->Message( - Chat::White, - fmt::format( - "Proximity | Say: {}", - target->proximity->say ? "Enabled" : "Disabled" - ).c_str() - ); - - c->Message( - Chat::White, - fmt::format( - "Proximity X | Min: {} Max: {} Range: {}", - target->GetProximityMinX(), - target->GetProximityMinX(), - (target->GetProximityMinX() - target->GetProximityMinX()) - ).c_str() - ); - - c->Message( - Chat::White, - fmt::format( - "Proximity Y | Min: {} Max: {} Range: {}", - target->GetProximityMinY(), - target->GetProximityMaxY(), - (target->GetProximityMaxY() - target->GetProximityMinY()) - ).c_str() - ); - - c->Message( - Chat::White, - fmt::format( - "Proximity Z | Min: {} Max: {} Range: {}", - target->GetProximityMinZ(), - target->GetProximityMaxZ(), - (target->GetProximityMaxZ() - target->GetProximityMinZ()) - ).c_str() - ); - } + target->ShowStats(c); // Loot Data if (target->GetLoottableID()) { diff --git a/zone/mob.cpp b/zone/mob.cpp index 0ac852b56..bed80846c 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1606,42 +1606,635 @@ void Mob::ShowStats(Client* client) { if (IsClient()) { CastToClient()->SendStatsWindow(client, RuleB(Character, UseNewStatsWindow)); - } - else if (IsCorpse()) { + } else if (IsCorpse()) { if (IsPlayerCorpse()) { - client->Message(Chat::White, " CharID: %i PlayerCorpse: %i", CastToCorpse()->GetCharID(), CastToCorpse()->GetCorpseDBID()); + client->Message( + Chat::White, + fmt::format( + "Player Corpse | Character ID: {} ID: {}", + CastToCorpse()->GetCharID(), + CastToCorpse()->GetCorpseDBID() + ).c_str() + ); + } else { + client->Message( + Chat::White, + fmt::format( + "NPC Corpse | ID: {}", + GetID() + ).c_str() + ); } - else { - client->Message(Chat::White, " NPCCorpse", GetID()); - } - } - else { - client->Message(Chat::White, " Level: %i AC: %i Class: %i Size: %1.1f Haste: %i", GetLevel(), ACSum(), GetClass(), GetSize(), GetHaste()); - client->Message(Chat::White, " HP: %i Max HP: %i",GetHP(), GetMaxHP()); - client->Message(Chat::White, " Mana: %i Max Mana: %i", GetMana(), GetMaxMana()); - client->Message(Chat::White, " Total ATK: %i Worn/Spell ATK (Cap %i): %i", GetATK(), RuleI(Character, ItemATKCap), GetATKBonus()); - client->Message(Chat::White, " STR: %i STA: %i DEX: %i AGI: %i INT: %i WIS: %i CHA: %i", GetSTR(), GetSTA(), GetDEX(), GetAGI(), GetINT(), GetWIS(), GetCHA()); - client->Message(Chat::White, " MR: %i PR: %i FR: %i CR: %i DR: %i Corruption: %i PhR: %i", GetMR(), GetPR(), GetFR(), GetCR(), GetDR(), GetCorrup(), GetPhR()); - client->Message(Chat::White, " Race: %i BaseRace: %i Texture: %i HelmTexture: %i Gender: %i BaseGender: %i", GetRace(), GetBaseRace(), GetTexture(), GetHelmTexture(), GetGender(), GetBaseGender()); - if (client->Admin() >= 100) - client->Message(Chat::White, " EntityID: %i PetID: %i OwnerID: %i AIControlled: %i Targetted: %i", GetID(), GetPetID(), GetOwnerID(), IsAIControlled(), targeted); + } else { + NPC* target = CastToNPC(); + std::string target_name = target->GetCleanName(); + std::string target_last_name = target->GetLastName(); + bool has_charmed_stats = ( + target->GetCharmedAccuracy() != 0 || + target->GetCharmedArmorClass() != 0 || + target->GetCharmedAttack() != 0 || + target->GetCharmedAttackDelay() != 0 || + target->GetCharmedAvoidance() != 0 || + target->GetCharmedMaxDamage() != 0 || + target->GetCharmedMinDamage() != 0 + ); - if (IsNPC()) { - NPC *n = CastToNPC(); - uint32 spawngroupid = 0; - if(n->respawn2 != 0) - spawngroupid = n->respawn2->SpawnGroupID(); - client->Message(Chat::White, " NPCID: %u SpawnGroupID: %u Grid: %i LootTable: %u FactionID: %i SpellsID: %u ", GetNPCTypeID(),spawngroupid, n->GetGrid(), n->GetLoottableID(), n->GetNPCFactionID(), n->GetNPCSpellsID()); - client->Message(Chat::White, " Accuracy: %i MerchantID: %i EmoteID: %i Runspeed: %.3f Walkspeed: %.3f", n->GetAccuracyRating(), n->MerchantType, n->GetEmoteID(), static_cast(0.025f * n->GetRunspeed()), static_cast(0.025f * n->GetWalkspeed())); - n->QueryLoot(client); - } - if (IsAIControlled()) { - client->Message(Chat::White, " AggroRange: %1.0f AssistRange: %1.0f", GetAggroRange(), GetAssistRange()); + // Spawn Data + client->Message( + Chat::White, + fmt::format( + "Spawn | Group: {} Point: {} Grid: {}", + target->GetSpawnGroupId(), + target->GetSpawnPointID(), + target->GetGrid() + ).c_str() + ); + + client->Message( + Chat::White, + fmt::format( + "Spawn | Raid: {} Rare: {}", + target->IsRaidTarget() ? "Yes" : "No", + target->IsRareSpawn() ? "Yes" : "No", + target->GetSkipGlobalLoot() ? "Yes" : "No" + ).c_str() + ); + + client->Message( + Chat::White, + fmt::format( + "Spawn | Skip Global Loot: {} Ignore Despawn: {}", + target->GetSkipGlobalLoot() ? "Yes" : "No", + target->GetIgnoreDespawn() ? "Yes" : "No" + ).c_str() + ); + + client->Message( + Chat::White, + fmt::format( + "Spawn | Findable: {} Trackable: {} Underwater: {}", + target->IsFindable() ? "Yes" : "No", + target->IsTrackable() ? "Yes" : "No", + target->IsUnderwaterOnly() ? "Yes" : "No" + ).c_str() + ); + + client->Message( + Chat::White, + fmt::format( + "Spawn | Stuck Behavior: {} Fly Mode: {}", + target->GetStuckBehavior(), + static_cast(target->GetFlyMode()) + ).c_str() + ); + + client->Message( + Chat::White, + fmt::format( + "Spawn | Aggro NPCs: {} Always Aggro: {}", + target->GetNPCAggro() ? "Yes" : "No", + target->GetAlwaysAggro() ? "Yes" : "No" + ).c_str() + ); + + // NPC + client->Message( + Chat::White, + fmt::format( + "NPC | ID: {} Name: {}{} Level: {}", + target->GetNPCTypeID(), + target_name, + ( + !target_last_name.empty() ? + fmt::format(" ({})", target_last_name) : + "" + ), + target->GetLevel() + ).c_str() + ); + + // Race / Class / Gender + client->Message( + Chat::White, + fmt::format( + "Race: {} ({}) Class: {} ({}) Gender: {} ({})", + GetRaceIDName(target->GetRace()), + target->GetRace(), + GetClassIDName(target->GetClass()), + target->GetClass(), + GetGenderName(target->GetGender()), + target->GetGender() + ).c_str() + ); + + // Faction + if (target->GetNPCFactionID()) { + auto faction_id = target->GetNPCFactionID(); + auto faction_name = content_db.GetFactionName(faction_id); + client->Message( + Chat::White, + fmt::format( + "Faction: {} ({})", + faction_name, + faction_id + ).c_str() + ); } - client->Message(Chat::White, " compute_tohit: %i TotalToHit: %i", compute_tohit(EQ::skills::SkillHandtoHand), GetTotalToHit(EQ::skills::SkillHandtoHand, 0)); - client->Message(Chat::White, " compute_defense: %i TotalDefense: %i", compute_defense(), GetTotalDefense()); - client->Message(Chat::White, " offense: %i mitigation ac: %i", offense(EQ::skills::SkillHandtoHand), GetMitigationAC()); + // Adventure Template + if (target->GetAdventureTemplate()) { + client->Message( + Chat::White, + fmt::format( + "Adventure Template: {}", + target->GetAdventureTemplate() + ).c_str() + ); + } + + // Body + client->Message( + Chat::White, + fmt::format( + "Body | Size: {:.2f} Type: {}", + target->GetSize(), + target->GetBodyType() + ).c_str() + ); + + // Face + client->Message( + Chat::White, + fmt::format( + "Features | Face: {} Eye One: {} Eye Two: {}", + target->GetLuclinFace(), + target->GetEyeColor1(), + target->GetEyeColor2() + ).c_str() + ); + + // Hair + client->Message( + Chat::White, + fmt::format( + "Features | Hair: {} Hair Color: {}", + target->GetHairStyle(), + target->GetHairColor() + ).c_str() + ); + + // Beard + client->Message( + Chat::White, + fmt::format( + "Features | Beard: {} Beard Color: {}", + target->GetBeard(), + target->GetBeardColor() + ).c_str() + ); + + // Drakkin Features + if (target->GetRace() == RACE_DRAKKIN_522) { + client->Message( + Chat::White, + fmt::format( + "Drakkin Features | Heritage: {} Tattoo: {} Details: {}", + target->GetDrakkinHeritage(), + target->GetDrakkinTattoo(), + target->GetDrakkinDetails() + ).c_str() + ); + } + + // Textures + client->Message( + Chat::White, + fmt::format( + "Textures | Armor: {} Helmet: {}", + target->GetTexture(), + target->GetHelmTexture() + ).c_str() + ); + + client->Message( + Chat::White, + fmt::format( + "Textures | Arms: {} Bracers: {} Hands: {}", + target->GetArmTexture(), + target->GetBracerTexture(), + target->GetHandTexture() + ).c_str() + ); + + client->Message( + Chat::White, + fmt::format( + "Textures | Legs: {} Feet: {}", + target->GetLegTexture(), + target->GetFeetTexture() + ).c_str() + ); + + // Hero's Forge + if (target->GetHeroForgeModel()) { + client->Message( + Chat::White, + fmt::format( + "Hero's Forge: {}", + target->GetHeroForgeModel() + ).c_str() + ); + } + + // Owner Data + if (target->GetOwner()) { + auto owner_name = target->GetOwner()->GetCleanName(); + auto owner_type = ( + target->GetOwner()->IsNPC() ? + "NPC" : + ( + target->GetOwner()->IsClient() ? + "Client" : + "Other" + ) + ); + auto owner_id = target->GetOwnerID(); + client->Message( + Chat::White, + fmt::format( + "Owner | Name: {} ({}) Type: {}", + owner_name, + owner_id, + owner_type + ).c_str() + ); + } + + // Pet Data + if (target->GetPet()) { + auto pet_name = target->GetPet()->GetCleanName(); + auto pet_id = target->GetPetID(); + client->Message( + Chat::White, + fmt::format( + "Pet | Name: {} ({})", + pet_name, + pet_id + ).c_str() + ); + } + + // Merchant Data + if (target->MerchantType) { + client->Message( + Chat::White, + fmt::format( + "Merchant | ID: {} Currency Type: {}", + target->MerchantType, + target->GetAltCurrencyType() + ).c_str() + ); + } + + // Spell Data + if (target->AI_HasSpells() || target->AI_HasSpellsEffects()) { + client->Message( + Chat::White, + fmt::format( + "Spells | ID: {} Effects ID: {}", + target->GetNPCSpellsID(), + target->GetNPCSpellsEffectsID() + ).c_str() + ); + } + + // Health + client->Message( + Chat::White, + fmt::format( + "Health: {}/{} ({:.2f}%) Regen: {}", + target->GetHP(), + target->GetMaxHP(), + target->GetHPRatio(), + target->GetHPRegen() + ).c_str() + ); + + // Mana + if (target->GetMaxMana() > 0) { + client->Message( + Chat::White, + fmt::format( + "Mana: {}/{} ({:.2f}%) Regen: {}", + target->GetMana(), + target->GetMaxMana(), + target->GetManaRatio(), + target->GetManaRegen() + ).c_str() + ); + } + + // Damage + client->Message( + Chat::White, + fmt::format( + "Damage | Min: {} Max: {}", + target->GetMinDMG(), + target->GetMaxDMG() + ).c_str() + ); + + // Attack Count / Delay + client->Message( + Chat::White, + fmt::format( + "Attack | Count: {} Delay: {}", + target->GetNumberOfAttacks(), + target->GetAttackDelay() + ).c_str() + ); + + // Weapon Textures + client->Message( + Chat::White, + fmt::format( + "Weapon Textures | Primary: {} Secondary: {} Ammo: {}", + target->GetEquipmentMaterial(EQ::textures::weaponPrimary), + target->GetEquipmentMaterial(EQ::textures::weaponSecondary), + target->GetAmmoIDfile() + ).c_str() + ); + + // Weapon Types + client->Message( + Chat::White, + fmt::format( + "Weapon Types | Primary: {} ({}) Secondary: {} ({})", + EQ::skills::GetSkillName(static_cast(target->GetPrimSkill())), + target->GetPrimSkill(), + EQ::skills::GetSkillName(static_cast(target->GetSecSkill())), + target->GetSecSkill() + ).c_str() + ); + + client->Message( + Chat::White, + fmt::format( + "Weapon Types | Ranged: {} ({})", + EQ::skills::GetSkillName(static_cast(target->GetRangedSkill())), + target->GetRangedSkill() + ).c_str() + ); + + // Combat Stats + client->Message( + Chat::White, + fmt::format( + "Combat Stats | Accuracy: {} Armor Class: {} Attack: {}", + target->GetAccuracyRating(), + target->GetAC(), + target->GetATK() + ).c_str() + ); + + client->Message( + Chat::White, + fmt::format( + "Combat Stats | Avoidance: {} Slow Mitigation: {}", + target->GetAvoidanceRating(), + target->GetSlowMitigation() + ).c_str() + ); + + client->Message( + Chat::White, + fmt::format( + "Combat Stats | To Hit: {} Total To Hit: {}", + compute_tohit(EQ::skills::SkillHandtoHand), + GetTotalToHit(EQ::skills::SkillHandtoHand, 0) + ).c_str() + ); + + client->Message( + Chat::White, + fmt::format( + "Combat Stats | Defense: {} Total Defense: {}", + compute_defense(), + GetTotalDefense() + ).c_str() + ); + + client->Message( + Chat::White, + fmt::format( + "Combat Stats | Offense: {} Mitigation Armor Class: {}", + offense(EQ::skills::SkillHandtoHand), + GetMitigationAC() + ).c_str() + ); + + // Stats + client->Message( + Chat::White, + fmt::format( + "Stats | Agility: {} Charisma: {} Dexterity: {} Intelligence: {}", + target->GetAGI(), + target->GetCHA(), + target->GetDEX(), + target->GetINT() + ).c_str() + ); + + client->Message( + Chat::White, + fmt::format( + "Stats | Stamina: {} Strength: {} Wisdom: {}", + target->GetSTA(), + target->GetSTR(), + target->GetWIS() + ).c_str() + ); + + // Charmed Stats + if (has_charmed_stats) { + client->Message( + Chat::White, + fmt::format( + "Charmed Stats | Attack: {} Attack Delay: {}", + target->GetCharmedAttack(), + target->GetCharmedAttackDelay() + ).c_str() + ); + + client->Message( + Chat::White, + fmt::format( + "Charmed Stats | Accuracy: {} Avoidance: {}", + target->GetCharmedAccuracy(), + target->GetCharmedAvoidance() + ).c_str() + ); + + client->Message( + Chat::White, + fmt::format( + "Charmed Stats | Min Damage: {} Max Damage: {}", + target->GetCharmedMinDamage(), + target->GetCharmedMaxDamage() + ).c_str() + ); + } + + // Resists + client->Message( + Chat::White, + fmt::format( + "Resists | Cold: {} Disease: {} Fire: {} Magic: {}", + target->GetCR(), + target->GetDR(), + target->GetFR(), + target->GetMR() + ).c_str() + ); + + client->Message( + Chat::White, + fmt::format( + "Resists | Poison: {} Corruption: {} Physical: {}", + target->GetPR(), + target->GetCorrup(), + target->GetPhR() + ).c_str() + ); + + // Scaling + client->Message( + Chat::White, + fmt::format( + "Scaling | Heal: {} Spell: {}", + target->GetHealScale(), + target->GetSpellScale() + ).c_str() + ); + + // See Invisible / Invisible vs. Undead / Hide / Improved Hide + client->Message( + Chat::White, + fmt::format( + "Can See | Invisible: {} Invisible vs. Undead: {}", + target->SeeInvisible() ? "Yes" : "No", + target->SeeInvisibleUndead() ? "Yes" : "No" + ).c_str() + ); + + client->Message( + Chat::White, + fmt::format( + "Can See | Hide: {} Improved Hide: {}", + target->SeeHide() ? "Yes" : "No", + target->SeeImprovedHide() ? "Yes" : "No" + ).c_str() + ); + + // Aggro / Assist Radius + client->Message( + Chat::White, + fmt::format( + "Radius | Aggro: {} Assist: {}", + target->GetAggroRange(), + target->GetAssistRange() + ).c_str() + ); + + // Emote + client->Message( + Chat::White, + fmt::format( + "Emote: {}", + target->GetEmoteID() + ).c_str() + ); + + // Run/Walk Speed + client->Message( + Chat::White, + fmt::format( + "Speed | Run: {} Walk: {}", + target->GetRunspeed(), + target->GetWalkspeed() + ).c_str() + ); + + // Position + client->Message( + Chat::White, + fmt::format( + "Position | {}, {}, {}, {}", + target->GetX(), + target->GetY(), + target->GetZ(), + target->GetHeading() + ).c_str() + ); + + // Experience Modifier + client->Message( + Chat::White, + fmt::format( + "Experience Modifier: {}", + target->GetKillExpMod() + ).c_str() + ); + + // Quest Globals + client->Message( + Chat::White, + fmt::format( + "Quest Globals: {}", + target->qglobal ? "Enabled" : "Disabled" + ).c_str() + ); + + // Proximity + if (target->IsProximitySet()) { + client->Message( + Chat::White, + fmt::format( + "Proximity | Say: {}", + target->proximity->say ? "Enabled" : "Disabled" + ).c_str() + ); + + client->Message( + Chat::White, + fmt::format( + "Proximity X | Min: {} Max: {} Range: {}", + target->GetProximityMinX(), + target->GetProximityMinX(), + (target->GetProximityMinX() - target->GetProximityMinX()) + ).c_str() + ); + + client->Message( + Chat::White, + fmt::format( + "Proximity Y | Min: {} Max: {} Range: {}", + target->GetProximityMinY(), + target->GetProximityMaxY(), + (target->GetProximityMaxY() - target->GetProximityMinY()) + ).c_str() + ); + + client->Message( + Chat::White, + fmt::format( + "Proximity Z | Min: {} Max: {} Range: {}", + target->GetProximityMinZ(), + target->GetProximityMaxZ(), + (target->GetProximityMaxZ() - target->GetProximityMinZ()) + ).c_str() + ); + } } } From 6661672e2d2bcf4a8c8db2e8cbbcf98a69550eeb Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 9 Nov 2021 23:24:46 -0500 Subject: [PATCH 348/624] [Commands] Cleanup #showskills Command. (#1698) * [Commands] Cleanup #showskills Command. - Cleanup display and use GetSkillName() helper method. * Add optional "all" parameter to show all skills. * Formatting. * Formatting. * Target, not c. --- zone/command.cpp | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 2dc790b87..3f99bbe7b 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -3144,14 +3144,44 @@ void command_flymode(Client *c, const Seperator *sep) void command_showskills(Client *c, const Seperator *sep) { - Client *t=c; + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } - if(c->GetTarget() && c->GetTarget()->IsClient()) - t=c->GetTarget()->CastToClient(); + bool show_all = false; - c->Message(Chat::White, "Skills for %s", t->GetName()); - for (EQ::skills::SkillType i = EQ::skills::Skill1HBlunt; i <= EQ::skills::HIGHEST_SKILL; i = (EQ::skills::SkillType)(i + 1)) - c->Message(Chat::White, "Skill [%d] is at [%d] - %u", i, t->GetSkill(i), t->GetRawSkill(i)); + if (!strcasecmp("all", sep->arg[1])) { + show_all = true; + } + + c->Message( + Chat::White, + fmt::format( + "Skills | Name: {}", + target->GetCleanName() + ).c_str() + ); + + for ( + EQ::skills::SkillType skill_type = EQ::skills::Skill1HBlunt; + skill_type <= EQ::skills::HIGHEST_SKILL; + skill_type = (EQ::skills::SkillType)(skill_type + 1) + ) { + if (show_all || (target->CanHaveSkill(skill_type) && target->MaxSkill(skill_type))) { + c->Message( + Chat::White, + fmt::format( + "{} ({}) | Current: {} Max: {} Raw: {}", + EQ::skills::GetSkillName(skill_type), + skill_type, + target->GetSkill(skill_type), + target->MaxSkill(skill_type), + target->GetRawSkill(skill_type) + ).c_str() + ); + } + } } void command_findclass(Client *c, const Seperator *sep) From 32d606c6678ace7ac0a3c3cfe381ad69d999f88e Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Wed, 10 Nov 2021 19:48:02 -0500 Subject: [PATCH 349/624] [Bug Fix] Fix Mob::ShowStats() Proximity Display. (#1708) --- zone/mob.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index bed80846c..74fb9d1f0 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -2210,8 +2210,8 @@ void Mob::ShowStats(Client* client) fmt::format( "Proximity X | Min: {} Max: {} Range: {}", target->GetProximityMinX(), - target->GetProximityMinX(), - (target->GetProximityMinX() - target->GetProximityMinX()) + target->GetProximityMaxX(), + (target->GetProximityMaxX() - target->GetProximityMinX()) ).c_str() ); From b17c24d2dfdaaf531e677c2cdf61bd7214f48e07 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Wed, 10 Nov 2021 21:20:40 -0500 Subject: [PATCH 350/624] [Commands] Cleanup #setskill Command. (#1704) * [Commands] Cleanup #setskill Command. - Cleanup message and logic. * Optimize GetSkillName(). --- common/skills.cpp | 8 ++---- zone/command.cpp | 65 +++++++++++++++++++++++++++++++++-------------- 2 files changed, 48 insertions(+), 25 deletions(-) diff --git a/common/skills.cpp b/common/skills.cpp index 0d423f556..2276a0e39 100644 --- a/common/skills.cpp +++ b/common/skills.cpp @@ -263,12 +263,8 @@ const std::map& EQ::skills::GetSkillTypeMap( std::string EQ::skills::GetSkillName(SkillType skill) { if (skill >= Skill1HBlunt && skill <= Skill2HPiercing) { - std::map skills = GetSkillTypeMap(); - for (auto current_skill : skills) { - if (skill == current_skill.first) { - return current_skill.second; - } - } + auto skills = GetSkillTypeMap(); + return skills[skill]; } return std::string(); } diff --git a/zone/command.cpp b/zone/command.cpp index 3f99bbe7b..a8d6d79d6 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -3721,29 +3721,56 @@ void command_setlanguage(Client *c, const Seperator *sep) } } -void command_setskill(Client *c, const Seperator *sep) +void command_setskill(Client* c, const Seperator* sep) { - if (c->GetTarget() == nullptr) { - c->Message(Chat::White, "Error: #setskill: No target."); + Client* target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); } - else if (!c->GetTarget()->IsClient()) { - c->Message(Chat::White, "Error: #setskill: Target must be a client."); - } - else if ( - !sep->IsNumber(1) || atoi(sep->arg[1]) < 0 || atoi(sep->arg[1]) > EQ::skills::HIGHEST_SKILL || - !sep->IsNumber(2) || atoi(sep->arg[2]) < 0 || atoi(sep->arg[2]) > HIGHEST_CAN_SET_SKILL - ) - { - c->Message(Chat::White, "Usage: #setskill skill x "); - c->Message(Chat::White, " skill = 0 to %d", EQ::skills::HIGHEST_SKILL); - c->Message(Chat::White, " x = 0 to %d", HIGHEST_CAN_SET_SKILL); + + auto skill_id = sep->IsNumber(1) ? std::stoi(sep->arg[1]) : -1; + auto skill_value = sep->IsNumber(2) ? std::stoi(sep->arg[2]) : -1; + if ( + skill_id < 0 || + skill_id > EQ::skills::HIGHEST_SKILL || + skill_value < 0 || + skill_value > HIGHEST_CAN_SET_SKILL + ) { + c->Message(Chat::White, "Usage: #setskill [Skill ID] [Skill Value]"); + c->Message(Chat::White, fmt::format("Skill ID = 0 to {}", EQ::skills::HIGHEST_SKILL).c_str()); + c->Message(Chat::White, fmt::format("Skill Value = 0 to {}", HIGHEST_CAN_SET_SKILL).c_str()); } else { - LogInfo("Set skill request from [{}], target:[{}] skill_id:[{}] value:[{}]", c->GetName(), c->GetTarget()->GetName(), atoi(sep->arg[1]), atoi(sep->arg[2]) ); - int skill_num = atoi(sep->arg[1]); - uint16 skill_value = atoi(sep->arg[2]); - if (skill_num <= EQ::skills::HIGHEST_SKILL) - c->GetTarget()->CastToClient()->SetSkill((EQ::skills::SkillType)skill_num, skill_value); + LogInfo( + "Set skill request from [{}], Target: [{}] Skill ID: [{}] Skill Value: [{}]", + c->GetCleanName(), + target->GetCleanName(), + skill_id, + skill_value + ); + + if ( + skill_id >= EQ::skills::Skill1HBlunt && + skill_id <= EQ::skills::HIGHEST_SKILL + ) { + target->SetSkill( + (EQ::skills::SkillType)skill_id, + skill_value + ); + + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "Set {} ({}) to {} for {}.", + EQ::skills::GetSkillName((EQ::skills::SkillType)skill_id), + skill_id, + skill_value, + target->GetCleanName() + ).c_str() + ); + } + } } } From aac0dd2993aca07c216cf494a9b458009defa575 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Wed, 10 Nov 2021 21:20:51 -0500 Subject: [PATCH 351/624] [Commands] Cleanup #setlanguage Command. (#1705) * [Commands] Cleanup #setlanguage Command. - Cleanup message and lofic. - Add GetLanguageName() helper and GetLanguageMap() for future use. * Optimize GetLanguageName(). --- common/emu_constants.cpp | 45 +++++++++++++++++ common/emu_constants.h | 3 ++ zone/command.cpp | 104 +++++++++++++++++++-------------------- 3 files changed, 99 insertions(+), 53 deletions(-) diff --git a/common/emu_constants.cpp b/common/emu_constants.cpp index 8a6d2a8bd..619b7055a 100644 --- a/common/emu_constants.cpp +++ b/common/emu_constants.cpp @@ -18,6 +18,7 @@ */ #include "emu_constants.h" +#include "languages.h" int16 EQ::invtype::GetInvTypeSize(int16 inv_type) { @@ -152,3 +153,47 @@ int EQ::constants::ConvertStanceTypeToIndex(StanceType stance_type) { return 0; } + +const std::map& EQ::constants::GetLanguageMap() +{ + static const std::map language_map = { + { LANG_COMMON_TONGUE, "Common Tongue" }, + { LANG_BARBARIAN, "Barbarian" }, + { LANG_ERUDIAN, "Erudian" }, + { LANG_ELVISH, "Elvish" }, + { LANG_DARK_ELVISH, "Dark Elvish" }, + { LANG_DWARVISH, "Dwarvish" }, + { LANG_TROLL, "Troll" }, + { LANG_OGRE, "Ogre" }, + { LANG_GNOMISH, "Gnomish" }, + { LANG_HALFLING, "Halfling" }, + { LANG_THIEVES_CANT, "Thieves Cant" }, + { LANG_OLD_ERUDIAN, "Old Erudian" }, + { LANG_ELDER_ELVISH, "Elder Elvish" }, + { LANG_FROGLOK, "Froglok" }, + { LANG_GOBLIN, "Goblin" }, + { LANG_GNOLL, "Gnoll" }, + { LANG_COMBINE_TONGUE, "Combine Tongue" }, + { LANG_ELDER_TEIRDAL, "Elder Teirdal" }, + { LANG_LIZARDMAN, "Lizardman" }, + { LANG_ORCISH, "Orcish" }, + { LANG_FAERIE, "Faerie" }, + { LANG_DRAGON, "Dragon" }, + { LANG_ELDER_DRAGON, "Elder Dragon" }, + { LANG_DARK_SPEECH, "Dark Speech" }, + { LANG_VAH_SHIR, "Vah Shir" }, + { LANG_ALARAN, "Alaran" }, + { LANG_HADAL, "Hadal" }, + { LANG_UNKNOWN, "Unknown" } + }; + return language_map; +} + +std::string EQ::constants::GetLanguageName(int language_id) +{ + if (language_id >= LANG_COMMON_TONGUE && language_id <= LANG_UNKNOWN) { + auto languages = EQ::constants::GetLanguageMap(); + return languages[language_id]; + } + return std::string(); +} diff --git a/common/emu_constants.h b/common/emu_constants.h index a29418394..c64199082 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -223,6 +223,9 @@ namespace EQ const char *GetStanceName(StanceType stance_type); int ConvertStanceTypeToIndex(StanceType stance_type); + extern const std::map& GetLanguageMap(); + std::string GetLanguageName(int language_id); + const int STANCE_TYPE_FIRST = stancePassive; const int STANCE_TYPE_LAST = stanceBurnAE; const int STANCE_TYPE_COUNT = stanceBurnAE; diff --git a/zone/command.cpp b/zone/command.cpp index a8d6d79d6..5ed0e8f03 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -79,6 +79,7 @@ #include "../common/http/httplib.h" #include "../common/shared_tasks.h" #include "gm_commands/door_manipulation.h" +#include "../common/languages.h" extern QueryServ* QServ; extern WorldServer worldserver; @@ -3664,60 +3665,57 @@ void command_castspell(Client *c, const Seperator *sep) void command_setlanguage(Client *c, const Seperator *sep) { - if (strcasecmp(sep->arg[1], "list" ) == 0 ) - { - c->Message(Chat::White, "Languages:"); - c->Message(Chat::White, "(0) Common Tongue"); - c->Message(Chat::White, "(1) Barbarian"); - c->Message(Chat::White, "(2) Erudian"); - c->Message(Chat::White, "(3) Elvish"); - c->Message(Chat::White, "(4) Dark Elvish"); - c->Message(Chat::White, "(5) Dwarvish"); - c->Message(Chat::White, "(6) Troll"); - c->Message(Chat::White, "(7) Ogre"); - c->Message(Chat::White, "(8) Gnomish"); - c->Message(Chat::White, "(9) Halfling"); - c->Message(Chat::White, "(10) Thieves Cant"); - c->Message(Chat::White, "(11) Old Erudian"); - c->Message(Chat::White, "(12) Elder Elvish"); - c->Message(Chat::White, "(13) Froglok"); - c->Message(Chat::White, "(14) Goblin"); - c->Message(Chat::White, "(15) Gnoll"); - c->Message(Chat::White, "(16) Combine Tongue"); - c->Message(Chat::White, "(17) Elder Teir`Dal"); - c->Message(Chat::White, "(18) Lizardman"); - c->Message(Chat::White, "(19) Orcish"); - c->Message(Chat::White, "(20) Faerie"); - c->Message(Chat::White, "(21) Dragon"); - c->Message(Chat::White, "(22) Elder Dragon"); - c->Message(Chat::White, "(23) Dark Speech"); - c->Message(Chat::White, "(24) Vah Shir"); - c->Message(Chat::White, "(25) Alaran"); - c->Message(Chat::White, "(26) Hadal"); - c->Message(Chat::White, "(27) Unknown1"); + Client* target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); } - else if( c->GetTarget() == 0 ) - { - c->Message(Chat::White, "Error: #setlanguage: No target."); - } - else if( !c->GetTarget()->IsClient() ) - { - c->Message(Chat::White, "Error: Target must be a player."); - } - else if ( - !sep->IsNumber(1) || atoi(sep->arg[1]) < 0 || atoi(sep->arg[1]) > 27 || - !sep->IsNumber(2) || atoi(sep->arg[2]) < 0 || atoi(sep->arg[2]) > 100 - ) - { - c->Message(Chat::White, "Usage: #setlanguage [language ID] [value] (0-27, 0-100)"); - c->Message(Chat::White, "Try #setlanguage list for a list of language IDs"); - } - else - { - LogInfo("Set language request from [{}], target:[{}] lang_id:[{}] value:[{}]", c->GetName(), c->GetTarget()->GetName(), atoi(sep->arg[1]), atoi(sep->arg[2]) ); - uint8 langid = (uint8)atoi(sep->arg[1]); - uint8 value = (uint8)atoi(sep->arg[2]); - c->GetTarget()->CastToClient()->SetLanguageSkill( langid, value ); + + auto language_id = sep->IsNumber(1) ? std::stoi(sep->arg[1]) : -1; + auto language_value = sep->IsNumber(2) ? std::stoi(sep->arg[2]) : -1; + if (!strcasecmp(sep->arg[1], "list" )) { + for (int language = LANG_COMMON_TONGUE; language <= LANG_UNKNOWN; language++) { + c->Message( + Chat::White, + fmt::format( + "Language {}: {}", + language, + EQ::constants::GetLanguageName(language) + ).c_str() + ); + } + } else if ( + language_id < LANG_COMMON_TONGUE || + language_id > LANG_UNKNOWN || + language_value < 0 || + language_value > 100 + ) { + c->Message(Chat::White, "Usage: #setlanguage [Language ID] [Language Value]"); + c->Message(Chat::White, "Usage: #setlanguage [List]"); + c->Message(Chat::White, "Language ID = 0 to 27", LANG_UNKNOWN); + c->Message(Chat::White, "Language Value = 0 to 100", HIGHEST_CAN_SET_SKILL); + } else { + LogInfo( + "Set language request from [{}], Target: [{}] Language ID: [{}] Language Value: [{}]", + c->GetCleanName(), + target->GetCleanName(), + language_id, + language_value + ); + + target->SetLanguageSkill(language_id, language_value); + + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "Set {} ({}) to {} for {}.", + EQ::constants::GetLanguageName(language_id), + language_id, + language_value, + target->GetCleanName() + ).c_str() + ); + } } } From 990729fe21e5ac0d9f15cdcd45cd0e5b65e93c69 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Wed, 10 Nov 2021 21:21:06 -0500 Subject: [PATCH 352/624] [Commands] Cleanup #distance Command. (#1707) - Cleanup message. --- zone/command.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 5ed0e8f03..49952eaf1 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -13720,10 +13720,22 @@ void command_globalview(Client *c, const Seperator *sep) } void command_distance(Client *c, const Seperator *sep) { - if(c && c->GetTarget()) { + if (c->GetTarget()) { Mob* target = c->GetTarget(); - - c->Message(Chat::White, "Your target, %s, is %1.1f units from you.", c->GetTarget()->GetName(), Distance(c->GetPosition(), target->GetPosition())); + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "{} ({}) is {:.2f} units from you.", + target->GetCleanName(), + target->GetID(), + Distance( + c->GetPosition(), + target->GetPosition() + ) + ).c_str() + ); + } } } From d9c8e80bca63334028a072f61f5f5d7dab0c99a6 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 10 Nov 2021 21:23:49 -0500 Subject: [PATCH 353/624] [Spells] Allow item click effects to have cast time and recast time modified by focus effects. (#1695) * prelim * Spell Focus implemented * AA implemented * Update spdat.h * Update spdat.h * working * Update spells.cpp * prelim excludes * enum limit expansion * overhaul * v2 testing * updates * working * Fin * Update spell_effects.cpp * Update spell_effects.cpp * update * Update spells.cpp * fix * fix * Update spell_effects.cpp * remove debugs * Update spells.cpp --- zone/spell_effects.cpp | 54 +++++++++++++++++++++++++++++++++++------- zone/spells.cpp | 50 ++++++++++++++++++++++++++------------ 2 files changed, 81 insertions(+), 23 deletions(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 2fbbcb58c..f79515198 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -4539,13 +4539,21 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) int spell_level = 0; int lvldiff = 0; uint32 effect = 0; - int32 base_value = 0; - int32 limit_value = 0; + int32 base_value = 0; + int32 limit_value = 0; uint32 slot = 0; int index_id = -1; uint32 focus_reuse_time = 0; + bool is_from_item_click = false; + bool try_apply_to_item_click = false; + bool has_item_limit_check = false; + + if (casting_spell_inventory_slot && casting_spell_inventory_slot != -1) { + is_from_item_click = true; + } + bool LimitFailure = false; bool LimitInclude[MaxLimitInclude] = {false}; /* Certain limits require only one of several Include conditions to be true. Ie. Add damage to fire OR ice @@ -4903,6 +4911,7 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) break; case SE_FFItemClass: + has_item_limit_check = true; if (casting_spell_inventory_slot && casting_spell_inventory_slot != -1) { if (IsClient() && casting_spell_slot == EQ::spells::CastingSlot::Item && casting_spell_inventory_slot != 0xFFFFFFFF) { auto item = CastToClient()->GetInv().GetItem(casting_spell_inventory_slot); @@ -4995,18 +5004,21 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) case SE_IncreaseSpellHaste: if (type == focusSpellHaste && base_value > value) { value = base_value; + try_apply_to_item_click = is_from_item_click ? true : false; } break; case SE_Fc_CastTimeMod2: if (type == focusFcCastTimeMod2 && base_value > value) { value = base_value; + try_apply_to_item_click = is_from_item_click ? true : false; } break; case SE_Fc_CastTimeAmt: if (type == focusFcCastTimeAmt && base_value > value) { value = base_value; + try_apply_to_item_click = is_from_item_click ? true : false; } break; @@ -5081,6 +5093,7 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) case SE_ReduceReuseTimer: if (type == focusReduceRecastTime) { value = base_value / 1000; + try_apply_to_item_click = is_from_item_click ? true : false; } break; @@ -5247,6 +5260,10 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) } } + if (try_apply_to_item_click && !has_item_limit_check) { + return 0; + } + if (LimitFailure) { return 0; } @@ -5290,6 +5307,14 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo uint32 Caston_spell_id = 0; int index_id = -1; uint32 focus_reuse_time = 0; //If this is set and all limits pass, start timer at end of script. + + bool is_from_item_click = false; + bool try_apply_to_item_click = false; + bool has_item_limit_check = false; + + if (casting_spell_inventory_slot && casting_spell_inventory_slot != -1) { + is_from_item_click = true; + } bool LimitInclude[MaxLimitInclude] = {false}; /* Certain limits require only one of several Include conditions to be true. Determined by limits being negative or positive @@ -5638,14 +5663,14 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo Limits focuses to check if cast from item clicks. Can be used to INCLUDE or EXCLUDE items by ItemType and/or SubType and/or Slots Not used on live, going on information we have plus implemented as broadly as possible to allow all possible options. base = item table field 'ItemType' Limit = item table field 'SubType' Max = item table field 'Slots' (this is slot bitmask) - + When including: Setting base, limit, max respectively to -1 will cause it to ignore that check, letting any type or slot ect be used. - - Special rules for excluding. base value needs to be negative < -1, if excluding all ItemTypes set to -1000. + + Special rules for excluding. base value needs to be negative < -1, if excluding all ItemTypes set to -1000. For SubType and Slots set using same rules above as for includes. Ie. -1 for all, positive for specifics To exclude a specific ItemType we have to do some math. The exclude value will be the negative value of (ItemType + 100). If ItemType = 10, then SET ItemType= -110 to exclude. If its ItemType 0, then SET ItemType= -100 to exclude ect. Not ideal but it works. - + Usage example: [INCLUDE] Only focus spell if from click cast and is a 'defense armor' item type=10 [base= 10, limit= -1, max= -1] Usage example: [INCLUDE] Only focus spell if from click cast and is from helmet slot' slots= 4 [base= -1, limit= -1, max= 4] Usage example: [EXCLUDE] Do not focus spell if it is from an item click. [base= -1000, limit= -1, max= -1] @@ -5653,9 +5678,9 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo Usage example: [EXCLUDE] Do not focus spell if it is from an item click and is a 'defense armor' item type=10. [base= -110, limit= -1, max= -1] Note: You can apply multiple includes or excludes to a single focus spell, using multiple SPA 415 limits in the spell. Ie. Check for clicks from ItemType 10 or 11. - + */ - + has_item_limit_check = true; if (casting_spell_inventory_slot && casting_spell_inventory_slot != -1) { if (IsClient() && casting_spell_slot == EQ::spells::CastingSlot::Item && casting_spell_inventory_slot != 0xFFFFFFFF) { auto item = CastToClient()->GetInv().GetItem(casting_spell_inventory_slot); @@ -5756,18 +5781,21 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_IncreaseSpellHaste: if (type == focusSpellHaste && focus_spell.base_value[i] > value) { value = focus_spell.base_value[i]; + try_apply_to_item_click = is_from_item_click ? true : false; } break; case SE_Fc_CastTimeMod2: if (type == focusFcCastTimeMod2 && focus_spell.base_value[i] > value) { value = focus_spell.base_value[i]; + try_apply_to_item_click = is_from_item_click ? true : false; } break; case SE_Fc_CastTimeAmt: if (type == focusFcCastTimeAmt && focus_spell.base_value[i] > value) { value = focus_spell.base_value[i]; + try_apply_to_item_click = is_from_item_click ? true : false; } break; @@ -5828,6 +5856,7 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_ReduceReuseTimer: if (type == focusReduceRecastTime) { value = focus_spell.base_value[i] / 1000; + try_apply_to_item_click = is_from_item_click ? true : false; } break; @@ -6011,6 +6040,15 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo return 0; } } + + /* + For item click cast/recast focus modifiers. Only use if SPA 415 exists. + This is an item click but does not have SPA 415 limiter. Fail here. + */ + + if (try_apply_to_item_click && !has_item_limit_check) { + return 0; + } if (Caston_spell_id) { if (IsValidSpell(Caston_spell_id) && (Caston_spell_id != spell_id)) { diff --git a/zone/spells.cpp b/zone/spells.cpp index 4d6446fc5..386963949 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -417,14 +417,21 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, target_id = GetID(); } - if(cast_time <= -1) { + if (cast_time <= -1) { // save the non-reduced cast time to use in the packet cast_time = orgcasttime = spell.cast_time; // if there's a cast time, check if they have a modifier for it - if(cast_time) { + if (cast_time) { cast_time = GetActSpellCasttime(spell_id, cast_time); } } + //must use SPA 415 with focus (SPA 127/500/501) to reduce item recast + else if (cast_time && IsClient() && slot == CastingSlot::Item && item_slot != 0xFFFFFFFF) { + orgcasttime = cast_time; + if (cast_time) { + cast_time = GetActSpellCasttime(spell_id, cast_time); + } + } else orgcasttime = cast_time; @@ -2543,6 +2550,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui recast -= GetAA(aaTouchoftheWicked) * 420; } int reduction = CastToClient()->GetFocusEffect(focusReduceRecastTime, spell_id);//Client only + if(reduction) recast -= reduction; @@ -2556,20 +2564,32 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui EQ::ItemInstance *itm = CastToClient()->GetInv().GetItem(inventory_slot); if(itm && itm->GetItem()->RecastDelay > 0){ auto recast_type = itm->GetItem()->RecastType; - CastToClient()->GetPTimers().Start((pTimerItemStart + recast_type), itm->GetItem()->RecastDelay); - if (recast_type != -1) { - database.UpdateItemRecastTimestamps( - CastToClient()->CharacterID(), - recast_type, - CastToClient()->GetPTimers().Get(pTimerItemStart + recast_type)->GetReadyTimestamp() - ); + int recast_delay = itm->GetItem()->RecastDelay; + //must use SPA 415 with focus (SPA 310) to reduce item recast + int reduction = CastToClient()->GetFocusEffect(focusReduceRecastTime, spell_id); + if (reduction) { + recast_delay -= reduction; + } + recast_delay = std::max(recast_delay, 0); + + if (recast_delay > 0) { + + CastToClient()->GetPTimers().Start((pTimerItemStart + recast_type), recast_delay); + if (recast_type != -1) { + database.UpdateItemRecastTimestamps( + CastToClient()->CharacterID(), + recast_type, + CastToClient()->GetPTimers().Get(pTimerItemStart + recast_type)->GetReadyTimestamp() + ); + } + + auto outapp = new EQApplicationPacket(OP_ItemRecastDelay, sizeof(ItemRecastDelay_Struct)); + ItemRecastDelay_Struct *ird = (ItemRecastDelay_Struct *)outapp->pBuffer; + ird->recast_delay = static_cast(recast_delay); + ird->recast_type = recast_type; + CastToClient()->QueuePacket(outapp); + safe_delete(outapp); } - auto outapp = new EQApplicationPacket(OP_ItemRecastDelay, sizeof(ItemRecastDelay_Struct)); - ItemRecastDelay_Struct *ird = (ItemRecastDelay_Struct *)outapp->pBuffer; - ird->recast_delay = itm->GetItem()->RecastDelay; - ird->recast_type = recast_type; - CastToClient()->QueuePacket(outapp); - safe_delete(outapp); } } From 33c30d3cbbb3db2feda5a1c603cd988ea3df7bac Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 10 Nov 2021 21:27:51 -0500 Subject: [PATCH 354/624] [Bug Fix] Fix for dual wield animation when same delay weapons. (#1671) * DW animation fix * spelling * better animation looks better for low skill where dw doesn't fire as often. * Update attack.cpp --- zone/attack.cpp | 35 ++++++++++++++++++++++++++++++++++- zone/mob.cpp | 1 + zone/mob.h | 3 +++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index c869792a7..abe667b04 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -140,9 +140,25 @@ EQ::skills::SkillType Mob::AttackAnimation(int Hand, const EQ::ItemInstance* wea } // If we're attacking with the secondary hand, play the dual wield anim - if (Hand == EQ::invslot::slotSecondary) // DW anim + if (Hand == EQ::invslot::slotSecondary) {// DW anim type = animDualWield; + //allow animation chance to fire to be similar to your dw chance + if (GetDualWieldingSameDelayWeapons() == 2) { + SetDualWieldingSameDelayWeapons(3); + } + } + + //If both weapons have same delay this allows a chance for DW animation + if (GetDualWieldingSameDelayWeapons() && Hand == EQ::invslot::slotPrimary) { + + if (GetDualWieldingSameDelayWeapons() == 3 && zone->random.Roll(50)) { + type = animDualWield; + SetDualWieldingSameDelayWeapons(2);//Don't roll again till you do another dw attack. + } + SetDualWieldingSameDelayWeapons(2);//Ensures first attack is always primary. + } + DoAnim(type, 0, false); return skillinuse; @@ -5541,6 +5557,8 @@ void Mob::SetAttackTimer() void Client::SetAttackTimer() { float haste_mod = GetHaste() * 0.01f; + int primary_speed = 0; + int secondary_speed = 0; //default value for attack timer in case they have //an invalid weapon equipped: @@ -5618,6 +5636,21 @@ void Client::SetAttackTimer() speed = static_cast(speed + ((hhe / 100.0f) * delay)); } TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true, true); + + if (i == EQ::invslot::slotPrimary) { + primary_speed = speed; + } + else if (i == EQ::invslot::slotSecondary) { + secondary_speed = speed; + } + } + + //To allow for duel wield animation to display correctly if both weapons have same delay + if (primary_speed == secondary_speed) { + SetDualWieldingSameDelayWeapons(1); + } + else { + SetDualWieldingSameDelayWeapons(0); } } diff --git a/zone/mob.cpp b/zone/mob.cpp index 74fb9d1f0..2276275ee 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -487,6 +487,7 @@ Mob::Mob( npc_assist_cap = 0; use_double_melee_round_dmg_bonus = false; + dw_same_delay = 0; #ifdef BOTS m_manual_follow = false; diff --git a/zone/mob.h b/zone/mob.h index ec7a84700..a3a664f1f 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -852,6 +852,8 @@ public: int GetHealRate() const { return itembonuses.HealRate + spellbonuses.HealRate + aabonuses.HealRate; } int GetMemoryBlurChance(int base_chance); inline bool HasBaseEffectFocus() const { return (spellbonuses.FocusEffects[focusFcBaseEffects] || aabonuses.FocusEffects[focusFcBaseEffects] || itembonuses.FocusEffects[focusFcBaseEffects]); } + int32 GetDualWieldingSameDelayWeapons() const { return dw_same_delay; } + inline void SetDualWieldingSameDelayWeapons(int32 val) { dw_same_delay = val; } bool TryDoubleMeleeRoundEffect(); bool GetUseDoubleMeleeRoundDmgBonus() const { return use_double_melee_round_dmg_bonus; } @@ -1467,6 +1469,7 @@ protected: int16 slow_mitigation; // Allows for a slow mitigation (100 = 100%, 50% = 50%) Timer tic_timer; Timer mana_timer; + int32 dw_same_delay; Timer focusproclimit_timer[MAX_FOCUS_PROC_LIMIT_TIMERS]; //SPA 511 int32 focusproclimit_spellid[MAX_FOCUS_PROC_LIMIT_TIMERS]; //SPA 511 From 994ef712b2aa6e2d952b36d0fb6f8fc6fd7a21bf Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 11 Nov 2021 16:48:35 -0500 Subject: [PATCH 355/624] [Commands] Cleanup #cast Command. (#1706) * [Commands] Cleanup #cast Command. - Cleanup message. * Add optional cast non-instant parameter. - Fix cast time. * Fix message. --- zone/command.cpp | 77 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 22 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 49952eaf1..fe87b200e 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -182,7 +182,7 @@ int command_init(void) #endif command_add("camerashake", "Shakes the camera on everyone's screen globally.", 80, command_camerashake) || - command_add("castspell", "[spellid] - Cast a spell", 50, command_castspell) || + command_add("castspell", "[Spell ID] [Instant (0 = False, 1 = True, Default is 1 if Unused)] - Cast a spell", 50, command_castspell) || command_add("chat", "[channel num] [message] - Send a channel message to all zones", 200, command_chat) || command_add("checklos", "- Check for line of sight to your target", 50, command_checklos) || command_add("copycharacter", "[source_char_name] [dest_char_name] [dest_account_name] Copies character to destination account", 250, command_copycharacter) || @@ -3638,28 +3638,61 @@ inline bool CastRestrictedSpell(int spellid) void command_castspell(Client *c, const Seperator *sep) { - if (!sep->IsNumber(1)) - c->Message(Chat::White, "Usage: #CastSpell spellid"); - else { - uint16 spellid = atoi(sep->arg[1]); - /* - Spell restrictions. - */ - if (CastRestrictedSpell(spellid) && c->Admin() < commandCastSpecials) + if (SPDAT_RECORDS <= 0) { + c->Message(Chat::White, "Spells not loaded."); + return; + } + + Mob *target = c; + if(c->GetTarget()) { + target = c->GetTarget(); + } + + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #castspell [Spell ID] [Instant (0 = False, 1 = True, Default is 1 if Unused)]"); + } else { + uint16 spell_id = std::stoul(sep->arg[1]); + + if (CastRestrictedSpell(spell_id) && c->Admin() < commandCastSpecials) { c->Message(Chat::Red, "Unable to cast spell."); - else if (spellid >= SPDAT_RECORDS) - c->Message(Chat::White, "Error: #CastSpell: Argument out of range"); - else - if (c->GetTarget() == 0) - if(c->Admin() >= commandInstacast) - c->SpellFinished(spellid, 0, EQ::spells::CastingSlot::Item, 0, -1, spells[spellid].resist_difficulty); - else - c->CastSpell(spellid, 0, EQ::spells::CastingSlot::Item, 0); - else - if(c->Admin() >= commandInstacast) - c->SpellFinished(spellid, c->GetTarget(), EQ::spells::CastingSlot::Item, 0, -1, spells[spellid].resist_difficulty); - else - c->CastSpell(spellid, c->GetTarget()->GetID(), EQ::spells::CastingSlot::Item, 0); + } else if (spell_id >= SPDAT_RECORDS) { + c->Message(Chat::White, "Invalid Spell ID."); + } else { + bool instant_cast = (c->Admin() >= commandInstacast ? true : false); + if (instant_cast && sep->IsNumber(2)) { + instant_cast = std::stoi(sep->arg[2]) ? true : false; + c->Message(Chat::White, fmt::format("{}", std::stoi(sep->arg[2])).c_str()); + } + + if (c->Admin() >= commandInstacast && instant_cast) { + c->SpellFinished(spell_id, target, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty); + } else { + c->CastSpell(spell_id, target->GetID(), EQ::spells::CastingSlot::Item, spells[spell_id].cast_time); + } + + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "Cast {} ({}) on {}{}.", + GetSpellName(spell_id), + spell_id, + target->GetCleanName(), + instant_cast ? " instantly" : "" + ).c_str() + ); + } else { + c->Message( + Chat::White, + fmt::format( + "Cast {} ({}) on yourself{}.", + GetSpellName(spell_id), + spell_id, + instant_cast ? " instantly" : "" + ).c_str() + ); + } + } } } From fa0706446640415e3f20ef5b2a0bca4a98fa429b Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 11 Nov 2021 16:48:50 -0500 Subject: [PATCH 356/624] [Commands] Cleanup #cvs Command. (#1709) * [Commands] Cleanup #cvs Command. - Cleanup message and display. - Add Total Clients to message. - Add Unique IPs to message. * Formatting. * Formatting. --- world/clientlist.cpp | 104 +++++++++++++++++++++---------------------- zone/command.cpp | 22 ++++----- 2 files changed, 58 insertions(+), 68 deletions(-) diff --git a/world/clientlist.cpp b/world/clientlist.cpp index 7a7dfe408..7ce779130 100644 --- a/world/clientlist.cpp +++ b/world/clientlist.cpp @@ -1343,71 +1343,67 @@ void ClientList::GetClients(const char *zone_name, std::vector unique_ips; + std::map client_count = { + { EQ::versions::ClientVersion::Titanium, 0 }, + { EQ::versions::ClientVersion::SoF, 0 }, + { EQ::versions::ClientVersion::SoD, 0 }, + { EQ::versions::ClientVersion::UF, 0 }, + { EQ::versions::ClientVersion::RoF, 0 }, + { EQ::versions::ClientVersion::RoF2, 0 } + }; LinkedListIterator Iterator(clientlist); - Iterator.Reset(); - - while(Iterator.MoreElements()) - { + while (Iterator.MoreElements()) { ClientListEntry* CLE = Iterator.GetData(); + if (CLE && CLE->zone()) { + auto client_version = CLE->GetClientVersion(); + if ( + client_version >= (uint8) EQ::versions::ClientVersion::Titanium && + client_version <= (uint8) EQ::versions::ClientVersion::RoF2 + ) { + client_count[(EQ::versions::ClientVersion)client_version]++; + } - if(CLE && CLE->zone()) - { - switch(CLE->GetClientVersion()) - { - case 1: - { - break; - } - case 2: - { - ++ClientTitaniumCount; - break; - } - case 3: - { - ++ClientSoFCount; - break; - } - case 4: - { - ++ClientSoDCount; - break; - } - case 5: - { - ++ClientUnderfootCount; - break; - } - case 6: - { - ++ClientRoFCount; - break; - } - case 7: - { - ++ClientRoF2Count; - break; - } - default: - break; + if (std::find(unique_ips.begin(), unique_ips.begin(), CLE->GetIP()) == unique_ips.end()) { + unique_ips.push_back(CLE->GetIP()); } } Iterator.Advance(); - } - zoneserver_list.SendEmoteMessage(Name, 0, 0, 13, "There are %i Titanium, %i SoF, %i SoD, %i UF, %i RoF, %i RoF2 clients currently connected.", - ClientTitaniumCount, ClientSoFCount, ClientSoDCount, ClientUnderfootCount, ClientRoFCount, ClientRoF2Count); + uint32 total_clients = ( + client_count[EQ::versions::ClientVersion::Titanium] + + client_count[EQ::versions::ClientVersion::SoF] + + client_count[EQ::versions::ClientVersion::SoD] + + client_count[EQ::versions::ClientVersion::UF] + + client_count[EQ::versions::ClientVersion::RoF] + + client_count[EQ::versions::ClientVersion::RoF2] + ); + + zoneserver_list.SendEmoteMessage( + Name, + 0, + 0, + Chat::White, + fmt::format( + "There {} {} Titanium, {} SoF, {} SoD, {} UF, {} RoF, and {} RoF2 Client{} currently connected for a total of {} Client{} and {} Unique IP{} connected.", + (total_clients != 1 ? "are" : "is"), + client_count[EQ::versions::ClientVersion::Titanium], + client_count[EQ::versions::ClientVersion::SoF], + client_count[EQ::versions::ClientVersion::SoD], + client_count[EQ::versions::ClientVersion::UF], + client_count[EQ::versions::ClientVersion::RoF], + client_count[EQ::versions::ClientVersion::RoF2], + (total_clients != 1 ? "s" : ""), + total_clients, + (total_clients != 1 ? "s" : ""), + unique_ips.size(), + (unique_ips.size() != 1 ? "s" : "") + ).c_str() + ); } void ClientList::OnTick(EQ::Timer *t) diff --git a/zone/command.cpp b/zone/command.cpp index fe87b200e..8b449d1e9 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -13778,20 +13778,14 @@ void command_door(Client *c, const Seperator *sep) { void command_cvs(Client *c, const Seperator *sep) { - if(c) - { - auto pack = - new ServerPacket(ServerOP_ClientVersionSummary, sizeof(ServerRequestClientVersionSummary_Struct)); - - ServerRequestClientVersionSummary_Struct *srcvss = (ServerRequestClientVersionSummary_Struct*)pack->pBuffer; - - strn0cpy(srcvss->Name, c->GetName(), sizeof(srcvss->Name)); - - worldserver.SendPacket(pack); - - safe_delete(pack); - - } + auto pack = new ServerPacket( + ServerOP_ClientVersionSummary, + sizeof(ServerRequestClientVersionSummary_Struct) + ); + auto srcvss = (ServerRequestClientVersionSummary_Struct*)pack->pBuffer; + strn0cpy(srcvss->Name, c->GetName(), sizeof(srcvss->Name)); + worldserver.SendPacket(pack); + safe_delete(pack); } void command_max_all_skills(Client *c, const Seperator *sep) From 239033a269d6bd1afdf40b3dbcd9cca5effcfb19 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 11 Nov 2021 18:32:16 -0500 Subject: [PATCH 357/624] [Bug Fix] Prevent critical DOTs from affecting beneficial damage over time (#1710) * no critical from lich * better --- zone/effects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index 0d9c412d0..26e77c14e 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -212,7 +212,7 @@ int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) { if (spells[spell_id].override_crit_chance > 0 && chance > spells[spell_id].override_crit_chance) chance = spells[spell_id].override_crit_chance; - if (chance > 0 && (zone->random.Roll(chance))) { + if (!spells[spell_id].good_effect && chance > 0 && (zone->random.Roll(chance))) { int32 ratio = 200; ratio += itembonuses.DotCritDmgIncrease + spellbonuses.DotCritDmgIncrease + aabonuses.DotCritDmgIncrease; value = base_value*ratio/100; From 17c8e8414cedebc5879119e3f13f32e07ca2008c Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 11 Nov 2021 20:27:50 -0500 Subject: [PATCH 358/624] [Spells] Fixed proc rate for Ranged procs (#1715) --- zone/attack.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index abe667b04..c5fa1d3fb 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -4136,7 +4136,7 @@ void Mob::TryDefensiveProc(Mob *on, uint16 hand) { float ProcChance, ProcBonus; on->GetDefensiveProcChances(ProcBonus, ProcChance, hand, this); - if (hand != EQ::invslot::slotPrimary) { + if (hand == EQ::invslot::slotSecondary) { ProcChance /= 2; } @@ -4238,7 +4238,7 @@ void Mob::TryWeaponProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon ProcBonus += static_cast(itembonuses.ProcChance) / 10.0f; // Combat Effects float ProcChance = GetProcChances(ProcBonus, hand); - if (hand != EQ::invslot::slotPrimary) //Is Archery intened to proc at 50% rate? + if (hand == EQ::invslot::slotSecondary) ProcChance /= 2; // Try innate proc on weapon @@ -4321,7 +4321,7 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon, float ProcChance = 0.0f; ProcChance = GetProcChances(ProcBonus, hand); - if (hand != EQ::invslot::slotPrimary) //Is Archery intened to proc at 50% rate? + if (hand == EQ::invslot::slotSecondary) ProcChance /= 2; bool rangedattk = false; @@ -5216,7 +5216,7 @@ float Mob::GetSkillProcChances(uint16 ReuseTime, uint16 hand) { if (!ReuseTime && hand) { weapon_speed = GetWeaponSpeedbyHand(hand); ProcChance = static_cast(weapon_speed) * (RuleR(Combat, AvgProcsPerMinute) / 60000.0f); - if (hand != EQ::invslot::slotPrimary) + if (hand == EQ::invslot::slotSecondary) ProcChance /= 2; } From acf5836253a9e4bc3a2698c4d9e35a523272f16f Mon Sep 17 00:00:00 2001 From: mmcgarvey Date: Thu, 11 Nov 2021 20:37:14 -0500 Subject: [PATCH 359/624] [Rules] Add optional rule for lifetap heals (#1689) What: Add toggle for compounding bonuses for lifetap heals. Why: When spell damage and heal amount bonuses are scaled to ludicrous levels, this double dip results in very high heals from relatively weak lifetaps. Created new rule: Spells:CompoundLifetapHeals If true (default): Apply spell damage bonuses to lifetap damage Pass that amount through heal bonuses Heal for this resulting amount If false: Apply spell damage bonuses to lifetap damage Heal for this resulting amount --- common/ruletypes.h | 1 + zone/attack.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 5be339c17..2def8abb8 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -387,6 +387,7 @@ RULE_BOOL(Spells, InvisRequiresGroup, false, "Invis requires the the target to b RULE_INT(Spells, ClericInnateHealFocus, 5, "Clerics on live get a 5 pct innate heal focus") RULE_BOOL(Spells, DOTsScaleWithSpellDmg, false, "Allow SpellDmg stat to affect DoT spells") RULE_BOOL(Spells, HOTsScaleWithHealAmt, false, "Allow HealAmt stat to affect HoT spells") +RULE_BOOL(Spells, CompoundLifetapHeals, true, "True: Lifetap heals calculate damage bonuses and then heal bonuses. False: Lifetaps heal using the amount damaged to mob.") RULE_CATEGORY_END() RULE_CATEGORY(Combat) diff --git a/zone/attack.cpp b/zone/attack.cpp index c5fa1d3fb..c1a46fbac 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3613,7 +3613,7 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const if (spell_id != SPELL_UNKNOWN && IsLifetapSpell(spell_id)) { int healed = damage; - healed = attacker->GetActSpellHealing(spell_id, healed); + healed = RuleB(Spells, CompoundLifetapHeals) ? attacker->GetActSpellHealing(spell_id, healed) : healed; LogCombat("Applying lifetap heal of [{}] to [{}]", healed, attacker->GetName()); attacker->HealDamage(healed); From e4bd6f5bd22c8f276e12888d888ef198ce2ae067 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 11 Nov 2021 17:37:35 -0800 Subject: [PATCH 360/624] [Networking] Servertalk Legacy World Connections for Login (#1662) * servertalk server connections will now attempt to parse legacy connections as well as modern ones * Some fixes for legacy connections * Change legacy default from local to eqemu --- common/net/servertalk_server_connection.cpp | 173 ++++++++++++++++++-- common/net/servertalk_server_connection.h | 3 + 2 files changed, 164 insertions(+), 12 deletions(-) diff --git a/common/net/servertalk_server_connection.cpp b/common/net/servertalk_server_connection.cpp index 0942c9d50..f3823cda6 100644 --- a/common/net/servertalk_server_connection.cpp +++ b/common/net/servertalk_server_connection.cpp @@ -11,6 +11,7 @@ EQ::Net::ServertalkServerConnection::ServertalkServerConnection(std::shared_ptr< m_connection->OnRead(std::bind(&ServertalkServerConnection::OnRead, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); m_connection->OnDisconnect(std::bind(&ServertalkServerConnection::OnDisconnect, this, std::placeholders::_1)); m_connection->Start(); + m_legacy_mode = false; } EQ::Net::ServertalkServerConnection::~ServertalkServerConnection() @@ -19,17 +20,73 @@ EQ::Net::ServertalkServerConnection::~ServertalkServerConnection() void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet & p) { - // pad zero size packets - if (p.Length() == 0) { - p.PutUInt8(0, 0); + if (m_legacy_mode) { + if (!m_connection) + return; + + if (opcode == ServerOP_UsertoWorldReq) { + auto req_in = (UsertoWorldRequest_Struct*)p.Data(); + + EQ::Net::DynamicPacket req; + size_t i = 0; + req.PutUInt32(i, req_in->lsaccountid); i += 4; + req.PutUInt32(i, req_in->worldid); i += 4; + req.PutUInt32(i, req_in->FromID); i += 4; + req.PutUInt32(i, req_in->ToID); i += 4; + req.PutData(i, req_in->IPAddr, 64); i += 64; + + EQ::Net::DynamicPacket out; + out.PutUInt16(0, ServerOP_UsertoWorldReqLeg); + out.PutUInt16(2, req.Length() + 4); + out.PutPacket(4, req); + + m_connection->Write((const char*)out.Data(), out.Length()); + return; + } + + if (opcode == ServerOP_LSClientAuth) { + auto req_in = (ClientAuth_Struct*)p.Data(); + + EQ::Net::DynamicPacket req; + size_t i = 0; + req.PutUInt32(i, req_in->loginserver_account_id); i += 4; + req.PutData(i, req_in->account_name, 30); i += 30; + req.PutData(i, req_in->key, 30); i += 30; + req.PutUInt8(i, req_in->lsadmin); i += 1; + req.PutUInt16(i, req_in->is_world_admin); i += 2; + req.PutUInt32(i, req_in->ip); i += 4; + req.PutUInt8(i, req_in->is_client_from_local_network); i += 1; + + EQ::Net::DynamicPacket out; + out.PutUInt16(0, ServerOP_LSClientAuthLeg); + out.PutUInt16(2, req.Length() + 4); + out.PutPacket(4, req); + + m_connection->Write((const char*)out.Data(), out.Length()); + return; + } + + EQ::Net::DynamicPacket out; + out.PutUInt16(0, opcode); + out.PutUInt16(2, p.Length() + 4); + out.PutPacket(4, p); + + m_connection->Write((const char*)out.Data(), out.Length()); + } else { + // pad zero size packets + // pad packets that would cause a collision with legacy identification code + // It's unlikely we'd send a 4MB msg for any reason but just incase. + if (p.Length() == 0 || p.Length() == 43061256) { + p.PutUInt8(0, 0); + } + + EQ::Net::DynamicPacket out; + out.PutUInt32(0, p.Length()); + out.PutUInt16(4, opcode); + out.PutPacket(6, p); + + InternalSend(ServertalkMessage, out); } - - EQ::Net::DynamicPacket out; - out.PutUInt32(0, p.Length()); - out.PutUInt16(4, opcode); - out.PutPacket(6, p); - - InternalSend(ServertalkMessage, out); } void EQ::Net::ServertalkServerConnection::SendPacket(ServerPacket *p) @@ -54,17 +111,41 @@ void EQ::Net::ServertalkServerConnection::OnMessage(std::functionConnectionIdentified(this); + ProcessOldReadBuffer(); + return; + } + /* //header: //uint32 length; @@ -129,6 +210,57 @@ void EQ::Net::ServertalkServerConnection::ProcessReadBuffer() } } +void EQ::Net::ServertalkServerConnection::ProcessOldReadBuffer() +{ + size_t current = 0; + size_t total = m_buffer.size(); + + while (current < total) { + auto left = total - current; + + /* + //header: + //uint32 length; + //uint8 type; + */ + uint16_t length = 0; + uint16_t opcode = 0; + if (left < 4) { + break; + } + + opcode = *(uint16_t*)&m_buffer[current]; + length = *(uint16_t*)&m_buffer[current + 2]; + if (length < 4) { + break; + } + + length -= 4; + + if (current + 4 + length > total) { + break; + } + + if (length == 0) { + EQ::Net::DynamicPacket p; + ProcessMessageOld(opcode, p); + } + else { + EQ::Net::StaticPacket p(&m_buffer[current + 4], length); + ProcessMessageOld(opcode, p); + } + + current += length + 4; + } + + if (current == total) { + m_buffer.clear(); + } + else { + m_buffer.erase(m_buffer.begin(), m_buffer.begin() + current); + } +} + void EQ::Net::ServertalkServerConnection::OnDisconnect(TCPConnection *c) { m_parent->ConnectionDisconnected(this); @@ -144,7 +276,7 @@ void EQ::Net::ServertalkServerConnection::SendHello() void EQ::Net::ServertalkServerConnection::InternalSend(ServertalkPacketType type, EQ::Net::Packet &p) { - if (!m_connection) + if (!m_connection || m_legacy_mode) return; EQ::Net::DynamicPacket out; @@ -201,3 +333,20 @@ void EQ::Net::ServertalkServerConnection::ProcessMessage(EQ::Net::Packet &p) LogError("Error parsing message from client: {0}", ex.what()); } } + +void EQ::Net::ServertalkServerConnection::ProcessMessageOld(uint16_t opcode, EQ::Net::Packet &p) +{ + try { + auto cb = m_message_callbacks.find(opcode); + if (cb != m_message_callbacks.end()) { + cb->second(opcode, p); + } + + if (m_message_callback) { + m_message_callback(opcode, p); + } + } + catch (std::exception &ex) { + LogError("Error parsing legacy message from client: {0}", ex.what()); + } +} diff --git a/common/net/servertalk_server_connection.h b/common/net/servertalk_server_connection.h index f8110f51a..8e262baa1 100644 --- a/common/net/servertalk_server_connection.h +++ b/common/net/servertalk_server_connection.h @@ -27,11 +27,13 @@ namespace EQ private: void OnRead(TCPConnection* c, const unsigned char* data, size_t sz); void ProcessReadBuffer(); + void ProcessOldReadBuffer(); void OnDisconnect(TCPConnection* c); void SendHello(); void InternalSend(ServertalkPacketType type, EQ::Net::Packet &p); void ProcessHandshake(EQ::Net::Packet &p); void ProcessMessage(EQ::Net::Packet &p); + void ProcessMessageOld(uint16_t opcode, EQ::Net::Packet &p); std::shared_ptr m_connection; ServertalkServer *m_parent; @@ -41,6 +43,7 @@ namespace EQ std::function m_message_callback; std::string m_identifier; std::string m_uuid; + bool m_legacy_mode; }; } } From 65197ac027c1f4941e46f103bed86fd46da69736 Mon Sep 17 00:00:00 2001 From: cybernine186 <91032199+cybernine186@users.noreply.github.com> Date: Thu, 11 Nov 2021 20:41:03 -0500 Subject: [PATCH 361/624] [Rules] Gate /tgb, /autofire and /melody (#1679) * Rules to negate /tgb, /autofire, and /melody Created new rules to negate server and client side effects of commands: /tgb, /autofire, and /melody. These commands are enabled by default and can be disabled to enforce a classic EQ experience if using progression style play for example. Rules -------------- RULE_BOOL(Character, EnableBardMelody, true, "Enable Bard /melody by default, to disable change to false for a classic experience.") RULE_BOOL(Character, EnableRangerAutoFire, true, "Enable Ranger /autofire by default, to disable change to false for a classic experience.") RULE_BOOL(Character, EnableTGB, true, "Enable /tgb (Target Group Buff) by default, to disable change to false for a classic experience.") * Removed sql query for rules per Mackal recommendation. --- common/ruletypes.h | 3 +++ zone/client_packet.cpp | 3 +++ zone/client_process.cpp | 5 +++++ zone/spells.cpp | 3 ++- 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 2def8abb8..3ee4b49c8 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -171,6 +171,9 @@ RULE_BOOL(Character, EnableTestBuff, false, "Allow the use of /testbuff") RULE_BOOL(Character, UseResurrectionSickness, true, "Use Resurrection Sickness based on Resurrection spell cast, set to false to disable Resurrection Sickness.") RULE_INT(Character, OldResurrectionSicknessSpellID, 757, "757 is Default Old Resurrection Sickness Spell ID") RULE_INT(Character, ResurrectionSicknessSpellID, 756, "756 is Default Resurrection Sickness Spell ID") +RULE_BOOL(Character, EnableBardMelody, true, "Enable Bard /melody by default, to disable change to false for a classic experience.") +RULE_BOOL(Character, EnableRangerAutoFire, true, "Enable Ranger /autofire by default, to disable change to false for a classic experience.") +RULE_BOOL(Character, EnableTGB, true, "Enable /tgb (Target Group Buff) by default, to disable change to false for a classic experience.") RULE_CATEGORY_END() RULE_CATEGORY(Mercs) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 6fa07623f..2372dd31c 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -3388,6 +3388,9 @@ void Client::Handle_OP_AutoFire(const EQApplicationPacket *app) } bool *af = (bool*)app->pBuffer; auto_fire = *af; + if(!RuleB(Character, EnableRangerAutoFire)) + auto_fire = false; + auto_attack = false; SetAttackTimer(); } diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 97dfdb012..d4108a819 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -1026,6 +1026,11 @@ void Client::OPTGB(const EQApplicationPacket *app) { if(!app) return; if(!app->pBuffer) return; + + if(!RuleB(Character, EnableTGB)) + { + return; + } uint32 tgb_flag = *(uint32 *)app->pBuffer; if(tgb_flag == 2) diff --git a/zone/spells.cpp b/zone/spells.cpp index 386963949..73b9b49bc 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1479,7 +1479,8 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo } if (spells[spell_id].timer_id > 0 && slot < CastingSlot::MaxGems) c->SetLinkedSpellReuseTimer(spells[spell_id].timer_id, spells[spell_id].recast_time / 1000); - c->MemorizeSpell(static_cast(slot), spell_id, memSpellSpellbar); + if(RuleB(Spells, EnableBardMelody)) + c->MemorizeSpell(static_cast(slot), spell_id, memSpellSpellbar); } LogSpells("Bard song [{}] should be started", spell_id); } From 099759c47758b78a406eba2c10c6c6aa1bf9c95c Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 11 Nov 2021 20:41:59 -0500 Subject: [PATCH 362/624] [Commands] #tune command rewrite (#1677) * tune updates * Update tune.cpp * tune update * updates * updates * less zero * update * up * u * Update tune.cpp * Update tune.cpp * avoidance working * accuracy * save1 * Update tune.cpp * override * Removed Old Tune Code * cleanup1 * up * finished v1 * Update command.cpp * Update command.cpp * spellfix * Update command.cpp * remove test command * added SYNC comments Hopefully if anyone changes these functions they will change the corresponding tune * Tune_ to Tune Co-authored-by: Akkadius --- zone/attack.cpp | 21 +- zone/client.h | 4 - zone/command.cpp | 300 ++++--- zone/mob.h | 42 +- zone/tune.cpp | 2137 ++++++++++++++++++++++++++-------------------- 5 files changed, 1478 insertions(+), 1026 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index c1a46fbac..071ae327e 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -59,6 +59,7 @@ extern FastMath g_Math; extern EntityList entity_list; extern Zone* zone; +//SYNC WITH: tune.cpp, mob.h TuneAttackAnimation EQ::skills::SkillType Mob::AttackAnimation(int Hand, const EQ::ItemInstance* weapon, EQ::skills::SkillType skillinuse) { // Determine animation @@ -163,7 +164,7 @@ EQ::skills::SkillType Mob::AttackAnimation(int Hand, const EQ::ItemInstance* wea return skillinuse; } - +//SYNC WITH: tune.cpp, mob.h Tunecompute_tohit int Mob::compute_tohit(EQ::skills::SkillType skillinuse) { int tohit = GetSkill(EQ::skills::SkillOffense) + 7; @@ -184,6 +185,7 @@ int Mob::compute_tohit(EQ::skills::SkillType skillinuse) } // return -1 in cases that always hit +//SYNC WITH: tune.cpp, mob.h TuneGetTotalToHit int Mob::GetTotalToHit(EQ::skills::SkillType skill, int chance_mod) { if (chance_mod >= 10000) // override for stuff like SE_SkillAttack @@ -247,6 +249,7 @@ int Mob::GetTotalToHit(EQ::skills::SkillType skill, int chance_mod) // based on dev quotes // the AGI bonus has actually drastically changed from classic +//SYNC WITH: tune.cpp, mob.h Tunecompute_defense int Mob::compute_defense() { int defense = GetSkill(EQ::skills::SkillDefense) * 400 / 225; @@ -275,6 +278,7 @@ int Mob::compute_defense() } // return -1 in cases that always miss +// SYNC WITH : tune.cpp, mob.h TuneGetTotalDefense() int Mob::GetTotalDefense() { auto avoidance = compute_defense() + 10; // add 10 in case the NPC's stats are fucked @@ -302,6 +306,7 @@ int Mob::GetTotalDefense() // called when a mob is attacked, does the checks to see if it's a hit // and does other mitigation checks. 'this' is the mob being attacked. +// SYNC WITH : tune.cpp, mob.h TuneCheckHitChance() bool Mob::CheckHitChance(Mob* other, DamageHitInfo &hit) { #ifdef LUA_EQEMU @@ -834,7 +839,7 @@ int Mob::GetClassRaceACBonus() return ac_bonus; } - +//SYNC WITH: tune.cpp, mob.h TuneACSum int Mob::ACSum(bool skip_caps) { int ac = 0; // this should be base AC whenever shrouds come around @@ -909,7 +914,7 @@ int Mob::ACSum(bool skip_caps) } int Mob::GetBestMeleeSkill() - { +{ int bestSkill=0; EQ::skills::SkillType meleeSkills[]= @@ -931,8 +936,8 @@ int Mob::GetBestMeleeSkill() } return bestSkill; - } - +} +//SYNC WITH: tune.cpp, mob.h Tuneoffense int Mob::offense(EQ::skills::SkillType skill) { int offense = GetSkill(skill); @@ -987,7 +992,7 @@ double Mob::RollD20(int offense, int mitigation) return mods[index]; } - +//SYNC WITH: tune.cpp, mob.h TuneMeleeMitigation void Mob::MeleeMitigation(Mob *attacker, DamageHitInfo &hit, ExtraAttackOptions *opts) { #ifdef LUA_EQEMU @@ -1363,6 +1368,7 @@ int Client::DoDamageCaps(int base_damage) } // other is the defender, this is the attacker +//SYNC WITH: tune.cpp, mob.h TuneDoAttack void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts) { if (!other) @@ -1420,6 +1426,7 @@ void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts) //note: throughout this method, setting `damage` to a negative is a way to //stop the attack calculations // IsFromSpell added to allow spell effects to use Attack. (Mainly for the Rampage AA right now.) +//SYNC WITH: tune.cpp, mob.h TuneClientAttack bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts) { if (!other) { @@ -1989,7 +1996,7 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQ::skills::Skill parse->EventPlayer(EVENT_DEATH_COMPLETE, this, export_string, 0); return true; } - +//SYNC WITH: tune.cpp, mob.h TuneNPCAttack bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts) { if (!other) { diff --git a/zone/client.h b/zone/client.h index 7df7bb143..cbdd41084 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1594,10 +1594,6 @@ public: uint32 GetLastInvSnapshotTime() { return m_epp.last_invsnapshot_time; } uint32 GetNextInvSnapshotTime() { return m_epp.next_invsnapshot_time; } - //Command #Tune functions - virtual int32 Tune_GetMeleeMitDmg(Mob* GM, Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating); - int32 GetMeleeDamage(Mob* other, bool GetMinDamage = false); - void QuestReward(Mob* target, uint32 copper = 0, uint32 silver = 0, uint32 gold = 0, uint32 platinum = 0, uint32 itemid = 0, uint32 exp = 0, bool faction = false); void QuestReward(Mob* target, const QuestReward_Struct &reward, bool faction); // TODO: Fix faction processing diff --git a/zone/command.cpp b/zone/command.cpp index 8b449d1e9..7f44ad6df 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -429,7 +429,7 @@ int command_init(void) command_add("titlesuffix", "[text] [1 = create title table row] - Set your or your player target's title suffix", 50, command_titlesuffix) || command_add("traindisc", "[level] - Trains all the disciplines usable by the target, up to level specified. (may freeze client for a few seconds)", 150, command_traindisc) || command_add("trapinfo", "- Gets infomation about the traps currently spawned in the zone.", 81, command_trapinfo) || - command_add("tune", "Calculate ideal statical values related to combat.", 100, command_tune) || + command_add("tune", "Calculate statistical values related to combat.", 100, command_tune) || command_add("ucs", "- Attempts to reconnect to the UCS server", 0, command_ucs) || command_add("undyeme", "- Remove dye from all of your armor slots", 0, command_undyeme) || command_add("unfreeze", "- Unfreeze your target", 80, command_unfreeze) || @@ -14328,53 +14328,108 @@ void command_tune(Client *c, const Seperator *sep) { //Work in progress - Kayen - if(sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { + if (sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { c->Message(Chat::White, "Syntax: #tune [subcommand]."); c->Message(Chat::White, "-- Tune System Commands --"); - c->Message(Chat::White, "-- Usage: Returning recommended combat statistical values based on a desired outcome."); - c->Message(Chat::White, "-- Note: If targeted mob does not have a target (ie not engaged in combat), YOU will be considered the target."); + c->Message(Chat::White, "-- Usage: Returns recommended combat statistical values based on a desired outcome through simulated combat."); + c->Message(Chat::White, "-- This commmand can answer the following difficult questions whening tunings NPCs and Players."); + c->Message(Chat::White, "-- Question: What is the average damage mitigation my AC provides against a specific targets attacks?"); + c->Message(Chat::White, "-- Question: What is amount of AC would I need to add to acheive a specific average damage mitigation agianst specific targets attacks?"); + c->Message(Chat::White, "-- Question: What is amount of AC would I need to add to my target to acheive a specific average damage mitigation from my attacks?"); + c->Message(Chat::White, "-- Question: What is my targets average AC damage mitigation based on my ATK stat?"); + c->Message(Chat::White, "-- Question: What is amount of ATK would I need to add to myself to acheive a specific average damage mitigation on my target?"); + c->Message(Chat::White, "-- Question: What is amount of ATK would I need to add to my target to acheive a specific average AC damage mitigation on myself?"); + c->Message(Chat::White, "-- Question: What is my hit chance against a target?"); + c->Message(Chat::White, "-- Question: What is the amount of avoidance I need to add to my target to achieve a specific hit chance?"); + c->Message(Chat::White, "-- Question: What is the amount of accuracy I need to add to my target to achieve a specific chance of hitting me?"); + c->Message(Chat::White, "-- Question: ... and many more..."); + c->Message(Chat::White, " "); + c->Message(Chat::White, "...#tune stats [A/D]"); + c->Message(Chat::White, "...#tune FindATK [A/D] [pct mitigation] [interval] [loop_max] [AC override] [Info Level]"); + c->Message(Chat::White, "...#tune FindAC [A/D] [pct mitigation] [interval] [loop_max] [ATK override] [Info Level] "); + c->Message(Chat::White, "...#tune FindAccuracy [A/D] [hit chance] [interval] [loop_max] [Avoidance override] [Info Level]"); + c->Message(Chat::White, "...#tune FindAvoidance [A/D] [hit chance] [interval] [loop_max] [Accuracy override] [Info Level] "); + c->Message(Chat::White, " "); + c->Message(Chat::White, "-- DETAILS AND EXAMPLES ON USAGE"); + c->Message(Chat::White, " "); + c->Message(Chat::White, "...Returns combat statistics, including AC mitigation pct, hit chance, and avoid melee chance for attacker and defender."); + c->Message(Chat::White, "...#tune stats [A/D]"); + c->Message(Chat::White, "..."); + c->Message(Chat::White, "...Returns recommended ATK adjustment (+/-) on ATTACKER that will result in a specific average AC mitigation pct on DEFENDER. "); + c->Message(Chat::White, "...#tune FindATK [A/D] [pct mitigation] [interval][loop_max][AC override][Info Level]"); + c->Message(Chat::White, "...Example: Find the amount of ATK stat I need to add to the targeted NPC so that it hits me for 50 pct damage on average."); + c->Message(Chat::White, "...Example: #tune FindATK D 50"); + c->Message(Chat::White, "..."); + c->Message(Chat::White, "...Returns recommended AC adjustment(+/-) on DEFENDER for a specific average AC mitigation pct from ATTACKER. "); + c->Message(Chat::White, "...#tune FindAC [A/D] [pct mitigation] [interval][loop_max][ATK override][Info Level] "); + c->Message(Chat::White, "...Example: Find the amount of AC stat I need to add to the targeted NPC so that I hit it for 70 pct damage on average."); + c->Message(Chat::White, "...Example: #tune FindAC D 70"); + c->Message(Chat::White, "..."); + c->Message(Chat::White, "...Returns recommended Accuracy adjustment (+/-) on ATTACKER that will result in a specific hit chance pct on DEFENDER. "); + c->Message(Chat::White, "...#tune FindAccuracy [A/D] [hit chance] [interval][loop_max][Avoidance override][Info Level]"); + c->Message(Chat::White, "...Example: Find the amount of Accuracy stat I need to add to the targeted NPC so that it has a 60 pct hit chance against me."); + c->Message(Chat::White, "...Example: #tune FindAccuracy D 60"); + c->Message(Chat::White, "..."); + c->Message(Chat::White, "...Returns recommended Avoidance adjustment (+/-) on DEFENDER for in a specific hit chance pct from ATTACKER. "); + c->Message(Chat::White, "...#tune FindAvoidance [A/D] [hit chance] [interval][loop_max][Accuracy override][Info Level] "); + c->Message(Chat::White, "...Example: Find the amount of Avoidance stat I need to add to the targeted NPC so that I have a 30 pct hit chance against it."); + c->Message(Chat::White, "...Example: #tune FindAvoidance D 30"); + c->Message(Chat::White, "... "); + c->Message(Chat::White, "...Usage: [A/D] You must input either A or D."); + c->Message(Chat::White, "...Category [A] : YOU are the ATTACKER. YOUR TARGET is the DEFENDER."); + c->Message(Chat::White, "...Category [D] : YOU are the DEFENDER. YOUR TARGET is the ATTACKER."); + c->Message(Chat::White, "...If TARGET is in combat, DEFENDER is the TARGETs TARGET."); + + c->Message(Chat::White, " "); + c->Message(Chat::White, "-- Warning: The calculations done in this process are intense and can potentially cause zone crashes depending on parameters set, use with caution!"); c->Message(Chat::White, "-- Below are OPTIONAL parameters."); - c->Message(Chat::White, "-- Note: [interval] Determines how fast the stat being checked increases/decreases till it finds the best result. Default [ATK/AC 50][Acc/Avoid 10] "); - c->Message(Chat::White, "-- Note: [loop_max] Determines how many iterations are done to increases/decreases the stat till it finds the best result. Default [ATK/AC 100][Acc/Avoid 1000]"); - c->Message(Chat::White, "-- Note: [Stat Override] Will override that stat on mob being checkd with the specified value. Default=0"); - c->Message(Chat::White, "-- Note: [Info Level] How much statistical detail is displayed[0 - 3]. Default=0 "); - c->Message(Chat::White, "-- Note: Results are only approximations usually accurate to +/- 2 intervals."); - - c->Message(Chat::White, "... "); - c->Message(Chat::White, "...### Category A ### Target = ATTACKER ### YOU or Target's Target = DEFENDER ###"); - c->Message(Chat::White, "...### Category B ### Target = DEFENDER ### YOU or Target's Target = ATTACKER ###"); - c->Message(Chat::White, "... "); - c->Message(Chat::White, "...#Returns recommended ATK adjustment +/- on ATTACKER that will result in an average mitigation pct on DEFENDER. "); - c->Message(Chat::White, "...tune FindATK [A/B] [pct mitigation] [interval][loop_max][AC Overwride][Info Level]"); - c->Message(Chat::White, "... "); - c->Message(Chat::White, "...#Returns recommended AC adjustment +/- on DEFENDER for an average mitigation pct from ATTACKER. "); - c->Message(Chat::White, "...tune FindAC [A/B] [pct mitigation] [interval][loop_max][ATK Overwride][Info Level] "); - c->Message(Chat::White, "... "); - c->Message(Chat::White, "...#Returns recommended Accuracy adjustment +/- on ATTACKER that will result in a hit chance pct on DEFENDER. "); - c->Message(Chat::White, "...tune FindAccuracy [A/B] [hit chance] [interval][loop_max][Avoidance Overwride][Info Level]"); - c->Message(Chat::White, "... "); - c->Message(Chat::White, "...#Returns recommended Avoidance adjustment +/- on DEFENDER for in a hit chance pct from ATTACKER. "); - c->Message(Chat::White, "...tune FindAvoidance [A/B] [pct mitigation] [interval][loop_max][Accuracy Overwride][Info Level] "); + c->Message(Chat::White, "-- Note: [interval] Determines how much the stat being checked increases/decreases till it finds the best result. Lower is more accurate. Default=10"); + c->Message(Chat::White, "-- Note: [loop_max] Determines how many iterations are done to increases/decreases the stat till it finds the best result. Higher is more accurate. Default=1000"); + c->Message(Chat::White, "-- Note: [Stat Override] Will override that stat on mob being checked with the specified value. Default=0"); + c->Message(Chat::White, "-- Example: If as the attacker you want to find the ATK value you would need to have agianst a target with 1000 AC to achieve an average AC mitigation of 50 pct."); + c->Message(Chat::White, "-- Example: #tune FindATK A 50 0 0 1000"); + c->Message(Chat::White, "-- Note: [Info Level] How much parsing detail is displayed[0 - 1]. Default: [0] "); + c->Message(Chat::White, " "); return; } - //Default is category A for attacker/defender settings, which then are swapped under category B. - Mob* defender = c; - Mob* attacker = c->GetTarget(); + /* + Category A: YOU are the attacker and your target is the defender + Category D: YOU are the defender and your target is the attacker + */ - if (!attacker) + Mob* attacker = c; + Mob* defender = c->GetTarget(); + + if (!defender) { - c->Message(Chat::White, "#Tune - Error no target selected. [#Tune help]"); + c->Message(Chat::White, "[#Tune] - Error no target selected. [#Tune help]"); return; } + //Use if checkings on engaged targets. Mob* ttarget = attacker->GetTarget(); - - if (ttarget) + if (ttarget) { defender = ttarget; + } - if(!strcasecmp(sep->arg[1], "FindATK")) + if (!strcasecmp(sep->arg[1], "stats")) + { + + if (!strcasecmp(sep->arg[2], "A")) { + c->TuneGetStats(defender, attacker); + } + else if (!strcasecmp(sep->arg[2], "D")){ + c->TuneGetStats(attacker, defender); + } + else { + c->TuneGetStats(defender, attacker); + } + return; + } + + if (!strcasecmp(sep->arg[1], "FindATK")) { float pct_mitigation = atof(sep->arg[3]); int interval = atoi(sep->arg[4]); @@ -14384,32 +14439,48 @@ void command_tune(Client *c, const Seperator *sep) if (!pct_mitigation) { - c->Message(Chat::Red, "#Tune - Error must enter the desired percent mitigation on defender. Ie. Defender to mitigate on average 20 pct of max damage."); + c->Message(Chat::White, "[#Tune] - Error must enter the desired percent mitigation on defender."); + c->Message(Chat::White, "...Returns recommended ATK adjustment (+/-) on ATTACKER that will result in a specific average AC mitigation pct on DEFENDER. "); + c->Message(Chat::White, "...#tune FindATK [A/D] [pct mitigation] [interval][loop_max][AC override][Info Level]"); + c->Message(Chat::White, "...Example: Find the amount of ATK stat I need to add to the targeted NPC so that it hits me for 50 pct damage on average."); + c->Message(Chat::White, "...Example: #tune FindATK D 50"); return; } - if (!interval) - interval = 50; - if (!max_loop) - max_loop = 100; - if(!ac_override) + if (!interval) { + interval = 10; + } + if (!max_loop) { + max_loop = 1000; + } + if (!ac_override) { ac_override = 0; - if (!info_level) - info_level = 1; + } + if (!info_level) { + info_level = 0; + } - if(!strcasecmp(sep->arg[2], "A")) - c->Tune_FindATKByPctMitigation(defender, attacker, pct_mitigation, interval, max_loop,ac_override,info_level); - else if(!strcasecmp(sep->arg[2], "B")) - c->Tune_FindATKByPctMitigation(attacker,defender, pct_mitigation, interval, max_loop,ac_override,info_level); + if (!strcasecmp(sep->arg[2], "A")) { + c->TuneGetATKByPctMitigation(defender, attacker, pct_mitigation, interval, max_loop, ac_override, info_level); + } + else if (!strcasecmp(sep->arg[2], "D")) { + c->TuneGetATKByPctMitigation(attacker, defender, pct_mitigation, interval, max_loop, ac_override, info_level); + } else { c->Message(Chat::White, "#Tune - Error no category selcted. [#Tune help]"); c->Message(Chat::White, "Usage #tune FindATK [A/B] [pct mitigation] [interval][loop_max][AC Overwride][Info Level] "); - c->Message(Chat::White, "Example #tune FindATK A 60"); + c->Message(Chat::White, "...Usage: [A/D] You must input either A or D."); + c->Message(Chat::White, "...Category [A] : YOU are the ATTACKER. YOUR TARGET is the DEFENDER."); + c->Message(Chat::White, "...Category [D] : YOU are the DEFENDER. YOUR TARGET is the ATTACKER."); + c->Message(Chat::White, "...If TARGET is in combat, DEFENDER is the TARGETs TARGET."); + c->Message(Chat::White, "... "); + c->Message(Chat::White, "...Example: Find the amount of ATK stat I need to add to the targeted NPC so that it hits me for 50 pct damage on average."); + c->Message(Chat::White, "...Example: #tune FindATK D 50"); } return; } - if(!strcasecmp(sep->arg[1], "FindAC")) + if (!strcasecmp(sep->arg[1], "FindAC")) { float pct_mitigation = atof(sep->arg[3]); int interval = atoi(sep->arg[4]); @@ -14419,33 +14490,49 @@ void command_tune(Client *c, const Seperator *sep) if (!pct_mitigation) { - c->Message(Chat::Red, "#Tune - Error must enter the desired percent mitigation on defender. Ie. Defender to mitigate on average 20 pct of max damage."); + c->Message(Chat::White, "#Tune - Error must enter the desired percent mitigation on defender."); + c->Message(Chat::White, "...Returns recommended AC adjustment(+/-) on DEFENDER for a specific average AC mitigation pct from ATTACKER. "); + c->Message(Chat::White, "...#tune FindAC [A/D] [pct mitigation] [interval][loop_max][ATK override][Info Level] "); + c->Message(Chat::White, "...Example: Find the amount of AC stat I need to add to the targeted NPC so that I hit it for 70 pct damage on average."); + c->Message(Chat::White, "...Example: #tune FindAC D 70"); return; } - if (!interval) - interval = 50; - if (!max_loop) - max_loop = 100; - if(!atk_override) + if (!interval) { + interval = 10; + } + if (!max_loop) { + max_loop = 1000; + } + if (!atk_override) { atk_override = 0; - if (!info_level) - info_level = 1; + } + if (!info_level) { + info_level = 0; + } - if(!strcasecmp(sep->arg[2], "A")) - c->Tune_FindACByPctMitigation(defender, attacker, pct_mitigation, interval, max_loop,atk_override,info_level); - else if(!strcasecmp(sep->arg[2], "B")) - c->Tune_FindACByPctMitigation(attacker, defender, pct_mitigation, interval, max_loop,atk_override,info_level); + if (!strcasecmp(sep->arg[2], "A")) { + c->TuneGetACByPctMitigation(defender, attacker, pct_mitigation, interval, max_loop, atk_override, info_level); + } + else if (!strcasecmp(sep->arg[2], "D")) { + c->TuneGetACByPctMitigation(attacker, defender, pct_mitigation, interval, max_loop, atk_override, info_level); + } else { c->Message(Chat::White, "#Tune - Error no category selcted. [#Tune help]"); - c->Message(Chat::White, "Usage #tune FindAC [A/B] [pct mitigation] [interval][loop_max][ATK Overwride][Info Level] "); - c->Message(Chat::White, "Example #tune FindAC A 60"); + c->Message(Chat::White, "Usage #tune FindATK [A/B] [pct mitigation] [interval][loop_max][AC Overwride][Info Level] "); + c->Message(Chat::White, "...Usage: [A/D] You must input either A or D."); + c->Message(Chat::White, "...Category [A] : YOU are the ATTACKER. YOUR TARGET is the DEFENDER."); + c->Message(Chat::White, "...Category [D] : YOU are the DEFENDER. YOUR TARGET is the ATTACKER."); + c->Message(Chat::White, "...If TARGET is in combat, DEFENDER is the TARGETs TARGET."); + c->Message(Chat::White, "... "); + c->Message(Chat::White, "...Example: Find the amount of AC stat I need to add to the targeted NPC so that I hit it for 70 pct damage on average."); + c->Message(Chat::White, "...Example: #tune FindAC D 70"); } return; } - if(!strcasecmp(sep->arg[1], "FindAccuracy")) + if (!strcasecmp(sep->arg[1], "FindAccuracy")) { float hit_chance = atof(sep->arg[3]); int interval = atoi(sep->arg[4]); @@ -14455,39 +14542,47 @@ void command_tune(Client *c, const Seperator *sep) if (!hit_chance) { - c->Message(Chat::NPCQuestSay, "#Tune - Error must enter the desired percent mitigation on defender. Ie. Defender to mitigate on average 20 pct of max damage."); + c->Message(Chat::White, "#Tune - Error must enter the desired hit chance on defender."); + c->Message(Chat::White, "...Returns recommended Accuracy adjustment (+/-) on ATTACKER that will result in a specific hit chance pct on DEFENDER. "); + c->Message(Chat::White, "...#tune FindAccuracy [A/D] [hit chance] [interval][loop_max][Avoidance override][Info Level]"); + c->Message(Chat::White, "...Example: Find the amount of Accuracy stat I need to add to the targeted NPC so that it has a 60 pct hit chance against me."); + c->Message(Chat::White, "...Example: #tune FindAccuracy D 60"); return; } - if (!interval) + if (!interval) { interval = 10; - if (!max_loop) + } + if (!max_loop) { max_loop = 1000; - if(!avoid_override) + } + if (!avoid_override) { avoid_override = 0; - if (!info_level) - info_level = 1; - - if (hit_chance > RuleR(Combat,MaxChancetoHit) || hit_chance < RuleR(Combat,MinChancetoHit)) - { - c->Message(Chat::NPCQuestSay, "#Tune - Error hit chance out of bounds. [Max %.2f Min .2f]", RuleR(Combat,MaxChancetoHit),RuleR(Combat,MinChancetoHit)); - return; + } + if (!info_level) { + info_level = 0; } - if(!strcasecmp(sep->arg[2], "A")) - c->Tune_FindAccuaryByHitChance(defender, attacker, hit_chance, interval, max_loop,avoid_override,info_level); - else if(!strcasecmp(sep->arg[2], "B")) - c->Tune_FindAccuaryByHitChance(attacker, defender, hit_chance, interval, max_loop,avoid_override,info_level); + if (!strcasecmp(sep->arg[2], "A")) + c->TuneGetAccuracyByHitChance(defender, attacker, hit_chance, interval, max_loop, avoid_override, info_level); + else if (!strcasecmp(sep->arg[2], "D")) + c->TuneGetAccuracyByHitChance(attacker, defender, hit_chance, interval, max_loop, avoid_override, info_level); else { c->Message(Chat::White, "#Tune - Error no category selcted. [#Tune help]"); - c->Message(Chat::White, "Usage #tune FindAcccuracy [A/B] [hit chance] [interval][loop_max][Avoidance Overwride][Info Level]"); - c->Message(Chat::White, "Exampled #tune FindAccuracy B 30"); + c->Message(Chat::White, "...#tune FindAccuracy [A/D] [hit chance] [interval][loop_max][Avoidance override][Info Level]"); + c->Message(Chat::White, "...Usage: [A/D] You must input either A or D."); + c->Message(Chat::White, "...Category [A] : YOU are the ATTACKER. YOUR TARGET is the DEFENDER."); + c->Message(Chat::White, "...Category [D] : YOU are the DEFENDER. YOUR TARGET is the ATTACKER."); + c->Message(Chat::White, "...If TARGET is in combat, DEFENDER is the TARGETs TARGET."); + c->Message(Chat::White, "... "); + c->Message(Chat::White, "...Example: Find the amount of Accuracy stat I need to add to the targeted NPC so that it has a 60 pct hit chance against me."); + c->Message(Chat::White, "...Example: #tune FindAccuracy D 60"); } return; } - if(!strcasecmp(sep->arg[1], "FindAvoidance")) + if (!strcasecmp(sep->arg[1], "FindAvoidance")) { float hit_chance = atof(sep->arg[3]); int interval = atoi(sep->arg[4]); @@ -14497,39 +14592,46 @@ void command_tune(Client *c, const Seperator *sep) if (!hit_chance) { - c->Message(Chat::White, "#Tune - Error must enter the desired hit chance on defender. Ie. Defender to have hit chance of 40 pct."); + c->Message(Chat::White, "#Tune - Error must enter the desired hit chance on defender."); + c->Message(Chat::White, "...Returns recommended Avoidance adjustment (+/-) on DEFENDER for in a specific hit chance pct from ATTACKER. "); + c->Message(Chat::White, "...#tune FindAvoidance [A/D] [hit chance] [interval][loop_max][Accuracy override][Info Level] "); + c->Message(Chat::White, "...Example: Find the amount of Avoidance stat I need to add to the targeted NPC so that I have a 30 pct hit chance against it."); + c->Message(Chat::White, "...Example: #tune FindAvoidance D 30"); return; } - - if (!interval) + if (!interval) { interval = 10; - if (!max_loop) + } + if (!max_loop) { max_loop = 1000; - if(!acc_override) + } + if (!acc_override) { acc_override = 0; - if (!info_level) - info_level = 1; - - if (hit_chance > RuleR(Combat,MaxChancetoHit) || hit_chance < RuleR(Combat,MinChancetoHit)) - { - c->Message(Chat::NPCQuestSay, "#Tune - Error hit chance out of bounds. [Max %.2f Min .2f]", RuleR(Combat,MaxChancetoHit),RuleR(Combat,MinChancetoHit)); - return; + } + if (!info_level) { + info_level = 0; } - if(!strcasecmp(sep->arg[2], "A")) - c->Tune_FindAvoidanceByHitChance(defender, attacker, hit_chance, interval, max_loop,acc_override, info_level); - else if(!strcasecmp(sep->arg[2], "B")) - c->Tune_FindAvoidanceByHitChance(attacker, defender, hit_chance, interval, max_loop,acc_override, info_level); + if (!strcasecmp(sep->arg[2], "A")) + c->TuneGetAvoidanceByHitChance(defender, attacker, hit_chance, interval, max_loop, acc_override, info_level); + else if (!strcasecmp(sep->arg[2], "D")) + c->TuneGetAvoidanceByHitChance(attacker, defender, hit_chance, interval, max_loop, acc_override, info_level); else { c->Message(Chat::White, "#Tune - Error no category selcted. [#Tune help]"); - c->Message(Chat::White, "Usage #tune FindAvoidance [A/B] [hit chance] [interval][loop_max][Accuracy Overwride][Info Level]"); - c->Message(Chat::White, "Exampled #tune FindAvoidance B 30"); + c->Message(Chat::White, "...#tune FindAvoidance [A/D] [hit chance] [interval][loop_max][Accuracy override][Info Level] "); + c->Message(Chat::White, "...Usage: [A/D] You must input either A or D."); + c->Message(Chat::White, "...Category [A] : YOU are the ATTACKER. YOUR TARGET is the DEFENDER."); + c->Message(Chat::White, "...Category [D] : YOU are the DEFENDER. YOUR TARGET is the ATTACKER."); + c->Message(Chat::White, "...If TARGET is in combat, DEFENDER is the TARGETs TARGET."); + c->Message(Chat::White, "... "); + c->Message(Chat::White, "...Example: Find the amount of Avoidance stat I need to add to the targeted NPC so that I have a 30 pct hit chance against it."); + c->Message(Chat::White, "...Example: #tune FindAvoidance D 30"); } return; } - + c->Message(Chat::White, "#Tune - Error no command [#Tune help]"); return; } diff --git a/zone/mob.h b/zone/mob.h index a3a664f1f..327f73f9c 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1132,7 +1132,7 @@ public: void SendItemAnimation(Mob *to, const EQ::ItemData *item, EQ::skills::SkillType skillInUse, float velocity = 4.0); inline int& GetNextIncHPEvent() { return nextinchpevent; } void SetNextIncHPEvent( int inchpevent ); - + inline bool DivineAura() const { return spellbonuses.DivineAura; } inline bool Sanctuary() const { return spellbonuses.Sanctuary; } @@ -1226,14 +1226,38 @@ public: bool mod_will_aggro(Mob *attacker, Mob *on); //Command #Tune functions - int32 Tune_MeleeMitigation(Mob* GM, Mob *attacker, int32 damage, int32 minhit, ExtraAttackOptions *opts = nullptr, int Msg =0, int ac_override=0, int atk_override=0, int add_ac=0, int add_atk = 0); - virtual int32 Tune_GetMeleeMitDmg(Mob* GM, Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating); - uint32 Tune_GetMeanDamage(Mob* GM, Mob *attacker, int32 damage, int32 minhit, ExtraAttackOptions *opts = nullptr, int Msg = 0,int ac_override=0, int atk_override=0, int add_ac=0, int add_atk = 0); - void Tune_FindATKByPctMitigation(Mob* defender, Mob *attacker, float pct_mitigation, int interval = 50, int max_loop = 100, int ac_override=0,int Msg =0); - void Tune_FindACByPctMitigation(Mob* defender, Mob *attacker, float pct_mitigation, int interval = 50, int max_loop = 100, int atk_override=0,int Msg =0); - float Tune_CheckHitChance(Mob* defender, Mob* attacker, EQ::skills::SkillType skillinuse, int Hand, int16 chance_mod, int Msg = 1, int acc_override = 0, int avoid_override = 0, int add_acc = 0, int add_avoid = 0); - void Tune_FindAccuaryByHitChance(Mob* defender, Mob *attacker, float hit_chance, int interval, int max_loop, int avoid_override, int Msg = 0); - void Tune_FindAvoidanceByHitChance(Mob* defender, Mob *attacker, float hit_chance, int interval, int max_loop, int acc_override, int Msg = 0); + void TuneGetStats(Mob* defender, Mob *attacker); + void TuneGetACByPctMitigation(Mob* defender, Mob *attacker, float pct_mitigation, int interval = 10, int max_loop = 1000, int atk_override = 0, int Msg = 0); + void TuneGetATKByPctMitigation(Mob* defender, Mob *attacker, float pct_mitigation, int interval = 10, int max_loop = 1000, int ac_override = 0, int Msg = 0); + void TuneGetAvoidanceByHitChance(Mob* defender, Mob *attacker, float hit_chance, int interval, int max_loop, int accuracy_override, int Msg); + void TuneGetAccuracyByHitChance(Mob* defender, Mob *attacker, float hit_chance, int interval, int max_loop, int avoidance_override, int Msg); + /*support functions*/ + int TuneClientGetMeanDamage(Mob* other, int ac_override = 0, int atk_override = 0, int add_ac = 0, int add_atk = 0); + int TuneClientGetMaxDamage(Mob* other); + int TuneClientGetMinDamage(Mob* other, int max_hit); + float TuneGetACMitigationPct(Mob* defender, Mob *attacker); + int TuneGetOffense(Mob* defender, Mob *attacker, int atk_override = 0); + int TuneGetAccuracy(Mob* defender, Mob *attacker, int accuracy_override = 0, int add_accuracy = 0); + int TuneGetAvoidance(Mob* defender, Mob *attacker, int avoidance_override = 0, int add_avoidance = 0); + float TuneGetHitChance(Mob* defender, Mob *attacker, int avoidance_override = 0, int accuracy_override = 0, int add_avoidance = 0, int add_accuracy = 0); + float TuneGetAvoidMeleeChance(Mob* defender, Mob *attacker, int type); + int TuneCalcEvasionBonus(int final_avoidance, int base_avoidance); + /*modified combat code - These SYNC to attack.cpp, relevant changes to these functions in attack.cpp should be changed to the below as well*/ + int TuneNPCAttack(Mob* other, bool no_avoid = true, bool no_hit_chance = true, int hit_chance_bonus = 10000, int ac_override = 0, int atk_override = 0, int add_ac = 0, int add_atk = 0, + bool get_offense = false, bool get_accuracy = false, int avoidance_override = 0, int accuracy_override = 0, int add_avoidance = 0, int add_accuracy = 0); + int TuneClientAttack(Mob* other, bool no_avoid = true, bool no_hit_chance = true, int hit_chance_bonus = 10000, int ac_override = 0, int atk_override = 0, int add_ac = 0, int add_atk = 0, + bool get_offense = false, bool get_accuracy = false, int avoidance_override = 0, int accuracy_override = 0, int add_avoidance = 0, int add_accuracy = 0); + void TuneDoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts = nullptr, bool no_avoid = true, bool no_hit_chance = true, int ac_override = 0, int add_ac = 0, + int avoidance_override = 0, int accuracy_override = 0, int add_avoidance = 0, int add_accuracy = 0); + void TuneMeleeMitigation(Mob *attacker, DamageHitInfo &hit, int ac_override, int add_ac); + int Tuneoffense(EQ::skills::SkillType skill, int atk_override = 0, int add_atk = 0); + int TuneACSum(bool skip_caps=false, int ac_override = 0, int add_ac = 0); + int TuneGetTotalToHit(EQ::skills::SkillType skill, int chance_mod, int accuracy_override = 0, int add_accurracy = 0); // compute_tohit + spell bonuses + int Tunecompute_tohit(EQ::skills::SkillType skillinuse, int accuracy_override = 0, int add_accuracy = 0); + int TuneGetTotalDefense(int avoidance_override = 0, int add_avoidance = 0); + int Tunecompute_defense(int avoidance_override = 0, int add_avoidance = 0); + bool TuneCheckHitChance(Mob* other, DamageHitInfo &hit, int avoidance_override = 0, int add_avoidance = 0); + EQ::skills::SkillType TuneAttackAnimation(int Hand, const EQ::ItemInstance* weapon, EQ::skills::SkillType skillinuse = EQ::skills::Skill1HBlunt); //aa new uint32 GetAA(uint32 rank_id, uint32 *charges = nullptr) const; diff --git a/zone/tune.cpp b/zone/tune.cpp index f22fe6a53..be806eb92 100644 --- a/zone/tune.cpp +++ b/zone/tune.cpp @@ -27,16 +27,23 @@ #include "../common/skills.h" #include "../common/spdat.h" #include "../common/string_util.h" +#include "../common/data_verification.h" +#include "../common/misc_functions.h" #include "queryserv.h" #include "quest_parser_collection.h" #include "string_ids.h" #include "water_map.h" #include "worldserver.h" #include "zone.h" +#include "lua_parser.h" +#include "fastmath.h" +#include "mob.h" +#include "npc.h" #include #include #include +#include #ifdef BOTS #include "bot.h" @@ -44,6 +51,7 @@ extern QueryServ* QServ; extern WorldServer worldserver; +extern FastMath g_Math; #ifdef _WINDOWS #define snprintf _snprintf @@ -54,1035 +62,1350 @@ extern WorldServer worldserver; extern EntityList entity_list; extern Zone* zone; -void Mob::Tune_FindATKByPctMitigation(Mob* defender,Mob *attacker, float pct_mitigation, int interval, int max_loop, int ac_override, int Msg) + +void Mob::TuneGetStats(Mob* defender, Mob *attacker) { - /*Find the amount of 'ATTACK' stat that has to be added/subtracted FROM ATTACKER to reach a specific average mitigation value on the TARGET. - Can use ac_override to find the value verse a hypothetical amount of worn AC */ - - int atk = 0; - uint32 total_damage = 0; - int32 damage = 0; - uint32 minhit = 0; - int mean_dmg = 0; - float tmp_pct_mitigated = 0.0f; - int end = 0; - - if (attacker->IsNPC()) - { - damage = static_cast(attacker->CastToNPC()->GetMaxDMG()); - minhit = attacker->CastToNPC()->GetMinDMG(); - } - else if (attacker->IsClient()) - { - damage = static_cast(attacker->CastToClient()->GetMeleeDamage(this)); - minhit = attacker->CastToClient()->GetMeleeDamage(this, true); - } - - if (damage == 0 || minhit == 0) - { - Message(0, "#Tune - Processing... Abort! Damage not found! [MaxDMG %i MinDMG %i]", damage,minhit); + if (!defender || !attacker) { + Message(0, "#Tune - Processing... Abort! Can not find attacker or defender"); return; } + int max_damage = 0; + int min_damage = 0; + int mean_dmg = 0; + float tmp_pct_mitigated = 0.0f; + float hit_chance = 0.0f; - mean_dmg = defender->Tune_GetMeanDamage(this, attacker, damage, minhit, nullptr, 0, ac_override, 0, 0,atk); - tmp_pct_mitigated = 100.0f - static_cast( mean_dmg * 100 /damage); + max_damage = attacker->TuneClientGetMaxDamage(defender); + min_damage = attacker->TuneClientGetMinDamage(defender, max_damage); - Message(0, "#Tune - Begin Parse [Interval %i Max Loop Iterations %i]", interval, max_loop); - Message(0, "#Tune - Processing... Find ATK for attacker Mitigation (%.0f) pct on defender [MaxDMG %i MinDMG %i Current Mitigation %.2f]", pct_mitigation, damage, minhit,tmp_pct_mitigated); - - if (tmp_pct_mitigated < pct_mitigation) - interval = interval * -1; - - for (int j=0; j < max_loop; j++) + if (!max_damage) { - mean_dmg = defender->Tune_GetMeanDamage(this, attacker, damage, minhit, nullptr, 0, ac_override,0, 0,atk); - tmp_pct_mitigated = 100.0f - ( static_cast(mean_dmg) * 100.0f /static_cast(damage)); + Message(0, "#Tune - Processing... Abort! Damage not found! [MaxDMG %i MinDMG %i]", max_damage, min_damage); + return; + } + mean_dmg = attacker->TuneClientGetMeanDamage(defender); + tmp_pct_mitigated = 100.0f - (static_cast(mean_dmg) * 100.0f / static_cast(max_damage)); - if (Msg >= 3) - Message(0, "#Tune - Processing... [%i] [ATK %i] Average Melee Hit %i | Pct Mitigated %.2f ",j,atk, mean_dmg, tmp_pct_mitigated); + hit_chance = TuneGetHitChance(defender, attacker); - if (interval > 0 && tmp_pct_mitigated <= pct_mitigation) - end = 1; - - else if (interval < 0 && tmp_pct_mitigated >= pct_mitigation) - end = 1; - - else if (interval < 0 && mean_dmg == minhit) - end = 2; + Message(0, "#STATS#############START######################"); + Message(0, "[#Tune] Defender Statistics vs Attacker"); + Message(0, "[#Tune] Defender Name: %s", defender->GetCleanName()); + Message(0, "[#Tune] AC Mitigation pct: %.0f pct ", round(tmp_pct_mitigated)); + Message(0, "[#Tune] Total AC: %i ", defender->TuneACSum()); + Message(0, "[#Tune] Mean Damage Taken: %i per hit", mean_dmg); + Message(0, "[#Tune] Chance to be missed: %.0f pct", (100.0f - round(hit_chance))); + Message(0, "[#Tune] Avoidance: %i ", TuneGetAvoidance(defender, attacker)); + Message(0, "[#Tune] Riposte Chance: %.0f pct ", round(TuneGetAvoidMeleeChance(defender, attacker, DMG_RIPOSTED))); + Message(0, "[#Tune] Block Chance: %.0f pct ", round(TuneGetAvoidMeleeChance(defender, attacker, DMG_BLOCKED))); + Message(0, "[#Tune] Parry Chance: %.0f pct ", round(TuneGetAvoidMeleeChance(defender, attacker, DMG_PARRIED))); + Message(0, "[#Tune] Dodge Chance: %.0f pct ", round(TuneGetAvoidMeleeChance(defender, attacker, DMG_DODGED))); - if (end >= 1){ + Message(0, "################################################"); + Message(0, "[#Tune] Attacker Statistics vs Defender"); + Message(0, "[#Tune] Attacker Name: %s", attacker->GetCleanName()); - defender->Tune_MeleeMitigation(this, attacker, damage, minhit, nullptr,Msg,ac_override, 0, 0, atk); - - if (end == 2) - Message(0, "#Tune - [WARNING] Mitigation can not be further decreased due to minium hit value (%i).",minhit); + if (max_damage > 0) { + Message(0, "[#Tune] Max Damage %i Min Damage %i", max_damage, min_damage); + Message(0, "[#Tune] Total Offense: %i ", TuneGetOffense(defender, attacker)); + Message(0, "[#Tune] Chance to hit: %.0f pct", round(hit_chance)); + Message(0, "[#Tune] Accuracy: %i ", TuneGetAccuracy( defender,attacker)); - if (attacker->IsNPC()){ - Message(0, "#Tune - Recommended NPC ATK ADJUSTMENT ( %i ) on ' %s ' average mitigation of (%.0f) pct verse ' %s '. ",atk, attacker->GetCleanName(), pct_mitigation, defender->GetCleanName()); - Message(0, "#SET: [NPC Attack STAT] = [%i]",atk + defender->CastToNPC()->ATK); - } - if (attacker->IsClient()){ - Message(0, "#Tune - Recommended CLIENT ATK ADJUSTMENT ( %i ) on ' %s ' average mitigation of (%.0f) pct verse ' %s '. ", atk, attacker->GetCleanName(), pct_mitigation, defender->GetCleanName()); - Message(0, "#Modify (+/-): [Client Attack STAT/SE_ATK(2)] [%i]",atk); - } - - return; - } - - atk = atk + interval; + } + else{ + Message(0, "[#Tune] Can not melee this target"); } - Message(0, "#Tune - Error: Unable to find desired result for (%.0f) pct - Increase interval (%i) AND/OR max loop value (%i) and run again.", pct_mitigation, interval, max_loop); - Message(0, "#Tune - Parse ended at ATK ADJUSTMENT ( %i ) average target mitigation of (%.0f) pct.",atk,tmp_pct_mitigated); + Message(0, "#STATS#############COMPLETE###################"); + return; } -void Mob::Tune_FindACByPctMitigation(Mob* defender, Mob *attacker, float pct_mitigation, int interval, int max_loop, int atk_override, int Msg) +void Mob::TuneGetACByPctMitigation(Mob* defender, Mob *attacker, float pct_mitigation, int interval, int max_loop, int atk_override, int Msg) { + Message(0, " "); + /* + Find the amount of AC stat that has to be added/subtracted from DEFENDER to reach a specific average mitigation value based on ATTACKER's offense statistics. + Can use atk_override to find the value verse a hypothetical amount of worn ATK + */ - /*Find the amount of AC stat that has to be added/subtracted from TARGET to reach a specific average mitigation value based on ATTACKER's statistics. - Can use ac_override to find the value verse a hypothetical amount of worn AC */ - - int add_ac = 0; - uint32 total_damage = 0; - int32 damage = 0; - uint32 minhit = 0; - int mean_dmg = 0; - float tmp_pct_mitigated = 0.0f; - int end = 0; - - - if (attacker->IsNPC()) - { - damage = static_cast(attacker->CastToNPC()->GetMaxDMG()); - minhit = attacker->CastToNPC()->GetMinDMG(); + if (pct_mitigation > 100 || pct_mitigation < 0) { + Message(0, "[#Tune] - Processing... Abort! Mitigation value out of range ( %.0f ) pct. Must be between 0-100.", pct_mitigation); + return; } - else if (attacker->IsClient()) - { - damage = static_cast(attacker->CastToClient()->GetMeleeDamage(this)); - minhit = attacker->CastToClient()->GetMeleeDamage(this, true); + if (!defender) { + Message(0, "[#Tune] - Processing... Abort! No Defender found."); + return; } - - if (damage == 0 || minhit == 0) - { - Message(0, "#Tune - Processing... Abort! Damage not found! [MaxDMG %i MinDMG %i]", damage,minhit); + if (!attacker) { + Message(0, "[#Tune] - Processing... Abort! No Attacker found."); + return; + } + if (defender->GetID() == attacker->GetID()) { + Message(0, "[#Tune] - Processing... Abort! Error Attacker can not be the Defender."); return; } - mean_dmg = defender->Tune_GetMeanDamage(this, attacker, damage, minhit, nullptr, 0, 0, atk_override); - tmp_pct_mitigated = 100.0f - static_cast( mean_dmg * 100 /damage); + int max_damage = 0; + int min_damage = 0; + int mean_dmg = 0; + float tmp_pct_mitigated = 0.0f; + float base_pct_mitigation = pct_mitigation; + int loop_add_ac = 0; + int end = 0; + int value = 0; - Message(0, "#Tune - Begin Parse [Interval %i Max Loop Iterations %i]", interval, max_loop); - Message(0, "#Tune - Processing... Find AC for defender Mitigation (%.0f) pct from attacker [MaxDMG %i MinDMG %i Current Mitigation %.2f]", pct_mitigation, damage, minhit,tmp_pct_mitigated); + max_damage = attacker->TuneClientGetMaxDamage(defender); + min_damage = attacker->TuneClientGetMinDamage(defender, max_damage); + + if (!max_damage) + { + Message(0, "#Tune - Processing... Abort! Damage not found! [MaxDMG %i MinDMG %i]", max_damage, min_damage); + return; + } + + //Obtain baseline mitigation for current stats + mean_dmg = attacker->TuneClientGetMeanDamage(defender,0,atk_override); + tmp_pct_mitigated = 100.0f - (static_cast(mean_dmg) * 100.0f / static_cast(max_damage)); + + Message(0, "###################START###################"); + Message(0, "[#Tune] DFENDER Name: %s", defender->GetCleanName()); + Message(0, "[#Tune] DEFENDER AC Mitigation pct: %.0f pct ", round(tmp_pct_mitigated)); + Message(0, "[#Tune] DEFENDER Total AC: %i ", defender->TuneACSum()); + Message(0, "[#Tune] ATTACKER Name: %s", attacker->GetCleanName()); + Message(0, "[#Tune] ATTACKER Max Damage %i Min Damage %i", max_damage, min_damage); + Message(0, "[#Tune] ATTACKER Total Offense: %i ", TuneGetOffense(defender, attacker, atk_override)); + Message(0, "##########################################"); + Message(0, "[#Tune] Begin Parse [Interval %i Max Loop Iterations %i]", interval, max_loop); if (tmp_pct_mitigated > pct_mitigation) - interval = interval * -1; - - for (int j=0; j < max_loop; j++) { - mean_dmg = defender->Tune_GetMeanDamage(this, attacker, damage, minhit, nullptr, 0, 0,atk_override, add_ac, 0); - tmp_pct_mitigated = 100.0f - ( static_cast(mean_dmg) * 100.0f /static_cast(damage)); + interval = interval * -1; + Message(0, "[#Tune] NOTE: Defenders 'AC' must be LOWERED due to defenders AC Mitigation ( %.0f pct ) being greater than the desired ( %.0f pct )", tmp_pct_mitigated, pct_mitigation); + } - if (Msg >= 3) - Message(0, "#Tune - Processing... [%i] [AC %i] Average Melee Hit %i | Pct Mitigated %.2f ",j,add_ac, mean_dmg, tmp_pct_mitigated); + Message(0, "[#Tune] Processing... Find AC for defender to have Mitigation of ( %.0f pct ) agianst this attacker.", pct_mitigation); + + + for (int j = 0; j < max_loop; j++) + { + mean_dmg = attacker->TuneClientGetMeanDamage(defender, 0, atk_override, loop_add_ac,0); + tmp_pct_mitigated = 100.0f - (static_cast(mean_dmg) * 100.0f / static_cast(max_damage)); + + if (Msg >= 1) + { + Message(0, "[#Tune] - Processing... [%i] [AC %i] Average Melee Hit %i | Pct Mitigated %.2f ", j, loop_add_ac, mean_dmg, tmp_pct_mitigated); + } if (interval > 0 && tmp_pct_mitigated >= pct_mitigation) + { end = 1; + } else if (interval < 0 && tmp_pct_mitigated <= pct_mitigation) + { end = 1; + } - else if (interval < 0 && mean_dmg == minhit) - end = 2; + else if (interval < 0 && mean_dmg == min_damage) + { + Message(0, "[#Tune][WARNING] Mitigation can not be further decreased due to minium hit value (%i). Minium mitigation ( %.0f ) pct", min_damage, tmp_pct_mitigated); + base_pct_mitigation = tmp_pct_mitigated; + end = 1; + } - if (end >= 1){ + if (end >= 1) { - defender->Tune_MeleeMitigation(this, attacker, damage, minhit, nullptr,Msg,0,atk_override, add_ac, 0); - - if (end == 2) - Message(0, "#Tune - [WARNING] Mitigation can not be further decreased due to minium hit value (%i).",minhit); + Message(0, "###################RESULTS###################"); - if (defender->IsNPC()){ - Message(Chat::LightGray, "#Tune - Recommended NPC AC ADJUSTMENT ( %i ) on ' %s ' for an average mitigation of (+ %.0f) pct from attacker ' %s '.",add_ac,defender->GetCleanName(), pct_mitigation, attacker->GetCleanName()); - Message(0, "#SET: [NPC Attack STAT] = [%i]",add_ac + defender->CastToNPC()->GetRawAC()); + if (atk_override) { + Message(0, "[#Tune] ATK STAT OVERRRIDE. This is the amount of AC adjustment needed if this attacker had ( %i ) raw ATK stat", atk_override); } - if (defender->IsClient()){ - Message(Chat::LightGray, "#Tune - Recommended CLIENT AC ADJUSTMENT ( %i ) on ' %s ' for an average mitigation of (+ %.0f) pct from attacker ' %s '.",add_ac,defender->GetCleanName(), pct_mitigation, attacker->GetCleanName()); - Message(0, "#Modify (+/-): [Client AC STAT/SE_AC(1)] [%i]",add_ac); + + if (defender->IsNPC()) + { + + Message(0, "[#Tune] Recommended NPC RAW AC ADJUSTMENT ( %i ) on ' %s ' to acheive an average mitigation of ( %.0f pct ) verse ' %s '", loop_add_ac, defender->GetCleanName(), base_pct_mitigation, attacker->GetCleanName()); + Message(0, "[#Tune] SET NPC 'AC' stat value = [ %i ]", loop_add_ac + defender->CastToNPC()->GetRawAC()); + Message(0, "###################COMPLETE###################"); + } + if (defender->IsClient()) + { + Message(0, "[#Tune] Recommended CLIENT AC ADJUSTMENT ( %i ) on ' %s ' to acheive an average mitigation of ( %.0f pct ) verse ' %s '", loop_add_ac, defender->GetCleanName(), base_pct_mitigation, attacker->GetCleanName()); + + if (loop_add_ac >= 0) { + Message(0, "[#Tune] MODIFY Client Item AC or Spell Effect AC by [+ %i ]", loop_add_ac); + } + else { + Message(0, "[#Tune] MODIFY Client Item AC or Spell Effect AC by [ %i ]", loop_add_ac); + } + + Message(0, "###################COMPLETE###################"); } return; } - - - add_ac = add_ac + interval; + loop_add_ac = loop_add_ac + interval; } - Message(0, "#Tune - Error: Unable to find desired result for (%.0f) pct - Increase interval (%i) AND/OR max loop value (%i) and run again.", pct_mitigation, interval, max_loop); - Message(0, "#Tune - Parse ended at AC ADJUSTMENT ( %i ) at average mitigation of (%.0f) / (%.0f) pct.",add_ac,tmp_pct_mitigated / pct_mitigation); + Message(0, "###################ABORT#######################"); + Message(0, "[#Tune] - Error: Unable to find desired result for ( %.0f pct ) - Increase interval ( %i ) AND/OR max loop value ( %i ) and run again.", pct_mitigation, interval, max_loop); + Message(0, "[#Tune] - Parse ended at an AC ADJUSTMENT of ( %i ) on ' %s ' to acheive an average mitigation of ( %.0f pct ) verse ' %s '", loop_add_ac, attacker->GetCleanName(), tmp_pct_mitigated, defender->GetCleanName()); + Message(0, "###################COMPLETE###################"); + return; } -uint32 Mob::Tune_GetMeanDamage(Mob* GM, Mob *attacker, int32 damage, int32 minhit, ExtraAttackOptions *opts, int Msg, - int ac_override, int atk_override, int add_ac, int add_atk) +void Mob::TuneGetATKByPctMitigation(Mob* defender, Mob *attacker, float pct_mitigation, int interval, int max_loop, int ac_override, int Msg) +{ + Message(0, " "); + /* + Find the amount of ATK stat that has to be added/subtracted from ATTACKER to reach a specific average mitigation value based on DEFENDERS's mitigation statistics. + Can use ac_override to find the value verse a hypothetical amount of worn AC + */ + if (pct_mitigation > 100 || pct_mitigation < 0) { + Message(0, "[#Tune] - Processing... Abort! Mitigation value out of range ( %.0f ) pct. Must be between 0-100.", pct_mitigation); + return; + } + if (!defender) { + Message(0, "[#Tune] - Processing... Abort! No Defender found."); + return; + } + if (!attacker) { + Message(0, "[#Tune] - Processing... Abort! No Attacker found."); + return; + } + if (defender->GetID() == attacker->GetID()) { + Message(0, "[#Tune] - Processing... Abort! Error Attacker can not be the Defender."); + return; + } + + int max_damage = 0; + int min_damage = 0; + int mean_dmg = 0; + float tmp_pct_mitigated = 0.0f; + float base_pct_mitigation = pct_mitigation; + int loop_add_atk = 0; + int end = 0; + int value = 0; + + + max_damage = attacker->TuneClientGetMaxDamage(defender); + min_damage = attacker->TuneClientGetMinDamage(defender, max_damage); + + if (!max_damage) + { + Message(0, "#Tune - Processing... Abort! Damage not found! [MaxDMG %i MinDMG %i]", max_damage, min_damage); + return; + } + + //Obtain baseline mitigation for current stats + mean_dmg = attacker->TuneClientGetMeanDamage(defender, ac_override); + tmp_pct_mitigated = 100.0f - (static_cast(mean_dmg) * 100.0f / static_cast(max_damage)); + + Message(0, "###################START###################"); + Message(0, "[#Tune] DFENDER Name: %s", defender->GetCleanName()); + Message(0, "[#Tune] DEFENDER AC Mitigation pct: %.0f pct ", round(tmp_pct_mitigated)); + Message(0, "[#Tune] DEFENDER Total AC: %i ", defender->TuneACSum(false, ac_override)); + Message(0, "[#Tune] ATTACKER Name: %s", attacker->GetCleanName()); + Message(0, "[#Tune] ATTACKER Max Damage %i Min Damage %i", max_damage, min_damage); + Message(0, "[#Tune] ATTACKER Total Offense: %i ", TuneGetOffense(defender, attacker)); + Message(0, "##########################################"); + Message(0, "[#Tune] Begin Parse [Interval %i Max Loop Iterations %i]", interval, max_loop); + + if (tmp_pct_mitigated < pct_mitigation) { + interval = interval * -1; + Message(0, "[#Tune] NOTE: Attackers 'ATK' must be LOWERED due to defenders AC Mitigation ( %.0f pct ) being less than the desired ( %.0f pct )", tmp_pct_mitigated, pct_mitigation); + } + + Message(0, "[#Tune] Processing... Find ATK on attacker for defender to have Mitigation of ( %.0f pct ) agianst this attacker.", pct_mitigation); + + for (int j = 0; j < max_loop; j++) + { + mean_dmg = attacker->TuneClientGetMeanDamage(defender, ac_override, 0, 0, loop_add_atk); + tmp_pct_mitigated = 100.0f - (static_cast(mean_dmg) * 100.0f / static_cast(max_damage)); + + if (Msg >= 3) + { + Message(0, "[#Tune] - Processing... [%i] [ATK %i] Average Melee Hit %i | Pct Mitigated %.2f ", j, loop_add_atk, mean_dmg, tmp_pct_mitigated); + } + + if (interval > 0 && tmp_pct_mitigated <= pct_mitigation) { + end = 1; + } + else if (interval < 0 && tmp_pct_mitigated >= pct_mitigation) { + end = 1; + } + + else if (interval < 0 && mean_dmg == min_damage) + { + Message(0, "[#Tune] [WARNING] Mitigation can not be further decreased due to minium hit value ( %i ). Minium mitigation ( %.0f pct )", min_damage, tmp_pct_mitigated); + base_pct_mitigation = tmp_pct_mitigated; + end = 1; + } + + if (end >= 1) { + + Message(0, "###################RESULTS###################"); + + if (ac_override) { + Message(0, "[#Tune] AC STAT OVERRRIDE. This is the amount of ATK adjustment needed if this defender had ( %i ) raw AC stat", ac_override); + } + + if (attacker->IsNPC()) { + Message(0, "[#Tune] Recommended NPC ATK ADJUSTMENT ( %i ) on ' %s ' so that their hits on average are mitgiated by ( %.0f pct ) verse ' %s '. ", loop_add_atk, attacker->GetCleanName(), base_pct_mitigation, defender->GetCleanName()); + Message(0, "[#Tune] SET NPC 'ATK' stat value = [ %i ]", loop_add_atk + defender->CastToNPC()->ATK); + Message(0, "###################COMPLETE###################"); + } + if (attacker->IsClient()) { + Message(0, "[#Tune] Recommended CLIENT ATK ADJUSTMENT ( %i ) on ' %s ' so that their hits on average are mitigated by ( %.0f pct ) verse ' %s '. ", loop_add_atk, attacker->GetCleanName(), base_pct_mitigation, defender->GetCleanName()); + + if (loop_add_atk >= 0) { + Message(0, "[#Tune] MODIFY Client Item ATK or Spell Effect ATK by [+ %i ]", loop_add_atk); + } + else { + Message(0, "[#Tune] MODIFY Client Item ATK or Spell Effect ATK by [ %i ]", loop_add_atk); + } + + Message(0, "###################COMPLETE###################"); + } + + return; + } + + loop_add_atk = loop_add_atk + interval; + } + + Message(0, "###################ABORT#######################"); + Message(0, "[#Tune] - Error: Unable to find desired result for ( %.0f pct ) - Increase interval ( %i ) AND/OR max loop value ( %i ) and run again.", pct_mitigation, interval, max_loop); + Message(0, "[#Tune] - Parse ended at an ATK ADJUSTMENT of ( %i ) on ' %s ' so that their hits on average are mitigated by ( %.0f pct ) verse ' %s '.", loop_add_atk, attacker->GetCleanName(), tmp_pct_mitigated, defender->GetCleanName()); + Message(0, "###################COMPLETE###################"); + return; +} + +void Mob::TuneGetAvoidanceByHitChance(Mob* defender, Mob *attacker, float hit_chance, int interval, int max_loop, int accuracy_override, int Msg) +{ + Message(0, " "); + if (hit_chance > 100 || hit_chance < 0) { + Message(0, "[#Tune] - Processing... Abort! Hit Chance value out of range ( %.0f ) pct. Must be between 0-100.", hit_chance); + return; + } + if (!defender) { + Message(0, "[#Tune] - Processing... Abort! No Defender found."); + return; + } + if (!attacker) { + Message(0, "[#Tune] - Processing... Abort! No Attacker found."); + return; + } + if (defender->GetID() == attacker->GetID()) { + Message(0, "[#Tune] - Processing... Abort! Error Attacker can not be the Defender."); + return; + } + + int loop_add_avoid = 0; + float tmp_hit_chance = 0.0f; + bool end = false; + int base_avoidance = TuneGetAvoidance(defender, attacker); + + tmp_hit_chance = TuneGetHitChance(defender, attacker, 0, accuracy_override); + + Message(0, "###################START###################"); + Message(0, "[#Tune] DEFENDER Name: %s", defender->GetCleanName()); + Message(0, "[#Tune] DEFENDER Chance to be missed: %.0f pct", (100.0f - round(tmp_hit_chance))); + Message(0, "[#Tune] DEFENDER Avoidance: %i ", TuneGetAvoidance(defender, attacker)); + Message(0, "[#Tune] ATTACKER Name: %s", attacker->GetCleanName()); + Message(0, "[#Tune] ATTACKER Chance to hit: %.0f pct", round(tmp_hit_chance)); + Message(0, "[#Tune] ATTACKER Accuracy: %i ", TuneGetAccuracy(defender, attacker, accuracy_override)); + Message(0, "##########################################"); + Message(0, "[#Tune] Begin Parse [Interval %i Max Loop Iterations %i]", interval, max_loop); + + if (tmp_hit_chance < hit_chance) { + interval = interval * -1; + Message(0, "[#Tune] NOTE: Defenders 'AVOIDANCE' must be LOWERED due to defenders ( %.0f pct ) chance to be hit being less than the desired ( %.0f pct )", tmp_hit_chance, hit_chance); + } + + Message(0, "[#Tune] - Processing... Find Avoidance needed on defender for a ( %.0f pct ) hit chance from attacker. Base attacker hit chance ( %.0f pct ). ", hit_chance, tmp_hit_chance); + + for (int j = 0; j < max_loop; j++) + { + tmp_hit_chance = TuneGetHitChance(defender, attacker,0, accuracy_override, loop_add_avoid,0); + + if (Msg >= 3) + { + Message(0, "[#Tune] - Processing... [%i] AVOIDANCE %i | Hit Chance %.2f ", j, loop_add_avoid, tmp_hit_chance); + } + + if (interval > 0 && tmp_hit_chance <= hit_chance) + { + end = true; + } + else if (interval < 0 && tmp_hit_chance >= hit_chance) + { + end = true; + } + + if (end) { + + Message(0, "###################RESULTS###################"); + + if (accuracy_override) { + Message(0, "[#Tune] ACCURACY STAT OVERRRIDE. This is the amount of AVOIDANCE adjustment needed if this attacker had ( %i ) raw ACCURACY stat", accuracy_override); + } + + if (defender->IsNPC()) { + Message(0, "[#Tune] Recommended NPC AVOIDANCE ADJUSTMENT of ( %i ) on ' %s ' will result in ' %s ' having a ( %.0f pct) hit chance.", loop_add_avoid, defender->GetCleanName(), attacker->GetCleanName(), hit_chance); + Message(0, "[#Tune] SET NPC 'AVOIDANCE' stat value = [ %i ]", loop_add_avoid + defender->CastToNPC()->GetAvoidanceRating()); + Message(0, "###################COMPLETE###################"); + } + else if (defender->IsClient()) { + Message(0, "[#Tune] Recommended CLIENT AVOIDANCE ADJUSTMENT of ( %i ) on ' %s ' will result in ' %s ' having a ( %.0f pct) hit chance.", loop_add_avoid, defender->GetCleanName(), attacker->GetCleanName(), hit_chance); + + int final_avoidance = TuneGetAvoidance(defender, attacker, 0, loop_add_avoid); + int evasion_bonus = TuneCalcEvasionBonus(final_avoidance, base_avoidance); + + if (loop_add_avoid >= 0) { + Message(0, "[#Tune] OPTION1: MODIFY Client Heroic AGI or Avoidance Mod2 stat by [+ %i ]", loop_add_avoid); + Message(0, "[#Tune] OPTION2: Give CLIENT an evasion bonus using SPA 172 Evasion SE_AvoidMeleeChance from (spells/items/aa) of [+ %i pct ]", evasion_bonus); + + } + else { + Message(0, "[#Tune] OPTION1: MODIFY Client Heroic AGI or Avoidance Mod2 stat by [ %i ]", loop_add_avoid); + Message(0, "[#Tune] OPTION2: Give CLIENT an evasion bonus using SPA 172 Evasion SE_AvoidMeleeChance from (spells/items/aa) of [ %i pct ]", evasion_bonus); + } + + Message(0, "###################COMPLETE###################"); + } + + return; + } + + loop_add_avoid = loop_add_avoid + interval; + } + + Message(0, "###################ABORT#######################"); + Message(0, "[#Tune] Error: Unable to find desired result for ( %.0f pct) - Increase interval (%i) AND/OR max loop value (%i) and run again.", hit_chance, interval, max_loop); + Message(0, "[#Tune] Parse ended at AVOIDANCE ADJUSTMENT ( %i ) on ' %s ' will result in ' %s ' having a ( %.0f pct) hit chance.", loop_add_avoid, defender->GetCleanName(), hit_chance, attacker->GetCleanName()); + Message(0, "###################COMPLETE###################"); +} + +void Mob::TuneGetAccuracyByHitChance(Mob* defender, Mob *attacker, float hit_chance, int interval, int max_loop, int avoidance_override, int Msg) +{ + Message(0, " "); + if (hit_chance > 100 || hit_chance < 0) { + Message(0, "[#Tune] - Processing... Abort! Hit Chance value out of range ( %.0f ) pct. Must be between 0-100.", hit_chance); + return; + } + if (!defender) { + Message(0, "[#Tune] - Processing... Abort! No Defender found."); + return; + } + if (!attacker) { + Message(0, "[#Tune] - Processing... Abort! No Attacker found."); + return; + } + if (defender->GetID() == attacker->GetID()) { + Message(0, "[#Tune] - Processing... Abort! Error Attacker can not be the Defender."); + return; + } + + int loop_add_accuracy = 0; + float tmp_hit_chance = 0.0f; + bool end = false; + + tmp_hit_chance = TuneGetHitChance(defender, attacker, avoidance_override); + + Message(0, "###################START###################"); + Message(0, "[#Tune] DEFENDER Name: %s", defender->GetCleanName()); + Message(0, "[#Tune] DEFENDER Chance to be missed: %.0f pct", (100.0f - round(tmp_hit_chance))); + Message(0, "[#Tune] DEFENDER Avoidance: %i ", TuneGetAvoidance(defender, attacker, avoidance_override)); + Message(0, "[#Tune] ATTACKER Name: %s", attacker->GetCleanName()); + Message(0, "[#Tune] ATTACKER Chance to hit: %.0f pct", round(tmp_hit_chance)); + Message(0, "[#Tune] ATTACKER Accuracy: %i ", TuneGetAccuracy(defender, attacker)); + Message(0, "##########################################"); + Message(0, "[#Tune] Begin Parse [Interval %i Max Loop Iterations %i]", interval, max_loop); + + + if (tmp_hit_chance > hit_chance) { + interval = interval * -1; + Message(0, "[#Tune] NOTE: Attackers 'ACCURACY' must be LOWERED due to attackers ( %.0f pct ) chance to hit being less than the desired ( %.0f pct )", tmp_hit_chance, hit_chance); + } + + Message(0, "[#Tune] - Processing... Find Accuracy needed on attacker for a ( %.0f pct ) hit chance on defender. Base attacker hit chance ( %.0f pct ). ", hit_chance, tmp_hit_chance); + + for (int j = 0; j < max_loop; j++) + { + + if (Msg >= 3) + { + Message(0, "[#Tune] - Processing... [%i] ACCURACY %i | Hit Chance %.2f ", j, loop_add_accuracy, tmp_hit_chance); + } + + if (interval > 0 && tmp_hit_chance >= hit_chance) + { + end = true; + } + + else if (interval < 0 && tmp_hit_chance <= hit_chance) + { + end = true; + } + + if (end) { + + Message(0, "###################RESULTS###################"); + + if (avoidance_override) { + Message(0, "[#Tune] AVOIDANCE STAT OVERRRIDE. This is the amount of ACCURACY adjustment needed if this defender had ( %i ) raw AVOIDANCE stat", avoidance_override); + } + + if (defender->IsNPC()) { + Message(0, "[#Tune] Recommended NPC ACCURACY ADJUSTMENT of ( %i ) on ' %s ' will result in ( %.0f pct ) chance to hit ' %s '.", loop_add_accuracy, defender->GetCleanName(), hit_chance, attacker->GetCleanName()); + Message(0, "[#Tune] SET NPC 'ACCURACY' stat value = [ %i ]", loop_add_accuracy + defender->CastToNPC()->GetAccuracyRating()); + Message(0, "###################COMPLETE###################"); + } + else if (defender->IsClient()) { + Message(0, "[#Tune] Recommended CLIENT AVOIDANCE ADJUSTMENT of ( %i ) on %s ' will result in ( %.0f pct ) chance to hit ' %s '.", loop_add_accuracy, defender->GetCleanName(), hit_chance, attacker->GetCleanName()); + + if (loop_add_accuracy >= 0) { + Message(0, "[#Tune] OPTION1: MODIFY Client Avoidance Mod2 stat or SPA 216 Melee Accuracy (spells/items/aa) [+ %i ]", loop_add_accuracy); + + } + else { + Message(0, "[#Tune] OPTION1: MODIFY Client Avoidance Mod2 stat or SPA 216 Melee Accuracy (spells/items/aa) [ %i ]", loop_add_accuracy); + } + + Message(0, "###################COMPLETE###################"); + } + + return; + } + + loop_add_accuracy = loop_add_accuracy + interval; + } + + Message(0, "###################ABORT#######################"); + Message(0, "[#Tune] Error: Unable to find desired result for ( %.0f pct) - Increase interval (%i) AND/OR max loop value (%i) and run again.", hit_chance, interval, max_loop); + Message(0, "[#Tune] Parse ended at ACCURACY ADJUSTMENT of ( %i ) on ' %s ' will result in ( %.0f pct ) chance to hit ' %s '.", loop_add_accuracy, defender->GetCleanName(), hit_chance, attacker->GetCleanName()); + Message(0, "###################COMPLETE###################"); +} + +/* + Tune support functions +*/ + +int Mob::TuneClientGetMeanDamage(Mob* other, int ac_override, int atk_override, int add_ac, int add_atk) { uint32 total_damage = 0; int loop_max = 1000; - for (int i=0; i < loop_max ; i++) + for (int i = 0; i < loop_max; i++) { - total_damage += Tune_MeleeMitigation(GM, attacker, damage, minhit, nullptr,0,ac_override, atk_override, add_ac, add_atk); - } - - return(total_damage/loop_max); -} - -int32 Mob::Tune_MeleeMitigation(Mob* GM, Mob *attacker, int32 damage, int32 minhit, ExtraAttackOptions *opts, int Msg, - int ac_override, int atk_override, int add_ac, int add_atk) -{ - if (damage <= 0) - return 0; - - Mob* defender = this; - float aa_mit = (aabonuses.CombatStability + itembonuses.CombatStability + - spellbonuses.CombatStability) / 100.0f; - - if (Msg){ - - GM->Message(Chat::White, "######### Melee Mitigation Report: Start [Detail Level %i]#########", Msg); - GM->Message(Chat::White, "#ATTACKER: %s", attacker->GetCleanName()); - GM->Message(Chat::White, "#DEFENDER: %s", defender->GetCleanName()); - } - - if (RuleB(Combat, UseIntervalAC)) { - float softcap = (GetSkill(EQ::skills::SkillDefense) + GetLevel()) * - RuleR(Combat, SoftcapFactor) * (1.0 + aa_mit); - float mitigation_rating = 0.0; - float attack_rating = 0.0; - int shield_ac = 0; - int armor = 0; - float weight = 0.0; - - if (Msg >= 2){ - GM->Message(Chat::White, " "); - GM->Message(Chat::White, "### Calculate Mitigation Rating ###"); - if (aabonuses.CombatStability) - GM->Message(Chat::White, "# %i #### DEFENDER SE_CombatStability(259) AA Bonus", aabonuses.CombatStability); - if (spellbonuses.CombatStability) - GM->Message(Chat::White, "# %i #### DEFENDER SE_CombatStability(259) Spell Bonus", spellbonuses.CombatStability); - if (itembonuses.CombatStability) - GM->Message(Chat::White, "# %i #### DEFENDER SE_CombatStability(259) Worn Bonus", itembonuses.CombatStability); - - GM->Message(Chat::White, "# %.2f #### DEFENDER Base Soft Cap", softcap); - } - - float monkweight = RuleI(Combat, MonkACBonusWeight); - monkweight = mod_monk_weight(monkweight, attacker); - if (IsClient()) { - armor = CastToClient()->GetRawACNoShield(shield_ac) + add_ac; - weight = (CastToClient()->CalcCurrentWeight() / 10.0); - - if (ac_override) - armor = ac_override; - - if (Msg >=2 ){ - GM->Message(Chat::White, "# %i #### DEFENDER AC Equiped/Worn Bonus", itembonuses.AC); - GM->Message(Chat::White, "# %i #### DEFENDER SE_ArmorClass(1) AA Bonus", aabonuses.AC); - GM->Message(Chat::White, "# %i #### DEFENDER SE_ArmorClass(1) Spell Bonus", spellbonuses.AC); - GM->Message(Chat::White, "# %i #### DEFENDER Shield AC", shield_ac); - GM->Message(Chat::White, "# %i #### DEFENDER Total Client Armor - NO shield", armor); - } - - } else if (IsNPC()) { - armor = CastToNPC()->GetRawAC() + add_ac; - - if (ac_override) - armor = ac_override; - - if (Msg >=2 ){ - GM->Message(Chat::White, "# %i #### DEFENDER AC Equiped/Worn Bonus", itembonuses.AC); - GM->Message(Chat::White, "# %i #### DEFENDER SE_ArmorClass(1) Spell Bonus", spellbonuses.AC); - GM->Message(Chat::White, "# %i #### DEFENDER NPC AC Stat", CastToNPC()->GetRawAC()); - } - - int PetACBonus = 0; - - if (!IsPet()){ - armor = (armor / RuleR(Combat, NPCACFactor)); - if (Msg >=2 ) - GM->Message(Chat::White, "# %i #### DEFENDER NPC Armor after RuleR(Combat, NPCACFactor) %.2f", armor, RuleR(Combat, NPCACFactor)); - } - - Mob *owner = nullptr; - if (IsPet()) - owner = GetOwner(); - else if ((CastToNPC()->GetSwarmOwner())) - owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner()); - - if (owner){ - PetACBonus = owner->aabonuses.PetMeleeMitigation + owner->itembonuses.PetMeleeMitigation + owner->spellbonuses.PetMeleeMitigation; - - if (Msg >=2 ){ - if (owner->aabonuses.PetMeleeMitigation) - GM->Message(Chat::White, "# %i #### DEFENDER Pet Owner SE_PetMeleeMitigation(379) AA Bonus", owner->aabonuses.PetMeleeMitigation); - if (owner->spellbonuses.PetMeleeMitigation) - GM->Message(Chat::White, "# %i #### DEFENDER Pet Owner SE_PetMeleeMitigation(379) Spell Bonus",owner->spellbonuses.PetMeleeMitigation); - if (owner->itembonuses.PetMeleeMitigation) - GM->Message(Chat::White, "# %i #### DEFENDER Pet Owner SE_PetMeleeMitigation(379) Worn Bonus", owner->itembonuses.PetMeleeMitigation); - } - } - - armor += spellbonuses.AC + itembonuses.AC + PetACBonus + 1; - - if (Msg >= 2) - GM->Message(Chat::White, "# %i #### DEFENDER NPC Total Base Armor",armor); + total_damage += TuneClientAttack(other, true, true, 10000, ac_override, atk_override, add_ac, add_atk); } - - if (opts) { - armor *= (1.0f - opts->armor_pen_percent); - armor -= opts->armor_pen_flat; + else { + total_damage += TuneNPCAttack(other, true, true, 10000, ac_override, atk_override, add_ac, add_atk); } - - if (RuleB(Combat, OldACSoftcapRules)) { - if (GetClass() == WIZARD || GetClass() == MAGICIAN || - GetClass() == NECROMANCER || GetClass() == ENCHANTER) - softcap = RuleI(Combat, ClothACSoftcap); - else if (GetClass() == MONK && weight <= monkweight) - softcap = RuleI(Combat, MonkACSoftcap); - else if(GetClass() == DRUID || GetClass() == BEASTLORD || GetClass() == MONK) - softcap = RuleI(Combat, LeatherACSoftcap); - else if(GetClass() == SHAMAN || GetClass() == ROGUE || - GetClass() == BERSERKER || GetClass() == RANGER) - softcap = RuleI(Combat, ChainACSoftcap); - else - softcap = RuleI(Combat, PlateACSoftcap); - } - softcap += shield_ac; - armor += shield_ac; - - if (RuleB(Combat, OldACSoftcapRules)) - softcap += (softcap * (aa_mit * RuleR(Combat, AAMitigationACFactor))); - if (armor > softcap) { - int softcap_armor = armor - softcap; - if (RuleB(Combat, OldACSoftcapRules)) { - if (GetClass() == WARRIOR) - softcap_armor = softcap_armor * RuleR(Combat, WarriorACSoftcapReturn); - else if (GetClass() == SHADOWKNIGHT || GetClass() == PALADIN || - (GetClass() == MONK && weight <= monkweight)) - softcap_armor = softcap_armor * RuleR(Combat, KnightACSoftcapReturn); - else if (GetClass() == CLERIC || GetClass() == BARD || - GetClass() == BERSERKER || GetClass() == ROGUE || - GetClass() == SHAMAN || GetClass() == MONK) - softcap_armor = softcap_armor * RuleR(Combat, LowPlateChainACSoftcapReturn); - else if (GetClass() == RANGER || GetClass() == BEASTLORD) - softcap_armor = softcap_armor * RuleR(Combat, LowChainLeatherACSoftcapReturn); - else if (GetClass() == WIZARD || GetClass() == MAGICIAN || - GetClass() == NECROMANCER || GetClass() == ENCHANTER || - GetClass() == DRUID) - softcap_armor = softcap_armor * RuleR(Combat, CasterACSoftcapReturn); - else - softcap_armor = softcap_armor * RuleR(Combat, MiscACSoftcapReturn); - } else { - if (GetClass() == WARRIOR) - softcap_armor *= RuleR(Combat, WarACSoftcapReturn); - else if (GetClass() == PALADIN || GetClass() == SHADOWKNIGHT) - softcap_armor *= RuleR(Combat, PalShdACSoftcapReturn); - else if (GetClass() == CLERIC || GetClass() == RANGER || - GetClass() == MONK || GetClass() == BARD) - softcap_armor *= RuleR(Combat, ClrRngMnkBrdACSoftcapReturn); - else if (GetClass() == DRUID || GetClass() == NECROMANCER || - GetClass() == WIZARD || GetClass() == ENCHANTER || - GetClass() == MAGICIAN) - softcap_armor *= RuleR(Combat, DruNecWizEncMagACSoftcapReturn); - else if (GetClass() == ROGUE || GetClass() == SHAMAN || - GetClass() == BEASTLORD || GetClass() == BERSERKER) - softcap_armor *= RuleR(Combat, RogShmBstBerACSoftcapReturn); - else - softcap_armor *= RuleR(Combat, MiscACSoftcapReturn); - } - - - armor = softcap + softcap_armor; - if (Msg >= 2) - GM->Message(Chat::White, "# %i #### DEFENDER Final Armor [Soft Cap %i Soft Cap Armor %i]",armor, softcap,softcap_armor); - } - int tmp_armor = armor; - if (GetClass() == WIZARD || GetClass() == MAGICIAN || - GetClass() == NECROMANCER || GetClass() == ENCHANTER){ - mitigation_rating = ((GetSkill(EQ::skills::SkillDefense) + itembonuses.HeroicAGI / 10) / 4.0) + armor + 1; - if (Msg >= 2) - GM->Message(Chat::White, "# + %.2f #### DEFENDER Armor Bonus [Defense Skill %i Heroic Agi %i]", mitigation_rating - tmp_armor, GetSkill(EQ::skills::SkillDefense), itembonuses.HeroicAGI); - } - else{ - mitigation_rating = ((GetSkill(EQ::skills::SkillDefense) + itembonuses.HeroicAGI / 10) / 3.0) + (armor * 1.333333) + 1; - if (Msg >= 2) - GM->Message(Chat::White, "# + %.2f #### DEFENDER Armor Bonus [Defense Skill %i Heroic Agi %i]", mitigation_rating - tmp_armor, GetSkill(EQ::skills::SkillDefense), itembonuses.HeroicAGI); - - } - mitigation_rating *= 0.847; - - if (Msg >= 1) - GM->Message(Chat::White, "# %.2f #### DEFENDER Final Mitigation Rating", mitigation_rating); - - - if (Msg >= 2){ - GM->Message(Chat::White, " "); - GM->Message(Chat::White, "### Mitigation Bonus Effects ###"); - if (itembonuses.MeleeMitigation) - GM->Message(Chat::White, "# %i #### DEFENDER Item Mod2 Shielding", itembonuses.MeleeMitigation); - if (aabonuses.MeleeMitigationEffect) - GM->Message(Chat::White, "# %i #### DEFENDER SE_MeleeMitigation(168) AA Bonus", aabonuses.MeleeMitigationEffect); - if (spellbonuses.MeleeMitigationEffect) - GM->Message(Chat::White, "# %i #### DEFENDER SE_MeleeMitigation(168) Spell Bonus", spellbonuses.MeleeMitigationEffect); - if (itembonuses.MeleeMitigationEffect) - GM->Message(Chat::White, "# %i #### DEFENDER SE_MeleeMitigation(168) Worn Bonus", itembonuses.MeleeMitigationEffect); - } - - mitigation_rating = mod_mitigation_rating(mitigation_rating, attacker); - - if (attacker->IsClient()){ - if (atk_override) - attack_rating = (atk_override + ((attacker->GetSTR() - 66) * 0.9) + (attacker->GetSkill(EQ::skills::SkillOffense)*1.345)); - else - attack_rating = ((attacker->CastToClient()->CalcATK() + add_atk) + ((attacker->GetSTR() - 66) * 0.9) + (attacker->GetSkill(EQ::skills::SkillOffense)*1.345)); - - } - else{ - if (atk_override) - attack_rating = (atk_override + (attacker->GetSkill(EQ::skills::SkillOffense)*1.345) + ((attacker->GetSTR() - 66) * 0.9)); - else - attack_rating = ((attacker->GetATK() + add_atk) + (attacker->GetSkill(EQ::skills::SkillOffense)*1.345) + ((attacker->GetSTR() - 66) * 0.9)); - } - - attack_rating = attacker->mod_attack_rating(attack_rating, this); - - if (Msg >= 2){ - GM->Message(Chat::White, " "); - GM->Message(Chat::White, "### Calculate Attack Rating ###"); - if (attacker->IsClient()){ - GM->Message(Chat::White, "# %i #### ATTACKER Worn/Equip ATK Bonus", attacker->itembonuses.ATK); - GM->Message(Chat::White, "# %i #### ATTACKER SE_ATK(2) AA Bonus", attacker->aabonuses.ATK); - GM->Message(Chat::White, "# %i #### ATTACKER SE_ATK(2) spell Bonus", attacker->spellbonuses.ATK); - GM->Message(Chat::White, "# %i #### ATTACKER Leadership Bonus", attacker->CastToClient()->GroupLeadershipAAOffenseEnhancement()); - GM->Message(Chat::White, "# %i #### ATTACKER Worn/Equip ATK Bonus", attacker->itembonuses.ATK); - GM->Message(Chat::White, "# %i #### ATTACKER Worn/Equip ATK Bonus", attacker->itembonuses.ATK); - GM->Message(Chat::White, "# %.2f #### ATTACKER Strength Stat ATK Bonus [Stat Amt: %i]", ((attacker->GetSTR()-66) * 0.9),attacker->GetSTR()); - GM->Message(Chat::White, "# %.2f #### ATTACKER Offensive Skill ATK Bonus [Stat Amt: %i]", (attacker->GetSkill(EQ::skills::SkillOffense)*1.345), attacker->GetSkill(EQ::skills::SkillOffense)); - } - - else{ - GM->Message(Chat::White, "# %i #### ATTACKER Worn/Equip ATK Bonus", attacker->itembonuses.ATK); - GM->Message(Chat::White, "# %i #### ATTACKER SE_ATK(2) spell Bonus", attacker->spellbonuses.ATK); - GM->Message(Chat::White, "# %i #### ATTACKER NPC ATK Stat", attacker->CastToNPC()->ATK); - GM->Message(Chat::White, "# %.2f #### ATTACKER Strength Stat ATK Bonus [Stat Amt: %i]", ((attacker->GetSTR()-66) * 0.9),attacker->GetSTR()); - GM->Message(Chat::White, "# %.2f #### ATTACKER Offensive Skill ATK Bonus [Stat Amt: %i]", (attacker->GetSkill(EQ::skills::SkillOffense)*1.345), attacker->GetSkill(EQ::skills::SkillOffense)); - } - } - - if (Msg >= 1){ - GM->Message(Chat::White, "# %.2f #### ATTACKER Final Attack Rating", attack_rating); - GM->Message(Chat::White, "######### Melee Mitigation Report: Complete #########", Msg); - } - - - //damage = GetMeleeMitDmg(attacker, damage, minhit, mitigation_rating, attack_rating); - } - - if (damage < 0) - damage = 0; - - return damage; -} - -// This is called when the Mob is the one being hit -int32 Mob::Tune_GetMeleeMitDmg(Mob* GM, Mob *attacker, int32 damage, int32 minhit, - float mit_rating, float atk_rating) -{ - float d = 10.0; - float mit_roll = zone->random.Real(0, mit_rating); - float atk_roll = zone->random.Real(0, atk_rating); - - if (atk_roll > mit_roll) { - float a_diff = atk_roll - mit_roll; - float thac0 = atk_rating * RuleR(Combat, ACthac0Factor); - float thac0cap = attacker->GetLevel() * 9 + 20; - if (thac0 > thac0cap) - thac0 = thac0cap; - - d -= 10.0 * (a_diff / thac0); - } else if (mit_roll > atk_roll) { - float m_diff = mit_roll - atk_roll; - float thac20 = mit_rating * RuleR(Combat, ACthac20Factor); - float thac20cap = GetLevel() * 9 + 20; - if (thac20 > thac20cap) - thac20 = thac20cap; - - d += 10.0 * (m_diff / thac20); } - if (d < 0.0) - d = 0.0; - else if (d > 20.0) - d = 20.0; - - float interval = (damage - minhit) / 20.0; - damage -= ((int)d * interval); - - damage -= (minhit * itembonuses.MeleeMitigation / 100); - damage -= (damage * (spellbonuses.MeleeMitigationEffect + itembonuses.MeleeMitigationEffect + aabonuses.MeleeMitigationEffect) / 100); - return damage; + return(total_damage / loop_max); } -// This is called when the Client is the one being hit -int32 Client::Tune_GetMeleeMitDmg(Mob* GM, Mob *attacker, int32 damage, int32 minhit, - float mit_rating, float atk_rating) +int Mob::TuneClientGetMaxDamage(Mob* other) { - if (!attacker->IsNPC() || RuleB(Combat, UseOldDamageIntervalRules)) - return 0; //Mob::GetMeleeMitDmg(attacker, damage, minhit, mit_rating, atk_rating); - int d = 10; - // floats for the rounding issues - float dmg_interval = (damage - minhit) / 19.0; - float dmg_bonus = minhit - dmg_interval; - float spellMeleeMit = (spellbonuses.MeleeMitigationEffect + itembonuses.MeleeMitigationEffect + aabonuses.MeleeMitigationEffect) / 100.0; - if (GetClass() == WARRIOR) - spellMeleeMit += 0.05; - dmg_bonus -= dmg_bonus * (itembonuses.MeleeMitigation / 100.0); - dmg_interval -= dmg_interval * spellMeleeMit; + uint32 max_hit = 0; + uint32 current_hit = 0; + int loop_max = 1000; - float mit_roll = zone->random.Real(0, mit_rating); - float atk_roll = zone->random.Real(0, atk_rating); + for (int i = 0; i < loop_max; i++) + { + if (IsClient()) { + current_hit = TuneClientAttack(other, true, true, 10000, 1, 10000); + } + else { + current_hit = TuneNPCAttack(other, true, true, 10000, 1, 10000); + } - if (atk_roll > mit_roll) { - float a_diff = atk_roll - mit_roll; - float thac0 = atk_rating * RuleR(Combat, ACthac0Factor); - float thac0cap = attacker->GetLevel() * 9 + 20; - if (thac0 > thac0cap) - thac0 = thac0cap; + if (current_hit > max_hit) { + max_hit = current_hit; + } + } + return(max_hit); +} - d += 10 * (a_diff / thac0); - } else if (mit_roll > atk_roll) { - float m_diff = mit_roll - atk_roll; - float thac20 = mit_rating * RuleR(Combat, ACthac20Factor); - float thac20cap = GetLevel() * 9 + 20; - if (thac20 > thac20cap) - thac20 = thac20cap; +int Mob::TuneClientGetMinDamage(Mob* other, int max_hit) +{ + uint32 min_hit = max_hit; + uint32 current_hit = 0; + int loop_max = 1000; - d -= 10 * (m_diff / thac20); + for (int i = 0; i < loop_max; i++) + { + if (IsClient()) { + current_hit = TuneClientAttack(other, true, true, 10000, 10000, 1); + } + else { + current_hit = TuneNPCAttack(other, true, true, 10000, 10000, 1); + } + + if (current_hit < min_hit) { + min_hit = current_hit; + } + } + return(min_hit); +} + +float Mob::TuneGetACMitigationPct(Mob* defender, Mob *attacker) { + + int max_damage = 0; + int min_damage = 0; + + max_damage = attacker->TuneClientGetMaxDamage(defender); + min_damage = attacker->TuneClientGetMinDamage(defender, max_damage); + + if (!max_damage) + { + Message(0, "[#Tune] Calculation Failure. Error: [Mob::TuneGetACMitigationPct] No max damage found"); + return max_damage; } - if (d < 1) - d = 1; - else if (d > 20) - d = 20; + int mean_dmg = attacker->TuneClientGetMeanDamage(defender); + float tmp_pct_mitigated = 100.0f - (static_cast(mean_dmg) * 100.0f / static_cast(max_damage)); - return static_cast((dmg_bonus + dmg_interval * d)); + return tmp_pct_mitigated; } - -int32 Client::GetMeleeDamage(Mob* other, bool GetMinDamage) +int Mob::TuneGetOffense(Mob* defender, Mob *attacker, int atk_override) { + int offense_rating = 0; + if (attacker->IsClient()) { + offense_rating = attacker->TuneClientAttack(defender, true, true, 0, 0, atk_override, 0, 0, true); + } + else { + offense_rating = attacker->TuneNPCAttack(defender, true, true, 0, 0, atk_override, 0, 0, true); + } + return offense_rating; +} + +int Mob::TuneGetAccuracy(Mob* defender, Mob *attacker, int accuracy_override, int add_accuracy) +{ + int accuracy = 0; + if (attacker->IsClient()) { + accuracy = attacker->TuneClientAttack(defender, true, true, 0, 0, 0, 0, 0, false, true,0,accuracy_override,0,add_accuracy); + } + else { + accuracy = attacker->TuneNPCAttack(defender, true, true, 0, 0, 0, 0, 0, false, true, 0, accuracy_override, 0, add_accuracy); + } + return accuracy; +} + +int Mob::TuneGetAvoidance(Mob* defender, Mob *attacker, int avoidance_override, int add_avoidance) +{ + return defender->TuneGetTotalDefense(avoidance_override, add_avoidance); +} + +float Mob::TuneGetHitChance(Mob* defender, Mob *attacker, int avoidance_override, int accuracy_override, int add_avoidance, int add_accuracy) +{ + uint32 hit_count = 0; + uint32 current_hit = 0; + + int loop_max = 2000; + + for (int i = 0; i < loop_max; i++) + { + if (attacker->IsClient()) { + current_hit = attacker->TuneClientAttack(defender, true, false, 0, 0, 0, 0, 0, false, false, avoidance_override, accuracy_override, add_avoidance, add_accuracy); + } + else { + current_hit = attacker->TuneNPCAttack(defender, true, false, 0, 0, 0, 0, 0, false, false, avoidance_override, accuracy_override, add_avoidance, add_accuracy); + } + + if (current_hit > 0) { + hit_count++; + } + } + float chance = (static_cast(hit_count) / 2000.0f) * 100.0f; + return chance; +} + +float Mob::TuneGetAvoidMeleeChance(Mob* defender, Mob *attacker, int type) +{ + uint32 current_hit = 0; + uint32 hit_count = 0; + + /* + -1 - block + -2 - parry + -3 - riposte + -4 - dodge + */ + int loop_max = 3000; + + for (int i = 0; i < loop_max; i++) + { + if (attacker->IsClient()) { + current_hit = attacker->TuneClientAttack(defender, false, true, 0); + } + else { + current_hit = attacker->TuneNPCAttack(defender, false, true, 0); + } + + if (current_hit == type) { + hit_count++; + } + } + float chance = (static_cast(hit_count) / 3000.0f) * 100.0f; + return chance; +} + +int Mob::TuneCalcEvasionBonus(int final_avoidance, int base_avoidance) { + + /* + float eb = static_cast(final_avoidance) / static_cast(base_avoidance); + Shout(" eb %.2f ", eb); + eb = eb * 100.f; + Shout(" eb %.2f ", eb); + eb = eb - 100.0f; + Shout(" eb %.2f ", eb); + return eb; + */ + + int loop_max = 3000; + int evasion_bonus = 10; + int current_avoidance = 0; + + int interval = 5; + + if (base_avoidance > final_avoidance) + { + interval = interval * -1; + } + + for (int i = 0; i < loop_max; i++) + { + current_avoidance = (base_avoidance * (100 + evasion_bonus)) / 100; + + if (interval > 0 && current_avoidance >= final_avoidance) + { + return evasion_bonus; + } + else if (interval < 0 && current_avoidance <= final_avoidance) + { + return evasion_bonus; + } + evasion_bonus = evasion_bonus + interval; + } + return 0; +} + +/* + Calculate from modified attack.cpp functions. +*/ + +int Mob::TuneNPCAttack(Mob* other, bool no_avoid, bool no_hit_chance, int hit_chance_bonus, int ac_override, int atk_override, int add_ac, int add_atk, bool get_offense, bool get_accuracy, + int avoidance_override, int accuracy_override, int add_avoidance, int add_accuracy) +{ + if (!IsNPC()) { + Message(Chat::Red, "#Tune Failure: A null NON NPC object was passed to TuneNPCAttack() for evaluation!"); + return false; + } + + if (!other) { + Message(Chat::Red, "#Tune Failure: A null Mob object was passed to TuneNPCAttack() for evaluation!"); + return false; + } + + //Check that we can attack before we calc heading and face our target + if (!IsAttackAllowed(other)) { + Message(Chat::Red, "#Tune Failure: This NPC can not attack this target!"); + return false; + } + + DamageHitInfo my_hit; + my_hit.skill = EQ::skills::SkillHandtoHand; + my_hit.hand = EQ::invslot::slotPrimary; + my_hit.damage_done = 1; + + my_hit.skill = static_cast(CastToNPC()->GetPrimSkill()); + OffHandAtk(false); + + uint8 otherlevel = other->GetLevel(); + uint8 mylevel = this->GetLevel(); + + otherlevel = otherlevel ? otherlevel : 1; + mylevel = mylevel ? mylevel : 1; + + my_hit.base_damage = CastToNPC()->GetBaseDamage(); + my_hit.min_damage = CastToNPC()->GetMinDamage(); + //int32 hate = my_hit.base_damage + my_hit.min_damage; + + my_hit.offense = Tuneoffense(my_hit.skill, atk_override, add_atk); + if (get_offense) { + return my_hit.offense; + } + my_hit.tohit = TuneGetTotalToHit(my_hit.skill, hit_chance_bonus, accuracy_override, add_accuracy); + if (get_accuracy) { + return my_hit.tohit; + } + + TuneDoAttack(other, my_hit, nullptr, no_avoid, no_hit_chance, ac_override, add_ac, avoidance_override, accuracy_override, add_avoidance, add_accuracy); + + LogCombat("Final damage against [{}]: [{}]", other->GetName(), my_hit.damage_done); + if (other->IsClient() && IsPet() && GetOwner()->IsClient()) { + //pets do half damage to clients in pvp + my_hit.damage_done /= 2; + if (my_hit.damage_done < 1) + my_hit.damage_done = 1; + } + + return my_hit.damage_done; +} + +int Mob::TuneClientAttack(Mob* other, bool no_avoid, bool no_hit_chance, int hit_chance_bonus, int ac_override, int atk_override, int add_ac, int add_atk, bool get_offense, bool get_accuracy, + int avoidance_override, int accuracy_override, int add_avoidance, int add_accuracy) +{ + if (!IsClient()) { + Message(Chat::Red, "#Tune Failure: A null NON CLIENT object was passed to TuneClientAttack() for evaluation!"); + return false; + } + + if (!other) { + Message(Chat::Red, "#Tune Failure: A null Mob object was passed to TuneClientAttack() for evaluation!"); + return false; + } + int Hand = EQ::invslot::slotPrimary; - if (!other) - return 0; - - EQ::ItemInstance* weapon; - weapon = GetInv().GetItem(EQ::invslot::slotPrimary); - - if(weapon != nullptr) { + EQ::ItemInstance* weapon = nullptr; + weapon = CastToClient()->GetInv().GetItem(EQ::invslot::slotPrimary); + OffHandAtk(false); + + if (weapon != nullptr) { if (!weapon->IsWeapon()) { - return(0); + Message(Chat::Red, "#Tune Failure: Attack cancelled, Item %s is not a weapon!", weapon->GetItem()->Name); + return(false); } - } + } - EQ::skills::SkillType skillinuse = AttackAnimation(Hand, weapon); + DamageHitInfo my_hit; + my_hit.skill = TuneAttackAnimation(Hand, weapon); - int damage = 0; + // Now figure out damage + my_hit.damage_done = 1; + my_hit.min_damage = 0; uint8 mylevel = GetLevel() ? GetLevel() : 1; uint32 hate = 0; - if (weapon) hate = weapon->GetItem()->Damage + weapon->GetItem()->ElemDmgAmt; - int weapon_damage = GetWeaponDamage(other, weapon, &hate); - if (hate == 0 && weapon_damage > 1) hate = weapon_damage; + if (weapon) + hate = (weapon->GetItem()->Damage + weapon->GetItem()->ElemDmgAmt); - if(weapon_damage > 0){ - if(IsBerserk() && GetClass() == BERSERKER){ - int bonus = 3 + GetLevel()/10; //unverified - weapon_damage = weapon_damage * (100+bonus) / 100; + my_hit.base_damage = GetWeaponDamage(other, weapon, &hate); + if (hate == 0 && my_hit.base_damage > 1) + hate = my_hit.base_damage; + + //if weapon damage > 0 then we know we can hit the target with this weapon + //otherwise we cannot and we set the damage to -5 later on + if (my_hit.base_damage > 0) { + // if we revamp this function be more general, we will have to make sure this isn't + // executed for anything BUT normal melee damage weapons from auto attack + if (Hand == EQ::invslot::slotPrimary || Hand == EQ::invslot::slotSecondary) + my_hit.base_damage = CastToClient()->DoDamageCaps(my_hit.base_damage); + auto shield_inc = spellbonuses.ShieldEquipDmgMod + itembonuses.ShieldEquipDmgMod + aabonuses.ShieldEquipDmgMod; + if (shield_inc > 0 && HasShieldEquiped() && Hand == EQ::invslot::slotPrimary) { + my_hit.base_damage = my_hit.base_damage * (100 + shield_inc) / 100; + hate = hate * (100 + shield_inc) / 100; } - int min_hit = 1; - int max_hit = 2;//(2*weapon_damage*GetDamageTable(skillinuse)) / 100; - - if(GetLevel() < 10 && max_hit > RuleI(Combat, HitCapPre10)) - max_hit = (RuleI(Combat, HitCapPre10)); - else if(GetLevel() < 20 && max_hit > RuleI(Combat, HitCapPre20)) - max_hit = (RuleI(Combat, HitCapPre20)); - - CheckIncreaseSkill(skillinuse, other, -15); - CheckIncreaseSkill(EQ::skills::SkillOffense, other, -15); - - -#ifndef EQEMU_NO_WEAPON_DAMAGE_BONUS + // *************************************************************** + // *** Calculate the damage bonus, if applicable, for this hit *** + // *************************************************************** int ucDamageBonus = 0; if (Hand == EQ::invslot::slotPrimary && GetLevel() >= 28 && IsWarriorClass()) { + // Damage bonuses apply only to hits from the main hand (Hand == MainPrimary) by characters level 28 and above + // who belong to a melee class. If we're here, then all of these conditions apply. + ucDamageBonus = GetWeaponDamageBonus(weapon ? weapon->GetItem() : (const EQ::ItemData*) nullptr); - min_hit += (int) ucDamageBonus; - max_hit += (int) ucDamageBonus; + my_hit.min_damage = ucDamageBonus; hate += ucDamageBonus; } -#endif - min_hit += min_hit * GetMeleeMinDamageMod_SE(skillinuse) / 100; - if(max_hit < min_hit) - max_hit = min_hit; + my_hit.offense = Tuneoffense(my_hit.skill, atk_override, add_atk); // we need this a few times + if (get_offense) { + return my_hit.offense; + } + my_hit.hand = Hand; - if (GetMinDamage) - return min_hit; + my_hit.tohit = TuneGetTotalToHit(my_hit.skill, hit_chance_bonus, accuracy_override, add_accuracy); + if (get_accuracy) { + return my_hit.tohit; + } + TuneDoAttack(other, my_hit, nullptr, no_avoid, no_hit_chance, ac_override, add_ac, avoidance_override, accuracy_override, add_avoidance, add_accuracy); + } + else { + my_hit.damage_done = DMG_INVULNERABLE; + } + + /////////////////////////////////////////////////////////// + ////// Send Attack Damage + /////////////////////////////////////////////////////////// + + //other->Damage(this, my_hit.damage_done, SPELL_UNKNOWN, my_hit.skill, true, -1, false, m_specialattacks); + + return my_hit.damage_done; +} + +void Mob::TuneDoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, bool no_avoid, bool no_hit_chance, int ac_override, int add_ac, + int avoidance_override, int accuracy_override, int add_avoidance, int add_accuracy) +{ + if (!other) + return; + LogCombat("[{}]::DoAttack vs [{}] base [{}] min [{}] offense [{}] tohit [{}] skill [{}]", GetName(), + other->GetName(), hit.base_damage, hit.min_damage, hit.offense, hit.tohit, hit.skill); + + // check to see if we hit.. + if (no_avoid || (!no_avoid && other->AvoidDamage(this, hit))) { + int strike_through = itembonuses.StrikeThrough + spellbonuses.StrikeThrough + aabonuses.StrikeThrough; + if (strike_through && zone->random.Roll(strike_through)) { + MessageString(Chat::StrikeThrough, + STRIKETHROUGH_STRING); // You strike through your opponents defenses! + hit.damage_done = 1; // set to one, we will check this to continue + } + // I'm pretty sure you can riposte a riposte + if (hit.damage_done == DMG_RIPOSTED) { + //DoRiposte(other); //Disabled for TUNE + //if (IsDead()) + return; + } + LogCombat("Avoided/strikethrough damage with code [{}]", hit.damage_done); + } + + if (hit.damage_done >= 0) { + if (no_hit_chance || (!no_hit_chance && other->TuneCheckHitChance(this, hit, avoidance_override, add_avoidance))) { + other->TuneMeleeMitigation(this, hit, ac_override, add_ac); + if (hit.damage_done > 0) { + ApplyDamageTable(hit); + CommonOutgoingHitSuccess(other, hit, opts); + } + LogCombat("Final damage after all reductions: [{}]", hit.damage_done); + } + else { + LogCombat("Attack missed. Damage set to 0"); + hit.damage_done = 0; + } + } +} + +void Mob::TuneMeleeMitigation(Mob *attacker, DamageHitInfo &hit, int ac_override, int add_ac) +{ + if (hit.damage_done < 0 || hit.base_damage == 0) + return; + + Mob* defender = this; + //auto mitigation = defender->GetMitigationAC(); + auto mitigation = defender->TuneACSum(false, ac_override, add_ac); + if (IsClient() && attacker->IsClient()) + mitigation = mitigation * 80 / 100; // 2004 PvP changes + + auto roll = RollD20(hit.offense, mitigation); + + // +0.5 for rounding, min to 1 dmg + hit.damage_done = std::max(static_cast(roll * static_cast(hit.base_damage) + 0.5), 1); + + //Shout("mitigation %d vs offense %d. base %d rolled %f damage %d", mitigation, hit.offense, hit.base_damage, roll, hit.damage_done); +} + +int Mob::TuneACSum(bool skip_caps, int ac_override, int add_ac) +{ + int ac = 0; // this should be base AC whenever shrouds come around + ac += itembonuses.AC; // items + food + tribute + + if (IsClient()) { + ac = ac_override; + ac += add_ac; + } + + int shield_ac = 0; + if (HasShieldEquiped() && IsClient()) { + auto client = CastToClient(); + auto inst = client->GetInv().GetItem(EQ::invslot::slotSecondary); + if (inst) { + if (inst->GetItemRecommendedLevel(true) <= GetLevel()) + shield_ac = inst->GetItemArmorClass(true); + else + shield_ac = client->CalcRecommendedLevelBonus(GetLevel(), inst->GetItemRecommendedLevel(true), inst->GetItemArmorClass(true)); + } + shield_ac += client->GetHeroicSTR() / 10; + } + // EQ math + ac = (ac * 4) / 3; + // anti-twink + if (!skip_caps && IsClient() && GetLevel() < RuleI(Combat, LevelToStopACTwinkControl)) + ac = std::min(ac, 25 + 6 * GetLevel()); + ac = std::max(0, ac + GetClassRaceACBonus()); + if (IsNPC()) { + // This is the developer tweaked number + // for the VAST amount of NPCs in EQ this number didn't exceed 600 until recently (PoWar) + // According to the guild hall Combat Dummies, a level 50 classic EQ mob it should be ~115 + // For a 60 PoP mob ~120, 70 OoW ~120 + + + if (ac_override) { + ac += ac_override; + } + else { + ac += GetAC(); + } + ac += add_ac; + + ac += GetPetACBonusFromOwner(); + auto spell_aa_ac = aabonuses.AC + spellbonuses.AC; + ac += GetSkill(EQ::skills::SkillDefense) / 5; + if (EQ::ValueWithin(static_cast(GetClass()), NECROMANCER, ENCHANTER)) + ac += spell_aa_ac / 3; else - return max_hit; + ac += spell_aa_ac / 4; + } + else { // TODO: so we can't set NPC skills ... so the skill bonus ends up being HUGE so lets nerf them a bit + auto spell_aa_ac = aabonuses.AC + spellbonuses.AC; + if (EQ::ValueWithin(static_cast(GetClass()), NECROMANCER, ENCHANTER)) + ac += GetSkill(EQ::skills::SkillDefense) / 2 + spell_aa_ac / 3; + else + ac += GetSkill(EQ::skills::SkillDefense) / 3 + spell_aa_ac / 4; } - return 0; + if (GetAGI() > 70) + ac += GetAGI() / 20; + if (ac < 0) + ac = 0; + + if (!skip_caps && (IsClient())) { + auto softcap = GetACSoftcap(); + auto returns = GetSoftcapReturns(); + int total_aclimitmod = aabonuses.CombatStability + itembonuses.CombatStability + spellbonuses.CombatStability; + if (total_aclimitmod) + softcap = (softcap * (100 + total_aclimitmod)) / 100; + softcap += shield_ac; + if (ac > softcap) { + auto over_cap = ac - softcap; + ac = softcap + (over_cap * returns); + } + //Shout("ACSum ac %i softcap %i returns %.2f", ac, softcap, static_cast(returns)); + } + else { + //Shout("ACSum ac %i", ac); + } + + return ac; } -void Mob::Tune_FindAccuaryByHitChance(Mob* defender, Mob *attacker, float hit_chance, int interval, int max_loop, int avoid_override, int Msg) +int Mob::Tuneoffense(EQ::skills::SkillType skill, int atk_override, int add_atk) { + int offense = GetSkill(skill); + int stat_bonus = GetSTR(); - int add_acc = 0; - float tmp_hit_chance = 0.0f; - bool end = false; + switch (skill) { + case EQ::skills::SkillArchery: + case EQ::skills::SkillThrowing: + stat_bonus = GetDEX(); + break; - EQ::skills::SkillType skillinuse = EQ::skills::SkillHandtoHand; - if (attacker->IsClient()) - {//Will check first equiped weapon for skill. Ie. remove wepaons to assess bow. - EQ::ItemInstance* weapon; - weapon = attacker->CastToClient()->GetInv().GetItem(EQ::invslot::slotPrimary); - - if(weapon && weapon->IsWeapon()){ - skillinuse = attacker->CastToClient()->AttackAnimation(EQ::invslot::slotPrimary, weapon); + // Mobs with no weapons default to H2H. + // Since H2H is capped at 100 for many many classes, + // lets not handicap mobs based on not spawning with a + // weapon. + // + // Maybe we tweak this if Disarm is actually implemented. + + case EQ::skills::SkillHandtoHand: + offense = GetBestMeleeSkill(); + break; + } + + if (stat_bonus >= 75) + offense += (2 * stat_bonus - 150) / 3; + + int32 tune_atk = GetATK(); + if (atk_override) { + tune_atk = atk_override; + } + + tune_atk += add_atk; + + offense += tune_atk + GetPetATKBonusFromOwner(); + return offense; +} + +EQ::skills::SkillType Mob::TuneAttackAnimation(int Hand, const EQ::ItemInstance* weapon, EQ::skills::SkillType skillinuse) +{ + // Determine animation + int type = 0; + if (weapon && weapon->IsClassCommon()) { + const EQ::ItemData* item = weapon->GetItem(); + + switch (item->ItemType) { + case EQ::item::ItemType1HSlash: // 1H Slashing + skillinuse = EQ::skills::Skill1HSlashing; + type = anim1HWeapon; + break; + case EQ::item::ItemType2HSlash: // 2H Slashing + skillinuse = EQ::skills::Skill2HSlashing; + type = anim2HSlashing; + break; + case EQ::item::ItemType1HPiercing: // Piercing + skillinuse = EQ::skills::Skill1HPiercing; + type = anim1HPiercing; + break; + case EQ::item::ItemType1HBlunt: // 1H Blunt + skillinuse = EQ::skills::Skill1HBlunt; + type = anim1HWeapon; + break; + case EQ::item::ItemType2HBlunt: // 2H Blunt + skillinuse = EQ::skills::Skill2HBlunt; + type = RuleB(Combat, Classic2HBAnimation) ? anim2HWeapon : anim2HSlashing; + break; + case EQ::item::ItemType2HPiercing: // 2H Piercing + if (IsClient() && CastToClient()->ClientVersion() < EQ::versions::ClientVersion::RoF2) + skillinuse = EQ::skills::Skill1HPiercing; + else + skillinuse = EQ::skills::Skill2HPiercing; + type = anim2HWeapon; + break; + case EQ::item::ItemTypeMartial: + skillinuse = EQ::skills::SkillHandtoHand; + type = animHand2Hand; + break; + default: + skillinuse = EQ::skills::SkillHandtoHand; + type = animHand2Hand; + break; + }// switch + } + else if (IsNPC()) { + switch (skillinuse) { + case EQ::skills::Skill1HSlashing: // 1H Slashing + type = anim1HWeapon; + break; + case EQ::skills::Skill2HSlashing: // 2H Slashing + type = anim2HSlashing; + break; + case EQ::skills::Skill1HPiercing: // Piercing + type = anim1HPiercing; + break; + case EQ::skills::Skill1HBlunt: // 1H Blunt + type = anim1HWeapon; + break; + case EQ::skills::Skill2HBlunt: // 2H Blunt + type = anim2HSlashing; //anim2HWeapon + break; + case EQ::skills::Skill2HPiercing: // 2H Piercing + type = anim2HWeapon; + break; + case EQ::skills::SkillHandtoHand: + type = animHand2Hand; + break; + default: + type = animHand2Hand; + break; + }// switch + } + else { + skillinuse = EQ::skills::SkillHandtoHand; + type = animHand2Hand; + } + + // If we're attacking with the secondary hand, play the dual wield anim + if (Hand == EQ::invslot::slotSecondary) // DW anim + type = animDualWield; + + return skillinuse; +} + +int Mob::Tunecompute_tohit(EQ::skills::SkillType skillinuse, int accuracy_override, int add_accuracy) +{ + int tohit = GetSkill(EQ::skills::SkillOffense) + 7; + tohit += GetSkill(skillinuse); + if (IsNPC()) { + if (accuracy_override) { + tohit += accuracy_override; } else { - weapon = attacker->CastToClient()->GetInv().GetItem(EQ::invslot::slotSecondary); - if (weapon && weapon->IsWeapon()) - skillinuse = attacker->CastToClient()->AttackAnimation(EQ::invslot::slotSecondary, weapon); - else { - weapon = attacker->CastToClient()->GetInv().GetItem(EQ::invslot::slotRange); - if (weapon && weapon->IsWeapon()) - skillinuse = attacker->CastToClient()->AttackAnimation(EQ::invslot::slotRange, weapon); - } + tohit += CastToNPC()->GetAccuracyRating(); + } + tohit += add_accuracy; + } + if (IsClient()) { + double reduction = CastToClient()->m_pp.intoxication / 2.0; + if (reduction > 20.0) { + reduction = std::min((110 - reduction) / 100.0, 1.0); + tohit = reduction * static_cast(tohit); + } + else if (IsBerserk()) { + tohit += (GetLevel() * 2) / 5; } } - - tmp_hit_chance = Tune_CheckHitChance(defender, attacker, skillinuse, EQ::invslot::slotPrimary, 0, 0, 0, avoid_override); - - - Message(0, "#Tune - Begin Parse [Interval %i Max Loop Iterations %i]", interval, max_loop); - Message(0, "#Tune - Processing... Find Accuracy for hit chance on attacker of (%.0f) pct on defender [Current Hit Chance %.2f]", hit_chance, tmp_hit_chance); - - - if (tmp_hit_chance > hit_chance) - interval = interval * -1; - - for (int j=0; j < max_loop; j++) - { - tmp_hit_chance = Tune_CheckHitChance(defender, attacker, skillinuse, EQ::invslot::slotPrimary, 0, false, 0, avoid_override, add_acc); - - if (Msg >= 3) - Message(Chat::Yellow, "#Tune - Processing... [%i] [ACCURACY %i] Hit Chance %.2f ",j,add_acc,tmp_hit_chance); - - if (interval > 0 && tmp_hit_chance >= hit_chance){ - end = true; - } - - else if (interval < 0 && tmp_hit_chance <= hit_chance){ - end = true; - } - - if (end){ - - Tune_CheckHitChance(defender, attacker, skillinuse, EQ::invslot::slotPrimary, 0, Msg, 0, avoid_override);//Display Stat Report - - Message(0, " "); - - if (attacker->IsNPC()){ - Message(0, "#Recommended NPC Accuracy Statistic adjustment of ( %i ) on ' %s ' for a hit chance of (+ %.0f) pct verse ' %s '. ",add_acc,defender->GetCleanName(), hit_chance, attacker->GetCleanName()); - Message(0, "#SET: [NPC Avoidance] = [%i]",add_acc + defender->CastToNPC()->GetAccuracyRating()); - } - else if (attacker->IsClient()){ - Message(0, "#Recommended Client Accuracy Bonus adjustment of ( %i ) on ' %s ' for a hit chance of (+ %.0f) pct verse ' %s '. ",add_acc,defender->GetCleanName(), hit_chance, attacker->GetCleanName()); - Message(0, "#Modify (+/-): [Item Mod2 Accuracy] [%i]",add_acc); - Message(0, "#Modify (+/-): [SE_Accuracy(216)] [%i]",add_acc); - Message(0, "#Modify (+/-): [SE_HitChance(184)] [%i]",add_acc / 15); - } - - return; - } - - - add_acc = add_acc + interval; - } - - Message(Chat::LightGray, "#Tune - Error: Unable to find desired result for (%.0f) pct - Increase interval (%i) AND/OR max loop value (%i) and run again.", hit_chance, interval, max_loop); - Message(Chat::LightGray, "#Tune - Parse ended at ACCURACY ADJUSTMENTT ( %i ) at hit chance of (%.0f) / (%.0f) pct.",add_acc,tmp_hit_chance / hit_chance); + return std::max(tohit, 1); } -void Mob::Tune_FindAvoidanceByHitChance(Mob* defender, Mob *attacker, float hit_chance, int interval, int max_loop, int acc_override, int Msg) +// return -1 in cases that always hit +int Mob::TuneGetTotalToHit(EQ::skills::SkillType skill, int chance_mod, int accuracy_override, int add_accuracy) { - int add_avoid = 0; - float tmp_hit_chance = 0.0f; - bool end = false; + if (chance_mod >= 10000) // override for stuff like SE_SkillAttack + return -1; - EQ::skills::SkillType skillinuse = EQ::skills::SkillHandtoHand; - if (attacker->IsClient()) - {//Will check first equiped weapon for skill. Ie. remove wepaons to assess bow. - EQ::ItemInstance* weapon; - weapon = attacker->CastToClient()->GetInv().GetItem(EQ::invslot::slotPrimary); - - if(weapon && weapon->IsWeapon()){ - skillinuse = attacker->CastToClient()->AttackAnimation(EQ::invslot::slotPrimary, weapon); + // calculate attacker's accuracy + auto accuracy = Tunecompute_tohit(skill, accuracy_override, add_accuracy) + 10; // add 10 in case the NPC's stats are fucked + if (chance_mod > 0) // multiplier + accuracy *= chance_mod; + + // Torven parsed an apparent constant of 1.2 somewhere in here * 6 / 5 looks eqmathy to me! + // new test clients have 121 / 100 + accuracy = (accuracy * 121) / 100; + + // unsure on the stacking order of these effects, rather hard to parse + // item mod2 accuracy isn't applied to range? Theory crafting and parses back it up I guess + // mod2 accuracy -- flat bonus + if (skill != EQ::skills::SkillArchery && skill != EQ::skills::SkillThrowing) + accuracy += itembonuses.HitChance; + + //518 Increase ATK accuracy by percentage, stackable + auto atkhit_bonus = itembonuses.Attack_Accuracy_Max_Percent + aabonuses.Attack_Accuracy_Max_Percent + spellbonuses.Attack_Accuracy_Max_Percent; + if (atkhit_bonus) + accuracy += round(static_cast(accuracy) * static_cast(atkhit_bonus) * 0.0001); + + // 216 Melee Accuracy Amt aka SE_Accuracy -- flat bonus + accuracy += itembonuses.Accuracy[EQ::skills::HIGHEST_SKILL + 1] + + aabonuses.Accuracy[EQ::skills::HIGHEST_SKILL + 1] + + spellbonuses.Accuracy[EQ::skills::HIGHEST_SKILL + 1] + + itembonuses.Accuracy[skill] + + aabonuses.Accuracy[skill] + + spellbonuses.Accuracy[skill]; + + if (IsClient()) { + if (accuracy_override) { + accuracy = accuracy_override; + } + accuracy += add_accuracy; + } + + // auto hit discs (and looks like there are some autohit AAs) + if (spellbonuses.HitChanceEffect[skill] >= 10000 || aabonuses.HitChanceEffect[skill] >= 10000) + return -1; + + if (spellbonuses.HitChanceEffect[EQ::skills::HIGHEST_SKILL + 1] >= 10000) + return -1; + + // 184 Accuracy % aka SE_HitChance -- percentage increase + auto hit_bonus = itembonuses.HitChanceEffect[EQ::skills::HIGHEST_SKILL + 1] + + aabonuses.HitChanceEffect[EQ::skills::HIGHEST_SKILL + 1] + + spellbonuses.HitChanceEffect[EQ::skills::HIGHEST_SKILL + 1] + + itembonuses.HitChanceEffect[skill] + + aabonuses.HitChanceEffect[skill] + + spellbonuses.HitChanceEffect[skill]; + + accuracy = (accuracy * (100 + hit_bonus)) / 100; + return accuracy; +} + +// return -1 in cases that always miss +int Mob::TuneGetTotalDefense(int avoidance_override, int add_avoidance) +{ + auto avoidance = Tunecompute_defense(avoidance_override, add_avoidance) + 10; // add 10 in case the NPC's stats are fucked + auto evasion_bonus = spellbonuses.AvoidMeleeChanceEffect; // we check this first since it has a special case + if (evasion_bonus >= 10000) + return -1; + + // 515 SE_AC_Avoidance_Max_Percent + auto ac_aviodance_bonus = itembonuses.AC_Avoidance_Max_Percent + aabonuses.AC_Avoidance_Max_Percent + spellbonuses.AC_Avoidance_Max_Percent; + if (ac_aviodance_bonus) + avoidance += round(static_cast(avoidance) * static_cast(ac_aviodance_bonus) * 0.0001); + + // 172 Evasion aka SE_AvoidMeleeChance + evasion_bonus += itembonuses.AvoidMeleeChanceEffect + aabonuses.AvoidMeleeChanceEffect; // item bonus here isn't mod2 avoidance + + // 215 Pet Avoidance % aka SE_PetAvoidance + evasion_bonus += GetPetAvoidanceBonusFromOwner(); + + // Evasion is a percentage bonus according to AA descriptions + if (evasion_bonus) + avoidance = (avoidance * (100 + evasion_bonus)) / 100; + + return avoidance; +} + +int Mob::Tunecompute_defense(int avoidance_override, int add_avoidance) +{ + int defense = GetSkill(EQ::skills::SkillDefense) * 400 / 225; + defense += (8000 * (GetAGI() - 40)) / 36000; + if (IsClient()) { + if (avoidance_override) { + defense = avoidance_override; } else { - weapon = attacker->CastToClient()->GetInv().GetItem(EQ::invslot::slotSecondary); - if (weapon && weapon->IsWeapon()) - skillinuse = attacker->CastToClient()->AttackAnimation(EQ::invslot::slotSecondary, weapon); - else { - weapon = attacker->CastToClient()->GetInv().GetItem(EQ::invslot::slotRange); - if (weapon && weapon->IsWeapon()) - skillinuse = attacker->CastToClient()->AttackAnimation(EQ::invslot::slotRange, weapon); - } + defense += CastToClient()->GetHeroicAGI() / 10; + } + defense += add_avoidance; //1 pt = 10 heroic agi + } + + //516 SE_AC_Mitigation_Max_Percent + auto ac_bonus = itembonuses.AC_Mitigation_Max_Percent + aabonuses.AC_Mitigation_Max_Percent + spellbonuses.AC_Mitigation_Max_Percent; + if (ac_bonus) + defense += round(static_cast(defense) * static_cast(ac_bonus) * 0.0001); + + defense += itembonuses.AvoidMeleeChance; // item mod2 + if (IsNPC()) { + if (avoidance_override) { + defense += avoidance_override; + } + else { + defense += CastToNPC()->GetAvoidanceRating(); + } + defense += add_avoidance; + } + + if (IsClient()) { + double reduction = CastToClient()->m_pp.intoxication / 2.0; + if (reduction > 20.0) { + reduction = std::min((110 - reduction) / 100.0, 1.0); + defense = reduction * static_cast(defense); } } - tmp_hit_chance = Tune_CheckHitChance(defender, attacker, skillinuse, EQ::invslot::slotPrimary, 0, 0, acc_override, 0); - - Message(0, "#Tune - Begin Parse [Interval %i Max Loop Iterations %i]", interval, max_loop); - Message(0, "#Tune - Processing... Find Avoidance for hit chance on defender of (%.0f) pct from attacker. [Current Hit Chance %.2f]", hit_chance, tmp_hit_chance); - - if (tmp_hit_chance < hit_chance) - interval = interval * -1; - - for (int j=0; j < max_loop; j++) - { - tmp_hit_chance = Tune_CheckHitChance(defender, attacker, skillinuse, EQ::invslot::slotPrimary, 0, 0, acc_override, 0, 0, add_avoid); - - if (Msg >= 3) - Message(0, "#Tune - Processing... [%i] [AVOIDANCE %i] Hit Chance %.2f ",j,add_avoid,tmp_hit_chance); - - if (interval > 0 && tmp_hit_chance <= hit_chance){ - end = true; - } - - else if (interval < 0 && tmp_hit_chance >= hit_chance){ - end = true; - } - - if (end){ - - Tune_CheckHitChance(defender, attacker, skillinuse, EQ::invslot::slotPrimary, 0, Msg, acc_override, 0);//Display Stat Report - - Message(0, " "); - - if (defender->IsNPC()){ - Message(0, "#Recommended NPC Avoidance Statistic adjustment of ( %i ) on ' %s ' for a hit chance of ( %.0f) pct from ' %s '. ",add_avoid,defender->GetCleanName(), hit_chance, attacker->GetCleanName()); - Message(0, "#SET: [NPC Avoidance] = [%i]",add_avoid + defender->CastToNPC()->GetAvoidanceRating()); - } - else if (defender->IsClient()){ - Message(0, "#Recommended Client Avoidance Bonus adjustment of ( %i ) on ' %s ' for a hit chance of ( %.0f) pct from ' %s '. ",add_avoid,defender->GetCleanName(), hit_chance, attacker->GetCleanName()); - Message(0, "#Modify (+/-): [Item Mod2 Avoidance] [%i]",add_avoid); - Message(0, "#Modify (+/-): [SE_AvoidMeleeChance(172)] [%i]",add_avoid / 10); - } - - return; - } - - add_avoid = add_avoid + interval; - } - - Message(0, "#Tune - Error: Unable to find desired result for (%.0f) pct - Increase interval (%i) AND/OR max loop value (%i) and run again.", hit_chance, interval, max_loop); - Message(0, "#Tune - Parse ended at AVOIDANCE ADJUSTMENT ( %i ) at hit chance of (%.0f) / (%.0f) pct.",add_avoid,tmp_hit_chance / hit_chance); + return std::max(1, defense); } - -float Mob::Tune_CheckHitChance(Mob* defender, Mob* attacker, EQ::skills::SkillType skillinuse, int Hand, int16 chance_mod, int Msg, int acc_override, int avoid_override, int add_acc, int add_avoid) +// called when a mob is attacked, does the checks to see if it's a hit +// and does other mitigation checks. 'this' is the mob being attacked. +bool Mob::TuneCheckHitChance(Mob* other, DamageHitInfo &hit, int avoidance_override, int add_avoidance) { - float chancetohit = RuleR(Combat, BaseHitChance); + Mob *attacker = other; + Mob *defender = this; + //Shout("CheckHitChance(%s) attacked by %s", defender->GetName(), attacker->GetName()); - if(attacker->IsNPC() && !attacker->IsPet()) - chancetohit += RuleR(Combat, NPCBonusHitChance); - - if (Msg){ - - Message(0, "######### Hit Chance Report: Start [Detail Level %i]#########", Msg); - Message(0, "#ATTACKER: %s", attacker->GetCleanName()); - Message(0, "#DEFENDER: %s", defender->GetCleanName()); - if (Msg >= 2){ - Message(0, " "); - Message(0, "### Calculate Base Hit Chance ###"); - Message(0, "# + %.2f Total: %.2f #### RuleR(Combat, BaseHitChance)", RuleR(Combat, BaseHitChance), RuleR(Combat, BaseHitChance)); - if (attacker->IsNPC()) - Message(0, "# + %.2f Total: %.2f #### RuleR(Combat, NPCBonusHitChance)", RuleR(Combat, NPCBonusHitChance), chancetohit); - } - } - - float temp_chancetohit = chancetohit; - - bool pvpmode = false; - if(IsClient() && attacker->IsClient()) - pvpmode = true; - - if (chance_mod >= 10000) + if (defender->IsClient() && defender->CastToClient()->IsSitting()) return true; - float avoidanceBonus = 0; - float hitBonus = 0; + auto avoidance = defender->TuneGetTotalDefense(avoidance_override, add_avoidance); + if (avoidance == -1) // some sort of auto avoid disc + return false; - //////////////////////////////////////////////////////// - // To hit calcs go here - //////////////////////////////////////////////////////// + auto accuracy = hit.tohit; + if (accuracy == -1) + return true; - uint8 attacker_level = attacker->GetLevel() ? attacker->GetLevel() : 1; - uint8 defender_level = defender->GetLevel() ? defender->GetLevel() : 1; + // so now we roll! + // relevant dev quote: + // Then your chance to simply avoid the attack is checked (defender's avoidance roll beat the attacker's accuracy roll.) + int tohit_roll = zone->random.Roll0(accuracy); + int avoid_roll = zone->random.Roll0(avoidance); + //Shout("CheckHitChance accuracy(%d => %d) avoidance(%d => %d)", accuracy, tohit_roll, avoidance, avoid_roll); - //Calculate the level difference - - double level_difference = attacker_level - defender_level; - double range = defender->GetLevel(); - range = ((range / 4) + 3); - - if(level_difference < 0) - { - if(level_difference >= -range) - { - chancetohit += (level_difference / range) * RuleR(Combat,HitFalloffMinor); //5 - } - else if (level_difference >= -(range+3.0)) - { - chancetohit -= RuleR(Combat,HitFalloffMinor); - chancetohit += ((level_difference+range) / (3.0)) * RuleR(Combat,HitFalloffModerate); //7 - } - else - { - chancetohit -= (RuleR(Combat,HitFalloffMinor) + RuleR(Combat,HitFalloffModerate)); - chancetohit += ((level_difference+range+3.0)/12.0) * RuleR(Combat,HitFalloffMajor); //50 - } - } - else - { - chancetohit += (RuleR(Combat,HitBonusPerLevel) * level_difference); - } - - if (Msg >= 2) - Message(0, "# + %.2f Total: %.2f #### Level Modifers", chancetohit - temp_chancetohit, chancetohit); - - temp_chancetohit = chancetohit; - - chancetohit -= ((float)defender->GetAGI() * RuleR(Combat, AgiHitFactor)); - - if (Msg >= 2) - Message(0, "# - %.2f Total: %.2f #### DEFENDER Agility", ((float)defender->GetAGI() * RuleR(Combat, AgiHitFactor)), chancetohit); - - if(attacker->IsClient()) - { - chancetohit -= (RuleR(Combat,WeaponSkillFalloff) * (attacker->CastToClient()->MaxSkill(skillinuse) - attacker->GetSkill(skillinuse))); - if (Msg >= 2) - Message(0, "# - %.2f Total: %.2f ##### ATTACKER Wpn Skill Mod: ", (RuleR(Combat,WeaponSkillFalloff) * (attacker->CastToClient()->MaxSkill(skillinuse) - attacker->GetSkill(skillinuse))), chancetohit); - } - - if(defender->IsClient()) - { - chancetohit += (RuleR(Combat, WeaponSkillFalloff) * (defender->CastToClient()->MaxSkill(EQ::skills::SkillDefense) - defender->GetSkill(EQ::skills::SkillDefense))); - if (Msg >= 2) - Message(0, "# + %.2f Total: %.2f #### DEFENDER Defense Skill Mod", (RuleR(Combat, WeaponSkillFalloff) * (defender->CastToClient()->MaxSkill(EQ::skills::SkillDefense) - defender->GetSkill(EQ::skills::SkillDefense))), chancetohit); - } - - - //I dont think this is 100% correct, but at least it does something... - if(attacker->spellbonuses.MeleeSkillCheckSkill == skillinuse || attacker->spellbonuses.MeleeSkillCheckSkill == 255) { - chancetohit += attacker->spellbonuses.MeleeSkillCheck; - if (Msg >= 2) - Message(0, "# + %.2f Total: %.2f #### ATTACKER SE_MeleeSkillCheck(183) Spell Bonus", attacker->spellbonuses.MeleeSkillCheck , chancetohit); - } - if(attacker->itembonuses.MeleeSkillCheckSkill == skillinuse || attacker->itembonuses.MeleeSkillCheckSkill == 255) { - chancetohit += attacker->itembonuses.MeleeSkillCheck; - if (Msg >= 2) - Message(0, "# + %.2f Total: %.2f #### ATTACKER SE_MeleeSkillCheck(183) Worn Bonus", attacker->itembonuses.MeleeSkillCheck , chancetohit); - } - - if (Msg) - Message(0, "#FINAL Base Hit Chance: %.2f percent", chancetohit); - - if (Msg >= 2){ - Message(0, " "); - Message(0, "######### Calculate Avoidance Bonuses #########"); - } - - //Avoidance Bonuses on defender decreases baseline hit chance by percent. - avoidanceBonus = defender->spellbonuses.AvoidMeleeChanceEffect + - defender->itembonuses.AvoidMeleeChanceEffect + - defender->aabonuses.AvoidMeleeChanceEffect + - (defender->itembonuses.AvoidMeleeChance / 10.0f); //Item Mod 'Avoidence' - - if (Msg >= 2){ - if (defender->aabonuses.AvoidMeleeChanceEffect) - Message(0, "# %i #### DEFENDER SE_AvoidMeleeChance(172) AA Bonus", defender->aabonuses.AvoidMeleeChanceEffect); - if (defender->spellbonuses.AvoidMeleeChanceEffect) - Message(0, "# %i #### DEFENDER SE_AvoidMeleeChance(172) Spell Bonus", defender->spellbonuses.AvoidMeleeChanceEffect); - if (defender->itembonuses.AvoidMeleeChanceEffect) - Message(0, "# %i #### DEFENDER SE_AvoidMeleeChance(172) Worn Bonus", defender->itembonuses.AvoidMeleeChanceEffect); - if (defender->itembonuses.AvoidMeleeChance) - Message(0, "# %i #### DEFENDER Avoidance Item Mod2 Bonus[Amt: %i] ", defender->itembonuses.AvoidMeleeChance / 10.0f,defender->itembonuses.AvoidMeleeChance); - } - - - Mob *owner = nullptr; - if (defender->IsPet()) - owner = defender->GetOwner(); - else if ((defender->IsNPC() && defender->CastToNPC()->GetSwarmOwner())) - owner = entity_list.GetMobID(defender->CastToNPC()->GetSwarmOwner()); - - if (owner){ - avoidanceBonus += owner->aabonuses.PetAvoidance + owner->spellbonuses.PetAvoidance + owner->itembonuses.PetAvoidance; - - if (Msg >= 2){ - if (owner->aabonuses.PetAvoidance) - Message(0, "# %i #### DEFENDER SE_PetAvoidance(215) AA Bonus", owner->aabonuses.PetAvoidance); - if (owner->aabonuses.PetAvoidance) - Message(0, "# %i #### DEFENDER SE_PetAvoidance(215) Spell Bonus", owner->itembonuses.PetAvoidance); - if (owner->aabonuses.PetAvoidance) - Message(0, "# %i #### DEFENDER SE_PetAvoidance(215) Worn Bonus", owner->spellbonuses.PetAvoidance); - } - } - - if(defender->IsNPC()){ - avoidanceBonus += ((defender->CastToNPC()->GetAvoidanceRating() + add_avoid) / 10.0f); //Modifier from database - if (Msg >= 2) - Message(0, "# + %.2f #### DEFENDER NPC AVOIDANCE STAT [Stat Amt: %i] ", ((defender->CastToNPC()->GetAvoidanceRating() + add_avoid) / 10.0f),defender->CastToNPC()->GetAvoidanceRating()); - } - else if(defender->IsClient()){ - avoidanceBonus += (add_avoid / 10.0f); //Avoidance Item Mod - } - - //#tune override value - if (avoid_override){ - avoidanceBonus = (avoid_override / 10.0f); - if (Msg >= 2) - Message(0, "%.2f #### DEFENDER 'AVOIDANCE OVERRIDE'", avoidanceBonus); - } - - if (Msg) - Message(0, "#FINAL Avoidance Bonus': %.2f percent ", avoidanceBonus); - - if (Msg >= 2){ - Message(0, " "); - Message(0, "######### Calculate Accuracy Bonuses #########"); - } - - //Hit Chance Bonuses on attacker increases baseline hit chance by percent. - hitBonus += attacker->itembonuses.HitChanceEffect[skillinuse] + - attacker->spellbonuses.HitChanceEffect[skillinuse]+ - attacker->aabonuses.HitChanceEffect[skillinuse]+ - attacker->itembonuses.HitChanceEffect[EQ::skills::HIGHEST_SKILL + 1] + - attacker->spellbonuses.HitChanceEffect[EQ::skills::HIGHEST_SKILL + 1] + - attacker->aabonuses.HitChanceEffect[EQ::skills::HIGHEST_SKILL + 1]; - - if (Msg >= 2){ - if (attacker->aabonuses.HitChanceEffect[EQ::skills::HIGHEST_SKILL + 1]) - Message(0, "# %i #### ATTACKER SE_HitChance(184) AA Bonus [All Skills]", attacker->aabonuses.HitChanceEffect[EQ::skills::HIGHEST_SKILL + 1]); - if (attacker->spellbonuses.HitChanceEffect[EQ::skills::HIGHEST_SKILL + 1]) - Message(0, "# %i #### ATTACKER SE_HitChance(184) Spell Bonus [All Skills]", attacker->spellbonuses.HitChanceEffect[EQ::skills::HIGHEST_SKILL + 1]); - if (attacker->itembonuses.HitChanceEffect[EQ::skills::HIGHEST_SKILL + 1]) - Message(0, "# %i #### ATTACKER SE_HitChance(184) Worn Bonus [All Skills]", attacker->itembonuses.HitChanceEffect[EQ::skills::HIGHEST_SKILL + 1]); - if (attacker->itembonuses.HitChanceEffect[skillinuse]) - Message(0, "# %i #### ATTACKER SE_HitChance(184) AA Bonus [Skill]", attacker->aabonuses.HitChanceEffect[skillinuse]); - if (attacker->spellbonuses.HitChanceEffect[skillinuse]) - Message(0, "# %i #### ATTACKER SE_HitChance(184) Spell Bonus [Skill]", attacker->spellbonuses.HitChanceEffect[skillinuse]); - if (attacker->itembonuses.HitChanceEffect[skillinuse]) - Message(0, "# %i #### ATTACKER SE_HitChance(184) Worn Bonus [Skill]", attacker->itembonuses.HitChanceEffect[skillinuse]); - } - - //Accuracy = Spell Effect , HitChance = 'Accuracy' from Item Effect - //Only AA derived accuracy can be skill limited. ie (Precision of the Pathfinder, Dead Aim) - hitBonus += (attacker->itembonuses.Accuracy[EQ::skills::HIGHEST_SKILL + 1] + - attacker->spellbonuses.Accuracy[EQ::skills::HIGHEST_SKILL + 1] + - attacker->aabonuses.Accuracy[EQ::skills::HIGHEST_SKILL + 1] + - attacker->aabonuses.Accuracy[skillinuse] + - attacker->itembonuses.HitChance) / 15.0f; //Item Mod 'Accuracy' - - if (Msg >= 2) { - if (attacker->aabonuses.Accuracy[EQ::skills::HIGHEST_SKILL + 1]) - Message(0, "# %.2f #### ATTACKER SE_Accuracy(216) AA Bonus [All Skills] [Stat Amt: %i]", static_cast(attacker->aabonuses.Accuracy[EQ::skills::HIGHEST_SKILL + 1]) / 15.0f, attacker->aabonuses.Accuracy[EQ::skills::HIGHEST_SKILL + 1]); - if (attacker->spellbonuses.Accuracy[EQ::skills::HIGHEST_SKILL + 1]) - Message(0, "# %.2f #### ATTACKER SE_Accuracy(216) Spell Bonus [All Skills] [Stat Amt: %i]", static_cast(attacker->spellbonuses.Accuracy[EQ::skills::HIGHEST_SKILL + 1]) / 15.0f, attacker->spellbonuses.Accuracy[EQ::skills::HIGHEST_SKILL + 1]); - if (attacker->itembonuses.Accuracy[EQ::skills::HIGHEST_SKILL + 1]) - Message(0, "# %.2f #### ATTACKER SE_Accuracy(216) Worn Bonus [All Skills] [Stat Amt: %i]", static_cast(attacker->itembonuses.Accuracy[EQ::skills::HIGHEST_SKILL + 1]) / 15.0f, attacker->itembonuses.Accuracy[EQ::skills::HIGHEST_SKILL + 1]); - if (attacker->aabonuses.Accuracy[skillinuse]) - Message(0, "# %.2f #### ATTACKER SE_Accuracy(216) AA Bonus [Skill] [Stat Amt: %i]", static_cast(attacker->aabonuses.Accuracy[skillinuse])/15.0f,attacker->aabonuses.Accuracy[skillinuse]); - if (attacker->itembonuses.HitChance) - Message(0, "# %.2f #### ATTACKER Accuracy Item Mod2 Bonus [Stat Amt: %i]", static_cast(attacker->itembonuses.HitChance)/15.0f,attacker->itembonuses.HitChance); - } - - hitBonus += chance_mod; //Modifier applied from casted/disc skill attacks. - - if(attacker->IsNPC()){ - if (acc_override){ - hitBonus = (acc_override / 10.0f); - if (Msg >= 2) - Message(0, "# %.2f #### ATTACKER 'ACCURACY OVERRIDE'", hitBonus); - } - else { - hitBonus += ((attacker->CastToNPC()->GetAccuracyRating() + add_acc) / 10.0f); //Modifier from database - if (Msg >= 2){ - Message(0, "# %.2f #### ATTACKER NPC ACCURACY STAT [Stat Amt: %i] ", ((attacker->CastToNPC()->GetAccuracyRating() + add_avoid) / 10.0f),attacker->CastToNPC()->GetAccuracyRating()); - } - } - } - else if(attacker->IsClient()){ - if (acc_override){ - hitBonus = (acc_override / 15.0f); - if (Msg >= 2) - Message(0, "# %.2f #### ATTACKER 'ACCURACY OVERRIDE': %.2f "); - } - else - hitBonus += (add_acc / 15.0f); //Modifier from database - } - - if (skillinuse == EQ::skills::SkillArchery){ - hitBonus -= hitBonus*RuleR(Combat, ArcheryHitPenalty); - if (Msg >= 2) - Message(0, "# %.2f pct #### RuleR(Combat, ArcheryHitPenalty) ", RuleR(Combat, ArcheryHitPenalty)); - } - - //Calculate final chance to hit - chancetohit += ((chancetohit * (hitBonus - avoidanceBonus)) / 100.0f); - - if (Msg){ - Message(0, "#FINAL Accuracy Bonus': %.2f percent", hitBonus); - - if (Msg >= 2) - Message(0, " "); - - Message(0, "#FINAL Hit Chance: %.2f percent [Max: %.2f Min: %.2f] ", chancetohit, RuleR(Combat,MaxChancetoHit), RuleR(Combat,MinChancetoHit) ); - Message(0, "######### Hit Chance Report: Completed #########"); - } - - chancetohit = mod_hit_chance(chancetohit, skillinuse, attacker); - - // Chance to hit; Max 95%, Min 5% DEFAULTS - if(chancetohit > 1000 || chancetohit < -1000) { - //if chance to hit is crazy high, that means a discipline is in use, and let it stay there - } - else if(chancetohit > RuleR(Combat,MaxChancetoHit)) { - chancetohit = RuleR(Combat,MaxChancetoHit); - } - else if(chancetohit < RuleR(Combat,MinChancetoHit)) { - chancetohit = RuleR(Combat,MinChancetoHit); - } - - return(chancetohit); + // tie breaker? Don't want to be biased any one way + if (tohit_roll == avoid_roll) + return zone->random.Roll(50); + return tohit_roll > avoid_roll; } From cbea7045fa60750aece48289f4e17b3188c72fd0 Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Thu, 11 Nov 2021 21:13:30 -0500 Subject: [PATCH 363/624] [Loginserver] Identify unknown login client packet fields (#1680) * Add player login reply struct * Use player login reply struct for failed logins * Use base message struct for login requests * Refactor server list reply serialization Use BaseMessage and BaseReplyMessage structs for server list and add flags for server type and status * Use reply message struct for login handshake Remove client version checks, the packets are the same for titanium and rof2 * Use base headers for join server requests * Log correct server list ip * Add compressed flag to base message header Document encrypt type flag more --- loginserver/client.cpp | 159 +++++++++++++++--------------- loginserver/client.h | 24 ++++- loginserver/login_structures.h | 139 +++++++++++++------------- loginserver/server_manager.cpp | 175 +++++++++------------------------ loginserver/server_manager.h | 2 +- loginserver/world_server.cpp | 99 +++++++++++++------ loginserver/world_server.h | 2 + 7 files changed, 290 insertions(+), 310 deletions(-) diff --git a/loginserver/client.cpp b/loginserver/client.cpp index e92d408dc..7f30e8434 100644 --- a/loginserver/client.cpp +++ b/loginserver/client.cpp @@ -120,36 +120,20 @@ void Client::Handle_SessionReady(const char *data, unsigned int size) m_client_status = cs_waiting_for_login; /** - * The packets are mostly the same but slightly different between the two versions + * The packets are identical between the two versions */ - if (m_client_version == cv_sod) { - auto *outapp = new EQApplicationPacket(OP_ChatMessage, 17); - outapp->pBuffer[0] = 0x02; - outapp->pBuffer[10] = 0x01; - outapp->pBuffer[11] = 0x65; + auto *outapp = new EQApplicationPacket(OP_ChatMessage, sizeof(LoginHandShakeReply_Struct)); + auto buf = reinterpret_cast(outapp->pBuffer); + buf->base_header.sequence = 0x02; + buf->base_reply.success = true; + buf->base_reply.error_str_id = 0x65; // 101 "No Error" - if (server.options.IsDumpOutPacketsOn()) { - DumpPacket(outapp); - } - - m_connection->QueuePacket(outapp); - delete outapp; + if (server.options.IsDumpOutPacketsOn()) { + DumpPacket(outapp); } - else { - const char *msg = "ChatMessage"; - auto *outapp = new EQApplicationPacket(OP_ChatMessage, 16 + strlen(msg)); - outapp->pBuffer[0] = 0x02; - outapp->pBuffer[10] = 0x01; - outapp->pBuffer[11] = 0x65; - strcpy((char *) (outapp->pBuffer + 15), msg); - if (server.options.IsDumpOutPacketsOn()) { - DumpPacket(outapp); - } - - m_connection->QueuePacket(outapp); - delete outapp; - } + m_connection->QueuePacket(outapp); + delete outapp; } /** @@ -165,14 +149,18 @@ void Client::Handle_Login(const char *data, unsigned int size) return; } - if ((size - 12) % 8 != 0) { - LogError("Login received packet of size: {0}, this would cause a block corruption, discarding", size); + // login user/pass are variable length after unencrypted opcode and base message header (size includes opcode) + constexpr int header_size = sizeof(uint16_t) + sizeof(LoginBaseMessage_Struct); + int data_size = size - header_size; + + if (size <= header_size) { + LogError("Login received packet of size: {0}, this would cause a buffer overflow, discarding", size); return; } - if (size < sizeof(LoginLoginRequest_Struct)) { - LogError("Login received packet of size: {0}, this would cause a buffer overflow, discarding", size); + if (data_size % 8 != 0) { + LogError("Login received packet of size: {0}, this would cause a block corruption, discarding", size); return; } @@ -189,13 +177,14 @@ void Client::Handle_Login(const char *data, unsigned int size) std::string db_account_password_hash; std::string outbuffer; - outbuffer.resize(size - 12); + outbuffer.resize(data_size); if (outbuffer.length() == 0) { LogError("Corrupt buffer sent to server, no length"); return; } - auto r = eqcrypt_block(data + 10, size - 12, &outbuffer[0], 0); + // data starts at base message header (opcode not included) + auto r = eqcrypt_block(data + sizeof(LoginBaseMessage_Struct), data_size, &outbuffer[0], 0); if (r == nullptr) { LogError("Failed to decrypt eqcrypt block"); return; @@ -209,7 +198,8 @@ void Client::Handle_Login(const char *data, unsigned int size) return; } - memcpy(&m_llrs, data, sizeof(LoginLoginRequest_Struct)); + // only need to copy the base header for reply options, ignore login info + memcpy(&m_llrs, data, sizeof(LoginBaseMessage_Struct)); bool result = false; if (outbuffer[0] == 0 && outbuffer[1] == 0) { @@ -297,8 +287,8 @@ void Client::Handle_Play(const char *data) } const auto *play = (const PlayEverquestRequest_Struct *) data; - auto server_id_in = (unsigned int) play->ServerNumber; - auto sequence_in = (unsigned int) play->Sequence; + auto server_id_in = (unsigned int) play->server_number; + auto sequence_in = (unsigned int) play->base_header.sequence; if (server.options.IsTraceOn()) { LogInfo( @@ -309,7 +299,7 @@ void Client::Handle_Play(const char *data) ); } - m_play_server_id = (unsigned int) play->ServerNumber; + m_play_server_id = (unsigned int) play->server_number; m_play_sequence_id = sequence_in; m_play_server_id = server_id_in; server.server_manager->SendUserToWorldRequest(server_id_in, m_account_id, m_loginserver_name); @@ -320,14 +310,13 @@ void Client::Handle_Play(const char *data) */ void Client::SendServerListPacket(uint32 seq) { - EQApplicationPacket *outapp = server.server_manager->CreateServerListPacket(this, seq); + auto outapp = server.server_manager->CreateServerListPacket(this, seq); if (server.options.IsDumpOutPacketsOn()) { - DumpPacket(outapp); + DumpPacket(outapp.get()); } - m_connection->QueuePacket(outapp); - delete outapp; + m_connection->QueuePacket(outapp.get()); } void Client::SendPlayResponse(EQApplicationPacket *outapp) @@ -412,16 +401,26 @@ void Client::DoFailedLogin() m_stored_user.clear(); m_stored_pass.clear(); - EQApplicationPacket outapp(OP_LoginAccepted, sizeof(LoginLoginFailed_Struct)); - auto *login_failed = (LoginLoginFailed_Struct *) outapp.pBuffer; + // unencrypted + LoginBaseMessage_Struct base_header{}; + base_header.sequence = m_llrs.sequence; // login (3) + base_header.encrypt_type = m_llrs.encrypt_type; - login_failed->unknown1 = m_llrs.unknown1; - login_failed->unknown2 = m_llrs.unknown2; - login_failed->unknown3 = m_llrs.unknown3; - login_failed->unknown4 = m_llrs.unknown4; - login_failed->unknown5 = m_llrs.unknown5; + // encrypted + PlayerLoginReply_Struct login_reply{}; + login_reply.base_reply.success = false; + login_reply.base_reply.error_str_id = 105; // Error - The username and/or password were not valid - memcpy(login_failed->unknown6, FailedLoginResponseData, sizeof(FailedLoginResponseData)); + char encrypted_buffer[80] = {0}; + auto rc = eqcrypt_block((const char*)&login_reply, sizeof(login_reply), encrypted_buffer, 1); + if (rc == nullptr) { + LogDebug("Failed to encrypt eqcrypt block for failed login"); + } + + constexpr int outsize = sizeof(LoginBaseMessage_Struct) + sizeof(encrypted_buffer); + EQApplicationPacket outapp(OP_LoginAccepted, outsize); + outapp.WriteData(&base_header, sizeof(base_header)); + outapp.WriteData(&encrypted_buffer, sizeof(encrypted_buffer)); if (server.options.IsDumpOutPacketsOn()) { DumpPacket(&outapp); @@ -539,51 +538,47 @@ void Client::DoSuccessfulLogin( m_account_name = in_account_name; m_loginserver_name = db_loginserver; - auto *outapp = new EQApplicationPacket(OP_LoginAccepted, 10 + 80); - auto *login_accepted = (LoginAccepted_Struct *) outapp->pBuffer; - login_accepted->unknown1 = m_llrs.unknown1; - login_accepted->unknown2 = m_llrs.unknown2; - login_accepted->unknown3 = m_llrs.unknown3; - login_accepted->unknown4 = m_llrs.unknown4; - login_accepted->unknown5 = m_llrs.unknown5; + // unencrypted + LoginBaseMessage_Struct base_header{}; + base_header.sequence = m_llrs.sequence; + base_header.compressed = false; + base_header.encrypt_type = m_llrs.encrypt_type; + base_header.unk3 = m_llrs.unk3; - auto *login_failed_attempts = new LoginFailedAttempts_Struct; - memset(login_failed_attempts, 0, sizeof(LoginFailedAttempts_Struct)); - - login_failed_attempts->failed_attempts = 0; - login_failed_attempts->message = 0x01; - login_failed_attempts->lsid = db_account_id; - login_failed_attempts->unknown3[3] = 0x03; - login_failed_attempts->unknown4[3] = 0x02; - login_failed_attempts->unknown5[0] = 0xe7; - login_failed_attempts->unknown5[1] = 0x03; - login_failed_attempts->unknown6[0] = 0xff; - login_failed_attempts->unknown6[1] = 0xff; - login_failed_attempts->unknown6[2] = 0xff; - login_failed_attempts->unknown6[3] = 0xff; - login_failed_attempts->unknown7[0] = 0xa0; - login_failed_attempts->unknown7[1] = 0x05; - login_failed_attempts->unknown8[3] = 0x02; - login_failed_attempts->unknown9[0] = 0xff; - login_failed_attempts->unknown9[1] = 0x03; - login_failed_attempts->unknown11[0] = 0x63; - login_failed_attempts->unknown12[0] = 0x01; - memcpy(login_failed_attempts->key, m_key.c_str(), m_key.size()); + // not serializing any of the variable length strings so just use struct directly + PlayerLoginReply_Struct login_reply{}; + login_reply.base_reply.success = true; + login_reply.base_reply.error_str_id = 101; // No Error + login_reply.unk1 = 0; + login_reply.unk2 = 0; + login_reply.lsid = db_account_id; + login_reply.failed_attempts = 0; + login_reply.show_player_count = false; // todo: config option + login_reply.offer_min_days = 99; + login_reply.offer_min_views = -1; + login_reply.offer_cooldown_minutes = 0; + login_reply.web_offer_number = 0; + login_reply.web_offer_min_days = 99; + login_reply.web_offer_min_views = -1; + login_reply.web_offer_cooldown_minutes = 0; + memcpy(login_reply.key, m_key.c_str(), m_key.size()); char encrypted_buffer[80] = {0}; - auto rc = eqcrypt_block((const char *) login_failed_attempts, 75, encrypted_buffer, 1); + auto rc = eqcrypt_block((const char*)&login_reply, sizeof(login_reply), encrypted_buffer, 1); if (rc == nullptr) { LogDebug("Failed to encrypt eqcrypt block"); } - memcpy(login_accepted->encrypt, encrypted_buffer, 80); + constexpr int outsize = sizeof(LoginBaseMessage_Struct) + sizeof(encrypted_buffer); + auto outapp = std::make_unique(OP_LoginAccepted, outsize); + outapp->WriteData(&base_header, sizeof(base_header)); + outapp->WriteData(&encrypted_buffer, sizeof(encrypted_buffer)); if (server.options.IsDumpOutPacketsOn()) { - DumpPacket(outapp); + DumpPacket(outapp.get()); } - m_connection->QueuePacket(outapp); - delete outapp; + m_connection->QueuePacket(outapp.get()); m_client_status = cs_logged_in; } diff --git a/loginserver/client.h b/loginserver/client.h index 4089e3d29..1a9b931fd 100644 --- a/loginserver/client.h +++ b/loginserver/client.h @@ -23,6 +23,28 @@ enum LSClientStatus { cs_logged_in }; +namespace LS { + namespace ServerStatusFlags { + enum eServerStatusFlags { + Up = 0, // default + Down = 1, + Unused = 2, + Locked = 4 // can be combined with Down to show "Locked (Down)" + }; + } + + namespace ServerTypeFlags { + enum eServerTypeFlags { + None = 0, + Standard = 1, + Unknown2 = 2, + Unknown4 = 4, + Preferred = 8, + Legends = 16 // can be combined with Preferred flag to override color in Legends section with Preferred color (green) + }; + } +} + /** * Client class, controls a single client and it's connection to the login server */ @@ -189,7 +211,7 @@ private: std::unique_ptr m_login_connection_manager; std::shared_ptr m_login_connection; - LoginLoginRequest_Struct m_llrs; + LoginBaseMessage_Struct m_llrs; std::string m_stored_user; std::string m_stored_pass; diff --git a/loginserver/login_structures.h b/loginserver/login_structures.h index 35564cff9..01a0bf201 100644 --- a/loginserver/login_structures.h +++ b/loginserver/login_structures.h @@ -3,91 +3,94 @@ #pragma pack(1) -struct LoginChatMessage_Struct { - short Unknown0; - uint32 Unknown1; - uint32 Unknown2; - uint32 Unknown3; - uint8 Unknown4; - char ChatMessage[1]; +// unencrypted base message header in all packets +struct LoginBaseMessage_Struct +{ + int32_t sequence; // request type/login sequence (2: handshake, 3: login, 4: serverlist, ...) + bool compressed; // true: deflated + int8_t encrypt_type; // 1: invert (unused) 2: des (2 for encrypted player logins and order expansions) (client uses what it sent, ignores in reply) + int32_t unk3; // unused? }; -struct LoginLoginRequest_Struct { - short unknown1; - short unknown2; - short unknown3; - short unknown4; - short unknown5; - char unknown6[16]; +struct LoginBaseReplyMessage_Struct +{ + bool success; // 0: failure (shows error string) 1: success + int32_t error_str_id; // last error eqlsstr id, default: 101 (no error) + char str[1]; // variable length, unknown (may be unused, this struct is a common pattern elsewhere) }; -struct LoginAccepted_Struct { - short unknown1; - short unknown2; - short unknown3; - short unknown4; - short unknown5; - char encrypt[80]; +struct LoginHandShakeReply_Struct +{ + LoginBaseMessage_Struct base_header; + LoginBaseReplyMessage_Struct base_reply; + char unknown[1]; // variable length string }; -struct LoginFailedAttempts_Struct { - char message; //0x01 - char unknown2[7]; //0x00 - uint32 lsid; - char key[11]; //10 char + null term; - uint32 failed_attempts; - char unknown3[4]; //0x00, 0x00, 0x00, 0x03 - char unknown4[4]; //0x00, 0x00, 0x00, 0x02 - char unknown5[4]; //0xe7, 0x03, 0x00, 0x00 - char unknown6[4]; //0xff, 0xff, 0xff, 0xff - char unknown7[4]; //0xa0, 0x05, 0x00, 0x00 - char unknown8[4]; //0x00, 0x00, 0x00, 0x02 - char unknown9[4]; //0xff, 0x03, 0x00, 0x00 - char unknown10[4]; //0x00, 0x00, 0x00, 0x00 - char unknown11[4]; //0x63, 0x00, 0x00, 0x00 - char unknown12[4]; //0x01, 0x00, 0x00, 0x00 - char unknown13[4]; //0x00, 0x00, 0x00, 0x00 - char unknown14[4]; //0x00, 0x00, 0x00, 0x00 +// for reference, login buffer is variable (minimum size 8 due to encryption) +struct PlayerLogin_Struct +{ + LoginBaseMessage_Struct base_header; + char username[1]; + char password[1]; }; -struct LoginLoginFailed_Struct { - short unknown1; - short unknown2; - short unknown3; - short unknown4; - short unknown5; - char unknown6[74]; +// variable length, can use directly if not serializing strings +struct PlayerLoginReply_Struct +{ + // base header excluded to make struct data easier to encrypt + //LoginBaseMessage_Struct base_header; + LoginBaseReplyMessage_Struct base_reply; + + int8_t unk1; // (default: 0) + int8_t unk2; // (default: 0) + int32_t lsid; // (default: -1) + char key[11]; // client reads until null (variable length) + int32_t failed_attempts; + bool show_player_count; // admin flag, enables admin button and shows server player counts (default: false) + int32_t offer_min_days; // guess, needs more investigation, maybe expansion offers (default: 99) + int32_t offer_min_views; // guess (default: -1) + int32_t offer_cooldown_minutes; // guess (default: 0) + int32_t web_offer_number; // web order view number, 0 nothing (default: 0) + int32_t web_offer_min_days; // number of days to show offer (based on first offer time in client eqls ini) (default: 99) + int32_t web_offer_min_views; // mininum views, -1 for no minimum, 0 for never shows (based on client eqls ini) (default: -1) + int32_t web_offer_cooldown_minutes; // minimum minutes between offers (based on last offer time in client eqls ini) (default: 0) + char username[1]; // variable length, if not empty client attempts to re-login to server select when quitting from char select and sends this in a struct + char unknown[1]; // variable length, password unlikely? client doesn't send this on re-login from char select }; -struct ServerListHeader_Struct { +// variable length, for reference +struct LoginClientServerData_Struct +{ + char ip[1]; + int32_t server_type; // legends, preferred, standard + int32_t server_id; + char server_name[1]; + char country_code[1]; // if doesn't match client locale then server is colored dark grey in list and joining is prevented (to block for "us" use one of "kr", "tw", "jp", "de", "fr", or "cn") (ISO 3166-1 alpha-2) + char language_code[1]; + int32_t server_status; // see ServerStatusFlags + int32_t player_count; +}; - uint32 Unknown1; - uint32 Unknown2; - uint32 Unknown3; - uint32 Unknown4; - uint32 NumberOfServers; +// variable length, for reference +struct ServerListReply_Struct +{ + LoginBaseMessage_Struct base_header; + LoginBaseReplyMessage_Struct base_reply; + + int32_t server_count; + LoginClientServerData_Struct servers[0]; }; struct PlayEverquestRequest_Struct { - uint16 Sequence; - uint32 Unknown1; - uint32 Unknown2; - uint32 ServerNumber; + LoginBaseMessage_Struct base_header; + uint32 server_number; }; +// SCJoinServerReply struct PlayEverquestResponse_Struct { - uint8 Sequence; - uint8 Unknown1[9]; - uint8 Allowed; - uint16 Message; - uint8 Unknown2[3]; - uint32 ServerNumber; -}; - -static const unsigned char FailedLoginResponseData[] = { - 0xf6, 0x85, 0x9c, 0x23, 0x57, 0x7e, 0x3e, 0x55, 0xb3, 0x4c, 0xf8, 0xc8, 0xcb, 0x77, 0xd5, 0x16, - 0x09, 0x7a, 0x63, 0xdc, 0x57, 0x7e, 0x3e, 0x55, 0xb3, 0x4c, 0xf8, 0xc8, 0xcb, 0x77, 0xd5, 0x16, - 0x09, 0x7a, 0x63, 0xdc, 0x57, 0x7e, 0x3e, 0x55, 0xb3 + LoginBaseMessage_Struct base_header; + LoginBaseReplyMessage_Struct base_reply; + uint32 server_number; }; diff --git a/loginserver/server_manager.cpp b/loginserver/server_manager.cpp index e3b839189..d49809278 100644 --- a/loginserver/server_manager.cpp +++ b/loginserver/server_manager.cpp @@ -97,9 +97,8 @@ WorldServer *ServerManager::GetServerByAddress(const std::string &ip_address, in * @param sequence * @return */ -EQApplicationPacket *ServerManager::CreateServerListPacket(Client *client, uint32 sequence) +std::unique_ptr ServerManager::CreateServerListPacket(Client *client, uint32 sequence) { - unsigned int packet_size = sizeof(ServerListHeader_Struct); unsigned int server_count = 0; in_addr in{}; in.s_addr = client->GetConnection()->GetRemoteIP(); @@ -107,144 +106,60 @@ EQApplicationPacket *ServerManager::CreateServerListPacket(Client *client, uint3 LogDebug("ServerManager::CreateServerListPacket via client address [{0}]", client_ip); - auto iter = m_world_servers.begin(); - while (iter != m_world_servers.end()) { - if (!(*iter)->IsAuthorized()) { + for (const auto& world_server : m_world_servers) + { + if (world_server->IsAuthorized()) { + ++server_count; + } + } + + SerializeBuffer buf; + + // LoginBaseMessage_Struct header + buf.WriteInt32(sequence); + buf.WriteInt8(0); + buf.WriteInt8(0); + buf.WriteInt32(0); + + // LoginBaseReplyMessage_Struct + buf.WriteInt8(true); // success (no error) + buf.WriteInt32(0x65); // 101 "No Error" eqlsstr + buf.WriteString(""); + + // ServerListReply_Struct + buf.WriteInt32(server_count); + + for (const auto& world_server : m_world_servers) + { + if (!world_server->IsAuthorized()) { LogDebug( - "ServerManager::CreateServerListPacket | Server [{0}] via IP [{1}] is not authorized to be listed", - (*iter)->GetServerLongName(), - (*iter)->GetConnection()->Handle()->RemoteIP() + "ServerManager::CreateServerListPacket | Server [{}] via IP [{}] is not authorized to be listed", + world_server->GetServerLongName(), + world_server->GetConnection()->Handle()->RemoteIP() ); - ++iter; continue; } - std::string world_ip = (*iter)->GetConnection()->Handle()->RemoteIP(); - if (world_ip == client_ip) { - packet_size += (*iter)->GetServerLongName().size() + (*iter)->GetLocalIP().size() + 24; + bool use_local_ip = false; - LogDebug( - "CreateServerListPacket | Building list entry | Client [{0}] IP [{1}] Server Long Name [{2}] Server IP [{3}] (Local)", - client->GetAccountName(), - client_ip, - (*iter)->GetServerLongName(), - (*iter)->GetLocalIP() - ); - } - else if (IpUtil::IsIpInPrivateRfc1918(client_ip)) { - packet_size += (*iter)->GetServerLongName().size() + (*iter)->GetLocalIP().size() + 24; - - LogDebug( - "CreateServerListPacket | Building list entry | Client [{0}] IP [{1}] Server Long Name [{2}] Server IP [{3}] (Local)", - client->GetAccountName(), - client_ip, - (*iter)->GetServerLongName(), - (*iter)->GetLocalIP() - ); - } - else { - packet_size += (*iter)->GetServerLongName().size() + (*iter)->GetRemoteIP().size() + 24; - - LogDebug( - "CreateServerListPacket | Building list entry | Client [{0}] IP [{1}] Server Long Name [{2}] Server IP [{3}] (Remote)", - client->GetAccountName(), - client_ip, - (*iter)->GetServerLongName(), - (*iter)->GetRemoteIP() - ); + std::string world_ip = world_server->GetConnection()->Handle()->RemoteIP(); + if (world_ip == client_ip || IpUtil::IsIpInPrivateRfc1918(client_ip)) { + use_local_ip = true; } - server_count++; - ++iter; + LogDebug( + "CreateServerListPacket | Building list entry | Client [{}] IP [{}] Server Long Name [{}] Server IP [{}] ({})", + client->GetAccountName(), + client_ip, + world_server->GetServerLongName(), + use_local_ip ? world_server->GetLocalIP() : world_server->GetRemoteIP(), + use_local_ip ? "Local" : "Remote" + ); + + world_server->SerializeForClientServerList(buf, use_local_ip); } - auto *outapp = new EQApplicationPacket(OP_ServerListResponse, packet_size); - auto *server_list = (ServerListHeader_Struct *) outapp->pBuffer; - - server_list->Unknown1 = sequence; - server_list->Unknown2 = 0x00000000; - server_list->Unknown3 = 0x01650000; - - /** - * Not sure what this is but it should be noted setting it to - * 0xFFFFFFFF crashes the client so: don't do that. - */ - server_list->Unknown4 = 0x00000000; - server_list->NumberOfServers = server_count; - - unsigned char *data_pointer = outapp->pBuffer; - data_pointer += sizeof(ServerListHeader_Struct); - - iter = m_world_servers.begin(); - while (iter != m_world_servers.end()) { - if (!(*iter)->IsAuthorized()) { - ++iter; - continue; - } - - std::string world_ip = (*iter)->GetConnection()->Handle()->RemoteIP(); - if (world_ip == client_ip) { - memcpy(data_pointer, (*iter)->GetLocalIP().c_str(), (*iter)->GetLocalIP().size()); - data_pointer += ((*iter)->GetLocalIP().size() + 1); - } - else if (IpUtil::IsIpInPrivateRfc1918(client_ip)) { - memcpy(data_pointer, (*iter)->GetLocalIP().c_str(), (*iter)->GetLocalIP().size()); - data_pointer += ((*iter)->GetLocalIP().size() + 1); - } - else { - memcpy(data_pointer, (*iter)->GetRemoteIP().c_str(), (*iter)->GetRemoteIP().size()); - data_pointer += ((*iter)->GetRemoteIP().size() + 1); - } - - switch ((*iter)->GetServerListID()) { - case 1: { - *(unsigned int *) data_pointer = 0x00000030; - break; - } - case 2: { - *(unsigned int *) data_pointer = 0x00000009; - break; - } - default: { - *(unsigned int *) data_pointer = 0x00000001; - } - } - - data_pointer += 4; - - *(unsigned int *) data_pointer = (*iter)->GetServerId(); - data_pointer += 4; - - memcpy(data_pointer, (*iter)->GetServerLongName().c_str(), (*iter)->GetServerLongName().size()); - data_pointer += ((*iter)->GetServerLongName().size() + 1); - - memcpy(data_pointer, "EN", 2); - data_pointer += 3; - - memcpy(data_pointer, "US", 2); - data_pointer += 3; - - // 0 = Up, 1 = Down, 2 = Up, 3 = down, 4 = locked, 5 = locked(down) - if ((*iter)->GetStatus() < 0) { - if ((*iter)->GetZonesBooted() == 0) { - *(uint32 *) data_pointer = 0x01; - } - else { - *(uint32 *) data_pointer = 0x04; - } - } - else { - *(uint32 *) data_pointer = 0x02; - } - data_pointer += 4; - - *(uint32 *) data_pointer = (*iter)->GetPlayersOnline(); - data_pointer += 4; - - ++iter; - } - - return outapp; + return std::make_unique(OP_ServerListResponse, buf); } /** diff --git a/loginserver/server_manager.h b/loginserver/server_manager.h index c89c7c85e..ac652a2b5 100644 --- a/loginserver/server_manager.h +++ b/loginserver/server_manager.h @@ -45,7 +45,7 @@ public: * @param sequence * @return */ - EQApplicationPacket *CreateServerListPacket(Client *client, uint32 sequence); + std::unique_ptr CreateServerListPacket(Client *client, uint32 sequence); /** * Checks to see if there is a server exists with this name, ignoring option diff --git a/loginserver/world_server.cpp b/loginserver/world_server.cpp index d6917c845..b6dc6885d 100644 --- a/loginserver/world_server.cpp +++ b/loginserver/world_server.cpp @@ -216,11 +216,11 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne ); auto *per = (PlayEverquestResponse_Struct *) outapp->pBuffer; - per->Sequence = client->GetPlaySequence(); - per->ServerNumber = client->GetPlayServerID(); + per->base_header.sequence = client->GetPlaySequence(); + per->server_number = client->GetPlayServerID(); if (user_to_world_response->response > 0) { - per->Allowed = 1; + per->base_reply.success = true; SendClientAuth( client->GetConnection()->GetRemoteAddr(), client->GetAccountName(), @@ -232,34 +232,34 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne switch (user_to_world_response->response) { case UserToWorldStatusSuccess: - per->Message = 101; + per->base_reply.error_str_id = 101; break; case UserToWorldStatusWorldUnavail: - per->Message = 326; + per->base_reply.error_str_id = 326; break; case UserToWorldStatusSuspended: - per->Message = 337; + per->base_reply.error_str_id = 337; break; case UserToWorldStatusBanned: - per->Message = 338; + per->base_reply.error_str_id = 338; break; case UserToWorldStatusWorldAtCapacity: - per->Message = 339; + per->base_reply.error_str_id = 339; break; case UserToWorldStatusAlreadyOnline: - per->Message = 111; + per->base_reply.error_str_id = 111; break; default: - per->Message = 102; + per->base_reply.error_str_id = 102; } if (server.options.IsWorldTraceOn()) { LogDebug( "Sending play response: allowed [{0}] sequence [{1}] server number [{2}] message [{3}]", - per->Allowed, - per->Sequence, - per->ServerNumber, - per->Message + per->base_reply.success, + per->base_header.sequence, + per->server_number, + per->base_reply.error_str_id ); LogDebug("[Size: [{0}]] {1}", outapp->size, DumpPacketToString(outapp)); @@ -334,8 +334,8 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac ); auto *per = (PlayEverquestResponse_Struct *) outapp->pBuffer; - per->Sequence = client->GetPlaySequence(); - per->ServerNumber = client->GetPlayServerID(); + per->base_header.sequence = client->GetPlaySequence(); + per->server_number = client->GetPlayServerID(); LogDebug( "Found sequence and play of [{0}] [{1}]", @@ -346,7 +346,7 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac LogDebug("[Size: [{0}]] {1}", outapp->size, DumpPacketToString(outapp)); if (user_to_world_response->response > 0) { - per->Allowed = 1; + per->base_reply.success = true; SendClientAuth( client->GetConnection()->GetRemoteAddr(), client->GetAccountName(), @@ -358,34 +358,34 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac switch (user_to_world_response->response) { case UserToWorldStatusSuccess: - per->Message = 101; + per->base_reply.error_str_id = 101; break; case UserToWorldStatusWorldUnavail: - per->Message = 326; + per->base_reply.error_str_id = 326; break; case UserToWorldStatusSuspended: - per->Message = 337; + per->base_reply.error_str_id = 337; break; case UserToWorldStatusBanned: - per->Message = 338; + per->base_reply.error_str_id = 338; break; case UserToWorldStatusWorldAtCapacity: - per->Message = 339; + per->base_reply.error_str_id = 339; break; case UserToWorldStatusAlreadyOnline: - per->Message = 111; + per->base_reply.error_str_id = 111; break; default: - per->Message = 102; + per->base_reply.error_str_id = 102; } if (server.options.IsTraceOn()) { LogDebug( "Sending play response with following data, allowed [{0}], sequence {1}, server number {2}, message {3}", - per->Allowed, - per->Sequence, - per->ServerNumber, - per->Message + per->base_reply.success, + per->base_header.sequence, + per->server_number, + per->base_reply.error_str_id ); LogDebug("[Size: [{0}]] {1}", outapp->size, DumpPacketToString(outapp)); } @@ -1025,6 +1025,49 @@ bool WorldServer::ValidateWorldServerAdminLogin( return false; } +void WorldServer::SerializeForClientServerList(SerializeBuffer& out, bool use_local_ip) const +{ + // see LoginClientServerData_Struct + if (use_local_ip) { + out.WriteString(GetLocalIP()); + } else { + out.WriteString(GetRemoteIP()); + } + + switch (GetServerListID()) + { + case 1: + out.WriteInt32(LS::ServerTypeFlags::Legends); + break; + case 2: + out.WriteInt32(LS::ServerTypeFlags::Preferred); + break; + default: + out.WriteInt32(LS::ServerTypeFlags::Standard); + break; + } + + out.WriteUInt32(GetServerId()); + out.WriteString(GetServerLongName()); + out.WriteString("us"); // country code + out.WriteString("en"); // language code + + // 0 = Up, 1 = Down, 2 = Up, 3 = down, 4 = locked, 5 = locked(down) + if (GetStatus() < 0) { + if (GetZonesBooted() == 0) { + out.WriteInt32(LS::ServerStatusFlags::Down); + } + else { + out.WriteInt32(LS::ServerStatusFlags::Locked); + } + } + else { + out.WriteInt32(LS::ServerStatusFlags::Up); + } + + out.WriteUInt32(GetPlayersOnline()); +} + /** * @param in_server_list_id * @return diff --git a/loginserver/world_server.h b/loginserver/world_server.h index f3e533b91..af63d22a9 100644 --- a/loginserver/world_server.h +++ b/loginserver/world_server.h @@ -150,6 +150,8 @@ public: bool HandleNewLoginserverRegisteredOnly(Database::DbWorldRegistration &world_registration); bool HandleNewLoginserverInfoUnregisteredAllowed(Database::DbWorldRegistration &world_registration); + void SerializeForClientServerList(class SerializeBuffer& out, bool use_local_ip) const; + private: /** From a6c85babfc4d01a5c079969937591d4c5bc97807 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Thu, 11 Nov 2021 22:38:41 -0600 Subject: [PATCH 364/624] [Loginserver] Add config option to display player count (#1738) * [Loginserver] Add config option to display player count * Update name --- loginserver/client.cpp | 2 +- loginserver/login_util/login.json | 1 + loginserver/main.cpp | 2 ++ loginserver/options.h | 11 +++++++++++ 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/loginserver/client.cpp b/loginserver/client.cpp index 7f30e8434..01a5abf50 100644 --- a/loginserver/client.cpp +++ b/loginserver/client.cpp @@ -553,7 +553,7 @@ void Client::DoSuccessfulLogin( login_reply.unk2 = 0; login_reply.lsid = db_account_id; login_reply.failed_attempts = 0; - login_reply.show_player_count = false; // todo: config option + login_reply.show_player_count = server.options.IsShowPlayerCountEnabled(); login_reply.offer_min_days = 99; login_reply.offer_min_views = -1; login_reply.offer_cooldown_minutes = 0; diff --git a/loginserver/login_util/login.json b/loginserver/login_util/login.json index 6fbd8f742..af0f042cf 100644 --- a/loginserver/login_util/login.json +++ b/loginserver/login_util/login.json @@ -11,6 +11,7 @@ }, "worldservers": { "unregistered_allowed": true, + "show_player_count": false, "reject_duplicate_servers": false }, "web_api": { diff --git a/loginserver/main.cpp b/loginserver/main.cpp index 50f86d673..be925efa6 100644 --- a/loginserver/main.cpp +++ b/loginserver/main.cpp @@ -64,6 +64,7 @@ void LoadServerConfig() false )); server.options.AllowUnregistered(server.config.GetVariableBool("worldservers", "unregistered_allowed", true)); + server.options.SetShowPlayerCount(server.config.GetVariableBool("worldservers", "show_player_count", false)); /** * Account @@ -240,6 +241,7 @@ int main(int argc, char **argv) #endif LogInfo("[Config] [WorldServer] IsRejectingDuplicateServers [{0}]", server.options.IsRejectingDuplicateServers()); LogInfo("[Config] [WorldServer] IsUnregisteredAllowed [{0}]", server.options.IsUnregisteredAllowed()); + LogInfo("[Config] [WorldServer] ShowPlayerCount [{0}]", server.options.IsShowPlayerCountEnabled()); LogInfo("[Config] [Security] GetEncryptionMode [{0}]", server.options.GetEncryptionMode()); LogInfo("[Config] [Security] IsTokenLoginAllowed [{0}]", server.options.IsTokenLoginAllowed()); LogInfo("[Config] [Security] IsPasswordLoginAllowed [{0}]", server.options.IsPasswordLoginAllowed()); diff --git a/loginserver/options.h b/loginserver/options.h index e38114500..c6fc1589d 100644 --- a/loginserver/options.h +++ b/loginserver/options.h @@ -113,6 +113,15 @@ public: inline void UpdateInsecurePasswords(bool b) { update_insecure_passwords = b; } inline bool IsUpdatingInsecurePasswords() const { return update_insecure_passwords; } + inline bool IsShowPlayerCountEnabled() const + { + return show_player_count; + } + inline void SetShowPlayerCount(bool show_player_count) + { + Options::show_player_count = show_player_count; + } + private: bool allow_unregistered; bool trace; @@ -122,6 +131,7 @@ private: bool reject_duplicate_servers; bool allow_token_login; bool allow_password_login; + bool show_player_count; bool auto_create_accounts; bool auto_link_accounts; bool update_insecure_passwords; @@ -130,5 +140,6 @@ private: std::string default_loginserver_name; }; + #endif From dc1c7bb284968463cb8b2841d8fd5b782a1d43d5 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 12 Nov 2021 05:53:44 -0500 Subject: [PATCH 365/624] [Commands] Remove #serversidename Command. (#1720) - Remove unused command. --- zone/command.cpp | 8 -------- zone/command.h | 1 - 2 files changed, 9 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 7f44ad6df..1e420ad5d 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -985,14 +985,6 @@ void command_setfaction(Client *c, const Seperator *sep) content_db.QueryDatabase(query); } -void command_serversidename(Client *c, const Seperator *sep) -{ - if(c->GetTarget()) - c->Message(Chat::White, c->GetTarget()->GetName()); - else - c->Message(Chat::White, "Error: no target"); -} - void command_wc(Client *c, const Seperator *sep) { if (sep->argnum < 2) { diff --git a/zone/command.h b/zone/command.h index d6464c8d1..273cd19f3 100644 --- a/zone/command.h +++ b/zone/command.h @@ -274,7 +274,6 @@ void command_sendzonespawns(Client *c, const Seperator *sep); void command_sensetrap(Client *c, const Seperator *sep); void command_serverinfo(Client *c, const Seperator *sep); void command_serverrules(Client *c, const Seperator *sep); -void command_serversidename(Client *c, const Seperator *sep); void command_set_adventure_points(Client *c, const Seperator *sep); void command_setaapts(Client *c, const Seperator *sep); void command_setaaxp(Client *c, const Seperator *sep); From 7d495c56b3574f1e715df2d99ea34660f626c6b1 Mon Sep 17 00:00:00 2001 From: JJ <3617814+joligario@users.noreply.github.com> Date: Fri, 12 Nov 2021 05:54:21 -0500 Subject: [PATCH 366/624] [Logs] Show local_address in correct location (#1721) --- world/zoneserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 7921c4466..6403b2540 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -614,7 +614,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { if (sci->local_address[0]) { strn0cpy(client_local_address, sci->local_address, 250); - LogInfo("Zone specified local address [{}]", sci->address); + LogInfo("Zone specified local address [{}]", sci->local_address); } if (sci->process_id) { From caf32290b826b0bcc4a30e67a481171c2fa9a370 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 12 Nov 2021 05:54:44 -0500 Subject: [PATCH 367/624] [Commands] Remove #sendop Command. (#1722) - Remove unused command. --- zone/command.h | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/command.h b/zone/command.h index 273cd19f3..fe5602bc1 100644 --- a/zone/command.h +++ b/zone/command.h @@ -269,7 +269,6 @@ void command_save(Client *c, const Seperator *sep); void command_scale(Client *c, const Seperator *sep); void command_scribespell(Client *c, const Seperator *sep); void command_scribespells(Client *c, const Seperator *sep); -void command_sendop(Client *c, const Seperator *sep); void command_sendzonespawns(Client *c, const Seperator *sep); void command_sensetrap(Client *c, const Seperator *sep); void command_serverinfo(Client *c, const Seperator *sep); From 87cdf7feb1bdb148b0329066f0920af8a64fb364 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 12 Nov 2021 05:55:06 -0500 Subject: [PATCH 368/624] [Commands] Remove #synctod Command. (#1723) - Remove unused command. --- zone/command.h | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/command.h b/zone/command.h index fe5602bc1..688c26475 100644 --- a/zone/command.h +++ b/zone/command.h @@ -311,7 +311,6 @@ void command_summon(Client *c, const Seperator *sep); void command_summonburiedplayercorpse(Client *c, const Seperator *sep); void command_summonitem(Client *c, const Seperator *sep); void command_suspend(Client *c, const Seperator *sep); -void command_synctod(Client *c, const Seperator *sep); void command_task(Client *c, const Seperator *sep); void command_tattoo(Client *c, const Seperator *sep); void command_tempname(Client *c, const Seperator *sep); From 4a376b78597fb794ac259faf5e2df39e3173aa29 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 12 Nov 2021 05:55:35 -0500 Subject: [PATCH 369/624] [Commands] Remove #testspawn and #testspawnkill Commands. (#1724) - Remove unused commands. --- zone/command.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/zone/command.h b/zone/command.h index 688c26475..e03969627 100644 --- a/zone/command.h +++ b/zone/command.h @@ -315,8 +315,6 @@ void command_task(Client *c, const Seperator *sep); void command_tattoo(Client *c, const Seperator *sep); void command_tempname(Client *c, const Seperator *sep); void command_petname(Client *c, const Seperator *sep); -void command_testspawn(Client *c, const Seperator *sep); -void command_testspawnkill(Client *c, const Seperator *sep); void command_texture(Client *c, const Seperator *sep); void command_time(Client *c, const Seperator *sep); void command_timers(Client *c, const Seperator *sep); From 7b022502da510bd9b7a9ade8529bfda655606d5f Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 12 Nov 2021 05:55:54 -0500 Subject: [PATCH 370/624] [Commands] Remove #qtest Command. (#1725) - Remove unused command. --- zone/command.h | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/command.h b/zone/command.h index e03969627..60920cbd7 100644 --- a/zone/command.h +++ b/zone/command.h @@ -238,7 +238,6 @@ void command_proximity(Client *c, const Seperator *sep); void command_push(Client *c, const Seperator *sep); void command_pvp(Client *c, const Seperator *sep); void command_qglobal(Client *c, const Seperator *sep); -void command_qtest(Client *c, const Seperator *sep); void command_questerrors(Client *c, const Seperator *sep); void command_race(Client *c, const Seperator *sep); void command_raidloot(Client* c, const Seperator *sep); From 6f79ea117c458123785a2f735b42ba4119b993f6 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 12 Nov 2021 05:56:11 -0500 Subject: [PATCH 371/624] [Commands] Remove #refundaa Command. (#1726) - Remove unused command. --- zone/command.h | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/command.h b/zone/command.h index 60920cbd7..263935484 100644 --- a/zone/command.h +++ b/zone/command.h @@ -243,7 +243,6 @@ void command_race(Client *c, const Seperator *sep); void command_raidloot(Client* c, const Seperator *sep); void command_randomfeatures(Client *c, const Seperator *sep); void command_refreshgroup(Client *c, const Seperator *sep); -void command_refundaa(Client *c, const Seperator *sep); void command_reloadaa(Client *c, const Seperator *sep); void command_reloadallrules(Client *c, const Seperator *sep); void command_reloademote(Client* c, const Seperator *sep); From 8d7b7d6cc4bdb8db1916c309fca53aa9f804d49e Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 12 Nov 2021 05:56:32 -0500 Subject: [PATCH 372/624] [Commands] Remove #optest Command. (#1727) - Remove unused command. --- zone/command.h | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/command.h b/zone/command.h index 263935484..99f570976 100644 --- a/zone/command.h +++ b/zone/command.h @@ -213,7 +213,6 @@ void command_numauths(Client *c, const Seperator *sep); void command_object(Client* c, const Seperator *sep); void command_oocmute(Client *c, const Seperator *sep); void command_opcode(Client *c, const Seperator *sep); -void command_optest(Client *c, const Seperator *sep); #ifdef PACKET_PROFILER void command_packetprofile(Client *c, const Seperator *sep); From e12e8df3efb41daedf54be0bd644718fad4ac3ef Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 12 Nov 2021 05:56:50 -0500 Subject: [PATCH 373/624] [Commands] Remove #numauths Command. (#1728) - Remove unused command. --- zone/command.h | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/command.h b/zone/command.h index 99f570976..2a6e2009f 100644 --- a/zone/command.h +++ b/zone/command.h @@ -209,7 +209,6 @@ void command_npctypespawn(Client *c, const Seperator *sep); void command_nudge(Client* c, const Seperator* sep); void command_nukebuffs(Client *c, const Seperator *sep); void command_nukeitem(Client *c, const Seperator *sep); -void command_numauths(Client *c, const Seperator *sep); void command_object(Client* c, const Seperator *sep); void command_oocmute(Client *c, const Seperator *sep); void command_opcode(Client *c, const Seperator *sep); From 575237d76461632e101fdc290041ca5fdd52d59b Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 12 Nov 2021 05:57:09 -0500 Subject: [PATCH 374/624] [Commands] Remove #mysqltest Command. (#1729) - Removed unused/deprecated command. --- zone/command.cpp | 15 --------------- zone/command.h | 1 - 2 files changed, 16 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 1e420ad5d..0dadc2469 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -298,7 +298,6 @@ int command_init(void) command_add("movechar", "[charname] [zonename] - Move charname to zonename", 50, command_movechar) || command_add("movement", "Various movement commands", 200, command_movement) || command_add("myskills", "- Show details about your current skill levels", 0, command_myskills) || - command_add("mysqltest", "Akkadius MySQL Bench Test", 250, command_mysqltest) || command_add("mysql", "Mysql CLI, see 'help' for options.", 250, command_mysql) || command_add("mystats", "- Show details about you or your pet", 50, command_mystats) || command_add("name", "[newname] - Rename your player target", 150, command_name) || @@ -14720,20 +14719,6 @@ void command_logs(Client *c, const Seperator *sep){ } } -void command_mysqltest(Client *c, const Seperator *sep) -{ - clock_t t = std::clock(); /* Function timer start */ - if (sep->IsNumber(1)){ - uint32 i = 0; - t = std::clock(); - for (i = 0; i < atoi(sep->arg[1]); i++){ - std::string query = "SELECT * FROM `zone`"; - auto results = content_db.QueryDatabase(query); - } - } - LogDebug("MySQL Test Took [{}] seconds", ((float)(std::clock() - t)) / CLOCKS_PER_SEC); -} - void command_resetaa_timer(Client *c, const Seperator *sep) { Client *target = nullptr; if(!c->GetTarget() || !c->GetTarget()->IsClient()) { diff --git a/zone/command.h b/zone/command.h index 2a6e2009f..2b9a7622c 100644 --- a/zone/command.h +++ b/zone/command.h @@ -189,7 +189,6 @@ void command_movechar(Client *c, const Seperator *sep); void command_movement(Client *c, const Seperator *sep); void command_myskills(Client *c, const Seperator *sep); void command_mysql(Client *c, const Seperator *sep); -void command_mysqltest(Client *c, const Seperator *sep); void command_mystats(Client *c, const Seperator *sep); void command_name(Client *c, const Seperator *sep); void command_netstats(Client *c, const Seperator *sep); From cf8bf9e4fca8df9570a3e397da395daaec8ed3f6 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 12 Nov 2021 05:57:25 -0500 Subject: [PATCH 375/624] [Commands] Remove #manastat Command. (#1730) - Remove unused command. --- zone/command.h | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/command.h b/zone/command.h index 2b9a7622c..387dbfb7a 100644 --- a/zone/command.h +++ b/zone/command.h @@ -178,7 +178,6 @@ void command_logs(Client *c, const Seperator *sep); void command_logtest(Client *c, const Seperator *sep); void command_makepet(Client *c, const Seperator *sep); void command_mana(Client *c, const Seperator *sep); -void command_manastat(Client *c, const Seperator *sep); void command_max_all_skills(Client *c, const Seperator *sep); void command_memspell(Client *c, const Seperator *sep); void command_merchantcloseshop(Client *c, const Seperator *sep); From f9ec45c7ff35727969a2260c34941d1c414f8dee Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 12 Nov 2021 05:57:43 -0500 Subject: [PATCH 376/624] [Commands] Remove #ipc Command. (#1732) - Remove unused command. --- zone/command.h | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/command.h b/zone/command.h index 387dbfb7a..b2ba6e7b7 100644 --- a/zone/command.h +++ b/zone/command.h @@ -160,7 +160,6 @@ void command_interrupt(Client *c, const Seperator *sep); void command_invsnapshot(Client *c, const Seperator *sep); void command_invul(Client *c, const Seperator *sep); void command_ipban(Client *c, const Seperator *sep); -void command_ipc(Client *c, const Seperator *sep); void command_iplookup(Client *c, const Seperator *sep); void command_iteminfo(Client *c, const Seperator *sep); void command_itemsearch(Client *c, const Seperator *sep); From f4a70eff439011efa9f25636b389d85afdce1099 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 12 Nov 2021 05:58:01 -0500 Subject: [PATCH 377/624] [Commands] Remove #d1 Command. (#1733) - Remove unused command. --- zone/command.h | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/command.h b/zone/command.h index b2ba6e7b7..eed9ac642 100644 --- a/zone/command.h +++ b/zone/command.h @@ -76,7 +76,6 @@ void command_corpse(Client *c, const Seperator *sep); void command_corpsefix(Client *c, const Seperator *sep); void command_crashtest(Client *c, const Seperator *sep); void command_cvs(Client *c, const Seperator *sep); -void command_d1(Client *c, const Seperator *sep); void command_damage(Client *c, const Seperator *sep); void command_databuckets(Client *c, const Seperator *sep); void command_date(Client *c, const Seperator *sep); From 9e8d03d92d90d8ffe61d83419c8a894836489312 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 12 Nov 2021 05:58:18 -0500 Subject: [PATCH 378/624] [Commands] Remove #connectworldserver Command. (#1735) - Remove unused command. --- zone/command.h | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/command.h b/zone/command.h index eed9ac642..a5d65f5b4 100644 --- a/zone/command.h +++ b/zone/command.h @@ -70,7 +70,6 @@ void command_castspell(Client *c, const Seperator *sep); void command_chat(Client *c, const Seperator *sep); void command_checklos(Client *c, const Seperator *sep); void command_clearinvsnapshots(Client *c, const Seperator *sep); -void command_connectworldserver(Client *c, const Seperator *sep); void command_copycharacter(Client *c, const Seperator *sep); void command_corpse(Client *c, const Seperator *sep); void command_corpsefix(Client *c, const Seperator *sep); From 7178a7e55ded7ba1c87a7fa0c652eb5082eef7ab Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 12 Nov 2021 07:41:46 -0500 Subject: [PATCH 379/624] [Commands] Remove #clearinvsnapshots Command. (#1736) * [Commands] Remove #clearinvsnapshots Command. - Remove unused command. * Web editor conflict fail Co-authored-by: JJ <3617814+joligario@users.noreply.github.com> --- zone/command.h | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/command.h b/zone/command.h index a5d65f5b4..eae72b2c9 100644 --- a/zone/command.h +++ b/zone/command.h @@ -69,7 +69,6 @@ void command_camerashake(Client *c, const Seperator *sep); void command_castspell(Client *c, const Seperator *sep); void command_chat(Client *c, const Seperator *sep); void command_checklos(Client *c, const Seperator *sep); -void command_clearinvsnapshots(Client *c, const Seperator *sep); void command_copycharacter(Client *c, const Seperator *sep); void command_corpse(Client *c, const Seperator *sep); void command_corpsefix(Client *c, const Seperator *sep); From 8d9415191a35ed1c605dde747291b1fbe8f6bfcc Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 12 Nov 2021 07:42:22 -0500 Subject: [PATCH 380/624] [Commands] Cleanup #reloadquest Command. (#1712) - Cleanup message and logic. --- zone/command.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 0dadc2469..d28d18f40 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -5393,19 +5393,22 @@ void command_viewnpctype(Client *c, const Seperator *sep) void command_reloadqst(Client *c, const Seperator *sep) { - if (sep->arg[1][0] == 0) - { - c->Message(Chat::White, "Clearing quest memory cache."); - entity_list.ClearAreas(); - parse->ReloadQuests(); - } - else - { - c->Message(Chat::White, "Clearing quest memory cache and stopping timers."); - entity_list.ClearAreas(); - parse->ReloadQuests(true); + bool stop_timers = false; + + if (sep->IsNumber(1)) { + stop_timers = std::stoi(sep->arg[1]) != 0 ? true : false; } + std::string stop_timers_message = stop_timers ? " and stopping timers" : ""; + c->Message( + Chat::White, + fmt::format( + "Clearing quest memory cache{}.", + stop_timers_message + ).c_str() + ); + entity_list.ClearAreas(); + parse->ReloadQuests(stop_timers); } void command_corpsefix(Client *c, const Seperator *sep) From 110d2a0e10b42aaa4b80ac100af8911c38b66bda Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 12 Nov 2021 08:06:53 -0500 Subject: [PATCH 381/624] [Commands] Cleanup #heal Command. (#1717) * [Commands] Cleanup #heal Command. - Add message. * Remove target requirement. * Add self message. * Typo. --- zone/command.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index d28d18f40..b00033842 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -4366,10 +4366,20 @@ void command_nudge(Client* c, const Seperator* sep) void command_heal(Client *c, const Seperator *sep) { - if (c->GetTarget()==0) - c->Message(Chat::White, "Error: #Heal: No Target."); - else - c->GetTarget()->Heal(); + auto target = c->GetTarget() ? c->GetTarget() : c; + target->Heal(); + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "Healed {} ({}) to full.", + target->GetCleanName(), + target->GetID() + ).c_str() + ); + } else { + c->Message(Chat::White, "Healed yourself to full."); + } } void command_appearance(Client *c, const Seperator *sep) From f8c2e85f3eaa66e1cf291f58056ace482c51aee0 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 12 Nov 2021 08:10:43 -0500 Subject: [PATCH 382/624] [Commands] Cleanup #mana Command. (#1718) * [Commands] Cleanup #mana Command. - Add message. * Add self message. --- zone/command.cpp | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index b00033842..c679961b3 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -3087,14 +3087,25 @@ void command_size(Client *c, const Seperator *sep) void command_mana(Client *c, const Seperator *sep) { - Mob *t; + auto target = c->GetTarget() ? c->GetTarget() : c; + if(target->IsClient()) { + target->CastToClient()->SetMana(target->CastToClient()->CalcMaxMana()); + } else { + target->SetMana(target->CalcMaxMana()); + } - t = c->GetTarget() ? c->GetTarget() : c; - - if(t->IsClient()) - t->CastToClient()->SetMana(t->CastToClient()->CalcMaxMana()); - else - t->SetMana(t->CalcMaxMana()); + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "Set {} ({}) to full Mana.", + target->GetCleanName(), + target->GetID() + ).c_str() + ); + } else { + c->Message(Chat::White, "Restored your Mana to full."); + } } void command_flymode(Client *c, const Seperator *sep) From fb8539e679d0b34c016cf42872e9fb9acc7248b0 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 12 Nov 2021 08:11:45 -0500 Subject: [PATCH 383/624] [Commands] Cleanup #endurance Command. (#1719) - Add message. --- zone/command.cpp | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index c679961b3..fe8b52d0f 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -884,19 +884,30 @@ void command_worldwide(Client *c, const Seperator *sep) c->Message(Chat::White, "Usage: #worldwide moveinstance [Instance ID]"); } } + void command_endurance(Client *c, const Seperator *sep) { - Mob *t; + auto target = c->GetTarget() ? c->GetTarget() : c; + if (target->IsClient()) { + target->CastToClient()->SetEndurance(target->CastToClient()->GetMaxEndurance()); + } else { + target->SetEndurance(target->GetMaxEndurance()); + } - t = c->GetTarget() ? c->GetTarget() : c; - - if (t->IsClient()) - t->CastToClient()->SetEndurance(t->CastToClient()->GetMaxEndurance()); - else - t->SetEndurance(t->GetMaxEndurance()); - - t->Message(Chat::White, "Your endurance has been refilled."); + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "Set {} ({}) to full Endurance.", + target->GetCleanName(), + target->GetID() + ).c_str() + ); + } else { + c->Message(Chat::White, "Restored your Endurance to full."); + } } + void command_setstat(Client* c, const Seperator* sep){ if(sep->arg[1][0] && sep->arg[2][0] && c->GetTarget()!=0 && c->GetTarget()->IsClient()){ c->GetTarget()->CastToClient()->SetStats(atoi(sep->arg[1]),atoi(sep->arg[2])); From 0bf6627fb08210e2e09ccf2cf01db6cfb9873cc0 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 12 Nov 2021 08:19:58 -0500 Subject: [PATCH 384/624] [Commands] Remove #crashtest Command. (#1734) - Remove unused/deprecated command. --- zone/command.cpp | 8 -------- zone/command.h | 1 - 2 files changed, 9 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index fe8b52d0f..9f63d6f1b 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -188,7 +188,6 @@ int command_init(void) command_add("copycharacter", "[source_char_name] [dest_char_name] [dest_account_name] Copies character to destination account", 250, command_copycharacter) || command_add("corpse", "- Manipulate corpses, use with no arguments for help", 50, command_corpse) || command_add("corpsefix", "Attempts to bring corpses from underneath the ground within close proximity of the player", 0, command_corpsefix) || - command_add("crashtest", "- Crash the zoneserver", 255, command_crashtest) || command_add("cvs", "- Summary of client versions currently online.", 200, command_cvs) || command_add("damage", "[amount] - Damage your target", 100, command_damage) || command_add("databuckets", "View|Delete [key] [limit]- View data buckets, limit 50 default or Delete databucket by key", 80, command_databuckets) || @@ -14673,13 +14672,6 @@ void command_logtest(Client *c, const Seperator *sep){ } } -void command_crashtest(Client *c, const Seperator *sep) -{ - c->Message(Chat::White, "Alright, now we get an GPF ;) "); - char* gpf = 0; - memcpy(gpf, "Ready to crash", 30); -} - void command_logs(Client *c, const Seperator *sep){ int logs_set = 0; if (sep->argnum > 0) { diff --git a/zone/command.h b/zone/command.h index eae72b2c9..d225eadc7 100644 --- a/zone/command.h +++ b/zone/command.h @@ -72,7 +72,6 @@ void command_checklos(Client *c, const Seperator *sep); void command_copycharacter(Client *c, const Seperator *sep); void command_corpse(Client *c, const Seperator *sep); void command_corpsefix(Client *c, const Seperator *sep); -void command_crashtest(Client *c, const Seperator *sep); void command_cvs(Client *c, const Seperator *sep); void command_damage(Client *c, const Seperator *sep); void command_databuckets(Client *c, const Seperator *sep); From 0997a8a31ede593853c1d3f3c481a94f443a8278 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 12 Nov 2021 08:23:22 -0500 Subject: [PATCH 385/624] [Commands] Remove #bug Command. (#1737) - Remove unused command. --- zone/command.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/zone/command.h b/zone/command.h index d225eadc7..37bad3dd6 100644 --- a/zone/command.h +++ b/zone/command.h @@ -60,11 +60,6 @@ void command_ban(Client *c, const Seperator *sep); void command_beard(Client *c, const Seperator *sep); void command_beardcolor(Client *c, const Seperator *sep); void command_bind(Client* c, const Seperator *sep); - -#ifdef BUGTRACK -void command_bug(Client *c, const Seperator *sep); -#endif - void command_camerashake(Client *c, const Seperator *sep); void command_castspell(Client *c, const Seperator *sep); void command_chat(Client *c, const Seperator *sep); From f591378ed3c4d73199ae4dde8077d0e15899711d Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 12 Nov 2021 08:58:43 -0500 Subject: [PATCH 386/624] [Commands] Cleanup #viewnpctype Command. (#1713) * [Commands] Cleanup #viewnpctype Command. - Create a temporary NPC to use ShowStats() instead. - Cleanup message. * Cleanup spawn/emote/textures logic in ShowStats() when unused. * Formatting. --- zone/command.cpp | 44 ++++++++++++------------ zone/mob.cpp | 87 +++++++++++++++++++++++++++++------------------- 2 files changed, 75 insertions(+), 56 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 9f63d6f1b..940d9b5e6 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -438,7 +438,7 @@ int command_init(void) command_add("untraindiscs", "- Untrains all disciplines from your target.", 180, command_untraindiscs) || command_add("uptime", "[zone server id] - Get uptime of worldserver, or zone server if argument provided", 10, command_uptime) || command_add("version", "- Display current version of EQEmu server", 0, command_version) || - command_add("viewnpctype", "[npctype id] - Show info about an npctype", 100, command_viewnpctype) || + command_add("viewnpctype", "[NPC ID] - Show stats for an NPC by NPC ID", 100, command_viewnpctype) || command_add("viewpetition", "[petition number] - View a petition", 20, command_viewpetition) || command_add("viewzoneloot", "[item id] - Allows you to search a zone's loot for a specific item ID. (0 shows all loot in the zone)", 80, command_viewzoneloot) || command_add("wc", "[wear slot] [material] - Sends an OP_WearChange for your target", 200, command_wc) || @@ -5397,28 +5397,28 @@ void command_findzone(Client *c, const Seperator *sep) void command_viewnpctype(Client *c, const Seperator *sep) { - if (!sep->IsNumber(1)) - c->Message(Chat::White, "Usage: #viewnpctype [npctype id]"); - else - { - uint32 npctypeid=atoi(sep->arg[1]); - const NPCType* npct = content_db.LoadNPCTypesData(npctypeid); - if (npct) { - c->Message(Chat::White, " NPCType Info, "); - c->Message(Chat::White, " NPCTypeID: %u", npct->npc_id); - c->Message(Chat::White, " Name: %s", npct->name); - c->Message(Chat::White, " Level: %i", npct->level); - c->Message(Chat::White, " Race: %i", npct->race); - c->Message(Chat::White, " Class: %i", npct->class_); - c->Message(Chat::White, " MinDmg: %i", npct->min_dmg); - c->Message(Chat::White, " MaxDmg: %i", npct->max_dmg); - c->Message(Chat::White, " Special Abilities: %s", npct->special_abilities); - c->Message(Chat::White, " Spells: %i", npct->npc_spells_id); - c->Message(Chat::White, " Loot Table: %i", npct->loottable_id); - c->Message(Chat::White, " NPCFactionID: %i", npct->npc_faction_id); + if (sep->IsNumber(1)) { + uint32 npc_id = std::stoul(sep->arg[1]); + const NPCType* npc_type_data = content_db.LoadNPCTypesData(npc_id); + if (npc_type_data) { + auto npc = new NPC( + npc_type_data, + nullptr, + c->GetPosition(), + GravityBehavior::Water + ); + npc->ShowStats(c); + } else { + c->Message( + Chat::White, + fmt::format( + "NPC ID {} was not found.", + npc_id + ).c_str() + ); } - else - c->Message(Chat::White, "NPC #%d not found", npctypeid); + } else { + c->Message(Chat::White, "Usage: #viewnpctype [NPC ID]"); } } diff --git a/zone/mob.cpp b/zone/mob.cpp index 2276275ee..914d7fdf7 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1641,15 +1641,21 @@ void Mob::ShowStats(Client* client) ); // Spawn Data - client->Message( - Chat::White, - fmt::format( - "Spawn | Group: {} Point: {} Grid: {}", - target->GetSpawnGroupId(), - target->GetSpawnPointID(), - target->GetGrid() - ).c_str() - ); + if ( + target->GetGrid() || + target->GetSpawnGroupId() || + target->GetSpawnPointID() + ) { + client->Message( + Chat::White, + fmt::format( + "Spawn | Group: {} Point: {} Grid: {}", + target->GetSpawnGroupId(), + target->GetSpawnPointID(), + target->GetGrid() + ).c_str() + ); + } client->Message( Chat::White, @@ -1817,24 +1823,35 @@ void Mob::ShowStats(Client* client) ).c_str() ); - client->Message( - Chat::White, - fmt::format( - "Textures | Arms: {} Bracers: {} Hands: {}", - target->GetArmTexture(), - target->GetBracerTexture(), - target->GetHandTexture() - ).c_str() - ); + if ( + target->GetArmTexture() || + target->GetBracerTexture() || + target->GetHandTexture() + ) { + client->Message( + Chat::White, + fmt::format( + "Textures | Arms: {} Bracers: {} Hands: {}", + target->GetArmTexture(), + target->GetBracerTexture(), + target->GetHandTexture() + ).c_str() + ); + } - client->Message( - Chat::White, - fmt::format( - "Textures | Legs: {} Feet: {}", - target->GetLegTexture(), - target->GetFeetTexture() - ).c_str() - ); + if ( + target->GetFeetTexture() || + target->GetLegTexture() + ) { + client->Message( + Chat::White, + fmt::format( + "Textures | Legs: {} Feet: {}", + target->GetLegTexture(), + target->GetFeetTexture() + ).c_str() + ); + } // Hero's Forge if (target->GetHeroForgeModel()) { @@ -2147,14 +2164,16 @@ void Mob::ShowStats(Client* client) ).c_str() ); - // Emote - client->Message( - Chat::White, - fmt::format( - "Emote: {}", - target->GetEmoteID() - ).c_str() - ); + // Emote + if (target->GetEmoteID()) { + client->Message( + Chat::White, + fmt::format( + "Emote: {}", + target->GetEmoteID() + ).c_str() + ); + } // Run/Walk Speed client->Message( From 908c6c18af762717b4d6b9cdf69fc1245624ac8a Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 12 Nov 2021 09:19:43 -0500 Subject: [PATCH 387/624] [Commands] Cleanup #findnpctype Command. (#1714) * [Commands] Cleanup #findnpctype Command. - Cleanup message and logic. * Logic cleanup, found_count is always greater than 0. * Fix order. * Add return. --- zone/command.cpp | 88 +++++++++++++++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 30 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 940d9b5e6..3fe2374f7 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -5144,47 +5144,75 @@ void command_invsnapshot(Client *c, const Seperator *sep) void command_findnpctype(Client *c, const Seperator *sep) { - if(sep->arg[1][0] == 0) { - c->Message(Chat::White, "Usage: #findnpctype [search criteria]"); + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #findnpctype [Search Criteria]"); return; } std::string query; - - int id = atoi((const char *)sep->arg[1]); - if (id == 0) // If id evaluates to 0, then search as if user entered a string. - query = StringFormat("SELECT id, name FROM npc_types WHERE name LIKE '%%%s%%'", sep->arg[1]); - else // Otherwise, look for just that npc id. - query = StringFormat("SELECT id, name FROM npc_types WHERE id = %i", id); + std::string search_criteria = sep->arg[1]; + if (sep->IsNumber(1)) { + query = fmt::format( + "SELECT id, name FROM npc_types WHERE id = {}", + search_criteria + ); + } else { + query = fmt::format( + "SELECT id, name FROM npc_types WHERE name LIKE '%%{}%%'", + search_criteria + ); + } auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message (0, "Error querying database."); - c->Message (0, query.c_str()); - } + if (!results.Success() || !results.RowCount()) { + c->Message( + Chat::White, + fmt::format( + "No matches found for '{}'.", + search_criteria + ).c_str() + ); + return; + } - if (results.RowCount() == 0) // No matches found. - c->Message (0, "No matches found for %s.", sep->arg[1]); + int found_count = 0; - // If query runs successfully. - int count = 0; - const int maxrows = 20; + for (auto row : results) { + int found_number = (found_count + 1); + if (found_count == 20) { + break; + } - // Process each row returned. - for (auto row = results.begin(); row != results.end(); ++row) { - // Limit to returning maxrows rows. - if (++count > maxrows) { - c->Message (0, "%i npc types shown. Too many results.", maxrows); - break; - } + c->Message( + Chat::White, + fmt::format( + "NPC {} | {} ({})", + found_number, + row[1], + row[0] + ).c_str() + ); + found_count++; + } - c->Message (0, " %s: %s", row[0], row[1]); - } - - // If we did not hit the maxrows limit. - if (count <= maxrows) - c->Message (0, "Query complete. %i rows shown.", count); + if (found_count == 20) { + c->Message(Chat::White, "20 NPCs were found, max reached."); + } else { + auto npc_message = ( + found_count == 1 ? + "An NPC was" : + fmt::format("{} NPCs were", found_count) + ); + c->Message( + Chat::White, + fmt::format( + "{} found.", + npc_message + ).c_str() + ); + } } void command_faction(Client *c, const Seperator *sep) From e870ee5e0e546d02cc17da8e707cae5315d8049f Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 12 Nov 2021 15:46:05 -0500 Subject: [PATCH 388/624] [Commands] Remove #logtest Command. (#1731) - Remove unused/deprecated command. --- zone/command.cpp | 13 ------------- zone/command.h | 1 - 2 files changed, 14 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 3fe2374f7..322bc0611 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -285,7 +285,6 @@ int command_init(void) command_add("loc", "- Print out your or your target's current location and heading", 0, command_loc) || command_add("lock", "- Lock the worldserver", 150, command_lock) || command_add("logs", "Manage anything to do with logs", 250, command_logs) || - command_add("logtest", "Performs log performance testing.", 250, command_logtest) || command_add("makepet", "[level] [class] [race] [texture] - Make a pet", 50, command_makepet) || command_add("mana", "- Fill your or your target's mana", 50, command_mana) || command_add("maxskills", "Maxes skills for you.", 200, command_max_all_skills) || @@ -14688,18 +14687,6 @@ void command_tune(Client *c, const Seperator *sep) return; } -void command_logtest(Client *c, const Seperator *sep){ - clock_t t = std::clock(); /* Function timer start */ - if (sep->IsNumber(1)){ - uint32 i = 0; - t = std::clock(); - for (i = 0; i < atoi(sep->arg[1]); i++){ - LogDebug("[[{}]] Test #2 Took [{}] seconds", i, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); - } - - } -} - void command_logs(Client *c, const Seperator *sep){ int logs_set = 0; if (sep->argnum > 0) { diff --git a/zone/command.h b/zone/command.h index 37bad3dd6..b0d5ccc40 100644 --- a/zone/command.h +++ b/zone/command.h @@ -165,7 +165,6 @@ void command_load_shared_memory(Client *c, const Seperator *sep); void command_loc(Client *c, const Seperator *sep); void command_lock(Client *c, const Seperator *sep); void command_logs(Client *c, const Seperator *sep); -void command_logtest(Client *c, const Seperator *sep); void command_makepet(Client *c, const Seperator *sep); void command_mana(Client *c, const Seperator *sep); void command_max_all_skills(Client *c, const Seperator *sep); From 1a6921804500a202ff4996ef06f1ca3d89a7cdac Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 12 Nov 2021 18:28:14 -0500 Subject: [PATCH 389/624] [Commands] Cleanup #faction Command. (#1716) * [Commands] Cleanup #faction Command. - Remove find subcommand as we have #findfaction now. - Cleanup message. * Remove #setfaction message. --- zone/command.cpp | 222 +++++++++++++++++++++++++++-------------------- 1 file changed, 130 insertions(+), 92 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 322bc0611..1dea47cb5 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -5216,20 +5216,11 @@ void command_findnpctype(Client *c, const Seperator *sep) void command_faction(Client *c, const Seperator *sep) { - if (sep->arg[1][0] == 0) { - c->Message(Chat::White, "Usage: #faction -- Displays Target NPC's Primary faction"); - c->Message(Chat::White, "Usage: #faction Find [criteria | all] -- Displays factions name & id"); - c->Message(Chat::White, "Usage: #faction Review [criteria | all] -- Review Targeted Players faction hits"); - c->Message(Chat::White, "Usage: #faction Reset [id] -- Reset Targeted Players specified faction to base"); - uint32 npcfac; - std::string npcname; - if (c->GetTarget() && c->GetTarget()->IsNPC()) { - npcfac = c->GetTarget()->CastToNPC()->GetPrimaryFaction(); - npcname = c->GetTarget()->CastToNPC()->GetCleanName(); - std::string blurb = fmt::format("( Target Npc: {} : has primary faction id: {} )", npcname, npcfac); - c->Message(Chat::Yellow, blurb.c_str()); - c->Message(Chat::White, "Use: #setfaction [id] - to alter an NPC's faction"); - } + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #faction review [Search Criteria | All] - Review Targeted Player's Faction Hits"); + c->Message(Chat::White, "Usage: #faction reset [Faction ID] - Reset Targeted Player's Faction to Base Faction Value"); + c->Message(Chat::White, "Usage: #faction view - Displays Target NPC's Primary Faction"); return; } @@ -5237,95 +5228,142 @@ void command_faction(Client *c, const Seperator *sep) if (sep->arg[2]) { faction_filter = str_tolower(sep->arg[2]); } - if (strcasecmp(sep->arg[1], "find") == 0) { - std::string query; - if (strcasecmp(sep->arg[2], "all") == 0) { - query = "SELECT `id`,`name` FROM `faction_list`"; - } - else { - query = fmt::format("SELECT `id`,`name` FROM `faction_list` WHERE `name` LIKE '%{}%'", faction_filter.c_str()); - } - auto results = content_db.QueryDatabase(query); - if (!results.Success()) - return; - if (results.RowCount() == 0) { - c->Message(Chat::Yellow, "No factions found with specified criteria"); - return; - } - int _ctr = 0; - for (auto row = results.begin(); row != results.end(); ++row) { - auto id = static_cast(atoi(row[0])); - std::string name = row[1]; - _ctr++; - c->Message(Chat::Yellow, "%s : id: %s", name.c_str(), std::to_string(id).c_str()); - } - std::string response = _ctr > 0 ? fmt::format("Found {} matching factions", _ctr).c_str() : "No factions found."; - c->Message(Chat::Yellow, response.c_str()); - } - if (strcasecmp(sep->arg[1], "review") == 0) { + if (!strcasecmp(sep->arg[1], "review")) { if (!(c->GetTarget() && c->GetTarget()->IsClient())) { c->Message(Chat::Red, "Player Target Required for faction review"); return; } - uint32 charid = c->GetTarget()->CastToClient()->CharacterID(); - std::string revquery; - if (strcasecmp(sep->arg[2], "all") == 0) { - revquery = fmt::format( - "SELECT id,`name`, current_value FROM faction_list INNER JOIN faction_values ON faction_list.id = faction_values.faction_id WHERE char_id = {}", charid); - } - else - { - revquery = fmt::format( - "SELECT id,`name`, current_value FROM faction_list INNER JOIN faction_values ON faction_list.id = faction_values.faction_id WHERE `name` like '%{}%' and char_id = {}", faction_filter.c_str(), charid); - } - auto revresults = content_db.QueryDatabase(revquery); - if (!revresults.Success()) - return; - if (revresults.RowCount() == 0) { - c->Message(Chat::Yellow, "No faction hits found. All are at base level"); - return; - } - int _ctr2 = 0; - for (auto rrow = revresults.begin(); rrow != revresults.end(); ++rrow) { - auto f_id = static_cast(atoi(rrow[0])); - std::string cname = rrow[1]; - std::string fvalue = rrow[2]; - _ctr2++; - std::string resetlink = fmt::format("#faction reset {}", f_id); - c->Message(Chat::Yellow, "Reset: %s id: %s (%s)", EQ::SayLinkEngine::GenerateQuestSaylink(resetlink, false, cname.c_str()).c_str(), std::to_string(f_id).c_str(), fvalue.c_str()); - } - std::string response = _ctr2 > 0 ? fmt::format("Found {} matching factions", _ctr2).c_str() : "No faction hits found."; - c->Message(Chat::Yellow, response.c_str()); - } - else if (strcasecmp(sep->arg[1], "reset") == 0) - { - if (!(faction_filter == "")) { - if (c->GetTarget() && c->GetTarget()->IsClient()) - { - if (!c->CastToClient()->GetFeigned() && c->CastToClient()->GetAggroCount() == 0) - { - uint32 charid = c->GetTarget()->CastToClient()->CharacterID(); - uint32 factionid = atoi(faction_filter.c_str()); - if (c->GetTarget()->CastToClient()->ReloadCharacterFaction(c->GetTarget()->CastToClient(), factionid, charid)) - c->Message(Chat::Yellow, "faction %u was cleared.", factionid); - else - c->Message(Chat::Red, "An error occurred clearing faction %u", factionid); - } - else - { - c->Message(Chat::Red, "Cannot be in Combat"); + Client* target = c->GetTarget()->CastToClient(); + uint32 character_id = target->CharacterID(); + std::string query; + if (!strcasecmp(faction_filter.c_str(), "all")) { + query = fmt::format( + "SELECT id, `name`, current_value FROM faction_list INNER JOIN faction_values ON faction_list.id = faction_values.faction_id WHERE char_id = {}", + character_id + ); + } else { + query = fmt::format( + "SELECT id, `name`, current_value FROM faction_list INNER JOIN faction_values ON faction_list.id = faction_values.faction_id WHERE `name` like '%{}%' and char_id = {}", + faction_filter.c_str(), + character_id + ); + } + + auto results = content_db.QueryDatabase(query); + if (!results.Success() || !results.RowCount()) { + c->Message(Chat::Yellow, "No faction hits found. All are at base level."); + return; + } + + uint32 found_count = 0; + for (auto row : results) { + uint32 faction_number = (found_count + 1); + auto faction_id = std::stoul(row[0]); + std::string faction_name = row[1]; + std::string faction_value = row[2]; + std::string reset_link = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#faction reset {}", faction_id), + false, + "Reset" + ); + + c->Message( + Chat::White, + fmt::format( + "Faction {} | Name: {} ({}) Value: {} [{}]", + faction_number, + faction_name, + faction_id, + faction_value, + reset_link + ).c_str() + ); + found_count++; + } + + auto faction_message = ( + found_count > 0 ? + ( + found_count == 1 ? + "A Faction was" : + fmt::format("{} Factions were", found_count) + ) : + "No Factions were" + ); + c->Message( + Chat::White, + fmt::format( + "{} found.", + faction_message + ).c_str() + ); + } else if (!strcasecmp(sep->arg[1], "reset")) { + if (strlen(faction_filter.c_str()) > 0) { + if (c->GetTarget() && c->GetTarget()->IsClient()) { + Client* target = c->GetTarget()->CastToClient(); + if ( + ( + !c->GetFeigned() && + c->GetAggroCount() == 0 + ) || + ( + !target->GetFeigned() && + target->GetAggroCount() == 0 + ) + ) { + uint32 character_id = target->CharacterID(); + uint32 faction_id = std::stoul(faction_filter.c_str()); + if (target->ReloadCharacterFaction(target, faction_id, character_id)) { + c->Message( + Chat::White, + fmt::format( + "Faction Reset | {} ({}) was reset for {}.", + content_db.GetFactionName(faction_id), + faction_id, + target->GetCleanName() + ).c_str() + ); + } else { + c->Message( + Chat::White, + fmt::format( + "Faction Reset Failed | {} ({}) was unable to be reset for {}.", + content_db.GetFactionName(faction_id), + faction_id, + target->GetCleanName() + ).c_str() + ); + } + } else { + c->Message(Chat::White, "You cannot reset factions while you or your target is in combat or feigned."); return; } - } - else { - c->Message(Chat::Red, "Player Target Required (whose not feigning death)"); + } else { + c->Message(Chat::White, "You must target a PC for this command."); return; } + } else { + c->Message(Chat::White, "Usage: #faction reset [Faction ID] - Reset Targeted Player's Faction to Base Faction Value"); + } + } else if (!strcasecmp(sep->arg[1], "view")) { + if (c->GetTarget() && c->GetTarget()->IsNPC()) { + Mob* target = c->GetTarget(); + uint32 npc_id = target->GetNPCTypeID(); + uint32 npc_faction_id = target->CastToNPC()->GetPrimaryFaction(); + std::string npc_name = target->GetCleanName(); + c->Message( + Chat::White, + fmt::format( + "{} ({}) has a Primary Faction of {} ({}).", + npc_name, + npc_id, + content_db.GetFactionName(npc_faction_id), + npc_faction_id + ).c_str() + ); } - else - c->Message(Chat::Red, "No faction id entered"); } } From 3c8748055361c9270a5d49d0acbc945cdfcc8e26 Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Fri, 12 Nov 2021 21:16:39 -0500 Subject: [PATCH 390/624] [Quest API] Add back removed lua class properties (#1742) Fixes regression from 7b6decae --- zone/lua_iteminst.cpp | 4 +++- zone/lua_packet.cpp | 2 ++ zone/lua_raid.cpp | 2 ++ zone/lua_spawn.cpp | 2 ++ zone/lua_spell.cpp | 2 ++ 5 files changed, 11 insertions(+), 1 deletion(-) diff --git a/zone/lua_iteminst.cpp b/zone/lua_iteminst.cpp index ee164ded1..1c5155e5d 100644 --- a/zone/lua_iteminst.cpp +++ b/zone/lua_iteminst.cpp @@ -265,10 +265,12 @@ void Lua_ItemInst::ClearTimers() { } luabind::scope lua_register_iteminst() { - return luabind::class_("ItemInst") + return luabind::class_("ItemInst") .def(luabind::constructor<>()) .def(luabind::constructor()) .def(luabind::constructor()) + .property("null", &Lua_ItemInst::Null) + .property("valid", &Lua_ItemInst::Valid) .def("AddExp", (void(Lua_ItemInst::*)(uint32))&Lua_ItemInst::AddExp) .def("ClearTimers", (void(Lua_ItemInst::*)(void))&Lua_ItemInst::ClearTimers) .def("Clone", (Lua_ItemInst(Lua_ItemInst::*)(void))&Lua_ItemInst::Clone) diff --git a/zone/lua_packet.cpp b/zone/lua_packet.cpp index bf246465b..c7a039ca4 100644 --- a/zone/lua_packet.cpp +++ b/zone/lua_packet.cpp @@ -326,6 +326,8 @@ luabind::scope lua_register_packet() { .def(luabind::constructor<>()) .def(luabind::constructor()) .def(luabind::constructor()) + .property("null", &Lua_Packet::Null) + .property("valid", &Lua_Packet::Valid) .def("GetOpcode", &Lua_Packet::GetOpcode) .def("GetRawOpcode", &Lua_Packet::GetRawOpcode) .def("GetSize", &Lua_Packet::GetSize) diff --git a/zone/lua_raid.cpp b/zone/lua_raid.cpp index aa209fa7b..4bc99a7cd 100644 --- a/zone/lua_raid.cpp +++ b/zone/lua_raid.cpp @@ -146,6 +146,8 @@ bool Lua_Raid::DoesAnyMemberHaveExpeditionLockout(std::string expedition_name, s luabind::scope lua_register_raid() { return luabind::class_("Raid") .def(luabind::constructor<>()) + .property("null", &Lua_Raid::Null) + .property("valid", &Lua_Raid::Valid) .def("BalanceHP", (void(Lua_Raid::*)(int,uint32))&Lua_Raid::BalanceHP) .def("CastGroupSpell", (void(Lua_Raid::*)(Lua_Mob,int,uint32))&Lua_Raid::CastGroupSpell) .def("DoesAnyMemberHaveExpeditionLockout", (bool(Lua_Raid::*)(std::string, std::string))&Lua_Raid::DoesAnyMemberHaveExpeditionLockout) diff --git a/zone/lua_spawn.cpp b/zone/lua_spawn.cpp index 56fb94757..117b4a6b4 100644 --- a/zone/lua_spawn.cpp +++ b/zone/lua_spawn.cpp @@ -141,6 +141,8 @@ uint32 Lua_Spawn::GetKillCount() { luabind::scope lua_register_spawn() { return luabind::class_("Spawn") .def(luabind::constructor<>()) + .property("null", &Lua_Spawn::Null) + .property("valid", &Lua_Spawn::Valid) .def("CurrentNPCID", (uint32(Lua_Spawn::*)(void))&Lua_Spawn::CurrentNPCID) .def("Depop", (void(Lua_Spawn::*)(void))&Lua_Spawn::Depop) .def("Disable", (void(Lua_Spawn::*)(void))&Lua_Spawn::Disable) diff --git a/zone/lua_spell.cpp b/zone/lua_spell.cpp index 28c7e7cb5..c2a5e622d 100644 --- a/zone/lua_spell.cpp +++ b/zone/lua_spell.cpp @@ -493,6 +493,8 @@ luabind::scope lua_register_spell() { return luabind::class_("Spell") .def(luabind::constructor<>()) .def(luabind::constructor()) + .property("null", &Lua_Spell::Null) + .property("valid", &Lua_Spell::Valid) .def("AEDuration", &Lua_Spell::GetAEDuration) .def("AEMaxTargets", &Lua_Spell::GetAEMaxTargets) .def("Activated", &Lua_Spell::GetActivated) From 8b83a1356014756917ac9ee74b7fdc124766e27b Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 12 Nov 2021 23:04:09 -0500 Subject: [PATCH 391/624] [Rules] Add Archery/Throwing Ammo Consumption Rules. (#1743) - Add RULE_BOOL(Combat, ArcheryConsumesAmmo, true, "Set to false to disable Archery Ammo Consumption") - Add RULE_BOOL(Combat, ThrowingConsumesAmmo, true, "Set to false to disable Throwing Ammo Consumption") --- common/ruletypes.h | 2 ++ zone/special_attacks.cpp | 27 ++++++++++++++++++++++----- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 3ee4b49c8..c2b91c6b5 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -489,6 +489,8 @@ RULE_BOOL(Combat, UseExtendedPoisonProcs, false, "Allow old school poisons to la RULE_BOOL(Combat, EnableSneakPull, false, "Enable implementation of Sneak Pull") RULE_INT(Combat, SneakPullAssistRange, 400, "Modified range of assist for sneak pull") RULE_BOOL(Combat, Classic2HBAnimation, false, "2HB will use the 2 hand piercing animation instead of the overhead slashing animation") +RULE_BOOL(Combat, ArcheryConsumesAmmo, true, "Set to false to disable Archery Ammo Consumption") +RULE_BOOL(Combat, ThrowingConsumesAmmo, true, "Set to false to disable Throwing Ammo Consumption") RULE_CATEGORY_END() RULE_CATEGORY(NPC) diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index db3efff05..c7025fc26 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -756,11 +756,22 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) { //EndlessQuiver AA base1 = 100% Chance to avoid consumption arrow. int ChanceAvoidConsume = aabonuses.ConsumeProjectile + itembonuses.ConsumeProjectile + spellbonuses.ConsumeProjectile; - if (RangeItem->ExpendableArrow || !ChanceAvoidConsume || (ChanceAvoidConsume < 100 && zone->random.Int(0,99) > ChanceAvoidConsume)){ + // Consume Ammo, unless Ammo Consumption is disabled or player has Endless Quiver + bool consumes_ammo = RuleB(Combat, ArcheryConsumesAmmo); + if ( + consumes_ammo && + ( + RangeItem->ExpendableArrow || + !ChanceAvoidConsume || + (ChanceAvoidConsume < 100 && zone->random.Int(0,99) > ChanceAvoidConsume) + ) + ) { DeleteItemInInventory(ammo_slot, 1, true); - LogCombat("Consumed one arrow from slot [{}]", ammo_slot); + LogCombat("Consumed Archery Ammo from slot {}.", ammo_slot); + } else if (!consumes_ammo) { + LogCombat("Archery Ammo Consumption is disabled."); } else { - LogCombat("Endless Quiver prevented ammo consumption"); + LogCombat("Endless Quiver prevented Ammo Consumption."); } CheckIncreaseSkill(EQ::skills::SkillArchery, GetTarget(), -15); @@ -1338,8 +1349,14 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51 DoThrowingAttackDmg(other, RangeWeapon, item); - //consume ammo - DeleteItemInInventory(ammo_slot, 1, true); + // Consume Ammo, unless Ammo Consumption is disabled + if (RuleB(Combat, ThrowingConsumesAmmo)) { + DeleteItemInInventory(ammo_slot, 1, true); + LogCombat("Consumed Throwing Ammo from slot {}.", ammo_slot); + } else { + LogCombat("Throwing Ammo Consumption is disabled."); + } + CommonBreakInvisibleFromCombat(); } From a9d1034298a2d5180411fc4e6a7ad39a071aa76c Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Fri, 12 Nov 2021 23:02:05 -0600 Subject: [PATCH 392/624] [Loginserver] Worldserver Name Sanitization (#1739) * Sanitize bad words in server names * Add config options and enforcement for dev/test servers and servers starting with a special character * Refine bad word logic * Add installer to dev/test servers * Change server prefixes * Special char prefix * Formatting * Remove multi words * Add server types enum * Add error constants * Remove sanitize from world level * Use strn0cpy --- common/string_util.cpp | 679 ++++++++++++++---- common/string_util.h | 4 + loginserver/CMakeLists.txt | 2 +- loginserver/client.h | 37 +- .../{login_structures.h => login_types.h} | 91 ++- loginserver/login_util/login.json | 2 + loginserver/main.cpp | 33 +- loginserver/options.h | 16 + loginserver/server_manager.cpp | 2 +- loginserver/world_server.cpp | 100 ++- loginserver/world_server.h | 1 + world/login_server.cpp | 2 - 12 files changed, 726 insertions(+), 243 deletions(-) rename loginserver/{login_structures.h => login_types.h} (57%) diff --git a/common/string_util.cpp b/common/string_util.cpp index 77961dfc5..f0a42b91d 100644 --- a/common/string_util.cpp +++ b/common/string_util.cpp @@ -19,32 +19,33 @@ #include #ifdef _WINDOWS - #include +#include - #define snprintf _snprintf - #define strncasecmp _strnicmp - #define strcasecmp _stricmp +#define snprintf _snprintf +#define strncasecmp _strnicmp +#define strcasecmp _stricmp #else - #include - #include + +#include +#include #include #endif #ifndef va_copy - #define va_copy(d,s) ((d) = (s)) +#define va_copy(d,s) ((d) = (s)) #endif // original source: // https://github.com/facebook/folly/blob/master/folly/String.cpp // -const std::string vStringFormat(const char* format, va_list args) +const std::string vStringFormat(const char *format, va_list args) { std::string output; - va_list tmpargs; + va_list tmpargs; - va_copy(tmpargs,args); + va_copy(tmpargs, args); int characters_used = vsnprintf(nullptr, 0, format, tmpargs); va_end(tmpargs); @@ -52,7 +53,7 @@ const std::string vStringFormat(const char* format, va_list args) if (characters_used > 0) { output.resize(characters_used + 1); - va_copy(tmpargs,args); + va_copy(tmpargs, args); characters_used = vsnprintf(&output[0], output.capacity(), format, tmpargs); va_end(tmpargs); @@ -60,8 +61,9 @@ const std::string vStringFormat(const char* format, va_list args) // We shouldn't have a format error by this point, but I can't imagine what error we // could have by this point. Still, return empty string; - if (characters_used < 0) + if (characters_used < 0) { output.clear(); + } } return output; } @@ -87,8 +89,9 @@ const std::string str_toupper(std::string s) const std::string ucfirst(std::string s) { std::string output = s; - if (!s.empty()) + if (!s.empty()) { output[0] = static_cast(::toupper(s[0])); + } return output; } @@ -102,18 +105,20 @@ const std::string StringFormat(const char *format, ...) return output; } -std::vector SplitString(const std::string &str, const char delim) { +std::vector SplitString(const std::string &str, const char delim) +{ std::vector ret; - std::string::size_type start = 0; - auto end = str.find(delim); + std::string::size_type start = 0; + auto end = str.find(delim); while (end != std::string::npos) { ret.emplace_back(str, start, end - start); start = end + 1; - end = str.find(delim, start); + end = str.find(delim, start); } // this will catch the last word since the string is unlikely to end with a delimiter - if (str.length() > start) + if (str.length() > start) { ret.emplace_back(str, start, str.length() - start); + } return ret; } @@ -153,14 +158,16 @@ std::string get_between(const std::string &s, std::string start_delim, std::stri return ""; } -std::string::size_type search_deliminated_string(const std::string &haystack, const std::string &needle, const char deliminator) +std::string::size_type +search_deliminated_string(const std::string &haystack, const std::string &needle, const char deliminator) { // this shouldn't go out of bounds, even without obvious bounds checks auto pos = haystack.find(needle); while (pos != std::string::npos) { auto c = haystack[pos + needle.length()]; - if ((c == '\0' || c == deliminator) && (pos == 0 || haystack[pos - 1] == deliminator)) + if ((c == '\0' || c == deliminator) && (pos == 0 || haystack[pos - 1] == deliminator)) { return pos; + } pos = haystack.find(needle, pos + needle.length()); } return std::string::npos; @@ -180,7 +187,7 @@ std::string implode(std::string glue, std::vector src) } std::string final_output = output.str(); - final_output.resize (output.str().size () - glue.size()); + final_output.resize(output.str().size() - glue.size()); return final_output; } @@ -202,80 +209,83 @@ std::vector wrap(std::vector &src, std::string charact return new_vector; } -std::string EscapeString(const std::string &s) { +std::string EscapeString(const std::string &s) +{ std::string ret; - size_t sz = s.length(); - for(size_t i = 0; i < sz; ++i) { + size_t sz = s.length(); + for (size_t i = 0; i < sz; ++i) { char c = s[i]; - switch(c) { - case '\x00': - ret += "\\x00"; - break; - case '\n': - ret += "\\n"; - break; - case '\r': - ret += "\\r"; - break; - case '\\': - ret += "\\\\"; - break; - case '\'': - ret += "\\'"; - break; - case '\"': - ret += "\\\""; - break; - case '\x1a': - ret += "\\x1a"; - break; - default: - ret.push_back(c); - break; + switch (c) { + case '\x00': + ret += "\\x00"; + break; + case '\n': + ret += "\\n"; + break; + case '\r': + ret += "\\r"; + break; + case '\\': + ret += "\\\\"; + break; + case '\'': + ret += "\\'"; + break; + case '\"': + ret += "\\\""; + break; + case '\x1a': + ret += "\\x1a"; + break; + default: + ret.push_back(c); + break; } } return ret; } -std::string EscapeString(const char *src, size_t sz) { +std::string EscapeString(const char *src, size_t sz) +{ std::string ret; - for(size_t i = 0; i < sz; ++i) { + for (size_t i = 0; i < sz; ++i) { char c = src[i]; - switch(c) { - case '\x00': - ret += "\\x00"; - break; - case '\n': - ret += "\\n"; - break; - case '\r': - ret += "\\r"; - break; - case '\\': - ret += "\\\\"; - break; - case '\'': - ret += "\\'"; - break; - case '\"': - ret += "\\\""; - break; - case '\x1a': - ret += "\\x1a"; - break; - default: - ret.push_back(c); - break; + switch (c) { + case '\x00': + ret += "\\x00"; + break; + case '\n': + ret += "\\n"; + break; + case '\r': + ret += "\\r"; + break; + case '\\': + ret += "\\\\"; + break; + case '\'': + ret += "\\'"; + break; + case '\"': + ret += "\\\""; + break; + case '\x1a': + ret += "\\x1a"; + break; + default: + ret.push_back(c); + break; } } return ret; } -bool StringIsNumber(const std::string &s) { +bool StringIsNumber(const std::string &s) +{ try { auto r = stod(s); return true; @@ -285,15 +295,18 @@ bool StringIsNumber(const std::string &s) { } } -void ToLowerString(std::string &s) { +void ToLowerString(std::string &s) +{ std::transform(s.begin(), s.end(), s.begin(), ::tolower); } -void ToUpperString(std::string &s) { +void ToUpperString(std::string &s) +{ std::transform(s.begin(), s.end(), s.begin(), ::toupper); } -std::string JoinString(const std::vector& ar, const std::string &delim) { +std::string JoinString(const std::vector &ar, const std::string &delim) +{ std::string ret; for (size_t i = 0; i < ar.size(); ++i) { if (i != 0) { @@ -313,7 +326,7 @@ void find_replace(std::string &string_subject, const std::string &search_string, } size_t start_pos = 0; - while((start_pos = string_subject.find(search_string, start_pos)) != std::string::npos) { + while ((start_pos = string_subject.find(search_string, start_pos)) != std::string::npos) { string_subject.replace(start_pos, search_string.length(), replace_string); start_pos += replace_string.length(); } @@ -334,9 +347,9 @@ void ParseAccountString(const std::string &s, std::string &account, std::string auto split = SplitString(s, ':'); if (split.size() == 2) { loginserver = split[0]; - account = split[1]; + account = split[1]; } - else if(split.size() == 1) { + else if (split.size() == 1) { account = split[0]; } } @@ -345,9 +358,11 @@ void ParseAccountString(const std::string &s, std::string &account, std::string // normal strncpy doesnt put a null term on copied strings, this one does // ref: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcecrt/htm/_wcecrt_strncpy_wcsncpy.asp -char* strn0cpy(char* dest, const char* source, uint32 size) { - if (!dest) +char *strn0cpy(char *dest, const char *source, uint32 size) +{ + if (!dest) { return 0; + } if (size == 0 || source == 0) { dest[0] = 0; return dest; @@ -359,123 +374,159 @@ char* strn0cpy(char* dest, const char* source, uint32 size) { // String N w/null Copy Truncated? // return value =true if entire string(source) fit, false if it was truncated -bool strn0cpyt(char* dest, const char* source, uint32 size) { - if (!dest) +bool strn0cpyt(char *dest, const char *source, uint32 size) +{ + if (!dest) { return 0; + } if (size == 0 || source == 0) { dest[0] = 0; return false; } strncpy(dest, source, size); dest[size - 1] = 0; - return (bool)(source[strlen(dest)] == 0); + return (bool) (source[strlen(dest)] == 0); } -const char *MakeLowerString(const char *source) { +const char *MakeLowerString(const char *source) +{ static char str[128]; - if (!source) + if (!source) { return nullptr; + } MakeLowerString(source, str); return str; } -void MakeLowerString(const char *source, char *target) { +void MakeLowerString(const char *source, char *target) +{ if (!source || !target) { *target = 0; return; } - while (*source) - { + while (*source) { *target = tolower(*source); - target++; source++; + target++; + source++; } *target = 0; } -uint32 hextoi(const char* num) { - if (num == nullptr) +uint32 hextoi(const char *num) +{ + if (num == nullptr) { return 0; + } int len = strlen(num); - if (len < 3) + if (len < 3) { return 0; + } - if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) + if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) { return 0; + } - uint32 ret = 0; - int mul = 1; - for (int i = len - 1; i >= 2; i--) { - if (num[i] >= 'A' && num[i] <= 'F') + uint32 ret = 0; + int mul = 1; + for (int i = len - 1; i >= 2; i--) { + if (num[i] >= 'A' && num[i] <= 'F') { ret += ((num[i] - 'A') + 10) * mul; - else if (num[i] >= 'a' && num[i] <= 'f') + } + else if (num[i] >= 'a' && num[i] <= 'f') { ret += ((num[i] - 'a') + 10) * mul; - else if (num[i] >= '0' && num[i] <= '9') + } + else if (num[i] >= '0' && num[i] <= '9') { ret += (num[i] - '0') * mul; - else + } + else { return 0; + } mul *= 16; } return ret; } -uint64 hextoi64(const char* num) { - if (num == nullptr) +uint64 hextoi64(const char *num) +{ + if (num == nullptr) { return 0; + } int len = strlen(num); - if (len < 3) + if (len < 3) { return 0; + } - if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) + if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) { return 0; + } - uint64 ret = 0; - int mul = 1; - for (int i = len - 1; i >= 2; i--) { - if (num[i] >= 'A' && num[i] <= 'F') + uint64 ret = 0; + int mul = 1; + for (int i = len - 1; i >= 2; i--) { + if (num[i] >= 'A' && num[i] <= 'F') { ret += ((num[i] - 'A') + 10) * mul; - else if (num[i] >= 'a' && num[i] <= 'f') + } + else if (num[i] >= 'a' && num[i] <= 'f') { ret += ((num[i] - 'a') + 10) * mul; - else if (num[i] >= '0' && num[i] <= '9') + } + else if (num[i] >= '0' && num[i] <= '9') { ret += (num[i] - '0') * mul; - else + } + else { return 0; + } mul *= 16; } return ret; } -bool atobool(const char* iBool) { +bool atobool(const char *iBool) +{ - if (iBool == nullptr) + if (iBool == nullptr) { return false; - if (!strcasecmp(iBool, "true")) + } + if (!strcasecmp(iBool, "true")) { return true; - if (!strcasecmp(iBool, "false")) + } + if (!strcasecmp(iBool, "false")) { return false; - if (!strcasecmp(iBool, "yes")) + } + if (!strcasecmp(iBool, "yes")) { return true; - if (!strcasecmp(iBool, "no")) + } + if (!strcasecmp(iBool, "no")) { return false; - if (!strcasecmp(iBool, "on")) + } + if (!strcasecmp(iBool, "on")) { return true; - if (!strcasecmp(iBool, "off")) + } + if (!strcasecmp(iBool, "off")) { return false; - if (!strcasecmp(iBool, "enable")) + } + if (!strcasecmp(iBool, "enable")) { return true; - if (!strcasecmp(iBool, "disable")) + } + if (!strcasecmp(iBool, "disable")) { return false; - if (!strcasecmp(iBool, "enabled")) + } + if (!strcasecmp(iBool, "enabled")) { return true; - if (!strcasecmp(iBool, "disabled")) + } + if (!strcasecmp(iBool, "disabled")) { return false; - if (!strcasecmp(iBool, "y")) + } + if (!strcasecmp(iBool, "y")) { return true; - if (!strcasecmp(iBool, "n")) + } + if (!strcasecmp(iBool, "n")) { return false; - if (atoi(iBool)) + } + if (atoi(iBool)) { return true; + } return false; } @@ -484,21 +535,19 @@ char *CleanMobName(const char *in, char *out) { unsigned i, j; - for (i = j = 0; i < strlen(in); i++) - { + for (i = j = 0; i < strlen(in); i++) { // convert _ to space.. any other conversions like this? I *think* this // is the only non alpha char that's not stripped but converted. - if (in[i] == '_') - { + if (in[i] == '_') { out[j++] = ' '; } - else - { - if (isalpha(in[i]) || (in[i] == '`')) // numbers, #, or any other crap just gets skipped + else { + if (isalpha(in[i]) || (in[i] == '`')) { // numbers, #, or any other crap just gets skipped out[j++] = in[i]; + } } } - out[j] = 0; // terimnate the string before returning it + out[j] = 0; // terimnate the string before returning it return out; } @@ -506,8 +555,9 @@ char *CleanMobName(const char *in, char *out) void RemoveApostrophes(std::string &s) { for (unsigned int i = 0; i < s.length(); ++i) - if (s[i] == '\'') + if (s[i] == '\'') { s[i] = '_'; + } } char *RemoveApostrophes(const char *s) @@ -517,8 +567,9 @@ char *RemoveApostrophes(const char *s) strcpy(NewString, s); for (unsigned int i = 0; i < strlen(NewString); ++i) - if (NewString[i] == '\'') + if (NewString[i] == '\'') { NewString[i] = '_'; + } return NewString; } @@ -537,11 +588,12 @@ const char *ConvertArrayF(float input, char *returnchar) bool isAlphaNumeric(const char *text) { - for (unsigned int charIndex = 0; charIndex 'z') && (text[charIndex] < 'A' || text[charIndex] > 'Z') && - (text[charIndex] < '0' || text[charIndex] > '9')) + (text[charIndex] < '0' || text[charIndex] > '9')) { return false; + } } return true; @@ -596,11 +648,10 @@ std::string numberToWords(unsigned long long int n) } // first letter capitalized and rest made lower case -std::string FormatName(const std::string& char_name) +std::string FormatName(const std::string &char_name) { std::string formatted(char_name); - if (!formatted.empty()) - { + if (!formatted.empty()) { std::transform(formatted.begin(), formatted.end(), formatted.begin(), ::tolower); formatted[0] = ::toupper(formatted[0]); } @@ -620,6 +671,12 @@ bool IsAllowedWorldServerCharacterList(char c) void SanitizeWorldServerName(char *name) { std::string server_long_name = name; + + strcpy(name, SanitizeWorldServerName(server_long_name).c_str()); +} + +std::string SanitizeWorldServerName(std::string server_long_name) +{ server_long_name.erase( std::remove_if( server_long_name.begin(), @@ -632,5 +689,333 @@ void SanitizeWorldServerName(char *name) server_long_name = trim(server_long_name); - strcpy(name, server_long_name.c_str()); + // bad word filter + for (auto &piece: split_string(server_long_name, " ")) { + for (auto &word: GetBadWords()) { + // for shorter words that can actually be part of legitimate words + // make sure that it isn't part of another word by matching on a space + if (str_tolower(piece) == word) { + find_replace( + server_long_name, + piece, + repeat("*", (int) word.length()) + ); + continue; + } + + auto pos = str_tolower(piece).find(word); + if (str_tolower(piece).find(word) != std::string::npos && piece.length() > 4 && word.length() > 4) { + auto found_word = piece.substr(pos, word.length()); + std::string replaced_piece = piece.substr(pos, word.length()); + + find_replace( + server_long_name, + replaced_piece, + repeat("*", (int) word.length()) + ); + } + } + } + + return server_long_name; +} + +std::string repeat(std::string s, int n) +{ + std::string s1 = s; + for (int i = 1; i < n; i++) { + s += s1; + } + + return s; +} + +std::vector GetBadWords() +{ + return std::vector{ + "2g1c", + "acrotomophilia", + "anal", + "anilingus", + "anus", + "apeshit", + "arsehole", + "ass", + "asshole", + "assmunch", + "autoerotic", + "babeland", + "bangbros", + "bangbus", + "bareback", + "barenaked", + "bastard", + "bastardo", + "bastinado", + "bbw", + "bdsm", + "beaner", + "beaners", + "beaver", + "beastiality", + "bestiality", + "bimbos", + "birdlock", + "bitch", + "bitches", + "blowjob", + "blumpkin", + "bollocks", + "bondage", + "boner", + "boob", + "boobs", + "bukkake", + "bulldyke", + "bullshit", + "bung", + "bunghole", + "busty", + "butt", + "buttcheeks", + "butthole", + "camel toe", + "camgirl", + "camslut", + "camwhore", + "carpetmuncher", + "cialis", + "circlejerk", + "clit", + "clitoris", + "clusterfuck", + "cock", + "cocks", + "coprolagnia", + "coprophilia", + "cornhole", + "coon", + "coons", + "creampie", + "cum", + "cumming", + "cumshot", + "cumshots", + "cunnilingus", + "cunt", + "darkie", + "daterape", + "deepthroat", + "dendrophilia", + "dick", + "dildo", + "dingleberry", + "dingleberries", + "doggiestyle", + "doggystyle", + "dolcett", + "domination", + "dominatrix", + "dommes", + "hump", + "dvda", + "ecchi", + "ejaculation", + "erotic", + "erotism", + "escort", + "eunuch", + "fag", + "faggot", + "fecal", + "felch", + "fellatio", + "feltch", + "femdom", + "figging", + "fingerbang", + "fingering", + "fisting", + "footjob", + "frotting", + "fuck", + "fuckin", + "fucking", + "fucktards", + "fudgepacker", + "futanari", + "gangbang", + "gangbang", + "gaysex", + "genitals", + "goatcx", + "goatse", + "gokkun", + "goodpoop", + "goregasm", + "grope", + "g-spot", + "guro", + "handjob", + "hentai", + "homoerotic", + "honkey", + "hooker", + "horny", + "humping", + "incest", + "intercourse", + "jailbait", + "jigaboo", + "jiggaboo", + "jiggerboo", + "jizz", + "juggs", + "kike", + "kinbaku", + "kinkster", + "kinky", + "knobbing", + "livesex", + "lolita", + "lovemaking", + "masturbate", + "masturbating", + "masturbation", + "milf", + "mong", + "motherfucker", + "muffdiving", + "nambla", + "nawashi", + "negro", + "neonazi", + "nigga", + "nigger", + "nimphomania", + "nipple", + "nipples", + "nsfw", + "nude", + "nudity", + "nutten", + "nympho", + "nymphomania", + "octopussy", + "omorashi", + "orgasm", + "orgy", + "paedophile", + "paki", + "panties", + "panty", + "pedobear", + "pedophile", + "pegging", + "penis", + "pikey", + "pissing", + "pisspig", + "playboy", + "ponyplay", + "poof", + "poon", + "poontang", + "punany", + "poopchute", + "porn", + "porno", + "pornography", + "pthc", + "pubes", + "pussy", + "queaf", + "queef", + "quim", + "raghead", + "rape", + "raping", + "rapist", + "rectum", + "rimjob", + "rimming", + "sadism", + "santorum", + "scat", + "schlong", + "scissoring", + "semen", + "sex", + "sexcam", + "sexo", + "sexy", + "sexual", + "sexually", + "sexuality", + "shemale", + "shibari", + "shit", + "shitblimp", + "shitty", + "shota", + "shrimping", + "skeet", + "slanteye", + "slut", + "s&m", + "smut", + "snatch", + "snowballing", + "sodomize", + "sodomy", + "spastic", + "spic", + "splooge", + "spooge", + "spunk", + "strapon", + "strappado", + "suck", + "sucks", + "swastika", + "swinger", + "threesome", + "throating", + "thumbzilla", + "tight white", + "tit", + "tits", + "titties", + "titty", + "topless", + "tosser", + "towelhead", + "tranny", + "tribadism", + "tubgirl", + "tushy", + "twat", + "twink", + "twinkie", + "undressing", + "upskirt", + "urophilia", + "vagina", + "viagra", + "vibrator", + "vorarephilia", + "voyeur", + "voyeurweb", + "voyuer", + "vulva", + "wank", + "wetback", + "whore", + "worldsex", + "xx", + "xxx", + "yaoi", + "yiffy", + "zoophilia" + }; } diff --git a/common/string_util.h b/common/string_util.h index 9402bfcb6..0353c5c98 100644 --- a/common/string_util.h +++ b/common/string_util.h @@ -208,7 +208,11 @@ void RemoveApostrophes(std::string &s); std::string convert2digit(int n, std::string suffix); std::string numberToWords(unsigned long long int n); std::string FormatName(const std::string& char_name); +bool IsAllowedWorldServerCharacterList(char c); void SanitizeWorldServerName(char *name); +std::string SanitizeWorldServerName(std::string server_long_name); +std::string repeat(std::string s, int n); +std::vector GetBadWords(); template auto CleanMobName(InputIterator first, InputIterator last, OutputIterator result) diff --git a/loginserver/CMakeLists.txt b/loginserver/CMakeLists.txt index 7d60655fc..eec55af2a 100644 --- a/loginserver/CMakeLists.txt +++ b/loginserver/CMakeLists.txt @@ -22,7 +22,7 @@ SET(eqlogin_headers loginserver_command_handler.h loginserver_webserver.h login_server.h - login_structures.h + login_types.h options.h server_manager.h world_server.h diff --git a/loginserver/client.h b/loginserver/client.h index 1a9b931fd..95aa0cfa7 100644 --- a/loginserver/client.h +++ b/loginserver/client.h @@ -7,44 +7,9 @@ #include "../common/eq_stream_intf.h" #include "../common/net/dns.h" #include "../common/net/daybreak_connection.h" -#include "login_structures.h" +#include "login_types.h" #include -enum LSClientVersion { - cv_titanium, - cv_sod -}; - -enum LSClientStatus { - cs_not_sent_session_ready, - cs_waiting_for_login, - cs_creating_account, - cs_failed_to_login, - cs_logged_in -}; - -namespace LS { - namespace ServerStatusFlags { - enum eServerStatusFlags { - Up = 0, // default - Down = 1, - Unused = 2, - Locked = 4 // can be combined with Down to show "Locked (Down)" - }; - } - - namespace ServerTypeFlags { - enum eServerTypeFlags { - None = 0, - Standard = 1, - Unknown2 = 2, - Unknown4 = 4, - Preferred = 8, - Legends = 16 // can be combined with Preferred flag to override color in Legends section with Preferred color (green) - }; - } -} - /** * Client class, controls a single client and it's connection to the login server */ diff --git a/loginserver/login_structures.h b/loginserver/login_types.h similarity index 57% rename from loginserver/login_structures.h rename to loginserver/login_types.h index 01a0bf201..2dfca14fc 100644 --- a/loginserver/login_structures.h +++ b/loginserver/login_types.h @@ -4,39 +4,34 @@ #pragma pack(1) // unencrypted base message header in all packets -struct LoginBaseMessage_Struct -{ +struct LoginBaseMessage_Struct { int32_t sequence; // request type/login sequence (2: handshake, 3: login, 4: serverlist, ...) bool compressed; // true: deflated int8_t encrypt_type; // 1: invert (unused) 2: des (2 for encrypted player logins and order expansions) (client uses what it sent, ignores in reply) int32_t unk3; // unused? }; -struct LoginBaseReplyMessage_Struct -{ +struct LoginBaseReplyMessage_Struct { bool success; // 0: failure (shows error string) 1: success int32_t error_str_id; // last error eqlsstr id, default: 101 (no error) char str[1]; // variable length, unknown (may be unused, this struct is a common pattern elsewhere) }; -struct LoginHandShakeReply_Struct -{ - LoginBaseMessage_Struct base_header; +struct LoginHandShakeReply_Struct { + LoginBaseMessage_Struct base_header; LoginBaseReplyMessage_Struct base_reply; - char unknown[1]; // variable length string + char unknown[1]; // variable length string }; // for reference, login buffer is variable (minimum size 8 due to encryption) -struct PlayerLogin_Struct -{ +struct PlayerLogin_Struct { LoginBaseMessage_Struct base_header; - char username[1]; - char password[1]; + char username[1]; + char password[1]; }; // variable length, can use directly if not serializing strings -struct PlayerLoginReply_Struct -{ +struct PlayerLoginReply_Struct { // base header excluded to make struct data easier to encrypt //LoginBaseMessage_Struct base_header; LoginBaseReplyMessage_Struct base_reply; @@ -59,8 +54,7 @@ struct PlayerLoginReply_Struct }; // variable length, for reference -struct LoginClientServerData_Struct -{ +struct LoginClientServerData_Struct { char ip[1]; int32_t server_type; // legends, preferred, standard int32_t server_id; @@ -72,29 +66,78 @@ struct LoginClientServerData_Struct }; // variable length, for reference -struct ServerListReply_Struct -{ - LoginBaseMessage_Struct base_header; +struct ServerListReply_Struct { + LoginBaseMessage_Struct base_header; LoginBaseReplyMessage_Struct base_reply; - int32_t server_count; + int32_t server_count; LoginClientServerData_Struct servers[0]; }; struct PlayEverquestRequest_Struct { LoginBaseMessage_Struct base_header; - uint32 server_number; + uint32 server_number; }; // SCJoinServerReply struct PlayEverquestResponse_Struct { - LoginBaseMessage_Struct base_header; + LoginBaseMessage_Struct base_header; LoginBaseReplyMessage_Struct base_reply; - uint32 server_number; + uint32 server_number; }; - #pragma pack() +enum LSClientVersion { + cv_titanium, + cv_sod +}; + +enum LSClientStatus { + cs_not_sent_session_ready, + cs_waiting_for_login, + cs_creating_account, + cs_failed_to_login, + cs_logged_in +}; + +namespace LS { + namespace ServerStatusFlags { + enum eServerStatusFlags { + Up = 0, // default + Down = 1, + Unused = 2, + Locked = 4 // can be combined with Down to show "Locked (Down)" + }; + } + + namespace ServerTypeFlags { + enum eServerTypeFlags { + None = 0, + Standard = 1, + Unknown2 = 2, + Unknown4 = 4, + Preferred = 8, + Legends = 16 // can be combined with Preferred flag to override color in Legends section with Preferred color (green) + }; + } + + enum ServerType { + Standard = 3, + Preferred = 2, + Legends = 1, + }; + + namespace ErrStr { + constexpr static int NO_ERROR = 101; // No Error + constexpr static int SERVER_UNAVAILABLE = 326; // That server is currently unavailable. Please check the EverQuest webpage for current server status and try again later. + constexpr static int ACCOUNT_SUSPENDED = 337; // This account is currently suspended. Please contact customer service for more information. + constexpr static int ACCOUNT_BANNED = 338; // This account is currently banned. Please contact customer service for more information. + constexpr static int WORLD_MAX_CAPACITY = 339; // The world server is currently at maximum capacity and not allowing further logins until the number of players online decreases. Please try again later. + constexpr static int ERROR_1018_ACTIVE_CHARACTER = 111; // Error 1018: You currently have an active character on that EverQuest Server, please allow a minute for synchronization and try again. + constexpr static int UNKNOWN_ERROR = 102; // Error - Unknown Error Occurred + }; +} + #endif diff --git a/loginserver/login_util/login.json b/loginserver/login_util/login.json index af0f042cf..c25242f7c 100644 --- a/loginserver/login_util/login.json +++ b/loginserver/login_util/login.json @@ -12,6 +12,8 @@ "worldservers": { "unregistered_allowed": true, "show_player_count": false, + "dev_test_servers_list_bottom": false, + "special_character_start_list_bottom": false, "reject_duplicate_servers": false }, "web_api": { diff --git a/loginserver/main.cpp b/loginserver/main.cpp index be925efa6..46f2313b3 100644 --- a/loginserver/main.cpp +++ b/loginserver/main.cpp @@ -62,9 +62,30 @@ void LoadServerConfig() "worldservers", "reject_duplicate_servers", false - )); - server.options.AllowUnregistered(server.config.GetVariableBool("worldservers", "unregistered_allowed", true)); + ) + ); server.options.SetShowPlayerCount(server.config.GetVariableBool("worldservers", "show_player_count", false)); + server.options.AllowUnregistered( + server.config.GetVariableBool( + "worldservers", + "unregistered_allowed", + true + ) + ); + server.options.SetWorldDevTestServersListBottom( + server.config.GetVariableBool( + "worldservers", + "dev_test_servers_list_bottom", + false + ) + ); + server.options.SetWorldSpecialCharacterStartListBottom( + server.config.GetVariableBool( + "worldservers", + "special_character_start_list_bottom", + false + ) + ); /** * Account @@ -242,6 +263,14 @@ int main(int argc, char **argv) LogInfo("[Config] [WorldServer] IsRejectingDuplicateServers [{0}]", server.options.IsRejectingDuplicateServers()); LogInfo("[Config] [WorldServer] IsUnregisteredAllowed [{0}]", server.options.IsUnregisteredAllowed()); LogInfo("[Config] [WorldServer] ShowPlayerCount [{0}]", server.options.IsShowPlayerCountEnabled()); + LogInfo( + "[Config] [WorldServer] DevAndTestServersListBottom [{0}]", + server.options.IsWorldDevTestServersListBottom() + ); + LogInfo( + "[Config] [WorldServer] SpecialCharactersStartListBottom [{0}]", + server.options.IsWorldSpecialCharacterStartListBottom() + ); LogInfo("[Config] [Security] GetEncryptionMode [{0}]", server.options.GetEncryptionMode()); LogInfo("[Config] [Security] IsTokenLoginAllowed [{0}]", server.options.IsTokenLoginAllowed()); LogInfo("[Config] [Security] IsPasswordLoginAllowed [{0}]", server.options.IsPasswordLoginAllowed()); diff --git a/loginserver/options.h b/loginserver/options.h index c6fc1589d..cf1de76d3 100644 --- a/loginserver/options.h +++ b/loginserver/options.h @@ -121,6 +121,20 @@ public: { Options::show_player_count = show_player_count; } + inline bool IsWorldDevTestServersListBottom() const { return world_dev_test_servers_list_bottom; } + inline void SetWorldDevTestServersListBottom(bool dev_test_servers_list_bottom) + { + Options::world_dev_test_servers_list_bottom = dev_test_servers_list_bottom; + } + + inline bool IsWorldSpecialCharacterStartListBottom() const + { + return world_special_character_start_list_bottom; + } + inline void SetWorldSpecialCharacterStartListBottom(bool world_special_character_start_list_bottom) + { + Options::world_special_character_start_list_bottom = world_special_character_start_list_bottom; + } private: bool allow_unregistered; @@ -129,6 +143,8 @@ private: bool dump_in_packets; bool dump_out_packets; bool reject_duplicate_servers; + bool world_dev_test_servers_list_bottom; + bool world_special_character_start_list_bottom; bool allow_token_login; bool allow_password_login; bool show_player_count; diff --git a/loginserver/server_manager.cpp b/loginserver/server_manager.cpp index d49809278..05758dc07 100644 --- a/loginserver/server_manager.cpp +++ b/loginserver/server_manager.cpp @@ -1,6 +1,6 @@ #include "server_manager.h" #include "login_server.h" -#include "login_structures.h" +#include "login_types.h" #include #include "../common/eqemu_logsys.h" diff --git a/loginserver/world_server.cpp b/loginserver/world_server.cpp index b6dc6885d..6e72befdf 100644 --- a/loginserver/world_server.cpp +++ b/loginserver/world_server.cpp @@ -1,6 +1,6 @@ #include "world_server.h" #include "login_server.h" -#include "login_structures.h" +#include "login_types.h" #include "../common/ip_util.h" #include "../common/string_util.h" @@ -198,15 +198,15 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne LogDebug("User-To-World Response received"); } - auto *user_to_world_response = (UsertoWorldResponseLegacy_Struct *) packet.Data(); + auto *r = (UsertoWorldResponseLegacy_Struct *) packet.Data(); - LogDebug("Trying to find client with user id of [{0}]", user_to_world_response->lsaccountid); - Client *client = server.client_manager->GetClient(user_to_world_response->lsaccountid, "eqemu"); + LogDebug("Trying to find client with user id of [{0}]", r->lsaccountid); + Client *client = server.client_manager->GetClient(r->lsaccountid, "eqemu"); if (client) { LogDebug( "Found client with user id of [{0}] and account name of [{1}]", - user_to_world_response->lsaccountid, + r->lsaccountid, client->GetAccountName() ); @@ -217,9 +217,9 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne auto *per = (PlayEverquestResponse_Struct *) outapp->pBuffer; per->base_header.sequence = client->GetPlaySequence(); - per->server_number = client->GetPlayServerID(); + per->server_number = client->GetPlayServerID(); - if (user_to_world_response->response > 0) { + if (r->response > 0) { per->base_reply.success = true; SendClientAuth( client->GetConnection()->GetRemoteAddr(), @@ -230,27 +230,27 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne ); } - switch (user_to_world_response->response) { + switch (r->response) { case UserToWorldStatusSuccess: - per->base_reply.error_str_id = 101; + per->base_reply.error_str_id = LS::ErrStr::NO_ERROR; break; case UserToWorldStatusWorldUnavail: - per->base_reply.error_str_id = 326; + per->base_reply.error_str_id = LS::ErrStr::SERVER_UNAVAILABLE; break; case UserToWorldStatusSuspended: - per->base_reply.error_str_id = 337; + per->base_reply.error_str_id = LS::ErrStr::ACCOUNT_SUSPENDED; break; case UserToWorldStatusBanned: - per->base_reply.error_str_id = 338; + per->base_reply.error_str_id = LS::ErrStr::ACCOUNT_BANNED; break; case UserToWorldStatusWorldAtCapacity: - per->base_reply.error_str_id = 339; + per->base_reply.error_str_id = LS::ErrStr::WORLD_MAX_CAPACITY; break; case UserToWorldStatusAlreadyOnline: - per->base_reply.error_str_id = 111; + per->base_reply.error_str_id = LS::ErrStr::ERROR_1018_ACTIVE_CHARACTER; break; default: - per->base_reply.error_str_id = 102; + per->base_reply.error_str_id = LS::ErrStr::UNKNOWN_ERROR; } if (server.options.IsWorldTraceOn()) { @@ -275,7 +275,7 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne else { LogError( "Received User-To-World Response for [{0}] but could not find the client referenced!", - user_to_world_response->lsaccountid + r->lsaccountid ); } } @@ -335,7 +335,7 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac auto *per = (PlayEverquestResponse_Struct *) outapp->pBuffer; per->base_header.sequence = client->GetPlaySequence(); - per->server_number = client->GetPlayServerID(); + per->server_number = client->GetPlayServerID(); LogDebug( "Found sequence and play of [{0}] [{1}]", @@ -358,25 +358,25 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac switch (user_to_world_response->response) { case UserToWorldStatusSuccess: - per->base_reply.error_str_id = 101; + per->base_reply.error_str_id = LS::ErrStr::NO_ERROR; break; case UserToWorldStatusWorldUnavail: - per->base_reply.error_str_id = 326; + per->base_reply.error_str_id = LS::ErrStr::SERVER_UNAVAILABLE; break; case UserToWorldStatusSuspended: - per->base_reply.error_str_id = 337; + per->base_reply.error_str_id = LS::ErrStr::ACCOUNT_SUSPENDED; break; case UserToWorldStatusBanned: - per->base_reply.error_str_id = 338; + per->base_reply.error_str_id = LS::ErrStr::ACCOUNT_BANNED; break; case UserToWorldStatusWorldAtCapacity: - per->base_reply.error_str_id = 339; + per->base_reply.error_str_id = LS::ErrStr::WORLD_MAX_CAPACITY; break; case UserToWorldStatusAlreadyOnline: - per->base_reply.error_str_id = 111; + per->base_reply.error_str_id = LS::ErrStr::ERROR_1018_ACTIVE_CHARACTER; break; default: - per->base_reply.error_str_id = 102; + per->base_reply.error_str_id = LS::ErrStr::UNKNOWN_ERROR; } if (server.options.IsTraceOn()) { @@ -571,6 +571,12 @@ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info GetServerLongName(), GetRemoteIp() ); + + WorldServer::FormatWorldServerName( + new_world_server_info_packet->server_long_name, + world_registration.server_list_type + ); + SetLongName(new_world_server_info_packet->server_long_name); } /** @@ -1025,21 +1031,21 @@ bool WorldServer::ValidateWorldServerAdminLogin( return false; } -void WorldServer::SerializeForClientServerList(SerializeBuffer& out, bool use_local_ip) const +void WorldServer::SerializeForClientServerList(SerializeBuffer &out, bool use_local_ip) const { // see LoginClientServerData_Struct if (use_local_ip) { out.WriteString(GetLocalIP()); - } else { + } + else { out.WriteString(GetRemoteIP()); } - switch (GetServerListID()) - { - case 1: + switch (GetServerListID()) { + case LS::ServerType::Legends: out.WriteInt32(LS::ServerTypeFlags::Legends); break; - case 2: + case LS::ServerType::Preferred: out.WriteInt32(LS::ServerTypeFlags::Preferred); break; default: @@ -1348,3 +1354,37 @@ void WorldServer::OnKeepAlive(EQ::Timer *t) ServerPacket pack(ServerOP_KeepAlive, 0); m_connection->SendPacket(&pack); } + +void WorldServer::FormatWorldServerName(char *name, int8 server_list_type) +{ + std::string server_long_name = name; + server_long_name = trim(server_long_name); + + bool name_set_to_bottom = false; + if (server_list_type == LS::ServerType::Standard) { + if (server.options.IsWorldDevTestServersListBottom()) { + std::string s = str_tolower(server_long_name); + if (s.find("dev") != std::string::npos) { + server_long_name = fmt::format("|D| {}", server_long_name); + name_set_to_bottom = true; + } + else if (s.find("test") != std::string::npos) { + server_long_name = fmt::format("|T| {}", server_long_name); + name_set_to_bottom = true; + } + else if (s.find("installer") != std::string::npos) { + server_long_name = fmt::format("|I| {}", server_long_name); + name_set_to_bottom = true; + } + } + if (server.options.IsWorldSpecialCharacterStartListBottom() && !name_set_to_bottom) { + auto first_char = server_long_name.c_str()[0]; + if (IsAllowedWorldServerCharacterList(first_char) && first_char != '|') { + server_long_name = fmt::format("|*| {}", server_long_name); + name_set_to_bottom = true; + } + } + } + + strn0cpy(name, server_long_name.c_str(), 201); +} diff --git a/loginserver/world_server.h b/loginserver/world_server.h index af63d22a9..c4ea5f87d 100644 --- a/loginserver/world_server.h +++ b/loginserver/world_server.h @@ -194,6 +194,7 @@ private: void OnKeepAlive(EQ::Timer *t); std::unique_ptr m_keepalive; + static void FormatWorldServerName(char *name, int8 server_list_type); }; #endif diff --git a/world/login_server.cpp b/world/login_server.cpp index 0dc665f36..796438f35 100644 --- a/world/login_server.cpp +++ b/world/login_server.cpp @@ -603,8 +603,6 @@ void LoginServer::SendInfo() WorldConfig::SetLocalAddress(l->local_ip_address); } - SanitizeWorldServerName(l->server_long_name); - LogInfo( "[LoginServer::SendInfo] protocol_version [{}] server_version [{}] long_name [{}] short_name [{}] account_name [{}] remote_ip_address [{}] local_ip [{}]", l->protocol_version, From 9c55cf9a8ea1c7182bea40a43545811ab62dedd4 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 13 Nov 2021 02:00:45 -0500 Subject: [PATCH 393/624] [Bug Fix] Loginserver Error String Constants. (#1747) - Constant was named after Windows macro. --- loginserver/login_types.h | 14 +++++++------- loginserver/world_server.cpp | 30 ++++++++++++++++-------------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/loginserver/login_types.h b/loginserver/login_types.h index 2dfca14fc..25e64307e 100644 --- a/loginserver/login_types.h +++ b/loginserver/login_types.h @@ -129,13 +129,13 @@ namespace LS { }; namespace ErrStr { - constexpr static int NO_ERROR = 101; // No Error - constexpr static int SERVER_UNAVAILABLE = 326; // That server is currently unavailable. Please check the EverQuest webpage for current server status and try again later. - constexpr static int ACCOUNT_SUSPENDED = 337; // This account is currently suspended. Please contact customer service for more information. - constexpr static int ACCOUNT_BANNED = 338; // This account is currently banned. Please contact customer service for more information. - constexpr static int WORLD_MAX_CAPACITY = 339; // The world server is currently at maximum capacity and not allowing further logins until the number of players online decreases. Please try again later. - constexpr static int ERROR_1018_ACTIVE_CHARACTER = 111; // Error 1018: You currently have an active character on that EverQuest Server, please allow a minute for synchronization and try again. - constexpr static int UNKNOWN_ERROR = 102; // Error - Unknown Error Occurred + constexpr static int ERROR_NONE = 101; // No Error + constexpr static int ERROR_UNKNOWN = 102; // Error - Unknown Error Occurred + constexpr static int ERROR_ACTIVE_CHARACTER = 111; // Error 1018: You currently have an active character on that EverQuest Server, please allow a minute for synchronization and try again. + constexpr static int ERROR_SERVER_UNAVAILABLE = 326; // That server is currently unavailable. Please check the EverQuest webpage for current server status and try again later. + constexpr static int ERROR_ACCOUNT_SUSPENDED = 337; // This account is currently suspended. Please contact customer service for more information. + constexpr static int ERROR_ACCOUNT_BANNED = 338; // This account is currently banned. Please contact customer service for more information. + constexpr static int ERROR_WORLD_MAX_CAPACITY = 339; // The world server is currently at maximum capacity and not allowing further logins until the number of players online decreases. Please try again later. }; } diff --git a/loginserver/world_server.cpp b/loginserver/world_server.cpp index 6e72befdf..24004347d 100644 --- a/loginserver/world_server.cpp +++ b/loginserver/world_server.cpp @@ -232,25 +232,26 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne switch (r->response) { case UserToWorldStatusSuccess: - per->base_reply.error_str_id = LS::ErrStr::NO_ERROR; + per->base_reply.error_str_id = LS::ErrStr::ERROR_NONE; break; case UserToWorldStatusWorldUnavail: - per->base_reply.error_str_id = LS::ErrStr::SERVER_UNAVAILABLE; + per->base_reply.error_str_id = LS::ErrStr::ERROR_SERVER_UNAVAILABLE; break; case UserToWorldStatusSuspended: - per->base_reply.error_str_id = LS::ErrStr::ACCOUNT_SUSPENDED; + per->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_SUSPENDED; break; case UserToWorldStatusBanned: - per->base_reply.error_str_id = LS::ErrStr::ACCOUNT_BANNED; + per->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_BANNED; break; case UserToWorldStatusWorldAtCapacity: - per->base_reply.error_str_id = LS::ErrStr::WORLD_MAX_CAPACITY; + per->base_reply.error_str_id = LS::ErrStr::ERROR_WORLD_MAX_CAPACITY; break; case UserToWorldStatusAlreadyOnline: - per->base_reply.error_str_id = LS::ErrStr::ERROR_1018_ACTIVE_CHARACTER; + per->base_reply.error_str_id = LS::ErrStr::ERROR_ACTIVE_CHARACTER; break; default: - per->base_reply.error_str_id = LS::ErrStr::UNKNOWN_ERROR; + per->base_reply.error_str_id = LS::ErrStr::ERROR_UNKNOWN; + break; } if (server.options.IsWorldTraceOn()) { @@ -358,25 +359,26 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac switch (user_to_world_response->response) { case UserToWorldStatusSuccess: - per->base_reply.error_str_id = LS::ErrStr::NO_ERROR; + per->base_reply.error_str_id = LS::ErrStr::ERROR_NONE; break; case UserToWorldStatusWorldUnavail: - per->base_reply.error_str_id = LS::ErrStr::SERVER_UNAVAILABLE; + per->base_reply.error_str_id = LS::ErrStr::ERROR_SERVER_UNAVAILABLE; break; case UserToWorldStatusSuspended: - per->base_reply.error_str_id = LS::ErrStr::ACCOUNT_SUSPENDED; + per->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_SUSPENDED; break; case UserToWorldStatusBanned: - per->base_reply.error_str_id = LS::ErrStr::ACCOUNT_BANNED; + per->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_BANNED; break; case UserToWorldStatusWorldAtCapacity: - per->base_reply.error_str_id = LS::ErrStr::WORLD_MAX_CAPACITY; + per->base_reply.error_str_id = LS::ErrStr::ERROR_WORLD_MAX_CAPACITY; break; case UserToWorldStatusAlreadyOnline: - per->base_reply.error_str_id = LS::ErrStr::ERROR_1018_ACTIVE_CHARACTER; + per->base_reply.error_str_id = LS::ErrStr::ERROR_ACTIVE_CHARACTER; break; default: - per->base_reply.error_str_id = LS::ErrStr::UNKNOWN_ERROR; + per->base_reply.error_str_id = LS::ErrStr::ERROR_UNKNOWN; + break; } if (server.options.IsTraceOn()) { From e8607a0c78541f20cf5e6614614970b6795fef47 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 13 Nov 2021 05:25:58 -0500 Subject: [PATCH 394/624] [Commands] Cleanup #checklos Command. (#1744) - Cleanup message and logic. --- zone/command.cpp | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 1dea47cb5..c7f6b3b3a 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -9582,17 +9582,20 @@ void command_oocmute(Client *c, const Seperator *sep) void command_checklos(Client *c, const Seperator *sep) { - if (c->GetTarget()) { - if (c->CheckLosFN(c->GetTarget())) { - c->Message(Chat::White, "You have LOS to %s", c->GetTarget()->GetName()); - } - else { - c->Message(Chat::White, "You do not have LOS to %s", c->GetTarget()->GetName()); - } - } - else { - c->Message(Chat::White, "ERROR: Target required"); + if (!c->GetTarget()) { + c->Message(Chat::White, "You must have a target to use this command."); } + + bool has_los = c->CheckLosFN(c->GetTarget()); + c->Message( + Chat::White, + fmt::format( + "You {}have line of sight to {} ({}).", + has_los ? "" : "do not ", + c->GetTarget()->GetCleanName(), + c->GetTarget()->GetID() + ).c_str() + ); } void command_set_adventure_points(Client *c, const Seperator *sep) From cef352f0ac6459a16a2823fe9d801554eab4865c Mon Sep 17 00:00:00 2001 From: JJ <3617814+joligario@users.noreply.github.com> Date: Sat, 13 Nov 2021 10:39:35 -0500 Subject: [PATCH 395/624] [Bug Fix] Removed unused pointer. Fixes #157. (#1748) --- zone/hate_list.cpp | 2 +- zone/hate_list.h | 2 +- zone/spell_effects.cpp | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index 5e13f3ed5..83ec481fa 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -266,7 +266,7 @@ void HateList::DoFactionHits(int32 npc_faction_level_id) { } } -int HateList::GetSummonedPetCountOnHateList(Mob *hater) { +int HateList::GetSummonedPetCountOnHateList() { //Function to get number of 'Summoned' pets on a targets hate list to allow calculations for certian spell effects. //Unclear from description that pets are required to be 'summoned body type'. Will not require at this time. diff --git a/zone/hate_list.h b/zone/hate_list.h index 22971aa2e..0dbfae841 100644 --- a/zone/hate_list.h +++ b/zone/hate_list.h @@ -60,7 +60,7 @@ public: bool RemoveEntFromHateList(Mob *ent); int AreaRampage(Mob *caster, Mob *target, int count, ExtraAttackOptions *opts); - int GetSummonedPetCountOnHateList(Mob *hater); + int GetSummonedPetCountOnHateList(); int GetHateRatio(Mob *top, Mob *other); int32 GetEntHateAmount(Mob *ent, bool in_damage = false); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index f79515198..efe826c9d 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -7651,7 +7651,7 @@ bool Mob::PassCastRestriction(int value) break; case HAS_BETWEEN_1_TO_2_PETS_ON_HATELIST: { - int count = hate_list.GetSummonedPetCountOnHateList(this); + int count = hate_list.GetSummonedPetCountOnHateList(); if (count >= 1 && count <= 2) { return true; } @@ -7659,7 +7659,7 @@ bool Mob::PassCastRestriction(int value) } case HAS_BETWEEN_3_TO_5_PETS_ON_HATELIST: { - int count = hate_list.GetSummonedPetCountOnHateList(this); + int count = hate_list.GetSummonedPetCountOnHateList(); if (count >= 3 && count <= 5) { return true; } @@ -7667,7 +7667,7 @@ bool Mob::PassCastRestriction(int value) } case HAS_BETWEEN_6_TO_9_PETS_ON_HATELIST: { - int count = hate_list.GetSummonedPetCountOnHateList(this); + int count = hate_list.GetSummonedPetCountOnHateList(); if (count >= 6 && count <= 9) { return true; } @@ -7675,7 +7675,7 @@ bool Mob::PassCastRestriction(int value) } case HAS_BETWEEN_10_TO_14_PETS_ON_HATELIST: { - int count = hate_list.GetSummonedPetCountOnHateList(this); + int count = hate_list.GetSummonedPetCountOnHateList(); if (count >= 10 && count <= 14) { return true; } @@ -7683,7 +7683,7 @@ bool Mob::PassCastRestriction(int value) } case HAS_MORE_THAN_14_PETS_ON_HATELIST: { - int count = hate_list.GetSummonedPetCountOnHateList(this); + int count = hate_list.GetSummonedPetCountOnHateList(); if (count > 14) { return true; } @@ -8239,7 +8239,7 @@ bool Mob::PassCastRestriction(int value) } if (value >= HAS_AT_LEAST_1_PET_ON_HATELIST && value <= HAS_AT_LEAST_20_PETS_ON_HATELIST) { - int count = hate_list.GetSummonedPetCountOnHateList(this); + int count = hate_list.GetSummonedPetCountOnHateList(); int minium_amount_of_pets_needed = (1 + value) - HAS_AT_LEAST_1_PET_ON_HATELIST; if (count >= minium_amount_of_pets_needed) { From 776449aa3dcb16774dbed3a76df8664d6b9c56cd Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 13 Nov 2021 14:47:42 -0500 Subject: [PATCH 396/624] [Spells] Update to IsCombatProc checks (#1741) * Update spells.cpp * Update spells.cpp * Update spells.cpp * Update spells.cpp --- zone/spells.cpp | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 73b9b49bc..b38d23486 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -5669,21 +5669,36 @@ bool Mob::FindType(uint16 type, bool bOffensive, uint16 threshold) { bool Mob::IsCombatProc(uint16 spell_id) { - if (RuleB(Spells, FocusCombatProcs)) + if (RuleB(Spells, FocusCombatProcs)) { return false; + } - if(spell_id == SPELL_UNKNOWN) + if (spell_id == SPELL_UNKNOWN) { return(false); + } if ((spells[spell_id].cast_time == 0) && (spells[spell_id].recast_time == 0) && (spells[spell_id].recovery_time == 0)) { for (int i = 0; i < MAX_PROCS; i++){ - if (PermaProcs[i].spellID == spell_id || SpellProcs[i].spellID == spell_id - || RangedProcs[i].spellID == spell_id){ + if (PermaProcs[i].spellID == spell_id || + SpellProcs[i].spellID == spell_id || + RangedProcs[i].spellID == spell_id || + DefensiveProcs[i].spellID == spell_id){ return true; } } + + if (IsClient()) { + for (int i = 0; i < MAX_AA_PROCS; i += 4) { + + if (aabonuses.SpellProc[i + 1] == spell_id || + aabonuses.RangedProc[i + 1] == spell_id || + aabonuses.DefensiveProc[i + 1] == spell_id) { + return true; + } + } + } } return false; From 80a891e541ffdf08df1c53f512d928f4633ac213 Mon Sep 17 00:00:00 2001 From: JJ <3617814+joligario@users.noreply.github.com> Date: Sat, 13 Nov 2021 20:14:39 -0500 Subject: [PATCH 397/624] Make room for host names. Since m_remote_ip_address does not resolve IP address, world server may still use host name. --- loginserver/login_util/login_schema.sql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/loginserver/login_util/login_schema.sql b/loginserver/login_util/login_schema.sql index b07362830..f85f9262e 100644 --- a/loginserver/login_util/login_schema.sql +++ b/loginserver/login_util/login_schema.sql @@ -5,7 +5,7 @@ CREATE TABLE `login_accounts` ( `account_password` text NOT NULL, `account_email` varchar(100) NOT NULL, `source_loginserver` varchar(64) DEFAULT NULL, - `last_ip_address` varchar(15) NOT NULL, + `last_ip_address` varchar(30) NOT NULL, `last_login_date` datetime NOT NULL, `created_at` datetime DEFAULT NULL, `updated_at` datetime DEFAULT current_timestamp(), @@ -22,7 +22,7 @@ CREATE TABLE `login_server_admins` ( `last_name` varchar(50) NOT NULL, `email` varchar(100) NOT NULL, `registration_date` datetime NOT NULL, - `registration_ip_address` varchar(15) NOT NULL, + `registration_ip_address` varchar(30) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; @@ -45,7 +45,7 @@ CREATE TABLE `login_world_servers` ( `tag_description` varchar(50) NOT NULL DEFAULT '', `login_server_list_type_id` int(11) NOT NULL, `last_login_date` datetime DEFAULT NULL, - `last_ip_address` varchar(15) DEFAULT NULL, + `last_ip_address` varchar(30) DEFAULT NULL, `login_server_admin_id` int(11) NOT NULL, `is_server_trusted` int(11) NOT NULL, `note` varchar(255) DEFAULT NULL, @@ -61,4 +61,4 @@ CREATE TABLE `login_api_tokens` ( `created_at` datetime DEFAULT NULL, `updated_at` datetime DEFAULT current_timestamp(), PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; \ No newline at end of file +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; From 27f8ae399921325cd7462fb2823ba195ad02164f Mon Sep 17 00:00:00 2001 From: JJ <3617814+joligario@users.noreply.github.com> Date: Sat, 13 Nov 2021 22:15:49 -0500 Subject: [PATCH 398/624] [Hotfix] Optional SQL for existing servers (#1756) --- utils/sql/git/optional/2021_11_13_loginserver_hostname.sql | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 utils/sql/git/optional/2021_11_13_loginserver_hostname.sql diff --git a/utils/sql/git/optional/2021_11_13_loginserver_hostname.sql b/utils/sql/git/optional/2021_11_13_loginserver_hostname.sql new file mode 100644 index 000000000..521b6f853 --- /dev/null +++ b/utils/sql/git/optional/2021_11_13_loginserver_hostname.sql @@ -0,0 +1,3 @@ +ALTER TABLE login_accounts MODIFY COLUMN last_ip_address VARCHAR(30) NOT NULL; +ALTER TABLE login_server_admins MODIFY COLUMN registration_ip_address VARCHAR(30) NOT NULL; +ALTER TABLE login_world_servers MODIFY COLUMN last_ip_address VARCHAR(30) DEFAULT NULL; From 90bcc5f03cad615ac27df27c465c7ace55181821 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 14 Nov 2021 11:32:08 -0500 Subject: [PATCH 399/624] [Commands] Cleanup #zonelock Command. (#1711) * [Commands] Cleanup #zonelock Command. - Add support for Zone IDs. - Cleanup messages and display. - Fix dangling pointer in ZoneLongName() helper method so name is displayed properly. * Add account status enum. * Typo. * Typo. * Convert list to constants. * Cleanup. * Update command.cpp * Fix compile. --- common/emu_constants.h | 25 +++++++++++++ world/world_store.cpp | 20 +++++++++++ world/world_store.h | 10 ++++-- world/zonelist.cpp | 37 +++++++++++++++---- world/zoneserver.cpp | 52 +++++++++++++++++++-------- world/zoneserver.h | 2 +- zone/command.cpp | 80 +++++++++++++++++++++++++----------------- 7 files changed, 170 insertions(+), 56 deletions(-) diff --git a/common/emu_constants.h b/common/emu_constants.h index c64199082..806ce0a86 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -230,6 +230,31 @@ namespace EQ const int STANCE_TYPE_LAST = stanceBurnAE; const int STANCE_TYPE_COUNT = stanceBurnAE; + enum ServerLockType : int { + List, + Lock, + Unlock + }; + + enum AccountStatus : uint8 { + Player = 0, + Steward = 10, + ApprenticeGuide = 20, + Guide = 50, + QuestTroupe = 80, + SeniorGuide = 81, + GMTester = 85, + EQSupport = 90, + GMStaff = 95, + GMAdmin = 100, + GMLeadAdmin = 150, + QuestMaster = 160, + GMAreas = 170, + GMCoder = 180, + GMMgmt = 200, + GMImpossible = 250, + Max = 255 + }; } /*constants*/ namespace profile { diff --git a/world/world_store.cpp b/world/world_store.cpp index 5a8201124..62c484172 100644 --- a/world/world_store.cpp +++ b/world/world_store.cpp @@ -85,6 +85,26 @@ std::string WorldStore::GetZoneName(uint32 zone_id) return std::string(); } +/** + * @param zone_id + * @param error_unknown + * @return + */ +const char *WorldStore::GetZoneLongName(uint32 zone_id, bool error_unknown) +{ + for (auto &z: zones) { + if (z.zoneidnumber == zone_id) { + return z.long_name.c_str(); + } + } + + if (error_unknown) { + return "UNKNOWN"; + } + + return nullptr; +} + /** * @param zone_id * @return diff --git a/world/world_store.h b/world/world_store.h index 985419ff0..ecbba4b5e 100644 --- a/world/world_store.h +++ b/world/world_store.h @@ -40,7 +40,7 @@ public: std::string GetZoneName(uint32 zone_id); std::string GetZoneLongName(uint32 zone_id); const char *GetZoneName(uint32 zone_id, bool error_unknown = false); - + const char *GetZoneLongName(uint32 zone_id, bool error_unknown = false); }; extern WorldStore world_store; @@ -57,7 +57,13 @@ inline const char *ZoneName(uint32 zone_id, bool error_unknown = false) error_unknown ); } -inline const char *ZoneLongName(uint32 zone_id) { return world_store.GetZoneLongName(zone_id).c_str(); } +inline const char *ZoneLongName(uint32 zone_id, bool error_unknown = false) +{ + return world_store.GetZoneLongName( + zone_id, + error_unknown + ); +} inline ZoneRepository::Zone GetZone(uint32 zone_id, int version = 0) { return world_store.GetZone(zone_id, version); }; inline ZoneRepository::Zone GetZone(const char *in_zone_name) { return world_store.GetZone(in_zone_name); }; diff --git a/world/zonelist.cpp b/world/zonelist.cpp index 400c07854..a949a8bd5 100644 --- a/world/zonelist.cpp +++ b/world/zonelist.cpp @@ -270,14 +270,39 @@ bool ZSList::IsZoneLocked(uint16 iZoneID) { } void ZSList::ListLockedZones(const char* to, WorldTCPConnection* connection) { - int x = 0; - for (auto &zone : pLockedZones) { - if (zone) { - connection->SendEmoteMessageRaw(to, 0, 0, 0, ZoneName(zone, true)); - x++; + int zone_count = 0; + for (const auto& zone_id : pLockedZones) { + if (zone_id) { + int zone_number = (zone_count + 1); + connection->SendEmoteMessageRaw( + to, + 0, + EQ::constants::AccountStatus::Player, + Chat::White, + fmt::format( + "Zone {} | Name: {} ({}) ID: {}", + zone_number, + ZoneLongName(zone_id), + ZoneName(zone_id), + zone_id + ).c_str() + ); + zone_count++; } } - connection->SendEmoteMessage(to, 0, 0, 0, "%i zones locked.", x); + + std::string zone_message = ( + zone_count ? + fmt::format("{} Zones are locked.", zone_count) : + "There are no zones locked." + ); + connection->SendEmoteMessage( + to, + 0, + EQ::constants::AccountStatus::Player, + Chat::White, + zone_message.c_str() + ); } void ZSList::SendZoneStatus(const char* to, int16 admin, WorldTCPConnection* connection) { diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 6403b2540..7f11dc8b3 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -1022,22 +1022,44 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { LogInfo("Wrong size on ServerOP_LockZone. Got: [{}], Expected: [{}]", pack->size, sizeof(ServerLockZone_Struct)); break; } - ServerLockZone_Struct* s = (ServerLockZone_Struct*)pack->pBuffer; - switch (s->op) { - case 0: - zoneserver_list.ListLockedZones(s->adminname, this); + + ServerLockZone_Struct* lock_zone = (ServerLockZone_Struct*) pack->pBuffer; + if (lock_zone->op == EQ::constants::ServerLockType::List) { + zoneserver_list.ListLockedZones(lock_zone->adminname, this); break; - case 1: - if (zoneserver_list.SetLockedZone(s->zoneID, true)) - zoneserver_list.SendEmoteMessage(0, 0, 80, 15, "Zone locked: %s", ZoneName(s->zoneID)); - else - this->SendEmoteMessageRaw(s->adminname, 0, 0, 0, "Failed to change lock"); - break; - case 2: - if (zoneserver_list.SetLockedZone(s->zoneID, false)) - zoneserver_list.SendEmoteMessage(0, 0, 80, 15, "Zone unlocked: %s", ZoneName(s->zoneID)); - else - this->SendEmoteMessageRaw(s->adminname, 0, 0, 0, "Failed to change lock"); + } else if ( + lock_zone->op == EQ::constants::ServerLockType::Lock || + lock_zone->op == EQ::constants::ServerLockType::Unlock + ) { + if (zoneserver_list.SetLockedZone(lock_zone->zoneID, lock_zone->op == EQ::constants::ServerLockType::Lock)) { + zoneserver_list.SendEmoteMessage( + 0, + 0, + EQ::constants::AccountStatus::QuestTroupe, + Chat::White, + fmt::format( + "Zone {} | Name: {} ({}) ID: {}", + lock_zone->op == EQ::constants::ServerLockType::Lock ? "Locked" : "Unlocked", + ZoneLongName(lock_zone->zoneID), + ZoneName(lock_zone->zoneID), + lock_zone->zoneID + ).c_str() + ); + } else { + SendEmoteMessageRaw( + lock_zone->adminname, + 0, + EQ::constants::AccountStatus::Player, + Chat::White, + fmt::format( + "Zone Failed to {} | Name: {} ({}) ID: {}", + lock_zone->op == EQ::constants::ServerLockType::Lock ? "Lock" : "Unlock", + ZoneLongName(lock_zone->zoneID), + ZoneName(lock_zone->zoneID), + lock_zone->zoneID + ).c_str() + ); + } break; } break; diff --git a/world/zoneserver.h b/world/zoneserver.h index 8bb1a6da8..98f60dcdb 100644 --- a/world/zoneserver.h +++ b/world/zoneserver.h @@ -22,6 +22,7 @@ #include "../common/net/servertalk_server.h" #include "../common/event/timer.h" #include "../common/timer.h" +#include "../common/emu_constants.h" #include "console.h" #include #include @@ -29,7 +30,6 @@ class Client; class ServerPacket; - class ZoneServer : public WorldTCPConnection { public: ZoneServer(std::shared_ptr connection, EQ::Net::ConsoleServer *console); diff --git a/zone/command.cpp b/zone/command.cpp index c7f6b3b3a..16ee02d55 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -455,7 +455,7 @@ int command_init(void) command_add("zone", "[zonename] [x] [y] [z] - Go to specified zone (coords optional)", 50, command_zone) || command_add("zonebootup", "[ZoneServerID] [shortname] - Make a zone server boot a specific zone", 150, command_zonebootup) || command_add("zoneinstance", "[instanceid] [x] [y] [z] - Go to specified instance zone (coords optional)", 50, command_zone_instance) || - command_add("zonelock", "[list/lock/unlock] - Set/query lock flag for zoneservers", 100, command_zonelock) || + command_add("zonelock", "[List|Lock|Unlock] [Zone ID|Zone Short Name] - Set or get lock status of a Zone by ID or Short Name", 100, command_zonelock) || command_add("zoneshutdown", "[shortname] - Shut down a zone server", 150, command_zoneshutdown) || command_add("zonespawn", "- Not implemented", 250, command_zonespawn) || command_add("zonestatus", "- Show connected zoneservers, synonymous with /servers", 150, command_zonestatus) || @@ -5757,40 +5757,56 @@ void command_equipitem(Client *c, const Seperator *sep) void command_zonelock(Client *c, const Seperator *sep) { + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #zonelock list - Lists Locked Zones"); + if (c->Admin() >= commandLockZones) { + c->Message(Chat::White, "Usage: #zonelock lock [Zone ID] or #zonelock lock [Zone Short Name] - Locks a Zone by ID or Short Name"); + c->Message(Chat::White, "Usage: #zonelock unlock [Zone ID] or #zonelock unlock [Zone Short Name] - Unlocks a Zone by ID or Short Name"); + } + return; + } + + std::string lock_type = str_tolower(sep->arg[1]); + bool is_list = lock_type.find("list") != std::string::npos; + bool is_lock = lock_type.find("lock") != std::string::npos; + bool is_unlock = lock_type.find("unlock") != std::string::npos; + if (!is_list && !is_lock && !is_unlock) { + c->Message(Chat::White, "Usage: #zonelock list - Lists Locked Zones"); + if (c->Admin() >= commandLockZones) { + c->Message(Chat::White, "Usage: #zonelock lock [Zone ID] or #zonelock lock [Zone Short Name] - Locks a Zone by ID or Short Name"); + c->Message(Chat::White, "Usage: #zonelock unlock [Zone ID] or #zonelock unlock [Zone Short Name] - Unlocks a Zone by ID or Short Name"); + } + return; + } + auto pack = new ServerPacket(ServerOP_LockZone, sizeof(ServerLockZone_Struct)); - ServerLockZone_Struct* s = (ServerLockZone_Struct*) pack->pBuffer; - strn0cpy(s->adminname, c->GetName(), sizeof(s->adminname)); - if (strcasecmp(sep->arg[1], "list") == 0) { - s->op = 0; + ServerLockZone_Struct* lock_zone = (ServerLockZone_Struct*) pack->pBuffer; + strn0cpy(lock_zone->adminname, c->GetName(), sizeof(lock_zone->adminname)); + + if (is_list) { + lock_zone->op = EQ::constants::ServerLockType::List; worldserver.SendPacket(pack); - } - else if (strcasecmp(sep->arg[1], "lock") == 0 && c->Admin() >= commandLockZones) { - uint16 tmp = ZoneID(sep->arg[2]); - if (tmp) { - s->op = 1; - s->zoneID = tmp; + } else if (!is_list && c->Admin() >= commandLockZones) { + auto zone_id = ( + sep->IsNumber(2) ? + static_cast(std::stoul(sep->arg[2])) : + static_cast(ZoneID(sep->arg[2])) + ); + std::string zone_short_name = str_tolower(ZoneName(zone_id, true)); + bool is_unknown_zone = zone_short_name.find("unknown") != std::string::npos; + if (zone_id && !is_unknown_zone) { + lock_zone->op = is_lock ? EQ::constants::ServerLockType::Lock : EQ::constants::ServerLockType::Unlock; + lock_zone->zoneID = zone_id; worldserver.SendPacket(pack); - } - else - c->Message(Chat::White, "Usage: #zonelock lock [zonename]"); - } - else if (strcasecmp(sep->arg[1], "unlock") == 0 && c->Admin() >= commandLockZones) { - uint16 tmp = ZoneID(sep->arg[2]); - if (tmp) { - s->op = 2; - s->zoneID = tmp; - worldserver.SendPacket(pack); - } - else - c->Message(Chat::White, "Usage: #zonelock unlock [zonename]"); - } - else { - c->Message(Chat::White, "#zonelock sub-commands"); - c->Message(Chat::White, " list"); - if(c->Admin() >= commandLockZones) - { - c->Message(Chat::White, " lock [zonename]"); - c->Message(Chat::White, " unlock [zonename]"); + } else { + c->Message( + Chat::White, + fmt::format( + "Usage: #zonelock {} [Zone ID] or #zonelock {} [Zone Short Name]", + is_lock ? "lock" : "unlock" + ).c_str() + ); } } safe_delete(pack); From ddcb1841835d2d244df73db5c1aee415e7fd7c36 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 14 Nov 2021 14:05:44 -0500 Subject: [PATCH 400/624] [Commands] Cleanup #stun Command. (#1749) * [Commands] Cleanup #stun Command. - Cleanup message. - Add ConvertSecondsToTime() to string_util.h and convert Quest API Methods to use helper. * Add days to ConvertSecondsToTime() and cleanup logic. * Cleanup. * Typo. * Cleanup. * Cleanup. --- common/string_util.cpp | 144 +++++++++++++++++++++++++++++++++++++++++ common/string_util.h | 1 + zone/command.cpp | 69 ++++++++++++++++---- zone/questmgr.cpp | 27 +------- 4 files changed, 201 insertions(+), 40 deletions(-) diff --git a/common/string_util.cpp b/common/string_util.cpp index f0a42b91d..a770ce202 100644 --- a/common/string_util.cpp +++ b/common/string_util.cpp @@ -15,6 +15,7 @@ */ #include "string_util.h" +#include #include #include @@ -1019,3 +1020,146 @@ std::vector GetBadWords() "zoophilia" }; } + +std::string ConvertSecondsToTime(int duration) +{ + int timer_length = duration; + int days = int(timer_length / 86400000); + timer_length %= 86400000; + int hours = int(timer_length / 3600); + timer_length %= 3600; + int minutes = int(timer_length / 60); + timer_length %= 60; + int seconds = timer_length; + std::string time_string = "Unknown"; + std::string day_string = (days == 1 ? "Day" : "Days"); + std::string hour_string = (hours == 1 ? "Hour" : "Hours"); + std::string minute_string = (minutes == 1 ? "Minute" : "Minutes"); + std::string second_string = (seconds == 1 ? "Second" : "Seconds"); + if (days && hours && minutes && seconds) { // DHMS + time_string = fmt::format( + "{} {}, {} {}, {} {}, and {} {}", + days, + day_string, + hours, + hour_string, + minutes, + minute_string, + seconds, + second_string + ); + } else if (days && hours && minutes && !seconds) { // DHM + time_string = fmt::format( + "{} {}, {} {}, and {} {}", + days, + day_string, + hours, + hour_string, + minutes, + minute_string + ); + } else if (days && hours && !minutes && seconds) { // DHS + time_string = fmt::format( + "{} {}, {} {}, and {} {}", + days, + day_string, + hours, + hour_string, + seconds, + second_string + ); + } else if (days && hours && !minutes && !seconds) { // DH + time_string = fmt::format( + "{} {} and {} {}", + days, + day_string, + hours, + hour_string + ); + } else if (days && !hours && minutes && seconds) { // DMS + time_string = fmt::format( + "{} {}, {} {}, and {} {}", + days, + day_string, + minutes, + minute_string, + seconds, + second_string + ); + } else if (days && !hours && minutes && !seconds) { // DM + time_string = fmt::format( + "{} {} and {} {}", + days, + day_string, + minutes, + minute_string + ); + } else if (days && !hours && !minutes && seconds) { // DS + time_string = fmt::format( + "{} {} and {} {}", + days, + day_string, + seconds, + second_string + ); + } else if (days && !hours && !minutes && !seconds) { // D + time_string = fmt::format( + "{} {}", + days, + day_string + ); + } else if (!days && hours && minutes && seconds) { // HMS + time_string = fmt::format( + "{} {}, {} {}, and {} {}", + hours, + hour_string, + minutes, + minute_string, + seconds, + second_string + ); + } else if (!days && hours && minutes && !seconds) { // HM + time_string = fmt::format( + "{} {} and {} {}", + hours, + hour_string, + minutes, + minute_string + ); + } else if (!days && hours && !minutes && seconds) { // HS + time_string = fmt::format( + "{} {} and {} {}", + hours, + hour_string, + seconds, + second_string + ); + } else if (!days && hours && !minutes && !seconds) { // H + time_string = fmt::format( + "{} {}", + hours, + hour_string + ); + } else if (!days && !hours && minutes && seconds) { // MS + time_string = fmt::format( + "{} {} and {} {}", + minutes, + minute_string, + seconds, + second_string + ); + } else if (!days && !hours && minutes && !seconds) { // M + time_string = fmt::format( + "{} {}", + minutes, + minute_string + ); + } else if (!days && !hours && !minutes && seconds) { // S + time_string = fmt::format( + "{} {}", + seconds, + second_string + ); + } + return time_string; +} diff --git a/common/string_util.h b/common/string_util.h index 0353c5c98..07b5ef5d0 100644 --- a/common/string_util.h +++ b/common/string_util.h @@ -45,6 +45,7 @@ std::vector wrap(std::vector &src, std::string charact std::string implode(std::string glue, std::vector src); std::string convert2digit(int n, std::string suffix); std::string numberToWords(unsigned long long int n); +std::string ConvertSecondsToTime(int duration); // For converstion of numerics into English // Used for grid nodes, as NPC names remove numerals. diff --git a/zone/command.cpp b/zone/command.cpp index 16ee02d55..066062d2a 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -9291,21 +9291,62 @@ void command_setcrystals(Client *c, const Seperator *sep) void command_stun(Client *c, const Seperator *sep) { - Mob *t=c->CastToMob(); - uint32 duration; - - if(sep->arg[1][0]) - { - duration = atoi(sep->arg[1]); - if(c->GetTarget()) - t=c->GetTarget(); - if(t->IsClient()) - t->CastToClient()->Stun(duration); - else - t->CastToNPC()->Stun(duration); + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #stun [Duration]"); + return; } - else - c->Message(Chat::White, "Usage: #stun [duration]"); + + Mob* target = c; + int duration = static_cast(std::min(std::stoll(sep->arg[1]), (long long) 2000000000)); + + if (duration < 0) { + duration = 0; + } + + if (c->GetTarget()) { + target = c->GetTarget(); + if (target->IsClient()) { + target->CastToClient()->Stun(duration); + } else if (target->IsNPC()) { + target->CastToNPC()->Stun(duration); + } + } else { + c->Stun(duration); + } + + std::string stun_message = ( + duration ? + fmt::format( + "You stunned {} for {}.", + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), + ConvertSecondsToTime(duration) + ) : + fmt::format( + "You unstunned {}.", + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ) + ) + ); + c->Message( + Chat::White, + stun_message.c_str() + ); } diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index b7a8976fb..18431e47b 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -3353,32 +3353,7 @@ EQ::ItemInstance *QuestManager::CreateItem(uint32 item_id, int16 charges, uint32 } std::string QuestManager::secondstotime(int duration) { - int timer_length = duration; - int hours = int(timer_length / 3600); - timer_length %= 3600; - int minutes = int(timer_length / 60); - timer_length %= 60; - int seconds = timer_length; - std::string time_string = "Unknown"; - std::string hour_string = (hours == 1 ? "Hour" : "Hours"); - std::string minute_string = (minutes == 1 ? "Minute" : "Minutes"); - std::string second_string = (seconds == 1 ? "Second" : "Seconds"); - if (hours > 0 && minutes > 0 && seconds > 0) { - time_string = fmt::format("{} {}, {} {}, and {} {}", hours, hour_string, minutes, minute_string, seconds, second_string); - } else if (hours > 0 && minutes > 0 && seconds == 0) { - time_string = fmt::format("{} {} and {} {}", hours, hour_string, minutes, minute_string); - } else if (hours > 0 && minutes == 0 && seconds > 0) { - time_string = fmt::format("{} {} and {} {}", hours, hour_string, seconds, second_string); - } else if (hours > 0 && minutes == 0 && seconds == 0) { - time_string = fmt::format("{} {}", hours, hour_string); - } else if (hours == 0 && minutes > 0 && seconds > 0) { - time_string = fmt::format("{} {} and {} {}", minutes, minute_string, seconds, second_string); - } else if (hours == 0 && minutes > 0 && seconds == 0) { - time_string = fmt::format("{} {}", minutes, minute_string); - } else if (hours == 0 && minutes == 0 && seconds > 0) { - time_string = fmt::format("{} {}", seconds, second_string); - } - return time_string; + return ConvertSecondsToTime(duration); } std::string QuestManager::gethexcolorcode(std::string color_name) { From 1103d5073314a69ebea8d5a268fa7529be4dc90c Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 14 Nov 2021 14:06:57 -0500 Subject: [PATCH 401/624] [Commands] Cleanup #setaapts Command. (#1750) * [Commands] Cleanup #setaapts Command. - Cleanup message and logic. - Increase cap from 5,000 to 2,000,000,000. * Update command.cpp * Update command.cpp * Cleanup. * Cleanup. --- zone/command.cpp | 77 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 25 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 066062d2a..ae4aeac53 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -377,7 +377,7 @@ int command_init(void) command_add("sensetrap", "Analog for ldon sense trap for the newer clients since we still don't have it working.", 0, command_sensetrap) || command_add("serverinfo", "- Get OS info about server host", 200, command_serverinfo) || command_add("serverrules", "- Read this server's rules", 0, command_serverrules) || - command_add("setaapts", "[value] - Set your or your player target's available AA points", 100, command_setaapts) || + command_add("setaapts", "[AA|Group|Raid] [AA Amount] - Set your or your player target's Available AA Points by Type", 100, command_setaapts) || command_add("setaaxp", "[value] - Set your or your player target's AA experience", 100, command_setaaxp) || command_add("setadventurepoints", "- Set your or your player target's available adventure points", 150, command_set_adventure_points) || command_add("setanim", "[animnum] - Set target's appearance to animnum", 200, command_setanim) || @@ -9237,31 +9237,58 @@ void command_setaaxp(Client *c, const Seperator *sep) void command_setaapts(Client *c, const Seperator *sep) { - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient()) - t=c->GetTarget()->CastToClient(); - - if(sep->arg[1][0] == '\0' || sep->arg[2][0] == '\0') - c->Message(Chat::White, "Usage: #setaapts "); - else if(atoi(sep->arg[2]) <= 0 || atoi(sep->arg[2]) > 5000) - c->Message(Chat::White, "You must have a number greater than 0 for points and no more than 5000."); - else if(!strcasecmp(sep->arg[1], "group")) { - t->GetPP().group_leadership_points = atoi(sep->arg[2]); - t->GetPP().group_leadership_exp = 0; - t->Message(Chat::Experience, "Setting Group AA points to %u", t->GetPP().group_leadership_points); - t->SendLeadershipEXPUpdate(); - } else if(!strcasecmp(sep->arg[1], "raid")) { - t->GetPP().raid_leadership_points = atoi(sep->arg[2]); - t->GetPP().raid_leadership_exp = 0; - t->Message(Chat::Experience, "Setting Raid AA points to %u", t->GetPP().raid_leadership_points); - t->SendLeadershipEXPUpdate(); - } else { - t->GetPP().aapoints = atoi(sep->arg[2]); - t->GetPP().expAA = 0; - t->Message(Chat::Experience, "Setting personal AA points to %u", t->GetPP().aapoints); - t->SendAlternateAdvancementStats(); + int arguments = sep->argnum; + if (arguments <= 1 || !sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #setaapts [AA|Group|Raid] [AA Amount]"); + return; } + + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + + std::string aa_type = str_tolower(sep->arg[1]); + std::string group_raid_string; + uint32 aa_points = static_cast(std::min(std::stoull(sep->arg[2]), (unsigned long long) 2000000000)); + bool is_aa = aa_type.find("aa") != std::string::npos; + bool is_group = aa_type.find("group") != std::string::npos; + bool is_raid = aa_type.find("raid") != std::string::npos; + if (!is_aa && !is_group && !is_raid) { + c->Message(Chat::White, "Usage: #setaapts [AA|Group|Raid] [AA Amount]"); + return; + } + + if (is_aa) { + target->GetPP().aapoints = aa_points; + target->GetPP().expAA = 0; + target->SendAlternateAdvancementStats(); + } else if (is_group || is_raid) { + if (is_group) { + group_raid_string = "Group "; + target->GetPP().group_leadership_points = aa_points; + target->GetPP().group_leadership_exp = 0; + } else if (is_raid) { + group_raid_string = "Raid "; + target->GetPP().raid_leadership_points = aa_points; + target->GetPP().raid_leadership_exp = 0; + } + target->SendLeadershipEXPUpdate(); + } + + std::string aa_message = fmt::format( + "{} now {} {} {}AA Point{}.", + c == target ? "You" : target->GetCleanName(), + c == target ? "have" : "has", + aa_points, + group_raid_string, + aa_points != 1 ? "s" : "" + + ); + c->Message( + Chat::White, + aa_message.c_str() + ); } void command_setcrystals(Client *c, const Seperator *sep) From 4f550fcbf3da51fb463f08777978149f3c8598e5 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 14 Nov 2021 14:09:10 -0500 Subject: [PATCH 402/624] [Commands] Cleanup #loc Command. (#1752) - Cleanup message and logic. --- zone/command.cpp | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index ae4aeac53..fbb790307 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -7308,9 +7308,32 @@ void command_spawnfix(Client *c, const Seperator *sep) { void command_loc(Client *c, const Seperator *sep) { - Mob *t=c->GetTarget()?c->GetTarget():c->CastToMob(); + Mob *target = c; + if (c->GetTarget()) { + target = c->GetTarget(); + } - c->Message(Chat::White, "%s's Location (XYZ): %1.2f, %1.2f, %1.2f; heading=%1.1f", t->GetName(), t->GetX(), t->GetY(), t->GetZ(), t->GetHeading()); + auto target_position = target->GetPosition(); + + c->Message( + Chat::White, + fmt::format( + "{} Location | XYZ: {:.2f}, {:.2f}, {:.2f} Heading: {:.2f}", + ( + c == target ? + "Your" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), + target_position.x, + target_position.y, + target_position.z, + target_position.w + ).c_str() + ); } void command_goto(Client *c, const Seperator *sep) From c44b82500d308c6cb7244c2523c9ceac543ab1d0 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 14 Nov 2021 14:47:17 -0500 Subject: [PATCH 403/624] [Commands] Cleanup #setaaxp Command. (#1751) * [Commands] Cleanup #setaaxp Command. - Add message. - Cleanup logic. * Update command.cpp * Cleanup. --- zone/command.cpp | 63 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 11 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index fbb790307..e41ca1fe8 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -378,7 +378,7 @@ int command_init(void) command_add("serverinfo", "- Get OS info about server host", 200, command_serverinfo) || command_add("serverrules", "- Read this server's rules", 0, command_serverrules) || command_add("setaapts", "[AA|Group|Raid] [AA Amount] - Set your or your player target's Available AA Points by Type", 100, command_setaapts) || - command_add("setaaxp", "[value] - Set your or your player target's AA experience", 100, command_setaaxp) || + command_add("setaaxp", "[AA|Group|Raid] [AA Experience] - Set your or your player target's AA Experience by Type", 100, command_setaaxp) || command_add("setadventurepoints", "- Set your or your player target's available adventure points", 150, command_set_adventure_points) || command_add("setanim", "[animnum] - Set target's appearance to animnum", 200, command_setanim) || command_add("setcrystals", "[value] - Set your or your player target's available radiant or ebon crystals", 100, command_setcrystals) || @@ -9244,18 +9244,59 @@ void command_itemsearch(Client *c, const Seperator *sep) void command_setaaxp(Client *c, const Seperator *sep) { - Client *t=c; + int arguments = sep->argnum; + if (arguments <= 1 || !sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #setaaxp [AA|Group|Raid] [AA Experience]"); + return; + } - if(c->GetTarget() && c->GetTarget()->IsClient()) - t=c->GetTarget()->CastToClient(); + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } - if (sep->IsNumber(1)) { - t->SetEXP(t->GetEXP(), atoi(sep->arg[1]), false); - if(sep->IsNumber(2) && sep->IsNumber(3)) { - t->SetLeadershipEXP(atoi(sep->arg[2]), atoi(sep->arg[3])); - } - } else - c->Message(Chat::White, "Usage: #setaaxp ( )"); + std::string aa_type = str_tolower(sep->arg[1]); + std::string group_raid_string; + uint32 aa_experience = static_cast(std::min(std::stoull(sep->arg[2]), (unsigned long long) 2000000000)); + bool is_aa = aa_type.find("aa") != std::string::npos; + bool is_group = aa_type.find("group") != std::string::npos; + bool is_raid = aa_type.find("raid") != std::string::npos; + if (!is_aa && !is_group && !is_raid) { + c->Message(Chat::White, "Usage: #setaaxp [AA|Group|Raid] [AA Experience]"); + return; + } + + if (is_aa) { + target->SetEXP( + target->GetEXP(), + aa_experience, + false + ); + } else if (is_group) { + group_raid_string = "Group "; + target->SetLeadershipEXP( + aa_experience, + target->GetRaidEXP() + ); + } else if (is_raid) { + group_raid_string = "Raid "; + target->SetLeadershipEXP( + target->GetGroupEXP(), + aa_experience + ); + } + + std::string aa_exp_message = fmt::format( + "{} now {} {} {}AA Experience.", + c == target ? "You" : target->GetCleanName(), + c == target ? "have" : "has", + aa_experience, + group_raid_string + ); + c->Message( + Chat::White, + aa_exp_message.c_str() + ); } void command_setaapts(Client *c, const Seperator *sep) From f5d37a9959a0b65859535c724557639b76c8c8d7 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 14 Nov 2021 14:47:25 -0500 Subject: [PATCH 404/624] [Commands] Cleanup #setpvppoints Command. (#1755) * [Commands] Cleanup #setpvppoints Command. - Cleanup message and logic. * Cleanup. --- zone/command.cpp | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index e41ca1fe8..b0a25eeb7 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -387,7 +387,7 @@ int command_init(void) command_add("setlanguage", "[language ID] [value] - Set your target's language skillnum to value", 50, command_setlanguage) || command_add("setlsinfo", "[email] [password] - Set login server email address and password (if supported by login server)", 10, command_setlsinfo) || command_add("setpass", "[accountname] [password] - Set local password for accountname", 150, command_setpass) || - command_add("setpvppoints", "[value] - Set your or your player target's PVP points", 100, command_setpvppoints) || + command_add("setpvppoints", "[Amount] - Set your or your player target's PVP points", 100, command_setpvppoints) || command_add("setskill", "[skillnum] [value] - Set your target's skill skillnum to value", 50, command_setskill) || command_add("setskillall", "[value] - Set all of your target's skills to value", 50, command_setskillall) || command_add("setstartzone", "[zoneid] - Set target's starting zone. Set to zero to allow the player to use /setstartcity", 80, command_setstartzone) || @@ -7007,23 +7007,32 @@ void command_setxp(Client *c, const Seperator *sep) void command_setpvppoints(Client *c, const Seperator *sep) { - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient()) - t=c->GetTarget()->CastToClient(); - - if (sep->IsNumber(1)) { - if (atoi(sep->arg[1]) > 9999999) - c->Message(Chat::White, "Error: Value too high."); - else - { - t->SetPVPPoints(atoi(sep->arg[1])); - t->Save(); - t->SendPVPStats(); - } + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Command Syntax: #setpvppoints [Amount]"); + return; } - else - c->Message(Chat::White, "Usage: #setpvppoints number"); + + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + + uint32 pvp_points = static_cast(std::min(std::stoull(sep->arg[1]), (unsigned long long) 2000000000)); + target->SetPVPPoints(pvp_points); + target->Save(); + target->SendPVPStats(); + std::string pvp_message = fmt::format( + "{} now {} {} PVP Point{}.", + c == target ? "You" : target->GetCleanName(), + c == target ? "have" : "has", + pvp_points, + pvp_points != 1 ? "s" : "" + ); + c->Message( + Chat::White, + pvp_message.c_str() + ); } void command_name(Client *c, const Seperator *sep) From 9bcb617f906ddf1d76bbea82844d3507e76413dd Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 14 Nov 2021 15:59:03 -0600 Subject: [PATCH 405/624] [Hotfix] Line ending changes --- utils/sql/git/optional/2021_11_13_loginserver_hostname.sql | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/sql/git/optional/2021_11_13_loginserver_hostname.sql b/utils/sql/git/optional/2021_11_13_loginserver_hostname.sql index 521b6f853..f61d04964 100644 --- a/utils/sql/git/optional/2021_11_13_loginserver_hostname.sql +++ b/utils/sql/git/optional/2021_11_13_loginserver_hostname.sql @@ -1,3 +1,3 @@ -ALTER TABLE login_accounts MODIFY COLUMN last_ip_address VARCHAR(30) NOT NULL; -ALTER TABLE login_server_admins MODIFY COLUMN registration_ip_address VARCHAR(30) NOT NULL; -ALTER TABLE login_world_servers MODIFY COLUMN last_ip_address VARCHAR(30) DEFAULT NULL; +ALTER TABLE login_accounts MODIFY COLUMN last_ip_address VARCHAR(30) NOT NULL; +ALTER TABLE login_server_admins MODIFY COLUMN registration_ip_address VARCHAR(30) NOT NULL; +ALTER TABLE login_world_servers MODIFY COLUMN last_ip_address VARCHAR(30) DEFAULT NULL; From 264c6cb0195b79c422971751ce1332971375fd79 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 14 Nov 2021 20:47:36 -0500 Subject: [PATCH 406/624] [Commands] Cleanup #setcrystals Command. (#1745) * [Commands] Cleanup #setcrystals Command. - Add message. - Increase cap from 100,000 to 2,000,000,000. * Cleanup. * Remove condition, ID is always defined. * Cleanup. --- zone/command.cpp | 56 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index b0a25eeb7..6e6cb5fea 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -9366,27 +9366,49 @@ void command_setaapts(Client *c, const Seperator *sep) void command_setcrystals(Client *c, const Seperator *sep) { - Client *t=c; + int arguments = sep->argnum; + if (arguments <= 1 || !sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #setcrystals [Ebon|Radiant] [Crystal Amount]"); + return; + } - if(c->GetTarget() && c->GetTarget()->IsClient()) - t=c->GetTarget()->CastToClient(); + Client *target = c; + if(c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } - if(sep->arg[1][0] == '\0' || sep->arg[2][0] == '\0') - c->Message(Chat::White, "Usage: #setcrystals "); - else if(atoi(sep->arg[2]) <= 0 || atoi(sep->arg[2]) > 100000) - c->Message(Chat::White, "You must have a number greater than 0 for crystals and no more than 100000."); - else if(!strcasecmp(sep->arg[1], "radiant")) - { - t->SetRadiantCrystals(atoi(sep->arg[2])); + std::string crystal_type = str_tolower(sep->arg[1]); + uint32 crystal_amount = static_cast(std::min(std::stoull(sep->arg[2]), (unsigned long long) 2000000000)); + bool is_ebon = crystal_type.find("ebon") != std::string::npos; + bool is_radiant = crystal_type.find("radiant") != std::string::npos; + if (!is_ebon && !is_radiant) { + c->Message(Chat::White, "Usage: #setcrystals [Ebon|Radiant] [Crystal Amount]"); + return; } - else if(!strcasecmp(sep->arg[1], "ebon")) - { - t->SetEbonCrystals(atoi(sep->arg[2])); - } - else - { - c->Message(Chat::White, "Usage: #setcrystals "); + + uint32 crystal_item_id = ( + is_ebon ? + RuleI(Zone, EbonCrystalItemID) : + RuleI(Zone, RadiantCrystalItemID) + ); + + auto crystal_link = database.CreateItemLink(crystal_item_id); + if (is_radiant) { + target->SetRadiantCrystals(crystal_amount); + } else { + target->SetEbonCrystals(crystal_amount); } + + c->Message( + Chat::White, + fmt::format( + "{} now {} {} {}.", + c == target ? "You" : target->GetCleanName(), + c == target ? "have" : "has", + crystal_amount, + crystal_link + ).c_str() + ); } void command_stun(Client *c, const Seperator *sep) From b2c86f55714a0ef1b6a7452403b9a9d17645d329 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 14 Nov 2021 20:50:51 -0500 Subject: [PATCH 407/624] [Commands] Cleanup #raidloot Command. (#1757) - Cleanup message and logic. - Add RaidLootTypes enum and map for names. --- zone/command.cpp | 100 +++++++++++++++++++++++++---------------------- zone/zonedb.h | 9 +++++ 2 files changed, 62 insertions(+), 47 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 6e6cb5fea..46454d39a 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -346,7 +346,7 @@ int command_init(void) command_add("qglobal", "[on/off/view] - Toggles qglobal functionality on an NPC", 100, command_qglobal) || command_add("questerrors", "Shows quest errors.", 100, command_questerrors) || command_add("race", "[racenum] - Change your or your target's race. Use racenum 0 to return to normal", 50, command_race) || - command_add("raidloot", "LEADER|GROUPLEADER|SELECTED|ALL - Sets your raid loot settings if you have permission to do so.", 0, command_raidloot) || + command_add("raidloot", "[All|GroupLeader|RaidLeader|Selected] - Sets your Raid Loot Type if you have permission to do so.", 0, command_raidloot) || command_add("randomfeatures", "- Temporarily randomizes the Facial Features of your target", 80, command_randomfeatures) || command_add("refreshgroup", "- Refreshes Group.", 0, command_refreshgroup) || command_add("reloadaa", "Reloads AA data", 200, command_reloadaa) || @@ -13783,58 +13783,64 @@ void command_showspellslist(Client *c, const Seperator *sep) void command_raidloot(Client *c, const Seperator *sep) { - if(!sep->arg[1][0]) { - c->Message(Chat::White, "Usage: #raidloot [LEADER/GROUPLEADER/SELECTED/ALL]"); + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #raidloot [All|GroupLeader|RaidLeader|Selected]"); return; } - Raid *r = c->GetRaid(); - if(r) - { - for(int x = 0; x < 72; ++x) - { - if(r->members[x].member == c) - { - if(r->members[x].IsRaidLeader == 0) - { - c->Message(Chat::White, "You must be the raid leader to use this command."); - } - else - { - break; - } - } - } + auto client_raid = c->GetRaid(); + if (!client_raid) { + c->Message(Chat::White, "You must be in a Raid to use this command."); + return; + } - if(strcasecmp(sep->arg[1], "LEADER") == 0) - { - c->Message(Chat::Yellow, "Loot type changed to: 1"); - r->ChangeLootType(1); - } - else if(strcasecmp(sep->arg[1], "GROUPLEADER") == 0) - { - c->Message(Chat::Yellow, "Loot type changed to: 2"); - r->ChangeLootType(2); - } - else if(strcasecmp(sep->arg[1], "SELECTED") == 0) - { - c->Message(Chat::Yellow, "Loot type changed to: 3"); - r->ChangeLootType(3); - } - else if(strcasecmp(sep->arg[1], "ALL") == 0) - { - c->Message(Chat::Yellow, "Loot type changed to: 4"); - r->ChangeLootType(4); - } - else - { - c->Message(Chat::White, "Usage: #raidloot [LEADER/GROUPLEADER/SELECTED/ALL]"); - } + if (!client_raid->IsLeader(c)) { + c->Message(Chat::White, "You must be the Raid Leader to use this command."); + return; } - else - { - c->Message(Chat::White, "You must be in a raid to use that command."); + + std::string raid_loot_type = str_tolower(sep->arg[1]); + bool is_all = raid_loot_type.find("all") != std::string::npos; + bool is_group_leader = raid_loot_type.find("groupleader") != std::string::npos; + bool is_raid_leader = raid_loot_type.find("raidleader") != std::string::npos; + bool is_selected = raid_loot_type.find("selected") != std::string::npos; + if ( + !is_all && + !is_group_leader && + !is_raid_leader && + !is_selected + ) { + c->Message(Chat::White, "Usage: #raidloot [All|GroupLeader|RaidLeader|Selected]"); + return; } + + std::map loot_types = { + { RaidLootTypes::All, "All" }, + { RaidLootTypes::GroupLeader, "GroupLeader" }, + { RaidLootTypes::RaidLeader, "RaidLeader" }, + { RaidLootTypes::Selected, "Selected" } + }; + + uint32 loot_type; + if (is_all) { + loot_type = RaidLootTypes::All; + } else if (is_group_leader) { + loot_type = RaidLootTypes::GroupLeader; + } else if (is_raid_leader) { + loot_type = RaidLootTypes::RaidLeader; + } else if (is_selected) { + loot_type = RaidLootTypes::Selected; + } + + c->Message( + Chat::White, + fmt::format( + "Loot type changed to {} ({}).", + loot_types[loot_type], + loot_type + ).c_str() + ); } void command_emoteview(Client *c, const Seperator *sep) diff --git a/zone/zonedb.h b/zone/zonedb.h index 074f983b6..a72299c65 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -257,6 +257,15 @@ namespace BeastlordPetData { }; } +namespace RaidLootTypes { + enum : uint32 { + RaidLeader = 1, + GroupLeader, + Selected, + All + }; +} + class ZoneDatabase : public SharedDatabase { typedef std::list ItemList; public: From b3471c51df47fdd2b4bf73319f7e57163b112d5b Mon Sep 17 00:00:00 2001 From: JJ <3617814+joligario@users.noreply.github.com> Date: Sun, 14 Nov 2021 20:53:14 -0500 Subject: [PATCH 408/624] [Config] Delete extra versions (#1763) * [Config] Delete extra versions [CI SKIP] * Same --- README.md | 2 +- utils/defaults/eqemu_config.json | 8 --- utils/defaults/eqemu_config.json.full | 54 --------------- utils/defaults/eqemu_config.xml | 7 -- utils/defaults/eqemu_config.xml.full | 95 --------------------------- 5 files changed, 1 insertion(+), 165 deletions(-) delete mode 100755 utils/defaults/eqemu_config.json delete mode 100755 utils/defaults/eqemu_config.json.full delete mode 100644 utils/defaults/eqemu_config.xml delete mode 100644 utils/defaults/eqemu_config.xml.full diff --git a/README.md b/README.md index fea2c23a4..83d26b7b0 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ * You can use curl or wget to kick off the installer (whichever your OS has) > curl -O https://raw.githubusercontent.com/EQEmu/Server/master/utils/scripts/linux_installer/install.sh install.sh && chmod 755 install.sh && ./install.sh -> wget --no-check-certificate https://raw.githubusercontent.com/EQEmu/Server/master/utils/scripts/linux_installer/install.sh -O install.sh && chmod 755 install.sh && ./install.sh +> wget --no-check-certificate https://raw.githubusercontent.com/EQEmu/Server/master/utils/scripts/linux_installer/install.sh -O install.sh && chmod 755 install.sh && ./install.sh ## Supported Clients diff --git a/utils/defaults/eqemu_config.json b/utils/defaults/eqemu_config.json deleted file mode 100755 index 73930ab9f..000000000 --- a/utils/defaults/eqemu_config.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "server": { - "world": { - "shortname": "setme", - "longname": "I Forgot To Edit My Config" - } - } -} diff --git a/utils/defaults/eqemu_config.json.full b/utils/defaults/eqemu_config.json.full deleted file mode 100755 index 49cb9a5c6..000000000 --- a/utils/defaults/eqemu_config.json.full +++ /dev/null @@ -1,54 +0,0 @@ -{ - "server": { - "zones": { - "defaultstatus": "20", - "ports": { - "low": "7000", - "high": "7100" - } - }, - "database": { - "password": "eq", - "db": "eq", - "host": "127.0.0.1", - "port": "3306", - "username": "eq" - }, - "world": { - "shortname": "setme", - "longname": "I Forgot To Edit My Config", - "loginserver": { - "password": "", - "host": "login.eqemulator.net", - "port": "5998", - "account": "" - }, - "tcp": { - "port": "9000", - "telnet": "disable", - "ip": "127.0.0.1" - }, - "key": "some long random string", - "http": { - "mimefile": "mime.types", - "port": "9080", - "enabled": "false" - } - }, - "mailserver": { - "host": "channels.eqemulator.net", - "port": "7778" - }, - "chatserver": { - "host": "channels.eqemulator.net", - "port": "7778" - }, - "qsdatabase": { - "host": "127.0.0.1", - "port": "3306", - "username": "eq", - "password": "eq", - "db": "eq" - } - } -} diff --git a/utils/defaults/eqemu_config.xml b/utils/defaults/eqemu_config.xml deleted file mode 100644 index 7357cdb60..000000000 --- a/utils/defaults/eqemu_config.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - setme - I Forgot To Edit My Config - - diff --git a/utils/defaults/eqemu_config.xml.full b/utils/defaults/eqemu_config.xml.full deleted file mode 100644 index 3195352c5..000000000 --- a/utils/defaults/eqemu_config.xml.full +++ /dev/null @@ -1,95 +0,0 @@ - - - - setme - I Forgot To Edit My Config - - - - - - - - login.eqemulator.net - 5998 - - - - - - - - - - - - - some long random string - - - - - - - - channels.eqemulator.net - 7778 - - - - - channels.eqemulator.net - 7778 - - - - 20 - - - - - - - - 127.0.0.1 - 3306 - eq - eq - eq - - - - 127.0.0.1 - 3306 - eq - eq - eq - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 76b0183a0fd910d335c5d596d8d0cf933d86675c Mon Sep 17 00:00:00 2001 From: JJ <3617814+joligario@users.noreply.github.com> Date: Sun, 14 Nov 2021 20:53:53 -0500 Subject: [PATCH 409/624] [Loginserver] Increase IP/hostname size a bit more (#1765) --- loginserver/login_util/login_schema.sql | 6 +++--- utils/sql/git/optional/2021_11_13_loginserver_hostname.sql | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/loginserver/login_util/login_schema.sql b/loginserver/login_util/login_schema.sql index f85f9262e..ad9805cf8 100644 --- a/loginserver/login_util/login_schema.sql +++ b/loginserver/login_util/login_schema.sql @@ -5,7 +5,7 @@ CREATE TABLE `login_accounts` ( `account_password` text NOT NULL, `account_email` varchar(100) NOT NULL, `source_loginserver` varchar(64) DEFAULT NULL, - `last_ip_address` varchar(30) NOT NULL, + `last_ip_address` varchar(80) NOT NULL, `last_login_date` datetime NOT NULL, `created_at` datetime DEFAULT NULL, `updated_at` datetime DEFAULT current_timestamp(), @@ -22,7 +22,7 @@ CREATE TABLE `login_server_admins` ( `last_name` varchar(50) NOT NULL, `email` varchar(100) NOT NULL, `registration_date` datetime NOT NULL, - `registration_ip_address` varchar(30) NOT NULL, + `registration_ip_address` varchar(80) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; @@ -45,7 +45,7 @@ CREATE TABLE `login_world_servers` ( `tag_description` varchar(50) NOT NULL DEFAULT '', `login_server_list_type_id` int(11) NOT NULL, `last_login_date` datetime DEFAULT NULL, - `last_ip_address` varchar(30) DEFAULT NULL, + `last_ip_address` varchar(80) DEFAULT NULL, `login_server_admin_id` int(11) NOT NULL, `is_server_trusted` int(11) NOT NULL, `note` varchar(255) DEFAULT NULL, diff --git a/utils/sql/git/optional/2021_11_13_loginserver_hostname.sql b/utils/sql/git/optional/2021_11_13_loginserver_hostname.sql index f61d04964..10ba57f68 100644 --- a/utils/sql/git/optional/2021_11_13_loginserver_hostname.sql +++ b/utils/sql/git/optional/2021_11_13_loginserver_hostname.sql @@ -1,3 +1,3 @@ -ALTER TABLE login_accounts MODIFY COLUMN last_ip_address VARCHAR(30) NOT NULL; -ALTER TABLE login_server_admins MODIFY COLUMN registration_ip_address VARCHAR(30) NOT NULL; -ALTER TABLE login_world_servers MODIFY COLUMN last_ip_address VARCHAR(30) DEFAULT NULL; +ALTER TABLE login_accounts MODIFY COLUMN last_ip_address VARCHAR(80) NOT NULL; +ALTER TABLE login_server_admins MODIFY COLUMN registration_ip_address VARCHAR(80) NOT NULL; +ALTER TABLE login_world_servers MODIFY COLUMN last_ip_address VARCHAR(80) DEFAULT NULL; From 6400e2f8bc0d63eadd9ae07bf7ac8446e491e127 Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Sun, 14 Nov 2021 19:54:45 -0600 Subject: [PATCH 410/624] [Rules] Add option rule to load AA based on CurrentExpansion rule (#1758) * Add option rule to load AA based on CurrentExpansion rule * Default UseCurrentExpansionAAOnly to true * Only clear the PlayerAA when reloadingAA --- common/ruletypes.h | 1 + zone/aa.cpp | 25 +++++++++++++++++++++---- zone/client.h | 2 ++ zone/entity.cpp | 1 + 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index c2b91c6b5..e1bf1a926 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -764,6 +764,7 @@ RULE_CATEGORY_END() RULE_CATEGORY(Expansion) RULE_INT(Expansion, CurrentExpansion, -1, "The current expansion enabled for the server [-1 = ALL, 0 = Classic, 1 = Kunark etc.]") +RULE_BOOL(Expansion, UseCurrentExpansionAAOnly, true, "When true will only load AA ranks that match CurrentExpansion rule") RULE_CATEGORY_END() RULE_CATEGORY(Instances) diff --git a/zone/aa.cpp b/zone/aa.cpp index 4945b2a94..f9ea7b0fd 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -499,9 +499,19 @@ void Client::ResetAA() { void Client::SendClearAA() { - auto outapp = new EQApplicationPacket(OP_ClearLeadershipAbilities, 0); + SendClearLeadershipAA(); + SendClearPlayerAA(); +} + +void Client::SendClearPlayerAA() +{ + auto outapp = new EQApplicationPacket(OP_ClearAA, 0); FastQueuePacket(&outapp); - outapp = new EQApplicationPacket(OP_ClearAA, 0); +} + +void Client::SendClearLeadershipAA() +{ + auto outapp = new EQApplicationPacket(OP_ClearLeadershipAbilities, 0); FastQueuePacket(&outapp); } @@ -1700,11 +1710,18 @@ bool ZoneDatabase::LoadAlternateAdvancementAbilities(std::unordered_map= 0) { + query = fmt::format("SELECT id, upper_hotkey_sid, lower_hotkey_sid, title_sid, desc_sid, cost, level_req, spell, spell_type, recast_time, " + "next_id, expansion FROM aa_ranks WHERE expansion <= {}", expansion); + } else { + query = "SELECT id, upper_hotkey_sid, lower_hotkey_sid, title_sid, desc_sid, cost, level_req, spell, spell_type, recast_time, " "next_id, expansion FROM aa_ranks"; + } results = QueryDatabase(query); if(results.Success()) { for(auto row = results.begin(); row != results.end(); ++row) { diff --git a/zone/client.h b/zone/client.h index cbdd41084..6d913a82b 100644 --- a/zone/client.h +++ b/zone/client.h @@ -897,6 +897,8 @@ public: void ResetAA(); void RefundAA(); void SendClearAA(); + void SendClearLeadershipAA(); + void SendClearPlayerAA(); inline uint32 GetAAXP() const { return m_pp.expAA; } inline uint32 GetAAPercent() const { return m_epp.perAA; } int32 CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id); diff --git a/zone/entity.cpp b/zone/entity.cpp index 8d182fff5..08ebe2831 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -5331,6 +5331,7 @@ void EntityList::StopMobAI() void EntityList::SendAlternateAdvancementStats() { for(auto &c : client_list) { + c.second->SendClearPlayerAA(); c.second->SendAlternateAdvancementTable(); c.second->SendAlternateAdvancementStats(); c.second->SendAlternateAdvancementPoints(); From 5d75b7b36521b442f0010c1915e7c318ce082a9a Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 14 Nov 2021 20:58:55 -0500 Subject: [PATCH 411/624] [Commands] Cleanup #advnpcspawn and #npcspawn Commands. (#1754) * [Commands] Cleanup #advnpcspawn and #npcspawn Commands. - Cleanup messages and logic. - Add enum for spawn types to remove magic numbers. - Cleanup messages that were improper/unused. * Cleanup. * Cleanup. * Cleanup. * Typo. * Update command.cpp --- zone/client.cpp | 62 ++-- zone/command.cpp | 816 +++++++++++++++++++++++++++++++---------------- zone/npc.cpp | 14 +- zone/zonedb.h | 12 + 4 files changed, 601 insertions(+), 303 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 893a2079e..1eadd3fcc 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -6228,36 +6228,44 @@ void Client::LocateCorpse() void Client::NPCSpawn(NPC *target_npc, const char *identifier, uint32 extra) { - if (!target_npc || !identifier) + if (!target_npc || !identifier) { return; - - std::string id = identifier; - for(int i = 0; i < id.length(); ++i) - { - id[i] = tolower(id[i]); } - if (id == "create") { - // extra tries to create the npc_type ID within the range for the current zone (zone_id * 1000) - content_db.NPCSpawnDB(0, zone->GetShortName(), zone->GetInstanceVersion(), this, target_npc->CastToNPC(), extra); - } - else if (id == "add") { - // extra sets the respawn timer for add - content_db.NPCSpawnDB(1, zone->GetShortName(), zone->GetInstanceVersion(), this, target_npc->CastToNPC(), extra); - } - else if (id == "update") { - content_db.NPCSpawnDB(2, zone->GetShortName(), zone->GetInstanceVersion(), this, target_npc->CastToNPC()); - } - else if (id == "remove") { - content_db.NPCSpawnDB(3, zone->GetShortName(), zone->GetInstanceVersion(), this, target_npc->CastToNPC()); - target_npc->Depop(false); - } - else if (id == "delete") { - content_db.NPCSpawnDB(4, zone->GetShortName(), zone->GetInstanceVersion(), this, target_npc->CastToNPC()); - target_npc->Depop(false); - } - else { - return; + std::string spawn_type = str_tolower(identifier); + bool is_add = spawn_type.find("add") != std::string::npos; + bool is_create = spawn_type.find("create") != std::string::npos; + bool is_delete = spawn_type.find("delete") != std::string::npos; + bool is_remove = spawn_type.find("remove") != std::string::npos; + bool is_update = spawn_type.find("update") != std::string::npos; + if (is_add || is_create) { + // Add: extra tries to create the NPC ID within the range for the current Zone (Zone ID * 1000) + // Create: extra sets the Respawn Timer for add + content_db.NPCSpawnDB( + is_add ? NPCSpawnTypes::AddNewSpawngroup : NPCSpawnTypes::CreateNewSpawn, + zone->GetShortName(), + zone->GetInstanceVersion(), + this, + target_npc->CastToNPC(), + extra + ); + } else if (is_delete || is_remove || is_update) { + uint8 spawn_update_type = ( + is_delete ? + NPCSpawnTypes::DeleteSpawn : + ( + is_remove ? + NPCSpawnTypes::RemoveSpawn : + NPCSpawnTypes::UpdateAppearance + ) + ); + content_db.NPCSpawnDB( + spawn_update_type, + zone->GetShortName(), + zone->GetInstanceVersion(), + this, + target_npc->CastToNPC() + ); } } diff --git a/zone/command.cpp b/zone/command.cpp index 46454d39a..0e8f204aa 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -7233,53 +7233,96 @@ void command_zonespawn(Client *c, const Seperator *sep) void command_npcspawn(Client *c, const Seperator *sep) { - Mob *target=c->GetTarget(); - uint32 extra = 0; - - if (target && target->IsNPC()) { - if (strcasecmp(sep->arg[1], "create") == 0) { - if (atoi(sep->arg[2])) - { - // Option to try to create the npc_type ID within the range for the current zone (zone_id * 1000) - extra = 1; - } - content_db.NPCSpawnDB(0, zone->GetShortName(), zone->GetInstanceVersion(), c, target->CastToNPC(), extra); - c->Message(Chat::White, "%s created successfully!", target->GetName()); - } - else if (strcasecmp(sep->arg[1], "add") == 0) { - if (atoi(sep->arg[2])) - { - extra = atoi(sep->arg[2]); - } - else - { - // Respawn Timer default if not set - extra = 1200; - } - content_db.NPCSpawnDB(1, zone->GetShortName(), zone->GetInstanceVersion(), c, target->CastToNPC(), extra); - c->Message(Chat::White, "%s added successfully!", target->GetName()); - } - else if (strcasecmp(sep->arg[1], "update") == 0) { - content_db.NPCSpawnDB(2, zone->GetShortName(), zone->GetInstanceVersion(), c, target->CastToNPC()); - c->Message(Chat::White, "%s updated!", target->GetName()); - } - else if (strcasecmp(sep->arg[1], "remove") == 0) { - content_db.NPCSpawnDB(3, zone->GetShortName(), zone->GetInstanceVersion(), c, target->CastToNPC()); - c->Message(Chat::White, "%s removed successfully from database!", target->GetName()); - target->Depop(false); - } - else if (strcasecmp(sep->arg[1], "delete") == 0) { - content_db.NPCSpawnDB(4, zone->GetShortName(), zone->GetInstanceVersion(), c, target->CastToNPC()); - c->Message(Chat::White, "%s deleted from database!", target->GetName()); - target->Depop(false); - } - else { - c->Message(Chat::White, "Error: #npcspawn: Invalid command."); - c->Message(Chat::White, "Usage: #npcspawn [create|add|update|remove|delete]"); - } + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Command Syntax: #npcspawn [Add|Create|Delete|Remove|Update]"); + return; + } + + if (!(c->GetTarget() && c->GetTarget()->IsNPC())) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; + } + + NPC* target = c->GetTarget()->CastToNPC(); + std::string spawn_type = str_tolower(sep->arg[1]); + uint32 extra = 0; + bool is_add = spawn_type.find("add") != std::string::npos; + bool is_create = spawn_type.find("create") != std::string::npos; + bool is_delete = spawn_type.find("delete") != std::string::npos; + bool is_remove = spawn_type.find("remove") != std::string::npos; + bool is_update = spawn_type.find("update") != std::string::npos; + if (!is_add && !is_create && !is_delete && !is_remove && !is_update) { + c->Message(Chat::White, "Command Syntax: #npcspawn [Add|Create|Delete|Remove|Update]"); + return; + } + + if (is_add || is_create) { + extra = ( + sep->IsNumber(2) ? + ( + is_add ? + std::stoi(sep->arg[2]) : + 1 + ) : ( + is_add ? + 1200 : + 0 + ) + ); // Default to 1200 for Add, 0 for Create if not set + content_db.NPCSpawnDB( + is_add ? NPCSpawnTypes::AddNewSpawngroup : NPCSpawnTypes::CreateNewSpawn, + zone->GetShortName(), + zone->GetInstanceVersion(), + c, + target, + extra + ); + c->Message( + Chat::White, + fmt::format( + "Spawn {} | Name: {} ({})", + is_add ? "Added" : "Created", + target->GetCleanName(), + target->GetID() + ).c_str() + ); + } else if (is_delete || is_remove || is_update) { + uint8 spawn_update_type = ( + is_delete ? + NPCSpawnTypes::DeleteSpawn : + ( + is_remove ? + NPCSpawnTypes::RemoveSpawn : + NPCSpawnTypes::UpdateAppearance + ) + ); + std::string spawn_message = ( + is_delete ? + "Deleted" : + ( + is_remove ? + "Removed" : + "Updated" + ) + ); + content_db.NPCSpawnDB( + spawn_update_type, + zone->GetShortName(), + zone->GetInstanceVersion(), + c, + target + ); + c->Message( + Chat::White, + fmt::format( + "Spawn {} | Name: {} ({})", + spawn_message, + target->GetCleanName(), + target->GetID() + ).c_str() + ); } - else - c->Message(Chat::White, "Error: #npcspawn: You must have a NPC targeted!"); } void command_spawnfix(Client *c, const Seperator *sep) { @@ -12054,253 +12097,488 @@ void command_refreshgroup(Client *c, const Seperator *sep) void command_advnpcspawn(Client *c, const Seperator *sep) { - Mob *target=c->GetTarget(); + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #advnpcspawn addentry [Spawngroup ID] [NPC ID] [Spawn Chance] - Adds a new Spawngroup Entry"); + c->Message(Chat::White, "Usage: #advnpcspawn addspawn [Spawngroup ID] - Adds a new Spawngroup Entry from an existing Spawngroup"); + c->Message(Chat::White, "Usage: #advnpcspawn clearbox [Spawngroup ID] - Clears the roambox of a Spawngroup"); + c->Message(Chat::White, "Usage: #advnpcspawn deletespawn - Deletes a Spawngroup"); + c->Message(Chat::White, "Usage: #advnpcspawn editbox [Spawngroup ID] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay] - Edit the roambox of a Spawngroup"); + c->Message(Chat::White, "Usage: #advnpcspawn editrespawn [Respawn Timer] [Variance] - Edit the Respawn Timer of a Spawngroup"); + c->Message(Chat::White, "Usage: #advnpcspawn makegroup [Spawn Group Name] [Spawn Limit] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay] - Makes a new Spawngroup"); + c->Message(Chat::White, "Usage: #advnpcspawn makenpc - Makes a new NPC"); + c->Message(Chat::White, "Usage: #advnpcspawn movespawn - Moves a Spawngroup to your current location"); + c->Message(Chat::White, "Usage: #advnpcspawn setversion [Version] - Sets a Spawngroup's Version"); + return; + } - if (strcasecmp(sep->arg[1], "maketype") == 0) { - if(!target || !target->IsNPC()) { - c->Message(Chat::White, "Target Required!"); - return; - } + std::string spawn_command = str_tolower(sep->arg[1]); + bool is_add_entry = spawn_command.find("addentry") != std::string::npos; + bool is_add_spawn = spawn_command.find("addspawn") != std::string::npos; + bool is_clear_box = spawn_command.find("clearbox") != std::string::npos; + bool is_delete_spawn = spawn_command.find("deletespawn") != std::string::npos; + bool is_edit_box = spawn_command.find("editgroup") != std::string::npos; + bool is_edit_respawn = spawn_command.find("editrespawn") != std::string::npos; + bool is_make_group = spawn_command.find("makegroup") != std::string::npos; + bool is_make_npc = spawn_command.find("makenpc") != std::string::npos; + bool is_move_spawn = spawn_command.find("movespawn") != std::string::npos; + bool is_set_version = spawn_command.find("setversion") != std::string::npos; + if ( + !is_add_entry && + !is_add_spawn && + !is_clear_box && + !is_delete_spawn && + !is_edit_box && + !is_edit_respawn && + !is_make_group && + !is_make_npc && + !is_move_spawn && + !is_set_version + ) { + c->Message(Chat::White, "Usage: #advnpcspawn addentry [Spawngroup ID] [NPC ID] [Spawn Chance] - Adds a new Spawngroup Entry"); + c->Message(Chat::White, "Usage: #advnpcspawn addspawn [Spawngroup ID] - Adds a new Spawngroup Entry from an existing Spawngroup"); + c->Message(Chat::White, "Usage: #advnpcspawn clearbox [Spawngroup ID] - Clears the roambox of a Spawngroup"); + c->Message(Chat::White, "Usage: #advnpcspawn deletespawn - Deletes a Spawngroup"); + c->Message(Chat::White, "Usage: #advnpcspawn editbox [Spawngroup ID] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay] - Edit the roambox of a Spawngroup"); + c->Message(Chat::White, "Usage: #advnpcspawn editrespawn [Respawn Timer] [Variance] - Edit the Respawn Timer of a Spawngroup"); + c->Message(Chat::White, "Usage: #advnpcspawn makegroup [Spawn Group Name] [Spawn Limit] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay] - Makes a new Spawngroup"); + c->Message(Chat::White, "Usage: #advnpcspawn makenpc - Makes a new NPC"); + c->Message(Chat::White, "Usage: #advnpcspawn movespawn - Moves a Spawngroup to your current location"); + c->Message(Chat::White, "Usage: #advnpcspawn setversion [Version] - Sets a Spawngroup's Version"); + return; + } - content_db.NPCSpawnDB(6, zone->GetShortName(), zone->GetInstanceVersion(), c, target->CastToNPC()); - return; - } - if (strcasecmp(sep->arg[1], "makegroup") == 0) { - if(!sep->arg[2]) { - c->Message(Chat::White, "Format: #advnpdspawn makegroup [spawn limit] [dist] [max x] [min x] [max y] [min y] [delay]"); - return; - } + if (is_add_entry) { + if(arguments < 4) { + c->Message(Chat::White, "Usage: #advnpcspawn addentry [Spawngroup ID] [NPC ID] [Spawn Chance]"); + return; + } + + auto spawngroup_id = std::stoi(sep->arg[2]); + auto npc_id = std::stoi(sep->arg[2]); + auto spawn_chance = std::stoi(sep->arg[2]); - std::string query = StringFormat("INSERT INTO spawngroup " - "(name, spawn_limit, dist, max_x, min_x, max_y, min_y, delay) " - "VALUES (\"%s\", %i, %f, %f, %f, %f, %f, %i)", - sep->arg[2], - (sep->arg[3]? atoi(sep->arg[3]): 0), - (sep->arg[4]? atof(sep->arg[4]): 0), - (sep->arg[5]? atof(sep->arg[5]): 0), - (sep->arg[6]? atof(sep->arg[6]): 0), - (sep->arg[7]? atof(sep->arg[7]): 0), - (sep->arg[8]? atof(sep->arg[8]): 0), - (sep->arg[9]? atoi(sep->arg[9]): 0)); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Invalid Arguments -- MySQL gave the following error:"); - c->Message(Chat::Red, results.ErrorMessage().c_str()); - return; - } + std::string query = fmt::format( + SQL( + INSERT INTO spawnentry (spawngroupID, npcID, chance) + VALUES ({}, {}, {}) + ), + spawngroup_id, + npc_id, + spawn_chance + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Failed to add entry to Spawngroup."); + return; + } - c->Message(Chat::White, "Group ID %i created successfully!", results.LastInsertedID()); - return; - } + c->Message( + Chat::White, + fmt::format( + "{} ({}) added to Spawngroup {}, its spawn chance is {}%%.", + database.GetCleanNPCNameByID(npc_id), + npc_id, + spawngroup_id, + spawn_chance + ).c_str() + ); + return; + } else if (is_add_spawn) { + content_db.NPCSpawnDB( + NPCSpawnTypes::AddSpawnFromSpawngroup, + zone->GetShortName(), + zone->GetInstanceVersion(), + c, + 0, + std::stoi(sep->arg[2]) + ); + c->Message( + Chat::White, + fmt::format( + "Spawn Added | Added spawn from Spawngroup ID {}.", + std::stoi(sep->arg[2]) + ).c_str() + ); + return; + } else if (is_clear_box) { + if (arguments != 2 || !sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #advnpcspawn clearbox [Spawngroup ID]"); + return; + } - if (strcasecmp(sep->arg[1], "addgroupentry") == 0) { - if(!atoi(sep->arg[2]) || !atoi(sep->arg[3]) || !atoi(sep->arg[4])) { - c->Message(Chat::White, "Format: #advnpdspawn addgroupentry "); - return; - } + std::string query = fmt::format( + "UPDATE spawngroup SET dist = 0, min_x = 0, max_x = 0, min_y = 0, max_y = 0, delay = 0 WHERE id = {}", + std::stoi(sep->arg[2]) + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Failed to clear Spawngroup box."); + return; + } - std::string query = StringFormat("INSERT INTO spawnentry (spawngroupID, npcID, chance) " - "VALUES (%i, %i, %i)", - atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4])); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Invalid Arguments -- MySQL gave the following error:"); - c->Message(Chat::Red, results.ErrorMessage().c_str()); - return; - } + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Roambox Cleared | Delay: 0 Distance: 0.00", + std::stoi(sep->arg[2]) + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Roambox Cleared | Minimum X: 0.00 Maximum X: 0.00", + std::stoi(sep->arg[2]) + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Roambox Cleared | Minimum Y: 0.00 Maximum Y: 0.00", + std::stoi(sep->arg[2]) + ).c_str() + ); + return; + } else if (is_delete_spawn) { + if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; + } - c->Message(Chat::White, "NPC %i added to group %i with %i chance!", atoi(sep->arg[3]), atoi(sep->arg[2]), atoi(sep->arg[4]) ); + NPC *target = c->GetTarget()->CastToNPC(); + Spawn2* spawn2 = target->respawn2; + if(!spawn2) { + c->Message(Chat::White, "Failed to delete spawn because NPC has no Spawn2."); + return; + } - return; - } + auto spawn2_id = spawn2->GetID(); + std::string query = fmt::format( + "DELETE FROM spawn2 WHERE id = {}", + spawn2_id + ); + auto results = content_db.QueryDatabase(query); + if(!results.Success()) { + c->Message(Chat::White, "Failed to delete spawn."); + return; + } - if (strcasecmp(sep->arg[1], "editgroupbox") == 0) { - if(!atof(sep->arg[2]) || !atof(sep->arg[3]) || !atof(sep->arg[4]) || !atof(sep->arg[5]) || !atof(sep->arg[6]) || !atof(sep->arg[7]) || !atof(sep->arg[8])) { - c->Message(Chat::White, "Format: #advnpdspawn editgroupbox "); - return; - } + c->Message( + Chat::White, + fmt::format( + "Spawn2 {} Deleted | Name: {} ({})", + spawn2_id, + target->GetCleanName(), + target->GetID() + ).c_str() + ); + target->Depop(false); + return; + } else if (is_edit_box) { + if ( + arguments != 8 || + !sep->IsNumber(3) || + !sep->IsNumber(4) || + !sep->IsNumber(5) || + !sep->IsNumber(6) || + !sep->IsNumber(7) || + !sep->IsNumber(8) + ) { + c->Message(Chat::White, "Usage: #advnpcspawn editbox [Spawngroup ID] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay]"); + return; + } + auto spawngroup_id = std::stoi(sep->arg[2]); + auto distance = std::stof(sep->arg[3]); + auto minimum_x = std::stof(sep->arg[4]); + auto maximum_x = std::stof(sep->arg[5]); + auto minimum_y = std::stof(sep->arg[6]); + auto maximum_y = std::stof(sep->arg[7]); + auto delay = std::stoi(sep->arg[8]); - std::string query = StringFormat("UPDATE spawngroup SET dist = '%f', max_x = '%f', min_x = '%f', " - "max_y = '%f', min_y = '%f', delay = '%i' WHERE id = '%i'", - atof(sep->arg[3]), atof(sep->arg[4]), atof(sep->arg[5]), - atof(sep->arg[6]), atof(sep->arg[7]), atoi(sep->arg[8]), - atoi(sep->arg[2])); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Invalid Arguments -- MySQL gave the following error:"); - c->Message(Chat::Red, results.ErrorMessage().c_str()); - return; - } + std::string query = fmt::format( + "UPDATE spawngroup SET dist = {:.2f}, min_x = {:.2f}, max_x = {:.2f}, max_y = {:.2f}, min_y = {:.2f}, delay = {} WHERE id = {}", + distance, + minimum_x, + maximum_x, + minimum_y, + maximum_y, + delay, + spawngroup_id + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Failed to edit Spawngroup box."); + return; + } - c->Message(Chat::White, "Group ID %i created successfully!", results.LastInsertedID()); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Roambox Edited | Delay: {} Distance: {:.2f}", + spawngroup_id, + delay, + distance + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Roambox Edited | Minimum X: {:.2f} Maximum X: {:.2f}", + spawngroup_id, + minimum_x, + maximum_x + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Roambox Edited | Minimum Y: {:.2f} Maximum Y: {:.2f}", + spawngroup_id, + minimum_y, + maximum_y + ).c_str() + ); + return; + } else if (is_edit_respawn) { + if (arguments < 2 || !sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #advnpcspawn editrespawn [Respawn Timer] [Variance]"); + return; + } - return; - } + if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; + } - if (strcasecmp(sep->arg[1], "cleargroupbox") == 0) { - if(!atoi(sep->arg[2])) { - c->Message(Chat::White, "Format: #advnpdspawn cleargroupbox "); - return; - } + NPC *target = c->GetTarget()->CastToNPC(); + Spawn2* spawn2 = target->respawn2; + if(!spawn2) { + c->Message(Chat::White, "Failed to edit respawn because NPC has no Spawn2."); + return; + } - std::string query = StringFormat("UPDATE spawngroup " - "SET dist = '0', max_x = '0', min_x = '0', " - "max_y = '0', min_y = '0', delay = '0' " - "WHERE id = '%i' ", atoi(sep->arg[2])); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Invalid Arguments -- MySQL gave the following error:"); - c->Message(Chat::Red, results.ErrorMessage().c_str()); - return; - } + auto spawn2_id = spawn2->GetID(); + uint32 respawn_timer = std::stoi(sep->arg[2]); + uint32 variance = ( + sep->IsNumber(3) ? + std::stoi(sep->arg[3]) : + spawn2->GetVariance() + ); + std::string query = fmt::format( + "UPDATE spawn2 SET respawntime = {}, variance = {} WHERE id = {}", + respawn_timer, + variance, + spawn2_id + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Failed to edit respawn."); + return; + } - c->Message(Chat::White, "Group ID %i created successfully!", results.LastInsertedID()); + c->Message( + Chat::White, + fmt::format( + "Spawn2 {} Respawn Modified | Name: {} ({}) Respawn Timer: {} Variance: {}", + spawn2_id, + target->GetCleanName(), + target->GetID(), + respawn_timer, + variance + ).c_str() + ); + spawn2->SetRespawnTimer(respawn_timer); + spawn2->SetVariance(variance); + return; + } else if (is_make_group) { + if ( + arguments != 9 || + !sep->IsNumber(3) || + !sep->IsNumber(4) || + !sep->IsNumber(5) || + !sep->IsNumber(6) || + !sep->IsNumber(7) || + !sep->IsNumber(8) || + !sep->IsNumber(9) + ) { + c->Message(Chat::White, "Usage: #advncspawn makegroup [Spawn Group Name] [Spawn Limit] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay]"); + return; + } + std::string spawngroup_name = sep->arg[2]; + auto spawn_limit = std::stoi(sep->arg[3]); + auto distance = std::stof(sep->arg[4]); + auto minimum_x = std::stof(sep->arg[5]); + auto maximum_x = std::stof(sep->arg[6]); + auto minimum_y = std::stof(sep->arg[7]); + auto maximum_y = std::stof(sep->arg[8]); + auto delay = std::stoi(sep->arg[9]); - return; - } + std::string query = fmt::format( + "INSERT INTO spawngroup" + "(name, spawn_limit, dist, min_x, max_x, min_y, max_y, delay)" + "VALUES ('{}', {}, {:.2f}, {:.2f}, {:.2f}, {:.2f}, {:.2f}, {})", + spawngroup_name, + spawn_limit, + distance, + minimum_x, + maximum_x, + minimum_y, + maximum_y, + delay + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Failed to make Spawngroup."); + return; + } - if (strcasecmp(sep->arg[1], "addgroupspawn") == 0 && atoi(sep->arg[2])!=0) { - content_db.NPCSpawnDB(5, zone->GetShortName(), zone->GetInstanceVersion(), c, 0, atoi(sep->arg[2])); - c->Message(Chat::White, "Mob of group %i added successfully!", atoi(sep->arg[2])); - return; - } + auto spawngroup_id = results.LastInsertedID(); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Created | Name: {} Spawn Limit: {}", + spawngroup_id, + spawngroup_name, + spawn_limit + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Created | Delay: {} Distance: {:.2f}", + spawngroup_id, + delay, + distance + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Created | Minimum X: {:.2f} Maximum X: {:.2f}", + spawngroup_id, + minimum_x, + maximum_x + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Created | Minimum Y: {:.2f} Maximum Y: {:.2f}", + spawngroup_id, + minimum_y, + maximum_y + ).c_str() + ); + return; + } else if (is_make_npc) { + if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; + } - if (strcasecmp(sep->arg[1], "removegroupspawn") == 0) { - if (!target || !target->IsNPC()) { - c->Message(Chat::White, "Error: Need an NPC target."); - return; - } + NPC *target = c->GetTarget()->CastToNPC(); + content_db.NPCSpawnDB( + NPCSpawnTypes::CreateNewNPC, + zone->GetShortName(), + zone->GetInstanceVersion(), + c, + target + ); + return; + } else if (is_move_spawn) { + if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; + } - Spawn2* s2 = target->CastToNPC()->respawn2; + NPC *target = c->GetTarget()->CastToNPC(); + Spawn2* spawn2 = target->respawn2; + if(!spawn2) { + c->Message(Chat::White, "Failed to move spawn because NPC has no Spawn2."); + return; + } - if(!s2) { - c->Message(Chat::White, "removegroupspawn FAILED -- cannot determine which spawn entry in the database this mob came from."); - return; - } + auto client_position = c->GetPosition(); + auto spawn2_id = spawn2->GetID(); + std::string query = fmt::format( + "UPDATE spawn2 SET x = {:.2f}, y = {:.2f}, z = {:.2f}, heading = {:.2f} WHERE id = {}", + client_position.x, + client_position.y, + client_position.z, + client_position.w, + spawn2_id + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Failed to move spawn."); + return; + } - std::string query = StringFormat("DELETE FROM spawn2 WHERE id = '%i'", s2->GetID()); - auto results = content_db.QueryDatabase(query); - if(!results.Success()) { - c->Message(Chat::Red, "Update failed! MySQL gave the following error:"); - c->Message(Chat::Red, results.ErrorMessage().c_str()); - return; - } + c->Message( + Chat::White, + fmt::format( + "Spawn2 {} Moved | Name: {} ({})", + spawn2_id, + target->GetCleanName(), + target->GetID() + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "Spawn2 {} Moved | XYZ: {}, {}, {} Heading: {}", + spawn2_id, + client_position.x, + client_position.y, + client_position.z, + client_position.w + ).c_str() + ); + target->GMMove( + client_position.x, + client_position.y, + client_position.z, + client_position.w + ); + return; + } else if (is_set_version) { + if (arguments != 2 || !sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #advnpcspawn setversion [Version]"); + return; + } - c->Message(Chat::White, "Spawnpoint Removed successfully."); - target->Depop(false); + if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; + } - return; - } + NPC* target = c->GetTarget()->CastToNPC(); + auto version = std::stoi(sep->arg[2]); + std::string query = fmt::format( + "UPDATE spawn2 SET version = {} WHERE spawngroupID = {}", + version, + target->GetSpawnGroupId() + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Failed to set version."); + return; + } - if (strcasecmp(sep->arg[1], "movespawn") == 0) { - if (!target || !target->IsNPC()) { - c->Message(Chat::White, "Error: Need an NPC target."); - return; - } - - Spawn2* s2 = target->CastToNPC()->respawn2; - - if(!s2) { - c->Message(Chat::White, "movespawn FAILED -- cannot determine which spawn entry in the database this mob came from."); - return; - } - - std::string query = StringFormat("UPDATE spawn2 SET x = '%f', y = '%f', z = '%f', heading = '%f' " - "WHERE id = '%i'", - c->GetX(), c->GetY(), c->GetZ(), c->GetHeading(),s2->GetID()); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::Red, "Update failed! MySQL gave the following error:"); - c->Message(Chat::Red, results.ErrorMessage().c_str()); - return; - } - - c->Message(Chat::White, "Updating coordinates successful."); - target->GMMove(c->GetX(), c->GetY(), c->GetZ(), c->GetHeading()); - - return; - } - - if (strcasecmp(sep->arg[1], "editrespawn") == 0) { - if (!target || !target->IsNPC()) { - c->Message(Chat::White, "Error: Need an NPC target."); - return; - } - - Spawn2* s2 = target->CastToNPC()->respawn2; - - uint32 new_rs = 0; - uint32 new_var = s2->GetVariance(); - if(!sep->IsNumber(2)) { - c->Message(Chat::White, "editrespawn FAILED -- cannot set respawn to be 0"); - return; - } - - new_rs = atoi(sep->arg[2]); - - if(sep->IsNumber(3)) - new_var = atoi(sep->arg[3]); - - if(!s2) { - c->Message(Chat::White, "editrespawn FAILED -- cannot determine which spawn entry in the database this mob came from."); - return; - } - - std::string query = StringFormat("UPDATE spawn2 SET respawntime = %u, variance = %u " - "WHERE id = '%i'", new_rs, new_var, s2->GetID()); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::Red, "Update failed! MySQL gave the following error:"); - c->Message(Chat::Red, results.ErrorMessage().c_str()); - return; - } - - c->Message(Chat::White, "Updating respawn timer successful."); - s2->SetRespawnTimer(new_rs); - s2->SetVariance(new_var); - - return; - } - - if (strcasecmp(sep->arg[1], "setversion") == 0) { - if (!target || !target->IsNPC()) { - c->Message(Chat::White, "Error: Need an NPC target."); - return; - } - - if(!sep->IsNumber(2)) { - c->Message(Chat::White, "setversion FAILED -- You must set a version number"); - return; - } - - int16 version = atoi(sep->arg[2]); - std::string query = StringFormat("UPDATE spawn2 SET version = %i " - "WHERE spawngroupID = '%i'", - version, c->GetTarget()->CastToNPC()->GetSpawnGroupId()); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::Red, "Update failed! MySQL gave the following error:"); - c->Message(Chat::Red, results.ErrorMessage().c_str()); - return; - } - - c->Message(Chat::White, "Version change to %i was successful from SpawnGroupID %i", version, - c->GetTarget()->CastToNPC()->GetSpawnGroupId()); - c->GetTarget()->Depop(false); - - return; - } - - if (strcasecmp(sep->arg[1], "testload") == 0 && atoi(sep->arg[2])!=0) { - content_db.LoadSpawnGroupsByID(atoi(sep->arg[2]),&zone->spawn_group_list); - c->Message(Chat::White, "Group %i loaded successfully!", atoi(sep->arg[2])); - return; - } - - c->Message(Chat::White, "Error: #advnpcspawn: Invalid command."); - c->Message(Chat::White, "Usage: #advnpcspawn [maketype|makegroup|addgroupentry|addgroupspawn|setversion]"); - c->Message(Chat::White, "Usage: #advnpcspawn [removegroupspawn|movespawn|editrespawn|editgroupbox|cleargroupbox]"); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Version Modified | Name: {} ({}) Version: {}", + target->GetSpawnGroupId(), + target->GetCleanName(), + target->GetID(), + version + ).c_str() + ); + target->Depop(false); + return; + } } void command_aggrozone(Client *c, const Seperator *sep) { @@ -13773,7 +14051,7 @@ void command_object(Client *c, const Seperator *sep) void command_showspellslist(Client *c, const Seperator *sep) { Mob *target = c->GetTarget(); - if (!target || !target->IsNPC()) { + if (!target || !target->IsNPC()) { c->Message(Chat::White, "You must target an NPC to use this command."); return; } diff --git a/zone/npc.cpp b/zone/npc.cpp index c44bb15f6..85d1d6755 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -1719,25 +1719,25 @@ uint32 ZoneDatabase::AddNPCTypes(const char *zone, uint32 zone_version, Client * uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn, uint32 extra) { switch (command) { - case 0: { // Create a new NPC and add all spawn related data + case NPCSpawnTypes::CreateNewSpawn: { // Create a new NPC and add all spawn related data return CreateNewNPCCommand(zone, zone_version, c, spawn, extra); } - case 1:{ // Add new spawn group and spawn point for an existing NPC Type ID + case NPCSpawnTypes::AddNewSpawngroup: { // Add new spawn group and spawn point for an existing NPC Type ID return AddNewNPCSpawnGroupCommand(zone, zone_version, c, spawn, extra); } - case 2: { // Update npc_type appearance and other data on targeted spawn + case NPCSpawnTypes::UpdateAppearance: { // Update npc_type appearance and other data on targeted spawn return UpdateNPCTypeAppearance(c, spawn); } - case 3: { // delete spawn from spawning, but leave in npc_types table + case NPCSpawnTypes::RemoveSpawn: { // delete spawn from spawning, but leave in npc_types table return DeleteSpawnLeaveInNPCTypeTable(zone, c, spawn); } - case 4: { //delete spawn from DB (including npc_type) + case NPCSpawnTypes::DeleteSpawn: { //delete spawn from DB (including npc_type) return DeleteSpawnRemoveFromNPCTypeTable(zone, zone_version, c, spawn); } - case 5: { // add a spawn from spawngroup + case NPCSpawnTypes::AddSpawnFromSpawngroup: { // add a spawn from spawngroup return AddSpawnFromSpawnGroup(zone, zone_version, c, spawn, extra); } - case 6: { // add npc_type + case NPCSpawnTypes::CreateNewNPC: { // add npc_type return AddNPCTypes(zone, zone_version, c, spawn, extra); } } diff --git a/zone/zonedb.h b/zone/zonedb.h index a72299c65..2a35d5632 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -257,6 +257,18 @@ namespace BeastlordPetData { }; } +namespace NPCSpawnTypes { + enum : uint8 { + CreateNewSpawn, + AddNewSpawngroup, + UpdateAppearance, + RemoveSpawn, + DeleteSpawn, + AddSpawnFromSpawngroup, + CreateNewNPC + }; +} + namespace RaidLootTypes { enum : uint32 { RaidLeader = 1, From 293361a1f72ee652b9a7faaee89ca92bb7b943c5 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 14 Nov 2021 22:01:13 -0500 Subject: [PATCH 412/624] [Cleanup] Make use of AccountStatus constants wherever status is checked or used. (#1764) * [Cleanup] Make use of AccountStatus constants wherever status is checked or used. - Cleanup all instances of SendEmoteMessage. - Cleanup all instances of SendEmoteMessageRaw. - Cleanup all instances of MessageStatus. - Convert Quest API method defaults to use constants. * Cleanup constant names. --- common/emu_constants.h | 58 +- common/guild_base.cpp | 2 +- common/net/console_server_connection.cpp | 4 +- common/net/websocket_server.cpp | 5 +- world/cliententry.cpp | 4 +- world/cliententry.h | 2 +- world/clientlist.cpp | 182 +++--- world/clientlist.h | 2 +- world/console.cpp | 45 +- world/console.old.cpp | 18 +- world/eqw.cpp | 9 +- world/login_server.cpp | 8 +- world/main.cpp | 16 +- world/world_event_scheduler.cpp | 8 +- world/zonelist.cpp | 118 +++- world/zoneserver.cpp | 179 ++++-- zone/bot_command.cpp | 99 +-- zone/client.cpp | 29 +- zone/client_packet.cpp | 50 +- zone/client_process.cpp | 4 +- zone/command.cpp | 730 +++++++++++------------ zone/corpse.cpp | 10 +- zone/embparser_api.cpp | 84 +-- zone/entity.cpp | 8 +- zone/entity.h | 2 +- zone/exp.cpp | 2 +- zone/guild_mgr.cpp | 12 +- zone/lua_entity_list.cpp | 7 +- zone/mob.cpp | 2 +- zone/pathing.cpp | 6 +- zone/perl_entity.cpp | 7 +- zone/questmgr.cpp | 122 +++- zone/questmgr.h | 18 +- zone/spells.cpp | 2 +- zone/trading.cpp | 48 +- zone/worldserver.cpp | 310 +++++++--- zone/zone.h | 21 +- zone/zoning.cpp | 6 +- 38 files changed, 1369 insertions(+), 870 deletions(-) diff --git a/common/emu_constants.h b/common/emu_constants.h index 806ce0a86..e071ca449 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -230,31 +230,6 @@ namespace EQ const int STANCE_TYPE_LAST = stanceBurnAE; const int STANCE_TYPE_COUNT = stanceBurnAE; - enum ServerLockType : int { - List, - Lock, - Unlock - }; - - enum AccountStatus : uint8 { - Player = 0, - Steward = 10, - ApprenticeGuide = 20, - Guide = 50, - QuestTroupe = 80, - SeniorGuide = 81, - GMTester = 85, - EQSupport = 90, - GMStaff = 95, - GMAdmin = 100, - GMLeadAdmin = 150, - QuestMaster = 160, - GMAreas = 170, - GMCoder = 180, - GMMgmt = 200, - GMImpossible = 250, - Max = 255 - }; } /*constants*/ namespace profile { @@ -353,13 +328,32 @@ namespace EQ Guild }; }; // namespace consent - } /*EQEmu*/ +enum ServerLockType : int { + List, + Lock, + Unlock +}; + +enum AccountStatus : uint8 { + Player = 0, + Steward = 10, + ApprenticeGuide = 20, + Guide = 50, + QuestTroupe = 80, + SeniorGuide = 81, + GMTester = 85, + EQSupport = 90, + GMStaff = 95, + GMAdmin = 100, + GMLeadAdmin = 150, + QuestMaster = 160, + GMAreas = 170, + GMCoder = 180, + GMMgmt = 200, + GMImpossible = 250, + Max = 255 +}; + #endif /*COMMON_EMU_CONSTANTS_H*/ - -/* hack list to prevent circular references - - eq_limits.h:EQ::inventory::LookupEntry::InventoryTypeSize[n]; - -*/ diff --git a/common/guild_base.cpp b/common/guild_base.cpp index b7ac8515c..198dd1c5a 100644 --- a/common/guild_base.cpp +++ b/common/guild_base.cpp @@ -1208,7 +1208,7 @@ BaseGuildManager::RankInfo::RankInfo() { BaseGuildManager::GuildInfo::GuildInfo() { leader_char_id = 0; - minstatus = 0; + minstatus = AccountStatus::Player; } uint32 BaseGuildManager::DoesAccountContainAGuildLeader(uint32 AccountID) diff --git a/common/net/console_server_connection.cpp b/common/net/console_server_connection.cpp index aab26d188..9e245a8f8 100644 --- a/common/net/console_server_connection.cpp +++ b/common/net/console_server_connection.cpp @@ -15,7 +15,7 @@ EQ::Net::ConsoleServerConnection::ConsoleServerConnection(ConsoleServer *parent, memset(m_line, 0, MaxConsoleLineLength); m_accept_messages = false; m_user_id = 0; - m_admin = 0; + m_admin = AccountStatus::Player; m_connection->OnRead(std::bind(&ConsoleServerConnection::OnRead, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); m_connection->OnDisconnect(std::bind(&ConsoleServerConnection::OnDisconnect, this, std::placeholders::_1)); @@ -29,7 +29,7 @@ EQ::Net::ConsoleServerConnection::ConsoleServerConnection(ConsoleServer *parent, if (addr.find("127.0.0.1") != std::string::npos || addr.find("::0") != std::string::npos) { SendLine("Connection established from localhost, assuming admin"); m_status = ConsoleStatusLoggedIn; - m_admin = 255; + m_admin = AccountStatus::Max; SendPrompt(); } else { diff --git a/common/net/websocket_server.cpp b/common/net/websocket_server.cpp index dda1a4a96..f74f437ba 100644 --- a/common/net/websocket_server.cpp +++ b/common/net/websocket_server.cpp @@ -5,6 +5,7 @@ #include #include #include +#include "../emu_constants.h" struct MethodHandlerEntry { @@ -174,13 +175,13 @@ Json::Value EQ::Net::WebsocketServer::Login(WebsocketServerConnection *connectio auto r = _impl->login_handler(connection, user, pass); if (r.logged_in) { - connection->SetAuthorized(true, r.account_name, r.account_id, 255); + connection->SetAuthorized(true, r.account_name, r.account_id, AccountStatus::Max); ret["status"] = "Ok"; } else if (user == "admin" && (connection->RemoteIP() == "127.0.0.1" || connection->RemoteIP() == "::")) { r.logged_in = true; r.account_id = 0; - connection->SetAuthorized(true, r.account_name, r.account_id, 255); + connection->SetAuthorized(true, r.account_name, r.account_id, AccountStatus::Max); ret["status"] = "Ok"; } else { diff --git a/world/cliententry.cpp b/world/cliententry.cpp index 9e10f495d..68252c802 100644 --- a/world/cliententry.cpp +++ b/world/cliententry.cpp @@ -274,7 +274,7 @@ void ClientListEntry::ClearVars(bool iAll) paccountid = 0; memset(paccountname, 0, sizeof(paccountname)); - padmin = 0; + padmin = AccountStatus::Player; } pzoneserver = 0; pzone = 0; @@ -365,7 +365,7 @@ bool ClientListEntry::CheckAuth(uint32 loginserver_account_id, const char *key_p } std::string lsworldadmin; if (database.GetVariable("honorlsworldadmin", lsworldadmin)) { - if (atoi(lsworldadmin.c_str()) == 1 && pworldadmin != 0 && (padmin < pworldadmin || padmin == 0)) { + if (atoi(lsworldadmin.c_str()) == 1 && pworldadmin != 0 && (padmin < pworldadmin || padmin == AccountStatus::Player)) { padmin = pworldadmin; } } diff --git a/world/cliententry.h b/world/cliententry.h index 769700403..f3ce31dea 100644 --- a/world/cliententry.h +++ b/world/cliententry.h @@ -60,7 +60,7 @@ public: * @param scl * @param iOnline */ - ClientListEntry(uint32 id, uint32 iAccID, const char* iAccName, MD5& iMD5Pass, int16 iAdmin = 0); + ClientListEntry(uint32 id, uint32 iAccID, const char* iAccName, MD5& iMD5Pass, int16 iAdmin = AccountStatus::Player); ClientListEntry(uint32 id, ZoneServer* iZS, ServerClientList_Struct* scl, CLE_Status iOnline); ~ClientListEntry(); bool CheckStale(); diff --git a/world/clientlist.cpp b/world/clientlist.cpp index 7ce779130..1513cb2df 100644 --- a/world/clientlist.cpp +++ b/world/clientlist.cpp @@ -296,7 +296,13 @@ void ClientList::SendCLEList(const int16& admin, const char* to, WorldTCPConnect fmt:format_to(out, "{} CharID: {} CharName: {} Zone: {} ({})", newline, cle->CharID(), cle->name(), ZoneName(cle->zone()), cle->zone()); if (out.size() >= 3072) { auto output = fmt::to_string(out); - connection->SendEmoteMessageRaw(to, 0, 0, 10, output.c_str()); + connection->SendEmoteMessageRaw( + to, + 0, + AccountStatus::Player, + Chat::NPCQuestSay, + output.c_str() + ); addnewline = false; out.clear(); } else { @@ -309,7 +315,13 @@ void ClientList::SendCLEList(const int16& admin, const char* to, WorldTCPConnect } fmt::format_to(out, "{}{} CLEs in memory. {} CLEs listed. numplayers = {}.", newline, x, y, numplayers); auto output = fmt::to_string(out); - connection->SendEmoteMessageRaw(to, 0, 0, 10, output.c_str()); + connection->SendEmoteMessageRaw( + to, + 0, + AccountStatus::Player, + Chat::NPCQuestSay, + output.c_str() + ); } @@ -558,7 +570,7 @@ void ClientList::SendWhoAll(uint32 fromid,const char* to, int16 admin, Who_All_S (countcle->Online() >= CLE_Status::Zoning) && (!countcle->GetGM() || countcle->Anon() != 1 || admin >= countcle->Admin()) && (whom == 0 || ( - ((countcle->Admin() >= 80 && countcle->GetGM()) || whom->gmlookup == 0xFFFF) && + ((countcle->Admin() >= AccountStatus::QuestTroupe && countcle->GetGM()) || whom->gmlookup == 0xFFFF) && (whom->lvllow == 0xFFFF || (countcle->level() >= whom->lvllow && countcle->level() <= whom->lvlhigh && (countcle->Anon()==0 || admin > countcle->Admin()))) && (whom->wclass == 0xFFFF || (countcle->class_() == whom->wclass && (countcle->Anon()==0 || admin > countcle->Admin()))) && (whom->wrace == 0xFFFF || (countcle->race() == whom->wrace && (countcle->Anon()==0 || admin > countcle->Admin()))) && @@ -566,18 +578,18 @@ void ClientList::SendWhoAll(uint32 fromid,const char* to, int16 admin, Who_All_S (tmpZone != 0 && strncasecmp(tmpZone, whom->whom, whomlen) == 0) || strncasecmp(countcle->name(),whom->whom, whomlen) == 0 || (strncasecmp(guild_mgr.GetGuildName(countcle->GuildID()), whom->whom, whomlen) == 0) || - (admin >= 100 && strncasecmp(countcle->AccountName(), whom->whom, whomlen) == 0) + (admin >= AccountStatus::GMAdmin && strncasecmp(countcle->AccountName(), whom->whom, whomlen) == 0) )) )) ) { - if((countcle->Anon()>0 && admin>=countcle->Admin() && admin>0) || countcle->Anon()==0 ){ + if((countcle->Anon()>0 && admin >= countcle->Admin() && admin > AccountStatus::Player) || countcle->Anon()==0 ){ totalusers++; - if(totalusers<=20 || admin>=100) + if(totalusers<=20 || admin >= AccountStatus::GMAdmin) totallength=totallength+strlen(countcle->name())+strlen(countcle->AccountName())+strlen(guild_mgr.GetGuildName(countcle->GuildID()))+5; } else if((countcle->Anon()>0 && admin<=countcle->Admin()) || (countcle->Anon()==0 && !countcle->GetGM())) { totalusers++; - if(totalusers<=20 || admin>=100) + if(totalusers<=20 || admin >= AccountStatus::GMAdmin) totallength=totallength+strlen(countcle->name())+strlen(guild_mgr.GetGuildName(countcle->GuildID()))+5; } } @@ -589,7 +601,7 @@ void ClientList::SendWhoAll(uint32 fromid,const char* to, int16 admin, Who_All_S uint8 unknown35=0x0A; uint32 unknown36=0; uint32 playersinzonestring=5028; - if(totalusers>20 && admin<100){ + if(totalusers>20 && adminOnline() >= CLE_Status::Zoning) && (!cle->GetGM() || cle->Anon() != 1 || admin >= cle->Admin()) && (whom == 0 || ( - ((cle->Admin() >= 80 && cle->GetGM()) || whom->gmlookup == 0xFFFF) && + ((cle->Admin() >= AccountStatus::QuestTroupe && cle->GetGM()) || whom->gmlookup == 0xFFFF) && (whom->lvllow == 0xFFFF || (cle->level() >= whom->lvllow && cle->level() <= whom->lvlhigh && (cle->Anon()==0 || admin>cle->Admin()))) && (whom->wclass == 0xFFFF || (cle->class_() == whom->wclass && (cle->Anon()==0 || admin>cle->Admin()))) && (whom->wrace == 0xFFFF || (cle->race() == whom->wrace && (cle->Anon()==0 || admin>cle->Admin()))) && @@ -646,60 +658,60 @@ void ClientList::SendWhoAll(uint32 fromid,const char* to, int16 admin, Who_All_S (tmpZone != 0 && strncasecmp(tmpZone, whom->whom, whomlen) == 0) || strncasecmp(cle->name(),whom->whom, whomlen) == 0 || (strncasecmp(guild_mgr.GetGuildName(cle->GuildID()), whom->whom, whomlen) == 0) || - (admin >= 100 && strncasecmp(cle->AccountName(), whom->whom, whomlen) == 0) + (admin >= AccountStatus::GMAdmin && strncasecmp(cle->AccountName(), whom->whom, whomlen) == 0) )) )) ) { line[0] = 0; - uint32 rankstring=0xFFFFFFFF; - if((cle->Anon()==1 && cle->GetGM() && cle->Admin()>admin) || (idx>=20 && admin<100)){ //hide gms that are anon from lesser gms and normal players, cut off at 20 - rankstring=0; + uint32 rankstring = 0xFFFFFFFF; + if((cle->Anon()==1 && cle->GetGM() && cle->Admin()>admin) || (idx>=20 && admin < AccountStatus::GMAdmin)){ //hide gms that are anon from lesser gms and normal players, cut off at 20 + rankstring = 0; iterator.Advance(); continue; } else if (cle->GetGM()) { - if (cle->Admin() >=250) - rankstring=5021; - else if (cle->Admin() >= 200) - rankstring=5020; - else if (cle->Admin() >= 180) - rankstring=5019; - else if (cle->Admin() >= 170) - rankstring=5018; - else if (cle->Admin() >= 160) - rankstring=5017; - else if (cle->Admin() >= 150) - rankstring=5016; - else if (cle->Admin() >= 100) - rankstring=5015; - else if (cle->Admin() >= 95) - rankstring=5014; - else if (cle->Admin() >= 90) - rankstring=5013; - else if (cle->Admin() >= 85) - rankstring=5012; - else if (cle->Admin() >= 81) - rankstring=5011; - else if (cle->Admin() >= 80) - rankstring=5010; - else if (cle->Admin() >= 50) - rankstring=5009; - else if (cle->Admin() >= 20) - rankstring=5008; - else if (cle->Admin() >= 10) - rankstring=5007; + if (cle->Admin() >= AccountStatus::GMImpossible) + rankstring = 5021; + else if (cle->Admin() >= AccountStatus::GMMgmt) + rankstring = 5020; + else if (cle->Admin() >= AccountStatus::GMCoder) + rankstring = 5019; + else if (cle->Admin() >= AccountStatus::GMAreas) + rankstring = 5018; + else if (cle->Admin() >= AccountStatus::QuestMaster) + rankstring = 5017; + else if (cle->Admin() >= AccountStatus::GMLeadAdmin) + rankstring = 5016; + else if (cle->Admin() >= AccountStatus::GMAdmin) + rankstring = 5015; + else if (cle->Admin() >= AccountStatus::GMStaff) + rankstring = 5014; + else if (cle->Admin() >= AccountStatus::EQSupport) + rankstring = 5013; + else if (cle->Admin() >= AccountStatus::GMTester) + rankstring = 5012; + else if (cle->Admin() >= AccountStatus::SeniorGuide) + rankstring = 5011; + else if (cle->Admin() >= AccountStatus::QuestTroupe) + rankstring = 5010; + else if (cle->Admin() >= AccountStatus::Guide) + rankstring = 5009; + else if (cle->Admin() >= AccountStatus::ApprenticeGuide) + rankstring = 5008; + else if (cle->Admin() >= AccountStatus::Steward) + rankstring = 5007; } idx++; char guildbuffer[67]={0}; if (cle->GuildID() != GUILD_NONE && cle->GuildID()>0) sprintf(guildbuffer,"<%s>", guild_mgr.GetGuildName(cle->GuildID())); uint32 formatstring=5025; - if(cle->Anon()==1 && (adminAdmin() || admin==0)) + if(cle->Anon()==1 && (adminAdmin() || admin == AccountStatus::Player)) formatstring=5024; - else if(cle->Anon()==1 && admin>=cle->Admin() && admin>0) + else if(cle->Anon()==1 && admin>=cle->Admin() && admin > AccountStatus::Player) formatstring=5022; - else if(cle->Anon()==2 && (adminAdmin() || admin==0)) + else if(cle->Anon()==2 && (adminAdmin() || admin == AccountStatus::Player)) formatstring=5023;//display guild - else if(cle->Anon()==2 && admin>=cle->Admin() && admin>0) + else if(cle->Anon()==2 && admin>=cle->Admin() && admin > AccountStatus::Player) formatstring=5022;//display everything //war* wars2 = (war*)pack2->pBuffer; @@ -711,10 +723,10 @@ void ClientList::SendWhoAll(uint32 fromid,const char* to, int16 admin, Who_All_S uint32 zonestring=0xFFFFFFFF; uint32 plzone=0; uint32 unknown80[2]; - if(cle->Anon()==0 || (admin>=cle->Admin() && admin>0)){ + if(cle->Anon()==0 || (admin>=cle->Admin() && admin> AccountStatus::Player)){ plclass_=cle->class_(); pllevel=cle->level(); - if(admin>=100) + if(admin>=AccountStatus::GMAdmin) pidstring=5003; plrace=cle->race(); zonestring=5006; @@ -722,7 +734,7 @@ void ClientList::SendWhoAll(uint32 fromid,const char* to, int16 admin, Who_All_S } - if(admin>=cle->Admin() && admin>0) + if(admin>=cle->Admin() && admin > AccountStatus::Player) unknown80[0]=cle->Admin(); else unknown80[0]=0xFFFFFFFF; @@ -735,7 +747,7 @@ void ClientList::SendWhoAll(uint32 fromid,const char* to, int16 admin, Who_All_S strcpy(plname,cle->name()); char placcount[30]={0}; - if(admin>=cle->Admin() && admin>0) + if(admin>=cle->Admin() && admin > AccountStatus::Player) strcpy(placcount,cle->AccountName()); memcpy(bufptr,&formatstring, sizeof(uint32)); @@ -1020,7 +1032,7 @@ void ClientList::ConsoleSendWhoAll(const char* to, int16 admin, Who_All_Struct* if ( (cle->Online() >= CLE_Status::Zoning) && (whom == 0 || ( - ((cle->Admin() >= 80 && cle->GetGM()) || whom->gmlookup == 0xFFFF) && + ((cle->Admin() >= AccountStatus::QuestTroupe && cle->GetGM()) || whom->gmlookup == 0xFFFF) && (whom->lvllow == 0xFFFF || (cle->level() >= whom->lvllow && cle->level() <= whom->lvlhigh)) && (whom->wclass == 0xFFFF || cle->class_() == whom->wclass) && (whom->wrace == 0xFFFF || cle->race() == whom->wrace) && @@ -1028,41 +1040,41 @@ void ClientList::ConsoleSendWhoAll(const char* to, int16 admin, Who_All_Struct* (tmpZone != 0 && strncasecmp(tmpZone, whom->whom, whomlen) == 0) || strncasecmp(cle->name(), whom->whom, whomlen) == 0 || (strncasecmp(guild_mgr.GetGuildName(cle->GuildID()), whom->whom, whomlen) == 0) || - (admin >= 100 && strncasecmp(cle->AccountName(), whom->whom, whomlen) == 0) + (admin >= AccountStatus::GMAdmin && strncasecmp(cle->AccountName(), whom->whom, whomlen) == 0) )) )) ) { line[0] = 0; // MYRA - use new (5.x) Status labels in who for telnet connection - if (cle->Admin() >= 250) + if (cle->Admin() >= AccountStatus::GMImpossible) strcpy(tmpgm, "* GM-Impossible * "); - else if (cle->Admin() >= 200) + else if (cle->Admin() >= AccountStatus::GMMgmt) strcpy(tmpgm, "* GM-Mgmt * "); - else if (cle->Admin() >= 180) + else if (cle->Admin() >= AccountStatus::GMCoder) strcpy(tmpgm, "* GM-Coder * "); - else if (cle->Admin() >= 170) + else if (cle->Admin() >= AccountStatus::GMAreas) strcpy(tmpgm, "* GM-Areas * "); - else if (cle->Admin() >= 160) + else if (cle->Admin() >= AccountStatus::QuestMaster) strcpy(tmpgm, "* QuestMaster * "); - else if (cle->Admin() >= 150) + else if (cle->Admin() >= AccountStatus::GMLeadAdmin) strcpy(tmpgm, "* GM-Lead Admin * "); - else if (cle->Admin() >= 100) + else if (cle->Admin() >= AccountStatus::GMAdmin) strcpy(tmpgm, "* GM-Admin * "); - else if (cle->Admin() >= 95) + else if (cle->Admin() >= AccountStatus::GMStaff) strcpy(tmpgm, "* GM-Staff * "); - else if (cle->Admin() >= 90) + else if (cle->Admin() >= AccountStatus::EQSupport) strcpy(tmpgm, "* EQ Support * "); - else if (cle->Admin() >= 85) + else if (cle->Admin() >= AccountStatus::GMTester) strcpy(tmpgm, "* GM-Tester * "); - else if (cle->Admin() >= 81) + else if (cle->Admin() >= AccountStatus::SeniorGuide) strcpy(tmpgm, "* Senior Guide * "); - else if (cle->Admin() >= 80) + else if (cle->Admin() >= AccountStatus::QuestTroupe) strcpy(tmpgm, "* QuestTroupe * "); - else if (cle->Admin() >= 50) + else if (cle->Admin() >= AccountStatus::Guide) strcpy(tmpgm, "* Guide * "); - else if (cle->Admin() >= 20) + else if (cle->Admin() >= AccountStatus::ApprenticeGuide) strcpy(tmpgm, "* Apprentice Guide * "); - else if (cle->Admin() >= 10) + else if (cle->Admin() >= AccountStatus::Steward) strcpy(tmpgm, "* Steward * "); else tmpgm[0] = 0; @@ -1079,16 +1091,16 @@ void ClientList::ConsoleSendWhoAll(const char* to, int16 admin, Who_All_Struct* else LFG[0] = 0; - if (admin >= 150 && admin >= cle->Admin()) { + if (admin >= AccountStatus::GMLeadAdmin && admin >= cle->Admin()) { sprintf(accinfo, " AccID: %i AccName: %s LSID: %i Status: %i", cle->AccountID(), cle->AccountName(), cle->LSAccountID(), cle->Admin()); } else accinfo[0] = 0; if (cle->Anon() == 2) { // Roleplay - if (admin >= 100 && admin >= cle->Admin()) + if (admin >= AccountStatus::GMAdmin && admin >= cle->Admin()) sprintf(line, " %s[RolePlay %i %s] %s (%s)%s zone: %s%s%s", tmpgm, cle->level(), GetClassIDName(cle->class_(), cle->level()), cle->name(), GetRaceIDName(cle->race()), tmpguild, tmpZone, LFG, accinfo); - else if (cle->Admin() >= 80 && admin < 80 && cle->GetGM()) { + else if (cle->Admin() >= AccountStatus::QuestTroupe && admin < AccountStatus::QuestTroupe && cle->GetGM()) { iterator.Advance(); continue; } @@ -1096,9 +1108,9 @@ void ClientList::ConsoleSendWhoAll(const char* to, int16 admin, Who_All_Struct* sprintf(line, " %s[ANONYMOUS] %s%s%s%s", tmpgm, cle->name(), tmpguild, LFG, accinfo); } else if (cle->Anon() == 1) { // Anon - if (admin >= 100 && admin >= cle->Admin()) + if (admin >= AccountStatus::GMAdmin && admin >= cle->Admin()) sprintf(line, " %s[ANON %i %s] %s (%s)%s zone: %s%s%s", tmpgm, cle->level(), GetClassIDName(cle->class_(), cle->level()), cle->name(), GetRaceIDName(cle->race()), tmpguild, tmpZone, LFG, accinfo); - else if (cle->Admin() >= 80 && cle->GetGM()) { + else if (cle->Admin() >= AccountStatus::QuestTroupe && cle->GetGM()) { iterator.Advance(); continue; } @@ -1111,7 +1123,13 @@ void ClientList::ConsoleSendWhoAll(const char* to, int16 admin, Who_All_Struct* fmt::format_to(out, line); if (out.size() >= 3584) { auto output = fmt::to_string(out); - connection->SendEmoteMessageRaw(to, 0, 0, 10, output.c_str()); + connection->SendEmoteMessageRaw( + to, + 0, + AccountStatus::Player, + Chat::NPCQuestSay, + output.c_str() + ); out.clear(); } else { @@ -1121,17 +1139,17 @@ void ClientList::ConsoleSendWhoAll(const char* to, int16 admin, Who_All_Struct* fmt::format_to(out, "\n"); } x++; - if (x >= 20 && admin < 80) + if (x >= 20 && admin < AccountStatus::QuestTroupe) break; } iterator.Advance(); } - if (x >= 20 && admin < 80) + if (x >= 20 && admin < AccountStatus::QuestTroupe) fmt::format_to(out, "too many results...20 players shown"); else fmt::format_to(out, "{} players online", x); - if (admin >= 150 && (whom == 0 || whom->gmlookup != 0xFFFF)) { + if (admin >= AccountStatus::GMAdmin && (whom == 0 || whom->gmlookup != 0xFFFF)) { if (connection->IsConsole()) fmt::format_to(out, "\r\n"); else @@ -1140,7 +1158,13 @@ void ClientList::ConsoleSendWhoAll(const char* to, int16 admin, Who_All_Struct* //console_list.SendConsoleWho(connection, to, admin, &output, &outsize, &outlen); } auto output = fmt::to_string(out); - connection->SendEmoteMessageRaw(to, 0, 0, 10, output.c_str()); + connection->SendEmoteMessageRaw( + to, + 0, + AccountStatus::Player, + Chat::NPCQuestSay, + output.c_str() + ); } void ClientList::Add(Client* client) { @@ -1386,7 +1410,7 @@ void ClientList::SendClientVersionSummary(const char *Name) zoneserver_list.SendEmoteMessage( Name, 0, - 0, + AccountStatus::Player, Chat::White, fmt::format( "There {} {} Titanium, {} SoF, {} SoD, {} UF, {} RoF, and {} RoF2 Client{} currently connected for a total of {} Client{} and {} Unique IP{} connected.", diff --git a/world/clientlist.h b/world/clientlist.h index ed66a11c2..2f9583784 100644 --- a/world/clientlist.h +++ b/world/clientlist.h @@ -62,7 +62,7 @@ public: void DisconnectByIP(uint32 iIP); void CLCheckStale(); void CLEKeepAlive(uint32 numupdates, uint32* wid); - void CLEAdd(uint32 iLSID, const char* iLoginServerName, const char* iLoginName, const char* iLoginKey, int16 iWorldAdmin = 0, uint32 ip = 0, uint8 local=0); + void CLEAdd(uint32 iLSID, const char* iLoginServerName, const char* iLoginName, const char* iLoginKey, int16 iWorldAdmin = AccountStatus::Player, uint32 ip = 0, uint8 local=0); void UpdateClientGuild(uint32 char_id, uint32 guild_id); void RemoveCLEByLSID(uint32 iLSID); bool IsAccountInGame(uint32 iLSID); diff --git a/world/console.cpp b/world/console.cpp index f2cce0000..2fa162b98 100644 --- a/world/console.cpp +++ b/world/console.cpp @@ -280,20 +280,33 @@ void ConsoleEmote( join_args.erase(join_args.begin(), join_args.begin() + 2); if (strcasecmp(args[0].c_str(), "world") == 0) { - zoneserver_list.SendEmoteMessageRaw(0, 0, 0, atoi(args[1].c_str()), JoinString(join_args, " ").c_str()); + zoneserver_list.SendEmoteMessageRaw( + 0, + 0, + AccountStatus::Player, + atoi(args[1].c_str()), + JoinString(join_args, " ").c_str() + ); } else { ZoneServer *zs = zoneserver_list.FindByName(args[0].c_str()); if (zs != 0) { - zs->SendEmoteMessageRaw(0, 0, 0, atoi(args[1].c_str()), JoinString(join_args, " ").c_str()); + zs->SendEmoteMessageRaw( + 0, + 0, + AccountStatus::Player, + atoi(args[1].c_str()), + JoinString(join_args, " ").c_str() + ); } else { zoneserver_list.SendEmoteMessageRaw( args[0].c_str(), 0, - 0, + AccountStatus::Player, atoi(args[1].c_str()), - JoinString(join_args, " ").c_str()); + JoinString(join_args, " ").c_str() + ); } } } @@ -637,7 +650,16 @@ void ConsoleZoneLock( uint16 tmp = ZoneID(args[1].c_str()); if (tmp) { if (zoneserver_list.SetLockedZone(tmp, true)) { - zoneserver_list.SendEmoteMessage(0, 0, 80, 15, "Zone locked: %s", ZoneName(tmp)); + zoneserver_list.SendEmoteMessage( + 0, + 0, + AccountStatus::QuestTroupe, + Chat::Yellow, + fmt::format( + "Zone locked: {}", + ZoneName(tmp) + ).c_str() + ); } else { connection->SendLine("Failed to change lock"); @@ -655,7 +677,16 @@ void ConsoleZoneLock( uint16 tmp = ZoneID(args[1].c_str()); if (tmp) { if (zoneserver_list.SetLockedZone(tmp, false)) { - zoneserver_list.SendEmoteMessage(0, 0, 80, 15, "Zone unlocked: %s", ZoneName(tmp)); + zoneserver_list.SendEmoteMessage( + 0, + 0, + AccountStatus::QuestTroupe, + Chat::Yellow, + fmt::format( + "Zone unlocked: {}", + ZoneName(tmp) + ).c_str() + ); } else { connection->SendLine("Failed to change lock"); @@ -782,7 +813,7 @@ void ConsoleWorldShutdown( zoneserver_list.SendEmoteMessage( 0, 0, - 0, + AccountStatus::Player, Chat::Yellow, "[SYSTEM] World shutdown has been aborted." ); diff --git a/world/console.old.cpp b/world/console.old.cpp index 5917b506f..0bc08899c 100644 --- a/world/console.old.cpp +++ b/world/console.old.cpp @@ -481,10 +481,10 @@ void Console::ProcessCommand(const char* command) { SendMessage(1, " version"); SendMessage(1, " worldshutdown"); } - if (admin >= 201) { + if (admin >= AccountStatus::GMMgmt) { SendMessage(1, " IPLookup [name]"); } - if (admin >= 100) { + if (admin >= AccountStatus::GMAdmin) { SendMessage(1, " LSReconnect"); SendMessage(1, " signalcharbyname charname ID"); SendMessage(1, " reloadworld"); @@ -799,7 +799,7 @@ void Console::ProcessCommand(const char* command) { SendMessage(1, " Compiled on: %s at %s", COMPILE_DATE, COMPILE_TIME); SendMessage(1, " Last modified on: %s", LAST_MODIFIED); } - else if (strcasecmp(sep.arg[0], "serverinfo") == 0 && admin >= 200) { + else if (strcasecmp(sep.arg[0], "serverinfo") == 0 && admin >= AccountStatus::GMMgmt) { if (strcasecmp(sep.arg[1], "os") == 0) { #ifdef _WINDOWS GetOS(); @@ -821,10 +821,10 @@ void Console::ProcessCommand(const char* command) { SendMessage(1, " OS - Operating system version information."); } } - else if (strcasecmp(sep.arg[0], "IPLookup") == 0 && admin >= 201) { + else if (strcasecmp(sep.arg[0], "IPLookup") == 0 && admin >= AccountStatus::GMMgmt) { client_list.SendCLEList(admin, 0, this, sep.argplus[1]); } - else if (strcasecmp(sep.arg[0], "LSReconnect") == 0 && admin >= 100) { + else if (strcasecmp(sep.arg[0], "LSReconnect") == 0 && admin >= AccountStatus::GMAdmin) { #ifdef _WINDOWS _beginthread(AutoInitLoginServer, 0, nullptr); #else @@ -839,7 +839,7 @@ void Console::ProcessCommand(const char* command) { if (strcasecmp(sep.arg[1], "list") == 0) { zoneserver_list.ListLockedZones(0, this); } - else if (strcasecmp(sep.arg[1], "lock") == 0 && admin >= 101) { + else if (strcasecmp(sep.arg[1], "lock") == 0 && admin >= AccountStatus::GMAdmin) { uint16 tmp = ZoneID(sep.arg[2]); if (tmp) { if (zoneserver_list.SetLockedZone(tmp, true)) @@ -850,7 +850,7 @@ void Console::ProcessCommand(const char* command) { else SendMessage(1, "Usage: #zonelock lock [zonename]"); } - else if (strcasecmp(sep.arg[1], "unlock") == 0 && admin >= 101) { + else if (strcasecmp(sep.arg[1], "unlock") == 0 && admin >= AccountStatus::GMAdmin) { uint16 tmp = ZoneID(sep.arg[2]); if (tmp) { if (zoneserver_list.SetLockedZone(tmp, false)) @@ -864,13 +864,13 @@ void Console::ProcessCommand(const char* command) { else { SendMessage(1, "#zonelock sub-commands"); SendMessage(1, " list"); - if (admin >= 101) { + if (admin >= AccountStatus::GMAdmin) { SendMessage(1, " lock [zonename]"); SendMessage(1, " unlock [zonename]"); } } } - else if (strcasecmp(sep.arg[0], "reloadworld") == 0 && admin > 101) + else if (strcasecmp(sep.arg[0], "reloadworld") == 0 && admin > AccountStatus::GMAdmin) { SendEmoteMessage(0,0,0,15,"Reloading World..."); auto pack = new ServerPacket(ServerOP_ReloadWorld, sizeof(ReloadWorld_Struct)); diff --git a/world/eqw.cpp b/world/eqw.cpp index 8d997edca..3b480836d 100644 --- a/world/eqw.cpp +++ b/world/eqw.cpp @@ -37,6 +37,7 @@ #include "launcher_list.h" #include "launcher_link.h" #include "wguild_mgr.h" +#include "../common/emu_constants.h" #ifdef seed #undef seed @@ -360,7 +361,13 @@ void EQW::ResolveBug(const char *id) { } void EQW::SendMessage(uint32 type, const char *msg) { - zoneserver_list.SendEmoteMessage(0, 0, 0, type, msg); + zoneserver_list.SendEmoteMessage( + 0, + 0, + AccountStatus::Player, + type, + msg + ); } void EQW::WorldShutDown(uint32 time, uint32 interval) { diff --git a/world/login_server.cpp b/world/login_server.cpp index 796438f35..edcdacf23 100644 --- a/world/login_server.cpp +++ b/world/login_server.cpp @@ -306,7 +306,13 @@ void LoginServer::ProcessSystemwideMessage(uint16_t opcode, EQ::Net::Packet &p) LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode); ServerSystemwideMessage *swm = (ServerSystemwideMessage *) p.Data(); - zoneserver_list.SendEmoteMessageRaw(0, 0, 0, swm->type, swm->message); + zoneserver_list.SendEmoteMessageRaw( + 0, + 0, + AccountStatus::Player, + swm->type, + swm->message + ); } void LoginServer::ProcessLSRemoteAddr(uint16_t opcode, EQ::Net::Packet &p) diff --git a/world/main.cpp b/world/main.cpp index 7d3da9bfd..1e7814a91 100644 --- a/world/main.cpp +++ b/world/main.cpp @@ -252,13 +252,15 @@ static void GMSayHookCallBackProcessWorld(uint16 log_category, std::string messa for (size_t iter = 0; iter < message_split.size(); ++iter) { zoneserver_list.SendEmoteMessage( - nullptr, 0, - 80, + 0, + AccountStatus::QuestTroupe, LogSys.GetGMSayColorFromCategory(log_category), - " %s%s", - (iter == 0 ? " ---" : ""), - message_split[iter].c_str() + fmt::format( + " {}{}", + (iter == 0 ? " ---" : ""), + message_split[iter] + ).c_str() ); } @@ -266,9 +268,9 @@ static void GMSayHookCallBackProcessWorld(uint16 log_category, std::string messa } zoneserver_list.SendEmoteMessage( - nullptr, 0, - 80, + 0, + AccountStatus::QuestTroupe, LogSys.GetGMSayColorFromCategory(log_category), "%s", message.c_str() diff --git a/world/world_event_scheduler.cpp b/world/world_event_scheduler.cpp index d32d44758..9b71a4bbd 100644 --- a/world/world_event_scheduler.cpp +++ b/world/world_event_scheduler.cpp @@ -45,7 +45,13 @@ void WorldEventScheduler::Process(ZSList *zs_list) if (ValidateEventReadyToActivate(e)) { if (e.event_type == ServerEvents::EVENT_TYPE_BROADCAST) { LogScheduler("Sending broadcast [{}]", e.event_data.c_str()); - zs_list->SendEmoteMessage(nullptr, 0, 0, 15, e.event_data.c_str()); + zs_list->SendEmoteMessage( + 0, + 0, + AccountStatus::Player, + Chat::Yellow, + e.event_data.c_str() + ); } if (e.event_type == ServerEvents::EVENT_TYPE_RELOAD_WORLD) { diff --git a/world/zonelist.cpp b/world/zonelist.cpp index a949a8bd5..c3aa358d3 100644 --- a/world/zonelist.cpp +++ b/world/zonelist.cpp @@ -51,19 +51,17 @@ ZSList::~ZSList() { void ZSList::ShowUpTime(WorldTCPConnection* con, const char* adminname) { uint32 ms = Timer::GetCurrentTime(); - uint32 d = ms / 86400000; - ms -= d * 86400000; - uint32 h = ms / 3600000; - ms -= h * 3600000; - uint32 m = ms / 60000; - ms -= m * 60000; - uint32 s = ms / 1000; - if (d) - con->SendEmoteMessage(adminname, 0, 0, 0, "Worldserver Uptime: %02id %02ih %02im %02is", d, h, m, s); - else if (h) - con->SendEmoteMessage(adminname, 0, 0, 0, "Worldserver Uptime: %02ih %02im %02is", h, m, s); - else - con->SendEmoteMessage(adminname, 0, 0, 0, "Worldserver Uptime: %02im %02is", m, s); + std::string time_string = ConvertSecondsToTime(ms); + con->SendEmoteMessage( + adminname, + 0, + AccountStatus::Player, + Chat::White, + fmt::format( + "Worldserver Uptime | {}", + time_string + ).c_str() + ); } void ZSList::Add(ZoneServer* zoneserver) { @@ -113,7 +111,7 @@ void ZSList::Process() { SendEmoteMessage( 0, 0, - 0, + AccountStatus::Player, Chat::Yellow, fmt::format( "[SYSTEM] World will be shutting down in {} minutes.", @@ -277,7 +275,7 @@ void ZSList::ListLockedZones(const char* to, WorldTCPConnection* connection) { connection->SendEmoteMessageRaw( to, 0, - EQ::constants::AccountStatus::Player, + AccountStatus::Player, Chat::White, fmt::format( "Zone {} | Name: {} ({}) ID: {}", @@ -299,7 +297,7 @@ void ZSList::ListLockedZones(const char* to, WorldTCPConnection* connection) { connection->SendEmoteMessage( to, 0, - EQ::constants::AccountStatus::Player, + AccountStatus::Player, Chat::White, zone_message.c_str() ); @@ -355,7 +353,7 @@ void ZSList::SendZoneStatus(const char* to, int16 admin, WorldTCPConnection* con else is_static_string[0] = 'D'; - if (admin >= 150) { + if (admin >= AccountStatus::GMLeadAdmin) { if (zone_server_data->GetZoneID()) { snprintf(zone_data_string, sizeof(zone_data_string), "%s (%i)", zone_server_data->GetZoneName(), zone_server_data->GetZoneID()); } @@ -381,7 +379,13 @@ void ZSList::SendZoneStatus(const char* to, int16 admin, WorldTCPConnection* con if (out.size() >= 3584) { auto output = fmt::to_string(out); - connection->SendEmoteMessageRaw(to, 0, 0, 10, output.c_str()); + connection->SendEmoteMessageRaw( + to, + 0, + AccountStatus::Player, + Chat::NPCQuestSay, + output.c_str() + ); out.clear(); } else { @@ -400,7 +404,13 @@ void ZSList::SendZoneStatus(const char* to, int16 admin, WorldTCPConnection* con fmt::format_to(out, " #{} {} {}", zone_server_data->GetID(), is_static_string, zone_data_string); if (out.size() >= 3584) { auto output = fmt::to_string(out); - connection->SendEmoteMessageRaw(to, 0, 0, 10, output.c_str()); + connection->SendEmoteMessageRaw( + to, + 0, + AccountStatus::Player, + Chat::NPCQuestSay, + output.c_str() + ); out.clear(); } else { @@ -427,7 +437,13 @@ void ZSList::SendZoneStatus(const char* to, int16 admin, WorldTCPConnection* con fmt::format_to(out, "{} zones are static zones, {} zones are booted zones, {} zones available.", z, w, v); auto output = fmt::to_string(out); - connection->SendEmoteMessageRaw(to, 0, 0, 10, output.c_str()); + connection->SendEmoteMessageRaw( + to, + 0, + AccountStatus::Player, + Chat::NPCQuestSay, + output.c_str() + ); } void ZSList::SendChannelMessage(const char* from, const char* to, uint8 chan_num, uint8 language, const char* message, ...) { @@ -558,20 +574,52 @@ void ZSList::SOPZoneBootup(const char* adminname, uint32 ZoneServerID, const cha ZoneServer* zs = 0; ZoneServer* zs2 = 0; uint32 zoneid; - if (!(zoneid = ZoneID(zonename))) - SendEmoteMessage(adminname, 0, 0, 0, "Error: SOP_ZoneBootup: zone '%s' not found in 'zone' table. Typo protection=ON.", zonename); - else { - if (ZoneServerID != 0) + if (!(zoneid = ZoneID(zonename))) { + SendEmoteMessage( + adminname, + 0, + AccountStatus::Player, + Chat::White, + fmt::format( + "Error: SOP_ZoneBootup: Zone '{}' not found in 'zone' table.", + zonename + ).c_str() + ); + } else { + if (ZoneServerID != 0) { zs = FindByID(ZoneServerID); - else - SendEmoteMessage(adminname, 0, 0, 0, "Error: SOP_ZoneBootup: ServerID must be specified"); + } else { + SendEmoteMessage( + adminname, + 0, + AccountStatus::Player, + Chat::White, + "Error: SOP_ZoneBootup: Server ID must be specified." + ); + } - if (zs == 0) - SendEmoteMessage(adminname, 0, 0, 0, "Error: SOP_ZoneBootup: zoneserver not found"); - else { + if (!zs) { + SendEmoteMessage( + adminname, + 0, + AccountStatus::Player, + Chat::White, + "Error: SOP_ZoneBootup: Zoneserver not found." + ); + } else { zs2 = FindByName(zonename); if (zs2 != 0) - SendEmoteMessage(adminname, 0, 0, 0, "Error: SOP_ZoneBootup: zone '%s' already being hosted by ZoneServer #%i", zonename, zs2->GetID()); + SendEmoteMessage( + adminname, + 0, + AccountStatus::Player, + Chat::White, + fmt::format( + "Error: SOP_ZoneBootup: Zone '{}' already being hosted by Zoneserver ID {}.", + zonename, + zs2->GetID() + ).c_str() + ); else { zs->TriggerBootup(zoneid, 0, adminname, iMakeStatic); } @@ -714,7 +762,7 @@ void ZSList::WorldShutDown(uint32 time, uint32 interval) SendEmoteMessage( 0, 0, - 0, + AccountStatus::Player, Chat::Yellow, fmt::format( "[SYSTEM] World will be shutting down in {} minutes.", @@ -733,7 +781,13 @@ void ZSList::WorldShutDown(uint32 time, uint32 interval) reminder->Start(); } else { - SendEmoteMessage(0, 0, 0, 15, "[SYSTEM] World is shutting down."); + SendEmoteMessage( + 0, + 0, + AccountStatus::Player, + Chat::Yellow, + "[SYSTEM] World is shutting down." + ); auto pack = new ServerPacket; pack->opcode = ServerOP_ShutdownAll; pack->size = 0; diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 7f11dc8b3..ae0b2682b 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -438,7 +438,16 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { safe_delete(pack); }))) && (!scm->noreply)) { - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "%s is not online at this time.", scm->to); + zoneserver_list.SendEmoteMessage( + scm->from, + 0, + AccountStatus::Player, + Chat::White, + fmt::format( + "{} is not online at this time.", + scm->to + ).c_str() + ); } } break; @@ -446,7 +455,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { ClientListEntry* cle = client_list.FindCharacter(scm->deliverto); if (cle == 0 || cle->Online() < CLE_Status::Zoning || - (cle->TellsOff() && ((cle->Anon() == 1 && scm->fromadmin < cle->Admin()) || scm->fromadmin < 80))) { + (cle->TellsOff() && ((cle->Anon() == 1 && scm->fromadmin < cle->Admin()) || scm->fromadmin < AccountStatus::QuestTroupe))) { if (!scm->noreply) { ClientListEntry* sender = client_list.FindCharacter(scm->from); if (!sender || !sender->Server()) @@ -491,7 +500,17 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { } else if (cle->Server() == 0) { if (!scm->noreply) - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not contactable at this time'", scm->to, scm->to); + zoneserver_list.SendEmoteMessage( + scm->from, + 0, + AccountStatus::Player, + Chat::White, + fmt::format( + "You told {}, '{} is not contactable at this time'", + scm->to, + scm->to + ).c_str() + ); } else cle->Server()->SendPacket(pack); @@ -518,29 +537,36 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { } case ServerOP_EmoteMessage: { ServerEmoteMessage_Struct* sem = (ServerEmoteMessage_Struct*)pack->pBuffer; - zoneserver_list.SendEmoteMessageRaw(sem->to, sem->guilddbid, sem->minstatus, sem->type, sem->message); + zoneserver_list.SendEmoteMessageRaw( + sem->to, + sem->guilddbid, + sem->minstatus, + sem->type, + sem->message + ); break; } case ServerOP_VoiceMacro: { - ServerVoiceMacro_Struct* svm = (ServerVoiceMacro_Struct*)pack->pBuffer; - if (svm->Type == VoiceMacroTell) { - ClientListEntry* cle = client_list.FindCharacter(svm->To); - if (!cle || (cle->Online() < CLE_Status::Zoning) || !cle->Server()) { - - zoneserver_list.SendEmoteMessage(svm->From, 0, 0, 0, "'%s is not online at this time'", svm->To); - + zoneserver_list.SendEmoteMessage( + svm->From, + 0, + AccountStatus::Player, + Chat::White, + fmt::format( + "'{} is not online at this time'", + svm->To + ).c_str() + ); break; } - cle->Server()->SendPacket(pack); - } - else + } else { zoneserver_list.SendPacket(pack); - + } break; } @@ -656,17 +682,31 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { case ServerOP_ZoneShutdown: { ServerZoneStateChange_struct* s = (ServerZoneStateChange_struct *)pack->pBuffer; ZoneServer* zs = 0; - if (s->ZoneServerID != 0) + if (s->ZoneServerID != 0) { zs = zoneserver_list.FindByID(s->ZoneServerID); - else if (s->zoneid != 0) + } else if (s->zoneid != 0) { zs = zoneserver_list.FindByName(ZoneName(s->zoneid)); - else - zoneserver_list.SendEmoteMessage(s->adminname, 0, 0, 0, "Error: SOP_ZoneShutdown: neither ID nor name specified"); + } else { + zoneserver_list.SendEmoteMessage( + s->adminname, + 0, + AccountStatus::Player, + Chat::White, + "Error: SOP_ZoneShutdown: neither ID nor name specified" + ); + } - if (zs == 0) - zoneserver_list.SendEmoteMessage(s->adminname, 0, 0, 0, "Error: SOP_ZoneShutdown: zoneserver not found"); - else + if (!zs) { + zoneserver_list.SendEmoteMessage( + s->adminname, + 0, + AccountStatus::Player, + Chat::White, + "Error: SOP_ZoneShutdown: zoneserver not found" + ); + } else { zs->SendPacket(pack); + } break; } case ServerOP_ZoneBootup: { @@ -717,7 +757,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { if (GetZoneID() == ztz->current_zone_id && GetInstanceID() == ztz->current_instance_id) { LogInfo("Processing ZTZ for egress from zone for client [{}]", ztz->name); - if (ztz->admin < 80 && ztz->ignorerestrictions < 2 && zoneserver_list.IsZoneLocked(ztz->requested_zone_id)) { + if (ztz->admin < AccountStatus::QuestTroupe && ztz->ignorerestrictions < 2 && zoneserver_list.IsZoneLocked(ztz->requested_zone_id)) { ztz->response = 0; SendPacket(pack); break; @@ -909,15 +949,43 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { ServerGMGoto_Struct* gmg = (ServerGMGoto_Struct*)pack->pBuffer; ClientListEntry* cle = client_list.FindCharacter(gmg->gotoname); if (cle != 0) { - if (cle->Server() == 0) - this->SendEmoteMessage(gmg->myname, 0, 0, 13, "Error: Cannot identify %s's zoneserver.", gmg->gotoname); - else if (cle->Anon() == 1 && cle->Admin() > gmg->admin) // no snooping for anon GMs - this->SendEmoteMessage(gmg->myname, 0, 0, 13, "Error: %s not found", gmg->gotoname); - else + if (!cle->Server()) { + SendEmoteMessage( + gmg->myname, + 0, + AccountStatus::Player, + Chat::Red, + fmt::format( + "Error: Cannot identify {}'s zoneserver.", + gmg->gotoname + ).c_str() + ); + } else if (cle->Anon() == 1 && cle->Admin() > gmg->admin) { // no snooping for anon GMs + SendEmoteMessage( + gmg->myname, + 0, + AccountStatus::Player, + Chat::Red, + fmt::format( + "Error: {} not found.", + gmg->gotoname + ).c_str() + ); + } else { cle->Server()->SendPacket(pack); + } } else { - this->SendEmoteMessage(gmg->myname, 0, 0, 13, "Error: %s not found", gmg->gotoname); + SendEmoteMessage( + gmg->myname, + 0, + AccountStatus::Player, + Chat::Red, + fmt::format( + "Error: {} not found", + gmg->gotoname + ).c_str() + ); } break; } @@ -933,16 +1001,28 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { WorldConfig::UnlockWorld(); if (loginserverlist.Connected()) { loginserverlist.SendStatus(); - if (slock->mode >= 1) - this->SendEmoteMessage(slock->myname, 0, 0, 13, "World locked"); - else - this->SendEmoteMessage(slock->myname, 0, 0, 13, "World unlocked"); + SendEmoteMessage( + slock->myname, + 0, + AccountStatus::Player, + Chat::Red, + fmt::format( + "World {}.", + slock->mode ? "locked" : "unlocked" + ).c_str() + ); } else { - if (slock->mode >= 1) - this->SendEmoteMessage(slock->myname, 0, 0, 13, "World locked, but login server not connected."); - else - this->SendEmoteMessage(slock->myname, 0, 0, 13, "World unlocked, but login server not conencted."); + SendEmoteMessage( + slock->myname, + 0, + AccountStatus::Player, + Chat::Red, + fmt::format( + "World {}, but login server not connected.", + slock->mode ? "locked" : "unlocked" + ).c_str() + ); } break; } @@ -953,7 +1033,6 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { } ServerMotd_Struct* smotd = (ServerMotd_Struct*)pack->pBuffer; database.SetVariable("MOTD", smotd->motd); - //this->SendEmoteMessage(smotd->myname, 0, 0, 13, "Updated Motd."); zoneserver_list.SendPacket(pack); break; } @@ -1024,22 +1103,22 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { } ServerLockZone_Struct* lock_zone = (ServerLockZone_Struct*) pack->pBuffer; - if (lock_zone->op == EQ::constants::ServerLockType::List) { + if (lock_zone->op == ServerLockType::List) { zoneserver_list.ListLockedZones(lock_zone->adminname, this); break; } else if ( - lock_zone->op == EQ::constants::ServerLockType::Lock || - lock_zone->op == EQ::constants::ServerLockType::Unlock + lock_zone->op == ServerLockType::Lock || + lock_zone->op == ServerLockType::Unlock ) { - if (zoneserver_list.SetLockedZone(lock_zone->zoneID, lock_zone->op == EQ::constants::ServerLockType::Lock)) { + if (zoneserver_list.SetLockedZone(lock_zone->zoneID, lock_zone->op == ServerLockType::Lock)) { zoneserver_list.SendEmoteMessage( 0, 0, - EQ::constants::AccountStatus::QuestTroupe, + AccountStatus::QuestTroupe, Chat::White, fmt::format( "Zone {} | Name: {} ({}) ID: {}", - lock_zone->op == EQ::constants::ServerLockType::Lock ? "Locked" : "Unlocked", + lock_zone->op == ServerLockType::Lock ? "Locked" : "Unlocked", ZoneLongName(lock_zone->zoneID), ZoneName(lock_zone->zoneID), lock_zone->zoneID @@ -1049,11 +1128,11 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { SendEmoteMessageRaw( lock_zone->adminname, 0, - EQ::constants::AccountStatus::Player, + AccountStatus::Player, Chat::White, fmt::format( "Zone Failed to {} | Name: {} ({}) ID: {}", - lock_zone->op == EQ::constants::ServerLockType::Lock ? "Lock" : "Unlock", + lock_zone->op == ServerLockType::Lock ? "Lock" : "Unlock", ZoneLongName(lock_zone->zoneID), ZoneName(lock_zone->zoneID), lock_zone->zoneID @@ -1404,7 +1483,13 @@ void ZoneServer::SendEmoteMessage(const char* to, uint32 to_guilddbid, int16 to_ va_start(argptr, message); vsnprintf(buffer, sizeof(buffer), message, argptr); va_end(argptr); - SendEmoteMessageRaw(to, to_guilddbid, to_minstatus, type, buffer); + SendEmoteMessageRaw( + to, + to_guilddbid, + to_minstatus, + type, + buffer + ); } void ZoneServer::SendEmoteMessageRaw(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message) { diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index bd826078b..fd476e83b 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1652,47 +1652,68 @@ int bot_command_real_dispatch(Client *c, const char *message) void bot_command_log_command(Client *c, const char *message) { - int admin = c->Admin(); +int admin = c->Admin(); bool continueevents = false; - switch (zone->loglevelvar) { //catch failsafe - case 9: // log only LeadGM - if ((admin >= 150) && (admin <200)) + switch (zone->loglevelvar){ //catch failsafe + case 9: { // log only LeadGM + if ( + admin >= AccountStatus::GMLeadAdmin && + admin < AccountStatus::GMMgmt + ) { + continueevents = true; + } + break; + } + case 8: { // log only GM + if ( + admin >= AccountStatus::GMAdmin && + admin < AccountStatus::GMLeadAdmin + ) { + continueevents = true; + } + break; + } + case 1: { + if (admin >= AccountStatus::GMMgmt) { + continueevents = true; + } + break; + } + case 2: { + if (admin >= AccountStatus::GMLeadAdmin) { + continueevents = true; + } + break; + } + case 3: { + if (admin >= AccountStatus::GMAdmin) { + continueevents = true; + } + break; + } + case 4: { + if (admin >= AccountStatus::QuestTroupe) { + continueevents = true; + } + break; + } + case 5: { + if (admin >= AccountStatus::ApprenticeGuide) { + continueevents = true; + } + break; + } + case 6: { + if (admin >= AccountStatus::Steward) { + continueevents = true; + } + break; + } + case 7: { continueevents = true; - break; - case 8: // log only GM - if ((admin >= 100) && (admin <150)) - continueevents = true; - break; - case 1: - if ((admin >= 200)) - continueevents = true; - break; - case 2: - if ((admin >= 150)) - continueevents = true; - break; - case 3: - if ((admin >= 100)) - continueevents = true; - break; - case 4: - if ((admin >= 80)) - continueevents = true; - break; - case 5: - if ((admin >= 20)) - continueevents = true; - break; - case 6: - if ((admin >= 10)) - continueevents = true; - break; - case 7: - continueevents = true; - break; - default: - break; + break; + } } if (continueevents) @@ -3382,7 +3403,7 @@ void bot_command_heal_rotation(Client *c, const Seperator *sep) return; #if (EQDEBUG >= 12) - while (c->Admin() >= 250) { + while (c->Admin() >= AccountStatus::GMImpossible) { if (strcasecmp(sep->arg[1], "shone")) { break; } Bot* my_bot = ActionableBots::AsTarget_ByBot(c); if (!my_bot || !(my_bot->IsHealRotationMember())) { break; } diff --git a/zone/client.cpp b/zone/client.cpp index 1eadd3fcc..88f63508f 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -200,7 +200,7 @@ Client::Client(EQStreamInterface* ieqs) TrackingID = 0; WID = 0; account_id = 0; - admin = 0; + admin = AccountStatus::Player; lsaccountid = 0; guild_id = GUILD_NONE; guildrank = 0; @@ -1013,7 +1013,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s else return; } - if(worldserver.IsOOCMuted() && admin < 100) + if(worldserver.IsOOCMuted() && admin < AccountStatus::GMAdmin) { Message(0,"OOC has been muted. Try again later."); return; @@ -1052,7 +1052,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s } case ChatChannel_Broadcast: /* Broadcast */ case ChatChannel_GMSAY: { /* GM Say */ - if (!(admin >= 80)) + if (!(admin >= AccountStatus::QuestTroupe)) Message(0, "Error: Only GMs can use this channel"); else if (!worldserver.SendChannelMessage(this, targetname, chan_num, 0, language, lang_skill, message)) Message(0, "Error: World server disconnected"); @@ -1230,7 +1230,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s } void Client::ChannelMessageSend(const char* from, const char* to, uint8 chan_num, uint8 language, uint8 lang_skill, const char* message, ...) { - if ((chan_num==11 && !(this->GetGM())) || (chan_num==10 && this->Admin()<80)) // dont need to send /pr & /petition to everybody + if ((chan_num==11 && !(this->GetGM())) || (chan_num==10 && this->Admin() < AccountStatus::QuestTroupe)) // dont need to send /pr & /petition to everybody return; va_list argptr; char buffer[4096]; @@ -2647,7 +2647,7 @@ void Client::GMKill() { } bool Client::CheckAccess(int16 iDBLevel, int16 iDefaultLevel) { - if ((admin >= iDBLevel) || (iDBLevel == 255 && admin >= iDefaultLevel)) + if ((admin >= iDBLevel) || (iDBLevel == AccountStatus::Max && admin >= iDefaultLevel)) return true; else return false; @@ -7007,7 +7007,7 @@ void Client::SendStatsWindow(Client* client, bool use_window) Extra_Info: client->Message(Chat::White, " BaseRace: %i Gender: %i BaseGender: %i Texture: %i HelmTexture: %i", GetBaseRace(), GetGender(), GetBaseGender(), GetTexture(), GetHelmTexture()); - if (client->Admin() >= 100) { + if (client->Admin() >= AccountStatus::GMAdmin) { client->Message(Chat::White, " CharID: %i EntityID: %i PetID: %i OwnerID: %i AIControlled: %i Targetted: %i", CharacterID(), GetID(), GetPetID(), GetOwnerID(), IsAIControlled(), targeted); } } @@ -8566,14 +8566,25 @@ void Client::ExpeditionSay(const char *str, int ExpID) { return; if(results.RowCount() == 0) { - this->Message(Chat::Lime, "You say to the expedition, '%s'", str); + Message(Chat::Lime, "You say to the expedition, '%s'", str); return; } for(auto row = results.begin(); row != results.end(); ++row) { const char* charName = row[0]; - if(strcmp(charName, this->GetCleanName()) != 0) - worldserver.SendEmoteMessage(charName, 0, 0, 14, "%s says to the expedition, '%s'", this->GetCleanName(), str); + if(strcmp(charName, GetCleanName()) != 0) { + worldserver.SendEmoteMessage( + charName, + 0, + AccountStatus::Player, + Chat::Lime, + fmt::format( + "{} says to the expedition, '{}'", + GetCleanName(), + str + ).c_str() + ); + } // ChannelList->CreateChannel(ChannelName, ChannelOwner, ChannelPassword, true, atoi(row[3])); } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 2372dd31c..b140934cb 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -4756,7 +4756,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { MakeSpawnUpdate(position_update); if (gm_hide_me) { - entity_list.QueueClientsStatus(this, outapp, true, Admin(), 255); + entity_list.QueueClientsStatus(this, outapp, true, Admin(), AccountStatus::Max); } else { entity_list.QueueCloseClients(this, outapp, true, RuleI(Range, ClientPositionUpdates), nullptr, true); } @@ -6667,7 +6667,7 @@ void Client::Handle_OP_GMZoneRequest(const EQApplicationPacket *app) GMZoneRequest_Struct* gmzr = (GMZoneRequest_Struct*)app->pBuffer; float target_x = -1, target_y = -1, target_z = -1, target_heading; - int16 min_status = 0; + int16 min_status = AccountStatus::Player; uint8 min_level = 0; char target_zone[32]; uint16 zone_id = gmzr->zone_id; @@ -7797,16 +7797,30 @@ void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app) { //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()); + worldserver.SendEmoteMessage( + gj->inviter, + 0, + Chat::White, + fmt::format( + "{} has declined to join the guild.", + GetCleanName() + ).c_str() + ); 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()); - + worldserver.SendEmoteMessage( + gj->inviter, + 0, + Chat::White, + fmt::format( + "{} has declined to join the guild.", + GetCleanName() + ).c_str() + ); return; } @@ -7838,7 +7852,15 @@ void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app) //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, guildrank)) { - worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has sent an invalid response to your invite!", GetName()); + worldserver.SendEmoteMessage( + gj->inviter, + 0, + Chat::White, + fmt::format( + "{} has sent an invalid response to your invite!", + GetCleanName() + ).c_str() + ); Message(Chat::Red, "Invalid invite response packet!"); return; } @@ -10913,7 +10935,17 @@ void Client::Handle_OP_Petition(const EQApplicationPacket *app) database.InsertPetitionToDB(pet); petition_list.UpdateGMQueue(); petition_list.UpdateZoneListQueue(); - worldserver.SendEmoteMessage(0, 0, 80, 15, "%s has made a petition. #%i", GetName(), pet->GetID()); + worldserver.SendEmoteMessage( + 0, + 0, + AccountStatus::QuestTroupe, + Chat::Yellow, + fmt::format( + "{} has made a petition. ID: {}", + GetCleanName(), + pet->GetID() + ).c_str() + ); } return; } @@ -15300,7 +15332,7 @@ void Client::Handle_OP_YellForHelp(const EQApplicationPacket *app) void Client::Handle_OP_ResetAA(const EQApplicationPacket *app) { - if (Admin() >= 50) { + if (Admin() >= AccountStatus::Guide) { Message(0, "Resetting AA points."); ResetAA(); } diff --git a/zone/client_process.cpp b/zone/client_process.cpp index d4108a819..c014c5f0e 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -533,7 +533,7 @@ bool Client::Process() { OnDisconnect(true); LogInfo("Client linkdead: {}", name); - if (Admin() > 100) { + if (Admin() > AccountStatus::GMAdmin) { if (GetMerc()) { GetMerc()->Save(); GetMerc()->Depop(); @@ -1729,7 +1729,7 @@ void Client::OPGMSummon(const EQApplicationPacket *app) } else { - if(admin < 80) + if(admin < AccountStatus::QuestTroupe) { return; } diff --git a/zone/command.cpp b/zone/command.cpp index 0e8f204aa..e33e37cf9 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -162,310 +162,310 @@ int command_init(void) commandaliases.clear(); if ( - command_add("acceptrules", "[acceptrules] - Accept the EQEmu Agreement", 0, command_acceptrules) || - command_add("advnpcspawn", "[maketype|makegroup|addgroupentry|addgroupspawn][removegroupspawn|movespawn|editgroupbox|cleargroupbox]", 150, command_advnpcspawn) || - command_add("aggro", "(range) [-v] - Display aggro information for all mobs 'range' distance from your target. -v is verbose faction info.", 80, command_aggro) || - command_add("aggrozone", "[aggro] - Aggro every mob in the zone with X aggro. Default is 0. Not recommend if you're not invulnerable.", 100, command_aggrozone) || - command_add("ai", "[factionid/spellslist/con/guard/roambox/stop/start] - Modify AI on NPC target", 100, command_ai) || - command_add("appearance", "[type] [value] - Send an appearance packet for you or your target", 150, command_appearance) || - command_add("apply_shared_memory", "[shared_memory_name] - Tells every zone and world to apply a specific shared memory segment by name.", 250, command_apply_shared_memory) || - command_add("attack", "[targetname] - Make your NPC target attack targetname", 150, command_attack) || - command_add("augmentitem", "Force augments an item. Must have the augment item window open.", 250, command_augmentitem) || - command_add("ban", "[name] [reason]- Ban by character name", 150, command_ban) || - command_add("beard", "- Change the beard of your target", 80, command_beard) || - command_add("beardcolor", "- Change the beard color of your target", 80, command_beardcolor) || - command_add("bestz", "- Ask map for a good Z coord for your x,y coords.", 0, command_bestz) || - command_add("bind", "- Sets your targets bind spot to their current location", 200, command_bind) || + command_add("acceptrules", "[acceptrules] - Accept the EQEmu Agreement", AccountStatus::Player, command_acceptrules) || + command_add("advnpcspawn", "[maketype|makegroup|addgroupentry|addgroupspawn][removegroupspawn|movespawn|editgroupbox|cleargroupbox]", AccountStatus::GMLeadAdmin, command_advnpcspawn) || + command_add("aggro", "(range) [-v] - Display aggro information for all mobs 'range' distance from your target. -v is verbose faction info.", AccountStatus::QuestTroupe, command_aggro) || + command_add("aggrozone", "[aggro] - Aggro every mob in the zone with X aggro. Default is 0. Not recommend if you're not invulnerable.", AccountStatus::GMAdmin, command_aggrozone) || + command_add("ai", "[factionid/spellslist/con/guard/roambox/stop/start] - Modify AI on NPC target", AccountStatus::GMAdmin, command_ai) || + command_add("appearance", "[type] [value] - Send an appearance packet for you or your target", AccountStatus::GMLeadAdmin, command_appearance) || + command_add("apply_shared_memory", "[shared_memory_name] - Tells every zone and world to apply a specific shared memory segment by name.", AccountStatus::GMImpossible, command_apply_shared_memory) || + command_add("attack", "[targetname] - Make your NPC target attack targetname", AccountStatus::GMLeadAdmin, command_attack) || + command_add("augmentitem", "Force augments an item. Must have the augment item window open.", AccountStatus::GMImpossible, command_augmentitem) || + command_add("ban", "[name] [reason]- Ban by character name", AccountStatus::GMLeadAdmin, command_ban) || + command_add("beard", "- Change the beard of your target", AccountStatus::QuestTroupe, command_beard) || + command_add("beardcolor", "- Change the beard color of your target", AccountStatus::QuestTroupe, command_beardcolor) || + command_add("bestz", "- Ask map for a good Z coord for your x,y coords.", AccountStatus::Player, command_bestz) || + command_add("bind", "- Sets your targets bind spot to their current location", AccountStatus::GMMgmt, command_bind) || #ifdef BOTS - command_add("bot", "- Type \"#bot help\" or \"^help\" to the see the list of available commands for bots.", 0, command_bot) || + command_add("bot", "- Type \"#bot help\" or \"^help\" to the see the list of available commands for bots.", AccountStatus::Player, command_bot) || #endif - command_add("camerashake", "Shakes the camera on everyone's screen globally.", 80, command_camerashake) || - command_add("castspell", "[Spell ID] [Instant (0 = False, 1 = True, Default is 1 if Unused)] - Cast a spell", 50, command_castspell) || - command_add("chat", "[channel num] [message] - Send a channel message to all zones", 200, command_chat) || - command_add("checklos", "- Check for line of sight to your target", 50, command_checklos) || - command_add("copycharacter", "[source_char_name] [dest_char_name] [dest_account_name] Copies character to destination account", 250, command_copycharacter) || - command_add("corpse", "- Manipulate corpses, use with no arguments for help", 50, command_corpse) || - command_add("corpsefix", "Attempts to bring corpses from underneath the ground within close proximity of the player", 0, command_corpsefix) || - command_add("cvs", "- Summary of client versions currently online.", 200, command_cvs) || - command_add("damage", "[amount] - Damage your target", 100, command_damage) || - command_add("databuckets", "View|Delete [key] [limit]- View data buckets, limit 50 default or Delete databucket by key", 80, command_databuckets) || - command_add("date", "[yyyy] [mm] [dd] [HH] [MM] - Set EQ time", 90, command_date) || - command_add("dbspawn2", "[spawngroup] [respawn] [variance] - Spawn an NPC from a predefined row in the spawn2 table", 100, command_dbspawn2) || - command_add("delacct", "[accountname] - Delete an account", 150, command_delacct) || - command_add("deletegraveyard", "[zone name] - Deletes the graveyard for the specified zone.", 200, command_deletegraveyard) || - command_add("delpetition", "[petition number] - Delete a petition", 20, command_delpetition) || - command_add("depop", "- Depop your NPC target", 50, command_depop) || - command_add("depopzone", "- Depop the zone", 100, command_depopzone) || - command_add("devtools", "- Manages devtools", 200, command_devtools) || - command_add("details", "- Change the details of your target (Drakkin Only)", 80, command_details) || - command_add("disablerecipe", "[recipe_id] - Disables a recipe using the recipe id.", 80, command_disablerecipe) || - command_add("disarmtrap", "Analog for ldon disarm trap for the newer clients since we still don't have it working.", 80, command_disarmtrap) || - command_add("distance", "- Reports the distance between you and your target.", 80, command_distance) || - command_add("door", "Door editing command", 80, command_door) || - command_add("doanim", "[animnum] [type] - Send an EmoteAnim for you or your target", 50, command_doanim) || - command_add("dye", "[slot|'help'] [red] [green] [blue] [use_tint] - Dyes the specified armor slot to Red, Green, and Blue provided, allows you to bypass darkness limits.", 20, command_dye) || - command_add("dz", "Manage expeditions and dynamic zone instances", 80, command_dz) || - command_add("dzkickplayers", "Removes all players from current expedition. (/kickplayers alternative for pre-RoF clients)", 0, command_dzkickplayers) || - command_add("editmassrespawn", "[name-search] [second-value] - Mass (Zone wide) NPC respawn timer editing command", 100, command_editmassrespawn) || - command_add("emote", "['name'/'world'/'zone'] [type] [message] - Send an emote message", 80, command_emote) || - command_add("emotesearch", "Searches NPC Emotes", 80, command_emotesearch) || - command_add("emoteview", "Lists all NPC Emotes", 80, command_emoteview) || - command_add("emptyinventory", "- Clears you or your target's entire inventory (Equipment, General, Bank, and Shared Bank)", 250, command_emptyinventory) || - command_add("enablerecipe", "[recipe_id] - Enables a recipe using the recipe id.", 80, command_enablerecipe) || - command_add("endurance", "Restores you or your target's endurance.", 50, command_endurance) || - command_add("equipitem", "[slotid(0-21)] - Equip the item on your cursor into the specified slot", 50, command_equipitem) || - command_add("face", "- Change the face of your target", 80, command_face) || - command_add("faction", "[Find (criteria | all ) | Review (criteria | all) | Reset (id)] - Resets Player's Faction", 80, command_faction) || - command_add("findaliases", "[search criteria]- Searches for available command aliases, by alias or command", 0, command_findaliases) || - command_add("findclass", "[search criteria] - Search for a class", 50, command_findclass) || - command_add("findfaction", "[search criteria] - Search for a faction", 50, command_findfaction) || - command_add("findnpctype", "[search criteria] - Search database NPC types", 100, command_findnpctype) || - command_add("findrace", "[search criteria] - Search for a race", 50, command_findrace) || - command_add("findskill", "[search criteria] - Search for a skill", 50, command_findskill) || - command_add("findspell", "[search criteria] - Search for a spell", 50, command_findspell) || - command_add("findtask", "[search criteria] - Search for a task", 50, command_findtask) || - command_add("findzone", "[search criteria] - Search database zones", 100, command_findzone) || - command_add("fixmob", "[race|gender|texture|helm|face|hair|haircolor|beard|beardcolor|heritage|tattoo|detail] [next|prev] - Manipulate appearance of your target", 80, command_fixmob) || - command_add("flag", "[status] [acctname] - Refresh your admin status, or set an account's admin status if arguments provided", 0, command_flag) || - command_add("flagedit", "- Edit zone flags on your target", 100, command_flagedit) || - command_add("flags", "- displays the flags of you or your target", 0, command_flags) || - command_add("flymode", "[0/1/2/3/4/5] - Set your or your player target's flymode to ground/flying/levitate/water/floating/levitate_running", 50, command_flymode) || - command_add("fov", "- Check wether you're behind or in your target's field of view", 80, command_fov) || - command_add("freeze", "- Freeze your target", 80, command_freeze) || - command_add("gassign", "[id] - Assign targetted NPC to predefined wandering grid id", 100, command_gassign) || - command_add("gearup", "Developer tool to quickly equip a character", 200, command_gearup) || - command_add("gender", "[0/1/2] - Change your or your target's gender to male/female/neuter", 50, command_gender) || - command_add("getplayerburiedcorpsecount", "- Get the target's total number of buried player corpses.", 100, command_getplayerburiedcorpsecount) || - command_add("getvariable", "[varname] - Get the value of a variable from the database", 200, command_getvariable) || - command_add("ginfo", "- get group info on target.", 20, command_ginfo) || - command_add("giveitem", "[itemid] [charges] - Summon an item onto your target's cursor. Charges are optional.", 200, command_giveitem) || - command_add("givemoney", "[pp] [gp] [sp] [cp] - Gives specified amount of money to the target player.", 200, command_givemoney) || - command_add("globalview", "Lists all qglobals in cache if you were to do a quest with this target.", 80, command_globalview) || - command_add("gm", "- Turn player target's or your GM flag on or off", 80, command_gm) || - command_add("gmspeed", "[on/off] - Turn GM speed hack on/off for you or your player target", 100, command_gmspeed) || - command_add("gmzone", "[zone_short_name] [zone_version=0] [identifier=gmzone] - Zones to a private GM instance", 100, command_gmzone) || - command_add("goto", "[playername] or [x y z] [h] - Teleport to the provided coordinates or to your target", 10, command_goto) || - command_add("grid", "[add/delete] [grid_num] [wandertype] [pausetype] - Create/delete a wandering grid", 170, command_grid) || - command_add("guild", "- Guild manipulation commands. Use argument help for more info.", 10, command_guild) || - command_add("guildapprove", "[guildapproveid] - Approve a guild with specified ID (guild creator receives the id)", 0, command_guildapprove) || - command_add("guildcreate", "[guildname] - Creates an approval setup for guild name specified", 0, command_guildcreate) || - command_add("guildlist", "[guildapproveid] - Lists character names who have approved the guild specified by the approve id", 0, command_guildlist) || - command_add("hair", "- Change the hair style of your target", 80, command_hair) || - command_add("haircolor", "- Change the hair color of your target", 80, command_haircolor) || - command_add("haste", "[percentage] - Set your haste percentage", 100, command_haste) || - command_add("hatelist", " - Display hate list for target.", 80, command_hatelist) || - command_add("heal", "- Completely heal your target", 10, command_heal) || - command_add("helm", "- Change the helm of your target", 80, command_helm) || - command_add("help", "[search term] - List available commands and their description, specify partial command as argument to search", 0, command_help) || - command_add("heritage", "- Change the heritage of your target (Drakkin Only)", 80, command_heritage) || - command_add("heromodel", "[hero model] [slot] - Full set of Hero's Forge Armor appearance. If slot is set, sends exact model just to slot.", 200, command_heromodel) || - command_add("hideme", "[on/off] - Hide yourself from spawn lists.", 80, command_hideme) || - command_add("hotfix", "[hotfix_name] - Reloads shared memory into a hotfix, equiv to load_shared_memory followed by apply_shared_memory", 250, command_hotfix) || - command_add("hp", "- Refresh your HP bar from the server.", 0, command_hp) || - command_add("incstat", "- Increases or Decreases a client's stats permanently.", 200, command_incstat) || - command_add("instance", "- Modify Instances", 200, command_instance) || - command_add("interrogateinv", "- use [help] argument for available options", 0, command_interrogateinv) || - command_add("interrupt", "[message id] [color] - Interrupt your casting. Arguments are optional.", 50, command_interrupt) || - command_add("invsnapshot", "- Manipulates inventory snapshots for your current target", 80, command_invsnapshot) || - command_add("invul", "[on/off] - Turn player target's or your invulnerable flag on or off", 80, command_invul) || - command_add("ipban", "[IP address] - Ban IP by character name", 200, command_ipban) || - command_add("iplookup", "[charname] - Look up IP address of charname", 200, command_iplookup) || - command_add("iteminfo", "- Get information about the item on your cursor", 10, command_iteminfo) || - command_add("itemsearch", "[search criteria] - Search for an item", 10, command_itemsearch) || - command_add("kick", "[charname] - Disconnect charname", 150, command_kick) || - command_add("kill", "- Kill your target", 100, command_kill) || - command_add("killallnpcs", " [npc_name] Kills all npcs by search name, leave blank for all attackable NPC's", 200, command_killallnpcs) || - command_add("lastname", "[new lastname] - Set your or your player target's lastname", 50, command_lastname) || - command_add("level", "[level] - Set your or your target's level", 10, command_level) || - command_add("list", "[npcs|players|corpses|doors|objects] [search] - Search entities", 20, command_list) || - command_add("listpetition", "- List petitions", 50, command_listpetition) || - command_add("load_shared_memory", "[shared_memory_name] - Reloads shared memory and uses the input as output", 250, command_load_shared_memory) || - command_add("loc", "- Print out your or your target's current location and heading", 0, command_loc) || - command_add("lock", "- Lock the worldserver", 150, command_lock) || - command_add("logs", "Manage anything to do with logs", 250, command_logs) || - command_add("makepet", "[level] [class] [race] [texture] - Make a pet", 50, command_makepet) || - command_add("mana", "- Fill your or your target's mana", 50, command_mana) || - command_add("maxskills", "Maxes skills for you.", 200, command_max_all_skills) || - command_add("memspell", "[slotid] [spellid] - Memorize spellid in the specified slot", 50, command_memspell) || - command_add("merchant_close_shop", "Closes a merchant shop", 100, command_merchantcloseshop) || - command_add("merchant_open_shop", "Opens a merchants shop", 100, command_merchantopenshop) || - command_add("modifynpcstat", "- Modifys a NPC's stats", 150, command_modifynpcstat) || - command_add("motd", "[new motd] - Set message of the day", 150, command_motd) || - command_add("movechar", "[charname] [zonename] - Move charname to zonename", 50, command_movechar) || - command_add("movement", "Various movement commands", 200, command_movement) || - command_add("myskills", "- Show details about your current skill levels", 0, command_myskills) || - command_add("mysql", "Mysql CLI, see 'help' for options.", 250, command_mysql) || - command_add("mystats", "- Show details about you or your pet", 50, command_mystats) || - command_add("name", "[newname] - Rename your player target", 150, command_name) || - command_add("netstats", "- Gets the network stats for a stream.", 200, command_netstats) || - command_add("network", "- Admin commands for the udp network interface.", 250, command_network) || - command_add("npccast", "[targetname/entityid] [spellid] - Causes NPC target to cast spellid on targetname/entityid", 80, command_npccast) || - command_add("npcedit", "[column] [value] - Mega NPC editing command", 100, command_npcedit) || - command_add("npceditmass", "[name-search] [column] [value] - Mass (Zone wide) NPC data editing command", 100, command_npceditmass) || - command_add("npcemote", "[message] - Make your NPC target emote a message.", 150, command_npcemote) || - command_add("npcloot", "[show/money/add/remove] [itemid/all/money: pp gp sp cp] - Manipulate the loot an NPC is carrying", 80, command_npcloot) || - command_add("npcsay", "[message] - Make your NPC target say a message.", 150, command_npcsay) || - command_add("npcshout", "[message] - Make your NPC target shout a message.", 150, command_npcshout) || - command_add("npcspawn", "[create/add/update/remove/delete] - Manipulate spawn DB", 170, command_npcspawn) || - command_add("npcspecialattk", "[flagchar] [perm] - Set NPC special attack flags. Flags are E(nrage) F(lurry) R(ampage) S(ummon).", 80, command_npcspecialattk) || - command_add("npcstats", "- Show stats about target NPC", 80, command_npcstats) || - command_add("npctype_cache", "[id] or all - Clears the npc type cache for either the id or all npcs.", 250, command_npctype_cache) || - command_add("npctypespawn", "[npctypeid] [factionid] - Spawn an NPC from the db", 10, command_npctypespawn) || - command_add("nudge", "- Nudge your target's current position by specific values", 80, command_nudge) || - command_add("nukebuffs", "- Strip all buffs on you or your target", 50, command_nukebuffs) || - command_add("nukeitem", "[itemid] - Remove itemid from your player target's inventory", 150, command_nukeitem) || - command_add("object", "List|Add|Edit|Move|Rotate|Copy|Save|Undo|Delete - Manipulate static and tradeskill objects within the zone", 100, command_object) || - command_add("oocmute", "[1/0] - Mutes OOC chat", 200, command_oocmute) || - command_add("opcode", "- opcode management", 250, command_opcode) || + command_add("camerashake", "Shakes the camera on everyone's screen globally.", AccountStatus::QuestTroupe, command_camerashake) || + command_add("castspell", "[Spell ID] [Instant (0 = False, 1 = True, Default is 1 if Unused)] - Cast a spell", AccountStatus::Guide, command_castspell) || + command_add("chat", "[channel num] [message] - Send a channel message to all zones", AccountStatus::GMMgmt, command_chat) || + command_add("checklos", "- Check for line of sight to your target", AccountStatus::Guide, command_checklos) || + command_add("copycharacter", "[source_char_name] [dest_char_name] [dest_account_name] Copies character to destination account", AccountStatus::GMImpossible, command_copycharacter) || + command_add("corpse", "- Manipulate corpses, use with no arguments for help", AccountStatus::Guide, command_corpse) || + command_add("corpsefix", "Attempts to bring corpses from underneath the ground within close proximity of the player", AccountStatus::Player, command_corpsefix) || + command_add("cvs", "- Summary of client versions currently online.", AccountStatus::GMMgmt, command_cvs) || + command_add("damage", "[amount] - Damage your target", AccountStatus::GMAdmin, command_damage) || + command_add("databuckets", "View|Delete [key] [limit]- View data buckets, limit 50 default or Delete databucket by key", AccountStatus::QuestTroupe, command_databuckets) || + command_add("date", "[yyyy] [mm] [dd] [HH] [MM] - Set EQ time", AccountStatus::EQSupport, command_date) || + command_add("dbspawn2", "[spawngroup] [respawn] [variance] - Spawn an NPC from a predefined row in the spawn2 table", AccountStatus::GMAdmin, command_dbspawn2) || + command_add("delacct", "[accountname] - Delete an account", AccountStatus::GMLeadAdmin, command_delacct) || + command_add("deletegraveyard", "[zone name] - Deletes the graveyard for the specified zone.", AccountStatus::GMMgmt, command_deletegraveyard) || + command_add("delpetition", "[petition number] - Delete a petition", AccountStatus::ApprenticeGuide, command_delpetition) || + command_add("depop", "- Depop your NPC target", AccountStatus::Guide, command_depop) || + command_add("depopzone", "- Depop the zone", AccountStatus::GMAdmin, command_depopzone) || + command_add("devtools", "- Manages devtools", AccountStatus::GMMgmt, command_devtools) || + command_add("details", "- Change the details of your target (Drakkin Only)", AccountStatus::QuestTroupe, command_details) || + command_add("disablerecipe", "[recipe_id] - Disables a recipe using the recipe id.", AccountStatus::QuestTroupe, command_disablerecipe) || + command_add("disarmtrap", "Analog for ldon disarm trap for the newer clients since we still don't have it working.", AccountStatus::QuestTroupe, command_disarmtrap) || + command_add("distance", "- Reports the distance between you and your target.", AccountStatus::QuestTroupe, command_distance) || + command_add("door", "Door editing command", AccountStatus::QuestTroupe, command_door) || + command_add("doanim", "[animnum] [type] - Send an EmoteAnim for you or your target", AccountStatus::Guide, command_doanim) || + command_add("dye", "[slot|'help'] [red] [green] [blue] [use_tint] - Dyes the specified armor slot to Red, Green, and Blue provided, allows you to bypass darkness limits.", AccountStatus::ApprenticeGuide, command_dye) || + command_add("dz", "Manage expeditions and dynamic zone instances", AccountStatus::QuestTroupe, command_dz) || + command_add("dzkickplayers", "Removes all players from current expedition. (/kickplayers alternative for pre-RoF clients)", AccountStatus::Player, command_dzkickplayers) || + command_add("editmassrespawn", "[name-search] [second-value] - Mass (Zone wide) NPC respawn timer editing command", AccountStatus::GMAdmin, command_editmassrespawn) || + command_add("emote", "['name'/'world'/'zone'] [type] [message] - Send an emote message", AccountStatus::QuestTroupe, command_emote) || + command_add("emotesearch", "Searches NPC Emotes", AccountStatus::QuestTroupe, command_emotesearch) || + command_add("emoteview", "Lists all NPC Emotes", AccountStatus::QuestTroupe, command_emoteview) || + command_add("emptyinventory", "- Clears you or your target's entire inventory (Equipment, General, Bank, and Shared Bank)", AccountStatus::GMImpossible, command_emptyinventory) || + command_add("enablerecipe", "[recipe_id] - Enables a recipe using the recipe id.", AccountStatus::QuestTroupe, command_enablerecipe) || + command_add("endurance", "Restores you or your target's endurance.", AccountStatus::Guide, command_endurance) || + command_add("equipitem", "[slotid(0-21)] - Equip the item on your cursor into the specified slot", AccountStatus::Guide, command_equipitem) || + command_add("face", "- Change the face of your target", AccountStatus::QuestTroupe, command_face) || + command_add("faction", "[Find (criteria | all ) | Review (criteria | all) | Reset (id)] - Resets Player's Faction", AccountStatus::QuestTroupe, command_faction) || + command_add("findaliases", "[search criteria]- Searches for available command aliases, by alias or command", AccountStatus::Player, command_findaliases) || + command_add("findclass", "[search criteria] - Search for a class", AccountStatus::Guide, command_findclass) || + command_add("findfaction", "[search criteria] - Search for a faction", AccountStatus::Guide, command_findfaction) || + command_add("findnpctype", "[search criteria] - Search database NPC types", AccountStatus::GMAdmin, command_findnpctype) || + command_add("findrace", "[search criteria] - Search for a race", AccountStatus::Guide, command_findrace) || + command_add("findskill", "[search criteria] - Search for a skill", AccountStatus::Guide, command_findskill) || + command_add("findspell", "[search criteria] - Search for a spell", AccountStatus::Guide, command_findspell) || + command_add("findtask", "[search criteria] - Search for a task", AccountStatus::Guide, command_findtask) || + command_add("findzone", "[search criteria] - Search database zones", AccountStatus::GMAdmin, command_findzone) || + command_add("fixmob", "[race|gender|texture|helm|face|hair|haircolor|beard|beardcolor|heritage|tattoo|detail] [next|prev] - Manipulate appearance of your target", AccountStatus::QuestTroupe, command_fixmob) || + command_add("flag", "[status] [acctname] - Refresh your admin status, or set an account's admin status if arguments provided", AccountStatus::Player, command_flag) || + command_add("flagedit", "- Edit zone flags on your target", AccountStatus::GMAdmin, command_flagedit) || + command_add("flags", "- displays the flags of you or your target", AccountStatus::Player, command_flags) || + command_add("flymode", "[0/1/2/3/4/5] - Set your or your player target's flymode to ground/flying/levitate/water/floating/levitate_running", AccountStatus::Guide, command_flymode) || + command_add("fov", "- Check wether you're behind or in your target's field of view", AccountStatus::QuestTroupe, command_fov) || + command_add("freeze", "- Freeze your target", AccountStatus::QuestTroupe, command_freeze) || + command_add("gassign", "[id] - Assign targetted NPC to predefined wandering grid id", AccountStatus::GMAdmin, command_gassign) || + command_add("gearup", "Developer tool to quickly equip a character", AccountStatus::GMMgmt, command_gearup) || + command_add("gender", "[0/1/2] - Change your or your target's gender to male/female/neuter", AccountStatus::Guide, command_gender) || + command_add("getplayerburiedcorpsecount", "- Get the target's total number of buried player corpses.", AccountStatus::GMAdmin, command_getplayerburiedcorpsecount) || + command_add("getvariable", "[varname] - Get the value of a variable from the database", AccountStatus::GMMgmt, command_getvariable) || + command_add("ginfo", "- get group info on target.", AccountStatus::ApprenticeGuide, command_ginfo) || + command_add("giveitem", "[itemid] [charges] - Summon an item onto your target's cursor. Charges are optional.", AccountStatus::GMMgmt, command_giveitem) || + command_add("givemoney", "[pp] [gp] [sp] [cp] - Gives specified amount of money to the target player.", AccountStatus::GMMgmt, command_givemoney) || + command_add("globalview", "Lists all qglobals in cache if you were to do a quest with this target.", AccountStatus::QuestTroupe, command_globalview) || + command_add("gm", "- Turn player target's or your GM flag on or off", AccountStatus::QuestTroupe, command_gm) || + command_add("gmspeed", "[on/off] - Turn GM speed hack on/off for you or your player target", AccountStatus::GMAdmin, command_gmspeed) || + command_add("gmzone", "[zone_short_name] [zone_version=0] [identifier=gmzone] - Zones to a private GM instance", AccountStatus::GMAdmin, command_gmzone) || + command_add("goto", "[playername] or [x y z] [h] - Teleport to the provided coordinates or to your target", AccountStatus::Steward, command_goto) || + command_add("grid", "[add/delete] [grid_num] [wandertype] [pausetype] - Create/delete a wandering grid", AccountStatus::GMAreas, command_grid) || + command_add("guild", "- Guild manipulation commands. Use argument help for more info.", AccountStatus::Steward, command_guild) || + command_add("guildapprove", "[guildapproveid] - Approve a guild with specified ID (guild creator receives the id)", AccountStatus::Player, command_guildapprove) || + command_add("guildcreate", "[guildname] - Creates an approval setup for guild name specified", AccountStatus::Player, command_guildcreate) || + command_add("guildlist", "[guildapproveid] - Lists character names who have approved the guild specified by the approve id", AccountStatus::Player, command_guildlist) || + command_add("hair", "- Change the hair style of your target", AccountStatus::QuestTroupe, command_hair) || + command_add("haircolor", "- Change the hair color of your target", AccountStatus::QuestTroupe, command_haircolor) || + command_add("haste", "[percentage] - Set your haste percentage", AccountStatus::GMAdmin, command_haste) || + command_add("hatelist", " - Display hate list for target.", AccountStatus::QuestTroupe, command_hatelist) || + command_add("heal", "- Completely heal your target", AccountStatus::Steward, command_heal) || + command_add("helm", "- Change the helm of your target", AccountStatus::QuestTroupe, command_helm) || + command_add("help", "[search term] - List available commands and their description, specify partial command as argument to search", AccountStatus::Player, command_help) || + command_add("heritage", "- Change the heritage of your target (Drakkin Only)", AccountStatus::QuestTroupe, command_heritage) || + command_add("heromodel", "[hero model] [slot] - Full set of Hero's Forge Armor appearance. If slot is set, sends exact model just to slot.", AccountStatus::GMMgmt, command_heromodel) || + command_add("hideme", "[on/off] - Hide yourself from spawn lists.", AccountStatus::QuestTroupe, command_hideme) || + command_add("hotfix", "[hotfix_name] - Reloads shared memory into a hotfix, equiv to load_shared_memory followed by apply_shared_memory", AccountStatus::GMImpossible, command_hotfix) || + command_add("hp", "- Refresh your HP bar from the server.", AccountStatus::Player, command_hp) || + command_add("incstat", "- Increases or Decreases a client's stats permanently.", AccountStatus::GMMgmt, command_incstat) || + command_add("instance", "- Modify Instances", AccountStatus::GMMgmt, command_instance) || + command_add("interrogateinv", "- use [help] argument for available options", AccountStatus::Player, command_interrogateinv) || + command_add("interrupt", "[message id] [color] - Interrupt your casting. Arguments are optional.", AccountStatus::Guide, command_interrupt) || + command_add("invsnapshot", "- Manipulates inventory snapshots for your current target", AccountStatus::QuestTroupe, command_invsnapshot) || + command_add("invul", "[on/off] - Turn player target's or your invulnerable flag on or off", AccountStatus::QuestTroupe, command_invul) || + command_add("ipban", "[IP address] - Ban IP by character name", AccountStatus::GMMgmt, command_ipban) || + command_add("iplookup", "[charname] - Look up IP address of charname", AccountStatus::GMMgmt, command_iplookup) || + command_add("iteminfo", "- Get information about the item on your cursor", AccountStatus::Steward, command_iteminfo) || + command_add("itemsearch", "[search criteria] - Search for an item", AccountStatus::Steward, command_itemsearch) || + command_add("kick", "[charname] - Disconnect charname", AccountStatus::GMLeadAdmin, command_kick) || + command_add("kill", "- Kill your target", AccountStatus::GMAdmin, command_kill) || + command_add("killallnpcs", " [npc_name] Kills all npcs by search name, leave blank for all attackable NPC's", AccountStatus::GMMgmt, command_killallnpcs) || + command_add("lastname", "[new lastname] - Set your or your player target's lastname", AccountStatus::Guide, command_lastname) || + command_add("level", "[level] - Set your or your target's level", AccountStatus::Steward, command_level) || + command_add("list", "[npcs|players|corpses|doors|objects] [search] - Search entities", AccountStatus::ApprenticeGuide, command_list) || + command_add("listpetition", "- List petitions", AccountStatus::Guide, command_listpetition) || + command_add("load_shared_memory", "[shared_memory_name] - Reloads shared memory and uses the input as output", AccountStatus::GMImpossible, command_load_shared_memory) || + command_add("loc", "- Print out your or your target's current location and heading", AccountStatus::Player, command_loc) || + command_add("lock", "- Lock the worldserver", AccountStatus::GMLeadAdmin, command_lock) || + command_add("logs", "Manage anything to do with logs", AccountStatus::GMImpossible, command_logs) || + command_add("makepet", "[level] [class] [race] [texture] - Make a pet", AccountStatus::Guide, command_makepet) || + command_add("mana", "- Fill your or your target's mana", AccountStatus::Guide, command_mana) || + command_add("maxskills", "Maxes skills for you.", AccountStatus::GMMgmt, command_max_all_skills) || + command_add("memspell", "[slotid] [spellid] - Memorize spellid in the specified slot", AccountStatus::Guide, command_memspell) || + command_add("merchant_close_shop", "Closes a merchant shop", AccountStatus::GMAdmin, command_merchantcloseshop) || + command_add("merchant_open_shop", "Opens a merchants shop", AccountStatus::GMAdmin, command_merchantopenshop) || + command_add("modifynpcstat", "- Modifys a NPC's stats", AccountStatus::GMLeadAdmin, command_modifynpcstat) || + command_add("motd", "[new motd] - Set message of the day", AccountStatus::GMLeadAdmin, command_motd) || + command_add("movechar", "[charname] [zonename] - Move charname to zonename", AccountStatus::Guide, command_movechar) || + command_add("movement", "Various movement commands", AccountStatus::GMMgmt, command_movement) || + command_add("myskills", "- Show details about your current skill levels", AccountStatus::Player, command_myskills) || + command_add("mysql", "Mysql CLI, see 'help' for options.", AccountStatus::GMImpossible, command_mysql) || + command_add("mystats", "- Show details about you or your pet", AccountStatus::Guide, command_mystats) || + command_add("name", "[newname] - Rename your player target", AccountStatus::GMLeadAdmin, command_name) || + command_add("netstats", "- Gets the network stats for a stream.", AccountStatus::GMMgmt, command_netstats) || + command_add("network", "- Admin commands for the udp network interface.", AccountStatus::GMImpossible, command_network) || + command_add("npccast", "[targetname/entityid] [spellid] - Causes NPC target to cast spellid on targetname/entityid", AccountStatus::QuestTroupe, command_npccast) || + command_add("npcedit", "[column] [value] - Mega NPC editing command", AccountStatus::GMAdmin, command_npcedit) || + command_add("npceditmass", "[name-search] [column] [value] - Mass (Zone wide) NPC data editing command", AccountStatus::GMAdmin, command_npceditmass) || + command_add("npcemote", "[message] - Make your NPC target emote a message.", AccountStatus::GMLeadAdmin, command_npcemote) || + command_add("npcloot", "[show/money/add/remove] [itemid/all/money: pp gp sp cp] - Manipulate the loot an NPC is carrying", AccountStatus::QuestTroupe, command_npcloot) || + command_add("npcsay", "[message] - Make your NPC target say a message.", AccountStatus::GMLeadAdmin, command_npcsay) || + command_add("npcshout", "[message] - Make your NPC target shout a message.", AccountStatus::GMLeadAdmin, command_npcshout) || + command_add("npcspawn", "[create/add/update/remove/delete] - Manipulate spawn DB", AccountStatus::GMAreas, command_npcspawn) || + command_add("npcspecialattk", "[flagchar] [perm] - Set NPC special attack flags. Flags are E(nrage) F(lurry) R(ampage) S(ummon).", AccountStatus::QuestTroupe, command_npcspecialattk) || + command_add("npcstats", "- Show stats about target NPC", AccountStatus::QuestTroupe, command_npcstats) || + command_add("npctype_cache", "[id] or all - Clears the npc type cache for either the id or all npcs.", AccountStatus::GMImpossible, command_npctype_cache) || + command_add("npctypespawn", "[npctypeid] [factionid] - Spawn an NPC from the db", AccountStatus::Steward, command_npctypespawn) || + command_add("nudge", "- Nudge your target's current position by specific values", AccountStatus::QuestTroupe, command_nudge) || + command_add("nukebuffs", "- Strip all buffs on you or your target", AccountStatus::Guide, command_nukebuffs) || + command_add("nukeitem", "[itemid] - Remove itemid from your player target's inventory", AccountStatus::GMLeadAdmin, command_nukeitem) || + command_add("object", "List|Add|Edit|Move|Rotate|Copy|Save|Undo|Delete - Manipulate static and tradeskill objects within the zone", AccountStatus::GMAdmin, command_object) || + command_add("oocmute", "[1/0] - Mutes OOC chat", AccountStatus::GMMgmt, command_oocmute) || + command_add("opcode", "- opcode management", AccountStatus::GMImpossible, command_opcode) || #ifdef PACKET_PROFILER - command_add("packetprofile", "- Dump packet profile for target or self.", 250, command_packetprofile) || + command_add("packetprofile", "- Dump packet profile for target or self.", AccountStatus::GMImpossible, command_packetprofile) || #endif - command_add("path", "- view and edit pathing", 200, command_path) || - command_add("peekinv", "[equip/gen/cursor/poss/limbo/curlim/trib/bank/shbank/allbank/trade/world/all] - Print out contents of your player target's inventory", 100, command_peekinv) || - command_add("peqzone", "[zonename] - Go to specified zone, if you have > 75% health", 0, command_peqzone) || - command_add("permaclass", "[classnum] - Change your or your player target's class (target is disconnected)", 80, command_permaclass) || - command_add("permagender", "[gendernum] - Change your or your player target's gender (zone to take effect)", 80, command_permagender) || - command_add("permarace", "[racenum] - Change your or your player target's race (zone to take effect)", 80, command_permarace) || - command_add("petitioninfo", "[petition number] - Get info about a petition", 20, command_petitioninfo) || - command_add("pf", "- Display additional mob coordinate and wandering data", 0, command_pf) || - command_add("picklock", "Analog for ldon pick lock for the newer clients since we still don't have it working.", 0, command_picklock) || - command_add("profanity", "Manage censored language.", 150, command_profanity) || + command_add("path", "- view and edit pathing", AccountStatus::GMMgmt, command_path) || + command_add("peekinv", "[equip/gen/cursor/poss/limbo/curlim/trib/bank/shbank/allbank/trade/world/all] - Print out contents of your player target's inventory", AccountStatus::GMAdmin, command_peekinv) || + command_add("peqzone", "[zonename] - Go to specified zone, if you have > 75% health", AccountStatus::Player, command_peqzone) || + command_add("permaclass", "[classnum] - Change your or your player target's class (target is disconnected)", AccountStatus::QuestTroupe, command_permaclass) || + command_add("permagender", "[gendernum] - Change your or your player target's gender (zone to take effect)", AccountStatus::QuestTroupe, command_permagender) || + command_add("permarace", "[racenum] - Change your or your player target's race (zone to take effect)", AccountStatus::QuestTroupe, command_permarace) || + command_add("petitioninfo", "[petition number] - Get info about a petition", AccountStatus::ApprenticeGuide, command_petitioninfo) || + command_add("pf", "- Display additional mob coordinate and wandering data", AccountStatus::Player, command_pf) || + command_add("picklock", "Analog for ldon pick lock for the newer clients since we still don't have it working.", AccountStatus::Player, command_picklock) || + command_add("profanity", "Manage censored language.", AccountStatus::GMLeadAdmin, command_profanity) || #ifdef EQPROFILE - command_add("profiledump", "- Dump profiling info to logs", 250, command_profiledump) || - command_add("profilereset", "- Reset profiling info", 250, command_profilereset) || + command_add("profiledump", "- Dump profiling info to logs", AccountStatus::GMImpossible, command_profiledump) || + command_add("profilereset", "- Reset profiling info", AccountStatus::GMImpossible, command_profilereset) || #endif - command_add("push", "Lets you do spell push", 150, command_push) || - command_add("proximity", "Shows NPC proximity", 150, command_proximity) || - command_add("pvp", "[on/off] - Set your or your player target's PVP status", 100, command_pvp) || - command_add("qglobal", "[on/off/view] - Toggles qglobal functionality on an NPC", 100, command_qglobal) || - command_add("questerrors", "Shows quest errors.", 100, command_questerrors) || - command_add("race", "[racenum] - Change your or your target's race. Use racenum 0 to return to normal", 50, command_race) || - command_add("raidloot", "[All|GroupLeader|RaidLeader|Selected] - Sets your Raid Loot Type if you have permission to do so.", 0, command_raidloot) || - command_add("randomfeatures", "- Temporarily randomizes the Facial Features of your target", 80, command_randomfeatures) || - command_add("refreshgroup", "- Refreshes Group.", 0, command_refreshgroup) || - command_add("reloadaa", "Reloads AA data", 200, command_reloadaa) || - command_add("reloadallrules", "Executes a reload of all rules.", 80, command_reloadallrules) || - command_add("reloademote", "Reloads NPC Emotes", 80, command_reloademote) || - command_add("reloadlevelmods", nullptr, 255, command_reloadlevelmods) || - command_add("reloadmerchants", nullptr, 255, command_reloadmerchants) || - command_add("reloadperlexportsettings", nullptr, 255, command_reloadperlexportsettings) || - command_add("reloadqst", " - Clear quest cache (any argument causes it to also stop all timers)", 150, command_reloadqst) || - command_add("reloadrulesworld", "Executes a reload of all rules in world specifically.", 80, command_reloadworldrules) || - command_add("reloadstatic", "- Reload Static Zone Data", 150, command_reloadstatic) || - command_add("reloadtraps", "- Repops all traps in the current zone.", 80, command_reloadtraps) || - command_add("reloadtitles", "- Reload player titles from the database", 150, command_reloadtitles) || - command_add("reloadworld", "[0|1] - Clear quest cache (0 - no repop, 1 - repop)", 255, command_reloadworld) || - command_add("reloadzps", "- Reload zone points from database", 150, command_reloadzps) || - command_add("repop", "[delay] - Repop the zone with optional delay", 100, command_repop) || - command_add("resetaa", "- Resets a Player's AA in their profile and refunds spent AA's to unspent, may disconnect player.", 200, command_resetaa) || - command_add("resetaa_timer", "Command to reset AA cooldown timers.", 200, command_resetaa_timer) || - command_add("resetdisc_timer", "Command to reset all discipline cooldown timers.", 200, command_resetdisc_timer) || - command_add("revoke", "[charname] [1/0] - Makes charname unable to talk on OOC", 200, command_revoke) || - command_add("roambox", "Manages roambox settings for an NPC", 200, command_roambox) || - command_add("rules", "(subcommand) - Manage server rules", 250, command_rules) || - command_add("save", "- Force your player or player corpse target to be saved to the database", 50, command_save) || - command_add("scale", "- Handles npc scaling", 150, command_scale) || - command_add("scribespell", "[spellid] - Scribe specified spell in your target's spell book.", 180, command_scribespell) || - command_add("scribespells", "[max level] [min level] - Scribe all spells for you or your player target that are usable by them, up to level specified. (may freeze client for a few seconds)", 150, command_scribespells) || - command_add("sendzonespawns", "- Refresh spawn list for all clients in zone", 150, command_sendzonespawns) || - command_add("sensetrap", "Analog for ldon sense trap for the newer clients since we still don't have it working.", 0, command_sensetrap) || - command_add("serverinfo", "- Get OS info about server host", 200, command_serverinfo) || - command_add("serverrules", "- Read this server's rules", 0, command_serverrules) || - command_add("setaapts", "[AA|Group|Raid] [AA Amount] - Set your or your player target's Available AA Points by Type", 100, command_setaapts) || - command_add("setaaxp", "[AA|Group|Raid] [AA Experience] - Set your or your player target's AA Experience by Type", 100, command_setaaxp) || - command_add("setadventurepoints", "- Set your or your player target's available adventure points", 150, command_set_adventure_points) || - command_add("setanim", "[animnum] - Set target's appearance to animnum", 200, command_setanim) || - command_add("setcrystals", "[value] - Set your or your player target's available radiant or ebon crystals", 100, command_setcrystals) || - command_add("setfaction", "[faction number] - Sets targeted NPC's faction in the database", 170, command_setfaction) || - command_add("setgraveyard", "[zone name] - Creates a graveyard for the specified zone based on your target's LOC.", 200, command_setgraveyard) || - command_add("setlanguage", "[language ID] [value] - Set your target's language skillnum to value", 50, command_setlanguage) || - command_add("setlsinfo", "[email] [password] - Set login server email address and password (if supported by login server)", 10, command_setlsinfo) || - command_add("setpass", "[accountname] [password] - Set local password for accountname", 150, command_setpass) || - command_add("setpvppoints", "[Amount] - Set your or your player target's PVP points", 100, command_setpvppoints) || - command_add("setskill", "[skillnum] [value] - Set your target's skill skillnum to value", 50, command_setskill) || - command_add("setskillall", "[value] - Set all of your target's skills to value", 50, command_setskillall) || - command_add("setstartzone", "[zoneid] - Set target's starting zone. Set to zero to allow the player to use /setstartcity", 80, command_setstartzone) || - command_add("setstat", "- Sets the stats to a specific value.", 255, command_setstat) || - command_add("setxp", "[value] - Set your or your player target's experience", 100, command_setxp) || - command_add("showbonusstats", "[item|spell|all] Shows bonus stats for target from items or spells. Shows both by default.", 50, command_showbonusstats) || - command_add("showbuffs", "- List buffs active on your target or you if no target", 50, command_showbuffs) || - command_add("shownumhits", "Shows buffs numhits for yourself.", 0, command_shownumhits) || - command_add("shownpcgloballoot", "Show GlobalLoot entires on this npc", 50, command_shownpcgloballoot) || - command_add("showskills", "- Show the values of your or your player target's skills", 50, command_showskills) || - command_add("showspellslist", "Shows spell list of targeted NPC", 100, command_showspellslist) || - command_add("showstats", "- Show details about you or your target", 50, command_showstats) || - command_add("showzonegloballoot", "Show GlobalLoot entires on this zone", 50, command_showzonegloballoot) || - command_add("showzonepoints", "Show zone points for current zone", 50, command_showzonepoints) || - command_add("shutdown", "- Shut this zone process down", 150, command_shutdown) || - command_add("size", "[size] - Change size of you or your target", 50, command_size) || - command_add("spawn", "[name] [race] [level] [material] [hp] [gender] [class] [priweapon] [secweapon] [merchantid] - Spawn an NPC", 10, command_spawn) || - command_add("spawneditmass", "Mass editing spawn command", 150, command_spawneditmass) || - command_add("spawnfix", "- Find targeted NPC in database based on its X/Y/heading and update the database to make it spawn at your current location/heading.", 170, command_spawnfix) || - command_add("spawnstatus", "- Show respawn timer status", 100, command_spawnstatus) || - command_add("spellinfo", "[spellid] - Get detailed info about a spell", 10, command_spellinfo) || - command_add("stun", "[duration] - Stuns you or your target for duration", 100, command_stun) || - command_add("summon", "[charname] - Summons your player/npc/corpse target, or charname if specified", 80, command_summon) || - command_add("summonburiedplayercorpse", "- Summons the target's oldest buried corpse, if any exist.", 100, command_summonburiedplayercorpse) || - command_add("summonitem", "[itemid] [charges] - Summon an item onto your cursor. Charges are optional.", 200, command_summonitem) || - command_add("suspend", "[name] [days] [reason] - Suspend by character name and for specificed number of days", 150, command_suspend) || - command_add("task", "(subcommand) - Task system commands", 150, command_task) || - command_add("tattoo", "- Change the tattoo of your target (Drakkin Only)", 80, command_tattoo) || - command_add("tempname", "[newname] - Temporarily renames your target. Leave name blank to restore the original name.", 100, command_tempname) || - command_add("petname", "[newname] - Temporarily renames your pet. Leave name blank to restore the original name.", 100, command_petname) || - command_add("texture", "[texture] [helmtexture] - Change your or your target's appearance, use 255 to show equipment", 10, command_texture) || - command_add("time", "[HH] [MM] - Set EQ time", 90, command_time) || - command_add("timers", "- Display persistent timers for target", 200, command_timers) || - command_add("timezone", "[HH] [MM] - Set timezone. Minutes are optional", 90, command_timezone) || - command_add("title", "[text] [1 = create title table row] - Set your or your player target's title", 50, command_title) || - command_add("titlesuffix", "[text] [1 = create title table row] - Set your or your player target's title suffix", 50, command_titlesuffix) || - command_add("traindisc", "[level] - Trains all the disciplines usable by the target, up to level specified. (may freeze client for a few seconds)", 150, command_traindisc) || - command_add("trapinfo", "- Gets infomation about the traps currently spawned in the zone.", 81, command_trapinfo) || - command_add("tune", "Calculate statistical values related to combat.", 100, command_tune) || - command_add("ucs", "- Attempts to reconnect to the UCS server", 0, command_ucs) || - command_add("undyeme", "- Remove dye from all of your armor slots", 0, command_undyeme) || - command_add("unfreeze", "- Unfreeze your target", 80, command_unfreeze) || - command_add("unlock", "- Unlock the worldserver", 150, command_unlock) || - command_add("unscribespell", "[spellid] - Unscribe specified spell from your target's spell book.", 180, command_unscribespell) || - command_add("unscribespells", "- Clear out your or your player target's spell book.", 180, command_unscribespells) || - command_add("untraindisc", "[spellid] - Untrain specified discipline from your target.", 180, command_untraindisc) || - command_add("untraindiscs", "- Untrains all disciplines from your target.", 180, command_untraindiscs) || - command_add("uptime", "[zone server id] - Get uptime of worldserver, or zone server if argument provided", 10, command_uptime) || - command_add("version", "- Display current version of EQEmu server", 0, command_version) || - command_add("viewnpctype", "[NPC ID] - Show stats for an NPC by NPC ID", 100, command_viewnpctype) || - command_add("viewpetition", "[petition number] - View a petition", 20, command_viewpetition) || - command_add("viewzoneloot", "[item id] - Allows you to search a zone's loot for a specific item ID. (0 shows all loot in the zone)", 80, command_viewzoneloot) || - command_add("wc", "[wear slot] [material] - Sends an OP_WearChange for your target", 200, command_wc) || - command_add("weather", "[0/1/2/3] (Off/Rain/Snow/Manual) - Change the weather", 80, command_weather) || - command_add("who", "[search]", 20, command_who) || - command_add("worldshutdown", "- Shut down world and all zones", 200, command_worldshutdown) || - command_add("wp", "[add|delete] [grid_id] [pause] [waypoint_id] [-h] - Add or delete a waypoint by grid ID. (-h to use current heading)", 170, command_wp) || - command_add("wpadd", "[pause] [-h] - Add your current location as a waypoint to your NPC target's AI path. (-h to use current heading)", 170, command_wpadd) || - command_add("wpinfo", "- Show waypoint info about your NPC target", 170, command_wpinfo) || - command_add("worldwide", "Performs world-wide GM functions such as cast (can be extended for other commands). Use caution", 250, command_worldwide) || - command_add("xtargets", "Show your targets Extended Targets and optionally set how many xtargets they can have.", 250, command_xtargets) || - command_add("zclip", "[min] [max] - modifies and resends zhdr packet", 80, command_zclip) || - command_add("zcolor", "[red] [green] [blue] - Change sky color", 80, command_zcolor) || - command_add("zheader", "[zonename] - Load zheader for zonename from the database", 80, command_zheader) || - command_add("zone", "[zonename] [x] [y] [z] - Go to specified zone (coords optional)", 50, command_zone) || - command_add("zonebootup", "[ZoneServerID] [shortname] - Make a zone server boot a specific zone", 150, command_zonebootup) || - command_add("zoneinstance", "[instanceid] [x] [y] [z] - Go to specified instance zone (coords optional)", 50, command_zone_instance) || - command_add("zonelock", "[List|Lock|Unlock] [Zone ID|Zone Short Name] - Set or get lock status of a Zone by ID or Short Name", 100, command_zonelock) || - command_add("zoneshutdown", "[shortname] - Shut down a zone server", 150, command_zoneshutdown) || - command_add("zonespawn", "- Not implemented", 250, command_zonespawn) || - command_add("zonestatus", "- Show connected zoneservers, synonymous with /servers", 150, command_zonestatus) || - command_add("zopp", "Troubleshooting command - Sends a fake item packet to you. No server reference is created.", 250, command_zopp) || - command_add("zsafecoords", "[x] [y] [z] - Set safe coords", 80, command_zsafecoords) || - command_add("zsave", " - Saves zheader to the database", 80, command_zsave) || - command_add("zsky", "[skytype] - Change zone sky type", 80, command_zsky) || - command_add("zstats", "- Show info about zone header", 80, command_zstats) || - command_add("zunderworld", "[zcoord] - Sets the underworld using zcoord", 80, command_zunderworld) || - command_add("zuwcoords", "[z coord] - Set underworld coord", 80, command_zuwcoords) + command_add("push", "Lets you do spell push", AccountStatus::GMLeadAdmin, command_push) || + command_add("proximity", "Shows NPC proximity", AccountStatus::GMLeadAdmin, command_proximity) || + command_add("pvp", "[on/off] - Set your or your player target's PVP status", AccountStatus::GMAdmin, command_pvp) || + command_add("qglobal", "[on/off/view] - Toggles qglobal functionality on an NPC", AccountStatus::GMAdmin, command_qglobal) || + command_add("questerrors", "Shows quest errors.", AccountStatus::GMAdmin, command_questerrors) || + command_add("race", "[racenum] - Change your or your target's race. Use racenum 0 to return to normal", AccountStatus::Guide, command_race) || + command_add("raidloot", "[All|GroupLeader|RaidLeader|Selected] - Sets your Raid Loot Type if you have permission to do so.", AccountStatus::Player, command_raidloot) || + command_add("randomfeatures", "- Temporarily randomizes the Facial Features of your target", AccountStatus::QuestTroupe, command_randomfeatures) || + command_add("refreshgroup", "- Refreshes Group.", AccountStatus::Player, command_refreshgroup) || + command_add("reloadaa", "Reloads AA data", AccountStatus::GMMgmt, command_reloadaa) || + command_add("reloadallrules", "Executes a reload of all rules.", AccountStatus::QuestTroupe, command_reloadallrules) || + command_add("reloademote", "Reloads NPC Emotes", AccountStatus::QuestTroupe, command_reloademote) || + command_add("reloadlevelmods", nullptr, AccountStatus::Max, command_reloadlevelmods) || + command_add("reloadmerchants", nullptr, AccountStatus::Max, command_reloadmerchants) || + command_add("reloadperlexportsettings", nullptr, AccountStatus::Max, command_reloadperlexportsettings) || + command_add("reloadqst", " - Clear quest cache (any argument causes it to also stop all timers)", AccountStatus::GMLeadAdmin, command_reloadqst) || + command_add("reloadrulesworld", "Executes a reload of all rules in world specifically.", AccountStatus::QuestTroupe, command_reloadworldrules) || + command_add("reloadstatic", "- Reload Static Zone Data", AccountStatus::GMLeadAdmin, command_reloadstatic) || + command_add("reloadtraps", "- Repops all traps in the current zone.", AccountStatus::QuestTroupe, command_reloadtraps) || + command_add("reloadtitles", "- Reload player titles from the database", AccountStatus::GMLeadAdmin, command_reloadtitles) || + command_add("reloadworld", "[0|1] - Clear quest cache (0 - no repop, 1 - repop)", AccountStatus::Max, command_reloadworld) || + command_add("reloadzps", "- Reload zone points from database", AccountStatus::GMLeadAdmin, command_reloadzps) || + command_add("repop", "[delay] - Repop the zone with optional delay", AccountStatus::GMAdmin, command_repop) || + command_add("resetaa", "- Resets a Player's AA in their profile and refunds spent AA's to unspent, may disconnect player.", AccountStatus::GMMgmt, command_resetaa) || + command_add("resetaa_timer", "Command to reset AA cooldown timers.", AccountStatus::GMMgmt, command_resetaa_timer) || + command_add("resetdisc_timer", "Command to reset all discipline cooldown timers.", AccountStatus::GMMgmt, command_resetdisc_timer) || + command_add("revoke", "[charname] [1/0] - Makes charname unable to talk on OOC", AccountStatus::GMMgmt, command_revoke) || + command_add("roambox", "Manages roambox settings for an NPC", AccountStatus::GMMgmt, command_roambox) || + command_add("rules", "(subcommand) - Manage server rules", AccountStatus::GMImpossible, command_rules) || + command_add("save", "- Force your player or player corpse target to be saved to the database", AccountStatus::Guide, command_save) || + command_add("scale", "- Handles npc scaling", AccountStatus::GMLeadAdmin, command_scale) || + command_add("scribespell", "[spellid] - Scribe specified spell in your target's spell book.", AccountStatus::GMCoder, command_scribespell) || + command_add("scribespells", "[max level] [min level] - Scribe all spells for you or your player target that are usable by them, up to level specified. (may freeze client for a few seconds)", AccountStatus::GMLeadAdmin, command_scribespells) || + command_add("sendzonespawns", "- Refresh spawn list for all clients in zone", AccountStatus::GMLeadAdmin, command_sendzonespawns) || + command_add("sensetrap", "Analog for ldon sense trap for the newer clients since we still don't have it working.", AccountStatus::Player, command_sensetrap) || + command_add("serverinfo", "- Get OS info about server host", AccountStatus::GMMgmt, command_serverinfo) || + command_add("serverrules", "- Read this server's rules", AccountStatus::Player, command_serverrules) || + command_add("setaapts", "[AA|Group|Raid] [AA Amount] - Set your or your player target's Available AA Points by Type", AccountStatus::GMAdmin, command_setaapts) || + command_add("setaaxp", "[AA|Group|Raid] [AA Experience] - Set your or your player target's AA Experience by Type", AccountStatus::GMAdmin, command_setaaxp) || + command_add("setadventurepoints", "- Set your or your player target's available adventure points", AccountStatus::GMLeadAdmin, command_set_adventure_points) || + command_add("setanim", "[animnum] - Set target's appearance to animnum", AccountStatus::GMMgmt, command_setanim) || + command_add("setcrystals", "[value] - Set your or your player target's available radiant or ebon crystals", AccountStatus::GMAdmin, command_setcrystals) || + command_add("setfaction", "[faction number] - Sets targeted NPC's faction in the database", AccountStatus::GMAreas, command_setfaction) || + command_add("setgraveyard", "[zone name] - Creates a graveyard for the specified zone based on your target's LOC.", AccountStatus::GMMgmt, command_setgraveyard) || + command_add("setlanguage", "[language ID] [value] - Set your target's language skillnum to value", AccountStatus::Guide, command_setlanguage) || + command_add("setlsinfo", "[email] [password] - Set login server email address and password (if supported by login server)", AccountStatus::Steward, command_setlsinfo) || + command_add("setpass", "[accountname] [password] - Set local password for accountname", AccountStatus::GMLeadAdmin, command_setpass) || + command_add("setpvppoints", "[Amount] - Set your or your player target's PVP points", AccountStatus::GMAdmin, command_setpvppoints) || + command_add("setskill", "[skillnum] [value] - Set your target's skill skillnum to value", AccountStatus::Guide, command_setskill) || + command_add("setskillall", "[value] - Set all of your target's skills to value", AccountStatus::Guide, command_setskillall) || + command_add("setstartzone", "[zoneid] - Set target's starting zone. Set to zero to allow the player to use /setstartcity", AccountStatus::QuestTroupe, command_setstartzone) || + command_add("setstat", "- Sets the stats to a specific value.", AccountStatus::Max, command_setstat) || + command_add("setxp", "[value] - Set your or your player target's experience", AccountStatus::GMAdmin, command_setxp) || + command_add("showbonusstats", "[item|spell|all] Shows bonus stats for target from items or spells. Shows both by default.", AccountStatus::Guide, command_showbonusstats) || + command_add("showbuffs", "- List buffs active on your target or you if no target", AccountStatus::Guide, command_showbuffs) || + command_add("shownumhits", "Shows buffs numhits for yourself.", AccountStatus::Player, command_shownumhits) || + command_add("shownpcgloballoot", "Show GlobalLoot entires on this npc", AccountStatus::Guide, command_shownpcgloballoot) || + command_add("showskills", "- Show the values of your or your player target's skills", AccountStatus::Guide, command_showskills) || + command_add("showspellslist", "Shows spell list of targeted NPC", AccountStatus::GMAdmin, command_showspellslist) || + command_add("showstats", "- Show details about you or your target", AccountStatus::Guide, command_showstats) || + command_add("showzonegloballoot", "Show GlobalLoot entires on this zone", AccountStatus::Guide, command_showzonegloballoot) || + command_add("showzonepoints", "Show zone points for current zone", AccountStatus::Guide, command_showzonepoints) || + command_add("shutdown", "- Shut this zone process down", AccountStatus::GMLeadAdmin, command_shutdown) || + command_add("size", "[size] - Change size of you or your target", AccountStatus::Guide, command_size) || + command_add("spawn", "[name] [race] [level] [material] [hp] [gender] [class] [priweapon] [secweapon] [merchantid] - Spawn an NPC", AccountStatus::Steward, command_spawn) || + command_add("spawneditmass", "Mass editing spawn command", AccountStatus::GMLeadAdmin, command_spawneditmass) || + command_add("spawnfix", "- Find targeted NPC in database based on its X/Y/heading and update the database to make it spawn at your current location/heading.", AccountStatus::GMAreas, command_spawnfix) || + command_add("spawnstatus", "- Show respawn timer status", AccountStatus::GMAdmin, command_spawnstatus) || + command_add("spellinfo", "[spellid] - Get detailed info about a spell", AccountStatus::Steward, command_spellinfo) || + command_add("stun", "[duration] - Stuns you or your target for duration", AccountStatus::GMAdmin, command_stun) || + command_add("summon", "[charname] - Summons your player/npc/corpse target, or charname if specified", AccountStatus::QuestTroupe, command_summon) || + command_add("summonburiedplayercorpse", "- Summons the target's oldest buried corpse, if any exist.", AccountStatus::GMAdmin, command_summonburiedplayercorpse) || + command_add("summonitem", "[itemid] [charges] - Summon an item onto your cursor. Charges are optional.", AccountStatus::GMMgmt, command_summonitem) || + command_add("suspend", "[name] [days] [reason] - Suspend by character name and for specificed number of days", AccountStatus::GMLeadAdmin, command_suspend) || + command_add("task", "(subcommand) - Task system commands", AccountStatus::GMLeadAdmin, command_task) || + command_add("tattoo", "- Change the tattoo of your target (Drakkin Only)", AccountStatus::QuestTroupe, command_tattoo) || + command_add("tempname", "[newname] - Temporarily renames your target. Leave name blank to restore the original name.", AccountStatus::GMAdmin, command_tempname) || + command_add("petname", "[newname] - Temporarily renames your pet. Leave name blank to restore the original name.", AccountStatus::GMAdmin, command_petname) || + command_add("texture", "[texture] [helmtexture] - Change your or your target's appearance, use 255 to show equipment", AccountStatus::Steward, command_texture) || + command_add("time", "[HH] [MM] - Set EQ time", AccountStatus::EQSupport, command_time) || + command_add("timers", "- Display persistent timers for target", AccountStatus::GMMgmt, command_timers) || + command_add("timezone", "[HH] [MM] - Set timezone. Minutes are optional", AccountStatus::EQSupport, command_timezone) || + command_add("title", "[text] [1 = create title table row] - Set your or your player target's title", AccountStatus::Guide, command_title) || + command_add("titlesuffix", "[text] [1 = create title table row] - Set your or your player target's title suffix", AccountStatus::Guide, command_titlesuffix) || + command_add("traindisc", "[level] - Trains all the disciplines usable by the target, up to level specified. (may freeze client for a few seconds)", AccountStatus::GMLeadAdmin, command_traindisc) || + command_add("trapinfo", "- Gets infomation about the traps currently spawned in the zone.", AccountStatus::QuestTroupe, command_trapinfo) || + command_add("tune", "Calculate statistical values related to combat.", AccountStatus::GMAdmin, command_tune) || + command_add("ucs", "- Attempts to reconnect to the UCS server", AccountStatus::Player, command_ucs) || + command_add("undyeme", "- Remove dye from all of your armor slots", AccountStatus::Player, command_undyeme) || + command_add("unfreeze", "- Unfreeze your target", AccountStatus::QuestTroupe, command_unfreeze) || + command_add("unlock", "- Unlock the worldserver", AccountStatus::GMLeadAdmin, command_unlock) || + command_add("unscribespell", "[spellid] - Unscribe specified spell from your target's spell book.", AccountStatus::GMCoder, command_unscribespell) || + command_add("unscribespells", "- Clear out your or your player target's spell book.", AccountStatus::GMCoder, command_unscribespells) || + command_add("untraindisc", "[spellid] - Untrain specified discipline from your target.", AccountStatus::GMCoder, command_untraindisc) || + command_add("untraindiscs", "- Untrains all disciplines from your target.", AccountStatus::GMCoder, command_untraindiscs) || + command_add("uptime", "[zone server id] - Get uptime of worldserver, or zone server if argument provided", AccountStatus::Steward, command_uptime) || + command_add("version", "- Display current version of EQEmu server", AccountStatus::Player, command_version) || + command_add("viewnpctype", "[NPC ID] - Show stats for an NPC by NPC ID", AccountStatus::GMAdmin, command_viewnpctype) || + command_add("viewpetition", "[petition number] - View a petition", AccountStatus::ApprenticeGuide, command_viewpetition) || + command_add("viewzoneloot", "[item id] - Allows you to search a zone's loot for a specific item ID. (0 shows all loot in the zone)", AccountStatus::QuestTroupe, command_viewzoneloot) || + command_add("wc", "[wear slot] [material] - Sends an OP_WearChange for your target", AccountStatus::GMMgmt, command_wc) || + command_add("weather", "[0/1/2/3] (Off/Rain/Snow/Manual) - Change the weather", AccountStatus::QuestTroupe, command_weather) || + command_add("who", "[search]", AccountStatus::ApprenticeGuide, command_who) || + command_add("worldshutdown", "- Shut down world and all zones", AccountStatus::GMMgmt, command_worldshutdown) || + command_add("wp", "[add|delete] [grid_id] [pause] [waypoint_id] [-h] - Add or delete a waypoint by grid ID. (-h to use current heading)", AccountStatus::GMAreas, command_wp) || + command_add("wpadd", "[pause] [-h] - Add your current location as a waypoint to your NPC target's AI path. (-h to use current heading)", AccountStatus::GMAreas, command_wpadd) || + command_add("wpinfo", "- Show waypoint info about your NPC target", AccountStatus::GMAreas, command_wpinfo) || + command_add("worldwide", "Performs world-wide GM functions such as cast (can be extended for other commands). Use caution", AccountStatus::GMImpossible, command_worldwide) || + command_add("xtargets", "Show your targets Extended Targets and optionally set how many xtargets they can have.", AccountStatus::GMImpossible, command_xtargets) || + command_add("zclip", "[min] [max] - modifies and resends zhdr packet", AccountStatus::QuestTroupe, command_zclip) || + command_add("zcolor", "[red] [green] [blue] - Change sky color", AccountStatus::QuestTroupe, command_zcolor) || + command_add("zheader", "[zonename] - Load zheader for zonename from the database", AccountStatus::QuestTroupe, command_zheader) || + command_add("zone", "[zonename] [x] [y] [z] - Go to specified zone (coords optional)", AccountStatus::Guide, command_zone) || + command_add("zonebootup", "[ZoneServerID] [shortname] - Make a zone server boot a specific zone", AccountStatus::GMLeadAdmin, command_zonebootup) || + command_add("zoneinstance", "[instanceid] [x] [y] [z] - Go to specified instance zone (coords optional)", AccountStatus::Guide, command_zone_instance) || + command_add("zonelock", "[List|Lock|Unlock] [Zone ID|Zone Short Name] - Set or get lock status of a Zone by ID or Short Name", AccountStatus::GMAdmin, command_zonelock) || + command_add("zoneshutdown", "[shortname] - Shut down a zone server", AccountStatus::GMLeadAdmin, command_zoneshutdown) || + command_add("zonespawn", "- Not implemented", AccountStatus::GMImpossible, command_zonespawn) || + command_add("zonestatus", "- Show connected zoneservers, synonymous with /servers", AccountStatus::GMLeadAdmin, command_zonestatus) || + command_add("zopp", "Troubleshooting command - Sends a fake item packet to you. No server reference is created.", AccountStatus::GMImpossible, command_zopp) || + command_add("zsafecoords", "[x] [y] [z] - Set safe coords", AccountStatus::QuestTroupe, command_zsafecoords) || + command_add("zsave", " - Saves zheader to the database", AccountStatus::QuestTroupe, command_zsave) || + command_add("zsky", "[skytype] - Change zone sky type", AccountStatus::QuestTroupe, command_zsky) || + command_add("zstats", "- Show info about zone header", AccountStatus::QuestTroupe, command_zstats) || + command_add("zunderworld", "[zcoord] - Sets the underworld using zcoord", AccountStatus::QuestTroupe, command_zunderworld) || + command_add("zuwcoords", "[z coord] - Set underworld coord", AccountStatus::QuestTroupe, command_zuwcoords) ) { command_deinit(); return -1; @@ -681,53 +681,67 @@ int command_realdispatch(Client *c, const char *message) void command_logcommand(Client *c, const char *message) { - int admin=c->Admin(); + int admin = c->Admin(); - bool continueevents=false; + bool continueevents = false; switch (zone->loglevelvar){ //catch failsafe case 9: { // log only LeadGM - if ((admin>= 150) && (admin <200)) - continueevents=true; + if ( + admin >= AccountStatus::GMLeadAdmin && + admin < AccountStatus::GMMgmt + ) { + continueevents = true; + } break; } case 8: { // log only GM - if ((admin>= 100) && (admin <150)) - continueevents=true; + if ( + admin >= AccountStatus::GMAdmin && + admin < AccountStatus::GMLeadAdmin + ) { + continueevents = true; + } break; } case 1: { - if ((admin>= 200)) - continueevents=true; + if (admin >= AccountStatus::GMMgmt) { + continueevents = true; + } break; } case 2: { - if ((admin>= 150)) - continueevents=true; + if (admin >= AccountStatus::GMLeadAdmin) { + continueevents = true; + } break; } case 3: { - if ((admin>= 100)) - continueevents=true; + if (admin >= AccountStatus::GMAdmin) { + continueevents = true; + } break; } case 4: { - if ((admin>= 80)) - continueevents=true; + if (admin >= AccountStatus::QuestTroupe) { + continueevents = true; + } break; } case 5: { - if ((admin>= 20)) - continueevents=true; + if (admin >= AccountStatus::ApprenticeGuide) { + continueevents = true; + } break; } case 6: { - if ((admin>= 10)) - continueevents=true; + if (admin >= AccountStatus::Steward) { + continueevents = true; + } break; } case 7: { - continueevents=true; - break; + continueevents = true; + break; } } @@ -1274,10 +1288,7 @@ void command_summon(Client *c, const Seperator *sep) t=c->GetTarget(); else { - /*if(c->Admin() < 150) - c->Message(Chat::White, "You need a NPC/corpse target for this command"); - else*/ - c->Message(Chat::White, "Usage: #summon [charname] Either target or charname is required"); + c->Message(Chat::White, "Usage: #summon [charname] Either target or charname is required"); return; } @@ -1297,11 +1308,6 @@ void command_summon(Client *c, const Seperator *sep) } else if (t->IsClient()) { - /*if(c->Admin() < 150) - { - c->Message(Chat::White, "You may not summon a player."); - return; - }*/ c->Message(Chat::White, "Summoning player %s to %1.1f, %1.1f, %1.1f", t->GetName(), c->GetX(), c->GetY(), c->GetZ()); t->CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), c->GetX(), c->GetY(), c->GetZ(), c->GetHeading(), 2, GMSummon); } @@ -2016,13 +2022,23 @@ void command_emote(Client *c, const Seperator *sep) for(newmessage = strtok((char*)sep->arg[3],"^");newmessage!=nullptr;newmessage=strtok(nullptr, "^")) entity_list.Message(0, atoi(sep->arg[2]), newmessage); } - } - else if (!worldserver.Connected()) + } else if (!worldserver.Connected()) { c->Message(Chat::White, "Error: World server disconnected"); - else if (strcasecmp(sep->arg[1], "world") == 0) - worldserver.SendEmoteMessage(0, 0, atoi(sep->arg[2]), sep->argplus[3]); - else - worldserver.SendEmoteMessage(sep->arg[1], 0, atoi(sep->arg[2]), sep->argplus[3]); + } else if (!strcasecmp(sep->arg[1], "world")) { + worldserver.SendEmoteMessage( + 0, + 0, + atoi(sep->arg[2]), + sep->argplus[3] + ); + } else { + worldserver.SendEmoteMessage( + sep->arg[1], + 0, + atoi(sep->arg[2]), + sep->argplus[3] + ); + } } } @@ -5785,7 +5801,7 @@ void command_zonelock(Client *c, const Seperator *sep) strn0cpy(lock_zone->adminname, c->GetName(), sizeof(lock_zone->adminname)); if (is_list) { - lock_zone->op = EQ::constants::ServerLockType::List; + lock_zone->op = ServerLockType::List; worldserver.SendPacket(pack); } else if (!is_list && c->Admin() >= commandLockZones) { auto zone_id = ( @@ -5796,7 +5812,7 @@ void command_zonelock(Client *c, const Seperator *sep) std::string zone_short_name = str_tolower(ZoneName(zone_id, true)); bool is_unknown_zone = zone_short_name.find("unknown") != std::string::npos; if (zone_id && !is_unknown_zone) { - lock_zone->op = is_lock ? EQ::constants::ServerLockType::Lock : EQ::constants::ServerLockType::Unlock; + lock_zone->op = is_lock ? ServerLockType::Lock : ServerLockType::Unlock; lock_zone->zoneID = zone_id; worldserver.SendPacket(pack); } else { @@ -6274,7 +6290,7 @@ void command_gmzone(Client *c, const Seperator *sep) if (instance_id > 0) { float target_x = -1, target_y = -1, target_z = -1, target_heading = -1; - int16 min_status = 0; + int16 min_status = AccountStatus::Player; uint8 min_level = 0; if (!content_db.GetSafePoints( @@ -7494,7 +7510,7 @@ void command_iteminfo(Client *c, const Seperator *sep) c->Message(Chat::White, ">> EffectType: 0x%02X, CastTime: %.2f", (uint8)item->Click.Type, ((double)item->CastTime / 1000)); } - if (c->Admin() >= 200) + if (c->Admin() >= AccountStatus::GMMgmt) c->Message(Chat::White, ">> MinStatus: %u", item->MinStatus); } @@ -7638,58 +7654,6 @@ void command_guild(Client *c, const Seperator *sep) guild_mgr.DescribeGuild(c, tmp); } } - /* - else if (strcasecmp(sep->arg[1], "edit") == 0) { - if (c->GuildDBID() == 0) - c->Message(Chat::White, "You arent in a guild!"); - else if (!sep->IsNumber(2)) - c->Message(Chat::White, "Error: invalid rank #."); - else if (atoi(sep->arg[2]) < 0 || atoi(sep->arg[2]) > GUILD_MAX_RANK) - c->Message(Chat::White, "Error: invalid rank #."); - else if (!c->GuildRank() == 0) - c->Message(Chat::White, "You must be rank %s to use edit.", guilds[c->GuildEQID()].rank[0].rankname); - else if (!worldserver.Connected()) - c->Message(Chat::White, "Error: World server dirconnected"); - else { - if (!helper_guild_edit(c, c->GuildDBID(), c->GuildEQID(), atoi(sep->arg[2]), sep->arg[3], sep->argplus[4])) { - c->Message(Chat::White, " #guild edit rank title newtitle"); - c->Message(Chat::White, " #guild edit rank permission 0/1"); - } - else { - ServerPacket* pack = new ServerPacket(ServerOP_RefreshGuild, 5); - int32 geqid=c->GuildEQID(); - memcpy(pack->pBuffer, &geqid, 4); - worldserver.SendPacket(pack); - safe_delete(pack); - } - } - } - else if (strcasecmp(sep->arg[1], "gmedit") == 0 && admin >= 100) { - if (!sep->IsNumber(2)) - c->Message(Chat::White, "Error: invalid guilddbid."); - else if (!sep->IsNumber(3)) - c->Message(Chat::White, "Error: invalid rank #."); - else if (atoi(sep->arg[3]) < 0 || atoi(sep->arg[3]) > GUILD_MAX_RANK) - c->Message(Chat::White, "Error: invalid rank #."); - else if (!worldserver.Connected()) - c->Message(Chat::White, "Error: World server dirconnected"); - else { - uint32 eqid = database.GetGuildEQID(atoi(sep->arg[2])); - if (eqid == GUILD_NONE) - c->Message(Chat::White, "Error: Guild not found"); - else if (!helper_guild_edit(c, atoi(sep->arg[2]), eqid, atoi(sep->arg[3]), sep->arg[4], sep->argplus[5])) { - c->Message(Chat::White, " #guild gmedit guilddbid rank title newtitle"); - c->Message(Chat::White, " #guild gmedit guilddbid rank permission 0/1"); - } - else { - ServerPacket* pack = new ServerPacket(ServerOP_RefreshGuild, 5); - memcpy(pack->pBuffer, &eqid, 4); - worldserver.SendPacket(pack); - safe_delete(pack); - } - } - } - */ else if (strcasecmp(sep->arg[1], "set") == 0) { if (!sep->IsNumber(3)) c->Message(Chat::White, "Usage: #guild set charname guildgbid (0 = clear guildtag)"); diff --git a/zone/corpse.cpp b/zone/corpse.cpp index bd8a36418..6401abb95 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -953,7 +953,7 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a // return; } - if(is_locked && client->Admin() < 100) { + if(is_locked && client->Admin() < AccountStatus::GMAdmin) { SendLootReqErrorPacket(client, LootResponse::SomeoneElse); client->Message(Chat::Red, "Error: Corpse locked by GM."); return; @@ -977,7 +977,7 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a // loot_request_type is scoped to class Corpse and reset on a per-loot session basis if (client->GetGM()) { - if (client->Admin() >= 100) + if (client->Admin() >= AccountStatus::GMAdmin) loot_request_type = LootRequestType::GMAllowed; else loot_request_type = LootRequestType::GMPeek; @@ -1203,14 +1203,14 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) } if (IsPlayerCorpse() && !CanPlayerLoot(client->CharacterID()) && !become_npc && - (char_id != client->CharacterID() && client->Admin() < 150)) { + (char_id != client->CharacterID() && client->Admin() < AccountStatus::GMLeadAdmin)) { client->Message(Chat::Red, "Error: This is a player corpse and you dont own it."); client->QueuePacket(app); SendEndLootErrorPacket(client); return; } - if (is_locked && client->Admin() < 100) { + if (is_locked && client->Admin() < AccountStatus::GMAdmin) { client->QueuePacket(app); SendLootReqErrorPacket(client, LootResponse::SomeoneElse); client->Message(Chat::Red, "Error: Corpse locked by GM."); @@ -1580,7 +1580,7 @@ bool Corpse::Summon(Client* client, bool spell, bool CheckDistance) { uint32 dist2 = 10000; // pow(100, 2); if (!spell) { if (this->GetCharID() == client->CharacterID()) { - if (IsLocked() && client->Admin() < 100) { + if (IsLocked() && client->Admin() < AccountStatus::GMAdmin) { client->Message(Chat::Red, "That corpse is locked by a GM."); return false; } diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index b0b1a312e..f5fc4ad6b 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -7473,8 +7473,8 @@ XS(XS__worldwideaddldonloss) { uint8 update_type = CZLDoNUpdateSubtype_AddLoss; uint32 theme_id = (uint32)SvUV(ST(0)); int points = 1; - uint8 min_status = 0; - uint8 max_status = 0; + uint8 min_status = AccountStatus::Player; + uint8 max_status = AccountStatus::Player; if (items == 2) min_status = (uint8)SvUV(ST(1)); @@ -7495,8 +7495,8 @@ XS(XS__worldwideaddldonpoints) { uint8 update_type = CZLDoNUpdateSubtype_AddPoints; uint32 theme_id = (uint32)SvUV(ST(0)); int points = 1; - uint8 min_status = 0; - uint8 max_status = 0; + uint8 min_status = AccountStatus::Player; + uint8 max_status = AccountStatus::Player; if (items == 2) points = (int)SvIV(ST(1)); @@ -7520,8 +7520,8 @@ XS(XS__worldwideaddldonwin) { uint8 update_type = CZLDoNUpdateSubtype_AddWin; uint32 theme_id = (uint32)SvUV(ST(0)); int points = 1; - uint8 min_status = 0; - uint8 max_status = 0; + uint8 min_status = AccountStatus::Player; + uint8 max_status = AccountStatus::Player; if (items == 2) min_status = (uint8)SvUV(ST(1)); @@ -7541,8 +7541,8 @@ XS(XS__worldwideassigntask) { { uint8 update_type = WWTaskUpdateType_AssignTask; uint32 task_identifier = (uint32) SvUV(ST(0)); - uint8 min_status = 0; - uint8 max_status = 0; + uint8 min_status = AccountStatus::Player; + uint8 max_status = AccountStatus::Player; int task_subidentifier = -1; int update_count = 1; bool enforce_level_requirement = false; @@ -7565,8 +7565,8 @@ XS(XS__worldwidecastspell) { { uint8 update_type = WWSpellUpdateType_Cast; uint32 spell_id = (uint32) SvUV(ST(0)); - uint8 min_status = 0; - uint8 max_status = 0; + uint8 min_status = AccountStatus::Player; + uint8 max_status = AccountStatus::Player; if (items == 2) min_status = (uint8) SvUV(ST(1)); @@ -7585,8 +7585,8 @@ XS(XS__worldwidedialoguewindow) { Perl_croak(aTHX_ "Usage: quest::worldwidedialoguewindow(string message, [uint8 min_status = 0, uint8 max_status = 0])"); { const char* message = (const char*) SvPV_nolen(ST(0)); - uint8 min_status = 0; - uint8 max_status = 0; + uint8 min_status = AccountStatus::Player; + uint8 max_status = AccountStatus::Player; if (items == 2) min_status = (uint8)SvUV(ST(1)); @@ -7606,8 +7606,8 @@ XS(XS__worldwidedisabletask) { { uint8 update_type = WWTaskUpdateType_DisableTask; uint32 task_identifier = (uint32) SvUV(ST(0)); - uint8 min_status = 0; - uint8 max_status = 0; + uint8 min_status = AccountStatus::Player; + uint8 max_status = AccountStatus::Player; int task_subidentifier = -1; int update_count = 1; bool enforce_level_requirement = false; @@ -7630,8 +7630,8 @@ XS(XS__worldwideenabletask) { { uint8 update_type = WWTaskUpdateType_EnableTask; uint32 task_identifier = (uint32) SvUV(ST(0)); - uint8 min_status = 0; - uint8 max_status = 0; + uint8 min_status = AccountStatus::Player; + uint8 max_status = AccountStatus::Player; int task_subidentifier = -1; int update_count = 1; bool enforce_level_requirement = false; @@ -7654,8 +7654,8 @@ XS(XS__worldwidefailtask) { { uint8 update_type = WWTaskUpdateType_FailTask; uint32 task_identifier = (uint32) SvUV(ST(0)); - uint8 min_status = 0; - uint8 max_status = 0; + uint8 min_status = AccountStatus::Player; + uint8 max_status = AccountStatus::Player; int task_subidentifier = -1; int update_count = 1; bool enforce_level_requirement = false; @@ -7682,8 +7682,8 @@ XS(XS__worldwidemarquee) { uint32 fade_out = (uint32) SvUV(ST(3)); uint32 duration = (uint32) SvUV(ST(4)); const char* message = (const char*) SvPV_nolen(ST(5)); - uint8 min_status = 0; - uint8 max_status = 0; + uint8 min_status = AccountStatus::Player; + uint8 max_status = AccountStatus::Player; if (items == 7) min_status = (uint8) SvUV(ST(6)); @@ -7703,8 +7703,8 @@ XS(XS__worldwidemessage) { { uint32 type = (uint32) SvUV(ST(0)); const char* message = (const char*) SvPV_nolen(ST(1)); - uint8 min_status = 0; - uint8 max_status = 0; + uint8 min_status = AccountStatus::Player; + uint8 max_status = AccountStatus::Player; if (items == 3) min_status = (uint8) SvUV(ST(2)); @@ -7725,8 +7725,8 @@ XS(XS__worldwidemove) { uint8 update_type = WWMoveUpdateType_MoveZone; const char* zone_short_name = (const char*) SvPV_nolen(ST(0)); uint16 instance_id = 0; - uint8 min_status = 0; - uint8 max_status = 0; + uint8 min_status = AccountStatus::Player; + uint8 max_status = AccountStatus::Player; if (items == 2) min_status = (uint8) SvUV(ST(1)); @@ -7747,8 +7747,8 @@ XS(XS__worldwidemoveinstance) { uint8 update_type = WWMoveUpdateType_MoveZoneInstance; const char* zone_short_name = ""; uint16 instance_id = (uint16) SvUV(ST(0)); - uint8 min_status = 0; - uint8 max_status = 0; + uint8 min_status = AccountStatus::Player; + uint8 max_status = AccountStatus::Player; if (items == 2) min_status = (uint8) SvUV(ST(1)); @@ -7769,8 +7769,8 @@ XS(XS__worldwideremoveldonloss) { uint8 update_type = CZLDoNUpdateSubtype_RemoveLoss; uint32 theme_id = (uint32)SvUV(ST(0)); int points = 1; - uint8 min_status = 0; - uint8 max_status = 0; + uint8 min_status = AccountStatus::Player; + uint8 max_status = AccountStatus::Player; if (items == 2) min_status = (uint8)SvUV(ST(1)); @@ -7791,8 +7791,8 @@ XS(XS__worldwideremoveldonwin) { uint8 update_type = CZLDoNUpdateSubtype_RemoveWin; uint32 theme_id = (uint32)SvUV(ST(0)); int points = 1; - uint8 min_status = 0; - uint8 max_status = 0; + uint8 min_status = AccountStatus::Player; + uint8 max_status = AccountStatus::Player; if (items == 2) min_status = (uint8)SvUV(ST(1)); @@ -7812,8 +7812,8 @@ XS(XS__worldwideremovespell) { { uint8 update_type = WWSpellUpdateType_Remove; uint32 spell_id = (uint32) SvUV(ST(0)); - uint8 min_status = 0; - uint8 max_status = 0; + uint8 min_status = AccountStatus::Player; + uint8 max_status = AccountStatus::Player; if (items == 2) min_status = (uint8) SvUV(ST(1)); @@ -7833,8 +7833,8 @@ XS(XS__worldwideremovetask) { { uint8 update_type = WWTaskUpdateType_RemoveTask; uint32 task_identifier = (uint32) SvUV(ST(0)); - uint8 min_status = 0; - uint8 max_status = 0; + uint8 min_status = AccountStatus::Player; + uint8 max_status = AccountStatus::Player; int task_subidentifier = -1; int update_count = 1; bool enforce_level_requirement = false; @@ -7859,8 +7859,8 @@ XS(XS__worldwideresetactivity) { uint8 update_type = WWTaskUpdateType_ActivityReset; uint32 task_identifier = (uint32) SvUV(ST(0)); int task_subidentifier = (int) SvIV(ST(1)); - uint8 min_status = 0; - uint8 max_status = 0; + uint8 min_status = AccountStatus::Player; + uint8 max_status = AccountStatus::Player; int update_count = 1; bool enforce_level_requirement = false; if (items == 3) @@ -7883,8 +7883,8 @@ XS(XS__worldwidesetentityvariableclient) { uint8 update_type = WWSetEntityVariableUpdateType_Character; const char* variable_name = (const char*) SvPV_nolen(ST(0)); const char* variable_value = (const char*) SvPV_nolen(ST(1)); - uint8 min_status = 0; - uint8 max_status = 0; + uint8 min_status = AccountStatus::Player; + uint8 max_status = AccountStatus::Player; if (items == 3) min_status = (uint8) SvUV(ST(2)); @@ -7931,8 +7931,8 @@ XS(XS__worldwidesignalclient) { { uint8 update_type = WWSignalUpdateType_Character; uint32 signal = (uint32) SvUV(ST(0)); - uint8 min_status = 0; - uint8 max_status = 0; + uint8 min_status = AccountStatus::Player; + uint8 max_status = AccountStatus::Player; if (items == 2) min_status = (uint8) SvUV(ST(1)); @@ -7953,8 +7953,8 @@ XS(XS__worldwideupdateactivity) { uint8 update_type = WWTaskUpdateType_ActivityUpdate; uint32 task_identifier = (uint32) SvUV(ST(0)); int task_subidentifier = (int) SvIV(ST(1)); - uint8 min_status = 0; - uint8 max_status = 0; + uint8 min_status = AccountStatus::Player; + uint8 max_status = AccountStatus::Player; int update_count = 1; bool enforce_level_requirement = false; if (items == 3) diff --git a/zone/entity.cpp b/zone/entity.cpp index 08ebe2831..68d8f5f71 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -3243,7 +3243,7 @@ void EntityList::SendPetitionToAdmins() pcus->quetotal=0; auto it = client_list.begin(); while (it != client_list.end()) { - if (it->second->CastToClient()->Admin() >= 80) + if (it->second->CastToClient()->Admin() >= AccountStatus::QuestTroupe) it->second->CastToClient()->QueuePacket(outapp); ++it; } @@ -3271,7 +3271,7 @@ void EntityList::SendPetitionToAdmins(Petition *pet) pcus->quetotal = petition_list.GetTotalPetitions(); auto it = client_list.begin(); while (it != client_list.end()) { - if (it->second->CastToClient()->Admin() >= 80) { + if (it->second->CastToClient()->Admin() >= AccountStatus::QuestTroupe) { if (pet->CheckedOut()) strcpy(pcus->gmsenttoo, ""); else @@ -3296,7 +3296,7 @@ void EntityList::ClearClientPetitionQueue() pet->quetotal = petition_list.GetTotalPetitions(); auto it = client_list.begin(); while (it != client_list.end()) { - if (it->second->CastToClient()->Admin() >= 100) { + if (it->second->CastToClient()->Admin() >= AccountStatus::GMAdmin) { int x = 0; for (x = 0; x < 64; x++) { pet->petnumber = x; @@ -4686,7 +4686,7 @@ void EntityList::ZoneWho(Client *c, Who_All_Struct *Who) WAPP2->RankMSGID = 12315; else if (ClientEntry->IsBuyer()) WAPP2->RankMSGID = 6056; - else if (ClientEntry->Admin() >= 10 && ClientEntry->GetGM()) + else if (ClientEntry->Admin() >= AccountStatus::Steward && ClientEntry->GetGM()) WAPP2->RankMSGID = 12312; else WAPP2->RankMSGID = 0xFFFFFFFF; diff --git a/zone/entity.h b/zone/entity.h index b7f65978a..a42165fc6 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -391,7 +391,7 @@ public: void ReplaceWithTarget(Mob* pOldMob, Mob*pNewTarget); void QueueCloseClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, float distance=200, Mob* skipped_mob = 0, bool is_ack_required = true, eqFilterType filter=FilterNone); void QueueClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, bool ackreq = true); - void QueueClientsStatus(Mob* sender, const EQApplicationPacket* app, bool ignore_sender = false, uint8 minstatus = 0, uint8 maxstatus = 0); + void QueueClientsStatus(Mob* sender, const EQApplicationPacket* app, bool ignore_sender = false, uint8 minstatus = AccountStatus::Player, uint8 maxstatus = AccountStatus::Player); void QueueClientsGuild(Mob* sender, const EQApplicationPacket* app, bool ignore_sender = false, uint32 guildeqid = 0); void QueueClientsGuildBankItemUpdate(const GuildBankItemUpdate_Struct *gbius, uint32 GuildID); void QueueClientsByTarget(Mob* sender, const EQApplicationPacket* app, bool iSendToSender = true, Mob* SkipThisMob = 0, bool ackreq = true, diff --git a/zone/exp.cpp b/zone/exp.cpp index 9b4df5492..b7261ccf7 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -793,7 +793,7 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { FastQueuePacket(&outapp); } - if (admin>=100 && GetGM()) { + if (admin >= AccountStatus::GMAdmin && GetGM()) { char val1[20]={0}; char val2[20]={0}; char val3[20]={0}; diff --git a/zone/guild_mgr.cpp b/zone/guild_mgr.cpp index c63effea5..e3c4e6071 100644 --- a/zone/guild_mgr.cpp +++ b/zone/guild_mgr.cpp @@ -1513,7 +1513,17 @@ void GuildApproval::GuildApproved() database.InsertPetitionToDB(pet); petition_list.UpdateGMQueue(); petition_list.UpdateZoneListQueue(); - worldserver.SendEmoteMessage(0, 0, 80, 15, "%s has made a petition. #%i", owner->CastToClient()->GetName(), pet->GetID()); + worldserver.SendEmoteMessage( + 0, + 0, + AccountStatus::QuestTroupe, + Chat::Yellow, + fmt::format( + "{} has made a petition. ID: {}", + owner->CastToClient()->GetName(), + pet->GetID() + ).c_str() + ); auto pack = new ServerPacket; pack->opcode = ServerOP_RefreshGuild; pack->size = tmp; diff --git a/zone/lua_entity_list.cpp b/zone/lua_entity_list.cpp index 9a9996d96..c7721271e 100644 --- a/zone/lua_entity_list.cpp +++ b/zone/lua_entity_list.cpp @@ -217,7 +217,12 @@ void Lua_EntityList::Message(uint32 guild_dbid, uint32 type, const char *message void Lua_EntityList::MessageStatus(uint32 guild_dbid, int min_status, uint32 type, const char *message) { Lua_Safe_Call_Void(); - self->MessageStatus(guild_dbid, min_status, type, message); + self->MessageStatus( + guild_dbid, + min_status, + type, + message + ); } void Lua_EntityList::MessageClose(Lua_Mob sender, bool skip_sender, float dist, uint32 type, const char *message) { diff --git a/zone/mob.cpp b/zone/mob.cpp index 914d7fdf7..394de8bba 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3935,7 +3935,7 @@ void Mob::SetTarget(Mob *mob) else if (IsClient()) { parse->EventPlayer(EVENT_TARGET_CHANGE, CastToClient(), "", 0); - if (CastToClient()->admin > 200) { + if (CastToClient()->admin > AccountStatus::GMMgmt) { DisplayInfo(mob); } diff --git a/zone/pathing.cpp b/zone/pathing.cpp index 1513cd5d6..87f8977f7 100644 --- a/zone/pathing.cpp +++ b/zone/pathing.cpp @@ -49,7 +49,7 @@ void Client::SendPathPacket(const std::vector &points) { .Then([this](const EQ::Any &result) { auto points = EQ::any_cast>(result); if (points.size() < 2) { - if (Admin() > 10) { + if (Admin() > AccountStatus::Steward) { Message(Chat::System, "Too few points"); } @@ -59,7 +59,7 @@ void Client::SendPathPacket(const std::vector &points) { } if (points.size() > 36) { - if (Admin() > 10) { + if (Admin() > AccountStatus::Steward) { Message(Chat::System, "Too many points %u", points.size()); } @@ -68,7 +68,7 @@ void Client::SendPathPacket(const std::vector &points) { return; } - if (Admin() > 10) { + if (Admin() > AccountStatus::Steward) { Message(Chat::System, "Total points %u", points.size()); } diff --git a/zone/perl_entity.cpp b/zone/perl_entity.cpp index 835929ef9..8ae7976eb 100644 --- a/zone/perl_entity.cpp +++ b/zone/perl_entity.cpp @@ -856,7 +856,12 @@ XS(XS_EntityList_MessageStatus) { uint32 type = (uint32) SvUV(ST(3)); char *message = (char *) SvPV_nolen(ST(4)); VALIDATE_THIS_IS_ENTITY; - THIS->MessageStatus(to_guilddbid, to_minstatus, type, message); + THIS->MessageStatus( + to_guilddbid, + to_minstatus, + type, + message + ); } XSRETURN_EMPTY; } diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 18431e47b..527706567 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -765,18 +765,39 @@ void QuestManager::shout2(const char *str) { if (!owner) { LogQuests("QuestManager::shout2 called with nullptr owner. Probably syntax error in quest file"); return; - } - else { - worldserver.SendEmoteMessage(0,0,0,13, "%s shouts, '%s'", owner->GetCleanName(), str); + } else { + worldserver.SendEmoteMessage( + 0, + 0, + AccountStatus::Player, + Chat::Red, + fmt::format( + "{} shouts, '{}'", + owner->GetCleanName(), + str + ).c_str() + ); } } void QuestManager::gmsay(const char *str, uint32 color, bool send_to_world, uint32 to_guilddbid, uint32 to_minstatus) { QuestManagerCurrentQuestVars(); - if(send_to_world) - worldserver.SendEmoteMessage(0, to_guilddbid, to_minstatus, color, "%s", str); - else - entity_list.MessageStatus(to_guilddbid, to_minstatus, color, "%s", str); + if(send_to_world) { + worldserver.SendEmoteMessage( + 0, + to_guilddbid, + to_minstatus, + color, + str + ); + } else { + entity_list.MessageStatus( + to_guilddbid, + to_minstatus, + color, + str + ); + } } void QuestManager::depop(int npc_type) { @@ -1349,30 +1370,66 @@ void QuestManager::setguild(uint32 new_guild_id, uint8 new_rank) { void QuestManager::CreateGuild(const char *guild_name, const char *leader) { QuestManagerCurrentQuestVars(); - uint32 cid = database.GetCharacterID(leader); - char hString[250]; - if (cid == 0) { - worldserver.SendEmoteMessage(0, 0, 80, 15, "%s", "Guild Creation: Guild leader not found."); - return; - } - - uint32 tmp = guild_mgr.FindGuildByLeader(cid); - if (tmp != GUILD_NONE) { - sprintf(hString, "Guild Creation: Error: %s already is the leader of DB# %u '%s'.", leader, tmp, guild_mgr.GetGuildName(tmp)); - worldserver.SendEmoteMessage(0, 0, 80, 15, "%s", hString); - } - else { - uint32 gid = guild_mgr.CreateGuild(guild_name, cid); - if (gid == GUILD_NONE) - worldserver.SendEmoteMessage(0, 0, 80, 15, "%s", "Guild Creation: Guild creation failed"); - else { - sprintf(hString, "Guild Creation: Guild created: Leader: %u, number %u: %s", cid, gid, leader); - worldserver.SendEmoteMessage(0, 0, 80, 15, "%s", hString); - if(!guild_mgr.SetGuild(cid, gid, GUILD_LEADER)) - worldserver.SendEmoteMessage(0, 0, 80, 15, "%s", "Unable to set guild leader's guild in the database. Your going to have to run #guild set"); - } + uint32 character_id = database.GetCharacterID(leader); + if (character_id == 0) { + worldserver.SendEmoteMessage( + 0, + 0, + AccountStatus::QuestTroupe, + Chat::Yellow, + "Guild Error | Guild leader not found." + ); + return; + } + uint32 tmp = guild_mgr.FindGuildByLeader(character_id); + if (tmp != GUILD_NONE) { + worldserver.SendEmoteMessage( + 0, + 0, + AccountStatus::QuestTroupe, + Chat::Yellow, + fmt::format( + "Guild Error | {} already is the leader of {} ({}).", + leader, + guild_mgr.GetGuildName(tmp), + tmp + ).c_str() + ); + } else { + uint32 gid = guild_mgr.CreateGuild(guild_name, character_id); + if (gid == GUILD_NONE) { + worldserver.SendEmoteMessage( + 0, + 0, + AccountStatus::QuestTroupe, + Chat::Yellow, + "Guild Error | Guild creation failed." + ); + } else { + worldserver.SendEmoteMessage( + 0, + 0, + AccountStatus::QuestTroupe, + Chat::Yellow, + fmt::format( + "Guild Created | Leader: {} ({}) ID: {}", + leader, + character_id, + gid + ).c_str() + ); + if (!guild_mgr.SetGuild(character_id, gid, GUILD_LEADER)) { + worldserver.SendEmoteMessage( + 0, + 0, + AccountStatus::QuestTroupe, + Chat::Yellow, + "Unable to set guild leader's guild in the database. Use #guild set." + ); } + } + } } void QuestManager::settime(uint8 new_hour, uint8 new_min, bool update_world /*= true*/) @@ -2458,7 +2515,12 @@ void QuestManager::ze(int type, const char *str) { } void QuestManager::we(int type, const char *str) { - worldserver.SendEmoteMessage(0, 0, type, str); + worldserver.SendEmoteMessage( + 0, + 0, + type, + str + ); } void QuestManager::message(int color, const char *message) { diff --git a/zone/questmgr.h b/zone/questmgr.h index 2b14e4fe6..d41b2d3ae 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -302,15 +302,15 @@ public: void CrossZoneSignal(uint8 update_type, int update_identifier, uint32 signal, const char* client_name = ""); void CrossZoneSpell(uint8 update_type, uint8 update_subtype, int update_identifier, uint32 spell_id, const char* client_name = ""); void CrossZoneTaskUpdate(uint8 update_type, uint8 update_subtype, int update_identifier, uint32 task_identifier, int task_subidentifier = -1, int update_count = 1, bool enforce_level_requirement = false, const char* client_name = ""); - void WorldWideDialogueWindow(const char* message, uint8 min_status = 0, uint8 max_status = 0); - void WorldWideLDoNUpdate(uint8 update_type, uint32 theme_id, int points = 1, uint8 min_status = 0, uint8 max_status = 0); - void WorldWideMarquee(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char* message, uint8 min_status = 0, uint8 max_status = 0); - void WorldWideMessage(uint32 type, const char* message, uint8 min_status = 0, uint8 max_status = 0); - void WorldWideMove(uint8 update_type, const char* zone_short_name, uint16 instance_id = 0, uint8 min_status = 0, uint8 max_status = 0); - void WorldWideSetEntityVariable(uint8 update_type, const char* variable_name, const char* variable_value, uint8 min_status = 0, uint8 max_status = 0); - void WorldWideSignal(uint8 update_type, uint32 signal, uint8 min_status = 0, uint8 max_status = 0); - void WorldWideSpell(uint8 update_type, uint32 spell_id, uint8 min_status = 0, uint8 max_status = 0); - void WorldWideTaskUpdate(uint8 update_type, uint32 task_identifier, int task_subidentifier = -1, int update_count = 1, bool enforce_level_requirement = false, uint8 min_status = 0, uint8 max_status = 0); + void WorldWideDialogueWindow(const char* message, uint8 min_status = AccountStatus::Player, uint8 max_status = AccountStatus::Player); + void WorldWideLDoNUpdate(uint8 update_type, uint32 theme_id, int points = 1, uint8 min_status = AccountStatus::Player, uint8 max_status = AccountStatus::Player); + void WorldWideMarquee(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, const char* message, uint8 min_status = AccountStatus::Player, uint8 max_status = AccountStatus::Player); + void WorldWideMessage(uint32 type, const char* message, uint8 min_status = AccountStatus::Player, uint8 max_status = AccountStatus::Player); + void WorldWideMove(uint8 update_type, const char* zone_short_name, uint16 instance_id = 0, uint8 min_status = AccountStatus::Player, uint8 max_status = AccountStatus::Player); + void WorldWideSetEntityVariable(uint8 update_type, const char* variable_name, const char* variable_value, uint8 min_status = AccountStatus::Player, uint8 max_status = AccountStatus::Player); + void WorldWideSignal(uint8 update_type, uint32 signal, uint8 min_status = AccountStatus::Player, uint8 max_status = AccountStatus::Player); + void WorldWideSpell(uint8 update_type, uint32 spell_id, uint8 min_status = AccountStatus::Player, uint8 max_status = AccountStatus::Player); + void WorldWideTaskUpdate(uint8 update_type, uint32 task_identifier, int task_subidentifier = -1, int update_count = 1, bool enforce_level_requirement = false, uint8 min_status = AccountStatus::Player, uint8 max_status = AccountStatus::Player); bool EnableRecipe(uint32 recipe_id); bool DisableRecipe(uint32 recipe_id); void ClearNPCTypeCache(int npctype_id); diff --git a/zone/spells.cpp b/zone/spells.cpp index b38d23486..9783bfc40 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2183,7 +2183,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui ( this->IsClient() && (zone->GetZoneID() == 183 || zone->GetZoneID() == 184) && // load - CastToClient()->Admin() < 80 + CastToClient()->Admin() < AccountStatus::QuestTroupe ) { if diff --git a/zone/trading.cpp b/zone/trading.cpp index 92b132477..27555b8fc 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -205,41 +205,45 @@ void Trade::LogTrade() item_count++; } - if (((this->cp + this->sp + this->gp + this->pp)>0) || (item_count>0)) + if ((cp + sp + gp + pp) || item_count) { admin_level = trader->Admin(); - else - admin_level = 999; + } else { + admin_level = (AccountStatus::Max + 1); + } if (zone->tradevar == 7) { logtrade = true; - } - else if ((admin_level>=10) && (admin_level<20)) { - if ((zone->tradevar<8) && (zone->tradevar>5)) + } else if ( + admin_level >= AccountStatus::Steward && + admin_level < AccountStatus::ApprenticeGuide + ) { + if (zone->tradevar < 8 && zone->tradevar > 5) { logtrade = true; - } - else if (admin_level<=20) { - if ((zone->tradevar<8) && (zone->tradevar>4)) + } + } else if (admin_level <= AccountStatus::ApprenticeGuide) { + if (zone->tradevar < 8 && zone->tradevar > 4) { logtrade = true; - } - else if (admin_level<=80) { - if ((zone->tradevar<8) && (zone->tradevar>3)) + } + } else if (admin_level <= AccountStatus::QuestTroupe) { + if (zone->tradevar < 8 && zone->tradevar > 3) { logtrade = true; - } - else if (admin_level<=100){ - if ((zone->tradevar<9) && (zone->tradevar>2)) + } + } else if (admin_level <= AccountStatus::GMAdmin) { + if (zone->tradevar < 9 && zone->tradevar > 2) { logtrade = true; - } - else if (admin_level<=150){ - if (((zone->tradevar<8) && (zone->tradevar>1)) || (zone->tradevar==9)) + } + } else if (admin_level <= AccountStatus::GMLeadAdmin) { + if ((zone->tradevar < 8 && zone->tradevar > 1) || zone->tradevar == 9) { logtrade = true; - } - else if (admin_level<=255){ - if ((zone->tradevar<8) && (zone->tradevar>0)) + } + } else if (admin_level <= AccountStatus::Max){ + if (zone->tradevar < 8 && zone->tradevar > 0) { logtrade = true; + } } } - if (logtrade == true) { + if (logtrade) { char logtext[1000] = {0}; uint32 cash = 0; bool comma = false; diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index a77eb4a17..be039be8d 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -161,13 +161,22 @@ void WorldServer::OnConnected() { safe_delete(pack); if (is_zone_loaded) { - this->SetZoneData(zone->GetZoneID(), zone->GetInstanceID()); + SetZoneData(zone->GetZoneID(), zone->GetInstanceID()); entity_list.UpdateWho(true); - this->SendEmoteMessage(0, 0, 15, "Zone connect: %s", zone->GetLongName()); + SendEmoteMessage( + 0, + 0, + Chat::Yellow, + fmt::format( + "Zone Connected | {} ({})", + zone->GetLongName(), + zone->GetZoneID() + ).c_str() + ); zone->GetTimeSync(); } else { - this->SetZoneData(0); + SetZoneData(0); } pack = new ServerPacket(ServerOP_LSZoneBoot, sizeof(ZoneBoot_Struct)); @@ -449,7 +458,12 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) ServerEmoteMessage_Struct* sem = (ServerEmoteMessage_Struct*)pack->pBuffer; if (sem->to[0] != 0) { if (strcasecmp(sem->to, zone->GetShortName()) == 0) - entity_list.MessageStatus(sem->guilddbid, sem->minstatus, sem->type, (char*)sem->message); + entity_list.MessageStatus( + sem->guilddbid, + sem->minstatus, + sem->type, + (char*)sem->message + ); else { Client* client = entity_list.GetClientByName(sem->to); if (client) { @@ -465,11 +479,22 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } else { char* newmessage = 0; - if (strstr(sem->message, "^") == 0) - entity_list.MessageStatus(sem->guilddbid, sem->minstatus, sem->type, sem->message); - else { - for (newmessage = strtok((char*)sem->message, "^"); newmessage != nullptr; newmessage = strtok(nullptr, "^")) - entity_list.MessageStatus(sem->guilddbid, sem->minstatus, sem->type, newmessage); + if (strstr(sem->message, "^") == 0) { + entity_list.MessageStatus( + sem->guilddbid, + sem->minstatus, + sem->type, + sem->message + ); + } else { + for (newmessage = strtok((char*)sem->message, "^"); newmessage != nullptr; newmessage = strtok(nullptr, "^")) { + entity_list.MessageStatus( + sem->guilddbid, + sem->minstatus, + sem->type, + newmessage + ); + } } } break; @@ -502,7 +527,16 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) SetZoneData(0); } else { - SendEmoteMessage(0, 0, 15, "Zone shutdown: %s", zone->GetLongName()); + SendEmoteMessage( + 0, + 0, + Chat::Yellow, + fmt::format( + "Zone Shutdown | {} ({})", + zone->GetLongName(), + zone->GetZoneID() + ).c_str() + ); ServerZoneStateChange_struct* zst = (ServerZoneStateChange_struct *)pack->pBuffer; std::cout << "Zone shutdown by " << zst->adminname << std::endl; @@ -523,7 +557,16 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) zone->StartShutdownTimer(AUTHENTICATION_TIMEOUT * 1000); } else { - SendEmoteMessage(zst->adminname, 0, 0, "Zone bootup failed: Already running '%s'", zone->GetShortName()); + SendEmoteMessage( + zst->adminname, + 0, + Chat::White, + fmt::format( + "Zone Bootup Failed | {} ({}) Already running", + zone->GetLongName(), + zone->GetZoneID() + ).c_str() + ); } break; } @@ -590,7 +633,19 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) else if (client->GetAnon() == 1 && client->Admin() > szp->adminrank) break; else { - SendEmoteMessage(szp->adminname, 0, 0, "Summoning %s to %s %1.1f, %1.1f, %1.1f", szp->name, szp->zone, szp->x_pos, szp->y_pos, szp->z_pos); + SendEmoteMessage( + szp->adminname, + 0, + Chat::White, + fmt::format( + "Summoning {} to {} at {:.2f}, {:.2f}, {:.2f}", + szp->name, + szp->zone, + szp->x_pos, + szp->y_pos, + szp->z_pos + ).c_str() + ); } if (!szp->instance_id) { client->MovePC(ZoneID(szp->zone), szp->instance_id, szp->x_pos, szp->y_pos, szp->z_pos, client->GetHeading(), szp->ignorerestrictions, GMSummon); @@ -615,13 +670,33 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) if (client) { if (skp->adminrank >= client->Admin()) { client->WorldKick(); - if (is_zone_loaded) - SendEmoteMessage(skp->adminname, 0, 0, "Remote Kick: %s booted in zone %s.", skp->name, zone->GetShortName()); - else - SendEmoteMessage(skp->adminname, 0, 0, "Remote Kick: %s booted.", skp->name); + SendEmoteMessage( + skp->adminname, + 0, + Chat::White, + fmt::format( + "Remote Kick | {} booted{}", + skp->name, + is_zone_loaded ? + fmt::format( + "in {} ({})", + zone->GetLongName(), + zone->GetZoneID() + ) : + "" + ).c_str() + ); + } else if (client->GetAnon() != 1) { + SendEmoteMessage( + skp->adminname, + 0, + Chat::White, + fmt::format( + "Remote Kick | Your Status Level is not high enough to kick {}.", + skp->name + ).c_str() + ); } - else if (client->GetAnon() != 1) - SendEmoteMessage(skp->adminname, 0, 0, "Remote Kick: Your avatar level is not high enough to kick %s", skp->name); } break; } @@ -631,13 +706,33 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) if (client) { if (skp->admin >= client->Admin()) { client->GMKill(); - if (is_zone_loaded) - SendEmoteMessage(skp->gmname, 0, 0, "Remote Kill: %s killed in zone %s.", skp->target, zone->GetShortName()); - else - SendEmoteMessage(skp->gmname, 0, 0, "Remote Kill: %s killed.", skp->target); + SendEmoteMessage( + skp->gmname, + 0, + Chat::White, + fmt::format( + "Remote Kill | {} killed{}", + skp->target, + is_zone_loaded ? + fmt::format( + "in {} ({})", + zone->GetLongName(), + zone->GetZoneID() + ) : + "" + ).c_str() + ); + } else if (client->GetAnon() != 1) { + SendEmoteMessage( + skp->gmname, + 0, + Chat::White, + fmt::format( + "Remote Kill | Your Status Level is not high enough to kill {}", + skp->target + ).c_str() + ); } - else if (client->GetAnon() != 1) - SendEmoteMessage(skp->gmname, 0, 0, "Remote Kill: Your avatar level is not high enough to kill %s", skp->target); } break; } @@ -674,7 +769,20 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) ServerGMGoto_Struct* gmg = (ServerGMGoto_Struct*)pack->pBuffer; Client* client = entity_list.GetClientByName(gmg->gotoname); if (client) { - SendEmoteMessage(gmg->myname, 0, 13, "Summoning you to: %s @ %s, %1.1f, %1.1f, %1.1f", client->GetName(), zone->GetShortName(), client->GetX(), client->GetY(), client->GetZ()); + SendEmoteMessage( + gmg->myname, + 0, + Chat::Red, + fmt::format( + "Summoning you to {} ({}) in {} at {:.2f}, {:.2f}, {:.2f}", + client->GetCleanName(), + zone->GetLongName(), + zone->GetZoneID(), + client->GetX(), + client->GetY(), + client->GetZ() + ).c_str() + ); auto outpack = new ServerPacket(ServerOP_ZonePlayer, sizeof(ServerZonePlayer_Struct)); ServerZonePlayer_Struct* szp = (ServerZonePlayer_Struct*)outpack->pBuffer; strcpy(szp->adminname, gmg->myname); @@ -688,7 +796,15 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) safe_delete(outpack); } else { - SendEmoteMessage(gmg->myname, 0, 13, "Error: %s not found", gmg->gotoname); + SendEmoteMessage( + gmg->myname, + 0, + Chat::Red, + fmt::format( + "Error: {} not found.", + gmg->gotoname + ).c_str() + ); } break; } @@ -710,19 +826,17 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } ServerUptime_Struct* sus = (ServerUptime_Struct*)pack->pBuffer; uint32 ms = Timer::GetCurrentTime(); - uint32 d = ms / 86400000; - ms -= d * 86400000; - uint32 h = ms / 3600000; - ms -= h * 3600000; - uint32 m = ms / 60000; - ms -= m * 60000; - uint32 s = ms / 1000; - if (d) - this->SendEmoteMessage(sus->adminname, 0, 0, "Zone #%i Uptime: %02id %02ih %02im %02is", sus->zoneserverid, d, h, m, s); - else if (h) - this->SendEmoteMessage(sus->adminname, 0, 0, "Zone #%i Uptime: %02ih %02im %02is", sus->zoneserverid, h, m, s); - else - this->SendEmoteMessage(sus->adminname, 0, 0, "Zone #%i Uptime: %02im %02is", sus->zoneserverid, m, s); + std::string time_string = ConvertSecondsToTime(ms); + SendEmoteMessage( + sus->adminname, + 0, + Chat::White, + fmt::format( + "Zoneserver {} | Uptime: {}", + sus->zoneserverid, + time_string + ).c_str() + ); } case ServerOP_Petition: { std::cout << "Got Server Requested Petition List Refresh" << std::endl; @@ -856,9 +970,19 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) case ServerOP_Revoke: { RevokeStruct* rev = (RevokeStruct*)pack->pBuffer; Client* client = entity_list.GetClientByName(rev->name); - if (client) - { - SendEmoteMessage(rev->adminname, 0, 0, "%s: %srevoking %s", zone->GetShortName(), rev->toggle ? "" : "un", client->GetName()); + if (client) { + SendEmoteMessage( + rev->adminname, + 0, + Chat::White, + fmt::format( + "Zone {} ({}) | {} {}.", + zone->GetLongName(), + zone->GetZoneID(), + rev->toggle ? "Revoking" : "Unrevoking", + client->GetCleanName() + ).c_str() + ); client->SetRevoked(rev->toggle); } break; @@ -1826,12 +1950,30 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) break; } case ServerOP_ReloadRules: { - worldserver.SendEmoteMessage( - 0, 0, 100, 15, - "Rules reloaded for Zone: '%s' Instance ID: %u", - (zone ? zone->GetLongName() : StringFormat("Null zone pointer [pid]:[%i]", getpid()).c_str()), - (zone ? zone->GetInstanceID() : 0xFFFFFFFFF) - ); + if (zone) { + worldserver.SendEmoteMessage( + 0, + 0, + AccountStatus::GMAdmin, + Chat::Yellow, + fmt::format( + "Rules reloaded for {}.", + fmt::format( + "{} ({})", + zone->GetLongName(), + zone->GetZoneID() + ), + ( + zone->GetInstanceID() ? + fmt::format( + "Instance ID: {}", + zone->GetInstanceID() + ) : + "" + ) + ).c_str() + ); + } RuleManager::Instance()->LoadRules(&database, RuleManager::Instance()->GetActiveRuleset(), true); break; } @@ -2716,7 +2858,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) uint8 min_status = WWDW->min_status; uint8 max_status = WWDW->max_status; for (auto &client : entity_list.GetClientList()) { - if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == AccountStatus::Player)) { DialogueWindow::Render(client.second, message); } } @@ -2733,27 +2875,27 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) for (auto &client : entity_list.GetClientList()) { switch (update_type) { case WWLDoNUpdateType_AddLoss: - if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == AccountStatus::Player)) { client.second->UpdateLDoNWinLoss(theme_id, false); } break; case WWLDoNUpdateType_AddPoints: - if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == AccountStatus::Player)) { client.second->UpdateLDoNPoints(theme_id, points); } break; case WWLDoNUpdateType_AddWin: - if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == AccountStatus::Player)) { client.second->UpdateLDoNWinLoss(theme_id, true); } break; case WWLDoNUpdateType_RemoveLoss: - if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == AccountStatus::Player)) { client.second->UpdateLDoNWinLoss(theme_id, false, true); } break; case WWLDoNUpdateType_RemoveWin: - if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == AccountStatus::Player)) { client.second->UpdateLDoNWinLoss(theme_id, true, true); } break; @@ -2773,7 +2915,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) uint8 min_status = WWM->min_status; uint8 max_status = WWM->max_status; for (auto &client : entity_list.GetClientList()) { - if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == AccountStatus::Player)) { client.second->SendMarqueeMessage(type, priority, fade_in, fade_out, duration, message); } } @@ -2787,7 +2929,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) uint8 min_status = WWM->min_status; uint8 max_status = WWM->max_status; for (auto &client : entity_list.GetClientList()) { - if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == AccountStatus::Player)) { client.second->Message(type, message); } } @@ -2804,12 +2946,12 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) for (auto &client : entity_list.GetClientList()) { switch (update_type) { case WWMoveUpdateType_MoveZone: - if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == AccountStatus::Player)) { client.second->MoveZone(zone_short_name); } break; case WWMoveUpdateType_MoveZoneInstance: - if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == AccountStatus::Player)) { client.second->MoveZoneInstance(instance_id); } break; @@ -2827,7 +2969,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) uint8 max_status = WWSEV->max_status; if (update_type == WWSetEntityVariableUpdateType_Character) { for (auto &client : entity_list.GetClientList()) { - if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == AccountStatus::Player)) { client.second->SetEntityVariable(variable_name, variable_value); } } @@ -2847,7 +2989,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) uint8 max_status = WWS->max_status; if (update_type == WWSignalUpdateType_Character) { for (auto &client : entity_list.GetClientList()) { - if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == AccountStatus::Player)) { client.second->Signal(signal); } } @@ -2867,13 +3009,13 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) uint8 max_status = WWS->max_status; if (update_type == WWSpellUpdateType_Cast) { for (auto &client : entity_list.GetClientList()) { - if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == AccountStatus::Player)) { client.second->SpellFinished(spell_id, client.second); } } } else if (update_type == WWSpellUpdateType_Remove) { for (auto &client : entity_list.GetClientList()) { - if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == AccountStatus::Player)) { client.second->BuffFadeBySpellID(spell_id); } } @@ -2891,7 +3033,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) uint8 min_status = WWTU->min_status; uint8 max_status = WWTU->max_status; for (auto &client : entity_list.GetClientList()) { - if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == 0)) { + if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == AccountStatus::Player)) { switch (update_type) { case WWTaskUpdateType_ActivityReset: client.second->ResetTaskActivity(task_identifier, task_subidentifier); @@ -2959,11 +3101,28 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) if (request_zone_short_name == local_zone_short_name || can_reload_global_script) { zone->SetQuestHotReloadQueued(true); } else if (request_zone_short_name == "all") { - std::string reload_quest_saylink = EQ::SayLinkEngine::GenerateQuestSaylink("#reloadquest", false, "Locally"); - std::string reload_world_saylink = EQ::SayLinkEngine::GenerateQuestSaylink("#reloadworld", false, "Globally"); - worldserver.SendEmoteMessage(0, 0, 20, 15, "A quest, plugin, or global script has changed reload quests [%s] [%s].", reload_quest_saylink.c_str(), reload_world_saylink.c_str()); + std::string reload_quest_saylink = EQ::SayLinkEngine::GenerateQuestSaylink( + "#reloadquest", + false, + "Locally" + ); + std::string reload_world_saylink = EQ::SayLinkEngine::GenerateQuestSaylink( + "#reloadworld", + false, + "Globally" + ); + worldserver.SendEmoteMessage( + 0, + 0, + AccountStatus::ApprenticeGuide, + Chat::Yellow, + fmt::format( + "A quest, plugin, or global script has changed. Reload: [{}] [{}]", + reload_quest_saylink, + reload_world_saylink + ).c_str() + ); } - break; } case ServerOP_ChangeSharedMem: @@ -3075,7 +3234,7 @@ bool WorldServer::SendChannelMessage(Client* from, const char* to, uint8 chan_nu if (from == 0) { strcpy(scm->from, "ZServer"); - scm->fromadmin = 0; + scm->fromadmin = AccountStatus::Player; } else { strcpy(scm->from, from->GetName()); @@ -3110,7 +3269,13 @@ bool WorldServer::SendEmoteMessage(const char* to, uint32 to_guilddbid, uint32 t vsnprintf(buffer, sizeof(buffer) - 1, message, argptr); va_end(argptr); - return SendEmoteMessage(to, to_guilddbid, 0, type, buffer); + return SendEmoteMessage( + to, + to_guilddbid, + AccountStatus::Player, + type, + buffer + ); } bool WorldServer::SendEmoteMessage(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message, ...) { @@ -3122,7 +3287,12 @@ bool WorldServer::SendEmoteMessage(const char* to, uint32 to_guilddbid, int16 to va_end(argptr); if (!Connected() && to == 0) { - entity_list.MessageStatus(to_guilddbid, to_minstatus, type, buffer); + entity_list.MessageStatus( + to_guilddbid, + to_minstatus, + type, + buffer + ); return false; } diff --git a/zone/zone.h b/zone/zone.h index af6f571ae..0a8a0aeff 100755 --- a/zone/zone.h +++ b/zone/zone.h @@ -328,24 +328,29 @@ public: auto message_split = SplitString(message, '\n'); entity_list.MessageStatus( 0, - 80, + AccountStatus::QuestTroupe, LogSys.GetGMSayColorFromCategory(log_category), - "%s", message_split[0].c_str() ); for (size_t iter = 1; iter < message_split.size(); ++iter) { entity_list.MessageStatus( 0, - 80, + AccountStatus::QuestTroupe, LogSys.GetGMSayColorFromCategory(log_category), - "--- %s", - message_split[iter].c_str() + fmt::format( + "--- {}", + message_split[iter] + ).c_str() ); } - } - else { - entity_list.MessageStatus(0, 80, LogSys.GetGMSayColorFromCategory(log_category), "%s", message.c_str()); + } else { + entity_list.MessageStatus( + 0, + AccountStatus::QuestTroupe, + LogSys.GetGMSayColorFromCategory(log_category), + message.c_str() + ); } } diff --git a/zone/zoning.cpp b/zone/zoning.cpp index 7ca47aa7b..d305003b8 100644 --- a/zone/zoning.cpp +++ b/zone/zoning.cpp @@ -181,7 +181,7 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { /* Load up the Safe Coordinates, restrictions and verify the zone name*/ float safe_x, safe_y, safe_z, safe_heading; - int16 min_status = 0; + int16 min_status = AccountStatus::Player; uint8 min_level = 0; char flag_needed[128]; if(!content_db.GetSafePoints( @@ -1059,7 +1059,7 @@ void Client::SendZoneFlagInfo(Client *to) const { const char* zone_short_name = ZoneName(zone_id); std::string zone_long_name = zone_store.GetZoneLongName(zone_id); float safe_x, safe_y, safe_z, safe_heading; - int16 min_status = 0; + int16 min_status = AccountStatus::Player; uint8 min_level = 0; char flag_name[128]; if(!content_db.GetSafePoints( @@ -1089,7 +1089,7 @@ bool Client::CanBeInZone() { return(true); float safe_x, safe_y, safe_z, safe_heading; - int16 min_status = 0; + int16 min_status = AccountStatus::Player; uint8 min_level = 0; char flag_needed[128]; if(!content_db.GetSafePoints( From 0550fcfd3f4757fd7f73d56b9743525f97e5c2da Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 14 Nov 2021 22:48:47 -0600 Subject: [PATCH 413/624] [GM Commands] Split GM Commands Into Separate Files (#1766) * Split GM commands into their own files * Code cleanup --- .gitignore | 2 + common/file_util.cpp | 7 +- common/file_util.h | 1 + common/spdat.cpp | 56 +- common/spdat.h | 533 +- utils/gm_commands/main.go | 183 + zone/CMakeLists.txt | 849 +- zone/command.cpp | 15194 +--------------- zone/command.h | 63 +- zone/gm_commands/acceptrules.cpp | 11 + zone/gm_commands/advnpcspawn.cpp | 534 + zone/gm_commands/aggro.cpp | 21 + zone/gm_commands/aggrozone.cpp | 19 + zone/gm_commands/ai.cpp | 139 + zone/gm_commands/appearance.cpp | 26 + zone/gm_commands/attack.cpp | 18 + zone/gm_commands/augmentitem.cpp | 18 + zone/gm_commands/ban.cpp | 72 + zone/gm_commands/beard.cpp | 37 + zone/gm_commands/beardcolor.cpp | 37 + zone/gm_commands/bestz.cpp | 89 + zone/gm_commands/bind.cpp | 17 + zone/gm_commands/camerashake.cpp | 24 + zone/gm_commands/castspell.cpp | 77 + zone/gm_commands/chat.cpp | 15 + zone/gm_commands/checklos.cpp | 20 + zone/gm_commands/copycharacter.cpp | 34 + zone/gm_commands/corpse.cpp | 168 + zone/gm_commands/corpsefix.cpp | 8 + zone/gm_commands/cvs.cpp | 17 + zone/gm_commands/damage.cpp | 21 + zone/gm_commands/databuckets.cpp | 92 + zone/gm_commands/date.cpp | 29 + zone/gm_commands/dbspawn2.cpp | 31 + zone/gm_commands/delacct.cpp | 21 + zone/gm_commands/deletegraveyard.cpp | 35 + zone/gm_commands/delpetition.cpp | 20 + zone/gm_commands/depop.cpp | 14 + zone/gm_commands/depopzone.cpp | 8 + zone/gm_commands/details.cpp | 37 + zone/gm_commands/devtools.cpp | 22 + zone/gm_commands/disablerecipe.cpp | 29 + zone/gm_commands/disarmtrap.cpp | 25 + zone/gm_commands/distance.cpp | 23 + zone/gm_commands/doanim.cpp | 20 + zone/gm_commands/door.cpp | 9 + zone/gm_commands/door_manipulation.cpp | 15 +- zone/gm_commands/dye.cpp | 87 + zone/gm_commands/dz.cpp | 244 + zone/gm_commands/dzkickplayers.cpp | 13 + zone/gm_commands/editmassrespawn.cpp | 140 + zone/gm_commands/emote.cpp | 45 + zone/gm_commands/emotesearch.cpp | 78 + zone/gm_commands/emoteview.cpp | 39 + zone/gm_commands/enablerecipe.cpp | 29 + zone/gm_commands/endurance.cpp | 27 + zone/gm_commands/equipitem.cpp | 82 + zone/gm_commands/face.cpp | 37 + zone/gm_commands/faction.cpp | 174 + zone/gm_commands/findclass.cpp | 84 + zone/gm_commands/findfaction.cpp | 89 + zone/gm_commands/findnpctype.cpp | 77 + zone/gm_commands/findrace.cpp | 84 + zone/gm_commands/findskill.cpp | 90 + zone/gm_commands/findspell.cpp | 129 + zone/gm_commands/findtask.cpp | 89 + zone/gm_commands/findzone.cpp | 95 + zone/gm_commands/fixmob.cpp | 250 + zone/gm_commands/flag.cpp | 53 + zone/gm_commands/flagedit.cpp | 157 + zone/gm_commands/flags.cpp | 16 + zone/gm_commands/flymode.cpp | 40 + zone/gm_commands/fov.cpp | 42 + zone/gm_commands/freeze.cpp | 12 + zone/gm_commands/gassign.cpp | 14 + zone/gm_commands/gearup.cpp | 181 + zone/gm_commands/gender.cpp | 17 + .../getplayerburiedcorpsecount.cpp | 27 + zone/gm_commands/getvariable.cpp | 13 + zone/gm_commands/ginfo.cpp | 52 + zone/gm_commands/giveitem.cpp | 111 + zone/gm_commands/givemoney.cpp | 33 + zone/gm_commands/globalview.cpp | 80 + zone/gm_commands/gm.cpp | 27 + zone/gm_commands/gmspeed.cpp | 20 + zone/gm_commands/gmzone.cpp | 88 + zone/gm_commands/goto.cpp | 63 + zone/gm_commands/grid.cpp | 143 + zone/gm_commands/guild.cpp | 444 + zone/gm_commands/guildapprove.cpp | 8 + zone/gm_commands/guildcreate.cpp | 13 + zone/gm_commands/guildlist.cpp | 14 + zone/gm_commands/hair.cpp | 37 + zone/gm_commands/haircolor.cpp | 37 + zone/gm_commands/haste.cpp | 20 + zone/gm_commands/hatelist.cpp | 15 + zone/gm_commands/heal.cpp | 21 + zone/gm_commands/helm.cpp | 37 + zone/gm_commands/heritage.cpp | 37 + zone/gm_commands/heromodel.cpp | 35 + zone/gm_commands/hideme.cpp | 16 + zone/gm_commands/hp.cpp | 8 + zone/gm_commands/incstat.cpp | 18 + zone/gm_commands/instance.cpp | 188 + zone/gm_commands/interrogateinv.cpp | 76 + zone/gm_commands/interrupt.cpp | 16 + zone/gm_commands/invsnapshot.cpp | 418 + zone/gm_commands/invul.cpp | 20 + zone/gm_commands/ipban.cpp | 21 + zone/gm_commands/iplookup.cpp | 23 + zone/gm_commands/iteminfo.cpp | 94 + zone/gm_commands/itemsearch.cpp | 125 + zone/gm_commands/kick.cpp | 36 + zone/gm_commands/kill.cpp | 12 + zone/gm_commands/killallnpcs.cpp | 45 + zone/gm_commands/lastname.cpp | 19 + zone/gm_commands/list.cpp | 265 + zone/gm_commands/listpetition.cpp | 22 + zone/gm_commands/loc.cpp | 32 + zone/gm_commands/lock.cpp | 15 + zone/gm_commands/logcommand.cpp | 85 + zone/gm_commands/logs.cpp | 101 + zone/gm_commands/makepet.cpp | 12 + zone/gm_commands/mana.cpp | 27 + zone/gm_commands/max_all_skills.cpp | 20 + zone/gm_commands/memspell.cpp | 22 + zone/gm_commands/merchantcloseshop.cpp | 13 + zone/gm_commands/merchantopenshop.cpp | 13 + zone/gm_commands/modifynpcstat.cpp | 30 + zone/gm_commands/motd.cpp | 15 + zone/gm_commands/movechar.cpp | 32 + zone/gm_commands/movement.cpp | 76 + zone/gm_commands/myskills.cpp | 7 + zone/gm_commands/mysql.cpp | 87 + zone/gm_commands/mystats.cpp | 17 + zone/gm_commands/name.cpp | 30 + zone/gm_commands/netstats.cpp | 141 + zone/gm_commands/network.cpp | 175 + zone/gm_commands/npccast.cpp | 94 + zone/gm_commands/npcedit.cpp | 1408 ++ zone/gm_commands/npceditmass.cpp | 194 + zone/gm_commands/npcemote.cpp | 12 + zone/gm_commands/npcloot.cpp | 104 + zone/gm_commands/npcsay.cpp | 12 + zone/gm_commands/npcshout.cpp | 12 + zone/gm_commands/npcspawn.cpp | 97 + zone/gm_commands/npcspecialattk.cpp | 16 + zone/gm_commands/npcstats.cpp | 20 + zone/gm_commands/npctype_cache.cpp | 27 + zone/gm_commands/npctypespawn.cpp | 29 + zone/gm_commands/nudge.cpp | 77 + zone/gm_commands/nukebuffs.cpp | 12 + zone/gm_commands/nukeitem.cpp | 16 + zone/gm_commands/object.cpp | 1263 ++ zone/gm_commands/oocmute.cpp | 18 + zone/gm_commands/opcode.cpp | 11 + zone/gm_commands/path.cpp | 27 + zone/gm_commands/peekinv.cpp | 332 + zone/gm_commands/peqzone.cpp | 75 + zone/gm_commands/permaclass.cpp | 28 + zone/gm_commands/permagender.cpp | 29 + zone/gm_commands/permarace.cpp | 34 + zone/gm_commands/petitioninfo.cpp | 39 + zone/gm_commands/petname.cpp | 22 + zone/gm_commands/pf.cpp | 21 + zone/gm_commands/picklock.cpp | 24 + zone/gm_commands/profanity.cpp | 74 + zone/gm_commands/profilereset.cpp | 8 + zone/gm_commands/proximity.cpp | 74 + zone/gm_commands/push.cpp | 35 + zone/gm_commands/pvp.cpp | 20 + zone/gm_commands/qglobal.cpp | 62 + zone/gm_commands/questerrors.cpp | 23 + zone/gm_commands/race.cpp | 29 + zone/gm_commands/raidloot.cpp | 70 + zone/gm_commands/randomfeatures.cpp | 18 + zone/gm_commands/refreshgroup.cpp | 19 + zone/gm_commands/reloadaa.cpp | 17 + zone/gm_commands/reloadallrules.cpp | 16 + zone/gm_commands/reloademote.cpp | 9 + zone/gm_commands/reloadlevelmods.cpp | 15 + zone/gm_commands/reloadmerchants.cpp | 8 + zone/gm_commands/reloadperlexportsettings.cpp | 16 + zone/gm_commands/reloadqst.cpp | 23 + zone/gm_commands/reloadstatic.cpp | 8 + zone/gm_commands/reloadtitles.cpp | 14 + zone/gm_commands/reloadtraps.cpp | 8 + zone/gm_commands/reloadworld.cpp | 22 + zone/gm_commands/reloadworldrules.cpp | 15 + zone/gm_commands/reloadzps.cpp | 8 + zone/gm_commands/repop.cpp | 40 + zone/gm_commands/resetaa.cpp | 13 + zone/gm_commands/resetaa_timer.cpp | 26 + zone/gm_commands/resetdisc_timer.cpp | 23 + zone/gm_commands/revoke.cpp | 48 + zone/gm_commands/roambox.cpp | 93 + zone/gm_commands/rules.cpp | 232 + zone/gm_commands/save.cpp | 33 + zone/gm_commands/scale.cpp | 136 + zone/gm_commands/scribespell.cpp | 66 + zone/gm_commands/scribespells.cpp | 65 + zone/gm_commands/sendzonespawns.cpp | 7 + zone/gm_commands/sensetrap.cpp | 24 + zone/gm_commands/serverinfo.cpp | 33 + zone/gm_commands/serverrules.cpp | 7 + zone/gm_commands/set_adventure_points.cpp | 24 + zone/gm_commands/setaapts.cpp | 63 + zone/gm_commands/setaaxp.cpp | 67 + zone/gm_commands/setanim.cpp | 16 + zone/gm_commands/setcrystals.cpp | 53 + zone/gm_commands/setfaction.cpp | 20 + zone/gm_commands/setgraveyard.cpp | 44 + zone/gm_commands/setlanguage.cpp | 61 + zone/gm_commands/setlsinfo.cpp | 24 + zone/gm_commands/setpass.cpp | 29 + zone/gm_commands/setpvppoints.cpp | 32 + zone/gm_commands/setskill.cpp | 55 + zone/gm_commands/setskillall.cpp | 30 + zone/gm_commands/setstartzone.cpp | 35 + zone/gm_commands/setstat.cpp | 14 + zone/gm_commands/setxp.cpp | 23 + zone/gm_commands/showbonusstats.cpp | 49 + zone/gm_commands/showbuffs.cpp | 12 + zone/gm_commands/shownpcgloballoot.cpp | 16 + zone/gm_commands/shownumhits.cpp | 8 + zone/gm_commands/showskills.cpp | 44 + zone/gm_commands/showspellslist.cpp | 13 + zone/gm_commands/showstats.cpp | 12 + zone/gm_commands/showzonegloballoot.cpp | 13 + zone/gm_commands/showzonepoints.cpp | 157 + zone/gm_commands/shutdown.cpp | 8 + zone/gm_commands/size.cpp | 46 + zone/gm_commands/spawn.cpp | 29 + zone/gm_commands/spawnfix.cpp | 39 + zone/gm_commands/spawnstatus.cpp | 28 + zone/gm_commands/spellinfo.cpp | 154 + zone/gm_commands/stun.cpp | 65 + zone/gm_commands/summon.cpp | 97 + zone/gm_commands/summonburiedplayercorpse.cpp | 28 + zone/gm_commands/summonitem.cpp | 92 + zone/gm_commands/suspend.cpp | 101 + zone/gm_commands/task.cpp | 198 + zone/gm_commands/tattoo.cpp | 37 + zone/gm_commands/tempname.cpp | 22 + zone/gm_commands/texture.cpp | 52 + zone/gm_commands/time.cpp | 32 + zone/gm_commands/timers.cpp | 22 + zone/gm_commands/timezone.cpp | 32 + zone/gm_commands/title.cpp | 65 + zone/gm_commands/titlesuffix.cpp | 65 + zone/gm_commands/traindisc.cpp | 65 + zone/gm_commands/trapinfo.cpp | 7 + zone/gm_commands/tune.cpp | 524 + zone/gm_commands/ucs.cpp | 89 + zone/gm_commands/undye.cpp | 12 + zone/gm_commands/undyeme.cpp | 10 + zone/gm_commands/unfreeze.cpp | 12 + zone/gm_commands/unlock.cpp | 15 + zone/gm_commands/unscribespell.cpp | 62 + zone/gm_commands/unscribespells.cpp | 13 + zone/gm_commands/untraindisc.cpp | 17 + zone/gm_commands/untraindiscs.cpp | 13 + zone/gm_commands/uptime.cpp | 22 + zone/gm_commands/version.cpp | 10 + zone/gm_commands/viewnpctype.cpp | 31 + zone/gm_commands/viewpetition.cpp | 31 + zone/gm_commands/viewzoneloot.cpp | 117 + zone/gm_commands/wc.cpp | 44 + zone/gm_commands/weather.cpp | 70 + zone/gm_commands/who.cpp | 202 + zone/gm_commands/worldshutdown.cpp | 79 + zone/gm_commands/worldwide.cpp | 148 + zone/gm_commands/wp.cpp | 51 + zone/gm_commands/wpadd.cpp | 52 + zone/gm_commands/wpinfo.cpp | 15 + zone/gm_commands/xtargets.cpp | 28 + zone/gm_commands/zclip.cpp | 39 + zone/gm_commands/zcolor.cpp | 30 + zone/gm_commands/zheader.cpp | 26 + zone/gm_commands/zonebootup.cpp | 25 + zone/gm_commands/zonelock.cpp | 76 + zone/gm_commands/zoneshutdown.cpp | 30 + zone/gm_commands/zonespawn.cpp | 40 + zone/gm_commands/zonestatus.cpp | 19 + zone/gm_commands/zopp.cpp | 64 + zone/gm_commands/zsafecoords.cpp | 26 + zone/gm_commands/zsave.cpp | 12 + zone/gm_commands/zsky.cpp | 20 + zone/gm_commands/zstats.cpp | 208 + zone/gm_commands/zunderworld.cpp | 12 + zone/gm_commands/zuwcoords.cpp | 19 + 291 files changed, 19224 insertions(+), 15798 deletions(-) create mode 100644 utils/gm_commands/main.go create mode 100755 zone/gm_commands/acceptrules.cpp create mode 100755 zone/gm_commands/advnpcspawn.cpp create mode 100755 zone/gm_commands/aggro.cpp create mode 100755 zone/gm_commands/aggrozone.cpp create mode 100755 zone/gm_commands/ai.cpp create mode 100755 zone/gm_commands/appearance.cpp create mode 100755 zone/gm_commands/attack.cpp create mode 100755 zone/gm_commands/augmentitem.cpp create mode 100755 zone/gm_commands/ban.cpp create mode 100755 zone/gm_commands/beard.cpp create mode 100755 zone/gm_commands/beardcolor.cpp create mode 100755 zone/gm_commands/bestz.cpp create mode 100755 zone/gm_commands/bind.cpp create mode 100755 zone/gm_commands/camerashake.cpp create mode 100755 zone/gm_commands/castspell.cpp create mode 100755 zone/gm_commands/chat.cpp create mode 100755 zone/gm_commands/checklos.cpp create mode 100755 zone/gm_commands/copycharacter.cpp create mode 100755 zone/gm_commands/corpse.cpp create mode 100755 zone/gm_commands/corpsefix.cpp create mode 100755 zone/gm_commands/cvs.cpp create mode 100755 zone/gm_commands/damage.cpp create mode 100755 zone/gm_commands/databuckets.cpp create mode 100755 zone/gm_commands/date.cpp create mode 100755 zone/gm_commands/dbspawn2.cpp create mode 100755 zone/gm_commands/delacct.cpp create mode 100755 zone/gm_commands/deletegraveyard.cpp create mode 100755 zone/gm_commands/delpetition.cpp create mode 100755 zone/gm_commands/depop.cpp create mode 100755 zone/gm_commands/depopzone.cpp create mode 100755 zone/gm_commands/details.cpp create mode 100755 zone/gm_commands/devtools.cpp create mode 100755 zone/gm_commands/disablerecipe.cpp create mode 100755 zone/gm_commands/disarmtrap.cpp create mode 100755 zone/gm_commands/distance.cpp create mode 100755 zone/gm_commands/doanim.cpp create mode 100755 zone/gm_commands/door.cpp create mode 100755 zone/gm_commands/dye.cpp create mode 100755 zone/gm_commands/dz.cpp create mode 100755 zone/gm_commands/dzkickplayers.cpp create mode 100755 zone/gm_commands/editmassrespawn.cpp create mode 100755 zone/gm_commands/emote.cpp create mode 100755 zone/gm_commands/emotesearch.cpp create mode 100755 zone/gm_commands/emoteview.cpp create mode 100755 zone/gm_commands/enablerecipe.cpp create mode 100755 zone/gm_commands/endurance.cpp create mode 100755 zone/gm_commands/equipitem.cpp create mode 100755 zone/gm_commands/face.cpp create mode 100755 zone/gm_commands/faction.cpp create mode 100755 zone/gm_commands/findclass.cpp create mode 100755 zone/gm_commands/findfaction.cpp create mode 100755 zone/gm_commands/findnpctype.cpp create mode 100755 zone/gm_commands/findrace.cpp create mode 100755 zone/gm_commands/findskill.cpp create mode 100755 zone/gm_commands/findspell.cpp create mode 100755 zone/gm_commands/findtask.cpp create mode 100755 zone/gm_commands/findzone.cpp create mode 100755 zone/gm_commands/fixmob.cpp create mode 100755 zone/gm_commands/flag.cpp create mode 100755 zone/gm_commands/flagedit.cpp create mode 100755 zone/gm_commands/flags.cpp create mode 100755 zone/gm_commands/flymode.cpp create mode 100755 zone/gm_commands/fov.cpp create mode 100755 zone/gm_commands/freeze.cpp create mode 100755 zone/gm_commands/gassign.cpp create mode 100755 zone/gm_commands/gearup.cpp create mode 100755 zone/gm_commands/gender.cpp create mode 100755 zone/gm_commands/getplayerburiedcorpsecount.cpp create mode 100755 zone/gm_commands/getvariable.cpp create mode 100755 zone/gm_commands/ginfo.cpp create mode 100755 zone/gm_commands/giveitem.cpp create mode 100755 zone/gm_commands/givemoney.cpp create mode 100755 zone/gm_commands/globalview.cpp create mode 100755 zone/gm_commands/gm.cpp create mode 100755 zone/gm_commands/gmspeed.cpp create mode 100755 zone/gm_commands/gmzone.cpp create mode 100755 zone/gm_commands/goto.cpp create mode 100755 zone/gm_commands/grid.cpp create mode 100755 zone/gm_commands/guild.cpp create mode 100755 zone/gm_commands/guildapprove.cpp create mode 100755 zone/gm_commands/guildcreate.cpp create mode 100755 zone/gm_commands/guildlist.cpp create mode 100755 zone/gm_commands/hair.cpp create mode 100755 zone/gm_commands/haircolor.cpp create mode 100755 zone/gm_commands/haste.cpp create mode 100755 zone/gm_commands/hatelist.cpp create mode 100755 zone/gm_commands/heal.cpp create mode 100755 zone/gm_commands/helm.cpp create mode 100755 zone/gm_commands/heritage.cpp create mode 100755 zone/gm_commands/heromodel.cpp create mode 100755 zone/gm_commands/hideme.cpp create mode 100755 zone/gm_commands/hp.cpp create mode 100755 zone/gm_commands/incstat.cpp create mode 100755 zone/gm_commands/instance.cpp create mode 100755 zone/gm_commands/interrogateinv.cpp create mode 100755 zone/gm_commands/interrupt.cpp create mode 100755 zone/gm_commands/invsnapshot.cpp create mode 100755 zone/gm_commands/invul.cpp create mode 100755 zone/gm_commands/ipban.cpp create mode 100755 zone/gm_commands/iplookup.cpp create mode 100755 zone/gm_commands/iteminfo.cpp create mode 100755 zone/gm_commands/itemsearch.cpp create mode 100755 zone/gm_commands/kick.cpp create mode 100755 zone/gm_commands/kill.cpp create mode 100755 zone/gm_commands/killallnpcs.cpp create mode 100755 zone/gm_commands/lastname.cpp create mode 100755 zone/gm_commands/list.cpp create mode 100755 zone/gm_commands/listpetition.cpp create mode 100755 zone/gm_commands/loc.cpp create mode 100755 zone/gm_commands/lock.cpp create mode 100755 zone/gm_commands/logcommand.cpp create mode 100755 zone/gm_commands/logs.cpp create mode 100755 zone/gm_commands/makepet.cpp create mode 100755 zone/gm_commands/mana.cpp create mode 100755 zone/gm_commands/max_all_skills.cpp create mode 100755 zone/gm_commands/memspell.cpp create mode 100755 zone/gm_commands/merchantcloseshop.cpp create mode 100755 zone/gm_commands/merchantopenshop.cpp create mode 100755 zone/gm_commands/modifynpcstat.cpp create mode 100755 zone/gm_commands/motd.cpp create mode 100755 zone/gm_commands/movechar.cpp create mode 100755 zone/gm_commands/movement.cpp create mode 100755 zone/gm_commands/myskills.cpp create mode 100755 zone/gm_commands/mysql.cpp create mode 100755 zone/gm_commands/mystats.cpp create mode 100755 zone/gm_commands/name.cpp create mode 100755 zone/gm_commands/netstats.cpp create mode 100755 zone/gm_commands/network.cpp create mode 100755 zone/gm_commands/npccast.cpp create mode 100755 zone/gm_commands/npcedit.cpp create mode 100755 zone/gm_commands/npceditmass.cpp create mode 100755 zone/gm_commands/npcemote.cpp create mode 100755 zone/gm_commands/npcloot.cpp create mode 100755 zone/gm_commands/npcsay.cpp create mode 100755 zone/gm_commands/npcshout.cpp create mode 100755 zone/gm_commands/npcspawn.cpp create mode 100755 zone/gm_commands/npcspecialattk.cpp create mode 100755 zone/gm_commands/npcstats.cpp create mode 100755 zone/gm_commands/npctype_cache.cpp create mode 100755 zone/gm_commands/npctypespawn.cpp create mode 100755 zone/gm_commands/nudge.cpp create mode 100755 zone/gm_commands/nukebuffs.cpp create mode 100755 zone/gm_commands/nukeitem.cpp create mode 100755 zone/gm_commands/object.cpp create mode 100755 zone/gm_commands/oocmute.cpp create mode 100755 zone/gm_commands/opcode.cpp create mode 100755 zone/gm_commands/path.cpp create mode 100755 zone/gm_commands/peekinv.cpp create mode 100755 zone/gm_commands/peqzone.cpp create mode 100755 zone/gm_commands/permaclass.cpp create mode 100755 zone/gm_commands/permagender.cpp create mode 100755 zone/gm_commands/permarace.cpp create mode 100755 zone/gm_commands/petitioninfo.cpp create mode 100755 zone/gm_commands/petname.cpp create mode 100755 zone/gm_commands/pf.cpp create mode 100755 zone/gm_commands/picklock.cpp create mode 100755 zone/gm_commands/profanity.cpp create mode 100755 zone/gm_commands/profilereset.cpp create mode 100755 zone/gm_commands/proximity.cpp create mode 100755 zone/gm_commands/push.cpp create mode 100755 zone/gm_commands/pvp.cpp create mode 100755 zone/gm_commands/qglobal.cpp create mode 100755 zone/gm_commands/questerrors.cpp create mode 100755 zone/gm_commands/race.cpp create mode 100755 zone/gm_commands/raidloot.cpp create mode 100755 zone/gm_commands/randomfeatures.cpp create mode 100755 zone/gm_commands/refreshgroup.cpp create mode 100755 zone/gm_commands/reloadaa.cpp create mode 100755 zone/gm_commands/reloadallrules.cpp create mode 100755 zone/gm_commands/reloademote.cpp create mode 100755 zone/gm_commands/reloadlevelmods.cpp create mode 100755 zone/gm_commands/reloadmerchants.cpp create mode 100755 zone/gm_commands/reloadperlexportsettings.cpp create mode 100755 zone/gm_commands/reloadqst.cpp create mode 100755 zone/gm_commands/reloadstatic.cpp create mode 100755 zone/gm_commands/reloadtitles.cpp create mode 100755 zone/gm_commands/reloadtraps.cpp create mode 100755 zone/gm_commands/reloadworld.cpp create mode 100755 zone/gm_commands/reloadworldrules.cpp create mode 100755 zone/gm_commands/reloadzps.cpp create mode 100755 zone/gm_commands/repop.cpp create mode 100755 zone/gm_commands/resetaa.cpp create mode 100755 zone/gm_commands/resetaa_timer.cpp create mode 100755 zone/gm_commands/resetdisc_timer.cpp create mode 100755 zone/gm_commands/revoke.cpp create mode 100755 zone/gm_commands/roambox.cpp create mode 100755 zone/gm_commands/rules.cpp create mode 100755 zone/gm_commands/save.cpp create mode 100755 zone/gm_commands/scale.cpp create mode 100755 zone/gm_commands/scribespell.cpp create mode 100755 zone/gm_commands/scribespells.cpp create mode 100755 zone/gm_commands/sendzonespawns.cpp create mode 100755 zone/gm_commands/sensetrap.cpp create mode 100755 zone/gm_commands/serverinfo.cpp create mode 100755 zone/gm_commands/serverrules.cpp create mode 100755 zone/gm_commands/set_adventure_points.cpp create mode 100755 zone/gm_commands/setaapts.cpp create mode 100755 zone/gm_commands/setaaxp.cpp create mode 100755 zone/gm_commands/setanim.cpp create mode 100755 zone/gm_commands/setcrystals.cpp create mode 100755 zone/gm_commands/setfaction.cpp create mode 100755 zone/gm_commands/setgraveyard.cpp create mode 100755 zone/gm_commands/setlanguage.cpp create mode 100755 zone/gm_commands/setlsinfo.cpp create mode 100755 zone/gm_commands/setpass.cpp create mode 100755 zone/gm_commands/setpvppoints.cpp create mode 100755 zone/gm_commands/setskill.cpp create mode 100755 zone/gm_commands/setskillall.cpp create mode 100755 zone/gm_commands/setstartzone.cpp create mode 100755 zone/gm_commands/setstat.cpp create mode 100755 zone/gm_commands/setxp.cpp create mode 100755 zone/gm_commands/showbonusstats.cpp create mode 100755 zone/gm_commands/showbuffs.cpp create mode 100755 zone/gm_commands/shownpcgloballoot.cpp create mode 100755 zone/gm_commands/shownumhits.cpp create mode 100755 zone/gm_commands/showskills.cpp create mode 100755 zone/gm_commands/showspellslist.cpp create mode 100755 zone/gm_commands/showstats.cpp create mode 100755 zone/gm_commands/showzonegloballoot.cpp create mode 100755 zone/gm_commands/showzonepoints.cpp create mode 100755 zone/gm_commands/shutdown.cpp create mode 100755 zone/gm_commands/size.cpp create mode 100755 zone/gm_commands/spawn.cpp create mode 100755 zone/gm_commands/spawnfix.cpp create mode 100755 zone/gm_commands/spawnstatus.cpp create mode 100755 zone/gm_commands/spellinfo.cpp create mode 100755 zone/gm_commands/stun.cpp create mode 100755 zone/gm_commands/summon.cpp create mode 100755 zone/gm_commands/summonburiedplayercorpse.cpp create mode 100755 zone/gm_commands/summonitem.cpp create mode 100755 zone/gm_commands/suspend.cpp create mode 100755 zone/gm_commands/task.cpp create mode 100755 zone/gm_commands/tattoo.cpp create mode 100755 zone/gm_commands/tempname.cpp create mode 100755 zone/gm_commands/texture.cpp create mode 100755 zone/gm_commands/time.cpp create mode 100755 zone/gm_commands/timers.cpp create mode 100755 zone/gm_commands/timezone.cpp create mode 100755 zone/gm_commands/title.cpp create mode 100755 zone/gm_commands/titlesuffix.cpp create mode 100755 zone/gm_commands/traindisc.cpp create mode 100755 zone/gm_commands/trapinfo.cpp create mode 100755 zone/gm_commands/tune.cpp create mode 100755 zone/gm_commands/ucs.cpp create mode 100755 zone/gm_commands/undye.cpp create mode 100755 zone/gm_commands/undyeme.cpp create mode 100755 zone/gm_commands/unfreeze.cpp create mode 100755 zone/gm_commands/unlock.cpp create mode 100755 zone/gm_commands/unscribespell.cpp create mode 100755 zone/gm_commands/unscribespells.cpp create mode 100755 zone/gm_commands/untraindisc.cpp create mode 100755 zone/gm_commands/untraindiscs.cpp create mode 100755 zone/gm_commands/uptime.cpp create mode 100755 zone/gm_commands/version.cpp create mode 100755 zone/gm_commands/viewnpctype.cpp create mode 100755 zone/gm_commands/viewpetition.cpp create mode 100755 zone/gm_commands/viewzoneloot.cpp create mode 100755 zone/gm_commands/wc.cpp create mode 100755 zone/gm_commands/weather.cpp create mode 100755 zone/gm_commands/who.cpp create mode 100755 zone/gm_commands/worldshutdown.cpp create mode 100755 zone/gm_commands/worldwide.cpp create mode 100755 zone/gm_commands/wp.cpp create mode 100755 zone/gm_commands/wpadd.cpp create mode 100755 zone/gm_commands/wpinfo.cpp create mode 100755 zone/gm_commands/xtargets.cpp create mode 100755 zone/gm_commands/zclip.cpp create mode 100755 zone/gm_commands/zcolor.cpp create mode 100755 zone/gm_commands/zheader.cpp create mode 100755 zone/gm_commands/zonebootup.cpp create mode 100755 zone/gm_commands/zonelock.cpp create mode 100755 zone/gm_commands/zoneshutdown.cpp create mode 100755 zone/gm_commands/zonespawn.cpp create mode 100755 zone/gm_commands/zonestatus.cpp create mode 100755 zone/gm_commands/zopp.cpp create mode 100755 zone/gm_commands/zsafecoords.cpp create mode 100755 zone/gm_commands/zsave.cpp create mode 100755 zone/gm_commands/zsky.cpp create mode 100755 zone/gm_commands/zstats.cpp create mode 100755 zone/gm_commands/zunderworld.cpp create mode 100755 zone/gm_commands/zuwcoords.cpp diff --git a/.gitignore b/.gitignore index 1db525804..d835a1ceb 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,5 @@ bin/ /Win32 /x64 /client_files/**/CMakeFiles/ + +.idea diff --git a/common/file_util.cpp b/common/file_util.cpp index 8c9aca9a4..7e2535512 100644 --- a/common/file_util.cpp +++ b/common/file_util.cpp @@ -64,4 +64,9 @@ void FileUtil::mkdir(const std::string& directory_name) } ::mkdir(directory_name.c_str(), 0755); #endif -} \ No newline at end of file +} + +bool file_exists(const std::string& name) { + std::ifstream f(name.c_str()); + return f.good(); +} diff --git a/common/file_util.h b/common/file_util.h index 05869d303..02e47d198 100644 --- a/common/file_util.h +++ b/common/file_util.h @@ -28,5 +28,6 @@ public: static void mkdir(const std::string& directory_name); }; +bool file_exists(const std::string& name); #endif //EQEMU_FILE_UTIL_H diff --git a/common/spdat.cpp b/common/spdat.cpp index 525c70c1c..b81b8167c 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1239,10 +1239,10 @@ bool IsEffectIgnoredInStacking(int spa) case SE_GravityEffect: case 425: //Spell effects implemented after ROF2, following same pattern, lets assume these should go here. - case SE_Fc_Spell_Damage_Pct_IncomingPC: + case SE_Fc_Spell_Damage_Pct_IncomingPC: case SE_Fc_Spell_Damage_Amt_IncomingPC: case SE_Ff_CasterClass: - case SE_Ff_Same_Caster: + case SE_Ff_Same_Caster: case SE_Proc_Timer_Modifier: case SE_Weapon_Stance: case SE_TwinCastBlocker: @@ -1417,8 +1417,8 @@ bool IsInstrumentModAppliedToSpellEffect(int32 spell_id, int effect) case SE_MinDamageModifier: case SE_ProcChance: case SE_PetFlurry: // ? Need verified - case SE_DiseaseCounter: - case SE_PoisonCounter: + case SE_DiseaseCounter: + case SE_PoisonCounter: case SE_CurseCounter: case SE_CorruptionCounter: return true; @@ -1576,7 +1576,7 @@ int GetSpellStatValue(uint32 spell_id, const char* stat_identifier, uint8 slot) return 0; } -bool IsVirusSpell(int32 spell_id) +bool IsVirusSpell(int32 spell_id) { if (GetViralMinSpreadTime(spell_id) && GetViralMaxSpreadTime(spell_id) && GetViralSpreadRange(spell_id)){ return true; @@ -1584,17 +1584,17 @@ bool IsVirusSpell(int32 spell_id) return false; } -int32 GetViralMinSpreadTime(int32 spell_id) +int32 GetViralMinSpreadTime(int32 spell_id) { return spells[spell_id].viral_targets; } -int32 GetViralMaxSpreadTime(int32 spell_id) +int32 GetViralMaxSpreadTime(int32 spell_id) { return spells[spell_id].viral_timer; } -int32 GetViralSpreadRange(int32 spell_id) +int32 GetViralSpreadRange(int32 spell_id) { return spells[spell_id].viral_range; } @@ -1605,7 +1605,7 @@ uint32 GetProcLimitTimer(int32 spell_id, int proc_type) { if (!IsValidSpell(spell_id)) { return 0; } - + bool use_next_timer = false; for (int i = 0; i < EFFECT_COUNT; ++i) { @@ -1633,3 +1633,41 @@ uint32 GetProcLimitTimer(int32 spell_id, int proc_type) { } return 0; } + +bool CastRestrictedSpell(int spellid) +{ + switch (spellid) { + case SPELL_TOUCH_OF_VINITRAS: + case SPELL_DESPERATE_HOPE: + case SPELL_CHARM: + case SPELL_METAMORPHOSIS65: + case SPELL_JT_BUFF: + case SPELL_CAN_O_WHOOP_ASS: + case SPELL_PHOENIX_CHARM: + case SPELL_CAZIC_TOUCH: + case SPELL_AVATAR_KNOCKBACK: + case SPELL_SHAPECHANGE65: + case SPELL_SUNSET_HOME1218: + case SPELL_SUNSET_HOME819: + case SPELL_SHAPECHANGE75: + case SPELL_SHAPECHANGE80: + case SPELL_SHAPECHANGE85: + case SPELL_SHAPECHANGE90: + case SPELL_SHAPECHANGE95: + case SPELL_SHAPECHANGE100: + case SPELL_SHAPECHANGE25: + case SPELL_SHAPECHANGE30: + case SPELL_SHAPECHANGE35: + case SPELL_SHAPECHANGE40: + case SPELL_SHAPECHANGE45: + case SPELL_SHAPECHANGE50: + case SPELL_NPC_AEGOLISM: + case SPELL_SHAPECHANGE55: + case SPELL_SHAPECHANGE60: + case SPELL_COMMAND_OF_DRUZZIL: + case SPELL_SHAPECHANGE70: + return true; + default: + return false; + } +} diff --git a/common/spdat.h b/common/spdat.h index 2e6d59d5e..4efe92f94 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -219,292 +219,292 @@ enum SpellRestriction { UNKNOWN_3 = 3, // | caster restriction | seen in spell 30183 Mind Spiral IS_NOT_ON_HORSE = 5, // | caster restriction | - IS_ANIMAL_OR_HUMANOID = 100, // This spell will only work on animals or humanoid creatures. - IS_DRAGON = 101, // This spell will only work on dragons. - IS_ANIMAL_OR_INSECT = 102, // This spell will only work on animals or insects. - IS_BODY_TYPE_MISC = 103, // This spell will only work on humanoids, lycanthropes, giants, Kael Drakkel giants, Coldain, animals, insects, constructs, dragons, Skyshrine dragons, Muramites, or creatures constructed from magic. - IS_BODY_TYPE_MISC2 = 104, // This spell will only work on humanoids, lycanthropes, giants, Kael Drakkel giants, Coldain, animals, or insects. - IS_PLANT = 105, // This spell will only work on plants. + IS_ANIMAL_OR_HUMANOID = 100, // This spell will only work on animals or humanoid creatures. + IS_DRAGON = 101, // This spell will only work on dragons. + IS_ANIMAL_OR_INSECT = 102, // This spell will only work on animals or insects. + IS_BODY_TYPE_MISC = 103, // This spell will only work on humanoids, lycanthropes, giants, Kael Drakkel giants, Coldain, animals, insects, constructs, dragons, Skyshrine dragons, Muramites, or creatures constructed from magic. + IS_BODY_TYPE_MISC2 = 104, // This spell will only work on humanoids, lycanthropes, giants, Kael Drakkel giants, Coldain, animals, or insects. + IS_PLANT = 105, // This spell will only work on plants. IS_GIANT = 106, // This spell will only work on animals. | Live used to have this on spells restricted to Giants, but those spells were removed... We still have them - IS_NOT_ANIMAL_OR_HUMANOID = 108, // This spell will only work on targets that are neither animals or humanoid. - IS_BIXIE = 109, // This spell will only work on bixies. - IS_HARPY = 110, // This spell will only work on harpies. - IS_GNOLL = 111, // This spell will only work on gnolls. - IS_SPORALI = 112, // This spell will only work on fungusoids. - IS_KOBOLD = 113, // This spell will only work on kobolds. - IS_FROSTCRYPT_SHADE = 114, // This spell will only work on undead creatures or the Shades of Frostcrypt. - IS_DRAKKIN = 115, // This spell will only work on Drakkin. - IS_UNDEAD_OR_VALDEHOLM_GIANT = 116, // This spell will only work on undead creatures or the inhabitants of Valdeholm. - IS_ANIMAL_OR_PLANT = 117, // This spell will only work on plants or animals. - IS_SUMMONED = 118, // This spell will only work on constructs, elementals, or summoned elemental minions. - IS_WIZARD_USED_ON_MAGE_FIRE_PET = 119, // This spell will only work on wizards. | Live uses this on high level mage fire pets, which are wizard class - IS_UNDEAD = 120, // - IS_NOT_UNDEAD_OR_SUMMONED_OR_VAMPIRE = 121, // This spell will only work on creatures that are not undead, constructs, elementals, or vampires. - IS_FAE_OR_PIXIE = 122, // This spell will only work on Fae or pixies. - IS_HUMANOID = 123, // - IS_UNDEAD_AND_HP_LESS_THAN_10_PCT = 124, // The Essence Extractor whirrs but does not light up. - IS_CLOCKWORK_AND_HP_LESS_THAN_45_PCT = 125, // This spell will only work on clockwork gnomes. - IS_WISP_AND_HP_LESS_THAN_10_PCT = 126, // This spell will only work on wisps at or below 10% of their maximum HP. - IS_CLASS_MELEE_THAT_CAN_BASH_OR_KICK_EXCEPT_BARD = 127, // This spell will only work on non-bard targets that can bash or kick. - IS_CLASS_PURE_MELEE = 128, // This spell will only affect melee classes (warriors, monks, rogues, and berserkers). - IS_CLASS_PURE_CASTER = 129, // This spell will only affect pure caster classes (necromancers, wizards, magicians, and enchanters). - IS_CLASS_HYBRID_CLASS = 130, // This spell will only affect hybrid classes (paladins, rangers, shadow knights, bards, and beastlords). - IS_CLASS_WARRIOR = 131, // This spell will only affect Warriors. - IS_CLASS_CLERIC = 132, // This spell will only affect Clerics. - IS_CLASS_PALADIN = 133, // This spell will only affect Paladins. - IS_CLASS_RANGER = 134, // This spell will only affect Rangers. - IS_CLASS_SHADOWKNIGHT = 135, // This spell will only affect Shadow Knights. - IS_CLASS_DRUID = 136, // This spell will only affect Druids. - IS_CLASS_MONK = 137, // This spell will only affect Monks. - IS_CLASS_BARD = 138, // This spell will only affect Bards. - IS_CLASS_ROGUE = 139, // This spell will only affect Rogues. - IS_CLASS_SHAMAN = 140, // This spell will only affect Shamans. - IS_CLASS_NECRO = 141, // This spell will only affect Necromancers. - IS_CLASS_WIZARD = 142, // This spell will only affect Wizards. - IS_CLASS_MAGE = 143, // This spell will only affect Magicians. - IS_CLASS_ENCHANTER = 144, // This spell will only affect Enchanters. - IS_CLASS_BEASTLORD = 145, // This spell will only affect Beastlords. - IS_CLASS_BERSERKER = 146, // This spell will only affect Berserkers. - IS_CLASS_CLR_SHM_DRU = 147, // This spell will only affect priest classes (clerics, druids, and shaman). - IS_CLASS_NOT_WAR_PAL_SK = 148, // This spell will not affect Warriors, Paladins, or Shadow Knights. - IS_LEVEL_UNDER_100 = 150, // This spell will not affect any target over level 100. - IS_NOT_RAID_BOSS = 190, // This spell will not affect raid bosses. - IS_RAID_BOSS = 191, // This spell will only affect raid bosses. - FRENZIED_BURNOUT_ACTIVE = 192, // This spell will only cast if you have Frenzied Burnout active. - FRENZIED_BURNOUT_NOT_ACTIVE = 193, // This spell will only cast if you do not have Frenzied Burnout active. + IS_NOT_ANIMAL_OR_HUMANOID = 108, // This spell will only work on targets that are neither animals or humanoid. + IS_BIXIE = 109, // This spell will only work on bixies. + IS_HARPY = 110, // This spell will only work on harpies. + IS_GNOLL = 111, // This spell will only work on gnolls. + IS_SPORALI = 112, // This spell will only work on fungusoids. + IS_KOBOLD = 113, // This spell will only work on kobolds. + IS_FROSTCRYPT_SHADE = 114, // This spell will only work on undead creatures or the Shades of Frostcrypt. + IS_DRAKKIN = 115, // This spell will only work on Drakkin. + IS_UNDEAD_OR_VALDEHOLM_GIANT = 116, // This spell will only work on undead creatures or the inhabitants of Valdeholm. + IS_ANIMAL_OR_PLANT = 117, // This spell will only work on plants or animals. + IS_SUMMONED = 118, // This spell will only work on constructs, elementals, or summoned elemental minions. + IS_WIZARD_USED_ON_MAGE_FIRE_PET = 119, // This spell will only work on wizards. | Live uses this on high level mage fire pets, which are wizard class + IS_UNDEAD = 120, // + IS_NOT_UNDEAD_OR_SUMMONED_OR_VAMPIRE = 121, // This spell will only work on creatures that are not undead, constructs, elementals, or vampires. + IS_FAE_OR_PIXIE = 122, // This spell will only work on Fae or pixies. + IS_HUMANOID = 123, // + IS_UNDEAD_AND_HP_LESS_THAN_10_PCT = 124, // The Essence Extractor whirrs but does not light up. + IS_CLOCKWORK_AND_HP_LESS_THAN_45_PCT = 125, // This spell will only work on clockwork gnomes. + IS_WISP_AND_HP_LESS_THAN_10_PCT = 126, // This spell will only work on wisps at or below 10% of their maximum HP. + IS_CLASS_MELEE_THAT_CAN_BASH_OR_KICK_EXCEPT_BARD = 127, // This spell will only work on non-bard targets that can bash or kick. + IS_CLASS_PURE_MELEE = 128, // This spell will only affect melee classes (warriors, monks, rogues, and berserkers). + IS_CLASS_PURE_CASTER = 129, // This spell will only affect pure caster classes (necromancers, wizards, magicians, and enchanters). + IS_CLASS_HYBRID_CLASS = 130, // This spell will only affect hybrid classes (paladins, rangers, shadow knights, bards, and beastlords). + IS_CLASS_WARRIOR = 131, // This spell will only affect Warriors. + IS_CLASS_CLERIC = 132, // This spell will only affect Clerics. + IS_CLASS_PALADIN = 133, // This spell will only affect Paladins. + IS_CLASS_RANGER = 134, // This spell will only affect Rangers. + IS_CLASS_SHADOWKNIGHT = 135, // This spell will only affect Shadow Knights. + IS_CLASS_DRUID = 136, // This spell will only affect Druids. + IS_CLASS_MONK = 137, // This spell will only affect Monks. + IS_CLASS_BARD = 138, // This spell will only affect Bards. + IS_CLASS_ROGUE = 139, // This spell will only affect Rogues. + IS_CLASS_SHAMAN = 140, // This spell will only affect Shamans. + IS_CLASS_NECRO = 141, // This spell will only affect Necromancers. + IS_CLASS_WIZARD = 142, // This spell will only affect Wizards. + IS_CLASS_MAGE = 143, // This spell will only affect Magicians. + IS_CLASS_ENCHANTER = 144, // This spell will only affect Enchanters. + IS_CLASS_BEASTLORD = 145, // This spell will only affect Beastlords. + IS_CLASS_BERSERKER = 146, // This spell will only affect Berserkers. + IS_CLASS_CLR_SHM_DRU = 147, // This spell will only affect priest classes (clerics, druids, and shaman). + IS_CLASS_NOT_WAR_PAL_SK = 148, // This spell will not affect Warriors, Paladins, or Shadow Knights. + IS_LEVEL_UNDER_100 = 150, // This spell will not affect any target over level 100. + IS_NOT_RAID_BOSS = 190, // This spell will not affect raid bosses. + IS_RAID_BOSS = 191, // This spell will only affect raid bosses. + FRENZIED_BURNOUT_ACTIVE = 192, // This spell will only cast if you have Frenzied Burnout active. + FRENZIED_BURNOUT_NOT_ACTIVE = 193, // This spell will only cast if you do not have Frenzied Burnout active. UNKNOWN_199 = 199, // - IS_HP_ABOVE_75_PCT = 201, // + IS_HP_ABOVE_75_PCT = 201, // IS_HP_LESS_THAN_20_PCT = 203, // Your target's HP must be at 20% of its maximum or below. | caster restriction | IS_HP_LESS_THAN_50_PCT = 204, // Your target's HP must be at 50% of its maximum or below. | caster restriction | - IS_HP_LESS_THAN_75_PCT = 205, // Your target's HP must be at 75% of its maximum or below. - IS_NOT_IN_COMBAT = 216, // This spell will only affect creatures that are not in combat. - HAS_AT_LEAST_1_PET_ON_HATELIST = 221, // - HAS_AT_LEAST_2_PETS_ON_HATELIST = 222, // - HAS_AT_LEAST_3_PETS_ON_HATELIST = 223, // - HAS_AT_LEAST_4_PETS_ON_HATELIST = 224, // - HAS_AT_LEAST_5_PETS_ON_HATELIST = 225, // - HAS_AT_LEAST_6_PETS_ON_HATELIST = 226, // - HAS_AT_LEAST_7_PETS_ON_HATELIST = 227, // - HAS_AT_LEAST_8_PETS_ON_HATELIST = 228, // - HAS_AT_LEAST_9_PETS_ON_HATELIST = 229, // - HAS_AT_LEAST_10_PETS_ON_HATELIST = 230, // - HAS_AT_LEAST_11_PETS_ON_HATELIST = 231, // - HAS_AT_LEAST_12_PETS_ON_HATELIST = 232, // - HAS_AT_LEAST_13_PETS_ON_HATELIST = 233, // - HAS_AT_LEAST_14_PETS_ON_HATELIST = 234, // - HAS_AT_LEAST_15_PETS_ON_HATELIST = 235, // - HAS_AT_LEAST_16_PETS_ON_HATELIST = 236, // - HAS_AT_LEAST_17_PETS_ON_HATELIST = 237, // - HAS_AT_LEAST_18_PETS_ON_HATELIST = 238, // - HAS_AT_LEAST_19_PETS_ON_HATELIST = 239, // - HAS_AT_LEAST_20_PETS_ON_HATELIST = 240, // + IS_HP_LESS_THAN_75_PCT = 205, // Your target's HP must be at 75% of its maximum or below. + IS_NOT_IN_COMBAT = 216, // This spell will only affect creatures that are not in combat. + HAS_AT_LEAST_1_PET_ON_HATELIST = 221, // + HAS_AT_LEAST_2_PETS_ON_HATELIST = 222, // + HAS_AT_LEAST_3_PETS_ON_HATELIST = 223, // + HAS_AT_LEAST_4_PETS_ON_HATELIST = 224, // + HAS_AT_LEAST_5_PETS_ON_HATELIST = 225, // + HAS_AT_LEAST_6_PETS_ON_HATELIST = 226, // + HAS_AT_LEAST_7_PETS_ON_HATELIST = 227, // + HAS_AT_LEAST_8_PETS_ON_HATELIST = 228, // + HAS_AT_LEAST_9_PETS_ON_HATELIST = 229, // + HAS_AT_LEAST_10_PETS_ON_HATELIST = 230, // + HAS_AT_LEAST_11_PETS_ON_HATELIST = 231, // + HAS_AT_LEAST_12_PETS_ON_HATELIST = 232, // + HAS_AT_LEAST_13_PETS_ON_HATELIST = 233, // + HAS_AT_LEAST_14_PETS_ON_HATELIST = 234, // + HAS_AT_LEAST_15_PETS_ON_HATELIST = 235, // + HAS_AT_LEAST_16_PETS_ON_HATELIST = 236, // + HAS_AT_LEAST_17_PETS_ON_HATELIST = 237, // + HAS_AT_LEAST_18_PETS_ON_HATELIST = 238, // + HAS_AT_LEAST_19_PETS_ON_HATELIST = 239, // + HAS_AT_LEAST_20_PETS_ON_HATELIST = 240, // IS_HP_LESS_THAN_35_PCT = 250, // Your target's HP must be at 35% of its maximum or below. - HAS_BETWEEN_1_TO_2_PETS_ON_HATELIST = 260, // between 1 and 2 pets - HAS_BETWEEN_3_TO_5_PETS_ON_HATELIST = 261, // between 3 and 5 pets - HAS_BETWEEN_6_TO_9_PETS_ON_HATELIST = 262, // between 6 and 9 pets - HAS_BETWEEN_10_TO_14_PETS_ON_HATELIST = 263, // between 10 and 14 pets - HAS_MORE_THAN_14_PETS_ON_HATELIST = 264, // 15 or more pets - IS_CLASS_CHAIN_OR_PLATE = 304, // This spell will only affect plate or chain wearing classes. - IS_HP_BETWEEN_5_AND_9_PCT = 350, // Your target's HP must be between 5% and 9% of its maximum. - IS_HP_BETWEEN_10_AND_14_PCT = 351, // Your target's HP must be between 10% and 14% of its maximum. - IS_HP_BETWEEN_15_AND_19_PCT = 352, // Your target's HP must be between 15% and 19% of its maximum. - IS_HP_BETWEEN_20_AND_24_PCT = 353, // Your target's HP must be between 20% and 24% of its maximum. - IS_HP_BETWEEN_25_AND_29_PCT = 354, // Your target's HP must be between 25% and 29% of its maximum. - IS_HP_BETWEEN_30_AND_34_PCT = 355, // Your target's HP must be between 30% and 34% of its maximum. - IS_HP_BETWEEN_35_AND_39_PCT = 356, // Your target's HP must be between 35% and 39% of its maximum. - IS_HP_BETWEEN_40_AND_44_PCT = 357, // Your target's HP must be between 40% and 44% of its maximum. - IS_HP_BETWEEN_45_AND_49_PCT = 358, // Your target's HP must be between 45% and 49% of its maximum. - IS_HP_BETWEEN_50_AND_54_PCT = 359, // Your target's HP must be between 50% and 54% of its maximum. - IS_HP_BETWEEN_55_AND_59_PCT = 360, // Your target's HP must be between 55% and 59% of its maximum. - IS_HP_BETWEEN_5_AND_15_PCT = 398, // Your target's HP must be between 5% and 15% of its maximum. - IS_HP_BETWEEN_15_AND_25_PCT = 399, // Your target's HP must be between 15% and 25% of its maximum. - IS_HP_BETWEEN_1_AND_25_PCT = 400, // Your target's HP must be at 25% of its maximum or below. - IS_HP_BETWEEN_25_AND_35_PCT = 401, // Your target's HP must be between 25% and 35% of its maximum. - IS_HP_BETWEEN_35_AND_45_PCT = 402, // Your target's HP must be between 35% and 45% of its maximum. - IS_HP_BETWEEN_45_AND_55_PCT = 403, // Your target's HP must be between 45% and 55% of its maximum. - IS_HP_BETWEEN_55_AND_65_PCT = 404, // Your target's HP must be between 55% and 65% of its maximum. - IS_HP_BETWEEN_65_AND_75_PCT = 405, // Your target's HP must be between 65% and 75% of its maximum. - IS_HP_BETWEEN_75_AND_85_PCT = 406, // Your target's HP must be between 75% and 85% of its maximum. - IS_HP_BETWEEN_85_AND_95_PCT = 407, // Your target's HP must be between 85% and 95% of its maximum. - IS_HP_ABOVE_45_PCT = 408, // Your target's HP must be at least 45% of its maximum. - IS_HP_ABOVE_55_PCT = 409, // Your target's HP must be at least 55% of its maximum. - UNKNOWN_TOO_MUCH_HP_410 = 410, // Your target has too much HP to be affected by this spell. - UNKNOWN_TOO_MUCH_HP_411 = 411, // Your target has too much HP to be affected by this spell. - IS_HP_ABOVE_99_PCT = 412, // - IS_MANA_ABOVE_10_PCT = 429, // You must have at least 10% of your maximum mana available to cast this spell. | caster restriction | - IS_HP_BELOW_5_PCT = 501, // - IS_HP_BELOW_10_PCT = 502, // - IS_HP_BELOW_15_PCT = 503, // - IS_HP_BELOW_20_PCT = 504, // Your target's HP must be at 20% of its maximum or below. - IS_HP_BELOW_25_PCT = 505, // - IS_HP_BELOW_30_PCT = 506, // - IS_HP_BELOW_35_PCT = 507, // - IS_HP_BELOW_40_PCT = 508, // - IS_HP_BELOW_45_PCT = 509, // Your target's HP must be at 45% of its maximum or below. - IS_HP_BELOW_50_PCT = 510, // - IS_HP_BELOW_55_PCT = 511, // - IS_HP_BELOW_60_PCT = 512, // - IS_HP_BELOW_65_PCT = 513, // - IS_HP_BELOW_70_PCT = 514, // - IS_HP_BELOW_75_PCT = 515, // - IS_HP_BELOW_80_PCT = 516, // - IS_HP_BELOW_85_PCT = 517, // + HAS_BETWEEN_1_TO_2_PETS_ON_HATELIST = 260, // between 1 and 2 pets + HAS_BETWEEN_3_TO_5_PETS_ON_HATELIST = 261, // between 3 and 5 pets + HAS_BETWEEN_6_TO_9_PETS_ON_HATELIST = 262, // between 6 and 9 pets + HAS_BETWEEN_10_TO_14_PETS_ON_HATELIST = 263, // between 10 and 14 pets + HAS_MORE_THAN_14_PETS_ON_HATELIST = 264, // 15 or more pets + IS_CLASS_CHAIN_OR_PLATE = 304, // This spell will only affect plate or chain wearing classes. + IS_HP_BETWEEN_5_AND_9_PCT = 350, // Your target's HP must be between 5% and 9% of its maximum. + IS_HP_BETWEEN_10_AND_14_PCT = 351, // Your target's HP must be between 10% and 14% of its maximum. + IS_HP_BETWEEN_15_AND_19_PCT = 352, // Your target's HP must be between 15% and 19% of its maximum. + IS_HP_BETWEEN_20_AND_24_PCT = 353, // Your target's HP must be between 20% and 24% of its maximum. + IS_HP_BETWEEN_25_AND_29_PCT = 354, // Your target's HP must be between 25% and 29% of its maximum. + IS_HP_BETWEEN_30_AND_34_PCT = 355, // Your target's HP must be between 30% and 34% of its maximum. + IS_HP_BETWEEN_35_AND_39_PCT = 356, // Your target's HP must be between 35% and 39% of its maximum. + IS_HP_BETWEEN_40_AND_44_PCT = 357, // Your target's HP must be between 40% and 44% of its maximum. + IS_HP_BETWEEN_45_AND_49_PCT = 358, // Your target's HP must be between 45% and 49% of its maximum. + IS_HP_BETWEEN_50_AND_54_PCT = 359, // Your target's HP must be between 50% and 54% of its maximum. + IS_HP_BETWEEN_55_AND_59_PCT = 360, // Your target's HP must be between 55% and 59% of its maximum. + IS_HP_BETWEEN_5_AND_15_PCT = 398, // Your target's HP must be between 5% and 15% of its maximum. + IS_HP_BETWEEN_15_AND_25_PCT = 399, // Your target's HP must be between 15% and 25% of its maximum. + IS_HP_BETWEEN_1_AND_25_PCT = 400, // Your target's HP must be at 25% of its maximum or below. + IS_HP_BETWEEN_25_AND_35_PCT = 401, // Your target's HP must be between 25% and 35% of its maximum. + IS_HP_BETWEEN_35_AND_45_PCT = 402, // Your target's HP must be between 35% and 45% of its maximum. + IS_HP_BETWEEN_45_AND_55_PCT = 403, // Your target's HP must be between 45% and 55% of its maximum. + IS_HP_BETWEEN_55_AND_65_PCT = 404, // Your target's HP must be between 55% and 65% of its maximum. + IS_HP_BETWEEN_65_AND_75_PCT = 405, // Your target's HP must be between 65% and 75% of its maximum. + IS_HP_BETWEEN_75_AND_85_PCT = 406, // Your target's HP must be between 75% and 85% of its maximum. + IS_HP_BETWEEN_85_AND_95_PCT = 407, // Your target's HP must be between 85% and 95% of its maximum. + IS_HP_ABOVE_45_PCT = 408, // Your target's HP must be at least 45% of its maximum. + IS_HP_ABOVE_55_PCT = 409, // Your target's HP must be at least 55% of its maximum. + UNKNOWN_TOO_MUCH_HP_410 = 410, // Your target has too much HP to be affected by this spell. + UNKNOWN_TOO_MUCH_HP_411 = 411, // Your target has too much HP to be affected by this spell. + IS_HP_ABOVE_99_PCT = 412, // + IS_MANA_ABOVE_10_PCT = 429, // You must have at least 10% of your maximum mana available to cast this spell. | caster restriction | + IS_HP_BELOW_5_PCT = 501, // + IS_HP_BELOW_10_PCT = 502, // + IS_HP_BELOW_15_PCT = 503, // + IS_HP_BELOW_20_PCT = 504, // Your target's HP must be at 20% of its maximum or below. + IS_HP_BELOW_25_PCT = 505, // + IS_HP_BELOW_30_PCT = 506, // + IS_HP_BELOW_35_PCT = 507, // + IS_HP_BELOW_40_PCT = 508, // + IS_HP_BELOW_45_PCT = 509, // Your target's HP must be at 45% of its maximum or below. + IS_HP_BELOW_50_PCT = 510, // + IS_HP_BELOW_55_PCT = 511, // + IS_HP_BELOW_60_PCT = 512, // + IS_HP_BELOW_65_PCT = 513, // + IS_HP_BELOW_70_PCT = 514, // + IS_HP_BELOW_75_PCT = 515, // + IS_HP_BELOW_80_PCT = 516, // + IS_HP_BELOW_85_PCT = 517, // IS_HP_BELOW_90_PCT = 518, // This ability requires you to be at or below 90% of your maximum HP. | caster restriction | - IS_HP_BELOW_95_PCT = 519, // + IS_HP_BELOW_95_PCT = 519, // IS_MANA_BELOW_UNKNOWN_PCT = 521, // - IS_ENDURANCE_BELOW_40_PCT = 522, // - IS_MANA_BELOW_40_PCT = 523, // - IS_HP_ABOVE_20_PCT = 524, // Your target's HP must be at least 21% of its maximum. + IS_ENDURANCE_BELOW_40_PCT = 522, // + IS_MANA_BELOW_40_PCT = 523, // + IS_HP_ABOVE_20_PCT = 524, // Your target's HP must be at least 21% of its maximum. IS_BODY_TYPE_UNDEFINED = 600, // This spell will only work on creatures with an undefined body type. - IS_BODY_TYPE_HUMANOID = 601, // This spell will only work on humanoid creatures. - IS_BODY_TYPE_WEREWOLF = 602, // This spell will only work on lycanthrope creatures. - IS_BODY_TYPE_UNDEAD = 603, // This spell will only work on undead creatures. - IS_BODY_TYPE_GIANTS = 604, // This spell will only work on giants. - IS_BODY_TYPE_CONSTRUCTS = 605, // This spell will only work on constructs. - IS_BODY_TYPE_EXTRAPLANAR = 606, // This spell will only work on extraplanar creatures. - IS_BODY_TYPE_MAGICAL_CREATURE = 607, // This spell will only work on creatures constructed from magic. - IS_BODY_TYPE_UNDEADPET = 608, // This spell will only work on animated undead servants. - IS_BODY_TYPE_KAELGIANT = 609, // This spell will only work on the Giants of Kael Drakkal. - IS_BODY_TYPE_COLDAIN = 610, // This spell will only work on Coldain Dwarves. - IS_BODY_TYPE_VAMPIRE = 612, // This spell will only work on vampires. - IS_BODY_TYPE_ATEN_HA_RA = 613, // This spell will only work on Aten Ha Ra. - IS_BODY_TYPE_GREATER_AHKEVANS = 614, // This spell will only work on Greater Ahkevans. - IS_BODY_TYPE_KHATI_SHA = 615, // This spell will only work on Khati Sha. - IS_BODY_TYPE_LORD_INQUISITOR_SERU = 616, // This spell will only work on Lord Inquisitor Seru. - IS_BODY_TYPE_GRIEG_VENEFICUS = 617, // This spell will only work on Grieg Veneficus. + IS_BODY_TYPE_HUMANOID = 601, // This spell will only work on humanoid creatures. + IS_BODY_TYPE_WEREWOLF = 602, // This spell will only work on lycanthrope creatures. + IS_BODY_TYPE_UNDEAD = 603, // This spell will only work on undead creatures. + IS_BODY_TYPE_GIANTS = 604, // This spell will only work on giants. + IS_BODY_TYPE_CONSTRUCTS = 605, // This spell will only work on constructs. + IS_BODY_TYPE_EXTRAPLANAR = 606, // This spell will only work on extraplanar creatures. + IS_BODY_TYPE_MAGICAL_CREATURE = 607, // This spell will only work on creatures constructed from magic. + IS_BODY_TYPE_UNDEADPET = 608, // This spell will only work on animated undead servants. + IS_BODY_TYPE_KAELGIANT = 609, // This spell will only work on the Giants of Kael Drakkal. + IS_BODY_TYPE_COLDAIN = 610, // This spell will only work on Coldain Dwarves. + IS_BODY_TYPE_VAMPIRE = 612, // This spell will only work on vampires. + IS_BODY_TYPE_ATEN_HA_RA = 613, // This spell will only work on Aten Ha Ra. + IS_BODY_TYPE_GREATER_AHKEVANS = 614, // This spell will only work on Greater Ahkevans. + IS_BODY_TYPE_KHATI_SHA = 615, // This spell will only work on Khati Sha. + IS_BODY_TYPE_LORD_INQUISITOR_SERU = 616, // This spell will only work on Lord Inquisitor Seru. + IS_BODY_TYPE_GRIEG_VENEFICUS = 617, // This spell will only work on Grieg Veneficus. IS_BODY_TYPE_FROM_PLANE_OF_WAR = 619, // This spell will only work on creatures from the Plane of War. - IS_BODY_TYPE_LUGGALD = 620, // This spell will only work on Luggalds. - IS_BODY_TYPE_ANIMAL = 621, // This spell will only work on animals. - IS_BODY_TYPE_INSECT = 622, // This spell will only work on insects. - IS_BODY_TYPE_MONSTER = 623, // This spell will only work on monsters. - IS_BODY_TYPE_ELEMENTAL = 624, // This spell will only work on elemental creatures. - IS_BODY_TYPE_PLANT = 625, // This spell will only work on plants. - IS_BODY_TYPE_DRAGON2 = 626, // This spell will only work on dragons. - IS_BODY_TYPE_SUMMONED_ELEMENTAL = 627, // This spell will only work on summoned elementals. - IS_BODY_TYPE_WARDER = 628, // - IS_BODY_TYPE_DRAGON_OF_TOV = 630, // This spell will only work on Dragons of Veeshan's Temple. - IS_BODY_TYPE_FAMILIAR = 631, // This spell will only work on familiars. - IS_BODY_TYPE_MURAMITE = 634, // This spell will only work on Muramites. - IS_NOT_UNDEAD_OR_SUMMONED = 635, // - IS_NOT_PLANT = 636, // This spell will not affect plants. - IS_NOT_CLIENT = 700, // This spell will not work on adventurers. - IS_CLIENT = 701, // This spell will only work on adventurers. + IS_BODY_TYPE_LUGGALD = 620, // This spell will only work on Luggalds. + IS_BODY_TYPE_ANIMAL = 621, // This spell will only work on animals. + IS_BODY_TYPE_INSECT = 622, // This spell will only work on insects. + IS_BODY_TYPE_MONSTER = 623, // This spell will only work on monsters. + IS_BODY_TYPE_ELEMENTAL = 624, // This spell will only work on elemental creatures. + IS_BODY_TYPE_PLANT = 625, // This spell will only work on plants. + IS_BODY_TYPE_DRAGON2 = 626, // This spell will only work on dragons. + IS_BODY_TYPE_SUMMONED_ELEMENTAL = 627, // This spell will only work on summoned elementals. + IS_BODY_TYPE_WARDER = 628, // + IS_BODY_TYPE_DRAGON_OF_TOV = 630, // This spell will only work on Dragons of Veeshan's Temple. + IS_BODY_TYPE_FAMILIAR = 631, // This spell will only work on familiars. + IS_BODY_TYPE_MURAMITE = 634, // This spell will only work on Muramites. + IS_NOT_UNDEAD_OR_SUMMONED = 635, // + IS_NOT_PLANT = 636, // This spell will not affect plants. + IS_NOT_CLIENT = 700, // This spell will not work on adventurers. + IS_CLIENT = 701, // This spell will only work on adventurers. IS_LEVEL_ABOVE_42_AND_IS_CLIENT = 800, // This spell will only work on level 43 or higher adventurers. UNKNOWN_812 = 812, // | seen in spell 22616 Thaumatize Pet Mana Regen Base | UNKNOWN_814 = 814, // | seen in spell 22704 Vegetentacles I | - IS_TREANT = 815, // This spell will only work on treants. - IS_BIXIE2 = 816, // This spell will only work on bixies. - IS_SCARECROW = 817, // This spell will only work on scarecrows. - IS_VAMPIRE_OR_UNDEAD_OR_UNDEADPET = 818, // This spell will only work on vampires, undead, or animated undead creatures. - IS_NOT_VAMPIRE_OR_UNDEAD = 819, // This spell will not work on vampires or undead creatures. - IS_CLASS_KNIGHT_HYBRID_MELEE = 820, // This spell will only work on knights, hybrids, or melee classes. - IS_CLASS_WARRIOR_CASTER_PRIEST = 821, // This spell will only work on warriors, casters, or priests. + IS_TREANT = 815, // This spell will only work on treants. + IS_BIXIE2 = 816, // This spell will only work on bixies. + IS_SCARECROW = 817, // This spell will only work on scarecrows. + IS_VAMPIRE_OR_UNDEAD_OR_UNDEADPET = 818, // This spell will only work on vampires, undead, or animated undead creatures. + IS_NOT_VAMPIRE_OR_UNDEAD = 819, // This spell will not work on vampires or undead creatures. + IS_CLASS_KNIGHT_HYBRID_MELEE = 820, // This spell will only work on knights, hybrids, or melee classes. + IS_CLASS_WARRIOR_CASTER_PRIEST = 821, // This spell will only work on warriors, casters, or priests. UNKNOWN_822 = 822, // | seen in spell 22870 Morell's Distraction 822 | - IS_END_BELOW_21_PCT = 825, // This ability requires you to be at or below 21% of your maximum endurance. - IS_END_BELOW_25_PCT = 826, // This ability requires you to be at or below 25% of your maximum endurance. - IS_END_BELOW_29_PCT = 827, // This ability requires you to be at or below 29% of your maximum endurance. - IS_REGULAR_SERVER = 836, // - IS_PROGRESSION_SERVER = 837, // + IS_END_BELOW_21_PCT = 825, // This ability requires you to be at or below 21% of your maximum endurance. + IS_END_BELOW_25_PCT = 826, // This ability requires you to be at or below 25% of your maximum endurance. + IS_END_BELOW_29_PCT = 827, // This ability requires you to be at or below 29% of your maximum endurance. + IS_REGULAR_SERVER = 836, // + IS_PROGRESSION_SERVER = 837, // IS_GOD_EXPANSION_UNLOCKED = 839, // UNKNOWN_840 = 840, // | caster restriction | seen in spell 6883 Expedient Recovery UNKNOWN_841 = 841, // | caster restriction | seen in spell 32192 Merciless Blow - IS_HUMANOID_LEVEL_84_MAX = 842, // - IS_HUMANOID_LEVEL_86_MAX = 843, // - IS_HUMANOID_LEVEL_88_MAX = 844, // + IS_HUMANOID_LEVEL_84_MAX = 842, // + IS_HUMANOID_LEVEL_86_MAX = 843, // + IS_HUMANOID_LEVEL_88_MAX = 844, // HAS_CRYSTALLIZED_FLAME_BUFF = 845, // This spell will only work on targets afflicted by Crystallized Flame. | On live spell does not appear to be a buff - HAS_INCENDIARY_OOZE_BUFF = 847, // This spell will only work on targets afflicted by Incendiary Ooze. - IS_LEVEL_90_MAX = 860, // - IS_LEVEL_92_MAX = 861, // - IS_LEVEL_94_MAX = 862, // - IS_LEVEL_95_MAX = 863, // - IS_LEVEL_97_MAX = 864, // - IS_LEVEL_99_MAX = 865, // + HAS_INCENDIARY_OOZE_BUFF = 847, // This spell will only work on targets afflicted by Incendiary Ooze. + IS_LEVEL_90_MAX = 860, // + IS_LEVEL_92_MAX = 861, // + IS_LEVEL_94_MAX = 862, // + IS_LEVEL_95_MAX = 863, // + IS_LEVEL_97_MAX = 864, // + IS_LEVEL_99_MAX = 865, // HAS_WEAPONSTANCE_DEFENSIVE_PROFICIENCY = 866, // | caster restriction | - HAS_WEAPONSTANCE_TWO_HAND_PROFICIENCY = 867, // | caster restriction | + HAS_WEAPONSTANCE_TWO_HAND_PROFICIENCY = 867, // | caster restriction | HAS_WEAPONSTANCE_DUAL_WEILD_PROFICIENCY = 868, // | caster restriction | - IS_LEVEL_100_MAX = 869, // - IS_LEVEL_102_MAX = 870, // - IS_LEVEL_104_MAX = 871, // - IS_LEVEL_105_MAX = 872, // - IS_LEVEL_107_MAX = 873, // - IS_LEVEL_109_MAX = 874, // - IS_LEVEL_110_MAX = 875, // - IS_LEVEL_112_MAX = 876, // - IS_LEVEL_114_MAX = 877, // + IS_LEVEL_100_MAX = 869, // + IS_LEVEL_102_MAX = 870, // + IS_LEVEL_104_MAX = 871, // + IS_LEVEL_105_MAX = 872, // + IS_LEVEL_107_MAX = 873, // + IS_LEVEL_109_MAX = 874, // + IS_LEVEL_110_MAX = 875, // + IS_LEVEL_112_MAX = 876, // + IS_LEVEL_114_MAX = 877, // HAS_TBL_ESIANTI_ACCESS = 997, // This spell will only transport adventurers who have gained access to Esianti: Palace of the Winds. | not implemented - HAS_ITEM_CLOCKWORK_SCRAPS = 999, // - IS_BETWEEN_LEVEL_1_AND_75 = 1000, // - IS_BETWEEN_LEVEL_76_AND_85 = 1001, // - IS_BETWEEN_LEVEL_86_AND_95 = 1002, // - IS_BETWEEN_LEVEL_96_AND_105 = 1003, // - IS_HP_LESS_THAN_80_PCT = 1004, // - IS_LEVEL_ABOVE_34 = 1474, // Your target must be level 35 or higher. - IN_TWO_HANDED_STANCE = 2000, // You must be in your two-handed stance to use this ability. - IN_DUAL_WIELD_HANDED_STANCE = 2001, // You must be in your dual-wielding stance to use this ability. - IN_SHIELD_STANCE = 2002, // You must be in your shield stance to use this ability. - NOT_IN_TWO_HANDED_STANCE = 2010, // You may not use this ability if you are in your two-handed stance. - NOT_IN_DUAL_WIELD_HANDED_STANCE = 2011, // You may not use this ability if you are in your dual-wielding stance. - NOT_IN_SHIELD_STANCE = 2012, // You may not use this ability if you are in your shield stance. - LEVEL_46_MAX = 2761, // - DISABLED_UNTIL_EXPANSION_ROK = 7000, // This ability is disabled until Ruins of Kunark. - DISABLED_UNTIL_EXPANSION_SOV = 7001, // This ability is disabled until Scars of Velious. - DISABLED_UNTIL_EXPANSION_SOL = 7002, // This ability is disabled until Shadows of Luclin. - DISABLED_UNTIL_EXPANSION_POP = 7003, // This ability is disabled until Planes of Power. - DISABLED_UNTIL_EXPANSION_LOY = 7004, // This ability is disabled until Legacy of Ykesha. - DISABLED_UNTIL_EXPANSION_LDON = 7005, // This ability is disabled until Lost Dungeons of Norrath. - DISABLED_UNTIL_EXPANSION_GOD = 7006, // This ability is disabled until Gates of Discord. - DISABLED_UNTIL_EXPANSION_OOW = 7007, // This ability is disabled until Omens of War. - DISABLED_UNTIL_EXPANSION_DON = 7008, // This ability is disabled until Dragons of Norrath. - DISABLED_UNTIL_EXPANSION_DOD = 7009, // This ability is disabled until Depths of Darkhollow. - DISABLED_UNTIL_EXPANSION_POR = 7010, // This ability is disabled until Prophecy of Ro. - DISABLED_UNTIL_EXPANSION_TSS = 7011, // This ability is disabled until Serpent's Spine. - DISABLED_UNTIL_EXPANSION_TBS = 7012, // This ability is disabled until Buried Sea. - DISABLED_UNTIL_EXPANSION_SOF = 7013, // This ability is disabled until Secrets of Faydwer. - DISABLED_UNTIL_EXPANSION_SOD = 7014, // This ability is disabled until Seeds of Destruction. - DISABLED_UNTIL_EXPANSION_UF = 7015, // This ability is disabled until Underfoot. - DISABLED_UNTIL_EXPANSION_HOT = 7016, // This ability is disabled until House of Thule. - DISABLED_UNTIL_EXPANSION_VOA = 7017, // This ability is disabled until Veil of Alaris. - DISABLED_UNTIL_EXPANSION_ROF = 7018, // This ability is disabled until Rain of Fear. - DISABLED_UNTIL_EXPANSION_COF = 7019, // This ability is disabled until Call of the Forsaken. - DISABLED_UNTIL_EXPANSION_TDS = 7020, // This ability is disabled until Darkened Sea. - DISABLED_UNTIL_EXPANSION_TBM = 7021, // This ability is disabled until Broken Mirror. - DISABLED_UNTIL_EXPANSION_EOK = 7022, // This ability is disabled until Empires of Kunark. - DISABLED_UNTIL_EXPANSION_ROS = 7023, // This ability is disabled until Ring of Scale. - DISABLED_UNTIL_EXPANSION_TBL = 7024, // This ability is disabled until The Burning Lands. - DISABLED_UNTIL_EXPANSION_TOV = 7025, // This ability is disabled until Torment of Velious. - DISABLED_UNTIL_EXPANSION_COV = 7026, // This ability is disabled until Claws of Veeshan. - HAS_NO_MANA_BURN_BUFF = 8450, // This spell will not take hold until the effects of the previous Mana Burn have expired. + HAS_ITEM_CLOCKWORK_SCRAPS = 999, // + IS_BETWEEN_LEVEL_1_AND_75 = 1000, // + IS_BETWEEN_LEVEL_76_AND_85 = 1001, // + IS_BETWEEN_LEVEL_86_AND_95 = 1002, // + IS_BETWEEN_LEVEL_96_AND_105 = 1003, // + IS_HP_LESS_THAN_80_PCT = 1004, // + IS_LEVEL_ABOVE_34 = 1474, // Your target must be level 35 or higher. + IN_TWO_HANDED_STANCE = 2000, // You must be in your two-handed stance to use this ability. + IN_DUAL_WIELD_HANDED_STANCE = 2001, // You must be in your dual-wielding stance to use this ability. + IN_SHIELD_STANCE = 2002, // You must be in your shield stance to use this ability. + NOT_IN_TWO_HANDED_STANCE = 2010, // You may not use this ability if you are in your two-handed stance. + NOT_IN_DUAL_WIELD_HANDED_STANCE = 2011, // You may not use this ability if you are in your dual-wielding stance. + NOT_IN_SHIELD_STANCE = 2012, // You may not use this ability if you are in your shield stance. + LEVEL_46_MAX = 2761, // + DISABLED_UNTIL_EXPANSION_ROK = 7000, // This ability is disabled until Ruins of Kunark. + DISABLED_UNTIL_EXPANSION_SOV = 7001, // This ability is disabled until Scars of Velious. + DISABLED_UNTIL_EXPANSION_SOL = 7002, // This ability is disabled until Shadows of Luclin. + DISABLED_UNTIL_EXPANSION_POP = 7003, // This ability is disabled until Planes of Power. + DISABLED_UNTIL_EXPANSION_LOY = 7004, // This ability is disabled until Legacy of Ykesha. + DISABLED_UNTIL_EXPANSION_LDON = 7005, // This ability is disabled until Lost Dungeons of Norrath. + DISABLED_UNTIL_EXPANSION_GOD = 7006, // This ability is disabled until Gates of Discord. + DISABLED_UNTIL_EXPANSION_OOW = 7007, // This ability is disabled until Omens of War. + DISABLED_UNTIL_EXPANSION_DON = 7008, // This ability is disabled until Dragons of Norrath. + DISABLED_UNTIL_EXPANSION_DOD = 7009, // This ability is disabled until Depths of Darkhollow. + DISABLED_UNTIL_EXPANSION_POR = 7010, // This ability is disabled until Prophecy of Ro. + DISABLED_UNTIL_EXPANSION_TSS = 7011, // This ability is disabled until Serpent's Spine. + DISABLED_UNTIL_EXPANSION_TBS = 7012, // This ability is disabled until Buried Sea. + DISABLED_UNTIL_EXPANSION_SOF = 7013, // This ability is disabled until Secrets of Faydwer. + DISABLED_UNTIL_EXPANSION_SOD = 7014, // This ability is disabled until Seeds of Destruction. + DISABLED_UNTIL_EXPANSION_UF = 7015, // This ability is disabled until Underfoot. + DISABLED_UNTIL_EXPANSION_HOT = 7016, // This ability is disabled until House of Thule. + DISABLED_UNTIL_EXPANSION_VOA = 7017, // This ability is disabled until Veil of Alaris. + DISABLED_UNTIL_EXPANSION_ROF = 7018, // This ability is disabled until Rain of Fear. + DISABLED_UNTIL_EXPANSION_COF = 7019, // This ability is disabled until Call of the Forsaken. + DISABLED_UNTIL_EXPANSION_TDS = 7020, // This ability is disabled until Darkened Sea. + DISABLED_UNTIL_EXPANSION_TBM = 7021, // This ability is disabled until Broken Mirror. + DISABLED_UNTIL_EXPANSION_EOK = 7022, // This ability is disabled until Empires of Kunark. + DISABLED_UNTIL_EXPANSION_ROS = 7023, // This ability is disabled until Ring of Scale. + DISABLED_UNTIL_EXPANSION_TBL = 7024, // This ability is disabled until The Burning Lands. + DISABLED_UNTIL_EXPANSION_TOV = 7025, // This ability is disabled until Torment of Velious. + DISABLED_UNTIL_EXPANSION_COV = 7026, // This ability is disabled until Claws of Veeshan. + HAS_NO_MANA_BURN_BUFF = 8450, // This spell will not take hold until the effects of the previous Mana Burn have expired. IS_RACE_FIRST_CUSTOM = 10000, // | custom range to restrict targets or casters by race *not on live* | IS_RACE_LAST_CUSTOM = 11000, // | custom range to restrict targets or casters by race *not on live* | - IS_CLIENT_AND_MALE_PLATE_USER = 11044, // Your target wouldn't look right as that Jann. - IS_CLEINT_AND_MALE_DRUID_ENCHANTER_MAGICIAN_NECROANCER_SHAMAN_OR_WIZARD = 11090, // Your target wouldn't look right as that Jann. - IS_CLIENT_AND_MALE_BEASTLORD_BERSERKER_MONK_RANGER_OR_ROGUE = 11209, // Your target wouldn't look right as that Jann. - IS_CLIENT_AND_FEMALE_PLATE_USER = 11210, // Your target wouldn't look right as that Jann. - IS_CLIENT_AND_FEMALE_DRUID_ENCHANTER_MAGICIAN_NECROANCER_SHAMAN_OR_WIZARD = 11211, // Your target wouldn't look right as that Jann. - IS_CLIENT_AND_FEMALE_BEASTLORD_BERSERKER_MONK_RANGER_OR_ROGUE = 11248, // Your target wouldn't look right as that Jann. - HAS_TRAVELED_TO_STRATOS = 11260, // You must travel to Stratos at least once before wishing to go there. - HAS_TRAVELED_TO_AALISHAI = 11261, // You must travel to Aalishai at least once before wishing to go there. - HAS_TRAVELED_TO_MEARATS = 11268, // You must travel to Mearatas at least once before wishing to go there. - HAS_NO_ILLUSIONS_OF_GRANDEUR_BUFF = 12519, // - IS_HP_ABOVE_50_PCT = 16010, // - IS_HP_UNDER_50_PCT = 16031, // - IS_OFF_HAND_EQUIPED = 27672, // You must be wielding a weapon or shield in your offhand to use this ability. + IS_CLIENT_AND_MALE_PLATE_USER = 11044, // Your target wouldn't look right as that Jann. + IS_CLEINT_AND_MALE_DRUID_ENCHANTER_MAGICIAN_NECROANCER_SHAMAN_OR_WIZARD = 11090, // Your target wouldn't look right as that Jann. + IS_CLIENT_AND_MALE_BEASTLORD_BERSERKER_MONK_RANGER_OR_ROGUE = 11209, // Your target wouldn't look right as that Jann. + IS_CLIENT_AND_FEMALE_PLATE_USER = 11210, // Your target wouldn't look right as that Jann. + IS_CLIENT_AND_FEMALE_DRUID_ENCHANTER_MAGICIAN_NECROANCER_SHAMAN_OR_WIZARD = 11211, // Your target wouldn't look right as that Jann. + IS_CLIENT_AND_FEMALE_BEASTLORD_BERSERKER_MONK_RANGER_OR_ROGUE = 11248, // Your target wouldn't look right as that Jann. + HAS_TRAVELED_TO_STRATOS = 11260, // You must travel to Stratos at least once before wishing to go there. + HAS_TRAVELED_TO_AALISHAI = 11261, // You must travel to Aalishai at least once before wishing to go there. + HAS_TRAVELED_TO_MEARATS = 11268, // You must travel to Mearatas at least once before wishing to go there. + HAS_NO_ILLUSIONS_OF_GRANDEUR_BUFF = 12519, // + IS_HP_ABOVE_50_PCT = 16010, // + IS_HP_UNDER_50_PCT = 16031, // + IS_OFF_HAND_EQUIPED = 27672, // You must be wielding a weapon or shield in your offhand to use this ability. HAS_NO_PACT_OF_FATE_RECOURSE_BUFF = 29556, // This spell will not work while Pact of Fate Recourse is active. | caster restriction | - HAS_NO_SHROUD_OF_PRAYER_BUFF = 32339, // Your target cannot receive another Quiet Prayer this soon. - IS_MANA_BELOW_20_PCT = 38311, // This ability requires you to be at or below 20% of your maximum mana. - IS_MANA_ABOVE_50_PCT = 38312, // This ability requires you to be at or above 50% of your maximum mana. - COMPLETED_ACHIEVEMENT_LEGENDARY_ANSWERER = 39281, // You have completed Legendary Answerer. + HAS_NO_SHROUD_OF_PRAYER_BUFF = 32339, // Your target cannot receive another Quiet Prayer this soon. + IS_MANA_BELOW_20_PCT = 38311, // This ability requires you to be at or below 20% of your maximum mana. + IS_MANA_ABOVE_50_PCT = 38312, // This ability requires you to be at or above 50% of your maximum mana. + COMPLETED_ACHIEVEMENT_LEGENDARY_ANSWERER = 39281, // You have completed Legendary Answerer. HAS_NO_ROGUES_FURY_BUFF = 40297, // This spell will not affect anyone that currently has Rogue's Fury active. | caster restriction | - NOT_COMPLETED_ACHIEVEMENT_LEGENDARY_ANSWERER = 42280, // You must complete Legendary Answerer. - IS_SUMMONED_OR_UNDEAD = 49326, // - IS_CLASS_CASTER_PRIEST = 49529, // + NOT_COMPLETED_ACHIEVEMENT_LEGENDARY_ANSWERER = 42280, // You must complete Legendary Answerer. + IS_SUMMONED_OR_UNDEAD = 49326, // + IS_CLASS_CASTER_PRIEST = 49529, // IS_END_OR_MANA_ABOVE_20_PCT = 49543, // You must have at least 20% of your maximum mana and endurance to use this ability. //pure melee class check end, other check mana IS_END_OR_MANA_BELOW_30_PCT = 49573, // Your target already has 30% or more of their maximum mana or endurance. //pure melee class check the, other check more - IS_CLASS_BARD2 = 49574, // - IS_NOT_CLASS_BARD = 49575, // - HAS_NO_FURIOUS_RAMPAGE_BUFF = 49612, // This ability cannot be activated while Furious Rampage is active. - IS_END_OR_MANA_BELOW_30_PCT2 = 49809, // You can only perform this solo if you have less than 30% mana or endurance. - HAS_NO_HARMONIOUS_PRECISION_BUFF = 50003, // This spell will not work if you have the Harmonious Precision line active. + IS_CLASS_BARD2 = 49574, // + IS_NOT_CLASS_BARD = 49575, // + HAS_NO_FURIOUS_RAMPAGE_BUFF = 49612, // This ability cannot be activated while Furious Rampage is active. + IS_END_OR_MANA_BELOW_30_PCT2 = 49809, // You can only perform this solo if you have less than 30% mana or endurance. + HAS_NO_HARMONIOUS_PRECISION_BUFF = 50003, // This spell will not work if you have the Harmonious Precision line active. HAS_NO_HARMONIOUS_EXPANSE_BUFF = 50009, // This spell will not work if you have the Harmonious Expanse line active. UNKNOWN_99999 = 99999, // | caster restriction | works will spell 27672 Strike of Ire }; @@ -912,7 +912,7 @@ typedef enum { #define SE_FleshToBone 207 // implemented //#define SE_PurgePoison 208 // not used #define SE_DispelBeneficial 209 // implemented, @Dispel, removes only beneficial effects on a target, base: pct chance (950=95%), limit: none, max: none -#define SE_PetShield 210 // implmented, @ShieldAbility, allows pet to 'shield' owner for 50 pct of damage taken for a duration, base: Time multiplier 1=12 seconds, 2=24 ect, limit: mitigation on pet owner override (not on live), max: mitigation on pet overide (not on live) +#define SE_PetShield 210 // implmented, @ShieldAbility, allows pet to 'shield' owner for 50 pct of damage taken for a duration, base: Time multiplier 1=12 seconds, 2=24 ect, limit: mitigation on pet owner override (not on live), max: mitigation on pet overide (not on live) #define SE_AEMelee 211 // implemented TO DO: Implement to allow NPC use (client only atm). #define SE_FrenziedDevastation 212 // implemented - increase spell criticals + all DD spells cast 2x mana. #define SE_PetMaxHP 213 // implemented[AA] - increases the maximum hit points of your pet @@ -993,7 +993,7 @@ typedef enum { #define SE_SkillAttackProc 288 // implemented[AA] - Chance to proc spell on skill attack usage (ex. Dragon Punch) #define SE_CastOnFadeEffect 289 // implemented - Triggers only if fades after natural duration. #define SE_IncreaseRunSpeedCap 290 // implemented[AA] - increases run speed over the hard cap -#define SE_Purify 291 // implemented, @Dispel, remove up specified amount of detiremental spells, base: amt removed, limit: none, max: none, Note: excluding charm, fear, resurrection, and revival sickness +#define SE_Purify 291 // implemented, @Dispel, remove up specified amount of detiremental spells, base: amt removed, limit: none, max: none, Note: excluding charm, fear, resurrection, and revival sickness #define SE_StrikeThrough2 292 // implemented[AA] - increasing chance of bypassing an opponent's special defenses, such as dodge, block, parry, and riposte. #define SE_FrontalStunResist 293 // implemented[AA] - Reduce chance to be stunned from front. -- live descriptions sounds like this isn't limited to frontal anymore #define SE_CriticalSpellChance 294 // implemented - increase chance to critical hit and critical damage modifier. @@ -1041,7 +1041,7 @@ typedef enum { //#define SE_IllusionaryTarget 336 // not used #define SE_PercentXPIncrease 337 // implemented #define SE_SummonAndResAllCorpses 338 // implemented -#define SE_TriggerOnCast 339 // implemented, @Fc, On Caster, cast on spell use, base: chance pct limit: spellid +#define SE_TriggerOnCast 339 // implemented, @Fc, On Caster, cast on spell use, base: chance pct limit: spellid #define SE_SpellTrigger 340 // implemented - chance to trigger spell [Share rolls with 469] All base2 spells share roll chance, only 1 cast. #define SE_ItemAttackCapIncrease 341 // implemented[AA] - increases the maximum amount of attack you can gain from items. #define SE_ImmuneFleeing 342 // implemented - stop mob from fleeing @@ -1186,14 +1186,14 @@ typedef enum { #define SE_Fc_Cast_Spell_On_Land 481 // implemented, @Fc, On Target, cast spell if hit by spell, base: chance pct, limit: spellid #define SE_Skill_Base_Damage_Mod 482 // implemented, @OffBonus, modify base melee damage by percent, base: pct, limit: skill(-1=ALL), max: none #define SE_Fc_Spell_Damage_Pct_IncomingPC 483 // implemented, @Fc, On Target, spell damage taken mod pct, base: min pct, limit: max pct -#define SE_Fc_Spell_Damage_Amt_IncomingPC 484 // implemented, @Fc, On Target, damage taken flat amt, base: amt +#define SE_Fc_Spell_Damage_Amt_IncomingPC 484 // implemented, @Fc, On Target, damage taken flat amt, base: amt #define SE_Ff_CasterClass 485 // implemented, @Ff, Caster of spell on target with a focus effect that is checked by incoming spells must be specified class(es). base1: class(es), Note: Set multiple classes same as would for items #define SE_Ff_Same_Caster 486 // implemented, @Ff, Caster of spell on target with a focus effect that is checked by incoming spells, base1: 0=Must be different caster 1=Must be same caster //#define SE_Extend_Tradeskill_Cap 487 // //#define SE_Defender_Melee_Force_Pct_PC 488 // #define SE_Worn_Endurance_Regen_Cap 489 // implemented, modify worn regen cap, base: amt, limit: none, max: none #define SE_Ff_ReuseTimeMin 490 // implemented, @Ff, Minimum recast time of a spell that can be focused, base: recast time -#define SE_Ff_ReuseTimeMax 491 // implemented, @Ff, Max recast time of a spell that can be focused, base: recast time +#define SE_Ff_ReuseTimeMax 491 // implemented, @Ff, Max recast time of a spell that can be focused, base: recast time #define SE_Ff_Endurance_Min 492 // implemented, @Ff, Minimum endurance cost of a spell that can be focused, base: endurance cost #define SE_Ff_Endurance_Max 493 // implemented, @Ff, Max endurance cost of a spell that can be focused, base: endurance cost #define SE_Pet_Add_Atk 494 // implemented - Bonus on pet owner which gives their pet increased attack stat @@ -1213,7 +1213,7 @@ typedef enum { #define SE_Fc_Amplify_Amt 508 // implemented, @Fc, On Caster, damage-heal-dot mod flat amt, base: amt #define SE_Health_Transfer 509 // implemented - exchange health for damage or healing on a target. ie Lifeburn/Act of Valor #define SE_Fc_ResistIncoming 510 // implemented, @Fc, On Target, resist modifier, base: amt -#define SE_Ff_FocusTimerMin 511 // implemented, @Ff, sets a recast time until focus can be used again, base: 1, limit: time ms, Note: ie. limit to 1 trigger every 1.5 seconds +#define SE_Ff_FocusTimerMin 511 // implemented, @Ff, sets a recast time until focus can be used again, base: 1, limit: time ms, Note: ie. limit to 1 trigger every 1.5 seconds #define SE_Proc_Timer_Modifier 512 // implemented - limits procs per amount of a time based on timer value, base: 1, limit: time ms, Note:, ie limit to 1 proc every 55 seconds) //#define SE_Mana_Max_Percent 513 // //#define SE_Endurance_Max_Percent 514 // @@ -1531,5 +1531,6 @@ bool IsShortDurationBuff(uint16 spell_id); bool IsSpellUsableThisZoneType(uint16 spell_id, uint8 zone_type); const char *GetSpellName(uint16 spell_id); int GetSpellStatValue(uint32 spell_id, const char* stat_identifier, uint8 slot = 0); +bool CastRestrictedSpell(int spellid); #endif diff --git a/utils/gm_commands/main.go b/utils/gm_commands/main.go new file mode 100644 index 000000000..4ef678d97 --- /dev/null +++ b/utils/gm_commands/main.go @@ -0,0 +1,183 @@ +package main + +import ( + "fmt" + "io/ioutil" + "log" + "os" + "sort" + "strings" +) + +func main() { + // zone/command.cpp + commands, err := os.ReadFile("./zone/command.cpp") + if err != nil { + log.Fatal(err) + } + commandsString := string(commands) + + s := strings.Split(commandsString, "void command_") + commandFiles := []string{} + if len(s) > 1 { + startListing := false + for i, chunk := range s { + if strings.Contains(chunk, "logcommand(Client *c") { + startListing = true + } + + // get function name + functionName := "" + nameSplit := strings.Split(chunk, "(Client") + if len(nameSplit) > 0 { + functionName = strings.TrimSpace(nameSplit[0]) + } + + if startListing && + len(s[i-1]) > 0 && + !strings.Contains(s[i-1], "#ifdef") && + !strings.Contains(chunk, "#ifdef") && + !strings.Contains(chunk, "#ifdef BOTS") && + !strings.Contains(chunk, "#ifdef EQPROFILE") && + !strings.Contains(functionName, "bot") && + !strings.Contains(functionName, "help") && + !strings.Contains(functionName, "findaliases") { + + fmt.Println(functionName) + + // build command file name + commandFile := fmt.Sprintf("zone/gm_commands/%v.cpp", functionName) + + // append command file nam eto list + commandFiles = append(commandFiles, commandFile) + + includes := "" + if strings.Contains(chunk, "Client") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../client.h\"") + } + if strings.Contains(chunk, "parse->") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../quest_parser_collection.h\"") + } + if strings.Contains(chunk, "worldserver.") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../worldserver.h\"") + includes = fmt.Sprintf("%v%v\n", includes, "extern WorldServer worldserver;") + } + if strings.Contains(chunk, "RegionType") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../water_map.h\"") + } + if strings.Contains(chunk, "Corpse") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../corpse.h\"") + } + if strings.Contains(chunk, "Object") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../object.h\"") + } + if strings.Contains(chunk, "DoorManipulation") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"door_manipulation.h\"") + } + if strings.Contains(chunk, "Group") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../groups.h\"") + } + if strings.Contains(chunk, "httplib") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../../common/http/httplib.h\"") + } + if strings.Contains(chunk, "guild_mgr") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../guild_mgr.h\"") + } + if strings.Contains(chunk, "expedition") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../expedition.h\"") + } + if strings.Contains(chunk, "DataBucket::") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../data_bucket.h\"") + } + if strings.Contains(chunk, "file_exists") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../../common/file_util.h\"") + } + if strings.Contains(chunk, "std::thread") { + includes = fmt.Sprintf("%v%v\n", includes, "#include ") + } + if strings.Contains(chunk, "Door") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../doors.h\"") + } + if strings.Contains(chunk, "NOW_INVISIBLE") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../string_ids.h\"") + } + if strings.Contains(chunk, "Expansion::") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../../common/content/world_content_service.h\"") + } + if strings.Contains(chunk, "MobMovementManager::") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../mob_movement_manager.h\"") + } + if strings.Contains(chunk, "MobStuckBehavior::") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../mob_movement_manager.h\"") + } + if strings.Contains(chunk, "ReloadAllPatches") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../../common/patches/patches.h\"") + } + if strings.Contains(chunk, "ProfanityManager") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../../common/profanity_manager.h\"") + } + if strings.Contains(chunk, "npc_scale_manager") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../npc_scale_manager.h\"") + } + if strings.Contains(chunk, "g_Math") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../fastmath.h\"") + includes = fmt.Sprintf("%v%v\n", includes, "extern FastMath g_Math;") + } + if strings.Contains(chunk, "raid") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../raids.h\"") + } + if strings.Contains(chunk, "Raid") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../raids.h\"") + } + if strings.Contains(chunk, "GetOS") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../../common/serverinfo.h\"") + } + if strings.Contains(chunk, "LANG_") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../../common/languages.h\"") + } + if strings.Contains(chunk, "ServerOP_Shared") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../../common/shared_tasks.h\"") + } + if strings.Contains(chunk, "title_manager") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../titles.h\"") + } + if strings.Contains(chunk, "CatchSignal") { + includes = fmt.Sprintf("%v%v\n", includes, "#include \"../../world/main.h\"") + } + + // build the contents of the command file + commandString := fmt.Sprintf("%v\nvoid command_%v", includes, chunk) + + //write file contents + err := ioutil.WriteFile(commandFile, []byte(commandString), 0777) + if err != nil { + fmt.Println(err) + } + + commandOnly := fmt.Sprintf("void command_%v", chunk) + commandsString = strings.ReplaceAll(commandsString, commandOnly, "") + + } + } + + // rewrite commands.cpp with functions removed + err := ioutil.WriteFile("zone/command.cpp", []byte(commandsString), 0777) + if err != nil { + fmt.Println(err) + } + + fmt.Println("# CmakeLists") + + // sort a-z + sort.Slice(commandFiles, func(i, j int) bool { + return commandFiles[i] < commandFiles[j] + }) + + for _, file := range commandFiles { + file = strings.ReplaceAll(file, "zone/", "") + fmt.Println(file) + } + } + + //fmt.Print(string(commands)) +} diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index dd4188c25..46c235455 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -1,293 +1,576 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.2) SET(zone_sources - aa.cpp - aa_ability.cpp - aggro.cpp - aggromanager.cpp - api_service.cpp - attack.cpp - aura.cpp - beacon.cpp - bonuses.cpp - bot.cpp - bot_command.cpp - bot_database.cpp - botspellsai.cpp - cheat_manager.cpp - client.cpp - client_mods.cpp - client_packet.cpp - client_process.cpp - command.cpp - corpse.cpp - data_bucket.cpp - doors.cpp - dialogue_window.cpp - dynamic_zone.cpp - effects.cpp - embparser.cpp - embparser_api.cpp - embperl.cpp - embxs.cpp - encounter.cpp - entity.cpp - exp.cpp - expedition.cpp - expedition_database.cpp - expedition_request.cpp - fastmath.cpp - fearpath.cpp - forage.cpp - groups.cpp - guild.cpp - guild_mgr.cpp - hate_list.cpp - heal_rotation.cpp - horse.cpp - inventory.cpp - loottables.cpp - lua_bot.cpp - lua_bit.cpp - lua_corpse.cpp - lua_client.cpp - lua_door.cpp - lua_encounter.cpp - lua_entity.cpp - lua_entity_list.cpp - lua_expedition.cpp - lua_general.cpp - lua_group.cpp - lua_hate_list.cpp - lua_inventory.cpp - lua_item.cpp - lua_iteminst.cpp - lua_mob.cpp - lua_mod.cpp - lua_npc.cpp - lua_object.cpp - lua_packet.cpp - lua_parser.cpp - lua_parser_events.cpp - lua_raid.cpp - lua_spawn.cpp - lua_spell.cpp - lua_stat_bonuses.cpp - embperl.cpp - embxs.cpp - entity.cpp - exp.cpp - fearpath.cpp - forage.cpp - global_loot_manager.cpp - gm_commands/door_manipulation.cpp - groups.cpp - guild.cpp - guild_mgr.cpp - hate_list.cpp - horse.cpp - inventory.cpp - loottables.cpp - main.cpp - map.cpp - merc.cpp - mob.cpp - mob_ai.cpp - mob_appearance.cpp - mob_movement_manager.cpp - mob_info.cpp - mod_functions.cpp - npc.cpp - npc_ai.cpp - npc_scale_manager.cpp - object.cpp - oriented_bounding_box.cpp - pathfinder_interface.cpp - pathfinder_nav_mesh.cpp - pathfinder_null.cpp - pathing.cpp - perl_bot.cpp - perl_client.cpp - perl_doors.cpp - perl_entity.cpp - perl_expedition.cpp - perl_groups.cpp - perl_hateentry.cpp - perl_inventory.cpp - perl_mob.cpp - perl_npc.cpp - perl_object.cpp - perl_perlpacket.cpp - perl_player_corpse.cpp - perl_questitem.cpp - perl_raids.cpp - perl_spell.cpp - perlpacket.cpp - petitions.cpp - pets.cpp - position.cpp - qglobals.cpp - queryserv.cpp - questmgr.cpp - quest_parser_collection.cpp - raids.cpp - raycast_mesh.cpp - shared_task_zone_messaging.cpp - spawn2.cpp - spawn2.h - spawngroup.cpp - special_attacks.cpp - spell_effects.cpp - spells.cpp - task_client_state.cpp - task_goal_list_manager.cpp - task_manager.cpp - task_proximity_manager.cpp - tasks.cpp - titles.cpp - tradeskills.cpp - trading.cpp - trap.cpp - tribute.cpp - tune.cpp - water_map.cpp - water_map_v1.cpp - water_map_v2.cpp - waypoints.cpp - worldserver.cpp - xtargetautohaters.cpp - zone.cpp - zone_config.cpp - zonedb.cpp - zone_event_scheduler.cpp - zone_reload.cpp - zone_store.cpp - zoning.cpp -) + aa.cpp + aa_ability.cpp + aggro.cpp + aggromanager.cpp + api_service.cpp + attack.cpp + aura.cpp + beacon.cpp + bonuses.cpp + bot.cpp + bot_command.cpp + bot_database.cpp + botspellsai.cpp + cheat_manager.cpp + client.cpp + client_mods.cpp + client_packet.cpp + client_process.cpp + command.cpp + corpse.cpp + data_bucket.cpp + doors.cpp + dialogue_window.cpp + dynamic_zone.cpp + effects.cpp + embparser.cpp + embparser_api.cpp + embperl.cpp + embxs.cpp + encounter.cpp + entity.cpp + exp.cpp + expedition.cpp + expedition_database.cpp + expedition_request.cpp + fastmath.cpp + fearpath.cpp + forage.cpp + groups.cpp + guild.cpp + guild_mgr.cpp + hate_list.cpp + heal_rotation.cpp + horse.cpp + inventory.cpp + loottables.cpp + lua_bot.cpp + lua_bit.cpp + lua_corpse.cpp + lua_client.cpp + lua_door.cpp + lua_encounter.cpp + lua_entity.cpp + lua_entity_list.cpp + lua_expedition.cpp + lua_general.cpp + lua_group.cpp + lua_hate_list.cpp + lua_inventory.cpp + lua_item.cpp + lua_iteminst.cpp + lua_mob.cpp + lua_mod.cpp + lua_npc.cpp + lua_object.cpp + lua_packet.cpp + lua_parser.cpp + lua_parser_events.cpp + lua_raid.cpp + lua_spawn.cpp + lua_spell.cpp + lua_stat_bonuses.cpp + embperl.cpp + embxs.cpp + entity.cpp + exp.cpp + fearpath.cpp + forage.cpp + global_loot_manager.cpp + gm_commands/door_manipulation.cpp + groups.cpp + guild.cpp + guild_mgr.cpp + hate_list.cpp + horse.cpp + inventory.cpp + loottables.cpp + main.cpp + map.cpp + merc.cpp + mob.cpp + mob_ai.cpp + mob_appearance.cpp + mob_movement_manager.cpp + mob_info.cpp + mod_functions.cpp + npc.cpp + npc_ai.cpp + npc_scale_manager.cpp + object.cpp + oriented_bounding_box.cpp + pathfinder_interface.cpp + pathfinder_nav_mesh.cpp + pathfinder_null.cpp + pathing.cpp + perl_bot.cpp + perl_client.cpp + perl_doors.cpp + perl_entity.cpp + perl_expedition.cpp + perl_groups.cpp + perl_hateentry.cpp + perl_inventory.cpp + perl_mob.cpp + perl_npc.cpp + perl_object.cpp + perl_perlpacket.cpp + perl_player_corpse.cpp + perl_questitem.cpp + perl_raids.cpp + perl_spell.cpp + perlpacket.cpp + petitions.cpp + pets.cpp + position.cpp + qglobals.cpp + queryserv.cpp + questmgr.cpp + quest_parser_collection.cpp + raids.cpp + raycast_mesh.cpp + shared_task_zone_messaging.cpp + spawn2.cpp + spawn2.h + spawngroup.cpp + special_attacks.cpp + spell_effects.cpp + spells.cpp + task_client_state.cpp + task_goal_list_manager.cpp + task_manager.cpp + task_proximity_manager.cpp + tasks.cpp + titles.cpp + tradeskills.cpp + trading.cpp + trap.cpp + tribute.cpp + tune.cpp + water_map.cpp + water_map_v1.cpp + water_map_v2.cpp + waypoints.cpp + worldserver.cpp + xtargetautohaters.cpp + zone.cpp + zone_config.cpp + zonedb.cpp + zone_event_scheduler.cpp + zone_reload.cpp + zone_store.cpp + zoning.cpp + ) SET(zone_headers - aa.h - aa_ability.h - aggromanager.h - api_service.h - aura.h - basic_functions.h - beacon.h - bot.h - bot_command.h - bot_database.h - bot_structs.h - cheat_manager.h - client.h - client_packet.h - command.h - common.h - corpse.h - data_bucket.h - doors.h - dialogue_window.h - dynamic_zone.h - embparser.h - embperl.h - embxs.h - encounter.h - entity.h - errmsg.h - event_codes.h - expedition.h - expedition_database.h - expedition_request.h - fastmath.h - forage.h - global_loot_manager.h - gm_commands/door_manipulation.h - groups.h - guild_mgr.h - hate_list.h - heal_rotation.h - horse.h - lua_bot.h - lua_bit.h - lua_client.h - lua_corpse.h - lua_door.h - lua_encounter.h - lua_entity.h - lua_entity_list.h - lua_expedition.h - lua_general.h - lua_group.h - lua_hate_list.h - lua_inventory.h - lua_item.h - lua_iteminst.h - lua_mob.h - lua_mod.h - lua_npc.h - lua_object.h - lua_packet.h - lua_parser.h - lua_parser_events.h - lua_ptr.h - lua_raid.h - lua_spawn.h - lua_spell.h - lua_stat_bonuses.h - map.h - masterentity.h - maxskill.h - message.h - merc.h - mob.h - mob_movement_manager.h - npc.h - npc_ai.h - npc_scale_manager.h - object.h - oriented_bounding_box.h - pathfinder_interface.h - pathfinder_nav_mesh.h - pathfinder_null.h - perlpacket.h - petitions.h - pets.h - position.h - qglobals.h - quest_interface.h - queryserv.h - quest_interface.h - questmgr.h - quest_parser_collection.h - raids.h - raycast_mesh.h - skills.h - shared_task_zone_messaging.h - spawn2.cpp - spawn2.h - spawngroup.h - string_ids.h - task_client_state.h - task_goal_list_manager.h - task_manager.h - task_proximity_manager.h - tasks.h - titles.h - trap.h - water_map.h - water_map_v1.h - water_map_v2.h - worldserver.h - xtargetautohaters.h - zone.h - zone_event_scheduler.h - zone_config.h - zonedb.h - zonedump.h - zone_reload.h - zone_store.h + aa.h + aa_ability.h + aggromanager.h + api_service.h + aura.h + basic_functions.h + beacon.h + bot.h + bot_command.h + bot_database.h + bot_structs.h + cheat_manager.h + client.h + client_packet.h + command.h + common.h + corpse.h + data_bucket.h + doors.h + dialogue_window.h + dynamic_zone.h + embparser.h + embperl.h + embxs.h + encounter.h + entity.h + errmsg.h + event_codes.h + expedition.h + expedition_database.h + expedition_request.h + fastmath.h + forage.h + global_loot_manager.h + gm_commands/door_manipulation.h + groups.h + guild_mgr.h + hate_list.h + heal_rotation.h + horse.h + lua_bot.h + lua_bit.h + lua_client.h + lua_corpse.h + lua_door.h + lua_encounter.h + lua_entity.h + lua_entity_list.h + lua_expedition.h + lua_general.h + lua_group.h + lua_hate_list.h + lua_inventory.h + lua_item.h + lua_iteminst.h + lua_mob.h + lua_mod.h + lua_npc.h + lua_object.h + lua_packet.h + lua_parser.h + lua_parser_events.h + lua_ptr.h + lua_raid.h + lua_spawn.h + lua_spell.h + lua_stat_bonuses.h + map.h + masterentity.h + maxskill.h + message.h + merc.h + mob.h + mob_movement_manager.h + npc.h + npc_ai.h + npc_scale_manager.h + object.h + oriented_bounding_box.h + pathfinder_interface.h + pathfinder_nav_mesh.h + pathfinder_null.h + perlpacket.h + petitions.h + pets.h + position.h + qglobals.h + quest_interface.h + queryserv.h + quest_interface.h + questmgr.h + quest_parser_collection.h + raids.h + raycast_mesh.h + skills.h + shared_task_zone_messaging.h + spawn2.cpp + spawn2.h + spawngroup.h + string_ids.h + task_client_state.h + task_goal_list_manager.h + task_manager.h + task_proximity_manager.h + tasks.h + titles.h + trap.h + water_map.h + water_map_v1.h + water_map_v2.h + worldserver.h + xtargetautohaters.h + zone.h + zone_event_scheduler.h + zone_config.h + zonedb.h + zonedump.h + zone_reload.h + zone_store.h + ) + +SET(gm_commands + gm_commands/acceptrules.cpp + gm_commands/advnpcspawn.cpp + gm_commands/aggro.cpp + gm_commands/aggrozone.cpp + gm_commands/ai.cpp + gm_commands/appearance.cpp + gm_commands/attack.cpp + gm_commands/augmentitem.cpp + gm_commands/ban.cpp + gm_commands/beard.cpp + gm_commands/beardcolor.cpp + gm_commands/bestz.cpp + gm_commands/bind.cpp + gm_commands/camerashake.cpp + gm_commands/castspell.cpp + gm_commands/chat.cpp + gm_commands/checklos.cpp + gm_commands/copycharacter.cpp + gm_commands/corpse.cpp + gm_commands/corpsefix.cpp + gm_commands/cvs.cpp + gm_commands/damage.cpp + gm_commands/databuckets.cpp + gm_commands/date.cpp + gm_commands/dbspawn2.cpp + gm_commands/delacct.cpp + gm_commands/deletegraveyard.cpp + gm_commands/delpetition.cpp + gm_commands/depop.cpp + gm_commands/depopzone.cpp + gm_commands/details.cpp + gm_commands/devtools.cpp + gm_commands/disablerecipe.cpp + gm_commands/disarmtrap.cpp + gm_commands/distance.cpp + gm_commands/doanim.cpp + gm_commands/door.cpp + gm_commands/dye.cpp + gm_commands/dz.cpp + gm_commands/dzkickplayers.cpp + gm_commands/editmassrespawn.cpp + gm_commands/emote.cpp + gm_commands/emotesearch.cpp + gm_commands/emoteview.cpp + gm_commands/enablerecipe.cpp + gm_commands/endurance.cpp + gm_commands/equipitem.cpp + gm_commands/face.cpp + gm_commands/faction.cpp + gm_commands/findclass.cpp + gm_commands/findfaction.cpp + gm_commands/findnpctype.cpp + gm_commands/findrace.cpp + gm_commands/findskill.cpp + gm_commands/findspell.cpp + gm_commands/findtask.cpp + gm_commands/findzone.cpp + gm_commands/fixmob.cpp + gm_commands/flag.cpp + gm_commands/flagedit.cpp + gm_commands/flags.cpp + gm_commands/flymode.cpp + gm_commands/fov.cpp + gm_commands/freeze.cpp + gm_commands/gassign.cpp + gm_commands/gearup.cpp + gm_commands/gender.cpp + gm_commands/getplayerburiedcorpsecount.cpp + gm_commands/getvariable.cpp + gm_commands/ginfo.cpp + gm_commands/giveitem.cpp + gm_commands/givemoney.cpp + gm_commands/globalview.cpp + gm_commands/gm.cpp + gm_commands/gmspeed.cpp + gm_commands/gmzone.cpp + gm_commands/goto.cpp + gm_commands/grid.cpp + gm_commands/guild.cpp + gm_commands/guildapprove.cpp + gm_commands/guildcreate.cpp + gm_commands/guildlist.cpp + gm_commands/hair.cpp + gm_commands/haircolor.cpp + gm_commands/haste.cpp + gm_commands/hatelist.cpp + gm_commands/heal.cpp + gm_commands/helm.cpp + gm_commands/heritage.cpp + gm_commands/heromodel.cpp + gm_commands/hideme.cpp + gm_commands/hp.cpp + gm_commands/incstat.cpp + gm_commands/instance.cpp + gm_commands/interrogateinv.cpp + gm_commands/interrupt.cpp + gm_commands/invsnapshot.cpp + gm_commands/invul.cpp + gm_commands/ipban.cpp + gm_commands/iplookup.cpp + gm_commands/iteminfo.cpp + gm_commands/itemsearch.cpp + gm_commands/kick.cpp + gm_commands/kill.cpp + gm_commands/killallnpcs.cpp + gm_commands/lastname.cpp + gm_commands/list.cpp + gm_commands/listpetition.cpp + gm_commands/loc.cpp + gm_commands/lock.cpp + gm_commands/logcommand.cpp + gm_commands/logs.cpp + gm_commands/makepet.cpp + gm_commands/mana.cpp + gm_commands/max_all_skills.cpp + gm_commands/memspell.cpp + gm_commands/merchantcloseshop.cpp + gm_commands/merchantopenshop.cpp + gm_commands/modifynpcstat.cpp + gm_commands/motd.cpp + gm_commands/movechar.cpp + gm_commands/movement.cpp + gm_commands/myskills.cpp + gm_commands/mysql.cpp + gm_commands/mystats.cpp + gm_commands/name.cpp + gm_commands/netstats.cpp + gm_commands/network.cpp + gm_commands/npccast.cpp + gm_commands/npcedit.cpp + gm_commands/npceditmass.cpp + gm_commands/npcemote.cpp + gm_commands/npcloot.cpp + gm_commands/npcsay.cpp + gm_commands/npcshout.cpp + gm_commands/npcspawn.cpp + gm_commands/npcspecialattk.cpp + gm_commands/npcstats.cpp + gm_commands/npctype_cache.cpp + gm_commands/npctypespawn.cpp + gm_commands/nudge.cpp + gm_commands/nukebuffs.cpp + gm_commands/nukeitem.cpp + gm_commands/object.cpp + gm_commands/oocmute.cpp + gm_commands/opcode.cpp + gm_commands/path.cpp + gm_commands/peekinv.cpp + gm_commands/peqzone.cpp + gm_commands/permaclass.cpp + gm_commands/permagender.cpp + gm_commands/permarace.cpp + gm_commands/petitioninfo.cpp + gm_commands/petname.cpp + gm_commands/pf.cpp + gm_commands/picklock.cpp + gm_commands/profanity.cpp + gm_commands/proximity.cpp + gm_commands/push.cpp + gm_commands/pvp.cpp + gm_commands/qglobal.cpp + gm_commands/questerrors.cpp + gm_commands/race.cpp + gm_commands/raidloot.cpp + gm_commands/randomfeatures.cpp + gm_commands/refreshgroup.cpp + gm_commands/reloadaa.cpp + gm_commands/reloadallrules.cpp + gm_commands/reloademote.cpp + gm_commands/reloadlevelmods.cpp + gm_commands/reloadmerchants.cpp + gm_commands/reloadperlexportsettings.cpp + gm_commands/reloadqst.cpp + gm_commands/reloadstatic.cpp + gm_commands/reloadtitles.cpp + gm_commands/reloadtraps.cpp + gm_commands/reloadworld.cpp + gm_commands/reloadworldrules.cpp + gm_commands/reloadzps.cpp + gm_commands/repop.cpp + gm_commands/resetaa.cpp + gm_commands/resetaa_timer.cpp + gm_commands/resetdisc_timer.cpp + gm_commands/revoke.cpp + gm_commands/roambox.cpp + gm_commands/rules.cpp + gm_commands/save.cpp + gm_commands/scale.cpp + gm_commands/scribespell.cpp + gm_commands/scribespells.cpp + gm_commands/sendzonespawns.cpp + gm_commands/sensetrap.cpp + gm_commands/serverinfo.cpp + gm_commands/serverrules.cpp + gm_commands/set_adventure_points.cpp + gm_commands/setaapts.cpp + gm_commands/setaaxp.cpp + gm_commands/setanim.cpp + gm_commands/setcrystals.cpp + gm_commands/setfaction.cpp + gm_commands/setgraveyard.cpp + gm_commands/setlanguage.cpp + gm_commands/setlsinfo.cpp + gm_commands/setpass.cpp + gm_commands/setpvppoints.cpp + gm_commands/setskill.cpp + gm_commands/setskillall.cpp + gm_commands/setstartzone.cpp + gm_commands/setstat.cpp + gm_commands/setxp.cpp + gm_commands/showbonusstats.cpp + gm_commands/showbuffs.cpp + gm_commands/shownpcgloballoot.cpp + gm_commands/shownumhits.cpp + gm_commands/showskills.cpp + gm_commands/showspellslist.cpp + gm_commands/showstats.cpp + gm_commands/showzonegloballoot.cpp + gm_commands/showzonepoints.cpp + gm_commands/shutdown.cpp + gm_commands/size.cpp + gm_commands/spawn.cpp + gm_commands/spawnfix.cpp + gm_commands/spawnstatus.cpp + gm_commands/spellinfo.cpp + gm_commands/stun.cpp + gm_commands/summon.cpp + gm_commands/summonburiedplayercorpse.cpp + gm_commands/summonitem.cpp + gm_commands/suspend.cpp + gm_commands/task.cpp + gm_commands/tattoo.cpp + gm_commands/tempname.cpp + gm_commands/texture.cpp + gm_commands/time.cpp + gm_commands/timers.cpp + gm_commands/timezone.cpp + gm_commands/title.cpp + gm_commands/titlesuffix.cpp + gm_commands/traindisc.cpp + gm_commands/trapinfo.cpp + gm_commands/tune.cpp + gm_commands/ucs.cpp + gm_commands/undye.cpp + gm_commands/undyeme.cpp + gm_commands/unfreeze.cpp + gm_commands/unlock.cpp + gm_commands/unscribespell.cpp + gm_commands/unscribespells.cpp + gm_commands/untraindisc.cpp + gm_commands/untraindiscs.cpp + gm_commands/uptime.cpp + gm_commands/version.cpp + gm_commands/viewnpctype.cpp + gm_commands/viewpetition.cpp + gm_commands/viewzoneloot.cpp + gm_commands/wc.cpp + gm_commands/weather.cpp + gm_commands/who.cpp + gm_commands/worldshutdown.cpp + gm_commands/worldwide.cpp + gm_commands/wp.cpp + gm_commands/wpadd.cpp + gm_commands/wpinfo.cpp + gm_commands/xtargets.cpp + gm_commands/zclip.cpp + gm_commands/zcolor.cpp + gm_commands/zheader.cpp + gm_commands/zonebootup.cpp + gm_commands/zonelock.cpp + gm_commands/zoneshutdown.cpp + gm_commands/zonespawn.cpp + gm_commands/zonestatus.cpp + gm_commands/zopp.cpp + gm_commands/zsafecoords.cpp + gm_commands/zsave.cpp + gm_commands/zsky.cpp + gm_commands/zstats.cpp + gm_commands/zunderworld.cpp + gm_commands/zuwcoords.cpp ) -ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers}) +ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers} ${gm_commands}) INSTALL(TARGETS zone RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) diff --git a/zone/command.cpp b/zone/command.cpp index e33e37cf9..1ed2629e2 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -1,36 +1,3 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.org) - - 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 -*/ - -/* - - To add a new command 3 things must be done: - - 1. At the bottom of command.h you must add a prototype for it. - 2. Add the function in this file. - 3. In the command_init function you must add a call to command_add - for your function. - - Notes: If you want an alias for your command, add an entry to the - `command_settings` table in your database. The access level you - set with command_add is the default setting if the command isn't - listed in the `command_settings` db table. - -*/ #include #include @@ -47,16 +14,12 @@ #include "../common/global_define.h" #include "../common/eq_packet.h" #include "../common/features.h" -#include "../common/guilds.h" -#include "../common/patches/patches.h" #include "../common/ptimer.h" #include "../common/rulesys.h" -#include "../common/serverinfo.h" #include "../common/string_util.h" #include "../common/say_link.h" -#include "../common/eqemu_logsys.h" -#include "../common/profanity_manager.h" #include "../common/net/eqstream.h" +#include "../common/file_util.h" #include "../common/repositories/dynamic_zones_repository.h" #include "data_bucket.h" @@ -64,22 +27,15 @@ #include "dynamic_zone.h" #include "expedition.h" #include "guild_mgr.h" -#include "map.h" #include "qglobals.h" #include "queryserv.h" #include "quest_parser_collection.h" -#include "string_ids.h" #include "titles.h" #include "water_map.h" #include "worldserver.h" #include "fastmath.h" #include "mob_movement_manager.h" #include "npc_scale_manager.h" -#include "../common/content/world_content_service.h" -#include "../common/http/httplib.h" -#include "../common/shared_tasks.h" -#include "gm_commands/door_manipulation.h" -#include "../common/languages.h" extern QueryServ* QServ; extern WorldServer worldserver; @@ -94,10 +50,6 @@ int commandcount; // how many commands we have // init has been performed to point at the real function int (*command_dispatch)(Client *,char const *)=command_notavail; - -void command_bestz(Client *c, const Seperator *message); -void command_pf(Client *c, const Seperator *message); - std::map commandlist; std::map commandaliases; @@ -319,11 +271,6 @@ int command_init(void) command_add("object", "List|Add|Edit|Move|Rotate|Copy|Save|Undo|Delete - Manipulate static and tradeskill objects within the zone", AccountStatus::GMAdmin, command_object) || command_add("oocmute", "[1/0] - Mutes OOC chat", AccountStatus::GMMgmt, command_oocmute) || command_add("opcode", "- opcode management", AccountStatus::GMImpossible, command_opcode) || - -#ifdef PACKET_PROFILER - command_add("packetprofile", "- Dump packet profile for target or self.", AccountStatus::GMImpossible, command_packetprofile) || -#endif - command_add("path", "- view and edit pathing", AccountStatus::GMMgmt, command_path) || command_add("peekinv", "[equip/gen/cursor/poss/limbo/curlim/trib/bank/shbank/allbank/trade/world/all] - Print out contents of your player target's inventory", AccountStatus::GMAdmin, command_peekinv) || command_add("peqzone", "[zonename] - Go to specified zone, if you have > 75% health", AccountStatus::Player, command_peqzone) || @@ -334,12 +281,6 @@ int command_init(void) command_add("pf", "- Display additional mob coordinate and wandering data", AccountStatus::Player, command_pf) || command_add("picklock", "Analog for ldon pick lock for the newer clients since we still don't have it working.", AccountStatus::Player, command_picklock) || command_add("profanity", "Manage censored language.", AccountStatus::GMLeadAdmin, command_profanity) || - -#ifdef EQPROFILE - command_add("profiledump", "- Dump profiling info to logs", AccountStatus::GMImpossible, command_profiledump) || - command_add("profilereset", "- Reset profiling info", AccountStatus::GMImpossible, command_profilereset) || -#endif - command_add("push", "Lets you do spell push", AccountStatus::GMLeadAdmin, command_push) || command_add("proximity", "Shows NPC proximity", AccountStatus::GMLeadAdmin, command_proximity) || command_add("pvp", "[on/off] - Set your or your player target's PVP status", AccountStatus::GMAdmin, command_pvp) || @@ -679,279 +620,6 @@ int command_realdispatch(Client *c, const char *message) } -void command_logcommand(Client *c, const char *message) -{ - int admin = c->Admin(); - - bool continueevents = false; - switch (zone->loglevelvar){ //catch failsafe - case 9: { // log only LeadGM - if ( - admin >= AccountStatus::GMLeadAdmin && - admin < AccountStatus::GMMgmt - ) { - continueevents = true; - } - break; - } - case 8: { // log only GM - if ( - admin >= AccountStatus::GMAdmin && - admin < AccountStatus::GMLeadAdmin - ) { - continueevents = true; - } - break; - } - case 1: { - if (admin >= AccountStatus::GMMgmt) { - continueevents = true; - } - break; - } - case 2: { - if (admin >= AccountStatus::GMLeadAdmin) { - continueevents = true; - } - break; - } - case 3: { - if (admin >= AccountStatus::GMAdmin) { - continueevents = true; - } - break; - } - case 4: { - if (admin >= AccountStatus::QuestTroupe) { - continueevents = true; - } - break; - } - case 5: { - if (admin >= AccountStatus::ApprenticeGuide) { - continueevents = true; - } - break; - } - case 6: { - if (admin >= AccountStatus::Steward) { - continueevents = true; - } - break; - } - case 7: { - continueevents = true; - break; - } - } - - if (continueevents) - database.logevents( - c->AccountName(), - c->AccountID(), - admin,c->GetName(), - c->GetTarget()?c->GetTarget()->GetName():"None", - "Command", - message, - 1 - ); -} - - -/* - * commands go below here - */ -void command_worldwide(Client *c, const Seperator *sep) -{ - std::string sub_command; - if (sep->arg[1]) { - sub_command = sep->arg[1]; - } - - if (sub_command == "cast") { - if (sep->arg[2] && Seperator::IsNumber(sep->arg[2])) { - uint8 update_type = WWSpellUpdateType_Cast; - auto spell_id = std::stoul(sep->arg[2]); - bool disable_message = false; - if (sep->arg[3] && Seperator::IsNumber(sep->arg[3])) { - disable_message = std::stoi(sep->arg[3]) ? true : false; - } - - c->Message( - Chat::White, - fmt::format( - "World Wide Cast Spell | Spell: {} ({})", - GetSpellName(spell_id), - spell_id - ).c_str() - ); - - quest_manager.WorldWideSpell(update_type, spell_id); - if (!disable_message) { - quest_manager.WorldWideMessage( - Chat::Yellow, - fmt::format( - "[SYSTEM] A GM has cast [{}] world-wide!", - GetSpellName(spell_id) - ).c_str() - ); - } - } else { - c->Message(Chat::White, "Usage: #worldwide cast [Spell ID] [Disable Message]"); - } - } else if (sub_command == "remove") { - if (sep->arg[2] && Seperator::IsNumber(sep->arg[2])) { - uint8 update_type = WWSpellUpdateType_Remove; - auto spell_id = std::stoul(sep->arg[2]); - - c->Message( - Chat::White, - fmt::format( - "World Wide Remove Spell | Spell: {} ({})", - GetSpellName(spell_id), - spell_id - ).c_str() - ); - - quest_manager.WorldWideSpell(update_type, spell_id); - } else { - c->Message(Chat::White, "Usage: #worldwide remove [Spell ID]"); - } - } else if (sub_command == "message") { - if (sep->arg[2]) { - std::string message = sep->arg[2]; - quest_manager.WorldWideMessage( - Chat::White, - fmt::format( - "{}", - message - ).c_str() - ); - } else { - c->Message(Chat::White, "Usage: #worldwide message [Message]"); - } - } else if (sub_command == "move") { - if (sep->arg[2]) { - uint8 update_type = WWMoveUpdateType_MoveZone; - uint32 zone_id = 0; - std::string zone_short_name; - if (Seperator::IsNumber(sep->arg[2])) { - zone_id = std::stoul(sep->arg[2]); - } - - if (zone_id) { - zone_short_name = ZoneName(zone_id); - } else { - zone_short_name = sep->arg[2]; - } - - c->Message( - Chat::White, - fmt::format( - "World Wide Zone | Zone: {} ({}) ID: {}", - ZoneLongName( - ZoneID(zone_short_name) - ), - zone_short_name, - ZoneID(zone_short_name) - ).c_str() - ); - - quest_manager.WorldWideMove(update_type, zone_short_name.c_str()); - } else { - c->Message( - Chat::White, - "Usage: #worldwide move [Zone ID] or #worldwide move [Zone Short Name]" - ); - } - } else if (sub_command == "moveinstance") { - if (Seperator::IsNumber(sep->arg[2])) { - uint8 update_type = WWMoveUpdateType_MoveZoneInstance; - const char* zone_short_name = ""; - uint16 instance_id = std::stoi(sep->arg[2]); - - c->Message( - Chat::White, - fmt::format( - "World Wide Zone Instance | Instance ID: {}", - instance_id - ).c_str() - ); - - quest_manager.WorldWideMove(update_type, zone_short_name, instance_id); - } else { - c->Message(Chat::White, "Usage: #worldwide moveinstance [Instance ID]"); - } - } - - if (!sep->arg[1]) { - c->Message(Chat::White, "This command is used to perform world-wide tasks."); - c->Message(Chat::White, "Usage: #worldwide cast [Spell ID] [Disable Message]"); - c->Message(Chat::White, "Usage: #worldwide remove [Spell ID]"); - c->Message(Chat::White, "Usage: #worldwide message [Message]"); - c->Message( - Chat::White, - "Usage: #worldwide move [Zone ID] or #worldwide move [Zone Short Name]" - ); - c->Message(Chat::White, "Usage: #worldwide moveinstance [Instance ID]"); - } -} - -void command_endurance(Client *c, const Seperator *sep) -{ - auto target = c->GetTarget() ? c->GetTarget() : c; - if (target->IsClient()) { - target->CastToClient()->SetEndurance(target->CastToClient()->GetMaxEndurance()); - } else { - target->SetEndurance(target->GetMaxEndurance()); - } - - if (c != target) { - c->Message( - Chat::White, - fmt::format( - "Set {} ({}) to full Endurance.", - target->GetCleanName(), - target->GetID() - ).c_str() - ); - } else { - c->Message(Chat::White, "Restored your Endurance to full."); - } -} - -void command_setstat(Client* c, const Seperator* sep){ - if(sep->arg[1][0] && sep->arg[2][0] && c->GetTarget()!=0 && c->GetTarget()->IsClient()){ - c->GetTarget()->CastToClient()->SetStats(atoi(sep->arg[1]),atoi(sep->arg[2])); - } - else{ - c->Message(Chat::White,"This command is used to permanently increase or decrease a players stats."); - c->Message(Chat::White,"Usage: #setstat {type} {value the stat should be}"); - c->Message(Chat::White,"Types: Str: 0, Sta: 1, Agi: 2, Dex: 3, Int: 4, Wis: 5, Cha: 6"); - } -} - -void command_incstat(Client* c, const Seperator* sep){ - if(sep->arg[1][0] && sep->arg[2][0] && c->GetTarget()!=0 && c->GetTarget()->IsClient()){ - c->GetTarget()->CastToClient()->IncStats(atoi(sep->arg[1]),atoi(sep->arg[2])); - } - else{ - c->Message(Chat::White,"This command is used to permanently increase or decrease a players stats."); - c->Message(Chat::White,"Usage: #setstat {type} {value by which to increase or decrease}"); - c->Message(Chat::White,"Note: The value is in increments of 2, so a value of 3 will actually increase the stat by 6"); - c->Message(Chat::White,"Types: Str: 0, Sta: 1, Agi: 2, Dex: 3, Int: 4, Wis: 5, Cha: 6"); - } -} - -void command_resetaa(Client* c,const Seperator *sep) { - if(c->GetTarget() && c->GetTarget()->IsClient()){ - c->GetTarget()->CastToClient()->ResetAA(); - c->Message(Chat::Red,"Successfully reset %s's AAs", c->GetTarget()->GetName()); - } - else - c->Message(Chat::White,"Usage: Target a client and use #resetaa to reset the AA data in their Profile."); -} - void command_help(Client *c, const Seperator *sep) { int commands_shown=0; @@ -984,335 +652,6 @@ void command_help(Client *c, const Seperator *sep) } -void command_version(Client *c, const Seperator *sep) -{ - c->Message(Chat::White, "Current version information."); - c->Message(Chat::White, " %s", CURRENT_VERSION); - c->Message(Chat::White, " Compiled on: %s at %s", COMPILE_DATE, COMPILE_TIME); - c->Message(Chat::White, " Last modified on: %s", LAST_MODIFIED); -} - -void command_setfaction(Client *c, const Seperator *sep) -{ - if((sep->arg[1][0] == 0 || strcasecmp(sep->arg[1],"*")==0) || ((c->GetTarget()==0) || (c->GetTarget()->IsClient()))) { - c->Message(Chat::White, "Usage: #setfaction [faction number]"); - return; - } - - auto npcTypeID = c->GetTarget()->CastToNPC()->GetNPCTypeID(); - c->Message(Chat::Yellow,"Setting NPC %u to faction %i", npcTypeID, atoi(sep->argplus[1])); - - std::string query = StringFormat("UPDATE npc_types SET npc_faction_id = %i WHERE id = %i", - atoi(sep->argplus[1]), npcTypeID); - content_db.QueryDatabase(query); -} - -void command_wc(Client *c, const Seperator *sep) -{ - if (sep->argnum < 2) { - c->Message( - 0, - "Usage: #wc [wear slot] [material] [ [hero_forge_model] [elite_material] [unknown06] [unknown18] ]" - ); - } - else if (c->GetTarget() == nullptr) { - c->Message(Chat::Red, "You must have a target to do a wear change."); - } - else { - uint32 hero_forge_model = 0; - uint32 wearslot = atoi(sep->arg[1]); - - // Hero Forge - if (sep->argnum > 2) { - hero_forge_model = atoi(sep->arg[3]); - - if (hero_forge_model != 0 && hero_forge_model < 1000) { - // Shorthand Hero Forge ID. Otherwise use the value the user entered. - hero_forge_model = (hero_forge_model * 100) + wearslot; - } - } - /* - // Leaving here to add color option to the #wc command eventually - uint32 Color; - if (c->GetTarget()->IsClient()) - Color = c->GetTarget()->GetEquipmentColor(atoi(sep->arg[1])); - else - Color = c->GetTarget()->GetArmorTint(atoi(sep->arg[1])); - */ - c->GetTarget()->SendTextureWC( - wearslot, - atoi(sep->arg[2]), - hero_forge_model, - atoi(sep->arg[4]), - atoi(sep->arg[5]), - atoi(sep->arg[6])); - } -} - -void command_heromodel(Client *c, const Seperator *sep) -{ - if (sep->argnum < 1) { - c->Message(Chat::White, "Usage: #heromodel [hero forge model] [ [slot] ] (example: #heromodel 63)"); - } - else if (c->GetTarget() == nullptr) { - c->Message(Chat::Red, "You must have a target to do a wear change for Hero's Forge Models."); - } - else { - uint32 hero_forge_model = atoi(sep->arg[1]); - - if (sep->argnum > 1) { - uint8 wearslot = (uint8) atoi(sep->arg[2]); - c->GetTarget()->SendTextureWC(wearslot, 0, hero_forge_model, 0, 0, 0); - } - else { - if (hero_forge_model > 0) { - // Conversion to simplify the command arguments - // Hero's Forge model is actually model * 1000 + texture * 100 + wearslot - // Hero's Forge Model slot 7 is actually for Robes, but it still needs to use wearslot 1 in the packet - hero_forge_model *= 100; - - for (uint8 wearslot = 0; wearslot < 7; wearslot++) { - c->GetTarget()->SendTextureWC(wearslot, 0, (hero_forge_model + wearslot), 0, 0, 0); - } - } - else { - c->Message(Chat::Red, "Hero's Forge Model must be greater than 0."); - } - } - } -} - -void command_setanim(Client *c, const Seperator *sep) -{ - if (c->GetTarget() && sep->IsNumber(1)) { - int num = atoi(sep->arg[1]); - if (num < 0 || num >= _eaMaxAppearance) { - c->Message(Chat::White, "Invalid animation number, between 0 and %d", _eaMaxAppearance - 1); - } - c->GetTarget()->SetAppearance(EmuAppearance(num)); - } - else { - c->Message(Chat::White, "Usage: #setanim [animnum]"); - } -} - -void command_serverinfo(Client *c, const Seperator *sep) -{ - auto os = EQ::GetOS(); - auto cpus = EQ::GetCPUs(); - auto pid = EQ::GetPID(); - auto rss = EQ::GetRSS(); - auto uptime = EQ::GetUptime(); - - c->Message(Chat::White, "Operating System Information"); - c->Message(Chat::White, "=================================================="); - c->Message(Chat::White, "System: %s", os.sysname.c_str()); - c->Message(Chat::White, "Release: %s", os.release.c_str()); - c->Message(Chat::White, "Version: %s", os.version.c_str()); - c->Message(Chat::White, "Machine: %s", os.machine.c_str()); - c->Message(Chat::White, "Uptime: %.2f seconds", uptime); - c->Message(Chat::White, "=================================================="); - c->Message(Chat::White, "CPU Information"); - c->Message(Chat::White, "=================================================="); - for (size_t i = 0; i < cpus.size(); ++i) { - auto &cp = cpus[i]; - c->Message(Chat::White, "CPU #%i: %s, Speed: %.2fGhz", i, cp.model.c_str(), cp.speed); - } - c->Message(Chat::White, "=================================================="); - c->Message(Chat::White, "Process Information"); - c->Message(Chat::White, "=================================================="); - c->Message(Chat::White, "PID: %u", pid); - c->Message(Chat::White, "RSS: %.2f MB", rss / 1048576.0); - c->Message(Chat::White, "=================================================="); -} - -void command_getvariable(Client *c, const Seperator *sep) -{ - std::string tmp; - if (database.GetVariable(sep->argplus[1], tmp)) - c->Message(Chat::White, "%s = %s", sep->argplus[1], tmp.c_str()); - else - c->Message(Chat::White, "GetVariable(%s) returned false", sep->argplus[1]); -} - -void command_chat(Client *c, const Seperator *sep) -{ - if (sep->arg[2][0] == 0) - c->Message(Chat::White, "Usage: #chat [channum] [message]"); - else - if (!worldserver.SendChannelMessage(0, 0, (uint8) atoi(sep->arg[1]), 0, 0, 100, sep->argplus[2])) - c->Message(Chat::White, "Error: World server disconnected"); -} - -void command_npcloot(Client *c, const Seperator *sep) -{ - if (c->GetTarget() == 0) - c->Message(Chat::White, "Error: No target"); - // #npcloot show - else if (strcasecmp(sep->arg[1], "show") == 0) - { - if (c->GetTarget()->IsNPC()) - c->GetTarget()->CastToNPC()->QueryLoot(c); - else if (c->GetTarget()->IsCorpse()) - c->GetTarget()->CastToCorpse()->QueryLoot(c); - else - c->Message(Chat::White, "Error: Target's type doesnt have loot"); - } - // These 2 types are *BAD* for the next few commands - else if (c->GetTarget()->IsClient() || c->GetTarget()->IsCorpse()) - c->Message(Chat::White, "Error: Invalid target type, try a NPC =)."); - // #npcloot add - else if (strcasecmp(sep->arg[1], "add") == 0) - { - // #npcloot add item - if (c->GetTarget()->IsNPC() && sep->IsNumber(2)) - { - uint32 item = atoi(sep->arg[2]); - if (database.GetItem(item)) - { - if (sep->arg[3][0] != 0 && sep->IsNumber(3)) - c->GetTarget()->CastToNPC()->AddItem(item, atoi(sep->arg[3]), 0); - else - c->GetTarget()->CastToNPC()->AddItem(item, 1, 0); - c->Message(Chat::White, "Added item(%i) to the %s's loot.", item, c->GetTarget()->GetName()); - } - else - c->Message(Chat::White, "Error: #npcloot add: Item(%i) does not exist!", item); - } - else if (!sep->IsNumber(2)) - c->Message(Chat::White, "Error: #npcloot add: Itemid must be a number."); - else - c->Message(Chat::White, "Error: #npcloot add: This is not a valid target."); - } - // #npcloot remove - else if (strcasecmp(sep->arg[1], "remove") == 0) - { - //#npcloot remove all - if (strcasecmp(sep->arg[2], "all") == 0) - c->Message(Chat::White, "Error: #npcloot remove all: Not yet implemented."); - //#npcloot remove itemid - else - { - if(c->GetTarget()->IsNPC() && sep->IsNumber(2)) - { - uint32 item = atoi(sep->arg[2]); - c->GetTarget()->CastToNPC()->RemoveItem(item); - c->Message(Chat::White, "Removed item(%i) from the %s's loot.", item, c->GetTarget()->GetName()); - } - else if (!sep->IsNumber(2)) - c->Message(Chat::White, "Error: #npcloot remove: Item must be a number."); - else - c->Message(Chat::White, "Error: #npcloot remove: This is not a valid target."); - } - } - // #npcloot money - else if (strcasecmp(sep->arg[1], "money") == 0) - { - if (c->GetTarget()->IsNPC() && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4) && sep->IsNumber(5)) - { - if ((atoi(sep->arg[2]) < 34465 && atoi(sep->arg[2]) >= 0) && (atoi(sep->arg[3]) < 34465 && atoi(sep->arg[3]) >= 0) && (atoi(sep->arg[4]) < 34465 && atoi(sep->arg[4]) >= 0) && (atoi(sep->arg[5]) < 34465 && atoi(sep->arg[5]) >= 0)) - { - c->GetTarget()->CastToNPC()->AddCash(atoi(sep->arg[5]), atoi(sep->arg[4]), atoi(sep->arg[3]), atoi(sep->arg[2])); - c->Message(Chat::White, "Set %i Platinum, %i Gold, %i Silver, and %i Copper as %s's money.", atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), atoi(sep->arg[5]), c->GetTarget()->GetName()); - } - else - c->Message(Chat::White, "Error: #npcloot money: Values must be between 0-34465."); - } - else - c->Message(Chat::White, "Usage: #npcloot money platinum gold silver copper"); - } - else - c->Message(Chat::White, "Usage: #npcloot [show/money/add/remove] [itemid/all/money: pp gp sp cp]"); -} - -void command_gm(Client *c, const Seperator *sep) -{ - bool state=atobool(sep->arg[1]); - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient()) - t=c->GetTarget()->CastToClient(); - - if(sep->arg[1][0] != 0) { - t->SetGM(state); - c->Message(Chat::White, "%s is %s a GM.", t->GetName(), state?"now":"no longer"); - } - else - c->Message(Chat::White, "Usage: #gm [on/off]"); -} - -// there's no need for this, as /summon already takes care of it -// this command is here for reference but it is not added to the -// list above - -//To whoever wrote the above: And what about /kill, /zone, /zoneserver, etc? -//There is a reason for the # commands: so that admins can specifically enable certain -//commands for their users. Some might want users to #summon but not to /kill. Cant do that if they are a GM -void command_summon(Client *c, const Seperator *sep) -{ - Mob *t; - - if(sep->arg[1][0] != 0) // arg specified - { - Client* client = entity_list.GetClientByName(sep->arg[1]); - if (client != 0) // found player in zone - t=client->CastToMob(); - else - { - if (!worldserver.Connected()) - c->Message(Chat::White, "Error: World server disconnected."); - else - { // player is in another zone - //Taking this command out until we test the factor of 8 in ServerOP_ZonePlayer - //c->Message(Chat::White, "Summoning player from another zone not yet implemented."); - //return; - - auto pack = new ServerPacket(ServerOP_ZonePlayer, sizeof(ServerZonePlayer_Struct)); - ServerZonePlayer_Struct* szp = (ServerZonePlayer_Struct*) pack->pBuffer; - strcpy(szp->adminname, c->GetName()); - szp->adminrank = c->Admin(); - szp->ignorerestrictions = 2; - strcpy(szp->name, sep->arg[1]); - strcpy(szp->zone, zone->GetShortName()); - szp->x_pos = c->GetX(); // May need to add a factor of 8 in here.. - szp->y_pos = c->GetY(); - szp->z_pos = c->GetZ(); - szp->instance_id = zone->GetInstanceID(); - worldserver.SendPacket(pack); - safe_delete(pack); - } - return; - } - } - else if(c->GetTarget()) // have target - t=c->GetTarget(); - else - { - c->Message(Chat::White, "Usage: #summon [charname] Either target or charname is required"); - return; - } - - if(!t) - return; - - if (t->IsNPC()) - { // npc target - c->Message(Chat::White, "Summoning NPC %s to %1.1f, %1.1f, %1.1f", t->GetName(), c->GetX(), c->GetY(), c->GetZ()); - t->CastToNPC()->GMMove(c->GetX(), c->GetY(), c->GetZ(), c->GetHeading()); - t->CastToNPC()->SaveGuardSpot(glm::vec4(0.0f)); - } - else if (t->IsCorpse()) - { // corpse target - c->Message(Chat::White, "Summoning corpse %s to %1.1f, %1.1f, %1.1f", t->GetName(), c->GetX(), c->GetY(), c->GetZ()); - t->CastToCorpse()->GMMove(c->GetX(), c->GetY(), c->GetZ(), c->GetHeading()); - } - else if (t->IsClient()) - { - c->Message(Chat::White, "Summoning player %s to %1.1f, %1.1f, %1.1f", t->GetName(), c->GetX(), c->GetY(), c->GetZ()); - t->CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), c->GetX(), c->GetY(), c->GetZ(), c->GetHeading(), 2, GMSummon); - } -} - void command_zone(Client *c, const Seperator *sep) { if(c->Admin() < commandZoneToCoords && @@ -1424,2656 +763,6 @@ void command_zone_instance(Client *c, const Seperator *sep) } } -void command_showbuffs(Client *c, const Seperator *sep) -{ - if (c->GetTarget() == 0) - c->CastToMob()->ShowBuffs(c); - else - c->GetTarget()->CastToMob()->ShowBuffs(c); -} - -void command_peqzone(Client *c, const Seperator *sep) -{ - uint32 timeleft = c->GetPTimers().GetRemainingTime(pTimerPeqzoneReuse)/60; - - if(!c->GetPTimers().Expired(&database, pTimerPeqzoneReuse, false)) { - c->Message(Chat::Red,"You must wait %i minute(s) before using this ability again.", timeleft); - return; - } - - if(c->GetHPRatio() < 75) { - c->Message(Chat::White, "You cannot use this command with less than 75 percent health."); - return; - } - - //this isnt perfect, but its better... - if( - c->IsInvisible(c) - || c->IsRooted() - || c->IsStunned() - || c->IsMezzed() - || c->AutoAttackEnabled() - || c->GetInvul() - ) { - c->Message(Chat::White, "You cannot use this command in your current state. Settle down and wait."); - return; - } - - uint16 zoneid = 0; - uint8 destzone = 0; - if (sep->IsNumber(1)) - { - zoneid = atoi(sep->arg[1]); - destzone = content_db.GetPEQZone(zoneid, 0); - if(destzone == 0){ - c->Message(Chat::Red, "You cannot use this command to enter that zone!"); - return; - } - if(zoneid == zone->GetZoneID()) { - c->Message(Chat::Red, "You cannot use this command on the zone you are in!"); - return; - } - } - else if (sep->arg[1][0] == 0 || sep->IsNumber(2) || sep->IsNumber(3) || sep->IsNumber(4) || sep->IsNumber(5)) - { - c->Message(Chat::White, "Usage: #peqzone [zonename]"); - c->Message(Chat::White, "Optional Usage: #peqzone [zoneid]"); - return; - } - else { - zoneid = ZoneID(sep->arg[1]); - destzone = content_db.GetPEQZone(zoneid, 0); - if(zoneid == 0) { - c->Message(Chat::White, "Unable to locate zone '%s'", sep->arg[1]); - return; - } - if(destzone == 0){ - c->Message(Chat::Red, "You cannot use this command to enter that zone!"); - return; - } - if(zoneid == zone->GetZoneID()) { - c->Message(Chat::Red, "You cannot use this command on the zone you are in!"); - return; - } - } - - if(RuleB (Zone, UsePEQZoneDebuffs)){ - c->SpellOnTarget(RuleI(Zone, PEQZoneDebuff1), c); - c->SpellOnTarget(RuleI(Zone, PEQZoneDebuff2), c); - } - - //zone to safe coords - c->GetPTimers().Start(pTimerPeqzoneReuse, RuleI(Zone, PEQZoneReuseTime)); - c->MovePC(zoneid, 0.0f, 0.0f, 0.0f, 0.0f, 0, ZoneToSafeCoords); -} - -void command_movechar(Client *c, const Seperator *sep) -{ - if(sep->arg[1][0]==0 || sep->arg[2][0] == 0) - c->Message(Chat::White, "Usage: #movechar [charactername] [zonename]"); - else if (c->Admin() < commandMovecharToSpecials && strcasecmp(sep->arg[2], "cshome") == 0 || strcasecmp(sep->arg[2], "load") == 0 || strcasecmp(sep->arg[2], "load2") == 0) - c->Message(Chat::White, "Invalid zone name"); - else - { - uint32 tmp = database.GetAccountIDByChar(sep->arg[1]); - if (tmp) - { - if (c->Admin() >= commandMovecharSelfOnly || tmp == c->AccountID()) - if (!database.MoveCharacterToZone((char*) sep->arg[1], ZoneID(sep->arg[2]))) - c->Message(Chat::White, "Character Move Failed!"); - else - c->Message(Chat::White, "Character has been moved."); - else - c->Message(Chat::Red,"You cannot move characters that are not on your account."); - } - else - c->Message(Chat::White, "Character Does Not Exist"); - } -} - -void command_movement(Client *c, const Seperator *sep) -{ - auto &mgr = MobMovementManager::Get(); - - if (sep->arg[1][0] == 0) { - c->Message(Chat::White, "Usage: #movement stats/clearstats/walkto/runto/rotateto/stop/packet"); - return; - } - - if (strcasecmp(sep->arg[1], "stats") == 0) - { - mgr.DumpStats(c); - } - else if (strcasecmp(sep->arg[1], "clearstats") == 0) - { - mgr.ClearStats(); - } - else if (strcasecmp(sep->arg[1], "walkto") == 0) - { - auto target = c->GetTarget(); - if (target == nullptr) { - c->Message(Chat::White, "No target found."); - return; - } - - target->WalkTo(c->GetX(), c->GetY(), c->GetZ()); - } - else if (strcasecmp(sep->arg[1], "runto") == 0) - { - auto target = c->GetTarget(); - if (target == nullptr) { - c->Message(Chat::White, "No target found."); - return; - } - - target->RunTo(c->GetX(), c->GetY(), c->GetZ()); - } - else if (strcasecmp(sep->arg[1], "rotateto") == 0) - { - auto target = c->GetTarget(); - if (target == nullptr) { - c->Message(Chat::White, "No target found."); - return; - } - - target->RotateToWalking(target->CalculateHeadingToTarget(c->GetX(), c->GetY())); - } - else if (strcasecmp(sep->arg[1], "stop") == 0) - { - auto target = c->GetTarget(); - if (target == nullptr) { - c->Message(Chat::White, "No target found."); - return; - } - - target->StopNavigation(); - } - else if (strcasecmp(sep->arg[1], "packet") == 0) - { - auto target = c->GetTarget(); - if (target == nullptr) { - c->Message(Chat::White, "No target found."); - return; - } - - mgr.SendCommandToClients(target, atof(sep->arg[2]), atof(sep->arg[3]), atof(sep->arg[4]), atof(sep->arg[5]), atoi(sep->arg[6]), ClientRangeAny); - } - else { - c->Message(Chat::White, "Usage: #movement stats/clearstats/walkto/runto/rotateto/stop/packet"); - } -} - -void command_viewpetition(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0] == 0) { - c->Message(Chat::White, "Usage: #viewpetition (petition number) Type #listpetition for a list"); - return; - } - - c->Message(Chat::Red," ID : Character Name , Petition Text"); - - std::string query = "SELECT petid, charname, petitiontext FROM petitions ORDER BY petid"; - auto results = database.QueryDatabase(query); - if (!results.Success()) - return; - - LogInfo("View petition request from [{}], petition number: [{}]", c->GetName(), atoi(sep->argplus[1]) ); - - if (results.RowCount() == 0) { - c->Message(Chat::Red,"There was an error in your request: ID not found! Please check the Id and try again."); - return; - } - - for (auto row = results.begin(); row != results.end(); ++row) - if (strcasecmp(row[0], sep->argplus[1]) == 0) - c->Message(Chat::Yellow, " %s: %s , %s ", row[0], row[1], row[2]); - -} - -void command_petitioninfo(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0] == 0) { - c->Message(Chat::White, "Usage: #petitioninfo (petition number) Type #listpetition for a list"); - return; - } - - std::string query = "SELECT petid, charname, accountname, zone, charclass, charrace, charlevel FROM petitions ORDER BY petid"; - auto results = database.QueryDatabase(query); - if (!results.Success()) - return; - - LogInfo("Petition information request from [{}], petition number:", c->GetName(), atoi(sep->argplus[1]) ); - - if (results.RowCount() == 0) { - c->Message(Chat::Red,"There was an error in your request: ID not found! Please check the Id and try again."); - return; - } - - for (auto row = results.begin(); row != results.end(); ++row) - if (strcasecmp(row[0],sep->argplus[1])== 0) - c->Message(Chat::Red," ID : %s Character Name: %s Account Name: %s Zone: %s Character Class: %s Character Race: %s Character Level: %s", row[0],row[1],row[2],row[3],row[4],row[5],row[6]); - -} - -void command_delpetition(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0] == 0 || strcasecmp(sep->arg[1],"*") == 0) { - c->Message(Chat::White, "Usage: #delpetition (petition number) Type #listpetition for a list"); - return; - } - - c->Message(Chat::Red,"Attempting to delete petition number: %i", atoi(sep->argplus[1])); - std::string query = StringFormat("DELETE FROM petitions WHERE petid = %i", atoi(sep->argplus[1])); - auto results = database.QueryDatabase(query); - if (!results.Success()) - return; - - LogInfo("Delete petition request from [{}], petition number:", c->GetName(), atoi(sep->argplus[1]) ); - -} - -void command_list(Client *c, const Seperator *sep) -{ - std::string search_type; - if (strcasecmp(sep->arg[1], "npcs") == 0) { - search_type = "npcs"; - } - - if (strcasecmp(sep->arg[1], "players") == 0) { - search_type = "players"; - } - - if (strcasecmp(sep->arg[1], "corpses") == 0) { - search_type = "corpses"; - } - - if (strcasecmp(sep->arg[1], "doors") == 0) { - search_type = "doors"; - } - - if (strcasecmp(sep->arg[1], "objects") == 0) { - search_type = "objects"; - } - - if (search_type.length() > 0) { - - int entity_count = 0; - int found_count = 0; - - std::string search_string; - - if (sep->arg[2]) { - search_string = sep->arg[2]; - } - - /** - * NPC - */ - if (search_type.find("npcs") != std::string::npos) { - auto &entity_list_search = entity_list.GetMobList(); - - for (auto &itr : entity_list_search) { - Mob *entity = itr.second; - if (!entity->IsNPC()) { - continue; - } - - entity_count++; - - std::string entity_name = entity->GetName(); - - /** - * Filter by name - */ - if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { - continue; - } - - std::string saylink = StringFormat( - "#goto %.0f %0.f %.0f", - entity->GetX(), - entity->GetY(), - entity->GetZ() + (entity->IsBoat() ? 50 : 0)); - - c->Message( - 0, - "| %s | ID %5d | %s | x %.0f | y %0.f | z %.0f", - EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), - entity->GetID(), - entity->GetName(), - entity->GetX(), - entity->GetY(), - entity->GetZ() - ); - - found_count++; - } - } - - /** - * Client - */ - if (search_type.find("players") != std::string::npos) { - auto &entity_list_search = entity_list.GetClientList(); - - for (auto &itr : entity_list_search) { - Client *entity = itr.second; - - entity_count++; - - std::string entity_name = entity->GetName(); - - /** - * Filter by name - */ - if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { - continue; - } - - std::string saylink = StringFormat( - "#goto %.0f %0.f %.0f", - entity->GetX(), - entity->GetY(), - entity->GetZ()); - - c->Message( - 0, - "| %s | ID %5d | %s | x %.0f | y %0.f | z %.0f", - EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), - entity->GetID(), - entity->GetName(), - entity->GetX(), - entity->GetY(), - entity->GetZ() - ); - - found_count++; - } - } - - /** - * Corpse - */ - if (search_type.find("corpses") != std::string::npos) { - auto &entity_list_search = entity_list.GetCorpseList(); - - for (auto &itr : entity_list_search) { - Corpse *entity = itr.second; - - entity_count++; - - std::string entity_name = entity->GetName(); - - /** - * Filter by name - */ - if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { - continue; - } - - std::string saylink = StringFormat( - "#goto %.0f %0.f %.0f", - entity->GetX(), - entity->GetY(), - entity->GetZ()); - - c->Message( - 0, - "| %s | ID %5d | %s | x %.0f | y %0.f | z %.0f", - EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), - entity->GetID(), - entity->GetName(), - entity->GetX(), - entity->GetY(), - entity->GetZ() - ); - - found_count++; - } - } - - /** - * Doors - */ - if (search_type.find("doors") != std::string::npos) { - auto &entity_list_search = entity_list.GetDoorsList(); - - for (auto &itr : entity_list_search) { - Doors * entity = itr.second; - - entity_count++; - - std::string entity_name = entity->GetDoorName(); - - /** - * Filter by name - */ - if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { - continue; - } - - std::string saylink = StringFormat( - "#goto %.0f %0.f %.0f", - entity->GetX(), - entity->GetY(), - entity->GetZ()); - - c->Message( - 0, - "| %s | Entity ID %5d | Door ID %i | %s | x %.0f | y %0.f | z %.0f", - EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), - entity->GetID(), - entity->GetDoorID(), - entity->GetDoorName(), - entity->GetX(), - entity->GetY(), - entity->GetZ() - ); - - found_count++; - } - } - - /** - * Objects - */ - if (search_type.find("objects") != std::string::npos) { - auto &entity_list_search = entity_list.GetObjectList(); - - for (auto &itr : entity_list_search) { - Object * entity = itr.second; - - entity_count++; - - std::string entity_name = entity->GetModelName(); - - /** - * Filter by name - */ - if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { - continue; - } - - std::string saylink = StringFormat( - "#goto %.0f %0.f %.0f", - entity->GetX(), - entity->GetY(), - entity->GetZ()); - - c->Message( - 0, - "| %s | Entity ID %5d | Object DBID %i | %s | x %.0f | y %0.f | z %.0f", - EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), - entity->GetID(), - entity->GetDBID(), - entity->GetModelName(), - entity->GetX(), - entity->GetY(), - entity->GetZ() - ); - - found_count++; - } - } - - if (found_count) { - c->Message( - 0, "Found (%i) of type (%s) in zone (%i) total", - found_count, - search_type.c_str(), - entity_count - ); - } - } - else { - c->Message(Chat::White, "Usage of #list"); - c->Message(Chat::White, "- #list [npcs|players|corpses|doors|objects] [search]"); - c->Message(Chat::White, "- Example: #list npcs (Blank for all)"); - } -} - -void command_date(Client *c, const Seperator *sep) -{ - //yyyy mm dd hh mm local - if(sep->arg[3][0]==0 || !sep->IsNumber(1) || !sep->IsNumber(2) || !sep->IsNumber(3)) { - c->Message(Chat::Red, "Usage: #date yyyy mm dd [HH MM]"); - } - else { - int h=0, m=0; - TimeOfDay_Struct eqTime; - zone->zone_time.GetCurrentEQTimeOfDay( time(0), &eqTime); - if(!sep->IsNumber(4)) - h=eqTime.hour; - else - h=atoi(sep->arg[4]); - if(!sep->IsNumber(5)) - m=eqTime.minute; - else - m=atoi(sep->arg[5]); - c->Message(Chat::Red, "Setting world time to %s-%s-%s %i:%i...", sep->arg[1], sep->arg[2], sep->arg[3], h, m); - zone->SetDate(atoi(sep->arg[1]), atoi(sep->arg[2]), atoi(sep->arg[3]), h, m); - } -} - -void command_timezone(Client *c, const Seperator *sep) -{ - if(sep->arg[1][0]==0 && !sep->IsNumber(1)) { - c->Message(Chat::Red, "Usage: #timezone HH [MM]"); - c->Message(Chat::Red, "Current timezone is: %ih %im", zone->zone_time.getEQTimeZoneHr(), zone->zone_time.getEQTimeZoneMin()); - } - else { - uint8 hours = atoi(sep->arg[1]); - uint8 minutes = atoi(sep->arg[2]); - if(!sep->IsNumber(2)) - minutes = 0; - c->Message(Chat::Red, "Setting timezone to %i h %i m", hours, minutes); - uint32 ntz=(hours*60)+minutes; - zone->zone_time.setEQTimeZone(ntz); - content_db.SetZoneTZ(zone->GetZoneID(), zone->GetInstanceVersion(), ntz); - - // Update all clients with new TZ. - auto outapp = new EQApplicationPacket(OP_TimeOfDay, sizeof(TimeOfDay_Struct)); - TimeOfDay_Struct* tod = (TimeOfDay_Struct*)outapp->pBuffer; - zone->zone_time.GetCurrentEQTimeOfDay(time(0), tod); - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } -} - -void command_invul(Client *c, const Seperator *sep) -{ - bool state=atobool(sep->arg[1]); - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient()) - t=c->GetTarget()->CastToClient(); - - if(sep->arg[1][0] != 0) { - t->SetInvul(state); - c->Message(Chat::White, "%s is %s invulnerable from attack.", t->GetName(), state?"now":"no longer"); - } - else - c->Message(Chat::White, "Usage: #invulnerable [on/off]"); -} - -void command_hideme(Client *c, const Seperator *sep) -{ - bool state=atobool(sep->arg[1]); - - if(sep->arg[1][0]==0) - c->Message(Chat::White, "Usage: #hideme [on/off]"); - else - { - c->SetHideMe(state); - c->MessageString(Chat::Broadcasts, c->GetHideMe() ? NOW_INVISIBLE : NOW_VISIBLE, c->GetName()); - } -} - -void command_emote(Client *c, const Seperator *sep) -{ - if (sep->arg[3][0] == 0) - c->Message(Chat::White, "Usage: #emote [name | world | zone] type# message"); - else { - if (strcasecmp(sep->arg[1], "zone") == 0){ - char* newmessage=0; - if(strstr(sep->arg[3],"^")==0) - entity_list.Message(0, atoi(sep->arg[2]), sep->argplus[3]); - else{ - for(newmessage = strtok((char*)sep->arg[3],"^");newmessage!=nullptr;newmessage=strtok(nullptr, "^")) - entity_list.Message(0, atoi(sep->arg[2]), newmessage); - } - } else if (!worldserver.Connected()) { - c->Message(Chat::White, "Error: World server disconnected"); - } else if (!strcasecmp(sep->arg[1], "world")) { - worldserver.SendEmoteMessage( - 0, - 0, - atoi(sep->arg[2]), - sep->argplus[3] - ); - } else { - worldserver.SendEmoteMessage( - sep->arg[1], - 0, - atoi(sep->arg[2]), - sep->argplus[3] - ); - } - } -} - -void command_fov(Client *c, const Seperator *sep) -{ - if (c->GetTarget()) { - auto target = c->GetTarget(); - std::string behind_message = ( - c->BehindMob( - target, - c->GetX(), - c->GetY() - ) ? - "behind" : - "not behind" - ); - std::string gender_message = ( - target->GetGender() == MALE ? - "he" : - ( - target->GetGender() == FEMALE ? - "she" : - "it" - ) - ); - - c->Message( - Chat::White, - fmt::format( - "You are {} {} ({}), {} has a heading of {}.", - behind_message, - target->GetCleanName(), - target->GetID(), - gender_message, - target->GetHeading() - ).c_str() - ); - } else { - c->Message(Chat::White, "You must have a target to use this command."); - } -} - -void command_npcstats(Client *c, const Seperator *sep) -{ - if (c->GetTarget() && c->GetTarget()->IsNPC()) { - NPC* target = c->GetTarget()->CastToNPC(); - - // Stats - target->ShowStats(c); - - // Loot Data - if (target->GetLoottableID()) { - target->QueryLoot(c); - } - } else { - c->Message(Chat::White, "You must target an NPC to use this command."); - } -} - -void command_zclip(Client *c, const Seperator *sep) -{ - // modifys and resends zhdr packet - if(sep->arg[2][0]==0) - c->Message(Chat::White, "Usage: #zclip "); - else if(atoi(sep->arg[1])<=0) - c->Message(Chat::White, "ERROR: Min clip can not be zero or less!"); - else if(atoi(sep->arg[2])<=0) - c->Message(Chat::White, "ERROR: Max clip can not be zero or less!"); - else if(atoi(sep->arg[1])>atoi(sep->arg[2])) - c->Message(Chat::White, "ERROR: Min clip is greater than max clip!"); - else { - zone->newzone_data.minclip = atof(sep->arg[1]); - zone->newzone_data.maxclip = atof(sep->arg[2]); - if(sep->arg[3][0]!=0) - zone->newzone_data.fog_minclip[0]=atof(sep->arg[3]); - if(sep->arg[4][0]!=0) - zone->newzone_data.fog_minclip[1]=atof(sep->arg[4]); - if(sep->arg[5][0]!=0) - zone->newzone_data.fog_maxclip[0]=atof(sep->arg[5]); - if(sep->arg[6][0]!=0) - zone->newzone_data.fog_maxclip[1]=atof(sep->arg[6]); - auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); - memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } -} - -void command_npccast(Client *c, const Seperator *sep) -{ - if (c->GetTarget() && c->GetTarget()->IsNPC()) { - NPC* target = c->GetTarget()->CastToNPC(); - if (!sep->IsNumber(1) && sep->arg[1] && sep->IsNumber(2)) { - const char* entity_name = sep->arg[1] ? sep->arg[1] : 0; - auto spell_id = sep->arg[2] ? std::stoul(sep->arg[2]) : 0; - Mob* spell_target = entity_list.GetMob(entity_name); - if (spell_target && IsValidSpell(spell_id) && spell_id < SPDAT_RECORDS) { - c->Message( - Chat::White, - fmt::format( - "{} ({}) casting {} ({}) on {} ({}).", - target->GetCleanName(), - target->GetID(), - GetSpellName(static_cast(spell_id)), - spell_id, - spell_target->GetCleanName(), - spell_target->GetID() - ).c_str() - ); - - target->CastSpell(spell_id, spell_target->GetID()); - } else { - if (!spell_target) { - c->Message( - Chat::White, - fmt::format( - "Entity {} was not found", - entity_name - ).c_str() - ); - } else if (!spell_id || !IsValidSpell(spell_id)) { - c->Message( - Chat::White, - fmt::format( - "Spell ID {} was not found", - spell_id - ).c_str() - ); - } - } - } else if (sep->IsNumber(1) && sep->IsNumber(2) ) { - uint16 entity_id = sep->arg[1] ? std::stoul(sep->arg[1]) : 0; - auto spell_id = sep->arg[2] ? std::stoul(sep->arg[2]) : 0; - Mob* spell_target = entity_list.GetMob(entity_id); - if (spell_target && IsValidSpell(spell_id) && spell_id < SPDAT_RECORDS) { - c->Message( - Chat::White, - fmt::format( - "{} ({}) casting {} ({}) on {} ({}).", - target->GetCleanName(), - target->GetID(), - GetSpellName(static_cast(spell_id)), - spell_id, - spell_target->GetCleanName(), - spell_target->GetID() - ).c_str() - ); - - target->CastSpell(spell_id, spell_target->GetID()); - } else { - if (!spell_target) { - c->Message( - Chat::White, - fmt::format( - "Entity ID {} was not found", - entity_id - ).c_str() - ); - } else if (!spell_id || !IsValidSpell(spell_id)) { - c->Message( - Chat::White, - fmt::format( - "Spell ID {} was not found", - spell_id - ).c_str() - ); - } - } - } - } else { - c->Message(Chat::White, "You must target an NPC to use this command."); - } -} - -void command_zstats(Client *c, const Seperator *sep) -{ - // Zone - c->Message( - Chat::White, - fmt::format( - "Zone | ID: {} Instance ID: {} Name: {} ({})", - zone->GetZoneID(), - zone->GetInstanceID(), - zone->GetLongName(), - zone->GetShortName() - ).c_str() - ); - - // Type - c->Message( - Chat::White, - fmt::format( - "Type: {}", - zone->newzone_data.ztype - ).c_str() - ); - - // Fog Data - for (int fog_index = 0; fog_index < 4; fog_index++) { - int fog_number = (fog_index + 1); - c->Message( - Chat::White, - fmt::format( - "Fog {} Colors | Red: {} Blue: {} Green: {} ", - fog_number, - zone->newzone_data.fog_red[fog_index], - zone->newzone_data.fog_green[fog_index], - zone->newzone_data.fog_blue[fog_index] - ).c_str() - ); - - c->Message( - Chat::White, - fmt::format( - "Fog {} Clipping Distance | Min: {} Max: {}", - fog_number, - zone->newzone_data.fog_minclip[fog_index], - zone->newzone_data.fog_maxclip[fog_index] - ).c_str() - ); - } - - // Fog Density - c->Message( - Chat::White, - fmt::format( - "Fog Density: {}", - zone->newzone_data.fog_density - ).c_str() - ); - - - // Gravity - c->Message( - Chat::White, - fmt::format( - "Gravity: {}", - zone->newzone_data.gravity - ).c_str() - ); - - // Time Type - c->Message( - Chat::White, - fmt::format( - "Time Type: {}", - zone->newzone_data.time_type - ).c_str() - ); - - // Experience Multiplier - c->Message( - Chat::White, - fmt::format( - "Experience Multiplier: {}", - zone->newzone_data.zone_exp_multiplier - ).c_str() - ); - - // Safe Coordinates - c->Message( - Chat::White, - fmt::format( - "Safe Coordinates: {}, {}, {}", - zone->newzone_data.safe_x, - zone->newzone_data.safe_y, - zone->newzone_data.safe_z - ).c_str() - ); - - // Max Z - c->Message( - Chat::White, - fmt::format( - "Max Z: {}", - zone->newzone_data.max_z - ).c_str() - ); - - // Underworld Z - c->Message( - Chat::White, - fmt::format( - "Underworld Z: {}", - zone->newzone_data.underworld - ).c_str() - ); - - // Clipping Distance - c->Message( - Chat::White, - fmt::format( - "Clipping Distance | Min: {} Max: {}", - zone->newzone_data.minclip, - zone->newzone_data.maxclip - ).c_str() - ); - - // Weather Data - for (int weather_index = 0; weather_index < 4; weather_index++) { - int weather_number = (weather_index + 1); - c->Message( - Chat::White, - fmt::format( - "Rain {} | Chance: {} Duration: {} ", - weather_number, - zone->newzone_data.rain_chance[weather_index], - zone->newzone_data.rain_duration[weather_index] - ).c_str() - ); - - c->Message( - Chat::White, - fmt::format( - "Snow {} | Chance: {} Duration: {}", - weather_number, - zone->newzone_data.snow_chance[weather_index], - zone->newzone_data.snow_duration[weather_index] - ).c_str() - ); - } - - // Sky Type - c->Message( - Chat::White, - fmt::format( - "Sky Type: {}", - zone->newzone_data.sky - ).c_str() - ); - - // Suspend Buffs - c->Message( - Chat::White, - fmt::format( - "Suspend Buffs: {}", - zone->newzone_data.SuspendBuffs - ).c_str() - ); - - // Regeneration Data - c->Message( - Chat::White, - fmt::format( - "Regen | Health: {} Mana: {} Endurance: {}", - zone->newzone_data.FastRegenHP, - zone->newzone_data.FastRegenMana, - zone->newzone_data.FastRegenEndurance - ).c_str() - ); - - // NPC Max Aggro Distance - c->Message( - Chat::White, - fmt::format( - "NPC Max Aggro Distance: {}", - zone->newzone_data.NPCAggroMaxDist - ).c_str() - ); - - // Underworld Teleport Index - c->Message( - Chat::White, - fmt::format( - "Underworld Teleport Index: {}", - zone->newzone_data.underworld_teleport_index - ).c_str() - ); - - // Lava Damage - c->Message( - Chat::White, - fmt::format( - "Lava Damage | Min: {} Max: {}", - zone->newzone_data.MinLavaDamage, - zone->newzone_data.LavaDamage - ).c_str() - ); -} - -void command_permaclass(Client *c, const Seperator *sep) -{ - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient()) - t=c->GetTarget()->CastToClient(); - - if(sep->arg[1][0]==0) { - c->Message(Chat::White,"Usage: #permaclass "); - } - else if(!t->IsClient()) - c->Message(Chat::White,"Target is not a client."); - else { - c->Message(Chat::White, "Setting %s's class...Sending to char select.", t->GetName()); - LogInfo("Class change request from [{}] for [{}], requested class:[{}]", c->GetName(), t->GetName(), atoi(sep->arg[1]) ); - t->SetBaseClass(atoi(sep->arg[1])); - t->Save(); - t->Kick("Class was changed."); - } -} - -void command_permarace(Client *c, const Seperator *sep) -{ - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient()) - t=c->GetTarget()->CastToClient(); - - if(sep->arg[1][0]==0) { - c->Message(Chat::White,"Usage: #permarace "); - c->Message(Chat::White,"NOTE: Not all models are global. If a model is not global, it will appear as a human on character select and in zones without the model."); - } - else if(!t->IsClient()) - c->Message(Chat::White,"Target is not a client."); - else { - c->Message(Chat::White, "Setting %s's race - zone to take effect", t->GetName()); - LogInfo("Permanant race change request from [{}] for [{}], requested race:[{}]", c->GetName(), t->GetName(), atoi(sep->arg[1]) ); - uint32 tmp = Mob::GetDefaultGender(atoi(sep->arg[1]), t->GetBaseGender()); - t->SetBaseRace(atoi(sep->arg[1])); - t->SetBaseGender(tmp); - t->Save(); - t->SendIllusionPacket(atoi(sep->arg[1])); - } -} - -void command_permagender(Client *c, const Seperator *sep) -{ - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient()) - t=c->GetTarget()->CastToClient(); - - if(sep->arg[1][0]==0) { - c->Message(Chat::White,"Usage: #permagender "); - c->Message(Chat::White,"Gender Numbers: 0=Male, 1=Female, 2=Neuter"); - } - else if(!t->IsClient()) - c->Message(Chat::White,"Target is not a client."); - else { - c->Message(Chat::White, "Setting %s's gender - zone to take effect", t->GetName()); - LogInfo("Permanant gender change request from [{}] for [{}], requested gender:[{}]", c->GetName(), t->GetName(), atoi(sep->arg[1]) ); - t->SetBaseGender(atoi(sep->arg[1])); - t->Save(); - t->SendIllusionPacket(atoi(sep->arg[1])); - } -} - -void command_weather(Client *c, const Seperator *sep) -{ - if (!(sep->arg[1][0] == '0' || sep->arg[1][0] == '1' || sep->arg[1][0] == '2' || sep->arg[1][0] == '3')) { - c->Message(Chat::White, "Usage: #weather <0/1/2/3> - Off/Rain/Snow/Manual."); - } - else if(zone->zone_weather == 0) { - if(sep->arg[1][0] == '3') { // Put in modifications here because it had a very good chance at screwing up the client's weather system if rain was sent during snow -T7 - if(sep->arg[2][0] != 0 && sep->arg[3][0] != 0) { - c->Message(Chat::White, "Sending weather packet... TYPE=%s, INTENSITY=%s", sep->arg[2], sep->arg[3]); - zone->zone_weather = atoi(sep->arg[2]); - auto outapp = new EQApplicationPacket(OP_Weather, 8); - outapp->pBuffer[0] = atoi(sep->arg[2]); - outapp->pBuffer[4] = atoi(sep->arg[3]); // This number changes in the packets, intensity? - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } - else { - c->Message(Chat::White, "Manual Usage: #weather 3 "); - } - } - else if(sep->arg[1][0] == '2') { - entity_list.Message(0, 0, "Snowflakes begin to fall from the sky."); - zone->zone_weather = 2; - auto outapp = new EQApplicationPacket(OP_Weather, 8); - outapp->pBuffer[0] = 0x01; - outapp->pBuffer[4] = 0x02; // This number changes in the packets, intensity? - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } - else if(sep->arg[1][0] == '1') { - entity_list.Message(0, 0, "Raindrops begin to fall from the sky."); - zone->zone_weather = 1; - auto outapp = new EQApplicationPacket(OP_Weather, 8); - outapp->pBuffer[4] = 0x01; // This is how it's done in Fear, and you can see a decent distance with it at this value - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } - } - else { - if(zone->zone_weather == 1) { // Doing this because if you have rain/snow on, you can only turn one off. - entity_list.Message(0, 0, "The sky clears as the rain ceases to fall."); - zone->zone_weather = 0; - auto outapp = new EQApplicationPacket(OP_Weather, 8); - // To shutoff weather you send an empty 8 byte packet (You get this everytime you zone even if the sky is clear) - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } - else if(zone->zone_weather == 2) { - entity_list.Message(0, 0, "The sky clears as the snow stops falling."); - zone->zone_weather = 0; - auto outapp = new EQApplicationPacket(OP_Weather, 8); - // To shutoff weather you send an empty 8 byte packet (You get this everytime you zone even if the sky is clear) - outapp->pBuffer[0] = 0x01; // Snow has it's own shutoff packet - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } - else { - entity_list.Message(0, 0, "The sky clears."); - zone->zone_weather = 0; - auto outapp = new EQApplicationPacket(OP_Weather, 8); - // To shutoff weather you send an empty 8 byte packet (You get this everytime you zone even if the sky is clear) - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } - } -} - -void command_zheader(Client *c, const Seperator *sep) -{ - // sends zhdr packet - if(sep->arg[1][0]==0) { - c->Message(Chat::White, "Usage: #zheader "); - } - else if(ZoneID(sep->argplus[1])==0) - c->Message(Chat::White, "Invalid Zone Name: %s", sep->argplus[1]); - else { - - if (zone->LoadZoneCFG(sep->argplus[1], 0)) - c->Message(Chat::White, "Successfully loaded zone header for %s from database.", sep->argplus[1]); - else - c->Message(Chat::White, "Failed to load zone header %s from database", sep->argplus[1]); - auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); - memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } -} - -void command_zsky(Client *c, const Seperator *sep) -{ - // modifys and resends zhdr packet - if(sep->arg[1][0]==0) - c->Message(Chat::White, "Usage: #zsky "); - else if(atoi(sep->arg[1])<0||atoi(sep->arg[1])>255) - c->Message(Chat::White, "ERROR: Sky type can not be less than 0 or greater than 255!"); - else { - zone->newzone_data.sky = atoi(sep->arg[1]); - auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); - memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } -} - -void command_zcolor(Client *c, const Seperator *sep) -{ - // modifys and resends zhdr packet - if (sep->arg[3][0]==0) - c->Message(Chat::White, "Usage: #zcolor "); - else if (atoi(sep->arg[1])<0||atoi(sep->arg[1])>255) - c->Message(Chat::White, "ERROR: Red can not be less than 0 or greater than 255!"); - else if (atoi(sep->arg[2])<0||atoi(sep->arg[2])>255) - c->Message(Chat::White, "ERROR: Green can not be less than 0 or greater than 255!"); - else if (atoi(sep->arg[3])<0||atoi(sep->arg[3])>255) - c->Message(Chat::White, "ERROR: Blue can not be less than 0 or greater than 255!"); - else { - for (int z=0; z<4; z++) { - zone->newzone_data.fog_red[z] = atoi(sep->arg[1]); - zone->newzone_data.fog_green[z] = atoi(sep->arg[2]); - zone->newzone_data.fog_blue[z] = atoi(sep->arg[3]); - } - auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); - memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } -} - -void command_gassign(Client *c, const Seperator *sep) -{ - if (sep->IsNumber(1) && c->GetTarget() && c->GetTarget()->IsNPC() && c->GetTarget()->CastToNPC()->GetSpawnPointID() > 0) { - int spawn2id = c->GetTarget()->CastToNPC()->GetSpawnPointID(); - database.AssignGrid(c, atoi(sep->arg[1]), spawn2id); - } - else - c->Message(Chat::White, "Usage: #gassign [num] - must have an npc target!"); -} - -void command_ai(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - - if (strcasecmp(sep->arg[1], "factionid") == 0) { - if (target && sep->IsNumber(2)) { - if (target->IsNPC()) - target->CastToNPC()->SetNPCFactionID(atoi(sep->arg[2])); - else - c->Message(Chat::White, "%s is not an NPC.", target->GetName()); - } - else - c->Message(Chat::White, "Usage: (targeted) #ai factionid [factionid]"); - } - else if (strcasecmp(sep->arg[1], "spellslist") == 0) { - if (target && sep->IsNumber(2) && atoi(sep->arg[2]) >= 0) { - if (target->IsNPC()) - target->CastToNPC()->AI_AddNPCSpells(atoi(sep->arg[2])); - else - c->Message(Chat::White, "%s is not an NPC.", target->GetName()); - } - else - c->Message(Chat::White, "Usage: (targeted) #ai spellslist [npc_spells_id]"); - } - else if (strcasecmp(sep->arg[1], "con") == 0) { - if (target && sep->arg[2][0] != 0) { - Mob* tar2 = entity_list.GetMob(sep->arg[2]); - if (tar2) - c->Message(Chat::White, "%s considering %s: %i", target->GetName(), tar2->GetName(), tar2->GetReverseFactionCon(target)); - else - c->Message(Chat::White, "Error: %s not found.", sep->arg[2]); - } - else - c->Message(Chat::White, "Usage: (targeted) #ai con [mob name]"); - } - else if (strcasecmp(sep->arg[1], "guard") == 0) { - if (target && target->IsNPC()) - target->CastToNPC()->SaveGuardSpot(target->GetPosition()); - else - c->Message(Chat::White, "Usage: (targeted) #ai guard - sets npc to guard the current location (use #summon to move)"); - } - else if (strcasecmp(sep->arg[1], "roambox") == 0) { - if (target && target->IsAIControlled() && target->IsNPC()) { - if ((sep->argnum == 6 || sep->argnum == 7 || sep->argnum == 8) && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4) && sep->IsNumber(5) && sep->IsNumber(6)) { - uint32 tmp = 2500; - uint32 tmp2 = 2500; - if (sep->IsNumber(7)) - tmp = atoi(sep->arg[7]); - if (sep->IsNumber(8)) - tmp2 = atoi(sep->arg[8]); - target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), atof(sep->arg[4]), atof(sep->arg[5]), atof(sep->arg[6]), tmp, tmp2); - } - else if ((sep->argnum == 3 || sep->argnum == 4) && sep->IsNumber(2) && sep->IsNumber(3)) { - uint32 tmp = 2500; - uint32 tmp2 = 2500; - if (sep->IsNumber(4)) - tmp = atoi(sep->arg[4]); - if (sep->IsNumber(5)) - tmp2 = atoi(sep->arg[5]); - target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), tmp, tmp2); - } - else { - c->Message(Chat::White, "Usage: #ai roambox dist max_x min_x max_y min_y [delay] [mindelay]"); - c->Message(Chat::White, "Usage: #ai roambox dist roamdist [delay] [mindelay]"); - } - } - else - c->Message(Chat::White, "You need a AI NPC targeted"); - } - else if (strcasecmp(sep->arg[1], "stop") == 0 && c->Admin() >= commandToggleAI) { - if (target) { - if (target->IsAIControlled()) - target->AI_Stop(); - else - c->Message(Chat::White, "Error: Target is not AI controlled"); - } - else - c->Message(Chat::White, "Usage: Target a Mob with AI enabled and use this to turn off their AI."); - } - else if (strcasecmp(sep->arg[1], "start") == 0 && c->Admin() >= commandToggleAI) { - if (target) { - if (!target->IsAIControlled()) - target->AI_Start(); - else - c->Message(Chat::White, "Error: Target is already AI controlled"); - } - else - c->Message(Chat::White, "Usage: Target a Mob with AI disabled and use this to turn on their AI."); - } - else { - c->Message(Chat::White, "#AI Sub-commands"); - c->Message(Chat::White, " factionid"); - c->Message(Chat::White, " spellslist"); - c->Message(Chat::White, " con"); - c->Message(Chat::White, " guard"); - } -} - -void command_worldshutdown(Client *c, const Seperator *sep) -{ - // GM command to shutdown world server and all zone servers - uint32 time = 0; - uint32 interval = 0; - if (worldserver.Connected()) { - if ( - sep->IsNumber(1) && - sep->IsNumber(2) && - (time = std::stoi(sep->arg[1]) > 0) && - (interval = std::stoi(sep->arg[2]) > 0) - ) { - int time_minutes = (time / 60); - quest_manager.WorldWideMessage( - Chat::Yellow, - fmt::format( - "[SYSTEM] World will be shutting down in {} minutes.", - time_minutes - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "World will be shutting down in {} minutes, notifying every {} seconds", - time_minutes, - interval - ).c_str() - ); - auto pack = new ServerPacket(ServerOP_ShutdownAll, sizeof(WorldShutDown_Struct)); - WorldShutDown_Struct* wsd = (WorldShutDown_Struct*)pack->pBuffer; - wsd->time = (time * 1000); - wsd->interval = (interval * 1000); - worldserver.SendPacket(pack); - safe_delete(pack); - } else if (!strcasecmp(sep->arg[1], "now")){ - quest_manager.WorldWideMessage( - Chat::Yellow, - "[SYSTEM] World is shutting down now." - ); - c->Message(Chat::White, "World is shutting down now."); - auto pack = new ServerPacket; - pack->opcode = ServerOP_ShutdownAll; - pack->size = 0; - worldserver.SendPacket(pack); - safe_delete(pack); - } else if (!strcasecmp(sep->arg[1], "disable")) { - c->Message(Chat::White, "World shutdown has been aborted."); - auto pack = new ServerPacket(ServerOP_ShutdownAll, sizeof(WorldShutDown_Struct)); - WorldShutDown_Struct* wsd = (WorldShutDown_Struct*)pack->pBuffer; - wsd->time = 0; - wsd->interval = 0; - worldserver.SendPacket(pack); - safe_delete(pack); - } else { - c->Message(Chat::White,"#worldshutdown - Shuts down the server and all zones."); - c->Message(Chat::White,"Usage: #worldshutdown now - Shuts down the server and all zones immediately."); - c->Message(Chat::White,"Usage: #worldshutdown disable - Stops the server from a previously scheduled shut down."); - c->Message(Chat::White,"Usage: #worldshutdown [timer] [interval] - Shuts down the server and all zones after [timer] seconds and notifies players every [interval] seconds."); - } - } else { - c->Message(Chat::White, "Error: World server is disconnected."); - } -} - -void command_sendzonespawns(Client *c, const Seperator *sep) -{ - entity_list.SendZoneSpawns(c); -} - -void command_zsave(Client *c, const Seperator *sep) -{ - if (zone->SaveZoneCFG()) { - c->Message(Chat::Red, "Zone header saved successfully."); - } - else { - c->Message(Chat::Red, "ERROR: Zone header data was NOT saved."); - } -} - -void command_dbspawn2(Client *c, const Seperator *sep) -{ - - if (sep->IsNumber(1) && sep->IsNumber(2) && sep->IsNumber(3)) { - LogInfo("Spawning database spawn"); - uint16 cond = 0; - int16 cond_min = 0; - if(sep->IsNumber(4)) { - cond = atoi(sep->arg[4]); - if(sep->IsNumber(5)) - cond_min = atoi(sep->arg[5]); - } - database.CreateSpawn2(c, atoi(sep->arg[1]), zone->GetShortName(), c->GetPosition(), atoi(sep->arg[2]), atoi(sep->arg[3]), cond, cond_min); - } - else { - c->Message(Chat::White, "Usage: #dbspawn2 spawngroup respawn variance [condition_id] [condition_min]"); - } -} - -void command_shutdown(Client *c, const Seperator *sep) -{ - CatchSignal(2); -} - -void command_delacct(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0] == 0) - c->Message(Chat::White, "Format: #delacct accountname"); - else { - std::string user; - std::string loginserver; - ParseAccountString(sep->arg[1], user, loginserver); - - if (database.DeleteAccount(user.c_str(), loginserver.c_str())) - c->Message(Chat::White, "The account was deleted."); - else - c->Message(Chat::White, "Unable to delete account."); - } -} - -void command_setpass(Client *c, const Seperator *sep) -{ - if(sep->argnum != 2) - c->Message(Chat::White, "Format: #setpass accountname password"); - else { - std::string user; - std::string loginserver; - ParseAccountString(sep->arg[1], user, loginserver); - - int16 tmpstatus = 0; - uint32 tmpid = database.GetAccountIDByName(user.c_str(), loginserver.c_str(), &tmpstatus); - if (!tmpid) - c->Message(Chat::White, "Error: Account not found"); - else if (tmpstatus > c->Admin()) - c->Message(Chat::White, "Cannot change password: Account's status is higher than yours"); - else if (database.SetLocalPassword(tmpid, sep->arg[2])) - c->Message(Chat::White, "Password changed."); - else - c->Message(Chat::White, "Error changing password."); - } -} - -void command_setlsinfo(Client *c, const Seperator *sep) -{ - if(sep->argnum != 2) - c->Message(Chat::White, "Format: #setlsinfo email password"); - else { - auto pack = new ServerPacket(ServerOP_LSAccountUpdate, sizeof(ServerLSAccountUpdate_Struct)); - ServerLSAccountUpdate_Struct* s = (ServerLSAccountUpdate_Struct *) pack->pBuffer; - s->useraccountid = c->LSAccountID(); - strn0cpy(s->useraccount, c->AccountName(), 30); - strn0cpy(s->user_email, sep->arg[1], 100); - strn0cpy(s->userpassword, sep->arg[2], 50); - worldserver.SendPacket(pack); - c->Message(Chat::White, "Login Server update packet sent."); - } -} - -void command_grid(Client *c, const Seperator *sep) -{ - auto command_type = sep->arg[1]; - auto zone_id = zone->GetZoneID(); - if (strcasecmp("max", command_type) == 0) { - c->Message( - Chat::White, - fmt::format( - "Highest grid ID in this zone is {}.", - content_db.GetHighestGrid(zone_id) - ).c_str() - ); - } else if (strcasecmp("add", command_type) == 0) { - auto grid_id = atoi(sep->arg[2]); - auto wander_type = atoi(sep->arg[3]); - auto pause_type = atoi(sep->arg[4]); - if (!content_db.GridExistsInZone(zone_id, grid_id)) { - content_db.ModifyGrid(c, false, grid_id, wander_type, pause_type, zone_id); - c->Message( - Chat::White, - fmt::format( - "Grid {} added to zone ID {} with wander type {} and pause type {}.", - grid_id, - zone_id, - wander_type, - pause_type - ).c_str() - ); - } else { - c->Message( - Chat::White, - fmt::format( - "Grid {} already exists in zone ID {}.", - grid_id, - zone_id - ).c_str() - ); - return; - } - } else if (strcasecmp("show", command_type) == 0) { - Mob *target = c->GetTarget(); - if (!target || !target->IsNPC()) { - c->Message(Chat::White, "You need to target an NPC!"); - return; - } - - auto grid_id = target->CastToNPC()->GetGrid(); - std::string query = fmt::format( - "SELECT `x`, `y`, `z`, `heading`, `number` " - "FROM `grid_entries` " - "WHERE `zoneid` = {} AND `gridid` = {} " - "ORDER BY `number`", - zone_id, - grid_id - ); - - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Error querying database."); - c->Message(Chat::White, query.c_str()); - } - - if (results.RowCount() == 0) { - c->Message(Chat::White, "No grid found."); - return; - } - - // Depop any node npc's already spawned - entity_list.DespawnGridNodes(grid_id); - - // Spawn grid nodes - std::map, int32> zoffset; - for (auto row : results) { - glm::vec4 node_position = glm::vec4(atof(row[0]), atof(row[1]), atof(row[2]), atof(row[3])); - std::vector node_loc { - node_position.x, - node_position.y, - node_position.z - }; - - // If we already have a node at this location, set the z offset - // higher from the existing one so we can see it. Adjust so if - // there is another at the same spot we adjust again. - auto search = zoffset.find(node_loc); - if (search != zoffset.end()) { - search->second = search->second + 3; - } else { - zoffset[node_loc] = 0.0; - } - - node_position.z += zoffset[node_loc]; - NPC::SpawnGridNodeNPC(node_position, grid_id, atoi(row[4]), zoffset[node_loc]); - } - c->Message( - Chat::White, - fmt::format( - "Spawning nodes for grid {}.", - grid_id - ).c_str() - ); - } else if (strcasecmp("hide", command_type) == 0) { - Mob* target = c->GetTarget(); - if (!target || !target->IsNPC()) { - c->Message(Chat::White, "You need to target an NPC!"); - return; - } - - auto grid_id = target->CastToNPC()->GetGrid(); - entity_list.DespawnGridNodes(grid_id); - c->Message( - Chat::White, - fmt::format( - "Depawning nodes for grid {}.", - grid_id - ).c_str() - ); - } else if (strcasecmp("delete", command_type) == 0) { - auto grid_id = atoi(sep->arg[2]); - content_db.ModifyGrid(c, true, grid_id, 0, 0, zone_id); - c->Message( - Chat::White, - fmt::format( - "Grid {} deleted from zone ID {}.", - grid_id, - zone_id - ).c_str() - ); - } else { - c->Message(Chat::White, "Usage: #grid [add|delete] [grid_id] [wander_type] [pause_type]"); - c->Message(Chat::White, "Usage: #grid [max] - displays the highest grid ID used in this zone (for add)"); - c->Message(Chat::White, "Usage: #grid [show] - displays wp nodes as boxes"); - } -} - -void command_wp(Client *c, const Seperator *sep) -{ - auto command_type = sep->arg[1]; - auto grid_id = atoi(sep->arg[2]); - if (grid_id != 0) { - auto pause = atoi(sep->arg[3]); - auto waypoint = atoi(sep->arg[4]); - auto zone_id = zone->GetZoneID(); - if (strcasecmp("add", command_type) == 0) { - if (waypoint == 0) { // Default to highest if it's left blank, or we enter 0 - waypoint = (content_db.GetHighestWaypoint(zone_id, grid_id) + 1); - } - - if (strcasecmp("-h", sep->arg[5]) == 0) { - content_db.AddWP(c, grid_id, waypoint, c->GetPosition(), pause, zone_id); - } else { - auto position = c->GetPosition(); - position.w = -1; - content_db.AddWP(c, grid_id, waypoint, position, pause, zone_id); - } - c->Message( - Chat::White, - fmt::format( - "Waypoint {} added to grid {} with a pause of {} {}.", - waypoint, - grid_id, - pause, - (pause == 1 ? "second" : "seconds") - ).c_str() - ); - } else if (strcasecmp("delete", command_type) == 0) { - content_db.DeleteWaypoint(c, grid_id, waypoint, zone_id); - c->Message( - Chat::White, - fmt::format( - "Waypoint {} deleted from grid {}.", - waypoint, - grid_id - ).c_str() - ); - } - } else { - c->Message(Chat::White,"Usage: #wp [add|delete] [grid_id] [pause] [waypoint_id] [-h]"); - } -} - -void command_iplookup(Client *c, const Seperator *sep) -{ - auto pack = - new ServerPacket(ServerOP_IPLookup, sizeof(ServerGenericWorldQuery_Struct) + strlen(sep->argplus[1]) + 1); - ServerGenericWorldQuery_Struct* s = (ServerGenericWorldQuery_Struct *) pack->pBuffer; - strcpy(s->from, c->GetName()); - s->admin = c->Admin(); - if (sep->argplus[1][0] != 0) - strcpy(s->query, sep->argplus[1]); - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void command_size(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - if (!sep->IsNumber(1)) - c->Message(Chat::White, "Usage: #size [0 - 255] (Decimal increments are allowed)"); - else { - float newsize = atof(sep->arg[1]); - if (newsize > 255) - c->Message(Chat::White, "Error: #size: Size can not be greater than 255."); - else if (newsize < 0) - c->Message(Chat::White, "Error: #size: Size can not be less than 0."); - else if (!target) - c->Message(Chat::White,"Error: this command requires a target"); - else { - uint16 Race = target->GetModel(); - uint8 Gender = target->GetGender(); - uint8 Texture = 0xFF; - uint8 HelmTexture = 0xFF; - uint8 HairColor = target->GetHairColor(); - uint8 BeardColor = target->GetBeardColor(); - uint8 EyeColor1 = target->GetEyeColor1(); - uint8 EyeColor2 = target->GetEyeColor2(); - uint8 HairStyle = target->GetHairStyle(); - uint8 LuclinFace = target->GetLuclinFace(); - uint8 Beard = target->GetBeard(); - uint32 DrakkinHeritage = target->GetDrakkinHeritage(); - uint32 DrakkinTattoo = target->GetDrakkinTattoo(); - uint32 DrakkinDetails = target->GetDrakkinDetails(); - - target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, - EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, - DrakkinHeritage, DrakkinTattoo, DrakkinDetails, newsize); - - c->Message(Chat::White,"Size = %f", atof(sep->arg[1])); - } - } -} - -void command_mana(Client *c, const Seperator *sep) -{ - auto target = c->GetTarget() ? c->GetTarget() : c; - if(target->IsClient()) { - target->CastToClient()->SetMana(target->CastToClient()->CalcMaxMana()); - } else { - target->SetMana(target->CalcMaxMana()); - } - - if (c != target) { - c->Message( - Chat::White, - fmt::format( - "Set {} ({}) to full Mana.", - target->GetCleanName(), - target->GetID() - ).c_str() - ); - } else { - c->Message(Chat::White, "Restored your Mana to full."); - } -} - -void command_flymode(Client *c, const Seperator *sep) -{ - Mob *t = c; - - if (strlen(sep->arg[1]) == 1 && sep->IsNumber(1) && atoi(sep->arg[1]) >= 0 && atoi(sep->arg[1]) <= 5) { - if (c->GetTarget()) { - t = c->GetTarget(); - } - - int fm = atoi(sep->arg[1]); - - t->SetFlyMode(static_cast(fm)); - t->SendAppearancePacket(AT_Levitate, fm); - if (sep->arg[1][0] == '0') { - c->Message(Chat::White, "Setting %s to Grounded", t->GetName()); - } - else if (sep->arg[1][0] == '1') { - c->Message(Chat::White, "Setting %s to Flying", t->GetName()); - } - else if (sep->arg[1][0] == '2') { - c->Message(Chat::White, "Setting %s to Levitating", t->GetName()); - } - else if (sep->arg[1][0] == '3') { - c->Message(Chat::White, "Setting %s to In Water", t->GetName()); - } - else if (sep->arg[1][0] == '4') { - c->Message(Chat::White, "Setting %s to Floating(Boat)", t->GetName()); - } - else if (sep->arg[1][0] == '5') { - c->Message(Chat::White, "Setting %s to Levitating While Running", t->GetName()); - } - } else { - c->Message(Chat::White, "#flymode [0/1/2/3/4/5]"); - } -} - - -void command_showskills(Client *c, const Seperator *sep) -{ - Client *target = c; - if (c->GetTarget() && c->GetTarget()->IsClient()) { - target = c->GetTarget()->CastToClient(); - } - - bool show_all = false; - - if (!strcasecmp("all", sep->arg[1])) { - show_all = true; - } - - c->Message( - Chat::White, - fmt::format( - "Skills | Name: {}", - target->GetCleanName() - ).c_str() - ); - - for ( - EQ::skills::SkillType skill_type = EQ::skills::Skill1HBlunt; - skill_type <= EQ::skills::HIGHEST_SKILL; - skill_type = (EQ::skills::SkillType)(skill_type + 1) - ) { - if (show_all || (target->CanHaveSkill(skill_type) && target->MaxSkill(skill_type))) { - c->Message( - Chat::White, - fmt::format( - "{} ({}) | Current: {} Max: {} Raw: {}", - EQ::skills::GetSkillName(skill_type), - skill_type, - target->GetSkill(skill_type), - target->MaxSkill(skill_type), - target->GetRawSkill(skill_type) - ).c_str() - ); - } - } -} - -void command_findclass(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - - if (arguments == 0) { - c->Message(Chat::White, "Command Syntax: #findclass [search criteria]"); - return; - } - - if (sep->IsNumber(1)) { - int class_id = std::stoi(sep->arg[1]); - if (class_id >= WARRIOR && class_id <= MERCERNARY_MASTER) { - std::string class_name = GetClassIDName(class_id); - c->Message( - Chat::White, - fmt::format( - "Class {}: {}", - class_id, - class_name - ).c_str() - ); - } else { - c->Message( - Chat::White, - fmt::format( - "Class ID {} was not found.", - class_id - ).c_str() - ); - } - } else { - std::string search_criteria = str_tolower(sep->argplus[1]); - int found_count = 0; - for (int class_id = WARRIOR; class_id <= MERCERNARY_MASTER; class_id++) { - std::string class_name = GetClassIDName(class_id); - std::string class_name_lower = str_tolower(class_name); - if (search_criteria.length() > 0 && class_name_lower.find(search_criteria) == std::string::npos) { - continue; - } - - c->Message( - Chat::White, - fmt::format( - "Class {}: {}", - class_id, - class_name - ).c_str() - ); - found_count++; - - if (found_count == 20) { - break; - } - } - - if (found_count == 20) { - c->Message(Chat::White, "20 Classes found... max reached."); - } else { - auto class_message = ( - found_count > 0 ? - ( - found_count == 1 ? - "A Class was" : - fmt::format("{} Classes were", found_count) - ) : - "No Classes were" - ); - - c->Message( - Chat::White, - fmt::format( - "{} found.", - class_message - ).c_str() - ); - } - } -} - -void command_findfaction(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - - if (arguments == 0) { - c->Message(Chat::White, "Command Syntax: #findfaction [search criteria]"); - return; - } - - if (sep->IsNumber(1)) { - int faction_id = std::stoi(sep->arg[1]); - auto faction_name = content_db.GetFactionName(faction_id); - if (!faction_name.empty()) { - c->Message( - Chat::White, - fmt::format( - "Faction {}: {}", - faction_id, - faction_name - ).c_str() - ); - } else { - c->Message( - Chat::White, - fmt::format( - "Faction ID {} was not found.", - faction_id - ).c_str() - ); - } - } else { - std::string search_criteria = str_tolower(sep->argplus[1]); - int found_count = 0; - int max_faction_id = content_db.GetMaxFaction(); - for (int faction_id = 0; faction_id < max_faction_id; faction_id++) { - std::string faction_name = content_db.GetFactionName(faction_id); - std::string faction_name_lower = str_tolower(faction_name); - if (faction_name.empty()) { - continue; - } - - if (faction_name.find(search_criteria) == std::string::npos) { - continue; - } - - c->Message( - Chat::White, - fmt::format( - "Faction {}: {}", - faction_id, - faction_name - ).c_str() - ); - found_count++; - - if (found_count == 20) { - break; - } - } - - if (found_count == 20) { - c->Message(Chat::White, "20 Factions found... max reached."); - } else { - auto faction_message = ( - found_count > 0 ? - ( - found_count == 1 ? - "A Faction was" : - fmt::format("{} Factions were", found_count) - ) : - "No Factions were" - ); - - c->Message( - Chat::White, - fmt::format( - "{} found.", - faction_message - ).c_str() - ); - } - } -} - -void command_findrace(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - - if (arguments == 0) { - c->Message(Chat::White, "Command Syntax: #findrace [search criteria]"); - return; - } - - if (sep->IsNumber(1)) { - int race_id = std::stoi(sep->arg[1]); - std::string race_name = GetRaceIDName(race_id); - if (race_id >= RACE_HUMAN_1 && race_id <= RACE_PEGASUS_732) { - c->Message( - Chat::White, - fmt::format( - "Race {}: {}", - race_id, - race_name - ).c_str() - ); - } else { - c->Message( - Chat::White, - fmt::format( - "Race ID {} was not found.", - race_id - ).c_str() - ); - } - } else { - std::string search_criteria = str_tolower(sep->argplus[1]); - int found_count = 0; - for (int race_id = RACE_HUMAN_1; race_id <= RACE_PEGASUS_732; race_id++) { - std::string race_name = GetRaceIDName(race_id); - std::string race_name_lower = str_tolower(race_name); - if (search_criteria.length() > 0 && race_name_lower.find(search_criteria) == std::string::npos) { - continue; - } - - c->Message( - Chat::White, - fmt::format( - "Race {}: {}", - race_id, - race_name - ).c_str() - ); - found_count++; - - if (found_count == 20) { - break; - } - } - - if (found_count == 20) { - c->Message(Chat::White, "20 Races found... max reached."); - } else { - auto race_message = ( - found_count > 0 ? - ( - found_count == 1 ? - "A Race was" : - fmt::format("{} Races were", found_count) - ) : - "No Races were" - ); - - c->Message( - Chat::White, - fmt::format( - "{} found.", - race_message - ).c_str() - ); - } - } -} - -void command_findskill(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - - if (arguments == 0) { - c->Message(Chat::White, "Command Syntax: #findskill [search criteria]"); - return; - } - - std::map skills = EQ::skills::GetSkillTypeMap(); - if (sep->IsNumber(1)) { - int skill_id = std::stoi(sep->arg[1]); - if (skill_id >= EQ::skills::Skill1HBlunt && skill_id < EQ::skills::SkillCount) { - for (auto skill : skills) { - if (skill_id == skill.first) { - c->Message( - Chat::White, - fmt::format( - "Skill {}: {}", - skill.first, - skill.second - ).c_str() - ); - break; - } - } - } else { - c->Message( - Chat::White, - fmt::format( - "Skill ID {} was not found.", - skill_id - ).c_str() - ); - } - } else { - std::string search_criteria = str_tolower(sep->argplus[1]); - if (!search_criteria.empty()) { - int found_count = 0; - for (auto skill : skills) { - std::string skill_name_lower = str_tolower(skill.second); - if (skill_name_lower.find(search_criteria) == std::string::npos) { - continue; - } - - c->Message( - Chat::White, - fmt::format( - "Skill {}: {}", - skill.first, - skill.second - ).c_str() - ); - found_count++; - - if (found_count == 20) { - break; - } - } - - if (found_count == 20) { - c->Message(Chat::White, "20 Skills were found, max reached."); - } else { - auto skill_message = ( - found_count > 0 ? - ( - found_count == 1 ? - "A Skill was" : - fmt::format("{} Skills were", found_count) - ) : - "No Skills were" - ); - - c->Message( - Chat::White, - fmt::format( - "{} found.", - skill_message - ).c_str() - ); - } - } - } -} - -void command_findspell(Client *c, const Seperator *sep) -{ - if (SPDAT_RECORDS <= 0) { - c->Message(Chat::White, "Spells not loaded"); - return; - } - - int arguments = sep->argnum; - - if (arguments == 0) { - c->Message(Chat::White, "Command Syntax: #findspell [search criteria]"); - return; - } - - if (sep->IsNumber(1)) { - int spell_id = std::stoi(sep->arg[1]); - if (!IsValidSpell(spell_id)) { - c->Message( - Chat::White, - fmt::format( - "Spell ID {} was not found.", - spell_id - ).c_str() - ); - } else { - c->Message( - Chat::White, - fmt::format( - "Spell {}: {}", - spell_id, - spells[spell_id].name - ).c_str() - ); - } - } else { - std::string search_criteria = str_tolower(sep->argplus[1]); - int found_count = 0; - for (int spell_id = 0; spell_id < SPDAT_RECORDS; spell_id++) { - auto current_spell = spells[spell_id]; - if (current_spell.name[0] != 0) { - std::string spell_name = current_spell.name; - std::string spell_name_lower = str_tolower(spell_name); - if (search_criteria.length() > 0 && spell_name_lower.find(search_criteria) == std::string::npos) { - continue; - } - - c->Message( - Chat::White, - fmt::format( - "Spell {}: {}", - spell_id, - spell_name - ).c_str() - ); - found_count++; - - if (found_count == 20) { - break; - } - } - } - - if (found_count == 20) { - c->Message(Chat::White, "20 Spells found... max reached."); - } else { - auto spell_message = ( - found_count > 0 ? - ( - found_count == 1 ? - "A Spell was" : - fmt::format("{} Spells were", found_count) - ) : - "No Spells were" - ); - - c->Message( - Chat::White, - fmt::format( - "{} found.", - spell_message - ).c_str() - ); - } - } -} - -inline bool CastRestrictedSpell(int spellid) -{ - switch (spellid) { - case SPELL_TOUCH_OF_VINITRAS: - case SPELL_DESPERATE_HOPE: - case SPELL_CHARM: - case SPELL_METAMORPHOSIS65: - case SPELL_JT_BUFF: - case SPELL_CAN_O_WHOOP_ASS: - case SPELL_PHOENIX_CHARM: - case SPELL_CAZIC_TOUCH: - case SPELL_AVATAR_KNOCKBACK: - case SPELL_SHAPECHANGE65: - case SPELL_SUNSET_HOME1218: - case SPELL_SUNSET_HOME819: - case SPELL_SHAPECHANGE75: - case SPELL_SHAPECHANGE80: - case SPELL_SHAPECHANGE85: - case SPELL_SHAPECHANGE90: - case SPELL_SHAPECHANGE95: - case SPELL_SHAPECHANGE100: - case SPELL_SHAPECHANGE25: - case SPELL_SHAPECHANGE30: - case SPELL_SHAPECHANGE35: - case SPELL_SHAPECHANGE40: - case SPELL_SHAPECHANGE45: - case SPELL_SHAPECHANGE50: - case SPELL_NPC_AEGOLISM: - case SPELL_SHAPECHANGE55: - case SPELL_SHAPECHANGE60: - case SPELL_COMMAND_OF_DRUZZIL: - case SPELL_SHAPECHANGE70: - return true; - default: - return false; - } -} - -void command_castspell(Client *c, const Seperator *sep) -{ - if (SPDAT_RECORDS <= 0) { - c->Message(Chat::White, "Spells not loaded."); - return; - } - - Mob *target = c; - if(c->GetTarget()) { - target = c->GetTarget(); - } - - if (!sep->IsNumber(1)) { - c->Message(Chat::White, "Usage: #castspell [Spell ID] [Instant (0 = False, 1 = True, Default is 1 if Unused)]"); - } else { - uint16 spell_id = std::stoul(sep->arg[1]); - - if (CastRestrictedSpell(spell_id) && c->Admin() < commandCastSpecials) { - c->Message(Chat::Red, "Unable to cast spell."); - } else if (spell_id >= SPDAT_RECORDS) { - c->Message(Chat::White, "Invalid Spell ID."); - } else { - bool instant_cast = (c->Admin() >= commandInstacast ? true : false); - if (instant_cast && sep->IsNumber(2)) { - instant_cast = std::stoi(sep->arg[2]) ? true : false; - c->Message(Chat::White, fmt::format("{}", std::stoi(sep->arg[2])).c_str()); - } - - if (c->Admin() >= commandInstacast && instant_cast) { - c->SpellFinished(spell_id, target, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty); - } else { - c->CastSpell(spell_id, target->GetID(), EQ::spells::CastingSlot::Item, spells[spell_id].cast_time); - } - - if (c != target) { - c->Message( - Chat::White, - fmt::format( - "Cast {} ({}) on {}{}.", - GetSpellName(spell_id), - spell_id, - target->GetCleanName(), - instant_cast ? " instantly" : "" - ).c_str() - ); - } else { - c->Message( - Chat::White, - fmt::format( - "Cast {} ({}) on yourself{}.", - GetSpellName(spell_id), - spell_id, - instant_cast ? " instantly" : "" - ).c_str() - ); - } - } - } -} - -void command_setlanguage(Client *c, const Seperator *sep) -{ - Client* target = c; - if (c->GetTarget() && c->GetTarget()->IsClient()) { - target = c->GetTarget()->CastToClient(); - } - - auto language_id = sep->IsNumber(1) ? std::stoi(sep->arg[1]) : -1; - auto language_value = sep->IsNumber(2) ? std::stoi(sep->arg[2]) : -1; - if (!strcasecmp(sep->arg[1], "list" )) { - for (int language = LANG_COMMON_TONGUE; language <= LANG_UNKNOWN; language++) { - c->Message( - Chat::White, - fmt::format( - "Language {}: {}", - language, - EQ::constants::GetLanguageName(language) - ).c_str() - ); - } - } else if ( - language_id < LANG_COMMON_TONGUE || - language_id > LANG_UNKNOWN || - language_value < 0 || - language_value > 100 - ) { - c->Message(Chat::White, "Usage: #setlanguage [Language ID] [Language Value]"); - c->Message(Chat::White, "Usage: #setlanguage [List]"); - c->Message(Chat::White, "Language ID = 0 to 27", LANG_UNKNOWN); - c->Message(Chat::White, "Language Value = 0 to 100", HIGHEST_CAN_SET_SKILL); - } else { - LogInfo( - "Set language request from [{}], Target: [{}] Language ID: [{}] Language Value: [{}]", - c->GetCleanName(), - target->GetCleanName(), - language_id, - language_value - ); - - target->SetLanguageSkill(language_id, language_value); - - if (c != target) { - c->Message( - Chat::White, - fmt::format( - "Set {} ({}) to {} for {}.", - EQ::constants::GetLanguageName(language_id), - language_id, - language_value, - target->GetCleanName() - ).c_str() - ); - } - } -} - -void command_setskill(Client* c, const Seperator* sep) -{ - Client* target = c; - if (c->GetTarget() && c->GetTarget()->IsClient()) { - target = c->GetTarget()->CastToClient(); - } - - auto skill_id = sep->IsNumber(1) ? std::stoi(sep->arg[1]) : -1; - auto skill_value = sep->IsNumber(2) ? std::stoi(sep->arg[2]) : -1; - if ( - skill_id < 0 || - skill_id > EQ::skills::HIGHEST_SKILL || - skill_value < 0 || - skill_value > HIGHEST_CAN_SET_SKILL - ) { - c->Message(Chat::White, "Usage: #setskill [Skill ID] [Skill Value]"); - c->Message(Chat::White, fmt::format("Skill ID = 0 to {}", EQ::skills::HIGHEST_SKILL).c_str()); - c->Message(Chat::White, fmt::format("Skill Value = 0 to {}", HIGHEST_CAN_SET_SKILL).c_str()); - } - else { - LogInfo( - "Set skill request from [{}], Target: [{}] Skill ID: [{}] Skill Value: [{}]", - c->GetCleanName(), - target->GetCleanName(), - skill_id, - skill_value - ); - - if ( - skill_id >= EQ::skills::Skill1HBlunt && - skill_id <= EQ::skills::HIGHEST_SKILL - ) { - target->SetSkill( - (EQ::skills::SkillType)skill_id, - skill_value - ); - - if (c != target) { - c->Message( - Chat::White, - fmt::format( - "Set {} ({}) to {} for {}.", - EQ::skills::GetSkillName((EQ::skills::SkillType)skill_id), - skill_id, - skill_value, - target->GetCleanName() - ).c_str() - ); - } - } - } -} - -void command_setskillall(Client *c, const Seperator *sep) -{ - if (c->GetTarget() == 0) - c->Message(Chat::White, "Error: #setallskill: No target."); - else if (!c->GetTarget()->IsClient()) - c->Message(Chat::White, "Error: #setskill: Target must be a client."); - else if (!sep->IsNumber(1) || atoi(sep->arg[1]) < 0 || atoi(sep->arg[1]) > HIGHEST_CAN_SET_SKILL) { - c->Message(Chat::White, "Usage: #setskillall value "); - c->Message(Chat::White, " value = 0 to %d", HIGHEST_CAN_SET_SKILL); - } - else { - if (c->Admin() >= commandSetSkillsOther || c->GetTarget()==c || c->GetTarget()==0) { - LogInfo("Set ALL skill request from [{}], target:[{}]", c->GetName(), c->GetTarget()->GetName()); - uint16 level = atoi(sep->arg[1]); - for (EQ::skills::SkillType skill_num = EQ::skills::Skill1HBlunt; skill_num <= EQ::skills::HIGHEST_SKILL; skill_num = (EQ::skills::SkillType)(skill_num + 1)) { - c->GetTarget()->CastToClient()->SetSkill(skill_num, level); - } - } - else - c->Message(Chat::White, "Error: Your status is not high enough to set anothers skills"); - } -} - -void command_race(Client *c, const Seperator *sep) -{ - Mob *target = c->CastToMob(); - - if (sep->IsNumber(1)) { - auto race = atoi(sep->arg[1]); - if ((race >= 0 && race <= RuleI(NPC, MaxRaceID)) || (race >= 2253 && race <= 2259)) { - if ((c->GetTarget()) && c->Admin() >= commandRaceOthers) { - target = c->GetTarget(); - } - target->SendIllusionPacket(race); - } - else { - c->Message(Chat::White, fmt::format("Usage: #race [0-{}, 2253-2259] (0 for back to normal)", RuleI(NPC, MaxRaceID)).c_str()); - } - } - else { - c->Message(Chat::White, fmt::format("Usage: #race [0-{}, 2253-2259] (0 for back to normal)", RuleI(NPC, MaxRaceID)).c_str()); - } -} - -void command_gearup(Client *c, const Seperator *sep) -{ - std::string tool_table_name = "tool_gearup_armor_sets"; - if (!database.DoesTableExist(tool_table_name)) { - c->Message( - Chat::Yellow, - fmt::format( - "Table [{}] does not exist. Downloading from Github and installing...", - tool_table_name - ).c_str() - ); - - // http get request - httplib::Client cli("https://raw.githubusercontent.com"); - cli.set_connection_timeout(0, 15000000); // 15 sec - cli.set_read_timeout(15, 0); // 15 seconds - cli.set_write_timeout(15, 0); // 15 seconds - - int sourced_queries = 0; - std::string url = "/EQEmu/Server/master/utils/sql/git/optional/2020_07_20_tool_gearup_armor_sets.sql"; - - if (auto res = cli.Get(url.c_str())) { - if (res->status == 200) { - for (auto &s: SplitString(res->body, ';')) { - if (!trim(s).empty()) { - auto results = database.QueryDatabase(s); - if (!results.ErrorMessage().empty()) { - c->Message( - Chat::Yellow, - fmt::format( - "Error sourcing SQL [{}]", results.ErrorMessage() - ).c_str() - ); - return; - } - sourced_queries++; - } - } - } - } - else { - c->Message( - Chat::Yellow, - fmt::format( - "Error retrieving URL [{}]", - url - ).c_str() - ); - } - - c->Message( - Chat::Yellow, - fmt::format( - "Table [{}] installed. Sourced [{}] queries", - tool_table_name, sourced_queries - ).c_str() - ); - } - - std::string expansion_arg = sep->arg[1]; - std::string expansion_filter; - if (expansion_arg.length() > 0) { - expansion_filter = fmt::format("and `expansion` = {}", expansion_arg); - } - - auto results = database.QueryDatabase( - fmt::format( - SQL ( - select - item_id, - slot - from - {} - where - `class` = {} - and `level` = {} - {} - order by score desc, expansion desc - ), - tool_table_name, - c->GetClass(), - c->GetLevel(), - expansion_filter - ) - ); - - int items_equipped = 0; - int items_already_have = 0; - std::set equipped; - - for (auto row = results.begin(); row != results.end(); ++row) { - int item_id = atoi(row[0]); - int slot_id = atoi(row[1]); - - if (equipped.find(slot_id) != equipped.end()) { - if (slot_id == EQ::invslot::slotEar1) { - slot_id = EQ::invslot::slotEar2; - } - if (slot_id == EQ::invslot::slotFinger1) { - slot_id = EQ::invslot::slotFinger2; - } - if (slot_id == EQ::invslot::slotWrist1) { - slot_id = EQ::invslot::slotWrist2; - } - } - - if (equipped.find(slot_id) == equipped.end()) { - const EQ::ItemData *item = database.GetItem(item_id); - bool has_item = (c->GetInv().HasItem(item_id, 1, invWhereWorn) != INVALID_INDEX); - bool can_wear_item = !c->CheckLoreConflict(item) && !has_item; - if (!can_wear_item) { - items_already_have++; - } - - if (c->CastToMob()->CanClassEquipItem(item_id) && can_wear_item) { - equipped.insert(slot_id); - c->SummonItem( - item_id, - 0, 0, 0, 0, 0, 0, 0, 0, - slot_id - ); - items_equipped++; - } - } - } - - c->Message( - Chat::White, - fmt::format( - "Equipped items [{}] already had [{}] items equipped", - items_equipped, - items_already_have - ).c_str() - ); - - if (expansion_arg.empty()) { - results = database.QueryDatabase( - fmt::format( - SQL ( - select - expansion - from - {} - where - class = {} - and level = {} - group by - expansion; - ), - tool_table_name, - c->GetClass(), - c->GetLevel() - ) - ); - - c->Message(Chat::White, "Choose armor from a specific era"); - std::string message; - for (auto row = results.begin(); row != results.end(); ++row) { - int expansion = atoi(row[0]); - message += "[" + EQ::SayLinkEngine::GenerateQuestSaylink( - fmt::format("#gearup {}", expansion), - false, - Expansion::ExpansionName[expansion] - ) + "] "; - - if (message.length() > 2000) { - c->Message(Chat::White, message.c_str()); - message = ""; - } - } - if (message.length() > 0) { - c->Message(Chat::White, message.c_str()); - } - } - -} - -void command_gender(Client *c, const Seperator *sep) -{ - Mob *t=c->CastToMob(); - - if (sep->IsNumber(1) && atoi(sep->arg[1]) >= 0 && atoi(sep->arg[1]) <= 500) { - if ((c->GetTarget()) && c->Admin() >= commandGenderOthers) - t=c->GetTarget(); - t->SendIllusionPacket(t->GetRace(), atoi(sep->arg[1])); - } - else - c->Message(Chat::White, "Usage: #gender [0/1/2]"); -} - -void command_makepet(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0] == '\0') - c->Message(Chat::White, "Usage: #makepet pet_type_name (will not survive across zones)"); - else - c->MakePet(0, sep->arg[1]); -} - void command_level(Client *c, const Seperator *sep) { uint16 level = atoi(sep->arg[1]); @@ -4238,4184 +927,6 @@ void command_spawneditmass(Client *c, const Seperator *sep) } } -void command_spawn(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0] != 0){ - Client* client = entity_list.GetClientByName(sep->arg[1]); - if(client){ - c->Message(Chat::White,"You cannot spawn a mob with the same name as a character!"); - return; - } - } - - NPC* npc = NPC::SpawnNPC(sep->argplus[1], c->GetPosition(), c); - if (!npc) { - c->Message(Chat::White, "Format: #spawn name race level material hp gender class priweapon secweapon merchantid bodytype - spawns a npc those parameters."); - c->Message(Chat::White, "Name Format: NPCFirstname_NPCLastname - All numbers in a name are stripped and \"_\" characters become a space."); - c->Message(Chat::White, "Note: Using \"-\" for gender will autoselect the gender for the race. Using \"-\" for HP will use the calculated maximum HP."); - } -} - -void command_texture(Client *c, const Seperator *sep) -{ - - uint16 texture; - - if (sep->IsNumber(1) && atoi(sep->arg[1]) >= 0 && atoi(sep->arg[1]) <= 255) { - texture = atoi(sep->arg[1]); - uint8 helm = 0xFF; - - // Player Races Wear Armor, so Wearchange is sent instead - int i; - if (!c->GetTarget()) - for (i = EQ::textures::textureBegin; i <= EQ::textures::LastTintableTexture; i++) - { - c->SendTextureWC(i, texture); - } - else if ((c->GetTarget()->GetModel() > 0 && c->GetTarget()->GetModel() <= 12) || - c->GetTarget()->GetModel() == 128 || c->GetTarget()->GetModel() == 130 || - c->GetTarget()->GetModel() == 330 || c->GetTarget()->GetModel() == 522) { - for (i = EQ::textures::textureBegin; i <= EQ::textures::LastTintableTexture; i++) - { - c->GetTarget()->SendTextureWC(i, texture); - } - } - else // Non-Player Races only need Illusion Packets to be sent for texture - { - if (sep->IsNumber(2) && atoi(sep->arg[2]) >= 0 && atoi(sep->arg[2]) <= 255) - helm = atoi(sep->arg[2]); - else - helm = texture; - - if (texture == 255) { - texture = 0xFFFF; // Should be pulling these from the database instead - helm = 0xFF; - } - - if ((c->GetTarget()) && (c->Admin() >= commandTextureOthers)) - c->GetTarget()->SendIllusionPacket(c->GetTarget()->GetModel(), 0xFF, texture, helm); - else - c->SendIllusionPacket(c->GetRace(), 0xFF, texture, helm); - } - } - else - c->Message(Chat::White, "Usage: #texture [texture] [helmtexture] (0-255, 255 for show equipment)"); -} - -void command_npctypespawn(Client *c, const Seperator *sep) -{ - if (sep->IsNumber(1)) { - const NPCType* tmp = 0; - if ((tmp = content_db.LoadNPCTypesData(atoi(sep->arg[1])))) { - //tmp->fixedZ = 1; - auto npc = new NPC(tmp, 0, c->GetPosition(), GravityBehavior::Water); - if (npc && sep->IsNumber(2)) - npc->SetNPCFactionID(atoi(sep->arg[2])); - - npc->AddLootTable(); - if (npc->DropsGlobalLoot()) - npc->CheckGlobalLootTables(); - entity_list.AddNPC(npc); - } - else - c->Message(Chat::White, "NPC Type %i not found", atoi(sep->arg[1])); - } - else - c->Message(Chat::White, "Usage: #npctypespawn npctypeid factionid"); - -} - -void command_nudge(Client* c, const Seperator* sep) -{ - if (sep->arg[1][0] == 0) { - c->Message(Chat::White, "Usage: #nudge [x=f] [y=f] [z=f] [h=f] (partial/mixed arguments allowed)"); - } - else { - - auto target = c->GetTarget(); - if (!target) { - - c->Message(Chat::Yellow, "This command requires a target."); - return; - } - if (target->IsMoving()) { - - c->Message(Chat::Yellow, "This command requires a stationary target."); - return; - } - - glm::vec4 position_offset(0.0f, 0.0f, 0.0f, 0.0f); - for (auto index = 1; index <= 4; ++index) { - - if (!sep->arg[index]) { - continue; - } - - Seperator argsep(sep->arg[index], '='); - if (!argsep.arg[1][0]) { - continue; - } - - switch (argsep.arg[0][0]) { - case 'x': - position_offset.x = atof(argsep.arg[1]); - break; - case 'y': - position_offset.y = atof(argsep.arg[1]); - break; - case 'z': - position_offset.z = atof(argsep.arg[1]); - break; - case 'h': - position_offset.w = atof(argsep.arg[1]); - break; - default: - break; - } - } - - const auto& current_position = target->GetPosition(); - glm::vec4 new_position( - (current_position.x + position_offset.x), - (current_position.y + position_offset.y), - (current_position.z + position_offset.z), - (current_position.w + position_offset.w) - ); - - target->GMMove(new_position.x, new_position.y, new_position.z, new_position.w); - - c->Message( - Chat::White, - "Nudging '%s' to {%1.3f, %1.3f, %1.3f, %1.2f} (adjustment: {%1.3f, %1.3f, %1.3f, %1.2f})", - target->GetName(), - new_position.x, - new_position.y, - new_position.z, - new_position.w, - position_offset.x, - position_offset.y, - position_offset.z, - position_offset.w - ); - } -} - -void command_heal(Client *c, const Seperator *sep) -{ - auto target = c->GetTarget() ? c->GetTarget() : c; - target->Heal(); - if (c != target) { - c->Message( - Chat::White, - fmt::format( - "Healed {} ({}) to full.", - target->GetCleanName(), - target->GetID() - ).c_str() - ); - } else { - c->Message(Chat::White, "Healed yourself to full."); - } -} - -void command_appearance(Client *c, const Seperator *sep) -{ - Mob *t=c->CastToMob(); - - // sends any appearance packet - // Dev debug command, for appearance types - if (sep->arg[2][0] == 0) - c->Message(Chat::White, "Usage: #appearance type value"); - else { - if ((c->GetTarget())) - t=c->GetTarget(); - t->SendAppearancePacket(atoi(sep->arg[1]), atoi(sep->arg[2])); - c->Message(Chat::White, "Sending appearance packet: target=%s, type=%s, value=%s", t->GetName(), sep->arg[1], sep->arg[2]); - } -} - -void command_nukeitem(Client *c, const Seperator *sep) -{ - int numitems, itemid; - - if (c->GetTarget() && c->GetTarget()->IsClient() && (sep->IsNumber(1) || sep->IsHexNumber(1))) { - itemid=sep->IsNumber(1)?atoi(sep->arg[1]):hextoi(sep->arg[1]); - numitems = c->GetTarget()->CastToClient()->NukeItem(itemid); - c->Message(Chat::White, " %u items deleted", numitems); - } - else - c->Message(Chat::White, "Usage: (targted) #nukeitem itemnum - removes the item from the player's inventory"); -} - -void command_peekinv(Client *c, const Seperator *sep) -{ - // this can be cleaned up once inventory is cleaned up - enum { - peekNone = 0x0000, - peekEquip = 0x0001, - peekGen = 0x0002, - peekCursor = 0x0004, - peekLimbo = 0x0008, - peekTrib = 0x0010, - peekBank = 0x0020, - peekShBank = 0x0040, - peekTrade = 0x0080, - peekWorld = 0x0100, - peekOutOfScope = (peekWorld * 2) // less than - }; - - static const char* scope_prefix[] = { "equip", "gen", "cursor", "limbo", "trib", "bank", "shbank", "trade", "world" }; - - static const int16 scope_range[][2] = { - { EQ::invslot::EQUIPMENT_BEGIN, EQ::invslot::EQUIPMENT_END }, - { EQ::invslot::GENERAL_BEGIN, EQ::invslot::GENERAL_END }, - { EQ::invslot::slotCursor, EQ::invslot::slotCursor }, - { EQ::invslot::SLOT_INVALID, EQ::invslot::SLOT_INVALID }, - { EQ::invslot::TRIBUTE_BEGIN, EQ::invslot::TRIBUTE_END }, - { EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END }, - { EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END }, - { EQ::invslot::TRADE_BEGIN, EQ::invslot::TRADE_END }, - { EQ::invslot::SLOT_BEGIN, (EQ::invtype::WORLD_SIZE - 1) } - }; - - static const bool scope_bag[] = { false, true, true, true, false, true, true, true, true }; - - if (!c) - return; - - if (c->GetTarget() && !c->GetTarget()->IsClient()) { - c->Message(Chat::White, "You must target a PC for this command."); - return; - } - - int scopeMask = peekNone; - - if (strcasecmp(sep->arg[1], "all") == 0) { scopeMask = (peekOutOfScope - 1); } - else if (strcasecmp(sep->arg[1], "equip") == 0) { scopeMask |= peekEquip; } - else if (strcasecmp(sep->arg[1], "gen") == 0) { scopeMask |= peekGen; } - else if (strcasecmp(sep->arg[1], "cursor") == 0) { scopeMask |= peekCursor; } - else if (strcasecmp(sep->arg[1], "poss") == 0) { scopeMask |= (peekEquip | peekGen | peekCursor); } - else if (strcasecmp(sep->arg[1], "limbo") == 0) { scopeMask |= peekLimbo; } - else if (strcasecmp(sep->arg[1], "curlim") == 0) { scopeMask |= (peekCursor | peekLimbo); } - else if (strcasecmp(sep->arg[1], "trib") == 0) { scopeMask |= peekTrib; } - else if (strcasecmp(sep->arg[1], "bank") == 0) { scopeMask |= peekBank; } - else if (strcasecmp(sep->arg[1], "shbank") == 0) { scopeMask |= peekShBank; } - else if (strcasecmp(sep->arg[1], "allbank") == 0) { scopeMask |= (peekBank | peekShBank); } - else if (strcasecmp(sep->arg[1], "trade") == 0) { scopeMask |= peekTrade; } - else if (strcasecmp(sep->arg[1], "world") == 0) { scopeMask |= peekWorld; } - - if (!scopeMask) { - c->Message(Chat::White, "Usage: #peekinv [equip|gen|cursor|poss|limbo|curlim|trib|bank|shbank|allbank|trade|world|all]"); - c->Message(Chat::White, "- Displays a portion of the targeted user's inventory"); - c->Message(Chat::White, "- Caution: 'all' is a lot of information!"); - return; - } - - Client* targetClient = c; - if (c->GetTarget()) - targetClient = c->GetTarget()->CastToClient(); - - const EQ::ItemInstance* inst_main = nullptr; - const EQ::ItemInstance* inst_sub = nullptr; - const EQ::ItemInstance* inst_aug = nullptr; - const EQ::ItemData* item_data = nullptr; - - EQ::SayLinkEngine linker; - linker.SetLinkType(EQ::saylink::SayLinkItemInst); - - c->Message(Chat::White, "Displaying inventory for %s...", targetClient->GetName()); - - Object* objectTradeskill = targetClient->GetTradeskillObject(); - - bool itemsFound = false; - - for (int scopeIndex = 0, scopeBit = peekEquip; scopeBit < peekOutOfScope; ++scopeIndex, scopeBit <<= 1) { - if (scopeBit & ~scopeMask) - continue; - - if (scopeBit & peekWorld) { - if (objectTradeskill == nullptr) { - c->Message(Chat::Default, "No world tradeskill object selected..."); - continue; - } - else { - c->Message(Chat::White, "[WorldObject DBID: %i (entityid: %i)]", objectTradeskill->GetDBID(), objectTradeskill->GetID()); - } - } - - for (int16 indexMain = scope_range[scopeIndex][0]; indexMain <= scope_range[scopeIndex][1]; ++indexMain) { - if (indexMain == EQ::invslot::SLOT_INVALID) - continue; - - inst_main = ((scopeBit & peekWorld) ? objectTradeskill->GetItem(indexMain) : targetClient->GetInv().GetItem(indexMain)); - if (inst_main) { - itemsFound = true; - item_data = inst_main->GetItem(); - } - else { - item_data = nullptr; - } - - linker.SetItemInst(inst_main); - - c->Message( - (item_data == nullptr), - "%sSlot: %i, Item: %i (%s), Charges: %i", - scope_prefix[scopeIndex], - ((scopeBit & peekWorld) ? (EQ::invslot::WORLD_BEGIN + indexMain) : indexMain), - ((item_data == nullptr) ? 0 : item_data->ID), - linker.GenerateLink().c_str(), - ((inst_main == nullptr) ? 0 : inst_main->GetCharges()) - ); - - if (inst_main && inst_main->IsClassCommon()) { - for (uint8 indexAug = EQ::invaug::SOCKET_BEGIN; indexAug <= EQ::invaug::SOCKET_END; ++indexAug) { - inst_aug = inst_main->GetItem(indexAug); - if (!inst_aug) // extant only - continue; - - item_data = inst_aug->GetItem(); - linker.SetItemInst(inst_aug); - - c->Message( - (item_data == nullptr), - ".%sAugSlot: %i (Slot #%i, Aug idx #%i), Item: %i (%s), Charges: %i", - scope_prefix[scopeIndex], - INVALID_INDEX, - ((scopeBit & peekWorld) ? (EQ::invslot::WORLD_BEGIN + indexMain) : indexMain), - indexAug, - ((item_data == nullptr) ? 0 : item_data->ID), - linker.GenerateLink().c_str(), - ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()) - ); - } - } - - if (!scope_bag[scopeIndex] || !(inst_main && inst_main->IsClassBag())) - continue; - - for (uint8 indexSub = EQ::invbag::SLOT_BEGIN; indexSub <= EQ::invbag::SLOT_END; ++indexSub) { - inst_sub = inst_main->GetItem(indexSub); - if (!inst_sub) // extant only - continue; - - item_data = inst_sub->GetItem(); - linker.SetItemInst(inst_sub); - - c->Message( - (item_data == nullptr), - "..%sBagSlot: %i (Slot #%i, Bag idx #%i), Item: %i (%s), Charges: %i", - scope_prefix[scopeIndex], - ((scopeBit & peekWorld) ? INVALID_INDEX : EQ::InventoryProfile::CalcSlotId(indexMain, indexSub)), - ((scopeBit & peekWorld) ? (EQ::invslot::WORLD_BEGIN + indexMain) : indexMain), - indexSub, - ((item_data == nullptr) ? 0 : item_data->ID), - linker.GenerateLink().c_str(), - ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()) - ); - - if (inst_sub->IsClassCommon()) { - for (uint8 indexAug = EQ::invaug::SOCKET_BEGIN; indexAug <= EQ::invaug::SOCKET_END; ++indexAug) { - inst_aug = inst_sub->GetItem(indexAug); - if (!inst_aug) // extant only - continue; - - item_data = inst_aug->GetItem(); - linker.SetItemInst(inst_aug); - - c->Message( - (item_data == nullptr), - "...%sAugSlot: %i (Slot #%i, Sub idx #%i, Aug idx #%i), Item: %i (%s), Charges: %i", - scope_prefix[scopeIndex], - INVALID_INDEX, - ((scopeBit & peekWorld) ? INVALID_INDEX : EQ::InventoryProfile::CalcSlotId(indexMain, indexSub)), - indexSub, - indexAug, - ((item_data == nullptr) ? 0 : item_data->ID), - linker.GenerateLink().c_str(), - ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()) - ); - } - } - } - } - - if (scopeBit & peekLimbo) { - int limboIndex = 0; - for (auto it = targetClient->GetInv().cursor_cbegin(); (it != targetClient->GetInv().cursor_cend()); ++it, ++limboIndex) { - if (it == targetClient->GetInv().cursor_cbegin()) - continue; - - inst_main = *it; - if (inst_main) { - itemsFound = true; - item_data = inst_main->GetItem(); - } - else { - item_data = nullptr; - } - - linker.SetItemInst(inst_main); - - c->Message( - (item_data == nullptr), - "%sSlot: %i, Item: %i (%s), Charges: %i", - scope_prefix[scopeIndex], - (8000 + limboIndex), - ((item_data == nullptr) ? 0 : item_data->ID), - linker.GenerateLink().c_str(), - ((inst_main == nullptr) ? 0 : inst_main->GetCharges()) - ); - - if (inst_main && inst_main->IsClassCommon()) { - for (uint8 indexAug = EQ::invaug::SOCKET_BEGIN; indexAug <= EQ::invaug::SOCKET_END; ++indexAug) { - inst_aug = inst_main->GetItem(indexAug); - if (!inst_aug) // extant only - continue; - - item_data = inst_aug->GetItem(); - linker.SetItemInst(inst_aug); - - c->Message( - (item_data == nullptr), - ".%sAugSlot: %i (Slot #%i, Aug idx #%i), Item: %i (%s), Charges: %i", - scope_prefix[scopeIndex], - INVALID_INDEX, - (8000 + limboIndex), - indexAug, - ((item_data == nullptr) ? 0 : item_data->ID), - linker.GenerateLink().c_str(), - ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()) - ); - } - } - - if (!scope_bag[scopeIndex] || !(inst_main && inst_main->IsClassBag())) - continue; - - for (uint8 indexSub = EQ::invbag::SLOT_BEGIN; indexSub <= EQ::invbag::SLOT_END; ++indexSub) { - inst_sub = inst_main->GetItem(indexSub); - if (!inst_sub) - continue; - - item_data = (inst_sub == nullptr) ? nullptr : inst_sub->GetItem(); - - linker.SetItemInst(inst_sub); - - c->Message( - (item_data == nullptr), - "..%sBagSlot: %i (Slot #%i, Bag idx #%i), Item: %i (%s), Charges: %i", - scope_prefix[scopeIndex], - INVALID_INDEX, - (8000 + limboIndex), - indexSub, - ((item_data == nullptr) ? 0 : item_data->ID), - linker.GenerateLink().c_str(), - ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()) - ); - - if (inst_sub->IsClassCommon()) { - for (uint8 indexAug = EQ::invaug::SOCKET_BEGIN; indexAug <= EQ::invaug::SOCKET_END; ++indexAug) { - inst_aug = inst_sub->GetItem(indexAug); - if (!inst_aug) // extant only - continue; - - item_data = inst_aug->GetItem(); - linker.SetItemInst(inst_aug); - - c->Message( - (item_data == nullptr), - "...%sAugSlot: %i (Slot #%i, Sub idx #%i, Aug idx #%i), Item: %i (%s), Charges: %i", - scope_prefix[scopeIndex], - INVALID_INDEX, - (8000 + limboIndex), - indexSub, - indexAug, - ((item_data == nullptr) ? 0 : item_data->ID), - linker.GenerateLink().c_str(), - ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()) - ); - } - } - } - } - } - } - - if (!itemsFound) - c->Message(Chat::White, "No items found."); -} - -void command_interrogateinv(Client *c, const Seperator *sep) -{ - // 'command_interrogateinv' is an in-memory inventory interrogation tool only. - // - // it does not verify against actual database entries..but, the output can be - // used to verify that something has been corrupted in a player's inventory. - // any error condition should be assumed that the item in question will be - // lost when the player logs out or zones (or incurrs any action that will - // consume the Client-Inventory object instance in question.) - // - // any item instances located at a greater depth than a reported error should - // be treated as an error themselves regardless of whether they report as the - // same or not. - - if (strcasecmp(sep->arg[1], "help") == 0) { - if (c->Admin() < commandInterrogateInv) { - c->Message(Chat::White, "Usage: #interrogateinv"); - c->Message(Chat::White, " Displays your inventory's current in-memory nested storage references"); - } - else { - c->Message(Chat::White, "Usage: #interrogateinv [log] [silent]"); - c->Message(Chat::White, " Displays your or your Player target inventory's current in-memory nested storage references"); - c->Message(Chat::White, " [log] - Logs interrogation to file"); - c->Message(Chat::White, " [silent] - Omits the in-game message portion of the interrogation"); - } - return; - } - - Client* target = nullptr; - std::map instmap; - bool log = false; - bool silent = false; - bool error = false; - bool allowtrip = false; - - if (c->Admin() < commandInterrogateInv) { - if (c->GetInterrogateInvState()) { - c->Message(Chat::Red, "The last use of #interrogateinv on this inventory instance discovered an error..."); - c->Message(Chat::Red, "Logging out, zoning or re-arranging items at this point will result in item loss!"); - return; - } - target = c; - allowtrip = true; - } - else { - if (c->GetTarget() == nullptr) { - target = c; - } - else if (c->GetTarget()->IsClient()) { - target = c->GetTarget()->CastToClient(); - } - else { - c->Message(Chat::Default, "Use of this command is limited to Client entities"); - return; - } - - if (strcasecmp(sep->arg[1], "log") == 0) - log = true; - if (strcasecmp(sep->arg[2], "silent") == 0) - silent = true; - } - - bool success = target->InterrogateInventory(c, log, silent, allowtrip, error); - - if (!success) - c->Message(Chat::Red, "An unknown error occurred while processing Client::InterrogateInventory()"); -} - -void command_invsnapshot(Client *c, const Seperator *sep) -{ - if (!c) - return; - - if (sep->argnum == 0 || strcmp(sep->arg[1], "help") == 0) { - std::string window_title = "Inventory Snapshot Argument Help Menu"; - - std::string window_text = - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - ""; - - if (c->Admin() >= commandInvSnapshot) - window_text.append( - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - ); - - window_text.append( - "
Usage:#invsnapshot arguments
(required optional)
helpthis menu
capturetakes snapshot of character inventory
gcountreturns global snapshot count
gclear
now
delete all snapshots - rule
delete all snapshots - now
countreturns character snapshot count
clear
now
delete character snapshots - rule
delete character snapshots - now
list
count
lists entry ids for current character
limits to count
parsetstmpdisplays slots and items in snapshot
comparetstmpcompares inventory against snapshot
restoretstmprestores slots and items in snapshot
" - ); - - c->SendPopupToClient(window_title.c_str(), window_text.c_str()); - - return; - } - - if (c->Admin() >= commandInvSnapshot) { // global arguments - - if (strcmp(sep->arg[1], "gcount") == 0) { - auto is_count = database.CountInvSnapshots(); - c->Message(Chat::White, "There %s %i inventory snapshot%s.", (is_count == 1 ? "is" : "are"), is_count, (is_count == 1 ? "" : "s")); - - return; - } - - if (strcmp(sep->arg[1], "gclear") == 0) { - if (strcmp(sep->arg[2], "now") == 0) { - database.ClearInvSnapshots(true); - c->Message(Chat::White, "Inventory snapshots cleared using current time."); - } - else { - database.ClearInvSnapshots(); - c->Message(Chat::White, "Inventory snapshots cleared using RuleI(Character, InvSnapshotHistoryD) (%i day%s).", - RuleI(Character, InvSnapshotHistoryD), (RuleI(Character, InvSnapshotHistoryD) == 1 ? "" : "s")); - } - - return; - } - } - - if (!c->GetTarget() || !c->GetTarget()->IsClient()) { - c->Message(Chat::White, "Target must be a client."); - return; - } - - auto tc = (Client*)c->GetTarget(); - - if (strcmp(sep->arg[1], "capture") == 0) { - if (database.SaveCharacterInvSnapshot(tc->CharacterID())) { - tc->SetNextInvSnapshot(RuleI(Character, InvSnapshotMinIntervalM)); - c->Message(Chat::White, "Successful inventory snapshot taken of %s - setting next interval for %i minute%s.", - tc->GetName(), RuleI(Character, InvSnapshotMinIntervalM), (RuleI(Character, InvSnapshotMinIntervalM) == 1 ? "" : "s")); - } - else { - tc->SetNextInvSnapshot(RuleI(Character, InvSnapshotMinRetryM)); - c->Message(Chat::White, "Failed to take inventory snapshot of %s - retrying in %i minute%s.", - tc->GetName(), RuleI(Character, InvSnapshotMinRetryM), (RuleI(Character, InvSnapshotMinRetryM) == 1 ? "" : "s")); - } - - return; - } - - if (c->Admin() >= commandInvSnapshot) { - if (strcmp(sep->arg[1], "count") == 0) { - auto is_count = database.CountCharacterInvSnapshots(tc->CharacterID()); - c->Message(Chat::White, "%s (id: %u) has %i inventory snapshot%s.", tc->GetName(), tc->CharacterID(), is_count, (is_count == 1 ? "" : "s")); - - return; - } - - if (strcmp(sep->arg[1], "clear") == 0) { - if (strcmp(sep->arg[2], "now") == 0) { - database.ClearCharacterInvSnapshots(tc->CharacterID(), true); - c->Message(Chat::White, "%s\'s (id: %u) inventory snapshots cleared using current time.", tc->GetName(), tc->CharacterID()); - } - else { - database.ClearCharacterInvSnapshots(tc->CharacterID()); - c->Message(Chat::White, "%s\'s (id: %u) inventory snapshots cleared using RuleI(Character, InvSnapshotHistoryD) (%i day%s).", - tc->GetName(), tc->CharacterID(), RuleI(Character, InvSnapshotHistoryD), (RuleI(Character, InvSnapshotHistoryD) == 1 ? "" : "s")); - } - - return; - } - - if (strcmp(sep->arg[1], "list") == 0) { - std::list> is_list; - database.ListCharacterInvSnapshots(tc->CharacterID(), is_list); - - if (is_list.empty()) { - c->Message(Chat::White, "No inventory snapshots for %s (id: %u)", tc->GetName(), tc->CharacterID()); - return; - } - - auto list_count = 0; - if (sep->IsNumber(2)) - list_count = atoi(sep->arg[2]); - if (list_count < 1 || list_count > is_list.size()) - list_count = is_list.size(); - - std::string window_title = StringFormat("Snapshots for %s", tc->GetName()); - - std::string window_text = - "" - "" - "" - "" - ""; - - for (auto iter : is_list) { - if (!list_count) - break; - - window_text.append(StringFormat( - "" - "" - "" - "", - iter.first, - iter.second - )); - - --list_count; - } - - window_text.append( - "
TimestampEntry Count
%u%i
" - ); - - c->SendPopupToClient(window_title.c_str(), window_text.c_str()); - - return; - } - - if (strcmp(sep->arg[1], "parse") == 0) { - if (!sep->IsNumber(2)) { - c->Message(Chat::White, "A timestamp is required to use this option."); - return; - } - - uint32 timestamp = atoul(sep->arg[2]); - - if (!database.ValidateCharacterInvSnapshotTimestamp(tc->CharacterID(), timestamp)) { - c->Message(Chat::White, "No inventory snapshots for %s (id: %u) exist at %u.", tc->GetName(), tc->CharacterID(), timestamp); - return; - } - - std::list> parse_list; - database.ParseCharacterInvSnapshot(tc->CharacterID(), timestamp, parse_list); - - std::string window_title = StringFormat("Snapshot Parse for %s @ %u", tc->GetName(), timestamp); - - std::string window_text = "Slot: ItemID - Description
"; - - for (auto iter : parse_list) { - auto item_data = database.GetItem(iter.second); - std::string window_line = StringFormat("%i: %u - %s
", iter.first, iter.second, (item_data ? item_data->Name : "[error]")); - - if (window_text.length() + window_line.length() < 4095) { - window_text.append(window_line); - } - else { - c->Message(Chat::White, "Too many snapshot entries to list..."); - break; - } - } - - c->SendPopupToClient(window_title.c_str(), window_text.c_str()); - - return; - } - - if (strcmp(sep->arg[1], "compare") == 0) { - if (!sep->IsNumber(2)) { - c->Message(Chat::White, "A timestamp is required to use this option."); - return; - } - - uint32 timestamp = atoul(sep->arg[2]); - - if (!database.ValidateCharacterInvSnapshotTimestamp(tc->CharacterID(), timestamp)) { - c->Message(Chat::White, "No inventory snapshots for %s (id: %u) exist at %u.", tc->GetName(), tc->CharacterID(), timestamp); - return; - } - - std::list> inv_compare_list; - database.DivergeCharacterInventoryFromInvSnapshot(tc->CharacterID(), timestamp, inv_compare_list); - - std::list> iss_compare_list; - database.DivergeCharacterInvSnapshotFromInventory(tc->CharacterID(), timestamp, iss_compare_list); - - std::string window_title = StringFormat("Snapshot Comparison for %s @ %u", tc->GetName(), timestamp); - - std::string window_text = "Slot: (action) Snapshot -> Inventory
"; - - auto inv_iter = inv_compare_list.begin(); - auto iss_iter = iss_compare_list.begin(); - - while (true) { - std::string window_line; - - if (inv_iter == inv_compare_list.end() && iss_iter == iss_compare_list.end()) { - break; - } - else if (inv_iter != inv_compare_list.end() && iss_iter == iss_compare_list.end()) { - window_line = StringFormat("%i: (delete) [empty] -> %u
", inv_iter->first, inv_iter->second); - ++inv_iter; - } - else if (inv_iter == inv_compare_list.end() && iss_iter != iss_compare_list.end()) { - window_line = StringFormat("%i: (insert) %u -> [empty]
", iss_iter->first, iss_iter->second); - ++iss_iter; - } - else { - if (inv_iter->first < iss_iter->first) { - window_line = StringFormat("%i: (delete) [empty] -> %u
", inv_iter->first, inv_iter->second); - ++inv_iter; - } - else if (inv_iter->first > iss_iter->first) { - window_line = StringFormat("%i: (insert) %u -> [empty]
", iss_iter->first, iss_iter->second); - ++iss_iter; - } - else { - window_line = StringFormat("%i: (replace) %u -> %u
", iss_iter->first, iss_iter->second, inv_iter->second); - ++inv_iter; - ++iss_iter; - } - } - - if (window_text.length() + window_line.length() < 4095) { - window_text.append(window_line); - } - else { - c->Message(Chat::White, "Too many comparison entries to list..."); - break; - } - } - - c->SendPopupToClient(window_title.c_str(), window_text.c_str()); - - return; - } - - if (strcmp(sep->arg[1], "restore") == 0) { - if (!sep->IsNumber(2)) { - c->Message(Chat::White, "A timestamp is required to use this option."); - return; - } - - uint32 timestamp = atoul(sep->arg[2]); - - if (!database.ValidateCharacterInvSnapshotTimestamp(tc->CharacterID(), timestamp)) { - c->Message(Chat::White, "No inventory snapshots for %s (id: %u) exist at %u.", tc->GetName(), tc->CharacterID(), timestamp); - return; - } - - if (database.SaveCharacterInvSnapshot(tc->CharacterID())) { - tc->SetNextInvSnapshot(RuleI(Character, InvSnapshotMinIntervalM)); - } - else { - c->Message(Chat::Red, "Failed to take pre-restore inventory snapshot of %s (id: %u).", - tc->GetName(), tc->CharacterID()); - return; - } - - if (database.RestoreCharacterInvSnapshot(tc->CharacterID(), timestamp)) { - // cannot delete all valid item slots from client..so, we worldkick - tc->WorldKick(); // self restores update before the 'kick' is processed - - c->Message(Chat::White, "Successfully applied snapshot %u to %s's (id: %u) inventory.", - timestamp, tc->GetName(), tc->CharacterID()); - } - else { - c->Message(Chat::Red, "Failed to apply snapshot %u to %s's (id: %u) inventory.", - timestamp, tc->GetName(), tc->CharacterID()); - } - - return; - } - } -} - -void command_findnpctype(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - if (!arguments) { - c->Message(Chat::White, "Usage: #findnpctype [Search Criteria]"); - return; - } - - std::string query; - std::string search_criteria = sep->arg[1]; - if (sep->IsNumber(1)) { - query = fmt::format( - "SELECT id, name FROM npc_types WHERE id = {}", - search_criteria - ); - } else { - query = fmt::format( - "SELECT id, name FROM npc_types WHERE name LIKE '%%{}%%'", - search_criteria - ); - } - - auto results = content_db.QueryDatabase(query); - if (!results.Success() || !results.RowCount()) { - c->Message( - Chat::White, - fmt::format( - "No matches found for '{}'.", - search_criteria - ).c_str() - ); - return; - } - - int found_count = 0; - - for (auto row : results) { - int found_number = (found_count + 1); - if (found_count == 20) { - break; - } - - c->Message( - Chat::White, - fmt::format( - "NPC {} | {} ({})", - found_number, - row[1], - row[0] - ).c_str() - ); - found_count++; - } - - if (found_count == 20) { - c->Message(Chat::White, "20 NPCs were found, max reached."); - } else { - auto npc_message = ( - found_count == 1 ? - "An NPC was" : - fmt::format("{} NPCs were", found_count) - ); - - c->Message( - Chat::White, - fmt::format( - "{} found.", - npc_message - ).c_str() - ); - } -} - -void command_faction(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - if (!arguments) { - c->Message(Chat::White, "Usage: #faction review [Search Criteria | All] - Review Targeted Player's Faction Hits"); - c->Message(Chat::White, "Usage: #faction reset [Faction ID] - Reset Targeted Player's Faction to Base Faction Value"); - c->Message(Chat::White, "Usage: #faction view - Displays Target NPC's Primary Faction"); - return; - } - - std::string faction_filter; - if (sep->arg[2]) { - faction_filter = str_tolower(sep->arg[2]); - } - - if (!strcasecmp(sep->arg[1], "review")) { - if (!(c->GetTarget() && c->GetTarget()->IsClient())) { - c->Message(Chat::Red, "Player Target Required for faction review"); - return; - } - - Client* target = c->GetTarget()->CastToClient(); - uint32 character_id = target->CharacterID(); - std::string query; - if (!strcasecmp(faction_filter.c_str(), "all")) { - query = fmt::format( - "SELECT id, `name`, current_value FROM faction_list INNER JOIN faction_values ON faction_list.id = faction_values.faction_id WHERE char_id = {}", - character_id - ); - } else { - query = fmt::format( - "SELECT id, `name`, current_value FROM faction_list INNER JOIN faction_values ON faction_list.id = faction_values.faction_id WHERE `name` like '%{}%' and char_id = {}", - faction_filter.c_str(), - character_id - ); - } - - auto results = content_db.QueryDatabase(query); - if (!results.Success() || !results.RowCount()) { - c->Message(Chat::Yellow, "No faction hits found. All are at base level."); - return; - } - - uint32 found_count = 0; - for (auto row : results) { - uint32 faction_number = (found_count + 1); - auto faction_id = std::stoul(row[0]); - std::string faction_name = row[1]; - std::string faction_value = row[2]; - std::string reset_link = EQ::SayLinkEngine::GenerateQuestSaylink( - fmt::format("#faction reset {}", faction_id), - false, - "Reset" - ); - - c->Message( - Chat::White, - fmt::format( - "Faction {} | Name: {} ({}) Value: {} [{}]", - faction_number, - faction_name, - faction_id, - faction_value, - reset_link - ).c_str() - ); - found_count++; - } - - auto faction_message = ( - found_count > 0 ? - ( - found_count == 1 ? - "A Faction was" : - fmt::format("{} Factions were", found_count) - ) : - "No Factions were" - ); - c->Message( - Chat::White, - fmt::format( - "{} found.", - faction_message - ).c_str() - ); - } else if (!strcasecmp(sep->arg[1], "reset")) { - if (strlen(faction_filter.c_str()) > 0) { - if (c->GetTarget() && c->GetTarget()->IsClient()) { - Client* target = c->GetTarget()->CastToClient(); - if ( - ( - !c->GetFeigned() && - c->GetAggroCount() == 0 - ) || - ( - !target->GetFeigned() && - target->GetAggroCount() == 0 - ) - ) { - uint32 character_id = target->CharacterID(); - uint32 faction_id = std::stoul(faction_filter.c_str()); - if (target->ReloadCharacterFaction(target, faction_id, character_id)) { - c->Message( - Chat::White, - fmt::format( - "Faction Reset | {} ({}) was reset for {}.", - content_db.GetFactionName(faction_id), - faction_id, - target->GetCleanName() - ).c_str() - ); - } else { - c->Message( - Chat::White, - fmt::format( - "Faction Reset Failed | {} ({}) was unable to be reset for {}.", - content_db.GetFactionName(faction_id), - faction_id, - target->GetCleanName() - ).c_str() - ); - } - } else { - c->Message(Chat::White, "You cannot reset factions while you or your target is in combat or feigned."); - return; - } - } else { - c->Message(Chat::White, "You must target a PC for this command."); - return; - } - } else { - c->Message(Chat::White, "Usage: #faction reset [Faction ID] - Reset Targeted Player's Faction to Base Faction Value"); - } - } else if (!strcasecmp(sep->arg[1], "view")) { - if (c->GetTarget() && c->GetTarget()->IsNPC()) { - Mob* target = c->GetTarget(); - uint32 npc_id = target->GetNPCTypeID(); - uint32 npc_faction_id = target->CastToNPC()->GetPrimaryFaction(); - std::string npc_name = target->GetCleanName(); - c->Message( - Chat::White, - fmt::format( - "{} ({}) has a Primary Faction of {} ({}).", - npc_name, - npc_id, - content_db.GetFactionName(npc_faction_id), - npc_faction_id - ).c_str() - ); - } - } -} - -void command_findzone(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0] == 0) { - c->Message(Chat::White, "Usage: #findzone [search criteria]"); - c->Message(Chat::White, "Usage: #findzone expansion [expansion number]"); - return; - } - - std::string query; - int id = atoi((const char *) sep->arg[1]); - - std::string arg1 = sep->arg[1]; - - if (arg1 == "expansion") { - query = fmt::format( - "SELECT zoneidnumber, short_name, long_name, version FROM zone WHERE expansion = {}", - sep->arg[2] - ); - } - else { - - /** - * If id evaluates to 0, then search as if user entered a string - */ - if (id == 0) { - query = fmt::format( - "SELECT zoneidnumber, short_name, long_name, version FROM zone WHERE long_name LIKE '%{}%' OR `short_name` LIKE '%{}%'", - EscapeString(sep->arg[1]), - EscapeString(sep->arg[1]) - ); - } - else { - query = fmt::format( - "SELECT zoneidnumber, short_name, long_name, version FROM zone WHERE zoneidnumber = {}", - id - ); - } - } - - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Error querying database."); - c->Message(Chat::White, query.c_str()); - return; - } - - int count = 0; - const int maxrows = 100; - - for (auto row = results.begin(); row != results.end(); ++row) { - std::string zone_id = row[0]; - std::string short_name = row[1]; - std::string long_name = row[2]; - int version = atoi(row[3]); - - if (++count > maxrows) { - c->Message(Chat::White, "%i zones shown. Too many results.", maxrows); - break; - } - - std::string command_zone = EQ::SayLinkEngine::GenerateQuestSaylink("#zone " + short_name, false, "zone"); - std::string command_gmzone = EQ::SayLinkEngine::GenerateQuestSaylink( - fmt::format("#gmzone {} {}", short_name, version), - false, - "gmzone" - ); - - c->Message( - Chat::White, - fmt::format( - "[{}] [{}] [{}] ID ({}) Version ({}) [{}]", - (version == 0 ? command_zone : "zone"), - command_gmzone, - short_name, - zone_id, - version, - long_name - ).c_str() - ); - } - - if (count <= maxrows) { - c->Message( - Chat::White, - "Query complete. %i rows shown. %s", - count, - (arg1 == "expansion" ? "(expansion search)" : "")); - } - else if (count == 0) { - c->Message(Chat::White, "No matches found for %s.", sep->arg[1]); - } -} - -void command_viewnpctype(Client *c, const Seperator *sep) -{ - if (sep->IsNumber(1)) { - uint32 npc_id = std::stoul(sep->arg[1]); - const NPCType* npc_type_data = content_db.LoadNPCTypesData(npc_id); - if (npc_type_data) { - auto npc = new NPC( - npc_type_data, - nullptr, - c->GetPosition(), - GravityBehavior::Water - ); - npc->ShowStats(c); - } else { - c->Message( - Chat::White, - fmt::format( - "NPC ID {} was not found.", - npc_id - ).c_str() - ); - } - } else { - c->Message(Chat::White, "Usage: #viewnpctype [NPC ID]"); - } -} - -void command_reloadqst(Client *c, const Seperator *sep) -{ - bool stop_timers = false; - - if (sep->IsNumber(1)) { - stop_timers = std::stoi(sep->arg[1]) != 0 ? true : false; - } - - std::string stop_timers_message = stop_timers ? " and stopping timers" : ""; - c->Message( - Chat::White, - fmt::format( - "Clearing quest memory cache{}.", - stop_timers_message - ).c_str() - ); - entity_list.ClearAreas(); - parse->ReloadQuests(stop_timers); -} - -void command_corpsefix(Client *c, const Seperator *sep) -{ - entity_list.CorpseFix(c); -} - -void command_reloadworld(Client *c, const Seperator *sep) -{ - int world_repop = atoi(sep->arg[1]); - if (world_repop == 0) - c->Message(Chat::White, "Reloading quest cache worldwide."); - else - c->Message(Chat::White, "Reloading quest cache and repopping zones worldwide."); - - auto pack = new ServerPacket(ServerOP_ReloadWorld, sizeof(ReloadWorld_Struct)); - ReloadWorld_Struct* RW = (ReloadWorld_Struct*) pack->pBuffer; - RW->Option = world_repop; - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void command_reloadmerchants(Client *c, const Seperator *sep) { - entity_list.ReloadMerchants(); - c->Message(Chat::Yellow, "Reloading merchants."); -} - -void command_reloadlevelmods(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0] == 0) - { - if(RuleB(Zone, LevelBasedEXPMods)){ - zone->LoadLevelEXPMods(); - c->Message(Chat::Yellow, "Level based EXP Mods have been reloaded zonewide"); - }else{ - c->Message(Chat::Yellow, "Level based EXP Mods are disabled in rules!"); - } - } -} - -void command_reloadzps(Client *c, const Seperator *sep) -{ - content_db.LoadStaticZonePoints(&zone->zone_point_list, zone->GetShortName(), zone->GetInstanceVersion()); - c->Message(Chat::White, "Reloading server zone_points."); -} - -void command_zoneshutdown(Client *c, const Seperator *sep) -{ - if (!worldserver.Connected()) - c->Message(Chat::White, "Error: World server disconnected"); - else if (sep->arg[1][0] == 0) - c->Message(Chat::White, "Usage: #zoneshutdown zoneshortname"); - else { - auto pack = new ServerPacket(ServerOP_ZoneShutdown, sizeof(ServerZoneStateChange_struct)); - ServerZoneStateChange_struct* s = (ServerZoneStateChange_struct *) pack->pBuffer; - strcpy(s->adminname, c->GetName()); - if (sep->arg[1][0] >= '0' && sep->arg[1][0] <= '9') - s->ZoneServerID = atoi(sep->arg[1]); - else - s->zoneid = ZoneID(sep->arg[1]); - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - -void command_zonebootup(Client *c, const Seperator *sep) -{ - if (!worldserver.Connected()) - c->Message(Chat::White, "Error: World server disconnected"); - else if (sep->arg[2][0] == 0) { - c->Message(Chat::White, "Usage: #zonebootup ZoneServerID# zoneshortname"); - } - else { - auto pack = new ServerPacket(ServerOP_ZoneBootup, sizeof(ServerZoneStateChange_struct)); - ServerZoneStateChange_struct* s = (ServerZoneStateChange_struct *) pack->pBuffer; - s->ZoneServerID = atoi(sep->arg[1]); - strcpy(s->adminname, c->GetName()); - s->zoneid = ZoneID(sep->arg[2]); - s->makestatic = (bool) (strcasecmp(sep->arg[3], "static") == 0); - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - -void command_kick(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0] == 0) - c->Message(Chat::White, "Usage: #kick [charname]"); - else { - Client* client = entity_list.GetClientByName(sep->arg[1]); - if (client != 0) { - if (client->Admin() <= c->Admin()) { - client->Message(Chat::White, "You have been kicked by %s", c->GetName()); - auto outapp = new EQApplicationPacket(OP_GMKick, 0); - client->QueuePacket(outapp); - client->Kick("Ordered kicked by command"); - c->Message(Chat::White, "Kick: local: kicking %s", sep->arg[1]); - } - } - else if (!worldserver.Connected()) - c->Message(Chat::White, "Error: World server disconnected"); - else { - auto pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); - ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*) pack->pBuffer; - strcpy(skp->adminname, c->GetName()); - strcpy(skp->name, sep->arg[1]); - skp->adminrank = c->Admin(); - worldserver.SendPacket(pack); - safe_delete(pack); - } - } -} - -void command_attack(Client *c, const Seperator *sep) -{ - if (c->GetTarget() && c->GetTarget()->IsNPC() && sep->arg[1] != 0) { - Mob* sictar = entity_list.GetMob(sep->argplus[1]); - if (sictar) - c->GetTarget()->CastToNPC()->AddToHateList(sictar, 1, 0); - else - c->Message(Chat::White, "Error: %s not found", sep->arg[1]); - } - else - c->Message(Chat::White, "Usage: (needs NPC targeted) #attack targetname"); -} - -void command_lock(Client *c, const Seperator *sep) -{ - auto outpack = new ServerPacket(ServerOP_Lock, sizeof(ServerLock_Struct)); - ServerLock_Struct* lss = (ServerLock_Struct*) outpack->pBuffer; - strcpy(lss->myname, c->GetName()); - lss->mode = 1; - worldserver.SendPacket(outpack); - safe_delete(outpack); -} - -void command_unlock(Client *c, const Seperator *sep) -{ - auto outpack = new ServerPacket(ServerOP_Lock, sizeof(ServerLock_Struct)); - ServerLock_Struct* lss = (ServerLock_Struct*) outpack->pBuffer; - strcpy(lss->myname, c->GetName()); - lss->mode = 0; - worldserver.SendPacket(outpack); - safe_delete(outpack); -} - -void command_motd(Client *c, const Seperator *sep) -{ - auto outpack = new ServerPacket(ServerOP_Motd, sizeof(ServerMotd_Struct)); - ServerMotd_Struct* mss = (ServerMotd_Struct*) outpack->pBuffer; - strn0cpy(mss->myname, c->GetName(),64); - strn0cpy(mss->motd, sep->argplus[1],512); - worldserver.SendPacket(outpack); - safe_delete(outpack); -} - -void command_listpetition(Client *c, const Seperator *sep) -{ - std::string query = "SELECT petid, charname, accountname FROM petitions ORDER BY petid"; - auto results = database.QueryDatabase(query); - if (!results.Success()) - return; - - LogInfo("Petition list requested by [{}]", c->GetName()); - - if (results.RowCount() == 0) - return; - - c->Message(Chat::Red," ID : Character Name , Account Name"); - - for (auto row = results.begin(); row != results.end(); ++row) - c->Message(Chat::Yellow, " %s: %s , %s ", row[0],row[1],row[2]); -} - -void command_equipitem(Client *c, const Seperator *sep) -{ - uint32 slot_id = atoi(sep->arg[1]); - if (sep->IsNumber(1) && (slot_id >= EQ::invslot::EQUIPMENT_BEGIN && slot_id <= EQ::invslot::EQUIPMENT_END)) { - const EQ::ItemInstance* from_inst = c->GetInv().GetItem(EQ::invslot::slotCursor); - const EQ::ItemInstance* to_inst = c->GetInv().GetItem(slot_id); // added (desync issue when forcing stack to stack) - bool partialmove = false; - int16 movecount; - - if (from_inst && from_inst->IsClassCommon()) { - auto outapp = new EQApplicationPacket(OP_MoveItem, sizeof(MoveItem_Struct)); - MoveItem_Struct* mi = (MoveItem_Struct*)outapp->pBuffer; - mi->from_slot = EQ::invslot::slotCursor; - mi->to_slot = slot_id; - // mi->number_in_stack = from_inst->GetCharges(); // replaced with con check for stacking - - // crude stackable check to only 'move' the difference count on client instead of entire stack when applicable - if (to_inst && to_inst->IsStackable() && - (to_inst->GetItem()->ID == from_inst->GetItem()->ID) && - (to_inst->GetCharges() < to_inst->GetItem()->StackSize) && - (from_inst->GetCharges() > to_inst->GetItem()->StackSize - to_inst->GetCharges())) { - movecount = to_inst->GetItem()->StackSize - to_inst->GetCharges(); - mi->number_in_stack = (uint32)movecount; - partialmove = true; - } - else - mi->number_in_stack = from_inst->GetCharges(); - - // Save move changes - // Added conditional check to packet send..would have sent change even on a swap failure..whoops! - - if (partialmove) { // remove this con check if someone can figure out removing charges from cursor stack issue below - // mi->number_in_stack is always from_inst->GetCharges() when partialmove is false - c->Message(Chat::Red, "Error: Partial stack added to existing stack exceeds allowable stacksize"); - safe_delete(outapp); - return; - } - else if(c->SwapItem(mi)) { - c->FastQueuePacket(&outapp); - - // if the below code is still needed..just send an an item trade packet to each slot..it should overwrite the client instance - - // below code has proper logic, but client does not like to have cursor charges changed - // (we could delete the cursor item and resend, but issues would arise if there are queued items) - //if (partialmove) { - // EQApplicationPacket* outapp2 = new EQApplicationPacket(OP_DeleteItem, sizeof(DeleteItem_Struct)); - // DeleteItem_Struct* di = (DeleteItem_Struct*)outapp2->pBuffer; - // di->from_slot = SLOT_CURSOR; - // di->to_slot = 0xFFFFFFFF; - // di->number_in_stack = 0xFFFFFFFF; - - // c->Message(Chat::White, "Deleting %i charges from stack", movecount); // debug line..delete - - // for (int16 deletecount=0; deletecount < movecount; deletecount++) - // have to use 'movecount' because mi->number_in_stack is 'ENCODED' at this point (i.e., 99 charges returns 22...) - // c->QueuePacket(outapp2); - - // safe_delete(outapp2); - //} - } - else { - c->Message(Chat::Red, "Error: Unable to equip current item"); - } - safe_delete(outapp); - - // also send out a wear change packet? - } - else if (from_inst == nullptr) - c->Message(Chat::Red, "Error: There is no item on your cursor"); - else - c->Message(Chat::Red, "Error: Item on your cursor cannot be equipped"); - } - else - c->Message(Chat::White, "Usage: #equipitem slotid[0-21] - equips the item on your cursor to the position"); -} - -void command_zonelock(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - if (!arguments) { - c->Message(Chat::White, "Usage: #zonelock list - Lists Locked Zones"); - if (c->Admin() >= commandLockZones) { - c->Message(Chat::White, "Usage: #zonelock lock [Zone ID] or #zonelock lock [Zone Short Name] - Locks a Zone by ID or Short Name"); - c->Message(Chat::White, "Usage: #zonelock unlock [Zone ID] or #zonelock unlock [Zone Short Name] - Unlocks a Zone by ID or Short Name"); - } - return; - } - - std::string lock_type = str_tolower(sep->arg[1]); - bool is_list = lock_type.find("list") != std::string::npos; - bool is_lock = lock_type.find("lock") != std::string::npos; - bool is_unlock = lock_type.find("unlock") != std::string::npos; - if (!is_list && !is_lock && !is_unlock) { - c->Message(Chat::White, "Usage: #zonelock list - Lists Locked Zones"); - if (c->Admin() >= commandLockZones) { - c->Message(Chat::White, "Usage: #zonelock lock [Zone ID] or #zonelock lock [Zone Short Name] - Locks a Zone by ID or Short Name"); - c->Message(Chat::White, "Usage: #zonelock unlock [Zone ID] or #zonelock unlock [Zone Short Name] - Unlocks a Zone by ID or Short Name"); - } - return; - } - - auto pack = new ServerPacket(ServerOP_LockZone, sizeof(ServerLockZone_Struct)); - ServerLockZone_Struct* lock_zone = (ServerLockZone_Struct*) pack->pBuffer; - strn0cpy(lock_zone->adminname, c->GetName(), sizeof(lock_zone->adminname)); - - if (is_list) { - lock_zone->op = ServerLockType::List; - worldserver.SendPacket(pack); - } else if (!is_list && c->Admin() >= commandLockZones) { - auto zone_id = ( - sep->IsNumber(2) ? - static_cast(std::stoul(sep->arg[2])) : - static_cast(ZoneID(sep->arg[2])) - ); - std::string zone_short_name = str_tolower(ZoneName(zone_id, true)); - bool is_unknown_zone = zone_short_name.find("unknown") != std::string::npos; - if (zone_id && !is_unknown_zone) { - lock_zone->op = is_lock ? ServerLockType::Lock : ServerLockType::Unlock; - lock_zone->zoneID = zone_id; - worldserver.SendPacket(pack); - } else { - c->Message( - Chat::White, - fmt::format( - "Usage: #zonelock {} [Zone ID] or #zonelock {} [Zone Short Name]", - is_lock ? "lock" : "unlock" - ).c_str() - ); - } - } - safe_delete(pack); -} - -void command_copycharacter(Client *c, const Seperator *sep) -{ - if (sep->argnum < 3) { - c->Message( - Chat::White, - "Usage: [source_character_name] [destination_character_name] [destination_account_name]" - ); - return; - } - - std::string source_character_name = sep->arg[1]; - std::string destination_character_name = sep->arg[2]; - std::string destination_account_name = sep->arg[3]; - - bool result = database.CopyCharacter( - source_character_name, - destination_character_name, - destination_account_name - ); - - c->Message( - Chat::Yellow, - fmt::format( - "Character Copy [{}] to [{}] via account [{}] [{}]", - source_character_name, - destination_character_name, - destination_account_name, - result ? "Success" : "Failed" - ).c_str() - ); -} - -void command_corpse(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - - if (strcasecmp(sep->arg[1], "DeletePlayerCorpses") == 0 && c->Admin() >= commandEditPlayerCorpses) { - int32 tmp = entity_list.DeletePlayerCorpses(); - if (tmp >= 0) - c->Message(Chat::White, "%i corpses deleted.", tmp); - else - c->Message(Chat::White, "DeletePlayerCorpses Error #%i", tmp); - } - else if (strcasecmp(sep->arg[1], "delete") == 0) { - if (target == 0 || !target->IsCorpse()) - c->Message(Chat::White, "Error: Target the corpse you wish to delete"); - else if (target->IsNPCCorpse()) { - - c->Message(Chat::White, "Depoping %s.", target->GetName()); - target->CastToCorpse()->Delete(); - } - else if (c->Admin() >= commandEditPlayerCorpses) { - c->Message(Chat::White, "Deleting %s.", target->GetName()); - target->CastToCorpse()->Delete(); - } - else - c->Message(Chat::White, "Insufficient status to delete player corpse."); - } - else if (strcasecmp(sep->arg[1], "ListNPC") == 0) { - entity_list.ListNPCCorpses(c); - } - else if (strcasecmp(sep->arg[1], "ListPlayer") == 0) { - entity_list.ListPlayerCorpses(c); - } - else if (strcasecmp(sep->arg[1], "DeleteNPCCorpses") == 0) { - int32 tmp = entity_list.DeleteNPCCorpses(); - if (tmp >= 0) - c->Message(Chat::White, "%d corpses deleted.", tmp); - else - c->Message(Chat::White, "DeletePlayerCorpses Error #%d", tmp); - } - else if (strcasecmp(sep->arg[1], "charid") == 0 && c->Admin() >= commandEditPlayerCorpses) { - if (target == 0 || !target->IsPlayerCorpse()) - c->Message(Chat::White, "Error: Target must be a player corpse."); - else if (!sep->IsNumber(2)) - c->Message(Chat::White, "Error: charid must be a number."); - else - c->Message(Chat::White, "Setting CharID=%u on PlayerCorpse '%s'", target->CastToCorpse()->SetCharID(atoi(sep->arg[2])), target->GetName()); - } - else if (strcasecmp(sep->arg[1], "ResetLooter") == 0) { - if (target == 0 || !target->IsCorpse()) - c->Message(Chat::White, "Error: Target the corpse you wish to reset"); - else - target->CastToCorpse()->ResetLooter(); - } - else if (strcasecmp(sep->arg[1], "RemoveCash") == 0) { - if (target == 0 || !target->IsCorpse()) - c->Message(Chat::White, "Error: Target the corpse you wish to remove the cash from"); - else if (!target->IsPlayerCorpse() || c->Admin() >= commandEditPlayerCorpses) { - c->Message(Chat::White, "Removing Cash from %s.", target->GetName()); - target->CastToCorpse()->RemoveCash(); - } - else - c->Message(Chat::White, "Insufficient status to modify player corpse."); - } - else if (strcasecmp(sep->arg[1], "InspectLoot") == 0) { - if (target == 0 || !target->IsCorpse()) - c->Message(Chat::White, "Error: Target must be a corpse."); - else - target->CastToCorpse()->QueryLoot(c); - } - else if (strcasecmp(sep->arg[1], "lock") == 0) { - if (target == 0 || !target->IsCorpse()) - c->Message(Chat::White, "Error: Target must be a corpse."); - else { - target->CastToCorpse()->Lock(); - c->Message(Chat::White, "Locking %s...", target->GetName()); - } - } - else if (strcasecmp(sep->arg[1], "unlock") == 0) { - if (target == 0 || !target->IsCorpse()) - c->Message(Chat::White, "Error: Target must be a corpse."); - else { - target->CastToCorpse()->UnLock(); - c->Message(Chat::White, "Unlocking %s...", target->GetName()); - } - } - else if (strcasecmp(sep->arg[1], "depop") == 0) { - if (target == 0 || !target->IsPlayerCorpse()) - c->Message(Chat::White, "Error: Target must be a player corpse."); - else if (c->Admin() >= commandEditPlayerCorpses && target->IsPlayerCorpse()) { - c->Message(Chat::White, "Depoping %s.", target->GetName()); - target->CastToCorpse()->DepopPlayerCorpse(); - if(!sep->arg[2][0] || atoi(sep->arg[2]) != 0) - target->CastToCorpse()->Bury(); - } - else - c->Message(Chat::White, "Insufficient status to depop player corpse."); - } - else if (strcasecmp(sep->arg[1], "depopall") == 0) { - if (target == 0 || !target->IsClient()) - c->Message(Chat::White, "Error: Target must be a player."); - else if (c->Admin() >= commandEditPlayerCorpses && target->IsClient()) { - c->Message(Chat::White, "Depoping %s\'s corpses.", target->GetName()); - target->CastToClient()->DepopAllCorpses(); - if(!sep->arg[2][0] || atoi(sep->arg[2]) != 0) - target->CastToClient()->BuryPlayerCorpses(); - } - else - c->Message(Chat::White, "Insufficient status to depop player corpse."); - - } - else if (strcasecmp(sep->arg[1], "moveallgraveyard") == 0) { - int count = entity_list.MovePlayerCorpsesToGraveyard(true); - c->Message(Chat::White, "Moved [%d] player corpse(s) to zone graveyard", count); - } - else if (sep->arg[1][0] == 0 || strcasecmp(sep->arg[1], "help") == 0) { - c->Message(Chat::White, "#Corpse Sub-Commands:"); - c->Message(Chat::White, " DeleteNPCCorpses"); - c->Message(Chat::White, " Delete - Delete targetted corpse"); - c->Message(Chat::White, " ListNPC"); - c->Message(Chat::White, " ListPlayer"); - c->Message(Chat::White, " Lock - GM locks the corpse - cannot be looted by non-GM"); - c->Message(Chat::White, " MoveAllGraveyard - move all player corpses to zone's graveyard or non-instance"); - c->Message(Chat::White, " UnLock"); - c->Message(Chat::White, " RemoveCash"); - c->Message(Chat::White, " InspectLoot"); - c->Message(Chat::White, " [to remove items from corpses, loot them]"); - c->Message(Chat::White, "Lead-GM status required to delete/modify player corpses"); - c->Message(Chat::White, " DeletePlayerCorpses"); - c->Message(Chat::White, " CharID [charid] - change player corpse's owner"); - c->Message(Chat::White, " Depop [bury] - Depops single target corpse."); - c->Message(Chat::White, " Depopall [bury] - Depops all target player's corpses."); - c->Message(Chat::White, "Set bury to 0 to skip burying the corpses."); - } - else - c->Message(Chat::White, "Error, #corpse sub-command not found"); -} - -void command_fixmob(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - const char* Usage = "Usage: #fixmob [race|gender|texture|helm|face|hair|haircolor|beard|beardcolor|heritage|tattoo|detail] [next|prev]"; - - if (!sep->arg[1]) - c->Message(Chat::White,Usage); - else if (!target) - c->Message(Chat::White,"Error: this command requires a target"); - else - { - - uint32 Adjustment = 1; // Previous or Next - char codeMove = 0; - - if (sep->arg[2]) - { - char* command2 = sep->arg[2]; - codeMove = (command2[0] | 0x20); // First character, lower-cased - if (codeMove == 'n') - Adjustment = 1; - else if (codeMove == 'p') - Adjustment = -1; - } - - uint16 Race = target->GetRace(); - uint8 Gender = target->GetGender(); - uint8 Texture = 0xFF; - uint8 HelmTexture = 0xFF; - uint8 HairColor = target->GetHairColor(); - uint8 BeardColor = target->GetBeardColor(); - uint8 EyeColor1 = target->GetEyeColor1(); - uint8 EyeColor2 = target->GetEyeColor2(); - uint8 HairStyle = target->GetHairStyle(); - uint8 LuclinFace = target->GetLuclinFace(); - uint8 Beard = target->GetBeard(); - uint32 DrakkinHeritage = target->GetDrakkinHeritage(); - uint32 DrakkinTattoo = target->GetDrakkinTattoo(); - uint32 DrakkinDetails = target->GetDrakkinDetails(); - - const char* ChangeType = nullptr; // If it's still nullptr after processing, they didn't send a valid command - uint32 ChangeSetting; - char* command = sep->arg[1]; - - if (strcasecmp(command, "race") == 0) - { - if (Race == 1 && codeMove == 'p') - Race = RuleI(NPC, MaxRaceID); - else if (Race >= RuleI(NPC, MaxRaceID) && codeMove != 'p') - Race = 1; - else - Race += Adjustment; - ChangeType = "Race"; - ChangeSetting = Race; - } - else if (strcasecmp(command, "gender") == 0) - { - if (Gender == 0 && codeMove == 'p') - Gender = 2; - else if (Gender >= 2 && codeMove != 'p') - Gender = 0; - else - Gender += Adjustment; - ChangeType = "Gender"; - ChangeSetting = Gender; - } - else if (strcasecmp(command, "texture") == 0) - { - Texture = target->GetTexture(); - - if (Texture == 0 && codeMove == 'p') - Texture = 25; - else if (Texture >= 25 && codeMove != 'p') - Texture = 0; - else - Texture += Adjustment; - ChangeType = "Texture"; - ChangeSetting = Texture; - } - else if (strcasecmp(command, "helm") == 0) - { - HelmTexture = target->GetHelmTexture(); - if (HelmTexture == 0 && codeMove == 'p') - HelmTexture = 25; - else if (HelmTexture >= 25 && codeMove != 'p') - HelmTexture = 0; - else - HelmTexture += Adjustment; - ChangeType = "HelmTexture"; - ChangeSetting = HelmTexture; - } - else if (strcasecmp(command, "face") == 0) - { - if (LuclinFace == 0 && codeMove == 'p') - LuclinFace = 87; - else if (LuclinFace >= 87 && codeMove != 'p') - LuclinFace = 0; - else - LuclinFace += Adjustment; - ChangeType = "LuclinFace"; - ChangeSetting = LuclinFace; - } - else if (strcasecmp(command, "hair") == 0) - { - if (HairStyle == 0 && codeMove == 'p') - HairStyle = 8; - else if (HairStyle >= 8 && codeMove != 'p') - HairStyle = 0; - else - HairStyle += Adjustment; - ChangeType = "HairStyle"; - ChangeSetting = HairStyle; - } - else if (strcasecmp(command, "haircolor") == 0) - { - if (HairColor == 0 && codeMove == 'p') - HairColor = 24; - else if (HairColor >= 24 && codeMove != 'p') - HairColor = 0; - else - HairColor += Adjustment; - ChangeType = "HairColor"; - ChangeSetting = HairColor; - } - else if (strcasecmp(command, "beard") == 0) - { - if (Beard == 0 && codeMove == 'p') - Beard = 11; - else if (Beard >= 11 && codeMove != 'p') - Beard = 0; - else - Beard += Adjustment; - ChangeType = "Beard"; - ChangeSetting = Beard; - } - else if (strcasecmp(command, "beardcolor") == 0) - { - if (BeardColor == 0 && codeMove == 'p') - BeardColor = 24; - else if (BeardColor >= 24 && codeMove != 'p') - BeardColor = 0; - else - BeardColor += Adjustment; - ChangeType = "BeardColor"; - ChangeSetting = BeardColor; - } - else if (strcasecmp(command, "heritage") == 0) - { - if (DrakkinHeritage == 0 && codeMove == 'p') - DrakkinHeritage = 6; - else if (DrakkinHeritage >= 6 && codeMove != 'p') - DrakkinHeritage = 0; - else - DrakkinHeritage += Adjustment; - ChangeType = "DrakkinHeritage"; - ChangeSetting = DrakkinHeritage; - } - else if (strcasecmp(command, "tattoo") == 0) - { - if (DrakkinTattoo == 0 && codeMove == 'p') - DrakkinTattoo = 8; - else if (DrakkinTattoo >= 8 && codeMove != 'p') - DrakkinTattoo = 0; - else - DrakkinTattoo += Adjustment; - ChangeType = "DrakkinTattoo"; - ChangeSetting = DrakkinTattoo; - } - else if (strcasecmp(command, "detail") == 0) - { - if (DrakkinDetails == 0 && codeMove == 'p') - DrakkinDetails = 7; - else if (DrakkinDetails >= 7 && codeMove != 'p') - DrakkinDetails = 0; - else - DrakkinDetails += Adjustment; - ChangeType = "DrakkinDetails"; - ChangeSetting = DrakkinDetails; - } - - // Hack to fix some races that base features from face - switch (Race) - { - case 2: // Barbarian - if (LuclinFace > 10) { - LuclinFace -= ((DrakkinTattoo - 1) * 10); - } - LuclinFace += (DrakkinTattoo * 10); - break; - case 3: // Erudite - if (LuclinFace > 10) { - LuclinFace -= ((HairStyle - 1) * 10); - } - LuclinFace += (HairStyle * 10); - break; - case 5: // HighElf - case 6: // DarkElf - case 7: // HalfElf - if (LuclinFace > 10) { - LuclinFace -= ((Beard - 1) * 10); - } - LuclinFace += (Beard * 10); - break; - default: - break; - } - - - if (ChangeType == nullptr) - { - c->Message(Chat::White,Usage); - } - else - { - target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, - EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, - DrakkinHeritage, DrakkinTattoo, DrakkinDetails); - - c->Message(Chat::White, "%s=%i", ChangeType, ChangeSetting); - } - } -} - -void command_gmspeed(Client *c, const Seperator *sep) -{ - bool state = atobool(sep->arg[1]); - Client *t = c; - - if (c->GetTarget() && c->GetTarget()->IsClient()) { - t = c->GetTarget()->CastToClient(); - } - - if (sep->arg[1][0] != 0) { - database.SetGMSpeed(t->AccountID(), state ? 1 : 0); - c->Message(Chat::White, "Turning GMSpeed %s for %s (zone to take effect)", state ? "On" : "Off", t->GetName()); - } - else { - c->Message(Chat::White, "Usage: #gmspeed [on/off]"); - } -} - -void command_gmzone(Client *c, const Seperator *sep) -{ - if (!sep->arg[1]) { - c->Message(Chat::White, "Usage"); - c->Message(Chat::White, "-------"); - c->Message(Chat::White, "#gmzone [zone_short_name] [zone_version=0]"); - return; - } - - std::string zone_short_name_string = sep->arg[1]; - const char *zone_short_name = sep->arg[1]; - auto zone_version = static_cast(sep->arg[2] ? atoi(sep->arg[2]) : 0); - std::string identifier = "gmzone"; - uint32 zone_id = ZoneID(zone_short_name); - uint32 duration = 100000000; - uint16 instance_id = 0; - - if (zone_id == 0) { - c->Message(Chat::Red, "Invalid zone specified"); - return; - } - - if (sep->arg[3] && sep->arg[3][0]) { - identifier = sep->arg[3]; - } - - std::string bucket_key = StringFormat("%s-%s-%u-instance", zone_short_name, identifier.c_str(), zone_version); - std::string existing_zone_instance = DataBucket::GetData(bucket_key); - - if (existing_zone_instance.length() > 0) { - instance_id = std::stoi(existing_zone_instance); - - c->Message(Chat::Yellow, "Found already created instance (%s) (%u)", zone_short_name, instance_id); - } - - if (instance_id == 0) { - if (!database.GetUnusedInstanceID(instance_id)) { - c->Message(Chat::Red, "Server was unable to find a free instance id."); - return; - } - - if (!database.CreateInstance(instance_id, zone_id, zone_version, duration)) { - c->Message(Chat::Red, "Server was unable to create a new instance."); - return; - } - - c->Message(Chat::Yellow, "New private GM instance %s was created with id %lu.", zone_short_name, (unsigned long) instance_id); - DataBucket::SetData(bucket_key, std::to_string(instance_id)); - } - - if (instance_id > 0) { - float target_x = -1, target_y = -1, target_z = -1, target_heading = -1; - int16 min_status = AccountStatus::Player; - uint8 min_level = 0; - - if (!content_db.GetSafePoints( - zone_short_name, - zone_version, - &target_x, - &target_y, - &target_z, - &target_heading, - &min_status, - &min_level - )) { - c->Message(Chat::Red, "Failed to find safe coordinates for specified zone"); - } - - c->Message(Chat::Yellow, "Zoning to private GM instance (%s) (%u)", zone_short_name, instance_id); - - c->AssignToInstance(instance_id); - c->MovePC(zone_id, instance_id, target_x, target_y, target_z, target_heading, 1); - } -} - -void command_title(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0]==0) - c->Message(Chat::White, "Usage: #title [remove|text] [1 = Create row in title table] - remove or set title to 'text'"); - else { - bool Save = (atoi(sep->arg[2]) == 1); - - Mob *target_mob = c->GetTarget(); - if(!target_mob) - target_mob = c; - if(!target_mob->IsClient()) { - c->Message(Chat::Red, "#title only works on players."); - return; - } - Client *t = target_mob->CastToClient(); - - if(strlen(sep->arg[1]) > 31) { - c->Message(Chat::Red, "Title must be 31 characters or less."); - return; - } - - bool removed = false; - if(!strcasecmp(sep->arg[1], "remove")) { - t->SetAATitle(""); - removed = true; - } else { - for(unsigned int i=0; iarg[1]); i++) - if(sep->arg[1][i]=='_') - sep->arg[1][i] = ' '; - if(!Save) - t->SetAATitle(sep->arg[1]); - else - title_manager.CreateNewPlayerTitle(t, sep->arg[1]); - } - - t->Save(); - - if(removed) { - c->Message(Chat::Red, "%s's title has been removed.", t->GetName(), sep->arg[1]); - if(t != c) - t->Message(Chat::Red, "Your title has been removed.", sep->arg[1]); - } else { - c->Message(Chat::Red, "%s's title has been changed to '%s'.", t->GetName(), sep->arg[1]); - if(t != c) - t->Message(Chat::Red, "Your title has been changed to '%s'.", sep->arg[1]); - } - } -} - - -void command_titlesuffix(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0]==0) - c->Message(Chat::White, "Usage: #titlesuffix [remove|text] [1 = create row in title table] - remove or set title suffix to 'text'"); - else { - bool Save = (atoi(sep->arg[2]) == 1); - - Mob *target_mob = c->GetTarget(); - if(!target_mob) - target_mob = c; - if(!target_mob->IsClient()) { - c->Message(Chat::Red, "#titlesuffix only works on players."); - return; - } - Client *t = target_mob->CastToClient(); - - if(strlen(sep->arg[1]) > 31) { - c->Message(Chat::Red, "Title suffix must be 31 characters or less."); - return; - } - - bool removed = false; - if(!strcasecmp(sep->arg[1], "remove")) { - t->SetTitleSuffix(""); - removed = true; - } else { - for(unsigned int i=0; iarg[1]); i++) - if(sep->arg[1][i]=='_') - sep->arg[1][i] = ' '; - - if(!Save) - t->SetTitleSuffix(sep->arg[1]); - else - title_manager.CreateNewPlayerSuffix(t, sep->arg[1]); - } - - t->Save(); - - if(removed) { - c->Message(Chat::Red, "%s's title suffix has been removed.", t->GetName(), sep->arg[1]); - if(t != c) - t->Message(Chat::Red, "Your title suffix has been removed.", sep->arg[1]); - } else { - c->Message(Chat::Red, "%s's title suffix has been changed to '%s'.", t->GetName(), sep->arg[1]); - if(t != c) - t->Message(Chat::Red, "Your title suffix has been changed to '%s'.", sep->arg[1]); - } - } -} - -void command_spellinfo(Client *c, const Seperator *sep) -{ - if(sep->arg[1][0]==0) - c->Message(Chat::White, "Usage: #spellinfo [spell_id]"); - else { - short int spell_id=atoi(sep->arg[1]); - const struct SPDat_Spell_Struct *s=&spells[spell_id]; - c->Message(Chat::White, "Spell info for spell #%d:", spell_id); - c->Message(Chat::White, " name: %s", s->name); - c->Message(Chat::White, " player_1: %s", s->player_1); - c->Message(Chat::White, " teleport_zone: %s", s->teleport_zone); - c->Message(Chat::White, " you_cast: %s", s->you_cast); - c->Message(Chat::White, " other_casts: %s", s->other_casts); - c->Message(Chat::White, " cast_on_you: %s", s->cast_on_you); - c->Message(Chat::White, " spell_fades: %s", s->spell_fades); - c->Message(Chat::White, " range: %f", s->range); - c->Message(Chat::White, " aoe_range: %f", s->aoe_range); - c->Message(Chat::White, " push_back: %f", s->push_back); - c->Message(Chat::White, " push_up: %f", s->push_up); - c->Message(Chat::White, " cast_time: %d", s->cast_time); - c->Message(Chat::White, " recovery_time: %d", s->recovery_time); - c->Message(Chat::White, " recast_time: %d", s->recast_time); - c->Message(Chat::White, " buff_duration_formula: %d", s->buff_duration_formula); - c->Message(Chat::White, " buff_duration: %d", s->buff_duration); - c->Message(Chat::White, " AEDuration: %d", s->aoe_duration); - c->Message(Chat::White, " mana: %d", s->mana); - c->Message(Chat::White, " base[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", s->base_value[0], s->base_value[1], s->base_value[2], s->base_value[3], s->base_value[4], s->base_value[5], s->base_value[6], s->base_value[7], s->base_value[8], s->base_value[9], s->base_value[10], s->base_value[11]); - c->Message(Chat::White, " base22[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", s->limit_value[0], s->limit_value[1], s->limit_value[2], s->limit_value[3], s->limit_value[4], s->limit_value[5], s->limit_value[6], s->limit_value[7], s->limit_value[8], s->limit_value[9], s->limit_value[10], s->limit_value[11]); - c->Message(Chat::White, " max[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", s->max_value[0], s->max_value[1], s->max_value[2], s->max_value[3], s->max_value[4], s->max_value[5], s->max_value[6], s->max_value[7], s->max_value[8], s->max_value[9], s->max_value[10], s->max_value[11]); - c->Message(Chat::White, " components[4]: %d, %d, %d, %d", s->component[0], s->component[1], s->component[2], s->component[3]); - c->Message(Chat::White, " component_counts[4]: %d, %d, %d, %d", s->component_count[0], s->component_count[1], s->component_count[2], s->component_count[3]); - c->Message(Chat::White, " NoexpendReagent[4]: %d, %d, %d, %d", s->no_expend_reagent[0], s->no_expend_reagent[1], s->no_expend_reagent[2], s->no_expend_reagent[3]); - c->Message(Chat::White, " formula[12]: 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x", s->formula[0], s->formula[1], s->formula[2], s->formula[3], s->formula[4], s->formula[5], s->formula[6], s->formula[7], s->formula[8], s->formula[9], s->formula[10], s->formula[11]); - c->Message(Chat::White, " goodEffect: %d", s->good_effect); - c->Message(Chat::White, " Activated: %d", s->activated); - c->Message(Chat::White, " resisttype: %d", s->resist_type); - c->Message(Chat::White, " effectid[12]: 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x", s->effect_id[0], s->effect_id[1], s->effect_id[2], s->effect_id[3], s->effect_id[4], s->effect_id[5], s->effect_id[6], s->effect_id[7], s->effect_id[8], s->effect_id[9], s->effect_id[10], s->effect_id[11]); - c->Message(Chat::White, " targettype: %d", s->target_type); - c->Message(Chat::White, " basediff: %d", s->base_difficulty); - c->Message(Chat::White, " skill: %d", s->skill); - c->Message(Chat::White, " zonetype: %d", s->zone_type); - c->Message(Chat::White, " EnvironmentType: %d", s->environment_type); - c->Message(Chat::White, " TimeOfDay: %d", s->time_of_day); - c->Message(Chat::White, " classes[15]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", - s->classes[0], s->classes[1], s->classes[2], s->classes[3], s->classes[4], - s->classes[5], s->classes[6], s->classes[7], s->classes[8], s->classes[9], - s->classes[10], s->classes[11], s->classes[12], s->classes[13], s->classes[14]); - c->Message(Chat::White, " CastingAnim: %d", s->casting_animation); - c->Message(Chat::White, " SpellAffectIndex: %d", s->spell_affect_index); - c->Message(Chat::White, " RecourseLink: %d", s->recourse_link); - } -} - -void command_lastname(Client *c, const Seperator *sep) -{ - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient()) - t=c->GetTarget()->CastToClient(); - LogInfo("#lastname request from [{}] for [{}]", c->GetName(), t->GetName()); - - if(strlen(sep->arg[1]) <= 70) - t->ChangeLastName(sep->arg[1]); - else - c->Message(Chat::White, "Usage: #lastname where is less than 70 chars long"); -} - -void command_memspell(Client *c, const Seperator *sep) -{ - uint32 slot; - uint16 spell_id; - - if (!(sep->IsNumber(1) && sep->IsNumber(2))) - { - c->Message(Chat::White, "Usage: #MemSpell slotid spellid"); - } - else - { - slot = atoi(sep->arg[1]) - 1; - spell_id = atoi(sep->arg[2]); - if (slot > EQ::spells::SPELL_GEM_COUNT || spell_id >= SPDAT_RECORDS) - { - c->Message(Chat::White, "Error: #MemSpell: Arguement out of range"); - } - else - { - c->MemSpell(spell_id, slot); - c->Message(Chat::White, "Spell slot changed, have fun!"); - } - } -} -void command_save(Client *c, const Seperator *sep) -{ - if (c->GetTarget() == 0) - c->Message(Chat::White, "Error: no target"); - else if (c->GetTarget()->IsClient()) { - if (c->GetTarget()->CastToClient()->Save(2)) - c->Message(Chat::White, "%s successfully saved.", c->GetTarget()->GetName()); - else - c->Message(Chat::White, "Manual save for %s failed.", c->GetTarget()->GetName()); - } - else if (c->GetTarget()->IsPlayerCorpse()) { - if (c->GetTarget()->CastToMob()->Save()) - c->Message(Chat::White, "%s successfully saved. (dbid=%u)", c->GetTarget()->GetName(), c->GetTarget()->CastToCorpse()->GetCorpseDBID()); - else - c->Message(Chat::White, "Manual save for %s failed.", c->GetTarget()->GetName()); - } - else - c->Message(Chat::White, "Error: target not a Client/PlayerCorpse"); -} - -void command_showstats(Client *c, const Seperator *sep) -{ - if (c->GetTarget() != 0 ) - c->GetTarget()->ShowStats(c); - else - c->ShowStats(c); -} - -void command_showzonegloballoot(Client *c, const Seperator *sep) -{ - c->Message(Chat::White, "GlobalLoot for %s (%d:%d)", zone->GetShortName(), zone->GetZoneID(), zone->GetInstanceVersion()); - zone->ShowZoneGlobalLoot(c); -} - -void command_showzonepoints(Client *c, const Seperator *sep) -{ - auto &mob_list = entity_list.GetMobList(); - for (auto itr : mob_list) { - Mob *mob = itr.second; - if (mob->IsNPC() && mob->GetRace() == 2254) { - mob->Depop(); - } - } - - int found_zone_points = 0; - - c->Message(Chat::White, "Listing zone points..."); - c->SendChatLineBreak(); - - for (auto &virtual_zone_point : zone->virtual_zone_point_list) { - std::string zone_long_name = zone_store.GetZoneLongName(virtual_zone_point.target_zone_id); - - c->Message( - Chat::White, - fmt::format( - "Virtual Zone Point x [{}] y [{}] z [{}] h [{}] width [{}] height [{}] | To [{}] ({}) x [{}] y [{}] z [{}] h [{}]", - virtual_zone_point.x, - virtual_zone_point.y, - virtual_zone_point.z, - virtual_zone_point.heading, - virtual_zone_point.width, - virtual_zone_point.height, - zone_long_name.c_str(), - virtual_zone_point.target_zone_id, - virtual_zone_point.target_x, - virtual_zone_point.target_y, - virtual_zone_point.target_z, - virtual_zone_point.target_heading - ).c_str() - ); - - std::string node_name = fmt::format("ZonePoint To [{}]", zone_long_name); - - float half_width = ((float) virtual_zone_point.width / 2); - - NPC::SpawnZonePointNodeNPC(node_name, glm::vec4( - (float) virtual_zone_point.x + half_width, - (float) virtual_zone_point.y + half_width, - virtual_zone_point.z, - virtual_zone_point.heading - )); - - NPC::SpawnZonePointNodeNPC(node_name, glm::vec4( - (float) virtual_zone_point.x + half_width, - (float) virtual_zone_point.y - half_width, - virtual_zone_point.z, - virtual_zone_point.heading - )); - - NPC::SpawnZonePointNodeNPC(node_name, glm::vec4( - (float) virtual_zone_point.x - half_width, - (float) virtual_zone_point.y - half_width, - virtual_zone_point.z, - virtual_zone_point.heading - )); - - NPC::SpawnZonePointNodeNPC(node_name, glm::vec4( - (float) virtual_zone_point.x - half_width, - (float) virtual_zone_point.y + half_width, - virtual_zone_point.z, - virtual_zone_point.heading - )); - - NPC::SpawnZonePointNodeNPC(node_name, glm::vec4( - (float) virtual_zone_point.x + half_width, - (float) virtual_zone_point.y + half_width, - (float) virtual_zone_point.z + (float) virtual_zone_point.height, - virtual_zone_point.heading - )); - - NPC::SpawnZonePointNodeNPC(node_name, glm::vec4( - (float) virtual_zone_point.x + half_width, - (float) virtual_zone_point.y - half_width, - (float) virtual_zone_point.z + (float) virtual_zone_point.height, - virtual_zone_point.heading - )); - - NPC::SpawnZonePointNodeNPC(node_name, glm::vec4( - (float) virtual_zone_point.x - half_width, - (float) virtual_zone_point.y - half_width, - (float) virtual_zone_point.z + (float) virtual_zone_point.height, - virtual_zone_point.heading - )); - - NPC::SpawnZonePointNodeNPC(node_name, glm::vec4( - (float) virtual_zone_point.x - half_width, - (float) virtual_zone_point.y + half_width, - (float) virtual_zone_point.z + (float) virtual_zone_point.height, - virtual_zone_point.heading - )); - - found_zone_points++; - } - - LinkedListIterator iterator(zone->zone_point_list); - iterator.Reset(); - while (iterator.MoreElements()) { - ZonePoint *zone_point = iterator.GetData(); - std::string zone_long_name = zone_store.GetZoneLongName(zone_point->target_zone_id); - std::string node_name = fmt::format("ZonePoint To [{}]", zone_long_name); - - NPC::SpawnZonePointNodeNPC( - node_name, glm::vec4( - zone_point->x, - zone_point->y, - zone_point->z, - zone_point->heading - ) - ); - - c->Message( - Chat::White, - fmt::format( - "Client Side Zone Point x [{}] y [{}] z [{}] h [{}] number [{}] | To [{}] ({}) x [{}] y [{}] z [{}] h [{}]", - zone_point->x, - zone_point->y, - zone_point->z, - zone_point->heading, - zone_point->number, - zone_long_name.c_str(), - zone_point->target_zone_id, - zone_point->target_x, - zone_point->target_y, - zone_point->target_z, - zone_point->target_heading - ).c_str() - ); - - iterator.Advance(); - - found_zone_points++; - } - - if (found_zone_points == 0) { - c->Message(Chat::White, "There were no zone points found..."); - } - - c->SendChatLineBreak(); - -} - -void command_mystats(Client *c, const Seperator *sep) -{ - if (c->GetTarget() && c->GetPet()) { - if (c->GetTarget()->IsPet() && c->GetTarget() == c->GetPet()) - c->GetTarget()->ShowStats(c); - else - c->ShowStats(c); - } - else - c->ShowStats(c); -} - -void command_myskills(Client *c, const Seperator *sep) -{ - c->ShowSkillsWindow(); -} - -void command_bind(Client *c, const Seperator *sep) -{ - if (c->GetTarget() != 0 ) { - if (c->GetTarget()->IsClient()) - c->GetTarget()->CastToClient()->SetBindPoint(); - else - c->Message(Chat::White, "Error: target not a Player"); - } else - c->SetBindPoint(); -} - -void command_depop(Client *c, const Seperator *sep) -{ - if (c->GetTarget() == 0 || !(c->GetTarget()->IsNPC() || c->GetTarget()->IsNPCCorpse())) - c->Message(Chat::White, "You must have a NPC target for this command. (maybe you meant #depopzone?)"); - else { - c->Message(Chat::White, "Depoping '%s'.", c->GetTarget()->GetName()); - c->GetTarget()->Depop(); - } -} - -void command_depopzone(Client *c, const Seperator *sep) -{ - zone->Depop(); - c->Message(Chat::White, "Zone depoped."); -} - -void command_devtools(Client *c, const Seperator *sep) -{ - std::string dev_tools_key = StringFormat("%i-dev-tools-disabled", c->AccountID()); - - /** - * Handle window toggle - */ - if (strcasecmp(sep->arg[1], "disable") == 0) { - DataBucket::SetData(dev_tools_key, "true"); - c->SetDevToolsEnabled(false); - } - if (strcasecmp(sep->arg[1], "enable") == 0) { - DataBucket::DeleteData(dev_tools_key); - c->SetDevToolsEnabled(true); - } - - c->ShowDevToolsMenu(); -} - -void command_repop(Client *c, const Seperator *sep) -{ - int timearg = 1; - int delay = 0; - - if (sep->arg[1] && strcasecmp(sep->arg[1], "force") == 0) { - timearg++; - - LinkedListIterator iterator(zone->spawn2_list); - iterator.Reset(); - while (iterator.MoreElements()) { - std::string query = StringFormat( - "DELETE FROM respawn_times WHERE id = %lu AND instance_id = %lu", - (unsigned long)iterator.GetData()->GetID(), - (unsigned long)zone->GetInstanceID() - ); - auto results = database.QueryDatabase(query); - iterator.Advance(); - } - c->Message(Chat::White, "Zone depop: Force resetting spawn timers."); - } - - if (!sep->IsNumber(timearg)) { - c->Message(Chat::White, "Zone depopped - repopping now."); - - zone->Repop(); - - /* Force a spawn2 timer trigger so we don't delay actually spawning the NPC's */ - zone->spawn2_timer.Trigger(); - return; - } - - c->Message(Chat::White, "Zone depoped. Repop in %i seconds", atoi(sep->arg[timearg])); - zone->Repop(atoi(sep->arg[timearg]) * 1000); - - zone->spawn2_timer.Trigger(); -} - -void command_spawnstatus(Client *c, const Seperator *sep) -{ - if((sep->arg[1][0] == 'e') | (sep->arg[1][0] == 'E')) - { - // show only enabled spawns - zone->ShowEnabledSpawnStatus(c); - } - else if((sep->arg[1][0] == 'd') | (sep->arg[1][0] == 'D')) - { - // show only disabled spawns - zone->ShowDisabledSpawnStatus(c); - } - else if((sep->arg[1][0] == 'a') | (sep->arg[1][0] == 'A')) - { - // show all spawn staus with no filters - zone->SpawnStatus(c); - } - else if(sep->IsNumber(1)) - { - // show spawn status by spawn2 id - zone->ShowSpawnStatusByID(c, atoi(sep->arg[1])); - } - else if(strcmp(sep->arg[1], "help") == 0) - { - c->Message(Chat::White, "Usage: #spawnstatus <[a]ll | [d]isabled | [e]nabled | {Spawn2 ID}>"); - } - else { - zone->SpawnStatus(c); - } -} - -void command_nukebuffs(Client *c, const Seperator *sep) -{ - if (c->GetTarget() == 0) - c->BuffFadeAll(); - else - c->GetTarget()->BuffFadeAll(); -} - -void command_zuwcoords(Client *c, const Seperator *sep) -{ - // modifys and resends zhdr packet - if(sep->arg[1][0]==0) - c->Message(Chat::White, "Usage: #zuwcoords "); - else { - zone->newzone_data.underworld = atof(sep->arg[1]); - //float newdata = atof(sep->arg[1]); - //memcpy(&zone->zone_header_data[130], &newdata, sizeof(float)); - auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); - memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } -} - -void command_zunderworld(Client *c, const Seperator *sep) -{ - if(sep->arg[1][0]==0) - c->Message(Chat::White, "Usage: #zunderworld "); - else { - zone->newzone_data.underworld = atof(sep->arg[1]); - } -} - -void command_zsafecoords(Client *c, const Seperator *sep) -{ - // modifys and resends zhdr packet - if(sep->arg[3][0]==0) - c->Message(Chat::White, "Usage: #zsafecoords "); - else { - zone->newzone_data.safe_x = atof(sep->arg[1]); - zone->newzone_data.safe_y = atof(sep->arg[2]); - zone->newzone_data.safe_z = atof(sep->arg[3]); - //float newdatax = atof(sep->arg[1]); - //float newdatay = atof(sep->arg[2]); - //float newdataz = atof(sep->arg[3]); - //memcpy(&zone->zone_header_data[114], &newdatax, sizeof(float)); - //memcpy(&zone->zone_header_data[118], &newdatay, sizeof(float)); - //memcpy(&zone->zone_header_data[122], &newdataz, sizeof(float)); - //zone->SetSafeCoords(); - auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); - memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } -} - -void command_freeze(Client *c, const Seperator *sep) -{ - if (c->GetTarget() != 0) - c->GetTarget()->SendAppearancePacket(AT_Anim, ANIM_FREEZE); - else - c->Message(Chat::White, "ERROR: Freeze requires a target."); -} - -void command_unfreeze(Client *c, const Seperator *sep) -{ - if (c->GetTarget() != 0) - c->GetTarget()->SendAppearancePacket(AT_Anim, ANIM_STAND); - else - c->Message(Chat::White, "ERROR: Unfreeze requires a target."); -} - -void command_push(Client *c, const Seperator *sep) -{ - Mob *t = c; - if (c->GetTarget() != nullptr) - t = c->GetTarget(); - - if (!sep->arg[1] || !sep->IsNumber(1)) { - c->Message(Chat::White, "ERROR: Must provide at least a push back."); - return; - } - - float back = atof(sep->arg[1]); - float up = 0.0f; - - if (sep->arg[2] && sep->IsNumber(2)) - up = atof(sep->arg[2]); - - if (t->IsNPC()) { - t->IncDeltaX(back * g_Math.FastSin(c->GetHeading())); - t->IncDeltaY(back * g_Math.FastCos(c->GetHeading())); - t->IncDeltaZ(up); - t->SetForcedMovement(6); - } else if (t->IsClient()) { - // TODO: send packet to push - } -} - -void command_proximity(Client *c, const Seperator *sep) -{ - if (!c->GetTarget() || (c->GetTarget() && !c->GetTarget()->IsNPC())) { - c->Message(Chat::White, "You must target an NPC"); - return; - } - - for (auto &iter : entity_list.GetNPCList()) { - auto npc = iter.second; - std::string name = npc->GetName(); - - if (name.find("Proximity") != std::string::npos) { - npc->Depop(); - } - } - - NPC *npc = c->GetTarget()->CastToNPC(); - - std::vector points; - - FindPerson_Point p{}; - - if (npc->IsProximitySet()) { - glm::vec4 position; - position.w = npc->GetHeading(); - position.x = npc->GetProximityMinX(); - position.y = npc->GetProximityMinY(); - position.z = npc->GetZ(); - - position.x = npc->GetProximityMinX(); - position.y = npc->GetProximityMinY(); - NPC::SpawnNodeNPC("Proximity", "", position); - - position.x = npc->GetProximityMinX(); - position.y = npc->GetProximityMaxY(); - NPC::SpawnNodeNPC("Proximity", "", position); - - position.x = npc->GetProximityMaxX(); - position.y = npc->GetProximityMinY(); - NPC::SpawnNodeNPC("Proximity", "", position); - - position.x = npc->GetProximityMaxX(); - position.y = npc->GetProximityMaxY(); - NPC::SpawnNodeNPC("Proximity", "", position); - - p.x = npc->GetProximityMinX(); - p.y = npc->GetProximityMinY(); - p.z = npc->GetZ(); - points.push_back(p); - - p.x = npc->GetProximityMinX(); - p.y = npc->GetProximityMaxY(); - points.push_back(p); - - p.x = npc->GetProximityMaxX(); - p.y = npc->GetProximityMaxY(); - points.push_back(p); - - p.x = npc->GetProximityMaxX(); - p.y = npc->GetProximityMinY(); - points.push_back(p); - - p.x = npc->GetProximityMinX(); - p.y = npc->GetProximityMinY(); - points.push_back(p); - } - - if (c->ClientVersion() >= EQ::versions::ClientVersion::RoF) { - c->SendPathPacket(points); - } -} - -void command_pvp(Client *c, const Seperator *sep) -{ - bool state=atobool(sep->arg[1]); - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient()) - t=c->GetTarget()->CastToClient(); - - if(sep->arg[1][0] != 0) { - t->SetPVP(state); - c->Message(Chat::White, "%s now follows the ways of %s.", t->GetName(), state?"discord":"order"); - } - else - c->Message(Chat::White, "Usage: #pvp [on/off]"); -} - -void command_setxp(Client *c, const Seperator *sep) -{ - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient()) - t=c->GetTarget()->CastToClient(); - - if (sep->IsNumber(1)) { - if (atoi(sep->arg[1]) > 9999999) - c->Message(Chat::White, "Error: Value too high."); - else - t->AddEXP(atoi(sep->arg[1])); - } - else - c->Message(Chat::White, "Usage: #setxp number"); -} - -void command_setpvppoints(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - if (!arguments || !sep->IsNumber(1)) { - c->Message(Chat::White, "Command Syntax: #setpvppoints [Amount]"); - return; - } - - Client *target = c; - if (c->GetTarget() && c->GetTarget()->IsClient()) { - target = c->GetTarget()->CastToClient(); - } - - uint32 pvp_points = static_cast(std::min(std::stoull(sep->arg[1]), (unsigned long long) 2000000000)); - target->SetPVPPoints(pvp_points); - target->Save(); - target->SendPVPStats(); - std::string pvp_message = fmt::format( - "{} now {} {} PVP Point{}.", - c == target ? "You" : target->GetCleanName(), - c == target ? "have" : "has", - pvp_points, - pvp_points != 1 ? "s" : "" - ); - c->Message( - Chat::White, - pvp_message.c_str() - ); -} - -void command_name(Client *c, const Seperator *sep) -{ - Client *target; - - if( (strlen(sep->arg[1]) == 0) || (!(c->GetTarget() && c->GetTarget()->IsClient())) ) - c->Message(Chat::White, "Usage: #name newname (requires player target)"); - else - { - target = c->GetTarget()->CastToClient(); - char *oldname = strdup(target->GetName()); - if(target->ChangeFirstName(sep->arg[1], c->GetName())) - { - c->Message(Chat::White, "Successfully renamed %s to %s", oldname, sep->arg[1]); - // until we get the name packet working right this will work - c->Message(Chat::White, "Sending player to char select."); - target->Kick("Name was changed"); - } - else - c->Message(Chat::Red, "ERROR: Unable to rename %s. Check that the new name '%s' isn't already taken.", oldname, sep->arg[2]); - free(oldname); - } -} - -void command_tempname(Client *c, const Seperator *sep) -{ - Mob *target; - target = c->GetTarget(); - - if(!target) - c->Message(Chat::White, "Usage: #tempname newname (requires a target)"); - else if(strlen(sep->arg[1]) > 0) - { - char *oldname = strdup(target->GetName()); - target->TempName(sep->arg[1]); - c->Message(Chat::White, "Renamed %s to %s", oldname, sep->arg[1]); - free(oldname); - } - else { - target->TempName(); - c->Message(Chat::White, "Restored the original name"); - } -} - -void command_petname(Client *c, const Seperator *sep) -{ - Mob *target; - target = c->GetTarget(); - - if(!target) - c->Message(Chat::White, "Usage: #petname newname (requires a target)"); - else if(target->IsPet() && (target->GetOwnerID() == c->GetID()) && strlen(sep->arg[1]) > 0) - { - char *oldname = strdup(target->GetName()); - target->TempName(sep->arg[1]); - c->Message(Chat::White, "Renamed %s to %s", oldname, sep->arg[1]); - free(oldname); - } - else { - target->TempName(); - c->Message(Chat::White, "Restored the original name"); - } -} - -void command_npcspecialattk(Client *c, const Seperator *sep) -{ - if (c->GetTarget()==0 || c->GetTarget()->IsClient() || strlen(sep->arg[1]) <= 0 || strlen(sep->arg[2]) <= 0) - c->Message(Chat::White, "Usage: #npcspecialattk *flagchar* *permtag* (Flags are E(nrage) F(lurry) R(ampage) S(ummon), permtag is 1 = True, 0 = False)."); - else { - c->GetTarget()->CastToNPC()->NPCSpecialAttacks(sep->arg[1],atoi(sep->arg[2])); - c->Message(Chat::White, "NPC Special Attack set."); - } -} - -void command_kill(Client *c, const Seperator *sep) -{ - if (!c->GetTarget()) { - c->Message(Chat::White, "Error: #Kill: No target."); - } - else - if (!c->GetTarget()->IsClient() || c->GetTarget()->CastToClient()->Admin() <= c->Admin()) - c->GetTarget()->Kill(); -} - -void command_killallnpcs(Client *c, const Seperator *sep) -{ - std::string search_string; - if (sep->arg[1]) { - search_string = sep->arg[1]; - } - - int count = 0; - for (auto &itr : entity_list.GetMobList()) { - Mob *entity = itr.second; - if (!entity->IsNPC()) { - continue; - } - - std::string entity_name = entity->GetName(); - - /** - * Filter by name - */ - if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { - continue; - } - - bool is_not_attackable = - ( - entity->IsInvisible() || - !entity->IsAttackAllowed(c) || - entity->GetRace() == 127 || - entity->GetRace() == 240 - ); - - if (is_not_attackable) { - continue; - } - - entity->Damage(c, 1000000000, 0, EQ::skills::SkillDragonPunch); - - count++; - } - - c->Message(Chat::Yellow, "Killed (%i) npc(s)", count); -} - -void command_haste(Client *c, const Seperator *sep) -{ - // #haste command to set client attack speed. Takes a percentage (100 = twice normal attack speed) - if(sep->arg[1][0] != 0) { - uint16 Haste = atoi(sep->arg[1]); - if(Haste > 85) - Haste = 85; - c->SetExtraHaste(Haste); - // SetAttackTimer must be called to make this take effect, so player needs to change - // the primary weapon. - c->Message(Chat::White, "Haste set to %d%% - Need to re-equip primary weapon before it takes effect", Haste); - } - else - c->Message(Chat::White, "Usage: #haste [percentage]"); -} - -void command_damage(Client *c, const Seperator *sep) -{ - if (c->GetTarget()==0) - c->Message(Chat::White, "Error: #Damage: No Target."); - else if (!sep->IsNumber(1)) { - c->Message(Chat::White, "Usage: #damage x"); - } - else { - int32 nkdmg = atoi(sep->arg[1]); - if (nkdmg > 2100000000) - c->Message(Chat::White, "Enter a value less then 2,100,000,000."); - else - c->GetTarget()->Damage(c, nkdmg, SPELL_UNKNOWN, EQ::skills::SkillHandtoHand, false); - } -} - -void command_zonespawn(Client *c, const Seperator *sep) -{ - c->Message(Chat::White, "This command is not yet implemented."); - return; - -/* this was kept from client.cpp verbatim (it was commented out) */ - // if (target && target->IsNPC()) { - // Message(0, "Inside main if."); - // if (strcasecmp(sep->arg[1], "add")==0) { - // Message(0, "Inside add if."); - // database.DBSpawn(1, StaticGetZoneName(this->GetPP().current_zone), target->CastToNPC()); - // } - // else if (strcasecmp(sep->arg[1], "update")==0) { - // database.DBSpawn(2, StaticGetZoneName(this->GetPP().current_zone), target->CastToNPC()); - // } - // else if (strcasecmp(sep->arg[1], "remove")==0) { - // if (strcasecmp(sep->arg[2], "all")==0) { - // database.DBSpawn(4, StaticGetZoneName(this->GetPP().current_zone)); - // } - // else { - // if (database.DBSpawn(3, StaticGetZoneName(this->GetPP().current_zone), target->CastToNPC())) { - // Message(0, "#zonespawn: %s removed successfully!", target->GetName()); - // target->CastToNPC()->Death(target, target->GetHP()); - // } - // } - // } - // else - // Message(0, "Error: #dbspawn: Invalid command. (Note: EDIT and REMOVE are NOT in yet.)"); - // if (target->CastToNPC()->GetNPCTypeID() > 0) { - // Message(0, "Spawn is type %i", target->CastToNPC()->GetNPCTypeID()); - // } - // } - // else if(!target || !target->IsNPC()) - // Message(0, "Error: #zonespawn: You must have a NPC targeted!"); - // else - // Message(0, "Usage: #zonespawn [add|edit|remove|remove all]"); -} - -void command_npcspawn(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - if (!arguments) { - c->Message(Chat::White, "Command Syntax: #npcspawn [Add|Create|Delete|Remove|Update]"); - return; - } - - if (!(c->GetTarget() && c->GetTarget()->IsNPC())) { - c->Message(Chat::White, "You must target an NPC to use this command."); - return; - } - - NPC* target = c->GetTarget()->CastToNPC(); - std::string spawn_type = str_tolower(sep->arg[1]); - uint32 extra = 0; - bool is_add = spawn_type.find("add") != std::string::npos; - bool is_create = spawn_type.find("create") != std::string::npos; - bool is_delete = spawn_type.find("delete") != std::string::npos; - bool is_remove = spawn_type.find("remove") != std::string::npos; - bool is_update = spawn_type.find("update") != std::string::npos; - if (!is_add && !is_create && !is_delete && !is_remove && !is_update) { - c->Message(Chat::White, "Command Syntax: #npcspawn [Add|Create|Delete|Remove|Update]"); - return; - } - - if (is_add || is_create) { - extra = ( - sep->IsNumber(2) ? - ( - is_add ? - std::stoi(sep->arg[2]) : - 1 - ) : ( - is_add ? - 1200 : - 0 - ) - ); // Default to 1200 for Add, 0 for Create if not set - content_db.NPCSpawnDB( - is_add ? NPCSpawnTypes::AddNewSpawngroup : NPCSpawnTypes::CreateNewSpawn, - zone->GetShortName(), - zone->GetInstanceVersion(), - c, - target, - extra - ); - c->Message( - Chat::White, - fmt::format( - "Spawn {} | Name: {} ({})", - is_add ? "Added" : "Created", - target->GetCleanName(), - target->GetID() - ).c_str() - ); - } else if (is_delete || is_remove || is_update) { - uint8 spawn_update_type = ( - is_delete ? - NPCSpawnTypes::DeleteSpawn : - ( - is_remove ? - NPCSpawnTypes::RemoveSpawn : - NPCSpawnTypes::UpdateAppearance - ) - ); - std::string spawn_message = ( - is_delete ? - "Deleted" : - ( - is_remove ? - "Removed" : - "Updated" - ) - ); - content_db.NPCSpawnDB( - spawn_update_type, - zone->GetShortName(), - zone->GetInstanceVersion(), - c, - target - ); - c->Message( - Chat::White, - fmt::format( - "Spawn {} | Name: {} ({})", - spawn_message, - target->GetCleanName(), - target->GetID() - ).c_str() - ); - } -} - -void command_spawnfix(Client *c, const Seperator *sep) { - Mob *target_mob = c->GetTarget(); - if (!target_mob || !target_mob->IsNPC()) { - c->Message(Chat::White, "Error: #spawnfix: Need an NPC target."); - return; - } - - Spawn2* s2 = target_mob->CastToNPC()->respawn2; - - if(!s2) { - c->Message(Chat::White, "#spawnfix FAILED -- cannot determine which spawn entry in the database this mob came from."); - return; - } - - std::string query = StringFormat( - "UPDATE spawn2 SET x = '%f', y = '%f', z = '%f', heading = '%f' WHERE id = '%i'", - c->GetX(), - c->GetY(), - target_mob->GetFixedZ(c->GetPosition()), - c->GetHeading(), - s2->GetID() - ); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::Red, "Update failed! MySQL gave the following error:"); - c->Message(Chat::Red, results.ErrorMessage().c_str()); - return; - } - - c->Message(Chat::White, "Updating coordinates successful."); - target_mob->Depop(false); -} - -void command_loc(Client *c, const Seperator *sep) -{ - Mob *target = c; - if (c->GetTarget()) { - target = c->GetTarget(); - } - - auto target_position = target->GetPosition(); - - c->Message( - Chat::White, - fmt::format( - "{} Location | XYZ: {:.2f}, {:.2f}, {:.2f} Heading: {:.2f}", - ( - c == target ? - "Your" : - fmt::format( - "{} ({})", - target->GetCleanName(), - target->GetID() - ) - ), - target_position.x, - target_position.y, - target_position.z, - target_position.w - ).c_str() - ); -} - -void command_goto(Client *c, const Seperator *sep) -{ - std::string arg1 = sep->arg[1]; - - bool goto_via_target_no_args = sep->arg[1][0] == '\0' && c->GetTarget(); - bool goto_via_player_name = !sep->IsNumber(1) && !arg1.empty(); - bool goto_via_x_y_z = sep->IsNumber(1) && sep->IsNumber(2) && sep->IsNumber(3); - - if (goto_via_target_no_args) { - c->MovePC( - zone->GetZoneID(), - zone->GetInstanceID(), - c->GetTarget()->GetX(), - c->GetTarget()->GetY(), - c->GetTarget()->GetZ(), - c->GetTarget()->GetHeading() - ); - } - else if (goto_via_player_name) { - - /** - * Find them in zone first - */ - const char *player_name = sep->arg[1]; - std::string player_name_string = sep->arg[1]; - Client *client = entity_list.GetClientByName(player_name); - if (client) { - c->MovePC( - zone->GetZoneID(), - zone->GetInstanceID(), - client->GetX(), - client->GetY(), - client->GetZ(), - client->GetHeading() - ); - - c->Message(Chat::Yellow, "Goto player '%s' same zone", player_name_string.c_str()); - } - else if (c->GotoPlayer(player_name_string)) { - c->Message(Chat::Yellow, "Goto player '%s' different zone", player_name_string.c_str()); - } - else { - c->Message(Chat::Yellow, "Player '%s' not found", player_name_string.c_str()); - } - } - else if (goto_via_x_y_z) { - c->MovePC( - zone->GetZoneID(), - zone->GetInstanceID(), - atof(sep->arg[1]), - atof(sep->arg[2]), - atof(sep->arg[3]), - (sep->arg[4] ? atof(sep->arg[4]) : c->GetHeading()) - ); - } - else { - c->Message(Chat::White, "Usage: #goto [x y z] [h]"); - c->Message(Chat::White, "Usage: #goto [player_name]"); - } -} - -void command_iteminfo(Client *c, const Seperator *sep) -{ - auto inst = c->GetInv()[EQ::invslot::slotCursor]; - if (!inst) { - c->Message(Chat::Red, "Error: You need an item on your cursor for this command"); - return; - } - auto item = inst->GetItem(); - if (!item) { - LogInventory("([{}]) Command #iteminfo processed an item with no data pointer"); - c->Message(Chat::Red, "Error: This item has no data reference"); - return; - } - - EQ::SayLinkEngine linker; - linker.SetLinkType(EQ::saylink::SayLinkItemInst); - linker.SetItemInst(inst); - - c->Message(Chat::White, "*** Item Info for [%s] ***", linker.GenerateLink().c_str()); - c->Message(Chat::White, ">> ID: %u, ItemUseType: %u, ItemClassType: %u", item->ID, item->ItemType, item->ItemClass); - c->Message(Chat::White, ">> IDFile: '%s', IconID: %u", item->IDFile, item->Icon); - c->Message(Chat::White, ">> Size: %u, Weight: %u, Price: %u, LDoNPrice: %u", item->Size, item->Weight, item->Price, item->LDoNPrice); - c->Message(Chat::White, ">> Material: 0x%02X, Color: 0x%08X, Tint: 0x%08X, Light: 0x%02X", item->Material, item->Color, inst->GetColor(), item->Light); - c->Message(Chat::White, ">> IsLore: %s, LoreGroup: %u, Lore: '%s'", (item->LoreFlag ? "TRUE" : "FALSE"), item->LoreGroup, item->Lore); - c->Message(Chat::White, ">> NoDrop: %u, NoRent: %u, NoPet: %u, NoTransfer: %u, FVNoDrop: %u", - item->NoDrop, item->NoRent, (uint8)item->NoPet, (uint8)item->NoTransfer, item->FVNoDrop); - - if (item->IsClassBook()) { - c->Message(Chat::White, "*** This item is a Book (filename:'%s') ***", item->Filename); - } - else if (item->IsClassBag()) { - c->Message(Chat::White, "*** This item is a Container (%u slots) ***", item->BagSlots); - } - else { - c->Message(Chat::White, "*** This item is Common ***"); - c->Message(Chat::White, ">> Classes: %u, Races: %u, Slots: %u", item->Classes, item->Races, item->Slots); - c->Message(Chat::White, ">> ReqSkill: %u, ReqLevel: %u, RecLevel: %u", item->RecSkill, item->ReqLevel, item->RecLevel); - c->Message(Chat::White, ">> SkillModType: %u, SkillModValue: %i", item->SkillModType, item->SkillModValue); - c->Message(Chat::White, ">> BaneRaceType: %u, BaneRaceDamage: %u, BaneBodyType: %u, BaneBodyDamage: %i", - item->BaneDmgRace, item->BaneDmgRaceAmt, item->BaneDmgBody, item->BaneDmgAmt); - c->Message(Chat::White, ">> Magic: %s, SpellID: %i, ProcLevel: %u, Charges: %u, MaxCharges: %u", - (item->Magic ? "TRUE" : "FALSE"), item->Click.Effect, item->Click.Level, inst->GetCharges(), item->MaxCharges); - c->Message(Chat::White, ">> EffectType: 0x%02X, CastTime: %.2f", (uint8)item->Click.Type, ((double)item->CastTime / 1000)); - } - - if (c->Admin() >= AccountStatus::GMMgmt) - c->Message(Chat::White, ">> MinStatus: %u", item->MinStatus); -} - -void command_uptime(Client *c, const Seperator *sep) -{ - if (!worldserver.Connected()) - c->Message(Chat::White, "Error: World server disconnected"); - else - { - auto pack = new ServerPacket(ServerOP_Uptime, sizeof(ServerUptime_Struct)); - ServerUptime_Struct* sus = (ServerUptime_Struct*) pack->pBuffer; - strcpy(sus->adminname, c->GetName()); - if (sep->IsNumber(1) && atoi(sep->arg[1]) > 0) - sus->zoneserverid = atoi(sep->arg[1]); - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - -void command_flag(Client *c, const Seperator *sep) -{ - if(sep->arg[2][0] == 0) { - if (!c->GetTarget() || (c->GetTarget() && c->GetTarget() == c)) { - c->UpdateAdmin(); - c->Message(Chat::White, "Refreshed your admin flag from DB."); - } else if (c->GetTarget() && c->GetTarget() != c && c->GetTarget()->IsClient()) { - c->GetTarget()->CastToClient()->UpdateAdmin(); - c->Message(Chat::White, "%s's admin flag has been refreshed.", c->GetTarget()->GetName()); - c->GetTarget()->Message(Chat::White, "%s refreshed your admin flag.", c->GetName()); - } - } - else if (!sep->IsNumber(1) || atoi(sep->arg[1]) < -2 || atoi(sep->arg[1]) > 255 || strlen(sep->arg[2]) == 0) - c->Message(Chat::White, "Usage: #flag [status] [acctname]"); - - else if (c->Admin() < commandChangeFlags) { - //this check makes banning players by less than this level - //impossible, but i'll leave it in anyways - c->Message(Chat::White, "You may only refresh your own flag, doing so now."); - c->UpdateAdmin(); - } - else { - if (atoi(sep->arg[1]) > c->Admin()) - c->Message(Chat::White, "You cannot set people's status to higher than your own"); - else if (atoi(sep->arg[1]) < 0 && c->Admin() < commandBanPlayers) - c->Message(Chat::White, "You have too low of status to suspend/ban"); - else if (!database.SetAccountStatus(sep->argplus[2], atoi(sep->arg[1]))) - c->Message(Chat::White, "Unable to set GM Flag."); - else { - c->Message(Chat::White, "Set GM Flag on account."); - - std::string user; - std::string loginserver; - ParseAccountString(sep->argplus[2], user, loginserver); - - ServerPacket pack(ServerOP_FlagUpdate, 6); - *((uint32*) pack.pBuffer) = database.GetAccountIDByName(user.c_str(), loginserver.c_str()); - *((int16*) &pack.pBuffer[4]) = atoi(sep->arg[1]); - worldserver.SendPacket(&pack); - } - } -} - -void command_time(Client *c, const Seperator *sep) -{ - char timeMessage[255]; - int minutes=0; - if(sep->IsNumber(1)) { - if(sep->IsNumber(2)) { - minutes=atoi(sep->arg[2]); - } - c->Message(Chat::Red, "Setting world time to %s:%i (Timezone: 0)...", sep->arg[1], minutes); - zone->SetTime(atoi(sep->arg[1])+1, minutes); - LogInfo("{} :: Setting world time to {}:{} (Timezone: 0)...", c->GetCleanName(), sep->arg[1], minutes); - } - else { - c->Message(Chat::Red, "To set the Time: #time HH [MM]"); - TimeOfDay_Struct eqTime; - zone->zone_time.GetCurrentEQTimeOfDay( time(0), &eqTime); - sprintf(timeMessage,"%02d:%s%d %s (Timezone: %ih %im)", - ((eqTime.hour - 1) % 12) == 0 ? 12 : ((eqTime.hour - 1) % 12), - (eqTime.minute < 10) ? "0" : "", - eqTime.minute, - (eqTime.hour >= 13) ? "pm" : "am", - zone->zone_time.getEQTimeZoneHr(), - zone->zone_time.getEQTimeZoneMin() - ); - c->Message(Chat::Red, "It is now %s.", timeMessage); - LogInfo("Current Time is: {}", timeMessage); - } -} - -void command_guild(Client *c, const Seperator *sep) -{ - int admin=c->Admin(); - Mob *target=c->GetTarget(); - - if (strcasecmp(sep->arg[1], "help") == 0) { - c->Message(Chat::White, "GM Guild commands:"); - c->Message(Chat::White, " #guild list - lists all guilds on the server"); - c->Message(Chat::White, " #guild create {guildleader charname or CharID} guildname"); - c->Message(Chat::White, " #guild delete guildID"); - c->Message(Chat::White, " #guild rename guildID newname"); - c->Message(Chat::White, " #guild set charname guildID (0=no guild)"); - c->Message(Chat::White, " #guild setrank charname rank"); - c->Message(Chat::White, " #guild setleader guildID {guildleader charname or CharID}"); - } - else if (strcasecmp(sep->arg[1], "status") == 0 || strcasecmp(sep->arg[1], "stat") == 0) { - Client* client = 0; - if (sep->arg[2][0] != 0) - client = entity_list.GetClientByName(sep->argplus[2]); - else if (target != 0 && target->IsClient()) - client = target->CastToClient(); - if (client == 0) - c->Message(Chat::White, "You must target someone or specify a character name"); - else if ((client->Admin() >= minStatusToEditOtherGuilds && admin < minStatusToEditOtherGuilds) && client->GuildID() != c->GuildID()) // no peeping for GMs, make sure tell message stays the same - c->Message(Chat::White, "You must target someone or specify a character name."); - else { - if (client->IsInAGuild()) - c->Message(Chat::White, "%s is not in a guild.", client->GetName()); - else if (guild_mgr.IsGuildLeader(client->GuildID(), client->CharacterID())) - c->Message(Chat::White, "%s is the leader of <%s> rank: %s", client->GetName(), guild_mgr.GetGuildName(client->GuildID()), guild_mgr.GetRankName(client->GuildID(), client->GuildRank())); - else - c->Message(Chat::White, "%s is a member of <%s> rank: %s", client->GetName(), guild_mgr.GetGuildName(client->GuildID()), guild_mgr.GetRankName(client->GuildID(), client->GuildRank())); - } - } - else if (strcasecmp(sep->arg[1], "info") == 0) { - if (sep->arg[2][0] == 0 && c->IsInAGuild()) { - if (admin >= minStatusToEditOtherGuilds) - c->Message(Chat::White, "Usage: #guildinfo guild_id"); - else - c->Message(Chat::White, "You're not in a guild"); - } - else { - uint32 tmp = GUILD_NONE; - if (sep->arg[2][0] == 0) - tmp = c->GuildID(); - else if (admin >= minStatusToEditOtherGuilds) - tmp = atoi(sep->arg[2]); - - if(tmp != GUILD_NONE) - guild_mgr.DescribeGuild(c, tmp); - } - } - else if (strcasecmp(sep->arg[1], "set") == 0) { - if (!sep->IsNumber(3)) - c->Message(Chat::White, "Usage: #guild set charname guildgbid (0 = clear guildtag)"); - else { - uint32 guild_id = atoi(sep->arg[3]); - - if(guild_id == 0) - guild_id = GUILD_NONE; - else if(!guild_mgr.GuildExists(guild_id)) { - c->Message(Chat::Red, "Guild %d does not exist.", guild_id); - return; - } - - uint32 charid = database.GetCharacterID(sep->arg[2]); - if(charid == 0) { - c->Message(Chat::Red, "Unable to find character '%s'", charid); - return; - } - - //we could do the checking we need for guild_mgr.CheckGMStatus, but im lazy right now - if(admin < minStatusToEditOtherGuilds) { - c->Message(Chat::Red, "Access denied."); - return; - } - - if(guild_id == GUILD_NONE) { - LogGuilds("[{}]: Removing [{}] ([{}]) from guild with GM command", c->GetName(), sep->arg[2], charid); - } else { - LogGuilds("[{}]: Putting [{}] ([{}]) into guild [{}] ([{}]) with GM command", c->GetName(), sep->arg[2], charid, guild_mgr.GetGuildName(guild_id), guild_id); - } - - if(!guild_mgr.SetGuild(charid, guild_id, GUILD_MEMBER)) { - c->Message(Chat::Red, "Error putting '%s' into guild %d", sep->arg[2], guild_id); - } else { - c->Message(Chat::White, "%s has been put into guild %d", sep->arg[2], guild_id); - } - } - } - /*else if (strcasecmp(sep->arg[1], "setdoor") == 0 && admin >= minStatusToEditOtherGuilds) { - - if (!sep->IsNumber(2)) - c->Message(Chat::White, "Usage: #guild setdoor guildEQid (0 = delete guilddoor)"); - else { -// guild doors - if((!guilds[atoi(sep->arg[2])].databaseID) && (atoi(sep->arg[2])!=0) ) - { - - c->Message(Chat::White, "These is no guild with this guildEQid"); - } - else { - c->SetIsSettingGuildDoor(true); - c->Message(Chat::White, "Click on a door you want to become a guilddoor"); - c->SetSetGuildDoorID(atoi(sep->arg[2])); - } - } - }*/ - else if (strcasecmp(sep->arg[1], "setrank") == 0) { - int rank = atoi(sep->arg[3]); - if (!sep->IsNumber(3)) - c->Message(Chat::White, "Usage: #guild setrank charname rank"); - else if (rank < 0 || rank > GUILD_MAX_RANK) - c->Message(Chat::White, "Error: invalid rank #."); - else { - uint32 charid = database.GetCharacterID(sep->arg[2]); - if(charid == 0) { - c->Message(Chat::Red, "Unable to find character '%s'", charid); - return; - } - - //we could do the checking we need for guild_mgr.CheckGMStatus, but im lazy right now - if(admin < minStatusToEditOtherGuilds) { - c->Message(Chat::Red, "Access denied."); - return; - } - - LogGuilds("[{}]: Setting [{}] ([{}])'s guild rank to [{}] with GM command", c->GetName(), sep->arg[2], charid, rank); - - if(!guild_mgr.SetGuildRank(charid, rank)) - c->Message(Chat::Red, "Error while setting rank %d on '%s'.", rank, sep->arg[2]); - else - c->Message(Chat::White, "%s has been set to rank %d", sep->arg[2], rank); - } - } - else if (strcasecmp(sep->arg[1], "create") == 0) { - if (sep->arg[3][0] == 0) - c->Message(Chat::White, "Usage: #guild create {guildleader charname or CharID} guild name"); - else if (!worldserver.Connected()) - c->Message(Chat::White, "Error: World server dirconnected"); - else { - uint32 leader = 0; - if (sep->IsNumber(2)) { - leader = atoi(sep->arg[2]); - } else if((leader=database.GetCharacterID(sep->arg[2])) != 0) { - //got it from the db.. - } else { - c->Message(Chat::Red, "Unable to find char '%s'", sep->arg[2]); - return; - } - if (leader == 0) { - c->Message(Chat::White, "Guild leader not found."); - return; - } - - uint32 tmp = guild_mgr.FindGuildByLeader(leader); - if (tmp != GUILD_NONE) { - c->Message(Chat::White, "Error: %s already is the leader of DB# %i '%s'.", sep->arg[2], tmp, guild_mgr.GetGuildName(tmp)); - } - else { - - if(admin < minStatusToEditOtherGuilds) { - c->Message(Chat::Red, "Access denied."); - return; - } - - uint32 id = guild_mgr.CreateGuild(sep->argplus[3], leader); - - LogGuilds("[{}]: Creating guild [{}] with leader [{}] with GM command. It was given id [{}]", c->GetName(), - sep->argplus[3], leader, (unsigned long)id); - - if (id == GUILD_NONE) - c->Message(Chat::White, "Guild creation failed."); - else { - c->Message(Chat::White, "Guild created: Leader: %i, number %i: %s", leader, id, sep->argplus[3]); - - if(!guild_mgr.SetGuild(leader, id, GUILD_LEADER)) - c->Message(Chat::White, "Unable to set guild leader's guild in the database. Your going to have to run #guild set"); - } - - } - } - } - else if (strcasecmp(sep->arg[1], "delete") == 0) { - if (!sep->IsNumber(2)) - c->Message(Chat::White, "Usage: #guild delete guildID"); - else if (!worldserver.Connected()) - c->Message(Chat::White, "Error: World server dirconnected"); - else { - uint32 id = atoi(sep->arg[2]); - - if(!guild_mgr.GuildExists(id)) { - c->Message(Chat::White, "Guild %d does not exist!", id); - return; - } - - if(admin < minStatusToEditOtherGuilds) { - //this person is not allowed to just edit any guild, check this guild's min status. - if(c->GuildID() != id) { - c->Message(Chat::Red, "Access denied to edit other people's guilds"); - return; - } else if(!guild_mgr.CheckGMStatus(id, admin)) { - c->Message(Chat::Red, "Access denied to edit your guild with GM commands."); - return; - } - } - - LogGuilds("[{}]: Deleting guild [{}] ([{}]) with GM command", c->GetName(), - guild_mgr.GetGuildName(id), id); - - if (!guild_mgr.DeleteGuild(id)) - c->Message(Chat::White, "Guild delete failed."); - else { - c->Message(Chat::White, "Guild %d deleted.", id); - } - } - } - else if (strcasecmp(sep->arg[1], "rename") == 0) { - if ((!sep->IsNumber(2)) || sep->arg[3][0] == 0) - c->Message(Chat::White, "Usage: #guild rename guildID newname"); - else if (!worldserver.Connected()) - c->Message(Chat::White, "Error: World server dirconnected"); - else { - uint32 id = atoi(sep->arg[2]); - - if(!guild_mgr.GuildExists(id)) { - c->Message(Chat::White, "Guild %d does not exist!", id); - return; - } - - if(admin < minStatusToEditOtherGuilds) { - //this person is not allowed to just edit any guild, check this guild's min status. - if(c->GuildID() != id) { - c->Message(Chat::Red, "Access denied to edit other people's guilds"); - return; - } else if(!guild_mgr.CheckGMStatus(id, admin)) { - c->Message(Chat::Red, "Access denied to edit your guild with GM commands."); - return; - } - } - - LogGuilds("[{}]: Renaming guild [{}] ([{}]) to [{}] with GM command", c->GetName(), - guild_mgr.GetGuildName(id), id, sep->argplus[3]); - - if (!guild_mgr.RenameGuild(id, sep->argplus[3])) - c->Message(Chat::White, "Guild rename failed."); - else { - c->Message(Chat::White, "Guild %d renamed to %s", id, sep->argplus[3]); - } - } - } - else if (strcasecmp(sep->arg[1], "setleader") == 0) { - if (sep->arg[3][0] == 0 || !sep->IsNumber(2)) - c->Message(Chat::White, "Usage: #guild setleader guild_id {guildleader charname or CharID}"); - else if (!worldserver.Connected()) - c->Message(Chat::White, "Error: World server dirconnected"); - else { - uint32 leader = 0; - if (sep->IsNumber(2)) { - leader = atoi(sep->arg[2]); - } else if((leader=database.GetCharacterID(sep->arg[2])) != 0) { - //got it from the db.. - } else { - c->Message(Chat::Red, "Unable to find char '%s'", sep->arg[2]); - return; - } - - uint32 tmpdb = guild_mgr.FindGuildByLeader(leader); - if (leader == 0) - c->Message(Chat::White, "New leader not found."); - else if (tmpdb != 0) { - c->Message(Chat::White, "Error: %s already is the leader of guild # %i", sep->arg[2], tmpdb); - } - else { - uint32 id = atoi(sep->arg[2]); - - if(!guild_mgr.GuildExists(id)) { - c->Message(Chat::White, "Guild %d does not exist!", id); - return; - } - - if(admin < minStatusToEditOtherGuilds) { - //this person is not allowed to just edit any guild, check this guild's min status. - if(c->GuildID() != id) { - c->Message(Chat::Red, "Access denied to edit other people's guilds"); - return; - } else if(!guild_mgr.CheckGMStatus(id, admin)) { - c->Message(Chat::Red, "Access denied to edit your guild with GM commands."); - return; - } - } - - LogGuilds("[{}]: Setting leader of guild [{}] ([{}]) to [{}] with GM command", c->GetName(), - guild_mgr.GetGuildName(id), id, leader); - - if(!guild_mgr.SetGuildLeader(id, leader)) - c->Message(Chat::White, "Guild leader change failed."); - else { - c->Message(Chat::White, "Guild leader changed: guild # %d, Leader: %s", id, sep->argplus[3]); - } - } - } - } - else if (strcasecmp(sep->arg[1], "list") == 0) { - if(admin < minStatusToEditOtherGuilds) { - c->Message(Chat::Red, "Access denied."); - return; - } - guild_mgr.ListGuilds(c); - } - else { - c->Message(Chat::White, "Unknown guild command, try #guild help"); - } -} -/* -bool helper_guild_edit(Client *c, uint32 dbid, uint32 eqid, uint8 rank, const char* what, const char* value) { - struct GuildRankLevel_Struct grl; - strcpy(grl.rankname, guild_mgr.GetRankName(eqid, rank)); - grl.demote = guilds[eqid].rank[rank].demote; - grl.heargu = guilds[eqid].rank[rank].heargu; - grl.invite = guilds[eqid].rank[rank].invite; - grl.motd = guilds[eqid].rank[rank].motd; - grl.promote = guilds[eqid].rank[rank].promote; - grl.remove = guilds[eqid].rank[rank].remove; - grl.speakgu = guilds[eqid].rank[rank].speakgu; - grl.warpeace = guilds[eqid].rank[rank].warpeace; - - if (strcasecmp(what, "title") == 0) { - if (strlen(value) > 100) - c->Message(Chat::White, "Error: Title has a maxium length of 100 characters."); - else - strcpy(grl.rankname, value); - } - else if (rank == 0) - c->Message(Chat::White, "Error: Rank 0's permissions can not be changed."); - else { - if (!(strlen(value) == 1 && (value[0] == '0' || value[0] == '1'))) - - return false; - if (strcasecmp(what, "demote") == 0) - grl.demote = (value[0] == '1'); - else if (strcasecmp(what, "heargu") == 0) - grl.heargu = (value[0] == '1'); - else if (strcasecmp(what, "invite") == 0) - grl.invite = (value[0] == '1'); - else if (strcasecmp(what, "motd") == 0) - grl.motd = (value[0] == '1'); - else if (strcasecmp(what, "promote") == 0) - grl.promote = (value[0] == '1'); - else if (strcasecmp(what, "remove") == 0) - - grl.remove = (value[0] == '1'); - else if (strcasecmp(what, "speakgu") == 0) - grl.speakgu = (value[0] == '1'); - else if (strcasecmp(what, "warpeace") == 0) - grl.warpeace = (value[0] == '1'); - else - c->Message(Chat::White, "Error: Permission name not recognized."); - } - if (!database.EditGuild(dbid, rank, &grl)) - c->Message(Chat::White, "Error: database.EditGuild() failed"); - return true; -}*/ - -void command_zonestatus(Client *c, const Seperator *sep) -{ - if (!worldserver.Connected()) - c->Message(Chat::White, "Error: World server disconnected"); - else { - auto pack = new ServerPacket(ServerOP_ZoneStatus, strlen(c->GetName()) + 2); - memset(pack->pBuffer, (uint8) c->Admin(), 1); - strcpy((char *) &pack->pBuffer[1], c->GetName()); - worldserver.SendPacket(pack); - delete pack; - } -} - -void command_doanim(Client *c, const Seperator *sep) -{ - if (!sep->IsNumber(1)) - c->Message(Chat::White, "Usage: #DoAnim [number]"); - else - if (c->Admin() >= commandDoAnimOthers) - if (c->GetTarget() == 0) - c->Message(Chat::White, "Error: You need a target."); - else - c->GetTarget()->DoAnim(atoi(sep->arg[1]),atoi(sep->arg[2])); - else - c->DoAnim(atoi(sep->arg[1]),atoi(sep->arg[2])); -} - -void command_dz(Client* c, const Seperator* sep) -{ - if (!c || !zone) { - return; - } - - if (strcasecmp(sep->arg[1], "cache") == 0) - { - if (strcasecmp(sep->arg[2], "reload") == 0) - { - DynamicZone::CacheAllFromDatabase(); - Expedition::CacheAllFromDatabase(); - c->Message(Chat::White, fmt::format( - "Reloaded [{}] dynamic zone(s) and [{}] expedition(s) from database", - zone->dynamic_zone_cache.size(), zone->expedition_cache.size() - ).c_str()); - } - } - else if (strcasecmp(sep->arg[1], "expedition") == 0) - { - if (strcasecmp(sep->arg[2], "list") == 0) - { - std::vector expeditions; - for (const auto& expedition : zone->expedition_cache) - { - expeditions.emplace_back(expedition.second.get()); - } - - std::sort(expeditions.begin(), expeditions.end(), - [](const Expedition* lhs, const Expedition* rhs) { - return lhs->GetID() < rhs->GetID(); - }); - - c->Message(Chat::White, fmt::format("Total Active Expeditions: [{}]", expeditions.size()).c_str()); - for (const auto& expedition : expeditions) - { - auto dz = expedition->GetDynamicZone(); - if (!dz) - { - LogExpeditions("Expedition [{}] has an invalid dz [{}] in cache", expedition->GetID(), expedition->GetDynamicZoneID()); - continue; - } - - auto leader_saylink = EQ::SayLinkEngine::GenerateQuestSaylink(fmt::format( - "#goto {}", expedition->GetLeaderName()), false, expedition->GetLeaderName()); - auto zone_saylink = EQ::SayLinkEngine::GenerateQuestSaylink(fmt::format( - "#zoneinstance {}", dz->GetInstanceID()), false, "zone"); - - auto seconds = dz->GetSecondsRemaining(); - - c->Message(Chat::White, fmt::format( - "expedition id: [{}] dz id: [{}] name: [{}] leader: [{}] {}: [{}]:[{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]", - expedition->GetID(), - expedition->GetDynamicZoneID(), - expedition->GetName(), - leader_saylink, - zone_saylink, - ZoneName(dz->GetZoneID()), - dz->GetZoneID(), - dz->GetInstanceID(), - dz->GetZoneVersion(), - dz->GetMemberCount(), - seconds / 3600, // hours - (seconds / 60) % 60, // minutes - seconds % 60 // seconds - ).c_str()); - } - } - else if (strcasecmp(sep->arg[2], "reload") == 0) - { - Expedition::CacheAllFromDatabase(); - c->Message(Chat::White, fmt::format( - "Reloaded [{}] expeditions to cache from database.", zone->expedition_cache.size() - ).c_str()); - } - else if (strcasecmp(sep->arg[2], "destroy") == 0 && sep->IsNumber(3)) - { - auto expedition_id = std::strtoul(sep->arg[3], nullptr, 10); - auto expedition = Expedition::FindCachedExpeditionByID(expedition_id); - if (expedition) - { - c->Message(Chat::White, fmt::format("Destroying expedition [{}] ({})", - expedition_id, expedition->GetName()).c_str()); - expedition->GetDynamicZone()->RemoveAllMembers(); - } - else - { - c->Message(Chat::Red, fmt::format("Failed to destroy expedition [{}]", sep->arg[3]).c_str()); - } - } - else if (strcasecmp(sep->arg[2], "unlock") == 0 && sep->IsNumber(3)) - { - auto expedition_id = std::strtoul(sep->arg[3], nullptr, 10); - auto expedition = Expedition::FindCachedExpeditionByID(expedition_id); - if (expedition) - { - c->Message(Chat::White, fmt::format("Unlocking expedition [{}]", expedition_id).c_str()); - expedition->SetLocked(false, ExpeditionLockMessage::None, true); - } - else - { - c->Message(Chat::Red, fmt::format("Failed to find expedition [{}]", sep->arg[3]).c_str()); - } - } - } - else if (strcasecmp(sep->arg[1], "list") == 0) - { - c->Message(Chat::White, fmt::format("Total Dynamic Zones (cache): [{}]", zone->dynamic_zone_cache.size()).c_str()); - - std::vector dynamic_zones; - for (const auto& dz : zone->dynamic_zone_cache) - { - dynamic_zones.emplace_back(dz.second.get()); - } - - std::sort(dynamic_zones.begin(), dynamic_zones.end(), - [](const DynamicZone* lhs, const DynamicZone* rhs) { - return lhs->GetID() < rhs->GetID(); - }); - - for (const auto& dz : dynamic_zones) - { - auto seconds = dz->GetSecondsRemaining(); - auto zone_saylink = EQ::SayLinkEngine::GenerateQuestSaylink( - fmt::format("#zoneinstance {}", dz->GetInstanceID()), false, "zone"); - - std::string aligned_type = fmt::format("[{}]", DynamicZone::GetDynamicZoneTypeName(static_cast(dz->GetType()))); - c->Message(Chat::White, fmt::format( - "id: [{}] type: {:>10} {}: [{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]", - dz->GetID(), - aligned_type, - zone_saylink, - dz->GetZoneID(), - dz->GetInstanceID(), - dz->GetZoneVersion(), - dz->GetMemberCount(), - seconds / 3600, // hours - (seconds / 60) % 60, // minutes - seconds % 60 // seconds - ).c_str()); - } - } - else if (strcasecmp(sep->arg[1], "listdb") == 0) - { - auto dz_list = DynamicZonesRepository::AllDzInstancePlayerCounts(database); - c->Message(Chat::White, fmt::format("Total Dynamic Zones (database): [{}]", dz_list.size()).c_str()); - - auto now = std::chrono::system_clock::now(); - - for (const auto& dz : dz_list) - { - auto expire_time = std::chrono::system_clock::from_time_t(dz.start_time + dz.duration); - auto remaining = std::chrono::duration_cast(expire_time - now); - auto seconds = std::max(0, static_cast(remaining.count())); - bool is_expired = now > expire_time; - - if (!is_expired || strcasecmp(sep->arg[2], "all") == 0) - { - auto zone_saylink = is_expired ? "zone" : EQ::SayLinkEngine::GenerateQuestSaylink( - fmt::format("#zoneinstance {}", dz.instance), false, "zone"); - - c->Message(Chat::White, fmt::format( - "id: [{}] type: [{}] {}: [{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]", - dz.id, - DynamicZone::GetDynamicZoneTypeName(static_cast(dz.type)), - zone_saylink, - dz.zone, - dz.instance, - dz.version, - dz.member_count, - seconds / 3600, // hours - (seconds / 60) % 60, // minutes - seconds % 60 // seconds - ).c_str()); - } - } - } - else if (strcasecmp(sep->arg[1], "lockouts") == 0) - { - if (strcasecmp(sep->arg[2], "remove") == 0 && sep->arg[3][0] != '\0') - { - if (sep->arg[5][0] == '\0') - { - c->Message(Chat::White, fmt::format( - "Removing [{}] lockouts on [{}].", sep->arg[4][0] ? sep->arg[4] : "all", sep->arg[3] - ).c_str()); - } - else - { - c->Message(Chat::White, fmt::format( - "Removing [{}]:[{}] lockout on [{}].", sep->arg[4], sep->arg[5], sep->arg[3] - ).c_str()); - } - Expedition::RemoveLockoutsByCharacterName(sep->arg[3], sep->arg[4], sep->arg[5]); - } - } - else if (strcasecmp(sep->arg[1], "makeleader") == 0 && sep->IsNumber(2) && sep->arg[3][0] != '\0') - { - auto expedition_id = std::strtoul(sep->arg[2], nullptr, 10); - auto expedition = Expedition::FindCachedExpeditionByID(expedition_id); - if (expedition) - { - auto char_name = FormatName(sep->arg[3]); - c->Message(Chat::White, fmt::format("Setting expedition [{}] leader to [{}]", expedition_id, char_name).c_str()); - expedition->SendWorldMakeLeaderRequest(c->CharacterID(), char_name); - } - else - { - c->Message(Chat::Red, fmt::format("Failed to find expedition [{}]", expedition_id).c_str()); - } - } - else - { - c->Message(Chat::White, "#dz usage:"); - c->Message(Chat::White, "#dz cache reload - reload the current zone cache from db (also reloads expedition cache dependency)"); - c->Message(Chat::White, "#dz expedition list - list expeditions in current zone cache"); - c->Message(Chat::White, "#dz expedition reload - reload expedition zone cache from database"); - c->Message(Chat::White, "#dz expedition destroy - destroy expedition globally (must be in cache)"); - c->Message(Chat::White, "#dz expedition unlock - unlock expedition"); - c->Message(Chat::White, "#dz list - list all dynamic zone instances from current zone cache"); - c->Message(Chat::White, "#dz listdb [all] - list dynamic zone instances from database -- 'all' includes expired"); - c->Message(Chat::White, "#dz lockouts remove - delete all of character's expedition lockouts"); - c->Message(Chat::White, "#dz lockouts remove \"\" - delete lockouts by expedition"); - c->Message(Chat::White, "#dz lockouts remove \"\" \"\" - delete lockout by expedition event"); - c->Message(Chat::White, "#dz makeleader - set new expedition leader"); - } -} - -void command_dzkickplayers(Client* c, const Seperator* sep) -{ - if (c) - { - auto expedition = c->GetExpedition(); - if (expedition) - { - expedition->DzKickPlayers(c); - } - } -} - -void command_editmassrespawn(Client* c, const Seperator* sep) -{ - if (strcasecmp(sep->arg[1], "usage") == 0) { - c->Message(Chat::White, "#editmassrespawn [exact_match: =]npc_type_name new_respawn_seconds (apply)"); - return; - } - - std::string search_npc_type; - if (sep->arg[1]) { - search_npc_type = sep->arg[1]; - } - - int change_respawn_seconds = 0; - if (sep->arg[2] && sep->IsNumber(2)) { - change_respawn_seconds = atoi(sep->arg[2]); - } - - bool change_apply = false; - if (sep->arg[3] && strcasecmp(sep->arg[3], "apply") == 0) { - change_apply = true; - } - - std::string search_encapsulator = "%"; - if (search_npc_type[0] == '=') { - - search_npc_type = search_npc_type.substr(1); - search_encapsulator = ""; - } - - std::string query = fmt::format( - SQL( - SELECT npc_types.id, spawn2.spawngroupID, spawn2.id, npc_types.name, spawn2.respawntime - FROM spawn2 - INNER JOIN spawnentry ON spawn2.spawngroupID = spawnentry.spawngroupID - INNER JOIN npc_types ON spawnentry.npcID = npc_types.id - WHERE spawn2.zone LIKE '{}' - AND spawn2.version = '{}' - AND npc_types.name LIKE '{}{}{}' - ORDER BY npc_types.id, spawn2.spawngroupID, spawn2.id - ), - zone->GetShortName(), - zone->GetInstanceVersion(), - search_encapsulator, - search_npc_type, - search_encapsulator - ); - - std::string status = "(Searching)"; - if (change_apply) { - status = "(Applying)"; - } - - int results_count = 0; - - auto results = content_db.QueryDatabase(query); - if (results.Success() && results.RowCount()) { - - results_count = results.RowCount(); - - for (auto row : results) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC (npcid:{}) (sgid:{}) (s2id:{}) [{}] Respawn: Current [{}] New [{}] {}", - row[0], - row[1], - row[2], - row[3], - row[4], - change_respawn_seconds, - status - ).c_str() - ); - } - - c->Message(Chat::Yellow, "Found (%i) NPC's that match this search...", results_count); - - if (change_respawn_seconds > 0) { - - if (change_apply) { - - results = content_db.QueryDatabase( - fmt::format( - SQL( - UPDATE spawn2 - SET respawntime = '{}' - WHERE id IN ( - SELECT spawn2.id - FROM spawn2 - INNER JOIN spawnentry ON spawn2.spawngroupID = spawnentry.spawngroupID - INNER JOIN npc_types ON spawnentry.npcID = npc_types.id - WHERE spawn2.zone LIKE '{}' - AND spawn2.version = '{}' - AND npc_types.name LIKE '{}{}{}' - ) - ), - change_respawn_seconds, - zone->GetShortName(), - zone->GetInstanceVersion(), - search_encapsulator, - search_npc_type, - search_encapsulator - ) - ); - - if (results.Success()) { - - c->Message(Chat::Yellow, "Changes applied to (%i) NPC 'Spawn2' entries", results_count); - zone->Repop(); - } - else { - - c->Message(Chat::Yellow, "Found (0) NPC's that match this search..."); - } - } - else { - - std::string saylink = fmt::format( - "#editmassrespawn {}{} {} apply", - (search_encapsulator.empty() ? "=" : ""), - search_npc_type, - change_respawn_seconds - ); - - c->Message( - Chat::Yellow, "To apply these changes, click <%s> or type [%s]", - EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Apply").c_str(), - saylink.c_str() - ); - } - } - } - else { - - c->Message(Chat::Yellow, "Found (0) NPC's that match this search..."); - } -} - -void command_randomfeatures(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - if (!target) - c->Message(Chat::White,"Error: This command requires a target"); - else - { - if (target->RandomizeFeatures()) - c->Message(Chat::White,"Features Randomized"); - else - c->Message(Chat::White,"This command requires a Playable Race as the target"); - } -} - -void command_face(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - if (!sep->IsNumber(1)) - c->Message(Chat::White,"Usage: #face [number of face]"); - else if (!target) - c->Message(Chat::White,"Error: this command requires a target"); - else { - uint16 Race = target->GetRace(); - uint8 Gender = target->GetGender(); - uint8 Texture = 0xFF; - uint8 HelmTexture = 0xFF; - uint8 HairColor = target->GetHairColor(); - uint8 BeardColor = target->GetBeardColor(); - uint8 EyeColor1 = target->GetEyeColor1(); - uint8 EyeColor2 = target->GetEyeColor2(); - uint8 HairStyle = target->GetHairStyle(); - uint8 LuclinFace = atoi(sep->arg[1]); - uint8 Beard = target->GetBeard(); - uint32 DrakkinHeritage = target->GetDrakkinHeritage(); - uint32 DrakkinTattoo = target->GetDrakkinTattoo(); - uint32 DrakkinDetails = target->GetDrakkinDetails(); - - target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, - EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, - DrakkinHeritage, DrakkinTattoo, DrakkinDetails); - - c->Message(Chat::White,"Face = %i", atoi(sep->arg[1])); - } -} - void command_findaliases(Client *c, const Seperator *sep) { if (!sep->arg[1][0]) { @@ -8448,6845 +959,6 @@ void command_findaliases(Client *c, const Seperator *sep) c->Message(Chat::White, "%d command alias%s listed.", commandaliasesshown, commandaliasesshown != 1 ? "es" : ""); } -void command_details(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - if (!sep->IsNumber(1)) - c->Message(Chat::White,"Usage: #details [number of drakkin detail]"); - else if (!target) - c->Message(Chat::White,"Error: this command requires a target"); - else { - uint16 Race = target->GetRace(); - uint8 Gender = target->GetGender(); - uint8 Texture = 0xFF; - uint8 HelmTexture = 0xFF; - uint8 HairColor = target->GetHairColor(); - uint8 BeardColor = target->GetBeardColor(); - uint8 EyeColor1 = target->GetEyeColor1(); - uint8 EyeColor2 = target->GetEyeColor2(); - uint8 HairStyle = target->GetHairStyle(); - uint8 LuclinFace = target->GetLuclinFace(); - uint8 Beard = target->GetBeard(); - uint32 DrakkinHeritage = target->GetDrakkinHeritage(); - uint32 DrakkinTattoo = target->GetDrakkinTattoo(); - uint32 DrakkinDetails = atoi(sep->arg[1]); - - target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, - EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, - DrakkinHeritage, DrakkinTattoo, DrakkinDetails); - - c->Message(Chat::White,"Details = %i", atoi(sep->arg[1])); - } -} - -void command_heritage(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - if (!sep->IsNumber(1)) - c->Message(Chat::White,"Usage: #heritage [number of Drakkin heritage]"); - else if (!target) - c->Message(Chat::White,"Error: this command requires a target"); - else { - uint16 Race = target->GetRace(); - uint8 Gender = target->GetGender(); - uint8 Texture = 0xFF; - uint8 HelmTexture = 0xFF; - uint8 HairColor = target->GetHairColor(); - uint8 BeardColor = target->GetBeardColor(); - uint8 EyeColor1 = target->GetEyeColor1(); - uint8 EyeColor2 = target->GetEyeColor2(); - uint8 HairStyle = target->GetHairStyle(); - uint8 LuclinFace = target->GetLuclinFace(); - uint8 Beard = target->GetBeard(); - uint32 DrakkinHeritage = atoi(sep->arg[1]); - uint32 DrakkinTattoo = target->GetDrakkinTattoo(); - uint32 DrakkinDetails = target->GetDrakkinDetails(); - - target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, - EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, - DrakkinHeritage, DrakkinTattoo, DrakkinDetails); - - c->Message(Chat::White,"Heritage = %i", atoi(sep->arg[1])); - } -} - -void command_tattoo(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - if (!sep->IsNumber(1)) - c->Message(Chat::White,"Usage: #tattoo [number of Drakkin tattoo]"); - else if (!target) - c->Message(Chat::White,"Error: this command requires a target"); - else { - uint16 Race = target->GetRace(); - uint8 Gender = target->GetGender(); - uint8 Texture = 0xFF; - uint8 HelmTexture = 0xFF; - uint8 HairColor = target->GetHairColor(); - uint8 BeardColor = target->GetBeardColor(); - uint8 EyeColor1 = target->GetEyeColor1(); - uint8 EyeColor2 = target->GetEyeColor2(); - uint8 HairStyle = target->GetHairStyle(); - uint8 LuclinFace = target->GetLuclinFace(); - uint8 Beard = target->GetBeard(); - uint32 DrakkinHeritage = target->GetDrakkinHeritage(); - uint32 DrakkinTattoo = atoi(sep->arg[1]); - uint32 DrakkinDetails = target->GetDrakkinDetails(); - - target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, - EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, - DrakkinHeritage, DrakkinTattoo, DrakkinDetails); - - c->Message(Chat::White,"Tattoo = %i", atoi(sep->arg[1])); - } -} - -void command_helm(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - if (!sep->IsNumber(1)) - c->Message(Chat::White,"Usage: #helm [number of helm texture]"); - else if (!target) - c->Message(Chat::White,"Error: this command requires a target"); - else { - uint16 Race = target->GetRace(); - uint8 Gender = target->GetGender(); - uint8 Texture = 0xFF; - uint8 HelmTexture = atoi(sep->arg[1]); - uint8 HairColor = target->GetHairColor(); - uint8 BeardColor = target->GetBeardColor(); - uint8 EyeColor1 = target->GetEyeColor1(); - uint8 EyeColor2 = target->GetEyeColor2(); - uint8 HairStyle = target->GetHairStyle(); - uint8 LuclinFace = target->GetLuclinFace(); - uint8 Beard = target->GetBeard(); - uint32 DrakkinHeritage = target->GetDrakkinHeritage(); - uint32 DrakkinTattoo = target->GetDrakkinTattoo(); - uint32 DrakkinDetails = target->GetDrakkinDetails(); - - target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, - EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, - DrakkinHeritage, DrakkinTattoo, DrakkinDetails); - - c->Message(Chat::White,"Helm = %i", atoi(sep->arg[1])); - } -} - -void command_hair(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - if (!sep->IsNumber(1)) - c->Message(Chat::White,"Usage: #hair [number of hair style]"); - else if (!target) - c->Message(Chat::White,"Error: this command requires a target"); - else { - uint16 Race = target->GetRace(); - uint8 Gender = target->GetGender(); - uint8 Texture = 0xFF; - uint8 HelmTexture = 0xFF; - uint8 HairColor = target->GetHairColor(); - uint8 BeardColor = target->GetBeardColor(); - uint8 EyeColor1 = target->GetEyeColor1(); - uint8 EyeColor2 = target->GetEyeColor2(); - uint8 HairStyle = atoi(sep->arg[1]); - uint8 LuclinFace = target->GetLuclinFace(); - uint8 Beard = target->GetBeard(); - uint32 DrakkinHeritage = target->GetDrakkinHeritage(); - uint32 DrakkinTattoo = target->GetDrakkinTattoo(); - uint32 DrakkinDetails = target->GetDrakkinDetails(); - - target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, - EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, - DrakkinHeritage, DrakkinTattoo, DrakkinDetails); - - c->Message(Chat::White,"Hair = %i", atoi(sep->arg[1])); - } -} - -void command_haircolor(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - if (!sep->IsNumber(1)) - c->Message(Chat::White,"Usage: #haircolor [number of hair color]"); - else if (!target) - c->Message(Chat::White,"Error: this command requires a target"); - else { - uint16 Race = target->GetRace(); - uint8 Gender = target->GetGender(); - uint8 Texture = 0xFF; - uint8 HelmTexture = 0xFF; - uint8 HairColor = atoi(sep->arg[1]); - uint8 BeardColor = target->GetBeardColor(); - uint8 EyeColor1 = target->GetEyeColor1(); - uint8 EyeColor2 = target->GetEyeColor2(); - uint8 HairStyle = target->GetHairStyle(); - uint8 LuclinFace = target->GetLuclinFace(); - uint8 Beard = target->GetBeard(); - uint32 DrakkinHeritage = target->GetDrakkinHeritage(); - uint32 DrakkinTattoo = target->GetDrakkinTattoo(); - uint32 DrakkinDetails = target->GetDrakkinDetails(); - - target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, - EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, - DrakkinHeritage, DrakkinTattoo, DrakkinDetails); - - c->Message(Chat::White,"Hair Color = %i", atoi(sep->arg[1])); - } -} - -void command_beard(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - if (!sep->IsNumber(1)) - c->Message(Chat::White,"Usage: #beard [number of beard style]"); - else if (!target) - c->Message(Chat::White,"Error: this command requires a target"); - else { - uint16 Race = target->GetRace(); - uint8 Gender = target->GetGender(); - uint8 Texture = 0xFF; - uint8 HelmTexture = 0xFF; - uint8 HairColor = target->GetHairColor(); - uint8 BeardColor = target->GetBeardColor(); - uint8 EyeColor1 = target->GetEyeColor1(); - uint8 EyeColor2 = target->GetEyeColor2(); - uint8 HairStyle = target->GetHairStyle(); - uint8 LuclinFace = target->GetLuclinFace(); - uint8 Beard = atoi(sep->arg[1]); - uint32 DrakkinHeritage = target->GetDrakkinHeritage(); - uint32 DrakkinTattoo = target->GetDrakkinTattoo(); - uint32 DrakkinDetails = target->GetDrakkinDetails(); - - target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, - EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, - DrakkinHeritage, DrakkinTattoo, DrakkinDetails); - - c->Message(Chat::White,"Beard = %i", atoi(sep->arg[1])); - } -} - -void command_beardcolor(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget(); - if (!sep->IsNumber(1)) - c->Message(Chat::White,"Usage: #beardcolor [number of beard color]"); - else if (!target) - c->Message(Chat::White,"Error: this command requires a target"); - else { - uint16 Race = target->GetRace(); - uint8 Gender = target->GetGender(); - uint8 Texture = 0xFF; - uint8 HelmTexture = 0xFF; - uint8 HairColor = target->GetHairColor(); - uint8 BeardColor = atoi(sep->arg[1]); - uint8 EyeColor1 = target->GetEyeColor1(); - uint8 EyeColor2 = target->GetEyeColor2(); - uint8 HairStyle = target->GetHairStyle(); - uint8 LuclinFace = target->GetLuclinFace(); - uint8 Beard = target->GetBeard(); - uint32 DrakkinHeritage = target->GetDrakkinHeritage(); - uint32 DrakkinTattoo = target->GetDrakkinTattoo(); - uint32 DrakkinDetails = target->GetDrakkinDetails(); - - target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, - EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, - DrakkinHeritage, DrakkinTattoo, DrakkinDetails); - - c->Message(Chat::White,"Beard Color = %i", atoi(sep->arg[1])); - } -} - -void command_scribespells(Client *c, const Seperator *sep) -{ - Client *target = c; - if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { - target = c->GetTarget()->CastToClient(); - } - - if(sep->argnum < 1 || !sep->IsNumber(1)) { - c->Message(Chat::White, "FORMAT: #scribespells "); - return; - } - - uint8 rule_max_level = (uint8) RuleI(Character, MaxLevel); - uint8 max_level = (uint8) std::stoi(sep->arg[1]); - uint8 min_level = ( - sep->IsNumber(2) ? - (uint8) - std::stoi(sep->arg[2]) : - 1 - ); // Default to Level 1 if there isn't a 2nd argument - - if (!c->GetGM()) { // Default to Character:MaxLevel if we're not a GM and Level is higher than the max level - if (max_level > rule_max_level) { - max_level = rule_max_level; - } - - if (min_level > rule_max_level) { - min_level = rule_max_level; - } - } - - if(max_level < 1 || min_level < 1) { - c->Message(Chat::White, "ERROR: Level must be greater than or equal to 1."); - return; - } - - if (min_level > max_level) { - c->Message(Chat::White, "ERROR: Minimum Level must be less than or equal to Maximum Level."); - return; - } - - uint16 scribed_spells = target->ScribeSpells(min_level, max_level); - if (target != c) { - std::string spell_message = ( - scribed_spells > 0 ? - ( - scribed_spells == 1 ? - "A new spell" : - fmt::format("{} New spells", scribed_spells) - ) : - "No new spells" - ); - c->Message( - Chat::White, - fmt::format( - "{} scribed for {}.", - spell_message, - target->GetCleanName() - ).c_str() - ); - } -} - -void command_scribespell(Client *c, const Seperator *sep) { - uint16 spell_id = 0; - uint16 book_slot = -1; - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) - t=c->GetTarget()->CastToClient(); - - if(!sep->arg[1][0]) { - c->Message(Chat::White, "FORMAT: #scribespell "); - return; - } - - spell_id = atoi(sep->arg[1]); - - if(IsValidSpell(spell_id)) { - t->Message(Chat::White, "Scribing spell: %s (%i) to spellbook.", spells[spell_id].name, spell_id); - - if(t != c) - c->Message(Chat::White, "Scribing spell: %s (%i) for %s.", spells[spell_id].name, spell_id, t->GetName()); - - LogInfo("Scribe spell: [{}] ([{}]) request for [{}] from [{}]", spells[spell_id].name, spell_id, t->GetName(), c->GetName()); - - if (spells[spell_id].classes[WARRIOR] != 0 && spells[spell_id].skill != 52 && spells[spell_id].classes[t->GetPP().class_ - 1] > 0 && !IsDiscipline(spell_id)) { - book_slot = t->GetNextAvailableSpellBookSlot(); - - if(book_slot >= 0 && t->FindSpellBookSlotBySpellID(spell_id) < 0) - t->ScribeSpell(spell_id, book_slot); - else { - t->Message(Chat::Red, "Unable to scribe spell: %s (%i) to your spellbook.", spells[spell_id].name, spell_id); - - if(t != c) - c->Message(Chat::Red, "Unable to scribe spell: %s (%i) for %s.", spells[spell_id].name, spell_id, t->GetName()); - } - } - else - c->Message(Chat::Red, "Your target can not scribe this spell."); - } - else - c->Message(Chat::Red, "Spell ID: %i is an unknown spell and cannot be scribed.", spell_id); -} - -void command_unscribespell(Client *c, const Seperator *sep) { - uint16 spell_id = 0; - uint16 book_slot = -1; - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) - t=c->GetTarget()->CastToClient(); - - if(!sep->arg[1][0]) { - c->Message(Chat::White, "FORMAT: #unscribespell "); - return; - } - - spell_id = atoi(sep->arg[1]); - - if(IsValidSpell(spell_id)) { - book_slot = t->FindSpellBookSlotBySpellID(spell_id); - - if(book_slot >= 0) { - t->UnscribeSpell(book_slot); - - t->Message(Chat::White, "Unscribing spell: %s (%i) from spellbook.", spells[spell_id].name, spell_id); - - if(t != c) - c->Message(Chat::White, "Unscribing spell: %s (%i) for %s.", spells[spell_id].name, spell_id, t->GetName()); - - LogInfo("Unscribe spell: [{}] ([{}]) request for [{}] from [{}]", spells[spell_id].name, spell_id, t->GetName(), c->GetName()); - } - else { - t->Message(Chat::Red, "Unable to unscribe spell: %s (%i) from your spellbook. This spell is not scribed.", spells[spell_id].name, spell_id); - - if(t != c) - c->Message(Chat::Red, "Unable to unscribe spell: %s (%i) for %s due to spell not scribed.", spells[spell_id].name, spell_id, t->GetName()); - } - } -} - -void command_unscribespells(Client *c, const Seperator *sep) -{ - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) - t=c->GetTarget()->CastToClient(); - - t->UnscribeSpellAll(); -} - -void command_untraindisc(Client *c, const Seperator *sep) { - Client *t = c; - if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) - t = c->GetTarget()->CastToClient(); - - for (int i = 0; i < MAX_PP_DISCIPLINES; i++) { - if (t->GetPP().disciplines.values[i] == atoi(sep->arg[1])) { - t->UntrainDisc(i, 1); - return; - } - } -} - -void command_untraindiscs(Client *c, const Seperator *sep) { - Client *t = c; - if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) - t = c->GetTarget()->CastToClient(); - - t->UntrainDiscAll(); - t->Message(Chat::Yellow, "All disciplines removed."); -} - -void command_wpinfo(Client *c, const Seperator *sep) -{ - Mob *t=c->GetTarget(); - - if (t == nullptr || !t->IsNPC()) { - c->Message(Chat::White,"You must target an NPC to use this."); - return; - } - - NPC *n = t->CastToNPC(); - n->DisplayWaypointInfo(c); -} - -void command_wpadd(Client *c, const Seperator *sep) -{ - int type1 = 0, type2 = 0, pause = 0; // Defaults for a new grid - Mob *target = c->GetTarget(); - if (target && target->IsNPC()) { - Spawn2 *s2info = target->CastToNPC()->respawn2; - if (s2info == nullptr) { - c->Message( - Chat::White, - "#wpadd Failed, you must target a valid spawn." - ); - return; - } - - if (sep->arg[1][0]) { - if (atoi(sep->arg[1]) >= 0) { - pause = atoi(sep->arg[1]); - } else { - c->Message(Chat::White, "Usage: #wpadd [pause] [-h]"); - return; - } - } - auto position = c->GetPosition(); - if (strcmp("-h", sep->arg[2]) != 0) { - position.w = -1; - } - - auto zone_id = zone->GetZoneID(); - uint32 tmp_grid = content_db.AddWPForSpawn(c, s2info->GetID(), position, pause, type1, type2, zone_id); - if (tmp_grid) { - target->CastToNPC()->SetGrid(tmp_grid); - } - - auto grid_id = target->CastToNPC()->GetGrid(); - target->CastToNPC()->AssignWaypoints(grid_id); - c->Message( - Chat::White, - fmt::format( - "Waypoint added to grid {} in zone ID {}. Use #wpinfo to see waypoints for this NPC (may need to #repop first).", - grid_id, - zone_id - ).c_str() - ); - } else { - c->Message(Chat::White, "You must target an NPC to use this."); - } -} - -void command_interrupt(Client *c, const Seperator *sep) -{ - uint16 ci_message=0x01b7, ci_color=0x0121; - - if(sep->arg[1][0]) - ci_message=atoi(sep->arg[1]); - if(sep->arg[2][0]) - ci_color=atoi(sep->arg[2]); - - c->InterruptSpell(ci_message, ci_color); -} - -void command_summonitem(Client *c, const Seperator *sep) -{ - uint32 item_id = 0; - int16 charges = -1; - uint32 augment_one = 0; - uint32 augment_two = 0; - uint32 augment_three = 0; - uint32 augment_four = 0; - uint32 augment_five = 0; - uint32 augment_six = 0; - int arguments = sep->argnum; - std::string cmd_msg = sep->msg; - size_t link_open = cmd_msg.find('\x12'); - size_t link_close = cmd_msg.find_last_of('\x12'); - if (link_open != link_close && (cmd_msg.length() - link_open) > EQ::constants::SAY_LINK_BODY_SIZE) { - EQ::SayLinkBody_Struct link_body; - EQ::saylink::DegenerateLinkBody(link_body, cmd_msg.substr(link_open + 1, EQ::constants::SAY_LINK_BODY_SIZE)); - item_id = link_body.item_id; - augment_one = link_body.augment_1; - augment_two = link_body.augment_2; - augment_three = link_body.augment_3; - augment_four = link_body.augment_4; - augment_five = link_body.augment_5; - augment_six = link_body.augment_6; - } else if (!sep->IsNumber(1)) { - c->Message(Chat::White, "Usage: #summonitem [item id | link] [charges] [augment_one_id] [augment_two_id] [augment_three_id] [augment_four_id] [augment_five_id] [augment_six_id] (Charges are optional.)"); - return; - } else { - item_id = atoi(sep->arg[1]); - } - - if (!item_id) { - c->Message(Chat::White, "Enter a valid item ID."); - return; - } - - uint8 item_status = 0; - uint8 current_status = c->Admin(); - const EQ::ItemData* item = database.GetItem(item_id); - if (item) { - item_status = item->MinStatus; - } - - if (item_status > current_status) { - c->Message( - Chat::White, - fmt::format( - "Insufficient status to summon this item, current status is {}, required status is {}.", - current_status, - item_status - ).c_str() - ); - } - - if (arguments >= 2 && sep->IsNumber(2)) { - charges = atoi(sep->arg[2]); - } - - if (arguments >= 3 && sep->IsNumber(3)) { - augment_one = atoi(sep->arg[3]); - } - - if (arguments >= 4 && sep->IsNumber(4)) { - augment_two = atoi(sep->arg[4]); - } - - if (arguments >= 5 && sep->IsNumber(5)) { - augment_three = atoi(sep->arg[5]); - } - - if (arguments >= 6 && sep->IsNumber(6)) { - augment_four = atoi(sep->arg[6]); - } - - if (arguments >= 7 && sep->IsNumber(7)) { - augment_five = atoi(sep->arg[7]); - } - - if (arguments == 8 && sep->IsNumber(8)) { - augment_six = atoi(sep->arg[8]); - } - - c->SummonItem(item_id, charges, augment_one, augment_two, augment_three, augment_four, augment_five, augment_six); -} - -void command_giveitem(Client *c, const Seperator *sep) -{ - uint32 item_id = 0; - int16 charges = -1; - uint32 augment_one = 0; - uint32 augment_two = 0; - uint32 augment_three = 0; - uint32 augment_four = 0; - uint32 augment_five = 0; - uint32 augment_six = 0; - int arguments = sep->argnum; - std::string cmd_msg = sep->msg; - size_t link_open = cmd_msg.find('\x12'); - size_t link_close = cmd_msg.find_last_of('\x12'); - if (c->GetTarget()) { - if (!c->GetTarget()->IsClient()) { - c->Message(Chat::Red, "You can only give items to players with this command."); - return; - } - - if (link_open != link_close && (cmd_msg.length() - link_open) > EQ::constants::SAY_LINK_BODY_SIZE) { - EQ::SayLinkBody_Struct link_body; - EQ::saylink::DegenerateLinkBody(link_body, cmd_msg.substr(link_open + 1, EQ::constants::SAY_LINK_BODY_SIZE)); - item_id = link_body.item_id; - augment_one = link_body.augment_1; - augment_two = link_body.augment_2; - augment_three = link_body.augment_3; - augment_four = link_body.augment_4; - augment_five = link_body.augment_5; - augment_six = link_body.augment_6; - } else if (sep->IsNumber(1)) { - item_id = atoi(sep->arg[1]); - } else if (!sep->IsNumber(1)) { - c->Message(Chat::Red, "Usage: #giveitem [item id | link] [charges] [augment_one_id] [augment_two_id] [augment_three_id] [augment_four_id] [augment_five_id] [augment_six_id] (Charges are optional.)"); - return; - } - - Client *client_target = c->GetTarget()->CastToClient(); - uint8 item_status = 0; - uint8 current_status = c->Admin(); - const EQ::ItemData* item = database.GetItem(item_id); - if (item) { - item_status = item->MinStatus; - } - - if (item_status > current_status) { - c->Message( - Chat::White, - fmt::format( - "Insufficient status to summon this item, current status is {}, required status is {}.", - current_status, - item_status - ).c_str() - ); - return; - } - - if (arguments >= 2 && sep->IsNumber(2)) { - charges = atoi(sep->arg[2]); - } - - if (arguments >= 3 && sep->IsNumber(3)) { - augment_one = atoi(sep->arg[3]); - } - - if (arguments >= 4 && sep->IsNumber(4)) { - augment_two = atoi(sep->arg[4]); - } - - if (arguments >= 5 && sep->IsNumber(5)) { - augment_three = atoi(sep->arg[5]); - } - - if (arguments >= 6 && sep->IsNumber(6)) { - augment_four = atoi(sep->arg[6]); - } - - if (arguments >= 7 && sep->IsNumber(7)) { - augment_five = atoi(sep->arg[7]); - } - - if (arguments == 8 && sep->IsNumber(8)) { - augment_six = atoi(sep->arg[8]); - } - - client_target->SummonItem(item_id, charges, augment_one, augment_two, augment_three, augment_four, augment_five, augment_six); - } else { - c->Message(Chat::Red, "You must target a client to give the item to."); - return; - } -} - -void command_givemoney(Client *c, const Seperator *sep) -{ - if (!sep->IsNumber(1)) { //as long as the first one is a number, we'll just let atoi convert the rest to 0 or a number - c->Message(Chat::Red, "Usage: #Usage: #givemoney [pp] [gp] [sp] [cp]"); - } - else if(c->GetTarget() == nullptr) { - c->Message(Chat::Red, "You must target a player to give money to."); - } - else if(!c->GetTarget()->IsClient()) { - c->Message(Chat::Red, "You can only give money to players with this command."); - } - else { - //TODO: update this to the client, otherwise the client doesn't show any weight change until you zone, move an item, etc - c->GetTarget()->CastToClient()->AddMoneyToPP(atoi(sep->arg[4]), atoi(sep->arg[3]), atoi(sep->arg[2]), atoi(sep->arg[1]), true); - c->Message(Chat::White, "Added %i Platinum, %i Gold, %i Silver, and %i Copper to %s's inventory.", atoi(sep->arg[1]), atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), c->GetTarget()->GetName()); - } -} - -void command_itemsearch(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0] == 0) - c->Message(Chat::White, "Usage: #itemsearch [search string]"); - else - { - const char *search_criteria=sep->argplus[1]; - - const EQ::ItemData* item = nullptr; - EQ::SayLinkEngine linker; - linker.SetLinkType(EQ::saylink::SayLinkItemData); - - if (Seperator::IsNumber(search_criteria)) { - item = database.GetItem(atoi(search_criteria)); - if (item) { - linker.SetItemData(item); - std::string item_id = std::to_string(item->ID); - std::string saylink_commands = - "[" + - EQ::SayLinkEngine::GenerateQuestSaylink( - "#si " + item_id, - false, - "X" - ) + - "] "; - - if (item->Stackable && item->StackSize > 1) { - std::string stack_size = std::to_string(item->StackSize); - saylink_commands += - "[" + - EQ::SayLinkEngine::GenerateQuestSaylink( - "#si " + item_id + " " + stack_size, - false, - stack_size - ) + - "]"; - } - - c->Message( - Chat::White, - fmt::format( - " Summon {} [{}] [{}]", - saylink_commands, - linker.GenerateLink(), - item->ID - ).c_str() - ); - } - else { - c->Message( - Chat::White, - fmt::format( - "Item {} not found", - search_criteria - ).c_str() - ); - } - - return; - } - - int count = 0; - char sName[64]; - char sCriteria[255]; - strn0cpy(sCriteria, search_criteria, sizeof(sCriteria)); - strupr(sCriteria); - char* pdest; - uint32 it = 0; - while ((item = database.IterateItems(&it))) { - strn0cpy(sName, item->Name, sizeof(sName)); - strupr(sName); - pdest = strstr(sName, sCriteria); - if (pdest != nullptr) { - linker.SetItemData(item); - std::string item_id = std::to_string(item->ID); - std::string saylink_commands = - "[" + - EQ::SayLinkEngine::GenerateQuestSaylink( - "#si " + item_id, - false, - "X" - ) + - "] "; - if (item->Stackable && item->StackSize > 1) { - std::string stack_size = std::to_string(item->StackSize); - saylink_commands += - "[" + - EQ::SayLinkEngine::GenerateQuestSaylink( - "#si " + item_id + " " + stack_size, - false, - stack_size - ) + - "]"; - } - - c->Message( - Chat::White, - fmt::format( - " Summon {} [{}] [{}]", - saylink_commands, - linker.GenerateLink(), - item->ID - ).c_str() - ); - - ++count; - } - - if (count == 50) - break; - } - - if (count == 50) - c->Message(Chat::White, "50 items shown...too many results."); - else - c->Message(Chat::White, "%i items found", count); - - } -} - -void command_setaaxp(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - if (arguments <= 1 || !sep->IsNumber(2)) { - c->Message(Chat::White, "Usage: #setaaxp [AA|Group|Raid] [AA Experience]"); - return; - } - - Client *target = c; - if (c->GetTarget() && c->GetTarget()->IsClient()) { - target = c->GetTarget()->CastToClient(); - } - - std::string aa_type = str_tolower(sep->arg[1]); - std::string group_raid_string; - uint32 aa_experience = static_cast(std::min(std::stoull(sep->arg[2]), (unsigned long long) 2000000000)); - bool is_aa = aa_type.find("aa") != std::string::npos; - bool is_group = aa_type.find("group") != std::string::npos; - bool is_raid = aa_type.find("raid") != std::string::npos; - if (!is_aa && !is_group && !is_raid) { - c->Message(Chat::White, "Usage: #setaaxp [AA|Group|Raid] [AA Experience]"); - return; - } - - if (is_aa) { - target->SetEXP( - target->GetEXP(), - aa_experience, - false - ); - } else if (is_group) { - group_raid_string = "Group "; - target->SetLeadershipEXP( - aa_experience, - target->GetRaidEXP() - ); - } else if (is_raid) { - group_raid_string = "Raid "; - target->SetLeadershipEXP( - target->GetGroupEXP(), - aa_experience - ); - } - - std::string aa_exp_message = fmt::format( - "{} now {} {} {}AA Experience.", - c == target ? "You" : target->GetCleanName(), - c == target ? "have" : "has", - aa_experience, - group_raid_string - ); - c->Message( - Chat::White, - aa_exp_message.c_str() - ); -} - -void command_setaapts(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - if (arguments <= 1 || !sep->IsNumber(2)) { - c->Message(Chat::White, "Usage: #setaapts [AA|Group|Raid] [AA Amount]"); - return; - } - - Client *target = c; - if (c->GetTarget() && c->GetTarget()->IsClient()) { - target = c->GetTarget()->CastToClient(); - } - - std::string aa_type = str_tolower(sep->arg[1]); - std::string group_raid_string; - uint32 aa_points = static_cast(std::min(std::stoull(sep->arg[2]), (unsigned long long) 2000000000)); - bool is_aa = aa_type.find("aa") != std::string::npos; - bool is_group = aa_type.find("group") != std::string::npos; - bool is_raid = aa_type.find("raid") != std::string::npos; - if (!is_aa && !is_group && !is_raid) { - c->Message(Chat::White, "Usage: #setaapts [AA|Group|Raid] [AA Amount]"); - return; - } - - if (is_aa) { - target->GetPP().aapoints = aa_points; - target->GetPP().expAA = 0; - target->SendAlternateAdvancementStats(); - } else if (is_group || is_raid) { - if (is_group) { - group_raid_string = "Group "; - target->GetPP().group_leadership_points = aa_points; - target->GetPP().group_leadership_exp = 0; - } else if (is_raid) { - group_raid_string = "Raid "; - target->GetPP().raid_leadership_points = aa_points; - target->GetPP().raid_leadership_exp = 0; - } - target->SendLeadershipEXPUpdate(); - } - - std::string aa_message = fmt::format( - "{} now {} {} {}AA Point{}.", - c == target ? "You" : target->GetCleanName(), - c == target ? "have" : "has", - aa_points, - group_raid_string, - aa_points != 1 ? "s" : "" - - ); - c->Message( - Chat::White, - aa_message.c_str() - ); -} - -void command_setcrystals(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - if (arguments <= 1 || !sep->IsNumber(2)) { - c->Message(Chat::White, "Usage: #setcrystals [Ebon|Radiant] [Crystal Amount]"); - return; - } - - Client *target = c; - if(c->GetTarget() && c->GetTarget()->IsClient()) { - target = c->GetTarget()->CastToClient(); - } - - std::string crystal_type = str_tolower(sep->arg[1]); - uint32 crystal_amount = static_cast(std::min(std::stoull(sep->arg[2]), (unsigned long long) 2000000000)); - bool is_ebon = crystal_type.find("ebon") != std::string::npos; - bool is_radiant = crystal_type.find("radiant") != std::string::npos; - if (!is_ebon && !is_radiant) { - c->Message(Chat::White, "Usage: #setcrystals [Ebon|Radiant] [Crystal Amount]"); - return; - } - - uint32 crystal_item_id = ( - is_ebon ? - RuleI(Zone, EbonCrystalItemID) : - RuleI(Zone, RadiantCrystalItemID) - ); - - auto crystal_link = database.CreateItemLink(crystal_item_id); - if (is_radiant) { - target->SetRadiantCrystals(crystal_amount); - } else { - target->SetEbonCrystals(crystal_amount); - } - - c->Message( - Chat::White, - fmt::format( - "{} now {} {} {}.", - c == target ? "You" : target->GetCleanName(), - c == target ? "have" : "has", - crystal_amount, - crystal_link - ).c_str() - ); -} - -void command_stun(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - if (!arguments || !sep->IsNumber(1)) { - c->Message(Chat::White, "Usage: #stun [Duration]"); - return; - } - - Mob* target = c; - int duration = static_cast(std::min(std::stoll(sep->arg[1]), (long long) 2000000000)); - - if (duration < 0) { - duration = 0; - } - - if (c->GetTarget()) { - target = c->GetTarget(); - if (target->IsClient()) { - target->CastToClient()->Stun(duration); - } else if (target->IsNPC()) { - target->CastToNPC()->Stun(duration); - } - } else { - c->Stun(duration); - } - - std::string stun_message = ( - duration ? - fmt::format( - "You stunned {} for {}.", - ( - c == target ? - "yourself" : - fmt::format( - "{} ({})", - target->GetCleanName(), - target->GetID() - ) - ), - ConvertSecondsToTime(duration) - ) : - fmt::format( - "You unstunned {}.", - ( - c == target ? - "yourself" : - fmt::format( - "{} ({})", - target->GetCleanName(), - target->GetID() - ) - ) - ) - ); - c->Message( - Chat::White, - stun_message.c_str() - ); -} - - -void command_ban(Client *c, const Seperator *sep) -{ -if(sep->arg[1][0] == 0 || sep->arg[2][0] == 0) { - c->Message(Chat::White, "Usage: #ban "); - return; - } - - auto account_id = database.GetAccountIDByChar(sep->arg[1]); - - std::string message; - int i = 2; - while(1) { - if(sep->arg[i][0] == 0) { - break; - } - - if(message.length() > 0) { - message.push_back(' '); - } - - message += sep->arg[i]; - ++i; - } - - if(message.length() == 0) { - c->Message(Chat::White, "Usage: #ban "); - return; - } - - if(account_id == 0) { - c->Message(Chat::Red, "Character does not exist."); - return; - } - - std::string query = StringFormat("UPDATE account SET status = -2, ban_reason = '%s' " - "WHERE id = %i", EscapeString(message).c_str(), account_id); - auto results = database.QueryDatabase(query); - - c->Message(Chat::Red, "Account number %i with the character %s has been banned with message: \"%s\"", account_id, sep->arg[1], message.c_str()); - - ServerPacket flagUpdatePack(ServerOP_FlagUpdate, 6); - *((uint32*)&flagUpdatePack.pBuffer[0]) = account_id; - *((int16*)&flagUpdatePack.pBuffer[4]) = -2; - worldserver.SendPacket(&flagUpdatePack); - - Client *client = nullptr; - client = entity_list.GetClientByName(sep->arg[1]); - if(client) { - client->WorldKick(); - return; - } - - ServerPacket kickPlayerPack(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); - ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*)kickPlayerPack.pBuffer; - strcpy(skp->adminname, c->GetName()); - strcpy(skp->name, sep->arg[1]); - skp->adminrank = c->Admin(); - worldserver.SendPacket(&kickPlayerPack); -} - -void command_suspend(Client *c, const Seperator *sep) -{ - if((sep->arg[1][0] == 0) || (sep->arg[2][0] == 0)) { - c->Message(Chat::White, "Usage: #suspend (Specify 0 days to lift the suspension immediately) "); - return; - } - - int duration = atoi(sep->arg[2]); - - if(duration < 0) - duration = 0; - - std::string message; - - if(duration > 0) { - int i = 3; - while(1) { - if(sep->arg[i][0] == 0) { - break; - } - - if(message.length() > 0) { - message.push_back(' '); - } - - message += sep->arg[i]; - ++i; - } - - if(message.length() == 0) { - c->Message(Chat::White, "Usage: #suspend (Specify 0 days to lift the suspension immediately) "); - return; - } - } - - auto escName = new char[strlen(sep->arg[1]) * 2 + 1]; - database.DoEscapeString(escName, sep->arg[1], strlen(sep->arg[1])); - int accountID = database.GetAccountIDByChar(escName); - safe_delete_array(escName); - - if (accountID <= 0) { - c->Message(Chat::Red,"Character does not exist."); - return; - } - - std::string query = StringFormat("UPDATE `account` SET `suspendeduntil` = DATE_ADD(NOW(), INTERVAL %i DAY), " - "suspend_reason = '%s' WHERE `id` = %i", - duration, EscapeString(message).c_str(), accountID); - auto results = database.QueryDatabase(query); - - if(duration) - c->Message(Chat::Red,"Account number %i with the character %s has been temporarily suspended for %i day(s).", accountID, sep->arg[1], duration); - else - c->Message(Chat::Red,"Account number %i with the character %s is no longer suspended.", accountID, sep->arg[1]); - - Client *bannedClient = entity_list.GetClientByName(sep->arg[1]); - - if(bannedClient) { - bannedClient->WorldKick(); - return; - } - - auto pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); - ServerKickPlayer_Struct *sks = (ServerKickPlayer_Struct *)pack->pBuffer; - - strn0cpy(sks->adminname, c->GetName(), sizeof(sks->adminname)); - strn0cpy(sks->name, sep->arg[1], sizeof(sks->name)); - sks->adminrank = c->Admin(); - - worldserver.SendPacket(pack); - - safe_delete(pack); -} - -void command_ipban(Client *c, const Seperator *sep) -{ - if(sep->arg[1] == 0) - { - c->Message(Chat::White, "Usage: #ipban [xxx.xxx.xxx.xxx]"); - } else { - if(database.AddBannedIP(sep->arg[1], c->GetName())) { - c->Message(Chat::White, "%s has been successfully added to the banned_ips table by %s", sep->arg[1], c->GetName()); - } else { - c->Message(Chat::White, "IPBan Failed (IP address is possibly already in the table?)"); - } - } -} - -void command_revoke(Client *c, const Seperator *sep) -{ - if(sep->arg[1][0] == 0 || sep->arg[2][0] == 0) { - c->Message(Chat::White, "Usage: #revoke [charname] [1/0]"); - return; - } - - uint32 characterID = database.GetAccountIDByChar(sep->arg[1]); - if(characterID == 0) { - c->Message(Chat::Red,"Character does not exist."); - return; - } - - int flag = sep->arg[2][0] == '1' ? true : false; - std::string query = StringFormat("UPDATE account SET revoked = %d WHERE id = %i", flag, characterID); - auto results = database.QueryDatabase(query); - - c->Message(Chat::Red,"%s account number %i with the character %s.", flag? "Revoking": "Unrevoking", characterID, sep->arg[1]); - - Client* revokee = entity_list.GetClientByAccID(characterID); - if(revokee) { - c->Message(Chat::White, "Found %s in this zone.", revokee->GetName()); - revokee->SetRevoked(flag); - return; - } - - c->Message(Chat::Red, "#revoke: Couldn't find %s in this zone, passing request to worldserver.", sep->arg[1]); - - auto outapp = new ServerPacket(ServerOP_Revoke, sizeof(RevokeStruct)); - RevokeStruct *revoke = (RevokeStruct *)outapp->pBuffer; - strn0cpy(revoke->adminname, c->GetName(), 64); - strn0cpy(revoke->name, sep->arg[1], 64); - revoke->toggle = flag; - worldserver.SendPacket(outapp); - safe_delete(outapp); -} - -void command_roambox(Client *c, const Seperator *sep) -{ - std::string arg1 = sep->arg[1]; - - Mob *target = c->GetTarget(); - if (!target || !target->IsNPC()) { - c->Message(Chat::Red, "You need a valid NPC target for this command"); - return; - } - - NPC *npc = dynamic_cast(target); - int spawn_group_id = npc->GetSpawnGroupId(); - if (spawn_group_id <= 0) { - c->Message(Chat::Red, "NPC needs a valid SpawnGroup!"); - return; - } - - if (arg1 == "set") { - int box_size = (sep->arg[2] ? atoi(sep->arg[2]) : 0); - int delay = (sep->arg[3] ? atoi(sep->arg[3]) : 15000); - if (box_size > 0) { - std::string query = fmt::format( - SQL( - UPDATE spawngroup SET - dist = {}, - min_x = {}, - max_x = {}, - min_y = {}, - max_y = {}, - delay = {} - WHERE id = {} - ), - (box_size / 2), - npc->GetX() - (box_size / 2), - npc->GetX() + (box_size / 2), - npc->GetY() - (box_size / 2), - npc->GetY() + (box_size / 2), - delay, - spawn_group_id - ); - - database.QueryDatabase(query); - - c->Message( - Chat::Yellow, - "NPC (%s) Roam Box set to box size of [%i] SpawnGroupId [%i] delay [%i]", - npc->GetCleanName(), - box_size, - spawn_group_id, - delay - ); - - return; - } - - c->Message(Chat::Red, "Box size must be set!"); - } - - if (arg1 == "remove") { - std::string query = fmt::format( - SQL( - UPDATE spawngroup SET - dist = 0, - min_x = 0, - max_x = 0, - min_y = 0, - max_y = 0, - delay = 0 - WHERE id = {} - ), - spawn_group_id - ); - - database.QueryDatabase(query); - - c->Message( - Chat::Yellow, - "NPC (%s) Roam Box has been removed from SpawnGroupID [%i]", - npc->GetCleanName(), - spawn_group_id - ); - - return; - } - - c->Message(Chat::Yellow, "> Command Usage"); - c->Message(Chat::Yellow, "#roambox set box_size [delay = 0]"); - c->Message(Chat::Yellow, "#roambox remove"); -} - -void command_oocmute(Client *c, const Seperator *sep) -{ - if(sep->arg[1][0] == 0 || !(sep->arg[1][0] == '1' || sep->arg[1][0] == '0')) - c->Message(Chat::White, "Usage: #oocmute [1/0]"); - else { - auto outapp = new ServerPacket(ServerOP_OOCMute, 1); - *(outapp->pBuffer) = atoi(sep->arg[1]); - worldserver.SendPacket(outapp); - safe_delete(outapp); - } -} - -void command_checklos(Client *c, const Seperator *sep) -{ - if (!c->GetTarget()) { - c->Message(Chat::White, "You must have a target to use this command."); - } - - bool has_los = c->CheckLosFN(c->GetTarget()); - c->Message( - Chat::White, - fmt::format( - "You {}have line of sight to {} ({}).", - has_los ? "" : "do not ", - c->GetTarget()->GetCleanName(), - c->GetTarget()->GetID() - ).c_str() - ); -} - -void command_set_adventure_points(Client *c, const Seperator *sep) -{ - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient()) - t=c->GetTarget()->CastToClient(); - - if(!sep->arg[1][0]) - { - c->Message(Chat::White, "Usage: #setadventurepoints [theme] [points]"); - return; - } - - if(!sep->IsNumber(1) || !sep->IsNumber(2)) - { - c->Message(Chat::White, "Usage: #setadventurepoints [theme] [points]"); - return; - } - - c->Message(Chat::White, "Updating adventure points for %s", t->GetName()); - t->UpdateLDoNPoints(atoi(sep->arg[1]), atoi(sep->arg[2])); -} - -void command_npcsay(Client *c, const Seperator *sep) -{ - if(c->GetTarget() && c->GetTarget()->IsNPC() && sep->arg[1][0]) - { - c->GetTarget()->Say(sep->argplus[1]); - } - else - { - c->Message(Chat::White, "Usage: #npcsay message (requires NPC target"); - } -} - -void command_npcshout(Client *c, const Seperator *sep) -{ - if(c->GetTarget() && c->GetTarget()->IsNPC() && sep->arg[1][0]) - { - c->GetTarget()->Shout(sep->argplus[1]); - } - else - { - c->Message(Chat::White, "Usage: #npcshout message (requires NPC target"); - } -} - -void command_timers(Client *c, const Seperator *sep) { - if(!c->GetTarget() || !c->GetTarget()->IsClient()) { - c->Message(Chat::White,"Need a player target for timers."); - return; - } - Client *them = c->GetTarget()->CastToClient(); - - std::vector< std::pair > res; - them->GetPTimers().ToVector(res); - - c->Message(Chat::White,"Timers for target:"); - - int r; - int l = res.size(); - for(r = 0; r < l; r++) { - c->Message(Chat::White,"Timer %d: %d seconds remain.", res[r].first, res[r].second->GetRemainingTime()); - } -} - -void command_npcemote(Client *c, const Seperator *sep) -{ - if(c->GetTarget() && c->GetTarget()->IsNPC() && sep->arg[1][0]) - { - c->GetTarget()->Emote(sep->argplus[1]); - } - else - { - c->Message(Chat::White, "Usage: #npcemote message (requires NPC target"); - } -} - -void command_npceditmass(Client *c, const Seperator *sep) -{ - if (strcasecmp(sep->arg[1], "usage") == 0) { - c->Message(Chat::White, "#npceditmass search_column [exact_match: =]search_value change_column change_value (apply)"); - return; - } - - std::string query = SQL( - SELECT - COLUMN_NAME - FROM - INFORMATION_SCHEMA.COLUMNS - WHERE - table_name = 'npc_types' - AND - COLUMN_NAME != 'id' - ); - - std::string search_column, search_value, change_column, change_value; - if (sep->arg[1]) { - search_column = sep->arg[1]; - } - if (sep->arg[2]) { - search_value = sep->arg[2]; - } - if (sep->arg[3]) { - change_column = sep->arg[3]; - } - if (sep->arg[4]) { - change_value = sep->arg[4]; - } - - bool valid_change_column = false; - bool valid_search_column = false; - auto results = content_db.QueryDatabase(query); - - std::vector possible_column_options; - - for (auto row = results.begin(); row != results.end(); ++row) { - if (row[0] == change_column) { - valid_change_column = true; - } - if (row[0] == search_column) { - valid_search_column = true; - } - - possible_column_options.push_back(row[0]); - } - - std::string options_glue = ", "; - - if (!valid_search_column) { - c->Message(Chat::Red, "You must specify a valid search column. [%s] is not valid", search_column.c_str()); - c->Message(Chat::Yellow, "Possible columns [%s]", implode(options_glue, possible_column_options).c_str()); - return; - } - - if (!valid_change_column) { - c->Message(Chat::Red, "You must specify a valid change column. [%s] is not valid", change_column.c_str()); - c->Message(Chat::Yellow, "Possible columns [%s]", implode(options_glue, possible_column_options).c_str()); - return; - } - - if (!valid_search_column || !valid_change_column) { - c->Message(Chat::Red, "One requested column is invalid"); - return; - } - - query = fmt::format( - SQL( - select - id, - name, - {0}, - {1} - from - npc_types - where - id IN( - select - spawnentry.npcID - from - spawnentry - join spawn2 on spawn2.spawngroupID = spawnentry.spawngroupID - where - spawn2.zone = '{2}' and spawn2.version = {3} - ) - ), - search_column, - change_column, - zone->GetShortName(), - zone->GetInstanceVersion() - ); - - std::string status = "(Searching)"; - - if (strcasecmp(sep->arg[5], "apply") == 0) { - status = "(Applying)"; - } - - std::vector npc_ids; - - bool exact_match = false; - if (search_value[0] == '=') { - exact_match = true; - search_value = search_value.substr(1); - } - - int found_count = 0; - results = content_db.QueryDatabase(query); - for (auto row = results.begin(); row != results.end(); ++row) { - - std::string npc_id = row[0]; - std::string npc_name = row[1]; - std::string search_column_value = str_tolower(row[2]); - std::string change_column_current_value = row[3]; - - if (exact_match) { - if (search_column_value.compare(search_value) != 0) { - continue; - } - } - else { - if (search_column_value.find(search_value) == std::string::npos) { - continue; - } - } - - c->Message( - Chat::Yellow, - fmt::format( - "NPC ({0}) [{1}] ({2}) [{3}] Current ({4}) [{5}] New [{6}] {7}", - npc_id, - npc_name, - search_column, - search_column_value, - change_column, - change_column_current_value, - change_value, - status - ).c_str() - ); - - npc_ids.push_back(npc_id); - - found_count++; - } - - std::string saylink = fmt::format( - "#npceditmass {} {}{} {} {} apply", - search_column, - (exact_match ? "=" : ""), - search_value, - change_column, - change_value - ); - - if (strcasecmp(sep->arg[5], "apply") == 0) { - std::string npc_ids_string = implode(",", npc_ids); - if (npc_ids_string.empty()) { - c->Message(Chat::Red, "Error: Ran into an unknown error compiling NPC IDs"); - return; - } - - content_db.QueryDatabase( - fmt::format( - "UPDATE `npc_types` SET {} = '{}' WHERE id IN ({})", - change_column, - change_value, - npc_ids_string - ) - ); - - c->Message(Chat::Yellow, "Changes applied to (%i) NPC's", found_count); - zone->Repop(); - } - else { - c->Message(Chat::Yellow, "Found (%i) NPC's that match this search...", found_count); - - if (found_count > 0) { - c->Message( - Chat::Yellow, "To apply these changes, click <%s> or type [%s]", - EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Apply").c_str(), - saylink.c_str() - ); - } - } -} - -void command_npcedit(Client *c, const Seperator *sep) -{ if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { - c->Message(Chat::White, "Error: Must have NPC targeted"); - return; - } - - if (strcasecmp(sep->arg[1], "help") == 0) { - - c->Message(Chat::White, "Help File for #npcedit. Syntax for commands are:"); - c->Message(Chat::White, "#npcedit name - Sets an NPC's Name"); - c->Message(Chat::White, "#npcedit lastname - Sets an NPC's Lastname"); - c->Message(Chat::White, "#npcedit level - Sets an NPC's Level"); - c->Message(Chat::White, "#npcedit race - Sets an NPC's Race"); - c->Message(Chat::White, "#npcedit class - Sets an NPC's Class"); - c->Message(Chat::White, "#npcedit bodytype - Sets an NPC's Bodytype"); - c->Message(Chat::White, "#npcedit hp - Sets an NPC's Hitpoints"); - c->Message(Chat::White, "#npcedit mana - Sets an NPC's Mana"); - c->Message(Chat::White, "#npcedit gender - Sets an NPC's Gender"); - c->Message(Chat::White, "#npcedit texture - Sets an NPC's Texture"); - c->Message(Chat::White, "#npcedit helmtexture - Sets an NPC's Helmet Texture"); - c->Message(Chat::White, "#npcedit herosforgemodel - Sets an NPC's Hero's Forge Model"); - c->Message(Chat::White, "#npcedit size - Sets an NPC's Size"); - c->Message(Chat::White, "#npcedit hpregen - Sets an NPC's Hitpoints Regeneration Rate Per Tick"); - c->Message(Chat::White, "#npcedit manaregen - Sets an NPC's Mana Regeneration Rate Per Tick"); - c->Message(Chat::White, "#npcedit loottable - Sets an NPC's Loottable ID"); - c->Message(Chat::White, "#npcedit merchantid - Sets an NPC's Merchant ID"); - c->Message(Chat::White, "#npcedit alt_currency_id - Sets an NPC's Alternate Currency ID"); - c->Message(Chat::White, "#npcedit spell - Sets an NPC's Spells List ID"); - c->Message(Chat::White, "#npcedit npc_spells_effects_id - Sets an NPC's Spell Effects ID"); - c->Message(Chat::White, "#npcedit faction - Sets an NPC's Faction ID"); - c->Message(Chat::White, "#npcedit adventure_template_id - Sets an NPC's Adventure Template ID"); - c->Message(Chat::White, "#npcedit trap_template - Sets an NPC's Trap Template ID"); - c->Message(Chat::White, "#npcedit damage [minimum] [maximum] - Sets an NPC's Damage"); - c->Message(Chat::White, "#npcedit attackcount - Sets an NPC's Attack Count"); - c->Message(Chat::White, "#npcedit special_attacks - Sets an NPC's Special Attacks"); - c->Message(Chat::White, "#npcedit special_abilities - Sets an NPC's Special Abilities"); - c->Message(Chat::White, "#npcedit aggroradius - Sets an NPC's Aggro Radius"); - c->Message(Chat::White, "#npcedit assistradius - Sets an NPC's Assist Radius"); - c->Message(Chat::White, "#npcedit featuresave - Saves an NPC's current facial features to the database"); - c->Message(Chat::White, "#npcedit armortint_id - Sets an NPC's Armor Tint ID"); - c->Message(Chat::White, "#npcedit color [red] [green] [blue] - Sets an NPC's Red, Green, and Blue armor tint"); - c->Message(Chat::White, "#npcedit ammoidfile - Sets an NPC's Ammo ID File"); - c->Message(Chat::White, "#npcedit weapon [primary_model] [secondary_model] - Sets an NPC's Primary and Secondary Weapon Model"); - c->Message(Chat::White, "#npcedit meleetype [primary_type] [secondary_type] - Sets an NPC's Melee Types"); - c->Message(Chat::White, "#npcedit rangedtype - Sets an NPC's Ranged Type"); - c->Message(Chat::White, "#npcedit runspeed - Sets an NPC's Run Speed"); - c->Message(Chat::White, "#npcedit mr - Sets an NPC's Magic Resistance"); - c->Message(Chat::White, "#npcedit pr - Sets an NPC's Poison Resistance"); - c->Message(Chat::White, "#npcedit dr - Sets an NPC's Disease Resistance"); - c->Message(Chat::White, "#npcedit fr - Sets an NPC's Fire Resistance"); - c->Message(Chat::White, "#npcedit cr - Sets an NPC's Cold Resistance"); - c->Message(Chat::White, "#npcedit corrup - Sets an NPC's Corruption Resistance"); - c->Message(Chat::White, "#npcedit phr - Sets and NPC's Physical Resistance"); - c->Message(Chat::White, "#npcedit seeinvis - Sets an NPC's See Invisible Flag [0 = Cannot See Invisible, 1 = Can See Invisible]"); - c->Message(Chat::White, "#npcedit seeinvisundead - Sets an NPC's See Invisible vs. Undead Flag [0 = Cannot See Invisible vs. Undead, 1 = Can See Invisible vs. Undead]"); - c->Message(Chat::White, "#npcedit qglobal - Sets an NPC's Quest Global Flag [0 = Quest Globals Off, 1 = Quest Globals On]"); - c->Message(Chat::White, "#npcedit ac - Sets an NPC's Armor Class"); - c->Message(Chat::White, "#npcedit npcaggro - Sets an NPC's NPC Aggro Flag [0 = Aggro NPCs Off, 1 = Aggro NPCs On]"); - c->Message(Chat::White, "#npcedit spawn_limit - Sets an NPC's Spawn Limit Counter"); - c->Message(Chat::White, "#npcedit attackspeed - Sets an NPC's Attack Speed Modifier"); - c->Message(Chat::White, "#npcedit attackdelay - Sets an NPC's Attack Delay"); - c->Message(Chat::White, "#npcedit findable - Sets an NPC's Findable Flag [0 = Not Findable, 1 = Findable]"); - c->Message(Chat::White, "#npcedit str - Sets an NPC's Strength"); - c->Message(Chat::White, "#npcedit sta - Sets an NPC's Stamina"); - c->Message(Chat::White, "#npcedit dex - Sets an NPC's Dexterity"); - c->Message(Chat::White, "#npcedit agi - Sets an NPC's Agility"); - c->Message(Chat::White, "#npcedit int - Sets an NPC's Intelligence"); - c->Message(Chat::White, "#npcedit wis - Sets an NPC's Wisdom"); - c->Message(Chat::White, "#npcedit cha - Sets an NPC's Charisma"); - c->Message(Chat::White, "#npcedit seehide - Sets an NPC's See Hide Flag [0 = Cannot See Hide, 1 = Can See Hide]"); - c->Message(Chat::White, "#npcedit seeimprovedhide - Sets an NPC's See Improved Hide Flag [0 = Cannot See Improved Hide, 1 = Can See Improved Hide]"); - c->Message(Chat::White, "#npcedit trackable - Sets an NPC's Trackable Flag [0 = Not Trackable, 1 = Trackable]"); - c->Message(Chat::White, "#npcedit atk - Sets an NPC's Attack"); - c->Message(Chat::White, "#npcedit accuracy - Sets an NPC's Accuracy"); - c->Message(Chat::White, "#npcedit avoidance - Sets an NPC's Avoidance"); - c->Message(Chat::White, "#npcedit slow_mitigation - Sets an NPC's Slow Mitigation"); - c->Message(Chat::White, "#npcedit version - Sets an NPC's Version"); - c->Message(Chat::White, "#npcedit maxlevel - Sets an NPC's Maximum Level"); - c->Message(Chat::White, "#npcedit scalerate - Sets an NPC's Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]"); - c->Message(Chat::White, "#npcedit spellscale - Sets an NPC's Spell Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]"); - c->Message(Chat::White, "#npcedit healscale - Sets an NPC's Heal Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]"); - c->Message(Chat::White, "#npcedit no_target - Sets an NPC's No Target Hotkey Flag [0 = Not Targetable with Target Hotkey, 1 = Targetable with Target Hotkey]"); - c->Message(Chat::White, "#npcedit raidtarget - Sets an NPC's Raid Target Flag [0 = Not a Raid Target, 1 = Raid Target]"); - c->Message(Chat::White, "#npcedit armtexture - Sets an NPC's Arm Texture"); - c->Message(Chat::White, "#npcedit bracertexture - Sets an NPC's Bracer Texture"); - c->Message(Chat::White, "#npcedit handtexture - Sets an NPC's Hand Texture"); - c->Message(Chat::White, "#npcedit legtexture - Sets an NPC's Leg Texture"); - c->Message(Chat::White, "#npcedit feettexture - Sets an NPC's Feet Texture"); - c->Message(Chat::White, "#npcedit walkspeed - Sets an NPC's Walk Speed"); - c->Message(Chat::White, "#npcedit show_name - Sets an NPC's Show Name Flag [0 = Hidden, 1 = Shown]"); - c->Message(Chat::White, "#npcedit untargetable - Sets an NPC's Untargetable Flag [0 = Targetable, 1 = Untargetable]"); - c->Message(Chat::White, "#npcedit charm_ac - Sets an NPC's Armor Class while Charmed"); - c->Message(Chat::White, "#npcedit charm_min_dmg - Sets an NPC's Minimum Damage while Charmed"); - c->Message(Chat::White, "#npcedit charm_max_dmg - Sets an NPC's Max Damage while Charmed"); - c->Message(Chat::White, "#npcedit charm_attack_delay - Sets an NPC's Attack Delay while Charmed"); - c->Message(Chat::White, "#npcedit charm_accuracy_rating - Sets an NPC's Accuracy Rating while Charmed"); - c->Message(Chat::White, "#npcedit charm_avoidance_rating - Sets an NPC's Avoidance Rating while Charmed"); - c->Message(Chat::White, "#npcedit charm_atk - Sets an NPC's Attack while Charmed"); - c->Message(Chat::White, "#npcedit skip_global_loot - Sets an NPC's Skip Global Loot Flag [0 = Don't Skip, 1 = Skip"); - c->Message(Chat::White, "#npcedit rarespawn - Sets an NPC's Rare Spawn Flag [0 = Not a Rare Spawn, 1 = Rare Spawn]"); - c->Message(Chat::White, "#npcedit stuck_behavior - Sets an NPC's Stuck Behavior [0 = Run to Target, 1 = Warp to Target, 2 = Take No Action, 3 = Evade Combat]"); - c->Message(Chat::White, "#npcedit flymode - Sets an NPC's flymode [0 = Ground, 1 = Flying, 2 = Levitating, 3 = Water, 4 = Floating, 5 = Levitating While Running]"); - c->Message(Chat::White, "#npcedit always_aggro - Sets an NPC's Always Aggro Flag [0 = Does not Always Aggro, 1 = Always Aggro]"); - c->Message(Chat::White, "#npcedit exp_mod - Sets an NPC's Experience Modifier [50 = 50%, 100 = 100%, 200 = 200%]"); - c->Message(Chat::White, "#npcedit setanimation - Sets an NPC's Animation on Spawn (Stored in spawn2 table)"); - c->Message(Chat::White, "#npcedit respawntime - Sets an NPC's Respawn Timer in Seconds (Stored in spawn2 table)"); - } - - uint32 npc_id = c->GetTarget()->CastToNPC()->GetNPCTypeID(); - if (strcasecmp(sep->arg[1], "name") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has the name '{}'.", npc_id, sep->arg[2]).c_str()); - std::string query = fmt::format("UPDATE npc_types SET name = '{}' WHERE id = {}", sep->arg[2], npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "lastname") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has the lastname '{}'.", npc_id, sep->arg[2]).c_str()); - std::string query = fmt::format("UPDATE npc_types SET lastname = '{}' WHERE id = {}", sep->arg[2], npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "level") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now level {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET level = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "race") == 0) { - auto race_id = atoi(sep->arg[2]); - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now a {} ({}).", npc_id, GetRaceIDName(race_id), race_id).c_str()); - std::string query = fmt::format("UPDATE npc_types SET race = {} WHERE id = {}", race_id, npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "class") == 0) { - auto class_id = atoi(sep->arg[2]); - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now a {} ({}).", npc_id, GetClassIDName(class_id), class_id).c_str()); - std::string query = fmt::format("UPDATE npc_types SET class = {} WHERE id = {}", class_id, npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "bodytype") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Bodytype {} .", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET bodytype = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "hp") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Health.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET hp = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "mana") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Mana.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET mana = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "gender") == 0) { - auto gender_id = atoi(sep->arg[2]); - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now a {} ({}).", npc_id, gender_id, GetGenderName(gender_id)).c_str()); - std::string query = fmt::format("UPDATE npc_types SET gender = {} WHERE id = {}", gender_id, npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "texture") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET texture = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "helmtexture") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Helmet Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET helmtexture = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "herosforgemodel") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Hero's Forge Model {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET herosforgemodel = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "size") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now Size {:.2f}.", npc_id, atof(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET size = {:.2f} WHERE id = {}", atof(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "hpregen") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now regenerates {} Health per Tick.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET hp_regen_rate = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "manaregen") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now regenerates {} Mana per Tick.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET mana_regen_rate = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "loottable") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using loottable ID {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET loottable_id = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "merchantid") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using merchant ID {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET merchant_id = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "alt_currency_id") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Alternate Currency ID {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET alt_currency_id = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "spell") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Spell List ID {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET npc_spells_id = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "npc_spells_effects_id") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using NPC Spells Effects ID {}.", npc_id, sep->arg[2]).c_str()); - std::string query = fmt::format("UPDATE npc_types SET npc_spells_effects_id = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "faction") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Faction ID {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET npc_faction_id = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "adventure_template_id") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Adventure Template ID {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET adventure_template_id = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "trap_template") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Trap Template ID {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET trap_template = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "damage") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now hits from {} to {} damage.", npc_id, atoi(sep->arg[2]), atoi(sep->arg[3])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET mindmg = {}, maxdmg = {} WHERE id = {}", atoi(sep->arg[2]), atoi(sep->arg[3]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "attackcount") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has an Attack Count of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET attack_count = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "special_attacks") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using the following Special Attacks '{}'.", npc_id, sep->arg[2]).c_str()); - std::string query = fmt::format("UPDATE npc_types SET npcspecialattks = '{}' WHERE id = {}", sep->arg[2], npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "special_abilities") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using the following Special Abilities '{}'.", npc_id, sep->arg[2]).c_str()); - std::string query = fmt::format("UPDATE npc_types SET special_abilities = '{}' WHERE id = {}", sep->arg[2], npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "aggroradius") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has an Aggro Radius of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET aggroradius = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "assistradius") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has an Assist Radius of {}", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET assistradius = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "featuresave") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} saved with all current facial feature settings.", npc_id).c_str()); - Mob* target = c->GetTarget(); - std::string query = fmt::format( - "UPDATE npc_types " - "SET luclin_haircolor = {}, luclin_beardcolor = {}, " - "luclin_hairstyle = {}, luclin_beard = {}, " - "face = {}, drakkin_heritage = {}, " - "drakkin_tattoo = {}, drakkin_details = {} " - "WHERE id = {}", - target->GetHairColor(), target->GetBeardColor(), - target->GetHairStyle(), target->GetBeard(), - target->GetLuclinFace(), target->GetDrakkinHeritage(), - target->GetDrakkinTattoo(), target->GetDrakkinDetails(), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "armortint_id") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Armor Tint ID {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET armortint_id = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "color") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Red, {} Green, and {} Blue tinting on their armor.", npc_id, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET armortint_red = {}, armortint_green = {}, armortint_blue = {} WHERE id = {}", atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "ammoidfile") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Ammo ID File {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET ammo_idfile = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "weapon") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} will have Model {} set to their Primary and Model {} set to their Secondary on repop.", npc_id, atoi(sep->arg[2]), atoi(sep->arg[3])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET d_melee_texture1 = {}, d_melee_texture2 = {} WHERE id = {}", atoi(sep->arg[2]), atoi(sep->arg[3]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "meleetype") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Primary Melee Type of {} and a Secondary Melee Type of {}.", npc_id, atoi(sep->arg[2]), atoi(sep->arg[3])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET prim_melee_type = {}, sec_melee_type = {} WHERE id = {}", atoi(sep->arg[2]), atoi(sep->arg[3]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "rangedtype") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Ranged Type of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET ranged_type = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "runspeed") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now runs at {:.2f}.", npc_id, atof(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET runspeed = {:.2f} WHERE id = {}", atof(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "mr") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Magic Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET MR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "pr") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Poison Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET PR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "dr") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Disease Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET DR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "fr") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Fire Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET FR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "cr") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Cold Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET CR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "corrup") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Corruption Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET corrup = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "phr") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Physical Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET PhR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "seeinvis") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} can {} See Invisible.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET see_invis = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "seeinvisundead") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} can {} See Invisible vs. Undead.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET see_invis_undead = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "qglobal") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} can {} use Quest Globals.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET qglobal = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "ac") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Armor Class.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET ac = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "npcaggro") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} will {} aggro other NPCs that have a hostile faction.", npc_id, (atoi(sep->arg[2]) == 1 ? "now": "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET npc_aggro = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "spawn_limit") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Spawn Limit of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET spawn_limit = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "attackspeed") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has an Attack Speed of {:.2f}.", npc_id, atof(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET attack_speed = {:.2f} WHERE id = {}", atof(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "attackdelay") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has an Attack Delay of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET attack_delay = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "findable") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is {} Findable.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET findable = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "str") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Strength.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET STR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "sta") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Stamina.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET STA = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "agi") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Agility.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET AGI = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "dex") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Dexterity.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET DEX = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "int") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Intelligence.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET _INT = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "wis") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Magic Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET WIS = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "cha") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Charisma.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET CHA = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "seehide") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} can {} See Hide.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET see_hide = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "seeimprovedhide") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} can {} See Improved Hide.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET see_improved_hide = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "trackable") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is {} Trackable.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET trackable = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "atk") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Attack.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET atk = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "accuracy") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Accuracy.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET accuracy = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "avoidance") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Avoidance.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET avoidance = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "slow_mitigation") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Slow Mitigation.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET slow_mitigation = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "version") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Version {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET version = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "maxlevel") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Maximum Level of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET maxlevel = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "scalerate") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Scaling Rate of {}%%.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET scalerate = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "spellscale") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Spell Scaling Rate of {}%%.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET spellscale = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "healscale") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Heal Scaling Rate of {}%%.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET healscale = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "no_target") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is {} Targetable with Target Hotkey.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET no_target_hotkey = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "raidtarget") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is {} designated as a Raid Target.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET raid_target = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "armtexture") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Arm Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET armtexture = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "bracertexture") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Bracer Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET bracertexture = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "handtexture") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Hand Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET handtexture = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "legtexture") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Leg Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET legtexture = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "feettexture") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Feet Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET feettexture = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "walkspeed") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now walks at {:.2f}.", npc_id, atof(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET walkspeed = {:.2f} WHERE id = {}", atof(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "show_name") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} will {} show their name.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET show_name = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "untargetable") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} will {} be untargetable.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET untargetable = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "charm_ac") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Armor Class while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET charm_ac = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "charm_min_dmg") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now does {} Minimum Damage while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET charm_min_dmg = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "charm_max_dmg") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now does {} Maximum Damage while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET charm_max_dmg = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "charm_attack_delay") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Attack Delay while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET charm_attack_delay = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "charm_accuracy_rating") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Accuracy Rating while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET charm_accuracy_rating = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "charm_avoidance_rating") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Avoidance Rating while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET charm_avoidance_rating = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "charm_atk") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Attack while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET charm_atk = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "skip_global_loot") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} will {} skip Global Loot.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET skip_global_loot = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "rarespawn") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is {} designated as a Rare Spawn.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET rare_spawn = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "stuck_behavior") == 0) { - auto behavior_id = atoi(sep->arg[2]); - std::string behavior_name = "Unknown"; - if (behavior_id == MobStuckBehavior::RunToTarget) { - behavior_name = "Run To Target"; - } else if (behavior_id == MobStuckBehavior::WarpToTarget) { - behavior_name = "Warp To Target"; - } else if (behavior_id == MobStuckBehavior::TakeNoAction) { - behavior_name = "Take No Action"; - } else if (behavior_id == MobStuckBehavior::EvadeCombat) { - behavior_name = "Evade Combat"; - } - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Stuck Behavior {} ({}).", npc_id, behavior_name, behavior_id).c_str()); - std::string query = fmt::format("UPDATE npc_types SET stuck_behavior = {} WHERE id = {}", behavior_id, npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "flymode") == 0) { - auto flymode_id = atoi(sep->arg[2]); - std::string flymode_name = "Unknown"; - if (flymode_id == GravityBehavior::Ground) { - flymode_name = "Ground"; - } else if (flymode_id == GravityBehavior::Flying) { - flymode_name = "Flying"; - } else if (flymode_id == GravityBehavior::Levitating) { - flymode_name = "Levitating"; - } else if (flymode_id == GravityBehavior::Water) { - flymode_name = "Water"; - } else if (flymode_id == GravityBehavior::Floating) { - flymode_name = "Floating"; - } else if (flymode_id == GravityBehavior::LevitateWhileRunning) { - flymode_name = "Levitating While Running"; - } - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Fly Mode {} ({}).", npc_id, flymode_name, flymode_id).c_str()); - std::string query = fmt::format("UPDATE npc_types SET flymode = {} WHERE id = {}", flymode_id, npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "always_aggro") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} will {} Always Aggro.", npc_id, (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET always_aggro = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "exp_mod") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has an Experience Modifier of {}%%.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET exp_mod = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "setanimation") == 0) { - int animation = 0; - std::string animation_name = "Unknown"; - if(sep->arg[2] && atoi(sep->arg[2]) <= 4) { - if(strcasecmp(sep->arg[2], "stand") == 0 || atoi(sep->arg[2]) == 0) { // Stand - animation = 0; - animation_name = "Standing"; - } else if(strcasecmp(sep->arg[2], "sit") == 0 || atoi(sep->arg[2]) == 1) { // Sit - animation = 1; - animation_name = "Sitting"; - } else if(strcasecmp(sep->arg[2], "crouch") == 0 || atoi(sep->arg[2]) == 2) { // Crouch - animation = 2; - animation_name = "Crouching"; - } else if(strcasecmp(sep->arg[2], "dead") == 0 || atoi(sep->arg[2]) == 3) { // Dead - animation = 3; - animation_name = "Dead"; - } else if(strcasecmp(sep->arg[2], "loot") == 0 || atoi(sep->arg[2]) == 4) { // Looting Animation - animation = 4; - animation_name = "Looting"; - } - } else { - c->Message(Chat::White, "You must specify an Animation (0 = Stand, 1 = Sit, 2 = Crouch, 3 = Dead, 4 = Loot)"); - c->Message(Chat::White, "Example: #npcedit setanimation sit"); - c->Message(Chat::White, "Example: #npcedit setanimation 0"); - return; - } - - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} now has their Spawn Animation set to {} ({}) on Spawn Group ID {}.", - npc_id, - animation_name, - animation, - c->GetTarget()->CastToNPC()->GetSpawnGroupId() - ).c_str() - ); - std::string query = fmt::format( - "UPDATE spawn2 SET animation = {} WHERE spawngroupID = {}", - animation, - c->GetTarget()->CastToNPC()->GetSpawnGroupId() - ); - content_db.QueryDatabase(query); - - c->GetTarget()->SetAppearance(EmuAppearance(animation)); - return; - } - - if (strcasecmp(sep->arg[1], "respawntime") == 0) { - if (sep->arg[2][0] && sep->IsNumber(sep->arg[2]) && atoi(sep->arg[2]) > 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has a Respawn Timer of {} Seconds on Spawn Group ID {}.", npc_id, atoi(sep->arg[2]), c->GetTarget()->CastToNPC()->GetSpawnGroupId()).c_str()); - std::string query = fmt::format("UPDATE spawn2 SET respawntime = {} WHERE spawngroupID = {} AND version = {}", atoi(sep->arg[2]), c->GetTarget()->CastToNPC()->GetSpawnGroupId(), zone->GetInstanceVersion()); - content_db.QueryDatabase(query); - return; - } - } - - if((sep->arg[1][0] == 0 || strcasecmp(sep->arg[1],"*")==0) || ((c->GetTarget()==0) || (c->GetTarget()->IsClient()))) - c->Message(Chat::White, "Type #npcedit help for more info"); - -} - -#ifdef PACKET_PROFILER -void command_packetprofile(Client *c, const Seperator *sep) { - Client *t = c; - if(c->GetTarget() && c->GetTarget()->IsClient()) { - t = c->GetTarget()->CastToClient(); - } - c->DumpPacketProfile(); -} -#endif - -#ifdef EQPROFILE -void command_profiledump(Client *c, const Seperator *sep) { - DumpZoneProfile(); -} - -void command_profilereset(Client *c, const Seperator *sep) { - ResetZoneProfile(); -} -#endif - -void command_opcode(Client *c, const Seperator *sep) { - if(!strcasecmp(sep->arg[1], "reload" )) { - ReloadAllPatches(); - c->Message(Chat::White, "Opcodes for all patches have been reloaded"); - } -} - -void command_qglobal(Client *c, const Seperator *sep) { - //In-game switch for qglobal column - if(sep->arg[1][0] == 0) { - c->Message(Chat::White, "Syntax: #qglobal [on/off/view]. Requires NPC target."); - return; - } - - Mob *target = c->GetTarget(); - - if(!target || !target->IsNPC()) { - c->Message(Chat::Red, "NPC Target Required!"); - return; - } - - if(!strcasecmp(sep->arg[1], "on")) { - std::string query = StringFormat("UPDATE npc_types SET qglobal = 1 WHERE id = '%i'", - target->GetNPCTypeID()); - auto results = content_db.QueryDatabase(query); - if(!results.Success()) { - c->Message(Chat::Yellow, "Could not update database."); - return; - } - - c->Message(Chat::Yellow, "Success! Changes take effect on zone reboot."); - return; - } - - if(!strcasecmp(sep->arg[1], "off")) { - std::string query = StringFormat("UPDATE npc_types SET qglobal = 0 WHERE id = '%i'", - target->GetNPCTypeID()); - auto results = content_db.QueryDatabase(query); - if(!results.Success()) { - c->Message(Chat::Yellow, "Could not update database."); - return; - } - - c->Message(Chat::Yellow, "Success! Changes take effect on zone reboot."); - return; - } - - if(!strcasecmp(sep->arg[1], "view")) { - const NPCType *type = content_db.LoadNPCTypesData(target->GetNPCTypeID()); - if(!type) - c->Message(Chat::Yellow, "Invalid NPC type."); - else if(type->qglobal) - c->Message(Chat::Yellow, "This NPC has quest globals active."); - else - c->Message(Chat::Yellow, "This NPC has quest globals disabled."); - return; - } - - c->Message(Chat::Yellow, "Invalid action specified."); -} - -void command_path(Client *c, const Seperator *sep) -{ - if (zone->pathing) { - zone->pathing->DebugCommand(c, sep); - } -} - -void Client::Undye() { - for (int cur_slot = EQ::textures::textureBegin; cur_slot <= EQ::textures::LastTexture; cur_slot++) { - uint8 slot2=SlotConvert(cur_slot); - EQ::ItemInstance* inst = m_inv.GetItem(slot2); - - if(inst != nullptr) { - inst->SetColor(inst->GetItem()->Color); - database.SaveInventory(CharacterID(), inst, slot2); - } - - m_pp.item_tint.Slot[cur_slot].Color = 0; - SendWearChange(cur_slot); - } - - database.DeleteCharacterDye(this->CharacterID()); -} - -void command_undye(Client *c, const Seperator *sep) -{ - if(c->GetTarget() && c->GetTarget()->IsClient()) - { - c->GetTarget()->CastToClient()->Undye(); - } - else - { - c->Message(Chat::White, "ERROR: Client target required"); - } -} - -void command_ucs(Client *c, const Seperator *sep) -{ - if (!c) - return; - - LogInfo("Character [{}] attempting ucs reconnect while ucs server is [{}] available", - c->GetName(), (zone->IsUCSServerAvailable() ? "" : "un")); - - if (zone->IsUCSServerAvailable()) { - EQApplicationPacket* outapp = nullptr; - std::string buffer; - - std::string MailKey = database.GetMailKey(c->CharacterID(), true); - EQ::versions::UCSVersion ConnectionType = EQ::versions::ucsUnknown; - - // chat server packet - switch (c->ClientVersion()) { - case EQ::versions::ClientVersion::Titanium: - ConnectionType = EQ::versions::ucsTitaniumChat; - break; - case EQ::versions::ClientVersion::SoF: - ConnectionType = EQ::versions::ucsSoFCombined; - break; - case EQ::versions::ClientVersion::SoD: - ConnectionType = EQ::versions::ucsSoDCombined; - break; - case EQ::versions::ClientVersion::UF: - ConnectionType = EQ::versions::ucsUFCombined; - break; - case EQ::versions::ClientVersion::RoF: - ConnectionType = EQ::versions::ucsRoFCombined; - break; - case EQ::versions::ClientVersion::RoF2: - ConnectionType = EQ::versions::ucsRoF2Combined; - break; - default: - ConnectionType = EQ::versions::ucsUnknown; - break; - } - - buffer = StringFormat("%s,%i,%s.%s,%c%s", - Config->ChatHost.c_str(), - Config->ChatPort, - Config->ShortName.c_str(), - c->GetName(), - ConnectionType, - MailKey.c_str() - ); - - outapp = new EQApplicationPacket(OP_SetChatServer, (buffer.length() + 1)); - memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); - outapp->pBuffer[buffer.length()] = '\0'; - - c->QueuePacket(outapp); - safe_delete(outapp); - - // mail server packet - switch (c->ClientVersion()) { - case EQ::versions::ClientVersion::Titanium: - ConnectionType = EQ::versions::ucsTitaniumMail; - break; - default: - // retain value from previous switch - break; - } - - buffer = StringFormat("%s,%i,%s.%s,%c%s", - Config->MailHost.c_str(), - Config->MailPort, - Config->ShortName.c_str(), - c->GetName(), - ConnectionType, - MailKey.c_str() - ); - - outapp = new EQApplicationPacket(OP_SetChatServer2, (buffer.length() + 1)); - memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); - outapp->pBuffer[buffer.length()] = '\0'; - - c->QueuePacket(outapp); - safe_delete(outapp); - } -} - -void command_undyeme(Client *c, const Seperator *sep) -{ - if(c) { - c->Undye(); - c->Message(Chat::Red, "Dye removed from all slots. Please zone for the process to complete."); - } -} - -void command_ginfo(Client *c, const Seperator *sep) -{ - Client *t; - - if(c->GetTarget() && c->GetTarget()->IsClient()) - t = c->GetTarget()->CastToClient(); - else - t = c; - - Group *g = t->GetGroup(); - if(!g) { - c->Message(Chat::White, "This client is not in a group"); - return; - } - - c->Message(Chat::White, "Player: %s is in Group #%lu: with %i members", t->GetName(), (unsigned long)g->GetID(), g->GroupCount()); - - uint32 r; - for(r = 0; r < MAX_GROUP_MEMBERS; r++) { - if(g->members[r] == nullptr) { - if(g->membername[r][0] == '\0') - continue; - c->Message(Chat::White, "...Zoned Member: %s, Roles: %s %s %s", g->membername[r], - (g->MemberRoles[r] & RoleAssist) ? "Assist" : "", - (g->MemberRoles[r] & RoleTank) ? "Tank" : "", - (g->MemberRoles[r] & RolePuller) ? "Puller" : ""); - } else { - c->Message(Chat::White, "...In-Zone Member: %s (0x%x) Roles: %s %s %s", g->membername[r], g->members[r], - (g->MemberRoles[r] & RoleAssist) ? "Assist" : "", - (g->MemberRoles[r] & RoleTank) ? "Tank" : "", - (g->MemberRoles[r] & RolePuller) ? "Puller" : ""); - - } - } -} - -void command_hp(Client *c, const Seperator *sep) -{ - c->SendHPUpdate(); - c->CheckManaEndUpdate(); -} - -void command_aggro(Client *c, const Seperator *sep) -{ - if(c->GetTarget() == nullptr || !c->GetTarget()->IsNPC()) { - c->Message(Chat::White, "Error: you must have an NPC target."); - return; - } - float d = atof(sep->arg[1]); - if(d == 0.0f) { - c->Message(Chat::Red, "Error: distance argument required."); - return; - } - bool verbose = false; - if(sep->arg[2][0] == '-' && sep->arg[2][1] == 'v' && sep->arg[2][2] == '\0') { - verbose = true; - } - - entity_list.DescribeAggro(c, c->GetTarget()->CastToNPC(), d, verbose); -} - -void command_pf(Client *c, const Seperator *sep) -{ - if(c->GetTarget()) - { - Mob *who = c->GetTarget(); - c->Message(Chat::White, "POS: (%.2f, %.2f, %.2f)", who->GetX(), who->GetY(), who->GetZ()); - c->Message(Chat::White, "WP: %s (%d/%d)", to_string(who->GetCurrentWayPoint()).c_str(), who->IsNPC()?who->CastToNPC()->GetMaxWp():-1); - c->Message(Chat::White, "pause=%d RAspeed=%d", who->GetCWPP(), who->GetRunAnimSpeed()); - //who->DumpMovement(c); - } else { - c->Message(Chat::White, "ERROR: target required"); - } -} - -void command_bestz(Client *c, const Seperator *sep) { - if (zone->zonemap == nullptr) { - c->Message(Chat::White,"Map not loaded for this zone"); - } else { - glm::vec3 me; - me.x = c->GetX(); - me.y = c->GetY(); - me.z = c->GetZ() + (c->GetSize() == 0.0 ? 6 : c->GetSize()) * HEAD_POSITION; - glm::vec3 hit; - glm::vec3 bme(me); - bme.z -= 500; - - float best_z = zone->zonemap->FindBestZ(me, &hit); - - if (best_z != BEST_Z_INVALID) - { - c->Message(Chat::White, "Z is %.3f at (%.3f, %.3f).", best_z, me.x, me.y); - } - else - { - c->Message(Chat::White, "Found no Z."); - } - } - - if(zone->watermap == nullptr) { - c->Message(Chat::White,"Water Region Map not loaded for this zone"); - } else { - WaterRegionType RegionType; - float z; - - if(c->GetTarget()) { - z=c->GetTarget()->GetZ(); - auto position = glm::vec3(c->GetTarget()->GetX(), c->GetTarget()->GetY(), z); - RegionType = zone->watermap->ReturnRegionType(position); - c->Message(Chat::White,"InWater returns %d", zone->watermap->InWater(position)); - c->Message(Chat::White,"InLava returns %d", zone->watermap->InLava(position)); - - } - else { - z=c->GetZ(); - auto position = glm::vec3(c->GetX(), c->GetY(), z); - RegionType = zone->watermap->ReturnRegionType(position); - c->Message(Chat::White,"InWater returns %d", zone->watermap->InWater(position)); - c->Message(Chat::White,"InLava returns %d", zone->watermap->InLava(position)); - - } - - switch(RegionType) { - case RegionTypeNormal: { c->Message(Chat::White,"There is nothing special about the region you are in!"); break; } - case RegionTypeWater: { c->Message(Chat::White,"You/your target are in Water."); break; } - case RegionTypeLava: { c->Message(Chat::White,"You/your target are in Lava."); break; } - case RegionTypeVWater: { c->Message(Chat::White,"You/your target are in VWater (Icy Water?)."); break; } - case RegionTypePVP: { c->Message(Chat::White, "You/your target are in a pvp enabled area."); break; } - case RegionTypeSlime: { c->Message(Chat::White, "You/your target are in slime."); break; } - case RegionTypeIce: { c->Message(Chat::White, "You/your target are in ice."); break; } - default: c->Message(Chat::White,"You/your target are in an unknown region type."); - } - } - - -} - - -void command_reloadstatic(Client *c, const Seperator *sep) { - c->Message(Chat::White, "Reloading zone static data..."); - zone->ReloadStaticData(); -} - -void command_flags(Client *c, const Seperator *sep) { - Client *t = c; - - if(c->Admin() >= minStatusToSeeOthersZoneFlags) { - Mob *tgt = c->GetTarget(); - if(tgt != nullptr && tgt->IsClient()) - t = tgt->CastToClient(); - } - - t->SendZoneFlagInfo(c); -} - -void command_flagedit(Client *c, const Seperator *sep) { - //super-command for editing zone flags - if(sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { - c->Message(Chat::White, "Syntax: #flagedit [lockzone|unlockzone|listzones|give|take]."); - c->Message(Chat::White, "...lockzone [zone id/short] [flag name] - Set the specified flag name on the zone, locking the zone"); - c->Message(Chat::White, "...unlockzone [zone id/short] - Removes the flag requirement from the specified zone"); - c->Message(Chat::White, "...listzones - List all zones which require a flag, and their flag's name"); - c->Message(Chat::White, "...give [zone id/short] - Give your target the zone flag for the specified zone."); - c->Message(Chat::White, "...take [zone id/short] - Take the zone flag for the specified zone away from your target"); - c->Message(Chat::White, "...Note: use #flags to view flags on a person"); - return; - } - - if(!strcasecmp(sep->arg[1], "lockzone")) { - uint32 zoneid = 0; - if(sep->arg[2][0] != '\0') { - zoneid = atoi(sep->arg[2]); - if(zoneid < 1) { - zoneid = ZoneID(sep->arg[2]); - } - } - if(zoneid < 1) { - c->Message(Chat::Red, "zone required. see help."); - return; - } - - char flag_name[128]; - if(sep->argplus[3][0] == '\0') { - c->Message(Chat::Red, "flag name required. see help."); - return; - } - database.DoEscapeString(flag_name, sep->argplus[3], 64); - flag_name[127] = '\0'; - - std::string query = StringFormat("UPDATE zone SET flag_needed = '%s' " - "WHERE zoneidnumber = %d AND version = %d", - flag_name, zoneid, zone->GetInstanceVersion()); - auto results = content_db.QueryDatabase(query); - if(!results.Success()) { - c->Message(Chat::Red, "Error updating zone: %s", results.ErrorMessage().c_str()); - return; - } - - c->Message(Chat::Yellow, "Success! Zone %s now requires a flag, named %s", ZoneName(zoneid), flag_name); - return; - } - - if(!strcasecmp(sep->arg[1], "unlockzone")) { - uint32 zoneid = 0; - if(sep->arg[2][0] != '\0') { - zoneid = atoi(sep->arg[2]); - if(zoneid < 1) { - zoneid = ZoneID(sep->arg[2]); - } - } - - if(zoneid < 1) { - c->Message(Chat::Red, "zone required. see help."); - return; - } - - std::string query = StringFormat("UPDATE zone SET flag_needed = '' " - "WHERE zoneidnumber = %d AND version = %d", - zoneid, zone->GetInstanceVersion()); - auto results = content_db.QueryDatabase(query); - if(!results.Success()) { - c->Message(Chat::Yellow, "Error updating zone: %s", results.ErrorMessage().c_str()); - return; - } - - c->Message(Chat::Yellow, "Success! Zone %s no longer requires a flag.", ZoneName(zoneid)); - return; - } - - if(!strcasecmp(sep->arg[1], "listzones")) { - std::string query = "SELECT zoneidnumber, short_name, long_name, version, flag_needed " - "FROM zone WHERE flag_needed != ''"; - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - return; - } - - c->Message(Chat::White, "Zones which require flags:"); - for (auto row = results.begin(); row != results.end(); ++row) - c->Message(Chat::White, "Zone %s (%s,%s) version %s requires key %s", row[2], row[0], row[1], row[3], row[4]); - - return; - } - - if(!strcasecmp(sep->arg[1], "give")) { - uint32 zoneid = 0; - if(sep->arg[2][0] != '\0') { - zoneid = atoi(sep->arg[2]); - if(zoneid < 1) { - zoneid = ZoneID(sep->arg[2]); - } - } - if(zoneid < 1) { - c->Message(Chat::Red, "zone required. see help."); - return; - } - - Mob *t = c->GetTarget(); - if(t == nullptr || !t->IsClient()) { - c->Message(Chat::Red, "client target required"); - return; - } - - t->CastToClient()->SetZoneFlag(zoneid); - return; - } - - if(!strcasecmp(sep->arg[1], "give")) { - uint32 zoneid = 0; - if(sep->arg[2][0] != '\0') { - zoneid = atoi(sep->arg[2]); - if(zoneid < 1) { - zoneid = ZoneID(sep->arg[2]); - } - } - if(zoneid < 1) { - c->Message(Chat::Red, "zone required. see help."); - return; - } - - Mob *t = c->GetTarget(); - if(t == nullptr || !t->IsClient()) { - c->Message(Chat::Red, "client target required"); - return; - } - - t->CastToClient()->ClearZoneFlag(zoneid); - return; - } - - c->Message(Chat::Yellow, "Invalid action specified. use '#flagedit help' for help"); -} - -void command_serverrules(Client *c, const Seperator *sep) -{ - c->SendRules(c); -} - -void command_acceptrules(Client *c, const Seperator *sep) -{ - if(!database.GetAgreementFlag(c->AccountID())) - { - database.SetAgreementFlag(c->AccountID()); - c->SendAppearancePacket(AT_Anim, ANIM_STAND); - c->Message(Chat::White,"It is recorded you have agreed to the rules."); - } -} - -void command_guildcreate(Client *c, const Seperator *sep) -{ - if(strlen(sep->argplus[1])>4 && strlen(sep->argplus[1])<16) - { - guild_mgr.AddGuildApproval(sep->argplus[1],c); - } - else - { - c->Message(Chat::White,"Guild name must be more than 4 characters and less than 16."); - } -} - -void command_guildapprove(Client *c, const Seperator *sep) -{ - guild_mgr.AddMemberApproval(atoi(sep->arg[1]),c); -} - -void command_guildlist(Client *c, const Seperator *sep) -{ - GuildApproval* tmp = guild_mgr.FindGuildByIDApproval(atoi(sep->arg[1])); - if(tmp) - { - tmp->ApprovedMembers(c); - } - else - c->Message(Chat::White,"Could not find reference id."); -} - -void command_hatelist(Client *c, const Seperator *sep) { - Mob *target = c->GetTarget(); - if(target == nullptr) { - c->Message(Chat::White, "Error: you must have a target."); - return; - } - - c->Message(Chat::White, "Display hate list for %s..", target->GetName()); - target->PrintHateListToClient(c); -} - - -void command_rules(Client *c, const Seperator *sep) { - //super-command for managing rules settings - if(sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { - c->Message(Chat::White, "Syntax: #rules [subcommand]."); - c->Message(Chat::White, "-- Rule Set Manipulation --"); - c->Message(Chat::White, "...listsets - List avaliable rule sets"); - c->Message(Chat::White, "...current - gives the name of the ruleset currently running in this zone"); - c->Message(Chat::White, "...reload - Reload the selected ruleset in this zone"); - c->Message(Chat::White, "...switch (ruleset name) - Change the selected ruleset and load it"); - c->Message(Chat::White, "...load (ruleset name) - Load a ruleset in just this zone without changing the selected set"); -//too lazy to write this right now: -// c->Message(Chat::White, "...wload (ruleset name) - Load a ruleset in all zones without changing the selected set"); - c->Message(Chat::White, "...store [ruleset name] - Store the running ruleset as the specified name"); - c->Message(Chat::White, "---------------------"); - c->Message(Chat::White, "-- Running Rule Manipulation --"); - c->Message(Chat::White, "...reset - Reset all rules to their default values"); - c->Message(Chat::White, "...get [rule] - Get the specified rule's local value"); - c->Message(Chat::White, "...set (rule) (value) - Set the specified rule to the specified value locally only"); - c->Message(Chat::White, "...setdb (rule) (value) - Set the specified rule to the specified value locally and in the DB"); - c->Message(Chat::White, "...list [catname] - List all rules in the specified category (or all categiries if omitted)"); - c->Message(Chat::White, "...values [catname] - List the value of all rules in the specified category"); - return; - } - - if(!strcasecmp(sep->arg[1], "current")) { - c->Message(Chat::White, "Currently running ruleset '%s' (%d)", RuleManager::Instance()->GetActiveRuleset(), - RuleManager::Instance()->GetActiveRulesetID()); - } else if(!strcasecmp(sep->arg[1], "listsets")) { - std::map sets; - if(!RuleManager::Instance()->ListRulesets(&database, sets)) { - c->Message(Chat::Red, "Failed to list rule sets!"); - return; - } - - c->Message(Chat::White, "Avaliable rule sets:"); - std::map::iterator cur, end; - cur = sets.begin(); - end = sets.end(); - for(; cur != end; ++cur) { - c->Message(Chat::White, "(%d) %s", cur->first, cur->second.c_str()); - } - } else if(!strcasecmp(sep->arg[1], "reload")) { - RuleManager::Instance()->LoadRules(&database, RuleManager::Instance()->GetActiveRuleset(), true); - c->Message(Chat::White, "The active ruleset (%s (%d)) has been reloaded", RuleManager::Instance()->GetActiveRuleset(), - RuleManager::Instance()->GetActiveRulesetID()); - } else if(!strcasecmp(sep->arg[1], "switch")) { - //make sure this is a valid rule set.. - int rsid = RuleManager::Instance()->GetRulesetID(&database, sep->arg[2]); - if(rsid < 0) { - c->Message(Chat::Red, "Unknown rule set '%s'", sep->arg[2]); - return; - } - if(!database.SetVariable("RuleSet", sep->arg[2])) { - c->Message(Chat::Red, "Failed to update variables table to change selected rule set"); - return; - } - - //TODO: we likely want to reload this ruleset everywhere... - RuleManager::Instance()->LoadRules(&database, sep->arg[2], true); - - c->Message(Chat::White, "The selected ruleset has been changed to (%s (%d)) and reloaded locally", sep->arg[2], rsid); - } else if(!strcasecmp(sep->arg[1], "load")) { - //make sure this is a valid rule set.. - int rsid = RuleManager::Instance()->GetRulesetID(&database, sep->arg[2]); - if(rsid < 0) { - c->Message(Chat::Red, "Unknown rule set '%s'", sep->arg[2]); - return; - } - RuleManager::Instance()->LoadRules(&database, sep->arg[2], true); - c->Message(Chat::White, "Loaded ruleset '%s' (%d) locally", sep->arg[2], rsid); - } else if(!strcasecmp(sep->arg[1], "store")) { - if(sep->argnum == 1) { - //store current rule set. - RuleManager::Instance()->SaveRules(&database); - c->Message(Chat::White, "Rules saved"); - } else if(sep->argnum == 2) { - RuleManager::Instance()->SaveRules(&database, sep->arg[2]); - int prersid = RuleManager::Instance()->GetActiveRulesetID(); - int rsid = RuleManager::Instance()->GetRulesetID(&database, sep->arg[2]); - if(rsid < 0) { - c->Message(Chat::Red, "Unable to query ruleset ID after store, it most likely failed."); - } else { - c->Message(Chat::White, "Stored rules as ruleset '%s' (%d)", sep->arg[2], rsid); - if(prersid != rsid) { - c->Message(Chat::White, "Rule set %s (%d) is now active in this zone", sep->arg[2], rsid); - } - } - } else { - c->Message(Chat::Red, "Invalid argument count, see help."); - return; - } - } else if(!strcasecmp(sep->arg[1], "reset")) { - RuleManager::Instance()->ResetRules(true); - c->Message(Chat::White, "The running ruleset has been set to defaults"); - - } else if(!strcasecmp(sep->arg[1], "get")) { - if(sep->argnum != 2) { - c->Message(Chat::Red, "Invalid argument count, see help."); - return; - } - std::string value; - if(!RuleManager::Instance()->GetRule(sep->arg[2], value)) - c->Message(Chat::Red, "Unable to find rule %s", sep->arg[2]); - else - c->Message(Chat::White, "%s - %s", sep->arg[2], value.c_str()); - - } else if(!strcasecmp(sep->arg[1], "set")) { - if(sep->argnum != 3) { - c->Message(Chat::Red, "Invalid argument count, see help."); - return; - } - if(!RuleManager::Instance()->SetRule(sep->arg[2], sep->arg[3], nullptr, false, true)) { - c->Message(Chat::Red, "Failed to modify rule"); - } else { - c->Message(Chat::White, "Rule modified locally."); - } - } else if(!strcasecmp(sep->arg[1], "setdb")) { - if(sep->argnum != 3) { - c->Message(Chat::Red, "Invalid argument count, see help."); - return; - } - if(!RuleManager::Instance()->SetRule(sep->arg[2], sep->arg[3], &database, true, true)) { - c->Message(Chat::Red, "Failed to modify rule"); - } else { - c->Message(Chat::White, "Rule modified locally and in the database."); - } - } else if(!strcasecmp(sep->arg[1], "list")) { - if(sep->argnum == 1) { - std::vector rule_list; - if(!RuleManager::Instance()->ListCategories(rule_list)) { - c->Message(Chat::Red, "Failed to list categories!"); - return; - } - c->Message(Chat::White, "Rule Categories:"); - std::vector::iterator cur, end; - cur = rule_list.begin(); - end = rule_list.end(); - for(; cur != end; ++cur) { - c->Message(Chat::White, " %s", *cur); - } - } else if(sep->argnum == 2) { - const char *catfilt = nullptr; - if(std::string("all") != sep->arg[2]) - catfilt = sep->arg[2]; - std::vector rule_list; - if(!RuleManager::Instance()->ListRules(catfilt, rule_list)) { - c->Message(Chat::Red, "Failed to list rules!"); - return; - } - c->Message(Chat::White, "Rules in category %s:", sep->arg[2]); - std::vector::iterator cur, end; - cur = rule_list.begin(); - end = rule_list.end(); - for(; cur != end; ++cur) { - c->Message(Chat::White, " %s", *cur); - } - } else { - c->Message(Chat::Red, "Invalid argument count, see help."); - } - } else if(!strcasecmp(sep->arg[1], "values")) { - if(sep->argnum != 2) { - c->Message(Chat::Red, "Invalid argument count, see help."); - return; - } else { - const char *catfilt = nullptr; - if(std::string("all") != sep->arg[2]) - catfilt = sep->arg[2]; - std::vector rule_list; - if(!RuleManager::Instance()->ListRules(catfilt, rule_list)) { - c->Message(Chat::Red, "Failed to list rules!"); - return; - } - c->Message(Chat::White, "Rules & values in category %s:", sep->arg[2]); - std::vector::iterator cur, end; - cur = rule_list.begin(); - end = rule_list.end(); - for(std::string tmp_value; cur != end; ++cur) { - if (RuleManager::Instance()->GetRule(*cur, tmp_value)) - c->Message(Chat::White, " %s - %s", *cur, tmp_value.c_str()); - } - } - - } else { - c->Message(Chat::Yellow, "Invalid action specified. use '#rules help' for help"); - } -} - - -void command_task(Client *c, const Seperator *sep) { - //super-command for managing tasks - if(sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { - c->Message(Chat::White, "Syntax: #task [subcommand]"); - c->Message(Chat::White, "------------------------------------------------"); - c->Message(Chat::White, "# Task System Commands"); - c->Message(Chat::White, "------------------------------------------------"); - c->Message( - Chat::White, - fmt::format( - "--- [{}] List active tasks for a client", - EQ::SayLinkEngine::GenerateQuestSaylink("#task show", false, "show") - ).c_str() - ); - c->Message(Chat::White, "--- update [count] | Updates task"); - c->Message(Chat::White, "--- assign | Assigns task to client"); - c->Message( - Chat::White, - fmt::format( - "--- [{}] Reload all Task information from the database", - EQ::SayLinkEngine::GenerateQuestSaylink("#task reloadall", false, "reloadall") - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "--- [{}] Reload Task and Activity information for a single task", - EQ::SayLinkEngine::GenerateQuestSaylink("#task reload task", false, "reload task") - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "--- [{}] Reload goal/reward list information", - EQ::SayLinkEngine::GenerateQuestSaylink("#task reload lists", false, "reload lists") - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "--- [{}] Reload proximity information", - EQ::SayLinkEngine::GenerateQuestSaylink("#task reload prox", false, "reload prox") - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "--- [{}] Reload task set information", - EQ::SayLinkEngine::GenerateQuestSaylink("#task reload sets", false, "reload sets") - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "--- [{}] Purges targeted characters task timers", - EQ::SayLinkEngine::GenerateQuestSaylink("#task purgetimers", false, "purgetimers") - ).c_str() - ); - - c->Message(Chat::White, "------------------------------------------------"); - c->Message(Chat::White, "# Shared Task System Commands"); - c->Message(Chat::White, "------------------------------------------------"); - c->Message( - Chat::White, - fmt::format( - "--- [{}] Purges all active Shared Tasks in memory and database ", - EQ::SayLinkEngine::GenerateQuestSaylink("#task sharedpurge", false, "sharedpurge") - ).c_str() - ); - - return; - } - - Client *client_target = c; - if (c->GetTarget() && c->GetTarget()->IsClient()) { - client_target = c->GetTarget()->CastToClient(); - } - - if (!strcasecmp(sep->arg[1], "show")) { - c->ShowClientTasks(client_target); - return; - } - - if (!strcasecmp(sep->arg[1], "purgetimers")) { - c->Message(15, fmt::format("{}'s task timers have been purged", client_target->GetCleanName()).c_str()); - if (client_target != c) { - client_target->Message(15, "[GM] Your task timers have been purged by a GM"); - } - client_target->PurgeTaskTimers(); - return; - } - - if (!strcasecmp(sep->arg[1], "update")) { - if (sep->argnum >= 3) { - int task_id = atoi(sep->arg[2]); - int activity_id = atoi(sep->arg[3]); - int count = 1; - - if (sep->argnum >= 4) { - count = atoi(sep->arg[4]); - if (count <= 0) { - count = 1; - } - } - c->Message( - Chat::Yellow, - "Updating Task [%i] Activity [%i] Count [%i] for client [%s]", - task_id, - activity_id, - count, - client_target->GetCleanName() - ); - client_target->UpdateTaskActivity(task_id, activity_id, count); - c->ShowClientTasks(client_target); - } - return; - } - - if (!strcasecmp(sep->arg[1], "sharedpurge")) { - if (!strcasecmp(sep->arg[2], "confirm")) { - LogTasksDetail("Sending purge request"); - auto pack = new ServerPacket(ServerOP_SharedTaskPurgeAllCommand, 0); - worldserver.SendPacket(pack); - safe_delete(pack); - - return; - } - - c->Message( - Chat::White, - fmt::format( - "[WARNING] This will purge all active Shared Tasks [{}]?", - EQ::SayLinkEngine::GenerateQuestSaylink("#task sharedpurge confirm", false, "confirm") - ).c_str() - ); - - return; - } - - if (!strcasecmp(sep->arg[1], "assign")) { - int task_id = atoi(sep->arg[2]); - if ((task_id > 0) && (task_id < MAXTASKS)) { - client_target->AssignTask(task_id, 0, false); - c->Message(Chat::Yellow, "Assigned task [%i] to [%s]", task_id, client_target->GetCleanName()); - } - return; - } - - if (!strcasecmp(sep->arg[1], "reloadall")) { - c->Message(Chat::Yellow, "Sending reloadtasks to world"); - worldserver.SendReloadTasks(RELOADTASKS); - c->Message(Chat::Yellow, "Back again"); - return; - } - - if (!strcasecmp(sep->arg[1], "reload")) { - if (sep->arg[2][0] != '\0') { - if (!strcasecmp(sep->arg[2], "lists")) { - c->Message(Chat::Yellow, "Sending reload lists to world"); - worldserver.SendReloadTasks(RELOADTASKGOALLISTS); - c->Message(Chat::Yellow, "Reloaded"); - return; - } - if (!strcasecmp(sep->arg[2], "prox")) { - c->Message(Chat::Yellow, "Sending reload proximities to world"); - worldserver.SendReloadTasks(RELOADTASKPROXIMITIES); - c->Message(Chat::Yellow, "Reloaded"); - return; - } - if (!strcasecmp(sep->arg[2], "sets")) { - c->Message(Chat::Yellow, "Sending reload task sets to world"); - worldserver.SendReloadTasks(RELOADTASKSETS); - c->Message(Chat::Yellow, "Reloaded"); - return; - } - if (!strcasecmp(sep->arg[2], "task") && (sep->arg[3][0] != '\0')) { - int task_id = atoi(sep->arg[3]); - if ((task_id > 0) && (task_id < MAXTASKS)) { - c->Message(Chat::Yellow, "Sending reload task %i to world", task_id); - worldserver.SendReloadTasks(RELOADTASKS, task_id); - c->Message(Chat::Yellow, "Reloaded"); - return; - } - } - } - - } - c->Message(Chat::White, "Unable to interpret command. Type #task help"); - -} -void command_reloadtitles(Client *c, const Seperator *sep) -{ - auto pack = new ServerPacket(ServerOP_ReloadTitles, 0); - worldserver.SendPacket(pack); - safe_delete(pack); - c->Message(Chat::Yellow, "Player Titles Reloaded."); - -} - -void command_traindisc(Client *c, const Seperator *sep) -{ - Client *target = c; - if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { - target = c->GetTarget()->CastToClient(); - } - - if(sep->argnum < 1 || !sep->IsNumber(1)) { - c->Message(Chat::White, "FORMAT: #traindisc "); - return; - } - - uint8 rule_max_level = (uint8) RuleI(Character, MaxLevel); - uint8 max_level = (uint8) std::stoi(sep->arg[1]); - uint8 min_level = ( - sep->IsNumber(2) ? - (uint8) - std::stoi(sep->arg[2]) : - 1 - ); // Default to Level 1 if there isn't a 2nd argument - - if (!c->GetGM()) { // Default to Character:MaxLevel if we're not a GM and Level is higher than the max level - if (max_level > rule_max_level) { - max_level = rule_max_level; - } - - if (min_level > rule_max_level) { - min_level = rule_max_level; - } - } - - if(max_level < 1 || min_level < 1) { - c->Message(Chat::White, "ERROR: Level must be greater than or equal to 1."); - return; - } - - if (min_level > max_level) { - c->Message(Chat::White, "ERROR: Minimum Level must be less than or equal to Maximum Level."); - return; - } - - uint16 learned_disciplines = target->LearnDisciplines(min_level, max_level); - if (target != c) { - std::string discipline_message = ( - learned_disciplines > 0 ? - ( - learned_disciplines == 1 ? - "A new discipline" : - fmt::format("{} New disciplines", learned_disciplines) - ) : - "No new disciplines" - ); - c->Message( - Chat::White, - fmt::format( - "{} learned for {}.", - discipline_message, - target->GetCleanName() - ).c_str() - ); - } -} - -void command_setgraveyard(Client *c, const Seperator *sep) -{ - uint32 zoneid = 0; - uint32 graveyard_id = 0; - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) - t=c->GetTarget()->CastToClient(); - - if(!sep->arg[1][0]) { - c->Message(Chat::White, "Usage: #setgraveyard [zonename]"); - return; - } - - zoneid = ZoneID(sep->arg[1]); - - if(zoneid > 0) { - graveyard_id = content_db.CreateGraveyardRecord(zoneid, t->GetPosition()); - - if(graveyard_id > 0) { - c->Message(Chat::White, "Successfuly added a new record for this graveyard!"); - if(content_db.AddGraveyardIDToZone(zoneid, graveyard_id) > 0) { - c->Message(Chat::White, "Successfuly added this new graveyard for the zone %s.", sep->arg[1]); - // TODO: Set graveyard data to the running zone process. - c->Message(Chat::White, "Done!"); - } - else - c->Message(Chat::White, "Unable to add this new graveyard to the zone %s.", sep->arg[1]); - } - else { - c->Message(Chat::White, "Unable to create a new graveyard record in the database."); - } - } - else { - c->Message(Chat::White, "Unable to retrieve a ZoneID for the zone: %s", sep->arg[1]); - } - - return; -} - -void command_deletegraveyard(Client *c, const Seperator *sep) -{ - uint32 zoneid = 0; - uint32 graveyard_id = 0; - - if(!sep->arg[1][0]) { - c->Message(Chat::White, "Usage: #deletegraveyard [zonename]"); - return; - } - - zoneid = ZoneID(sep->arg[1]); - graveyard_id = content_db.GetZoneGraveyardID(zoneid, 0); - - if(zoneid > 0 && graveyard_id > 0) { - if(content_db.DeleteGraveyard(zoneid, graveyard_id)) - c->Message(Chat::White, "Successfuly deleted graveyard %u for zone %s.", graveyard_id, sep->arg[1]); - else - c->Message(Chat::White, "Unable to delete graveyard %u for zone %s.", graveyard_id, sep->arg[1]); - } - else { - if(zoneid <= 0) - c->Message(Chat::White, "Unable to retrieve a ZoneID for the zone: %s", sep->arg[1]); - else if(graveyard_id <= 0) - c->Message(Chat::White, "Unable to retrieve a valid GraveyardID for the zone: %s", sep->arg[1]); - } - - return; -} - -void command_summonburiedplayercorpse(Client *c, const Seperator *sep) -{ - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) - t = c->GetTarget()->CastToClient(); - else { - c->Message(Chat::White, "You must first select a target!"); - return; - } - - Corpse* PlayerCorpse = database.SummonBuriedCharacterCorpses(t->CharacterID(), t->GetZoneID(), zone->GetInstanceID(), t->GetPosition()); - - if(!PlayerCorpse) - c->Message(Chat::White, "Your target doesn't have any buried corpses."); - - return; -} - -void command_getplayerburiedcorpsecount(Client *c, const Seperator *sep) -{ - Client *t=c; - - if(c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) - t = c->GetTarget()->CastToClient(); - else { - c->Message(Chat::White, "You must first select a target!"); - return; - } - - uint32 CorpseCount = database.GetCharacterBuriedCorpseCount(t->CharacterID()); - - if(CorpseCount > 0) - c->Message(Chat::White, "Your target has a total of %u buried corpses.", CorpseCount); - else - c->Message(Chat::White, "Your target doesn't have any buried corpses."); - - return; -} - -void command_refreshgroup(Client *c, const Seperator *sep) -{ - if(!c) - return; - - Group *g = c->GetGroup(); - - if(!g) - return; - - database.RefreshGroupFromDB(c); - //g->SendUpdate(7, c); -} - -void command_advnpcspawn(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - if (!arguments) { - c->Message(Chat::White, "Usage: #advnpcspawn addentry [Spawngroup ID] [NPC ID] [Spawn Chance] - Adds a new Spawngroup Entry"); - c->Message(Chat::White, "Usage: #advnpcspawn addspawn [Spawngroup ID] - Adds a new Spawngroup Entry from an existing Spawngroup"); - c->Message(Chat::White, "Usage: #advnpcspawn clearbox [Spawngroup ID] - Clears the roambox of a Spawngroup"); - c->Message(Chat::White, "Usage: #advnpcspawn deletespawn - Deletes a Spawngroup"); - c->Message(Chat::White, "Usage: #advnpcspawn editbox [Spawngroup ID] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay] - Edit the roambox of a Spawngroup"); - c->Message(Chat::White, "Usage: #advnpcspawn editrespawn [Respawn Timer] [Variance] - Edit the Respawn Timer of a Spawngroup"); - c->Message(Chat::White, "Usage: #advnpcspawn makegroup [Spawn Group Name] [Spawn Limit] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay] - Makes a new Spawngroup"); - c->Message(Chat::White, "Usage: #advnpcspawn makenpc - Makes a new NPC"); - c->Message(Chat::White, "Usage: #advnpcspawn movespawn - Moves a Spawngroup to your current location"); - c->Message(Chat::White, "Usage: #advnpcspawn setversion [Version] - Sets a Spawngroup's Version"); - return; - } - - std::string spawn_command = str_tolower(sep->arg[1]); - bool is_add_entry = spawn_command.find("addentry") != std::string::npos; - bool is_add_spawn = spawn_command.find("addspawn") != std::string::npos; - bool is_clear_box = spawn_command.find("clearbox") != std::string::npos; - bool is_delete_spawn = spawn_command.find("deletespawn") != std::string::npos; - bool is_edit_box = spawn_command.find("editgroup") != std::string::npos; - bool is_edit_respawn = spawn_command.find("editrespawn") != std::string::npos; - bool is_make_group = spawn_command.find("makegroup") != std::string::npos; - bool is_make_npc = spawn_command.find("makenpc") != std::string::npos; - bool is_move_spawn = spawn_command.find("movespawn") != std::string::npos; - bool is_set_version = spawn_command.find("setversion") != std::string::npos; - if ( - !is_add_entry && - !is_add_spawn && - !is_clear_box && - !is_delete_spawn && - !is_edit_box && - !is_edit_respawn && - !is_make_group && - !is_make_npc && - !is_move_spawn && - !is_set_version - ) { - c->Message(Chat::White, "Usage: #advnpcspawn addentry [Spawngroup ID] [NPC ID] [Spawn Chance] - Adds a new Spawngroup Entry"); - c->Message(Chat::White, "Usage: #advnpcspawn addspawn [Spawngroup ID] - Adds a new Spawngroup Entry from an existing Spawngroup"); - c->Message(Chat::White, "Usage: #advnpcspawn clearbox [Spawngroup ID] - Clears the roambox of a Spawngroup"); - c->Message(Chat::White, "Usage: #advnpcspawn deletespawn - Deletes a Spawngroup"); - c->Message(Chat::White, "Usage: #advnpcspawn editbox [Spawngroup ID] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay] - Edit the roambox of a Spawngroup"); - c->Message(Chat::White, "Usage: #advnpcspawn editrespawn [Respawn Timer] [Variance] - Edit the Respawn Timer of a Spawngroup"); - c->Message(Chat::White, "Usage: #advnpcspawn makegroup [Spawn Group Name] [Spawn Limit] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay] - Makes a new Spawngroup"); - c->Message(Chat::White, "Usage: #advnpcspawn makenpc - Makes a new NPC"); - c->Message(Chat::White, "Usage: #advnpcspawn movespawn - Moves a Spawngroup to your current location"); - c->Message(Chat::White, "Usage: #advnpcspawn setversion [Version] - Sets a Spawngroup's Version"); - return; - } - - - if (is_add_entry) { - if(arguments < 4) { - c->Message(Chat::White, "Usage: #advnpcspawn addentry [Spawngroup ID] [NPC ID] [Spawn Chance]"); - return; - } - - auto spawngroup_id = std::stoi(sep->arg[2]); - auto npc_id = std::stoi(sep->arg[2]); - auto spawn_chance = std::stoi(sep->arg[2]); - - std::string query = fmt::format( - SQL( - INSERT INTO spawnentry (spawngroupID, npcID, chance) - VALUES ({}, {}, {}) - ), - spawngroup_id, - npc_id, - spawn_chance - ); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Failed to add entry to Spawngroup."); - return; - } - - c->Message( - Chat::White, - fmt::format( - "{} ({}) added to Spawngroup {}, its spawn chance is {}%%.", - database.GetCleanNPCNameByID(npc_id), - npc_id, - spawngroup_id, - spawn_chance - ).c_str() - ); - return; - } else if (is_add_spawn) { - content_db.NPCSpawnDB( - NPCSpawnTypes::AddSpawnFromSpawngroup, - zone->GetShortName(), - zone->GetInstanceVersion(), - c, - 0, - std::stoi(sep->arg[2]) - ); - c->Message( - Chat::White, - fmt::format( - "Spawn Added | Added spawn from Spawngroup ID {}.", - std::stoi(sep->arg[2]) - ).c_str() - ); - return; - } else if (is_clear_box) { - if (arguments != 2 || !sep->IsNumber(2)) { - c->Message(Chat::White, "Usage: #advnpcspawn clearbox [Spawngroup ID]"); - return; - } - - std::string query = fmt::format( - "UPDATE spawngroup SET dist = 0, min_x = 0, max_x = 0, min_y = 0, max_y = 0, delay = 0 WHERE id = {}", - std::stoi(sep->arg[2]) - ); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Failed to clear Spawngroup box."); - return; - } - - c->Message( - Chat::White, - fmt::format( - "Spawngroup {} Roambox Cleared | Delay: 0 Distance: 0.00", - std::stoi(sep->arg[2]) - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "Spawngroup {} Roambox Cleared | Minimum X: 0.00 Maximum X: 0.00", - std::stoi(sep->arg[2]) - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "Spawngroup {} Roambox Cleared | Minimum Y: 0.00 Maximum Y: 0.00", - std::stoi(sep->arg[2]) - ).c_str() - ); - return; - } else if (is_delete_spawn) { - if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { - c->Message(Chat::White, "You must target an NPC to use this command."); - return; - } - - NPC *target = c->GetTarget()->CastToNPC(); - Spawn2* spawn2 = target->respawn2; - if(!spawn2) { - c->Message(Chat::White, "Failed to delete spawn because NPC has no Spawn2."); - return; - } - - auto spawn2_id = spawn2->GetID(); - std::string query = fmt::format( - "DELETE FROM spawn2 WHERE id = {}", - spawn2_id - ); - auto results = content_db.QueryDatabase(query); - if(!results.Success()) { - c->Message(Chat::White, "Failed to delete spawn."); - return; - } - - c->Message( - Chat::White, - fmt::format( - "Spawn2 {} Deleted | Name: {} ({})", - spawn2_id, - target->GetCleanName(), - target->GetID() - ).c_str() - ); - target->Depop(false); - return; - } else if (is_edit_box) { - if ( - arguments != 8 || - !sep->IsNumber(3) || - !sep->IsNumber(4) || - !sep->IsNumber(5) || - !sep->IsNumber(6) || - !sep->IsNumber(7) || - !sep->IsNumber(8) - ) { - c->Message(Chat::White, "Usage: #advnpcspawn editbox [Spawngroup ID] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay]"); - return; - } - auto spawngroup_id = std::stoi(sep->arg[2]); - auto distance = std::stof(sep->arg[3]); - auto minimum_x = std::stof(sep->arg[4]); - auto maximum_x = std::stof(sep->arg[5]); - auto minimum_y = std::stof(sep->arg[6]); - auto maximum_y = std::stof(sep->arg[7]); - auto delay = std::stoi(sep->arg[8]); - - std::string query = fmt::format( - "UPDATE spawngroup SET dist = {:.2f}, min_x = {:.2f}, max_x = {:.2f}, max_y = {:.2f}, min_y = {:.2f}, delay = {} WHERE id = {}", - distance, - minimum_x, - maximum_x, - minimum_y, - maximum_y, - delay, - spawngroup_id - ); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Failed to edit Spawngroup box."); - return; - } - - c->Message( - Chat::White, - fmt::format( - "Spawngroup {} Roambox Edited | Delay: {} Distance: {:.2f}", - spawngroup_id, - delay, - distance - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "Spawngroup {} Roambox Edited | Minimum X: {:.2f} Maximum X: {:.2f}", - spawngroup_id, - minimum_x, - maximum_x - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "Spawngroup {} Roambox Edited | Minimum Y: {:.2f} Maximum Y: {:.2f}", - spawngroup_id, - minimum_y, - maximum_y - ).c_str() - ); - return; - } else if (is_edit_respawn) { - if (arguments < 2 || !sep->IsNumber(2)) { - c->Message(Chat::White, "Usage: #advnpcspawn editrespawn [Respawn Timer] [Variance]"); - return; - } - - if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { - c->Message(Chat::White, "You must target an NPC to use this command."); - return; - } - - NPC *target = c->GetTarget()->CastToNPC(); - Spawn2* spawn2 = target->respawn2; - if(!spawn2) { - c->Message(Chat::White, "Failed to edit respawn because NPC has no Spawn2."); - return; - } - - auto spawn2_id = spawn2->GetID(); - uint32 respawn_timer = std::stoi(sep->arg[2]); - uint32 variance = ( - sep->IsNumber(3) ? - std::stoi(sep->arg[3]) : - spawn2->GetVariance() - ); - std::string query = fmt::format( - "UPDATE spawn2 SET respawntime = {}, variance = {} WHERE id = {}", - respawn_timer, - variance, - spawn2_id - ); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Failed to edit respawn."); - return; - } - - c->Message( - Chat::White, - fmt::format( - "Spawn2 {} Respawn Modified | Name: {} ({}) Respawn Timer: {} Variance: {}", - spawn2_id, - target->GetCleanName(), - target->GetID(), - respawn_timer, - variance - ).c_str() - ); - spawn2->SetRespawnTimer(respawn_timer); - spawn2->SetVariance(variance); - return; - } else if (is_make_group) { - if ( - arguments != 9 || - !sep->IsNumber(3) || - !sep->IsNumber(4) || - !sep->IsNumber(5) || - !sep->IsNumber(6) || - !sep->IsNumber(7) || - !sep->IsNumber(8) || - !sep->IsNumber(9) - ) { - c->Message(Chat::White, "Usage: #advncspawn makegroup [Spawn Group Name] [Spawn Limit] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay]"); - return; - } - std::string spawngroup_name = sep->arg[2]; - auto spawn_limit = std::stoi(sep->arg[3]); - auto distance = std::stof(sep->arg[4]); - auto minimum_x = std::stof(sep->arg[5]); - auto maximum_x = std::stof(sep->arg[6]); - auto minimum_y = std::stof(sep->arg[7]); - auto maximum_y = std::stof(sep->arg[8]); - auto delay = std::stoi(sep->arg[9]); - - std::string query = fmt::format( - "INSERT INTO spawngroup" - "(name, spawn_limit, dist, min_x, max_x, min_y, max_y, delay)" - "VALUES ('{}', {}, {:.2f}, {:.2f}, {:.2f}, {:.2f}, {:.2f}, {})", - spawngroup_name, - spawn_limit, - distance, - minimum_x, - maximum_x, - minimum_y, - maximum_y, - delay - ); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Failed to make Spawngroup."); - return; - } - - auto spawngroup_id = results.LastInsertedID(); - c->Message( - Chat::White, - fmt::format( - "Spawngroup {} Created | Name: {} Spawn Limit: {}", - spawngroup_id, - spawngroup_name, - spawn_limit - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "Spawngroup {} Created | Delay: {} Distance: {:.2f}", - spawngroup_id, - delay, - distance - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "Spawngroup {} Created | Minimum X: {:.2f} Maximum X: {:.2f}", - spawngroup_id, - minimum_x, - maximum_x - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "Spawngroup {} Created | Minimum Y: {:.2f} Maximum Y: {:.2f}", - spawngroup_id, - minimum_y, - maximum_y - ).c_str() - ); - return; - } else if (is_make_npc) { - if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { - c->Message(Chat::White, "You must target an NPC to use this command."); - return; - } - - NPC *target = c->GetTarget()->CastToNPC(); - content_db.NPCSpawnDB( - NPCSpawnTypes::CreateNewNPC, - zone->GetShortName(), - zone->GetInstanceVersion(), - c, - target - ); - return; - } else if (is_move_spawn) { - if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { - c->Message(Chat::White, "You must target an NPC to use this command."); - return; - } - - NPC *target = c->GetTarget()->CastToNPC(); - Spawn2* spawn2 = target->respawn2; - if(!spawn2) { - c->Message(Chat::White, "Failed to move spawn because NPC has no Spawn2."); - return; - } - - auto client_position = c->GetPosition(); - auto spawn2_id = spawn2->GetID(); - std::string query = fmt::format( - "UPDATE spawn2 SET x = {:.2f}, y = {:.2f}, z = {:.2f}, heading = {:.2f} WHERE id = {}", - client_position.x, - client_position.y, - client_position.z, - client_position.w, - spawn2_id - ); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Failed to move spawn."); - return; - } - - c->Message( - Chat::White, - fmt::format( - "Spawn2 {} Moved | Name: {} ({})", - spawn2_id, - target->GetCleanName(), - target->GetID() - ).c_str() - ); - c->Message( - Chat::White, - fmt::format( - "Spawn2 {} Moved | XYZ: {}, {}, {} Heading: {}", - spawn2_id, - client_position.x, - client_position.y, - client_position.z, - client_position.w - ).c_str() - ); - target->GMMove( - client_position.x, - client_position.y, - client_position.z, - client_position.w - ); - return; - } else if (is_set_version) { - if (arguments != 2 || !sep->IsNumber(2)) { - c->Message(Chat::White, "Usage: #advnpcspawn setversion [Version]"); - return; - } - - if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { - c->Message(Chat::White, "You must target an NPC to use this command."); - return; - } - - NPC* target = c->GetTarget()->CastToNPC(); - auto version = std::stoi(sep->arg[2]); - std::string query = fmt::format( - "UPDATE spawn2 SET version = {} WHERE spawngroupID = {}", - version, - target->GetSpawnGroupId() - ); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Failed to set version."); - return; - } - - c->Message( - Chat::White, - fmt::format( - "Spawngroup {} Version Modified | Name: {} ({}) Version: {}", - target->GetSpawnGroupId(), - target->GetCleanName(), - target->GetID(), - version - ).c_str() - ); - target->Depop(false); - return; - } -} - -void command_aggrozone(Client *c, const Seperator *sep) { - if(!c) - return; - - Mob *m = c->CastToMob(); - - if (!m) - return; - - uint32 hate = atoi(sep->arg[1]); //should default to 0 if we don't enter anything - entity_list.AggroZone(m, hate); - c->Message(Chat::White, "Train to you! Last chance to go invulnerable..."); -} - -void command_modifynpcstat(Client *c, const Seperator *sep) -{ - if(!c) - return; - - if(sep->arg[1][0] == '\0') - { - c->Message(Chat::White, "usage #modifynpcstat arg value"); - c->Message(Chat::White, "Args: ac, str, sta, agi, dex, wis, _int, cha, max_hp, mr, fr, cr, pr, dr, runspeed, special_attacks, " - "attack_speed, atk, accuracy, trackable, min_hit, max_hit, see_invis_undead, see_hide, see_improved_hide, " - "hp_regen, mana_regen, aggro, assist, slow_mitigation, loottable_id, healscale, spellscale"); - return; - } - - if(!c->GetTarget()) - return; - - if(!c->GetTarget()->IsNPC()) - return; - - c->GetTarget()->CastToNPC()->ModifyNPCStat(sep->arg[1], sep->arg[2]); -} - -void command_instance(Client *c, const Seperator *sep) -{ - if(!c) - return; - - //options: - //help - //create [zone_id] [version] - //destroy [instance_id] - //add [instance_id] [player_name] - //remove [instance_id] [player_name] - //list [player_name] - - if(strcasecmp(sep->arg[1], "help") == 0) - { - c->Message(Chat::White, "#instance usage:"); - c->Message(Chat::White, "#instance create zone_id version duration - Creates an instance of version 'version' in the " - "zone with id matching zone_id, will last for duration seconds."); - c->Message(Chat::White, "#instance destroy instance_id - Destroys the instance with id matching instance_id."); - c->Message(Chat::White, "#instance add instance_id player_name - adds the player 'player_name' to the instance " - "with id matching instance_id."); - c->Message(Chat::White, "#instance remove instance_id player_name - removes the player 'player_name' from the " - "instance with id matching instance_id."); - c->Message(Chat::White, "#instance list player_name - lists all the instances 'player_name' is apart of."); - return; - } - else if(strcasecmp(sep->arg[1], "create") == 0) - { - if(!sep->IsNumber(3) || !sep->IsNumber(4)) - { - c->Message(Chat::White, "#instance create zone_id version duration - Creates an instance of version 'version' in the " - "zone with id matching zone_id, will last for duration seconds."); - return; - } - - const char * zn = nullptr; - uint32 zone_id = 0; - - if(sep->IsNumber(2)) - { - zone_id = atoi(sep->arg[2]); - } - else - { - zone_id = ZoneID(sep->arg[2]); - } - - uint32 version = atoi(sep->arg[3]); - uint32 duration = atoi(sep->arg[4]); - zn = ZoneName(zone_id); - - if(!zn) - { - c->Message(Chat::White, "Zone with id %lu was not found by the server.", (unsigned long)zone_id); - return; - } - - uint16 id = 0; - if(!database.GetUnusedInstanceID(id)) - { - c->Message(Chat::White, "Server was unable to find a free instance id."); - return; - } - - if(!database.CreateInstance(id, zone_id, version, duration)) - { - c->Message(Chat::White, "Server was unable to create a new instance."); - return; - } - - c->Message(Chat::White, "New instance %s was created with id %lu.", zn, (unsigned long)id); - } - else if(strcasecmp(sep->arg[1], "destroy") == 0) - { - if(!sep->IsNumber(2)) - { - c->Message(Chat::White, "#instance destroy instance_id - Destroys the instance with id matching instance_id."); - return; - } - - uint16 id = atoi(sep->arg[2]); - database.DeleteInstance(id); - c->Message(Chat::White, "Destroyed instance with id %lu.", (unsigned long)id); - } - else if(strcasecmp(sep->arg[1], "add") == 0) - { - if(!sep->IsNumber(2)) - { - c->Message(Chat::White, "#instance add instance_id player_name - adds the player 'player_name' to the instance " - "with id matching instance_id."); - return; - } - - uint16 id = atoi(sep->arg[2]); - uint32 charid = database.GetCharacterID(sep->arg[3]); - - if(id <= 0 || charid <= 0) - { - c->Message(Chat::White, "Must enter a valid instance id and player name."); - return; - } - - if(!database.CheckInstanceExists(id)) - { - c->Message(Chat::White, "Instance does not exist."); - return; - } - - uint32 zone_id = database.ZoneIDFromInstanceID(id); - uint32 version = database.VersionFromInstanceID(id); - uint32 cur_id = database.GetInstanceID(zone_id, charid, version); - if(cur_id == 0) - { - if(database.AddClientToInstance(id, charid)) - { - c->Message(Chat::White, "Added client to instance."); - } - else - { - c->Message(Chat::White, "Failed to add client to instance."); - } - } - else - { - c->Message(Chat::White, "Client was already saved to %u which has uses the same zone and version as that instance.", cur_id); - } - } - else if(strcasecmp(sep->arg[1], "remove") == 0) - { - if(!sep->IsNumber(2)) - { - c->Message(Chat::White, "#instance remove instance_id player_name - removes the player 'player_name' from the " - "instance with id matching instance_id."); - return; - } - - uint16 id = atoi(sep->arg[2]); - uint32 charid = database.GetCharacterID(sep->arg[3]); - - if(id <= 0 || charid <= 0) - { - c->Message(Chat::White, "Must enter a valid instance id and player name."); - } - - if(database.RemoveClientFromInstance(id, charid)) - { - c->Message(Chat::White, "Removed client from instance."); - } - else - { - c->Message(Chat::White, "Failed to remove client from instance."); - } - } - else if(strcasecmp(sep->arg[1], "list") == 0) - { - uint32 charid = database.GetCharacterID(sep->arg[2]); - if(charid <= 0) - { - if(c->GetTarget() == nullptr || (c->GetTarget() && !c->GetTarget()->IsClient())) - { - c->Message(Chat::White, "Character not found."); - return; - } - else - charid = c->GetTarget()->CastToClient()->CharacterID(); - } - - database.ListAllInstances(c, charid); - } - else - { - c->Message(Chat::White, "Invalid Argument."); - c->Message(Chat::White, "#instance usage:"); - c->Message(Chat::White, "#instance create zone_id version duration - Creates an instance of version 'version' in the " - "zone with id matching zone_id, will last for duration seconds."); - c->Message(Chat::White, "#instance destroy instance_id - Destroys the instance with id matching instance_id."); - c->Message(Chat::White, "#instance add instance_id player_name - adds the player 'player_name' to the instance " - "with id matching instance_id."); - c->Message(Chat::White, "#instance remove instance_id player_name - removes the player 'player_name' from the " - "instance with id matching instance_id."); - c->Message(Chat::White, "#instance list player_name - lists all the instances 'player_name' is apart of."); - return; - } -} - -void command_setstartzone(Client *c, const Seperator *sep) -{ - uint32 startzone = 0; - Client* target = nullptr; - if(c->GetTarget() && c->GetTarget()->IsClient() && sep->arg[1][0] != 0) - target = c->GetTarget()->CastToClient(); - else { - c->Message(Chat::White, "Usage: (needs PC target) #setstartzone zonename"); - c->Message(Chat::White, "Optional Usage: Use '#setstartzone reset' or '#setstartzone 0' to clear a starting zone. Player can select a starting zone using /setstartcity"); - return; - } - - if(sep->IsNumber(1)) { - startzone = atoi(sep->arg[1]); - } - else if(strcasecmp(sep->arg[1],"reset") == 0) { - startzone = 0; - } - else { - startzone = ZoneID(sep->arg[1]); - if(startzone == 0) { - c->Message(Chat::White, "Unable to locate zone '%s'", sep->arg[1]); - return; - } - } - - target->SetStartZone(startzone); -} - -void command_netstats(Client *c, const Seperator *sep) -{ - if(c) - { - auto client = c; - if (c->GetTarget() && c->GetTarget()->IsClient()) { - client = c->GetTarget()->CastToClient(); - } - - if (strcasecmp(sep->arg[1], "reset") == 0) { - auto connection = c->Connection(); - c->Message(Chat::White, "Resetting client stats (packet loss will not read correctly after reset)."); - connection->ResetStats(); - return; - } - - auto connection = c->Connection(); - auto opts = connection->GetManager()->GetOptions(); - auto eqs_stats = connection->GetStats(); - auto &stats = eqs_stats.DaybreakStats; - auto now = EQ::Net::Clock::now(); - auto sec_since_stats_reset = std::chrono::duration_cast>(now - stats.created).count(); - - c->Message(Chat::White, "Netstats:"); - c->Message(Chat::White, "--------------------------------------------------------------------"); - c->Message(Chat::White, "Sent Bytes: %u (%.2f/sec)", stats.sent_bytes, stats.sent_bytes / sec_since_stats_reset); - c->Message(Chat::White, "Recv Bytes: %u (%.2f/sec)", stats.recv_bytes, stats.recv_bytes / sec_since_stats_reset); - c->Message(Chat::White, "Bytes Before Encode (Sent): %u, Compression Rate: %.2f%%", stats.bytes_before_encode, - static_cast(stats.bytes_before_encode - stats.sent_bytes) / static_cast(stats.bytes_before_encode) * 100.0); - c->Message(Chat::White, "Bytes After Decode (Recv): %u, Compression Rate: %.2f%%", stats.bytes_after_decode, - static_cast(stats.bytes_after_decode - stats.recv_bytes) / static_cast(stats.bytes_after_decode) * 100.0); - c->Message(Chat::White, "Min Ping: %u", stats.min_ping); - c->Message(Chat::White, "Max Ping: %u", stats.max_ping); - c->Message(Chat::White, "Last Ping: %u", stats.last_ping); - c->Message(Chat::White, "Average Ping: %u", stats.avg_ping); - c->Message(Chat::White, "--------------------------------------------------------------------"); - c->Message(Chat::White, "(Realtime) Recv Packets: %u (%.2f/sec)", stats.recv_packets, stats.recv_packets / sec_since_stats_reset); - c->Message(Chat::White, "(Realtime) Sent Packets: %u (%.2f/sec)", stats.sent_packets, stats.sent_packets / sec_since_stats_reset); - c->Message(Chat::White, "(Sync) Recv Packets: %u", stats.sync_recv_packets); - c->Message(Chat::White, "(Sync) Sent Packets: %u", stats.sync_sent_packets); - c->Message(Chat::White, "(Sync) Remote Recv Packets: %u", stats.sync_remote_recv_packets); - c->Message(Chat::White, "(Sync) Remote Sent Packets: %u", stats.sync_remote_sent_packets); - c->Message(Chat::White, "Packet Loss In: %.2f%%", 100.0 * (1.0 - static_cast(stats.sync_recv_packets) / static_cast(stats.sync_remote_sent_packets))); - c->Message(Chat::White, "Packet Loss Out: %.2f%%", 100.0 * (1.0 - static_cast(stats.sync_remote_recv_packets) / static_cast(stats.sync_sent_packets))); - c->Message(Chat::White, "--------------------------------------------------------------------"); - c->Message(Chat::White, "Resent Packets: %u (%.2f/sec)", stats.resent_packets, stats.resent_packets / sec_since_stats_reset); - c->Message(Chat::White, "Resent Fragments: %u (%.2f/sec)", stats.resent_fragments, stats.resent_fragments / sec_since_stats_reset); - c->Message(Chat::White, "Resent Non-Fragments: %u (%.2f/sec)", stats.resent_full, stats.resent_full / sec_since_stats_reset); - c->Message(Chat::White, "Dropped Datarate Packets: %u (%.2f/sec)", stats.dropped_datarate_packets, stats.dropped_datarate_packets / sec_since_stats_reset); - - if (opts.daybreak_options.outgoing_data_rate > 0.0) { - c->Message(Chat::White, "Outgoing Link Saturation %.2f%% (%.2fkb/sec)", 100.0 * (1.0 - ((opts.daybreak_options.outgoing_data_rate - stats.datarate_remaining) / opts.daybreak_options.outgoing_data_rate)), opts.daybreak_options.outgoing_data_rate); - } - - if (strcasecmp(sep->arg[1], "full") == 0) { - c->Message(Chat::White, "--------------------------------------------------------------------"); - c->Message(Chat::White, "Sent Packet Types"); - for (auto i = 0; i < _maxEmuOpcode; ++i) { - auto cnt = eqs_stats.SentCount[i]; - if (cnt > 0) { - c->Message(Chat::White, "%s: %u (%.2f / sec)", OpcodeNames[i], cnt, cnt / sec_since_stats_reset); - } - } - - c->Message(Chat::White, "--------------------------------------------------------------------"); - c->Message(Chat::White, "Recv Packet Types"); - for (auto i = 0; i < _maxEmuOpcode; ++i) { - auto cnt = eqs_stats.RecvCount[i]; - if (cnt > 0) { - c->Message(Chat::White, "%s: %u (%.2f / sec)", OpcodeNames[i], cnt, cnt / sec_since_stats_reset); - } - } - } - - c->Message(Chat::White, "--------------------------------------------------------------------"); - } -} - -void command_object(Client *c, const Seperator *sep) -{ - if (!c) - return; // Crash Suppressant: No client. How did we get here? - - // Save it here. We sometimes have need to refer to it in multiple places. - const char *usage_string = "Usage: #object List|Add|Edit|Move|Rotate|Save|Copy|Delete|Undo"; - - if ((!sep) || (sep->argnum == 0)) { - c->Message(Chat::White, usage_string); - return; - } - - Object *o = nullptr; - Object_Struct od; - Door_Struct *ds; - uint32 id = 0; - uint32 itemid = 0; - uint32 icon = 0; - uint32 instance = 0; - uint32 newid = 0; - uint16 radius; - EQApplicationPacket *app; - - bool bNewObject = false; - - float x2; - float y2; - - // Temporary object type for static objects to allow manipulation - // NOTE: Zone::LoadZoneObjects() currently loads this as an uint8, so max value is 255! - static const uint32 staticType = 255; - - // Case insensitive commands (List == list == LIST) - strlwr(sep->arg[1]); - - if (strcasecmp(sep->arg[1], "list") == 0) { - // Insufficient or invalid args - if ((sep->argnum < 2) || (sep->arg[2][0] < '0') || - ((sep->arg[2][0] > '9') && ((sep->arg[2][0] & 0xDF) != 'A'))) { - c->Message(Chat::White, "Usage: #object List All|(radius)"); - return; - } - - if ((sep->arg[2][0] & 0xDF) == 'A') - radius = 0; // List All - else if ((radius = atoi(sep->arg[2])) <= 0) - radius = 500; // Invalid radius. Default to 500 units. - - if (radius == 0) - c->Message(Chat::White, "Objects within this zone:"); - else - c->Message(Chat::White, "Objects within %u units of your current location:", radius); - - std::string query; - if (radius) - query = StringFormat( - "SELECT id, xpos, ypos, zpos, heading, itemid, " - "objectname, type, icon, unknown08, unknown10, unknown20 " - "FROM object WHERE zoneid = %u AND version = %u " - "AND (xpos BETWEEN %.1f AND %.1f) " - "AND (ypos BETWEEN %.1f AND %.1f) " - "AND (zpos BETWEEN %.1f AND %.1f) " - "ORDER BY id", - zone->GetZoneID(), zone->GetInstanceVersion(), - c->GetX() - radius, // Yes, we're actually using a bounding box instead of a radius. - c->GetX() + radius, // Much less processing power used this way. - c->GetY() - radius, c->GetY() + radius, c->GetZ() - radius, c->GetZ() + radius); - else - query = StringFormat("SELECT id, xpos, ypos, zpos, heading, itemid, " - "objectname, type, icon, unknown08, unknown10, unknown20 " - "FROM object WHERE zoneid = %u AND version = %u " - "ORDER BY id", - zone->GetZoneID(), zone->GetInstanceVersion()); - - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Error in objects query"); - return; - } - - for (auto row = results.begin(); row != results.end(); ++row) { - id = atoi(row[0]); - od.x = atof(row[1]); - od.y = atof(row[2]); - od.z = atof(row[3]); - od.heading = atof(row[4]); - itemid = atoi(row[5]); - strn0cpy(od.object_name, row[6], sizeof(od.object_name)); - od.object_name[sizeof(od.object_name) - 1] = - '\0'; // Required if strlen(row[col++]) exactly == sizeof(object_name) - - od.object_type = atoi(row[7]); - icon = atoi(row[8]); - od.size = atoi(row[9]); - od.solidtype = atoi(row[10]); - od.unknown020 = atoi(row[11]); - - switch (od.object_type) { - case 0: // Static Object - case staticType: // Static Object unlocked for changes - if (od.size == 0) // Unknown08 field is optional Size parameter for static objects - od.size = 100; // Static object default Size is 100% - - c->Message(Chat::White, "- STATIC Object (%s): id %u, x %.1f, y %.1f, z %.1f, h %.1f, model %s, " - "size %u, solidtype %u, incline %u", - (od.object_type == 0) ? "locked" : "unlocked", id, od.x, od.y, od.z, - od.heading, od.object_name, od.size, od.solidtype, od.unknown020); - break; - - case OT_DROPPEDITEM: // Ground Spawn - c->Message(Chat::White, "- TEMPORARY Object: id %u, x %.1f, y %.1f, z %.1f, h %.1f, itemid %u, " - "model %s, icon %u", - id, od.x, od.y, od.z, od.heading, itemid, od.object_name, icon); - break; - - default: // All others == Tradeskill Objects - c->Message(Chat::White, "- TRADESKILL Object: id %u, x %.1f, y %.1f, z %.1f, h %.1f, model %s, " - "type %u, icon %u", - id, od.x, od.y, od.z, od.heading, od.object_name, od.object_type, icon); - break; - } - } - - c->Message(Chat::White, "%u object%s found", results.RowCount(), (results.RowCount() == 1) ? "" : "s"); - return; - } - - if (strcasecmp(sep->arg[1], "add") == 0) { - // Insufficient or invalid arguments - if ((sep->argnum < 3) || - ((sep->arg[3][0] == '\0') && (sep->arg[4][0] < '0') && (sep->arg[4][0] > '9'))) { - c->Message(Chat::White, "Usage: (Static Object): #object Add [ObjectID] 0 Model [SizePercent] " - "[SolidType] [Incline]"); - c->Message(Chat::White, "Usage: (Tradeskill Object): #object Add [ObjectID] TypeNum Model Icon"); - c->Message(Chat::White, "- Notes: Model must start with a letter, max length 16. SolidTypes = 0 (Solid), " - "1 (Sometimes Non-Solid)"); - return; - } - - int col; - - if (sep->argnum > 3) { // Model name in arg3? - if ((sep->arg[3][0] <= '9') && (sep->arg[3][0] >= '0')) { - // Nope, user must have specified ObjectID. Extract it. - id = atoi(sep->arg[2]); - col = 1; // Bump all other arguments one to the right. Model is in arg4. - } else { - // Yep, arg3 is non-numeric, ObjectID must be omitted and model must be arg3 - id = 0; - col = 0; - } - } else { - // Nope, only 3 args. Object ID must be omitted and arg3 must be model. - id = 0; - col = 0; - } - - memset(&od, 0, sizeof(od)); - - od.object_type = atoi(sep->arg[2 + col]); - - switch (od.object_type) { - case 0: // Static Object - if ((sep->argnum - col) > 3) { - od.size = atoi(sep->arg[4 + col]); // Size specified - - if ((sep->argnum - col) > 4) { - od.solidtype = atoi(sep->arg[5 + col]); // SolidType specified - - if ((sep->argnum - col) > 5) - od.unknown020 = atoi(sep->arg[6 + col]); // Incline specified - } - } - break; - - case 1: // Ground Spawn - c->Message(Chat::White, "ERROR: Object Type 1 is used for temporarily spawned ground spawns and dropped " - "items, which are not supported with #object. See the 'ground_spawns' table in " - "the database."); - return; - - default: // Everything else == Tradeskill Object - icon = ((sep->argnum - col) > 3) ? atoi(sep->arg[4 + col]) : 0; - - if (icon == 0) { - c->Message(Chat::White, "ERROR: Required property 'Icon' not specified for Tradeskill Object"); - return; - } - - break; - } - - od.x = c->GetX(); - od.y = c->GetY(); - od.z = c->GetZ() - (c->GetSize() * 0.625f); - od.heading = c->GetHeading(); - - std::string query; - if (id) { - // ID specified. Verify that it doesn't already exist. - query = StringFormat("SELECT COUNT(*) FROM object WHERE ID = %u", id); - auto results = content_db.QueryDatabase(query); - if (results.Success() && results.RowCount() != 0) { - auto row = results.begin(); - if (atoi(row[0]) > 0) // Yep, in database already. - id = 0; - } - - // Not in database. Already spawned, just not saved? - // Yep, already spawned. - if (id && entity_list.FindObject(id)) - id = 0; - - if (id == 0) { - c->Message(Chat::White, "ERROR: An object already exists with the id %u", atoi(sep->arg[2])); - return; - } - } - - int objectsFound = 0; - // Verify no other objects already in this spot (accidental double-click of Hotkey?) - query = StringFormat( - "SELECT COUNT(*) FROM object WHERE zoneid = %u " - "AND version=%u AND (xpos BETWEEN %.1f AND %.1f) " - "AND (ypos BETWEEN %.1f AND %.1f) " - "AND (zpos BETWEEN %.1f AND %.1f)", - zone->GetZoneID(), zone->GetInstanceVersion(), od.x - 0.2f, - od.x + 0.2f, // Yes, we're actually using a bounding box instead of a radius. - od.y - 0.2f, od.y + 0.2f, // Much less processing power used this way. - od.z - 0.2f, od.z + 0.2f); // It's pretty forgiving, though, allowing for close-proximity objects - - auto results = content_db.QueryDatabase(query); - if (results.Success() && results.RowCount() != 0) { - auto row = results.begin(); - objectsFound = atoi(row[0]); // Number of nearby objects from database - } - - // No objects found in database too close. How about spawned but not yet saved? - if (objectsFound == 0 && entity_list.FindNearbyObject(od.x, od.y, od.z, 0.2f)) - objectsFound = 1; - - if (objectsFound) { - c->Message(Chat::White, "ERROR: Object already at this location."); - return; - } - - // Strip any single quotes from objectname (SQL injection FTL!) - strn0cpy(od.object_name, sep->arg[3 + col], sizeof(od.object_name)); - - uint32 len = strlen(od.object_name); - for (col = 0; col < (uint32)len; col++) { - if (od.object_name[col] != '\'') - continue; - - // Uh oh, 1337 h4x0r monkeying around! Strip that apostrophe! - memcpy(&od.object_name[col], &od.object_name[col + 1], len - col); - len--; - col--; - } - - strupr(od.object_name); // Model names are always upper-case. - - if ((od.object_name[0] < 'A') || (od.object_name[0] > 'Z')) { - c->Message(Chat::White, "ERROR: Model name must start with a letter."); - return; - } - - if (id == 0) { - // No ID specified. Get a best-guess next number from the database - // If there's a problem retrieving an ID from the database, it'll end up being object # 1. No - // biggie. - - query = "SELECT MAX(id) FROM object"; - results = content_db.QueryDatabase(query); - if (results.Success() && results.RowCount() != 0) { - auto row = results.begin(); - id = atoi(row[0]); - } - - id++; - } - - // Make sure not to overwrite already-spawned objects that haven't been saved yet. - while (o = entity_list.FindObject(id)) - id++; - - // Static object - if (od.object_type == 0) - od.object_type = staticType; // Temporary. We'll make it 0 when we Save - - od.zone_id = zone->GetZoneID(); - od.zone_instance = zone->GetInstanceVersion(); - - o = new Object(id, od.object_type, icon, od, nullptr); - - // Add to our zone entity list and spawn immediately for all clients - entity_list.AddObject(o, true); - - // Bump player back to avoid getting stuck inside new object - - x2 = 10.0f * sin(c->GetHeading() / 256.0f * 3.14159265f); - y2 = 10.0f * cos(c->GetHeading() / 256.0f * 3.14159265f); - c->MovePC(c->GetX() - x2, c->GetY() - y2, c->GetZ(), c->GetHeading()); - - c->Message(Chat::White, "Spawning object with tentative id %u at location (%.1f, %.1f, %.1f heading %.1f). Use " - "'#object Save' to save to database when satisfied with placement.", - id, od.x, od.y, od.z, od.heading); - - // Temporary Static Object - if (od.object_type == staticType) - c->Message(Chat::White, "- Note: Static Object will act like a tradeskill container and will not reflect " - "size, solidtype, or incline values until you commit with '#object Save', after " - "which it will be unchangeable until you use '#object Edit' and zone back in."); - - return; - } - - if (strcasecmp(sep->arg[1], "edit") == 0) { - - if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) < 1)) { - c->Message(Chat::White, "Usage: #object Edit (ObjectID) [PropertyName] [NewValue]"); - c->Message(Chat::White, "- Static Object (Type 0) Properties: model, type, size, solidtype, incline"); - c->Message(Chat::White, "- Tradeskill Object (Type 2+) Properties: model, type, icon"); - - return; - } - - o = entity_list.FindObject(id); - - // Object already available in-zone? - if (o) { - // Yep, looks like we can make real-time changes. - if (sep->argnum < 4) { - // Or not. '#object Edit (ObjectID)' called without PropertyName and NewValue - c->Message(Chat::White, "Note: Object %u already unlocked and ready for changes", id); - return; - } - } else { - // Object not found in-zone in a modifiable form. Check for valid matching circumstances. - std::string query = StringFormat("SELECT zoneid, version, type FROM object WHERE id = %u", id); - auto results = content_db.QueryDatabase(query); - if (!results.Success() || results.RowCount() == 0) { - c->Message(Chat::White, "ERROR: Object %u not found", id); - return; - } - - auto row = results.begin(); - od.zone_id = atoi(row[0]); - od.zone_instance = atoi(row[1]); - od.object_type = atoi(row[2]); - uint32 objectsFound = 1; - - // Object not in this zone? - if (od.zone_id != zone->GetZoneID()) { - c->Message(Chat::White, "ERROR: Object %u not in this zone.", id); - return; - } - - // Object not in this instance? - if (od.zone_instance != zone->GetInstanceVersion()) { - c->Message(Chat::White, "ERROR: Object %u not part of this instance version.", id); - return; - } - - switch (od.object_type) { - case 0: // Static object needing unlocking - // Convert to tradeskill object temporarily for changes - query = StringFormat("UPDATE object SET type = %u WHERE id = %u", staticType, id); - - content_db.QueryDatabase(query); - - c->Message(Chat::White, "Static Object %u unlocked for editing. You must zone out and back in to " - "make your changes, then commit them with '#object Save'.", - id); - if (sep->argnum >= 4) - c->Message(Chat::White, "NOTE: The change you specified has not been applied, since the " - "static object had not been unlocked for editing yet."); - return; - - case OT_DROPPEDITEM: - c->Message(Chat::White, "ERROR: Object %u is a temporarily spawned ground spawn or dropped item, " - "which cannot be manipulated with #object. See the 'ground_spawns' table " - "in the database.", - id); - return; - - case staticType: - c->Message(Chat::White, "ERROR: Object %u has been unlocked for editing, but you must zone out " - "and back in for your client to refresh its object table before you can " - "make changes to it.", - id); - return; - - default: - // Unknown error preventing us from seeing the object in the zone. - c->Message(Chat::White, "ERROR: Unknown problem attempting to manipulate object %u", id); - return; - } - } - - // If we're here, we have a manipulable object ready for changes. - strlwr(sep->arg[3]); // Case insensitive PropertyName - strupr(sep->arg[4]); // In case it's model name, which should always be upper-case - - // Read current object info for reference - icon = o->GetIcon(); - o->GetObjectData(&od); - - // We'll be a little more picky with property names, to prevent errors. Check against the whole word. - if (strcmp(sep->arg[3], "model") == 0) { - - if ((sep->arg[4][0] < 'A') || (sep->arg[4][0] > 'Z')) { - c->Message(Chat::White, "ERROR: Model names must begin with a letter."); - return; - } - - strn0cpy(od.object_name, sep->arg[4], sizeof(od.object_name)); - - o->SetObjectData(&od); - - c->Message(Chat::White, "Object %u now being rendered with model '%s'", id, od.object_name); - } else if (strcmp(sep->arg[3], "type") == 0) { - if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) { - c->Message(Chat::White, "ERROR: Invalid type number"); - return; - } - - od.object_type = atoi(sep->arg[4]); - - switch (od.object_type) { - case 0: - // Convert Static Object to temporary changeable type - od.object_type = staticType; - c->Message(Chat::White, "Note: Static Object will still act like tradeskill object and will not " - "reflect size, solidtype, or incline settings until committed to the " - "database with '#object Save', after which it will be unchangeable until " - "it is unlocked again with '#object Edit'."); - break; - - case OT_DROPPEDITEM: - c->Message(Chat::White, "ERROR: Object Type 1 is used for temporarily spawned ground spawns and " - "dropped items, which are not supported with #object. See the " - "'ground_spawns' table in the database."); - return; - - default: - c->Message(Chat::White, "Object %u changed to Tradeskill Object Type %u", id, od.object_type); - break; - } - - o->SetType(od.object_type); - } else if (strcmp(sep->arg[3], "size") == 0) { - if (od.object_type != staticType) { - c->Message( - 0, "ERROR: Object %u is not a Static Object and does not support the Size property", - id); - return; - } - - if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) { - c->Message(Chat::White, "ERROR: Invalid size specified. Please enter a number."); - return; - } - - od.size = atoi(sep->arg[4]); - o->SetObjectData(&od); - - if (od.size == 0) // 0 == unspecified == 100% - od.size = 100; - - c->Message(Chat::White, "Static Object %u set to %u%% size. Size will take effect when you commit to the " - "database with '#object Save', after which the object will be unchangeable until " - "you unlock it again with '#object Edit' and zone out and back in.", - id, od.size); - } else if (strcmp(sep->arg[3], "solidtype") == 0) { - - if (od.object_type != staticType) { - c->Message(Chat::White, "ERROR: Object %u is not a Static Object and does not support the " - "SolidType property", - id); - return; - } - - if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) { - c->Message(Chat::White, "ERROR: Invalid solidtype specified. Please enter a number."); - return; - } - - od.solidtype = atoi(sep->arg[4]); - o->SetObjectData(&od); - - c->Message(Chat::White, "Static Object %u set to SolidType %u. Change will take effect when you commit " - "to the database with '#object Save'. Support for this property is on a " - "per-model basis, mostly seen in smaller objects such as chests and tables.", - id, od.solidtype); - } else if (strcmp(sep->arg[3], "icon") == 0) { - - if ((od.object_type < 2) || (od.object_type == staticType)) { - c->Message(Chat::White, "ERROR: Object %u is not a Tradeskill Object and does not support the " - "Icon property", - id); - return; - } - - if ((icon = atoi(sep->arg[4])) == 0) { - c->Message(Chat::White, "ERROR: Invalid Icon specified. Please enter an icon number."); - return; - } - - o->SetIcon(icon); - c->Message(Chat::White, "Tradeskill Object %u icon set to %u", id, icon); - } else if (strcmp(sep->arg[3], "incline") == 0) { - if (od.object_type != staticType) { - c->Message( - 0, - "ERROR: Object %u is not a Static Object and does not support the Incline property", - id); - return; - } - - if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) { - c->Message( - 0, - "ERROR: Invalid Incline specified. Please enter a number. Normal range is 0-512."); - return; - } - - od.unknown020 = atoi(sep->arg[4]); - o->SetObjectData(&od); - - c->Message(Chat::White, "Static Object %u set to %u incline. Incline will take effect when you commit to " - "the database with '#object Save', after which the object will be unchangeable " - "until you unlock it again with '#object Edit' and zone out and back in.", - id, od.unknown020); - } else { - c->Message(Chat::White, "ERROR: Unrecognized property name: %s", sep->arg[3]); - return; - } - - // Repop object to have it reflect the change. - app = new EQApplicationPacket(); - o->CreateDeSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - - app = new EQApplicationPacket(); - o->CreateSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - return; - } - - if (strcasecmp(sep->arg[1], "move") == 0) { - - if ((sep->argnum < 2) || // Not enough arguments - ((id = atoi(sep->arg[2])) == 0) || // ID not specified - (((sep->arg[3][0] < '0') || (sep->arg[3][0] > '9')) && ((sep->arg[3][0] & 0xDF) != 'T') && - (sep->arg[3][0] != '-') && (sep->arg[3][0] != '.'))) { // Location argument not specified correctly - c->Message(Chat::White, "Usage: #object Move (ObjectID) ToMe|(x y z [h])"); - return; - } - - if (!(o = entity_list.FindObject(id))) { - std::string query = StringFormat("SELECT zoneid, version, type FROM object WHERE id = %u", id); - auto results = content_db.QueryDatabase(query); - if (!results.Success() || results.RowCount() == 0) { - c->Message(Chat::White, "ERROR: Object %u not found", id); - return; - } - - auto row = results.begin(); - od.zone_id = atoi(row[0]); - od.zone_instance = atoi(row[1]); - od.object_type = atoi(row[2]); - - if (od.zone_id != zone->GetZoneID()) { - c->Message(Chat::White, "ERROR: Object %u is not in this zone", id); - return; - } - - if (od.zone_instance != zone->GetInstanceVersion()) { - c->Message(Chat::White, "ERROR: Object %u is not in this instance version", id); - return; - } - - switch (od.object_type) { - case 0: - c->Message(Chat::White, "ERROR: Object %u is not yet unlocked for editing. Use '#object Edit' " - "then zone out and back in to move it.", - id); - return; - - case staticType: - c->Message(Chat::White, "ERROR: Object %u has been unlocked for editing, but you must zone out " - "and back in before your client sees the change and will allow you to " - "move it.", - id); - return; - - case 1: - c->Message(Chat::White, "ERROR: Object %u is a temporary spawned object and cannot be " - "manipulated with #object. See the 'ground_spawns' table in the " - "database.", - id); - return; - - default: - c->Message(Chat::White, "ERROR: Object %u not located in zone.", id); - return; - } - } - - // Move To Me - if ((sep->arg[3][0] & 0xDF) == 'T') { - od.x = c->GetX(); - od.y = c->GetY(); - od.z = c->GetZ() - - (c->GetSize() * - 0.625f); // Compensate for #loc bumping up Z coordinate by 62.5% of character's size. - - o->SetHeading(c->GetHeading()); - - // Bump player back to avoid getting stuck inside object - - x2 = 10.0f * std::sin(c->GetHeading() / 256.0f * 3.14159265f); - y2 = 10.0f * std::cos(c->GetHeading() / 256.0f * 3.14159265f); - c->MovePC(c->GetX() - x2, c->GetY() - y2, c->GetZ(), c->GetHeading()); - } // Move to x, y, z [h] - else { - od.x = atof(sep->arg[3]); - if (sep->argnum > 3) - od.y = atof(sep->arg[4]); - else - o->GetLocation(nullptr, &od.y, nullptr); - - if (sep->argnum > 4) - od.z = atof(sep->arg[5]); - else - o->GetLocation(nullptr, nullptr, &od.z); - - if (sep->argnum > 5) - o->SetHeading(atof(sep->arg[6])); - } - - o->SetLocation(od.x, od.y, od.z); - - // Despawn and respawn object to reflect change - app = new EQApplicationPacket(); - o->CreateDeSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - - app = new EQApplicationPacket(); - o->CreateSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - return; - } - - if (strcasecmp(sep->arg[1], "rotate") == 0) { - // Insufficient or invalid arguments - if ((sep->argnum < 3) || ((id = atoi(sep->arg[2])) == 0)) { - c->Message(Chat::White, "Usage: #object Rotate (ObjectID) (Heading, 0-512)"); - return; - } - - if ((o = entity_list.FindObject(id)) == nullptr) { - c->Message(Chat::White, "ERROR: Object %u not found in zone, or is a static object not yet unlocked with " - "'#object Edit' for editing.", - id); - return; - } - - o->SetHeading(atof(sep->arg[3])); - - // Despawn and respawn object to reflect change - app = new EQApplicationPacket(); - o->CreateDeSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - - app = new EQApplicationPacket(); - o->CreateSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - return; - } - - if (strcasecmp(sep->arg[1], "save") == 0) { - // Insufficient or invalid arguments - if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) == 0)) { - c->Message(Chat::White, "Usage: #object Save (ObjectID)"); - return; - } - - o = entity_list.FindObject(id); - - od.zone_id = 0; - od.zone_instance = 0; - od.object_type = 0; - - // If this ID isn't in the database yet, it's a new object - bNewObject = true; - std::string query = StringFormat("SELECT zoneid, version, type FROM object WHERE id = %u", id); - auto results = content_db.QueryDatabase(query); - if (results.Success() && results.RowCount() != 0) { - auto row = results.begin(); - od.zone_id = atoi(row[0]); - od.zone_instance = atoi(row[1]); - od.object_type = atoi(row[2]); - - // ID already in database. Not a new object. - bNewObject = false; - } - - if (!o) { - // Object not found in zone. Can't save an object we can't see. - - if (bNewObject) { - c->Message(Chat::White, "ERROR: Object %u not found", id); - return; - } - - if (od.zone_id != zone->GetZoneID()) { - c->Message(Chat::White, "ERROR: Wrong Object ID. %u is not part of this zone.", id); - return; - } - - if (od.zone_instance != zone->GetInstanceVersion()) { - c->Message(Chat::White, "ERROR: Wrong Object ID. %u is not part of this instance version.", id); - return; - } - - if (od.object_type == 0) { - c->Message(Chat::White, "ERROR: Static Object %u has already been committed. Use '#object Edit " - "%u' and zone out and back in to make changes.", - id, id); - return; - } - - if (od.object_type == 1) { - c->Message(Chat::White, "ERROR: Object %u is a temporarily spawned ground spawn or dropped item, " - "which is not supported with #object. See the 'ground_spawns' table in " - "the database.", - id); - return; - } - - c->Message(Chat::White, "ERROR: Object %u not found.", id); - return; - } - - // Oops! Another GM already saved an object with our id from another zone. - // We'll have to get a new one. - if ((od.zone_id > 0) && (od.zone_id != zone->GetZoneID())) - id = 0; - - // Oops! Another GM already saved an object with our id from another instance. - // We'll have to get a new one. - if ((id > 0) && (od.zone_instance != zone->GetInstanceVersion())) - id = 0; - - // If we're asking for a new ID, it's a new object. - bNewObject |= (id == 0); - - o->GetObjectData(&od); - od.object_type = o->GetType(); - icon = o->GetIcon(); - - // We're committing to the database now. Return temporary object type to actual. - if (od.object_type == staticType) - od.object_type = 0; - - if (!bNewObject) - query = StringFormat("UPDATE object SET zoneid = %u, version = %u, " - "xpos = %.1f, ypos=%.1f, zpos=%.1f, heading=%.1f, " - "objectname = '%s', type = %u, icon = %u, " - "unknown08 = %u, unknown10 = %u, unknown20 = %u " - "WHERE ID = %u", - zone->GetZoneID(), zone->GetInstanceVersion(), od.x, od.y, od.z, - od.heading, od.object_name, od.object_type, icon, od.size, - od.solidtype, od.unknown020, id); - else if (id == 0) - query = StringFormat("INSERT INTO object " - "(zoneid, version, xpos, ypos, zpos, heading, objectname, " - "type, icon, unknown08, unknown10, unknown20) " - "VALUES (%u, %u, %.1f, %.1f, %.1f, %.1f, '%s', %u, %u, %u, %u, %u)", - zone->GetZoneID(), zone->GetInstanceVersion(), od.x, od.y, od.z, - od.heading, od.object_name, od.object_type, icon, od.size, - od.solidtype, od.unknown020); - else - query = StringFormat("INSERT INTO object " - "(id, zoneid, version, xpos, ypos, zpos, heading, objectname, " - "type, icon, unknown08, unknown10, unknown20) " - "VALUES (%u, %u, %u, %.1f, %.1f, %.1f, %.1f, '%s', %u, %u, %u, %u, %u)", - id, zone->GetZoneID(), zone->GetInstanceVersion(), od.x, od.y, od.z, - od.heading, od.object_name, od.object_type, icon, od.size, - od.solidtype, od.unknown020); - - results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Database Error: %s", results.ErrorMessage().c_str()); - return; - } - - if (results.RowsAffected() == 0) { - // No change made, but no error message given - c->Message(Chat::White, "Database Error: Could not save change to Object %u", id); - return; - } - - if (bNewObject) { - if (newid == results.LastInsertedID()) { - c->Message(Chat::White, "Saved new Object %u to database", id); - return; - } - - c->Message(Chat::White, "Saved Object. NOTE: Database returned a new ID number for object: %u", newid); - id = newid; - return; - } - - c->Message(Chat::White, "Saved changes to Object %u", id); - newid = id; - - if (od.object_type == 0) { - // Static Object - Respawn as nonfunctional door - - app = new EQApplicationPacket(); - o->CreateDeSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - - entity_list.RemoveObject(o->GetID()); - - auto door = DoorsRepository::NewEntity(); - - door.zone = zone->GetShortName(); - - door.id = 1000000000 + id; // Out of range of normal use for doors.id - door.doorid = -1; // Client doesn't care if these are all the same door_id - door.pos_x = od.x; - door.pos_y = od.y; - door.pos_z = od.z; - door.heading = od.heading; - - door.name = od.object_name; - - // Strip trailing "_ACTORDEF" if present. Client won't accept it for doors. - int pos = door.name.size() - strlen("_ACTORDEF"); - if (pos > 0 && door.name.compare(pos, std::string::npos, "_ACTORDEF") == 0) - { - door.name.erase(pos); - } - - door.dest_zone = "NONE"; - - if ((door.size = od.size) == 0) // unknown08 = optional size percentage - door.size = 100; - - door.opentype = od.solidtype; - - switch (door.opentype) // unknown10 = optional request_nonsolid (0 or 1 or experimental number) - { - case 0: - door.opentype = 31; - break; - - case 1: - door.opentype = 9; - break; - } - - door.incline = od.unknown020; // unknown20 = optional incline value - door.client_version_mask = 0xFFFFFFFF; - - Doors* doors = new Doors(door); - - entity_list.AddDoor(doors); - - app = new EQApplicationPacket(OP_SpawnDoor, sizeof(Door_Struct)); - ds = (Door_Struct *)app->pBuffer; - - memset(ds, 0, sizeof(Door_Struct)); - memcpy(ds->name, door.name.c_str(), 32); - ds->xPos = door.pos_x; - ds->yPos = door.pos_y; - ds->zPos = door.pos_z; - ds->heading = door.heading; - ds->incline = door.incline; - ds->size = door.size; - ds->doorId = door.doorid; - ds->opentype = door.opentype; - ds->unknown0052[9] = 1; // *ptr-1 and *ptr-3 from EntityList::MakeDoorSpawnPacket() - ds->unknown0052[11] = 1; - - entity_list.QueueClients(0, app); - safe_delete(app); - - c->Message(Chat::White, "NOTE: Object %u is now a static object, and is unchangeable. To make future " - "changes, use '#object Edit' to convert it to a changeable form, then zone out " - "and back in.", - id); - } - return; - } - - if (strcasecmp(sep->arg[1], "copy") == 0) { - // Insufficient or invalid arguments - if ((sep->argnum < 3) || - (((sep->arg[2][0] & 0xDF) != 'A') && ((sep->arg[2][0] < '0') || (sep->arg[2][0] > '9')))) { - c->Message(Chat::White, "Usage: #object Copy All|(ObjectID) (InstanceVersion)"); - c->Message(Chat::White, "- Note: Only objects saved in the database can be copied to another instance."); - return; - } - - od.zone_instance = atoi(sep->arg[3]); - - if (od.zone_instance == zone->GetInstanceVersion()) { - c->Message(Chat::White, "ERROR: Source and destination instance versions are the same."); - return; - } - - if ((sep->arg[2][0] & 0xDF) == 'A') { - // Copy All - - std::string query = - StringFormat("INSERT INTO object " - "(zoneid, version, xpos, ypos, zpos, heading, itemid, " - "objectname, type, icon, unknown08, unknown10, unknown20) " - "SELECT zoneid, %u, xpos, ypos, zpos, heading, itemid, " - "objectname, type, icon, unknown08, unknown10, unknown20 " - "FROM object WHERE zoneid = %u) AND version = %u", - od.zone_instance, zone->GetZoneID(), zone->GetInstanceVersion()); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::White, "Database Error: %s", results.ErrorMessage().c_str()); - return; - } - - c->Message(Chat::White, "Copied %u object%s into instance version %u", results.RowCount(), - (results.RowCount() == 1) ? "" : "s", od.zone_instance); - return; - } - - id = atoi(sep->arg[2]); - - std::string query = StringFormat("INSERT INTO object " - "(zoneid, version, xpos, ypos, zpos, heading, itemid, " - "objectname, type, icon, unknown08, unknown10, unknown20) " - "SELECT zoneid, %u, xpos, ypos, zpos, heading, itemid, " - "objectname, type, icon, unknown08, unknown10, unknown20 " - "FROM object WHERE id = %u AND zoneid = %u AND version = %u", - od.zone_instance, id, zone->GetZoneID(), zone->GetInstanceVersion()); - auto results = content_db.QueryDatabase(query); - if (results.Success() && results.RowsAffected() > 0) { - c->Message(Chat::White, "Copied Object %u into instance version %u", id, od.zone_instance); - return; - } - - // Couldn't copy the object. - - // got an error message - if (!results.Success()) { - c->Message(Chat::White, "Database Error: %s", results.ErrorMessage().c_str()); - return; - } - - // No database error returned. See if we can figure out why. - - query = StringFormat("SELECT zoneid, version FROM object WHERE id = %u", id); - results = content_db.QueryDatabase(query); - if (!results.Success()) - return; - - if (results.RowCount() == 0) { - c->Message(Chat::White, "ERROR: Object %u not found", id); - return; - } - - auto row = results.begin(); - // Wrong ZoneID? - if (atoi(row[0]) != zone->GetZoneID()) { - c->Message(Chat::White, "ERROR: Object %u is not part of this zone.", id); - return; - } - - // Wrong Instance Version? - if (atoi(row[1]) != zone->GetInstanceVersion()) { - c->Message(Chat::White, "ERROR: Object %u is not part of this instance version.", id); - return; - } - - // Well, NO clue at this point. Just let 'em know something screwed up. - c->Message(Chat::White, "ERROR: Unknown database error copying Object %u to instance version %u", id, - od.zone_instance); - return; - } - - if (strcasecmp(sep->arg[1], "delete") == 0) { - - if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) <= 0)) { - c->Message(Chat::White, "Usage: #object Delete (ObjectID) -- NOTE: Object deletions are permanent and " - "cannot be undone!"); - return; - } - - o = entity_list.FindObject(id); - - if (o) { - // Object found in zone. - - app = new EQApplicationPacket(); - o->CreateDeSpawnPacket(app); - entity_list.QueueClients(nullptr, app); - - entity_list.RemoveObject(o->GetID()); - - // Verifying ZoneID and Version in case someone else ended up adding an object with our ID - // from a different zone/version. Don't want to delete someone else's work. - std::string query = StringFormat("DELETE FROM object " - "WHERE id = %u AND zoneid = %u " - "AND version = %u LIMIT 1", - id, zone->GetZoneID(), zone->GetInstanceVersion()); - auto results = content_db.QueryDatabase(query); - - c->Message(Chat::White, "Object %u deleted", id); - return; - } - - // Object not found in zone. - std::string query = StringFormat("SELECT type FROM object " - "WHERE id = %u AND zoneid = %u " - "AND version = %u LIMIT 1", - id, zone->GetZoneID(), zone->GetInstanceVersion()); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) - return; - - if (results.RowCount() == 0) { - c->Message(Chat::White, "ERROR: Object %u not found in this zone or instance!", id); - return; - } - - auto row = results.begin(); - - switch (atoi(row[0])) { - case 0: // Static Object - query = StringFormat("DELETE FROM object WHERE id = %u " - "AND zoneid = %u AND version = %u LIMIT 1", - id, zone->GetZoneID(), zone->GetInstanceVersion()); - results = content_db.QueryDatabase(query); - - c->Message(Chat::White, "Object %u deleted. NOTE: This static object will remain for anyone currently in " - "the zone until they next zone out and in.", - id); - return; - - case 1: // Temporary Spawn - c->Message(Chat::White, "ERROR: Object %u is a temporarily spawned ground spawn or dropped item, which " - "is not supported with #object. See the 'ground_spawns' table in the database.", - id); - return; - } - - return; - } - - if (strcasecmp(sep->arg[1], "undo") == 0) { - // Insufficient or invalid arguments - if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) == 0)) { - c->Message(Chat::White, "Usage: #object Undo (ObjectID) -- Reload object from database, undoing any " - "changes you have made"); - return; - } - - o = entity_list.FindObject(id); - - if (!o) { - c->Message(Chat::White, "ERROR: Object %u not found in zone in a manipulable form. No changes to undo.", - id); - return; - } - - if (o->GetType() == OT_DROPPEDITEM) { - c->Message(Chat::White, "ERROR: Object %u is a temporary spawned item and cannot be manipulated with " - "#object. See the 'ground_spawns' table in the database.", - id); - return; - } - - // Despawn current item for reloading from database - app = new EQApplicationPacket(); - o->CreateDeSpawnPacket(app); - entity_list.QueueClients(0, app); - entity_list.RemoveObject(o->GetID()); - safe_delete(app); - - std::string query = StringFormat("SELECT xpos, ypos, zpos, " - "heading, objectname, type, icon, " - "unknown08, unknown10, unknown20 " - "FROM object WHERE id = %u", - id); - auto results = content_db.QueryDatabase(query); - if (!results.Success() || results.RowCount() == 0) { - c->Message(Chat::White, "Database Error: %s", results.ErrorMessage().c_str()); - return; - } - - memset(&od, 0, sizeof(od)); - - auto row = results.begin(); - - od.x = atof(row[0]); - od.y = atof(row[1]); - od.z = atof(row[2]); - od.heading = atof(row[3]); - strn0cpy(od.object_name, row[4], sizeof(od.object_name)); - od.object_type = atoi(row[5]); - icon = atoi(row[6]); - od.size = atoi(row[7]); - od.solidtype = atoi(row[8]); - od.unknown020 = atoi(row[9]); - - if (od.object_type == 0) - od.object_type = staticType; - - o = new Object(id, od.object_type, icon, od, nullptr); - entity_list.AddObject(o, true); - - c->Message(Chat::White, "Object %u reloaded from database.", id); - return; - } - - c->Message(Chat::White, usage_string); -} - -void command_showspellslist(Client *c, const Seperator *sep) -{ - Mob *target = c->GetTarget(); - if (!target || !target->IsNPC()) { - c->Message(Chat::White, "You must target an NPC to use this command."); - return; - } - target->CastToNPC()->AISpellsList(c); - return; -} - -void command_raidloot(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - if (!arguments) { - c->Message(Chat::White, "Usage: #raidloot [All|GroupLeader|RaidLeader|Selected]"); - return; - } - - auto client_raid = c->GetRaid(); - if (!client_raid) { - c->Message(Chat::White, "You must be in a Raid to use this command."); - return; - } - - if (!client_raid->IsLeader(c)) { - c->Message(Chat::White, "You must be the Raid Leader to use this command."); - return; - } - - std::string raid_loot_type = str_tolower(sep->arg[1]); - bool is_all = raid_loot_type.find("all") != std::string::npos; - bool is_group_leader = raid_loot_type.find("groupleader") != std::string::npos; - bool is_raid_leader = raid_loot_type.find("raidleader") != std::string::npos; - bool is_selected = raid_loot_type.find("selected") != std::string::npos; - if ( - !is_all && - !is_group_leader && - !is_raid_leader && - !is_selected - ) { - c->Message(Chat::White, "Usage: #raidloot [All|GroupLeader|RaidLeader|Selected]"); - return; - } - - std::map loot_types = { - { RaidLootTypes::All, "All" }, - { RaidLootTypes::GroupLeader, "GroupLeader" }, - { RaidLootTypes::RaidLeader, "RaidLeader" }, - { RaidLootTypes::Selected, "Selected" } - }; - - uint32 loot_type; - if (is_all) { - loot_type = RaidLootTypes::All; - } else if (is_group_leader) { - loot_type = RaidLootTypes::GroupLeader; - } else if (is_raid_leader) { - loot_type = RaidLootTypes::RaidLeader; - } else if (is_selected) { - loot_type = RaidLootTypes::Selected; - } - - c->Message( - Chat::White, - fmt::format( - "Loot type changed to {} ({}).", - loot_types[loot_type], - loot_type - ).c_str() - ); -} - -void command_emoteview(Client *c, const Seperator *sep) -{ - if(!c->GetTarget() || !c->GetTarget()->IsNPC()) - { - c->Message(Chat::White, "You must target a NPC to view their emotes."); - return; - } - - if(c->GetTarget() && c->GetTarget()->IsNPC()) - { - int count=0; - int emoteid = c->GetTarget()->CastToNPC()->GetEmoteID(); - - LinkedListIterator iterator(zone->NPCEmoteList); - iterator.Reset(); - while(iterator.MoreElements()) - { - NPC_Emote_Struct* nes = iterator.GetData(); - if(emoteid == nes->emoteid) - { - c->Message(Chat::White, "EmoteID: %i Event: %i Type: %i Text: %s", nes->emoteid, nes->event_, nes->type, nes->text); - count++; - } - iterator.Advance(); - } - if (count == 0) - c->Message(Chat::White, "No emotes found."); - else - c->Message(Chat::White, "%i emote(s) found", count); - } -} - -void command_emotesearch(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0] == 0) - c->Message(Chat::White, "Usage: #emotesearch [search string or emoteid]"); - else - { - const char *search_criteria=sep->argplus[1]; - int count=0; - - if (Seperator::IsNumber(search_criteria)) - { - uint16 emoteid = atoi(search_criteria); - LinkedListIterator iterator(zone->NPCEmoteList); - iterator.Reset(); - while(iterator.MoreElements()) - { - NPC_Emote_Struct* nes = iterator.GetData(); - if(emoteid == nes->emoteid) - { - c->Message(Chat::White, "EmoteID: %i Event: %i Type: %i Text: %s", nes->emoteid, nes->event_, nes->type, nes->text); - count++; - } - iterator.Advance(); - } - if (count == 0) - c->Message(Chat::White, "No emotes found."); - else - c->Message(Chat::White, "%i emote(s) found", count); - } - else - { - char sText[64]; - char sCriteria[515]; - strn0cpy(sCriteria, search_criteria, sizeof(sCriteria)); - strupr(sCriteria); - char* pdest; - - LinkedListIterator iterator(zone->NPCEmoteList); - iterator.Reset(); - while(iterator.MoreElements()) - { - NPC_Emote_Struct* nes = iterator.GetData(); - strn0cpy(sText, nes->text, sizeof(sText)); - strupr(sText); - pdest = strstr(sText, sCriteria); - if (pdest != nullptr) - { - c->Message(Chat::White, "EmoteID: %i Event: %i Type: %i Text: %s", nes->emoteid, nes->event_, nes->type, nes->text); - count++; - } - if (count == 50) - break; - - iterator.Advance(); - } - if (count == 50) - c->Message(Chat::White, "50 emotes shown...too many results."); - else - c->Message(Chat::White, "%i emote(s) found", count); - } - } -} - -void command_reloademote(Client *c, const Seperator *sep) -{ - zone->NPCEmoteList.Clear(); - zone->LoadNPCEmotes(&zone->NPCEmoteList); - c->Message(Chat::White, "NPC emotes reloaded."); -} - -void command_globalview(Client *c, const Seperator *sep) -{ - NPC * npcmob = nullptr; - - if(c->GetTarget() && c->GetTarget()->IsNPC()) - { - npcmob = c->GetTarget()->CastToNPC(); - QGlobalCache *npc_c = nullptr; - QGlobalCache *char_c = nullptr; - QGlobalCache *zone_c = nullptr; - - if(npcmob) - npc_c = npcmob->GetQGlobals(); - - char_c = c->GetQGlobals(); - zone_c = zone->GetQGlobals(); - - std::list globalMap; - uint32 ntype = 0; - - if(npcmob) - ntype = npcmob->GetNPCTypeID(); - - if(npc_c) - { - QGlobalCache::Combine(globalMap, npc_c->GetBucket(), ntype, c->CharacterID(), zone->GetZoneID()); - } - - if(char_c) - { - QGlobalCache::Combine(globalMap, char_c->GetBucket(), ntype, c->CharacterID(), zone->GetZoneID()); - } - - if(zone_c) - { - QGlobalCache::Combine(globalMap, zone_c->GetBucket(), ntype, c->CharacterID(), zone->GetZoneID()); - } - - auto iter = globalMap.begin(); - uint32 gcount = 0; - - c->Message(Chat::White, "Name, Value"); - while(iter != globalMap.end()) - { - c->Message(Chat::White, "%s %s", (*iter).name.c_str(), (*iter).value.c_str()); - ++iter; - ++gcount; - } - c->Message(Chat::White, "%u globals loaded.", gcount); - } - else - { - QGlobalCache *char_c = nullptr; - QGlobalCache *zone_c = nullptr; - - char_c = c->GetQGlobals(); - zone_c = zone->GetQGlobals(); - - std::list globalMap; - uint32 ntype = 0; - - if(char_c) - { - QGlobalCache::Combine(globalMap, char_c->GetBucket(), ntype, c->CharacterID(), zone->GetZoneID()); - } - - if(zone_c) - { - QGlobalCache::Combine(globalMap, zone_c->GetBucket(), ntype, c->CharacterID(), zone->GetZoneID()); - } - - auto iter = globalMap.begin(); - uint32 gcount = 0; - - c->Message(Chat::White, "Name, Value"); - while(iter != globalMap.end()) - { - c->Message(Chat::White, "%s %s", (*iter).name.c_str(), (*iter).value.c_str()); - ++iter; - ++gcount; - } - c->Message(Chat::White, "%u globals loaded.", gcount); - } -} - -void command_distance(Client *c, const Seperator *sep) { - if (c->GetTarget()) { - Mob* target = c->GetTarget(); - if (c != target) { - c->Message( - Chat::White, - fmt::format( - "{} ({}) is {:.2f} units from you.", - target->GetCleanName(), - target->GetID(), - Distance( - c->GetPosition(), - target->GetPosition() - ) - ).c_str() - ); - } - } -} - -void command_door(Client *c, const Seperator *sep) { - DoorManipulation::CommandHandler(c, sep); -} - -void command_cvs(Client *c, const Seperator *sep) -{ - auto pack = new ServerPacket( - ServerOP_ClientVersionSummary, - sizeof(ServerRequestClientVersionSummary_Struct) - ); - auto srcvss = (ServerRequestClientVersionSummary_Struct*)pack->pBuffer; - strn0cpy(srcvss->Name, c->GetName(), sizeof(srcvss->Name)); - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void command_max_all_skills(Client *c, const Seperator *sep) -{ - if(c) { - Client* client_target = (c->GetTarget() ? (c->GetTarget()->IsClient() ? c->GetTarget()->CastToClient() : c) : c); - auto Skills = EQ::skills::GetSkillTypeMap(); - for (auto& skills_iter : Skills) { - auto skill_id = skills_iter.first; - auto current_skill_value = ( - (EQ::skills::IsSpecializedSkill(skill_id)) ? - 50 : - content_db.GetSkillCap(client_target->GetClass(), skill_id, client_target->GetLevel()) - ); - client_target->SetSkill(skill_id, current_skill_value); - } - } -} - -void command_showbonusstats(Client *c, const Seperator *sep) -{ - if (c->GetTarget() == 0) - c->Message(Chat::White, "ERROR: No target!"); - else if (!c->GetTarget()->IsMob() && !c->GetTarget()->IsClient()) - c->Message(Chat::White, "ERROR: Target is not a Mob or Player!"); - else { - bool bAll = false; - if(sep->arg[1][0] == '\0' || strcasecmp(sep->arg[1], "all") == 0) - bAll = true; - if (bAll || (strcasecmp(sep->arg[1], "item")==0)) { - c->Message(Chat::White, "Target Item Bonuses:"); - c->Message(Chat::White, " Accuracy: %i%% Divine Save: %i%%", c->GetTarget()->GetItemBonuses().Accuracy, c->GetTarget()->GetItemBonuses().DivineSaveChance); - c->Message(Chat::White, " Flurry: %i%% HitChance: %i%%", c->GetTarget()->GetItemBonuses().FlurryChance, c->GetTarget()->GetItemBonuses().HitChance / 15); - } - if (bAll || (strcasecmp(sep->arg[1], "spell")==0)) { - c->Message(Chat::White, " Target Spell Bonuses:"); - c->Message(Chat::White, " Accuracy: %i%% Divine Save: %i%%", c->GetTarget()->GetSpellBonuses().Accuracy, c->GetTarget()->GetSpellBonuses().DivineSaveChance); - c->Message(Chat::White, " Flurry: %i%% HitChance: %i%% ", c->GetTarget()->GetSpellBonuses().FlurryChance, c->GetTarget()->GetSpellBonuses().HitChance / 15); - } - c->Message(Chat::White, " Effective Casting Level: %i", c->GetTarget()->GetCasterLevel(0)); - } -} - -void command_reloadallrules(Client *c, const Seperator *sep) -{ - if(c) - { - auto pack = new ServerPacket(ServerOP_ReloadRules, 0); - worldserver.SendPacket(pack); - c->Message(Chat::Red, "Successfully sent the packet to world to reload rules globally. (including world)"); - safe_delete(pack); - - } -} - -void command_reloadworldrules(Client *c, const Seperator *sep) -{ - if(c) - { - auto pack = new ServerPacket(ServerOP_ReloadRulesWorld, 0); - worldserver.SendPacket(pack); - c->Message(Chat::Red, "Successfully sent the packet to world to reload rules. (only world)"); - safe_delete(pack); - } -} - -void command_camerashake(Client *c, const Seperator *sep) -{ - if(c) - { - if(sep->arg[1][0] && sep->arg[2][0]) - { - auto pack = new ServerPacket(ServerOP_CameraShake, sizeof(ServerCameraShake_Struct)); - ServerCameraShake_Struct* scss = (ServerCameraShake_Struct*) pack->pBuffer; - scss->duration = atoi(sep->arg[1]); - scss->intensity = atoi(sep->arg[2]); - worldserver.SendPacket(pack); - c->Message(Chat::Red, "Successfully sent the packet to world! Shake it, world, shake it!"); - safe_delete(pack); - } - else { - c->Message(Chat::Red, "Usage -- #camerashake [duration], [intensity [1-10])"); - } - } - return; -} - -void command_disarmtrap(Client *c, const Seperator *sep) -{ - Mob *target = c->GetTarget(); - - if(!target) - { - c->Message(Chat::Red, "You must have a target."); - return; - } - - if(target->IsNPC()) - { - if (c->HasSkill(EQ::skills::SkillDisarmTraps)) - { - if(DistanceSquaredNoZ(c->GetPosition(), target->GetPosition()) > RuleI(Adventure, LDoNTrapDistanceUse)) - { - c->Message(Chat::Red, "%s is too far away.", target->GetCleanName()); - return; - } - c->HandleLDoNDisarm(target->CastToNPC(), c->GetSkill(EQ::skills::SkillDisarmTraps), LDoNTypeMechanical); - } - else - c->Message(Chat::Red, "You do not have the disarm trap skill."); - } -} - -void command_sensetrap(Client *c, const Seperator *sep) -{ - Mob * target = c->GetTarget(); - if(!target) - { - c->Message(Chat::Red, "You must have a target."); - return; - } - - if(target->IsNPC()) - { - if (c->HasSkill(EQ::skills::SkillSenseTraps)) - { - if(DistanceSquaredNoZ(c->GetPosition(), target->GetPosition()) > RuleI(Adventure, LDoNTrapDistanceUse)) - { - c->Message(Chat::Red, "%s is too far away.", target->GetCleanName()); - return; - } - c->HandleLDoNSenseTraps(target->CastToNPC(), c->GetSkill(EQ::skills::SkillSenseTraps), LDoNTypeMechanical); - } - else - c->Message(Chat::Red, "You do not have the sense traps skill."); - } -} - -void command_picklock(Client *c, const Seperator *sep) -{ - Mob * target = c->GetTarget(); - if(!target) - { - c->Message(Chat::Red, "You must have a target."); - return; - } - - if(target->IsNPC()) - { - if (c->HasSkill(EQ::skills::SkillPickLock)) - { - if(DistanceSquaredNoZ(c->GetPosition(), target->GetPosition()) > RuleI(Adventure, LDoNTrapDistanceUse)) - { - c->Message(Chat::Red, "%s is too far away.", target->GetCleanName()); - return; - } - c->HandleLDoNPickLock(target->CastToNPC(), c->GetSkill(EQ::skills::SkillPickLock), LDoNTypeMechanical); - } - else - c->Message(Chat::Red, "You do not have the pick locks skill."); - } -} - -void command_profanity(Client *c, const Seperator *sep) -{ - std::string arg1(sep->arg[1]); - - while (true) { - if (arg1.compare("list") == 0) { - // do nothing - } - else if (arg1.compare("clear") == 0) { - EQ::ProfanityManager::DeleteProfanityList(&database); - auto pack = new ServerPacket(ServerOP_RefreshCensorship); - worldserver.SendPacket(pack); - safe_delete(pack); - } - else if (arg1.compare("add") == 0) { - if (!EQ::ProfanityManager::AddProfanity(&database, sep->arg[2])) - c->Message(Chat::Red, "Could not add '%s' to the profanity list.", sep->arg[2]); - auto pack = new ServerPacket(ServerOP_RefreshCensorship); - worldserver.SendPacket(pack); - safe_delete(pack); - } - else if (arg1.compare("del") == 0) { - if (!EQ::ProfanityManager::RemoveProfanity(&database, sep->arg[2])) - c->Message(Chat::Red, "Could not delete '%s' from the profanity list.", sep->arg[2]); - auto pack = new ServerPacket(ServerOP_RefreshCensorship); - worldserver.SendPacket(pack); - safe_delete(pack); - } - else if (arg1.compare("reload") == 0) { - if (!EQ::ProfanityManager::UpdateProfanityList(&database)) - c->Message(Chat::Red, "Could not reload the profanity list."); - auto pack = new ServerPacket(ServerOP_RefreshCensorship); - worldserver.SendPacket(pack); - safe_delete(pack); - } - else { - break; - } - - std::string popup; - const auto &list = EQ::ProfanityManager::GetProfanityList(); - for (const auto &iter : list) { - popup.append(iter); - popup.append("
"); - } - if (list.empty()) - popup.append("** Censorship Inactive **
"); - else - popup.append("** End of List **
"); - - c->SendPopupToClient("Profanity List", popup.c_str()); - - return; - } - - c->Message(Chat::White, "Usage: #profanity [list] - shows profanity list"); - c->Message(Chat::White, "Usage: #profanity [clear] - deletes all entries"); - c->Message(Chat::White, "Usage: #profanity [add] [] - adds entry"); - c->Message(Chat::White, "Usage: #profanity [del] [] - deletes entry"); - c->Message(Chat::White, "Usage: #profanity [reload] - reloads profanity list"); -} - -void command_mysql(Client *c, const Seperator *sep) -{ - if(!sep->arg[1][0] || !sep->arg[2][0]) { - c->Message(Chat::White, "Usage: #mysql query \"Query here\""); - return; - } - - if (strcasecmp(sep->arg[1], "help") == 0) { - c->Message(Chat::White, "MYSQL In-Game CLI Interface:"); - c->Message(Chat::White, "Example: #mysql query \"Query goes here quoted\" -s -h"); - c->Message(Chat::White, "To use 'like \"%%something%%\" replace the %% with #"); - c->Message(Chat::White, "Example: #mysql query \"select * from table where name like \"#something#\""); - c->Message(Chat::White, "-s - Spaces select entries apart"); - c->Message(Chat::White, "-h - Colors every other select result"); - return; - } - - if (strcasecmp(sep->arg[1], "query") == 0) { - ///Parse switches here - int argnum = 3; - bool optionS = false; - bool optionH = false; - while(sep->arg[argnum] && strlen(sep->arg[argnum]) > 1){ - switch(sep->arg[argnum][1]){ - case 's': optionS = true; break; - case 'h': optionH = true; break; - default: - c->Message(Chat::Yellow, "%s, there is no option '%c'", c->GetName(), sep->arg[argnum][1]); - return; - } - ++argnum; - } - - int highlightTextIndex = 0; - std::string query(sep->arg[2]); - //swap # for % so like queries can work - std::replace(query.begin(), query.end(), '#', '%'); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - return; - } - - //Using sep->arg[2] again, replace # with %% so it doesn't screw up when sent through vsnprintf in Message - query = sep->arg[2]; - int pos = query.find('#'); - while(pos != std::string::npos) { - query.erase(pos,1); - query.insert(pos, "%%"); - pos = query.find('#'); - } - c->Message(Chat::Yellow, "---Running query: '%s'", query.c_str()); - - for (auto row = results.begin(); row != results.end(); ++row) { - std::stringstream lineText; - std::vector lineVec; - for(int i = 0; i < results.RowCount(); i++) { - //split lines that could overflow the buffer in Client::Message and get cut off - //This will crash MQ2 @ 4000 since their internal buffer is only 2048. - //Reducing it to 2000 fixes that but splits more results from tables with a lot of columns. - if(lineText.str().length() > 4000) { - lineVec.push_back(lineText.str()); - lineText.str(""); - } - lineText << results.FieldName(i) << ":" << "[" << (row[i] ? row[i] : "nullptr") << "] "; - } - - lineVec.push_back(lineText.str()); - - if(optionS) //This provides spacing for the space switch - c->Message(Chat::White, " "); - if(optionH) //This option will highlight every other row - highlightTextIndex = 1 - highlightTextIndex; - - for(int lineNum = 0; lineNum < lineVec.size(); ++lineNum) - c->Message(highlightTextIndex, lineVec[lineNum].c_str()); - } - } -} - -void command_xtargets(Client *c, const Seperator *sep) -{ - Client *t; - - if(c->GetTarget() && c->GetTarget()->IsClient()) - t = c->GetTarget()->CastToClient(); - else - t = c; - - if(sep->arg[1][0]) - { - uint8 NewMax = atoi(sep->arg[1]); - - if((NewMax < 5) || (NewMax > XTARGET_HARDCAP)) - { - c->Message(Chat::Red, "Number of XTargets must be between 5 and %i", XTARGET_HARDCAP); - return; - } - t->SetMaxXTargets(NewMax); - c->Message(Chat::White, "Max number of XTargets set to %i", NewMax); - } - else - t->ShowXTargets(c); -} - -void command_zopp(Client *c, const Seperator *sep) -{ // - Owner only command..non-targetable to eliminate malicious or mischievious activities. - if (!c) - return; - else if (sep->argnum < 3 || sep->argnum > 4) - c->Message(Chat::White, "Usage: #zopp [trade/summon] [slot id] [item id] [*charges]"); - else if (!strcasecmp(sep->arg[1], "trade") == 0 && !strcasecmp(sep->arg[1], "t") == 0 && !strcasecmp(sep->arg[1], "summon") == 0 && !strcasecmp(sep->arg[1], "s") == 0) - c->Message(Chat::White, "Usage: #zopp [trade/summon] [slot id] [item id] [*charges]"); - else if (!sep->IsNumber(2) || !sep->IsNumber(3) || (sep->argnum == 4 && !sep->IsNumber(4))) - c->Message(Chat::White, "Usage: #zopp [trade/summon] [slot id] [item id] [*charges]"); - else { - ItemPacketType packettype; - - if (strcasecmp(sep->arg[1], "trade") == 0 || strcasecmp(sep->arg[1], "t") == 0) { - packettype = ItemPacketTrade; - } - else { - packettype = ItemPacketLimbo; - } - - int16 slotid = atoi(sep->arg[2]); - uint32 itemid = atoi(sep->arg[3]); - int16 charges = sep->argnum == 4 ? atoi(sep->arg[4]) : 1; // defaults to 1 charge if not specified - - const EQ::ItemData* FakeItem = database.GetItem(itemid); - - if (!FakeItem) { - c->Message(Chat::Red, "Error: Item [%u] is not a valid item id.", itemid); - return; - } - - int16 item_status = 0; - const EQ::ItemData* item = database.GetItem(itemid); - if(item) { - item_status = static_cast(item->MinStatus); - } - if (item_status > c->Admin()) { - c->Message(Chat::Red, "Error: Insufficient status to use this command."); - return; - } - - if (charges < 0 || charges > FakeItem->StackSize) { - c->Message(Chat::Red, "Warning: The specified charge count does not meet expected criteria!"); - c->Message(Chat::White, "Processing request..results may cause unpredictable behavior."); - } - - EQ::ItemInstance* FakeItemInst = database.CreateItem(FakeItem, charges); - c->SendItemPacket(slotid, FakeItemInst, packettype); - c->Message(Chat::White, "Sending zephyr op packet to client - [%s] %s (%u) with %i %s to slot %i.", - packettype == ItemPacketTrade ? "Trade" : "Summon", FakeItem->Name, itemid, charges, - std::abs(charges == 1) ? "charge" : "charges", slotid); - safe_delete(FakeItemInst); - } -} - -void command_augmentitem(Client *c, const Seperator *sep) -{ - if (!c) - return; - - auto in_augment = new AugmentItem_Struct[sizeof(AugmentItem_Struct)]; - in_augment->container_slot = 1000; // - in_augment->augment_slot = -1; - if (c->GetTradeskillObject() != nullptr) - Object::HandleAugmentation(c, in_augment, c->GetTradeskillObject()); - safe_delete_array(in_augment); -} - -void command_questerrors(Client *c, const Seperator *sep) -{ - std::list err; - parse->GetErrors(err); - c->Message(Chat::White, "Current Quest Errors:"); - - auto iter = err.begin(); - int i = 0; - while(iter != err.end()) { - if(i >= 30) { - c->Message(Chat::White, "Maximum of 30 Errors shown..."); - break; - } - - c->Message(Chat::White, iter->c_str()); - ++i; - ++iter; - } -} - -void command_enablerecipe(Client *c, const Seperator *sep) -{ - uint32 recipe_id = 0; - bool success = false; - if (c) { - if (sep->argnum == 1) { - recipe_id = atoi(sep->arg[1]); - } - else { - c->Message(Chat::White, "Invalid number of arguments.\nUsage: #enablerecipe recipe_id"); - return; - } - if (recipe_id > 0) { - success = content_db.EnableRecipe(recipe_id); - if (success) { - c->Message(Chat::White, "Recipe enabled."); - } - else { - c->Message(Chat::White, "Recipe not enabled."); - } - } - else { - c->Message(Chat::White, "Invalid recipe id.\nUsage: #enablerecipe recipe_id"); - } - } -} - -void command_disablerecipe(Client *c, const Seperator *sep) -{ - uint32 recipe_id = 0; - bool success = false; - if (c) { - if (sep->argnum == 1) { - recipe_id = atoi(sep->arg[1]); - } - else { - c->Message(Chat::White, "Invalid number of arguments.\nUsage: #disablerecipe recipe_id"); - return; - } - if (recipe_id > 0) { - success = content_db.DisableRecipe(recipe_id); - if (success) { - c->Message(Chat::White, "Recipe disabled."); - } - else { - c->Message(Chat::White, "Recipe not disabled."); - } - } - else { - c->Message(Chat::White, "Invalid recipe id.\nUsage: #disablerecipe recipe_id"); - } - } -} - -void command_npctype_cache(Client *c, const Seperator *sep) -{ - if (sep->argnum > 0) { - for (int i = 0; i < sep->argnum; ++i) { - if (strcasecmp(sep->arg[i + 1], "all") == 0) { - c->Message(Chat::White, "Clearing all npc types from the cache."); - zone->ClearNPCTypeCache(-1); - } - else { - int id = atoi(sep->arg[i + 1]); - if (id > 0) { - c->Message(Chat::White, "Clearing npc type %d from the cache.", id); - zone->ClearNPCTypeCache(id); - return; - } - } - } - } - else { - c->Message(Chat::White, "Usage:"); - c->Message(Chat::White, "#npctype_cache [npctype_id] ..."); - c->Message(Chat::White, "#npctype_cache all"); - } -} - -void command_merchantopenshop(Client *c, const Seperator *sep) -{ - Mob *merchant = c->GetTarget(); - if (!merchant || merchant->GetClass() != MERCHANT) { - c->Message(Chat::White, "You must target a merchant to open their shop."); - return; - } - - merchant->CastToNPC()->MerchantOpenShop(); -} - -void command_merchantcloseshop(Client *c, const Seperator *sep) -{ - Mob *merchant = c->GetTarget(); - if (!merchant || merchant->GetClass() != MERCHANT) { - c->Message(Chat::White, "You must target a merchant to close their shop."); - return; - } - - merchant->CastToNPC()->MerchantCloseShop(); -} - -void command_shownumhits(Client *c, const Seperator *sep) -{ - c->ShowNumHits(); - return; -} - -void command_shownpcgloballoot(Client *c, const Seperator *sep) -{ - auto tar = c->GetTarget(); - - if (!tar || !tar->IsNPC()) { - c->Message(Chat::White, "You must target an NPC to use this command."); - return; - } - - auto npc = tar->CastToNPC(); - c->Message(Chat::White, "GlobalLoot for %s (%d)", npc->GetName(), npc->GetNPCTypeID()); - zone->ShowNPCGlobalLoot(c, npc); -} - -void command_tune(Client *c, const Seperator *sep) -{ - //Work in progress - Kayen - - if (sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { - c->Message(Chat::White, "Syntax: #tune [subcommand]."); - c->Message(Chat::White, "-- Tune System Commands --"); - c->Message(Chat::White, "-- Usage: Returns recommended combat statistical values based on a desired outcome through simulated combat."); - c->Message(Chat::White, "-- This commmand can answer the following difficult questions whening tunings NPCs and Players."); - c->Message(Chat::White, "-- Question: What is the average damage mitigation my AC provides against a specific targets attacks?"); - c->Message(Chat::White, "-- Question: What is amount of AC would I need to add to acheive a specific average damage mitigation agianst specific targets attacks?"); - c->Message(Chat::White, "-- Question: What is amount of AC would I need to add to my target to acheive a specific average damage mitigation from my attacks?"); - c->Message(Chat::White, "-- Question: What is my targets average AC damage mitigation based on my ATK stat?"); - c->Message(Chat::White, "-- Question: What is amount of ATK would I need to add to myself to acheive a specific average damage mitigation on my target?"); - c->Message(Chat::White, "-- Question: What is amount of ATK would I need to add to my target to acheive a specific average AC damage mitigation on myself?"); - c->Message(Chat::White, "-- Question: What is my hit chance against a target?"); - c->Message(Chat::White, "-- Question: What is the amount of avoidance I need to add to my target to achieve a specific hit chance?"); - c->Message(Chat::White, "-- Question: What is the amount of accuracy I need to add to my target to achieve a specific chance of hitting me?"); - c->Message(Chat::White, "-- Question: ... and many more..."); - c->Message(Chat::White, " "); - c->Message(Chat::White, "...#tune stats [A/D]"); - c->Message(Chat::White, "...#tune FindATK [A/D] [pct mitigation] [interval] [loop_max] [AC override] [Info Level]"); - c->Message(Chat::White, "...#tune FindAC [A/D] [pct mitigation] [interval] [loop_max] [ATK override] [Info Level] "); - c->Message(Chat::White, "...#tune FindAccuracy [A/D] [hit chance] [interval] [loop_max] [Avoidance override] [Info Level]"); - c->Message(Chat::White, "...#tune FindAvoidance [A/D] [hit chance] [interval] [loop_max] [Accuracy override] [Info Level] "); - c->Message(Chat::White, " "); - c->Message(Chat::White, "-- DETAILS AND EXAMPLES ON USAGE"); - c->Message(Chat::White, " "); - c->Message(Chat::White, "...Returns combat statistics, including AC mitigation pct, hit chance, and avoid melee chance for attacker and defender."); - c->Message(Chat::White, "...#tune stats [A/D]"); - c->Message(Chat::White, "..."); - c->Message(Chat::White, "...Returns recommended ATK adjustment (+/-) on ATTACKER that will result in a specific average AC mitigation pct on DEFENDER. "); - c->Message(Chat::White, "...#tune FindATK [A/D] [pct mitigation] [interval][loop_max][AC override][Info Level]"); - c->Message(Chat::White, "...Example: Find the amount of ATK stat I need to add to the targeted NPC so that it hits me for 50 pct damage on average."); - c->Message(Chat::White, "...Example: #tune FindATK D 50"); - c->Message(Chat::White, "..."); - c->Message(Chat::White, "...Returns recommended AC adjustment(+/-) on DEFENDER for a specific average AC mitigation pct from ATTACKER. "); - c->Message(Chat::White, "...#tune FindAC [A/D] [pct mitigation] [interval][loop_max][ATK override][Info Level] "); - c->Message(Chat::White, "...Example: Find the amount of AC stat I need to add to the targeted NPC so that I hit it for 70 pct damage on average."); - c->Message(Chat::White, "...Example: #tune FindAC D 70"); - c->Message(Chat::White, "..."); - c->Message(Chat::White, "...Returns recommended Accuracy adjustment (+/-) on ATTACKER that will result in a specific hit chance pct on DEFENDER. "); - c->Message(Chat::White, "...#tune FindAccuracy [A/D] [hit chance] [interval][loop_max][Avoidance override][Info Level]"); - c->Message(Chat::White, "...Example: Find the amount of Accuracy stat I need to add to the targeted NPC so that it has a 60 pct hit chance against me."); - c->Message(Chat::White, "...Example: #tune FindAccuracy D 60"); - c->Message(Chat::White, "..."); - c->Message(Chat::White, "...Returns recommended Avoidance adjustment (+/-) on DEFENDER for in a specific hit chance pct from ATTACKER. "); - c->Message(Chat::White, "...#tune FindAvoidance [A/D] [hit chance] [interval][loop_max][Accuracy override][Info Level] "); - c->Message(Chat::White, "...Example: Find the amount of Avoidance stat I need to add to the targeted NPC so that I have a 30 pct hit chance against it."); - c->Message(Chat::White, "...Example: #tune FindAvoidance D 30"); - c->Message(Chat::White, "... "); - c->Message(Chat::White, "...Usage: [A/D] You must input either A or D."); - c->Message(Chat::White, "...Category [A] : YOU are the ATTACKER. YOUR TARGET is the DEFENDER."); - c->Message(Chat::White, "...Category [D] : YOU are the DEFENDER. YOUR TARGET is the ATTACKER."); - c->Message(Chat::White, "...If TARGET is in combat, DEFENDER is the TARGETs TARGET."); - - c->Message(Chat::White, " "); - - c->Message(Chat::White, "-- Warning: The calculations done in this process are intense and can potentially cause zone crashes depending on parameters set, use with caution!"); - c->Message(Chat::White, "-- Below are OPTIONAL parameters."); - c->Message(Chat::White, "-- Note: [interval] Determines how much the stat being checked increases/decreases till it finds the best result. Lower is more accurate. Default=10"); - c->Message(Chat::White, "-- Note: [loop_max] Determines how many iterations are done to increases/decreases the stat till it finds the best result. Higher is more accurate. Default=1000"); - c->Message(Chat::White, "-- Note: [Stat Override] Will override that stat on mob being checked with the specified value. Default=0"); - c->Message(Chat::White, "-- Example: If as the attacker you want to find the ATK value you would need to have agianst a target with 1000 AC to achieve an average AC mitigation of 50 pct."); - c->Message(Chat::White, "-- Example: #tune FindATK A 50 0 0 1000"); - c->Message(Chat::White, "-- Note: [Info Level] How much parsing detail is displayed[0 - 1]. Default: [0] "); - c->Message(Chat::White, " "); - - return; - } - /* - Category A: YOU are the attacker and your target is the defender - Category D: YOU are the defender and your target is the attacker - */ - - Mob* attacker = c; - Mob* defender = c->GetTarget(); - - if (!defender) - { - c->Message(Chat::White, "[#Tune] - Error no target selected. [#Tune help]"); - return; - } - - //Use if checkings on engaged targets. - Mob* ttarget = attacker->GetTarget(); - if (ttarget) { - defender = ttarget; - } - - if (!strcasecmp(sep->arg[1], "stats")) - { - - if (!strcasecmp(sep->arg[2], "A")) { - c->TuneGetStats(defender, attacker); - } - else if (!strcasecmp(sep->arg[2], "D")){ - c->TuneGetStats(attacker, defender); - } - else { - c->TuneGetStats(defender, attacker); - } - return; - } - - if (!strcasecmp(sep->arg[1], "FindATK")) - { - float pct_mitigation = atof(sep->arg[3]); - int interval = atoi(sep->arg[4]); - int max_loop = atoi(sep->arg[5]); - int ac_override = atoi(sep->arg[6]); - int info_level = atoi(sep->arg[7]); - - if (!pct_mitigation) - { - c->Message(Chat::White, "[#Tune] - Error must enter the desired percent mitigation on defender."); - c->Message(Chat::White, "...Returns recommended ATK adjustment (+/-) on ATTACKER that will result in a specific average AC mitigation pct on DEFENDER. "); - c->Message(Chat::White, "...#tune FindATK [A/D] [pct mitigation] [interval][loop_max][AC override][Info Level]"); - c->Message(Chat::White, "...Example: Find the amount of ATK stat I need to add to the targeted NPC so that it hits me for 50 pct damage on average."); - c->Message(Chat::White, "...Example: #tune FindATK D 50"); - return; - } - - if (!interval) { - interval = 10; - } - if (!max_loop) { - max_loop = 1000; - } - if (!ac_override) { - ac_override = 0; - } - if (!info_level) { - info_level = 0; - } - - if (!strcasecmp(sep->arg[2], "A")) { - c->TuneGetATKByPctMitigation(defender, attacker, pct_mitigation, interval, max_loop, ac_override, info_level); - } - else if (!strcasecmp(sep->arg[2], "D")) { - c->TuneGetATKByPctMitigation(attacker, defender, pct_mitigation, interval, max_loop, ac_override, info_level); - } - else { - c->Message(Chat::White, "#Tune - Error no category selcted. [#Tune help]"); - c->Message(Chat::White, "Usage #tune FindATK [A/B] [pct mitigation] [interval][loop_max][AC Overwride][Info Level] "); - c->Message(Chat::White, "...Usage: [A/D] You must input either A or D."); - c->Message(Chat::White, "...Category [A] : YOU are the ATTACKER. YOUR TARGET is the DEFENDER."); - c->Message(Chat::White, "...Category [D] : YOU are the DEFENDER. YOUR TARGET is the ATTACKER."); - c->Message(Chat::White, "...If TARGET is in combat, DEFENDER is the TARGETs TARGET."); - c->Message(Chat::White, "... "); - c->Message(Chat::White, "...Example: Find the amount of ATK stat I need to add to the targeted NPC so that it hits me for 50 pct damage on average."); - c->Message(Chat::White, "...Example: #tune FindATK D 50"); - } - return; - } - - if (!strcasecmp(sep->arg[1], "FindAC")) - { - float pct_mitigation = atof(sep->arg[3]); - int interval = atoi(sep->arg[4]); - int max_loop = atoi(sep->arg[5]); - int atk_override = atoi(sep->arg[6]); - int info_level = atoi(sep->arg[7]); - - if (!pct_mitigation) - { - c->Message(Chat::White, "#Tune - Error must enter the desired percent mitigation on defender."); - c->Message(Chat::White, "...Returns recommended AC adjustment(+/-) on DEFENDER for a specific average AC mitigation pct from ATTACKER. "); - c->Message(Chat::White, "...#tune FindAC [A/D] [pct mitigation] [interval][loop_max][ATK override][Info Level] "); - c->Message(Chat::White, "...Example: Find the amount of AC stat I need to add to the targeted NPC so that I hit it for 70 pct damage on average."); - c->Message(Chat::White, "...Example: #tune FindAC D 70"); - return; - } - - if (!interval) { - interval = 10; - } - if (!max_loop) { - max_loop = 1000; - } - if (!atk_override) { - atk_override = 0; - } - if (!info_level) { - info_level = 0; - } - - if (!strcasecmp(sep->arg[2], "A")) { - c->TuneGetACByPctMitigation(defender, attacker, pct_mitigation, interval, max_loop, atk_override, info_level); - } - else if (!strcasecmp(sep->arg[2], "D")) { - c->TuneGetACByPctMitigation(attacker, defender, pct_mitigation, interval, max_loop, atk_override, info_level); - } - else { - c->Message(Chat::White, "#Tune - Error no category selcted. [#Tune help]"); - c->Message(Chat::White, "Usage #tune FindATK [A/B] [pct mitigation] [interval][loop_max][AC Overwride][Info Level] "); - c->Message(Chat::White, "...Usage: [A/D] You must input either A or D."); - c->Message(Chat::White, "...Category [A] : YOU are the ATTACKER. YOUR TARGET is the DEFENDER."); - c->Message(Chat::White, "...Category [D] : YOU are the DEFENDER. YOUR TARGET is the ATTACKER."); - c->Message(Chat::White, "...If TARGET is in combat, DEFENDER is the TARGETs TARGET."); - c->Message(Chat::White, "... "); - c->Message(Chat::White, "...Example: Find the amount of AC stat I need to add to the targeted NPC so that I hit it for 70 pct damage on average."); - c->Message(Chat::White, "...Example: #tune FindAC D 70"); - } - - return; - } - - if (!strcasecmp(sep->arg[1], "FindAccuracy")) - { - float hit_chance = atof(sep->arg[3]); - int interval = atoi(sep->arg[4]); - int max_loop = atoi(sep->arg[5]); - int avoid_override = atoi(sep->arg[6]); - int info_level = atoi(sep->arg[7]); - - if (!hit_chance) - { - c->Message(Chat::White, "#Tune - Error must enter the desired hit chance on defender."); - c->Message(Chat::White, "...Returns recommended Accuracy adjustment (+/-) on ATTACKER that will result in a specific hit chance pct on DEFENDER. "); - c->Message(Chat::White, "...#tune FindAccuracy [A/D] [hit chance] [interval][loop_max][Avoidance override][Info Level]"); - c->Message(Chat::White, "...Example: Find the amount of Accuracy stat I need to add to the targeted NPC so that it has a 60 pct hit chance against me."); - c->Message(Chat::White, "...Example: #tune FindAccuracy D 60"); - return; - } - - if (!interval) { - interval = 10; - } - if (!max_loop) { - max_loop = 1000; - } - if (!avoid_override) { - avoid_override = 0; - } - if (!info_level) { - info_level = 0; - } - - if (!strcasecmp(sep->arg[2], "A")) - c->TuneGetAccuracyByHitChance(defender, attacker, hit_chance, interval, max_loop, avoid_override, info_level); - else if (!strcasecmp(sep->arg[2], "D")) - c->TuneGetAccuracyByHitChance(attacker, defender, hit_chance, interval, max_loop, avoid_override, info_level); - else { - c->Message(Chat::White, "#Tune - Error no category selcted. [#Tune help]"); - c->Message(Chat::White, "...#tune FindAccuracy [A/D] [hit chance] [interval][loop_max][Avoidance override][Info Level]"); - c->Message(Chat::White, "...Usage: [A/D] You must input either A or D."); - c->Message(Chat::White, "...Category [A] : YOU are the ATTACKER. YOUR TARGET is the DEFENDER."); - c->Message(Chat::White, "...Category [D] : YOU are the DEFENDER. YOUR TARGET is the ATTACKER."); - c->Message(Chat::White, "...If TARGET is in combat, DEFENDER is the TARGETs TARGET."); - c->Message(Chat::White, "... "); - c->Message(Chat::White, "...Example: Find the amount of Accuracy stat I need to add to the targeted NPC so that it has a 60 pct hit chance against me."); - c->Message(Chat::White, "...Example: #tune FindAccuracy D 60"); - } - - return; - } - - if (!strcasecmp(sep->arg[1], "FindAvoidance")) - { - float hit_chance = atof(sep->arg[3]); - int interval = atoi(sep->arg[4]); - int max_loop = atoi(sep->arg[5]); - int acc_override = atoi(sep->arg[6]); - int info_level = atoi(sep->arg[7]); - - if (!hit_chance) - { - c->Message(Chat::White, "#Tune - Error must enter the desired hit chance on defender."); - c->Message(Chat::White, "...Returns recommended Avoidance adjustment (+/-) on DEFENDER for in a specific hit chance pct from ATTACKER. "); - c->Message(Chat::White, "...#tune FindAvoidance [A/D] [hit chance] [interval][loop_max][Accuracy override][Info Level] "); - c->Message(Chat::White, "...Example: Find the amount of Avoidance stat I need to add to the targeted NPC so that I have a 30 pct hit chance against it."); - c->Message(Chat::White, "...Example: #tune FindAvoidance D 30"); - return; - } - if (!interval) { - interval = 10; - } - if (!max_loop) { - max_loop = 1000; - } - if (!acc_override) { - acc_override = 0; - } - if (!info_level) { - info_level = 0; - } - - if (!strcasecmp(sep->arg[2], "A")) - c->TuneGetAvoidanceByHitChance(defender, attacker, hit_chance, interval, max_loop, acc_override, info_level); - else if (!strcasecmp(sep->arg[2], "D")) - c->TuneGetAvoidanceByHitChance(attacker, defender, hit_chance, interval, max_loop, acc_override, info_level); - else { - c->Message(Chat::White, "#Tune - Error no category selcted. [#Tune help]"); - c->Message(Chat::White, "...#tune FindAvoidance [A/D] [hit chance] [interval][loop_max][Accuracy override][Info Level] "); - c->Message(Chat::White, "...Usage: [A/D] You must input either A or D."); - c->Message(Chat::White, "...Category [A] : YOU are the ATTACKER. YOUR TARGET is the DEFENDER."); - c->Message(Chat::White, "...Category [D] : YOU are the DEFENDER. YOUR TARGET is the ATTACKER."); - c->Message(Chat::White, "...If TARGET is in combat, DEFENDER is the TARGETs TARGET."); - c->Message(Chat::White, "... "); - c->Message(Chat::White, "...Example: Find the amount of Avoidance stat I need to add to the targeted NPC so that I have a 30 pct hit chance against it."); - c->Message(Chat::White, "...Example: #tune FindAvoidance D 30"); - } - - return; - } - - c->Message(Chat::White, "#Tune - Error no command [#Tune help]"); - return; -} - -void command_logs(Client *c, const Seperator *sep){ - int logs_set = 0; - if (sep->argnum > 0) { - /* #logs reload_all */ - if (strcasecmp(sep->arg[1], "reload_all") == 0){ - auto pack = new ServerPacket(ServerOP_ReloadLogs, 0); - worldserver.SendPacket(pack); - c->Message(Chat::Red, "Successfully sent the packet to world to reload log settings from the database for all zones"); - safe_delete(pack); - } - /* #logs list_settings */ - if (strcasecmp(sep->arg[1], "list_settings") == 0 || - (strcasecmp(sep->arg[1], "set") == 0 && strcasecmp(sep->arg[3], "") == 0)) { - c->Message(Chat::White, "[Category ID | console | file | gmsay | Category Description]"); - int redisplay_columns = 0; - for (int i = 0; i < Logs::LogCategory::MaxCategoryID; i++) { - if (redisplay_columns == 10) { - c->Message(Chat::White, "[Category ID | console | file | gmsay | Category Description]"); - redisplay_columns = 0; - } - c->Message( - 0, - StringFormat( - "--- %i | %u | %u | %u | %s", - i, - LogSys.log_settings[i].log_to_console, - LogSys.log_settings[i].log_to_file, - LogSys.log_settings[i].log_to_gmsay, - Logs::LogCategoryName[i] - ).c_str()); - redisplay_columns++; - } - } - /* #logs set */ - if (strcasecmp(sep->arg[1], "set") == 0){ - if (strcasecmp(sep->arg[2], "console") == 0){ - LogSys.log_settings[atoi(sep->arg[3])].log_to_console = atoi(sep->arg[4]); - logs_set = 1; - } - else if (strcasecmp(sep->arg[2], "file") == 0){ - LogSys.log_settings[atoi(sep->arg[3])].log_to_file = atoi(sep->arg[4]); - logs_set = 1; - } - else if (strcasecmp(sep->arg[2], "gmsay") == 0){ - LogSys.log_settings[atoi(sep->arg[3])].log_to_gmsay = atoi(sep->arg[4]); - logs_set = 1; - } - else{ - c->Message(Chat::White, "--- #logs set [console|file|gmsay] - Sets log settings during the lifetime of the zone"); - c->Message(Chat::White, "--- #logs set gmsay 20 1 - Would output Quest errors to gmsay"); - } - if (logs_set == 1){ - c->Message(Chat::Yellow, "Your Log Settings have been applied"); - c->Message(Chat::Yellow, "Output Method: %s :: Debug Level: %i - Category: %s", sep->arg[2], atoi(sep->arg[4]), Logs::LogCategoryName[atoi(sep->arg[3])]); - } - /* We use a general 'is_category_enabled' now, let's update when we update any output settings - This is used in hot places of code to check if its enabled in any way before triggering logs - */ - if (atoi(sep->arg[4]) > 0){ - LogSys.log_settings[atoi(sep->arg[3])].is_category_enabled = 1; - } - else{ - LogSys.log_settings[atoi(sep->arg[3])].is_category_enabled = 0; - } - } - } - else { - c->Message(Chat::White, "#logs usage:"); - c->Message(Chat::White, "--- #logs reload_all - Reload all settings in world and all zone processes with what is defined in the database"); - c->Message(Chat::White, "--- #logs list_settings - Shows current log settings and categories loaded into the current process' memory"); - c->Message(Chat::White, "--- #logs set [console|file|gmsay] - Sets log settings during the lifetime of the zone"); - } -} - -void command_resetaa_timer(Client *c, const Seperator *sep) { - Client *target = nullptr; - if(!c->GetTarget() || !c->GetTarget()->IsClient()) { - target = c; - } else { - target = c->GetTarget()->CastToClient(); - } - - if(sep->IsNumber(1)) - { - int timer_id = atoi(sep->arg[1]); - c->Message(Chat::White, "Reset of timer %i for %s", timer_id, c->GetName()); - c->ResetAlternateAdvancementTimer(timer_id); - } - else if(!strcasecmp(sep->arg[1], "all")) - { - c->Message(Chat::White, "Reset all timers for %s", c->GetName()); - c->ResetAlternateAdvancementTimers(); - } - else - { - c->Message(Chat::White, "usage: #resetaa_timer [all | timer_id]"); - } -} - -void command_resetdisc_timer(Client *c, const Seperator *sep) -{ - Client *target = c->GetTarget()->CastToClient(); - if (!c->GetTarget() || !c->GetTarget()->IsClient()) { - target = c; - } - - if (sep->IsNumber(1)) { - int timer_id = atoi(sep->arg[1]); - c->Message(Chat::White, "Reset of disc timer %i for %s", timer_id, c->GetName()); - c->ResetDisciplineTimer(timer_id); - } - else if (!strcasecmp(sep->arg[1], "all")) { - c->Message(Chat::White, "Reset all disc timers for %s", c->GetName()); - c->ResetAllDisciplineTimers(); - } - else { - c->Message(Chat::White, "usage: #resetdisc_timer [all | timer_id]"); - } -} - -void command_reloadaa(Client *c, const Seperator *sep) { - c->Message(Chat::White, "Reloading Alternate Advancement Data..."); - zone->LoadAlternateAdvancement(); - c->Message(Chat::White, "Alternate Advancement Data Reloaded"); - entity_list.SendAlternateAdvancementStats(); -} - -inline bool file_exists(const std::string& name) { - std::ifstream f(name.c_str()); - return f.good(); -} - void command_hotfix(Client *c, const Seperator *sep) { std::string hotfix; @@ -15398,870 +1070,6 @@ void command_apply_shared_memory(Client *c, const Seperator *sep) { worldserver.SendPacket(&pack); } -void command_reloadperlexportsettings(Client *c, const Seperator *sep) -{ - if (c) - { - auto pack = new ServerPacket(ServerOP_ReloadPerlExportSettings, 0); - worldserver.SendPacket(pack); - c->Message(Chat::Red, "Successfully sent the packet to world to reload Perl Export settings"); - safe_delete(pack); - - } -} - -void command_trapinfo(Client *c, const Seperator *sep) -{ - entity_list.GetTrapInfo(c); -} - -void command_reloadtraps(Client *c, const Seperator *sep) -{ - entity_list.UpdateAllTraps(true, true); - c->Message(Chat::Default, "Traps reloaded for %s.", zone->GetShortName()); -} - -void command_scale(Client *c, const Seperator *sep) -{ - if (sep->argnum == 0) { - c->Message(Chat::Yellow, "# Usage # "); - c->Message(Chat::Yellow, "#scale [static/dynamic] (With targeted NPC)"); - c->Message(Chat::Yellow, "#scale [npc_name_search] [static/dynamic] (To make zone-wide changes)"); - c->Message(Chat::Yellow, "#scale all [static/dynamic]"); - return; - } - - /** - * Targeted changes - */ - if (c->GetTarget() && c->GetTarget()->IsNPC() && sep->argnum < 2) { - NPC * npc = c->GetTarget()->CastToNPC(); - - bool apply_status = false; - if (strcasecmp(sep->arg[1], "dynamic") == 0) { - c->Message(Chat::Yellow, "Applying global base scaling to npc dynamically (All stats set to zeroes)..."); - apply_status = npc_scale_manager->ApplyGlobalBaseScalingToNPCDynamically(npc); - } - else if (strcasecmp(sep->arg[1], "static") == 0) { - c->Message(Chat::Yellow, "Applying global base scaling to npc statically (Copying base stats onto NPC)..."); - apply_status = npc_scale_manager->ApplyGlobalBaseScalingToNPCStatically(npc); - } - else { - return; - } - - if (apply_status) { - c->Message(Chat::Yellow, "Applied to NPC '%s' successfully!", npc->GetName()); - } - else { - c->Message(Chat::Yellow, "Failed to load scaling data from the database " - "for this npc / type, see 'NPCScaling' log for more info"); - } - } - else if (c->GetTarget() && sep->argnum < 2) { - c->Message(Chat::Yellow, "Target must be an npc!"); - } - - /** - * Zonewide - */ - if (sep->argnum > 1) { - - std::string scale_type; - if (strcasecmp(sep->arg[2], "dynamic") == 0) { - scale_type = "dynamic"; - } - else if (strcasecmp(sep->arg[2], "static") == 0) { - scale_type = "static"; - } - - if (scale_type.length() <= 0) { - c->Message(Chat::Yellow, "You must first set if you intend on using static versus dynamic for these changes"); - c->Message(Chat::Yellow, "#scale [npc_name_search] [static/dynamic]"); - c->Message(Chat::Yellow, "#scale all [static/dynamic]"); - return; - } - - std::string search_string = sep->arg[1]; - - auto &entity_list_search = entity_list.GetNPCList(); - - int found_count = 0; - for (auto &itr : entity_list_search) { - NPC *entity = itr.second; - - std::string entity_name = entity->GetName(); - - /** - * Filter by name - */ - if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos && strcasecmp(sep->arg[1], "all") != 0) { - continue; - } - - std::string status = "(Searching)"; - - if (strcasecmp(sep->arg[3], "apply") == 0) { - status = "(Applying)"; - - if (strcasecmp(sep->arg[2], "dynamic") == 0) { - npc_scale_manager->ApplyGlobalBaseScalingToNPCDynamically(entity); - } - if (strcasecmp(sep->arg[2], "static") == 0) { - npc_scale_manager->ApplyGlobalBaseScalingToNPCStatically(entity); - } - } - - c->Message( - 15, - "| ID %5d | %s | x %.0f | y %0.f | z %.0f | DBID %u %s", - entity->GetID(), - entity->GetName(), - entity->GetX(), - entity->GetY(), - entity->GetZ(), - entity->GetNPCTypeID(), - status.c_str() - ); - - found_count++; - } - - if (strcasecmp(sep->arg[3], "apply") == 0) { - c->Message(Chat::Yellow, "%s scaling applied against (%i) NPC's", sep->arg[2], found_count); - } - else { - - std::string saylink = StringFormat( - "#scale %s %s apply", - sep->arg[1], - sep->arg[2] - ); - - c->Message(Chat::Yellow, "Found (%i) NPC's that match this search...", found_count); - c->Message( - Chat::Yellow, "To apply these changes, click <%s> or type %s", - EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Apply").c_str(), - saylink.c_str() - ); - } - } -} - -void command_databuckets(Client *c, const Seperator *sep) - { - if (sep->arg[1][0] == 0) { - c->Message(Chat::Yellow, "Usage: #databuckets view (partial key)|(limit) OR #databuckets delete (key)"); - return; - } - if (strcasecmp(sep->arg[1], "view") == 0) { - - std::string key_filter; - uint8 limit = 50; - for (int i = 2; i < 4; i++) { - if (sep->arg[i][0] == '\0') - break; - if (strcasecmp(sep->arg[i], "limit") == 0) { - limit = (uint8)atoi(sep->arg[i + 1]); - continue; - } - } - if (sep->arg[2]) { - key_filter = str_tolower(sep->arg[2]); - } - std::string query = "SELECT `id`, `key`, `value`, `expires` FROM data_buckets"; - if (!key_filter.empty()) query += StringFormat(" WHERE `key` LIKE '%%%s%%'", key_filter.c_str()); - query += StringFormat(" LIMIT %u", limit); - auto results = database.QueryDatabase(query); - if (!results.Success()) - return; - if (results.RowCount() == 0) { - c->Message(Chat::Yellow, "No data_buckets found"); - return; - } - int _ctr = 0; - // put in window for easier readability in case want command line for something else - std::string window_title = "Data Buckets"; - std::string window_text = - "" - "" - "" - "" - "" - "" - ""; - for (auto row = results.begin(); row != results.end(); ++row) { - auto id = static_cast(atoi(row[0])); - std::string key = row[1]; - std::string value = row[2]; - std::string expires = row[3]; - window_text.append(StringFormat( - "" - "" - "" - "" - "" - "", - id, - expires.c_str(), - key.c_str(), - value.c_str() - )); - _ctr++; - std::string del_saylink = StringFormat("#databuckets delete %s", key.c_str()); - c->Message(Chat::White, "%s : %s", - EQ::SayLinkEngine::GenerateQuestSaylink(del_saylink, false, "Delete").c_str(), key.c_str(), " Value: ", value.c_str()); - } - window_text.append("
IDExpiresKeyValue
%u%s%s%s
"); - c->SendPopupToClient(window_title.c_str(), window_text.c_str()); - std::string response = _ctr > 0 ? StringFormat("Found %i matching data buckets", _ctr).c_str() : "No Databuckets found."; - c->Message(Chat::Yellow, response.c_str()); - } - else if (strcasecmp(sep->arg[1], "delete") == 0) - { - if (DataBucket::DeleteData(sep->argplus[2])) - c->Message(Chat::Yellow, "data bucket %s deleted.", sep->argplus[2]); - else - c->Message(Chat::Red, "An error occurred deleting data bucket %s", sep->argplus[2]); - return; - } -} - -void command_who(Client *c, const Seperator *sep) -{ - std::string query = - SQL ( - SELECT - character_data.account_id, - character_data.name, - character_data.zone_id, - character_data.zone_instance, - COALESCE( - ( - select - guilds.name - from - guilds - where - id = ( - ( - select - guild_id - from - guild_members - where - char_id = character_data.id - ) - ) - ), - "" - ) as guild_name, - character_data.level, - character_data.race, - character_data.class, - COALESCE( - ( - select - account.status - from - account - where - account.id = character_data.account_id - LIMIT - 1 - ), 0 - ) as account_status, - COALESCE( - ( - select - account.name - from - account - where - account.id = character_data.account_id - LIMIT - 1 - ), - 0 - ) as account_name, - COALESCE( - ( - select - account_ip.ip - from - account_ip - where - account_ip.accid = character_data.account_id - ORDER BY - account_ip.lastused DESC - LIMIT - 1 - ), - "" - ) as account_ip - FROM - character_data - WHERE - last_login > (UNIX_TIMESTAMP() - 600) - ORDER BY - character_data.name; - ) - ; - - auto results = database.QueryDatabase(query); - if (!results.Success()) - return; - - if (results.RowCount() == 0) { - c->Message(Chat::Yellow, "No results found"); - return; - } - - std::string search_string; - - if (sep->arg[1]) { - search_string = str_tolower(sep->arg[1]); - } - - int found_count = 0; - - c->Message(Chat::Magenta, "Players in EverQuest"); - c->Message(Chat::Magenta, "--------------------"); - - for (auto row = results.begin(); row != results.end(); ++row) { - auto account_id = static_cast(atoi(row[0])); - std::string player_name = row[1]; - auto zone_id = static_cast(atoi(row[2])); - std::string zone_short_name = ZoneName(zone_id); - auto zone_instance = static_cast(atoi(row[3])); - std::string guild_name = row[4]; - auto player_level = static_cast(atoi(row[5])); - auto player_race = static_cast(atoi(row[6])); - auto player_class = static_cast(atoi(row[7])); - auto account_status = static_cast(atoi(row[8])); - std::string account_name = row[9]; - std::string account_ip = row[10]; - std::string base_class_name = GetClassIDName(static_cast(player_class), 1); - std::string displayed_race_name = GetRaceIDName(static_cast(player_race)); - - if (search_string.length() > 0) { - bool found_search_term = - ( - str_tolower(player_name).find(search_string) != std::string::npos || - str_tolower(zone_short_name).find(search_string) != std::string::npos || - str_tolower(displayed_race_name).find(search_string) != std::string::npos || - str_tolower(base_class_name).find(search_string) != std::string::npos || - str_tolower(guild_name).find(search_string) != std::string::npos || - str_tolower(account_name).find(search_string) != std::string::npos || - str_tolower(account_ip).find(search_string) != std::string::npos - ); - - if (!found_search_term) { - continue; - } - } - - std::string displayed_guild_name; - if (guild_name.length() > 0) { - displayed_guild_name = EQ::SayLinkEngine::GenerateQuestSaylink( - StringFormat( - "#who \"%s\"", - guild_name.c_str()), - false, - StringFormat("<%s>", guild_name.c_str()) - ); - } - - std::string goto_saylink = EQ::SayLinkEngine::GenerateQuestSaylink( - StringFormat("#goto %s", player_name.c_str()), false, "Goto" - ); - - std::string display_class_name = GetClassIDName(static_cast(player_class), static_cast(player_level)); - - c->Message( - 5, "%s[%u %s] %s (%s) %s ZONE: %s (%u) (%s) (%s) (%s)", - (account_status > 0 ? "* GM * " : ""), - player_level, - EQ::SayLinkEngine::GenerateQuestSaylink(StringFormat("#who %s", base_class_name.c_str()), false, display_class_name).c_str(), - player_name.c_str(), - EQ::SayLinkEngine::GenerateQuestSaylink(StringFormat("#who %s", displayed_race_name.c_str()), false, displayed_race_name).c_str(), - displayed_guild_name.c_str(), - EQ::SayLinkEngine::GenerateQuestSaylink(StringFormat("#who %s", zone_short_name.c_str()), false, zone_short_name).c_str(), - zone_instance, - goto_saylink.c_str(), - EQ::SayLinkEngine::GenerateQuestSaylink(StringFormat("#who %s", account_name.c_str()), false, account_name).c_str(), - EQ::SayLinkEngine::GenerateQuestSaylink(StringFormat("#who %s", account_ip.c_str()), false, account_ip).c_str() - ); - - found_count++; - } - - std::string message = ( - found_count > 0 ? - StringFormat("There is %i player(s) in EverQuest", found_count).c_str() : - "There are no players in EverQuest that match those who filters." - ); - - c->Message(Chat::Magenta, message.c_str()); -} - -void command_network(Client *c, const Seperator *sep) -{ - if (!strcasecmp(sep->arg[1], "getopt")) - { - auto eqsi = c->Connection(); - auto manager = eqsi->GetManager(); - auto opts = manager->GetOptions(); - - if (!strcasecmp(sep->arg[2], "all")) - { - c->Message(Chat::White, "max_packet_size: %llu", (uint64_t)opts.daybreak_options.max_packet_size); - c->Message(Chat::White, "max_connection_count: %llu", (uint64_t)opts.daybreak_options.max_connection_count); - c->Message(Chat::White, "keepalive_delay_ms: %llu", (uint64_t)opts.daybreak_options.keepalive_delay_ms); - c->Message(Chat::White, "resend_delay_factor: %.2f", opts.daybreak_options.resend_delay_factor); - c->Message(Chat::White, "resend_delay_ms: %llu", (uint64_t)opts.daybreak_options.resend_delay_ms); - c->Message(Chat::White, "resend_delay_min: %llu", (uint64_t)opts.daybreak_options.resend_delay_min); - c->Message(Chat::White, "resend_delay_max: %llu", (uint64_t)opts.daybreak_options.resend_delay_max); - c->Message(Chat::White, "connect_delay_ms: %llu", (uint64_t)opts.daybreak_options.connect_delay_ms); - c->Message(Chat::White, "connect_stale_ms: %llu", (uint64_t)opts.daybreak_options.connect_stale_ms); - c->Message(Chat::White, "stale_connection_ms: %llu", (uint64_t)opts.daybreak_options.stale_connection_ms); - c->Message(Chat::White, "crc_length: %llu", (uint64_t)opts.daybreak_options.crc_length); - c->Message(Chat::White, "hold_size: %llu", (uint64_t)opts.daybreak_options.hold_size); - c->Message(Chat::White, "hold_length_ms: %llu", (uint64_t)opts.daybreak_options.hold_length_ms); - c->Message(Chat::White, "simulated_in_packet_loss: %llu", (uint64_t)opts.daybreak_options.simulated_in_packet_loss); - c->Message(Chat::White, "simulated_out_packet_loss: %llu", (uint64_t)opts.daybreak_options.simulated_out_packet_loss); - c->Message(Chat::White, "tic_rate_hertz: %.2f", opts.daybreak_options.tic_rate_hertz); - c->Message(Chat::White, "resend_timeout: %llu", (uint64_t)opts.daybreak_options.resend_timeout); - c->Message(Chat::White, "connection_close_time: %llu", (uint64_t)opts.daybreak_options.connection_close_time); - c->Message(Chat::White, "encode_passes[0]: %llu", (uint64_t)opts.daybreak_options.encode_passes[0]); - c->Message(Chat::White, "encode_passes[1]: %llu", (uint64_t)opts.daybreak_options.encode_passes[1]); - c->Message(Chat::White, "port: %llu", (uint64_t)opts.daybreak_options.port); - } - else { - c->Message(Chat::White, "Unknown get option: %s", sep->arg[2]); - c->Message(Chat::White, "Available options:"); - //Todo the rest of these when im less lazy. - //c->Message(Chat::White, "max_packet_size"); - //c->Message(Chat::White, "max_connection_count"); - //c->Message(Chat::White, "keepalive_delay_ms"); - //c->Message(Chat::White, "resend_delay_factor"); - //c->Message(Chat::White, "resend_delay_ms"); - //c->Message(Chat::White, "resend_delay_min"); - //c->Message(Chat::White, "resend_delay_max"); - //c->Message(Chat::White, "connect_delay_ms"); - //c->Message(Chat::White, "connect_stale_ms"); - //c->Message(Chat::White, "stale_connection_ms"); - //c->Message(Chat::White, "crc_length"); - //c->Message(Chat::White, "hold_size"); - //c->Message(Chat::White, "hold_length_ms"); - //c->Message(Chat::White, "simulated_in_packet_loss"); - //c->Message(Chat::White, "simulated_out_packet_loss"); - //c->Message(Chat::White, "tic_rate_hertz"); - //c->Message(Chat::White, "resend_timeout"); - //c->Message(Chat::White, "connection_close_time"); - //c->Message(Chat::White, "encode_passes[0]"); - //c->Message(Chat::White, "encode_passes[1]"); - //c->Message(Chat::White, "port"); - c->Message(Chat::White, "all"); - } - } - else if (!strcasecmp(sep->arg[1], "setopt")) - { - auto eqsi = c->Connection(); - auto manager = eqsi->GetManager(); - auto opts = manager->GetOptions(); - - if (!strcasecmp(sep->arg[3], "")) - { - c->Message(Chat::White, "Missing value for set"); - return; - } - - std::string value = sep->arg[3]; - if (!strcasecmp(sep->arg[2], "max_connection_count")) - { - opts.daybreak_options.max_connection_count = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "keepalive_delay_ms")) - { - opts.daybreak_options.keepalive_delay_ms = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "resend_delay_factor")) - { - opts.daybreak_options.resend_delay_factor = std::stod(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "resend_delay_ms")) - { - opts.daybreak_options.resend_delay_ms = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "resend_delay_min")) - { - opts.daybreak_options.resend_delay_min = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "resend_delay_max")) - { - opts.daybreak_options.resend_delay_max = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "connect_delay_ms")) - { - opts.daybreak_options.connect_delay_ms = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "connect_stale_ms")) - { - opts.daybreak_options.connect_stale_ms = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "stale_connection_ms")) - { - opts.daybreak_options.stale_connection_ms = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "hold_size")) - { - opts.daybreak_options.hold_size = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "hold_length_ms")) - { - opts.daybreak_options.hold_length_ms = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "simulated_in_packet_loss")) - { - opts.daybreak_options.simulated_in_packet_loss = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "simulated_out_packet_loss")) - { - opts.daybreak_options.simulated_out_packet_loss = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "resend_timeout")) - { - opts.daybreak_options.resend_timeout = std::stoull(value); - manager->SetOptions(opts); - } - else if (!strcasecmp(sep->arg[2], "connection_close_time")) - { - opts.daybreak_options.connection_close_time = std::stoull(value); - manager->SetOptions(opts); - } - else { - c->Message(Chat::White, "Unknown set option: %s", sep->arg[2]); - c->Message(Chat::White, "Available options:"); - c->Message(Chat::White, "max_connection_count"); - c->Message(Chat::White, "keepalive_delay_ms"); - c->Message(Chat::White, "resend_delay_factor"); - c->Message(Chat::White, "resend_delay_ms"); - c->Message(Chat::White, "resend_delay_min"); - c->Message(Chat::White, "resend_delay_max"); - c->Message(Chat::White, "connect_delay_ms"); - c->Message(Chat::White, "connect_stale_ms"); - c->Message(Chat::White, "stale_connection_ms"); - c->Message(Chat::White, "hold_size"); - c->Message(Chat::White, "hold_length_ms"); - c->Message(Chat::White, "simulated_in_packet_loss"); - c->Message(Chat::White, "simulated_out_packet_loss"); - c->Message(Chat::White, "resend_timeout"); - c->Message(Chat::White, "connection_close_time"); - } - } - else { - c->Message(Chat::White, "Unknown command: %s", sep->arg[1]); - c->Message(Chat::White, "Network commands avail:"); - c->Message(Chat::White, "getopt optname - Retrieve the current option value set."); - c->Message(Chat::White, "setopt optname - Set the current option allowed."); - } -} - -void command_viewzoneloot(Client *c, const Seperator *sep) -{ - std::map zone_loot_list; - auto npc_list = entity_list.GetNPCList(); - uint32 loot_amount = 0, loot_id = 1, search_item_id = 0; - if (sep->argnum == 1 && sep->IsNumber(1)) { - search_item_id = atoi(sep->arg[1]); - } else if (sep->argnum == 1 && !sep->IsNumber(1)) { - c->Message( - Chat::Yellow, - "Usage: #viewzoneloot [item id]" - ); - return; - } - - for (auto npc_entity : npc_list) { - auto current_npc_item_list = npc_entity.second->GetItemList(); - zone_loot_list.insert({ npc_entity.second->GetID(), current_npc_item_list }); - } - - for (auto loot_item : zone_loot_list) { - uint32 current_entity_id = loot_item.first; - auto current_item_list = loot_item.second; - auto current_npc = entity_list.GetNPCByID(current_entity_id); - std::string npc_link; - if (current_npc) { - std::string npc_name = current_npc->GetCleanName(); - uint32 instance_id = zone->GetInstanceID(); - uint32 zone_id = zone->GetZoneID(); - std::string command_link = EQ::SayLinkEngine::GenerateQuestSaylink( - fmt::format( - "#{} {} {} {} {}", - (instance_id != 0 ? "zoneinstance" : "zone"), - (instance_id != 0 ? instance_id : zone_id), - current_npc->GetX(), - current_npc->GetY(), - current_npc->GetZ() - ), - false, - "Goto" - ); - npc_link = fmt::format( - " NPC: {} (ID {}) [{}]", - npc_name, - current_entity_id, - command_link - ); - } - - for (auto current_item : current_item_list) { - if (search_item_id == 0 || current_item->item_id == search_item_id) { - EQ::SayLinkEngine linker; - linker.SetLinkType(EQ::saylink::SayLinkLootItem); - linker.SetLootData(current_item); - c->Message( - Chat::White, - fmt::format( - "{}. {} ({}){}", - loot_id, - linker.GenerateLink(), - current_item->item_id, - npc_link - ).c_str() - ); - loot_id++; - loot_amount++; - } - } - } - - - if (search_item_id != 0) { - std::string drop_string = ( - loot_amount > 0 ? - fmt::format( - "dropping in {} {}", - loot_amount, - (loot_amount > 1 ? "places" : "place") - ) : - "not dropping" - ); - - c->Message( - Chat::White, - fmt::format( - "{} ({}) is {}.", - database.CreateItemLink(search_item_id), - search_item_id, - drop_string - ).c_str() - ); - } else { - std::string drop_string = ( - loot_amount > 0 ? - fmt::format( - "{} {} dropping", - (loot_amount > 1 ? "items" : "item"), - (loot_amount > 1 ? "are" : "is") - ) : - "items are dropping" - ); - - c->Message( - Chat::White, - fmt::format( - "{} {}.", - loot_amount, - drop_string - ).c_str() - ); - } -} - -void command_dye(Client *c, const Seperator *sep) -{ - int arguments = sep->argnum; - - if (arguments == 0) { - c->Message(Chat::White, "Command Syntax: #dye help | #dye [slot] [red] [green] [blue] [use_tint]"); - return; - } - - uint8 slot = 0; - uint8 red = 255; - uint8 green = 255; - uint8 blue = 255; - uint8 use_tint = 255; - - std::vector dye_slots = { - "Helmet", - "Chest", - "Arms", - "Wrist", - "Hands", - "Legs", - "Feet" - }; - - if (arguments == 1 && !strcasecmp(sep->arg[1], "help")) { - int slot_id = 0; - std::vector slot_messages; - c->Message(Chat::White, "Command Syntax: #dye help | #dye [slot] [red] [green] [blue] [use_tint]"); - c->Message(Chat::White, "Red, Green, and Blue go from 0 to 255."); - - for (const auto& slot : dye_slots) { - slot_messages.push_back(fmt::format("({}) {}", slot_id, slot)); - slot_id++; - } - - c->Message( - Chat::White, - fmt::format( - "{} {}", - "Slots are as follows:", - implode(", ", slot_messages) - ).c_str() - ); - return; - } - - if (arguments >= 1 && sep->IsNumber(1)) { - slot = atoi(sep->arg[1]); - } - - if (arguments >= 2 && sep->IsNumber(2)) { - red = atoi(sep->arg[2]); - } - - if (arguments >= 3 && sep->IsNumber(3)) { - green = atoi(sep->arg[3]); - } - - if (arguments >= 4 && sep->IsNumber(4)) { - blue = atoi(sep->arg[4]); - } - - if (arguments >= 5 && sep->IsNumber(5)) { - use_tint = atoi(sep->arg[5]); - } - - if (RuleB(Command, DyeCommandRequiresDyes)) { - uint32 dye_item_id = 32557; - if (c->CountItem(dye_item_id) >= 1) { - c->RemoveItem(dye_item_id); - } else { - EQ::SayLinkEngine linker; - linker.SetLinkType(EQ::saylink::SayLinkItemData); - const EQ::ItemData *dye_item = database.GetItem(dye_item_id); - linker.SetItemData(dye_item); - c->Message(Chat::White, fmt::format("This command requires a {} to use.", linker.GenerateLink()).c_str()); - return; - } - } - - c->DyeArmorBySlot(slot, red, green, blue, use_tint); -} - -void command_findtask(Client *c, const Seperator *sep) -{ - if (RuleB(TaskSystem, EnableTaskSystem)) { - int arguments = sep->argnum; - - if (arguments == 0) { - c->Message(Chat::White, "Command Syntax: #findtask [search criteria]"); - return; - } - - if (sep->IsNumber(1)) { - auto task_id = std::stoul(sep->arg[1]); - auto task_name = task_manager->GetTaskName(task_id); - auto task_message = ( - !task_name.empty() ? - fmt::format( - "Task {}: {}", - task_id, - task_name - ).c_str() : - fmt::format( - "Task ID {} was not found.", - task_id - ).c_str() - ); - - c->Message( - Chat::White, - task_message - ); - } else { - std::string search_criteria = str_tolower(sep->argplus[1]); - if (!search_criteria.empty()) { - int found_count = 0; - for (uint32 task_id = 1; task_id <= MAXTASKS; task_id++) { - auto task_name = task_manager->GetTaskName(task_id); - std::string task_name_lower = str_tolower(task_name); - if (task_name_lower.find(search_criteria) == std::string::npos) { - continue; - } - - c->Message( - Chat::White, - fmt::format( - "Task {}: {}", - task_id, - task_name - ).c_str() - ); - found_count++; - - if (found_count == 20) { - break; - } - } - - if (found_count == 20) { - c->Message(Chat::White, "20 Tasks were found, max reached."); - } else { - auto task_message = ( - found_count > 0 ? - ( - found_count == 1 ? - "A Task was" : - fmt::format("{} Tasks were", found_count) - ) : - "No Tasks were" - ); - - c->Message( - Chat::White, - fmt::format( - "{} found.", - task_message - ).c_str() - ); - } - } - } - } else { - c->Message(Chat::White, "This command cannot be used while the Task system is disabled."); - } -} - void command_emptyinventory(Client *c, const Seperator *sep) { Client *target = c; diff --git a/zone/command.h b/zone/command.h index b0d5ccc40..4b5f99b2d 100644 --- a/zone/command.h +++ b/zone/command.h @@ -1,22 +1,3 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.org) - - 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 COMMAND_H #define COMMAND_H @@ -25,18 +6,18 @@ class Seperator; #include "../common/types.h" -#define COMMAND_CHAR '#' +#define COMMAND_CHAR '#' -typedef void (*CmdFuncPtr)(Client *,const Seperator *); +typedef void (*CmdFuncPtr)(Client *, const Seperator *); typedef struct { - int access; - const char *desc; // description of command - CmdFuncPtr function; // null means perl function -} CommandRecord; + int access; + const char *desc; // description of command + CmdFuncPtr function; // null means perl function +} CommandRecord; -extern int (*command_dispatch)(Client *,char const*); -extern int commandcount; // number of commands loaded +extern int (*command_dispatch)(Client *, char const *); +extern int commandcount; // number of commands loaded // the command system: int command_init(void); @@ -59,7 +40,7 @@ void command_augmentitem(Client *c, const Seperator *sep); void command_ban(Client *c, const Seperator *sep); void command_beard(Client *c, const Seperator *sep); void command_beardcolor(Client *c, const Seperator *sep); -void command_bind(Client* c, const Seperator *sep); +void command_bind(Client *c, const Seperator *sep); void command_camerashake(Client *c, const Seperator *sep); void command_castspell(Client *c, const Seperator *sep); void command_chat(Client *c, const Seperator *sep); @@ -87,10 +68,10 @@ void command_doanim(Client *c, const Seperator *sep); void command_dye(Client *c, const Seperator *sep); void command_dz(Client *c, const Seperator *sep); void command_dzkickplayers(Client *c, const Seperator *sep); -void command_editmassrespawn(Client* c, const Seperator* sep); +void command_editmassrespawn(Client *c, const Seperator *sep); void command_emote(Client *c, const Seperator *sep); -void command_emotesearch(Client* c, const Seperator *sep); -void command_emoteview(Client* c, const Seperator *sep); +void command_emotesearch(Client *c, const Seperator *sep); +void command_emoteview(Client *c, const Seperator *sep); void command_emptyinventory(Client *c, const Seperator *sep); void command_enablerecipe(Client *c, const Seperator *sep); void command_endurance(Client *c, const Seperator *sep); @@ -121,14 +102,14 @@ void command_getvariable(Client *c, const Seperator *sep); void command_ginfo(Client *c, const Seperator *sep); void command_giveitem(Client *c, const Seperator *sep); void command_givemoney(Client *c, const Seperator *sep); -void command_globalview(Client* c, const Seperator *sep); +void command_globalview(Client *c, const Seperator *sep); void command_gm(Client *c, const Seperator *sep); void command_gmspeed(Client *c, const Seperator *sep); void command_gmzone(Client *c, const Seperator *sep); void command_goto(Client *c, const Seperator *sep); void command_grid(Client *c, const Seperator *sep); void command_guild(Client *c, const Seperator *sep); -bool helper_guild_edit(Client *c, uint32 dbid, uint32 eqid, uint8 rank, const char* what, const char* value); +bool helper_guild_edit(Client *c, uint32 dbid, uint32 eqid, uint8 rank, const char *what, const char *value); void command_guildapprove(Client *c, const Seperator *sep); void command_guildcreate(Client *c, const Seperator *sep); void command_guildlist(Client *c, const Seperator *sep); @@ -193,12 +174,14 @@ void command_npcspecialattk(Client *c, const Seperator *sep); void command_npcstats(Client *c, const Seperator *sep); void command_npctype_cache(Client *c, const Seperator *sep); void command_npctypespawn(Client *c, const Seperator *sep); -void command_nudge(Client* c, const Seperator* sep); +void command_nudge(Client *c, const Seperator *sep); void command_nukebuffs(Client *c, const Seperator *sep); void command_nukeitem(Client *c, const Seperator *sep); -void command_object(Client* c, const Seperator *sep); +void command_object(Client *c, const Seperator *sep); void command_oocmute(Client *c, const Seperator *sep); void command_opcode(Client *c, const Seperator *sep); +void command_bestz(Client *c, const Seperator *message); +void command_pf(Client *c, const Seperator *message); #ifdef PACKET_PROFILER void command_packetprofile(Client *c, const Seperator *sep); @@ -225,24 +208,24 @@ void command_pvp(Client *c, const Seperator *sep); void command_qglobal(Client *c, const Seperator *sep); void command_questerrors(Client *c, const Seperator *sep); void command_race(Client *c, const Seperator *sep); -void command_raidloot(Client* c, const Seperator *sep); +void command_raidloot(Client *c, const Seperator *sep); void command_randomfeatures(Client *c, const Seperator *sep); void command_refreshgroup(Client *c, const Seperator *sep); void command_reloadaa(Client *c, const Seperator *sep); void command_reloadallrules(Client *c, const Seperator *sep); -void command_reloademote(Client* c, const Seperator *sep); +void command_reloademote(Client *c, const Seperator *sep); void command_reloadlevelmods(Client *c, const Seperator *sep); void command_reloadmerchants(Client *c, const Seperator *sep); void command_reloadperlexportsettings(Client *c, const Seperator *sep); void command_reloadqst(Client *c, const Seperator *sep); void command_reloadstatic(Client *c, const Seperator *sep); void command_reloadtitles(Client *c, const Seperator *sep); -void command_reloadtraps(Client* c, const Seperator *sep); +void command_reloadtraps(Client *c, const Seperator *sep); void command_reloadworld(Client *c, const Seperator *sep); void command_reloadworldrules(Client *c, const Seperator *sep); void command_reloadzps(Client *c, const Seperator *sep); void command_repop(Client *c, const Seperator *sep); -void command_resetaa(Client* c,const Seperator *sep); +void command_resetaa(Client *c, const Seperator *sep); void command_resetaa_timer(Client *c, const Seperator *sep); void command_resetdisc_timer(Client *c, const Seperator *sep); void command_revoke(Client *c, const Seperator *sep); @@ -305,7 +288,7 @@ void command_timezone(Client *c, const Seperator *sep); void command_title(Client *c, const Seperator *sep); void command_titlesuffix(Client *c, const Seperator *sep); void command_traindisc(Client *c, const Seperator *sep); -void command_trapinfo(Client* c, const Seperator *sep); +void command_trapinfo(Client *c, const Seperator *sep); void command_tune(Client *c, const Seperator *sep); void command_ucs(Client *c, const Seperator *sep); void command_undye(Client *c, const Seperator *sep); diff --git a/zone/gm_commands/acceptrules.cpp b/zone/gm_commands/acceptrules.cpp new file mode 100755 index 000000000..a73e098a2 --- /dev/null +++ b/zone/gm_commands/acceptrules.cpp @@ -0,0 +1,11 @@ +#include "../client.h" + +void command_acceptrules(Client *c, const Seperator *sep) +{ + if (!database.GetAgreementFlag(c->AccountID())) { + database.SetAgreementFlag(c->AccountID()); + c->SendAppearancePacket(AT_Anim, ANIM_STAND); + c->Message(Chat::White, "It is recorded you have agreed to the rules."); + } +} + diff --git a/zone/gm_commands/advnpcspawn.cpp b/zone/gm_commands/advnpcspawn.cpp new file mode 100755 index 000000000..7545ded35 --- /dev/null +++ b/zone/gm_commands/advnpcspawn.cpp @@ -0,0 +1,534 @@ +#include "../client.h" +#include "../groups.h" + +void command_advnpcspawn(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (!arguments) { + c->Message( + Chat::White, + "Usage: #advnpcspawn addentry [Spawngroup ID] [NPC ID] [Spawn Chance] - Adds a new Spawngroup Entry" + ); + c->Message( + Chat::White, + "Usage: #advnpcspawn addspawn [Spawngroup ID] - Adds a new Spawngroup Entry from an existing Spawngroup" + ); + c->Message(Chat::White, "Usage: #advnpcspawn clearbox [Spawngroup ID] - Clears the roambox of a Spawngroup"); + c->Message(Chat::White, "Usage: #advnpcspawn deletespawn - Deletes a Spawngroup"); + c->Message( + Chat::White, + "Usage: #advnpcspawn editbox [Spawngroup ID] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay] - Edit the roambox of a Spawngroup" + ); + c->Message( + Chat::White, + "Usage: #advnpcspawn editrespawn [Respawn Timer] [Variance] - Edit the Respawn Timer of a Spawngroup" + ); + c->Message( + Chat::White, + "Usage: #advnpcspawn makegroup [Spawn Group Name] [Spawn Limit] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay] - Makes a new Spawngroup" + ); + c->Message(Chat::White, "Usage: #advnpcspawn makenpc - Makes a new NPC"); + c->Message(Chat::White, "Usage: #advnpcspawn movespawn - Moves a Spawngroup to your current location"); + c->Message(Chat::White, "Usage: #advnpcspawn setversion [Version] - Sets a Spawngroup's Version"); + return; + } + + std::string spawn_command = str_tolower(sep->arg[1]); + bool is_add_entry = spawn_command.find("addentry") != std::string::npos; + bool is_add_spawn = spawn_command.find("addspawn") != std::string::npos; + bool is_clear_box = spawn_command.find("clearbox") != std::string::npos; + bool is_delete_spawn = spawn_command.find("deletespawn") != std::string::npos; + bool is_edit_box = spawn_command.find("editgroup") != std::string::npos; + bool is_edit_respawn = spawn_command.find("editrespawn") != std::string::npos; + bool is_make_group = spawn_command.find("makegroup") != std::string::npos; + bool is_make_npc = spawn_command.find("makenpc") != std::string::npos; + bool is_move_spawn = spawn_command.find("movespawn") != std::string::npos; + bool is_set_version = spawn_command.find("setversion") != std::string::npos; + if ( + !is_add_entry && + !is_add_spawn && + !is_clear_box && + !is_delete_spawn && + !is_edit_box && + !is_edit_respawn && + !is_make_group && + !is_make_npc && + !is_move_spawn && + !is_set_version + ) { + c->Message( + Chat::White, + "Usage: #advnpcspawn addentry [Spawngroup ID] [NPC ID] [Spawn Chance] - Adds a new Spawngroup Entry" + ); + c->Message( + Chat::White, + "Usage: #advnpcspawn addspawn [Spawngroup ID] - Adds a new Spawngroup Entry from an existing Spawngroup" + ); + c->Message(Chat::White, "Usage: #advnpcspawn clearbox [Spawngroup ID] - Clears the roambox of a Spawngroup"); + c->Message(Chat::White, "Usage: #advnpcspawn deletespawn - Deletes a Spawngroup"); + c->Message( + Chat::White, + "Usage: #advnpcspawn editbox [Spawngroup ID] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay] - Edit the roambox of a Spawngroup" + ); + c->Message( + Chat::White, + "Usage: #advnpcspawn editrespawn [Respawn Timer] [Variance] - Edit the Respawn Timer of a Spawngroup" + ); + c->Message( + Chat::White, + "Usage: #advnpcspawn makegroup [Spawn Group Name] [Spawn Limit] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay] - Makes a new Spawngroup" + ); + c->Message(Chat::White, "Usage: #advnpcspawn makenpc - Makes a new NPC"); + c->Message(Chat::White, "Usage: #advnpcspawn movespawn - Moves a Spawngroup to your current location"); + c->Message(Chat::White, "Usage: #advnpcspawn setversion [Version] - Sets a Spawngroup's Version"); + return; + } + + + if (is_add_entry) { + if (arguments < 4) { + c->Message(Chat::White, "Usage: #advnpcspawn addentry [Spawngroup ID] [NPC ID] [Spawn Chance]"); + return; + } + + auto spawngroup_id = std::stoi(sep->arg[2]); + auto npc_id = std::stoi(sep->arg[2]); + auto spawn_chance = std::stoi(sep->arg[2]); + + std::string query = fmt::format( + SQL( + INSERT INTO spawnentry(spawngroupID, npcID, chance) + VALUES({}, {}, {}) + ), + spawngroup_id, + npc_id, + spawn_chance + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Failed to add entry to Spawngroup."); + return; + } + + c->Message( + Chat::White, + fmt::format( + "{} ({}) added to Spawngroup {}, its spawn chance is {}%%.", + database.GetCleanNPCNameByID(npc_id), + npc_id, + spawngroup_id, + spawn_chance + ).c_str() + ); + return; + } + else if (is_add_spawn) { + content_db.NPCSpawnDB( + NPCSpawnTypes::AddSpawnFromSpawngroup, + zone->GetShortName(), + zone->GetInstanceVersion(), + c, + 0, + std::stoi(sep->arg[2]) + ); + c->Message( + Chat::White, + fmt::format( + "Spawn Added | Added spawn from Spawngroup ID {}.", + std::stoi(sep->arg[2]) + ).c_str() + ); + return; + } + else if (is_clear_box) { + if (arguments != 2 || !sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #advnpcspawn clearbox [Spawngroup ID]"); + return; + } + + std::string query = fmt::format( + "UPDATE spawngroup SET dist = 0, min_x = 0, max_x = 0, min_y = 0, max_y = 0, delay = 0 WHERE id = {}", + std::stoi(sep->arg[2]) + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Failed to clear Spawngroup box."); + return; + } + + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Roambox Cleared | Delay: 0 Distance: 0.00", + std::stoi(sep->arg[2]) + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Roambox Cleared | Minimum X: 0.00 Maximum X: 0.00", + std::stoi(sep->arg[2]) + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Roambox Cleared | Minimum Y: 0.00 Maximum Y: 0.00", + std::stoi(sep->arg[2]) + ).c_str() + ); + return; + } + else if (is_delete_spawn) { + if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; + } + + NPC *target = c->GetTarget()->CastToNPC(); + Spawn2 *spawn2 = target->respawn2; + if (!spawn2) { + c->Message(Chat::White, "Failed to delete spawn because NPC has no Spawn2."); + return; + } + + auto spawn2_id = spawn2->GetID(); + std::string query = fmt::format( + "DELETE FROM spawn2 WHERE id = {}", + spawn2_id + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Failed to delete spawn."); + return; + } + + c->Message( + Chat::White, + fmt::format( + "Spawn2 {} Deleted | Name: {} ({})", + spawn2_id, + target->GetCleanName(), + target->GetID() + ).c_str() + ); + target->Depop(false); + return; + } + else if (is_edit_box) { + if ( + arguments != 8 || + !sep->IsNumber(3) || + !sep->IsNumber(4) || + !sep->IsNumber(5) || + !sep->IsNumber(6) || + !sep->IsNumber(7) || + !sep->IsNumber(8) + ) { + c->Message( + Chat::White, + "Usage: #advnpcspawn editbox [Spawngroup ID] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay]" + ); + return; + } + auto spawngroup_id = std::stoi(sep->arg[2]); + auto distance = std::stof(sep->arg[3]); + auto minimum_x = std::stof(sep->arg[4]); + auto maximum_x = std::stof(sep->arg[5]); + auto minimum_y = std::stof(sep->arg[6]); + auto maximum_y = std::stof(sep->arg[7]); + auto delay = std::stoi(sep->arg[8]); + + std::string query = fmt::format( + "UPDATE spawngroup SET dist = {:.2f}, min_x = {:.2f}, max_x = {:.2f}, max_y = {:.2f}, min_y = {:.2f}, delay = {} WHERE id = {}", + distance, + minimum_x, + maximum_x, + minimum_y, + maximum_y, + delay, + spawngroup_id + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Failed to edit Spawngroup box."); + return; + } + + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Roambox Edited | Delay: {} Distance: {:.2f}", + spawngroup_id, + delay, + distance + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Roambox Edited | Minimum X: {:.2f} Maximum X: {:.2f}", + spawngroup_id, + minimum_x, + maximum_x + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Roambox Edited | Minimum Y: {:.2f} Maximum Y: {:.2f}", + spawngroup_id, + minimum_y, + maximum_y + ).c_str() + ); + return; + } + else if (is_edit_respawn) { + if (arguments < 2 || !sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #advnpcspawn editrespawn [Respawn Timer] [Variance]"); + return; + } + + if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; + } + + NPC *target = c->GetTarget()->CastToNPC(); + Spawn2 *spawn2 = target->respawn2; + if (!spawn2) { + c->Message(Chat::White, "Failed to edit respawn because NPC has no Spawn2."); + return; + } + + auto spawn2_id = spawn2->GetID(); + uint32 respawn_timer = std::stoi(sep->arg[2]); + uint32 variance = ( + sep->IsNumber(3) ? + std::stoi(sep->arg[3]) : + spawn2->GetVariance() + ); + std::string query = fmt::format( + "UPDATE spawn2 SET respawntime = {}, variance = {} WHERE id = {}", + respawn_timer, + variance, + spawn2_id + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Failed to edit respawn."); + return; + } + + c->Message( + Chat::White, + fmt::format( + "Spawn2 {} Respawn Modified | Name: {} ({}) Respawn Timer: {} Variance: {}", + spawn2_id, + target->GetCleanName(), + target->GetID(), + respawn_timer, + variance + ).c_str() + ); + spawn2->SetRespawnTimer(respawn_timer); + spawn2->SetVariance(variance); + return; + } + else if (is_make_group) { + if ( + arguments != 9 || + !sep->IsNumber(3) || + !sep->IsNumber(4) || + !sep->IsNumber(5) || + !sep->IsNumber(6) || + !sep->IsNumber(7) || + !sep->IsNumber(8) || + !sep->IsNumber(9) + ) { + c->Message( + Chat::White, + "Usage: #advncspawn makegroup [Spawn Group Name] [Spawn Limit] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay]" + ); + return; + } + std::string spawngroup_name = sep->arg[2]; + auto spawn_limit = std::stoi(sep->arg[3]); + auto distance = std::stof(sep->arg[4]); + auto minimum_x = std::stof(sep->arg[5]); + auto maximum_x = std::stof(sep->arg[6]); + auto minimum_y = std::stof(sep->arg[7]); + auto maximum_y = std::stof(sep->arg[8]); + auto delay = std::stoi(sep->arg[9]); + + std::string query = fmt::format( + "INSERT INTO spawngroup" + "(name, spawn_limit, dist, min_x, max_x, min_y, max_y, delay)" + "VALUES ('{}', {}, {:.2f}, {:.2f}, {:.2f}, {:.2f}, {:.2f}, {})", + spawngroup_name, + spawn_limit, + distance, + minimum_x, + maximum_x, + minimum_y, + maximum_y, + delay + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Failed to make Spawngroup."); + return; + } + + auto spawngroup_id = results.LastInsertedID(); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Created | Name: {} Spawn Limit: {}", + spawngroup_id, + spawngroup_name, + spawn_limit + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Created | Delay: {} Distance: {:.2f}", + spawngroup_id, + delay, + distance + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Created | Minimum X: {:.2f} Maximum X: {:.2f}", + spawngroup_id, + minimum_x, + maximum_x + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Created | Minimum Y: {:.2f} Maximum Y: {:.2f}", + spawngroup_id, + minimum_y, + maximum_y + ).c_str() + ); + return; + } + else if (is_make_npc) { + if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; + } + + NPC *target = c->GetTarget()->CastToNPC(); + content_db.NPCSpawnDB( + NPCSpawnTypes::CreateNewNPC, + zone->GetShortName(), + zone->GetInstanceVersion(), + c, + target + ); + return; + } + else if (is_move_spawn) { + if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; + } + + NPC *target = c->GetTarget()->CastToNPC(); + Spawn2 *spawn2 = target->respawn2; + if (!spawn2) { + c->Message(Chat::White, "Failed to move spawn because NPC has no Spawn2."); + return; + } + + auto client_position = c->GetPosition(); + auto spawn2_id = spawn2->GetID(); + std::string query = fmt::format( + "UPDATE spawn2 SET x = {:.2f}, y = {:.2f}, z = {:.2f}, heading = {:.2f} WHERE id = {}", + client_position.x, + client_position.y, + client_position.z, + client_position.w, + spawn2_id + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Failed to move spawn."); + return; + } + + c->Message( + Chat::White, + fmt::format( + "Spawn2 {} Moved | Name: {} ({})", + spawn2_id, + target->GetCleanName(), + target->GetID() + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "Spawn2 {} Moved | XYZ: {}, {}, {} Heading: {}", + spawn2_id, + client_position.x, + client_position.y, + client_position.z, + client_position.w + ).c_str() + ); + target->GMMove( + client_position.x, + client_position.y, + client_position.z, + client_position.w + ); + return; + } + else if (is_set_version) { + if (arguments != 2 || !sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #advnpcspawn setversion [Version]"); + return; + } + + if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; + } + + NPC *target = c->GetTarget()->CastToNPC(); + auto version = std::stoi(sep->arg[2]); + std::string query = fmt::format( + "UPDATE spawn2 SET version = {} WHERE spawngroupID = {}", + version, + target->GetSpawnGroupId() + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Failed to set version."); + return; + } + + c->Message( + Chat::White, + fmt::format( + "Spawngroup {} Version Modified | Name: {} ({}) Version: {}", + target->GetSpawnGroupId(), + target->GetCleanName(), + target->GetID(), + version + ).c_str() + ); + target->Depop(false); + return; + } +} + diff --git a/zone/gm_commands/aggro.cpp b/zone/gm_commands/aggro.cpp new file mode 100755 index 000000000..9b8307cb2 --- /dev/null +++ b/zone/gm_commands/aggro.cpp @@ -0,0 +1,21 @@ +#include "../client.h" + +void command_aggro(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == nullptr || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "Error: you must have an NPC target."); + return; + } + float d = atof(sep->arg[1]); + if (d == 0.0f) { + c->Message(Chat::Red, "Error: distance argument required."); + return; + } + bool verbose = false; + if (sep->arg[2][0] == '-' && sep->arg[2][1] == 'v' && sep->arg[2][2] == '\0') { + verbose = true; + } + + entity_list.DescribeAggro(c, c->GetTarget()->CastToNPC(), d, verbose); +} + diff --git a/zone/gm_commands/aggrozone.cpp b/zone/gm_commands/aggrozone.cpp new file mode 100755 index 000000000..afdf4ad64 --- /dev/null +++ b/zone/gm_commands/aggrozone.cpp @@ -0,0 +1,19 @@ +#include "../client.h" + +void command_aggrozone(Client *c, const Seperator *sep) +{ + if (!c) { + return; + } + + Mob *m = c->CastToMob(); + + if (!m) { + return; + } + + uint32 hate = atoi(sep->arg[1]); //should default to 0 if we don't enter anything + entity_list.AggroZone(m, hate); + c->Message(Chat::White, "Train to you! Last chance to go invulnerable..."); +} + diff --git a/zone/gm_commands/ai.cpp b/zone/gm_commands/ai.cpp new file mode 100755 index 000000000..93f9a057f --- /dev/null +++ b/zone/gm_commands/ai.cpp @@ -0,0 +1,139 @@ +#include "../client.h" + +void command_ai(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + + if (strcasecmp(sep->arg[1], "factionid") == 0) { + if (target && sep->IsNumber(2)) { + if (target->IsNPC()) { + target->CastToNPC()->SetNPCFactionID(atoi(sep->arg[2])); + } + else { + c->Message(Chat::White, "%s is not an NPC.", target->GetName()); + } + } + else { + c->Message(Chat::White, "Usage: (targeted) #ai factionid [factionid]"); + } + } + else if (strcasecmp(sep->arg[1], "spellslist") == 0) { + if (target && sep->IsNumber(2) && atoi(sep->arg[2]) >= 0) { + if (target->IsNPC()) { + target->CastToNPC()->AI_AddNPCSpells(atoi(sep->arg[2])); + } + else { + c->Message(Chat::White, "%s is not an NPC.", target->GetName()); + } + } + else { + c->Message(Chat::White, "Usage: (targeted) #ai spellslist [npc_spells_id]"); + } + } + else if (strcasecmp(sep->arg[1], "con") == 0) { + if (target && sep->arg[2][0] != 0) { + Mob *tar2 = entity_list.GetMob(sep->arg[2]); + if (tar2) { + c->Message( + Chat::White, + "%s considering %s: %i", + target->GetName(), + tar2->GetName(), + tar2->GetReverseFactionCon(target)); + } + else { + c->Message(Chat::White, "Error: %s not found.", sep->arg[2]); + } + } + else { + c->Message(Chat::White, "Usage: (targeted) #ai con [mob name]"); + } + } + else if (strcasecmp(sep->arg[1], "guard") == 0) { + if (target && target->IsNPC()) { + target->CastToNPC()->SaveGuardSpot(target->GetPosition()); + } + else { + c->Message( + Chat::White, + "Usage: (targeted) #ai guard - sets npc to guard the current location (use #summon to move)" + ); + } + } + else if (strcasecmp(sep->arg[1], "roambox") == 0) { + if (target && target->IsAIControlled() && target->IsNPC()) { + if ((sep->argnum == 6 || sep->argnum == 7 || sep->argnum == 8) && sep->IsNumber(2) && sep->IsNumber(3) && + sep->IsNumber(4) && sep->IsNumber(5) && sep->IsNumber(6)) { + uint32 tmp = 2500; + uint32 tmp2 = 2500; + if (sep->IsNumber(7)) { + tmp = atoi(sep->arg[7]); + } + if (sep->IsNumber(8)) { + tmp2 = atoi(sep->arg[8]); + } + target->CastToNPC()->AI_SetRoambox( + atof(sep->arg[2]), + atof(sep->arg[3]), + atof(sep->arg[4]), + atof(sep->arg[5]), + atof(sep->arg[6]), + tmp, + tmp2 + ); + } + else if ((sep->argnum == 3 || sep->argnum == 4) && sep->IsNumber(2) && sep->IsNumber(3)) { + uint32 tmp = 2500; + uint32 tmp2 = 2500; + if (sep->IsNumber(4)) { + tmp = atoi(sep->arg[4]); + } + if (sep->IsNumber(5)) { + tmp2 = atoi(sep->arg[5]); + } + target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), tmp, tmp2); + } + else { + c->Message(Chat::White, "Usage: #ai roambox dist max_x min_x max_y min_y [delay] [mindelay]"); + c->Message(Chat::White, "Usage: #ai roambox dist roamdist [delay] [mindelay]"); + } + } + else { + c->Message(Chat::White, "You need a AI NPC targeted"); + } + } + else if (strcasecmp(sep->arg[1], "stop") == 0 && c->Admin() >= commandToggleAI) { + if (target) { + if (target->IsAIControlled()) { + target->AI_Stop(); + } + else { + c->Message(Chat::White, "Error: Target is not AI controlled"); + } + } + else { + c->Message(Chat::White, "Usage: Target a Mob with AI enabled and use this to turn off their AI."); + } + } + else if (strcasecmp(sep->arg[1], "start") == 0 && c->Admin() >= commandToggleAI) { + if (target) { + if (!target->IsAIControlled()) { + target->AI_Start(); + } + else { + c->Message(Chat::White, "Error: Target is already AI controlled"); + } + } + else { + c->Message(Chat::White, "Usage: Target a Mob with AI disabled and use this to turn on their AI."); + } + } + else { + c->Message(Chat::White, "#AI Sub-commands"); + c->Message(Chat::White, " factionid"); + c->Message(Chat::White, " spellslist"); + c->Message(Chat::White, " con"); + c->Message(Chat::White, " guard"); + } +} + diff --git a/zone/gm_commands/appearance.cpp b/zone/gm_commands/appearance.cpp new file mode 100755 index 000000000..4f28fc50f --- /dev/null +++ b/zone/gm_commands/appearance.cpp @@ -0,0 +1,26 @@ +#include "../client.h" + +void command_appearance(Client *c, const Seperator *sep) +{ + Mob *t = c->CastToMob(); + + // sends any appearance packet + // Dev debug command, for appearance types + if (sep->arg[2][0] == 0) { + c->Message(Chat::White, "Usage: #appearance type value"); + } + else { + if ((c->GetTarget())) { + t = c->GetTarget(); + } + t->SendAppearancePacket(atoi(sep->arg[1]), atoi(sep->arg[2])); + c->Message( + Chat::White, + "Sending appearance packet: target=%s, type=%s, value=%s", + t->GetName(), + sep->arg[1], + sep->arg[2] + ); + } +} + diff --git a/zone/gm_commands/attack.cpp b/zone/gm_commands/attack.cpp new file mode 100755 index 000000000..c4a483aa9 --- /dev/null +++ b/zone/gm_commands/attack.cpp @@ -0,0 +1,18 @@ +#include "../client.h" + +void command_attack(Client *c, const Seperator *sep) +{ + if (c->GetTarget() && c->GetTarget()->IsNPC() && sep->arg[1] != 0) { + Mob *sictar = entity_list.GetMob(sep->argplus[1]); + if (sictar) { + c->GetTarget()->CastToNPC()->AddToHateList(sictar, 1, 0); + } + else { + c->Message(Chat::White, "Error: %s not found", sep->arg[1]); + } + } + else { + c->Message(Chat::White, "Usage: (needs NPC targeted) #attack targetname"); + } +} + diff --git a/zone/gm_commands/augmentitem.cpp b/zone/gm_commands/augmentitem.cpp new file mode 100755 index 000000000..81b5523aa --- /dev/null +++ b/zone/gm_commands/augmentitem.cpp @@ -0,0 +1,18 @@ +#include "../client.h" +#include "../object.h" + +void command_augmentitem(Client *c, const Seperator *sep) +{ + if (!c) { + return; + } + + auto in_augment = new AugmentItem_Struct[sizeof(AugmentItem_Struct)]; + in_augment->container_slot = 1000; // + in_augment->augment_slot = -1; + if (c->GetTradeskillObject() != nullptr) { + Object::HandleAugmentation(c, in_augment, c->GetTradeskillObject()); + } + safe_delete_array(in_augment); +} + diff --git a/zone/gm_commands/ban.cpp b/zone/gm_commands/ban.cpp new file mode 100755 index 000000000..3582aca19 --- /dev/null +++ b/zone/gm_commands/ban.cpp @@ -0,0 +1,72 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_ban(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0 || sep->arg[2][0] == 0) { + c->Message(Chat::White, "Usage: #ban "); + return; + } + + auto account_id = database.GetAccountIDByChar(sep->arg[1]); + + std::string message; + int i = 2; + while (1) { + if (sep->arg[i][0] == 0) { + break; + } + + if (message.length() > 0) { + message.push_back(' '); + } + + message += sep->arg[i]; + ++i; + } + + if (message.length() == 0) { + c->Message(Chat::White, "Usage: #ban "); + return; + } + + if (account_id == 0) { + c->Message(Chat::Red, "Character does not exist."); + return; + } + + std::string query = StringFormat( + "UPDATE account SET status = -2, ban_reason = '%s' " + "WHERE id = %i", EscapeString(message).c_str(), account_id + ); + auto results = database.QueryDatabase(query); + + c->Message( + Chat::Red, + "Account number %i with the character %s has been banned with message: \"%s\"", + account_id, + sep->arg[1], + message.c_str()); + + ServerPacket flagUpdatePack(ServerOP_FlagUpdate, 6); + *((uint32 *) &flagUpdatePack.pBuffer[0]) = account_id; + *((int16 *) &flagUpdatePack.pBuffer[4]) = -2; + worldserver.SendPacket(&flagUpdatePack); + + Client *client = nullptr; + client = entity_list.GetClientByName(sep->arg[1]); + if (client) { + client->WorldKick(); + return; + } + + ServerPacket kickPlayerPack(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); + ServerKickPlayer_Struct *skp = (ServerKickPlayer_Struct *) kickPlayerPack.pBuffer; + strcpy(skp->adminname, c->GetName()); + strcpy(skp->name, sep->arg[1]); + skp->adminrank = c->Admin(); + worldserver.SendPacket(&kickPlayerPack); +} + diff --git a/zone/gm_commands/beard.cpp b/zone/gm_commands/beard.cpp new file mode 100755 index 000000000..b43b7b9d4 --- /dev/null +++ b/zone/gm_commands/beard.cpp @@ -0,0 +1,37 @@ +#include "../client.h" + +void command_beard(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #beard [number of beard style]"); + } + else if (!target) { + c->Message(Chat::White, "Error: this command requires a target"); + } + else { + uint16 Race = target->GetRace(); + uint8 Gender = target->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = 0xFF; + uint8 HairColor = target->GetHairColor(); + uint8 BeardColor = target->GetBeardColor(); + uint8 EyeColor1 = target->GetEyeColor1(); + uint8 EyeColor2 = target->GetEyeColor2(); + uint8 HairStyle = target->GetHairStyle(); + uint8 LuclinFace = target->GetLuclinFace(); + uint8 Beard = atoi(sep->arg[1]); + uint32 DrakkinHeritage = target->GetDrakkinHeritage(); + uint32 DrakkinTattoo = target->GetDrakkinTattoo(); + uint32 DrakkinDetails = target->GetDrakkinDetails(); + + target->SendIllusionPacket( + Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails + ); + + c->Message(Chat::White, "Beard = %i", atoi(sep->arg[1])); + } +} + diff --git a/zone/gm_commands/beardcolor.cpp b/zone/gm_commands/beardcolor.cpp new file mode 100755 index 000000000..85eccf390 --- /dev/null +++ b/zone/gm_commands/beardcolor.cpp @@ -0,0 +1,37 @@ +#include "../client.h" + +void command_beardcolor(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #beardcolor [number of beard color]"); + } + else if (!target) { + c->Message(Chat::White, "Error: this command requires a target"); + } + else { + uint16 Race = target->GetRace(); + uint8 Gender = target->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = 0xFF; + uint8 HairColor = target->GetHairColor(); + uint8 BeardColor = atoi(sep->arg[1]); + uint8 EyeColor1 = target->GetEyeColor1(); + uint8 EyeColor2 = target->GetEyeColor2(); + uint8 HairStyle = target->GetHairStyle(); + uint8 LuclinFace = target->GetLuclinFace(); + uint8 Beard = target->GetBeard(); + uint32 DrakkinHeritage = target->GetDrakkinHeritage(); + uint32 DrakkinTattoo = target->GetDrakkinTattoo(); + uint32 DrakkinDetails = target->GetDrakkinDetails(); + + target->SendIllusionPacket( + Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails + ); + + c->Message(Chat::White, "Beard Color = %i", atoi(sep->arg[1])); + } +} + diff --git a/zone/gm_commands/bestz.cpp b/zone/gm_commands/bestz.cpp new file mode 100755 index 000000000..809e63ec1 --- /dev/null +++ b/zone/gm_commands/bestz.cpp @@ -0,0 +1,89 @@ +#include "../client.h" +#include "../water_map.h" + +void command_bestz(Client *c, const Seperator *sep) +{ + if (zone->zonemap == nullptr) { + c->Message(Chat::White, "Map not loaded for this zone"); + } + else { + glm::vec3 me; + me.x = c->GetX(); + me.y = c->GetY(); + me.z = c->GetZ() + (c->GetSize() == 0.0 ? 6 : c->GetSize()) * HEAD_POSITION; + glm::vec3 hit; + glm::vec3 bme(me); + bme.z -= 500; + + float best_z = zone->zonemap->FindBestZ(me, &hit); + + if (best_z != BEST_Z_INVALID) { + c->Message(Chat::White, "Z is %.3f at (%.3f, %.3f).", best_z, me.x, me.y); + } + else { + c->Message(Chat::White, "Found no Z."); + } + } + + if (zone->watermap == nullptr) { + c->Message(Chat::White, "Water Region Map not loaded for this zone"); + } + else { + WaterRegionType RegionType; + float z; + + if (c->GetTarget()) { + z = c->GetTarget()->GetZ(); + auto position = glm::vec3(c->GetTarget()->GetX(), c->GetTarget()->GetY(), z); + RegionType = zone->watermap->ReturnRegionType(position); + c->Message(Chat::White, "InWater returns %d", zone->watermap->InWater(position)); + c->Message(Chat::White, "InLava returns %d", zone->watermap->InLava(position)); + + } + else { + z = c->GetZ(); + auto position = glm::vec3(c->GetX(), c->GetY(), z); + RegionType = zone->watermap->ReturnRegionType(position); + c->Message(Chat::White, "InWater returns %d", zone->watermap->InWater(position)); + c->Message(Chat::White, "InLava returns %d", zone->watermap->InLava(position)); + + } + + switch (RegionType) { + case RegionTypeNormal: { + c->Message(Chat::White, "There is nothing special about the region you are in!"); + break; + } + case RegionTypeWater: { + c->Message(Chat::White, "You/your target are in Water."); + break; + } + case RegionTypeLava: { + c->Message(Chat::White, "You/your target are in Lava."); + break; + } + case RegionTypeVWater: { + c->Message(Chat::White, "You/your target are in VWater (Icy Water?)."); + break; + } + case RegionTypePVP: { + c->Message(Chat::White, "You/your target are in a pvp enabled area."); + break; + } + case RegionTypeSlime: { + c->Message(Chat::White, "You/your target are in slime."); + break; + } + case RegionTypeIce: { + c->Message(Chat::White, "You/your target are in ice."); + break; + } + default: + c->Message(Chat::White, "You/your target are in an unknown region type."); + } + } + + +} + + diff --git a/zone/gm_commands/bind.cpp b/zone/gm_commands/bind.cpp new file mode 100755 index 000000000..216985ae3 --- /dev/null +++ b/zone/gm_commands/bind.cpp @@ -0,0 +1,17 @@ +#include "../client.h" + +void command_bind(Client *c, const Seperator *sep) +{ + if (c->GetTarget() != 0) { + if (c->GetTarget()->IsClient()) { + c->GetTarget()->CastToClient()->SetBindPoint(); + } + else { + c->Message(Chat::White, "Error: target not a Player"); + } + } + else { + c->SetBindPoint(); + } +} + diff --git a/zone/gm_commands/camerashake.cpp b/zone/gm_commands/camerashake.cpp new file mode 100755 index 000000000..39a6ebcfd --- /dev/null +++ b/zone/gm_commands/camerashake.cpp @@ -0,0 +1,24 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_camerashake(Client *c, const Seperator *sep) +{ + if (c) { + if (sep->arg[1][0] && sep->arg[2][0]) { + auto pack = new ServerPacket(ServerOP_CameraShake, sizeof(ServerCameraShake_Struct)); + ServerCameraShake_Struct *scss = (ServerCameraShake_Struct *) pack->pBuffer; + scss->duration = atoi(sep->arg[1]); + scss->intensity = atoi(sep->arg[2]); + worldserver.SendPacket(pack); + c->Message(Chat::Red, "Successfully sent the packet to world! Shake it, world, shake it!"); + safe_delete(pack); + } + else { + c->Message(Chat::Red, "Usage -- #camerashake [duration], [intensity [1-10])"); + } + } + return; +} + diff --git a/zone/gm_commands/castspell.cpp b/zone/gm_commands/castspell.cpp new file mode 100755 index 000000000..b4410084d --- /dev/null +++ b/zone/gm_commands/castspell.cpp @@ -0,0 +1,77 @@ +#include "../client.h" + +void command_castspell(Client *c, const Seperator *sep) +{ + if (SPDAT_RECORDS <= 0) { + c->Message(Chat::White, "Spells not loaded."); + return; + } + + Mob *target = c; + if (c->GetTarget()) { + target = c->GetTarget(); + } + + if (!sep->IsNumber(1)) { + c->Message( + Chat::White, + "Usage: #castspell [Spell ID] [Instant (0 = False, 1 = True, Default is 1 if Unused)]" + ); + } + else { + uint16 spell_id = std::stoul(sep->arg[1]); + + if (CastRestrictedSpell(spell_id) && c->Admin() < commandCastSpecials) { + c->Message(Chat::Red, "Unable to cast spell."); + } + else if (spell_id >= SPDAT_RECORDS) { + c->Message(Chat::White, "Invalid Spell ID."); + } + else { + bool instant_cast = (c->Admin() >= commandInstacast ? true : false); + if (instant_cast && sep->IsNumber(2)) { + instant_cast = std::stoi(sep->arg[2]) ? true : false; + c->Message(Chat::White, fmt::format("{}", std::stoi(sep->arg[2])).c_str()); + } + + if (c->Admin() >= commandInstacast && instant_cast) { + c->SpellFinished( + spell_id, + target, + EQ::spells::CastingSlot::Item, + 0, + -1, + spells[spell_id].resist_difficulty + ); + } + else { + c->CastSpell(spell_id, target->GetID(), EQ::spells::CastingSlot::Item, spells[spell_id].cast_time); + } + + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "Cast {} ({}) on {}{}.", + GetSpellName(spell_id), + spell_id, + target->GetCleanName(), + instant_cast ? " instantly" : "" + ).c_str() + ); + } + else { + c->Message( + Chat::White, + fmt::format( + "Cast {} ({}) on yourself{}.", + GetSpellName(spell_id), + spell_id, + instant_cast ? " instantly" : "" + ).c_str() + ); + } + } + } +} + diff --git a/zone/gm_commands/chat.cpp b/zone/gm_commands/chat.cpp new file mode 100755 index 000000000..4b511ec84 --- /dev/null +++ b/zone/gm_commands/chat.cpp @@ -0,0 +1,15 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_chat(Client *c, const Seperator *sep) +{ + if (sep->arg[2][0] == 0) { + c->Message(Chat::White, "Usage: #chat [channum] [message]"); + } + else if (!worldserver.SendChannelMessage(0, 0, (uint8) atoi(sep->arg[1]), 0, 0, 100, sep->argplus[2])) { + c->Message(Chat::White, "Error: World server disconnected"); + } +} + diff --git a/zone/gm_commands/checklos.cpp b/zone/gm_commands/checklos.cpp new file mode 100755 index 000000000..fa54a56ef --- /dev/null +++ b/zone/gm_commands/checklos.cpp @@ -0,0 +1,20 @@ +#include "../client.h" + +void command_checklos(Client *c, const Seperator *sep) +{ + if (!c->GetTarget()) { + c->Message(Chat::White, "You must have a target to use this command."); + } + + bool has_los = c->CheckLosFN(c->GetTarget()); + c->Message( + Chat::White, + fmt::format( + "You {}have line of sight to {} ({}).", + has_los ? "" : "do not ", + c->GetTarget()->GetCleanName(), + c->GetTarget()->GetID() + ).c_str() + ); +} + diff --git a/zone/gm_commands/copycharacter.cpp b/zone/gm_commands/copycharacter.cpp new file mode 100755 index 000000000..136093005 --- /dev/null +++ b/zone/gm_commands/copycharacter.cpp @@ -0,0 +1,34 @@ +#include "../client.h" + +void command_copycharacter(Client *c, const Seperator *sep) +{ + if (sep->argnum < 3) { + c->Message( + Chat::White, + "Usage: [source_character_name] [destination_character_name] [destination_account_name]" + ); + return; + } + + std::string source_character_name = sep->arg[1]; + std::string destination_character_name = sep->arg[2]; + std::string destination_account_name = sep->arg[3]; + + bool result = database.CopyCharacter( + source_character_name, + destination_character_name, + destination_account_name + ); + + c->Message( + Chat::Yellow, + fmt::format( + "Character Copy [{}] to [{}] via account [{}] [{}]", + source_character_name, + destination_character_name, + destination_account_name, + result ? "Success" : "Failed" + ).c_str() + ); +} + diff --git a/zone/gm_commands/corpse.cpp b/zone/gm_commands/corpse.cpp new file mode 100755 index 000000000..739c3458a --- /dev/null +++ b/zone/gm_commands/corpse.cpp @@ -0,0 +1,168 @@ +#include "../client.h" +#include "../corpse.h" + +void command_corpse(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + + if (strcasecmp(sep->arg[1], "DeletePlayerCorpses") == 0 && c->Admin() >= commandEditPlayerCorpses) { + int32 tmp = entity_list.DeletePlayerCorpses(); + if (tmp >= 0) { + c->Message(Chat::White, "%i corpses deleted.", tmp); + } + else { + c->Message(Chat::White, "DeletePlayerCorpses Error #%i", tmp); + } + } + else if (strcasecmp(sep->arg[1], "delete") == 0) { + if (target == 0 || !target->IsCorpse()) { + c->Message(Chat::White, "Error: Target the corpse you wish to delete"); + } + else if (target->IsNPCCorpse()) { + + c->Message(Chat::White, "Depoping %s.", target->GetName()); + target->CastToCorpse()->Delete(); + } + else if (c->Admin() >= commandEditPlayerCorpses) { + c->Message(Chat::White, "Deleting %s.", target->GetName()); + target->CastToCorpse()->Delete(); + } + else { + c->Message(Chat::White, "Insufficient status to delete player corpse."); + } + } + else if (strcasecmp(sep->arg[1], "ListNPC") == 0) { + entity_list.ListNPCCorpses(c); + } + else if (strcasecmp(sep->arg[1], "ListPlayer") == 0) { + entity_list.ListPlayerCorpses(c); + } + else if (strcasecmp(sep->arg[1], "DeleteNPCCorpses") == 0) { + int32 tmp = entity_list.DeleteNPCCorpses(); + if (tmp >= 0) { + c->Message(Chat::White, "%d corpses deleted.", tmp); + } + else { + c->Message(Chat::White, "DeletePlayerCorpses Error #%d", tmp); + } + } + else if (strcasecmp(sep->arg[1], "charid") == 0 && c->Admin() >= commandEditPlayerCorpses) { + if (target == 0 || !target->IsPlayerCorpse()) { + c->Message(Chat::White, "Error: Target must be a player corpse."); + } + else if (!sep->IsNumber(2)) { + c->Message(Chat::White, "Error: charid must be a number."); + } + else { + c->Message( + Chat::White, + "Setting CharID=%u on PlayerCorpse '%s'", + target->CastToCorpse()->SetCharID(atoi(sep->arg[2])), + target->GetName()); + } + } + else if (strcasecmp(sep->arg[1], "ResetLooter") == 0) { + if (target == 0 || !target->IsCorpse()) { + c->Message(Chat::White, "Error: Target the corpse you wish to reset"); + } + else { + target->CastToCorpse()->ResetLooter(); + } + } + else if (strcasecmp(sep->arg[1], "RemoveCash") == 0) { + if (target == 0 || !target->IsCorpse()) { + c->Message(Chat::White, "Error: Target the corpse you wish to remove the cash from"); + } + else if (!target->IsPlayerCorpse() || c->Admin() >= commandEditPlayerCorpses) { + c->Message(Chat::White, "Removing Cash from %s.", target->GetName()); + target->CastToCorpse()->RemoveCash(); + } + else { + c->Message(Chat::White, "Insufficient status to modify player corpse."); + } + } + else if (strcasecmp(sep->arg[1], "InspectLoot") == 0) { + if (target == 0 || !target->IsCorpse()) { + c->Message(Chat::White, "Error: Target must be a corpse."); + } + else { + target->CastToCorpse()->QueryLoot(c); + } + } + else if (strcasecmp(sep->arg[1], "lock") == 0) { + if (target == 0 || !target->IsCorpse()) { + c->Message(Chat::White, "Error: Target must be a corpse."); + } + else { + target->CastToCorpse()->Lock(); + c->Message(Chat::White, "Locking %s...", target->GetName()); + } + } + else if (strcasecmp(sep->arg[1], "unlock") == 0) { + if (target == 0 || !target->IsCorpse()) { + c->Message(Chat::White, "Error: Target must be a corpse."); + } + else { + target->CastToCorpse()->UnLock(); + c->Message(Chat::White, "Unlocking %s...", target->GetName()); + } + } + else if (strcasecmp(sep->arg[1], "depop") == 0) { + if (target == 0 || !target->IsPlayerCorpse()) { + c->Message(Chat::White, "Error: Target must be a player corpse."); + } + else if (c->Admin() >= commandEditPlayerCorpses && target->IsPlayerCorpse()) { + c->Message(Chat::White, "Depoping %s.", target->GetName()); + target->CastToCorpse()->DepopPlayerCorpse(); + if (!sep->arg[2][0] || atoi(sep->arg[2]) != 0) { + target->CastToCorpse()->Bury(); + } + } + else { + c->Message(Chat::White, "Insufficient status to depop player corpse."); + } + } + else if (strcasecmp(sep->arg[1], "depopall") == 0) { + if (target == 0 || !target->IsClient()) { + c->Message(Chat::White, "Error: Target must be a player."); + } + else if (c->Admin() >= commandEditPlayerCorpses && target->IsClient()) { + c->Message(Chat::White, "Depoping %s\'s corpses.", target->GetName()); + target->CastToClient()->DepopAllCorpses(); + if (!sep->arg[2][0] || atoi(sep->arg[2]) != 0) { + target->CastToClient()->BuryPlayerCorpses(); + } + } + else { + c->Message(Chat::White, "Insufficient status to depop player corpse."); + } + + } + else if (strcasecmp(sep->arg[1], "moveallgraveyard") == 0) { + int count = entity_list.MovePlayerCorpsesToGraveyard(true); + c->Message(Chat::White, "Moved [%d] player corpse(s) to zone graveyard", count); + } + else if (sep->arg[1][0] == 0 || strcasecmp(sep->arg[1], "help") == 0) { + c->Message(Chat::White, "#Corpse Sub-Commands:"); + c->Message(Chat::White, " DeleteNPCCorpses"); + c->Message(Chat::White, " Delete - Delete targetted corpse"); + c->Message(Chat::White, " ListNPC"); + c->Message(Chat::White, " ListPlayer"); + c->Message(Chat::White, " Lock - GM locks the corpse - cannot be looted by non-GM"); + c->Message(Chat::White, " MoveAllGraveyard - move all player corpses to zone's graveyard or non-instance"); + c->Message(Chat::White, " UnLock"); + c->Message(Chat::White, " RemoveCash"); + c->Message(Chat::White, " InspectLoot"); + c->Message(Chat::White, " [to remove items from corpses, loot them]"); + c->Message(Chat::White, "Lead-GM status required to delete/modify player corpses"); + c->Message(Chat::White, " DeletePlayerCorpses"); + c->Message(Chat::White, " CharID [charid] - change player corpse's owner"); + c->Message(Chat::White, " Depop [bury] - Depops single target corpse."); + c->Message(Chat::White, " Depopall [bury] - Depops all target player's corpses."); + c->Message(Chat::White, "Set bury to 0 to skip burying the corpses."); + } + else { + c->Message(Chat::White, "Error, #corpse sub-command not found"); + } +} + diff --git a/zone/gm_commands/corpsefix.cpp b/zone/gm_commands/corpsefix.cpp new file mode 100755 index 000000000..0de35a8d3 --- /dev/null +++ b/zone/gm_commands/corpsefix.cpp @@ -0,0 +1,8 @@ +#include "../client.h" +#include "../corpse.h" + +void command_corpsefix(Client *c, const Seperator *sep) +{ + entity_list.CorpseFix(c); +} + diff --git a/zone/gm_commands/cvs.cpp b/zone/gm_commands/cvs.cpp new file mode 100755 index 000000000..1632c8663 --- /dev/null +++ b/zone/gm_commands/cvs.cpp @@ -0,0 +1,17 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_cvs(Client *c, const Seperator *sep) +{ + auto pack = new ServerPacket( + ServerOP_ClientVersionSummary, + sizeof(ServerRequestClientVersionSummary_Struct) + ); + auto srcvss = (ServerRequestClientVersionSummary_Struct *) pack->pBuffer; + strn0cpy(srcvss->Name, c->GetName(), sizeof(srcvss->Name)); + worldserver.SendPacket(pack); + safe_delete(pack); +} + diff --git a/zone/gm_commands/damage.cpp b/zone/gm_commands/damage.cpp new file mode 100755 index 000000000..80de8470e --- /dev/null +++ b/zone/gm_commands/damage.cpp @@ -0,0 +1,21 @@ +#include "../client.h" + +void command_damage(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == 0) { + c->Message(Chat::White, "Error: #Damage: No Target."); + } + else if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #damage x"); + } + else { + int32 nkdmg = atoi(sep->arg[1]); + if (nkdmg > 2100000000) { + c->Message(Chat::White, "Enter a value less then 2,100,000,000."); + } + else { + c->GetTarget()->Damage(c, nkdmg, SPELL_UNKNOWN, EQ::skills::SkillHandtoHand, false); + } + } +} + diff --git a/zone/gm_commands/databuckets.cpp b/zone/gm_commands/databuckets.cpp new file mode 100755 index 000000000..82df39342 --- /dev/null +++ b/zone/gm_commands/databuckets.cpp @@ -0,0 +1,92 @@ +#include "../client.h" +#include "../data_bucket.h" + +void command_databuckets(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + c->Message(Chat::Yellow, "Usage: #databuckets view (partial key)|(limit) OR #databuckets delete (key)"); + return; + } + if (strcasecmp(sep->arg[1], "view") == 0) { + + std::string key_filter; + uint8 limit = 50; + for (int i = 2; i < 4; i++) { + if (sep->arg[i][0] == '\0') { + break; + } + if (strcasecmp(sep->arg[i], "limit") == 0) { + limit = (uint8) atoi(sep->arg[i + 1]); + continue; + } + } + if (sep->arg[2]) { + key_filter = str_tolower(sep->arg[2]); + } + std::string query = "SELECT `id`, `key`, `value`, `expires` FROM data_buckets"; + if (!key_filter.empty()) { query += StringFormat(" WHERE `key` LIKE '%%%s%%'", key_filter.c_str()); } + query += StringFormat(" LIMIT %u", limit); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + return; + } + if (results.RowCount() == 0) { + c->Message(Chat::Yellow, "No data_buckets found"); + return; + } + int _ctr = 0; + // put in window for easier readability in case want command line for something else + std::string window_title = "Data Buckets"; + std::string window_text = + "" + "" + "" + "" + "" + "" + ""; + for (auto row = results.begin(); row != results.end(); ++row) { + auto id = static_cast(atoi(row[0])); + std::string key = row[1]; + std::string value = row[2]; + std::string expires = row[3]; + window_text.append( + StringFormat( + "" + "" + "" + "" + "" + "", + id, + expires.c_str(), + key.c_str(), + value.c_str() + )); + _ctr++; + std::string del_saylink = StringFormat("#databuckets delete %s", key.c_str()); + c->Message( + Chat::White, + "%s : %s", + EQ::SayLinkEngine::GenerateQuestSaylink(del_saylink, false, "Delete").c_str(), + key.c_str(), + " Value: ", + value.c_str()); + } + window_text.append("
IDExpiresKeyValue
%u%s%s%s
"); + c->SendPopupToClient(window_title.c_str(), window_text.c_str()); + std::string response = _ctr > 0 ? StringFormat("Found %i matching data buckets", _ctr).c_str() + : "No Databuckets found."; + c->Message(Chat::Yellow, response.c_str()); + } + else if (strcasecmp(sep->arg[1], "delete") == 0) { + if (DataBucket::DeleteData(sep->argplus[2])) { + c->Message(Chat::Yellow, "data bucket %s deleted.", sep->argplus[2]); + } + else { + c->Message(Chat::Red, "An error occurred deleting data bucket %s", sep->argplus[2]); + } + return; + } +} + diff --git a/zone/gm_commands/date.cpp b/zone/gm_commands/date.cpp new file mode 100755 index 000000000..3e8aa2455 --- /dev/null +++ b/zone/gm_commands/date.cpp @@ -0,0 +1,29 @@ +#include "../client.h" + +void command_date(Client *c, const Seperator *sep) +{ + //yyyy mm dd hh mm local + if (sep->arg[3][0] == 0 || !sep->IsNumber(1) || !sep->IsNumber(2) || !sep->IsNumber(3)) { + c->Message(Chat::Red, "Usage: #date yyyy mm dd [HH MM]"); + } + else { + int h = 0, m = 0; + TimeOfDay_Struct eqTime; + zone->zone_time.GetCurrentEQTimeOfDay(time(0), &eqTime); + if (!sep->IsNumber(4)) { + h = eqTime.hour; + } + else { + h = atoi(sep->arg[4]); + } + if (!sep->IsNumber(5)) { + m = eqTime.minute; + } + else { + m = atoi(sep->arg[5]); + } + c->Message(Chat::Red, "Setting world time to %s-%s-%s %i:%i...", sep->arg[1], sep->arg[2], sep->arg[3], h, m); + zone->SetDate(atoi(sep->arg[1]), atoi(sep->arg[2]), atoi(sep->arg[3]), h, m); + } +} + diff --git a/zone/gm_commands/dbspawn2.cpp b/zone/gm_commands/dbspawn2.cpp new file mode 100755 index 000000000..2f31e2f19 --- /dev/null +++ b/zone/gm_commands/dbspawn2.cpp @@ -0,0 +1,31 @@ +#include "../client.h" + +void command_dbspawn2(Client *c, const Seperator *sep) +{ + + if (sep->IsNumber(1) && sep->IsNumber(2) && sep->IsNumber(3)) { + LogInfo("Spawning database spawn"); + uint16 cond = 0; + int16 cond_min = 0; + if (sep->IsNumber(4)) { + cond = atoi(sep->arg[4]); + if (sep->IsNumber(5)) { + cond_min = atoi(sep->arg[5]); + } + } + database.CreateSpawn2( + c, + atoi(sep->arg[1]), + zone->GetShortName(), + c->GetPosition(), + atoi(sep->arg[2]), + atoi(sep->arg[3]), + cond, + cond_min + ); + } + else { + c->Message(Chat::White, "Usage: #dbspawn2 spawngroup respawn variance [condition_id] [condition_min]"); + } +} + diff --git a/zone/gm_commands/delacct.cpp b/zone/gm_commands/delacct.cpp new file mode 100755 index 000000000..959fe7404 --- /dev/null +++ b/zone/gm_commands/delacct.cpp @@ -0,0 +1,21 @@ +#include "../client.h" + +void command_delacct(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Format: #delacct accountname"); + } + else { + std::string user; + std::string loginserver; + ParseAccountString(sep->arg[1], user, loginserver); + + if (database.DeleteAccount(user.c_str(), loginserver.c_str())) { + c->Message(Chat::White, "The account was deleted."); + } + else { + c->Message(Chat::White, "Unable to delete account."); + } + } +} + diff --git a/zone/gm_commands/deletegraveyard.cpp b/zone/gm_commands/deletegraveyard.cpp new file mode 100755 index 000000000..43560eb6f --- /dev/null +++ b/zone/gm_commands/deletegraveyard.cpp @@ -0,0 +1,35 @@ +#include "../client.h" + +void command_deletegraveyard(Client *c, const Seperator *sep) +{ + uint32 zoneid = 0; + uint32 graveyard_id = 0; + + if (!sep->arg[1][0]) { + c->Message(Chat::White, "Usage: #deletegraveyard [zonename]"); + return; + } + + zoneid = ZoneID(sep->arg[1]); + graveyard_id = content_db.GetZoneGraveyardID(zoneid, 0); + + if (zoneid > 0 && graveyard_id > 0) { + if (content_db.DeleteGraveyard(zoneid, graveyard_id)) { + c->Message(Chat::White, "Successfuly deleted graveyard %u for zone %s.", graveyard_id, sep->arg[1]); + } + else { + c->Message(Chat::White, "Unable to delete graveyard %u for zone %s.", graveyard_id, sep->arg[1]); + } + } + else { + if (zoneid <= 0) { + c->Message(Chat::White, "Unable to retrieve a ZoneID for the zone: %s", sep->arg[1]); + } + else if (graveyard_id <= 0) { + c->Message(Chat::White, "Unable to retrieve a valid GraveyardID for the zone: %s", sep->arg[1]); + } + } + + return; +} + diff --git a/zone/gm_commands/delpetition.cpp b/zone/gm_commands/delpetition.cpp new file mode 100755 index 000000000..556e33866 --- /dev/null +++ b/zone/gm_commands/delpetition.cpp @@ -0,0 +1,20 @@ +#include "../client.h" + +void command_delpetition(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0 || strcasecmp(sep->arg[1], "*") == 0) { + c->Message(Chat::White, "Usage: #delpetition (petition number) Type #listpetition for a list"); + return; + } + + c->Message(Chat::Red, "Attempting to delete petition number: %i", atoi(sep->argplus[1])); + std::string query = StringFormat("DELETE FROM petitions WHERE petid = %i", atoi(sep->argplus[1])); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + return; + } + + LogInfo("Delete petition request from [{}], petition number:", c->GetName(), atoi(sep->argplus[1])); + +} + diff --git a/zone/gm_commands/depop.cpp b/zone/gm_commands/depop.cpp new file mode 100755 index 000000000..3a1693205 --- /dev/null +++ b/zone/gm_commands/depop.cpp @@ -0,0 +1,14 @@ +#include "../client.h" +#include "../corpse.h" + +void command_depop(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == 0 || !(c->GetTarget()->IsNPC() || c->GetTarget()->IsNPCCorpse())) { + c->Message(Chat::White, "You must have a NPC target for this command. (maybe you meant #depopzone?)"); + } + else { + c->Message(Chat::White, "Depoping '%s'.", c->GetTarget()->GetName()); + c->GetTarget()->Depop(); + } +} + diff --git a/zone/gm_commands/depopzone.cpp b/zone/gm_commands/depopzone.cpp new file mode 100755 index 000000000..518ef42db --- /dev/null +++ b/zone/gm_commands/depopzone.cpp @@ -0,0 +1,8 @@ +#include "../client.h" + +void command_depopzone(Client *c, const Seperator *sep) +{ + zone->Depop(); + c->Message(Chat::White, "Zone depoped."); +} + diff --git a/zone/gm_commands/details.cpp b/zone/gm_commands/details.cpp new file mode 100755 index 000000000..f8b5dee8f --- /dev/null +++ b/zone/gm_commands/details.cpp @@ -0,0 +1,37 @@ +#include "../client.h" + +void command_details(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #details [number of drakkin detail]"); + } + else if (!target) { + c->Message(Chat::White, "Error: this command requires a target"); + } + else { + uint16 Race = target->GetRace(); + uint8 Gender = target->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = 0xFF; + uint8 HairColor = target->GetHairColor(); + uint8 BeardColor = target->GetBeardColor(); + uint8 EyeColor1 = target->GetEyeColor1(); + uint8 EyeColor2 = target->GetEyeColor2(); + uint8 HairStyle = target->GetHairStyle(); + uint8 LuclinFace = target->GetLuclinFace(); + uint8 Beard = target->GetBeard(); + uint32 DrakkinHeritage = target->GetDrakkinHeritage(); + uint32 DrakkinTattoo = target->GetDrakkinTattoo(); + uint32 DrakkinDetails = atoi(sep->arg[1]); + + target->SendIllusionPacket( + Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails + ); + + c->Message(Chat::White, "Details = %i", atoi(sep->arg[1])); + } +} + diff --git a/zone/gm_commands/devtools.cpp b/zone/gm_commands/devtools.cpp new file mode 100755 index 000000000..da46b4f77 --- /dev/null +++ b/zone/gm_commands/devtools.cpp @@ -0,0 +1,22 @@ +#include "../client.h" +#include "../data_bucket.h" + +void command_devtools(Client *c, const Seperator *sep) +{ + std::string dev_tools_key = StringFormat("%i-dev-tools-disabled", c->AccountID()); + + /** + * Handle window toggle + */ + if (strcasecmp(sep->arg[1], "disable") == 0) { + DataBucket::SetData(dev_tools_key, "true"); + c->SetDevToolsEnabled(false); + } + if (strcasecmp(sep->arg[1], "enable") == 0) { + DataBucket::DeleteData(dev_tools_key); + c->SetDevToolsEnabled(true); + } + + c->ShowDevToolsMenu(); +} + diff --git a/zone/gm_commands/disablerecipe.cpp b/zone/gm_commands/disablerecipe.cpp new file mode 100755 index 000000000..ca5c6e152 --- /dev/null +++ b/zone/gm_commands/disablerecipe.cpp @@ -0,0 +1,29 @@ +#include "../client.h" + +void command_disablerecipe(Client *c, const Seperator *sep) +{ + uint32 recipe_id = 0; + bool success = false; + if (c) { + if (sep->argnum == 1) { + recipe_id = atoi(sep->arg[1]); + } + else { + c->Message(Chat::White, "Invalid number of arguments.\nUsage: #disablerecipe recipe_id"); + return; + } + if (recipe_id > 0) { + success = content_db.DisableRecipe(recipe_id); + if (success) { + c->Message(Chat::White, "Recipe disabled."); + } + else { + c->Message(Chat::White, "Recipe not disabled."); + } + } + else { + c->Message(Chat::White, "Invalid recipe id.\nUsage: #disablerecipe recipe_id"); + } + } +} + diff --git a/zone/gm_commands/disarmtrap.cpp b/zone/gm_commands/disarmtrap.cpp new file mode 100755 index 000000000..84efe93cf --- /dev/null +++ b/zone/gm_commands/disarmtrap.cpp @@ -0,0 +1,25 @@ +#include "../client.h" + +void command_disarmtrap(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + + if (!target) { + c->Message(Chat::Red, "You must have a target."); + return; + } + + if (target->IsNPC()) { + if (c->HasSkill(EQ::skills::SkillDisarmTraps)) { + if (DistanceSquaredNoZ(c->GetPosition(), target->GetPosition()) > RuleI(Adventure, LDoNTrapDistanceUse)) { + c->Message(Chat::Red, "%s is too far away.", target->GetCleanName()); + return; + } + c->HandleLDoNDisarm(target->CastToNPC(), c->GetSkill(EQ::skills::SkillDisarmTraps), LDoNTypeMechanical); + } + else { + c->Message(Chat::Red, "You do not have the disarm trap skill."); + } + } +} + diff --git a/zone/gm_commands/distance.cpp b/zone/gm_commands/distance.cpp new file mode 100755 index 000000000..578f94bb5 --- /dev/null +++ b/zone/gm_commands/distance.cpp @@ -0,0 +1,23 @@ +#include "../client.h" + +void command_distance(Client *c, const Seperator *sep) +{ + if (c->GetTarget()) { + Mob *target = c->GetTarget(); + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "{} ({}) is {:.2f} units from you.", + target->GetCleanName(), + target->GetID(), + Distance( + c->GetPosition(), + target->GetPosition() + ) + ).c_str() + ); + } + } +} + diff --git a/zone/gm_commands/doanim.cpp b/zone/gm_commands/doanim.cpp new file mode 100755 index 000000000..d48725a97 --- /dev/null +++ b/zone/gm_commands/doanim.cpp @@ -0,0 +1,20 @@ +#include "../client.h" + +void command_doanim(Client *c, const Seperator *sep) +{ + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #DoAnim [number]"); + } + else if (c->Admin() >= commandDoAnimOthers) { + if (c->GetTarget() == 0) { + c->Message(Chat::White, "Error: You need a target."); + } + else { + c->GetTarget()->DoAnim(atoi(sep->arg[1]), atoi(sep->arg[2])); + } + } + else { + c->DoAnim(atoi(sep->arg[1]), atoi(sep->arg[2])); + } +} + diff --git a/zone/gm_commands/door.cpp b/zone/gm_commands/door.cpp new file mode 100755 index 000000000..5a92e5f62 --- /dev/null +++ b/zone/gm_commands/door.cpp @@ -0,0 +1,9 @@ +#include "../client.h" +#include "door_manipulation.h" +#include "../doors.h" + +void command_door(Client *c, const Seperator *sep) +{ + DoorManipulation::CommandHandler(c, sep); +} + diff --git a/zone/gm_commands/door_manipulation.cpp b/zone/gm_commands/door_manipulation.cpp index afb91b07a..a724fb61f 100644 --- a/zone/gm_commands/door_manipulation.cpp +++ b/zone/gm_commands/door_manipulation.cpp @@ -37,7 +37,10 @@ void DoorManipulation::CommandHandler(Client *c, const Seperator *sep) // option if (arg1.empty()) { DoorManipulation::CommandHeader(c); - c->Message(Chat::White, "#door create | Creates a door from a model. (Example IT78 creates a campfire)"); + c->Message( + Chat::White, + "#door create | Creates a door from a model. (Example IT78 creates a campfire)" + ); c->Message(Chat::White, "#door setinvertstate [0|1] | Sets selected door invert state"); c->Message(Chat::White, "#door setincline | Sets selected door incline"); c->Message(Chat::White, "#door opentype | Sets selected door opentype"); @@ -129,7 +132,7 @@ void DoorManipulation::CommandHandler(Client *c, const Seperator *sep) std::vector move_h_options_negative; std::vector set_size_options_positive; std::vector set_size_options_negative; - for (const auto &move_option : move_options) { + for (const auto &move_option : move_options) { if (move_option == move_x_action) { move_x_options_positive.emplace_back( EQ::SayLinkEngine::GenerateQuestSaylink( @@ -297,10 +300,10 @@ void DoorManipulation::CommandHandler(Client *c, const Seperator *sep) // we're passing a move action here if (!arg3.empty() && StringIsNumber(arg3)) { - float x_move = 0.0f; - float y_move = 0.0f; - float z_move = 0.0f; - float h_move = 0.0f; + float x_move = 0.0f; + float y_move = 0.0f; + float z_move = 0.0f; + float h_move = 0.0f; float set_size = 0.0f; if (arg2 == move_x_action) { diff --git a/zone/gm_commands/dye.cpp b/zone/gm_commands/dye.cpp new file mode 100755 index 000000000..ecc04f7ca --- /dev/null +++ b/zone/gm_commands/dye.cpp @@ -0,0 +1,87 @@ +#include "../client.h" + +void command_dye(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + + if (arguments == 0) { + c->Message(Chat::White, "Command Syntax: #dye help | #dye [slot] [red] [green] [blue] [use_tint]"); + return; + } + + uint8 slot = 0; + uint8 red = 255; + uint8 green = 255; + uint8 blue = 255; + uint8 use_tint = 255; + + std::vector dye_slots = { + "Helmet", + "Chest", + "Arms", + "Wrist", + "Hands", + "Legs", + "Feet" + }; + + if (arguments == 1 && !strcasecmp(sep->arg[1], "help")) { + int slot_id = 0; + std::vector slot_messages; + c->Message(Chat::White, "Command Syntax: #dye help | #dye [slot] [red] [green] [blue] [use_tint]"); + c->Message(Chat::White, "Red, Green, and Blue go from 0 to 255."); + + for (const auto &slot : dye_slots) { + slot_messages.push_back(fmt::format("({}) {}", slot_id, slot)); + slot_id++; + } + + c->Message( + Chat::White, + fmt::format( + "{} {}", + "Slots are as follows:", + implode(", ", slot_messages) + ).c_str() + ); + return; + } + + if (arguments >= 1 && sep->IsNumber(1)) { + slot = atoi(sep->arg[1]); + } + + if (arguments >= 2 && sep->IsNumber(2)) { + red = atoi(sep->arg[2]); + } + + if (arguments >= 3 && sep->IsNumber(3)) { + green = atoi(sep->arg[3]); + } + + if (arguments >= 4 && sep->IsNumber(4)) { + blue = atoi(sep->arg[4]); + } + + if (arguments >= 5 && sep->IsNumber(5)) { + use_tint = atoi(sep->arg[5]); + } + + if (RuleB(Command, DyeCommandRequiresDyes)) { + uint32 dye_item_id = 32557; + if (c->CountItem(dye_item_id) >= 1) { + c->RemoveItem(dye_item_id); + } + else { + EQ::SayLinkEngine linker; + linker.SetLinkType(EQ::saylink::SayLinkItemData); + const EQ::ItemData *dye_item = database.GetItem(dye_item_id); + linker.SetItemData(dye_item); + c->Message(Chat::White, fmt::format("This command requires a {} to use.", linker.GenerateLink()).c_str()); + return; + } + } + + c->DyeArmorBySlot(slot, red, green, blue, use_tint); +} + diff --git a/zone/gm_commands/dz.cpp b/zone/gm_commands/dz.cpp new file mode 100755 index 000000000..506a203fb --- /dev/null +++ b/zone/gm_commands/dz.cpp @@ -0,0 +1,244 @@ +#include "../client.h" +#include "../expedition.h" + +void command_dz(Client *c, const Seperator *sep) +{ + if (!c || !zone) { + return; + } + + if (strcasecmp(sep->arg[1], "cache") == 0) { + if (strcasecmp(sep->arg[2], "reload") == 0) { + DynamicZone::CacheAllFromDatabase(); + Expedition::CacheAllFromDatabase(); + c->Message( + Chat::White, fmt::format( + "Reloaded [{}] dynamic zone(s) and [{}] expedition(s) from database", + zone->dynamic_zone_cache.size(), zone->expedition_cache.size() + ).c_str()); + } + } + else if (strcasecmp(sep->arg[1], "expedition") == 0) { + if (strcasecmp(sep->arg[2], "list") == 0) { + std::vector expeditions; + for (const auto &expedition : zone->expedition_cache) { + expeditions.emplace_back(expedition.second.get()); + } + + std::sort( + expeditions.begin(), expeditions.end(), + [](const Expedition *lhs, const Expedition *rhs) { + return lhs->GetID() < rhs->GetID(); + } + ); + + c->Message(Chat::White, fmt::format("Total Active Expeditions: [{}]", expeditions.size()).c_str()); + for (const auto &expedition : expeditions) { + auto dz = expedition->GetDynamicZone(); + if (!dz) { + LogExpeditions("Expedition [{}] has an invalid dz [{}] in cache", + expedition->GetID(), + expedition->GetDynamicZoneID()); + continue; + } + + auto leader_saylink = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format( + "#goto {}", expedition->GetLeaderName()), false, expedition->GetLeaderName()); + auto zone_saylink = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format( + "#zoneinstance {}", dz->GetInstanceID()), false, "zone" + ); + + auto seconds = dz->GetSecondsRemaining(); + + c->Message( + Chat::White, fmt::format( + "expedition id: [{}] dz id: [{}] name: [{}] leader: [{}] {}: [{}]:[{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]", + expedition->GetID(), + expedition->GetDynamicZoneID(), + expedition->GetName(), + leader_saylink, + zone_saylink, + ZoneName(dz->GetZoneID()), + dz->GetZoneID(), + dz->GetInstanceID(), + dz->GetZoneVersion(), + dz->GetMemberCount(), + seconds / 3600, // hours + (seconds / 60) % 60, // minutes + seconds % 60 // seconds + ).c_str()); + } + } + else if (strcasecmp(sep->arg[2], "reload") == 0) { + Expedition::CacheAllFromDatabase(); + c->Message( + Chat::White, fmt::format( + "Reloaded [{}] expeditions to cache from database.", zone->expedition_cache.size() + ).c_str()); + } + else if (strcasecmp(sep->arg[2], "destroy") == 0 && sep->IsNumber(3)) { + auto expedition_id = std::strtoul(sep->arg[3], nullptr, 10); + auto expedition = Expedition::FindCachedExpeditionByID(expedition_id); + if (expedition) { + c->Message( + Chat::White, fmt::format( + "Destroying expedition [{}] ({})", + expedition_id, expedition->GetName()).c_str()); + expedition->GetDynamicZone()->RemoveAllMembers(); + } + else { + c->Message(Chat::Red, fmt::format("Failed to destroy expedition [{}]", sep->arg[3]).c_str()); + } + } + else if (strcasecmp(sep->arg[2], "unlock") == 0 && sep->IsNumber(3)) { + auto expedition_id = std::strtoul(sep->arg[3], nullptr, 10); + auto expedition = Expedition::FindCachedExpeditionByID(expedition_id); + if (expedition) { + c->Message(Chat::White, fmt::format("Unlocking expedition [{}]", expedition_id).c_str()); + expedition->SetLocked(false, ExpeditionLockMessage::None, true); + } + else { + c->Message(Chat::Red, fmt::format("Failed to find expedition [{}]", sep->arg[3]).c_str()); + } + } + } + else if (strcasecmp(sep->arg[1], "list") == 0) { + c->Message( + Chat::White, + fmt::format("Total Dynamic Zones (cache): [{}]", zone->dynamic_zone_cache.size()).c_str()); + + std::vector dynamic_zones; + for (const auto &dz : zone->dynamic_zone_cache) { + dynamic_zones.emplace_back(dz.second.get()); + } + + std::sort( + dynamic_zones.begin(), dynamic_zones.end(), + [](const DynamicZone *lhs, const DynamicZone *rhs) { + return lhs->GetID() < rhs->GetID(); + } + ); + + for (const auto &dz : dynamic_zones) { + auto seconds = dz->GetSecondsRemaining(); + auto zone_saylink = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#zoneinstance {}", dz->GetInstanceID()), false, "zone" + ); + + std::string aligned_type = fmt::format( + "[{}]", + DynamicZone::GetDynamicZoneTypeName(static_cast(dz->GetType()))); + c->Message( + Chat::White, fmt::format( + "id: [{}] type: {:>10} {}: [{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]", + dz->GetID(), + aligned_type, + zone_saylink, + dz->GetZoneID(), + dz->GetInstanceID(), + dz->GetZoneVersion(), + dz->GetMemberCount(), + seconds / 3600, // hours + (seconds / 60) % 60, // minutes + seconds % 60 // seconds + ).c_str()); + } + } + else if (strcasecmp(sep->arg[1], "listdb") == 0) { + auto dz_list = DynamicZonesRepository::AllDzInstancePlayerCounts(database); + c->Message(Chat::White, fmt::format("Total Dynamic Zones (database): [{}]", dz_list.size()).c_str()); + + auto now = std::chrono::system_clock::now(); + + for (const auto &dz : dz_list) { + auto expire_time = std::chrono::system_clock::from_time_t(dz.start_time + dz.duration); + auto remaining = std::chrono::duration_cast(expire_time - now); + auto seconds = std::max(0, static_cast(remaining.count())); + bool is_expired = now > expire_time; + + if (!is_expired || strcasecmp(sep->arg[2], "all") == 0) { + auto zone_saylink = is_expired ? "zone" : EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#zoneinstance {}", dz.instance), false, "zone" + ); + + c->Message( + Chat::White, fmt::format( + "id: [{}] type: [{}] {}: [{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]", + dz.id, + DynamicZone::GetDynamicZoneTypeName(static_cast(dz.type)), + zone_saylink, + dz.zone, + dz.instance, + dz.version, + dz.member_count, + seconds / 3600, // hours + (seconds / 60) % 60, // minutes + seconds % 60 // seconds + ).c_str()); + } + } + } + else if (strcasecmp(sep->arg[1], "lockouts") == 0) { + if (strcasecmp(sep->arg[2], "remove") == 0 && sep->arg[3][0] != '\0') { + if (sep->arg[5][0] == '\0') { + c->Message( + Chat::White, fmt::format( + "Removing [{}] lockouts on [{}].", sep->arg[4][0] ? sep->arg[4] : "all", sep->arg[3] + ).c_str()); + } + else { + c->Message( + Chat::White, fmt::format( + "Removing [{}]:[{}] lockout on [{}].", sep->arg[4], sep->arg[5], sep->arg[3] + ).c_str()); + } + Expedition::RemoveLockoutsByCharacterName(sep->arg[3], sep->arg[4], sep->arg[5]); + } + } + else if (strcasecmp(sep->arg[1], "makeleader") == 0 && sep->IsNumber(2) && sep->arg[3][0] != '\0') { + auto expedition_id = std::strtoul(sep->arg[2], nullptr, 10); + auto expedition = Expedition::FindCachedExpeditionByID(expedition_id); + if (expedition) { + auto char_name = FormatName(sep->arg[3]); + c->Message( + Chat::White, + fmt::format("Setting expedition [{}] leader to [{}]", expedition_id, char_name).c_str()); + expedition->SendWorldMakeLeaderRequest(c->CharacterID(), char_name); + } + else { + c->Message(Chat::Red, fmt::format("Failed to find expedition [{}]", expedition_id).c_str()); + } + } + else { + c->Message(Chat::White, "#dz usage:"); + c->Message( + Chat::White, + "#dz cache reload - reload the current zone cache from db (also reloads expedition cache dependency)" + ); + c->Message(Chat::White, "#dz expedition list - list expeditions in current zone cache"); + c->Message(Chat::White, "#dz expedition reload - reload expedition zone cache from database"); + c->Message( + Chat::White, + "#dz expedition destroy - destroy expedition globally (must be in cache)" + ); + c->Message(Chat::White, "#dz expedition unlock - unlock expedition"); + c->Message(Chat::White, "#dz list - list all dynamic zone instances from current zone cache"); + c->Message( + Chat::White, + "#dz listdb [all] - list dynamic zone instances from database -- 'all' includes expired" + ); + c->Message(Chat::White, "#dz lockouts remove - delete all of character's expedition lockouts"); + c->Message( + Chat::White, + "#dz lockouts remove \"\" - delete lockouts by expedition" + ); + c->Message( + Chat::White, + "#dz lockouts remove \"\" \"\" - delete lockout by expedition event" + ); + c->Message(Chat::White, "#dz makeleader - set new expedition leader"); + } +} + diff --git a/zone/gm_commands/dzkickplayers.cpp b/zone/gm_commands/dzkickplayers.cpp new file mode 100755 index 000000000..149ccc0af --- /dev/null +++ b/zone/gm_commands/dzkickplayers.cpp @@ -0,0 +1,13 @@ +#include "../client.h" +#include "../expedition.h" + +void command_dzkickplayers(Client *c, const Seperator *sep) +{ + if (c) { + auto expedition = c->GetExpedition(); + if (expedition) { + expedition->DzKickPlayers(c); + } + } +} + diff --git a/zone/gm_commands/editmassrespawn.cpp b/zone/gm_commands/editmassrespawn.cpp new file mode 100755 index 000000000..41fcc6613 --- /dev/null +++ b/zone/gm_commands/editmassrespawn.cpp @@ -0,0 +1,140 @@ +#include "../client.h" + +void command_editmassrespawn(Client *c, const Seperator *sep) +{ + if (strcasecmp(sep->arg[1], "usage") == 0) { + c->Message(Chat::White, "#editmassrespawn [exact_match: =]npc_type_name new_respawn_seconds (apply)"); + return; + } + + std::string search_npc_type; + if (sep->arg[1]) { + search_npc_type = sep->arg[1]; + } + + int change_respawn_seconds = 0; + if (sep->arg[2] && sep->IsNumber(2)) { + change_respawn_seconds = atoi(sep->arg[2]); + } + + bool change_apply = false; + if (sep->arg[3] && strcasecmp(sep->arg[3], "apply") == 0) { + change_apply = true; + } + + std::string search_encapsulator = "%"; + if (search_npc_type[0] == '=') { + + search_npc_type = search_npc_type.substr(1); + search_encapsulator = ""; + } + + std::string query = fmt::format( + SQL( + SELECT npc_types.id, spawn2.spawngroupID, spawn2.id, npc_types.name, spawn2.respawntime + FROM spawn2 + INNER JOIN spawnentry ON spawn2.spawngroupID = spawnentry.spawngroupID + INNER JOIN npc_types ON spawnentry.npcID = npc_types.id + WHERE spawn2.zone LIKE '{}' + AND spawn2.version = '{}' + AND npc_types.name LIKE '{}{}{}' + ORDER BY npc_types.id, spawn2.spawngroupID, spawn2.id + ), + zone->GetShortName(), + zone->GetInstanceVersion(), + search_encapsulator, + search_npc_type, + search_encapsulator + ); + + std::string status = "(Searching)"; + if (change_apply) { + status = "(Applying)"; + } + + int results_count = 0; + + auto results = content_db.QueryDatabase(query); + if (results.Success() && results.RowCount()) { + + results_count = results.RowCount(); + + for (auto row : results) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC (npcid:{}) (sgid:{}) (s2id:{}) [{}] Respawn: Current [{}] New [{}] {}", + row[0], + row[1], + row[2], + row[3], + row[4], + change_respawn_seconds, + status + ).c_str() + ); + } + + c->Message(Chat::Yellow, "Found (%i) NPC's that match this search...", results_count); + + if (change_respawn_seconds > 0) { + + if (change_apply) { + + results = content_db.QueryDatabase( + fmt::format( + SQL( + UPDATE spawn2 + SET respawntime = '{}' + WHERE id IN( + SELECT spawn2.id + FROM spawn2 + INNER JOIN spawnentry ON spawn2.spawngroupID = spawnentry.spawngroupID + INNER JOIN npc_types ON spawnentry.npcID = npc_types.id + WHERE spawn2.zone LIKE '{}' + AND spawn2.version = '{}' + AND npc_types.name LIKE '{}{}{}' + ) + ), + change_respawn_seconds, + zone->GetShortName(), + zone->GetInstanceVersion(), + search_encapsulator, + search_npc_type, + search_encapsulator + ) + ); + + if (results.Success()) { + + c->Message(Chat::Yellow, "Changes applied to (%i) NPC 'Spawn2' entries", results_count); + zone->Repop(); + } + else { + + c->Message(Chat::Yellow, "Found (0) NPC's that match this search..."); + } + } + else { + + std::string saylink = fmt::format( + "#editmassrespawn {}{} {} apply", + (search_encapsulator.empty() ? "=" : ""), + search_npc_type, + change_respawn_seconds + ); + + c->Message( + Chat::Yellow, "To apply these changes, click <%s> or type [%s]", + EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Apply").c_str(), + saylink.c_str() + ); + } + } + } + else { + + c->Message(Chat::Yellow, "Found (0) NPC's that match this search..."); + } +} + diff --git a/zone/gm_commands/emote.cpp b/zone/gm_commands/emote.cpp new file mode 100755 index 000000000..6deda54b5 --- /dev/null +++ b/zone/gm_commands/emote.cpp @@ -0,0 +1,45 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_emote(Client *c, const Seperator *sep) +{ + if (sep->arg[3][0] == 0) { + c->Message(Chat::White, "Usage: #emote [name | world | zone] type# message"); + } + else { + if (strcasecmp(sep->arg[1], "zone") == 0) { + char *newmessage = 0; + if (strstr(sep->arg[3], "^") == 0) { + entity_list.Message(0, atoi(sep->arg[2]), sep->argplus[3]); + } + else { + for (newmessage = strtok((char *) sep->arg[3], "^"); + newmessage != nullptr; + newmessage = strtok(nullptr, "^")) + entity_list.Message(0, atoi(sep->arg[2]), newmessage); + } + } + else if (!worldserver.Connected()) { + c->Message(Chat::White, "Error: World server disconnected"); + } + else if (!strcasecmp(sep->arg[1], "world")) { + worldserver.SendEmoteMessage( + 0, + 0, + atoi(sep->arg[2]), + sep->argplus[3] + ); + } + else { + worldserver.SendEmoteMessage( + sep->arg[1], + 0, + atoi(sep->arg[2]), + sep->argplus[3] + ); + } + } +} + diff --git a/zone/gm_commands/emotesearch.cpp b/zone/gm_commands/emotesearch.cpp new file mode 100755 index 000000000..40d5bfac6 --- /dev/null +++ b/zone/gm_commands/emotesearch.cpp @@ -0,0 +1,78 @@ +#include "../client.h" + +void command_emotesearch(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #emotesearch [search string or emoteid]"); + } + else { + const char *search_criteria = sep->argplus[1]; + int count = 0; + + if (Seperator::IsNumber(search_criteria)) { + uint16 emoteid = atoi(search_criteria); + LinkedListIterator iterator(zone->NPCEmoteList); + iterator.Reset(); + while (iterator.MoreElements()) { + NPC_Emote_Struct *nes = iterator.GetData(); + if (emoteid == nes->emoteid) { + c->Message( + Chat::White, + "EmoteID: %i Event: %i Type: %i Text: %s", + nes->emoteid, + nes->event_, + nes->type, + nes->text + ); + count++; + } + iterator.Advance(); + } + if (count == 0) { + c->Message(Chat::White, "No emotes found."); + } + else { + c->Message(Chat::White, "%i emote(s) found", count); + } + } + else { + char sText[64]; + char sCriteria[515]; + strn0cpy(sCriteria, search_criteria, sizeof(sCriteria)); + strupr(sCriteria); + char *pdest; + + LinkedListIterator iterator(zone->NPCEmoteList); + iterator.Reset(); + while (iterator.MoreElements()) { + NPC_Emote_Struct *nes = iterator.GetData(); + strn0cpy(sText, nes->text, sizeof(sText)); + strupr(sText); + pdest = strstr(sText, sCriteria); + if (pdest != nullptr) { + c->Message( + Chat::White, + "EmoteID: %i Event: %i Type: %i Text: %s", + nes->emoteid, + nes->event_, + nes->type, + nes->text + ); + count++; + } + if (count == 50) { + break; + } + + iterator.Advance(); + } + if (count == 50) { + c->Message(Chat::White, "50 emotes shown...too many results."); + } + else { + c->Message(Chat::White, "%i emote(s) found", count); + } + } + } +} + diff --git a/zone/gm_commands/emoteview.cpp b/zone/gm_commands/emoteview.cpp new file mode 100755 index 000000000..afbec21aa --- /dev/null +++ b/zone/gm_commands/emoteview.cpp @@ -0,0 +1,39 @@ +#include "../client.h" + +void command_emoteview(Client *c, const Seperator *sep) +{ + if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "You must target a NPC to view their emotes."); + return; + } + + if (c->GetTarget() && c->GetTarget()->IsNPC()) { + int count = 0; + int emoteid = c->GetTarget()->CastToNPC()->GetEmoteID(); + + LinkedListIterator iterator(zone->NPCEmoteList); + iterator.Reset(); + while (iterator.MoreElements()) { + NPC_Emote_Struct *nes = iterator.GetData(); + if (emoteid == nes->emoteid) { + c->Message( + Chat::White, + "EmoteID: %i Event: %i Type: %i Text: %s", + nes->emoteid, + nes->event_, + nes->type, + nes->text + ); + count++; + } + iterator.Advance(); + } + if (count == 0) { + c->Message(Chat::White, "No emotes found."); + } + else { + c->Message(Chat::White, "%i emote(s) found", count); + } + } +} + diff --git a/zone/gm_commands/enablerecipe.cpp b/zone/gm_commands/enablerecipe.cpp new file mode 100755 index 000000000..acfa1ff93 --- /dev/null +++ b/zone/gm_commands/enablerecipe.cpp @@ -0,0 +1,29 @@ +#include "../client.h" + +void command_enablerecipe(Client *c, const Seperator *sep) +{ + uint32 recipe_id = 0; + bool success = false; + if (c) { + if (sep->argnum == 1) { + recipe_id = atoi(sep->arg[1]); + } + else { + c->Message(Chat::White, "Invalid number of arguments.\nUsage: #enablerecipe recipe_id"); + return; + } + if (recipe_id > 0) { + success = content_db.EnableRecipe(recipe_id); + if (success) { + c->Message(Chat::White, "Recipe enabled."); + } + else { + c->Message(Chat::White, "Recipe not enabled."); + } + } + else { + c->Message(Chat::White, "Invalid recipe id.\nUsage: #enablerecipe recipe_id"); + } + } +} + diff --git a/zone/gm_commands/endurance.cpp b/zone/gm_commands/endurance.cpp new file mode 100755 index 000000000..26132c913 --- /dev/null +++ b/zone/gm_commands/endurance.cpp @@ -0,0 +1,27 @@ +#include "../client.h" + +void command_endurance(Client *c, const Seperator *sep) +{ + auto target = c->GetTarget() ? c->GetTarget() : c; + if (target->IsClient()) { + target->CastToClient()->SetEndurance(target->CastToClient()->GetMaxEndurance()); + } + else { + target->SetEndurance(target->GetMaxEndurance()); + } + + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "Set {} ({}) to full Endurance.", + target->GetCleanName(), + target->GetID() + ).c_str() + ); + } + else { + c->Message(Chat::White, "Restored your Endurance to full."); + } +} + diff --git a/zone/gm_commands/equipitem.cpp b/zone/gm_commands/equipitem.cpp new file mode 100755 index 000000000..7ffe8ad84 --- /dev/null +++ b/zone/gm_commands/equipitem.cpp @@ -0,0 +1,82 @@ +#include "../client.h" + +void command_equipitem(Client *c, const Seperator *sep) +{ + uint32 slot_id = atoi(sep->arg[1]); + if (sep->IsNumber(1) && (slot_id >= EQ::invslot::EQUIPMENT_BEGIN && slot_id <= EQ::invslot::EQUIPMENT_END)) { + const EQ::ItemInstance *from_inst = c->GetInv().GetItem(EQ::invslot::slotCursor); + const EQ::ItemInstance *to_inst = c->GetInv().GetItem(slot_id); // added (desync issue when forcing stack to stack) + bool partialmove = false; + int16 movecount; + + if (from_inst && from_inst->IsClassCommon()) { + auto outapp = new EQApplicationPacket(OP_MoveItem, sizeof(MoveItem_Struct)); + MoveItem_Struct *mi = (MoveItem_Struct *) outapp->pBuffer; + mi->from_slot = EQ::invslot::slotCursor; + mi->to_slot = slot_id; + // mi->number_in_stack = from_inst->GetCharges(); // replaced with con check for stacking + + // crude stackable check to only 'move' the difference count on client instead of entire stack when applicable + if (to_inst && to_inst->IsStackable() && + (to_inst->GetItem()->ID == from_inst->GetItem()->ID) && + (to_inst->GetCharges() < to_inst->GetItem()->StackSize) && + (from_inst->GetCharges() > to_inst->GetItem()->StackSize - to_inst->GetCharges())) { + movecount = to_inst->GetItem()->StackSize - to_inst->GetCharges(); + mi->number_in_stack = (uint32) movecount; + partialmove = true; + } + else { + mi->number_in_stack = from_inst->GetCharges(); + } + + // Save move changes + // Added conditional check to packet send..would have sent change even on a swap failure..whoops! + + if (partialmove) { // remove this con check if someone can figure out removing charges from cursor stack issue below + // mi->number_in_stack is always from_inst->GetCharges() when partialmove is false + c->Message(Chat::Red, "Error: Partial stack added to existing stack exceeds allowable stacksize"); + safe_delete(outapp); + return; + } + else if (c->SwapItem(mi)) { + c->FastQueuePacket(&outapp); + + // if the below code is still needed..just send an an item trade packet to each slot..it should overwrite the client instance + + // below code has proper logic, but client does not like to have cursor charges changed + // (we could delete the cursor item and resend, but issues would arise if there are queued items) + //if (partialmove) { + // EQApplicationPacket* outapp2 = new EQApplicationPacket(OP_DeleteItem, sizeof(DeleteItem_Struct)); + // DeleteItem_Struct* di = (DeleteItem_Struct*)outapp2->pBuffer; + // di->from_slot = SLOT_CURSOR; + // di->to_slot = 0xFFFFFFFF; + // di->number_in_stack = 0xFFFFFFFF; + + // c->Message(Chat::White, "Deleting %i charges from stack", movecount); // debug line..delete + + // for (int16 deletecount=0; deletecount < movecount; deletecount++) + // have to use 'movecount' because mi->number_in_stack is 'ENCODED' at this point (i.e., 99 charges returns 22...) + // c->QueuePacket(outapp2); + + // safe_delete(outapp2); + //} + } + else { + c->Message(Chat::Red, "Error: Unable to equip current item"); + } + safe_delete(outapp); + + // also send out a wear change packet? + } + else if (from_inst == nullptr) { + c->Message(Chat::Red, "Error: There is no item on your cursor"); + } + else { + c->Message(Chat::Red, "Error: Item on your cursor cannot be equipped"); + } + } + else { + c->Message(Chat::White, "Usage: #equipitem slotid[0-21] - equips the item on your cursor to the position"); + } +} + diff --git a/zone/gm_commands/face.cpp b/zone/gm_commands/face.cpp new file mode 100755 index 000000000..274d5a300 --- /dev/null +++ b/zone/gm_commands/face.cpp @@ -0,0 +1,37 @@ +#include "../client.h" + +void command_face(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #face [number of face]"); + } + else if (!target) { + c->Message(Chat::White, "Error: this command requires a target"); + } + else { + uint16 Race = target->GetRace(); + uint8 Gender = target->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = 0xFF; + uint8 HairColor = target->GetHairColor(); + uint8 BeardColor = target->GetBeardColor(); + uint8 EyeColor1 = target->GetEyeColor1(); + uint8 EyeColor2 = target->GetEyeColor2(); + uint8 HairStyle = target->GetHairStyle(); + uint8 LuclinFace = atoi(sep->arg[1]); + uint8 Beard = target->GetBeard(); + uint32 DrakkinHeritage = target->GetDrakkinHeritage(); + uint32 DrakkinTattoo = target->GetDrakkinTattoo(); + uint32 DrakkinDetails = target->GetDrakkinDetails(); + + target->SendIllusionPacket( + Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails + ); + + c->Message(Chat::White, "Face = %i", atoi(sep->arg[1])); + } +} + diff --git a/zone/gm_commands/faction.cpp b/zone/gm_commands/faction.cpp new file mode 100755 index 000000000..3a704c81a --- /dev/null +++ b/zone/gm_commands/faction.cpp @@ -0,0 +1,174 @@ +#include "../client.h" + +void command_faction(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (!arguments) { + c->Message( + Chat::White, + "Usage: #faction review [Search Criteria | All] - Review Targeted Player's Faction Hits" + ); + c->Message( + Chat::White, + "Usage: #faction reset [Faction ID] - Reset Targeted Player's Faction to Base Faction Value" + ); + c->Message(Chat::White, "Usage: #faction view - Displays Target NPC's Primary Faction"); + return; + } + + std::string faction_filter; + if (sep->arg[2]) { + faction_filter = str_tolower(sep->arg[2]); + } + + if (!strcasecmp(sep->arg[1], "review")) { + if (!(c->GetTarget() && c->GetTarget()->IsClient())) { + c->Message(Chat::Red, "Player Target Required for faction review"); + return; + } + + Client *target = c->GetTarget()->CastToClient(); + uint32 character_id = target->CharacterID(); + std::string query; + if (!strcasecmp(faction_filter.c_str(), "all")) { + query = fmt::format( + "SELECT id, `name`, current_value FROM faction_list INNER JOIN faction_values ON faction_list.id = faction_values.faction_id WHERE char_id = {}", + character_id + ); + } + else { + query = fmt::format( + "SELECT id, `name`, current_value FROM faction_list INNER JOIN faction_values ON faction_list.id = faction_values.faction_id WHERE `name` like '%{}%' and char_id = {}", + faction_filter.c_str(), + character_id + ); + } + + auto results = content_db.QueryDatabase(query); + if (!results.Success() || !results.RowCount()) { + c->Message(Chat::Yellow, "No faction hits found. All are at base level."); + return; + } + + uint32 found_count = 0; + for (auto row : results) { + uint32 faction_number = (found_count + 1); + auto faction_id = std::stoul(row[0]); + std::string faction_name = row[1]; + std::string faction_value = row[2]; + std::string reset_link = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#faction reset {}", faction_id), + false, + "Reset" + ); + + c->Message( + Chat::White, + fmt::format( + "Faction {} | Name: {} ({}) Value: {} [{}]", + faction_number, + faction_name, + faction_id, + faction_value, + reset_link + ).c_str() + ); + found_count++; + } + + auto faction_message = ( + found_count > 0 ? + ( + found_count == 1 ? + "A Faction was" : + fmt::format("{} Factions were", found_count) + ) : + "No Factions were" + ); + c->Message( + Chat::White, + fmt::format( + "{} found.", + faction_message + ).c_str() + ); + } + else if (!strcasecmp(sep->arg[1], "reset")) { + if (strlen(faction_filter.c_str()) > 0) { + if (c->GetTarget() && c->GetTarget()->IsClient()) { + Client *target = c->GetTarget()->CastToClient(); + if ( + ( + !c->GetFeigned() && + c->GetAggroCount() == 0 + ) || + ( + !target->GetFeigned() && + target->GetAggroCount() == 0 + ) + ) { + uint32 character_id = target->CharacterID(); + uint32 faction_id = std::stoul(faction_filter.c_str()); + if (target->ReloadCharacterFaction(target, faction_id, character_id)) { + c->Message( + Chat::White, + fmt::format( + "Faction Reset | {} ({}) was reset for {}.", + content_db.GetFactionName(faction_id), + faction_id, + target->GetCleanName() + ).c_str() + ); + } + else { + c->Message( + Chat::White, + fmt::format( + "Faction Reset Failed | {} ({}) was unable to be reset for {}.", + content_db.GetFactionName(faction_id), + faction_id, + target->GetCleanName() + ).c_str() + ); + } + } + else { + c->Message( + Chat::White, + "You cannot reset factions while you or your target is in combat or feigned." + ); + return; + } + } + else { + c->Message(Chat::White, "You must target a PC for this command."); + return; + } + } + else { + c->Message( + Chat::White, + "Usage: #faction reset [Faction ID] - Reset Targeted Player's Faction to Base Faction Value" + ); + } + } + else if (!strcasecmp(sep->arg[1], "view")) { + if (c->GetTarget() && c->GetTarget()->IsNPC()) { + Mob *target = c->GetTarget(); + uint32 npc_id = target->GetNPCTypeID(); + uint32 npc_faction_id = target->CastToNPC()->GetPrimaryFaction(); + std::string npc_name = target->GetCleanName(); + c->Message( + Chat::White, + fmt::format( + "{} ({}) has a Primary Faction of {} ({}).", + npc_name, + npc_id, + content_db.GetFactionName(npc_faction_id), + npc_faction_id + ).c_str() + ); + } + } +} + diff --git a/zone/gm_commands/findclass.cpp b/zone/gm_commands/findclass.cpp new file mode 100755 index 000000000..a220f5ded --- /dev/null +++ b/zone/gm_commands/findclass.cpp @@ -0,0 +1,84 @@ +#include "../client.h" + +void command_findclass(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + + if (arguments == 0) { + c->Message(Chat::White, "Command Syntax: #findclass [search criteria]"); + return; + } + + if (sep->IsNumber(1)) { + int class_id = std::stoi(sep->arg[1]); + if (class_id >= WARRIOR && class_id <= MERCERNARY_MASTER) { + std::string class_name = GetClassIDName(class_id); + c->Message( + Chat::White, + fmt::format( + "Class {}: {}", + class_id, + class_name + ).c_str() + ); + } + else { + c->Message( + Chat::White, + fmt::format( + "Class ID {} was not found.", + class_id + ).c_str() + ); + } + } + else { + std::string search_criteria = str_tolower(sep->argplus[1]); + int found_count = 0; + for (int class_id = WARRIOR; class_id <= MERCERNARY_MASTER; class_id++) { + std::string class_name = GetClassIDName(class_id); + std::string class_name_lower = str_tolower(class_name); + if (search_criteria.length() > 0 && class_name_lower.find(search_criteria) == std::string::npos) { + continue; + } + + c->Message( + Chat::White, + fmt::format( + "Class {}: {}", + class_id, + class_name + ).c_str() + ); + found_count++; + + if (found_count == 20) { + break; + } + } + + if (found_count == 20) { + c->Message(Chat::White, "20 Classes found... max reached."); + } + else { + auto class_message = ( + found_count > 0 ? + ( + found_count == 1 ? + "A Class was" : + fmt::format("{} Classes were", found_count) + ) : + "No Classes were" + ); + + c->Message( + Chat::White, + fmt::format( + "{} found.", + class_message + ).c_str() + ); + } + } +} + diff --git a/zone/gm_commands/findfaction.cpp b/zone/gm_commands/findfaction.cpp new file mode 100755 index 000000000..29b72f441 --- /dev/null +++ b/zone/gm_commands/findfaction.cpp @@ -0,0 +1,89 @@ +#include "../client.h" + +void command_findfaction(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + + if (arguments == 0) { + c->Message(Chat::White, "Command Syntax: #findfaction [search criteria]"); + return; + } + + if (sep->IsNumber(1)) { + int faction_id = std::stoi(sep->arg[1]); + auto faction_name = content_db.GetFactionName(faction_id); + if (!faction_name.empty()) { + c->Message( + Chat::White, + fmt::format( + "Faction {}: {}", + faction_id, + faction_name + ).c_str() + ); + } + else { + c->Message( + Chat::White, + fmt::format( + "Faction ID {} was not found.", + faction_id + ).c_str() + ); + } + } + else { + std::string search_criteria = str_tolower(sep->argplus[1]); + int found_count = 0; + int max_faction_id = content_db.GetMaxFaction(); + for (int faction_id = 0; faction_id < max_faction_id; faction_id++) { + std::string faction_name = content_db.GetFactionName(faction_id); + std::string faction_name_lower = str_tolower(faction_name); + if (faction_name.empty()) { + continue; + } + + if (faction_name.find(search_criteria) == std::string::npos) { + continue; + } + + c->Message( + Chat::White, + fmt::format( + "Faction {}: {}", + faction_id, + faction_name + ).c_str() + ); + found_count++; + + if (found_count == 20) { + break; + } + } + + if (found_count == 20) { + c->Message(Chat::White, "20 Factions found... max reached."); + } + else { + auto faction_message = ( + found_count > 0 ? + ( + found_count == 1 ? + "A Faction was" : + fmt::format("{} Factions were", found_count) + ) : + "No Factions were" + ); + + c->Message( + Chat::White, + fmt::format( + "{} found.", + faction_message + ).c_str() + ); + } + } +} + diff --git a/zone/gm_commands/findnpctype.cpp b/zone/gm_commands/findnpctype.cpp new file mode 100755 index 000000000..991bd8f18 --- /dev/null +++ b/zone/gm_commands/findnpctype.cpp @@ -0,0 +1,77 @@ +#include "../client.h" + +void command_findnpctype(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #findnpctype [Search Criteria]"); + return; + } + + std::string query; + std::string search_criteria = sep->arg[1]; + if (sep->IsNumber(1)) { + query = fmt::format( + "SELECT id, name FROM npc_types WHERE id = {}", + search_criteria + ); + } + else { + query = fmt::format( + "SELECT id, name FROM npc_types WHERE name LIKE '%%{}%%'", + search_criteria + ); + } + + auto results = content_db.QueryDatabase(query); + if (!results.Success() || !results.RowCount()) { + c->Message( + Chat::White, + fmt::format( + "No matches found for '{}'.", + search_criteria + ).c_str() + ); + return; + } + + int found_count = 0; + + for (auto row : results) { + int found_number = (found_count + 1); + if (found_count == 20) { + break; + } + + c->Message( + Chat::White, + fmt::format( + "NPC {} | {} ({})", + found_number, + row[1], + row[0] + ).c_str() + ); + found_count++; + } + + if (found_count == 20) { + c->Message(Chat::White, "20 NPCs were found, max reached."); + } + else { + auto npc_message = ( + found_count == 1 ? + "An NPC was" : + fmt::format("{} NPCs were", found_count) + ); + + c->Message( + Chat::White, + fmt::format( + "{} found.", + npc_message + ).c_str() + ); + } +} + diff --git a/zone/gm_commands/findrace.cpp b/zone/gm_commands/findrace.cpp new file mode 100755 index 000000000..413687160 --- /dev/null +++ b/zone/gm_commands/findrace.cpp @@ -0,0 +1,84 @@ +#include "../client.h" + +void command_findrace(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + + if (arguments == 0) { + c->Message(Chat::White, "Command Syntax: #findrace [search criteria]"); + return; + } + + if (sep->IsNumber(1)) { + int race_id = std::stoi(sep->arg[1]); + std::string race_name = GetRaceIDName(race_id); + if (race_id >= RACE_HUMAN_1 && race_id <= RACE_PEGASUS_732) { + c->Message( + Chat::White, + fmt::format( + "Race {}: {}", + race_id, + race_name + ).c_str() + ); + } + else { + c->Message( + Chat::White, + fmt::format( + "Race ID {} was not found.", + race_id + ).c_str() + ); + } + } + else { + std::string search_criteria = str_tolower(sep->argplus[1]); + int found_count = 0; + for (int race_id = RACE_HUMAN_1; race_id <= RACE_PEGASUS_732; race_id++) { + std::string race_name = GetRaceIDName(race_id); + std::string race_name_lower = str_tolower(race_name); + if (search_criteria.length() > 0 && race_name_lower.find(search_criteria) == std::string::npos) { + continue; + } + + c->Message( + Chat::White, + fmt::format( + "Race {}: {}", + race_id, + race_name + ).c_str() + ); + found_count++; + + if (found_count == 20) { + break; + } + } + + if (found_count == 20) { + c->Message(Chat::White, "20 Races found... max reached."); + } + else { + auto race_message = ( + found_count > 0 ? + ( + found_count == 1 ? + "A Race was" : + fmt::format("{} Races were", found_count) + ) : + "No Races were" + ); + + c->Message( + Chat::White, + fmt::format( + "{} found.", + race_message + ).c_str() + ); + } + } +} + diff --git a/zone/gm_commands/findskill.cpp b/zone/gm_commands/findskill.cpp new file mode 100755 index 000000000..b26ad3ab1 --- /dev/null +++ b/zone/gm_commands/findskill.cpp @@ -0,0 +1,90 @@ +#include "../client.h" + +void command_findskill(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + + if (arguments == 0) { + c->Message(Chat::White, "Command Syntax: #findskill [search criteria]"); + return; + } + + std::map skills = EQ::skills::GetSkillTypeMap(); + if (sep->IsNumber(1)) { + int skill_id = std::stoi(sep->arg[1]); + if (skill_id >= EQ::skills::Skill1HBlunt && skill_id < EQ::skills::SkillCount) { + for (auto skill : skills) { + if (skill_id == skill.first) { + c->Message( + Chat::White, + fmt::format( + "Skill {}: {}", + skill.first, + skill.second + ).c_str() + ); + break; + } + } + } + else { + c->Message( + Chat::White, + fmt::format( + "Skill ID {} was not found.", + skill_id + ).c_str() + ); + } + } + else { + std::string search_criteria = str_tolower(sep->argplus[1]); + if (!search_criteria.empty()) { + int found_count = 0; + for (auto skill : skills) { + std::string skill_name_lower = str_tolower(skill.second); + if (skill_name_lower.find(search_criteria) == std::string::npos) { + continue; + } + + c->Message( + Chat::White, + fmt::format( + "Skill {}: {}", + skill.first, + skill.second + ).c_str() + ); + found_count++; + + if (found_count == 20) { + break; + } + } + + if (found_count == 20) { + c->Message(Chat::White, "20 Skills were found, max reached."); + } + else { + auto skill_message = ( + found_count > 0 ? + ( + found_count == 1 ? + "A Skill was" : + fmt::format("{} Skills were", found_count) + ) : + "No Skills were" + ); + + c->Message( + Chat::White, + fmt::format( + "{} found.", + skill_message + ).c_str() + ); + } + } + } +} + diff --git a/zone/gm_commands/findspell.cpp b/zone/gm_commands/findspell.cpp new file mode 100755 index 000000000..b17febac4 --- /dev/null +++ b/zone/gm_commands/findspell.cpp @@ -0,0 +1,129 @@ +#include "../client.h" + +void command_findspell(Client *c, const Seperator *sep) +{ + if (SPDAT_RECORDS <= 0) { + c->Message(Chat::White, "Spells not loaded"); + return; + } + + int arguments = sep->argnum; + + if (arguments == 0) { + c->Message(Chat::White, "Command Syntax: #findspell [search criteria]"); + return; + } + + if (sep->IsNumber(1)) { + int spell_id = std::stoi(sep->arg[1]); + if (!IsValidSpell(spell_id)) { + c->Message( + Chat::White, + fmt::format( + "Spell ID {} was not found.", + spell_id + ).c_str() + ); + } + else { + c->Message( + Chat::White, + fmt::format( + "Spell {}: {}", + spell_id, + spells[spell_id].name + ).c_str() + ); + } + } + else { + std::string search_criteria = str_tolower(sep->argplus[1]); + int found_count = 0; + for (int spell_id = 0; spell_id < SPDAT_RECORDS; spell_id++) { + auto current_spell = spells[spell_id]; + if (current_spell.name[0] != 0) { + std::string spell_name = current_spell.name; + std::string spell_name_lower = str_tolower(spell_name); + if (search_criteria.length() > 0 && spell_name_lower.find(search_criteria) == std::string::npos) { + continue; + } + + c->Message( + Chat::White, + fmt::format( + "Spell {}: {}", + spell_id, + spell_name + ).c_str() + ); + found_count++; + + if (found_count == 20) { + break; + } + } + } + + if (found_count == 20) { + c->Message(Chat::White, "20 Spells found... max reached."); + } + else { + auto spell_message = ( + found_count > 0 ? + ( + found_count == 1 ? + "A Spell was" : + fmt::format("{} Spells were", found_count) + ) : + "No Spells were" + ); + + c->Message( + Chat::White, + fmt::format( + "{} found.", + spell_message + ).c_str() + ); + } + } +} + +inline bool CastRestrictedSpell(int spellid) +{ + switch (spellid) { + case SPELL_TOUCH_OF_VINITRAS: + case SPELL_DESPERATE_HOPE: + case SPELL_CHARM: + case SPELL_METAMORPHOSIS65: + case SPELL_JT_BUFF: + case SPELL_CAN_O_WHOOP_ASS: + case SPELL_PHOENIX_CHARM: + case SPELL_CAZIC_TOUCH: + case SPELL_AVATAR_KNOCKBACK: + case SPELL_SHAPECHANGE65: + case SPELL_SUNSET_HOME1218: + case SPELL_SUNSET_HOME819: + case SPELL_SHAPECHANGE75: + case SPELL_SHAPECHANGE80: + case SPELL_SHAPECHANGE85: + case SPELL_SHAPECHANGE90: + case SPELL_SHAPECHANGE95: + case SPELL_SHAPECHANGE100: + case SPELL_SHAPECHANGE25: + case SPELL_SHAPECHANGE30: + case SPELL_SHAPECHANGE35: + case SPELL_SHAPECHANGE40: + case SPELL_SHAPECHANGE45: + case SPELL_SHAPECHANGE50: + case SPELL_NPC_AEGOLISM: + case SPELL_SHAPECHANGE55: + case SPELL_SHAPECHANGE60: + case SPELL_COMMAND_OF_DRUZZIL: + case SPELL_SHAPECHANGE70: + return true; + default: + return false; + } +} + diff --git a/zone/gm_commands/findtask.cpp b/zone/gm_commands/findtask.cpp new file mode 100755 index 000000000..5edf0dcde --- /dev/null +++ b/zone/gm_commands/findtask.cpp @@ -0,0 +1,89 @@ +#include "../client.h" + +void command_findtask(Client *c, const Seperator *sep) +{ + if (RuleB(TaskSystem, EnableTaskSystem)) { + int arguments = sep->argnum; + + if (arguments == 0) { + c->Message(Chat::White, "Command Syntax: #findtask [search criteria]"); + return; + } + + if (sep->IsNumber(1)) { + auto task_id = std::stoul(sep->arg[1]); + auto task_name = task_manager->GetTaskName(task_id); + auto task_message = ( + !task_name.empty() ? + fmt::format( + "Task {}: {}", + task_id, + task_name + ).c_str() : + fmt::format( + "Task ID {} was not found.", + task_id + ).c_str() + ); + + c->Message( + Chat::White, + task_message + ); + } + else { + std::string search_criteria = str_tolower(sep->argplus[1]); + if (!search_criteria.empty()) { + int found_count = 0; + for (uint32 task_id = 1; task_id <= MAXTASKS; task_id++) { + auto task_name = task_manager->GetTaskName(task_id); + std::string task_name_lower = str_tolower(task_name); + if (task_name_lower.find(search_criteria) == std::string::npos) { + continue; + } + + c->Message( + Chat::White, + fmt::format( + "Task {}: {}", + task_id, + task_name + ).c_str() + ); + found_count++; + + if (found_count == 20) { + break; + } + } + + if (found_count == 20) { + c->Message(Chat::White, "20 Tasks were found, max reached."); + } + else { + auto task_message = ( + found_count > 0 ? + ( + found_count == 1 ? + "A Task was" : + fmt::format("{} Tasks were", found_count) + ) : + "No Tasks were" + ); + + c->Message( + Chat::White, + fmt::format( + "{} found.", + task_message + ).c_str() + ); + } + } + } + } + else { + c->Message(Chat::White, "This command cannot be used while the Task system is disabled."); + } +} + diff --git a/zone/gm_commands/findzone.cpp b/zone/gm_commands/findzone.cpp new file mode 100755 index 000000000..53dacc52d --- /dev/null +++ b/zone/gm_commands/findzone.cpp @@ -0,0 +1,95 @@ +#include "../client.h" + +void command_findzone(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #findzone [search criteria]"); + c->Message(Chat::White, "Usage: #findzone expansion [expansion number]"); + return; + } + + std::string query; + int id = atoi((const char *) sep->arg[1]); + + std::string arg1 = sep->arg[1]; + + if (arg1 == "expansion") { + query = fmt::format( + "SELECT zoneidnumber, short_name, long_name, version FROM zone WHERE expansion = {}", + sep->arg[2] + ); + } + else { + + /** + * If id evaluates to 0, then search as if user entered a string + */ + if (id == 0) { + query = fmt::format( + "SELECT zoneidnumber, short_name, long_name, version FROM zone WHERE long_name LIKE '%{}%' OR `short_name` LIKE '%{}%'", + EscapeString(sep->arg[1]), + EscapeString(sep->arg[1]) + ); + } + else { + query = fmt::format( + "SELECT zoneidnumber, short_name, long_name, version FROM zone WHERE zoneidnumber = {}", + id + ); + } + } + + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Error querying database."); + c->Message(Chat::White, query.c_str()); + return; + } + + int count = 0; + const int maxrows = 100; + + for (auto row = results.begin(); row != results.end(); ++row) { + std::string zone_id = row[0]; + std::string short_name = row[1]; + std::string long_name = row[2]; + int version = atoi(row[3]); + + if (++count > maxrows) { + c->Message(Chat::White, "%i zones shown. Too many results.", maxrows); + break; + } + + std::string command_zone = EQ::SayLinkEngine::GenerateQuestSaylink("#zone " + short_name, false, "zone"); + std::string command_gmzone = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#gmzone {} {}", short_name, version), + false, + "gmzone" + ); + + c->Message( + Chat::White, + fmt::format( + "[{}] [{}] [{}] ID ({}) Version ({}) [{}]", + (version == 0 ? command_zone : "zone"), + command_gmzone, + short_name, + zone_id, + version, + long_name + ).c_str() + ); + } + + if (count <= maxrows) { + c->Message( + Chat::White, + "Query complete. %i rows shown. %s", + count, + (arg1 == "expansion" ? "(expansion search)" : "")); + } + else if (count == 0) { + c->Message(Chat::White, "No matches found for %s.", sep->arg[1]); + } +} + diff --git a/zone/gm_commands/fixmob.cpp b/zone/gm_commands/fixmob.cpp new file mode 100755 index 000000000..00e2edf11 --- /dev/null +++ b/zone/gm_commands/fixmob.cpp @@ -0,0 +1,250 @@ +#include "../client.h" + +void command_fixmob(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + const char *Usage = "Usage: #fixmob [race|gender|texture|helm|face|hair|haircolor|beard|beardcolor|heritage|tattoo|detail] [next|prev]"; + + if (!sep->arg[1]) { + c->Message(Chat::White, Usage); + } + else if (!target) { + c->Message(Chat::White, "Error: this command requires a target"); + } + else { + + uint32 Adjustment = 1; // Previous or Next + char codeMove = 0; + + if (sep->arg[2]) { + char *command2 = sep->arg[2]; + codeMove = (command2[0] | 0x20); // First character, lower-cased + if (codeMove == 'n') { + Adjustment = 1; + } + else if (codeMove == 'p') { + Adjustment = -1; + } + } + + uint16 Race = target->GetRace(); + uint8 Gender = target->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = 0xFF; + uint8 HairColor = target->GetHairColor(); + uint8 BeardColor = target->GetBeardColor(); + uint8 EyeColor1 = target->GetEyeColor1(); + uint8 EyeColor2 = target->GetEyeColor2(); + uint8 HairStyle = target->GetHairStyle(); + uint8 LuclinFace = target->GetLuclinFace(); + uint8 Beard = target->GetBeard(); + uint32 DrakkinHeritage = target->GetDrakkinHeritage(); + uint32 DrakkinTattoo = target->GetDrakkinTattoo(); + uint32 DrakkinDetails = target->GetDrakkinDetails(); + + const char *ChangeType = nullptr; // If it's still nullptr after processing, they didn't send a valid command + uint32 ChangeSetting; + char *command = sep->arg[1]; + + if (strcasecmp(command, "race") == 0) { + if (Race == 1 && codeMove == 'p') { + Race = RuleI(NPC, MaxRaceID); + } + else if (Race >= RuleI(NPC, MaxRaceID) && codeMove != 'p') { + Race = 1; + } + else { + Race += Adjustment; + } + ChangeType = "Race"; + ChangeSetting = Race; + } + else if (strcasecmp(command, "gender") == 0) { + if (Gender == 0 && codeMove == 'p') { + Gender = 2; + } + else if (Gender >= 2 && codeMove != 'p') { + Gender = 0; + } + else { + Gender += Adjustment; + } + ChangeType = "Gender"; + ChangeSetting = Gender; + } + else if (strcasecmp(command, "texture") == 0) { + Texture = target->GetTexture(); + + if (Texture == 0 && codeMove == 'p') { + Texture = 25; + } + else if (Texture >= 25 && codeMove != 'p') { + Texture = 0; + } + else { + Texture += Adjustment; + } + ChangeType = "Texture"; + ChangeSetting = Texture; + } + else if (strcasecmp(command, "helm") == 0) { + HelmTexture = target->GetHelmTexture(); + if (HelmTexture == 0 && codeMove == 'p') { + HelmTexture = 25; + } + else if (HelmTexture >= 25 && codeMove != 'p') { + HelmTexture = 0; + } + else { + HelmTexture += Adjustment; + } + ChangeType = "HelmTexture"; + ChangeSetting = HelmTexture; + } + else if (strcasecmp(command, "face") == 0) { + if (LuclinFace == 0 && codeMove == 'p') { + LuclinFace = 87; + } + else if (LuclinFace >= 87 && codeMove != 'p') { + LuclinFace = 0; + } + else { + LuclinFace += Adjustment; + } + ChangeType = "LuclinFace"; + ChangeSetting = LuclinFace; + } + else if (strcasecmp(command, "hair") == 0) { + if (HairStyle == 0 && codeMove == 'p') { + HairStyle = 8; + } + else if (HairStyle >= 8 && codeMove != 'p') { + HairStyle = 0; + } + else { + HairStyle += Adjustment; + } + ChangeType = "HairStyle"; + ChangeSetting = HairStyle; + } + else if (strcasecmp(command, "haircolor") == 0) { + if (HairColor == 0 && codeMove == 'p') { + HairColor = 24; + } + else if (HairColor >= 24 && codeMove != 'p') { + HairColor = 0; + } + else { + HairColor += Adjustment; + } + ChangeType = "HairColor"; + ChangeSetting = HairColor; + } + else if (strcasecmp(command, "beard") == 0) { + if (Beard == 0 && codeMove == 'p') { + Beard = 11; + } + else if (Beard >= 11 && codeMove != 'p') { + Beard = 0; + } + else { + Beard += Adjustment; + } + ChangeType = "Beard"; + ChangeSetting = Beard; + } + else if (strcasecmp(command, "beardcolor") == 0) { + if (BeardColor == 0 && codeMove == 'p') { + BeardColor = 24; + } + else if (BeardColor >= 24 && codeMove != 'p') { + BeardColor = 0; + } + else { + BeardColor += Adjustment; + } + ChangeType = "BeardColor"; + ChangeSetting = BeardColor; + } + else if (strcasecmp(command, "heritage") == 0) { + if (DrakkinHeritage == 0 && codeMove == 'p') { + DrakkinHeritage = 6; + } + else if (DrakkinHeritage >= 6 && codeMove != 'p') { + DrakkinHeritage = 0; + } + else { + DrakkinHeritage += Adjustment; + } + ChangeType = "DrakkinHeritage"; + ChangeSetting = DrakkinHeritage; + } + else if (strcasecmp(command, "tattoo") == 0) { + if (DrakkinTattoo == 0 && codeMove == 'p') { + DrakkinTattoo = 8; + } + else if (DrakkinTattoo >= 8 && codeMove != 'p') { + DrakkinTattoo = 0; + } + else { + DrakkinTattoo += Adjustment; + } + ChangeType = "DrakkinTattoo"; + ChangeSetting = DrakkinTattoo; + } + else if (strcasecmp(command, "detail") == 0) { + if (DrakkinDetails == 0 && codeMove == 'p') { + DrakkinDetails = 7; + } + else if (DrakkinDetails >= 7 && codeMove != 'p') { + DrakkinDetails = 0; + } + else { + DrakkinDetails += Adjustment; + } + ChangeType = "DrakkinDetails"; + ChangeSetting = DrakkinDetails; + } + + // Hack to fix some races that base features from face + switch (Race) { + case 2: // Barbarian + if (LuclinFace > 10) { + LuclinFace -= ((DrakkinTattoo - 1) * 10); + } + LuclinFace += (DrakkinTattoo * 10); + break; + case 3: // Erudite + if (LuclinFace > 10) { + LuclinFace -= ((HairStyle - 1) * 10); + } + LuclinFace += (HairStyle * 10); + break; + case 5: // HighElf + case 6: // DarkElf + case 7: // HalfElf + if (LuclinFace > 10) { + LuclinFace -= ((Beard - 1) * 10); + } + LuclinFace += (Beard * 10); + break; + default: + break; + } + + + if (ChangeType == nullptr) { + c->Message(Chat::White, Usage); + } + else { + target->SendIllusionPacket( + Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails + ); + + c->Message(Chat::White, "%s=%i", ChangeType, ChangeSetting); + } + } +} + diff --git a/zone/gm_commands/flag.cpp b/zone/gm_commands/flag.cpp new file mode 100755 index 000000000..8f02c8624 --- /dev/null +++ b/zone/gm_commands/flag.cpp @@ -0,0 +1,53 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_flag(Client *c, const Seperator *sep) +{ + if (sep->arg[2][0] == 0) { + if (!c->GetTarget() || (c->GetTarget() && c->GetTarget() == c)) { + c->UpdateAdmin(); + c->Message(Chat::White, "Refreshed your admin flag from DB."); + } + else if (c->GetTarget() && c->GetTarget() != c && c->GetTarget()->IsClient()) { + c->GetTarget()->CastToClient()->UpdateAdmin(); + c->Message(Chat::White, "%s's admin flag has been refreshed.", c->GetTarget()->GetName()); + c->GetTarget()->Message(Chat::White, "%s refreshed your admin flag.", c->GetName()); + } + } + else if (!sep->IsNumber(1) || atoi(sep->arg[1]) < -2 || atoi(sep->arg[1]) > 255 || strlen(sep->arg[2]) == 0) { + c->Message(Chat::White, "Usage: #flag [status] [acctname]"); + } + + else if (c->Admin() < commandChangeFlags) { + //this check makes banning players by less than this level + //impossible, but i'll leave it in anyways + c->Message(Chat::White, "You may only refresh your own flag, doing so now."); + c->UpdateAdmin(); + } + else { + if (atoi(sep->arg[1]) > c->Admin()) { + c->Message(Chat::White, "You cannot set people's status to higher than your own"); + } + else if (atoi(sep->arg[1]) < 0 && c->Admin() < commandBanPlayers) { + c->Message(Chat::White, "You have too low of status to suspend/ban"); + } + else if (!database.SetAccountStatus(sep->argplus[2], atoi(sep->arg[1]))) { + c->Message(Chat::White, "Unable to set GM Flag."); + } + else { + c->Message(Chat::White, "Set GM Flag on account."); + + std::string user; + std::string loginserver; + ParseAccountString(sep->argplus[2], user, loginserver); + + ServerPacket pack(ServerOP_FlagUpdate, 6); + *((uint32 *) pack.pBuffer) = database.GetAccountIDByName(user.c_str(), loginserver.c_str()); + *((int16 *) &pack.pBuffer[4]) = atoi(sep->arg[1]); + worldserver.SendPacket(&pack); + } + } +} + diff --git a/zone/gm_commands/flagedit.cpp b/zone/gm_commands/flagedit.cpp new file mode 100755 index 000000000..7e0bff517 --- /dev/null +++ b/zone/gm_commands/flagedit.cpp @@ -0,0 +1,157 @@ +#include "../client.h" + +void command_flagedit(Client *c, const Seperator *sep) +{ + //super-command for editing zone flags + if (sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { + c->Message(Chat::White, "Syntax: #flagedit [lockzone|unlockzone|listzones|give|take]."); + c->Message( + Chat::White, + "...lockzone [zone id/short] [flag name] - Set the specified flag name on the zone, locking the zone" + ); + c->Message(Chat::White, "...unlockzone [zone id/short] - Removes the flag requirement from the specified zone"); + c->Message(Chat::White, "...listzones - List all zones which require a flag, and their flag's name"); + c->Message(Chat::White, "...give [zone id/short] - Give your target the zone flag for the specified zone."); + c->Message( + Chat::White, + "...take [zone id/short] - Take the zone flag for the specified zone away from your target" + ); + c->Message(Chat::White, "...Note: use #flags to view flags on a person"); + return; + } + + if (!strcasecmp(sep->arg[1], "lockzone")) { + uint32 zoneid = 0; + if (sep->arg[2][0] != '\0') { + zoneid = atoi(sep->arg[2]); + if (zoneid < 1) { + zoneid = ZoneID(sep->arg[2]); + } + } + if (zoneid < 1) { + c->Message(Chat::Red, "zone required. see help."); + return; + } + + char flag_name[128]; + if (sep->argplus[3][0] == '\0') { + c->Message(Chat::Red, "flag name required. see help."); + return; + } + database.DoEscapeString(flag_name, sep->argplus[3], 64); + flag_name[127] = '\0'; + + std::string query = StringFormat( + "UPDATE zone SET flag_needed = '%s' " + "WHERE zoneidnumber = %d AND version = %d", + flag_name, zoneid, zone->GetInstanceVersion()); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::Red, "Error updating zone: %s", results.ErrorMessage().c_str()); + return; + } + + c->Message(Chat::Yellow, "Success! Zone %s now requires a flag, named %s", ZoneName(zoneid), flag_name); + return; + } + + if (!strcasecmp(sep->arg[1], "unlockzone")) { + uint32 zoneid = 0; + if (sep->arg[2][0] != '\0') { + zoneid = atoi(sep->arg[2]); + if (zoneid < 1) { + zoneid = ZoneID(sep->arg[2]); + } + } + + if (zoneid < 1) { + c->Message(Chat::Red, "zone required. see help."); + return; + } + + std::string query = StringFormat( + "UPDATE zone SET flag_needed = '' " + "WHERE zoneidnumber = %d AND version = %d", + zoneid, zone->GetInstanceVersion()); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::Yellow, "Error updating zone: %s", results.ErrorMessage().c_str()); + return; + } + + c->Message(Chat::Yellow, "Success! Zone %s no longer requires a flag.", ZoneName(zoneid)); + return; + } + + if (!strcasecmp(sep->arg[1], "listzones")) { + std::string query = "SELECT zoneidnumber, short_name, long_name, version, flag_needed " + "FROM zone WHERE flag_needed != ''"; + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + return; + } + + c->Message(Chat::White, "Zones which require flags:"); + for (auto row = results.begin(); row != results.end(); ++row) + c->Message( + Chat::White, + "Zone %s (%s,%s) version %s requires key %s", + row[2], + row[0], + row[1], + row[3], + row[4] + ); + + return; + } + + if (!strcasecmp(sep->arg[1], "give")) { + uint32 zoneid = 0; + if (sep->arg[2][0] != '\0') { + zoneid = atoi(sep->arg[2]); + if (zoneid < 1) { + zoneid = ZoneID(sep->arg[2]); + } + } + if (zoneid < 1) { + c->Message(Chat::Red, "zone required. see help."); + return; + } + + Mob *t = c->GetTarget(); + if (t == nullptr || !t->IsClient()) { + c->Message(Chat::Red, "client target required"); + return; + } + + t->CastToClient()->SetZoneFlag(zoneid); + return; + } + + if (!strcasecmp(sep->arg[1], "give")) { + uint32 zoneid = 0; + if (sep->arg[2][0] != '\0') { + zoneid = atoi(sep->arg[2]); + if (zoneid < 1) { + zoneid = ZoneID(sep->arg[2]); + } + } + if (zoneid < 1) { + c->Message(Chat::Red, "zone required. see help."); + return; + } + + Mob *t = c->GetTarget(); + if (t == nullptr || !t->IsClient()) { + c->Message(Chat::Red, "client target required"); + return; + } + + t->CastToClient()->ClearZoneFlag(zoneid); + return; + } + + c->Message(Chat::Yellow, "Invalid action specified. use '#flagedit help' for help"); +} + diff --git a/zone/gm_commands/flags.cpp b/zone/gm_commands/flags.cpp new file mode 100755 index 000000000..c2e3d1f2d --- /dev/null +++ b/zone/gm_commands/flags.cpp @@ -0,0 +1,16 @@ +#include "../client.h" + +void command_flags(Client *c, const Seperator *sep) +{ + Client *t = c; + + if (c->Admin() >= minStatusToSeeOthersZoneFlags) { + Mob *tgt = c->GetTarget(); + if (tgt != nullptr && tgt->IsClient()) { + t = tgt->CastToClient(); + } + } + + t->SendZoneFlagInfo(c); +} + diff --git a/zone/gm_commands/flymode.cpp b/zone/gm_commands/flymode.cpp new file mode 100755 index 000000000..905241b50 --- /dev/null +++ b/zone/gm_commands/flymode.cpp @@ -0,0 +1,40 @@ +#include "../client.h" + +void command_flymode(Client *c, const Seperator *sep) +{ + Mob *t = c; + + if (strlen(sep->arg[1]) == 1 && sep->IsNumber(1) && atoi(sep->arg[1]) >= 0 && atoi(sep->arg[1]) <= 5) { + if (c->GetTarget()) { + t = c->GetTarget(); + } + + int fm = atoi(sep->arg[1]); + + t->SetFlyMode(static_cast(fm)); + t->SendAppearancePacket(AT_Levitate, fm); + if (sep->arg[1][0] == '0') { + c->Message(Chat::White, "Setting %s to Grounded", t->GetName()); + } + else if (sep->arg[1][0] == '1') { + c->Message(Chat::White, "Setting %s to Flying", t->GetName()); + } + else if (sep->arg[1][0] == '2') { + c->Message(Chat::White, "Setting %s to Levitating", t->GetName()); + } + else if (sep->arg[1][0] == '3') { + c->Message(Chat::White, "Setting %s to In Water", t->GetName()); + } + else if (sep->arg[1][0] == '4') { + c->Message(Chat::White, "Setting %s to Floating(Boat)", t->GetName()); + } + else if (sep->arg[1][0] == '5') { + c->Message(Chat::White, "Setting %s to Levitating While Running", t->GetName()); + } + } + else { + c->Message(Chat::White, "#flymode [0/1/2/3/4/5]"); + } +} + + diff --git a/zone/gm_commands/fov.cpp b/zone/gm_commands/fov.cpp new file mode 100755 index 000000000..50447a326 --- /dev/null +++ b/zone/gm_commands/fov.cpp @@ -0,0 +1,42 @@ +#include "../client.h" + +void command_fov(Client *c, const Seperator *sep) +{ + if (c->GetTarget()) { + auto target = c->GetTarget(); + std::string behind_message = ( + c->BehindMob( + target, + c->GetX(), + c->GetY() + ) ? + "behind" : + "not behind" + ); + std::string gender_message = ( + target->GetGender() == MALE ? + "he" : + ( + target->GetGender() == FEMALE ? + "she" : + "it" + ) + ); + + c->Message( + Chat::White, + fmt::format( + "You are {} {} ({}), {} has a heading of {}.", + behind_message, + target->GetCleanName(), + target->GetID(), + gender_message, + target->GetHeading() + ).c_str() + ); + } + else { + c->Message(Chat::White, "You must have a target to use this command."); + } +} + diff --git a/zone/gm_commands/freeze.cpp b/zone/gm_commands/freeze.cpp new file mode 100755 index 000000000..8cce36ba2 --- /dev/null +++ b/zone/gm_commands/freeze.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +void command_freeze(Client *c, const Seperator *sep) +{ + if (c->GetTarget() != 0) { + c->GetTarget()->SendAppearancePacket(AT_Anim, ANIM_FREEZE); + } + else { + c->Message(Chat::White, "ERROR: Freeze requires a target."); + } +} + diff --git a/zone/gm_commands/gassign.cpp b/zone/gm_commands/gassign.cpp new file mode 100755 index 000000000..1a830b2fd --- /dev/null +++ b/zone/gm_commands/gassign.cpp @@ -0,0 +1,14 @@ +#include "../client.h" + +void command_gassign(Client *c, const Seperator *sep) +{ + if (sep->IsNumber(1) && c->GetTarget() && c->GetTarget()->IsNPC() && + c->GetTarget()->CastToNPC()->GetSpawnPointID() > 0) { + int spawn2id = c->GetTarget()->CastToNPC()->GetSpawnPointID(); + database.AssignGrid(c, atoi(sep->arg[1]), spawn2id); + } + else { + c->Message(Chat::White, "Usage: #gassign [num] - must have an npc target!"); + } +} + diff --git a/zone/gm_commands/gearup.cpp b/zone/gm_commands/gearup.cpp new file mode 100755 index 000000000..e02ba600d --- /dev/null +++ b/zone/gm_commands/gearup.cpp @@ -0,0 +1,181 @@ +#include "../client.h" +#include "../../common/http/httplib.h" +#include "../../common/content/world_content_service.h" + +void command_gearup(Client *c, const Seperator *sep) +{ + std::string tool_table_name = "tool_gearup_armor_sets"; + if (!database.DoesTableExist(tool_table_name)) { + c->Message( + Chat::Yellow, + fmt::format( + "Table [{}] does not exist. Downloading from Github and installing...", + tool_table_name + ).c_str() + ); + + // http get request + httplib::Client cli("https://raw.githubusercontent.com"); + cli.set_connection_timeout(0, 15000000); // 15 sec + cli.set_read_timeout(15, 0); // 15 seconds + cli.set_write_timeout(15, 0); // 15 seconds + + int sourced_queries = 0; + std::string url = "/EQEmu/Server/master/utils/sql/git/optional/2020_07_20_tool_gearup_armor_sets.sql"; + + if (auto res = cli.Get(url.c_str())) { + if (res->status == 200) { + for (auto &s: SplitString(res->body, ';')) { + if (!trim(s).empty()) { + auto results = database.QueryDatabase(s); + if (!results.ErrorMessage().empty()) { + c->Message( + Chat::Yellow, + fmt::format( + "Error sourcing SQL [{}]", results.ErrorMessage() + ).c_str() + ); + return; + } + sourced_queries++; + } + } + } + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Error retrieving URL [{}]", + url + ).c_str() + ); + } + + c->Message( + Chat::Yellow, + fmt::format( + "Table [{}] installed. Sourced [{}] queries", + tool_table_name, sourced_queries + ).c_str() + ); + } + + std::string expansion_arg = sep->arg[1]; + std::string expansion_filter; + if (expansion_arg.length() > 0) { + expansion_filter = fmt::format("and `expansion` = {}", expansion_arg); + } + + auto results = database.QueryDatabase( + fmt::format( + SQL ( + select + item_id, + slot + from + {} + where + `class` = {} + and `level` = {} + {} + order by score desc, expansion desc + ), + tool_table_name, + c->GetClass(), + c->GetLevel(), + expansion_filter + ) + ); + + int items_equipped = 0; + int items_already_have = 0; + std::set equipped; + + for (auto row = results.begin(); row != results.end(); ++row) { + int item_id = atoi(row[0]); + int slot_id = atoi(row[1]); + + if (equipped.find(slot_id) != equipped.end()) { + if (slot_id == EQ::invslot::slotEar1) { + slot_id = EQ::invslot::slotEar2; + } + if (slot_id == EQ::invslot::slotFinger1) { + slot_id = EQ::invslot::slotFinger2; + } + if (slot_id == EQ::invslot::slotWrist1) { + slot_id = EQ::invslot::slotWrist2; + } + } + + if (equipped.find(slot_id) == equipped.end()) { + const EQ::ItemData *item = database.GetItem(item_id); + bool has_item = (c->GetInv().HasItem(item_id, 1, invWhereWorn) != INVALID_INDEX); + bool can_wear_item = !c->CheckLoreConflict(item) && !has_item; + if (!can_wear_item) { + items_already_have++; + } + + if (c->CastToMob()->CanClassEquipItem(item_id) && can_wear_item) { + equipped.insert(slot_id); + c->SummonItem( + item_id, + 0, 0, 0, 0, 0, 0, 0, 0, + slot_id + ); + items_equipped++; + } + } + } + + c->Message( + Chat::White, + fmt::format( + "Equipped items [{}] already had [{}] items equipped", + items_equipped, + items_already_have + ).c_str() + ); + + if (expansion_arg.empty()) { + results = database.QueryDatabase( + fmt::format( + SQL ( + select + expansion + from + {} + where + class = {} + and level = {} + group by + expansion; + ), + tool_table_name, + c->GetClass(), + c->GetLevel() + ) + ); + + c->Message(Chat::White, "Choose armor from a specific era"); + std::string message; + for (auto row = results.begin(); row != results.end(); ++row) { + int expansion = atoi(row[0]); + message += "[" + EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format("#gearup {}", expansion), + false, + Expansion::ExpansionName[expansion] + ) + "] "; + + if (message.length() > 2000) { + c->Message(Chat::White, message.c_str()); + message = ""; + } + } + if (message.length() > 0) { + c->Message(Chat::White, message.c_str()); + } + } + +} + diff --git a/zone/gm_commands/gender.cpp b/zone/gm_commands/gender.cpp new file mode 100755 index 000000000..0051ddd43 --- /dev/null +++ b/zone/gm_commands/gender.cpp @@ -0,0 +1,17 @@ +#include "../client.h" + +void command_gender(Client *c, const Seperator *sep) +{ + Mob *t = c->CastToMob(); + + if (sep->IsNumber(1) && atoi(sep->arg[1]) >= 0 && atoi(sep->arg[1]) <= 500) { + if ((c->GetTarget()) && c->Admin() >= commandGenderOthers) { + t = c->GetTarget(); + } + t->SendIllusionPacket(t->GetRace(), atoi(sep->arg[1])); + } + else { + c->Message(Chat::White, "Usage: #gender [0/1/2]"); + } +} + diff --git a/zone/gm_commands/getplayerburiedcorpsecount.cpp b/zone/gm_commands/getplayerburiedcorpsecount.cpp new file mode 100755 index 000000000..3be9b205c --- /dev/null +++ b/zone/gm_commands/getplayerburiedcorpsecount.cpp @@ -0,0 +1,27 @@ +#include "../client.h" +#include "../corpse.h" + +void command_getplayerburiedcorpsecount(Client *c, const Seperator *sep) +{ + Client *t = c; + + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + t = c->GetTarget()->CastToClient(); + } + else { + c->Message(Chat::White, "You must first select a target!"); + return; + } + + uint32 CorpseCount = database.GetCharacterBuriedCorpseCount(t->CharacterID()); + + if (CorpseCount > 0) { + c->Message(Chat::White, "Your target has a total of %u buried corpses.", CorpseCount); + } + else { + c->Message(Chat::White, "Your target doesn't have any buried corpses."); + } + + return; +} + diff --git a/zone/gm_commands/getvariable.cpp b/zone/gm_commands/getvariable.cpp new file mode 100755 index 000000000..23c90219e --- /dev/null +++ b/zone/gm_commands/getvariable.cpp @@ -0,0 +1,13 @@ +#include "../client.h" + +void command_getvariable(Client *c, const Seperator *sep) +{ + std::string tmp; + if (database.GetVariable(sep->argplus[1], tmp)) { + c->Message(Chat::White, "%s = %s", sep->argplus[1], tmp.c_str()); + } + else { + c->Message(Chat::White, "GetVariable(%s) returned false", sep->argplus[1]); + } +} + diff --git a/zone/gm_commands/ginfo.cpp b/zone/gm_commands/ginfo.cpp new file mode 100755 index 000000000..ddec8ba24 --- /dev/null +++ b/zone/gm_commands/ginfo.cpp @@ -0,0 +1,52 @@ +#include "../client.h" +#include "../groups.h" + +void command_ginfo(Client *c, const Seperator *sep) +{ + Client *t; + + if (c->GetTarget() && c->GetTarget()->IsClient()) { + t = c->GetTarget()->CastToClient(); + } + else { + t = c; + } + + Group *g = t->GetGroup(); + if (!g) { + c->Message(Chat::White, "This client is not in a group"); + return; + } + + c->Message( + Chat::White, + "Player: %s is in Group #%lu: with %i members", + t->GetName(), + (unsigned long) g->GetID(), + g->GroupCount()); + + uint32 r; + for (r = 0; r < MAX_GROUP_MEMBERS; r++) { + if (g->members[r] == nullptr) { + if (g->membername[r][0] == '\0') { + continue; + } + c->Message( + Chat::White, "...Zoned Member: %s, Roles: %s %s %s", g->membername[r], + (g->MemberRoles[r] & RoleAssist) ? "Assist" : "", + (g->MemberRoles[r] & RoleTank) ? "Tank" : "", + (g->MemberRoles[r] & RolePuller) ? "Puller" : "" + ); + } + else { + c->Message( + Chat::White, "...In-Zone Member: %s (0x%x) Roles: %s %s %s", g->membername[r], g->members[r], + (g->MemberRoles[r] & RoleAssist) ? "Assist" : "", + (g->MemberRoles[r] & RoleTank) ? "Tank" : "", + (g->MemberRoles[r] & RolePuller) ? "Puller" : "" + ); + + } + } +} + diff --git a/zone/gm_commands/giveitem.cpp b/zone/gm_commands/giveitem.cpp new file mode 100755 index 000000000..17458182b --- /dev/null +++ b/zone/gm_commands/giveitem.cpp @@ -0,0 +1,111 @@ +#include "../client.h" + +void command_giveitem(Client *c, const Seperator *sep) +{ + uint32 item_id = 0; + int16 charges = -1; + uint32 augment_one = 0; + uint32 augment_two = 0; + uint32 augment_three = 0; + uint32 augment_four = 0; + uint32 augment_five = 0; + uint32 augment_six = 0; + int arguments = sep->argnum; + std::string cmd_msg = sep->msg; + size_t link_open = cmd_msg.find('\x12'); + size_t link_close = cmd_msg.find_last_of('\x12'); + if (c->GetTarget()) { + if (!c->GetTarget()->IsClient()) { + c->Message(Chat::Red, "You can only give items to players with this command."); + return; + } + + if (link_open != link_close && (cmd_msg.length() - link_open) > EQ::constants::SAY_LINK_BODY_SIZE) { + EQ::SayLinkBody_Struct link_body; + EQ::saylink::DegenerateLinkBody( + link_body, + cmd_msg.substr(link_open + 1, EQ::constants::SAY_LINK_BODY_SIZE)); + item_id = link_body.item_id; + augment_one = link_body.augment_1; + augment_two = link_body.augment_2; + augment_three = link_body.augment_3; + augment_four = link_body.augment_4; + augment_five = link_body.augment_5; + augment_six = link_body.augment_6; + } + else if (sep->IsNumber(1)) { + item_id = atoi(sep->arg[1]); + } + else if (!sep->IsNumber(1)) { + c->Message( + Chat::Red, + "Usage: #giveitem [item id | link] [charges] [augment_one_id] [augment_two_id] [augment_three_id] [augment_four_id] [augment_five_id] [augment_six_id] (Charges are optional.)" + ); + return; + } + + Client *client_target = c->GetTarget()->CastToClient(); + uint8 item_status = 0; + uint8 current_status = c->Admin(); + const EQ::ItemData *item = database.GetItem(item_id); + if (item) { + item_status = item->MinStatus; + } + + if (item_status > current_status) { + c->Message( + Chat::White, + fmt::format( + "Insufficient status to summon this item, current status is {}, required status is {}.", + current_status, + item_status + ).c_str() + ); + return; + } + + if (arguments >= 2 && sep->IsNumber(2)) { + charges = atoi(sep->arg[2]); + } + + if (arguments >= 3 && sep->IsNumber(3)) { + augment_one = atoi(sep->arg[3]); + } + + if (arguments >= 4 && sep->IsNumber(4)) { + augment_two = atoi(sep->arg[4]); + } + + if (arguments >= 5 && sep->IsNumber(5)) { + augment_three = atoi(sep->arg[5]); + } + + if (arguments >= 6 && sep->IsNumber(6)) { + augment_four = atoi(sep->arg[6]); + } + + if (arguments >= 7 && sep->IsNumber(7)) { + augment_five = atoi(sep->arg[7]); + } + + if (arguments == 8 && sep->IsNumber(8)) { + augment_six = atoi(sep->arg[8]); + } + + client_target->SummonItem( + item_id, + charges, + augment_one, + augment_two, + augment_three, + augment_four, + augment_five, + augment_six + ); + } + else { + c->Message(Chat::Red, "You must target a client to give the item to."); + return; + } +} + diff --git a/zone/gm_commands/givemoney.cpp b/zone/gm_commands/givemoney.cpp new file mode 100755 index 000000000..e5b2c3167 --- /dev/null +++ b/zone/gm_commands/givemoney.cpp @@ -0,0 +1,33 @@ +#include "../client.h" + +void command_givemoney(Client *c, const Seperator *sep) +{ + if (!sep->IsNumber(1)) { //as long as the first one is a number, we'll just let atoi convert the rest to 0 or a number + c->Message(Chat::Red, "Usage: #Usage: #givemoney [pp] [gp] [sp] [cp]"); + } + else if (c->GetTarget() == nullptr) { + c->Message(Chat::Red, "You must target a player to give money to."); + } + else if (!c->GetTarget()->IsClient()) { + c->Message(Chat::Red, "You can only give money to players with this command."); + } + else { + //TODO: update this to the client, otherwise the client doesn't show any weight change until you zone, move an item, etc + c->GetTarget()->CastToClient()->AddMoneyToPP( + atoi(sep->arg[4]), + atoi(sep->arg[3]), + atoi(sep->arg[2]), + atoi(sep->arg[1]), + true + ); + c->Message( + Chat::White, + "Added %i Platinum, %i Gold, %i Silver, and %i Copper to %s's inventory.", + atoi(sep->arg[1]), + atoi(sep->arg[2]), + atoi(sep->arg[3]), + atoi(sep->arg[4]), + c->GetTarget()->GetName()); + } +} + diff --git a/zone/gm_commands/globalview.cpp b/zone/gm_commands/globalview.cpp new file mode 100755 index 000000000..948ecd353 --- /dev/null +++ b/zone/gm_commands/globalview.cpp @@ -0,0 +1,80 @@ +#include "../client.h" + +void command_globalview(Client *c, const Seperator *sep) +{ + NPC *npcmob = nullptr; + + if (c->GetTarget() && c->GetTarget()->IsNPC()) { + npcmob = c->GetTarget()->CastToNPC(); + QGlobalCache *npc_c = nullptr; + QGlobalCache *char_c = nullptr; + QGlobalCache *zone_c = nullptr; + + if (npcmob) { + npc_c = npcmob->GetQGlobals(); + } + + char_c = c->GetQGlobals(); + zone_c = zone->GetQGlobals(); + + std::list globalMap; + uint32 ntype = 0; + + if (npcmob) { + ntype = npcmob->GetNPCTypeID(); + } + + if (npc_c) { + QGlobalCache::Combine(globalMap, npc_c->GetBucket(), ntype, c->CharacterID(), zone->GetZoneID()); + } + + if (char_c) { + QGlobalCache::Combine(globalMap, char_c->GetBucket(), ntype, c->CharacterID(), zone->GetZoneID()); + } + + if (zone_c) { + QGlobalCache::Combine(globalMap, zone_c->GetBucket(), ntype, c->CharacterID(), zone->GetZoneID()); + } + + auto iter = globalMap.begin(); + uint32 gcount = 0; + + c->Message(Chat::White, "Name, Value"); + while (iter != globalMap.end()) { + c->Message(Chat::White, "%s %s", (*iter).name.c_str(), (*iter).value.c_str()); + ++iter; + ++gcount; + } + c->Message(Chat::White, "%u globals loaded.", gcount); + } + else { + QGlobalCache *char_c = nullptr; + QGlobalCache *zone_c = nullptr; + + char_c = c->GetQGlobals(); + zone_c = zone->GetQGlobals(); + + std::list globalMap; + uint32 ntype = 0; + + if (char_c) { + QGlobalCache::Combine(globalMap, char_c->GetBucket(), ntype, c->CharacterID(), zone->GetZoneID()); + } + + if (zone_c) { + QGlobalCache::Combine(globalMap, zone_c->GetBucket(), ntype, c->CharacterID(), zone->GetZoneID()); + } + + auto iter = globalMap.begin(); + uint32 gcount = 0; + + c->Message(Chat::White, "Name, Value"); + while (iter != globalMap.end()) { + c->Message(Chat::White, "%s %s", (*iter).name.c_str(), (*iter).value.c_str()); + ++iter; + ++gcount; + } + c->Message(Chat::White, "%u globals loaded.", gcount); + } +} + diff --git a/zone/gm_commands/gm.cpp b/zone/gm_commands/gm.cpp new file mode 100755 index 000000000..2920c5026 --- /dev/null +++ b/zone/gm_commands/gm.cpp @@ -0,0 +1,27 @@ +#include "../client.h" + +void command_gm(Client *c, const Seperator *sep) +{ + bool state = atobool(sep->arg[1]); + Client *t = c; + + if (c->GetTarget() && c->GetTarget()->IsClient()) { + t = c->GetTarget()->CastToClient(); + } + + if (sep->arg[1][0] != 0) { + t->SetGM(state); + c->Message(Chat::White, "%s is %s a GM.", t->GetName(), state ? "now" : "no longer"); + } + else { + c->Message(Chat::White, "Usage: #gm [on/off]"); + } +} + +// there's no need for this, as /summon already takes care of it +// this command is here for reference but it is not added to the +// list above + +//To whoever wrote the above: And what about /kill, /zone, /zoneserver, etc? +//There is a reason for the # commands: so that admins can specifically enable certain +//commands for their users. Some might want users to #summon but not to /kill. Cant do that if they are a GM diff --git a/zone/gm_commands/gmspeed.cpp b/zone/gm_commands/gmspeed.cpp new file mode 100755 index 000000000..424c68ef5 --- /dev/null +++ b/zone/gm_commands/gmspeed.cpp @@ -0,0 +1,20 @@ +#include "../client.h" + +void command_gmspeed(Client *c, const Seperator *sep) +{ + bool state = atobool(sep->arg[1]); + Client *t = c; + + if (c->GetTarget() && c->GetTarget()->IsClient()) { + t = c->GetTarget()->CastToClient(); + } + + if (sep->arg[1][0] != 0) { + database.SetGMSpeed(t->AccountID(), state ? 1 : 0); + c->Message(Chat::White, "Turning GMSpeed %s for %s (zone to take effect)", state ? "On" : "Off", t->GetName()); + } + else { + c->Message(Chat::White, "Usage: #gmspeed [on/off]"); + } +} + diff --git a/zone/gm_commands/gmzone.cpp b/zone/gm_commands/gmzone.cpp new file mode 100755 index 000000000..074ae7c7d --- /dev/null +++ b/zone/gm_commands/gmzone.cpp @@ -0,0 +1,88 @@ +#include "../client.h" +#include "../data_bucket.h" + +void command_gmzone(Client *c, const Seperator *sep) +{ + if (!sep->arg[1]) { + c->Message(Chat::White, "Usage"); + c->Message(Chat::White, "-------"); + c->Message(Chat::White, "#gmzone [zone_short_name] [zone_version=0]"); + return; + } + + std::string zone_short_name_string = sep->arg[1]; + const char *zone_short_name = sep->arg[1]; + auto zone_version = static_cast(sep->arg[2] ? atoi(sep->arg[2]) : 0); + std::string identifier = "gmzone"; + uint32 zone_id = ZoneID(zone_short_name); + uint32 duration = 100000000; + uint16 instance_id = 0; + + if (zone_id == 0) { + c->Message(Chat::Red, "Invalid zone specified"); + return; + } + + if (sep->arg[3] && sep->arg[3][0]) { + identifier = sep->arg[3]; + } + + std::string bucket_key = StringFormat( + "%s-%s-%u-instance", + zone_short_name, + identifier.c_str(), + zone_version + ); + std::string existing_zone_instance = DataBucket::GetData(bucket_key); + + if (existing_zone_instance.length() > 0) { + instance_id = std::stoi(existing_zone_instance); + + c->Message(Chat::Yellow, "Found already created instance (%s) (%u)", zone_short_name, instance_id); + } + + if (instance_id == 0) { + if (!database.GetUnusedInstanceID(instance_id)) { + c->Message(Chat::Red, "Server was unable to find a free instance id."); + return; + } + + if (!database.CreateInstance(instance_id, zone_id, zone_version, duration)) { + c->Message(Chat::Red, "Server was unable to create a new instance."); + return; + } + + c->Message( + Chat::Yellow, + "New private GM instance %s was created with id %lu.", + zone_short_name, + (unsigned long) instance_id + ); + DataBucket::SetData(bucket_key, std::to_string(instance_id)); + } + + if (instance_id > 0) { + float target_x = -1, target_y = -1, target_z = -1, target_heading = -1; + int16 min_status = AccountStatus::Player; + uint8 min_level = 0; + + if (!content_db.GetSafePoints( + zone_short_name, + zone_version, + &target_x, + &target_y, + &target_z, + &target_heading, + &min_status, + &min_level + )) { + c->Message(Chat::Red, "Failed to find safe coordinates for specified zone"); + } + + c->Message(Chat::Yellow, "Zoning to private GM instance (%s) (%u)", zone_short_name, instance_id); + + c->AssignToInstance(instance_id); + c->MovePC(zone_id, instance_id, target_x, target_y, target_z, target_heading, 1); + } +} + diff --git a/zone/gm_commands/goto.cpp b/zone/gm_commands/goto.cpp new file mode 100755 index 000000000..cdc8da2e2 --- /dev/null +++ b/zone/gm_commands/goto.cpp @@ -0,0 +1,63 @@ +#include "../client.h" + +void command_goto(Client *c, const Seperator *sep) +{ + std::string arg1 = sep->arg[1]; + + bool goto_via_target_no_args = sep->arg[1][0] == '\0' && c->GetTarget(); + bool goto_via_player_name = !sep->IsNumber(1) && !arg1.empty(); + bool goto_via_x_y_z = sep->IsNumber(1) && sep->IsNumber(2) && sep->IsNumber(3); + + if (goto_via_target_no_args) { + c->MovePC( + zone->GetZoneID(), + zone->GetInstanceID(), + c->GetTarget()->GetX(), + c->GetTarget()->GetY(), + c->GetTarget()->GetZ(), + c->GetTarget()->GetHeading() + ); + } + else if (goto_via_player_name) { + + /** + * Find them in zone first + */ + const char *player_name = sep->arg[1]; + std::string player_name_string = sep->arg[1]; + Client *client = entity_list.GetClientByName(player_name); + if (client) { + c->MovePC( + zone->GetZoneID(), + zone->GetInstanceID(), + client->GetX(), + client->GetY(), + client->GetZ(), + client->GetHeading() + ); + + c->Message(Chat::Yellow, "Goto player '%s' same zone", player_name_string.c_str()); + } + else if (c->GotoPlayer(player_name_string)) { + c->Message(Chat::Yellow, "Goto player '%s' different zone", player_name_string.c_str()); + } + else { + c->Message(Chat::Yellow, "Player '%s' not found", player_name_string.c_str()); + } + } + else if (goto_via_x_y_z) { + c->MovePC( + zone->GetZoneID(), + zone->GetInstanceID(), + atof(sep->arg[1]), + atof(sep->arg[2]), + atof(sep->arg[3]), + (sep->arg[4] ? atof(sep->arg[4]) : c->GetHeading()) + ); + } + else { + c->Message(Chat::White, "Usage: #goto [x y z] [h]"); + c->Message(Chat::White, "Usage: #goto [player_name]"); + } +} + diff --git a/zone/gm_commands/grid.cpp b/zone/gm_commands/grid.cpp new file mode 100755 index 000000000..3ff62539c --- /dev/null +++ b/zone/gm_commands/grid.cpp @@ -0,0 +1,143 @@ +#include "../client.h" + +void command_grid(Client *c, const Seperator *sep) +{ + auto command_type = sep->arg[1]; + auto zone_id = zone->GetZoneID(); + if (strcasecmp("max", command_type) == 0) { + c->Message( + Chat::White, + fmt::format( + "Highest grid ID in this zone is {}.", + content_db.GetHighestGrid(zone_id) + ).c_str() + ); + } + else if (strcasecmp("add", command_type) == 0) { + auto grid_id = atoi(sep->arg[2]); + auto wander_type = atoi(sep->arg[3]); + auto pause_type = atoi(sep->arg[4]); + if (!content_db.GridExistsInZone(zone_id, grid_id)) { + content_db.ModifyGrid(c, false, grid_id, wander_type, pause_type, zone_id); + c->Message( + Chat::White, + fmt::format( + "Grid {} added to zone ID {} with wander type {} and pause type {}.", + grid_id, + zone_id, + wander_type, + pause_type + ).c_str() + ); + } + else { + c->Message( + Chat::White, + fmt::format( + "Grid {} already exists in zone ID {}.", + grid_id, + zone_id + ).c_str() + ); + return; + } + } + else if (strcasecmp("show", command_type) == 0) { + Mob *target = c->GetTarget(); + if (!target || !target->IsNPC()) { + c->Message(Chat::White, "You need to target an NPC!"); + return; + } + + auto grid_id = target->CastToNPC()->GetGrid(); + std::string query = fmt::format( + "SELECT `x`, `y`, `z`, `heading`, `number` " + "FROM `grid_entries` " + "WHERE `zoneid` = {} AND `gridid` = {} " + "ORDER BY `number`", + zone_id, + grid_id + ); + + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Error querying database."); + c->Message(Chat::White, query.c_str()); + } + + if (results.RowCount() == 0) { + c->Message(Chat::White, "No grid found."); + return; + } + + // Depop any node npc's already spawned + entity_list.DespawnGridNodes(grid_id); + + // Spawn grid nodes + std::map, int32> zoffset; + for (auto row : results) { + glm::vec4 node_position = glm::vec4(atof(row[0]), atof(row[1]), atof(row[2]), atof(row[3])); + std::vector node_loc{ + node_position.x, + node_position.y, + node_position.z + }; + + // If we already have a node at this location, set the z offset + // higher from the existing one so we can see it. Adjust so if + // there is another at the same spot we adjust again. + auto search = zoffset.find(node_loc); + if (search != zoffset.end()) { + search->second = search->second + 3; + } + else { + zoffset[node_loc] = 0.0; + } + + node_position.z += zoffset[node_loc]; + NPC::SpawnGridNodeNPC(node_position, grid_id, atoi(row[4]), zoffset[node_loc]); + } + c->Message( + Chat::White, + fmt::format( + "Spawning nodes for grid {}.", + grid_id + ).c_str() + ); + } + else if (strcasecmp("hide", command_type) == 0) { + Mob *target = c->GetTarget(); + if (!target || !target->IsNPC()) { + c->Message(Chat::White, "You need to target an NPC!"); + return; + } + + auto grid_id = target->CastToNPC()->GetGrid(); + entity_list.DespawnGridNodes(grid_id); + c->Message( + Chat::White, + fmt::format( + "Depawning nodes for grid {}.", + grid_id + ).c_str() + ); + } + else if (strcasecmp("delete", command_type) == 0) { + auto grid_id = atoi(sep->arg[2]); + content_db.ModifyGrid(c, true, grid_id, 0, 0, zone_id); + c->Message( + Chat::White, + fmt::format( + "Grid {} deleted from zone ID {}.", + grid_id, + zone_id + ).c_str() + ); + } + else { + c->Message(Chat::White, "Usage: #grid [add|delete] [grid_id] [wander_type] [pause_type]"); + c->Message(Chat::White, "Usage: #grid [max] - displays the highest grid ID used in this zone (for add)"); + c->Message(Chat::White, "Usage: #grid [show] - displays wp nodes as boxes"); + } +} + diff --git a/zone/gm_commands/guild.cpp b/zone/gm_commands/guild.cpp new file mode 100755 index 000000000..e76f83965 --- /dev/null +++ b/zone/gm_commands/guild.cpp @@ -0,0 +1,444 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +#include "../guild_mgr.h" +#include "../doors.h" + +void command_guild(Client *c, const Seperator *sep) +{ + int admin = c->Admin(); + Mob *target = c->GetTarget(); + + if (strcasecmp(sep->arg[1], "help") == 0) { + c->Message(Chat::White, "GM Guild commands:"); + c->Message(Chat::White, " #guild list - lists all guilds on the server"); + c->Message(Chat::White, " #guild create {guildleader charname or CharID} guildname"); + c->Message(Chat::White, " #guild delete guildID"); + c->Message(Chat::White, " #guild rename guildID newname"); + c->Message(Chat::White, " #guild set charname guildID (0=no guild)"); + c->Message(Chat::White, " #guild setrank charname rank"); + c->Message(Chat::White, " #guild setleader guildID {guildleader charname or CharID}"); + } + else if (strcasecmp(sep->arg[1], "status") == 0 || strcasecmp(sep->arg[1], "stat") == 0) { + Client *client = 0; + if (sep->arg[2][0] != 0) { + client = entity_list.GetClientByName(sep->argplus[2]); + } + else if (target != 0 && target->IsClient()) { + client = target->CastToClient(); + } + if (client == 0) { + c->Message(Chat::White, "You must target someone or specify a character name"); + } + else if ((client->Admin() >= minStatusToEditOtherGuilds && admin < minStatusToEditOtherGuilds) && + client->GuildID() != c->GuildID()) { // no peeping for GMs, make sure tell message stays the same + c->Message(Chat::White, "You must target someone or specify a character name."); + } + else { + if (client->IsInAGuild()) { + c->Message(Chat::White, "%s is not in a guild.", client->GetName()); + } + else if (guild_mgr.IsGuildLeader(client->GuildID(), client->CharacterID())) { + c->Message( + Chat::White, + "%s is the leader of <%s> rank: %s", + client->GetName(), + guild_mgr.GetGuildName(client->GuildID()), + guild_mgr.GetRankName(client->GuildID(), client->GuildRank())); + } + else { + c->Message( + Chat::White, + "%s is a member of <%s> rank: %s", + client->GetName(), + guild_mgr.GetGuildName(client->GuildID()), + guild_mgr.GetRankName(client->GuildID(), client->GuildRank())); + } + } + } + else if (strcasecmp(sep->arg[1], "info") == 0) { + if (sep->arg[2][0] == 0 && c->IsInAGuild()) { + if (admin >= minStatusToEditOtherGuilds) { + c->Message(Chat::White, "Usage: #guildinfo guild_id"); + } + else { + c->Message(Chat::White, "You're not in a guild"); + } + } + else { + uint32 tmp = GUILD_NONE; + if (sep->arg[2][0] == 0) { + tmp = c->GuildID(); + } + else if (admin >= minStatusToEditOtherGuilds) { + tmp = atoi(sep->arg[2]); + } + + if (tmp != GUILD_NONE) { + guild_mgr.DescribeGuild(c, tmp); + } + } + } + else if (strcasecmp(sep->arg[1], "set") == 0) { + if (!sep->IsNumber(3)) { + c->Message(Chat::White, "Usage: #guild set charname guildgbid (0 = clear guildtag)"); + } + else { + uint32 guild_id = atoi(sep->arg[3]); + + if (guild_id == 0) { + guild_id = GUILD_NONE; + } + else if (!guild_mgr.GuildExists(guild_id)) { + c->Message(Chat::Red, "Guild %d does not exist.", guild_id); + return; + } + + uint32 charid = database.GetCharacterID(sep->arg[2]); + if (charid == 0) { + c->Message(Chat::Red, "Unable to find character '%s'", charid); + return; + } + + //we could do the checking we need for guild_mgr.CheckGMStatus, but im lazy right now + if (admin < minStatusToEditOtherGuilds) { + c->Message(Chat::Red, "Access denied."); + return; + } + + if (guild_id == GUILD_NONE) { + LogGuilds("[{}]: Removing [{}] ([{}]) from guild with GM command", c->GetName(), sep->arg[2], charid); + } + else { + LogGuilds("[{}]: Putting [{}] ([{}]) into guild [{}] ([{}]) with GM command", + c->GetName(), + sep->arg[2], + charid, + guild_mgr.GetGuildName(guild_id), + guild_id); + } + + if (!guild_mgr.SetGuild(charid, guild_id, GUILD_MEMBER)) { + c->Message(Chat::Red, "Error putting '%s' into guild %d", sep->arg[2], guild_id); + } + else { + c->Message(Chat::White, "%s has been put into guild %d", sep->arg[2], guild_id); + } + } + } + /*else if (strcasecmp(sep->arg[1], "setdoor") == 0 && admin >= minStatusToEditOtherGuilds) { + + if (!sep->IsNumber(2)) + c->Message(Chat::White, "Usage: #guild setdoor guildEQid (0 = delete guilddoor)"); + else { + // guild doors + if((!guilds[atoi(sep->arg[2])].databaseID) && (atoi(sep->arg[2])!=0) ) + { + + c->Message(Chat::White, "These is no guild with this guildEQid"); + } + else { + c->SetIsSettingGuildDoor(true); + c->Message(Chat::White, "Click on a door you want to become a guilddoor"); + c->SetSetGuildDoorID(atoi(sep->arg[2])); + } + } + }*/ + else if (strcasecmp(sep->arg[1], "setrank") == 0) { + int rank = atoi(sep->arg[3]); + if (!sep->IsNumber(3)) { + c->Message(Chat::White, "Usage: #guild setrank charname rank"); + } + else if (rank < 0 || rank > GUILD_MAX_RANK) { + c->Message(Chat::White, "Error: invalid rank #."); + } + else { + uint32 charid = database.GetCharacterID(sep->arg[2]); + if (charid == 0) { + c->Message(Chat::Red, "Unable to find character '%s'", charid); + return; + } + + //we could do the checking we need for guild_mgr.CheckGMStatus, but im lazy right now + if (admin < minStatusToEditOtherGuilds) { + c->Message(Chat::Red, "Access denied."); + return; + } + + LogGuilds("[{}]: Setting [{}] ([{}])'s guild rank to [{}] with GM command", + c->GetName(), + sep->arg[2], + charid, + rank); + + if (!guild_mgr.SetGuildRank(charid, rank)) { + c->Message(Chat::Red, "Error while setting rank %d on '%s'.", rank, sep->arg[2]); + } + else { + c->Message(Chat::White, "%s has been set to rank %d", sep->arg[2], rank); + } + } + } + else if (strcasecmp(sep->arg[1], "create") == 0) { + if (sep->arg[3][0] == 0) { + c->Message(Chat::White, "Usage: #guild create {guildleader charname or CharID} guild name"); + } + else if (!worldserver.Connected()) { + c->Message(Chat::White, "Error: World server dirconnected"); + } + else { + uint32 leader = 0; + if (sep->IsNumber(2)) { + leader = atoi(sep->arg[2]); + } + else if ((leader = database.GetCharacterID(sep->arg[2])) != 0) { + //got it from the db.. + } + else { + c->Message(Chat::Red, "Unable to find char '%s'", sep->arg[2]); + return; + } + if (leader == 0) { + c->Message(Chat::White, "Guild leader not found."); + return; + } + + uint32 tmp = guild_mgr.FindGuildByLeader(leader); + if (tmp != GUILD_NONE) { + c->Message( + Chat::White, + "Error: %s already is the leader of DB# %i '%s'.", + sep->arg[2], + tmp, + guild_mgr.GetGuildName(tmp)); + } + else { + + if (admin < minStatusToEditOtherGuilds) { + c->Message(Chat::Red, "Access denied."); + return; + } + + uint32 id = guild_mgr.CreateGuild(sep->argplus[3], leader); + + LogGuilds("[{}]: Creating guild [{}] with leader [{}] with GM command. It was given id [{}]", + c->GetName(), + sep->argplus[3], + leader, + (unsigned long) id); + + if (id == GUILD_NONE) { + c->Message(Chat::White, "Guild creation failed."); + } + else { + c->Message(Chat::White, "Guild created: Leader: %i, number %i: %s", leader, id, sep->argplus[3]); + + if (!guild_mgr.SetGuild(leader, id, GUILD_LEADER)) { + c->Message( + Chat::White, + "Unable to set guild leader's guild in the database. Your going to have to run #guild set" + ); + } + } + + } + } + } + else if (strcasecmp(sep->arg[1], "delete") == 0) { + if (!sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #guild delete guildID"); + } + else if (!worldserver.Connected()) { + c->Message(Chat::White, "Error: World server dirconnected"); + } + else { + uint32 id = atoi(sep->arg[2]); + + if (!guild_mgr.GuildExists(id)) { + c->Message(Chat::White, "Guild %d does not exist!", id); + return; + } + + if (admin < minStatusToEditOtherGuilds) { + //this person is not allowed to just edit any guild, check this guild's min status. + if (c->GuildID() != id) { + c->Message(Chat::Red, "Access denied to edit other people's guilds"); + return; + } + else if (!guild_mgr.CheckGMStatus(id, admin)) { + c->Message(Chat::Red, "Access denied to edit your guild with GM commands."); + return; + } + } + + LogGuilds("[{}]: Deleting guild [{}] ([{}]) with GM command", c->GetName(), + guild_mgr.GetGuildName(id), id); + + if (!guild_mgr.DeleteGuild(id)) { + c->Message(Chat::White, "Guild delete failed."); + } + else { + c->Message(Chat::White, "Guild %d deleted.", id); + } + } + } + else if (strcasecmp(sep->arg[1], "rename") == 0) { + if ((!sep->IsNumber(2)) || sep->arg[3][0] == 0) { + c->Message(Chat::White, "Usage: #guild rename guildID newname"); + } + else if (!worldserver.Connected()) { + c->Message(Chat::White, "Error: World server dirconnected"); + } + else { + uint32 id = atoi(sep->arg[2]); + + if (!guild_mgr.GuildExists(id)) { + c->Message(Chat::White, "Guild %d does not exist!", id); + return; + } + + if (admin < minStatusToEditOtherGuilds) { + //this person is not allowed to just edit any guild, check this guild's min status. + if (c->GuildID() != id) { + c->Message(Chat::Red, "Access denied to edit other people's guilds"); + return; + } + else if (!guild_mgr.CheckGMStatus(id, admin)) { + c->Message(Chat::Red, "Access denied to edit your guild with GM commands."); + return; + } + } + + LogGuilds("[{}]: Renaming guild [{}] ([{}]) to [{}] with GM command", c->GetName(), + guild_mgr.GetGuildName(id), id, sep->argplus[3]); + + if (!guild_mgr.RenameGuild(id, sep->argplus[3])) { + c->Message(Chat::White, "Guild rename failed."); + } + else { + c->Message(Chat::White, "Guild %d renamed to %s", id, sep->argplus[3]); + } + } + } + else if (strcasecmp(sep->arg[1], "setleader") == 0) { + if (sep->arg[3][0] == 0 || !sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #guild setleader guild_id {guildleader charname or CharID}"); + } + else if (!worldserver.Connected()) { + c->Message(Chat::White, "Error: World server dirconnected"); + } + else { + uint32 leader = 0; + if (sep->IsNumber(2)) { + leader = atoi(sep->arg[2]); + } + else if ((leader = database.GetCharacterID(sep->arg[2])) != 0) { + //got it from the db.. + } + else { + c->Message(Chat::Red, "Unable to find char '%s'", sep->arg[2]); + return; + } + + uint32 tmpdb = guild_mgr.FindGuildByLeader(leader); + if (leader == 0) { + c->Message(Chat::White, "New leader not found."); + } + else if (tmpdb != 0) { + c->Message(Chat::White, "Error: %s already is the leader of guild # %i", sep->arg[2], tmpdb); + } + else { + uint32 id = atoi(sep->arg[2]); + + if (!guild_mgr.GuildExists(id)) { + c->Message(Chat::White, "Guild %d does not exist!", id); + return; + } + + if (admin < minStatusToEditOtherGuilds) { + //this person is not allowed to just edit any guild, check this guild's min status. + if (c->GuildID() != id) { + c->Message(Chat::Red, "Access denied to edit other people's guilds"); + return; + } + else if (!guild_mgr.CheckGMStatus(id, admin)) { + c->Message(Chat::Red, "Access denied to edit your guild with GM commands."); + return; + } + } + + LogGuilds("[{}]: Setting leader of guild [{}] ([{}]) to [{}] with GM command", c->GetName(), + guild_mgr.GetGuildName(id), id, leader); + + if (!guild_mgr.SetGuildLeader(id, leader)) { + c->Message(Chat::White, "Guild leader change failed."); + } + else { + c->Message(Chat::White, "Guild leader changed: guild # %d, Leader: %s", id, sep->argplus[3]); + } + } + } + } + else if (strcasecmp(sep->arg[1], "list") == 0) { + if (admin < minStatusToEditOtherGuilds) { + c->Message(Chat::Red, "Access denied."); + return; + } + guild_mgr.ListGuilds(c); + } + else { + c->Message(Chat::White, "Unknown guild command, try #guild help"); + } +} +/* +bool helper_guild_edit(Client *c, uint32 dbid, uint32 eqid, uint8 rank, const char* what, const char* value) { + struct GuildRankLevel_Struct grl; + strcpy(grl.rankname, guild_mgr.GetRankName(eqid, rank)); + grl.demote = guilds[eqid].rank[rank].demote; + grl.heargu = guilds[eqid].rank[rank].heargu; + grl.invite = guilds[eqid].rank[rank].invite; + grl.motd = guilds[eqid].rank[rank].motd; + grl.promote = guilds[eqid].rank[rank].promote; + grl.remove = guilds[eqid].rank[rank].remove; + grl.speakgu = guilds[eqid].rank[rank].speakgu; + grl.warpeace = guilds[eqid].rank[rank].warpeace; + + if (strcasecmp(what, "title") == 0) { + if (strlen(value) > 100) + c->Message(Chat::White, "Error: Title has a maxium length of 100 characters."); + else + strcpy(grl.rankname, value); + } + else if (rank == 0) + c->Message(Chat::White, "Error: Rank 0's permissions can not be changed."); + else { + if (!(strlen(value) == 1 && (value[0] == '0' || value[0] == '1'))) + + return false; + if (strcasecmp(what, "demote") == 0) + grl.demote = (value[0] == '1'); + else if (strcasecmp(what, "heargu") == 0) + grl.heargu = (value[0] == '1'); + else if (strcasecmp(what, "invite") == 0) + grl.invite = (value[0] == '1'); + else if (strcasecmp(what, "motd") == 0) + grl.motd = (value[0] == '1'); + else if (strcasecmp(what, "promote") == 0) + grl.promote = (value[0] == '1'); + else if (strcasecmp(what, "remove") == 0) + + grl.remove = (value[0] == '1'); + else if (strcasecmp(what, "speakgu") == 0) + grl.speakgu = (value[0] == '1'); + else if (strcasecmp(what, "warpeace") == 0) + grl.warpeace = (value[0] == '1'); + else + c->Message(Chat::White, "Error: Permission name not recognized."); + } + if (!database.EditGuild(dbid, rank, &grl)) + c->Message(Chat::White, "Error: database.EditGuild() failed"); + return true; +}*/ + diff --git a/zone/gm_commands/guildapprove.cpp b/zone/gm_commands/guildapprove.cpp new file mode 100755 index 000000000..5c7805224 --- /dev/null +++ b/zone/gm_commands/guildapprove.cpp @@ -0,0 +1,8 @@ +#include "../client.h" +#include "../guild_mgr.h" + +void command_guildapprove(Client *c, const Seperator *sep) +{ + guild_mgr.AddMemberApproval(atoi(sep->arg[1]), c); +} + diff --git a/zone/gm_commands/guildcreate.cpp b/zone/gm_commands/guildcreate.cpp new file mode 100755 index 000000000..48e90d3ef --- /dev/null +++ b/zone/gm_commands/guildcreate.cpp @@ -0,0 +1,13 @@ +#include "../client.h" +#include "../guild_mgr.h" + +void command_guildcreate(Client *c, const Seperator *sep) +{ + if (strlen(sep->argplus[1]) > 4 && strlen(sep->argplus[1]) < 16) { + guild_mgr.AddGuildApproval(sep->argplus[1], c); + } + else { + c->Message(Chat::White, "Guild name must be more than 4 characters and less than 16."); + } +} + diff --git a/zone/gm_commands/guildlist.cpp b/zone/gm_commands/guildlist.cpp new file mode 100755 index 000000000..3ec40ef02 --- /dev/null +++ b/zone/gm_commands/guildlist.cpp @@ -0,0 +1,14 @@ +#include "../client.h" +#include "../guild_mgr.h" + +void command_guildlist(Client *c, const Seperator *sep) +{ + GuildApproval *tmp = guild_mgr.FindGuildByIDApproval(atoi(sep->arg[1])); + if (tmp) { + tmp->ApprovedMembers(c); + } + else { + c->Message(Chat::White, "Could not find reference id."); + } +} + diff --git a/zone/gm_commands/hair.cpp b/zone/gm_commands/hair.cpp new file mode 100755 index 000000000..56f80aaed --- /dev/null +++ b/zone/gm_commands/hair.cpp @@ -0,0 +1,37 @@ +#include "../client.h" + +void command_hair(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #hair [number of hair style]"); + } + else if (!target) { + c->Message(Chat::White, "Error: this command requires a target"); + } + else { + uint16 Race = target->GetRace(); + uint8 Gender = target->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = 0xFF; + uint8 HairColor = target->GetHairColor(); + uint8 BeardColor = target->GetBeardColor(); + uint8 EyeColor1 = target->GetEyeColor1(); + uint8 EyeColor2 = target->GetEyeColor2(); + uint8 HairStyle = atoi(sep->arg[1]); + uint8 LuclinFace = target->GetLuclinFace(); + uint8 Beard = target->GetBeard(); + uint32 DrakkinHeritage = target->GetDrakkinHeritage(); + uint32 DrakkinTattoo = target->GetDrakkinTattoo(); + uint32 DrakkinDetails = target->GetDrakkinDetails(); + + target->SendIllusionPacket( + Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails + ); + + c->Message(Chat::White, "Hair = %i", atoi(sep->arg[1])); + } +} + diff --git a/zone/gm_commands/haircolor.cpp b/zone/gm_commands/haircolor.cpp new file mode 100755 index 000000000..2767808ac --- /dev/null +++ b/zone/gm_commands/haircolor.cpp @@ -0,0 +1,37 @@ +#include "../client.h" + +void command_haircolor(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #haircolor [number of hair color]"); + } + else if (!target) { + c->Message(Chat::White, "Error: this command requires a target"); + } + else { + uint16 Race = target->GetRace(); + uint8 Gender = target->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = 0xFF; + uint8 HairColor = atoi(sep->arg[1]); + uint8 BeardColor = target->GetBeardColor(); + uint8 EyeColor1 = target->GetEyeColor1(); + uint8 EyeColor2 = target->GetEyeColor2(); + uint8 HairStyle = target->GetHairStyle(); + uint8 LuclinFace = target->GetLuclinFace(); + uint8 Beard = target->GetBeard(); + uint32 DrakkinHeritage = target->GetDrakkinHeritage(); + uint32 DrakkinTattoo = target->GetDrakkinTattoo(); + uint32 DrakkinDetails = target->GetDrakkinDetails(); + + target->SendIllusionPacket( + Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails + ); + + c->Message(Chat::White, "Hair Color = %i", atoi(sep->arg[1])); + } +} + diff --git a/zone/gm_commands/haste.cpp b/zone/gm_commands/haste.cpp new file mode 100755 index 000000000..bf5be4d26 --- /dev/null +++ b/zone/gm_commands/haste.cpp @@ -0,0 +1,20 @@ +#include "../client.h" + +void command_haste(Client *c, const Seperator *sep) +{ + // #haste command to set client attack speed. Takes a percentage (100 = twice normal attack speed) + if (sep->arg[1][0] != 0) { + uint16 Haste = atoi(sep->arg[1]); + if (Haste > 85) { + Haste = 85; + } + c->SetExtraHaste(Haste); + // SetAttackTimer must be called to make this take effect, so player needs to change + // the primary weapon. + c->Message(Chat::White, "Haste set to %d%% - Need to re-equip primary weapon before it takes effect", Haste); + } + else { + c->Message(Chat::White, "Usage: #haste [percentage]"); + } +} + diff --git a/zone/gm_commands/hatelist.cpp b/zone/gm_commands/hatelist.cpp new file mode 100755 index 000000000..13006d47b --- /dev/null +++ b/zone/gm_commands/hatelist.cpp @@ -0,0 +1,15 @@ +#include "../client.h" + +void command_hatelist(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (target == nullptr) { + c->Message(Chat::White, "Error: you must have a target."); + return; + } + + c->Message(Chat::White, "Display hate list for %s..", target->GetName()); + target->PrintHateListToClient(c); +} + + diff --git a/zone/gm_commands/heal.cpp b/zone/gm_commands/heal.cpp new file mode 100755 index 000000000..f309b30e3 --- /dev/null +++ b/zone/gm_commands/heal.cpp @@ -0,0 +1,21 @@ +#include "../client.h" + +void command_heal(Client *c, const Seperator *sep) +{ + auto target = c->GetTarget() ? c->GetTarget() : c; + target->Heal(); + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "Healed {} ({}) to full.", + target->GetCleanName(), + target->GetID() + ).c_str() + ); + } + else { + c->Message(Chat::White, "Healed yourself to full."); + } +} + diff --git a/zone/gm_commands/helm.cpp b/zone/gm_commands/helm.cpp new file mode 100755 index 000000000..8d0a36407 --- /dev/null +++ b/zone/gm_commands/helm.cpp @@ -0,0 +1,37 @@ +#include "../client.h" + +void command_helm(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #helm [number of helm texture]"); + } + else if (!target) { + c->Message(Chat::White, "Error: this command requires a target"); + } + else { + uint16 Race = target->GetRace(); + uint8 Gender = target->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = atoi(sep->arg[1]); + uint8 HairColor = target->GetHairColor(); + uint8 BeardColor = target->GetBeardColor(); + uint8 EyeColor1 = target->GetEyeColor1(); + uint8 EyeColor2 = target->GetEyeColor2(); + uint8 HairStyle = target->GetHairStyle(); + uint8 LuclinFace = target->GetLuclinFace(); + uint8 Beard = target->GetBeard(); + uint32 DrakkinHeritage = target->GetDrakkinHeritage(); + uint32 DrakkinTattoo = target->GetDrakkinTattoo(); + uint32 DrakkinDetails = target->GetDrakkinDetails(); + + target->SendIllusionPacket( + Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails + ); + + c->Message(Chat::White, "Helm = %i", atoi(sep->arg[1])); + } +} + diff --git a/zone/gm_commands/heritage.cpp b/zone/gm_commands/heritage.cpp new file mode 100755 index 000000000..0f4e1d9e9 --- /dev/null +++ b/zone/gm_commands/heritage.cpp @@ -0,0 +1,37 @@ +#include "../client.h" + +void command_heritage(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #heritage [number of Drakkin heritage]"); + } + else if (!target) { + c->Message(Chat::White, "Error: this command requires a target"); + } + else { + uint16 Race = target->GetRace(); + uint8 Gender = target->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = 0xFF; + uint8 HairColor = target->GetHairColor(); + uint8 BeardColor = target->GetBeardColor(); + uint8 EyeColor1 = target->GetEyeColor1(); + uint8 EyeColor2 = target->GetEyeColor2(); + uint8 HairStyle = target->GetHairStyle(); + uint8 LuclinFace = target->GetLuclinFace(); + uint8 Beard = target->GetBeard(); + uint32 DrakkinHeritage = atoi(sep->arg[1]); + uint32 DrakkinTattoo = target->GetDrakkinTattoo(); + uint32 DrakkinDetails = target->GetDrakkinDetails(); + + target->SendIllusionPacket( + Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails + ); + + c->Message(Chat::White, "Heritage = %i", atoi(sep->arg[1])); + } +} + diff --git a/zone/gm_commands/heromodel.cpp b/zone/gm_commands/heromodel.cpp new file mode 100755 index 000000000..33ea72c2c --- /dev/null +++ b/zone/gm_commands/heromodel.cpp @@ -0,0 +1,35 @@ +#include "../client.h" + +void command_heromodel(Client *c, const Seperator *sep) +{ + if (sep->argnum < 1) { + c->Message(Chat::White, "Usage: #heromodel [hero forge model] [ [slot] ] (example: #heromodel 63)"); + } + else if (c->GetTarget() == nullptr) { + c->Message(Chat::Red, "You must have a target to do a wear change for Hero's Forge Models."); + } + else { + uint32 hero_forge_model = atoi(sep->arg[1]); + + if (sep->argnum > 1) { + uint8 wearslot = (uint8) atoi(sep->arg[2]); + c->GetTarget()->SendTextureWC(wearslot, 0, hero_forge_model, 0, 0, 0); + } + else { + if (hero_forge_model > 0) { + // Conversion to simplify the command arguments + // Hero's Forge model is actually model * 1000 + texture * 100 + wearslot + // Hero's Forge Model slot 7 is actually for Robes, but it still needs to use wearslot 1 in the packet + hero_forge_model *= 100; + + for (uint8 wearslot = 0; wearslot < 7; wearslot++) { + c->GetTarget()->SendTextureWC(wearslot, 0, (hero_forge_model + wearslot), 0, 0, 0); + } + } + else { + c->Message(Chat::Red, "Hero's Forge Model must be greater than 0."); + } + } + } +} + diff --git a/zone/gm_commands/hideme.cpp b/zone/gm_commands/hideme.cpp new file mode 100755 index 000000000..d936a7fdc --- /dev/null +++ b/zone/gm_commands/hideme.cpp @@ -0,0 +1,16 @@ +#include "../client.h" +#include "../string_ids.h" + +void command_hideme(Client *c, const Seperator *sep) +{ + bool state = atobool(sep->arg[1]); + + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #hideme [on/off]"); + } + else { + c->SetHideMe(state); + c->MessageString(Chat::Broadcasts, c->GetHideMe() ? NOW_INVISIBLE : NOW_VISIBLE, c->GetName()); + } +} + diff --git a/zone/gm_commands/hp.cpp b/zone/gm_commands/hp.cpp new file mode 100755 index 000000000..178ef9890 --- /dev/null +++ b/zone/gm_commands/hp.cpp @@ -0,0 +1,8 @@ +#include "../client.h" + +void command_hp(Client *c, const Seperator *sep) +{ + c->SendHPUpdate(); + c->CheckManaEndUpdate(); +} + diff --git a/zone/gm_commands/incstat.cpp b/zone/gm_commands/incstat.cpp new file mode 100755 index 000000000..6a09c1785 --- /dev/null +++ b/zone/gm_commands/incstat.cpp @@ -0,0 +1,18 @@ +#include "../client.h" + +void command_incstat(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] && sep->arg[2][0] && c->GetTarget() != 0 && c->GetTarget()->IsClient()) { + c->GetTarget()->CastToClient()->IncStats(atoi(sep->arg[1]), atoi(sep->arg[2])); + } + else { + c->Message(Chat::White, "This command is used to permanently increase or decrease a players stats."); + c->Message(Chat::White, "Usage: #setstat {type} {value by which to increase or decrease}"); + c->Message( + Chat::White, + "Note: The value is in increments of 2, so a value of 3 will actually increase the stat by 6" + ); + c->Message(Chat::White, "Types: Str: 0, Sta: 1, Agi: 2, Dex: 3, Int: 4, Wis: 5, Cha: 6"); + } +} + diff --git a/zone/gm_commands/instance.cpp b/zone/gm_commands/instance.cpp new file mode 100755 index 000000000..27c1171d7 --- /dev/null +++ b/zone/gm_commands/instance.cpp @@ -0,0 +1,188 @@ +#include "../client.h" + +void command_instance(Client *c, const Seperator *sep) +{ + if (!c) { + return; + } + + //options: + //help + //create [zone_id] [version] + //destroy [instance_id] + //add [instance_id] [player_name] + //remove [instance_id] [player_name] + //list [player_name] + + if (strcasecmp(sep->arg[1], "help") == 0) { + c->Message(Chat::White, "#instance usage:"); + c->Message( + Chat::White, "#instance create zone_id version duration - Creates an instance of version 'version' in the " + "zone with id matching zone_id, will last for duration seconds." + ); + c->Message(Chat::White, "#instance destroy instance_id - Destroys the instance with id matching instance_id."); + c->Message( + Chat::White, "#instance add instance_id player_name - adds the player 'player_name' to the instance " + "with id matching instance_id." + ); + c->Message( + Chat::White, "#instance remove instance_id player_name - removes the player 'player_name' from the " + "instance with id matching instance_id." + ); + c->Message(Chat::White, "#instance list player_name - lists all the instances 'player_name' is apart of."); + return; + } + else if (strcasecmp(sep->arg[1], "create") == 0) { + if (!sep->IsNumber(3) || !sep->IsNumber(4)) { + c->Message( + Chat::White, + "#instance create zone_id version duration - Creates an instance of version 'version' in the " + "zone with id matching zone_id, will last for duration seconds." + ); + return; + } + + const char *zn = nullptr; + uint32 zone_id = 0; + + if (sep->IsNumber(2)) { + zone_id = atoi(sep->arg[2]); + } + else { + zone_id = ZoneID(sep->arg[2]); + } + + uint32 version = atoi(sep->arg[3]); + uint32 duration = atoi(sep->arg[4]); + zn = ZoneName(zone_id); + + if (!zn) { + c->Message(Chat::White, "Zone with id %lu was not found by the server.", (unsigned long) zone_id); + return; + } + + uint16 id = 0; + if (!database.GetUnusedInstanceID(id)) { + c->Message(Chat::White, "Server was unable to find a free instance id."); + return; + } + + if (!database.CreateInstance(id, zone_id, version, duration)) { + c->Message(Chat::White, "Server was unable to create a new instance."); + return; + } + + c->Message(Chat::White, "New instance %s was created with id %lu.", zn, (unsigned long) id); + } + else if (strcasecmp(sep->arg[1], "destroy") == 0) { + if (!sep->IsNumber(2)) { + c->Message( + Chat::White, + "#instance destroy instance_id - Destroys the instance with id matching instance_id." + ); + return; + } + + uint16 id = atoi(sep->arg[2]); + database.DeleteInstance(id); + c->Message(Chat::White, "Destroyed instance with id %lu.", (unsigned long) id); + } + else if (strcasecmp(sep->arg[1], "add") == 0) { + if (!sep->IsNumber(2)) { + c->Message( + Chat::White, "#instance add instance_id player_name - adds the player 'player_name' to the instance " + "with id matching instance_id." + ); + return; + } + + uint16 id = atoi(sep->arg[2]); + uint32 charid = database.GetCharacterID(sep->arg[3]); + + if (id <= 0 || charid <= 0) { + c->Message(Chat::White, "Must enter a valid instance id and player name."); + return; + } + + if (!database.CheckInstanceExists(id)) { + c->Message(Chat::White, "Instance does not exist."); + return; + } + + uint32 zone_id = database.ZoneIDFromInstanceID(id); + uint32 version = database.VersionFromInstanceID(id); + uint32 cur_id = database.GetInstanceID(zone_id, charid, version); + if (cur_id == 0) { + if (database.AddClientToInstance(id, charid)) { + c->Message(Chat::White, "Added client to instance."); + } + else { + c->Message(Chat::White, "Failed to add client to instance."); + } + } + else { + c->Message( + Chat::White, + "Client was already saved to %u which has uses the same zone and version as that instance.", + cur_id + ); + } + } + else if (strcasecmp(sep->arg[1], "remove") == 0) { + if (!sep->IsNumber(2)) { + c->Message( + Chat::White, "#instance remove instance_id player_name - removes the player 'player_name' from the " + "instance with id matching instance_id." + ); + return; + } + + uint16 id = atoi(sep->arg[2]); + uint32 charid = database.GetCharacterID(sep->arg[3]); + + if (id <= 0 || charid <= 0) { + c->Message(Chat::White, "Must enter a valid instance id and player name."); + } + + if (database.RemoveClientFromInstance(id, charid)) { + c->Message(Chat::White, "Removed client from instance."); + } + else { + c->Message(Chat::White, "Failed to remove client from instance."); + } + } + else if (strcasecmp(sep->arg[1], "list") == 0) { + uint32 charid = database.GetCharacterID(sep->arg[2]); + if (charid <= 0) { + if (c->GetTarget() == nullptr || (c->GetTarget() && !c->GetTarget()->IsClient())) { + c->Message(Chat::White, "Character not found."); + return; + } + else { + charid = c->GetTarget()->CastToClient()->CharacterID(); + } + } + + database.ListAllInstances(c, charid); + } + else { + c->Message(Chat::White, "Invalid Argument."); + c->Message(Chat::White, "#instance usage:"); + c->Message( + Chat::White, "#instance create zone_id version duration - Creates an instance of version 'version' in the " + "zone with id matching zone_id, will last for duration seconds." + ); + c->Message(Chat::White, "#instance destroy instance_id - Destroys the instance with id matching instance_id."); + c->Message( + Chat::White, "#instance add instance_id player_name - adds the player 'player_name' to the instance " + "with id matching instance_id." + ); + c->Message( + Chat::White, "#instance remove instance_id player_name - removes the player 'player_name' from the " + "instance with id matching instance_id." + ); + c->Message(Chat::White, "#instance list player_name - lists all the instances 'player_name' is apart of."); + return; + } +} + diff --git a/zone/gm_commands/interrogateinv.cpp b/zone/gm_commands/interrogateinv.cpp new file mode 100755 index 000000000..58451e05c --- /dev/null +++ b/zone/gm_commands/interrogateinv.cpp @@ -0,0 +1,76 @@ +#include "../client.h" + +void command_interrogateinv(Client *c, const Seperator *sep) +{ + // 'command_interrogateinv' is an in-memory inventory interrogation tool only. + // + // it does not verify against actual database entries..but, the output can be + // used to verify that something has been corrupted in a player's inventory. + // any error condition should be assumed that the item in question will be + // lost when the player logs out or zones (or incurrs any action that will + // consume the Client-Inventory object instance in question.) + // + // any item instances located at a greater depth than a reported error should + // be treated as an error themselves regardless of whether they report as the + // same or not. + + if (strcasecmp(sep->arg[1], "help") == 0) { + if (c->Admin() < commandInterrogateInv) { + c->Message(Chat::White, "Usage: #interrogateinv"); + c->Message(Chat::White, " Displays your inventory's current in-memory nested storage references"); + } + else { + c->Message(Chat::White, "Usage: #interrogateinv [log] [silent]"); + c->Message( + Chat::White, + " Displays your or your Player target inventory's current in-memory nested storage references" + ); + c->Message(Chat::White, " [log] - Logs interrogation to file"); + c->Message(Chat::White, " [silent] - Omits the in-game message portion of the interrogation"); + } + return; + } + + Client *target = nullptr; + std::map instmap; + bool log = false; + bool silent = false; + bool error = false; + bool allowtrip = false; + + if (c->Admin() < commandInterrogateInv) { + if (c->GetInterrogateInvState()) { + c->Message(Chat::Red, "The last use of #interrogateinv on this inventory instance discovered an error..."); + c->Message(Chat::Red, "Logging out, zoning or re-arranging items at this point will result in item loss!"); + return; + } + target = c; + allowtrip = true; + } + else { + if (c->GetTarget() == nullptr) { + target = c; + } + else if (c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + else { + c->Message(Chat::Default, "Use of this command is limited to Client entities"); + return; + } + + if (strcasecmp(sep->arg[1], "log") == 0) { + log = true; + } + if (strcasecmp(sep->arg[2], "silent") == 0) { + silent = true; + } + } + + bool success = target->InterrogateInventory(c, log, silent, allowtrip, error); + + if (!success) { + c->Message(Chat::Red, "An unknown error occurred while processing Client::InterrogateInventory()"); + } +} + diff --git a/zone/gm_commands/interrupt.cpp b/zone/gm_commands/interrupt.cpp new file mode 100755 index 000000000..1126a850c --- /dev/null +++ b/zone/gm_commands/interrupt.cpp @@ -0,0 +1,16 @@ +#include "../client.h" + +void command_interrupt(Client *c, const Seperator *sep) +{ + uint16 ci_message = 0x01b7, ci_color = 0x0121; + + if (sep->arg[1][0]) { + ci_message = atoi(sep->arg[1]); + } + if (sep->arg[2][0]) { + ci_color = atoi(sep->arg[2]); + } + + c->InterruptSpell(ci_message, ci_color); +} + diff --git a/zone/gm_commands/invsnapshot.cpp b/zone/gm_commands/invsnapshot.cpp new file mode 100755 index 000000000..6e818524a --- /dev/null +++ b/zone/gm_commands/invsnapshot.cpp @@ -0,0 +1,418 @@ +#include "../client.h" + +void command_invsnapshot(Client *c, const Seperator *sep) +{ + if (!c) { + return; + } + + if (sep->argnum == 0 || strcmp(sep->arg[1], "help") == 0) { + std::string window_title = "Inventory Snapshot Argument Help Menu"; + + std::string window_text = + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + ""; + + if (c->Admin() >= commandInvSnapshot) { + window_text.append( + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + ); + } + + window_text.append( + "
Usage:#invsnapshot arguments
(required optional)
helpthis menu
capturetakes snapshot of character inventory
gcountreturns global snapshot count
gclear
now
delete all snapshots - rule
delete all snapshots - now
countreturns character snapshot count
clear
now
delete character snapshots - rule
delete character snapshots - now
list
count
lists entry ids for current character
limits to count
parsetstmpdisplays slots and items in snapshot
comparetstmpcompares inventory against snapshot
restoretstmprestores slots and items in snapshot
" + ); + + c->SendPopupToClient(window_title.c_str(), window_text.c_str()); + + return; + } + + if (c->Admin() >= commandInvSnapshot) { // global arguments + + if (strcmp(sep->arg[1], "gcount") == 0) { + auto is_count = database.CountInvSnapshots(); + c->Message( + Chat::White, + "There %s %i inventory snapshot%s.", + (is_count == 1 ? "is" : "are"), + is_count, + (is_count == 1 ? "" : "s")); + + return; + } + + if (strcmp(sep->arg[1], "gclear") == 0) { + if (strcmp(sep->arg[2], "now") == 0) { + database.ClearInvSnapshots(true); + c->Message(Chat::White, "Inventory snapshots cleared using current time."); + } + else { + database.ClearInvSnapshots(); + c->Message( + Chat::White, "Inventory snapshots cleared using RuleI(Character, InvSnapshotHistoryD) (%i day%s).", + RuleI(Character, InvSnapshotHistoryD), (RuleI(Character, InvSnapshotHistoryD) == 1 ? "" : "s")); + } + + return; + } + } + + if (!c->GetTarget() || !c->GetTarget()->IsClient()) { + c->Message(Chat::White, "Target must be a client."); + return; + } + + auto tc = (Client *) c->GetTarget(); + + if (strcmp(sep->arg[1], "capture") == 0) { + if (database.SaveCharacterInvSnapshot(tc->CharacterID())) { + tc->SetNextInvSnapshot(RuleI(Character, InvSnapshotMinIntervalM)); + c->Message( + Chat::White, + "Successful inventory snapshot taken of %s - setting next interval for %i minute%s.", + tc->GetName(), + RuleI(Character, InvSnapshotMinIntervalM), + (RuleI(Character, InvSnapshotMinIntervalM) == 1 ? "" : "s")); + } + else { + tc->SetNextInvSnapshot(RuleI(Character, InvSnapshotMinRetryM)); + c->Message( + Chat::White, + "Failed to take inventory snapshot of %s - retrying in %i minute%s.", + tc->GetName(), + RuleI(Character, InvSnapshotMinRetryM), + (RuleI(Character, InvSnapshotMinRetryM) == 1 ? "" : "s")); + } + + return; + } + + if (c->Admin() >= commandInvSnapshot) { + if (strcmp(sep->arg[1], "count") == 0) { + auto is_count = database.CountCharacterInvSnapshots(tc->CharacterID()); + c->Message( + Chat::White, + "%s (id: %u) has %i inventory snapshot%s.", + tc->GetName(), + tc->CharacterID(), + is_count, + (is_count == 1 ? "" : "s")); + + return; + } + + if (strcmp(sep->arg[1], "clear") == 0) { + if (strcmp(sep->arg[2], "now") == 0) { + database.ClearCharacterInvSnapshots(tc->CharacterID(), true); + c->Message( + Chat::White, + "%s\'s (id: %u) inventory snapshots cleared using current time.", + tc->GetName(), + tc->CharacterID()); + } + else { + database.ClearCharacterInvSnapshots(tc->CharacterID()); + c->Message( + Chat::White, + "%s\'s (id: %u) inventory snapshots cleared using RuleI(Character, InvSnapshotHistoryD) (%i day%s).", + tc->GetName(), + tc->CharacterID(), + RuleI(Character, InvSnapshotHistoryD), + (RuleI(Character, InvSnapshotHistoryD) == 1 ? "" : "s")); + } + + return; + } + + if (strcmp(sep->arg[1], "list") == 0) { + std::list> is_list; + database.ListCharacterInvSnapshots(tc->CharacterID(), is_list); + + if (is_list.empty()) { + c->Message(Chat::White, "No inventory snapshots for %s (id: %u)", tc->GetName(), tc->CharacterID()); + return; + } + + auto list_count = 0; + if (sep->IsNumber(2)) { + list_count = atoi(sep->arg[2]); + } + if (list_count < 1 || list_count > is_list.size()) { + list_count = is_list.size(); + } + + std::string window_title = StringFormat("Snapshots for %s", tc->GetName()); + + std::string window_text = + "" + "" + "" + "" + ""; + + for (auto iter : is_list) { + if (!list_count) { + break; + } + + window_text.append( + StringFormat( + "" + "" + "" + "", + iter.first, + iter.second + )); + + --list_count; + } + + window_text.append( + "
TimestampEntry Count
%u%i
" + ); + + c->SendPopupToClient(window_title.c_str(), window_text.c_str()); + + return; + } + + if (strcmp(sep->arg[1], "parse") == 0) { + if (!sep->IsNumber(2)) { + c->Message(Chat::White, "A timestamp is required to use this option."); + return; + } + + uint32 timestamp = atoul(sep->arg[2]); + + if (!database.ValidateCharacterInvSnapshotTimestamp(tc->CharacterID(), timestamp)) { + c->Message( + Chat::White, + "No inventory snapshots for %s (id: %u) exist at %u.", + tc->GetName(), + tc->CharacterID(), + timestamp + ); + return; + } + + std::list> parse_list; + database.ParseCharacterInvSnapshot(tc->CharacterID(), timestamp, parse_list); + + std::string window_title = StringFormat("Snapshot Parse for %s @ %u", tc->GetName(), timestamp); + + std::string window_text = "Slot: ItemID - Description
"; + + for (auto iter : parse_list) { + auto item_data = database.GetItem(iter.second); + std::string window_line = StringFormat( + "%i: %u - %s
", + iter.first, + iter.second, + (item_data ? item_data->Name : "[error]")); + + if (window_text.length() + window_line.length() < 4095) { + window_text.append(window_line); + } + else { + c->Message(Chat::White, "Too many snapshot entries to list..."); + break; + } + } + + c->SendPopupToClient(window_title.c_str(), window_text.c_str()); + + return; + } + + if (strcmp(sep->arg[1], "compare") == 0) { + if (!sep->IsNumber(2)) { + c->Message(Chat::White, "A timestamp is required to use this option."); + return; + } + + uint32 timestamp = atoul(sep->arg[2]); + + if (!database.ValidateCharacterInvSnapshotTimestamp(tc->CharacterID(), timestamp)) { + c->Message( + Chat::White, + "No inventory snapshots for %s (id: %u) exist at %u.", + tc->GetName(), + tc->CharacterID(), + timestamp + ); + return; + } + + std::list> inv_compare_list; + database.DivergeCharacterInventoryFromInvSnapshot(tc->CharacterID(), timestamp, inv_compare_list); + + std::list> iss_compare_list; + database.DivergeCharacterInvSnapshotFromInventory(tc->CharacterID(), timestamp, iss_compare_list); + + std::string window_title = StringFormat("Snapshot Comparison for %s @ %u", tc->GetName(), timestamp); + + std::string window_text = "Slot: (action) Snapshot -> Inventory
"; + + auto inv_iter = inv_compare_list.begin(); + auto iss_iter = iss_compare_list.begin(); + + while (true) { + std::string window_line; + + if (inv_iter == inv_compare_list.end() && iss_iter == iss_compare_list.end()) { + break; + } + else if (inv_iter != inv_compare_list.end() && iss_iter == iss_compare_list.end()) { + window_line = StringFormat("%i: (delete) [empty] -> %u
", inv_iter->first, inv_iter->second); + ++inv_iter; + } + else if (inv_iter == inv_compare_list.end() && iss_iter != iss_compare_list.end()) { + window_line = StringFormat("%i: (insert) %u -> [empty]
", iss_iter->first, iss_iter->second); + ++iss_iter; + } + else { + if (inv_iter->first < iss_iter->first) { + window_line = StringFormat( + "%i: (delete) [empty] -> %u
", + inv_iter->first, + inv_iter->second + ); + ++inv_iter; + } + else if (inv_iter->first > iss_iter->first) { + window_line = StringFormat( + "%i: (insert) %u -> [empty]
", + iss_iter->first, + iss_iter->second + ); + ++iss_iter; + } + else { + window_line = StringFormat( + "%i: (replace) %u -> %u
", + iss_iter->first, + iss_iter->second, + inv_iter->second + ); + ++inv_iter; + ++iss_iter; + } + } + + if (window_text.length() + window_line.length() < 4095) { + window_text.append(window_line); + } + else { + c->Message(Chat::White, "Too many comparison entries to list..."); + break; + } + } + + c->SendPopupToClient(window_title.c_str(), window_text.c_str()); + + return; + } + + if (strcmp(sep->arg[1], "restore") == 0) { + if (!sep->IsNumber(2)) { + c->Message(Chat::White, "A timestamp is required to use this option."); + return; + } + + uint32 timestamp = atoul(sep->arg[2]); + + if (!database.ValidateCharacterInvSnapshotTimestamp(tc->CharacterID(), timestamp)) { + c->Message( + Chat::White, + "No inventory snapshots for %s (id: %u) exist at %u.", + tc->GetName(), + tc->CharacterID(), + timestamp + ); + return; + } + + if (database.SaveCharacterInvSnapshot(tc->CharacterID())) { + tc->SetNextInvSnapshot(RuleI(Character, InvSnapshotMinIntervalM)); + } + else { + c->Message( + Chat::Red, "Failed to take pre-restore inventory snapshot of %s (id: %u).", + tc->GetName(), tc->CharacterID()); + return; + } + + if (database.RestoreCharacterInvSnapshot(tc->CharacterID(), timestamp)) { + // cannot delete all valid item slots from client..so, we worldkick + tc->WorldKick(); // self restores update before the 'kick' is processed + + c->Message( + Chat::White, "Successfully applied snapshot %u to %s's (id: %u) inventory.", + timestamp, tc->GetName(), tc->CharacterID()); + } + else { + c->Message( + Chat::Red, "Failed to apply snapshot %u to %s's (id: %u) inventory.", + timestamp, tc->GetName(), tc->CharacterID()); + } + + return; + } + } +} + diff --git a/zone/gm_commands/invul.cpp b/zone/gm_commands/invul.cpp new file mode 100755 index 000000000..4af42ceaf --- /dev/null +++ b/zone/gm_commands/invul.cpp @@ -0,0 +1,20 @@ +#include "../client.h" + +void command_invul(Client *c, const Seperator *sep) +{ + bool state = atobool(sep->arg[1]); + Client *t = c; + + if (c->GetTarget() && c->GetTarget()->IsClient()) { + t = c->GetTarget()->CastToClient(); + } + + if (sep->arg[1][0] != 0) { + t->SetInvul(state); + c->Message(Chat::White, "%s is %s invulnerable from attack.", t->GetName(), state ? "now" : "no longer"); + } + else { + c->Message(Chat::White, "Usage: #invulnerable [on/off]"); + } +} + diff --git a/zone/gm_commands/ipban.cpp b/zone/gm_commands/ipban.cpp new file mode 100755 index 000000000..5dd63a0fb --- /dev/null +++ b/zone/gm_commands/ipban.cpp @@ -0,0 +1,21 @@ +#include "../client.h" + +void command_ipban(Client *c, const Seperator *sep) +{ + if (sep->arg[1] == 0) { + c->Message(Chat::White, "Usage: #ipban [xxx.xxx.xxx.xxx]"); + } + else { + if (database.AddBannedIP(sep->arg[1], c->GetName())) { + c->Message( + Chat::White, + "%s has been successfully added to the banned_ips table by %s", + sep->arg[1], + c->GetName()); + } + else { + c->Message(Chat::White, "IPBan Failed (IP address is possibly already in the table?)"); + } + } +} + diff --git a/zone/gm_commands/iplookup.cpp b/zone/gm_commands/iplookup.cpp new file mode 100755 index 000000000..1595a5ee6 --- /dev/null +++ b/zone/gm_commands/iplookup.cpp @@ -0,0 +1,23 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_iplookup(Client *c, const Seperator *sep) +{ + auto pack = + new ServerPacket( + ServerOP_IPLookup, + sizeof(ServerGenericWorldQuery_Struct) + + strlen(sep->argplus[1]) + 1 + ); + ServerGenericWorldQuery_Struct *s = (ServerGenericWorldQuery_Struct *) pack->pBuffer; + strcpy(s->from, c->GetName()); + s->admin = c->Admin(); + if (sep->argplus[1][0] != 0) { + strcpy(s->query, sep->argplus[1]); + } + worldserver.SendPacket(pack); + safe_delete(pack); +} + diff --git a/zone/gm_commands/iteminfo.cpp b/zone/gm_commands/iteminfo.cpp new file mode 100755 index 000000000..683c23075 --- /dev/null +++ b/zone/gm_commands/iteminfo.cpp @@ -0,0 +1,94 @@ +#include "../client.h" +#include "../groups.h" + +void command_iteminfo(Client *c, const Seperator *sep) +{ + auto inst = c->GetInv()[EQ::invslot::slotCursor]; + if (!inst) { + c->Message(Chat::Red, "Error: You need an item on your cursor for this command"); + return; + } + auto item = inst->GetItem(); + if (!item) { + LogInventory("([{}]) Command #iteminfo processed an item with no data pointer"); + c->Message(Chat::Red, "Error: This item has no data reference"); + return; + } + + EQ::SayLinkEngine linker; + linker.SetLinkType(EQ::saylink::SayLinkItemInst); + linker.SetItemInst(inst); + + c->Message(Chat::White, "*** Item Info for [%s] ***", linker.GenerateLink().c_str()); + c->Message(Chat::White, ">> ID: %u, ItemUseType: %u, ItemClassType: %u", item->ID, item->ItemType, item->ItemClass); + c->Message(Chat::White, ">> IDFile: '%s', IconID: %u", item->IDFile, item->Icon); + c->Message( + Chat::White, + ">> Size: %u, Weight: %u, Price: %u, LDoNPrice: %u", + item->Size, + item->Weight, + item->Price, + item->LDoNPrice + ); + c->Message( + Chat::White, + ">> Material: 0x%02X, Color: 0x%08X, Tint: 0x%08X, Light: 0x%02X", + item->Material, + item->Color, + inst->GetColor(), + item->Light + ); + c->Message( + Chat::White, + ">> IsLore: %s, LoreGroup: %u, Lore: '%s'", + (item->LoreFlag ? "TRUE" : "FALSE"), + item->LoreGroup, + item->Lore + ); + c->Message( + Chat::White, ">> NoDrop: %u, NoRent: %u, NoPet: %u, NoTransfer: %u, FVNoDrop: %u", + item->NoDrop, item->NoRent, (uint8) item->NoPet, (uint8) item->NoTransfer, item->FVNoDrop + ); + + if (item->IsClassBook()) { + c->Message(Chat::White, "*** This item is a Book (filename:'%s') ***", item->Filename); + } + else if (item->IsClassBag()) { + c->Message(Chat::White, "*** This item is a Container (%u slots) ***", item->BagSlots); + } + else { + c->Message(Chat::White, "*** This item is Common ***"); + c->Message(Chat::White, ">> Classes: %u, Races: %u, Slots: %u", item->Classes, item->Races, item->Slots); + c->Message( + Chat::White, + ">> ReqSkill: %u, ReqLevel: %u, RecLevel: %u", + item->RecSkill, + item->ReqLevel, + item->RecLevel + ); + c->Message(Chat::White, ">> SkillModType: %u, SkillModValue: %i", item->SkillModType, item->SkillModValue); + c->Message( + Chat::White, ">> BaneRaceType: %u, BaneRaceDamage: %u, BaneBodyType: %u, BaneBodyDamage: %i", + item->BaneDmgRace, item->BaneDmgRaceAmt, item->BaneDmgBody, item->BaneDmgAmt + ); + c->Message( + Chat::White, + ">> Magic: %s, SpellID: %i, ProcLevel: %u, Charges: %u, MaxCharges: %u", + (item->Magic ? "TRUE" : "FALSE"), + item->Click.Effect, + item->Click.Level, + inst->GetCharges(), + item->MaxCharges + ); + c->Message( + Chat::White, + ">> EffectType: 0x%02X, CastTime: %.2f", + (uint8) item->Click.Type, + ((double) item->CastTime / 1000)); + } + + if (c->Admin() >= AccountStatus::GMMgmt) { + c->Message(Chat::White, ">> MinStatus: %u", item->MinStatus); + } +} + diff --git a/zone/gm_commands/itemsearch.cpp b/zone/gm_commands/itemsearch.cpp new file mode 100755 index 000000000..4617e2397 --- /dev/null +++ b/zone/gm_commands/itemsearch.cpp @@ -0,0 +1,125 @@ +#include "../client.h" + +void command_itemsearch(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #itemsearch [search string]"); + } + else { + const char *search_criteria = sep->argplus[1]; + + const EQ::ItemData *item = nullptr; + EQ::SayLinkEngine linker; + linker.SetLinkType(EQ::saylink::SayLinkItemData); + + if (Seperator::IsNumber(search_criteria)) { + item = database.GetItem(atoi(search_criteria)); + if (item) { + linker.SetItemData(item); + std::string item_id = std::to_string(item->ID); + std::string saylink_commands = + "[" + + EQ::SayLinkEngine::GenerateQuestSaylink( + "#si " + item_id, + false, + "X" + ) + + "] "; + + if (item->Stackable && item->StackSize > 1) { + std::string stack_size = std::to_string(item->StackSize); + saylink_commands += + "[" + + EQ::SayLinkEngine::GenerateQuestSaylink( + "#si " + item_id + " " + stack_size, + false, + stack_size + ) + + "]"; + } + + c->Message( + Chat::White, + fmt::format( + " Summon {} [{}] [{}]", + saylink_commands, + linker.GenerateLink(), + item->ID + ).c_str() + ); + } + else { + c->Message( + Chat::White, + fmt::format( + "Item {} not found", + search_criteria + ).c_str() + ); + } + + return; + } + + int count = 0; + char sName[64]; + char sCriteria[255]; + strn0cpy(sCriteria, search_criteria, sizeof(sCriteria)); + strupr(sCriteria); + char *pdest; + uint32 it = 0; + while ((item = database.IterateItems(&it))) { + strn0cpy(sName, item->Name, sizeof(sName)); + strupr(sName); + pdest = strstr(sName, sCriteria); + if (pdest != nullptr) { + linker.SetItemData(item); + std::string item_id = std::to_string(item->ID); + std::string saylink_commands = + "[" + + EQ::SayLinkEngine::GenerateQuestSaylink( + "#si " + item_id, + false, + "X" + ) + + "] "; + if (item->Stackable && item->StackSize > 1) { + std::string stack_size = std::to_string(item->StackSize); + saylink_commands += + "[" + + EQ::SayLinkEngine::GenerateQuestSaylink( + "#si " + item_id + " " + stack_size, + false, + stack_size + ) + + "]"; + } + + c->Message( + Chat::White, + fmt::format( + " Summon {} [{}] [{}]", + saylink_commands, + linker.GenerateLink(), + item->ID + ).c_str() + ); + + ++count; + } + + if (count == 50) { + break; + } + } + + if (count == 50) { + c->Message(Chat::White, "50 items shown...too many results."); + } + else { + c->Message(Chat::White, "%i items found", count); + } + + } +} + diff --git a/zone/gm_commands/kick.cpp b/zone/gm_commands/kick.cpp new file mode 100755 index 000000000..a6a4644c1 --- /dev/null +++ b/zone/gm_commands/kick.cpp @@ -0,0 +1,36 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_kick(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #kick [charname]"); + } + else { + Client *client = entity_list.GetClientByName(sep->arg[1]); + if (client != 0) { + if (client->Admin() <= c->Admin()) { + client->Message(Chat::White, "You have been kicked by %s", c->GetName()); + auto outapp = new EQApplicationPacket(OP_GMKick, 0); + client->QueuePacket(outapp); + client->Kick("Ordered kicked by command"); + c->Message(Chat::White, "Kick: local: kicking %s", sep->arg[1]); + } + } + else if (!worldserver.Connected()) { + c->Message(Chat::White, "Error: World server disconnected"); + } + else { + auto pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); + ServerKickPlayer_Struct *skp = (ServerKickPlayer_Struct *) pack->pBuffer; + strcpy(skp->adminname, c->GetName()); + strcpy(skp->name, sep->arg[1]); + skp->adminrank = c->Admin(); + worldserver.SendPacket(pack); + safe_delete(pack); + } + } +} + diff --git a/zone/gm_commands/kill.cpp b/zone/gm_commands/kill.cpp new file mode 100755 index 000000000..d54245662 --- /dev/null +++ b/zone/gm_commands/kill.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +void command_kill(Client *c, const Seperator *sep) +{ + if (!c->GetTarget()) { + c->Message(Chat::White, "Error: #Kill: No target."); + } + else if (!c->GetTarget()->IsClient() || c->GetTarget()->CastToClient()->Admin() <= c->Admin()) { + c->GetTarget()->Kill(); + } +} + diff --git a/zone/gm_commands/killallnpcs.cpp b/zone/gm_commands/killallnpcs.cpp new file mode 100755 index 000000000..c298f2abb --- /dev/null +++ b/zone/gm_commands/killallnpcs.cpp @@ -0,0 +1,45 @@ +#include "../client.h" + +void command_killallnpcs(Client *c, const Seperator *sep) +{ + std::string search_string; + if (sep->arg[1]) { + search_string = sep->arg[1]; + } + + int count = 0; + for (auto &itr : entity_list.GetMobList()) { + Mob *entity = itr.second; + if (!entity->IsNPC()) { + continue; + } + + std::string entity_name = entity->GetName(); + + /** + * Filter by name + */ + if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { + continue; + } + + bool is_not_attackable = + ( + entity->IsInvisible() || + !entity->IsAttackAllowed(c) || + entity->GetRace() == 127 || + entity->GetRace() == 240 + ); + + if (is_not_attackable) { + continue; + } + + entity->Damage(c, 1000000000, 0, EQ::skills::SkillDragonPunch); + + count++; + } + + c->Message(Chat::Yellow, "Killed (%i) npc(s)", count); +} + diff --git a/zone/gm_commands/lastname.cpp b/zone/gm_commands/lastname.cpp new file mode 100755 index 000000000..09bed4771 --- /dev/null +++ b/zone/gm_commands/lastname.cpp @@ -0,0 +1,19 @@ +#include "../client.h" + +void command_lastname(Client *c, const Seperator *sep) +{ + Client *t = c; + + if (c->GetTarget() && c->GetTarget()->IsClient()) { + t = c->GetTarget()->CastToClient(); + } + LogInfo("#lastname request from [{}] for [{}]", c->GetName(), t->GetName()); + + if (strlen(sep->arg[1]) <= 70) { + t->ChangeLastName(sep->arg[1]); + } + else { + c->Message(Chat::White, "Usage: #lastname where is less than 70 chars long"); + } +} + diff --git a/zone/gm_commands/list.cpp b/zone/gm_commands/list.cpp new file mode 100755 index 000000000..a38b3ddc3 --- /dev/null +++ b/zone/gm_commands/list.cpp @@ -0,0 +1,265 @@ +#include "../client.h" +#include "../corpse.h" +#include "../object.h" +#include "../doors.h" + +void command_list(Client *c, const Seperator *sep) +{ + std::string search_type; + if (strcasecmp(sep->arg[1], "npcs") == 0) { + search_type = "npcs"; + } + + if (strcasecmp(sep->arg[1], "players") == 0) { + search_type = "players"; + } + + if (strcasecmp(sep->arg[1], "corpses") == 0) { + search_type = "corpses"; + } + + if (strcasecmp(sep->arg[1], "doors") == 0) { + search_type = "doors"; + } + + if (strcasecmp(sep->arg[1], "objects") == 0) { + search_type = "objects"; + } + + if (search_type.length() > 0) { + + int entity_count = 0; + int found_count = 0; + + std::string search_string; + + if (sep->arg[2]) { + search_string = sep->arg[2]; + } + + /** + * NPC + */ + if (search_type.find("npcs") != std::string::npos) { + auto &entity_list_search = entity_list.GetMobList(); + + for (auto &itr : entity_list_search) { + Mob *entity = itr.second; + if (!entity->IsNPC()) { + continue; + } + + entity_count++; + + std::string entity_name = entity->GetName(); + + /** + * Filter by name + */ + if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { + continue; + } + + std::string saylink = StringFormat( + "#goto %.0f %0.f %.0f", + entity->GetX(), + entity->GetY(), + entity->GetZ() + (entity->IsBoat() ? 50 : 0)); + + c->Message( + 0, + "| %s | ID %5d | %s | x %.0f | y %0.f | z %.0f", + EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), + entity->GetID(), + entity->GetName(), + entity->GetX(), + entity->GetY(), + entity->GetZ() + ); + + found_count++; + } + } + + /** + * Client + */ + if (search_type.find("players") != std::string::npos) { + auto &entity_list_search = entity_list.GetClientList(); + + for (auto &itr : entity_list_search) { + Client *entity = itr.second; + + entity_count++; + + std::string entity_name = entity->GetName(); + + /** + * Filter by name + */ + if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { + continue; + } + + std::string saylink = StringFormat( + "#goto %.0f %0.f %.0f", + entity->GetX(), + entity->GetY(), + entity->GetZ()); + + c->Message( + 0, + "| %s | ID %5d | %s | x %.0f | y %0.f | z %.0f", + EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), + entity->GetID(), + entity->GetName(), + entity->GetX(), + entity->GetY(), + entity->GetZ() + ); + + found_count++; + } + } + + /** + * Corpse + */ + if (search_type.find("corpses") != std::string::npos) { + auto &entity_list_search = entity_list.GetCorpseList(); + + for (auto &itr : entity_list_search) { + Corpse *entity = itr.second; + + entity_count++; + + std::string entity_name = entity->GetName(); + + /** + * Filter by name + */ + if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { + continue; + } + + std::string saylink = StringFormat( + "#goto %.0f %0.f %.0f", + entity->GetX(), + entity->GetY(), + entity->GetZ()); + + c->Message( + 0, + "| %s | ID %5d | %s | x %.0f | y %0.f | z %.0f", + EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), + entity->GetID(), + entity->GetName(), + entity->GetX(), + entity->GetY(), + entity->GetZ() + ); + + found_count++; + } + } + + /** + * Doors + */ + if (search_type.find("doors") != std::string::npos) { + auto &entity_list_search = entity_list.GetDoorsList(); + + for (auto &itr : entity_list_search) { + Doors *entity = itr.second; + + entity_count++; + + std::string entity_name = entity->GetDoorName(); + + /** + * Filter by name + */ + if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { + continue; + } + + std::string saylink = StringFormat( + "#goto %.0f %0.f %.0f", + entity->GetX(), + entity->GetY(), + entity->GetZ()); + + c->Message( + 0, + "| %s | Entity ID %5d | Door ID %i | %s | x %.0f | y %0.f | z %.0f", + EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), + entity->GetID(), + entity->GetDoorID(), + entity->GetDoorName(), + entity->GetX(), + entity->GetY(), + entity->GetZ() + ); + + found_count++; + } + } + + /** + * Objects + */ + if (search_type.find("objects") != std::string::npos) { + auto &entity_list_search = entity_list.GetObjectList(); + + for (auto &itr : entity_list_search) { + Object *entity = itr.second; + + entity_count++; + + std::string entity_name = entity->GetModelName(); + + /** + * Filter by name + */ + if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { + continue; + } + + std::string saylink = StringFormat( + "#goto %.0f %0.f %.0f", + entity->GetX(), + entity->GetY(), + entity->GetZ()); + + c->Message( + 0, + "| %s | Entity ID %5d | Object DBID %i | %s | x %.0f | y %0.f | z %.0f", + EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), + entity->GetID(), + entity->GetDBID(), + entity->GetModelName(), + entity->GetX(), + entity->GetY(), + entity->GetZ() + ); + + found_count++; + } + } + + if (found_count) { + c->Message( + 0, "Found (%i) of type (%s) in zone (%i) total", + found_count, + search_type.c_str(), + entity_count + ); + } + } + else { + c->Message(Chat::White, "Usage of #list"); + c->Message(Chat::White, "- #list [npcs|players|corpses|doors|objects] [search]"); + c->Message(Chat::White, "- Example: #list npcs (Blank for all)"); + } +} + diff --git a/zone/gm_commands/listpetition.cpp b/zone/gm_commands/listpetition.cpp new file mode 100755 index 000000000..110809deb --- /dev/null +++ b/zone/gm_commands/listpetition.cpp @@ -0,0 +1,22 @@ +#include "../client.h" + +void command_listpetition(Client *c, const Seperator *sep) +{ + std::string query = "SELECT petid, charname, accountname FROM petitions ORDER BY petid"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { + return; + } + + LogInfo("Petition list requested by [{}]", c->GetName()); + + if (results.RowCount() == 0) { + return; + } + + c->Message(Chat::Red, " ID : Character Name , Account Name"); + + for (auto row = results.begin(); row != results.end(); ++row) + c->Message(Chat::Yellow, " %s: %s , %s ", row[0], row[1], row[2]); +} + diff --git a/zone/gm_commands/loc.cpp b/zone/gm_commands/loc.cpp new file mode 100755 index 000000000..79d734711 --- /dev/null +++ b/zone/gm_commands/loc.cpp @@ -0,0 +1,32 @@ +#include "../client.h" + +void command_loc(Client *c, const Seperator *sep) +{ + Mob *target = c; + if (c->GetTarget()) { + target = c->GetTarget(); + } + + auto target_position = target->GetPosition(); + + c->Message( + Chat::White, + fmt::format( + "{} Location | XYZ: {:.2f}, {:.2f}, {:.2f} Heading: {:.2f}", + ( + c == target ? + "Your" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), + target_position.x, + target_position.y, + target_position.z, + target_position.w + ).c_str() + ); +} + diff --git a/zone/gm_commands/lock.cpp b/zone/gm_commands/lock.cpp new file mode 100755 index 000000000..1fda53841 --- /dev/null +++ b/zone/gm_commands/lock.cpp @@ -0,0 +1,15 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_lock(Client *c, const Seperator *sep) +{ + auto outpack = new ServerPacket(ServerOP_Lock, sizeof(ServerLock_Struct)); + ServerLock_Struct *lss = (ServerLock_Struct *) outpack->pBuffer; + strcpy(lss->myname, c->GetName()); + lss->mode = 1; + worldserver.SendPacket(outpack); + safe_delete(outpack); +} + diff --git a/zone/gm_commands/logcommand.cpp b/zone/gm_commands/logcommand.cpp new file mode 100755 index 000000000..3b85e2ff3 --- /dev/null +++ b/zone/gm_commands/logcommand.cpp @@ -0,0 +1,85 @@ +#include "../client.h" + +void command_logcommand(Client *c, const char *message) +{ + int admin = c->Admin(); + + bool continueevents = false; + switch (zone->loglevelvar) { //catch failsafe + case 9: { // log only LeadGM + if ( + admin >= AccountStatus::GMLeadAdmin && + admin < AccountStatus::GMMgmt + ) { + continueevents = true; + } + break; + } + case 8: { // log only GM + if ( + admin >= AccountStatus::GMAdmin && + admin < AccountStatus::GMLeadAdmin + ) { + continueevents = true; + } + break; + } + case 1: { + if (admin >= AccountStatus::GMMgmt) { + continueevents = true; + } + break; + } + case 2: { + if (admin >= AccountStatus::GMLeadAdmin) { + continueevents = true; + } + break; + } + case 3: { + if (admin >= AccountStatus::GMAdmin) { + continueevents = true; + } + break; + } + case 4: { + if (admin >= AccountStatus::QuestTroupe) { + continueevents = true; + } + break; + } + case 5: { + if (admin >= AccountStatus::ApprenticeGuide) { + continueevents = true; + } + break; + } + case 6: { + if (admin >= AccountStatus::Steward) { + continueevents = true; + } + break; + } + case 7: { + continueevents = true; + break; + } + } + + if (continueevents) { + database.logevents( + c->AccountName(), + c->AccountID(), + admin, c->GetName(), + c->GetTarget() ? c->GetTarget()->GetName() : "None", + "Command", + message, + 1 + ); + } +} + + +/* + * commands go below here + */ diff --git a/zone/gm_commands/logs.cpp b/zone/gm_commands/logs.cpp new file mode 100755 index 000000000..16ba3439a --- /dev/null +++ b/zone/gm_commands/logs.cpp @@ -0,0 +1,101 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_logs(Client *c, const Seperator *sep) +{ + int logs_set = 0; + if (sep->argnum > 0) { + /* #logs reload_all */ + if (strcasecmp(sep->arg[1], "reload_all") == 0) { + auto pack = new ServerPacket(ServerOP_ReloadLogs, 0); + worldserver.SendPacket(pack); + c->Message( + Chat::Red, + "Successfully sent the packet to world to reload log settings from the database for all zones" + ); + safe_delete(pack); + } + /* #logs list_settings */ + if (strcasecmp(sep->arg[1], "list_settings") == 0 || + (strcasecmp(sep->arg[1], "set") == 0 && strcasecmp(sep->arg[3], "") == 0)) { + c->Message(Chat::White, "[Category ID | console | file | gmsay | Category Description]"); + int redisplay_columns = 0; + for (int i = 0; i < Logs::LogCategory::MaxCategoryID; i++) { + if (redisplay_columns == 10) { + c->Message(Chat::White, "[Category ID | console | file | gmsay | Category Description]"); + redisplay_columns = 0; + } + c->Message( + 0, + StringFormat( + "--- %i | %u | %u | %u | %s", + i, + LogSys.log_settings[i].log_to_console, + LogSys.log_settings[i].log_to_file, + LogSys.log_settings[i].log_to_gmsay, + Logs::LogCategoryName[i] + ).c_str()); + redisplay_columns++; + } + } + /* #logs set */ + if (strcasecmp(sep->arg[1], "set") == 0) { + if (strcasecmp(sep->arg[2], "console") == 0) { + LogSys.log_settings[atoi(sep->arg[3])].log_to_console = atoi(sep->arg[4]); + logs_set = 1; + } + else if (strcasecmp(sep->arg[2], "file") == 0) { + LogSys.log_settings[atoi(sep->arg[3])].log_to_file = atoi(sep->arg[4]); + logs_set = 1; + } + else if (strcasecmp(sep->arg[2], "gmsay") == 0) { + LogSys.log_settings[atoi(sep->arg[3])].log_to_gmsay = atoi(sep->arg[4]); + logs_set = 1; + } + else { + c->Message( + Chat::White, + "--- #logs set [console|file|gmsay] - Sets log settings during the lifetime of the zone" + ); + c->Message(Chat::White, "--- #logs set gmsay 20 1 - Would output Quest errors to gmsay"); + } + if (logs_set == 1) { + c->Message(Chat::Yellow, "Your Log Settings have been applied"); + c->Message( + Chat::Yellow, + "Output Method: %s :: Debug Level: %i - Category: %s", + sep->arg[2], + atoi(sep->arg[4]), + Logs::LogCategoryName[atoi(sep->arg[3])] + ); + } + /* We use a general 'is_category_enabled' now, let's update when we update any output settings + This is used in hot places of code to check if its enabled in any way before triggering logs + */ + if (atoi(sep->arg[4]) > 0) { + LogSys.log_settings[atoi(sep->arg[3])].is_category_enabled = 1; + } + else { + LogSys.log_settings[atoi(sep->arg[3])].is_category_enabled = 0; + } + } + } + else { + c->Message(Chat::White, "#logs usage:"); + c->Message( + Chat::White, + "--- #logs reload_all - Reload all settings in world and all zone processes with what is defined in the database" + ); + c->Message( + Chat::White, + "--- #logs list_settings - Shows current log settings and categories loaded into the current process' memory" + ); + c->Message( + Chat::White, + "--- #logs set [console|file|gmsay] - Sets log settings during the lifetime of the zone" + ); + } +} + diff --git a/zone/gm_commands/makepet.cpp b/zone/gm_commands/makepet.cpp new file mode 100755 index 000000000..d83411404 --- /dev/null +++ b/zone/gm_commands/makepet.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +void command_makepet(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == '\0') { + c->Message(Chat::White, "Usage: #makepet pet_type_name (will not survive across zones)"); + } + else { + c->MakePet(0, sep->arg[1]); + } +} + diff --git a/zone/gm_commands/mana.cpp b/zone/gm_commands/mana.cpp new file mode 100755 index 000000000..33fe4e388 --- /dev/null +++ b/zone/gm_commands/mana.cpp @@ -0,0 +1,27 @@ +#include "../client.h" + +void command_mana(Client *c, const Seperator *sep) +{ + auto target = c->GetTarget() ? c->GetTarget() : c; + if (target->IsClient()) { + target->CastToClient()->SetMana(target->CastToClient()->CalcMaxMana()); + } + else { + target->SetMana(target->CalcMaxMana()); + } + + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "Set {} ({}) to full Mana.", + target->GetCleanName(), + target->GetID() + ).c_str() + ); + } + else { + c->Message(Chat::White, "Restored your Mana to full."); + } +} + diff --git a/zone/gm_commands/max_all_skills.cpp b/zone/gm_commands/max_all_skills.cpp new file mode 100755 index 000000000..6c63c60ca --- /dev/null +++ b/zone/gm_commands/max_all_skills.cpp @@ -0,0 +1,20 @@ +#include "../client.h" + +void command_max_all_skills(Client *c, const Seperator *sep) +{ + if (c) { + Client *client_target = (c->GetTarget() ? (c->GetTarget()->IsClient() ? c->GetTarget()->CastToClient() : c) + : c); + auto Skills = EQ::skills::GetSkillTypeMap(); + for (auto &skills_iter : Skills) { + auto skill_id = skills_iter.first; + auto current_skill_value = ( + (EQ::skills::IsSpecializedSkill(skill_id)) ? + 50 : + content_db.GetSkillCap(client_target->GetClass(), skill_id, client_target->GetLevel()) + ); + client_target->SetSkill(skill_id, current_skill_value); + } + } +} + diff --git a/zone/gm_commands/memspell.cpp b/zone/gm_commands/memspell.cpp new file mode 100755 index 000000000..5511dae39 --- /dev/null +++ b/zone/gm_commands/memspell.cpp @@ -0,0 +1,22 @@ +#include "../client.h" + +void command_memspell(Client *c, const Seperator *sep) +{ + uint32 slot; + uint16 spell_id; + + if (!(sep->IsNumber(1) && sep->IsNumber(2))) { + c->Message(Chat::White, "Usage: #MemSpell slotid spellid"); + } + else { + slot = atoi(sep->arg[1]) - 1; + spell_id = atoi(sep->arg[2]); + if (slot > EQ::spells::SPELL_GEM_COUNT || spell_id >= SPDAT_RECORDS) { + c->Message(Chat::White, "Error: #MemSpell: Arguement out of range"); + } + else { + c->MemSpell(spell_id, slot); + c->Message(Chat::White, "Spell slot changed, have fun!"); + } + } +} diff --git a/zone/gm_commands/merchantcloseshop.cpp b/zone/gm_commands/merchantcloseshop.cpp new file mode 100755 index 000000000..571d80d46 --- /dev/null +++ b/zone/gm_commands/merchantcloseshop.cpp @@ -0,0 +1,13 @@ +#include "../client.h" + +void command_merchantcloseshop(Client *c, const Seperator *sep) +{ + Mob *merchant = c->GetTarget(); + if (!merchant || merchant->GetClass() != MERCHANT) { + c->Message(Chat::White, "You must target a merchant to close their shop."); + return; + } + + merchant->CastToNPC()->MerchantCloseShop(); +} + diff --git a/zone/gm_commands/merchantopenshop.cpp b/zone/gm_commands/merchantopenshop.cpp new file mode 100755 index 000000000..cd05465ba --- /dev/null +++ b/zone/gm_commands/merchantopenshop.cpp @@ -0,0 +1,13 @@ +#include "../client.h" + +void command_merchantopenshop(Client *c, const Seperator *sep) +{ + Mob *merchant = c->GetTarget(); + if (!merchant || merchant->GetClass() != MERCHANT) { + c->Message(Chat::White, "You must target a merchant to open their shop."); + return; + } + + merchant->CastToNPC()->MerchantOpenShop(); +} + diff --git a/zone/gm_commands/modifynpcstat.cpp b/zone/gm_commands/modifynpcstat.cpp new file mode 100755 index 000000000..5cde6e8f3 --- /dev/null +++ b/zone/gm_commands/modifynpcstat.cpp @@ -0,0 +1,30 @@ +#include "../client.h" + +void command_modifynpcstat(Client *c, const Seperator *sep) +{ + if (!c) { + return; + } + + if (sep->arg[1][0] == '\0') { + c->Message(Chat::White, "usage #modifynpcstat arg value"); + c->Message( + Chat::White, + "Args: ac, str, sta, agi, dex, wis, _int, cha, max_hp, mr, fr, cr, pr, dr, runspeed, special_attacks, " + "attack_speed, atk, accuracy, trackable, min_hit, max_hit, see_invis_undead, see_hide, see_improved_hide, " + "hp_regen, mana_regen, aggro, assist, slow_mitigation, loottable_id, healscale, spellscale" + ); + return; + } + + if (!c->GetTarget()) { + return; + } + + if (!c->GetTarget()->IsNPC()) { + return; + } + + c->GetTarget()->CastToNPC()->ModifyNPCStat(sep->arg[1], sep->arg[2]); +} + diff --git a/zone/gm_commands/motd.cpp b/zone/gm_commands/motd.cpp new file mode 100755 index 000000000..fdfba84b4 --- /dev/null +++ b/zone/gm_commands/motd.cpp @@ -0,0 +1,15 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_motd(Client *c, const Seperator *sep) +{ + auto outpack = new ServerPacket(ServerOP_Motd, sizeof(ServerMotd_Struct)); + ServerMotd_Struct *mss = (ServerMotd_Struct *) outpack->pBuffer; + strn0cpy(mss->myname, c->GetName(), 64); + strn0cpy(mss->motd, sep->argplus[1], 512); + worldserver.SendPacket(outpack); + safe_delete(outpack); +} + diff --git a/zone/gm_commands/movechar.cpp b/zone/gm_commands/movechar.cpp new file mode 100755 index 000000000..1fc3c7f27 --- /dev/null +++ b/zone/gm_commands/movechar.cpp @@ -0,0 +1,32 @@ +#include "../client.h" + +void command_movechar(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0 || sep->arg[2][0] == 0) { + c->Message(Chat::White, "Usage: #movechar [charactername] [zonename]"); + } + else if (c->Admin() < commandMovecharToSpecials && strcasecmp(sep->arg[2], "cshome") == 0 || + strcasecmp(sep->arg[2], "load") == 0 || strcasecmp(sep->arg[2], "load2") == 0) { + c->Message(Chat::White, "Invalid zone name"); + } + else { + uint32 tmp = database.GetAccountIDByChar(sep->arg[1]); + if (tmp) { + if (c->Admin() >= commandMovecharSelfOnly || tmp == c->AccountID()) { + if (!database.MoveCharacterToZone((char *) sep->arg[1], ZoneID(sep->arg[2]))) { + c->Message(Chat::White, "Character Move Failed!"); + } + else { + c->Message(Chat::White, "Character has been moved."); + } + } + else { + c->Message(Chat::Red, "You cannot move characters that are not on your account."); + } + } + else { + c->Message(Chat::White, "Character Does Not Exist"); + } + } +} + diff --git a/zone/gm_commands/movement.cpp b/zone/gm_commands/movement.cpp new file mode 100755 index 000000000..f934f900c --- /dev/null +++ b/zone/gm_commands/movement.cpp @@ -0,0 +1,76 @@ +#include "../client.h" +#include "../mob_movement_manager.h" + +void command_movement(Client *c, const Seperator *sep) +{ + auto &mgr = MobMovementManager::Get(); + + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #movement stats/clearstats/walkto/runto/rotateto/stop/packet"); + return; + } + + if (strcasecmp(sep->arg[1], "stats") == 0) { + mgr.DumpStats(c); + } + else if (strcasecmp(sep->arg[1], "clearstats") == 0) { + mgr.ClearStats(); + } + else if (strcasecmp(sep->arg[1], "walkto") == 0) { + auto target = c->GetTarget(); + if (target == nullptr) { + c->Message(Chat::White, "No target found."); + return; + } + + target->WalkTo(c->GetX(), c->GetY(), c->GetZ()); + } + else if (strcasecmp(sep->arg[1], "runto") == 0) { + auto target = c->GetTarget(); + if (target == nullptr) { + c->Message(Chat::White, "No target found."); + return; + } + + target->RunTo(c->GetX(), c->GetY(), c->GetZ()); + } + else if (strcasecmp(sep->arg[1], "rotateto") == 0) { + auto target = c->GetTarget(); + if (target == nullptr) { + c->Message(Chat::White, "No target found."); + return; + } + + target->RotateToWalking(target->CalculateHeadingToTarget(c->GetX(), c->GetY())); + } + else if (strcasecmp(sep->arg[1], "stop") == 0) { + auto target = c->GetTarget(); + if (target == nullptr) { + c->Message(Chat::White, "No target found."); + return; + } + + target->StopNavigation(); + } + else if (strcasecmp(sep->arg[1], "packet") == 0) { + auto target = c->GetTarget(); + if (target == nullptr) { + c->Message(Chat::White, "No target found."); + return; + } + + mgr.SendCommandToClients( + target, + atof(sep->arg[2]), + atof(sep->arg[3]), + atof(sep->arg[4]), + atof(sep->arg[5]), + atoi(sep->arg[6]), + ClientRangeAny + ); + } + else { + c->Message(Chat::White, "Usage: #movement stats/clearstats/walkto/runto/rotateto/stop/packet"); + } +} + diff --git a/zone/gm_commands/myskills.cpp b/zone/gm_commands/myskills.cpp new file mode 100755 index 000000000..d781f50ca --- /dev/null +++ b/zone/gm_commands/myskills.cpp @@ -0,0 +1,7 @@ +#include "../client.h" + +void command_myskills(Client *c, const Seperator *sep) +{ + c->ShowSkillsWindow(); +} + diff --git a/zone/gm_commands/mysql.cpp b/zone/gm_commands/mysql.cpp new file mode 100755 index 000000000..67fc0f0df --- /dev/null +++ b/zone/gm_commands/mysql.cpp @@ -0,0 +1,87 @@ +#include "../client.h" + +void command_mysql(Client *c, const Seperator *sep) +{ + if (!sep->arg[1][0] || !sep->arg[2][0]) { + c->Message(Chat::White, "Usage: #mysql query \"Query here\""); + return; + } + + if (strcasecmp(sep->arg[1], "help") == 0) { + c->Message(Chat::White, "MYSQL In-Game CLI Interface:"); + c->Message(Chat::White, "Example: #mysql query \"Query goes here quoted\" -s -h"); + c->Message(Chat::White, "To use 'like \"%%something%%\" replace the %% with #"); + c->Message(Chat::White, "Example: #mysql query \"select * from table where name like \"#something#\""); + c->Message(Chat::White, "-s - Spaces select entries apart"); + c->Message(Chat::White, "-h - Colors every other select result"); + return; + } + + if (strcasecmp(sep->arg[1], "query") == 0) { + ///Parse switches here + int argnum = 3; + bool optionS = false; + bool optionH = false; + while (sep->arg[argnum] && strlen(sep->arg[argnum]) > 1) { + switch (sep->arg[argnum][1]) { + case 's': + optionS = true; + break; + case 'h': + optionH = true; + break; + default: + c->Message(Chat::Yellow, "%s, there is no option '%c'", c->GetName(), sep->arg[argnum][1]); + return; + } + ++argnum; + } + + int highlightTextIndex = 0; + std::string query(sep->arg[2]); + //swap # for % so like queries can work + std::replace(query.begin(), query.end(), '#', '%'); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + return; + } + + //Using sep->arg[2] again, replace # with %% so it doesn't screw up when sent through vsnprintf in Message + query = sep->arg[2]; + int pos = query.find('#'); + while (pos != std::string::npos) { + query.erase(pos, 1); + query.insert(pos, "%%"); + pos = query.find('#'); + } + c->Message(Chat::Yellow, "---Running query: '%s'", query.c_str()); + + for (auto row = results.begin(); row != results.end(); ++row) { + std::stringstream lineText; + std::vector lineVec; + for (int i = 0; i < results.RowCount(); i++) { + //split lines that could overflow the buffer in Client::Message and get cut off + //This will crash MQ2 @ 4000 since their internal buffer is only 2048. + //Reducing it to 2000 fixes that but splits more results from tables with a lot of columns. + if (lineText.str().length() > 4000) { + lineVec.push_back(lineText.str()); + lineText.str(""); + } + lineText << results.FieldName(i) << ":" << "[" << (row[i] ? row[i] : "nullptr") << "] "; + } + + lineVec.push_back(lineText.str()); + + if (optionS) { //This provides spacing for the space switch + c->Message(Chat::White, " "); + } + if (optionH) { //This option will highlight every other row + highlightTextIndex = 1 - highlightTextIndex; + } + + for (int lineNum = 0; lineNum < lineVec.size(); ++lineNum) + c->Message(highlightTextIndex, lineVec[lineNum].c_str()); + } + } +} + diff --git a/zone/gm_commands/mystats.cpp b/zone/gm_commands/mystats.cpp new file mode 100755 index 000000000..e5a781d8a --- /dev/null +++ b/zone/gm_commands/mystats.cpp @@ -0,0 +1,17 @@ +#include "../client.h" + +void command_mystats(Client *c, const Seperator *sep) +{ + if (c->GetTarget() && c->GetPet()) { + if (c->GetTarget()->IsPet() && c->GetTarget() == c->GetPet()) { + c->GetTarget()->ShowStats(c); + } + else { + c->ShowStats(c); + } + } + else { + c->ShowStats(c); + } +} + diff --git a/zone/gm_commands/name.cpp b/zone/gm_commands/name.cpp new file mode 100755 index 000000000..fbe60f752 --- /dev/null +++ b/zone/gm_commands/name.cpp @@ -0,0 +1,30 @@ +#include "../client.h" + +void command_name(Client *c, const Seperator *sep) +{ + Client *target; + + if ((strlen(sep->arg[1]) == 0) || (!(c->GetTarget() && c->GetTarget()->IsClient()))) { + c->Message(Chat::White, "Usage: #name newname (requires player target)"); + } + else { + target = c->GetTarget()->CastToClient(); + char *oldname = strdup(target->GetName()); + if (target->ChangeFirstName(sep->arg[1], c->GetName())) { + c->Message(Chat::White, "Successfully renamed %s to %s", oldname, sep->arg[1]); + // until we get the name packet working right this will work + c->Message(Chat::White, "Sending player to char select."); + target->Kick("Name was changed"); + } + else { + c->Message( + Chat::Red, + "ERROR: Unable to rename %s. Check that the new name '%s' isn't already taken.", + oldname, + sep->arg[2] + ); + } + free(oldname); + } +} + diff --git a/zone/gm_commands/netstats.cpp b/zone/gm_commands/netstats.cpp new file mode 100755 index 000000000..6bd56f4fa --- /dev/null +++ b/zone/gm_commands/netstats.cpp @@ -0,0 +1,141 @@ +#include "../client.h" + +void command_netstats(Client *c, const Seperator *sep) +{ + if (c) { + auto client = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + client = c->GetTarget()->CastToClient(); + } + + if (strcasecmp(sep->arg[1], "reset") == 0) { + auto connection = c->Connection(); + c->Message(Chat::White, "Resetting client stats (packet loss will not read correctly after reset)."); + connection->ResetStats(); + return; + } + + auto connection = c->Connection(); + auto opts = connection->GetManager()->GetOptions(); + auto eqs_stats = connection->GetStats(); + auto &stats = eqs_stats.DaybreakStats; + auto now = EQ::Net::Clock::now(); + auto sec_since_stats_reset = std::chrono::duration_cast>( + now - stats.created + ).count(); + + c->Message(Chat::White, "Netstats:"); + c->Message(Chat::White, "--------------------------------------------------------------------"); + c->Message( + Chat::White, + "Sent Bytes: %u (%.2f/sec)", + stats.sent_bytes, + stats.sent_bytes / sec_since_stats_reset + ); + c->Message( + Chat::White, + "Recv Bytes: %u (%.2f/sec)", + stats.recv_bytes, + stats.recv_bytes / sec_since_stats_reset + ); + c->Message( + Chat::White, "Bytes Before Encode (Sent): %u, Compression Rate: %.2f%%", stats.bytes_before_encode, + static_cast(stats.bytes_before_encode - stats.sent_bytes) / + static_cast(stats.bytes_before_encode) * 100.0 + ); + c->Message( + Chat::White, "Bytes After Decode (Recv): %u, Compression Rate: %.2f%%", stats.bytes_after_decode, + static_cast(stats.bytes_after_decode - stats.recv_bytes) / + static_cast(stats.bytes_after_decode) * 100.0 + ); + c->Message(Chat::White, "Min Ping: %u", stats.min_ping); + c->Message(Chat::White, "Max Ping: %u", stats.max_ping); + c->Message(Chat::White, "Last Ping: %u", stats.last_ping); + c->Message(Chat::White, "Average Ping: %u", stats.avg_ping); + c->Message(Chat::White, "--------------------------------------------------------------------"); + c->Message( + Chat::White, + "(Realtime) Recv Packets: %u (%.2f/sec)", + stats.recv_packets, + stats.recv_packets / sec_since_stats_reset + ); + c->Message( + Chat::White, + "(Realtime) Sent Packets: %u (%.2f/sec)", + stats.sent_packets, + stats.sent_packets / sec_since_stats_reset + ); + c->Message(Chat::White, "(Sync) Recv Packets: %u", stats.sync_recv_packets); + c->Message(Chat::White, "(Sync) Sent Packets: %u", stats.sync_sent_packets); + c->Message(Chat::White, "(Sync) Remote Recv Packets: %u", stats.sync_remote_recv_packets); + c->Message(Chat::White, "(Sync) Remote Sent Packets: %u", stats.sync_remote_sent_packets); + c->Message( + Chat::White, + "Packet Loss In: %.2f%%", + 100.0 * (1.0 - static_cast(stats.sync_recv_packets) / + static_cast(stats.sync_remote_sent_packets))); + c->Message( + Chat::White, + "Packet Loss Out: %.2f%%", + 100.0 * (1.0 - static_cast(stats.sync_remote_recv_packets) / + static_cast(stats.sync_sent_packets))); + c->Message(Chat::White, "--------------------------------------------------------------------"); + c->Message( + Chat::White, + "Resent Packets: %u (%.2f/sec)", + stats.resent_packets, + stats.resent_packets / sec_since_stats_reset + ); + c->Message( + Chat::White, + "Resent Fragments: %u (%.2f/sec)", + stats.resent_fragments, + stats.resent_fragments / sec_since_stats_reset + ); + c->Message( + Chat::White, + "Resent Non-Fragments: %u (%.2f/sec)", + stats.resent_full, + stats.resent_full / sec_since_stats_reset + ); + c->Message( + Chat::White, + "Dropped Datarate Packets: %u (%.2f/sec)", + stats.dropped_datarate_packets, + stats.dropped_datarate_packets / sec_since_stats_reset + ); + + if (opts.daybreak_options.outgoing_data_rate > 0.0) { + c->Message( + Chat::White, + "Outgoing Link Saturation %.2f%% (%.2fkb/sec)", + 100.0 * (1.0 - ((opts.daybreak_options.outgoing_data_rate - stats.datarate_remaining) / + opts.daybreak_options.outgoing_data_rate)), + opts.daybreak_options.outgoing_data_rate + ); + } + + if (strcasecmp(sep->arg[1], "full") == 0) { + c->Message(Chat::White, "--------------------------------------------------------------------"); + c->Message(Chat::White, "Sent Packet Types"); + for (auto i = 0; i < _maxEmuOpcode; ++i) { + auto cnt = eqs_stats.SentCount[i]; + if (cnt > 0) { + c->Message(Chat::White, "%s: %u (%.2f / sec)", OpcodeNames[i], cnt, cnt / sec_since_stats_reset); + } + } + + c->Message(Chat::White, "--------------------------------------------------------------------"); + c->Message(Chat::White, "Recv Packet Types"); + for (auto i = 0; i < _maxEmuOpcode; ++i) { + auto cnt = eqs_stats.RecvCount[i]; + if (cnt > 0) { + c->Message(Chat::White, "%s: %u (%.2f / sec)", OpcodeNames[i], cnt, cnt / sec_since_stats_reset); + } + } + } + + c->Message(Chat::White, "--------------------------------------------------------------------"); + } +} + diff --git a/zone/gm_commands/network.cpp b/zone/gm_commands/network.cpp new file mode 100755 index 000000000..1c35d28ec --- /dev/null +++ b/zone/gm_commands/network.cpp @@ -0,0 +1,175 @@ +#include "../client.h" + +void command_network(Client *c, const Seperator *sep) +{ + if (!strcasecmp(sep->arg[1], "getopt")) { + auto eqsi = c->Connection(); + auto manager = eqsi->GetManager(); + auto opts = manager->GetOptions(); + + if (!strcasecmp(sep->arg[2], "all")) { + c->Message(Chat::White, "max_packet_size: %llu", (uint64_t) opts.daybreak_options.max_packet_size); + c->Message( + Chat::White, + "max_connection_count: %llu", + (uint64_t) opts.daybreak_options.max_connection_count + ); + c->Message(Chat::White, "keepalive_delay_ms: %llu", (uint64_t) opts.daybreak_options.keepalive_delay_ms); + c->Message(Chat::White, "resend_delay_factor: %.2f", opts.daybreak_options.resend_delay_factor); + c->Message(Chat::White, "resend_delay_ms: %llu", (uint64_t) opts.daybreak_options.resend_delay_ms); + c->Message(Chat::White, "resend_delay_min: %llu", (uint64_t) opts.daybreak_options.resend_delay_min); + c->Message(Chat::White, "resend_delay_max: %llu", (uint64_t) opts.daybreak_options.resend_delay_max); + c->Message(Chat::White, "connect_delay_ms: %llu", (uint64_t) opts.daybreak_options.connect_delay_ms); + c->Message(Chat::White, "connect_stale_ms: %llu", (uint64_t) opts.daybreak_options.connect_stale_ms); + c->Message(Chat::White, "stale_connection_ms: %llu", (uint64_t) opts.daybreak_options.stale_connection_ms); + c->Message(Chat::White, "crc_length: %llu", (uint64_t) opts.daybreak_options.crc_length); + c->Message(Chat::White, "hold_size: %llu", (uint64_t) opts.daybreak_options.hold_size); + c->Message(Chat::White, "hold_length_ms: %llu", (uint64_t) opts.daybreak_options.hold_length_ms); + c->Message( + Chat::White, + "simulated_in_packet_loss: %llu", + (uint64_t) opts.daybreak_options.simulated_in_packet_loss + ); + c->Message( + Chat::White, + "simulated_out_packet_loss: %llu", + (uint64_t) opts.daybreak_options.simulated_out_packet_loss + ); + c->Message(Chat::White, "tic_rate_hertz: %.2f", opts.daybreak_options.tic_rate_hertz); + c->Message(Chat::White, "resend_timeout: %llu", (uint64_t) opts.daybreak_options.resend_timeout); + c->Message( + Chat::White, + "connection_close_time: %llu", + (uint64_t) opts.daybreak_options.connection_close_time + ); + c->Message(Chat::White, "encode_passes[0]: %llu", (uint64_t) opts.daybreak_options.encode_passes[0]); + c->Message(Chat::White, "encode_passes[1]: %llu", (uint64_t) opts.daybreak_options.encode_passes[1]); + c->Message(Chat::White, "port: %llu", (uint64_t) opts.daybreak_options.port); + } + else { + c->Message(Chat::White, "Unknown get option: %s", sep->arg[2]); + c->Message(Chat::White, "Available options:"); + //Todo the rest of these when im less lazy. + //c->Message(Chat::White, "max_packet_size"); + //c->Message(Chat::White, "max_connection_count"); + //c->Message(Chat::White, "keepalive_delay_ms"); + //c->Message(Chat::White, "resend_delay_factor"); + //c->Message(Chat::White, "resend_delay_ms"); + //c->Message(Chat::White, "resend_delay_min"); + //c->Message(Chat::White, "resend_delay_max"); + //c->Message(Chat::White, "connect_delay_ms"); + //c->Message(Chat::White, "connect_stale_ms"); + //c->Message(Chat::White, "stale_connection_ms"); + //c->Message(Chat::White, "crc_length"); + //c->Message(Chat::White, "hold_size"); + //c->Message(Chat::White, "hold_length_ms"); + //c->Message(Chat::White, "simulated_in_packet_loss"); + //c->Message(Chat::White, "simulated_out_packet_loss"); + //c->Message(Chat::White, "tic_rate_hertz"); + //c->Message(Chat::White, "resend_timeout"); + //c->Message(Chat::White, "connection_close_time"); + //c->Message(Chat::White, "encode_passes[0]"); + //c->Message(Chat::White, "encode_passes[1]"); + //c->Message(Chat::White, "port"); + c->Message(Chat::White, "all"); + } + } + else if (!strcasecmp(sep->arg[1], "setopt")) { + auto eqsi = c->Connection(); + auto manager = eqsi->GetManager(); + auto opts = manager->GetOptions(); + + if (!strcasecmp(sep->arg[3], "")) { + c->Message(Chat::White, "Missing value for set"); + return; + } + + std::string value = sep->arg[3]; + if (!strcasecmp(sep->arg[2], "max_connection_count")) { + opts.daybreak_options.max_connection_count = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "keepalive_delay_ms")) { + opts.daybreak_options.keepalive_delay_ms = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "resend_delay_factor")) { + opts.daybreak_options.resend_delay_factor = std::stod(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "resend_delay_ms")) { + opts.daybreak_options.resend_delay_ms = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "resend_delay_min")) { + opts.daybreak_options.resend_delay_min = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "resend_delay_max")) { + opts.daybreak_options.resend_delay_max = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "connect_delay_ms")) { + opts.daybreak_options.connect_delay_ms = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "connect_stale_ms")) { + opts.daybreak_options.connect_stale_ms = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "stale_connection_ms")) { + opts.daybreak_options.stale_connection_ms = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "hold_size")) { + opts.daybreak_options.hold_size = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "hold_length_ms")) { + opts.daybreak_options.hold_length_ms = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "simulated_in_packet_loss")) { + opts.daybreak_options.simulated_in_packet_loss = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "simulated_out_packet_loss")) { + opts.daybreak_options.simulated_out_packet_loss = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "resend_timeout")) { + opts.daybreak_options.resend_timeout = std::stoull(value); + manager->SetOptions(opts); + } + else if (!strcasecmp(sep->arg[2], "connection_close_time")) { + opts.daybreak_options.connection_close_time = std::stoull(value); + manager->SetOptions(opts); + } + else { + c->Message(Chat::White, "Unknown set option: %s", sep->arg[2]); + c->Message(Chat::White, "Available options:"); + c->Message(Chat::White, "max_connection_count"); + c->Message(Chat::White, "keepalive_delay_ms"); + c->Message(Chat::White, "resend_delay_factor"); + c->Message(Chat::White, "resend_delay_ms"); + c->Message(Chat::White, "resend_delay_min"); + c->Message(Chat::White, "resend_delay_max"); + c->Message(Chat::White, "connect_delay_ms"); + c->Message(Chat::White, "connect_stale_ms"); + c->Message(Chat::White, "stale_connection_ms"); + c->Message(Chat::White, "hold_size"); + c->Message(Chat::White, "hold_length_ms"); + c->Message(Chat::White, "simulated_in_packet_loss"); + c->Message(Chat::White, "simulated_out_packet_loss"); + c->Message(Chat::White, "resend_timeout"); + c->Message(Chat::White, "connection_close_time"); + } + } + else { + c->Message(Chat::White, "Unknown command: %s", sep->arg[1]); + c->Message(Chat::White, "Network commands avail:"); + c->Message(Chat::White, "getopt optname - Retrieve the current option value set."); + c->Message(Chat::White, "setopt optname - Set the current option allowed."); + } +} + diff --git a/zone/gm_commands/npccast.cpp b/zone/gm_commands/npccast.cpp new file mode 100755 index 000000000..4abef226f --- /dev/null +++ b/zone/gm_commands/npccast.cpp @@ -0,0 +1,94 @@ +#include "../client.h" + +void command_npccast(Client *c, const Seperator *sep) +{ + if (c->GetTarget() && c->GetTarget()->IsNPC()) { + NPC *target = c->GetTarget()->CastToNPC(); + if (!sep->IsNumber(1) && sep->arg[1] && sep->IsNumber(2)) { + const char *entity_name = sep->arg[1] ? sep->arg[1] : 0; + auto spell_id = sep->arg[2] ? std::stoul(sep->arg[2]) : 0; + Mob *spell_target = entity_list.GetMob(entity_name); + if (spell_target && IsValidSpell(spell_id) && spell_id < SPDAT_RECORDS) { + c->Message( + Chat::White, + fmt::format( + "{} ({}) casting {} ({}) on {} ({}).", + target->GetCleanName(), + target->GetID(), + GetSpellName(static_cast(spell_id)), + spell_id, + spell_target->GetCleanName(), + spell_target->GetID() + ).c_str() + ); + + target->CastSpell(spell_id, spell_target->GetID()); + } + else { + if (!spell_target) { + c->Message( + Chat::White, + fmt::format( + "Entity {} was not found", + entity_name + ).c_str() + ); + } + else if (!spell_id || !IsValidSpell(spell_id)) { + c->Message( + Chat::White, + fmt::format( + "Spell ID {} was not found", + spell_id + ).c_str() + ); + } + } + } + else if (sep->IsNumber(1) && sep->IsNumber(2)) { + uint16 entity_id = sep->arg[1] ? std::stoul(sep->arg[1]) : 0; + auto spell_id = sep->arg[2] ? std::stoul(sep->arg[2]) : 0; + Mob *spell_target = entity_list.GetMob(entity_id); + if (spell_target && IsValidSpell(spell_id) && spell_id < SPDAT_RECORDS) { + c->Message( + Chat::White, + fmt::format( + "{} ({}) casting {} ({}) on {} ({}).", + target->GetCleanName(), + target->GetID(), + GetSpellName(static_cast(spell_id)), + spell_id, + spell_target->GetCleanName(), + spell_target->GetID() + ).c_str() + ); + + target->CastSpell(spell_id, spell_target->GetID()); + } + else { + if (!spell_target) { + c->Message( + Chat::White, + fmt::format( + "Entity ID {} was not found", + entity_id + ).c_str() + ); + } + else if (!spell_id || !IsValidSpell(spell_id)) { + c->Message( + Chat::White, + fmt::format( + "Spell ID {} was not found", + spell_id + ).c_str() + ); + } + } + } + } + else { + c->Message(Chat::White, "You must target an NPC to use this command."); + } +} + diff --git a/zone/gm_commands/npcedit.cpp b/zone/gm_commands/npcedit.cpp new file mode 100755 index 000000000..332b9efa0 --- /dev/null +++ b/zone/gm_commands/npcedit.cpp @@ -0,0 +1,1408 @@ +#include "../client.h" +#include "../groups.h" +#include "../mob_movement_manager.h" +#include "../raids.h" +#include "../raids.h" + +void command_npcedit(Client *c, const Seperator *sep) +{ + if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "Error: Must have NPC targeted"); + return; + } + + if (strcasecmp(sep->arg[1], "help") == 0) { + + c->Message(Chat::White, "Help File for #npcedit. Syntax for commands are:"); + c->Message(Chat::White, "#npcedit name - Sets an NPC's Name"); + c->Message(Chat::White, "#npcedit lastname - Sets an NPC's Lastname"); + c->Message(Chat::White, "#npcedit level - Sets an NPC's Level"); + c->Message(Chat::White, "#npcedit race - Sets an NPC's Race"); + c->Message(Chat::White, "#npcedit class - Sets an NPC's Class"); + c->Message(Chat::White, "#npcedit bodytype - Sets an NPC's Bodytype"); + c->Message(Chat::White, "#npcedit hp - Sets an NPC's Hitpoints"); + c->Message(Chat::White, "#npcedit mana - Sets an NPC's Mana"); + c->Message(Chat::White, "#npcedit gender - Sets an NPC's Gender"); + c->Message(Chat::White, "#npcedit texture - Sets an NPC's Texture"); + c->Message(Chat::White, "#npcedit helmtexture - Sets an NPC's Helmet Texture"); + c->Message(Chat::White, "#npcedit herosforgemodel - Sets an NPC's Hero's Forge Model"); + c->Message(Chat::White, "#npcedit size - Sets an NPC's Size"); + c->Message(Chat::White, "#npcedit hpregen - Sets an NPC's Hitpoints Regeneration Rate Per Tick"); + c->Message(Chat::White, "#npcedit manaregen - Sets an NPC's Mana Regeneration Rate Per Tick"); + c->Message(Chat::White, "#npcedit loottable - Sets an NPC's Loottable ID"); + c->Message(Chat::White, "#npcedit merchantid - Sets an NPC's Merchant ID"); + c->Message(Chat::White, "#npcedit alt_currency_id - Sets an NPC's Alternate Currency ID"); + c->Message(Chat::White, "#npcedit spell - Sets an NPC's Spells List ID"); + c->Message(Chat::White, "#npcedit npc_spells_effects_id - Sets an NPC's Spell Effects ID"); + c->Message(Chat::White, "#npcedit faction - Sets an NPC's Faction ID"); + c->Message(Chat::White, "#npcedit adventure_template_id - Sets an NPC's Adventure Template ID"); + c->Message(Chat::White, "#npcedit trap_template - Sets an NPC's Trap Template ID"); + c->Message(Chat::White, "#npcedit damage [minimum] [maximum] - Sets an NPC's Damage"); + c->Message(Chat::White, "#npcedit attackcount - Sets an NPC's Attack Count"); + c->Message(Chat::White, "#npcedit special_attacks - Sets an NPC's Special Attacks"); + c->Message(Chat::White, "#npcedit special_abilities - Sets an NPC's Special Abilities"); + c->Message(Chat::White, "#npcedit aggroradius - Sets an NPC's Aggro Radius"); + c->Message(Chat::White, "#npcedit assistradius - Sets an NPC's Assist Radius"); + c->Message(Chat::White, "#npcedit featuresave - Saves an NPC's current facial features to the database"); + c->Message(Chat::White, "#npcedit armortint_id - Sets an NPC's Armor Tint ID"); + c->Message(Chat::White, "#npcedit color [red] [green] [blue] - Sets an NPC's Red, Green, and Blue armor tint"); + c->Message(Chat::White, "#npcedit ammoidfile - Sets an NPC's Ammo ID File"); + c->Message( + Chat::White, + "#npcedit weapon [primary_model] [secondary_model] - Sets an NPC's Primary and Secondary Weapon Model" + ); + c->Message(Chat::White, "#npcedit meleetype [primary_type] [secondary_type] - Sets an NPC's Melee Types"); + c->Message(Chat::White, "#npcedit rangedtype - Sets an NPC's Ranged Type"); + c->Message(Chat::White, "#npcedit runspeed - Sets an NPC's Run Speed"); + c->Message(Chat::White, "#npcedit mr - Sets an NPC's Magic Resistance"); + c->Message(Chat::White, "#npcedit pr - Sets an NPC's Poison Resistance"); + c->Message(Chat::White, "#npcedit dr - Sets an NPC's Disease Resistance"); + c->Message(Chat::White, "#npcedit fr - Sets an NPC's Fire Resistance"); + c->Message(Chat::White, "#npcedit cr - Sets an NPC's Cold Resistance"); + c->Message(Chat::White, "#npcedit corrup - Sets an NPC's Corruption Resistance"); + c->Message(Chat::White, "#npcedit phr - Sets and NPC's Physical Resistance"); + c->Message( + Chat::White, + "#npcedit seeinvis - Sets an NPC's See Invisible Flag [0 = Cannot See Invisible, 1 = Can See Invisible]" + ); + c->Message( + Chat::White, + "#npcedit seeinvisundead - Sets an NPC's See Invisible vs. Undead Flag [0 = Cannot See Invisible vs. Undead, 1 = Can See Invisible vs. Undead]" + ); + c->Message( + Chat::White, + "#npcedit qglobal - Sets an NPC's Quest Global Flag [0 = Quest Globals Off, 1 = Quest Globals On]" + ); + c->Message(Chat::White, "#npcedit ac - Sets an NPC's Armor Class"); + c->Message( + Chat::White, + "#npcedit npcaggro - Sets an NPC's NPC Aggro Flag [0 = Aggro NPCs Off, 1 = Aggro NPCs On]" + ); + c->Message(Chat::White, "#npcedit spawn_limit - Sets an NPC's Spawn Limit Counter"); + c->Message(Chat::White, "#npcedit attackspeed - Sets an NPC's Attack Speed Modifier"); + c->Message(Chat::White, "#npcedit attackdelay - Sets an NPC's Attack Delay"); + c->Message(Chat::White, "#npcedit findable - Sets an NPC's Findable Flag [0 = Not Findable, 1 = Findable]"); + c->Message(Chat::White, "#npcedit str - Sets an NPC's Strength"); + c->Message(Chat::White, "#npcedit sta - Sets an NPC's Stamina"); + c->Message(Chat::White, "#npcedit dex - Sets an NPC's Dexterity"); + c->Message(Chat::White, "#npcedit agi - Sets an NPC's Agility"); + c->Message(Chat::White, "#npcedit int - Sets an NPC's Intelligence"); + c->Message(Chat::White, "#npcedit wis - Sets an NPC's Wisdom"); + c->Message(Chat::White, "#npcedit cha - Sets an NPC's Charisma"); + c->Message( + Chat::White, + "#npcedit seehide - Sets an NPC's See Hide Flag [0 = Cannot See Hide, 1 = Can See Hide]" + ); + c->Message( + Chat::White, + "#npcedit seeimprovedhide - Sets an NPC's See Improved Hide Flag [0 = Cannot See Improved Hide, 1 = Can See Improved Hide]" + ); + c->Message(Chat::White, "#npcedit trackable - Sets an NPC's Trackable Flag [0 = Not Trackable, 1 = Trackable]"); + c->Message(Chat::White, "#npcedit atk - Sets an NPC's Attack"); + c->Message(Chat::White, "#npcedit accuracy - Sets an NPC's Accuracy"); + c->Message(Chat::White, "#npcedit avoidance - Sets an NPC's Avoidance"); + c->Message(Chat::White, "#npcedit slow_mitigation - Sets an NPC's Slow Mitigation"); + c->Message(Chat::White, "#npcedit version - Sets an NPC's Version"); + c->Message(Chat::White, "#npcedit maxlevel - Sets an NPC's Maximum Level"); + c->Message(Chat::White, "#npcedit scalerate - Sets an NPC's Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]"); + c->Message( + Chat::White, + "#npcedit spellscale - Sets an NPC's Spell Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]" + ); + c->Message( + Chat::White, + "#npcedit healscale - Sets an NPC's Heal Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]" + ); + c->Message( + Chat::White, + "#npcedit no_target - Sets an NPC's No Target Hotkey Flag [0 = Not Targetable with Target Hotkey, 1 = Targetable with Target Hotkey]" + ); + c->Message( + Chat::White, + "#npcedit raidtarget - Sets an NPC's Raid Target Flag [0 = Not a Raid Target, 1 = Raid Target]" + ); + c->Message(Chat::White, "#npcedit armtexture - Sets an NPC's Arm Texture"); + c->Message(Chat::White, "#npcedit bracertexture - Sets an NPC's Bracer Texture"); + c->Message(Chat::White, "#npcedit handtexture - Sets an NPC's Hand Texture"); + c->Message(Chat::White, "#npcedit legtexture - Sets an NPC's Leg Texture"); + c->Message(Chat::White, "#npcedit feettexture - Sets an NPC's Feet Texture"); + c->Message(Chat::White, "#npcedit walkspeed - Sets an NPC's Walk Speed"); + c->Message(Chat::White, "#npcedit show_name - Sets an NPC's Show Name Flag [0 = Hidden, 1 = Shown]"); + c->Message( + Chat::White, + "#npcedit untargetable - Sets an NPC's Untargetable Flag [0 = Targetable, 1 = Untargetable]" + ); + c->Message(Chat::White, "#npcedit charm_ac - Sets an NPC's Armor Class while Charmed"); + c->Message(Chat::White, "#npcedit charm_min_dmg - Sets an NPC's Minimum Damage while Charmed"); + c->Message(Chat::White, "#npcedit charm_max_dmg - Sets an NPC's Max Damage while Charmed"); + c->Message(Chat::White, "#npcedit charm_attack_delay - Sets an NPC's Attack Delay while Charmed"); + c->Message(Chat::White, "#npcedit charm_accuracy_rating - Sets an NPC's Accuracy Rating while Charmed"); + c->Message(Chat::White, "#npcedit charm_avoidance_rating - Sets an NPC's Avoidance Rating while Charmed"); + c->Message(Chat::White, "#npcedit charm_atk - Sets an NPC's Attack while Charmed"); + c->Message( + Chat::White, + "#npcedit skip_global_loot - Sets an NPC's Skip Global Loot Flag [0 = Don't Skip, 1 = Skip" + ); + c->Message( + Chat::White, + "#npcedit rarespawn - Sets an NPC's Rare Spawn Flag [0 = Not a Rare Spawn, 1 = Rare Spawn]" + ); + c->Message( + Chat::White, + "#npcedit stuck_behavior - Sets an NPC's Stuck Behavior [0 = Run to Target, 1 = Warp to Target, 2 = Take No Action, 3 = Evade Combat]" + ); + c->Message( + Chat::White, + "#npcedit flymode - Sets an NPC's flymode [0 = Ground, 1 = Flying, 2 = Levitating, 3 = Water, 4 = Floating, 5 = Levitating While Running]" + ); + c->Message( + Chat::White, + "#npcedit always_aggro - Sets an NPC's Always Aggro Flag [0 = Does not Always Aggro, 1 = Always Aggro]" + ); + c->Message( + Chat::White, + "#npcedit exp_mod - Sets an NPC's Experience Modifier [50 = 50%, 100 = 100%, 200 = 200%]" + ); + c->Message(Chat::White, "#npcedit setanimation - Sets an NPC's Animation on Spawn (Stored in spawn2 table)"); + c->Message( + Chat::White, + "#npcedit respawntime - Sets an NPC's Respawn Timer in Seconds (Stored in spawn2 table)" + ); + } + + uint32 npc_id = c->GetTarget()->CastToNPC()->GetNPCTypeID(); + if (strcasecmp(sep->arg[1], "name") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has the name '{}'.", npc_id, sep->arg[2]).c_str()); + std::string query = fmt::format("UPDATE npc_types SET name = '{}' WHERE id = {}", sep->arg[2], npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "lastname") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has the lastname '{}'.", npc_id, sep->arg[2]).c_str()); + std::string query = fmt::format("UPDATE npc_types SET lastname = '{}' WHERE id = {}", sep->arg[2], npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "level") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now level {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET level = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "race") == 0) { + auto race_id = atoi(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now a {} ({}).", npc_id, GetRaceIDName(race_id), race_id).c_str()); + std::string query = fmt::format("UPDATE npc_types SET race = {} WHERE id = {}", race_id, npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "class") == 0) { + auto class_id = atoi(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now a {} ({}).", npc_id, GetClassIDName(class_id), class_id).c_str()); + std::string query = fmt::format("UPDATE npc_types SET class = {} WHERE id = {}", class_id, npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "bodytype") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Bodytype {} .", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET bodytype = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "hp") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Health.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET hp = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "mana") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Mana.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET mana = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "gender") == 0) { + auto gender_id = atoi(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now a {} ({}).", npc_id, gender_id, GetGenderName(gender_id)).c_str()); + std::string query = fmt::format("UPDATE npc_types SET gender = {} WHERE id = {}", gender_id, npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "texture") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET texture = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "helmtexture") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Helmet Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET helmtexture = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "herosforgemodel") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Hero's Forge Model {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET herosforgemodel = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "size") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now Size {:.2f}.", npc_id, atof(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET size = {:.2f} WHERE id = {}", atof(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "hpregen") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now regenerates {} Health per Tick.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET hp_regen_rate = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "manaregen") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now regenerates {} Mana per Tick.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET mana_regen_rate = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "loottable") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using loottable ID {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET loottable_id = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "merchantid") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using merchant ID {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET merchant_id = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "alt_currency_id") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Alternate Currency ID {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET alt_currency_id = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "spell") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Spell List ID {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET npc_spells_id = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "npc_spells_effects_id") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using NPC Spells Effects ID {}.", npc_id, sep->arg[2]).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET npc_spells_effects_id = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "faction") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Faction ID {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET npc_faction_id = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "adventure_template_id") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Adventure Template ID {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET adventure_template_id = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "trap_template") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Trap Template ID {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET trap_template = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "damage") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} now hits from {} to {} damage.", + npc_id, + atoi(sep->arg[2]), + atoi(sep->arg[3])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET mindmg = {}, maxdmg = {} WHERE id = {}", + atoi(sep->arg[2]), + atoi(sep->arg[3]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "attackcount") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has an Attack Count of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET attack_count = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "special_attacks") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} is now using the following Special Attacks '{}'.", + npc_id, + sep->arg[2] + ).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET npcspecialattks = '{}' WHERE id = {}", + sep->arg[2], + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "special_abilities") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} is now using the following Special Abilities '{}'.", + npc_id, + sep->arg[2] + ).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET special_abilities = '{}' WHERE id = {}", + sep->arg[2], + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "aggroradius") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has an Aggro Radius of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET aggroradius = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "assistradius") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has an Assist Radius of {}", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET assistradius = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "featuresave") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} saved with all current facial feature settings.", npc_id).c_str()); + Mob *target = c->GetTarget(); + std::string query = fmt::format( + "UPDATE npc_types " + "SET luclin_haircolor = {}, luclin_beardcolor = {}, " + "luclin_hairstyle = {}, luclin_beard = {}, " + "face = {}, drakkin_heritage = {}, " + "drakkin_tattoo = {}, drakkin_details = {} " + "WHERE id = {}", + target->GetHairColor(), target->GetBeardColor(), + target->GetHairStyle(), target->GetBeard(), + target->GetLuclinFace(), target->GetDrakkinHeritage(), + target->GetDrakkinTattoo(), target->GetDrakkinDetails(), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "armortint_id") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Armor Tint ID {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET armortint_id = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "color") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} now has {} Red, {} Green, and {} Blue tinting on their armor.", + npc_id, + atoi(sep->arg[2]), + atoi(sep->arg[3]), + atoi(sep->arg[4])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET armortint_red = {}, armortint_green = {}, armortint_blue = {} WHERE id = {}", + atoi(sep->arg[2]), + atoi(sep->arg[3]), + atoi(sep->arg[4]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "ammoidfile") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Ammo ID File {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET ammo_idfile = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "weapon") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} will have Model {} set to their Primary and Model {} set to their Secondary on repop.", + npc_id, + atoi(sep->arg[2]), + atoi(sep->arg[3])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET d_melee_texture1 = {}, d_melee_texture2 = {} WHERE id = {}", + atoi(sep->arg[2]), + atoi(sep->arg[3]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "meleetype") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} now has a Primary Melee Type of {} and a Secondary Melee Type of {}.", + npc_id, + atoi(sep->arg[2]), + atoi(sep->arg[3])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET prim_melee_type = {}, sec_melee_type = {} WHERE id = {}", + atoi(sep->arg[2]), + atoi(sep->arg[3]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "rangedtype") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Ranged Type of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET ranged_type = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "runspeed") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now runs at {:.2f}.", npc_id, atof(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET runspeed = {:.2f} WHERE id = {}", + atof(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "mr") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Magic Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET MR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "pr") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Poison Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET PR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "dr") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Disease Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET DR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "fr") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Fire Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET FR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "cr") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Cold Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET CR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "corrup") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Corruption Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET corrup = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "phr") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Physical Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET PhR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "seeinvis") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} can {} See Invisible.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET see_invis = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "seeinvisundead") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} can {} See Invisible vs. Undead.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET see_invis_undead = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "qglobal") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} can {} use Quest Globals.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET qglobal = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "ac") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Armor Class.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET ac = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "npcaggro") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} will {} aggro other NPCs that have a hostile faction.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET npc_aggro = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "spawn_limit") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Spawn Limit of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET spawn_limit = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "attackspeed") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has an Attack Speed of {:.2f}.", npc_id, atof(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET attack_speed = {:.2f} WHERE id = {}", + atof(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "attackdelay") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has an Attack Delay of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET attack_delay = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "findable") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} is {} Findable.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET findable = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "str") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Strength.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET STR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "sta") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Stamina.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET STA = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "agi") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Agility.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET AGI = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "dex") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Dexterity.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET DEX = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "int") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Intelligence.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET _INT = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "wis") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Magic Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET WIS = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "cha") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Charisma.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET CHA = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "seehide") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} can {} See Hide.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET see_hide = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "seeimprovedhide") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} can {} See Improved Hide.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET see_improved_hide = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "trackable") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} is {} Trackable.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET trackable = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "atk") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Attack.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET atk = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "accuracy") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Accuracy.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET accuracy = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "avoidance") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Avoidance.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET avoidance = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "slow_mitigation") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has {} Slow Mitigation.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET slow_mitigation = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "version") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Version {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET version = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "maxlevel") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Maximum Level of {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET maxlevel = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "scalerate") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Scaling Rate of {}%%.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET scalerate = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "spellscale") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Spell Scaling Rate of {}%%.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET spellscale = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "healscale") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has a Heal Scaling Rate of {}%%.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET healscale = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "no_target") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} is {} Targetable with Target Hotkey.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET no_target_hotkey = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "raidtarget") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} is {} designated as a Raid Target.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET raid_target = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "armtexture") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Arm Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET armtexture = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "bracertexture") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Bracer Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET bracertexture = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "handtexture") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Hand Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET handtexture = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "legtexture") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Leg Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET legtexture = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "feettexture") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Feet Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET feettexture = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "walkspeed") == 0) { + c->Message(Chat::Yellow, fmt::format("NPC ID {} now walks at {:.2f}.", npc_id, atof(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET walkspeed = {:.2f} WHERE id = {}", + atof(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "show_name") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} will {} show their name.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format("UPDATE npc_types SET show_name = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "untargetable") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} will {} be untargetable.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET untargetable = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "charm_ac") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has {} Armor Class while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET charm_ac = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "charm_min_dmg") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} now does {} Minimum Damage while Charmed.", + npc_id, + atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET charm_min_dmg = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "charm_max_dmg") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} now does {} Maximum Damage while Charmed.", + npc_id, + atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET charm_max_dmg = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "charm_attack_delay") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has {} Attack Delay while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET charm_attack_delay = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "charm_accuracy_rating") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} now has {} Accuracy Rating while Charmed.", + npc_id, + atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET charm_accuracy_rating = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "charm_avoidance_rating") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} now has {} Avoidance Rating while Charmed.", + npc_id, + atoi(sep->arg[2])).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET charm_avoidance_rating = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "charm_atk") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has {} Attack while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET charm_atk = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "skip_global_loot") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} will {} skip Global Loot.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET skip_global_loot = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "rarespawn") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} is {} designated as a Rare Spawn.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET rare_spawn = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "stuck_behavior") == 0) { + auto behavior_id = atoi(sep->arg[2]); + std::string behavior_name = "Unknown"; + if (behavior_id == MobStuckBehavior::RunToTarget) { + behavior_name = "Run To Target"; + } + else if (behavior_id == MobStuckBehavior::WarpToTarget) { + behavior_name = "Warp To Target"; + } + else if (behavior_id == MobStuckBehavior::TakeNoAction) { + behavior_name = "Take No Action"; + } + else if (behavior_id == MobStuckBehavior::EvadeCombat) { + behavior_name = "Evade Combat"; + } + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} is now using Stuck Behavior {} ({}).", + npc_id, + behavior_name, + behavior_id + ).c_str()); + std::string query = fmt::format("UPDATE npc_types SET stuck_behavior = {} WHERE id = {}", behavior_id, npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "flymode") == 0) { + auto flymode_id = atoi(sep->arg[2]); + std::string flymode_name = "Unknown"; + if (flymode_id == GravityBehavior::Ground) { + flymode_name = "Ground"; + } + else if (flymode_id == GravityBehavior::Flying) { + flymode_name = "Flying"; + } + else if (flymode_id == GravityBehavior::Levitating) { + flymode_name = "Levitating"; + } + else if (flymode_id == GravityBehavior::Water) { + flymode_name = "Water"; + } + else if (flymode_id == GravityBehavior::Floating) { + flymode_name = "Floating"; + } + else if (flymode_id == GravityBehavior::LevitateWhileRunning) { + flymode_name = "Levitating While Running"; + } + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} is now using Fly Mode {} ({}).", npc_id, flymode_name, flymode_id).c_str()); + std::string query = fmt::format("UPDATE npc_types SET flymode = {} WHERE id = {}", flymode_id, npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "always_aggro") == 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} will {} Always Aggro.", + npc_id, + (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); + std::string query = fmt::format( + "UPDATE npc_types SET always_aggro = {} WHERE id = {}", + atoi(sep->arg[2]), + npc_id + ); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "exp_mod") == 0) { + c->Message( + Chat::Yellow, + fmt::format("NPC ID {} now has an Experience Modifier of {}%%.", npc_id, atoi(sep->arg[2])).c_str()); + std::string query = fmt::format("UPDATE npc_types SET exp_mod = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + return; + } + + if (strcasecmp(sep->arg[1], "setanimation") == 0) { + int animation = 0; + std::string animation_name = "Unknown"; + if (sep->arg[2] && atoi(sep->arg[2]) <= 4) { + if (strcasecmp(sep->arg[2], "stand") == 0 || atoi(sep->arg[2]) == 0) { // Stand + animation = 0; + animation_name = "Standing"; + } + else if (strcasecmp(sep->arg[2], "sit") == 0 || atoi(sep->arg[2]) == 1) { // Sit + animation = 1; + animation_name = "Sitting"; + } + else if (strcasecmp(sep->arg[2], "crouch") == 0 || atoi(sep->arg[2]) == 2) { // Crouch + animation = 2; + animation_name = "Crouching"; + } + else if (strcasecmp(sep->arg[2], "dead") == 0 || atoi(sep->arg[2]) == 3) { // Dead + animation = 3; + animation_name = "Dead"; + } + else if (strcasecmp(sep->arg[2], "loot") == 0 || atoi(sep->arg[2]) == 4) { // Looting Animation + animation = 4; + animation_name = "Looting"; + } + } + else { + c->Message( + Chat::White, + "You must specify an Animation (0 = Stand, 1 = Sit, 2 = Crouch, 3 = Dead, 4 = Loot)" + ); + c->Message(Chat::White, "Example: #npcedit setanimation sit"); + c->Message(Chat::White, "Example: #npcedit setanimation 0"); + return; + } + + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} now has their Spawn Animation set to {} ({}) on Spawn Group ID {}.", + npc_id, + animation_name, + animation, + c->GetTarget()->CastToNPC()->GetSpawnGroupId() + ).c_str() + ); + std::string query = fmt::format( + "UPDATE spawn2 SET animation = {} WHERE spawngroupID = {}", + animation, + c->GetTarget()->CastToNPC()->GetSpawnGroupId() + ); + content_db.QueryDatabase(query); + + c->GetTarget()->SetAppearance(EmuAppearance(animation)); + return; + } + + if (strcasecmp(sep->arg[1], "respawntime") == 0) { + if (sep->arg[2][0] && sep->IsNumber(sep->arg[2]) && atoi(sep->arg[2]) > 0) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC ID {} now has a Respawn Timer of {} Seconds on Spawn Group ID {}.", + npc_id, + atoi(sep->arg[2]), + c->GetTarget()->CastToNPC()->GetSpawnGroupId()).c_str()); + std::string query = fmt::format( + "UPDATE spawn2 SET respawntime = {} WHERE spawngroupID = {} AND version = {}", + atoi(sep->arg[2]), + c->GetTarget()->CastToNPC()->GetSpawnGroupId(), + zone->GetInstanceVersion()); + content_db.QueryDatabase(query); + return; + } + } + + if ((sep->arg[1][0] == 0 || strcasecmp(sep->arg[1], "*") == 0) || + ((c->GetTarget() == 0) || (c->GetTarget()->IsClient()))) { + c->Message(Chat::White, "Type #npcedit help for more info"); + } + +} + diff --git a/zone/gm_commands/npceditmass.cpp b/zone/gm_commands/npceditmass.cpp new file mode 100755 index 000000000..8dcb2c338 --- /dev/null +++ b/zone/gm_commands/npceditmass.cpp @@ -0,0 +1,194 @@ +#include "../client.h" + +void command_npceditmass(Client *c, const Seperator *sep) +{ + if (strcasecmp(sep->arg[1], "usage") == 0) { + c->Message( + Chat::White, + "#npceditmass search_column [exact_match: =]search_value change_column change_value (apply)" + ); + return; + } + + std::string query = SQL( + SELECT + COLUMN_NAME + FROM + INFORMATION_SCHEMA.COLUMNS + WHERE + table_name = 'npc_types' + AND + COLUMN_NAME != 'id' + ); + + std::string search_column, search_value, change_column, change_value; + if (sep->arg[1]) { + search_column = sep->arg[1]; + } + if (sep->arg[2]) { + search_value = sep->arg[2]; + } + if (sep->arg[3]) { + change_column = sep->arg[3]; + } + if (sep->arg[4]) { + change_value = sep->arg[4]; + } + + bool valid_change_column = false; + bool valid_search_column = false; + auto results = content_db.QueryDatabase(query); + + std::vector possible_column_options; + + for (auto row = results.begin(); row != results.end(); ++row) { + if (row[0] == change_column) { + valid_change_column = true; + } + if (row[0] == search_column) { + valid_search_column = true; + } + + possible_column_options.push_back(row[0]); + } + + std::string options_glue = ", "; + + if (!valid_search_column) { + c->Message(Chat::Red, "You must specify a valid search column. [%s] is not valid", search_column.c_str()); + c->Message(Chat::Yellow, "Possible columns [%s]", implode(options_glue, possible_column_options).c_str()); + return; + } + + if (!valid_change_column) { + c->Message(Chat::Red, "You must specify a valid change column. [%s] is not valid", change_column.c_str()); + c->Message(Chat::Yellow, "Possible columns [%s]", implode(options_glue, possible_column_options).c_str()); + return; + } + + if (!valid_search_column || !valid_change_column) { + c->Message(Chat::Red, "One requested column is invalid"); + return; + } + + query = fmt::format( + SQL( + select + id, + name, + { 0 }, + { 1 } + from + npc_types + where + id IN( + select + spawnentry.npcID + from + spawnentry + join spawn2 on spawn2.spawngroupID = spawnentry.spawngroupID + where + spawn2.zone = '{2}' and spawn2.version = {3} + ) + ), + search_column, + change_column, + zone->GetShortName(), + zone->GetInstanceVersion() + ); + + std::string status = "(Searching)"; + + if (strcasecmp(sep->arg[5], "apply") == 0) { + status = "(Applying)"; + } + + std::vector npc_ids; + + bool exact_match = false; + if (search_value[0] == '=') { + exact_match = true; + search_value = search_value.substr(1); + } + + int found_count = 0; + results = content_db.QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + + std::string npc_id = row[0]; + std::string npc_name = row[1]; + std::string search_column_value = str_tolower(row[2]); + std::string change_column_current_value = row[3]; + + if (exact_match) { + if (search_column_value.compare(search_value) != 0) { + continue; + } + } + else { + if (search_column_value.find(search_value) == std::string::npos) { + continue; + } + } + + c->Message( + Chat::Yellow, + fmt::format( + "NPC ({0}) [{1}] ({2}) [{3}] Current ({4}) [{5}] New [{6}] {7}", + npc_id, + npc_name, + search_column, + search_column_value, + change_column, + change_column_current_value, + change_value, + status + ).c_str() + ); + + npc_ids.push_back(npc_id); + + found_count++; + } + + std::string saylink = fmt::format( + "#npceditmass {} {}{} {} {} apply", + search_column, + (exact_match ? "=" : ""), + search_value, + change_column, + change_value + ); + + if (strcasecmp(sep->arg[5], "apply") == 0) { + std::string npc_ids_string = implode(",", npc_ids); + if (npc_ids_string.empty()) { + c->Message(Chat::Red, "Error: Ran into an unknown error compiling NPC IDs"); + return; + } + + content_db.QueryDatabase( + fmt::format( + "UPDATE `npc_types` SET {} = '{}' WHERE id IN ({})", + change_column, + change_value, + npc_ids_string + ) + ); + + c->Message(Chat::Yellow, "Changes applied to (%i) NPC's", found_count); + zone->Repop(); + } + else { + c->Message(Chat::Yellow, "Found (%i) NPC's that match this search...", found_count); + + if (found_count > 0) { + c->Message( + Chat::Yellow, "To apply these changes, click <%s> or type [%s]", + EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Apply").c_str(), + saylink.c_str() + ); + } + } +} + diff --git a/zone/gm_commands/npcemote.cpp b/zone/gm_commands/npcemote.cpp new file mode 100755 index 000000000..c4ba83388 --- /dev/null +++ b/zone/gm_commands/npcemote.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +void command_npcemote(Client *c, const Seperator *sep) +{ + if (c->GetTarget() && c->GetTarget()->IsNPC() && sep->arg[1][0]) { + c->GetTarget()->Emote(sep->argplus[1]); + } + else { + c->Message(Chat::White, "Usage: #npcemote message (requires NPC target"); + } +} + diff --git a/zone/gm_commands/npcloot.cpp b/zone/gm_commands/npcloot.cpp new file mode 100755 index 000000000..6ebe557ac --- /dev/null +++ b/zone/gm_commands/npcloot.cpp @@ -0,0 +1,104 @@ +#include "../client.h" +#include "../corpse.h" + +void command_npcloot(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == 0) { + c->Message(Chat::White, "Error: No target"); + // #npcloot show + } + else if (strcasecmp(sep->arg[1], "show") == 0) { + if (c->GetTarget()->IsNPC()) { + c->GetTarget()->CastToNPC()->QueryLoot(c); + } + else if (c->GetTarget()->IsCorpse()) { + c->GetTarget()->CastToCorpse()->QueryLoot(c); + } + else { + c->Message(Chat::White, "Error: Target's type doesnt have loot"); + } + } + // These 2 types are *BAD* for the next few commands + else if (c->GetTarget()->IsClient() || c->GetTarget()->IsCorpse()) { + c->Message(Chat::White, "Error: Invalid target type, try a NPC =)."); + // #npcloot add + } + else if (strcasecmp(sep->arg[1], "add") == 0) { + // #npcloot add item + if (c->GetTarget()->IsNPC() && sep->IsNumber(2)) { + uint32 item = atoi(sep->arg[2]); + if (database.GetItem(item)) { + if (sep->arg[3][0] != 0 && sep->IsNumber(3)) { + c->GetTarget()->CastToNPC()->AddItem(item, atoi(sep->arg[3]), 0); + } + else { + c->GetTarget()->CastToNPC()->AddItem(item, 1, 0); + } + c->Message(Chat::White, "Added item(%i) to the %s's loot.", item, c->GetTarget()->GetName()); + } + else { + c->Message(Chat::White, "Error: #npcloot add: Item(%i) does not exist!", item); + } + } + else if (!sep->IsNumber(2)) { + c->Message(Chat::White, "Error: #npcloot add: Itemid must be a number."); + } + else { + c->Message(Chat::White, "Error: #npcloot add: This is not a valid target."); + } + } + // #npcloot remove + else if (strcasecmp(sep->arg[1], "remove") == 0) { + //#npcloot remove all + if (strcasecmp(sep->arg[2], "all") == 0) { + c->Message(Chat::White, "Error: #npcloot remove all: Not yet implemented."); + //#npcloot remove itemid + } + else { + if (c->GetTarget()->IsNPC() && sep->IsNumber(2)) { + uint32 item = atoi(sep->arg[2]); + c->GetTarget()->CastToNPC()->RemoveItem(item); + c->Message(Chat::White, "Removed item(%i) from the %s's loot.", item, c->GetTarget()->GetName()); + } + else if (!sep->IsNumber(2)) { + c->Message(Chat::White, "Error: #npcloot remove: Item must be a number."); + } + else { + c->Message(Chat::White, "Error: #npcloot remove: This is not a valid target."); + } + } + } + // #npcloot money + else if (strcasecmp(sep->arg[1], "money") == 0) { + if (c->GetTarget()->IsNPC() && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4) && sep->IsNumber(5)) { + if ((atoi(sep->arg[2]) < 34465 && atoi(sep->arg[2]) >= 0) && + (atoi(sep->arg[3]) < 34465 && atoi(sep->arg[3]) >= 0) && + (atoi(sep->arg[4]) < 34465 && atoi(sep->arg[4]) >= 0) && + (atoi(sep->arg[5]) < 34465 && atoi(sep->arg[5]) >= 0)) { + c->GetTarget()->CastToNPC()->AddCash( + atoi(sep->arg[5]), + atoi(sep->arg[4]), + atoi(sep->arg[3]), + atoi(sep->arg[2])); + c->Message( + Chat::White, + "Set %i Platinum, %i Gold, %i Silver, and %i Copper as %s's money.", + atoi(sep->arg[2]), + atoi(sep->arg[3]), + atoi(sep->arg[4]), + atoi(sep->arg[5]), + c->GetTarget()->GetName()); + } + else { + c->Message(Chat::White, "Error: #npcloot money: Values must be between 0-34465."); + } + } + else { + c->Message(Chat::White, "Usage: #npcloot money platinum gold silver copper"); + } + } + else { + c->Message(Chat::White, "Usage: #npcloot [show/money/add/remove] [itemid/all/money: pp gp sp cp]"); + } +} + diff --git a/zone/gm_commands/npcsay.cpp b/zone/gm_commands/npcsay.cpp new file mode 100755 index 000000000..96d79232f --- /dev/null +++ b/zone/gm_commands/npcsay.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +void command_npcsay(Client *c, const Seperator *sep) +{ + if (c->GetTarget() && c->GetTarget()->IsNPC() && sep->arg[1][0]) { + c->GetTarget()->Say(sep->argplus[1]); + } + else { + c->Message(Chat::White, "Usage: #npcsay message (requires NPC target"); + } +} + diff --git a/zone/gm_commands/npcshout.cpp b/zone/gm_commands/npcshout.cpp new file mode 100755 index 000000000..f04d74a9f --- /dev/null +++ b/zone/gm_commands/npcshout.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +void command_npcshout(Client *c, const Seperator *sep) +{ + if (c->GetTarget() && c->GetTarget()->IsNPC() && sep->arg[1][0]) { + c->GetTarget()->Shout(sep->argplus[1]); + } + else { + c->Message(Chat::White, "Usage: #npcshout message (requires NPC target"); + } +} + diff --git a/zone/gm_commands/npcspawn.cpp b/zone/gm_commands/npcspawn.cpp new file mode 100755 index 000000000..dd88bbece --- /dev/null +++ b/zone/gm_commands/npcspawn.cpp @@ -0,0 +1,97 @@ +#include "../client.h" + +void command_npcspawn(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Command Syntax: #npcspawn [Add|Create|Delete|Remove|Update]"); + return; + } + + if (!(c->GetTarget() && c->GetTarget()->IsNPC())) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; + } + + NPC *target = c->GetTarget()->CastToNPC(); + std::string spawn_type = str_tolower(sep->arg[1]); + uint32 extra = 0; + bool is_add = spawn_type.find("add") != std::string::npos; + bool is_create = spawn_type.find("create") != std::string::npos; + bool is_delete = spawn_type.find("delete") != std::string::npos; + bool is_remove = spawn_type.find("remove") != std::string::npos; + bool is_update = spawn_type.find("update") != std::string::npos; + if (!is_add && !is_create && !is_delete && !is_remove && !is_update) { + c->Message(Chat::White, "Command Syntax: #npcspawn [Add|Create|Delete|Remove|Update]"); + return; + } + + if (is_add || is_create) { + extra = ( + sep->IsNumber(2) ? + ( + is_add ? + std::stoi(sep->arg[2]) : + 1 + ) : ( + is_add ? + 1200 : + 0 + ) + ); // Default to 1200 for Add, 0 for Create if not set + content_db.NPCSpawnDB( + is_add ? NPCSpawnTypes::AddNewSpawngroup : NPCSpawnTypes::CreateNewSpawn, + zone->GetShortName(), + zone->GetInstanceVersion(), + c, + target, + extra + ); + c->Message( + Chat::White, + fmt::format( + "Spawn {} | Name: {} ({})", + is_add ? "Added" : "Created", + target->GetCleanName(), + target->GetID() + ).c_str() + ); + } + else if (is_delete || is_remove || is_update) { + uint8 spawn_update_type = ( + is_delete ? + NPCSpawnTypes::DeleteSpawn : + ( + is_remove ? + NPCSpawnTypes::RemoveSpawn : + NPCSpawnTypes::UpdateAppearance + ) + ); + std::string spawn_message = ( + is_delete ? + "Deleted" : + ( + is_remove ? + "Removed" : + "Updated" + ) + ); + content_db.NPCSpawnDB( + spawn_update_type, + zone->GetShortName(), + zone->GetInstanceVersion(), + c, + target + ); + c->Message( + Chat::White, + fmt::format( + "Spawn {} | Name: {} ({})", + spawn_message, + target->GetCleanName(), + target->GetID() + ).c_str() + ); + } +} + diff --git a/zone/gm_commands/npcspecialattk.cpp b/zone/gm_commands/npcspecialattk.cpp new file mode 100755 index 000000000..4d2a63dc8 --- /dev/null +++ b/zone/gm_commands/npcspecialattk.cpp @@ -0,0 +1,16 @@ +#include "../client.h" + +void command_npcspecialattk(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == 0 || c->GetTarget()->IsClient() || strlen(sep->arg[1]) <= 0 || strlen(sep->arg[2]) <= 0) { + c->Message( + Chat::White, + "Usage: #npcspecialattk *flagchar* *permtag* (Flags are E(nrage) F(lurry) R(ampage) S(ummon), permtag is 1 = True, 0 = False)." + ); + } + else { + c->GetTarget()->CastToNPC()->NPCSpecialAttacks(sep->arg[1], atoi(sep->arg[2])); + c->Message(Chat::White, "NPC Special Attack set."); + } +} + diff --git a/zone/gm_commands/npcstats.cpp b/zone/gm_commands/npcstats.cpp new file mode 100755 index 000000000..9bc05e766 --- /dev/null +++ b/zone/gm_commands/npcstats.cpp @@ -0,0 +1,20 @@ +#include "../client.h" + +void command_npcstats(Client *c, const Seperator *sep) +{ + if (c->GetTarget() && c->GetTarget()->IsNPC()) { + NPC *target = c->GetTarget()->CastToNPC(); + + // Stats + target->ShowStats(c); + + // Loot Data + if (target->GetLoottableID()) { + target->QueryLoot(c); + } + } + else { + c->Message(Chat::White, "You must target an NPC to use this command."); + } +} + diff --git a/zone/gm_commands/npctype_cache.cpp b/zone/gm_commands/npctype_cache.cpp new file mode 100755 index 000000000..27cc45429 --- /dev/null +++ b/zone/gm_commands/npctype_cache.cpp @@ -0,0 +1,27 @@ +#include "../client.h" + +void command_npctype_cache(Client *c, const Seperator *sep) +{ + if (sep->argnum > 0) { + for (int i = 0; i < sep->argnum; ++i) { + if (strcasecmp(sep->arg[i + 1], "all") == 0) { + c->Message(Chat::White, "Clearing all npc types from the cache."); + zone->ClearNPCTypeCache(-1); + } + else { + int id = atoi(sep->arg[i + 1]); + if (id > 0) { + c->Message(Chat::White, "Clearing npc type %d from the cache.", id); + zone->ClearNPCTypeCache(id); + return; + } + } + } + } + else { + c->Message(Chat::White, "Usage:"); + c->Message(Chat::White, "#npctype_cache [npctype_id] ..."); + c->Message(Chat::White, "#npctype_cache all"); + } +} + diff --git a/zone/gm_commands/npctypespawn.cpp b/zone/gm_commands/npctypespawn.cpp new file mode 100755 index 000000000..18c757aa6 --- /dev/null +++ b/zone/gm_commands/npctypespawn.cpp @@ -0,0 +1,29 @@ +#include "../client.h" + +void command_npctypespawn(Client *c, const Seperator *sep) +{ + if (sep->IsNumber(1)) { + const NPCType *tmp = 0; + if ((tmp = content_db.LoadNPCTypesData(atoi(sep->arg[1])))) { + //tmp->fixedZ = 1; + auto npc = new NPC(tmp, 0, c->GetPosition(), GravityBehavior::Water); + if (npc && sep->IsNumber(2)) { + npc->SetNPCFactionID(atoi(sep->arg[2])); + } + + npc->AddLootTable(); + if (npc->DropsGlobalLoot()) { + npc->CheckGlobalLootTables(); + } + entity_list.AddNPC(npc); + } + else { + c->Message(Chat::White, "NPC Type %i not found", atoi(sep->arg[1])); + } + } + else { + c->Message(Chat::White, "Usage: #npctypespawn npctypeid factionid"); + } + +} + diff --git a/zone/gm_commands/nudge.cpp b/zone/gm_commands/nudge.cpp new file mode 100755 index 000000000..e8f4bfaa0 --- /dev/null +++ b/zone/gm_commands/nudge.cpp @@ -0,0 +1,77 @@ +#include "../client.h" + +void command_nudge(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #nudge [x=f] [y=f] [z=f] [h=f] (partial/mixed arguments allowed)"); + } + else { + + auto target = c->GetTarget(); + if (!target) { + + c->Message(Chat::Yellow, "This command requires a target."); + return; + } + if (target->IsMoving()) { + + c->Message(Chat::Yellow, "This command requires a stationary target."); + return; + } + + glm::vec4 position_offset(0.0f, 0.0f, 0.0f, 0.0f); + for (auto index = 1; index <= 4; ++index) { + + if (!sep->arg[index]) { + continue; + } + + Seperator argsep(sep->arg[index], '='); + if (!argsep.arg[1][0]) { + continue; + } + + switch (argsep.arg[0][0]) { + case 'x': + position_offset.x = atof(argsep.arg[1]); + break; + case 'y': + position_offset.y = atof(argsep.arg[1]); + break; + case 'z': + position_offset.z = atof(argsep.arg[1]); + break; + case 'h': + position_offset.w = atof(argsep.arg[1]); + break; + default: + break; + } + } + + const auto ¤t_position = target->GetPosition(); + glm::vec4 new_position( + (current_position.x + position_offset.x), + (current_position.y + position_offset.y), + (current_position.z + position_offset.z), + (current_position.w + position_offset.w) + ); + + target->GMMove(new_position.x, new_position.y, new_position.z, new_position.w); + + c->Message( + Chat::White, + "Nudging '%s' to {%1.3f, %1.3f, %1.3f, %1.2f} (adjustment: {%1.3f, %1.3f, %1.3f, %1.2f})", + target->GetName(), + new_position.x, + new_position.y, + new_position.z, + new_position.w, + position_offset.x, + position_offset.y, + position_offset.z, + position_offset.w + ); + } +} + diff --git a/zone/gm_commands/nukebuffs.cpp b/zone/gm_commands/nukebuffs.cpp new file mode 100755 index 000000000..8d1267b3c --- /dev/null +++ b/zone/gm_commands/nukebuffs.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +void command_nukebuffs(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == 0) { + c->BuffFadeAll(); + } + else { + c->GetTarget()->BuffFadeAll(); + } +} + diff --git a/zone/gm_commands/nukeitem.cpp b/zone/gm_commands/nukeitem.cpp new file mode 100755 index 000000000..91e219b4d --- /dev/null +++ b/zone/gm_commands/nukeitem.cpp @@ -0,0 +1,16 @@ +#include "../client.h" + +void command_nukeitem(Client *c, const Seperator *sep) +{ + int numitems, itemid; + + if (c->GetTarget() && c->GetTarget()->IsClient() && (sep->IsNumber(1) || sep->IsHexNumber(1))) { + itemid = sep->IsNumber(1) ? atoi(sep->arg[1]) : hextoi(sep->arg[1]); + numitems = c->GetTarget()->CastToClient()->NukeItem(itemid); + c->Message(Chat::White, " %u items deleted", numitems); + } + else { + c->Message(Chat::White, "Usage: (targted) #nukeitem itemnum - removes the item from the player's inventory"); + } +} + diff --git a/zone/gm_commands/object.cpp b/zone/gm_commands/object.cpp new file mode 100755 index 000000000..9c233cd80 --- /dev/null +++ b/zone/gm_commands/object.cpp @@ -0,0 +1,1263 @@ +#include "../client.h" +#include "../object.h" +#include "../doors.h" + +void command_object(Client *c, const Seperator *sep) +{ + if (!c) { + return; + } // Crash Suppressant: No client. How did we get here? + + // Save it here. We sometimes have need to refer to it in multiple places. + const char *usage_string = "Usage: #object List|Add|Edit|Move|Rotate|Save|Copy|Delete|Undo"; + + if ((!sep) || (sep->argnum == 0)) { + c->Message(Chat::White, usage_string); + return; + } + + Object *o = nullptr; + Object_Struct od; + Door_Struct *ds; + uint32 id = 0; + uint32 itemid = 0; + uint32 icon = 0; + uint32 instance = 0; + uint32 newid = 0; + uint16 radius; + EQApplicationPacket *app; + + bool bNewObject = false; + + float x2; + float y2; + + // Temporary object type for static objects to allow manipulation + // NOTE: Zone::LoadZoneObjects() currently loads this as an uint8, so max value is 255! + static const uint32 staticType = 255; + + // Case insensitive commands (List == list == LIST) + strlwr(sep->arg[1]); + + if (strcasecmp(sep->arg[1], "list") == 0) { + // Insufficient or invalid args + if ((sep->argnum < 2) || (sep->arg[2][0] < '0') || + ((sep->arg[2][0] > '9') && ((sep->arg[2][0] & 0xDF) != 'A'))) { + c->Message(Chat::White, "Usage: #object List All|(radius)"); + return; + } + + if ((sep->arg[2][0] & 0xDF) == 'A') { + radius = 0; // List All + } + else if ((radius = atoi(sep->arg[2])) <= 0) { + radius = 500; + } // Invalid radius. Default to 500 units. + + if (radius == 0) + c->Message(Chat::White, "Objects within this zone:"); + else + c->Message(Chat::White, "Objects within %u units of your current location:", radius); + + std::string query; + if (radius) + query = StringFormat( + "SELECT id, xpos, ypos, zpos, heading, itemid, " + "objectname, type, icon, unknown08, unknown10, unknown20 " + "FROM object WHERE zoneid = %u AND version = %u " + "AND (xpos BETWEEN %.1f AND %.1f) " + "AND (ypos BETWEEN %.1f AND %.1f) " + "AND (zpos BETWEEN %.1f AND %.1f) " + "ORDER BY id", + zone->GetZoneID(), zone->GetInstanceVersion(), + c->GetX() - radius, // Yes, we're actually using a bounding box instead of a radius. + c->GetX() + radius, // Much less processing power used this way. + c->GetY() - radius, c->GetY() + radius, c->GetZ() - radius, c->GetZ() + radius + ); + else + query = StringFormat( + "SELECT id, xpos, ypos, zpos, heading, itemid, " + "objectname, type, icon, unknown08, unknown10, unknown20 " + "FROM object WHERE zoneid = %u AND version = %u " + "ORDER BY id", + zone->GetZoneID(), zone->GetInstanceVersion()); + + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Error in objects query"); + return; + } + + for (auto row = results.begin(); row != results.end(); ++row) { + id = atoi(row[0]); + od.x = atof(row[1]); + od.y = atof(row[2]); + od.z = atof(row[3]); + od.heading = atof(row[4]); + itemid = atoi(row[5]); + strn0cpy(od.object_name, row[6], sizeof(od.object_name)); + od.object_name[sizeof(od.object_name) - 1] = + '\0'; // Required if strlen(row[col++]) exactly == sizeof(object_name) + + od.object_type = atoi(row[7]); + icon = atoi(row[8]); + od.size = atoi(row[9]); + od.solidtype = atoi(row[10]); + od.unknown020 = atoi(row[11]); + + switch (od.object_type) { + case 0: // Static Object + case staticType: // Static Object unlocked for changes + if (od.size == 0) // Unknown08 field is optional Size parameter for static objects + od.size = 100; // Static object default Size is 100% + + c->Message( + Chat::White, "- STATIC Object (%s): id %u, x %.1f, y %.1f, z %.1f, h %.1f, model %s, " + "size %u, solidtype %u, incline %u", + (od.object_type == 0) ? "locked" : "unlocked", id, od.x, od.y, od.z, + od.heading, od.object_name, od.size, od.solidtype, od.unknown020 + ); + break; + + case OT_DROPPEDITEM: // Ground Spawn + c->Message( + Chat::White, "- TEMPORARY Object: id %u, x %.1f, y %.1f, z %.1f, h %.1f, itemid %u, " + "model %s, icon %u", + id, od.x, od.y, od.z, od.heading, itemid, od.object_name, icon + ); + break; + + default: // All others == Tradeskill Objects + c->Message( + Chat::White, "- TRADESKILL Object: id %u, x %.1f, y %.1f, z %.1f, h %.1f, model %s, " + "type %u, icon %u", + id, od.x, od.y, od.z, od.heading, od.object_name, od.object_type, icon + ); + break; + } + } + + c->Message(Chat::White, "%u object%s found", results.RowCount(), (results.RowCount() == 1) ? "" : "s"); + return; + } + + if (strcasecmp(sep->arg[1], "add") == 0) { + // Insufficient or invalid arguments + if ((sep->argnum < 3) || + ((sep->arg[3][0] == '\0') && (sep->arg[4][0] < '0') && (sep->arg[4][0] > '9'))) { + c->Message( + Chat::White, "Usage: (Static Object): #object Add [ObjectID] 0 Model [SizePercent] " + "[SolidType] [Incline]" + ); + c->Message(Chat::White, "Usage: (Tradeskill Object): #object Add [ObjectID] TypeNum Model Icon"); + c->Message( + Chat::White, "- Notes: Model must start with a letter, max length 16. SolidTypes = 0 (Solid), " + "1 (Sometimes Non-Solid)" + ); + return; + } + + int col; + + if (sep->argnum > 3) { // Model name in arg3? + if ((sep->arg[3][0] <= '9') && (sep->arg[3][0] >= '0')) { + // Nope, user must have specified ObjectID. Extract it. + id = atoi(sep->arg[2]); + col = 1; // Bump all other arguments one to the right. Model is in arg4. + } + else { + // Yep, arg3 is non-numeric, ObjectID must be omitted and model must be arg3 + id = 0; + col = 0; + } + } + else { + // Nope, only 3 args. Object ID must be omitted and arg3 must be model. + id = 0; + col = 0; + } + + memset(&od, 0, sizeof(od)); + + od.object_type = atoi(sep->arg[2 + col]); + + switch (od.object_type) { + case 0: // Static Object + if ((sep->argnum - col) > 3) { + od.size = atoi(sep->arg[4 + col]); // Size specified + + if ((sep->argnum - col) > 4) { + od.solidtype = atoi(sep->arg[5 + col]); // SolidType specified + + if ((sep->argnum - col) > 5) { + od.unknown020 = atoi(sep->arg[6 + col]); + } // Incline specified + } + } + break; + + case 1: // Ground Spawn + c->Message( + Chat::White, "ERROR: Object Type 1 is used for temporarily spawned ground spawns and dropped " + "items, which are not supported with #object. See the 'ground_spawns' table in " + "the database." + ); + return; + + default: // Everything else == Tradeskill Object + icon = ((sep->argnum - col) > 3) ? atoi(sep->arg[4 + col]) : 0; + + if (icon == 0) { + c->Message(Chat::White, "ERROR: Required property 'Icon' not specified for Tradeskill Object"); + return; + } + + break; + } + + od.x = c->GetX(); + od.y = c->GetY(); + od.z = c->GetZ() - (c->GetSize() * 0.625f); + od.heading = c->GetHeading(); + + std::string query; + if (id) { + // ID specified. Verify that it doesn't already exist. + query = StringFormat("SELECT COUNT(*) FROM object WHERE ID = %u", id); + auto results = content_db.QueryDatabase(query); + if (results.Success() && results.RowCount() != 0) { + auto row = results.begin(); + if (atoi(row[0]) > 0) { // Yep, in database already. + id = 0; + } + } + + // Not in database. Already spawned, just not saved? + // Yep, already spawned. + if (id && entity_list.FindObject(id)) { + id = 0; + } + + if (id == 0) { + c->Message(Chat::White, "ERROR: An object already exists with the id %u", atoi(sep->arg[2])); + return; + } + } + + int objectsFound = 0; + // Verify no other objects already in this spot (accidental double-click of Hotkey?) + query = StringFormat( + "SELECT COUNT(*) FROM object WHERE zoneid = %u " + "AND version=%u AND (xpos BETWEEN %.1f AND %.1f) " + "AND (ypos BETWEEN %.1f AND %.1f) " + "AND (zpos BETWEEN %.1f AND %.1f)", + zone->GetZoneID(), zone->GetInstanceVersion(), od.x - 0.2f, + od.x + 0.2f, // Yes, we're actually using a bounding box instead of a radius. + od.y - 0.2f, od.y + 0.2f, // Much less processing power used this way. + od.z - 0.2f, od.z + 0.2f + ); // It's pretty forgiving, though, allowing for close-proximity objects + + auto results = content_db.QueryDatabase(query); + if (results.Success() && results.RowCount() != 0) { + auto row = results.begin(); + objectsFound = atoi(row[0]); // Number of nearby objects from database + } + + // No objects found in database too close. How about spawned but not yet saved? + if (objectsFound == 0 && entity_list.FindNearbyObject(od.x, od.y, od.z, 0.2f)) { + objectsFound = 1; + } + + if (objectsFound) { + c->Message(Chat::White, "ERROR: Object already at this location."); + return; + } + + // Strip any single quotes from objectname (SQL injection FTL!) + strn0cpy(od.object_name, sep->arg[3 + col], sizeof(od.object_name)); + + uint32 len = strlen(od.object_name); + for (col = 0; col < (uint32) len; col++) { + if (od.object_name[col] != '\'') { + continue; + } + + // Uh oh, 1337 h4x0r monkeying around! Strip that apostrophe! + memcpy(&od.object_name[col], &od.object_name[col + 1], len - col); + len--; + col--; + } + + strupr(od.object_name); // Model names are always upper-case. + + if ((od.object_name[0] < 'A') || (od.object_name[0] > 'Z')) { + c->Message(Chat::White, "ERROR: Model name must start with a letter."); + return; + } + + if (id == 0) { + // No ID specified. Get a best-guess next number from the database + // If there's a problem retrieving an ID from the database, it'll end up being object # 1. No + // biggie. + + query = "SELECT MAX(id) FROM object"; + results = content_db.QueryDatabase(query); + if (results.Success() && results.RowCount() != 0) { + auto row = results.begin(); + id = atoi(row[0]); + } + + id++; + } + + // Make sure not to overwrite already-spawned objects that haven't been saved yet. + while (o = entity_list.FindObject(id)) + id++; + + // Static object + if (od.object_type == 0) { + od.object_type = staticType; + } // Temporary. We'll make it 0 when we Save + + od.zone_id = zone->GetZoneID(); + od.zone_instance = zone->GetInstanceVersion(); + + o = new Object(id, od.object_type, icon, od, nullptr); + + // Add to our zone entity list and spawn immediately for all clients + entity_list.AddObject(o, true); + + // Bump player back to avoid getting stuck inside new object + + x2 = 10.0f * sin(c->GetHeading() / 256.0f * 3.14159265f); + y2 = 10.0f * cos(c->GetHeading() / 256.0f * 3.14159265f); + c->MovePC(c->GetX() - x2, c->GetY() - y2, c->GetZ(), c->GetHeading()); + + c->Message( + Chat::White, "Spawning object with tentative id %u at location (%.1f, %.1f, %.1f heading %.1f). Use " + "'#object Save' to save to database when satisfied with placement.", + id, od.x, od.y, od.z, od.heading + ); + + // Temporary Static Object + if (od.object_type == staticType) + c->Message( + Chat::White, "- Note: Static Object will act like a tradeskill container and will not reflect " + "size, solidtype, or incline values until you commit with '#object Save', after " + "which it will be unchangeable until you use '#object Edit' and zone back in." + ); + + return; + } + + if (strcasecmp(sep->arg[1], "edit") == 0) { + + if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) < 1)) { + c->Message(Chat::White, "Usage: #object Edit (ObjectID) [PropertyName] [NewValue]"); + c->Message(Chat::White, "- Static Object (Type 0) Properties: model, type, size, solidtype, incline"); + c->Message(Chat::White, "- Tradeskill Object (Type 2+) Properties: model, type, icon"); + + return; + } + + o = entity_list.FindObject(id); + + // Object already available in-zone? + if (o) { + // Yep, looks like we can make real-time changes. + if (sep->argnum < 4) { + // Or not. '#object Edit (ObjectID)' called without PropertyName and NewValue + c->Message(Chat::White, "Note: Object %u already unlocked and ready for changes", id); + return; + } + } + else { + // Object not found in-zone in a modifiable form. Check for valid matching circumstances. + std::string query = StringFormat("SELECT zoneid, version, type FROM object WHERE id = %u", id); + auto results = content_db.QueryDatabase(query); + if (!results.Success() || results.RowCount() == 0) { + c->Message(Chat::White, "ERROR: Object %u not found", id); + return; + } + + auto row = results.begin(); + od.zone_id = atoi(row[0]); + od.zone_instance = atoi(row[1]); + od.object_type = atoi(row[2]); + uint32 objectsFound = 1; + + // Object not in this zone? + if (od.zone_id != zone->GetZoneID()) { + c->Message(Chat::White, "ERROR: Object %u not in this zone.", id); + return; + } + + // Object not in this instance? + if (od.zone_instance != zone->GetInstanceVersion()) { + c->Message(Chat::White, "ERROR: Object %u not part of this instance version.", id); + return; + } + + switch (od.object_type) { + case 0: // Static object needing unlocking + // Convert to tradeskill object temporarily for changes + query = StringFormat("UPDATE object SET type = %u WHERE id = %u", staticType, id); + + content_db.QueryDatabase(query); + + c->Message( + Chat::White, "Static Object %u unlocked for editing. You must zone out and back in to " + "make your changes, then commit them with '#object Save'.", + id + ); + if (sep->argnum >= 4) { + c->Message( + Chat::White, "NOTE: The change you specified has not been applied, since the " + "static object had not been unlocked for editing yet." + ); + } + return; + + case OT_DROPPEDITEM: + c->Message( + Chat::White, "ERROR: Object %u is a temporarily spawned ground spawn or dropped item, " + "which cannot be manipulated with #object. See the 'ground_spawns' table " + "in the database.", + id + ); + return; + + case staticType: + c->Message( + Chat::White, "ERROR: Object %u has been unlocked for editing, but you must zone out " + "and back in for your client to refresh its object table before you can " + "make changes to it.", + id + ); + return; + + default: + // Unknown error preventing us from seeing the object in the zone. + c->Message(Chat::White, "ERROR: Unknown problem attempting to manipulate object %u", id); + return; + } + } + + // If we're here, we have a manipulable object ready for changes. + strlwr(sep->arg[3]); // Case insensitive PropertyName + strupr(sep->arg[4]); // In case it's model name, which should always be upper-case + + // Read current object info for reference + icon = o->GetIcon(); + o->GetObjectData(&od); + + // We'll be a little more picky with property names, to prevent errors. Check against the whole word. + if (strcmp(sep->arg[3], "model") == 0) { + + if ((sep->arg[4][0] < 'A') || (sep->arg[4][0] > 'Z')) { + c->Message(Chat::White, "ERROR: Model names must begin with a letter."); + return; + } + + strn0cpy(od.object_name, sep->arg[4], sizeof(od.object_name)); + + o->SetObjectData(&od); + + c->Message(Chat::White, "Object %u now being rendered with model '%s'", id, od.object_name); + } + else if (strcmp(sep->arg[3], "type") == 0) { + if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) { + c->Message(Chat::White, "ERROR: Invalid type number"); + return; + } + + od.object_type = atoi(sep->arg[4]); + + switch (od.object_type) { + case 0: + // Convert Static Object to temporary changeable type + od.object_type = staticType; + c->Message( + Chat::White, "Note: Static Object will still act like tradeskill object and will not " + "reflect size, solidtype, or incline settings until committed to the " + "database with '#object Save', after which it will be unchangeable until " + "it is unlocked again with '#object Edit'." + ); + break; + + case OT_DROPPEDITEM: + c->Message( + Chat::White, "ERROR: Object Type 1 is used for temporarily spawned ground spawns and " + "dropped items, which are not supported with #object. See the " + "'ground_spawns' table in the database." + ); + return; + + default: + c->Message(Chat::White, "Object %u changed to Tradeskill Object Type %u", id, od.object_type); + break; + } + + o->SetType(od.object_type); + } + else if (strcmp(sep->arg[3], "size") == 0) { + if (od.object_type != staticType) { + c->Message( + 0, "ERROR: Object %u is not a Static Object and does not support the Size property", + id + ); + return; + } + + if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) { + c->Message(Chat::White, "ERROR: Invalid size specified. Please enter a number."); + return; + } + + od.size = atoi(sep->arg[4]); + o->SetObjectData(&od); + + if (od.size == 0) { // 0 == unspecified == 100% + od.size = 100; + } + + c->Message( + Chat::White, "Static Object %u set to %u%% size. Size will take effect when you commit to the " + "database with '#object Save', after which the object will be unchangeable until " + "you unlock it again with '#object Edit' and zone out and back in.", + id, od.size + ); + } + else if (strcmp(sep->arg[3], "solidtype") == 0) { + + if (od.object_type != staticType) { + c->Message( + Chat::White, "ERROR: Object %u is not a Static Object and does not support the " + "SolidType property", + id + ); + return; + } + + if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) { + c->Message(Chat::White, "ERROR: Invalid solidtype specified. Please enter a number."); + return; + } + + od.solidtype = atoi(sep->arg[4]); + o->SetObjectData(&od); + + c->Message( + Chat::White, "Static Object %u set to SolidType %u. Change will take effect when you commit " + "to the database with '#object Save'. Support for this property is on a " + "per-model basis, mostly seen in smaller objects such as chests and tables.", + id, od.solidtype + ); + } + else if (strcmp(sep->arg[3], "icon") == 0) { + + if ((od.object_type < 2) || (od.object_type == staticType)) { + c->Message( + Chat::White, "ERROR: Object %u is not a Tradeskill Object and does not support the " + "Icon property", + id + ); + return; + } + + if ((icon = atoi(sep->arg[4])) == 0) { + c->Message(Chat::White, "ERROR: Invalid Icon specified. Please enter an icon number."); + return; + } + + o->SetIcon(icon); + c->Message(Chat::White, "Tradeskill Object %u icon set to %u", id, icon); + } + else if (strcmp(sep->arg[3], "incline") == 0) { + if (od.object_type != staticType) { + c->Message( + 0, + "ERROR: Object %u is not a Static Object and does not support the Incline property", + id + ); + return; + } + + if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) { + c->Message( + 0, + "ERROR: Invalid Incline specified. Please enter a number. Normal range is 0-512." + ); + return; + } + + od.unknown020 = atoi(sep->arg[4]); + o->SetObjectData(&od); + + c->Message( + Chat::White, "Static Object %u set to %u incline. Incline will take effect when you commit to " + "the database with '#object Save', after which the object will be unchangeable " + "until you unlock it again with '#object Edit' and zone out and back in.", + id, od.unknown020 + ); + } + else { + c->Message(Chat::White, "ERROR: Unrecognized property name: %s", sep->arg[3]); + return; + } + + // Repop object to have it reflect the change. + app = new EQApplicationPacket(); + o->CreateDeSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + + app = new EQApplicationPacket(); + o->CreateSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + return; + } + + if (strcasecmp(sep->arg[1], "move") == 0) { + + if ((sep->argnum < 2) || // Not enough arguments + ((id = atoi(sep->arg[2])) == 0) || // ID not specified + (((sep->arg[3][0] < '0') || (sep->arg[3][0] > '9')) && ((sep->arg[3][0] & 0xDF) != 'T') && + (sep->arg[3][0] != '-') && (sep->arg[3][0] != '.'))) { // Location argument not specified correctly + c->Message(Chat::White, "Usage: #object Move (ObjectID) ToMe|(x y z [h])"); + return; + } + + if (!(o = entity_list.FindObject(id))) { + std::string query = StringFormat("SELECT zoneid, version, type FROM object WHERE id = %u", id); + auto results = content_db.QueryDatabase(query); + if (!results.Success() || results.RowCount() == 0) { + c->Message(Chat::White, "ERROR: Object %u not found", id); + return; + } + + auto row = results.begin(); + od.zone_id = atoi(row[0]); + od.zone_instance = atoi(row[1]); + od.object_type = atoi(row[2]); + + if (od.zone_id != zone->GetZoneID()) { + c->Message(Chat::White, "ERROR: Object %u is not in this zone", id); + return; + } + + if (od.zone_instance != zone->GetInstanceVersion()) { + c->Message(Chat::White, "ERROR: Object %u is not in this instance version", id); + return; + } + + switch (od.object_type) { + case 0: + c->Message( + Chat::White, "ERROR: Object %u is not yet unlocked for editing. Use '#object Edit' " + "then zone out and back in to move it.", + id + ); + return; + + case staticType: + c->Message( + Chat::White, "ERROR: Object %u has been unlocked for editing, but you must zone out " + "and back in before your client sees the change and will allow you to " + "move it.", + id + ); + return; + + case 1: + c->Message( + Chat::White, "ERROR: Object %u is a temporary spawned object and cannot be " + "manipulated with #object. See the 'ground_spawns' table in the " + "database.", + id + ); + return; + + default: + c->Message(Chat::White, "ERROR: Object %u not located in zone.", id); + return; + } + } + + // Move To Me + if ((sep->arg[3][0] & 0xDF) == 'T') { + od.x = c->GetX(); + od.y = c->GetY(); + od.z = c->GetZ() - + (c->GetSize() * + 0.625f); // Compensate for #loc bumping up Z coordinate by 62.5% of character's size. + + o->SetHeading(c->GetHeading()); + + // Bump player back to avoid getting stuck inside object + + x2 = 10.0f * std::sin(c->GetHeading() / 256.0f * 3.14159265f); + y2 = 10.0f * std::cos(c->GetHeading() / 256.0f * 3.14159265f); + c->MovePC(c->GetX() - x2, c->GetY() - y2, c->GetZ(), c->GetHeading()); + } // Move to x, y, z [h] + else { + od.x = atof(sep->arg[3]); + if (sep->argnum > 3) { + od.y = atof(sep->arg[4]); + } + else { + o->GetLocation(nullptr, &od.y, nullptr); + } + + if (sep->argnum > 4) { + od.z = atof(sep->arg[5]); + } + else { + o->GetLocation(nullptr, nullptr, &od.z); + } + + if (sep->argnum > 5) { + o->SetHeading(atof(sep->arg[6])); + } + } + + o->SetLocation(od.x, od.y, od.z); + + // Despawn and respawn object to reflect change + app = new EQApplicationPacket(); + o->CreateDeSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + + app = new EQApplicationPacket(); + o->CreateSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + return; + } + + if (strcasecmp(sep->arg[1], "rotate") == 0) { + // Insufficient or invalid arguments + if ((sep->argnum < 3) || ((id = atoi(sep->arg[2])) == 0)) { + c->Message(Chat::White, "Usage: #object Rotate (ObjectID) (Heading, 0-512)"); + return; + } + + if ((o = entity_list.FindObject(id)) == nullptr) { + c->Message( + Chat::White, "ERROR: Object %u not found in zone, or is a static object not yet unlocked with " + "'#object Edit' for editing.", + id + ); + return; + } + + o->SetHeading(atof(sep->arg[3])); + + // Despawn and respawn object to reflect change + app = new EQApplicationPacket(); + o->CreateDeSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + + app = new EQApplicationPacket(); + o->CreateSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + return; + } + + if (strcasecmp(sep->arg[1], "save") == 0) { + // Insufficient or invalid arguments + if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) == 0)) { + c->Message(Chat::White, "Usage: #object Save (ObjectID)"); + return; + } + + o = entity_list.FindObject(id); + + od.zone_id = 0; + od.zone_instance = 0; + od.object_type = 0; + + // If this ID isn't in the database yet, it's a new object + bNewObject = true; + std::string query = StringFormat("SELECT zoneid, version, type FROM object WHERE id = %u", id); + auto results = content_db.QueryDatabase(query); + if (results.Success() && results.RowCount() != 0) { + auto row = results.begin(); + od.zone_id = atoi(row[0]); + od.zone_instance = atoi(row[1]); + od.object_type = atoi(row[2]); + + // ID already in database. Not a new object. + bNewObject = false; + } + + if (!o) { + // Object not found in zone. Can't save an object we can't see. + + if (bNewObject) { + c->Message(Chat::White, "ERROR: Object %u not found", id); + return; + } + + if (od.zone_id != zone->GetZoneID()) { + c->Message(Chat::White, "ERROR: Wrong Object ID. %u is not part of this zone.", id); + return; + } + + if (od.zone_instance != zone->GetInstanceVersion()) { + c->Message(Chat::White, "ERROR: Wrong Object ID. %u is not part of this instance version.", id); + return; + } + + if (od.object_type == 0) { + c->Message( + Chat::White, "ERROR: Static Object %u has already been committed. Use '#object Edit " + "%u' and zone out and back in to make changes.", + id, id + ); + return; + } + + if (od.object_type == 1) { + c->Message( + Chat::White, "ERROR: Object %u is a temporarily spawned ground spawn or dropped item, " + "which is not supported with #object. See the 'ground_spawns' table in " + "the database.", + id + ); + return; + } + + c->Message(Chat::White, "ERROR: Object %u not found.", id); + return; + } + + // Oops! Another GM already saved an object with our id from another zone. + // We'll have to get a new one. + if ((od.zone_id > 0) && (od.zone_id != zone->GetZoneID())) { + id = 0; + } + + // Oops! Another GM already saved an object with our id from another instance. + // We'll have to get a new one. + if ((id > 0) && (od.zone_instance != zone->GetInstanceVersion())) { + id = 0; + } + + // If we're asking for a new ID, it's a new object. + bNewObject |= (id == 0); + + o->GetObjectData(&od); + od.object_type = o->GetType(); + icon = o->GetIcon(); + + // We're committing to the database now. Return temporary object type to actual. + if (od.object_type == staticType) { + od.object_type = 0; + } + + if (!bNewObject) { + query = StringFormat( + "UPDATE object SET zoneid = %u, version = %u, " + "xpos = %.1f, ypos=%.1f, zpos=%.1f, heading=%.1f, " + "objectname = '%s', type = %u, icon = %u, " + "unknown08 = %u, unknown10 = %u, unknown20 = %u " + "WHERE ID = %u", + zone->GetZoneID(), zone->GetInstanceVersion(), od.x, od.y, od.z, + od.heading, od.object_name, od.object_type, icon, od.size, + od.solidtype, od.unknown020, id + ); + } + else if (id == 0) { + query = StringFormat( + "INSERT INTO object " + "(zoneid, version, xpos, ypos, zpos, heading, objectname, " + "type, icon, unknown08, unknown10, unknown20) " + "VALUES (%u, %u, %.1f, %.1f, %.1f, %.1f, '%s', %u, %u, %u, %u, %u)", + zone->GetZoneID(), zone->GetInstanceVersion(), od.x, od.y, od.z, + od.heading, od.object_name, od.object_type, icon, od.size, + od.solidtype, od.unknown020 + ); + } + else { + query = StringFormat( + "INSERT INTO object " + "(id, zoneid, version, xpos, ypos, zpos, heading, objectname, " + "type, icon, unknown08, unknown10, unknown20) " + "VALUES (%u, %u, %u, %.1f, %.1f, %.1f, %.1f, '%s', %u, %u, %u, %u, %u)", + id, zone->GetZoneID(), zone->GetInstanceVersion(), od.x, od.y, od.z, + od.heading, od.object_name, od.object_type, icon, od.size, + od.solidtype, od.unknown020 + ); + } + + results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Database Error: %s", results.ErrorMessage().c_str()); + return; + } + + if (results.RowsAffected() == 0) { + // No change made, but no error message given + c->Message(Chat::White, "Database Error: Could not save change to Object %u", id); + return; + } + + if (bNewObject) { + if (newid == results.LastInsertedID()) { + c->Message(Chat::White, "Saved new Object %u to database", id); + return; + } + + c->Message(Chat::White, "Saved Object. NOTE: Database returned a new ID number for object: %u", newid); + id = newid; + return; + } + + c->Message(Chat::White, "Saved changes to Object %u", id); + newid = id; + + if (od.object_type == 0) { + // Static Object - Respawn as nonfunctional door + + app = new EQApplicationPacket(); + o->CreateDeSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + + entity_list.RemoveObject(o->GetID()); + + auto door = DoorsRepository::NewEntity(); + + door.zone = zone->GetShortName(); + + door.id = 1000000000 + id; // Out of range of normal use for doors.id + door.doorid = -1; // Client doesn't care if these are all the same door_id + door.pos_x = od.x; + door.pos_y = od.y; + door.pos_z = od.z; + door.heading = od.heading; + + door.name = od.object_name; + + // Strip trailing "_ACTORDEF" if present. Client won't accept it for doors. + int pos = door.name.size() - strlen("_ACTORDEF"); + if (pos > 0 && door.name.compare(pos, std::string::npos, "_ACTORDEF") == 0) { + door.name.erase(pos); + } + + door.dest_zone = "NONE"; + + if ((door.size = od.size) == 0) { // unknown08 = optional size percentage + door.size = 100; + } + + door.opentype = od.solidtype; + + switch (door.opentype) // unknown10 = optional request_nonsolid (0 or 1 or experimental number) + { + case 0: + door.opentype = 31; + break; + + case 1: + door.opentype = 9; + break; + } + + door.incline = od.unknown020; // unknown20 = optional incline value + door.client_version_mask = 0xFFFFFFFF; + + Doors *doors = new Doors(door); + + entity_list.AddDoor(doors); + + app = new EQApplicationPacket(OP_SpawnDoor, sizeof(Door_Struct)); + ds = (Door_Struct *) app->pBuffer; + + memset(ds, 0, sizeof(Door_Struct)); + memcpy(ds->name, door.name.c_str(), 32); + ds->xPos = door.pos_x; + ds->yPos = door.pos_y; + ds->zPos = door.pos_z; + ds->heading = door.heading; + ds->incline = door.incline; + ds->size = door.size; + ds->doorId = door.doorid; + ds->opentype = door.opentype; + ds->unknown0052[9] = 1; // *ptr-1 and *ptr-3 from EntityList::MakeDoorSpawnPacket() + ds->unknown0052[11] = 1; + + entity_list.QueueClients(0, app); + safe_delete(app); + + c->Message( + Chat::White, "NOTE: Object %u is now a static object, and is unchangeable. To make future " + "changes, use '#object Edit' to convert it to a changeable form, then zone out " + "and back in.", + id + ); + } + return; + } + + if (strcasecmp(sep->arg[1], "copy") == 0) { + // Insufficient or invalid arguments + if ((sep->argnum < 3) || + (((sep->arg[2][0] & 0xDF) != 'A') && ((sep->arg[2][0] < '0') || (sep->arg[2][0] > '9')))) { + c->Message(Chat::White, "Usage: #object Copy All|(ObjectID) (InstanceVersion)"); + c->Message(Chat::White, "- Note: Only objects saved in the database can be copied to another instance."); + return; + } + + od.zone_instance = atoi(sep->arg[3]); + + if (od.zone_instance == zone->GetInstanceVersion()) { + c->Message(Chat::White, "ERROR: Source and destination instance versions are the same."); + return; + } + + if ((sep->arg[2][0] & 0xDF) == 'A') { + // Copy All + + std::string query = + StringFormat( + "INSERT INTO object " + "(zoneid, version, xpos, ypos, zpos, heading, itemid, " + "objectname, type, icon, unknown08, unknown10, unknown20) " + "SELECT zoneid, %u, xpos, ypos, zpos, heading, itemid, " + "objectname, type, icon, unknown08, unknown10, unknown20 " + "FROM object WHERE zoneid = %u) AND version = %u", + od.zone_instance, zone->GetZoneID(), zone->GetInstanceVersion()); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::White, "Database Error: %s", results.ErrorMessage().c_str()); + return; + } + + c->Message( + Chat::White, "Copied %u object%s into instance version %u", results.RowCount(), + (results.RowCount() == 1) ? "" : "s", od.zone_instance + ); + return; + } + + id = atoi(sep->arg[2]); + + std::string query = StringFormat( + "INSERT INTO object " + "(zoneid, version, xpos, ypos, zpos, heading, itemid, " + "objectname, type, icon, unknown08, unknown10, unknown20) " + "SELECT zoneid, %u, xpos, ypos, zpos, heading, itemid, " + "objectname, type, icon, unknown08, unknown10, unknown20 " + "FROM object WHERE id = %u AND zoneid = %u AND version = %u", + od.zone_instance, id, zone->GetZoneID(), zone->GetInstanceVersion()); + auto results = content_db.QueryDatabase(query); + if (results.Success() && results.RowsAffected() > 0) { + c->Message(Chat::White, "Copied Object %u into instance version %u", id, od.zone_instance); + return; + } + + // Couldn't copy the object. + + // got an error message + if (!results.Success()) { + c->Message(Chat::White, "Database Error: %s", results.ErrorMessage().c_str()); + return; + } + + // No database error returned. See if we can figure out why. + + query = StringFormat("SELECT zoneid, version FROM object WHERE id = %u", id); + results = content_db.QueryDatabase(query); + if (!results.Success()) { + return; + } + + if (results.RowCount() == 0) { + c->Message(Chat::White, "ERROR: Object %u not found", id); + return; + } + + auto row = results.begin(); + // Wrong ZoneID? + if (atoi(row[0]) != zone->GetZoneID()) { + c->Message(Chat::White, "ERROR: Object %u is not part of this zone.", id); + return; + } + + // Wrong Instance Version? + if (atoi(row[1]) != zone->GetInstanceVersion()) { + c->Message(Chat::White, "ERROR: Object %u is not part of this instance version.", id); + return; + } + + // Well, NO clue at this point. Just let 'em know something screwed up. + c->Message( + Chat::White, "ERROR: Unknown database error copying Object %u to instance version %u", id, + od.zone_instance + ); + return; + } + + if (strcasecmp(sep->arg[1], "delete") == 0) { + + if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) <= 0)) { + c->Message( + Chat::White, "Usage: #object Delete (ObjectID) -- NOTE: Object deletions are permanent and " + "cannot be undone!" + ); + return; + } + + o = entity_list.FindObject(id); + + if (o) { + // Object found in zone. + + app = new EQApplicationPacket(); + o->CreateDeSpawnPacket(app); + entity_list.QueueClients(nullptr, app); + + entity_list.RemoveObject(o->GetID()); + + // Verifying ZoneID and Version in case someone else ended up adding an object with our ID + // from a different zone/version. Don't want to delete someone else's work. + std::string query = StringFormat( + "DELETE FROM object " + "WHERE id = %u AND zoneid = %u " + "AND version = %u LIMIT 1", + id, zone->GetZoneID(), zone->GetInstanceVersion()); + auto results = content_db.QueryDatabase(query); + + c->Message(Chat::White, "Object %u deleted", id); + return; + } + + // Object not found in zone. + std::string query = StringFormat( + "SELECT type FROM object " + "WHERE id = %u AND zoneid = %u " + "AND version = %u LIMIT 1", + id, zone->GetZoneID(), zone->GetInstanceVersion()); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + return; + } + + if (results.RowCount() == 0) { + c->Message(Chat::White, "ERROR: Object %u not found in this zone or instance!", id); + return; + } + + auto row = results.begin(); + + switch (atoi(row[0])) { + case 0: // Static Object + query = StringFormat( + "DELETE FROM object WHERE id = %u " + "AND zoneid = %u AND version = %u LIMIT 1", + id, zone->GetZoneID(), zone->GetInstanceVersion()); + results = content_db.QueryDatabase(query); + + c->Message( + Chat::White, "Object %u deleted. NOTE: This static object will remain for anyone currently in " + "the zone until they next zone out and in.", + id + ); + return; + + case 1: // Temporary Spawn + c->Message( + Chat::White, "ERROR: Object %u is a temporarily spawned ground spawn or dropped item, which " + "is not supported with #object. See the 'ground_spawns' table in the database.", + id + ); + return; + } + + return; + } + + if (strcasecmp(sep->arg[1], "undo") == 0) { + // Insufficient or invalid arguments + if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) == 0)) { + c->Message( + Chat::White, "Usage: #object Undo (ObjectID) -- Reload object from database, undoing any " + "changes you have made" + ); + return; + } + + o = entity_list.FindObject(id); + + if (!o) { + c->Message( + Chat::White, "ERROR: Object %u not found in zone in a manipulable form. No changes to undo.", + id + ); + return; + } + + if (o->GetType() == OT_DROPPEDITEM) { + c->Message( + Chat::White, "ERROR: Object %u is a temporary spawned item and cannot be manipulated with " + "#object. See the 'ground_spawns' table in the database.", + id + ); + return; + } + + // Despawn current item for reloading from database + app = new EQApplicationPacket(); + o->CreateDeSpawnPacket(app); + entity_list.QueueClients(0, app); + entity_list.RemoveObject(o->GetID()); + safe_delete(app); + + std::string query = StringFormat( + "SELECT xpos, ypos, zpos, " + "heading, objectname, type, icon, " + "unknown08, unknown10, unknown20 " + "FROM object WHERE id = %u", + id + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success() || results.RowCount() == 0) { + c->Message(Chat::White, "Database Error: %s", results.ErrorMessage().c_str()); + return; + } + + memset(&od, 0, sizeof(od)); + + auto row = results.begin(); + + od.x = atof(row[0]); + od.y = atof(row[1]); + od.z = atof(row[2]); + od.heading = atof(row[3]); + strn0cpy(od.object_name, row[4], sizeof(od.object_name)); + od.object_type = atoi(row[5]); + icon = atoi(row[6]); + od.size = atoi(row[7]); + od.solidtype = atoi(row[8]); + od.unknown020 = atoi(row[9]); + + if (od.object_type == 0) { + od.object_type = staticType; + } + + o = new Object(id, od.object_type, icon, od, nullptr); + entity_list.AddObject(o, true); + + c->Message(Chat::White, "Object %u reloaded from database.", id); + return; + } + + c->Message(Chat::White, usage_string); +} + diff --git a/zone/gm_commands/oocmute.cpp b/zone/gm_commands/oocmute.cpp new file mode 100755 index 000000000..a1e1199a2 --- /dev/null +++ b/zone/gm_commands/oocmute.cpp @@ -0,0 +1,18 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_oocmute(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0 || !(sep->arg[1][0] == '1' || sep->arg[1][0] == '0')) { + c->Message(Chat::White, "Usage: #oocmute [1/0]"); + } + else { + auto outapp = new ServerPacket(ServerOP_OOCMute, 1); + *(outapp->pBuffer) = atoi(sep->arg[1]); + worldserver.SendPacket(outapp); + safe_delete(outapp); + } +} + diff --git a/zone/gm_commands/opcode.cpp b/zone/gm_commands/opcode.cpp new file mode 100755 index 000000000..8c8601e96 --- /dev/null +++ b/zone/gm_commands/opcode.cpp @@ -0,0 +1,11 @@ +#include "../client.h" +#include "../../common/patches/patches.h" + +void command_opcode(Client *c, const Seperator *sep) +{ + if (!strcasecmp(sep->arg[1], "reload")) { + ReloadAllPatches(); + c->Message(Chat::White, "Opcodes for all patches have been reloaded"); + } +} + diff --git a/zone/gm_commands/path.cpp b/zone/gm_commands/path.cpp new file mode 100755 index 000000000..69604e271 --- /dev/null +++ b/zone/gm_commands/path.cpp @@ -0,0 +1,27 @@ +#include "../client.h" + +void command_path(Client *c, const Seperator *sep) +{ + if (zone->pathing) { + zone->pathing->DebugCommand(c, sep); + } +} + +void Client::Undye() +{ + for (int cur_slot = EQ::textures::textureBegin; cur_slot <= EQ::textures::LastTexture; cur_slot++) { + uint8 slot2 = SlotConvert(cur_slot); + EQ::ItemInstance *inst = m_inv.GetItem(slot2); + + if (inst != nullptr) { + inst->SetColor(inst->GetItem()->Color); + database.SaveInventory(CharacterID(), inst, slot2); + } + + m_pp.item_tint.Slot[cur_slot].Color = 0; + SendWearChange(cur_slot); + } + + database.DeleteCharacterDye(this->CharacterID()); +} + diff --git a/zone/gm_commands/peekinv.cpp b/zone/gm_commands/peekinv.cpp new file mode 100755 index 000000000..fe6956191 --- /dev/null +++ b/zone/gm_commands/peekinv.cpp @@ -0,0 +1,332 @@ +#include "../client.h" +#include "../object.h" + +void command_peekinv(Client *c, const Seperator *sep) +{ + // this can be cleaned up once inventory is cleaned up + enum { + peekNone = 0x0000, + peekEquip = 0x0001, + peekGen = 0x0002, + peekCursor = 0x0004, + peekLimbo = 0x0008, + peekTrib = 0x0010, + peekBank = 0x0020, + peekShBank = 0x0040, + peekTrade = 0x0080, + peekWorld = 0x0100, + peekOutOfScope = (peekWorld * 2) // less than + }; + + static const char *scope_prefix[] = {"equip", "gen", "cursor", "limbo", "trib", "bank", "shbank", "trade", "world"}; + + static const int16 scope_range[][2] = { + {EQ::invslot::EQUIPMENT_BEGIN, EQ::invslot::EQUIPMENT_END}, + {EQ::invslot::GENERAL_BEGIN, EQ::invslot::GENERAL_END}, + {EQ::invslot::slotCursor, EQ::invslot::slotCursor}, + {EQ::invslot::SLOT_INVALID, EQ::invslot::SLOT_INVALID}, + {EQ::invslot::TRIBUTE_BEGIN, EQ::invslot::TRIBUTE_END}, + {EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END}, + {EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END}, + {EQ::invslot::TRADE_BEGIN, EQ::invslot::TRADE_END}, + {EQ::invslot::SLOT_BEGIN, (EQ::invtype::WORLD_SIZE - 1)} + }; + + static const bool scope_bag[] = {false, true, true, true, false, true, true, true, true}; + + if (!c) { + return; + } + + if (c->GetTarget() && !c->GetTarget()->IsClient()) { + c->Message(Chat::White, "You must target a PC for this command."); + return; + } + + int scopeMask = peekNone; + + if (strcasecmp(sep->arg[1], "all") == 0) { scopeMask = (peekOutOfScope - 1); } + else if (strcasecmp(sep->arg[1], "equip") == 0) { scopeMask |= peekEquip; } + else if (strcasecmp(sep->arg[1], "gen") == 0) { scopeMask |= peekGen; } + else if (strcasecmp(sep->arg[1], "cursor") == 0) { scopeMask |= peekCursor; } + else if (strcasecmp(sep->arg[1], "poss") == 0) { scopeMask |= (peekEquip | peekGen | peekCursor); } + else if (strcasecmp(sep->arg[1], "limbo") == 0) { scopeMask |= peekLimbo; } + else if (strcasecmp(sep->arg[1], "curlim") == 0) { scopeMask |= (peekCursor | peekLimbo); } + else if (strcasecmp(sep->arg[1], "trib") == 0) { scopeMask |= peekTrib; } + else if (strcasecmp(sep->arg[1], "bank") == 0) { scopeMask |= peekBank; } + else if (strcasecmp(sep->arg[1], "shbank") == 0) { scopeMask |= peekShBank; } + else if (strcasecmp(sep->arg[1], "allbank") == 0) { scopeMask |= (peekBank | peekShBank); } + else if (strcasecmp(sep->arg[1], "trade") == 0) { scopeMask |= peekTrade; } + else if (strcasecmp(sep->arg[1], "world") == 0) { scopeMask |= peekWorld; } + + if (!scopeMask) { + c->Message( + Chat::White, + "Usage: #peekinv [equip|gen|cursor|poss|limbo|curlim|trib|bank|shbank|allbank|trade|world|all]" + ); + c->Message(Chat::White, "- Displays a portion of the targeted user's inventory"); + c->Message(Chat::White, "- Caution: 'all' is a lot of information!"); + return; + } + + Client *targetClient = c; + if (c->GetTarget()) { + targetClient = c->GetTarget()->CastToClient(); + } + + const EQ::ItemInstance *inst_main = nullptr; + const EQ::ItemInstance *inst_sub = nullptr; + const EQ::ItemInstance *inst_aug = nullptr; + const EQ::ItemData *item_data = nullptr; + + EQ::SayLinkEngine linker; + linker.SetLinkType(EQ::saylink::SayLinkItemInst); + + c->Message(Chat::White, "Displaying inventory for %s...", targetClient->GetName()); + + Object *objectTradeskill = targetClient->GetTradeskillObject(); + + bool itemsFound = false; + + for (int scopeIndex = 0, scopeBit = peekEquip; scopeBit < peekOutOfScope; ++scopeIndex, scopeBit <<= 1) { + if (scopeBit & ~scopeMask) { + continue; + } + + if (scopeBit & peekWorld) { + if (objectTradeskill == nullptr) { + c->Message(Chat::Default, "No world tradeskill object selected..."); + continue; + } + else { + c->Message( + Chat::White, + "[WorldObject DBID: %i (entityid: %i)]", + objectTradeskill->GetDBID(), + objectTradeskill->GetID()); + } + } + + for (int16 indexMain = scope_range[scopeIndex][0]; indexMain <= scope_range[scopeIndex][1]; ++indexMain) { + if (indexMain == EQ::invslot::SLOT_INVALID) { + continue; + } + + inst_main = ((scopeBit & peekWorld) ? objectTradeskill->GetItem(indexMain) : targetClient->GetInv().GetItem( + indexMain + )); + if (inst_main) { + itemsFound = true; + item_data = inst_main->GetItem(); + } + else { + item_data = nullptr; + } + + linker.SetItemInst(inst_main); + + c->Message( + (item_data == nullptr), + "%sSlot: %i, Item: %i (%s), Charges: %i", + scope_prefix[scopeIndex], + ((scopeBit & peekWorld) ? (EQ::invslot::WORLD_BEGIN + indexMain) : indexMain), + ((item_data == nullptr) ? 0 : item_data->ID), + linker.GenerateLink().c_str(), + ((inst_main == nullptr) ? 0 : inst_main->GetCharges()) + ); + + if (inst_main && inst_main->IsClassCommon()) { + for (uint8 indexAug = EQ::invaug::SOCKET_BEGIN; indexAug <= EQ::invaug::SOCKET_END; ++indexAug) { + inst_aug = inst_main->GetItem(indexAug); + if (!inst_aug) { // extant only + continue; + } + + item_data = inst_aug->GetItem(); + linker.SetItemInst(inst_aug); + + c->Message( + (item_data == nullptr), + ".%sAugSlot: %i (Slot #%i, Aug idx #%i), Item: %i (%s), Charges: %i", + scope_prefix[scopeIndex], + INVALID_INDEX, + ((scopeBit & peekWorld) ? (EQ::invslot::WORLD_BEGIN + indexMain) : indexMain), + indexAug, + ((item_data == nullptr) ? 0 : item_data->ID), + linker.GenerateLink().c_str(), + ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()) + ); + } + } + + if (!scope_bag[scopeIndex] || !(inst_main && inst_main->IsClassBag())) { + continue; + } + + for (uint8 indexSub = EQ::invbag::SLOT_BEGIN; indexSub <= EQ::invbag::SLOT_END; ++indexSub) { + inst_sub = inst_main->GetItem(indexSub); + if (!inst_sub) { // extant only + continue; + } + + item_data = inst_sub->GetItem(); + linker.SetItemInst(inst_sub); + + c->Message( + (item_data == nullptr), + "..%sBagSlot: %i (Slot #%i, Bag idx #%i), Item: %i (%s), Charges: %i", + scope_prefix[scopeIndex], + ((scopeBit & peekWorld) ? INVALID_INDEX : EQ::InventoryProfile::CalcSlotId(indexMain, indexSub)), + ((scopeBit & peekWorld) ? (EQ::invslot::WORLD_BEGIN + indexMain) : indexMain), + indexSub, + ((item_data == nullptr) ? 0 : item_data->ID), + linker.GenerateLink().c_str(), + ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()) + ); + + if (inst_sub->IsClassCommon()) { + for (uint8 indexAug = EQ::invaug::SOCKET_BEGIN; indexAug <= EQ::invaug::SOCKET_END; ++indexAug) { + inst_aug = inst_sub->GetItem(indexAug); + if (!inst_aug) { // extant only + continue; + } + + item_data = inst_aug->GetItem(); + linker.SetItemInst(inst_aug); + + c->Message( + (item_data == nullptr), + "...%sAugSlot: %i (Slot #%i, Sub idx #%i, Aug idx #%i), Item: %i (%s), Charges: %i", + scope_prefix[scopeIndex], + INVALID_INDEX, + ((scopeBit & peekWorld) ? INVALID_INDEX : EQ::InventoryProfile::CalcSlotId( + indexMain, + indexSub + )), + indexSub, + indexAug, + ((item_data == nullptr) ? 0 : item_data->ID), + linker.GenerateLink().c_str(), + ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()) + ); + } + } + } + } + + if (scopeBit & peekLimbo) { + int limboIndex = 0; + for (auto it = targetClient->GetInv().cursor_cbegin(); + (it != targetClient->GetInv().cursor_cend()); + ++it, ++limboIndex) { + if (it == targetClient->GetInv().cursor_cbegin()) { + continue; + } + + inst_main = *it; + if (inst_main) { + itemsFound = true; + item_data = inst_main->GetItem(); + } + else { + item_data = nullptr; + } + + linker.SetItemInst(inst_main); + + c->Message( + (item_data == nullptr), + "%sSlot: %i, Item: %i (%s), Charges: %i", + scope_prefix[scopeIndex], + (8000 + limboIndex), + ((item_data == nullptr) ? 0 : item_data->ID), + linker.GenerateLink().c_str(), + ((inst_main == nullptr) ? 0 : inst_main->GetCharges()) + ); + + if (inst_main && inst_main->IsClassCommon()) { + for (uint8 indexAug = EQ::invaug::SOCKET_BEGIN; indexAug <= EQ::invaug::SOCKET_END; ++indexAug) { + inst_aug = inst_main->GetItem(indexAug); + if (!inst_aug) { // extant only + continue; + } + + item_data = inst_aug->GetItem(); + linker.SetItemInst(inst_aug); + + c->Message( + (item_data == nullptr), + ".%sAugSlot: %i (Slot #%i, Aug idx #%i), Item: %i (%s), Charges: %i", + scope_prefix[scopeIndex], + INVALID_INDEX, + (8000 + limboIndex), + indexAug, + ((item_data == nullptr) ? 0 : item_data->ID), + linker.GenerateLink().c_str(), + ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()) + ); + } + } + + if (!scope_bag[scopeIndex] || !(inst_main && inst_main->IsClassBag())) { + continue; + } + + for (uint8 indexSub = EQ::invbag::SLOT_BEGIN; indexSub <= EQ::invbag::SLOT_END; ++indexSub) { + inst_sub = inst_main->GetItem(indexSub); + if (!inst_sub) { + continue; + } + + item_data = (inst_sub == nullptr) ? nullptr : inst_sub->GetItem(); + + linker.SetItemInst(inst_sub); + + c->Message( + (item_data == nullptr), + "..%sBagSlot: %i (Slot #%i, Bag idx #%i), Item: %i (%s), Charges: %i", + scope_prefix[scopeIndex], + INVALID_INDEX, + (8000 + limboIndex), + indexSub, + ((item_data == nullptr) ? 0 : item_data->ID), + linker.GenerateLink().c_str(), + ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()) + ); + + if (inst_sub->IsClassCommon()) { + for (uint8 indexAug = EQ::invaug::SOCKET_BEGIN; + indexAug <= EQ::invaug::SOCKET_END; + ++indexAug) { + inst_aug = inst_sub->GetItem(indexAug); + if (!inst_aug) { // extant only + continue; + } + + item_data = inst_aug->GetItem(); + linker.SetItemInst(inst_aug); + + c->Message( + (item_data == nullptr), + "...%sAugSlot: %i (Slot #%i, Sub idx #%i, Aug idx #%i), Item: %i (%s), Charges: %i", + scope_prefix[scopeIndex], + INVALID_INDEX, + (8000 + limboIndex), + indexSub, + indexAug, + ((item_data == nullptr) ? 0 : item_data->ID), + linker.GenerateLink().c_str(), + ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()) + ); + } + } + } + } + } + } + + if (!itemsFound) { + c->Message(Chat::White, "No items found."); + } +} + diff --git a/zone/gm_commands/peqzone.cpp b/zone/gm_commands/peqzone.cpp new file mode 100755 index 000000000..704e6dd1d --- /dev/null +++ b/zone/gm_commands/peqzone.cpp @@ -0,0 +1,75 @@ +#include "../client.h" + +void command_peqzone(Client *c, const Seperator *sep) +{ + uint32 timeleft = c->GetPTimers().GetRemainingTime(pTimerPeqzoneReuse) / 60; + + if (!c->GetPTimers().Expired(&database, pTimerPeqzoneReuse, false)) { + c->Message(Chat::Red, "You must wait %i minute(s) before using this ability again.", timeleft); + return; + } + + if (c->GetHPRatio() < 75) { + c->Message(Chat::White, "You cannot use this command with less than 75 percent health."); + return; + } + + //this isnt perfect, but its better... + if ( + c->IsInvisible(c) + || c->IsRooted() + || c->IsStunned() + || c->IsMezzed() + || c->AutoAttackEnabled() + || c->GetInvul() + ) { + c->Message(Chat::White, "You cannot use this command in your current state. Settle down and wait."); + return; + } + + uint16 zoneid = 0; + uint8 destzone = 0; + if (sep->IsNumber(1)) { + zoneid = atoi(sep->arg[1]); + destzone = content_db.GetPEQZone(zoneid, 0); + if (destzone == 0) { + c->Message(Chat::Red, "You cannot use this command to enter that zone!"); + return; + } + if (zoneid == zone->GetZoneID()) { + c->Message(Chat::Red, "You cannot use this command on the zone you are in!"); + return; + } + } + else if (sep->arg[1][0] == 0 || sep->IsNumber(2) || sep->IsNumber(3) || sep->IsNumber(4) || sep->IsNumber(5)) { + c->Message(Chat::White, "Usage: #peqzone [zonename]"); + c->Message(Chat::White, "Optional Usage: #peqzone [zoneid]"); + return; + } + else { + zoneid = ZoneID(sep->arg[1]); + destzone = content_db.GetPEQZone(zoneid, 0); + if (zoneid == 0) { + c->Message(Chat::White, "Unable to locate zone '%s'", sep->arg[1]); + return; + } + if (destzone == 0) { + c->Message(Chat::Red, "You cannot use this command to enter that zone!"); + return; + } + if (zoneid == zone->GetZoneID()) { + c->Message(Chat::Red, "You cannot use this command on the zone you are in!"); + return; + } + } + + if (RuleB (Zone, UsePEQZoneDebuffs)) { + c->SpellOnTarget(RuleI(Zone, PEQZoneDebuff1), c); + c->SpellOnTarget(RuleI(Zone, PEQZoneDebuff2), c); + } + + //zone to safe coords + c->GetPTimers().Start(pTimerPeqzoneReuse, RuleI(Zone, PEQZoneReuseTime)); + c->MovePC(zoneid, 0.0f, 0.0f, 0.0f, 0.0f, 0, ZoneToSafeCoords); +} + diff --git a/zone/gm_commands/permaclass.cpp b/zone/gm_commands/permaclass.cpp new file mode 100755 index 000000000..72c69b80f --- /dev/null +++ b/zone/gm_commands/permaclass.cpp @@ -0,0 +1,28 @@ +#include "../client.h" + +void command_permaclass(Client *c, const Seperator *sep) +{ + Client *t = c; + + if (c->GetTarget() && c->GetTarget()->IsClient()) { + t = c->GetTarget()->CastToClient(); + } + + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #permaclass "); + } + else if (!t->IsClient()) { + c->Message(Chat::White, "Target is not a client."); + } + else { + c->Message(Chat::White, "Setting %s's class...Sending to char select.", t->GetName()); + LogInfo("Class change request from [{}] for [{}], requested class:[{}]", + c->GetName(), + t->GetName(), + atoi(sep->arg[1])); + t->SetBaseClass(atoi(sep->arg[1])); + t->Save(); + t->Kick("Class was changed."); + } +} + diff --git a/zone/gm_commands/permagender.cpp b/zone/gm_commands/permagender.cpp new file mode 100755 index 000000000..ee0c7ecf9 --- /dev/null +++ b/zone/gm_commands/permagender.cpp @@ -0,0 +1,29 @@ +#include "../client.h" + +void command_permagender(Client *c, const Seperator *sep) +{ + Client *t = c; + + if (c->GetTarget() && c->GetTarget()->IsClient()) { + t = c->GetTarget()->CastToClient(); + } + + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #permagender "); + c->Message(Chat::White, "Gender Numbers: 0=Male, 1=Female, 2=Neuter"); + } + else if (!t->IsClient()) { + c->Message(Chat::White, "Target is not a client."); + } + else { + c->Message(Chat::White, "Setting %s's gender - zone to take effect", t->GetName()); + LogInfo("Permanant gender change request from [{}] for [{}], requested gender:[{}]", + c->GetName(), + t->GetName(), + atoi(sep->arg[1])); + t->SetBaseGender(atoi(sep->arg[1])); + t->Save(); + t->SendIllusionPacket(atoi(sep->arg[1])); + } +} + diff --git a/zone/gm_commands/permarace.cpp b/zone/gm_commands/permarace.cpp new file mode 100755 index 000000000..ac33c8605 --- /dev/null +++ b/zone/gm_commands/permarace.cpp @@ -0,0 +1,34 @@ +#include "../client.h" + +void command_permarace(Client *c, const Seperator *sep) +{ + Client *t = c; + + if (c->GetTarget() && c->GetTarget()->IsClient()) { + t = c->GetTarget()->CastToClient(); + } + + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #permarace "); + c->Message( + Chat::White, + "NOTE: Not all models are global. If a model is not global, it will appear as a human on character select and in zones without the model." + ); + } + else if (!t->IsClient()) { + c->Message(Chat::White, "Target is not a client."); + } + else { + c->Message(Chat::White, "Setting %s's race - zone to take effect", t->GetName()); + LogInfo("Permanant race change request from [{}] for [{}], requested race:[{}]", + c->GetName(), + t->GetName(), + atoi(sep->arg[1])); + uint32 tmp = Mob::GetDefaultGender(atoi(sep->arg[1]), t->GetBaseGender()); + t->SetBaseRace(atoi(sep->arg[1])); + t->SetBaseGender(tmp); + t->Save(); + t->SendIllusionPacket(atoi(sep->arg[1])); + } +} + diff --git a/zone/gm_commands/petitioninfo.cpp b/zone/gm_commands/petitioninfo.cpp new file mode 100755 index 000000000..1700e4100 --- /dev/null +++ b/zone/gm_commands/petitioninfo.cpp @@ -0,0 +1,39 @@ +#include "../client.h" + +void command_petitioninfo(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #petitioninfo (petition number) Type #listpetition for a list"); + return; + } + + std::string query = "SELECT petid, charname, accountname, zone, charclass, charrace, charlevel FROM petitions ORDER BY petid"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { + return; + } + + LogInfo("Petition information request from [{}], petition number:", c->GetName(), atoi(sep->argplus[1])); + + if (results.RowCount() == 0) { + c->Message(Chat::Red, "There was an error in your request: ID not found! Please check the Id and try again."); + return; + } + + for (auto row = results.begin(); row != results.end(); ++row) + if (strcasecmp(row[0], sep->argplus[1]) == 0) { + c->Message( + Chat::Red, + " ID : %s Character Name: %s Account Name: %s Zone: %s Character Class: %s Character Race: %s Character Level: %s", + row[0], + row[1], + row[2], + row[3], + row[4], + row[5], + row[6] + ); + } + +} + diff --git a/zone/gm_commands/petname.cpp b/zone/gm_commands/petname.cpp new file mode 100755 index 000000000..47eea80c3 --- /dev/null +++ b/zone/gm_commands/petname.cpp @@ -0,0 +1,22 @@ +#include "../client.h" + +void command_petname(Client *c, const Seperator *sep) +{ + Mob *target; + target = c->GetTarget(); + + if (!target) { + c->Message(Chat::White, "Usage: #petname newname (requires a target)"); + } + else if (target->IsPet() && (target->GetOwnerID() == c->GetID()) && strlen(sep->arg[1]) > 0) { + char *oldname = strdup(target->GetName()); + target->TempName(sep->arg[1]); + c->Message(Chat::White, "Renamed %s to %s", oldname, sep->arg[1]); + free(oldname); + } + else { + target->TempName(); + c->Message(Chat::White, "Restored the original name"); + } +} + diff --git a/zone/gm_commands/pf.cpp b/zone/gm_commands/pf.cpp new file mode 100755 index 000000000..5867e346b --- /dev/null +++ b/zone/gm_commands/pf.cpp @@ -0,0 +1,21 @@ +#include "../client.h" + +void command_pf(Client *c, const Seperator *sep) +{ + if (c->GetTarget()) { + Mob *who = c->GetTarget(); + c->Message(Chat::White, "POS: (%.2f, %.2f, %.2f)", who->GetX(), who->GetY(), who->GetZ()); + c->Message( + Chat::White, + "WP: %s (%d/%d)", + to_string(who->GetCurrentWayPoint()).c_str(), + who->IsNPC() ? who->CastToNPC()->GetMaxWp() : -1 + ); + c->Message(Chat::White, "pause=%d RAspeed=%d", who->GetCWPP(), who->GetRunAnimSpeed()); + //who->DumpMovement(c); + } + else { + c->Message(Chat::White, "ERROR: target required"); + } +} + diff --git a/zone/gm_commands/picklock.cpp b/zone/gm_commands/picklock.cpp new file mode 100755 index 000000000..c4b538eff --- /dev/null +++ b/zone/gm_commands/picklock.cpp @@ -0,0 +1,24 @@ +#include "../client.h" + +void command_picklock(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!target) { + c->Message(Chat::Red, "You must have a target."); + return; + } + + if (target->IsNPC()) { + if (c->HasSkill(EQ::skills::SkillPickLock)) { + if (DistanceSquaredNoZ(c->GetPosition(), target->GetPosition()) > RuleI(Adventure, LDoNTrapDistanceUse)) { + c->Message(Chat::Red, "%s is too far away.", target->GetCleanName()); + return; + } + c->HandleLDoNPickLock(target->CastToNPC(), c->GetSkill(EQ::skills::SkillPickLock), LDoNTypeMechanical); + } + else { + c->Message(Chat::Red, "You do not have the pick locks skill."); + } + } +} + diff --git a/zone/gm_commands/profanity.cpp b/zone/gm_commands/profanity.cpp new file mode 100755 index 000000000..6774c43c6 --- /dev/null +++ b/zone/gm_commands/profanity.cpp @@ -0,0 +1,74 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +#include "../../common/profanity_manager.h" + +void command_profanity(Client *c, const Seperator *sep) +{ + std::string arg1(sep->arg[1]); + + while (true) { + if (arg1.compare("list") == 0) { + // do nothing + } + else if (arg1.compare("clear") == 0) { + EQ::ProfanityManager::DeleteProfanityList(&database); + auto pack = new ServerPacket(ServerOP_RefreshCensorship); + worldserver.SendPacket(pack); + safe_delete(pack); + } + else if (arg1.compare("add") == 0) { + if (!EQ::ProfanityManager::AddProfanity(&database, sep->arg[2])) { + c->Message(Chat::Red, "Could not add '%s' to the profanity list.", sep->arg[2]); + } + auto pack = new ServerPacket(ServerOP_RefreshCensorship); + worldserver.SendPacket(pack); + safe_delete(pack); + } + else if (arg1.compare("del") == 0) { + if (!EQ::ProfanityManager::RemoveProfanity(&database, sep->arg[2])) { + c->Message(Chat::Red, "Could not delete '%s' from the profanity list.", sep->arg[2]); + } + auto pack = new ServerPacket(ServerOP_RefreshCensorship); + worldserver.SendPacket(pack); + safe_delete(pack); + } + else if (arg1.compare("reload") == 0) { + if (!EQ::ProfanityManager::UpdateProfanityList(&database)) { + c->Message(Chat::Red, "Could not reload the profanity list."); + } + auto pack = new ServerPacket(ServerOP_RefreshCensorship); + worldserver.SendPacket(pack); + safe_delete(pack); + } + else { + break; + } + + std::string popup; + const auto &list = EQ::ProfanityManager::GetProfanityList(); + for (const auto &iter : list) { + popup.append(iter); + popup.append("
"); + } + if (list.empty()) { + popup.append("** Censorship Inactive **
"); + } + else { + popup.append("** End of List **
"); + } + + c->SendPopupToClient("Profanity List", popup.c_str()); + + return; + } + + c->Message(Chat::White, "Usage: #profanity [list] - shows profanity list"); + c->Message(Chat::White, "Usage: #profanity [clear] - deletes all entries"); + c->Message(Chat::White, "Usage: #profanity [add] [] - adds entry"); + c->Message(Chat::White, "Usage: #profanity [del] [] - deletes entry"); + c->Message(Chat::White, "Usage: #profanity [reload] - reloads profanity list"); +} + diff --git a/zone/gm_commands/profilereset.cpp b/zone/gm_commands/profilereset.cpp new file mode 100755 index 000000000..0f98dae23 --- /dev/null +++ b/zone/gm_commands/profilereset.cpp @@ -0,0 +1,8 @@ +#include "../client.h" + +void command_profilereset(Client *c, const Seperator *sep) +{ + ResetZoneProfile(); +} +#endif + diff --git a/zone/gm_commands/proximity.cpp b/zone/gm_commands/proximity.cpp new file mode 100755 index 000000000..3db0a714e --- /dev/null +++ b/zone/gm_commands/proximity.cpp @@ -0,0 +1,74 @@ +#include "../client.h" + +void command_proximity(Client *c, const Seperator *sep) +{ + if (!c->GetTarget() || (c->GetTarget() && !c->GetTarget()->IsNPC())) { + c->Message(Chat::White, "You must target an NPC"); + return; + } + + for (auto &iter : entity_list.GetNPCList()) { + auto npc = iter.second; + std::string name = npc->GetName(); + + if (name.find("Proximity") != std::string::npos) { + npc->Depop(); + } + } + + NPC *npc = c->GetTarget()->CastToNPC(); + + std::vector points; + + FindPerson_Point p{}; + + if (npc->IsProximitySet()) { + glm::vec4 position; + position.w = npc->GetHeading(); + position.x = npc->GetProximityMinX(); + position.y = npc->GetProximityMinY(); + position.z = npc->GetZ(); + + position.x = npc->GetProximityMinX(); + position.y = npc->GetProximityMinY(); + NPC::SpawnNodeNPC("Proximity", "", position); + + position.x = npc->GetProximityMinX(); + position.y = npc->GetProximityMaxY(); + NPC::SpawnNodeNPC("Proximity", "", position); + + position.x = npc->GetProximityMaxX(); + position.y = npc->GetProximityMinY(); + NPC::SpawnNodeNPC("Proximity", "", position); + + position.x = npc->GetProximityMaxX(); + position.y = npc->GetProximityMaxY(); + NPC::SpawnNodeNPC("Proximity", "", position); + + p.x = npc->GetProximityMinX(); + p.y = npc->GetProximityMinY(); + p.z = npc->GetZ(); + points.push_back(p); + + p.x = npc->GetProximityMinX(); + p.y = npc->GetProximityMaxY(); + points.push_back(p); + + p.x = npc->GetProximityMaxX(); + p.y = npc->GetProximityMaxY(); + points.push_back(p); + + p.x = npc->GetProximityMaxX(); + p.y = npc->GetProximityMinY(); + points.push_back(p); + + p.x = npc->GetProximityMinX(); + p.y = npc->GetProximityMinY(); + points.push_back(p); + } + + if (c->ClientVersion() >= EQ::versions::ClientVersion::RoF) { + c->SendPathPacket(points); + } +} + diff --git a/zone/gm_commands/push.cpp b/zone/gm_commands/push.cpp new file mode 100755 index 000000000..6ca548640 --- /dev/null +++ b/zone/gm_commands/push.cpp @@ -0,0 +1,35 @@ +#include "../client.h" +#include "../fastmath.h" + +extern FastMath g_Math; + +void command_push(Client *c, const Seperator *sep) +{ + Mob *t = c; + if (c->GetTarget() != nullptr) { + t = c->GetTarget(); + } + + if (!sep->arg[1] || !sep->IsNumber(1)) { + c->Message(Chat::White, "ERROR: Must provide at least a push back."); + return; + } + + float back = atof(sep->arg[1]); + float up = 0.0f; + + if (sep->arg[2] && sep->IsNumber(2)) { + up = atof(sep->arg[2]); + } + + if (t->IsNPC()) { + t->IncDeltaX(back * g_Math.FastSin(c->GetHeading())); + t->IncDeltaY(back * g_Math.FastCos(c->GetHeading())); + t->IncDeltaZ(up); + t->SetForcedMovement(6); + } + else if (t->IsClient()) { + // TODO: send packet to push + } +} + diff --git a/zone/gm_commands/pvp.cpp b/zone/gm_commands/pvp.cpp new file mode 100755 index 000000000..1f31f29b9 --- /dev/null +++ b/zone/gm_commands/pvp.cpp @@ -0,0 +1,20 @@ +#include "../client.h" + +void command_pvp(Client *c, const Seperator *sep) +{ + bool state = atobool(sep->arg[1]); + Client *t = c; + + if (c->GetTarget() && c->GetTarget()->IsClient()) { + t = c->GetTarget()->CastToClient(); + } + + if (sep->arg[1][0] != 0) { + t->SetPVP(state); + c->Message(Chat::White, "%s now follows the ways of %s.", t->GetName(), state ? "discord" : "order"); + } + else { + c->Message(Chat::White, "Usage: #pvp [on/off]"); + } +} + diff --git a/zone/gm_commands/qglobal.cpp b/zone/gm_commands/qglobal.cpp new file mode 100755 index 000000000..7625a2604 --- /dev/null +++ b/zone/gm_commands/qglobal.cpp @@ -0,0 +1,62 @@ +#include "../client.h" + +void command_qglobal(Client *c, const Seperator *sep) +{ + //In-game switch for qglobal column + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Syntax: #qglobal [on/off/view]. Requires NPC target."); + return; + } + + Mob *target = c->GetTarget(); + + if (!target || !target->IsNPC()) { + c->Message(Chat::Red, "NPC Target Required!"); + return; + } + + if (!strcasecmp(sep->arg[1], "on")) { + std::string query = StringFormat( + "UPDATE npc_types SET qglobal = 1 WHERE id = '%i'", + target->GetNPCTypeID()); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::Yellow, "Could not update database."); + return; + } + + c->Message(Chat::Yellow, "Success! Changes take effect on zone reboot."); + return; + } + + if (!strcasecmp(sep->arg[1], "off")) { + std::string query = StringFormat( + "UPDATE npc_types SET qglobal = 0 WHERE id = '%i'", + target->GetNPCTypeID()); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::Yellow, "Could not update database."); + return; + } + + c->Message(Chat::Yellow, "Success! Changes take effect on zone reboot."); + return; + } + + if (!strcasecmp(sep->arg[1], "view")) { + const NPCType *type = content_db.LoadNPCTypesData(target->GetNPCTypeID()); + if (!type) { + c->Message(Chat::Yellow, "Invalid NPC type."); + } + else if (type->qglobal) { + c->Message(Chat::Yellow, "This NPC has quest globals active."); + } + else { + c->Message(Chat::Yellow, "This NPC has quest globals disabled."); + } + return; + } + + c->Message(Chat::Yellow, "Invalid action specified."); +} + diff --git a/zone/gm_commands/questerrors.cpp b/zone/gm_commands/questerrors.cpp new file mode 100755 index 000000000..cbc35e599 --- /dev/null +++ b/zone/gm_commands/questerrors.cpp @@ -0,0 +1,23 @@ +#include "../client.h" +#include "../quest_parser_collection.h" + +void command_questerrors(Client *c, const Seperator *sep) +{ + std::list err; + parse->GetErrors(err); + c->Message(Chat::White, "Current Quest Errors:"); + + auto iter = err.begin(); + int i = 0; + while (iter != err.end()) { + if (i >= 30) { + c->Message(Chat::White, "Maximum of 30 Errors shown..."); + break; + } + + c->Message(Chat::White, iter->c_str()); + ++i; + ++iter; + } +} + diff --git a/zone/gm_commands/race.cpp b/zone/gm_commands/race.cpp new file mode 100755 index 000000000..2729965b1 --- /dev/null +++ b/zone/gm_commands/race.cpp @@ -0,0 +1,29 @@ +#include "../client.h" + +void command_race(Client *c, const Seperator *sep) +{ + Mob *target = c->CastToMob(); + + if (sep->IsNumber(1)) { + auto race = atoi(sep->arg[1]); + if ((race >= 0 && race <= RuleI(NPC, MaxRaceID)) || (race >= 2253 && race <= 2259)) { + if ((c->GetTarget()) && c->Admin() >= commandRaceOthers) { + target = c->GetTarget(); + } + target->SendIllusionPacket(race); + } + else { + c->Message( + Chat::White, + fmt::format( + "Usage: #race [0-{}, 2253-2259] (0 for back to normal)", + RuleI(NPC, MaxRaceID)).c_str()); + } + } + else { + c->Message( + Chat::White, + fmt::format("Usage: #race [0-{}, 2253-2259] (0 for back to normal)", RuleI(NPC, MaxRaceID)).c_str()); + } +} + diff --git a/zone/gm_commands/raidloot.cpp b/zone/gm_commands/raidloot.cpp new file mode 100755 index 000000000..9dfb1c21c --- /dev/null +++ b/zone/gm_commands/raidloot.cpp @@ -0,0 +1,70 @@ +#include "../client.h" +#include "../groups.h" +#include "../raids.h" +#include "../raids.h" + +void command_raidloot(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #raidloot [All|GroupLeader|RaidLeader|Selected]"); + return; + } + + auto client_raid = c->GetRaid(); + if (!client_raid) { + c->Message(Chat::White, "You must be in a Raid to use this command."); + return; + } + + if (!client_raid->IsLeader(c)) { + c->Message(Chat::White, "You must be the Raid Leader to use this command."); + return; + } + + std::string raid_loot_type = str_tolower(sep->arg[1]); + bool is_all = raid_loot_type.find("all") != std::string::npos; + bool is_group_leader = raid_loot_type.find("groupleader") != std::string::npos; + bool is_raid_leader = raid_loot_type.find("raidleader") != std::string::npos; + bool is_selected = raid_loot_type.find("selected") != std::string::npos; + if ( + !is_all && + !is_group_leader && + !is_raid_leader && + !is_selected + ) { + c->Message(Chat::White, "Usage: #raidloot [All|GroupLeader|RaidLeader|Selected]"); + return; + } + + std::map loot_types = { + {RaidLootTypes::All, "All"}, + {RaidLootTypes::GroupLeader, "GroupLeader"}, + {RaidLootTypes::RaidLeader, "RaidLeader"}, + {RaidLootTypes::Selected, "Selected"} + }; + + uint32 loot_type; + if (is_all) { + loot_type = RaidLootTypes::All; + } + else if (is_group_leader) { + loot_type = RaidLootTypes::GroupLeader; + } + else if (is_raid_leader) { + loot_type = RaidLootTypes::RaidLeader; + } + else if (is_selected) { + loot_type = RaidLootTypes::Selected; + } + + c->Message( + Chat::White, + fmt::format( + "Loot type changed to {} ({}).", + loot_types[loot_type], + loot_type + ).c_str() + ); +} + diff --git a/zone/gm_commands/randomfeatures.cpp b/zone/gm_commands/randomfeatures.cpp new file mode 100755 index 000000000..9b2c85c10 --- /dev/null +++ b/zone/gm_commands/randomfeatures.cpp @@ -0,0 +1,18 @@ +#include "../client.h" + +void command_randomfeatures(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!target) { + c->Message(Chat::White, "Error: This command requires a target"); + } + else { + if (target->RandomizeFeatures()) { + c->Message(Chat::White, "Features Randomized"); + } + else { + c->Message(Chat::White, "This command requires a Playable Race as the target"); + } + } +} + diff --git a/zone/gm_commands/refreshgroup.cpp b/zone/gm_commands/refreshgroup.cpp new file mode 100755 index 000000000..243997141 --- /dev/null +++ b/zone/gm_commands/refreshgroup.cpp @@ -0,0 +1,19 @@ +#include "../client.h" +#include "../groups.h" + +void command_refreshgroup(Client *c, const Seperator *sep) +{ + if (!c) { + return; + } + + Group *g = c->GetGroup(); + + if (!g) { + return; + } + + database.RefreshGroupFromDB(c); + //g->SendUpdate(7, c); +} + diff --git a/zone/gm_commands/reloadaa.cpp b/zone/gm_commands/reloadaa.cpp new file mode 100755 index 000000000..5df40b105 --- /dev/null +++ b/zone/gm_commands/reloadaa.cpp @@ -0,0 +1,17 @@ +#include "../client.h" +#include "../../common/file_util.h" + +void command_reloadaa(Client *c, const Seperator *sep) +{ + c->Message(Chat::White, "Reloading Alternate Advancement Data..."); + zone->LoadAlternateAdvancement(); + c->Message(Chat::White, "Alternate Advancement Data Reloaded"); + entity_list.SendAlternateAdvancementStats(); +} + +inline bool file_exists(const std::string &name) +{ + std::ifstream f(name.c_str()); + return f.good(); +} + diff --git a/zone/gm_commands/reloadallrules.cpp b/zone/gm_commands/reloadallrules.cpp new file mode 100755 index 000000000..d5fb8b757 --- /dev/null +++ b/zone/gm_commands/reloadallrules.cpp @@ -0,0 +1,16 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_reloadallrules(Client *c, const Seperator *sep) +{ + if (c) { + auto pack = new ServerPacket(ServerOP_ReloadRules, 0); + worldserver.SendPacket(pack); + c->Message(Chat::Red, "Successfully sent the packet to world to reload rules globally. (including world)"); + safe_delete(pack); + + } +} + diff --git a/zone/gm_commands/reloademote.cpp b/zone/gm_commands/reloademote.cpp new file mode 100755 index 000000000..3003776e4 --- /dev/null +++ b/zone/gm_commands/reloademote.cpp @@ -0,0 +1,9 @@ +#include "../client.h" + +void command_reloademote(Client *c, const Seperator *sep) +{ + zone->NPCEmoteList.Clear(); + zone->LoadNPCEmotes(&zone->NPCEmoteList); + c->Message(Chat::White, "NPC emotes reloaded."); +} + diff --git a/zone/gm_commands/reloadlevelmods.cpp b/zone/gm_commands/reloadlevelmods.cpp new file mode 100755 index 000000000..29ce28c11 --- /dev/null +++ b/zone/gm_commands/reloadlevelmods.cpp @@ -0,0 +1,15 @@ +#include "../client.h" + +void command_reloadlevelmods(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + if (RuleB(Zone, LevelBasedEXPMods)) { + zone->LoadLevelEXPMods(); + c->Message(Chat::Yellow, "Level based EXP Mods have been reloaded zonewide"); + } + else { + c->Message(Chat::Yellow, "Level based EXP Mods are disabled in rules!"); + } + } +} + diff --git a/zone/gm_commands/reloadmerchants.cpp b/zone/gm_commands/reloadmerchants.cpp new file mode 100755 index 000000000..560551682 --- /dev/null +++ b/zone/gm_commands/reloadmerchants.cpp @@ -0,0 +1,8 @@ +#include "../client.h" + +void command_reloadmerchants(Client *c, const Seperator *sep) +{ + entity_list.ReloadMerchants(); + c->Message(Chat::Yellow, "Reloading merchants."); +} + diff --git a/zone/gm_commands/reloadperlexportsettings.cpp b/zone/gm_commands/reloadperlexportsettings.cpp new file mode 100755 index 000000000..1c3137728 --- /dev/null +++ b/zone/gm_commands/reloadperlexportsettings.cpp @@ -0,0 +1,16 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_reloadperlexportsettings(Client *c, const Seperator *sep) +{ + if (c) { + auto pack = new ServerPacket(ServerOP_ReloadPerlExportSettings, 0); + worldserver.SendPacket(pack); + c->Message(Chat::Red, "Successfully sent the packet to world to reload Perl Export settings"); + safe_delete(pack); + + } +} + diff --git a/zone/gm_commands/reloadqst.cpp b/zone/gm_commands/reloadqst.cpp new file mode 100755 index 000000000..48909377a --- /dev/null +++ b/zone/gm_commands/reloadqst.cpp @@ -0,0 +1,23 @@ +#include "../client.h" +#include "../quest_parser_collection.h" + +void command_reloadqst(Client *c, const Seperator *sep) +{ + bool stop_timers = false; + + if (sep->IsNumber(1)) { + stop_timers = std::stoi(sep->arg[1]) != 0 ? true : false; + } + + std::string stop_timers_message = stop_timers ? " and stopping timers" : ""; + c->Message( + Chat::White, + fmt::format( + "Clearing quest memory cache{}.", + stop_timers_message + ).c_str() + ); + entity_list.ClearAreas(); + parse->ReloadQuests(stop_timers); +} + diff --git a/zone/gm_commands/reloadstatic.cpp b/zone/gm_commands/reloadstatic.cpp new file mode 100755 index 000000000..39666f724 --- /dev/null +++ b/zone/gm_commands/reloadstatic.cpp @@ -0,0 +1,8 @@ +#include "../client.h" + +void command_reloadstatic(Client *c, const Seperator *sep) +{ + c->Message(Chat::White, "Reloading zone static data..."); + zone->ReloadStaticData(); +} + diff --git a/zone/gm_commands/reloadtitles.cpp b/zone/gm_commands/reloadtitles.cpp new file mode 100755 index 000000000..f9143b966 --- /dev/null +++ b/zone/gm_commands/reloadtitles.cpp @@ -0,0 +1,14 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_reloadtitles(Client *c, const Seperator *sep) +{ + auto pack = new ServerPacket(ServerOP_ReloadTitles, 0); + worldserver.SendPacket(pack); + safe_delete(pack); + c->Message(Chat::Yellow, "Player Titles Reloaded."); + +} + diff --git a/zone/gm_commands/reloadtraps.cpp b/zone/gm_commands/reloadtraps.cpp new file mode 100755 index 000000000..1057ec771 --- /dev/null +++ b/zone/gm_commands/reloadtraps.cpp @@ -0,0 +1,8 @@ +#include "../client.h" + +void command_reloadtraps(Client *c, const Seperator *sep) +{ + entity_list.UpdateAllTraps(true, true); + c->Message(Chat::Default, "Traps reloaded for %s.", zone->GetShortName()); +} + diff --git a/zone/gm_commands/reloadworld.cpp b/zone/gm_commands/reloadworld.cpp new file mode 100755 index 000000000..f87574d79 --- /dev/null +++ b/zone/gm_commands/reloadworld.cpp @@ -0,0 +1,22 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_reloadworld(Client *c, const Seperator *sep) +{ + int world_repop = atoi(sep->arg[1]); + if (world_repop == 0) { + c->Message(Chat::White, "Reloading quest cache worldwide."); + } + else { + c->Message(Chat::White, "Reloading quest cache and repopping zones worldwide."); + } + + auto pack = new ServerPacket(ServerOP_ReloadWorld, sizeof(ReloadWorld_Struct)); + ReloadWorld_Struct *RW = (ReloadWorld_Struct *) pack->pBuffer; + RW->Option = world_repop; + worldserver.SendPacket(pack); + safe_delete(pack); +} + diff --git a/zone/gm_commands/reloadworldrules.cpp b/zone/gm_commands/reloadworldrules.cpp new file mode 100755 index 000000000..8e1b12ff3 --- /dev/null +++ b/zone/gm_commands/reloadworldrules.cpp @@ -0,0 +1,15 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_reloadworldrules(Client *c, const Seperator *sep) +{ + if (c) { + auto pack = new ServerPacket(ServerOP_ReloadRulesWorld, 0); + worldserver.SendPacket(pack); + c->Message(Chat::Red, "Successfully sent the packet to world to reload rules. (only world)"); + safe_delete(pack); + } +} + diff --git a/zone/gm_commands/reloadzps.cpp b/zone/gm_commands/reloadzps.cpp new file mode 100755 index 000000000..353499d88 --- /dev/null +++ b/zone/gm_commands/reloadzps.cpp @@ -0,0 +1,8 @@ +#include "../client.h" + +void command_reloadzps(Client *c, const Seperator *sep) +{ + content_db.LoadStaticZonePoints(&zone->zone_point_list, zone->GetShortName(), zone->GetInstanceVersion()); + c->Message(Chat::White, "Reloading server zone_points."); +} + diff --git a/zone/gm_commands/repop.cpp b/zone/gm_commands/repop.cpp new file mode 100755 index 000000000..114ef6fa5 --- /dev/null +++ b/zone/gm_commands/repop.cpp @@ -0,0 +1,40 @@ +#include "../client.h" + +void command_repop(Client *c, const Seperator *sep) +{ + int timearg = 1; + int delay = 0; + + if (sep->arg[1] && strcasecmp(sep->arg[1], "force") == 0) { + timearg++; + + LinkedListIterator iterator(zone->spawn2_list); + iterator.Reset(); + while (iterator.MoreElements()) { + std::string query = StringFormat( + "DELETE FROM respawn_times WHERE id = %lu AND instance_id = %lu", + (unsigned long) iterator.GetData()->GetID(), + (unsigned long) zone->GetInstanceID() + ); + auto results = database.QueryDatabase(query); + iterator.Advance(); + } + c->Message(Chat::White, "Zone depop: Force resetting spawn timers."); + } + + if (!sep->IsNumber(timearg)) { + c->Message(Chat::White, "Zone depopped - repopping now."); + + zone->Repop(); + + /* Force a spawn2 timer trigger so we don't delay actually spawning the NPC's */ + zone->spawn2_timer.Trigger(); + return; + } + + c->Message(Chat::White, "Zone depoped. Repop in %i seconds", atoi(sep->arg[timearg])); + zone->Repop(atoi(sep->arg[timearg]) * 1000); + + zone->spawn2_timer.Trigger(); +} + diff --git a/zone/gm_commands/resetaa.cpp b/zone/gm_commands/resetaa.cpp new file mode 100755 index 000000000..d83ba6beb --- /dev/null +++ b/zone/gm_commands/resetaa.cpp @@ -0,0 +1,13 @@ +#include "../client.h" + +void command_resetaa(Client *c, const Seperator *sep) +{ + if (c->GetTarget() && c->GetTarget()->IsClient()) { + c->GetTarget()->CastToClient()->ResetAA(); + c->Message(Chat::Red, "Successfully reset %s's AAs", c->GetTarget()->GetName()); + } + else { + c->Message(Chat::White, "Usage: Target a client and use #resetaa to reset the AA data in their Profile."); + } +} + diff --git a/zone/gm_commands/resetaa_timer.cpp b/zone/gm_commands/resetaa_timer.cpp new file mode 100755 index 000000000..452ed4266 --- /dev/null +++ b/zone/gm_commands/resetaa_timer.cpp @@ -0,0 +1,26 @@ +#include "../client.h" + +void command_resetaa_timer(Client *c, const Seperator *sep) +{ + Client *target = nullptr; + if (!c->GetTarget() || !c->GetTarget()->IsClient()) { + target = c; + } + else { + target = c->GetTarget()->CastToClient(); + } + + if (sep->IsNumber(1)) { + int timer_id = atoi(sep->arg[1]); + c->Message(Chat::White, "Reset of timer %i for %s", timer_id, c->GetName()); + c->ResetAlternateAdvancementTimer(timer_id); + } + else if (!strcasecmp(sep->arg[1], "all")) { + c->Message(Chat::White, "Reset all timers for %s", c->GetName()); + c->ResetAlternateAdvancementTimers(); + } + else { + c->Message(Chat::White, "usage: #resetaa_timer [all | timer_id]"); + } +} + diff --git a/zone/gm_commands/resetdisc_timer.cpp b/zone/gm_commands/resetdisc_timer.cpp new file mode 100755 index 000000000..0c3cfe267 --- /dev/null +++ b/zone/gm_commands/resetdisc_timer.cpp @@ -0,0 +1,23 @@ +#include "../client.h" + +void command_resetdisc_timer(Client *c, const Seperator *sep) +{ + Client *target = c->GetTarget()->CastToClient(); + if (!c->GetTarget() || !c->GetTarget()->IsClient()) { + target = c; + } + + if (sep->IsNumber(1)) { + int timer_id = atoi(sep->arg[1]); + c->Message(Chat::White, "Reset of disc timer %i for %s", timer_id, c->GetName()); + c->ResetDisciplineTimer(timer_id); + } + else if (!strcasecmp(sep->arg[1], "all")) { + c->Message(Chat::White, "Reset all disc timers for %s", c->GetName()); + c->ResetAllDisciplineTimers(); + } + else { + c->Message(Chat::White, "usage: #resetdisc_timer [all | timer_id]"); + } +} + diff --git a/zone/gm_commands/revoke.cpp b/zone/gm_commands/revoke.cpp new file mode 100755 index 000000000..34ee10ee3 --- /dev/null +++ b/zone/gm_commands/revoke.cpp @@ -0,0 +1,48 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_revoke(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0 || sep->arg[2][0] == 0) { + c->Message(Chat::White, "Usage: #revoke [charname] [1/0]"); + return; + } + + uint32 characterID = database.GetAccountIDByChar(sep->arg[1]); + if (characterID == 0) { + c->Message(Chat::Red, "Character does not exist."); + return; + } + + int flag = sep->arg[2][0] == '1' ? true : false; + std::string query = StringFormat("UPDATE account SET revoked = %d WHERE id = %i", flag, characterID); + auto results = database.QueryDatabase(query); + + c->Message( + Chat::Red, + "%s account number %i with the character %s.", + flag ? "Revoking" : "Unrevoking", + characterID, + sep->arg[1] + ); + + Client *revokee = entity_list.GetClientByAccID(characterID); + if (revokee) { + c->Message(Chat::White, "Found %s in this zone.", revokee->GetName()); + revokee->SetRevoked(flag); + return; + } + + c->Message(Chat::Red, "#revoke: Couldn't find %s in this zone, passing request to worldserver.", sep->arg[1]); + + auto outapp = new ServerPacket(ServerOP_Revoke, sizeof(RevokeStruct)); + RevokeStruct *revoke = (RevokeStruct *) outapp->pBuffer; + strn0cpy(revoke->adminname, c->GetName(), 64); + strn0cpy(revoke->name, sep->arg[1], 64); + revoke->toggle = flag; + worldserver.SendPacket(outapp); + safe_delete(outapp); +} + diff --git a/zone/gm_commands/roambox.cpp b/zone/gm_commands/roambox.cpp new file mode 100755 index 000000000..3c31f2312 --- /dev/null +++ b/zone/gm_commands/roambox.cpp @@ -0,0 +1,93 @@ +#include "../client.h" +#include "../groups.h" + +void command_roambox(Client *c, const Seperator *sep) +{ + std::string arg1 = sep->arg[1]; + + Mob *target = c->GetTarget(); + if (!target || !target->IsNPC()) { + c->Message(Chat::Red, "You need a valid NPC target for this command"); + return; + } + + NPC *npc = dynamic_cast(target); + int spawn_group_id = npc->GetSpawnGroupId(); + if (spawn_group_id <= 0) { + c->Message(Chat::Red, "NPC needs a valid SpawnGroup!"); + return; + } + + if (arg1 == "set") { + int box_size = (sep->arg[2] ? atoi(sep->arg[2]) : 0); + int delay = (sep->arg[3] ? atoi(sep->arg[3]) : 15000); + if (box_size > 0) { + std::string query = fmt::format( + SQL( + UPDATE spawngroup SET + dist = {}, + min_x = {}, + max_x = {}, + min_y = {}, + max_y = {}, + delay = {} + WHERE id = {} + ), + (box_size / 2), + npc->GetX() - (box_size / 2), + npc->GetX() + (box_size / 2), + npc->GetY() - (box_size / 2), + npc->GetY() + (box_size / 2), + delay, + spawn_group_id + ); + + database.QueryDatabase(query); + + c->Message( + Chat::Yellow, + "NPC (%s) Roam Box set to box size of [%i] SpawnGroupId [%i] delay [%i]", + npc->GetCleanName(), + box_size, + spawn_group_id, + delay + ); + + return; + } + + c->Message(Chat::Red, "Box size must be set!"); + } + + if (arg1 == "remove") { + std::string query = fmt::format( + SQL( + UPDATE spawngroup SET + dist = 0, + min_x = 0, + max_x = 0, + min_y = 0, + max_y = 0, + delay = 0 + WHERE id = {} + ), + spawn_group_id + ); + + database.QueryDatabase(query); + + c->Message( + Chat::Yellow, + "NPC (%s) Roam Box has been removed from SpawnGroupID [%i]", + npc->GetCleanName(), + spawn_group_id + ); + + return; + } + + c->Message(Chat::Yellow, "> Command Usage"); + c->Message(Chat::Yellow, "#roambox set box_size [delay = 0]"); + c->Message(Chat::Yellow, "#roambox remove"); +} + diff --git a/zone/gm_commands/rules.cpp b/zone/gm_commands/rules.cpp new file mode 100755 index 000000000..65ef8bc37 --- /dev/null +++ b/zone/gm_commands/rules.cpp @@ -0,0 +1,232 @@ +#include "../client.h" + +void command_rules(Client *c, const Seperator *sep) +{ + //super-command for managing rules settings + if (sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { + c->Message(Chat::White, "Syntax: #rules [subcommand]."); + c->Message(Chat::White, "-- Rule Set Manipulation --"); + c->Message(Chat::White, "...listsets - List avaliable rule sets"); + c->Message(Chat::White, "...current - gives the name of the ruleset currently running in this zone"); + c->Message(Chat::White, "...reload - Reload the selected ruleset in this zone"); + c->Message(Chat::White, "...switch (ruleset name) - Change the selected ruleset and load it"); + c->Message( + Chat::White, + "...load (ruleset name) - Load a ruleset in just this zone without changing the selected set" + ); +//too lazy to write this right now: +// c->Message(Chat::White, "...wload (ruleset name) - Load a ruleset in all zones without changing the selected set"); + c->Message(Chat::White, "...store [ruleset name] - Store the running ruleset as the specified name"); + c->Message(Chat::White, "---------------------"); + c->Message(Chat::White, "-- Running Rule Manipulation --"); + c->Message(Chat::White, "...reset - Reset all rules to their default values"); + c->Message(Chat::White, "...get [rule] - Get the specified rule's local value"); + c->Message(Chat::White, "...set (rule) (value) - Set the specified rule to the specified value locally only"); + c->Message( + Chat::White, + "...setdb (rule) (value) - Set the specified rule to the specified value locally and in the DB" + ); + c->Message( + Chat::White, + "...list [catname] - List all rules in the specified category (or all categiries if omitted)" + ); + c->Message(Chat::White, "...values [catname] - List the value of all rules in the specified category"); + return; + } + + if (!strcasecmp(sep->arg[1], "current")) { + c->Message( + Chat::White, "Currently running ruleset '%s' (%d)", RuleManager::Instance()->GetActiveRuleset(), + RuleManager::Instance()->GetActiveRulesetID()); + } + else if (!strcasecmp(sep->arg[1], "listsets")) { + std::map sets; + if (!RuleManager::Instance()->ListRulesets(&database, sets)) { + c->Message(Chat::Red, "Failed to list rule sets!"); + return; + } + + c->Message(Chat::White, "Avaliable rule sets:"); + std::map::iterator cur, end; + cur = sets.begin(); + end = sets.end(); + for (; cur != end; ++cur) { + c->Message(Chat::White, "(%d) %s", cur->first, cur->second.c_str()); + } + } + else if (!strcasecmp(sep->arg[1], "reload")) { + RuleManager::Instance()->LoadRules(&database, RuleManager::Instance()->GetActiveRuleset(), true); + c->Message( + Chat::White, "The active ruleset (%s (%d)) has been reloaded", RuleManager::Instance()->GetActiveRuleset(), + RuleManager::Instance()->GetActiveRulesetID()); + } + else if (!strcasecmp(sep->arg[1], "switch")) { + //make sure this is a valid rule set.. + int rsid = RuleManager::Instance()->GetRulesetID(&database, sep->arg[2]); + if (rsid < 0) { + c->Message(Chat::Red, "Unknown rule set '%s'", sep->arg[2]); + return; + } + if (!database.SetVariable("RuleSet", sep->arg[2])) { + c->Message(Chat::Red, "Failed to update variables table to change selected rule set"); + return; + } + + //TODO: we likely want to reload this ruleset everywhere... + RuleManager::Instance()->LoadRules(&database, sep->arg[2], true); + + c->Message( + Chat::White, + "The selected ruleset has been changed to (%s (%d)) and reloaded locally", + sep->arg[2], + rsid + ); + } + else if (!strcasecmp(sep->arg[1], "load")) { + //make sure this is a valid rule set.. + int rsid = RuleManager::Instance()->GetRulesetID(&database, sep->arg[2]); + if (rsid < 0) { + c->Message(Chat::Red, "Unknown rule set '%s'", sep->arg[2]); + return; + } + RuleManager::Instance()->LoadRules(&database, sep->arg[2], true); + c->Message(Chat::White, "Loaded ruleset '%s' (%d) locally", sep->arg[2], rsid); + } + else if (!strcasecmp(sep->arg[1], "store")) { + if (sep->argnum == 1) { + //store current rule set. + RuleManager::Instance()->SaveRules(&database); + c->Message(Chat::White, "Rules saved"); + } + else if (sep->argnum == 2) { + RuleManager::Instance()->SaveRules(&database, sep->arg[2]); + int prersid = RuleManager::Instance()->GetActiveRulesetID(); + int rsid = RuleManager::Instance()->GetRulesetID(&database, sep->arg[2]); + if (rsid < 0) { + c->Message(Chat::Red, "Unable to query ruleset ID after store, it most likely failed."); + } + else { + c->Message(Chat::White, "Stored rules as ruleset '%s' (%d)", sep->arg[2], rsid); + if (prersid != rsid) { + c->Message(Chat::White, "Rule set %s (%d) is now active in this zone", sep->arg[2], rsid); + } + } + } + else { + c->Message(Chat::Red, "Invalid argument count, see help."); + return; + } + } + else if (!strcasecmp(sep->arg[1], "reset")) { + RuleManager::Instance()->ResetRules(true); + c->Message(Chat::White, "The running ruleset has been set to defaults"); + + } + else if (!strcasecmp(sep->arg[1], "get")) { + if (sep->argnum != 2) { + c->Message(Chat::Red, "Invalid argument count, see help."); + return; + } + std::string value; + if (!RuleManager::Instance()->GetRule(sep->arg[2], value)) { + c->Message(Chat::Red, "Unable to find rule %s", sep->arg[2]); + } + else { + c->Message(Chat::White, "%s - %s", sep->arg[2], value.c_str()); + } + + } + else if (!strcasecmp(sep->arg[1], "set")) { + if (sep->argnum != 3) { + c->Message(Chat::Red, "Invalid argument count, see help."); + return; + } + if (!RuleManager::Instance()->SetRule(sep->arg[2], sep->arg[3], nullptr, false, true)) { + c->Message(Chat::Red, "Failed to modify rule"); + } + else { + c->Message(Chat::White, "Rule modified locally."); + } + } + else if (!strcasecmp(sep->arg[1], "setdb")) { + if (sep->argnum != 3) { + c->Message(Chat::Red, "Invalid argument count, see help."); + return; + } + if (!RuleManager::Instance()->SetRule(sep->arg[2], sep->arg[3], &database, true, true)) { + c->Message(Chat::Red, "Failed to modify rule"); + } + else { + c->Message(Chat::White, "Rule modified locally and in the database."); + } + } + else if (!strcasecmp(sep->arg[1], "list")) { + if (sep->argnum == 1) { + std::vector rule_list; + if (!RuleManager::Instance()->ListCategories(rule_list)) { + c->Message(Chat::Red, "Failed to list categories!"); + return; + } + c->Message(Chat::White, "Rule Categories:"); + std::vector::iterator cur, end; + cur = rule_list.begin(); + end = rule_list.end(); + for (; cur != end; ++cur) { + c->Message(Chat::White, " %s", *cur); + } + } + else if (sep->argnum == 2) { + const char *catfilt = nullptr; + if (std::string("all") != sep->arg[2]) { + catfilt = sep->arg[2]; + } + std::vector rule_list; + if (!RuleManager::Instance()->ListRules(catfilt, rule_list)) { + c->Message(Chat::Red, "Failed to list rules!"); + return; + } + c->Message(Chat::White, "Rules in category %s:", sep->arg[2]); + std::vector::iterator cur, end; + cur = rule_list.begin(); + end = rule_list.end(); + for (; cur != end; ++cur) { + c->Message(Chat::White, " %s", *cur); + } + } + else { + c->Message(Chat::Red, "Invalid argument count, see help."); + } + } + else if (!strcasecmp(sep->arg[1], "values")) { + if (sep->argnum != 2) { + c->Message(Chat::Red, "Invalid argument count, see help."); + return; + } + else { + const char *catfilt = nullptr; + if (std::string("all") != sep->arg[2]) { + catfilt = sep->arg[2]; + } + std::vector rule_list; + if (!RuleManager::Instance()->ListRules(catfilt, rule_list)) { + c->Message(Chat::Red, "Failed to list rules!"); + return; + } + c->Message(Chat::White, "Rules & values in category %s:", sep->arg[2]); + std::vector::iterator cur, end; + cur = rule_list.begin(); + end = rule_list.end(); + for (std::string tmp_value; cur != end; ++cur) { + if (RuleManager::Instance()->GetRule(*cur, tmp_value)) { + c->Message(Chat::White, " %s - %s", *cur, tmp_value.c_str()); + } + } + } + + } + else { + c->Message(Chat::Yellow, "Invalid action specified. use '#rules help' for help"); + } +} + + diff --git a/zone/gm_commands/save.cpp b/zone/gm_commands/save.cpp new file mode 100755 index 000000000..e22b2cbfd --- /dev/null +++ b/zone/gm_commands/save.cpp @@ -0,0 +1,33 @@ +#include "../client.h" +#include "../corpse.h" + +void command_save(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == 0) { + c->Message(Chat::White, "Error: no target"); + } + else if (c->GetTarget()->IsClient()) { + if (c->GetTarget()->CastToClient()->Save(2)) { + c->Message(Chat::White, "%s successfully saved.", c->GetTarget()->GetName()); + } + else { + c->Message(Chat::White, "Manual save for %s failed.", c->GetTarget()->GetName()); + } + } + else if (c->GetTarget()->IsPlayerCorpse()) { + if (c->GetTarget()->CastToMob()->Save()) { + c->Message( + Chat::White, + "%s successfully saved. (dbid=%u)", + c->GetTarget()->GetName(), + c->GetTarget()->CastToCorpse()->GetCorpseDBID()); + } + else { + c->Message(Chat::White, "Manual save for %s failed.", c->GetTarget()->GetName()); + } + } + else { + c->Message(Chat::White, "Error: target not a Client/PlayerCorpse"); + } +} + diff --git a/zone/gm_commands/scale.cpp b/zone/gm_commands/scale.cpp new file mode 100755 index 000000000..d260a25e4 --- /dev/null +++ b/zone/gm_commands/scale.cpp @@ -0,0 +1,136 @@ +#include "../client.h" +#include "../npc_scale_manager.h" + +void command_scale(Client *c, const Seperator *sep) +{ + if (sep->argnum == 0) { + c->Message(Chat::Yellow, "# Usage # "); + c->Message(Chat::Yellow, "#scale [static/dynamic] (With targeted NPC)"); + c->Message(Chat::Yellow, "#scale [npc_name_search] [static/dynamic] (To make zone-wide changes)"); + c->Message(Chat::Yellow, "#scale all [static/dynamic]"); + return; + } + + /** + * Targeted changes + */ + if (c->GetTarget() && c->GetTarget()->IsNPC() && sep->argnum < 2) { + NPC *npc = c->GetTarget()->CastToNPC(); + + bool apply_status = false; + if (strcasecmp(sep->arg[1], "dynamic") == 0) { + c->Message(Chat::Yellow, "Applying global base scaling to npc dynamically (All stats set to zeroes)..."); + apply_status = npc_scale_manager->ApplyGlobalBaseScalingToNPCDynamically(npc); + } + else if (strcasecmp(sep->arg[1], "static") == 0) { + c->Message(Chat::Yellow, "Applying global base scaling to npc statically (Copying base stats onto NPC)..."); + apply_status = npc_scale_manager->ApplyGlobalBaseScalingToNPCStatically(npc); + } + else { + return; + } + + if (apply_status) { + c->Message(Chat::Yellow, "Applied to NPC '%s' successfully!", npc->GetName()); + } + else { + c->Message( + Chat::Yellow, "Failed to load scaling data from the database " + "for this npc / type, see 'NPCScaling' log for more info" + ); + } + } + else if (c->GetTarget() && sep->argnum < 2) { + c->Message(Chat::Yellow, "Target must be an npc!"); + } + + /** + * Zonewide + */ + if (sep->argnum > 1) { + + std::string scale_type; + if (strcasecmp(sep->arg[2], "dynamic") == 0) { + scale_type = "dynamic"; + } + else if (strcasecmp(sep->arg[2], "static") == 0) { + scale_type = "static"; + } + + if (scale_type.length() <= 0) { + c->Message( + Chat::Yellow, + "You must first set if you intend on using static versus dynamic for these changes" + ); + c->Message(Chat::Yellow, "#scale [npc_name_search] [static/dynamic]"); + c->Message(Chat::Yellow, "#scale all [static/dynamic]"); + return; + } + + std::string search_string = sep->arg[1]; + + auto &entity_list_search = entity_list.GetNPCList(); + + int found_count = 0; + for (auto &itr : entity_list_search) { + NPC *entity = itr.second; + + std::string entity_name = entity->GetName(); + + /** + * Filter by name + */ + if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos && + strcasecmp(sep->arg[1], "all") != 0) { + continue; + } + + std::string status = "(Searching)"; + + if (strcasecmp(sep->arg[3], "apply") == 0) { + status = "(Applying)"; + + if (strcasecmp(sep->arg[2], "dynamic") == 0) { + npc_scale_manager->ApplyGlobalBaseScalingToNPCDynamically(entity); + } + if (strcasecmp(sep->arg[2], "static") == 0) { + npc_scale_manager->ApplyGlobalBaseScalingToNPCStatically(entity); + } + } + + c->Message( + 15, + "| ID %5d | %s | x %.0f | y %0.f | z %.0f | DBID %u %s", + entity->GetID(), + entity->GetName(), + entity->GetX(), + entity->GetY(), + entity->GetZ(), + entity->GetNPCTypeID(), + status.c_str() + ); + + found_count++; + } + + if (strcasecmp(sep->arg[3], "apply") == 0) { + c->Message(Chat::Yellow, "%s scaling applied against (%i) NPC's", sep->arg[2], found_count); + } + else { + + std::string saylink = StringFormat( + "#scale %s %s apply", + sep->arg[1], + sep->arg[2] + ); + + c->Message(Chat::Yellow, "Found (%i) NPC's that match this search...", found_count); + c->Message( + Chat::Yellow, "To apply these changes, click <%s> or type %s", + EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Apply").c_str(), + saylink.c_str() + ); + } + } +} + diff --git a/zone/gm_commands/scribespell.cpp b/zone/gm_commands/scribespell.cpp new file mode 100755 index 000000000..5acfd3a8d --- /dev/null +++ b/zone/gm_commands/scribespell.cpp @@ -0,0 +1,66 @@ +#include "../client.h" + +void command_scribespell(Client *c, const Seperator *sep) +{ + uint16 spell_id = 0; + uint16 book_slot = -1; + Client *t = c; + + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + t = c->GetTarget()->CastToClient(); + } + + if (!sep->arg[1][0]) { + c->Message(Chat::White, "FORMAT: #scribespell "); + return; + } + + spell_id = atoi(sep->arg[1]); + + if (IsValidSpell(spell_id)) { + t->Message(Chat::White, "Scribing spell: %s (%i) to spellbook.", spells[spell_id].name, spell_id); + + if (t != c) { + c->Message(Chat::White, "Scribing spell: %s (%i) for %s.", spells[spell_id].name, spell_id, t->GetName()); + } + + LogInfo("Scribe spell: [{}] ([{}]) request for [{}] from [{}]", + spells[spell_id].name, + spell_id, + t->GetName(), + c->GetName()); + + if (spells[spell_id].classes[WARRIOR] != 0 && spells[spell_id].skill != 52 && + spells[spell_id].classes[t->GetPP().class_ - 1] > 0 && !IsDiscipline(spell_id)) { + book_slot = t->GetNextAvailableSpellBookSlot(); + + if (book_slot >= 0 && t->FindSpellBookSlotBySpellID(spell_id) < 0) { + t->ScribeSpell(spell_id, book_slot); + } + else { + t->Message( + Chat::Red, + "Unable to scribe spell: %s (%i) to your spellbook.", + spells[spell_id].name, + spell_id + ); + + if (t != c) { + c->Message( + Chat::Red, + "Unable to scribe spell: %s (%i) for %s.", + spells[spell_id].name, + spell_id, + t->GetName()); + } + } + } + else { + c->Message(Chat::Red, "Your target can not scribe this spell."); + } + } + else { + c->Message(Chat::Red, "Spell ID: %i is an unknown spell and cannot be scribed.", spell_id); + } +} + diff --git a/zone/gm_commands/scribespells.cpp b/zone/gm_commands/scribespells.cpp new file mode 100755 index 000000000..ef30d6384 --- /dev/null +++ b/zone/gm_commands/scribespells.cpp @@ -0,0 +1,65 @@ +#include "../client.h" + +void command_scribespells(Client *c, const Seperator *sep) +{ + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + target = c->GetTarget()->CastToClient(); + } + + if (sep->argnum < 1 || !sep->IsNumber(1)) { + c->Message(Chat::White, "FORMAT: #scribespells "); + return; + } + + uint8 rule_max_level = (uint8) RuleI(Character, MaxLevel); + uint8 max_level = (uint8) std::stoi(sep->arg[1]); + uint8 min_level = ( + sep->IsNumber(2) ? + (uint8) + std::stoi(sep->arg[2]) : + 1 + ); // Default to Level 1 if there isn't a 2nd argument + + if (!c->GetGM()) { // Default to Character:MaxLevel if we're not a GM and Level is higher than the max level + if (max_level > rule_max_level) { + max_level = rule_max_level; + } + + if (min_level > rule_max_level) { + min_level = rule_max_level; + } + } + + if (max_level < 1 || min_level < 1) { + c->Message(Chat::White, "ERROR: Level must be greater than or equal to 1."); + return; + } + + if (min_level > max_level) { + c->Message(Chat::White, "ERROR: Minimum Level must be less than or equal to Maximum Level."); + return; + } + + uint16 scribed_spells = target->ScribeSpells(min_level, max_level); + if (target != c) { + std::string spell_message = ( + scribed_spells > 0 ? + ( + scribed_spells == 1 ? + "A new spell" : + fmt::format("{} New spells", scribed_spells) + ) : + "No new spells" + ); + c->Message( + Chat::White, + fmt::format( + "{} scribed for {}.", + spell_message, + target->GetCleanName() + ).c_str() + ); + } +} + diff --git a/zone/gm_commands/sendzonespawns.cpp b/zone/gm_commands/sendzonespawns.cpp new file mode 100755 index 000000000..caca8f6d0 --- /dev/null +++ b/zone/gm_commands/sendzonespawns.cpp @@ -0,0 +1,7 @@ +#include "../client.h" + +void command_sendzonespawns(Client *c, const Seperator *sep) +{ + entity_list.SendZoneSpawns(c); +} + diff --git a/zone/gm_commands/sensetrap.cpp b/zone/gm_commands/sensetrap.cpp new file mode 100755 index 000000000..792d7867d --- /dev/null +++ b/zone/gm_commands/sensetrap.cpp @@ -0,0 +1,24 @@ +#include "../client.h" + +void command_sensetrap(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!target) { + c->Message(Chat::Red, "You must have a target."); + return; + } + + if (target->IsNPC()) { + if (c->HasSkill(EQ::skills::SkillSenseTraps)) { + if (DistanceSquaredNoZ(c->GetPosition(), target->GetPosition()) > RuleI(Adventure, LDoNTrapDistanceUse)) { + c->Message(Chat::Red, "%s is too far away.", target->GetCleanName()); + return; + } + c->HandleLDoNSenseTraps(target->CastToNPC(), c->GetSkill(EQ::skills::SkillSenseTraps), LDoNTypeMechanical); + } + else { + c->Message(Chat::Red, "You do not have the sense traps skill."); + } + } +} + diff --git a/zone/gm_commands/serverinfo.cpp b/zone/gm_commands/serverinfo.cpp new file mode 100755 index 000000000..e51da7266 --- /dev/null +++ b/zone/gm_commands/serverinfo.cpp @@ -0,0 +1,33 @@ +#include "../client.h" +#include "../../common/serverinfo.h" + +void command_serverinfo(Client *c, const Seperator *sep) +{ + auto os = EQ::GetOS(); + auto cpus = EQ::GetCPUs(); + auto pid = EQ::GetPID(); + auto rss = EQ::GetRSS(); + auto uptime = EQ::GetUptime(); + + c->Message(Chat::White, "Operating System Information"); + c->Message(Chat::White, "=================================================="); + c->Message(Chat::White, "System: %s", os.sysname.c_str()); + c->Message(Chat::White, "Release: %s", os.release.c_str()); + c->Message(Chat::White, "Version: %s", os.version.c_str()); + c->Message(Chat::White, "Machine: %s", os.machine.c_str()); + c->Message(Chat::White, "Uptime: %.2f seconds", uptime); + c->Message(Chat::White, "=================================================="); + c->Message(Chat::White, "CPU Information"); + c->Message(Chat::White, "=================================================="); + for (size_t i = 0; i < cpus.size(); ++i) { + auto &cp = cpus[i]; + c->Message(Chat::White, "CPU #%i: %s, Speed: %.2fGhz", i, cp.model.c_str(), cp.speed); + } + c->Message(Chat::White, "=================================================="); + c->Message(Chat::White, "Process Information"); + c->Message(Chat::White, "=================================================="); + c->Message(Chat::White, "PID: %u", pid); + c->Message(Chat::White, "RSS: %.2f MB", rss / 1048576.0); + c->Message(Chat::White, "=================================================="); +} + diff --git a/zone/gm_commands/serverrules.cpp b/zone/gm_commands/serverrules.cpp new file mode 100755 index 000000000..69bf799a7 --- /dev/null +++ b/zone/gm_commands/serverrules.cpp @@ -0,0 +1,7 @@ +#include "../client.h" + +void command_serverrules(Client *c, const Seperator *sep) +{ + c->SendRules(c); +} + diff --git a/zone/gm_commands/set_adventure_points.cpp b/zone/gm_commands/set_adventure_points.cpp new file mode 100755 index 000000000..a9b1f47e3 --- /dev/null +++ b/zone/gm_commands/set_adventure_points.cpp @@ -0,0 +1,24 @@ +#include "../client.h" + +void command_set_adventure_points(Client *c, const Seperator *sep) +{ + Client *t = c; + + if (c->GetTarget() && c->GetTarget()->IsClient()) { + t = c->GetTarget()->CastToClient(); + } + + if (!sep->arg[1][0]) { + c->Message(Chat::White, "Usage: #setadventurepoints [theme] [points]"); + return; + } + + if (!sep->IsNumber(1) || !sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #setadventurepoints [theme] [points]"); + return; + } + + c->Message(Chat::White, "Updating adventure points for %s", t->GetName()); + t->UpdateLDoNPoints(atoi(sep->arg[1]), atoi(sep->arg[2])); +} + diff --git a/zone/gm_commands/setaapts.cpp b/zone/gm_commands/setaapts.cpp new file mode 100755 index 000000000..e8222a08f --- /dev/null +++ b/zone/gm_commands/setaapts.cpp @@ -0,0 +1,63 @@ +#include "../client.h" +#include "../groups.h" +#include "../raids.h" +#include "../raids.h" + +void command_setaapts(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (arguments <= 1 || !sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #setaapts [AA|Group|Raid] [AA Amount]"); + return; + } + + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + + std::string aa_type = str_tolower(sep->arg[1]); + std::string group_raid_string; + uint32 aa_points = static_cast(std::min(std::stoull(sep->arg[2]), (unsigned long long) 2000000000)); + bool is_aa = aa_type.find("aa") != std::string::npos; + bool is_group = aa_type.find("group") != std::string::npos; + bool is_raid = aa_type.find("raid") != std::string::npos; + if (!is_aa && !is_group && !is_raid) { + c->Message(Chat::White, "Usage: #setaapts [AA|Group|Raid] [AA Amount]"); + return; + } + + if (is_aa) { + target->GetPP().aapoints = aa_points; + target->GetPP().expAA = 0; + target->SendAlternateAdvancementStats(); + } + else if (is_group || is_raid) { + if (is_group) { + group_raid_string = "Group "; + target->GetPP().group_leadership_points = aa_points; + target->GetPP().group_leadership_exp = 0; + } + else if (is_raid) { + group_raid_string = "Raid "; + target->GetPP().raid_leadership_points = aa_points; + target->GetPP().raid_leadership_exp = 0; + } + target->SendLeadershipEXPUpdate(); + } + + std::string aa_message = fmt::format( + "{} now {} {} {}AA Point{}.", + c == target ? "You" : target->GetCleanName(), + c == target ? "have" : "has", + aa_points, + group_raid_string, + aa_points != 1 ? "s" : "" + + ); + c->Message( + Chat::White, + aa_message.c_str() + ); +} + diff --git a/zone/gm_commands/setaaxp.cpp b/zone/gm_commands/setaaxp.cpp new file mode 100755 index 000000000..32c060aed --- /dev/null +++ b/zone/gm_commands/setaaxp.cpp @@ -0,0 +1,67 @@ +#include "../client.h" +#include "../groups.h" +#include "../raids.h" +#include "../raids.h" + +void command_setaaxp(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (arguments <= 1 || !sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #setaaxp [AA|Group|Raid] [AA Experience]"); + return; + } + + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + + std::string aa_type = str_tolower(sep->arg[1]); + std::string group_raid_string; + uint32 aa_experience = static_cast(std::min( + std::stoull(sep->arg[2]), + (unsigned long long) 2000000000 + )); + bool is_aa = aa_type.find("aa") != std::string::npos; + bool is_group = aa_type.find("group") != std::string::npos; + bool is_raid = aa_type.find("raid") != std::string::npos; + if (!is_aa && !is_group && !is_raid) { + c->Message(Chat::White, "Usage: #setaaxp [AA|Group|Raid] [AA Experience]"); + return; + } + + if (is_aa) { + target->SetEXP( + target->GetEXP(), + aa_experience, + false + ); + } + else if (is_group) { + group_raid_string = "Group "; + target->SetLeadershipEXP( + aa_experience, + target->GetRaidEXP() + ); + } + else if (is_raid) { + group_raid_string = "Raid "; + target->SetLeadershipEXP( + target->GetGroupEXP(), + aa_experience + ); + } + + std::string aa_exp_message = fmt::format( + "{} now {} {} {}AA Experience.", + c == target ? "You" : target->GetCleanName(), + c == target ? "have" : "has", + aa_experience, + group_raid_string + ); + c->Message( + Chat::White, + aa_exp_message.c_str() + ); +} + diff --git a/zone/gm_commands/setanim.cpp b/zone/gm_commands/setanim.cpp new file mode 100755 index 000000000..d7ffa11e7 --- /dev/null +++ b/zone/gm_commands/setanim.cpp @@ -0,0 +1,16 @@ +#include "../client.h" + +void command_setanim(Client *c, const Seperator *sep) +{ + if (c->GetTarget() && sep->IsNumber(1)) { + int num = atoi(sep->arg[1]); + if (num < 0 || num >= _eaMaxAppearance) { + c->Message(Chat::White, "Invalid animation number, between 0 and %d", _eaMaxAppearance - 1); + } + c->GetTarget()->SetAppearance(EmuAppearance(num)); + } + else { + c->Message(Chat::White, "Usage: #setanim [animnum]"); + } +} + diff --git a/zone/gm_commands/setcrystals.cpp b/zone/gm_commands/setcrystals.cpp new file mode 100755 index 000000000..32d22f449 --- /dev/null +++ b/zone/gm_commands/setcrystals.cpp @@ -0,0 +1,53 @@ +#include "../client.h" + +void command_setcrystals(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (arguments <= 1 || !sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #setcrystals [Ebon|Radiant] [Crystal Amount]"); + return; + } + + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + + std::string crystal_type = str_tolower(sep->arg[1]); + uint32 crystal_amount = static_cast(std::min( + std::stoull(sep->arg[2]), + (unsigned long long) 2000000000 + )); + bool is_ebon = crystal_type.find("ebon") != std::string::npos; + bool is_radiant = crystal_type.find("radiant") != std::string::npos; + if (!is_ebon && !is_radiant) { + c->Message(Chat::White, "Usage: #setcrystals [Ebon|Radiant] [Crystal Amount]"); + return; + } + + uint32 crystal_item_id = ( + is_ebon ? + RuleI(Zone, EbonCrystalItemID) : + RuleI(Zone, RadiantCrystalItemID) + ); + + auto crystal_link = database.CreateItemLink(crystal_item_id); + if (is_radiant) { + target->SetRadiantCrystals(crystal_amount); + } + else { + target->SetEbonCrystals(crystal_amount); + } + + c->Message( + Chat::White, + fmt::format( + "{} now {} {} {}.", + c == target ? "You" : target->GetCleanName(), + c == target ? "have" : "has", + crystal_amount, + crystal_link + ).c_str() + ); +} + diff --git a/zone/gm_commands/setfaction.cpp b/zone/gm_commands/setfaction.cpp new file mode 100755 index 000000000..36662d3df --- /dev/null +++ b/zone/gm_commands/setfaction.cpp @@ -0,0 +1,20 @@ +#include "../client.h" + +void command_setfaction(Client *c, const Seperator *sep) +{ + if ((sep->arg[1][0] == 0 || strcasecmp(sep->arg[1], "*") == 0) || + ((c->GetTarget() == 0) || (c->GetTarget()->IsClient()))) { + c->Message(Chat::White, "Usage: #setfaction [faction number]"); + return; + } + + auto npcTypeID = c->GetTarget()->CastToNPC()->GetNPCTypeID(); + c->Message(Chat::Yellow, "Setting NPC %u to faction %i", npcTypeID, atoi(sep->argplus[1])); + + std::string query = StringFormat( + "UPDATE npc_types SET npc_faction_id = %i WHERE id = %i", + atoi(sep->argplus[1]), npcTypeID + ); + content_db.QueryDatabase(query); +} + diff --git a/zone/gm_commands/setgraveyard.cpp b/zone/gm_commands/setgraveyard.cpp new file mode 100755 index 000000000..e1b043e4b --- /dev/null +++ b/zone/gm_commands/setgraveyard.cpp @@ -0,0 +1,44 @@ +#include "../client.h" + +void command_setgraveyard(Client *c, const Seperator *sep) +{ + uint32 zoneid = 0; + uint32 graveyard_id = 0; + Client *t = c; + + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + t = c->GetTarget()->CastToClient(); + } + + if (!sep->arg[1][0]) { + c->Message(Chat::White, "Usage: #setgraveyard [zonename]"); + return; + } + + zoneid = ZoneID(sep->arg[1]); + + if (zoneid > 0) { + graveyard_id = content_db.CreateGraveyardRecord(zoneid, t->GetPosition()); + + if (graveyard_id > 0) { + c->Message(Chat::White, "Successfuly added a new record for this graveyard!"); + if (content_db.AddGraveyardIDToZone(zoneid, graveyard_id) > 0) { + c->Message(Chat::White, "Successfuly added this new graveyard for the zone %s.", sep->arg[1]); + // TODO: Set graveyard data to the running zone process. + c->Message(Chat::White, "Done!"); + } + else { + c->Message(Chat::White, "Unable to add this new graveyard to the zone %s.", sep->arg[1]); + } + } + else { + c->Message(Chat::White, "Unable to create a new graveyard record in the database."); + } + } + else { + c->Message(Chat::White, "Unable to retrieve a ZoneID for the zone: %s", sep->arg[1]); + } + + return; +} + diff --git a/zone/gm_commands/setlanguage.cpp b/zone/gm_commands/setlanguage.cpp new file mode 100755 index 000000000..50353f150 --- /dev/null +++ b/zone/gm_commands/setlanguage.cpp @@ -0,0 +1,61 @@ +#include "../client.h" +#include "../../common/languages.h" + +void command_setlanguage(Client *c, const Seperator *sep) +{ + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + + auto language_id = sep->IsNumber(1) ? std::stoi(sep->arg[1]) : -1; + auto language_value = sep->IsNumber(2) ? std::stoi(sep->arg[2]) : -1; + if (!strcasecmp(sep->arg[1], "list")) { + for (int language = LANG_COMMON_TONGUE; language <= LANG_UNKNOWN; language++) { + c->Message( + Chat::White, + fmt::format( + "Language {}: {}", + language, + EQ::constants::GetLanguageName(language) + ).c_str() + ); + } + } + else if ( + language_id < LANG_COMMON_TONGUE || + language_id > LANG_UNKNOWN || + language_value < 0 || + language_value > 100 + ) { + c->Message(Chat::White, "Usage: #setlanguage [Language ID] [Language Value]"); + c->Message(Chat::White, "Usage: #setlanguage [List]"); + c->Message(Chat::White, "Language ID = 0 to 27", LANG_UNKNOWN); + c->Message(Chat::White, "Language Value = 0 to 100", HIGHEST_CAN_SET_SKILL); + } + else { + LogInfo( + "Set language request from [{}], Target: [{}] Language ID: [{}] Language Value: [{}]", + c->GetCleanName(), + target->GetCleanName(), + language_id, + language_value + ); + + target->SetLanguageSkill(language_id, language_value); + + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "Set {} ({}) to {} for {}.", + EQ::constants::GetLanguageName(language_id), + language_id, + language_value, + target->GetCleanName() + ).c_str() + ); + } + } +} + diff --git a/zone/gm_commands/setlsinfo.cpp b/zone/gm_commands/setlsinfo.cpp new file mode 100755 index 000000000..64f38da01 --- /dev/null +++ b/zone/gm_commands/setlsinfo.cpp @@ -0,0 +1,24 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_setlsinfo(Client *c, const Seperator *sep) +{ + if (sep->argnum != 2) { + c->Message(Chat::White, "Format: #setlsinfo email password"); + } + else { + auto pack = new ServerPacket( + ServerOP_LSAccountUpdate, + sizeof(ServerLSAccountUpdate_Struct)); + ServerLSAccountUpdate_Struct *s = (ServerLSAccountUpdate_Struct *) pack->pBuffer; + s->useraccountid = c->LSAccountID(); + strn0cpy(s->useraccount, c->AccountName(), 30); + strn0cpy(s->user_email, sep->arg[1], 100); + strn0cpy(s->userpassword, sep->arg[2], 50); + worldserver.SendPacket(pack); + c->Message(Chat::White, "Login Server update packet sent."); + } +} + diff --git a/zone/gm_commands/setpass.cpp b/zone/gm_commands/setpass.cpp new file mode 100755 index 000000000..4bf84d300 --- /dev/null +++ b/zone/gm_commands/setpass.cpp @@ -0,0 +1,29 @@ +#include "../client.h" + +void command_setpass(Client *c, const Seperator *sep) +{ + if (sep->argnum != 2) { + c->Message(Chat::White, "Format: #setpass accountname password"); + } + else { + std::string user; + std::string loginserver; + ParseAccountString(sep->arg[1], user, loginserver); + + int16 tmpstatus = 0; + uint32 tmpid = database.GetAccountIDByName(user.c_str(), loginserver.c_str(), &tmpstatus); + if (!tmpid) { + c->Message(Chat::White, "Error: Account not found"); + } + else if (tmpstatus > c->Admin()) { + c->Message(Chat::White, "Cannot change password: Account's status is higher than yours"); + } + else if (database.SetLocalPassword(tmpid, sep->arg[2])) { + c->Message(Chat::White, "Password changed."); + } + else { + c->Message(Chat::White, "Error changing password."); + } + } +} + diff --git a/zone/gm_commands/setpvppoints.cpp b/zone/gm_commands/setpvppoints.cpp new file mode 100755 index 000000000..27eae29cd --- /dev/null +++ b/zone/gm_commands/setpvppoints.cpp @@ -0,0 +1,32 @@ +#include "../client.h" + +void command_setpvppoints(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Command Syntax: #setpvppoints [Amount]"); + return; + } + + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + + uint32 pvp_points = static_cast(std::min(std::stoull(sep->arg[1]), (unsigned long long) 2000000000)); + target->SetPVPPoints(pvp_points); + target->Save(); + target->SendPVPStats(); + std::string pvp_message = fmt::format( + "{} now {} {} PVP Point{}.", + c == target ? "You" : target->GetCleanName(), + c == target ? "have" : "has", + pvp_points, + pvp_points != 1 ? "s" : "" + ); + c->Message( + Chat::White, + pvp_message.c_str() + ); +} + diff --git a/zone/gm_commands/setskill.cpp b/zone/gm_commands/setskill.cpp new file mode 100755 index 000000000..625d54e0e --- /dev/null +++ b/zone/gm_commands/setskill.cpp @@ -0,0 +1,55 @@ +#include "../client.h" + +void command_setskill(Client *c, const Seperator *sep) +{ + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + + auto skill_id = sep->IsNumber(1) ? std::stoi(sep->arg[1]) : -1; + auto skill_value = sep->IsNumber(2) ? std::stoi(sep->arg[2]) : -1; + if ( + skill_id < 0 || + skill_id > EQ::skills::HIGHEST_SKILL || + skill_value < 0 || + skill_value > HIGHEST_CAN_SET_SKILL + ) { + c->Message(Chat::White, "Usage: #setskill [Skill ID] [Skill Value]"); + c->Message(Chat::White, fmt::format("Skill ID = 0 to {}", EQ::skills::HIGHEST_SKILL).c_str()); + c->Message(Chat::White, fmt::format("Skill Value = 0 to {}", HIGHEST_CAN_SET_SKILL).c_str()); + } + else { + LogInfo( + "Set skill request from [{}], Target: [{}] Skill ID: [{}] Skill Value: [{}]", + c->GetCleanName(), + target->GetCleanName(), + skill_id, + skill_value + ); + + if ( + skill_id >= EQ::skills::Skill1HBlunt && + skill_id <= EQ::skills::HIGHEST_SKILL + ) { + target->SetSkill( + (EQ::skills::SkillType) skill_id, + skill_value + ); + + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "Set {} ({}) to {} for {}.", + EQ::skills::GetSkillName((EQ::skills::SkillType) skill_id), + skill_id, + skill_value, + target->GetCleanName() + ).c_str() + ); + } + } + } +} + diff --git a/zone/gm_commands/setskillall.cpp b/zone/gm_commands/setskillall.cpp new file mode 100755 index 000000000..dd8b9992a --- /dev/null +++ b/zone/gm_commands/setskillall.cpp @@ -0,0 +1,30 @@ +#include "../client.h" + +void command_setskillall(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == 0) { + c->Message(Chat::White, "Error: #setallskill: No target."); + } + else if (!c->GetTarget()->IsClient()) { + c->Message(Chat::White, "Error: #setskill: Target must be a client."); + } + else if (!sep->IsNumber(1) || atoi(sep->arg[1]) < 0 || atoi(sep->arg[1]) > HIGHEST_CAN_SET_SKILL) { + c->Message(Chat::White, "Usage: #setskillall value "); + c->Message(Chat::White, " value = 0 to %d", HIGHEST_CAN_SET_SKILL); + } + else { + if (c->Admin() >= commandSetSkillsOther || c->GetTarget() == c || c->GetTarget() == 0) { + LogInfo("Set ALL skill request from [{}], target:[{}]", c->GetName(), c->GetTarget()->GetName()); + uint16 level = atoi(sep->arg[1]); + for (EQ::skills::SkillType skill_num = EQ::skills::Skill1HBlunt; + skill_num <= EQ::skills::HIGHEST_SKILL; + skill_num = (EQ::skills::SkillType) (skill_num + 1)) { + c->GetTarget()->CastToClient()->SetSkill(skill_num, level); + } + } + else { + c->Message(Chat::White, "Error: Your status is not high enough to set anothers skills"); + } + } +} + diff --git a/zone/gm_commands/setstartzone.cpp b/zone/gm_commands/setstartzone.cpp new file mode 100755 index 000000000..bff04d296 --- /dev/null +++ b/zone/gm_commands/setstartzone.cpp @@ -0,0 +1,35 @@ +#include "../client.h" + +void command_setstartzone(Client *c, const Seperator *sep) +{ + uint32 startzone = 0; + Client *target = nullptr; + if (c->GetTarget() && c->GetTarget()->IsClient() && sep->arg[1][0] != 0) { + target = c->GetTarget()->CastToClient(); + } + else { + c->Message(Chat::White, "Usage: (needs PC target) #setstartzone zonename"); + c->Message( + Chat::White, + "Optional Usage: Use '#setstartzone reset' or '#setstartzone 0' to clear a starting zone. Player can select a starting zone using /setstartcity" + ); + return; + } + + if (sep->IsNumber(1)) { + startzone = atoi(sep->arg[1]); + } + else if (strcasecmp(sep->arg[1], "reset") == 0) { + startzone = 0; + } + else { + startzone = ZoneID(sep->arg[1]); + if (startzone == 0) { + c->Message(Chat::White, "Unable to locate zone '%s'", sep->arg[1]); + return; + } + } + + target->SetStartZone(startzone); +} + diff --git a/zone/gm_commands/setstat.cpp b/zone/gm_commands/setstat.cpp new file mode 100755 index 000000000..56ff81a05 --- /dev/null +++ b/zone/gm_commands/setstat.cpp @@ -0,0 +1,14 @@ +#include "../client.h" + +void command_setstat(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] && sep->arg[2][0] && c->GetTarget() != 0 && c->GetTarget()->IsClient()) { + c->GetTarget()->CastToClient()->SetStats(atoi(sep->arg[1]), atoi(sep->arg[2])); + } + else { + c->Message(Chat::White, "This command is used to permanently increase or decrease a players stats."); + c->Message(Chat::White, "Usage: #setstat {type} {value the stat should be}"); + c->Message(Chat::White, "Types: Str: 0, Sta: 1, Agi: 2, Dex: 3, Int: 4, Wis: 5, Cha: 6"); + } +} + diff --git a/zone/gm_commands/setxp.cpp b/zone/gm_commands/setxp.cpp new file mode 100755 index 000000000..c82bb87a3 --- /dev/null +++ b/zone/gm_commands/setxp.cpp @@ -0,0 +1,23 @@ +#include "../client.h" + +void command_setxp(Client *c, const Seperator *sep) +{ + Client *t = c; + + if (c->GetTarget() && c->GetTarget()->IsClient()) { + t = c->GetTarget()->CastToClient(); + } + + if (sep->IsNumber(1)) { + if (atoi(sep->arg[1]) > 9999999) { + c->Message(Chat::White, "Error: Value too high."); + } + else { + t->AddEXP(atoi(sep->arg[1])); + } + } + else { + c->Message(Chat::White, "Usage: #setxp number"); + } +} + diff --git a/zone/gm_commands/showbonusstats.cpp b/zone/gm_commands/showbonusstats.cpp new file mode 100755 index 000000000..2680b36f1 --- /dev/null +++ b/zone/gm_commands/showbonusstats.cpp @@ -0,0 +1,49 @@ +#include "../client.h" + +void command_showbonusstats(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == 0) { + c->Message(Chat::White, "ERROR: No target!"); + } + else if (!c->GetTarget()->IsMob() && !c->GetTarget()->IsClient()) { + c->Message(Chat::White, "ERROR: Target is not a Mob or Player!"); + } + else { + bool bAll = false; + if (sep->arg[1][0] == '\0' || strcasecmp(sep->arg[1], "all") == 0) { + bAll = true; + } + if (bAll || (strcasecmp(sep->arg[1], "item") == 0)) { + c->Message(Chat::White, "Target Item Bonuses:"); + c->Message( + Chat::White, + " Accuracy: %i%% Divine Save: %i%%", + c->GetTarget()->GetItemBonuses().Accuracy, + c->GetTarget()->GetItemBonuses().DivineSaveChance + ); + c->Message( + Chat::White, + " Flurry: %i%% HitChance: %i%%", + c->GetTarget()->GetItemBonuses().FlurryChance, + c->GetTarget()->GetItemBonuses().HitChance / 15 + ); + } + if (bAll || (strcasecmp(sep->arg[1], "spell") == 0)) { + c->Message(Chat::White, " Target Spell Bonuses:"); + c->Message( + Chat::White, + " Accuracy: %i%% Divine Save: %i%%", + c->GetTarget()->GetSpellBonuses().Accuracy, + c->GetTarget()->GetSpellBonuses().DivineSaveChance + ); + c->Message( + Chat::White, + " Flurry: %i%% HitChance: %i%% ", + c->GetTarget()->GetSpellBonuses().FlurryChance, + c->GetTarget()->GetSpellBonuses().HitChance / 15 + ); + } + c->Message(Chat::White, " Effective Casting Level: %i", c->GetTarget()->GetCasterLevel(0)); + } +} + diff --git a/zone/gm_commands/showbuffs.cpp b/zone/gm_commands/showbuffs.cpp new file mode 100755 index 000000000..bd8e558d6 --- /dev/null +++ b/zone/gm_commands/showbuffs.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +void command_showbuffs(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == 0) { + c->CastToMob()->ShowBuffs(c); + } + else { + c->GetTarget()->CastToMob()->ShowBuffs(c); + } +} + diff --git a/zone/gm_commands/shownpcgloballoot.cpp b/zone/gm_commands/shownpcgloballoot.cpp new file mode 100755 index 000000000..546631c5c --- /dev/null +++ b/zone/gm_commands/shownpcgloballoot.cpp @@ -0,0 +1,16 @@ +#include "../client.h" + +void command_shownpcgloballoot(Client *c, const Seperator *sep) +{ + auto tar = c->GetTarget(); + + if (!tar || !tar->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; + } + + auto npc = tar->CastToNPC(); + c->Message(Chat::White, "GlobalLoot for %s (%d)", npc->GetName(), npc->GetNPCTypeID()); + zone->ShowNPCGlobalLoot(c, npc); +} + diff --git a/zone/gm_commands/shownumhits.cpp b/zone/gm_commands/shownumhits.cpp new file mode 100755 index 000000000..b8b06867b --- /dev/null +++ b/zone/gm_commands/shownumhits.cpp @@ -0,0 +1,8 @@ +#include "../client.h" + +void command_shownumhits(Client *c, const Seperator *sep) +{ + c->ShowNumHits(); + return; +} + diff --git a/zone/gm_commands/showskills.cpp b/zone/gm_commands/showskills.cpp new file mode 100755 index 000000000..e970dd7aa --- /dev/null +++ b/zone/gm_commands/showskills.cpp @@ -0,0 +1,44 @@ +#include "../client.h" + +void command_showskills(Client *c, const Seperator *sep) +{ + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + + bool show_all = false; + + if (!strcasecmp("all", sep->arg[1])) { + show_all = true; + } + + c->Message( + Chat::White, + fmt::format( + "Skills | Name: {}", + target->GetCleanName() + ).c_str() + ); + + for ( + EQ::skills::SkillType skill_type = EQ::skills::Skill1HBlunt; + skill_type <= EQ::skills::HIGHEST_SKILL; + skill_type = (EQ::skills::SkillType) (skill_type + 1) + ) { + if (show_all || (target->CanHaveSkill(skill_type) && target->MaxSkill(skill_type))) { + c->Message( + Chat::White, + fmt::format( + "{} ({}) | Current: {} Max: {} Raw: {}", + EQ::skills::GetSkillName(skill_type), + skill_type, + target->GetSkill(skill_type), + target->MaxSkill(skill_type), + target->GetRawSkill(skill_type) + ).c_str() + ); + } + } +} + diff --git a/zone/gm_commands/showspellslist.cpp b/zone/gm_commands/showspellslist.cpp new file mode 100755 index 000000000..2b50864e6 --- /dev/null +++ b/zone/gm_commands/showspellslist.cpp @@ -0,0 +1,13 @@ +#include "../client.h" + +void command_showspellslist(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!target || !target->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; + } + target->CastToNPC()->AISpellsList(c); + return; +} + diff --git a/zone/gm_commands/showstats.cpp b/zone/gm_commands/showstats.cpp new file mode 100755 index 000000000..cae9609c2 --- /dev/null +++ b/zone/gm_commands/showstats.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +void command_showstats(Client *c, const Seperator *sep) +{ + if (c->GetTarget() != 0) { + c->GetTarget()->ShowStats(c); + } + else { + c->ShowStats(c); + } +} + diff --git a/zone/gm_commands/showzonegloballoot.cpp b/zone/gm_commands/showzonegloballoot.cpp new file mode 100755 index 000000000..ecf774f88 --- /dev/null +++ b/zone/gm_commands/showzonegloballoot.cpp @@ -0,0 +1,13 @@ +#include "../client.h" + +void command_showzonegloballoot(Client *c, const Seperator *sep) +{ + c->Message( + Chat::White, + "GlobalLoot for %s (%d:%d)", + zone->GetShortName(), + zone->GetZoneID(), + zone->GetInstanceVersion()); + zone->ShowZoneGlobalLoot(c); +} + diff --git a/zone/gm_commands/showzonepoints.cpp b/zone/gm_commands/showzonepoints.cpp new file mode 100755 index 000000000..f86dcd29e --- /dev/null +++ b/zone/gm_commands/showzonepoints.cpp @@ -0,0 +1,157 @@ +#include "../client.h" + +void command_showzonepoints(Client *c, const Seperator *sep) +{ + auto &mob_list = entity_list.GetMobList(); + for (auto itr : mob_list) { + Mob *mob = itr.second; + if (mob->IsNPC() && mob->GetRace() == 2254) { + mob->Depop(); + } + } + + int found_zone_points = 0; + + c->Message(Chat::White, "Listing zone points..."); + c->SendChatLineBreak(); + + for (auto &virtual_zone_point : zone->virtual_zone_point_list) { + std::string zone_long_name = zone_store.GetZoneLongName(virtual_zone_point.target_zone_id); + + c->Message( + Chat::White, + fmt::format( + "Virtual Zone Point x [{}] y [{}] z [{}] h [{}] width [{}] height [{}] | To [{}] ({}) x [{}] y [{}] z [{}] h [{}]", + virtual_zone_point.x, + virtual_zone_point.y, + virtual_zone_point.z, + virtual_zone_point.heading, + virtual_zone_point.width, + virtual_zone_point.height, + zone_long_name.c_str(), + virtual_zone_point.target_zone_id, + virtual_zone_point.target_x, + virtual_zone_point.target_y, + virtual_zone_point.target_z, + virtual_zone_point.target_heading + ).c_str() + ); + + std::string node_name = fmt::format("ZonePoint To [{}]", zone_long_name); + + float half_width = ((float) virtual_zone_point.width / 2); + + NPC::SpawnZonePointNodeNPC( + node_name, glm::vec4( + (float) virtual_zone_point.x + half_width, + (float) virtual_zone_point.y + half_width, + virtual_zone_point.z, + virtual_zone_point.heading + )); + + NPC::SpawnZonePointNodeNPC( + node_name, glm::vec4( + (float) virtual_zone_point.x + half_width, + (float) virtual_zone_point.y - half_width, + virtual_zone_point.z, + virtual_zone_point.heading + )); + + NPC::SpawnZonePointNodeNPC( + node_name, glm::vec4( + (float) virtual_zone_point.x - half_width, + (float) virtual_zone_point.y - half_width, + virtual_zone_point.z, + virtual_zone_point.heading + )); + + NPC::SpawnZonePointNodeNPC( + node_name, glm::vec4( + (float) virtual_zone_point.x - half_width, + (float) virtual_zone_point.y + half_width, + virtual_zone_point.z, + virtual_zone_point.heading + )); + + NPC::SpawnZonePointNodeNPC( + node_name, glm::vec4( + (float) virtual_zone_point.x + half_width, + (float) virtual_zone_point.y + half_width, + (float) virtual_zone_point.z + (float) virtual_zone_point.height, + virtual_zone_point.heading + )); + + NPC::SpawnZonePointNodeNPC( + node_name, glm::vec4( + (float) virtual_zone_point.x + half_width, + (float) virtual_zone_point.y - half_width, + (float) virtual_zone_point.z + (float) virtual_zone_point.height, + virtual_zone_point.heading + )); + + NPC::SpawnZonePointNodeNPC( + node_name, glm::vec4( + (float) virtual_zone_point.x - half_width, + (float) virtual_zone_point.y - half_width, + (float) virtual_zone_point.z + (float) virtual_zone_point.height, + virtual_zone_point.heading + )); + + NPC::SpawnZonePointNodeNPC( + node_name, glm::vec4( + (float) virtual_zone_point.x - half_width, + (float) virtual_zone_point.y + half_width, + (float) virtual_zone_point.z + (float) virtual_zone_point.height, + virtual_zone_point.heading + )); + + found_zone_points++; + } + + LinkedListIterator iterator(zone->zone_point_list); + iterator.Reset(); + while (iterator.MoreElements()) { + ZonePoint *zone_point = iterator.GetData(); + std::string zone_long_name = zone_store.GetZoneLongName(zone_point->target_zone_id); + std::string node_name = fmt::format("ZonePoint To [{}]", zone_long_name); + + NPC::SpawnZonePointNodeNPC( + node_name, glm::vec4( + zone_point->x, + zone_point->y, + zone_point->z, + zone_point->heading + ) + ); + + c->Message( + Chat::White, + fmt::format( + "Client Side Zone Point x [{}] y [{}] z [{}] h [{}] number [{}] | To [{}] ({}) x [{}] y [{}] z [{}] h [{}]", + zone_point->x, + zone_point->y, + zone_point->z, + zone_point->heading, + zone_point->number, + zone_long_name.c_str(), + zone_point->target_zone_id, + zone_point->target_x, + zone_point->target_y, + zone_point->target_z, + zone_point->target_heading + ).c_str() + ); + + iterator.Advance(); + + found_zone_points++; + } + + if (found_zone_points == 0) { + c->Message(Chat::White, "There were no zone points found..."); + } + + c->SendChatLineBreak(); + +} + diff --git a/zone/gm_commands/shutdown.cpp b/zone/gm_commands/shutdown.cpp new file mode 100755 index 000000000..2af0eaf67 --- /dev/null +++ b/zone/gm_commands/shutdown.cpp @@ -0,0 +1,8 @@ +#include "../client.h" +#include "../../world/main.h" + +void command_shutdown(Client *c, const Seperator *sep) +{ + CatchSignal(2); +} + diff --git a/zone/gm_commands/size.cpp b/zone/gm_commands/size.cpp new file mode 100755 index 000000000..3ff412549 --- /dev/null +++ b/zone/gm_commands/size.cpp @@ -0,0 +1,46 @@ +#include "../client.h" + +void command_size(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #size [0 - 255] (Decimal increments are allowed)"); + } + else { + float newsize = atof(sep->arg[1]); + if (newsize > 255) { + c->Message(Chat::White, "Error: #size: Size can not be greater than 255."); + } + else if (newsize < 0) { + c->Message(Chat::White, "Error: #size: Size can not be less than 0."); + } + else if (!target) { + c->Message(Chat::White, "Error: this command requires a target"); + } + else { + uint16 Race = target->GetModel(); + uint8 Gender = target->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = 0xFF; + uint8 HairColor = target->GetHairColor(); + uint8 BeardColor = target->GetBeardColor(); + uint8 EyeColor1 = target->GetEyeColor1(); + uint8 EyeColor2 = target->GetEyeColor2(); + uint8 HairStyle = target->GetHairStyle(); + uint8 LuclinFace = target->GetLuclinFace(); + uint8 Beard = target->GetBeard(); + uint32 DrakkinHeritage = target->GetDrakkinHeritage(); + uint32 DrakkinTattoo = target->GetDrakkinTattoo(); + uint32 DrakkinDetails = target->GetDrakkinDetails(); + + target->SendIllusionPacket( + Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails, newsize + ); + + c->Message(Chat::White, "Size = %f", atof(sep->arg[1])); + } + } +} + diff --git a/zone/gm_commands/spawn.cpp b/zone/gm_commands/spawn.cpp new file mode 100755 index 000000000..2dd42ccd6 --- /dev/null +++ b/zone/gm_commands/spawn.cpp @@ -0,0 +1,29 @@ +#include "../client.h" + +void command_spawn(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] != 0) { + Client *client = entity_list.GetClientByName(sep->arg[1]); + if (client) { + c->Message(Chat::White, "You cannot spawn a mob with the same name as a character!"); + return; + } + } + + NPC *npc = NPC::SpawnNPC(sep->argplus[1], c->GetPosition(), c); + if (!npc) { + c->Message( + Chat::White, + "Format: #spawn name race level material hp gender class priweapon secweapon merchantid bodytype - spawns a npc those parameters." + ); + c->Message( + Chat::White, + "Name Format: NPCFirstname_NPCLastname - All numbers in a name are stripped and \"_\" characters become a space." + ); + c->Message( + Chat::White, + "Note: Using \"-\" for gender will autoselect the gender for the race. Using \"-\" for HP will use the calculated maximum HP." + ); + } +} + diff --git a/zone/gm_commands/spawnfix.cpp b/zone/gm_commands/spawnfix.cpp new file mode 100755 index 000000000..830ae870a --- /dev/null +++ b/zone/gm_commands/spawnfix.cpp @@ -0,0 +1,39 @@ +#include "../client.h" + +void command_spawnfix(Client *c, const Seperator *sep) +{ + Mob *target_mob = c->GetTarget(); + if (!target_mob || !target_mob->IsNPC()) { + c->Message(Chat::White, "Error: #spawnfix: Need an NPC target."); + return; + } + + Spawn2 *s2 = target_mob->CastToNPC()->respawn2; + + if (!s2) { + c->Message( + Chat::White, + "#spawnfix FAILED -- cannot determine which spawn entry in the database this mob came from." + ); + return; + } + + std::string query = StringFormat( + "UPDATE spawn2 SET x = '%f', y = '%f', z = '%f', heading = '%f' WHERE id = '%i'", + c->GetX(), + c->GetY(), + target_mob->GetFixedZ(c->GetPosition()), + c->GetHeading(), + s2->GetID() + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message(Chat::Red, "Update failed! MySQL gave the following error:"); + c->Message(Chat::Red, results.ErrorMessage().c_str()); + return; + } + + c->Message(Chat::White, "Updating coordinates successful."); + target_mob->Depop(false); +} + diff --git a/zone/gm_commands/spawnstatus.cpp b/zone/gm_commands/spawnstatus.cpp new file mode 100755 index 000000000..9c9307bd8 --- /dev/null +++ b/zone/gm_commands/spawnstatus.cpp @@ -0,0 +1,28 @@ +#include "../client.h" + +void command_spawnstatus(Client *c, const Seperator *sep) +{ + if ((sep->arg[1][0] == 'e') | (sep->arg[1][0] == 'E')) { + // show only enabled spawns + zone->ShowEnabledSpawnStatus(c); + } + else if ((sep->arg[1][0] == 'd') | (sep->arg[1][0] == 'D')) { + // show only disabled spawns + zone->ShowDisabledSpawnStatus(c); + } + else if ((sep->arg[1][0] == 'a') | (sep->arg[1][0] == 'A')) { + // show all spawn staus with no filters + zone->SpawnStatus(c); + } + else if (sep->IsNumber(1)) { + // show spawn status by spawn2 id + zone->ShowSpawnStatusByID(c, atoi(sep->arg[1])); + } + else if (strcmp(sep->arg[1], "help") == 0) { + c->Message(Chat::White, "Usage: #spawnstatus <[a]ll | [d]isabled | [e]nabled | {Spawn2 ID}>"); + } + else { + zone->SpawnStatus(c); + } +} + diff --git a/zone/gm_commands/spellinfo.cpp b/zone/gm_commands/spellinfo.cpp new file mode 100755 index 000000000..a40a3be66 --- /dev/null +++ b/zone/gm_commands/spellinfo.cpp @@ -0,0 +1,154 @@ +#include "../client.h" + +void command_spellinfo(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #spellinfo [spell_id]"); + } + else { + short int spell_id = atoi(sep->arg[1]); + const struct SPDat_Spell_Struct *s = &spells[spell_id]; + c->Message(Chat::White, "Spell info for spell #%d:", spell_id); + c->Message(Chat::White, " name: %s", s->name); + c->Message(Chat::White, " player_1: %s", s->player_1); + c->Message(Chat::White, " teleport_zone: %s", s->teleport_zone); + c->Message(Chat::White, " you_cast: %s", s->you_cast); + c->Message(Chat::White, " other_casts: %s", s->other_casts); + c->Message(Chat::White, " cast_on_you: %s", s->cast_on_you); + c->Message(Chat::White, " spell_fades: %s", s->spell_fades); + c->Message(Chat::White, " range: %f", s->range); + c->Message(Chat::White, " aoe_range: %f", s->aoe_range); + c->Message(Chat::White, " push_back: %f", s->push_back); + c->Message(Chat::White, " push_up: %f", s->push_up); + c->Message(Chat::White, " cast_time: %d", s->cast_time); + c->Message(Chat::White, " recovery_time: %d", s->recovery_time); + c->Message(Chat::White, " recast_time: %d", s->recast_time); + c->Message(Chat::White, " buff_duration_formula: %d", s->buff_duration_formula); + c->Message(Chat::White, " buff_duration: %d", s->buff_duration); + c->Message(Chat::White, " AEDuration: %d", s->aoe_duration); + c->Message(Chat::White, " mana: %d", s->mana); + c->Message( + Chat::White, + " base[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", + s->base_value[0], + s->base_value[1], + s->base_value[2], + s->base_value[3], + s->base_value[4], + s->base_value[5], + s->base_value[6], + s->base_value[7], + s->base_value[8], + s->base_value[9], + s->base_value[10], + s->base_value[11] + ); + c->Message( + Chat::White, + " base22[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", + s->limit_value[0], + s->limit_value[1], + s->limit_value[2], + s->limit_value[3], + s->limit_value[4], + s->limit_value[5], + s->limit_value[6], + s->limit_value[7], + s->limit_value[8], + s->limit_value[9], + s->limit_value[10], + s->limit_value[11] + ); + c->Message( + Chat::White, + " max[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", + s->max_value[0], + s->max_value[1], + s->max_value[2], + s->max_value[3], + s->max_value[4], + s->max_value[5], + s->max_value[6], + s->max_value[7], + s->max_value[8], + s->max_value[9], + s->max_value[10], + s->max_value[11] + ); + c->Message( + Chat::White, + " components[4]: %d, %d, %d, %d", + s->component[0], + s->component[1], + s->component[2], + s->component[3] + ); + c->Message( + Chat::White, + " component_counts[4]: %d, %d, %d, %d", + s->component_count[0], + s->component_count[1], + s->component_count[2], + s->component_count[3] + ); + c->Message( + Chat::White, + " NoexpendReagent[4]: %d, %d, %d, %d", + s->no_expend_reagent[0], + s->no_expend_reagent[1], + s->no_expend_reagent[2], + s->no_expend_reagent[3] + ); + c->Message( + Chat::White, + " formula[12]: 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x", + s->formula[0], + s->formula[1], + s->formula[2], + s->formula[3], + s->formula[4], + s->formula[5], + s->formula[6], + s->formula[7], + s->formula[8], + s->formula[9], + s->formula[10], + s->formula[11] + ); + c->Message(Chat::White, " goodEffect: %d", s->good_effect); + c->Message(Chat::White, " Activated: %d", s->activated); + c->Message(Chat::White, " resisttype: %d", s->resist_type); + c->Message( + Chat::White, + " effectid[12]: 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x", + s->effect_id[0], + s->effect_id[1], + s->effect_id[2], + s->effect_id[3], + s->effect_id[4], + s->effect_id[5], + s->effect_id[6], + s->effect_id[7], + s->effect_id[8], + s->effect_id[9], + s->effect_id[10], + s->effect_id[11] + ); + c->Message(Chat::White, " targettype: %d", s->target_type); + c->Message(Chat::White, " basediff: %d", s->base_difficulty); + c->Message(Chat::White, " skill: %d", s->skill); + c->Message(Chat::White, " zonetype: %d", s->zone_type); + c->Message(Chat::White, " EnvironmentType: %d", s->environment_type); + c->Message(Chat::White, " TimeOfDay: %d", s->time_of_day); + c->Message( + Chat::White, " classes[15]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", + s->classes[0], s->classes[1], s->classes[2], s->classes[3], s->classes[4], + s->classes[5], s->classes[6], s->classes[7], s->classes[8], s->classes[9], + s->classes[10], s->classes[11], s->classes[12], s->classes[13], s->classes[14] + ); + c->Message(Chat::White, " CastingAnim: %d", s->casting_animation); + c->Message(Chat::White, " SpellAffectIndex: %d", s->spell_affect_index); + c->Message(Chat::White, " RecourseLink: %d", s->recourse_link); + } +} + diff --git a/zone/gm_commands/stun.cpp b/zone/gm_commands/stun.cpp new file mode 100755 index 000000000..9775a45a3 --- /dev/null +++ b/zone/gm_commands/stun.cpp @@ -0,0 +1,65 @@ +#include "../client.h" + +void command_stun(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #stun [Duration]"); + return; + } + + Mob *target = c; + int duration = static_cast(std::min(std::stoll(sep->arg[1]), (long long) 2000000000)); + + if (duration < 0) { + duration = 0; + } + + if (c->GetTarget()) { + target = c->GetTarget(); + if (target->IsClient()) { + target->CastToClient()->Stun(duration); + } + else if (target->IsNPC()) { + target->CastToNPC()->Stun(duration); + } + } + else { + c->Stun(duration); + } + + std::string stun_message = ( + duration ? + fmt::format( + "You stunned {} for {}.", + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), + ConvertSecondsToTime(duration) + ) : + fmt::format( + "You unstunned {}.", + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ) + ) + ); + c->Message( + Chat::White, + stun_message.c_str() + ); +} + + diff --git a/zone/gm_commands/summon.cpp b/zone/gm_commands/summon.cpp new file mode 100755 index 000000000..586217112 --- /dev/null +++ b/zone/gm_commands/summon.cpp @@ -0,0 +1,97 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +#include "../corpse.h" + +void command_summon(Client *c, const Seperator *sep) +{ + Mob *t; + + if (sep->arg[1][0] != 0) // arg specified + { + Client *client = entity_list.GetClientByName(sep->arg[1]); + if (client != 0) { // found player in zone + t = client->CastToMob(); + } + else { + if (!worldserver.Connected()) { + c->Message(Chat::White, "Error: World server disconnected."); + } + else { // player is in another zone + //Taking this command out until we test the factor of 8 in ServerOP_ZonePlayer + //c->Message(Chat::White, "Summoning player from another zone not yet implemented."); + //return; + + auto pack = new ServerPacket(ServerOP_ZonePlayer, sizeof(ServerZonePlayer_Struct)); + ServerZonePlayer_Struct *szp = (ServerZonePlayer_Struct *) pack->pBuffer; + strcpy(szp->adminname, c->GetName()); + szp->adminrank = c->Admin(); + szp->ignorerestrictions = 2; + strcpy(szp->name, sep->arg[1]); + strcpy(szp->zone, zone->GetShortName()); + szp->x_pos = c->GetX(); // May need to add a factor of 8 in here.. + szp->y_pos = c->GetY(); + szp->z_pos = c->GetZ(); + szp->instance_id = zone->GetInstanceID(); + worldserver.SendPacket(pack); + safe_delete(pack); + } + return; + } + } + else if (c->GetTarget()) { // have target + t = c->GetTarget(); + } + else { + c->Message(Chat::White, "Usage: #summon [charname] Either target or charname is required"); + return; + } + + if (!t) { + return; + } + + if (t->IsNPC()) { // npc target + c->Message( + Chat::White, + "Summoning NPC %s to %1.1f, %1.1f, %1.1f", + t->GetName(), + c->GetX(), + c->GetY(), + c->GetZ()); + t->CastToNPC()->GMMove(c->GetX(), c->GetY(), c->GetZ(), c->GetHeading()); + t->CastToNPC()->SaveGuardSpot(glm::vec4(0.0f)); + } + else if (t->IsCorpse()) { // corpse target + c->Message( + Chat::White, + "Summoning corpse %s to %1.1f, %1.1f, %1.1f", + t->GetName(), + c->GetX(), + c->GetY(), + c->GetZ()); + t->CastToCorpse()->GMMove(c->GetX(), c->GetY(), c->GetZ(), c->GetHeading()); + } + else if (t->IsClient()) { + c->Message( + Chat::White, + "Summoning player %s to %1.1f, %1.1f, %1.1f", + t->GetName(), + c->GetX(), + c->GetY(), + c->GetZ()); + t->CastToClient()->MovePC( + zone->GetZoneID(), + zone->GetInstanceID(), + c->GetX(), + c->GetY(), + c->GetZ(), + c->GetHeading(), + 2, + GMSummon + ); + } +} + diff --git a/zone/gm_commands/summonburiedplayercorpse.cpp b/zone/gm_commands/summonburiedplayercorpse.cpp new file mode 100755 index 000000000..5534ecadc --- /dev/null +++ b/zone/gm_commands/summonburiedplayercorpse.cpp @@ -0,0 +1,28 @@ +#include "../client.h" +#include "../corpse.h" + +void command_summonburiedplayercorpse(Client *c, const Seperator *sep) +{ + Client *t = c; + + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + t = c->GetTarget()->CastToClient(); + } + else { + c->Message(Chat::White, "You must first select a target!"); + return; + } + + Corpse *PlayerCorpse = database.SummonBuriedCharacterCorpses( + t->CharacterID(), + t->GetZoneID(), + zone->GetInstanceID(), + t->GetPosition()); + + if (!PlayerCorpse) { + c->Message(Chat::White, "Your target doesn't have any buried corpses."); + } + + return; +} + diff --git a/zone/gm_commands/summonitem.cpp b/zone/gm_commands/summonitem.cpp new file mode 100755 index 000000000..e3053bd71 --- /dev/null +++ b/zone/gm_commands/summonitem.cpp @@ -0,0 +1,92 @@ +#include "../client.h" + +void command_summonitem(Client *c, const Seperator *sep) +{ + uint32 item_id = 0; + int16 charges = -1; + uint32 augment_one = 0; + uint32 augment_two = 0; + uint32 augment_three = 0; + uint32 augment_four = 0; + uint32 augment_five = 0; + uint32 augment_six = 0; + int arguments = sep->argnum; + std::string cmd_msg = sep->msg; + size_t link_open = cmd_msg.find('\x12'); + size_t link_close = cmd_msg.find_last_of('\x12'); + if (link_open != link_close && (cmd_msg.length() - link_open) > EQ::constants::SAY_LINK_BODY_SIZE) { + EQ::SayLinkBody_Struct link_body; + EQ::saylink::DegenerateLinkBody(link_body, cmd_msg.substr(link_open + 1, EQ::constants::SAY_LINK_BODY_SIZE)); + item_id = link_body.item_id; + augment_one = link_body.augment_1; + augment_two = link_body.augment_2; + augment_three = link_body.augment_3; + augment_four = link_body.augment_4; + augment_five = link_body.augment_5; + augment_six = link_body.augment_6; + } + else if (!sep->IsNumber(1)) { + c->Message( + Chat::White, + "Usage: #summonitem [item id | link] [charges] [augment_one_id] [augment_two_id] [augment_three_id] [augment_four_id] [augment_five_id] [augment_six_id] (Charges are optional.)" + ); + return; + } + else { + item_id = atoi(sep->arg[1]); + } + + if (!item_id) { + c->Message(Chat::White, "Enter a valid item ID."); + return; + } + + uint8 item_status = 0; + uint8 current_status = c->Admin(); + const EQ::ItemData *item = database.GetItem(item_id); + if (item) { + item_status = item->MinStatus; + } + + if (item_status > current_status) { + c->Message( + Chat::White, + fmt::format( + "Insufficient status to summon this item, current status is {}, required status is {}.", + current_status, + item_status + ).c_str() + ); + } + + if (arguments >= 2 && sep->IsNumber(2)) { + charges = atoi(sep->arg[2]); + } + + if (arguments >= 3 && sep->IsNumber(3)) { + augment_one = atoi(sep->arg[3]); + } + + if (arguments >= 4 && sep->IsNumber(4)) { + augment_two = atoi(sep->arg[4]); + } + + if (arguments >= 5 && sep->IsNumber(5)) { + augment_three = atoi(sep->arg[5]); + } + + if (arguments >= 6 && sep->IsNumber(6)) { + augment_four = atoi(sep->arg[6]); + } + + if (arguments >= 7 && sep->IsNumber(7)) { + augment_five = atoi(sep->arg[7]); + } + + if (arguments == 8 && sep->IsNumber(8)) { + augment_six = atoi(sep->arg[8]); + } + + c->SummonItem(item_id, charges, augment_one, augment_two, augment_three, augment_four, augment_five, augment_six); +} + diff --git a/zone/gm_commands/suspend.cpp b/zone/gm_commands/suspend.cpp new file mode 100755 index 000000000..49721507e --- /dev/null +++ b/zone/gm_commands/suspend.cpp @@ -0,0 +1,101 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_suspend(Client *c, const Seperator *sep) +{ + if ((sep->arg[1][0] == 0) || (sep->arg[2][0] == 0)) { + c->Message( + Chat::White, + "Usage: #suspend (Specify 0 days to lift the suspension immediately) " + ); + return; + } + + int duration = atoi(sep->arg[2]); + + if (duration < 0) { + duration = 0; + } + + std::string message; + + if (duration > 0) { + int i = 3; + while (1) { + if (sep->arg[i][0] == 0) { + break; + } + + if (message.length() > 0) { + message.push_back(' '); + } + + message += sep->arg[i]; + ++i; + } + + if (message.length() == 0) { + c->Message( + Chat::White, + "Usage: #suspend (Specify 0 days to lift the suspension immediately) " + ); + return; + } + } + + auto escName = new char[strlen(sep->arg[1]) * 2 + 1]; + database.DoEscapeString(escName, sep->arg[1], strlen(sep->arg[1])); + int accountID = database.GetAccountIDByChar(escName); + safe_delete_array(escName); + + if (accountID <= 0) { + c->Message(Chat::Red, "Character does not exist."); + return; + } + + std::string query = StringFormat( + "UPDATE `account` SET `suspendeduntil` = DATE_ADD(NOW(), INTERVAL %i DAY), " + "suspend_reason = '%s' WHERE `id` = %i", + duration, EscapeString(message).c_str(), accountID + ); + auto results = database.QueryDatabase(query); + + if (duration) { + c->Message( + Chat::Red, + "Account number %i with the character %s has been temporarily suspended for %i day(s).", + accountID, + sep->arg[1], + duration + ); + } + else { + c->Message( + Chat::Red, + "Account number %i with the character %s is no longer suspended.", + accountID, + sep->arg[1] + ); + } + + Client *bannedClient = entity_list.GetClientByName(sep->arg[1]); + + if (bannedClient) { + bannedClient->WorldKick(); + return; + } + + auto pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); + ServerKickPlayer_Struct *sks = (ServerKickPlayer_Struct *) pack->pBuffer; + + strn0cpy(sks->adminname, c->GetName(), sizeof(sks->adminname)); + strn0cpy(sks->name, sep->arg[1], sizeof(sks->name)); + sks->adminrank = c->Admin(); + + worldserver.SendPacket(pack); + + safe_delete(pack); +} + diff --git a/zone/gm_commands/task.cpp b/zone/gm_commands/task.cpp new file mode 100755 index 000000000..a616eb06d --- /dev/null +++ b/zone/gm_commands/task.cpp @@ -0,0 +1,198 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +#include "../../common/shared_tasks.h" + +void command_task(Client *c, const Seperator *sep) +{ + //super-command for managing tasks + if (sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { + c->Message(Chat::White, "Syntax: #task [subcommand]"); + c->Message(Chat::White, "------------------------------------------------"); + c->Message(Chat::White, "# Task System Commands"); + c->Message(Chat::White, "------------------------------------------------"); + c->Message( + Chat::White, + fmt::format( + "--- [{}] List active tasks for a client", + EQ::SayLinkEngine::GenerateQuestSaylink("#task show", false, "show") + ).c_str() + ); + c->Message(Chat::White, "--- update [count] | Updates task"); + c->Message(Chat::White, "--- assign | Assigns task to client"); + c->Message( + Chat::White, + fmt::format( + "--- [{}] Reload all Task information from the database", + EQ::SayLinkEngine::GenerateQuestSaylink("#task reloadall", false, "reloadall") + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "--- [{}] Reload Task and Activity information for a single task", + EQ::SayLinkEngine::GenerateQuestSaylink("#task reload task", false, "reload task") + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "--- [{}] Reload goal/reward list information", + EQ::SayLinkEngine::GenerateQuestSaylink("#task reload lists", false, "reload lists") + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "--- [{}] Reload proximity information", + EQ::SayLinkEngine::GenerateQuestSaylink("#task reload prox", false, "reload prox") + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "--- [{}] Reload task set information", + EQ::SayLinkEngine::GenerateQuestSaylink("#task reload sets", false, "reload sets") + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "--- [{}] Purges targeted characters task timers", + EQ::SayLinkEngine::GenerateQuestSaylink("#task purgetimers", false, "purgetimers") + ).c_str() + ); + + c->Message(Chat::White, "------------------------------------------------"); + c->Message(Chat::White, "# Shared Task System Commands"); + c->Message(Chat::White, "------------------------------------------------"); + c->Message( + Chat::White, + fmt::format( + "--- [{}] Purges all active Shared Tasks in memory and database ", + EQ::SayLinkEngine::GenerateQuestSaylink("#task sharedpurge", false, "sharedpurge") + ).c_str() + ); + + return; + } + + Client *client_target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + client_target = c->GetTarget()->CastToClient(); + } + + if (!strcasecmp(sep->arg[1], "show")) { + c->ShowClientTasks(client_target); + return; + } + + if (!strcasecmp(sep->arg[1], "purgetimers")) { + c->Message(15, fmt::format("{}'s task timers have been purged", client_target->GetCleanName()).c_str()); + if (client_target != c) { + client_target->Message(15, "[GM] Your task timers have been purged by a GM"); + } + client_target->PurgeTaskTimers(); + return; + } + + if (!strcasecmp(sep->arg[1], "update")) { + if (sep->argnum >= 3) { + int task_id = atoi(sep->arg[2]); + int activity_id = atoi(sep->arg[3]); + int count = 1; + + if (sep->argnum >= 4) { + count = atoi(sep->arg[4]); + if (count <= 0) { + count = 1; + } + } + c->Message( + Chat::Yellow, + "Updating Task [%i] Activity [%i] Count [%i] for client [%s]", + task_id, + activity_id, + count, + client_target->GetCleanName() + ); + client_target->UpdateTaskActivity(task_id, activity_id, count); + c->ShowClientTasks(client_target); + } + return; + } + + if (!strcasecmp(sep->arg[1], "sharedpurge")) { + if (!strcasecmp(sep->arg[2], "confirm")) { + LogTasksDetail("Sending purge request"); + auto pack = new ServerPacket(ServerOP_SharedTaskPurgeAllCommand, 0); + worldserver.SendPacket(pack); + safe_delete(pack); + + return; + } + + c->Message( + Chat::White, + fmt::format( + "[WARNING] This will purge all active Shared Tasks [{}]?", + EQ::SayLinkEngine::GenerateQuestSaylink("#task sharedpurge confirm", false, "confirm") + ).c_str() + ); + + return; + } + + if (!strcasecmp(sep->arg[1], "assign")) { + int task_id = atoi(sep->arg[2]); + if ((task_id > 0) && (task_id < MAXTASKS)) { + client_target->AssignTask(task_id, 0, false); + c->Message(Chat::Yellow, "Assigned task [%i] to [%s]", task_id, client_target->GetCleanName()); + } + return; + } + + if (!strcasecmp(sep->arg[1], "reloadall")) { + c->Message(Chat::Yellow, "Sending reloadtasks to world"); + worldserver.SendReloadTasks(RELOADTASKS); + c->Message(Chat::Yellow, "Back again"); + return; + } + + if (!strcasecmp(sep->arg[1], "reload")) { + if (sep->arg[2][0] != '\0') { + if (!strcasecmp(sep->arg[2], "lists")) { + c->Message(Chat::Yellow, "Sending reload lists to world"); + worldserver.SendReloadTasks(RELOADTASKGOALLISTS); + c->Message(Chat::Yellow, "Reloaded"); + return; + } + if (!strcasecmp(sep->arg[2], "prox")) { + c->Message(Chat::Yellow, "Sending reload proximities to world"); + worldserver.SendReloadTasks(RELOADTASKPROXIMITIES); + c->Message(Chat::Yellow, "Reloaded"); + return; + } + if (!strcasecmp(sep->arg[2], "sets")) { + c->Message(Chat::Yellow, "Sending reload task sets to world"); + worldserver.SendReloadTasks(RELOADTASKSETS); + c->Message(Chat::Yellow, "Reloaded"); + return; + } + if (!strcasecmp(sep->arg[2], "task") && (sep->arg[3][0] != '\0')) { + int task_id = atoi(sep->arg[3]); + if ((task_id > 0) && (task_id < MAXTASKS)) { + c->Message(Chat::Yellow, "Sending reload task %i to world", task_id); + worldserver.SendReloadTasks(RELOADTASKS, task_id); + c->Message(Chat::Yellow, "Reloaded"); + return; + } + } + } + + } + c->Message(Chat::White, "Unable to interpret command. Type #task help"); + +} diff --git a/zone/gm_commands/tattoo.cpp b/zone/gm_commands/tattoo.cpp new file mode 100755 index 000000000..8d1433bbe --- /dev/null +++ b/zone/gm_commands/tattoo.cpp @@ -0,0 +1,37 @@ +#include "../client.h" + +void command_tattoo(Client *c, const Seperator *sep) +{ + Mob *target = c->GetTarget(); + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #tattoo [number of Drakkin tattoo]"); + } + else if (!target) { + c->Message(Chat::White, "Error: this command requires a target"); + } + else { + uint16 Race = target->GetRace(); + uint8 Gender = target->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = 0xFF; + uint8 HairColor = target->GetHairColor(); + uint8 BeardColor = target->GetBeardColor(); + uint8 EyeColor1 = target->GetEyeColor1(); + uint8 EyeColor2 = target->GetEyeColor2(); + uint8 HairStyle = target->GetHairStyle(); + uint8 LuclinFace = target->GetLuclinFace(); + uint8 Beard = target->GetBeard(); + uint32 DrakkinHeritage = target->GetDrakkinHeritage(); + uint32 DrakkinTattoo = atoi(sep->arg[1]); + uint32 DrakkinDetails = target->GetDrakkinDetails(); + + target->SendIllusionPacket( + Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails + ); + + c->Message(Chat::White, "Tattoo = %i", atoi(sep->arg[1])); + } +} + diff --git a/zone/gm_commands/tempname.cpp b/zone/gm_commands/tempname.cpp new file mode 100755 index 000000000..849549472 --- /dev/null +++ b/zone/gm_commands/tempname.cpp @@ -0,0 +1,22 @@ +#include "../client.h" + +void command_tempname(Client *c, const Seperator *sep) +{ + Mob *target; + target = c->GetTarget(); + + if (!target) { + c->Message(Chat::White, "Usage: #tempname newname (requires a target)"); + } + else if (strlen(sep->arg[1]) > 0) { + char *oldname = strdup(target->GetName()); + target->TempName(sep->arg[1]); + c->Message(Chat::White, "Renamed %s to %s", oldname, sep->arg[1]); + free(oldname); + } + else { + target->TempName(); + c->Message(Chat::White, "Restored the original name"); + } +} + diff --git a/zone/gm_commands/texture.cpp b/zone/gm_commands/texture.cpp new file mode 100755 index 000000000..d6fdf252c --- /dev/null +++ b/zone/gm_commands/texture.cpp @@ -0,0 +1,52 @@ +#include "../client.h" + +void command_texture(Client *c, const Seperator *sep) +{ + + uint16 texture; + + if (sep->IsNumber(1) && atoi(sep->arg[1]) >= 0 && atoi(sep->arg[1]) <= 255) { + texture = atoi(sep->arg[1]); + uint8 helm = 0xFF; + + // Player Races Wear Armor, so Wearchange is sent instead + int i; + if (!c->GetTarget()) { + for (i = EQ::textures::textureBegin; i <= EQ::textures::LastTintableTexture; i++) { + c->SendTextureWC(i, texture); + } + } + else if ((c->GetTarget()->GetModel() > 0 && c->GetTarget()->GetModel() <= 12) || + c->GetTarget()->GetModel() == 128 || c->GetTarget()->GetModel() == 130 || + c->GetTarget()->GetModel() == 330 || c->GetTarget()->GetModel() == 522) { + for (i = EQ::textures::textureBegin; i <= EQ::textures::LastTintableTexture; i++) { + c->GetTarget()->SendTextureWC(i, texture); + } + } + else // Non-Player Races only need Illusion Packets to be sent for texture + { + if (sep->IsNumber(2) && atoi(sep->arg[2]) >= 0 && atoi(sep->arg[2]) <= 255) { + helm = atoi(sep->arg[2]); + } + else { + helm = texture; + } + + if (texture == 255) { + texture = 0xFFFF; // Should be pulling these from the database instead + helm = 0xFF; + } + + if ((c->GetTarget()) && (c->Admin() >= commandTextureOthers)) { + c->GetTarget()->SendIllusionPacket(c->GetTarget()->GetModel(), 0xFF, texture, helm); + } + else { + c->SendIllusionPacket(c->GetRace(), 0xFF, texture, helm); + } + } + } + else { + c->Message(Chat::White, "Usage: #texture [texture] [helmtexture] (0-255, 255 for show equipment)"); + } +} + diff --git a/zone/gm_commands/time.cpp b/zone/gm_commands/time.cpp new file mode 100755 index 000000000..fd241d75f --- /dev/null +++ b/zone/gm_commands/time.cpp @@ -0,0 +1,32 @@ +#include "../client.h" + +void command_time(Client *c, const Seperator *sep) +{ + char timeMessage[255]; + int minutes = 0; + if (sep->IsNumber(1)) { + if (sep->IsNumber(2)) { + minutes = atoi(sep->arg[2]); + } + c->Message(Chat::Red, "Setting world time to %s:%i (Timezone: 0)...", sep->arg[1], minutes); + zone->SetTime(atoi(sep->arg[1]) + 1, minutes); + LogInfo("{} :: Setting world time to {}:{} (Timezone: 0)...", c->GetCleanName(), sep->arg[1], minutes); + } + else { + c->Message(Chat::Red, "To set the Time: #time HH [MM]"); + TimeOfDay_Struct eqTime; + zone->zone_time.GetCurrentEQTimeOfDay(time(0), &eqTime); + sprintf( + timeMessage, "%02d:%s%d %s (Timezone: %ih %im)", + ((eqTime.hour - 1) % 12) == 0 ? 12 : ((eqTime.hour - 1) % 12), + (eqTime.minute < 10) ? "0" : "", + eqTime.minute, + (eqTime.hour >= 13) ? "pm" : "am", + zone->zone_time.getEQTimeZoneHr(), + zone->zone_time.getEQTimeZoneMin() + ); + c->Message(Chat::Red, "It is now %s.", timeMessage); + LogInfo("Current Time is: {}", timeMessage); + } +} + diff --git a/zone/gm_commands/timers.cpp b/zone/gm_commands/timers.cpp new file mode 100755 index 000000000..d8e74a8b3 --- /dev/null +++ b/zone/gm_commands/timers.cpp @@ -0,0 +1,22 @@ +#include "../client.h" + +void command_timers(Client *c, const Seperator *sep) +{ + if (!c->GetTarget() || !c->GetTarget()->IsClient()) { + c->Message(Chat::White, "Need a player target for timers."); + return; + } + Client *them = c->GetTarget()->CastToClient(); + + std::vector > res; + them->GetPTimers().ToVector(res); + + c->Message(Chat::White, "Timers for target:"); + + int r; + int l = res.size(); + for (r = 0; r < l; r++) { + c->Message(Chat::White, "Timer %d: %d seconds remain.", res[r].first, res[r].second->GetRemainingTime()); + } +} + diff --git a/zone/gm_commands/timezone.cpp b/zone/gm_commands/timezone.cpp new file mode 100755 index 000000000..d4e628cc0 --- /dev/null +++ b/zone/gm_commands/timezone.cpp @@ -0,0 +1,32 @@ +#include "../client.h" + +void command_timezone(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0 && !sep->IsNumber(1)) { + c->Message(Chat::Red, "Usage: #timezone HH [MM]"); + c->Message( + Chat::Red, + "Current timezone is: %ih %im", + zone->zone_time.getEQTimeZoneHr(), + zone->zone_time.getEQTimeZoneMin()); + } + else { + uint8 hours = atoi(sep->arg[1]); + uint8 minutes = atoi(sep->arg[2]); + if (!sep->IsNumber(2)) { + minutes = 0; + } + c->Message(Chat::Red, "Setting timezone to %i h %i m", hours, minutes); + uint32 ntz = (hours * 60) + minutes; + zone->zone_time.setEQTimeZone(ntz); + content_db.SetZoneTZ(zone->GetZoneID(), zone->GetInstanceVersion(), ntz); + + // Update all clients with new TZ. + auto outapp = new EQApplicationPacket(OP_TimeOfDay, sizeof(TimeOfDay_Struct)); + TimeOfDay_Struct *tod = (TimeOfDay_Struct *) outapp->pBuffer; + zone->zone_time.GetCurrentEQTimeOfDay(time(0), tod); + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } +} + diff --git a/zone/gm_commands/title.cpp b/zone/gm_commands/title.cpp new file mode 100755 index 000000000..0be244d44 --- /dev/null +++ b/zone/gm_commands/title.cpp @@ -0,0 +1,65 @@ +#include "../client.h" +#include "../titles.h" + +void command_title(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + c->Message( + Chat::White, + "Usage: #title [remove|text] [1 = Create row in title table] - remove or set title to 'text'" + ); + } + else { + bool Save = (atoi(sep->arg[2]) == 1); + + Mob *target_mob = c->GetTarget(); + if (!target_mob) { + target_mob = c; + } + if (!target_mob->IsClient()) { + c->Message(Chat::Red, "#title only works on players."); + return; + } + Client *t = target_mob->CastToClient(); + + if (strlen(sep->arg[1]) > 31) { + c->Message(Chat::Red, "Title must be 31 characters or less."); + return; + } + + bool removed = false; + if (!strcasecmp(sep->arg[1], "remove")) { + t->SetAATitle(""); + removed = true; + } + else { + for (unsigned int i = 0; i < strlen(sep->arg[1]); i++) + if (sep->arg[1][i] == '_') { + sep->arg[1][i] = ' '; + } + if (!Save) { + t->SetAATitle(sep->arg[1]); + } + else { + title_manager.CreateNewPlayerTitle(t, sep->arg[1]); + } + } + + t->Save(); + + if (removed) { + c->Message(Chat::Red, "%s's title has been removed.", t->GetName(), sep->arg[1]); + if (t != c) { + t->Message(Chat::Red, "Your title has been removed.", sep->arg[1]); + } + } + else { + c->Message(Chat::Red, "%s's title has been changed to '%s'.", t->GetName(), sep->arg[1]); + if (t != c) { + t->Message(Chat::Red, "Your title has been changed to '%s'.", sep->arg[1]); + } + } + } +} + + diff --git a/zone/gm_commands/titlesuffix.cpp b/zone/gm_commands/titlesuffix.cpp new file mode 100755 index 000000000..10f41e563 --- /dev/null +++ b/zone/gm_commands/titlesuffix.cpp @@ -0,0 +1,65 @@ +#include "../client.h" +#include "../titles.h" + +void command_titlesuffix(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + c->Message( + Chat::White, + "Usage: #titlesuffix [remove|text] [1 = create row in title table] - remove or set title suffix to 'text'" + ); + } + else { + bool Save = (atoi(sep->arg[2]) == 1); + + Mob *target_mob = c->GetTarget(); + if (!target_mob) { + target_mob = c; + } + if (!target_mob->IsClient()) { + c->Message(Chat::Red, "#titlesuffix only works on players."); + return; + } + Client *t = target_mob->CastToClient(); + + if (strlen(sep->arg[1]) > 31) { + c->Message(Chat::Red, "Title suffix must be 31 characters or less."); + return; + } + + bool removed = false; + if (!strcasecmp(sep->arg[1], "remove")) { + t->SetTitleSuffix(""); + removed = true; + } + else { + for (unsigned int i = 0; i < strlen(sep->arg[1]); i++) + if (sep->arg[1][i] == '_') { + sep->arg[1][i] = ' '; + } + + if (!Save) { + t->SetTitleSuffix(sep->arg[1]); + } + else { + title_manager.CreateNewPlayerSuffix(t, sep->arg[1]); + } + } + + t->Save(); + + if (removed) { + c->Message(Chat::Red, "%s's title suffix has been removed.", t->GetName(), sep->arg[1]); + if (t != c) { + t->Message(Chat::Red, "Your title suffix has been removed.", sep->arg[1]); + } + } + else { + c->Message(Chat::Red, "%s's title suffix has been changed to '%s'.", t->GetName(), sep->arg[1]); + if (t != c) { + t->Message(Chat::Red, "Your title suffix has been changed to '%s'.", sep->arg[1]); + } + } + } +} + diff --git a/zone/gm_commands/traindisc.cpp b/zone/gm_commands/traindisc.cpp new file mode 100755 index 000000000..1e3226a85 --- /dev/null +++ b/zone/gm_commands/traindisc.cpp @@ -0,0 +1,65 @@ +#include "../client.h" + +void command_traindisc(Client *c, const Seperator *sep) +{ + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + target = c->GetTarget()->CastToClient(); + } + + if (sep->argnum < 1 || !sep->IsNumber(1)) { + c->Message(Chat::White, "FORMAT: #traindisc "); + return; + } + + uint8 rule_max_level = (uint8) RuleI(Character, MaxLevel); + uint8 max_level = (uint8) std::stoi(sep->arg[1]); + uint8 min_level = ( + sep->IsNumber(2) ? + (uint8) + std::stoi(sep->arg[2]) : + 1 + ); // Default to Level 1 if there isn't a 2nd argument + + if (!c->GetGM()) { // Default to Character:MaxLevel if we're not a GM and Level is higher than the max level + if (max_level > rule_max_level) { + max_level = rule_max_level; + } + + if (min_level > rule_max_level) { + min_level = rule_max_level; + } + } + + if (max_level < 1 || min_level < 1) { + c->Message(Chat::White, "ERROR: Level must be greater than or equal to 1."); + return; + } + + if (min_level > max_level) { + c->Message(Chat::White, "ERROR: Minimum Level must be less than or equal to Maximum Level."); + return; + } + + uint16 learned_disciplines = target->LearnDisciplines(min_level, max_level); + if (target != c) { + std::string discipline_message = ( + learned_disciplines > 0 ? + ( + learned_disciplines == 1 ? + "A new discipline" : + fmt::format("{} New disciplines", learned_disciplines) + ) : + "No new disciplines" + ); + c->Message( + Chat::White, + fmt::format( + "{} learned for {}.", + discipline_message, + target->GetCleanName() + ).c_str() + ); + } +} + diff --git a/zone/gm_commands/trapinfo.cpp b/zone/gm_commands/trapinfo.cpp new file mode 100755 index 000000000..6d2d8fcf1 --- /dev/null +++ b/zone/gm_commands/trapinfo.cpp @@ -0,0 +1,7 @@ +#include "../client.h" + +void command_trapinfo(Client *c, const Seperator *sep) +{ + entity_list.GetTrapInfo(c); +} + diff --git a/zone/gm_commands/tune.cpp b/zone/gm_commands/tune.cpp new file mode 100755 index 000000000..e34f9aa49 --- /dev/null +++ b/zone/gm_commands/tune.cpp @@ -0,0 +1,524 @@ +#include "../client.h" + +void command_tune(Client *c, const Seperator *sep) +{ + //Work in progress - Kayen + + if (sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { + c->Message(Chat::White, "Syntax: #tune [subcommand]."); + c->Message(Chat::White, "-- Tune System Commands --"); + c->Message( + Chat::White, + "-- Usage: Returns recommended combat statistical values based on a desired outcome through simulated combat." + ); + c->Message( + Chat::White, + "-- This commmand can answer the following difficult questions whening tunings NPCs and Players." + ); + c->Message( + Chat::White, + "-- Question: What is the average damage mitigation my AC provides against a specific targets attacks?" + ); + c->Message( + Chat::White, + "-- Question: What is amount of AC would I need to add to acheive a specific average damage mitigation agianst specific targets attacks?" + ); + c->Message( + Chat::White, + "-- Question: What is amount of AC would I need to add to my target to acheive a specific average damage mitigation from my attacks?" + ); + c->Message(Chat::White, "-- Question: What is my targets average AC damage mitigation based on my ATK stat?"); + c->Message( + Chat::White, + "-- Question: What is amount of ATK would I need to add to myself to acheive a specific average damage mitigation on my target?" + ); + c->Message( + Chat::White, + "-- Question: What is amount of ATK would I need to add to my target to acheive a specific average AC damage mitigation on myself?" + ); + c->Message(Chat::White, "-- Question: What is my hit chance against a target?"); + c->Message( + Chat::White, + "-- Question: What is the amount of avoidance I need to add to my target to achieve a specific hit chance?" + ); + c->Message( + Chat::White, + "-- Question: What is the amount of accuracy I need to add to my target to achieve a specific chance of hitting me?" + ); + c->Message(Chat::White, "-- Question: ... and many more..."); + c->Message(Chat::White, " "); + c->Message(Chat::White, "...#tune stats [A/D]"); + c->Message( + Chat::White, + "...#tune FindATK [A/D] [pct mitigation] [interval] [loop_max] [AC override] [Info Level]" + ); + c->Message( + Chat::White, + "...#tune FindAC [A/D] [pct mitigation] [interval] [loop_max] [ATK override] [Info Level] " + ); + c->Message( + Chat::White, + "...#tune FindAccuracy [A/D] [hit chance] [interval] [loop_max] [Avoidance override] [Info Level]" + ); + c->Message( + Chat::White, + "...#tune FindAvoidance [A/D] [hit chance] [interval] [loop_max] [Accuracy override] [Info Level] " + ); + c->Message(Chat::White, " "); + c->Message(Chat::White, "-- DETAILS AND EXAMPLES ON USAGE"); + c->Message(Chat::White, " "); + c->Message( + Chat::White, + "...Returns combat statistics, including AC mitigation pct, hit chance, and avoid melee chance for attacker and defender." + ); + c->Message(Chat::White, "...#tune stats [A/D]"); + c->Message(Chat::White, "..."); + c->Message( + Chat::White, + "...Returns recommended ATK adjustment (+/-) on ATTACKER that will result in a specific average AC mitigation pct on DEFENDER. " + ); + c->Message( + Chat::White, + "...#tune FindATK [A/D] [pct mitigation] [interval][loop_max][AC override][Info Level]" + ); + c->Message( + Chat::White, + "...Example: Find the amount of ATK stat I need to add to the targeted NPC so that it hits me for 50 pct damage on average." + ); + c->Message(Chat::White, "...Example: #tune FindATK D 50"); + c->Message(Chat::White, "..."); + c->Message( + Chat::White, + "...Returns recommended AC adjustment(+/-) on DEFENDER for a specific average AC mitigation pct from ATTACKER. " + ); + c->Message( + Chat::White, + "...#tune FindAC [A/D] [pct mitigation] [interval][loop_max][ATK override][Info Level] " + ); + c->Message( + Chat::White, + "...Example: Find the amount of AC stat I need to add to the targeted NPC so that I hit it for 70 pct damage on average." + ); + c->Message(Chat::White, "...Example: #tune FindAC D 70"); + c->Message(Chat::White, "..."); + c->Message( + Chat::White, + "...Returns recommended Accuracy adjustment (+/-) on ATTACKER that will result in a specific hit chance pct on DEFENDER. " + ); + c->Message( + Chat::White, + "...#tune FindAccuracy [A/D] [hit chance] [interval][loop_max][Avoidance override][Info Level]" + ); + c->Message( + Chat::White, + "...Example: Find the amount of Accuracy stat I need to add to the targeted NPC so that it has a 60 pct hit chance against me." + ); + c->Message(Chat::White, "...Example: #tune FindAccuracy D 60"); + c->Message(Chat::White, "..."); + c->Message( + Chat::White, + "...Returns recommended Avoidance adjustment (+/-) on DEFENDER for in a specific hit chance pct from ATTACKER. " + ); + c->Message( + Chat::White, + "...#tune FindAvoidance [A/D] [hit chance] [interval][loop_max][Accuracy override][Info Level] " + ); + c->Message( + Chat::White, + "...Example: Find the amount of Avoidance stat I need to add to the targeted NPC so that I have a 30 pct hit chance against it." + ); + c->Message(Chat::White, "...Example: #tune FindAvoidance D 30"); + c->Message(Chat::White, "... "); + c->Message(Chat::White, "...Usage: [A/D] You must input either A or D."); + c->Message(Chat::White, "...Category [A] : YOU are the ATTACKER. YOUR TARGET is the DEFENDER."); + c->Message(Chat::White, "...Category [D] : YOU are the DEFENDER. YOUR TARGET is the ATTACKER."); + c->Message(Chat::White, "...If TARGET is in combat, DEFENDER is the TARGETs TARGET."); + + c->Message(Chat::White, " "); + + c->Message( + Chat::White, + "-- Warning: The calculations done in this process are intense and can potentially cause zone crashes depending on parameters set, use with caution!" + ); + c->Message(Chat::White, "-- Below are OPTIONAL parameters."); + c->Message( + Chat::White, + "-- Note: [interval] Determines how much the stat being checked increases/decreases till it finds the best result. Lower is more accurate. Default=10" + ); + c->Message( + Chat::White, + "-- Note: [loop_max] Determines how many iterations are done to increases/decreases the stat till it finds the best result. Higher is more accurate. Default=1000" + ); + c->Message( + Chat::White, + "-- Note: [Stat Override] Will override that stat on mob being checked with the specified value. Default=0" + ); + c->Message( + Chat::White, + "-- Example: If as the attacker you want to find the ATK value you would need to have agianst a target with 1000 AC to achieve an average AC mitigation of 50 pct." + ); + c->Message(Chat::White, "-- Example: #tune FindATK A 50 0 0 1000"); + c->Message(Chat::White, "-- Note: [Info Level] How much parsing detail is displayed[0 - 1]. Default: [0] "); + c->Message(Chat::White, " "); + + return; + } + /* + Category A: YOU are the attacker and your target is the defender + Category D: YOU are the defender and your target is the attacker + */ + + Mob *attacker = c; + Mob *defender = c->GetTarget(); + + if (!defender) { + c->Message(Chat::White, "[#Tune] - Error no target selected. [#Tune help]"); + return; + } + + //Use if checkings on engaged targets. + Mob *ttarget = attacker->GetTarget(); + if (ttarget) { + defender = ttarget; + } + + if (!strcasecmp(sep->arg[1], "stats")) { + + if (!strcasecmp(sep->arg[2], "A")) { + c->TuneGetStats(defender, attacker); + } + else if (!strcasecmp(sep->arg[2], "D")) { + c->TuneGetStats(attacker, defender); + } + else { + c->TuneGetStats(defender, attacker); + } + return; + } + + if (!strcasecmp(sep->arg[1], "FindATK")) { + float pct_mitigation = atof(sep->arg[3]); + int interval = atoi(sep->arg[4]); + int max_loop = atoi(sep->arg[5]); + int ac_override = atoi(sep->arg[6]); + int info_level = atoi(sep->arg[7]); + + if (!pct_mitigation) { + c->Message(Chat::White, "[#Tune] - Error must enter the desired percent mitigation on defender."); + c->Message( + Chat::White, + "...Returns recommended ATK adjustment (+/-) on ATTACKER that will result in a specific average AC mitigation pct on DEFENDER. " + ); + c->Message( + Chat::White, + "...#tune FindATK [A/D] [pct mitigation] [interval][loop_max][AC override][Info Level]" + ); + c->Message( + Chat::White, + "...Example: Find the amount of ATK stat I need to add to the targeted NPC so that it hits me for 50 pct damage on average." + ); + c->Message(Chat::White, "...Example: #tune FindATK D 50"); + return; + } + + if (!interval) { + interval = 10; + } + if (!max_loop) { + max_loop = 1000; + } + if (!ac_override) { + ac_override = 0; + } + if (!info_level) { + info_level = 0; + } + + if (!strcasecmp(sep->arg[2], "A")) { + c->TuneGetATKByPctMitigation( + defender, + attacker, + pct_mitigation, + interval, + max_loop, + ac_override, + info_level + ); + } + else if (!strcasecmp(sep->arg[2], "D")) { + c->TuneGetATKByPctMitigation( + attacker, + defender, + pct_mitigation, + interval, + max_loop, + ac_override, + info_level + ); + } + else { + c->Message(Chat::White, "#Tune - Error no category selcted. [#Tune help]"); + c->Message( + Chat::White, + "Usage #tune FindATK [A/B] [pct mitigation] [interval][loop_max][AC Overwride][Info Level] " + ); + c->Message(Chat::White, "...Usage: [A/D] You must input either A or D."); + c->Message(Chat::White, "...Category [A] : YOU are the ATTACKER. YOUR TARGET is the DEFENDER."); + c->Message(Chat::White, "...Category [D] : YOU are the DEFENDER. YOUR TARGET is the ATTACKER."); + c->Message(Chat::White, "...If TARGET is in combat, DEFENDER is the TARGETs TARGET."); + c->Message(Chat::White, "... "); + c->Message( + Chat::White, + "...Example: Find the amount of ATK stat I need to add to the targeted NPC so that it hits me for 50 pct damage on average." + ); + c->Message(Chat::White, "...Example: #tune FindATK D 50"); + } + return; + } + + if (!strcasecmp(sep->arg[1], "FindAC")) { + float pct_mitigation = atof(sep->arg[3]); + int interval = atoi(sep->arg[4]); + int max_loop = atoi(sep->arg[5]); + int atk_override = atoi(sep->arg[6]); + int info_level = atoi(sep->arg[7]); + + if (!pct_mitigation) { + c->Message(Chat::White, "#Tune - Error must enter the desired percent mitigation on defender."); + c->Message( + Chat::White, + "...Returns recommended AC adjustment(+/-) on DEFENDER for a specific average AC mitigation pct from ATTACKER. " + ); + c->Message( + Chat::White, + "...#tune FindAC [A/D] [pct mitigation] [interval][loop_max][ATK override][Info Level] " + ); + c->Message( + Chat::White, + "...Example: Find the amount of AC stat I need to add to the targeted NPC so that I hit it for 70 pct damage on average." + ); + c->Message(Chat::White, "...Example: #tune FindAC D 70"); + return; + } + + if (!interval) { + interval = 10; + } + if (!max_loop) { + max_loop = 1000; + } + if (!atk_override) { + atk_override = 0; + } + if (!info_level) { + info_level = 0; + } + + if (!strcasecmp(sep->arg[2], "A")) { + c->TuneGetACByPctMitigation( + defender, + attacker, + pct_mitigation, + interval, + max_loop, + atk_override, + info_level + ); + } + else if (!strcasecmp(sep->arg[2], "D")) { + c->TuneGetACByPctMitigation( + attacker, + defender, + pct_mitigation, + interval, + max_loop, + atk_override, + info_level + ); + } + else { + c->Message(Chat::White, "#Tune - Error no category selcted. [#Tune help]"); + c->Message( + Chat::White, + "Usage #tune FindATK [A/B] [pct mitigation] [interval][loop_max][AC Overwride][Info Level] " + ); + c->Message(Chat::White, "...Usage: [A/D] You must input either A or D."); + c->Message(Chat::White, "...Category [A] : YOU are the ATTACKER. YOUR TARGET is the DEFENDER."); + c->Message(Chat::White, "...Category [D] : YOU are the DEFENDER. YOUR TARGET is the ATTACKER."); + c->Message(Chat::White, "...If TARGET is in combat, DEFENDER is the TARGETs TARGET."); + c->Message(Chat::White, "... "); + c->Message( + Chat::White, + "...Example: Find the amount of AC stat I need to add to the targeted NPC so that I hit it for 70 pct damage on average." + ); + c->Message(Chat::White, "...Example: #tune FindAC D 70"); + } + + return; + } + + if (!strcasecmp(sep->arg[1], "FindAccuracy")) { + float hit_chance = atof(sep->arg[3]); + int interval = atoi(sep->arg[4]); + int max_loop = atoi(sep->arg[5]); + int avoid_override = atoi(sep->arg[6]); + int info_level = atoi(sep->arg[7]); + + if (!hit_chance) { + c->Message(Chat::White, "#Tune - Error must enter the desired hit chance on defender."); + c->Message( + Chat::White, + "...Returns recommended Accuracy adjustment (+/-) on ATTACKER that will result in a specific hit chance pct on DEFENDER. " + ); + c->Message( + Chat::White, + "...#tune FindAccuracy [A/D] [hit chance] [interval][loop_max][Avoidance override][Info Level]" + ); + c->Message( + Chat::White, + "...Example: Find the amount of Accuracy stat I need to add to the targeted NPC so that it has a 60 pct hit chance against me." + ); + c->Message(Chat::White, "...Example: #tune FindAccuracy D 60"); + return; + } + + if (!interval) { + interval = 10; + } + if (!max_loop) { + max_loop = 1000; + } + if (!avoid_override) { + avoid_override = 0; + } + if (!info_level) { + info_level = 0; + } + + if (!strcasecmp(sep->arg[2], "A")) { + c->TuneGetAccuracyByHitChance( + defender, + attacker, + hit_chance, + interval, + max_loop, + avoid_override, + info_level + ); + } + else if (!strcasecmp(sep->arg[2], "D")) { + c->TuneGetAccuracyByHitChance( + attacker, + defender, + hit_chance, + interval, + max_loop, + avoid_override, + info_level + ); + } + else { + c->Message(Chat::White, "#Tune - Error no category selcted. [#Tune help]"); + c->Message( + Chat::White, + "...#tune FindAccuracy [A/D] [hit chance] [interval][loop_max][Avoidance override][Info Level]" + ); + c->Message(Chat::White, "...Usage: [A/D] You must input either A or D."); + c->Message(Chat::White, "...Category [A] : YOU are the ATTACKER. YOUR TARGET is the DEFENDER."); + c->Message(Chat::White, "...Category [D] : YOU are the DEFENDER. YOUR TARGET is the ATTACKER."); + c->Message(Chat::White, "...If TARGET is in combat, DEFENDER is the TARGETs TARGET."); + c->Message(Chat::White, "... "); + c->Message( + Chat::White, + "...Example: Find the amount of Accuracy stat I need to add to the targeted NPC so that it has a 60 pct hit chance against me." + ); + c->Message(Chat::White, "...Example: #tune FindAccuracy D 60"); + } + + return; + } + + if (!strcasecmp(sep->arg[1], "FindAvoidance")) { + float hit_chance = atof(sep->arg[3]); + int interval = atoi(sep->arg[4]); + int max_loop = atoi(sep->arg[5]); + int acc_override = atoi(sep->arg[6]); + int info_level = atoi(sep->arg[7]); + + if (!hit_chance) { + c->Message(Chat::White, "#Tune - Error must enter the desired hit chance on defender."); + c->Message( + Chat::White, + "...Returns recommended Avoidance adjustment (+/-) on DEFENDER for in a specific hit chance pct from ATTACKER. " + ); + c->Message( + Chat::White, + "...#tune FindAvoidance [A/D] [hit chance] [interval][loop_max][Accuracy override][Info Level] " + ); + c->Message( + Chat::White, + "...Example: Find the amount of Avoidance stat I need to add to the targeted NPC so that I have a 30 pct hit chance against it." + ); + c->Message(Chat::White, "...Example: #tune FindAvoidance D 30"); + return; + } + if (!interval) { + interval = 10; + } + if (!max_loop) { + max_loop = 1000; + } + if (!acc_override) { + acc_override = 0; + } + if (!info_level) { + info_level = 0; + } + + if (!strcasecmp(sep->arg[2], "A")) { + c->TuneGetAvoidanceByHitChance( + defender, + attacker, + hit_chance, + interval, + max_loop, + acc_override, + info_level + ); + } + else if (!strcasecmp(sep->arg[2], "D")) { + c->TuneGetAvoidanceByHitChance( + attacker, + defender, + hit_chance, + interval, + max_loop, + acc_override, + info_level + ); + } + else { + c->Message(Chat::White, "#Tune - Error no category selcted. [#Tune help]"); + c->Message( + Chat::White, + "...#tune FindAvoidance [A/D] [hit chance] [interval][loop_max][Accuracy override][Info Level] " + ); + c->Message(Chat::White, "...Usage: [A/D] You must input either A or D."); + c->Message(Chat::White, "...Category [A] : YOU are the ATTACKER. YOUR TARGET is the DEFENDER."); + c->Message(Chat::White, "...Category [D] : YOU are the DEFENDER. YOUR TARGET is the ATTACKER."); + c->Message(Chat::White, "...If TARGET is in combat, DEFENDER is the TARGETs TARGET."); + c->Message(Chat::White, "... "); + c->Message( + Chat::White, + "...Example: Find the amount of Avoidance stat I need to add to the targeted NPC so that I have a 30 pct hit chance against it." + ); + c->Message(Chat::White, "...Example: #tune FindAvoidance D 30"); + } + + return; + } + + c->Message(Chat::White, "#Tune - Error no command [#Tune help]"); + return; +} + diff --git a/zone/gm_commands/ucs.cpp b/zone/gm_commands/ucs.cpp new file mode 100755 index 000000000..6d7d65f6a --- /dev/null +++ b/zone/gm_commands/ucs.cpp @@ -0,0 +1,89 @@ +#include "../client.h" + +void command_ucs(Client *c, const Seperator *sep) +{ + if (!c) { + return; + } + + LogInfo("Character [{}] attempting ucs reconnect while ucs server is [{}] available", + c->GetName(), (zone->IsUCSServerAvailable() ? "" : "un")); + + if (zone->IsUCSServerAvailable()) { + EQApplicationPacket *outapp = nullptr; + std::string buffer; + + std::string MailKey = database.GetMailKey(c->CharacterID(), true); + EQ::versions::UCSVersion ConnectionType = EQ::versions::ucsUnknown; + + // chat server packet + switch (c->ClientVersion()) { + case EQ::versions::ClientVersion::Titanium: + ConnectionType = EQ::versions::ucsTitaniumChat; + break; + case EQ::versions::ClientVersion::SoF: + ConnectionType = EQ::versions::ucsSoFCombined; + break; + case EQ::versions::ClientVersion::SoD: + ConnectionType = EQ::versions::ucsSoDCombined; + break; + case EQ::versions::ClientVersion::UF: + ConnectionType = EQ::versions::ucsUFCombined; + break; + case EQ::versions::ClientVersion::RoF: + ConnectionType = EQ::versions::ucsRoFCombined; + break; + case EQ::versions::ClientVersion::RoF2: + ConnectionType = EQ::versions::ucsRoF2Combined; + break; + default: + ConnectionType = EQ::versions::ucsUnknown; + break; + } + + buffer = StringFormat( + "%s,%i,%s.%s,%c%s", + Config->ChatHost.c_str(), + Config->ChatPort, + Config->ShortName.c_str(), + c->GetName(), + ConnectionType, + MailKey.c_str() + ); + + outapp = new EQApplicationPacket(OP_SetChatServer, (buffer.length() + 1)); + memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); + outapp->pBuffer[buffer.length()] = '\0'; + + c->QueuePacket(outapp); + safe_delete(outapp); + + // mail server packet + switch (c->ClientVersion()) { + case EQ::versions::ClientVersion::Titanium: + ConnectionType = EQ::versions::ucsTitaniumMail; + break; + default: + // retain value from previous switch + break; + } + + buffer = StringFormat( + "%s,%i,%s.%s,%c%s", + Config->MailHost.c_str(), + Config->MailPort, + Config->ShortName.c_str(), + c->GetName(), + ConnectionType, + MailKey.c_str() + ); + + outapp = new EQApplicationPacket(OP_SetChatServer2, (buffer.length() + 1)); + memcpy(outapp->pBuffer, buffer.c_str(), buffer.length()); + outapp->pBuffer[buffer.length()] = '\0'; + + c->QueuePacket(outapp); + safe_delete(outapp); + } +} + diff --git a/zone/gm_commands/undye.cpp b/zone/gm_commands/undye.cpp new file mode 100755 index 000000000..af07be7a7 --- /dev/null +++ b/zone/gm_commands/undye.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +void command_undye(Client *c, const Seperator *sep) +{ + if (c->GetTarget() && c->GetTarget()->IsClient()) { + c->GetTarget()->CastToClient()->Undye(); + } + else { + c->Message(Chat::White, "ERROR: Client target required"); + } +} + diff --git a/zone/gm_commands/undyeme.cpp b/zone/gm_commands/undyeme.cpp new file mode 100755 index 000000000..b2e451202 --- /dev/null +++ b/zone/gm_commands/undyeme.cpp @@ -0,0 +1,10 @@ +#include "../client.h" + +void command_undyeme(Client *c, const Seperator *sep) +{ + if (c) { + c->Undye(); + c->Message(Chat::Red, "Dye removed from all slots. Please zone for the process to complete."); + } +} + diff --git a/zone/gm_commands/unfreeze.cpp b/zone/gm_commands/unfreeze.cpp new file mode 100755 index 000000000..9066af500 --- /dev/null +++ b/zone/gm_commands/unfreeze.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +void command_unfreeze(Client *c, const Seperator *sep) +{ + if (c->GetTarget() != 0) { + c->GetTarget()->SendAppearancePacket(AT_Anim, ANIM_STAND); + } + else { + c->Message(Chat::White, "ERROR: Unfreeze requires a target."); + } +} + diff --git a/zone/gm_commands/unlock.cpp b/zone/gm_commands/unlock.cpp new file mode 100755 index 000000000..2d35220e6 --- /dev/null +++ b/zone/gm_commands/unlock.cpp @@ -0,0 +1,15 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_unlock(Client *c, const Seperator *sep) +{ + auto outpack = new ServerPacket(ServerOP_Lock, sizeof(ServerLock_Struct)); + ServerLock_Struct *lss = (ServerLock_Struct *) outpack->pBuffer; + strcpy(lss->myname, c->GetName()); + lss->mode = 0; + worldserver.SendPacket(outpack); + safe_delete(outpack); +} + diff --git a/zone/gm_commands/unscribespell.cpp b/zone/gm_commands/unscribespell.cpp new file mode 100755 index 000000000..2c66bea8f --- /dev/null +++ b/zone/gm_commands/unscribespell.cpp @@ -0,0 +1,62 @@ +#include "../client.h" + +void command_unscribespell(Client *c, const Seperator *sep) +{ + uint16 spell_id = 0; + uint16 book_slot = -1; + Client *t = c; + + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + t = c->GetTarget()->CastToClient(); + } + + if (!sep->arg[1][0]) { + c->Message(Chat::White, "FORMAT: #unscribespell "); + return; + } + + spell_id = atoi(sep->arg[1]); + + if (IsValidSpell(spell_id)) { + book_slot = t->FindSpellBookSlotBySpellID(spell_id); + + if (book_slot >= 0) { + t->UnscribeSpell(book_slot); + + t->Message(Chat::White, "Unscribing spell: %s (%i) from spellbook.", spells[spell_id].name, spell_id); + + if (t != c) { + c->Message( + Chat::White, + "Unscribing spell: %s (%i) for %s.", + spells[spell_id].name, + spell_id, + t->GetName()); + } + + LogInfo("Unscribe spell: [{}] ([{}]) request for [{}] from [{}]", + spells[spell_id].name, + spell_id, + t->GetName(), + c->GetName()); + } + else { + t->Message( + Chat::Red, + "Unable to unscribe spell: %s (%i) from your spellbook. This spell is not scribed.", + spells[spell_id].name, + spell_id + ); + + if (t != c) { + c->Message( + Chat::Red, + "Unable to unscribe spell: %s (%i) for %s due to spell not scribed.", + spells[spell_id].name, + spell_id, + t->GetName()); + } + } + } +} + diff --git a/zone/gm_commands/unscribespells.cpp b/zone/gm_commands/unscribespells.cpp new file mode 100755 index 000000000..7b4b93211 --- /dev/null +++ b/zone/gm_commands/unscribespells.cpp @@ -0,0 +1,13 @@ +#include "../client.h" + +void command_unscribespells(Client *c, const Seperator *sep) +{ + Client *t = c; + + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + t = c->GetTarget()->CastToClient(); + } + + t->UnscribeSpellAll(); +} + diff --git a/zone/gm_commands/untraindisc.cpp b/zone/gm_commands/untraindisc.cpp new file mode 100755 index 000000000..c5d6ca1f2 --- /dev/null +++ b/zone/gm_commands/untraindisc.cpp @@ -0,0 +1,17 @@ +#include "../client.h" + +void command_untraindisc(Client *c, const Seperator *sep) +{ + Client *t = c; + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + t = c->GetTarget()->CastToClient(); + } + + for (int i = 0; i < MAX_PP_DISCIPLINES; i++) { + if (t->GetPP().disciplines.values[i] == atoi(sep->arg[1])) { + t->UntrainDisc(i, 1); + return; + } + } +} + diff --git a/zone/gm_commands/untraindiscs.cpp b/zone/gm_commands/untraindiscs.cpp new file mode 100755 index 000000000..8058c6e76 --- /dev/null +++ b/zone/gm_commands/untraindiscs.cpp @@ -0,0 +1,13 @@ +#include "../client.h" + +void command_untraindiscs(Client *c, const Seperator *sep) +{ + Client *t = c; + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + t = c->GetTarget()->CastToClient(); + } + + t->UntrainDiscAll(); + t->Message(Chat::Yellow, "All disciplines removed."); +} + diff --git a/zone/gm_commands/uptime.cpp b/zone/gm_commands/uptime.cpp new file mode 100755 index 000000000..3c1c7bfbc --- /dev/null +++ b/zone/gm_commands/uptime.cpp @@ -0,0 +1,22 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_uptime(Client *c, const Seperator *sep) +{ + if (!worldserver.Connected()) { + c->Message(Chat::White, "Error: World server disconnected"); + } + else { + auto pack = new ServerPacket(ServerOP_Uptime, sizeof(ServerUptime_Struct)); + ServerUptime_Struct *sus = (ServerUptime_Struct *) pack->pBuffer; + strcpy(sus->adminname, c->GetName()); + if (sep->IsNumber(1) && atoi(sep->arg[1]) > 0) { + sus->zoneserverid = atoi(sep->arg[1]); + } + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + diff --git a/zone/gm_commands/version.cpp b/zone/gm_commands/version.cpp new file mode 100755 index 000000000..ecb9b74d4 --- /dev/null +++ b/zone/gm_commands/version.cpp @@ -0,0 +1,10 @@ +#include "../client.h" + +void command_version(Client *c, const Seperator *sep) +{ + c->Message(Chat::White, "Current version information."); + c->Message(Chat::White, " %s", CURRENT_VERSION); + c->Message(Chat::White, " Compiled on: %s at %s", COMPILE_DATE, COMPILE_TIME); + c->Message(Chat::White, " Last modified on: %s", LAST_MODIFIED); +} + diff --git a/zone/gm_commands/viewnpctype.cpp b/zone/gm_commands/viewnpctype.cpp new file mode 100755 index 000000000..70447c145 --- /dev/null +++ b/zone/gm_commands/viewnpctype.cpp @@ -0,0 +1,31 @@ +#include "../client.h" + +void command_viewnpctype(Client *c, const Seperator *sep) +{ + if (sep->IsNumber(1)) { + uint32 npc_id = std::stoul(sep->arg[1]); + const NPCType *npc_type_data = content_db.LoadNPCTypesData(npc_id); + if (npc_type_data) { + auto npc = new NPC( + npc_type_data, + nullptr, + c->GetPosition(), + GravityBehavior::Water + ); + npc->ShowStats(c); + } + else { + c->Message( + Chat::White, + fmt::format( + "NPC ID {} was not found.", + npc_id + ).c_str() + ); + } + } + else { + c->Message(Chat::White, "Usage: #viewnpctype [NPC ID]"); + } +} + diff --git a/zone/gm_commands/viewpetition.cpp b/zone/gm_commands/viewpetition.cpp new file mode 100755 index 000000000..a59821a81 --- /dev/null +++ b/zone/gm_commands/viewpetition.cpp @@ -0,0 +1,31 @@ +#include "../client.h" + +void command_viewpetition(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #viewpetition (petition number) Type #listpetition for a list"); + return; + } + + c->Message(Chat::Red, " ID : Character Name , Petition Text"); + + std::string query = "SELECT petid, charname, petitiontext FROM petitions ORDER BY petid"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { + return; + } + + LogInfo("View petition request from [{}], petition number: [{}]", c->GetName(), atoi(sep->argplus[1])); + + if (results.RowCount() == 0) { + c->Message(Chat::Red, "There was an error in your request: ID not found! Please check the Id and try again."); + return; + } + + for (auto row = results.begin(); row != results.end(); ++row) + if (strcasecmp(row[0], sep->argplus[1]) == 0) { + c->Message(Chat::Yellow, " %s: %s , %s ", row[0], row[1], row[2]); + } + +} + diff --git a/zone/gm_commands/viewzoneloot.cpp b/zone/gm_commands/viewzoneloot.cpp new file mode 100755 index 000000000..89518750a --- /dev/null +++ b/zone/gm_commands/viewzoneloot.cpp @@ -0,0 +1,117 @@ +#include "../client.h" + +void command_viewzoneloot(Client *c, const Seperator *sep) +{ + std::map zone_loot_list; + auto npc_list = entity_list.GetNPCList(); + uint32 loot_amount = 0, loot_id = 1, search_item_id = 0; + if (sep->argnum == 1 && sep->IsNumber(1)) { + search_item_id = atoi(sep->arg[1]); + } + else if (sep->argnum == 1 && !sep->IsNumber(1)) { + c->Message( + Chat::Yellow, + "Usage: #viewzoneloot [item id]" + ); + return; + } + + for (auto npc_entity : npc_list) { + auto current_npc_item_list = npc_entity.second->GetItemList(); + zone_loot_list.insert({npc_entity.second->GetID(), current_npc_item_list}); + } + + for (auto loot_item : zone_loot_list) { + uint32 current_entity_id = loot_item.first; + auto current_item_list = loot_item.second; + auto current_npc = entity_list.GetNPCByID(current_entity_id); + std::string npc_link; + if (current_npc) { + std::string npc_name = current_npc->GetCleanName(); + uint32 instance_id = zone->GetInstanceID(); + uint32 zone_id = zone->GetZoneID(); + std::string command_link = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format( + "#{} {} {} {} {}", + (instance_id != 0 ? "zoneinstance" : "zone"), + (instance_id != 0 ? instance_id : zone_id), + current_npc->GetX(), + current_npc->GetY(), + current_npc->GetZ() + ), + false, + "Goto" + ); + npc_link = fmt::format( + " NPC: {} (ID {}) [{}]", + npc_name, + current_entity_id, + command_link + ); + } + + for (auto current_item : current_item_list) { + if (search_item_id == 0 || current_item->item_id == search_item_id) { + EQ::SayLinkEngine linker; + linker.SetLinkType(EQ::saylink::SayLinkLootItem); + linker.SetLootData(current_item); + c->Message( + Chat::White, + fmt::format( + "{}. {} ({}){}", + loot_id, + linker.GenerateLink(), + current_item->item_id, + npc_link + ).c_str() + ); + loot_id++; + loot_amount++; + } + } + } + + + if (search_item_id != 0) { + std::string drop_string = ( + loot_amount > 0 ? + fmt::format( + "dropping in {} {}", + loot_amount, + (loot_amount > 1 ? "places" : "place") + ) : + "not dropping" + ); + + c->Message( + Chat::White, + fmt::format( + "{} ({}) is {}.", + database.CreateItemLink(search_item_id), + search_item_id, + drop_string + ).c_str() + ); + } + else { + std::string drop_string = ( + loot_amount > 0 ? + fmt::format( + "{} {} dropping", + (loot_amount > 1 ? "items" : "item"), + (loot_amount > 1 ? "are" : "is") + ) : + "items are dropping" + ); + + c->Message( + Chat::White, + fmt::format( + "{} {}.", + loot_amount, + drop_string + ).c_str() + ); + } +} + diff --git a/zone/gm_commands/wc.cpp b/zone/gm_commands/wc.cpp new file mode 100755 index 000000000..0ec247aca --- /dev/null +++ b/zone/gm_commands/wc.cpp @@ -0,0 +1,44 @@ +#include "../client.h" + +void command_wc(Client *c, const Seperator *sep) +{ + if (sep->argnum < 2) { + c->Message( + 0, + "Usage: #wc [wear slot] [material] [ [hero_forge_model] [elite_material] [unknown06] [unknown18] ]" + ); + } + else if (c->GetTarget() == nullptr) { + c->Message(Chat::Red, "You must have a target to do a wear change."); + } + else { + uint32 hero_forge_model = 0; + uint32 wearslot = atoi(sep->arg[1]); + + // Hero Forge + if (sep->argnum > 2) { + hero_forge_model = atoi(sep->arg[3]); + + if (hero_forge_model != 0 && hero_forge_model < 1000) { + // Shorthand Hero Forge ID. Otherwise use the value the user entered. + hero_forge_model = (hero_forge_model * 100) + wearslot; + } + } + /* + // Leaving here to add color option to the #wc command eventually + uint32 Color; + if (c->GetTarget()->IsClient()) + Color = c->GetTarget()->GetEquipmentColor(atoi(sep->arg[1])); + else + Color = c->GetTarget()->GetArmorTint(atoi(sep->arg[1])); + */ + c->GetTarget()->SendTextureWC( + wearslot, + atoi(sep->arg[2]), + hero_forge_model, + atoi(sep->arg[4]), + atoi(sep->arg[5]), + atoi(sep->arg[6])); + } +} + diff --git a/zone/gm_commands/weather.cpp b/zone/gm_commands/weather.cpp new file mode 100755 index 000000000..2590c51e3 --- /dev/null +++ b/zone/gm_commands/weather.cpp @@ -0,0 +1,70 @@ +#include "../client.h" + +void command_weather(Client *c, const Seperator *sep) +{ + if (!(sep->arg[1][0] == '0' || sep->arg[1][0] == '1' || sep->arg[1][0] == '2' || sep->arg[1][0] == '3')) { + c->Message(Chat::White, "Usage: #weather <0/1/2/3> - Off/Rain/Snow/Manual."); + } + else if (zone->zone_weather == 0) { + if (sep->arg[1][0] == + '3') { // Put in modifications here because it had a very good chance at screwing up the client's weather system if rain was sent during snow -T7 + if (sep->arg[2][0] != 0 && sep->arg[3][0] != 0) { + c->Message(Chat::White, "Sending weather packet... TYPE=%s, INTENSITY=%s", sep->arg[2], sep->arg[3]); + zone->zone_weather = atoi(sep->arg[2]); + auto outapp = new EQApplicationPacket(OP_Weather, 8); + outapp->pBuffer[0] = atoi(sep->arg[2]); + outapp->pBuffer[4] = atoi(sep->arg[3]); // This number changes in the packets, intensity? + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } + else { + c->Message(Chat::White, "Manual Usage: #weather 3 "); + } + } + else if (sep->arg[1][0] == '2') { + entity_list.Message(0, 0, "Snowflakes begin to fall from the sky."); + zone->zone_weather = 2; + auto outapp = new EQApplicationPacket(OP_Weather, 8); + outapp->pBuffer[0] = 0x01; + outapp->pBuffer[4] = 0x02; // This number changes in the packets, intensity? + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } + else if (sep->arg[1][0] == '1') { + entity_list.Message(0, 0, "Raindrops begin to fall from the sky."); + zone->zone_weather = 1; + auto outapp = new EQApplicationPacket(OP_Weather, 8); + outapp->pBuffer[4] = 0x01; // This is how it's done in Fear, and you can see a decent distance with it at this value + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } + } + else { + if (zone->zone_weather == 1) { // Doing this because if you have rain/snow on, you can only turn one off. + entity_list.Message(0, 0, "The sky clears as the rain ceases to fall."); + zone->zone_weather = 0; + auto outapp = new EQApplicationPacket(OP_Weather, 8); + // To shutoff weather you send an empty 8 byte packet (You get this everytime you zone even if the sky is clear) + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } + else if (zone->zone_weather == 2) { + entity_list.Message(0, 0, "The sky clears as the snow stops falling."); + zone->zone_weather = 0; + auto outapp = new EQApplicationPacket(OP_Weather, 8); + // To shutoff weather you send an empty 8 byte packet (You get this everytime you zone even if the sky is clear) + outapp->pBuffer[0] = 0x01; // Snow has it's own shutoff packet + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } + else { + entity_list.Message(0, 0, "The sky clears."); + zone->zone_weather = 0; + auto outapp = new EQApplicationPacket(OP_Weather, 8); + // To shutoff weather you send an empty 8 byte packet (You get this everytime you zone even if the sky is clear) + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } + } +} + diff --git a/zone/gm_commands/who.cpp b/zone/gm_commands/who.cpp new file mode 100755 index 000000000..f39733d1e --- /dev/null +++ b/zone/gm_commands/who.cpp @@ -0,0 +1,202 @@ +#include "../client.h" + +void command_who(Client *c, const Seperator *sep) +{ + std::string query = + SQL ( + SELECT + character_data.account_id, + character_data.name, + character_data.zone_id, + character_data.zone_instance, + COALESCE( + ( + select + guilds.name + from + guilds + where + id = ( + ( + select + guild_id + from + guild_members + where + char_id = character_data.id + ) + ) + ), + "" + ) as guild_name, + character_data.level, + character_data.race, + character_data.class, + COALESCE( + ( + select + account.status + from + account + where + account.id = character_data.account_id + LIMIT + 1 + ), 0 + ) as account_status, + COALESCE( + ( + select + account.name + from + account + where + account.id = character_data.account_id + LIMIT + 1 + ), + 0 + ) as account_name, + COALESCE( + ( + select + account_ip.ip + from + account_ip + where + account_ip.accid = character_data.account_id + ORDER BY + account_ip.lastused DESC + LIMIT + 1 + ), + "" + ) as account_ip + FROM + character_data + WHERE + last_login > (UNIX_TIMESTAMP() - 600) + ORDER BY + character_data.name; + ); + + auto results = database.QueryDatabase(query); + if (!results.Success()) { + return; + } + + if (results.RowCount() == 0) { + c->Message(Chat::Yellow, "No results found"); + return; + } + + std::string search_string; + + if (sep->arg[1]) { + search_string = str_tolower(sep->arg[1]); + } + + int found_count = 0; + + c->Message(Chat::Magenta, "Players in EverQuest"); + c->Message(Chat::Magenta, "--------------------"); + + for (auto row = results.begin(); row != results.end(); ++row) { + auto account_id = static_cast(atoi(row[0])); + std::string player_name = row[1]; + auto zone_id = static_cast(atoi(row[2])); + std::string zone_short_name = ZoneName(zone_id); + auto zone_instance = static_cast(atoi(row[3])); + std::string guild_name = row[4]; + auto player_level = static_cast(atoi(row[5])); + auto player_race = static_cast(atoi(row[6])); + auto player_class = static_cast(atoi(row[7])); + auto account_status = static_cast(atoi(row[8])); + std::string account_name = row[9]; + std::string account_ip = row[10]; + std::string base_class_name = GetClassIDName(static_cast(player_class), 1); + std::string displayed_race_name = GetRaceIDName(static_cast(player_race)); + + if (search_string.length() > 0) { + bool found_search_term = + ( + str_tolower(player_name).find(search_string) != std::string::npos || + str_tolower(zone_short_name).find(search_string) != std::string::npos || + str_tolower(displayed_race_name).find(search_string) != std::string::npos || + str_tolower(base_class_name).find(search_string) != std::string::npos || + str_tolower(guild_name).find(search_string) != std::string::npos || + str_tolower(account_name).find(search_string) != std::string::npos || + str_tolower(account_ip).find(search_string) != std::string::npos + ); + + if (!found_search_term) { + continue; + } + } + + std::string displayed_guild_name; + if (guild_name.length() > 0) { + displayed_guild_name = EQ::SayLinkEngine::GenerateQuestSaylink( + StringFormat( + "#who \"%s\"", + guild_name.c_str()), + false, + StringFormat("<%s>", guild_name.c_str()) + ); + } + + std::string goto_saylink = EQ::SayLinkEngine::GenerateQuestSaylink( + StringFormat("#goto %s", player_name.c_str()), false, "Goto" + ); + + std::string display_class_name = GetClassIDName( + static_cast(player_class), + static_cast(player_level)); + + c->Message( + 5, "%s[%u %s] %s (%s) %s ZONE: %s (%u) (%s) (%s) (%s)", + (account_status > 0 ? "* GM * " : ""), + player_level, + EQ::SayLinkEngine::GenerateQuestSaylink( + StringFormat("#who %s", base_class_name.c_str()), + false, + display_class_name + ).c_str(), + player_name.c_str(), + EQ::SayLinkEngine::GenerateQuestSaylink( + StringFormat("#who %s", displayed_race_name.c_str()), + false, + displayed_race_name + ).c_str(), + displayed_guild_name.c_str(), + EQ::SayLinkEngine::GenerateQuestSaylink( + StringFormat("#who %s", zone_short_name.c_str()), + false, + zone_short_name + ).c_str(), + zone_instance, + goto_saylink.c_str(), + EQ::SayLinkEngine::GenerateQuestSaylink( + StringFormat("#who %s", account_name.c_str()), + false, + account_name + ).c_str(), + EQ::SayLinkEngine::GenerateQuestSaylink( + StringFormat("#who %s", account_ip.c_str()), + false, + account_ip + ).c_str() + ); + + found_count++; + } + + std::string message = ( + found_count > 0 ? + StringFormat("There is %i player(s) in EverQuest", found_count).c_str() : + "There are no players in EverQuest that match those who filters." + ); + + c->Message(Chat::Magenta, message.c_str()); +} + diff --git a/zone/gm_commands/worldshutdown.cpp b/zone/gm_commands/worldshutdown.cpp new file mode 100755 index 000000000..4f27c4ad9 --- /dev/null +++ b/zone/gm_commands/worldshutdown.cpp @@ -0,0 +1,79 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_worldshutdown(Client *c, const Seperator *sep) +{ + // GM command to shutdown world server and all zone servers + uint32 time = 0; + uint32 interval = 0; + if (worldserver.Connected()) { + if ( + sep->IsNumber(1) && + sep->IsNumber(2) && + (time = std::stoi(sep->arg[1]) > 0) && + (interval = std::stoi(sep->arg[2]) > 0) + ) { + int time_minutes = (time / 60); + quest_manager.WorldWideMessage( + Chat::Yellow, + fmt::format( + "[SYSTEM] World will be shutting down in {} minutes.", + time_minutes + ).c_str() + ); + c->Message( + Chat::White, + fmt::format( + "World will be shutting down in {} minutes, notifying every {} seconds", + time_minutes, + interval + ).c_str() + ); + auto pack = new ServerPacket(ServerOP_ShutdownAll, sizeof(WorldShutDown_Struct)); + WorldShutDown_Struct *wsd = (WorldShutDown_Struct *) pack->pBuffer; + wsd->time = (time * 1000); + wsd->interval = (interval * 1000); + worldserver.SendPacket(pack); + safe_delete(pack); + } + else if (!strcasecmp(sep->arg[1], "now")) { + quest_manager.WorldWideMessage( + Chat::Yellow, + "[SYSTEM] World is shutting down now." + ); + c->Message(Chat::White, "World is shutting down now."); + auto pack = new ServerPacket; + pack->opcode = ServerOP_ShutdownAll; + pack->size = 0; + worldserver.SendPacket(pack); + safe_delete(pack); + } + else if (!strcasecmp(sep->arg[1], "disable")) { + c->Message(Chat::White, "World shutdown has been aborted."); + auto pack = new ServerPacket(ServerOP_ShutdownAll, sizeof(WorldShutDown_Struct)); + WorldShutDown_Struct *wsd = (WorldShutDown_Struct *) pack->pBuffer; + wsd->time = 0; + wsd->interval = 0; + worldserver.SendPacket(pack); + safe_delete(pack); + } + else { + c->Message(Chat::White, "#worldshutdown - Shuts down the server and all zones."); + c->Message(Chat::White, "Usage: #worldshutdown now - Shuts down the server and all zones immediately."); + c->Message( + Chat::White, + "Usage: #worldshutdown disable - Stops the server from a previously scheduled shut down." + ); + c->Message( + Chat::White, + "Usage: #worldshutdown [timer] [interval] - Shuts down the server and all zones after [timer] seconds and notifies players every [interval] seconds." + ); + } + } + else { + c->Message(Chat::White, "Error: World server is disconnected."); + } +} + diff --git a/zone/gm_commands/worldwide.cpp b/zone/gm_commands/worldwide.cpp new file mode 100755 index 000000000..29b617a05 --- /dev/null +++ b/zone/gm_commands/worldwide.cpp @@ -0,0 +1,148 @@ +#include "../client.h" + +void command_worldwide(Client *c, const Seperator *sep) +{ + std::string sub_command; + if (sep->arg[1]) { + sub_command = sep->arg[1]; + } + + if (sub_command == "cast") { + if (sep->arg[2] && Seperator::IsNumber(sep->arg[2])) { + uint8 update_type = WWSpellUpdateType_Cast; + auto spell_id = std::stoul(sep->arg[2]); + bool disable_message = false; + if (sep->arg[3] && Seperator::IsNumber(sep->arg[3])) { + disable_message = std::stoi(sep->arg[3]) ? true : false; + } + + c->Message( + Chat::White, + fmt::format( + "World Wide Cast Spell | Spell: {} ({})", + GetSpellName(spell_id), + spell_id + ).c_str() + ); + + quest_manager.WorldWideSpell(update_type, spell_id); + if (!disable_message) { + quest_manager.WorldWideMessage( + Chat::Yellow, + fmt::format( + "[SYSTEM] A GM has cast [{}] world-wide!", + GetSpellName(spell_id) + ).c_str() + ); + } + } + else { + c->Message(Chat::White, "Usage: #worldwide cast [Spell ID] [Disable Message]"); + } + } + else if (sub_command == "remove") { + if (sep->arg[2] && Seperator::IsNumber(sep->arg[2])) { + uint8 update_type = WWSpellUpdateType_Remove; + auto spell_id = std::stoul(sep->arg[2]); + + c->Message( + Chat::White, + fmt::format( + "World Wide Remove Spell | Spell: {} ({})", + GetSpellName(spell_id), + spell_id + ).c_str() + ); + + quest_manager.WorldWideSpell(update_type, spell_id); + } + else { + c->Message(Chat::White, "Usage: #worldwide remove [Spell ID]"); + } + } + else if (sub_command == "message") { + if (sep->arg[2]) { + std::string message = sep->arg[2]; + quest_manager.WorldWideMessage( + Chat::White, + fmt::format( + "{}", + message + ).c_str() + ); + } + else { + c->Message(Chat::White, "Usage: #worldwide message [Message]"); + } + } + else if (sub_command == "move") { + if (sep->arg[2]) { + uint8 update_type = WWMoveUpdateType_MoveZone; + uint32 zone_id = 0; + std::string zone_short_name; + if (Seperator::IsNumber(sep->arg[2])) { + zone_id = std::stoul(sep->arg[2]); + } + + if (zone_id) { + zone_short_name = ZoneName(zone_id); + } + else { + zone_short_name = sep->arg[2]; + } + + c->Message( + Chat::White, + fmt::format( + "World Wide Zone | Zone: {} ({}) ID: {}", + ZoneLongName( + ZoneID(zone_short_name) + ), + zone_short_name, + ZoneID(zone_short_name) + ).c_str() + ); + + quest_manager.WorldWideMove(update_type, zone_short_name.c_str()); + } + else { + c->Message( + Chat::White, + "Usage: #worldwide move [Zone ID] or #worldwide move [Zone Short Name]" + ); + } + } + else if (sub_command == "moveinstance") { + if (Seperator::IsNumber(sep->arg[2])) { + uint8 update_type = WWMoveUpdateType_MoveZoneInstance; + const char *zone_short_name = ""; + uint16 instance_id = std::stoi(sep->arg[2]); + + c->Message( + Chat::White, + fmt::format( + "World Wide Zone Instance | Instance ID: {}", + instance_id + ).c_str() + ); + + quest_manager.WorldWideMove(update_type, zone_short_name, instance_id); + } + else { + c->Message(Chat::White, "Usage: #worldwide moveinstance [Instance ID]"); + } + } + + if (!sep->arg[1]) { + c->Message(Chat::White, "This command is used to perform world-wide tasks."); + c->Message(Chat::White, "Usage: #worldwide cast [Spell ID] [Disable Message]"); + c->Message(Chat::White, "Usage: #worldwide remove [Spell ID]"); + c->Message(Chat::White, "Usage: #worldwide message [Message]"); + c->Message( + Chat::White, + "Usage: #worldwide move [Zone ID] or #worldwide move [Zone Short Name]" + ); + c->Message(Chat::White, "Usage: #worldwide moveinstance [Instance ID]"); + } +} + diff --git a/zone/gm_commands/wp.cpp b/zone/gm_commands/wp.cpp new file mode 100755 index 000000000..4f0b9330e --- /dev/null +++ b/zone/gm_commands/wp.cpp @@ -0,0 +1,51 @@ +#include "../client.h" + +void command_wp(Client *c, const Seperator *sep) +{ + auto command_type = sep->arg[1]; + auto grid_id = atoi(sep->arg[2]); + if (grid_id != 0) { + auto pause = atoi(sep->arg[3]); + auto waypoint = atoi(sep->arg[4]); + auto zone_id = zone->GetZoneID(); + if (strcasecmp("add", command_type) == 0) { + if (waypoint == 0) { // Default to highest if it's left blank, or we enter 0 + waypoint = (content_db.GetHighestWaypoint(zone_id, grid_id) + 1); + } + + if (strcasecmp("-h", sep->arg[5]) == 0) { + content_db.AddWP(c, grid_id, waypoint, c->GetPosition(), pause, zone_id); + } + else { + auto position = c->GetPosition(); + position.w = -1; + content_db.AddWP(c, grid_id, waypoint, position, pause, zone_id); + } + c->Message( + Chat::White, + fmt::format( + "Waypoint {} added to grid {} with a pause of {} {}.", + waypoint, + grid_id, + pause, + (pause == 1 ? "second" : "seconds") + ).c_str() + ); + } + else if (strcasecmp("delete", command_type) == 0) { + content_db.DeleteWaypoint(c, grid_id, waypoint, zone_id); + c->Message( + Chat::White, + fmt::format( + "Waypoint {} deleted from grid {}.", + waypoint, + grid_id + ).c_str() + ); + } + } + else { + c->Message(Chat::White, "Usage: #wp [add|delete] [grid_id] [pause] [waypoint_id] [-h]"); + } +} + diff --git a/zone/gm_commands/wpadd.cpp b/zone/gm_commands/wpadd.cpp new file mode 100755 index 000000000..df74c03e4 --- /dev/null +++ b/zone/gm_commands/wpadd.cpp @@ -0,0 +1,52 @@ +#include "../client.h" + +void command_wpadd(Client *c, const Seperator *sep) +{ + int type1 = 0, type2 = 0, pause = 0; // Defaults for a new grid + Mob *target = c->GetTarget(); + if (target && target->IsNPC()) { + Spawn2 *s2info = target->CastToNPC()->respawn2; + if (s2info == nullptr) { + c->Message( + Chat::White, + "#wpadd Failed, you must target a valid spawn." + ); + return; + } + + if (sep->arg[1][0]) { + if (atoi(sep->arg[1]) >= 0) { + pause = atoi(sep->arg[1]); + } + else { + c->Message(Chat::White, "Usage: #wpadd [pause] [-h]"); + return; + } + } + auto position = c->GetPosition(); + if (strcmp("-h", sep->arg[2]) != 0) { + position.w = -1; + } + + auto zone_id = zone->GetZoneID(); + uint32 tmp_grid = content_db.AddWPForSpawn(c, s2info->GetID(), position, pause, type1, type2, zone_id); + if (tmp_grid) { + target->CastToNPC()->SetGrid(tmp_grid); + } + + auto grid_id = target->CastToNPC()->GetGrid(); + target->CastToNPC()->AssignWaypoints(grid_id); + c->Message( + Chat::White, + fmt::format( + "Waypoint added to grid {} in zone ID {}. Use #wpinfo to see waypoints for this NPC (may need to #repop first).", + grid_id, + zone_id + ).c_str() + ); + } + else { + c->Message(Chat::White, "You must target an NPC to use this."); + } +} + diff --git a/zone/gm_commands/wpinfo.cpp b/zone/gm_commands/wpinfo.cpp new file mode 100755 index 000000000..4f0f988e5 --- /dev/null +++ b/zone/gm_commands/wpinfo.cpp @@ -0,0 +1,15 @@ +#include "../client.h" + +void command_wpinfo(Client *c, const Seperator *sep) +{ + Mob *t = c->GetTarget(); + + if (t == nullptr || !t->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this."); + return; + } + + NPC *n = t->CastToNPC(); + n->DisplayWaypointInfo(c); +} + diff --git a/zone/gm_commands/xtargets.cpp b/zone/gm_commands/xtargets.cpp new file mode 100755 index 000000000..bd300e819 --- /dev/null +++ b/zone/gm_commands/xtargets.cpp @@ -0,0 +1,28 @@ +#include "../client.h" + +void command_xtargets(Client *c, const Seperator *sep) +{ + Client *t; + + if (c->GetTarget() && c->GetTarget()->IsClient()) { + t = c->GetTarget()->CastToClient(); + } + else { + t = c; + } + + if (sep->arg[1][0]) { + uint8 NewMax = atoi(sep->arg[1]); + + if ((NewMax < 5) || (NewMax > XTARGET_HARDCAP)) { + c->Message(Chat::Red, "Number of XTargets must be between 5 and %i", XTARGET_HARDCAP); + return; + } + t->SetMaxXTargets(NewMax); + c->Message(Chat::White, "Max number of XTargets set to %i", NewMax); + } + else { + t->ShowXTargets(c); + } +} + diff --git a/zone/gm_commands/zclip.cpp b/zone/gm_commands/zclip.cpp new file mode 100755 index 000000000..4f825c745 --- /dev/null +++ b/zone/gm_commands/zclip.cpp @@ -0,0 +1,39 @@ +#include "../client.h" + +void command_zclip(Client *c, const Seperator *sep) +{ + // modifys and resends zhdr packet + if (sep->arg[2][0] == 0) { + c->Message(Chat::White, "Usage: #zclip "); + } + else if (atoi(sep->arg[1]) <= 0) { + c->Message(Chat::White, "ERROR: Min clip can not be zero or less!"); + } + else if (atoi(sep->arg[2]) <= 0) { + c->Message(Chat::White, "ERROR: Max clip can not be zero or less!"); + } + else if (atoi(sep->arg[1]) > atoi(sep->arg[2])) { + c->Message(Chat::White, "ERROR: Min clip is greater than max clip!"); + } + else { + zone->newzone_data.minclip = atof(sep->arg[1]); + zone->newzone_data.maxclip = atof(sep->arg[2]); + if (sep->arg[3][0] != 0) { + zone->newzone_data.fog_minclip[0] = atof(sep->arg[3]); + } + if (sep->arg[4][0] != 0) { + zone->newzone_data.fog_minclip[1] = atof(sep->arg[4]); + } + if (sep->arg[5][0] != 0) { + zone->newzone_data.fog_maxclip[0] = atof(sep->arg[5]); + } + if (sep->arg[6][0] != 0) { + zone->newzone_data.fog_maxclip[1] = atof(sep->arg[6]); + } + auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); + memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } +} + diff --git a/zone/gm_commands/zcolor.cpp b/zone/gm_commands/zcolor.cpp new file mode 100755 index 000000000..feac259b9 --- /dev/null +++ b/zone/gm_commands/zcolor.cpp @@ -0,0 +1,30 @@ +#include "../client.h" + +void command_zcolor(Client *c, const Seperator *sep) +{ + // modifys and resends zhdr packet + if (sep->arg[3][0] == 0) { + c->Message(Chat::White, "Usage: #zcolor "); + } + else if (atoi(sep->arg[1]) < 0 || atoi(sep->arg[1]) > 255) { + c->Message(Chat::White, "ERROR: Red can not be less than 0 or greater than 255!"); + } + else if (atoi(sep->arg[2]) < 0 || atoi(sep->arg[2]) > 255) { + c->Message(Chat::White, "ERROR: Green can not be less than 0 or greater than 255!"); + } + else if (atoi(sep->arg[3]) < 0 || atoi(sep->arg[3]) > 255) { + c->Message(Chat::White, "ERROR: Blue can not be less than 0 or greater than 255!"); + } + else { + for (int z = 0; z < 4; z++) { + zone->newzone_data.fog_red[z] = atoi(sep->arg[1]); + zone->newzone_data.fog_green[z] = atoi(sep->arg[2]); + zone->newzone_data.fog_blue[z] = atoi(sep->arg[3]); + } + auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); + memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } +} + diff --git a/zone/gm_commands/zheader.cpp b/zone/gm_commands/zheader.cpp new file mode 100755 index 000000000..788140dbe --- /dev/null +++ b/zone/gm_commands/zheader.cpp @@ -0,0 +1,26 @@ +#include "../client.h" + +void command_zheader(Client *c, const Seperator *sep) +{ + // sends zhdr packet + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #zheader "); + } + else if (ZoneID(sep->argplus[1]) == 0) { + c->Message(Chat::White, "Invalid Zone Name: %s", sep->argplus[1]); + } + else { + + if (zone->LoadZoneCFG(sep->argplus[1], 0)) { + c->Message(Chat::White, "Successfully loaded zone header for %s from database.", sep->argplus[1]); + } + else { + c->Message(Chat::White, "Failed to load zone header %s from database", sep->argplus[1]); + } + auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); + memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } +} + diff --git a/zone/gm_commands/zonebootup.cpp b/zone/gm_commands/zonebootup.cpp new file mode 100755 index 000000000..7c54e24fb --- /dev/null +++ b/zone/gm_commands/zonebootup.cpp @@ -0,0 +1,25 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_zonebootup(Client *c, const Seperator *sep) +{ + if (!worldserver.Connected()) { + c->Message(Chat::White, "Error: World server disconnected"); + } + else if (sep->arg[2][0] == 0) { + c->Message(Chat::White, "Usage: #zonebootup ZoneServerID# zoneshortname"); + } + else { + auto pack = new ServerPacket(ServerOP_ZoneBootup, sizeof(ServerZoneStateChange_struct)); + ServerZoneStateChange_struct *s = (ServerZoneStateChange_struct *) pack->pBuffer; + s->ZoneServerID = atoi(sep->arg[1]); + strcpy(s->adminname, c->GetName()); + s->zoneid = ZoneID(sep->arg[2]); + s->makestatic = (bool) (strcasecmp(sep->arg[3], "static") == 0); + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + diff --git a/zone/gm_commands/zonelock.cpp b/zone/gm_commands/zonelock.cpp new file mode 100755 index 000000000..f89f1e9ad --- /dev/null +++ b/zone/gm_commands/zonelock.cpp @@ -0,0 +1,76 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_zonelock(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #zonelock list - Lists Locked Zones"); + if (c->Admin() >= commandLockZones) { + c->Message( + Chat::White, + "Usage: #zonelock lock [Zone ID] or #zonelock lock [Zone Short Name] - Locks a Zone by ID or Short Name" + ); + c->Message( + Chat::White, + "Usage: #zonelock unlock [Zone ID] or #zonelock unlock [Zone Short Name] - Unlocks a Zone by ID or Short Name" + ); + } + return; + } + + std::string lock_type = str_tolower(sep->arg[1]); + bool is_list = lock_type.find("list") != std::string::npos; + bool is_lock = lock_type.find("lock") != std::string::npos; + bool is_unlock = lock_type.find("unlock") != std::string::npos; + if (!is_list && !is_lock && !is_unlock) { + c->Message(Chat::White, "Usage: #zonelock list - Lists Locked Zones"); + if (c->Admin() >= commandLockZones) { + c->Message( + Chat::White, + "Usage: #zonelock lock [Zone ID] or #zonelock lock [Zone Short Name] - Locks a Zone by ID or Short Name" + ); + c->Message( + Chat::White, + "Usage: #zonelock unlock [Zone ID] or #zonelock unlock [Zone Short Name] - Unlocks a Zone by ID or Short Name" + ); + } + return; + } + + auto pack = new ServerPacket(ServerOP_LockZone, sizeof(ServerLockZone_Struct)); + ServerLockZone_Struct *lock_zone = (ServerLockZone_Struct *) pack->pBuffer; + strn0cpy(lock_zone->adminname, c->GetName(), sizeof(lock_zone->adminname)); + + if (is_list) { + lock_zone->op = ServerLockType::List; + worldserver.SendPacket(pack); + } + else if (!is_list && c->Admin() >= commandLockZones) { + auto zone_id = ( + sep->IsNumber(2) ? + static_cast(std::stoul(sep->arg[2])) : + static_cast(ZoneID(sep->arg[2])) + ); + std::string zone_short_name = str_tolower(ZoneName(zone_id, true)); + bool is_unknown_zone = zone_short_name.find("unknown") != std::string::npos; + if (zone_id && !is_unknown_zone) { + lock_zone->op = is_lock ? ServerLockType::Lock : ServerLockType::Unlock; + lock_zone->zoneID = zone_id; + worldserver.SendPacket(pack); + } + else { + c->Message( + Chat::White, + fmt::format( + "Usage: #zonelock {} [Zone ID] or #zonelock {} [Zone Short Name]", + is_lock ? "lock" : "unlock" + ).c_str() + ); + } + } + safe_delete(pack); +} + diff --git a/zone/gm_commands/zoneshutdown.cpp b/zone/gm_commands/zoneshutdown.cpp new file mode 100755 index 000000000..14550b9db --- /dev/null +++ b/zone/gm_commands/zoneshutdown.cpp @@ -0,0 +1,30 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_zoneshutdown(Client *c, const Seperator *sep) +{ + if (!worldserver.Connected()) { + c->Message(Chat::White, "Error: World server disconnected"); + } + else if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #zoneshutdown zoneshortname"); + } + else { + auto pack = new ServerPacket( + ServerOP_ZoneShutdown, + sizeof(ServerZoneStateChange_struct)); + ServerZoneStateChange_struct *s = (ServerZoneStateChange_struct *) pack->pBuffer; + strcpy(s->adminname, c->GetName()); + if (sep->arg[1][0] >= '0' && sep->arg[1][0] <= '9') { + s->ZoneServerID = atoi(sep->arg[1]); + } + else { + s->zoneid = ZoneID(sep->arg[1]); + } + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + diff --git a/zone/gm_commands/zonespawn.cpp b/zone/gm_commands/zonespawn.cpp new file mode 100755 index 000000000..01a3a8683 --- /dev/null +++ b/zone/gm_commands/zonespawn.cpp @@ -0,0 +1,40 @@ +#include "../client.h" + +void command_zonespawn(Client *c, const Seperator *sep) +{ + c->Message(Chat::White, "This command is not yet implemented."); + return; + +/* this was kept from client.cpp verbatim (it was commented out) */ + // if (target && target->IsNPC()) { + // Message(0, "Inside main if."); + // if (strcasecmp(sep->arg[1], "add")==0) { + // Message(0, "Inside add if."); + // database.DBSpawn(1, StaticGetZoneName(this->GetPP().current_zone), target->CastToNPC()); + // } + // else if (strcasecmp(sep->arg[1], "update")==0) { + // database.DBSpawn(2, StaticGetZoneName(this->GetPP().current_zone), target->CastToNPC()); + // } + // else if (strcasecmp(sep->arg[1], "remove")==0) { + // if (strcasecmp(sep->arg[2], "all")==0) { + // database.DBSpawn(4, StaticGetZoneName(this->GetPP().current_zone)); + // } + // else { + // if (database.DBSpawn(3, StaticGetZoneName(this->GetPP().current_zone), target->CastToNPC())) { + // Message(0, "#zonespawn: %s removed successfully!", target->GetName()); + // target->CastToNPC()->Death(target, target->GetHP()); + // } + // } + // } + // else + // Message(0, "Error: #dbspawn: Invalid command. (Note: EDIT and REMOVE are NOT in yet.)"); + // if (target->CastToNPC()->GetNPCTypeID() > 0) { + // Message(0, "Spawn is type %i", target->CastToNPC()->GetNPCTypeID()); + // } + // } + // else if(!target || !target->IsNPC()) + // Message(0, "Error: #zonespawn: You must have a NPC targeted!"); + // else + // Message(0, "Usage: #zonespawn [add|edit|remove|remove all]"); +} + diff --git a/zone/gm_commands/zonestatus.cpp b/zone/gm_commands/zonestatus.cpp new file mode 100755 index 000000000..51c5f763e --- /dev/null +++ b/zone/gm_commands/zonestatus.cpp @@ -0,0 +1,19 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_zonestatus(Client *c, const Seperator *sep) +{ + if (!worldserver.Connected()) { + c->Message(Chat::White, "Error: World server disconnected"); + } + else { + auto pack = new ServerPacket(ServerOP_ZoneStatus, strlen(c->GetName()) + 2); + memset(pack->pBuffer, (uint8) c->Admin(), 1); + strcpy((char *) &pack->pBuffer[1], c->GetName()); + worldserver.SendPacket(pack); + delete pack; + } +} + diff --git a/zone/gm_commands/zopp.cpp b/zone/gm_commands/zopp.cpp new file mode 100755 index 000000000..08f10395b --- /dev/null +++ b/zone/gm_commands/zopp.cpp @@ -0,0 +1,64 @@ +#include "../client.h" + +void command_zopp(Client *c, const Seperator *sep) +{ // - Owner only command..non-targetable to eliminate malicious or mischievious activities. + if (!c) { + return; + } + else if (sep->argnum < 3 || sep->argnum > 4) { + c->Message(Chat::White, "Usage: #zopp [trade/summon] [slot id] [item id] [*charges]"); + } + else if (!strcasecmp(sep->arg[1], "trade") == 0 && !strcasecmp(sep->arg[1], "t") == 0 && + !strcasecmp(sep->arg[1], "summon") == 0 && !strcasecmp(sep->arg[1], "s") == 0) { + c->Message(Chat::White, "Usage: #zopp [trade/summon] [slot id] [item id] [*charges]"); + } + else if (!sep->IsNumber(2) || !sep->IsNumber(3) || (sep->argnum == 4 && !sep->IsNumber(4))) { + c->Message(Chat::White, "Usage: #zopp [trade/summon] [slot id] [item id] [*charges]"); + } + else { + ItemPacketType packettype; + + if (strcasecmp(sep->arg[1], "trade") == 0 || strcasecmp(sep->arg[1], "t") == 0) { + packettype = ItemPacketTrade; + } + else { + packettype = ItemPacketLimbo; + } + + int16 slotid = atoi(sep->arg[2]); + uint32 itemid = atoi(sep->arg[3]); + int16 charges = sep->argnum == 4 ? atoi(sep->arg[4]) : 1; // defaults to 1 charge if not specified + + const EQ::ItemData *FakeItem = database.GetItem(itemid); + + if (!FakeItem) { + c->Message(Chat::Red, "Error: Item [%u] is not a valid item id.", itemid); + return; + } + + int16 item_status = 0; + const EQ::ItemData *item = database.GetItem(itemid); + if (item) { + item_status = static_cast(item->MinStatus); + } + if (item_status > c->Admin()) { + c->Message(Chat::Red, "Error: Insufficient status to use this command."); + return; + } + + if (charges < 0 || charges > FakeItem->StackSize) { + c->Message(Chat::Red, "Warning: The specified charge count does not meet expected criteria!"); + c->Message(Chat::White, "Processing request..results may cause unpredictable behavior."); + } + + EQ::ItemInstance *FakeItemInst = database.CreateItem(FakeItem, charges); + c->SendItemPacket(slotid, FakeItemInst, packettype); + c->Message( + Chat::White, "Sending zephyr op packet to client - [%s] %s (%u) with %i %s to slot %i.", + packettype == ItemPacketTrade ? "Trade" : "Summon", FakeItem->Name, itemid, charges, + std::abs(charges == 1) ? "charge" : "charges", slotid + ); + safe_delete(FakeItemInst); + } +} + diff --git a/zone/gm_commands/zsafecoords.cpp b/zone/gm_commands/zsafecoords.cpp new file mode 100755 index 000000000..237f288da --- /dev/null +++ b/zone/gm_commands/zsafecoords.cpp @@ -0,0 +1,26 @@ +#include "../client.h" + +void command_zsafecoords(Client *c, const Seperator *sep) +{ + // modifys and resends zhdr packet + if (sep->arg[3][0] == 0) { + c->Message(Chat::White, "Usage: #zsafecoords "); + } + else { + zone->newzone_data.safe_x = atof(sep->arg[1]); + zone->newzone_data.safe_y = atof(sep->arg[2]); + zone->newzone_data.safe_z = atof(sep->arg[3]); + //float newdatax = atof(sep->arg[1]); + //float newdatay = atof(sep->arg[2]); + //float newdataz = atof(sep->arg[3]); + //memcpy(&zone->zone_header_data[114], &newdatax, sizeof(float)); + //memcpy(&zone->zone_header_data[118], &newdatay, sizeof(float)); + //memcpy(&zone->zone_header_data[122], &newdataz, sizeof(float)); + //zone->SetSafeCoords(); + auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); + memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } +} + diff --git a/zone/gm_commands/zsave.cpp b/zone/gm_commands/zsave.cpp new file mode 100755 index 000000000..8c1cf85dc --- /dev/null +++ b/zone/gm_commands/zsave.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +void command_zsave(Client *c, const Seperator *sep) +{ + if (zone->SaveZoneCFG()) { + c->Message(Chat::Red, "Zone header saved successfully."); + } + else { + c->Message(Chat::Red, "ERROR: Zone header data was NOT saved."); + } +} + diff --git a/zone/gm_commands/zsky.cpp b/zone/gm_commands/zsky.cpp new file mode 100755 index 000000000..8a3dc2c7f --- /dev/null +++ b/zone/gm_commands/zsky.cpp @@ -0,0 +1,20 @@ +#include "../client.h" + +void command_zsky(Client *c, const Seperator *sep) +{ + // modifys and resends zhdr packet + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #zsky "); + } + else if (atoi(sep->arg[1]) < 0 || atoi(sep->arg[1]) > 255) { + c->Message(Chat::White, "ERROR: Sky type can not be less than 0 or greater than 255!"); + } + else { + zone->newzone_data.sky = atoi(sep->arg[1]); + auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); + memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } +} + diff --git a/zone/gm_commands/zstats.cpp b/zone/gm_commands/zstats.cpp new file mode 100755 index 000000000..9c4330ae5 --- /dev/null +++ b/zone/gm_commands/zstats.cpp @@ -0,0 +1,208 @@ +#include "../client.h" + +void command_zstats(Client *c, const Seperator *sep) +{ + // Zone + c->Message( + Chat::White, + fmt::format( + "Zone | ID: {} Instance ID: {} Name: {} ({})", + zone->GetZoneID(), + zone->GetInstanceID(), + zone->GetLongName(), + zone->GetShortName() + ).c_str() + ); + + // Type + c->Message( + Chat::White, + fmt::format( + "Type: {}", + zone->newzone_data.ztype + ).c_str() + ); + + // Fog Data + for (int fog_index = 0; fog_index < 4; fog_index++) { + int fog_number = (fog_index + 1); + c->Message( + Chat::White, + fmt::format( + "Fog {} Colors | Red: {} Blue: {} Green: {} ", + fog_number, + zone->newzone_data.fog_red[fog_index], + zone->newzone_data.fog_green[fog_index], + zone->newzone_data.fog_blue[fog_index] + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Fog {} Clipping Distance | Min: {} Max: {}", + fog_number, + zone->newzone_data.fog_minclip[fog_index], + zone->newzone_data.fog_maxclip[fog_index] + ).c_str() + ); + } + + // Fog Density + c->Message( + Chat::White, + fmt::format( + "Fog Density: {}", + zone->newzone_data.fog_density + ).c_str() + ); + + + // Gravity + c->Message( + Chat::White, + fmt::format( + "Gravity: {}", + zone->newzone_data.gravity + ).c_str() + ); + + // Time Type + c->Message( + Chat::White, + fmt::format( + "Time Type: {}", + zone->newzone_data.time_type + ).c_str() + ); + + // Experience Multiplier + c->Message( + Chat::White, + fmt::format( + "Experience Multiplier: {}", + zone->newzone_data.zone_exp_multiplier + ).c_str() + ); + + // Safe Coordinates + c->Message( + Chat::White, + fmt::format( + "Safe Coordinates: {}, {}, {}", + zone->newzone_data.safe_x, + zone->newzone_data.safe_y, + zone->newzone_data.safe_z + ).c_str() + ); + + // Max Z + c->Message( + Chat::White, + fmt::format( + "Max Z: {}", + zone->newzone_data.max_z + ).c_str() + ); + + // Underworld Z + c->Message( + Chat::White, + fmt::format( + "Underworld Z: {}", + zone->newzone_data.underworld + ).c_str() + ); + + // Clipping Distance + c->Message( + Chat::White, + fmt::format( + "Clipping Distance | Min: {} Max: {}", + zone->newzone_data.minclip, + zone->newzone_data.maxclip + ).c_str() + ); + + // Weather Data + for (int weather_index = 0; weather_index < 4; weather_index++) { + int weather_number = (weather_index + 1); + c->Message( + Chat::White, + fmt::format( + "Rain {} | Chance: {} Duration: {} ", + weather_number, + zone->newzone_data.rain_chance[weather_index], + zone->newzone_data.rain_duration[weather_index] + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Snow {} | Chance: {} Duration: {}", + weather_number, + zone->newzone_data.snow_chance[weather_index], + zone->newzone_data.snow_duration[weather_index] + ).c_str() + ); + } + + // Sky Type + c->Message( + Chat::White, + fmt::format( + "Sky Type: {}", + zone->newzone_data.sky + ).c_str() + ); + + // Suspend Buffs + c->Message( + Chat::White, + fmt::format( + "Suspend Buffs: {}", + zone->newzone_data.SuspendBuffs + ).c_str() + ); + + // Regeneration Data + c->Message( + Chat::White, + fmt::format( + "Regen | Health: {} Mana: {} Endurance: {}", + zone->newzone_data.FastRegenHP, + zone->newzone_data.FastRegenMana, + zone->newzone_data.FastRegenEndurance + ).c_str() + ); + + // NPC Max Aggro Distance + c->Message( + Chat::White, + fmt::format( + "NPC Max Aggro Distance: {}", + zone->newzone_data.NPCAggroMaxDist + ).c_str() + ); + + // Underworld Teleport Index + c->Message( + Chat::White, + fmt::format( + "Underworld Teleport Index: {}", + zone->newzone_data.underworld_teleport_index + ).c_str() + ); + + // Lava Damage + c->Message( + Chat::White, + fmt::format( + "Lava Damage | Min: {} Max: {}", + zone->newzone_data.MinLavaDamage, + zone->newzone_data.LavaDamage + ).c_str() + ); +} + diff --git a/zone/gm_commands/zunderworld.cpp b/zone/gm_commands/zunderworld.cpp new file mode 100755 index 000000000..0ba52f156 --- /dev/null +++ b/zone/gm_commands/zunderworld.cpp @@ -0,0 +1,12 @@ +#include "../client.h" + +void command_zunderworld(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #zunderworld "); + } + else { + zone->newzone_data.underworld = atof(sep->arg[1]); + } +} + diff --git a/zone/gm_commands/zuwcoords.cpp b/zone/gm_commands/zuwcoords.cpp new file mode 100755 index 000000000..353b48739 --- /dev/null +++ b/zone/gm_commands/zuwcoords.cpp @@ -0,0 +1,19 @@ +#include "../client.h" + +void command_zuwcoords(Client *c, const Seperator *sep) +{ + // modifys and resends zhdr packet + if (sep->arg[1][0] == 0) { + c->Message(Chat::White, "Usage: #zuwcoords "); + } + else { + zone->newzone_data.underworld = atof(sep->arg[1]); + //float newdata = atof(sep->arg[1]); + //memcpy(&zone->zone_header_data[130], &newdata, sizeof(float)); + auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); + memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } +} + From a1116688882a1516c9c741acb8a8968396424a43 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 15 Nov 2021 04:07:55 -0600 Subject: [PATCH 414/624] [Hotfix] Default PR #1758 to false --- common/ruletypes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index e1bf1a926..5e62ba960 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -764,7 +764,7 @@ RULE_CATEGORY_END() RULE_CATEGORY(Expansion) RULE_INT(Expansion, CurrentExpansion, -1, "The current expansion enabled for the server [-1 = ALL, 0 = Classic, 1 = Kunark etc.]") -RULE_BOOL(Expansion, UseCurrentExpansionAAOnly, true, "When true will only load AA ranks that match CurrentExpansion rule") +RULE_BOOL(Expansion, UseCurrentExpansionAAOnly, false, "When true will only load AA ranks that match CurrentExpansion rule") RULE_CATEGORY_END() RULE_CATEGORY(Instances) From 0ebb1cc54c4ff3e1d29dae1b9542105d24e6172f Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 15 Nov 2021 13:27:01 -0500 Subject: [PATCH 415/624] Fix linking tests due to ddcb18418 (#1769) --- tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e7b97b57f..41c7aa547 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -20,7 +20,7 @@ SET(tests_headers ADD_EXECUTABLE(tests ${tests_sources} ${tests_headers}) -TARGET_LINK_LIBRARIES(tests common cppunit) +TARGET_LINK_LIBRARIES(tests common cppunit fmt) INSTALL(TARGETS tests RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) From bf8a0328b3f031fb7d8416d7ed79a9499b723a34 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Mon, 15 Nov 2021 18:19:42 -0500 Subject: [PATCH 416/624] [Cleanup] Add Entity ID to ShowStats() NPC display. (#1770) --- zone/mob.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index 394de8bba..a16aab713 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1708,8 +1708,9 @@ void Mob::ShowStats(Client* client) client->Message( Chat::White, fmt::format( - "NPC | ID: {} Name: {}{} Level: {}", + "NPC | ID: {} Entity ID: {} Name: {}{} Level: {}", target->GetNPCTypeID(), + target->GetID(), target_name, ( !target_last_name.empty() ? From 3efd9c7f602eb5a3d11baf31e224678ee4bf5136 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 16 Nov 2021 08:52:22 -0500 Subject: [PATCH 417/624] [Cleanup] Convert DeleteItemInInventory quantity to int16. (#1767) * [Cleanup] Convert DeleteItemInInventory quantity to int16. * Type conversion. --- common/inventory_profile.cpp | 2 +- common/inventory_profile.h | 2 +- zone/client.cpp | 12 ++-- zone/client.h | 8 +-- zone/client_packet.cpp | 78 ++++--------------------- zone/inventory.cpp | 109 +++++++++-------------------------- zone/perl_client.cpp | 12 ++-- zone/spell_effects.cpp | 29 +++++----- zone/trading.cpp | 4 +- zone/tribute.cpp | 14 ++--- 10 files changed, 81 insertions(+), 189 deletions(-) diff --git a/common/inventory_profile.cpp b/common/inventory_profile.cpp index 2f0fc98a0..743b0a31c 100644 --- a/common/inventory_profile.cpp +++ b/common/inventory_profile.cpp @@ -399,7 +399,7 @@ bool EQ::InventoryProfile::SwapItem( } // Remove item from inventory (with memory delete) -bool EQ::InventoryProfile::DeleteItem(int16 slot_id, uint8 quantity) { +bool EQ::InventoryProfile::DeleteItem(int16 slot_id, int16 quantity) { // Pop item out of inventory map (or queue) ItemInstance *item_to_delete = PopItem(slot_id); diff --git a/common/inventory_profile.h b/common/inventory_profile.h index 654605aff..58a65e443 100644 --- a/common/inventory_profile.h +++ b/common/inventory_profile.h @@ -132,7 +132,7 @@ namespace EQ bool SwapItem(int16 source_slot, int16 destination_slot, SwapItemFailState& fail_state, uint16 race_id = 0, uint8 class_id = 0, uint16 deity_id = 0, uint8 level = 0); // Remove item from inventory - bool DeleteItem(int16 slot_id, uint8 quantity = 0); + bool DeleteItem(int16 slot_id, int16 quantity = 0); // Checks All items in a bag for No Drop bool CheckNoDrop(int16 slot_id, bool recurse = true); diff --git a/zone/client.cpp b/zone/client.cpp index 88f63508f..3d7b0e89c 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -8501,7 +8501,7 @@ void Client::Consume(const EQ::ItemData *item, uint8 type, int16 slot, bool auto LogFood("Consuming food, points added to hunger_level: [{}] - current_hunger: [{}]", increase, m_pp.hunger_level); - DeleteItemInInventory(slot, 1, false); + DeleteItemInInventory(slot, 1); if (!auto_consume) // no message if the client consumed for us entity_list.MessageCloseString(this, true, 50, 0, EATING_MESSAGE, GetName(), item->Name); @@ -8516,7 +8516,7 @@ void Client::Consume(const EQ::ItemData *item, uint8 type, int16 slot, bool auto m_pp.thirst_level += increase; - DeleteItemInInventory(slot, 1, false); + DeleteItemInInventory(slot, 1); LogFood("Consuming drink, points added to thirst_level: [{}] current_thirst: [{}]", increase, m_pp.thirst_level); @@ -10285,7 +10285,7 @@ void Client::RemoveItem(uint32 item_id, uint32 quantity) { EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END }, { EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END }, }; - int removed_count = 0; + int16 removed_count = 0; const size_t size = sizeof(slots) / sizeof(slots[0]); for (int slot_index = 0; slot_index < size; ++slot_index) { for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { @@ -10295,13 +10295,13 @@ void Client::RemoveItem(uint32 item_id, uint32 quantity) item = GetInv().GetItem(slot_id); if (item && item->GetID() == item_id) { - int charges = item->IsStackable() ? item->GetCharges() : 0; - int stack_size = std::max(charges, 1); + int16 charges = item->IsStackable() ? item->GetCharges() : 0; + int16 stack_size = std::max(charges, static_cast(1)); if ((removed_count + stack_size) <= quantity) { removed_count += stack_size; DeleteItemInInventory(slot_id, charges, true); } else { - int amount_left = (quantity - removed_count); + int16 amount_left = (quantity - removed_count); if (amount_left > 0 && stack_size >= amount_left) { removed_count += amount_left; DeleteItemInInventory(slot_id, amount_left, true); diff --git a/zone/client.h b/zone/client.h index 6d913a82b..68a86500f 100644 --- a/zone/client.h +++ b/zone/client.h @@ -301,8 +301,8 @@ public: uint16 FindTraderItem(int32 SerialNumber,uint16 Quantity); uint32 FindTraderItemSerialNumber(int32 ItemID); EQ::ItemInstance* FindTraderItemBySerialNumber(int32 SerialNumber); - void FindAndNukeTraderItem(int32 item_id,uint16 quantity,Client* customer,uint16 traderslot); - void NukeTraderItem(uint16 slot, int16 charges, uint16 quantity, Client* customer, uint16 traderslot, int32 uniqueid, int32 itemid = 0); + void FindAndNukeTraderItem(int32 item_id,int16 quantity,Client* customer,uint16 traderslot); + void NukeTraderItem(uint16 slot, int16 charges, int16 quantity, Client* customer, uint16 traderslot, int32 uniqueid, int32 itemid = 0); void ReturnTraderReq(const EQApplicationPacket* app,int16 traderitemcharges, uint32 itemid = 0); void TradeRequestFailed(const EQApplicationPacket* app); void BuyTraderItem(TraderBuy_Struct* tbs,Client* trader,const EQApplicationPacket* app); @@ -919,7 +919,7 @@ public: bool PutItemInInventory(int16 slot_id, const EQ::ItemInstance& inst, bool client_update = false); bool PushItemOnCursor(const EQ::ItemInstance& inst, bool client_update = false); void SendCursorBuffer(); - void DeleteItemInInventory(int16 slot_id, int8 quantity = 0, bool client_update = false, bool update_db = true); + void DeleteItemInInventory(int16 slot_id, int16 quantity = 0, bool client_update = false, bool update_db = true); int CountItem(uint32 item_id); void RemoveItem(uint32 item_id, uint32 quantity = 1); bool SwapItem(MoveItem_Struct* move_in); @@ -973,7 +973,7 @@ public: //remove charges/multiple objects from inventory: //bool DecreaseByType(uint32 type, uint8 amt); - bool DecreaseByID(uint32 type, uint8 amt); + bool DecreaseByID(uint32 type, int16 quantity); uint8 SlotConvert2(uint8 slot); //Maybe not needed. void Escape(); //AA Escape void DisenchantSummonedBags(bool client_update = true); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index b140934cb..ed767a041 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -2278,7 +2278,7 @@ void Client::Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app) if (!inst->IsStackable()) { - DeleteItemInInventory(ams_in->slot, 0, false); + DeleteItemInInventory(ams_in->slot); } else { @@ -2293,7 +2293,7 @@ void Client::Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app) return; } - DeleteItemInInventory(ams_in->slot, ams_in->charges, false); + DeleteItemInInventory(ams_in->slot, ams_in->charges); price *= ams_in->charges; } @@ -2764,7 +2764,7 @@ void Client::Handle_OP_AltCurrencySell(const EQApplicationPacket *app) if (!inst->IsStackable()) { - DeleteItemInInventory(sell->slot_id, 0, false); + DeleteItemInInventory(sell->slot_id); } else { @@ -2779,7 +2779,7 @@ void Client::Handle_OP_AltCurrencySell(const EQApplicationPacket *app) return; } - DeleteItemInInventory(sell->slot_id, sell->charges, false); + DeleteItemInInventory(sell->slot_id, sell->charges); cost *= sell->charges; } @@ -9057,48 +9057,6 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) { LogDebug("Error: unknown item->Click.Type ([{}])", item->Click.Type); } - else - { - - /* - //This is food/drink - consume it - if (item->ItemType == EQ::item::ItemTypeFood && m_pp.hunger_level < 5000) - { - Consume(item, item->ItemType, slot_id, false); - } - else if (item->ItemType == EQ::item::ItemTypeDrink && m_pp.thirst_level < 5000) - { - Consume(item, item->ItemType, slot_id, false); - } - else if (item->ItemType == EQ::item::ItemTypeAlcohol) - { -#if EQDEBUG >= 1 - LogDebug("Drinking Alcohol from slot:[{}]", slot_id); -#endif - // This Seems to be handled in OP_DeleteItem handling - //DeleteItemInInventory(slot_id, 1, false); - //entity_list.MessageCloseString(this, true, 50, 0, DRINKING_MESSAGE, GetName(), item->Name); - //Should add intoxication level to the PP at some point - //CheckIncreaseSkill(ALCOHOL_TOLERANCE, nullptr, 25); - } - - EQApplicationPacket *outapp2 = nullptr; - outapp2 = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); - Stamina_Struct* sta = (Stamina_Struct*)outapp2->pBuffer; - - if (m_pp.hunger_level > 6000) - sta->food = 6000; - if (m_pp.thirst_level > 6000) - sta->water = 6000; - - sta->food = m_pp.hunger_level; - sta->water = m_pp.thirst_level; - - QueuePacket(outapp2); - safe_delete(outapp2); - */ - } - } else { @@ -13413,26 +13371,14 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app) // 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 { - // HACK: DeleteItemInInventory uses int8 for quantity type. There is no consistent use of types in code in this path so for now iteratively delete from inventory. - if (mp->quantity > 255) { - uint32 temp = mp->quantity; - while (temp > 255 && temp != 0) { - // Delete chunks of 255 - this->DeleteItemInInventory(mp->itemslot, 255, false); - temp -= 255; - } - if (temp != 0) { - // Delete remaining - this->DeleteItemInInventory(mp->itemslot, temp, false); - } - } - else { - this->DeleteItemInInventory(mp->itemslot, mp->quantity, false); - } - } + DeleteItemInInventory( + mp->itemslot, + ( + !inst->IsStackable() ? + 0 : + mp->quantity + ) + ); //This forces the price to show up correctly for charged items. diff --git a/zone/inventory.cpp b/zone/inventory.cpp index bc79d7c43..62c3148ec 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -1093,7 +1093,7 @@ void Client::SendCursorBuffer() } // Remove item from inventory -void Client::DeleteItemInInventory(int16 slot_id, int8 quantity, bool client_update, bool update_db) { +void Client::DeleteItemInInventory(int16 slot_id, int16 quantity, bool client_update, bool update_db) { #if (EQDEBUG >= 5) LogDebug("DeleteItemInInventory([{}], [{}], [{}])", slot_id, quantity, (client_update) ? "true":"false"); #endif @@ -2503,67 +2503,14 @@ void Client::DyeArmorBySlot(uint8 slot, uint8 red, uint8 green, uint8 blue, uint SendWearChange(slot); } -#if 0 -bool Client::DecreaseByItemType(uint32 type, uint8 amt) { - const ItemData* TempItem = 0; - EQ::ItemInstance* ins; - int x; - for(x=EQ::legacy::POSSESSIONS_BEGIN; x <= EQ::legacy::POSSESSIONS_END; x++) - { - TempItem = 0; - ins = GetInv().GetItem(x); - if (ins) - TempItem = ins->GetItem(); - if (TempItem && TempItem->ItemType == type) - { - if (ins->GetCharges() < amt) - { - amt -= ins->GetCharges(); - DeleteItemInInventory(x,amt,true); - } - else - { - DeleteItemInInventory(x,amt,true); - amt = 0; - } - if (amt < 1) - return true; - } - } - for(x=EQ::legacy::GENERAL_BAGS_BEGIN; x <= EQ::legacy::GENERAL_BAGS_END; x++) - { - TempItem = 0; - ins = GetInv().GetItem(x); - if (ins) - TempItem = ins->GetItem(); - if (TempItem && TempItem->ItemType == type) - { - if (ins->GetCharges() < amt) - { - amt -= ins->GetCharges(); - DeleteItemInInventory(x,amt,true); - } - else - { - DeleteItemInInventory(x,amt,true); - amt = 0; - } - if (amt < 1) - return true; - } - } - return false; -} -#endif - -bool Client::DecreaseByID(uint32 type, uint8 amt) { +bool Client::DecreaseByID(uint32 type, int16 quantity) { const EQ::ItemData* TempItem = nullptr; EQ::ItemInstance* ins = nullptr; int x; int num = 0; for (x = EQ::invslot::POSSESSIONS_BEGIN; x <= EQ::invslot::POSSESSIONS_END; ++x) { - if (num >= amt) + if (num >= quantity) break; if (((uint64)1 << x) & GetInv().GetLookup()->PossessionsBitmask == 0) continue; @@ -2577,7 +2524,7 @@ bool Client::DecreaseByID(uint32 type, uint8 amt) { } for (x = EQ::invbag::GENERAL_BAGS_BEGIN; x <= EQ::invbag::GENERAL_BAGS_END; ++x) { - if (num >= amt) + if (num >= quantity) break; if ((((uint64)1 << (EQ::invslot::GENERAL_BEGIN + ((x - EQ::invbag::GENERAL_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT))) & GetInv().GetLookup()->PossessionsBitmask) == 0) continue; @@ -2591,7 +2538,7 @@ bool Client::DecreaseByID(uint32 type, uint8 amt) { } for (x = EQ::invbag::CURSOR_BAG_BEGIN; x <= EQ::invbag::CURSOR_BAG_END; ++x) { - if (num >= amt) + if (num >= quantity) break; TempItem = nullptr; @@ -2602,12 +2549,12 @@ bool Client::DecreaseByID(uint32 type, uint8 amt) { num += ins->GetCharges(); } - if (num < amt) + if (num < quantity) return false; for (x = EQ::invslot::POSSESSIONS_BEGIN; x <= EQ::invslot::POSSESSIONS_END; ++x) { - if (amt < 1) + if (quantity < 1) break; if (((uint64)1 << x) & GetInv().GetLookup()->PossessionsBitmask == 0) continue; @@ -2619,18 +2566,18 @@ bool Client::DecreaseByID(uint32 type, uint8 amt) { if (TempItem && TempItem->ID != type) continue; - if (ins->GetCharges() < amt) { - amt -= ins->GetCharges(); - DeleteItemInInventory(x, amt, true); + if (ins->GetCharges() < quantity) { + quantity -= ins->GetCharges(); + DeleteItemInInventory(x, quantity, true); } else { - DeleteItemInInventory(x, amt, true); - amt = 0; + DeleteItemInInventory(x, quantity, true); + quantity = 0; } } for (x = EQ::invbag::GENERAL_BAGS_BEGIN; x <= EQ::invbag::GENERAL_BAGS_END; ++x) { - if (amt < 1) + if (quantity < 1) break; if ((((uint64)1 << (EQ::invslot::GENERAL_BEGIN + ((x - EQ::invbag::GENERAL_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT))) & GetInv().GetLookup()->PossessionsBitmask) == 0) continue; @@ -2642,18 +2589,18 @@ bool Client::DecreaseByID(uint32 type, uint8 amt) { if (TempItem && TempItem->ID != type) continue; - if (ins->GetCharges() < amt) { - amt -= ins->GetCharges(); - DeleteItemInInventory(x, amt, true); + if (ins->GetCharges() < quantity) { + quantity -= ins->GetCharges(); + DeleteItemInInventory(x, quantity, true); } else { - DeleteItemInInventory(x, amt, true); - amt = 0; + DeleteItemInInventory(x, quantity, true); + quantity = 0; } } for (x = EQ::invbag::CURSOR_BAG_BEGIN; x <= EQ::invbag::CURSOR_BAG_END; ++x) { - if (amt < 1) + if (quantity < 1) break; TempItem = nullptr; @@ -2663,13 +2610,13 @@ bool Client::DecreaseByID(uint32 type, uint8 amt) { if (TempItem && TempItem->ID != type) continue; - if (ins->GetCharges() < amt) { - amt -= ins->GetCharges(); - DeleteItemInInventory(x, amt, true); + if (ins->GetCharges() < quantity) { + quantity -= ins->GetCharges(); + DeleteItemInInventory(x, quantity, true); } else { - DeleteItemInInventory(x, amt, true); - amt = 0; + DeleteItemInInventory(x, quantity, true); + quantity = 0; } } @@ -2906,7 +2853,7 @@ void Client::RemoveNoRent(bool client_update) auto inst = m_inv[slot_id]; if(inst && !inst->GetItem()->NoRent) { LogInventory("NoRent Timer Lapse: Deleting [{}] from slot [{}]", inst->GetItem()->Name, slot_id); - DeleteItemInInventory(slot_id, 0, false); // Can't delete from client Bank slots + DeleteItemInInventory(slot_id); // Can't delete from client Bank slots } } @@ -2918,7 +2865,7 @@ void Client::RemoveNoRent(bool client_update) auto inst = m_inv[slot_id]; if(inst && !inst->GetItem()->NoRent) { LogInventory("NoRent Timer Lapse: Deleting [{}] from slot [{}]", inst->GetItem()->Name, slot_id); - DeleteItemInInventory(slot_id, 0, false); // Can't delete from client Bank Container slots + DeleteItemInInventory(slot_id); // Can't delete from client Bank Container slots } } @@ -2926,7 +2873,7 @@ void Client::RemoveNoRent(bool client_update) auto inst = m_inv[slot_id]; if(inst && !inst->GetItem()->NoRent) { LogInventory("NoRent Timer Lapse: Deleting [{}] from slot [{}]", inst->GetItem()->Name, slot_id); - DeleteItemInInventory(slot_id, 0, false); // Can't delete from client Shared Bank slots + DeleteItemInInventory(slot_id); // Can't delete from client Shared Bank slots } } @@ -2934,7 +2881,7 @@ void Client::RemoveNoRent(bool client_update) auto inst = m_inv[slot_id]; if(inst && !inst->GetItem()->NoRent) { LogInventory("NoRent Timer Lapse: Deleting [{}] from slot [{}]", inst->GetItem()->Name, slot_id); - DeleteItemInInventory(slot_id, 0, false); // Can't delete from client Shared Bank Container slots + DeleteItemInInventory(slot_id); // Can't delete from client Shared Bank Container slots } } diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 5bceb1e5d..fc300e200 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -2379,17 +2379,17 @@ XS(XS_Client_DeleteItemInInventory); /* prototype to pass -Wmissing-prototypes * XS(XS_Client_DeleteItemInInventory) { dXSARGS; if (items < 2 || items > 4) - Perl_croak(aTHX_ "Usage: Client::DeleteItemInInventory(THIS, int16 slot_id, [int8 quantity = 0], [bool client_update = false])"); // @categories Inventory and Items + Perl_croak(aTHX_ "Usage: Client::DeleteItemInInventory(THIS, int16 slot_id, [int16 quantity = 0], [bool client_update = false])"); // @categories Inventory and Items { Client *THIS; int16 slot_id = (int16) SvIV(ST(1)); - int8 quantity; + int16 quantity; bool client_update; VALIDATE_THIS_IS_CLIENT; if (items < 3) quantity = 0; else { - quantity = (int8) SvIV(ST(2)); + quantity = (int16) SvIV(ST(2)); } if (items < 4) @@ -2638,14 +2638,14 @@ XS(XS_Client_DecreaseByID); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_DecreaseByID) { dXSARGS; if (items != 3) - Perl_croak(aTHX_ "Usage: Client::DecreaseByID(THIS, uint32 type, unit8 amount)"); // @categories Script Utility + Perl_croak(aTHX_ "Usage: Client::DecreaseByID(THIS, uint32 type, int16 quantity)"); // @categories Script Utility { Client *THIS; bool RETVAL; uint32 type = (uint32) SvUV(ST(1)); - uint8 amt = (uint8) SvUV(ST(2)); + int16 quantity = (int16) SvIV(ST(2)); VALIDATE_THIS_IS_CLIENT; - RETVAL = THIS->DecreaseByID(type, amt); + RETVAL = THIS->DecreaseByID(type, quantity); ST(0) = boolSV(RETVAL); sv_2mortal(ST(0)); } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index efe826c9d..fedd4b3cb 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -622,22 +622,21 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if(IsClient()){ EQ::ItemInstance* transI = CastToClient()->GetInv().GetItem(EQ::invslot::slotCursor); if (transI && transI->IsClassCommon() && transI->IsStackable()){ - uint32 fcharges = transI->GetCharges(); - //Does it sound like meat... maybe should check if it looks like meat too... - if(strstr(transI->GetItem()->Name, "meat") || - strstr(transI->GetItem()->Name, "Meat") || - strstr(transI->GetItem()->Name, "flesh") || - strstr(transI->GetItem()->Name, "Flesh") || - strstr(transI->GetItem()->Name, "parts") || - strstr(transI->GetItem()->Name, "Parts")){ - CastToClient()->DeleteItemInInventory(EQ::invslot::slotCursor, fcharges, true); - CastToClient()->SummonItem(13073, fcharges); - } - else{ - Message(Chat::Red, "You can only transmute flesh to bone."); - } + int16 fcharges = transI->GetCharges(); + //Does it sound like meat... maybe should check if it looks like meat too... + if(strstr(transI->GetItem()->Name, "meat") || + strstr(transI->GetItem()->Name, "Meat") || + strstr(transI->GetItem()->Name, "flesh") || + strstr(transI->GetItem()->Name, "Flesh") || + strstr(transI->GetItem()->Name, "parts") || + strstr(transI->GetItem()->Name, "Parts")){ + CastToClient()->DeleteItemInInventory(EQ::invslot::slotCursor, fcharges, true); + CastToClient()->SummonItem(13073, fcharges); } - else{ + else{ + Message(Chat::Red, "You can only transmute flesh to bone."); + } + } else{ Message(Chat::Red, "You can only transmute flesh to bone."); } } diff --git a/zone/trading.cpp b/zone/trading.cpp index 27555b8fc..87ba86d68 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -1373,7 +1373,7 @@ uint16 Client::FindTraderItem(int32 SerialNumber, uint16 Quantity){ return 0; } -void Client::NukeTraderItem(uint16 Slot,int16 Charges,uint16 Quantity,Client* Customer,uint16 TraderSlot, int32 SerialNumber, int32 itemid) { +void Client::NukeTraderItem(uint16 Slot,int16 Charges,int16 Quantity,Client* Customer,uint16 TraderSlot, int32 SerialNumber, int32 itemid) { if(!Customer) return; @@ -1451,7 +1451,7 @@ void Client::TraderUpdate(uint16 SlotID,uint32 TraderID){ safe_delete(outapp); } -void Client::FindAndNukeTraderItem(int32 SerialNumber, uint16 Quantity, Client* Customer, uint16 TraderSlot){ +void Client::FindAndNukeTraderItem(int32 SerialNumber, int16 Quantity, Client* Customer, uint16 TraderSlot){ const EQ::ItemInstance* item= nullptr; bool Stackable = false; diff --git a/zone/tribute.cpp b/zone/tribute.cpp index 95640947e..ed8c69cf5 100644 --- a/zone/tribute.cpp +++ b/zone/tribute.cpp @@ -138,20 +138,20 @@ void Client::DoTributeUpdate() { uint32 tid = m_pp.tributes[r].tribute; if(tid == TRIBUTE_NONE) { if (m_inv[EQ::invslot::TRIBUTE_BEGIN + r]) - DeleteItemInInventory(EQ::invslot::TRIBUTE_BEGIN + r, 0, false); + DeleteItemInInventory(EQ::invslot::TRIBUTE_BEGIN + r); continue; } if(tribute_list.count(tid) != 1) { if (m_inv[EQ::invslot::TRIBUTE_BEGIN + r]) - DeleteItemInInventory(EQ::invslot::TRIBUTE_BEGIN + r, 0, false); + DeleteItemInInventory(EQ::invslot::TRIBUTE_BEGIN + r); continue; } //sanity check if(m_pp.tributes[r].tier >= MAX_TRIBUTE_TIERS) { if (m_inv[EQ::invslot::TRIBUTE_BEGIN + r]) - DeleteItemInInventory(EQ::invslot::TRIBUTE_BEGIN + r, 0, false); + DeleteItemInInventory(EQ::invslot::TRIBUTE_BEGIN + r); m_pp.tributes[r].tier = 0; continue; } @@ -165,7 +165,7 @@ void Client::DoTributeUpdate() { if(inst == nullptr) continue; - PutItemInInventory(EQ::invslot::TRIBUTE_BEGIN + r, *inst, false); + PutItemInInventory(EQ::invslot::TRIBUTE_BEGIN + r, *inst); SendItemPacket(EQ::invslot::TRIBUTE_BEGIN + r, inst, ItemPacketTributeItem); safe_delete(inst); } @@ -173,7 +173,7 @@ void Client::DoTributeUpdate() { //unequip tribute items... for (r = 0; r < EQ::invtype::TRIBUTE_SIZE; r++) { if (m_inv[EQ::invslot::TRIBUTE_BEGIN + r]) - DeleteItemInInventory(EQ::invslot::TRIBUTE_BEGIN + r, 0, false); + DeleteItemInInventory(EQ::invslot::TRIBUTE_BEGIN + r); } } CalcBonuses(); @@ -261,10 +261,10 @@ int32 Client::TributeItem(uint32 slot, uint32 quantity) { if(inst->IsStackable()) { if(inst->GetCharges() < (int32)quantity) //dont have enough.... return(0); - DeleteItemInInventory(slot, quantity, false); + DeleteItemInInventory(slot, quantity); } else { quantity = 1; - DeleteItemInInventory(slot, 0, false); + DeleteItemInInventory(slot); } pts *= quantity; From fac0d795f2ac5a0b36d75ac8de13a6478a329494 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 18 Nov 2021 09:10:02 -0500 Subject: [PATCH 418/624] [Bug Fix] Melee Life tap overflowing and causing damage (#1773) * Update mob.cpp * Update mob.cpp --- zone/mob.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index a16aab713..faea82b8e 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -5199,18 +5199,20 @@ int16 Mob::GetPositionalDmgAmt(Mob* defender) void Mob::MeleeLifeTap(int32 damage) { int32 lifetap_amt = 0; - lifetap_amt = spellbonuses.MeleeLifetap + itembonuses.MeleeLifetap + aabonuses.MeleeLifetap - + spellbonuses.Vampirism + itembonuses.Vampirism + aabonuses.Vampirism; + int32 melee_lifetap_mod = spellbonuses.MeleeLifetap + itembonuses.MeleeLifetap + aabonuses.MeleeLifetap + + spellbonuses.Vampirism + itembonuses.Vampirism + aabonuses.Vampirism; - if(lifetap_amt && damage > 0){ + if(melee_lifetap_mod && damage > 0){ - lifetap_amt = damage * lifetap_amt / 100; - LogCombat("Melee lifetap healing for [{}] damage", damage); + lifetap_amt = damage * (static_cast(melee_lifetap_mod) / 100.0f); + LogCombat("Melee lifetap healing [{}] points of damage with modifier of [{}] ", lifetap_amt, melee_lifetap_mod); - if (lifetap_amt > 0) + if (lifetap_amt >= 0) { HealDamage(lifetap_amt); //Heal self for modified damage amount. - else + } + else { Damage(this, -lifetap_amt, 0, EQ::skills::SkillEvocation, false); //Dmg self for modified damage amount. + } } } From 7559732408ed8e311837b0057c56a68c761bffce Mon Sep 17 00:00:00 2001 From: splose Date: Sun, 21 Nov 2021 01:19:04 -0500 Subject: [PATCH 419/624] [Bug Fix] Frenzy is supposed to use 1H Animation. (#1774) --- zone/special_attacks.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index c7025fc26..d29206db0 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -331,7 +331,7 @@ void Client::OPCombatAbility(const CombatAbility_Struct *ca_atk) CheckIncreaseSkill(EQ::skills::SkillFrenzy, GetTarget(), 10); int AtkRounds = 1; int32 max_dmg = GetBaseSkillDamage(EQ::skills::SkillFrenzy, GetTarget()); - DoAnim(anim2HSlashing, 0, false); + DoAnim(anim1HWeapon, 0, false); max_dmg = mod_frenzy_damage(max_dmg); @@ -1851,7 +1851,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) if (skill_to_use == EQ::skills::SkillFrenzy) { CheckIncreaseSkill(EQ::skills::SkillFrenzy, GetTarget(), 10); int AtkRounds = 1; - DoAnim(anim2HSlashing, 0, false); + DoAnim(anim1HWeapon, 0, false); ReuseTime = (FrenzyReuseTime - 1) / HasteMod; From 7c12c5d5ef2d513d757a028929079a00d94e3662 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 21 Nov 2021 09:56:10 -0500 Subject: [PATCH 420/624] [Commands] Cleanup #permaclass Command. (#1777) - Cleanup message and logic. --- zone/command.cpp | 2 +- zone/gm_commands/permaclass.cpp | 48 ++++++++++++++++++++------------- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 1ed2629e2..66956d85b 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -274,7 +274,7 @@ int command_init(void) command_add("path", "- view and edit pathing", AccountStatus::GMMgmt, command_path) || command_add("peekinv", "[equip/gen/cursor/poss/limbo/curlim/trib/bank/shbank/allbank/trade/world/all] - Print out contents of your player target's inventory", AccountStatus::GMAdmin, command_peekinv) || command_add("peqzone", "[zonename] - Go to specified zone, if you have > 75% health", AccountStatus::Player, command_peqzone) || - command_add("permaclass", "[classnum] - Change your or your player target's class (target is disconnected)", AccountStatus::QuestTroupe, command_permaclass) || + command_add("permaclass", "[Class ID] - Change your or your player target's class, changed client is disconnected", AccountStatus::QuestTroupe, command_permaclass) || command_add("permagender", "[gendernum] - Change your or your player target's gender (zone to take effect)", AccountStatus::QuestTroupe, command_permagender) || command_add("permarace", "[racenum] - Change your or your player target's race (zone to take effect)", AccountStatus::QuestTroupe, command_permarace) || command_add("petitioninfo", "[petition number] - Get info about a petition", AccountStatus::ApprenticeGuide, command_petitioninfo) || diff --git a/zone/gm_commands/permaclass.cpp b/zone/gm_commands/permaclass.cpp index 72c69b80f..3aa72c97a 100755 --- a/zone/gm_commands/permaclass.cpp +++ b/zone/gm_commands/permaclass.cpp @@ -2,27 +2,39 @@ void command_permaclass(Client *c, const Seperator *sep) { - Client *t = c; + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #permaclass [Class ID]"); + return; + } + Client *target = c; if (c->GetTarget() && c->GetTarget()->IsClient()) { - t = c->GetTarget()->CastToClient(); + target = c->GetTarget()->CastToClient(); } - if (sep->arg[1][0] == 0) { - c->Message(Chat::White, "Usage: #permaclass "); - } - else if (!t->IsClient()) { - c->Message(Chat::White, "Target is not a client."); - } - else { - c->Message(Chat::White, "Setting %s's class...Sending to char select.", t->GetName()); - LogInfo("Class change request from [{}] for [{}], requested class:[{}]", - c->GetName(), - t->GetName(), - atoi(sep->arg[1])); - t->SetBaseClass(atoi(sep->arg[1])); - t->Save(); - t->Kick("Class was changed."); + auto class_id = std::stoi(sep->arg[1]); + + LogInfo("Class changed by {} for {} to {} ({})", + c->GetCleanName(), + target->GetCleanName(), + GetClassIDName(class_id), + class_id + ); + + target->SetBaseClass(class_id); + target->Save(); + target->Kick("Class was changed."); + + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "Class changed for {} to {} ({}).", + target->GetCleanName(), + GetClassIDName(class_id), + class_id + ).c_str() + ); } } - From fb2f901539c634c23f0418b95cf2a3f650be7b4f Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 21 Nov 2021 09:56:27 -0500 Subject: [PATCH 421/624] [Commands] Cleanup #permarace Command. (#1778) - Cleanup message and logic. --- zone/command.cpp | 2 +- zone/gm_commands/permarace.cpp | 58 ++++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 66956d85b..1fb71af5c 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -276,7 +276,7 @@ int command_init(void) command_add("peqzone", "[zonename] - Go to specified zone, if you have > 75% health", AccountStatus::Player, command_peqzone) || command_add("permaclass", "[Class ID] - Change your or your player target's class, changed client is disconnected", AccountStatus::QuestTroupe, command_permaclass) || command_add("permagender", "[gendernum] - Change your or your player target's gender (zone to take effect)", AccountStatus::QuestTroupe, command_permagender) || - command_add("permarace", "[racenum] - Change your or your player target's race (zone to take effect)", AccountStatus::QuestTroupe, command_permarace) || + command_add("permarace", "[Race ID] - Change your or your player target's race", AccountStatus::QuestTroupe, command_permarace) || command_add("petitioninfo", "[petition number] - Get info about a petition", AccountStatus::ApprenticeGuide, command_petitioninfo) || command_add("pf", "- Display additional mob coordinate and wandering data", AccountStatus::Player, command_pf) || command_add("picklock", "Analog for ldon pick lock for the newer clients since we still don't have it working.", AccountStatus::Player, command_picklock) || diff --git a/zone/gm_commands/permarace.cpp b/zone/gm_commands/permarace.cpp index ac33c8605..d6065a797 100755 --- a/zone/gm_commands/permarace.cpp +++ b/zone/gm_commands/permarace.cpp @@ -2,33 +2,43 @@ void command_permarace(Client *c, const Seperator *sep) { - Client *t = c; - - if (c->GetTarget() && c->GetTarget()->IsClient()) { - t = c->GetTarget()->CastToClient(); - } - - if (sep->arg[1][0] == 0) { - c->Message(Chat::White, "Usage: #permarace "); + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #permarace [Race ID]"); c->Message( Chat::White, "NOTE: Not all models are global. If a model is not global, it will appear as a human on character select and in zones without the model." ); + return; } - else if (!t->IsClient()) { - c->Message(Chat::White, "Target is not a client."); - } - else { - c->Message(Chat::White, "Setting %s's race - zone to take effect", t->GetName()); - LogInfo("Permanant race change request from [{}] for [{}], requested race:[{}]", - c->GetName(), - t->GetName(), - atoi(sep->arg[1])); - uint32 tmp = Mob::GetDefaultGender(atoi(sep->arg[1]), t->GetBaseGender()); - t->SetBaseRace(atoi(sep->arg[1])); - t->SetBaseGender(tmp); - t->Save(); - t->SendIllusionPacket(atoi(sep->arg[1])); - } -} + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + + auto race_id = std::stoi(sep->arg[1]); + auto gender_id = Mob::GetDefaultGender(race_id, target->GetBaseGender()); + + LogInfo("Race changed by {} for {} to {} ({})", + c->GetCleanName(), + target->GetCleanName(), + GetRaceIDName(race_id), + race_id + ); + + target->SetBaseRace(race_id); + target->SetBaseGender(gender_id); + target->Save(); + target->SendIllusionPacket(race_id, gender_id); + + c->Message( + Chat::White, + fmt::format( + "Race changed for {} to {} ({}).", + c == target ? "yourself" : target->GetCleanName(), + GetRaceIDName(race_id), + race_id + ).c_str() + ); +} From f1d9221b4cb48bf67cb3b32f929fc9a874151550 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 21 Nov 2021 09:58:13 -0500 Subject: [PATCH 422/624] [Commands] Cleanup #invul Command. (#1780) - Cleanup message and logic. --- zone/command.cpp | 2 +- zone/gm_commands/invul.cpp | 28 ++++++++++++++++++---------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 1fb71af5c..56643a28d 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -221,7 +221,7 @@ int command_init(void) command_add("interrogateinv", "- use [help] argument for available options", AccountStatus::Player, command_interrogateinv) || command_add("interrupt", "[message id] [color] - Interrupt your casting. Arguments are optional.", AccountStatus::Guide, command_interrupt) || command_add("invsnapshot", "- Manipulates inventory snapshots for your current target", AccountStatus::QuestTroupe, command_invsnapshot) || - command_add("invul", "[on/off] - Turn player target's or your invulnerable flag on or off", AccountStatus::QuestTroupe, command_invul) || + command_add("invul", "[On|Off]] - Turn player target's or your invulnerable flag on or off", AccountStatus::QuestTroupe, command_invul) || command_add("ipban", "[IP address] - Ban IP by character name", AccountStatus::GMMgmt, command_ipban) || command_add("iplookup", "[charname] - Look up IP address of charname", AccountStatus::GMMgmt, command_iplookup) || command_add("iteminfo", "- Get information about the item on your cursor", AccountStatus::Steward, command_iteminfo) || diff --git a/zone/gm_commands/invul.cpp b/zone/gm_commands/invul.cpp index 4af42ceaf..a6db1f3c1 100755 --- a/zone/gm_commands/invul.cpp +++ b/zone/gm_commands/invul.cpp @@ -2,19 +2,27 @@ void command_invul(Client *c, const Seperator *sep) { - bool state = atobool(sep->arg[1]); - Client *t = c; + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #invul [On|Off]"); + return; + } + bool invul_flag = atobool(sep->arg[1]); + Client *target = c; if (c->GetTarget() && c->GetTarget()->IsClient()) { - t = c->GetTarget()->CastToClient(); + target = c->GetTarget()->CastToClient(); } - if (sep->arg[1][0] != 0) { - t->SetInvul(state); - c->Message(Chat::White, "%s is %s invulnerable from attack.", t->GetName(), state ? "now" : "no longer"); - } - else { - c->Message(Chat::White, "Usage: #invulnerable [on/off]"); - } + target->SetInvul(invul_flag); + c->Message( + Chat::White, + fmt::format( + "{} {} now {}.", + c == target ? "You" : target->GetCleanName(), + c == target ? "are" : "is", + invul_flag ? "invulnerable" : "vulnerable" + ).c_str() + ); } From 69d5fee471283083f35140e3bd16e7d9918bd574 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 21 Nov 2021 09:58:23 -0500 Subject: [PATCH 423/624] [Commands] Cleanup #givemoney Command. (#1781) * [Commands] Cleanup #givemoney Command. - Cleanup message and logic. * Update givemoney.cpp --- zone/command.cpp | 2 +- zone/gm_commands/givemoney.cpp | 133 ++++++++++++++++++++++++++------- 2 files changed, 107 insertions(+), 28 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 56643a28d..f63928a8f 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -193,7 +193,7 @@ int command_init(void) command_add("getvariable", "[varname] - Get the value of a variable from the database", AccountStatus::GMMgmt, command_getvariable) || command_add("ginfo", "- get group info on target.", AccountStatus::ApprenticeGuide, command_ginfo) || command_add("giveitem", "[itemid] [charges] - Summon an item onto your target's cursor. Charges are optional.", AccountStatus::GMMgmt, command_giveitem) || - command_add("givemoney", "[pp] [gp] [sp] [cp] - Gives specified amount of money to the target player.", AccountStatus::GMMgmt, command_givemoney) || + command_add("givemoney", "[Platinum] [Gold] [Silver] [Copper] - Gives specified amount of money to you or your player target", AccountStatus::GMMgmt, command_givemoney) || command_add("globalview", "Lists all qglobals in cache if you were to do a quest with this target.", AccountStatus::QuestTroupe, command_globalview) || command_add("gm", "- Turn player target's or your GM flag on or off", AccountStatus::QuestTroupe, command_gm) || command_add("gmspeed", "[on/off] - Turn GM speed hack on/off for you or your player target", AccountStatus::GMAdmin, command_gmspeed) || diff --git a/zone/gm_commands/givemoney.cpp b/zone/gm_commands/givemoney.cpp index e5b2c3167..ab71df4ce 100755 --- a/zone/gm_commands/givemoney.cpp +++ b/zone/gm_commands/givemoney.cpp @@ -2,32 +2,111 @@ void command_givemoney(Client *c, const Seperator *sep) { - if (!sep->IsNumber(1)) { //as long as the first one is a number, we'll just let atoi convert the rest to 0 or a number - c->Message(Chat::Red, "Usage: #Usage: #givemoney [pp] [gp] [sp] [cp]"); + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { //as long as the first one is a number, we'll just let atoi convert the rest to 0 or a number + c->Message(Chat::Red, "Usage: #Usage: #givemoney [Platinum] [Gold] [Silver] [Copper]"); + return; } - else if (c->GetTarget() == nullptr) { - c->Message(Chat::Red, "You must target a player to give money to."); - } - else if (!c->GetTarget()->IsClient()) { - c->Message(Chat::Red, "You can only give money to players with this command."); - } - else { - //TODO: update this to the client, otherwise the client doesn't show any weight change until you zone, move an item, etc - c->GetTarget()->CastToClient()->AddMoneyToPP( - atoi(sep->arg[4]), - atoi(sep->arg[3]), - atoi(sep->arg[2]), - atoi(sep->arg[1]), - true - ); - c->Message( - Chat::White, - "Added %i Platinum, %i Gold, %i Silver, and %i Copper to %s's inventory.", - atoi(sep->arg[1]), - atoi(sep->arg[2]), - atoi(sep->arg[3]), - atoi(sep->arg[4]), - c->GetTarget()->GetName()); - } -} + + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + + uint32 platinum = std::stoi(sep->arg[1]); + uint32 gold = std::stoi(sep->arg[2]); + uint32 silver = std::stoi(sep->arg[3]); + uint32 copper = std::stoi(sep->arg[4]); + if (!platinum && !gold && !silver && !copper) { + c->Message(Chat::Red, "Usage: #Usage: #givemoney [Platinum] [Gold] [Silver] [Copper]"); + return; + } + + target->AddMoneyToPP( + copper, + silver, + gold, + platinum, + true + ); + std::string money_string; + if (copper && silver && gold && platinum) { // CSGP + money_string = fmt::format( + "{} Platinum, {} Gold, {} Silver, and {} Copper", + platinum, + gold, + silver, + copper + ); + } else if (copper && silver && gold && !platinum) { // CSG + money_string = fmt::format( + "{} Gold, {} Silver, and {} Copper", + gold, + silver, + copper + ); + } else if (copper && silver && !gold && !platinum) { // CS + money_string = fmt::format( + "{} Silver and {} Copper", + silver, + copper + ); + } else if (copper && !silver && !gold && !platinum) { // C + money_string = fmt::format( + "{} Copper", + copper + ); + } else if (!copper && silver && gold && platinum) { // SGP + money_string = fmt::format( + "{} Platinum, {} Gold, and {} Silver", + platinum, + gold, + silver + ); + } else if (!copper && silver && gold && !platinum) { // SG + money_string = fmt::format( + "{} Gold and {} Silver", + gold, + silver + ); + } else if (!copper && silver && !gold && !platinum) { // S + money_string = fmt::format( + "{} Silver", + silver + ); + } else if (copper && !silver && gold && platinum) { // CGP + money_string = fmt::format( + "{} Platinum, {} Gold, and {} Copper", + platinum, + gold, + copper + ); + } else if (copper && !silver && gold && !platinum) { // CG + money_string = fmt::format( + "{} Gold and {} Copper", + gold, + copper + ); + } else if (!copper && !silver && gold && platinum) { // GP + money_string = fmt::format( + "{} Platinum and {} Gold", + platinum, + gold + ); + } else if (!copper && !silver && gold && !platinum) { // G + money_string = fmt::format( + "{} Gold", + gold + ); + } + + c->Message( + Chat::White, + fmt::format( + "Added {} to {}.", + money_string, + c == target ? "yourself" : target->GetCleanName() + ).c_str() + ); +} From dfe43ce1890ca1c0a02c0f4682446c113827dfbc Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 21 Nov 2021 09:59:07 -0500 Subject: [PATCH 424/624] [Commands] Cleanup #nukeitem Command. (#1782) * [Commands] Cleanup #nukeitem Command. - Cleanup message and logic. * Typo. --- zone/command.cpp | 2 +- zone/gm_commands/nukeitem.cpp | 41 ++++++++++++++++++++++++++++------- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index f63928a8f..173316ebf 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -267,7 +267,7 @@ int command_init(void) command_add("npctypespawn", "[npctypeid] [factionid] - Spawn an NPC from the db", AccountStatus::Steward, command_npctypespawn) || command_add("nudge", "- Nudge your target's current position by specific values", AccountStatus::QuestTroupe, command_nudge) || command_add("nukebuffs", "- Strip all buffs on you or your target", AccountStatus::Guide, command_nukebuffs) || - command_add("nukeitem", "[itemid] - Remove itemid from your player target's inventory", AccountStatus::GMLeadAdmin, command_nukeitem) || + command_add("nukeitem", "[Item ID] - Removes the specified Item ID from you or your player target's inventory", AccountStatus::GMLeadAdmin, command_nukeitem) || command_add("object", "List|Add|Edit|Move|Rotate|Copy|Save|Undo|Delete - Manipulate static and tradeskill objects within the zone", AccountStatus::GMAdmin, command_object) || command_add("oocmute", "[1/0] - Mutes OOC chat", AccountStatus::GMMgmt, command_oocmute) || command_add("opcode", "- opcode management", AccountStatus::GMImpossible, command_opcode) || diff --git a/zone/gm_commands/nukeitem.cpp b/zone/gm_commands/nukeitem.cpp index 91e219b4d..60fccd00f 100755 --- a/zone/gm_commands/nukeitem.cpp +++ b/zone/gm_commands/nukeitem.cpp @@ -2,15 +2,40 @@ void command_nukeitem(Client *c, const Seperator *sep) { - int numitems, itemid; - - if (c->GetTarget() && c->GetTarget()->IsClient() && (sep->IsNumber(1) || sep->IsHexNumber(1))) { - itemid = sep->IsNumber(1) ? atoi(sep->arg[1]) : hextoi(sep->arg[1]); - numitems = c->GetTarget()->CastToClient()->NukeItem(itemid); - c->Message(Chat::White, " %u items deleted", numitems); + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #nukeitem [Item ID] - Removes the specified Item ID from you or your player target's inventory"); + return; } - else { - c->Message(Chat::White, "Usage: (targted) #nukeitem itemnum - removes the item from the player's inventory"); + + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + + auto item_id = std::stoi(sep->arg[1]); + auto deleted_count = target->NukeItem(item_id); + if (deleted_count) { + c->Message( + Chat::White, + fmt::format( + "{} {} ({}) deleted from {}.", + deleted_count, + database.CreateItemLink(item_id), + item_id, + c == target ? "yourself" : target->GetCleanName() + ).c_str() + ); + } else { + c->Message( + Chat::White, + fmt::format( + "Could not find any {} ({}) to delete from {}.", + database.CreateItemLink(item_id), + item_id, + c == target ? "yourself" : target->GetCleanName() + ).c_str() + ); } } From 7154d5b84138d570a7444bb04cbaf13fc927a4e9 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 21 Nov 2021 10:03:08 -0500 Subject: [PATCH 425/624] [Commands] Cleanup #flags Command. (#1783) - Cleanup message and logic. --- zone/gm_commands/flags.cpp | 15 ++++++----- zone/zoning.cpp | 55 +++++++++++++++++++++++++++++--------- 2 files changed, 51 insertions(+), 19 deletions(-) diff --git a/zone/gm_commands/flags.cpp b/zone/gm_commands/flags.cpp index c2e3d1f2d..8b28c7d69 100755 --- a/zone/gm_commands/flags.cpp +++ b/zone/gm_commands/flags.cpp @@ -2,15 +2,16 @@ void command_flags(Client *c, const Seperator *sep) { - Client *t = c; + Client *target = c; - if (c->Admin() >= minStatusToSeeOthersZoneFlags) { - Mob *tgt = c->GetTarget(); - if (tgt != nullptr && tgt->IsClient()) { - t = tgt->CastToClient(); - } + if ( + c->GetTarget() && + c->GetTarget()->IsClient() && + c->Admin() >= minStatusToSeeOthersZoneFlags + ) { + target = c->GetTarget()->CastToClient(); } - t->SendZoneFlagInfo(c); + target->SendZoneFlagInfo(c); } diff --git a/zone/zoning.cpp b/zone/zoning.cpp index d305003b8..688d34e2f 100644 --- a/zone/zoning.cpp +++ b/zone/zoning.cpp @@ -1043,21 +1043,31 @@ bool Client::HasZoneFlag(uint32 zone_id) const { void Client::SendZoneFlagInfo(Client *to) const { if(zone_flags.empty()) { - to->Message(Chat::White, "%s has no zone flags.", GetName()); + to->Message( + Chat::White, + fmt::format( + "{} {} no Zone Flags.", + to == this ? "You" : GetName(), + to == this ? "have" : "has" + ).c_str() + ); return; } - std::set::const_iterator cur, end; - cur = zone_flags.begin(); - end = zone_flags.end(); - char empty[1] = { '\0' }; + to->Message( + Chat::White, + fmt::format( + "{} {} the following Flags:", + to == this ? "You" : GetName(), + to == this ? "have" : "has" + ).c_str() + ); - to->Message(Chat::White, "Flags for %s:", GetName()); - - for(; cur != end; ++cur) { - uint32 zone_id = *cur; + int flag_count = 0; + for (const auto& zone_id : zone_flags) { + int flag_number = (flag_count + 1); const char* zone_short_name = ZoneName(zone_id); - std::string zone_long_name = zone_store.GetZoneLongName(zone_id); + std::string zone_long_name = ZoneLongName(zone_id); float safe_x, safe_y, safe_z, safe_heading; int16 min_status = AccountStatus::Player; uint8 min_level = 0; @@ -1073,11 +1083,32 @@ void Client::SendZoneFlagInfo(Client *to) const { &min_level, flag_name )) { - strcpy(flag_name, "(ERROR GETTING NAME)"); + strcpy(flag_name, "ERROR"); } - to->Message(Chat::White, "Has Flag %s for zone %s (%d,%s)", flag_name, zone_long_name.c_str(), zone_id, zone_short_name); + to->Message( + Chat::White, + fmt::format( + "Zone Flag {} | Zone ID: {} Zone Name: {} ({}) Flag Name: {}", + flag_number, + zone_id, + zone_long_name, + zone_short_name, + flag_name + ).c_str() + ); + flag_count++; } + + to->Message( + Chat::White, + fmt::format( + "{} {} {} Zone Flags.", + to == this ? "You" : GetName(), + to == this ? "have" : "has", + flag_count + ).c_str() + ); } bool Client::CanBeInZone() { From 40edefa6f4e5f14d5913f9a92f409e05014191a5 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 21 Nov 2021 10:03:20 -0500 Subject: [PATCH 426/624] [Commands] Cleanup #camerashake Command. (#1787) - Cleanup message and logic. - Fix ConvertSecondsToTime logic for milliseconds. - Add ConvertMillisecondsToTime inline function. --- common/string_util.cpp | 21 ++++++++++++++++-- common/string_util.h | 5 ++++- world/zonelist.cpp | 2 +- zone/command.cpp | 2 +- zone/gm_commands/camerashake.cpp | 37 ++++++++++++++++++++------------ zone/gm_commands/stun.cpp | 2 +- zone/worldserver.cpp | 2 +- 7 files changed, 50 insertions(+), 21 deletions(-) diff --git a/common/string_util.cpp b/common/string_util.cpp index a770ce202..d89bbe39b 100644 --- a/common/string_util.cpp +++ b/common/string_util.cpp @@ -1021,9 +1021,26 @@ std::vector GetBadWords() }; } -std::string ConvertSecondsToTime(int duration) +std::string ConvertSecondsToTime(int duration, bool is_milliseconds) { - int timer_length = duration; + if (duration <= 0) { + return "Unknown"; + } + + if (is_milliseconds && duration < 1000) { + return fmt::format( + "{} Millisecond{}", + duration, + duration != 1 ? "s" : "" + ); + } + + int timer_length = ( + is_milliseconds ? + static_cast(std::ceil(static_cast(duration) / 1000.0f)) : + duration + ); + int days = int(timer_length / 86400000); timer_length %= 86400000; int hours = int(timer_length / 3600); diff --git a/common/string_util.h b/common/string_util.h index 07b5ef5d0..e9c3bfa15 100644 --- a/common/string_util.h +++ b/common/string_util.h @@ -45,7 +45,10 @@ std::vector wrap(std::vector &src, std::string charact std::string implode(std::string glue, std::vector src); std::string convert2digit(int n, std::string suffix); std::string numberToWords(unsigned long long int n); -std::string ConvertSecondsToTime(int duration); +std::string ConvertSecondsToTime(int duration, bool is_milliseconds = false); +inline std::string ConvertMillisecondsToTime(int duration) { + return ConvertSecondsToTime(duration, true); +} // For converstion of numerics into English // Used for grid nodes, as NPC names remove numerals. diff --git a/world/zonelist.cpp b/world/zonelist.cpp index c3aa358d3..128800e12 100644 --- a/world/zonelist.cpp +++ b/world/zonelist.cpp @@ -51,7 +51,7 @@ ZSList::~ZSList() { void ZSList::ShowUpTime(WorldTCPConnection* con, const char* adminname) { uint32 ms = Timer::GetCurrentTime(); - std::string time_string = ConvertSecondsToTime(ms); + std::string time_string = ConvertMillisecondsToTime(ms); con->SendEmoteMessage( adminname, 0, diff --git a/zone/command.cpp b/zone/command.cpp index 173316ebf..1d87b1861 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -133,7 +133,7 @@ int command_init(void) command_add("bot", "- Type \"#bot help\" or \"^help\" to the see the list of available commands for bots.", AccountStatus::Player, command_bot) || #endif - command_add("camerashake", "Shakes the camera on everyone's screen globally.", AccountStatus::QuestTroupe, command_camerashake) || + command_add("camerashake", "[Duration (Milliseconds)] [Intensity (1-10)] - Shakes the camera on everyone's screen globally.", AccountStatus::QuestTroupe, command_camerashake) || command_add("castspell", "[Spell ID] [Instant (0 = False, 1 = True, Default is 1 if Unused)] - Cast a spell", AccountStatus::Guide, command_castspell) || command_add("chat", "[channel num] [message] - Send a channel message to all zones", AccountStatus::GMMgmt, command_chat) || command_add("checklos", "- Check for line of sight to your target", AccountStatus::Guide, command_checklos) || diff --git a/zone/gm_commands/camerashake.cpp b/zone/gm_commands/camerashake.cpp index 39a6ebcfd..cb781a99a 100755 --- a/zone/gm_commands/camerashake.cpp +++ b/zone/gm_commands/camerashake.cpp @@ -5,20 +5,29 @@ extern WorldServer worldserver; void command_camerashake(Client *c, const Seperator *sep) { - if (c) { - if (sep->arg[1][0] && sep->arg[2][0]) { - auto pack = new ServerPacket(ServerOP_CameraShake, sizeof(ServerCameraShake_Struct)); - ServerCameraShake_Struct *scss = (ServerCameraShake_Struct *) pack->pBuffer; - scss->duration = atoi(sep->arg[1]); - scss->intensity = atoi(sep->arg[2]); - worldserver.SendPacket(pack); - c->Message(Chat::Red, "Successfully sent the packet to world! Shake it, world, shake it!"); - safe_delete(pack); - } - else { - c->Message(Chat::Red, "Usage -- #camerashake [duration], [intensity [1-10])"); - } + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1) || !sep->IsNumber(2)) { + c->Message(Chat::Red, "Usage: #camerashake [Duration (Milliseconds)] [Intensity (1-10)]"); + return; } - return; + + auto duration = std::stoi(sep->arg[1]); + auto intensity = std::stoi(sep->arg[2]); + + auto pack = new ServerPacket(ServerOP_CameraShake, sizeof(ServerCameraShake_Struct)); + ServerCameraShake_Struct *camera_shake = (ServerCameraShake_Struct *) pack->pBuffer; + camera_shake->duration = duration; + camera_shake->intensity = intensity; + worldserver.SendPacket(pack); + c->Message( + Chat::White, + fmt::format( + "Sending camera shake to world with a duration of {} ({}) and an intensity of {}.", + ConvertMillisecondsToTime(duration), + duration, + intensity + ).c_str() + ); + safe_delete(pack); } diff --git a/zone/gm_commands/stun.cpp b/zone/gm_commands/stun.cpp index 9775a45a3..01acce4f0 100755 --- a/zone/gm_commands/stun.cpp +++ b/zone/gm_commands/stun.cpp @@ -41,7 +41,7 @@ void command_stun(Client *c, const Seperator *sep) target->GetID() ) ), - ConvertSecondsToTime(duration) + ConvertMillisecondsToTime(duration) ) : fmt::format( "You unstunned {}.", diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index be039be8d..09f1e8134 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -826,7 +826,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } ServerUptime_Struct* sus = (ServerUptime_Struct*)pack->pBuffer; uint32 ms = Timer::GetCurrentTime(); - std::string time_string = ConvertSecondsToTime(ms); + std::string time_string = ConvertMillisecondsToTime(ms); SendEmoteMessage( sus->adminname, 0, From 39b39970f636016138c2d2660a4df3a09dc70bbf Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 21 Nov 2021 10:03:31 -0500 Subject: [PATCH 427/624] [Commands] Cleanup #ginfo Command. (#1788) - Cleanup message and logic. --- zone/gm_commands/ginfo.cpp | 93 +++++++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 32 deletions(-) diff --git a/zone/gm_commands/ginfo.cpp b/zone/gm_commands/ginfo.cpp index ddec8ba24..53af4fbf6 100755 --- a/zone/gm_commands/ginfo.cpp +++ b/zone/gm_commands/ginfo.cpp @@ -3,49 +3,78 @@ void command_ginfo(Client *c, const Seperator *sep) { - Client *t; - + Client *target = c; if (c->GetTarget() && c->GetTarget()->IsClient()) { - t = c->GetTarget()->CastToClient(); - } - else { - t = c; + target = c->GetTarget()->CastToClient(); } - Group *g = t->GetGroup(); - if (!g) { - c->Message(Chat::White, "This client is not in a group"); + auto target_group = target->GetGroup(); + if (!target_group) { + c->Message( + Chat::White, + fmt::format( + "{} {} not in a group.", + c == target ? "You" : target->GetCleanName(), + c == target ? "are" : "is" + ).c_str() + ); return; } c->Message( Chat::White, - "Player: %s is in Group #%lu: with %i members", - t->GetName(), - (unsigned long) g->GetID(), - g->GroupCount()); + fmt::format( + "Group Info for {} | ID: {} Members: {}", + c == target ? "Yourself" : target->GetCleanName(), + target_group->GetID(), + target_group->GroupCount() + ).c_str() + ); - uint32 r; - for (r = 0; r < MAX_GROUP_MEMBERS; r++) { - if (g->members[r] == nullptr) { - if (g->membername[r][0] == '\0') { - continue; - } - c->Message( - Chat::White, "...Zoned Member: %s, Roles: %s %s %s", g->membername[r], - (g->MemberRoles[r] & RoleAssist) ? "Assist" : "", - (g->MemberRoles[r] & RoleTank) ? "Tank" : "", - (g->MemberRoles[r] & RolePuller) ? "Puller" : "" - ); + for (int group_member = 0; group_member < MAX_GROUP_MEMBERS; group_member++) { + if (target_group->membername[group_member][0] == '\0') { + continue; } - else { - c->Message( - Chat::White, "...In-Zone Member: %s (0x%x) Roles: %s %s %s", g->membername[r], g->members[r], - (g->MemberRoles[r] & RoleAssist) ? "Assist" : "", - (g->MemberRoles[r] & RoleTank) ? "Tank" : "", - (g->MemberRoles[r] & RolePuller) ? "Puller" : "" - ); + + int member_number = (group_member + 1); + bool is_assist = target_group->MemberRoles[group_member] & RoleAssist; + bool is_puller = target_group->MemberRoles[group_member] & RolePuller; + bool is_tank = target_group->MemberRoles[group_member] & RoleTank; + auto member_string = ( + strcmp(target_group->membername[group_member], c->GetCleanName()) ? + ( + fmt::format( + "Name: {} In Zone: {}", + target_group->membername[group_member], + target_group->members[group_member] ? "Yes" : "No" + ) + ) : + "You" + ); + c->Message( + Chat::White, + fmt::format( + "Member {} | {}", + member_number, + member_string + ).c_str() + ); + if ( + is_assist || + is_puller || + is_tank + ) { + c->Message( + Chat::White, + fmt::format( + "Member {} Roles | Assist: {} Puller: {} Tank: {}", + member_number, + is_assist ? "Yes" : "No", + is_puller ? "Yes" : "No", + is_tank ? "Yes" : "No" + ).c_str() + ); } } } From ec1cf68ce2dbaf78351d9b2f4a5b99988c6aa8c7 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 21 Nov 2021 10:08:20 -0500 Subject: [PATCH 428/624] [Commands] Cleanup #damage Command. (#1789) - Cleanup logic. --- zone/command.cpp | 2 +- zone/gm_commands/damage.cpp | 27 ++++++++++++--------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 1d87b1861..1a3d1e66c 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -141,7 +141,7 @@ int command_init(void) command_add("corpse", "- Manipulate corpses, use with no arguments for help", AccountStatus::Guide, command_corpse) || command_add("corpsefix", "Attempts to bring corpses from underneath the ground within close proximity of the player", AccountStatus::Player, command_corpsefix) || command_add("cvs", "- Summary of client versions currently online.", AccountStatus::GMMgmt, command_cvs) || - command_add("damage", "[amount] - Damage your target", AccountStatus::GMAdmin, command_damage) || + command_add("damage", "[Amount] - Damage yourself or your target", AccountStatus::GMAdmin, command_damage) || command_add("databuckets", "View|Delete [key] [limit]- View data buckets, limit 50 default or Delete databucket by key", AccountStatus::QuestTroupe, command_databuckets) || command_add("date", "[yyyy] [mm] [dd] [HH] [MM] - Set EQ time", AccountStatus::EQSupport, command_date) || command_add("dbspawn2", "[spawngroup] [respawn] [variance] - Spawn an NPC from a predefined row in the spawn2 table", AccountStatus::GMAdmin, command_dbspawn2) || diff --git a/zone/gm_commands/damage.cpp b/zone/gm_commands/damage.cpp index 80de8470e..e2262bfaa 100755 --- a/zone/gm_commands/damage.cpp +++ b/zone/gm_commands/damage.cpp @@ -2,20 +2,17 @@ void command_damage(Client *c, const Seperator *sep) { - if (c->GetTarget() == 0) { - c->Message(Chat::White, "Error: #Damage: No Target."); + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #damage [Amount]"); + return; } - else if (!sep->IsNumber(1)) { - c->Message(Chat::White, "Usage: #damage x"); - } - else { - int32 nkdmg = atoi(sep->arg[1]); - if (nkdmg > 2100000000) { - c->Message(Chat::White, "Enter a value less then 2,100,000,000."); - } - else { - c->GetTarget()->Damage(c, nkdmg, SPELL_UNKNOWN, EQ::skills::SkillHandtoHand, false); - } - } -} + Mob* target = c; + if (c->GetTarget()) { + target = c->GetTarget(); + } + + int damage = static_cast(std::min(std::stoll(sep->arg[1]), (long long) 2000000000)); + target->Damage(c, damage, SPELL_UNKNOWN, EQ::skills::SkillHandtoHand, false); +} From d73194c1f6c1132b7c51feee5b037f40dcadea4a Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 21 Nov 2021 10:10:52 -0500 Subject: [PATCH 429/624] [Commands] Cleanup #setanim Command. (#1791) - Cleanup message and logic. - SetAppearance was not sending to self, so if you modified your own appearance, you wouldn't be able to see it. --- zone/command.cpp | 2 +- zone/gm_commands/setanim.cpp | 58 +++++++++++++++++++++++++++++++----- zone/mob.cpp | 7 +++-- 3 files changed, 56 insertions(+), 11 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 1a3d1e66c..178f4d8d1 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -321,7 +321,7 @@ int command_init(void) command_add("setaapts", "[AA|Group|Raid] [AA Amount] - Set your or your player target's Available AA Points by Type", AccountStatus::GMAdmin, command_setaapts) || command_add("setaaxp", "[AA|Group|Raid] [AA Experience] - Set your or your player target's AA Experience by Type", AccountStatus::GMAdmin, command_setaaxp) || command_add("setadventurepoints", "- Set your or your player target's available adventure points", AccountStatus::GMLeadAdmin, command_set_adventure_points) || - command_add("setanim", "[animnum] - Set target's appearance to animnum", AccountStatus::GMMgmt, command_setanim) || + command_add("setanim", "[Animation ID (IDs are 0 to 4)] - Set target's appearance to Animation ID", AccountStatus::GMMgmt, command_setanim) || command_add("setcrystals", "[value] - Set your or your player target's available radiant or ebon crystals", AccountStatus::GMAdmin, command_setcrystals) || command_add("setfaction", "[faction number] - Sets targeted NPC's faction in the database", AccountStatus::GMAreas, command_setfaction) || command_add("setgraveyard", "[zone name] - Creates a graveyard for the specified zone based on your target's LOC.", AccountStatus::GMMgmt, command_setgraveyard) || diff --git a/zone/gm_commands/setanim.cpp b/zone/gm_commands/setanim.cpp index d7ffa11e7..63b925166 100755 --- a/zone/gm_commands/setanim.cpp +++ b/zone/gm_commands/setanim.cpp @@ -2,15 +2,57 @@ void command_setanim(Client *c, const Seperator *sep) { - if (c->GetTarget() && sep->IsNumber(1)) { - int num = atoi(sep->arg[1]); - if (num < 0 || num >= _eaMaxAppearance) { - c->Message(Chat::White, "Invalid animation number, between 0 and %d", _eaMaxAppearance - 1); - } - c->GetTarget()->SetAppearance(EmuAppearance(num)); + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #setanim [Animation ID (IDs are 0 to 4)]"); + return; } - else { - c->Message(Chat::White, "Usage: #setanim [animnum]"); + + Mob* target = c; + if (c->GetTarget()) { + target = c->GetTarget(); } + + + int animation_id = std::stoi(sep->arg[1]); + if ( + animation_id < 0 || + animation_id > eaLooting + ) { + c->Message(Chat::White, "Usage: #setanim [Animation ID (IDs are 0 to 4)]"); + return; + } + + target->SetAppearance(static_cast(animation_id), false); + std::string animation_name; + if (animation_id == eaStanding) { + animation_name = "Standing"; + } else if (animation_id == eaSitting) { + animation_name = "Sitting"; + } else if (animation_id == eaCrouching) { + animation_name = "Crouching"; + } else if (animation_id == eaDead) { + animation_name = "Dead"; + } else if (animation_id == eaLooting) { + animation_name = "Looting"; + } + + c->Message( + Chat::White, + fmt::format( + "Set animation to {} ({}) for {}.", + animation_name, + animation_id, + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ).c_str() + ) + ).c_str() + ); } diff --git a/zone/mob.cpp b/zone/mob.cpp index faea82b8e..834ad6646 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -2999,12 +2999,15 @@ const int32& Mob::SetMana(int32 amount) void Mob::SetAppearance(EmuAppearance app, bool iIgnoreSelf) { - if (_appearance == app) + if (_appearance == app) { return; + } + _appearance = app; SendAppearancePacket(AT_Anim, GetAppearanceValue(app), true, iIgnoreSelf); - if (this->IsClient() && this->IsAIControlled()) + if (IsClient() && IsAIControlled()) { SendAppearancePacket(AT_Anim, ANIM_FREEZE, false, false); + } } bool Mob::UpdateActiveLight() From 51fb46556d2711a747cc6052e65d4e55a22ee8dc Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 21 Nov 2021 10:11:03 -0500 Subject: [PATCH 430/624] [Commands] Cleanup #setfaction Command. (#1792) - Cleanup message and logic. - Doesn't allow you to use invalid faction IDs anymore. --- zone/command.cpp | 2 +- zone/gm_commands/setfaction.cpp | 51 ++++++++++++++++++++++++++------- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 178f4d8d1..8c26d228d 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -323,7 +323,7 @@ int command_init(void) command_add("setadventurepoints", "- Set your or your player target's available adventure points", AccountStatus::GMLeadAdmin, command_set_adventure_points) || command_add("setanim", "[Animation ID (IDs are 0 to 4)] - Set target's appearance to Animation ID", AccountStatus::GMMgmt, command_setanim) || command_add("setcrystals", "[value] - Set your or your player target's available radiant or ebon crystals", AccountStatus::GMAdmin, command_setcrystals) || - command_add("setfaction", "[faction number] - Sets targeted NPC's faction in the database", AccountStatus::GMAreas, command_setfaction) || + command_add("setfaction", "[Faction ID] - Sets targeted NPC's faction in the database", AccountStatus::GMAreas, command_setfaction) || command_add("setgraveyard", "[zone name] - Creates a graveyard for the specified zone based on your target's LOC.", AccountStatus::GMMgmt, command_setgraveyard) || command_add("setlanguage", "[language ID] [value] - Set your target's language skillnum to value", AccountStatus::Guide, command_setlanguage) || command_add("setlsinfo", "[email] [password] - Set login server email address and password (if supported by login server)", AccountStatus::Steward, command_setlsinfo) || diff --git a/zone/gm_commands/setfaction.cpp b/zone/gm_commands/setfaction.cpp index 36662d3df..17b3c288f 100755 --- a/zone/gm_commands/setfaction.cpp +++ b/zone/gm_commands/setfaction.cpp @@ -2,19 +2,50 @@ void command_setfaction(Client *c, const Seperator *sep) { - if ((sep->arg[1][0] == 0 || strcasecmp(sep->arg[1], "*") == 0) || - ((c->GetTarget() == 0) || (c->GetTarget()->IsClient()))) { - c->Message(Chat::White, "Usage: #setfaction [faction number]"); + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #setfaction [Faction ID]"); + return; + } + + if ( + !c->GetTarget() || + ( + c->GetTarget() && + c->GetTarget()->IsClient() + ) + ) { + c->Message(Chat::White, "You must target an NPC to use this command."); return; } - auto npcTypeID = c->GetTarget()->CastToNPC()->GetNPCTypeID(); - c->Message(Chat::Yellow, "Setting NPC %u to faction %i", npcTypeID, atoi(sep->argplus[1])); + NPC* target = c->GetTarget()->CastToNPC(); + auto npc_id = target->GetNPCTypeID(); + auto faction_id = std::stoi(sep->arg[1]); + auto faction_name = content_db.GetFactionName(faction_id); + if (!faction_name.empty()) { + c->Message( + Chat::White, + fmt::format( + "Faction Changed | Name: {} ({}) Faction: {} ({}).", + target->GetCleanName(), + npc_id, + content_db.GetFactionName(faction_id), + faction_id + ).c_str() + ); - std::string query = StringFormat( - "UPDATE npc_types SET npc_faction_id = %i WHERE id = %i", - atoi(sep->argplus[1]), npcTypeID - ); - content_db.QueryDatabase(query); + std::string query = fmt::format( + "UPDATE npc_types SET npc_faction_id = {} WHERE id = {}", + faction_id, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message( + Chat::White, + "Invalid Faction ID, please specify a valid Faction ID." + ); + } } From 80f15ed04a4eee5470ecbd4761ec62c13294d45a Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 21 Nov 2021 10:11:29 -0500 Subject: [PATCH 431/624] [Commands] Cleanup #myskills Command. (#1796) - Cleanup popup window display and logic. --- zone/client.cpp | 97 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 23 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 3d7b0e89c..274a99bb4 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -5321,31 +5321,82 @@ uint32 Client::GetStartZone() void Client::ShowSkillsWindow() { - const char *WindowTitle = "Skills"; - std::string WindowText; - std::map Skills = EQ::skills::GetSkillTypeMap(); + std::string popup_text; + std::map skills_map = EQ::skills::GetSkillTypeMap(); - if (ClientVersion() < EQ::versions::ClientVersion::RoF2) - Skills[EQ::skills::Skill1HPiercing] = "Piercing"; - - // print out all available skills - for (auto skills_iter : Skills) { - if (skills_iter.first == EQ::skills::Skill2HPiercing && ClientVersion() < EQ::versions::ClientVersion::RoF2) - continue; - if (!GetSkill(skills_iter.first) && !MaxSkill(skills_iter.first)) - continue; - - WindowText += skills_iter.second; - // line up the values - WindowText += "      "; - WindowText += itoa(this->GetSkill(skills_iter.first)); - if (MaxSkill(skills_iter.first) > 0) { - WindowText += "/"; - WindowText += itoa(this->GetMaxSkillAfterSpecializationRules(skills_iter.first, this->MaxSkill(skills_iter.first))); - } - WindowText += "
"; + if (ClientVersion() < EQ::versions::ClientVersion::RoF2) { + skills_map[EQ::skills::Skill1HPiercing] = "Piercing"; } - this->SendPopupToClient(WindowTitle, WindowText.c_str()); + + // Table Start + popup_text += ""; + + for (const auto& skill : skills_map) { + auto skill_id = skill.first; + auto skill_name = skill.second; + auto can_have_skill = CanHaveSkill(skill_id); + auto current_skill = GetSkill(skill_id); + auto max_skill = MaxSkill(skill_id); + auto skill_maxed = current_skill >= max_skill; + if ( + skill_id == EQ::skills::Skill2HPiercing && + ClientVersion() < EQ::versions::ClientVersion::RoF2 + ) { + continue; + } + + if ( + !can_have_skill || + !current_skill || + !max_skill + ) { + continue; + } + + // Row Start + popup_text += ""; + + // Skill Name + popup_text += fmt::format( + "", + skill_name + ); + + // Current Skill Level out of Max Skill Level or a Check Mark for Maxed + popup_text += fmt::format( + "", + ( + skill_maxed ? + "" : + "" + ), + ( + skill_maxed ? + "✔" : + fmt::format( + "{}/{}", + current_skill, + max_skill + ) + ), + ( + skill_maxed ? + "" : + "" + ) + ); + + // Row End + popup_text += ""; + } + + // Table End + popup_text += "
{}{}{}{}
"; + + SendPopupToClient( + "Skills", + popup_text.c_str() + ); } void Client::Signal(uint32 data) From 446c5d90ec7dbafae73bd1de056c6590f74c44e5 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 21 Nov 2021 10:11:47 -0500 Subject: [PATCH 432/624] [Commands] Cleanup #killallnpcs Command. (#1797) - Cleanup message and logic. --- zone/gm_commands/killallnpcs.cpp | 83 ++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 30 deletions(-) diff --git a/zone/gm_commands/killallnpcs.cpp b/zone/gm_commands/killallnpcs.cpp index c298f2abb..bdb2750f0 100755 --- a/zone/gm_commands/killallnpcs.cpp +++ b/zone/gm_commands/killallnpcs.cpp @@ -4,42 +4,65 @@ void command_killallnpcs(Client *c, const Seperator *sep) { std::string search_string; if (sep->arg[1]) { - search_string = sep->arg[1]; + search_string = str_tolower(sep->arg[1]); } - int count = 0; - for (auto &itr : entity_list.GetMobList()) { - Mob *entity = itr.second; - if (!entity->IsNPC()) { + int killed_count = 0; + for (auto& npc_entity : entity_list.GetNPCList()) { + auto entity_id = npc_entity.first; + auto npc = npc_entity.second; + std::string entity_name = str_tolower(npc->GetName()); + if ( + ( + !search_string.empty() && + entity_name.find(search_string) == std::string::npos + ) || + !npc->IsAttackAllowed(c) + ) { continue; } - std::string entity_name = entity->GetName(); + npc->Damage( + c, + npc->GetHP(), + SPELL_UNKNOWN, + EQ::skills::SkillDragonPunch + ); - /** - * Filter by name - */ - if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { - continue; - } - - bool is_not_attackable = - ( - entity->IsInvisible() || - !entity->IsAttackAllowed(c) || - entity->GetRace() == 127 || - entity->GetRace() == 240 - ); - - if (is_not_attackable) { - continue; - } - - entity->Damage(c, 1000000000, 0, EQ::skills::SkillDragonPunch); - - count++; + killed_count++; } - c->Message(Chat::Yellow, "Killed (%i) npc(s)", count); + if (killed_count) { + c->Message( + Chat::White, + fmt::format( + "Killed {} NPC{}{}.", + killed_count, + killed_count != 1 ? "s" : "", + ( + !search_string.empty() ? + fmt::format( + " that matched '{}'", + search_string + ) : + "" + ) + ).c_str() + ); + } else { + c->Message( + Chat::White, + fmt::format( + "There were no NPCs to kill{}.", + ( + !search_string.empty() ? + fmt::format( + " that matched '{}'", + search_string + ) : + "" + ) + ).c_str() + ); + } } - From 04fda24c8e0a19cb8a10203ad18ce6224f876870 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 21 Nov 2021 10:12:02 -0500 Subject: [PATCH 433/624] [Commands] Cleanup #aggrozone Command. (#1798) - Cleanup message and logic. - Add the ability to aggro the zone on your target if you have one. --- zone/gm_commands/aggrozone.cpp | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/zone/gm_commands/aggrozone.cpp b/zone/gm_commands/aggrozone.cpp index afdf4ad64..797ed2128 100755 --- a/zone/gm_commands/aggrozone.cpp +++ b/zone/gm_commands/aggrozone.cpp @@ -2,18 +2,30 @@ void command_aggrozone(Client *c, const Seperator *sep) { - if (!c) { - return; + Mob *target = c; + if (c->GetTarget()) { + target = c->GetTarget(); } - Mob *m = c->CastToMob(); - - if (!m) { - return; + uint32 hate = 0; + if (sep->IsNumber(1)) { + hate = std::stoul(sep->arg[1]); } - uint32 hate = atoi(sep->arg[1]); //should default to 0 if we don't enter anything - entity_list.AggroZone(m, hate); - c->Message(Chat::White, "Train to you! Last chance to go invulnerable..."); + entity_list.AggroZone(target, hate); + c->Message( + Chat::White, + fmt::format( + "Aggroing zone on {}.", + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ) + ).c_str() + ); } - From b9214bfdee3c94ad3cfe9be9bcd07b3ed0158adf Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 21 Nov 2021 10:12:12 -0500 Subject: [PATCH 434/624] [Commands] Cleanup #aggro Command. (#1799) - Cleanup messages and logic. - Cleanup constant names and references. - Cleanup aggro description methods. --- common/faction.cpp | 44 +-- common/faction.h | 12 +- common/features.h | 2 +- zone/aggro.cpp | 612 ++++++++++++++++++++++++------------- zone/client.cpp | 28 +- zone/client_packet.cpp | 16 +- zone/command.cpp | 2 +- zone/effects.cpp | 8 +- zone/entity.cpp | 2 +- zone/gm_commands/aggro.cpp | 31 +- zone/lua_general.cpp | 10 +- zone/mob.cpp | 34 +-- zone/mob.h | 2 +- zone/npc.cpp | 10 +- zone/questmgr.cpp | 10 +- zone/spells.cpp | 8 +- 16 files changed, 512 insertions(+), 319 deletions(-) diff --git a/common/faction.cpp b/common/faction.cpp index 3dd8584e1..5f3fddacc 100644 --- a/common/faction.cpp +++ b/common/faction.cpp @@ -20,31 +20,31 @@ #include "races.h" #include "rulesys.h" -const char *FactionValueToString(FACTION_VALUE fv) +const char *FactionValueToString(FACTION_VALUE faction_value) { - switch (fv) { + switch (faction_value) { case FACTION_ALLY: - return ("Ally"); + return "Ally"; case FACTION_WARMLY: - return ("Warmly"); + return "Warmly"; case FACTION_KINDLY: - return ("Kindly"); - case FACTION_AMIABLE: - return ("Amiable"); - case FACTION_INDIFFERENT: - return ("Indifferent"); - case FACTION_APPREHENSIVE: - return ("Apprehensive"); - case FACTION_DUBIOUS: - return ("Dubious"); - case FACTION_THREATENLY: - return ("Threatenly"); + return "Kindly"; + case FACTION_AMIABLY: + return "Amiably"; + case FACTION_INDIFFERENTLY: + return "Indifferently"; + case FACTION_APPREHENSIVELY: + return "Apprehensively"; + case FACTION_DUBIOUSLY: + return "Dubiously"; + case FACTION_THREATENINGLY: + return "Threateningly"; case FACTION_SCOWLS: - return ("Scowls, ready to attack."); + return "Scowls"; default: break; } - return ("Unknown Faction Con"); + return "Unknown"; } @@ -70,19 +70,19 @@ FACTION_VALUE CalculateFaction(FactionMods* fm, int32 tmpCharacter_value) return FACTION_KINDLY; } if (character_value >= RuleI(Faction, AmiablyFactionMinimum)) { - return FACTION_AMIABLE; + return FACTION_AMIABLY; } if (character_value >= RuleI(Faction, IndifferentlyFactionMinimum)) { - return FACTION_INDIFFERENT; + return FACTION_INDIFFERENTLY; } if (character_value >= RuleI(Faction, ApprehensivelyFactionMinimum)) { - return FACTION_APPREHENSIVE; + return FACTION_APPREHENSIVELY; } if (character_value >= RuleI(Faction, DubiouslyFactionMinimum)) { - return FACTION_DUBIOUS; + return FACTION_DUBIOUSLY; } if (character_value >= RuleI(Faction, ThreateninglyFactionMinimum)) { - return FACTION_THREATENLY; + return FACTION_THREATENINGLY; } return FACTION_SCOWLS; } diff --git a/common/faction.h b/common/faction.h index 8c4bd8452..907e393fc 100755 --- a/common/faction.h +++ b/common/faction.h @@ -27,13 +27,13 @@ enum FACTION_VALUE { FACTION_ALLY = 1, FACTION_WARMLY = 2, FACTION_KINDLY = 3, - FACTION_AMIABLE = 4, + FACTION_AMIABLY = 4, - FACTION_INDIFFERENT = 5, + FACTION_INDIFFERENTLY = 5, - FACTION_APPREHENSIVE = 6, - FACTION_DUBIOUS = 7, - FACTION_THREATENLY = 8, + FACTION_APPREHENSIVELY = 6, + FACTION_DUBIOUSLY = 7, + FACTION_THREATENINGLY = 8, FACTION_SCOWLS = 9 }; @@ -75,6 +75,6 @@ struct NPCFaction uint8 temp; }; -const char *FactionValueToString(FACTION_VALUE fv); +const char *FactionValueToString(FACTION_VALUE faction_value); FACTION_VALUE CalculateFaction(FactionMods* fm, int32 tmpCharacter_value); #endif diff --git a/common/features.h b/common/features.h index 6881e2e1b..e1ca61f64 100644 --- a/common/features.h +++ b/common/features.h @@ -204,7 +204,7 @@ enum { //some random constants #define MIN_LEVEL_ALCHEMY 25 //chance ratio that a -#define THREATENLY_ARRGO_CHANCE 32 // 32/128 (25%) chance that a mob will arrgo on con Threatenly +#define THREATENINGLY_AGGRO_CHANCE 32 // 32/128 (25%) chance that a mob will arrgo on con Threatenly //max factions per npc faction list #define MAX_NPC_FACTIONS 20 diff --git a/zone/aggro.cpp b/zone/aggro.cpp index ffca54c49..ba217960e 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -38,190 +38,359 @@ extern Zone* zone; //#define LOSDEBUG 6 void EntityList::DescribeAggro(Client *towho, NPC *from_who, float d, bool verbose) { - float d2 = d*d; + float distance_squared = (d * d); - towho->Message(Chat::White, "Describing aggro for %s", from_who->GetName()); + towho->Message( + Chat::White, + fmt::format( + "Describing aggro for {} ({}).", + from_who->GetCleanName(), + from_who->GetID() + ).c_str() + ); - bool engaged = from_who->IsEngaged(); - if(engaged) { + bool is_engaged = from_who->IsEngaged(); + bool will_aggro_npcs = from_who->WillAggroNPCs(); + if (is_engaged) { Mob *top = from_who->GetHateTop(); - towho->Message(Chat::White, ".. I am currently fighting with %s", top == nullptr?"(nullptr)":top->GetName()); + towho->Message( + Chat::White, + fmt::format( + "I am currently engaged with {}.", + ( + !top ? + "nothing" : + fmt::format( + "{} ({})", + top->GetCleanName(), + top->GetID() + ) + ) + ).c_str() + ); } - bool check_npcs = from_who->WillAggroNPCs(); - if(verbose) { - char namebuf[256]; - - int my_primary = from_who->GetPrimaryFaction(); - Mob *own = from_who->GetOwner(); - if(own != nullptr) - my_primary = own->GetPrimaryFaction(); - - if(my_primary == 0) { - strcpy(namebuf, "(No faction)"); - } else if(my_primary < 0) { - strcpy(namebuf, "(Special faction)"); - } else { - if(!content_db.GetFactionName(my_primary, namebuf, sizeof(namebuf))) - strcpy(namebuf, "(Unknown)"); + if (verbose) { + int faction_id = from_who->GetPrimaryFaction(); + Mob *owner = from_who->GetOwner(); + if(owner) { + faction_id = owner->GetPrimaryFaction(); } - towho->Message(Chat::White, ".. I am on faction %s (%d)\n", namebuf, my_primary); + + std::string faction_name = ( + faction_id > 0 ? + content_db.GetFactionName(faction_id) : + ( + faction_id == 0 ? + "None" : + fmt::format( + "Special Faction {}", + faction_id + ) + ) + ); + + towho->Message( + Chat::White, + fmt::format( + "{} ({}) is on Faction {} ({}).", + from_who->GetCleanName(), + from_who->GetID(), + faction_name, + faction_id + ).c_str() + ); } - for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { - Mob *mob = it->second; - if (mob->IsClient()) //also ensures that mob != around + for (const auto& npc_entity : entity_list.GetNPCList()) { + auto entity_id = npc_entity.first; + auto npc = npc_entity.second; + if (npc == from_who) { continue; + } - if (DistanceSquared(mob->GetPosition(), from_who->GetPosition()) > d2) + if (DistanceSquared(npc->GetPosition(), from_who->GetPosition()) > distance_squared) { continue; + } - if (engaged) { - uint32 amm = from_who->GetHateAmount(mob); - if (amm == 0) - towho->Message(Chat::White, "... %s is not on my hate list.", mob->GetName()); - else - towho->Message(Chat::White, "... %s is on my hate list with value %lu", mob->GetName(), (unsigned long)amm); - } else if (!check_npcs && mob->IsNPC()) { - towho->Message(Chat::White, "... %s is an NPC and my npc_aggro is disabled.", mob->GetName()); + if (is_engaged) { + uint32 hate_amount = from_who->GetHateAmount(npc); + towho->Message( + Chat::White, + fmt::format( + "{} ({}) is {}on my hate list{}.", + npc->GetCleanName(), + npc->GetID(), + !hate_amount ? "not " : "", + ( + !hate_amount ? + "" : + fmt::format( + " with a hate amount of {}.", + hate_amount + ) + ) + ).c_str() + ); + } else if (!will_aggro_npcs) { + towho->Message( + Chat::White, + fmt::format( + "{} ({}) is an NPC and I cannot aggro NPCs.", + npc->GetCleanName(), + npc->GetID() + ).c_str() + ); } else { - from_who->DescribeAggro(towho, mob, verbose); + from_who->DescribeAggro(towho, npc, verbose); } } } -void NPC::DescribeAggro(Client *towho, Mob *mob, bool verbose) { +void NPC::DescribeAggro(Client *towho, Mob *mob, bool verbose) { //this logic is duplicated from below, try to keep it up to date. - float iAggroRange = GetAggroRange(); - - float t1, t2, t3; - t1 = std::abs(mob->GetX() - GetX()); - t2 = std::abs(mob->GetY() - GetY()); - t3 = std::abs(mob->GetZ() - GetZ()); - - if(( t1 > iAggroRange) - || ( t2 > iAggroRange) - || ( t3 > iAggroRange) ) { - towho->Message(Chat::White, "...%s is out of range (fast). distances (%.3f,%.3f,%.3f), range %.3f", mob->GetName(), - t1, t2, t3, iAggroRange); + float aggro_range = GetAggroRange(); + float x_range = std::abs(mob->GetX() - GetX()); + float y_range = std::abs(mob->GetY() - GetY()); + float z_range = std::abs(mob->GetZ() - GetZ()); + if ( + x_range > aggro_range || + y_range > aggro_range || + z_range > aggro_range + ) { + towho->Message( + Chat::White, + fmt::format( + "{} ({}) is out of range. X Range: {} Y Range: {} Z Range: {} Aggro Range: {}", + mob->GetCleanName(), + mob->GetID(), + x_range, + y_range, + z_range, + aggro_range + ).c_str() + ); return; } - if(mob->IsInvisible(this)) { - towho->Message(Chat::White, "...%s is invisible to me. ", mob->GetName()); + if (mob->IsInvisible(this)) { + towho->Message( + Chat::White, + fmt::format( + "{} ({}) is invisible to me. ", + mob->GetCleanName(), + mob->GetID() + ).c_str() + ); return; } - if((mob->IsClient() && - (!mob->CastToClient()->Connected() - || mob->CastToClient()->IsLD() - || mob->CastToClient()->IsBecomeNPC() - || mob->CastToClient()->GetGM() + + if ( + mob->IsClient() && + ( + !mob->CastToClient()->Connected() || + mob->CastToClient()->IsLD() || + mob->CastToClient()->IsBecomeNPC() || + mob->CastToClient()->GetGM() ) - )) - { - towho->Message(Chat::White, "...%s is my owner. ", mob->GetName()); + ) { + towho->Message( + Chat::White, + fmt::format( + "{} ({}) is a GM or is not connected. ", + mob->GetCleanName(), + mob->GetID() + ).c_str() + ); return; } - if(mob == GetOwner()) { - towho->Message(Chat::White, "...%s a GM or is not connected. ", mob->GetName()); + if (mob == GetOwner()) { + towho->Message( + Chat::White, + fmt::format( + "{} ({}) is my owner. ", + mob->GetCleanName(), + mob->GetID() + ).c_str() + ); return; } - float dist2 = DistanceSquared(mob->GetPosition(), m_Position); - - float iAggroRange2 = iAggroRange*iAggroRange; - if( dist2 > iAggroRange2 ) { - towho->Message(Chat::White, "...%s is out of range. %.3f > %.3f ", mob->GetName(), - dist2, iAggroRange2); + float distance_squared = DistanceSquared(mob->GetPosition(), m_Position); + float aggro_range_squared = (aggro_range * aggro_range); + if (distance_squared > aggro_range_squared) { + towho->Message( + Chat::White, + fmt::format( + "{} ({}) is out of range. Distance: {:.2f} Aggro Range: {:.2f}", + mob->GetCleanName(), + mob->GetID(), + distance_squared, + aggro_range_squared + ).c_str() + ); return; } - if (RuleB(Aggro, UseLevelAggro)) - { - if (GetLevel() < RuleI(Aggro, MinAggroLevel) && mob->GetLevelCon(GetLevel()) == CON_GRAY && GetBodyType() != 3 && !AlwaysAggro()) - { - towho->Message(Chat::White, "...%s is red to me (basically)", mob->GetName(), dist2, iAggroRange2); + if (RuleB(Aggro, UseLevelAggro)) { + if ( + GetLevel() < RuleI(Aggro, MinAggroLevel) && + mob->GetLevelCon(GetLevel()) == CON_GRAY && + GetBodyType() != BT_Undead && + !AlwaysAggro() + ) { + towho->Message( + Chat::White, + fmt::format( + "{} ({}) considers Red to me.", + mob->GetCleanName(), + mob->GetID() + ).c_str() + ); return; } - } - else - { - if(GetINT() > RuleI(Aggro, IntAggroThreshold) && mob->GetLevelCon(GetLevel()) == CON_GRAY && !AlwaysAggro()) { - towho->Message(Chat::White, "...%s is red to me (basically)", mob->GetName(), - dist2, iAggroRange2); + } else { + if ( + GetINT() > RuleI(Aggro, IntAggroThreshold) && + mob->GetLevelCon(GetLevel()) == CON_GRAY && + !AlwaysAggro() + ) { + towho->Message( + Chat::White, + fmt::format( + "{} ({}) considers Red to me.", + mob->GetCleanName(), + mob->GetID() + ).c_str() + ); return; } } - if(verbose) { - int my_primary = GetPrimaryFaction(); - int mob_primary = mob->GetPrimaryFaction(); - Mob *own = GetOwner(); - if(own != nullptr) - my_primary = own->GetPrimaryFaction(); - own = mob->GetOwner(); - if(mob_primary > 0 && own != nullptr) - mob_primary = own->GetPrimaryFaction(); + if (verbose) { + int faction_id = GetPrimaryFaction(); + int mob_faction_id = mob->GetPrimaryFaction(); + Mob *owner = GetOwner(); + if (owner) { + faction_id = owner->GetPrimaryFaction(); + } - if(mob_primary == 0) { - towho->Message(Chat::White, "...%s has no primary faction", mob->GetName()); - } else if(mob_primary < 0) { - towho->Message(Chat::White, "...%s is on special faction %d", mob->GetName(), mob_primary); + owner = mob->GetOwner(); + if (mob_faction_id && owner) { + mob_faction_id = owner->GetPrimaryFaction(); + } + + if (!mob_faction_id) { + towho->Message( + Chat::White, + fmt::format( + "{} ({}) has no primary Faction.", + mob->GetCleanName(), + mob->GetID() + ).c_str() + ); + } else if (mob_faction_id < 0) { + towho->Message( + Chat::White, + fmt::format( + "{} ({}) is on special Faction {}.", + mob->GetCleanName(), + mob->GetID(), + mob_faction_id + ).c_str() + ); } else { - char namebuf[256]; - if(!content_db.GetFactionName(mob_primary, namebuf, sizeof(namebuf))) - strcpy(namebuf, "(Unknown)"); - std::list::iterator cur,end; - cur = faction_list.begin(); - end = faction_list.end(); - bool res = false; - for(; cur != end; ++cur) { - struct NPCFaction* fac = *cur; - if ((int32)fac->factionID == mob_primary) { - if (fac->npc_value > 0) { - towho->Message(Chat::White, "...%s is on ALLY faction %s (%d) with %d", mob->GetName(), namebuf, mob_primary, fac->npc_value); - res = true; - break; - } else if (fac->npc_value < 0) { - towho->Message(Chat::White, "...%s is on ENEMY faction %s (%d) with %d", mob->GetName(), namebuf, mob_primary, fac->npc_value); - res = true; - break; - } else { - towho->Message(Chat::White, "...%s is on NEUTRAL faction %s (%d) with 0", mob->GetName(), namebuf, mob_primary); - res = true; - break; - } + auto faction_name = content_db.GetFactionName(mob_faction_id); + bool has_entry = false; + for (auto faction : faction_list) { + if (static_cast(faction->factionID) == mob_faction_id) { + towho->Message( + Chat::White, + fmt::format( + "{} ({}) has {} standing with Faction {} ({}) with their Faction Level of {}", + mob->GetCleanName(), + mob->GetID(), + ( + faction->npc_value != 0 ? + ( + faction->npc_value > 0 ? + "positive" : + "negative" + ) : + "neutral" + ), + faction_name, + faction->factionID, + faction->npc_value + ).c_str() + ); + has_entry = true; + break; } } - if(!res) { - towho->Message(Chat::White, "...%s is on faction %s (%d), which I have no entry for.", mob->GetName(), namebuf, mob_primary); + + if (!has_entry) { + towho->Message( + Chat::White, + fmt::format( + "{} ({}) is on Faction {} ({}), for which I do not have an entry.", + mob->GetCleanName(), + mob->GetID(), + faction_name, + mob_faction_id + ).c_str() + ); } } } - FACTION_VALUE fv = mob->GetReverseFactionCon(this); + auto faction_value = mob->GetReverseFactionCon(this); - if(!( - fv == FACTION_SCOWLS - || - (mob->GetPrimaryFaction() != GetPrimaryFaction() && mob->GetPrimaryFaction() == -4 && GetOwner() == nullptr) - || - fv == FACTION_THREATENLY - )) { - towho->Message(Chat::White, "...%s faction not low enough. value='%s'", mob->GetName(), FactionValueToString(fv)); + if( + !( + faction_value == FACTION_THREATENINGLY || + faction_value == FACTION_SCOWLS || + ( + mob->GetPrimaryFaction() != GetPrimaryFaction() && + mob->GetPrimaryFaction() == -4 && + !GetOwner() + ) + ) + ) { + towho->Message( + Chat::White, + fmt::format( + "{} ({}) does not have low enough faction, their Faction Level is {} ({}).", + mob->GetCleanName(), + mob->GetID(), + FactionValueToString(faction_value), + faction_value + ).c_str() + ); return; } - if(fv == FACTION_THREATENLY) { - towho->Message(Chat::White, "...%s threatening to me, so they only have a %d chance per check of attacking.", mob->GetName()); - } if(!CheckLosFN(mob)) { - towho->Message(Chat::White, "...%s is out of sight.", mob->GetName()); + towho->Message( + Chat::White, + fmt::format( + "{} ({}) is out of sight.", + mob->GetCleanName(), + mob->GetID() + ).c_str() + ); } - towho->Message(Chat::White, "...%s meets all conditions, I should be attacking them.", mob->GetName()); + towho->Message( + Chat::White, + fmt::format( + "{} ({}) meets all conditions, I should be attacking them.", + mob->GetCleanName(), + mob->GetID() + ).c_str() + ); } /* @@ -229,65 +398,87 @@ void NPC::DescribeAggro(Client *towho, Mob *mob, bool verbose) { to keep the #aggro command accurate. */ bool Mob::CheckWillAggro(Mob *mob) { - if(!mob) + if(!mob) { return false; + } //sometimes if a client has some lag while zoning into a dangerous place while either invis or a GM //they will aggro mobs even though it's supposed to be impossible, to lets make sure we've finished connecting if (mob->IsClient()) { - if (!mob->CastToClient()->ClientFinishedLoading() || mob->CastToClient()->IsHoveringForRespawn() || mob->CastToClient()->bZoning) + if ( + !mob->CastToClient()->ClientFinishedLoading() || + mob->CastToClient()->IsHoveringForRespawn() || + mob->CastToClient()->bZoning + ) { return false; + } } // We don't want to aggro clients outside of water if we're water only. - if (mob->IsClient() && mob->CastToClient()->GetLastRegion() != RegionTypeWater && IsUnderwaterOnly()) { + if ( + mob->IsClient() && + mob->CastToClient()->GetLastRegion() != RegionTypeWater && + IsUnderwaterOnly() + ) { return false; } /** * Pets shouldn't scan for aggro */ - if (this->GetOwner()) { + if (GetOwner()) { return false; } Mob *pet_owner = mob->GetOwner(); - if (pet_owner && pet_owner->IsClient() && (!RuleB(Aggro, AggroPlayerPets) || pet_owner->CastToClient()->GetGM())) { + if ( + pet_owner && + pet_owner->IsClient() && + ( + !RuleB(Aggro, AggroPlayerPets) || + pet_owner->CastToClient()->GetGM() + ) + ) { return false; } - float iAggroRange = GetAggroRange(); - // Check If it's invisible and if we can see invis // Check if it's a client, and that the client is connected and not linkdead, // and that the client isn't Playing an NPC, with thier gm flag on // Check if it's not a Interactive NPC // Trumpcard: The 1st 3 checks are low cost calcs to filter out unnessecary distance checks. Leave them at the beginning, they are the most likely occurence. // Image: I moved this up by itself above faction and distance checks because if one of these return true, theres no reason to go through the other information + + float aggro_range = GetAggroRange(); + float x_range = std::abs(mob->GetX() - GetX()); + float y_range = std::abs(mob->GetY() - GetY()); + float z_range = std::abs(mob->GetZ() - GetZ()); - float t1, t2, t3; - t1 = std::abs(mob->GetX() - GetX()); - t2 = std::abs(mob->GetY() - GetY()); - t3 = std::abs(mob->GetZ() - GetZ()); - - if(( t1 > iAggroRange) - || ( t2 > iAggroRange) - || ( t3 > iAggroRange) - || (mob->IsInvisible(this)) - || (mob->IsClient() && - (!mob->CastToClient()->Connected() + if ( + x_range > aggro_range || + y_range > aggro_range || + z_range > aggro_range || + mob->IsInvisible(this) || + ( + mob->IsClient() && + ( + !mob->CastToClient()->Connected() || mob->CastToClient()->IsLD() || mob->CastToClient()->IsBecomeNPC() || mob->CastToClient()->GetGM() ) - )) - { - return(false); + ) + ) { + return false; } // Don't aggro new clients if we are already engaged unless PROX_AGGRO is set if (IsEngaged() && (!GetSpecialAbility(PROX_AGGRO) || (GetSpecialAbility(PROX_AGGRO) && !CombatRange(mob)))) { - LogAggro("[{}] is in combat, and does not have prox_aggro, or does and is out of combat range with [{}]", GetName(), mob->GetName()); + LogAggro( + "[{}] is in combat, and does not have prox_aggro, or does and is out of combat range with [{}]", + GetName(), + mob->GetName() + ); return false; } @@ -296,105 +487,100 @@ bool Mob::CheckWillAggro(Mob *mob) { //aggro this mob...??? //changed to be 'if I have an owner and this is it' if(mob == GetOwner()) { - return(false); + return false; } - float dist2 = DistanceSquared(mob->GetPosition(), m_Position); - float iAggroRange2 = iAggroRange*iAggroRange; + float distance_squared = DistanceSquared(mob->GetPosition(), m_Position); + float aggro_range_squared = (aggro_range * aggro_range); - if( dist2 > iAggroRange2 ) { + if (distance_squared > aggro_range_squared ) { // Skip it, out of range - return(false); + return false; } //Image: Get their current target and faction value now that its required //this function call should seem backwards - FACTION_VALUE fv = mob->GetReverseFactionCon(this); + FACTION_VALUE faction_value = mob->GetReverseFactionCon(this); // Make sure they're still in the zone // Are they in range? // Are they kos? // Are we stupid or are they green // and they don't have their gm flag on - int heroicCHA_mod = mob->itembonuses.HeroicCHA/25; // 800 Heroic CHA cap - if(heroicCHA_mod > THREATENLY_ARRGO_CHANCE) - heroicCHA_mod = THREATENLY_ARRGO_CHANCE; - if (RuleB(Aggro, UseLevelAggro) && - ( - //old InZone check taken care of above by !mob->CastToClient()->Connected() - ( - ( GetLevel() >= RuleI(Aggro, MinAggroLevel)) - ||(GetBodyType() == 3) || AlwaysAggro() - ||( mob->IsClient() && mob->CastToClient()->IsSitting() ) - ||( mob->GetLevelCon(GetLevel()) != CON_GRAY) + int heroic_cha_mod = (mob->itembonuses.HeroicCHA / 25); // 800 Heroic CHA cap + if(heroic_cha_mod > THREATENINGLY_AGGRO_CHANCE) { + heroic_cha_mod = THREATENINGLY_AGGRO_CHANCE; + } - ) - && - ( + if ( + RuleB(Aggro, UseLevelAggro) && ( - fv == FACTION_SCOWLS - || - (mob->GetPrimaryFaction() != GetPrimaryFaction() && mob->GetPrimaryFaction() == -4 && GetOwner() == nullptr) - || + GetLevel() >= RuleI(Aggro, MinAggroLevel) || + GetBodyType() == BT_Undead || + AlwaysAggro() || ( - fv == FACTION_THREATENLY - && zone->random.Roll(THREATENLY_ARRGO_CHANCE - heroicCHA_mod) + mob->IsClient() && + mob->CastToClient()->IsSitting() + ) || + mob->GetLevelCon(GetLevel()) != CON_GRAY + ) && + ( + faction_value == FACTION_SCOWLS || + ( + mob->GetPrimaryFaction() != GetPrimaryFaction() && + mob->GetPrimaryFaction() == -4 && + !GetOwner() + ) || + ( + faction_value == FACTION_THREATENINGLY && + zone->random.Roll(THREATENINGLY_AGGRO_CHANCE - heroic_cha_mod) ) ) - ) - ) - ) - { - //FatherNiwtit: make sure we can see them. last since it is very expensive + ) { if(CheckLosFN(mob)) { LogAggro("Check aggro for [{}] target [{}]", GetName(), mob->GetName()); - return( mod_will_aggro(mob, this) ); + return mod_will_aggro(mob, this); } - } - else - { - if - ( - //old InZone check taken care of above by !mob->CastToClient()->Connected() - ( - ( GetINT() <= RuleI(Aggro, IntAggroThreshold) ) - || AlwaysAggro() - ||( mob->IsClient() && mob->CastToClient()->IsSitting() ) - ||( mob->GetLevelCon(GetLevel()) != CON_GRAY) - - ) - && - ( + } else { + if ( ( - fv == FACTION_SCOWLS - || - (mob->GetPrimaryFaction() != GetPrimaryFaction() && mob->GetPrimaryFaction() == -4 && GetOwner() == nullptr) - || + GetINT() <= RuleI(Aggro, IntAggroThreshold) || + AlwaysAggro() || ( - fv == FACTION_THREATENLY - && zone->random.Roll(THREATENLY_ARRGO_CHANCE - heroicCHA_mod) + mob->IsClient() && + mob->CastToClient()->IsSitting() + ) || + mob->GetLevelCon(GetLevel()) != CON_GRAY + ) && + ( + faction_value == FACTION_SCOWLS || + ( + mob->GetPrimaryFaction() != GetPrimaryFaction() && + mob->GetPrimaryFaction() == -4 && + !GetOwner() + ) || + ( + faction_value == FACTION_THREATENINGLY + && zone->random.Roll(THREATENINGLY_AGGRO_CHANCE - heroic_cha_mod) ) ) - ) - ) - { - //FatherNiwtit: make sure we can see them. last since it is very expensive + ) { if(CheckLosFN(mob)) { LogAggro("Check aggro for [{}] target [{}]", GetName(), mob->GetName()); - return( mod_will_aggro(mob, this) ); + return mod_will_aggro(mob, this); } } } LogAggro("Is In zone?:[{}]\n", mob->InZone()); - LogAggro("Dist^2: [{}]\n", dist2); - LogAggro("Range^2: [{}]\n", iAggroRange2); - LogAggro("Faction: [{}]\n", fv); + LogAggro("Dist^2: [{}]\n", distance_squared); + LogAggro("Range^2: [{}]\n", aggro_range_squared); + LogAggro("Faction: [{}]\n", faction_value); LogAggro("AlwaysAggroFlag: [{}]\n", AlwaysAggro()); LogAggro("Int: [{}]\n", GetINT()); LogAggro("Con: [{}]\n", GetLevelCon(mob->GetLevel())); - return(false); + return false; } int EntityList::GetHatedCount(Mob *attacker, Mob *exclude, bool inc_gray_con) diff --git a/zone/client.cpp b/zone/client.cpp index 274a99bb4..61e6a3dc6 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -3471,7 +3471,7 @@ float Client::CalcPriceMod(Mob* other, bool reverse) if (other) { int factionlvl = GetFactionLevel(CharacterID(), other->CastToNPC()->GetNPCTypeID(), GetFactionRace(), GetClass(), GetDeity(), other->CastToNPC()->GetPrimaryFaction(), other); - if (factionlvl >= FACTION_APPREHENSIVE) // Apprehensive or worse. + if (factionlvl >= FACTION_APPREHENSIVELY) // Apprehensive or worse. { if (GetCHA() > 103) { @@ -3486,7 +3486,7 @@ float Client::CalcPriceMod(Mob* other, bool reverse) chaformula = 1*(RuleI(Merchant, PricePenaltyPct)); } } - if (factionlvl <= FACTION_INDIFFERENT) // Indifferent or better. + if (factionlvl <= FACTION_INDIFFERENTLY) // Indifferent or better. { if (GetCHA() > 75) { @@ -7860,7 +7860,7 @@ FACTION_VALUE Client::GetReverseFactionCon(Mob* iOther) { return GetSpecialFactionCon(iOther); if (iOther->GetPrimaryFaction() == 0) - return FACTION_INDIFFERENT; + return FACTION_INDIFFERENTLY; return GetFactionLevel(CharacterID(), 0, GetFactionRace(), GetClass(), GetDeity(), iOther->GetPrimaryFaction(), iOther); } @@ -7883,25 +7883,25 @@ FACTION_VALUE Client::GetFactionLevel(uint32 char_id, uint32 npc_id, uint32 p_ra { if (pFaction < 0) return GetSpecialFactionCon(tnpc); - FACTION_VALUE fac = FACTION_INDIFFERENT; + FACTION_VALUE fac = FACTION_INDIFFERENTLY; int32 tmpFactionValue; FactionMods fmods; // few optimizations if (GetFeigned()) - return FACTION_INDIFFERENT; + return FACTION_INDIFFERENTLY; if(!zone->CanDoCombat()) - return FACTION_INDIFFERENT; + return FACTION_INDIFFERENTLY; if (invisible_undead && tnpc && !tnpc->SeeInvisibleUndead()) - return FACTION_INDIFFERENT; + return FACTION_INDIFFERENTLY; if (IsInvisible(tnpc)) - return FACTION_INDIFFERENT; + return FACTION_INDIFFERENTLY; if (tnpc && tnpc->GetOwnerID() != 0) // pets con amiably to owner and indiff to rest { if (char_id == tnpc->GetOwner()->CastToClient()->CharacterID()) - return FACTION_AMIABLE; + return FACTION_AMIABLY; else - return FACTION_INDIFFERENT; + return FACTION_INDIFFERENTLY; } //First get the NPC's Primary faction @@ -7921,15 +7921,15 @@ FACTION_VALUE Client::GetFactionLevel(uint32 char_id, uint32 npc_id, uint32 p_ra } else { - return(FACTION_INDIFFERENT); + return(FACTION_INDIFFERENTLY); } // merchant fix - if (tnpc && tnpc->IsNPC() && tnpc->CastToNPC()->MerchantType && (fac == FACTION_THREATENLY || fac == FACTION_SCOWLS)) - fac = FACTION_DUBIOUS; + if (tnpc && tnpc->IsNPC() && tnpc->CastToNPC()->MerchantType && (fac == FACTION_THREATENINGLY || fac == FACTION_SCOWLS)) + fac = FACTION_DUBIOUSLY; if (tnpc != 0 && fac != FACTION_SCOWLS && tnpc->CastToNPC()->CheckAggro(this)) - fac = FACTION_THREATENLY; + fac = FACTION_THREATENINGLY; return fac; } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index ed767a041..227193a7c 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -4875,7 +4875,7 @@ void Client::Handle_OP_Consider(const EQApplicationPacket *app) if (tmob->IsNPC()) { if (GetFeigned()) - con->faction = FACTION_INDIFFERENT; + con->faction = FACTION_INDIFFERENTLY; } if (!(con->faction == FACTION_SCOWLS)) @@ -4883,21 +4883,21 @@ void Client::Handle_OP_Consider(const EQApplicationPacket *app) if (tmob->IsNPC()) { if (tmob->CastToNPC()->IsOnHatelist(this)) - con->faction = FACTION_THREATENLY; + con->faction = FACTION_THREATENINGLY; } } - if (con->faction == FACTION_APPREHENSIVE) { + if (con->faction == FACTION_APPREHENSIVELY) { con->faction = FACTION_SCOWLS; } - else if (con->faction == FACTION_DUBIOUS) { - con->faction = FACTION_THREATENLY; + else if (con->faction == FACTION_DUBIOUSLY) { + con->faction = FACTION_THREATENINGLY; } else if (con->faction == FACTION_SCOWLS) { - con->faction = FACTION_APPREHENSIVE; + con->faction = FACTION_APPREHENSIVELY; } - else if (con->faction == FACTION_THREATENLY) { - con->faction = FACTION_DUBIOUS; + else if (con->faction == FACTION_THREATENINGLY) { + con->faction = FACTION_DUBIOUSLY; } mod_consider(tmob, con); diff --git a/zone/command.cpp b/zone/command.cpp index 8c26d228d..60aa37b65 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -116,7 +116,7 @@ int command_init(void) if ( command_add("acceptrules", "[acceptrules] - Accept the EQEmu Agreement", AccountStatus::Player, command_acceptrules) || command_add("advnpcspawn", "[maketype|makegroup|addgroupentry|addgroupspawn][removegroupspawn|movespawn|editgroupbox|cleargroupbox]", AccountStatus::GMLeadAdmin, command_advnpcspawn) || - command_add("aggro", "(range) [-v] - Display aggro information for all mobs 'range' distance from your target. -v is verbose faction info.", AccountStatus::QuestTroupe, command_aggro) || + command_add("aggro", "[Distance] [-v] - Display aggro information for all mobs 'Distance' distance from your target. (-v is verbose Faction Information)", AccountStatus::QuestTroupe, command_aggro) || command_add("aggrozone", "[aggro] - Aggro every mob in the zone with X aggro. Default is 0. Not recommend if you're not invulnerable.", AccountStatus::GMAdmin, command_aggrozone) || command_add("ai", "[factionid/spellslist/con/guard/roambox/stop/start] - Modify AI on NPC target", AccountStatus::GMAdmin, command_ai) || command_add("appearance", "[type] [value] - Send an appearance packet for you or your target", AccountStatus::GMLeadAdmin, command_appearance) || diff --git a/zone/effects.cpp b/zone/effects.cpp index 26e77c14e..3749bc91b 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -1033,14 +1033,14 @@ void EntityList::AESpell( //which have bad faction with us if ( !(caster_mob->CheckAggro(current_mob) || - faction_value == FACTION_THREATENLY || + faction_value == FACTION_THREATENINGLY || faction_value == FACTION_SCOWLS)) { continue; } } else { //only affect mobs we would assist. - if (!(faction_value <= FACTION_AMIABLE)) { + if (!(faction_value <= FACTION_AMIABLY)) { continue; } } @@ -1209,13 +1209,13 @@ void EntityList::AEBardPulse( if (is_detrimental_spell) { //affect mobs that are on our hate list, or //which have bad faction with us - if (!(caster->CheckAggro(current_mob) || faction == FACTION_THREATENLY || faction == FACTION_SCOWLS)) { + if (!(caster->CheckAggro(current_mob) || faction == FACTION_THREATENINGLY || faction == FACTION_SCOWLS)) { continue; } } else { //only affect mobs we would assist. - if (!(faction <= FACTION_AMIABLE)) { + if (!(faction <= FACTION_AMIABLY)) { continue; } } diff --git a/zone/entity.cpp b/zone/entity.cpp index 68d8f5f71..1922ec5f4 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -3671,7 +3671,7 @@ void EntityList::SendAlarm(Trap *trap, Mob *currenttarget, uint8 kos) if (kos) { uint8 factioncon = currenttarget->GetReverseFactionCon(cur); - if (factioncon == FACTION_THREATENLY || factioncon == FACTION_SCOWLS) { + if (factioncon == FACTION_THREATENINGLY || factioncon == FACTION_SCOWLS) { cur->AddToHateList(currenttarget,1); } } diff --git a/zone/gm_commands/aggro.cpp b/zone/gm_commands/aggro.cpp index 9b8307cb2..90fe9e335 100755 --- a/zone/gm_commands/aggro.cpp +++ b/zone/gm_commands/aggro.cpp @@ -2,20 +2,27 @@ void command_aggro(Client *c, const Seperator *sep) { - if (c->GetTarget() == nullptr || !c->GetTarget()->IsNPC()) { - c->Message(Chat::White, "Error: you must have an NPC target."); + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #aggro [Distance] [-v] (-v is verbose Faction Information)"); return; } - float d = atof(sep->arg[1]); - if (d == 0.0f) { - c->Message(Chat::Red, "Error: distance argument required."); - return; - } - bool verbose = false; - if (sep->arg[2][0] == '-' && sep->arg[2][1] == 'v' && sep->arg[2][2] == '\0') { - verbose = true; - } - entity_list.DescribeAggro(c, c->GetTarget()->CastToNPC(), d, verbose); + if ( + !c->GetTarget() || + ( + c->GetTarget() && + !c->GetTarget()->IsNPC() + ) + ) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; + } + + NPC* target = c->GetTarget()->CastToNPC(); + + float distance = std::stof(sep->arg[1]); + bool verbose = !strcasecmp("-v", sep->arg[2]); + entity_list.DescribeAggro(c, target, distance, verbose); } diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index af3cc0e4b..7e76d1ddf 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -4193,11 +4193,11 @@ luabind::scope lua_register_faction() { luabind::value("Ally", static_cast(FACTION_ALLY)), luabind::value("Warmly", static_cast(FACTION_WARMLY)), luabind::value("Kindly", static_cast(FACTION_KINDLY)), - luabind::value("Amiable", static_cast(FACTION_AMIABLE)), - luabind::value("Indifferent", static_cast(FACTION_INDIFFERENT)), - luabind::value("Apprehensive", static_cast(FACTION_APPREHENSIVE)), - luabind::value("Dubious", static_cast(FACTION_DUBIOUS)), - luabind::value("Threatenly", static_cast(FACTION_THREATENLY)), + luabind::value("Amiable", static_cast(FACTION_AMIABLY)), + luabind::value("Indifferent", static_cast(FACTION_INDIFFERENTLY)), + luabind::value("Apprehensive", static_cast(FACTION_APPREHENSIVELY)), + luabind::value("Dubious", static_cast(FACTION_DUBIOUSLY)), + luabind::value("Threatenly", static_cast(FACTION_THREATENINGLY)), luabind::value("Scowls", static_cast(FACTION_SCOWLS)) ]; } diff --git a/zone/mob.cpp b/zone/mob.cpp index 834ad6646..a3d0cb8a3 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -5715,7 +5715,7 @@ void Mob::ClearItemFactionBonuses() { FACTION_VALUE Mob::GetSpecialFactionCon(Mob* iOther) { if (!iOther) - return FACTION_INDIFFERENT; + return FACTION_INDIFFERENTLY; iOther = iOther->GetOwnerOrSelf(); Mob* self = this->GetOwnerOrSelf(); @@ -5726,9 +5726,9 @@ FACTION_VALUE Mob::GetSpecialFactionCon(Mob* iOther) { int iOtherPrimaryFaction = iOther->GetPrimaryFaction(); if (selfPrimaryFaction >= 0 && selfAIcontrolled) - return FACTION_INDIFFERENT; + return FACTION_INDIFFERENTLY; if (iOther->GetPrimaryFaction() >= 0) - return FACTION_INDIFFERENT; + return FACTION_INDIFFERENTLY; /* special values: -2 = indiff to player, ally to AI on special values, indiff to AI -3 = dub to player, ally to AI on special values, indiff to AI @@ -5748,27 +5748,27 @@ FACTION_VALUE Mob::GetSpecialFactionCon(Mob* iOther) { if (selfAIcontrolled && iOtherAIControlled) return FACTION_ALLY; else - return FACTION_INDIFFERENT; + return FACTION_INDIFFERENTLY; case -3: // -3 = dub to player, ally to AI on special values, indiff to AI if (selfAIcontrolled && iOtherAIControlled) return FACTION_ALLY; else - return FACTION_DUBIOUS; + return FACTION_DUBIOUSLY; case -4: // -4 = atk to player, ally to AI on special values, indiff to AI if (selfAIcontrolled && iOtherAIControlled) return FACTION_ALLY; else return FACTION_SCOWLS; case -5: // -5 = indiff to player, indiff to AI - return FACTION_INDIFFERENT; + return FACTION_INDIFFERENTLY; case -6: // -6 = dub to player, indiff to AI if (selfAIcontrolled && iOtherAIControlled) - return FACTION_INDIFFERENT; + return FACTION_INDIFFERENTLY; else - return FACTION_DUBIOUS; + return FACTION_DUBIOUSLY; case -7: // -7 = atk to player, indiff to AI if (selfAIcontrolled && iOtherAIControlled) - return FACTION_INDIFFERENT; + return FACTION_INDIFFERENTLY; else return FACTION_SCOWLS; case -8: // -8 = indiff to players, ally to AI on same value, indiff to AI @@ -5776,25 +5776,25 @@ FACTION_VALUE Mob::GetSpecialFactionCon(Mob* iOther) { if (selfPrimaryFaction == iOtherPrimaryFaction) return FACTION_ALLY; else - return FACTION_INDIFFERENT; + return FACTION_INDIFFERENTLY; } else - return FACTION_INDIFFERENT; + return FACTION_INDIFFERENTLY; case -9: // -9 = dub to players, ally to AI on same value, indiff to AI if (selfAIcontrolled && iOtherAIControlled) { if (selfPrimaryFaction == iOtherPrimaryFaction) return FACTION_ALLY; else - return FACTION_INDIFFERENT; + return FACTION_INDIFFERENTLY; } else - return FACTION_DUBIOUS; + return FACTION_DUBIOUSLY; case -10: // -10 = atk to players, ally to AI on same value, indiff to AI if (selfAIcontrolled && iOtherAIControlled) { if (selfPrimaryFaction == iOtherPrimaryFaction) return FACTION_ALLY; else - return FACTION_INDIFFERENT; + return FACTION_INDIFFERENTLY; } else return FACTION_SCOWLS; @@ -5806,7 +5806,7 @@ FACTION_VALUE Mob::GetSpecialFactionCon(Mob* iOther) { return FACTION_SCOWLS; } else - return FACTION_INDIFFERENT; + return FACTION_INDIFFERENTLY; case -12: // -12 = dub to players, ally to AI on same value, atk to AI if (selfAIcontrolled && iOtherAIControlled) { if (selfPrimaryFaction == iOtherPrimaryFaction) @@ -5817,7 +5817,7 @@ FACTION_VALUE Mob::GetSpecialFactionCon(Mob* iOther) { } else - return FACTION_DUBIOUS; + return FACTION_DUBIOUSLY; case -13: // -13 = atk to players, ally to AI on same value, atk to AI if (selfAIcontrolled && iOtherAIControlled) { if (selfPrimaryFaction == iOtherPrimaryFaction) @@ -5828,7 +5828,7 @@ FACTION_VALUE Mob::GetSpecialFactionCon(Mob* iOther) { else return FACTION_SCOWLS; default: - return FACTION_INDIFFERENT; + return FACTION_INDIFFERENTLY; } } diff --git a/zone/mob.h b/zone/mob.h index 327f73f9c..1500f41af 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1176,7 +1176,7 @@ public: inline float GetCWPP() const { return(static_cast(cur_wp_pause)); } inline int GetCWP() const { return(cur_wp); } void SetCurrentWP(int waypoint) { cur_wp = waypoint; } - virtual FACTION_VALUE GetReverseFactionCon(Mob* iOther) { return FACTION_INDIFFERENT; } + virtual FACTION_VALUE GetReverseFactionCon(Mob* iOther) { return FACTION_INDIFFERENTLY; } virtual const bool IsUnderwaterOnly() const { return false; } inline bool IsTrackable() const { return(trackable); } diff --git a/zone/npc.cpp b/zone/npc.cpp index 85d1d6755..c11e6beb0 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -2907,7 +2907,7 @@ FACTION_VALUE NPC::GetReverseFactionCon(Mob* iOther) { return GetSpecialFactionCon(iOther); if (primaryFaction == 0) - return FACTION_INDIFFERENT; + return FACTION_INDIFFERENTLY; //if we are a pet, use our owner's faction stuff Mob *own = GetOwner(); @@ -2917,7 +2917,7 @@ FACTION_VALUE NPC::GetReverseFactionCon(Mob* iOther) { //make sure iOther is an npc //also, if we dont have a faction, then they arnt gunna think anything of us either if(!iOther->IsNPC() || GetPrimaryFaction() == 0) - return(FACTION_INDIFFERENT); + return(FACTION_INDIFFERENTLY); //if we get here, iOther is an NPC too @@ -2941,7 +2941,7 @@ FACTION_VALUE NPC::CheckNPCFactionAlly(int32 other_faction) { else if (fac->npc_value < 0) return FACTION_SCOWLS; else - return FACTION_INDIFFERENT; + return FACTION_INDIFFERENTLY; } } @@ -2953,7 +2953,7 @@ FACTION_VALUE NPC::CheckNPCFactionAlly(int32 other_faction) { if (GetPrimaryFaction() == other_faction) return FACTION_ALLY; else - return FACTION_INDIFFERENT; + return FACTION_INDIFFERENTLY; } bool NPC::IsFactionListAlly(uint32 other_faction) { @@ -3421,7 +3421,7 @@ void NPC::AIYellForHelp(Mob *sender, Mob *attacker) } } - if (sender->GetReverseFactionCon(mob) <= FACTION_AMIABLE) { + if (sender->GetReverseFactionCon(mob) <= FACTION_AMIABLY) { //attacking someone on same faction, or a friend //Father Nitwit: make sure we can see them. if (mob->CheckLosFN(sender)) { diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 527706567..a825cf505 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -3105,19 +3105,19 @@ uint8 QuestManager::FactionValue() case FACTION_SCOWLS: newfac = 1; break; - case FACTION_THREATENLY: + case FACTION_THREATENINGLY: newfac = 2; break; - case FACTION_DUBIOUS: + case FACTION_DUBIOUSLY: newfac = 3; break; - case FACTION_APPREHENSIVE: + case FACTION_APPREHENSIVELY: newfac = 4; break; - case FACTION_INDIFFERENT: + case FACTION_INDIFFERENTLY: newfac = 5; break; - case FACTION_AMIABLE: + case FACTION_AMIABLY: newfac = 6; break; case FACTION_KINDLY: diff --git a/zone/spells.cpp b/zone/spells.cpp index 9783bfc40..e01df8a33 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -6181,13 +6181,13 @@ void Mob::BeamDirectional(uint16 spell_id, int16 resist_adjust) auto fac = (*iter)->GetReverseFactionCon(this); if (beneficial_targets) { // only affect mobs we would assist. - if (!(fac <= FACTION_AMIABLE)) { + if (!(fac <= FACTION_AMIABLY)) { ++iter; continue; } } else { // affect mobs that are on our hate list, or which have bad faction with us - if (!(CheckAggro(*iter) || fac == FACTION_THREATENLY || fac == FACTION_SCOWLS)) { + if (!(CheckAggro(*iter) || fac == FACTION_THREATENINGLY || fac == FACTION_SCOWLS)) { ++iter; continue; } @@ -6256,13 +6256,13 @@ void Mob::ConeDirectional(uint16 spell_id, int16 resist_adjust) auto fac = (*iter)->GetReverseFactionCon(this); if (beneficial_targets) { // only affect mobs we would assist. - if (!(fac <= FACTION_AMIABLE)) { + if (!(fac <= FACTION_AMIABLY)) { ++iter; continue; } } else { // affect mobs that are on our hate list, or which have bad faction with us - if (!(CheckAggro(*iter) || fac == FACTION_THREATENLY || fac == FACTION_SCOWLS)) { + if (!(CheckAggro(*iter) || fac == FACTION_THREATENINGLY || fac == FACTION_SCOWLS)) { ++iter; continue; } From 6a4263938610e336753175ac3c604d5f0659075c Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 21 Nov 2021 10:12:23 -0500 Subject: [PATCH 435/624] [Commands] Cleanup #pvp Command. (#1800) - Cleanup messages and logic. --- zone/client.cpp | 9 +++++---- zone/command.cpp | 2 +- zone/gm_commands/pvp.cpp | 28 ++++++++++++++++++---------- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 61e6a3dc6..3fcd5f443 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -2613,10 +2613,11 @@ void Client::SetPVP(bool toggle, bool message) { m_pp.pvp = toggle ? 1 : 0; if (message) { - if(GetPVP()) - this->MessageString(Chat::Shout,PVP_ON); - else - Message(Chat::Red, "You no longer follow the ways of discord."); + if(GetPVP()) { + MessageString(Chat::Shout, PVP_ON); + } else { + Message(Chat::Shout, "You now follow the ways of Order."); + } } SendAppearancePacket(AT_PVP, GetPVP()); diff --git a/zone/command.cpp b/zone/command.cpp index 60aa37b65..f91094ad1 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -283,7 +283,7 @@ int command_init(void) command_add("profanity", "Manage censored language.", AccountStatus::GMLeadAdmin, command_profanity) || command_add("push", "Lets you do spell push", AccountStatus::GMLeadAdmin, command_push) || command_add("proximity", "Shows NPC proximity", AccountStatus::GMLeadAdmin, command_proximity) || - command_add("pvp", "[on/off] - Set your or your player target's PVP status", AccountStatus::GMAdmin, command_pvp) || + command_add("pvp", "[On|Off] - Set you or your player target's PVP status", AccountStatus::GMAdmin, command_pvp) || command_add("qglobal", "[on/off/view] - Toggles qglobal functionality on an NPC", AccountStatus::GMAdmin, command_qglobal) || command_add("questerrors", "Shows quest errors.", AccountStatus::GMAdmin, command_questerrors) || command_add("race", "[racenum] - Change your or your target's race. Use racenum 0 to return to normal", AccountStatus::Guide, command_race) || diff --git a/zone/gm_commands/pvp.cpp b/zone/gm_commands/pvp.cpp index 1f31f29b9..5ba3d092a 100755 --- a/zone/gm_commands/pvp.cpp +++ b/zone/gm_commands/pvp.cpp @@ -2,19 +2,27 @@ void command_pvp(Client *c, const Seperator *sep) { - bool state = atobool(sep->arg[1]); - Client *t = c; + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #pvp [On|Off]"); + return; + } + bool pvp_state = atobool(sep->arg[1]); + Client* target = c; if (c->GetTarget() && c->GetTarget()->IsClient()) { - t = c->GetTarget()->CastToClient(); + target = c->GetTarget()->CastToClient(); } - if (sep->arg[1][0] != 0) { - t->SetPVP(state); - c->Message(Chat::White, "%s now follows the ways of %s.", t->GetName(), state ? "discord" : "order"); - } - else { - c->Message(Chat::White, "Usage: #pvp [on/off]"); + target->SetPVP(pvp_state); + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "{} now follows the ways of {}.", + target->GetCleanName(), + pvp_state ? "Discord" : "Order" + ).c_str() + ); } } - From 8a27fce3a8d808d746946ce3e817e41b153480d3 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 21 Nov 2021 10:12:37 -0500 Subject: [PATCH 436/624] [Commands] Cleanup #memspell Command. (#1801) - Cleanup message and logic. - Add support for memorizing spell to target if you have GM enabled. --- zone/command.cpp | 2 +- zone/gm_commands/memspell.cpp | 56 +++++++++++++++++++++++++---------- 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index f91094ad1..24e0ab814 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -240,7 +240,7 @@ int command_init(void) command_add("makepet", "[level] [class] [race] [texture] - Make a pet", AccountStatus::Guide, command_makepet) || command_add("mana", "- Fill your or your target's mana", AccountStatus::Guide, command_mana) || command_add("maxskills", "Maxes skills for you.", AccountStatus::GMMgmt, command_max_all_skills) || - command_add("memspell", "[slotid] [spellid] - Memorize spellid in the specified slot", AccountStatus::Guide, command_memspell) || + command_add("memspell", "[Slot] [Spell ID] - Memorize a Spell by ID in the specified Slot", AccountStatus::Guide, command_memspell) || command_add("merchant_close_shop", "Closes a merchant shop", AccountStatus::GMAdmin, command_merchantcloseshop) || command_add("merchant_open_shop", "Opens a merchants shop", AccountStatus::GMAdmin, command_merchantopenshop) || command_add("modifynpcstat", "- Modifys a NPC's stats", AccountStatus::GMLeadAdmin, command_modifynpcstat) || diff --git a/zone/gm_commands/memspell.cpp b/zone/gm_commands/memspell.cpp index 5511dae39..6fce2900f 100755 --- a/zone/gm_commands/memspell.cpp +++ b/zone/gm_commands/memspell.cpp @@ -2,21 +2,47 @@ void command_memspell(Client *c, const Seperator *sep) { - uint32 slot; - uint16 spell_id; - - if (!(sep->IsNumber(1) && sep->IsNumber(2))) { - c->Message(Chat::White, "Usage: #MemSpell slotid spellid"); + int arguments = sep->argnum; + if ( + !arguments || + !sep->IsNumber(1) || + !sep->IsNumber(2) + ) { + c->Message(Chat::White, "Usage: #memspell [Slot] [Spell ID]"); + return; } - else { - slot = atoi(sep->arg[1]) - 1; - spell_id = atoi(sep->arg[2]); - if (slot > EQ::spells::SPELL_GEM_COUNT || spell_id >= SPDAT_RECORDS) { - c->Message(Chat::White, "Error: #MemSpell: Arguement out of range"); - } - else { - c->MemSpell(spell_id, slot); - c->Message(Chat::White, "Spell slot changed, have fun!"); - } + + Client* target = c; + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + target = c->GetTarget()->CastToClient(); + } + + uint32 spell_gem = std::stoul(sep->arg[1]); + uint32 slot = (spell_gem - 1); + uint16 spell_id = static_cast(std::stoul(sep->arg[2])); + if ( + IsValidSpell(spell_id) && + slot < EQ::spells::SPELL_GEM_COUNT + ) { + target->MemSpell(spell_id, slot); + c->Message( + Chat::White, + fmt::format( + "{} ({}) has been memorized to Spell Gem {} ({}) for {}.", + GetSpellName(spell_id), + spell_id, + spell_gem, + slot, + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ) + ).c_str() + ); } } From 03847fb1ac545c6eca7d872fc776ee5f32930f6d Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 21 Nov 2021 10:12:47 -0500 Subject: [PATCH 437/624] [Commands] Cleanup #lastname Command. (#1802) - Cleanup message and logic. --- zone/command.cpp | 2 +- zone/gm_commands/lastname.cpp | 36 ++++++++++++++++++++++++++--------- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 24e0ab814..b28317c08 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -229,7 +229,7 @@ int command_init(void) command_add("kick", "[charname] - Disconnect charname", AccountStatus::GMLeadAdmin, command_kick) || command_add("kill", "- Kill your target", AccountStatus::GMAdmin, command_kill) || command_add("killallnpcs", " [npc_name] Kills all npcs by search name, leave blank for all attackable NPC's", AccountStatus::GMMgmt, command_killallnpcs) || - command_add("lastname", "[new lastname] - Set your or your player target's lastname", AccountStatus::Guide, command_lastname) || + command_add("lastname", "[Last Name] - Set you or your player target's lastname", AccountStatus::Guide, command_lastname) || command_add("level", "[level] - Set your or your target's level", AccountStatus::Steward, command_level) || command_add("list", "[npcs|players|corpses|doors|objects] [search] - Search entities", AccountStatus::ApprenticeGuide, command_list) || command_add("listpetition", "- List petitions", AccountStatus::Guide, command_listpetition) || diff --git a/zone/gm_commands/lastname.cpp b/zone/gm_commands/lastname.cpp index 09bed4771..a990785d9 100755 --- a/zone/gm_commands/lastname.cpp +++ b/zone/gm_commands/lastname.cpp @@ -2,18 +2,36 @@ void command_lastname(Client *c, const Seperator *sep) { - Client *t = c; - + Client *target = c; if (c->GetTarget() && c->GetTarget()->IsClient()) { - t = c->GetTarget()->CastToClient(); + target = c->GetTarget()->CastToClient(); } - LogInfo("#lastname request from [{}] for [{}]", c->GetName(), t->GetName()); - if (strlen(sep->arg[1]) <= 70) { - t->ChangeLastName(sep->arg[1]); - } - else { - c->Message(Chat::White, "Usage: #lastname where is less than 70 chars long"); + LogInfo("#lastname request from [{}] for [{}]", c->GetCleanName(), target->GetCleanName()); + + std::string last_name = sep->arg[1]; + if (last_name.size() > 64) { + c->Message(Chat::White, "Usage: #lastname [Last Name] (Last Name must be 64 characters or less)"); + return; } + + target->ChangeLastName(last_name.c_str()); + c->Message( + Chat::White, + fmt::format( + "{} now {} a last name of '{}'.", + ( + c == target ? + "You" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), + c == target ? "have" : "has", + last_name + ).c_str() + ); } From 0a34809bb3cb36312746428d1e3c1db7b039be76 Mon Sep 17 00:00:00 2001 From: splose Date: Sun, 21 Nov 2021 10:15:36 -0500 Subject: [PATCH 438/624] [Bug Fix] - Monk AA Spirit of Master Wu (#1775) * Monk AA Spirit of Master Wu sends message even if it grants 0 extra attacks. * fomatting + put if statement in wrong place.. oops. * Need to push something to set up my Tortoise Github Auth * test * test * test * test * test * requested changes * Update special_attacks.cpp Co-authored-by: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> --- zone/special_attacks.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index d29206db0..6f99acc9e 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -403,10 +403,16 @@ void Client::OPCombatAbility(const CombatAbility_Struct *ca_atk) } wuchance /= 4; } - // They didn't add a string ID for this. - std::string msg = StringFormat("The spirit of Master Wu fills you! You gain %d additional attack(s).", extra); - // live uses 400 here -- not sure if it's the best for all clients though - SendColoredText(400, msg); + if (extra) { + SendColoredText( + 400, + fmt::format( + "The spirit of Master Wu fills you! You gain {} additional attack{}.", + extra, + extra != 1 ? "s" : "" + ) + ); + } auto classic = RuleB(Combat, ClassicMasterWu); while (extra) { MonkSpecialAttack(GetTarget(), (classic ? MonkSPA[zone->random.Int(0, 4)] : ca_atk->m_skill)); From a84536cd058ca6884ba84e11b03326c8d5c220fe Mon Sep 17 00:00:00 2001 From: splose Date: Sun, 21 Nov 2021 10:16:20 -0500 Subject: [PATCH 439/624] [Bug Fix] Autofire attacking yourself (#1776) * Fix being able to attack yourself with autofire if Combat:MinRangedAttackDist == 0 * requested changes * 2 --- zone/client_packet.cpp | 7 +++++++ zone/client_process.cpp | 4 ++++ zone/special_attacks.cpp | 2 ++ 3 files changed, 13 insertions(+) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 227193a7c..c2b44842e 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -3386,6 +3386,13 @@ void Client::Handle_OP_AutoFire(const EQApplicationPacket *app) DumpPacket(app); return; } + + if (GetTarget() == this) { + MessageString(Chat::TooFarAway, TRY_ATTACKING_SOMEONE); + auto_fire = false; + return; + } + bool *af = (bool*)app->pBuffer; auto_fire = *af; if(!RuleB(Character, EnableRangerAutoFire)) diff --git a/zone/client_process.cpp b/zone/client_process.cpp index c014c5f0e..0134ce3f4 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -302,6 +302,10 @@ bool Client::Process() { } if (AutoFireEnabled()) { + if (GetTarget() == this) { + MessageString(Chat::TooFarAway, TRY_ATTACKING_SOMEONE); + auto_fire = false; + } EQ::ItemInstance *ranged = GetInv().GetItem(EQ::invslot::slotRange); if (ranged) { diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 6f99acc9e..f6003a8d2 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -645,6 +645,8 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) { //conditions to use an attack checked before we are called if (!other) return; + else if (other == this) + return; //make sure the attack and ranged timers are up //if the ranged timer is disabled, then they have no ranged weapon and shouldent be attacking anyhow if(!CanDoubleAttack && ((attack_timer.Enabled() && !attack_timer.Check(false)) || (ranged_timer.Enabled() && !ranged_timer.Check()))) { From e9fc80815a8f9ef27aef8b6cc6991d5087b4cfa1 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 21 Nov 2021 10:16:55 -0500 Subject: [PATCH 440/624] [Spells] Support for SPA 161 and 450 to give percent spell or dot mitigation from Items or AA's. (#1793) * spell dot shield item AA support * Update spdat.h * Update attack.cpp --- common/spdat.h | 4 ++-- zone/attack.cpp | 8 +++++--- zone/bonuses.cpp | 20 ++++++++++++++++++++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 4efe92f94..372cd97db 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -863,7 +863,7 @@ typedef enum { #define SE_Reflect 158 // implemented, @SpellMisc, reflect casted detrimental spell back at caster, base: chance pct, limit: resist modifier (positive value reduces resists), max: pct of base dmg mod (50=50pct of base) #define SE_AllStats 159 // implemented //#define SE_MakeDrunk 160 // *not implemented - Effect works entirely client side (Should check against tolerance) -#define SE_MitigateSpellDamage 161 // implemented - rune with max value +#define SE_MitigateSpellDamage 161 // implemented, @Runes, mitigate incoming spell damage by percentage until rune fades, base: percent mitigation, limit: max dmg absorbed per hit, max: rune amt, Note: If placed on item or AA, will provide stackable percent mitigation. #define SE_MitigateMeleeDamage 162 // implemented - rune with max value #define SE_NegateAttacks 163 // implemented #define SE_AppraiseLDonChest 164 // implemented @@ -1152,7 +1152,7 @@ typedef enum { #define SE_BStacker 447 // implemented #define SE_CStacker 448 // implemented #define SE_DStacker 449 // implemented -#define SE_MitigateDotDamage 450 // implemented DOT spell mitigation rune with max value +#define SE_MitigateDotDamage 450 // implemented, @Runes, mitigate incoming dot damage by percentage until rune fades, base: percent mitigation, limit: max dmg absorbed per hit, max: rune amt, Note: If placed on item or AA, will provide stackable percent mitigation. #define SE_MeleeThresholdGuard 451 // implemented Partial Melee Rune that only is lowered if melee hits are over X amount of damage #define SE_SpellThresholdGuard 452 // implemented Partial Spell Rune that only is lowered if spell hits are over X amount of damage #define SE_TriggerMeleeThreshold 453 // implemented Trigger effect on X amount of melee damage taken in a single hit diff --git a/zone/attack.cpp b/zone/attack.cpp index 071ae327e..07aa4b350 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3298,7 +3298,8 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi // If this is a DoT, use DoT Shielding... if (iBuffTic) { - damage -= (damage * itembonuses.DoTShielding / 100); + int total_dotshielding = itembonuses.DoTShielding + itembonuses.MitigateDotRune[SBIndex::MITIGATION_RUNE_PERCENT] + aabonuses.MitigateDotRune[SBIndex::MITIGATION_RUNE_PERCENT]; + damage -= (damage * total_dotshielding / 100); if (spellbonuses.MitigateDotRune[SBIndex::MITIGATION_RUNE_PERCENT]) { slot = spellbonuses.MitigateDotRune[SBIndex::MITIGATION_RUNE_BUFFSLOT]; @@ -3330,8 +3331,9 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi else { // Reduce damage by the Spell Shielding first so that the runes don't take the raw damage. - damage -= (damage * itembonuses.SpellShield / 100); - + int total_spellshielding = itembonuses.SpellShield + itembonuses.MitigateSpellRune[SBIndex::MITIGATION_RUNE_PERCENT] + aabonuses.MitigateSpellRune[SBIndex::MITIGATION_RUNE_PERCENT]; + damage -= (damage * total_spellshielding / 100); + //Only mitigate if damage is above the minimium specified. if (spellbonuses.SpellThresholdGuard[SBIndex::THRESHOLDGUARD_MITIGATION_PERCENT]) { slot = spellbonuses.SpellThresholdGuard[SBIndex::THRESHOLDGUARD_BUFFSLOT]; diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index e15175cad..b71b7a568 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1761,6 +1761,18 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) newbon->Amplification += base_value; break; + case SE_MitigateSpellDamage: + { + newbon->MitigateSpellRune[SBIndex::MITIGATION_RUNE_PERCENT] += base_value; + break; + } + + case SE_MitigateDotDamage: + { + newbon->MitigateDotRune[SBIndex::MITIGATION_RUNE_PERCENT] += base_value; + break; + } + // to do case SE_PetDiscipline: break; @@ -3019,6 +3031,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_MitigateSpellDamage: { + if (WornType) { + new_bonus->MitigateSpellRune[SBIndex::MITIGATION_RUNE_PERCENT] += effect_value; + } + if (new_bonus->MitigateSpellRune[SBIndex::MITIGATION_RUNE_PERCENT] < effect_value){ new_bonus->MitigateSpellRune[SBIndex::MITIGATION_RUNE_PERCENT] = effect_value; new_bonus->MitigateSpellRune[SBIndex::MITIGATION_RUNE_BUFFSLOT] = buffslot; @@ -3030,6 +3046,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_MitigateDotDamage: { + if (WornType) { + new_bonus->MitigateDotRune[SBIndex::MITIGATION_RUNE_PERCENT] += effect_value; + } + if (new_bonus->MitigateDotRune[SBIndex::MITIGATION_RUNE_PERCENT] < effect_value){ new_bonus->MitigateDotRune[SBIndex::MITIGATION_RUNE_PERCENT] = effect_value; new_bonus->MitigateDotRune[SBIndex::MITIGATION_RUNE_BUFFSLOT] = buffslot; From 1acdc6034b169534b0d6a04ebc9fc08dcd688382 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 21 Nov 2021 14:01:15 -0500 Subject: [PATCH 441/624] [Commands] Cleanup #permagender Command. (#1779) - Cleanup message and logic. --- zone/command.cpp | 2 +- zone/gm_commands/permagender.cpp | 50 +++++++++++++++++++------------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index b28317c08..7da3253d3 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -275,7 +275,7 @@ int command_init(void) command_add("peekinv", "[equip/gen/cursor/poss/limbo/curlim/trib/bank/shbank/allbank/trade/world/all] - Print out contents of your player target's inventory", AccountStatus::GMAdmin, command_peekinv) || command_add("peqzone", "[zonename] - Go to specified zone, if you have > 75% health", AccountStatus::Player, command_peqzone) || command_add("permaclass", "[Class ID] - Change your or your player target's class, changed client is disconnected", AccountStatus::QuestTroupe, command_permaclass) || - command_add("permagender", "[gendernum] - Change your or your player target's gender (zone to take effect)", AccountStatus::QuestTroupe, command_permagender) || + command_add("permagender", "[Gender ID] - Change your or your player target's gender", AccountStatus::QuestTroupe, command_permagender) || command_add("permarace", "[Race ID] - Change your or your player target's race", AccountStatus::QuestTroupe, command_permarace) || command_add("petitioninfo", "[petition number] - Get info about a petition", AccountStatus::ApprenticeGuide, command_petitioninfo) || command_add("pf", "- Display additional mob coordinate and wandering data", AccountStatus::Player, command_pf) || diff --git a/zone/gm_commands/permagender.cpp b/zone/gm_commands/permagender.cpp index ee0c7ecf9..8520d3374 100755 --- a/zone/gm_commands/permagender.cpp +++ b/zone/gm_commands/permagender.cpp @@ -2,28 +2,38 @@ void command_permagender(Client *c, const Seperator *sep) { - Client *t = c; + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #permagender [Gender ID]"); + c->Message(Chat::White, "Genders: 0 = Male, 1 = Female, 2 = Neuter"); + return; + } + Client *target = c; if (c->GetTarget() && c->GetTarget()->IsClient()) { - t = c->GetTarget()->CastToClient(); + target = c->GetTarget()->CastToClient(); } - if (sep->arg[1][0] == 0) { - c->Message(Chat::White, "Usage: #permagender "); - c->Message(Chat::White, "Gender Numbers: 0=Male, 1=Female, 2=Neuter"); - } - else if (!t->IsClient()) { - c->Message(Chat::White, "Target is not a client."); - } - else { - c->Message(Chat::White, "Setting %s's gender - zone to take effect", t->GetName()); - LogInfo("Permanant gender change request from [{}] for [{}], requested gender:[{}]", - c->GetName(), - t->GetName(), - atoi(sep->arg[1])); - t->SetBaseGender(atoi(sep->arg[1])); - t->Save(); - t->SendIllusionPacket(atoi(sep->arg[1])); - } + auto gender_id = std::stoi(sep->arg[1]); + + LogInfo("Gender changed by {} for {} to {} ({})", + c->GetCleanName(), + target->GetCleanName(), + GetGenderName(gender_id), + gender_id + ); + + target->SetBaseGender(gender_id); + target->Save(); + target->SendIllusionPacket(target->GetRace(), gender_id); + + c->Message( + Chat::White, + fmt::format( + "Gender changed for {} to {} ({}).", + c == target ? "yourself" : target->GetCleanName(), + GetGenderName(gender_id), + gender_id + ).c_str() + ); } - From 5470ec629329e6390613c7cc691c97f491041bf5 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 21 Nov 2021 14:02:03 -0500 Subject: [PATCH 442/624] [Commands] Cleanup #corpse Command. (#1790) - Cleanup message and logic. - Add ConvertMoneyToString(platinum, gold, silver, copper) helper method. - Cleanup NPC::QueryLoot() and Corpse::QueryLoot(). --- common/string_util.cpp | 75 ++++++ common/string_util.h | 3 +- zone/corpse.cpp | 102 +++++--- zone/entity.cpp | 102 +++++--- zone/entity.h | 4 +- zone/gm_commands/corpse.cpp | 488 +++++++++++++++++++++++++----------- zone/lua_entity_list.cpp | 8 +- zone/lua_entity_list.h | 4 +- zone/npc.cpp | 24 +- zone/perl_entity.cpp | 8 +- 10 files changed, 566 insertions(+), 252 deletions(-) diff --git a/common/string_util.cpp b/common/string_util.cpp index d89bbe39b..d8f5cab9b 100644 --- a/common/string_util.cpp +++ b/common/string_util.cpp @@ -1180,3 +1180,78 @@ std::string ConvertSecondsToTime(int duration, bool is_milliseconds) } return time_string; } + +std::string ConvertMoneyToString(uint32 platinum, uint32 gold, uint32 silver, uint32 copper) +{ + std::string money_string = "Unknown"; + if (copper && silver && gold && platinum) { // CSGP + money_string = fmt::format( + "{} Platinum, {} Gold, {} Silver, and {} Copper", + platinum, + gold, + silver, + copper + ); + } else if (copper && silver && gold && !platinum) { // CSG + money_string = fmt::format( + "{} Gold, {} Silver, and {} Copper", + gold, + silver, + copper + ); + } else if (copper && silver && !gold && !platinum) { // CS + money_string = fmt::format( + "{} Silver and {} Copper", + silver, + copper + ); + } else if (copper && !silver && !gold && !platinum) { // C + money_string = fmt::format( + "{} Copper", + copper + ); + } else if (!copper && silver && gold && platinum) { // SGP + money_string = fmt::format( + "{} Platinum, {} Gold, and {} Silver", + platinum, + gold, + silver + ); + } else if (!copper && silver && gold && !platinum) { // SG + money_string = fmt::format( + "{} Gold and {} Silver", + gold, + silver + ); + } else if (!copper && silver && !gold && !platinum) { // S + money_string = fmt::format( + "{} Silver", + silver + ); + } else if (copper && !silver && gold && platinum) { // CGP + money_string = fmt::format( + "{} Platinum, {} Gold, and {} Copper", + platinum, + gold, + copper + ); + } else if (copper && !silver && gold && !platinum) { // CG + money_string = fmt::format( + "{} Gold and {} Copper", + gold, + copper + ); + } else if (!copper && !silver && gold && platinum) { // GP + money_string = fmt::format( + "{} Platinum and {} Gold", + platinum, + gold + ); + } else if (!copper && !silver && gold && !platinum) { // G + money_string = fmt::format( + "{} Gold", + gold + ); + } + return money_string; +} \ No newline at end of file diff --git a/common/string_util.h b/common/string_util.h index e9c3bfa15..f6b7e35ce 100644 --- a/common/string_util.h +++ b/common/string_util.h @@ -45,9 +45,10 @@ std::vector wrap(std::vector &src, std::string charact std::string implode(std::string glue, std::vector src); std::string convert2digit(int n, std::string suffix); std::string numberToWords(unsigned long long int n); +std::string ConvertMoneyToString(uint32 platinum, uint32 gold = 0, uint32 silver = 0, uint32 copper = 0); std::string ConvertSecondsToTime(int duration, bool is_milliseconds = false); inline std::string ConvertMillisecondsToTime(int duration) { - return ConvertSecondsToTime(duration, true); + return ConvertSecondsToTime(duration, true); } // For converstion of numerics into English diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 6401abb95..654aa4b00 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -1456,54 +1456,74 @@ void Corpse::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { } void Corpse::QueryLoot(Client* to) { - int x = 0, y = 0; // x = visible items, y = total items - to->Message(Chat::White, "Coin: %ip, %ig, %is, %ic", platinum, gold, silver, copper); + if (itemlist.size() > 0) { + int player_corpse_limit = to->GetInv().GetLookup()->InventoryTypeSize.Corpse; + to->Message( + Chat::White, + fmt::format( + "Loot | Name: {} ID: {}", + GetName(), + GetNPCTypeID() + ).c_str() + ); - ItemList::iterator cur,end; - cur = itemlist.begin(); - end = itemlist.end(); + int item_count = 0; + for (auto current_item : itemlist) { + int item_number = (item_count + 1); + if (!current_item) { + LogError("Corpse::QueryLoot() - ItemList error, null item."); + continue; + } - int corpselootlimit = to->GetInv().GetLookup()->InventoryTypeSize.Corpse; + if (!current_item->item_id || !database.GetItem(current_item->item_id)) { + LogError("Corpse::QueryLoot() - Database error, invalid item."); + continue; + } - for(; cur != end; ++cur) { - ServerLootItem_Struct* sitem = *cur; + EQ::SayLinkEngine linker; + linker.SetLinkType(EQ::saylink::SayLinkLootItem); + linker.SetLootData(current_item); - if (IsPlayerCorpse()) { - if (sitem->equip_slot >= EQ::invbag::GENERAL_BAGS_BEGIN && sitem->equip_slot <= EQ::invbag::CURSOR_BAG_END) - sitem->lootslot = 0xFFFF; - else - x < corpselootlimit ? sitem->lootslot = x : sitem->lootslot = 0xFFFF; - - const EQ::ItemData* item = database.GetItem(sitem->item_id); - - if (item) - to->Message((sitem->lootslot == 0xFFFF), "LootSlot: %i (EquipSlot: %i) Item: %s (%d), Count: %i", static_cast(sitem->lootslot), sitem->equip_slot, item->Name, item->ID, sitem->charges); - else - to->Message((sitem->lootslot == 0xFFFF), "Error: 0x%04x", sitem->item_id); - - if (sitem->lootslot != 0xFFFF) - x++; - - y++; - } - else { - sitem->lootslot=y; - const EQ::ItemData* item = database.GetItem(sitem->item_id); - - if (item) - to->Message(Chat::White, "LootSlot: %i Item: %s (%d), Count: %i", sitem->lootslot, item->Name, item->ID, sitem->charges); - else - to->Message(Chat::White, "Error: 0x%04x", sitem->item_id); - - y++; + to->Message( + Chat::White, + fmt::format( + "Item {} | Name: {} ({}){}", + item_number, + linker.GenerateLink().c_str(), + current_item->item_id, + ( + current_item->charges > 1 ? + fmt::format( + " Amount: {}", + current_item->charges + ) : + "" + ) + ).c_str() + ); + item_count++; } } - if (IsPlayerCorpse()) { - to->Message(Chat::White, "%i visible %s (%i total) on %s (DBID: %i).", x, x==1?"item":"items", y, this->GetName(), this->GetCorpseDBID()); - } - else { - to->Message(Chat::White, "%i %s on %s.", y, y==1?"item":"items", this->GetName()); + bool has_money = ( + platinum > 0 || + gold > 0 || + silver > 0 || + copper > 0 + ); + if (has_money) { + to->Message( + Chat::White, + fmt::format( + "Money | {}", + ConvertMoneyToString( + platinum, + gold, + silver, + copper + ) + ).c_str() + ); } } diff --git a/zone/entity.cpp b/zone/entity.cpp index 1922ec5f4..944418f29 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -3152,50 +3152,75 @@ char *EntityList::RemoveNumbers(char *name) void EntityList::ListNPCCorpses(Client *client) { - uint32 x = 0; - - auto it = corpse_list.begin(); - client->Message(Chat::White, "NPC Corpses in the zone:"); - while (it != corpse_list.end()) { - if (it->second->IsNPCCorpse()) { - client->Message(Chat::White, " %5d: %s", it->first, it->second->GetName()); - x++; + uint32 corpse_count = 0; + for (const auto& corpse : corpse_list) { + uint32 corpse_number = (corpse_count + 1); + if (corpse.second->IsNPCCorpse()) { + client->Message( + Chat::White, + fmt::format( + "Corpse {} | Name: {} ({})", + corpse_number, + corpse.second->GetName(), + corpse.second->GetID() + ).c_str() + ); + corpse_count++; } - ++it; } - client->Message(Chat::White, "%d npc corpses listed.", x); + + if (corpse_count > 0) { + client->Message( + Chat::White, + fmt::format( + "{} NPC corpses listed.", + corpse_count + ).c_str() + ); + } } void EntityList::ListPlayerCorpses(Client *client) { - uint32 x = 0; - - auto it = corpse_list.begin(); - client->Message(Chat::White, "Player Corpses in the zone:"); - while (it != corpse_list.end()) { - if (it->second->IsPlayerCorpse()) { - client->Message(Chat::White, " %5d: %s", it->first, it->second->GetName()); - x++; + uint32 corpse_count = 0; + for (const auto& corpse : corpse_list) { + uint32 corpse_number = (corpse_count + 1); + if (corpse.second->IsPlayerCorpse()) { + client->Message( + Chat::White, + fmt::format( + "Corpse {} | Name: {} ({})", + corpse_number, + corpse.second->GetName(), + corpse.second->GetID() + ).c_str() + ); + corpse_count++; } - ++it; } - client->Message(Chat::White, "%d player corpses listed.", x); + + if (corpse_count > 0) { + client->Message( + Chat::White, + fmt::format( + "{} Player corpses listed.", + corpse_count + ).c_str() + ); + } } // returns the number of corpses deleted. A negative number indicates an error code. -int32 EntityList::DeleteNPCCorpses() +uint32 EntityList::DeleteNPCCorpses() { - int32 x = 0; - - auto it = corpse_list.begin(); - while (it != corpse_list.end()) { - if (it->second->IsNPCCorpse()) { - it->second->DepopNPCCorpse(); - x++; + uint32 corpse_count = 0; + for (const auto& corpse : corpse_list) { + if (corpse.second->IsNPCCorpse()) { + corpse.second->DepopNPCCorpse(); + corpse_count++; } - ++it; } - return x; + return corpse_count; } void EntityList::CorpseFix(Client* c) @@ -3215,19 +3240,16 @@ void EntityList::CorpseFix(Client* c) } // returns the number of corpses deleted. A negative number indicates an error code. -int32 EntityList::DeletePlayerCorpses() +uint32 EntityList::DeletePlayerCorpses() { - int32 x = 0; - - auto it = corpse_list.begin(); - while (it != corpse_list.end()) { - if (it->second->IsPlayerCorpse()) { - it->second->CastToCorpse()->Delete(); - x++; + uint32 corpse_count = 0; + for (const auto& corpse : corpse_list) { + if (corpse.second->IsPlayerCorpse()) { + corpse.second->Delete(); + corpse_count++; } - ++it; } - return x; + return corpse_count; } void EntityList::SendPetitionToAdmins() diff --git a/zone/entity.h b/zone/entity.h index a42165fc6..c264237c9 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -440,8 +440,8 @@ public: void ListNPCCorpses(Client* client); void ListPlayerCorpses(Client* client); - int32 DeleteNPCCorpses(); - int32 DeletePlayerCorpses(); + uint32 DeleteNPCCorpses(); + uint32 DeletePlayerCorpses(); void CorpseFix(Client* c); void WriteEntityIDs(); void HalveAggro(Mob* who); diff --git a/zone/gm_commands/corpse.cpp b/zone/gm_commands/corpse.cpp index 739c3458a..43c760fc4 100755 --- a/zone/gm_commands/corpse.cpp +++ b/zone/gm_commands/corpse.cpp @@ -3,166 +3,354 @@ void command_corpse(Client *c, const Seperator *sep) { + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #corpse delete - Delete targeted corpse"); + c->Message(Chat::White, "Usage: #corpse deletenpccorpses - Deletes all NPC corpses"); + c->Message(Chat::White, "Usage: #corpse inspectloot - Inspects the loot on a corpse"); + c->Message(Chat::White, "Usage: #corpse listnpc - Lists all NPC corpses"); + c->Message(Chat::White, "Usage: #corpse lock - Locks the corpse, only GMs can loot the corpse when it is locked"); + c->Message(Chat::White, "Usage: #corpse removecash - Removes the cash from a corpse"); + c->Message(Chat::White, "Usage: #corpse unlock - Unlocks the corpses, allowing non-GMs to loot the corpse"); + if (c->Admin() >= commandEditPlayerCorpses) { + c->Message(Chat::White, "Usage: #corpse charid [Character ID] - Change player corpse's owner"); + c->Message(Chat::White, "Usage: #corpse deleteplayercorpses - Deletes all player corpses"); + c->Message(Chat::White, "Usage: #corpse depop [Bury] - Depops single target corpse."); + c->Message(Chat::White, "Usage: #corpse depopall [Bury] - Depops all target player's corpses."); + c->Message(Chat::White, "Usage: #corpse listplayer - Lists all player corpses"); + c->Message(Chat::White, "Usage: #corpse moveallgraveyard - Moves all player corpses to the current zone's graveyard or non-instance"); + c->Message(Chat::White, "Note: Set bury to 0 to skip burying the corpses."); + } + return; + } + Mob *target = c->GetTarget(); + bool is_character_id = !strcasecmp(sep->arg[1], "charid"); + bool is_delete = !strcasecmp(sep->arg[1], "delete"); + bool is_delete_npc_corpses = !strcasecmp(sep->arg[1], "deletenpccorpses"); + bool is_delete_player_corpses = !strcasecmp(sep->arg[1], "deleteplayercorpses"); + bool is_depop = !strcasecmp(sep->arg[1], "depop"); + bool is_depop_all = !strcasecmp(sep->arg[1], "depopall"); + bool is_inspect_loot = !strcasecmp(sep->arg[1], "inspectloot"); + bool is_list_npc = !strcasecmp(sep->arg[1], "listnpc"); + bool is_list_player = !strcasecmp(sep->arg[1], "listplayer"); + bool is_lock = !strcasecmp(sep->arg[1], "lock"); + bool is_move_all_to_graveyard = !strcasecmp(sep->arg[1], "moveallgraveyard"); + bool is_remove_cash = !strcasecmp(sep->arg[1], "removecash"); + bool is_reset_looter = !strcasecmp(sep->arg[1], "resetlooter"); + bool is_unlock = !strcasecmp(sep->arg[1], "unlock"); + if ( + !is_character_id && + !is_delete && + !is_delete_npc_corpses && + !is_delete_player_corpses && + !is_depop && + !is_depop_all && + !is_inspect_loot && + !is_list_npc && + !is_list_player && + !is_lock && + !is_move_all_to_graveyard && + !is_remove_cash && + !is_reset_looter && + !is_unlock + ) { + c->Message(Chat::White, "Usage: #corpse delete - Delete targeted corpse"); + c->Message(Chat::White, "Usage: #corpse deletenpccorpses - Deletes all NPC corpses"); + c->Message(Chat::White, "Usage: #corpse inspectloot - Inspects the loot on a corpse"); + c->Message(Chat::White, "Usage: #corpse listnpc - Lists all NPC corpses"); + c->Message(Chat::White, "Usage: #corpse lock - Locks the corpse, only GMs can loot the corpse when it is locked"); + c->Message(Chat::White, "Usage: #corpse removecash - Removes the cash from a corpse"); + c->Message(Chat::White, "Usage: #corpse unlock - Unlocks the corpses, allowing non-GMs to loot the corpse"); + if (c->Admin() >= commandEditPlayerCorpses) { + c->Message(Chat::White, "Usage: #corpse charid [Character ID] - Change player corpse's owner"); + c->Message(Chat::White, "Usage: #corpse deleteplayercorpses - Deletes all player corpses"); + c->Message(Chat::White, "Usage: #corpse depop [Bury] - Depops single target corpse."); + c->Message(Chat::White, "Usage: #corpse depopall [Bury] - Depops all target player's corpses."); + c->Message(Chat::White, "Usage: #corpse listplayer - Lists all player corpses"); + c->Message(Chat::White, "Usage: #corpse moveallgraveyard - Moves all player corpses to the current zone's graveyard or non-instance"); + c->Message(Chat::White, "Note: Set bury to 0 to skip burying the corpses."); + } + return; + } - if (strcasecmp(sep->arg[1], "DeletePlayerCorpses") == 0 && c->Admin() >= commandEditPlayerCorpses) { - int32 tmp = entity_list.DeletePlayerCorpses(); - if (tmp >= 0) { - c->Message(Chat::White, "%i corpses deleted.", tmp); - } - else { - c->Message(Chat::White, "DeletePlayerCorpses Error #%i", tmp); - } - } - else if (strcasecmp(sep->arg[1], "delete") == 0) { - if (target == 0 || !target->IsCorpse()) { - c->Message(Chat::White, "Error: Target the corpse you wish to delete"); - } - else if (target->IsNPCCorpse()) { - c->Message(Chat::White, "Depoping %s.", target->GetName()); - target->CastToCorpse()->Delete(); + if (is_delete_player_corpses) { + if (c->Admin() >= commandEditPlayerCorpses) { + auto corpses_deleted = entity_list.DeletePlayerCorpses(); + auto deleted_string = ( + corpses_deleted ? + fmt::format( + "{} Player corpse{} deleted.", + corpses_deleted, + corpses_deleted != 1 ? "s" : "" + ) : + "There are no player corpses to delete." + ); + c->Message(Chat::White, deleted_string.c_str()); + } else { + c->Message(Chat::White, "Your status is not high enough to delete player corpses."); + return; } - else if (c->Admin() >= commandEditPlayerCorpses) { - c->Message(Chat::White, "Deleting %s.", target->GetName()); - target->CastToCorpse()->Delete(); + } else if (is_delete) { + if (!target || !target->IsCorpse()) { + c->Message(Chat::White, "You must target a corpse to use this command."); + return; } - else { - c->Message(Chat::White, "Insufficient status to delete player corpse."); + + if (target->IsPlayerCorpse() && c->Admin() < commandEditPlayerCorpses) { + c->Message(Chat::White, "Your status is not high enough to delete a player corpse."); + return; } - } - else if (strcasecmp(sep->arg[1], "ListNPC") == 0) { - entity_list.ListNPCCorpses(c); - } - else if (strcasecmp(sep->arg[1], "ListPlayer") == 0) { - entity_list.ListPlayerCorpses(c); - } - else if (strcasecmp(sep->arg[1], "DeleteNPCCorpses") == 0) { - int32 tmp = entity_list.DeleteNPCCorpses(); - if (tmp >= 0) { - c->Message(Chat::White, "%d corpses deleted.", tmp); - } - else { - c->Message(Chat::White, "DeletePlayerCorpses Error #%d", tmp); - } - } - else if (strcasecmp(sep->arg[1], "charid") == 0 && c->Admin() >= commandEditPlayerCorpses) { - if (target == 0 || !target->IsPlayerCorpse()) { - c->Message(Chat::White, "Error: Target must be a player corpse."); - } - else if (!sep->IsNumber(2)) { - c->Message(Chat::White, "Error: charid must be a number."); - } - else { + + if ( + target->IsNPCCorpse() || + c->Admin() >= commandEditPlayerCorpses + ) { c->Message( Chat::White, - "Setting CharID=%u on PlayerCorpse '%s'", - target->CastToCorpse()->SetCharID(atoi(sep->arg[2])), - target->GetName()); - } - } - else if (strcasecmp(sep->arg[1], "ResetLooter") == 0) { - if (target == 0 || !target->IsCorpse()) { - c->Message(Chat::White, "Error: Target the corpse you wish to reset"); - } - else { - target->CastToCorpse()->ResetLooter(); - } - } - else if (strcasecmp(sep->arg[1], "RemoveCash") == 0) { - if (target == 0 || !target->IsCorpse()) { - c->Message(Chat::White, "Error: Target the corpse you wish to remove the cash from"); - } - else if (!target->IsPlayerCorpse() || c->Admin() >= commandEditPlayerCorpses) { - c->Message(Chat::White, "Removing Cash from %s.", target->GetName()); - target->CastToCorpse()->RemoveCash(); - } - else { - c->Message(Chat::White, "Insufficient status to modify player corpse."); - } - } - else if (strcasecmp(sep->arg[1], "InspectLoot") == 0) { - if (target == 0 || !target->IsCorpse()) { - c->Message(Chat::White, "Error: Target must be a corpse."); - } - else { - target->CastToCorpse()->QueryLoot(c); - } - } - else if (strcasecmp(sep->arg[1], "lock") == 0) { - if (target == 0 || !target->IsCorpse()) { - c->Message(Chat::White, "Error: Target must be a corpse."); - } - else { - target->CastToCorpse()->Lock(); - c->Message(Chat::White, "Locking %s...", target->GetName()); - } - } - else if (strcasecmp(sep->arg[1], "unlock") == 0) { - if (target == 0 || !target->IsCorpse()) { - c->Message(Chat::White, "Error: Target must be a corpse."); - } - else { - target->CastToCorpse()->UnLock(); - c->Message(Chat::White, "Unlocking %s...", target->GetName()); - } - } - else if (strcasecmp(sep->arg[1], "depop") == 0) { - if (target == 0 || !target->IsPlayerCorpse()) { - c->Message(Chat::White, "Error: Target must be a player corpse."); - } - else if (c->Admin() >= commandEditPlayerCorpses && target->IsPlayerCorpse()) { - c->Message(Chat::White, "Depoping %s.", target->GetName()); - target->CastToCorpse()->DepopPlayerCorpse(); - if (!sep->arg[2][0] || atoi(sep->arg[2]) != 0) { - target->CastToCorpse()->Bury(); - } - } - else { - c->Message(Chat::White, "Insufficient status to depop player corpse."); - } - } - else if (strcasecmp(sep->arg[1], "depopall") == 0) { - if (target == 0 || !target->IsClient()) { - c->Message(Chat::White, "Error: Target must be a player."); - } - else if (c->Admin() >= commandEditPlayerCorpses && target->IsClient()) { - c->Message(Chat::White, "Depoping %s\'s corpses.", target->GetName()); - target->CastToClient()->DepopAllCorpses(); - if (!sep->arg[2][0] || atoi(sep->arg[2]) != 0) { - target->CastToClient()->BuryPlayerCorpses(); - } - } - else { - c->Message(Chat::White, "Insufficient status to depop player corpse."); + fmt::format( + "Deleting {} corpse {} ({}).", + target->IsNPCCorpse() ? "NPC" : "player", + target->GetName(), + target->GetID() + ).c_str() + ); + target->CastToCorpse()->Delete(); + } + } else if (is_list_npc) { + entity_list.ListNPCCorpses(c); + } else if (is_list_player) { + if (c->Admin() < commandEditPlayerCorpses) { + c->Message(Chat::White, "Your status is not high enough to list player corpses."); + return; } - } - else if (strcasecmp(sep->arg[1], "moveallgraveyard") == 0) { - int count = entity_list.MovePlayerCorpsesToGraveyard(true); - c->Message(Chat::White, "Moved [%d] player corpse(s) to zone graveyard", count); - } - else if (sep->arg[1][0] == 0 || strcasecmp(sep->arg[1], "help") == 0) { - c->Message(Chat::White, "#Corpse Sub-Commands:"); - c->Message(Chat::White, " DeleteNPCCorpses"); - c->Message(Chat::White, " Delete - Delete targetted corpse"); - c->Message(Chat::White, " ListNPC"); - c->Message(Chat::White, " ListPlayer"); - c->Message(Chat::White, " Lock - GM locks the corpse - cannot be looted by non-GM"); - c->Message(Chat::White, " MoveAllGraveyard - move all player corpses to zone's graveyard or non-instance"); - c->Message(Chat::White, " UnLock"); - c->Message(Chat::White, " RemoveCash"); - c->Message(Chat::White, " InspectLoot"); - c->Message(Chat::White, " [to remove items from corpses, loot them]"); - c->Message(Chat::White, "Lead-GM status required to delete/modify player corpses"); - c->Message(Chat::White, " DeletePlayerCorpses"); - c->Message(Chat::White, " CharID [charid] - change player corpse's owner"); - c->Message(Chat::White, " Depop [bury] - Depops single target corpse."); - c->Message(Chat::White, " Depopall [bury] - Depops all target player's corpses."); - c->Message(Chat::White, "Set bury to 0 to skip burying the corpses."); - } - else { - c->Message(Chat::White, "Error, #corpse sub-command not found"); + entity_list.ListPlayerCorpses(c); + } else if (is_delete_npc_corpses) { + auto corpses_deleted = entity_list.DeleteNPCCorpses(); + auto deleted_string = ( + corpses_deleted ? + fmt::format( + "{} NPC corpse{} deleted.", + corpses_deleted, + corpses_deleted != 1 ? "s" : "" + ) : + "There are no NPC corpses to delete." + ); + c->Message(Chat::White, deleted_string.c_str()); + } else if (is_character_id) { + if (c->Admin() >= commandEditPlayerCorpses) { + if (!target || !target->IsPlayerCorpse()) { + c->Message(Chat::White, "You must target a player corpse to use this command."); + return; + } + + if (!sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #corpse charid [Character ID] - Change player corpse's owner"); + return; + } + + auto character_id = std::stoi(sep->arg[2]); + c->Message( + Chat::White, + fmt::format( + "Setting the owner to {} ({}) for the player corpse {} ({}).", + database.GetCharNameByID(character_id), + target->CastToCorpse()->SetCharID(character_id), + target->GetName(), + target->GetID() + ).c_str() + ); + } else { + c->Message(Chat::White, "Your status is not high enough to modify a player corpse's owner."); + return; + } + } else if (is_reset_looter) { + if (!target || !target->IsCorpse()) { + c->Message(Chat::White, "You must target a corpse to use this command."); + return; + } + + if (target->IsPlayerCorpse() && c->Admin() < commandEditPlayerCorpses) { + c->Message(Chat::White, "Your status is not high enough to reset looter on a player corpse."); + return; + } + + target->CastToCorpse()->ResetLooter(); + c->Message( + Chat::White, + fmt::format( + "Reset looter for {} corpse {} ({}).", + target->IsNPCCorpse() ? "NPC" : "player", + target->GetName(), + target->GetID() + ).c_str() + ); + } else if (is_remove_cash) { + if (!target || !target->IsCorpse()) { + c->Message(Chat::White, "You must target a corpse to use this command."); + return; + } + + if (target->IsPlayerCorpse() && c->Admin() < commandEditPlayerCorpses) { + c->Message(Chat::White, "Your status is not high enough to remove cash from a player corpse."); + return; + } + + if ( + target->IsNPCCorpse() || + c->Admin() >= commandEditPlayerCorpses + ) { + target->CastToCorpse()->RemoveCash(); + c->Message( + Chat::White, + fmt::format( + "Removed cash from {} corpse {} ({}).", + target->IsNPCCorpse() ? "NPC" : "player", + target->GetName(), + target->GetID() + ).c_str() + ); + } + } else if (is_inspect_loot) { + if (!target || !target->IsCorpse()) { + c->Message(Chat::White, "You must target a corpse to use this command."); + return; + } + + if (target->IsPlayerCorpse() && c->Admin() < commandEditPlayerCorpses) { + c->Message(Chat::White, "Your status is not high enough to inspect the loot of a player corpse."); + return; + } + + target->CastToCorpse()->QueryLoot(c); + } else if (is_lock) { + if (!target || !target->IsCorpse()) { + c->Message(Chat::White, "You must target a corpse to use this command."); + return; + } + + if (target->IsPlayerCorpse() && c->Admin() < commandEditPlayerCorpses) { + c->Message(Chat::White, "Your status is not high enough to lock player corpses."); + return; + } + + target->CastToCorpse()->Lock(); + c->Message( + Chat::White, + fmt::format( + "Locking {} corpse {} ({}).", + target->IsNPCCorpse() ? "NPC" : "player", + target->GetName(), + target->GetID() + ).c_str() + ); + } else if (is_unlock) { + if (!target || !target->IsCorpse()) { + c->Message(Chat::White, "You must target a corpse to use this command."); + return; + } + + if (target->IsPlayerCorpse() && c->Admin() < commandEditPlayerCorpses) { + c->Message(Chat::White, "Your status is not high enough to unlock player corpses."); + return; + } + + target->CastToCorpse()->UnLock(); + c->Message( + Chat::White, + fmt::format( + "Unlocking {} corpse {} ({}).", + target->IsNPCCorpse() ? "NPC" : "player", + target->GetName(), + target->GetID() + ).c_str() + ); + } else if (is_depop) { + if (!target || !target->IsPlayerCorpse()) { + c->Message(Chat::White, "You must target a player corpse to use this command."); + return; + } + + if (c->Admin() >= commandEditPlayerCorpses) { + bool bury_corpse = ( + sep->IsNumber(2) ? + ( + std::stoi(sep->arg[2]) != 0 ? + true : + false + ) : + false + ); + c->Message( + Chat::White, + fmt::format( + "Depopping player corpse {} ({}).", + target->GetName(), + target->GetID() + ).c_str() + ); + target->CastToCorpse()->DepopPlayerCorpse(); + if (bury_corpse) { + target->CastToCorpse()->Bury(); + } + } else { + c->Message(Chat::White, "Your status is not high enough to depop a player corpse."); + return; + } + } else if (is_depop_all) { + if (!target || !target->IsClient()) { + c->Message(Chat::White, "You must target a player to use this command."); + return; + } + + if (c->Admin() >= commandEditPlayerCorpses) { + bool bury_corpse = ( + sep->IsNumber(2) ? + ( + std::stoi(sep->arg[2]) != 0 ? + true : + false + ) : + false + ); + c->Message( + Chat::White, + fmt::format( + "Depopping all player corpses for {} ({}).", + target->GetName(), + target->GetID() + ).c_str() + ); + target->CastToClient()->DepopAllCorpses(); + if (bury_corpse) { + target->CastToClient()->BuryPlayerCorpses(); + } + } else { + c->Message(Chat::White, "Your status is not high enough to depop all of a player's corpses."); + return; + } + } else if (is_move_all_to_graveyard) { + int moved_count = entity_list.MovePlayerCorpsesToGraveyard(true); + if (c->Admin() >= commandEditPlayerCorpses) { + if (moved_count) { + c->Message( + Chat::White, + fmt::format( + "Moved {} player corpse{} to graveyard in {} ({}).", + moved_count, + moved_count != 1 ? "s" : "", + ZoneLongName(zone->GetZoneID()), + ZoneName(zone->GetZoneID()) + ).c_str() + ); + } else { + c->Message(Chat::White, "There are no player corpses to move to the graveyard."); + } + } else { + c->Message(Chat::White, "Your status is not high enough to move all player corpses to the graveyard."); + } } } diff --git a/zone/lua_entity_list.cpp b/zone/lua_entity_list.cpp index c7721271e..9ee08ecbf 100644 --- a/zone/lua_entity_list.cpp +++ b/zone/lua_entity_list.cpp @@ -277,12 +277,12 @@ void Lua_EntityList::SignalMobsByNPCID(uint32 npc_id, int signal) { self->SignalMobsByNPCID(npc_id, signal); } -int Lua_EntityList::DeleteNPCCorpses() { +uint32 Lua_EntityList::DeleteNPCCorpses() { Lua_Safe_Call_Int(); return self->DeleteNPCCorpses(); } -int Lua_EntityList::DeletePlayerCorpses() { +uint32 Lua_EntityList::DeletePlayerCorpses() { Lua_Safe_Call_Int(); return self->DeletePlayerCorpses(); } @@ -489,8 +489,8 @@ luabind::scope lua_register_entity_list() { .def("ChannelMessage", (void(Lua_EntityList::*)(Lua_Mob, int, int, const char*))&Lua_EntityList::ChannelMessage) .def("ClearClientPetitionQueue", (void(Lua_EntityList::*)(void))&Lua_EntityList::ClearClientPetitionQueue) .def("ClearFeignAggro", (void(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::ClearFeignAggro) - .def("DeleteNPCCorpses", (int(Lua_EntityList::*)(void))&Lua_EntityList::DeleteNPCCorpses) - .def("DeletePlayerCorpses", (int(Lua_EntityList::*)(void))&Lua_EntityList::DeletePlayerCorpses) + .def("DeleteNPCCorpses", (uint32(Lua_EntityList::*)(void))&Lua_EntityList::DeleteNPCCorpses) + .def("DeletePlayerCorpses", (uint32(Lua_EntityList::*)(void))&Lua_EntityList::DeletePlayerCorpses) .def("DoubleAggro", (void(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::DoubleAggro) .def("Fighting", (bool(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::Fighting) .def("FilteredMessageClose", &Lua_EntityList::FilteredMessageClose) diff --git a/zone/lua_entity_list.h b/zone/lua_entity_list.h index 4fa7b97b9..ba67971dd 100644 --- a/zone/lua_entity_list.h +++ b/zone/lua_entity_list.h @@ -99,8 +99,8 @@ public: std::string MakeNameUnique(const char *name); std::string RemoveNumbers(const char *name); void SignalMobsByNPCID(uint32 npc_id, int signal); - int DeleteNPCCorpses(); - int DeletePlayerCorpses(); + uint32 DeleteNPCCorpses(); + uint32 DeletePlayerCorpses(); void HalveAggro(Lua_Mob who); void DoubleAggro(Lua_Mob who); void ClearFeignAggro(Lua_Mob who); diff --git a/zone/npc.cpp b/zone/npc.cpp index c11e6beb0..905257a28 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -674,12 +674,18 @@ void NPC::QueryLoot(Client* to) to->Message( Chat::White, fmt::format( - "Item {} | Name: {} ID: {} Min Level: {} Max Level: {}", + "Item {} | Name: {} ({}){}", item_number, linker.GenerateLink().c_str(), current_item->item_id, - current_item->trivial_min_level, - current_item->trivial_max_level + ( + current_item->charges > 1 ? + fmt::format( + " Amount: {}", + current_item->charges + ) : + "" + ) ).c_str() ); item_count++; @@ -696,11 +702,13 @@ void NPC::QueryLoot(Client* to) to->Message( Chat::White, fmt::format( - "Money | Platinum: {} Gold: {} Silver: {} Copper: {}", - platinum, - gold, - silver, - copper + "Money | {}", + ConvertMoneyToString( + platinum, + gold, + silver, + copper + ) ).c_str() ); } diff --git a/zone/perl_entity.cpp b/zone/perl_entity.cpp index 8ae7976eb..7dddf791b 100644 --- a/zone/perl_entity.cpp +++ b/zone/perl_entity.cpp @@ -1040,12 +1040,12 @@ XS(XS_EntityList_DeleteNPCCorpses) { Perl_croak(aTHX_ "Usage: EntityList::DeleteNPCCorpses(THIS)"); // @categories Corpse { EntityList *THIS; - int32 RETVAL; + uint32 RETVAL; dXSTARG; VALIDATE_THIS_IS_ENTITY; RETVAL = THIS->DeleteNPCCorpses(); XSprePUSH; - PUSHi((IV) RETVAL); + PUSHu((UV) RETVAL); } XSRETURN(1); } @@ -1057,12 +1057,12 @@ XS(XS_EntityList_DeletePlayerCorpses) { Perl_croak(aTHX_ "Usage: EntityList::DeletePlayerCorpses(THIS)"); // @categories Account and Character, Corpse { EntityList *THIS; - int32 RETVAL; + uint32 RETVAL; dXSTARG; VALIDATE_THIS_IS_ENTITY; RETVAL = THIS->DeletePlayerCorpses(); XSprePUSH; - PUSHi((IV) RETVAL); + PUSHu((UV) RETVAL); } XSRETURN(1); } From 39c27c987d09078746a3db48e20801e644ec1811 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 21 Nov 2021 14:19:08 -0500 Subject: [PATCH 443/624] [Commands] Cleanup #peqzone Command. (#1794) - Cleanup messages and logic. - Add RULE_INT(Zone, PEQZoneHPRatio, 75, "Required HP Ratio to use #peqzone") - Modify #peqzone Timer rule to allow it to be disabled. --- common/ruletypes.h | 3 +- zone/client.cpp | 2 +- zone/client_packet.cpp | 2 +- zone/command.cpp | 2 +- zone/gm_commands/peqzone.cpp | 137 +++++++++++++++++----------- zone/gm_commands/showzonepoints.cpp | 4 +- zone/zone_store.cpp | 20 ++++ zone/zone_store.h | 9 +- 8 files changed, 121 insertions(+), 58 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 5e62ba960..8b60ddb71 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -263,10 +263,11 @@ RULE_INT(Zone, ClientLinkdeadMS, 90000, "The time a client remains link dead on RULE_INT(Zone, GraveyardTimeMS, 1200000, "Time until a player corpse is moved to a zone's graveyard, if one is specified for the zone (milliseconds)") RULE_BOOL(Zone, EnableShadowrest, 1, "Enables or disables the Shadowrest zone feature for player corpses. Default is turned on") RULE_INT(Zone, AutoShutdownDelay, 5000, "How long a dynamic zone stays loaded while empty (milliseconds)") -RULE_INT(Zone, PEQZoneReuseTime, 900, "Time between two uses of the #peqzone command (seconds)") +RULE_INT(Zone, PEQZoneReuseTime, 900, "Seconds between two uses of the #peqzone command (Set to 0 to disable)") RULE_INT(Zone, PEQZoneDebuff1, 4454, "First debuff casted by #peqzone Default is Cursed Keeper's Blight") RULE_INT(Zone, PEQZoneDebuff2, 2209, "Second debuff casted by #peqzone Default is Tendrils of Apathy") RULE_BOOL(Zone, UsePEQZoneDebuffs, true, "Setting if the command #peqzone applies the defined debuffs") +RULE_INT(Zone, PEQZoneHPRatio, 75, "Required HP Ratio to use #peqzone") RULE_REAL(Zone, HotZoneBonus, 0.75, "Value which is added to the experience multiplier. This also applies to AA experience.") RULE_INT(Zone, EbonCrystalItemID, 40902, "Item ID for Ebon Crystal") RULE_INT(Zone, RadiantCrystalItemID, 40903, "Item ID for Radiant Crystal") diff --git a/zone/client.cpp b/zone/client.cpp index 3fcd5f443..ed29755ab 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -9459,7 +9459,7 @@ void Client::CheckVirtualZoneLines() LogZonePoints( "Virtual Zone Box Sending player [{}] to [{}]", GetCleanName(), - zone_store.GetZoneLongName(virtual_zone_point.target_zone_id) + ZoneLongName(virtual_zone_point.target_zone_id) ); } } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index c2b44842e..ef62a693b 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -12876,7 +12876,7 @@ void Client::Handle_OP_SetStartCity(const EQApplicationPacket *app) else zone_id = atoi(row[0]); - std::string zone_long_name = zone_store.GetZoneLongName(zone_id); + std::string zone_long_name = ZoneLongName(zone_id); Message(Chat::Yellow, "%d - %s", zone_id, zone_long_name.c_str()); } diff --git a/zone/command.cpp b/zone/command.cpp index 7da3253d3..858e6c5b8 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -273,7 +273,7 @@ int command_init(void) command_add("opcode", "- opcode management", AccountStatus::GMImpossible, command_opcode) || command_add("path", "- view and edit pathing", AccountStatus::GMMgmt, command_path) || command_add("peekinv", "[equip/gen/cursor/poss/limbo/curlim/trib/bank/shbank/allbank/trade/world/all] - Print out contents of your player target's inventory", AccountStatus::GMAdmin, command_peekinv) || - command_add("peqzone", "[zonename] - Go to specified zone, if you have > 75% health", AccountStatus::Player, command_peqzone) || + command_add("peqzone", "[Zone ID|Zone Short Name] - Teleports you to the specified zone if you meet the requirements.", AccountStatus::Player, command_peqzone) || command_add("permaclass", "[Class ID] - Change your or your player target's class, changed client is disconnected", AccountStatus::QuestTroupe, command_permaclass) || command_add("permagender", "[Gender ID] - Change your or your player target's gender", AccountStatus::QuestTroupe, command_permagender) || command_add("permarace", "[Race ID] - Change your or your player target's race", AccountStatus::QuestTroupe, command_permarace) || diff --git a/zone/gm_commands/peqzone.cpp b/zone/gm_commands/peqzone.cpp index 704e6dd1d..3c94965fc 100755 --- a/zone/gm_commands/peqzone.cpp +++ b/zone/gm_commands/peqzone.cpp @@ -2,74 +2,109 @@ void command_peqzone(Client *c, const Seperator *sep) { - uint32 timeleft = c->GetPTimers().GetRemainingTime(pTimerPeqzoneReuse) / 60; - - if (!c->GetPTimers().Expired(&database, pTimerPeqzoneReuse, false)) { - c->Message(Chat::Red, "You must wait %i minute(s) before using this ability again.", timeleft); + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #peqzone [Zone ID] or #peqzone [Zone Short Name]"); return; } - if (c->GetHPRatio() < 75) { - c->Message(Chat::White, "You cannot use this command with less than 75 percent health."); + auto reuse_timer = RuleI(Zone, PEQZoneReuseTime); + if (reuse_timer) { + uint32 time_left = c->GetPTimers().GetRemainingTime(pTimerPeqzoneReuse); + if (!c->GetPTimers().Expired(&database, pTimerPeqzoneReuse, false)) { + c->Message( + Chat::White, + fmt::format( + "You must wait {} before using this command again.", + ConvertSecondsToTime(time_left) + ).c_str() + ); + return; + } + } + + auto hp_ratio = RuleI(Zone, PEQZoneHPRatio); + if (c->GetHPRatio() < hp_ratio) { + c->Message( + Chat::White, + fmt::format( + "You cannot use this command with less than {}%% health.", + hp_ratio + ).c_str() + ); return; } - //this isnt perfect, but its better... if ( - c->IsInvisible(c) - || c->IsRooted() - || c->IsStunned() - || c->IsMezzed() - || c->AutoAttackEnabled() - || c->GetInvul() - ) { + c->IsInvisible(c) || + c->IsRooted() || + c->IsStunned() || + c->IsMezzed() || + c->AutoAttackEnabled() || + c->GetInvul() + ) { c->Message(Chat::White, "You cannot use this command in your current state. Settle down and wait."); return; } - uint16 zoneid = 0; - uint8 destzone = 0; - if (sep->IsNumber(1)) { - zoneid = atoi(sep->arg[1]); - destzone = content_db.GetPEQZone(zoneid, 0); - if (destzone == 0) { - c->Message(Chat::Red, "You cannot use this command to enter that zone!"); - return; - } - if (zoneid == zone->GetZoneID()) { - c->Message(Chat::Red, "You cannot use this command on the zone you are in!"); - return; - } - } - else if (sep->arg[1][0] == 0 || sep->IsNumber(2) || sep->IsNumber(3) || sep->IsNumber(4) || sep->IsNumber(5)) { - c->Message(Chat::White, "Usage: #peqzone [zonename]"); - c->Message(Chat::White, "Optional Usage: #peqzone [zoneid]"); + auto zone_id = ( + sep->IsNumber(1) ? + static_cast(std::stoul(sep->arg[1])) : + static_cast(ZoneID(sep->arg[1])) + ); + auto zone_short_name = ZoneName(zone_id); + auto zone_long_name = ZoneLongName(zone_id); + if ( + !zone_id || + !zone_short_name + ) { + c->Message( + Chat::White, + fmt::format( + "No zones were found matching '{}'.", + sep->arg[1] + ).c_str() + ); return; } - else { - zoneid = ZoneID(sep->arg[1]); - destzone = content_db.GetPEQZone(zoneid, 0); - if (zoneid == 0) { - c->Message(Chat::White, "Unable to locate zone '%s'", sep->arg[1]); - return; - } - if (destzone == 0) { - c->Message(Chat::Red, "You cannot use this command to enter that zone!"); - return; - } - if (zoneid == zone->GetZoneID()) { - c->Message(Chat::Red, "You cannot use this command on the zone you are in!"); - return; - } + + bool allows_peqzone = ( + content_db.GetPEQZone(zone_id, 0) ? + true : + false + ); + if (!allows_peqzone) { + c->Message( + Chat::White, + fmt::format( + "You cannot use this command to enter {} ({}).", + zone_long_name, + zone_short_name + ).c_str() + ); + return; } - if (RuleB (Zone, UsePEQZoneDebuffs)) { + if (zone_id == zone->GetZoneID()) { + c->Message( + Chat::White, + fmt::format( + "You are already in {} ({}).", + zone->GetLongName(), + zone->GetShortName() + ).c_str() + ); + return; + } + + if (RuleB(Zone, UsePEQZoneDebuffs)) { c->SpellOnTarget(RuleI(Zone, PEQZoneDebuff1), c); c->SpellOnTarget(RuleI(Zone, PEQZoneDebuff2), c); } - //zone to safe coords - c->GetPTimers().Start(pTimerPeqzoneReuse, RuleI(Zone, PEQZoneReuseTime)); - c->MovePC(zoneid, 0.0f, 0.0f, 0.0f, 0.0f, 0, ZoneToSafeCoords); -} + if (reuse_timer) { + c->GetPTimers().Start(pTimerPeqzoneReuse, reuse_timer); + } + c->MoveZone(zone_short_name); +} diff --git a/zone/gm_commands/showzonepoints.cpp b/zone/gm_commands/showzonepoints.cpp index f86dcd29e..d1e8c2428 100755 --- a/zone/gm_commands/showzonepoints.cpp +++ b/zone/gm_commands/showzonepoints.cpp @@ -16,7 +16,7 @@ void command_showzonepoints(Client *c, const Seperator *sep) c->SendChatLineBreak(); for (auto &virtual_zone_point : zone->virtual_zone_point_list) { - std::string zone_long_name = zone_store.GetZoneLongName(virtual_zone_point.target_zone_id); + std::string zone_long_name = ZoneLongName(virtual_zone_point.target_zone_id); c->Message( Chat::White, @@ -112,7 +112,7 @@ void command_showzonepoints(Client *c, const Seperator *sep) iterator.Reset(); while (iterator.MoreElements()) { ZonePoint *zone_point = iterator.GetData(); - std::string zone_long_name = zone_store.GetZoneLongName(zone_point->target_zone_id); + std::string zone_long_name = ZoneLongName(zone_point->target_zone_id); std::string node_name = fmt::format("ZonePoint To [{}]", zone_long_name); NPC::SpawnZonePointNodeNPC( diff --git a/zone/zone_store.cpp b/zone/zone_store.cpp index 467c3af25..67eaeb21e 100644 --- a/zone/zone_store.cpp +++ b/zone/zone_store.cpp @@ -80,6 +80,26 @@ const char *ZoneStore::GetZoneName(uint32 zone_id, bool error_unknown) return nullptr; } +/** + * @param zone_id + * @param error_unknown + * @return + */ +const char *ZoneStore::GetZoneLongName(uint32 zone_id, bool error_unknown) +{ + for (auto &z: zones) { + if (z.zoneidnumber == zone_id) { + return z.long_name.c_str(); + } + } + + if (error_unknown) { + return "UNKNOWN"; + } + + return nullptr; +} + /** * @param zone_id * @return diff --git a/zone/zone_store.h b/zone/zone_store.h index 8513c51fa..521ec63bb 100644 --- a/zone/zone_store.h +++ b/zone/zone_store.h @@ -41,6 +41,7 @@ public: std::string GetZoneName(uint32 zone_id); std::string GetZoneLongName(uint32 zone_id); const char *GetZoneName(uint32 zone_id, bool error_unknown = false); + const char *GetZoneLongName(uint32 zone_id, bool error_unknown = false); static void LoadContentFlags(); static void SetContentFlag(const std::string& content_flag_name, bool enabled); @@ -60,7 +61,13 @@ inline const char *ZoneName(uint32 zone_id, bool error_unknown = false) error_unknown ); } -inline const char *ZoneLongName(uint32 zone_id) { return zone_store.GetZoneLongName(zone_id).c_str(); } +inline const char *ZoneLongName(uint32 zone_id, bool error_unknown = false) +{ + return zone_store.GetZoneLongName( + zone_id, + error_unknown + ); +} inline ZoneRepository::Zone GetZone(uint32 zone_id, int version = 0) { return zone_store.GetZone(zone_id, version); }; inline ZoneRepository::Zone GetZone(const char *in_zone_name) { return zone_store.GetZone(in_zone_name); }; From d29993fafa33587e4973744981047620a4fd3c6d Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 21 Nov 2021 15:20:16 -0500 Subject: [PATCH 444/624] [Commands] Cleanup #nukebuffs Command. (#1795) * [Commands] Cleanup #nukebuffs Command. - Cleanup messages and logic. - #nukebuffs now allows you to nuke all, beneficial, or detrimental buffs, also added a help menu. - Add BuffFadeBeneficial(). - Cleanup logic in some buff fade methods. - Fix several spots where we were using CalcBonuses() when it was unnecessary, i.e when you fade no buffs you do not need to recalculate bonuses. * Update spells.cpp --- zone/command.cpp | 2 +- zone/gm_commands/nukebuffs.cpp | 45 ++++- zone/mob.h | 7 +- zone/spells.cpp | 290 ++++++++++++++++++++------------- 4 files changed, 220 insertions(+), 124 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 858e6c5b8..5742da04f 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -266,7 +266,7 @@ int command_init(void) command_add("npctype_cache", "[id] or all - Clears the npc type cache for either the id or all npcs.", AccountStatus::GMImpossible, command_npctype_cache) || command_add("npctypespawn", "[npctypeid] [factionid] - Spawn an NPC from the db", AccountStatus::Steward, command_npctypespawn) || command_add("nudge", "- Nudge your target's current position by specific values", AccountStatus::QuestTroupe, command_nudge) || - command_add("nukebuffs", "- Strip all buffs on you or your target", AccountStatus::Guide, command_nukebuffs) || + command_add("nukebuffs", "[Beneficial|Detrimental|Help] - Strip all buffs by type on you or your target (no argument to remove all buffs)", AccountStatus::Guide, command_nukebuffs) || command_add("nukeitem", "[Item ID] - Removes the specified Item ID from you or your player target's inventory", AccountStatus::GMLeadAdmin, command_nukeitem) || command_add("object", "List|Add|Edit|Move|Rotate|Copy|Save|Undo|Delete - Manipulate static and tradeskill objects within the zone", AccountStatus::GMAdmin, command_object) || command_add("oocmute", "[1/0] - Mutes OOC chat", AccountStatus::GMMgmt, command_oocmute) || diff --git a/zone/gm_commands/nukebuffs.cpp b/zone/gm_commands/nukebuffs.cpp index 8d1267b3c..bb6542ea0 100755 --- a/zone/gm_commands/nukebuffs.cpp +++ b/zone/gm_commands/nukebuffs.cpp @@ -1,12 +1,47 @@ #include "../client.h" void command_nukebuffs(Client *c, const Seperator *sep) -{ - if (c->GetTarget() == 0) { - c->BuffFadeAll(); +{ + Mob* target = c; + if (c->GetTarget()) { + target = c->GetTarget(); } - else { - c->GetTarget()->BuffFadeAll(); + + std::string buff_identifier = str_tolower(sep->arg[1]); + std::string buff_type; + bool is_beneficial = buff_identifier.find("beneficial") != std::string::npos; + bool is_detrimental = buff_identifier.find("detrimental") != std::string::npos; + bool is_help = buff_identifier.find("help") != std::string::npos; + if (is_beneficial) { + target->BuffFadeBeneficial(); + buff_type = " beneficial"; + } else if (is_detrimental) { + target->BuffFadeDetrimental(); + buff_type = " detrimental"; + } else if (is_help) { + c->Message(Chat::White, "Usage: #nukebuffs"); + c->Message(Chat::White, "Usage: #nukebuffs beneficial"); + c->Message(Chat::White, "Usage: #nukebuffs detrimental"); + return; + } else { + target->BuffFadeAll(); } + + c->Message( + Chat::White, + fmt::format( + "Faded all{} buffs for {}.", + buff_type, + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ) + ).c_str() + ); } diff --git a/zone/mob.h b/zone/mob.h index 1500f41af..a59cbe400 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -361,8 +361,9 @@ public: virtual void DoBuffTic(const Buffs_Struct &buff, int slot, Mob* caster = nullptr); void BuffFadeBySpellID(uint16 spell_id); void BuffFadeBySpellIDAndCaster(uint16 spell_id, uint16 caster_id); - void BuffFadeByEffect(int effect_id, int skipslot = -1); + void BuffFadeByEffect(int effect_id, int slot_to_skip = -1); void BuffFadeAll(); + void BuffFadeBeneficial(); void BuffFadeNonPersistDeath(); void BuffFadeDetrimental(); void BuffFadeBySlot(int slot, bool iRecalcBonuses = true); @@ -393,7 +394,7 @@ public: void DoGravityEffect(); void DamageShield(Mob* other, bool spell_ds = false); int32 RuneAbsorb(int32 damage, uint16 type); - bool FindBuff(uint16 spellid); + bool FindBuff(uint16 spell_id); uint16 FindBuffBySlot(int slot); uint32 BuffCount(); bool FindType(uint16 type, bool bOffensive = false, uint16 threshold = 100); @@ -421,7 +422,7 @@ public: inline float GetTargetRingZ() const { return m_TargetRing.z; } inline bool HasEndurUpkeep() const { return endur_upkeep; } inline void SetEndurUpkeep(bool val) { endur_upkeep = val; } - bool HasBuffWithSpellGroup(int spellgroup); + bool HasBuffWithSpellGroup(int spell_group); //Basic Stats/Inventory virtual void SetLevel(uint8 in_level, bool command = false) { level = in_level; } diff --git a/zone/spells.cpp b/zone/spells.cpp index e01df8a33..344d69781 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4255,21 +4255,27 @@ void Corpse::CastRezz(uint16 spellid, Mob* Caster) safe_delete(outapp); } -bool Mob::FindBuff(uint16 spellid) +bool Mob::FindBuff(uint16 spell_id) { - int i; - uint32 buff_count = GetMaxTotalSlots(); - for(i = 0; i < buff_count; i++) - if(buffs[i].spellid == spellid) + for (int buff_slot = 0; buff_slot < buff_count; buff_slot++) { + auto current_spell_id = buffs[buff_slot].spellid; + if ( + IsValidSpell(current_spell_id) && + current_spell_id == spell_id + ) { return true; + } + } return false; } uint16 Mob::FindBuffBySlot(int slot) { - if (buffs[slot].spellid != SPELL_UNKNOWN) - return buffs[slot].spellid; + auto current_spell_id = buffs[slot].spellid; + if (IsValidSpell(current_spell_id)) { + return current_spell_id; + } return 0; } @@ -4277,167 +4283,221 @@ uint16 Mob::FindBuffBySlot(int slot) { uint32 Mob::BuffCount() { uint32 active_buff_count = 0; int buff_count = GetMaxTotalSlots(); - for (int i = 0; i < buff_count; i++) - if (buffs[i].spellid != SPELL_UNKNOWN) + for (int buff_slot = 0; buff_slot < buff_count; buff_slot++) { + if (IsValidSpell(buffs[buff_slot].spellid)) { active_buff_count++; + } + } return active_buff_count; } -bool Mob::HasBuffWithSpellGroup(int spellgroup) +bool Mob::HasBuffWithSpellGroup(int spell_group) { - for (int i = 0; i < GetMaxTotalSlots(); i++) { - if (IsValidSpell(buffs[i].spellid) && spells[buffs[i].spellid].spell_group == spellgroup) { + for (int buff_slot = 0; buff_slot < GetMaxTotalSlots(); buff_slot++) { + auto current_spell_id = buffs[buff_slot].spellid; + if ( + IsValidSpell(current_spell_id) && + spells[current_spell_id].spell_group == spell_group + ) { return true; } } + return false; } -// removes all buffs void Mob::BuffFadeAll() { + bool recalc_bonus = false; int buff_count = GetMaxTotalSlots(); - for (int j = 0; j < buff_count; j++) { - if(buffs[j].spellid != SPELL_UNKNOWN) - BuffFadeBySlot(j, false); - } - //we tell BuffFadeBySlot not to recalc, so we can do it only once when were done - CalcBonuses(); -} - -void Mob::BuffFadeNonPersistDeath() -{ - int buff_count = GetMaxTotalSlots(); - for (int j = 0; j < buff_count; j++) { - if (buffs[j].spellid != SPELL_UNKNOWN && !IsPersistDeathSpell(buffs[j].spellid)) - BuffFadeBySlot(j, false); - } - //we tell BuffFadeBySlot not to recalc, so we can do it only once when were done - CalcBonuses(); -} - -void Mob::BuffFadeDetrimental() { - int buff_count = GetMaxTotalSlots(); - for (int j = 0; j < buff_count; j++) { - if(buffs[j].spellid != SPELL_UNKNOWN) { - if(IsDetrimentalSpell(buffs[j].spellid)) - BuffFadeBySlot(j, false); - } - } - //we tell BuffFadeBySlot not to recalc, so we can do it only once when were done - CalcBonuses(); -} - -void Mob::BuffFadeDetrimentalByCaster(Mob *caster) -{ - if(!caster) - return; - - int buff_count = GetMaxTotalSlots(); - for (int j = 0; j < buff_count; j++) { - if(buffs[j].spellid != SPELL_UNKNOWN) { - if(IsDetrimentalSpell(buffs[j].spellid)) - { - //this is a pretty terrible way to do this but - //there really isn't another way till I rewrite the basics - Mob * c = entity_list.GetMob(buffs[j].casterid); - if(c && c == caster) - BuffFadeBySlot(j, false); - } - } - } - //we tell BuffFadeBySlot not to recalc, so we can do it only once when were done - CalcBonuses(); -} - -void Mob::BuffFadeBySitModifier() -{ - bool r_bonus = false; - uint32 buff_count = GetMaxTotalSlots(); - for(uint32 j = 0; j < buff_count; ++j) - { - if(buffs[j].spellid != SPELL_UNKNOWN) - { - if(spells[buffs[j].spellid].disallow_sit) - { - BuffFadeBySlot(j, false); - r_bonus = true; - } + for (int buff_slot = 0; buff_slot < buff_count; buff_slot++) { + if (IsValidSpell(buffs[buff_slot].spellid)) { + BuffFadeBySlot(buff_slot, false); + recalc_bonus = true; } } - if(r_bonus) - { + if (recalc_bonus) { CalcBonuses(); } } -// removes the buff matching spell_id -void Mob::BuffFadeBySpellID(uint16 spell_id) +void Mob::BuffFadeNonPersistDeath() { + bool recalc_bonus = false; int buff_count = GetMaxTotalSlots(); - for (int j = 0; j < buff_count; j++) - { - if (buffs[j].spellid == spell_id) - BuffFadeBySlot(j, false); + for (int buff_slot = 0; buff_slot < buff_count; buff_slot++) { + auto current_spell_id = buffs[buff_slot].spellid; + if ( + IsValidSpell(current_spell_id) && + !IsPersistDeathSpell(current_spell_id) + ) { + BuffFadeBySlot(buff_slot, false); + recalc_bonus = true; + } } - //we tell BuffFadeBySlot not to recalc, so we can do it only once when were done - CalcBonuses(); + if (recalc_bonus) { + CalcBonuses(); + } +} + +void Mob::BuffFadeBeneficial() { + bool recalc_bonus = false; + int buff_count = GetMaxTotalSlots(); + for (int buff_slot = 0; buff_slot < buff_count; buff_slot++) { + auto current_spell_id = buffs[buff_slot].spellid; + if ( + IsValidSpell(current_spell_id) && + IsBeneficialSpell(current_spell_id) + ) { + BuffFadeBySlot(buff_slot, false); + recalc_bonus = true; + } + } + + if (recalc_bonus) { + CalcBonuses(); + } +} + +void Mob::BuffFadeDetrimental() { + bool recalc_bonus = false; + int buff_count = GetMaxTotalSlots(); + for (int buff_slot = 0; buff_slot < buff_count; buff_slot++) { + auto current_spell_id = buffs[buff_slot].spellid; + if ( + IsValidSpell(current_spell_id) && + IsDetrimentalSpell(current_spell_id) + ) { + BuffFadeBySlot(buff_slot, false); + recalc_bonus = true; + } + } + + if (recalc_bonus) { + CalcBonuses(); + } +} + +void Mob::BuffFadeDetrimentalByCaster(Mob *caster) +{ + if(!caster) { + return; + } + + bool recalc_bonus = false; + int buff_count = GetMaxTotalSlots(); + for (int buff_slot = 0; buff_slot < buff_count; buff_slot++) { + auto current_spell_id = buffs[buff_slot].spellid; + if ( + IsValidSpell(current_spell_id) && + IsDetrimentalSpell(current_spell_id) && + caster->GetID() == buffs[buff_slot].casterid + ) { + BuffFadeBySlot(buff_slot, false); + recalc_bonus = true; + } + } + + if (recalc_bonus) { + CalcBonuses(); + } +} + +void Mob::BuffFadeBySitModifier() +{ + bool recalc_bonus = false; + int buff_count = GetMaxTotalSlots(); + for (int buff_slot = 0; buff_slot < buff_count; buff_slot++) { + auto current_spell_id = buffs[buff_slot].spellid; + if ( + IsValidSpell(current_spell_id) && + spells[current_spell_id].disallow_sit + ) { + BuffFadeBySlot(buff_slot, false); + recalc_bonus = true; + } + } + + if (recalc_bonus) { + CalcBonuses(); + } +} + +void Mob::BuffFadeBySpellID(uint16 spell_id) +{ + bool recalc_bonus = false; + int buff_count = GetMaxTotalSlots(); + for (int buff_slot = 0; buff_slot < buff_count; buff_slot++) { + if (buffs[buff_slot].spellid == spell_id) { + BuffFadeBySlot(buff_slot, false); + recalc_bonus = true; + } + } + + if (recalc_bonus) { + CalcBonuses(); + } } void Mob::BuffFadeBySpellIDAndCaster(uint16 spell_id, uint16 caster_id) { bool recalc_bonus = false; auto buff_count = GetMaxTotalSlots(); - for (int i = 0; i < buff_count; ++i) { - if (buffs[i].spellid == spell_id && buffs[i].casterid == caster_id) { - BuffFadeBySlot(i, false); + for (int buff_slot = 0; buff_slot < buff_count; buff_slot++) { + if ( + buffs[buff_slot].spellid == spell_id && + buffs[buff_slot].casterid == caster_id + ) { + BuffFadeBySlot(buff_slot, false); recalc_bonus = true; } } - if (recalc_bonus) + if (recalc_bonus) { CalcBonuses(); + } } -// removes buffs containing effectid, skipping skipslot -void Mob::BuffFadeByEffect(int effect_id, int skipslot) +void Mob::BuffFadeByEffect(int effect_id, int slot_to_skip) { - int i; - + bool recalc_bonus = false; int buff_count = GetMaxTotalSlots(); - for(i = 0; i < buff_count; i++) - { - if(buffs[i].spellid == SPELL_UNKNOWN) - continue; - if(IsEffectInSpell(buffs[i].spellid, effect_id) && i != skipslot) - BuffFadeBySlot(i, false); + for(int buff_slot = 0; buff_slot < buff_count; buff_slot++) { + auto current_spell_id = buffs[buff_slot].spellid; + if ( + IsValidSpell(current_spell_id) && + IsEffectInSpell(current_spell_id, effect_id) && + buff_slot != slot_to_skip + ) { + BuffFadeBySlot(buff_slot, false); + recalc_bonus = true; + } } - //we tell BuffFadeBySlot not to recalc, so we can do it only once when were done - CalcBonuses(); + if (recalc_bonus) { + CalcBonuses(); + } } bool Mob::IsAffectedByBuff(uint16 spell_id) { - int buff_count = GetMaxTotalSlots(); - for (int i = 0; i < buff_count; ++i) - if (buffs[i].spellid == spell_id) - return true; - - return false; + return FindBuff(spell_id); } bool Mob::IsAffectedByBuffByGlobalGroup(GlobalGroup group) { int buff_count = GetMaxTotalSlots(); - for (int i = 0; i < buff_count; ++i) { - if (buffs[i].spellid == SPELL_UNKNOWN) - continue; - if (spells[buffs[i].spellid].spell_category == static_cast(group)) + for (int buff_slot = 0; buff_slot < buff_count; buff_slot++) { + auto current_spell_id = buffs[buff_slot].spellid; + if ( + IsValidSpell(current_spell_id) && + spells[current_spell_id].spell_category == static_cast(group) + ) { return true; + } } return false; From cece66adc6c8a730b9cc85685106f2439f889abf Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 21 Nov 2021 19:02:01 -0500 Subject: [PATCH 445/624] [Commands] Cleanup #instance Command. (#1803) * [Commands] Cleanup #instance Command. - Cleanups message and logic. - Cleanup ListAllInstances() method. - Fix day calculation in ConvertSecondsToTime(). * Cleanup. * Add return. --- common/string_util.cpp | 4 +- zone/gm_commands/instance.cpp | 408 +++++++++++++++++++++++----------- zone/zonedb.cpp | 146 ++++++++++-- zone/zonedb.h | 2 +- 4 files changed, 406 insertions(+), 154 deletions(-) diff --git a/common/string_util.cpp b/common/string_util.cpp index d8f5cab9b..4b53d32dc 100644 --- a/common/string_util.cpp +++ b/common/string_util.cpp @@ -1041,8 +1041,8 @@ std::string ConvertSecondsToTime(int duration, bool is_milliseconds) duration ); - int days = int(timer_length / 86400000); - timer_length %= 86400000; + int days = int(timer_length / 86400); + timer_length %= 86400; int hours = int(timer_length / 3600); timer_length %= 3600; int minutes = int(timer_length / 60); diff --git a/zone/gm_commands/instance.cpp b/zone/gm_commands/instance.cpp index 27c1171d7..f9bd357e3 100755 --- a/zone/gm_commands/instance.cpp +++ b/zone/gm_commands/instance.cpp @@ -2,187 +2,327 @@ void command_instance(Client *c, const Seperator *sep) { - if (!c) { + int arguments = sep->argnum; + if (!arguments) { + c->Message( + Chat::White, + "Usage: #instance create [Zone ID|Zone Short Name] [Version] [Duration]" + ); + c->Message( + Chat::White, + "Usage: #instance destroy [Instance ID]" + ); + c->Message( + Chat::White, + "Usage: #instance add [Instance ID] [Name]" + ); + c->Message( + Chat::White, + "Usage: #instance remove [Instance ID] [Name]" + ); + c->Message( + Chat::White, + "Usage: #instance list [Name]" + ); return; } - //options: - //help - //create [zone_id] [version] - //destroy [instance_id] - //add [instance_id] [player_name] - //remove [instance_id] [player_name] - //list [player_name] + Client* target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } - if (strcasecmp(sep->arg[1], "help") == 0) { - c->Message(Chat::White, "#instance usage:"); + bool is_add = !strcasecmp(sep->arg[1], "add"); + bool is_create = !strcasecmp(sep->arg[1], "create"); + bool is_destroy = !strcasecmp(sep->arg[1], "destroy"); + bool is_help = !strcasecmp(sep->arg[1], "help"); + bool is_list = !strcasecmp(sep->arg[1], "list"); + bool is_remove = !strcasecmp(sep->arg[1], "remove"); + if ( + !is_add && + !is_create && + !is_destroy && + !is_help && + !is_list && + !is_remove + ) { c->Message( - Chat::White, "#instance create zone_id version duration - Creates an instance of version 'version' in the " - "zone with id matching zone_id, will last for duration seconds." - ); - c->Message(Chat::White, "#instance destroy instance_id - Destroys the instance with id matching instance_id."); - c->Message( - Chat::White, "#instance add instance_id player_name - adds the player 'player_name' to the instance " - "with id matching instance_id." + Chat::White, + "Usage: #instance create [Zone ID|Zone Short Name] [Version] [Duration]" ); c->Message( - Chat::White, "#instance remove instance_id player_name - removes the player 'player_name' from the " - "instance with id matching instance_id." + Chat::White, + "Usage: #instance destroy [Instance ID]" + ); + c->Message( + Chat::White, + "Usage: #instance add [Instance ID] [Name]" + ); + c->Message( + Chat::White, + "Usage: #instance remove [Instance ID] [Name]" + ); + c->Message( + Chat::White, + "Usage: #instance list [Name]" ); - c->Message(Chat::White, "#instance list player_name - lists all the instances 'player_name' is apart of."); return; } - else if (strcasecmp(sep->arg[1], "create") == 0) { - if (!sep->IsNumber(3) || !sep->IsNumber(4)) { + + if (is_add) { + if (!sep->IsNumber(2)) { c->Message( Chat::White, - "#instance create zone_id version duration - Creates an instance of version 'version' in the " - "zone with id matching zone_id, will last for duration seconds." + "#instance add [Instance ID] [Name]" ); return; } - const char *zn = nullptr; - uint32 zone_id = 0; - - if (sep->IsNumber(2)) { - zone_id = atoi(sep->arg[2]); - } - else { - zone_id = ZoneID(sep->arg[2]); - } - - uint32 version = atoi(sep->arg[3]); - uint32 duration = atoi(sep->arg[4]); - zn = ZoneName(zone_id); - - if (!zn) { - c->Message(Chat::White, "Zone with id %lu was not found by the server.", (unsigned long) zone_id); + std::string character_name = sep->arg[3]; + uint16 instance_id = static_cast(std::stoul(sep->arg[2])); + uint32 character_id = database.GetCharacterID(character_name.c_str()); + if (instance_id <= 0 || character_id <= 0) { + c->Message(Chat::White, "You must enter a valid Instance ID and player name."); return; } - uint16 id = 0; - if (!database.GetUnusedInstanceID(id)) { + if (!database.CheckInstanceExists(instance_id)) { + c->Message( + Chat::White, + fmt::format( + "Instance ID {} does not exist.", + instance_id + ).c_str() + ); + return; + } + + uint32 zone_id = database.ZoneIDFromInstanceID(instance_id); + uint32 version = database.VersionFromInstanceID(instance_id); + uint32 current_id = database.GetInstanceID(zone_id, character_id, version); + if (!current_id) { + std::string target_string = ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + character_name, + character_id + ) + ); + + c->Message( + Chat::White, + ( + database.AddClientToInstance(instance_id, character_id) ? + fmt::format( + "Added {} to Instance ID {}.", + target_string, + instance_id + ) : + fmt::format( + "Failed to add {} to Instance ID {}.", + target_string, + instance_id + ) + ).c_str() + ); + } + else { + c->Message( + Chat::White, + fmt::format( + "Client was already saved to Instance ID {}{}.", + current_id, + ( + current_id != instance_id ? + fmt::format( + "which has the same zone and version as Instance ID {}", + instance_id + ) : + "" + ) + ).c_str() + ); + } + } else if (is_create) { + if (!sep->IsNumber(3) || !sep->IsNumber(4)) { + c->Message( + Chat::White, + "Usage: #instance create [Zone ID|Zone Short Name] [Version] [Duration]" + ); + return; + } + + uint32 zone_id = ( + sep->IsNumber(2) ? + std::stoul(sep->arg[2]) : + ZoneID(sep->arg[2]) + ); + uint32 version = std::stoul(sep->arg[3]); + uint32 duration = std::stoul(sep->arg[4]); + std::string zone_short_name = ZoneName(zone_id); + if (zone_short_name.empty()) { + c->Message( + Chat::White, + fmt::format( + "Zone ID {} was not found by the server.", + zone_id + ).c_str() + ); + return; + } + + uint16 instance_id = 0; + if (!database.GetUnusedInstanceID(instance_id)) { c->Message(Chat::White, "Server was unable to find a free instance id."); return; } - if (!database.CreateInstance(id, zone_id, version, duration)) { + if (!database.CreateInstance(instance_id, zone_id, version, duration)) { c->Message(Chat::White, "Server was unable to create a new instance."); return; } - c->Message(Chat::White, "New instance %s was created with id %lu.", zn, (unsigned long) id); - } - else if (strcasecmp(sep->arg[1], "destroy") == 0) { + c->Message( + Chat::White, + fmt::format( + "Instance {} Created | Zone: {} ({}){}", + instance_id, + ZoneLongName(zone_id), + zone_id, + ( + version ? + fmt::format( + " Version: {}", + version + ) : + "" + ) + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Instance {} Created | Duration: {} ({})", + instance_id, + ConvertSecondsToTime(duration), + duration + ).c_str() + ); + } else if (is_destroy) { if (!sep->IsNumber(2)) { c->Message( Chat::White, - "#instance destroy instance_id - Destroys the instance with id matching instance_id." + "#instance destroy [Instance ID]" ); return; } - uint16 id = atoi(sep->arg[2]); - database.DeleteInstance(id); - c->Message(Chat::White, "Destroyed instance with id %lu.", (unsigned long) id); - } - else if (strcasecmp(sep->arg[1], "add") == 0) { - if (!sep->IsNumber(2)) { - c->Message( - Chat::White, "#instance add instance_id player_name - adds the player 'player_name' to the instance " - "with id matching instance_id." - ); - return; - } - - uint16 id = atoi(sep->arg[2]); - uint32 charid = database.GetCharacterID(sep->arg[3]); - - if (id <= 0 || charid <= 0) { - c->Message(Chat::White, "Must enter a valid instance id and player name."); - return; - } - - if (!database.CheckInstanceExists(id)) { - c->Message(Chat::White, "Instance does not exist."); - return; - } - - uint32 zone_id = database.ZoneIDFromInstanceID(id); - uint32 version = database.VersionFromInstanceID(id); - uint32 cur_id = database.GetInstanceID(zone_id, charid, version); - if (cur_id == 0) { - if (database.AddClientToInstance(id, charid)) { - c->Message(Chat::White, "Added client to instance."); - } - else { - c->Message(Chat::White, "Failed to add client to instance."); - } - } - else { + uint16 instance_id = std::stoul(sep->arg[2]); + if (!database.CheckInstanceExists(instance_id)) { c->Message( Chat::White, - "Client was already saved to %u which has uses the same zone and version as that instance.", - cur_id - ); - } - } - else if (strcasecmp(sep->arg[1], "remove") == 0) { - if (!sep->IsNumber(2)) { - c->Message( - Chat::White, "#instance remove instance_id player_name - removes the player 'player_name' from the " - "instance with id matching instance_id." + fmt::format( + "Instance ID {} does not exist.", + instance_id + ).c_str() ); return; } - uint16 id = atoi(sep->arg[2]); - uint32 charid = database.GetCharacterID(sep->arg[3]); - - if (id <= 0 || charid <= 0) { - c->Message(Chat::White, "Must enter a valid instance id and player name."); - } - - if (database.RemoveClientFromInstance(id, charid)) { - c->Message(Chat::White, "Removed client from instance."); - } - else { - c->Message(Chat::White, "Failed to remove client from instance."); - } - } - else if (strcasecmp(sep->arg[1], "list") == 0) { - uint32 charid = database.GetCharacterID(sep->arg[2]); - if (charid <= 0) { - if (c->GetTarget() == nullptr || (c->GetTarget() && !c->GetTarget()->IsClient())) { - c->Message(Chat::White, "Character not found."); - return; - } - else { - charid = c->GetTarget()->CastToClient()->CharacterID(); - } - } - - database.ListAllInstances(c, charid); - } - else { - c->Message(Chat::White, "Invalid Argument."); - c->Message(Chat::White, "#instance usage:"); + database.DeleteInstance(instance_id); c->Message( - Chat::White, "#instance create zone_id version duration - Creates an instance of version 'version' in the " - "zone with id matching zone_id, will last for duration seconds." + Chat::White, + fmt::format( + "Instance ID {} Deleted.", + instance_id + ).c_str() ); - c->Message(Chat::White, "#instance destroy instance_id - Destroys the instance with id matching instance_id."); + } else if (is_help) { c->Message( - Chat::White, "#instance add instance_id player_name - adds the player 'player_name' to the instance " - "with id matching instance_id." + Chat::White, + "Usage: #instance create [Zone ID|Zone Short Name] [Version] [Duration]" ); c->Message( - Chat::White, "#instance remove instance_id player_name - removes the player 'player_name' from the " - "instance with id matching instance_id." + Chat::White, + "Usage: #instance destroy [Instance ID]" + ); + c->Message( + Chat::White, + "Usage: #instance add [Instance ID] [Name]" + ); + c->Message( + Chat::White, + "Usage: #instance remove [Instance ID] [Name]" + ); + c->Message( + Chat::White, + "Usage: #instance list [Name]" ); - c->Message(Chat::White, "#instance list player_name - lists all the instances 'player_name' is apart of."); return; + } else if (is_list) { + uint32 character_id = database.GetCharacterID(sep->arg[2]); + if (character_id <= 0) { + character_id = target->CharacterID(); + } + + database.ListAllInstances(c, character_id); + } else if (is_remove) { + if (!sep->IsNumber(2)) { + c->Message( + Chat::White, + "#instance remove [Instance ID] [Name]" + ); + return; + } + + std::string character_name = sep->arg[3]; + uint16 instance_id = static_cast(std::stoul(sep->arg[2])); + uint32 character_id = database.GetCharacterID(character_name.c_str()); + if (instance_id <= 0 || character_id <= 0) { + c->Message(Chat::White, "You must enter a valid Instance ID and player name."); + return; + } + + if (!database.CheckInstanceExists(instance_id)) { + c->Message( + Chat::White, + fmt::format( + "Instance ID {} does not exist.", + instance_id + ).c_str() + ); + return; + } + + std::string target_string = ( + c->CharacterID() == character_id ? + "yourself" : + fmt::format( + "{} ({})", + character_name, + character_id + ) + ); + + c->Message( + Chat::White, + ( + database.RemoveClientFromInstance(instance_id, character_id) ? + fmt::format( + "Removed {} from Instance ID {}.", + target_string, + instance_id + ) : + fmt::format( + "Failed to remove {} from Instance ID {}.", + target_string, + instance_id + ) + ).c_str() + ); } } diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index f382c300e..c4b6363a0 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -3491,28 +3491,140 @@ void ZoneDatabase::UpdateKarma(uint32 acct_id, uint32 amount) QueryDatabase(query); } -void ZoneDatabase::ListAllInstances(Client* client, uint32 charid) +void ZoneDatabase::ListAllInstances(Client* client, uint32 character_id) { - if(!client) + if (!client) { return; + } - std::string query = StringFormat("SELECT instance_list.id, zone, version " - "FROM instance_list JOIN instance_list_player " - "ON instance_list.id = instance_list_player.id " - "WHERE instance_list_player.charid = %lu", - (unsigned long)charid); - auto results = QueryDatabase(query); - if (!results.Success()) - return; + std::string query = fmt::format( + "SELECT instance_list.id, zone, version, start_time, duration, never_expires " + "FROM instance_list JOIN instance_list_player " + "ON instance_list.id = instance_list_player.id " + "WHERE instance_list_player.charid = {}", + character_id + ); + auto results = QueryDatabase(query); + if (!results.Success()) { + return; + } - char name[64]; - database.GetCharName(charid, name); - client->Message(Chat::White, "%s is part of the following instances:", name); + auto character_name = database.GetCharNameByID(character_id); + bool is_same_client = client->CharacterID() == character_id; + if (character_name.empty()) { + client->Message( + Chat::White, + fmt::format( + "Character ID '{}' does not exist.", + character_id + ).c_str() + ); + return; + } - for (auto row = results.begin(); row != results.end(); ++row) { - client->Message(Chat::White, "%s - id: %lu, version: %lu", ZoneName(atoi(row[1])), - (unsigned long)atoi(row[0]), (unsigned long)atoi(row[2])); - } + if (!results.RowCount()) { + client->Message( + Chat::White, + fmt::format( + "{} not in any Instances.", + ( + is_same_client ? + "You are" : + fmt::format( + "{} ({}) is", + character_name, + character_id + ) + ) + ).c_str() + ); + return; + } + + client->Message( + Chat::White, + fmt::format( + "{} in the following Instances.", + ( + is_same_client ? + "You are" : + fmt::format( + "{} ({}) is", + character_name, + character_id + ) + ) + ).c_str() + ); + + uint32 instance_count = 0; + for (auto row : results) { + auto instance_id = std::stoul(row[0]); + auto zone_id = std::stoul(row[1]); + auto version = std::stoul(row[2]); + auto start_time = std::stoul(row[3]); + auto duration = std::stoul(row[4]); + auto never_expires = std::stoi(row[5]) ? true : false; + std::string remaining_time_string = "Never"; + timeval time_value; + gettimeofday(&time_value, nullptr); + auto current_time = time_value.tv_sec; + auto remaining_time = ((start_time + duration) - current_time); + if (!never_expires) { + if (remaining_time > 0) { + remaining_time_string = ConvertSecondsToTime(remaining_time); + } else { + remaining_time_string = "Already Expired"; + } + } + + client->Message( + Chat::White, + fmt::format("Instance {} | Zone: {} ({}){}", + instance_id, + ZoneLongName(zone_id), + zone_id, + ( + version ? + fmt::format( + " Version: {}", + version + ) : + "" + ) + ).c_str() + ); + + client->Message( + Chat::White, + fmt::format( + "Instance {} | Expires: {}", + instance_id, + remaining_time_string, + remaining_time + ).c_str() + ); + + instance_count++; + } + + client->Message( + Chat::White, + fmt::format( + "{} in {} Instance{}.", + ( + is_same_client ? + "You are" : + fmt::format( + "{} ({}) is", + character_name, + character_id + ) + ), + instance_count, + instance_count != 1 ? "s" : "" + ).c_str() + ); } void ZoneDatabase::QGlobalPurge() diff --git a/zone/zonedb.h b/zone/zonedb.h index 2a35d5632..d019d6fc4 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -576,7 +576,7 @@ public: uint8 RaidGroupCount(uint32 raidid, uint32 groupid); /* Instancing */ - void ListAllInstances(Client* c, uint32 charid); + void ListAllInstances(Client* c, uint32 character_id); /* QGlobals */ void QGlobalPurge(); From 26c728799731c711f7c461d31f985f93c26b72da Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Mon, 22 Nov 2021 21:16:42 -0500 Subject: [PATCH 446/624] [Commands] Cleanup #zunderworld Command. (#1809) * [Commands] Cleanup #zunderworld Command. - Cleanup message and logic. - Add parameter to allow data to be saved to database. * Update zunderworld.cpp --- zone/command.cpp | 2 +- zone/gm_commands/zunderworld.cpp | 39 +++++++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 5742da04f..2ea3a98d5 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -405,7 +405,7 @@ int command_init(void) command_add("zsave", " - Saves zheader to the database", AccountStatus::QuestTroupe, command_zsave) || command_add("zsky", "[skytype] - Change zone sky type", AccountStatus::QuestTroupe, command_zsky) || command_add("zstats", "- Show info about zone header", AccountStatus::QuestTroupe, command_zstats) || - command_add("zunderworld", "[zcoord] - Sets the underworld using zcoord", AccountStatus::QuestTroupe, command_zunderworld) || + command_add("zunderworld", "[Z] [Permanent (0 = False, 1 = True)] - Change zone underworld Z", AccountStatus::QuestTroupe, command_zunderworld) || command_add("zuwcoords", "[z coord] - Set underworld coord", AccountStatus::QuestTroupe, command_zuwcoords) ) { command_deinit(); diff --git a/zone/gm_commands/zunderworld.cpp b/zone/gm_commands/zunderworld.cpp index 0ba52f156..88499dc2e 100755 --- a/zone/gm_commands/zunderworld.cpp +++ b/zone/gm_commands/zunderworld.cpp @@ -2,11 +2,38 @@ void command_zunderworld(Client *c, const Seperator *sep) { - if (sep->arg[1][0] == 0) { - c->Message(Chat::White, "Usage: #zunderworld "); + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #zunderworld [Z] [Permanent (0 = False, 1 = True)]"); + return; } - else { - zone->newzone_data.underworld = atof(sep->arg[1]); - } -} + auto z = std::stof(sep->arg[1]); + auto permanent = sep->arg[2] ? atobool(sep->arg[2]) : false; + if (permanent) { + auto query = fmt::format( + "UPDATE zone SET underworld = {:.2f} WHERE zoneidnumber = {} AND version = {}", + z, + zone->GetZoneID(), + zone->GetInstanceVersion() + ); + database.QueryDatabase(query); + } + + zone->newzone_data.underworld = z; + auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); + memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + + c->Message( + Chat::White, + fmt::format( + "Underworld Z Changed | Zone: {} ({}) Z: {:.2f} Permanent: {}", + zone->GetLongName(), + zone->GetZoneID(), + z, + permanent ? "Yes" : "No" + ).c_str() + ); +} From 0da4610249f298d4906efcdff229df6d5684db52 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Mon, 22 Nov 2021 21:17:03 -0500 Subject: [PATCH 447/624] [Commands] Cleanup #zsave Command. (#1807) * [Commands] Cleanup #zsave Command. - Cleanup message and logic. * White. --- zone/gm_commands/zsave.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/zone/gm_commands/zsave.cpp b/zone/gm_commands/zsave.cpp index 8c1cf85dc..acfa4536d 100755 --- a/zone/gm_commands/zsave.cpp +++ b/zone/gm_commands/zsave.cpp @@ -2,11 +2,15 @@ void command_zsave(Client *c, const Seperator *sep) { - if (zone->SaveZoneCFG()) { - c->Message(Chat::Red, "Zone header saved successfully."); - } - else { - c->Message(Chat::Red, "ERROR: Zone header data was NOT saved."); - } + c->Message( + Chat::White, + fmt::format( + "Zone header {}.", + ( + zone->SaveZoneCFG() ? + "saved successfully" : + "failed to save" + ) + ).c_str() + ); } - From 9240497cbcf11f0e85983e0098c75b33e94f3a7c Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Mon, 22 Nov 2021 21:17:13 -0500 Subject: [PATCH 448/624] [Commands] Cleanup #zsky Command. (#1808) - Cleanup message and logic. - Add parameter to allow data to be saved to database. --- zone/command.cpp | 2 +- zone/gm_commands/zsky.cpp | 52 ++++++++++++++++++++++++++++----------- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 2ea3a98d5..febedd00c 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -403,7 +403,7 @@ int command_init(void) command_add("zopp", "Troubleshooting command - Sends a fake item packet to you. No server reference is created.", AccountStatus::GMImpossible, command_zopp) || command_add("zsafecoords", "[x] [y] [z] - Set safe coords", AccountStatus::QuestTroupe, command_zsafecoords) || command_add("zsave", " - Saves zheader to the database", AccountStatus::QuestTroupe, command_zsave) || - command_add("zsky", "[skytype] - Change zone sky type", AccountStatus::QuestTroupe, command_zsky) || + command_add("zsky", "[Sky Type] [Permanent (0 = False, 1 = True)] - Change zone sky type", AccountStatus::QuestTroupe, command_zsky) || command_add("zstats", "- Show info about zone header", AccountStatus::QuestTroupe, command_zstats) || command_add("zunderworld", "[Z] [Permanent (0 = False, 1 = True)] - Change zone underworld Z", AccountStatus::QuestTroupe, command_zunderworld) || command_add("zuwcoords", "[z coord] - Set underworld coord", AccountStatus::QuestTroupe, command_zuwcoords) diff --git a/zone/gm_commands/zsky.cpp b/zone/gm_commands/zsky.cpp index 8a3dc2c7f..d43b2f152 100755 --- a/zone/gm_commands/zsky.cpp +++ b/zone/gm_commands/zsky.cpp @@ -2,19 +2,43 @@ void command_zsky(Client *c, const Seperator *sep) { - // modifys and resends zhdr packet - if (sep->arg[1][0] == 0) { - c->Message(Chat::White, "Usage: #zsky "); + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #zsky [Sky Type] [Permanent (0 = False, 1 = True)]"); + return; } - else if (atoi(sep->arg[1]) < 0 || atoi(sep->arg[1]) > 255) { - c->Message(Chat::White, "ERROR: Sky type can not be less than 0 or greater than 255!"); - } - else { - zone->newzone_data.sky = atoi(sep->arg[1]); - auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); - memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } -} + auto sky_type = std::stoul(sep->arg[1]); + auto permanent = sep->arg[2] ? atobool(sep->arg[2]) : false; + if (sky_type < 0 || sky_type > 255) { + c->Message(Chat::White, "Sky Type cannot be less than 0 or greater than 255!"); + return; + } + + if (permanent) { + auto query = fmt::format( + "UPDATE zone SET sky = {} WHERE zoneidnumber = {} AND version = {}", + sky_type, + zone->GetZoneID(), + zone->GetInstanceVersion() + ); + database.QueryDatabase(query); + } + + zone->newzone_data.sky = static_cast(sky_type); + auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); + memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + + c->Message( + Chat::White, + fmt::format( + "Sky Changed | Zone: {} ({}) Sky Type: {} Permanent: {}", + zone->GetLongName(), + zone->GetZoneID(), + sky_type, + permanent ? "Yes" : "No" + ).c_str() + ); +} From 8c7e1be344123c7ac06a198799b6b3917f245b5c Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Mon, 22 Nov 2021 21:18:04 -0500 Subject: [PATCH 449/624] [Commands] Cleanup #givemoney Command. (#1804) - Cleanup money message to use new helper method. --- zone/gm_commands/givemoney.cpp | 74 +--------------------------------- 1 file changed, 2 insertions(+), 72 deletions(-) diff --git a/zone/gm_commands/givemoney.cpp b/zone/gm_commands/givemoney.cpp index ab71df4ce..4c3076f9e 100755 --- a/zone/gm_commands/givemoney.cpp +++ b/zone/gm_commands/givemoney.cpp @@ -30,82 +30,12 @@ void command_givemoney(Client *c, const Seperator *sep) platinum, true ); - std::string money_string; - if (copper && silver && gold && platinum) { // CSGP - money_string = fmt::format( - "{} Platinum, {} Gold, {} Silver, and {} Copper", - platinum, - gold, - silver, - copper - ); - } else if (copper && silver && gold && !platinum) { // CSG - money_string = fmt::format( - "{} Gold, {} Silver, and {} Copper", - gold, - silver, - copper - ); - } else if (copper && silver && !gold && !platinum) { // CS - money_string = fmt::format( - "{} Silver and {} Copper", - silver, - copper - ); - } else if (copper && !silver && !gold && !platinum) { // C - money_string = fmt::format( - "{} Copper", - copper - ); - } else if (!copper && silver && gold && platinum) { // SGP - money_string = fmt::format( - "{} Platinum, {} Gold, and {} Silver", - platinum, - gold, - silver - ); - } else if (!copper && silver && gold && !platinum) { // SG - money_string = fmt::format( - "{} Gold and {} Silver", - gold, - silver - ); - } else if (!copper && silver && !gold && !platinum) { // S - money_string = fmt::format( - "{} Silver", - silver - ); - } else if (copper && !silver && gold && platinum) { // CGP - money_string = fmt::format( - "{} Platinum, {} Gold, and {} Copper", - platinum, - gold, - copper - ); - } else if (copper && !silver && gold && !platinum) { // CG - money_string = fmt::format( - "{} Gold and {} Copper", - gold, - copper - ); - } else if (!copper && !silver && gold && platinum) { // GP - money_string = fmt::format( - "{} Platinum and {} Gold", - platinum, - gold - ); - } else if (!copper && !silver && gold && !platinum) { // G - money_string = fmt::format( - "{} Gold", - gold - ); - } - + c->Message( Chat::White, fmt::format( "Added {} to {}.", - money_string, + ConvertMoneyToString(platinum, gold, silver, copper), c == target ? "yourself" : target->GetCleanName() ).c_str() ); From 1935ea60d05319b6a6af769de127d847a40d9965 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 23 Nov 2021 05:48:39 -0500 Subject: [PATCH 450/624] [Commands] Remove #zonespawn Command. (#1811) * [Commands] Remove #zonespawn Command. - Remove unimplemented command. * Remove from CMakeLists.txt. * Fix. --- zone/CMakeLists.txt | 1 - zone/command.cpp | 1 - zone/command.h | 1 - zone/gm_commands/zonespawn.cpp | 40 ---------------------------------- 4 files changed, 43 deletions(-) delete mode 100755 zone/gm_commands/zonespawn.cpp diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 46c235455..2eb9749f1 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -559,7 +559,6 @@ SET(gm_commands gm_commands/zonebootup.cpp gm_commands/zonelock.cpp gm_commands/zoneshutdown.cpp - gm_commands/zonespawn.cpp gm_commands/zonestatus.cpp gm_commands/zopp.cpp gm_commands/zsafecoords.cpp diff --git a/zone/command.cpp b/zone/command.cpp index febedd00c..d9060227d 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -398,7 +398,6 @@ int command_init(void) command_add("zoneinstance", "[instanceid] [x] [y] [z] - Go to specified instance zone (coords optional)", AccountStatus::Guide, command_zone_instance) || command_add("zonelock", "[List|Lock|Unlock] [Zone ID|Zone Short Name] - Set or get lock status of a Zone by ID or Short Name", AccountStatus::GMAdmin, command_zonelock) || command_add("zoneshutdown", "[shortname] - Shut down a zone server", AccountStatus::GMLeadAdmin, command_zoneshutdown) || - command_add("zonespawn", "- Not implemented", AccountStatus::GMImpossible, command_zonespawn) || command_add("zonestatus", "- Show connected zoneservers, synonymous with /servers", AccountStatus::GMLeadAdmin, command_zonestatus) || command_add("zopp", "Troubleshooting command - Sends a fake item packet to you. No server reference is created.", AccountStatus::GMImpossible, command_zopp) || command_add("zsafecoords", "[x] [y] [z] - Set safe coords", AccountStatus::QuestTroupe, command_zsafecoords) || diff --git a/zone/command.h b/zone/command.h index 4b5f99b2d..6d5f9e247 100644 --- a/zone/command.h +++ b/zone/command.h @@ -321,7 +321,6 @@ void command_zonebootup(Client *c, const Seperator *sep); void command_zonelock(Client *c, const Seperator *sep); void command_viewzoneloot(Client *c, const Seperator *sep); void command_zoneshutdown(Client *c, const Seperator *sep); -void command_zonespawn(Client *c, const Seperator *sep); void command_zonestatus(Client *c, const Seperator *sep); void command_zopp(Client *c, const Seperator *sep); void command_zsafecoords(Client *c, const Seperator *sep); diff --git a/zone/gm_commands/zonespawn.cpp b/zone/gm_commands/zonespawn.cpp deleted file mode 100755 index 01a3a8683..000000000 --- a/zone/gm_commands/zonespawn.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "../client.h" - -void command_zonespawn(Client *c, const Seperator *sep) -{ - c->Message(Chat::White, "This command is not yet implemented."); - return; - -/* this was kept from client.cpp verbatim (it was commented out) */ - // if (target && target->IsNPC()) { - // Message(0, "Inside main if."); - // if (strcasecmp(sep->arg[1], "add")==0) { - // Message(0, "Inside add if."); - // database.DBSpawn(1, StaticGetZoneName(this->GetPP().current_zone), target->CastToNPC()); - // } - // else if (strcasecmp(sep->arg[1], "update")==0) { - // database.DBSpawn(2, StaticGetZoneName(this->GetPP().current_zone), target->CastToNPC()); - // } - // else if (strcasecmp(sep->arg[1], "remove")==0) { - // if (strcasecmp(sep->arg[2], "all")==0) { - // database.DBSpawn(4, StaticGetZoneName(this->GetPP().current_zone)); - // } - // else { - // if (database.DBSpawn(3, StaticGetZoneName(this->GetPP().current_zone), target->CastToNPC())) { - // Message(0, "#zonespawn: %s removed successfully!", target->GetName()); - // target->CastToNPC()->Death(target, target->GetHP()); - // } - // } - // } - // else - // Message(0, "Error: #dbspawn: Invalid command. (Note: EDIT and REMOVE are NOT in yet.)"); - // if (target->CastToNPC()->GetNPCTypeID() > 0) { - // Message(0, "Spawn is type %i", target->CastToNPC()->GetNPCTypeID()); - // } - // } - // else if(!target || !target->IsNPC()) - // Message(0, "Error: #zonespawn: You must have a NPC targeted!"); - // else - // Message(0, "Usage: #zonespawn [add|edit|remove|remove all]"); -} - From ef06a0d0b6bf8e89ae3f0a6f875114a245439824 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 23 Nov 2021 05:48:47 -0500 Subject: [PATCH 451/624] [Commands] Cleanup #zclip Command. (#1812) * [Commands] Cleanup #zclip Command. - Cleanup message and logic. - Add parameter to allow data to be saved to database. * Cleanup. --- zone/command.cpp | 2 +- zone/gm_commands/zclip.cpp | 119 +++++++++++++++++++++++++++++-------- 2 files changed, 95 insertions(+), 26 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index d9060227d..a53c5f070 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -390,7 +390,7 @@ int command_init(void) command_add("wpinfo", "- Show waypoint info about your NPC target", AccountStatus::GMAreas, command_wpinfo) || command_add("worldwide", "Performs world-wide GM functions such as cast (can be extended for other commands). Use caution", AccountStatus::GMImpossible, command_worldwide) || command_add("xtargets", "Show your targets Extended Targets and optionally set how many xtargets they can have.", AccountStatus::GMImpossible, command_xtargets) || - command_add("zclip", "[min] [max] - modifies and resends zhdr packet", AccountStatus::QuestTroupe, command_zclip) || + command_add("zclip", "[Minimum Clip] [Maximum Clip] [Fog Minimum Clip] [Fog Maximum Clip] [Permanent (0 = False, 1 = True)] - Change zone clipping", AccountStatus::QuestTroupe, command_zclip) || command_add("zcolor", "[red] [green] [blue] - Change sky color", AccountStatus::QuestTroupe, command_zcolor) || command_add("zheader", "[zonename] - Load zheader for zonename from the database", AccountStatus::QuestTroupe, command_zheader) || command_add("zone", "[zonename] [x] [y] [z] - Go to specified zone (coords optional)", AccountStatus::Guide, command_zone) || diff --git a/zone/gm_commands/zclip.cpp b/zone/gm_commands/zclip.cpp index 4f825c745..f8252885f 100755 --- a/zone/gm_commands/zclip.cpp +++ b/zone/gm_commands/zclip.cpp @@ -2,38 +2,107 @@ void command_zclip(Client *c, const Seperator *sep) { - // modifys and resends zhdr packet - if (sep->arg[2][0] == 0) { - c->Message(Chat::White, "Usage: #zclip "); + int arguments = sep->argnum; + if ( + !arguments || + !sep->IsNumber(1) || + !sep->IsNumber(2) + ) { + c->Message(Chat::White, "Usage: #zclip [Minimum Clip] [Maximum Clip] [Fog Minimum Clip] [Fog Maximum Clip] [Permanent (0 = False, 1 = True)]"); } - else if (atoi(sep->arg[1]) <= 0) { - c->Message(Chat::White, "ERROR: Min clip can not be zero or less!"); - } - else if (atoi(sep->arg[2]) <= 0) { - c->Message(Chat::White, "ERROR: Max clip can not be zero or less!"); - } - else if (atoi(sep->arg[1]) > atoi(sep->arg[2])) { - c->Message(Chat::White, "ERROR: Min clip is greater than max clip!"); - } - else { - zone->newzone_data.minclip = atof(sep->arg[1]); - zone->newzone_data.maxclip = atof(sep->arg[2]); - if (sep->arg[3][0] != 0) { - zone->newzone_data.fog_minclip[0] = atof(sep->arg[3]); + + auto minimum_clip = std::stof(sep->arg[1]); + auto maximum_clip = std::stof(sep->arg[2]); + auto minimum_fog_clip = sep->arg[3] ? std::stof(sep->arg[3]) : 0; + auto maximum_fog_clip = sep->arg[4] ? std::stof(sep->arg[4]) : 0; + auto permanent = sep->arg[5] ? atobool(sep->arg[5]) : false; + if (minimum_clip <= 0 || maximum_clip <= 0) { + c->Message(Chat::White, "Minimum Clip and Maximum Clip must be greater than 0."); + return; + } else if (minimum_clip > maximum_clip) { + c->Message(Chat::White, "Minimum Clip must be less than or equal to Maximum Clip!"); + return; + } else { + zone->newzone_data.minclip = minimum_clip; + zone->newzone_data.maxclip = maximum_clip; + + if (minimum_fog_clip) { + for (int fog_index = 0; fog_index < 4; fog_index++) { + zone->newzone_data.fog_minclip[fog_index] = minimum_fog_clip; + } } - if (sep->arg[4][0] != 0) { - zone->newzone_data.fog_minclip[1] = atof(sep->arg[4]); + + if (maximum_fog_clip) { + for (int fog_index = 0; fog_index < 4; fog_index++) { + zone->newzone_data.fog_maxclip[fog_index] = maximum_fog_clip; + } } - if (sep->arg[5][0] != 0) { - zone->newzone_data.fog_maxclip[0] = atof(sep->arg[5]); + + if (permanent) { + auto query = fmt::format( + "UPDATE zone SET minclip = {:.2f}, maxclip = {:.2f} WHERE zoneidnumber = {} AND version = {}", + minimum_clip, + maximum_clip, + zone->GetZoneID(), + zone->GetInstanceVersion() + ); + database.QueryDatabase(query); + + if (minimum_fog_clip) { + query = fmt::format( + "UPDATE zone SET fog_minclip = {:.2f} WHERE zoneidnumber = {} AND version = {}", + minimum_fog_clip, + zone->GetZoneID(), + zone->GetInstanceVersion() + ); + database.QueryDatabase(query); + } + + if (maximum_fog_clip) { + query = fmt::format( + "UPDATE zone SET fog_maxclip = {:.2f} WHERE zoneidnumber = {} AND version = {}", + maximum_fog_clip, + zone->GetZoneID(), + zone->GetInstanceVersion() + ); + database.QueryDatabase(query); + } } - if (sep->arg[6][0] != 0) { - zone->newzone_data.fog_maxclip[1] = atof(sep->arg[6]); - } - auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); + + auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); entity_list.QueueClients(c, outapp); safe_delete(outapp); + + c->Message( + Chat::White, + fmt::format( + "Clipping Changed | Zone: {} ({}) Permanent: {}", + zone->GetLongName(), + zone->GetZoneID(), + permanent ? "Yes" : "No" + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Clipping Changed | Minimum Clip: {:.2f} Maximum Clip: {:.2f}", + minimum_clip, + maximum_clip + ).c_str() + ); + + if (minimum_fog_clip || maximum_fog_clip) { + c->Message( + Chat::White, + fmt::format( + "Clipping Changed | Fog Minimum Clip: {:.2f} Fog Maximum Clip: {:.2f}", + minimum_fog_clip, + maximum_fog_clip + ).c_str() + ); + } } } From 2cbcefd9a006beeeb78649f7b209c79f2664a10d Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 23 Nov 2021 05:49:11 -0500 Subject: [PATCH 452/624] [Commands] Cleanup #zheader Command. (#1814) * [Commands] Cleanup #zheader Command. - Cleanup message and logic. - Add parameter to allow versions to be loaded. - Cleanup parameter name in CFG methods from instance_id to instance_version. * Update zonedb.cpp --- zone/command.cpp | 2 +- zone/gm_commands/zheader.cpp | 71 +++++++++++++++++++++++++++--------- zone/zone.cpp | 8 ++-- zone/zone.h | 2 +- zone/zonedb.cpp | 49 +++++++++++++++---------- zone/zonedb.h | 4 +- 6 files changed, 91 insertions(+), 45 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index a53c5f070..d46e36544 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -392,7 +392,7 @@ int command_init(void) command_add("xtargets", "Show your targets Extended Targets and optionally set how many xtargets they can have.", AccountStatus::GMImpossible, command_xtargets) || command_add("zclip", "[Minimum Clip] [Maximum Clip] [Fog Minimum Clip] [Fog Maximum Clip] [Permanent (0 = False, 1 = True)] - Change zone clipping", AccountStatus::QuestTroupe, command_zclip) || command_add("zcolor", "[red] [green] [blue] - Change sky color", AccountStatus::QuestTroupe, command_zcolor) || - command_add("zheader", "[zonename] - Load zheader for zonename from the database", AccountStatus::QuestTroupe, command_zheader) || + command_add("zheader", "[Zone ID|Zone Short Name] [Version] - Load a zone header from the database", AccountStatus::QuestTroupe, command_zheader) || command_add("zone", "[zonename] [x] [y] [z] - Go to specified zone (coords optional)", AccountStatus::Guide, command_zone) || command_add("zonebootup", "[ZoneServerID] [shortname] - Make a zone server boot a specific zone", AccountStatus::GMLeadAdmin, command_zonebootup) || command_add("zoneinstance", "[instanceid] [x] [y] [z] - Go to specified instance zone (coords optional)", AccountStatus::Guide, command_zone_instance) || diff --git a/zone/gm_commands/zheader.cpp b/zone/gm_commands/zheader.cpp index 788140dbe..f04672e63 100755 --- a/zone/gm_commands/zheader.cpp +++ b/zone/gm_commands/zheader.cpp @@ -2,25 +2,60 @@ void command_zheader(Client *c, const Seperator *sep) { - // sends zhdr packet - if (sep->arg[1][0] == 0) { - c->Message(Chat::White, "Usage: #zheader "); + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #zheader [Zone ID|Zone Short Name] [Version]"); + return; } - else if (ZoneID(sep->argplus[1]) == 0) { - c->Message(Chat::White, "Invalid Zone Name: %s", sep->argplus[1]); - } - else { - if (zone->LoadZoneCFG(sep->argplus[1], 0)) { - c->Message(Chat::White, "Successfully loaded zone header for %s from database.", sep->argplus[1]); - } - else { - c->Message(Chat::White, "Failed to load zone header %s from database", sep->argplus[1]); - } - auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); - memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); - entity_list.QueueClients(c, outapp); - safe_delete(outapp); + auto zone_id = ( + sep->IsNumber(1) ? + std::stoul(sep->arg[1]) : + ZoneID(sep->arg[1]) + ); + if (!zone_id) { + c->Message( + Chat::White, + fmt::format( + "Zone ID {} could not be found.", + zone_id + ).c_str() + ); + return; } + + auto zone_short_name = ZoneName(zone_id); + auto zone_long_name = ZoneLongName(zone_id); + auto version = ( + sep->IsNumber(2) ? + std::stoul(sep->arg[2]) : + 0 + ); + + auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); + memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + + c->Message( + Chat::White, + fmt::format( + "Zone Header Load {} | Zone: {} ({}){}", + ( + zone->LoadZoneCFG(zone_short_name, version) ? + "Suceeded" : + "Failed" + ), + zone_long_name, + zone_short_name, + ( + version ? + fmt::format( + " Version: {}", + version + ) : + "" + ) + ).c_str() + ); } - diff --git a/zone/zone.cpp b/zone/zone.cpp index 56a93bd96..9ab6fe545 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -1262,7 +1262,7 @@ void Zone::ReloadStaticData() { LogInfo("Zone Static Data Reloaded"); } -bool Zone::LoadZoneCFG(const char* filename, uint16 instance_id) +bool Zone::LoadZoneCFG(const char* filename, uint16 instance_version) { memset(&newzone_data, 0, sizeof(NewZone_Struct)); @@ -1270,7 +1270,7 @@ bool Zone::LoadZoneCFG(const char* filename, uint16 instance_id) if (!content_db.GetZoneCFG( ZoneID(filename), - instance_id, + instance_version, &newzone_data, can_bind, can_combat, @@ -1285,7 +1285,7 @@ bool Zone::LoadZoneCFG(const char* filename, uint16 instance_id) &map_name )) { // If loading a non-zero instance failed, try loading the default - if (instance_id != 0) { + if (instance_version != 0) { safe_delete_array(map_name); if (!content_db.GetZoneCFG( ZoneID(filename), @@ -1319,7 +1319,7 @@ bool Zone::LoadZoneCFG(const char* filename, uint16 instance_id) GetShortName(), GetLongName(), GetInstanceVersion(), - instance_id + instance_version ); return true; diff --git a/zone/zone.h b/zone/zone.h index 0a8a0aeff..814ddcf83 100755 --- a/zone/zone.h +++ b/zone/zone.h @@ -134,7 +134,7 @@ public: bool IsUCSServerAvailable() { return m_ucss_available; } bool IsZone(uint32 zone_id, uint16 instance_id) const; bool LoadGroundSpawns(); - bool LoadZoneCFG(const char *filename, uint16 instance_id); + bool LoadZoneCFG(const char *filename, uint16 instance_version); bool LoadZoneObjects(); bool Process(); bool SaveZoneCFG(); diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index c4b6363a0..df0cfb98d 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -61,20 +61,31 @@ ZoneDatabase::~ZoneDatabase() { } } -bool ZoneDatabase::SaveZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct* zd) { - - std::string query = StringFormat("UPDATE zone SET underworld = %f, minclip = %f, " - "maxclip = %f, fog_minclip = %f, fog_maxclip = %f, " - "fog_blue = %i, fog_red = %i, fog_green = %i, " - "sky = %i, ztype = %i, zone_exp_multiplier = %f, " - "safe_x = %f, safe_y = %f, safe_z = %f " - "WHERE zoneidnumber = %i AND version = %i", - zd->underworld, zd->minclip, - zd->maxclip, zd->fog_minclip[0], zd->fog_maxclip[0], - zd->fog_blue[0], zd->fog_red[0], zd->fog_green[0], - zd->sky, zd->ztype, zd->zone_exp_multiplier, - zd->safe_x, zd->safe_y, zd->safe_z, - zoneid, instance_id); +bool ZoneDatabase::SaveZoneCFG(uint32 zoneid, uint16 instance_version, NewZone_Struct* zd) { + std::string query = fmt::format( + "UPDATE zone SET underworld = {:.2f}, minclip = {:.2f}, " + "maxclip = {:.2f}, fog_minclip = {:.2f}, fog_maxclip = {:.2f}, " + "fog_blue = {}, fog_red = {}, fog_green = {}, " + "sky = {}, ztype = {}, zone_exp_multiplier = {:.2f}, " + "safe_x = {:.2f}, safe_y = {:.2f}, safe_z = {:.2f} " + "WHERE zoneidnumber = {} AND version = {}", + zd->underworld, + zd->minclip, + zd->maxclip, + zd->fog_minclip[0], + zd->fog_maxclip[0], + zd->fog_blue[0], + zd->fog_red[0], + zd->fog_green[0], + zd->sky, + zd->ztype, + zd->zone_exp_multiplier, + zd->safe_x, + zd->safe_y, + zd->safe_z, + zoneid, + instance_version + ); auto results = QueryDatabase(query); if (!results.Success()) { return false; @@ -85,7 +96,7 @@ bool ZoneDatabase::SaveZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct bool ZoneDatabase::GetZoneCFG( uint32 zoneid, - uint16 instance_id, + uint16 instance_version, NewZone_Struct *zone_data, bool &can_bind, bool &can_combat, @@ -102,7 +113,7 @@ bool ZoneDatabase::GetZoneCFG( *map_filename = new char[100]; zone_data->zone_id = zoneid; - std::string query = StringFormat( + std::string query = fmt::format( "SELECT " "ztype, " // 0 "fog_red, " // 1 @@ -169,10 +180,10 @@ bool ZoneDatabase::GetZoneCFG( "underworld_teleport_index, " // 62 "lava_damage, " // 63 "min_lava_damage " // 64 - "FROM zone WHERE zoneidnumber = %i AND version = %i %s", + "FROM zone WHERE zoneidnumber = {} AND version = {} {}", zoneid, - instance_id, - ContentFilterCriteria::apply().c_str() + instance_version, + ContentFilterCriteria::apply() ); auto results = QueryDatabase(query); if (!results.Success()) { diff --git a/zone/zonedb.h b/zone/zonedb.h index d019d6fc4..111f1d2c3 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -438,7 +438,7 @@ public: /* Zone related */ bool GetZoneCFG( uint32 zoneid, - uint16 instance_id, + uint16 instance_version, NewZone_Struct *data, bool &can_bind, bool &can_combat, @@ -451,7 +451,7 @@ public: uint8 &zone_type, int &ruleset, char **map_filename); - bool SaveZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct* zd); + bool SaveZoneCFG(uint32 zoneid, uint16 instance_version, NewZone_Struct* zd); bool LoadStaticZonePoints(LinkedList* zone_point_list,const char* zonename, uint32 version); bool UpdateZoneSafeCoords(const char* zonename, const glm::vec3& location); uint8 GetUseCFGSafeCoords(); From 8d3a179ecccb3d985873e2310fa4284a4b05934e Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 23 Nov 2021 06:03:09 -0500 Subject: [PATCH 453/624] [Commands] Remove #zuwcoords Command. (#1810) - Remove duplicate command of #zunderworld --- zone/CMakeLists.txt | 1 - zone/command.cpp | 3 +-- zone/command.h | 1 - zone/gm_commands/zuwcoords.cpp | 19 ------------------- 4 files changed, 1 insertion(+), 23 deletions(-) delete mode 100755 zone/gm_commands/zuwcoords.cpp diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 2eb9749f1..d05087713 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -566,7 +566,6 @@ SET(gm_commands gm_commands/zsky.cpp gm_commands/zstats.cpp gm_commands/zunderworld.cpp - gm_commands/zuwcoords.cpp ) ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers} ${gm_commands}) diff --git a/zone/command.cpp b/zone/command.cpp index d46e36544..93aeb69a2 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -404,8 +404,7 @@ int command_init(void) command_add("zsave", " - Saves zheader to the database", AccountStatus::QuestTroupe, command_zsave) || command_add("zsky", "[Sky Type] [Permanent (0 = False, 1 = True)] - Change zone sky type", AccountStatus::QuestTroupe, command_zsky) || command_add("zstats", "- Show info about zone header", AccountStatus::QuestTroupe, command_zstats) || - command_add("zunderworld", "[Z] [Permanent (0 = False, 1 = True)] - Change zone underworld Z", AccountStatus::QuestTroupe, command_zunderworld) || - command_add("zuwcoords", "[z coord] - Set underworld coord", AccountStatus::QuestTroupe, command_zuwcoords) + command_add("zunderworld", "[Z] [Permanent (0 = False, 1 = True)] - Change zone underworld Z", AccountStatus::QuestTroupe, command_zunderworld) ) { command_deinit(); return -1; diff --git a/zone/command.h b/zone/command.h index 6d5f9e247..ae574a0c3 100644 --- a/zone/command.h +++ b/zone/command.h @@ -328,7 +328,6 @@ void command_zsave(Client *c, const Seperator *sep); void command_zsky(Client *c, const Seperator *sep); void command_zstats(Client *c, const Seperator *sep); void command_zunderworld(Client *c, const Seperator *sep); -void command_zuwcoords(Client *c, const Seperator *sep); #ifdef BOTS #include "bot.h" diff --git a/zone/gm_commands/zuwcoords.cpp b/zone/gm_commands/zuwcoords.cpp deleted file mode 100755 index 353b48739..000000000 --- a/zone/gm_commands/zuwcoords.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "../client.h" - -void command_zuwcoords(Client *c, const Seperator *sep) -{ - // modifys and resends zhdr packet - if (sep->arg[1][0] == 0) { - c->Message(Chat::White, "Usage: #zuwcoords "); - } - else { - zone->newzone_data.underworld = atof(sep->arg[1]); - //float newdata = atof(sep->arg[1]); - //memcpy(&zone->zone_header_data[130], &newdata, sizeof(float)); - auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); - memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } -} - From 8f3cce6585b9b381500cd52a635d0a2f840ae164 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 23 Nov 2021 12:55:58 -0500 Subject: [PATCH 454/624] send graphic to target correctly (#1785) --- zone/mob.cpp | 14 +++++++++++--- zone/mob.h | 2 +- zone/perl_mob.cpp | 12 ++++++++---- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index a3d0cb8a3..e7fea3ca2 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -2910,13 +2910,21 @@ void Mob::CameraEffect(uint32 duration, uint32 intensity, Client *c, bool global safe_delete(outapp); } -void Mob::SendSpellEffect(uint32 effect_id, uint32 duration, uint32 finish_delay, bool zone_wide, uint32 unk020, bool perm_effect, Client *c) { +void Mob::SendSpellEffect(uint32 effect_id, uint32 duration, uint32 finish_delay, bool zone_wide, uint32 unk020, bool perm_effect, Client *c, uint32 caster_id, uint32 target_id) { + + if (!caster_id) { + caster_id = GetID(); + } + + if (!target_id) { + target_id = GetID(); + } auto outapp = new EQApplicationPacket(OP_SpellEffect, sizeof(SpellEffect_Struct)); SpellEffect_Struct* se = (SpellEffect_Struct*) outapp->pBuffer; se->EffectID = effect_id; // ID of the Particle Effect - se->EntityID = GetID(); - se->EntityID2 = GetID(); // EntityID again + se->EntityID = caster_id; //casting graphic animation + se->EntityID2 = target_id; // //target graphic animation se->Duration = duration; // In Milliseconds se->FinishDelay = finish_delay; // Seen 0 se->Unknown020 = unk020; // Seen 3000 diff --git a/zone/mob.h b/zone/mob.h index a59cbe400..13c812cca 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -291,7 +291,7 @@ public: //Spell void SendSpellEffect(uint32 effect_id, uint32 duration, uint32 finish_delay, bool zone_wide, - uint32 unk020, bool perm_effect = false, Client *c = nullptr); + uint32 unk020, bool perm_effect = false, Client *c = nullptr, uint32 caster_id = 0, uint32 target_id = 0); bool IsBeneficialAllowed(Mob *target); virtual int GetCasterLevel(uint16 spell_id); void ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* newbon, uint16 casterID = 0, diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index 2e64ec65f..f3460d6c8 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -4864,8 +4864,8 @@ XS(XS_Mob_CameraEffect) { XS(XS_Mob_SpellEffect); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_SpellEffect) { dXSARGS; - if (items < 2 || items > 8) - Perl_croak(aTHX_ "Usage: Mob::SpellEffect(THIS, uint32 effect, [uint32 duration = 5000], [uint32 finish_delay = 0], [bool zone_wide = false], [uint32 unk20 = 3000], [bool perm_effect = false], [Client* single_client])"); // @categories Spells and Disciplines + if (items < 2 || items > 10) + Perl_croak(aTHX_ "Usage: Mob::SpellEffect(THIS, uint32 effect, [uint32 duration = 5000], [uint32 finish_delay = 0], [bool zone_wide = false], [uint32 unk20 = 3000], [bool perm_effect = false], [Client* single_client]), [caster_id = 0], [target_id = 0]"); // @categories Spells and Disciplines { Mob *THIS; uint32 effect = (uint32) SvUV(ST(1)); @@ -4875,6 +4875,8 @@ XS(XS_Mob_SpellEffect) { uint32 unk20 = 3000; bool perm_effect = false; Client *client = nullptr; + uint32 caster_id = 0; + uint32 target_id = 0; VALIDATE_THIS_IS_MOB; if (items > 2) { duration = (uint32) SvUV(ST(2)); } if (items > 3) { finish_delay = (uint32) SvUV(ST(3)); } @@ -4890,9 +4892,11 @@ XS(XS_Mob_SpellEffect) { if (client == nullptr) Perl_croak(aTHX_ "client is nullptr, avoiding crash."); } + if (items > 8) { caster_id = (uint32)SvUV(ST(8)); } + if (items > 9) { target_id = (uint32)SvUV(ST(9)); } - THIS->SendSpellEffect(effect, duration, finish_delay, zone_wide, unk20, perm_effect, client); + THIS->SendSpellEffect(effect, duration, finish_delay, zone_wide, unk20, perm_effect, client, caster_id, target_id); } XSRETURN_EMPTY; } @@ -6743,7 +6747,7 @@ XS(boot_Mob) { newXSproto(strcpy(buf, "ShieldAbility"), XS_Mob_ShieldAbility, file, "$$$$$$$$"); newXSproto(strcpy(buf, "Shout"), XS_Mob_Shout, file, "$$;@"); newXSproto(strcpy(buf, "SignalClient"), XS_Mob_SignalClient, file, "$$$"); - newXSproto(strcpy(buf, "SpellEffect"), XS_Mob_SpellEffect, file, "$$;$$$$$$"); + newXSproto(strcpy(buf, "SpellEffect"), XS_Mob_SpellEffect, file, "$$;$$$$$$$$"); newXSproto(strcpy(buf, "SpellFinished"), XS_Mob_SpellFinished, file, "$$;$$"); newXSproto(strcpy(buf, "Spin"), XS_Mob_Spin, file, "$"); newXSproto(strcpy(buf, "StartEnrage"), XS_Mob_StartEnrage, file, "$"); From 4672e48fbd4ecdd0e90b5b487ec4ccc34d73076b Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 23 Nov 2021 16:45:31 -0500 Subject: [PATCH 455/624] [Commands] Cleanup #zcolor Command. (#1813) * [Commands] Cleanup #zcolor Command. - Cleanup message and logic. - Add parameter to allow data to be saved to database. * Update zcolor.cpp --- zone/command.cpp | 4 +- zone/gm_commands/zcolor.cpp | 86 ++++++++++++++++++++++++++----------- 2 files changed, 65 insertions(+), 25 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 93aeb69a2..5d5187769 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -390,8 +390,10 @@ int command_init(void) command_add("wpinfo", "- Show waypoint info about your NPC target", AccountStatus::GMAreas, command_wpinfo) || command_add("worldwide", "Performs world-wide GM functions such as cast (can be extended for other commands). Use caution", AccountStatus::GMImpossible, command_worldwide) || command_add("xtargets", "Show your targets Extended Targets and optionally set how many xtargets they can have.", AccountStatus::GMImpossible, command_xtargets) || + command_add("zclip", "[min] [max] - modifies and resends zhdr packet", AccountStatus::QuestTroupe, command_zclip) || + command_add("zheader", "[zonename] - Load zheader for zonename from the database", AccountStatus::QuestTroupe, command_zheader) || command_add("zclip", "[Minimum Clip] [Maximum Clip] [Fog Minimum Clip] [Fog Maximum Clip] [Permanent (0 = False, 1 = True)] - Change zone clipping", AccountStatus::QuestTroupe, command_zclip) || - command_add("zcolor", "[red] [green] [blue] - Change sky color", AccountStatus::QuestTroupe, command_zcolor) || + command_add("zcolor", "[Red] [Green] [Blue] [Permanent (0 = False, 1 = True)] - Change sky color", AccountStatus::QuestTroupe, command_zcolor) || command_add("zheader", "[Zone ID|Zone Short Name] [Version] - Load a zone header from the database", AccountStatus::QuestTroupe, command_zheader) || command_add("zone", "[zonename] [x] [y] [z] - Go to specified zone (coords optional)", AccountStatus::Guide, command_zone) || command_add("zonebootup", "[ZoneServerID] [shortname] - Make a zone server boot a specific zone", AccountStatus::GMLeadAdmin, command_zonebootup) || diff --git a/zone/gm_commands/zcolor.cpp b/zone/gm_commands/zcolor.cpp index feac259b9..bfe8cbd44 100755 --- a/zone/gm_commands/zcolor.cpp +++ b/zone/gm_commands/zcolor.cpp @@ -2,29 +2,67 @@ void command_zcolor(Client *c, const Seperator *sep) { - // modifys and resends zhdr packet - if (sep->arg[3][0] == 0) { - c->Message(Chat::White, "Usage: #zcolor "); + int arguments = sep->argnum; + if ( + !arguments || + !sep->IsNumber(1) || + !sep->IsNumber(2) || + !sep->IsNumber(3) + ) { + c->Message(Chat::White, "Usage: #zcolor [Red] [Green] [Blue] [Permanent (0 = False, 1 = True)]"); + return; } - else if (atoi(sep->arg[1]) < 0 || atoi(sep->arg[1]) > 255) { - c->Message(Chat::White, "ERROR: Red can not be less than 0 or greater than 255!"); - } - else if (atoi(sep->arg[2]) < 0 || atoi(sep->arg[2]) > 255) { - c->Message(Chat::White, "ERROR: Green can not be less than 0 or greater than 255!"); - } - else if (atoi(sep->arg[3]) < 0 || atoi(sep->arg[3]) > 255) { - c->Message(Chat::White, "ERROR: Blue can not be less than 0 or greater than 255!"); - } - else { - for (int z = 0; z < 4; z++) { - zone->newzone_data.fog_red[z] = atoi(sep->arg[1]); - zone->newzone_data.fog_green[z] = atoi(sep->arg[2]); - zone->newzone_data.fog_blue[z] = atoi(sep->arg[3]); - } - auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); - memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } -} + auto red = std::stoul(sep->arg[1]); + auto green = std::stoul(sep->arg[2]); + auto blue = std::stoul(sep->arg[3]); + auto permanent = sep->arg[4] ? atobool(sep->arg[4]) : false; + if ( + red < 0 || + red > 255 || + green < 0 || + green > 255 || + blue < 0 || + blue > 255 + ) { + c->Message(Chat::White, "Colors cannot be less than 0 or greater than 255."); + return; + } + + if (permanent) { + auto query = fmt::format( + "UPDATE zone SET fog_red = {}, fog_green = {}, fog_blue = {} " + "WHERE zoneidnumber = {} AND version = {}", + red, + green, + blue, + zone->GetZoneID(), + zone->GetInstanceVersion() + ); + database.QueryDatabase(query); + } + + for (int fog_index = 0; fog_index < 4; fog_index++) { + zone->newzone_data.fog_red[fog_index] = static_cast(red); + zone->newzone_data.fog_green[fog_index] = static_cast(green); + zone->newzone_data.fog_blue[fog_index] = static_cast(blue); + } + + auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); + memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + + c->Message( + Chat::White, + fmt::format( + "Fog Color Changed | Zone: {} ({}) Red: {} Green: {} Blue: {} Permanent: {}", + zone->GetLongName(), + zone->GetZoneID(), + red, + green, + blue, + permanent ? "Yes" : "No" + ).c_str() + ); +} From a11482ff23ee17dcdbc7357facd0a335ab2a8680 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 23 Nov 2021 18:25:02 -0500 Subject: [PATCH 456/624] [Cleanup] Utilize ConvertSecondsToTime() method. (#1805) * [Cleanup] Utilize ConvertSecondsToTime() method. * Lowercase. --- zone/bot.cpp | 11 +++- zone/client_packet.cpp | 123 ++++++++++++++++------------------------- zone/effects.cpp | 15 +++-- 3 files changed, 67 insertions(+), 82 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 8765fbb75..5de3bc7e9 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9549,8 +9549,15 @@ bool Bot::UseDiscipline(uint32 spell_id, uint32 target) { if(spells[spell_id].timer_id > 0 && spells[spell_id].timer_id < MAX_DISCIPLINE_TIMERS) SetDisciplineRecastTimer(spells[spell_id].timer_id, spell.recast_time); } else { - uint32 remain = (GetDisciplineRemainingTime(this, spells[spell_id].timer_id) / 1000); - GetOwner()->Message(Chat::White, "%s can use this discipline in %d minutes %d seconds.", GetCleanName(), (remain / 60), (remain % 60)); + uint32 remaining_time = (GetDisciplineRemainingTime(this, spells[spell_id].timer_id) / 1000); + GetOwner()->Message( + Chat::White, + fmt::format( + "{} can use this discipline in {}.", + GetCleanName(), + ConvertSecondsToTime(remaining_time) + ).c_str() + ); return false; } } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index ef62a693b..555d90144 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -857,39 +857,18 @@ void Client::CompleteConnect() } if (zone && zone->GetInstanceTimer()) { - - bool is_permanent = false; - uint32 remaining_time_seconds = database.GetTimeRemainingInstance(zone->GetInstanceID(), is_permanent); - uint32 day = (remaining_time_seconds / 86400); - uint32 hour = (remaining_time_seconds / 3600) % 24; - uint32 minute = (remaining_time_seconds / 60) % 60; - uint32 second = (remaining_time_seconds / 1) % 60; - - if (day) { - Message( - Chat::Yellow, "%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( - Chat::Yellow, "%s (%u) will expire in %u hours, %u minutes, and %u seconds.", - zone->GetLongName(), zone->GetInstanceID(), hour, minute, second - ); - } - else if (minute) { - Message( - Chat::Yellow, "%s (%u) will expire in %u minutes, and %u seconds.", - zone->GetLongName(), zone->GetInstanceID(), minute, second - ); - } - else { - Message( - Chat::Yellow, "%s (%u) will expire in in %u seconds.", - zone->GetLongName(), zone->GetInstanceID(), second - ); - } - + bool is_permanent = false; + uint32 remaining_time = database.GetTimeRemainingInstance(zone->GetInstanceID(), is_permanent); + auto time_string = ConvertSecondsToTime(remaining_time); + Message( + Chat::Yellow, + fmt::format( + "{} ({}) will expire in {}.", + zone->GetLongName(), + zone->GetInstanceID(), + time_string + ).c_str() + ); } SendRewards(); @@ -4963,54 +4942,44 @@ void Client::Handle_OP_Consider(const EQApplicationPacket *app) void Client::Handle_OP_ConsiderCorpse(const EQApplicationPacket *app) { - if (app->size != sizeof(Consider_Struct)) - { + if (app->size != sizeof(Consider_Struct)) { LogDebug("Size mismatch in Consider corpse expected [{}] got [{}]", sizeof(Consider_Struct), app->size); return; } + Consider_Struct* conin = (Consider_Struct*)app->pBuffer; - Corpse* tcorpse = entity_list.GetCorpseByID(conin->targetid); + Corpse* target = entity_list.GetCorpseByID(conin->targetid); std::string export_string = fmt::format("{}", conin->targetid); - if (tcorpse && tcorpse->IsNPCCorpse()) { - if (parse->EventPlayer(EVENT_CONSIDER_CORPSE, this, export_string, 0) == 1) { - return; - } - - 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 }; - MessageString(Chat::NPCQuestSay, CORPSE_DECAY1, ConvertArray(min, val1), ConvertArray(sec, val2)); - } - else { - MessageString(Chat::NPCQuestSay, CORPSE_DECAY_NOW); - } + if (!target) { + return; } - else if (tcorpse && tcorpse->IsPlayerCorpse()) { - if (parse->EventPlayer(EVENT_CONSIDER_CORPSE, this, export_string, 0) == 1) { - return; - } - 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); + if (parse->EventPlayer(EVENT_CONSIDER_CORPSE, this, export_string, 0)) { + return; + } - Message(0, "This corpse %s be resurrected.", tcorpse->IsRezzed() ? "cannot" : "can"); - } - else { - MessageString(Chat::NPCQuestSay, CORPSE_DECAY_NOW); + uint32 decay_time = target->GetDecayTime(); + if (decay_time) { + auto time_string = ConvertSecondsToTime(decay_time, true); + Message( + Chat::NPCQuestSay, + fmt::format( + "This corpse will decay in {}.", + time_string + ).c_str() + ); + + if (target->IsPlayerCorpse()) { + Message( + Chat::NPCQuestSay, + fmt::format( + "This corpse {} be resurrected.", + target->IsRezzed() ? "cannot" : "can" + ).c_str() + ); } + } else { + MessageString(Chat::NPCQuestSay, CORPSE_DECAY_NOW); } } @@ -12943,8 +12912,14 @@ void Client::Handle_OP_Shielding(const EQApplicationPacket *app) pTimerType timer = pTimerShieldAbility; if (!p_timers.Expired(&database, timer, false)) { - uint32 remain = p_timers.GetRemainingTime(timer); - Message(Chat::White, "You can use the ability /shield in %d minutes %d seconds.", ((remain) / 60), (remain % 60)); + uint32 remaining_time = p_timers.GetRemainingTime(timer); + Message( + Chat::White, + fmt::format( + "You can use the ability /shield in {}.", + ConvertSecondsToTime(remaining_time) + ).c_str() + ); return; } diff --git a/zone/effects.cpp b/zone/effects.cpp index 3749bc91b..32f836919 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -774,12 +774,15 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) { //Check the disc timer pTimerType DiscTimer = pTimerDisciplineReuseStart + spell.timer_id; if(!p_timers.Expired(&database, DiscTimer, false)) { // lets not set the reuse timer in case CastSpell fails (or we would have to turn off the timer, but CastSpell will set it as well) - /*char val1[20]={0};*/ //unused - /*char val2[20]={0};*/ //unused - uint32 remain = p_timers.GetRemainingTime(DiscTimer); - //MessageString(Chat::White, DISCIPLINE_CANUSEIN, ConvertArray((remain)/60,val1), ConvertArray(remain%60,val2)); - Message(0, "You can use this discipline in %d minutes %d seconds.", ((remain)/60), (remain%60)); - return(false); + uint32 remaining_time = p_timers.GetRemainingTime(DiscTimer); + Message( + Chat::White, + fmt::format( + "You can use this discipline in {}.", + ConvertSecondsToTime(remaining_time) + ).c_str() + ); + return false; } if(spell.recast_time > 0) From 64966901234fd471607fcd0e2853ed1315c50757 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 23 Nov 2021 18:25:12 -0500 Subject: [PATCH 457/624] [Commands] Cleanup #zsafecoords Command. (#1806) * [Commands] Cleanup #zsafecoords Command. - Cleanup message and logic. - Add parameter to allow data to be saved to database. * Typo. * Update zsafecoords.cpp * Update zsafecoords.cpp --- zone/command.cpp | 2 +- zone/gm_commands/zsafecoords.cpp | 76 ++++++++++++++++++++++++-------- 2 files changed, 59 insertions(+), 19 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 5d5187769..be724e859 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -402,7 +402,7 @@ int command_init(void) command_add("zoneshutdown", "[shortname] - Shut down a zone server", AccountStatus::GMLeadAdmin, command_zoneshutdown) || command_add("zonestatus", "- Show connected zoneservers, synonymous with /servers", AccountStatus::GMLeadAdmin, command_zonestatus) || command_add("zopp", "Troubleshooting command - Sends a fake item packet to you. No server reference is created.", AccountStatus::GMImpossible, command_zopp) || - command_add("zsafecoords", "[x] [y] [z] - Set safe coords", AccountStatus::QuestTroupe, command_zsafecoords) || + command_add("zsafecoords", "[X] [Y] [Z] [Heading] [Permanent (0 = False, 1 = True)] - Set the current zone's safe coordinates", AccountStatus::QuestTroupe, command_zsafecoords) || command_add("zsave", " - Saves zheader to the database", AccountStatus::QuestTroupe, command_zsave) || command_add("zsky", "[Sky Type] [Permanent (0 = False, 1 = True)] - Change zone sky type", AccountStatus::QuestTroupe, command_zsky) || command_add("zstats", "- Show info about zone header", AccountStatus::QuestTroupe, command_zstats) || diff --git a/zone/gm_commands/zsafecoords.cpp b/zone/gm_commands/zsafecoords.cpp index 237f288da..bce0ea743 100755 --- a/zone/gm_commands/zsafecoords.cpp +++ b/zone/gm_commands/zsafecoords.cpp @@ -2,25 +2,65 @@ void command_zsafecoords(Client *c, const Seperator *sep) { - // modifys and resends zhdr packet - if (sep->arg[3][0] == 0) { - c->Message(Chat::White, "Usage: #zsafecoords "); + int arguments = sep->argnum; + if ( + !arguments || + !sep->IsNumber(1) || + !sep->IsNumber(2) || + !sep->IsNumber(3) + ) { + c->Message(Chat::White, "Usage: #zsafecoords [X] [Y] [Z] [Heading] [Permanent (0 = False, 1 = True)]"); + c->Message(Chat::White, "Not sending Heading defaults to current Heading and the change is temporary."); + return; } - else { - zone->newzone_data.safe_x = atof(sep->arg[1]); - zone->newzone_data.safe_y = atof(sep->arg[2]); - zone->newzone_data.safe_z = atof(sep->arg[3]); - //float newdatax = atof(sep->arg[1]); - //float newdatay = atof(sep->arg[2]); - //float newdataz = atof(sep->arg[3]); - //memcpy(&zone->zone_header_data[114], &newdatax, sizeof(float)); - //memcpy(&zone->zone_header_data[118], &newdatay, sizeof(float)); - //memcpy(&zone->zone_header_data[122], &newdataz, sizeof(float)); - //zone->SetSafeCoords(); - auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); - memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); - entity_list.QueueClients(c, outapp); - safe_delete(outapp); + + auto x = std::stof(sep->arg[1]); + auto y = std::stof(sep->arg[2]); + auto z = std::stof(sep->arg[3]); + auto heading = sep->arg[3] ? std::stof(sep->arg[3]) : c->GetHeading(); + auto permanent = sep->arg[4] ? atobool(sep->arg[4]) : false; + if (permanent) { + auto query = fmt::format( + "UPDATE zone SET safe_x = {:.2f}, safe_y = {:.2f}, safe_z = {:.2f}, safe_heading = {:.2f} WHERE zoneidnumber = {} AND version = {}", + x, + y, + z, + heading, + zone->GetZoneID(), + zone->GetInstanceVersion() + ); + database.QueryDatabase(query); } + + zone->newzone_data.safe_x = x; + zone->newzone_data.safe_y = y; + zone->newzone_data.safe_z = z; + + auto outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); + memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + + c->Message( + Chat::White, + fmt::format( + "Safe Coordinates Changed | Zone: {} ({}){} XYZ: {:.2f}, {:.2f}, {:.2f} Heading: {:.2f} Permanent: {} ", + zone->GetLongName(), + zone->GetZoneID(), + ( + zone->GetInstanceVersion() ? + fmt::format( + " Version: {}", + zone->GetInstanceVersion() + ) : + "" + ), + x, + y, + z, + heading, + permanent ? "Yes" : "No" + ).c_str() + ); } From 6fa41a3b73ef627c528b7130c2d4591fefc43b78 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 23 Nov 2021 19:53:39 -0500 Subject: [PATCH 458/624] [Commands] Remove duplicate commands. (#1819) --- zone/command.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index be724e859..5a5943067 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -390,8 +390,6 @@ int command_init(void) command_add("wpinfo", "- Show waypoint info about your NPC target", AccountStatus::GMAreas, command_wpinfo) || command_add("worldwide", "Performs world-wide GM functions such as cast (can be extended for other commands). Use caution", AccountStatus::GMImpossible, command_worldwide) || command_add("xtargets", "Show your targets Extended Targets and optionally set how many xtargets they can have.", AccountStatus::GMImpossible, command_xtargets) || - command_add("zclip", "[min] [max] - modifies and resends zhdr packet", AccountStatus::QuestTroupe, command_zclip) || - command_add("zheader", "[zonename] - Load zheader for zonename from the database", AccountStatus::QuestTroupe, command_zheader) || command_add("zclip", "[Minimum Clip] [Maximum Clip] [Fog Minimum Clip] [Fog Maximum Clip] [Permanent (0 = False, 1 = True)] - Change zone clipping", AccountStatus::QuestTroupe, command_zclip) || command_add("zcolor", "[Red] [Green] [Blue] [Permanent (0 = False, 1 = True)] - Change sky color", AccountStatus::QuestTroupe, command_zcolor) || command_add("zheader", "[Zone ID|Zone Short Name] [Version] - Load a zone header from the database", AccountStatus::QuestTroupe, command_zheader) || From 8b5b19ae2c9f52257027f156aa655d7e25730d3b Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 23 Nov 2021 21:45:52 -0500 Subject: [PATCH 459/624] [Commands] Cleanup #enablerecipe Command. (#1817) - Cleanup message and logic. --- zone/command.cpp | 2 +- zone/gm_commands/enablerecipe.cpp | 46 +++++++++++++++---------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 5a5943067..f74a951c0 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -165,7 +165,7 @@ int command_init(void) command_add("emotesearch", "Searches NPC Emotes", AccountStatus::QuestTroupe, command_emotesearch) || command_add("emoteview", "Lists all NPC Emotes", AccountStatus::QuestTroupe, command_emoteview) || command_add("emptyinventory", "- Clears you or your target's entire inventory (Equipment, General, Bank, and Shared Bank)", AccountStatus::GMImpossible, command_emptyinventory) || - command_add("enablerecipe", "[recipe_id] - Enables a recipe using the recipe id.", AccountStatus::QuestTroupe, command_enablerecipe) || + command_add("enablerecipe", "[Recipe ID] - Enables a Recipe", AccountStatus::QuestTroupe, command_enablerecipe) || command_add("endurance", "Restores you or your target's endurance.", AccountStatus::Guide, command_endurance) || command_add("equipitem", "[slotid(0-21)] - Equip the item on your cursor into the specified slot", AccountStatus::Guide, command_equipitem) || command_add("face", "- Change the face of your target", AccountStatus::QuestTroupe, command_face) || diff --git a/zone/gm_commands/enablerecipe.cpp b/zone/gm_commands/enablerecipe.cpp index acfa1ff93..088907a75 100755 --- a/zone/gm_commands/enablerecipe.cpp +++ b/zone/gm_commands/enablerecipe.cpp @@ -2,28 +2,28 @@ void command_enablerecipe(Client *c, const Seperator *sep) { - uint32 recipe_id = 0; - bool success = false; - if (c) { - if (sep->argnum == 1) { - recipe_id = atoi(sep->arg[1]); - } - else { - c->Message(Chat::White, "Invalid number of arguments.\nUsage: #enablerecipe recipe_id"); - return; - } - if (recipe_id > 0) { - success = content_db.EnableRecipe(recipe_id); - if (success) { - c->Message(Chat::White, "Recipe enabled."); - } - else { - c->Message(Chat::White, "Recipe not enabled."); - } - } - else { - c->Message(Chat::White, "Invalid recipe id.\nUsage: #enablerecipe recipe_id"); - } + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #enablerecipe [Recipe ID]"); + return; } -} + auto recipe_id = std::stoul(sep->arg[1]); + if (!recipe_id) { + c->Message(Chat::White, "Usage: #enablerecipe [Recipe ID]"); + return; + } + + c->Message( + Chat::White, + fmt::format( + "Recipe ID {} {} enabled.", + recipe_id, + ( + content_db.EnableRecipe(recipe_id) ? + "successfully" : + "failed to be" + ) + ).c_str() + ); +} From 1a2897c4235bd0bb132c003f874b64912a20df24 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 23 Nov 2021 21:45:59 -0500 Subject: [PATCH 460/624] [Commands] Cleanup #disablerecipe Command. (#1816) - Cleanup message and logic. --- zone/command.cpp | 2 +- zone/gm_commands/disablerecipe.cpp | 46 +++++++++++++++--------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index f74a951c0..8163dcb0f 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -152,7 +152,7 @@ int command_init(void) command_add("depopzone", "- Depop the zone", AccountStatus::GMAdmin, command_depopzone) || command_add("devtools", "- Manages devtools", AccountStatus::GMMgmt, command_devtools) || command_add("details", "- Change the details of your target (Drakkin Only)", AccountStatus::QuestTroupe, command_details) || - command_add("disablerecipe", "[recipe_id] - Disables a recipe using the recipe id.", AccountStatus::QuestTroupe, command_disablerecipe) || + command_add("disablerecipe", "[Recipe ID] - Disables a Recipe", AccountStatus::QuestTroupe, command_disablerecipe) || command_add("disarmtrap", "Analog for ldon disarm trap for the newer clients since we still don't have it working.", AccountStatus::QuestTroupe, command_disarmtrap) || command_add("distance", "- Reports the distance between you and your target.", AccountStatus::QuestTroupe, command_distance) || command_add("door", "Door editing command", AccountStatus::QuestTroupe, command_door) || diff --git a/zone/gm_commands/disablerecipe.cpp b/zone/gm_commands/disablerecipe.cpp index ca5c6e152..b09a6317c 100755 --- a/zone/gm_commands/disablerecipe.cpp +++ b/zone/gm_commands/disablerecipe.cpp @@ -2,28 +2,28 @@ void command_disablerecipe(Client *c, const Seperator *sep) { - uint32 recipe_id = 0; - bool success = false; - if (c) { - if (sep->argnum == 1) { - recipe_id = atoi(sep->arg[1]); - } - else { - c->Message(Chat::White, "Invalid number of arguments.\nUsage: #disablerecipe recipe_id"); - return; - } - if (recipe_id > 0) { - success = content_db.DisableRecipe(recipe_id); - if (success) { - c->Message(Chat::White, "Recipe disabled."); - } - else { - c->Message(Chat::White, "Recipe not disabled."); - } - } - else { - c->Message(Chat::White, "Invalid recipe id.\nUsage: #disablerecipe recipe_id"); - } + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #disablerecipe [Recipe ID]"); + return; } -} + auto recipe_id = std::stoul(sep->arg[1]); + if (!recipe_id) { + c->Message(Chat::White, "Usage: #disablerecipe [Recipe ID]"); + return; + } + + c->Message( + Chat::White, + fmt::format( + "Recipe ID {} {} disabled.", + recipe_id, + ( + content_db.DisableRecipe(recipe_id) ? + "successfully" : + "failed to be" + ) + ).c_str() + ); +} From 6ff7f7aa539f69915a58c8a6a660e32fa3dc80aa Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 24 Nov 2021 12:48:23 -0500 Subject: [PATCH 461/624] [API] mob->SpellEffect small perl fix (#1820) * Update perl_mob.cpp * Update perl_mob.cpp --- zone/perl_mob.cpp | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index f3460d6c8..d87714367 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -4877,6 +4877,7 @@ XS(XS_Mob_SpellEffect) { Client *client = nullptr; uint32 caster_id = 0; uint32 target_id = 0; + bool nullclient = false; VALIDATE_THIS_IS_MOB; if (items > 2) { duration = (uint32) SvUV(ST(2)); } if (items > 3) { finish_delay = (uint32) SvUV(ST(3)); } @@ -4885,18 +4886,26 @@ XS(XS_Mob_SpellEffect) { if (items > 6) { perm_effect = (bool) SvTRUE(ST(6)); } if (items > 7) { if (sv_derived_from(ST(7), "Client")) { - IV tmp = SvIV((SV *) SvRV(ST(7))); + IV tmp = SvIV((SV *)SvRV(ST(7))); client = INT2PTR(Client *, tmp); - } else - Perl_croak(aTHX_ "client is not of type Client"); - if (client == nullptr) - Perl_croak(aTHX_ "client is nullptr, avoiding crash."); + } + else { + nullclient = true; + } + if (client == nullptr) { + nullclient = true; + } } if (items > 8) { caster_id = (uint32)SvUV(ST(8)); } if (items > 9) { target_id = (uint32)SvUV(ST(9)); } + + if (nullclient) { + THIS->SendSpellEffect(effect, duration, finish_delay, zone_wide, unk20, perm_effect, 0, caster_id, target_id); + } + else { + THIS->SendSpellEffect(effect, duration, finish_delay, zone_wide, unk20, perm_effect, client, caster_id, target_id); + } - - THIS->SendSpellEffect(effect, duration, finish_delay, zone_wide, unk20, perm_effect, client, caster_id, target_id); } XSRETURN_EMPTY; } From a6f5bf72be7c3a2eb56cc242e15b3b070b00f9c1 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 24 Nov 2021 19:43:43 -0500 Subject: [PATCH 462/624] Update spell_effects.cpp (#1822) --- zone/spell_effects.cpp | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index fedd4b3cb..ce93666f7 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3323,7 +3323,6 @@ int Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level, effect_value = CalcSpellEffectValue_formula(formula, base_value, max_value, caster_level, spell_id, ticsremaining); // this doesn't actually need to be a song to get mods, just the right skill - if (EQ::skills::IsBardInstrumentSkill(spells[spell_id].skill) && IsInstrumentModAppliedToSpellEffect(spell_id, spells[spell_id].effect_id[effect_id])) { oval = effect_value; @@ -3343,29 +3342,34 @@ int Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level, it will focus the base value correctly. */ - if (GetClass() != BARD) { - if (caster_id && instrument_mod > 10) { - //This is checked from Mob::ApplySpellBonuses, applied to buffs that receive bonuses. See above, must be in 10% intervals to work. + /* + Calculate base effects modifier for casters who are not bards. + */ + + //This is checked from Mob::SpellEffects and applied to instant spells and runes. + if (caster && caster->GetClass() != BARD && caster->HasBaseEffectFocus()) { + + oval = effect_value; + int mod = caster->GetFocusEffect(focusFcBaseEffects, spell_id); + effect_value += effect_value * mod / 100; + + LogSpells("Instant Effect value [{}] altered with base effects modifier of [{}] to yeild [{}]", + oval, mod, effect_value); + } + //This is checked from Mob::ApplySpellBonuses, applied to buffs that receive bonuses. See above, must be in 10% intervals to work. + else if (caster_id && instrument_mod > 10) { + + Mob* buff_caster = entity_list.GetMob(caster_id);//If targeted bard song needed to confirm caster is not bard. + if (buff_caster && buff_caster->GetClass() != BARD) { oval = effect_value; effect_value = effect_value * static_cast(instrument_mod) / 10; LogSpells("Bonus Effect value [{}] altered with base effects modifier of [{}] to yeild [{}]", oval, instrument_mod, effect_value); } - else if (!caster_id) { - //This is checked from Mob::SpellEffects and applied to instant spells and runes. - if (caster && caster->HasBaseEffectFocus()) { - oval = effect_value; - int mod = caster->GetFocusEffect(focusFcBaseEffects, spell_id); - effect_value += effect_value * mod / 100; - - LogSpells("Instant Effect value [{}] altered with base effects modifier of [{}] to yeild [{}]", - oval, mod, effect_value); - } - } } - + effect_value = mod_effect_value(effect_value, spell_id, spells[spell_id].effect_id[effect_id], caster, caster_id); return effect_value; From 9d59b3def4bb82ddd363f39c61716f2b1c763796 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 25 Nov 2021 08:32:46 -0500 Subject: [PATCH 463/624] [Spells] Bard AA clicks should not receive song modifiers. (#1824) --- zone/client_mods.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zone/client_mods.cpp b/zone/client_mods.cpp index ae2e6487a..307623b0f 100644 --- a/zone/client_mods.cpp +++ b/zone/client_mods.cpp @@ -1517,6 +1517,11 @@ uint32 Mob::GetInstrumentMod(uint16 spell_id) } return 10; } + + //AA's click effects that use instrument/singing skills don't apply modifiers (Confirmed on live 11/24/21 ~Kayen) + if (casting_spell_aa_id) { + return 10; + } uint32 effectmod = 10; int effectmodcap = 0; From ba427c64ba686b1e9c0f86e575ed28906331da16 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 25 Nov 2021 08:33:39 -0500 Subject: [PATCH 464/624] [Bug Fix] Numhits now display instantly on cast. (#1826) --- zone/spells.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/zone/spells.cpp b/zone/spells.cpp index 344d69781..7f0d49df1 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1458,6 +1458,16 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo if(DeleteChargeFromSlot >= 0) CastToClient()->DeleteItemInInventory(DeleteChargeFromSlot, 1, true); + //Check if buffs has numhits, then resend packet so it displays the hit count. + if (IsClient() && (spells[spell_id].buff_duration > 0 || spells[spell_id].short_buff_box)) { + for (int i = 0; i < GetMaxTotalSlots(); i++) { + if (buffs[i].spellid == spell_id && buffs[i].hit_number > 0) { + CastToClient()->SendBuffNumHitPacket(buffs[i], i); + break; + } + } + } + // // at this point the spell has successfully been cast // From 1a5f48521d97e7fcfb1ba89fc7e3095e0abf5c72 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 25 Nov 2021 10:16:28 -0500 Subject: [PATCH 465/624] [Bug Fix] Bind Sight will now function properly (#1825) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * start * bind sight fixed * Update spdat.h * Update spells.cpp * Search or jump to… Pull requests Issues Marketplace Explore @KayenEQ EQEmu / Server Public 60 338 290 Code Issues 106 Pull requests 11 Actions Projects 1 Wiki Security Insights [Bug Fix] Bind Sight will now function properly #1825 Open KayenEQ wants to merge 4 commits into EQEmu:master from KayenEQ:bindsightfix2 Open [Bug Fix] Bind Sight will now function properly --- common/spdat.h | 2 +- zone/client_packet.cpp | 3 ++- zone/spells.cpp | 14 +++++++++----- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 372cd97db..e77dcf521 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -775,7 +775,7 @@ typedef enum { //#define SE_CorpseBomb 70 // not used #define SE_NecPet 71 // implemented //#define SE_PreserveCorpse 72 // not used -#define SE_BindSight 73 // implemented +#define SE_BindSight 73 // implemented, @Vision, see through the eyes of your target, click off buff to end effect, base: 1, limit: none, max: none #define SE_FeignDeath 74 // implemented #define SE_VoiceGraft 75 // implemented #define SE_Sentinel 76 // *not implemented?(just seems to send a message) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 555d90144..0975c1df3 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -3986,8 +3986,9 @@ void Client::Handle_OP_BuffRemoveRequest(const EQApplicationPacket *app) uint16 SpellID = m->GetSpellIDFromSlot(brrs->SlotID); - if (SpellID && IsBeneficialSpell(SpellID) && !spells[SpellID].no_remove) + if (SpellID && (IsBeneficialSpell(SpellID) || IsEffectInSpell(SpellID, SE_BindSight)) && !spells[SpellID].no_remove) { m->BuffFadeBySlot(brrs->SlotID, true); + } } void Client::Handle_OP_Bug(const EQApplicationPacket *app) diff --git a/zone/spells.cpp b/zone/spells.cpp index 7f0d49df1..863646492 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1458,6 +1458,14 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo if(DeleteChargeFromSlot >= 0) CastToClient()->DeleteItemInInventory(DeleteChargeFromSlot, 1, true); + if (IsClient() && IsEffectInSpell(spell_id, SE_BindSight)) { + for (int i = 0; i < GetMaxTotalSlots(); i++) { + if (buffs[i].spellid == spell_id) { + CastToClient()->SendBuffNumHitPacket(buffs[i], i);//its hack, it works. + } + } + } + //Check if buffs has numhits, then resend packet so it displays the hit count. if (IsClient() && (spells[spell_id].buff_duration > 0 || spells[spell_id].short_buff_box)) { for (int i = 0; i < GetMaxTotalSlots(); i++) { @@ -3655,11 +3663,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes } // select target - if // Bind Sight line of spells - ( - spell_id == 500 || // bind sight - spell_id == 407 // cast sight - ) + if (IsEffectInSpell(spell_id, SE_BindSight)) { action->target = GetID(); } From d38b8a4867be2b4919c3129a941b1c74846f7ba6 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 25 Nov 2021 13:55:06 -0500 Subject: [PATCH 466/624] [Bug Fix] Fix possible crash in #givemoney. (#1828) --- common/string_util.cpp | 25 +++++++++++++++---------- zone/gm_commands/givemoney.cpp | 8 ++++---- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/common/string_util.cpp b/common/string_util.cpp index 4b53d32dc..fe392aedd 100644 --- a/common/string_util.cpp +++ b/common/string_util.cpp @@ -1205,11 +1205,6 @@ std::string ConvertMoneyToString(uint32 platinum, uint32 gold, uint32 silver, ui silver, copper ); - } else if (copper && !silver && !gold && !platinum) { // C - money_string = fmt::format( - "{} Copper", - copper - ); } else if (!copper && silver && gold && platinum) { // SGP money_string = fmt::format( "{} Platinum, {} Gold, and {} Silver", @@ -1223,11 +1218,6 @@ std::string ConvertMoneyToString(uint32 platinum, uint32 gold, uint32 silver, ui gold, silver ); - } else if (!copper && silver && !gold && !platinum) { // S - money_string = fmt::format( - "{} Silver", - silver - ); } else if (copper && !silver && gold && platinum) { // CGP money_string = fmt::format( "{} Platinum, {} Gold, and {} Copper", @@ -1247,11 +1237,26 @@ std::string ConvertMoneyToString(uint32 platinum, uint32 gold, uint32 silver, ui platinum, gold ); + } else if (!copper && !silver && !gold && platinum) { // P + money_string = fmt::format( + "{} Platinum", + platinum + ); } else if (!copper && !silver && gold && !platinum) { // G money_string = fmt::format( "{} Gold", gold ); + } else if (!copper && silver && !gold && !platinum) { // S + money_string = fmt::format( + "{} Silver", + silver + ); + } else if (copper && !silver && !gold && !platinum) { // C + money_string = fmt::format( + "{} Copper", + copper + ); } return money_string; } \ No newline at end of file diff --git a/zone/gm_commands/givemoney.cpp b/zone/gm_commands/givemoney.cpp index 4c3076f9e..3e3727292 100755 --- a/zone/gm_commands/givemoney.cpp +++ b/zone/gm_commands/givemoney.cpp @@ -14,10 +14,10 @@ void command_givemoney(Client *c, const Seperator *sep) target = c->GetTarget()->CastToClient(); } - uint32 platinum = std::stoi(sep->arg[1]); - uint32 gold = std::stoi(sep->arg[2]); - uint32 silver = std::stoi(sep->arg[3]); - uint32 copper = std::stoi(sep->arg[4]); + uint32 platinum = std::stoul(sep->arg[1]); + uint32 gold = sep->IsNumber(2) ? std::stoul(sep->arg[2]) : 0; + uint32 silver = sep->IsNumber(3) ? std::stoul(sep->arg[3]) : 0; + uint32 copper = sep->IsNumber(4) ? std::stoul(sep->arg[4]) : 0; if (!platinum && !gold && !silver && !copper) { c->Message(Chat::Red, "Usage: #Usage: #givemoney [Platinum] [Gold] [Silver] [Copper]"); return; From e474b2a2808e93fbc6416756d4542536db06f837 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 25 Nov 2021 14:50:05 -0500 Subject: [PATCH 467/624] [Commands] Add #petitems Command. (#1823) - Add #petitems command to show a person's pet items if they have access to the command. - Adds a default false parameter to QueryLoot for NPCs that keeps messages and logic from being ran on pets for no reason. - Cleaned up message a bit for loot and stuff. - Remove check for loottable ID when using #npcstats for NPCs that get items from a script or otherwise. --- zone/CMakeLists.txt | 1 + zone/command.cpp | 1 + zone/command.h | 1 + zone/gm_commands/npcstats.cpp | 4 +-- zone/gm_commands/petitems.cpp | 25 ++++++++++++++ zone/npc.cpp | 65 +++++++++++++++++++---------------- zone/npc.h | 2 +- 7 files changed, 65 insertions(+), 34 deletions(-) create mode 100644 zone/gm_commands/petitems.cpp diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index d05087713..3356c385c 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -440,6 +440,7 @@ SET(gm_commands gm_commands/permaclass.cpp gm_commands/permagender.cpp gm_commands/permarace.cpp + gm_commands/petitems.cpp gm_commands/petitioninfo.cpp gm_commands/petname.cpp gm_commands/pf.cpp diff --git a/zone/command.cpp b/zone/command.cpp index 8163dcb0f..d693c58ac 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -277,6 +277,7 @@ int command_init(void) command_add("permaclass", "[Class ID] - Change your or your player target's class, changed client is disconnected", AccountStatus::QuestTroupe, command_permaclass) || command_add("permagender", "[Gender ID] - Change your or your player target's gender", AccountStatus::QuestTroupe, command_permagender) || command_add("permarace", "[Race ID] - Change your or your player target's race", AccountStatus::QuestTroupe, command_permarace) || + command_add("petitems", "- View your pet's items if you have one", AccountStatus::ApprenticeGuide, command_petitems) || command_add("petitioninfo", "[petition number] - Get info about a petition", AccountStatus::ApprenticeGuide, command_petitioninfo) || command_add("pf", "- Display additional mob coordinate and wandering data", AccountStatus::Player, command_pf) || command_add("picklock", "Analog for ldon pick lock for the newer clients since we still don't have it working.", AccountStatus::Player, command_picklock) || diff --git a/zone/command.h b/zone/command.h index ae574a0c3..9643c3efc 100644 --- a/zone/command.h +++ b/zone/command.h @@ -193,6 +193,7 @@ void command_peqzone(Client *c, const Seperator *sep); void command_permaclass(Client *c, const Seperator *sep); void command_permagender(Client *c, const Seperator *sep); void command_permarace(Client *c, const Seperator *sep); +void command_petitems(Client *c, const Seperator *sep); void command_petitioninfo(Client *c, const Seperator *sep); void command_picklock(Client *c, const Seperator *sep); void command_profanity(Client *c, const Seperator *sep); diff --git a/zone/gm_commands/npcstats.cpp b/zone/gm_commands/npcstats.cpp index 9bc05e766..9f903d1f7 100755 --- a/zone/gm_commands/npcstats.cpp +++ b/zone/gm_commands/npcstats.cpp @@ -9,9 +9,7 @@ void command_npcstats(Client *c, const Seperator *sep) target->ShowStats(c); // Loot Data - if (target->GetLoottableID()) { - target->QueryLoot(c); - } + target->QueryLoot(c); } else { c->Message(Chat::White, "You must target an NPC to use this command."); diff --git a/zone/gm_commands/petitems.cpp b/zone/gm_commands/petitems.cpp new file mode 100644 index 000000000..833b9e276 --- /dev/null +++ b/zone/gm_commands/petitems.cpp @@ -0,0 +1,25 @@ +#include "../client.h" + +void command_petitems(Client *c, const Seperator *sep) +{ + if (!c->GetPet()) { + c->Message(Chat::White, "You must have a pet to use this command."); + return; + } + + auto pet = c->GetPet()->CastToNPC(); + auto loot_list = pet->GetLootList(); + if (!loot_list.empty()) { + pet->QueryLoot(c, true); + c->Message( + Chat::White, + fmt::format( + "Your pet has {} item{}.", + loot_list.size(), + loot_list.size() != 1 ? "s" : "" + ).c_str() + ); + } else { + c->Message(Chat::White, "Your pet has no items."); + } +} diff --git a/zone/npc.cpp b/zone/npc.cpp index 905257a28..7e2304095 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -641,18 +641,21 @@ void NPC::ClearItemList() { SendAppearancePacket(AT_Light, GetActiveLightType()); } -void NPC::QueryLoot(Client* to) +void NPC::QueryLoot(Client* to, bool is_pet_query) { - if (itemlist.size() > 0) { - to->Message( - Chat::White, - fmt::format( - "Loot | Name: {} ID: {} Loottable ID: {}", - GetName(), - GetNPCTypeID(), - GetLoottableID() - ).c_str() - ); + if (!itemlist.empty()) { + if (!is_pet_query) { + to->Message( + Chat::White, + fmt::format( + "Loot | {} ({}) ID: {} Loottable ID: {}", + GetName(), + GetID(), + GetNPCTypeID(), + GetLoottableID() + ).c_str() + ); + } int item_count = 0; for (auto current_item : itemlist) { @@ -674,7 +677,7 @@ void NPC::QueryLoot(Client* to) to->Message( Chat::White, fmt::format( - "Item {} | Name: {} ({}){}", + "Item {} | {} ({}){}", item_number, linker.GenerateLink().c_str(), current_item->item_id, @@ -692,25 +695,27 @@ void NPC::QueryLoot(Client* to) } } - bool has_money = ( - platinum > 0 || - gold > 0 || - silver > 0 || - copper > 0 - ); - if (has_money) { - to->Message( - Chat::White, - fmt::format( - "Money | {}", - ConvertMoneyToString( - platinum, - gold, - silver, - copper - ) - ).c_str() + if (!is_pet_query) { + bool has_money = ( + platinum > 0 || + gold > 0 || + silver > 0 || + copper > 0 ); + if (has_money) { + to->Message( + Chat::White, + fmt::format( + "Money | {}", + ConvertMoneyToString( + platinum, + gold, + silver, + copper + ) + ).c_str() + ); + } } } diff --git a/zone/npc.h b/zone/npc.h index ad7d14c86..df9159f0b 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -203,7 +203,7 @@ public: void AddCash(uint16 in_copper, uint16 in_silver, uint16 in_gold, uint16 in_platinum); void AddCash(); void RemoveCash(); - void QueryLoot(Client* to); + void QueryLoot(Client* to, bool is_pet_query = false); bool HasItem(uint32 item_id); uint16 CountItem(uint32 item_id); uint32 GetItemIDBySlot(uint16 loot_slot); From b29f398239df5e83fcaac39873c610bfc1521118 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 25 Nov 2021 21:01:23 -0500 Subject: [PATCH 468/624] [Commands] Cleanup #getplayerburiedcorpsecount Command. (#1818) * [Commands] Cleanup #getplayerburiedcorpsecount Command. - Cleanup message and logic. * Update command.cpp --- zone/command.cpp | 6 +-- .../getplayerburiedcorpsecount.cpp | 43 +++++++++++-------- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index d693c58ac..0e89dc4a9 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -164,9 +164,9 @@ int command_init(void) command_add("emote", "['name'/'world'/'zone'] [type] [message] - Send an emote message", AccountStatus::QuestTroupe, command_emote) || command_add("emotesearch", "Searches NPC Emotes", AccountStatus::QuestTroupe, command_emotesearch) || command_add("emoteview", "Lists all NPC Emotes", AccountStatus::QuestTroupe, command_emoteview) || - command_add("emptyinventory", "- Clears you or your target's entire inventory (Equipment, General, Bank, and Shared Bank)", AccountStatus::GMImpossible, command_emptyinventory) || + command_add("emptyinventory", "- Clears your or your target's entire inventory (Equipment, General, Bank, and Shared Bank)", AccountStatus::GMImpossible, command_emptyinventory) || command_add("enablerecipe", "[Recipe ID] - Enables a Recipe", AccountStatus::QuestTroupe, command_enablerecipe) || - command_add("endurance", "Restores you or your target's endurance.", AccountStatus::Guide, command_endurance) || + command_add("endurance", "Restores your or your target's endurance.", AccountStatus::Guide, command_endurance) || command_add("equipitem", "[slotid(0-21)] - Equip the item on your cursor into the specified slot", AccountStatus::Guide, command_equipitem) || command_add("face", "- Change the face of your target", AccountStatus::QuestTroupe, command_face) || command_add("faction", "[Find (criteria | all ) | Review (criteria | all) | Reset (id)] - Resets Player's Faction", AccountStatus::QuestTroupe, command_faction) || @@ -189,7 +189,7 @@ int command_init(void) command_add("gassign", "[id] - Assign targetted NPC to predefined wandering grid id", AccountStatus::GMAdmin, command_gassign) || command_add("gearup", "Developer tool to quickly equip a character", AccountStatus::GMMgmt, command_gearup) || command_add("gender", "[0/1/2] - Change your or your target's gender to male/female/neuter", AccountStatus::Guide, command_gender) || - command_add("getplayerburiedcorpsecount", "- Get the target's total number of buried player corpses.", AccountStatus::GMAdmin, command_getplayerburiedcorpsecount) || + command_add("getplayerburiedcorpsecount", "- Get your or your target's total number of buried player corpses.", AccountStatus::GMAdmin, command_getplayerburiedcorpsecount) || command_add("getvariable", "[varname] - Get the value of a variable from the database", AccountStatus::GMMgmt, command_getvariable) || command_add("ginfo", "- get group info on target.", AccountStatus::ApprenticeGuide, command_ginfo) || command_add("giveitem", "[itemid] [charges] - Summon an item onto your target's cursor. Charges are optional.", AccountStatus::GMMgmt, command_giveitem) || diff --git a/zone/gm_commands/getplayerburiedcorpsecount.cpp b/zone/gm_commands/getplayerburiedcorpsecount.cpp index 3be9b205c..1a9c1332a 100755 --- a/zone/gm_commands/getplayerburiedcorpsecount.cpp +++ b/zone/gm_commands/getplayerburiedcorpsecount.cpp @@ -3,25 +3,32 @@ void command_getplayerburiedcorpsecount(Client *c, const Seperator *sep) { - Client *t = c; - + Client *target = c; if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { - t = c->GetTarget()->CastToClient(); - } - else { - c->Message(Chat::White, "You must first select a target!"); - return; + target = c->GetTarget()->CastToClient(); } - uint32 CorpseCount = database.GetCharacterBuriedCorpseCount(t->CharacterID()); - - if (CorpseCount > 0) { - c->Message(Chat::White, "Your target has a total of %u buried corpses.", CorpseCount); - } - else { - c->Message(Chat::White, "Your target doesn't have any buried corpses."); - } - - return; + uint32 corpse_count = database.GetCharacterBuriedCorpseCount(target->CharacterID()); + c->Message( + Chat::White, + fmt::format( + "{} {} {} buried corpse{}.", + ( + c == target ? + "You" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), + c == target ? "have" : "has", + ( + corpse_count ? + std::to_string(corpse_count) : + "no" + ), + corpse_count != 1 ? "s" : "" + ).c_str() + ); } - From 514029a6bb98e6b46c76939b46bc02fa3dafe2bb Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 26 Nov 2021 10:01:04 -0500 Subject: [PATCH 469/624] [Commands] Cleanup #bind Command. (#1829) - Add message and cleanup logic. --- zone/gm_commands/bind.cpp | 61 ++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/zone/gm_commands/bind.cpp b/zone/gm_commands/bind.cpp index 216985ae3..f9a68502e 100755 --- a/zone/gm_commands/bind.cpp +++ b/zone/gm_commands/bind.cpp @@ -2,16 +2,55 @@ void command_bind(Client *c, const Seperator *sep) { - if (c->GetTarget() != 0) { - if (c->GetTarget()->IsClient()) { - c->GetTarget()->CastToClient()->SetBindPoint(); - } - else { - c->Message(Chat::White, "Error: target not a Player"); - } + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); } - else { - c->SetBindPoint(); - } -} + target->SetBindPoint(); + + bool in_persistent_instance = ( + zone->GetInstanceID() != 0 && + zone->IsInstancePersistent() + ); + + auto target_string = ( + c == target ? + "Yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ); + + c->Message( + Chat::White, + fmt::format( + "Set Bind Point for {} | Zone: {} ({}) ID: {} {}", + target_string, + zone->GetLongName(), + zone->GetShortName(), + zone->GetZoneID(), + ( + in_persistent_instance ? + fmt::format( + " Instance ID: {}", + zone->GetInstanceID() + ) : + "" + ) + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Set Bind Point for {} | XYZ: {:.2f}, {:.2f}, {:.2f}", + target_string, + target->GetX(), + target->GetY(), + target->GetZ() + ).c_str() + ); +} From 2dc3ca52dbd4c3c73bdc4506a8ec583b20465b7a Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 26 Nov 2021 10:01:13 -0500 Subject: [PATCH 470/624] [Commands] Cleanup #gm Command. (#1830) - Cleanup message and logic. - Cleanup SetGM() message. --- zone/client.cpp | 8 +++++++- zone/command.cpp | 2 +- zone/gm_commands/gm.cpp | 36 +++++++++++++++++++----------------- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index ed29755ab..fe163a2c7 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -2076,7 +2076,13 @@ bool Client::ChangeFirstName(const char* in_firstname, const char* gmname) void Client::SetGM(bool toggle) { m_pp.gm = toggle ? 1 : 0; m_inv.SetGMInventory((bool)m_pp.gm); - Message(Chat::Red, "You are %s a GM.", m_pp.gm ? "now" : "no longer"); + Message( + Chat::White, + fmt::format( + "You are {} flagged as a GM.", + m_pp.gm ? "now" : "no longer" + ).c_str() + ); SendAppearancePacket(AT_GM, m_pp.gm); Save(); UpdateWho(); diff --git a/zone/command.cpp b/zone/command.cpp index 0e89dc4a9..619406a69 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -195,7 +195,7 @@ int command_init(void) command_add("giveitem", "[itemid] [charges] - Summon an item onto your target's cursor. Charges are optional.", AccountStatus::GMMgmt, command_giveitem) || command_add("givemoney", "[Platinum] [Gold] [Silver] [Copper] - Gives specified amount of money to you or your player target", AccountStatus::GMMgmt, command_givemoney) || command_add("globalview", "Lists all qglobals in cache if you were to do a quest with this target.", AccountStatus::QuestTroupe, command_globalview) || - command_add("gm", "- Turn player target's or your GM flag on or off", AccountStatus::QuestTroupe, command_gm) || + command_add("gm", "[On|Off] - Modify your or your target's GM Flag", AccountStatus::QuestTroupe, command_gm) || command_add("gmspeed", "[on/off] - Turn GM speed hack on/off for you or your player target", AccountStatus::GMAdmin, command_gmspeed) || command_add("gmzone", "[zone_short_name] [zone_version=0] [identifier=gmzone] - Zones to a private GM instance", AccountStatus::GMAdmin, command_gmzone) || command_add("goto", "[playername] or [x y z] [h] - Teleport to the provided coordinates or to your target", AccountStatus::Steward, command_goto) || diff --git a/zone/gm_commands/gm.cpp b/zone/gm_commands/gm.cpp index 2920c5026..d781bb93a 100755 --- a/zone/gm_commands/gm.cpp +++ b/zone/gm_commands/gm.cpp @@ -2,26 +2,28 @@ void command_gm(Client *c, const Seperator *sep) { - bool state = atobool(sep->arg[1]); - Client *t = c; + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #gm [On|Off]"); + return; + } + bool gm_flag = atobool(sep->arg[1]); + Client *target = c; if (c->GetTarget() && c->GetTarget()->IsClient()) { - t = c->GetTarget()->CastToClient(); + target = c->GetTarget()->CastToClient(); } - if (sep->arg[1][0] != 0) { - t->SetGM(state); - c->Message(Chat::White, "%s is %s a GM.", t->GetName(), state ? "now" : "no longer"); - } - else { - c->Message(Chat::White, "Usage: #gm [on/off]"); + target->SetGM(gm_flag); + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "{} ({}) is {} flagged as a GM.", + target->GetCleanName(), + target->GetID(), + gm_flag ? "now" : "no longer" + ).c_str() + ); } } - -// there's no need for this, as /summon already takes care of it -// this command is here for reference but it is not added to the -// list above - -//To whoever wrote the above: And what about /kill, /zone, /zoneserver, etc? -//There is a reason for the # commands: so that admins can specifically enable certain -//commands for their users. Some might want users to #summon but not to /kill. Cant do that if they are a GM From e87b8e268226685eaef61cc4154b94afe966f8e5 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 26 Nov 2021 10:01:35 -0500 Subject: [PATCH 471/624] [Commands] Cleanup #gender Command. (#1832) - Cleanup message and logic. - Cleanup other spots using similar logic so they're all uniform. --- zone/command.cpp | 65 ++++++++++--------- zone/gm_commands/castspell.cpp | 41 +++++------- zone/gm_commands/gender.cpp | 47 +++++++++++--- .../getplayerburiedcorpsecount.cpp | 7 +- zone/gm_commands/ginfo.cpp | 23 +++++-- zone/gm_commands/givemoney.cpp | 10 ++- zone/gm_commands/loc.cpp | 14 ++-- zone/gm_commands/nukeitem.cpp | 20 +++++- zone/gm_commands/permagender.cpp | 15 ++++- zone/gm_commands/permarace.cpp | 10 ++- zone/gm_commands/scribespells.cpp | 26 ++++---- zone/gm_commands/setaapts.cpp | 18 +++-- zone/gm_commands/setaaxp.cpp | 20 ++++-- zone/gm_commands/setcrystals.cpp | 25 ++++--- zone/gm_commands/setpvppoints.cpp | 10 ++- zone/gm_commands/stun.cpp | 30 ++++----- zone/gm_commands/traindisc.cpp | 17 +++-- 17 files changed, 258 insertions(+), 140 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 619406a69..6855cd72a 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -1099,39 +1099,40 @@ void command_emptyinventory(Client *c, const Seperator *sep) } } - if (c != target) { - auto target_name = target->GetCleanName(); - if (removed_count) { - c->Message( - Chat::White, - fmt::format( - "Inventory cleared for {}, {} items deleted.", - target_name, - removed_count - ).c_str() - ); - } else { - c->Message( - Chat::White, - fmt::format( - "{} has no items to delete.", - target_name - ).c_str() - ); - } + if (removed_count) { + c->Message( + Chat::White, + fmt::format( + "Inventory cleared for {}, {} items deleted.", + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), + removed_count + ).c_str() + ); } else { - if (removed_count) { - c->Message( - Chat::White, - fmt::format( - "Your inventory has been cleared, {} items deleted.", - removed_count - ).c_str() - ); - } else { - c->Message(Chat::White, "You have no items to delete."); - } - } + c->Message( + Chat::White, + fmt::format( + "{} no items to delete.", + ( + c == target ? + "You have" : + fmt::format( + "{} ({}) has", + target->GetCleanName(), + target->GetID() + ) + ) + ).c_str() + ); + } } // All new code added to command.cpp should be BEFORE this comment line. Do no append code to this file below the BOTS code block. diff --git a/zone/gm_commands/castspell.cpp b/zone/gm_commands/castspell.cpp index b4410084d..68e2e1801 100755 --- a/zone/gm_commands/castspell.cpp +++ b/zone/gm_commands/castspell.cpp @@ -48,29 +48,24 @@ void command_castspell(Client *c, const Seperator *sep) c->CastSpell(spell_id, target->GetID(), EQ::spells::CastingSlot::Item, spells[spell_id].cast_time); } - if (c != target) { - c->Message( - Chat::White, - fmt::format( - "Cast {} ({}) on {}{}.", - GetSpellName(spell_id), - spell_id, - target->GetCleanName(), - instant_cast ? " instantly" : "" - ).c_str() - ); - } - else { - c->Message( - Chat::White, - fmt::format( - "Cast {} ({}) on yourself{}.", - GetSpellName(spell_id), - spell_id, - instant_cast ? " instantly" : "" - ).c_str() - ); - } + c->Message( + Chat::White, + fmt::format( + "Cast {} ({}) on {}{}.", + GetSpellName(spell_id), + spell_id, + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), + instant_cast ? " instantly" : "" + ).c_str() + ); } } } diff --git a/zone/gm_commands/gender.cpp b/zone/gm_commands/gender.cpp index 0051ddd43..000d65f23 100755 --- a/zone/gm_commands/gender.cpp +++ b/zone/gm_commands/gender.cpp @@ -2,16 +2,45 @@ void command_gender(Client *c, const Seperator *sep) { - Mob *t = c->CastToMob(); + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #gender [Gender ID]"); + c->Message(Chat::White, "Genders: 0 = Male, 1 = Female, 2 = Neuter"); + return; + } - if (sep->IsNumber(1) && atoi(sep->arg[1]) >= 0 && atoi(sep->arg[1]) <= 500) { - if ((c->GetTarget()) && c->Admin() >= commandGenderOthers) { - t = c->GetTarget(); - } - t->SendIllusionPacket(t->GetRace(), atoi(sep->arg[1])); + Mob *target = c; + if (c->GetTarget() && c->Admin() >= commandGenderOthers) { + target = c->GetTarget(); } - else { - c->Message(Chat::White, "Usage: #gender [0/1/2]"); + + auto gender_id = std::stoi(sep->arg[1]); + if (gender_id < 0 || gender_id > 2) { + c->Message(Chat::White, "Usage: #gender [Gender ID]"); + c->Message(Chat::White, "Genders: 0 = Male, 1 = Female, 2 = Neuter"); + return; } + + target->SendIllusionPacket( + target->GetRace(), + gender_id + ); + + c->Message( + Chat::White, + fmt::format( + "Gender changed for {} to {} ({}).", + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), + GetGenderName(gender_id), + gender_id + ).c_str() + ); } - diff --git a/zone/gm_commands/getplayerburiedcorpsecount.cpp b/zone/gm_commands/getplayerburiedcorpsecount.cpp index 1a9c1332a..20e82bac4 100755 --- a/zone/gm_commands/getplayerburiedcorpsecount.cpp +++ b/zone/gm_commands/getplayerburiedcorpsecount.cpp @@ -12,17 +12,16 @@ void command_getplayerburiedcorpsecount(Client *c, const Seperator *sep) c->Message( Chat::White, fmt::format( - "{} {} {} buried corpse{}.", + "{} {} buried corpse{}.", ( c == target ? - "You" : + "You have" : fmt::format( - "{} ({})", + "{} ({}) has", target->GetCleanName(), target->GetID() ) ), - c == target ? "have" : "has", ( corpse_count ? std::to_string(corpse_count) : diff --git a/zone/gm_commands/ginfo.cpp b/zone/gm_commands/ginfo.cpp index 53af4fbf6..a64b11fff 100755 --- a/zone/gm_commands/ginfo.cpp +++ b/zone/gm_commands/ginfo.cpp @@ -13,9 +13,16 @@ void command_ginfo(Client *c, const Seperator *sep) c->Message( Chat::White, fmt::format( - "{} {} not in a group.", - c == target ? "You" : target->GetCleanName(), - c == target ? "are" : "is" + "{} not in a group.", + ( + c == target ? + "You are" : + fmt::format( + "{} ({}) is", + target->GetCleanName(), + target->GetID() + ) + ) ).c_str() ); return; @@ -25,7 +32,15 @@ void command_ginfo(Client *c, const Seperator *sep) Chat::White, fmt::format( "Group Info for {} | ID: {} Members: {}", - c == target ? "Yourself" : target->GetCleanName(), + ( + c == target ? + "Yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), target_group->GetID(), target_group->GroupCount() ).c_str() diff --git a/zone/gm_commands/givemoney.cpp b/zone/gm_commands/givemoney.cpp index 3e3727292..705ab7461 100755 --- a/zone/gm_commands/givemoney.cpp +++ b/zone/gm_commands/givemoney.cpp @@ -36,7 +36,15 @@ void command_givemoney(Client *c, const Seperator *sep) fmt::format( "Added {} to {}.", ConvertMoneyToString(platinum, gold, silver, copper), - c == target ? "yourself" : target->GetCleanName() + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ) ).c_str() ); } diff --git a/zone/gm_commands/loc.cpp b/zone/gm_commands/loc.cpp index 79d734711..c45513dc9 100755 --- a/zone/gm_commands/loc.cpp +++ b/zone/gm_commands/loc.cpp @@ -12,15 +12,15 @@ void command_loc(Client *c, const Seperator *sep) c->Message( Chat::White, fmt::format( - "{} Location | XYZ: {:.2f}, {:.2f}, {:.2f} Heading: {:.2f}", + "Location for {} | XYZ: {:.2f}, {:.2f}, {:.2f} Heading: {:.2f}", ( c == target ? - "Your" : - fmt::format( - "{} ({})", - target->GetCleanName(), - target->GetID() - ) + "Yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) ), target_position.x, target_position.y, diff --git a/zone/gm_commands/nukeitem.cpp b/zone/gm_commands/nukeitem.cpp index 60fccd00f..1856b9b1f 100755 --- a/zone/gm_commands/nukeitem.cpp +++ b/zone/gm_commands/nukeitem.cpp @@ -23,7 +23,15 @@ void command_nukeitem(Client *c, const Seperator *sep) deleted_count, database.CreateItemLink(item_id), item_id, - c == target ? "yourself" : target->GetCleanName() + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ) ).c_str() ); } else { @@ -33,7 +41,15 @@ void command_nukeitem(Client *c, const Seperator *sep) "Could not find any {} ({}) to delete from {}.", database.CreateItemLink(item_id), item_id, - c == target ? "yourself" : target->GetCleanName() + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ) ).c_str() ); } diff --git a/zone/gm_commands/permagender.cpp b/zone/gm_commands/permagender.cpp index 8520d3374..bdfd33013 100755 --- a/zone/gm_commands/permagender.cpp +++ b/zone/gm_commands/permagender.cpp @@ -15,6 +15,11 @@ void command_permagender(Client *c, const Seperator *sep) } auto gender_id = std::stoi(sep->arg[1]); + if (gender_id < 0 || gender_id > 2) { + c->Message(Chat::White, "Usage: #permagender [Gender ID]"); + c->Message(Chat::White, "Genders: 0 = Male, 1 = Female, 2 = Neuter"); + return; + } LogInfo("Gender changed by {} for {} to {} ({})", c->GetCleanName(), @@ -31,7 +36,15 @@ void command_permagender(Client *c, const Seperator *sep) Chat::White, fmt::format( "Gender changed for {} to {} ({}).", - c == target ? "yourself" : target->GetCleanName(), + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), GetGenderName(gender_id), gender_id ).c_str() diff --git a/zone/gm_commands/permarace.cpp b/zone/gm_commands/permarace.cpp index d6065a797..bee8e6dc2 100755 --- a/zone/gm_commands/permarace.cpp +++ b/zone/gm_commands/permarace.cpp @@ -36,7 +36,15 @@ void command_permarace(Client *c, const Seperator *sep) Chat::White, fmt::format( "Race changed for {} to {} ({}).", - c == target ? "yourself" : target->GetCleanName(), + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), GetRaceIDName(race_id), race_id ).c_str() diff --git a/zone/gm_commands/scribespells.cpp b/zone/gm_commands/scribespells.cpp index ef30d6384..2d084d9df 100755 --- a/zone/gm_commands/scribespells.cpp +++ b/zone/gm_commands/scribespells.cpp @@ -13,12 +13,11 @@ void command_scribespells(Client *c, const Seperator *sep) } uint8 rule_max_level = (uint8) RuleI(Character, MaxLevel); - uint8 max_level = (uint8) std::stoi(sep->arg[1]); - uint8 min_level = ( + uint8 max_level = (uint8) std::stoi(sep->arg[1]); + uint8 min_level = ( sep->IsNumber(2) ? - (uint8) - std::stoi(sep->arg[2]) : - 1 + (uint8) std::stoi(sep->arg[2]) : + 1 ); // Default to Level 1 if there isn't a 2nd argument if (!c->GetGM()) { // Default to Character:MaxLevel if we're not a GM and Level is higher than the max level @@ -42,15 +41,18 @@ void command_scribespells(Client *c, const Seperator *sep) } uint16 scribed_spells = target->ScribeSpells(min_level, max_level); - if (target != c) { + if (c != target) { std::string spell_message = ( scribed_spells > 0 ? - ( - scribed_spells == 1 ? - "A new spell" : - fmt::format("{} New spells", scribed_spells) - ) : - "No new spells" + ( + scribed_spells == 1 ? + "A new spell" : + fmt::format( + "{} New spells", + scribed_spells + ) + ) : + "No new spells" ); c->Message( Chat::White, diff --git a/zone/gm_commands/setaapts.cpp b/zone/gm_commands/setaapts.cpp index e8222a08f..cb5e9f3f0 100755 --- a/zone/gm_commands/setaapts.cpp +++ b/zone/gm_commands/setaapts.cpp @@ -18,10 +18,10 @@ void command_setaapts(Client *c, const Seperator *sep) std::string aa_type = str_tolower(sep->arg[1]); std::string group_raid_string; - uint32 aa_points = static_cast(std::min(std::stoull(sep->arg[2]), (unsigned long long) 2000000000)); - bool is_aa = aa_type.find("aa") != std::string::npos; - bool is_group = aa_type.find("group") != std::string::npos; - bool is_raid = aa_type.find("raid") != std::string::npos; + uint32 aa_points = static_cast(std::min(std::stoull(sep->arg[2]), (unsigned long long) 2000000000)); + bool is_aa = aa_type.find("aa") != std::string::npos; + bool is_group = aa_type.find("group") != std::string::npos; + bool is_raid = aa_type.find("raid") != std::string::npos; if (!is_aa && !is_group && !is_raid) { c->Message(Chat::White, "Usage: #setaapts [AA|Group|Raid] [AA Amount]"); return; @@ -48,7 +48,15 @@ void command_setaapts(Client *c, const Seperator *sep) std::string aa_message = fmt::format( "{} now {} {} {}AA Point{}.", - c == target ? "You" : target->GetCleanName(), + ( + c == target ? + "You" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), c == target ? "have" : "has", aa_points, group_raid_string, diff --git a/zone/gm_commands/setaaxp.cpp b/zone/gm_commands/setaaxp.cpp index 32c060aed..081a7d1d6 100755 --- a/zone/gm_commands/setaaxp.cpp +++ b/zone/gm_commands/setaaxp.cpp @@ -16,15 +16,15 @@ void command_setaaxp(Client *c, const Seperator *sep) target = c->GetTarget()->CastToClient(); } - std::string aa_type = str_tolower(sep->arg[1]); + std::string aa_type = str_tolower(sep->arg[1]); std::string group_raid_string; - uint32 aa_experience = static_cast(std::min( + uint32 aa_experience = static_cast(std::min( std::stoull(sep->arg[2]), (unsigned long long) 2000000000 )); - bool is_aa = aa_type.find("aa") != std::string::npos; - bool is_group = aa_type.find("group") != std::string::npos; - bool is_raid = aa_type.find("raid") != std::string::npos; + bool is_aa = aa_type.find("aa") != std::string::npos; + bool is_group = aa_type.find("group") != std::string::npos; + bool is_raid = aa_type.find("raid") != std::string::npos; if (!is_aa && !is_group && !is_raid) { c->Message(Chat::White, "Usage: #setaaxp [AA|Group|Raid] [AA Experience]"); return; @@ -54,7 +54,15 @@ void command_setaaxp(Client *c, const Seperator *sep) std::string aa_exp_message = fmt::format( "{} now {} {} {}AA Experience.", - c == target ? "You" : target->GetCleanName(), + ( + c == target ? + "You" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), c == target ? "have" : "has", aa_experience, group_raid_string diff --git a/zone/gm_commands/setcrystals.cpp b/zone/gm_commands/setcrystals.cpp index 32d22f449..4f7f1f14f 100755 --- a/zone/gm_commands/setcrystals.cpp +++ b/zone/gm_commands/setcrystals.cpp @@ -13,13 +13,13 @@ void command_setcrystals(Client *c, const Seperator *sep) target = c->GetTarget()->CastToClient(); } - std::string crystal_type = str_tolower(sep->arg[1]); - uint32 crystal_amount = static_cast(std::min( + std::string crystal_type = str_tolower(sep->arg[1]); + uint32 crystal_amount = static_cast(std::min( std::stoull(sep->arg[2]), (unsigned long long) 2000000000 )); - bool is_ebon = crystal_type.find("ebon") != std::string::npos; - bool is_radiant = crystal_type.find("radiant") != std::string::npos; + bool is_ebon = crystal_type.find("ebon") != std::string::npos; + bool is_radiant = crystal_type.find("radiant") != std::string::npos; if (!is_ebon && !is_radiant) { c->Message(Chat::White, "Usage: #setcrystals [Ebon|Radiant] [Crystal Amount]"); return; @@ -27,15 +27,14 @@ void command_setcrystals(Client *c, const Seperator *sep) uint32 crystal_item_id = ( is_ebon ? - RuleI(Zone, EbonCrystalItemID) : - RuleI(Zone, RadiantCrystalItemID) + RuleI(Zone, EbonCrystalItemID) : + RuleI(Zone, RadiantCrystalItemID) ); auto crystal_link = database.CreateItemLink(crystal_item_id); if (is_radiant) { target->SetRadiantCrystals(crystal_amount); - } - else { + } else { target->SetEbonCrystals(crystal_amount); } @@ -43,7 +42,15 @@ void command_setcrystals(Client *c, const Seperator *sep) Chat::White, fmt::format( "{} now {} {} {}.", - c == target ? "You" : target->GetCleanName(), + ( + c == target ? + "You" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), c == target ? "have" : "has", crystal_amount, crystal_link diff --git a/zone/gm_commands/setpvppoints.cpp b/zone/gm_commands/setpvppoints.cpp index 27eae29cd..d124cb6eb 100755 --- a/zone/gm_commands/setpvppoints.cpp +++ b/zone/gm_commands/setpvppoints.cpp @@ -19,7 +19,15 @@ void command_setpvppoints(Client *c, const Seperator *sep) target->SendPVPStats(); std::string pvp_message = fmt::format( "{} now {} {} PVP Point{}.", - c == target ? "You" : target->GetCleanName(), + ( + c == target ? + "You" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), c == target ? "have" : "has", pvp_points, pvp_points != 1 ? "s" : "" diff --git a/zone/gm_commands/stun.cpp b/zone/gm_commands/stun.cpp index 01acce4f0..53bfad8cc 100755 --- a/zone/gm_commands/stun.cpp +++ b/zone/gm_commands/stun.cpp @@ -19,12 +19,10 @@ void command_stun(Client *c, const Seperator *sep) target = c->GetTarget(); if (target->IsClient()) { target->CastToClient()->Stun(duration); - } - else if (target->IsNPC()) { + } else if (target->IsNPC()) { target->CastToNPC()->Stun(duration); } - } - else { + } else { c->Stun(duration); } @@ -34,12 +32,12 @@ void command_stun(Client *c, const Seperator *sep) "You stunned {} for {}.", ( c == target ? - "yourself" : - fmt::format( - "{} ({})", - target->GetCleanName(), - target->GetID() - ) + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) ), ConvertMillisecondsToTime(duration) ) : @@ -47,12 +45,12 @@ void command_stun(Client *c, const Seperator *sep) "You unstunned {}.", ( c == target ? - "yourself" : - fmt::format( - "{} ({})", - target->GetCleanName(), - target->GetID() - ) + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) ) ) ); diff --git a/zone/gm_commands/traindisc.cpp b/zone/gm_commands/traindisc.cpp index 1e3226a85..7e5fff255 100755 --- a/zone/gm_commands/traindisc.cpp +++ b/zone/gm_commands/traindisc.cpp @@ -42,15 +42,18 @@ void command_traindisc(Client *c, const Seperator *sep) } uint16 learned_disciplines = target->LearnDisciplines(min_level, max_level); - if (target != c) { + if (c != target) { std::string discipline_message = ( learned_disciplines > 0 ? - ( - learned_disciplines == 1 ? - "A new discipline" : - fmt::format("{} New disciplines", learned_disciplines) - ) : - "No new disciplines" + ( + learned_disciplines == 1 ? + "A new discipline" : + fmt::format( + "{} New disciplines", + learned_disciplines + ) + ) : + "No new disciplines" ); c->Message( Chat::White, From 8b54bb34e442991fbc584b3e19ef8ad7098a6f17 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 26 Nov 2021 13:56:45 -0500 Subject: [PATCH 472/624] [Commands] Cleanup #gmspeed Command. (#1831) * [Commands] Cleanup #gmspeed Command. - Cleanup message and logic. * Update gmspeed.cpp * Update gmspeed.cpp --- zone/command.cpp | 2 +- zone/gm_commands/gmspeed.cpp | 55 +++++++++++++++++++++++++++++------- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 6855cd72a..1a254eda1 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -196,7 +196,7 @@ int command_init(void) command_add("givemoney", "[Platinum] [Gold] [Silver] [Copper] - Gives specified amount of money to you or your player target", AccountStatus::GMMgmt, command_givemoney) || command_add("globalview", "Lists all qglobals in cache if you were to do a quest with this target.", AccountStatus::QuestTroupe, command_globalview) || command_add("gm", "[On|Off] - Modify your or your target's GM Flag", AccountStatus::QuestTroupe, command_gm) || - command_add("gmspeed", "[on/off] - Turn GM speed hack on/off for you or your player target", AccountStatus::GMAdmin, command_gmspeed) || + command_add("gmspeed", "[On|Off] - Turn GM Speed On or Off for you or your player target", AccountStatus::GMAdmin, command_gmspeed) || command_add("gmzone", "[zone_short_name] [zone_version=0] [identifier=gmzone] - Zones to a private GM instance", AccountStatus::GMAdmin, command_gmzone) || command_add("goto", "[playername] or [x y z] [h] - Teleport to the provided coordinates or to your target", AccountStatus::Steward, command_goto) || command_add("grid", "[add/delete] [grid_num] [wandertype] [pausetype] - Create/delete a wandering grid", AccountStatus::GMAreas, command_grid) || diff --git a/zone/gm_commands/gmspeed.cpp b/zone/gm_commands/gmspeed.cpp index 424c68ef5..30b5eccfa 100755 --- a/zone/gm_commands/gmspeed.cpp +++ b/zone/gm_commands/gmspeed.cpp @@ -2,19 +2,54 @@ void command_gmspeed(Client *c, const Seperator *sep) { - bool state = atobool(sep->arg[1]); - Client *t = c; + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #gmspeed [On|Off]"); + return; + } + bool gm_speed_flag = atobool(sep->arg[1]); + Client *target = c; if (c->GetTarget() && c->GetTarget()->IsClient()) { - t = c->GetTarget()->CastToClient(); + target = c->GetTarget()->CastToClient(); } - if (sep->arg[1][0] != 0) { - database.SetGMSpeed(t->AccountID(), state ? 1 : 0); - c->Message(Chat::White, "Turning GMSpeed %s for %s (zone to take effect)", state ? "On" : "Off", t->GetName()); - } - else { - c->Message(Chat::White, "Usage: #gmspeed [on/off]"); - } + database.SetGMSpeed( + target->AccountID(), + gm_speed_flag ? 1 : 0 + ); + + c->Message( + Chat::White, + fmt::format( + "Turning GM Speed {} for {}.", + gm_speed_flag ? "on" : "off", + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ) + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Note: {} must zone for it to take effect.", + ( + c == target ? + "You" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ) + ).c_str() + ); } From 774e0c7faae059c823ee22f9dcb59acd8b585299 Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Fri, 26 Nov 2021 15:26:07 -0600 Subject: [PATCH 473/624] Do not set teleport doors to Open (#1786) --- zone/doors.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zone/doors.cpp b/zone/doors.cpp index 83f8dcb8a..6b505c677 100644 --- a/zone/doors.cpp +++ b/zone/doors.cpp @@ -383,7 +383,9 @@ void Doors::HandleClick(Client* sender, uint8 trigger) { if (!IsDoorOpen() || (open_type == 58)) { if (!disable_timer) close_timer.Start(); - SetOpenState(true); + + if(strncmp(destination_zone_name, "NONE", strlen("NONE")) == 0) + SetOpenState(true); } else { close_timer.Disable(); if (!disable_timer) From 4507b063f53fb03e8c8357bd7a21cb9f53bca044 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Fri, 26 Nov 2021 21:33:49 -0500 Subject: [PATCH 474/624] Switch server to use new style ManaChange_Struct (#1843) This will allow us to fix some bugs the current handling has. Note: the decoder isn't needed since the client always sends it up as a 0 length packet. --- common/eq_packet_structs.h | 1 + common/patches/rof.cpp | 14 -------------- common/patches/rof2.cpp | 14 -------------- common/patches/rof2_ops.h | 1 - common/patches/rof_ops.h | 1 - common/patches/sod.cpp | 14 -------------- common/patches/sod_ops.h | 1 - common/patches/sof.cpp | 14 -------------- common/patches/sof_ops.h | 1 - common/patches/titanium.cpp | 13 +++++++++++++ common/patches/titanium_ops.h | 1 + common/patches/uf.cpp | 14 -------------- common/patches/uf_ops.h | 1 - zone/client.cpp | 4 +--- zone/spells.cpp | 2 ++ 15 files changed, 18 insertions(+), 78 deletions(-) diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 304411d03..872f89859 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -448,6 +448,7 @@ struct ManaChange_Struct /*08*/ uint32 spell_id; /*12*/ uint8 keepcasting; // won't stop the cast. Change mana while casting? /*13*/ uint8 padding[3]; // client doesn't read it, garbage data seems like +/*16*/ int32 slot; // -1 normal, otherwise clear ETA and GCD }; struct SwapSpell_Struct diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 8e40e43ce..908ecc26b 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -1634,20 +1634,6 @@ namespace RoF FINISH_ENCODE(); } - ENCODE(OP_ManaChange) - { - ENCODE_LENGTH_EXACT(ManaChange_Struct); - SETUP_DIRECT_ENCODE(ManaChange_Struct, structs::ManaChange_Struct); - - OUT(new_mana); - OUT(stamina); - OUT(spell_id); - OUT(keepcasting); - eq->slot = -1; // this is spell gem slot. It's -1 in normal operation - - FINISH_ENCODE(); - } - ENCODE(OP_MercenaryDataResponse) { //consume the packet diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 03f636560..e6404a62a 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -1683,20 +1683,6 @@ namespace RoF2 FINISH_ENCODE(); } - ENCODE(OP_ManaChange) - { - ENCODE_LENGTH_EXACT(ManaChange_Struct); - SETUP_DIRECT_ENCODE(ManaChange_Struct, structs::ManaChange_Struct); - - OUT(new_mana); - OUT(stamina); - OUT(spell_id); - OUT(keepcasting); - eq->slot = -1; // this is spell gem slot. It's -1 in normal operation - - FINISH_ENCODE(); - } - ENCODE(OP_MercenaryDataResponse) { //consume the packet diff --git a/common/patches/rof2_ops.h b/common/patches/rof2_ops.h index 22b6e6662..130686cdf 100644 --- a/common/patches/rof2_ops.h +++ b/common/patches/rof2_ops.h @@ -93,7 +93,6 @@ E(OP_ItemVerifyReply) E(OP_LeadershipExpUpdate) E(OP_LogServer) E(OP_LootItem) -E(OP_ManaChange) E(OP_MercenaryDataResponse) E(OP_MercenaryDataUpdate) E(OP_MoveItem) diff --git a/common/patches/rof_ops.h b/common/patches/rof_ops.h index f06834d21..7cca08e61 100644 --- a/common/patches/rof_ops.h +++ b/common/patches/rof_ops.h @@ -79,7 +79,6 @@ E(OP_ItemVerifyReply) E(OP_LeadershipExpUpdate) E(OP_LogServer) E(OP_LootItem) -E(OP_ManaChange) E(OP_MercenaryDataResponse) E(OP_MercenaryDataUpdate) E(OP_MoveItem) diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index 034679bd5..32e58e432 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -1170,20 +1170,6 @@ namespace SoD FINISH_ENCODE(); } - ENCODE(OP_ManaChange) - { - ENCODE_LENGTH_EXACT(ManaChange_Struct); - SETUP_DIRECT_ENCODE(ManaChange_Struct, structs::ManaChange_Struct); - - OUT(new_mana); - OUT(stamina); - OUT(spell_id); - OUT(keepcasting); - eq->slot = -1; // this is spell gem slot. It's -1 in normal operation - - FINISH_ENCODE(); - } - ENCODE(OP_MercenaryDataResponse) { //consume the packet diff --git a/common/patches/sod_ops.h b/common/patches/sod_ops.h index 5216a77ce..c98ab5ff0 100644 --- a/common/patches/sod_ops.h +++ b/common/patches/sod_ops.h @@ -63,7 +63,6 @@ E(OP_ItemVerifyReply) E(OP_LeadershipExpUpdate) E(OP_LogServer) E(OP_LootItem) -E(OP_ManaChange) E(OP_MercenaryDataResponse) E(OP_MercenaryDataUpdate) E(OP_MoveItem) diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index 56fbba8f4..ff77e788b 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -966,20 +966,6 @@ namespace SoF FINISH_ENCODE(); } - ENCODE(OP_ManaChange) - { - ENCODE_LENGTH_EXACT(ManaChange_Struct); - SETUP_DIRECT_ENCODE(ManaChange_Struct, structs::ManaChange_Struct); - - OUT(new_mana); - OUT(stamina); - OUT(spell_id); - OUT(keepcasting); - eq->slot = -1; // this is spell gem slot. It's -1 in normal operation - - FINISH_ENCODE(); - } - ENCODE(OP_MemorizeSpell) { ENCODE_LENGTH_EXACT(MemorizeSpell_Struct); diff --git a/common/patches/sof_ops.h b/common/patches/sof_ops.h index 653bb5979..0dd34e060 100644 --- a/common/patches/sof_ops.h +++ b/common/patches/sof_ops.h @@ -59,7 +59,6 @@ E(OP_ItemVerifyReply) E(OP_LeadershipExpUpdate) E(OP_LogServer) E(OP_LootItem) -E(OP_ManaChange) E(OP_MemorizeSpell) E(OP_MoveItem) E(OP_NewSpawn) diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index 200beebf5..25ef3be5c 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -932,6 +932,19 @@ namespace Titanium FINISH_ENCODE(); } + ENCODE(OP_ManaChange) + { + ENCODE_LENGTH_EXACT(ManaChange_Struct); + SETUP_DIRECT_ENCODE(ManaChange_Struct, structs::ManaChange_Struct); + + OUT(new_mana); + OUT(stamina); + OUT(spell_id); + OUT(keepcasting); + + FINISH_ENCODE(); + } + ENCODE(OP_MemorizeSpell) { ENCODE_LENGTH_EXACT(MemorizeSpell_Struct); diff --git a/common/patches/titanium_ops.h b/common/patches/titanium_ops.h index 961c850fe..c7557a32f 100644 --- a/common/patches/titanium_ops.h +++ b/common/patches/titanium_ops.h @@ -55,6 +55,7 @@ E(OP_ItemPacket) E(OP_LeadershipExpUpdate) E(OP_LFGuild) E(OP_LootItem) +E(OP_ManaChange) E(OP_MemorizeSpell) E(OP_MoveItem) E(OP_OnLevelMessage) diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index 81c76aa62..1e33ed307 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -1390,20 +1390,6 @@ namespace UF FINISH_ENCODE(); } - ENCODE(OP_ManaChange) - { - ENCODE_LENGTH_EXACT(ManaChange_Struct); - SETUP_DIRECT_ENCODE(ManaChange_Struct, structs::ManaChange_Struct); - - OUT(new_mana); - OUT(stamina); - OUT(spell_id); - OUT(keepcasting); - eq->slot = -1; // this is spell gem slot. It's -1 in normal operation - - FINISH_ENCODE(); - } - ENCODE(OP_MercenaryDataResponse) { //consume the packet diff --git a/common/patches/uf_ops.h b/common/patches/uf_ops.h index 76b7e014b..5d728f067 100644 --- a/common/patches/uf_ops.h +++ b/common/patches/uf_ops.h @@ -68,7 +68,6 @@ E(OP_ItemVerifyReply) E(OP_LeadershipExpUpdate) E(OP_LogServer) E(OP_LootItem) -E(OP_ManaChange) E(OP_MercenaryDataResponse) E(OP_MercenaryDataUpdate) E(OP_MoveItem) diff --git a/zone/client.cpp b/zone/client.cpp index fe163a2c7..f09bcefcd 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -1869,9 +1869,7 @@ void Client::CheckManaEndUpdate() { mana_change->stamina = current_endurance; mana_change->spell_id = casting_spell_id; mana_change->keepcasting = 1; - mana_change->padding[0] = 0; - mana_change->padding[1] = 0; - mana_change->padding[2] = 0; + mana_change->slot = -1; outapp->priority = 6; QueuePacket(outapp); safe_delete(outapp); diff --git a/zone/spells.cpp b/zone/spells.cpp index 863646492..3d333d768 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1001,6 +1001,7 @@ void Mob::StopCasting() mc->stamina = GetEndurance(); mc->spell_id = casting_spell_id; mc->keepcasting = 0; + mc->slot = -1; c->FastQueuePacket(&outapp); } ZeroCastingVars(); @@ -5218,6 +5219,7 @@ void Mob::SendSpellBarEnable(uint16 spell_id) manachange->spell_id = spell_id; manachange->stamina = CastToClient()->GetEndurance(); manachange->keepcasting = 0; + manachange->slot = -1; outapp->priority = 6; CastToClient()->QueuePacket(outapp); safe_delete(outapp); From 298ae3e3ba3584f481f29bc018c879bd579e1952 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 27 Nov 2021 09:45:13 -0500 Subject: [PATCH 475/624] [Bug Fix] Fix possible crash with #killallnpcs. (#1846) --- zone/gm_commands/killallnpcs.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zone/gm_commands/killallnpcs.cpp b/zone/gm_commands/killallnpcs.cpp index bdb2750f0..e1651fe02 100755 --- a/zone/gm_commands/killallnpcs.cpp +++ b/zone/gm_commands/killallnpcs.cpp @@ -11,6 +11,10 @@ void command_killallnpcs(Client *c, const Seperator *sep) for (auto& npc_entity : entity_list.GetNPCList()) { auto entity_id = npc_entity.first; auto npc = npc_entity.second; + if (!npc) { + continue; + } + std::string entity_name = str_tolower(npc->GetName()); if ( ( From 8566662d56a2c3937f1dbd33d1023b7e2b829195 Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Sat, 27 Nov 2021 08:45:57 -0600 Subject: [PATCH 476/624] [Bug Fix] SendSpellBarEnable sends correct slotid to fix spellbar on RoF2 (#1848) * SendSpellBarEnable sends correct slotid to fix spellbar on RoF2 * Send correct data when using StopCasting() to re-enable spellbar --- zone/client.h | 1 + zone/spells.cpp | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/zone/client.h b/zone/client.h index 68a86500f..b8592d1eb 100644 --- a/zone/client.h +++ b/zone/client.h @@ -793,6 +793,7 @@ public: void UnmemSpellBySpellID(int32 spell_id); void UnmemSpellAll(bool update_client = true); uint16 FindMemmedSpellBySlot(int slot); + int FindMemmedSpellByID(uint16 spell_id); int MemmedCount(); std::vector GetLearnableDisciplines(uint8 min_level = 1, uint8 max_level = 0); std::vector GetLearnedDisciplines(); diff --git a/zone/spells.cpp b/zone/spells.cpp index 3d333d768..e5581d7af 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -995,13 +995,18 @@ void Mob::StopCasting() c->ResetAlternateAdvancementTimer(casting_spell_aa_id); } + int casting_slot = -1; + if (casting_spell_slot < CastingSlot::MaxGems) { + casting_slot = static_cast(casting_spell_slot); + } + auto outapp = new EQApplicationPacket(OP_ManaChange, sizeof(ManaChange_Struct)); auto mc = (ManaChange_Struct *)outapp->pBuffer; mc->new_mana = GetMana(); mc->stamina = GetEndurance(); mc->spell_id = casting_spell_id; mc->keepcasting = 0; - mc->slot = -1; + mc->slot = casting_slot; c->FastQueuePacket(&outapp); } ZeroCastingVars(); @@ -5219,7 +5224,7 @@ void Mob::SendSpellBarEnable(uint16 spell_id) manachange->spell_id = spell_id; manachange->stamina = CastToClient()->GetEndurance(); manachange->keepcasting = 0; - manachange->slot = -1; + manachange->slot = CastToClient()->FindMemmedSpellByID(spell_id); outapp->priority = 6; CastToClient()->QueuePacket(outapp); safe_delete(outapp); @@ -5428,6 +5433,15 @@ int Client::MemmedCount() { return memmed_count; } +int Client::FindMemmedSpellByID(uint16 spell_id) { + for (int i = 0; i < EQ::spells::SPELL_GEM_COUNT; i++) { + if (m_pp.mem_spells[i] == spell_id) { + return i; + } + } + return -1; +} + void Client::ScribeSpell(uint16 spell_id, int slot, bool update_client, bool defer_save) { From 6a28828e08eaa4a418c809537300163f8840cd42 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 27 Nov 2021 12:10:08 -0500 Subject: [PATCH 477/624] [API] mob->AppearanceEffects improved functionality. (#1821) * appearanceffectscript * update * debugged * [API] SendAppearanceEffect update * [API] SendAppearanceEffect update * [API] SendAppearanceEffect Upates perl method RemoveAppearanceEffect to remove the apperanceeffect * [API} AppearanceEffects update * [API] SendAppearanceEffects update --- zone/mob.cpp | 58 ++++++++++++++---- zone/mob.h | 3 +- zone/perl_mob.cpp | 147 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 190 insertions(+), 18 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index e7fea3ca2..30cc77e45 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -2838,8 +2838,44 @@ void Mob::SendStunAppearance() safe_delete(outapp); } -void Mob::SendAppearanceEffect(uint32 parm1, uint32 parm2, uint32 parm3, uint32 parm4, uint32 parm5, Client *specific_target){ +void Mob::SendAppearanceEffect(uint32 parm1, uint32 parm2, uint32 parm3, uint32 parm4, uint32 parm5, Client *specific_target, + uint32 value1slot, uint32 value1ground, uint32 value2slot, uint32 value2ground, uint32 value3slot, uint32 value3ground, + uint32 value4slot, uint32 value4ground, uint32 value5slot, uint32 value5ground){ auto outapp = new EQApplicationPacket(OP_LevelAppearance, sizeof(LevelAppearance_Struct)); + + /* Location of the effect from value#slot, this is removed upon mob death/despawn. + 0 = pelvis1 + 1 = pelvis2 + 2 = helm + 3 = Offhand + 4 = Mainhand + 5 = left foot + 6 = right foot + 9 = Face + + value#ground = 1, will place the effect on ground, of corresponding slot is set to 0 effect is permanenant, if > 0 will fade if mob death/despawn. + */ + + //higher values can crash client + if (value1slot > 9) { + value1slot = 1; + } + if (value2slot > 9) { + value2slot = 1; + } + if (value2slot > 9) { + value2slot = 1; + } + if (value3slot > 9) { + value3slot = 1; + } + if (value4slot > 9) { + value4slot = 1; + } + if (value5slot > 9) { + value5slot = 1; + } + LevelAppearance_Struct* la = (LevelAppearance_Struct*)outapp->pBuffer; la->spawn_id = GetID(); la->parm1 = parm1; @@ -2849,16 +2885,16 @@ void Mob::SendAppearanceEffect(uint32 parm1, uint32 parm2, uint32 parm3, uint32 la->parm5 = parm5; // Note that setting the b values to 0 will disable the related effect from the corresponding parameter. // Setting the a value appears to have no affect at all.s - la->value1a = 1; - la->value1b = 1; - la->value2a = 1; - la->value2b = 1; - la->value3a = 1; - la->value3b = 1; - la->value4a = 1; - la->value4b = 1; - la->value5a = 1; - la->value5b = 1; + la->value1a = value1slot; + la->value1b = value1ground; + la->value2a = value2slot; + la->value2b = value2ground; + la->value3a = value3slot; + la->value3b = value3ground; + la->value4a = value4slot; + la->value4b = value4ground; + la->value5a = value5slot; + la->value5b = value5ground; if(specific_target == nullptr) { entity_list.QueueClients(this,outapp); } diff --git a/zone/mob.h b/zone/mob.h index 13c812cca..e3ae9c7f6 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -277,7 +277,8 @@ public: void ChangeSize(float in_size, bool bNoRestriction = false); void DoAnim(const int animnum, int type=0, bool ackreq = true, eqFilterType filter = FilterNone); void ProjectileAnimation(Mob* to, int item_id, bool IsArrow = false, float speed = 0, float angle = 0, float tilt = 0, float arc = 0, const char *IDFile = nullptr, EQ::skills::SkillType skillInUse = EQ::skills::SkillArchery); - void SendAppearanceEffect(uint32 parm1, uint32 parm2, uint32 parm3, uint32 parm4, uint32 parm5, Client *specific_target=nullptr); + void SendAppearanceEffect(uint32 parm1, uint32 parm2, uint32 parm3, uint32 parm4, uint32 parm5, Client *specific_target=nullptr, uint32 value1slot = 1, uint32 value1ground = 1, uint32 value2slot = 1, uint32 value2ground = 1, + uint32 value3slot = 1, uint32 value3ground = 1, uint32 value4slot = 1, uint32 value4ground = 1, uint32 value5slot = 1, uint32 value5ground = 1); void SendLevelAppearance(); void SendStunAppearance(); void SendTargetable(bool on, Client *specific_target = nullptr); diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index d87714367..006492918 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -4701,8 +4701,8 @@ XS(XS_Mob_HasNPCSpecialAtk) { XS(XS_Mob_SendAppearanceEffect); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_SendAppearanceEffect) { dXSARGS; - if (items < 2 || items > 7) - Perl_croak(aTHX_ "Usage: Mob::SendAppearanceEffect(THIS, int32 param_1, [int32 param_2 = 0], [int32 param_3 = 0], [int32 param_4 = 0], [int32 param_5 = 0], [Client* single_client_to_send_to = null])"); // @categories Script Utility + if (items < 2 || items > 17) + Perl_croak(aTHX_ "Usage: Mob::SendAppearanceEffect(THIS, int32 effect1, [int32 effect2 = 0], [int32 effect3 = 0], [int32 effect4 = 0], [int32 effect5 = 0], [Client* single_client_to_send_to = null]), [uint32 slot1 = 1], [uint32 ground1 = 1], [uint32 slot2 = 1], [uint32 ground2 = 1], [uint32 slot3 = 1], [uint32 ground2 = 1], [uint32 slot4 = 1], [uint32 ground4 = 1], [uint32 slot5 = 1], [uint32 ground5 = 1]"); // @categories Script Utility { Mob *THIS; int32 parm1 = (int32) SvIV(ST(1)); @@ -4710,7 +4710,18 @@ XS(XS_Mob_SendAppearanceEffect) { int32 parm3 = 0; int32 parm4 = 0; int32 parm5 = 0; + uint32 value1slot = 1; + uint32 value1ground = 1; + uint32 value2slot = 1; + uint32 value2ground = 1; + uint32 value3slot = 1; + uint32 value3ground = 1; + uint32 value4slot = 1; + uint32 value4ground = 1; + uint32 value5slot = 1; + uint32 value5ground = 1; Client *client = nullptr; + bool nullclient = false; VALIDATE_THIS_IS_MOB; if (items > 2) { parm2 = (int32) SvIV(ST(2)); } if (items > 3) { parm3 = (int32) SvIV(ST(3)); } @@ -4718,15 +4729,136 @@ XS(XS_Mob_SendAppearanceEffect) { if (items > 5) { parm5 = (int32) SvIV(ST(5)); } if (items > 6) { if (sv_derived_from(ST(6), "Client")) { - IV tmp = SvIV((SV *) SvRV(ST(6))); + IV tmp = SvIV((SV *)SvRV(ST(6))); client = INT2PTR(Client *, tmp); - } else + } + else { + nullclient = true; + } + if (client == nullptr) { + nullclient = true; + } + } + if (items > 7) { value1slot = (uint32)SvIV(ST(7)); } + if (items > 8) { value1ground = (uint32)SvIV(ST(8)); } + if (items > 9) { value2slot = (uint32)SvIV(ST(9)); } + if (items > 10) { value2ground = (uint32)SvIV(ST(10)); } + if (items > 11) { value3slot = (uint32)SvIV(ST(11)); } + if (items > 12) { value3ground = (uint32)SvIV(ST(12)); } + if (items > 13) { value4slot = (uint32)SvIV(ST(13)); } + if (items > 14) { value4ground = (uint32)SvIV(ST(14)); } + if (items > 15) { value5slot = (uint32)SvIV(ST(15)); } + if (items > 16) { value5ground = (uint32)SvIV(ST(16)); } + + if (nullclient) { + THIS->SendAppearanceEffect(parm1, parm2, parm3, parm4, parm5, 0, value1slot, value1ground, value2slot, value2ground, value3slot, value3ground, + value4slot, value4ground, value5slot, value5ground); + } + else { + THIS->SendAppearanceEffect(parm1, parm2, parm3, parm4, parm5, client, value1slot, value1ground, value2slot, value2ground, value3slot, value3ground, + value4slot, value4ground, value5slot, value5ground); + } + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SendAppearanceEffectActor); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SendAppearanceEffectActor) { + dXSARGS; + if (items < 3 || items > 12) + Perl_croak(aTHX_ "Usage: Mob::SendAppearanceEffectActor(THIS, int32 effect1, uint32 slot1 = 0, [int32 effect2 = 0], [uint32 slot2 = 0], [int32 effect3 = 0], [uint32 slot3 = 0], [int32 effect4 = 0], [uint32 slot4 = 0], [int32 effect5 = 0], [uint32 slot5 = 0], [Client* single_client_to_send_to = null])"); // @categories Script Utility + { + Mob *THIS; + int32 parm1 = (int32)SvIV(ST(1)); + uint32 value1slot = (uint32)SvIV(ST(2)); + int32 parm2 = 0; + uint32 value2slot = 0; + int32 parm3 = 0; + uint32 value3slot = 0; + int32 parm4 = 0; + uint32 value4slot = 0; + int32 parm5 = 0; + uint32 value5slot = 0; + Client *client = nullptr; + VALIDATE_THIS_IS_MOB; + if (items > 3) { parm2 = (int32)SvIV(ST(3)); } + if (items > 4) { value2slot = (uint32)SvIV(ST(4)); } + if (items > 5) { parm3 = (int32)SvIV(ST(5)); } + if (items > 6) { value3slot = (uint32)SvIV(ST(6)); } + if (items > 7) { parm4 = (int32)SvIV(ST(7)); } + if (items > 8) { value4slot = (uint32)SvIV(ST(8)); } + if (items > 9) { parm5 = (int32)SvIV(ST(9)); } + if (items > 10) { value5slot = (uint32)SvIV(ST(10)); } + if (items > 11) { + if (sv_derived_from(ST(11), "Client")) { + IV tmp = SvIV((SV *)SvRV(ST(11))); + client = INT2PTR(Client *, tmp); + } + else Perl_croak(aTHX_ "client is not of type Client"); if (client == nullptr) Perl_croak(aTHX_ "client is nullptr, avoiding crash."); } - THIS->SendAppearanceEffect(parm1, parm2, parm3, parm4, parm5, client); + THIS->SendAppearanceEffect(parm1, parm2, parm3, parm4, parm5, client, value1slot, 0, value2slot, 0, value3slot, 0, + value4slot, 0, value5slot, 0); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SendAppearanceEffectGround); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SendAppearanceEffectGround) { + dXSARGS; + if (items < 3 || items > 12) + Perl_croak(aTHX_ "Usage: Mob::SendAppearanceEffectGround(THIS, int32 effect1, uint32 slot1 = 1, [int32 effect2 = 0], [uint32 slot2 = 1], [int32 effect3 = 0], [uint32 slot3 = 1], [int32 effect4 = 0], [uint32 slot4 = 1], [int32 effect5 = 0], [uint32 slot5 = 1], [Client* single_client_to_send_to = null])"); // @categories Script Utility + { + Mob *THIS; + int32 parm1 = (int32)SvIV(ST(1)); + uint32 value1slot = (uint32)SvIV(ST(2)); + int32 parm2 = 0; + uint32 value2slot = 1; + int32 parm3 = 0; + uint32 value3slot = 1; + int32 parm4 = 0; + uint32 value4slot = 1; + int32 parm5 = 0; + uint32 value5slot = 1; + Client *client = nullptr; + VALIDATE_THIS_IS_MOB; + if (items > 3) { parm2 = (int32)SvIV(ST(3)); } + if (items > 4) { value2slot = (uint32)SvIV(ST(4)); } + if (items > 5) { parm3 = (int32)SvIV(ST(5)); } + if (items > 6) { value3slot = (uint32)SvIV(ST(6)); } + if (items > 7) { parm4 = (int32)SvIV(ST(7)); } + if (items > 8) { value4slot = (uint32)SvIV(ST(8)); } + if (items > 9) { parm5 = (int32)SvIV(ST(9)); } + if (items > 10) { value5slot = (uint32)SvIV(ST(10)); } + if (items > 11) { + if (sv_derived_from(ST(6), "Client")) { + IV tmp = SvIV((SV *)SvRV(ST(11))); + client = INT2PTR(Client *, tmp); + } + else + Perl_croak(aTHX_ "client is not of type Client"); + if (client == nullptr) + Perl_croak(aTHX_ "client is nullptr, avoiding crash."); + } + + THIS->SendAppearanceEffect(parm1, parm2, parm3, parm4, parm5, client, value1slot, 1, value2slot, 1, value3slot, 1, + value4slot, 1, value5slot, 1); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_RemoveAllAppearanceEffects); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_RemoveAllAppearanceEffects) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::RemoveAllAppearanceEffects(THIS)"); // @categories Script Utility + { + Mob *THIS; + VALIDATE_THIS_IS_MOB; + THIS->SendIllusionPacket(THIS->GetRace()); } XSRETURN_EMPTY; } @@ -6698,6 +6830,7 @@ XS(boot_Mob) { newXSproto(strcpy(buf, "ProjectileAnim"), XS_Mob_ProjectileAnim, file, "$$$;$$$$$$"); newXSproto(strcpy(buf, "RandomizeFeatures"), XS_Mob_RandomizeFeatures, file, "$$;$"); newXSproto(strcpy(buf, "RangedAttack"), XS_Mob_RangedAttack, file, "$$"); + newXSproto(strcpy(buf, "RemoveAllAppearanceEffects"), XS_Mob_RemoveAllAppearanceEffects, file, "$"); newXSproto(strcpy(buf, "RemoveAllNimbusEffects"), XS_Mob_RemoveAllNimbusEffects, file, "$"); newXSproto(strcpy(buf, "RemoveFromFeignMemory"), XS_Mob_RemoveFromFeignMemory, file, "$$"); newXSproto(strcpy(buf, "RemoveNimbusEffect"), XS_Mob_RemoveNimbusEffect, file, "$$"); @@ -6710,7 +6843,9 @@ XS(boot_Mob) { newXSproto(strcpy(buf, "SeeImprovedHide"), XS_Mob_SeeImprovedHide, file, "$"); newXSproto(strcpy(buf, "SeeInvisible"), XS_Mob_SeeInvisible, file, "$"); newXSproto(strcpy(buf, "SeeInvisibleUndead"), XS_Mob_SeeInvisibleUndead, file, "$"); - newXSproto(strcpy(buf, "SendAppearanceEffect"), XS_Mob_SendAppearanceEffect, file, "$$;$$$$"); + newXSproto(strcpy(buf, "SendAppearanceEffect"), XS_Mob_SendAppearanceEffect, file, "$$;$$$$$$$$$$$$$$"); + newXSproto(strcpy(buf, "SendAppearanceEffectActor"), XS_Mob_SendAppearanceEffectActor, file, "$$$;$$$$$$$$$"); + newXSproto(strcpy(buf, "SendAppearanceEffectGround"), XS_Mob_SendAppearanceEffectGround, file, "$$$;$$$$$$$$$"); newXSproto(strcpy(buf, "SendIllusion"), XS_Mob_SendIllusion, file, "$$;$$$$$$$$$$$$"); newXSproto(strcpy(buf, "SendTo"), XS_Mob_SendTo, file, "$$$$"); newXSproto(strcpy(buf, "SendToFixZ"), XS_Mob_SendToFixZ, file, "$$$$"); From 8688e9c9fa5d1f270c4c8509d78e3dc3201c94cb Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 27 Nov 2021 12:11:23 -0500 Subject: [PATCH 478/624] [Spells] Eye of Zomm will now despawn and stack properly (#1849) * [Spells] Eye of Zomm stop chain spawning No more chain spawning. * [Spells] Eye of Zomm stop chain spawning * [Spells] Eye of Zomm update --- common/spdat.h | 1 + zone/client.h | 1 + zone/npc.h | 1 + zone/spell_effects.cpp | 12 ++++++++++-- zone/spells.cpp | 9 ++++++++- 5 files changed, 21 insertions(+), 3 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index e77dcf521..058fe64b5 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -162,6 +162,7 @@ #define SPELL_RESURRECTION_SICKNESS3 37624 #define SPELL_PACT_OF_HATE_RECOURSE 40375 #define SPELL_INCENDIARY_OOZE_BUFF 32513 +#define SPELL_EYE_OF_ZOMM 323 //spellgroup ids #define SPELLGROUP_FRENZIED_BURNOUT 2754 diff --git a/zone/client.h b/zone/client.h index b8592d1eb..2f0120331 100644 --- a/zone/client.h +++ b/zone/client.h @@ -848,6 +848,7 @@ public: void SummonHorse(uint16 spell_id); void SetHorseId(uint16 horseid_in); inline void SetControlledMobId(uint16 mob_id_in) { controlled_mob_id = mob_id_in; } + uint16 GetControlledMobId() const{ return controlled_mob_id; } uint16 GetHorseId() const { return horseId; } bool CanMedOnHorse(); diff --git a/zone/npc.h b/zone/npc.h index df9159f0b..da59ad591 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -300,6 +300,7 @@ public: void PickPocket(Client* thief); void Disarm(Client* client, int chance); void StartSwarmTimer(uint32 duration) { swarm_timer.Start(duration); } + void DisableSwarmTimer() { swarm_timer.Disable(); } void AddLootDrop( const EQ::ItemData *item2, diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index ce93666f7..a141c6a53 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -4440,9 +4440,17 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) case SE_EyeOfZomm: { if (IsClient()) - { - CastToClient()->SetControlledMobId(0); + { + NPC* tmp_eye_of_zomm = entity_list.GetNPCByID(CastToClient()->GetControlledMobId()); + //On live there is about a 6 second delay before it despawns once new one spawns. + if (tmp_eye_of_zomm) { + tmp_eye_of_zomm->GetSwarmInfo()->duration->Disable(); + tmp_eye_of_zomm->GetSwarmInfo()->duration->Start(6000); + tmp_eye_of_zomm->DisableSwarmTimer(); + tmp_eye_of_zomm->StartSwarmTimer(6000); } + CastToClient()->SetControlledMobId(0); + } } case SE_Weapon_Stance: diff --git a/zone/spells.cpp b/zone/spells.cpp index e5581d7af..ef4c9e910 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -221,8 +221,9 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, return(false); } - if (spellbonuses.NegateIfCombat) + if (spellbonuses.NegateIfCombat) { BuffFadeByEffect(SE_NegateIfCombat); + } if (IsClient() && IsHarmonySpell(spell_id) && !HarmonySpellLevelCheck(spell_id, entity_list.GetMobID(target_id))) { InterruptSpell(SPELL_NO_EFFECT, 0x121, spell_id); @@ -3019,6 +3020,12 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, } if (spellid1 == spellid2 ) { + + if (spellid1 == SPELL_EYE_OF_ZOMM && spellid2 == SPELL_EYE_OF_ZOMM) {//only the original Eye of Zomm spell will not take hold if affect is already on you, other versions client fades the buff as soon as cast. + MessageString(Chat::Red, SPELL_NO_HOLD); + return -1; + } + if (!IsStackableDot(spellid1) && !IsEffectInSpell(spellid1, SE_ManaBurn)) { // mana burn spells we need to use the stacking command blocks live actually checks those first, we should probably rework to that too if (caster_level1 > caster_level2) { // cur buff higher level than new if (IsEffectInSpell(spellid1, SE_ImprovedTaunt)) { From 96cdf1b07655e51d547e80c9b8b4d940b902b468 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 27 Nov 2021 18:18:27 -0500 Subject: [PATCH 479/624] [Commands] Cleanup #title Command. (#1833) - Cleanup message and logic. --- zone/command.cpp | 2 +- zone/gm_commands/title.cpp | 107 +++++++++++++++++++------------------ 2 files changed, 56 insertions(+), 53 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 1a254eda1..17a594f06 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -364,7 +364,7 @@ int command_init(void) command_add("time", "[HH] [MM] - Set EQ time", AccountStatus::EQSupport, command_time) || command_add("timers", "- Display persistent timers for target", AccountStatus::GMMgmt, command_timers) || command_add("timezone", "[HH] [MM] - Set timezone. Minutes are optional", AccountStatus::EQSupport, command_timezone) || - command_add("title", "[text] [1 = create title table row] - Set your or your player target's title", AccountStatus::Guide, command_title) || + command_add("title", "[Remove|Title] [Save (0 = False, 1 = True)] - Set your or your player target's title (use remove to remove title, Save defaults to false if not used)", AccountStatus::Guide, command_title) || command_add("titlesuffix", "[text] [1 = create title table row] - Set your or your player target's title suffix", AccountStatus::Guide, command_titlesuffix) || command_add("traindisc", "[level] - Trains all the disciplines usable by the target, up to level specified. (may freeze client for a few seconds)", AccountStatus::GMLeadAdmin, command_traindisc) || command_add("trapinfo", "- Gets infomation about the traps currently spawned in the zone.", AccountStatus::QuestTroupe, command_trapinfo) || diff --git a/zone/gm_commands/title.cpp b/zone/gm_commands/title.cpp index 0be244d44..48b935b3c 100755 --- a/zone/gm_commands/title.cpp +++ b/zone/gm_commands/title.cpp @@ -3,63 +3,66 @@ void command_title(Client *c, const Seperator *sep) { - if (sep->arg[1][0] == 0) { + int arguments = sep->argnum; + if (!arguments) { c->Message( Chat::White, - "Usage: #title [remove|text] [1 = Create row in title table] - remove or set title to 'text'" + "Usage: #title [Remove|Title] [Save (0 = False, 1 = True)]" ); + return; } - else { - bool Save = (atoi(sep->arg[2]) == 1); - - Mob *target_mob = c->GetTarget(); - if (!target_mob) { - target_mob = c; - } - if (!target_mob->IsClient()) { - c->Message(Chat::Red, "#title only works on players."); - return; - } - Client *t = target_mob->CastToClient(); - - if (strlen(sep->arg[1]) > 31) { - c->Message(Chat::Red, "Title must be 31 characters or less."); - return; - } - - bool removed = false; - if (!strcasecmp(sep->arg[1], "remove")) { - t->SetAATitle(""); - removed = true; - } - else { - for (unsigned int i = 0; i < strlen(sep->arg[1]); i++) - if (sep->arg[1][i] == '_') { - sep->arg[1][i] = ' '; - } - if (!Save) { - t->SetAATitle(sep->arg[1]); - } - else { - title_manager.CreateNewPlayerTitle(t, sep->arg[1]); - } - } - - t->Save(); - - if (removed) { - c->Message(Chat::Red, "%s's title has been removed.", t->GetName(), sep->arg[1]); - if (t != c) { - t->Message(Chat::Red, "Your title has been removed.", sep->arg[1]); - } - } - else { - c->Message(Chat::Red, "%s's title has been changed to '%s'.", t->GetName(), sep->arg[1]); - if (t != c) { - t->Message(Chat::Red, "Your title has been changed to '%s'.", sep->arg[1]); - } - } + + bool is_remove = !strcasecmp(sep->arg[1], "remove"); + std::string title = is_remove ? "" : sep->arg[1]; + bool save_title = sep->IsNumber(2) ? atobool(sep->arg[2]) : false; + + auto target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); } + + if (title.size() > 31) { + c->Message(Chat::White, "Title must be 31 characters or less."); + return; + } + + if (!title.empty()) { + find_replace(title, "_", " "); + } + + if (!save_title || is_remove) { + target->SetAATitle(title.c_str()); + } else if (save_title) { + title_manager.CreateNewPlayerTitle(target, title.c_str()); + } + + target->Save(); + + c->Message( + Chat::White, + fmt::format( + "Title has been {}{} for {}{}", + is_remove ? "removed" : "changed", + !is_remove && save_title ? " and saved" : "", + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), + ( + is_remove ? + "." : + fmt::format( + " to '{}'.", + title + ) + ) + ).c_str() + ); } From 7d1d38541881b0f137dd966dfe3c6434b89c31c4 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 27 Nov 2021 18:19:03 -0500 Subject: [PATCH 480/624] [Commands] Cleanup #gmzone Command. (#1836) - Cleanup messages and logic. - Add support for Zone ID. --- zone/command.cpp | 2 +- zone/gm_commands/gmzone.cpp | 164 +++++++++++++++++++++++++----------- 2 files changed, 118 insertions(+), 48 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 17a594f06..2fa64d94f 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -197,7 +197,7 @@ int command_init(void) command_add("globalview", "Lists all qglobals in cache if you were to do a quest with this target.", AccountStatus::QuestTroupe, command_globalview) || command_add("gm", "[On|Off] - Modify your or your target's GM Flag", AccountStatus::QuestTroupe, command_gm) || command_add("gmspeed", "[On|Off] - Turn GM Speed On or Off for you or your player target", AccountStatus::GMAdmin, command_gmspeed) || - command_add("gmzone", "[zone_short_name] [zone_version=0] [identifier=gmzone] - Zones to a private GM instance", AccountStatus::GMAdmin, command_gmzone) || + command_add("gmzone", "[Zone ID|Zone Short Name] [Version] [Instance Identifier] - Zones to a private GM instance (Version defaults to 0 and Instance Identifier defaults to 'gmzone' if not used)", AccountStatus::GMAdmin, command_gmzone) || command_add("goto", "[playername] or [x y z] [h] - Teleport to the provided coordinates or to your target", AccountStatus::Steward, command_goto) || command_add("grid", "[add/delete] [grid_num] [wandertype] [pausetype] - Create/delete a wandering grid", AccountStatus::GMAreas, command_grid) || command_add("guild", "- Guild manipulation commands. Use argument help for more info.", AccountStatus::Steward, command_guild) || diff --git a/zone/gm_commands/gmzone.cpp b/zone/gm_commands/gmzone.cpp index 074ae7c7d..223f04fb3 100755 --- a/zone/gm_commands/gmzone.cpp +++ b/zone/gm_commands/gmzone.cpp @@ -3,86 +3,156 @@ void command_gmzone(Client *c, const Seperator *sep) { - if (!sep->arg[1]) { - c->Message(Chat::White, "Usage"); - c->Message(Chat::White, "-------"); - c->Message(Chat::White, "#gmzone [zone_short_name] [zone_version=0]"); + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #gmzone [Zone ID|Zone Short Name] [Version] [Instance Identifier]"); return; } - std::string zone_short_name_string = sep->arg[1]; - const char *zone_short_name = sep->arg[1]; - auto zone_version = static_cast(sep->arg[2] ? atoi(sep->arg[2]) : 0); - std::string identifier = "gmzone"; - uint32 zone_id = ZoneID(zone_short_name); - uint32 duration = 100000000; - uint16 instance_id = 0; + std::string zone_short_name = str_tolower( + sep->IsNumber(1) ? + ZoneName(std::stoul(sep->arg[1]), true) : + sep->arg[1] + ); + bool is_unknown_zone = zone_short_name.find("unknown") != std::string::npos; + if (is_unknown_zone) { + c->Message( + Chat::White, + fmt::format( + "Zone {} could not be found.", + zone_short_name + ).c_str() + ); + } - if (zone_id == 0) { - c->Message(Chat::Red, "Invalid zone specified"); + auto zone_id = ZoneID(zone_short_name); + if (!zone_id) { + c->Message( + Chat::White, + fmt::format( + "Zone ID {} could not be found.", + zone_id + ).c_str() + ); return; } - if (sep->arg[3] && sep->arg[3][0]) { - identifier = sep->arg[3]; - } + std::string zone_long_name = ZoneLongName(zone_id); - std::string bucket_key = StringFormat( - "%s-%s-%u-instance", + auto zone_version = ( + sep->IsNumber(2) ? + std::stoul(sep->arg[2]) : + 0 + ); + + std::string instance_identifier = ( + sep->arg[3] ? + sep->arg[3] : + "gmzone" + ); + + auto bucket_key = fmt::format( + "{}-{}-{}-instance", zone_short_name, - identifier.c_str(), + instance_identifier, zone_version ); - std::string existing_zone_instance = DataBucket::GetData(bucket_key); - if (existing_zone_instance.length() > 0) { + auto existing_zone_instance = DataBucket::GetData(bucket_key); + uint16 instance_id = 0; + uint32 duration = 100000000; + + if (!existing_zone_instance.empty()) { instance_id = std::stoi(existing_zone_instance); - - c->Message(Chat::Yellow, "Found already created instance (%s) (%u)", zone_short_name, instance_id); + c->Message( + Chat::White, + fmt::format( + "You already have an Instance ID of {} for Version {} of {} ({}).", + instance_id, + zone_version, + zone_long_name, + zone_short_name + ).c_str() + ); } - if (instance_id == 0) { + if (!instance_id) { if (!database.GetUnusedInstanceID(instance_id)) { - c->Message(Chat::Red, "Server was unable to find a free instance id."); + c->Message(Chat::White, "Failed to find an unused Instance ID."); return; } if (!database.CreateInstance(instance_id, zone_id, zone_version, duration)) { - c->Message(Chat::Red, "Server was unable to create a new instance."); + c->Message(Chat::White, "Failed to create an Instance."); return; } c->Message( - Chat::Yellow, - "New private GM instance %s was created with id %lu.", - zone_short_name, - (unsigned long) instance_id + Chat::White, + fmt::format( + "New GM Instance ID {} for Version {} of {} ({}) was created.", + instance_id, + zone_version, + zone_long_name, + zone_short_name + ).c_str() + ); + + DataBucket::SetData( + bucket_key, + std::to_string(instance_id) ); - DataBucket::SetData(bucket_key, std::to_string(instance_id)); } - if (instance_id > 0) { - float target_x = -1, target_y = -1, target_z = -1, target_heading = -1; + if (instance_id) { + float target_x = -1, target_y = -1, target_z = -1, target_heading = -1; int16 min_status = AccountStatus::Player; - uint8 min_level = 0; + uint8 min_level = 0; - if (!content_db.GetSafePoints( - zone_short_name, - zone_version, - &target_x, - &target_y, - &target_z, - &target_heading, - &min_status, - &min_level - )) { - c->Message(Chat::Red, "Failed to find safe coordinates for specified zone"); + if ( + !content_db.GetSafePoints( + zone_short_name.c_str(), + zone_version, + &target_x, + &target_y, + &target_z, + &target_heading, + &min_status, + &min_level + ) + ) { + c->Message( + Chat::White, + fmt::format( + "Failed to find the safe coordinates for Version {} of {} ({}).", + zone_version, + zone_long_name, + zone_short_name + ).c_str() + ); } - c->Message(Chat::Yellow, "Zoning to private GM instance (%s) (%u)", zone_short_name, instance_id); + c->Message( + Chat::White, + fmt::format( + "Zoning to GM Instance ID {} for Version {} of {} ({}).", + instance_id, + zone_version, + zone_long_name, + zone_short_name + ).c_str() + ); c->AssignToInstance(instance_id); - c->MovePC(zone_id, instance_id, target_x, target_y, target_z, target_heading, 1); + c->MovePC( + zone_id, + instance_id, + target_x, + target_y, + target_z, + target_heading, + 1 + ); } } From b3b9899a23f3803c2ec50021ef1daa2e52ffc367 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 27 Nov 2021 19:06:40 -0500 Subject: [PATCH 481/624] [Commands] Cleanup #movechar Command. (#1838) - Cleanup messages and logic. - Add support for Zone ID versus Zone Short Name. - Add support for Character ID versus Character Name. --- zone/command.cpp | 2 +- zone/gm_commands/movechar.cpp | 118 +++++++++++++++++++++++++++------- 2 files changed, 95 insertions(+), 25 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 2fa64d94f..9a74605c2 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -245,7 +245,7 @@ int command_init(void) command_add("merchant_open_shop", "Opens a merchants shop", AccountStatus::GMAdmin, command_merchantopenshop) || command_add("modifynpcstat", "- Modifys a NPC's stats", AccountStatus::GMLeadAdmin, command_modifynpcstat) || command_add("motd", "[new motd] - Set message of the day", AccountStatus::GMLeadAdmin, command_motd) || - command_add("movechar", "[charname] [zonename] - Move charname to zonename", AccountStatus::Guide, command_movechar) || + command_add("movechar", "[Character ID|Character Name] [Zone ID|Zone Short Name] - Move an offline character to the specified zone", AccountStatus::Guide, command_movechar) || command_add("movement", "Various movement commands", AccountStatus::GMMgmt, command_movement) || command_add("myskills", "- Show details about your current skill levels", AccountStatus::Player, command_myskills) || command_add("mysql", "Mysql CLI, see 'help' for options.", AccountStatus::GMImpossible, command_mysql) || diff --git a/zone/gm_commands/movechar.cpp b/zone/gm_commands/movechar.cpp index 1fc3c7f27..cec371b83 100755 --- a/zone/gm_commands/movechar.cpp +++ b/zone/gm_commands/movechar.cpp @@ -2,31 +2,101 @@ void command_movechar(Client *c, const Seperator *sep) { - if (sep->arg[1][0] == 0 || sep->arg[2][0] == 0) { - c->Message(Chat::White, "Usage: #movechar [charactername] [zonename]"); + int arguments = sep->argnum; + if (arguments < 2) { + c->Message(Chat::White, "Usage: #movechar [Character ID|Character Name] [Zone ID|Zone Short Name]"); + return; } - else if (c->Admin() < commandMovecharToSpecials && strcasecmp(sep->arg[2], "cshome") == 0 || - strcasecmp(sep->arg[2], "load") == 0 || strcasecmp(sep->arg[2], "load2") == 0) { - c->Message(Chat::White, "Invalid zone name"); + + std::string character_name = ( + sep->IsNumber(1) ? + database.GetCharNameByID(std::stoul(sep->arg[1])) : + sep->arg[1] + ); + auto character_id = database.GetCharacterID(character_name.c_str()); + if (!character_id) { + c->Message( + Chat::White, + fmt::format( + "Character {} could not be found.", + character_name + ).c_str() + ); + return; } - else { - uint32 tmp = database.GetAccountIDByChar(sep->arg[1]); - if (tmp) { - if (c->Admin() >= commandMovecharSelfOnly || tmp == c->AccountID()) { - if (!database.MoveCharacterToZone((char *) sep->arg[1], ZoneID(sep->arg[2]))) { - c->Message(Chat::White, "Character Move Failed!"); - } - else { - c->Message(Chat::White, "Character has been moved."); - } - } - else { - c->Message(Chat::Red, "You cannot move characters that are not on your account."); - } - } - else { - c->Message(Chat::White, "Character Does Not Exist"); - } + + auto account_id = database.GetAccountIDByChar(character_name.c_str()); + + std::string zone_short_name = str_tolower( + sep->IsNumber(2) ? + ZoneName(std::stoul(sep->arg[2]), true) : + sep->arg[2] + ); + + bool is_unknown_zone = zone_short_name.find("unknown") != std::string::npos; + if (is_unknown_zone) { + c->Message( + Chat::White, + fmt::format( + "Zone ID {} could not be found.", + std::stoul(sep->arg[2]) + ).c_str() + ); + return; + } + + auto zone_id = ZoneID(zone_short_name); + std::string zone_long_name = ZoneLongName(zone_id); + + bool is_special_zone = ( + zone_short_name.find("cshome") != std::string::npos || + zone_short_name.find("load") != std::string::npos || + zone_short_name.find("load2") != std::string::npos + ); + + if (c->Admin() < commandMovecharToSpecials && is_special_zone) { + c->Message( + Chat::White, + fmt::format( + "{} ({}) is a special zone and you cannot move someone there.", + zone_long_name, + zone_short_name + ).c_str() + ); + return; + } + + if ( + c->Admin() >= commandMovecharSelfOnly || + account_id == c->AccountID() + ) { + bool moved = database.MoveCharacterToZone(character_name.c_str(), zone_id); + std::string moved_string = ( + moved ? + "Succeeded" : + "Failed" + ); + c->Message( + Chat::White, + fmt::format( + "Character Move {} | Character: {} ({})", + moved_string, + character_name, + character_id + ).c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Character Move {} | Zone: {} ({}) ID: {}", + moved_string, + zone_long_name, + zone_short_name, + zone_id + ).c_str() + ); + } else { + c->Message(Chat::White, "You cannot move characters that are not on your account."); } } - From c4c525643862d426d8512b044038c0975640fffc Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 27 Nov 2021 19:07:47 -0500 Subject: [PATCH 482/624] [Commands] Add #setmana Command. (#1839) * [Commands] Add #setmana Command. - Add #setmana [Mana] command to set an NPC or player's mana to a specified amount, or to max if the amount is greater than their max. - Cleanup #mana command message and logic. * Update mana.cpp --- zone/CMakeLists.txt | 1 + zone/command.cpp | 1 + zone/command.h | 1 + zone/gm_commands/mana.cpp | 36 ++++++++++++--------- zone/gm_commands/setmana.cpp | 63 ++++++++++++++++++++++++++++++++++++ 5 files changed, 87 insertions(+), 15 deletions(-) create mode 100644 zone/gm_commands/setmana.cpp diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 3356c385c..1eed7b72f 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -492,6 +492,7 @@ SET(gm_commands gm_commands/setgraveyard.cpp gm_commands/setlanguage.cpp gm_commands/setlsinfo.cpp + gm_commands/setmana.cpp gm_commands/setpass.cpp gm_commands/setpvppoints.cpp gm_commands/setskill.cpp diff --git a/zone/command.cpp b/zone/command.cpp index 9a74605c2..ca394b941 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -328,6 +328,7 @@ int command_init(void) command_add("setgraveyard", "[zone name] - Creates a graveyard for the specified zone based on your target's LOC.", AccountStatus::GMMgmt, command_setgraveyard) || command_add("setlanguage", "[language ID] [value] - Set your target's language skillnum to value", AccountStatus::Guide, command_setlanguage) || command_add("setlsinfo", "[email] [password] - Set login server email address and password (if supported by login server)", AccountStatus::Steward, command_setlsinfo) || + command_add("setmana", "[Mana] - Set your or your target's Mana", AccountStatus::GMAdmin, command_setmana) || command_add("setpass", "[accountname] [password] - Set local password for accountname", AccountStatus::GMLeadAdmin, command_setpass) || command_add("setpvppoints", "[Amount] - Set your or your player target's PVP points", AccountStatus::GMAdmin, command_setpvppoints) || command_add("setskill", "[skillnum] [value] - Set your target's skill skillnum to value", AccountStatus::Guide, command_setskill) || diff --git a/zone/command.h b/zone/command.h index 9643c3efc..d43163f36 100644 --- a/zone/command.h +++ b/zone/command.h @@ -249,6 +249,7 @@ void command_setfaction(Client *c, const Seperator *sep); void command_setgraveyard(Client *c, const Seperator *sep); void command_setlanguage(Client *c, const Seperator *sep); void command_setlsinfo(Client *c, const Seperator *sep); +void command_setmana(Client *c, const Seperator *sep); void command_setpass(Client *c, const Seperator *sep); void command_setpvppoints(Client *c, const Seperator *sep); void command_setskill(Client *c, const Seperator *sep); diff --git a/zone/gm_commands/mana.cpp b/zone/gm_commands/mana.cpp index 33fe4e388..dc75a0aa3 100755 --- a/zone/gm_commands/mana.cpp +++ b/zone/gm_commands/mana.cpp @@ -3,25 +3,31 @@ void command_mana(Client *c, const Seperator *sep) { auto target = c->GetTarget() ? c->GetTarget() : c; + int mana = 0; if (target->IsClient()) { - target->CastToClient()->SetMana(target->CastToClient()->CalcMaxMana()); + mana = target->CastToClient()->CalcMaxMana(); + target->CastToClient()->SetMana(mana); } else { - target->SetMana(target->CalcMaxMana()); + mana = target->CalcMaxMana(); + target->SetMana(mana); } - if (c != target) { - c->Message( - Chat::White, - fmt::format( - "Set {} ({}) to full Mana.", - target->GetCleanName(), - target->GetID() - ).c_str() - ); - } - else { - c->Message(Chat::White, "Restored your Mana to full."); - } + c->Message( + Chat::White, + fmt::format( + "Set {} to full Mana ({}).", + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), + mana + ).c_str() + ); } diff --git a/zone/gm_commands/setmana.cpp b/zone/gm_commands/setmana.cpp new file mode 100644 index 000000000..505e85afe --- /dev/null +++ b/zone/gm_commands/setmana.cpp @@ -0,0 +1,63 @@ +#include "../client.h" + +void command_setmana(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #setmana [Mana]"); + return; + } + + auto mana = static_cast(std::min(std::stoll(sep->arg[1]), (long long) 2000000000)); + bool set_to_max = false; + Mob* target = c; + if (c->GetTarget()) { + target = c->GetTarget(); + } + + if (target->IsClient()) { + if (mana >= target->CastToClient()->CalcMaxMana()) { + mana = target->CastToClient()->CalcMaxMana(); + set_to_max = true; + } + + target->CastToClient()->SetMana(mana); + } else { + if (mana >= target->CalcMaxMana()) { + mana = target->CalcMaxMana(); + set_to_max = true; + } + + target->SetMana(mana); + } + + c->Message( + Chat::White, + fmt::format( + "Set {} to {} Mana{}.", + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), + ( + set_to_max ? + "full" : + std::to_string(mana) + ), + ( + set_to_max ? + fmt::format( + " ({})", + mana + ) : + "" + ) + ).c_str() + ); +} + From a5348e207be6b6de63b19e4eafb7f9c5234f5b15 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 27 Nov 2021 19:08:00 -0500 Subject: [PATCH 483/624] [Commands] Add #sethp Command. (#1840) - Add #sethp [Health] command to set an NPC or player's health to a specified amount, or to max if the amount is greater than their max. - Cleanup #heal command message and logic. --- zone/CMakeLists.txt | 1 + zone/command.cpp | 1 + zone/command.h | 1 + zone/gm_commands/heal.cpp | 36 +++++++++++++++---------- zone/gm_commands/sethp.cpp | 55 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 80 insertions(+), 14 deletions(-) create mode 100644 zone/gm_commands/sethp.cpp diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 1eed7b72f..44a4d2b9a 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -490,6 +490,7 @@ SET(gm_commands gm_commands/setcrystals.cpp gm_commands/setfaction.cpp gm_commands/setgraveyard.cpp + gm_commands/sethp.cpp gm_commands/setlanguage.cpp gm_commands/setlsinfo.cpp gm_commands/setmana.cpp diff --git a/zone/command.cpp b/zone/command.cpp index ca394b941..f3fa9936b 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -326,6 +326,7 @@ int command_init(void) command_add("setcrystals", "[value] - Set your or your player target's available radiant or ebon crystals", AccountStatus::GMAdmin, command_setcrystals) || command_add("setfaction", "[Faction ID] - Sets targeted NPC's faction in the database", AccountStatus::GMAreas, command_setfaction) || command_add("setgraveyard", "[zone name] - Creates a graveyard for the specified zone based on your target's LOC.", AccountStatus::GMMgmt, command_setgraveyard) || + command_add("sethp", "[Health] - Set your or your target's Health", AccountStatus::GMAdmin, command_sethp) || command_add("setlanguage", "[language ID] [value] - Set your target's language skillnum to value", AccountStatus::Guide, command_setlanguage) || command_add("setlsinfo", "[email] [password] - Set login server email address and password (if supported by login server)", AccountStatus::Steward, command_setlsinfo) || command_add("setmana", "[Mana] - Set your or your target's Mana", AccountStatus::GMAdmin, command_setmana) || diff --git a/zone/command.h b/zone/command.h index d43163f36..4760b09cb 100644 --- a/zone/command.h +++ b/zone/command.h @@ -247,6 +247,7 @@ void command_setanim(Client *c, const Seperator *sep); void command_setcrystals(Client *c, const Seperator *sep); void command_setfaction(Client *c, const Seperator *sep); void command_setgraveyard(Client *c, const Seperator *sep); +void command_sethp(Client *c, const Seperator *sep); void command_setlanguage(Client *c, const Seperator *sep); void command_setlsinfo(Client *c, const Seperator *sep); void command_setmana(Client *c, const Seperator *sep); diff --git a/zone/gm_commands/heal.cpp b/zone/gm_commands/heal.cpp index f309b30e3..5a68a7c55 100755 --- a/zone/gm_commands/heal.cpp +++ b/zone/gm_commands/heal.cpp @@ -2,20 +2,28 @@ void command_heal(Client *c, const Seperator *sep) { - auto target = c->GetTarget() ? c->GetTarget() : c; + Mob* target = c; + if (c->GetTarget()) { + target = c->GetTarget(); + } + target->Heal(); - if (c != target) { - c->Message( - Chat::White, - fmt::format( - "Healed {} ({}) to full.", - target->GetCleanName(), - target->GetID() - ).c_str() - ); - } - else { - c->Message(Chat::White, "Healed yourself to full."); - } + + c->Message( + Chat::White, + fmt::format( + "Set {} to full Health ({}).", + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), + target->GetMaxHP() + ).c_str() + ); } diff --git a/zone/gm_commands/sethp.cpp b/zone/gm_commands/sethp.cpp new file mode 100644 index 000000000..c9c73c96f --- /dev/null +++ b/zone/gm_commands/sethp.cpp @@ -0,0 +1,55 @@ +#include "../client.h" + +void command_sethp(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #sethp [Health]"); + return; + } + + auto health = static_cast(std::min(std::stoll(sep->arg[1]), (long long) 2000000000)); + bool set_to_max = false; + Mob* target = c; + if (c->GetTarget()) { + target = c->GetTarget(); + } + + if (health >= target->GetMaxHP()) { + health = target->GetMaxHP(); + set_to_max = true; + } + + target->SetHP(health); + target->SendHPUpdate(); + + c->Message( + Chat::White, + fmt::format( + "Set {} to {} Health{}.", + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), + ( + set_to_max ? + "full" : + std::to_string(health) + ), + ( + set_to_max ? + fmt::format( + " ({})", + health + ) : + "" + ) + ).c_str() + ); +} + From 225497337c1912311b05ba2e556a37710da4c2d6 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 27 Nov 2021 19:08:07 -0500 Subject: [PATCH 484/624] [Commands] Add #setendurance Command. (#1841) - Add #setendurance [Endurance] command to set an NPC or player's endurance to a specified amount, or to max if the amount is greater than their max. - Cleanup #endurance command message and logic. --- zone/CMakeLists.txt | 1 + zone/command.cpp | 1 + zone/command.h | 1 + zone/gm_commands/endurance.cpp | 45 +++++++++++++--------- zone/gm_commands/setendurance.cpp | 62 +++++++++++++++++++++++++++++++ 5 files changed, 92 insertions(+), 18 deletions(-) create mode 100644 zone/gm_commands/setendurance.cpp diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 44a4d2b9a..c16a5d65c 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -488,6 +488,7 @@ SET(gm_commands gm_commands/setaaxp.cpp gm_commands/setanim.cpp gm_commands/setcrystals.cpp + gm_commands/setendurance.cpp gm_commands/setfaction.cpp gm_commands/setgraveyard.cpp gm_commands/sethp.cpp diff --git a/zone/command.cpp b/zone/command.cpp index f3fa9936b..af5cb680a 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -324,6 +324,7 @@ int command_init(void) command_add("setadventurepoints", "- Set your or your player target's available adventure points", AccountStatus::GMLeadAdmin, command_set_adventure_points) || command_add("setanim", "[Animation ID (IDs are 0 to 4)] - Set target's appearance to Animation ID", AccountStatus::GMMgmt, command_setanim) || command_add("setcrystals", "[value] - Set your or your player target's available radiant or ebon crystals", AccountStatus::GMAdmin, command_setcrystals) || + command_add("setendurance", "[Endurance] - Set your or your target's Endurance", AccountStatus::GMAdmin, command_setendurance) || command_add("setfaction", "[Faction ID] - Sets targeted NPC's faction in the database", AccountStatus::GMAreas, command_setfaction) || command_add("setgraveyard", "[zone name] - Creates a graveyard for the specified zone based on your target's LOC.", AccountStatus::GMMgmt, command_setgraveyard) || command_add("sethp", "[Health] - Set your or your target's Health", AccountStatus::GMAdmin, command_sethp) || diff --git a/zone/command.h b/zone/command.h index 4760b09cb..dde221481 100644 --- a/zone/command.h +++ b/zone/command.h @@ -245,6 +245,7 @@ void command_setaapts(Client *c, const Seperator *sep); void command_setaaxp(Client *c, const Seperator *sep); void command_setanim(Client *c, const Seperator *sep); void command_setcrystals(Client *c, const Seperator *sep); +void command_setendurance(Client *c, const Seperator *sep); void command_setfaction(Client *c, const Seperator *sep); void command_setgraveyard(Client *c, const Seperator *sep); void command_sethp(Client *c, const Seperator *sep); diff --git a/zone/gm_commands/endurance.cpp b/zone/gm_commands/endurance.cpp index 26132c913..9ae34a477 100755 --- a/zone/gm_commands/endurance.cpp +++ b/zone/gm_commands/endurance.cpp @@ -2,26 +2,35 @@ void command_endurance(Client *c, const Seperator *sep) { - auto target = c->GetTarget() ? c->GetTarget() : c; - if (target->IsClient()) { - target->CastToClient()->SetEndurance(target->CastToClient()->GetMaxEndurance()); - } - else { - target->SetEndurance(target->GetMaxEndurance()); + Mob* target = c; + if (c->GetTarget()) { + target = c->GetTarget(); } - if (c != target) { - c->Message( - Chat::White, - fmt::format( - "Set {} ({}) to full Endurance.", - target->GetCleanName(), - target->GetID() - ).c_str() - ); - } - else { - c->Message(Chat::White, "Restored your Endurance to full."); + int endurance = 0; + if (target->IsClient()) { + endurance = target->CastToClient()->GetMaxEndurance(); + target->CastToClient()->SetEndurance(endurance); + } else { + endurance = target->GetMaxEndurance(); + target->SetEndurance(endurance); } + + c->Message( + Chat::White, + fmt::format( + "Set {} to full Endurance ({}).", + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), + endurance + ).c_str() + ); } diff --git a/zone/gm_commands/setendurance.cpp b/zone/gm_commands/setendurance.cpp new file mode 100644 index 000000000..a6c42f8ea --- /dev/null +++ b/zone/gm_commands/setendurance.cpp @@ -0,0 +1,62 @@ +#include "../client.h" + +void command_setendurance(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #setendurance [Endurance]"); + return; + } + + auto endurance = static_cast(std::min(std::stoll(sep->arg[1]), (long long) 2000000000)); + bool set_to_max = false; + Mob* target = c; + if (c->GetTarget()) { + target = c->GetTarget(); + } + + if (target->IsClient()) { + if (endurance >= target->CastToClient()->GetMaxEndurance()) { + endurance = target->CastToClient()->GetMaxEndurance(); + set_to_max = true; + } + + target->CastToClient()->SetEndurance(endurance); + } else { + if (endurance >= target->GetMaxEndurance()) { + endurance = target->GetMaxEndurance(); + set_to_max = true; + } + + target->SetEndurance(endurance); + } + + c->Message( + Chat::White, + fmt::format( + "Set {} to {} Endurance{}.", + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), + ( + set_to_max ? + "full" : + std::to_string(endurance) + ), + ( + set_to_max ? + fmt::format( + " ({})", + endurance + ) : + "" + ) + ).c_str() + ); +} \ No newline at end of file From a6e5534b6403c30ef22f7eb1f351c1e88635cebb Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 27 Nov 2021 20:21:58 -0500 Subject: [PATCH 485/624] [Commands] Cleanup #texture Command. (#1835) * [Commands] Cleanup #texture Command. - Cleanup message and logic. * Update command.cpp --- zone/command.cpp | 2 +- zone/gm_commands/texture.cpp | 101 ++++++++++++++++++++--------------- 2 files changed, 58 insertions(+), 45 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index af5cb680a..14a2d06aa 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -363,7 +363,7 @@ int command_init(void) command_add("tattoo", "- Change the tattoo of your target (Drakkin Only)", AccountStatus::QuestTroupe, command_tattoo) || command_add("tempname", "[newname] - Temporarily renames your target. Leave name blank to restore the original name.", AccountStatus::GMAdmin, command_tempname) || command_add("petname", "[newname] - Temporarily renames your pet. Leave name blank to restore the original name.", AccountStatus::GMAdmin, command_petname) || - command_add("texture", "[texture] [helmtexture] - Change your or your target's appearance, use 255 to show equipment", AccountStatus::Steward, command_texture) || + command_add("texture", "[Texture] [Helmet Texture] - Change your or your target's texture (Helmet Texture defaults to 0 if not used)", AccountStatus::Steward, command_texture) || command_add("time", "[HH] [MM] - Set EQ time", AccountStatus::EQSupport, command_time) || command_add("timers", "- Display persistent timers for target", AccountStatus::GMMgmt, command_timers) || command_add("timezone", "[HH] [MM] - Set timezone. Minutes are optional", AccountStatus::EQSupport, command_timezone) || diff --git a/zone/gm_commands/texture.cpp b/zone/gm_commands/texture.cpp index d6fdf252c..09f00df4b 100755 --- a/zone/gm_commands/texture.cpp +++ b/zone/gm_commands/texture.cpp @@ -2,51 +2,64 @@ void command_texture(Client *c, const Seperator *sep) { - - uint16 texture; - - if (sep->IsNumber(1) && atoi(sep->arg[1]) >= 0 && atoi(sep->arg[1]) <= 255) { - texture = atoi(sep->arg[1]); - uint8 helm = 0xFF; - - // Player Races Wear Armor, so Wearchange is sent instead - int i; - if (!c->GetTarget()) { - for (i = EQ::textures::textureBegin; i <= EQ::textures::LastTintableTexture; i++) { - c->SendTextureWC(i, texture); - } - } - else if ((c->GetTarget()->GetModel() > 0 && c->GetTarget()->GetModel() <= 12) || - c->GetTarget()->GetModel() == 128 || c->GetTarget()->GetModel() == 130 || - c->GetTarget()->GetModel() == 330 || c->GetTarget()->GetModel() == 522) { - for (i = EQ::textures::textureBegin; i <= EQ::textures::LastTintableTexture; i++) { - c->GetTarget()->SendTextureWC(i, texture); - } - } - else // Non-Player Races only need Illusion Packets to be sent for texture - { - if (sep->IsNumber(2) && atoi(sep->arg[2]) >= 0 && atoi(sep->arg[2]) <= 255) { - helm = atoi(sep->arg[2]); - } - else { - helm = texture; - } - - if (texture == 255) { - texture = 0xFFFF; // Should be pulling these from the database instead - helm = 0xFF; - } - - if ((c->GetTarget()) && (c->Admin() >= commandTextureOthers)) { - c->GetTarget()->SendIllusionPacket(c->GetTarget()->GetModel(), 0xFF, texture, helm); - } - else { - c->SendIllusionPacket(c->GetRace(), 0xFF, texture, helm); - } - } + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #texture [Texture] [Helmet Texture]"); + return; } - else { - c->Message(Chat::White, "Usage: #texture [texture] [helmtexture] (0-255, 255 for show equipment)"); + + auto texture = static_cast(std::min(std::stoul(sep->arg[1]), (unsigned long) 65535)); + auto helmet_texture = static_cast( + sep->IsNumber(2) ? + std::min(std::stoul(sep->arg[2]), (unsigned long) 255) : + 0 + ); + + Mob* target = c; + if (c->GetTarget() && c->Admin() >= commandTextureOthers) { + target = c->GetTarget(); } + + if (Mob::IsPlayerRace(target->GetModel())) { // Player Races Wear Armor, so Wearchange is sent instead + for ( + int texture_slot = EQ::textures::textureBegin; + texture_slot <= EQ::textures::LastTintableTexture; + texture_slot++ + ) { + target->SendTextureWC(texture_slot, texture); + } + } else { // Non-Player Races only need Illusion Packets to be sent for texture + target->SendIllusionPacket( + target->GetModel(), + target->GetGender(), + texture, + helmet_texture + ); + } + + c->Message( + Chat::White, + fmt::format( + "Texture Changed for {} | Texture: {}{}", + ( + c == target ? + "Yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), + texture, + ( + Mob::IsPlayerRace(target->GetModel()) ? + "" : + fmt::format( + " Helmet Texture: {}", + helmet_texture + ) + ) + ).c_str() + ); } From fd862d16bb31de4bfa72d2133de6e790872114d2 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 27 Nov 2021 20:32:21 -0500 Subject: [PATCH 486/624] [Commands] Cleanup #mysql Command. (#1837) * [Commands] Cleanup #mysql Command. - Cleanup messages and logic. * Update mysql.cpp --- zone/command.cpp | 2 +- zone/gm_commands/mysql.cpp | 128 ++++++++++++++++++------------------- 2 files changed, 64 insertions(+), 66 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 14a2d06aa..5e1ede40d 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -248,7 +248,7 @@ int command_init(void) command_add("movechar", "[Character ID|Character Name] [Zone ID|Zone Short Name] - Move an offline character to the specified zone", AccountStatus::Guide, command_movechar) || command_add("movement", "Various movement commands", AccountStatus::GMMgmt, command_movement) || command_add("myskills", "- Show details about your current skill levels", AccountStatus::Player, command_myskills) || - command_add("mysql", "Mysql CLI, see 'help' for options.", AccountStatus::GMImpossible, command_mysql) || + command_add("mysql", "[Help|Query] [SQL Query] - Mysql CLI, see 'Help' for options.", AccountStatus::GMImpossible, command_mysql) || command_add("mystats", "- Show details about you or your pet", AccountStatus::Guide, command_mystats) || command_add("name", "[newname] - Rename your player target", AccountStatus::GMLeadAdmin, command_name) || command_add("netstats", "- Gets the network stats for a stream.", AccountStatus::GMMgmt, command_netstats) || diff --git a/zone/gm_commands/mysql.cpp b/zone/gm_commands/mysql.cpp index 67fc0f0df..3c2bb8724 100755 --- a/zone/gm_commands/mysql.cpp +++ b/zone/gm_commands/mysql.cpp @@ -2,85 +2,83 @@ void command_mysql(Client *c, const Seperator *sep) { - if (!sep->arg[1][0] || !sep->arg[2][0]) { - c->Message(Chat::White, "Usage: #mysql query \"Query here\""); + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #mysql [Help|Query] [SQL Query]"); return; } - if (strcasecmp(sep->arg[1], "help") == 0) { - c->Message(Chat::White, "MYSQL In-Game CLI Interface:"); - c->Message(Chat::White, "Example: #mysql query \"Query goes here quoted\" -s -h"); - c->Message(Chat::White, "To use 'like \"%%something%%\" replace the %% with #"); - c->Message(Chat::White, "Example: #mysql query \"select * from table where name like \"#something#\""); - c->Message(Chat::White, "-s - Spaces select entries apart"); - c->Message(Chat::White, "-h - Colors every other select result"); + bool is_help = !strcasecmp(sep->arg[1], "help"); + bool is_query = !strcasecmp(sep->arg[1], "query"); + if ( + !is_help && + !is_query + ) { + c->Message(Chat::White, "Usage: #mysql [Help|Query] [SQL Query]"); return; } - - if (strcasecmp(sep->arg[1], "query") == 0) { - ///Parse switches here - int argnum = 3; - bool optionS = false; - bool optionH = false; - while (sep->arg[argnum] && strlen(sep->arg[argnum]) > 1) { - switch (sep->arg[argnum][1]) { - case 's': - optionS = true; - break; - case 'h': - optionH = true; - break; - default: - c->Message(Chat::Yellow, "%s, there is no option '%c'", c->GetName(), sep->arg[argnum][1]); - return; - } - ++argnum; + + if (is_help) { + c->Message(Chat::White, "Usage: #mysql query \"Query goes here quoted\""); + c->Message(Chat::White, "Note: To use 'LIKE \"%%something%%\" replace the %% with a #"); + c->Message(Chat::White, "Example: #mysql query \"SELECT * FROM items WHERE `name` LIKE \"#Apple#\""); + return; + } else if (is_query) { + if (arguments < 2) { + c->Message(Chat::White, "Usage: #mysql query \"Query goes here quoted\""); + c->Message(Chat::White, "Note: To use 'LIKE \"%%something%%\" replace the %% with a #"); + c->Message(Chat::White, "Example: #mysql query \"SELECT * FROM items WHERE `name` LIKE \"#Apple#\""); + return; } - int highlightTextIndex = 0; - std::string query(sep->arg[2]); - //swap # for % so like queries can work - std::replace(query.begin(), query.end(), '#', '%'); + std::string query = sep->arg[2]; + find_replace(query, "#", "%"); auto results = database.QueryDatabase(query); if (!results.Success()) { return; } - //Using sep->arg[2] again, replace # with %% so it doesn't screw up when sent through vsnprintf in Message - query = sep->arg[2]; - int pos = query.find('#'); - while (pos != std::string::npos) { - query.erase(pos, 1); - query.insert(pos, "%%"); - pos = query.find('#'); + query = sep->arg[2]; + find_replace(query, "#", "%%"); + + c->Message( + Chat::White, + fmt::format( + "Running Query: '{}'", + query + ).c_str() + ); + + std::vector lines; + for (auto row : results) { + for ( + int row_index = 0; + row_index < results.ColumnCount(); + row_index++ + ) { + lines.push_back( + fmt::format( + "{} | {} ", + results.FieldName(row_index), + ( + row[row_index] ? + ( + strlen(row[row_index]) ? + row[row_index] : + "Empty String" + ) : + "NULL" + ) + ) + ); + } } - c->Message(Chat::Yellow, "---Running query: '%s'", query.c_str()); - for (auto row = results.begin(); row != results.end(); ++row) { - std::stringstream lineText; - std::vector lineVec; - for (int i = 0; i < results.RowCount(); i++) { - //split lines that could overflow the buffer in Client::Message and get cut off - //This will crash MQ2 @ 4000 since their internal buffer is only 2048. - //Reducing it to 2000 fixes that but splits more results from tables with a lot of columns. - if (lineText.str().length() > 4000) { - lineVec.push_back(lineText.str()); - lineText.str(""); - } - lineText << results.FieldName(i) << ":" << "[" << (row[i] ? row[i] : "nullptr") << "] "; - } - - lineVec.push_back(lineText.str()); - - if (optionS) { //This provides spacing for the space switch - c->Message(Chat::White, " "); - } - if (optionH) { //This option will highlight every other row - highlightTextIndex = 1 - highlightTextIndex; - } - - for (int lineNum = 0; lineNum < lineVec.size(); ++lineNum) - c->Message(highlightTextIndex, lineVec[lineNum].c_str()); + for (auto line : lines) { + c->Message( + Chat::White, + line.c_str() + ); } } } From d28f902ecc3ebe0c77fc5950aef8fbc156d9549a Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 27 Nov 2021 20:52:09 -0500 Subject: [PATCH 487/624] [Commands] Add #countitem Command. (#1842) * [Commands] Add #countitem Command. - Add #countitem [Item ID] command to count an item by ID on yourself or your player/NPC target. * Cleanup. --- zone/CMakeLists.txt | 1 + zone/command.cpp | 1 + zone/command.h | 1 + zone/gm_commands/countitem.cpp | 64 ++++++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+) create mode 100644 zone/gm_commands/countitem.cpp diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index c16a5d65c..010ee06f6 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -308,6 +308,7 @@ SET(gm_commands gm_commands/copycharacter.cpp gm_commands/corpse.cpp gm_commands/corpsefix.cpp + gm_commands/countitem.cpp gm_commands/cvs.cpp gm_commands/damage.cpp gm_commands/databuckets.cpp diff --git a/zone/command.cpp b/zone/command.cpp index 5e1ede40d..575175088 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -140,6 +140,7 @@ int command_init(void) command_add("copycharacter", "[source_char_name] [dest_char_name] [dest_account_name] Copies character to destination account", AccountStatus::GMImpossible, command_copycharacter) || command_add("corpse", "- Manipulate corpses, use with no arguments for help", AccountStatus::Guide, command_corpse) || command_add("corpsefix", "Attempts to bring corpses from underneath the ground within close proximity of the player", AccountStatus::Player, command_corpsefix) || + command_add("countitem", "[Item ID] - Counts the specified Item ID in your or your target's inventory", AccountStatus::GMLeadAdmin, command_countitem) || command_add("cvs", "- Summary of client versions currently online.", AccountStatus::GMMgmt, command_cvs) || command_add("damage", "[Amount] - Damage yourself or your target", AccountStatus::GMAdmin, command_damage) || command_add("databuckets", "View|Delete [key] [limit]- View data buckets, limit 50 default or Delete databucket by key", AccountStatus::QuestTroupe, command_databuckets) || diff --git a/zone/command.h b/zone/command.h index dde221481..0abb1ae7c 100644 --- a/zone/command.h +++ b/zone/command.h @@ -48,6 +48,7 @@ void command_checklos(Client *c, const Seperator *sep); void command_copycharacter(Client *c, const Seperator *sep); void command_corpse(Client *c, const Seperator *sep); void command_corpsefix(Client *c, const Seperator *sep); +void command_countitem(Client *c, const Seperator *sep); void command_cvs(Client *c, const Seperator *sep); void command_damage(Client *c, const Seperator *sep); void command_databuckets(Client *c, const Seperator *sep); diff --git a/zone/gm_commands/countitem.cpp b/zone/gm_commands/countitem.cpp new file mode 100644 index 000000000..01e507081 --- /dev/null +++ b/zone/gm_commands/countitem.cpp @@ -0,0 +1,64 @@ +#include "../client.h" + +void command_countitem(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #countitem [Item ID]"); + return; + } + + Mob* target = c; + if ( + c->GetTarget() && + ( + c->GetTarget()->IsClient() || + c->GetTarget()->IsNPC() + ) + ) { + target = c->GetTarget(); + } + + auto item_id = std::stoul(sep->arg[1]); + if (!database.GetItem(item_id)) { + c->Message( + Chat::White, + fmt::format( + "Item ID {} could not be found.", + item_id + ).c_str() + ); + return; + } + + uint16 item_count = 0; + if (target->IsClient()) { + item_count = target->CastToClient()->CountItem(item_id); + } else if (target->IsNPC()) { + item_count = target->CastToNPC()->CountItem(item_id); + } + + c->Message( + Chat::White, + fmt::format( + "{} {} {} {}.", + ( + c == target ? + "You" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), + c == target ? "have" : "has", + ( + item_count ? + std::to_string(item_count) : + "no" + ), + database.CreateItemLink(item_id) + ).c_str() + ); +} + From 5ab9b941e2116774d6ea9194d08630ff196b7906 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 27 Nov 2021 21:29:56 -0500 Subject: [PATCH 488/624] [Commands] Cleanup #titlesuffix Command. (#1834) - Cleanup message and logic. --- zone/command.cpp | 2 +- zone/gm_commands/titlesuffix.cpp | 109 ++++++++++++++++--------------- 2 files changed, 56 insertions(+), 55 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 575175088..e4167b167 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -369,7 +369,7 @@ int command_init(void) command_add("timers", "- Display persistent timers for target", AccountStatus::GMMgmt, command_timers) || command_add("timezone", "[HH] [MM] - Set timezone. Minutes are optional", AccountStatus::EQSupport, command_timezone) || command_add("title", "[Remove|Title] [Save (0 = False, 1 = True)] - Set your or your player target's title (use remove to remove title, Save defaults to false if not used)", AccountStatus::Guide, command_title) || - command_add("titlesuffix", "[text] [1 = create title table row] - Set your or your player target's title suffix", AccountStatus::Guide, command_titlesuffix) || + command_add("titlesuffix", "[Remove|Title Suffix] [Save (0 = False, 1 = True)] - Set your or your player target's title suffix (use remove to remove title suffix, Save defaults to false if not used)", AccountStatus::Guide, command_titlesuffix) || command_add("traindisc", "[level] - Trains all the disciplines usable by the target, up to level specified. (may freeze client for a few seconds)", AccountStatus::GMLeadAdmin, command_traindisc) || command_add("trapinfo", "- Gets infomation about the traps currently spawned in the zone.", AccountStatus::QuestTroupe, command_trapinfo) || command_add("tune", "Calculate statistical values related to combat.", AccountStatus::GMAdmin, command_tune) || diff --git a/zone/gm_commands/titlesuffix.cpp b/zone/gm_commands/titlesuffix.cpp index 10f41e563..7dbad5e2b 100755 --- a/zone/gm_commands/titlesuffix.cpp +++ b/zone/gm_commands/titlesuffix.cpp @@ -3,63 +3,64 @@ void command_titlesuffix(Client *c, const Seperator *sep) { - if (sep->arg[1][0] == 0) { + int arguments = sep->argnum; + if (!arguments) { c->Message( Chat::White, - "Usage: #titlesuffix [remove|text] [1 = create row in title table] - remove or set title suffix to 'text'" + "Usage: #titlesuffix [Remove|Title] [Save (0 = False, 1 = True)]" ); + return; } - else { - bool Save = (atoi(sep->arg[2]) == 1); - - Mob *target_mob = c->GetTarget(); - if (!target_mob) { - target_mob = c; - } - if (!target_mob->IsClient()) { - c->Message(Chat::Red, "#titlesuffix only works on players."); - return; - } - Client *t = target_mob->CastToClient(); - - if (strlen(sep->arg[1]) > 31) { - c->Message(Chat::Red, "Title suffix must be 31 characters or less."); - return; - } - - bool removed = false; - if (!strcasecmp(sep->arg[1], "remove")) { - t->SetTitleSuffix(""); - removed = true; - } - else { - for (unsigned int i = 0; i < strlen(sep->arg[1]); i++) - if (sep->arg[1][i] == '_') { - sep->arg[1][i] = ' '; - } - - if (!Save) { - t->SetTitleSuffix(sep->arg[1]); - } - else { - title_manager.CreateNewPlayerSuffix(t, sep->arg[1]); - } - } - - t->Save(); - - if (removed) { - c->Message(Chat::Red, "%s's title suffix has been removed.", t->GetName(), sep->arg[1]); - if (t != c) { - t->Message(Chat::Red, "Your title suffix has been removed.", sep->arg[1]); - } - } - else { - c->Message(Chat::Red, "%s's title suffix has been changed to '%s'.", t->GetName(), sep->arg[1]); - if (t != c) { - t->Message(Chat::Red, "Your title suffix has been changed to '%s'.", sep->arg[1]); - } - } + + bool is_remove = !strcasecmp(sep->arg[1], "remove"); + std::string suffix = is_remove ? "" : sep->arg[1]; + bool save_suffix = sep->IsNumber(2) ? atobool(sep->arg[2]) : false; + + auto target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); } + + if (suffix.size() > 31) { + c->Message(Chat::White, "Title suffix must be 31 characters or less."); + return; + } + + if (!suffix.empty()) { + find_replace(suffix, "_", " "); + } + + if (!save_suffix || is_remove) { + target->SetTitleSuffix(suffix.c_str()); + } else if (save_suffix) { + title_manager.CreateNewPlayerSuffix(target, suffix.c_str()); + } + + target->Save(); + + c->Message( + Chat::White, + fmt::format( + "Title suffix has been {}{} for {}{}", + is_remove ? "removed" : "changed", + !is_remove && save_suffix ? " and saved" : "", + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), + ( + is_remove ? + "." : + fmt::format( + " to '{}'.", + suffix + ) + ) + ).c_str() + ); } - From ba5bb09af7176fc16213715dd2ccd5902566f233 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 27 Nov 2021 21:39:54 -0500 Subject: [PATCH 489/624] [Commands] Cleanup #flymode Command. (#1845) - Cleanup message and logic. - Add GetFlyModeName() and GetFlyModeMap() helper methods. - Cleanup #npcedit flymode to use helper methods. --- common/emu_constants.cpp | 25 +++++++++++++ common/emu_constants.h | 12 +++++++ zone/gm_commands/flymode.cpp | 68 ++++++++++++++++++++---------------- zone/gm_commands/npcedit.cpp | 32 ++++++----------- 4 files changed, 84 insertions(+), 53 deletions(-) diff --git a/common/emu_constants.cpp b/common/emu_constants.cpp index 619b7055a..8658cc419 100644 --- a/common/emu_constants.cpp +++ b/common/emu_constants.cpp @@ -197,3 +197,28 @@ std::string EQ::constants::GetLanguageName(int language_id) } return std::string(); } + +const std::map& EQ::constants::GetFlyModeMap() +{ + static const std::map flymode_map = { + { EQ::constants::GravityBehavior::Ground, "Ground" }, + { EQ::constants::GravityBehavior::Flying, "Flying" }, + { EQ::constants::GravityBehavior::Levitating, "Levitating" }, + { EQ::constants::GravityBehavior::Water, "Water" }, + { EQ::constants::GravityBehavior::Floating, "Floating" }, + { EQ::constants::GravityBehavior::LevitateWhileRunning, "Levitating While Running" }, + }; + return flymode_map; +} + +std::string EQ::constants::GetFlyModeName(uint8 flymode_id) +{ + if ( + flymode_id >= GravityBehavior::Ground && + flymode_id <= GravityBehavior::LevitateWhileRunning + ) { + auto flymodes = EQ::constants::GetFlyModeMap(); + return flymodes[flymode_id]; + } + return std::string(); +} diff --git a/common/emu_constants.h b/common/emu_constants.h index e071ca449..322b91ba0 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -220,12 +220,24 @@ namespace EQ stanceBurnAE }; + enum GravityBehavior : uint8 { + Ground, + Flying, + Levitating, + Water, + Floating, + LevitateWhileRunning + }; + const char *GetStanceName(StanceType stance_type); int ConvertStanceTypeToIndex(StanceType stance_type); extern const std::map& GetLanguageMap(); std::string GetLanguageName(int language_id); + extern const std::map& GetFlyModeMap(); + std::string GetFlyModeName(uint8 flymode_id); + const int STANCE_TYPE_FIRST = stancePassive; const int STANCE_TYPE_LAST = stanceBurnAE; const int STANCE_TYPE_COUNT = stanceBurnAE; diff --git a/zone/gm_commands/flymode.cpp b/zone/gm_commands/flymode.cpp index 905241b50..c9c76fd98 100755 --- a/zone/gm_commands/flymode.cpp +++ b/zone/gm_commands/flymode.cpp @@ -2,39 +2,45 @@ void command_flymode(Client *c, const Seperator *sep) { - Mob *t = c; - - if (strlen(sep->arg[1]) == 1 && sep->IsNumber(1) && atoi(sep->arg[1]) >= 0 && atoi(sep->arg[1]) <= 5) { - if (c->GetTarget()) { - t = c->GetTarget(); - } - - int fm = atoi(sep->arg[1]); - - t->SetFlyMode(static_cast(fm)); - t->SendAppearancePacket(AT_Levitate, fm); - if (sep->arg[1][0] == '0') { - c->Message(Chat::White, "Setting %s to Grounded", t->GetName()); - } - else if (sep->arg[1][0] == '1') { - c->Message(Chat::White, "Setting %s to Flying", t->GetName()); - } - else if (sep->arg[1][0] == '2') { - c->Message(Chat::White, "Setting %s to Levitating", t->GetName()); - } - else if (sep->arg[1][0] == '3') { - c->Message(Chat::White, "Setting %s to In Water", t->GetName()); - } - else if (sep->arg[1][0] == '4') { - c->Message(Chat::White, "Setting %s to Floating(Boat)", t->GetName()); - } - else if (sep->arg[1][0] == '5') { - c->Message(Chat::White, "Setting %s to Levitating While Running", t->GetName()); - } + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + return; } - else { - c->Message(Chat::White, "#flymode [0/1/2/3/4/5]"); + + Mob *target = c; + if (c->GetTarget()) { + target = c->GetTarget(); } + + auto flymode_id = std::stoul(sep->arg[1]); + if ( + flymode_id < EQ::constants::GravityBehavior::Ground && + flymode_id > EQ::constants::GravityBehavior::LevitateWhileRunning + ) { + c->Message(Chat::White, "Usage:: #flymode [Flymode ID]"); + c->Message(Chat::White, "0 = Ground, 1 = Flying, 2 = Levitating, 3 = Water, 4 = Floating, 5 = Levitating While Running"); + return; + } + + target->SetFlyMode(static_cast(flymode_id)); + target->SendAppearancePacket(AT_Levitate, flymode_id); + c->Message( + Chat::White, + fmt::format( + "Fly Mode for {} is now {} ({}).", + ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), + EQ::constants::GetFlyModeName(flymode_id), + flymode_id + ).c_str() + ); } diff --git a/zone/gm_commands/npcedit.cpp b/zone/gm_commands/npcedit.cpp index 332b9efa0..922b93bf2 100755 --- a/zone/gm_commands/npcedit.cpp +++ b/zone/gm_commands/npcedit.cpp @@ -153,7 +153,7 @@ void command_npcedit(Client *c, const Seperator *sep) ); c->Message( Chat::White, - "#npcedit flymode - Sets an NPC's flymode [0 = Ground, 1 = Flying, 2 = Levitating, 3 = Water, 4 = Floating, 5 = Levitating While Running]" + "#npcedit flymode - Sets an NPC's Fly Mode [0 = Ground, 1 = Flying, 2 = Levitating, 3 = Water, 4 = Floating, 5 = Levitating While Running]" ); c->Message( Chat::White, @@ -1271,29 +1271,17 @@ void command_npcedit(Client *c, const Seperator *sep) } if (strcasecmp(sep->arg[1], "flymode") == 0) { - auto flymode_id = atoi(sep->arg[2]); - std::string flymode_name = "Unknown"; - if (flymode_id == GravityBehavior::Ground) { - flymode_name = "Ground"; - } - else if (flymode_id == GravityBehavior::Flying) { - flymode_name = "Flying"; - } - else if (flymode_id == GravityBehavior::Levitating) { - flymode_name = "Levitating"; - } - else if (flymode_id == GravityBehavior::Water) { - flymode_name = "Water"; - } - else if (flymode_id == GravityBehavior::Floating) { - flymode_name = "Floating"; - } - else if (flymode_id == GravityBehavior::LevitateWhileRunning) { - flymode_name = "Levitating While Running"; - } + auto flymode_id = static_cast(std::stoul(sep->arg[2])); + std::string flymode_name = EQ::constants::GetFlyModeName(flymode_id); c->Message( Chat::Yellow, - fmt::format("NPC ID {} is now using Fly Mode {} ({}).", npc_id, flymode_name, flymode_id).c_str()); + fmt::format( + "NPC ID {} is now using Fly Mode {} ({}).", + npc_id, + flymode_name, + flymode_id + ).c_str() + ); std::string query = fmt::format("UPDATE npc_types SET flymode = {} WHERE id = {}", flymode_id, npc_id); content_db.QueryDatabase(query); return; From 2be1321aa991ed19966f33c12cf21d5c605d57e5 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 27 Nov 2021 21:41:54 -0500 Subject: [PATCH 490/624] [Commands] Add #removeitem Command. (#1847) - Add #removeitem [Item ID] [Amount] command to remove items by amount versus nuking them all, removes all if amount is greater than what you or your target have. --- zone/CMakeLists.txt | 1 + zone/command.cpp | 1 + zone/command.h | 1 + zone/gm_commands/removeitem.cpp | 85 +++++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+) create mode 100644 zone/gm_commands/removeitem.cpp diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 010ee06f6..e9740a71d 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -469,6 +469,7 @@ SET(gm_commands gm_commands/reloadworld.cpp gm_commands/reloadworldrules.cpp gm_commands/reloadzps.cpp + gm_commands/removeitem.cpp gm_commands/repop.cpp gm_commands/resetaa.cpp gm_commands/resetaa_timer.cpp diff --git a/zone/command.cpp b/zone/command.cpp index e4167b167..815336719 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -305,6 +305,7 @@ int command_init(void) command_add("reloadtitles", "- Reload player titles from the database", AccountStatus::GMLeadAdmin, command_reloadtitles) || command_add("reloadworld", "[0|1] - Clear quest cache (0 - no repop, 1 - repop)", AccountStatus::Max, command_reloadworld) || command_add("reloadzps", "- Reload zone points from database", AccountStatus::GMLeadAdmin, command_reloadzps) || + command_add("removeitem", "[Item ID] [Amount] - Removes the specified Item ID by Amount from you or your player target's inventory (Amount defaults to 1 if not used)", AccountStatus::GMAdmin, command_removeitem) || command_add("repop", "[delay] - Repop the zone with optional delay", AccountStatus::GMAdmin, command_repop) || command_add("resetaa", "- Resets a Player's AA in their profile and refunds spent AA's to unspent, may disconnect player.", AccountStatus::GMMgmt, command_resetaa) || command_add("resetaa_timer", "Command to reset AA cooldown timers.", AccountStatus::GMMgmt, command_resetaa_timer) || diff --git a/zone/command.h b/zone/command.h index 0abb1ae7c..9ff50140f 100644 --- a/zone/command.h +++ b/zone/command.h @@ -226,6 +226,7 @@ void command_reloadtraps(Client *c, const Seperator *sep); void command_reloadworld(Client *c, const Seperator *sep); void command_reloadworldrules(Client *c, const Seperator *sep); void command_reloadzps(Client *c, const Seperator *sep); +void command_removeitem(Client *c, const Seperator *sep); void command_repop(Client *c, const Seperator *sep); void command_resetaa(Client *c, const Seperator *sep); void command_resetaa_timer(Client *c, const Seperator *sep); diff --git a/zone/gm_commands/removeitem.cpp b/zone/gm_commands/removeitem.cpp new file mode 100644 index 000000000..6285e65d4 --- /dev/null +++ b/zone/gm_commands/removeitem.cpp @@ -0,0 +1,85 @@ +#include "../client.h" + +void command_removeitem(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #removeitem [Item ID] [Amount]"); + return; + } + + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + + auto target_string = ( + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ); + + auto item_id = std::stoi(sep->arg[1]); + if (!database.GetItem(item_id)) { + c->Message( + Chat::White, + fmt::format( + "Item ID {} could not be found.", + item_id + ).c_str() + ); + return; + } + + auto item_link = database.CreateItemLink(item_id); + auto amount = sep->IsNumber(2) ? std::stoul(sep->arg[2]) : 1; + auto item_count = target->CountItem(item_id); + if (item_count) { + if (item_count >= amount) { + target->RemoveItem(item_id, amount); + + c->Message( + Chat::White, + fmt::format( + "Removed {} {} ({}) from {}.", + amount, + item_link, + item_id, + target_string + ).c_str() + ); + } else { + target->RemoveItem(item_id, item_count); + + c->Message( + Chat::White, + fmt::format( + "Removed {} {} ({}) from {} because {} did not have {} {} ({}).", + item_count, + item_link, + item_id, + target_string, + c == target ? "you" : "they", + amount, + item_link, + item_id + ).c_str() + ); + } + } else { + c->Message( + Chat::White, + fmt::format( + "Could not find any {} ({}) to delete from {}.", + database.CreateItemLink(item_id), + item_id, + target_string + ).c_str() + ); + } +} + From 7cac2e2bc3ebd28a8ddfe06af053fb882cf68224 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 28 Nov 2021 00:09:07 -0500 Subject: [PATCH 491/624] [Commands] Add #viewcurrencies Command. (#1844) * [Commands] Add #viewcurrencies Command. - Add #viewcurrencies command to view your or your target's currencies (Money, Crystals, Alternate Currency, LDoN, and PVP). - Add GetLDoNThemeName() helper method. * Update viewcurrencies.cpp * Cleanup name of map method. * Cleanup. --- common/emu_constants.cpp | 22 +++++ common/emu_constants.h | 3 + zone/CMakeLists.txt | 1 + zone/client_packet.cpp | 30 +++--- zone/command.cpp | 1 + zone/command.h | 3 +- zone/gm_commands/viewcurrencies.cpp | 137 ++++++++++++++++++++++++++++ 7 files changed, 181 insertions(+), 16 deletions(-) create mode 100644 zone/gm_commands/viewcurrencies.cpp diff --git a/common/emu_constants.cpp b/common/emu_constants.cpp index 8658cc419..a6a45c0d3 100644 --- a/common/emu_constants.cpp +++ b/common/emu_constants.cpp @@ -198,6 +198,28 @@ std::string EQ::constants::GetLanguageName(int language_id) return std::string(); } +const std::map& EQ::constants::GetLDoNThemeMap() +{ + static const std::map ldon_theme_map = { + { LDoNThemes::Unused, "Unused" }, + { LDoNThemes::GUK, "Deepest Guk" }, + { LDoNThemes::MIR, "Miragul's Menagerie" }, + { LDoNThemes::MMC, "Mistmoore Catacombs" }, + { LDoNThemes::RUJ, "Rujarkian Hills" }, + { LDoNThemes::TAK, "Takish-Hiz" }, + }; + return ldon_theme_map; +} + +std::string EQ::constants::GetLDoNThemeName(uint32 theme_id) +{ + if (theme_id >= LDoNThemes::Unused && theme_id <= LDoNThemes::TAK) { + auto ldon_themes = EQ::constants::GetLDoNThemeMap(); + return ldon_themes[theme_id]; + } + return std::string(); +} + const std::map& EQ::constants::GetFlyModeMap() { static const std::map flymode_map = { diff --git a/common/emu_constants.h b/common/emu_constants.h index 322b91ba0..c2157d78f 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -235,6 +235,9 @@ namespace EQ extern const std::map& GetLanguageMap(); std::string GetLanguageName(int language_id); + extern const std::map& GetLDoNThemeMap(); + std::string GetLDoNThemeName(uint32 theme_id); + extern const std::map& GetFlyModeMap(); std::string GetFlyModeName(uint8 flymode_id); diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index e9740a71d..b3dcb597a 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -547,6 +547,7 @@ SET(gm_commands gm_commands/untraindiscs.cpp gm_commands/uptime.cpp gm_commands/version.cpp + gm_commands/viewcurrencies.cpp gm_commands/viewnpctype.cpp gm_commands/viewpetition.cpp gm_commands/viewzoneloot.cpp diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 0975c1df3..ddb2d04f6 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1936,14 +1936,6 @@ void Client::Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app) } 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) && @@ -2000,39 +1992,47 @@ void Client::Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app) } if (item->LDoNTheme <= LDoNThemeBits::TAKBit) { + uint32 ldon_theme; if (item->LDoNTheme & LDoNThemeBits::TAKBit) { if (m_pp.ldon_points_tak < item_cost) { cannot_afford = true; - merchant_type = fmt::format("Deepest Guk Point{}", item_cost != 1 ? "s" : ""); + ldon_theme = LDoNThemes::TAK; } } else if (item->LDoNTheme & LDoNThemeBits::RUJBit) { if (m_pp.ldon_points_ruj < item_cost) { cannot_afford = true; - merchant_type = fmt::format("Miragul's Menagerie Point{}", item_cost != 1 ? "s" : ""); + ldon_theme = LDoNThemes::RUJ; } } else if (item->LDoNTheme & LDoNThemeBits::MMCBit) { if (m_pp.ldon_points_mmc < item_cost) { cannot_afford = true; - merchant_type = fmt::format("Mistmoore Catacombs Point{}", item_cost != 1 ? "s" : ""); + ldon_theme = LDoNThemes::MMC; } } else if (item->LDoNTheme & LDoNThemeBits::MIRBit) { if (m_pp.ldon_points_mir < item_cost) { cannot_afford = true; - merchant_type = fmt::format("Rujarkian Hills Point{}", item_cost != 1 ? "s" : ""); + ldon_theme = LDoNThemes::MIR; } } else if (item->LDoNTheme & LDoNThemeBits::GUKBit) { if (m_pp.ldon_points_guk < item_cost) { cannot_afford = true; - merchant_type = fmt::format("Takish-Hiz Point{}", item_cost != 1 ? "s" : ""); + ldon_theme = LDoNThemes::GUK; } } - + merchant_type = fmt::format( + "{} Point{}", + EQ::constants::GetLDoNThemeName(ldon_theme), + item_cost != 1 ? "s" : "" + ); } } else if (aps->Type == DiscordMerchant) { if (GetPVPPoints() < item_cost) { cannot_afford = true; - merchant_type = fmt::format("PVP Point{}", item_cost != 1 ? "s" : ""); + merchant_type = fmt::format( + "PVP Point{}", + item_cost != 1 ? "s" : "" + ); } } else if (aps->Type == NorrathsKeepersMerchant) { if (GetRadiantCrystals() < item_cost) { diff --git a/zone/command.cpp b/zone/command.cpp index 815336719..1149f09e5 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -384,6 +384,7 @@ int command_init(void) command_add("untraindiscs", "- Untrains all disciplines from your target.", AccountStatus::GMCoder, command_untraindiscs) || command_add("uptime", "[zone server id] - Get uptime of worldserver, or zone server if argument provided", AccountStatus::Steward, command_uptime) || command_add("version", "- Display current version of EQEmu server", AccountStatus::Player, command_version) || + command_add("viewcurrencies", "- View your or your target's currencies", AccountStatus::GMAdmin, command_viewcurrencies) || command_add("viewnpctype", "[NPC ID] - Show stats for an NPC by NPC ID", AccountStatus::GMAdmin, command_viewnpctype) || command_add("viewpetition", "[petition number] - View a petition", AccountStatus::ApprenticeGuide, command_viewpetition) || command_add("viewzoneloot", "[item id] - Allows you to search a zone's loot for a specific item ID. (0 shows all loot in the zone)", AccountStatus::QuestTroupe, command_viewzoneloot) || diff --git a/zone/command.h b/zone/command.h index 9ff50140f..d9113a5d5 100644 --- a/zone/command.h +++ b/zone/command.h @@ -307,8 +307,10 @@ void command_untraindisc(Client *c, const Seperator *sep); void command_untraindiscs(Client *c, const Seperator *sep); void command_uptime(Client *c, const Seperator *sep); void command_version(Client *c, const Seperator *sep); +void command_viewcurrencies(Client *c, const Seperator *sep); void command_viewnpctype(Client *c, const Seperator *sep); void command_viewpetition(Client *c, const Seperator *sep); +void command_viewzoneloot(Client *c, const Seperator *sep); void command_wc(Client *c, const Seperator *sep); void command_weather(Client *c, const Seperator *sep); void command_who(Client *c, const Seperator *sep); @@ -325,7 +327,6 @@ void command_zone(Client *c, const Seperator *sep); void command_zone_instance(Client *c, const Seperator *sep); void command_zonebootup(Client *c, const Seperator *sep); void command_zonelock(Client *c, const Seperator *sep); -void command_viewzoneloot(Client *c, const Seperator *sep); void command_zoneshutdown(Client *c, const Seperator *sep); void command_zonestatus(Client *c, const Seperator *sep); void command_zopp(Client *c, const Seperator *sep); diff --git a/zone/gm_commands/viewcurrencies.cpp b/zone/gm_commands/viewcurrencies.cpp new file mode 100644 index 000000000..c66393c6f --- /dev/null +++ b/zone/gm_commands/viewcurrencies.cpp @@ -0,0 +1,137 @@ +#include "../client.h" + +void command_viewcurrencies(Client *c, const Seperator *sep) +{ + Client *target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + + auto target_string = ( + c == target ? + "Yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ); + + auto platinum = ( + target->GetMoney(3, 0) + + target->GetMoney(3, 1) + + target->GetMoney(3, 2) + + target->GetMoney(3, 3) + ); + + auto gold = ( + target->GetMoney(2, 0) + + target->GetMoney(2, 1) + + target->GetMoney(2, 2) + ); + + auto silver = ( + target->GetMoney(1, 0) + + target->GetMoney(1, 1) + + target->GetMoney(1, 2) + ); + + auto copper = ( + target->GetMoney(0, 0) + + target->GetMoney(0, 1) + + target->GetMoney(0, 2) + ); + + if ( + platinum || + gold || + silver || + copper + ) { + c->Message( + Chat::White, + fmt::format( + "Money for {} | {}", + target_string, + ConvertMoneyToString( + platinum, + gold, + silver, + copper + ) + ).c_str() + ); + } + + auto ebon_crystals = target->GetEbonCrystals(); + if (ebon_crystals) { + c->Message( + Chat::White, + fmt::format( + "{} for {} | {}", + database.CreateItemLink(RuleI(Zone, EbonCrystalItemID)), + target_string, + ebon_crystals + ).c_str() + ); + } + + auto radiant_crystals = target->GetRadiantCrystals(); + if (radiant_crystals) { + c->Message( + Chat::White, + fmt::format( + "{} for {} | {}", + database.CreateItemLink(RuleI(Zone, RadiantCrystalItemID)), + target_string, + radiant_crystals + ).c_str() + ); + } + + for (const auto& alternate_currency : zone->AlternateCurrencies) { + auto currency_value = target->GetAlternateCurrencyValue(alternate_currency.id); + if (currency_value) { + c->Message( + Chat::White, + fmt::format( + "{} for {} | {}", + database.CreateItemLink(alternate_currency.item_id), + target_string, + currency_value + ).c_str() + ); + } + } + + for ( + uint32 ldon_currency_id = LDoNThemes::GUK; + ldon_currency_id <= LDoNThemes::TAK; + ldon_currency_id++ + ) { + auto ldon_currency_value = target->GetLDoNPointsTheme(ldon_currency_id); + if (ldon_currency_value) { + c->Message( + Chat::White, + fmt::format( + "{} for {} | {}", + EQ::constants::GetLDoNThemeName(ldon_currency_id), + target_string, + ldon_currency_value + ).c_str() + ); + } + } + + auto pvp_points = target->GetPVPPoints(); + if (pvp_points) { + c->Message( + Chat::White, + fmt::format( + "PVP Points for {} | {}", + target_string, + pvp_points + ).c_str() + ); + } +} From f70b4a79b2fed07cd7839b069c82834503bd5a06 Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Sun, 28 Nov 2021 15:42:42 -0600 Subject: [PATCH 492/624] SetPetID after we assign the new NPC an ID (#1851) --- zone/entity.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/zone/entity.cpp b/zone/entity.cpp index 944418f29..aad8a3e82 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -689,6 +689,14 @@ void EntityList::AddNPC(NPC *npc, bool SendSpawnPacket, bool dontqueue) { npc->SetID(GetFreeID()); + //If this is not set here we will despawn pets from new AC changes + auto owner_id = npc->GetOwnerID(); + if(owner_id) { + auto owner = entity_list.GetMob(owner_id); + if (owner) { + owner->SetPetID(npc->GetID()); + } + } parse->EventNPC(EVENT_SPAWN, npc, nullptr, "", 0); uint16 emoteid = npc->GetEmoteID(); From d972183a79fe2cf5caf59cedb2c117160cea1649 Mon Sep 17 00:00:00 2001 From: JJ <3617814+joligario@users.noreply.github.com> Date: Sun, 28 Nov 2021 22:35:52 -0500 Subject: [PATCH 493/624] [Cleanup] holdzones not used. (#1852) Fixes #1116. --- world/main.cpp | 1 - world/zonelist.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/world/main.cpp b/world/main.cpp index 1e7814a91..ff4cdece9 100644 --- a/world/main.cpp +++ b/world/main.cpp @@ -119,7 +119,6 @@ EQ::Random emu_random; volatile bool RunLoops = true; uint32 numclients = 0; uint32 numzones = 0; -bool holdzones = false; const WorldConfig *Config; EQEmuLogSys LogSys; WorldContentService content_service; diff --git a/world/zonelist.cpp b/world/zonelist.cpp index 128800e12..b548fa94a 100644 --- a/world/zonelist.cpp +++ b/world/zonelist.cpp @@ -30,7 +30,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "world_store.h" extern uint32 numzones; -extern bool holdzones; extern EQ::Random emu_random; extern WebInterfaceList web_interface; volatile bool UCSServerAvailable_ = false; From bc0795bb487e92ebdf6362835073e62981a66a93 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 1 Dec 2021 01:22:10 -0500 Subject: [PATCH 494/624] [Spells] SPA 310 SE_ReduceReuseTimer will now work on spell recast time (#1856) * [Spells] SPA 310 SE_ReduceReuseTimer will now work on spell recast time [Spells] SPA 310 SE_ReduceReuseTimer will now work on spell recast time * [Spells] SPA 310 SE_ReduceReuseTimer will now work on spell recast time [Spells] SPA 310 SE_ReduceReuseTimer will now work on spell recast time --- common/spdat.h | 2 +- zone/client.cpp | 4 ++-- zone/client.h | 2 +- zone/mob.cpp | 1 + zone/mob.h | 1 + zone/spell_effects.cpp | 5 ++++- zone/spells.cpp | 37 ++++++++++++++++++++++++++----------- 7 files changed, 36 insertions(+), 16 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 058fe64b5..575c8293d 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -1013,7 +1013,7 @@ typedef enum { //#define SE_Appraisal 307 // *not implemented Rogue AA - This ability allows you to estimate the selling price of an item you are holding on your cursor. #define SE_ZoneSuspendMinion 308 // implemented, @Pet, allow suspended pets to be resummoned upon zoning, base: 1, limit: none, max: none, Calc: Bool #define SE_GateCastersBindpoint 309 // implemented - Gate to casters bind point -#define SE_ReduceReuseTimer 310 // implemented, @Fc, On Caster, disc reuse time mod, base: milliseconds +#define SE_ReduceReuseTimer 310 // implemented, @Fc, On Caster, spell and disc reuse time mod by amount, base: milliseconds #define SE_LimitCombatSkills 311 // implemented, @Ff, Include or exclude combat skills or procs (non-memorizable spells) from being focused, base1: 0=Exclude if proc 1=Allow only if proc #define SE_Sanctuary 312 // implemented - Places caster at bottom hate list, effect fades if cast cast spell on targets other than self. #define SE_ForageAdditionalItems 313 // implemented[AA] - chance to forage additional items diff --git a/zone/client.cpp b/zone/client.cpp index f09bcefcd..e7d799d0a 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -2658,17 +2658,17 @@ bool Client::CheckAccess(int16 iDBLevel, int16 iDefaultLevel) { return false; } -void Client::MemorizeSpell(uint32 slot,uint32 spellid,uint32 scribing){ +void Client::MemorizeSpell(uint32 slot,uint32 spellid,uint32 scribing, uint32 reduction){ if (slot < 0 || slot >= EQ::spells::DynamicLookup(ClientVersion(), GetGM())->SpellbookSize) return; if ((spellid < 3 || spellid > EQ::spells::DynamicLookup(ClientVersion(), GetGM())->SpellIdMax) && spellid != 0xFFFFFFFF) return; - auto outapp = new EQApplicationPacket(OP_MemorizeSpell, sizeof(MemorizeSpell_Struct)); MemorizeSpell_Struct* mss=(MemorizeSpell_Struct*)outapp->pBuffer; mss->scribing=scribing; mss->slot=slot; mss->spell_id=spellid; + mss->reduction = reduction; outapp->priority = 5; QueuePacket(outapp); safe_delete(outapp); diff --git a/zone/client.h b/zone/client.h index 2f0120331..5dea68d2b 100644 --- a/zone/client.h +++ b/zone/client.h @@ -906,7 +906,7 @@ public: int32 CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id); void SetAATitle(const char *Title); void SetTitleSuffix(const char *txt); - void MemorizeSpell(uint32 slot, uint32 spellid, uint32 scribing); + void MemorizeSpell(uint32 slot, uint32 spellid, uint32 scribing, uint32 reduction = 0); // Item methods void EVENT_ITEM_ScriptStopReturn(); diff --git a/zone/mob.cpp b/zone/mob.cpp index 30cc77e45..9273137f9 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -333,6 +333,7 @@ Mob::Mob( casting_spell_timer_duration = 0; casting_spell_inventory_slot = 0; casting_spell_aa_id = 0; + casting_spell_recast_adjust = 0; target = 0; ActiveProjectileATK = false; diff --git a/zone/mob.h b/zone/mob.h index e3ae9c7f6..47dfae24b 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1529,6 +1529,7 @@ protected: uint32 casting_spell_type; int16 casting_spell_resist_adjust; uint32 casting_spell_aa_id; + uint32 casting_spell_recast_adjust; bool casting_spell_checks; uint16 bardsong; EQ::spells::CastingSlot bardsong_slot; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index a141c6a53..8ac522594 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2546,6 +2546,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if (CalcFocusEffect(focusFcTimerRefresh, spell_id, CastToClient()->m_pp.mem_spells[i])){ CastToClient()->m_pp.spellSlotRefresh[i] = 1; CastToClient()->GetPTimers().Clear(&database, (pTimerSpellStart + CastToClient()->m_pp.mem_spells[i])); + if (!CastToClient()->IsLinkedSpellReuseTimerReady(spells[CastToClient()->m_pp.mem_spells[i]].timer_id)) { + CastToClient()->GetPTimers().Clear(&database, (pTimerLinkedSpellReuseStart + spells[CastToClient()->m_pp.mem_spells[i]].timer_id)); + } } } } @@ -6323,7 +6326,7 @@ uint16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) { int32 Client::GetFocusEffect(focusType type, uint16 spell_id) { - if (IsBardSong(spell_id) && type != focusFcBaseEffects && type != focusSpellDuration) + if (IsBardSong(spell_id) && type != focusFcBaseEffects && type != focusSpellDuration && type != focusReduceRecastTime) return 0; int32 realTotal = 0; diff --git a/zone/spells.cpp b/zone/spells.cpp index ef4c9e910..0c2534b71 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -882,6 +882,7 @@ void Mob::ZeroCastingVars() casting_spell_resist_adjust = 0; casting_spell_checks = false; casting_spell_aa_id = 0; + casting_spell_recast_adjust = 0; delaytimer = false; } @@ -1502,10 +1503,12 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo if((IsFromItem && RuleB(Character, SkillUpFromItems)) || !IsFromItem) { c->CheckSongSkillIncrease(spell_id); } - if (spells[spell_id].timer_id > 0 && slot < CastingSlot::MaxGems) - c->SetLinkedSpellReuseTimer(spells[spell_id].timer_id, spells[spell_id].recast_time / 1000); - if(RuleB(Spells, EnableBardMelody)) - c->MemorizeSpell(static_cast(slot), spell_id, memSpellSpellbar); + if (spells[spell_id].timer_id > 0 && slot < CastingSlot::MaxGems) { + c->SetLinkedSpellReuseTimer(spells[spell_id].timer_id, (spells[spell_id].recast_time / 1000) - (casting_spell_recast_adjust / 1000)); + } + if (RuleB(Spells, EnableBardMelody)) { + c->MemorizeSpell(static_cast(slot), spell_id, memSpellSpellbar, casting_spell_recast_adjust); + } } LogSpells("Bard song [{}] should be started", spell_id); } @@ -1517,9 +1520,11 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo SendSpellBarEnable(spell_id); // this causes the delayed refresh of the spell bar gems - if (spells[spell_id].timer_id > 0 && slot < CastingSlot::MaxGems) - c->SetLinkedSpellReuseTimer(spells[spell_id].timer_id, spells[spell_id].recast_time / 1000); - c->MemorizeSpell(static_cast(slot), spell_id, memSpellSpellbar); + if (spells[spell_id].timer_id > 0 && slot < CastingSlot::MaxGems) { + c->SetLinkedSpellReuseTimer(spells[spell_id].timer_id, (spells[spell_id].recast_time / 1000) - (casting_spell_recast_adjust / 1000)); + } + + c->MemorizeSpell(static_cast(slot), spell_id, memSpellSpellbar, casting_spell_recast_adjust); // this tells the client that casting may happen again SetMana(GetMana()); @@ -2575,13 +2580,23 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui { recast -= GetAA(aaTouchoftheWicked) * 420; } - int reduction = CastToClient()->GetFocusEffect(focusReduceRecastTime, spell_id);//Client only - if(reduction) + int reduction = CastToClient()->GetFocusEffect(focusReduceRecastTime, spell_id); + + if (reduction) { recast -= reduction; - + casting_spell_recast_adjust = reduction * 1000; //used later to adjust on client with memorizespell_struct + if (recast < 0) { + casting_spell_recast_adjust = spells[spell_id].recast_time; + } + recast = std::max(recast, 0); + } + LogSpells("Spell [{}]: Setting long reuse timer to [{}] s (orig [{}])", spell_id, recast, spells[spell_id].recast_time); - CastToClient()->GetPTimers().Start(pTimerSpellStart + spell_id, recast); + + if (recast > 0) { + CastToClient()->GetPTimers().Start(pTimerSpellStart + spell_id, recast); + } } } From 9a0c98397eff0211bc9717a9ed318ac45ccded26 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Wed, 1 Dec 2021 12:01:19 -0500 Subject: [PATCH 495/624] [Bug Fix] Charm Break Invisibility Fix. (#1855) - Invisibility vs. Undead and Invisibility vs. Animals were not breaking charm. - Add Invisibility enumerator. - Add special identifier for Invisibility vs. Undead and Invisibility vs. Animals. --- common/emu_constants.h | 6 ++++++ zone/client.cpp | 2 +- zone/mob.cpp | 15 +++++++++------ zone/spell_effects.cpp | 6 ++++-- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/common/emu_constants.h b/common/emu_constants.h index c2157d78f..c49dc7730 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -371,4 +371,10 @@ enum AccountStatus : uint8 { Max = 255 }; +enum Invisibility : uint8 { + Visible, + Invisible, + Special = 255 +}; + #endif /*COMMON_EMU_CONSTANTS_H*/ diff --git a/zone/client.cpp b/zone/client.cpp index e7d799d0a..cd9167ab3 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -3465,7 +3465,7 @@ uint8 Client::SlotConvert2(uint8 slot){ void Client::Escape() { entity_list.RemoveFromTargets(this, true); - SetInvisible(1); + SetInvisible(Invisibility::Invisible); MessageString(Chat::Skills, ESCAPE); } diff --git a/zone/mob.cpp b/zone/mob.cpp index 9273137f9..bb8dd9d94 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -575,13 +575,16 @@ uint32 Mob::GetAppearanceValue(EmuAppearance iAppearance) { void Mob::SetInvisible(uint8 state) { - invisible = state; - SendAppearancePacket(AT_Invis, invisible); + if (state != Invisibility::Special) { + invisible = state; + SendAppearancePacket(AT_Invis, invisible); + } + // Invis and hide breaks charms - auto formerpet = GetPet(); - if (formerpet && formerpet->GetPetType() == petCharmed && (invisible || hidden || improved_hidden || invisible_animals || invisible_undead)) { - if (RuleB(Pets, LivelikeBreakCharmOnInvis) || IsInvisible(formerpet)) { - formerpet->BuffFadeByEffect(SE_Charm); + auto pet = GetPet(); + if (pet && pet->GetPetType() == petCharmed && (invisible || hidden || improved_hidden || invisible_animals || invisible_undead)) { + if (RuleB(Pets, LivelikeBreakCharmOnInvis) || IsInvisible(pet)) { + pet->BuffFadeByEffect(SE_Charm); } LogRules("Pets:LivelikeBreakCharmOnInvis for [{}] | Invis [{}] - Hidden [{}] - Shroud of Stealth [{}] - IVA [{}] - IVU [{}]", GetCleanName(), invisible, hidden, improved_hidden, invisible_animals, invisible_undead); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 8ac522594..02c0e53e5 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -593,6 +593,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove snprintf(effect_desc, _EDLEN, "Invisibility to Animals"); #endif invisible_animals = true; + SetInvisible(Invisibility::Special); break; } @@ -603,6 +604,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove snprintf(effect_desc, _EDLEN, "Invisibility to Undead"); #endif invisible_undead = true; + SetInvisible(Invisibility::Special); break; } case SE_SeeInvis: @@ -2291,7 +2293,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove else { entity_list.RemoveFromTargets(caster); - SetInvisible(1); + SetInvisible(Invisibility::Invisible); } } break; @@ -4190,7 +4192,7 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) case SE_Invisibility2: case SE_Invisibility: { - SetInvisible(0); + SetInvisible(Invisibility::Visible); break; } From 29dfe9d4040f0a73a04bdd949997b1035f479684 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Wed, 1 Dec 2021 20:31:20 -0500 Subject: [PATCH 496/624] [Quest API] Add GetLanguageName() to Perl/Lua. (#1860) - Add quest::getlanguagename(language_id) to Perl. - Add eq.get_language_name(language_id) to Lua. --- zone/embparser_api.cpp | 18 ++++++++++++++++++ zone/lua_general.cpp | 5 +++++ zone/questmgr.cpp | 4 ++++ zone/questmgr.h | 1 + 4 files changed, 28 insertions(+) diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index f5fc4ad6b..7882f5c34 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -8023,6 +8023,23 @@ XS(XS__getspell) { } } +XS(XS__getlanguagename); +XS(XS__getlanguagename) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::getlanguagename(int language_id)"); + { + dXSTARG; + int language_id = (int) SvIV(ST(0)); + std::string language_name = quest_manager.getlanguagename(language_id); + + sv_setpv(TARG, language_name.c_str()); + XSprePUSH; + PUSHTARG; + XSRETURN(1); + } +} + /* This is the callback perl will look for to setup the quest package's XSUBs @@ -8315,6 +8332,7 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "get_expedition_by_zone_instance"), XS__get_expedition_by_zone_instance, file); newXS(strcpy(buf, "get_expedition_lockout_by_char_id"), XS__get_expedition_lockout_by_char_id, file); newXS(strcpy(buf, "get_expedition_lockouts_by_char_id"), XS__get_expedition_lockouts_by_char_id, file); + newXS(strcpy(buf, "getlanguagename"), XS__getlanguagename, file); newXS(strcpy(buf, "getinventoryslotid"), XS__getinventoryslotid, file); newXS(strcpy(buf, "getitemname"), XS__getitemname, file); newXS(strcpy(buf, "getItemName"), XS_qc_getItemName, file); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 7e76d1ddf..4703d7657 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -3351,6 +3351,10 @@ Lua_Spell lua_get_spell(uint32 spell_id) { return Lua_Spell(spell_id); } +std::string lua_get_language_name(int language_id) { + return quest_manager.getlanguagename(language_id); +} + #define LuaCreateNPCParse(name, c_type, default_value) do { \ cur = table[#name]; \ if(luabind::type(cur) != LUA_TNIL) { \ @@ -3794,6 +3798,7 @@ luabind::scope lua_register_general() { luabind::def("is_npc_spawned", &lua_is_npc_spawned), luabind::def("count_spawned_npcs", &lua_count_spawned_npcs), luabind::def("get_spell", &lua_get_spell), + luabind::def("get_language_name", &lua_get_language_name), /* Cross Zone diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index a825cf505..ca8bcdca2 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1036,6 +1036,10 @@ std::string QuestManager::getskillname(int skill_id) { return EQ::skills::GetSkillName(static_cast(skill_id)); } +std::string QuestManager::getlanguagename(int language_id) { + return EQ::constants::GetLanguageName(language_id); +} + void QuestManager::safemove() { QuestManagerCurrentQuestVars(); if (initiator && initiator->IsClient()) diff --git a/zone/questmgr.h b/zone/questmgr.h index d41b2d3ae..c9a490e31 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -115,6 +115,7 @@ public: std::string getracename(uint16 race_id); std::string getspellname(uint32 spell_id); std::string getskillname(int skill_id); + std::string getlanguagename(int language_id); void safemove(); void rain(int weather); void snow(int weather); From 4a154686e1b71198e89adf0b28efb79dd28d17cb Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 2 Dec 2021 10:09:15 -0500 Subject: [PATCH 497/624] [Quest API] Add GetFactionName() to Perl/Lua. (#1859) * [Quest API] Add GetFactionName() to Perl/Lua. - Add quest::getfactionname(faction_id) to Perl. - Add eq.get_faction_name(faction_id) to Lua. * Update embparser_api.cpp * Update embparser_api.cpp * Update embparser_api.cpp --- zone/client.cpp | 2 +- zone/embparser_api.cpp | 20 +++++++++++++++++++- zone/lua_general.cpp | 5 +++++ zone/questmgr.cpp | 4 ++++ zone/questmgr.h | 1 + zone/zonedb.h | 4 ++-- 6 files changed, 32 insertions(+), 4 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index cd9167ab3..64453cf11 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -6946,7 +6946,7 @@ void Client::SendStatsWindow(Client* client, bool use_window) for (auto iter = item_faction_bonuses.begin(); iter != item_faction_bonuses.end(); ++iter) { memset(&faction_buf, 0, sizeof(faction_buf)); - if(!content_db.GetFactionName((int32)((*iter).first), faction_buf, sizeof(faction_buf))) + if(!content_db.GetFactionName((int)((*iter).first), faction_buf, sizeof(faction_buf))) strcpy(faction_buf, "Not in DB"); if((*iter).second > 0) { diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 7882f5c34..58984cce5 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -8023,6 +8023,23 @@ XS(XS__getspell) { } } +XS(XS__getfactionname); +XS(XS__getfactionname) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::getfactionname(int faction_id)"); + { + dXSTARG; + int faction_id = (int) SvIV(ST(0)); + std::string faction_name = quest_manager.getfactionname(faction_id); + + sv_setpv(TARG, faction_name.c_str()); + XSprePUSH; + PUSHTARG; + XSRETURN(1); + } +} + XS(XS__getlanguagename); XS(XS__getlanguagename) { dXSARGS; @@ -8332,11 +8349,12 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "get_expedition_by_zone_instance"), XS__get_expedition_by_zone_instance, file); newXS(strcpy(buf, "get_expedition_lockout_by_char_id"), XS__get_expedition_lockout_by_char_id, file); newXS(strcpy(buf, "get_expedition_lockouts_by_char_id"), XS__get_expedition_lockouts_by_char_id, file); - newXS(strcpy(buf, "getlanguagename"), XS__getlanguagename, file); + newXS(strcpy(buf, "getfactionname"), XS__getfactionname, file); newXS(strcpy(buf, "getinventoryslotid"), XS__getinventoryslotid, file); newXS(strcpy(buf, "getitemname"), XS__getitemname, file); newXS(strcpy(buf, "getItemName"), XS_qc_getItemName, file); newXS(strcpy(buf, "getitemstat"), XS__getitemstat, file); + newXS(strcpy(buf, "getlanguagename"), XS__getlanguagename, file); newXS(strcpy(buf, "getnpcnamebyid"), XS__getnpcnamebyid, file); newXS(strcpy(buf, "get_spawn_condition"), XS__get_spawn_condition, file); newXS(strcpy(buf, "getcharnamebyid"), XS__getcharnamebyid, file); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 4703d7657..62777f76b 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -3351,6 +3351,10 @@ Lua_Spell lua_get_spell(uint32 spell_id) { return Lua_Spell(spell_id); } +std::string lua_get_faction_name(int faction_id) { + return quest_manager.getfactionname(faction_id); +} + std::string lua_get_language_name(int language_id) { return quest_manager.getlanguagename(language_id); } @@ -3798,6 +3802,7 @@ luabind::scope lua_register_general() { luabind::def("is_npc_spawned", &lua_is_npc_spawned), luabind::def("count_spawned_npcs", &lua_count_spawned_npcs), luabind::def("get_spell", &lua_get_spell), + luabind::def("get_faction_name", &lua_get_faction_name), luabind::def("get_language_name", &lua_get_language_name), /* diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index ca8bcdca2..92b8b0b37 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1036,6 +1036,10 @@ std::string QuestManager::getskillname(int skill_id) { return EQ::skills::GetSkillName(static_cast(skill_id)); } +std::string QuestManager::getfactionname(int faction_id) { + return content_db.GetFactionName(faction_id); +} + std::string QuestManager::getlanguagename(int language_id) { return EQ::constants::GetLanguageName(language_id); } diff --git a/zone/questmgr.h b/zone/questmgr.h index c9a490e31..205142003 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -115,6 +115,7 @@ public: std::string getracename(uint16 race_id); std::string getspellname(uint32 spell_id); std::string getskillname(int skill_id); + std::string getfactionname(int faction_id); std::string getlanguagename(int language_id); void safemove(); void rain(int weather); diff --git a/zone/zonedb.h b/zone/zonedb.h index 111f1d2c3..e12a0ed19 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -423,8 +423,8 @@ public: /* Faction */ bool GetNPCFactionList(uint32 npcfaction_id, int32* faction_id, int32* value, uint8* temp, int32* primary_faction = 0); bool GetFactionData(FactionMods* fd, uint32 class_mod, uint32 race_mod, uint32 deity_mod, int32 faction_id); //needed for factions Dec, 16 2001 - bool GetFactionName(int32 faction_id, char* name, uint32 buflen); // needed for factions Dec, 16 2001 - std::string GetFactionName(int32 faction_id); + bool GetFactionName(int faction_id, char* name, uint32 buflen); // needed for factions Dec, 16 2001 + std::string GetFactionName(int faction_id); bool GetFactionIdsForNPC(uint32 nfl_id, std::list *faction_list, int32* primary_faction = 0); // improve faction handling bool SetCharacterFactionLevel(uint32 char_id, int32 faction_id, int32 value, uint8 temp, faction_map &val_list); // needed for factions Dec, 16 2001 bool LoadFactionData(); From 82000949e3bee0e7010704c9cbafe1168993cd1b Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 3 Dec 2021 15:39:06 -0500 Subject: [PATCH 498/624] [Spells] Update to SPA 297 and 484 to support focus from AA and items. (#1858) [Spells] Update to SPA 297 and 484 to support focus from AA and items. --- zone/attack.cpp | 2 +- zone/lua_mob.cpp | 4 +-- zone/lua_mob.h | 2 +- zone/mob.h | 2 +- zone/spell_effects.cpp | 68 +++--------------------------------------- 5 files changed, 9 insertions(+), 69 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 07aa4b350..403528430 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -5458,7 +5458,7 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac int pct_damage_reduction = defender->GetSkillDmgTaken(hit.skill, opts) + defender->GetPositionalDmgTaken(this); - hit.damage_done += (hit.damage_done * pct_damage_reduction / 100) + (defender->GetFcDamageAmtIncoming(this, 0, true, hit.skill)) + defender->GetPositionalDmgTakenAmt(this); + hit.damage_done += (hit.damage_done * pct_damage_reduction / 100) + defender->GetPositionalDmgTakenAmt(this); if (defender->GetShielderID()) { DoShieldDamageOnShielder(defender, hit.damage_done, hit.skill); diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index ad39f9af5..e19f93906 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -1941,10 +1941,10 @@ int Lua_Mob::GetSkillDmgTaken(int skill) { return self->GetSkillDmgTaken(static_cast(skill)); } -int Lua_Mob::GetFcDamageAmtIncoming(Lua_Mob caster, uint32 spell_id, bool use_skill, uint16 skill) +int Lua_Mob::GetFcDamageAmtIncoming(Lua_Mob caster, int32 spell_id) { Lua_Safe_Call_Int(); - return self->GetFcDamageAmtIncoming(caster, spell_id, use_skill, skill); + return self->GetFcDamageAmtIncoming(caster, spell_id); } int Lua_Mob::GetSkillDmgAmt(uint16 skill) diff --git a/zone/lua_mob.h b/zone/lua_mob.h index e2e964b3b..f6b678d36 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -373,7 +373,7 @@ public: void ModSkillDmgTaken(int skill, int value); int GetModSkillDmgTaken(int skill); int GetSkillDmgTaken(int skill); - int GetFcDamageAmtIncoming(Lua_Mob caster, uint32 spell_id, bool use_skill, uint16 skill); + int GetFcDamageAmtIncoming(Lua_Mob caster, int32 spell_id); int GetSkillDmgAmt(uint16 skill); void SetAllowBeneficial(bool value); bool GetAllowBeneficial(); diff --git a/zone/mob.h b/zone/mob.h index 47dfae24b..9d94fe311 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -811,7 +811,7 @@ public: bool TryFadeEffect(int slot); uint16 GetSpellEffectResistChance(uint16 spell_id); int32 GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining); - int32 GetFcDamageAmtIncoming(Mob *caster, uint32 spell_id, bool use_skill = false, uint16 skill=0); + int32 GetFcDamageAmtIncoming(Mob *caster, int32 spell_id); int32 GetFocusIncoming(focusType type, int effect, Mob *caster, uint32 spell_id); //**** This can be removed when bot healing focus code is updated **** int32 GetSkillDmgTaken(const EQ::skills::SkillType skill_used, ExtraAttackOptions *opts = nullptr); int32 GetPositionalDmgTaken(Mob *attacker); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 02c0e53e5..a72d9064d 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -7102,72 +7102,12 @@ bool Mob::DoHPToManaCovert(uint16 mana_cost) return false; } -int32 Mob::GetFcDamageAmtIncoming(Mob *caster, uint32 spell_id, bool use_skill, uint16 skill ) +int32 Mob::GetFcDamageAmtIncoming(Mob *caster, int32 spell_id) { - //Used to check focus derived from SE_FcDamageAmtIncoming which adds direct damage to Spells or Skill based attacks. - //Used to check focus derived from SE_Fc_Spell_Damage_Amt_IncomingPC which adds direct damage to Spells. + //THIS is target of spell cast int32 dmg = 0; - bool limit_exists = false; - bool skill_found = false; - - if (!caster) - return 0; - - if (spellbonuses.FocusEffects[focusFcDamageAmtIncoming]){ - int buff_count = GetMaxTotalSlots(); - for(int i = 0; i < buff_count; i++){ - - if( (IsValidSpell(buffs[i].spellid) && (IsEffectInSpell(buffs[i].spellid, SE_FcDamageAmtIncoming))) ){ - - if (use_skill){ - int32 temp_dmg = 0; - for (int e = 0; e < EFFECT_COUNT; e++) { - - if (spells[buffs[i].spellid].effect_id[e] == SE_FcDamageAmtIncoming){ - temp_dmg += spells[buffs[i].spellid].base_value[e]; - continue; - } - - if (!skill_found){ - if ((spells[buffs[i].spellid].effect_id[e] == SE_LimitToSkill) || - (spells[buffs[i].spellid].effect_id[e] == SE_LimitCastingSkill)){ - limit_exists = true; - - if (spells[buffs[i].spellid].base_value[e] == skill) - skill_found = true; - } - } - } - if ((!limit_exists) || (limit_exists && skill_found)){ - dmg += temp_dmg; - CheckNumHitsRemaining(NumHit::MatchingSpells, i); - } - } - - else{ - int32 focus = caster->CalcFocusEffect(focusFcDamageAmtIncoming, buffs[i].spellid, spell_id); - if(focus){ - dmg += focus; - CheckNumHitsRemaining(NumHit::MatchingSpells, i); - } - } - } - } - } - if (spellbonuses.FocusEffects[focusFcSpellDamageAmtIncomingPC]) { - int buff_count = GetMaxTotalSlots(); - for (int i = 0; i < buff_count; i++) { - - if ((IsValidSpell(buffs[i].spellid) && (IsEffectInSpell(buffs[i].spellid, SE_FcDamageAmtIncoming)))) { - - int32 focus = caster->CalcFocusEffect(focusFcSpellDamageAmtIncomingPC, buffs[i].spellid, spell_id); - if (focus) { - dmg += focus; - CheckNumHitsRemaining(NumHit::MatchingSpells, i); - } - } - } - } + dmg += GetFocusEffect(focusFcDamageAmtIncoming, spell_id); //SPA 297 SE_FcDamageAmtIncoming + dmg += GetFocusEffect(focusFcSpellDamageAmtIncomingPC, spell_id); //SPA 484 SE_Fc_Spell_Damage_Amt_IncomingPC return dmg; } From e09f28c62c83da8ec5b7e14500ce3c43b23d3408 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 3 Dec 2021 15:39:21 -0500 Subject: [PATCH 499/624] [Spells] Update to SPA 296 and 483 item and AA support (#1857) [Spells] Update to SPA 296 and 483 item and AA support --- zone/mob.cpp | 90 +++++-------------------------------------------- zone/spells.cpp | 2 +- 2 files changed, 10 insertions(+), 82 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index bb8dd9d94..7babbeb82 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4390,94 +4390,22 @@ int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining) int32 fc_spell_damage_pct_incomingPC_mod = 0; //Apply innate vulnerabilities from quest functions and tables - if (Vulnerability_Mod[GetSpellResistType(spell_id)] != 0) + if (Vulnerability_Mod[GetSpellResistType(spell_id)] != 0) { innate_mod = Vulnerability_Mod[GetSpellResistType(spell_id)]; - - else if (Vulnerability_Mod[HIGHEST_RESIST+1] != 0) - innate_mod = Vulnerability_Mod[HIGHEST_RESIST+1]; - - //[Apply spell derived vulnerabilities] Step 1: Check this focus effect exists on the mob. - if (spellbonuses.FocusEffects[focusSpellVulnerability]){ - - int32 tmp_focus = 0; - int tmp_buffslot = -1; - - /* - Find all buffs that may contain SPA 296, then find which slot has the highest possible effect. Since the focus can use - a min and max amount value to determine final focus amt. To find the best focus, use only max value if possible. Once the - best is found. Run it again to get the final value randoming between min and max. - */ - int buff_count = GetMaxTotalSlots(); - for(int i = 0; i < buff_count; i++) { - - if((IsValidSpell(buffs[i].spellid) && IsEffectInSpell(buffs[i].spellid, SE_FcSpellVulnerability))){ - - int32 focus = caster->CalcFocusEffect(focusSpellVulnerability, buffs[i].spellid, spell_id, true, buffs[tmp_buffslot].casterid); - - if (!focus) - continue; - - if (tmp_focus && focus > tmp_focus){ - tmp_focus = focus; - tmp_buffslot = i; - } - - else if (!tmp_focus){ - tmp_focus = focus; - tmp_buffslot = i; - } - } - } - - fc_spell_vulnerability_mod = caster->CalcFocusEffect(focusSpellVulnerability, buffs[tmp_buffslot].spellid, spell_id, false, buffs[tmp_buffslot].casterid); - - if (tmp_buffslot >= 0) - CheckNumHitsRemaining(NumHit::MatchingSpells, tmp_buffslot); - } - - if (spellbonuses.FocusEffects[focusFcSpellDamagePctIncomingPC]) { - - int32 tmp_focus = 0; - int tmp_buffslot = -1; - - /* - Find all buffs that may contain SPA 483, then find which slot has the highest possible effect. Since the focus can use - a min and max amount value to determine final focus amt. To find the best focus, use only max value if possible. Once the - best is found. Run it again to get the final value randoming between min and max. - */ - int buff_count = GetMaxTotalSlots(); - for (int i = 0; i < buff_count; i++) { - - if ((IsValidSpell(buffs[i].spellid) && IsEffectInSpell(buffs[i].spellid, SE_Fc_Spell_Damage_Pct_IncomingPC))) { - - int32 focus = caster->CalcFocusEffect(focusFcSpellDamagePctIncomingPC, buffs[i].spellid, spell_id, true, buffs[tmp_buffslot].casterid); - - if (!focus) - continue; - - if (tmp_focus && focus > tmp_focus) { - tmp_focus = focus; - tmp_buffslot = i; - } - - else if (!tmp_focus) { - tmp_focus = focus; - tmp_buffslot = i; - } - } - } - - fc_spell_damage_pct_incomingPC_mod = caster->CalcFocusEffect(focusFcSpellDamagePctIncomingPC, buffs[tmp_buffslot].spellid, spell_id, false, buffs[tmp_buffslot].casterid); - - if (tmp_buffslot >= 0) - CheckNumHitsRemaining(NumHit::MatchingSpells, tmp_buffslot); + } + else if (Vulnerability_Mod[HIGHEST_RESIST + 1] != 0) { + innate_mod = Vulnerability_Mod[HIGHEST_RESIST + 1]; } + fc_spell_vulnerability_mod = GetFocusEffect(focusSpellVulnerability, spell_id); + fc_spell_damage_pct_incomingPC_mod = GetFocusEffect(focusFcSpellDamagePctIncomingPC, spell_id); + total_mod = fc_spell_vulnerability_mod + fc_spell_damage_pct_incomingPC_mod; //Don't let focus derived mods reduce past 99% mitigation. Quest related can, and for custom functionality if negative will give a healing affect instead of damage. - if (total_mod < -99) + if (total_mod < -99) { total_mod = -99; + } total_mod += innate_mod; return total_mod; diff --git a/zone/spells.cpp b/zone/spells.cpp index 0c2534b71..60d7e9516 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1475,7 +1475,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo } //Check if buffs has numhits, then resend packet so it displays the hit count. - if (IsClient() && (spells[spell_id].buff_duration > 0 || spells[spell_id].short_buff_box)) { + if (IsClient() && spells[spell_id].hit_number) { for (int i = 0; i < GetMaxTotalSlots(); i++) { if (buffs[i].spellid == spell_id && buffs[i].hit_number > 0) { CastToClient()->SendBuffNumHitPacket(buffs[i], i); From 01a671918a0d1714136cd9d4339929a12161bd85 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 3 Dec 2021 19:52:42 -0500 Subject: [PATCH 500/624] [Quest API] Add GetBodyTypeName() to Perl/Lua. (#1863) * [Quest API] Add GetBodyTypeName() to Perl/Lua. - Add GetBodyTypeName() and GetBodyTypeMap() helper methods. - Add quest::getbodytypename(bodytype_id) to Perl. - Add eq.get_body_type_name(bodytype_id) to Lua. * ShowStats() cleanup. --- common/emu_constants.cpp | 69 ++++++++++++++++++++++++++++++++---- common/emu_constants.h | 4 +++ zone/embparser_api.cpp | 18 ++++++++++ zone/gm_commands/npcedit.cpp | 14 ++++++-- zone/lua_general.cpp | 5 +++ zone/mob.cpp | 14 +++++++- zone/questmgr.cpp | 4 +++ zone/questmgr.h | 1 + 8 files changed, 119 insertions(+), 10 deletions(-) diff --git a/common/emu_constants.cpp b/common/emu_constants.cpp index a6a45c0d3..0d5f3c8cd 100644 --- a/common/emu_constants.cpp +++ b/common/emu_constants.cpp @@ -19,6 +19,8 @@ #include "emu_constants.h" #include "languages.h" +#include "data_verification.h" +#include "bodytypes.h" int16 EQ::invtype::GetInvTypeSize(int16 inv_type) { @@ -148,8 +150,9 @@ const char *EQ::constants::GetStanceName(StanceType stance_type) { } int EQ::constants::ConvertStanceTypeToIndex(StanceType stance_type) { - if (stance_type >= EQ::constants::stancePassive && stance_type <= EQ::constants::stanceBurnAE) + if (EQ::ValueWithin(stance_type, EQ::constants::stancePassive, EQ::constants::stanceBurnAE)) { return (stance_type - EQ::constants::stancePassive); + } return 0; } @@ -191,7 +194,7 @@ const std::map& EQ::constants::GetLanguageMap() std::string EQ::constants::GetLanguageName(int language_id) { - if (language_id >= LANG_COMMON_TONGUE && language_id <= LANG_UNKNOWN) { + if (EQ::ValueWithin(language_id, LANG_COMMON_TONGUE, LANG_UNKNOWN)) { auto languages = EQ::constants::GetLanguageMap(); return languages[language_id]; } @@ -213,7 +216,7 @@ const std::map& EQ::constants::GetLDoNThemeMap() std::string EQ::constants::GetLDoNThemeName(uint32 theme_id) { - if (theme_id >= LDoNThemes::Unused && theme_id <= LDoNThemes::TAK) { + if (EQ::ValueWithin(theme_id, LDoNThemes::Unused, LDoNThemes::TAK)) { auto ldon_themes = EQ::constants::GetLDoNThemeMap(); return ldon_themes[theme_id]; } @@ -235,12 +238,64 @@ const std::map& EQ::constants::GetFlyModeMap() std::string EQ::constants::GetFlyModeName(uint8 flymode_id) { - if ( - flymode_id >= GravityBehavior::Ground && - flymode_id <= GravityBehavior::LevitateWhileRunning - ) { + if (EQ::ValueWithin(flymode_id, GravityBehavior::Ground, GravityBehavior::LevitateWhileRunning)) { auto flymodes = EQ::constants::GetFlyModeMap(); return flymodes[flymode_id]; } return std::string(); } + +const std::map& EQ::constants::GetBodyTypeMap() +{ + static const std::map bodytype_map = { + { BT_Humanoid, "Humanoid" }, + { BT_Lycanthrope, "Lycanthrope" }, + { BT_Undead, "Undead" }, + { BT_Giant, "Giant" }, + { BT_Construct, "Construct" }, + { BT_Extraplanar, "Extraplanar" }, + { BT_Magical, "Magical" }, + { BT_SummonedUndead, "Summoned Undead" }, + { BT_RaidGiant, "Raid Giant" }, + { BT_RaidColdain, "Raid Coldain" }, + { BT_NoTarget, "Untargetable" }, + { BT_Vampire, "Vampire" }, + { BT_Atenha_Ra, "Aten Ha Ra" }, + { BT_Greater_Akheva, "Greater Akheva" }, + { BT_Khati_Sha, "Khati Sha" }, + { BT_Seru, "Seru" }, + { BT_Grieg_Veneficus, "Grieg Veneficus" }, + { BT_Draz_Nurakk, "Draz Nurakk" }, + { BT_Zek, "Zek" }, + { BT_Luggald, "Luggald" }, + { BT_Animal, "Animal" }, + { BT_Insect, "Insect" }, + { BT_Monster, "Monster" }, + { BT_Summoned, "Summoned" }, + { BT_Plant, "Plant" }, + { BT_Dragon, "Dragon" }, + { BT_Summoned2, "Summoned 2" }, + { BT_Summoned3, "Summoned 3" }, + { BT_Dragon2, "Dragon 2" }, + { BT_VeliousDragon, "Velious Dragon" }, + { BT_Familiar, "Familiar" }, + { BT_Dragon3, "Dragon 3" }, + { BT_Boxes, "Boxes" }, + { BT_Muramite, "Muramite" }, + { BT_NoTarget2, "Untargetable 2" }, + { BT_SwarmPet, "Swarm Pet" }, + { BT_MonsterSummon, "Monster Summon" }, + { BT_InvisMan, "Invisible Man" }, + { BT_Special, "Special" }, + }; + return bodytype_map; +} + +std::string EQ::constants::GetBodyTypeName(bodyType bodytype_id) +{ + auto bodytypes = EQ::constants::GetBodyTypeMap(); + if (!bodytypes[bodytype_id].empty()) { + return bodytypes[bodytype_id]; + } + return std::string(); +} \ No newline at end of file diff --git a/common/emu_constants.h b/common/emu_constants.h index c49dc7730..88ca7f924 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -22,6 +22,7 @@ #include "eq_limits.h" #include "emu_versions.h" +#include "bodytypes.h" #include @@ -241,6 +242,9 @@ namespace EQ extern const std::map& GetFlyModeMap(); std::string GetFlyModeName(uint8 flymode_id); + extern const std::map& GetBodyTypeMap(); + std::string GetBodyTypeName(bodyType bodytype_id); + const int STANCE_TYPE_FIRST = stancePassive; const int STANCE_TYPE_LAST = stanceBurnAE; const int STANCE_TYPE_COUNT = stanceBurnAE; diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 58984cce5..65dd1bde7 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -8057,6 +8057,23 @@ XS(XS__getlanguagename) { } } +XS(XS__getbodytypename); +XS(XS__getbodytypename) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::getbodytypename(uint32 bodytype_id)"); + { + dXSTARG; + uint32 bodytype_id = (uint32) SvUV(ST(0)); + std::string bodytype_name = quest_manager.getbodytypename(bodytype_id); + + sv_setpv(TARG, bodytype_name.c_str()); + XSprePUSH; + PUSHTARG; + XSRETURN(1); + } +} + /* This is the callback perl will look for to setup the quest package's XSUBs @@ -8337,6 +8354,7 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "forcedoorclose"), XS__forcedoorclose, file); newXS(strcpy(buf, "forcedooropen"), XS__forcedooropen, file); newXS(strcpy(buf, "getaaexpmodifierbycharid"), XS__getaaexpmodifierbycharid, file); + newXS(strcpy(buf, "getbodytypename"), XS__getbodytypename, file); newXS(strcpy(buf, "getcharidbyname"), XS__getcharidbyname, file); newXS(strcpy(buf, "getclassname"), XS__getclassname, file); newXS(strcpy(buf, "getcleannpcnamebyid"), XS__getcleannpcnamebyid, file); diff --git a/zone/gm_commands/npcedit.cpp b/zone/gm_commands/npcedit.cpp index 922b93bf2..66b483384 100755 --- a/zone/gm_commands/npcedit.cpp +++ b/zone/gm_commands/npcedit.cpp @@ -215,8 +215,18 @@ void command_npcedit(Client *c, const Seperator *sep) if (strcasecmp(sep->arg[1], "bodytype") == 0) { c->Message( Chat::Yellow, - fmt::format("NPC ID {} is now using Bodytype {} .", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET bodytype = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + fmt::format( + "NPC ID {} is now using Bodytype {} ({}).", + npc_id, + EQ::constants::GetBodyTypeName(static_cast(std::stoul(sep->arg[2]))), + std::stoul(sep->arg[2]) + ).c_str() + ); + std::string query = fmt::format( + "UPDATE npc_types SET bodytype = {} WHERE id = {}", + std::stoul(sep->arg[2]), + npc_id + ); content_db.QueryDatabase(query); return; } diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 62777f76b..f0213f0b4 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -3359,6 +3359,10 @@ std::string lua_get_language_name(int language_id) { return quest_manager.getlanguagename(language_id); } +std::string lua_get_body_type_name(uint32 bodytype_id) { + return quest_manager.getbodytypename(bodytype_id); +} + #define LuaCreateNPCParse(name, c_type, default_value) do { \ cur = table[#name]; \ if(luabind::type(cur) != LUA_TNIL) { \ @@ -3804,6 +3808,7 @@ luabind::scope lua_register_general() { luabind::def("get_spell", &lua_get_spell), luabind::def("get_faction_name", &lua_get_faction_name), luabind::def("get_language_name", &lua_get_language_name), + luabind::def("get_body_type_name", &lua_get_body_type_name), /* Cross Zone diff --git a/zone/mob.cpp b/zone/mob.cpp index 7babbeb82..dc59d444c 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1765,12 +1765,24 @@ void Mob::ShowStats(Client* client) } // Body + auto bodytype_name = EQ::constants::GetBodyTypeName(target->GetBodyType()); client->Message( Chat::White, fmt::format( "Body | Size: {:.2f} Type: {}", target->GetSize(), - target->GetBodyType() + ( + bodytype_name.empty() ? + fmt::format( + "{}", + target->GetBodyType() + ) : + fmt::format( + "{} ({})", + bodytype_name, + target->GetBodyType() + ) + ) ).c_str() ); diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 92b8b0b37..c8242117b 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1044,6 +1044,10 @@ std::string QuestManager::getlanguagename(int language_id) { return EQ::constants::GetLanguageName(language_id); } +std::string QuestManager::getbodytypename(uint32 bodytype_id) { + return EQ::constants::GetBodyTypeName(static_cast(bodytype_id)); +} + void QuestManager::safemove() { QuestManagerCurrentQuestVars(); if (initiator && initiator->IsClient()) diff --git a/zone/questmgr.h b/zone/questmgr.h index 205142003..1836ae289 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -117,6 +117,7 @@ public: std::string getskillname(int skill_id); std::string getfactionname(int faction_id); std::string getlanguagename(int language_id); + std::string getbodytypename(uint32 bodytype_id); void safemove(); void rain(int weather); void snow(int weather); From aa4536e1ef132b94d5527ca921b6fd7be0fbee1e Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 3 Dec 2021 19:53:00 -0500 Subject: [PATCH 501/624] [Quest API] Add GetLDoNThemeName() to Perl/Lua. (#1861) * [Quest API] Add GetLDoNThemeName() to Perl/Lua. - Add quest::getldonthemename(theme_id) to Perl. - Add eq.get_ldon_theme_name(theme_id) to Lua. * Update embparser_api.cpp --- zone/embparser_api.cpp | 22 ++++++++++++++++++++-- zone/lua_general.cpp | 5 +++++ zone/questmgr.cpp | 4 ++++ zone/questmgr.h | 1 + 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 65dd1bde7..47b14a898 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -8023,6 +8023,23 @@ XS(XS__getspell) { } } +XS(XS__getldonthemename); +XS(XS__getldonthemename) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::getldonthemename(uint32 theme_id)"); + { + dXSTARG; + uint32 theme_id = (uint32) SvUV(ST(0)); + std::string theme_name = quest_manager.getldonthemename(theme_id); + + sv_setpv(TARG, theme_name.c_str()); + XSprePUSH; + PUSHTARG; + XSRETURN(1); + } +} + XS(XS__getfactionname); XS(XS__getfactionname) { dXSARGS; @@ -8034,7 +8051,7 @@ XS(XS__getfactionname) { std::string faction_name = quest_manager.getfactionname(faction_id); sv_setpv(TARG, faction_name.c_str()); - XSprePUSH; + XSprePUSH; PUSHTARG; XSRETURN(1); } @@ -8372,7 +8389,8 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "getitemname"), XS__getitemname, file); newXS(strcpy(buf, "getItemName"), XS_qc_getItemName, file); newXS(strcpy(buf, "getitemstat"), XS__getitemstat, file); - newXS(strcpy(buf, "getlanguagename"), XS__getlanguagename, file); + newXS(strcpy(buf, "getlanguagename"), XS__getlanguagename, file); + newXS(strcpy(buf, "getldonthemename"), XS__getldonthemename, file); newXS(strcpy(buf, "getnpcnamebyid"), XS__getnpcnamebyid, file); newXS(strcpy(buf, "get_spawn_condition"), XS__get_spawn_condition, file); newXS(strcpy(buf, "getcharnamebyid"), XS__getcharnamebyid, file); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index f0213f0b4..d3a5916d0 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -3351,6 +3351,10 @@ Lua_Spell lua_get_spell(uint32 spell_id) { return Lua_Spell(spell_id); } +std::string lua_get_ldon_theme_name(uint32 theme_id) { + return quest_manager.getldonthemename(theme_id); +} + std::string lua_get_faction_name(int faction_id) { return quest_manager.getfactionname(faction_id); } @@ -3806,6 +3810,7 @@ luabind::scope lua_register_general() { luabind::def("is_npc_spawned", &lua_is_npc_spawned), luabind::def("count_spawned_npcs", &lua_count_spawned_npcs), luabind::def("get_spell", &lua_get_spell), + luabind::def("get_ldon_theme_name", &lua_get_ldon_theme_name), luabind::def("get_faction_name", &lua_get_faction_name), luabind::def("get_language_name", &lua_get_language_name), luabind::def("get_body_type_name", &lua_get_body_type_name), diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index c8242117b..7a4d722a7 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1036,6 +1036,10 @@ std::string QuestManager::getskillname(int skill_id) { return EQ::skills::GetSkillName(static_cast(skill_id)); } +std::string QuestManager::getldonthemename(uint32 theme_id) { + return EQ::constants::GetLDoNThemeName(theme_id); +} + std::string QuestManager::getfactionname(int faction_id) { return content_db.GetFactionName(faction_id); } diff --git a/zone/questmgr.h b/zone/questmgr.h index 1836ae289..c03633c89 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -115,6 +115,7 @@ public: std::string getracename(uint16 race_id); std::string getspellname(uint32 spell_id); std::string getskillname(int skill_id); + std::string getldonthemename(uint32 theme_id); std::string getfactionname(int faction_id); std::string getlanguagename(int language_id); std::string getbodytypename(uint32 bodytype_id); From 8ec4afe7219fcb9b29ec6db9f6b096b9662a838a Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 4 Dec 2021 21:53:29 -0500 Subject: [PATCH 502/624] [Commands] Cleanup #wpinfo Command. (#1866) - Cleanup message and logic. - Only display grid/waypoints if NPC has a grid. --- zone/gm_commands/wpinfo.cpp | 23 ++++++++++---- zone/lua_npc.cpp | 4 +-- zone/lua_npc.h | 2 +- zone/npc.h | 2 +- zone/perl_npc.cpp | 21 +++++++------ zone/waypoints.cpp | 63 ++++++++++++++++++++++++++----------- 6 files changed, 78 insertions(+), 37 deletions(-) diff --git a/zone/gm_commands/wpinfo.cpp b/zone/gm_commands/wpinfo.cpp index 4f0f988e5..55b775581 100755 --- a/zone/gm_commands/wpinfo.cpp +++ b/zone/gm_commands/wpinfo.cpp @@ -2,14 +2,25 @@ void command_wpinfo(Client *c, const Seperator *sep) { - Mob *t = c->GetTarget(); - - if (t == nullptr || !t->IsNPC()) { - c->Message(Chat::White, "You must target an NPC to use this."); + if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); return; } - NPC *n = t->CastToNPC(); - n->DisplayWaypointInfo(c); + auto target = c->GetTarget()->CastToNPC(); + + if (!target->GetGrid()) { + c->Message( + Chat::White, + fmt::format( + "{} ({}) is not a part of any grid.", + target->GetCleanName(), + target->GetID() + ).c_str() + ); + return; + } + + target->DisplayWaypointInfo(c); } diff --git a/zone/lua_npc.cpp b/zone/lua_npc.cpp index 74cd4f3c2..cb569c566 100644 --- a/zone/lua_npc.cpp +++ b/zone/lua_npc.cpp @@ -267,9 +267,9 @@ int Lua_NPC::GetMaxWp() { return self->GetMaxWp(); } -void Lua_NPC::DisplayWaypointInfo(Lua_Client to) { +void Lua_NPC::DisplayWaypointInfo(Lua_Client client) { Lua_Safe_Call_Void(); - self->DisplayWaypointInfo(to); + self->DisplayWaypointInfo(client); } void Lua_NPC::CalculateNewWaypoint() { diff --git a/zone/lua_npc.h b/zone/lua_npc.h index 6c329907b..0e6ada5a7 100644 --- a/zone/lua_npc.h +++ b/zone/lua_npc.h @@ -80,7 +80,7 @@ public: void StartSwarmTimer(uint32 duration); void DoClassAttacks(Lua_Mob target); int GetMaxWp(); - void DisplayWaypointInfo(Lua_Client to); + void DisplayWaypointInfo(Lua_Client client); void CalculateNewWaypoint(); void AssignWaypoints(int grid); void SetWaypointPause(); diff --git a/zone/npc.h b/zone/npc.h index da59ad591..387c512be 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -327,7 +327,7 @@ public: //waypoint crap int GetMaxWp() const { return max_wp; } - void DisplayWaypointInfo(Client *to); + void DisplayWaypointInfo(Client *client); void CalculateNewWaypoint(); void AssignWaypoints(int32 grid_id, int start_wp = 0); void SetWaypointPause(); diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index a145bf0b0..4bd19e4e4 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -775,20 +775,23 @@ XS(XS_NPC_DisplayWaypointInfo); /* prototype to pass -Wmissing-prototypes */ XS(XS_NPC_DisplayWaypointInfo) { dXSARGS; if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::DisplayWaypointInfo(THIS, Client* target)"); // @categories Script Utility + Perl_croak(aTHX_ "Usage: NPC::DisplayWaypointInfo(THIS, Client* client)"); // @categories Script Utility { - NPC *THIS; - Client *to; + NPC *THIS; + Client *client; VALIDATE_THIS_IS_NPC; if (sv_derived_from(ST(1), "Client")) { IV tmp = SvIV((SV *) SvRV(ST(1))); - to = INT2PTR(Client *, tmp); - } else - Perl_croak(aTHX_ "to is not of type Client"); - if (to == nullptr) - Perl_croak(aTHX_ "to is nullptr, avoiding crash."); + client = INT2PTR(Client *, tmp); + } else { + Perl_croak(aTHX_ "client is not of type Client"); + } - THIS->DisplayWaypointInfo(to); + if (!client) { + Perl_croak(aTHX_ "client is nullptr, avoiding crash."); + } + + THIS->DisplayWaypointInfo(client); } XSRETURN_EMPTY; } diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index 2c8338e53..34b809238 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -76,26 +76,53 @@ void NPC::AI_SetRoambox( roambox_min_delay = min_delay; } -void NPC::DisplayWaypointInfo(Client *c) { +void NPC::DisplayWaypointInfo(Client *client) { + client->Message( + Chat::White, + fmt::format( + "Waypoint Info for {} ({}) | Grid: {} Waypoint: {} of {}", + GetCleanName(), + GetID(), + GetGrid(), + GetCurWp(), + GetMaxWp() + ).c_str() + ); - c->Message(Chat::White, "Mob is on grid %d, in spawn group %d, on waypoint %d/%d", - GetGrid(), - GetSpawnGroupId(), - GetCurWp(), - GetMaxWp()); + client->Message( + Chat::White, + fmt::format( + "Waypoint Info for {} ({}) | Spawn Group: {} Spawn Point: {}", + GetCleanName(), + GetID(), + GetSpawnGroupId(), + GetSpawnPointID() + ).c_str() + ); - - std::vector::iterator cur, end; - cur = Waypoints.begin(); - end = Waypoints.end(); - for (; cur != end; ++cur) { - c->Message(Chat::White, "Waypoint %d: (%.2f,%.2f,%.2f,%.2f) pause %d", - cur->index, - cur->x, - cur->y, - cur->z, - cur->heading, - cur->pause); + + for (const auto& current_waypoint : Waypoints) { + client->Message( + Chat::White, + fmt::format( + "Waypoint {}{} | XYZ: {:.2f}, {:.2f}, {:.2f} Heading: {:.2f}{}", + current_waypoint.index, + current_waypoint.centerpoint ? " (Center)" : "", + current_waypoint.x, + current_waypoint.y, + current_waypoint.z, + current_waypoint.heading, + ( + current_waypoint.pause ? + fmt::format( + "{} ({})", + ConvertSecondsToTime(current_waypoint.pause), + current_waypoint.pause + ) : + "" + ) + ).c_str() + ); } } From 0f4f5d70466f775f0ba5faed99c682009662c2f0 Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Tue, 7 Dec 2021 14:15:28 -0600 Subject: [PATCH 503/624] RemoveAllAppearanceEffects sends all relevant data so NPC appearance doesn't get altered. (#1864) --- zone/perl_mob.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index 006492918..1c9e399a2 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -4858,7 +4858,10 @@ XS(XS_Mob_RemoveAllAppearanceEffects) { { Mob *THIS; VALIDATE_THIS_IS_MOB; - THIS->SendIllusionPacket(THIS->GetRace()); + THIS->SendIllusionPacket(THIS->GetRace(), THIS->GetGender(), THIS->GetTexture(), THIS->GetHelmTexture(), + THIS->GetHairColor(), THIS->GetBeardColor(), THIS->GetEyeColor1(), THIS->GetEyeColor2(), + THIS->GetHairStyle(), THIS->GetLuclinFace(), THIS->GetBeard(), 0xFF, + THIS->GetDrakkinHeritage(), THIS->GetDrakkinTattoo(), THIS->GetDrakkinDetails(), THIS->GetSize()); } XSRETURN_EMPTY; } From 1a1c3abc24b8f8ff4c8c4230e8021f9a905b377f Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Wed, 8 Dec 2021 18:18:06 -0500 Subject: [PATCH 504/624] [Commands] Cleanup #setstartzone Command. (#1853) - Add a message when setting start zone. - Cleanup logic. --- zone/command.cpp | 2 +- zone/gm_commands/setstartzone.cpp | 67 +++++++++++++++++++++---------- 2 files changed, 47 insertions(+), 22 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 1149f09e5..a17361d18 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -337,7 +337,7 @@ int command_init(void) command_add("setpvppoints", "[Amount] - Set your or your player target's PVP points", AccountStatus::GMAdmin, command_setpvppoints) || command_add("setskill", "[skillnum] [value] - Set your target's skill skillnum to value", AccountStatus::Guide, command_setskill) || command_add("setskillall", "[value] - Set all of your target's skills to value", AccountStatus::Guide, command_setskillall) || - command_add("setstartzone", "[zoneid] - Set target's starting zone. Set to zero to allow the player to use /setstartcity", AccountStatus::QuestTroupe, command_setstartzone) || + command_add("setstartzone", "[Zone ID|Zone Short Name] - Sets your or your target's starting zone (Use '0' or 'Reset' to allow the player use of /setstartcity)", AccountStatus::QuestTroupe, command_setstartzone) || command_add("setstat", "- Sets the stats to a specific value.", AccountStatus::Max, command_setstat) || command_add("setxp", "[value] - Set your or your player target's experience", AccountStatus::GMAdmin, command_setxp) || command_add("showbonusstats", "[item|spell|all] Shows bonus stats for target from items or spells. Shows both by default.", AccountStatus::Guide, command_showbonusstats) || diff --git a/zone/gm_commands/setstartzone.cpp b/zone/gm_commands/setstartzone.cpp index bff04d296..cf946041d 100755 --- a/zone/gm_commands/setstartzone.cpp +++ b/zone/gm_commands/setstartzone.cpp @@ -2,34 +2,59 @@ void command_setstartzone(Client *c, const Seperator *sep) { - uint32 startzone = 0; - Client *target = nullptr; - if (c->GetTarget() && c->GetTarget()->IsClient() && sep->arg[1][0] != 0) { - target = c->GetTarget()->CastToClient(); - } - else { - c->Message(Chat::White, "Usage: (needs PC target) #setstartzone zonename"); + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #setstartzone [Zone ID|Zone Short Name]"); c->Message( Chat::White, - "Optional Usage: Use '#setstartzone reset' or '#setstartzone 0' to clear a starting zone. Player can select a starting zone using /setstartcity" + "Optional Usage: Use '#setstartzone Reset' or '#setstartzone 0' to clear a starting zone. Player can select a starting zone using /setstartcity" ); return; } - if (sep->IsNumber(1)) { - startzone = atoi(sep->arg[1]); - } - else if (strcasecmp(sep->arg[1], "reset") == 0) { - startzone = 0; - } - else { - startzone = ZoneID(sep->arg[1]); - if (startzone == 0) { - c->Message(Chat::White, "Unable to locate zone '%s'", sep->arg[1]); - return; - } + auto target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); } - target->SetStartZone(startzone); + auto zone_id = ( + sep->IsNumber(1) ? + std::stoul(sep->arg[1]) : + ZoneID(sep->arg[1]) + ); + + target->SetStartZone(zone_id); + + bool is_reset = ( + !strcasecmp(sep->arg[1], "reset") || + zone_id == 0 + ); + + c->Message( + Chat::White, + fmt::format( + "Start Zone {} for {} |{}", + is_reset ? "Reset" : "Changed", + ( + c == target ? + "Yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), + ( + zone_id ? + fmt::format( + " {} ({}) ID: {}", + ZoneLongName(zone_id), + ZoneName(zone_id), + zone_id + ) : + "" + ) + ).c_str() + ); } From 94166e0f958ed11222a85b797b37dd107c3e1f6b Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Wed, 8 Dec 2021 18:18:14 -0500 Subject: [PATCH 505/624] [Commands] Add #unmemspell and #unmemspells Commands. (#1867) - Add #unmemspell [Spell ID] command to unmemorize a spell by ID from you or your target. - Add #unmemspells command to unmemorize all spells from you or your target. - Cleanup #memspell command and change arguments from #memspell [Slot] [Spell ID] to #memspell [Spell ID] [Spell Gem] for easier use. - Add #memspell [Spell ID] functionality to memorize to first open spell gem if there are any using FindEmptyMemSlot helper method. - Rename client->FindMemmedSpellByID(spell_id) to FindMemmedSpellBySpellID(spell_id). - Add client->FindEmptyMemSlot() helper method. - Add $client->FindEmptyMemSlot() to Perl. - Add client:FindEmptyMemSlot() to Lua. - Add $client->FindMemmedSpellBySpellID(spell_id) to Perl. - Add client:FindMemmedSpellBySpellID(spell_id) to Lua. --- zone/CMakeLists.txt | 2 + zone/client.h | 3 +- zone/command.cpp | 4 +- zone/command.h | 2 + zone/gm_commands/memspell.cpp | 72 ++++++++++++++++++++++--------- zone/gm_commands/unmemspell.cpp | 68 +++++++++++++++++++++++++++++ zone/gm_commands/unmemspells.cpp | 43 +++++++++++++++++++ zone/lua_client.cpp | 12 ++++++ zone/lua_client.h | 2 + zone/perl_client.cpp | 37 ++++++++++++++++ zone/spells.cpp | 74 ++++++++++++++++++-------------- 11 files changed, 265 insertions(+), 54 deletions(-) create mode 100644 zone/gm_commands/unmemspell.cpp create mode 100644 zone/gm_commands/unmemspells.cpp diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index b3dcb597a..0a22718ea 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -541,6 +541,8 @@ SET(gm_commands gm_commands/undyeme.cpp gm_commands/unfreeze.cpp gm_commands/unlock.cpp + gm_commands/unmemspell.cpp + gm_commands/unmemspells.cpp gm_commands/unscribespell.cpp gm_commands/unscribespells.cpp gm_commands/untraindisc.cpp diff --git a/zone/client.h b/zone/client.h index 5dea68d2b..649f9d31f 100644 --- a/zone/client.h +++ b/zone/client.h @@ -792,8 +792,9 @@ public: void UnmemSpell(int slot, bool update_client = true); void UnmemSpellBySpellID(int32 spell_id); void UnmemSpellAll(bool update_client = true); + int FindEmptyMemSlot(); uint16 FindMemmedSpellBySlot(int slot); - int FindMemmedSpellByID(uint16 spell_id); + int FindMemmedSpellBySpellID(uint16 spell_id); int MemmedCount(); std::vector GetLearnableDisciplines(uint8 min_level = 1, uint8 max_level = 0); std::vector GetLearnedDisciplines(); diff --git a/zone/command.cpp b/zone/command.cpp index a17361d18..c1e16aa28 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -241,7 +241,7 @@ int command_init(void) command_add("makepet", "[level] [class] [race] [texture] - Make a pet", AccountStatus::Guide, command_makepet) || command_add("mana", "- Fill your or your target's mana", AccountStatus::Guide, command_mana) || command_add("maxskills", "Maxes skills for you.", AccountStatus::GMMgmt, command_max_all_skills) || - command_add("memspell", "[Slot] [Spell ID] - Memorize a Spell by ID in the specified Slot", AccountStatus::Guide, command_memspell) || + command_add("memspell", "[Spell ID] [Spell Gem] - Memorize a Spell by ID to the specified Spell Gem for you or your target", AccountStatus::Guide, command_memspell) || command_add("merchant_close_shop", "Closes a merchant shop", AccountStatus::GMAdmin, command_merchantcloseshop) || command_add("merchant_open_shop", "Opens a merchants shop", AccountStatus::GMAdmin, command_merchantopenshop) || command_add("modifynpcstat", "- Modifys a NPC's stats", AccountStatus::GMLeadAdmin, command_modifynpcstat) || @@ -378,6 +378,8 @@ int command_init(void) command_add("undyeme", "- Remove dye from all of your armor slots", AccountStatus::Player, command_undyeme) || command_add("unfreeze", "- Unfreeze your target", AccountStatus::QuestTroupe, command_unfreeze) || command_add("unlock", "- Unlock the worldserver", AccountStatus::GMLeadAdmin, command_unlock) || + command_add("unmemspell", "[Spell ID] - Unmemorize a Spell by ID for you or your target", AccountStatus::Guide, command_unmemspell) || + command_add("unmemspells", " - Unmemorize all spells for you or your target", AccountStatus::Guide, command_unmemspells) || command_add("unscribespell", "[spellid] - Unscribe specified spell from your target's spell book.", AccountStatus::GMCoder, command_unscribespell) || command_add("unscribespells", "- Clear out your or your player target's spell book.", AccountStatus::GMCoder, command_unscribespells) || command_add("untraindisc", "[spellid] - Untrain specified discipline from your target.", AccountStatus::GMCoder, command_untraindisc) || diff --git a/zone/command.h b/zone/command.h index d9113a5d5..9d64004a5 100644 --- a/zone/command.h +++ b/zone/command.h @@ -301,6 +301,8 @@ void command_undye(Client *c, const Seperator *sep); void command_undyeme(Client *c, const Seperator *sep); void command_unfreeze(Client *c, const Seperator *sep); void command_unlock(Client *c, const Seperator *sep); +void command_unmemspell(Client *c, const Seperator *sep); +void command_unmemspells(Client *c, const Seperator *sep); void command_unscribespell(Client *c, const Seperator *sep); void command_unscribespells(Client *c, const Seperator *sep); void command_untraindisc(Client *c, const Seperator *sep); diff --git a/zone/gm_commands/memspell.cpp b/zone/gm_commands/memspell.cpp index 6fce2900f..673d2efd0 100755 --- a/zone/gm_commands/memspell.cpp +++ b/zone/gm_commands/memspell.cpp @@ -5,43 +5,75 @@ void command_memspell(Client *c, const Seperator *sep) int arguments = sep->argnum; if ( !arguments || - !sep->IsNumber(1) || - !sep->IsNumber(2) + !sep->IsNumber(1) ) { - c->Message(Chat::White, "Usage: #memspell [Slot] [Spell ID]"); + c->Message(Chat::White, "Usage: #memspell [Spell ID] [Spell Gem]"); return; } - Client* target = c; + auto target = c; if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { target = c->GetTarget()->CastToClient(); } - uint32 spell_gem = std::stoul(sep->arg[1]); - uint32 slot = (spell_gem - 1); - uint16 spell_id = static_cast(std::stoul(sep->arg[2])); - if ( - IsValidSpell(spell_id) && - slot < EQ::spells::SPELL_GEM_COUNT - ) { - target->MemSpell(spell_id, slot); + auto spell_id = static_cast(std::stoul(sep->arg[1])); + if (!IsValidSpell(spell_id)) { c->Message( Chat::White, fmt::format( - "{} ({}) has been memorized to Spell Gem {} ({}) for {}.", - GetSpellName(spell_id), - spell_id, - spell_gem, - slot, + "Spell ID {} could not be found.", + spell_id + ).c_str() + ); + return; + } + + auto empty_slot = target->FindEmptyMemSlot(); + if (empty_slot == -1) { + c->Message( + Chat::White, + fmt::format( + "{} not have a place to memorize {} ({}).", ( c == target ? - "yourself" : + "You do" : fmt::format( - "{} ({})", + "{} ({}) does", target->GetCleanName(), target->GetID() ) - ) + ), + GetSpellName(spell_id), + spell_id + ).c_str() + ); + return; + } + + auto spell_gem = sep->IsNumber(2) ? std::stoul(sep->arg[2]) : empty_slot; + if (spell_gem > EQ::spells::SPELL_GEM_COUNT) { + c->Message( + Chat::White, + fmt::format( + "Spell Gems range from 0 to {}.", + EQ::spells::SPELL_GEM_COUNT + ).c_str() + ); + return; + } + + target->MemSpell(spell_id, spell_gem); + + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "{} ({}) memorized to spell gem {} for {} ({}).", + GetSpellName(spell_id), + spell_id, + spell_gem, + target->GetCleanName(), + target->GetID() ).c_str() ); } diff --git a/zone/gm_commands/unmemspell.cpp b/zone/gm_commands/unmemspell.cpp new file mode 100644 index 000000000..67ffc5664 --- /dev/null +++ b/zone/gm_commands/unmemspell.cpp @@ -0,0 +1,68 @@ +#include "../client.h" + +void command_unmemspell(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if ( + !arguments || + !sep->IsNumber(1) + ) { + c->Message(Chat::White, "Usage: #unmemspell [Spell ID]"); + return; + } + + auto target = c; + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + target = c->GetTarget()->CastToClient(); + } + + auto spell_id = static_cast(std::stoul(sep->arg[1])); + if (!IsValidSpell(spell_id)) { + c->Message( + Chat::White, + fmt::format( + "Spell ID {} could not be found.", + spell_id + ).c_str() + ); + return; + } + + auto spell_gem = target->FindMemmedSpellBySpellID(spell_id); + if (spell_gem == -1) { + c->Message( + Chat::White, + fmt::format( + "{} not have {} ({}) memorized.", + ( + c == target ? + "You do" : + fmt::format( + "{} ({}) does", + target->GetCleanName(), + target->GetID() + ) + ), + GetSpellName(spell_id), + spell_id + ).c_str() + ); + return; + } + + target->UnmemSpellBySpellID(spell_id); + + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "{} ({}) unmemorized for {} ({}) from spell gem {}.", + GetSpellName(spell_id), + spell_id, + target->GetCleanName(), + target->GetID(), + spell_gem + ).c_str() + ); + } +} diff --git a/zone/gm_commands/unmemspells.cpp b/zone/gm_commands/unmemspells.cpp new file mode 100644 index 000000000..f5f6f0987 --- /dev/null +++ b/zone/gm_commands/unmemspells.cpp @@ -0,0 +1,43 @@ +#include "../client.h" + +void command_unmemspells(Client *c, const Seperator *sep) +{ + auto target = c; + if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { + target = c->GetTarget()->CastToClient(); + } + + auto memmed_count = target->MemmedCount(); + if (!memmed_count) { + c->Message( + Chat::White, + fmt::format( + "{} no spells to unmemorize.", + ( + c == target ? + "You have" : + fmt::format( + "{} ({}) has", + target->GetCleanName(), + target->GetID() + ) + ) + ).c_str() + ); + return; + } + + target->UnmemSpellAll(); + + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "{} ({}) has had {} spells unmemorized.", + target->GetCleanName(), + target->GetID(), + memmed_count + ).c_str() + ); + } +} diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index a7459cff3..ec4c1bd65 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -650,6 +650,16 @@ uint16 Lua_Client::FindMemmedSpellBySlot(int slot) { return self->FindMemmedSpellBySlot(slot); } +int Lua_Client::FindMemmedSpellBySpellID(uint16 spell_id) { + Lua_Safe_Call_Int(); + return self->FindMemmedSpellBySpellID(spell_id); +} + +int Lua_Client::FindEmptyMemSlot() { + Lua_Safe_Call_Int(); + return self->FindEmptyMemSlot(); +} + int Lua_Client::MemmedCount() { Lua_Safe_Call_Int(); return self->MemmedCount(); @@ -2378,7 +2388,9 @@ luabind::scope lua_register_client() { .def("Escape", (void(Lua_Client::*)(void))&Lua_Client::Escape) .def("FailTask", (void(Lua_Client::*)(int))&Lua_Client::FailTask) .def("FilteredMessage", &Lua_Client::FilteredMessage) + .def("FindEmptyMemSlot", (int(Lua_Client::*)(void))&Lua_Client::FindEmptyMemSlot) .def("FindMemmedSpellBySlot", (uint16(Lua_Client::*)(int))&Lua_Client::FindMemmedSpellBySlot) + .def("FindMemmedSpellBySpellID", (int(Lua_Client::*)(uint16))&Lua_Client::FindMemmedSpellBySpellID) .def("FindSpellBookSlotBySpellID", (int(Lua_Client::*)(int))&Lua_Client::FindSpellBookSlotBySpellID) .def("Fling", (void(Lua_Client::*)(float,float,float,float))&Lua_Client::Fling) .def("Fling", (void(Lua_Client::*)(float,float,float,float,bool))&Lua_Client::Fling) diff --git a/zone/lua_client.h b/zone/lua_client.h index 5bab5c219..7e91ea9f1 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -163,7 +163,9 @@ public: void UnmemSpellBySpellID(int32 spell_id); void UnmemSpellAll(); void UnmemSpellAll(bool update_client); + int FindEmptyMemSlot(); uint16 FindMemmedSpellBySlot(int slot); + int FindMemmedSpellBySpellID(uint16 spell_id); int MemmedCount(); luabind::object GetLearnableDisciplines(lua_State* L); luabind::object GetLearnableDisciplines(lua_State* L, uint8 min_level); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index fc300e200..bb5ee2310 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -1897,6 +1897,23 @@ XS(XS_Client_UnmemSpellAll) { XSRETURN_EMPTY; } +XS(XS_Client_FindEmptyMemSlot); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_FindEmptyMemSlot) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::FindEmptyMemSlot(THIS)"); // @categories Account and Character, Spells and Disciplines + { + Client *THIS; + int RETVAL; + dXSTARG; + VALIDATE_THIS_IS_CLIENT; + RETVAL = THIS->FindEmptyMemSlot(); + XSprePUSH; + PUSHi((IV) RETVAL); + } + XSRETURN(1); +} + XS(XS_Client_FindMemmedSpellBySlot); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_FindMemmedSpellBySlot) { dXSARGS; @@ -1915,6 +1932,24 @@ XS(XS_Client_FindMemmedSpellBySlot) { XSRETURN(1); } +XS(XS_Client_FindMemmedSpellBySpellID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_FindMemmedSpellBySpellID) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::FindMemmedSpellBySpellID(THIS, uint16 spell_id)"); // @categories Account and Character, Spells and Disciplines + { + Client *THIS; + int RETVAL; + dXSTARG; + uint16 spell_id = (uint16) SvUV(ST(1)); + VALIDATE_THIS_IS_CLIENT; + RETVAL = THIS->FindMemmedSpellBySpellID(spell_id); + XSprePUSH; + PUSHi((IV) RETVAL); + } + XSRETURN(1); +} + XS(XS_Client_MemmedCount); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_MemmedCount) { dXSARGS; @@ -5960,7 +5995,9 @@ XS(boot_Client) { newXSproto(strcpy(buf, "Escape"), XS_Client_Escape, file, "$"); newXSproto(strcpy(buf, "ExpeditionMessage"), XS_Client_ExpeditionMessage, file, "$$$"); newXSproto(strcpy(buf, "FailTask"), XS_Client_FailTask, file, "$$"); + newXSproto(strcpy(buf, "FindEmptyMemSlot"), XS_Client_FindEmptyMemSlot, file, "$"); newXSproto(strcpy(buf, "FindMemmedSpellBySlot"), XS_Client_FindMemmedSpellBySlot, file, "$$"); + newXSproto(strcpy(buf, "FindMemmedSpellBySpellID"), XS_Client_FindMemmedSpellBySpellID, file, "$$"); newXSproto(strcpy(buf, "Fling"), XS_Client_Fling, file, "$$$$$;$$"); newXSproto(strcpy(buf, "ForageItem"), XS_Client_ForageItem, file, "$"); newXSproto(strcpy(buf, "Freeze"), XS_Client_Freeze, file, "$"); diff --git a/zone/spells.cpp b/zone/spells.cpp index 60d7e9516..e14d100ce 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -5246,7 +5246,7 @@ void Mob::SendSpellBarEnable(uint16 spell_id) manachange->spell_id = spell_id; manachange->stamina = CastToClient()->GetEndurance(); manachange->keepcasting = 0; - manachange->slot = CastToClient()->FindMemmedSpellByID(spell_id); + manachange->slot = CastToClient()->FindMemmedSpellBySpellID(spell_id); outapp->priority = 6; CastToClient()->QueuePacket(outapp); safe_delete(outapp); @@ -5377,88 +5377,98 @@ void Client::MakeBuffFadePacket(uint16 spell_id, int slot_id, bool send_message) void Client::MemSpell(uint16 spell_id, int slot, bool update_client) { - if(slot >= EQ::spells::SPELL_GEM_COUNT || slot < 0) + if (slot >= EQ::spells::SPELL_GEM_COUNT || slot < 0) { return; + } - if(update_client) - { - if(m_pp.mem_spells[slot] != 0xFFFFFFFF) + if(update_client) { + if (IsValidSpell(m_pp.mem_spells[slot])) { UnmemSpell(slot, update_client); + } } m_pp.mem_spells[slot] = spell_id; LogSpells("Spell [{}] memorized into slot [{}]", spell_id, slot); - database.SaveCharacterMemorizedSpell(this->CharacterID(), m_pp.mem_spells[slot], slot); + database.SaveCharacterMemorizedSpell(CharacterID(), m_pp.mem_spells[slot], slot); - if(update_client) - { + if(update_client) { MemorizeSpell(slot, spell_id, memSpellMemorize); } } void Client::UnmemSpell(int slot, bool update_client) { - if(slot > EQ::spells::SPELL_GEM_COUNT || slot < 0) + if (slot >= EQ::spells::SPELL_GEM_COUNT || slot < 0) { return; + } LogSpells("Spell [{}] forgotten from slot [{}]", m_pp.mem_spells[slot], slot); m_pp.mem_spells[slot] = 0xFFFFFFFF; - database.DeleteCharacterMemorizedSpell(this->CharacterID(), m_pp.mem_spells[slot], slot); + database.DeleteCharacterMemorizedSpell(CharacterID(), m_pp.mem_spells[slot], slot); - if(update_client) - { + if(update_client) { MemorizeSpell(slot, m_pp.mem_spells[slot], memSpellForget); } } void Client::UnmemSpellBySpellID(int32 spell_id) { - for(int i = 0; i < EQ::spells::SPELL_GEM_COUNT; i++) { - if(m_pp.mem_spells[i] == spell_id) { - UnmemSpell(i, true); - break; - } + auto spell_gem = FindMemmedSpellBySpellID(spell_id); + if (spell_gem >= EQ::spells::SPELL_GEM_COUNT || spell_gem < 0) { + return; } + + UnmemSpell(spell_gem); } void Client::UnmemSpellAll(bool update_client) { - int i; - - for(i = 0; i < EQ::spells::SPELL_GEM_COUNT; i++) - if(m_pp.mem_spells[i] != 0xFFFFFFFF) - UnmemSpell(i, update_client); + for (int spell_gem = 0; spell_gem < EQ::spells::SPELL_GEM_COUNT; spell_gem++) { + if (IsValidSpell(m_pp.mem_spells[spell_gem])) { + UnmemSpell(spell_gem, update_client); + } + } } uint32 Client::GetSpellIDByBookSlot(int book_slot) { if (book_slot <= EQ::spells::SPELLBOOK_SIZE) { return GetSpellByBookSlot(book_slot); } - return -1; //default + return -1; +} + +int Client::FindEmptyMemSlot() { + for (int spell_gem = 0; spell_gem < EQ::spells::SPELL_GEM_COUNT; spell_gem++) { + if (!IsValidSpell(m_pp.mem_spells[spell_gem])) { + return spell_gem; + } + } + return -1; } uint16 Client::FindMemmedSpellBySlot(int slot) { - if (m_pp.mem_spells[slot] != 0xFFFFFFFF) + if (IsValidSpell(m_pp.mem_spells[slot])) { return m_pp.mem_spells[slot]; - + } return 0; } int Client::MemmedCount() { int memmed_count = 0; - for (int i = 0; i < EQ::spells::SPELL_GEM_COUNT; i++) - if (m_pp.mem_spells[i] != 0xFFFFFFFF) + for (int spell_gem = 0; spell_gem < EQ::spells::SPELL_GEM_COUNT; spell_gem++) { + if (IsValidSpell(m_pp.mem_spells[spell_gem])) { memmed_count++; - + } + } return memmed_count; } -int Client::FindMemmedSpellByID(uint16 spell_id) { - for (int i = 0; i < EQ::spells::SPELL_GEM_COUNT; i++) { - if (m_pp.mem_spells[i] == spell_id) { - return i; +int Client::FindMemmedSpellBySpellID(uint16 spell_id) { + for (int spell_gem = 0; spell_gem < EQ::spells::SPELL_GEM_COUNT; spell_gem++) { + if (IsValidSpell(m_pp.mem_spells[spell_gem]) && m_pp.mem_spells[spell_gem] == spell_id) { + return spell_gem; } } return -1; From 294e51fca76d56bbd44f34720cdfc0487fcdad44 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Wed, 8 Dec 2021 18:58:06 -0500 Subject: [PATCH 506/624] [Commands] Add #setaltcurrency Command. (#1850) * [Commands] Add #setaltcurrency Command. - Add #setaltcurrency [Currency ID] [Amount] command to allow you to set a specific alternate currency to a value. - Add Zone::GetCurrencyID() and Zone::GetCurrencyItemID() helper methods. - Cleanup loops through zone->AlternateCurrencies. - Utilize helper methods where necessary. - Convert old methods parameters and return values from int to uint32 where necessary. * Typo. --- zone/CMakeLists.txt | 1 + zone/client.cpp | 33 ++++++---------- zone/client_packet.cpp | 54 ++++++++++---------------- zone/command.cpp | 1 + zone/command.h | 1 + zone/embparser_api.cpp | 6 +-- zone/gm_commands/setaltcurrency.cpp | 59 +++++++++++++++++++++++++++++ zone/lua_general.cpp | 4 +- zone/questmgr.cpp | 24 ++---------- zone/questmgr.h | 4 +- zone/zone.cpp | 37 ++++++++++++++++-- zone/zone.h | 3 ++ 12 files changed, 141 insertions(+), 86 deletions(-) create mode 100644 zone/gm_commands/setaltcurrency.cpp diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 0a22718ea..12034cbe9 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -488,6 +488,7 @@ SET(gm_commands gm_commands/set_adventure_points.cpp gm_commands/setaapts.cpp gm_commands/setaaxp.cpp + gm_commands/setaltcurrency.cpp gm_commands/setanim.cpp gm_commands/setcrystals.cpp gm_commands/setendurance.cpp diff --git a/zone/client.cpp b/zone/client.cpp index 64453cf11..5a8bc9d88 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -7082,23 +7082,16 @@ void Client::SendAltCurrencies() { altc->opcode = ALT_CURRENCY_OP_POPULATE; altc->count = count; - uint32 i = 0; - auto iter = zone->AlternateCurrencies.begin(); - while(iter != zone->AlternateCurrencies.end()) { - const EQ::ItemData* item = database.GetItem((*iter).item_id); - altc->entries[i].currency_number = (*iter).id; - altc->entries[i].unknown00 = 1; - altc->entries[i].currency_number2 = (*iter).id; - altc->entries[i].item_id = (*iter).item_id; - if(item) { - altc->entries[i].item_icon = item->Icon; - altc->entries[i].stack_size = item->StackSize; - } else { - altc->entries[i].item_icon = 1000; - altc->entries[i].stack_size = 1000; - } - i++; - ++iter; + uint32 currency_id = 0; + for (const auto& alternate_currency : zone->AlternateCurrencies) { + const EQ::ItemData* item = database.GetItem(alternate_currency.item_id); + altc->entries[currency_id].currency_number = alternate_currency.id; + altc->entries[currency_id].unknown00 = 1; + altc->entries[currency_id].currency_number2 = alternate_currency.id; + altc->entries[currency_id].item_id = alternate_currency.item_id; + altc->entries[currency_id].item_icon = item ? item->Icon : 1000; + altc->entries[currency_id].stack_size = item ? item->StackSize : 1000; + currency_id++; } FastQueuePacket(&outapp); @@ -7153,10 +7146,8 @@ void Client::AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 me void Client::SendAlternateCurrencyValues() { - auto iter = zone->AlternateCurrencies.begin(); - while(iter != zone->AlternateCurrencies.end()) { - SendAlternateCurrencyValue((*iter).id, false); - ++iter; + for (const auto& alternate_currency : zone->AlternateCurrencies) { + SendAlternateCurrencyValue(alternate_currency.id, false); } } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index ddb2d04f6..197d08364 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -2465,39 +2465,31 @@ 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 (DistanceSquared(m_Position, tar->GetPosition()) > USE_NPC_RANGE2) - return; - - if (tar->GetClass() != ALT_CURRENCY_MERCHANT) { + auto target = entity_list.GetNPCByID(*((uint32*)app->pBuffer)); + if (target) { + if (DistanceSquared(m_Position, target->GetPosition()) > USE_NPC_RANGE2) { return; } - uint32 alt_cur_id = tar->GetAltCurrencyType(); - if (alt_cur_id == 0) { + if (target->GetClass() != ALT_CURRENCY_MERCHANT) { return; } - auto 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; + uint32 currency_id = target->GetAltCurrencyType(); + if (!currency_id) { + return; } - if (!found) { + auto currency_item_id = zone->GetCurrencyItemID(currency_id); + if (!currency_item_id) { 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; + ss << currency_id << "|1|" << currency_id; uint32 count = 0; - uint32 merchant_id = tar->MerchantType; + uint32 merchant_id = target->MerchantType; const EQ::ItemData *item = nullptr; std::list merlist = zone->merchanttable[merchant_id]; @@ -2508,14 +2500,16 @@ void Client::Handle_OP_AltCurrencyMerchantRequest(const EQApplicationPacket *app continue; } - int32 fac = tar->GetPrimaryFaction(); - if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + auto faction_id = target->GetPrimaryFaction(); + if ( + faction_id && + GetModCharacterFactionLevel(faction_id) < ml.faction_required + ) { continue; } item = database.GetItem(ml.item); - if (item) - { + if (item) { item_ss << "^" << item->Name << "|"; item_ss << item->ID << "|"; item_ss << ml.alt_currency_cost << "|"; @@ -2529,8 +2523,7 @@ void Client::Handle_OP_AltCurrencyMerchantRequest(const EQApplicationPacket *app if (count > 0) { ss << "|" << count << item_ss.str(); - } - else { + } else { ss << "|0"; } @@ -2628,16 +2621,9 @@ 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; - auto iter = zone->AlternateCurrencies.begin(); - while (iter != zone->AlternateCurrencies.end()) { - if ((*iter).id == reclaim->currency_id) { - item_id = (*iter).item_id; - } - ++iter; - } + uint32 item_id = zone->GetCurrencyItemID(reclaim->currency_id); - if (item_id == 0) { + if (!item_id) { return; } diff --git a/zone/command.cpp b/zone/command.cpp index c1e16aa28..fb6b32c75 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -324,6 +324,7 @@ int command_init(void) command_add("setaapts", "[AA|Group|Raid] [AA Amount] - Set your or your player target's Available AA Points by Type", AccountStatus::GMAdmin, command_setaapts) || command_add("setaaxp", "[AA|Group|Raid] [AA Experience] - Set your or your player target's AA Experience by Type", AccountStatus::GMAdmin, command_setaaxp) || command_add("setadventurepoints", "- Set your or your player target's available adventure points", AccountStatus::GMLeadAdmin, command_set_adventure_points) || + command_add("setaltcurrency", "[Currency ID] [Amount] - Set your or your target's available Alternate Currency by Currency ID", AccountStatus::GMAdmin, command_setaltcurrency) || command_add("setanim", "[Animation ID (IDs are 0 to 4)] - Set target's appearance to Animation ID", AccountStatus::GMMgmt, command_setanim) || command_add("setcrystals", "[value] - Set your or your player target's available radiant or ebon crystals", AccountStatus::GMAdmin, command_setcrystals) || command_add("setendurance", "[Endurance] - Set your or your target's Endurance", AccountStatus::GMAdmin, command_setendurance) || diff --git a/zone/command.h b/zone/command.h index 9d64004a5..a4c758a16 100644 --- a/zone/command.h +++ b/zone/command.h @@ -245,6 +245,7 @@ void command_serverrules(Client *c, const Seperator *sep); void command_set_adventure_points(Client *c, const Seperator *sep); void command_setaapts(Client *c, const Seperator *sep); void command_setaaxp(Client *c, const Seperator *sep); +void command_setaltcurrency(Client *c, const Seperator *sep); void command_setanim(Client *c, const Seperator *sep); void command_setcrystals(Client *c, const Seperator *sep); void command_setendurance(Client *c, const Seperator *sep); diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 47b14a898..e78112de2 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -3483,7 +3483,7 @@ XS(XS__getcurrencyitemid) { dXSTARG; int RETVAL; - int currency_id = (int) SvUV(ST(0)); + uint32 currency_id = (uint32) SvUV(ST(0)); RETVAL = quest_manager.getcurrencyitemid(currency_id); @@ -3499,8 +3499,8 @@ XS(XS__getcurrencyid) { Perl_croak(aTHX_ "Usage: quest::getcurrencyid(uint32 item_id)"); dXSTARG; - int RETVAL; - uint32 item_id = (int) SvUV(ST(0)); + uint32 RETVAL; + uint32 item_id = (uint32) SvUV(ST(0)); RETVAL = quest_manager.getcurrencyid(item_id); XSprePUSH; diff --git a/zone/gm_commands/setaltcurrency.cpp b/zone/gm_commands/setaltcurrency.cpp new file mode 100644 index 000000000..339542d6e --- /dev/null +++ b/zone/gm_commands/setaltcurrency.cpp @@ -0,0 +1,59 @@ +#include "../client.h" + +void command_setaltcurrency(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if ( + arguments < 2 || + !sep->IsNumber(1) || + !sep->IsNumber(2) + ) { + c->Message(Chat::White, "Command Syntax: #setaltcurrency [Currency ID] [Amount]"); + return; + } + + auto target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + + auto currency_id = std::stoul(sep->arg[1]); + auto amount = static_cast(std::min(std::stoll(sep->arg[2]), (long long) 2000000000)); + uint32 currency_item_id = zone->GetCurrencyItemID(currency_id); + if (!currency_item_id) { + c->Message( + Chat::White, + fmt::format( + "Currency ID {} could not be found.", + currency_id + ).c_str() + ); + return; + } + + target->SetAlternateCurrencyValue(currency_id, amount); + + c->Message( + Chat::White, + fmt::format( + "{} now {} {} {}.", + ( + c == target ? + "You" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ), + c == target ? "have" : "has", + ( + amount ? + std::to_string(amount) : + "no" + ), + database.CreateItemLink(currency_item_id) + ).c_str() + ); +} + diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index d3a5916d0..a806be066 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -918,11 +918,11 @@ std::string lua_get_class_name(uint8 class_id, uint8 level) { return quest_manager.getclassname(class_id, level); } -int lua_get_currency_id(uint32 item_id) { +uint32 lua_get_currency_id(uint32 item_id) { return quest_manager.getcurrencyid(item_id); } -int lua_get_currency_item_id(int currency_id) { +uint32 lua_get_currency_item_id(uint32 currency_id) { return quest_manager.getcurrencyitemid(currency_id); } diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 7a4d722a7..7ee12f466 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -3025,28 +3025,12 @@ std::string QuestManager::getclassname(uint8 class_id, uint8 level) { return GetClassIDName(class_id, level); } -int QuestManager::getcurrencyid(uint32 item_id) { - auto iter = zone->AlternateCurrencies.begin(); - while (iter != zone->AlternateCurrencies.end()) { - if (item_id == (*iter).item_id) { - return (*iter).id; - } - ++iter; - } - return 0; +uint32 QuestManager::getcurrencyid(uint32 item_id) { + return zone->GetCurrencyID(item_id); } -int QuestManager::getcurrencyitemid(int currency_id) { - if (currency_id > 0) { - auto iter = zone->AlternateCurrencies.begin(); - while (iter != zone->AlternateCurrencies.end()) { - if (currency_id == (*iter).id) { - return (*iter).item_id; - } - ++iter; - } - } - return 0; +uint32 QuestManager::getcurrencyitemid(uint32 currency_id) { + return zone->GetCurrencyItemID(currency_id); } const char* QuestManager::getguildnamebyid(int guild_id) { diff --git a/zone/questmgr.h b/zone/questmgr.h index c03633c89..3bd3ddfe3 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -276,8 +276,8 @@ public: std::string getcharnamebyid(uint32 char_id); uint32 getcharidbyname(const char* name); std::string getclassname(uint8 class_id, uint8 level = 0); - int getcurrencyid(uint32 item_id); - int getcurrencyitemid(int currency_id); + uint32 getcurrencyid(uint32 item_id); + uint32 getcurrencyitemid(uint32 currency_id); const char* getguildnamebyid(int guild_id); int getguildidbycharid(uint32 char_id); int getgroupidbycharid(uint32 char_id); diff --git a/zone/zone.cpp b/zone/zone.cpp index 9ab6fe545..2bfbd882b 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -2421,10 +2421,9 @@ void Zone::LoadAlternateCurrencies() return; } - for (auto row = results.begin(); row != results.end(); ++row) - { - current_currency.id = atoi(row[0]); - current_currency.item_id = atoi(row[1]); + for (auto row : results) { + current_currency.id = std::stoul(row[0]); + current_currency.item_id = std::stoul(row[1]); AlternateCurrencies.push_back(current_currency); } @@ -2744,3 +2743,33 @@ DynamicZone* Zone::GetDynamicZone() return nullptr; } + +uint32 Zone::GetCurrencyID(uint32 item_id) +{ + if (!item_id) { + return 0; + } + + for (const auto& alternate_currency : AlternateCurrencies) { + if (item_id == alternate_currency.item_id) { + return alternate_currency.id; + } + } + + return 0; +} + +uint32 Zone::GetCurrencyItemID(uint32 currency_id) +{ + if (!currency_id) { + return 0; + } + + for (const auto& alternate_currency : AlternateCurrencies) { + if (currency_id == alternate_currency.id) { + return alternate_currency.item_id; + } + } + + return 0; +} \ No newline at end of file diff --git a/zone/zone.h b/zone/zone.h index 814ddcf83..23262778d 100755 --- a/zone/zone.h +++ b/zone/zone.h @@ -241,6 +241,9 @@ public: uint32 GetSpawnKillCount(uint32 in_spawnid); uint32 GetTempMerchantQuantity(uint32 NPCID, uint32 Slot); + uint32 GetCurrencyID(uint32 item_id); + uint32 GetCurrencyItemID(uint32 currency_id); + void AddAggroMob() { aggroedmobs++; } void AddAuth(ServerZoneIncomingClient_Struct *szic); void ChangeWeather(); From 42f439c4b71ebf9fe491a861e77d8c53de7dc15e Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Wed, 8 Dec 2021 21:39:35 -0600 Subject: [PATCH 507/624] [Quest API] Add ResetCastbarCooldownBySlot / ResetCastbarCooldownBySpellID / ResetAllCastbarCooldowns (#1873) * New function to reset spellbar in perl/lua ResetCastbarCooldownsBySlot -1 for all slots and anything else to do it by slot number * Add ResetCastbarCooldownsBySlot / ResetCastbarCooldownsBySpellID / ResetAllCastbarCooldowns --- zone/client.h | 4 +++ zone/lua_client.cpp | 18 +++++++++++++ zone/lua_client.h | 3 +++ zone/perl_client.cpp | 44 ++++++++++++++++++++++++++++++ zone/spells.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 133 insertions(+) diff --git a/zone/client.h b/zone/client.h index 649f9d31f..a9266230d 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1014,6 +1014,10 @@ public: void SetLinkedSpellReuseTimer(uint32 timer_id, uint32 duration); bool IsLinkedSpellReuseTimerReady(uint32 timer_id); + + void ResetCastbarCooldownBySlot(int slot); + void ResetAllCastbarCooldowns(); + void ResetCastbarCooldownBySpellID(uint32 spell_id); bool CheckTitle(int titleset); void EnableTitle(int titleset); diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index ec4c1bd65..8da35c6fa 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -2321,6 +2321,21 @@ int Lua_Client::GetNextAvailableDisciplineSlot(int starting_slot) { return self->GetNextAvailableDisciplineSlot(starting_slot); } +void Lua_Client::ResetCastbarCooldownBySlot(int slot) { + Lua_Safe_Call_Void(); + self->ResetCastbarCooldownBySlot(slot); +} + +void Lua_Client::ResetAllCastbarCooldowns() { + Lua_Safe_Call_Void(); + self->ResetAllCastbarCooldowns(); +} + +void Lua_Client::ResetCastbarCooldownBySpellID(uint32 spell_id) { + Lua_Safe_Call_Void(); + self->ResetCastbarCooldownBySpellID(spell_id); +} + luabind::scope lua_register_client() { return luabind::class_("Client") .def(luabind::constructor<>()) @@ -2598,6 +2613,9 @@ luabind::scope lua_register_client() { .def("RemoveLDoNWin", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveLDoNWin) .def("ResetAA", (void(Lua_Client::*)(void))&Lua_Client::ResetAA) .def("ResetAllDisciplineTimers", (void(Lua_Client::*)(void))&Lua_Client::ResetAllDisciplineTimers) + .def("ResetAllCastbarCooldowns", (void(Lua_Client::*)(void))&Lua_Client::ResetAllCastbarCooldowns) + .def("ResetCastbarCooldownBySlot", (void(Lua_Client::*)(int))&Lua_Client::ResetCastbarCooldownBySlot) + .def("ResetCastbarCooldownBySpellID", (void(Lua_Client::*)(uint32))&Lua_Client::ResetCastbarCooldownBySpellID) .def("ResetDisciplineTimer", (void(Lua_Client::*)(uint32))&Lua_Client::ResetDisciplineTimer) .def("ResetTrade", (void(Lua_Client::*)(void))&Lua_Client::ResetTrade) .def("Save", (void(Lua_Client::*)(int))&Lua_Client::Save) diff --git a/zone/lua_client.h b/zone/lua_client.h index 7e91ea9f1..ee6881160 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -241,6 +241,9 @@ public: void ResetTrade(); uint32 GetDisciplineTimer(uint32 timer_id); void ResetDisciplineTimer(uint32 timer_id); + void ResetCastbarCooldownBySlot(int slot); + void ResetAllCastbarCooldowns(); + void ResetCastbarCooldownBySpellID(uint32 spell_id); void ResetAllDisciplineTimers(); bool UseDiscipline(int spell_id, int target_id); bool HasDisciplineLearned(uint16 spell_id); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index bb5ee2310..d51c79fdd 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -5935,6 +5935,47 @@ XS(XS_Client_LearnDisciplines) { XSRETURN(1); } +XS(XS_Client_ResetCastbarCooldownBySlot); +XS(XS_Client_ResetCastbarCooldownBySlot) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::ResetCastbarCooldownBySlot(THIS, int slot)"); + { + Client* THIS; + int slot = (int) SvIV(ST(1)); + VALIDATE_THIS_IS_CLIENT; + THIS->ResetCastbarCooldownBySlot(slot); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_ResetAllCastbarCooldowns); +XS(XS_Client_ResetAllCastbarCooldowns) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::ResetAllCastbarCooldowns(THIS)"); + { + Client* THIS; + VALIDATE_THIS_IS_CLIENT; + THIS->ResetAllCastbarCooldowns(); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_ResetCastbarCooldownBySpellID); +XS(XS_Client_ResetCastbarCooldownBySpellID) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::ResetCastbarCooldownBySpellID(THIS, uint32 spell_id)"); + { + Client* THIS; + uint32 spell_id = (uint32) SvUV(ST(1)); + VALIDATE_THIS_IS_CLIENT; + THIS->ResetCastbarCooldownBySpellID(spell_id); + } + XSRETURN_EMPTY; +} + #ifdef __cplusplus extern "C" #endif @@ -6169,6 +6210,9 @@ XS(boot_Client) { newXSproto(strcpy(buf, "RemoveNoRent"), XS_Client_RemoveNoRent, file, "$"); newXSproto(strcpy(buf, "ResetAA"), XS_Client_ResetAA, file, "$"); newXSproto(strcpy(buf, "ResetAllDisciplineTimers"), XS_Client_ResetAllDisciplineTimers, file, "$"); + newXSproto(strcpy(buf, "ResetAllCastbarCooldowns"), XS_Client_ResetAllCastbarCooldowns, file, "$"); + newXSproto(strcpy(buf, "ResetCastbarCooldownBySlot"), XS_Client_ResetCastbarCooldownBySlot, file, "$$"); + newXSproto(strcpy(buf, "ResetCastbarCooldownBySpellID"), XS_Client_ResetCastbarCooldownBySpellID, file, "$$"); newXSproto(strcpy(buf, "ResetDisciplineTimer"), XS_Client_ResetDisciplineTimer, file, "$$"); newXSproto(strcpy(buf, "ResetTrade"), XS_Client_ResetTrade, file, "$"); newXSproto(strcpy(buf, "Save"), XS_Client_Save, file, "$$"); diff --git a/zone/spells.cpp b/zone/spells.cpp index e14d100ce..ea0f15c22 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -6449,3 +6449,67 @@ int Client::GetNextAvailableDisciplineSlot(int starting_slot) { return -1; // Return -1 if No Slots open } + +void Client::ResetCastbarCooldownBySlot(int slot) { + if (slot < 0) { + for (unsigned int i = 0; i < EQ::spells::SPELL_GEM_COUNT; ++i) { + if(IsValidSpell(m_pp.mem_spells[i])) { + m_pp.spellSlotRefresh[i] = 1; + GetPTimers().Clear(&database, (pTimerSpellStart + m_pp.mem_spells[i])); + if (!IsLinkedSpellReuseTimerReady(spells[m_pp.mem_spells[i]].timer_id)) { + GetPTimers().Clear(&database, (pTimerLinkedSpellReuseStart + spells[m_pp.mem_spells[i]].timer_id)); + } + if (spells[m_pp.mem_spells[i]].timer_id > 0 && spells[m_pp.mem_spells[i]].timer_id < MAX_DISCIPLINE_TIMERS) { + SetLinkedSpellReuseTimer(spells[m_pp.mem_spells[i]].timer_id, 0); + } + SendSpellBarEnable(m_pp.mem_spells[i]); + } + } + } else if (slot < EQ::spells::SPELL_GEM_COUNT) { + if(IsValidSpell(m_pp.mem_spells[slot])) { + m_pp.spellSlotRefresh[slot] = 1; + GetPTimers().Clear(&database, (pTimerSpellStart + m_pp.mem_spells[slot])); + if (!IsLinkedSpellReuseTimerReady(spells[m_pp.mem_spells[slot]].timer_id)) { + GetPTimers().Clear(&database, (pTimerLinkedSpellReuseStart + spells[m_pp.mem_spells[slot]].timer_id)); + + } + if (spells[m_pp.mem_spells[slot]].timer_id > 0 && spells[m_pp.mem_spells[slot]].timer_id < MAX_DISCIPLINE_TIMERS) { + SetLinkedSpellReuseTimer(spells[m_pp.mem_spells[slot]].timer_id, 0); + } + SendSpellBarEnable(m_pp.mem_spells[slot]); + } + } +} + +void Client::ResetAllCastbarCooldowns() { + for (unsigned int i = 0; i < EQ::spells::SPELL_GEM_COUNT; ++i) { + if(IsValidSpell(m_pp.mem_spells[i])) { + m_pp.spellSlotRefresh[i] = 1; + GetPTimers().Clear(&database, (pTimerSpellStart + m_pp.mem_spells[i])); + if (!IsLinkedSpellReuseTimerReady(spells[m_pp.mem_spells[i]].timer_id)) { + GetPTimers().Clear(&database, (pTimerLinkedSpellReuseStart + spells[m_pp.mem_spells[i]].timer_id)); + } + if (spells[m_pp.mem_spells[i]].timer_id > 0 && spells[m_pp.mem_spells[i]].timer_id < MAX_DISCIPLINE_TIMERS) { + SetLinkedSpellReuseTimer(spells[m_pp.mem_spells[i]].timer_id, 0); + } + SendSpellBarEnable(m_pp.mem_spells[i]); + } + } +} + +void Client::ResetCastbarCooldownBySpellID(uint32 spell_id) { + for (unsigned int i = 0; i < EQ::spells::SPELL_GEM_COUNT; ++i) { + if(IsValidSpell(m_pp.mem_spells[i]) && m_pp.mem_spells[i] == spell_id) { + m_pp.spellSlotRefresh[i] = 1; + GetPTimers().Clear(&database, (pTimerSpellStart + m_pp.mem_spells[i])); + if (!IsLinkedSpellReuseTimerReady(spells[m_pp.mem_spells[i]].timer_id)) { + GetPTimers().Clear(&database, (pTimerLinkedSpellReuseStart + spells[m_pp.mem_spells[i]].timer_id)); + } + if (spells[m_pp.mem_spells[i]].timer_id > 0 && spells[m_pp.mem_spells[i]].timer_id < MAX_DISCIPLINE_TIMERS) { + SetLinkedSpellReuseTimer(spells[m_pp.mem_spells[i]].timer_id, 0); + } + SendSpellBarEnable(m_pp.mem_spells[i]); + break; + } + } +} From eb2b4fd9e0a75ff7fa13894cc19f7b73f1aca8f7 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 10 Dec 2021 12:20:25 -0500 Subject: [PATCH 508/624] [Spells] Update to SPA 58 SE_Levitate to support limit value (#1875) * [Spells] Update to SPA 58 SE_Levitate to support limit value * [Spells] Update to SPA 58 SE_Levitate to support limit value apply same on zone in --- zone/client_packet.cpp | 7 ++++++- zone/spell_effects.cpp | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 197d08364..2e4170c76 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -743,7 +743,12 @@ void Client::CompleteConnect() } } else { - SendAppearancePacket(AT_Levitate, 2); + if (spell.limit_value[x1] == 1) { + SendAppearancePacket(AT_Levitate, EQ::constants::GravityBehavior::LevitateWhileRunning, true, true); + } + else { + SendAppearancePacket(AT_Levitate, EQ::constants::GravityBehavior::Levitating, true, true); + } } break; } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index a72d9064d..401d9de73 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1369,7 +1369,12 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #endif //this sends the levitate packet to everybody else //who does not otherwise receive the buff packet. - SendAppearancePacket(AT_Levitate, 2, true, true); + if (spells[spell_id].limit_value[i] == 1) { + SendAppearancePacket(AT_Levitate, EQ::constants::GravityBehavior::LevitateWhileRunning, true, true); + } + else { + SendAppearancePacket(AT_Levitate, EQ::constants::GravityBehavior::Levitating, true, true); + } break; } From 550485ba338e23ba2e4af44e924f2336f3f26c04 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 10 Dec 2021 12:21:19 -0500 Subject: [PATCH 509/624] [Spells] Fixed issue with permanent Illusions not being consistent when zoning. (#1876) * start of work * updates * [Spells] Fixed issue with permanent Illusions not being consistent when zoning. --- common/spdat.h | 4 + zone/client_packet.cpp | 51 +--------- zone/mob.h | 1 + zone/spell_effects.cpp | 225 +++++++++++++++++++++-------------------- zone/spells.cpp | 2 +- 5 files changed, 126 insertions(+), 157 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 575c8293d..4016821b4 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -163,6 +163,10 @@ #define SPELL_PACT_OF_HATE_RECOURSE 40375 #define SPELL_INCENDIARY_OOZE_BUFF 32513 #define SPELL_EYE_OF_ZOMM 323 +#define SPELL_MINOR_ILLUSION 287 +#define SPELL_ILLUSION_TREE 601 +#define SPELL_ILLUSION_FEMALE 1731 +#define SPELL_ILLUSION_MALE 1732 //spellgroup ids #define SPELLGROUP_FRENZIED_BURNOUT 2754 diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 2e4170c76..526be7058 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -652,55 +652,10 @@ void Client::CompleteConnect() for (int x1 = 0; x1 < EFFECT_COUNT; x1++) { switch (spell.effect_id[x1]) { - case SE_IllusionCopy: case SE_Illusion: { - if (spell.base_value[x1] == -1) { - if (gender == 1) - gender = 0; - else if (gender == 0) - gender = 1; - SendIllusionPacket(GetRace(), gender, 0xFF, 0xFF); - } - else if (spell.base_value[x1] == -2) // WTF IS THIS - { - if (GetRace() == 128 || GetRace() == 130 || GetRace() <= 12) - SendIllusionPacket(GetRace(), GetGender(), spell.limit_value[x1], spell.max_value[x1]); - } - else if (spell.max_value[x1] > 0) - { - SendIllusionPacket(spell.base_value[x1], 0xFF, spell.limit_value[x1], spell.max_value[x1]); - } - else - { - SendIllusionPacket(spell.base_value[x1], 0xFF, 0xFF, 0xFF); - } - switch (spell.base_value[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; + if (buffs[j1].persistant_buff) { + Mob *caster = entity_list.GetMobID(buffs[j1].casterid); + ApplySpellEffectIllusion(spell.id, caster, j1, spell.base_value[x1], spell.limit_value[x1], spell.max_value[x1]); } break; } diff --git a/zone/mob.h b/zone/mob.h index 9d94fe311..48c46db37 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -356,6 +356,7 @@ public: void BeamDirectional(uint16 spell_id, int16 resist_adjust); void ConeDirectional(uint16 spell_id, int16 resist_adjust); void TryOnSpellFinished(Mob *caster, Mob *target, uint16 spell_id); + void ApplySpellEffectIllusion(int32 spell_id, Mob* caster, int buffslot, int base, int limit, int max); //Buff void BuffProcess(); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 401d9de73..90f92a381 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1397,114 +1397,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Illusion: race %d", effect_value); #endif - // Gender Illusions - if(spell.base_value[i] == -1) { - // Specific Gender Illusions - if(spell_id == 1732 || spell_id == 1731) { - int specific_gender = -1; - // Male - if(spell_id == 1732) - specific_gender = 0; - // Female - else if (spell_id == 1731) - specific_gender = 1; - if(specific_gender > -1) { - if(caster && caster->GetTarget()) { - SendIllusionPacket - ( - caster->GetTarget()->GetBaseRace(), - specific_gender, - caster->GetTarget()->GetTexture() - ); - } - } - } - // Change Gender Illusions - else { - if(caster && caster->GetTarget()) { - int opposite_gender = 0; - if(caster->GetTarget()->GetGender() == 0) - opposite_gender = 1; - - SendIllusionPacket - ( - caster->GetTarget()->GetRace(), - opposite_gender, - caster->GetTarget()->GetTexture() - ); - } - } - } - // Racial Illusions - else { - auto gender_id = ( - spell.max_value[i] > 0 && - ( - spell.max_value[i] != 3 || - spell.limit_value[i] == 0 - ) ? - (spell.max_value[i] - 1) : - Mob::GetDefaultGender(spell.base_value[i], GetGender()) - ); - - auto race_size = GetRaceGenderDefaultHeight( - spell.base_value[i], - gender_id - ); - - if (spell.base_value[i] != RACE_ELEMENTAL_75) { - if (spell.max_value[i] > 0) { - if (spell.limit_value[i] == 0) { - SendIllusionPacket( - spell.base_value[i], - gender_id - ); - } else { - if (spell.max_value[i] != 3) { - SendIllusionPacket( - spell.base_value[i], - gender_id, - spell.limit_value[i], - spell.max_value[i] - ); - } else { - SendIllusionPacket( - spell.base_value[i], - gender_id, - spell.limit_value[i], - spell.limit_value[i] - ); - } - } - } else { - SendIllusionPacket( - spell.base_value[i], - gender_id, - spell.limit_value[i], - spell.max_value[i] - ); - } - - } else { - SendIllusionPacket( - spell.base_value[i], - gender_id, - spell.limit_value[i] - ); - } - SendAppearancePacket(AT_Size, race_size); - } - - for (int x = EQ::textures::textureBegin; x <= EQ::textures::LastTintableTexture; x++) { - SendWearChange(x); - } - - if (caster == this && spell.id != 287 && spell.id != 601 && - (spellbonuses.IllusionPersistence || aabonuses.IllusionPersistence || - itembonuses.IllusionPersistence)) - buffs[buffslot].persistant_buff = 1; - else - buffs[buffslot].persistant_buff = 0; + ApplySpellEffectIllusion(spell_id, caster, buffslot, spells[spell_id].base_value[i], spells[spell_id].limit_value[i], spells[spell_id].max_value[i]); break; } @@ -8922,3 +8815,119 @@ void Mob::SetProcLimitTimer(int32 base_spell_id, uint32 proc_reuse_time, int pro } } +void Mob::ApplySpellEffectIllusion(int32 spell_id, Mob *caster, int buffslot, int base, int limit, int max) +{ + // Gender Illusions + if (base == -1) { + // Specific Gender Illusions + if (spell_id == SPELL_ILLUSION_MALE || spell_id == SPELL_ILLUSION_FEMALE) { + int specific_gender = -1; + // Male + if (spell_id == SPELL_ILLUSION_MALE) + specific_gender = 0; + // Female + else if (spell_id == SPELL_ILLUSION_FEMALE) + specific_gender = 1; + if (specific_gender > -1) { + if (caster && caster->GetTarget()) { + SendIllusionPacket + ( + caster->GetTarget()->GetBaseRace(), + specific_gender, + caster->GetTarget()->GetTexture() + ); + } + } + } + // Change Gender Illusions + else { + if (caster && caster->GetTarget()) { + int opposite_gender = 0; + if (caster->GetTarget()->GetGender() == 0) + opposite_gender = 1; + + SendIllusionPacket + ( + caster->GetTarget()->GetRace(), + opposite_gender, + caster->GetTarget()->GetTexture() + ); + } + } + } + // Racial Illusions + else { + auto gender_id = ( + max > 0 && + ( + max != 3 || + limit == 0 + ) ? + (max - 1) : + Mob::GetDefaultGender(base, GetGender()) + ); + + auto race_size = GetRaceGenderDefaultHeight( + base, + gender_id + ); + + if (base != RACE_ELEMENTAL_75) { + if (max > 0) { + if (limit == 0) { + SendIllusionPacket( + base, + gender_id + ); + } + else { + if (max != 3) { + SendIllusionPacket( + base, + gender_id, + limit, + max + ); + } + else { + SendIllusionPacket( + base, + gender_id, + limit, + limit + ); + } + } + } + else { + SendIllusionPacket( + base, + gender_id, + limit, + max + ); + } + + } + else { + SendIllusionPacket( + base, + gender_id, + limit + ); + } + SendAppearancePacket(AT_Size, race_size); + } + + for (int x = EQ::textures::textureBegin; x <= EQ::textures::LastTintableTexture; x++) { + SendWearChange(x); + } + + if (caster == this && spell_id != SPELL_MINOR_ILLUSION && spell_id != SPELL_ILLUSION_TREE && + (spellbonuses.IllusionPersistence || aabonuses.IllusionPersistence || itembonuses.IllusionPersistence)) { + buffs[buffslot].persistant_buff = 1; + } + else { + buffs[buffslot].persistant_buff = 0; + } +} diff --git a/zone/spells.cpp b/zone/spells.cpp index ea0f15c22..a23aec6a1 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2930,7 +2930,7 @@ int Mob::CalcBuffDuration(Mob *caster, Mob *target, uint16 spell_id, int32 caste int res = CalcBuffDuration_formula(castlevel, formula, duration); if (caster == target && (target->aabonuses.IllusionPersistence || target->spellbonuses.IllusionPersistence || target->itembonuses.IllusionPersistence) && - spell_id != 287 && spell_id != 601 && IsEffectInSpell(spell_id, SE_Illusion)) + spell_id != SPELL_MINOR_ILLUSION && spell_id != SPELL_ILLUSION_TREE && IsEffectInSpell(spell_id, SE_Illusion)) res = 10000; // ~16h override From 8de410ebb7f99fb23adfd9694e9120be9f38c93a Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 10 Dec 2021 13:46:23 -0500 Subject: [PATCH 510/624] [Features] Appearance Effects will now be sent to clients upon zone in. GM commands. (#1874) * start * working * Update perl_mob.cpp * updates * Update perl_mob.cpp * illusion behavior * rework start * fix later * Update mob.cpp * rework * updates * Update mob.cpp * update * gm command updates * updates * Update CMakeLists.txt * [Features] Appearance Effects will now be sent to clients upon zone in. GM commands. remove debugs * [Features] Appearance Effects will now be sent to clients upon zone in. GM commands. perl fix * [Features] Appearance Effects will now be sent to clients upon zone in. GM commands. space fix * [Features] Appearance Effects will now be sent to clients upon zone in. GM commands. minor fix * Update CMakeLists.txt * [Features] Appearance Effects will now be sent to clients upon zone in. GM commands. cleaned up some inconsistency * [Features] Appearance Effects will now be sent to clients upon zone in. GM commands. --- common/spdat.h | 2 +- zone/CMakeLists.txt | 1 + zone/client_packet.cpp | 2 + zone/command.cpp | 1 + zone/command.h | 1 + zone/entity.cpp | 20 ++++++ zone/entity.h | 1 + zone/gm_commands/appearanceeffects.cpp | 44 +++++++++++++ zone/mob.cpp | 86 +++++++++++++++++++++++++- zone/mob.h | 9 ++- zone/perl_mob.cpp | 36 +++++------ 11 files changed, 177 insertions(+), 26 deletions(-) create mode 100644 zone/gm_commands/appearanceeffects.cpp diff --git a/common/spdat.h b/common/spdat.h index 4016821b4..1841ae624 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -188,7 +188,7 @@ #define MAX_SYMPATHETIC_PROCS 10 // Number of sympathetic procs a client can have (This is arbitrary) #define MAX_FOCUS_PROC_LIMIT_TIMERS 20 //Number of focus recast timers that can be going at same time (This is arbitrary) #define MAX_PROC_LIMIT_TIMERS 8 //Number of proc delay timers that can be going at same time, different proc types get their own timer array. (This is arbitrary) - +#define MAX_APPEARANCE_EFFECTS 20 //Up to 20 Appearance Effects can be saved to a mobs appearance effect array, these will be sent to other clients when they enter a zone (This is arbitrary) const int Z_AGGRO=10; diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 12034cbe9..1e1f548ba 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -294,6 +294,7 @@ SET(gm_commands gm_commands/aggrozone.cpp gm_commands/ai.cpp gm_commands/appearance.cpp + gm_commands/appearanceeffects.cpp gm_commands/attack.cpp gm_commands/augmentitem.cpp gm_commands/ban.cpp diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 526be7058..59b2b76e1 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -745,6 +745,8 @@ void Client::CompleteConnect() entity_list.SendUntargetable(this); + entity_list.SendAppearanceEffects(this); + int x; for (x = EQ::textures::textureBegin; x <= EQ::textures::LastTexture; x++) { SendWearChange(x); diff --git a/zone/command.cpp b/zone/command.cpp index fb6b32c75..166f2cc81 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -120,6 +120,7 @@ int command_init(void) command_add("aggrozone", "[aggro] - Aggro every mob in the zone with X aggro. Default is 0. Not recommend if you're not invulnerable.", AccountStatus::GMAdmin, command_aggrozone) || command_add("ai", "[factionid/spellslist/con/guard/roambox/stop/start] - Modify AI on NPC target", AccountStatus::GMAdmin, command_ai) || command_add("appearance", "[type] [value] - Send an appearance packet for you or your target", AccountStatus::GMLeadAdmin, command_appearance) || + command_add("appearanceeffects", "- [view] [set] [remove] appearance effects.", AccountStatus::GMAdmin, command_appearanceeffects) || command_add("apply_shared_memory", "[shared_memory_name] - Tells every zone and world to apply a specific shared memory segment by name.", AccountStatus::GMImpossible, command_apply_shared_memory) || command_add("attack", "[targetname] - Make your NPC target attack targetname", AccountStatus::GMLeadAdmin, command_attack) || command_add("augmentitem", "Force augments an item. Must have the augment item window open.", AccountStatus::GMImpossible, command_augmentitem) || diff --git a/zone/command.h b/zone/command.h index a4c758a16..177b53cb4 100644 --- a/zone/command.h +++ b/zone/command.h @@ -34,6 +34,7 @@ void command_aggro(Client *c, const Seperator *sep); void command_aggrozone(Client *c, const Seperator *sep); void command_ai(Client *c, const Seperator *sep); void command_appearance(Client *c, const Seperator *sep); +void command_appearanceeffects(Client *c, const Seperator *sep); void command_apply_shared_memory(Client *c, const Seperator *sep); void command_attack(Client *c, const Seperator *sep); void command_augmentitem(Client *c, const Seperator *sep); diff --git a/zone/entity.cpp b/zone/entity.cpp index aad8a3e82..d5b22c2fe 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -4582,6 +4582,26 @@ void EntityList::SendUntargetable(Client *c) } } +void EntityList::SendAppearanceEffects(Client *c) +{ + if (!c) + return; + + auto it = mob_list.begin(); + while (it != mob_list.end()) { + Mob *cur = it->second; + + if (cur) { + if (cur == c) { + ++it; + continue; + } + cur->SendSavedAppearenceEffects(c); + } + ++it; + } +} + void EntityList::ZoneWho(Client *c, Who_All_Struct *Who) { // This is only called for SoF clients, as regular /who is now handled server-side for that client. diff --git a/zone/entity.h b/zone/entity.h index c264237c9..9c437ae09 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -380,6 +380,7 @@ public: void SendZoneAppearance(Client *c); void SendNimbusEffects(Client *c); void SendUntargetable(Client *c); + void SendAppearanceEffects(Client *c); void DuelMessage(Mob* winner, Mob* loser, bool flee); void QuestJournalledSayClose(Mob *sender, float dist, const char* mobname, const char* message, Journal::Options &opts); void GroupMessage(uint32 gid, const char *from, const char *message); diff --git a/zone/gm_commands/appearanceeffects.cpp b/zone/gm_commands/appearanceeffects.cpp new file mode 100644 index 000000000..8a9a5eb44 --- /dev/null +++ b/zone/gm_commands/appearanceeffects.cpp @@ -0,0 +1,44 @@ +#include "../client.h" + +void command_appearanceeffects(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { + c->Message(Chat::White, "Syntax: #appearanceeffects [subcommand]."); + c->Message(Chat::White, "[view] Display all appearance effects saved to your target. #appearanceffects view"); + c->Message(Chat::White, "[set] Set an appearance effects saved to your target. #appearanceffects set [app_effectid] [slotid]"); + c->Message(Chat::White, "[remove] Remove all appearance effects saved to your target. #appearanceffects remove"); + } + + if (!strcasecmp(sep->arg[1], "view")) { + Mob* m_target = c->GetTarget(); + if (m_target) { + m_target->GetAppearenceEffects(); + } + return; + } + + + if (!strcasecmp(sep->arg[1], "set")) { + int32 app_effectid = atof(sep->arg[2]); + int32 slot = atoi(sep->arg[3]); + + Mob* m_target = c->GetTarget(); + if (m_target) { + m_target->SendAppearanceEffect(app_effectid, 0, 0, 0, 0, nullptr, slot, 0, 0, 0, 0, 0, 0, 0, 0, 0); + c->Message(Chat::White, "Appearance Effect ID %i for slot %i has been set.", app_effectid, slot); + } + } + + if (!strcasecmp(sep->arg[1], "remove")) { + Mob* m_target = c->GetTarget(); + if (m_target) { + m_target->SendIllusionPacket(m_target->GetRace(), m_target->GetGender(), m_target->GetTexture(), m_target->GetHelmTexture(), + m_target->GetHairColor(), m_target->GetBeardColor(), m_target->GetEyeColor1(), m_target->GetEyeColor2(), + m_target->GetHairStyle(), m_target->GetLuclinFace(), m_target->GetBeard(), 0xFF, + m_target->GetDrakkinHeritage(), m_target->GetDrakkinTattoo(), m_target->GetDrakkinDetails(), m_target->GetSize(), false); + m_target->ClearAppearenceEffects(); + c->Message(Chat::White, "All Appearance Effects have been removed."); + } + return; + } +} diff --git a/zone/mob.cpp b/zone/mob.cpp index dc59d444c..2274b34a4 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -480,6 +480,11 @@ Mob::Mob( Vulnerability_Mod[i] = 0; } + for (int i = 0; i < MAX_APPEARANCE_EFFECTS; i++) { + appearance_effects_id[i] = 0; + appearance_effects_slot[i] = 0; + } + emoteid = 0; endur_upkeep = false; degenerating_effects = false; @@ -2385,7 +2390,8 @@ void Mob::SendIllusionPacket( uint32 in_drakkin_heritage, uint32 in_drakkin_tattoo, uint32 in_drakkin_details, - float in_size + float in_size, + bool send_appearance_effects ) { uint8 new_texture = in_texture; @@ -2494,6 +2500,10 @@ void Mob::SendIllusionPacket( /* Refresh armor and tints after send illusion packet */ SendArmorAppearance(); + if (send_appearance_effects) { + SendSavedAppearenceEffects(nullptr); + } + LogSpells( "Illusion: Race [{}] Gender [{}] Texture [{}] HelmTexture [{}] HairColor [{}] BeardColor [{}] EyeColor1 [{}] EyeColor2 [{}] HairStyle [{}] Face [{}] DrakkinHeritage [{}] DrakkinTattoo [{}] DrakkinDetails [{}] Size [{}]", race, @@ -2869,7 +2879,7 @@ void Mob::SendAppearanceEffect(uint32 parm1, uint32 parm2, uint32 parm3, uint32 6 = right foot 9 = Face - value#ground = 1, will place the effect on ground, of corresponding slot is set to 0 effect is permanenant, if > 0 will fade if mob death/despawn. + value#ground = 1, will place the effect on ground, this is permanenant */ //higher values can crash client @@ -2892,6 +2902,22 @@ void Mob::SendAppearanceEffect(uint32 parm1, uint32 parm2, uint32 parm3, uint32 value5slot = 1; } + if (!value1ground && parm1) { + SetAppearenceEffects(value1slot, parm1); + } + if (!value2ground && parm2) { + SetAppearenceEffects(value2slot, parm2); + } + if (!value3ground && parm3) { + SetAppearenceEffects(value3slot, parm3); + } + if (!value4ground && parm4) { + SetAppearenceEffects(value4slot, parm4); + } + if (!value5ground && parm5) { + SetAppearenceEffects(value5slot, parm5); + } + LevelAppearance_Struct* la = (LevelAppearance_Struct*)outapp->pBuffer; la->spawn_id = GetID(); la->parm1 = parm1; @@ -2920,6 +2946,62 @@ void Mob::SendAppearanceEffect(uint32 parm1, uint32 parm2, uint32 parm3, uint32 safe_delete(outapp); } +void Mob::SetAppearenceEffects(int32 slot, int32 value) +{ + for (int i = 0; i < MAX_APPEARANCE_EFFECTS; i++) { + if (!appearance_effects_id[i]) { + appearance_effects_id[i] = value; + appearance_effects_slot[i] = slot; + return; + } + } +} + +void Mob::GetAppearenceEffects() +{ + //used with GM command + if (!appearance_effects_id[0]) { + Message(Chat::Red, "No Appearance Effects exist on this mob"); + return; + } + + for (int i = 0; i < MAX_APPEARANCE_EFFECTS; i++) { + Message(Chat::Red, "ID: %i :: App Effect ID %i :: Slot %i", i, appearance_effects_id[i], appearance_effects_slot[i]); + } +} + +void Mob::ClearAppearenceEffects() +{ + for (int i = 0; i < MAX_APPEARANCE_EFFECTS; i++) { + appearance_effects_id[i] = 0; + appearance_effects_slot[i] = 0; + } +} + +void Mob::SendSavedAppearenceEffects(Client *receiver = nullptr) +{ + if (!appearance_effects_id[0]) { + return; + } + + if (appearance_effects_id[0]) { + SendAppearanceEffect(appearance_effects_id[0], appearance_effects_id[1], appearance_effects_id[2], appearance_effects_id[3], appearance_effects_id[4], receiver, + appearance_effects_slot[0], 0, appearance_effects_slot[1], 0, appearance_effects_slot[2], 0, appearance_effects_slot[3], 0, appearance_effects_slot[4], 0); + } + if (appearance_effects_id[5]) { + SendAppearanceEffect(appearance_effects_id[5], appearance_effects_id[6], appearance_effects_id[7], appearance_effects_id[8], appearance_effects_id[9], receiver, + appearance_effects_slot[5], 0, appearance_effects_slot[6], 0, appearance_effects_slot[7], 0, appearance_effects_slot[8], 0, appearance_effects_slot[9], 0); + } + if (appearance_effects_id[10]) { + SendAppearanceEffect(appearance_effects_id[10], appearance_effects_id[11], appearance_effects_id[12], appearance_effects_id[13], appearance_effects_id[14], receiver, + appearance_effects_slot[10], 0, appearance_effects_slot[11], 0, appearance_effects_slot[12], 0, appearance_effects_slot[13], 0, appearance_effects_slot[14], 0); + } + if (appearance_effects_id[15]) { + SendAppearanceEffect(appearance_effects_id[15], appearance_effects_id[16], appearance_effects_id[17], appearance_effects_id[18], appearance_effects_id[19], receiver, + appearance_effects_slot[15], 0, appearance_effects_slot[16], 0, appearance_effects_slot[17], 0, appearance_effects_slot[18], 0, appearance_effects_slot[19], 0); + } +} + void Mob::SendTargetable(bool on, Client *specific_target) { auto outapp = new EQApplicationPacket(OP_Untargetable, sizeof(Untargetable_Struct)); Untargetable_Struct *ut = (Untargetable_Struct*)outapp->pBuffer; diff --git a/zone/mob.h b/zone/mob.h index 48c46db37..c1fe57a89 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -425,6 +425,10 @@ public: inline bool HasEndurUpkeep() const { return endur_upkeep; } inline void SetEndurUpkeep(bool val) { endur_upkeep = val; } bool HasBuffWithSpellGroup(int spell_group); + void SetAppearenceEffects(int32 slot, int32 value); + void GetAppearenceEffects(); + void ClearAppearenceEffects(); + void SendSavedAppearenceEffects(Client *receiver); //Basic Stats/Inventory virtual void SetLevel(uint8 in_level, bool command = false) { level = in_level; } @@ -788,7 +792,7 @@ public: uint8 in_haircolor = 0xFF, uint8 in_beardcolor = 0xFF, uint8 in_eyecolor1 = 0xFF, uint8 in_eyecolor2 = 0xFF, uint8 in_hairstyle = 0xFF, uint8 in_luclinface = 0xFF, uint8 in_beard = 0xFF, uint8 in_aa_title = 0xFF, uint32 in_drakkin_heritage = 0xFFFFFFFF, uint32 in_drakkin_tattoo = 0xFFFFFFFF, - uint32 in_drakkin_details = 0xFFFFFFFF, float in_size = -1.0f); + uint32 in_drakkin_details = 0xFFFFFFFF, float in_size = -1.0f, bool send_appearance_effects = true); bool RandomizeFeatures(bool send_illusion = true, bool set_variables = true); virtual void Stun(int duration); virtual void UnStun(); @@ -1508,6 +1512,9 @@ protected: Timer def_proclimit_timer[MAX_PROC_LIMIT_TIMERS]; //SPA 512 int32 def_proclimit_spellid[MAX_PROC_LIMIT_TIMERS]; //SPA 512 + int32 appearance_effects_id[MAX_APPEARANCE_EFFECTS]; + int32 appearance_effects_slot[MAX_APPEARANCE_EFFECTS]; + Timer shield_timer; uint32 m_shield_target_id; uint32 m_shielder_id; diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index 1c9e399a2..28b8be62d 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -4809,31 +4809,22 @@ XS(XS_Mob_SendAppearanceEffectActor) { XS(XS_Mob_SendAppearanceEffectGround); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_SendAppearanceEffectGround) { dXSARGS; - if (items < 3 || items > 12) - Perl_croak(aTHX_ "Usage: Mob::SendAppearanceEffectGround(THIS, int32 effect1, uint32 slot1 = 1, [int32 effect2 = 0], [uint32 slot2 = 1], [int32 effect3 = 0], [uint32 slot3 = 1], [int32 effect4 = 0], [uint32 slot4 = 1], [int32 effect5 = 0], [uint32 slot5 = 1], [Client* single_client_to_send_to = null])"); // @categories Script Utility + if (items < 3 || items > 8) + Perl_croak(aTHX_ "Usage: Mob::SendAppearanceEffectGround(THIS, int32 effect1, [int32 effect2 = 0], [int32 effect3 = 0], [int32 effect4 = 0], [int32 effect5 = 0], [Client* single_client_to_send_to = null])"); // @categories Script Utility { Mob *THIS; int32 parm1 = (int32)SvIV(ST(1)); - uint32 value1slot = (uint32)SvIV(ST(2)); int32 parm2 = 0; - uint32 value2slot = 1; int32 parm3 = 0; - uint32 value3slot = 1; int32 parm4 = 0; - uint32 value4slot = 1; int32 parm5 = 0; - uint32 value5slot = 1; Client *client = nullptr; VALIDATE_THIS_IS_MOB; - if (items > 3) { parm2 = (int32)SvIV(ST(3)); } - if (items > 4) { value2slot = (uint32)SvIV(ST(4)); } - if (items > 5) { parm3 = (int32)SvIV(ST(5)); } - if (items > 6) { value3slot = (uint32)SvIV(ST(6)); } - if (items > 7) { parm4 = (int32)SvIV(ST(7)); } - if (items > 8) { value4slot = (uint32)SvIV(ST(8)); } - if (items > 9) { parm5 = (int32)SvIV(ST(9)); } - if (items > 10) { value5slot = (uint32)SvIV(ST(10)); } - if (items > 11) { + if (items > 3) { parm2 = (int32)SvIV(ST(2)); } + if (items > 4) { parm3 = (int32)SvIV(ST(3)); } + if (items > 5) { parm4 = (int32)SvIV(ST(4)); } + if (items > 6) { parm5 = (int32)SvIV(ST(5)); } + if (items > 7) { if (sv_derived_from(ST(6), "Client")) { IV tmp = SvIV((SV *)SvRV(ST(11))); client = INT2PTR(Client *, tmp); @@ -4844,8 +4835,8 @@ XS(XS_Mob_SendAppearanceEffectGround) { Perl_croak(aTHX_ "client is nullptr, avoiding crash."); } - THIS->SendAppearanceEffect(parm1, parm2, parm3, parm4, parm5, client, value1slot, 1, value2slot, 1, value3slot, 1, - value4slot, 1, value5slot, 1); + THIS->SendAppearanceEffect(parm1, parm2, parm3, parm4, parm5, client, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1); } XSRETURN_EMPTY; } @@ -4858,10 +4849,11 @@ XS(XS_Mob_RemoveAllAppearanceEffects) { { Mob *THIS; VALIDATE_THIS_IS_MOB; - THIS->SendIllusionPacket(THIS->GetRace(), THIS->GetGender(), THIS->GetTexture(), THIS->GetHelmTexture(), - THIS->GetHairColor(), THIS->GetBeardColor(), THIS->GetEyeColor1(), THIS->GetEyeColor2(), - THIS->GetHairStyle(), THIS->GetLuclinFace(), THIS->GetBeard(), 0xFF, - THIS->GetDrakkinHeritage(), THIS->GetDrakkinTattoo(), THIS->GetDrakkinDetails(), THIS->GetSize()); + THIS->SendIllusionPacket(THIS->GetRace(), THIS->GetGender(), THIS->GetTexture(), THIS->GetHelmTexture(), + THIS->GetHairColor(), THIS->GetBeardColor(), THIS->GetEyeColor1(), THIS->GetEyeColor2(), + THIS->GetHairStyle(), THIS->GetLuclinFace(), THIS->GetBeard(), 0xFF, + THIS->GetDrakkinHeritage(), THIS->GetDrakkinTattoo(), THIS->GetDrakkinDetails(), THIS->GetSize(), false); + THIS->ClearAppearenceEffects(); } XSRETURN_EMPTY; } From 91c958ae63f03b08a0829e4ba7596a593de0bb03 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 12 Dec 2021 13:22:43 -0500 Subject: [PATCH 511/624] Update spell_effects.cpp (#1877) updated --- zone/spell_effects.cpp | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 90f92a381..32cbe43fb 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1622,16 +1622,23 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Model Size: %d%%", effect_value); #endif - // Only allow 2 size changes from Base Size - float modifyAmount = (static_cast(effect_value) / 100.0f); - float maxModAmount = GetBaseSize() * modifyAmount * modifyAmount; - if ((GetSize() <= GetBaseSize() && GetSize() > maxModAmount) || - (GetSize() >= GetBaseSize() && GetSize() < maxModAmount) || - (GetSize() <= GetBaseSize() && maxModAmount > 1.0f) || - (GetSize() >= GetBaseSize() && maxModAmount < 1.0f)) - { - ChangeSize(GetSize() * modifyAmount); + if (effect_value && effect_value != 100) { + // Only allow 2 size changes from Base Size + float modifyAmount = (static_cast(effect_value) / 100.0f); + float maxModAmount = GetBaseSize() * modifyAmount * modifyAmount; + if ((GetSize() <= GetBaseSize() && GetSize() > maxModAmount) || + (GetSize() >= GetBaseSize() && GetSize() < maxModAmount) || + (GetSize() <= GetBaseSize() && maxModAmount > 1.0f) || + (GetSize() >= GetBaseSize() && maxModAmount < 1.0f)) + { + ChangeSize(GetSize() * modifyAmount); + } } + //Only applies to SPA 89, max value also likely does something, but unknown. + else if (effect == SE_ModelSize && spells[spell_id].limit_value[i]) { + ChangeSize(spells[spell_id].limit_value[i]); + } + break; } From 7cf66a2daa4a56932035a1cb87c306ae5373a0ef Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Mon, 13 Dec 2021 18:49:33 -0500 Subject: [PATCH 512/624] [Spells] Update SPA 238 SE_IllusionPersistence allow illusions to persist through deaths at higher AA ranks. (#1884) * start * working --- zone/bonuses.cpp | 10 +++++----- zone/common.h | 2 +- zone/mob.h | 1 + zone/spell_effects.cpp | 10 ++++++++++ zone/spells.cpp | 8 +++++--- 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index b71b7a568..29a88bf32 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1454,7 +1454,7 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) } case SE_IllusionPersistence: - newbon->IllusionPersistence = true; + newbon->IllusionPersistence = base_value; break; case SE_LimitToSkill: { @@ -3529,7 +3529,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne } case SE_IllusionPersistence: - new_bonus->IllusionPersistence = true; + new_bonus->IllusionPersistence = effect_value; break; case SE_LimitToSkill:{ @@ -5410,9 +5410,9 @@ void Mob::NegateSpellEffectBonuses(uint16 spell_id) break; case SE_IllusionPersistence: - if (negate_spellbonus) { spellbonuses.IllusionPersistence = false; } - if (negate_itembonus) { itembonuses.IllusionPersistence = false; } - if (negate_aabonus) { aabonuses.IllusionPersistence = false; } + if (negate_spellbonus) { spellbonuses.IllusionPersistence = effect_value; } + if (negate_itembonus) { itembonuses.IllusionPersistence = effect_value; } + if (negate_aabonus) { aabonuses.IllusionPersistence = effect_value; } break; case SE_Attack_Accuracy_Max_Percent: diff --git a/zone/common.h b/zone/common.h index c52dc74e1..08cd81119 100644 --- a/zone/common.h +++ b/zone/common.h @@ -617,7 +617,7 @@ struct StatBonuses { uint32 Assassinate[2]; // Assassinate AA (Massive dmg vs humaniod w/ assassinate) 0= ? 1= Dmg uint8 AssassinateLevel[2]; // Max Level Assassinate will be effective at. int32 PetMeleeMitigation; // Add AC to owner's pet. - bool IllusionPersistence; // Causes illusions not to fade. + int IllusionPersistence; // 1=Causes illusions not to fade when zoning 2=Allow to persist after death. uint16 extra_xtargets; // extra xtarget entries bool ShroudofStealth; // rogue improved invisiblity uint16 ReduceFallDamage; // reduce fall damage by percent diff --git a/zone/mob.h b/zone/mob.h index c1fe57a89..f82d0eb87 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -861,6 +861,7 @@ public: inline bool HasBaseEffectFocus() const { return (spellbonuses.FocusEffects[focusFcBaseEffects] || aabonuses.FocusEffects[focusFcBaseEffects] || itembonuses.FocusEffects[focusFcBaseEffects]); } int32 GetDualWieldingSameDelayWeapons() const { return dw_same_delay; } inline void SetDualWieldingSameDelayWeapons(int32 val) { dw_same_delay = val; } + bool HasPersistDeathIllusion(int32 spell_id); bool TryDoubleMeleeRoundEffect(); bool GetUseDoubleMeleeRoundDmgBonus() const { return use_double_melee_round_dmg_bonus; } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 32cbe43fb..0394570e0 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -8938,3 +8938,13 @@ void Mob::ApplySpellEffectIllusion(int32 spell_id, Mob *caster, int buffslot, in buffs[buffslot].persistant_buff = 0; } } + +bool Mob::HasPersistDeathIllusion(int32 spell_id) { + + if (spellbonuses.IllusionPersistence > 1 || aabonuses.IllusionPersistence > 1 || itembonuses.IllusionPersistence > 1) { + if (spell_id != SPELL_MINOR_ILLUSION && spell_id != SPELL_ILLUSION_TREE && IsEffectInSpell(spell_id, SE_Illusion) && IsBeneficialSpell(spell_id)) { + return true; + } + } + return false; +} diff --git a/zone/spells.cpp b/zone/spells.cpp index a23aec6a1..aa01fb55a 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2929,9 +2929,10 @@ int Mob::CalcBuffDuration(Mob *caster, Mob *target, uint16 spell_id, int32 caste int res = CalcBuffDuration_formula(castlevel, formula, duration); if (caster == target && (target->aabonuses.IllusionPersistence || target->spellbonuses.IllusionPersistence || - target->itembonuses.IllusionPersistence) && - spell_id != SPELL_MINOR_ILLUSION && spell_id != SPELL_ILLUSION_TREE && IsEffectInSpell(spell_id, SE_Illusion)) + target->itembonuses.IllusionPersistence) && + spell_id != SPELL_MINOR_ILLUSION && spell_id != SPELL_ILLUSION_TREE && IsEffectInSpell(spell_id, SE_Illusion)) { res = 10000; // ~16h override + } res = mod_buff_duration(res, caster, target, spell_id); @@ -4373,7 +4374,8 @@ void Mob::BuffFadeNonPersistDeath() auto current_spell_id = buffs[buff_slot].spellid; if ( IsValidSpell(current_spell_id) && - !IsPersistDeathSpell(current_spell_id) + !IsPersistDeathSpell(current_spell_id) && + !HasPersistDeathIllusion(current_spell_id) ) { BuffFadeBySlot(buff_slot, false); recalc_bonus = true; From 1c2e1ea228a04f18b1528fd6ac37b04650486b5d Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Mon, 13 Dec 2021 18:49:53 -0500 Subject: [PATCH 513/624] rampage updates (#1882) --- common/spdat.h | 2 +- zone/client_process.cpp | 2 +- zone/effects.cpp | 18 +++++++++++------- zone/entity.h | 3 ++- zone/spell_effects.cpp | 13 ++++++++++--- 5 files changed, 25 insertions(+), 13 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 1841ae624..ddf5d4f39 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -912,7 +912,7 @@ typedef enum { #define SE_IllusionOther 202 // implemented - Project Illusion #define SE_MassGroupBuff 203 // implemented #define SE_GroupFearImmunity 204 // implemented - (Does not use bonus) -#define SE_Rampage 205 // implemented +#define SE_Rampage 205 // implemented, @Combat Instant, Perform a primary slot combat rounds on all creatures within a 40 foot radius, base: number of attack rounds, limit: max entities hit per round, max: none, Note: AE range is 40 by default. Custom: Set field 'aoe_range' to override default. Adding additional attacks and hit count limit. #define SE_AETaunt 206 // implemented #define SE_FleshToBone 207 // implemented //#define SE_PurgePoison 208 // not used diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 0134ce3f4..deadb2cbb 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -405,7 +405,7 @@ bool Client::Process() { } if (CheckAATimer(aaTimerRampage)) { - entity_list.AEAttack(this, 30); + entity_list.AEAttack(this, 40); } } } diff --git a/zone/effects.cpp b/zone/effects.cpp index 32f836919..a074615e5 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -1264,7 +1264,8 @@ void EntityList::AEAttack( float distance, int Hand, int count, - bool is_from_spell) + bool is_from_spell, + int attack_rounds) { Mob *current_mob = nullptr; float distance_squared = distance * distance; @@ -1276,15 +1277,18 @@ void EntityList::AEAttack( if (current_mob->IsNPC() && current_mob != attacker //this is not needed unless NPCs can use this && (attacker->IsAttackAllowed(current_mob)) - && current_mob->GetRace() != 216 && current_mob->GetRace() != 472 /* dont attack horses */ + && !current_mob->IsHorse() /* dont attack mounts */ && (DistanceSquared(current_mob->GetPosition(), attacker->GetPosition()) <= distance_squared) ) { - if (!attacker->IsClient() || attacker->GetClass() == MONK || attacker->GetClass() == RANGER) { - attacker->Attack(current_mob, Hand, false, false, is_from_spell); - } - else { - attacker->CastToClient()->DoAttackRounds(current_mob, Hand, is_from_spell); + for (int i = 0; i < attack_rounds; i++) { + + if (!attacker->IsClient() || attacker->GetClass() == MONK || attacker->GetClass() == RANGER) { + attacker->Attack(current_mob, Hand, false, false, is_from_spell); + } + else { + attacker->CastToClient()->DoAttackRounds(current_mob, Hand, is_from_spell); + } } hit_count++; diff --git a/zone/entity.h b/zone/entity.h index 9c437ae09..0984932fe 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -407,7 +407,8 @@ public: float distance, int Hand = EQ::invslot::slotPrimary, int count = 0, - bool is_from_spell = false + bool is_from_spell = false, + int attack_rounds = 1 ); void AETaunt(Client *caster, float range = 0, int32 bonus_hate = 0); void AESpell( diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 0394570e0..efefec421 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2209,9 +2209,16 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Rampage"); #endif - if(caster) - entity_list.AEAttack(caster, 30, EQ::invslot::slotPrimary, 0, true); // on live wars dont get a duration ramp, its a one shot deal - + //defulat live range is 40, with 1 attack per round, no hit count limit + float rampage_range = 40; + if (spells[spell_id].aoe_range) { + rampage_range = spells[spell_id].aoe_range; //added for expanded functionality + } + int attack_count = spells[spell_id].base_value[i]; //added for expanded functionality + int hit_count = spells[spell_id].limit_value[i]; //added for expanded functionality + if (caster) { + entity_list.AEAttack(caster, rampage_range, EQ::invslot::slotPrimary, hit_count, true, attack_count); // on live wars dont get a duration ramp, its a one shot deal + } break; } From ef1f6adf185e920b077ea4261a33d788c170e58b Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Mon, 13 Dec 2021 20:32:25 -0500 Subject: [PATCH 514/624] effective casting level update (#1886) --- zone/spell_effects.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index efefec421..3e347436c 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1040,14 +1040,13 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove caster->MessageString(Chat::SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name); break; } - int buff_count = GetMaxTotalSlots(); for(int slot = 0; slot < buff_count; slot++) { if( buffs[slot].spellid != SPELL_UNKNOWN && spells[buffs[slot].spellid].dispel_flag == 0 && !IsDiscipline(buffs[slot].spellid)) { - if (caster && TryDispel(caster->GetLevel(),buffs[slot].casterlevel, effect_value)){ + if (caster && TryDispel(caster->GetCasterLevel(spell_id), buffs[slot].casterlevel, effect_value)){ BuffFadeBySlot(slot); slot = buff_count; } @@ -1296,11 +1295,21 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Blind: %+i", effect_value); #endif - // this should catch the cures - if (BeneficialSpell(spell_id) && spells[spell_id].buff_duration == 0) - BuffFadeByEffect(SE_Blind); - else if (!IsClient()) + // 'cure blind' + if (BeneficialSpell(spell_id) && spells[spell_id].buff_duration == 0) { + int buff_count = GetMaxBuffSlots(); + for (int slot = 0; slot < buff_count; slot++) { + if (buffs[slot].spellid != SPELL_UNKNOWN && IsEffectInSpell(buffs[slot].spellid, SE_Blind)) { + if (caster && TryDispel(caster->GetCasterLevel(spell_id), buffs[slot].casterlevel, 1)) { + BuffFadeBySlot(slot); + slot = buff_count; + } + } + } + } + else if (!IsClient()) { CalculateNewFearpoint(); + } break; } From 26b21673ade0571b32197096783634d4c9980c19 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Mon, 13 Dec 2021 20:33:22 -0500 Subject: [PATCH 515/624] [Spells] Implemented SPA 245 SE_TrapCircumvention (#1885) * implemented * [Spells] Implemented SPA 245 SE_TrapCircumvention --- common/spdat.h | 2 +- zone/bonuses.cpp | 11 ++++++++--- zone/client_packet.cpp | 5 ++++- zone/common.h | 1 + 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index ddf5d4f39..1e4cb49cd 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -952,7 +952,7 @@ typedef enum { #define SE_IncreaseChanceMemwipe 242 // implemented - @Memblur, increases the chance to wipe hate with memory blurr, base: chance pct, limit: none, max: none, Note: Mods final blur chance after other bonuses added. #define SE_CharmBreakChance 243 // implemented - Total Domination #define SE_RootBreakChance 244 // implemented[AA] reduce the chance that your root will break. -#define SE_TrapCircumvention 245 // *not implemented[AA] - decreases the chance that you will set off a trap when opening a chest +#define SE_TrapCircumvention 245 // implemented, @Traps, decreases the chance that you will set off a trap when opening a chest or other similar container by percentage, base: chance modifer, limit: none, max: none #define SE_SetBreathLevel 246 // *not implemented as bonus #define SE_RaiseSkillCap 247 // implemented[AA] - adds skill over the skill cap. #define SE_SecondaryForte 248 // not implemented as bonus(gives you a 2nd specialize skill that can go past 50 to 100) diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 29a88bf32..8aef8452f 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1773,6 +1773,10 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) break; } + case SE_TrapCircumvention: + newbon->TrapCircumvention += base_value; + break; + // to do case SE_PetDiscipline: break; @@ -1784,9 +1788,6 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) break; case SE_NimbleEvasion: break; - case SE_TrapCircumvention: - break; - // not handled here case SE_HastenedAASkill: @@ -3794,6 +3795,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne new_bonus->CompleteHealBuffBlocker = true; break; + case SE_TrapCircumvention: + new_bonus->TrapCircumvention += effect_value; + break; + //Special custom cases for loading effects on to NPC from 'npc_spels_effects' table if (IsAISpellEffect) { diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 59b2b76e1..99017d7ce 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -5464,8 +5464,11 @@ void Client::Handle_OP_DisarmTraps(const EQApplicationPacket *app) } else { + int fail_rate = 25; + int trap_circumvention = spellbonuses.TrapCircumvention + itembonuses.TrapCircumvention + aabonuses.TrapCircumvention; + fail_rate -= fail_rate * trap_circumvention / 100; MessageString(Chat::Skills, FAIL_DISARM_DETECTED_TRAP); - if (zone->random.Int(0, 99) < 25) { + if (zone->random.Int(0, 99) < fail_rate) { trap->Trigger(this); } } diff --git a/zone/common.h b/zone/common.h index 08cd81119..d3503c1b1 100644 --- a/zone/common.h +++ b/zone/common.h @@ -561,6 +561,7 @@ struct StatBonuses { bool CompleteHealBuffBlocker; // Use in SPA 101 to prevent recast of complete heal from this effect till blocker buff is removed. // AAs + int32 TrapCircumvention; // reduce chance to trigger a trap. uint16 SecondaryForte; // allow a second skill to be specialized with a cap of this value. int32 ShieldDuration; // extends duration of /shield ability int32 ExtendedShielding; // extends range of /shield ability From 6da7116c668e613a2d854a01772a058643ee894a Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 14 Dec 2021 11:26:59 -0500 Subject: [PATCH 516/624] [Bug Fix] Hero Forge armor graphics not displaying properly to other clients in zone. (#1883) * fix part1 * updates * Update inventory.cpp * fixed * Update inventory.cpp * update * [Bug Fix] Hero Forge armor graphics not displaying properly to other clients in zone. --- zone/client.cpp | 2 ++ zone/client.h | 2 ++ zone/client_packet.cpp | 25 +++++-------------------- zone/client_process.cpp | 19 +++++++++++++++++++ zone/inventory.cpp | 6 +++++- zone/mob.cpp | 12 ++++++++++++ zone/mob.h | 3 +++ zone/mob_appearance.cpp | 3 +++ 8 files changed, 51 insertions(+), 21 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 5a8bc9d88..fbb1ac106 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -270,6 +270,8 @@ Client::Client(EQStreamInterface* ieqs) if (RuleI(World, PVPMinLevel) > 0 && level >= RuleI(World, PVPMinLevel) && m_pp.pvp == 0) SetPVP(true, false); dynamiczone_removal_timer.Disable(); + heroforge_wearchange_timer.Disable(); + //for good measure: memset(&m_pp, 0, sizeof(m_pp)); memset(&m_epp, 0, sizeof(m_epp)); diff --git a/zone/client.h b/zone/client.h index a9266230d..51649d1b9 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1870,6 +1870,8 @@ private: Timer dynamiczone_removal_timer; Timer task_request_timer; + Timer heroforge_wearchange_timer; + glm::vec3 m_Proximity; glm::vec4 last_position_before_bulk_update; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 99017d7ce..e23bebe65 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -747,28 +747,11 @@ void Client::CompleteConnect() entity_list.SendAppearanceEffects(this); - int x; - for (x = EQ::textures::textureBegin; x <= EQ::textures::LastTexture; x++) { - SendWearChange(x); - } - // added due to wear change above - UpdateActiveLight(); - SendAppearancePacket(AT_Light, GetActiveLightType()); - - Mob *pet = GetPet(); - if (pet != nullptr) { - for (x = EQ::textures::textureBegin; x <= EQ::textures::LastTexture; x++) { - pet->SendWearChange(x); - } - // added due to wear change above - pet->UpdateActiveLight(); - pet->SendAppearancePacket(AT_Light, pet->GetActiveLightType()); - } - entity_list.SendTraders(this); - if (GetPet()) { - GetPet()->SendPetBuffsToClient(); + Mob *pet = GetPet(); + if (pet) { + pet->SendPetBuffsToClient(); } if (GetGroup()) @@ -918,6 +901,8 @@ void Client::CompleteConnect() worldserver.SendPacket(p); safe_delete(p); } + + heroforge_wearchange_timer.Start(250); } // connecting opcode handlers diff --git a/zone/client_process.cpp b/zone/client_process.cpp index deadb2cbb..b363292e8 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -199,6 +199,25 @@ bool Client::Process() { instalog = true; } + if (heroforge_wearchange_timer.Check()) { + /* + This addresses bug where on zone in heroforge models would not be sent to other clients when this was + in Client::CompleteConnect(). Sending after a small 250 ms delay after that function resolves the issue. + Unclear the underlying reason for this, if a better solution can be found then can move this back. + */ + if (queue_wearchange_slot >= 0) { //Resend slot from Client::SwapItem if heroforge item is swapped. + SendWearChange(static_cast(queue_wearchange_slot)); + } + else { //Send from Client::CompleteConnect() + SendWearChangeAndLighting(EQ::textures::LastTexture); + Mob *pet = GetPet(); + if (pet) { + pet->SendWearChangeAndLighting(EQ::textures::LastTexture); + } + } + heroforge_wearchange_timer.Disable(); + } + if (IsStunned() && stunned_timer.Check()) Mob::UnStun(); diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 62c3148ec..41e67e7b9 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -2224,9 +2224,13 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { } int matslot = SlotConvert2(dst_slot_id); - if (dst_slot_id <= EQ::invslot::EQUIPMENT_END && matslot != EQ::textures::armorHead) { // think this is to allow the client to update with /showhelm + if (dst_slot_id <= EQ::invslot::EQUIPMENT_END) {// on Titanium and ROF2 /showhelm works even if sending helm slot SendWearChange(matslot); } + // This is part of a bug fix to ensure heroforge graphics display to other clients in zone. + if (queue_wearchange_slot >= 0) { + heroforge_wearchange_timer.Start(100); + } // Step 7: Save change to the database if (src_slot_id == EQ::invslot::slotCursor) { diff --git a/zone/mob.cpp b/zone/mob.cpp index 2274b34a4..972d40afb 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -495,6 +495,8 @@ Mob::Mob( use_double_melee_round_dmg_bonus = false; dw_same_delay = 0; + queue_wearchange_slot = -1; + #ifdef BOTS m_manual_follow = false; #endif @@ -3168,6 +3170,16 @@ bool Mob::UpdateActiveLight() return (m_Light.Level[EQ::lightsource::LightActive] != old_light_level); } +void Mob::SendWearChangeAndLighting(int8 last_texture) { + + for (int i = EQ::textures::textureBegin; i <= last_texture; i++) { + SendWearChange(i); + } + UpdateActiveLight(); + SendAppearancePacket(AT_Light, GetActiveLightType()); + +} + void Mob::ChangeSize(float in_size = 0, bool bNoRestriction) { // Size Code if (!bNoRestriction) diff --git a/zone/mob.h b/zone/mob.h index f82d0eb87..1a7818358 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -911,6 +911,7 @@ public: virtual void UpdateEquipmentLight() { m_Light.Type[EQ::lightsource::LightEquipment] = 0; m_Light.Level[EQ::lightsource::LightEquipment] = 0; } inline void SetSpellLightType(uint8 light_type) { m_Light.Type[EQ::lightsource::LightSpell] = (light_type & 0x0F); m_Light.Level[EQ::lightsource::LightSpell] = EQ::lightsource::TypeToLevel(m_Light.Type[EQ::lightsource::LightSpell]); } + void SendWearChangeAndLighting(int8 last_texture); inline uint8 GetActiveLightType() { return m_Light.Type[EQ::lightsource::LightActive]; } bool UpdateActiveLight(); // returns true if change, false if no change @@ -1515,6 +1516,8 @@ protected: int32 appearance_effects_id[MAX_APPEARANCE_EFFECTS]; int32 appearance_effects_slot[MAX_APPEARANCE_EFFECTS]; + + int queue_wearchange_slot; Timer shield_timer; uint32 m_shield_target_id; diff --git a/zone/mob_appearance.cpp b/zone/mob_appearance.cpp index e0629b83a..112ad81c1 100644 --- a/zone/mob_appearance.cpp +++ b/zone/mob_appearance.cpp @@ -433,6 +433,9 @@ void Mob::SendWearChange(uint8 material_slot, Client *one_client) wear_change->wear_slot_id = material_slot; + // Part of a bug fix to ensure heroforge models send to other clients in zone. + queue_wearchange_slot = wear_change->hero_forge_model ? material_slot : -1; + if (!one_client) { entity_list.QueueClients(this, packet); } From 73acc3310cb7753b41e19f9a857a0569536b641b Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 14 Dec 2021 12:31:38 -0500 Subject: [PATCH 517/624] [Spells] Updates and fixes to targeted focus effects (#1870) --- zone/bonuses.cpp | 8 +++--- zone/client.h | 2 +- zone/common.h | 2 +- zone/effects.cpp | 6 ++--- zone/mob.cpp | 24 +++++++++++++++--- zone/mob.h | 7 +++--- zone/npc.h | 2 ++ zone/spell_effects.cpp | 55 ++++++++++++++++++++++++++---------------- zone/spells.cpp | 2 +- 9 files changed, 71 insertions(+), 37 deletions(-) diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 8aef8452f..bf4c5cfb9 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -674,7 +674,7 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) uint8 focus = IsFocusEffect(0, 0, true, effect); if (focus) { - newbon->FocusEffects[focus] = static_cast(effect); + newbon->FocusEffects[focus] = effect; continue; } @@ -1887,7 +1887,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne } } else { - new_bonus->FocusEffects[focus] = static_cast(spells[spell_id].effect_id[i]); + new_bonus->FocusEffects[focus] = spells[spell_id].effect_id[i]; } continue; } @@ -4113,9 +4113,9 @@ uint8 Mob::IsFocusEffect(uint16 spell_id,int effect_index, bool AA,uint32 aa_eff case SE_Fc_ResistIncoming: focusFcResistIncoming; case SE_Fc_Amplify_Mod: - focusFcResistIncoming; + focusFcAmplifyMod; case SE_Fc_Amplify_Amt: - focusFcResistIncoming; + focusFcAmplifyAmt; case SE_SpellHateMod: return focusSpellHateMod; case SE_ReduceReuseTimer: diff --git a/zone/client.h b/zone/client.h index 51649d1b9..cd58da895 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1640,7 +1640,7 @@ protected: void MakeBuffFadePacket(uint16 spell_id, int slot_id, bool send_message = true); bool client_data_loaded; - int32 GetFocusEffect(focusType type, uint16 spell_id); + int32 GetFocusEffect(focusType type, uint16 spell_id, Mob *caster = nullptr); uint16 GetSympatheticFocusEffect(focusType type, uint16 spell_id); void FinishAlternateAdvancementPurchase(AA::Rank *rank, bool ignore_cost); diff --git a/zone/common.h b/zone/common.h index d3503c1b1..cd7d072be 100644 --- a/zone/common.h +++ b/zone/common.h @@ -496,7 +496,7 @@ struct StatBonuses { int32 CharmBreakChance; // chance to break charm int32 SongRange; // increases range of beneficial bard songs uint32 HPToManaConvert; // Uses HP to cast spells at specific conversion - uint8 FocusEffects[HIGHEST_FOCUS+1]; // Stores the focus effectid for each focustype you have. + int32 FocusEffects[HIGHEST_FOCUS+1]; // Stores the focus effectid for each focustype you have. int16 FocusEffectsWorn[HIGHEST_FOCUS+1]; // Optional to allow focus effects to be applied additively from worn slot bool NegateEffects; // Check if you contain a buff with negate effect. (only spellbonuses) int32 SkillDamageAmount2[EQ::skills::HIGHEST_SKILL + 2]; // Adds skill specific damage diff --git a/zone/effects.cpp b/zone/effects.cpp index a074615e5..0712ec6c5 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -356,8 +356,8 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { if (spells[spell_id].buff_duration < 1) { if (target) { - value += int(base_value * target->GetFocusEffect(focusFcHealPctIncoming, spell_id)/100); //SPA 393 Add before critical - value += int(base_value * target->GetFocusEffect(focusFcHealPctCritIncoming, spell_id)/100); //SPA 395 Add before critical (?) + value += int(base_value * target->GetFocusEffect(focusFcHealPctIncoming, spell_id, this)/100); //SPA 393 Add before critical + value += int(base_value * target->GetFocusEffect(focusFcHealPctCritIncoming, spell_id, this)/100); //SPA 395 Add before critical (?) } value += GetFocusEffect(focusFcHealAmtCrit, spell_id); //SPA 396 Add before critical @@ -379,7 +379,7 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { value += GetFocusEffect(focusFcAmplifyAmt, spell_id); //SPA 508 ? Add after critical if (target) { - value += target->GetFocusEffect(focusFcHealAmtIncoming, spell_id); //SPA 394 Add after critical + value += target->GetFocusEffect(focusFcHealAmtIncoming, spell_id, this); //SPA 394 Add after critical } if (IsNPC() && CastToNPC()->GetHealScale()) { diff --git a/zone/mob.cpp b/zone/mob.cpp index 972d40afb..c2e09c121 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4477,7 +4477,7 @@ void Mob::TryOnSpellFinished(Mob *caster, Mob *target, uint16 spell_id) } } -int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining) +int32 Mob::GetVulnerability(Mob *caster, uint32 spell_id, uint32 ticsremaining) { /* Modifies incoming spell damage by percent, to increase or decrease damage, can be limited to specific resists. @@ -4503,8 +4503,8 @@ int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining) innate_mod = Vulnerability_Mod[HIGHEST_RESIST + 1]; } - fc_spell_vulnerability_mod = GetFocusEffect(focusSpellVulnerability, spell_id); - fc_spell_damage_pct_incomingPC_mod = GetFocusEffect(focusFcSpellDamagePctIncomingPC, spell_id); + fc_spell_vulnerability_mod = GetFocusEffect(focusSpellVulnerability, spell_id, caster); + fc_spell_damage_pct_incomingPC_mod = GetFocusEffect(focusFcSpellDamagePctIncomingPC, spell_id, caster); total_mod = fc_spell_vulnerability_mod + fc_spell_damage_pct_incomingPC_mod; @@ -4517,6 +4517,24 @@ int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining) return total_mod; } +bool Mob::IsTargetedFocusEffect(int focus_type) { + + switch (focus_type) { + case focusSpellVulnerability: + case focusFcSpellDamagePctIncomingPC: + case focusFcDamageAmtIncoming: + case focusFcSpellDamageAmtIncomingPC: + case focusFcCastSpellOnLand: + case focusFcHealAmtIncoming: + case focusFcHealPctCritIncoming: + case focusFcHealPctIncoming: + return true; + default: + return false; + + } +} + int32 Mob::GetSkillDmgTaken(const EQ::skills::SkillType skill_used, ExtraAttackOptions *opts) { int skilldmg_mod = 0; diff --git a/zone/mob.h b/zone/mob.h index 1a7818358..07c5c4382 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -786,7 +786,7 @@ public: void QuestJournalledSay(Client *QuestInitiator, const char *str, Journal::Options &opts); int32 GetItemStat(uint32 itemid, const char *identifier); - int32 CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus=false, uint16 casterid=0); + int32 CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus=false, uint16 casterid = 0, Mob *caster = nullptr); uint8 IsFocusEffect(uint16 spellid, int effect_index, bool AA=false,uint32 aa_effect=0); void SendIllusionPacket(uint16 in_race, uint8 in_gender = 0xFF, uint8 in_texture = 0xFF, uint8 in_helmtexture = 0xFF, uint8 in_haircolor = 0xFF, uint8 in_beardcolor = 0xFF, uint8 in_eyecolor1 = 0xFF, uint8 in_eyecolor2 = 0xFF, @@ -815,7 +815,7 @@ public: void TrySympatheticProc(Mob *target, uint32 spell_id); bool TryFadeEffect(int slot); uint16 GetSpellEffectResistChance(uint16 spell_id); - int32 GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining); + int32 GetVulnerability(Mob *caster, uint32 spell_id, uint32 ticsremaining); int32 GetFcDamageAmtIncoming(Mob *caster, int32 spell_id); int32 GetFocusIncoming(focusType type, int effect, Mob *caster, uint32 spell_id); //**** This can be removed when bot healing focus code is updated **** int32 GetSkillDmgTaken(const EQ::skills::SkillType skill_used, ExtraAttackOptions *opts = nullptr); @@ -861,6 +861,7 @@ public: inline bool HasBaseEffectFocus() const { return (spellbonuses.FocusEffects[focusFcBaseEffects] || aabonuses.FocusEffects[focusFcBaseEffects] || itembonuses.FocusEffects[focusFcBaseEffects]); } int32 GetDualWieldingSameDelayWeapons() const { return dw_same_delay; } inline void SetDualWieldingSameDelayWeapons(int32 val) { dw_same_delay = val; } + bool IsTargetedFocusEffect(int focus_type); bool HasPersistDeathIllusion(int32 spell_id); bool TryDoubleMeleeRoundEffect(); @@ -1461,7 +1462,7 @@ protected: virtual #endif int GetBaseSkillDamage(EQ::skills::SkillType skill, Mob *target = nullptr); - virtual int32 GetFocusEffect(focusType type, uint16 spell_id) { return 0; } + virtual int32 GetFocusEffect(focusType type, uint16 spell_id, Mob *caster = nullptr) { return 0; } void CalculateNewFearpoint(); float FindGroundZ(float new_x, float new_y, float z_offset=0.0); float FindDestGroundZ(glm::vec3 dest, float z_offset=0.0); diff --git a/zone/npc.h b/zone/npc.h index 387c512be..084a3df8a 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -282,6 +282,8 @@ public: content_db.GetFactionIdsForNPC(npc_faction_id, &faction_list, &primary_faction); } + int32 GetFocusEffect(focusType type, uint16 spell_id, Mob* caster = nullptr); + glm::vec4 m_SpawnPoint; uint32 GetMaxDMG() const {return max_dmg;} diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 3e347436c..9b7e74d82 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -5218,13 +5218,12 @@ int32 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) //given an item/spell's focus ID and the spell being cast, determine the focus ammount, if any //assumes that spell_id is not a bard spell and that both ids are valid spell ids -int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus, uint16 casterid) +int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus, uint16 casterid, Mob *caster) { /* 'this' is always the caster of the spell_id, most foci check for effects on the caster, however some check for effects on the target. 'casterid' is the casterid of the caster of spell_id, used when spell_id is cast on a target with a focus effect that is checked by incoming spell. */ - if (!IsValidSpell(focus_id) || !IsValidSpell(spell_id)) { return 0; } @@ -5253,6 +5252,7 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo is_from_item_click = true; } + bool LimitInclude[MaxLimitInclude] = {false}; /* Certain limits require only one of several Include conditions to be true. Determined by limits being negative or positive Ie. Add damage to fire OR ice spells. If positive we 'Include', by checking each limit of same type to look for match until found. Opposed to @@ -5523,23 +5523,25 @@ int32 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_Ff_Same_Caster://hmm do i need to pass casterid from buff slot here if (focus_spell.base_value[i] == 0) { - if (casterid == GetID()) { + if (caster && casterid == caster->GetID()) { return 0; }//Mob casting is same as target, fail if you are casting on yourself. } else if (focus_spell.base_value[i] == 1) { - if (casterid != GetID()) { + if (caster && casterid != caster->GetID()) { return 0; }//Mob casting is not same as target, fail if you are not casting on yourself. } break; - case SE_Ff_CasterClass: + case SE_Ff_CasterClass: { + // Do not use this limit more then once per spell. If multiple class, treat value like items would. - if (!PassLimitClass(focus_spell.base_value[i], GetClass())) { + if (caster && !PassLimitClass(focus_spell.base_value[i], caster->GetClass())) { return 0; } break; + } case SE_Ff_DurationMax: if (focus_spell.base_value[i] > spell.buff_duration) { @@ -6247,10 +6249,11 @@ uint16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) { return 0; } -int32 Client::GetFocusEffect(focusType type, uint16 spell_id) +int32 Client::GetFocusEffect(focusType type, uint16 spell_id, Mob *caster) { - if (IsBardSong(spell_id) && type != focusFcBaseEffects && type != focusSpellDuration && type != focusReduceRecastTime) + if (IsBardSong(spell_id) && type != focusFcBaseEffects && type != focusSpellDuration && type != focusReduceRecastTime) { return 0; + } int32 realTotal = 0; int32 realTotal2 = 0; @@ -6448,7 +6451,7 @@ int32 Client::GetFocusEffect(focusType type, uint16 spell_id) continue; if(rand_effectiveness) { - focus_max2 = CalcFocusEffect(type, focusspellid, spell_id, true); + focus_max2 = CalcFocusEffect(type, focusspellid, spell_id, true, buffs[buff_slot].casterid, caster); if (focus_max2 > 0 && focus_max_real2 >= 0 && focus_max2 > focus_max_real2) { focus_max_real2 = focus_max2; buff_tracker = buff_slot; @@ -6460,7 +6463,7 @@ int32 Client::GetFocusEffect(focusType type, uint16 spell_id) } } else { - Total2 = CalcFocusEffect(type, focusspellid, spell_id); + Total2 = CalcFocusEffect(type, focusspellid, spell_id, false, buffs[buff_slot].casterid, caster); if (Total2 > 0 && realTotal2 >= 0 && Total2 > realTotal2) { realTotal2 = Total2; buff_tracker = buff_slot; @@ -6473,8 +6476,13 @@ int32 Client::GetFocusEffect(focusType type, uint16 spell_id) } } + uint16 original_caster_id = 0; + if (buff_tracker >= 0 && buffs[buff_tracker].casterid > 0) { + original_caster_id = buffs[buff_tracker].casterid; + } + if(focusspell_tracker && rand_effectiveness && focus_max_real2 != 0) - realTotal2 = CalcFocusEffect(type, focusspell_tracker, spell_id); + realTotal2 = CalcFocusEffect(type, focusspell_tracker, spell_id, false, original_caster_id, caster); // For effects like gift of mana that only fire once, save the spellid into an array that consists of all available buff slots. if(buff_tracker >= 0 && buffs[buff_tracker].hit_number > 0) { @@ -6523,7 +6531,7 @@ int32 Client::GetFocusEffect(focusType type, uint16 spell_id) return realTotal + realTotal2 + realTotal3 + worneffect_bonus; } -int32 NPC::GetFocusEffect(focusType type, uint16 spell_id) { +int32 NPC::GetFocusEffect(focusType type, uint16 spell_id, Mob* caster) { int32 realTotal = 0; int32 realTotal2 = 0; @@ -6586,7 +6594,7 @@ int32 NPC::GetFocusEffect(focusType type, uint16 spell_id) { realTotal = CalcFocusEffect(type, UsedFocusID, spell_id); } - if (RuleB(Spells, NPC_UseFocusFromSpells) && spellbonuses.FocusEffects[type]){ + if ((RuleB(Spells, NPC_UseFocusFromSpells) || IsTargetedFocusEffect(type)) && spellbonuses.FocusEffects[type]){ //Spell Focus int32 Total2 = 0; @@ -6604,7 +6612,7 @@ int32 NPC::GetFocusEffect(focusType type, uint16 spell_id) { continue; if(rand_effectiveness) { - focus_max2 = CalcFocusEffect(type, focusspellid, spell_id, true); + focus_max2 = CalcFocusEffect(type, focusspellid, spell_id, true, buffs[buff_slot].casterid, caster); if (focus_max2 > 0 && focus_max_real2 >= 0 && focus_max2 > focus_max_real2) { focus_max_real2 = focus_max2; buff_tracker = buff_slot; @@ -6616,7 +6624,7 @@ int32 NPC::GetFocusEffect(focusType type, uint16 spell_id) { } } else { - Total2 = CalcFocusEffect(type, focusspellid, spell_id); + Total2 = CalcFocusEffect(type, focusspellid, spell_id, false, buffs[buff_slot].casterid, caster); if (Total2 > 0 && realTotal2 >= 0 && Total2 > realTotal2) { realTotal2 = Total2; buff_tracker = buff_slot; @@ -6629,8 +6637,14 @@ int32 NPC::GetFocusEffect(focusType type, uint16 spell_id) { } } - if(focusspell_tracker && rand_effectiveness && focus_max_real2 != 0) - realTotal2 = CalcFocusEffect(type, focusspell_tracker, spell_id); + uint16 original_caster_id = 0; + if (buff_tracker >= 0 && buffs[buff_tracker].casterid > 0) { + original_caster_id = buffs[buff_tracker].casterid; + } + + if (focusspell_tracker && rand_effectiveness && focus_max_real2 != 0) { + realTotal2 = CalcFocusEffect(type, focusspell_tracker, spell_id, false, original_caster_id, caster); + } // For effects like gift of mana that only fire once, save the spellid into an array that consists of all available buff slots. if(buff_tracker >= 0 && buffs[buff_tracker].hit_number > 0) { @@ -7027,8 +7041,8 @@ int32 Mob::GetFcDamageAmtIncoming(Mob *caster, int32 spell_id) { //THIS is target of spell cast int32 dmg = 0; - dmg += GetFocusEffect(focusFcDamageAmtIncoming, spell_id); //SPA 297 SE_FcDamageAmtIncoming - dmg += GetFocusEffect(focusFcSpellDamageAmtIncomingPC, spell_id); //SPA 484 SE_Fc_Spell_Damage_Amt_IncomingPC + dmg += GetFocusEffect(focusFcDamageAmtIncoming, spell_id, caster); //SPA 297 SE_FcDamageAmtIncoming + dmg += GetFocusEffect(focusFcSpellDamageAmtIncomingPC, spell_id, caster); //SPA 484 SE_Fc_Spell_Damage_Amt_IncomingPC return dmg; } @@ -7094,7 +7108,6 @@ bool Mob::PassLimitClass(uint32 Classes_, uint16 Class_) return false; Class_ += 1; - for (int CurrentClass = 1; CurrentClass <= PLAYER_CLASS_COUNT; ++CurrentClass){ if (Classes_ % 2 == 1){ if (CurrentClass == Class_) @@ -8351,7 +8364,7 @@ void Mob::CastSpellOnLand(Mob* caster, int32 spell_id) if ((IsValidSpell(buffs[i].spellid) && (buffs[i].spellid != spell_id) && IsEffectInSpell(buffs[i].spellid, SE_Fc_Cast_Spell_On_Land))) { //Step 2: Check if we pass all focus limiters and focus chance roll - trigger_spell_id = caster->CalcFocusEffect(focusFcCastSpellOnLand, buffs[i].spellid, spell_id, false, buffs[i].casterid); + trigger_spell_id = CalcFocusEffect(focusFcCastSpellOnLand, buffs[i].spellid, spell_id, false, buffs[i].casterid, caster); if (IsValidSpell(trigger_spell_id) && (trigger_spell_id != spell_id)) { diff --git a/zone/spells.cpp b/zone/spells.cpp index aa01fb55a..f1ba4cfa4 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4820,7 +4820,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use resist_modifier -= 2 * focus_resist; - int focus_incoming_resist = GetFocusEffect(focusFcResistIncoming, spell_id); + int focus_incoming_resist = GetFocusEffect(focusFcResistIncoming, spell_id, caster); resist_modifier -= focus_incoming_resist; From 119b2d023f147f330e0be76c005e8b224889fa89 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 14 Dec 2021 12:34:51 -0500 Subject: [PATCH 518/624] [Spells] Throwing procs fixed and other proc updates (#1871) * first updating sbindex defines * updates * updates * proctypes added for organization * debug * updates * range procs cleaned up * skill proc clean up * fix * remove debugs * [Spells] Throwing procs fixed and other proc updates * [Spells] Throwing procs fixed and other proc updates bot fix * [Spells] Throwing procs fixed and other proc updates proctype updates --- common/spdat.cpp | 6 +- common/spdat.h | 13 ++- zone/attack.cpp | 180 ++++++++++++++++++++++--------------- zone/bonuses.cpp | 72 ++++++++------- zone/bot.cpp | 4 +- zone/client_packet.cpp | 6 +- zone/client_process.cpp | 4 +- zone/common.h | 16 ++-- zone/mob.cpp | 32 +++---- zone/mob.h | 9 +- zone/pets.cpp | 6 +- zone/special_attacks.cpp | 189 ++++++++++++++++++++++----------------- zone/spell_effects.cpp | 101 ++++++++++++++++++--- zone/tune.cpp | 8 -- 14 files changed, 393 insertions(+), 253 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index b81b8167c..edd670928 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1609,19 +1609,19 @@ uint32 GetProcLimitTimer(int32 spell_id, int proc_type) { bool use_next_timer = false; for (int i = 0; i < EFFECT_COUNT; ++i) { - if (proc_type == SE_WeaponProc) { + if (proc_type == ProcType::MELEE_PROC) { if (spells[spell_id].effect_id[i] == SE_WeaponProc || spells[spell_id].effect_id[i] == SE_AddMeleeProc) { use_next_timer = true; } } - if (proc_type == SE_RangedProc) { + if (proc_type == ProcType::RANGED_PROC) { if (spells[spell_id].effect_id[i] == SE_RangedProc) { use_next_timer = true; } } - if (proc_type == SE_DefensiveProc) { + if (proc_type == ProcType::DEFENSIVE_PROC) { if (spells[spell_id].effect_id[i] == SE_DefensiveProc) { use_next_timer = true; } diff --git a/common/spdat.h b/common/spdat.h index 1e4cb49cd..cf7f7b21e 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -533,6 +533,15 @@ enum ReflectSpellType RELFECT_ALL_SINGLE_TARGET_SPELLS = 3, REFLECT_ALL_SPELLS = 4, }; +//For better organizing in proc effects, not used in spells. +enum ProcType +{ + MELEE_PROC = 1, + RANGED_PROC = 2, + DEFENSIVE_PROC = 3, + SKILL_PROC = 4, + SKILL_PROC_SUCCESS = 5, +}; enum SpellTypes : uint32 { @@ -1134,8 +1143,8 @@ typedef enum { #define SE_GravityEffect 424 // implemented - Pulls/pushes you toward/away the mob at a set pace //#define SE_Display 425 // *not implemented - Illusion: Flying Dragon(21626) #define SE_IncreaseExtTargetWindow 426 // *not implmented[AA] - increases the capacity of your extended target window -#define SE_SkillProc 427 // implemented - chance to proc when using a skill(ie taunt) -#define SE_LimitToSkill 428 // implemented - limits what skills will effect a skill proc +#define SE_SkillProcAttempt 427 // implemented - chance to proc when using a skill(ie taunt) +#define SE_LimitToSkill 428 // implemented, @Procs, limits what combat skills will effect a skill proc, base: skill value, limit: none, max: none #define SE_SkillProcSuccess 429 // implemented - chance to proc when tje skill in use successfully fires. //#define SE_PostEffect 430 // *not implemented - Fear of the Dark(27641) - Alters vision //#define SE_PostEffectData 431 // *not implemented - Fear of the Dark(27641) - Alters vision diff --git a/zone/attack.cpp b/zone/attack.cpp index 403528430..da4394d91 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1603,12 +1603,12 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b /////////////////////////////////////////////////////////// ////// Send Attack Damage /////////////////////////////////////////////////////////// - if (my_hit.damage_done > 0 && aabonuses.SkillAttackProc[SBIndex::SKILLPROC_CHANCE] && aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SKILL] == my_hit.skill && - IsValidSpell(aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID])) { - float chance = aabonuses.SkillAttackProc[SBIndex::SKILLPROC_CHANCE] / 1000.0f; + if (my_hit.damage_done > 0 && aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] && aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SKILL] == my_hit.skill && + IsValidSpell(aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID])) { + float chance = aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] / 1000.0f; if (zone->random.Roll(chance)) - SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID], other, EQ::spells::CastingSlot::Item, 0, -1, - spells[aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID]].resist_difficulty); + SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID], other, EQ::spells::CastingSlot::Item, 0, -1, + spells[aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID]].resist_difficulty); } other->Damage(this, my_hit.damage_done, SPELL_UNKNOWN, my_hit.skill, true, -1, false, m_specialattacks); @@ -1616,9 +1616,6 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b MeleeLifeTap(my_hit.damage_done); - if (my_hit.damage_done > 0 && HasSkillProcSuccess() && other && other->GetHP() > 0) - TrySkillProc(other, my_hit.skill, 0, true, Hand); - CommonBreakInvisibleFromCombat(); if (GetTarget()) @@ -3471,7 +3468,6 @@ bool Mob::HasDefensiveProcs() const bool Mob::HasSkillProcs() const { - for (int i = 0; i < MAX_SKILL_PROCS; i++) { if (spellbonuses.SkillProc[i] || itembonuses.SkillProc[i] || aabonuses.SkillProc[i]) return true; @@ -4164,12 +4160,12 @@ void Mob::TryDefensiveProc(Mob *on, uint16 hand) { //Spell Procs and Quest added procs for (int i = 0; i < MAX_PROCS; i++) { if (IsValidSpell(DefensiveProcs[i].spellID)) { - if (!IsProcLimitTimerActive(DefensiveProcs[i].base_spellID, DefensiveProcs[i].proc_reuse_time, SE_DefensiveProc)) { + if (!IsProcLimitTimerActive(DefensiveProcs[i].base_spellID, DefensiveProcs[i].proc_reuse_time, ProcType::DEFENSIVE_PROC)) { float chance = ProcChance * (static_cast(DefensiveProcs[i].chance) / 100.0f); if (zone->random.Roll(chance)) { ExecWeaponProc(nullptr, DefensiveProcs[i].spellID, on); CheckNumHitsRemaining(NumHit::DefensiveSpellProcs, 0, DefensiveProcs[i].base_spellID); - SetProcLimitTimer(DefensiveProcs[i].base_spellID, DefensiveProcs[i].proc_reuse_time, SE_DefensiveProc); + SetProcLimitTimer(DefensiveProcs[i].base_spellID, DefensiveProcs[i].proc_reuse_time, ProcType::DEFENSIVE_PROC); } } } @@ -4178,17 +4174,17 @@ void Mob::TryDefensiveProc(Mob *on, uint16 hand) { //AA Procs if (IsClient()){ for (int i = 0; i < MAX_AA_PROCS; i += 4) { - int32 aa_rank_id = aabonuses.DefensiveProc[i]; - int32 aa_spell_id = aabonuses.DefensiveProc[i + 1]; - int32 aa_proc_chance = 100 + aabonuses.DefensiveProc[i + 2]; - uint32 aa_proc_reuse_timer = aabonuses.DefensiveProc[i + 3]; + int32 aa_rank_id = aabonuses.DefensiveProc[i + +SBIndex::COMBAT_PROC_ORIGIN_ID]; + int32 aa_spell_id = aabonuses.DefensiveProc[i + SBIndex::COMBAT_PROC_SPELL_ID]; + int32 aa_proc_chance = 100 + aabonuses.DefensiveProc[i + SBIndex::COMBAT_PROC_RATE_MOD]; + uint32 aa_proc_reuse_timer = aabonuses.DefensiveProc[i + SBIndex::COMBAT_PROC_REUSE_TIMER]; if (aa_rank_id) { - if (!IsProcLimitTimerActive(-aa_rank_id, aa_proc_reuse_timer, SE_DefensiveProc)) { + if (!IsProcLimitTimerActive(-aa_rank_id, aa_proc_reuse_timer, ProcType::DEFENSIVE_PROC)) { float chance = ProcChance * (static_cast(aa_proc_chance) / 100.0f); if (zone->random.Roll(chance) && IsValidSpell(aa_spell_id)) { ExecWeaponProc(nullptr, aa_spell_id, on); - SetProcLimitTimer(-aa_rank_id, aa_proc_reuse_timer, SE_DefensiveProc); + SetProcLimitTimer(-aa_rank_id, aa_proc_reuse_timer, ProcType::DEFENSIVE_PROC); } } } @@ -4197,7 +4193,8 @@ void Mob::TryDefensiveProc(Mob *on, uint16 hand) { } } -void Mob::TryWeaponProc(const EQ::ItemInstance* weapon_g, Mob *on, uint16 hand) { +void Mob::TryCombatProcs(const EQ::ItemInstance* weapon_g, Mob *on, uint16 hand, const EQ::ItemData* weapon_data) { + if (!on) { SetTarget(nullptr); LogError("A null Mob object was passed to Mob::TryWeaponProc for evaluation!"); @@ -4214,6 +4211,13 @@ void Mob::TryWeaponProc(const EQ::ItemInstance* weapon_g, Mob *on, uint16 hand) return; } + //used for special case when checking last ammo item on projectile hit. + if (!weapon_g && weapon_data) { + TryWeaponProc(nullptr, weapon_data, on, hand); + TrySpellProc(nullptr, weapon_data, on, hand); + return; + } + if (!weapon_g) { TrySpellProc(nullptr, (const EQ::ItemData*)nullptr, on); return; @@ -4253,7 +4257,7 @@ void Mob::TryWeaponProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon // Try innate proc on weapon // We can proc once here, either weapon or one aug bool proced = false; // silly bool to prevent augs from going if weapon does - skillinuse = GetSkillByItemType(weapon->ItemType); + if (weapon->Proc.Type == EQ::item::ItemEffectCombatProc && IsValidSpell(weapon->Proc.Effect)) { float WPC = ProcChance * (100.0f + // Proc chance for this weapon static_cast(weapon->ProcRate)) / 100.0f; @@ -4330,8 +4334,16 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon, float ProcChance = 0.0f; ProcChance = GetProcChances(ProcBonus, hand); - if (hand == EQ::invslot::slotSecondary) + bool passed_skill_limit_check = true; + EQ::skills::SkillType skillinuse = EQ::skills::SkillHandtoHand; + + if (weapon){ + skillinuse = GetSkillByItemType(weapon->ItemType); + } + + if (hand == EQ::invslot::slotSecondary) { ProcChance /= 2; + } bool rangedattk = false; if (weapon && hand == EQ::invslot::slotRange) { @@ -4343,8 +4355,9 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon, } } - if (!weapon && hand == EQ::invslot::slotRange && GetSpecialAbility(SPECATK_RANGED_ATK)) + if (!weapon && hand == EQ::invslot::slotRange && GetSpecialAbility(SPECATK_RANGED_ATK)) { rangedattk = true; + } int16 poison_slot=-1; @@ -4353,8 +4366,9 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon, continue; // If pets ever can proc from off hand, this will need to change if (SpellProcs[i].base_spellID == POISON_PROC && - (!weapon || weapon->ItemType != EQ::item::ItemType1HPiercing)) + (!weapon || weapon->ItemType != EQ::item::ItemType1HPiercing)) { continue; // Old school poison will only proc with 1HP equipped. + } // Not ranged if (!rangedattk) { @@ -4375,14 +4389,16 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon, poison_slot=i; continue; // Process the poison proc last per @mackal } - - if (!IsProcLimitTimerActive(SpellProcs[i].base_spellID, SpellProcs[i].proc_reuse_time, SE_WeaponProc)) { + + passed_skill_limit_check = PassLimitToSkill(skillinuse, SpellProcs[i].base_spellID, ProcType::MELEE_PROC); + + if (passed_skill_limit_check && !IsProcLimitTimerActive(SpellProcs[i].base_spellID, SpellProcs[i].proc_reuse_time, ProcType::MELEE_PROC)) { float chance = ProcChance * (static_cast(SpellProcs[i].chance) / 100.0f); if (zone->random.Roll(chance)) { LogCombat("Spell proc [{}] procing spell [{}] ([{}] percent chance)", i, SpellProcs[i].spellID, chance); SendBeginCast(SpellProcs[i].spellID, 0); ExecWeaponProc(nullptr, SpellProcs[i].spellID, on, SpellProcs[i].level_override); - SetProcLimitTimer(SpellProcs[i].base_spellID, SpellProcs[i].proc_reuse_time, SE_WeaponProc); + SetProcLimitTimer(SpellProcs[i].base_spellID, SpellProcs[i].proc_reuse_time, ProcType::MELEE_PROC); CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0, SpellProcs[i].base_spellID); } else { @@ -4395,13 +4411,15 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon, // ranged spell procs (buffs) if (RangedProcs[i].spellID != SPELL_UNKNOWN) { - if (!IsProcLimitTimerActive(RangedProcs[i].base_spellID, RangedProcs[i].proc_reuse_time, SE_RangedProc)) { + passed_skill_limit_check = PassLimitToSkill(skillinuse, RangedProcs[i].base_spellID, ProcType::RANGED_PROC); + + if (passed_skill_limit_check && !IsProcLimitTimerActive(RangedProcs[i].base_spellID, RangedProcs[i].proc_reuse_time, ProcType::RANGED_PROC)) { float chance = ProcChance * (static_cast(RangedProcs[i].chance) / 100.0f); if (zone->random.Roll(chance)) { LogCombat("Ranged proc [{}] procing spell [{}] ([{}] percent chance)", i, RangedProcs[i].spellID, chance); ExecWeaponProc(nullptr, RangedProcs[i].spellID, on); CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0, RangedProcs[i].base_spellID); - SetProcLimitTimer(RangedProcs[i].base_spellID, RangedProcs[i].proc_reuse_time, SE_RangedProc); + SetProcLimitTimer(RangedProcs[i].base_spellID, RangedProcs[i].proc_reuse_time, ProcType::RANGED_PROC); } else { LogCombat("Ranged proc [{}] failed to proc [{}] ([{}] percent chance)", i, RangedProcs[i].spellID, chance); @@ -4411,7 +4429,7 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon, } } - //AA Procs + //AA Melee and Ranged Procs if (IsClient()) { for (int i = 0; i < MAX_AA_PROCS; i += 4) { @@ -4423,22 +4441,25 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon, if (!rangedattk) { - aa_rank_id = aabonuses.SpellProc[i]; - aa_spell_id = aabonuses.SpellProc[i + 1]; - aa_proc_chance += aabonuses.SpellProc[i + 2]; - aa_proc_reuse_timer = aabonuses.SpellProc[i + 3]; - proc_type = SE_WeaponProc; + aa_rank_id = aabonuses.SpellProc[i + SBIndex::COMBAT_PROC_ORIGIN_ID]; + aa_spell_id = aabonuses.SpellProc[i + SBIndex::COMBAT_PROC_SPELL_ID]; + aa_proc_chance += aabonuses.SpellProc[i + SBIndex::COMBAT_PROC_RATE_MOD]; + aa_proc_reuse_timer = aabonuses.SpellProc[i + SBIndex::COMBAT_PROC_RATE_MOD]; + proc_type = ProcType::MELEE_PROC; } else { - aa_rank_id = aabonuses.RangedProc[i]; - aa_spell_id = aabonuses.RangedProc[i + 1]; - aa_proc_chance += aabonuses.RangedProc[i + 2]; - aa_proc_reuse_timer = aabonuses.RangedProc[i + 3]; - proc_type = SE_RangedProc; + aa_rank_id = aabonuses.RangedProc[i + SBIndex::COMBAT_PROC_ORIGIN_ID]; + aa_spell_id = aabonuses.RangedProc[i + SBIndex::COMBAT_PROC_SPELL_ID]; + aa_proc_chance += aabonuses.RangedProc[i + SBIndex::COMBAT_PROC_RATE_MOD]; + aa_proc_reuse_timer = aabonuses.RangedProc[i + SBIndex::COMBAT_PROC_RATE_MOD]; + proc_type = ProcType::RANGED_PROC; } if (aa_rank_id) { - if (!IsProcLimitTimerActive(-aa_rank_id, aa_proc_reuse_timer, proc_type)) { + + passed_skill_limit_check = PassLimitToSkill(skillinuse, 0, proc_type, aa_rank_id); + + if (passed_skill_limit_check && !IsProcLimitTimerActive(-aa_rank_id, aa_proc_reuse_timer, proc_type)) { float chance = ProcChance * (static_cast(aa_proc_chance) / 100.0f); if (zone->random.Roll(chance) && IsValidSpell(aa_spell_id)) { LogCombat("AA proc [{}] procing spell [{}] ([{}] percent chance)", aa_rank_id, aa_spell_id, chance); @@ -4469,13 +4490,12 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon, } if (HasSkillProcs() && hand != EQ::invslot::slotRange) { //We check ranged skill procs within the attack functions. - uint16 skillinuse = 28; - if (weapon) - skillinuse = GetSkillByItemType(weapon->ItemType); - TrySkillProc(on, skillinuse, 0, false, hand); } + if (HasSkillProcSuccess() && hand != EQ::invslot::slotRange) { //We check ranged skill procs within the attack functions. + TrySkillProc(on, skillinuse, 0, true, hand); + } return; } @@ -5040,7 +5060,7 @@ void Mob::ApplyDamageTable(DamageHitInfo &hit) Log(Logs::Detail, Logs::Attack, "Damage table applied %d (max %d)", percent, damage_table.max_extra); } -void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, uint16 hand, bool IsDefensive) +void Mob::TrySkillProc(Mob *on, EQ::skills::SkillType skill, uint16 ReuseTime, bool Success, uint16 hand, bool IsDefensive) { if (!on) { @@ -5049,12 +5069,19 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui return; } - if (!spellbonuses.LimitToSkill[skill] && !itembonuses.LimitToSkill[skill] && !aabonuses.LimitToSkill[skill]) + if (on->HasDied()) { return; + } - /*Allow one proc from each (Spell/Item/AA) - Kayen: Due to limited avialability of effects on live it is too difficult - to confirm how they stack at this time, will adjust formula when more data is avialablle to test.*/ + if (!spellbonuses.LimitToSkill[skill] && !itembonuses.LimitToSkill[skill] && !aabonuses.LimitToSkill[skill]) { + return; + } + + /* + Allow one proc from each (Spell/Item/AA) + Kayen: Due to limited avialability of effects on live it is too difficult + to confirm how they stack at this time, will adjust formula when more data is avialablle to test. + */ bool CanProc = true; uint16 base_spell_id = 0; @@ -5069,22 +5096,24 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui if (spellbonuses.LimitToSkill[skill]) { - for (int e = 0; e < MAX_SKILL_PROCS; e++) { + for (int i = 0; i < MAX_SKILL_PROCS; i++) { if (CanProc && - ((!Success && spellbonuses.SkillProc[e] && IsValidSpell(spellbonuses.SkillProc[e])) - || (Success && spellbonuses.SkillProcSuccess[e] && IsValidSpell(spellbonuses.SkillProcSuccess[e])))) { + ((!Success && spellbonuses.SkillProc[i] && IsValidSpell(spellbonuses.SkillProc[i])) + || (Success && spellbonuses.SkillProcSuccess[i] && IsValidSpell(spellbonuses.SkillProcSuccess[i])))) { - if (Success) - base_spell_id = spellbonuses.SkillProcSuccess[e]; - else - base_spell_id = spellbonuses.SkillProc[e]; + if (Success) { + base_spell_id = spellbonuses.SkillProcSuccess[i]; + } + else { + base_spell_id = spellbonuses.SkillProc[i]; + } proc_spell_id = 0; ProcMod = 0; for (int i = 0; i < EFFECT_COUNT; i++) { - if (spells[base_spell_id].effect_id[i] == SE_SkillProc || spells[base_spell_id].effect_id[i] == SE_SkillProcSuccess) { + if (spells[base_spell_id].effect_id[i] == SE_SkillProcAttempt || spells[base_spell_id].effect_id[i] == SE_SkillProcSuccess) { proc_spell_id = spells[base_spell_id].base_value[i]; ProcMod = static_cast(spells[base_spell_id].limit_value[i]); } @@ -5095,8 +5124,7 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui float final_chance = chance * (ProcMod / 100.0f); if (zone->random.Roll(final_chance)) { ExecWeaponProc(nullptr, proc_spell_id, on); - CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0, - base_spell_id); + CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0, base_spell_id); CanProc = false; break; } @@ -5114,21 +5142,23 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui if (itembonuses.LimitToSkill[skill]) { CanProc = true; - for (int e = 0; e < MAX_SKILL_PROCS; e++) { + for (int i = 0; i < MAX_SKILL_PROCS; i++) { if (CanProc && - ((!Success && itembonuses.SkillProc[e] && IsValidSpell(itembonuses.SkillProc[e])) - || (Success && itembonuses.SkillProcSuccess[e] && IsValidSpell(itembonuses.SkillProcSuccess[e])))) { + ((!Success && itembonuses.SkillProc[i] && IsValidSpell(itembonuses.SkillProc[i])) + || (Success && itembonuses.SkillProcSuccess[i] && IsValidSpell(itembonuses.SkillProcSuccess[i])))) { - if (Success) - base_spell_id = itembonuses.SkillProcSuccess[e]; - else - base_spell_id = itembonuses.SkillProc[e]; + if (Success) { + base_spell_id = itembonuses.SkillProcSuccess[i]; + } + else { + base_spell_id = itembonuses.SkillProc[i]; + } proc_spell_id = 0; ProcMod = 0; for (int i = 0; i < EFFECT_COUNT; i++) { - if (spells[base_spell_id].effect_id[i] == SE_SkillProc || spells[base_spell_id].effect_id[i] == SE_SkillProcSuccess) { + if (spells[base_spell_id].effect_id[i] == SE_SkillProcAttempt || spells[base_spell_id].effect_id[i] == SE_SkillProcSuccess) { proc_spell_id = spells[base_spell_id].base_value[i]; ProcMod = static_cast(spells[base_spell_id].limit_value[i]); } @@ -5161,16 +5191,16 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui int32 limit_value = 0; uint32 slot = 0; - for (int e = 0; e < MAX_SKILL_PROCS; e++) { + for (int i = 0; i < MAX_SKILL_PROCS; i++) { if (CanProc && - ((!Success && aabonuses.SkillProc[e]) - || (Success && aabonuses.SkillProcSuccess[e]))) { + ((!Success && aabonuses.SkillProc[i]) + || (Success && aabonuses.SkillProcSuccess[i]))) { int aaid = 0; if (Success) - base_spell_id = aabonuses.SkillProcSuccess[e]; + base_spell_id = aabonuses.SkillProcSuccess[i]; else - base_spell_id = aabonuses.SkillProc[e]; + base_spell_id = aabonuses.SkillProc[i]; proc_spell_id = 0; ProcMod = 0; @@ -5190,7 +5220,7 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui limit_value = effect.limit_value; slot = effect.slot; - if (effect_id == SE_SkillProc || effect_id == SE_SkillProcSuccess) { + if (effect_id == SE_SkillProcAttempt || effect_id == SE_SkillProcSuccess) { proc_spell_id = base_value; ProcMod = static_cast(limit_value); } @@ -5225,12 +5255,14 @@ float Mob::GetSkillProcChances(uint16 ReuseTime, uint16 hand) { if (!ReuseTime && hand) { weapon_speed = GetWeaponSpeedbyHand(hand); ProcChance = static_cast(weapon_speed) * (RuleR(Combat, AvgProcsPerMinute) / 60000.0f); - if (hand == EQ::invslot::slotSecondary) + if (hand == EQ::invslot::slotSecondary) { ProcChance /= 2; + } } - else + else { ProcChance = static_cast(ReuseTime) * (RuleR(Combat, AvgProcsPerMinute) / 60000.0f); + } return ProcChance; } diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index bf4c5cfb9..28b98d655 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1074,11 +1074,11 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) case SE_WeaponProc: case SE_AddMeleeProc: for (int i = 0; i < MAX_AA_PROCS; i += 4) { - if (!newbon->SpellProc[i]) { - newbon->SpellProc[i] = rank.id; //aa rank id - newbon->SpellProc[i + 1] = base_value; //proc spell id - newbon->SpellProc[i + 2] = limit_value; //proc rate modifer - newbon->SpellProc[i + 3] = 0; //Lock out Timer + if (!newbon->SpellProc[i + SBIndex::COMBAT_PROC_ORIGIN_ID]) { + newbon->SpellProc[i + SBIndex::COMBAT_PROC_ORIGIN_ID] = rank.id; //aa rank id + newbon->SpellProc[i + SBIndex::COMBAT_PROC_SPELL_ID] = base_value; //proc spell id + newbon->SpellProc[i + SBIndex::COMBAT_PROC_RATE_MOD] = limit_value; //proc rate modifer + newbon->SpellProc[i + SBIndex::COMBAT_PROC_REUSE_TIMER] = 0; //Lock out Timer break; } } @@ -1086,11 +1086,11 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) case SE_RangedProc: for (int i = 0; i < MAX_AA_PROCS; i += 4) { - if (!newbon->RangedProc[i]) { - newbon->RangedProc[i] = rank.id; //aa rank id - newbon->RangedProc[i + 1] = base_value; //proc spell id - newbon->RangedProc[i + 2] = limit_value; //proc rate modifer - newbon->RangedProc[i + 3] = 0; //Lock out Timer + if (!newbon->RangedProc[i + SBIndex::COMBAT_PROC_ORIGIN_ID]) { + newbon->RangedProc[i + SBIndex::COMBAT_PROC_ORIGIN_ID] = rank.id; //aa rank id + newbon->RangedProc[i + SBIndex::COMBAT_PROC_SPELL_ID] = base_value; //proc spell id + newbon->RangedProc[i + SBIndex::COMBAT_PROC_RATE_MOD] = limit_value; //proc rate modifer + newbon->RangedProc[i + SBIndex::COMBAT_PROC_REUSE_TIMER] = 0; //Lock out Timer break; } } @@ -1098,11 +1098,11 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) case SE_DefensiveProc: for (int i = 0; i < MAX_AA_PROCS; i += 4) { - if (!newbon->DefensiveProc[i]) { - newbon->DefensiveProc[i] = rank.id; //aa rank id - newbon->DefensiveProc[i + 1] = base_value; //proc spell id - newbon->DefensiveProc[i + 2] = limit_value; //proc rate modifer - newbon->DefensiveProc[i + 3] = 0; //Lock out Timer + if (!newbon->DefensiveProc[i + SBIndex::COMBAT_PROC_ORIGIN_ID]) { + newbon->DefensiveProc[i + SBIndex::COMBAT_PROC_ORIGIN_ID] = rank.id; //aa rank id + newbon->DefensiveProc[i + SBIndex::COMBAT_PROC_SPELL_ID] = base_value; //proc spell id + newbon->DefensiveProc[i + SBIndex::COMBAT_PROC_RATE_MOD] = limit_value; //proc rate modifer + newbon->DefensiveProc[i + SBIndex::COMBAT_PROC_REUSE_TIMER] = 0; //Lock out Timer break; } } @@ -1118,23 +1118,23 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) newbon->Proc_Timer_Modifier = true; for (int i = 0; i < MAX_AA_PROCS; i += 4) { - if (newbon->SpellProc[i] == rank.id) { - if (!newbon->SpellProc[i + 3]) { - newbon->SpellProc[i + 3] = limit_value;//Lock out Timer + if (newbon->SpellProc[i + SBIndex::COMBAT_PROC_ORIGIN_ID] == rank.id) { + if (!newbon->SpellProc[i + SBIndex::COMBAT_PROC_REUSE_TIMER]) { + newbon->SpellProc[i + SBIndex::COMBAT_PROC_REUSE_TIMER] = limit_value;//Lock out Timer break; } } - if (newbon->RangedProc[i] == rank.id) { - if (!newbon->RangedProc[i + 3]) { - newbon->RangedProc[i + 3] = limit_value;//Lock out Timer + if (newbon->RangedProc[i + SBIndex::COMBAT_PROC_ORIGIN_ID] == rank.id) { + if (!newbon->RangedProc[i + SBIndex::COMBAT_PROC_REUSE_TIMER]) { + newbon->RangedProc[i + SBIndex::COMBAT_PROC_REUSE_TIMER] = limit_value;//Lock out Timer break; } } - if (newbon->DefensiveProc[i] == rank.id) { - if (!newbon->DefensiveProc[i + 3]) { - newbon->DefensiveProc[i + 3] = limit_value;//Lock out Timer + if (newbon->DefensiveProc[i + SBIndex::COMBAT_PROC_ORIGIN_ID] == rank.id) { + if (!newbon->DefensiveProc[i + SBIndex::COMBAT_PROC_REUSE_TIMER]) { + newbon->DefensiveProc[i + SBIndex::COMBAT_PROC_REUSE_TIMER] = limit_value;//Lock out Timer break; } } @@ -1208,9 +1208,9 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) case SE_SkillAttackProc: { // You can only have one of these per client. [AA Dragon Punch] - newbon->SkillAttackProc[SBIndex::SKILLPROC_CHANCE] = base_value; // Chance base 1000 = 100% proc rate - newbon->SkillAttackProc[SBIndex::SKILLPROC_SKILL] = limit_value; // Skill to Proc Off - newbon->SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID] = rank.spell; // spell to proc + newbon->SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] = base_value; // Chance base 1000 = 100% proc rate + newbon->SkillAttackProc[SBIndex::SKILLATK_PROC_SKILL] = limit_value; // Skill to Proc Off + newbon->SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID] = rank.spell; // spell to proc break; } @@ -1458,15 +1458,19 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) break; case SE_LimitToSkill: { + // Bad data or unsupported new skill - if (limit_value > EQ::skills::HIGHEST_SKILL) + if (base_value > EQ::skills::HIGHEST_SKILL) { break; - if (base_value <= EQ::skills::HIGHEST_SKILL) + } + if (base_value <= EQ::skills::HIGHEST_SKILL) { newbon->LimitToSkill[base_value] = true; + newbon->LimitToSkill[EQ::skills::HIGHEST_SKILL + 3] = true; //Used as a general exists check + } break; } - case SE_SkillProc: { + case SE_SkillProcAttempt: { for (int e = 0; e < MAX_SKILL_PROCS; e++) { if (newbon->SkillProc[e] && newbon->SkillProc[e] == rank.id) break; // Do not use the same aa id more than once. @@ -3535,15 +3539,17 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_LimitToSkill:{ // Bad data or unsupported new skill - if (limit_value > EQ::skills::HIGHEST_SKILL) + if (effect_value > EQ::skills::HIGHEST_SKILL) { break; + } if (effect_value <= EQ::skills::HIGHEST_SKILL){ new_bonus->LimitToSkill[effect_value] = true; + new_bonus->LimitToSkill[EQ::skills::HIGHEST_SKILL + 3] = true; //Used as a general exists check } break; } - case SE_SkillProc:{ + case SE_SkillProcAttempt:{ for(int e = 0; e < MAX_SKILL_PROCS; e++) { @@ -5528,7 +5534,7 @@ void Mob::NegateSpellEffectBonuses(uint16 spell_id) } } - case SE_SkillProc: { + case SE_SkillProcAttempt: { for (int e = 0; e < MAX_SKILL_PROCS; e++) { if (negate_spellbonus) { spellbonuses.SkillProc[e] = effect_value; } diff --git a/zone/bot.cpp b/zone/bot.cpp index 5de3bc7e9..031282051 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3248,7 +3248,7 @@ void Bot::AI_Process() TriggerDefensiveProcs(tar, EQ::invslot::slotPrimary, false); TEST_COMBATANTS(); - TryWeaponProc(p_item, tar, EQ::invslot::slotPrimary); + TryCombatProcs(p_item, tar, EQ::invslot::slotPrimary); // bool tripleSuccess = false; @@ -3337,7 +3337,7 @@ void Bot::AI_Process() Attack(tar, EQ::invslot::slotSecondary); // Single attack with offhand TEST_COMBATANTS(); - TryWeaponProc(s_item, tar, EQ::invslot::slotSecondary); + TryCombatProcs(s_item, tar, EQ::invslot::slotSecondary); TEST_COMBATANTS(); if (CanThisClassDoubleAttack() && CheckBotDoubleAttack()) { diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index e23bebe65..b7b44277e 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -721,17 +721,17 @@ void Client::CompleteConnect() case SE_AddMeleeProc: case SE_WeaponProc: { - AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, buffs[j1].casterlevel, GetProcLimitTimer(buffs[j1].spellid, SE_WeaponProc)); + AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, buffs[j1].casterlevel, GetProcLimitTimer(buffs[j1].spellid, ProcType::MELEE_PROC)); break; } case SE_DefensiveProc: { - AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, GetProcLimitTimer(buffs[j1].spellid, SE_DefensiveProc)); + AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, GetProcLimitTimer(buffs[j1].spellid, ProcType::DEFENSIVE_PROC)); break; } case SE_RangedProc: { - AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, GetProcLimitTimer(buffs[j1].spellid, SE_RangedProc)); + AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, GetProcLimitTimer(buffs[j1].spellid, ProcType::RANGED_PROC)); break; } } diff --git a/zone/client_process.cpp b/zone/client_process.cpp index b363292e8..e96e2ac02 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -414,7 +414,7 @@ bool Client::Process() { else if (auto_attack_target->GetHP() > -10) // -10 so we can watch people bleed in PvP { EQ::ItemInstance *wpn = GetInv().GetItem(EQ::invslot::slotPrimary); - TryWeaponProc(wpn, auto_attack_target, EQ::invslot::slotPrimary); + TryCombatProcs(wpn, auto_attack_target, EQ::invslot::slotPrimary); TriggerDefensiveProcs(auto_attack_target, EQ::invslot::slotPrimary, false); DoAttackRounds(auto_attack_target, EQ::invslot::slotPrimary); @@ -460,7 +460,7 @@ bool Client::Process() { CheckIncreaseSkill(EQ::skills::SkillDualWield, auto_attack_target, -10); if (CheckDualWield()) { EQ::ItemInstance *wpn = GetInv().GetItem(EQ::invslot::slotSecondary); - TryWeaponProc(wpn, auto_attack_target, EQ::invslot::slotSecondary); + TryCombatProcs(wpn, auto_attack_target, EQ::invslot::slotSecondary); DoAttackRounds(auto_attack_target, EQ::invslot::slotSecondary); } diff --git a/zone/common.h b/zone/common.h index cd7d072be..4998d3889 100644 --- a/zone/common.h +++ b/zone/common.h @@ -533,7 +533,7 @@ struct StatBonuses { int32 Metabolism; // Food/drink consumption rates. bool Sanctuary; // Sanctuary effect, lowers place on hate list until cast on others. int32 FactionModPct; // Modifies amount of faction gained. - bool LimitToSkill[EQ::skills::HIGHEST_SKILL + 2]; // Determines if we need to search for a skill proc. + bool LimitToSkill[EQ::skills::HIGHEST_SKILL + 3]; // Determines if we need to search for a skill proc. uint32 SkillProc[MAX_SKILL_PROCS]; // Max number of spells containing skill_procs. uint32 SkillProcSuccess[MAX_SKILL_PROCS]; // Max number of spells containing skill_procs_success. int32 SpellProc[MAX_AA_PROCS]; // Max number of spells containing melee spell procs. @@ -671,9 +671,9 @@ namespace SBIndex { constexpr uint16 POSITION_FRONT = 1; // SPA 503-506 constexpr uint16 PET_RAMPAGE_CHANCE = 0; // SPA 464,465 constexpr uint16 PET_RAMPAGE_DMG_MOD = 1; // SPA 465,465 - constexpr uint16 SKILLPROC_CHANCE = 0; // SPA 427 - constexpr uint16 SKILLPROC_SKILL = 1; // SPA 427 - constexpr uint16 SKILLPROC_SPELL_ID = 2; // SPA 427 + constexpr uint16 SKILLATK_PROC_CHANCE = 0; // SPA 427 + constexpr uint16 SKILLATK_PROC_SKILL = 1; // SPA 427 + constexpr uint16 SKILLATK_PROC_SPELL_ID = 2; // SPA 427 constexpr uint16 SLAYUNDEAD_RATE_MOD = 0; // SPA 219 constexpr uint16 SLAYUNDEAD_DMG_MOD = 1; // SPA 219 constexpr uint16 DOUBLE_RIPOSTE_CHANCE = 0; // SPA 223 @@ -688,14 +688,18 @@ namespace SBIndex { constexpr uint16 REFLECT_CHANCE = 0; // SPA 158 constexpr uint16 REFLECT_RESISTANCE_MOD = 1; // SPA 158 constexpr uint16 REFLECT_DMG_EFFECTIVENESS = 2; // SPA 158 + constexpr uint16 COMBAT_PROC_ORIGIN_ID = 0; // SPA + constexpr uint16 COMBAT_PROC_SPELL_ID = 1; // SPA + constexpr uint16 COMBAT_PROC_RATE_MOD = 2; // SPA + constexpr uint16 COMBAT_PROC_REUSE_TIMER = 3; // SPA }; typedef struct { - uint16 spellID; + int32 spellID; uint16 chance; - uint16 base_spellID; + int32 base_spellID; int level_override; uint32 proc_reuse_time; } tProc; diff --git a/zone/mob.cpp b/zone/mob.cpp index c2e09c121..f4e4582be 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4213,15 +4213,17 @@ int Mob::GetSnaredAmount() void Mob::TriggerDefensiveProcs(Mob *on, uint16 hand, bool FromSkillProc, int damage) { - if (!on) + if (!on) { return; + } - if (!FromSkillProc) + if (!FromSkillProc) { on->TryDefensiveProc(this, hand); + } //Defensive Skill Procs if (damage < 0 && damage >= -4) { - uint16 skillinuse = 0; + EQ::skills::SkillType skillinuse = EQ::skills::SkillBlock; switch (damage) { case (-1): skillinuse = EQ::skills::SkillBlock; @@ -4240,11 +4242,13 @@ void Mob::TriggerDefensiveProcs(Mob *on, uint16 hand, bool FromSkillProc, int da break; } - if (on->HasSkillProcs()) + if (on->HasSkillProcs()) { on->TrySkillProc(this, skillinuse, 0, false, hand, true); + } - if (on->HasSkillProcSuccess()) + if (on && on->HasSkillProcSuccess()) { on->TrySkillProc(this, skillinuse, 0, true, hand, true); + } } } @@ -5617,7 +5621,7 @@ void Mob::SlowMitigation(Mob* caster) } } -uint16 Mob::GetSkillByItemType(int ItemType) +EQ::skills::SkillType Mob::GetSkillByItemType(int ItemType) { switch (ItemType) { case EQ::item::ItemType1HSlash: @@ -5673,22 +5677,6 @@ uint8 Mob::GetItemTypeBySkill(EQ::skills::SkillType skill) } } - -bool Mob::PassLimitToSkill(uint16 spell_id, uint16 skill) { - - if (!IsValidSpell(spell_id)) - return false; - - for (int i = 0; i < EFFECT_COUNT; i++) { - if (spells[spell_id].effect_id[i] == SE_LimitToSkill){ - if (spells[spell_id].base_value[i] == skill){ - return true; - } - } - } - return false; -} - uint16 Mob::GetWeaponSpeedbyHand(uint16 hand) { uint16 weapon_speed = 0; diff --git a/zone/mob.h b/zone/mob.h index 07c5c4382..0ee5a76c2 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -701,7 +701,7 @@ public: static uint32 RandomTimer(int min, int max); static uint8 GetDefaultGender(uint16 in_race, uint8 in_gender = 0xFF); static bool IsPlayerRace(uint16 in_race); - uint16 GetSkillByItemType(int ItemType); + EQ::skills::SkillType GetSkillByItemType(int ItemType); uint8 GetItemTypeBySkill(EQ::skills::SkillType skill); virtual void MakePet(uint16 spell_id, const char* pettype, const char *petname = nullptr); virtual void MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, const char *petname = nullptr, float in_size = 0.0f); @@ -864,6 +864,7 @@ public: bool IsTargetedFocusEffect(int focus_type); bool HasPersistDeathIllusion(int32 spell_id); + bool TryDoubleMeleeRoundEffect(); bool GetUseDoubleMeleeRoundDmgBonus() const { return use_double_melee_round_dmg_bonus; } inline void SetUseDoubleMeleeRoundDmgBonus(bool val) { use_double_melee_round_dmg_bonus = val; } @@ -1446,13 +1447,13 @@ protected: bool spawned; void CalcSpellBonuses(StatBonuses* newbon); virtual void CalcBonuses(); - void TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success = false, uint16 hand = 0, bool IsDefensive = false); // hand = SlotCharm? - bool PassLimitToSkill(uint16 spell_id, uint16 skill); + void TrySkillProc(Mob *on, EQ::skills::SkillType skill, uint16 ReuseTime, bool Success = false, uint16 hand = 0, bool IsDefensive = false); // hand = SlotCharm? + bool PassLimitToSkill(EQ::skills::SkillType skill, int32 spell_id, int proc_type, int aa_id=0); bool PassLimitClass(uint32 Classes_, uint16 Class_); void TryDefensiveProc(Mob *on, uint16 hand = EQ::invslot::slotPrimary); void TryWeaponProc(const EQ::ItemInstance* inst, const EQ::ItemData* weapon, Mob *on, uint16 hand = EQ::invslot::slotPrimary); void TrySpellProc(const EQ::ItemInstance* inst, const EQ::ItemData* weapon, Mob *on, uint16 hand = EQ::invslot::slotPrimary); - void TryWeaponProc(const EQ::ItemInstance* weapon, Mob *on, uint16 hand = EQ::invslot::slotPrimary); + void TryCombatProcs(const EQ::ItemInstance* weapon, Mob *on, uint16 hand = EQ::invslot::slotPrimary, const EQ::ItemData* weapon_data = nullptr); void ExecWeaponProc(const EQ::ItemInstance* weapon, uint16 spell_id, Mob *on, int level_override = -1); virtual float GetProcChances(float ProcBonus, uint16 hand = EQ::invslot::slotPrimary); virtual float GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 hand = EQ::invslot::slotPrimary, Mob *on = nullptr); diff --git a/zone/pets.cpp b/zone/pets.cpp index 8aeb1f5f2..d14989f73 100644 --- a/zone/pets.cpp +++ b/zone/pets.cpp @@ -580,13 +580,13 @@ void NPC::SetPetState(SpellBuff_Struct *pet_buffs, uint32 *items) { case SE_WeaponProc: // We need to reapply buff based procs // We need to do this here so suspended pets also regain their procs. - AddProcToWeapon(GetProcID(buffs[j1].spellid,x1), false, 100+spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, buffs[j1].casterlevel, GetProcLimitTimer(buffs[j1].spellid, SE_WeaponProc)); + AddProcToWeapon(GetProcID(buffs[j1].spellid,x1), false, 100+spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, buffs[j1].casterlevel, GetProcLimitTimer(buffs[j1].spellid, ProcType::MELEE_PROC)); break; case SE_DefensiveProc: - AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, GetProcLimitTimer(buffs[j1].spellid, SE_DefensiveProc)); + AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, GetProcLimitTimer(buffs[j1].spellid, ProcType::DEFENSIVE_PROC)); break; case SE_RangedProc: - AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, GetProcLimitTimer(buffs[j1].spellid, SE_RangedProc)); + AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, GetProcLimitTimer(buffs[j1].spellid, ProcType::RANGED_PROC)); break; case SE_Charm: case SE_Rune: diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index f6003a8d2..a8c6f932d 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -196,12 +196,12 @@ void Mob::DoSpecialAttackDamage(Mob *who, EQ::skills::SkillType skill, int32 bas DoAttack(who, my_hit); who->AddToHateList(this, hate, 0); - if (my_hit.damage_done > 0 && aabonuses.SkillAttackProc[SBIndex::SKILLPROC_CHANCE] && aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SKILL] == skill && - IsValidSpell(aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID])) { - float chance = aabonuses.SkillAttackProc[SBIndex::SKILLPROC_CHANCE] / 1000.0f; + if (my_hit.damage_done > 0 && aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] && aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SKILL] == skill && + IsValidSpell(aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID])) { + float chance = aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] / 1000.0f; if (zone->random.Roll(chance)) - SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID], who, EQ::spells::CastingSlot::Item, 0, -1, - spells[aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID]].resist_difficulty); + SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID], who, EQ::spells::CastingSlot::Item, 0, -1, + spells[aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID]].resist_difficulty); } who->Damage(this, my_hit.damage_done, SPELL_UNKNOWN, skill, false); @@ -212,11 +212,12 @@ void Mob::DoSpecialAttackDamage(Mob *who, EQ::skills::SkillType skill, int32 bas if (HasDied()) return; - if (HasSkillProcs()) + if (HasSkillProcs()) { TrySkillProc(who, skill, ReuseTime * 1000); - - if (my_hit.damage_done > 0 && HasSkillProcSuccess()) + } + if (my_hit.damage_done > 0 && HasSkillProcSuccess()) { TrySkillProc(who, skill, ReuseTime * 1000, true); + } } // We should probably refactor this to take the struct not the packet @@ -798,7 +799,7 @@ void Mob::DoArcheryAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, co const EQ::ItemInstance *_RangeWeapon = nullptr; const EQ::ItemInstance *_Ammo = nullptr; - const EQ::ItemData *ammo_lost = nullptr; + const EQ::ItemData *last_ammo_used = nullptr; /* If LaunchProjectile is false this function will do archery damage on target, @@ -822,7 +823,7 @@ void Mob::DoArcheryAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, co if (!RangeWeapon && !Ammo && range_id && ammo_id) { if (IsClient()) { _RangeWeapon = CastToClient()->m_inv[EQ::invslot::slotRange]; - if (_RangeWeapon && _RangeWeapon->GetItem() && + if (_RangeWeapon && _RangeWeapon->GetItem() && _RangeWeapon->GetItem()->ID == range_id) RangeWeapon = _RangeWeapon; @@ -830,7 +831,7 @@ void Mob::DoArcheryAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, co if (_Ammo && _Ammo->GetItem() && _Ammo->GetItem()->ID == ammo_id) Ammo = _Ammo; else - ammo_lost = database.GetItem(ammo_id); + last_ammo_used = database.GetItem(ammo_id); } } } @@ -900,34 +901,39 @@ void Mob::DoArcheryAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, co other->Damage(this, TotalDmg, SPELL_UNKNOWN, EQ::skills::SkillArchery); - // Skill Proc Success - if (TotalDmg > 0 && HasSkillProcSuccess() && other && !other->HasDied()) { - if (ReuseTime) - TrySkillProc(other, EQ::skills::SkillArchery, ReuseTime); - else - TrySkillProc(other, EQ::skills::SkillArchery, 0, true, EQ::invslot::slotRange); - } - // end of old fuck - if (LaunchProjectile) - return; // Shouldn't reach this point durring initial launch phase, but just in case. // Weapon Proc - if (RangeWeapon && other && !other->HasDied()) - TryWeaponProc(RangeWeapon, other, EQ::invslot::slotRange); + if (RangeWeapon && other && !other->HasDied()) { + TryCombatProcs(RangeWeapon, other, EQ::invslot::slotRange); + } - // Ammo Proc - if (ammo_lost) - TryWeaponProc(nullptr, ammo_lost, other, EQ::invslot::slotRange); - else if (Ammo && other && !other->HasDied()) - TryWeaponProc(Ammo, other, EQ::invslot::slotRange); + // Ammo Proc, do not try spell procs if from ammo. + if (last_ammo_used) { + TryWeaponProc(nullptr, last_ammo_used, other, EQ::invslot::slotRange); + } + else if (Ammo && other && !other->HasDied()) { + TryWeaponProc(Ammo, Ammo->GetItem(), other, EQ::invslot::slotRange); + } - // Skill Proc + // Skill Proc Attempt if (HasSkillProcs() && other && !other->HasDied()) { - if (ReuseTime) + if (ReuseTime) { TrySkillProc(other, EQ::skills::SkillArchery, ReuseTime); - else + } + else { TrySkillProc(other, EQ::skills::SkillArchery, 0, false, EQ::invslot::slotRange); + } + } + + // Skill Proc Success ... can proc off hits OR misses + if (HasSkillProcSuccess() && other && !other->HasDied()) { + if (ReuseTime) { + TrySkillProc(other, EQ::skills::SkillArchery, ReuseTime, true); + } + else { + TrySkillProc(other, EQ::skills::SkillArchery, 0, true, EQ::invslot::slotRange); + } } } @@ -991,7 +997,7 @@ bool Mob::TryProjectileAttack(Mob *other, const EQ::ItemData *item, EQ::skills:: if (Ammo && Ammo->GetItem()) ProjectileAtk[slot].ammo_id = Ammo->GetItem()->ID; - ProjectileAtk[slot].ammo_slot = 0; + ProjectileAtk[slot].ammo_slot = AmmoSlot; ProjectileAtk[slot].skill = skillInUse; ProjectileAtk[slot].speed_mod = speed; @@ -1268,15 +1274,18 @@ void NPC::DoRangedAttackDmg(Mob* other, bool Launch, int16 damage_mod, int16 cha other->Damage(this, TotalDmg, SPELL_UNKNOWN, skillInUse); - if (TotalDmg > 0 && HasSkillProcSuccess() && !other->HasDied()) - TrySkillProc(other, skillInUse, 0, true, EQ::invslot::slotRange); - //try proc on hits and misses - if(other && !other->HasDied()) + if (other && !other->HasDied()) { TrySpellProc(nullptr, (const EQ::ItemData*)nullptr, other, EQ::invslot::slotRange); + } - if (HasSkillProcs() && other && !other->HasDied()) + if (HasSkillProcs() && other && !other->HasDied()) { TrySkillProc(other, skillInUse, 0, false, EQ::invslot::slotRange); + } + + if (HasSkillProcSuccess() && other && !other->HasDied()) { + TrySkillProc(other, skillInUse, 0, true, EQ::invslot::slotRange); + } } void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51 @@ -1294,7 +1303,7 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51 int ammo_slot = EQ::invslot::slotRange; const EQ::ItemInstance* RangeWeapon = m_inv[EQ::invslot::slotRange]; - + if (!RangeWeapon || !RangeWeapon->IsClassCommon()) { LogCombat("Ranged attack canceled. Missing or invalid ranged weapon ([{}]) in slot [{}]", GetItemIDAt(EQ::invslot::slotRange), EQ::invslot::slotRange); Message(0, "Error: Rangeweapon: GetItem(%i)==0, you have nothing to throw!", GetItemIDAt(EQ::invslot::slotRange)); @@ -1355,7 +1364,7 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51 return; } - DoThrowingAttackDmg(other, RangeWeapon, item); + DoThrowingAttackDmg(other, RangeWeapon, item, 0, 0, 0, 0, 0,ammo_slot); // Consume Ammo, unless Ammo Consumption is disabled if (RuleB(Combat, ThrowingConsumesAmmo)) { @@ -1378,8 +1387,8 @@ void Mob::DoThrowingAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, c return; } - const EQ::ItemInstance *_RangeWeapon = nullptr; - const EQ::ItemData *ammo_lost = nullptr; + const EQ::ItemInstance *m_RangeWeapon = nullptr;//throwing weapon + const EQ::ItemData *last_ammo_used = nullptr; /* If LaunchProjectile is false this function will do archery damage on target, @@ -1394,12 +1403,14 @@ void Mob::DoThrowingAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, c } else { if (!RangeWeapon && range_id) { if (IsClient()) { - _RangeWeapon = CastToClient()->m_inv[AmmoSlot]; - if (_RangeWeapon && _RangeWeapon->GetItem() && - _RangeWeapon->GetItem()->ID != range_id) - RangeWeapon = _RangeWeapon; - else - ammo_lost = database.GetItem(range_id); + m_RangeWeapon = CastToClient()->m_inv[AmmoSlot]; + + if (m_RangeWeapon && m_RangeWeapon->GetItem() && m_RangeWeapon->GetItem()->ID == range_id) { + RangeWeapon = m_RangeWeapon; + } + else { + last_ammo_used = database.GetItem(range_id); + } } } } @@ -1412,10 +1423,12 @@ void Mob::DoThrowingAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, c int WDmg = 0; if (!weapon_damage) { - if (IsClient() && RangeWeapon) + if (IsClient() && RangeWeapon) { WDmg = GetWeaponDamage(other, RangeWeapon); - else if (AmmoItem) + } + else if (AmmoItem) { WDmg = GetWeaponDamage(other, AmmoItem); + } if (LaunchProjectile) { TryProjectileAttack(other, AmmoItem, EQ::skills::SkillThrowing, WDmg, RangeWeapon, @@ -1426,8 +1439,9 @@ void Mob::DoThrowingAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, c WDmg = weapon_damage; } - if (focus) // From FcBaseEffects + if (focus) { // no longer used, keep for quests WDmg += WDmg * focus / 100; + } int TotalDmg = 0; @@ -1455,29 +1469,28 @@ void Mob::DoThrowingAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, c other->Damage(this, TotalDmg, SPELL_UNKNOWN, EQ::skills::SkillThrowing); - if (TotalDmg > 0 && HasSkillProcSuccess() && other && !other->HasDied()) { - if (ReuseTime) - TrySkillProc(other, EQ::skills::SkillThrowing, ReuseTime); - else - TrySkillProc(other, EQ::skills::SkillThrowing, 0, true, EQ::invslot::slotRange); + if (other && !other->HasDied()) { + TryCombatProcs(RangeWeapon, other, EQ::invslot::slotRange, last_ammo_used); } - // end old shit - - if (LaunchProjectile) - return; - - // Throwing item Proc - if (ammo_lost) - TryWeaponProc(nullptr, ammo_lost, other, EQ::invslot::slotRange); - else if (RangeWeapon && other && !other->HasDied()) - TryWeaponProc(RangeWeapon, other, EQ::invslot::slotRange); if (HasSkillProcs() && other && !other->HasDied()) { - if (ReuseTime) + if (ReuseTime) { TrySkillProc(other, EQ::skills::SkillThrowing, ReuseTime); - else + } + else { TrySkillProc(other, EQ::skills::SkillThrowing, 0, false, EQ::invslot::slotRange); + } } + + if (HasSkillProcSuccess() && other && !other->HasDied()) { + if (ReuseTime) { + TrySkillProc(other, EQ::skills::SkillThrowing, ReuseTime, true); + } + else { + TrySkillProc(other, EQ::skills::SkillThrowing, 0, true, EQ::invslot::slotRange); + } + } + if (IsClient()) { CastToClient()->CheckIncreaseSkill(EQ::skills::SkillThrowing, GetTarget()); } @@ -1965,7 +1978,7 @@ void Mob::Taunt(NPC *who, bool always_succeed, int chance_bonus, bool FromSpell, Mob *hate_top = who->GetHateMost(); int level_difference = GetLevel() - who->GetLevel(); - bool Success = false; + bool success = false; // Support for how taunt worked pre 2000 on LIVE - Can not taunt NPC over your level. if ((RuleB(Combat, TauntOverLevel) == false) && (level_difference < 0) || @@ -1978,7 +1991,7 @@ void Mob::Taunt(NPC *who, bool always_succeed, int chance_bonus, bool FromSpell, if ((hate_top && hate_top->GetHPRatio() >= 20) || hate_top == nullptr || chance_bonus) { // SE_Taunt this is flat chance if (chance_bonus) { - Success = zone->random.Roll(chance_bonus); + success = zone->random.Roll(chance_bonus); } else { float tauntchance = 50.0f; @@ -2012,14 +2025,14 @@ void Mob::Taunt(NPC *who, bool always_succeed, int chance_bonus, bool FromSpell, tauntchance /= 100.0f; - Success = tauntchance > zone->random.Real(0, 1); + success = tauntchance > zone->random.Real(0, 1); } - if (Success) { + if (success) { if (hate_top && hate_top != this) { int newhate = (who->GetNPCHate(hate_top) - who->GetNPCHate(this)) + 1 + bonus_hate; who->CastToNPC()->AddToHateList(this, newhate); - Success = true; + success = true; } else { who->CastToNPC()->AddToHateList(this, 12); } @@ -2033,11 +2046,13 @@ void Mob::Taunt(NPC *who, bool always_succeed, int chance_bonus, bool FromSpell, MessageString(Chat::SpellFailure, FAILED_TAUNT); } - if (HasSkillProcs()) + if (HasSkillProcs()) { TrySkillProc(who, EQ::skills::SkillTaunt, TauntReuseTime * 1000); + } - if (Success && HasSkillProcSuccess()) + if (success && HasSkillProcSuccess()) { TrySkillProc(who, EQ::skills::SkillTaunt, TauntReuseTime * 1000, true); + } } void Mob::InstillDoubt(Mob *who) { @@ -2061,6 +2076,7 @@ void Mob::InstillDoubt(Mob *who) { //I think this formula needs work int value = 0; + bool success = false; //user's bonus value += GetSkill(EQ::skills::SkillIntimidation) + GetCHA() / 4; @@ -2073,6 +2089,7 @@ void Mob::InstillDoubt(Mob *who) { //cast fear on them... should prolly be a different spell //and should be un-resistable. SpellOnTarget(229, who, 0, true, -2000); + success = true; //is there a success message? } else { MessageString(Chat::LightBlue,NOT_SCARING); @@ -2083,6 +2100,14 @@ void Mob::InstillDoubt(Mob *who) { entity_list.AEAttack(target, 50); }*/ } + + if (HasSkillProcs()) { + TrySkillProc(who, EQ::skills::SkillIntimidation, InstillDoubtReuseTime * 1000); + } + + if (success && HasSkillProcSuccess()) { + TrySkillProc(who, EQ::skills::SkillIntimidation, InstillDoubtReuseTime * 1000, true); + } } int Mob::TryHeadShot(Mob *defender, EQ::skills::SkillType skillInUse) @@ -2228,12 +2253,12 @@ void Mob::DoMeleeSkillAttackDmg(Mob *other, uint16 weapon_damage, EQ::skills::Sk } other->AddToHateList(this, hate, 0); - if (damage > 0 && aabonuses.SkillAttackProc[SBIndex::SKILLPROC_CHANCE] && aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SKILL] == skillinuse && - IsValidSpell(aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID])) { - float chance = aabonuses.SkillAttackProc[SBIndex::SKILLPROC_CHANCE] / 1000.0f; + if (damage > 0 && aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] && aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SKILL] == skillinuse && + IsValidSpell(aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID])) { + float chance = aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] / 1000.0f; if (zone->random.Roll(chance)) - SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID], other, EQ::spells::CastingSlot::Item, 0, -1, - spells[aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID]].resist_difficulty); + SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID], other, EQ::spells::CastingSlot::Item, 0, -1, + spells[aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID]].resist_difficulty); } other->Damage(this, damage, SPELL_UNKNOWN, skillinuse); @@ -2241,11 +2266,13 @@ void Mob::DoMeleeSkillAttackDmg(Mob *other, uint16 weapon_damage, EQ::skills::Sk if (HasDied()) return; - if (CanSkillProc && HasSkillProcs()) + if (CanSkillProc && HasSkillProcs()) { TrySkillProc(other, skillinuse, ReuseTime); + } - if (CanSkillProc && (damage > 0) && HasSkillProcSuccess()) + if (CanSkillProc && (damage > 0) && HasSkillProcSuccess()) { TrySkillProc(other, skillinuse, ReuseTime, true); + } } bool Mob::CanDoSpecialAttack(Mob *other) { diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 9b7e74d82..782fd5d91 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1821,7 +1821,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Weapon Proc: %s (id %d)", spells[effect_value].name, procid); #endif - AddProcToWeapon(procid, false, 100 + spells[spell_id].limit_value[i], spell_id, caster_level, GetProcLimitTimer(spell_id, SE_WeaponProc)); + AddProcToWeapon(procid, false, 100 + spells[spell_id].limit_value[i], spell_id, caster_level, GetProcLimitTimer(spell_id, ProcType::MELEE_PROC)); break; } @@ -1831,7 +1831,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Ranged Proc: %+i", effect_value); #endif - AddRangedProc(procid, 100 + spells[spell_id].limit_value[i], spell_id, GetProcLimitTimer(spell_id, SE_RangedProc)); + AddRangedProc(procid, 100 + spells[spell_id].limit_value[i], spell_id, GetProcLimitTimer(spell_id, ProcType::RANGED_PROC)); break; } @@ -1841,7 +1841,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Defensive Proc: %s (id %d)", spells[effect_value].name, procid); #endif - AddDefensiveProc(procid, 100 + spells[spell_id].limit_value[i], spell_id, GetProcLimitTimer(spell_id, SE_DefensiveProc)); + AddDefensiveProc(procid, 100 + spells[spell_id].limit_value[i], spell_id, GetProcLimitTimer(spell_id, ProcType::DEFENSIVE_PROC)); break; } @@ -3156,7 +3156,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_LimitSpellClass: case SE_Sanctuary: case SE_PetMeleeMitigation: - case SE_SkillProc: + case SE_SkillProcAttempt: case SE_SkillProcSuccess: case SE_SpellResistReduction: case SE_Duration_HP_Pct: @@ -8518,6 +8518,87 @@ bool Mob::PassCharmTargetRestriction(Mob *target) { return true; } +bool Mob::PassLimitToSkill(EQ::skills::SkillType skill, int32 spell_id, int proc_type, int aa_id) +{ + /* + Check if SE_AddMeleProc or SE_RangedProc have a skill limiter. Passes automatically if no skill limiters present. + */ + int32 proc_type_spaid = 0; + if (proc_type == ProcType::MELEE_PROC) { + proc_type_spaid = SE_AddMeleeProc; + } + if (proc_type == ProcType::RANGED_PROC) { + proc_type_spaid = SE_RangedProc; + } + + bool match_proc_type = false; + bool has_limit_check = false; + + if (!aa_id && spellbonuses.LimitToSkill[EQ::skills::HIGHEST_SKILL + 3]) { + + if (spell_id == SPELL_UNKNOWN) { + return false; + } + + for (int i = 0; i < EFFECT_COUNT; i++) { + if (spells[spell_id].effect_id[i] == proc_type_spaid) { + match_proc_type = true; + } + if (match_proc_type && spells[spell_id].effect_id[i] == SE_LimitToSkill && spells[spell_id].base_value[i] <= EQ::skills::HIGHEST_SKILL) { + + has_limit_check = true; + if (spells[spell_id].base_value[i] == skill) { + return true; + } + } + } + } + else if (aabonuses.LimitToSkill[EQ::skills::HIGHEST_SKILL + 3]) { + + int rank_id = 1; + AA::Rank *rank = zone->GetAlternateAdvancementRank(aa_id); + + if (!rank) { + return true; + } + + AA::Ability *ability_in = rank->base_ability; + if (!ability_in) { + return true; + } + + for (auto &aa : aa_ranks) { + auto ability_rank = zone->GetAlternateAdvancementAbilityAndRank(aa.first, aa.second.first); + auto ability = ability_rank.first; + auto rank = ability_rank.second; + + if (!ability) { + continue; + } + + for (auto &effect : rank->effects) { + if (effect.effect_id == proc_type_spaid) { + match_proc_type = true; + } + + if (match_proc_type && effect.effect_id == SE_LimitToSkill && effect.base_value <= EQ::skills::HIGHEST_SKILL) { + has_limit_check = true; + if (effect.base_value == skill) { + return true; + } + } + } + } + } + + if (has_limit_check) { + return false; //Limit was found, but not matched, fail. + } + else { + return true; //No limit is present, automatically pass. + } +} + bool Mob::CanFocusUseRandomEffectivenessByType(focusType type) { switch (type) { @@ -8760,7 +8841,7 @@ bool Mob::IsProcLimitTimerActive(int32 base_spell_id, uint32 proc_reuse_time, in for (int i = 0; i < MAX_PROC_LIMIT_TIMERS; i++) { - if (proc_type == SE_WeaponProc) { + if (proc_type == ProcType::MELEE_PROC) { if (spell_proclimit_spellid[i] == base_spell_id) { if (spell_proclimit_timer[i].Enabled()) { if (spell_proclimit_timer[i].GetRemainingTime() > 0) { @@ -8773,7 +8854,7 @@ bool Mob::IsProcLimitTimerActive(int32 base_spell_id, uint32 proc_reuse_time, in } } } - else if (proc_type == SE_RangedProc) { + else if (proc_type == ProcType::RANGED_PROC) { if (ranged_proclimit_spellid[i] == base_spell_id) { if (ranged_proclimit_timer[i].Enabled()) { if (ranged_proclimit_timer[i].GetRemainingTime() > 0) { @@ -8786,7 +8867,7 @@ bool Mob::IsProcLimitTimerActive(int32 base_spell_id, uint32 proc_reuse_time, in } } } - else if (proc_type == SE_DefensiveProc) { + else if (proc_type == ProcType::DEFENSIVE_PROC) { if (def_proclimit_spellid[i] == base_spell_id) { if (def_proclimit_timer[i].Enabled()) { if (def_proclimit_timer[i].GetRemainingTime() > 0) { @@ -8813,7 +8894,7 @@ void Mob::SetProcLimitTimer(int32 base_spell_id, uint32 proc_reuse_time, int pro for (int i = 0; i < MAX_PROC_LIMIT_TIMERS; i++) { - if (proc_type == SE_WeaponProc) { + if (proc_type == ProcType::MELEE_PROC) { if (!spell_proclimit_spellid[i] && !is_set) { spell_proclimit_spellid[i] = base_spell_id; spell_proclimit_timer[i].SetTimer(proc_reuse_time); @@ -8825,7 +8906,7 @@ void Mob::SetProcLimitTimer(int32 base_spell_id, uint32 proc_reuse_time, int pro } } - if (proc_type == SE_RangedProc) { + if (proc_type == ProcType::RANGED_PROC) { if (!ranged_proclimit_spellid[i] && !is_set) { ranged_proclimit_spellid[i] = base_spell_id; ranged_proclimit_timer[i].SetTimer(proc_reuse_time); @@ -8837,7 +8918,7 @@ void Mob::SetProcLimitTimer(int32 base_spell_id, uint32 proc_reuse_time, int pro } } - if (proc_type == SE_DefensiveProc) { + if (proc_type == ProcType::DEFENSIVE_PROC) { if (!def_proclimit_spellid[i] && !is_set) { def_proclimit_spellid[i] = base_spell_id; def_proclimit_timer[i].SetTimer(proc_reuse_time); diff --git a/zone/tune.cpp b/zone/tune.cpp index be806eb92..a5bf04fed 100644 --- a/zone/tune.cpp +++ b/zone/tune.cpp @@ -1014,8 +1014,6 @@ void Mob::TuneMeleeMitigation(Mob *attacker, DamageHitInfo &hit, int ac_override // +0.5 for rounding, min to 1 dmg hit.damage_done = std::max(static_cast(roll * static_cast(hit.base_damage) + 0.5), 1); - - //Shout("mitigation %d vs offense %d. base %d rolled %f damage %d", mitigation, hit.offense, hit.base_damage, roll, hit.damage_done); } int Mob::TuneACSum(bool skip_caps, int ac_override, int add_ac) @@ -1093,10 +1091,6 @@ int Mob::TuneACSum(bool skip_caps, int ac_override, int add_ac) auto over_cap = ac - softcap; ac = softcap + (over_cap * returns); } - //Shout("ACSum ac %i softcap %i returns %.2f", ac, softcap, static_cast(returns)); - } - else { - //Shout("ACSum ac %i", ac); } return ac; @@ -1384,7 +1378,6 @@ bool Mob::TuneCheckHitChance(Mob* other, DamageHitInfo &hit, int avoidance_overr Mob *attacker = other; Mob *defender = this; - //Shout("CheckHitChance(%s) attacked by %s", defender->GetName(), attacker->GetName()); if (defender->IsClient() && defender->CastToClient()->IsSitting()) return true; @@ -1402,7 +1395,6 @@ bool Mob::TuneCheckHitChance(Mob* other, DamageHitInfo &hit, int avoidance_overr // Then your chance to simply avoid the attack is checked (defender's avoidance roll beat the attacker's accuracy roll.) int tohit_roll = zone->random.Roll0(accuracy); int avoid_roll = zone->random.Roll0(avoidance); - //Shout("CheckHitChance accuracy(%d => %d) avoidance(%d => %d)", accuracy, tohit_roll, avoidance, avoid_roll); // tie breaker? Don't want to be biased any one way if (tohit_roll == avoid_roll) From feed584a4173ad977f07f3dc0433183418ec5a9e Mon Sep 17 00:00:00 2001 From: mmcgarvey Date: Tue, 14 Dec 2021 13:57:35 -0500 Subject: [PATCH 519/624] [Database] Escape reserved mysql keyword rank w/ backticks (#1862) Fixes #1567 --- common/database.cpp | 2 +- common/database_conversions.cpp | 2 +- common/guild_base.cpp | 10 +++++----- zone/zonedb.cpp | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index 9a0ea375d..76673a31b 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -716,7 +716,7 @@ bool Database::SaveCharacterCreate(uint32 character_id, uint32 account_id, Playe /* HoTT Ability */ if(RuleB(Character, GrantHoTTOnCreate)) { - query = StringFormat("INSERT INTO `character_leadership_abilities` (id, slot, rank) VALUES (%u, %i, %i)", character_id, 14, 1); + query = StringFormat("INSERT INTO `character_leadership_abilities` (id, slot, `rank`) VALUES (%u, %i, %i)", character_id, 14, 1); results = QueryDatabase(query); } diff --git a/common/database_conversions.cpp b/common/database_conversions.cpp index 561e1679b..4ffe058e8 100644 --- a/common/database_conversions.cpp +++ b/common/database_conversions.cpp @@ -1452,7 +1452,7 @@ bool Database::CheckDatabaseConvertPPDeblob(){ for (i = 0; i < MAX_LEADERSHIP_AA_ARRAY; i++){ if (pp->leader_abilities.ranks[i] > 0 && pp->leader_abilities.ranks[i] < 6){ if (first_entry != 1){ - rquery = StringFormat("REPLACE INTO `character_leadership_abilities` (id, slot, rank) VALUES (%i, %u, %u)", character_id, i, pp->leader_abilities.ranks[i]); + rquery = StringFormat("REPLACE INTO `character_leadership_abilities` (id, slot, `rank`) VALUES (%i, %u, %u)", character_id, i, pp->leader_abilities.ranks[i]); first_entry = 1; } rquery = rquery + StringFormat(", (%i, %u, %u)", character_id, i, pp->leader_abilities.ranks[i]); diff --git a/common/guild_base.cpp b/common/guild_base.cpp index 198dd1c5a..f56cd3deb 100644 --- a/common/guild_base.cpp +++ b/common/guild_base.cpp @@ -63,7 +63,7 @@ bool BaseGuildManager::LoadGuilds() { for (auto row=results.begin();row!=results.end();++row) _CreateGuild(atoi(row[0]), row[1], atoi(row[2]), atoi(row[3]), row[4], row[5], row[6], row[7]); - query = "SELECT guild_id,rank,title,can_hear,can_speak,can_invite,can_remove,can_promote,can_demote,can_motd,can_warpeace FROM guild_ranks"; + query = "SELECT guild_id,`rank`,title,can_hear,can_speak,can_invite,can_remove,can_promote,can_demote,can_motd,can_warpeace FROM guild_ranks"; results = m_db->QueryDatabase(query); if (!results.Success()) @@ -131,7 +131,7 @@ bool BaseGuildManager::RefreshGuild(uint32 guild_id) { info = _CreateGuild(guild_id, row[0], atoi(row[1]), atoi(row[2]), row[3], row[4], row[5], row[6]); - query = StringFormat("SELECT guild_id, rank, title, can_hear, can_speak, can_invite, can_remove, can_promote, can_demote, can_motd, can_warpeace " + query = StringFormat("SELECT guild_id, `rank`, title, can_hear, can_speak, can_invite, can_remove, can_promote, can_demote, can_motd, can_warpeace " "FROM guild_ranks WHERE guild_id=%lu", (unsigned long)guild_id); results = m_db->QueryDatabase(query); @@ -268,7 +268,7 @@ bool BaseGuildManager::_StoreGuildDB(uint32 guild_id) { m_db->DoEscapeString(title_esc, rankInfo.name.c_str(), rankInfo.name.length()); query = StringFormat("INSERT INTO guild_ranks " - "(guild_id,rank,title,can_hear,can_speak,can_invite,can_remove,can_promote,can_demote,can_motd,can_warpeace)" + "(guild_id,`rank`,title,can_hear,can_speak,can_invite,can_remove,can_promote,can_demote,can_motd,can_warpeace)" " VALUES(%d,%d,'%s',%d,%d,%d,%d,%d,%d,%d,%d)", guild_id, rank, title_esc, rankInfo.permissions[GUILD_HEAR], @@ -738,7 +738,7 @@ bool BaseGuildManager::DBSetGuild(uint32 charid, uint32 guild_id, uint8 rank) { std::string query; if(guild_id != GUILD_NONE) { - query = StringFormat("REPLACE INTO guild_members (char_id,guild_id,rank,public_note) VALUES(%d,%d,%d,'')", charid, guild_id, rank); + query = StringFormat("REPLACE INTO guild_members (char_id,guild_id,`rank`,public_note) VALUES(%d,%d,%d,'')", charid, guild_id, rank); auto results = m_db->QueryDatabase(query); if (!results.Success()) { @@ -758,7 +758,7 @@ bool BaseGuildManager::DBSetGuild(uint32 charid, uint32 guild_id, uint8 rank) { } bool BaseGuildManager::DBSetGuildRank(uint32 charid, uint8 rank) { - std::string query = StringFormat("UPDATE guild_members SET rank=%d WHERE char_id=%d", rank, charid); + std::string query = StringFormat("UPDATE guild_members SET `rank`=%d WHERE char_id=%d", rank, charid); return(QueryWithLogging(query, "setting a guild member's rank")); } diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index df0cfb98d..12bcb378d 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1260,7 +1260,7 @@ bool ZoneDatabase::LoadCharacterLanguages(uint32 character_id, PlayerProfile_Str } bool ZoneDatabase::LoadCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp){ - std::string query = StringFormat("SELECT slot, rank FROM character_leadership_abilities WHERE `id` = %u", character_id); + std::string query = StringFormat("SELECT slot, `rank` FROM character_leadership_abilities WHERE `id` = %u", character_id); auto results = database.QueryDatabase(query); uint32 slot = 0; for (auto row = results.begin(); row != results.end(); ++row) { slot = atoi(row[0]); @@ -1562,7 +1562,7 @@ bool ZoneDatabase::SaveCharacterLeadershipAA(uint32 character_id, PlayerProfile_ for (int i = 0; i < MAX_LEADERSHIP_AA_ARRAY; i++){ if (pp->leader_abilities.ranks[i] > 0){ if (first_entry != 1){ - query = StringFormat("REPLACE INTO `character_leadership_abilities` (id, slot, rank) VALUES (%i, %u, %u)", character_id, i, pp->leader_abilities.ranks[i]); + query = StringFormat("REPLACE INTO `character_leadership_abilities` (id, slot, `rank`) VALUES (%i, %u, %u)", character_id, i, pp->leader_abilities.ranks[i]); first_entry = 1; } query = query + StringFormat(", (%i, %u, %u)", character_id, i, pp->leader_abilities.ranks[i]); From 3414d3a1aef6853a05369e1176aefdc08c0472d1 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 15 Dec 2021 13:17:15 -0500 Subject: [PATCH 520/624] fearstun update (#1889) --- zone/spell_effects.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 782fd5d91..c49662802 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2883,8 +2883,15 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove caster->MessageString(Chat::SpellFailure, IMMUNE_FEAR); break; } + int max_level = 0; + if (spells[spell_id].max_value[i] >= 1000) { + max_level = spells[spell_id].max_value[i] - 1000; + } + else { + max_level = caster->GetLevel() + spells[spell_id].max_value[i]; + } - if (spells[spell_id].max_value[i] == 0 || GetLevel() <= spells[spell_id].max_value[i]) { + if (spells[spell_id].max_value[i] == 0 || GetLevel() <= max_level) { if (IsClient() && spells[spell_id].limit_value[i]) Stun(spells[spell_id].limit_value[i]); else From fbc5d045de4759041f72749c7835bf46437bc925 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Wed, 15 Dec 2021 13:26:31 -0500 Subject: [PATCH 521/624] [Doors] Add new rule enabling classic "key on cursor" for pre keyring keys (#1869) --- common/ruletypes.h | 4 ++++ zone/doors.cpp | 16 +++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 8b60ddb71..de97a3183 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -808,6 +808,10 @@ RULE_CATEGORY(Command) RULE_BOOL(Command, DyeCommandRequiresDyes, false, "Enable this to require a Prismatic Dye (32557) each time someone uses #dye.") RULE_CATEGORY_END() +RULE_CATEGORY(Doors) +RULE_BOOL(Doors, RequireKeyOnCursor, false, "Enable this to require pre-keyring keys to be on player cursor to open doors.") +RULE_CATEGORY_END() + #undef RULE_CATEGORY #undef RULE_INT #undef RULE_REAL diff --git a/zone/doors.cpp b/zone/doors.cpp index 6b505c677..35eb92d5c 100644 --- a/zone/doors.cpp +++ b/zone/doors.cpp @@ -211,13 +211,23 @@ void Doors::HandleClick(Client* sender, uint8 trigger) { uint32 required_key_item = GetKeyItem(); uint8 disable_add_to_key_ring = GetNoKeyring(); - uint32 player_has_key = 0; + bool player_has_key = false; uint32 player_key = 0; const EQ::ItemInstance *lock_pick_item = sender->GetInv().GetItem(EQ::invslot::slotCursor); - player_has_key = static_cast(sender->GetInv().HasItem(required_key_item, 1)); - if (player_has_key != INVALID_INDEX) { + // If classic key on cursor rule, check for it, otherwise owning it ok. + if (RuleB(Doors, RequireKeyOnCursor)) { + if (lock_pick_item != nullptr && + lock_pick_item->GetItem()->ID == required_key_item) { + player_has_key = true; + } + } + else if (sender->GetInv().HasItem(required_key_item, 1) != INVALID_INDEX) { + player_has_key = true; + } + + if (player_has_key) { player_key = required_key_item; } From d460fb3db8a144c2d41e572024dae8a02ed51817 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 15 Dec 2021 22:00:34 -0500 Subject: [PATCH 522/624] [Spells] Update to SPA 440 SE_FinishingBlowMaxLevel limit value sets HP ratio for FB (#1890) * fb max level update * Update to SPA 440 SE_FinishingBlowMaxLevel limit value sets HP ratio for FB --- zone/attack.cpp | 9 +++++++-- zone/bonuses.cpp | 13 ++++++------- zone/common.h | 5 +++-- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index da4394d91..633383d17 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -4758,8 +4758,13 @@ void Mob::TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions * bool Mob::TryFinishingBlow(Mob *defender, int &damage) { - // base2 of FinishingBlowLvl is the HP limit (cur / max) * 1000, 10% is listed as 100 - if (defender && !defender->IsClient() && defender->GetHPRatio() < 10) { + float hp_limit = 10.0f; + auto fb_hp_limit = std::max({ aabonuses.FinishingBlowLvl[SBIndex::FINISHING_BLOW_LEVEL_HP_RATIO], spellbonuses.FinishingBlowLvl[SBIndex::FINISHING_BLOW_LEVEL_HP_RATIO], itembonuses.FinishingBlowLvl[SBIndex::FINISHING_BLOW_LEVEL_HP_RATIO] }); + + if (fb_hp_limit) { + hp_limit = fb_hp_limit/10.0f; + } + if (defender && !defender->IsClient() && defender->GetHPRatio() < hp_limit) { uint32 FB_Dmg = aabonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] + spellbonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] + itembonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_DMG]; diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 28b98d655..5684abfc4 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1349,7 +1349,7 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) // base1 = level, base2 = ??? (Set to 200 in AA data, possible proc rate mod?) if (newbon->FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] < base_value) { newbon->FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = base_value; - newbon->FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = limit_value; + newbon->FinishingBlowLvl[SBIndex::FINISHING_BLOW_LEVEL_HP_RATIO] = limit_value; } break; } @@ -3507,10 +3507,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_FinishingBlowLvl: { - //base1 = level, base2 = ??? (Set to 200 in AA data, possible proc rate mod?) if (new_bonus->FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] < effect_value){ - new_bonus->FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; - new_bonus->FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = limit_value; + new_bonus->FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; + new_bonus->FinishingBlowLvl[SBIndex::FINISHING_BLOW_LEVEL_HP_RATIO] = limit_value; } break; } @@ -5405,9 +5404,9 @@ void Mob::NegateSpellEffectBonuses(uint16 spell_id) if (negate_spellbonus) { spellbonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; } if (negate_aabonus) { aabonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; } if (negate_itembonus) { itembonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX] = effect_value; } - if (negate_spellbonus) { spellbonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; } - if (negate_aabonus) { aabonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; } - if (negate_itembonus) { itembonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_CHANCE_BONUS] = effect_value; } + if (negate_spellbonus) { spellbonuses.FinishingBlowLvl[SBIndex::FINISHING_BLOW_LEVEL_HP_RATIO] = effect_value; } + if (negate_aabonus) { aabonuses.FinishingBlowLvl[SBIndex::FINISHING_BLOW_LEVEL_HP_RATIO] = effect_value; } + if (negate_itembonus) { itembonuses.FinishingBlowLvl[SBIndex::FINISHING_BLOW_LEVEL_HP_RATIO] = effect_value; } break; case SE_Sanctuary: diff --git a/zone/common.h b/zone/common.h index 4998d3889..a853b564e 100644 --- a/zone/common.h +++ b/zone/common.h @@ -606,7 +606,7 @@ struct StatBonuses { int32 OffhandRiposteFail; // chance for opponent to fail riposte with offhand attack. int32 ItemATKCap; // Raise item attack cap int32 FinishingBlow[2]; // Chance to do a finishing blow for specified damage amount. - uint32 FinishingBlowLvl[2]; // Sets max level an NPC can be affected by FB. (base1 = lv, base2= ???) + uint32 FinishingBlowLvl[2]; // Sets max level an NPC can be affected by FB. (base1 = lv, base2= hit point ratio) int32 ShieldEquipDmgMod; // Increases weapon's base damage by base1 % when shield is equipped (indirectly increasing hate) bool TriggerOnCastRequirement; // Triggers off various different conditions defined as emum SpellRestrictions int8 StunBashChance; // chance to stun with bash. @@ -682,7 +682,8 @@ namespace SBIndex { constexpr uint16 FINISHING_EFFECT_PROC_CHANCE = 0; // SPA 278, 439, 217 constexpr uint16 FINISHING_EFFECT_DMG = 1; // SPA 278, 439, 217 constexpr uint16 FINISHING_EFFECT_LEVEL_MAX = 0; // SPA 440, 345, 346 - constexpr uint16 FINISHING_EFFECT_LEVEL_CHANCE_BONUS = 1; // SPA 440, 345, 346 + constexpr uint16 FINISHING_EFFECT_LEVEL_CHANCE_BONUS = 1; // SPA 345, 346 + constexpr uint16 FINISHING_BLOW_LEVEL_HP_RATIO = 1; // SPA 440 constexpr uint16 DOUBLE_MELEE_ROUND_CHANCE = 0; // SPA 471 constexpr uint16 DOUBLE_MELEE_ROUND_DMG_BONUS = 1; // SPA 471 constexpr uint16 REFLECT_CHANCE = 0; // SPA 158 From 898b1ea4d1b2656d4d7180f0eabc8a39397db2bc Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 16 Dec 2021 07:23:11 -0500 Subject: [PATCH 523/624] Update attack.cpp (#1892) --- zone/attack.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 633383d17..30b61f4a2 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -5286,28 +5286,31 @@ bool Mob::TryRootFadeByDamage(int buffslot, Mob* attacker) { - Root break chance values obtained from live parses. */ - if (!attacker || !spellbonuses.Root[SBIndex::ROOT_EXISTS] || spellbonuses.Root[SBIndex::ROOT_BUFFSLOT] < 0) + if (!attacker || !spellbonuses.Root[SBIndex::ROOT_EXISTS] || spellbonuses.Root[SBIndex::ROOT_BUFFSLOT] < 0) { return false; + } + + if (IsDetrimentalSpell(buffs[spellbonuses.Root[SBIndex::ROOT_BUFFSLOT]].spellid) && spellbonuses.Root[SBIndex::ROOT_BUFFSLOT] != buffslot) { - if (IsDetrimentalSpell(spellbonuses.Root[SBIndex::ROOT_BUFFSLOT]) && spellbonuses.Root[SBIndex::ROOT_BUFFSLOT] != buffslot) { int BreakChance = RuleI(Spells, RootBreakFromSpells); - BreakChance -= BreakChance * buffs[spellbonuses.Root[SBIndex::ROOT_BUFFSLOT]].RootBreakChance / 100; int level_diff = attacker->GetLevel() - GetLevel(); //Use baseline if level difference <= 1 (ie. If target is (1) level less than you, or equal or greater level) - if (level_diff == 2) + if (level_diff == 2) { BreakChance = (BreakChance * 80) / 100; //Decrease by 20%; - - else if (level_diff >= 3 && level_diff <= 20) + } + else if (level_diff >= 3 && level_diff <= 20) { BreakChance = (BreakChance * 60) / 100; //Decrease by 40%; - - else if (level_diff > 21) + } + else if (level_diff > 21) { BreakChance = (BreakChance * 20) / 100; //Decrease by 80%; + } - if (BreakChance < 1) + if (BreakChance < 1) { BreakChance = 1; + } if (zone->random.Roll(BreakChance)) { From 85971590c873863d864ff14c06881e0663bed97c Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Sun, 19 Dec 2021 15:17:04 -0600 Subject: [PATCH 524/624] Re-enable spellbar and reset Discipline timer when stopping casts in EVENT_CAST_BEGIN (#1891) --- zone/spells.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index f1ba4cfa4..7daabb156 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -307,7 +307,12 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, std::string export_string = fmt::format("{}", spell_id); if(IsClient()) { if (parse->EventPlayer(EVENT_CAST_BEGIN, CastToClient(), export_string, 0) != 0) { - return false; + if (IsDiscipline(spell_id)) { + CastToClient()->SendDisciplineTimer(spells[spell_id].timer_id, 0); + } else { + CastToClient()->SendSpellBarEnable(spell_id); + } + return(false); } } else if(IsNPC()) { parse->EventNPC(EVENT_CAST_BEGIN, CastToNPC(), nullptr, export_string, 0); From 886b321e66dfdbe6848e118482ef07e098f1e107 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Mon, 20 Dec 2021 09:47:32 -0500 Subject: [PATCH 525/624] [Spells] Rework of SPA 288 SE_SkillAttackProc (#1893) * start * updated 288 --- common/spdat.h | 3 +- zone/attack.cpp | 70 +++++++++++++++++++++++++++++++++------- zone/bonuses.cpp | 32 +++++++++++++++--- zone/common.h | 9 +++--- zone/mob.cpp | 2 ++ zone/mob.h | 3 +- zone/special_attacks.cpp | 28 +++++++--------- 7 files changed, 108 insertions(+), 39 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index cf7f7b21e..9358bde69 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -189,6 +189,7 @@ #define MAX_FOCUS_PROC_LIMIT_TIMERS 20 //Number of focus recast timers that can be going at same time (This is arbitrary) #define MAX_PROC_LIMIT_TIMERS 8 //Number of proc delay timers that can be going at same time, different proc types get their own timer array. (This is arbitrary) #define MAX_APPEARANCE_EFFECTS 20 //Up to 20 Appearance Effects can be saved to a mobs appearance effect array, these will be sent to other clients when they enter a zone (This is arbitrary) +#define MAX_CAST_ON_SKILL_USE 36; //Actual amount is MAX/3 const int Z_AGGRO=10; @@ -1004,7 +1005,7 @@ typedef enum { #define SE_NimbleEvasion 285 // *not implemented - base1 = 100 for max #define SE_FcDamageAmt 286 // implemented, @Fc, On Caster, spell damage mod flat amt, base: amt #define SE_SpellDurationIncByTic 287 // implemented, @Fc, SPA: 287, SE_SpellDurationIncByTic, On Caster, spell buff duration mod, base: tics -#define SE_SkillAttackProc 288 // implemented[AA] - Chance to proc spell on skill attack usage (ex. Dragon Punch) +#define SE_SkillAttackProc 288 // implemented, @Procs, chance to cast a spell when using a skill, base: chance, limit: skill, max: spellid, note: if used in AA the spell id is set in aa_ranks spell field, chance is calculated as 100% = value 1000. #define SE_CastOnFadeEffect 289 // implemented - Triggers only if fades after natural duration. #define SE_IncreaseRunSpeedCap 290 // implemented[AA] - increases run speed over the hard cap #define SE_Purify 291 // implemented, @Dispel, remove up specified amount of detiremental spells, base: amt removed, limit: none, max: none, Note: excluding charm, fear, resurrection, and revival sickness diff --git a/zone/attack.cpp b/zone/attack.cpp index 30b61f4a2..5568e6e15 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1603,29 +1603,26 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b /////////////////////////////////////////////////////////// ////// Send Attack Damage /////////////////////////////////////////////////////////// - if (my_hit.damage_done > 0 && aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] && aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SKILL] == my_hit.skill && - IsValidSpell(aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID])) { - float chance = aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] / 1000.0f; - if (zone->random.Roll(chance)) - SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID], other, EQ::spells::CastingSlot::Item, 0, -1, - spells[aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID]].resist_difficulty); - } other->Damage(this, my_hit.damage_done, SPELL_UNKNOWN, my_hit.skill, true, -1, false, m_specialattacks); - if (IsDead()) return false; + if (IsDead()) { + return false; + } MeleeLifeTap(my_hit.damage_done); CommonBreakInvisibleFromCombat(); - if (GetTarget()) + if (GetTarget()) { TriggerDefensiveProcs(other, Hand, true, my_hit.damage_done); + } - if (my_hit.damage_done > 0) + if (my_hit.damage_done > 0) { return true; - - else + } + else { return false; + } } //used by complete heal and #heal @@ -4489,6 +4486,8 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon, } } + TryCastOnSkillUse(on, skillinuse); + if (HasSkillProcs() && hand != EQ::invslot::slotRange) { //We check ranged skill procs within the attack functions. TrySkillProc(on, skillinuse, 0, false, hand); } @@ -5272,6 +5271,53 @@ float Mob::GetSkillProcChances(uint16 ReuseTime, uint16 hand) { return ProcChance; } +void Mob::TryCastOnSkillUse(Mob *on, EQ::skills::SkillType skill) { + + if (!spellbonuses.HasSkillAttackProc[skill] && !itembonuses.HasSkillAttackProc[skill] && !aabonuses.HasSkillAttackProc[skill]) { + return; + } + + if (!on) { + SetTarget(nullptr); + LogError("A null Mob object was passed to Mob::TryCastOnSkillUse for evaluation!"); + return; + } + + if (on->HasDied()) { + return; + } + + if (spellbonuses.HasSkillAttackProc[skill]) { + for (int i = 0; i < MAX_CAST_ON_SKILL_USE i += 3) { + if (spellbonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID] && skill == spellbonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SKILL]) { + if (IsValidSpell(spellbonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID]) && zone->random.Int(1, 1000) <= spellbonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_CHANCE]) { + SpellFinished(spellbonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID], on, EQ::spells::CastingSlot::Item, 0, -1, spells[spellbonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID]].resist_difficulty); + } + } + } + } + + if (itembonuses.HasSkillAttackProc[skill]) { + for (int i = 0; i < MAX_CAST_ON_SKILL_USE i += 3) { + if (itembonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID] && skill == itembonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SKILL]) { + if (IsValidSpell(itembonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID]) && zone->random.Int(1, 1000) <= spellbonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_CHANCE]) { + SpellFinished(itembonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID], on, EQ::spells::CastingSlot::Item, 0, -1, spells[itembonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID]].resist_difficulty); + } + } + } + } + + if (aabonuses.HasSkillAttackProc[skill]) { + for (int i = 0; i < MAX_CAST_ON_SKILL_USE i += 3) { + if (IsValidSpell(aabonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID]) && aabonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID] && skill == aabonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SKILL]) { + if (zone->random.Int(1, 1000) <= aabonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_CHANCE]) { + SpellFinished(aabonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID], on, EQ::spells::CastingSlot::Item, 0, -1, spells[aabonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID]].resist_difficulty); + } + } + } + } +} + bool Mob::TryRootFadeByDamage(int buffslot, Mob* attacker) { /*Dev Quote 2010: http://forums.station.sony.com/eq/posts/list.m?topic_id=161443 diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 5684abfc4..04ab98239 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1207,11 +1207,18 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) } case SE_SkillAttackProc: { - // You can only have one of these per client. [AA Dragon Punch] - newbon->SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] = base_value; // Chance base 1000 = 100% proc rate - newbon->SkillAttackProc[SBIndex::SKILLATK_PROC_SKILL] = limit_value; // Skill to Proc Off - newbon->SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID] = rank.spell; // spell to proc - break; + for (int i = 0; i < MAX_CAST_ON_SKILL_USE i += 3) { + if (!newbon->SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID]) { // spell id + newbon->SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID] = rank.spell; // spell to proc + newbon->SkillAttackProc[i + SBIndex::SKILLATK_PROC_CHANCE] = base_value; // Chance base 1000 = 100% proc rate + newbon->SkillAttackProc[i + SBIndex::SKILLATK_PROC_SKILL] = limit_value; // Skill to Proc Offr + + if (limit_value < EQ::skills::HIGHEST_SKILL) { + newbon->HasSkillAttackProc[limit_value] = true; //check first before looking for any effects. + } + break; + } + } } case SE_DamageModifier: { @@ -3578,6 +3585,21 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne break; } + case SE_SkillAttackProc: { + for (int i = 0; i < MAX_CAST_ON_SKILL_USE i += 3) { + if (!new_bonus->SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID]) { // spell id + new_bonus->SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID] = max_value; // spell to proc + new_bonus->SkillAttackProc[i + SBIndex::SKILLATK_PROC_CHANCE] = effect_value; // Chance base 1000 = 100% proc rate + new_bonus->SkillAttackProc[i + SBIndex::SKILLATK_PROC_SKILL] = limit_value; // Skill to Proc Offr + + if (limit_value < EQ::skills::HIGHEST_SKILL) { + new_bonus->HasSkillAttackProc[limit_value] = true; //check first before looking for any effects. + } + break; + } + } + } + case SE_PC_Pet_Rampage: { new_bonus->PC_Pet_Rampage[SBIndex::PET_RAMPAGE_CHANCE] += effect_value; //Chance to rampage if (new_bonus->PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] < limit_value) diff --git a/zone/common.h b/zone/common.h index a853b564e..a7a5f07a0 100644 --- a/zone/common.h +++ b/zone/common.h @@ -572,6 +572,7 @@ struct StatBonuses { uint8 IncreaseRunSpeedCap; // Increase max run speed above cap. int32 DoubleSpecialAttack; // Chance to to perform a double special attack (ie flying kick 2x) int32 SkillAttackProc[3]; // [0] chance to proc [2] spell on [1] skill usage + bool HasSkillAttackProc[EQ::skills::HIGHEST_SKILL + 1]; //check if any skill proc is present before assessing for all skill procs uint8 FrontalStunResist; // Chance to resist a frontal stun int32 BindWound; // Increase amount of HP by percent. int32 MaxBindWound; // Increase max amount of HP you can bind wound. @@ -671,9 +672,9 @@ namespace SBIndex { constexpr uint16 POSITION_FRONT = 1; // SPA 503-506 constexpr uint16 PET_RAMPAGE_CHANCE = 0; // SPA 464,465 constexpr uint16 PET_RAMPAGE_DMG_MOD = 1; // SPA 465,465 - constexpr uint16 SKILLATK_PROC_CHANCE = 0; // SPA 427 - constexpr uint16 SKILLATK_PROC_SKILL = 1; // SPA 427 - constexpr uint16 SKILLATK_PROC_SPELL_ID = 2; // SPA 427 + constexpr uint16 SKILLATK_PROC_SPELL_ID = 0; // SPA 288 + constexpr uint16 SKILLATK_PROC_CHANCE = 1; // SPA 288 + constexpr uint16 SKILLATK_PROC_SKILL = 2; // SPA 288 constexpr uint16 SLAYUNDEAD_RATE_MOD = 0; // SPA 219 constexpr uint16 SLAYUNDEAD_DMG_MOD = 1; // SPA 219 constexpr uint16 DOUBLE_RIPOSTE_CHANCE = 0; // SPA 223 @@ -692,7 +693,7 @@ namespace SBIndex { constexpr uint16 COMBAT_PROC_ORIGIN_ID = 0; // SPA constexpr uint16 COMBAT_PROC_SPELL_ID = 1; // SPA constexpr uint16 COMBAT_PROC_RATE_MOD = 2; // SPA - constexpr uint16 COMBAT_PROC_REUSE_TIMER = 3; // SPA + constexpr uint16 COMBAT_PROC_REUSE_TIMER = 3; // SPA }; diff --git a/zone/mob.cpp b/zone/mob.cpp index f4e4582be..87b315612 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4242,6 +4242,8 @@ void Mob::TriggerDefensiveProcs(Mob *on, uint16 hand, bool FromSkillProc, int da break; } + TryCastOnSkillUse(on, skillinuse); + if (on->HasSkillProcs()) { on->TrySkillProc(this, skillinuse, 0, false, hand, true); } diff --git a/zone/mob.h b/zone/mob.h index 0ee5a76c2..34c991177 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1447,9 +1447,10 @@ protected: bool spawned; void CalcSpellBonuses(StatBonuses* newbon); virtual void CalcBonuses(); - void TrySkillProc(Mob *on, EQ::skills::SkillType skill, uint16 ReuseTime, bool Success = false, uint16 hand = 0, bool IsDefensive = false); // hand = SlotCharm? + void TrySkillProc(Mob *on, EQ::skills::SkillType skill, uint16 ReuseTime, bool Success = false, uint16 hand = 0, bool IsDefensive = false); // hand if 0 means its a skill ability for proc rate checks, otherwise hand is passed. bool PassLimitToSkill(EQ::skills::SkillType skill, int32 spell_id, int proc_type, int aa_id=0); bool PassLimitClass(uint32 Classes_, uint16 Class_); + void TryCastOnSkillUse(Mob *on, EQ::skills::SkillType skill); void TryDefensiveProc(Mob *on, uint16 hand = EQ::invslot::slotPrimary); void TryWeaponProc(const EQ::ItemInstance* inst, const EQ::ItemData* weapon, Mob *on, uint16 hand = EQ::invslot::slotPrimary); void TrySpellProc(const EQ::ItemInstance* inst, const EQ::ItemData* weapon, Mob *on, uint16 hand = EQ::invslot::slotPrimary); diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index a8c6f932d..1e86a6792 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -196,14 +196,6 @@ void Mob::DoSpecialAttackDamage(Mob *who, EQ::skills::SkillType skill, int32 bas DoAttack(who, my_hit); who->AddToHateList(this, hate, 0); - if (my_hit.damage_done > 0 && aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] && aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SKILL] == skill && - IsValidSpell(aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID])) { - float chance = aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] / 1000.0f; - if (zone->random.Roll(chance)) - SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID], who, EQ::spells::CastingSlot::Item, 0, -1, - spells[aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID]].resist_difficulty); - } - who->Damage(this, my_hit.damage_done, SPELL_UNKNOWN, skill, false); // Make sure 'this' has not killed the target and 'this' is not dead (Damage shield ect). @@ -212,6 +204,8 @@ void Mob::DoSpecialAttackDamage(Mob *who, EQ::skills::SkillType skill, int32 bas if (HasDied()) return; + TryCastOnSkillUse(who, skill); + if (HasSkillProcs()) { TrySkillProc(who, skill, ReuseTime * 1000); } @@ -916,6 +910,8 @@ void Mob::DoArcheryAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, co TryWeaponProc(Ammo, Ammo->GetItem(), other, EQ::invslot::slotRange); } + TryCastOnSkillUse(other, EQ::skills::SkillArchery); + // Skill Proc Attempt if (HasSkillProcs() && other && !other->HasDied()) { if (ReuseTime) { @@ -1279,6 +1275,8 @@ void NPC::DoRangedAttackDmg(Mob* other, bool Launch, int16 damage_mod, int16 cha TrySpellProc(nullptr, (const EQ::ItemData*)nullptr, other, EQ::invslot::slotRange); } + TryCastOnSkillUse(other, skillInUse); + if (HasSkillProcs() && other && !other->HasDied()) { TrySkillProc(other, skillInUse, 0, false, EQ::invslot::slotRange); } @@ -2046,6 +2044,8 @@ void Mob::Taunt(NPC *who, bool always_succeed, int chance_bonus, bool FromSpell, MessageString(Chat::SpellFailure, FAILED_TAUNT); } + TryCastOnSkillUse(who, EQ::skills::SkillTaunt); + if (HasSkillProcs()) { TrySkillProc(who, EQ::skills::SkillTaunt, TauntReuseTime * 1000); } @@ -2101,6 +2101,8 @@ void Mob::InstillDoubt(Mob *who) { }*/ } + TryCastOnSkillUse(who, EQ::skills::SkillIntimidation); + if (HasSkillProcs()) { TrySkillProc(who, EQ::skills::SkillIntimidation, InstillDoubtReuseTime * 1000); } @@ -2253,19 +2255,13 @@ void Mob::DoMeleeSkillAttackDmg(Mob *other, uint16 weapon_damage, EQ::skills::Sk } other->AddToHateList(this, hate, 0); - if (damage > 0 && aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] && aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SKILL] == skillinuse && - IsValidSpell(aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID])) { - float chance = aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] / 1000.0f; - if (zone->random.Roll(chance)) - SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID], other, EQ::spells::CastingSlot::Item, 0, -1, - spells[aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID]].resist_difficulty); - } - other->Damage(this, damage, SPELL_UNKNOWN, skillinuse); if (HasDied()) return; + TryCastOnSkillUse(other, skillinuse); + if (CanSkillProc && HasSkillProcs()) { TrySkillProc(other, skillinuse, ReuseTime); } From f26d56d6d5ac84ff38c46dab9fd49eb821d59ed0 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 21 Dec 2021 09:17:35 -0500 Subject: [PATCH 526/624] validspell check (#1895) --- zone/client_packet.cpp | 7 ++++++- zone/spells.cpp | 13 +++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index b7b44277e..6afc3da5d 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -4061,7 +4061,12 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app) return; } - CastSpell(spell_to_cast, castspell->target_id, slot); + if (IsValidSpell(spell_to_cast)) { + CastSpell(spell_to_cast, castspell->target_id, slot); + } + else { + InterruptSpell(); + } } /* Spell Slot or Potion Belt Slot */ else if (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt) // ITEM or POTION cast diff --git a/zone/spells.cpp b/zone/spells.cpp index 7daabb156..11e8f201e 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -180,14 +180,19 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, { LogSpells("Spell casting canceled: not able to cast now. Valid? [{}], casting [{}], waiting? [{}], spellend? [{}], stunned? [{}], feared? [{}], mezed? [{}], silenced? [{}], amnesiad? [{}]", IsValidSpell(spell_id), casting_spell_id, delaytimer, spellend_timer.Enabled(), IsStunned(), IsFeared(), IsMezzed(), IsSilenced(), IsAmnesiad() ); - if(IsSilenced() && !IsDiscipline(spell_id)) + + if (IsSilenced() && !IsDiscipline(spell_id)) { MessageString(Chat::Red, SILENCED_STRING); - if(IsAmnesiad() && IsDiscipline(spell_id)) + } + if (IsAmnesiad() && IsDiscipline(spell_id)) { MessageString(Chat::Red, MELEE_SILENCE); - if(IsClient()) + } + if (IsClient()) { CastToClient()->SendSpellBarEnable(spell_id); - if(casting_spell_id && IsNPC()) + } + if (casting_spell_id && IsNPC()) { CastToNPC()->AI_Event_SpellCastFinished(false, static_cast(casting_spell_slot)); + } return(false); } //It appears that the Sanctuary effect is removed by a check on the client side (keep this however for redundancy) From 724d47432b060c2670057358860d8dcaea3f40dc Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Wed, 22 Dec 2021 15:27:25 -0500 Subject: [PATCH 527/624] [Bug Fix] Fix Perl Croak for GetEnt() (#1898) --- zone/perl_hateentry.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/perl_hateentry.cpp b/zone/perl_hateentry.cpp index 291a4e3df..65ecd41ac 100644 --- a/zone/perl_hateentry.cpp +++ b/zone/perl_hateentry.cpp @@ -34,7 +34,7 @@ XS(XS_HateEntry_GetEnt); /* prototype to pass -Wmissing-prototypes */ XS(XS_HateEntry_GetEnt) { dXSARGS; if (items != 1) - Perl_croak(aTHX_ "Usage: HateEntry::GetData(THIS)"); // @categories Script Utility, Hate and Aggro + Perl_croak(aTHX_ "Usage: HateEntry::GetEnt(THIS)"); // @categories Script Utility, Hate and Aggro { struct_HateList *THIS; Mob *RETVAL; From d0ec0872b9dd2910548717ff9b7231121d9ee40d Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Wed, 22 Dec 2021 22:35:48 -0600 Subject: [PATCH 528/624] Client will give 1 second window to start casting at the end of DA effect but we interrupt it and need to allow spellbar active after (#1894) --- zone/spells.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/zone/spells.cpp b/zone/spells.cpp index 11e8f201e..b6d10a3a9 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -223,6 +223,9 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, if(DivineAura()) { LogSpells("Spell casting canceled: cannot cast while Divine Aura is in effect"); InterruptSpell(173, 0x121, false); + if(IsClient()) { + CastToClient()->SendSpellBarEnable(spell_id); + } return(false); } From 6a77764f8be8402a6b72b67ae320a1c6ab8f1fc3 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 23 Dec 2021 13:04:26 -0500 Subject: [PATCH 529/624] [Commands] Cleanup #guild Command. (#1880) - Cleanup messages and logic. - Adds GetGuildNameByID, GetGuildRankName, GetGuildIDByCharacterID, and IsCharacterInGuild helper methods for guild stuff. - Convert #guild info message to a popup display to tidy it up and make it more legible. --- common/guild_base.cpp | 60 +++ common/guild_base.h | 4 + zone/client.cpp | 14 +- zone/gm_commands/guild.cpp | 899 +++++++++++++++++++++---------------- zone/guild_mgr.cpp | 134 ++++-- 5 files changed, 671 insertions(+), 440 deletions(-) diff --git a/common/guild_base.cpp b/common/guild_base.cpp index f56cd3deb..07ba3b040 100644 --- a/common/guild_base.cpp +++ b/common/guild_base.cpp @@ -1225,6 +1225,66 @@ uint32 BaseGuildManager::DoesAccountContainAGuildLeader(uint32 AccountID) return results.RowCount(); } +std::string BaseGuildManager::GetGuildNameByID(uint32 guild_id) const { + if(guild_id == GUILD_NONE) { + return std::string(); + } + std::map::const_iterator res; + res = m_guilds.find(guild_id); + if(res == m_guilds.end()) { + return "Invalid Guild"; + } + return res->second->name; +} +std::string BaseGuildManager::GetGuildRankName(uint32 guild_id, uint8 rank) const +{ + if(rank > GUILD_MAX_RANK) { + return "Invalid Rank"; + } + + std::map::const_iterator res; + res = m_guilds.find(guild_id); + if(res == m_guilds.end()) { + return "Invalid Guild Rank"; + } + + return res->second->ranks[rank].name; +} + +uint32 BaseGuildManager::GetGuildIDByCharacterID(uint32 character_id) +{ + if(!m_db) { + return GUILD_NONE; + } + + std::string query = fmt::format( + "SELECT `guild_id` FROM `guild_members` WHERE char_id = {} LIMIT 1", + character_id + ); + auto results = m_db->QueryDatabase(query); + if(!results.Success() || !results.RowCount()) { + return GUILD_NONE; + } + + auto row = results.begin(); + auto guild_id = std::stoul(row[0]); + return guild_id; +} + +bool BaseGuildManager::IsCharacterInGuild(uint32 character_id, uint32 guild_id) +{ + auto current_guild_id = GetGuildIDByCharacterID(character_id); + + if (current_guild_id == GUILD_NONE) { + return false; + } + + if (guild_id && current_guild_id != guild_id) { + return false; + } + + return true; +} \ No newline at end of file diff --git a/common/guild_base.h b/common/guild_base.h index ac8b1a895..122a9ff2f 100644 --- a/common/guild_base.h +++ b/common/guild_base.h @@ -76,8 +76,12 @@ class BaseGuildManager bool GetGuildChannel(uint32 GuildID, char *ChannelBuffer) const; const char *GetRankName(uint32 guild_id, uint8 rank) const; const char *GetGuildName(uint32 guild_id) const; + std::string GetGuildNameByID(uint32 guild_id) const; + std::string GetGuildRankName(uint32 guild_id, uint8 rank) const; + bool IsCharacterInGuild(uint32 character_id, uint32 guild_id = 0); bool GetGuildNameByID(uint32 guild_id, std::string &into) const; uint32 GetGuildIDByName(const char *GuildName); + uint32 GetGuildIDByCharacterID(uint32 character_id); bool IsGuildLeader(uint32 guild_id, uint32 char_id) const; uint8 GetDisplayedRank(uint32 guild_id, uint8 rank, uint32 char_id) const; bool CheckGMStatus(uint32 guild_id, uint8 status) const; diff --git a/zone/client.cpp b/zone/client.cpp index fbb1ac106..3e56614f0 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -5371,25 +5371,15 @@ void Client::ShowSkillsWindow() // Current Skill Level out of Max Skill Level or a Check Mark for Maxed popup_text += fmt::format( - "{}{}{}", + "{}", ( skill_maxed ? - "" : - "" - ), - ( - skill_maxed ? - "✔" : + "" : fmt::format( "{}/{}", current_skill, max_skill ) - ), - ( - skill_maxed ? - "" : - "" ) ); diff --git a/zone/gm_commands/guild.cpp b/zone/gm_commands/guild.cpp index e76f83965..5eaa7092b 100755 --- a/zone/gm_commands/guild.cpp +++ b/zone/gm_commands/guild.cpp @@ -8,437 +8,544 @@ extern WorldServer worldserver; void command_guild(Client *c, const Seperator *sep) { - int admin = c->Admin(); - Mob *target = c->GetTarget(); - - if (strcasecmp(sep->arg[1], "help") == 0) { - c->Message(Chat::White, "GM Guild commands:"); - c->Message(Chat::White, " #guild list - lists all guilds on the server"); - c->Message(Chat::White, " #guild create {guildleader charname or CharID} guildname"); - c->Message(Chat::White, " #guild delete guildID"); - c->Message(Chat::White, " #guild rename guildID newname"); - c->Message(Chat::White, " #guild set charname guildID (0=no guild)"); - c->Message(Chat::White, " #guild setrank charname rank"); - c->Message(Chat::White, " #guild setleader guildID {guildleader charname or CharID}"); + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "#guild create [Character ID|Character Name] [Guild Name]"); + c->Message(Chat::White, "#guild delete [Guild ID]"); + c->Message(Chat::White, "#guild help"); + c->Message(Chat::White, "#guild info [Guild ID]"); + c->Message(Chat::White, "#guild list"); + c->Message(Chat::White, "#guild rename [Guild ID] [New Name]"); + c->Message(Chat::White, "#guild set [Character ID|Character Name] [Guild ID] (Guild ID 0 is Guildless)"); + c->Message(Chat::White, "#guild setleader [Guild ID] [Character ID|Character Name]"); + c->Message(Chat::White, "#guild setrank [Character ID|Character Name] [Rank]"); + c->Message(Chat::White, "#guild status [Character ID|Character Name]"); + return; } - else if (strcasecmp(sep->arg[1], "status") == 0 || strcasecmp(sep->arg[1], "stat") == 0) { - Client *client = 0; - if (sep->arg[2][0] != 0) { - client = entity_list.GetClientByName(sep->argplus[2]); - } - else if (target != 0 && target->IsClient()) { - client = target->CastToClient(); - } - if (client == 0) { - c->Message(Chat::White, "You must target someone or specify a character name"); - } - else if ((client->Admin() >= minStatusToEditOtherGuilds && admin < minStatusToEditOtherGuilds) && - client->GuildID() != c->GuildID()) { // no peeping for GMs, make sure tell message stays the same - c->Message(Chat::White, "You must target someone or specify a character name."); - } - else { - if (client->IsInAGuild()) { - c->Message(Chat::White, "%s is not in a guild.", client->GetName()); - } - else if (guild_mgr.IsGuildLeader(client->GuildID(), client->CharacterID())) { + + auto target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + + bool is_create = !strcasecmp(sep->arg[1], "create"); + bool is_delete = !strcasecmp(sep->arg[1], "delete"); + bool is_help = !strcasecmp(sep->arg[1], "help"); + bool is_info = !strcasecmp(sep->arg[1], "info"); + bool is_list = !strcasecmp(sep->arg[1], "list"); + bool is_rename = !strcasecmp(sep->arg[1], "rename"); + bool is_set = !strcasecmp(sep->arg[1], "set"); + bool is_set_leader = !strcasecmp(sep->arg[1], "setleader"); + bool is_set_rank = !strcasecmp(sep->arg[1], "setrank"); + bool is_status = !strcasecmp(sep->arg[1], "status"); + if ( + !is_create && + !is_delete && + !is_help && + !is_info && + !is_list && + !is_rename && + !is_set && + !is_set_leader && + !is_set_rank && + !is_status + ) { + c->Message(Chat::White, "#guild create [Character ID|Character Name] [Guild Name]"); + c->Message(Chat::White, "#guild delete [Guild ID]"); + c->Message(Chat::White, "#guild help"); + c->Message(Chat::White, "#guild info [Guild ID]"); + c->Message(Chat::White, "#guild list"); + c->Message(Chat::White, "#guild rename [Guild ID] [New Name]"); + c->Message(Chat::White, "#guild set [Character ID|Character Name] [Guild ID] (Guild ID 0 is Guildless)"); + c->Message(Chat::White, "#guild setleader [Guild ID] [Character ID|Character Name]"); + c->Message(Chat::White, "#guild setrank [Character ID|Character Name] [Rank]"); + c->Message(Chat::White, "#guild status [Character ID|Character Name]"); + return; + } + + if (is_create) { + if (arguments != 3) { + c->Message(Chat::White, "Usage: #guild create [Character ID|Character Name] [Guild Name]"); + } else { + auto leader_id = ( + sep->IsNumber(2) ? + std::stoul(sep->arg[2]) : + database.GetCharacterID(sep->arg[2]) + ); + auto leader_name = database.GetCharNameByID(leader_id); + if (!leader_id || leader_name.empty()) { c->Message( Chat::White, - "%s is the leader of <%s> rank: %s", - client->GetName(), - guild_mgr.GetGuildName(client->GuildID()), - guild_mgr.GetRankName(client->GuildID(), client->GuildRank())); + fmt::format( + "Character ID {} could not be found.", + leader_id + ).c_str() + ); + return; } - else { + + auto guild_id = guild_mgr.FindGuildByLeader(leader_id); + if (guild_id != GUILD_NONE) { c->Message( Chat::White, - "%s is a member of <%s> rank: %s", - client->GetName(), - guild_mgr.GetGuildName(client->GuildID()), - guild_mgr.GetRankName(client->GuildID(), client->GuildRank())); - } - } - } - else if (strcasecmp(sep->arg[1], "info") == 0) { - if (sep->arg[2][0] == 0 && c->IsInAGuild()) { - if (admin >= minStatusToEditOtherGuilds) { - c->Message(Chat::White, "Usage: #guildinfo guild_id"); - } - else { - c->Message(Chat::White, "You're not in a guild"); - } - } - else { - uint32 tmp = GUILD_NONE; - if (sep->arg[2][0] == 0) { - tmp = c->GuildID(); - } - else if (admin >= minStatusToEditOtherGuilds) { - tmp = atoi(sep->arg[2]); - } - - if (tmp != GUILD_NONE) { - guild_mgr.DescribeGuild(c, tmp); - } - } - } - else if (strcasecmp(sep->arg[1], "set") == 0) { - if (!sep->IsNumber(3)) { - c->Message(Chat::White, "Usage: #guild set charname guildgbid (0 = clear guildtag)"); - } - else { - uint32 guild_id = atoi(sep->arg[3]); - - if (guild_id == 0) { - guild_id = GUILD_NONE; - } - else if (!guild_mgr.GuildExists(guild_id)) { - c->Message(Chat::Red, "Guild %d does not exist.", guild_id); - return; - } - - uint32 charid = database.GetCharacterID(sep->arg[2]); - if (charid == 0) { - c->Message(Chat::Red, "Unable to find character '%s'", charid); - return; - } - - //we could do the checking we need for guild_mgr.CheckGMStatus, but im lazy right now - if (admin < minStatusToEditOtherGuilds) { - c->Message(Chat::Red, "Access denied."); - return; - } - - if (guild_id == GUILD_NONE) { - LogGuilds("[{}]: Removing [{}] ([{}]) from guild with GM command", c->GetName(), sep->arg[2], charid); - } - else { - LogGuilds("[{}]: Putting [{}] ([{}]) into guild [{}] ([{}]) with GM command", - c->GetName(), - sep->arg[2], - charid, - guild_mgr.GetGuildName(guild_id), - guild_id); - } - - if (!guild_mgr.SetGuild(charid, guild_id, GUILD_MEMBER)) { - c->Message(Chat::Red, "Error putting '%s' into guild %d", sep->arg[2], guild_id); - } - else { - c->Message(Chat::White, "%s has been put into guild %d", sep->arg[2], guild_id); - } - } - } - /*else if (strcasecmp(sep->arg[1], "setdoor") == 0 && admin >= minStatusToEditOtherGuilds) { - - if (!sep->IsNumber(2)) - c->Message(Chat::White, "Usage: #guild setdoor guildEQid (0 = delete guilddoor)"); - else { - // guild doors - if((!guilds[atoi(sep->arg[2])].databaseID) && (atoi(sep->arg[2])!=0) ) - { - - c->Message(Chat::White, "These is no guild with this guildEQid"); - } - else { - c->SetIsSettingGuildDoor(true); - c->Message(Chat::White, "Click on a door you want to become a guilddoor"); - c->SetSetGuildDoorID(atoi(sep->arg[2])); - } - } - }*/ - else if (strcasecmp(sep->arg[1], "setrank") == 0) { - int rank = atoi(sep->arg[3]); - if (!sep->IsNumber(3)) { - c->Message(Chat::White, "Usage: #guild setrank charname rank"); - } - else if (rank < 0 || rank > GUILD_MAX_RANK) { - c->Message(Chat::White, "Error: invalid rank #."); - } - else { - uint32 charid = database.GetCharacterID(sep->arg[2]); - if (charid == 0) { - c->Message(Chat::Red, "Unable to find character '%s'", charid); - return; - } - - //we could do the checking we need for guild_mgr.CheckGMStatus, but im lazy right now - if (admin < minStatusToEditOtherGuilds) { - c->Message(Chat::Red, "Access denied."); - return; - } - - LogGuilds("[{}]: Setting [{}] ([{}])'s guild rank to [{}] with GM command", - c->GetName(), - sep->arg[2], - charid, - rank); - - if (!guild_mgr.SetGuildRank(charid, rank)) { - c->Message(Chat::Red, "Error while setting rank %d on '%s'.", rank, sep->arg[2]); - } - else { - c->Message(Chat::White, "%s has been set to rank %d", sep->arg[2], rank); - } - } - } - else if (strcasecmp(sep->arg[1], "create") == 0) { - if (sep->arg[3][0] == 0) { - c->Message(Chat::White, "Usage: #guild create {guildleader charname or CharID} guild name"); - } - else if (!worldserver.Connected()) { - c->Message(Chat::White, "Error: World server dirconnected"); - } - else { - uint32 leader = 0; - if (sep->IsNumber(2)) { - leader = atoi(sep->arg[2]); - } - else if ((leader = database.GetCharacterID(sep->arg[2])) != 0) { - //got it from the db.. - } - else { - c->Message(Chat::Red, "Unable to find char '%s'", sep->arg[2]); - return; - } - if (leader == 0) { - c->Message(Chat::White, "Guild leader not found."); - return; - } - - uint32 tmp = guild_mgr.FindGuildByLeader(leader); - if (tmp != GUILD_NONE) { - c->Message( - Chat::White, - "Error: %s already is the leader of DB# %i '%s'.", - sep->arg[2], - tmp, - guild_mgr.GetGuildName(tmp)); - } - else { - - if (admin < minStatusToEditOtherGuilds) { - c->Message(Chat::Red, "Access denied."); + fmt::format( + "{} ({}) is already the leader of {} ({}).", + leader_name, + leader_id, + guild_mgr.GetGuildNameByID(guild_id), + guild_id + ).c_str() + ); + } else { + if (c->Admin() < minStatusToEditOtherGuilds) { + c->Message(Chat::White, "You cannot edit other peoples' guilds."); return; } - uint32 id = guild_mgr.CreateGuild(sep->argplus[3], leader); + auto guild_name = sep->argplus[3]; + auto guild_id = guild_mgr.CreateGuild(sep->argplus[3], leader_id); - LogGuilds("[{}]: Creating guild [{}] with leader [{}] with GM command. It was given id [{}]", - c->GetName(), - sep->argplus[3], - leader, - (unsigned long) id); + LogGuilds( + "[{}]: Creating guild [{}] with leader [{}] with GM command. It was given id [{}]", + c->GetName(), + guild_name, + leader_id, + guild_id + ); - if (id == GUILD_NONE) { + if (guild_id == GUILD_NONE) { c->Message(Chat::White, "Guild creation failed."); - } - else { - c->Message(Chat::White, "Guild created: Leader: %i, number %i: %s", leader, id, sep->argplus[3]); + } else { + c->Message( + Chat::White, + fmt::format( + "Guild Created | Name: {} ({}) Leader: {} ({})", + guild_name, + guild_id, + leader_name, + leader_id + ).c_str() + ); - if (!guild_mgr.SetGuild(leader, id, GUILD_LEADER)) { + if (!guild_mgr.SetGuild(leader_id, guild_id, GUILD_LEADER)) { c->Message( Chat::White, - "Unable to set guild leader's guild in the database. Your going to have to run #guild set" + fmt::format( + "Unable to put {} ({}) in to {} ({}) in the database.", + leader_name, + leader_id, + guild_name, + guild_id + ).c_str() ); + + c->Message(Chat::White, "Note: Run #guild set to resolve this."); } } - } } - } - else if (strcasecmp(sep->arg[1], "delete") == 0) { + } else if (is_delete) { if (!sep->IsNumber(2)) { - c->Message(Chat::White, "Usage: #guild delete guildID"); - } - else if (!worldserver.Connected()) { - c->Message(Chat::White, "Error: World server dirconnected"); - } - else { - uint32 id = atoi(sep->arg[2]); - - if (!guild_mgr.GuildExists(id)) { - c->Message(Chat::White, "Guild %d does not exist!", id); + c->Message(Chat::White, "Usage: #guild delete [Guild ID]"); + } else { + auto guild_id = std::stoul(sep->arg[2]); + if (!guild_mgr.GuildExists(guild_id)) { + c->Message( + Chat::White, + fmt::format( + "Guild ID {} could not be found.", + guild_id + ).c_str() + ); return; } - if (admin < minStatusToEditOtherGuilds) { - //this person is not allowed to just edit any guild, check this guild's min status. - if (c->GuildID() != id) { - c->Message(Chat::Red, "Access denied to edit other people's guilds"); + if (c->Admin() < minStatusToEditOtherGuilds) { + if (c->GuildID() != guild_id) { + c->Message(Chat::White, "You cannot edit other peoples' guilds."); return; - } - else if (!guild_mgr.CheckGMStatus(id, admin)) { - c->Message(Chat::Red, "Access denied to edit your guild with GM commands."); + } else if (!guild_mgr.CheckGMStatus(guild_id, c->Admin())) { + c->Message(Chat::White, "You cannot edit your current guild, your status is not high enough."); return; } } - LogGuilds("[{}]: Deleting guild [{}] ([{}]) with GM command", c->GetName(), - guild_mgr.GetGuildName(id), id); + LogGuilds( + "[{}]: Deleting guild [{}] ([{}]) with GM command", + c->GetName(), + guild_mgr.GetGuildNameByID(guild_id), + guild_id + ); - if (!guild_mgr.DeleteGuild(id)) { - c->Message(Chat::White, "Guild delete failed."); + c->Message( + Chat::White, + fmt::format( + "Guild {} Deleted | Name: {} ({})", + guild_mgr.DeleteGuild(guild_id) ? "Successfully" : "Unsuccessfully", + guild_mgr.GetGuildNameByID(guild_id), + guild_id + ).c_str() + ); + } + } else if (is_help) { + c->Message(Chat::White, "#guild create [Character ID|Character Name] [Guild Name]"); + c->Message(Chat::White, "#guild delete [Guild ID]"); + c->Message(Chat::White, "#guild help"); + c->Message(Chat::White, "#guild info [Guild ID]"); + c->Message(Chat::White, "#guild list"); + c->Message(Chat::White, "#guild rename [Guild ID] [New Name]"); + c->Message(Chat::White, "#guild set [Character ID|Character Name] [Guild ID] (Guild ID 0 is Guildless)"); + c->Message(Chat::White, "#guild setleader [Guild ID] [Character ID|Character Name]"); + c->Message(Chat::White, "#guild setrank [Character ID|Character Name] [Rank]"); + c->Message(Chat::White, "#guild status [Character ID|Character Name]"); + } else if (is_info) { + if (arguments != 2 && c->IsInAGuild()) { + if (c->Admin() >= minStatusToEditOtherGuilds) { + c->Message(Chat::White, "#guild info [Guild ID]"); + } else { + c->Message(Chat::White, "You cannot edit other peoples' guilds."); } - else { - c->Message(Chat::White, "Guild %d deleted.", id); + } else { + auto guild_id = GUILD_NONE; + if (arguments != 2 || !sep->IsNumber(2)) { + guild_id = c->GuildID(); + } else if (c->Admin() >= minStatusToEditOtherGuilds) { + guild_id = std::stoul(sep->arg[2]); + } + + if (guild_id != GUILD_NONE) { + guild_mgr.DescribeGuild(c, guild_id); } } - } - else if (strcasecmp(sep->arg[1], "rename") == 0) { - if ((!sep->IsNumber(2)) || sep->arg[3][0] == 0) { - c->Message(Chat::White, "Usage: #guild rename guildID newname"); - } - else if (!worldserver.Connected()) { - c->Message(Chat::White, "Error: World server dirconnected"); - } - else { - uint32 id = atoi(sep->arg[2]); - - if (!guild_mgr.GuildExists(id)) { - c->Message(Chat::White, "Guild %d does not exist!", id); - return; - } - - if (admin < minStatusToEditOtherGuilds) { - //this person is not allowed to just edit any guild, check this guild's min status. - if (c->GuildID() != id) { - c->Message(Chat::Red, "Access denied to edit other people's guilds"); - return; - } - else if (!guild_mgr.CheckGMStatus(id, admin)) { - c->Message(Chat::Red, "Access denied to edit your guild with GM commands."); - return; - } - } - - LogGuilds("[{}]: Renaming guild [{}] ([{}]) to [{}] with GM command", c->GetName(), - guild_mgr.GetGuildName(id), id, sep->argplus[3]); - - if (!guild_mgr.RenameGuild(id, sep->argplus[3])) { - c->Message(Chat::White, "Guild rename failed."); - } - else { - c->Message(Chat::White, "Guild %d renamed to %s", id, sep->argplus[3]); - } - } - } - else if (strcasecmp(sep->arg[1], "setleader") == 0) { - if (sep->arg[3][0] == 0 || !sep->IsNumber(2)) { - c->Message(Chat::White, "Usage: #guild setleader guild_id {guildleader charname or CharID}"); - } - else if (!worldserver.Connected()) { - c->Message(Chat::White, "Error: World server dirconnected"); - } - else { - uint32 leader = 0; - if (sep->IsNumber(2)) { - leader = atoi(sep->arg[2]); - } - else if ((leader = database.GetCharacterID(sep->arg[2])) != 0) { - //got it from the db.. - } - else { - c->Message(Chat::Red, "Unable to find char '%s'", sep->arg[2]); - return; - } - - uint32 tmpdb = guild_mgr.FindGuildByLeader(leader); - if (leader == 0) { - c->Message(Chat::White, "New leader not found."); - } - else if (tmpdb != 0) { - c->Message(Chat::White, "Error: %s already is the leader of guild # %i", sep->arg[2], tmpdb); - } - else { - uint32 id = atoi(sep->arg[2]); - - if (!guild_mgr.GuildExists(id)) { - c->Message(Chat::White, "Guild %d does not exist!", id); - return; - } - - if (admin < minStatusToEditOtherGuilds) { - //this person is not allowed to just edit any guild, check this guild's min status. - if (c->GuildID() != id) { - c->Message(Chat::Red, "Access denied to edit other people's guilds"); - return; - } - else if (!guild_mgr.CheckGMStatus(id, admin)) { - c->Message(Chat::Red, "Access denied to edit your guild with GM commands."); - return; - } - } - - LogGuilds("[{}]: Setting leader of guild [{}] ([{}]) to [{}] with GM command", c->GetName(), - guild_mgr.GetGuildName(id), id, leader); - - if (!guild_mgr.SetGuildLeader(id, leader)) { - c->Message(Chat::White, "Guild leader change failed."); - } - else { - c->Message(Chat::White, "Guild leader changed: guild # %d, Leader: %s", id, sep->argplus[3]); - } - } - } - } - else if (strcasecmp(sep->arg[1], "list") == 0) { - if (admin < minStatusToEditOtherGuilds) { - c->Message(Chat::Red, "Access denied."); + } else if (is_list) { + if (c->Admin() < minStatusToEditOtherGuilds) { + c->Message(Chat::White, "You cannot edit other peoples' guilds."); return; } + guild_mgr.ListGuilds(c); - } - else { - c->Message(Chat::White, "Unknown guild command, try #guild help"); + } else if (is_rename) { + if ( + arguments != 3 || + !sep->IsNumber(2) + ) { + c->Message(Chat::White, "Usage: #guild rename [Guild ID] [New Guild Name]"); + } else { + auto guild_id = std::stoul(sep->arg[2]); + if (!guild_mgr.GuildExists(guild_id)) { + c->Message( + Chat::White, + fmt::format( + "Guild ID {} could not be found.", + guild_id + ).c_str() + ); + return; + } + + if (c->Admin() < minStatusToEditOtherGuilds) { + if (c->GuildID() != guild_id) { + c->Message(Chat::White, "You cannot edit other peoples' guilds."); + return; + } else if (!guild_mgr.CheckGMStatus(guild_id, c->Admin())) { + c->Message(Chat::White, "You cannot edit your current guild, your status is not high enough."); + return; + } + } + + auto new_guild_name = sep->argplus[3]; + LogGuilds( + "[{}]: Renaming guild [{}] ([{}]) to [{}] with GM command", + c->GetName(), + guild_mgr.GetGuildNameByID(guild_id), + guild_id, + new_guild_name + ); + + c->Message( + Chat::White, + fmt::format( + "Guild {} Renamed | Name: {} ({})", + guild_mgr.RenameGuild(guild_id, new_guild_name) ? "Successfully" : "Unsuccessfully", + new_guild_name, + guild_id + ).c_str() + ); + } + } else if (is_set) { + if ( + arguments != 3 || + !sep->IsNumber(3) + ) { + c->Message(Chat::White, "#guild set [Character ID|Character Name] [Guild ID] (Guild ID 0 is Guildless)"); + return; + } else { + auto guild_id = std::stoul(sep->arg[3]); + if (!guild_id) { + guild_id = GUILD_NONE; + } else if (!guild_mgr.GuildExists(guild_id)) { + c->Message( + Chat::White, + fmt::format( + "Guild ID {} could not be found.", + guild_id + ).c_str() + ); + return; + } + + auto character_id = ( + sep->IsNumber(2) ? + std::stoul(sep->arg[2]) : + database.GetCharacterID(sep->arg[2]) + ); + auto character_name = database.GetCharNameByID(character_id); + if (!character_id || character_name.empty()) { + c->Message( + Chat::White, + fmt::format( + "Character ID {} could not be found.", + character_id + ).c_str() + ); + return; + } + + if (c->Admin() < minStatusToEditOtherGuilds && guild_id != c->GuildID()) { + c->Message(Chat::White, "You cannot edit other peoples' guilds."); + return; + } + + if (!guild_id || guild_id == GUILD_NONE) { + LogGuilds( + "[{}]: Removing [{}] ([{}]) from guild with GM command", + c->GetName(), + character_name, + character_id + ); + } else { + LogGuilds( + "[{}]: Putting [{}] ([{}]) into guild [{}] ([{}]) with GM command", + c->GetName(), + character_name, + character_id, + guild_mgr.GetGuildNameByID(guild_id), + guild_id + ); + } + + if (guild_id && guild_id != GUILD_NONE) { + c->Message( + Chat::White, + fmt::format( + "{} ({}) has {} put into {} ({}).", + character_name, + character_id, + guild_mgr.SetGuild(character_id, guild_id, GUILD_MEMBER) ? "been" : "failed to be", + guild_mgr.GetGuildNameByID(guild_id), + guild_id + ).c_str() + ); + } else { + c->Message( + Chat::White, + fmt::format( + "{} ({}) is now Guildless.", + character_name, + character_id + ).c_str() + ); + } + } + } else if (is_set_leader) { + if ( + arguments != 3 || + !sep->IsNumber(2) + ) { + c->Message(Chat::White, "Usage: #guild setleader [Guild ID] [Character ID|Character Name]"); + } else { + auto leader_id = ( + sep->IsNumber(2) ? + std::stoul(sep->arg[2]) : + database.GetCharacterID(sep->arg[2]) + ); + auto leader_name = database.GetCharNameByID(leader_id); + if (!leader_id || leader_name.empty()) { + c->Message( + Chat::White, + fmt::format( + "Character ID {} could not be found.", + leader_id + ).c_str() + ); + return; + } + + auto guild_id = guild_mgr.FindGuildByLeader(leader_id); + if (guild_id != 0) { + c->Message( + Chat::White, + fmt::format( + "{} ({}) is already the leader of {} ({}).", + leader_name, + leader_id, + guild_mgr.GetGuildNameByID(guild_id), + guild_id + ).c_str() + ); + } + else { + auto guild_id = std::stoul(sep->arg[2]); + if (!guild_mgr.GuildExists(guild_id)) { + c->Message( + Chat::White, + fmt::format( + "Guild ID {} could not be found.", + guild_id + ).c_str() + ); + return; + } + + if (c->Admin() < minStatusToEditOtherGuilds) { + if (c->GuildID() != guild_id) { + c->Message(Chat::White, "You cannot edit other peoples' guilds."); + return; + } else if (!guild_mgr.CheckGMStatus(guild_id, c->Admin())) { + c->Message(Chat::White, "You cannot edit your current guild, your status is not high enough."); + return; + } + } + + LogGuilds( + "[{}]: Setting leader of guild [{}] ([{}]) to [{}] with GM command", + c->GetName(), + guild_mgr.GetGuildNameByID(guild_id), + guild_id, + leader_id + ); + + c->Message( + Chat::White, + fmt::format( + "Guild Leader {} Changed | Name: {} ({})", + guild_mgr.SetGuildLeader(guild_id, leader_id) ? "Successfully" : "Unsuccessfully", + guild_mgr.GetGuildNameByID(guild_id), + guild_id + ).c_str() + ); + } + } + } else if (is_set_rank) { + auto rank = static_cast(std::stoul(sep->arg[3])); + if (!sep->IsNumber(3)) { + c->Message(Chat::White, "#guild setrank [Character ID|Character Name] [Rank]"); + } else if (rank < 0 || rank > GUILD_MAX_RANK) { + c->Message( + Chat::White, + fmt::format( + "Guild Ranks are from 0 to {}.", + GUILD_MAX_RANK + ).c_str() + ); + } else { + auto character_id = ( + sep->IsNumber(2) ? + std::stoul(sep->arg[2]) : + database.GetCharacterID(sep->arg[2]) + ); + auto character_name = database.GetCharNameByID(character_id); + if (!character_id || character_name.empty()) { + c->Message( + Chat::White, + fmt::format( + "Character ID {} could not be found.", + character_id + ).c_str() + ); + return; + } + + if (!guild_mgr.IsCharacterInGuild(character_id)) { + c->Message( + Chat::White, + fmt::format( + "{} ({}) is not in a guild.", + character_name, + character_id + ).c_str() + ); + return; + } + + if (c->Admin() < minStatusToEditOtherGuilds && character_id != c->CharacterID()) { + c->Message(Chat::White, "You cannot edit other peoples' guilds."); + return; + } + + LogGuilds( + "[{}]: Setting [{}] ([{}])'s guild rank to [{}] with GM command", + c->GetName(), + sep->arg[2], + character_id, + rank + ); + + c->Message( + Chat::White, + fmt::format( + "{} ({}) has {} to rank {} ({}).", + character_name, + character_id, + guild_mgr.SetGuildRank(character_id, rank) ? "been set" : "failed to be set", + !guild_mgr.GetGuildRankName(guild_mgr.GetGuildIDByCharacterID(character_id), rank).empty() ? guild_mgr.GetGuildRankName(guild_mgr.GetGuildIDByCharacterID(character_id), rank) : "Nameless", + rank + ).c_str() + ); + } + } else if (is_status) { + auto client = ( + target ? + target : + ( + arguments == 2 ? + entity_list.GetClientByName(sep->arg[2]) : + c + ) + ); + if (!client) { + c->Message(Chat::White, "You must target someone or specify a character name."); + } else if (c->Admin() < minStatusToEditOtherGuilds && client->GuildID() != c->GuildID()) { + c->Message(Chat::White, "You cannot edit other peoples' guilds."); + } else { + if (!client->IsInAGuild()) { + c->Message( + Chat::White, + fmt::format( + "{} is not in a guild.", + client->GetCleanName() + ).c_str() + ); + } else if (guild_mgr.IsGuildLeader(client->GuildID(), client->CharacterID())) { + c->Message( + Chat::White, + fmt::format( + "{} is the leader of {}.", + client->GetName(), + guild_mgr.GetGuildNameByID(client->GuildID()) + ).c_str() + ); + } else { + c->Message( + Chat::White, + fmt::format( + "{} is a(n) {} of {}.", + client->GetName(), + guild_mgr.GetRankName(client->GuildID(), client->GuildRank()), + guild_mgr.GetGuildNameByID(client->GuildID()) + ).c_str() + ); + } + } } } -/* -bool helper_guild_edit(Client *c, uint32 dbid, uint32 eqid, uint8 rank, const char* what, const char* value) { - struct GuildRankLevel_Struct grl; - strcpy(grl.rankname, guild_mgr.GetRankName(eqid, rank)); - grl.demote = guilds[eqid].rank[rank].demote; - grl.heargu = guilds[eqid].rank[rank].heargu; - grl.invite = guilds[eqid].rank[rank].invite; - grl.motd = guilds[eqid].rank[rank].motd; - grl.promote = guilds[eqid].rank[rank].promote; - grl.remove = guilds[eqid].rank[rank].remove; - grl.speakgu = guilds[eqid].rank[rank].speakgu; - grl.warpeace = guilds[eqid].rank[rank].warpeace; - - if (strcasecmp(what, "title") == 0) { - if (strlen(value) > 100) - c->Message(Chat::White, "Error: Title has a maxium length of 100 characters."); - else - strcpy(grl.rankname, value); - } - else if (rank == 0) - c->Message(Chat::White, "Error: Rank 0's permissions can not be changed."); - else { - if (!(strlen(value) == 1 && (value[0] == '0' || value[0] == '1'))) - - return false; - if (strcasecmp(what, "demote") == 0) - grl.demote = (value[0] == '1'); - else if (strcasecmp(what, "heargu") == 0) - grl.heargu = (value[0] == '1'); - else if (strcasecmp(what, "invite") == 0) - grl.invite = (value[0] == '1'); - else if (strcasecmp(what, "motd") == 0) - grl.motd = (value[0] == '1'); - else if (strcasecmp(what, "promote") == 0) - grl.promote = (value[0] == '1'); - else if (strcasecmp(what, "remove") == 0) - - grl.remove = (value[0] == '1'); - else if (strcasecmp(what, "speakgu") == 0) - grl.speakgu = (value[0] == '1'); - else if (strcasecmp(what, "warpeace") == 0) - grl.warpeace = (value[0] == '1'); - else - c->Message(Chat::White, "Error: Permission name not recognized."); - } - if (!database.EditGuild(dbid, rank, &grl)) - c->Message(Chat::White, "Error: database.EditGuild() failed"); - return true; -}*/ - diff --git a/zone/guild_mgr.cpp b/zone/guild_mgr.cpp index e3c4e6071..81e221b6b 100644 --- a/zone/guild_mgr.cpp +++ b/zone/guild_mgr.cpp @@ -190,53 +190,123 @@ uint8 *ZoneGuildManager::MakeGuildMembers(uint32 guild_id, const char *prefix_na } void ZoneGuildManager::ListGuilds(Client *c) const { - c->Message(Chat::White, "Listing guilds on the server:"); - char leadername[64]; - std::map::const_iterator cur, end; - cur = m_guilds.begin(); - end = m_guilds.end(); - int r = 0; - for(; cur != end; ++cur) { - leadername[0] = '\0'; - database.GetCharName(cur->second->leader_char_id, leadername); - if (leadername[0] == '\0') - c->Message(Chat::White, " Guild #%i <%s>", cur->first, cur->second->name.c_str()); - else - c->Message(Chat::White, " Guild #%i <%s> Leader: %s", cur->first, cur->second->name.c_str(), leadername); - r++; + if (m_guilds.size()) { + c->Message( + Chat::White, + fmt::format( + "Listing {} Guild{}.", + m_guilds.size(), + m_guilds.size() != 1 ? "s" : "" + ).c_str() + ); + + for (const auto& guild : m_guilds) { + auto leader_name = database.GetCharNameByID(guild.second->leader_char_id); + c->Message( + Chat::White, + fmt::format( + "Guild {} | {}Name: {}", + guild.first, + ( + !leader_name.empty() ? + fmt::format( + "Leader: {} ({}) ", + leader_name, + guild.second->leader_char_id + ) : + "" + ), + guild.second->name + ).c_str() + ); + } + } else { + c->Message(Chat::White, "There are no Guilds to list."); } - c->Message(Chat::White, "%i guilds listed.", r); } void ZoneGuildManager::DescribeGuild(Client *c, uint32 guild_id) const { std::map::const_iterator res; res = m_guilds.find(guild_id); - if(res == m_guilds.end()) { - c->Message(Chat::White, "Guild %d not found.", guild_id); + if (res == m_guilds.end()) { + c->Message( + Chat::White, + fmt::format( + "Guild ID {} could not be found.", + guild_id + ).c_str() + ); return; } const GuildInfo *info = res->second; - c->Message(Chat::White, "Guild info DB# %i <%s>", guild_id, info->name.c_str()); + auto leader_name = database.GetCharNameByID(info->leader_char_id); + std::string popup_text = ""; + popup_text += fmt::format( + "", + info->name, + guild_id + ); + popup_text += fmt::format( + "", + leader_name, + info->leader_char_id + ); + popup_text += "

"; + popup_text += ""; + popup_text += ""; + popup_text += ""; + popup_text += ""; + popup_text += ""; + popup_text += ""; + popup_text += ""; + popup_text += ""; + popup_text += ""; + popup_text += ""; + popup_text += ""; - char leadername[64]; - database.GetCharName(info->leader_char_id, leadername); - c->Message(Chat::White, "Guild Leader: %s", leadername); - - char permbuffer[256]; - uint8 i; - for (i = 0; i <= GUILD_MAX_RANK; i++) { - char *permptr = permbuffer; - uint8 r; - for(r = 0; r < _MaxGuildAction; r++) - permptr += sprintf(permptr, " %s: %c", GuildActionNames[r], info->ranks[i].permissions[r]?'Y':'N'); - - c->Message(Chat::White, "Rank %i: %s", i, info->ranks[i].name.c_str()); - c->Message(Chat::White, "Permissions: %s", permbuffer); + for (uint8 guild_rank = 0; guild_rank <= GUILD_MAX_RANK; guild_rank++) { + auto can_hear_guild_chat = info->ranks[guild_rank].permissions[GUILD_HEAR] ? "" : ""; + auto can_speak_guild_chat = info->ranks[guild_rank].permissions[GUILD_SPEAK] ? "" : ""; + auto can_invite = info->ranks[guild_rank].permissions[GUILD_INVITE] ? "" : ""; + auto can_remove = info->ranks[guild_rank].permissions[GUILD_REMOVE] ? "" : ""; + auto can_promote = info->ranks[guild_rank].permissions[GUILD_PROMOTE] ? "" : ""; + auto can_demote = info->ranks[guild_rank].permissions[GUILD_DEMOTE] ? "" : ""; + auto can_set_motd = info->ranks[guild_rank].permissions[GUILD_MOTD] ? "" : ""; + auto can_war_peace = info->ranks[guild_rank].permissions[GUILD_WARPEACE] ? "" : ""; + popup_text += fmt::format( + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "", + !info->ranks[guild_rank].name.empty() ? info->ranks[guild_rank].name : "Nameless", + guild_rank, + can_demote, + can_hear_guild_chat, + can_invite, + can_promote, + can_remove, + can_set_motd, + can_speak_guild_chat, + can_war_peace + ); } + popup_text += "
Name{}Guild ID{}
Leader{}Character ID{}
RankDemoteHear Guild ChatInvitePromoteRemoveSet MOTDSpeak Guild ChatWar/Peace
{} ({}){}{}{}{}{}{}{}{}
"; + + c->SendPopupToClient( + "Guild Information", + popup_text.c_str() + ); } //in theory, we could get a pile of unused entries in this array, but only if From 8c78a19c952c6150163aad7b19908bdf88f426e0 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Thu, 23 Dec 2021 14:56:06 -0500 Subject: [PATCH 530/624] [Bug Fix] Pick Lock was allowing skillups on doors above player skill (#1815) * [Bux Fix] Pick Lock was allowing skillups on doors above player skill * Fixed indentation * Fix indentation #2 - I am not so bright :( * Further refine messages for pick lock to match live * sql to make pot pick locks book pickable by skill 1 and skillup --- .../2021_11_28_pot_pick_locks_book.sql | 1 + zone/client.cpp | 1 + zone/client.h | 2 ++ zone/doors.cpp | 20 ++++++++++++++++--- 4 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 utils/sql/git/optional/2021_11_28_pot_pick_locks_book.sql diff --git a/utils/sql/git/optional/2021_11_28_pot_pick_locks_book.sql b/utils/sql/git/optional/2021_11_28_pot_pick_locks_book.sql new file mode 100644 index 000000000..f7c2c55e3 --- /dev/null +++ b/utils/sql/git/optional/2021_11_28_pot_pick_locks_book.sql @@ -0,0 +1 @@ +UPDATE doors SET lockpick=1 WHERE doorid=46 AND zone="potranquility"; diff --git a/zone/client.cpp b/zone/client.cpp index 3e56614f0..5e293dd0e 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -154,6 +154,7 @@ Client::Client(EQStreamInterface* ieqs) TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000), charm_update_timer(6000), rest_timer(1), + pick_lock_timer(1000), charm_class_attacks_timer(3000), charm_cast_timer(3500), qglobal_purge_timer(30000), diff --git a/zone/client.h b/zone/client.h index cd58da895..a39ae4341 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1528,6 +1528,7 @@ public: void UpdateMercLevel(); void CheckMercSuspendTimer(); Timer* GetMercTimer() { return &merc_timer; }; + Timer* GetPickLockTimer() { return &pick_lock_timer; }; const char* GetRacePlural(Client* client); const char* GetClassPlural(Client* client); @@ -1869,6 +1870,7 @@ private: Timer consent_throttle_timer; Timer dynamiczone_removal_timer; Timer task_request_timer; + Timer pick_lock_timer; Timer heroforge_wearchange_timer; diff --git a/zone/doors.cpp b/zone/doors.cpp index 35eb92d5c..ef785b8d7 100644 --- a/zone/doors.cpp +++ b/zone/doors.cpp @@ -288,8 +288,11 @@ void Doors::HandleClick(Client* sender, uint8 trigger) { /** * Key required + * If using a lock_pick_item leave messaging to that code below */ - sender->Message(Chat::LightBlue, "This is locked..."); + if (lock_pick_item == nullptr && !IsDoorOpen()) { + sender->Message(Chat::LightBlue, "This is locked..."); + } /** * GM can always open locks @@ -335,19 +338,30 @@ void Doors::HandleClick(Client* sender, uint8 trigger) { */ else if (lock_pick_item != nullptr) { if (sender->GetSkill(EQ::skills::SkillPickLock)) { + Timer* pick_lock_timer = sender->GetPickLockTimer(); if (lock_pick_item->GetItem()->ItemType == EQ::item::ItemTypeLockPick) { + if (!pick_lock_timer->Check()) { + // Stop full scale mad spamming + safe_delete(outapp); + return; + } + float player_pick_lock_skill = sender->GetSkill(EQ::skills::SkillPickLock); - sender->CheckIncreaseSkill(EQ::skills::SkillPickLock, nullptr, 1); LogSkills("Client has lockpicks: skill=[{}]", player_pick_lock_skill); if (GetLockpick() <= player_pick_lock_skill) { + + // Stop full scale spamming + pick_lock_timer->Start(1000, true); + if (!IsDoorOpen()) { + sender->CheckIncreaseSkill(EQ::skills::SkillPickLock, nullptr, 1); move_door_packet->action = static_cast(invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR); + sender->MessageString(Chat::LightBlue, DOORS_SUCCESSFUL_PICK); } else { move_door_packet->action = static_cast(invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR); } - sender->MessageString(Chat::LightBlue, DOORS_SUCCESSFUL_PICK); } else { sender->MessageString(Chat::LightBlue, DOORS_INSUFFICIENT_SKILL); safe_delete(outapp); From 4fbb98a5f70b8a4d9326e741aa4841576ed82fa4 Mon Sep 17 00:00:00 2001 From: mmcgarvey Date: Thu, 23 Dec 2021 14:57:53 -0500 Subject: [PATCH 531/624] [Skills] Make Tracking Skill Configurable (#1784) Added 1 rule per class that defines tracking distance multiplier for that class Kept the defaults of 12 for ranger, 10 for druid, and 7 for bard Created 1 method for determining class tracking distance multiplier Created 1 method for determining if a class can track, based on multiplier Updated tracking logic to use these methods to determine whether a tracking packet should and can be sent or not. --- common/ruletypes.h | 16 ++++++++++++++++ zone/client.cpp | 43 ++++++++++++++++++++++++++++++++++++++++++ zone/client.h | 5 +++++ zone/client_packet.cpp | 8 ++++---- zone/entity.cpp | 8 ++------ 5 files changed, 70 insertions(+), 10 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index de97a3183..924e79875 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -174,6 +174,22 @@ RULE_INT(Character, ResurrectionSicknessSpellID, 756, "756 is Default Resurrecti RULE_BOOL(Character, EnableBardMelody, true, "Enable Bard /melody by default, to disable change to false for a classic experience.") RULE_BOOL(Character, EnableRangerAutoFire, true, "Enable Ranger /autofire by default, to disable change to false for a classic experience.") RULE_BOOL(Character, EnableTGB, true, "Enable /tgb (Target Group Buff) by default, to disable change to false for a classic experience.") +RULE_INT(Character, WarriorTrackingDistanceMultiplier, 0, "If you want warriors to be able to track, increase this above 0. 0 disables tracking packets.") +RULE_INT(Character, ClericTrackingDistanceMultiplier, 0, "If you want clerics to be able to track, increase this above 0. 0 disables tracking packets.") +RULE_INT(Character, PaladinTrackingDistanceMultiplier, 0, "If you want paladins to be able to track, increase this above 0. 0 disables tracking packets.") +RULE_INT(Character, RangerTrackingDistanceMultiplier, 12, "If you want rangers to be able to track, increase this above 0. 0 disables tracking packets.") +RULE_INT(Character, ShadowKnightTrackingDistanceMultiplier, 0, "If you want shadow knights to be able to track, increase this above 0. 0 disables tracking packets.") +RULE_INT(Character, DruidTrackingDistanceMultiplier, 10, "If you want druids to be able to track, increase this above 0. 0 disables tracking packets.") +RULE_INT(Character, MonkTrackingDistanceMultiplier, 0, "If you want monks to be able to track, increase this above 0. 0 disables tracking packets.") +RULE_INT(Character, BardTrackingDistanceMultiplier, 7, "If you want bards to be able to track, increase this above 0. 0 disables tracking packets.") +RULE_INT(Character, RogueTrackingDistanceMultiplier, 0, "If you want rogues to be able to track, increase this above 0. 0 disables tracking packets.") +RULE_INT(Character, ShamanTrackingDistanceMultiplier, 0, "If you want shaman to be able to track, increase this above 0. 0 disables tracking packets.") +RULE_INT(Character, NecromancerTrackingDistanceMultiplier, 0, "If you want necromancers to be able to track, increase this above 0. 0 disables tracking packets.") +RULE_INT(Character, WizardTrackingDistanceMultiplier, 0, "If you want wizards to be able to track, increase this above 0. 0 disables tracking packets.") +RULE_INT(Character, MagicianTrackingDistanceMultiplier, 0, "If you want magicians to be able to track, increase this above 0. 0 disables tracking packets.") +RULE_INT(Character, EnchanterTrackingDistanceMultiplier, 0, "If you want enchanters to be able to track, increase this above 0. 0 disables tracking packets.") +RULE_INT(Character, BeastlordTrackingDistanceMultiplier, 0, "If you want beastlords to be able to track, increase this above 0. 0 disables tracking packets.") +RULE_INT(Character, BerserkerTrackingDistanceMultiplier, 0, "If you want berserkers to be able to track, increase this above 0. 0 disables tracking packets.") RULE_CATEGORY_END() RULE_CATEGORY(Mercs) diff --git a/zone/client.cpp b/zone/client.cpp index 5e293dd0e..397a26c88 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -10869,3 +10869,46 @@ uint16 Client::LearnDisciplines(uint8 min_level, uint8 max_level) return learned_disciplines; } + +uint16 Client::GetClassTrackingDistanceMultiplier(uint16 class_) { + switch (class_) { + case WARRIOR: + return RuleI(Character, WarriorTrackingDistanceMultiplier); + case CLERIC: + return RuleI(Character, ClericTrackingDistanceMultiplier); + case PALADIN: + return RuleI(Character, PaladinTrackingDistanceMultiplier); + case RANGER: + return RuleI(Character, RangerTrackingDistanceMultiplier); + case SHADOWKNIGHT: + return RuleI(Character, ShadowKnightTrackingDistanceMultiplier); + case DRUID: + return RuleI(Character, DruidTrackingDistanceMultiplier); + case MONK: + return RuleI(Character, MonkTrackingDistanceMultiplier); + case BARD: + return RuleI(Character, BardTrackingDistanceMultiplier); + case ROGUE: + return RuleI(Character, RogueTrackingDistanceMultiplier); + case SHAMAN: + return RuleI(Character, ShamanTrackingDistanceMultiplier); + case NECROMANCER: + return RuleI(Character, NecromancerTrackingDistanceMultiplier); + case WIZARD: + return RuleI(Character, WizardTrackingDistanceMultiplier); + case MAGICIAN: + return RuleI(Character, MagicianTrackingDistanceMultiplier); + case ENCHANTER: + return RuleI(Character, EnchanterTrackingDistanceMultiplier); + case BEASTLORD: + return RuleI(Character, BeastlordTrackingDistanceMultiplier); + case BERSERKER: + return RuleI(Character, BerserkerTrackingDistanceMultiplier); + default: + return 0; + } +} + +bool Client::CanThisClassTrack() { + return (GetClassTrackingDistanceMultiplier(GetClass()) > 0) ? true : false; +} diff --git a/zone/client.h b/zone/client.h index a39ae4341..3254e9899 100644 --- a/zone/client.h +++ b/zone/client.h @@ -810,6 +810,11 @@ public: uint16 ScribeSpells(uint8 min_level, uint8 max_level); uint16 LearnDisciplines(uint8 min_level, uint8 max_level); + // Configurable Tracking Skill + uint16 GetClassTrackingDistanceMultiplier(uint16 class_); + + bool CanThisClassTrack(); + // defer save used when bulk saving void UnscribeSpell(int slot, bool update_client = true, bool defer_save = false); void UnscribeSpellAll(bool update_client = true); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 6afc3da5d..3c6801e8c 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -14064,8 +14064,9 @@ void Client::Handle_OP_TGB(const EQApplicationPacket *app) void Client::Handle_OP_Track(const EQApplicationPacket *app) { - if (GetClass() != RANGER && GetClass() != DRUID && GetClass() != BARD) + if (!CanThisClassTrack()) { return; + } if (GetSkill(EQ::skills::SkillTracking) == 0) SetSkill(EQ::skills::SkillTracking, 1); @@ -14080,10 +14081,9 @@ void Client::Handle_OP_Track(const EQApplicationPacket *app) void Client::Handle_OP_TrackTarget(const EQApplicationPacket *app) { - int PlayerClass = GetClass(); - - if ((PlayerClass != RANGER) && (PlayerClass != DRUID) && (PlayerClass != BARD)) + if (!CanThisClassTrack()) { return; + } if (app->size != sizeof(TrackTarget_Struct)) { diff --git a/zone/entity.cpp b/zone/entity.cpp index d5b22c2fe..7e66929e3 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -3561,12 +3561,8 @@ bool EntityList::MakeTrackPacket(Client *client) uint32 distance = 0; float MobDistance; - if (client->GetClass() == DRUID) - distance = (client->GetSkill(EQ::skills::SkillTracking) * 10); - else if (client->GetClass() == RANGER) - distance = (client->GetSkill(EQ::skills::SkillTracking) * 12); - else if (client->GetClass() == BARD) - distance = (client->GetSkill(EQ::skills::SkillTracking) * 7); + distance = (client->GetSkill(EQ::skills::SkillTracking) * client->GetClassTrackingDistanceMultiplier(client->GetClass())); + if (distance <= 0) return false; if (distance < 300) From 4f0e9945c6ae8ae9076195e9f51276b23aa59f09 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Thu, 23 Dec 2021 15:01:56 -0500 Subject: [PATCH 532/624] [Combat] Allow npcs/pets to kick vs opponents requiring magic weapons if wearing magic booties. (#1868) * [Pets/NPC Kick] Allow pets/npcs kick vs mobs that req magic if using magic boots * Backout accidental change to bash --- zone/special_attacks.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 1e86a6792..5116eb481 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -1617,6 +1617,8 @@ void NPC::DoClassAttacks(Mob *target) { bool ca_time = classattack_timer.Check(false); bool ka_time = knightattack_timer.Check(false); + const EQ::ItemData* boots = database.GetItem(equipment[EQ::invslot::slotFeet]); + //only check attack allowed if we are going to do something if((taunt_time || ca_time || ka_time) && !IsAttackAllowed(target)) return; @@ -1688,7 +1690,7 @@ void NPC::DoClassAttacks(Mob *target) { DoAnim(animKick, 0, false); int32 dmg = GetBaseSkillDamage(EQ::skills::SkillKick); - if (GetWeaponDamage(target, (const EQ::ItemData*)nullptr) <= 0) { + if (GetWeaponDamage(target, boots) <= 0) { dmg = DMG_INVULNERABLE; } @@ -1739,7 +1741,7 @@ void NPC::DoClassAttacks(Mob *target) { DoAnim(animKick, 0, false); int32 dmg = GetBaseSkillDamage(EQ::skills::SkillKick); - if (GetWeaponDamage(target, (const EQ::ItemData*)nullptr) <= 0) + if (GetWeaponDamage(target, boots) <= 0) dmg = DMG_INVULNERABLE; reuse = (KickReuseTime + 3) * 1000; From c79fbb99aa66d1b42748f4300c29e7cba3b17d34 Mon Sep 17 00:00:00 2001 From: mmcgarvey Date: Thu, 23 Dec 2021 15:20:15 -0500 Subject: [PATCH 533/624] [Shared Tasks] Cross Zone Remove Fix (#1740) * [Shared Tasks] Cross Zone Remove Fix Why: The cross_zone_remove_task quest methods were not removing from shared_task_members database table and were not clearing shared task cache. This resulted in a situation where a character could not request other shared tasks. What: Shamelessly copied shared task logic from ClientTaskState::CancelTask into ClientTaskState::RemoveTaskByTaskID * What: Instead of copying code from CancelTask into RemoveTaskByTaskID, it is better for code maintenance to simply call CancelTask from RemoveTaskByTaskID. This is cleaner. Note: I chose to be explicit with the remove_from_db parameter, despite true being the default. I tend to do this to protect from the default value changing in the future. * [Shared Tasks] RemoveTaskByTaskID Cleanup Removed unused variables. Distinguished log messages for Shared Tasks from regular Tasks. --- zone/task_client_state.cpp | 44 ++++++-------------------------------- 1 file changed, 6 insertions(+), 38 deletions(-) diff --git a/zone/task_client_state.cpp b/zone/task_client_state.cpp index 8017a0fec..264ad2456 100644 --- a/zone/task_client_state.cpp +++ b/zone/task_client_state.cpp @@ -2196,58 +2196,26 @@ void ClientTaskState::RemoveTask(Client *client, int sequence_number, TaskType t void ClientTaskState::RemoveTaskByTaskID(Client *client, uint32 task_id) { - auto task_type = task_manager->GetTaskType(task_id); - int character_id = client->CharacterID(); - - CharacterActivitiesRepository::DeleteWhere( - database, - fmt::format("charid = {} AND taskid = {}", character_id, task_id) - ); - - CharacterTasksRepository::DeleteWhere( - database, - fmt::format("charid = {} AND taskid = {} AND type = {}", character_id, task_id, (int) task_type) - ); - - switch (task_type) { + switch (task_manager->GetTaskType(task_id)) { case TaskType::Task: { if (m_active_task.task_id == task_id) { - auto outapp = new EQApplicationPacket(OP_CancelTask, sizeof(CancelTask_Struct)); - CancelTask_Struct *cts = (CancelTask_Struct *) outapp->pBuffer; - cts->SequenceNumber = TASKSLOTTASK; - cts->type = static_cast(task_type); LogTasks("[UPDATE] RemoveTaskByTaskID found Task [{}]", task_id); - client->QueuePacket(outapp); - safe_delete(outapp); - m_active_task.task_id = TASKSLOTEMPTY; + CancelTask(client, TASKSLOTTASK, TaskType::Task, true); } break; } case TaskType::Shared: { if (m_active_shared_task.task_id == task_id) { - auto outapp = new EQApplicationPacket(OP_CancelTask, sizeof(CancelTask_Struct)); - CancelTask_Struct *cts = (CancelTask_Struct *) outapp->pBuffer; - cts->SequenceNumber = TASKSLOTSHAREDTASK; - cts->type = static_cast(task_type); - LogTasks("[UPDATE] RemoveTaskByTaskID found Task [{}]", task_id); - client->QueuePacket(outapp); - safe_delete(outapp); - m_active_shared_task.task_id = TASKSLOTEMPTY; + LogTasks("[UPDATE] RemoveTaskByTaskID found Shared Task [{}]", task_id); + CancelTask(client, TASKSLOTSHAREDTASK, TaskType::Shared, true); } break; } case TaskType::Quest: { for (int active_quest = 0; active_quest < MAXACTIVEQUESTS; active_quest++) { - if (m_active_quests[active_quest].task_id == task_id) { - auto outapp = new EQApplicationPacket(OP_CancelTask, sizeof(CancelTask_Struct)); - CancelTask_Struct *cts = (CancelTask_Struct *) outapp->pBuffer; - cts->SequenceNumber = active_quest; - cts->type = static_cast(task_type); + if (m_active_quests[active_quest].task_id == task_id) { LogTasks("[UPDATE] RemoveTaskByTaskID found Quest [{}] at index [{}]", task_id, active_quest); - m_active_quests[active_quest].task_id = TASKSLOTEMPTY; - m_active_task_count--; - client->QueuePacket(outapp); - safe_delete(outapp); + CancelTask(client, active_quest, TaskType::Quest, true); } } } From 652ea89deacf1a528161f8a972449703476a532a Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 23 Dec 2021 15:21:46 -0500 Subject: [PATCH 534/624] [Rule] Added rule to disable SPA 173 from making player immune to enrage. (#1897) * immune enrage rule * [Rule] Added rule to disable SPA 173 from making player immune to enrage. spelling oops --- common/ruletypes.h | 1 + zone/attack.cpp | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 924e79875..67d898476 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -508,6 +508,7 @@ RULE_INT(Combat, SneakPullAssistRange, 400, "Modified range of assist for sneak RULE_BOOL(Combat, Classic2HBAnimation, false, "2HB will use the 2 hand piercing animation instead of the overhead slashing animation") RULE_BOOL(Combat, ArcheryConsumesAmmo, true, "Set to false to disable Archery Ammo Consumption") RULE_BOOL(Combat, ThrowingConsumesAmmo, true, "Set to false to disable Throwing Ammo Consumption") +RULE_BOOL(Combat, ImmuneToEnrageFromRiposteSpellEffect, true, "Set to false to disable SPA 173 SE_RiposteChance from making those with the effect on them immune to enrage") RULE_CATEGORY_END() RULE_CATEGORY(NPC) diff --git a/zone/attack.cpp b/zone/attack.cpp index 5568e6e15..5fec09739 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -432,7 +432,11 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit) } // riposte -- it may seem crazy, but if the attacker has SPA 173 on them, they are immune to Ripo - bool ImmuneRipo = attacker->aabonuses.RiposteChance || attacker->spellbonuses.RiposteChance || attacker->itembonuses.RiposteChance || attacker->IsEnraged(); + bool ImmuneRipo = false; + if (RuleB(Combat, ImmuneToEnrageFromRiposteSpellEffect)) { + ImmuneRipo = attacker->aabonuses.RiposteChance || attacker->spellbonuses.RiposteChance || attacker->itembonuses.RiposteChance || attacker->IsEnraged(); + } + // Need to check if we have something in MainHand to actually attack with (or fists) if (hit.hand != EQ::invslot::slotRange && (CanThisClassRiposte() || IsEnraged()) && InFront && !ImmuneRipo) { if (IsEnraged()) { From 5457f3065943e7ab3b594bece86f481e2447bfbe Mon Sep 17 00:00:00 2001 From: mmcgarvey Date: Thu, 23 Dec 2021 15:43:02 -0500 Subject: [PATCH 535/624] [Spells] Instant Heals honor IgnoreSpellDmgLvlRestriction (#1888) Why: Heal Over Time spells honor the Spells:IgnoreSpellDmgLvlRestriction rule, shouldn't instant heals honor this rule too? The fix: Added a check for Spells:IgnoreSpellDmgLvlRestriction in the GetActSpellHealing method. --- zone/effects.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index 0712ec6c5..73d2bf434 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -362,8 +362,12 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { value += GetFocusEffect(focusFcHealAmtCrit, spell_id); //SPA 396 Add before critical - if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) { - value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, base_value); //Item Heal Amt Add before critical + //Using IgnoreSpellDmgLvlRestriction to also allow healing to scale + if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt) { + value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, base_value);//Item Heal Amt Add before critical + } + else if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) { + value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, base_value);//Item Heal Amt Add before critical } if (target) { From 6a7782ab8de4a5f230384cd72cdc25dbcdb6ce7b Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Thu, 23 Dec 2021 14:47:24 -0600 Subject: [PATCH 536/624] [Doors] Ignore Doors that Have Non-Zero Trigger or Door Param (#1899) --- zone/mob_ai.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 1c1be6b69..1a071cd06 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1000,6 +1000,14 @@ void Mob::AI_Process() { continue; } + if (door->GetTriggerDoorID() > 0) { + continue; + } + + if (door->GetDoorParam() > 0) { + continue; + } + float distance = DistanceSquared(m_Position, door->GetPosition()); float distance_scan_door_open = 20; From 7f23c93ce5df5c8523ff1d50e10d370970e89257 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 24 Dec 2021 13:46:17 -0500 Subject: [PATCH 537/624] [Commands] Cleanup #setadventurepoints Command. (#1901) - Cleanup message and logic. --- zone/command.cpp | 2 +- zone/gm_commands/set_adventure_points.cpp | 82 ++++++++++++++++++++--- 2 files changed, 72 insertions(+), 12 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 166f2cc81..be3547754 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -324,7 +324,7 @@ int command_init(void) command_add("serverrules", "- Read this server's rules", AccountStatus::Player, command_serverrules) || command_add("setaapts", "[AA|Group|Raid] [AA Amount] - Set your or your player target's Available AA Points by Type", AccountStatus::GMAdmin, command_setaapts) || command_add("setaaxp", "[AA|Group|Raid] [AA Experience] - Set your or your player target's AA Experience by Type", AccountStatus::GMAdmin, command_setaaxp) || - command_add("setadventurepoints", "- Set your or your player target's available adventure points", AccountStatus::GMLeadAdmin, command_set_adventure_points) || + command_add("setadventurepoints", "[Theme] [Points] - Set your or your player target's available Adventure Points by Theme", AccountStatus::GMLeadAdmin, command_set_adventure_points) || command_add("setaltcurrency", "[Currency ID] [Amount] - Set your or your target's available Alternate Currency by Currency ID", AccountStatus::GMAdmin, command_setaltcurrency) || command_add("setanim", "[Animation ID (IDs are 0 to 4)] - Set target's appearance to Animation ID", AccountStatus::GMMgmt, command_setanim) || command_add("setcrystals", "[value] - Set your or your player target's available radiant or ebon crystals", AccountStatus::GMAdmin, command_setcrystals) || diff --git a/zone/gm_commands/set_adventure_points.cpp b/zone/gm_commands/set_adventure_points.cpp index a9b1f47e3..fff6eec9a 100755 --- a/zone/gm_commands/set_adventure_points.cpp +++ b/zone/gm_commands/set_adventure_points.cpp @@ -1,24 +1,84 @@ #include "../client.h" +#include "../../common/data_verification.h" void command_set_adventure_points(Client *c, const Seperator *sep) { - Client *t = c; + int arguments = sep->argnum; + + if ( + !arguments || + !sep->IsNumber(1) || + !sep->IsNumber(2) + ) { + c->Message(Chat::White, "Usage: #setadventurepoints [Theme] [Points]"); + c->Message(Chat::White, "Valid themes are as follows."); + auto theme_map = EQ::constants::GetLDoNThemeMap(); + for (const auto& theme : theme_map) { + c->Message( + Chat::White, + fmt::format( + "Theme {} | {}", + theme.first, + theme.second + ).c_str() + ); + } + c->Message(Chat::White, "Note: Theme 0 splits the points evenly across all Themes."); + return; + } + auto target = c; if (c->GetTarget() && c->GetTarget()->IsClient()) { - t = c->GetTarget()->CastToClient(); + target = c->GetTarget()->CastToClient(); } - if (!sep->arg[1][0]) { - c->Message(Chat::White, "Usage: #setadventurepoints [theme] [points]"); + auto theme_id = std::stoul(sep->arg[1]); + if (!EQ::ValueWithin(theme_id, LDoNThemes::Unused, LDoNThemes::TAK)) { + c->Message(Chat::White, "Valid themes are as follows."); + auto theme_map = EQ::constants::GetLDoNThemeMap(); + for (const auto& theme : theme_map) { + c->Message( + Chat::White, + fmt::format( + "Theme {} | {}", + theme.first, + theme.second + ).c_str() + ); + } + c->Message(Chat::White, "Note: Theme 0 splits the points evenly across all Themes."); return; } - if (!sep->IsNumber(1) || !sep->IsNumber(2)) { - c->Message(Chat::White, "Usage: #setadventurepoints [theme] [points]"); - return; - } + auto points = std::stoi(sep->arg[2]); - c->Message(Chat::White, "Updating adventure points for %s", t->GetName()); - t->UpdateLDoNPoints(atoi(sep->arg[1]), atoi(sep->arg[2])); + c->Message( + Chat::White, + fmt::format( + "{} for {}.", + ( + theme_id == LDoNThemes::Unused ? + fmt::format( + "Splitting {} Points Evenly", + points + ) : + fmt::format( + "Adding {} {} Points", + points, + EQ::constants::GetLDoNThemeName(theme_id) + ) + ), + ( + c == target ? + "Yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ) + ).c_str() + ); + + target->UpdateLDoNPoints(theme_id, points); } - From 323b35989c184e39c3988430e361c43625606a59 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Mon, 27 Dec 2021 11:33:57 -0500 Subject: [PATCH 538/624] [Spells] Implemented SPA 281 SE_PetFeignMinion (#1900) * start * update * debugs in * test * clean up * debugs removed * Update mob_ai.cpp * Update spdat.h * [Spells] Implemented SPA 281 SE_PetFeignMinion debug remoevd * [Spells] Implemented SPA 281 SE_PetFeignMinion npc forget timer * [Spells] Implemented SPA 281 SE_PetFeignMinion --- common/spdat.h | 3 ++- zone/aggro.cpp | 49 +++++++++++++++++++++++++++-------------- zone/attack.cpp | 2 +- zone/bonuses.cpp | 4 +++- zone/client.cpp | 18 --------------- zone/client.h | 5 ----- zone/client_packet.cpp | 42 ++++++++++++++++++++++++++++++++++- zone/entity.cpp | 25 ++++++++++++++------- zone/entity.h | 2 +- zone/mob.cpp | 21 ++++++++++++++++++ zone/mob.h | 17 +++++++++----- zone/mob_ai.cpp | 50 +++++++++++++++++++++++++----------------- zone/spell_effects.cpp | 7 +++--- 13 files changed, 164 insertions(+), 81 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 9358bde69..d8b1bd188 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -26,6 +26,7 @@ #define SPELLBOOK_UNKNOWN 0xFFFFFFFF //player profile spells are 32 bit //some spell IDs which will prolly change, but are needed +#define SPELL_LIFEBURN 2755 #define SPELL_LEECH_TOUCH 2766 #define SPELL_LAY_ON_HANDS 87 #define SPELL_HARM_TOUCH 88 @@ -998,7 +999,7 @@ typedef enum { #define SE_FinishingBlow 278 // implemented[AA] - chance to do massive damage under 10% HP (base1 = chance, base2 = damage) #define SE_Flurry 279 // implemented #define SE_PetFlurry 280 // implemented[AA] -#define SE_FeignedMinion 281 // *not implemented[AA] ability allows you to instruct your pet to feign death via the '/pet feign' command. value = succeed chance +#define SE_FeignedMinion 281 // implemented, ability allows you to instruct your pet to feign death via the '/pet feign' command, base: succeed chance, limit: none, max: none, Note: Only implemented as an AA. #define SE_ImprovedBindWound 282 // implemented[AA] - increase bind wound amount by percent. #define SE_DoubleSpecialAttack 283 // implemented[AA] - Chance to perform second special attack as monk //#define SE_LoHSetHeal 284 // not used diff --git a/zone/aggro.cpp b/zone/aggro.cpp index ba217960e..3d706efde 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -1402,45 +1402,62 @@ int32 Mob::CheckHealAggroAmount(uint16 spell_id, Mob *target, uint32 heal_possib return std::max(0, AggroAmount); } -void Mob::AddFeignMemory(Client* attacker) { - if(feign_memory_list.empty() && AI_feign_remember_timer != nullptr) +void Mob::AddFeignMemory(Mob* attacker) { + if (feign_memory_list.empty() && AI_feign_remember_timer != nullptr) { AI_feign_remember_timer->Start(AIfeignremember_delay); - feign_memory_list.insert(attacker->CharacterID()); + } + + if (attacker) { + feign_memory_list.insert(attacker->GetID()); + } } -void Mob::RemoveFromFeignMemory(Client* attacker) { - feign_memory_list.erase(attacker->CharacterID()); - if(feign_memory_list.empty() && AI_feign_remember_timer != nullptr) +void Mob::RemoveFromFeignMemory(Mob* attacker) { + + if (!attacker) { + return; + } + + feign_memory_list.erase(attacker->GetID()); + if (feign_memory_list.empty() && AI_feign_remember_timer != nullptr) { AI_feign_remember_timer->Disable(); + } if(feign_memory_list.empty()) { minLastFightingDelayMoving = RuleI(NPC, LastFightingDelayMovingMin); maxLastFightingDelayMoving = RuleI(NPC, LastFightingDelayMovingMax); - if(AI_feign_remember_timer != nullptr) + if (AI_feign_remember_timer != nullptr) { AI_feign_remember_timer->Disable(); + } } } void Mob::ClearFeignMemory() { - auto RememberedCharID = feign_memory_list.begin(); - while (RememberedCharID != feign_memory_list.end()) + auto remembered_feigned_mobid = feign_memory_list.begin(); + while (remembered_feigned_mobid != feign_memory_list.end()) { - Client* remember_client = entity_list.GetClientByCharID(*RememberedCharID); - if(remember_client != nullptr) //Still in zone - remember_client->RemoveXTarget(this, false); - ++RememberedCharID; + Mob* remembered_mob = entity_list.GetMob(*remembered_feigned_mobid); + if (remembered_mob->IsClient() && remembered_mob != nullptr) { //Still in zone + remembered_mob->CastToClient()->RemoveXTarget(this, false); + } + ++remembered_feigned_mobid; } feign_memory_list.clear(); minLastFightingDelayMoving = RuleI(NPC, LastFightingDelayMovingMin); maxLastFightingDelayMoving = RuleI(NPC, LastFightingDelayMovingMax); - if(AI_feign_remember_timer != nullptr) + if (AI_feign_remember_timer != nullptr) { AI_feign_remember_timer->Disable(); + } } -bool Mob::IsOnFeignMemory(Client *attacker) const +bool Mob::IsOnFeignMemory(Mob *attacker) const { - return feign_memory_list.find(attacker->CharacterID()) != feign_memory_list.end(); + if (!attacker) { + return 0; + } + + return feign_memory_list.find(attacker->GetID()) != feign_memory_list.end(); } bool Mob::PassCharismaCheck(Mob* caster, uint16 spell_id) { diff --git a/zone/attack.cpp b/zone/attack.cpp index 5fec09739..ff9f85264 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2813,7 +2813,7 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b hate_list.AddEntToHateList(other, hate, damage, bFrenzy, !iBuffTic); - if (other->IsClient() && !on_hatelist && !IsOnFeignMemory(other->CastToClient())) + if (other->IsClient() && !on_hatelist && !IsOnFeignMemory(other)) other->CastToClient()->AddAutoXTarget(this); #ifdef BOTS diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 04ab98239..4cb4047e9 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1571,8 +1571,10 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) break; case SE_FeignedMinion: - if (newbon->FeignedMinionChance < base_value) + if (newbon->FeignedMinionChance < base_value) { newbon->FeignedMinionChance = base_value; + } + newbon->PetCommands[PET_FEIGN] = true; break; case SE_AdditionalAura: diff --git a/zone/client.cpp b/zone/client.cpp index 397a26c88..62ffafd17 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -145,7 +145,6 @@ Client::Client(EQStreamInterface* ieqs) global_channel_timer(1000), fishing_timer(8000), endupkeep_timer(1000), - forget_timer(0), autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000), client_scan_npc_aggro_timer(RuleI(Aggro, ClientAggroCheckIdleInterval)), client_zone_wide_full_position_update_timer(5 * 60 * 1000), @@ -186,7 +185,6 @@ Client::Client(EQStreamInterface* ieqs) character_id = 0; conn_state = NoPacketsReceived; client_data_loaded = false; - feigned = false; berserk = false; dead = false; eqs = ieqs; @@ -2677,22 +2675,6 @@ void Client::MemorizeSpell(uint32 slot,uint32 spellid,uint32 scribing, uint32 re safe_delete(outapp); } -void Client::SetFeigned(bool in_feigned) { - if (in_feigned) - { - if(RuleB(Character, FeignKillsPet)) - { - SetPet(0); - } - SetHorseId(0); - entity_list.ClearFeignAggro(this); - forget_timer.Start(FeignMemoryDuration); - } else { - forget_timer.Disable(); - } - feigned=in_feigned; - } - void Client::LogMerchant(Client* player, Mob* merchant, uint32 quantity, uint32 price, const EQ::ItemData* item, bool buying) { if(!player || !merchant || !item) diff --git a/zone/client.h b/zone/client.h index 3254e9899..2ba96bd30 100644 --- a/zone/client.h +++ b/zone/client.h @@ -835,9 +835,6 @@ public: inline uint8 GetBecomeNPCLevel() const { return npclevel; } inline void SetBecomeNPC(bool flag) { npcflag = flag; } inline void SetBecomeNPCLevel(uint8 level) { npclevel = level; } - void SetFeigned(bool in_feigned); - /// this cures timing issues cuz dead animation isn't done but server side feigning is? - inline bool GetFeigned() const { return(feigned); } EQStreamInterface* Connection() { return eqs; } #ifdef PACKET_PROFILER void DumpPacketProfile() { if(eqs) eqs->DumpPacketProfile(); } @@ -1850,7 +1847,6 @@ private: Timer global_channel_timer; Timer fishing_timer; Timer endupkeep_timer; - Timer forget_timer; // our 2 min everybody forgets you timer Timer autosave_timer; Timer client_scan_npc_aggro_timer; Timer client_zone_wide_full_position_update_timer; @@ -1890,7 +1886,6 @@ private: bool npcflag; uint8 npclevel; - bool feigned; bool bZoning; bool tgb; bool instalog; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 3c6801e8c..cad61cdf3 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -10255,6 +10255,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) if ((mypet->GetPetType() == petAnimation && aabonuses.PetCommands[PetCommand]) || mypet->GetPetType() != petAnimation) { if (target != this && DistanceSquaredNoZ(mypet->GetPosition(), target->GetPosition()) <= (RuleR(Pets, AttackCommandRange)*RuleR(Pets, AttackCommandRange))) { + mypet->SetFeigned(false); if (mypet->IsPetStop()) { mypet->SetPetStop(false); SetPetCommandState(PET_BUTTON_STOP, 0); @@ -10301,6 +10302,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) if ((mypet->GetPetType() == petAnimation && aabonuses.PetCommands[PetCommand]) || mypet->GetPetType() != petAnimation) { if (GetTarget() != this && DistanceSquaredNoZ(mypet->GetPosition(), GetTarget()->GetPosition()) <= (RuleR(Pets, AttackCommandRange)*RuleR(Pets, AttackCommandRange))) { + mypet->SetFeigned(false); if (mypet->IsPetStop()) { mypet->SetPetStop(false); SetPetCommandState(PET_BUTTON_STOP, 0); @@ -10376,6 +10378,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) if (mypet->IsNPC()) { // Set Sit button to unpressed - send stand anim/end hpregen + mypet->SetFeigned(false); SetPetCommandState(PET_BUTTON_SIT, 0); mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); @@ -10396,6 +10399,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF if ((mypet->GetPetType() == petAnimation && aabonuses.PetCommands[PetCommand]) || mypet->GetPetType() != petAnimation) { + mypet->SetFeigned(false); mypet->SayString(this, Chat::PetResponse, PET_FOLLOWING); mypet->SetPetOrder(SPO_Follow); @@ -10443,6 +10447,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF if ((mypet->GetPetType() == petAnimation && aabonuses.PetCommands[PetCommand]) || mypet->GetPetType() != petAnimation) { + mypet->SetFeigned(false); mypet->SayString(this, Chat::PetResponse, PET_GUARDME_STRING); mypet->SetPetOrder(SPO_Follow); @@ -10463,12 +10468,14 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) if ((mypet->GetPetType() == petAnimation && aabonuses.PetCommands[PetCommand]) || mypet->GetPetType() != petAnimation) { if (mypet->GetPetOrder() == SPO_Sit) { + mypet->SetFeigned(false); mypet->SayString(this, Chat::PetResponse, PET_SIT_STRING); mypet->SetPetOrder(SPO_Follow); mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); } else { + mypet->SetFeigned(false); mypet->SayString(this, Chat::PetResponse, PET_SIT_STRING); mypet->SetPetOrder(SPO_Sit); mypet->SetRunAnimSpeed(0); @@ -10483,6 +10490,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF if ((mypet->GetPetType() == petAnimation && aabonuses.PetCommands[PetCommand]) || mypet->GetPetType() != petAnimation) { + mypet->SetFeigned(false); mypet->SayString(this, Chat::PetResponse, PET_SIT_STRING); SetPetCommandState(PET_BUTTON_SIT, 0); mypet->SetPetOrder(SPO_Follow); @@ -10494,6 +10502,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF if ((mypet->GetPetType() == petAnimation && aabonuses.PetCommands[PetCommand]) || mypet->GetPetType() != petAnimation) { + mypet->SetFeigned(false); mypet->SayString(this, Chat::PetResponse, PET_SIT_STRING); SetPetCommandState(PET_BUTTON_SIT, 1); mypet->SetPetOrder(SPO_Sit); @@ -10687,6 +10696,38 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) } break; } + + case PET_FEIGN: { + if (aabonuses.PetCommands[PetCommand] && mypet->IsNPC()) { + if (mypet->IsFeared()) + break; + + int pet_fd_chance = aabonuses.FeignedMinionChance; + if (zone->random.Int(0, 99) > pet_fd_chance) { + mypet->SetFeigned(false); + entity_list.MessageCloseString(this, false, 200, 10, STRING_FEIGNFAILED, mypet->GetCleanName()); + } + else { + bool immune_aggro = GetSpecialAbility(IMMUNE_AGGRO); + mypet->SetSpecialAbility(IMMUNE_AGGRO, 1); + mypet->WipeHateList(); + mypet->SetPetOrder(SPO_FeignDeath); + mypet->SetRunAnimSpeed(0); + mypet->StopNavigation(); + mypet->SendAppearancePacket(AT_Anim, ANIM_DEATH); + mypet->SetFeigned(true); + mypet->SetTarget(nullptr); + if (!mypet->UseBardSpellLogic()) { + mypet->InterruptSpell(); + } + + if (!immune_aggro) { + mypet->SetSpecialAbility(IMMUNE_AGGRO, 0); + } + } + } + break; + } case PET_STOP: { if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF @@ -10694,7 +10735,6 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) if (mypet->IsPetStop()) { mypet->SetPetStop(false); } else { - mypet->SetPetStop(true); mypet->StopNavigation(); mypet->SetTarget(nullptr); if (mypet->IsPetRegroup()) { diff --git a/zone/entity.cpp b/zone/entity.cpp index 7e66929e3..0179b3c54 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1515,10 +1515,10 @@ void EntityList::RemoveFromTargets(Mob *mob, bool RemoveFromXTargets) continue; if (RemoveFromXTargets) { - if (m->IsClient() && (mob->CheckAggro(m) || mob->IsOnFeignMemory(m->CastToClient()))) + if (m->IsClient() && (mob->CheckAggro(m) || mob->IsOnFeignMemory(m))) m->CastToClient()->RemoveXTarget(mob, false); // FadingMemories calls this function passing the client. - else if (mob->IsClient() && (m->CheckAggro(mob) || m->IsOnFeignMemory(mob->CastToClient()))) + else if (mob->IsClient() && (m->CheckAggro(mob) || m->IsOnFeignMemory(mob))) mob->CastToClient()->RemoveXTarget(m, false); } @@ -3488,7 +3488,7 @@ void EntityList::ClearFeignAggro(Mob *targ) auto it = npc_list.begin(); while (it != npc_list.end()) { // add Feign Memory check because sometimes weird stuff happens - if (it->second->CheckAggro(targ) || (targ->IsClient() && it->second->IsOnFeignMemory(targ->CastToClient()))) { + if (it->second->CheckAggro(targ) || (targ->IsClient() && it->second->IsOnFeignMemory(targ))) { if (it->second->GetSpecialAbility(IMMUNE_FEIGN_DEATH)) { ++it; continue; @@ -3514,22 +3514,31 @@ void EntityList::ClearFeignAggro(Mob *targ) it->second->RemoveFromHateList(targ); if (targ->IsClient()) { - if (it->second->GetLevel() >= 35 && zone->random.Roll(60)) - it->second->AddFeignMemory(targ->CastToClient()); - else + if (it->second->GetLevel() >= 35 && zone->random.Roll(60)) { + it->second->AddFeignMemory(targ); + } + else { targ->CastToClient()->RemoveXTarget(it->second, false); + } + } + else if (targ->IsPet()){ + if (it->second->GetLevel() >= 35 && zone->random.Roll(60)) { + it->second->AddFeignMemory(targ); + } } } ++it; } } -void EntityList::ClearZoneFeignAggro(Client *targ) +void EntityList::ClearZoneFeignAggro(Mob *targ) { auto it = npc_list.begin(); while (it != npc_list.end()) { it->second->RemoveFromFeignMemory(targ); - targ->CastToClient()->RemoveXTarget(it->second, false); + if (targ && targ->IsClient()) { + targ->CastToClient()->RemoveXTarget(it->second, false); + } ++it; } } diff --git a/zone/entity.h b/zone/entity.h index 0984932fe..542f95845 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -455,7 +455,7 @@ public: void ClearAggro(Mob* targ); void ClearWaterAggro(Mob* targ); void ClearFeignAggro(Mob* targ); - void ClearZoneFeignAggro(Client* targ); + void ClearZoneFeignAggro(Mob* targ); void AggroZone(Mob* who, uint32 hate = 0); bool Fighting(Mob* targ); diff --git a/zone/mob.cpp b/zone/mob.cpp index 87b315612..f1aebb7f2 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -109,6 +109,7 @@ Mob::Mob( stunned_timer(0), spun_timer(0), bardsong_timer(6000), + forget_timer(0), gravity_timer(1000), viral_timer(0), m_FearWalkTarget(-999999.0f, -999999.0f, -999999.0f), @@ -282,6 +283,8 @@ Mob::Mob( InitializeBuffSlots(); + feigned = false; + // clear the proc arrays for (int j = 0; j < MAX_PROCS; j++) { PermaProcs[j].spellID = SPELL_UNKNOWN; @@ -6505,6 +6508,24 @@ void Mob::ShieldAbilityClearVariables() } } +void Mob::SetFeigned(bool in_feigned) { + + if (in_feigned) { + if (IsClient()) { + if (RuleB(Character, FeignKillsPet)){ + SetPet(0); + } + CastToClient()->SetHorseId(0); + } + entity_list.ClearFeignAggro(this); + forget_timer.Start(FeignMemoryDuration); + } + else { + forget_timer.Disable(); + } + feigned = in_feigned; +} + #ifdef BOTS bool Mob::JoinHealRotationTargetPool(std::shared_ptr* heal_rotation) { diff --git a/zone/mob.h b/zone/mob.h index 34c991177..50ad8af58 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -71,7 +71,7 @@ class Mob : public Entity { public: enum CLIENT_CONN_STATUS { CLIENT_CONNECTING, CLIENT_CONNECTED, CLIENT_LINKDEAD, CLIENT_KICKED, DISCONNECTED, CLIENT_ERROR, CLIENT_CONNECTINGALL }; - enum eStandingPetOrder { SPO_Follow, SPO_Sit, SPO_Guard }; + enum eStandingPetOrder { SPO_Follow, SPO_Sit, SPO_Guard, SPO_FeignDeath }; struct SpecialAbility { SpecialAbility() { @@ -670,10 +670,10 @@ public: bool HateSummon(); void FaceTarget(Mob* mob_to_face = 0); void WipeHateList(); - void AddFeignMemory(Client* attacker); - void RemoveFromFeignMemory(Client* attacker); + void AddFeignMemory(Mob* attacker); + void RemoveFromFeignMemory(Mob* attacker); void ClearFeignMemory(); - bool IsOnFeignMemory(Client *attacker) const; + bool IsOnFeignMemory(Mob *attacker) const; void PrintHateListToClient(Client *who) { hate_list.PrintHateListToClient(who); } std::list& GetHateList() { return hate_list.GetHateList(); } std::list GetHateListByDistance(int distance = 0) { return hate_list.GetHateListByDistance(distance); } @@ -1296,6 +1296,10 @@ public: bool CanOpenDoors() const; void SetCanOpenDoors(bool can_open); + void SetFeigned(bool in_feigned); + /// this cures timing issues cuz dead animation isn't done but server side feigning is? + inline bool GetFeigned() const { return(feigned); } + void DeleteBucket(std::string bucket_name); std::string GetBucket(std::string bucket_name); std::string GetBucketExpires(std::string bucket_name); @@ -1720,6 +1724,9 @@ protected: AuraMgr aura_mgr; AuraMgr trap_mgr; + bool feigned; + Timer forget_timer; // our 2 min everybody forgets you timer + bool m_can_open_doors; MobMovementManager *mMovementManager; @@ -1727,7 +1734,7 @@ protected: private: void _StopSong(); //this is not what you think it is Mob* target; - + #ifdef BOTS std::shared_ptr m_target_of_heal_rotation; diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 1a071cd06..1d7d9c317 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -838,20 +838,21 @@ void Client::AI_Process() else { if(AI_feign_remember_timer->Check()) { - std::set::iterator RememberedCharID; - RememberedCharID = feign_memory_list.begin(); - while (RememberedCharID != feign_memory_list.end()) { - Client* remember_client = entity_list.GetClientByCharID(*RememberedCharID); - if (remember_client == nullptr) { + std::set::iterator remembered_feigned_mobid; + remembered_feigned_mobid = feign_memory_list.begin(); + while (remembered_feigned_mobid != feign_memory_list.end()) { + + Mob* remembered_mob = entity_list.GetMob(*remembered_feigned_mobid); + if (remembered_mob == nullptr || remembered_mob->IsCorpse()) { //they are gone now... - RememberedCharID = feign_memory_list.erase(RememberedCharID); - } else if (!remember_client->GetFeigned()) { - AddToHateList(remember_client->CastToMob(),1); - RememberedCharID = feign_memory_list.erase(RememberedCharID); + remembered_feigned_mobid = feign_memory_list.erase(remembered_feigned_mobid); + } else if (!remembered_mob->GetFeigned()) { + AddToHateList(remembered_mob,1); + remembered_feigned_mobid = feign_memory_list.erase(remembered_feigned_mobid); break; } else { //they are still feigned, carry on... - ++RememberedCharID; + ++remembered_feigned_mobid; } } } @@ -1373,22 +1374,22 @@ void Mob::AI_Process() { // 6/14/06 // Improved Feign Death Memory // check to see if any of our previous feigned targets have gotten up. - std::set::iterator RememberedCharID; - RememberedCharID = feign_memory_list.begin(); - while (RememberedCharID != feign_memory_list.end()) { - Client *remember_client = entity_list.GetClientByCharID(*RememberedCharID); - if (remember_client == nullptr) { + std::set::iterator remembered_feigned_mobid; + remembered_feigned_mobid = feign_memory_list.begin(); + while (remembered_feigned_mobid != feign_memory_list.end()) { + Mob *remembered_mob = entity_list.GetMob(*remembered_feigned_mobid); + if (remembered_mob == nullptr || remembered_mob->IsCorpse()) { //they are gone now... - RememberedCharID = feign_memory_list.erase(RememberedCharID); + remembered_feigned_mobid = feign_memory_list.erase(remembered_feigned_mobid); } - else if (!remember_client->GetFeigned()) { - AddToHateList(remember_client->CastToMob(), 1); - RememberedCharID = feign_memory_list.erase(RememberedCharID); + else if (!remembered_mob->GetFeigned()) { + AddToHateList(remembered_mob, 1); + remembered_feigned_mobid = feign_memory_list.erase(remembered_feigned_mobid); break; } else { //they are still feigned, carry on... - ++RememberedCharID; + ++remembered_feigned_mobid; } } } @@ -1485,6 +1486,10 @@ void Mob::AI_Process() { } break; } + case SPO_FeignDeath: { + SetAppearance(eaDead, false); + break; + } } if (IsPetRegroup()) { return; @@ -1557,6 +1562,11 @@ void Mob::AI_Process() { } } + if (forget_timer.Check()) { + forget_timer.Disable(); + entity_list.ClearZoneFeignAggro(this); + } + //Do Ranged attack here if (doranged) { RangedAttack(target); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index c49662802..c9d76de7a 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1557,18 +1557,17 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Feign Death"); #endif - //todo, look up spell ID in DB - if(spell_id == 2488) //Dook- Lifeburn fix + if(spell_id == SPELL_LIFEBURN) //Dook- Lifeburn fix break; if(IsClient()) { CastToClient()->SetHorseId(0); // dismount if have horse if (zone->random.Int(0, 99) > spells[spell_id].base_value[i]) { - CastToClient()->SetFeigned(false); + SetFeigned(false); entity_list.MessageCloseString(this, false, 200, 10, STRING_FEIGNFAILED, GetName()); } else { - CastToClient()->SetFeigned(true); + SetFeigned(true); } } break; From e45f02af95cccbda2a857281214e2563bb18c316 Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Wed, 29 Dec 2021 10:17:31 -0600 Subject: [PATCH 539/624] [Skills] RoF+ allows other classes to have feign death if set in skill_caps (#1902) --- zone/client_packet.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index cad61cdf3..fdd2c763f 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -5894,8 +5894,10 @@ void Client::Handle_OP_FaceChange(const EQApplicationPacket *app) void Client::Handle_OP_FeignDeath(const EQApplicationPacket *app) { - if (GetClass() != MONK) + if (!HasSkill(EQ::skills::SkillFeignDeath)) { return; + } + if (!p_timers.Expired(&database, pTimerFeignDeath, false)) { Message(Chat::Red, "Ability recovery time not yet met."); return; From c99c5c1f1cf8d766a90a312f323fb173b223ecc6 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Wed, 29 Dec 2021 12:06:51 -0500 Subject: [PATCH 540/624] [Bug Fix] Fix #guild rename, #killallnpcs, and #worldwide message errors. (#1904) - #guild rename was checking argument count and not allowing you to rename guilds to names that had spaces. - #killallnpcs was crashing zones when used sometimes due to getting a nullptr somewhere in the loop. - #worldwide message was using just the first word of the message sent using the command, not all of them. --- zone/gm_commands/guild.cpp | 5 +---- zone/gm_commands/killallnpcs.cpp | 4 ++++ zone/gm_commands/worldwide.cpp | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/zone/gm_commands/guild.cpp b/zone/gm_commands/guild.cpp index 5eaa7092b..b220dc53b 100755 --- a/zone/gm_commands/guild.cpp +++ b/zone/gm_commands/guild.cpp @@ -225,10 +225,7 @@ void command_guild(Client *c, const Seperator *sep) guild_mgr.ListGuilds(c); } else if (is_rename) { - if ( - arguments != 3 || - !sep->IsNumber(2) - ) { + if (!sep->IsNumber(2)) { c->Message(Chat::White, "Usage: #guild rename [Guild ID] [New Guild Name]"); } else { auto guild_id = std::stoul(sep->arg[2]); diff --git a/zone/gm_commands/killallnpcs.cpp b/zone/gm_commands/killallnpcs.cpp index e1651fe02..5ae74c3bb 100755 --- a/zone/gm_commands/killallnpcs.cpp +++ b/zone/gm_commands/killallnpcs.cpp @@ -10,6 +10,10 @@ void command_killallnpcs(Client *c, const Seperator *sep) int killed_count = 0; for (auto& npc_entity : entity_list.GetNPCList()) { auto entity_id = npc_entity.first; + if (!entity_id) { + continue; + } + auto npc = npc_entity.second; if (!npc) { continue; diff --git a/zone/gm_commands/worldwide.cpp b/zone/gm_commands/worldwide.cpp index 29b617a05..f940a4cbf 100755 --- a/zone/gm_commands/worldwide.cpp +++ b/zone/gm_commands/worldwide.cpp @@ -62,7 +62,7 @@ void command_worldwide(Client *c, const Seperator *sep) } else if (sub_command == "message") { if (sep->arg[2]) { - std::string message = sep->arg[2]; + std::string message = sep->argplus[2]; quest_manager.WorldWideMessage( Chat::White, fmt::format( From 0a5d8b42f1d6121ee3abf3fde95c6f492a99dae5 Mon Sep 17 00:00:00 2001 From: neckkola <65987027+neckkola@users.noreply.github.com> Date: Thu, 30 Dec 2021 15:34:41 -0400 Subject: [PATCH 541/624] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1db525804..1c39264c8 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,4 @@ bin/ /Win32 /x64 /client_files/**/CMakeFiles/ +CMakeSettings.json From d280d544468eee74387e1feb77fe762bba1fd8df Mon Sep 17 00:00:00 2001 From: j883376 <118647+j883376@users.noreply.github.com> Date: Thu, 30 Dec 2021 20:40:14 -0500 Subject: [PATCH 542/624] [Spells] Allow GMs to remove buffs from any target (#1907) --- zone/client_packet.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index fdd2c763f..3bb32aa25 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -3903,6 +3903,10 @@ void Client::Handle_OP_BuffRemoveRequest(const EQApplicationPacket *app) else if (brrs->EntityID == GetPetID()) { m = GetPet(); } + else if (GetGM()) + { + m = entity_list.GetMobID(brrs->EntityID); + } #ifdef BOTS else { Mob* bot_test = entity_list.GetMob(brrs->EntityID); @@ -3919,7 +3923,7 @@ void Client::Handle_OP_BuffRemoveRequest(const EQApplicationPacket *app) uint16 SpellID = m->GetSpellIDFromSlot(brrs->SlotID); - if (SpellID && (IsBeneficialSpell(SpellID) || IsEffectInSpell(SpellID, SE_BindSight)) && !spells[SpellID].no_remove) { + if (SpellID && (GetGM() || ((IsBeneficialSpell(SpellID) || IsEffectInSpell(SpellID, SE_BindSight)) && !spells[SpellID].no_remove))) { m->BuffFadeBySlot(brrs->SlotID, true); } } From c0f57bed1fdf35924e2b5efea7754dfdf273899e Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 30 Dec 2021 20:47:52 -0500 Subject: [PATCH 543/624] [Bug Fix] Cleanup Perl croaks for Spire parser. (#1908) - Client::SendToInstance() - Mob::DeleteBucket() - Mob::GetBucket() - Mob::GetBucketExpires() - Mob::GetBucketRemaining() - Mob::SetBucket() --- zone/perl_client.cpp | 2 +- zone/perl_mob.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index d51c79fdd..6e236a4cd 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -5613,7 +5613,7 @@ XS(XS_Client_SendToInstance); XS(XS_Client_SendToInstance) { dXSARGS; if (items != 10) - Perl_croak(aTHX_ "Usage: Client::SendToInstance(THIS, std::string instance_type, std::string zone_short_name, uint32 instance_version, float x, float y, float z, float heading, std::string instance_identifier, uint32 duration)"); + Perl_croak(aTHX_ "Usage: Client::SendToInstance(THIS, string instance_type, string zone_short_name, uint32 instance_version, float x, float y, float z, float heading, string instance_identifier, uint32 duration)"); { Client* THIS; std::string instance_type = (std::string) SvPV_nolen(ST(1)); diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index 28b8be62d..efa71d2dc 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -6203,7 +6203,7 @@ XS(XS_Mob_DeleteBucket); XS(XS_Mob_DeleteBucket) { dXSARGS; if (items != 2) - Perl_croak(aTHX_ "Usage: Mob::DeleteBucket(THIS, std::string bucket_name)"); // @categories Script Utility + Perl_croak(aTHX_ "Usage: Mob::DeleteBucket(THIS, string bucket_name)"); // @categories Script Utility { Mob* THIS; std::string bucket_name = (std::string) SvPV_nolen(ST(1)); @@ -6217,7 +6217,7 @@ XS(XS_Mob_GetBucket); XS(XS_Mob_GetBucket) { dXSARGS; if (items != 2) - Perl_croak(aTHX_ "Usage: Mob::GetBucket(THIS, std::string bucket_name)"); // @categories Script Utility + Perl_croak(aTHX_ "Usage: Mob::GetBucket(THIS, string bucket_name)"); // @categories Script Utility { Mob* THIS; dXSTARG; @@ -6236,7 +6236,7 @@ XS(XS_Mob_GetBucketExpires); XS(XS_Mob_GetBucketExpires) { dXSARGS; if (items != 2) - Perl_croak(aTHX_ "Usage: Mob::GetBucketExpires(THIS, std::string bucket_name)"); // @categories Script Utility + Perl_croak(aTHX_ "Usage: Mob::GetBucketExpires(THIS, string bucket_name)"); // @categories Script Utility { Mob* THIS; dXSTARG; @@ -6273,7 +6273,7 @@ XS(XS_Mob_GetBucketRemaining); XS(XS_Mob_GetBucketRemaining) { dXSARGS; if (items != 2) - Perl_croak(aTHX_ "Usage: Mob::GetBucketRemaining(THIS, std::string bucket_name)"); // @categories Script Utility + Perl_croak(aTHX_ "Usage: Mob::GetBucketRemaining(THIS, string bucket_name)"); // @categories Script Utility { Mob* THIS; dXSTARG; @@ -6292,7 +6292,7 @@ XS(XS_Mob_SetBucket); XS(XS_Mob_SetBucket) { dXSARGS; if (items < 3 || items > 4) - Perl_croak(aTHX_ "Usage: Mob::SetBucket(THIS, std::string bucket_name, std::string bucket_value, [std::string expiration])"); // @categories Script Utility + Perl_croak(aTHX_ "Usage: Mob::SetBucket(THIS, string bucket_name, string bucket_value, [string expiration])"); // @categories Script Utility { Mob* THIS; std::string key = (std::string) SvPV_nolen(ST(1)); From 9815f50efab22edc290fa4b966602787b617b0d6 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sun, 2 Jan 2022 20:52:29 -0600 Subject: [PATCH 544/624] [Expansion] Content Filtering Adjustments (#1910) * Change default expansion values for ALL to -1 from 0 * Adjust content_filter_criteria * Refactor content filtering logic * Allow flag strings to also just be empty instead of null * Formatting * Editor oops --- common/content/world_content_service.cpp | 110 +++++++++++++--- common/content/world_content_service.h | 25 ++-- .../criteria/content_filter_criteria.h | 23 ++-- common/servertalk.h | 1 + common/version.h | 2 +- utils/sql/db_update_manifest.txt | 1 + ...2022_01_02_expansion_default_value_all.sql | 117 ++++++++++++++++++ world/main.cpp | 5 + world/world_server_command_handler.cpp | 23 ++-- world/zoneserver.cpp | 6 + zone/CMakeLists.txt | 1 + zone/command.cpp | 1 + zone/command.h | 1 + zone/embparser_api.cpp | 19 +-- zone/gm_commands/reloadcontentflags.cpp | 15 +++ zone/lua_general.cpp | 4 +- zone/main.cpp | 6 +- zone/spawngroup.cpp | 7 +- zone/worldserver.cpp | 62 +++++++--- zone/zone.cpp | 7 +- zone/zone_event_scheduler.cpp | 9 +- zone/zone_store.cpp | 38 ------ zone/zone_store.h | 3 - 23 files changed, 365 insertions(+), 121 deletions(-) create mode 100644 utils/sql/git/required/2022_01_02_expansion_default_value_all.sql create mode 100755 zone/gm_commands/reloadcontentflags.cpp diff --git a/common/content/world_content_service.cpp b/common/content/world_content_service.cpp index feb0f5b76..42397cdbb 100644 --- a/common/content/world_content_service.cpp +++ b/common/content/world_content_service.cpp @@ -35,8 +35,12 @@ int WorldContentService::GetCurrentExpansion() const return current_expansion; } -void WorldContentService::SetExpansionContext() +WorldContentService *WorldContentService::SetExpansionContext() { + // do a rule manager reload until where we store expansion is changed to somewhere else + RuleManager::Instance()->LoadRules(GetDatabase(), "default", true); + + // pull expansion from rules int expansion = RuleI(Expansion, CurrentExpansion); if (expansion >= Expansion::Classic && expansion <= Expansion::MaxId) { content_service.SetCurrentExpansion(expansion); @@ -47,6 +51,8 @@ void WorldContentService::SetExpansionContext() GetCurrentExpansion(), GetCurrentExpansionName() ); + + return this; } std::string WorldContentService::GetCurrentExpansionName() @@ -73,15 +79,47 @@ void WorldContentService::SetCurrentExpansion(int current_expansion) /** * @return */ -const std::vector &WorldContentService::GetContentFlags() const +const std::vector &WorldContentService::GetContentFlags() const { return content_flags; } +/** + * @return + */ +std::vector WorldContentService::GetContentFlagsEnabled() +{ + std::vector enabled_flags; + + for (auto &f: GetContentFlags()) { + if (f.enabled) { + enabled_flags.emplace_back(f.flag_name); + } + } + + return enabled_flags; +} + +/** + * @return + */ +std::vector WorldContentService::GetContentFlagsDisabled() +{ + std::vector disabled_flags; + + for (auto &f: GetContentFlags()) { + if (!f.enabled) { + disabled_flags.emplace_back(f.flag_name); + } + } + + return disabled_flags; +} + /** * @param content_flags */ -void WorldContentService::SetContentFlags(std::vector content_flags) +void WorldContentService::SetContentFlags(std::vector content_flags) { WorldContentService::content_flags = content_flags; } @@ -90,10 +128,10 @@ void WorldContentService::SetContentFlags(std::vector content_flags * @param content_flag * @return */ -bool WorldContentService::IsContentFlagEnabled(const std::string& content_flag) +bool WorldContentService::IsContentFlagEnabled(const std::string &content_flag) { - for (auto &flag : GetContentFlags()) { - if (flag == content_flag) { + for (auto &f: GetContentFlags()) { + if (f.flag_name == content_flag) { return true; } } @@ -101,20 +139,58 @@ bool WorldContentService::IsContentFlagEnabled(const std::string& content_flag) return false; } -void WorldContentService::ReloadContentFlags(Database &db) +void WorldContentService::ReloadContentFlags() { - std::vector set_content_flags; - auto content_flags = ContentFlagsRepository::GetWhere(db, "enabled = 1"); + std::vector set_content_flags; + auto flags = ContentFlagsRepository::All(*GetDatabase()); - set_content_flags.reserve(content_flags.size()); - for (auto &flags: content_flags) { - set_content_flags.push_back(flags.flag_name); + set_content_flags.reserve(flags.size()); + for (auto &f: flags) { + set_content_flags.push_back(f); + + LogInfo( + "Loaded content flag [{}] [{}]", + f.flag_name, + (f.enabled ? "Enabled" : "Disabled") + ); } - LogInfo( - "Enabled content flags [{}]", - implode(", ", set_content_flags) - ); - SetContentFlags(set_content_flags); } + +Database *WorldContentService::GetDatabase() const +{ + return m_database; +} + +WorldContentService *WorldContentService::SetDatabase(Database *database) +{ + WorldContentService::m_database = database; + + return this; +} + +void WorldContentService::SetContentFlag(const std::string &content_flag_name, bool enabled) +{ + auto flags = ContentFlagsRepository::GetWhere( + *GetDatabase(), + fmt::format("flag_name = '{}'", content_flag_name) + ); + + auto f = ContentFlagsRepository::NewEntity(); + if (!flags.empty()) { + f = flags.front(); + } + + f.enabled = enabled ? 1 : 0; + f.flag_name = content_flag_name; + + if (!flags.empty()) { + ContentFlagsRepository::UpdateOne(*GetDatabase(), f); + } + else { + ContentFlagsRepository::InsertOne(*GetDatabase(), f); + } + + ReloadContentFlags(); +} diff --git a/common/content/world_content_service.h b/common/content/world_content_service.h index 47220b349..512f2c92c 100644 --- a/common/content/world_content_service.h +++ b/common/content/world_content_service.h @@ -23,6 +23,7 @@ #include #include +#include "../repositories/content_flags_repository.h" class Database; @@ -160,15 +161,25 @@ public: bool IsCurrentExpansionTheBurningLands() { return current_expansion == Expansion::ExpansionNumber::TheBurningLands; } bool IsCurrentExpansionTormentOfVelious() { return current_expansion == Expansion::ExpansionNumber::TormentOfVelious; } + const std::vector &GetContentFlags() const; + std::vector GetContentFlagsEnabled(); + std::vector GetContentFlagsDisabled(); + bool IsContentFlagEnabled(const std::string& content_flag); + void SetContentFlags(std::vector content_flags); + void ReloadContentFlags(); + WorldContentService * SetExpansionContext(); + + WorldContentService * SetDatabase(Database *database); + Database *GetDatabase() const; + + void SetContentFlag(const std::string &content_flag_name, bool enabled); + private: int current_expansion{}; - std::vector content_flags; -public: - const std::vector &GetContentFlags() const; - bool IsContentFlagEnabled(const std::string& content_flag); - void SetContentFlags(std::vector content_flags); - void ReloadContentFlags(Database &db); - void SetExpansionContext(); + std::vector content_flags; + + // reference to database + Database *m_database; }; extern WorldContentService content_service; diff --git a/common/repositories/criteria/content_filter_criteria.h b/common/repositories/criteria/content_filter_criteria.h index 6fa1adc71..8d73eefe4 100644 --- a/common/repositories/criteria/content_filter_criteria.h +++ b/common/repositories/criteria/content_filter_criteria.h @@ -40,43 +40,48 @@ namespace ContentFilterCriteria { } criteria += fmt::format( - " AND ({}min_expansion <= {} OR {}min_expansion = 0)", + " AND ({}min_expansion <= {} OR {}min_expansion = -1)", table_prefix, current_expansion_filter_criteria, table_prefix ); criteria += fmt::format( - " AND ({}max_expansion >= {} OR {}max_expansion = 0)", + " AND ({}max_expansion >= {} OR {}max_expansion = -1)", table_prefix, current_expansion_filter_criteria, table_prefix ); - std::vector flags = content_service.GetContentFlags(); + std::vector flags_disabled = content_service.GetContentFlagsDisabled(); + std::vector flags_enabled = content_service.GetContentFlagsEnabled(); std::string flags_in_filter_enabled; std::string flags_in_filter_disabled; - if (!flags.empty()) { + if (!flags_enabled.empty()) { flags_in_filter_enabled = fmt::format( " OR CONCAT(',', {}content_flags, ',') REGEXP ',({}),' ", table_prefix, - implode("|", flags) + implode("|", flags_enabled) ); + } + if (!flags_disabled.empty()) { flags_in_filter_disabled = fmt::format( - " OR CONCAT(',', {}content_flags_disabled, ',') NOT REGEXP ',({}),' ", + " OR CONCAT(',', {}content_flags_disabled, ',') REGEXP ',({}),' ", table_prefix, - implode("|", flags) + implode("|", flags_disabled) ); } criteria += fmt::format( - " AND ({}content_flags IS NULL{}) ", + " AND (({}content_flags IS NULL OR {}content_flags = ''){}) ", + table_prefix, table_prefix, flags_in_filter_enabled ); criteria += fmt::format( - " AND ({}content_flags_disabled IS NULL{}) ", + " AND (({}content_flags_disabled IS NULL OR {}content_flags_disabled = ''){}) ", + table_prefix, table_prefix, flags_in_filter_disabled ); diff --git a/common/servertalk.h b/common/servertalk.h index 86d4b08a6..37c96d844 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -226,6 +226,7 @@ #define ServerOP_UCSServerStatusReply 0x4010 #define ServerOP_HotReloadQuests 0x4011 #define ServerOP_UpdateSchedulerEvents 0x4012 +#define ServerOP_ReloadContentFlags 0x4013 #define ServerOP_CZDialogueWindow 0x4500 #define ServerOP_CZLDoNUpdate 0x4501 diff --git a/common/version.h b/common/version.h index f8eb10b8c..568513041 100644 --- a/common/version.h +++ b/common/version.h @@ -34,7 +34,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9174 +#define CURRENT_BINARY_DATABASE_VERSION 9175 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9028 diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 153155c5d..3c210a287 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -428,6 +428,7 @@ 9172|2021_05_21_shared_tasks.sql|SHOW TABLES LIKE 'shared_tasks'|empty| 9173|2021_09_14_zone_lava_damage.sql|SHOW COLUMNS FROM `zone` LIKE 'lava_damage'|empty| 9174|2021_10_09_not_null_door_columns.sql|SELECT * FROM db_version WHERE version >= 9174|empty| +9175|2022_01_02_expansion_default_value_all.sql|SHOW COLUMNS FROM `forage` LIKE 'min_expansion'|contains|unsigned # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2022_01_02_expansion_default_value_all.sql b/utils/sql/git/required/2022_01_02_expansion_default_value_all.sql new file mode 100644 index 000000000..3b2261014 --- /dev/null +++ b/utils/sql/git/required/2022_01_02_expansion_default_value_all.sql @@ -0,0 +1,117 @@ +-- forage + +ALTER TABLE `forage` CHANGE `max_expansion` `max_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +ALTER TABLE `forage` CHANGE `min_expansion` `min_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +UPDATE forage set min_expansion = -1 where min_expansion = 0; +UPDATE forage set max_expansion = -1 where max_expansion = 0; + +-- tradeskill_recipe + +ALTER TABLE `tradeskill_recipe` CHANGE `max_expansion` `max_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +ALTER TABLE `tradeskill_recipe` CHANGE `min_expansion` `min_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +UPDATE tradeskill_recipe set min_expansion = -1 where min_expansion = 0; +UPDATE tradeskill_recipe set max_expansion = -1 where max_expansion = 0; + +-- fishing + +ALTER TABLE `fishing` CHANGE `max_expansion` `max_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +ALTER TABLE `fishing` CHANGE `min_expansion` `min_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +UPDATE fishing set min_expansion = -1 where min_expansion = 0; +UPDATE fishing set max_expansion = -1 where max_expansion = 0; + +-- zone + +ALTER TABLE `zone` CHANGE `max_expansion` `max_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +ALTER TABLE `zone` CHANGE `min_expansion` `min_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +UPDATE zone set min_expansion = -1 where min_expansion = 0; +UPDATE zone set max_expansion = -1 where max_expansion = 0; + +-- traps + +ALTER TABLE `traps` CHANGE `max_expansion` `max_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +ALTER TABLE `traps` CHANGE `min_expansion` `min_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +UPDATE traps set min_expansion = -1 where min_expansion = 0; +UPDATE traps set max_expansion = -1 where max_expansion = 0; + +-- loottable + +ALTER TABLE `loottable` CHANGE `max_expansion` `max_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +ALTER TABLE `loottable` CHANGE `min_expansion` `min_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +UPDATE loottable set min_expansion = -1 where min_expansion = 0; +UPDATE loottable set max_expansion = -1 where max_expansion = 0; + +-- ground_spawns + +ALTER TABLE `ground_spawns` CHANGE `max_expansion` `max_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +ALTER TABLE `ground_spawns` CHANGE `min_expansion` `min_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +UPDATE ground_spawns set min_expansion = -1 where min_expansion = 0; +UPDATE ground_spawns set max_expansion = -1 where max_expansion = 0; + +-- starting_items + +ALTER TABLE `starting_items` CHANGE `max_expansion` `max_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +ALTER TABLE `starting_items` CHANGE `min_expansion` `min_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +UPDATE starting_items set min_expansion = -1 where min_expansion = 0; +UPDATE starting_items set max_expansion = -1 where max_expansion = 0; + +-- spawn2 + +ALTER TABLE `spawn2` CHANGE `max_expansion` `max_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +ALTER TABLE `spawn2` CHANGE `min_expansion` `min_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +UPDATE spawn2 set min_expansion = -1 where min_expansion = 0; +UPDATE spawn2 set max_expansion = -1 where max_expansion = 0; + +-- zone_points + +ALTER TABLE `zone_points` CHANGE `max_expansion` `max_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +ALTER TABLE `zone_points` CHANGE `min_expansion` `min_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +UPDATE zone_points set min_expansion = -1 where min_expansion = 0; +UPDATE zone_points set max_expansion = -1 where max_expansion = 0; + +-- lootdrop + +ALTER TABLE `lootdrop` CHANGE `max_expansion` `max_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +ALTER TABLE `lootdrop` CHANGE `min_expansion` `min_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +UPDATE lootdrop set min_expansion = -1 where min_expansion = 0; +UPDATE lootdrop set max_expansion = -1 where max_expansion = 0; + +-- global_loot + +ALTER TABLE `global_loot` CHANGE `max_expansion` `max_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +ALTER TABLE `global_loot` CHANGE `min_expansion` `min_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +UPDATE global_loot set min_expansion = -1 where min_expansion = 0; +UPDATE global_loot set max_expansion = -1 where max_expansion = 0; + +-- doors + +ALTER TABLE `doors` CHANGE `max_expansion` `max_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +ALTER TABLE `doors` CHANGE `min_expansion` `min_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +UPDATE doors set min_expansion = -1 where min_expansion = 0; +UPDATE doors set max_expansion = -1 where max_expansion = 0; + +-- object + +ALTER TABLE `object` CHANGE `max_expansion` `max_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +ALTER TABLE `object` CHANGE `min_expansion` `min_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +UPDATE object set min_expansion = -1 where min_expansion = 0; +UPDATE object set max_expansion = -1 where max_expansion = 0; + +-- start_zones + +ALTER TABLE `start_zones` CHANGE `max_expansion` `max_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +ALTER TABLE `start_zones` CHANGE `min_expansion` `min_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +UPDATE start_zones set min_expansion = -1 where min_expansion = 0; +UPDATE start_zones set max_expansion = -1 where max_expansion = 0; + +-- merchantlist + +ALTER TABLE `merchantlist` CHANGE `max_expansion` `max_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +ALTER TABLE `merchantlist` CHANGE `min_expansion` `min_expansion` tinyint(4) NOT NULL DEFAULT -1 COMMENT ''; +UPDATE merchantlist set min_expansion = -1 where min_expansion = 0; +UPDATE merchantlist set max_expansion = -1 where max_expansion = 0; + +-- spawnentry +ALTER TABLE `spawnentry` ADD `min_expansion` tinyint(4) NOT NULL DEFAULT -1; +ALTER TABLE `spawnentry` ADD `max_expansion` tinyint(4) NOT NULL DEFAULT -1; +ALTER TABLE `spawnentry` ADD `content_flags` varchar(100) NULL; +ALTER TABLE `spawnentry` ADD `content_flags_disabled` varchar(100) NULL; diff --git a/world/main.cpp b/world/main.cpp index ff4cdece9..ee9f12705 100644 --- a/world/main.cpp +++ b/world/main.cpp @@ -503,6 +503,11 @@ int main(int argc, char **argv) LogInfo("Initializing [EventScheduler]"); event_scheduler.SetDatabase(&database)->LoadScheduledEvents(); + LogInfo("Initializing [WorldContentService]"); + content_service.SetDatabase(&database) + ->SetExpansionContext() + ->ReloadContentFlags(); + LogInfo("Initializing [SharedTaskManager]"); shared_task_manager.SetDatabase(&database) ->SetContentDatabase(&content_db) diff --git a/world/world_server_command_handler.cpp b/world/world_server_command_handler.cpp index 4c5a36cc7..0eade4f43 100644 --- a/world/world_server_command_handler.cpp +++ b/world/world_server_command_handler.cpp @@ -165,37 +165,37 @@ namespace WorldserverCommandHandler { Json::Value player_tables_json; std::vector player_tables = DatabaseSchema::GetPlayerTables(); - for (const auto &table : player_tables) { + for (const auto &table: player_tables) { player_tables_json.append(table); } Json::Value content_tables_json; std::vector content_tables = DatabaseSchema::GetContentTables(); - for (const auto &table : content_tables) { + for (const auto &table: content_tables) { content_tables_json.append(table); } Json::Value server_tables_json; std::vector server_tables = DatabaseSchema::GetServerTables(); - for (const auto &table : server_tables) { + for (const auto &table: server_tables) { server_tables_json.append(table); } Json::Value login_tables_json; std::vector login_tables = DatabaseSchema::GetLoginTables(); - for (const auto &table : login_tables) { + for (const auto &table: login_tables) { login_tables_json.append(table); } Json::Value state_tables_json; std::vector state_tables = DatabaseSchema::GetStateTables(); - for (const auto &table : state_tables) { + for (const auto &table: state_tables) { state_tables_json.append(table); } Json::Value version_tables_json; std::vector version_tables = DatabaseSchema::GetVersionTables(); - for (const auto &table : version_tables) { + for (const auto &table: version_tables) { version_tables_json.append(table); } @@ -313,11 +313,20 @@ namespace WorldserverCommandHandler { content_service.SetCurrentExpansion(RuleI(Expansion, CurrentExpansion)); - std::vector flags = { + std::vector flags = {}; + auto f = ContentFlagsRepository::NewEntity(); + f.enabled = 1; + + std::vector flag_names = { "hateplane_enabled", "patch_nerf_7077", }; + for (auto &name: flag_names) { + f.flag_name = name; + flags.push_back(f); + } + content_service.SetContentFlags(flags); LogInfo( diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index ae0b2682b..8326c0657 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -41,6 +41,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "shared_task_world_messaging.h" #include "../common/shared_tasks.h" #include "shared_task_manager.h" +#include "../common/content/world_content_service.h" extern ClientList client_list; extern GroupLFPList LFPGroupList; @@ -868,6 +869,11 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { RuleManager::Instance()->LoadRules(&database, "default", true); break; } + case ServerOP_ReloadContentFlags: { + zoneserver_list.SendPacket(pack); + content_service.SetExpansionContext()->ReloadContentFlags(); + break; + } case ServerOP_ReloadRulesWorld: { RuleManager::Instance()->LoadRules(&database, "default", true); diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 1e1f548ba..3c24da07a 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -459,6 +459,7 @@ SET(gm_commands gm_commands/refreshgroup.cpp gm_commands/reloadaa.cpp gm_commands/reloadallrules.cpp + gm_commands/reloadcontentflags.cpp gm_commands/reloademote.cpp gm_commands/reloadlevelmods.cpp gm_commands/reloadmerchants.cpp diff --git a/zone/command.cpp b/zone/command.cpp index be3547754..2eee4e1f1 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -295,6 +295,7 @@ int command_init(void) command_add("refreshgroup", "- Refreshes Group.", AccountStatus::Player, command_refreshgroup) || command_add("reloadaa", "Reloads AA data", AccountStatus::GMMgmt, command_reloadaa) || command_add("reloadallrules", "Executes a reload of all rules.", AccountStatus::QuestTroupe, command_reloadallrules) || + command_add("reloadcontentflags", "Executes a reload of all expansion and content flags", AccountStatus::QuestTroupe, command_reloadcontentflags) || command_add("reloademote", "Reloads NPC Emotes", AccountStatus::QuestTroupe, command_reloademote) || command_add("reloadlevelmods", nullptr, AccountStatus::Max, command_reloadlevelmods) || command_add("reloadmerchants", nullptr, AccountStatus::Max, command_reloadmerchants) || diff --git a/zone/command.h b/zone/command.h index 177b53cb4..dc64da7b2 100644 --- a/zone/command.h +++ b/zone/command.h @@ -216,6 +216,7 @@ void command_randomfeatures(Client *c, const Seperator *sep); void command_refreshgroup(Client *c, const Seperator *sep); void command_reloadaa(Client *c, const Seperator *sep); void command_reloadallrules(Client *c, const Seperator *sep); +void command_reloadcontentflags(Client *c, const Seperator *sep); void command_reloademote(Client *c, const Seperator *sep); void command_reloadlevelmods(Client *c, const Seperator *sep); void command_reloadmerchants(Client *c, const Seperator *sep); diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index e78112de2..94e403508 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -4802,7 +4802,8 @@ XS(XS__SetContentFlag) std::string flag_name = (std::string) SvPV_nolen(ST(0)); bool enabled = (int) SvIV(ST(1)) != 0; - ZoneStore::SetContentFlag(flag_name, enabled); + + content_service.SetContentFlag(flag_name, enabled); XSRETURN_EMPTY; } @@ -5141,7 +5142,7 @@ XS(XS__gethexcolorcode) { sv_setpv(TARG, hex_color_code.c_str()); XSprePUSH; PUSHTARG; - XSRETURN(1); + XSRETURN(1); } XS(XS__getaaexpmodifierbycharid); @@ -5149,7 +5150,7 @@ XS(XS__getaaexpmodifierbycharid) { dXSARGS; if (items != 2) Perl_croak(aTHX_ "Usage: quest::getaaexpmodifierbycharid(uint32 character_id, uint32 zone_id)"); - + dXSTARG; double aa_modifier; uint32 character_id = (uint32) SvUV(ST(0)); @@ -5165,7 +5166,7 @@ XS(XS__getexpmodifierbycharid) { dXSARGS; if (items != 2) Perl_croak(aTHX_ "Usage: quest::getexpmodifierbycharid(uint32 character_id, uint32 zone_id)"); - + dXSTARG; double exp_modifier; uint32 character_id = (uint32) SvUV(ST(0)); @@ -5313,7 +5314,7 @@ XS(XS__getspellstat) { uint8 slot = 0; if (items == 3) slot = (uint8) SvUV(ST(2)); - + stat_value = quest_manager.getspellstat(spell_id, stat_identifier, slot); XSprePUSH; @@ -7551,7 +7552,7 @@ XS(XS__worldwideassigntask) { if (items == 3) max_status = (uint8) SvUV(ST(2)); - + quest_manager.WorldWideTaskUpdate(update_type, task_identifier, task_subidentifier, update_count, enforce_level_requirement, min_status, max_status); } XSRETURN_EMPTY; @@ -7616,7 +7617,7 @@ XS(XS__worldwidedisabletask) { if (items == 3) max_status = (uint8) SvUV(ST(2)); - + quest_manager.WorldWideTaskUpdate(update_type, task_identifier, task_subidentifier, update_count, enforce_level_requirement, min_status, max_status); } XSRETURN_EMPTY; @@ -7640,7 +7641,7 @@ XS(XS__worldwideenabletask) { if (items == 3) max_status = (uint8) SvUV(ST(2)); - + quest_manager.WorldWideTaskUpdate(update_type, task_identifier, task_subidentifier, update_count, enforce_level_requirement, min_status, max_status); } XSRETURN_EMPTY; @@ -7664,7 +7665,7 @@ XS(XS__worldwidefailtask) { if (items == 3) max_status = (uint8) SvUV(ST(2)); - + quest_manager.WorldWideTaskUpdate(update_type, task_identifier, task_subidentifier, update_count, enforce_level_requirement, min_status, max_status); } XSRETURN_EMPTY; diff --git a/zone/gm_commands/reloadcontentflags.cpp b/zone/gm_commands/reloadcontentflags.cpp new file mode 100755 index 000000000..2098fa329 --- /dev/null +++ b/zone/gm_commands/reloadcontentflags.cpp @@ -0,0 +1,15 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_reloadcontentflags(Client *c, const Seperator *sep) +{ + if (c) { + auto pack = new ServerPacket(ServerOP_ReloadContentFlags, 0); + worldserver.SendPacket(pack); + c->Message(Chat::Red, "Successfully sent the packet to world to reload content flags globally."); + safe_delete(pack); + } +} + diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index a806be066..444f1ad03 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -1410,7 +1410,7 @@ void lua_add_spawn_point(luabind::adl::object table) { lua_remove_spawn_point(spawn2_id); auto t = new Spawn2(spawn2_id, spawngroup_id, x, y, z, heading, respawn, - variance, timeleft, grid, path_when_zone_idle, condition_id, + variance, timeleft, grid, path_when_zone_idle, condition_id, condition_min_value, enabled, static_cast(animation)); zone->spawn2_list.Insert(t); @@ -1743,7 +1743,7 @@ bool lua_is_content_flag_enabled(std::string content_flag){ } void lua_set_content_flag(std::string flag_name, bool enabled){ - ZoneStore::SetContentFlag(flag_name, enabled); + content_service.SetContentFlag(flag_name, enabled); } Lua_Expedition lua_get_expedition() { diff --git a/zone/main.cpp b/zone/main.cpp index eab175639..f92ea9ea8 100644 --- a/zone/main.cpp +++ b/zone/main.cpp @@ -386,9 +386,9 @@ int main(int argc, char** argv) { LogInfo("Initialized dynamic dictionary entries"); } - content_service.SetExpansionContext(); - - ZoneStore::LoadContentFlags(); + content_service.SetDatabase(&database) + ->SetExpansionContext() + ->ReloadContentFlags(); event_scheduler.SetDatabase(&database)->LoadScheduledEvents(); diff --git a/zone/spawngroup.cpp b/zone/spawngroup.cpp index c337bfac0..84ae2a8f9 100644 --- a/zone/spawngroup.cpp +++ b/zone/spawngroup.cpp @@ -244,8 +244,11 @@ bool ZoneDatabase::LoadSpawnGroups(const char *zone_name, uint16 version, SpawnG AND spawnentry.spawngroupID = spawn2.spawngroupID AND - zone = '{}'), - zone_name + zone = '{}' + {} + ), + zone_name, + ContentFilterCriteria::apply("spawnentry") ); results = QueryDatabase(query); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 09f1e8134..e478f62f4 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1977,6 +1977,34 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) RuleManager::Instance()->LoadRules(&database, RuleManager::Instance()->GetActiveRuleset(), true); break; } + case ServerOP_ReloadContentFlags: { + if (zone) { + worldserver.SendEmoteMessage( + 0, + 0, + AccountStatus::GMAdmin, + Chat::Yellow, + fmt::format( + "Content flags (and expansion) reloaded for {}.", + fmt::format( + "{} ({})", + zone->GetLongName(), + zone->GetZoneID() + ), + ( + zone->GetInstanceID() ? + fmt::format( + "Instance ID: {}", + zone->GetInstanceID() + ) : + "" + ) + ).c_str() + ); + } + content_service.SetExpansionContext()->ReloadContentFlags(); + break; + } case ServerOP_ReloadLogs: { LogSys.LoadLogDatabaseSettings(); break; @@ -2069,7 +2097,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } } } else if (update_type == CZUpdateType_Expedition) { - for (auto &client: entity_list.GetClientList()) { + for (auto &client: entity_list.GetClientList()) { if (client.second->GetExpedition() && client.second->GetExpedition()->GetID() == update_identifier) { DialogueWindow::Render(client.second, message); } @@ -2243,7 +2271,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } } break; - } + } break; } case ServerOP_CZMarquee: @@ -2290,7 +2318,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } } } else if (update_type == CZUpdateType_Expedition) { - for (auto &client: entity_list.GetClientList()) { + for (auto &client: entity_list.GetClientList()) { if (client.second->GetExpedition() && client.second->GetExpedition()->GetID() == update_identifier) { client.second->SendMarqueeMessage(type, priority, fade_in, fade_out, duration, message); } @@ -2343,7 +2371,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } } } else if (update_type == CZUpdateType_Expedition) { - for (auto &client: entity_list.GetClientList()) { + for (auto &client: entity_list.GetClientList()) { if (client.second->GetExpedition() && client.second->GetExpedition()->GetID() == update_identifier) { client.second->Message(type, message); } @@ -2425,7 +2453,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } } } else if (update_type == CZUpdateType_Expedition) { - for (auto &client: entity_list.GetClientList()) { + for (auto &client: entity_list.GetClientList()) { if (client.second->GetExpedition() && client.second->GetExpedition()->GetID() == update_identifier) { switch (update_subtype) { case CZMoveUpdateSubtype_MoveZone: @@ -2492,7 +2520,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } } } else if (update_type == CZUpdateType_Expedition) { - for (auto &client: entity_list.GetClientList()) { + for (auto &client: entity_list.GetClientList()) { if (client.second->GetExpedition() && client.second->GetExpedition()->GetID() == update_identifier) { client.second->SetEntityVariable(variable_name, variable_value); } @@ -2549,7 +2577,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } } } else if (update_type == CZUpdateType_Expedition) { - for (auto &client: entity_list.GetClientList()) { + for (auto &client: entity_list.GetClientList()) { if (client.second->GetExpedition() && client.second->GetExpedition()->GetID() == update_identifier) { client.second->Signal(signal); } @@ -2609,7 +2637,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) if (client_raid) { for (int member_index = 0; member_index < MAX_RAID_MEMBERS; member_index++) { if (client_raid->members[member_index].member && client_raid->members[member_index].member->IsClient()) { - auto raid_member = client_raid->members[member_index].member->CastToClient(); + auto raid_member = client_raid->members[member_index].member->CastToClient(); switch (update_subtype) { case CZSpellUpdateSubtype_Cast: raid_member->SpellFinished(spell_id, raid_member); @@ -2635,7 +2663,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } } } else if (update_type == CZUpdateType_Expedition) { - for (auto &client: entity_list.GetClientList()) { + for (auto &client: entity_list.GetClientList()) { if (client.second->GetExpedition() && client.second->GetExpedition()->GetID() == update_identifier) { switch (update_subtype) { case CZSpellUpdateSubtype_Cast: @@ -2792,9 +2820,9 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) break; } } - } + } } else if (update_type == CZUpdateType_Expedition) { - for (auto &client: entity_list.GetClientList()) { + for (auto &client: entity_list.GetClientList()) { if (client.second->GetExpedition() && client.second->GetExpedition()->GetID() == update_identifier) { switch (update_subtype) { case CZTaskUpdateSubtype_ActivityReset: @@ -2968,7 +2996,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) uint8 min_status = WWSEV->min_status; uint8 max_status = WWSEV->max_status; if (update_type == WWSetEntityVariableUpdateType_Character) { - for (auto &client : entity_list.GetClientList()) { + for (auto &client : entity_list.GetClientList()) { if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == AccountStatus::Player)) { client.second->SetEntityVariable(variable_name, variable_value); } @@ -2988,7 +3016,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) uint8 min_status = WWS->min_status; uint8 max_status = WWS->max_status; if (update_type == WWSignalUpdateType_Character) { - for (auto &client : entity_list.GetClientList()) { + for (auto &client : entity_list.GetClientList()) { if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == AccountStatus::Player)) { client.second->Signal(signal); } @@ -3008,13 +3036,13 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) uint8 min_status = WWS->min_status; uint8 max_status = WWS->max_status; if (update_type == WWSpellUpdateType_Cast) { - for (auto &client : entity_list.GetClientList()) { + for (auto &client : entity_list.GetClientList()) { if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == AccountStatus::Player)) { client.second->SpellFinished(spell_id, client.second); } } } else if (update_type == WWSpellUpdateType_Remove) { - for (auto &client : entity_list.GetClientList()) { + for (auto &client : entity_list.GetClientList()) { if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == AccountStatus::Player)) { client.second->BuffFadeBySpellID(spell_id); } @@ -3031,8 +3059,8 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) int update_count = WWTU->update_count; bool enforce_level_requirement = WWTU->enforce_level_requirement; uint8 min_status = WWTU->min_status; - uint8 max_status = WWTU->max_status; - for (auto &client : entity_list.GetClientList()) { + uint8 max_status = WWTU->max_status; + for (auto &client : entity_list.GetClientList()) { if (client.second->Admin() >= min_status && (client.second->Admin() <= max_status || max_status == AccountStatus::Player)) { switch (update_type) { case WWTaskUpdateType_ActivityReset: diff --git a/zone/zone.cpp b/zone/zone.cpp index 2bfbd882b..2dffad339 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -1255,9 +1255,8 @@ void Zone::ReloadStaticData() { ); } // if that fails, try the file name, then load defaults - content_service.SetExpansionContext(); + content_service.SetExpansionContext()->ReloadContentFlags(); - ZoneStore::LoadContentFlags(); LogInfo("Zone Static Data Reloaded"); } @@ -2749,7 +2748,7 @@ uint32 Zone::GetCurrencyID(uint32 item_id) if (!item_id) { return 0; } - + for (const auto& alternate_currency : AlternateCurrencies) { if (item_id == alternate_currency.item_id) { return alternate_currency.id; @@ -2772,4 +2771,4 @@ uint32 Zone::GetCurrencyItemID(uint32 currency_id) } return 0; -} \ No newline at end of file +} diff --git a/zone/zone_event_scheduler.cpp b/zone/zone_event_scheduler.cpp index 4bdb2cd6f..2294ad50e 100644 --- a/zone/zone_event_scheduler.cpp +++ b/zone/zone_event_scheduler.cpp @@ -54,7 +54,7 @@ void ZoneEventScheduler::Process(Zone *zone, WorldContentService *content_servic auto flag_name = e.event_data; if (!flag_name.empty()) { LogScheduler("Deactivating event [{}] resetting content flags", e.description); - content_service->ReloadContentFlags(*m_database); + content_service->ReloadContentFlags(); } // force active events clear and reapply all active events because we reset the entire state @@ -117,8 +117,13 @@ void ZoneEventScheduler::Process(Zone *zone, WorldContentService *content_servic flag_name ); + // add new flag entity to stack auto flags = content_service->GetContentFlags(); - flags.push_back(flag_name); + auto f = ContentFlagsRepository::NewEntity(); + f.flag_name = flag_name; + f.enabled = 1; + flags.push_back(f); + content_service->SetContentFlags(flags); m_active_events.push_back(e); } diff --git a/zone/zone_store.cpp b/zone/zone_store.cpp index 67eaeb21e..52eeace3c 100644 --- a/zone/zone_store.cpp +++ b/zone/zone_store.cpp @@ -160,41 +160,3 @@ ZoneRepository::Zone ZoneStore::GetZone(const char *in_zone_name) return ZoneRepository::Zone(); } - -/** - * @return - */ -void ZoneStore::LoadContentFlags() -{ - content_service.ReloadContentFlags(database); -} - -/** - * Sets the value in the database and proceeds to load content flags into the server context again - * - * @param content_flag_name - * @param enabled - */ -void ZoneStore::SetContentFlag(const std::string &content_flag_name, bool enabled) -{ - auto content_flags = ContentFlagsRepository::GetWhere(database, - fmt::format("flag_name = '{}'", content_flag_name) - ); - - auto content_flag = ContentFlagsRepository::NewEntity(); - if (!content_flags.empty()) { - content_flag = content_flags.front(); - } - - content_flag.enabled = enabled ? 1 : 0; - content_flag.flag_name = content_flag_name; - - if (!content_flags.empty()) { - ContentFlagsRepository::UpdateOne(database, content_flag); - } - else { - ContentFlagsRepository::InsertOne(database, content_flag); - } - - LoadContentFlags(); -} diff --git a/zone/zone_store.h b/zone/zone_store.h index 521ec63bb..7238b0ade 100644 --- a/zone/zone_store.h +++ b/zone/zone_store.h @@ -42,9 +42,6 @@ public: std::string GetZoneLongName(uint32 zone_id); const char *GetZoneName(uint32 zone_id, bool error_unknown = false); const char *GetZoneLongName(uint32 zone_id, bool error_unknown = false); - - static void LoadContentFlags(); - static void SetContentFlag(const std::string& content_flag_name, bool enabled); }; extern ZoneStore zone_store; From 645251992dae8c572558a3b60432ee2f029c6aa3 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 2 Jan 2022 22:06:31 -0500 Subject: [PATCH 545/624] [Bug Fix] Remove possible Duel exploit. (#1911) * [Duels] Cleanup duel response/request logic. * Fixes and function name cleanup. * Patch file name changes. --- common/emu_oplist.h | 4 +- common/opcode_dispatch.h | 4 +- common/opcode_map.cpp | 4 +- utils/patches/opcodes.conf | 4 +- utils/patches/patch_RoF.conf | 4 +- utils/patches/patch_RoF2.conf | 4 +- utils/patches/patch_SoD.conf | 4 +- utils/patches/patch_SoF.conf | 4 +- utils/patches/patch_Titanium.conf | 4 +- utils/patches/patch_UF.conf | 4 +- zone/client.h | 4 +- zone/client_packet.cpp | 79 ++++++++++++++++++++++++------- zone/client_packet.h | 4 +- zone/lua_packet.cpp | 4 +- zone/perl_client.cpp | 4 +- 15 files changed, 89 insertions(+), 46 deletions(-) diff --git a/common/emu_oplist.h b/common/emu_oplist.h index 869a9bd3a..f5386e9b5 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -129,8 +129,8 @@ N(OP_DisciplineTimer), N(OP_DisciplineUpdate), N(OP_DiscordMerchantInventory), N(OP_DoGroupLeadershipAbility), -N(OP_DuelResponse), -N(OP_DuelResponse2), +N(OP_DuelDecline), +N(OP_DuelAccept), N(OP_DumpName), N(OP_Dye), N(OP_DynamicWall), diff --git a/common/opcode_dispatch.h b/common/opcode_dispatch.h index 496dcdea3..a19cb79a3 100644 --- a/common/opcode_dispatch.h +++ b/common/opcode_dispatch.h @@ -115,8 +115,8 @@ IN(OP_GMTraining, GMTrainee_Struct); IN(OP_GMEndTraining, GMTrainEnd_Struct); IN(OP_GMTrainSkill, GMSkillChange_Struct); IN(OP_RequestDuel, Duel_Struct); -IN(OP_DuelResponse, DuelResponse_Struct); -IN(OP_DuelResponse2, Duel_Struct); +IN(OP_DuelDecline, DuelResponse_Struct); +IN(OP_DuelAccept, Duel_Struct); IN(OP_SpawnAppearance, SpawnAppearance_Struct); IN(OP_BazaarInspect, BazaarInspect_Struct); IN(OP_Death, Death_Struct); diff --git a/common/opcode_map.cpp b/common/opcode_map.cpp index 7c8fed281..c0556cf2a 100644 --- a/common/opcode_map.cpp +++ b/common/opcode_map.cpp @@ -240,8 +240,8 @@ void load_opcode_names() opcode_map[0x00a1] = "LiveOP_SaveOnZoneReq"; opcode_map[0x0185] = "LiveOP_Logout"; opcode_map[0x0298] = "LiveOP_RequestDuel"; - opcode_map[0x0a5d] = "LiveOP_DuelResponse"; - opcode_map[0x016e] = "LiveOP_DuelResponse2"; + opcode_map[0x0a5d] = "LiveOP_DuelDecline"; + opcode_map[0x016e] = "LiveOP_DuelAccept"; opcode_map[0x007c] = "LiveOP_InstillDoubt"; opcode_map[0x00ac] = "LiveOP_SafeFallSuccess"; opcode_map[0x02fb] = "LiveOP_DisciplineUpdate"; diff --git a/utils/patches/opcodes.conf b/utils/patches/opcodes.conf index 562ba3347..3f5e87b5a 100644 --- a/utils/patches/opcodes.conf +++ b/utils/patches/opcodes.conf @@ -307,8 +307,8 @@ OP_RecipeAutoCombine=0x0353 OP_TradeSkillCombine=0x0b40 OP_RequestDuel=0x0000 -OP_DuelResponse=0x0000 -OP_DuelResponse2=0x0000 #when accepted +OP_DuelDecline=0x0000 +OP_DuelAccept=0x0000 #when accepted OP_RezzComplete=0x0000 #packet wrong on this OP_RezzRequest=0x0000 #packet wrong on this diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf index 93175209e..8065ca53c 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -288,8 +288,8 @@ OP_YellForHelp=0x0017 OP_LoadSpellSet=0x38b4 OP_Bandolier=0x2b6f OP_PotionBelt=0x2d1b # Was 0x4d3b -OP_DuelResponse=0x0dee -OP_DuelResponse2=0x5e04 +OP_DuelDecline=0x0dee +OP_DuelAccept=0x5e04 OP_SaveOnZoneReq=0x36b1 OP_ReadBook=0x383c OP_Dye=0x62d8 diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index ac69e5550..3ecb0df68 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -287,8 +287,8 @@ OP_YellForHelp=0x4e56 OP_LoadSpellSet=0x261d OP_Bandolier=0x7677 OP_PotionBelt=0x1a3e -OP_DuelResponse=0x6a46 -OP_DuelResponse2=0x68d3 +OP_DuelDecline=0x6a46 +OP_DuelAccept=0x68d3 OP_SaveOnZoneReq=0x600d OP_ReadBook=0x72df OP_Dye=0x23b9 diff --git a/utils/patches/patch_SoD.conf b/utils/patches/patch_SoD.conf index dd3ee0384..69f4c996b 100644 --- a/utils/patches/patch_SoD.conf +++ b/utils/patches/patch_SoD.conf @@ -281,7 +281,7 @@ OP_YellForHelp=0x6f79 # C OP_LoadSpellSet=0x7113 # C OP_Bandolier=0x441c # C OP_PotionBelt=0x5db5 # C -OP_DuelResponse=0x1ebb # C +OP_DuelDecline=0x1ebb # C OP_SaveOnZoneReq=0x6eff # C OP_ReadBook=0x2444 # C OP_Dye=0x3672 # C @@ -299,7 +299,7 @@ OP_DoGroupLeadershipAbility=0x540b # C OP_GroupLeadershipAAUpdate=0x0c33 OP_DelegateAbility=0x0322 # C OP_SetGroupTarget=0x521c # C -OP_DuelResponse2=0x52b5 # C +OP_DuelAccept=0x52b5 # C OP_Charm=0x7108 # C OP_Stun=0x2a6d # C OP_SendFindableNPCs=0x5360 diff --git a/utils/patches/patch_SoF.conf b/utils/patches/patch_SoF.conf index e6aa71168..56602d8f6 100644 --- a/utils/patches/patch_SoF.conf +++ b/utils/patches/patch_SoF.conf @@ -275,7 +275,7 @@ OP_YellForHelp=0x4F4A #Trevius 03/19/09 OP_LoadSpellSet=0x05B5 #Trevius 03/19/09 OP_Bandolier=0x3FD4 #Trevius 03/19/09 OP_PotionBelt=0x16F3 #Trevius 03/19/09 -OP_DuelResponse=0x5E59 #Derision 2009 +OP_DuelDecline=0x5E59 #Derision 2009 OP_SaveOnZoneReq=0x1103 #Trevius 03/20/09 OP_ReadBook=0x424a #Xinu 03/19/09 OP_Dye=0x3611 #Xinu 03/19/09 @@ -292,7 +292,7 @@ OP_ClearRaidNPCMarks=0x56a9 # OP_DoGroupLeadershipAbility=0x5a64 #Derision 2009 OP_DelegateAbility=0x57e3 #Derision 2009 OP_SetGroupTarget=0x1651 #Derision 2009 -OP_DuelResponse2=0x2A85 #Derision 2009 +OP_DuelAccept=0x2A85 #Derision 2009 OP_Charm=0x2F32 #Derision 2009 OP_Stun=0x55BF #Derision 2009 OP_FindPersonRequest=0x07F0 #Derision 2009 diff --git a/utils/patches/patch_Titanium.conf b/utils/patches/patch_Titanium.conf index 2c647f95c..28074dcf9 100644 --- a/utils/patches/patch_Titanium.conf +++ b/utils/patches/patch_Titanium.conf @@ -356,8 +356,8 @@ OP_RecipeAutoCombine=0x0353 OP_TradeSkillCombine=0x0b40 OP_RequestDuel=0x28e1 -OP_DuelResponse=0x3bad -OP_DuelResponse2=0x1b09 #when accepted +OP_DuelDecline=0x3bad +OP_DuelAccept=0x1b09 #when accepted OP_RezzComplete=0x4b05 OP_RezzRequest=0x1035 diff --git a/utils/patches/patch_UF.conf b/utils/patches/patch_UF.conf index 4dd97fad0..6a98e3b02 100644 --- a/utils/patches/patch_UF.conf +++ b/utils/patches/patch_UF.conf @@ -290,8 +290,8 @@ OP_YellForHelp=0x55a8 # C OP_LoadSpellSet=0x6617 # C OP_Bandolier=0x510c # C OP_PotionBelt=0x0651 # C -OP_DuelResponse=0x41a6 # C -OP_DuelResponse2=0x6d60 # C +OP_DuelDecline=0x41a6 # C +OP_DuelAccept=0x6d60 # C OP_SaveOnZoneReq=0x2913 # C OP_ReadBook=0x465e # C OP_Dye=0x2137 # C diff --git a/zone/client.h b/zone/client.h index 2ba96bd30..c6e12071e 100644 --- a/zone/client.h +++ b/zone/client.h @@ -783,9 +783,9 @@ public: void GMKill(); inline bool IsMedding() const {return medding;} - inline uint16 GetDuelTarget() const { return duel_target; } + inline uint32 GetDuelTarget() const { return duel_target; } inline bool IsDueling() const { return duelaccepted; } - inline void SetDuelTarget(uint16 set_id) { duel_target=set_id; } + inline void SetDuelTarget(uint32 set_id) { duel_target = set_id; } inline void SetDueling(bool duel) { duelaccepted = duel; } // use this one instead void MemSpell(uint16 spell_id, int slot, bool update_client = true); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 3bb32aa25..f50928479 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -194,8 +194,8 @@ void MapOpcodes() ConnectedOpcodes[OP_Disarm] = &Client::Handle_OP_Disarm; 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_DuelDecline] = &Client::Handle_OP_DuelDecline; + ConnectedOpcodes[OP_DuelAccept] = &Client::Handle_OP_DuelAccept; ConnectedOpcodes[OP_DumpName] = &Client::Handle_OP_DumpName; ConnectedOpcodes[OP_Dye] = &Client::Handle_OP_Dye; ConnectedOpcodes[OP_DzAddPlayer] = &Client::Handle_OP_DzAddPlayer; @@ -5543,37 +5543,57 @@ void Client::Handle_OP_DoGroupLeadershipAbility(const EQApplicationPacket *app) } } -void Client::Handle_OP_DuelResponse(const EQApplicationPacket *app) +void Client::Handle_OP_DuelDecline(const EQApplicationPacket *app) { - if (app->size != sizeof(DuelResponse_Struct)) + if (app->size != sizeof(DuelResponse_Struct)) { return; + } + DuelResponse_Struct* ds = (DuelResponse_Struct*)app->pBuffer; + if (!ds->target_id || !ds->entity_id) { + return; + } + 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()) + if (GetID() == initiator->GetID()) { entity->CastToClient()->MessageString(Chat::NPCQuestSay, DUEL_DECLINE, initiator->GetName()); - else + } else { initiator->CastToClient()->MessageString(Chat::NPCQuestSay, DUEL_DECLINE, entity->GetName()); + } return; } -void Client::Handle_OP_DuelResponse2(const EQApplicationPacket *app) +void Client::Handle_OP_DuelAccept(const EQApplicationPacket *app) { - if (app->size != sizeof(Duel_Struct)) + if (app->size != sizeof(Duel_Struct)) { return; + } Duel_Struct* ds = (Duel_Struct*)app->pBuffer; + if (!ds->duel_initiator || !ds->duel_target) { + return; + } + Entity* entity = entity_list.GetID(ds->duel_target); Entity* initiator = entity_list.GetID(ds->duel_initiator); + if (!entity || !initiator) { + return; + } - if (entity && initiator && entity == this && initiator->IsClient()) { + if (GetDuelTarget() != ds->duel_initiator || IsDueling()) { + return; + } + + if (entity == this && initiator->IsClient()) { auto outapp = new EQApplicationPacket(OP_RequestDuel, sizeof(Duel_Struct)); Duel_Struct* ds2 = (Duel_Struct*)outapp->pBuffer; @@ -5581,7 +5601,7 @@ void Client::Handle_OP_DuelResponse2(const EQApplicationPacket *app) ds2->duel_target = entity->GetID(); initiator->CastToClient()->QueuePacket(outapp); - outapp->SetOpcode(OP_DuelResponse2); + outapp->SetOpcode(OP_DuelAccept); ds2->duel_initiator = initiator->GetID(); initiator->CastToClient()->QueuePacket(outapp); @@ -5592,10 +5612,13 @@ void Client::Handle_OP_DuelResponse2(const EQApplicationPacket *app) SetDuelTarget(ds->duel_initiator); safe_delete(outapp); - if (IsCasting()) + if (IsCasting()) { InterruptSpell(); - if (initiator->CastToClient()->IsCasting()) + } + + if (initiator->CastToClient()->IsCasting()) { initiator->CastToClient()->InterruptSpell(); + } } return; } @@ -12455,34 +12478,54 @@ void Client::Handle_OP_Report(const EQApplicationPacket *app) void Client::Handle_OP_RequestDuel(const EQApplicationPacket *app) { - if (app->size != sizeof(Duel_Struct)) + if (app->size != sizeof(Duel_Struct)) { return; + } EQApplicationPacket* outapp = app->Copy(); Duel_Struct* ds = (Duel_Struct*)outapp->pBuffer; + if (!ds->duel_initiator || !ds->duel_target) { + return; + } + 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)) { + + if ( + GetID() != ds->duel_target && + entity->IsClient() && + entity->CastToClient()->IsDueling() && + entity->CastToClient()->GetDuelTarget() + ) { MessageString(Chat::NPCQuestSay, DUEL_CONSIDERING, entity->GetName()); return; } + if (IsDueling()) { MessageString(Chat::NPCQuestSay, DUEL_INPROGRESS); return; } - if (GetID() != ds->duel_target && entity->IsClient() && GetDuelTarget() == 0 && !IsDueling() && !entity->CastToClient()->IsDueling() && entity->CastToClient()->GetDuelTarget() == 0) { + if ( + GetID() != ds->duel_target && + entity->IsClient() && + !GetDuelTarget() && + !IsDueling() && + !entity->CastToClient()->IsDueling() && + !entity->CastToClient()->GetDuelTarget() + ) { 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 + } else { safe_delete(outapp); + } + return; } diff --git a/zone/client_packet.h b/zone/client_packet.h index ce85e38c5..887372cde 100644 --- a/zone/client_packet.h +++ b/zone/client_packet.h @@ -97,8 +97,8 @@ void Handle_OP_Disarm(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_DuelDecline(const EQApplicationPacket *app); + void Handle_OP_DuelAccept(const EQApplicationPacket *app); void Handle_OP_DumpName(const EQApplicationPacket *app); void Handle_OP_Dye(const EQApplicationPacket *app); void Handle_OP_DzAddPlayer(const EQApplicationPacket *app); diff --git a/zone/lua_packet.cpp b/zone/lua_packet.cpp index c7a039ca4..b07a7011f 100644 --- a/zone/lua_packet.cpp +++ b/zone/lua_packet.cpp @@ -582,7 +582,7 @@ luabind::scope lua_register_packet_opcodes() { luabind::value("Logout", static_cast(OP_Logout)), luabind::value("LogoutReply", static_cast(OP_LogoutReply)), luabind::value("PreLogoutReply", static_cast(OP_PreLogoutReply)), - luabind::value("DuelResponse2", static_cast(OP_DuelResponse2)), + luabind::value("DuelAccept", static_cast(OP_DuelAccept)), luabind::value("InstillDoubt", static_cast(OP_InstillDoubt)), luabind::value("SafeFallSuccess", static_cast(OP_SafeFallSuccess)), luabind::value("DisciplineUpdate", static_cast(OP_DisciplineUpdate)), @@ -659,7 +659,7 @@ luabind::scope lua_register_packet_opcodes() { luabind::value("TraderDelItem", static_cast(OP_TraderDelItem)), luabind::value("AdventureMerchantPurchase", static_cast(OP_AdventureMerchantPurchase)), luabind::value("TestBuff", static_cast(OP_TestBuff)), - luabind::value("DuelResponse", static_cast(OP_DuelResponse)), + luabind::value("DuelDecline", static_cast(OP_DuelDecline)), luabind::value("RequestDuel", static_cast(OP_RequestDuel)), luabind::value("BazaarInspect", static_cast(OP_BazaarInspect)), luabind::value("ClickDoor", static_cast(OP_ClickDoor)), diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 6e236a4cd..544b7c50b 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -1753,7 +1753,7 @@ XS(XS_Client_GetDuelTarget) { Perl_croak(aTHX_ "Usage: Client::GetDuelTarget(THIS)"); // @categories Account and Character, Script Utility { Client *THIS; - uint16 RETVAL; + uint32 RETVAL; dXSTARG; VALIDATE_THIS_IS_CLIENT; RETVAL = THIS->GetDuelTarget(); @@ -1786,7 +1786,7 @@ XS(XS_Client_SetDuelTarget) { Perl_croak(aTHX_ "Usage: Client::SetDuelTarget(THIS, set_id)"); // @categories Account and Character { Client *THIS; - uint16 set_id = (uint16) SvUV(ST(1)); + uint32 set_id = (uint32) SvUV(ST(1)); VALIDATE_THIS_IS_CLIENT; THIS->SetDuelTarget(set_id); } From 220d8497dd7f853b4813906f91a1d83c8b36f963 Mon Sep 17 00:00:00 2001 From: mmcgarvey Date: Sun, 2 Jan 2022 22:07:57 -0500 Subject: [PATCH 546/624] [XTarget] Disallow Corpses in XTarget Auto Slots (#1881) * [XTarget] Disallow Corpses in XTarget Auto Slots Why: There exists an odd state where corpses will fill up your XTarget window. This is reproducable using a combination of a pet to kill a mob and timely feign death to wipe the owner's aggro. What: Added an IsCorpse check to IsXTarget. Added a block to mark corpse XTargets as dirty to ProcessXTargetAutoHaters * fixup! [XTarget] Disallow Corpses in XTarget Auto Slots * fixup! [XTarget] Disallow Corpses in XTarget Auto Slots * [XTarget] Disallow Corpses Code Cleanup Added some safety, performance, and code readability changes per PR request. --- zone/attack.cpp | 5 +++++ zone/client.cpp | 16 +++++++++++++--- zone/mob.cpp | 4 ++++ zone/mob.h | 2 ++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index ff9f85264..173574641 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1799,6 +1799,7 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQ::skills::Skill entity_list.RemoveFromTargets(this, true); hate_list.RemoveEntFromHateList(this); RemoveAutoXTargets(); + ProcessXTargetAutoHaters(); //remove ourself from all proximities ClearAllProximities(); @@ -2569,6 +2570,10 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy } entity_list.RemoveFromAutoXTargets(this); + + if (killer->GetUltimateOwner() && killer->GetUltimateOwner()->IsClient()) { + killer->GetUltimateOwner()->CastToClient()->ProcessXTargetAutoHaters(); + } uint16 emoteid = this->GetEmoteID(); auto corpse = new Corpse(this, &itemlist, GetNPCTypeID(), &NPCTypedata, level > 54 ? RuleI(NPC, MajorNPCCorpseDecayTimeMS) diff --git a/zone/client.cpp b/zone/client.cpp index 62ffafd17..2bf75bf7f 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -7172,7 +7172,7 @@ void Client::OpenLFGuildWindow() bool Client::IsXTarget(const Mob *m) const { - if(!XTargettingAvailable() || !m || (m->GetID() == 0)) + if(!XTargettingAvailable() || !m || !m->IsValidXTarget()) return false; for(int i = 0; i < GetMaxXTargets(); ++i) @@ -7215,10 +7215,10 @@ void Client::UpdateClientXTarget(Client *c) // IT IS NOT SAFE TO CALL THIS IF IT'S NOT INITIAL AGGRO void Client::AddAutoXTarget(Mob *m, bool send) { - m_activeautohatermgr->increment_count(m); - if (!XTargettingAvailable() || !XTargetAutoAddHaters || IsXTarget(m)) return; + + m_activeautohatermgr->increment_count(m); for(int i = 0; i < GetMaxXTargets(); ++i) { @@ -7428,8 +7428,17 @@ void Client::ProcessXTargetAutoHaters() if (XTargets[i].Type != Auto) continue; + auto *mob = entity_list.GetMob(XTargets[i].ID); + if (XTargets[i].ID != 0 && !GetXTargetAutoMgr()->contains_mob(XTargets[i].ID)) { XTargets[i].ID = 0; + XTargets[i].Name[0] = 0; + XTargets[i].dirty = true; + } + + if (XTargets[i].ID != 0 && mob && !mob->IsValidXTarget()) { + XTargets[i].ID = 0; + XTargets[i].Name[0] = 0; XTargets[i].dirty = true; } @@ -7465,6 +7474,7 @@ void Client::ProcessXTargetAutoHaters() break; } } + m_dirtyautohaters = false; SendXTargetUpdates(); } diff --git a/zone/mob.cpp b/zone/mob.cpp index f1aebb7f2..48893ae6b 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -6649,3 +6649,7 @@ void Mob::SetBucket(std::string bucket_name, std::string bucket_value, std::stri std::string full_bucket_name = fmt::format("{}-{}", GetBucketKey(), bucket_name); DataBucket::SetData(full_bucket_name, bucket_value, expiration); } + +bool Mob::IsValidXTarget() const { + return (GetID() > 0 || !IsCorpse()); +} diff --git a/zone/mob.h b/zone/mob.h index 50ad8af58..dd5c29b70 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1307,6 +1307,8 @@ public: std::string GetBucketRemaining(std::string bucket_name); void SetBucket(std::string bucket_name, std::string bucket_value, std::string expiration = ""); + bool IsValidXTarget() const; + #ifdef BOTS // Bots HealRotation methods bool IsHealRotationTarget() { return (m_target_of_heal_rotation.use_count() && m_target_of_heal_rotation.get()); } From d107ff3069fc7df0627e2f1cc79e73580d4c01db Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 3 Jan 2022 01:06:17 -0600 Subject: [PATCH 547/624] [Hotfix] Add additional check to IsContentFlagEnabled given refactor from #1909 --- common/content/world_content_service.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/content/world_content_service.cpp b/common/content/world_content_service.cpp index 42397cdbb..c869d486a 100644 --- a/common/content/world_content_service.cpp +++ b/common/content/world_content_service.cpp @@ -131,7 +131,7 @@ void WorldContentService::SetContentFlags(std::vector Date: Mon, 3 Jan 2022 14:54:36 -0600 Subject: [PATCH 548/624] [Bug Fix] OP_Taunt checks if we have the skill (#1913) --- zone/client_packet.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index f50928479..d23353494 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -14117,6 +14117,10 @@ void Client::Handle_OP_Taunt(const EQApplicationPacket *app) std::cout << "Wrong size on OP_Taunt. Got: " << app->size << ", Expected: " << sizeof(ClientTarget_Struct) << std::endl; return; } + + if (!HasSkill(EQ::skills::SkillTaunt)) { + return; + } if (!p_timers.Expired(&database, pTimerTaunt, false)) { Message(Chat::Red, "Ability recovery time not yet met."); From d6d4c458e776ceede4b61207f337c567e8f4bfc6 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Mon, 3 Jan 2022 12:55:10 -0800 Subject: [PATCH 549/624] [Database] Mark titles as a server table so it at least shows up in dumps (#1914) --- common/database_schema.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/common/database_schema.h b/common/database_schema.h index ba5fb1c45..fc110bf2d 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -82,7 +82,6 @@ namespace DatabaseSchema { {"player_titlesets", "char_id"}, {"quest_globals", "charid"}, {"timers", "char_id"}, - {"titles", "char_id"}, {"trader", "char_id"}, {"zone_flags", "charID"} }; @@ -158,7 +157,6 @@ namespace DatabaseSchema { "spell_buckets", "spell_globals", "timers", - "titles", "trader", "trader_audit", "zone_flags" @@ -270,6 +268,7 @@ namespace DatabaseSchema { "perl_event_export_settings", "profanity_list", "rule_sets", + "titles", "rule_values", "variables", }; From 3853c4f15058b58db3a4afee19efe78842953979 Mon Sep 17 00:00:00 2001 From: mmcgarvey Date: Mon, 3 Jan 2022 22:22:21 -0500 Subject: [PATCH 550/624] [Bug Fix] XTarget Changes Causing Crashes (#1915) * [Bugfix] XTarget Changes Causing Crashes Added nullptr check when processing XTargetAutoHaters for pet owner. * [Bugfix] XTarget Changes Causing Crashes Added another needed sanity check after the nullptr check. * [Bugfix] XTarget Changes Causing Crashes Added another check against nullptr. * [Bugfix] XTarget Changes Causing Crashes Cleaned up nullptr checks per PR comments. --- zone/attack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 173574641..3216b92d7 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2571,7 +2571,7 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy entity_list.RemoveFromAutoXTargets(this); - if (killer->GetUltimateOwner() && killer->GetUltimateOwner()->IsClient()) { + if (killer != nullptr && killer->GetUltimateOwner() && killer->GetUltimateOwner()->IsClient()) { killer->GetUltimateOwner()->CastToClient()->ProcessXTargetAutoHaters(); } uint16 emoteid = this->GetEmoteID(); From 6bf5608cf3daf9bff26d2df541174dd688fa7208 Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Mon, 3 Jan 2022 21:26:37 -0600 Subject: [PATCH 551/624] [Bug Fix] Add range check to OP_PickPocket (#1912) * [Bug Fix] Add range check to OP_PickPocket * Pickpocket distance is 15 constant according to Mackal * Re-add wiggle room for distance check due to pathing --- zone/client_packet.cpp | 47 +++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 30 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index d23353494..6db486f78 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -11066,48 +11066,35 @@ void Client::Handle_OP_PickPocket(const EQApplicationPacket *app) return; p_timers.Start(pTimerBeggingPickPocket, 8); + auto 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(EQ::skills::SkillPickPockets); + pick_out->type = 0; + //if we do not send this packet the client will lock up and require the player to relog. + if (victim == this) { Message(0, "You catch yourself red-handed."); - auto 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(EQ::skills::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!"); - auto 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(EQ::skills::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 (Distance(GetPosition(), victim->GetPosition()) > 20) { + Message(Chat::Red, "Attempt to pickpocket out of range detected."); + database.SetMQDetectionFlag(this->AccountName(), this->GetName(), "OP_PickPocket was sent from outside combat range.", zone->GetShortName()); } else if (victim->IsNPC()) { + safe_delete(outapp); victim->CastToNPC()->PickPocket(this); + return; } else { Message(0, "Stealing from clients not yet supported."); - auto 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(EQ::skills::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); } + QueuePacket(outapp); + safe_delete(outapp); } void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app) From ffa968f64fca86f106bf502e1be2444749a1b675 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 4 Jan 2022 17:18:17 -0500 Subject: [PATCH 552/624] [Bug Fix] Disallow multiple augments in same item. (#1916) - Disallows multiple augments via #augmentitem or otherwise. - Added ItemInstance::ContainsAugmentByID(item_id) helper method for finding an augment in an item instance. --- common/emu_constants.h | 7 + common/item_instance.cpp | 21 +- common/item_instance.h | 1 + common/ruletypes.h | 1 + zone/client_packet.cpp | 415 +++++++++++++++++---------------------- zone/tradeskills.cpp | 166 +++++++--------- 6 files changed, 280 insertions(+), 331 deletions(-) diff --git a/common/emu_constants.h b/common/emu_constants.h index 88ca7f924..3c5680a37 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -381,4 +381,11 @@ enum Invisibility : uint8 { Special = 255 }; +enum AugmentActions : int { + Insert, + Remove, + Swap, + Destroy +}; + #endif /*COMMON_EMU_CONSTANTS_H*/ diff --git a/common/item_instance.cpp b/common/item_instance.cpp index d0bbbca50..5ff9e9630 100644 --- a/common/item_instance.cpp +++ b/common/item_instance.cpp @@ -689,6 +689,25 @@ bool EQ::ItemInstance::IsAugmented() return false; } +bool EQ::ItemInstance::ContainsAugmentByID(uint32 item_id) +{ + if (!m_item || !m_item->IsClassCommon()) { + return false; + } + + if (!item_id) { + return false; + } + + for (uint8 augment_slot = invaug::SOCKET_BEGIN; augment_slot <= invaug::SOCKET_END; ++augment_slot) { + if (GetAugmentItemID(augment_slot) == item_id) { + return true; + } + } + + return false; +} + // Has attack/delay? bool EQ::ItemInstance::IsWeapon() const { @@ -1706,4 +1725,4 @@ EvolveInfo::EvolveInfo(uint32 first, uint8 max, bool allkills, uint32 L2, uint32 EvolveInfo::~EvolveInfo() { -} +} \ No newline at end of file diff --git a/common/item_instance.h b/common/item_instance.h index 297908a50..008e4aefd 100644 --- a/common/item_instance.h +++ b/common/item_instance.h @@ -132,6 +132,7 @@ namespace EQ void DeleteAugment(uint8 slot); ItemInstance* RemoveAugment(uint8 index); bool IsAugmented(); + bool ContainsAugmentByID(uint32 item_id); ItemInstance* GetOrnamentationAug(int32 ornamentationAugtype) const; bool UpdateOrnamentationInfo(); static bool CanTransform(const ItemData *ItemToTry, const ItemData *Container, bool AllowAll = false); diff --git a/common/ruletypes.h b/common/ruletypes.h index 67d898476..056b568ed 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -744,6 +744,7 @@ RULE_BOOL(Inventory, EnforceAugmentWear, true, "Forces augment wear slot validat RULE_BOOL(Inventory, DeleteTransformationMold, true, "False if you want mold to last forever") RULE_BOOL(Inventory, AllowAnyWeaponTransformation, false, "Weapons can use any weapon transformation") RULE_BOOL(Inventory, TransformSummonedBags, false, "Transforms summoned bags into disenchanted ones instead of deleting") +RULE_BOOL(Inventory, AllowMultipleOfSameAugment, false, "Allows multiple of the same augment to be placed in an item via #augmentitem or MQ2, set to true to allow") RULE_CATEGORY_END() RULE_CATEGORY(Client) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 6db486f78..c25aad971 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -2929,18 +2929,16 @@ void Client::Handle_OP_AugmentInfo(const EQApplicationPacket *app) void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) { if (app->size != sizeof(AugmentItem_Struct)) { - LogError("Invalid size for AugmentItem_Struct: Expected: [{}], Got: [{}]", - sizeof(AugmentItem_Struct), app->size); + LogError("Invalid size for AugmentItem_Struct: Expected: [{}], Got: [{}]", sizeof(AugmentItem_Struct), app->size); return; } AugmentItem_Struct* in_augment = (AugmentItem_Struct*)app->pBuffer; - bool deleteItems = false; - if (ClientVersion() >= EQ::versions::ClientVersion::RoF) - { - if ((in_augment->container_slot < EQ::invslot::EQUIPMENT_BEGIN || in_augment->container_slot > EQ::invslot::GENERAL_END) && - (in_augment->container_slot < EQ::invbag::GENERAL_BAGS_BEGIN || in_augment->container_slot > EQ::invbag::GENERAL_BAGS_END)) - { + if (ClientVersion() >= EQ::versions::ClientVersion::RoF) { + if ( + (in_augment->container_slot < EQ::invslot::EQUIPMENT_BEGIN || in_augment->container_slot > EQ::invslot::GENERAL_END) && + (in_augment->container_slot < EQ::invbag::GENERAL_BAGS_BEGIN || in_augment->container_slot > EQ::invbag::GENERAL_BAGS_END) + ) { Message(Chat::Red, "The server does not allow augmentation actions from this slot."); auto cursor_item = m_inv[EQ::invslot::slotCursor]; auto augmented_item = m_inv[in_augment->container_slot]; @@ -2950,20 +2948,16 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) return; } - EQ::ItemInstance *itemOneToPush = nullptr, *itemTwoToPush = nullptr; - - //Log(Logs::DebugLevel::Moderate, Logs::Debug, "cslot: [{}] aslot: [{}] cidx: [{}] aidx: [{}] act: [{}] dest: [{}]", - // 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); + EQ::ItemInstance *item_one_to_push = nullptr, *item_two_to_push = nullptr; EQ::ItemInstance *tobe_auged = nullptr, *old_aug = nullptr, *new_aug = nullptr, *aug = nullptr, *solvent = nullptr; EQ::InventoryProfile& user_inv = GetInv(); uint16 item_slot = in_augment->container_slot; uint16 solvent_slot = in_augment->augment_slot; - uint8 mat = EQ::InventoryProfile::CalcMaterialFromSlot(item_slot); // for when player is augging a piece of equipment while they're wearing it + uint8 material = EQ::InventoryProfile::CalcMaterialFromSlot(item_slot); // for when player is augging a piece of equipment while they're wearing it - if (item_slot == INVALID_INDEX || solvent_slot == INVALID_INDEX) - { + if (item_slot == INVALID_INDEX || solvent_slot == INVALID_INDEX) { Message(Chat::Red, "Error: Invalid Aug Index."); return; } @@ -2971,272 +2965,231 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) tobe_auged = user_inv.GetItem(item_slot); solvent = user_inv.GetItem(solvent_slot); - if (!tobe_auged) - { + if (!tobe_auged) { Message(Chat::Red, "Error: Invalid item passed for augmenting."); return; } - if ((in_augment->augment_action == 1) || (in_augment->augment_action == 2)) - { - // Check for valid distiller if safely removing / swapping an augmentation - - if (!solvent) - { + if (in_augment->augment_action == AugmentActions::Remove || in_augment->augment_action == AugmentActions::Swap) { + if (!solvent) { // Check for valid distiller if safely removing / swapping an augmentation old_aug = tobe_auged->GetAugment(in_augment->augment_index); if (!old_aug || old_aug->GetItem()->AugDistiller != 0) { LogError("Player tried to safely remove an augment without a distiller"); Message(Chat::Red, "Error: Missing an augmentation distiller for safely removing this augment."); return; } - } - else if (solvent->GetItem()->ItemType == EQ::item::ItemTypeAugmentationDistiller) - { + } else if (solvent->GetItem()->ItemType == EQ::item::ItemTypeAugmentationDistiller) { old_aug = tobe_auged->GetAugment(in_augment->augment_index); - if (!old_aug) - { + if (!old_aug) { LogError("Player tried to safely remove a nonexistent augment"); Message(Chat::Red, "Error: No augment found in slot %i for safely removing.", in_augment->augment_index); return; - } - else if (solvent->GetItem()->ID != old_aug->GetItem()->AugDistiller) - { + } else if (solvent->GetItem()->ID != old_aug->GetItem()->AugDistiller) { LogError("Player tried to safely remove an augment with the wrong distiller (item [{}] vs expected [{}])", solvent->GetItem()->ID, old_aug->GetItem()->AugDistiller); Message(Chat::Red, "Error: Wrong augmentation distiller for safely removing this augment."); return; } - } - else if (solvent->GetItem()->ItemType != EQ::item::ItemTypePerfectedAugmentationDistiller) - { + } else if (solvent->GetItem()->ItemType != EQ::item::ItemTypePerfectedAugmentationDistiller) { LogError("Player tried to safely remove an augment with a non-distiller item"); Message(Chat::Red, "Error: Invalid augmentation distiller for safely removing this augment."); return; } } - switch (in_augment->augment_action) - { - case 0: // Adding an augment - case 2: // Swapping augment - new_aug = user_inv.GetItem(EQ::invslot::slotCursor); + switch (in_augment->augment_action) { + case AugmentActions::Insert: + case AugmentActions::Swap: + new_aug = user_inv.GetItem(EQ::invslot::slotCursor); - if (!new_aug) // Shouldn't get the OP code without the augment on the user's cursor, but maybe it's h4x. - { - LogError("AugmentItem OpCode with 'Insert' or 'Swap' action received, but no augment on client's cursor"); - Message(Chat::Red, "Error: No augment found on cursor for inserting."); - return; - } - else - { - if (((tobe_auged->IsAugmentSlotAvailable(new_aug->GetAugmentType(), in_augment->augment_index)) != -1) && - (tobe_auged->AvailableWearSlot(new_aug->GetItem()->Slots))) - { - old_aug = tobe_auged->RemoveAugment(in_augment->augment_index); - if (old_aug) - { - // An old augment was removed in order to be replaced with the new one (augment_action 2) - - CalcBonuses(); - - std::vector args; - args.push_back(old_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, old_aug, nullptr, "", in_augment->augment_index, &args); - } - - tobe_auged->PutAugment(in_augment->augment_index, *new_aug); - tobe_auged->UpdateOrnamentationInfo(); - - 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(Chat::Red, "Error: Could not properly insert augmentation into augment slot %i. Aborting.", in_augment->augment_index); + if (!new_aug) { // Shouldn't get the OP code without the augment on the user's cursor, but maybe it's h4x. + LogError("AugmentItem OpCode with 'Insert' or 'Swap' action received, but no augment on client's cursor"); + Message(Chat::Red, "Error: No augment found on cursor for inserting."); + return; + } else { + if (!RuleB(Inventory, AllowMultipleOfSameAugment) && tobe_auged->ContainsAugmentByID(new_aug->GetID())) { + Message(Chat::Red, "Error: Cannot put multiple of the same augment in an item."); return; } - itemOneToPush = tobe_auged->Clone(); - if (old_aug) - { - itemTwoToPush = old_aug->Clone(); - } - - // Must push items after the items in inventory are deleted - necessary due to lore items... - if (itemOneToPush) - { - DeleteItemInInventory(item_slot, 0, true); - DeleteItemInInventory(EQ::invslot::slotCursor, new_aug->IsStackable() ? 1 : 0, true); - - if (solvent) - { - // Consume the augment distiller - DeleteItemInInventory(solvent_slot, solvent->IsStackable() ? 1 : 0, true); - } - - if (itemTwoToPush) - { - // This is a swap. Return the old aug to the player's cursor. - if (!PutItemInInventory(EQ::invslot::slotCursor, *itemTwoToPush, true)) - { - LogError("Problem returning old augment to player's cursor after augmentation swap"); - Message(Chat::Yellow, "Error: Failed to retrieve old augment after augmentation swap!"); - } - } - - if (PutItemInInventory(item_slot, *itemOneToPush, true)) - { - // Successfully added an augment to the item - + if ( + ((tobe_auged->IsAugmentSlotAvailable(new_aug->GetAugmentType(), in_augment->augment_index)) != -1) && + tobe_auged->AvailableWearSlot(new_aug->GetItem()->Slots) + ) { + old_aug = tobe_auged->RemoveAugment(in_augment->augment_index); + if (old_aug) { // An old augment was removed in order to be replaced with the new one (augment_action 2) CalcBonuses(); - if (mat != EQ::textures::materialInvalid) - { - SendWearChange(mat); // Visible item augged while equipped. Send WC in case ornamentation changed. + std::vector args; + args.push_back(old_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, old_aug, nullptr, "", in_augment->augment_index, &args); + } + + tobe_auged->PutAugment(in_augment->augment_index, *new_aug); + tobe_auged->UpdateOrnamentationInfo(); + + 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( + Chat::Red, + fmt::format( + "Error: Could not properly insert augmentation into augment slot {}. Aborting.", + in_augment->augment_index + ).c_str() + ); + return; + } + + item_one_to_push = tobe_auged->Clone(); + if (old_aug) { + item_two_to_push = old_aug->Clone(); + } + + if (item_one_to_push) { // Must push items after the items in inventory are deleted - necessary due to lore items... + DeleteItemInInventory(item_slot, 0, true); + DeleteItemInInventory(EQ::invslot::slotCursor, new_aug->IsStackable() ? 1 : 0, true); + + if (solvent) { // Consume the augment distiller + DeleteItemInInventory(solvent_slot, solvent->IsStackable() ? 1 : 0, true); } + + if (item_two_to_push) { // This is a swap. Return the old aug to the player's cursor. + if (!PutItemInInventory(EQ::invslot::slotCursor, *item_two_to_push, true)) { + LogError("Problem returning old augment to player's cursor after augmentation swap"); + Message(Chat::Yellow, "Error: Failed to retrieve old augment after augmentation swap!"); + } + } + + if (PutItemInInventory(item_slot, *item_one_to_push, true)) { // Successfully added an augment to the item + CalcBonuses(); + if (material != EQ::textures::materialInvalid) { // Visible item augged while equipped. Send WC in case ornamentation changed. + SendWearChange(material); + } + } else { + Message(Chat::Red, "Error: No available slot for end result. Please free up the augment slot."); + } + } else { + Message(Chat::Red, "Error in cloning item for augment. Aborted."); } - else - { - Message(Chat::Red, "Error: No available slot for end result. Please free up the augment slot."); - } - } - else - { - Message(Chat::Red, "Error in cloning item for augment. Aborted."); + } else { + Message(Chat::Red, "Error: No available slot for augment in that item."); } } - else - { - Message(Chat::Red, "Error: No available slot for augment in that item."); + break; + case AugmentActions::Remove: + 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(Chat::Red, "Error: Could not find augmentation to remove at index %i. Aborting.", in_augment->augment_index); + return; } - } - break; - case 1: // Removing augment safely (distiller) - 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); + old_aug = tobe_auged->RemoveAugment(in_augment->augment_index); + tobe_auged->UpdateOrnamentationInfo(); - args.push_back(false); + item_one_to_push = tobe_auged->Clone(); + if (old_aug) { + item_two_to_push = old_aug->Clone(); + } - parse->EventItem(EVENT_AUGMENT_REMOVE, this, aug, nullptr, "", in_augment->augment_index, &args); - } - else - { - Message(Chat::Red, "Error: Could not find augmentation to remove at index %i. Aborting.", in_augment->augment_index); - return; - } + if (item_one_to_push && item_two_to_push) { + if (solvent) { + DeleteItemInInventory(solvent_slot, solvent->IsStackable() ? 1 : 0, true); // Consume the augment distiller + } - old_aug = tobe_auged->RemoveAugment(in_augment->augment_index); - tobe_auged->UpdateOrnamentationInfo(); + DeleteItemInInventory(item_slot, 0, true); // Remove the augmented item - itemOneToPush = tobe_auged->Clone(); - if (old_aug) - itemTwoToPush = old_aug->Clone(); + if (!PutItemInInventory(item_slot, *item_one_to_push, true)) { // Replace it with the unaugmented item + LogError("Problem returning equipment item to player's inventory after safe augment removal"); + Message(Chat::Yellow, "Error: Failed to return item after de-augmentation!"); + } - if (itemOneToPush && itemTwoToPush) - { - // Consume the augment distiller - if (solvent) - DeleteItemInInventory(solvent_slot, solvent->IsStackable() ? 1 : 0, true); + CalcBonuses(); - // Remove the augmented item - DeleteItemInInventory(item_slot, 0, true); + if (material != EQ::textures::materialInvalid) { + SendWearChange(material); // Visible item augged while equipped. Send WC in case ornamentation changed. + } - // Replace it with the unaugmented item - if (!PutItemInInventory(item_slot, *itemOneToPush, true)) - { - LogError("Problem returning equipment item to player's inventory after safe augment removal"); - Message(Chat::Yellow, "Error: Failed to return item after de-augmentation!"); + // Drop the removed augment on the player's cursor + if (!PutItemInInventory(EQ::invslot::slotCursor, *item_two_to_push, true)) { + LogError("Problem returning augment to player's cursor after safe removal"); + Message(Chat::Yellow, "Error: Failed to return augment after removal from item!"); + return; + } + } + break; + case AugmentActions::Destroy: + // RoF client does not require an augmentation solvent for destroying an augmentation in an item. + // Augments can be destroyed with a right click -> Destroy at any time. + 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(true); + parse->EventItem(EVENT_AUGMENT_REMOVE, this, aug, nullptr, "", in_augment->augment_index, &args); + } else { + Message( + Chat::Red, + fmt::format( + "Error: Could not find augmentation to remove at index {}. Aborting.", + in_augment->augment_index + ).c_str() + ); + return; + } + + tobe_auged->DeleteAugment(in_augment->augment_index); + tobe_auged->UpdateOrnamentationInfo(); + + item_one_to_push = tobe_auged->Clone(); + if (item_one_to_push) { + DeleteItemInInventory(item_slot, 0, true); + + if (!PutItemInInventory(item_slot, *item_one_to_push, true)) { + LogError("Problem returning equipment item to player's inventory after augment deletion"); + Message(Chat::Yellow, "Error: Failed to return item after destroying augment!"); + } } CalcBonuses(); - if (mat != EQ::textures::materialInvalid) - { - SendWearChange(mat); // Visible item augged while equipped. Send WC in case ornamentation changed. + if (material != EQ::textures::materialInvalid) { + SendWearChange(material); } - - // Drop the removed augment on the player's cursor - if (!PutItemInInventory(EQ::invslot::slotCursor, *itemTwoToPush, true)) - { - LogError("Problem returning augment to player's cursor after safe removal"); - Message(Chat::Yellow, "Error: Failed to return augment after removal from item!"); - return; - } - } - break; - case 3: // Destroying augment (formerly done in birdbath/sealer with a solvent) - - // RoF client does not require an augmentation solvent for destroying an augmentation in an item. - // Augments can be destroyed with a right click -> Destroy at any time. - - 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(true); - - parse->EventItem(EVENT_AUGMENT_REMOVE, this, aug, nullptr, "", in_augment->augment_index, &args); - } - else - { - Message(Chat::Red, "Error: Could not find augmentation to remove at index %i. Aborting."); - return; - } - - tobe_auged->DeleteAugment(in_augment->augment_index); - tobe_auged->UpdateOrnamentationInfo(); - - itemOneToPush = tobe_auged->Clone(); - if (itemOneToPush) - { - DeleteItemInInventory(item_slot, 0, true); - - if (!PutItemInInventory(item_slot, *itemOneToPush, true)) - { - LogError("Problem returning equipment item to player's inventory after augment deletion"); - Message(Chat::Yellow, "Error: Failed to return item after destroying augment!"); - } - } - - CalcBonuses(); - - if (mat != EQ::textures::materialInvalid) - { - SendWearChange(mat); - } - break; - default: // Unknown - LogInventory("Unrecognized augmentation action - cslot: [{}] aslot: [{}] cidx: [{}] aidx: [{}] act: [{}] dest: [{}]", - 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); - break; + break; + default: // Unknown + LogInventory( + "Unrecognized augmentation action - cslot: [{}] aslot: [{}] cidx: [{}] aidx: [{}] act: [{}] dest: [{}]", + 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 + ); + break; } - } - else - { - // Delegate to tradeskill object to perform combine - Object::HandleAugmentation(this, in_augment, m_tradeskill_object); + } else { + Object::HandleAugmentation(this, in_augment, m_tradeskill_object); // Delegate to tradeskill object to perform combine } return; } diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index 82d29e7f4..938aa1a0d 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -43,109 +43,93 @@ static const EQ::skills::SkillType TradeskillUnknown = EQ::skills::Skill1HBlunt; void Object::HandleAugmentation(Client* user, const AugmentItem_Struct* in_augment, Object *worldo) { - if (!user || !in_augment) - { + if (!user || !in_augment) { LogError("Client or AugmentItem_Struct not set in Object::HandleAugmentation"); return; } EQ::ItemInstance* container = nullptr; - if (worldo) - { + if (worldo) { container = worldo->m_inst; - } - else - { - // Check to see if they have an inventory container type 53 that is used for this. + } else { // Check to see if they have an inventory container type 53 that is used for this. EQ::InventoryProfile& user_inv = user->GetInv(); EQ::ItemInstance* inst = nullptr; inst = user_inv.GetItem(in_augment->container_slot); - if (inst) - { + if (inst) { const EQ::ItemData* item = inst->GetItem(); - if (item && inst->IsType(EQ::item::ItemClassBag) && item->BagType == 53) - { - // We have found an appropriate inventory augmentation sealer + if (item && inst->IsType(EQ::item::ItemClassBag) && item->BagType == EQ::item::BagTypeAugmentationSealer) { // We have found an appropriate inventory augmentation sealer container = inst; // Verify that no more than two items are in container to guarantee no inadvertant wipes. - uint8 itemsFound = 0; - for (uint8 i = EQ::invbag::SLOT_BEGIN; i < EQ::invtype::WORLD_SIZE; i++) - { + uint8 items_found = 0; + for (uint8 i = EQ::invbag::SLOT_BEGIN; i < EQ::invtype::WORLD_SIZE; i++) { const EQ::ItemInstance* inst = container->GetItem(i); - if (inst) - { - itemsFound++; + if (inst) { + items_found++; } } - if (itemsFound != 2) - { - user->Message(Chat::Red, "Error: Too many/few items in augmentation container."); + if (items_found < 2) { + user->Message(Chat::Red, "Error: Too few items in augmentation container."); + return; + } else if (items_found > 2) { + user->Message(Chat::Red, "Error: Too many items in augmentation container."); return; } } } } - if(!container) - { + if(!container) { LogError("Player tried to augment an item without a container set"); user->Message(Chat::Red, "Error: This item is not a container!"); return; } EQ::ItemInstance *tobe_auged = nullptr, *auged_with = nullptr; - int8 slot=-1; + int8 slot = -1; - // Verify 2 items in the augmentation device - if (container->GetItem(0) && container->GetItem(1)) - { + if (container->GetItem(0) && container->GetItem(1)) { // Verify 2 items in the augmentation device // Verify 1 item is augmentable and the other is not - if (container->GetItem(0)->IsAugmentable() && !container->GetItem(1)->IsAugmentable()) - { + if (container->GetItem(0)->IsAugmentable() && !container->GetItem(1)->IsAugmentable()) { tobe_auged = container->GetItem(0); auged_with = container->GetItem(1); - } - else if (!container->GetItem(0)->IsAugmentable() && container->GetItem(1)->IsAugmentable()) - { + } else if (!container->GetItem(0)->IsAugmentable() && container->GetItem(1)->IsAugmentable()) { tobe_auged = container->GetItem(1); auged_with = container->GetItem(0); - } - else - { + } else { // Either 2 augmentable items found or none found // This should never occur due to client restrictions, but prevent in case of a hack - user->Message(Chat::Red, "Error: Must be 1 augmentable item in the sealer"); + user->Message(Chat::Red, "Error: There must be 1 augmentable item in the sealer."); return; } - } - else - { - // This happens if the augment button is clicked more than once quickly while augmenting - if (!container->GetItem(0)) - { - user->Message(Chat::Red, "Error: No item in slot 0 of sealer"); + } else { // This happens if the augment button is clicked more than once quickly while augmenting + if (!container->GetItem(0)) { + user->Message(Chat::Red, "Error: No item in the first slot of sealer."); } - if (!container->GetItem(1)) - { - user->Message(Chat::Red, "Error: No item in slot 1 of sealer"); + + if (!container->GetItem(1)) { + user->Message(Chat::Red, "Error: No item in the second slot of sealer."); } return; } - bool deleteItems = false; + if (!RuleB(Inventory, AllowMultipleOfSameAugment) && tobe_auged->ContainsAugmentByID(auged_with->GetID())) { + user->Message(Chat::Red, "Error: Cannot put multiple of the same augment in an item."); + return; + } - EQ::ItemInstance *itemOneToPush = nullptr, *itemTwoToPush = nullptr; + bool delete_items = false; - // Adding augment - if (in_augment->augment_slot == -1) - { - if (((slot=tobe_auged->AvailableAugmentSlot(auged_with->GetAugmentType()))!=-1) && - (tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots))) - { + EQ::ItemInstance *item_one_to_push = nullptr, *item_two_to_push = nullptr; + + if (in_augment->augment_slot == -1) { // Adding augment + if ( + ((slot = tobe_auged->AvailableAugmentSlot(auged_with->GetAugmentType())) != -1) && + tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots) + ) { tobe_auged->PutAugment(slot, *auged_with); EQ::ItemInstance *aug = tobe_auged->GetAugment(slot); @@ -158,20 +142,15 @@ void Object::HandleAugmentation(Client* user, const AugmentItem_Struct* in_augme parse->EventItem(EVENT_AUGMENT_INSERT, user, aug, nullptr, "", slot, &args); } - itemOneToPush = tobe_auged->Clone(); - deleteItems = true; + item_one_to_push = tobe_auged->Clone(); + delete_items = true; + } else { + user->Message(Chat::Red, "Error: No available slot for augment."); } - else - { - user->Message(Chat::Red, "Error: No available slot for augment"); - } - } - else - { + } else { EQ::ItemInstance *old_aug = nullptr; - bool isSolvent = auged_with->GetItem()->ItemType == EQ::item::ItemTypeAugmentationSolvent; - if (!isSolvent && auged_with->GetItem()->ItemType != EQ::item::ItemTypeAugmentationDistiller) - { + bool is_solvent = auged_with->GetItem()->ItemType == EQ::item::ItemTypeAugmentationSolvent; + if (!is_solvent && auged_with->GetItem()->ItemType != EQ::item::ItemTypeAugmentationDistiller) { LogError("Player tried to remove an augment without a solvent or distiller"); user->Message(Chat::Red, "Error: Missing an augmentation solvent or distiller for removing this augment."); @@ -180,8 +159,7 @@ void Object::HandleAugmentation(Client* user, const AugmentItem_Struct* in_augme EQ::ItemInstance *aug = tobe_auged->GetAugment(in_augment->augment_slot); if (aug) { - if (!isSolvent && auged_with->GetItem()->ID != aug->GetItem()->AugDistiller) - { + if (!is_solvent && auged_with->GetItem()->ID != aug->GetItem()->AugDistiller) { LogError("Player tried to safely remove an augment with the wrong distiller (item [{}] vs expected [{}])", auged_with->GetItem()->ID, aug->GetItem()->AugDistiller); user->Message(Chat::Red, "Error: Wrong augmentation distiller for safely removing this augment."); return; @@ -191,29 +169,27 @@ void Object::HandleAugmentation(Client* user, const AugmentItem_Struct* in_augme parse->EventItem(EVENT_UNAUGMENT_ITEM, user, tobe_auged, nullptr, "", slot, &args); args.assign(1, tobe_auged); - args.push_back(&isSolvent); + args.push_back(&is_solvent); parse->EventItem(EVENT_AUGMENT_REMOVE, user, aug, nullptr, "", slot, &args); } - if (isSolvent) + if (is_solvent) { tobe_auged->DeleteAugment(in_augment->augment_slot); - else + } else { old_aug = tobe_auged->RemoveAugment(in_augment->augment_slot); + } - itemOneToPush = tobe_auged->Clone(); - if (old_aug) - itemTwoToPush = old_aug->Clone(); + item_one_to_push = tobe_auged->Clone(); + if (old_aug) { + item_two_to_push = old_aug->Clone(); + } - - - deleteItems = true; + delete_items = true; } - if (deleteItems) - { - if (worldo) - { + if (delete_items) { + if (worldo) { container->Clear(); auto outapp = new EQApplicationPacket(OP_ClearObject, sizeof(ClearObject_Struct)); ClearObject_Struct *cos = (ClearObject_Struct *)outapp->pBuffer; @@ -221,34 +197,26 @@ void Object::HandleAugmentation(Client* user, const AugmentItem_Struct* in_augme user->QueuePacket(outapp); safe_delete(outapp); database.DeleteWorldContainer(worldo->m_id, zone->GetZoneID()); - } - else - { - // Delete items in our inventory container... - for (uint8 i = EQ::invbag::SLOT_BEGIN; i < EQ::invtype::WORLD_SIZE; i++) - { + } else { // Delete items in our inventory container... + for (uint8 i = EQ::invbag::SLOT_BEGIN; i < EQ::invtype::WORLD_SIZE; i++) { const EQ::ItemInstance* inst = container->GetItem(i); - if (inst) - { + if (inst) { user->DeleteItemInInventory(EQ::InventoryProfile::CalcSlotId(in_augment->container_slot, i), 0, true); } } - // Explicitly mark container as cleared. - container->Clear(); + + container->Clear(); // Explicitly mark container as cleared. } } // Must push items after the items in inventory are deleted - necessary due to lore items... - if (itemOneToPush) - { - user->PushItemOnCursor(*itemOneToPush, true); + if (item_one_to_push) { + user->PushItemOnCursor(*item_one_to_push, true); } - if (itemTwoToPush) - { - user->PushItemOnCursor(*itemTwoToPush, true); + if (item_two_to_push) { + user->PushItemOnCursor(*item_two_to_push, true); } - } // Perform tradeskill combine From 7e065ad966b4c27d0663c81119b2530fe8425d63 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sat, 8 Jan 2022 13:57:31 -0600 Subject: [PATCH 553/624] [Docs] Update Readme to reflect new docs --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 83d26b7b0..225c246ab 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,11 @@ |**Install Count**|![Windows Install Count](http://analytics.akkadius.com/?install_count&windows_count)|![Linux Install Count](http://analytics.akkadius.com/?install_count&linux_count)| ### > Windows -* [Install Guide](https://eqemu.gitbook.io/server/categories/installation/server-installation-windows) +* [Install Guide](https://docs.eqemu.io/server/installation/server-installation-windows/) ### > Debian/Ubuntu/CentOS/Fedora -* [Install Guide](https://eqemu.gitbook.io/server/categories/installation/server-installation-linux) +* [Install Guide](https://docs.eqemu.io/server/installation/server-installation-linux/) * You can use curl or wget to kick off the installer (whichever your OS has) > curl -O https://raw.githubusercontent.com/EQEmu/Server/master/utils/scripts/linux_installer/install.sh install.sh && chmod 755 install.sh && ./install.sh @@ -56,7 +56,7 @@ forum, although pull requests will be much quicker and easier on all parties. ## Resources - [EQEmulator Forums](http://www.eqemulator.org/forums) -- [EQEmulator Wiki](https://eqemu.gitbook.io/) +- [EQEmulator Wiki](https://docs.eqemu.io/) ## Related Repositories * [ProjectEQ Quests](https://github.com/ProjectEQ/projecteqquests) From 10083387b6009c27678c10c2c7e2bd6d081af0d1 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 9 Jan 2022 08:32:09 -0500 Subject: [PATCH 554/624] [Spells] SPA 193 SE_SkillAttack will no longer trigger procs (#1919) * fixed * [Spells] SPA 193 SE_SkillAttack will no longer trigger procs --- zone/common.h | 1 + zone/mob.cpp | 1 + zone/mob.h | 6 +- zone/special_attacks.cpp | 132 ++++++++++++++++++++------------------- zone/spell_effects.cpp | 4 +- 5 files changed, 74 insertions(+), 70 deletions(-) diff --git a/zone/common.h b/zone/common.h index a7a5f07a0..e79293388 100644 --- a/zone/common.h +++ b/zone/common.h @@ -737,6 +737,7 @@ typedef struct int ammo_slot; uint8 skill; float speed_mod; + bool disable_procs; } tProjatk; //eventually turn this into a typedef and diff --git a/zone/mob.cpp b/zone/mob.cpp index 48893ae6b..d754e27e2 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -355,6 +355,7 @@ Mob::Mob( ProjectileAtk[i].ammo_slot = 0; ProjectileAtk[i].skill = 0; ProjectileAtk[i].speed_mod = 0.0f; + ProjectileAtk[i].disable_procs = false; } for (int i = 0; i < MAX_FOCUS_PROC_LIMIT_TIMERS; i++) { diff --git a/zone/mob.h b/zone/mob.h index dd5c29b70..edd3ac36a 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1000,10 +1000,10 @@ public: int32 ReduceAllDamage(int32 damage); void DoSpecialAttackDamage(Mob *who, EQ::skills::SkillType skill, int base_damage, int min_damage = 0, int32 hate_override = -1, int ReuseTime = 10); - virtual void DoThrowingAttackDmg(Mob* other, const EQ::ItemInstance* RangeWeapon = nullptr, const EQ::ItemData* AmmoItem = nullptr, uint16 weapon_damage = 0, int16 chance_mod = 0, int16 focus = 0, int ReuseTime = 0, uint32 range_id = 0, int AmmoSlot = 0, float speed = 4.0f); + virtual void DoThrowingAttackDmg(Mob* other, const EQ::ItemInstance* RangeWeapon = nullptr, const EQ::ItemData* AmmoItem = nullptr, uint16 weapon_damage = 0, int16 chance_mod = 0, int16 focus = 0, int ReuseTime = 0, uint32 range_id = 0, int AmmoSlot = 0, float speed = 4.0f, bool DisableProcs = false); void DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, EQ::skills::SkillType skillinuse, int16 chance_mod = 0, int16 focus = 0, bool CanRiposte = false, int ReuseTime = 0); - virtual void DoArcheryAttackDmg(Mob* other, const EQ::ItemInstance* RangeWeapon = nullptr, const EQ::ItemInstance* Ammo = nullptr, uint16 weapon_damage = 0, int16 chance_mod = 0, int16 focus = 0, int ReuseTime = 0, uint32 range_id = 0, uint32 ammo_id = 0, const EQ::ItemData *AmmoItem = nullptr, int AmmoSlot = 0, float speed = 4.0f); - bool TryProjectileAttack(Mob* other, const EQ::ItemData *item, EQ::skills::SkillType skillInUse, uint16 weapon_dmg, const EQ::ItemInstance* RangeWeapon, const EQ::ItemInstance* Ammo, int AmmoSlot, float speed); + virtual void DoArcheryAttackDmg(Mob* other, const EQ::ItemInstance* RangeWeapon = nullptr, const EQ::ItemInstance* Ammo = nullptr, uint16 weapon_damage = 0, int16 chance_mod = 0, int16 focus = 0, int ReuseTime = 0, uint32 range_id = 0, uint32 ammo_id = 0, const EQ::ItemData *AmmoItem = nullptr, int AmmoSlot = 0, float speed = 4.0f, bool DisableProcs = false); + bool TryProjectileAttack(Mob* other, const EQ::ItemData *item, EQ::skills::SkillType skillInUse, uint16 weapon_dmg, const EQ::ItemInstance* RangeWeapon, const EQ::ItemInstance* Ammo, int AmmoSlot, float speed, bool DisableProcs = false); void ProjectileAttack(); inline bool HasProjectileAttack() const { return ActiveProjectileATK; } inline void SetProjectileAttack(bool value) { ActiveProjectileATK = value; } diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 5116eb481..0a51d10fb 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -783,7 +783,7 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) { void Mob::DoArcheryAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, const EQ::ItemInstance *Ammo, uint16 weapon_damage, int16 chance_mod, int16 focus, int ReuseTime, uint32 range_id, - uint32 ammo_id, const EQ::ItemData *AmmoItem, int AmmoSlot, float speed) + uint32 ammo_id, const EQ::ItemData *AmmoItem, int AmmoSlot, float speed, bool DisableProcs) { if ((other == nullptr || ((IsClient() && CastToClient()->dead) || (other->IsClient() && other->CastToClient()->dead)) || @@ -848,7 +848,7 @@ void Mob::DoArcheryAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, co if (LaunchProjectile) { // 1: Shoot the Projectile once we calculate weapon damage. TryProjectileAttack(other, AmmoItem, EQ::skills::SkillArchery, (WDmg + ADmg), RangeWeapon, - Ammo, AmmoSlot, speed); + Ammo, AmmoSlot, speed, DisableProcs); return; } @@ -896,46 +896,49 @@ void Mob::DoArcheryAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, co other->Damage(this, TotalDmg, SPELL_UNKNOWN, EQ::skills::SkillArchery); + if (!DisableProcs) { + // Weapon Proc + if (RangeWeapon && other && !other->HasDied()) { + TryCombatProcs(RangeWeapon, other, EQ::invslot::slotRange); + } - // Weapon Proc - if (RangeWeapon && other && !other->HasDied()) { - TryCombatProcs(RangeWeapon, other, EQ::invslot::slotRange); - } - - // Ammo Proc, do not try spell procs if from ammo. - if (last_ammo_used) { - TryWeaponProc(nullptr, last_ammo_used, other, EQ::invslot::slotRange); - } - else if (Ammo && other && !other->HasDied()) { - TryWeaponProc(Ammo, Ammo->GetItem(), other, EQ::invslot::slotRange); + // Ammo Proc, do not try spell procs if from ammo. + if (last_ammo_used) { + TryWeaponProc(nullptr, last_ammo_used, other, EQ::invslot::slotRange); + } + else if (Ammo && other && !other->HasDied()) { + TryWeaponProc(Ammo, Ammo->GetItem(), other, EQ::invslot::slotRange); + } } TryCastOnSkillUse(other, EQ::skills::SkillArchery); - // Skill Proc Attempt - if (HasSkillProcs() && other && !other->HasDied()) { - if (ReuseTime) { - TrySkillProc(other, EQ::skills::SkillArchery, ReuseTime); + if (!DisableProcs) { + // Skill Proc Attempt + if (HasSkillProcs() && other && !other->HasDied()) { + if (ReuseTime) { + TrySkillProc(other, EQ::skills::SkillArchery, ReuseTime); + } + else { + TrySkillProc(other, EQ::skills::SkillArchery, 0, false, EQ::invslot::slotRange); + } } - else { - TrySkillProc(other, EQ::skills::SkillArchery, 0, false, EQ::invslot::slotRange); - } - } - // Skill Proc Success ... can proc off hits OR misses - if (HasSkillProcSuccess() && other && !other->HasDied()) { - if (ReuseTime) { - TrySkillProc(other, EQ::skills::SkillArchery, ReuseTime, true); - } - else { - TrySkillProc(other, EQ::skills::SkillArchery, 0, true, EQ::invslot::slotRange); + // Skill Proc Success ... can proc off hits OR misses + if (HasSkillProcSuccess() && other && !other->HasDied()) { + if (ReuseTime) { + TrySkillProc(other, EQ::skills::SkillArchery, ReuseTime, true); + } + else { + TrySkillProc(other, EQ::skills::SkillArchery, 0, true, EQ::invslot::slotRange); + } } } } bool Mob::TryProjectileAttack(Mob *other, const EQ::ItemData *item, EQ::skills::SkillType skillInUse, uint16 weapon_dmg, const EQ::ItemInstance *RangeWeapon, - const EQ::ItemInstance *Ammo, int AmmoSlot, float speed) + const EQ::ItemInstance *Ammo, int AmmoSlot, float speed, bool DisableProcs) { if (!other) return false; @@ -996,6 +999,7 @@ bool Mob::TryProjectileAttack(Mob *other, const EQ::ItemData *item, EQ::skills:: ProjectileAtk[slot].ammo_slot = AmmoSlot; ProjectileAtk[slot].skill = skillInUse; ProjectileAtk[slot].speed_mod = speed; + ProjectileAtk[slot].disable_procs = DisableProcs; SetProjectileAttack(true); @@ -1072,11 +1076,11 @@ void Mob::ProjectileAttack() DoArcheryAttackDmg(target, nullptr, nullptr, ProjectileAtk[i].wpn_dmg, 0, 0, 0, ProjectileAtk[i].ranged_id, ProjectileAtk[i].ammo_id, nullptr, - ProjectileAtk[i].ammo_slot); + ProjectileAtk[i].ammo_slot, 4.0f, ProjectileAtk[i].disable_procs); else if (ProjectileAtk[i].skill == EQ::skills::SkillThrowing) DoThrowingAttackDmg(target, nullptr, nullptr, ProjectileAtk[i].wpn_dmg, 0, 0, 0, ProjectileAtk[i].ranged_id, - ProjectileAtk[i].ammo_slot); + ProjectileAtk[i].ammo_slot, 4.0f, ProjectileAtk[i].disable_procs); else if (ProjectileAtk[i].skill == EQ::skills::SkillConjuration && IsValidSpell(ProjectileAtk[i].wpn_dmg)) SpellOnTarget(ProjectileAtk[i].wpn_dmg, target, false, true, @@ -1376,12 +1380,12 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51 } void Mob::DoThrowingAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, const EQ::ItemData *AmmoItem, - uint16 weapon_damage, int16 chance_mod, int16 focus, int ReuseTime, uint32 range_id, - int AmmoSlot, float speed) + uint16 weapon_damage, int16 chance_mod, int16 focus, int ReuseTime, uint32 range_id, + int AmmoSlot, float speed, bool DisableProcs) { if ((other == nullptr || - ((IsClient() && CastToClient()->dead) || (other->IsClient() && other->CastToClient()->dead)) || - HasDied() || (!IsAttackAllowed(other)) || (other->GetInvul() || other->GetSpecialAbility(IMMUNE_MELEE)))) { + ((IsClient() && CastToClient()->dead) || (other->IsClient() && other->CastToClient()->dead)) || + HasDied() || (!IsAttackAllowed(other)) || (other->GetInvul() || other->GetSpecialAbility(IMMUNE_MELEE)))) { return; } @@ -1398,11 +1402,12 @@ void Mob::DoThrowingAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, c if (RuleB(Combat, ProjectileDmgOnImpact)) { if (AmmoItem) { LaunchProjectile = true; - } else { + } + else { if (!RangeWeapon && range_id) { if (IsClient()) { m_RangeWeapon = CastToClient()->m_inv[AmmoSlot]; - + if (m_RangeWeapon && m_RangeWeapon->GetItem() && m_RangeWeapon->GetItem()->ID == range_id) { RangeWeapon = m_RangeWeapon; } @@ -1412,7 +1417,8 @@ void Mob::DoThrowingAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, c } } } - } else if (AmmoItem) { + } + else if (AmmoItem) { SendItemAnimation(other, AmmoItem, EQ::skills::SkillThrowing); } @@ -1430,10 +1436,11 @@ void Mob::DoThrowingAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, c if (LaunchProjectile) { TryProjectileAttack(other, AmmoItem, EQ::skills::SkillThrowing, WDmg, RangeWeapon, - nullptr, AmmoSlot, speed); + nullptr, AmmoSlot, speed); return; } - } else { + } + else { WDmg = weapon_damage; } @@ -1458,7 +1465,8 @@ void Mob::DoThrowingAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, c TotalDmg = my_hit.damage_done; LogCombat("Item DMG [{}]. Hit for damage [{}]", WDmg, TotalDmg); - } else { + } + else { TotalDmg = DMG_INVULNERABLE; } @@ -1467,25 +1475,29 @@ void Mob::DoThrowingAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, c other->Damage(this, TotalDmg, SPELL_UNKNOWN, EQ::skills::SkillThrowing); - if (other && !other->HasDied()) { + if (!DisableProcs && other && !other->HasDied()) { TryCombatProcs(RangeWeapon, other, EQ::invslot::slotRange, last_ammo_used); } - if (HasSkillProcs() && other && !other->HasDied()) { - if (ReuseTime) { - TrySkillProc(other, EQ::skills::SkillThrowing, ReuseTime); - } - else { - TrySkillProc(other, EQ::skills::SkillThrowing, 0, false, EQ::invslot::slotRange); - } - } + TryCastOnSkillUse(other, EQ::skills::SkillThrowing); - if (HasSkillProcSuccess() && other && !other->HasDied()) { - if (ReuseTime) { - TrySkillProc(other, EQ::skills::SkillThrowing, ReuseTime, true); + if (!DisableProcs) { + if (HasSkillProcs() && other && !other->HasDied()) { + if (ReuseTime) { + TrySkillProc(other, EQ::skills::SkillThrowing, ReuseTime); + } + else { + TrySkillProc(other, EQ::skills::SkillThrowing, 0, false, EQ::invslot::slotRange); + } } - else { - TrySkillProc(other, EQ::skills::SkillThrowing, 0, true, EQ::invslot::slotRange); + + if (HasSkillProcSuccess() && other && !other->HasDied()) { + if (ReuseTime) { + TrySkillProc(other, EQ::skills::SkillThrowing, ReuseTime, true); + } + else { + TrySkillProc(other, EQ::skills::SkillThrowing, 0, true, EQ::invslot::slotRange); + } } } @@ -2250,10 +2262,8 @@ void Mob::DoMeleeSkillAttackDmg(Mob *other, uint16 weapon_damage, EQ::skills::Sk damage = DMG_INVULNERABLE; } - bool CanSkillProc = true; if (skillinuse == EQ::skills::SkillOffense) { // Hack to allow damage to display. skillinuse = EQ::skills::SkillTigerClaw; //'strike' your opponent - Arbitrary choice for message. - CanSkillProc = false; // Disable skill procs } other->AddToHateList(this, hate, 0); @@ -2263,14 +2273,6 @@ void Mob::DoMeleeSkillAttackDmg(Mob *other, uint16 weapon_damage, EQ::skills::Sk return; TryCastOnSkillUse(other, skillinuse); - - if (CanSkillProc && HasSkillProcs()) { - TrySkillProc(other, skillinuse, ReuseTime); - } - - if (CanSkillProc && (damage > 0) && HasSkillProcSuccess()) { - TrySkillProc(other, skillinuse, ReuseTime, true); - } } bool Mob::CanDoSpecialAttack(Mob *other) { diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index c9d76de7a..cc4e90ccd 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2275,10 +2275,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove switch(spells[spell_id].skill) { case EQ::skills::SkillThrowing: - caster->DoThrowingAttackDmg(this, nullptr, nullptr, spells[spell_id].base_value[i],spells[spell_id].limit_value[i], 0, ReuseTime); + caster->DoThrowingAttackDmg(this, nullptr, nullptr, spells[spell_id].base_value[i],spells[spell_id].limit_value[i], 0, ReuseTime, 0, 0, 4.0f, true); break; case EQ::skills::SkillArchery: - caster->DoArcheryAttackDmg(this, nullptr, nullptr, spells[spell_id].base_value[i],spells[spell_id].limit_value[i], 0, ReuseTime); + caster->DoArcheryAttackDmg(this, nullptr, nullptr, spells[spell_id].base_value[i],spells[spell_id].limit_value[i], 0, ReuseTime, 0, 0, nullptr, 0, 4.0f, true); break; default: caster->DoMeleeSkillAttackDmg(this, spells[spell_id].base_value[i], spells[spell_id].skill, spells[spell_id].limit_value[i], 0, false, ReuseTime); From ae8273e0b194a231466e3568622f6294b241a5fc Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Mon, 10 Jan 2022 17:33:28 -0500 Subject: [PATCH 555/624] [Commands] #guild create argument count bug fix. (#1920) - Names with spaces were breaking command. --- zone/gm_commands/guild.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/gm_commands/guild.cpp b/zone/gm_commands/guild.cpp index b220dc53b..aea407f7a 100755 --- a/zone/gm_commands/guild.cpp +++ b/zone/gm_commands/guild.cpp @@ -64,7 +64,7 @@ void command_guild(Client *c, const Seperator *sep) } if (is_create) { - if (arguments != 3) { + if (arguments < 3) { c->Message(Chat::White, "Usage: #guild create [Character ID|Character Name] [Guild Name]"); } else { auto leader_id = ( From 59c373bcff57b80d27fbbd6b76b273b58615da4a Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Mon, 10 Jan 2022 21:22:56 -0500 Subject: [PATCH 556/624] Update client_packet.cpp (#1923) bug fix --- zone/client_packet.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index c25aad971..0e1556623 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -10717,6 +10717,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) if (mypet->IsPetStop()) { mypet->SetPetStop(false); } else { + mypet->SetPetStop(true); mypet->StopNavigation(); mypet->SetTarget(nullptr); if (mypet->IsPetRegroup()) { From f3002d9656ee12c6266fdd15ec2d2a3e9488ab93 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 11 Jan 2022 06:09:29 -0500 Subject: [PATCH 557/624] [Commands] Cleanup #who Command. (#1924) * [Commands] Cleanup #who Command. - Cleanup messages and logic. - Add GetAccountStatusMap() and GetAccountStatusName() helpers for account status stuff. - Use Chat::Who instead of Chat::Magenta so you can more easily see saylinks. - Add a summon saylink to the list of saylinks so you can summon the player. * New line. --- common/emu_constants.cpp | 40 ++++- common/emu_constants.h | 3 + zone/gm_commands/who.cpp | 343 ++++++++++++++++++++++----------------- 3 files changed, 233 insertions(+), 153 deletions(-) diff --git a/common/emu_constants.cpp b/common/emu_constants.cpp index 0d5f3c8cd..7c084bc9d 100644 --- a/common/emu_constants.cpp +++ b/common/emu_constants.cpp @@ -298,4 +298,42 @@ std::string EQ::constants::GetBodyTypeName(bodyType bodytype_id) return bodytypes[bodytype_id]; } return std::string(); -} \ No newline at end of file +} + +const std::map& EQ::constants::GetAccountStatusMap() +{ + static const std::map account_status_map = { + { AccountStatus::Player, "Player" }, + { AccountStatus::Steward, "Steward" }, + { AccountStatus::ApprenticeGuide, "Apprentice Guide" }, + { AccountStatus::Guide, "Guide" }, + { AccountStatus::QuestTroupe, "Quest Troupe" }, + { AccountStatus::SeniorGuide, "Senior Guide" }, + { AccountStatus::GMTester, "GM Tester" }, + { AccountStatus::EQSupport, "EQ Support" }, + { AccountStatus::GMStaff, "GM Staff" }, + { AccountStatus::GMAdmin, "GM Admin" }, + { AccountStatus::GMLeadAdmin, "GM Lead Admin" }, + { AccountStatus::QuestMaster, "Quest Master" }, + { AccountStatus::GMAreas, "GM Areas" }, + { AccountStatus::GMCoder, "GM Coder" }, + { AccountStatus::GMMgmt, "GM Mgmt" }, + { AccountStatus::GMImpossible, "GM Impossible" }, + { AccountStatus::Max, "GM Max" } + }; + return account_status_map; +} + +std::string EQ::constants::GetAccountStatusName(uint8 account_status) +{ + auto account_statuses = EQ::constants::GetAccountStatusMap(); + std::string status_name; + for (auto status_level = account_statuses.rbegin(); status_level != account_statuses.rend(); ++status_level) { + if (account_status >= status_level->first) { + status_name = status_level->second; + break; + } + } + + return status_name; +} diff --git a/common/emu_constants.h b/common/emu_constants.h index 3c5680a37..20985378f 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -245,6 +245,9 @@ namespace EQ extern const std::map& GetBodyTypeMap(); std::string GetBodyTypeName(bodyType bodytype_id); + extern const std::map& GetAccountStatusMap(); + std::string GetAccountStatusName(uint8 account_status); + const int STANCE_TYPE_FIRST = stancePassive; const int STANCE_TYPE_LAST = stanceBurnAE; const int STANCE_TYPE_COUNT = stanceBurnAE; diff --git a/zone/gm_commands/who.cpp b/zone/gm_commands/who.cpp index f39733d1e..43077573e 100755 --- a/zone/gm_commands/who.cpp +++ b/zone/gm_commands/who.cpp @@ -2,91 +2,53 @@ void command_who(Client *c, const Seperator *sep) { - std::string query = - SQL ( - SELECT - character_data.account_id, - character_data.name, - character_data.zone_id, - character_data.zone_instance, - COALESCE( - ( - select - guilds.name - from - guilds - where - id = ( + std::string query = SQL( + SELECT + character_data.account_id, + character_data.name, + character_data.zone_id, + character_data.zone_instance, + COALESCE( + ( + SELECT guilds.name FROM guilds WHERE id = ( ( - select - guild_id - from - guild_members - where - char_id = character_data.id - ) - ) - ), - "" - ) as guild_name, - character_data.level, - character_data.race, - character_data.class, - COALESCE( - ( - select - account.status - from - account - where - account.id = character_data.account_id - LIMIT - 1 - ), 0 - ) as account_status, - COALESCE( - ( - select - account.name - from - account - where - account.id = character_data.account_id - LIMIT - 1 - ), - 0 - ) as account_name, - COALESCE( - ( - select - account_ip.ip - from - account_ip - where - account_ip.accid = character_data.account_id - ORDER BY - account_ip.lastused DESC - LIMIT - 1 - ), - "" - ) as account_ip - FROM - character_data - WHERE - last_login > (UNIX_TIMESTAMP() - 600) - ORDER BY - character_data.name; - ); + SELECT guild_id FROM guild_members WHERE char_id = character_data.id + ) + ) + ), + "" + ) AS guild_name, + character_data.level, + character_data.race, + character_data.class, + COALESCE( + ( + SELECT account.status FROM account WHERE account.id = character_data.account_id LIMIT 1 + ), + 0 + ) AS account_status, + COALESCE( + ( + SELECT account.name FROM account WHERE account.id = character_data.account_id LIMIT 1 + ), + 0 + ) AS account_name, + COALESCE( + ( + SELECT account_ip.ip FROM account_ip WHERE account_ip.accid = character_data.account_id ORDER BY account_ip.lastused DESC LIMIT 1 + ), + "" + ) AS account_ip + FROM + character_data + WHERE + last_login > (UNIX_TIMESTAMP() - 600) + ORDER BY + character_data.name; + ); auto results = database.QueryDatabase(query); - if (!results.Success()) { - return; - } - - if (results.RowCount() == 0) { - c->Message(Chat::Yellow, "No results found"); + if (!results.Success() || !results.RowCount()) { return; } @@ -98,36 +60,36 @@ void command_who(Client *c, const Seperator *sep) int found_count = 0; - c->Message(Chat::Magenta, "Players in EverQuest"); - c->Message(Chat::Magenta, "--------------------"); + c->Message(Chat::Who, "Players in EverQuest:"); + c->Message(Chat::Who, "------------------------------"); - for (auto row = results.begin(); row != results.end(); ++row) { - auto account_id = static_cast(atoi(row[0])); - std::string player_name = row[1]; - auto zone_id = static_cast(atoi(row[2])); - std::string zone_short_name = ZoneName(zone_id); - auto zone_instance = static_cast(atoi(row[3])); - std::string guild_name = row[4]; - auto player_level = static_cast(atoi(row[5])); - auto player_race = static_cast(atoi(row[6])); - auto player_class = static_cast(atoi(row[7])); - auto account_status = static_cast(atoi(row[8])); - std::string account_name = row[9]; - std::string account_ip = row[10]; - std::string base_class_name = GetClassIDName(static_cast(player_class), 1); + for (auto row : results) { + auto account_id = std::stoul(row[0]); + std::string player_name = row[1]; + auto zone_id = std::stoul(row[2]); + std::string zone_short_name = ZoneName(zone_id); + std::string zone_long_name = ZoneLongName(zone_id); + auto zone_instance = std::stoul(row[3]); + std::string guild_name = row[4]; + auto player_level = std::stoul(row[5]); + auto player_race = std::stoul(row[6]); + auto player_class = std::stoul(row[7]); + auto account_status = std::stoul(row[8]); + std::string account_name = row[9]; + std::string account_ip = row[10]; + std::string base_class_name = GetClassIDName(static_cast(player_class)); std::string displayed_race_name = GetRaceIDName(static_cast(player_race)); - if (search_string.length() > 0) { - bool found_search_term = - ( - str_tolower(player_name).find(search_string) != std::string::npos || - str_tolower(zone_short_name).find(search_string) != std::string::npos || - str_tolower(displayed_race_name).find(search_string) != std::string::npos || - str_tolower(base_class_name).find(search_string) != std::string::npos || - str_tolower(guild_name).find(search_string) != std::string::npos || - str_tolower(account_name).find(search_string) != std::string::npos || - str_tolower(account_ip).find(search_string) != std::string::npos - ); + if (search_string.length()) { + bool found_search_term = ( + str_tolower(player_name).find(search_string) != std::string::npos || + str_tolower(zone_short_name).find(search_string) != std::string::npos || + str_tolower(displayed_race_name).find(search_string) != std::string::npos || + str_tolower(base_class_name).find(search_string) != std::string::npos || + str_tolower(guild_name).find(search_string) != std::string::npos || + str_tolower(account_name).find(search_string) != std::string::npos || + str_tolower(account_ip).find(search_string) != std::string::npos + ); if (!found_search_term) { continue; @@ -135,68 +97,145 @@ void command_who(Client *c, const Seperator *sep) } std::string displayed_guild_name; - if (guild_name.length() > 0) { + if (guild_name.length()) { displayed_guild_name = EQ::SayLinkEngine::GenerateQuestSaylink( - StringFormat( - "#who \"%s\"", - guild_name.c_str()), + fmt::format( + "#who \"{}\"", + guild_name + ), false, - StringFormat("<%s>", guild_name.c_str()) + fmt::format( + "<{}>", + guild_name + ) ); } - std::string goto_saylink = EQ::SayLinkEngine::GenerateQuestSaylink( - StringFormat("#goto %s", player_name.c_str()), false, "Goto" + auto goto_saylink = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format( + "#goto {}", + player_name + ), + false, + "Goto" + ); + + auto summon_saylink = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format( + "#summon {}", + player_name + ), + false, + "Summon" ); std::string display_class_name = GetClassIDName( static_cast(player_class), - static_cast(player_level)); + static_cast(player_level) + ); + + auto class_saylink = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format( + "#who {}", + base_class_name + ), + false, + display_class_name + ); + + auto race_saylink = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format( + "#who %s", + displayed_race_name + ), + false, + displayed_race_name + ); + + auto zone_saylink = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format( + "#who {}", + zone_short_name + ), + false, + zone_long_name + ); + + auto account_saylink = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format( + "#who {}", + account_name + ), + false, + account_name + ); + + auto account_ip_saylink = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format( + "#who {}", + account_ip + ), + false, + account_ip + ); + + auto status_level = ( + account_status ? + fmt::format( + "* {} * ", + EQ::constants::GetAccountStatusName(account_status) + ) : + "" + ); + + auto version_string = ( + zone_instance ? + fmt::format( + " ({})", + zone_instance + ) : + "" + ); c->Message( - 5, "%s[%u %s] %s (%s) %s ZONE: %s (%u) (%s) (%s) (%s)", - (account_status > 0 ? "* GM * " : ""), - player_level, - EQ::SayLinkEngine::GenerateQuestSaylink( - StringFormat("#who %s", base_class_name.c_str()), - false, - display_class_name - ).c_str(), - player_name.c_str(), - EQ::SayLinkEngine::GenerateQuestSaylink( - StringFormat("#who %s", displayed_race_name.c_str()), - false, - displayed_race_name - ).c_str(), - displayed_guild_name.c_str(), - EQ::SayLinkEngine::GenerateQuestSaylink( - StringFormat("#who %s", zone_short_name.c_str()), - false, - zone_short_name - ).c_str(), - zone_instance, - goto_saylink.c_str(), - EQ::SayLinkEngine::GenerateQuestSaylink( - StringFormat("#who %s", account_name.c_str()), - false, - account_name - ).c_str(), - EQ::SayLinkEngine::GenerateQuestSaylink( - StringFormat("#who %s", account_ip.c_str()), - false, - account_ip + Chat::Who, + fmt::format( + "{}[{} {} ({})] {} ({}) ({}) ({}) {} ZONE: {}{} ({} | {})", + status_level, + player_level, + class_saylink, + base_class_name, + player_name, + race_saylink, + account_saylink, + account_ip_saylink, + displayed_guild_name, + zone_saylink, + version_string, + goto_saylink, + summon_saylink ).c_str() ); found_count++; } + std::string count_string = found_count == 1 ? "is" : "are"; + std::string message = ( - found_count > 0 ? - StringFormat("There is %i player(s) in EverQuest", found_count).c_str() : - "There are no players in EverQuest that match those who filters." + found_count ? + fmt::format( + "There {} {} player{} in EverQuest.", + count_string, + found_count, + found_count > 1 ? "s" : "" + ) : + "There are no players in EverQuest that match those filters." ); - c->Message(Chat::Magenta, message.c_str()); + c->Message( + Chat::Who, + message.c_str() + ); } From d10145fc6fe2009552c03d8a8f4adb05c3f2bd29 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 11 Jan 2022 18:24:47 -0500 Subject: [PATCH 558/624] [Bug Fix] Fix Tradeskill Salvage (#1925) --- zone/tradeskills.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index 938aa1a0d..45fda2761 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -1128,9 +1128,11 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { itr = spec->salvage.begin(); uint8 sc = 0; while(itr != spec->salvage.end()) { - for(sc = 0; sc < itr->second; sc++) - if(zone->random.Roll(SalvageChance)) + for (sc = 0; sc < itr->second; sc++) { + if (zone->random.Roll(SalvageChance)) { SummonItem(itr->first, 1); + } + } ++itr; } } @@ -1403,7 +1405,6 @@ bool ZoneDatabase::GetTradeRecipe( DBTradeskillRecipe_Struct *spec ) { - std::string container_where_filter; if (some_id == 0) { // world combiner so no item number @@ -1526,10 +1527,10 @@ bool ZoneDatabase::GetTradeRecipe( "FROM tradeskill_recipe_entries " "WHERE salvagecount > 0 AND recipe_id = %u", recipe_id ); - + results = QueryDatabase(query); if (results.Success()) { - for (auto row = results.begin(); row != results.begin(); ++row) { + for (auto row = results.begin(); row != results.end(); ++row) { uint32 item = (uint32) atoi(row[0]); uint8 num = (uint8) atoi(row[1]); spec->salvage.push_back(std::pair(item, num)); From 1a556f44519788a689b747c3f6e74cf16db6b112 Mon Sep 17 00:00:00 2001 From: mmcgarvey Date: Wed, 12 Jan 2022 17:04:18 -0500 Subject: [PATCH 559/624] [XTarget] Performance Improvement After Corpse Change (#1918) Removed a conditional that was rendered obsolete by moving the addition of a mob to the auto haters list to fire after an IsValidXTarget check. This made an entity_list call unnecessary. [zone/client.cpp] Removed said unnecessary entity_list call. [zone/client.cpp] Removed a superfluous call to ProcessXTargetAutoHaters [zone/attack.cpp] --- zone/attack.cpp | 1 - zone/client.cpp | 10 +--------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 3216b92d7..c2e85530f 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1799,7 +1799,6 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQ::skills::Skill entity_list.RemoveFromTargets(this, true); hate_list.RemoveEntFromHateList(this); RemoveAutoXTargets(); - ProcessXTargetAutoHaters(); //remove ourself from all proximities ClearAllProximities(); diff --git a/zone/client.cpp b/zone/client.cpp index 2bf75bf7f..abfa0e8e0 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -7426,9 +7426,7 @@ void Client::ProcessXTargetAutoHaters() std::queue empty_slots; for (int i = 0; i < GetMaxXTargets(); ++i) { if (XTargets[i].Type != Auto) - continue; - - auto *mob = entity_list.GetMob(XTargets[i].ID); + continue; if (XTargets[i].ID != 0 && !GetXTargetAutoMgr()->contains_mob(XTargets[i].ID)) { XTargets[i].ID = 0; @@ -7436,12 +7434,6 @@ void Client::ProcessXTargetAutoHaters() XTargets[i].dirty = true; } - if (XTargets[i].ID != 0 && mob && !mob->IsValidXTarget()) { - XTargets[i].ID = 0; - XTargets[i].Name[0] = 0; - XTargets[i].dirty = true; - } - if (XTargets[i].ID == 0) { empty_slots.push(i); continue; From 613066976db8f42cf38ea375ee5aacbcdb6b0941 Mon Sep 17 00:00:00 2001 From: JJ <3617814+joligario@users.noreply.github.com> Date: Wed, 12 Jan 2022 22:04:58 -0500 Subject: [PATCH 560/624] [Bug Fix] Update to #1893 (#1926) * Update to #1893 * Missed bonuses.cpp --- common/spdat.h | 2 +- zone/attack.cpp | 6 +++--- zone/bonuses.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index d8b1bd188..e553daf34 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -190,7 +190,7 @@ #define MAX_FOCUS_PROC_LIMIT_TIMERS 20 //Number of focus recast timers that can be going at same time (This is arbitrary) #define MAX_PROC_LIMIT_TIMERS 8 //Number of proc delay timers that can be going at same time, different proc types get their own timer array. (This is arbitrary) #define MAX_APPEARANCE_EFFECTS 20 //Up to 20 Appearance Effects can be saved to a mobs appearance effect array, these will be sent to other clients when they enter a zone (This is arbitrary) -#define MAX_CAST_ON_SKILL_USE 36; //Actual amount is MAX/3 +#define MAX_CAST_ON_SKILL_USE 36 //Actual amount is MAX/3 const int Z_AGGRO=10; diff --git a/zone/attack.cpp b/zone/attack.cpp index c2e85530f..683ea78b5 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -5296,7 +5296,7 @@ void Mob::TryCastOnSkillUse(Mob *on, EQ::skills::SkillType skill) { } if (spellbonuses.HasSkillAttackProc[skill]) { - for (int i = 0; i < MAX_CAST_ON_SKILL_USE i += 3) { + for (int i = 0; i < MAX_CAST_ON_SKILL_USE; i += 3) { if (spellbonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID] && skill == spellbonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SKILL]) { if (IsValidSpell(spellbonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID]) && zone->random.Int(1, 1000) <= spellbonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_CHANCE]) { SpellFinished(spellbonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID], on, EQ::spells::CastingSlot::Item, 0, -1, spells[spellbonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID]].resist_difficulty); @@ -5306,7 +5306,7 @@ void Mob::TryCastOnSkillUse(Mob *on, EQ::skills::SkillType skill) { } if (itembonuses.HasSkillAttackProc[skill]) { - for (int i = 0; i < MAX_CAST_ON_SKILL_USE i += 3) { + for (int i = 0; i < MAX_CAST_ON_SKILL_USE; i += 3) { if (itembonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID] && skill == itembonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SKILL]) { if (IsValidSpell(itembonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID]) && zone->random.Int(1, 1000) <= spellbonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_CHANCE]) { SpellFinished(itembonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID], on, EQ::spells::CastingSlot::Item, 0, -1, spells[itembonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID]].resist_difficulty); @@ -5316,7 +5316,7 @@ void Mob::TryCastOnSkillUse(Mob *on, EQ::skills::SkillType skill) { } if (aabonuses.HasSkillAttackProc[skill]) { - for (int i = 0; i < MAX_CAST_ON_SKILL_USE i += 3) { + for (int i = 0; i < MAX_CAST_ON_SKILL_USE; i += 3) { if (IsValidSpell(aabonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID]) && aabonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID] && skill == aabonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SKILL]) { if (zone->random.Int(1, 1000) <= aabonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_CHANCE]) { SpellFinished(aabonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID], on, EQ::spells::CastingSlot::Item, 0, -1, spells[aabonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID]].resist_difficulty); diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 4cb4047e9..73f388fe1 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1207,7 +1207,7 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) } case SE_SkillAttackProc: { - for (int i = 0; i < MAX_CAST_ON_SKILL_USE i += 3) { + for (int i = 0; i < MAX_CAST_ON_SKILL_USE; i += 3) { if (!newbon->SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID]) { // spell id newbon->SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID] = rank.spell; // spell to proc newbon->SkillAttackProc[i + SBIndex::SKILLATK_PROC_CHANCE] = base_value; // Chance base 1000 = 100% proc rate @@ -3588,7 +3588,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne } case SE_SkillAttackProc: { - for (int i = 0; i < MAX_CAST_ON_SKILL_USE i += 3) { + for (int i = 0; i < MAX_CAST_ON_SKILL_USE; i += 3) { if (!new_bonus->SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID]) { // spell id new_bonus->SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID] = max_value; // spell to proc new_bonus->SkillAttackProc[i + SBIndex::SKILLATK_PROC_CHANCE] = effect_value; // Chance base 1000 = 100% proc rate From 91aa95030497fc2d2acdc6055684d976817db790 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 15 Jan 2022 18:28:21 -0500 Subject: [PATCH 561/624] fix for hasten AA (#1928) --- zone/aa.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/zone/aa.cpp b/zone/aa.cpp index f9ea7b0fd..ed7faafb9 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -1286,6 +1286,7 @@ int Mob::GetAlternateAdvancementCooldownReduction(AA::Rank *rank_in) { return 0; } + int total_reduction = 0; for(auto &aa : aa_ranks) { auto ability_rank = zone->GetAlternateAdvancementAbilityAndRank(aa.first, aa.second.first); auto ability = ability_rank.first; @@ -1297,12 +1298,12 @@ int Mob::GetAlternateAdvancementCooldownReduction(AA::Rank *rank_in) { for(auto &effect : rank->effects) { if(effect.effect_id == SE_HastenedAASkill && effect.limit_value == ability_in->id) { - return effect.base_value; + total_reduction += effect.base_value; } } } - return 0; + return total_reduction; } void Mob::ExpendAlternateAdvancementCharge(uint32 aa_id) { From 5f482a9b3006eebc2a99f2fdc67fedbb7b336ce0 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 15 Jan 2022 19:37:47 -0500 Subject: [PATCH 562/624] [Combat] Implemented rule for live like Riposte mechanics (#1927) * Live like Riposte * [Combat] Implemented rule for live like Riposte mechanics bot fix --- common/ruletypes.h | 2 +- zone/attack.cpp | 30 ++++++++++++++++++++++-------- zone/bot.cpp | 2 +- zone/mob.h | 2 +- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 056b568ed..e7817f4bb 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -508,7 +508,7 @@ RULE_INT(Combat, SneakPullAssistRange, 400, "Modified range of assist for sneak RULE_BOOL(Combat, Classic2HBAnimation, false, "2HB will use the 2 hand piercing animation instead of the overhead slashing animation") RULE_BOOL(Combat, ArcheryConsumesAmmo, true, "Set to false to disable Archery Ammo Consumption") RULE_BOOL(Combat, ThrowingConsumesAmmo, true, "Set to false to disable Throwing Ammo Consumption") -RULE_BOOL(Combat, ImmuneToEnrageFromRiposteSpellEffect, true, "Set to false to disable SPA 173 SE_RiposteChance from making those with the effect on them immune to enrage") +RULE_BOOL(Combat, UseLiveRiposteMechanics, false, "Set to true to disable SPA 173 SE_RiposteChance from making those with the effect on them immune to enrage, can longer riposte from a riposte.") RULE_CATEGORY_END() RULE_CATEGORY(NPC) diff --git a/zone/attack.cpp b/zone/attack.cpp index 683ea78b5..4392e983f 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -433,9 +433,20 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit) // riposte -- it may seem crazy, but if the attacker has SPA 173 on them, they are immune to Ripo bool ImmuneRipo = false; - if (RuleB(Combat, ImmuneToEnrageFromRiposteSpellEffect)) { + if (!RuleB(Combat, UseLiveRiposteMechanics)) { ImmuneRipo = attacker->aabonuses.RiposteChance || attacker->spellbonuses.RiposteChance || attacker->itembonuses.RiposteChance || attacker->IsEnraged(); } + /* + Live Riposte Mechanics (~Kayen updated 1/22) + -Ripostes can not trigger another riposte. (Ie. Riposte from defender can't then trigger the attacker to riposte) + -Ripostes can not be 'avoided', only hit or miss. + -Attacker with SPA 173 is not immune to riposte. The defender can riposte against the attackers melee hits. + + Legacy Riposte Mechanics + -Ripostes can trigger another riposte + -Attacker with SPA 173 is immune to riposte + -Attacker that is enraged is immune to riposte + */ // Need to check if we have something in MainHand to actually attack with (or fists) if (hit.hand != EQ::invslot::slotRange && (CanThisClassRiposte() || IsEnraged()) && InFront && !ImmuneRipo) { @@ -1373,25 +1384,27 @@ int Client::DoDamageCaps(int base_damage) // other is the defender, this is the attacker //SYNC WITH: tune.cpp, mob.h TuneDoAttack -void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts) +void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, bool FromRiposte) { if (!other) return; LogCombat("[{}]::DoAttack vs [{}] base [{}] min [{}] offense [{}] tohit [{}] skill [{}]", GetName(), other->GetName(), hit.base_damage, hit.min_damage, hit.offense, hit.tohit, hit.skill); - // check to see if we hit.. - if (other->AvoidDamage(this, hit)) { + if (!RuleB(Combat, UseLiveRiposteMechanics)) { + FromRiposte = false; + } + + // check to see if we hit.. + if (!FromRiposte && other->AvoidDamage(this, hit)) { int strike_through = itembonuses.StrikeThrough + spellbonuses.StrikeThrough + aabonuses.StrikeThrough; if (strike_through && zone->random.Roll(strike_through)) { MessageString(Chat::StrikeThrough, STRIKETHROUGH_STRING); // You strike through your opponents defenses! hit.damage_done = 1; // set to one, we will check this to continue } - // I'm pretty sure you can riposte a riposte if (hit.damage_done == DMG_RIPOSTED) { DoRiposte(other); - //if (IsDead()) return; } LogCombat("Avoided/strikethrough damage with code [{}]", hit.damage_done); @@ -1574,7 +1587,7 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b my_hit.tohit = GetTotalToHit(my_hit.skill, hit_chance_bonus); - DoAttack(other, my_hit, opts); + DoAttack(other, my_hit, opts, bRiposte); } else { my_hit.damage_done = DMG_INVULNERABLE; @@ -2166,7 +2179,7 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool my_hit.offense = offense(my_hit.skill); my_hit.tohit = GetTotalToHit(my_hit.skill, hit_chance_bonus); - DoAttack(other, my_hit, opts); + DoAttack(other, my_hit, opts, bRiposte); other->AddToHateList(this, hate); @@ -4822,6 +4835,7 @@ void Mob::DoRiposte(Mob *defender) } defender->Attack(this, EQ::invslot::slotPrimary, true); + if (HasDied()) return; diff --git a/zone/bot.cpp b/zone/bot.cpp index 031282051..c2b6b2d59 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4906,7 +4906,7 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b my_hit.tohit = GetTotalToHit(my_hit.skill, hit_chance_bonus); - DoAttack(other, my_hit, opts); + DoAttack(other, my_hit, opts, FromRiposte); LogCombat("Final damage after all reductions: [{}]", my_hit.damage_done); } else { diff --git a/zone/mob.h b/zone/mob.h index edd3ac36a..16bce03b3 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -195,7 +195,7 @@ public: // 13 = Primary (default), 14 = secondary virtual bool Attack(Mob* other, int Hand = EQ::invslot::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr) = 0; - void DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts = nullptr); + void DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts = nullptr, bool FromRiposte = false); int MonkSpecialAttack(Mob* other, uint8 skill_used); virtual void TryBackstab(Mob *other,int ReuseTime = 10); bool AvoidDamage(Mob *attacker, DamageHitInfo &hit); From 5ebbbf647b8baa217a310e4f010abcd0d257e2d0 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 16 Jan 2022 14:55:51 -0500 Subject: [PATCH 563/624] [Spells] SPA 299 Wake the Dead updates and crash fixes. SPA 306 Army of Dead implemented. (#1929) * start * wtd fix v1 * Update aa.cpp * rework done, army of dead supported * debugs * Update aa.cpp * Update spdat.h --- common/spdat.h | 4 +- zone/aa.cpp | 297 ++++++++++++++++++++++++----------------- zone/aa.h | 1 + zone/attack.cpp | 3 +- zone/entity.cpp | 50 +++++++ zone/entity.h | 1 + zone/mob.h | 2 +- zone/npc.cpp | 10 +- zone/spell_effects.cpp | 29 +++- 9 files changed, 259 insertions(+), 138 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index e553daf34..edbbcc01e 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -1017,14 +1017,14 @@ typedef enum { #define SE_FcSpellVulnerability 296 // implemented, @Fc, On Target, spell damage taken mod pct, base: min pct, limit: max pct #define SE_FcDamageAmtIncoming 297 // implemetned, @Fc, On Target, damage taken flat amt, base: amt #define SE_ChangeHeight 298 // implemented -#define SE_WakeTheDead 299 // implemented +#define SE_WakeTheDead 299 // implemented, @Pets, summon one temporary pet from nearby corpses that last a set duration, base: none, limit: none, max: duration (seconds). Note: max range of corpse is 250. #define SE_Doppelganger 300 // implemented #define SE_ArcheryDamageModifier 301 // implemented[AA] - increase archery damage by percent #define SE_FcDamagePctCrit 302 // implemented, @Fc, On Caster, spell damage mod pct, base: min pct, limit: max pct, Note: applied after critical hits has been calculated. #define SE_FcDamageAmtCrit 303 // implemented, @Fc, On Caster, spell damage mod flat amt, base: amt #define SE_OffhandRiposteFail 304 // implemented as bonus - enemy cannot riposte offhand attacks #define SE_MitigateDamageShield 305 // implemented - off hand attacks only (Shielding Resistance) -//#define SE_ArmyOfTheDead 306 // *not implemented NecroAA - This ability calls up to five shades of nearby corpses back to life to serve the necromancer. The soulless abominations will mindlessly fight the target until called back to the afterlife some time later. The first rank summons up to three shades that serve for 60 seconds, and each additional rank adds one more possible shade and increases their duration by 15 seconds +#define SE_ArmyOfTheDead 306 // implemented, @Pets, summon multiple temporary pets from nearby corpses that last a set duration, base: amount of corpses that a pet can summon from, limit: none, max: duration (seconds). Note: max range of corpse is 250. //#define SE_Appraisal 307 // *not implemented Rogue AA - This ability allows you to estimate the selling price of an item you are holding on your cursor. #define SE_ZoneSuspendMinion 308 // implemented, @Pet, allow suspended pets to be resummoned upon zoning, base: 1, limit: none, max: none, Calc: Bool #define SE_GateCastersBindpoint 309 // implemented - Gate to casters bind point diff --git a/zone/aa.cpp b/zone/aa.cpp index ed7faafb9..857b5e495 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -213,7 +213,7 @@ void Mob::TypesTemporaryPets(uint32 typesid, Mob *targ, const char *name_overrid glm::vec2(5, 5), glm::vec2(-5, 5), glm::vec2(5, -5), glm::vec2(-5, -5), glm::vec2(10, 10), glm::vec2(-10, 10), glm::vec2(10, -10), glm::vec2(-10, -10), glm::vec2(8, 8), glm::vec2(-8, 8), glm::vec2(8, -8), glm::vec2(-8, -8) - };; + }; while(summon_count > 0) { int pet_duration = pet.duration; @@ -273,63 +273,94 @@ void Mob::TypesTemporaryPets(uint32 typesid, Mob *targ, const char *name_overrid delete made_npc; } -void Mob::WakeTheDead(uint16 spell_id, Mob *target, uint32 duration) -{ - Corpse *CorpseToUse = nullptr; - CorpseToUse = entity_list.GetClosestCorpse(this, nullptr); +void Mob::WakeTheDead(uint16 spell_id, Corpse *corpse_to_use, Mob *target, uint32 duration) { - if(!CorpseToUse) + /* + SPA 299 Wake The Dead, 'animateDead' should be temp pet, always spawns 1 pet from corpse, max value is duration + SPA 306 Wake The Dead, 'animateDead#' should be temp pet, base is amount of pets from indivual corpses, max value is duration + Max range for closet corpse is 250 units. + TODO: Should use temp pets + */ + + if (!corpse_to_use) { return; + } - //assuming we have pets in our table; we take the first pet as a base type. - const NPCType *base_type = content_db.LoadNPCTypesData(500); - auto make_npc = new NPCType; - memcpy(make_npc, base_type, sizeof(NPCType)); + /* TODO: Does WTD use pet focus? + int act_power = 0; - //combat stats - make_npc->AC = ((GetLevel() * 7) + 550); - make_npc->ATK = GetLevel(); - make_npc->max_dmg = (GetLevel() * 4) + 2; - make_npc->min_dmg = 1; + if (IsClient()) { + act_power = CastToClient()->GetFocusEffect(focusPetPower, spell_id); + act_power = CastToClient()->mod_pet_power(act_power, spell_id); + } + */ - //base stats - make_npc->current_hp = (GetLevel() * 55); - make_npc->max_hp = (GetLevel() * 55); - make_npc->STR = 85 + (GetLevel() * 3); - make_npc->STA = 85 + (GetLevel() * 3); - make_npc->DEX = 85 + (GetLevel() * 3); - make_npc->AGI = 85 + (GetLevel() * 3); - make_npc->INT = 85 + (GetLevel() * 3); - make_npc->WIS = 85 + (GetLevel() * 3); - make_npc->CHA = 85 + (GetLevel() * 3); - make_npc->MR = 25; - make_npc->FR = 25; - make_npc->CR = 25; - make_npc->DR = 25; - make_npc->PR = 25; + SwarmPet_Struct pet; + pet.count = 1; + pet.duration = 1; + + //pet.duration += GetFocusEffect(focusSwarmPetDuration, spell_id) / 1000; //TODO: Does WTD use pet focus? + + pet.npc_id = WAKE_THE_DEAD_NPCTYPEID; + + NPCType *made_npc = nullptr; + + const NPCType *npc_type = content_db.LoadNPCTypesData(WAKE_THE_DEAD_NPCTYPEID); + if (npc_type == nullptr) { + //log write + LogError("Unknown npc type for 'Wake the Dead' swarm pet spell id: [{}]", spell_id); + Message(0, "Unable to find pet!"); + return; + } + + made_npc = new NPCType; + memcpy(made_npc, npc_type, sizeof(NPCType)); - //level class and gender - make_npc->level = GetLevel(); - make_npc->class_ = CorpseToUse->class_; - make_npc->race = CorpseToUse->race; - make_npc->gender = CorpseToUse->gender; - make_npc->loottable_id = 0; - //name char NewName[64]; sprintf(NewName, "%s`s Animated Corpse", GetCleanName()); - strcpy(make_npc->name, NewName); + strcpy(made_npc->name, NewName); + npc_type = made_npc; + + //combat stats + made_npc->AC = ((GetLevel() * 7) + 550); + made_npc->ATK = GetLevel(); + made_npc->max_dmg = (GetLevel() * 4) + 2; + made_npc->min_dmg = 1; + + //base stats + made_npc->current_hp = (GetLevel() * 55); + made_npc->max_hp = (GetLevel() * 55); + made_npc->STR = 85 + (GetLevel() * 3); + made_npc->STA = 85 + (GetLevel() * 3); + made_npc->DEX = 85 + (GetLevel() * 3); + made_npc->AGI = 85 + (GetLevel() * 3); + made_npc->INT = 85 + (GetLevel() * 3); + made_npc->WIS = 85 + (GetLevel() * 3); + made_npc->CHA = 85 + (GetLevel() * 3); + made_npc->MR = 25; + made_npc->FR = 25; + made_npc->CR = 25; + made_npc->DR = 25; + made_npc->PR = 25; + + //level class and gender + made_npc->level = GetLevel(); + made_npc->class_ = corpse_to_use->class_; + made_npc->race = corpse_to_use->race; + made_npc->gender = corpse_to_use->gender; + made_npc->loottable_id = 0; //appearance - make_npc->beard = CorpseToUse->beard; - make_npc->beardcolor = CorpseToUse->beardcolor; - make_npc->eyecolor1 = CorpseToUse->eyecolor1; - make_npc->eyecolor2 = CorpseToUse->eyecolor2; - make_npc->haircolor = CorpseToUse->haircolor; - make_npc->hairstyle = CorpseToUse->hairstyle; - make_npc->helmtexture = CorpseToUse->helmtexture; - make_npc->luclinface = CorpseToUse->luclinface; - make_npc->size = CorpseToUse->size; - make_npc->texture = CorpseToUse->texture; + made_npc->beard = corpse_to_use->beard; + made_npc->beardcolor = corpse_to_use->beardcolor; + made_npc->eyecolor1 = corpse_to_use->eyecolor1; + made_npc->eyecolor2 = corpse_to_use->eyecolor2; + made_npc->haircolor = corpse_to_use->haircolor; + made_npc->hairstyle = corpse_to_use->hairstyle; + made_npc->helmtexture = corpse_to_use->helmtexture; + made_npc->luclinface = corpse_to_use->luclinface; + made_npc->size = corpse_to_use->size; + made_npc->texture = corpse_to_use->texture; //cast stuff.. based off of PEQ's if you want to change //it you'll have to mod this code, but most likely @@ -337,130 +368,144 @@ void Mob::WakeTheDead(uint16 spell_id, Mob *target, uint32 duration) //part of their spell list; can't think of any smooth //way to do this //some basic combat mods here too since it's convienent - switch(CorpseToUse->class_) + switch (corpse_to_use->class_) { case CLERIC: - make_npc->npc_spells_id = 1; + made_npc->npc_spells_id = 1; break; case WIZARD: - make_npc->npc_spells_id = 2; + made_npc->npc_spells_id = 2; break; case NECROMANCER: - make_npc->npc_spells_id = 3; + made_npc->npc_spells_id = 3; break; case MAGICIAN: - make_npc->npc_spells_id = 4; + made_npc->npc_spells_id = 4; break; case ENCHANTER: - make_npc->npc_spells_id = 5; + made_npc->npc_spells_id = 5; break; case SHAMAN: - make_npc->npc_spells_id = 6; + made_npc->npc_spells_id = 6; break; case DRUID: - make_npc->npc_spells_id = 7; + made_npc->npc_spells_id = 7; break; case PALADIN: //SPECATK_TRIPLE - strcpy(make_npc->special_abilities, "6,1"); - make_npc->current_hp = make_npc->current_hp * 150 / 100; - make_npc->max_hp = make_npc->max_hp * 150 / 100; - make_npc->npc_spells_id = 8; + strcpy(made_npc->special_abilities, "6,1"); + made_npc->current_hp = made_npc->current_hp * 150 / 100; + made_npc->max_hp = made_npc->max_hp * 150 / 100; + made_npc->npc_spells_id = 8; break; case SHADOWKNIGHT: - strcpy(make_npc->special_abilities, "6,1"); - make_npc->current_hp = make_npc->current_hp * 150 / 100; - make_npc->max_hp = make_npc->max_hp * 150 / 100; - make_npc->npc_spells_id = 9; + strcpy(made_npc->special_abilities, "6,1"); + made_npc->current_hp = made_npc->current_hp * 150 / 100; + made_npc->max_hp = made_npc->max_hp * 150 / 100; + made_npc->npc_spells_id = 9; break; case RANGER: - strcpy(make_npc->special_abilities, "7,1"); - make_npc->current_hp = make_npc->current_hp * 135 / 100; - make_npc->max_hp = make_npc->max_hp * 135 / 100; - make_npc->npc_spells_id = 10; + strcpy(made_npc->special_abilities, "7,1"); + made_npc->current_hp = made_npc->current_hp * 135 / 100; + made_npc->max_hp = made_npc->max_hp * 135 / 100; + made_npc->npc_spells_id = 10; break; case BARD: - strcpy(make_npc->special_abilities, "6,1"); - make_npc->current_hp = make_npc->current_hp * 110 / 100; - make_npc->max_hp = make_npc->max_hp * 110 / 100; - make_npc->npc_spells_id = 11; + strcpy(made_npc->special_abilities, "6,1"); + made_npc->current_hp = made_npc->current_hp * 110 / 100; + made_npc->max_hp = made_npc->max_hp * 110 / 100; + made_npc->npc_spells_id = 11; break; case BEASTLORD: - strcpy(make_npc->special_abilities, "7,1"); - make_npc->current_hp = make_npc->current_hp * 110 / 100; - make_npc->max_hp = make_npc->max_hp * 110 / 100; - make_npc->npc_spells_id = 12; + strcpy(made_npc->special_abilities, "7,1"); + made_npc->current_hp = made_npc->current_hp * 110 / 100; + made_npc->max_hp = made_npc->max_hp * 110 / 100; + made_npc->npc_spells_id = 12; break; case ROGUE: - strcpy(make_npc->special_abilities, "7,1"); - make_npc->max_dmg = make_npc->max_dmg * 150 /100; - make_npc->current_hp = make_npc->current_hp * 110 / 100; - make_npc->max_hp = make_npc->max_hp * 110 / 100; + strcpy(made_npc->special_abilities, "7,1"); + made_npc->max_dmg = made_npc->max_dmg * 150 / 100; + made_npc->current_hp = made_npc->current_hp * 110 / 100; + made_npc->max_hp = made_npc->max_hp * 110 / 100; break; case MONK: - strcpy(make_npc->special_abilities, "7,1"); - make_npc->max_dmg = make_npc->max_dmg * 150 /100; - make_npc->current_hp = make_npc->current_hp * 135 / 100; - make_npc->max_hp = make_npc->max_hp * 135 / 100; + strcpy(made_npc->special_abilities, "7,1"); + made_npc->max_dmg = made_npc->max_dmg * 150 / 100; + made_npc->current_hp = made_npc->current_hp * 135 / 100; + made_npc->max_hp = made_npc->max_hp * 135 / 100; break; case WARRIOR: case BERSERKER: - strcpy(make_npc->special_abilities, "7,1"); - make_npc->max_dmg = make_npc->max_dmg * 150 /100; - make_npc->current_hp = make_npc->current_hp * 175 / 100; - make_npc->max_hp = make_npc->max_hp * 175 / 100; + strcpy(made_npc->special_abilities, "7,1"); + made_npc->max_dmg = made_npc->max_dmg * 150 / 100; + made_npc->current_hp = made_npc->current_hp * 175 / 100; + made_npc->max_hp = made_npc->max_hp * 175 / 100; break; default: - make_npc->npc_spells_id = 0; + made_npc->npc_spells_id = 0; break; } - make_npc->loottable_id = 0; - make_npc->merchanttype = 0; - make_npc->d_melee_texture1 = 0; - make_npc->d_melee_texture2 = 0; + made_npc->loottable_id = 0; + made_npc->merchanttype = 0; + made_npc->d_melee_texture1 = 0; + made_npc->d_melee_texture2 = 0; - auto npca = new NPC(make_npc, 0, GetPosition(), GravityBehavior::Water); - if(!npca->GetSwarmInfo()){ - auto nSI = new SwarmPet; - npca->SetSwarmInfo(nSI); - npca->GetSwarmInfo()->duration = new Timer(duration*1000); - } - else{ - npca->GetSwarmInfo()->duration->Start(duration*1000); - } + int summon_count = 0; + summon_count = pet.count; - npca->StartSwarmTimer(duration * 1000); - npca->GetSwarmInfo()->owner_id = GetID(); + NPC* swarm_pet_npc = nullptr; + //TODO: potenitally add support for multiple pets per corpse + while (summon_count > 0) { + int pet_duration = duration; - //give the pet somebody to "love" - if(target != nullptr){ - npca->AddToHateList(target, 100000); - npca->GetSwarmInfo()->target = target->GetID(); - } - - //gear stuff, need to make sure there's - //no situation where this stuff can be duped - for (int x = EQ::invslot::EQUIPMENT_BEGIN; x <= EQ::invslot::EQUIPMENT_END; x++) - { - uint32 sitem = 0; - sitem = CorpseToUse->GetWornItem(x); - if(sitem){ - const EQ::ItemData * itm = database.GetItem(sitem); - npca->AddLootDrop(itm, &npca->itemlist, NPC::NewLootDropEntry(), true); + NPCType *npc_dup = nullptr; + if (made_npc != nullptr) { + npc_dup = new NPCType; + memcpy(npc_dup, made_npc, sizeof(NPCType)); } + + swarm_pet_npc = new NPC( + (npc_dup != nullptr) ? npc_dup : npc_type, + 0, corpse_to_use->GetPosition(),GravityBehavior::Water); + + swarm_pet_npc->SetFollowID(GetID()); + + if (!swarm_pet_npc->GetSwarmInfo()) { + auto nSI = new SwarmPet; + swarm_pet_npc->SetSwarmInfo(nSI); + swarm_pet_npc->GetSwarmInfo()->duration = new Timer(pet_duration * 1000); + } + else { + swarm_pet_npc->GetSwarmInfo()->duration->Start(pet_duration * 1000); + } + + swarm_pet_npc->StartSwarmTimer(pet_duration * 1000); + + //removing this prevents the pet from attacking + swarm_pet_npc->GetSwarmInfo()->owner_id = GetID(); + + //give the pets somebody to "love" + if (target != nullptr) { + swarm_pet_npc->AddToHateList(target, 10000, 1000); + swarm_pet_npc->GetSwarmInfo()->target = 0; + } + + //we allocated a new NPC type object, give the NPC ownership of that memory + if (npc_dup != nullptr) + swarm_pet_npc->GiveNPCTypeData(npc_dup); + + entity_list.AddNPC(swarm_pet_npc, true, true); + summon_count--; } - //we allocated a new NPC type object, give the NPC ownership of that memory - if(make_npc != nullptr) - npca->GiveNPCTypeData(make_npc); - - entity_list.AddNPC(npca, true, true); - //the target of these swarm pets will take offense to being cast on... - if(target != nullptr) + if (target != nullptr) target->AddToHateList(this, 1, 0); + + // The other pointers we make are handled elsewhere. + delete made_npc; } void Client::ResetAA() { diff --git a/zone/aa.h b/zone/aa.h index 0ce16a787..4d9047e39 100644 --- a/zone/aa.h +++ b/zone/aa.h @@ -2,6 +2,7 @@ #define AA_H #define MAX_SWARM_PETS 12 //this can change as long as you make more coords (swarm_pet_x/swarm_pet_y) +#define WAKE_THE_DEAD_NPCTYPEID 500 //We use first pet in pets table as a template typedef enum { aaActionNone = 0, diff --git a/zone/attack.cpp b/zone/attack.cpp index 4392e983f..4ec8a8044 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2915,8 +2915,9 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b } } - if (other->GetTempPetCount()) + if (other->GetTempPetCount()) { entity_list.AddTempPetsToHateList(other, this, bFrenzy); + } if (!wasengaged) { if (IsNPC() && other->IsClient() && other->CastToClient()) diff --git a/zone/entity.cpp b/zone/entity.cpp index 0179b3c54..a5f3f96ae 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -4329,6 +4329,56 @@ Corpse *EntityList::GetClosestCorpse(Mob *sender, const char *Name) return ClosestCorpse; } +void EntityList::TryWakeTheDead(Mob *sender, Mob *target, int32 spell_id, uint32 max_distance, uint32 duration, uint32 amount_pets) +{ + if (!sender) { + return; + } + + std::vector used_corpse_list; + + for (int i = 0; i < amount_pets; i++) + { + uint32 CurrentDistance, ClosestDistance = 4294967295u; + Corpse *CurrentCorpse, *ClosestCorpse = nullptr; + + auto it = corpse_list.begin(); + while (it != corpse_list.end()) { + CurrentCorpse = it->second; + + ++it; + + bool corpse_already_used = false; + for (auto itr = used_corpse_list.begin(); itr != used_corpse_list.end(); ++itr) { + if ((*itr) && (*itr) == CurrentCorpse->GetID()) { + corpse_already_used = true; + continue; + } + } + + if (corpse_already_used) { + continue; + } + + CurrentDistance = static_cast(sender->CalculateDistance(CurrentCorpse->GetX(), CurrentCorpse->GetY(), CurrentCorpse->GetZ())); + + if (max_distance && CurrentDistance > max_distance) { + continue; + } + + if (CurrentDistance < ClosestDistance) { + ClosestDistance = CurrentDistance; + ClosestCorpse = CurrentCorpse; + } + } + + if (ClosestCorpse) { + sender->WakeTheDead(spell_id, ClosestCorpse, target, duration); + used_corpse_list.push_back(ClosestCorpse->GetID()); + } + } +} + void EntityList::ForceGroupUpdate(uint32 gid) { auto it = client_list.begin(); diff --git a/zone/entity.h b/zone/entity.h index 542f95845..4edc63b0d 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -478,6 +478,7 @@ public: uint32 CheckNPCsClose(Mob *center); Corpse* GetClosestCorpse(Mob* sender, const char *Name); + void TryWakeTheDead(Mob* sender, Mob* target, int32 spell_id, uint32 max_distance, uint32 duration, uint32 amount_pets); NPC* GetClosestBanker(Mob* sender, uint32 &distance); void CameraEffect(uint32 duration, uint32 intensity); Mob* GetClosestMobByBodyType(Mob* sender, bodyType BodyType, bool skip_client_pets=false); diff --git a/zone/mob.h b/zone/mob.h index 16bce03b3..40665d6e6 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -800,7 +800,7 @@ public: inline void Amnesia(bool newval) { amnesiad = newval; } void TemporaryPets(uint16 spell_id, Mob *target, const char *name_override = nullptr, uint32 duration_override = 0, bool followme=true, bool sticktarg=false, uint16 *controlled_pet_id = nullptr); void TypesTemporaryPets(uint32 typesid, Mob *target, const char *name_override = nullptr, uint32 duration_override = 0, bool followme=true, bool sticktarg=false); - void WakeTheDead(uint16 spell_id, Mob *target, uint32 duration); + void WakeTheDead(uint16 spell_id, Corpse *corpse_to_use, Mob *target, uint32 duration); void Spin(); void Kill(); bool PassCharismaCheck(Mob* caster, uint16 spell_id); diff --git a/zone/npc.cpp b/zone/npc.cpp index 7e2304095..110c652e4 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -3082,25 +3082,23 @@ void NPC::ClearLastName() void NPC::DepopSwarmPets() { - if (GetSwarmInfo()) { if (GetSwarmInfo()->duration->Check(false)){ Mob* owner = entity_list.GetMobID(GetSwarmInfo()->owner_id); - if (owner) + if (owner) { owner->SetTempPetCount(owner->GetTempPetCount() - 1); - + } Depop(); return; } - //This is only used for optional quest or rule derived behavior now if you force a temp pet on a specific target. if (GetSwarmInfo()->target) { Mob *targMob = entity_list.GetMob(GetSwarmInfo()->target); if(!targMob || (targMob && targMob->IsCorpse())){ Mob* owner = entity_list.GetMobID(GetSwarmInfo()->owner_id); - if (owner) + if (owner) { owner->SetTempPetCount(owner->GetTempPetCount() - 1); - + } Depop(); return; } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index cc4e90ccd..369fde125 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2295,10 +2295,35 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove //meh dupe issue with npc casting this if(caster && caster->IsClient()){ int dur = spells[spell_id].max_value[i]; - if (!dur) + if (!dur) { dur = 60; + } - caster->WakeTheDead(spell_id, caster->GetTarget(), dur); + Mob* m_target = caster->GetTarget(); + if (m_target) { + entity_list.TryWakeTheDead(caster, m_target, spell_id, 250, dur, 1); + } + } + break; + } + + case SE_ArmyOfTheDead: + { + if (caster && caster->IsClient()) { + int dur = spells[spell_id].max_value[i]; + if (!dur) { + dur = 60; + } + + int amount = spells[spell_id].base_value[i]; + if (!amount) { + amount = 1; + } + + Mob* m_target = caster->GetTarget(); + if (m_target) { + entity_list.TryWakeTheDead(caster, m_target, spell_id, 250, dur, amount); + } } break; } From 3a941327491d2789a0fc45717c1b08e040abcc72 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 16 Jan 2022 18:04:51 -0500 Subject: [PATCH 564/624] [Quest API] Add multiple augment related methods to Perl/Lua. (#1930) * [Quest API] Add multiple augment related methods to Perl/Lua. - Add $inventory->CountAugmentEquippedByID(item_id) to Perl. - Add $inventory->HasAugmentEquippedByID(item_id) to Perl. - Add $item->ContainsAugmentByID(item_id) to Perl. - Add $item->CountAugmentByID(item_id) to Perl. - Add inventory:CountAugmentEquippedByID(item_id) to Perl. - Add inventory:HasAugmentEquippedByID(item_id) to Perl. - Add item:ContainsAugmentByID(item_id) to Perl. - Add item:CountAugmentByID(item_id) to Perl. * Update inventory_profile.cpp --- common/inventory_profile.cpp | 31 ++++++++++++++++++++++++++++++ common/inventory_profile.h | 6 ++++++ common/item_instance.cpp | 20 +++++++++++++++++++ common/item_instance.h | 1 + zone/lua_inventory.cpp | 12 ++++++++++++ zone/lua_inventory.h | 2 ++ zone/lua_iteminst.cpp | 12 ++++++++++++ zone/lua_iteminst.h | 2 ++ zone/perl_inventory.cpp | 37 ++++++++++++++++++++++++++++++++++++ zone/perl_questitem.cpp | 37 ++++++++++++++++++++++++++++++++++++ 10 files changed, 160 insertions(+) diff --git a/common/inventory_profile.cpp b/common/inventory_profile.cpp index 743b0a31c..3f4c3b3dc 100644 --- a/common/inventory_profile.cpp +++ b/common/inventory_profile.cpp @@ -590,6 +590,37 @@ bool EQ::InventoryProfile::HasSpaceForItem(const ItemData *ItemToTry, int16 Quan // Checks that user has at least 'quantity' number of items in a given inventory slot // Returns first slot it was found in, or SLOT_INVALID if not found +bool EQ::InventoryProfile::HasAugmentEquippedByID(uint32 item_id) +{ + bool has_equipped = false; + ItemInstance* item = nullptr; + + for (int slot_id = EQ::invslot::EQUIPMENT_BEGIN; slot_id <= EQ::invslot::EQUIPMENT_END; ++slot_id) { + item = GetItem(slot_id); + if (item && item->ContainsAugmentByID(item_id)) { + has_equipped = true; + break; + } + } + + return has_equipped; +} + +int EQ::InventoryProfile::CountAugmentEquippedByID(uint32 item_id) +{ + int quantity = 0; + ItemInstance* item = nullptr; + + for (int slot_id = EQ::invslot::EQUIPMENT_BEGIN; slot_id <= EQ::invslot::EQUIPMENT_END; ++slot_id) { + item = GetItem(slot_id); + if (item && item->ContainsAugmentByID(item_id)) { + quantity += item->CountAugmentByID(item_id); + } + } + + return quantity; +} + //This function has a flaw in that it only returns the last stack that it looked at //when quantity is greater than 1 and not all of quantity can be found in 1 stack. int16 EQ::InventoryProfile::HasItem(uint32 item_id, uint8 quantity, uint8 where) diff --git a/common/inventory_profile.h b/common/inventory_profile.h index 58a65e443..b32b028fb 100644 --- a/common/inventory_profile.h +++ b/common/inventory_profile.h @@ -140,6 +140,12 @@ namespace EQ // Remove item from inventory (and take control of memory) ItemInstance* PopItem(int16 slot_id); + // Check if player has a specific augment equipped by Item ID + bool HasAugmentEquippedByID(uint32 item_id); + + // Check how many of a specific augment the player has equipped by Item ID + int CountAugmentEquippedByID(uint32 item_id); + // Check whether there is space for the specified number of the specified item. bool HasSpaceForItem(const ItemData *ItemToTry, int16 Quantity); diff --git a/common/item_instance.cpp b/common/item_instance.cpp index 5ff9e9630..cd6d17712 100644 --- a/common/item_instance.cpp +++ b/common/item_instance.cpp @@ -708,6 +708,26 @@ bool EQ::ItemInstance::ContainsAugmentByID(uint32 item_id) return false; } +int EQ::ItemInstance::CountAugmentByID(uint32 item_id) +{ + int quantity = 0; + if (!m_item || !m_item->IsClassCommon()) { + return quantity; + } + + if (!item_id) { + return quantity; + } + + for (uint8 augment_slot = invaug::SOCKET_BEGIN; augment_slot <= invaug::SOCKET_END; ++augment_slot) { + if (GetAugmentItemID(augment_slot) == item_id) { + quantity++; + } + } + + return quantity; +} + // Has attack/delay? bool EQ::ItemInstance::IsWeapon() const { diff --git a/common/item_instance.h b/common/item_instance.h index 008e4aefd..c6b97dd78 100644 --- a/common/item_instance.h +++ b/common/item_instance.h @@ -133,6 +133,7 @@ namespace EQ ItemInstance* RemoveAugment(uint8 index); bool IsAugmented(); bool ContainsAugmentByID(uint32 item_id); + int CountAugmentByID(uint32 item_id); ItemInstance* GetOrnamentationAug(int32 ornamentationAugtype) const; bool UpdateOrnamentationInfo(); static bool CanTransform(const ItemData *ItemToTry, const ItemData *Container, bool AllowAll = false); diff --git a/zone/lua_inventory.cpp b/zone/lua_inventory.cpp index 3f9af6947..f19b9af87 100644 --- a/zone/lua_inventory.cpp +++ b/zone/lua_inventory.cpp @@ -164,6 +164,16 @@ int Lua_Inventory::GetSlotByItemInst(Lua_ItemInst inst) { return self->GetSlotByItemInst(inst); } +int Lua_Inventory::CountAugmentEquippedByID(uint32 item_id) { + Lua_Safe_Call_Int(); + return self->CountAugmentEquippedByID(item_id); +} + +bool Lua_Inventory::HasAugmentEquippedByID(uint32 item_id) { + Lua_Safe_Call_Bool(); + return self->HasAugmentEquippedByID(item_id); +} + luabind::scope lua_register_inventory() { return luabind::class_("Inventory") .def(luabind::constructor<>()) @@ -174,6 +184,7 @@ luabind::scope lua_register_inventory() { .def("CalcSlotId", (int(Lua_Inventory::*)(int,int))&Lua_Inventory::CalcSlotId) .def("CanItemFitInContainer", (bool(Lua_Inventory::*)(Lua_Item,Lua_Item))&Lua_Inventory::CanItemFitInContainer) .def("CheckNoDrop", (bool(Lua_Inventory::*)(int))&Lua_Inventory::CheckNoDrop) + .def("CountAugmentEquippedByID", (int(Lua_Inventory::*)(uint32))&Lua_Inventory::CountAugmentEquippedByID) .def("DeleteItem", (bool(Lua_Inventory::*)(int))&Lua_Inventory::DeleteItem) .def("DeleteItem", (bool(Lua_Inventory::*)(int,int))&Lua_Inventory::DeleteItem) .def("FindFreeSlot", (int(Lua_Inventory::*)(bool,bool))&Lua_Inventory::FindFreeSlot) @@ -182,6 +193,7 @@ luabind::scope lua_register_inventory() { .def("GetItem", (Lua_ItemInst(Lua_Inventory::*)(int))&Lua_Inventory::GetItem) .def("GetItem", (Lua_ItemInst(Lua_Inventory::*)(int,int))&Lua_Inventory::GetItem) .def("GetSlotByItemInst", (int(Lua_Inventory::*)(Lua_ItemInst))&Lua_Inventory::GetSlotByItemInst) + .def("HasAugmentEquippedByID", (bool(Lua_Inventory::*)(uint32))&Lua_Inventory::HasAugmentEquippedByID) .def("HasItem", (int(Lua_Inventory::*)(int))&Lua_Inventory::HasItem) .def("HasItem", (int(Lua_Inventory::*)(int,int))&Lua_Inventory::HasItem) .def("HasItem", (int(Lua_Inventory::*)(int,int,int))&Lua_Inventory::HasItem) diff --git a/zone/lua_inventory.h b/zone/lua_inventory.h index b64e9c48c..33ba80b39 100644 --- a/zone/lua_inventory.h +++ b/zone/lua_inventory.h @@ -43,7 +43,9 @@ public: bool DeleteItem(int slot_id); bool DeleteItem(int slot_id, int quantity); bool CheckNoDrop(int slot_id); + int CountAugmentEquippedByID(uint32 item_id); Lua_ItemInst PopItem(int slot_id); + bool HasAugmentEquippedByID(uint32 item_id); int HasItem(int item_id); int HasItem(int item_id, int quantity); int HasItem(int item_id, int quantity, int where); diff --git a/zone/lua_iteminst.cpp b/zone/lua_iteminst.cpp index 1c5155e5d..149c087b6 100644 --- a/zone/lua_iteminst.cpp +++ b/zone/lua_iteminst.cpp @@ -264,6 +264,16 @@ void Lua_ItemInst::ClearTimers() { self->ClearTimers(); } +bool Lua_ItemInst::ContainsAugmentByID(uint32 item_id) { + Lua_Safe_Call_Bool(); + return self->ContainsAugmentByID(item_id); +} + +int Lua_ItemInst::CountAugmentByID(uint32 item_id) { + Lua_Safe_Call_Int(); + return self->CountAugmentByID(item_id); +} + luabind::scope lua_register_iteminst() { return luabind::class_("ItemInst") .def(luabind::constructor<>()) @@ -274,6 +284,8 @@ luabind::scope lua_register_iteminst() { .def("AddExp", (void(Lua_ItemInst::*)(uint32))&Lua_ItemInst::AddExp) .def("ClearTimers", (void(Lua_ItemInst::*)(void))&Lua_ItemInst::ClearTimers) .def("Clone", (Lua_ItemInst(Lua_ItemInst::*)(void))&Lua_ItemInst::Clone) + .def("ContainsAugmentByID", (bool(Lua_ItemInst::*)(uint32))&Lua_ItemInst::ContainsAugmentByID) + .def("CountAugmentByID", (int(Lua_ItemInst::*)(uint32))&Lua_ItemInst::CountAugmentByID) .def("DeleteCustomData", (void(Lua_ItemInst::*)(std::string))&Lua_ItemInst::DeleteCustomData) .def("GetAugment", (Lua_ItemInst(Lua_ItemInst::*)(int))&Lua_ItemInst::GetAugment) .def("GetAugmentItemID", (uint32(Lua_ItemInst::*)(int))&Lua_ItemInst::GetAugmentItemID) diff --git a/zone/lua_iteminst.h b/zone/lua_iteminst.h index 7614bea5d..c0a4d50c4 100644 --- a/zone/lua_iteminst.h +++ b/zone/lua_iteminst.h @@ -80,6 +80,8 @@ public: void SetTimer(std::string name, uint32 time); void StopTimer(std::string name); void ClearTimers(); + bool ContainsAugmentByID(uint32 item_id); + int CountAugmentByID(uint32 item_id); private: bool cloned_; diff --git a/zone/perl_inventory.cpp b/zone/perl_inventory.cpp index c2ea6679c..140042a47 100644 --- a/zone/perl_inventory.cpp +++ b/zone/perl_inventory.cpp @@ -414,6 +414,41 @@ XS(XS_Inventory_PutItem) { XSRETURN(1); } +XS(XS_Inventory_HasAugmentEquippedByID); +XS(XS_Inventory_HasAugmentEquippedByID) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Inventory::HasAugmentEquippedByID(THIS, uint32 item_id)"); + { + EQ::InventoryProfile* THIS; + bool has_equipped = false; + uint32 item_id = (uint32) SvUV(ST(1)); + VALIDATE_THIS_IS_INVENTORY; + has_equipped = THIS->HasAugmentEquippedByID(item_id); + ST(0) = boolSV(has_equipped); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Inventory_CountAugmentEquippedByID); +XS(XS_Inventory_CountAugmentEquippedByID) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Inventory::CountAugmentEquippedByID(THIS, uint32 item_id)"); + { + EQ::InventoryProfile* THIS; + int quantity = 0; + uint32 item_id = (uint32) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_INVENTORY; + quantity = THIS->CountAugmentEquippedByID(item_id); + XSprePUSH; + PUSHi((IV)quantity); + } + XSRETURN(1); +} + #ifdef __cplusplus extern "C" #endif @@ -430,6 +465,7 @@ XS(boot_Inventory) { char buf[128]; XS_VERSION_BOOTCHECK; newXSproto(strcpy(buf, "CanItemFitInContainer"), XS_Inventory_CanItemFitInContainer, file, "$$$"); + newXSproto(strcpy(buf, "CountAugmentEquippedByID"), XS_Inventory_CountAugmentEquippedByID, file, "$$"); newXSproto(strcpy(buf, "CheckNoDrop"), XS_Inventory_CheckNoDrop, file, "$$"); newXSproto(strcpy(buf, "DeleteItem"), XS_Inventory_DeleteItem, file, "$$;$"); newXSproto(strcpy(buf, "FindFreeSlot"), XS_Inventory_FindFreeSlot, file, "$$$;$$"); @@ -439,6 +475,7 @@ XS(boot_Inventory) { newXSproto(strcpy(buf, "GetSlotByItemInst"), XS_Inventory_GetSlotByItemInst, file, "$$"); newXSproto(strcpy(buf, "GetSlotFromMaterial"), XS_Inventory_GetSlotFromMaterial, file, "$$"); newXSproto(strcpy(buf, "GetSlotID"), XS_Inventory_GetSlotID, file, "$$;$"); + newXSproto(strcpy(buf, "HasAugmentEquippedByID"), XS_Inventory_HasAugmentEquippedByID, file, "$$"); newXSproto(strcpy(buf, "HasItem"), XS_Inventory_HasItem, file, "$$;$$"); newXSproto(strcpy(buf, "HasItemByLoreGroup"), XS_Inventory_HasItemByLoreGroup, file, "$$;$"); newXSproto(strcpy(buf, "HasItemByUse"), XS_Inventory_HasItemByUse, file, "$$;$$"); diff --git a/zone/perl_questitem.cpp b/zone/perl_questitem.cpp index 30a5a7ca1..709201a71 100644 --- a/zone/perl_questitem.cpp +++ b/zone/perl_questitem.cpp @@ -168,6 +168,41 @@ XS(XS_QuestItem_GetID) { XSRETURN(1); } +XS(XS_QuestItem_ContainsAugmentByID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_QuestItem_ContainsAugmentByID) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: QuestItem::ContainsAugmentByID(THIS, uint32 item_id)"); // @categories Inventory and Items + { + EQ::ItemInstance *THIS; + uint32 item_id = (uint32) SvUV(ST(1)); + bool contains_augment = false; + VALIDATE_THIS_IS_ITEM; + contains_augment = THIS->ContainsAugmentByID(item_id); + ST(0) = boolSV(contains_augment); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_QuestItem_CountAugmentByID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_QuestItem_CountAugmentByID) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: QuestItem::CountAugmentByID(THIS, uint32 item_id)"); // @categories Inventory and Items + { + EQ::ItemInstance *THIS; + int quantity = 0; + uint32 item_id = (uint32) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_ITEM; + quantity = THIS->CountAugmentByID(item_id); + XSprePUSH; + PUSHi((IV) quantity); + } + XSRETURN(1); +} + #ifdef __cplusplus extern "C" #endif @@ -186,6 +221,8 @@ XS(boot_QuestItem) { //add the strcpy stuff to get rid of const warnings.... XS_VERSION_BOOTCHECK; + newXSproto(strcpy(buf, "ContainsAugmentByID"), XS_QuestItem_ContainsAugmentByID, file, "$$"); + newXSproto(strcpy(buf, "CountAugmentByID"), XS_QuestItem_CountAugmentByID, file, "$$"); newXSproto(strcpy(buf, "GetAugment"), XS_QuestItem_GetAugment, file, "$$"); newXSproto(strcpy(buf, "GetCharges"), XS_QuestItem_GetCharges, file, "$"); newXSproto(strcpy(buf, "GetID"), XS_QuestItem_GetID, file, "$"); From 28b1abe1a7eed50d0cefe095f3773d0d4dc57b9d Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Mon, 17 Jan 2022 09:10:37 -0500 Subject: [PATCH 565/624] [Bug Fix] Fixes Enchanter AA Doppleganger crash issue (#1931) * Fix crash bug * [Bug Fix] Fixes Enchanter AA Doppleganger crash issue --- zone/client.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zone/client.cpp b/zone/client.cpp index abfa0e8e0..60f7212a8 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -6482,11 +6482,12 @@ void Client::Doppelganger(uint16 spell_id, Mob *target, const char *name_overrid swarm_pet_npc->StartSwarmTimer(pet_duration * 1000); swarm_pet_npc->GetSwarmInfo()->owner_id = GetID(); + swarm_pet_npc->SetFollowID(GetID()); // Give the pets alittle more agro than the caster and then agro them on the target target->AddToHateList(swarm_pet_npc, (target->GetHateAmount(this) + 100), (target->GetDamageAmount(this) + 100)); swarm_pet_npc->AddToHateList(target, 1000, 1000); - swarm_pet_npc->GetSwarmInfo()->target = target->GetID(); + swarm_pet_npc->GetSwarmInfo()->target = 0; //we allocated a new NPC type object, give the NPC ownership of that memory if(npc_dup != nullptr) From 176bfc8524ff6da9ba9824428140c657a308fd35 Mon Sep 17 00:00:00 2001 From: Natedog2012 Date: Tue, 18 Jan 2022 16:43:58 -0600 Subject: [PATCH 566/624] [Bug Fix] Loading pets from database will make unique name to not overlap existing pets with same name in zone (#1933) --- zone/pets.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zone/pets.cpp b/zone/pets.cpp index d14989f73..3b83df1e8 100644 --- a/zone/pets.cpp +++ b/zone/pets.cpp @@ -273,6 +273,8 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, if (petname != nullptr) { // Name was provided, use it. strn0cpy(npc_type->name, petname, 64); + EntityList::RemoveNumbers(npc_type->name); + entity_list.MakeNameUnique(npc_type->name); } else if (record.petnaming == 0) { strcpy(npc_type->name, this->GetCleanName()); npc_type->name[25] = '\0'; From 71c53cb18b231ae0381d169bedec8301168d7b84 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 18 Jan 2022 21:48:36 -0500 Subject: [PATCH 567/624] [Spells] Updates and fixes to Target Locked Pets (#1932) * start of rework * reworked v2 no timer * fix * more mechanics * Update pets.cpp * move to pet.cpp * [Spells] Updates and fixes to Target Locked Pets * [Spells] Updates and fixes to Target Locked Pets --- common/spdat.h | 1 + zone/aa.cpp | 16 ++++++++++---- zone/npc.cpp | 34 +++++------------------------ zone/npc.h | 1 + zone/pets.cpp | 58 +++++++++++++++++++++++++++++++++++++++++++------ zone/spells.cpp | 3 --- 6 files changed, 71 insertions(+), 42 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index edbbcc01e..d2b7dff04 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -168,6 +168,7 @@ #define SPELL_ILLUSION_TREE 601 #define SPELL_ILLUSION_FEMALE 1731 #define SPELL_ILLUSION_MALE 1732 +#define SPELL_UNSUMMON_SELF 892 //spellgroup ids #define SPELLGROUP_FRENZIED_BURNOUT 2754 diff --git a/zone/aa.cpp b/zone/aa.cpp index 857b5e495..a72bf2d7e 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -151,10 +151,14 @@ void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, u //give the pets somebody to "love" if (targ != nullptr) { swarm_pet_npc->AddToHateList(targ, 1000, 1000); - if (RuleB(Spells, SwarmPetTargetLock) || sticktarg) + if (RuleB(Spells, SwarmPetTargetLock) || sticktarg) { swarm_pet_npc->GetSwarmInfo()->target = targ->GetID(); - else + swarm_pet_npc->SetPetTargetLockID(targ->GetID()); + swarm_pet_npc->SetSpecialAbility(IMMUNE_AGGRO, 1); + } + else { swarm_pet_npc->GetSwarmInfo()->target = 0; + } } //we allocated a new NPC type object, give the NPC ownership of that memory @@ -255,10 +259,14 @@ void Mob::TypesTemporaryPets(uint32 typesid, Mob *targ, const char *name_overrid if(targ != nullptr){ swarm_pet_npc->AddToHateList(targ, 1000, 1000); - if (RuleB(Spells, SwarmPetTargetLock) || sticktarg) + if (RuleB(Spells, SwarmPetTargetLock) || sticktarg) { swarm_pet_npc->GetSwarmInfo()->target = targ->GetID(); - else + swarm_pet_npc->SetPetTargetLockID(targ->GetID()); + swarm_pet_npc->SetSpecialAbility(IMMUNE_AGGRO, 1); + } + else { swarm_pet_npc->GetSwarmInfo()->target = 0; + } } //we allocated a new NPC type object, give the NPC ownership of that memory diff --git a/zone/npc.cpp b/zone/npc.cpp index 110c652e4..a10713d06 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -509,12 +509,8 @@ void NPC::SetTarget(Mob* mob) { if(mob == GetTarget()) //dont bother if they are allready our target return; - //This is not the default behavior for swarm pets, must be specified from quest functions or rules value. - if(GetSwarmInfo() && GetSwarmInfo()->target && GetTarget() && (GetTarget()->GetHP() > 0)) { - Mob *targ = entity_list.GetMob(GetSwarmInfo()->target); - if(targ != mob){ - return; - } + if (GetPetTargetLockID()) { + TryDepopTargetLockedPets(mob); } if (mob) { @@ -847,6 +843,10 @@ bool NPC::Process() SpellProcess(); + if (swarm_timer.Check()) { + DepopSwarmPets(); + } + if (mob_close_scan_timer.Check()) { entity_list.ScanCloseMobs(close_mobs, this, IsMoving()); } @@ -3091,28 +3091,6 @@ void NPC::DepopSwarmPets() Depop(); return; } - - if (GetSwarmInfo()->target) { - Mob *targMob = entity_list.GetMob(GetSwarmInfo()->target); - if(!targMob || (targMob && targMob->IsCorpse())){ - Mob* owner = entity_list.GetMobID(GetSwarmInfo()->owner_id); - if (owner) { - owner->SetTempPetCount(owner->GetTempPetCount() - 1); - } - Depop(); - return; - } - } - } - - if (IsPet() && GetPetType() == petTargetLock && GetPetTargetLockID()){ - - Mob *targMob = entity_list.GetMob(GetPetTargetLockID()); - - if(!targMob || (targMob && targMob->IsCorpse())){ - Kill(); - return; - } } } diff --git a/zone/npc.h b/zone/npc.h index 084a3df8a..d3578f1d2 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -260,6 +260,7 @@ public: uint32 GetSwarmTarget(); void SetSwarmTarget(int target_id = 0); void DepopSwarmPets(); + void TryDepopTargetLockedPets(Mob* current_target); void PetOnSpawn(NewSpawn_Struct* ns); void SignalNPC(int _signal_id); diff --git a/zone/pets.cpp b/zone/pets.cpp index 3b83df1e8..1b3a7e3ec 100644 --- a/zone/pets.cpp +++ b/zone/pets.cpp @@ -387,20 +387,64 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, SetPetID(npc->GetID()); // We need to handle PetType 5 (petHatelist), add the current target to the hatelist of the pet - if (record.petcontrol == petTargetLock) { - Mob* target = GetTarget(); + Mob* m_target = GetTarget(); - if (target){ - npc->AddToHateList(target, 1); - npc->SetPetTargetLockID(target->GetID()); + bool activiate_pet = false; + if (m_target && m_target->GetID() != GetID()) { + + if (spells[spell_id].target_type == ST_Self) { + float distance = CalculateDistance(m_target->GetX(), m_target->GetY(), m_target->GetZ()); + if (distance <= 200) { //Live distance on targetlock pets that self cast. No message is given if not in range. + activiate_pet = true; + } + } + else { + activiate_pet = true; + } + } + + if (activiate_pet){ + npc->AddToHateList(m_target, 1); + npc->SetPetTargetLockID(m_target->GetID()); npc->SetSpecialAbility(IMMUNE_AGGRO, 1); } - else - npc->Kill(); //On live casts spell 892 Unsummon (Kayen - Too limiting to use that for emu since pet can have more than 20k HP) + else { + npc->CastSpell(SPELL_UNSUMMON_SELF, npc->GetID()); //Live like behavior, damages self for 20K + if (!npc->HasDied()) { + npc->Kill(); //Ensure pet dies if over 20k HP. + } + } } } + +void NPC::TryDepopTargetLockedPets(Mob* current_target) { + + if (!current_target || (current_target && (current_target->GetID() != GetPetTargetLockID()) || current_target->IsCorpse())) { + + //Use when swarmpets are set to auto lock from quest or rule + if (GetSwarmInfo() && GetSwarmInfo()->target) { + Mob* owner = entity_list.GetMobID(GetSwarmInfo()->owner_id); + if (owner) { + owner->SetTempPetCount(owner->GetTempPetCount() - 1); + } + Depop(); + return; + } + //Use when pets are given petype 5 + if (IsPet() && GetPetType() == petTargetLock && GetPetTargetLockID()) { + CastSpell(SPELL_UNSUMMON_SELF, GetID()); //Live like behavior, damages self for 20K + if (!HasDied()) { + Kill(); //Ensure pet dies if over 20k HP. + } + return; + } + } +} + + + /* This is why the pets ghost - pets were being spawned too far away from its npc owner and some into walls or objects (+10), this sometimes creates the "ghost" effect. I changed to +2 (as close as I could get while it still looked good). I also noticed this can happen if an NPC is spawned on the same spot of another or in a related bad spot.*/ diff --git a/zone/spells.cpp b/zone/spells.cpp index b6d10a3a9..44f1cd8e7 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -136,9 +136,6 @@ void Mob::SpellProcess() void NPC::SpellProcess() { Mob::SpellProcess(); - if (swarm_timer.Check()) { - DepopSwarmPets(); - } } /////////////////////////////////////////////////////////////////////////////// From 804f0681a9cb506f0c6c5988811b2ee4a1351d6c Mon Sep 17 00:00:00 2001 From: Randy Girard Date: Wed, 19 Jan 2022 13:58:15 -0500 Subject: [PATCH 568/624] [Content Flags] Load the content flags before loading shared data. (#1935) --- shared_memory/main.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shared_memory/main.cpp b/shared_memory/main.cpp index 57416aac5..d6e57d6f8 100644 --- a/shared_memory/main.cpp +++ b/shared_memory/main.cpp @@ -167,6 +167,9 @@ int main(int argc, char **argv) content_service.SetCurrentExpansion(RuleI(Expansion, CurrentExpansion)); + content_service.SetDatabase(&database) + ->SetExpansionContext() + ->ReloadContentFlags(); LogInfo( "Current expansion is [{}] ({})", From e09a8f8f8f225cac9e1201168d306ac92a69576a Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 19 Jan 2022 22:44:17 -0500 Subject: [PATCH 569/624] [Spells] Support for bards using Disciplines while casting or /melody. (#1936) * test * complete * Update effects.cpp * Update spells.cpp * Update effects.cpp * [Spells] Support for bards using Disciplines while casting or /melody. Support for spell field 'cast not standing' not allow casting from divine aura * [Spells] Support for bards using Disciplines while casting or /melody. DA bypass logic for spells with field 'cast_not_standing' --- zone/effects.cpp | 45 ++++++++++++++++++++++++++++++++------------ zone/mob.h | 4 ++-- zone/spells.cpp | 49 ++++++++++++++++++++++++++++++++++++------------ 3 files changed, 72 insertions(+), 26 deletions(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index 73d2bf434..3fedf9a24 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -727,6 +727,9 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) { // Dont let client waste a reuse timer if they can't use the disc if (IsStunned() || IsFeared() || IsMezzed() || IsAmnesiad() || IsPet()) { + if (IsAmnesiad()) { + MessageString(Chat::Red, MELEE_SILENCE); + } return(false); } @@ -745,6 +748,10 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) { return(false); } + if (DivineAura() && !spells[spell_id].cast_not_standing) { + return false; + } + //can we use the spell? const SPDat_Spell_Struct &spell = spells[spell_id]; uint8 level_to_use = spell.classes[GetClass() - 1]; @@ -789,8 +796,9 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) { return false; } - if(spell.recast_time > 0) - { + bool instant_recast = true; + + if(spell.recast_time > 0) { uint32 reduced_recast = spell.recast_time / 1000; auto focus = GetFocusEffect(focusReduceRecastTime, spell_id); // do stupid stuff because custom servers. @@ -805,18 +813,31 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) { reduced_recast -= focus; } - if (reduced_recast > 0) - CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline, -1, -1, 0, -1, (uint32)DiscTimer, reduced_recast); - else{ - CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline); - return true; + if (reduced_recast > 0){ + instant_recast = false; + + if (GetClass() == BARD && IsCasting() && spells[spell_id].cast_time == 0) { + if (DoCastingChecks(spell_id, target)) { + SpellFinished(spell_id, entity_list.GetMob(target), EQ::spells::CastingSlot::Discipline, 0, -1, spells[spell_id].resist_difficulty, false, -1, (uint32)DiscTimer, reduced_recast); + } + } + else { + CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline, -1, -1, 0, -1, (uint32)DiscTimer, reduced_recast); + } + + SendDisciplineTimer(spells[spell_id].timer_id, reduced_recast); } - - SendDisciplineTimer(spells[spell_id].timer_id, reduced_recast); } - else - { - CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline); + + if (instant_recast) { + if (GetClass() == BARD && IsCasting() && spells[spell_id].cast_time == 0) { + if (DoCastingChecks(spell_id, target)) { + SpellFinished(spell_id, entity_list.GetMob(target), EQ::spells::CastingSlot::Discipline); + } + } + else { + CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline); + } } return(true); } diff --git a/zone/mob.h b/zone/mob.h index 40665d6e6..e69802cc1 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -331,7 +331,7 @@ public: void CastedSpellFinished(uint16 spell_id, uint32 target_id, EQ::spells::CastingSlot slot, uint16 mana_used, uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0); bool SpellFinished(uint16 spell_id, Mob *target, EQ::spells::CastingSlot slot = EQ::spells::CastingSlot::Item, uint16 mana_used = 0, - uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false, int level_override = -1); + uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false, int level_override = -1, uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0); void SendBeginCast(uint16 spell_id, uint32 casttime); virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar, int reflect_effectiveness = 0, bool use_resist_adjust = false, int16 resist_adjust = 0, bool isproc = false, int level_override = -1, int32 duration_override = 0); @@ -347,7 +347,7 @@ public: void StopCasting(); inline bool IsCasting() const { return((casting_spell_id != 0)); } uint16 CastingSpellID() const { return casting_spell_id; } - bool DoCastingChecks(); + bool DoCastingChecks(int32 spell_id = SPELL_UNKNOWN, uint16 target_id = 0); bool TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier); bool TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed = 1.5f); void ResourceTap(int32 damage, uint16 spell_id); diff --git a/zone/spells.cpp b/zone/spells.cpp index 44f1cd8e7..fbefdad0e 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -159,9 +159,9 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, LogSpells("CastSpell called for spell [{}] ([{}]) on entity [{}], slot [{}], time [{}], mana [{}], from item slot [{}]", (IsValidSpell(spell_id))?spells[spell_id].name:"UNKNOWN SPELL", spell_id, target_id, static_cast(slot), cast_time, mana_cost, (item_slot==0xFFFFFFFF)?999:item_slot); - if(casting_spell_id == spell_id) + if (casting_spell_id == spell_id) { ZeroCastingVars(); - + } if ( !IsValidSpell(spell_id) || @@ -216,8 +216,8 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, return(false); } - //cannot cast under divine aura - if(DivineAura()) { + //cannot cast under divine aura, unless spell has 'cast_not_standing' flag. + if(DivineAura() && !spells[spell_id].cast_not_standing) { LogSpells("Spell casting canceled: cannot cast while Divine Aura is in effect"); InterruptSpell(173, 0x121, false); if(IsClient()) { @@ -240,7 +240,6 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, if ((item_slot != -1 && cast_time == 0) || aa_id) { can_send_spellbar_enable = false; } - if (can_send_spellbar_enable) { SendSpellBarEnable(spell_id); } @@ -620,15 +619,30 @@ void Mob::SendBeginCast(uint16 spell_id, uint32 casttime) * it's probably doing something wrong. */ -bool Mob::DoCastingChecks() +bool Mob::DoCastingChecks(int32 spell_id, uint16 target_id) { if (!IsClient() || (IsClient() && CastToClient()->GetGM())) { casting_spell_checks = true; return true; } - uint16 spell_id = casting_spell_id; - Mob *spell_target = entity_list.GetMob(casting_spell_targetid); + bool ignore_casting_spell_checks = false; + /* + If variables are passed into this function it is NOT being called from main spell process + thefore we do not want to set the 'casting_spell_checks' state keeping variable. + */ + if (spell_id != SPELL_UNKNOWN || target_id) { + ignore_casting_spell_checks = true; + } + + if (spell_id == SPELL_UNKNOWN) { + spell_id = casting_spell_id; + } + if (!target_id) { + target_id = casting_spell_targetid; + } + + Mob *spell_target = entity_list.GetMob(target_id); if (RuleB(Spells, BuffLevelRestrictions)) { // casting_spell_targetid is guaranteed to be what we went, check for ST_Self for now should work though @@ -665,7 +679,9 @@ bool Mob::DoCastingChecks() if (!CastToClient()->IsLinkedSpellReuseTimerReady(spells[spell_id].timer_id)) return false; - casting_spell_checks = true; + if (!ignore_casting_spell_checks){ + casting_spell_checks = true; + } return true; } @@ -2143,7 +2159,8 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce // we can't interrupt in this, or anything called from this! // if you need to abort the casting, return false bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, uint16 mana_used, - uint32 inventory_slot, int16 resist_adjust, bool isproc, int level_override) + uint32 inventory_slot, int16 resist_adjust, bool isproc, int level_override, + uint32 timer, uint32 timer_duration) { //EQApplicationPacket *outapp = nullptr; Mob *ae_center = nullptr; @@ -2567,6 +2584,12 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui //set our reuse timer on long ass reuse_time spells... if(IsClient() && !isproc) { + //Support for bards to get disc recast timers while singing. + if (GetClass() == BARD && spell_id != casting_spell_id && timer != 0xFFFFFFFF) { + CastToClient()->GetPTimers().Start(timer, timer_duration); + LogSpells("Spell [{}]: Setting bard custom disciple reuse timer [{}] to [{}]", spell_id, timer, timer_duration); + } + if(casting_spell_aa_id) { AA::Rank *rank = zone->GetAlternateAdvancementRank(casting_spell_aa_id); @@ -3765,8 +3788,10 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes } } - // invuln mobs can't be affected by any spells, good or bad - if(spelltar->GetInvul() || spelltar->DivineAura()) { + // invuln mobs can't be affected by any spells, good or bad, except if caster is casting a spell with 'cast_not_standing' on self. + if ((spelltar->GetInvul() && !spelltar->DivineAura()) || + (spelltar != this && spelltar->DivineAura()) || + (spelltar == this && spelltar->DivineAura() && !spells[spell_id].cast_not_standing)) { LogSpells("Casting spell [{}] on [{}] aborted: they are invulnerable", spell_id, spelltar->GetName()); safe_delete(action_packet); return false; From 3c09448e9057a697f2e9a279c8315611e43fcfda Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 20 Jan 2022 12:13:14 -0500 Subject: [PATCH 570/624] [Spells] NPC spell push should work on rooted mobs. (#1941) * Update spells.cpp * [Spells] NPC spell push should work on rooted mobs. don't push perma or psuedorooted mobs --- zone/spells.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index fbefdad0e..4b1326bab 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4234,7 +4234,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes spelltar->CastToClient()->cheat_manager.SetExemptStatus(KnockBack, true); } } - else if (RuleB(Spells, NPCSpellPush) && !spelltar->IsRooted() && spelltar->ForcedMovement == 0) { + else if (RuleB(Spells, NPCSpellPush) && !permarooted && !IsPseudoRooted() && spelltar->ForcedMovement == 0) { spelltar->m_Delta.x += action->force * g_Math.FastSin(action->hit_heading); spelltar->m_Delta.y += action->force * g_Math.FastCos(action->hit_heading); spelltar->m_Delta.z += action->hit_pitch; From 936043a53c67fd0b40b0aed3cdfcc922d3d17a81 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 23 Jan 2022 20:27:32 -0500 Subject: [PATCH 571/624] bind sight pets (#1942) --- zone/spells.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 4b1326bab..716f3b343 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3663,10 +3663,11 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes return false; } - if(spelltar->IsClient() && spelltar->CastToClient()->IsHoveringForRespawn()) + if (spelltar->IsClient() && spelltar->CastToClient()->IsHoveringForRespawn()) { return false; + } - if(IsDetrimentalSpell(spell_id) && !IsAttackAllowed(spelltar, true) && !IsResurrectionEffects(spell_id)) { + if(IsDetrimentalSpell(spell_id) && !IsAttackAllowed(spelltar, true) && !IsResurrectionEffects(spell_id) && !IsEffectInSpell(spell_id, SE_BindSight)) { if(!IsClient() || !CastToClient()->GetGM()) { MessageString(Chat::SpellFailure, SPELL_NO_HOLD); return false; @@ -3935,7 +3936,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes } } } - else if ( !IsAttackAllowed(spelltar, true) && !IsResurrectionEffects(spell_id)) // Detrimental spells - PVP check + else if ( !IsAttackAllowed(spelltar, true) && !IsResurrectionEffects(spell_id) && !IsEffectInSpell(spell_id, SE_BindSight)) // Detrimental spells - PVP check { LogSpells("Detrimental spell [{}] can't take hold [{}] -> [{}]", spell_id, GetName(), spelltar->GetName()); spelltar->MessageString(Chat::SpellFailure, YOU_ARE_PROTECTED, GetCleanName()); From e99c8dafc5e549cefb82e383ab2dc704f5c72ef2 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 23 Jan 2022 20:27:45 -0500 Subject: [PATCH 572/624] spellbar lock bug fix (#1943) --- zone/client.h | 1 + zone/client_packet.cpp | 4 ++++ zone/spells.cpp | 16 ++++++++++++++++ 3 files changed, 21 insertions(+) diff --git a/zone/client.h b/zone/client.h index c6e12071e..598d12f6a 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1491,6 +1491,7 @@ public: void LeaveRaidXTargets(Raid *r); bool GroupFollow(Client* inviter); inline bool GetRunMode() const { return runmode; } + void SendItemRecastTimer(uint32 recast_type, uint32 recast_delay = 0); inline bool AggroMeterAvailable() const { return ((m_ClientVersionBit & EQ::versions::maskRoF2AndLater)) && RuleB(Character, EnableAggroMeter); } // RoF untested inline void SetAggroMeterLock(int in) { m_aggrometer.set_lock_id(in); } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 0e1556623..9965778fc 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -8887,6 +8887,9 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) if (item->RecastDelay > 0) { if (!GetPTimers().Expired(&database, (pTimerItemStart + item->RecastType), false)) { + SendItemRecastTimer(item->RecastType); //Problem: When you loot corpse, recast display is not present. This causes it to display again. Could not get to display when sending from looting. + MessageString(Chat::Red, SPELL_RECAST); + SendSpellBarEnable(item->Click.Effect); LogSpells("Casting of [{}] canceled: item spell reuse timer not expired", spell_id); return; } @@ -8926,6 +8929,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) if (!GetPTimers().Expired(&database, (pTimerItemStart + augitem->RecastType), false)) { LogSpells("Casting of [{}] canceled: item spell reuse timer from augment not expired", spell_id); MessageString(Chat::Red, SPELL_RECAST); + SendSpellBarEnable(augitem->Click.Effect); return; } } diff --git a/zone/spells.cpp b/zone/spells.cpp index 716f3b343..a3db1d736 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -6284,6 +6284,22 @@ void Client::SendSpellAnim(uint16 targetid, uint16 spell_id) entity_list.QueueCloseClients(this, &app, false, RuleI(Range, SpellParticles)); } +void Client::SendItemRecastTimer(uint32 recast_type, uint32 recast_delay) +{ + if (!recast_delay) { + recast_delay = GetPTimers().GetRemainingTime(pTimerItemStart + recast_type); + } + + if (recast_delay) { + auto outapp = new EQApplicationPacket(OP_ItemRecastDelay, sizeof(ItemRecastDelay_Struct)); + ItemRecastDelay_Struct *ird = (ItemRecastDelay_Struct *)outapp->pBuffer; + ird->recast_delay = recast_delay; + ird->recast_type = recast_type; + QueuePacket(outapp); + safe_delete(outapp); + } +} + void Mob::CalcDestFromHeading(float heading, float distance, float MaxZDiff, float StartX, float StartY, float &dX, float &dY, float &dZ) { if (!distance) { return; } From e4f2aec11eafaa96a84d1e098cb88de37a72b5ff Mon Sep 17 00:00:00 2001 From: mmcgarvey Date: Sun, 23 Jan 2022 21:18:38 -0500 Subject: [PATCH 573/624] [XTarget] Revert All XTarget Corpse Changes (#1944) --- zone/attack.cpp | 3 --- zone/client.cpp | 7 +++---- zone/mob.cpp | 4 ---- zone/mob.h | 2 -- 4 files changed, 3 insertions(+), 13 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 4ec8a8044..1c8c0da6e 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2583,9 +2583,6 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy entity_list.RemoveFromAutoXTargets(this); - if (killer != nullptr && killer->GetUltimateOwner() && killer->GetUltimateOwner()->IsClient()) { - killer->GetUltimateOwner()->CastToClient()->ProcessXTargetAutoHaters(); - } uint16 emoteid = this->GetEmoteID(); auto corpse = new Corpse(this, &itemlist, GetNPCTypeID(), &NPCTypedata, level > 54 ? RuleI(NPC, MajorNPCCorpseDecayTimeMS) diff --git a/zone/client.cpp b/zone/client.cpp index 60f7212a8..dd45cbbc7 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -7173,7 +7173,7 @@ void Client::OpenLFGuildWindow() bool Client::IsXTarget(const Mob *m) const { - if(!XTargettingAvailable() || !m || !m->IsValidXTarget()) + if(!XTargettingAvailable() || !m || (m->GetID() == 0)) return false; for(int i = 0; i < GetMaxXTargets(); ++i) @@ -7216,10 +7216,10 @@ void Client::UpdateClientXTarget(Client *c) // IT IS NOT SAFE TO CALL THIS IF IT'S NOT INITIAL AGGRO void Client::AddAutoXTarget(Mob *m, bool send) { + m_activeautohatermgr->increment_count(m); + if (!XTargettingAvailable() || !XTargetAutoAddHaters || IsXTarget(m)) return; - - m_activeautohatermgr->increment_count(m); for(int i = 0; i < GetMaxXTargets(); ++i) { @@ -7431,7 +7431,6 @@ void Client::ProcessXTargetAutoHaters() if (XTargets[i].ID != 0 && !GetXTargetAutoMgr()->contains_mob(XTargets[i].ID)) { XTargets[i].ID = 0; - XTargets[i].Name[0] = 0; XTargets[i].dirty = true; } diff --git a/zone/mob.cpp b/zone/mob.cpp index d754e27e2..e60e7a847 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -6650,7 +6650,3 @@ void Mob::SetBucket(std::string bucket_name, std::string bucket_value, std::stri std::string full_bucket_name = fmt::format("{}-{}", GetBucketKey(), bucket_name); DataBucket::SetData(full_bucket_name, bucket_value, expiration); } - -bool Mob::IsValidXTarget() const { - return (GetID() > 0 || !IsCorpse()); -} diff --git a/zone/mob.h b/zone/mob.h index e69802cc1..39cee5a96 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1307,8 +1307,6 @@ public: std::string GetBucketRemaining(std::string bucket_name); void SetBucket(std::string bucket_name, std::string bucket_value, std::string expiration = ""); - bool IsValidXTarget() const; - #ifdef BOTS // Bots HealRotation methods bool IsHealRotationTarget() { return (m_target_of_heal_rotation.use_count() && m_target_of_heal_rotation.get()); } From 5a7ee28740aa1a1a383632ad5ee15a4df3a259e6 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Wed, 26 Jan 2022 15:57:18 -0500 Subject: [PATCH 574/624] [Bug Fix] Fix quest::updatespawntimer() Perl croak. (#1947) --- zone/embparser_api.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 94e403508..67034c4ab 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -3051,7 +3051,7 @@ XS(XS__UpdateSpawnTimer); XS(XS__UpdateSpawnTimer) { dXSARGS; if (items != 2) - Perl_croak(aTHX_ "Usage: quest::UpdateSpawnTimer(uint32 spawn2_id, uint32 updated_time_till_repop)"); + Perl_croak(aTHX_ "Usage: quest::updatespawntimer(uint32 spawn2_id, uint32 updated_time_till_repop)"); uint32 spawn2_id = (int) SvIV(ST(0)); uint32 updated_time_till_repop = (int) SvIV(ST(1)); From b9722c6d28b492fc01e5314bb711d8c019df7921 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Wed, 26 Jan 2022 17:02:37 -0500 Subject: [PATCH 575/624] [Bug Fix] Any use of TempName left old clean_name. (#1946) * [Bug Fix] Any use of TempName left old clean_name. * Dunsel change --- zone/mob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index e60e7a847..51bce159d 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3094,6 +3094,7 @@ void Mob::TempName(const char *newname) char temp_name[64]; char old_name[64]; strn0cpy(old_name, GetName(), 64); + clean_name[0] = 0; if(newname) strn0cpy(temp_name, newname, 64); @@ -3102,7 +3103,6 @@ void Mob::TempName(const char *newname) if(!newname) { strn0cpy(temp_name, GetOrigName(), 64); SetName(temp_name); - //CleanMobName(GetName(), temp_name); strn0cpy(temp_name, GetCleanName(), 64); } From e850d80656b667866e4c317d00edcdbe2be3a020 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Wed, 26 Jan 2022 18:17:25 -0500 Subject: [PATCH 576/624] [Bug Fix (faction)] Do not award faction if NPC is charmed. (#1945) * Do not award faction if npc is charmed. * No faction on kill of charmed mob or questreward of same --- zone/attack.cpp | 2 +- zone/client.cpp | 4 ++-- zone/questmgr.cpp | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 1c8c0da6e..122f43d31 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2411,7 +2411,7 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy give_exp_client = give_exp->CastToClient(); //do faction hits even if we are a merchant, so long as a player killed us - if (give_exp_client && !RuleB(NPC, EnableMeritBasedFaction)) + if (!IsCharmed() && give_exp_client && !RuleB(NPC, EnableMeritBasedFaction)) hate_list.DoFactionHits(GetNPCFactionID()); bool IsLdonTreasure = (this->GetClass() == LDON_TREASURE); diff --git a/zone/client.cpp b/zone/client.cpp index dd45cbbc7..7a1a43d65 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -8678,7 +8678,7 @@ void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold, if (faction) { - if (target && target->IsNPC()) + if (target && target->IsNPC() && !target->IsCharmed()) { int32 nfl_id = target->CastToNPC()->GetNPCFactionID(); SetFactionLevel(CharacterID(), nfl_id, GetBaseClass(), GetBaseRace(), GetDeity(), true); @@ -8714,7 +8714,7 @@ void Client::QuestReward(Mob* target, const QuestReward_Struct &reward, bool fac if (faction) { - if (target && target->IsNPC()) + if (target && target->IsNPC() && !target->IsCharmed()) { int32 nfl_id = target->CastToNPC()->GetNPCFactionID(); SetFactionLevel(CharacterID(), nfl_id, GetBaseClass(), GetBaseRace(), GetDeity(), true); diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 7ee12f466..5bcbbfb14 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1353,7 +1353,8 @@ void QuestManager::save() { void QuestManager::faction(int faction_id, int faction_value, int temp) { QuestManagerCurrentQuestVars(); - if (initiator && initiator->IsClient()) { + running_quest run = quests_running_.top(); + if(run.owner->IsCharmed() == false && initiator && initiator->IsClient()) { if(faction_id != 0 && faction_value != 0) { initiator->SetFactionLevel2( initiator->CharacterID(), From afdbc0ce80d86a2ea207f641ff35d758da056c9d Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 28 Jan 2022 22:05:29 -0500 Subject: [PATCH 577/624] bug fix for push while rooted (#1949) --- zone/mob.h | 1 + zone/spells.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/zone/mob.h b/zone/mob.h index 39cee5a96..f17c5c058 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1059,6 +1059,7 @@ public: inline const bool IsRoamer() const { return roamer; } inline const int GetWanderType() const { return wandertype; } inline const bool IsRooted() const { return rooted || permarooted; } + inline const bool IsPermaRooted() const { return permarooted; } int GetSnaredAmount(); inline const bool IsPseudoRooted() const { return pseudo_rooted; } inline void SetPseudoRoot(bool prState) { pseudo_rooted = prState; } diff --git a/zone/spells.cpp b/zone/spells.cpp index a3db1d736..b62b591e2 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4235,7 +4235,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes spelltar->CastToClient()->cheat_manager.SetExemptStatus(KnockBack, true); } } - else if (RuleB(Spells, NPCSpellPush) && !permarooted && !IsPseudoRooted() && spelltar->ForcedMovement == 0) { + else if (RuleB(Spells, NPCSpellPush) && !spelltar->IsPermaRooted() && !spelltar->IsPseudoRooted() && spelltar->ForcedMovement == 0) { spelltar->m_Delta.x += action->force * g_Math.FastSin(action->hit_heading); spelltar->m_Delta.y += action->force * g_Math.FastCos(action->hit_heading); spelltar->m_Delta.z += action->hit_pitch; From 44b8c9203a8a04e92bf7f184b60fbd10fc3e9c7e Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 28 Jan 2022 22:05:45 -0500 Subject: [PATCH 578/624] [Spells] Updates to spell field 'cast not stands' to ignore casting restrictions (#1938) * test * complete * Update effects.cpp * Update spells.cpp * Update effects.cpp * [Spells] Support for bards using Disciplines while casting or /melody. Support for spell field 'cast not standing' not allow casting from divine aura * [Spells] Support for bards using Disciplines while casting or /melody. DA bypass logic for spells with field 'cast_not_standing' * updates * stun and mez bypass * Update spdat.cpp * Update spdat.cpp * Update spells.cpp --- common/spdat.cpp | 12 ++++++++++++ common/spdat.h | 3 ++- zone/aa.cpp | 2 +- zone/effects.cpp | 8 ++++++-- zone/spell_effects.cpp | 2 +- zone/spells.cpp | 8 ++++---- 6 files changed, 26 insertions(+), 9 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index edd670928..486a5a499 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1671,3 +1671,15 @@ bool CastRestrictedSpell(int spellid) return false; } } + +bool IgnoreCastingRestriction(int32 spell_id) { + /* + field 'cast_not_standing' allows casting when sitting, stunned, mezed, Divine Aura, through SPA 343 Interrupt casting + Likely also allows for casting while feared, but need to confirm. Possibly also while charmed. + This field also allows for damage to ignore DA immunity. + */ + if (spells[spell_id].cast_not_standing) { + return true; + } + return false; +} diff --git a/common/spdat.h b/common/spdat.h index d2b7dff04..231953c33 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -1357,7 +1357,7 @@ struct SPDat_Spell_Struct /* 181 */ int pvp_duration; // buffdurationformula for PvP -- PVP_DURATION /* 182 */ int pvp_duration_cap; // buffduration for PvP -- PVP_DURATION_CAP /* 183 */ int pcnpc_only_flag; // valid values are 0, 1 = PCs (and mercs), and 2 = NPCs (and not mercs) -- PCNPC_ONLY_FLAG -/* 184 */ bool cast_not_standing; // this is checked in the client's EQ_Spell::IsCastWhileInvisSpell, this also blocks SE_InterruptCasting from affecting this spell -- CAST_NOT_STANDING +/* 184 */ bool cast_not_standing; // this is checked in the client's EQ_Spell::IsCastWhileInvisSpell, this also blocks SE_InterruptCasting from affecting this spell -- CAST_NOT_STANDING (Allows casting if DA, stun, mezed, charm? fear?, damage to invul targets) /* 185 */ bool can_mgb; // 0=no, -1 or 1 = yes -- CAN_MGB /* 186 */ int dispel_flag; // -- NO_DISPELL /* 187 */ //int npc_category; // -- NPC_MEM_CATEGORY @@ -1537,6 +1537,7 @@ int GetViralMaxSpreadTime(int32 spell_id); int GetViralSpreadRange(int32 spell_id); bool IsInstrumentModAppliedToSpellEffect(int32 spell_id, int effect); uint32 GetProcLimitTimer(int32 spell_id, int proc_type); +bool IgnoreCastingRestriction(int32 spell_id); int CalcPetHp(int levelb, int classb, int STA = 75); int GetSpellEffectDescNum(uint16 spell_id); diff --git a/zone/aa.cpp b/zone/aa.cpp index a72bf2d7e..84b78d4f8 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -1297,7 +1297,7 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { target_id = GetPetID(); // extra handling for cast_not_standing spells - if (!spells[rank->spell].cast_not_standing) { + if (!IgnoreCastingRestriction(rank->spell)) { if (GetAppearance() == eaSitting) // we need to stand! SetAppearance(eaStanding, false); diff --git a/zone/effects.cpp b/zone/effects.cpp index 3fedf9a24..80b48fc0f 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -725,7 +725,11 @@ void Client::SendDisciplineUpdate() { bool Client::UseDiscipline(uint32 spell_id, uint32 target) { // Dont let client waste a reuse timer if they can't use the disc - if (IsStunned() || IsFeared() || IsMezzed() || IsAmnesiad() || IsPet()) + if ((IsStunned() && !IgnoreCastingRestriction(spell_id))|| + IsFeared() || + (IsMezzed() && !IgnoreCastingRestriction(spell_id)) || + IsAmnesiad() || + IsPet()) { if (IsAmnesiad()) { MessageString(Chat::Red, MELEE_SILENCE); @@ -748,7 +752,7 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) { return(false); } - if (DivineAura() && !spells[spell_id].cast_not_standing) { + if (DivineAura() && !IgnoreCastingRestriction(spell_id)) { return false; } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 369fde125..51c0f99ef 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3911,7 +3911,7 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) case SE_InterruptCasting: { if (IsCasting()) { const auto &spell = spells[casting_spell_id]; - if (!spell.cast_not_standing && zone->random.Roll(spells[buff.spellid].base_value[i])) { + if (!IgnoreCastingRestriction(spell.id) && zone->random.Roll(spells[buff.spellid].base_value[i])) { InterruptSpell(); } } diff --git a/zone/spells.cpp b/zone/spells.cpp index b62b591e2..a862db9c1 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -168,9 +168,9 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, casting_spell_id || delaytimer || spellend_timer.Enabled() || - IsStunned() || + (IsStunned() && !IgnoreCastingRestriction(spell_id)) || IsFeared() || - IsMezzed() || + (IsMezzed() && !IgnoreCastingRestriction(spell_id)) || (IsSilenced() && !IsDiscipline(spell_id)) || (IsAmnesiad() && IsDiscipline(spell_id)) ) @@ -217,7 +217,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, } //cannot cast under divine aura, unless spell has 'cast_not_standing' flag. - if(DivineAura() && !spells[spell_id].cast_not_standing) { + if(DivineAura() && !IgnoreCastingRestriction(spell_id)) { LogSpells("Spell casting canceled: cannot cast while Divine Aura is in effect"); InterruptSpell(173, 0x121, false); if(IsClient()) { @@ -3792,7 +3792,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes // invuln mobs can't be affected by any spells, good or bad, except if caster is casting a spell with 'cast_not_standing' on self. if ((spelltar->GetInvul() && !spelltar->DivineAura()) || (spelltar != this && spelltar->DivineAura()) || - (spelltar == this && spelltar->DivineAura() && !spells[spell_id].cast_not_standing)) { + (spelltar == this && spelltar->DivineAura() && !IgnoreCastingRestriction(spell_id))) { LogSpells("Casting spell [{}] on [{}] aborted: they are invulnerable", spell_id, spelltar->GetName()); safe_delete(action_packet); return false; From 7f5706abcf2966e00994836b96a72b78cb80657a Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 28 Jan 2022 22:07:23 -0500 Subject: [PATCH 579/624] bard throw while casting (#1937) --- zone/special_attacks.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 0a51d10fb..4d9114dde 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -219,8 +219,8 @@ void Client::OPCombatAbility(const CombatAbility_Struct *ca_atk) { if (!GetTarget()) return; - // make sure were actually able to use such an attack. - if (spellend_timer.Enabled() || IsFeared() || IsStunned() || IsMezzed() || DivineAura() || dead) + // make sure were actually able to use such an attack. (Bards can throw while casting. ~Kayen confirmed on live 1/22) + if ((spellend_timer.Enabled() && GetClass() != BARD)|| IsFeared() || IsStunned() || IsMezzed() || DivineAura() || dead) return; pTimerType timer = pTimerCombatAbility; @@ -229,7 +229,6 @@ void Client::OPCombatAbility(const CombatAbility_Struct *ca_atk) if (ClientVersion() >= EQ::versions::ClientVersion::RoF2 && ca_atk->m_skill == EQ::skills::SkillTigerClaw) timer = pTimerCombatAbility2; - bool CanBypassSkillCheck = false; if (ca_atk->m_skill == EQ::skills::SkillBash) { // SLAM - Bash without a shield equipped @@ -1356,7 +1355,7 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51 } if(!IsAttackAllowed(other) || - IsCasting() || + (IsCasting() && GetClass() != BARD) || IsSitting() || (DivineAura() && !GetGM()) || IsStunned() || From cba95851a2661af2b6f13f3f85b9af84e8d63acd Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sat, 29 Jan 2022 17:47:35 -0800 Subject: [PATCH 580/624] [Combat] Legacy Combat Middleware Affected by PR #1858 (#1939) --- utils/mods/legacy_combat.lua | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/utils/mods/legacy_combat.lua b/utils/mods/legacy_combat.lua index c04644a8d..6a031ec86 100644 --- a/utils/mods/legacy_combat.lua +++ b/utils/mods/legacy_combat.lua @@ -962,15 +962,14 @@ function CommonOutgoingHitSuccess(e) ) ); - e.hit.damage_done = e.hit.damage_done + (e.hit.damage_done * e.other:GetSkillDmgTaken(e.hit.skill) / 100) + (e.self:GetSkillDmgAmt(e.hit.skill) + e.other:GetFcDamageAmtIncoming(e.self, 0, true, e.hit.skill)); + e.hit.damage_done = e.hit.damage_done + (e.hit.damage_done * e.other:GetSkillDmgTaken(e.hit.skill) / 100) + e.self:GetSkillDmgAmt(e.hit.skill); eq.log_combat( - string.format("[%s] [Mob::CommonOutgoingHitSuccess] Dmg [%i] SkillDmgTaken [%i] SkillDmgtAmt [%i] FcDmgAmtIncoming [%i] Post DmgCalcs", + string.format("[%s] [Mob::CommonOutgoingHitSuccess] Dmg [%i] SkillDmgTaken [%i] SkillDmgtAmt [%i] Post DmgCalcs", e.self:GetCleanName(), e.hit.damage_done, e.other:GetSkillDmgTaken(e.hit.skill), - e.self:GetSkillDmgAmt(e.hit.skill), - e.other:GetFcDamageAmtIncoming(e.self, 0, true, e.hit.skill) + e.self:GetSkillDmgAmt(e.hit.skill) ) ); @@ -997,4 +996,4 @@ function ApplyMeleeDamageBonus(e) e.hit.damage_done = e.hit.damage_done + (e.hit.damage_done * dmgbonusmod / 100); return e; -end \ No newline at end of file +end From a6cd0bc33aefc3e0c9015cb833a1839dfe152074 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 29 Jan 2022 20:54:26 -0500 Subject: [PATCH 581/624] [Bug Fix] Do not allow /open to be used on traps or auras, causes crash (#1951) * Update client_packet.cpp * [Bug Fix] Do not allow /open to be used on traps or auras, causes crash * [Bug Fix] Do not allow /open to be used on traps or auras, causes crash --- zone/client_packet.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 9965778fc..05fa73495 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -9052,7 +9052,7 @@ void Client::Handle_OP_LDoNButton(const EQApplicationPacket *app) void Client::Handle_OP_LDoNDisarmTraps(const EQApplicationPacket *app) { Mob * target = GetTarget(); - if (target->IsNPC()) + if (target && target->IsNPC() && !target->IsAura()) { if (HasSkill(EQ::skills::SkillDisarmTraps)) { @@ -9071,21 +9071,22 @@ void Client::Handle_OP_LDoNDisarmTraps(const EQApplicationPacket *app) void Client::Handle_OP_LDoNInspect(const EQApplicationPacket *app) { Mob * target = GetTarget(); - if (target && target->GetClass() == LDON_TREASURE) + if (target && target->GetClass() == LDON_TREASURE && !target->IsAura()) Message(Chat::Yellow, "%s", target->GetCleanName()); } void Client::Handle_OP_LDoNOpen(const EQApplicationPacket *app) { Mob * target = GetTarget(); - if (target && target->IsNPC()) + if (target && target->IsNPC() && !target->IsAura()) { HandleLDoNOpen(target->CastToNPC()); + } } void Client::Handle_OP_LDoNPickLock(const EQApplicationPacket *app) { Mob * target = GetTarget(); - if (target->IsNPC()) + if (target && target->IsNPC() && !target->IsAura()) { if (HasSkill(EQ::skills::SkillPickLock)) { @@ -9104,7 +9105,7 @@ void Client::Handle_OP_LDoNPickLock(const EQApplicationPacket *app) void Client::Handle_OP_LDoNSenseTraps(const EQApplicationPacket *app) { Mob * target = GetTarget(); - if (target->IsNPC()) + if (target && target->IsNPC() && !target->IsAura()) { if (HasSkill(EQ::skills::SkillSenseTraps)) { From 7749c626f0d12a99720e60f0efaf7b7ebb7058d2 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Sat, 29 Jan 2022 20:55:40 -0500 Subject: [PATCH 582/624] [Bug Fix] Fix issue with mobs summoning PCs into ceilings (#1921) --- zone/mob.cpp | 3 +++ zone/waypoints.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index 51bce159d..d16182eae 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3594,7 +3594,10 @@ bool Mob::HateSummon() { if(summon_level == 1) { entity_list.MessageClose(this, true, 500, Chat::Say, "%s says 'You will not evade me, %s!' ", GetCleanName(), target->GetCleanName() ); + float summoner_zoff = this->GetZOffset(); + float summoned_zoff = target->GetZOffset(); auto new_pos = m_Position; + new_pos.z -= (summoner_zoff - summoned_zoff); float angle = new_pos.w - target->GetHeading(); new_pos.w = target->GetHeading(); diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index 34b809238..f0c1fc1ca 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -958,6 +958,7 @@ void Mob::TryMoveAlong(float distance, float angle, bool send) } new_pos.z = GetFixedZ(new_pos); + Teleport(new_pos); } @@ -971,7 +972,6 @@ glm::vec4 Mob::TryMoveAlong(const glm::vec4 &start, float distance, float angle) glm::vec3 new_pos = start; new_pos.x += distance * g_Math.FastSin(angle); new_pos.y += distance * g_Math.FastCos(angle); - new_pos.z += GetZOffset(); if (zone->HasMap()) { if (zone->zonemap->LineIntersectsZone(start, new_pos, 0.0f, &tmp_pos)) From 58d5983ef13ff9c6be56419b7ca5de23592755d6 Mon Sep 17 00:00:00 2001 From: mmcgarvey Date: Sat, 29 Jan 2022 21:01:58 -0500 Subject: [PATCH 583/624] [Skills] Configurable Exponential Decay Formula for Skill Up (#1887) * [Skills] Exponential Decay Skill Up Formula Added an exponential decay skill up formula option. The current, linear, formula results in negative chances to skill up, which have been mitigated via a multiplier and minimum of 1% * [Skills]Configurable Exponential Decay Formala for Skill Up What this fixes: The existing formula for determining whether or not to skill up could result in negative chances, and made an assumption around the number 252. This would ultimately result in an override that would set the chance to 1. My fix: I created 2 new rules: Character:SkillUpMaximumChancePercentage Character:SkillUpMinimumChancePercentage I changed the forumla to: chance = ((max - min + skill_modification) * (.99^skill)) + min This results in an exponential decay that starts at skill-modified maximum and approaches minimum. I decided that max-min+skill_modification should never be less than min I also decided to continue to apply the Character:SkillUpModifier rule post-calculation. I do not really think this is necessary anymore, given this new formula, but we can discuss removing it. I chose 25 and 2 as default maximum and minimum based on feel. Related method signature fix: Client::mod_increase_skill_chance was changed to return a double and accept a double as an input for chance. This matches the actual data types provided while calling the method and eliminates some type coersion and resultant truncation. Right now, this method doesn't do anything, but in the future we could implement skill-specific training dummies that accelerate skill ups. I deduce that this is the purpose of this method call. * [Skills]Configurable Exponential Decay Formula for Skill Up What this fixes: The existing formula for determining whether or not to skill up could result in negative chances, and made an assumption around the number 252. This would ultimately result in an override that would set the chance to 1. My fix: I created 2 new rules: Character:SkillUpMaximumChancePercentage Character:SkillUpMinimumChancePercentage I changed the forumla to: chance = ((max - min + skill_modification) * (.99^skill)) + min This results in an exponential decay that starts at skill-modified maximum and approaches minimum. I decided that max-min+skill_modification should never be less than min I also decided to continue to apply the Character:SkillUpModifier rule post-calculation. I do not really think this is necessary anymore, given this new formula, but we can discuss removing it. I chose 25 and 2 as default maximum and minimum based on feel. Related method signature fix: Client::mod_increase_skill_chance was changed to return a double and accept a double as an input for chance. This matches the actual data types provided while calling the method and eliminates some type coersion and resultant truncation. Right now, this method doesn't do anything, but in the future we could implement skill-specific training dummies that accelerate skill ups. I deduce that this is the purpose of this method call. * fixup! [Skills]Configurable Exponential Decay Formula for Skill Up * fixup! [Skills]Configurable Exponential Decay Formula for Skill Up --- common/ruletypes.h | 2 ++ zone/client.cpp | 27 +++++++++++++++------------ zone/client.h | 2 +- zone/mod_functions.cpp | 2 +- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index e7817f4bb..4acd79dbd 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -174,6 +174,8 @@ RULE_INT(Character, ResurrectionSicknessSpellID, 756, "756 is Default Resurrecti RULE_BOOL(Character, EnableBardMelody, true, "Enable Bard /melody by default, to disable change to false for a classic experience.") RULE_BOOL(Character, EnableRangerAutoFire, true, "Enable Ranger /autofire by default, to disable change to false for a classic experience.") RULE_BOOL(Character, EnableTGB, true, "Enable /tgb (Target Group Buff) by default, to disable change to false for a classic experience.") +RULE_INT(Character, SkillUpMaximumChancePercentage, 25, "Maximum chance to improve a combat skill, before skill-specific modifiers. This should be greater than SkillUpMinimumChancePercentage.") +RULE_INT(Character, SkillUpMinimumChancePercentage, 2, "Minimum chance to improve a combat skill, after skill-specific modifiers. This should be lesser than SkillUpMaximumChancePercentage.") RULE_INT(Character, WarriorTrackingDistanceMultiplier, 0, "If you want warriors to be able to track, increase this above 0. 0 disables tracking packets.") RULE_INT(Character, ClericTrackingDistanceMultiplier, 0, "If you want clerics to be able to track, increase this above 0. 0 disables tracking packets.") RULE_INT(Character, PaladinTrackingDistanceMultiplier, 0, "If you want paladins to be able to track, increase this above 0. 0 disables tracking packets.") diff --git a/zone/client.cpp b/zone/client.cpp index 7a1a43d65..2857f86db 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -2422,7 +2422,7 @@ bool Client::CheckIncreaseSkill(EQ::skills::SkillType skillid, Mob *against_who, return false; if (skillid > EQ::skills::HIGHEST_SKILL) return false; - int skillval = GetRawSkill(skillid); + int skillval = GetRawSkill(skillid); int maxskill = GetMaxSkillAfterSpecializationRules(skillid, MaxSkill(skillid)); std::string export_string = fmt::format( "{} {}", @@ -2447,23 +2447,26 @@ bool Client::CheckIncreaseSkill(EQ::skills::SkillType skillid, Mob *against_who, // Make sure we're not already at skill cap if (skillval < maxskill) { - // the higher your current skill level, the harder it is - int32 Chance = 10 + chancemodi + ((252 - skillval) / 20); - - Chance = (Chance * RuleI(Character, SkillUpModifier) / 100); - - Chance = mod_increase_skill_chance(Chance, against_who); - - if(Chance < 1) - Chance = 1; // Make it always possible + double Chance = 0; + if (RuleI(Character, SkillUpMaximumChancePercentage) + chancemodi - RuleI(Character, SkillUpMinimumChancePercentage) <= RuleI(Character, SkillUpMinimumChancePercentage)) { + Chance = RuleI(Character, SkillUpMinimumChancePercentage); + } + else { + // f(x) = (max - min + modification) * .99^skillval + min + // This results in a exponential decay where as you skill up, you lose a slight chance to skill up, ranging from your modified maximum to approaching your minimum + // This result is increased by the existing SkillUpModifier rule + double working_chance = (((RuleI(Character, SkillUpMaximumChancePercentage) - RuleI(Character, SkillUpMinimumChancePercentage) + chancemodi) * (pow(0.99, skillval))) + RuleI(Character, SkillUpMinimumChancePercentage)); + Chance = (working_chance * RuleI(Character, SkillUpModifier) / 100); + Chance = mod_increase_skill_chance(Chance, against_who); + } if(zone->random.Real(0, 99) < Chance) { SetSkill(skillid, GetRawSkill(skillid) + 1); - LogSkills("Skill [{}] at value [{}] successfully gain with [{}]% chance (mod [{}])", skillid, skillval, Chance, chancemodi); + LogSkills("Skill [{}] at value [{}] successfully gain with [{}] chance (mod [{}])", skillid, skillval, Chance, chancemodi); return true; } else { - LogSkills("Skill [{}] at value [{}] failed to gain with [{}]% chance (mod [{}])", skillid, skillval, Chance, chancemodi); + LogSkills("Skill [{}] at value [{}] failed to gain with [{}] chance (mod [{}])", skillid, skillval, Chance, chancemodi); } } else { LogSkills("Skill [{}] at value [{}] cannot increase due to maxmum [{}]", skillid, skillval, maxskill); diff --git a/zone/client.h b/zone/client.h index 598d12f6a..4962d1e8f 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1566,7 +1566,7 @@ public: int mod_client_damage(int damage, EQ::skills::SkillType skillinuse, int hand, const EQ::ItemInstance* weapon, Mob* other); bool mod_client_message(char* message, uint8 chan_num); bool mod_can_increase_skill(EQ::skills::SkillType skillid, Mob* against_who); - int16 mod_increase_skill_chance(int16 chance, Mob* against_who); + double mod_increase_skill_chance(double chance, Mob* against_who); int mod_bindwound_percent(int max_percent, Mob* bindmob); int mod_bindwound_hp(int bindhps, Mob* bindmob); int mod_client_haste(int h); diff --git a/zone/mod_functions.cpp b/zone/mod_functions.cpp index b4951f76d..5091da606 100644 --- a/zone/mod_functions.cpp +++ b/zone/mod_functions.cpp @@ -61,7 +61,7 @@ bool Client::mod_client_message(char* message, uint8 chan_num) { return(true); } bool Client::mod_can_increase_skill(EQ::skills::SkillType skillid, Mob* against_who) { return(false); } //chance of general skill increase, rolled against 0-99 where higher chance is better. -int16 Client::mod_increase_skill_chance(int16 chance, Mob* against_who) { return(chance); } +double Client::mod_increase_skill_chance(double chance, Mob* against_who) { return(chance); } //Max percent of health you can bind wound starting with default value for class, item, and AA bonuses int Client::mod_bindwound_percent(int max_percent, Mob* bindmob) { return(max_percent); } From 7b235a6eded0c1cd8a7d1b1b2af9933d925a898a Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 29 Jan 2022 21:09:02 -0500 Subject: [PATCH 584/624] [Bug Fix] Fix issue where you can set your title to titles you don't have. (#1917) * [Bug Fix] Fix issue where you can set your title to titles you don't have. * Fixes. * Fix missing logic check for HasTitle Co-authored-by: Natedog2012 --- zone/client_packet.cpp | 18 +- zone/titles.cpp | 540 ++++++++++++++++++++--------------------- zone/titles.h | 49 ++-- 3 files changed, 294 insertions(+), 313 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 05fa73495..585878092 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -12838,17 +12838,15 @@ void Client::Handle_OP_SetTitle(const EQApplicationPacket *app) 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()); + if (!title_manager.HasTitle(this, sts->title_id)) { + return; } - else - { - Title = title_manager.GetSuffix(sts->title_id); - SetTitleSuffix(Title.c_str()); + + std::string title = !sts->is_suffix ? title_manager.GetPrefix(sts->title_id) : title_manager.GetSuffix(sts->title_id); + if (!sts->is_suffix) { + SetAATitle(title.c_str()); + } else { + SetTitleSuffix(title.c_str()); } } diff --git a/zone/titles.cpp b/zone/titles.cpp index a0cd10a12..8bdabc678 100644 --- a/zone/titles.cpp +++ b/zone/titles.cpp @@ -19,6 +19,7 @@ #include "../common/eq_packet_structs.h" #include "../common/string_util.h" #include "../common/misc_functions.h" +#include "../common/repositories/titles_repository.h" #include "client.h" #include "entity.h" @@ -34,206 +35,191 @@ TitleManager::TitleManager() { bool TitleManager::LoadTitles() { - Titles.clear(); + titles.clear(); std::string query = "SELECT `id`, `skill_id`, `min_skill_value`, `max_skill_value`, " - "`min_aa_points`, `max_aa_points`, `class`, `gender`, `char_id`, " - "`status`, `item_id`, `prefix`, `suffix`, `title_set` FROM titles"; - auto results = database.QueryDatabase(query); - if (!results.Success()) { + "`min_aa_points`, `max_aa_points`, `class`, `gender`, `char_id`, " + "`status`, `item_id`, `prefix`, `suffix`, `title_set` FROM titles"; + auto results = database.QueryDatabase(query); + if (!results.Success() || !results.RowCount()) { return false; } - for (auto row = results.begin(); row != results.end(); ++row) { - TitleEntry Title; - Title.TitleID = atoi(row[0]); - Title.SkillID = (EQ::skills::SkillType) atoi(row[1]); - Title.MinSkillValue = atoi(row[2]); - Title.MaxSkillValue = atoi(row[3]); - Title.MinAAPoints = atoi(row[4]); - Title.MaxAAPoints = atoi(row[5]); - Title.Class = atoi(row[6]); - Title.Gender = atoi(row[7]); - Title.CharID = atoi(row[8]); - Title.Status = atoi(row[9]); - Title.ItemID = atoi(row[10]); - Title.Prefix = row[11]; - Title.Suffix = row[12]; - Title.TitleSet = atoi(row[13]); - Titles.push_back(Title); + for (auto row : results) { + TitleEntry title; + title.title_id = std::stoi(row[0]); + title.skill_id = (EQ::skills::SkillType) std::stoi(row[1]); + title.min_skill_value = std::stoi(row[2]); + title.max_skill_value = std::stoi(row[3]); + title.min_aa_points = std::stoi(row[4]); + title.max_aa_points = std::stoi(row[5]); + title.class_id = std::stoi(row[6]); + title.gender_id = std::stoi(row[7]); + title.character_id = std::stoi(row[8]); + title.status = std::stoi(row[9]); + title.item_id = std::stoi(row[10]); + title.prefix = row[11]; + title.suffix = row[12]; + title.titleset = std::stoi(row[13]); + titles.push_back(title); } return true; } -EQApplicationPacket *TitleManager::MakeTitlesPacket(Client *c) +EQApplicationPacket *TitleManager::MakeTitlesPacket(Client *client) { - std::vector::iterator Iterator; - - std::vector AvailableTitles; - - uint32 Length = 4; - - Iterator = Titles.begin(); - - while(Iterator != Titles.end()) - { - if(!IsClientEligibleForTitle(c, Iterator)) - { - ++Iterator; + std::vector available_titles; + uint32 length = 4; + for (const auto& title : titles) { + if (!IsClientEligibleForTitle(client, title)) { continue; } - AvailableTitles.push_back((*Iterator)); - - Length += Iterator->Prefix.length() + Iterator->Suffix.length() + 6; - - ++Iterator; - + available_titles.push_back(title); + length += title.prefix.length() + title.suffix.length() + 6; } - auto outapp = new EQApplicationPacket(OP_SendTitleList, Length); - - char *Buffer = (char *)outapp->pBuffer; - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, AvailableTitles.size()); - - Iterator = AvailableTitles.begin(); - - while(Iterator != AvailableTitles.end()) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, Iterator->TitleID); - - VARSTRUCT_ENCODE_STRING(Buffer, Iterator->Prefix.c_str()); - - VARSTRUCT_ENCODE_STRING(Buffer, Iterator->Suffix.c_str()); - - ++Iterator; + auto outapp = new EQApplicationPacket(OP_SendTitleList, length); + char *buffer = (char *)outapp->pBuffer; + VARSTRUCT_ENCODE_TYPE(uint32, buffer, available_titles.size()); + for (const auto& available_title : available_titles) { + VARSTRUCT_ENCODE_TYPE(uint32, buffer, available_title.title_id); + VARSTRUCT_ENCODE_STRING(buffer, available_title.prefix.c_str()); + VARSTRUCT_ENCODE_STRING(buffer, available_title.suffix.c_str()); } return(outapp); } -int TitleManager::NumberOfAvailableTitles(Client *c) +int TitleManager::NumberOfAvailableTitles(Client *client) { - int Count = 0; - - std::vector::iterator Iterator; - - Iterator = Titles.begin(); - - while(Iterator != Titles.end()) - { - if(IsClientEligibleForTitle(c, Iterator)) - ++Count; - - ++Iterator; - } - - return Count; -} - -std::string TitleManager::GetPrefix(int TitleID) -{ - std::vector::iterator Iterator; - - Iterator = Titles.begin(); - - while(Iterator != Titles.end()) - { - if((*Iterator).TitleID == TitleID) - return (*Iterator).Prefix; - - ++Iterator; - } - - return ""; -} - -std::string TitleManager::GetSuffix(int TitleID) -{ - std::vector::iterator Iterator; - - Iterator = Titles.begin(); - - while(Iterator != Titles.end()) - { - if((*Iterator).TitleID == TitleID) - return (*Iterator).Suffix; - - ++Iterator; - } - - return ""; -} - -bool TitleManager::IsClientEligibleForTitle(Client *c, std::vector::iterator Title) -{ - if((Title->CharID >= 0) && (c->CharacterID() != static_cast(Title->CharID))) - return false; - - if((Title->Status >= 0) && (c->Admin() < Title->Status)) - return false; - - if((Title->Gender >= 0) && (c->GetBaseGender() != Title->Gender)) - return false; - - if((Title->Class >= 0) && (c->GetBaseClass() != Title->Class)) - return false; - - if((Title->MinAAPoints >= 0) && (c->GetSpentAA() < static_cast(Title->MinAAPoints))) - return false; - - if((Title->MaxAAPoints >= 0) && (c->GetSpentAA() > static_cast(Title->MaxAAPoints))) - return false; - - if(Title->SkillID >= 0) - { - if ((Title->MinSkillValue >= 0) && (c->GetRawSkill(static_cast(Title->SkillID)) < static_cast(Title->MinSkillValue))) - return false; - - if ((Title->MaxSkillValue >= 0) && (c->GetRawSkill(static_cast(Title->SkillID)) > static_cast(Title->MaxSkillValue))) - return false; - + int count = 0; + for (const auto& title : titles) { + if (IsClientEligibleForTitle(client, title)) { + ++count; } + } - if ((Title->ItemID >= 1) && (c->GetInv().HasItem(Title->ItemID, 0, 0xFF) == INVALID_INDEX)) - return false; - - if((Title->TitleSet > 0) && (!c->CheckTitle(Title->TitleSet))) - return false; - - return true; + return count; } -bool TitleManager::IsNewAATitleAvailable(int AAPoints, int Class) +std::string TitleManager::GetPrefix(int title_id) { - std::vector::iterator Iterator; + if (!title_id) { + return ""; + } - Iterator = Titles.begin(); + for (const auto& title : titles) { + if (title.title_id == title_id) { + return title.prefix; + } + } - while(Iterator != Titles.end()) - { - if((((*Iterator).Class == -1) || ((*Iterator).Class == Class)) && ((*Iterator).MinAAPoints == AAPoints)) - return true; + return ""; +} - ++Iterator; +std::string TitleManager::GetSuffix(int title_id) +{ + if (!title_id) { + return ""; + } + + for (const auto& title : titles) { + if (title.title_id == title_id) { + return title.suffix; + } + } + + return ""; +} + +bool TitleManager::HasTitle(Client* client, uint32 title_id) +{ + if (!client || !title_id) { + return false; + } + + for (const auto& title : titles) { + if (title.title_id == title_id) { + return IsClientEligibleForTitle(client, title); + } } return false; } -bool TitleManager::IsNewTradeSkillTitleAvailable(int SkillID, int SkillValue) +bool TitleManager::IsClientEligibleForTitle(Client *client, TitleEntry title) { - std::vector::iterator Iterator; + if (!client) { + return false; + } - Iterator = Titles.begin(); + if (title.character_id >= 0 && client->CharacterID() != static_cast(title.character_id)) { + return false; + } - while(Iterator != Titles.end()) - { - if(((*Iterator).SkillID == SkillID) && ((*Iterator).MinSkillValue == SkillValue)) + if (title.status >= 0 && client->Admin() < title.status) { + return false; + } + + if (title.gender_id >= 0 && client->GetBaseGender() != title.gender_id) { + return false; + } + + if (title.class_id >= 0 && client->GetBaseClass() != title.class_id) { + return false; + } + + if (title.min_aa_points >= 0 && client->GetSpentAA() < title.min_aa_points) { + return false; + } + + if (title.max_aa_points >= 0 && client->GetSpentAA() > title.max_aa_points) { + return false; + } + + if (title.skill_id >= 0) { + auto skill_id = static_cast(title.skill_id); + if (title.min_skill_value >= 0 && client->GetRawSkill(skill_id) < static_cast(title.min_skill_value)) { + return false; + } + + if (title.max_skill_value >= 0 && client->GetRawSkill(skill_id) > static_cast(title.max_skill_value)) { + return false; + } + } + + if (title.item_id >= 1 && client->GetInv().HasItem(title.item_id) == INVALID_INDEX) { + return false; + } + + if (title.titleset > 0 && !client->CheckTitle(title.titleset)) { + return false; + } + + return true; +} + +bool TitleManager::IsNewAATitleAvailable(int aa_points, int class_id) +{ + for (const auto& title : titles) { + if ( + (title.class_id == -1 || title.class_id == class_id) && + title.min_aa_points == aa_points + ) { return true; + } + } - ++Iterator; + return false; +} + +bool TitleManager::IsNewTradeSkillTitleAvailable(int skill_id, int skill_value) +{ + for (const auto& title : titles) { + if (title.skill_id == skill_id && title.min_skill_value == skill_value) { + return true; + } } return false; @@ -241,142 +227,138 @@ bool TitleManager::IsNewTradeSkillTitleAvailable(int SkillID, int SkillValue) void TitleManager::CreateNewPlayerTitle(Client *client, const char *title) { - if(!client || !title) + if (!client || !title) { return; - - auto escTitle = new char[strlen(title) * 2 + 1]; + } client->SetAATitle(title); - database.DoEscapeString(escTitle, title, strlen(title)); - auto query = StringFormat("SELECT `id` FROM titles " - "WHERE `prefix` = '%s' AND char_id = %i", - escTitle, client->CharacterID()); - auto results = database.QueryDatabase(query); - if (results.Success() && results.RowCount() > 0){ - safe_delete_array(escTitle); - return; + auto query = fmt::format( + "SELECT `id` FROM titles WHERE `prefix` = '{}' AND char_id = {}", + EscapeString(title), + client->CharacterID() + ); + auto results = database.QueryDatabase(query); + if (results.Success() && results.RowCount()){ + return; } - query = StringFormat("INSERT INTO titles (`char_id`, `prefix`) VALUES(%i, '%s')", - client->CharacterID(), escTitle); - safe_delete_array(escTitle); - results = database.QueryDatabase(query); - if(!results.Success()) { - return; - } + query = fmt::format( + "INSERT INTO titles (`char_id`, `prefix`) VALUES ({}, '{}')", + client->CharacterID(), + EscapeString(title) + ); + results = database.QueryDatabase(query); + if (!results.Success()) { + return; + } - auto pack = new ServerPacket(ServerOP_ReloadTitles, 0); - worldserver.SendPacket(pack); - safe_delete(pack); + auto pack = new ServerPacket(ServerOP_ReloadTitles, 0); + worldserver.SendPacket(pack); + safe_delete(pack); } void TitleManager::CreateNewPlayerSuffix(Client *client, const char *suffix) { - if(!client || !suffix) + if (!client || !suffix) { return; - - client->SetTitleSuffix(suffix); - - auto escSuffix = new char[strlen(suffix) * 2 + 1]; - database.DoEscapeString(escSuffix, suffix, strlen(suffix)); - - std::string query = StringFormat("SELECT `id` FROM titles " - "WHERE `suffix` = '%s' AND char_id = %i", - escSuffix, client->CharacterID()); - auto results = database.QueryDatabase(query); - if (results.Success() && results.RowCount() > 0) { - safe_delete_array(escSuffix); - return; - } - - query = StringFormat("INSERT INTO titles (`char_id`, `suffix`) VALUES(%i, '%s')", - client->CharacterID(), escSuffix); - safe_delete_array(escSuffix); - results = database.QueryDatabase(query); - if(!results.Success()) { - return; - } - - auto pack = new ServerPacket(ServerOP_ReloadTitles, 0); - worldserver.SendPacket(pack); - safe_delete(pack); -} - -void Client::SetAATitle(const char *Title) -{ - strn0cpy(m_pp.title, Title, sizeof(m_pp.title)); - - auto outapp = new EQApplicationPacket(OP_SetTitleReply, sizeof(SetTitleReply_Struct)); - - SetTitleReply_Struct *strs = (SetTitleReply_Struct *)outapp->pBuffer; - - strn0cpy(strs->title, Title, sizeof(strs->title)); - - strs->entity_id = GetID(); - - entity_list.QueueClients(this, outapp, false); - - safe_delete(outapp); -} - -void Client::SetTitleSuffix(const char *Suffix) -{ - strn0cpy(m_pp.suffix, Suffix, sizeof(m_pp.suffix)); - - auto outapp = new EQApplicationPacket(OP_SetTitleReply, sizeof(SetTitleReply_Struct)); - - SetTitleReply_Struct *strs = (SetTitleReply_Struct *)outapp->pBuffer; - - strs->is_suffix = 1; - - strn0cpy(strs->title, Suffix, sizeof(strs->title)); - - strs->entity_id = GetID(); - - entity_list.QueueClients(this, outapp, false); - - safe_delete(outapp); -} - -void Client::EnableTitle(int titleSet) { - - if (CheckTitle(titleSet)) - return; - - std::string query = StringFormat("INSERT INTO player_titlesets " - "(char_id, title_set) VALUES (%i, %i)", - CharacterID(), titleSet); - auto results = database.QueryDatabase(query); - if(!results.Success()) - LogError("Error in EnableTitle query for titleset [{}] and charid [{}]", titleSet, CharacterID()); - -} - -bool Client::CheckTitle(int titleSet) { - - std::string query = StringFormat("SELECT `id` FROM player_titlesets " - "WHERE `title_set`=%i AND `char_id`=%i LIMIT 1", - titleSet, CharacterID()); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - return false; } - if (results.RowCount() == 0) - return false; + client->SetTitleSuffix(suffix); + + std::string query = fmt::format( + "SELECT `id` FROM titles WHERE `suffix` = '{}' AND char_id = {}", + EscapeString(suffix), + client->CharacterID() + ); + auto results = database.QueryDatabase(query); + if (results.Success() && results.RowCount()) { + return; + } + + query = fmt::format( + "INSERT INTO titles (`char_id`, `suffix`) VALUES ({}, '{}')", + client->CharacterID(), + EscapeString(suffix) + ); + results = database.QueryDatabase(query); + if (!results.Success()) { + return; + } + + auto pack = new ServerPacket(ServerOP_ReloadTitles, 0); + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void Client::SetAATitle(const char *title) +{ + strn0cpy(m_pp.title, title, sizeof(m_pp.title)); + auto outapp = new EQApplicationPacket(OP_SetTitleReply, sizeof(SetTitleReply_Struct)); + SetTitleReply_Struct *strs = (SetTitleReply_Struct *)outapp->pBuffer; + strn0cpy(strs->title, title, sizeof(strs->title)); + strs->entity_id = GetID(); + entity_list.QueueClients(this, outapp, false); + safe_delete(outapp); +} + +void Client::SetTitleSuffix(const char *suffix) +{ + strn0cpy(m_pp.suffix, suffix, sizeof(m_pp.suffix)); + auto outapp = new EQApplicationPacket(OP_SetTitleReply, sizeof(SetTitleReply_Struct)); + SetTitleReply_Struct *strs = (SetTitleReply_Struct *)outapp->pBuffer; + strs->is_suffix = 1; + strn0cpy(strs->title, suffix, sizeof(strs->title)); + strs->entity_id = GetID(); + entity_list.QueueClients(this, outapp, false); + safe_delete(outapp); +} + +void Client::EnableTitle(int title_set) +{ + if (CheckTitle(title_set)) { + return; + } + + std::string query = fmt::format( + "INSERT INTO player_titlesets (char_id, title_set) VALUES ({}, {})", + CharacterID(), + title_set + ); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogError("Error in EnableTitle query for titleset [{}] and charid [{}]", title_set, CharacterID()); + } + +} + +bool Client::CheckTitle(int title_set) +{ + std::string query = fmt::format( + "SELECT `id` FROM player_titlesets WHERE `title_set` = {} AND `char_id` = {} LIMIT 1", + title_set, + CharacterID() + ); + auto results = database.QueryDatabase(query); + if (!results.Success() || !results.RowCount()) { + return false; + } return true; } -void Client::RemoveTitle(int titleSet) { - - if (!CheckTitle(titleSet)) +void Client::RemoveTitle(int title_set) +{ + if (!CheckTitle(title_set)) { return; + } - std::string query = StringFormat("DELETE FROM player_titlesets " - "WHERE `title_set` = %i AND `char_id` = %i", - titleSet, CharacterID()); - database.QueryDatabase(query); + TitlesRepository::DeleteWhere( + database, + fmt::format( + "`title_set` = {} AND `char_id` = {}", + title_set, + CharacterID() + ) + ); } - diff --git a/zone/titles.h b/zone/titles.h index a942a5688..640d4d29a 100644 --- a/zone/titles.h +++ b/zone/titles.h @@ -25,20 +25,20 @@ class EQApplicationPacket; struct TitleEntry { - int TitleID; - int SkillID; - int MinSkillValue; - int MaxSkillValue; - int MinAAPoints; - int MaxAAPoints; - int Class; - int Gender; - int CharID; - int Status; - int ItemID; - std::string Prefix; - std::string Suffix; - int TitleSet; + int title_id; + int skill_id; + int min_skill_value; + int max_skill_value; + int min_aa_points; + int max_aa_points; + int class_id; + int gender_id; + int character_id; + int status; + int item_id; + std::string prefix; + std::string suffix; + int titleset; }; class TitleManager @@ -48,18 +48,19 @@ public: bool LoadTitles(); - EQApplicationPacket *MakeTitlesPacket(Client *c); - std::string GetPrefix(int TitleID); - std::string GetSuffix(int TitleID); - int NumberOfAvailableTitles(Client *c); - bool IsClientEligibleForTitle(Client *c, std::vector::iterator Title); - bool IsNewAATitleAvailable(int AAPoints, int Class); - bool IsNewTradeSkillTitleAvailable(int SkillID, int SkillValue); - void CreateNewPlayerTitle(Client *c, const char *Title); - void CreateNewPlayerSuffix(Client *c, const char *Suffix); + EQApplicationPacket *MakeTitlesPacket(Client *client); + std::string GetPrefix(int title_id); + std::string GetSuffix(int title_id); + int NumberOfAvailableTitles(Client *client); + bool IsClientEligibleForTitle(Client *client, TitleEntry title); + bool IsNewAATitleAvailable(int aa_points, int class_id); + bool IsNewTradeSkillTitleAvailable(int skill_id, int skill_value); + void CreateNewPlayerTitle(Client *client, const char *title); + void CreateNewPlayerSuffix(Client *client, const char *suffix); + bool HasTitle(Client* client, uint32 title_id); protected: - std::vector Titles; + std::vector titles; }; extern TitleManager title_manager; From 00c41dda8c27cc02e139546d26f203a608c1de5f Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 2 Feb 2022 21:43:17 -0500 Subject: [PATCH 585/624] [Spells] Support for 'HateAdded' spell field to apply negative values to reduce hate. (#1953) * HateAdded field can be negative * [Spells] Support for 'HateAdded' spell field to apply negative values to reduce hate. --- zone/aggro.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 3d706efde..660bc95c9 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -1319,10 +1319,10 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc) if (dispel && target && target->GetHateAmount(this) < 100) AggroAmount += 50; - if (spells[spell_id].hate_added > 0) // overrides the hate (ex. tash) + if (spells[spell_id].hate_added != 0) // overrides the hate (ex. tash), can be negative. AggroAmount = spells[spell_id].hate_added; - if (GetOwner() && IsPet()) + if (GetOwner() && IsPet() && AggroAmount > 0) AggroAmount = AggroAmount * RuleI(Aggro, PetSpellAggroMod) / 100; // hate focus ignored on first action for some reason From e9f48d5fba9d44fe2c904b38682ceb49ba3fcf8e Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 3 Feb 2022 16:25:37 -0500 Subject: [PATCH 586/624] [Commands] Cleanup #npceditmass command. (#1957) * [Commands] Cleanup #npceditmass command. - Cleanup messages and logic. - Fix crash with SQL format. * Message change. --- zone/gm_commands/npceditmass.cpp | 122 +++++++++++++++++++++---------- 1 file changed, 85 insertions(+), 37 deletions(-) diff --git a/zone/gm_commands/npceditmass.cpp b/zone/gm_commands/npceditmass.cpp index 8dcb2c338..c76f209e0 100755 --- a/zone/gm_commands/npceditmass.cpp +++ b/zone/gm_commands/npceditmass.cpp @@ -22,29 +22,34 @@ void command_npceditmass(Client *c, const Seperator *sep) ); std::string search_column, search_value, change_column, change_value; + if (sep->arg[1]) { search_column = sep->arg[1]; } + if (sep->arg[2]) { search_value = sep->arg[2]; } + if (sep->arg[3]) { change_column = sep->arg[3]; } + if (sep->arg[4]) { change_value = sep->arg[4]; } bool valid_change_column = false; bool valid_search_column = false; - auto results = content_db.QueryDatabase(query); + auto results = content_db.QueryDatabase(query); std::vector possible_column_options; - for (auto row = results.begin(); row != results.end(); ++row) { + for (auto row : results) { if (row[0] == change_column) { valid_change_column = true; } + if (row[0] == search_column) { valid_search_column = true; } @@ -55,40 +60,58 @@ void command_npceditmass(Client *c, const Seperator *sep) std::string options_glue = ", "; if (!valid_search_column) { - c->Message(Chat::Red, "You must specify a valid search column. [%s] is not valid", search_column.c_str()); - c->Message(Chat::Yellow, "Possible columns [%s]", implode(options_glue, possible_column_options).c_str()); + c->Message( + Chat::Red, + fmt::format( + "You must specify a valid search column. [{}] is not valid", + search_column + ).c_str() + ); + + c->Message( + Chat::Yellow, + fmt::format( + "Possible columns [{}]", + implode(options_glue, possible_column_options) + ).c_str() + ); return; } if (!valid_change_column) { - c->Message(Chat::Red, "You must specify a valid change column. [%s] is not valid", change_column.c_str()); - c->Message(Chat::Yellow, "Possible columns [%s]", implode(options_glue, possible_column_options).c_str()); + c->Message( + Chat::Red, + fmt::format( + "You must specify a valid change column. [{}] is not valid", + change_column + ).c_str() + ); + + c->Message( + Chat::Yellow, + fmt::format( + "Possible columns [{}]", + implode(options_glue, possible_column_options) + ).c_str() + ); return; } if (!valid_search_column || !valid_change_column) { - c->Message(Chat::Red, "One requested column is invalid"); + c->Message(Chat::Red, "One requested column is invalid."); return; } query = fmt::format( SQL( - select - id, - name, - { 0 }, - { 1 } - from - npc_types - where - id IN( - select - spawnentry.npcID - from - spawnentry - join spawn2 on spawn2.spawngroupID = spawnentry.spawngroupID - where - spawn2.zone = '{2}' and spawn2.version = {3} + SELECT id, name, {}, {} + FROM npc_types + WHERE id IN( + SELECT spawnentry.npcID + FROM spawnentry + JOIN spawn2 + ON spawn2.spawngroupID = spawnentry.spawngroupID + WHERE spawn2.zone = '{}' AND spawn2.version = {} ) ), search_column, @@ -99,7 +122,7 @@ void command_npceditmass(Client *c, const Seperator *sep) std::string status = "(Searching)"; - if (strcasecmp(sep->arg[5], "apply") == 0) { + if (!strcasecmp(sep->arg[5], "apply")) { status = "(Applying)"; } @@ -113,15 +136,14 @@ void command_npceditmass(Client *c, const Seperator *sep) int found_count = 0; results = content_db.QueryDatabase(query); - for (auto row = results.begin(); row != results.end(); ++row) { - - std::string npc_id = row[0]; - std::string npc_name = row[1]; - std::string search_column_value = str_tolower(row[2]); + for (auto row : results) { + std::string npc_id = row[0]; + std::string npc_name = row[1]; + std::string search_column_value = str_tolower(row[2]); std::string change_column_current_value = row[3]; if (exact_match) { - if (search_column_value.compare(search_value) != 0) { + if (search_column_value.compare(search_value)) { continue; } } @@ -134,7 +156,7 @@ void command_npceditmass(Client *c, const Seperator *sep) c->Message( Chat::Yellow, fmt::format( - "NPC ({0}) [{1}] ({2}) [{3}] Current ({4}) [{5}] New [{6}] {7}", + "NPC ({}) [{}] ({}) [{}] Current ({}) [{}] New [{}] {}", npc_id, npc_name, search_column, @@ -176,18 +198,44 @@ void command_npceditmass(Client *c, const Seperator *sep) ) ); - c->Message(Chat::Yellow, "Changes applied to (%i) NPC's", found_count); + c->Message( + Chat::Yellow, + fmt::format( + "Changes applied to {} NPC{}.", + found_count, + found_count != 1 ? "s" : "" + ).c_str() + ); zone->Repop(); } else { - c->Message(Chat::Yellow, "Found (%i) NPC's that match this search...", found_count); - if (found_count > 0) { c->Message( - Chat::Yellow, "To apply these changes, click <%s> or type [%s]", - EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Apply").c_str(), - saylink.c_str() + Chat::Yellow, + fmt::format( + "{} NPC{} match your search.", + found_count, + found_count != 1 ? "s" : "" + ).c_str() ); + + c->Message( + Chat::Yellow, + fmt::format( + "Would you like to {} these changes?", + EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "apply") + ).c_str() + ); + + c->Message( + Chat::Yellow, + fmt::format( + "You can also use '{}'.", + saylink + ).c_str() + ); + } else { + c->Message(Chat::Yellow, "No NPCs match your search."); } } } From 4e297f3d961c386ee78973f90bfadfefcf0f02a6 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 3 Feb 2022 18:52:31 -0500 Subject: [PATCH 587/624] [Commands] #ginfo Cleanup. (#1955) * [Commands] #ginfo Cleanup. - Use popup over chat messages. * Remove leader since GetLeaderName() is wrong. --- zone/gm_commands/ginfo.cpp | 95 ++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 50 deletions(-) diff --git a/zone/gm_commands/ginfo.cpp b/zone/gm_commands/ginfo.cpp index a64b11fff..65874bfd0 100755 --- a/zone/gm_commands/ginfo.cpp +++ b/zone/gm_commands/ginfo.cpp @@ -28,69 +28,64 @@ void command_ginfo(Client *c, const Seperator *sep) return; } - c->Message( - Chat::White, + std::string popup_title = fmt::format( + "Group Info for {}", + c == target ? + "Yourself" : fmt::format( - "Group Info for {} | ID: {} Members: {}", - ( - c == target ? - "Yourself" : - fmt::format( - "{} ({})", - target->GetCleanName(), - target->GetID() - ) - ), - target_group->GetID(), - target_group->GroupCount() - ).c_str() + "{} ({})", + target->GetCleanName(), + target->GetID() + ) ); + std::string popup_text = ""; + popup_text += fmt::format( + "", + target_group->GetID(), + target_group->GroupCount() + ); + popup_text += "

"; + popup_text += ""; + popup_text += ""; + popup_text += ""; + popup_text += ""; + popup_text += ""; + popup_text += ""; + popup_text += ""; + popup_text += ""; for (int group_member = 0; group_member < MAX_GROUP_MEMBERS; group_member++) { if (target_group->membername[group_member][0] == '\0') { continue; } - int member_number = (group_member + 1); bool is_assist = target_group->MemberRoles[group_member] & RoleAssist; bool is_puller = target_group->MemberRoles[group_member] & RolePuller; bool is_tank = target_group->MemberRoles[group_member] & RoleTank; - auto member_string = ( - strcmp(target_group->membername[group_member], c->GetCleanName()) ? - ( - fmt::format( - "Name: {} In Zone: {}", - target_group->membername[group_member], - target_group->members[group_member] ? "Yes" : "No" - ) - ) : - "You" - ); - c->Message( - Chat::White, - fmt::format( - "Member {} | {}", - member_number, - member_string - ).c_str() - ); - if ( - is_assist || - is_puller || - is_tank - ) { - c->Message( - Chat::White, + popup_text += fmt::format( + "", + group_member, + ( + strcmp(target_group->membername[group_member], c->GetCleanName()) ? + target_group->membername[group_member] : fmt::format( - "Member {} Roles | Assist: {} Puller: {} Tank: {}", - member_number, - is_assist ? "Yes" : "No", - is_puller ? "Yes" : "No", - is_tank ? "Yes" : "No" - ).c_str() - ); - } + "{} (You)", + target_group->membername[group_member] + ) + ), + target_group->members[group_member] ? "" : "", + is_assist ? "" : "", + is_puller ? "" : "", + is_tank ? "" : "" + ); } + + popup_text += "
Group ID{}Members{}
IndexNameIn ZoneAssistPullerTank
{}{}{}{}{}{}
"; + + c->SendPopupToClient( + popup_title.c_str(), + popup_text.c_str() + ); } From cc0371c16e281776a2d6ca4a48da9eefe18759db Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 3 Feb 2022 22:00:52 -0500 Subject: [PATCH 588/624] [Spells] Swarm pet aggro logic fix (#1956) * temp commit * swarm pet logic fix * [Spells] Swarm pet aggro logic fix --- zone/attack.cpp | 14 ++++++++++---- zone/entity.cpp | 31 ++++++++++++++++++++++++++++++- zone/entity.h | 1 + zone/mob_ai.cpp | 2 +- 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 122f43d31..fee227e7c 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2869,7 +2869,7 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b } } //MERC - // then add pet owner if there's one + //if I am a pet, then add pet owner if there's one if (owner) { // Other is a pet, add him and it // EverHood 6/12/06 // Can't add a feigned owner to hate list @@ -2884,8 +2884,9 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b !(this->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && owner->IsClient()) && !(this->GetSpecialAbility(IMMUNE_AGGRO_NPC) && owner->IsNPC()) ) { - if (owner->IsClient() && !CheckAggro(owner)) + if (owner->IsClient() && !CheckAggro(owner)) { owner->CastToClient()->AddAutoXTarget(this); + } hate_list.AddEntToHateList(owner, 0, 0, false, !iBuffTic); } } @@ -2912,8 +2913,9 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b } } - if (other->GetTempPetCount()) { - entity_list.AddTempPetsToHateList(other, this, bFrenzy); + //I have a swarm pet, add other to it. + if (GetTempPetCount()) { + entity_list.AddTempPetsToHateList(this, other, bFrenzy); } if (!wasengaged) { @@ -3697,6 +3699,10 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const } } + if (GetTempPetCount()) { + entity_list.AddTempPetsToHateListOnOwnerDamage(this, attacker, spell_id); + } + //see if any runes want to reduce this damage if (spell_id == SPELL_UNKNOWN) { damage = ReduceDamage(damage); diff --git a/zone/entity.cpp b/zone/entity.cpp index a5f3f96ae..860331ef0 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -4208,7 +4208,7 @@ void EntityList::AddTempPetsToHateList(Mob *owner, Mob* other, bool bFrenzy) auto it = npc_list.begin(); while (it != npc_list.end()) { NPC* n = it->second; - if (n->GetSwarmInfo()) { + if (n && n->GetSwarmInfo()) { if (n->GetSwarmInfo()->owner_id == owner->GetID()) { if ( !n->GetSpecialAbility(IMMUNE_AGGRO) && @@ -4223,6 +4223,35 @@ void EntityList::AddTempPetsToHateList(Mob *owner, Mob* other, bool bFrenzy) } } +void EntityList::AddTempPetsToHateListOnOwnerDamage(Mob *owner, Mob* attacker, int32 spell_id) +{ + if (!attacker || !owner) + return; + + auto it = npc_list.begin(); + while (it != npc_list.end()) { + NPC* n = it->second; + if (n && n->GetSwarmInfo()) { + if (n->GetSwarmInfo()->owner_id == owner->GetID()) { + if ( + attacker && + attacker != n && + !n->IsEngaged() && + !n->GetSpecialAbility(IMMUNE_AGGRO) && + !(n->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && attacker->IsClient()) && + !(n->GetSpecialAbility(IMMUNE_AGGRO_NPC) && attacker->IsNPC()) && + !attacker->IsTrap() && + !attacker->IsCorpse() + ) { + n->AddToHateList(attacker, 1, 0, true, false, false, spell_id); + n->SetTarget(attacker); + } + } + } + ++it; + } +} + bool Entity::CheckCoordLosNoZLeaps(float cur_x, float cur_y, float cur_z, float trg_x, float trg_y, float trg_z, float perwalk) { diff --git a/zone/entity.h b/zone/entity.h index 4edc63b0d..54a5a2fff 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -315,6 +315,7 @@ public: void DestroyTempPets(Mob *owner); int16 CountTempPets(Mob *owner); void AddTempPetsToHateList(Mob *owner, Mob* other, bool bFrenzy = false); + void AddTempPetsToHateListOnOwnerDamage(Mob *owner, Mob* attacker, int32 spell_id); Entity *GetEntityMob(uint16 id); Entity *GetEntityMerc(uint16 id); Entity *GetEntityDoor(uint16 id); diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 1d7d9c317..491115d3c 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1349,7 +1349,7 @@ void Mob::AI_Process() { } // mob/npc waits until call for help complete, others can move else if (AI_movement_timer->Check() && target && - (GetOwnerID() || IsBot() || + (GetOwnerID() || IsBot() || IsTempPet() || CastToNPC()->GetCombatEvent())) { if (!IsRooted()) { LogAI("Pursuing [{}] while engaged", target->GetName()); From 5ce2889210ced81b2d7642456815cb4334bd4d3b Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 3 Feb 2022 22:04:15 -0500 Subject: [PATCH 589/624] [Bug Fix] Illusions will now properly display armor to other clients when they zone in. (#1958) * Fix for illusion wear change On zone in, mobs with illusions were not displaying correct armor. * [Bug Fix] Illusions will now properly display armor to other clients when they zone in better looping --- zone/bonuses.cpp | 8 ++++++++ zone/client_packet.cpp | 2 ++ zone/common.h | 1 + zone/entity.cpp | 19 +++++++++++++++++++ zone/entity.h | 1 + zone/mob.h | 1 + zone/spell_effects.cpp | 20 ++++++++++++++++++++ 7 files changed, 52 insertions(+) diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 73f388fe1..29306ebdb 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1460,6 +1460,10 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) break; } + case SE_Illusion: + newbon->Illusion = true; + break; + case SE_IllusionPersistence: newbon->IllusionPersistence = base_value; break; @@ -3541,6 +3545,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne break; } + case SE_Illusion: + new_bonus->Illusion = true; + break; + case SE_IllusionPersistence: new_bonus->IllusionPersistence = effect_value; break; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 585878092..9a532362d 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -747,6 +747,8 @@ void Client::CompleteConnect() entity_list.SendAppearanceEffects(this); + entity_list.SendIllusionWearChange(this); + entity_list.SendTraders(this); Mob *pet = GetPet(); diff --git a/zone/common.h b/zone/common.h index e79293388..648432213 100644 --- a/zone/common.h +++ b/zone/common.h @@ -559,6 +559,7 @@ struct StatBonuses { int32 WeaponStance[WEAPON_STANCE_TYPE_MAX +1];// base = trigger spell id, base2 = 0 is 2h, 1 is shield, 2 is dual wield, [0]spid 2h, [1]spid shield, [2]spid DW bool ZoneSuspendMinion; // base 1 allows suspended minions to zone bool CompleteHealBuffBlocker; // Use in SPA 101 to prevent recast of complete heal from this effect till blocker buff is removed. + bool Illusion; // check if illusion is present. // AAs int32 TrapCircumvention; // reduce chance to trigger a trap. diff --git a/zone/entity.cpp b/zone/entity.cpp index 860331ef0..ad9da930d 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -4686,6 +4686,25 @@ void EntityList::SendAppearanceEffects(Client *c) } } +void EntityList::SendIllusionWearChange(Client *c) +{ + if (!c) { + return; + } + + for (auto &e : mob_list) { + auto &mob = e.second; + + if (mob) { + if (mob == c) { + continue; + } + + mob->SendIllusionWearChange(c); + } + } +} + void EntityList::ZoneWho(Client *c, Who_All_Struct *Who) { // This is only called for SoF clients, as regular /who is now handled server-side for that client. diff --git a/zone/entity.h b/zone/entity.h index 54a5a2fff..c92a3d4f5 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -382,6 +382,7 @@ public: void SendNimbusEffects(Client *c); void SendUntargetable(Client *c); void SendAppearanceEffects(Client *c); + void SendIllusionWearChange(Client *c); void DuelMessage(Mob* winner, Mob* loser, bool flee); void QuestJournalledSayClose(Mob *sender, float dist, const char* mobname, const char* message, Journal::Options &opts); void GroupMessage(uint32 gid, const char *from, const char *message); diff --git a/zone/mob.h b/zone/mob.h index f17c5c058..2e77ef415 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -357,6 +357,7 @@ public: void ConeDirectional(uint16 spell_id, int16 resist_adjust); void TryOnSpellFinished(Mob *caster, Mob *target, uint16 spell_id); void ApplySpellEffectIllusion(int32 spell_id, Mob* caster, int buffslot, int base, int limit, int max); + void SendIllusionWearChange(Client* c); //Buff void BuffProcess(); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 51c0f99ef..e92e2cd92 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -8963,6 +8963,26 @@ void Mob::SetProcLimitTimer(int32 base_spell_id, uint32 proc_reuse_time, int pro } } +void Mob::SendIllusionWearChange(Client* c) { + + /* + We send this to client on Client::CompleteConnect() to properly update textures of + other mobs in zone with illusions on them. + */ + if (!c) { + return; + } + + if (!spellbonuses.Illusion && !itembonuses.Illusion && !aabonuses.Illusion) { + return; + } + + for (int x = EQ::textures::textureBegin; x <= EQ::textures::LastTintableTexture; x++) { + SendWearChange(x, c); + } +} + + void Mob::ApplySpellEffectIllusion(int32 spell_id, Mob *caster, int buffslot, int base, int limit, int max) { // Gender Illusions From 0400504adc6439b93cb62efa57871f6391dce286 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 4 Feb 2022 06:07:46 -0500 Subject: [PATCH 590/624] [Bug Fix] NPC::CountItem and Corpse::CountItem 0 Charge Item Fix. (#1959) - Fixes an issue where 0 charged or out of charge items do not count towards the return value. --- zone/corpse.cpp | 2 +- zone/npc.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 654aa4b00..bfedf47d1 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -1570,7 +1570,7 @@ uint16 Corpse::CountItem(uint32 item_id) { } if (loot_item->item_id == item_id) { - item_count += loot_item->charges; + item_count += loot_item->charges > 0 ? loot_item->charges : 1; } } return item_count; diff --git a/zone/npc.cpp b/zone/npc.cpp index a10713d06..5226efaa3 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -758,7 +758,7 @@ uint16 NPC::CountItem(uint32 item_id) { } if (loot_item->item_id == item_id) { - item_count += loot_item->charges; + item_count += loot_item->charges > 0 ? loot_item->charges : 1; } } return item_count; From d300e78b39b0c99ca36210246b61ee67c83c4c6c Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 4 Feb 2022 21:14:29 -0500 Subject: [PATCH 591/624] [Spells] Illusions will now persist onto the corpse when mob is killed. (#1960) * illusion applies to corpse * Update spell_effects.cpp * [Spells] Illusions will now persist onto the corpse when mob is killed. addressed comments --- zone/attack.cpp | 9 ++++++--- zone/bonuses.cpp | 2 +- zone/common.h | 2 +- zone/mob.h | 1 + zone/spell_effects.cpp | 32 ++++++++++++++++++++++++++------ 5 files changed, 35 insertions(+), 11 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index fee227e7c..a7a03127e 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1891,6 +1891,8 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQ::skills::Skill //m_epp.perAA = 0; //reset to no AA exp on death. } + int32 illusion_spell_id = spellbonuses.Illusion; + //this generates a lot of 'updates' to the client that the client does not need BuffFadeNonPersistDeath(); if (RuleB(Character, UnmemSpellsOnDeath)) { @@ -1936,13 +1938,12 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQ::skills::Skill } } } - entity_list.AddCorpse(new_corpse, GetID()); SetID(0); //send the become corpse packet to everybody else in the zone. entity_list.QueueClients(this, &app2, true); - + ApplyIllusionToCorpse(illusion_spell_id, new_corpse); LeftCorpse = true; } } @@ -2340,6 +2341,8 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy if (p_depop == true) return false; + int32 illusion_spell_id = spellbonuses.Illusion; + HasAISpellEffects = false; BuffFadeAll(); uint8 killed_level = GetLevel(); @@ -2595,8 +2598,8 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy // entity_list.RemoveMobFromCloseLists(this); close_mobs.clear(); - this->SetID(0); + ApplyIllusionToCorpse(illusion_spell_id, corpse); if (killer != 0 && emoteid != 0) corpse->CastToNPC()->DoNPCEmote(AFTERDEATH, emoteid); diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 29306ebdb..e799271aa 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -3546,7 +3546,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne } case SE_Illusion: - new_bonus->Illusion = true; + new_bonus->Illusion = spell_id; break; case SE_IllusionPersistence: diff --git a/zone/common.h b/zone/common.h index 648432213..795391ad3 100644 --- a/zone/common.h +++ b/zone/common.h @@ -559,7 +559,7 @@ struct StatBonuses { int32 WeaponStance[WEAPON_STANCE_TYPE_MAX +1];// base = trigger spell id, base2 = 0 is 2h, 1 is shield, 2 is dual wield, [0]spid 2h, [1]spid shield, [2]spid DW bool ZoneSuspendMinion; // base 1 allows suspended minions to zone bool CompleteHealBuffBlocker; // Use in SPA 101 to prevent recast of complete heal from this effect till blocker buff is removed. - bool Illusion; // check if illusion is present. + int32 Illusion; // illusion spell id // AAs int32 TrapCircumvention; // reduce chance to trigger a trap. diff --git a/zone/mob.h b/zone/mob.h index 2e77ef415..45b895dea 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -357,6 +357,7 @@ public: void ConeDirectional(uint16 spell_id, int16 resist_adjust); void TryOnSpellFinished(Mob *caster, Mob *target, uint16 spell_id); void ApplySpellEffectIllusion(int32 spell_id, Mob* caster, int buffslot, int base, int limit, int max); + void ApplyIllusionToCorpse(int32 spell_id, Corpse* new_corpse); void SendIllusionWearChange(Client* c); //Buff diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index e92e2cd92..6a945e23f 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -8982,6 +8982,24 @@ void Mob::SendIllusionWearChange(Client* c) { } } +void Mob::ApplyIllusionToCorpse(int32 spell_id, Corpse* new_corpse) { + + //Transfers most illusions over to the corpse upon death + if (!IsValidSpell(spell_id)) { + return; + } + + if (!new_corpse) { + return; + } + + for (int i = 0; i < EFFECT_COUNT; i++){ + if (spells[spell_id].effect_id[i] == SE_Illusion) { + new_corpse->ApplySpellEffectIllusion(spell_id, nullptr, -1, spells[spell_id].base_value[i], spells[spell_id].limit_value[i], spells[spell_id].max_value[i]); + return; + } + } +} void Mob::ApplySpellEffectIllusion(int32 spell_id, Mob *caster, int buffslot, int base, int limit, int max) { @@ -9091,12 +9109,14 @@ void Mob::ApplySpellEffectIllusion(int32 spell_id, Mob *caster, int buffslot, in SendWearChange(x); } - if (caster == this && spell_id != SPELL_MINOR_ILLUSION && spell_id != SPELL_ILLUSION_TREE && - (spellbonuses.IllusionPersistence || aabonuses.IllusionPersistence || itembonuses.IllusionPersistence)) { - buffs[buffslot].persistant_buff = 1; - } - else { - buffs[buffslot].persistant_buff = 0; + if (buffslot != -1) { + if (caster == this && spell_id != SPELL_MINOR_ILLUSION && spell_id != SPELL_ILLUSION_TREE && + (spellbonuses.IllusionPersistence || aabonuses.IllusionPersistence || itembonuses.IllusionPersistence)) { + buffs[buffslot].persistant_buff = 1; + } + else { + buffs[buffslot].persistant_buff = 0; + } } } From dbe6adbed0ed36235f2c17d50500a611245cc84d Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 4 Feb 2022 21:14:40 -0500 Subject: [PATCH 592/624] [Spells] SPA 311 SE_LimitCombatSkills should prevent focusing of procs even if proc is a 'casted' spell. (#1961) * proc limiter update * Update spdat.h * [Spells] SPA 311 SE_LimitCombatSkills should prevent focusing of procs even if proc is a 'casted' spell. --- common/spdat.h | 2 +- zone/spells.cpp | 40 ++++++++++++++++++---------------------- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 231953c33..b0aba49f5 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -1030,7 +1030,7 @@ typedef enum { #define SE_ZoneSuspendMinion 308 // implemented, @Pet, allow suspended pets to be resummoned upon zoning, base: 1, limit: none, max: none, Calc: Bool #define SE_GateCastersBindpoint 309 // implemented - Gate to casters bind point #define SE_ReduceReuseTimer 310 // implemented, @Fc, On Caster, spell and disc reuse time mod by amount, base: milliseconds -#define SE_LimitCombatSkills 311 // implemented, @Ff, Include or exclude combat skills or procs (non-memorizable spells) from being focused, base1: 0=Exclude if proc 1=Allow only if proc +#define SE_LimitCombatSkills 311 // implemented, @Ff, Include or exclude combat skills or procs from being focused, base1: 0=Exclude if proc 1=Allow only if proc. #define SE_Sanctuary 312 // implemented - Places caster at bottom hate list, effect fades if cast cast spell on targets other than self. #define SE_ForageAdditionalItems 313 // implemented[AA] - chance to forage additional items #define SE_Invisibility2 314 // implemented - fixed duration invisible diff --git a/zone/spells.cpp b/zone/spells.cpp index a862db9c1..ed7f27c10 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -5836,31 +5836,27 @@ bool Mob::IsCombatProc(uint16 spell_id) { if (spell_id == SPELL_UNKNOWN) { return(false); } - - if ((spells[spell_id].cast_time == 0) && (spells[spell_id].recast_time == 0) && (spells[spell_id].recovery_time == 0)) - { - - for (int i = 0; i < MAX_PROCS; i++){ - if (PermaProcs[i].spellID == spell_id || - SpellProcs[i].spellID == spell_id || - RangedProcs[i].spellID == spell_id || - DefensiveProcs[i].spellID == spell_id){ - return true; - } - } - - if (IsClient()) { - for (int i = 0; i < MAX_AA_PROCS; i += 4) { - - if (aabonuses.SpellProc[i + 1] == spell_id || - aabonuses.RangedProc[i + 1] == spell_id || - aabonuses.DefensiveProc[i + 1] == spell_id) { - return true; - } - } + /* + Procs that originate from casted spells are still limited by SPA 311 (~Kayen confirmed on live 2/4/22) + */ + for (int i = 0; i < MAX_PROCS; i++) { + if (PermaProcs[i].spellID == spell_id || + SpellProcs[i].spellID == spell_id || + RangedProcs[i].spellID == spell_id || + DefensiveProcs[i].spellID == spell_id) { + return true; } } + if (IsClient()) { + for (int i = 0; i < MAX_AA_PROCS; i += 4) { + if (aabonuses.SpellProc[i + 1] == spell_id || + aabonuses.RangedProc[i + 1] == spell_id || + aabonuses.DefensiveProc[i + 1] == spell_id) { + return true; + } + } + } return false; } From 7c20a86f23c474405575ac1625945ae56ddb7dbd Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 4 Feb 2022 21:14:53 -0500 Subject: [PATCH 593/624] escape fix for different target types (#1962) --- zone/spell_effects.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 6a945e23f..6a860c1b3 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2200,11 +2200,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove snprintf(effect_desc, _EDLEN, "Fading Memories"); #endif if(zone->random.Roll(spells[spell_id].base_value[i])) { - - if(caster && caster->IsClient()) - caster->CastToClient()->Escape(); - else - { + if (IsClient()) { + CastToClient()->Escape(); + } + else{ entity_list.RemoveFromTargets(caster); SetInvisible(Invisibility::Invisible); } @@ -3232,6 +3231,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_Ff_FocusTimerMin: case SE_Proc_Timer_Modifier: case SE_FFItemClass: + case SE_SpellEffectResistChance: { break; } From ee1f0ea91f0b174f0f25abc93ed16bbcf4b54706 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sat, 5 Feb 2022 00:34:28 -0600 Subject: [PATCH 594/624] [Maintenance Script] Pull from different maps mirror for now --- utils/scripts/eqemu_server.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl index da79fc448..22e2f1de8 100755 --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -1911,7 +1911,7 @@ sub fetch_peq_db_full sub map_files_fetch_bulk { print "[Install] Fetching Latest Maps... (This could take a few minutes...)\n"; - get_remote_file("http://github.com/Akkadius/EQEmuMaps/archive/master.zip", "maps/maps.zip", 1); + get_remote_file("http://analytics.akkadius.com/maps.zip", "maps/maps.zip", 1); unzip('maps/maps.zip', 'maps/'); my @files; my $start_dir = "maps/EQEmuMaps-master/"; From a5d8a647923806d85aff46355ffb57edf7377166 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 6 Feb 2022 13:21:48 -0500 Subject: [PATCH 595/624] [Quest API] Add inventory->CountItemEquippedByID(item_id) and inventory->HasItemEquippedByID(item_id) to Perl/Lua. (#1963) - Add $inventory->CountItemEquippedByID(item_id) to Perl. - Add $inventory->HasItemEquippedByID(item_id) to Perl. - Add inventory:CountItemEquippedByID(item_id) to Lua. - Add inventory:HasItemEquippedByID(item_id) to Lua --- common/inventory_profile.cpp | 31 ++++++++++++++++++++++++++++++ common/inventory_profile.h | 6 ++++++ zone/lua_inventory.cpp | 12 ++++++++++++ zone/lua_inventory.h | 2 ++ zone/perl_inventory.cpp | 37 ++++++++++++++++++++++++++++++++++++ 5 files changed, 88 insertions(+) diff --git a/common/inventory_profile.cpp b/common/inventory_profile.cpp index 3f4c3b3dc..fbc794351 100644 --- a/common/inventory_profile.cpp +++ b/common/inventory_profile.cpp @@ -621,6 +621,37 @@ int EQ::InventoryProfile::CountAugmentEquippedByID(uint32 item_id) return quantity; } +bool EQ::InventoryProfile::HasItemEquippedByID(uint32 item_id) +{ + bool has_equipped = false; + ItemInstance* item = nullptr; + + for (int slot_id = EQ::invslot::EQUIPMENT_BEGIN; slot_id <= EQ::invslot::EQUIPMENT_END; ++slot_id) { + item = GetItem(slot_id); + if (item && item->GetID() == item_id) { + has_equipped = true; + break; + } + } + + return has_equipped; +} + +int EQ::InventoryProfile::CountItemEquippedByID(uint32 item_id) +{ + int quantity = 0; + ItemInstance* item = nullptr; + + for (int slot_id = EQ::invslot::EQUIPMENT_BEGIN; slot_id <= EQ::invslot::EQUIPMENT_END; ++slot_id) { + item = GetItem(slot_id); + if (item && item->GetID() == item_id) { + quantity += item->IsStackable() ? item->GetCharges() : 1; + } + } + + return quantity; +} + //This function has a flaw in that it only returns the last stack that it looked at //when quantity is greater than 1 and not all of quantity can be found in 1 stack. int16 EQ::InventoryProfile::HasItem(uint32 item_id, uint8 quantity, uint8 where) diff --git a/common/inventory_profile.h b/common/inventory_profile.h index b32b028fb..c35f5679d 100644 --- a/common/inventory_profile.h +++ b/common/inventory_profile.h @@ -140,6 +140,12 @@ namespace EQ // Remove item from inventory (and take control of memory) ItemInstance* PopItem(int16 slot_id); + // Check if player has a specific item equipped by Item ID + bool HasItemEquippedByID(uint32 item_id); + + // Check how many of a specific item the player has equipped by Item ID + int CountItemEquippedByID(uint32 item_id); + // Check if player has a specific augment equipped by Item ID bool HasAugmentEquippedByID(uint32 item_id); diff --git a/zone/lua_inventory.cpp b/zone/lua_inventory.cpp index f19b9af87..9c5b9c599 100644 --- a/zone/lua_inventory.cpp +++ b/zone/lua_inventory.cpp @@ -174,6 +174,16 @@ bool Lua_Inventory::HasAugmentEquippedByID(uint32 item_id) { return self->HasAugmentEquippedByID(item_id); } +int Lua_Inventory::CountItemEquippedByID(uint32 item_id) { + Lua_Safe_Call_Int(); + return self->CountItemEquippedByID(item_id); +} + +bool Lua_Inventory::HasItemEquippedByID(uint32 item_id) { + Lua_Safe_Call_Bool(); + return self->HasItemEquippedByID(item_id); +} + luabind::scope lua_register_inventory() { return luabind::class_("Inventory") .def(luabind::constructor<>()) @@ -185,6 +195,7 @@ luabind::scope lua_register_inventory() { .def("CanItemFitInContainer", (bool(Lua_Inventory::*)(Lua_Item,Lua_Item))&Lua_Inventory::CanItemFitInContainer) .def("CheckNoDrop", (bool(Lua_Inventory::*)(int))&Lua_Inventory::CheckNoDrop) .def("CountAugmentEquippedByID", (int(Lua_Inventory::*)(uint32))&Lua_Inventory::CountAugmentEquippedByID) + .def("CountItemEquippedByID", (int(Lua_Inventory::*)(uint32))&Lua_Inventory::CountItemEquippedByID) .def("DeleteItem", (bool(Lua_Inventory::*)(int))&Lua_Inventory::DeleteItem) .def("DeleteItem", (bool(Lua_Inventory::*)(int,int))&Lua_Inventory::DeleteItem) .def("FindFreeSlot", (int(Lua_Inventory::*)(bool,bool))&Lua_Inventory::FindFreeSlot) @@ -202,6 +213,7 @@ luabind::scope lua_register_inventory() { .def("HasItemByUse", (int(Lua_Inventory::*)(int))&Lua_Inventory::HasItemByUse) .def("HasItemByUse", (int(Lua_Inventory::*)(int,uint8))&Lua_Inventory::HasItemByUse) .def("HasItemByUse", (int(Lua_Inventory::*)(int,uint8,uint8))&Lua_Inventory::HasItemByUse) + .def("HasItemEquippedByID", (bool(Lua_Inventory::*)(uint32))&Lua_Inventory::HasItemEquippedByID) .def("HasSpaceForItem", (bool(Lua_Inventory::*)(Lua_Item,int))&Lua_Inventory::HasSpaceForItem) .def("PopItem", (Lua_ItemInst(Lua_Inventory::*)(int))&Lua_Inventory::PopItem) .def("PushCursor", (int(Lua_Inventory::*)(Lua_ItemInst))&Lua_Inventory::PushCursor) diff --git a/zone/lua_inventory.h b/zone/lua_inventory.h index 33ba80b39..17966b4f4 100644 --- a/zone/lua_inventory.h +++ b/zone/lua_inventory.h @@ -44,8 +44,10 @@ public: bool DeleteItem(int slot_id, int quantity); bool CheckNoDrop(int slot_id); int CountAugmentEquippedByID(uint32 item_id); + int CountItemEquippedByID(uint32 item_id); Lua_ItemInst PopItem(int slot_id); bool HasAugmentEquippedByID(uint32 item_id); + bool HasItemEquippedByID(uint32 item_id); int HasItem(int item_id); int HasItem(int item_id, int quantity); int HasItem(int item_id, int quantity, int where); diff --git a/zone/perl_inventory.cpp b/zone/perl_inventory.cpp index 140042a47..ad3cfc08a 100644 --- a/zone/perl_inventory.cpp +++ b/zone/perl_inventory.cpp @@ -449,6 +449,41 @@ XS(XS_Inventory_CountAugmentEquippedByID) { XSRETURN(1); } +XS(XS_Inventory_HasItemEquippedByID); +XS(XS_Inventory_HasItemEquippedByID) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Inventory::HasItemEquippedByID(THIS, uint32 item_id)"); + { + EQ::InventoryProfile* THIS; + bool has_equipped = false; + uint32 item_id = (uint32) SvUV(ST(1)); + VALIDATE_THIS_IS_INVENTORY; + has_equipped = THIS->HasItemEquippedByID(item_id); + ST(0) = boolSV(has_equipped); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Inventory_CountItemEquippedByID); +XS(XS_Inventory_CountItemEquippedByID) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Inventory::CountItemEquippedByID(THIS, uint32 item_id)"); + { + EQ::InventoryProfile* THIS; + int quantity = 0; + uint32 item_id = (uint32) SvUV(ST(1)); + dXSTARG; + VALIDATE_THIS_IS_INVENTORY; + quantity = THIS->CountItemEquippedByID(item_id); + XSprePUSH; + PUSHi((IV)quantity); + } + XSRETURN(1); +} + #ifdef __cplusplus extern "C" #endif @@ -466,6 +501,7 @@ XS(boot_Inventory) { XS_VERSION_BOOTCHECK; newXSproto(strcpy(buf, "CanItemFitInContainer"), XS_Inventory_CanItemFitInContainer, file, "$$$"); newXSproto(strcpy(buf, "CountAugmentEquippedByID"), XS_Inventory_CountAugmentEquippedByID, file, "$$"); + newXSproto(strcpy(buf, "CountItemEquippedByID"), XS_Inventory_CountItemEquippedByID, file, "$$"); newXSproto(strcpy(buf, "CheckNoDrop"), XS_Inventory_CheckNoDrop, file, "$$"); newXSproto(strcpy(buf, "DeleteItem"), XS_Inventory_DeleteItem, file, "$$;$"); newXSproto(strcpy(buf, "FindFreeSlot"), XS_Inventory_FindFreeSlot, file, "$$$;$$"); @@ -479,6 +515,7 @@ XS(boot_Inventory) { newXSproto(strcpy(buf, "HasItem"), XS_Inventory_HasItem, file, "$$;$$"); newXSproto(strcpy(buf, "HasItemByLoreGroup"), XS_Inventory_HasItemByLoreGroup, file, "$$;$"); newXSproto(strcpy(buf, "HasItemByUse"), XS_Inventory_HasItemByUse, file, "$$;$$"); + newXSproto(strcpy(buf, "HasItemEquippedByID"), XS_Inventory_HasItemEquippedByID, file, "$$"); newXSproto(strcpy(buf, "HasSpaceForItem"), XS_Inventory_HasSpaceForItem, file, "$$$"); newXSproto(strcpy(buf, "PopItem"), XS_Inventory_PopItem, file, "$$"); newXSproto(strcpy(buf, "PushCursor"), XS_Inventory_PushCursor, file, "$$"); From a208801d1fc06f68a8377ae7cf26c61a1a66d7f5 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Mon, 7 Feb 2022 07:48:52 -0500 Subject: [PATCH 596/624] [Spells] Major update to Bard song pulsing, Bard item clicks while singing, and spell casting restriction logic. (#1954) * test * complete * Update effects.cpp * Update spells.cpp * Update effects.cpp * [Spells] Support for bards using Disciplines while casting or /melody. Support for spell field 'cast not standing' not allow casting from divine aura * [Spells] Support for bards using Disciplines while casting or /melody. DA bypass logic for spells with field 'cast_not_standing' * updates * stun and mez bypass * Update spdat.cpp * Update spdat.cpp * Update spells.cpp * clean * requirement messages * update * pct * save work * Reform code 1_22_22 * updated * update id to pointer * Update spells.cpp * rework 2 * update 1_23_22 * Update spells.cpp * updates * msg string works * fix disc timers not be set * more optimization * update 1_23_22 PM moved stop casting out charm and harmony moved in * update 1_25_22 rework of functions * updates 1_26_22 * remove old checks * gm override added for some * update bard AA casting checks * updates * addbuff exception for bard * debugs * charm working * update * moved skill check here * cast from item while singing * lets not attack mounts * instant cast items click * aug clicks working * aug tests Bug? Cast time not display on aug clicks for bards * aug recast from items semi ok * added item timer function * unified setting item recast timer * clean up time * update * bard AA cast updates * debugs removed * debugs removed * clean up * clean up * better placement of bindsight and numhits fix * move and rename function * Update spells.cpp * add logs * delete old DoCastingChecks * Removed old bard pulse functions * remove AEBardPulse and GroupPulse * removed Raid::GroupBardPulse * Pulse Restriction: Divine Aura * Pulse Restrictions : Fear behavior * Update spells.cpp * Update spells.cpp * [Spells] Major update to Bard song pulsing, Bard item clicks while singing, and spell casting restriction logic. bots... * [Spells] Major update to Bard song pulsing, Bard item clicks while singing, and spell casting restriction logic. added recommended isvalidspell check * [Spells] Major update to Bard song pulsing, Bard item clicks while singing, and spell casting restriction logic. merged * [Spells] Major update to Bard song pulsing, Bard item clicks while singing, and spell casting restriction logic. removed defines since we have them as constants --- common/spdat.cpp | 17 + common/spdat.h | 10 +- zone/aa.cpp | 7 +- zone/aggro.cpp | 3 + zone/api_service.cpp | 1 - zone/bot.cpp | 2 +- zone/client.h | 5 +- zone/client_packet.cpp | 57 +- zone/client_process.cpp | 4 +- zone/effects.cpp | 100 +-- zone/entity.h | 1 - zone/groups.cpp | 36 - zone/groups.h | 1 - zone/mob.cpp | 15 +- zone/mob.h | 26 +- zone/raids.cpp | 36 - zone/raids.h | 1 - zone/spell_effects.cpp | 1106 +++++++++++++++++++++++++++++- zone/spells.cpp | 1402 ++++++++++++++++++++------------------- 19 files changed, 1930 insertions(+), 900 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 486a5a499..08530df80 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1462,6 +1462,23 @@ bool IsInstrumentModAppliedToSpellEffect(int32 spell_id, int effect) //Allowing anything not confirmed to be restricted / allowed to receive modifiers, as to not inhbit anyone making custom bard songs. } +bool IsPulsingBardSong(int32 spell_id) +{ + if (!IsValidSpell(spell_id)) { + return false; + } + + if (spells[spell_id].buff_duration == 0xFFFF || + spells[spell_id].recast_time> 0 || + spells[spell_id].mana > 0 || + IsEffectInSpell(spell_id, SE_TemporaryPets) || + IsEffectInSpell(spell_id, SE_Familiar)) { + return false; + } + + return true; +} + int GetSpellStatValue(uint32 spell_id, const char* stat_identifier, uint8 slot) { if (!IsValidSpell(spell_id)) diff --git a/common/spdat.h b/common/spdat.h index b0aba49f5..7e0582f91 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -179,8 +179,6 @@ #define SPELLGROUP_FURIOUS_RAMPAGE 38106 #define SPELLGROUP_SHROUD_OF_PRAYER 41050 - - #define EFFECT_COUNT 12 #define MAX_SPELL_TRIGGER 12 // One for each slot(only 6 for AA since AA use 2) #define MAX_RESISTABLE_EFFECTS 12 // Number of effects that are typcially checked agianst resists. @@ -193,6 +191,12 @@ #define MAX_APPEARANCE_EFFECTS 20 //Up to 20 Appearance Effects can be saved to a mobs appearance effect array, these will be sent to other clients when they enter a zone (This is arbitrary) #define MAX_CAST_ON_SKILL_USE 36 //Actual amount is MAX/3 +//instrument item id's used as song components +#define INSTRUMENT_HAND_DRUM 13000 +#define INSTRUMENT_WOODEN_FLUTE 13001 +#define INSTRUMENT_LUTE 13011 +#define INSTRUMENT_HORN 13012 + const int Z_AGGRO=10; @@ -1536,9 +1540,9 @@ int GetViralMinSpreadTime(int32 spell_id); int GetViralMaxSpreadTime(int32 spell_id); int GetViralSpreadRange(int32 spell_id); bool IsInstrumentModAppliedToSpellEffect(int32 spell_id, int effect); +bool IsPulsingBardSong(int32 spell_id); uint32 GetProcLimitTimer(int32 spell_id, int proc_type); bool IgnoreCastingRestriction(int32 spell_id); - int CalcPetHp(int levelb, int classb, int STA = 75); int GetSpellEffectDescNum(uint16 spell_id); DmgShieldType GetDamageShieldType(uint16 spell_id, int32 DSType = 0); diff --git a/zone/aa.cpp b/zone/aa.cpp index 84b78d4f8..f455bfd53 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -1311,8 +1311,11 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { TogglePassiveAlternativeAdvancement(*rank, ability->id); } else { - // Bards can cast instant cast AAs while they are casting another song - if (spells[rank->spell].cast_time == 0 && GetClass() == BARD && IsBardSong(casting_spell_id)) { + // Bards can cast instant cast AAs while they are casting or channeling item cast. + if (GetClass() == BARD && IsCasting() && spells[rank->spell].cast_time == 0) { + if (!DoCastingChecksOnCaster(rank->spell)) { + return; + } if (!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQ::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].resist_difficulty, false)) { return; } diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 660bc95c9..7d3b81554 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -660,6 +660,9 @@ bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack) if (target->GetSpecialAbility(IMMUNE_DAMAGE_NPC) && IsNPC()) return false; + if (target->IsHorse()) + return false; + // can't damage own pet (applies to everthing) Mob *target_owner = target->GetOwner(); Mob *our_owner = GetOwner(); diff --git a/zone/api_service.cpp b/zone/api_service.cpp index 60f36418b..109ef3de8 100644 --- a/zone/api_service.cpp +++ b/zone/api_service.cpp @@ -436,7 +436,6 @@ Json::Value ApiGetMobListDetail(EQ::Net::WebsocketServerConnection *connection, row["cwp"] = mob->GetCWP(); row["cwpp"] = mob->GetCWPP(); row["divine_aura"] = mob->DivineAura(); - row["do_casting_checks"] = mob->DoCastingChecks(); row["dont_buff_me_before"] = mob->DontBuffMeBefore(); row["dont_cure_me_before"] = mob->DontCureMeBefore(); row["dont_dot_me_before"] = mob->DontDotMeBefore(); diff --git a/zone/bot.cpp b/zone/bot.cpp index c2b6b2d59..32599248c 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -7349,7 +7349,7 @@ void Bot::GenerateSpecialAttacks() { bool Bot::DoFinishedSpellAETarget(uint16 spell_id, Mob* spellTarget, EQ::spells::CastingSlot slot, bool& stopLogic) { if(GetClass() == BARD) { - if(!ApplyNextBardPulse(bardsong, this, bardsong_slot)) + if(!ApplyBardPulse(bardsong, this, bardsong_slot)) InterruptSpell(SONG_ENDS_ABRUPTLY, 0x121, bardsong); stopLogic = true; diff --git a/zone/client.h b/zone/client.h index 4962d1e8f..f30f9add7 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1491,7 +1491,10 @@ public: void LeaveRaidXTargets(Raid *r); bool GroupFollow(Client* inviter); inline bool GetRunMode() const { return runmode; } - void SendItemRecastTimer(uint32 recast_type, uint32 recast_delay = 0); + + void SendItemRecastTimer(int32 recast_type, uint32 recast_delay = 0); + void SetItemRecastTimer(int32 spell_id, uint32 inventory_slot); + bool HasItemRecastTimer(int32 spell_id, uint32 inventory_slot); inline bool AggroMeterAvailable() const { return ((m_ClientVersionBit & EQ::versions::maskRoF2AndLater)) && RuleB(Character, EnableAggroMeter); } // RoF untested inline void SetAggroMeterLock(int in) { m_aggrometer.set_lock_id(in); } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 9a532362d..bab86a465 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -4053,7 +4053,6 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app) { EQ::ItemInstance* p_inst = (EQ::ItemInstance*)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, slot, item->CastTime, 0, 0, castspell->inventoryslot); } @@ -8791,6 +8790,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) } spell_id = item->Click.Effect; + bool is_casting_bard_song = false; if ( @@ -8812,10 +8812,20 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) ) ) { - SendSpellBarEnable(spell_id); - return; + /* + Bards on live can click items while casting spell gems, it stops that song cast and replaces it with item click cast. + Can not click while casting other items. + */ + if (GetClass() == BARD && IsCasting() && casting_spell_slot < CastingSlot::MaxGems) + { + is_casting_bard_song = true; + } + else + { + SendSpellBarEnable(spell_id); + return; + } } - // Modern clients don't require pet targeted for item clicks that are ST_Pet if (spell_id > 0 && (spells[spell_id].target_type == ST_Pet || spells[spell_id].target_type == ST_SummonedPet)) target_id = GetPetID(); @@ -8903,11 +8913,16 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) { return; } - if (i == 0) { - if (!IsCastWhileInvis(item->Click.Effect)) + if (!IsCastWhileInvis(item->Click.Effect)) { CommonBreakInvisible(); // client can't do this for us :( - CastSpell(item->Click.Effect, target_id, CastingSlot::Item, item->CastTime, 0, 0, slot_id); + } + if (GetClass() == BARD){ + DoBardCastingFromItemClick(is_casting_bard_song, item->CastTime, item->Click.Effect, target_id, CastingSlot::Item, slot_id, item->RecastType, item->RecastDelay); + } + else { + CastSpell(item->Click.Effect, target_id, CastingSlot::Item, item->CastTime, 0, 0, slot_id); + } } } else @@ -8944,9 +8959,15 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) } if (i == 0) { - if (!IsCastWhileInvis(augitem->Click.Effect)) + if (!IsCastWhileInvis(augitem->Click.Effect)) { CommonBreakInvisible(); // client can't do this for us :( - CastSpell(augitem->Click.Effect, target_id, CastingSlot::Item, augitem->CastTime, 0, 0, slot_id); + } + if (GetClass() == BARD) { + DoBardCastingFromItemClick(is_casting_bard_song, augitem->CastTime, augitem->Click.Effect, target_id, CastingSlot::Item, slot_id, augitem->RecastType, augitem->RecastDelay); + } + else { + CastSpell(augitem->Click.Effect, target_id, CastingSlot::Item, augitem->CastTime, 0, 0, slot_id); + } } } else @@ -9552,16 +9573,20 @@ 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 + if (IsBardSong(casting_spell_id) || HasActiveSong()) { + InterruptSpell(SONG_ENDS, 0x121); //Live doesn't send song end message anymore (~Kayen 1/26/22) + } + 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; + /* + 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; + */ + else{ printf("OP_ManaChange from client:\n"); DumpPacket(app); } diff --git a/zone/client_process.cpp b/zone/client_process.cpp index e96e2ac02..454787517 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -238,9 +238,9 @@ bool Client::Process() { InterruptSpell(SONG_ENDS_ABRUPTLY, 0x121, bardsong); } else { - if (!ApplyNextBardPulse(bardsong, song_target, bardsong_slot)) + if (!ApplyBardPulse(bardsong, song_target, bardsong_slot)) { InterruptSpell(SONG_ENDS_ABRUPTLY, 0x121, bardsong); - //SpellFinished(bardsong, bardsong_target, bardsong_slot, spells[bardsong].mana); + } } } diff --git a/zone/effects.cpp b/zone/effects.cpp index 80b48fc0f..24f9afd52 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -821,22 +821,24 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) { instant_recast = false; if (GetClass() == BARD && IsCasting() && spells[spell_id].cast_time == 0) { - if (DoCastingChecks(spell_id, target)) { - SpellFinished(spell_id, entity_list.GetMob(target), EQ::spells::CastingSlot::Discipline, 0, -1, spells[spell_id].resist_difficulty, false, -1, (uint32)DiscTimer, reduced_recast); + if (DoCastingChecksOnCaster(spell_id)) { + if (SpellFinished(spell_id, entity_list.GetMob(target), EQ::spells::CastingSlot::Discipline, 0, -1, spells[spell_id].resist_difficulty, false, -1, (uint32)DiscTimer, reduced_recast, false)) { + SendDisciplineTimer(spells[spell_id].timer_id, reduced_recast); + } } } else { - CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline, -1, -1, 0, -1, (uint32)DiscTimer, reduced_recast); + if (CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline, -1, -1, 0, -1, (uint32)DiscTimer, reduced_recast)) { + SendDisciplineTimer(spells[spell_id].timer_id, reduced_recast); + } } - - SendDisciplineTimer(spells[spell_id].timer_id, reduced_recast); } } if (instant_recast) { if (GetClass() == BARD && IsCasting() && spells[spell_id].cast_time == 0) { - if (DoCastingChecks(spell_id, target)) { - SpellFinished(spell_id, entity_list.GetMob(target), EQ::spells::CastingSlot::Discipline); + if (DoCastingChecksOnCaster(spell_id)) { + SpellFinished(spell_id, entity_list.GetMob(target), EQ::spells::CastingSlot::Discipline, 0, -1, spells[spell_id].resist_difficulty, false, -1, 0xFFFFFFFF, 0, false); } } else { @@ -1194,90 +1196,6 @@ void EntityList::MassGroupBuff( } } -/** - * Causes caster to hit every mob within dist range of center with a bard pulse of spell_id - * NPC spells will only affect other NPCs with compatible faction - * - * @param caster - * @param center - * @param spell_id - * @param affect_caster - */ -void EntityList::AEBardPulse( - Mob *caster, - Mob *center, - uint16 spell_id, - bool affect_caster) -{ - Mob *current_mob = nullptr; - float distance = caster->GetAOERange(spell_id); - float distance_squared = distance * distance; - bool is_detrimental_spell = IsDetrimentalSpell(spell_id); - bool is_npc = caster->IsNPC(); - - for (auto &it : entity_list.GetCloseMobList(caster, distance)) { - current_mob = it.second; - - /** - * Skip self - */ - if (current_mob == center) { - continue; - } - - if (current_mob == caster && !affect_caster) { - continue; - } - - if (DistanceSquared(center->GetPosition(), current_mob->GetPosition()) > distance_squared) { //make sure they are in range - continue; - } - - /** - * check npc->npc casting - */ - if (is_npc && current_mob->IsNPC()) { - FACTION_VALUE faction = current_mob->GetReverseFactionCon(caster); - if (is_detrimental_spell) { - //affect mobs that are on our hate list, or - //which have bad faction with us - if (!(caster->CheckAggro(current_mob) || faction == FACTION_THREATENINGLY || faction == FACTION_SCOWLS)) { - continue; - } - } - else { - //only affect mobs we would assist. - if (!(faction <= FACTION_AMIABLY)) { - continue; - } - } - } - - /** - * LOS - */ - if (is_detrimental_spell) { - if (!center->CheckLosFN(current_mob)) { - continue; - } - } - else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... - // See notes in AESpell() above for more info. - if (caster->IsAttackAllowed(current_mob, true)) { - continue; - } - if (caster->CheckAggro(current_mob)) { - continue; - } - } - - current_mob->BardPulse(spell_id, caster); - } - if (caster->IsClient()) { - caster->CastToClient()->CheckSongSkillIncrease(spell_id); - } -} - /** * Rampage - Normal and Duration rampages * NPCs handle it differently in Mob::Rampage diff --git a/zone/entity.h b/zone/entity.h index c92a3d4f5..a743a18b0 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -422,7 +422,6 @@ public: int *max_targets = nullptr ); void MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); - void AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); //trap stuff Mob* GetTrapTrigger(Trap* trap); diff --git a/zone/groups.cpp b/zone/groups.cpp index aa4fc857a..40110b10e 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -847,42 +847,6 @@ void Group::CastGroupSpell(Mob* caster, uint16 spell_id) { disbandcheck = true; } -// does the caster + group -void Group::GroupBardPulse(Mob* caster, uint16 spell_id) { - uint32 z; - float range, distance; - - if(!caster) - return; - - castspell = true; - range = caster->GetAOERange(spell_id); - - float range2 = range*range; - - for(z=0; z < MAX_GROUP_MEMBERS; z++) { - if(members[z] == caster) { - caster->BardPulse(spell_id, caster); -#ifdef GROUP_BUFF_PETS - if(caster->GetPet() && caster->HasPetAffinity() && !caster->GetPet()->IsCharmed()) - caster->BardPulse(spell_id, caster->GetPet()); -#endif - } - else if(members[z] != nullptr) - { - distance = DistanceSquared(caster->GetPosition(), members[z]->GetPosition()); - if(distance <= range2) { - members[z]->BardPulse(spell_id, caster); -#ifdef GROUP_BUFF_PETS - if(members[z]->GetPet() && members[z]->HasPetAffinity() && !members[z]->GetPet()->IsCharmed()) - members[z]->GetPet()->BardPulse(spell_id, caster); -#endif - } else - LogSpells("Group bard pulse: [{}] is out of range [{}] at distance [{}] from [{}]", members[z]->GetName(), range, distance, caster->GetName()); - } - } -} - bool Group::IsGroupMember(Mob* client) { bool Result = false; diff --git a/zone/groups.h b/zone/groups.h index 57dfdbfb6..1a90b13ae 100644 --- a/zone/groups.h +++ b/zone/groups.h @@ -71,7 +71,6 @@ public: bool IsGroup() { return true; } void SendGroupJoinOOZ(Mob* NewMember); void CastGroupSpell(Mob* caster,uint16 spellid); - void GroupBardPulse(Mob* caster,uint16 spellid); void SplitExp(uint32 exp, Mob* other); void GroupMessage(Mob* sender,uint8 language,uint8 lang_skill,const char* message); void GroupMessageString(Mob* sender, uint32 type, uint32 string_id, const char* message,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0, uint32 distance = 0); diff --git a/zone/mob.cpp b/zone/mob.cpp index d16182eae..d6f56a8db 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4465,14 +4465,15 @@ void Mob::TryTwincast(Mob *caster, Mob *target, uint32 spell_id) } //Used for effects that should occur after the completion of the spell -void Mob::TryOnSpellFinished(Mob *caster, Mob *target, uint16 spell_id) +void Mob::ApplyHealthTransferDamage(Mob *caster, Mob *target, uint16 spell_id) { if (!IsValidSpell(spell_id)) return; - /*Apply damage from Lifeburn type effects on caster at end of spell cast. - This allows for the AE spells to function without repeatedly killing caster - Damage or heal portion can be found as regular single use spell effect + /* + Apply damage from Lifeburn type effects on caster at end of spell cast. + This allows for the AE spells to function without repeatedly killing caster + Damage or heal portion can be found as regular single use spell effect */ if (IsEffectInSpell(spell_id, SE_Health_Transfer)){ for (int i = 0; i < EFFECT_COUNT; i++) { @@ -4481,10 +4482,12 @@ void Mob::TryOnSpellFinished(Mob *caster, Mob *target, uint16 spell_id) int new_hp = GetMaxHP(); new_hp -= GetMaxHP() * spells[spell_id].base_value[i] / 1000; - if (new_hp > 0) + if (new_hp > 0) { SetHP(new_hp); - else + } + else { Kill(); + } } } } diff --git a/zone/mob.h b/zone/mob.h index 45b895dea..f0af8f8b9 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -285,11 +285,6 @@ public: void SetInvisible(uint8 state); void SetMobTextureProfile(uint8 material_slot, uint16 texture, uint32 color = 0, uint32 hero_forge_model = 0); - //Song - bool UseBardSpellLogic(uint16 spell_id = 0xffff, int slot = -1); - bool ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, EQ::spells::CastingSlot slot); - void BardPulse(uint16 spell_id, Mob *caster); - //Spell void SendSpellEffect(uint32 effect_id, uint32 duration, uint32 finish_delay, bool zone_wide, uint32 unk020, bool perm_effect = false, Client *c = nullptr, uint32 caster_id = 0, uint32 target_id = 0); @@ -331,13 +326,16 @@ public: void CastedSpellFinished(uint16 spell_id, uint32 target_id, EQ::spells::CastingSlot slot, uint16 mana_used, uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0); bool SpellFinished(uint16 spell_id, Mob *target, EQ::spells::CastingSlot slot = EQ::spells::CastingSlot::Item, uint16 mana_used = 0, - uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false, int level_override = -1, uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0); + uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false, int level_override = -1, uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, bool from_casted_spell = false); void SendBeginCast(uint16 spell_id, uint32 casttime); virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar, int reflect_effectiveness = 0, bool use_resist_adjust = false, int16 resist_adjust = 0, bool isproc = false, int level_override = -1, int32 duration_override = 0); virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100, int level_override = -1, int reflect_effectiveness = 0, int32 duration_override = 0); virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, EQ::spells::CastingSlot slot, bool isproc = false); + bool DoCastingChecksOnCaster(int32 spell_id); + bool DoCastingChecksZoneRestrictions(bool check_on_casting, int32 spell_id); + bool DoCastingChecksOnTarget(bool check_on_casting, int32 spell_id, Mob* spell_target); virtual bool CheckFizzle(uint16 spell_id); virtual bool CheckSpellLevelRestriction(uint16 spell_id); virtual bool IsImmuneToSpell(uint16 spell_id, Mob *caster); @@ -345,9 +343,9 @@ public: void InterruptSpell(uint16 spellid = SPELL_UNKNOWN); void InterruptSpell(uint16, uint16, uint16 spellid = SPELL_UNKNOWN); void StopCasting(); + void StopCastSpell(int32 spell_id, bool send_spellbar_enable); inline bool IsCasting() const { return((casting_spell_id != 0)); } uint16 CastingSpellID() const { return casting_spell_id; } - bool DoCastingChecks(int32 spell_id = SPELL_UNKNOWN, uint16 target_id = 0); bool TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier); bool TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed = 1.5f); void ResourceTap(int32 damage, uint16 spell_id); @@ -355,10 +353,19 @@ public: void CalcDestFromHeading(float heading, float distance, float MaxZDiff, float StartX, float StartY, float &dX, float &dY, float &dZ); void BeamDirectional(uint16 spell_id, int16 resist_adjust); void ConeDirectional(uint16 spell_id, int16 resist_adjust); - void TryOnSpellFinished(Mob *caster, Mob *target, uint16 spell_id); + void ApplyHealthTransferDamage(Mob *caster, Mob *target, uint16 spell_id); void ApplySpellEffectIllusion(int32 spell_id, Mob* caster, int buffslot, int base, int limit, int max); void ApplyIllusionToCorpse(int32 spell_id, Corpse* new_corpse); void SendIllusionWearChange(Client* c); + + //Bard + bool ApplyBardPulse(int32 spell_id, Mob *spell_target, EQ::spells::CastingSlot slot); + bool IsActiveBardSong(int32 spell_id); + bool HasActiveSong() const { return(bardsong != 0); } + void ZeroBardPulseVars(); + void DoBardCastingFromItemClick(bool is_casting_bard_song, uint32 cast_time, int32 spell_id, uint16 target_id, EQ::spells::CastingSlot slot, uint32 item_slot, + uint32 recast_type , uint32 recast_delay); + bool UseBardSpellLogic(uint16 spell_id = 0xffff, int slot = -1); //Buff void BuffProcess(); @@ -847,6 +854,7 @@ public: int32 GetExtraSpellAmt(uint16 spell_id, int32 extra_spell_amt, int32 base_spell_dmg); void MeleeLifeTap(int32 damage); bool PassCastRestriction(int value); + void SendCastRestrictionMessage(int requirement_id, bool is_target_requirement = true, bool is_discipline = false); bool ImprovedTaunt(); bool TryRootFadeByDamage(int buffslot, Mob* attacker); float GetSlowMitigation() const { return slow_mitigation; } @@ -1126,7 +1134,6 @@ public: void InstillDoubt(Mob *who); int16 GetResist(uint8 type) const; - bool HasActiveSong() const { return(bardsong != 0); } bool Charmed() const { return typeofpet == petCharmed; } static uint32 GetLevelHP(uint8 tlevel); uint32 GetZoneID() const; //for perl @@ -1735,7 +1742,6 @@ protected: MobMovementManager *mMovementManager; private: - void _StopSong(); //this is not what you think it is Mob* target; diff --git a/zone/raids.cpp b/zone/raids.cpp index ef883b917..712d0a1d5 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -827,42 +827,6 @@ void Raid::SplitMoney(uint32 gid, uint32 copper, uint32 silver, uint32 gold, uin } } -void Raid::GroupBardPulse(Mob* caster, uint16 spellid, uint32 gid){ - uint32 z; - float range, distance; - - if(!caster) - return; - - range = caster->GetAOERange(spellid); - - float range2 = range*range; - - for(z=0; z < MAX_RAID_MEMBERS; z++) { - if(members[z].member == caster) { - caster->BardPulse(spellid, caster); -#ifdef GROUP_BUFF_PETS - if(caster->GetPet() && caster->HasPetAffinity() && !caster->GetPet()->IsCharmed()) - caster->BardPulse(spellid, caster->GetPet()); -#endif - } - else if(members[z].member != nullptr) - { - if(members[z].GroupNumber == gid){ - distance = DistanceSquared(caster->GetPosition(), members[z].member->GetPosition()); - if(distance <= range2) { - members[z].member->BardPulse(spellid, caster); -#ifdef GROUP_BUFF_PETS - if(members[z].member->GetPet() && members[z].member->HasPetAffinity() && !members[z].member->GetPet()->IsCharmed()) - members[z].member->GetPet()->BardPulse(spellid, caster); -#endif - } else - LogSpells("Group bard pulse: [{}] is out of range [{}] at distance [{}] from [{}]", members[z].member->GetName(), range, distance, caster->GetName()); - } - } - } -} - void Raid::TeleportGroup(Mob* sender, uint32 zoneID, uint16 instance_id, float x, float y, float z, float heading, uint32 gid) { for(int i = 0; i < MAX_RAID_MEMBERS; i++) diff --git a/zone/raids.h b/zone/raids.h index a04d1eb54..e1d1cadb3 100644 --- a/zone/raids.h +++ b/zone/raids.h @@ -160,7 +160,6 @@ public: void BalanceMana(int32 penalty, uint32 gid, float range = 0, Mob* caster = nullptr, int32 limit = 0); void HealGroup(uint32 heal_amt, Mob* caster, uint32 gid, float range = 0); void SplitMoney(uint32 gid, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, Client *splitter = nullptr); - void GroupBardPulse(Mob* caster, uint16 spellid, uint32 gid); void TeleportGroup(Mob* sender, uint32 zoneID, uint16 instance_id, float x, float y, float z, float heading, uint32 gid); void TeleportRaid(Mob* sender, uint32 zoneID, uint16 instance_id, float x, float y, float z, float heading); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 6a860c1b3..ff09b955d 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2834,16 +2834,20 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove } break; } - /*Calc for base1 is found in TryOnSpellFinished() due to needing to account for AOE functionality - since effect can potentially kill caster*/ + /* + Calc for base1 is found in ApplyHealthTransferDamage() due to needing to account for AOE functionality + since effect can potentially kill caster. + */ case SE_Health_Transfer: { effect_value = spells[spell_id].limit_value[i]; int32 amt = abs(caster->GetMaxHP() * effect_value / 1000); - if (effect_value < 0) + if (effect_value < 0) { Damage(caster, amt, spell_id, spell.skill, false, buffslot, false); - else + } + else { HealDamage(amt, caster); + } break; } @@ -8189,6 +8193,1098 @@ bool Mob::PassCastRestriction(int value) return false; } +void Mob::SendCastRestrictionMessage(int requirement_id, bool target_requirement, bool is_discipline) { + + if (!IsClient()) { + return; + } + /* + Most of these are the live messages that modern clients give. Current live dbstr_us type 39 + Having these messages display greatly enhances the useabllity of these fields. (CastRestriction=target, caster_requirement_id=caster) + If target_requirement is false then use the caster requirement message. + Added support for different messages for certain high yield restrictions based on if + target or caster requirements. + + */ + + std::string msg = ""; + + if (target_requirement) { + msg = "Your target does not meet the spell requirements. "; + } + else { + if (is_discipline) { + msg = "Your combat ability is interrupted. "; + } + else { + msg = "Your spell is interrupted. "; + } + } + + switch (requirement_id) + { + case IS_ANIMAL_OR_HUMANOID: + Message(Chat::Red, "%s This spell will only work on animals or humanoid creatures.", msg); + break; + case IS_DRAGON: + Message(Chat::Red, "%s This spell will only work on dragons.", msg); + break; + case IS_ANIMAL_OR_INSECT: + Message(Chat::Red, "%s This spell will only work on animals or insects.", msg); + break; + case IS_BODY_TYPE_MISC: + Message(Chat::Red, "%s This spell will only work on humanoids, lycanthropes, giants, Kael Drakkel giants, Coldain, animals, insects, constructs, dragons, Skyshrine dragons, Muramites, or creatures constructed from magic.", msg); + break; + case IS_BODY_TYPE_MISC2: + Message(Chat::Red, "%s This spell will only work on humanoids, lycanthropes, giants, Kael Drakkel giants, Coldain, animals, or insects.", msg); + break; + case IS_PLANT: + Message(Chat::Red, "%s This spell will only work on plants.", msg); + break; + case IS_GIANT: + Message(Chat::Red, "%s This spell will only work on animals.", msg); + break; + case IS_NOT_ANIMAL_OR_HUMANOID: + Message(Chat::Red, "%s This spell will only work on targets that are neither animals or humanoid.", msg); + break; + case IS_BIXIE: + Message(Chat::Red, "%s This spell will only work on bixies.", msg); + break; + case IS_HARPY: + Message(Chat::Red, "%s This spell will only work on harpies.", msg); + break; + case IS_GNOLL: + Message(Chat::Red, "%s This spell will only work on gnolls.", msg); + break; + case IS_SPORALI: + Message(Chat::Red, "%s This spell will only work on fungusoids.", msg); + break; + case IS_KOBOLD: + Message(Chat::Red, "%s This spell will only work on kobolds.", msg); + break; + case IS_FROSTCRYPT_SHADE: + Message(Chat::Red, "%s This spell will only work on undead creatures or the Shades of Frostcrypt.", msg); + break; + case IS_DRAKKIN: + Message(Chat::Red, "%s This spell will only work on Drakkin.", msg); + break; + case IS_UNDEAD_OR_VALDEHOLM_GIANT: + Message(Chat::Red, "%s This spell will only work on undead creatures or the inhabitants of Valdeholm.", msg); + break; + case IS_ANIMAL_OR_PLANT: + Message(Chat::Red, "%s This spell will only work on plants or animals.", msg); + break; + case IS_SUMMONED: + Message(Chat::Red, "%s This spell will only work on constructs, elementals, or summoned elemental minions.", msg); + break; + case IS_WIZARD_USED_ON_MAGE_FIRE_PET: + Message(Chat::Red, "%s This spell will only work on wizards.", msg); + break; + case IS_UNDEAD: + Message(Chat::Red, "%s This spell will only work on undead.", msg); + break; + case IS_NOT_UNDEAD_OR_SUMMONED_OR_VAMPIRE: + Message(Chat::Red, "%s This spell will only work on creatures that are not undead, constructs, elementals, or vampires.", msg); + break; + case IS_FAE_OR_PIXIE: + Message(Chat::Red, "%s This spell will only work on Fae or pixies.", msg); + break; + case IS_HUMANOID: + Message(Chat::Red, "%s This spell will only work on humanoids.", msg); + break; + case IS_UNDEAD_AND_HP_LESS_THAN_10_PCT: + Message(Chat::Red, "%s The Essence Extractor whirrs but does not light up.", msg); + break; + case IS_CLOCKWORK_AND_HP_LESS_THAN_45_PCT: + Message(Chat::Red, "%s This spell will only work on clockwork gnomes.", msg); + break; + case IS_WISP_AND_HP_LESS_THAN_10_PCT: + Message(Chat::Red, "%s This spell will only work on wisps at or below 10 pct of their maximum HP.", msg); + break; + case IS_CLASS_MELEE_THAT_CAN_BASH_OR_KICK_EXCEPT_BARD: + Message(Chat::Red, "%s This spell will only work on non-bard targets that can bash or kick.", msg); + break; + case IS_CLASS_PURE_MELEE: + if (target_requirement) { + Message(Chat::Red, "%s This spell will only affect melee classes (warriors, monks, rogues, and berserkers).", msg); + } + else { + Message(Chat::Red, "%s This ability can only be used by melee classes (warriors, monks, rogues, and berserkers).", msg); + } + break; + case IS_CLASS_PURE_CASTER: + if (target_requirement) { + Message(Chat::Red, "%s This spell will only affect pure caster classes (necromancers, wizards, magicians, and enchanters).", msg); + } + else { + Message(Chat::Red, "%s This ability can only be used by pure caster classes (necromancers, wizards, magicians, and enchanters).", msg); + } + break; + case IS_CLASS_HYBRID_CLASS: + if (target_requirement) { + Message(Chat::Red, "%s This spell will only affect hybrid classes (paladins, rangers, shadow knights, bards, and beastlords).", msg); + } + else { + Message(Chat::Red, "%s This ability can only be used by hybrid classes (paladins, rangers, shadow knights, bards, and beastlords).", msg); + } + break; + case IS_CLASS_WARRIOR: + if (target_requirement) { + Message(Chat::Red, "%s This spell will only affect Warriors.", msg); + } + else { + Message(Chat::Red, "%s This ability can only be used by Warriors.", msg); + } + break; + case IS_CLASS_CLERIC: + if (target_requirement) { + Message(Chat::Red, "%s This spell will only affect Clerics.", msg); + } + else { + Message(Chat::Red, "%s This ability can only be used by Clerics.", msg); + } + break; + case IS_CLASS_PALADIN: + if (target_requirement) { + Message(Chat::Red, "%s This spell will only affect Paladins.", msg); + } + else { + Message(Chat::Red, "%s This ability can only be used by Paladins.", msg); + } + break; + case IS_CLASS_RANGER: + if (target_requirement) { + Message(Chat::Red, "%s This spell will only affect Warriors.", msg); + } + else { + Message(Chat::Red, "%s This ability can only be used by Warriors.", msg); + } + break; + case IS_CLASS_SHADOWKNIGHT: + if (target_requirement) { + Message(Chat::Red, "%s This spell will only affect Shadow Knights.", msg); + } + else { + Message(Chat::Red, "%s This ability can only be used by Shadow Knights.", msg); + } + break; + case IS_CLASS_DRUID: + if (target_requirement) { + Message(Chat::Red, "%s This spell will only affect Druids.", msg); + } + else { + Message(Chat::Red, "%s This ability can only be used by Druids.", msg); + } + break; + case IS_CLASS_MONK: + if (target_requirement) { + Message(Chat::Red, "%s This spell will only affect Monks.", msg); + } + else { + Message(Chat::Red, "%s This ability can only be used by Monks.", msg); + } + break; + case IS_CLASS_BARD: + if (target_requirement) { + Message(Chat::Red, "%s This spell will only affect Bards.", msg); + } + else { + Message(Chat::Red, "%s This ability can only be used by Bards..", msg); + } + break; + case IS_CLASS_ROGUE: + if (target_requirement) { + Message(Chat::Red, "%s This spell will only affect Rogues.", msg); + } + else { + Message(Chat::Red, "%s This ability can only be used by Rogues.", msg); + } + break; + case IS_CLASS_SHAMAN: + if (target_requirement) { + Message(Chat::Red, "%s This spell will only affect Shamans.", msg); + } + else { + Message(Chat::Red, "%s This ability can only be used by Shamans.", msg); + } + break; + case IS_CLASS_NECRO: + if (target_requirement) { + Message(Chat::Red, "%s This spell will only affect Necromancers.", msg); + } + else { + Message(Chat::Red, "%s This ability can only be used by Necromancers.", msg); + } + break; + case IS_CLASS_WIZARD: + if (target_requirement) { + Message(Chat::Red, "%s This spell will only affect Wizards.", msg); + } + else { + Message(Chat::Red, "%s This ability can only be used by Wizards.", msg); + } + break; + case IS_CLASS_MAGE: + if (target_requirement) { + Message(Chat::Red, "%s This spell will only affect Magicians.", msg); + } + else { + Message(Chat::Red, "%s This ability can only be used by Magicians.", msg); + } + break; + case IS_CLASS_ENCHANTER: + if (target_requirement) { + Message(Chat::Red, "%s This spell will only affect Enchanters.", msg); + } + else { + Message(Chat::Red, "%s This ability can only be used by Enchanters.", msg); + } + break; + case IS_CLASS_BEASTLORD: + if (target_requirement) { + Message(Chat::Red, "%s This spell will only affect Beastlords.", msg); + } + else { + Message(Chat::Red, "%s This ability can only be used by Beastlords.", msg); + } + break; + case IS_CLASS_BERSERKER: + if (target_requirement) { + Message(Chat::Red, "%s This spell will only affect Berserkers.", msg); + } + else { + Message(Chat::Red, "%s This ability can only be used by Berserkers.", msg); + } + break; + case IS_CLASS_CLR_SHM_DRU: + if (target_requirement) { + Message(Chat::Red, "%s This spell will only affect priest classes (clerics, druids, and shaman).", msg); + } + else { + Message(Chat::Red, "%s This ability can only be used by priest classes (clerics, druids, and shaman).", msg); + } + break; + case IS_CLASS_NOT_WAR_PAL_SK: + if (target_requirement) { + Message(Chat::Red, "%s This spell will not affect Warriors, Paladins, or Shadow Knights.", msg); + } + else { + Message(Chat::Red, "%s This ability can only be used by Warriors, Paladins, or Shadow Knights.", msg); + } + break; + case IS_LEVEL_UNDER_100: + Message(Chat::Red, "%s This spell will not affect any target over level 100.", msg); + break; + case IS_NOT_RAID_BOSS: + Message(Chat::Red, "%s This spell will not affect raid bosses.", msg); + break; + case IS_RAID_BOSS: + Message(Chat::Red, "%s This spell will only affect raid bosses.", msg); + break; + case FRENZIED_BURNOUT_ACTIVE: + Message(Chat::Red, "%s This spell will only cast if you have Frenzied Burnout active.", msg); + break; + case FRENZIED_BURNOUT_NOT_ACTIVE: + Message(Chat::Red, "%s This spell will only cast if you do not have Frenzied Burnout active.", msg); + break; + case IS_HP_ABOVE_75_PCT: + Message(Chat::Red, "%s Your taret's HP must be at or above 75 pct of its maximum.", msg); + break; + case IS_HP_LESS_THAN_20_PCT: + Message(Chat::Red, "%s Your target's HP must be at 20 pct of its maximum or below.", msg); + break; + case IS_HP_LESS_THAN_50_PCT: + Message(Chat::Red, "%s Your target's HP must be at 50 pct of its maximum or below.", msg); + break; + case IS_HP_LESS_THAN_75_PCT: + Message(Chat::Red, "%s Your target's HP must be at 75 pct of its maximum or below.", msg); + break; + case IS_NOT_IN_COMBAT: + Message(Chat::Red, "%s This spell will only affect creatures that are not in combat.", msg); + break; + case HAS_AT_LEAST_1_PET_ON_HATELIST: + Message(Chat::Red, "%s Your target must have least 1 pet on their hate list.", msg); + break; + case HAS_AT_LEAST_2_PETS_ON_HATELIST: + Message(Chat::Red, "%s Your target must have least 2 pets on their hate list.", msg); + break; + case HAS_AT_LEAST_3_PETS_ON_HATELIST: + Message(Chat::Red, "%s Your target must have least 3 pets on their hate list.", msg); + break; + case HAS_AT_LEAST_4_PETS_ON_HATELIST: + Message(Chat::Red, "%s Your target must have least 4 pets on their hate list.", msg); + break; + case HAS_AT_LEAST_5_PETS_ON_HATELIST: + Message(Chat::Red, "%s Your target must have least 5 pets on their hate list.", msg); + break; + case HAS_AT_LEAST_6_PETS_ON_HATELIST: + Message(Chat::Red, "%s Your target must have least 6 pets on their hate list.", msg); + break; + case HAS_AT_LEAST_7_PETS_ON_HATELIST: + Message(Chat::Red, "%s Your target must have least 7 pets on their hate list.", msg); + break; + case HAS_AT_LEAST_8_PETS_ON_HATELIST: + Message(Chat::Red, "%s Your target must have least 8 pets on their hate list.", msg); + break; + case HAS_AT_LEAST_9_PETS_ON_HATELIST: + Message(Chat::Red, "%s Your target must have least 9 pets on their hate list.", msg); + break; + case HAS_AT_LEAST_10_PETS_ON_HATELIST: + Message(Chat::Red, "%s Your target must have least 10 pets on their hate list.", msg); + break; + case HAS_AT_LEAST_11_PETS_ON_HATELIST: + Message(Chat::Red, "%s Your target must have least 11 pets on their hate list.", msg); + break; + case HAS_AT_LEAST_12_PETS_ON_HATELIST: + Message(Chat::Red, "%s Your target must have least 12 pets on their hate list.", msg); + break; + case HAS_AT_LEAST_13_PETS_ON_HATELIST: + Message(Chat::Red, "%s Your target must have least 13 pets on their hate list.", msg); + break; + case HAS_AT_LEAST_14_PETS_ON_HATELIST: + Message(Chat::Red, "%s Your target must have least 14 pets on their hate list.", msg); + break; + case HAS_AT_LEAST_15_PETS_ON_HATELIST: + Message(Chat::Red, "%s Your target must have least 15 pets on their hate list.", msg); + break; + case HAS_AT_LEAST_16_PETS_ON_HATELIST: + Message(Chat::Red, "%s Your target must have least 16 pets on their hate list.", msg); + break; + case HAS_AT_LEAST_17_PETS_ON_HATELIST: + Message(Chat::Red, "%s Your target must have least 17 pets on their hate list.", msg); + break; + case HAS_AT_LEAST_18_PETS_ON_HATELIST: + Message(Chat::Red, "%s Your target must have least 18 pets on their hate list.", msg); + break; + case HAS_AT_LEAST_19_PETS_ON_HATELIST: + Message(Chat::Red, "%s Your target must have least 19 pets on their hate list.", msg); + break; + case HAS_AT_LEAST_20_PETS_ON_HATELIST: + Message(Chat::Red, "%s Your target must have least 20 pets on their hate list.", msg); + break; + case IS_HP_LESS_THAN_35_PCT: + Message(Chat::Red, "%s Your target's HP must be at 35 pct of its maximum or below.", msg); + break; + case HAS_BETWEEN_1_TO_2_PETS_ON_HATELIST: + Message(Chat::Red, "%s Your target must have between 1 and 2 pets on their hate list.", msg); + break; + case HAS_BETWEEN_3_TO_5_PETS_ON_HATELIST: + Message(Chat::Red, "%s Your target must have between 3 and 5 pets on their hate list.", msg); + break; + case HAS_BETWEEN_6_TO_9_PETS_ON_HATELIST: + Message(Chat::Red, "%s Your target must have between 6 and 9 pets on their hate list.", msg); + break; + case HAS_BETWEEN_10_TO_14_PETS_ON_HATELIST: + Message(Chat::Red, "%s Your target must have between 10 and 14 pets on their hate list.", msg); + break; + case HAS_MORE_THAN_14_PETS_ON_HATELIST: + Message(Chat::Red, "%s Your target must have between 15 or more pets on their hate list.", msg); + break; + case IS_CLASS_CHAIN_OR_PLATE: + Message(Chat::Red, "%s This spell will only affect plate or chain wearing classes.", msg); + break; + case IS_HP_BETWEEN_5_AND_9_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be between 5 pct and 9 pct of its maximum.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be between 5 pct and 9 pct of your maximum HP.", msg); + } + break; + case IS_HP_BETWEEN_10_AND_14_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be between 10 pct and 14 pct of its maximum.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be between 10 pct and 14 pct of your maximum HP.", msg); + } + break; + case IS_HP_BETWEEN_15_AND_19_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be between 15 pct and 19 pct of its maximum.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be between 15 pct and 19 pct of your maximum HP.", msg); + } + break; + case IS_HP_BETWEEN_20_AND_24_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be between 20 pct and 54 pct of its maximum.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be between 20 pct and 24 pct of your maximum HP.", msg); + } + break; + case IS_HP_BETWEEN_25_AND_29_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be between 25 pct and 29 pct of its maximum.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be between 25 pct and 29 pct of your maximum HP.", msg); + } + break; + case IS_HP_BETWEEN_30_AND_34_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be between 30 pct and 34 pct of its maximum.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be between 30 pct and 34 pct of your maximum HP.", msg); + } + break; + case IS_HP_BETWEEN_35_AND_39_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be between 35 pct and 39 pct of its maximum.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be between 35 pct and 39 pct of your maximum HP.", msg); + } + break; + case IS_HP_BETWEEN_40_AND_44_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be between 40 pct and 44 pct of its maximum.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be between 40 pct and 44 pct of your maximum HP.", msg); + } + break; + case IS_HP_BETWEEN_45_AND_49_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be between 45 pct and 49 pct of its maximum.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be between 45 pct and 49 pct of your maximum HP.", msg); + } + break; + case IS_HP_BETWEEN_50_AND_54_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be between 50 pct and 54 pct of its maximum.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be between 50 pct and 54 pct of your maximum HP.", msg); + } + break; + case IS_HP_BETWEEN_55_AND_59_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be between 55 pct and 59 pct of its maximum.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be between 55 pct and 59 pct of your maximum HP.", msg); + } + break; + case IS_HP_BETWEEN_5_AND_15_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be between 5 pct and 15 pct of its maximum.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be between 5 pct and 15 pct of your maximum HP.", msg); + } + break; + case IS_HP_BETWEEN_15_AND_25_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be between 15 pct and 25 pct of its maximum.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be between 15 pct and 25 pct of your maximum HP.", msg); + } + break; + case IS_HP_BETWEEN_1_AND_25_PCT: + Message(Chat::Red, "%s Your target's HP must be at 25 pct of its maximum or below.", msg); + break; + case IS_HP_BETWEEN_25_AND_35_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be between 25 pct and 35 pct of its maximum.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be between 25 pct and 35 pct of your maximum HP.", msg); + } + break; + case IS_HP_BETWEEN_35_AND_45_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be between 35 pct and 45 pct of its maximum.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be between 35 pct and 45 pct of your maximum HP.", msg); + } + break; + case IS_HP_BETWEEN_45_AND_55_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be between 45 pct and 55 pct of its maximum.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be between 45 pct and 55 pct of your maximum HP.", msg); + } + break; + case IS_HP_BETWEEN_55_AND_65_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be between 55 pct and 65 pct of its maximum.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be between 55 pct and 65 pct of your maximum HP.", msg); + } + break; + case IS_HP_BETWEEN_65_AND_75_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be between 65 pct and 75 pct of its maximum.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be between 65 pct and 75 pct of your maximum HP.", msg); + } + break; + case IS_HP_BETWEEN_75_AND_85_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be between 75 pct and 85 pct of its maximum.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be between 75 pct and 85 pct of your maximum HP.", msg); + } + break; + case IS_HP_BETWEEN_85_AND_95_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be between 85 pct and 95 pct of its maximum.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be between 85 pct and 95 pct of your maximum HP.", msg); + } + break; + case IS_HP_ABOVE_45_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be at least 45 pct of its maximum.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be at least 45 pct of your maximum HP.", msg); + } + break; + case IS_HP_ABOVE_55_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be at least 55 pct of its maximum.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be at least 55 pct of your maximum HP.", msg); + } + break; + case UNKNOWN_TOO_MUCH_HP_410: + Message(Chat::Red, "%s Your target has too much HP to be affected by this spell.", msg); + break; + case UNKNOWN_TOO_MUCH_HP_411: + Message(Chat::Red, "%s Your target has too much HP to be affected by this spell.", msg); + break; + case IS_HP_ABOVE_99_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be at or above 99 pct of its maximum.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be at or above 99 pct of your maximum HP.", msg); + } + break; + case IS_MANA_ABOVE_10_PCT: + Message(Chat::Red, "%s You must have at least 10 pct of your maximum mana available to cast this spell.", msg); + break; + case IS_HP_BELOW_5_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be at 5 pct of its maximum or below.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be at or below 5 pct of your maximum HP.", msg); + } + break; + case IS_HP_BELOW_10_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be at 10 pct of its maximum or below.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be at or below 10 pct of your maximum HP.", msg); + } + break; + case IS_HP_BELOW_15_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be at 15 pct of its maximum or below.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be at or below 15 pct of your maximum HP.", msg); + } + break; + case IS_HP_BELOW_20_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be at 20 pct of its maximum or below.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be at or below 20 pct of your maximum HP.", msg); + } + break; + case IS_HP_BELOW_25_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be at 25 pct of its maximum or below.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be at or below 25 pct of your maximum HP.", msg); + } + break; + case IS_HP_BELOW_30_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be at 30 pct of its maximum or below.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be at or below 30 pct of your maximum HP.", msg); + } + break; + case IS_HP_BELOW_35_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be at 35 pct of its maximum or below.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be at or below 35 pct of your maximum HP.", msg); + } + break; + case IS_HP_BELOW_40_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be at 40 pct of its maximum or below.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be at or below 40 pct of your maximum HP.", msg); + } + break; + case IS_HP_BELOW_45_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be at 45 pct of its maximum or below.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be at or below 45 pct of your maximum HP.", msg); + } + break; + case IS_HP_BELOW_50_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be at 50 pct of its maximum or below.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be at or below 50 pct of your maximum HP.", msg); + } + break; + case IS_HP_BELOW_55_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be at 55 pct of its maximum or below.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be at or below 55 pct of your maximum HP.", msg); + } + break; + case IS_HP_BELOW_60_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be at 60 pct of its maximum or below.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be at or below 60 pct of your maximum HP.", msg); + } + break; + case IS_HP_BELOW_65_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be at 65 pct of its maximum or below.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be at or below 65 pct of your maximum HP.", msg); + } + break; + case IS_HP_BELOW_70_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be at 70 pct of its maximum or below.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be at or below 70 pct of your maximum HP.", msg); + } + break; + case IS_HP_BELOW_75_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be at 75 pct of its maximum or below.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be at or below 75 pct of your maximum HP.", msg); + } + break; + case IS_HP_BELOW_80_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be at 80 pct of its maximum or below.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be at or below 80 pct of your maximum HP.", msg); + } + break; + case IS_HP_BELOW_85_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be at 85 pct of its maximum or below.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be at or below 85 pct of your maximum HP.", msg); + } + break; + case IS_HP_BELOW_90_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be at 90 pct of its maximum or below.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be at or below 90 pct of your maximum HP.", msg); + } + break; + case IS_HP_BELOW_95_PCT: + if (target_requirement) { + Message(Chat::Red, "%s Your target's HP must be at 95 pct of its maximum or below.", msg); + } + else { + Message(Chat::Red, "%s This ability requires you to be at or below 95 pct of your maximum HP.", msg); + } + break; + case IS_ENDURANCE_BELOW_40_PCT: + Message(Chat::Red, "%s This ability requires you to be at or below 40 pct of your maximum endurance.", msg); + break; + case IS_MANA_BELOW_40_PCT: + Message(Chat::Red, "%s This spells requires you to be at or below 40 pct of your maximum mana.", msg); + break; + case IS_HP_ABOVE_20_PCT: + Message(Chat::Red, "%s Your target's HP must be at least 21 pct of its maximum.", msg); + break; + case IS_BODY_TYPE_UNDEFINED: + Message(Chat::Red, "%s This spell will only work on creatures with an undefined body type.", msg); + break; + case IS_BODY_TYPE_HUMANOID: + Message(Chat::Red, "%s This spell will only work on humanoid creatures.", msg); + break; + case IS_BODY_TYPE_WEREWOLF: + Message(Chat::Red, "%s This spell will only work on lycanthrope creatures.", msg); + break; + case IS_BODY_TYPE_UNDEAD: + Message(Chat::Red, "%s This spell will only work on undead creatures.", msg); + break; + case IS_BODY_TYPE_GIANTS: + Message(Chat::Red, "%s This spell will only work on giants.", msg); + break; + case IS_BODY_TYPE_CONSTRUCTS: + Message(Chat::Red, "%s This spell will only work on constructs.", msg); + break; + case IS_BODY_TYPE_EXTRAPLANAR: + Message(Chat::Red, "%s This spell will only work on extraplanar creatures.", msg); + break; + case IS_BODY_TYPE_MAGICAL_CREATURE: + Message(Chat::Red, "%s This spell will only work on creatures constructed from magic.", msg); + break; + case IS_BODY_TYPE_UNDEADPET: + Message(Chat::Red, "%s This spell will only work on animated undead servants.", msg); + break; + case IS_BODY_TYPE_KAELGIANT: + Message(Chat::Red, "%s This spell will only work on the Giants of Kael Drakkal.", msg); + break; + case IS_BODY_TYPE_COLDAIN: + Message(Chat::Red, "%s This spell will only work on Coldain Dwarves.", msg); + break; + case IS_BODY_TYPE_VAMPIRE: + Message(Chat::Red, "%s This spell will only work on vampires.", msg); + break; + case IS_BODY_TYPE_ATEN_HA_RA: + Message(Chat::Red, "%s This spell will only work on Aten Ha Ra.", msg); + break; + case IS_BODY_TYPE_GREATER_AHKEVANS: + Message(Chat::Red, "%s This spell will only work on Greater Ahkevans.", msg); + break; + case IS_BODY_TYPE_KHATI_SHA: + Message(Chat::Red, "%s This spell will only work on Khati Sha.", msg); + break; + case IS_BODY_TYPE_LORD_INQUISITOR_SERU: + Message(Chat::Red, "%s This spell will only work on Lord Inquisitor Seru.", msg); + break; + case IS_BODY_TYPE_GRIEG_VENEFICUS: + Message(Chat::Red, "%s This spell will only work on Grieg Veneficus.", msg); + break; + case IS_BODY_TYPE_FROM_PLANE_OF_WAR: + Message(Chat::Red, "%s This spell will only work on creatures from the Plane of War.", msg); + break; + case IS_BODY_TYPE_LUGGALD: + Message(Chat::Red, "%s This spell will only work on Luggalds.", msg); + break; + case IS_BODY_TYPE_ANIMAL: + Message(Chat::Red, "%s This spell will only work on animals.", msg); + break; + case IS_BODY_TYPE_INSECT: + Message(Chat::Red, "%s This spell will only work on insects.", msg); + break; + case IS_BODY_TYPE_MONSTER: + Message(Chat::Red, "%s This spell will only work on monsters.", msg); + break; + case IS_BODY_TYPE_ELEMENTAL: + Message(Chat::Red, "%s This spell will only work on elemental creatures.", msg); + break; + case IS_BODY_TYPE_PLANT: + Message(Chat::Red, "%s This spell will only work on plants.", msg); + break; + case IS_BODY_TYPE_DRAGON2: + Message(Chat::Red, "%s This spell will only work on dragons.", msg); + break; + case IS_BODY_TYPE_SUMMONED_ELEMENTAL: + Message(Chat::Red, "%s This spell will only work on summoned elementals.", msg); + break; + case IS_BODY_TYPE_DRAGON_OF_TOV: + Message(Chat::Red, "%s This spell will only work on Dragons of Veeshan's Temple.", msg); + break; + case IS_BODY_TYPE_FAMILIAR: + Message(Chat::Red, "%s This spell will only work on familiars.", msg); + break; + case IS_BODY_TYPE_MURAMITE: + Message(Chat::Red, "%s This spell will only work on Muramites.", msg); + break; + case IS_NOT_UNDEAD_OR_SUMMONED: + Message(Chat::Red, "%s This spell will only targets that are not undead or summoned.", msg); + break; + case IS_NOT_PLANT: + Message(Chat::Red, "%s This spell will not affect plants.", msg); + break; + case IS_NOT_CLIENT: + Message(Chat::Red, "%s This spell will not work on adventurers.", msg); + break; + case IS_CLIENT: + Message(Chat::Red, "%s This spell will only work on adventurers.", msg); + break; + case IS_LEVEL_ABOVE_42_AND_IS_CLIENT: + Message(Chat::Red, "%s This spell will only work on level 43 or higher adventurers.", msg); + break; + case IS_TREANT: + Message(Chat::Red, "%s This spell will only work on treants.", msg); + break; + case IS_BIXIE2: + Message(Chat::Red, "%s This spell will only work on bixies.", msg); + break; + case IS_SCARECROW: + Message(Chat::Red, "%s This spell will only work on scarecrows.", msg); + break; + case IS_VAMPIRE_OR_UNDEAD_OR_UNDEADPET: + Message(Chat::Red, "%s This spell will only work on vampires, undead, or animated undead creatures.", msg); + break; + case IS_NOT_VAMPIRE_OR_UNDEAD: + Message(Chat::Red, "%s This spell will not work on vampires or undead creatures.", msg); + break; + case IS_CLASS_KNIGHT_HYBRID_MELEE: + Message(Chat::Red, "%s This spell will only work on knights, hybrids, or melee classes.", msg); + break; + case IS_CLASS_WARRIOR_CASTER_PRIEST: + Message(Chat::Red, "%s This spell will only work on warriors, casters, or priests.", msg); + break; + case IS_END_BELOW_21_PCT: + Message(Chat::Red, "%s This ability requires you to be at or below 21 pct of your maximum endurance.", msg); + break; + case IS_END_BELOW_25_PCT: + Message(Chat::Red, "%s This ability requires you to be at or below 25 pct of your maximum endurance.", msg); + break; + case IS_END_BELOW_29_PCT: + Message(Chat::Red, "%s This ability requires you to be at or below 29 pct of your maximum endurance.", msg); + break; + case IS_HUMANOID_LEVEL_84_MAX: + Message(Chat::Red, "%s This spell will only work on humanoids up to level 84.", msg); + break; + case IS_HUMANOID_LEVEL_86_MAX: + Message(Chat::Red, "%s This spell will only work on humanoids up to level 86.", msg); + break; + case IS_HUMANOID_LEVEL_88_MAX: + Message(Chat::Red, "%s This spell will only work on humanoids up to level 88.", msg); + break; + case HAS_CRYSTALLIZED_FLAME_BUFF: + Message(Chat::Red, "%s This spell will only work on targets afflicted by Crystallized Flame.", msg); + break; + case HAS_INCENDIARY_OOZE_BUFF: + Message(Chat::Red, "%s This spell will only work on targets afflicted by Incendiary Ooze.", msg); + break; + case IS_LEVEL_90_MAX: + Message(Chat::Red, "%s This spell will ony work targets level up to level 90.", msg); + break; + case IS_LEVEL_92_MAX: + Message(Chat::Red, "%s This spell will ony work targets level up to level 92.", msg); + break; + case IS_LEVEL_94_MAX: + Message(Chat::Red, "%s This spell will ony work targets level up to level 94.", msg); + break; + case IS_LEVEL_95_MAX: + Message(Chat::Red, "%s This spell will ony work targets level up to level 95.", msg); + break; + case IS_LEVEL_97_MAX: + Message(Chat::Red, "%s This spell will ony work targets level up to level 97.", msg); + break; + case IS_LEVEL_99_MAX: + Message(Chat::Red, "%s This spell will ony work targets level up to level 99.", msg); + break; + case IS_LEVEL_100_MAX: + Message(Chat::Red, "%s This spell will ony work targets level up to level 100.", msg); + break; + case IS_LEVEL_102_MAX: + Message(Chat::Red, "%s This spell will ony work targets level up to level 102.", msg); + break; + case IS_LEVEL_104_MAX: + Message(Chat::Red, "%s This spell will ony work targets level up to level 104.", msg); + break; + case IS_LEVEL_105_MAX: + Message(Chat::Red, "%s This spell will ony work targets level up to level 105.", msg); + break; + case IS_LEVEL_107_MAX: + Message(Chat::Red, "%s This spell will ony work targets level up to level 107.", msg); + break; + case IS_LEVEL_109_MAX: + Message(Chat::Red, "%s This spell will ony work targets level up to level 109.", msg); + break; + case IS_LEVEL_110_MAX: + Message(Chat::Red, "%s This spell will ony work targets level up to level 110.", msg); + break; + case IS_LEVEL_112_MAX: + Message(Chat::Red, "%s This spell will ony work targets level up to level 112.", msg); + break; + case IS_LEVEL_114_MAX: + Message(Chat::Red, "%s This spell will ony work targets level up to level 114.", msg); + break; + case HAS_TBL_ESIANTI_ACCESS: + Message(Chat::Red, "%s This spell will only transport adventurers who have gained access to Esianti: Palace of the Winds.", msg); + break; + case IS_BETWEEN_LEVEL_1_AND_75: + Message(Chat::Red, "%s This spell will only work on targets between level 1 and 75.", msg); + break; + case IS_BETWEEN_LEVEL_76_AND_85: + Message(Chat::Red, "%s This spell will only work on targets between level 76 and 85.", msg); + break; + case IS_BETWEEN_LEVEL_86_AND_95: + Message(Chat::Red, "%s This spell will only work on targets between level 86 and 95.", msg); + break; + case IS_BETWEEN_LEVEL_96_AND_105: + Message(Chat::Red, "%s This spell will only work on targets between level 96 and 105.", msg); + break; + case IS_HP_LESS_THAN_80_PCT: + Message(Chat::Red, "%s Your target's HP must be at 80 pct of its maximum or below.", msg); + break; + case IS_LEVEL_ABOVE_34: + Message(Chat::Red, "%s Your target must be level 35 or higher.", msg); + break; + case IN_TWO_HANDED_STANCE: + Message(Chat::Red, "%s You must be in your two-handed stance to use this ability.", msg); + break; + case IN_DUAL_WIELD_HANDED_STANCE: + Message(Chat::Red, "%s You must be in your dual-wielding stance to use this ability.", msg); + break; + case IN_SHIELD_STANCE: + Message(Chat::Red, "%s You must be in your shield stance to use this ability.", msg); + break; + case NOT_IN_TWO_HANDED_STANCE: + Message(Chat::Red, "%s You may not use this ability if you are in your two-handed stance.", msg); + break; + case NOT_IN_DUAL_WIELD_HANDED_STANCE: + Message(Chat::Red, "%s You may not use this ability if you are in your dual-wielding stance.", msg); + break; + case NOT_IN_SHIELD_STANCE: + Message(Chat::Red, "%s You may not use this ability if you are in your shield stance.", msg); + break; + case LEVEL_46_MAX: + Message(Chat::Red, "%s Your target must be level 46 or below.", msg); + break; + case HAS_NO_MANA_BURN_BUFF: + Message(Chat::Red, "%s This spell will not take hold until the effects of the previous Mana Burn have expired.", msg); + break; + case IS_CLIENT_AND_MALE_PLATE_USER: + Message(Chat::Red, "%s Your target wouldn't look right as that Jann.", msg); + break; + case IS_CLEINT_AND_MALE_DRUID_ENCHANTER_MAGICIAN_NECROANCER_SHAMAN_OR_WIZARD: + Message(Chat::Red, "%s Your target wouldn't look right as that Jann.", msg); + break; + case IS_CLIENT_AND_MALE_BEASTLORD_BERSERKER_MONK_RANGER_OR_ROGUE: + Message(Chat::Red, "%s Your target wouldn't look right as that Jann.", msg); + break; + case IS_CLIENT_AND_FEMALE_PLATE_USER: + Message(Chat::Red, "%s Your target wouldn't look right as that Jann.", msg); + break; + case IS_CLIENT_AND_FEMALE_DRUID_ENCHANTER_MAGICIAN_NECROANCER_SHAMAN_OR_WIZARD: + Message(Chat::Red, "%s Your target wouldn't look right as that Jann.", msg); + break; + case IS_CLIENT_AND_FEMALE_BEASTLORD_BERSERKER_MONK_RANGER_OR_ROGUE: + Message(Chat::Red, "%s Your target wouldn't look right as that Jann.", msg); + break; + case HAS_TRAVELED_TO_STRATOS: + Message(Chat::Red, "%s You must travel to Stratos at least once before wishing to go there.", msg); + break; + case HAS_TRAVELED_TO_AALISHAI: + Message(Chat::Red, "%s You must travel to Aalishai at least once before wishing to go there.", msg); + break; + case HAS_TRAVELED_TO_MEARATS: + Message(Chat::Red, "%s You must travel to Mearatas at least once before wishing to go there.", msg); + break; + case IS_HP_ABOVE_50_PCT: + Message(Chat::Red, "%s This target must be above 50 pct of its maximum hit points.", msg); + break; + case IS_HP_UNDER_50_PCT: + Message(Chat::Red, "%s This target must be at oe below 50 pct of its maximum hit points.", msg); + break; + case IS_OFF_HAND_EQUIPED: + Message(Chat::Red, "%s You must be wielding a weapon or shield in your offhand to use this ability.", msg); + break; + case HAS_NO_PACT_OF_FATE_RECOURSE_BUFF: + Message(Chat::Red, "%s This spell will not work while Pact of Fate Recourse is active.", msg); + break; + case HAS_NO_SHROUD_OF_PRAYER_BUFF: + Message(Chat::Red, "%s Your target cannot receive another Quiet Prayer this soon.", msg); + break; + case IS_MANA_BELOW_20_PCT: + Message(Chat::Red, "%s This ability requires you to be at or below 20 pct of your maximum mana.", msg); + break; + case IS_MANA_ABOVE_50_PCT: + Message(Chat::Red, "%s This ability requires you to be at or above 50 pct of your maximum mana.", msg); + break; + case COMPLETED_ACHIEVEMENT_LEGENDARY_ANSWERER: + Message(Chat::Red, "%s You have completed Legendary Answerer.", msg); + break; + case HAS_NO_ROGUES_FURY_BUFF: + Message(Chat::Red, "%s This spell will not affect anyone that currently has Rogue's Fury active.", msg); + break; + case NOT_COMPLETED_ACHIEVEMENT_LEGENDARY_ANSWERER: + Message(Chat::Red, "%s You must complete Legendary Answerer.", msg); + break; + case IS_SUMMONED_OR_UNDEAD: + Message(Chat::Red, "%s This spell can only be used on summoned or undead.", msg); + break; + case IS_CLASS_CASTER_PRIEST: + Message(Chat::Red, "%s This ability requires you to be a caster or priest.", msg); + break; + case IS_END_OR_MANA_ABOVE_20_PCT: + Message(Chat::Red, "%s You must have at least 20 pct of your maximum mana and endurance to use this ability.", msg); + break; + case IS_END_OR_MANA_BELOW_30_PCT: + Message(Chat::Red, "%s Your target already has 30 pct or more of their maximum mana or endurance.", msg); + break; + case IS_CLASS_BARD2: + if (target_requirement) { + Message(Chat::Red, "%s This spell will only affect Bards.", msg); + } + else { + Message(Chat::Red, "%s This ability can only be used by Bards.", msg); + } + break; + case IS_NOT_CLASS_BARD: + if (target_requirement) { + Message(Chat::Red, "%s This spell can not affect Bards.", msg); + } + else { + Message(Chat::Red, "%s This ability can not be used by Bards.", msg); + } + break; + case HAS_NO_FURIOUS_RAMPAGE_BUFF: + Message(Chat::Red, "%s This ability cannot be activated while Furious Rampage is active.", msg); + break; + case IS_END_OR_MANA_BELOW_30_PCT2: + Message(Chat::Red, "%s You can only perform this solo if you have less than 30 pct mana or endurance.", msg); + break; + case HAS_NO_HARMONIOUS_PRECISION_BUFF: + Message(Chat::Red, "%s This spell will not work if you have the Harmonious Precision line active.", msg); + break; + case HAS_NO_HARMONIOUS_EXPANSE_BUFF: + Message(Chat::Red, "%s This spell will not work if you have the Harmonious Expanse line active.", msg); + break; + default: + if (target_requirement) { + Message(Chat::Red, "Your target does not meet the spell requirements.", msg); + } + else { + Message(Chat::Red, "Your spell would not take hold on your target.", msg); + } + break; + } +} + bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed) { /*For mage 'Bolt' line and other various spells. @@ -8526,7 +9622,7 @@ bool Mob::PassCharmTargetRestriction(Mob *target) { if (!target) { return false; } - + if (target->IsClient() && IsClient()) { MessageString(Chat::Red, CANNOT_AFFECT_PC); LogSpells("Spell casting canceled: Can not cast charm on a client."); diff --git a/zone/spells.cpp b/zone/spells.cpp index ed7f27c10..e4decc0f6 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -157,102 +157,52 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, uint32 aa_id) { LogSpells("CastSpell called for spell [{}] ([{}]) on entity [{}], slot [{}], time [{}], mana [{}], from item slot [{}]", - (IsValidSpell(spell_id))?spells[spell_id].name:"UNKNOWN SPELL", spell_id, target_id, static_cast(slot), cast_time, mana_cost, (item_slot==0xFFFFFFFF)?999:item_slot); + (IsValidSpell(spell_id)) ? spells[spell_id].name : "UNKNOWN SPELL", spell_id, target_id, static_cast(slot), cast_time, mana_cost, (item_slot == 0xFFFFFFFF) ? 999 : item_slot); if (casting_spell_id == spell_id) { ZeroCastingVars(); } - if - ( - !IsValidSpell(spell_id) || + + //If spell fails checks here determine if we need to send packet to client to reset spell bar. + bool send_spellbar_enable = true; + if ((item_slot != -1 && cast_time == 0) || aa_id) { + send_spellbar_enable = false; + } + + if (!IsValidSpell(spell_id) || casting_spell_id || delaytimer || - spellend_timer.Enabled() || - (IsStunned() && !IgnoreCastingRestriction(spell_id)) || - IsFeared() || - (IsMezzed() && !IgnoreCastingRestriction(spell_id)) || - (IsSilenced() && !IsDiscipline(spell_id)) || - (IsAmnesiad() && IsDiscipline(spell_id)) - ) - { - LogSpells("Spell casting canceled: not able to cast now. Valid? [{}], casting [{}], waiting? [{}], spellend? [{}], stunned? [{}], feared? [{}], mezed? [{}], silenced? [{}], amnesiad? [{}]", - IsValidSpell(spell_id), casting_spell_id, delaytimer, spellend_timer.Enabled(), IsStunned(), IsFeared(), IsMezzed(), IsSilenced(), IsAmnesiad() ); - - if (IsSilenced() && !IsDiscipline(spell_id)) { - MessageString(Chat::Red, SILENCED_STRING); - } - if (IsAmnesiad() && IsDiscipline(spell_id)) { - MessageString(Chat::Red, MELEE_SILENCE); - } - if (IsClient()) { - CastToClient()->SendSpellBarEnable(spell_id); - } - if (casting_spell_id && IsNPC()) { - CastToNPC()->AI_Event_SpellCastFinished(false, static_cast(casting_spell_slot)); - } - return(false); + spellend_timer.Enabled()) { + LogSpells("Spell casting canceled: not able to cast now. Valid? [{}], casting [{}], waiting? [{}], spellend? [{}]", + IsValidSpell(spell_id), casting_spell_id, delaytimer, spellend_timer.Enabled()); + StopCastSpell(spell_id, send_spellbar_enable); + return false; } + + if (!DoCastingChecksOnCaster(spell_id) || + !DoCastingChecksZoneRestrictions(true, spell_id) || + !DoCastingChecksOnTarget(true, spell_id, entity_list.GetMobID(target_id))) { + StopCastSpell(spell_id, send_spellbar_enable); + return false; + } + else { + casting_spell_checks = true; + } + //It appears that the Sanctuary effect is removed by a check on the client side (keep this however for redundancy) - if (spellbonuses.Sanctuary && (spells[spell_id].target_type != ST_Self && GetTarget() != this) || IsDetrimentalSpell(spell_id)) + if (spellbonuses.Sanctuary && (spells[spell_id].target_type != ST_Self && GetTarget() != this) || IsDetrimentalSpell(spell_id)) { BuffFadeByEffect(SE_Sanctuary); - - if(IsClient()){ - int chance = CastToClient()->GetFocusEffect(focusFcMute, spell_id);//Client only - - if (zone->random.Roll(chance)) { - MessageString(Chat::Red, SILENCED_STRING); - if(IsClient()) - CastToClient()->SendSpellBarEnable(spell_id); - return(false); - } - } - - if(IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()){ - MessageString(Chat::Red, SPELL_WOULDNT_HOLD); - if(IsClient()) - CastToClient()->SendSpellBarEnable(spell_id); - if(casting_spell_id && IsNPC()) - CastToNPC()->AI_Event_SpellCastFinished(false, static_cast(casting_spell_slot)); - return(false); - } - - //cannot cast under divine aura, unless spell has 'cast_not_standing' flag. - if(DivineAura() && !IgnoreCastingRestriction(spell_id)) { - LogSpells("Spell casting canceled: cannot cast while Divine Aura is in effect"); - InterruptSpell(173, 0x121, false); - if(IsClient()) { - CastToClient()->SendSpellBarEnable(spell_id); - } - return(false); } if (spellbonuses.NegateIfCombat) { BuffFadeByEffect(SE_NegateIfCombat); } - if (IsClient() && IsHarmonySpell(spell_id) && !HarmonySpellLevelCheck(spell_id, entity_list.GetMobID(target_id))) { - InterruptSpell(SPELL_NO_EFFECT, 0x121, spell_id); - return false; - } - - if (IsEffectInSpell(spell_id, SE_Charm) && !PassCharmTargetRestriction(entity_list.GetMobID(target_id))) { - bool can_send_spellbar_enable = true; - if ((item_slot != -1 && cast_time == 0) || aa_id) { - can_send_spellbar_enable = false; - } - if (can_send_spellbar_enable) { - SendSpellBarEnable(spell_id); - } - if (casting_spell_id && IsNPC()) { - CastToNPC()->AI_Event_SpellCastFinished(false, static_cast(casting_spell_slot)); - } - return false; - } - - if (HasActiveSong() && IsBardSong(spell_id)) { + //Casting a spell from an item click will also stop bard pulse. + if (HasActiveSong() && (IsBardSong(spell_id) || slot == CastingSlot::Item)) { LogSpells("Casting a new song while singing a song. Killing old song [{}]", bardsong); //Note: this does NOT tell the client - _StopSong(); + ZeroBardPulseVars(); } //Added to prevent MQ2 exploitation of equipping normally-unequippable/clickable items with effects and clicking them for benefits. @@ -442,8 +392,9 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, cast_time = GetActSpellCasttime(spell_id, cast_time); } } - else + else { orgcasttime = cast_time; + } // we checked for spells not requiring targets above if(target_id == 0) { @@ -455,83 +406,50 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, } else { InterruptSpell(0, 0, 0); //the 0 args should cause no messages } + ZeroCastingVars(); return(false); } // ok now we know the target casting_spell_targetid = target_id; - if (RuleB(Spells, InvisRequiresGroup) && IsInvisSpell(spell_id)) { - if (IsClient() && GetTarget() && GetTarget()->IsClient()) { - Client* spell_caster = this->CastToClient(); - Client* spell_target = entity_list.GetClientByID(target_id); - if (spell_target && spell_target->GetID() != GetID()) { - bool cast_failed = true; - if (spell_target->IsGrouped()) { - Group *target_group = spell_target->GetGroup(); - Group *my_group = GetGroup(); - if ( - target_group && - my_group && - (target_group->GetID() == my_group->GetID()) - ) { - cast_failed = false; - } - } else if (spell_target->IsRaidGrouped()) { - Raid *target_raid = spell_target->GetRaid(); - Raid *my_raid = GetRaid(); - if ( - target_raid && - my_raid && - (target_raid->GetGroup(spell_target) == my_raid->GetGroup(spell_caster)) - ) { - cast_failed = false; - } - } - - if (cast_failed) { - InterruptSpell(spell_id); - MessageString(Chat::Red, TARGET_GROUP_MEMBER); - return false; - } - } - } - } - // We don't get actual mana cost here, that's done when we consume the mana - if (mana_cost == -1) + if (mana_cost == -1) { mana_cost = spell.mana; + } // mana is checked for clients on the frontend. we need to recheck it for NPCs though // If you're at full mana, let it cast even if you dont have enough mana // we calculated this above, now enforce it - if(mana_cost > 0 && slot != CastingSlot::Item) - { + if(mana_cost > 0 && slot != CastingSlot::Item) { int my_curmana = GetMana(); int my_maxmana = GetMaxMana(); - if(my_curmana < mana_cost) // not enough mana - { + if(my_curmana < mana_cost) {// not enough mana //this is a special case for NPCs with no mana... - if(IsNPC() && my_curmana == my_maxmana) - { + if(IsNPC() && my_curmana == my_maxmana){ mana_cost = 0; - } else { + } + else { + //The client will prevent spell casting if insufficient mana, this is only for serverside enforcement. LogSpells("Spell Error not enough mana spell=[{}] mymana=[{}] cost=[{}]\n", spell_id, my_curmana, mana_cost); if(IsClient()) { //clients produce messages... npcs should not for this case MessageString(Chat::Red, INSUFFICIENT_MANA); InterruptSpell(); - } else { + } + else { InterruptSpell(0, 0, 0); //the 0 args should cause no messages } + ZeroCastingVars(); return(false); } } } - if(mana_cost > GetMana()) + if (mana_cost > GetMana()) { mana_cost = GetMana(); + } // we know our mana cost now casting_spell_mana = mana_cost; @@ -544,16 +462,13 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, // now tell the people in the area -- we ALWAYS want to send this, even instant cast spells. // The only time this is skipped is for NPC innate procs and weapon procs. Procs from buffs // oddly still send this. Since those cases don't reach here, we don't need to check them - if (slot != CastingSlot::Discipline) + if (slot != CastingSlot::Discipline) { SendBeginCast(spell_id, orgcasttime); + } // cast time is 0, just finish it right now and be done with it if(cast_time == 0) { - if (!DoCastingChecks()) { - StopCasting(); - return false; - } - CastedSpellFinished(spell_id, target_id, slot, mana_cost, item_slot, resist_adjust); + CastedSpellFinished(spell_id, target_id, slot, mana_cost, item_slot, resist_adjust); // return(true); } @@ -580,11 +495,6 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, MessageString(Chat::Spells, BEGINS_TO_GLOW, item->GetItem()->Name); } - if (!DoCastingChecks()) { - StopCasting(); - return false; - } - return(true); } @@ -611,77 +521,376 @@ void Mob::SendBeginCast(uint16 spell_id, uint32 casttime) safe_delete(outapp); } -/* - * Some failures should be caught before the spell finishes casting - * This is especially helpful to clients when they cast really long things - * If this passes it sets casting_spell_checks to true which is checked in - * SpellProcess(), if a situation ever arises where a spell is delayed by these - * it's probably doing something wrong. - */ +bool Mob::DoCastingChecksOnCaster(int32 spell_id) { -bool Mob::DoCastingChecks(int32 spell_id, uint16 target_id) -{ - if (!IsClient() || (IsClient() && CastToClient()->GetGM())) { - casting_spell_checks = true; - return true; - } - - bool ignore_casting_spell_checks = false; /* - If variables are passed into this function it is NOT being called from main spell process - thefore we do not want to set the 'casting_spell_checks' state keeping variable. + These are casting requirements on the CASTER that will cancel a spell before spell finishes casting or prevent spell from casting. + - caster_requirmement_id : checks specific requirements on caster (cast initiates) + - linked timer spells. (cast initiates) [cancel before begin cast message] + - must be out of combat spell field. (client blocks) + - must be in combat spell field. (client blocks) + + Always checked at the start of CastSpell. + Checked before special cases for bards casting from SpellFinished. */ - if (spell_id != SPELL_UNKNOWN || target_id) { - ignore_casting_spell_checks = true; + + /* + Cannot cast if stunned or mezzed, unless spell has 'cast_not_standing' flag. + */ + if ((IsStunned() || IsMezzed()) && !IgnoreCastingRestriction(spell_id)) { + LogSpells("Spell casting canceled [{}] : can not cast spell when stunned.", spell_id); + return false; + } + /* + Can not cast if feared. + */ + if (IsFeared()) { + LogSpells("Spell casting canceled [{}] : can not cast spell when feared.", spell_id); + return false; + } + /* + Can not cast if spell + */ + if ((IsSilenced() && !IsDiscipline(spell_id))) { + MessageString(Chat::Red, SILENCED_STRING); + LogSpells("Spell casting canceled [{}] : can not cast spell when silenced.", spell_id); + return false; + } + /* + Can not cast if discipline. + */ + if (IsAmnesiad() && IsDiscipline(spell_id)) { + MessageString(Chat::Red, MELEE_SILENCE); + LogSpells("Spell casting canceled [{}] : can not use discipline with amnesia.", spell_id); + return false; + } + /* + Cannot cast under divine aura, unless spell has 'cast_not_standing' flag. + */ + if (DivineAura() && !IgnoreCastingRestriction(spell_id)) { + LogSpells("Spell casting canceled [{}] : cannot cast while Divine Aura is in effect.", spell_id); + InterruptSpell(173, 0x121, false); //not sure we need this. + return false; + } + /* + Linked Reused Timers that are not ready + */ + if (IsClient() && spells[spell_id].timer_id > 0 && casting_spell_slot < CastingSlot::MaxGems) { + if (!CastToClient()->IsLinkedSpellReuseTimerReady(spells[spell_id].timer_id)) { + LogSpells("Spell casting canceled [{}] : linked reuse timer not ready.", spell_id); + return false; + } + } + /* + Spells that use caster_requirement_id field which requires specific conditions on caster to be met before casting. + */ + if (spells[spell_id].caster_requirement_id && !PassCastRestriction(spells[spell_id].caster_requirement_id)) { + SendCastRestrictionMessage(spells[spell_id].caster_requirement_id, false, IsDiscipline(spell_id)); + LogSpells("Spell casting canceled [{}] : caster requirement id [{}] not met.", spell_id, spells[spell_id].caster_requirement_id); + return false; + } + /* + Spells that use field can_cast_in_comabt or can_cast_out of combat restricting + caster to meet one of those conditions. If beneficial spell check casters state. + If detrimental check the targets state (done elsewhere in this function). + */ + if (!spells[spell_id].can_cast_in_combat && spells[spell_id].can_cast_out_of_combat) { + if (IsBeneficialSpell(spell_id)) { + if ((IsNPC() && IsEngaged()) || (IsClient() && CastToClient()->GetAggroCount())) { + if (IsDiscipline(spell_id)) { + MessageString(Chat::Red, NO_ABILITY_IN_COMBAT); + } + else { + MessageString(Chat::Red, NO_CAST_IN_COMBAT); + } + LogSpells("Spell casting canceled [{}] : can not use spell while in combat.", spell_id); + return false; + } + } + } + else if (spells[spell_id].can_cast_in_combat && !spells[spell_id].can_cast_out_of_combat) { + if (IsBeneficialSpell(spell_id)) { + if ((IsNPC() && !IsEngaged()) || (IsClient() && !CastToClient()->GetAggroCount())) { + if (IsDiscipline(spell_id)) { + MessageString(Chat::Red, NO_ABILITY_OUT_OF_COMBAT); + } + else { + MessageString(Chat::Red, NO_CAST_OUT_OF_COMBAT); + } + LogSpells("Spell casting canceled [{}] : can not use spell while out of combat.", spell_id); + return false; + } + } + } + /* + Focus version of Silence will prevent spell casting + */ + if (IsClient() && !IsDiscipline(spell_id)) { + int chance = CastToClient()->GetFocusEffect(focusFcMute, spell_id);//client only + if (chance && zone->random.Roll(chance)) { + MessageString(Chat::Red, SILENCED_STRING); + LogSpells("Spell casting canceled: can not cast spell when silenced from SPA 357 FcMute."); + return(false); + } } - if (spell_id == SPELL_UNKNOWN) { - spell_id = casting_spell_id; - } - if (!target_id) { - target_id = casting_spell_targetid; + return true; +} + +bool Mob::DoCastingChecksZoneRestrictions(bool check_on_casting, int32 spell_id) { + + /* + These are casting requirements determined by ZONE limiters that will cancel a spell before spell finishes casting or prevent spell from casting. + - levitate zone restriction (client blocks) [cancel before begin cast message] + - can not cast outdoor [cancels after spell finishes channeling] + + If the spell is a casted spell, check on CastSpell and ignore on SpellFinished. + If the spell is a initiated from SpellFinished, then check at start of SpellFinished. + */ + + bool ignore_if_npc_or_gm = false; + if (!IsClient() || (IsClient() && CastToClient()->GetGM())) { + ignore_if_npc_or_gm = true; } - Mob *spell_target = entity_list.GetMob(target_id); + /* + Zone ares that prevent blocked spells from being cast. + If on cast iniated then check any mob casting, if on spellfinished only check if is from client. + */ + if ((check_on_casting && !ignore_if_npc_or_gm) || (!check_on_casting && IsClient())) { + if (zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))) { + if (IsClient()) { + if (!CastToClient()->GetGM()) { + const char *msg = zone->GetSpellBlockedMessage(spell_id, glm::vec3(GetPosition())); + if (msg) { + Message(Chat::Red, msg); + return false; + } + else { + Message(Chat::Red, "You can't cast this spell here."); + return false; + } + LogSpells("Spell casting canceled [{}] : can not cast in this zone location blocked spell.", spell_id); + } + else { + LogSpells("GM Cast Blocked Spell: [{}] (ID [{}])", GetSpellName(spell_id), spell_id); + } + } + return false; + } + } + /* + Zones where you can not use levitate spells. + */ + if (!ignore_if_npc_or_gm && !zone->CanLevitate() && IsEffectInSpell(spell_id, SE_Levitate)) { //check on spellfinished. + Message(Chat::Red, "You have entered an area where levitation effects do not function."); + LogSpells("Spell casting canceled [{}] : can not cast levitation in this zone.", spell_id); + return false; + } + /* + Zones where you can not use detrimental spells. + */ + if (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) { + Message(Chat::Red, "You cannot cast detrimental spells here."); + return false; + } - if (RuleB(Spells, BuffLevelRestrictions)) { + if (check_on_casting) { + /* + Zones where you can not cast out door only spells. This is only checked when casting is completed. + */ + if (!ignore_if_npc_or_gm && spells[spell_id].zone_type == 1 && !zone->CanCastOutdoor()) { + if (IsClient() && !CastToClient()->GetGM()) { + MessageString(Chat::Red, CAST_OUTDOORS); + LogSpells("Spell casting canceled [{}] : can not cast outdoors.", spell_id); + return false; + } + } + /* + Zones where you can not gate. + */ + if (IsClient() && + (zone->GetZoneID() == Zones::TUTORIAL || zone->GetZoneID() == Zones::LOAD) && + CastToClient()->Admin() < AccountStatus::QuestTroupe) { + if (IsEffectInSpell(spell_id, SE_Gate) || + IsEffectInSpell(spell_id, SE_Translocate) || + IsEffectInSpell(spell_id, SE_Teleport)) { + Message(Chat::White, "The Gods brought you here, only they can send you away."); + return false; + } + } + } + + return true; +} + + +bool Mob::DoCastingChecksOnTarget(bool check_on_casting, int32 spell_id, Mob *spell_target) { + + /* + These are casting requirements or TARGETS that will cancel a spell before spell finishes casting or prevent spell from casting. + - cast_restriction : checks specific requirements on target (cast initiates) + - target level restriction on buffs (cast initiates) + - can not cast life tap on self (client blocks) [cancel before begin cast message] + - can not cast sacrifice on self (cast initiates) [cancel before begin cast message] + - charm restrictions (cast initiates) [cancel before begin cast message] + - pcnpc_only_flag - (client blocks] [cancel before being cast message] + + If the spell is a casted spell, check on CastSpell and ignore on SpellFinished. + If the spell is a initiated from SpellFinished, then check at start of SpellFinished. + Always check again on SpellOnTarget to account for AE checks. + */ + + + bool ignore_if_npc_or_gm = false; + if (!IsClient() || (IsClient() && CastToClient()->GetGM())) { + ignore_if_npc_or_gm = true; + } + + if (check_on_casting && !spell_target){ + + if (IsGroupSpell(spell_id) || + spells[spell_id].target_type == ST_AEClientV1 || + spells[spell_id].target_type == ST_AECaster || + spells[spell_id].target_type == ST_Ring || + spells[spell_id].target_type == ST_Beam){ + return true; + } + else if (spells[spell_id].target_type == ST_Self) { + spell_target = this; + } + } + + //If we still do not have a target end. + if (!spell_target){ + return false; + } + + /* + Spells that use caster_restriction field which requires specific conditions on target to be met before casting. + [Insufficient mana first] + */ + if (spells[spell_id].cast_restriction && !spell_target->PassCastRestriction(spells[spell_id].cast_restriction)) { + SendCastRestrictionMessage(spells[spell_id].cast_restriction, true, IsDiscipline(spell_id)); + LogSpells("Spell casting canceled [{}] : target requirement id [{}] not met.", spell_id, spells[spell_id].caster_requirement_id); + return false; + } + /* + Spells that use field can_cast_in_comabt or can_cast_out of combat restricting + caster to meet one of those conditions. If beneficial spell check casters state (done else where in this function) + if detrimental check the targets state. + */ + if (!spells[spell_id].can_cast_in_combat && spells[spell_id].can_cast_out_of_combat) { + if (IsDetrimentalSpell(spell_id)) { + if (((spell_target->IsNPC() && spell_target->IsEngaged()) || + (spell_target->IsClient() && spell_target->CastToClient()->GetAggroCount()))) { + MessageString(Chat::Red, SPELL_NO_EFFECT); // Unsure correct string + LogSpells("Spell casting canceled [{}] : can not use spell while your target is in combat.", spell_id); + return false; + } + } + } + else if (spells[spell_id].can_cast_in_combat && !spells[spell_id].can_cast_out_of_combat) { + if (IsDetrimentalSpell(spell_id)) { + if (((spell_target->IsNPC() && !spell_target->IsEngaged()) || + (spell_target->IsClient() && !spell_target->CastToClient()->GetAggroCount()))) { + MessageString(Chat::Red, SPELL_NO_EFFECT); // Unsure correct string + LogSpells("Spell casting canceled [{}] : can not use spell while your target is out of combat.", spell_id); + return false; + } + } + } + /* + Prevent buffs from being cast on targets who don't meet level restriction + */ + if (!ignore_if_npc_or_gm && RuleB(Spells, BuffLevelRestrictions)) { // casting_spell_targetid is guaranteed to be what we went, check for ST_Self for now should work though - if (spell_target && spells[spell_id].target_type != ST_Self && !spell_target->CheckSpellLevelRestriction(spell_id)) { + if (spells[spell_id].target_type != ST_Self && !spell_target->CheckSpellLevelRestriction(spell_id)) { LogSpells("Spell [{}] failed: recipient did not meet the level restrictions", spell_id); - if (!IsBardSong(spell_id)) + if (!IsBardSong(spell_id)) { MessageString(Chat::SpellFailure, SPELL_TOO_POWERFUL); + } return false; } } - - if (spells[spell_id].zone_type == 1 && !zone->CanCastOutdoor()) { - MessageString(Chat::Red, CAST_OUTDOORS); - return false; - } - - if (IsEffectInSpell(spell_id, SE_Levitate) && !zone->CanLevitate()) { - Message(Chat::Red, "You can't levitate in this zone."); - return false; - } - - if(zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))) { - const char *msg = zone->GetSpellBlockedMessage(spell_id, glm::vec3(GetPosition())); - if (msg) { - Message(Chat::Red, msg); + /* + Prevents buff from being cast based on tareget ing PC OR NPC (1 = PCs, 2 = NPCs) + These target types skip pcnpc only check (according to dev quotes) + */ + if (spells[spell_id].pcnpc_only_flag && spells[spell_id].target_type != ST_AETargetHateList && + spells[spell_id].target_type != ST_HateList) { + if (spells[spell_id].pcnpc_only_flag == 1 && !spell_target->IsClient() && !spell_target->IsMerc() && !spell_target->IsBot()) { return false; - } else { - Message(Chat::Red, "You can't cast this spell here."); + } + else if (spells[spell_id].pcnpc_only_flag == 2 && (spell_target->IsClient() || spell_target->IsMerc() || spell_target->IsBot())) { return false; } } - - if (IsClient() && spells[spell_id].timer_id > 0 && casting_spell_slot < CastingSlot::MaxGems) - if (!CastToClient()->IsLinkedSpellReuseTimerReady(spells[spell_id].timer_id)) - return false; - - if (!ignore_casting_spell_checks){ - casting_spell_checks = true; + /* + Cannot cast life tap on self + */ + if (this == spell_target && IsLifetapSpell(spell_id)) { + LogSpells("You cannot lifetap yourself"); + MessageString(Chat::SpellFailure, CANT_DRAIN_SELF); + return false; } + /* + Cannot cast sacrifice on self + */ + if (this == spell_target && IsSacrificeSpell(spell_id)) { + LogSpells("You cannot sacrifice yourself"); + MessageString(Chat::SpellFailure, CANNOT_SAC_SELF); + return false; + } + /* + Max level of target for harmony to take hold + */ + if (IsClient() && IsHarmonySpell(spell_id) && !HarmonySpellLevelCheck(spell_id, spell_target)) { + MessageString(Chat::SpellFailure, SPELL_NO_EFFECT); + LogSpells("Spell casting canceled [{}] : can not use harmony on this target.", spell_id); + return false; + } + /* + Various charm related target restrictions + */ + if (IsEffectInSpell(spell_id, SE_Charm) && !PassCharmTargetRestriction(spell_target)) { + LogSpells("Spell casting canceled [{}] : can not use charm on this target.", spell_id); + return false; + } + /* + Requires target to be in same group or same raid in order to apply invisible. + */ + if (check_on_casting && RuleB(Spells, InvisRequiresGroup) && IsInvisSpell(spell_id)) { + if (IsClient() && spell_target && spell_target->IsClient()) { + if (spell_target && spell_target->GetID() != GetID()) { + bool cast_failed = true; + if (spell_target->IsGrouped()) { + Group *target_group = spell_target->GetGroup(); + Group *my_group = GetGroup(); + if (target_group && + my_group && + (target_group->GetID() == my_group->GetID())) { + cast_failed = false; + } + } + else if (spell_target->IsRaidGrouped()) { + Raid *target_raid = spell_target->GetRaid(); + Raid *my_raid = GetRaid(); + if (target_raid && + my_raid && + (target_raid->GetGroup(spell_target->CastToClient()) == my_raid->GetGroup(this->CastToClient()))) { + cast_failed = false; + } + } + + if (cast_failed) { + MessageString(Chat::Red, TARGET_GROUP_MEMBER); + return false; + } + } + } + } + return true; } @@ -912,6 +1121,16 @@ void Mob::ZeroCastingVars() delaytimer = false; } + +//This will cause server to stop trying to pulse a bard song. Does not stop song clientside. +void Mob::ZeroBardPulseVars() +{ + bardsong = 0; + bardsong_target_id = 0; + bardsong_slot = CastingSlot::Gem1; + bardsong_timer.Disable(); +} + void Mob::InterruptSpell(uint16 spellid) { if (spellid == SPELL_UNKNOWN) @@ -951,8 +1170,9 @@ void Mob::InterruptSpell(uint16 message, uint16 color, uint16 spellid) if(!spellid) return; - if (bardsong || IsBardSong(casting_spell_id)) - _StopSong(); + if (bardsong || IsBardSong(casting_spell_id)) { + ZeroBardPulseVars(); + } if(bard_song_mode) { return; @@ -1040,6 +1260,23 @@ void Mob::StopCasting() ZeroCastingVars(); } +void Mob::StopCastSpell(int32 spell_id, bool send_spellbar_enable) +{ + /* + This is used when spells fail at CastSpell or when CastSpell is bypassed and spell is launched initially from SpellFinished. + send_spellbar_enabled is false when the following + -AA that fail at CastSpell because they never get timer set. + -Instant cast items that fail at CastSpell because they never get timer set. + */ + if (casting_spell_id && IsNPC()) { + CastToNPC()->AI_Event_SpellCastFinished(false, static_cast(casting_spell_slot)); + } + + if (send_spellbar_enable) { + SendSpellBarEnable(spell_id); + } +} + // this is called after the timer is up and the spell is finished // casting. everything goes through here, including items with zero cast time // only to be used from SpellProcess @@ -1049,6 +1286,13 @@ void Mob::StopCasting() void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slot, uint16 mana_used, uint32 inventory_slot, int16 resist_adjust) { + if (!IsValidSpell(spell_id)) + { + LogSpells("Casting of [{}] canceled: invalid spell id", spell_id); + InterruptSpell(); + return; + } + bool IsFromItem = false; EQ::ItemInstance *item = nullptr; @@ -1062,31 +1306,22 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo } } /* + Reinforcement only. Checks Item and Augment click recasts. Titanium client will prevent item recast on its own. This is only used to enforce. Titanium items are cast from Handle_OP_CastSpell. SOF+ client does not prevent item recast on its own. We enforce this in Handle_OP_ItemVerifyRequest where items are cast from. */ if(IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt)) { IsFromItem = true; - item = CastToClient()->GetInv().GetItem(inventory_slot); - if(item && item->GetItem()->RecastDelay > 0) - { - if(!CastToClient()->GetPTimers().Expired(&database, (pTimerItemStart + item->GetItem()->RecastType), false)) { - MessageString(Chat::Red, SPELL_RECAST); - LogSpells("Casting of [{}] canceled: item spell reuse timer not expired", spell_id); - StopCasting(); - return; - } + item = CastToClient()->GetInv().GetItem(inventory_slot); //checked for in reagents and charges. + if (CastToClient()->HasItemRecastTimer(spell_id, inventory_slot)) { + MessageString(Chat::Red, SPELL_RECAST); + LogSpells("Casting of [{}] canceled: item spell reuse timer not expired", spell_id); + StopCasting(); + return; } } - if(!IsValidSpell(spell_id)) - { - LogSpells("Casting of [{}] canceled: invalid spell id", spell_id); - InterruptSpell(); - return; - } - // prevent rapid recast - this can happen if somehow the spell gems // become desynced and the player casts again. if(IsClient()) @@ -1116,27 +1351,27 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo // a spell bar slot if(GetClass() == BARD) // bard's can move when casting any spell... { - if (IsBardSong(spell_id)) { - if(spells[spell_id].buff_duration == 0xFFFF) { + if (IsBardSong(spell_id) && slot < CastingSlot::MaxGems) { + if (spells[spell_id].buff_duration == 0xFFFF) { LogSpells("Bard song [{}] not applying bard logic because duration. dur=[{}], recast=[{}]", spells[spell_id].buff_duration); - } else { - // So long recast bard songs need special bard logic, although the effects don't repulse like other songs - // This is basically a hack to get that effect - // You can hold down the long recast spells, but you only get the effects once - // Songs with mana cost also do not repulse - // AAs that use SE_TemporaryPets or SE_Familiar also do not repulse - // TODO fuck bards. - if (spells[spell_id].recast_time == 0 && spells[spell_id].mana == 0 && !IsEffectInSpell(spell_id, SE_TemporaryPets) && !IsEffectInSpell(spell_id, SE_Familiar)) { + } + else { + if (IsPulsingBardSong(spell_id)) { bardsong = spell_id; bardsong_slot = slot; - //NOTE: theres a lot more target types than this to think about... - if (spell_target == nullptr || (spells[spell_id].target_type != ST_Target && spells[spell_id].target_type != ST_AETarget)) - bardsong_target_id = GetID(); - else + + if (spell_target) { bardsong_target_id = spell_target->GetID(); + } + else if (spells[spell_id].target_type != ST_Target && spells[spell_id].target_type != ST_AETarget) { + bardsong_target_id = GetID(); //This is a failsafe, you should always have a spell_target unless that target died/zoned. + } + else { + InterruptSpell(); + } bardsong_timer.Start(6000); } - LogSpells("Bard song [{}] started: slot [{}], target id [{}]", bardsong, (int) bardsong_slot, bardsong_target_id); + LogSpells("Bard song [{}] started: slot [{}], target id [{}]", bardsong, (int)bardsong_slot, bardsong_target_id); bard_song_mode = true; } } @@ -1270,7 +1505,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo continue; // no instrument required, go to next component // percussion songs (13000 = hand drum) - case 13000: + case INSTRUMENT_HAND_DRUM: if(itembonuses.percussionMod == 0) { // check for the appropriate instrument type HasInstrument = false; c->MessageString(Chat::Red, SONG_NEEDS_DRUM); // send an error message if missing @@ -1278,7 +1513,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo break; // wind songs (13001 = wooden flute) - case 13001: + case INSTRUMENT_WOODEN_FLUTE: if(itembonuses.windMod == 0) { HasInstrument = false; c->MessageString(Chat::Red, SONG_NEEDS_WIND); @@ -1286,7 +1521,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo break; // string songs (13011 = lute) - case 13011: + case INSTRUMENT_LUTE: if(itembonuses.stringedMod == 0) { HasInstrument = false; c->MessageString(Chat::Red, SONG_NEEDS_STRINGS); @@ -1294,7 +1529,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo break; // brass songs (13012 = horn) - case 13012: + case INSTRUMENT_HORN: if(itembonuses.brassMod == 0) { HasInstrument = false; c->MessageString(Chat::Red, SONG_NEEDS_BRASS); @@ -1422,29 +1657,6 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo break; } - //Test the aug recast delay - if(IsClient() && fromaug && recastdelay > 0) - { - if(!CastToClient()->GetPTimers().Expired(&database, (pTimerItemStart + recasttype), false)) { - MessageString(Chat::Red, SPELL_RECAST); - LogSpells("Casting of [{}] canceled: item spell reuse timer not expired", spell_id); - StopCasting(); - return; - } - else - { - //Can we start the timer here? I don't see why not. - CastToClient()->GetPTimers().Start((pTimerItemStart + recasttype), recastdelay); - if (recasttype != -1) { - database.UpdateItemRecastTimestamps( - CastToClient()->CharacterID(), - recasttype, - CastToClient()->GetPTimers().Get(pTimerItemStart + recasttype)->GetReadyTimestamp() - ); - } - } - } - if (item && item->IsClassCommon() && (item->GetItem()->Click.Effect == spell_id) && item->GetCharges() || fromaug) { //const ItemData* item = item->GetItem(); @@ -1469,7 +1681,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo } // we're done casting, now try to apply the spell - if( !SpellFinished(spell_id, spell_target, slot, mana_used, inventory_slot, resist_adjust) ) + if(!SpellFinished(spell_id, spell_target, slot, mana_used, inventory_slot, resist_adjust, false,-1, 0xFFFFFFFF, 0, true)) { LogSpells("Casting of [{}] canceled: SpellFinished returned false", spell_id); // most of the cases we return false have a message already or are logic errors that shouldn't happen @@ -1482,9 +1694,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo CheckNumHitsRemaining(NumHit::MatchingSpells); TrySympatheticProc(target, spell_id); } - - TryOnSpellFinished(this, target, spell_id); //Use for effects that should be checked after SpellFinished is completed. - + TryTwincast(this, target, spell_id); TryTriggerOnCastFocusEffect(focusTriggerOnCast, spell_id); @@ -1492,24 +1702,6 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo if(DeleteChargeFromSlot >= 0) CastToClient()->DeleteItemInInventory(DeleteChargeFromSlot, 1, true); - if (IsClient() && IsEffectInSpell(spell_id, SE_BindSight)) { - for (int i = 0; i < GetMaxTotalSlots(); i++) { - if (buffs[i].spellid == spell_id) { - CastToClient()->SendBuffNumHitPacket(buffs[i], i);//its hack, it works. - } - } - } - - //Check if buffs has numhits, then resend packet so it displays the hit count. - if (IsClient() && spells[spell_id].hit_number) { - for (int i = 0; i < GetMaxTotalSlots(); i++) { - if (buffs[i].spellid == spell_id && buffs[i].hit_number > 0) { - CastToClient()->SendBuffNumHitPacket(buffs[i], i); - break; - } - } - } - // // at this point the spell has successfully been cast // @@ -1535,6 +1727,10 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo if (RuleB(Spells, EnableBardMelody)) { c->MemorizeSpell(static_cast(slot), spell_id, memSpellSpellbar, casting_spell_recast_adjust); } + + if (!IsFromItem) { + c->CheckSongSkillIncrease(spell_id); + } } LogSpells("Bard song [{}] should be started", spell_id); } @@ -1632,62 +1828,6 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce if (isproc && IsNPC() && CastToNPC()->GetInnateProcSpellID() == spell_id) targetType = ST_Target; - if (spell_target && spells[spell_id].cast_restriction && !spell_target->PassCastRestriction(spells[spell_id].cast_restriction)){ - Message(Chat::Red, "Your target does not meet the spell requirements."); //Current live also adds description after this from dbstr_us type 39 - return false; - } - - if (spells[spell_id].caster_requirement_id && !PassCastRestriction(spells[spell_id].caster_requirement_id)) { - MessageString(Chat::Red, SPELL_WOULDNT_HOLD); - return false; - } - - //Must be out of combat. (If Beneficial checks casters combat state, Deterimental checks targets) - if (!spells[spell_id].can_cast_in_combat && spells[spell_id].can_cast_out_of_combat) { - if (IsDetrimentalSpell(spell_id)) { - if (spell_target && - ((spell_target->IsNPC() && spell_target->IsEngaged()) || - (spell_target->IsClient() && spell_target->CastToClient()->GetAggroCount()))) { - MessageString(Chat::Red, SPELL_NO_EFFECT); // Unsure correct string - return false; - } - } - - else if (IsBeneficialSpell(spell_id)) { - if ((IsNPC() && IsEngaged()) || (IsClient() && CastToClient()->GetAggroCount())) { - if (IsDiscipline(spell_id)) - MessageString(Chat::Red, NO_ABILITY_IN_COMBAT); - else - MessageString(Chat::Red, NO_CAST_IN_COMBAT); - - return false; - } - } - } - - // Must be in combat. (If Beneficial checks casters combat state, Deterimental checks targets) - else if (spells[spell_id].can_cast_in_combat && !spells[spell_id].can_cast_out_of_combat) { - if (IsDetrimentalSpell(spell_id)) { - if (spell_target && - ((spell_target->IsNPC() && !spell_target->IsEngaged()) || - (spell_target->IsClient() && !spell_target->CastToClient()->GetAggroCount()))) { - MessageString(Chat::Red, SPELL_NO_EFFECT); // Unsure correct string - return false; - } - } - - else if (IsBeneficialSpell(spell_id)) { - if ((IsNPC() && !IsEngaged()) || (IsClient() && !CastToClient()->GetAggroCount())) { - if (IsDiscipline(spell_id)) - MessageString(Chat::Red, NO_ABILITY_OUT_OF_COMBAT); - else - MessageString(Chat::Red, NO_CAST_OUT_OF_COMBAT); - - return false; - } - } - } - switch (targetType) { // single target spells @@ -2160,9 +2300,8 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce // if you need to abort the casting, return false bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, uint16 mana_used, uint32 inventory_slot, int16 resist_adjust, bool isproc, int level_override, - uint32 timer, uint32 timer_duration) + uint32 timer, uint32 timer_duration, bool from_casted_spell) { - //EQApplicationPacket *outapp = nullptr; Mob *ae_center = nullptr; if(!IsValidSpell(spell_id)) @@ -2177,7 +2316,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui } } - //Guard Assist Code + //Guard Assist Code if (RuleB(Character, PVPEnableGuardFactionAssist) && spell_target && IsDetrimentalSpell(spell_id) && spell_target != this) { if (IsClient() && spell_target->IsClient()|| (HasOwner() && GetOwner()->IsClient() && spell_target->IsClient())) { auto& mob_list = entity_list.GetCloseMobList(spell_target); @@ -2193,72 +2332,27 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui } } } - } - } - - if( spells[spell_id].zone_type == 1 && !zone->CanCastOutdoor()){ - if(IsClient()){ - if(!CastToClient()->GetGM()){ - MessageString(Chat::Red, CAST_OUTDOORS); - return false; - } - } - } - - if(IsEffectInSpell(spell_id, SE_Levitate) && !zone->CanLevitate()){ - if(IsClient()){ - if(!CastToClient()->GetGM()){ - Message(Chat::Red, "You can't levitate in this zone."); - return false; - } - } - } - - if(IsClient() && !CastToClient()->GetGM()){ - - if(zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))){ - const char *msg = zone->GetSpellBlockedMessage(spell_id, glm::vec3(GetPosition())); - if(msg){ - Message(Chat::Red, msg); - return false; - } - else{ - Message(Chat::Red, "You can't cast this spell here."); - return false; - } - } } - if (IsClient() && CastToClient()->GetGM()){ - if (zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))){ - LogSpells("GM Cast Blocked Spell: [{}] (ID [{}])", GetSpellName(spell_id), spell_id); + //If spell was casted then we already checked these so skip, otherwise check here if being called directly from spell finished. + if (!from_casted_spell){ + if (!DoCastingChecksZoneRestrictions(true, spell_id)) { + LogSpells("Spell [{}]: Zone restriction failure.", spell_id); + return false; } - } - - if - ( - this->IsClient() && - (zone->GetZoneID() == 183 || zone->GetZoneID() == 184) && // load - CastToClient()->Admin() < AccountStatus::QuestTroupe - ) - { - if - ( - IsEffectInSpell(spell_id, SE_Gate) || - IsEffectInSpell(spell_id, SE_Translocate) || - IsEffectInSpell(spell_id, SE_Teleport) - ) - { - Message(0, "The Gods brought you here, only they can send you away."); + if (!DoCastingChecksOnTarget(true, spell_id, spell_target)) { + LogSpells("Spell [{}]: Casting checks on Target failure.", spell_id); return false; } } //determine the type of spell target we have CastAction_type CastAction; - if(!DetermineSpellTargets(spell_id, spell_target, ae_center, CastAction, slot, isproc)) + if (!DetermineSpellTargets(spell_id, spell_target, ae_center, CastAction, slot, isproc)) { + LogSpells("Spell [{}]: Determine spell targets failure.", spell_id); return(false); + } LogSpells("Spell [{}]: target type [{}], target [{}], AE center [{}]", spell_id, CastAction, spell_target?spell_target->GetName():"NONE", ae_center?ae_center->GetName():"NONE"); @@ -2299,11 +2393,10 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui range = spells[spell_id].aoe_range; range = GetActSpellRange(spell_id, range); - if(IsPlayerIllusionSpell(spell_id) - && IsClient() - && (HasProjectIllusion())){ + if(IsClient() && IsPlayerIllusionSpell(spell_id) && (HasProjectIllusion())){ range = 100; } + if(spell_target != nullptr && spell_target != this) { //casting a spell on somebody but ourself, make sure they are in range float dist2 = DistanceSquared(m_Position, spell_target->GetPosition()); @@ -2578,16 +2671,18 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui SetEndurance(GetEndurance() - EQ::ClampUpper(end_cost, GetEndurance())); TryTriggerOnCastRequirement(); } - if (mgb) + if (mgb) { SetMGB(false); - - //set our reuse timer on long ass reuse_time spells... + } + /* + Set Recast Timer on spells. + */ if(IsClient() && !isproc) { - //Support for bards to get disc recast timers while singing. + //Support for bards to get disc recast timers while singing if (GetClass() == BARD && spell_id != casting_spell_id && timer != 0xFFFFFFFF) { CastToClient()->GetPTimers().Start(timer, timer_duration); - LogSpells("Spell [{}]: Setting bard custom disciple reuse timer [{}] to [{}]", spell_id, timer, timer_duration); + LogSpells("Spell [{}]: Setting bard disciple reuse timer from spell finished [{}] to [{}]", spell_id, timer, timer_duration); } if(casting_spell_aa_id) { @@ -2632,293 +2727,72 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui } } } + /* + Set Recast Timer on item clicks, including augmenets. + */ + if(IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt)){ + CastToClient()->SetItemRecastTimer(spell_id, inventory_slot); + } - if(IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt)) - { - EQ::ItemInstance *itm = CastToClient()->GetInv().GetItem(inventory_slot); - if(itm && itm->GetItem()->RecastDelay > 0){ - auto recast_type = itm->GetItem()->RecastType; - int recast_delay = itm->GetItem()->RecastDelay; - //must use SPA 415 with focus (SPA 310) to reduce item recast - int reduction = CastToClient()->GetFocusEffect(focusReduceRecastTime, spell_id); - if (reduction) { - recast_delay -= reduction; - } - recast_delay = std::max(recast_delay, 0); + if (IsNPC()) { + CastToNPC()->AI_Event_SpellCastFinished(true, static_cast(slot)); + } - if (recast_delay > 0) { + ApplyHealthTransferDamage(this, target, spell_id); - CastToClient()->GetPTimers().Start((pTimerItemStart + recast_type), recast_delay); - if (recast_type != -1) { - database.UpdateItemRecastTimestamps( - CastToClient()->CharacterID(), - recast_type, - CastToClient()->GetPTimers().Get(pTimerItemStart + recast_type)->GetReadyTimestamp() - ); - } - - auto outapp = new EQApplicationPacket(OP_ItemRecastDelay, sizeof(ItemRecastDelay_Struct)); - ItemRecastDelay_Struct *ird = (ItemRecastDelay_Struct *)outapp->pBuffer; - ird->recast_delay = static_cast(recast_delay); - ird->recast_type = recast_type; - CastToClient()->QueuePacket(outapp); - safe_delete(outapp); + //This needs to be here for bind sight to update correctly on client. + if (IsClient() && IsEffectInSpell(spell_id, SE_BindSight)) { + for (int i = 0; i < GetMaxTotalSlots(); i++) { + if (buffs[i].spellid == spell_id) { + CastToClient()->SendBuffNumHitPacket(buffs[i], i);//its hack, it works. + } + } + } + //Check if buffs has numhits, then resend packet so it displays the hit count. + if (IsClient() && spells[spell_id].hit_number) { + for (int i = 0; i < GetMaxTotalSlots(); i++) { + if (buffs[i].spellid == spell_id && buffs[i].hit_number > 0) { + CastToClient()->SendBuffNumHitPacket(buffs[i], i); + break; } } } - - if(IsNPC()) - CastToNPC()->AI_Event_SpellCastFinished(true, static_cast(slot)); return true; } -/* - * handle bard song pulses... - * - * we make several assumptions that SpellFinished does not: - * - there are no AEDuration (beacon) bard songs - * - there are no recourse spells on bard songs - * - there is no long recast delay on bard songs - * - * return false to stop the song - */ -bool Mob::ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, CastingSlot slot) { - if(slot == CastingSlot::Item) { - //bard songs should never come from items... - LogSpells("Bard Song Pulse [{}]: Supposidly cast from an item. Killing song", spell_id); - return(false); +bool Mob::ApplyBardPulse(int32 spell_id, Mob *spell_target, CastingSlot slot) { + + /* + Check any bard specific special behaviors we need before applying the next pulse. + Note: Silence does not stop an active bard pulse. + */ + if (!spell_target) { + return false; + } + /* + Bard song charm that have no mana will continue to try and pulse on target, but will only reapply when charm fades. + Live does not spam client with do not take hold messages. Checking here avoids that from happening. Only try to reapply if charm fades. + */ + if (spell_target->IsCharmed() && spells[spell_id].mana == 0 && spell_target->GetOwner() == this && IsEffectInSpell(spell_id, SE_Charm)) { + return true; + } + /* + If divine aura applied while pulsing, it is not interrupted but does not reapply until DA fades. + */ + if (DivineAura() && !IgnoreCastingRestriction(spell_id)) { + return true; + } + /* + Fear will stop pulsing. + */ + if (IsFeared()) { + return false; } - //determine the type of spell target we have - Mob *ae_center = nullptr; - CastAction_type CastAction; - if(!DetermineSpellTargets(spell_id, spell_target, ae_center, CastAction, slot)) { - LogSpells("Bard Song Pulse [{}]: was unable to determine target. Stopping", spell_id); - return(false); + if (!SpellFinished(spell_id, spell_target, slot, spells[spell_id].mana, 0xFFFFFFFF, spells[spell_id].resist_difficulty)) { + return false; } - - if(ae_center != nullptr && ae_center->IsBeacon()) { - LogSpells("Bard Song Pulse [{}]: Unsupported Beacon NPC AE spell", spell_id); - return(false); - } - - //use mana, if this spell has a mana cost - int mana_used = spells[spell_id].mana; - if(mana_used > 0) { - if(mana_used > GetMana()) { - //ran out of mana... this calls StopSong() for us - LogSpells("Ran out of mana while singing song [{}]", spell_id); - return(false); - } - - LogSpells("Bard Song Pulse [{}]: consuming [{}] mana (have [{}])", spell_id, mana_used, GetMana()); - SetMana(GetMana() - mana_used); - } - - // check line of sight to target if it's a detrimental spell - if(spell_target && IsDetrimentalSpell(spell_id) && !CheckLosFN(spell_target)) - { - LogSpells("Bard Song Pulse [{}]: cannot see target [{}]", spell_target->GetName()); - MessageString(Chat::Red, CANT_SEE_TARGET); - return(false); - } - - //range check our target, if we have one and it is not us - float range = 0.00f; - - range = GetActSpellRange(spell_id, spells[spell_id].range, true); - if(spell_target != nullptr && spell_target != this) { - //casting a spell on somebody but ourself, make sure they are in range - float dist2 = DistanceSquared(m_Position, spell_target->GetPosition()); - float range2 = range * range; - if(dist2 > range2) { - //target is out of range. - LogSpells("Bard Song Pulse [{}]: Spell target is out of range (squared: [{}] > [{}])", spell_id, dist2, range2); - MessageString(Chat::Red, TARGET_OUT_OF_RANGE); - return(false); - } - } - - // - // Switch #2 - execute the spell - // - switch(CastAction) - { - default: - case CastActUnknown: - case SingleTarget: - { - if(spell_target == nullptr) { - LogSpells("Bard Song Pulse [{}]: Targeted spell, but we have no target", spell_id); - return(false); - } - LogSpells("Bard Song Pulse: Targeted. spell [{}], target [{}]", spell_id, spell_target->GetName()); - spell_target->BardPulse(spell_id, this); - break; - } - - case AECaster: - { - if(IsBeneficialSpell(spell_id)) - SpellOnTarget(spell_id, this); - - bool affect_caster = !IsNPC(); //NPC AE spells do not affect the NPC caster - entity_list.AEBardPulse(this, this, spell_id, affect_caster); - break; - } - case AETarget: - { - // we can't cast an AE spell without something to center it on - if(ae_center == nullptr) { - LogSpells("Bard Song Pulse [{}]: AE Targeted spell, but we have no target", spell_id); - return(false); - } - - // regular PB AE or targeted AE spell - spell_target is null if PB - if(spell_target) { // this must be an AETarget spell - // affect the target too - spell_target->BardPulse(spell_id, this); - LogSpells("Bard Song Pulse: spell [{}], AE target [{}]", spell_id, spell_target->GetName()); - } else { - LogSpells("Bard Song Pulse: spell [{}], AE with no target", spell_id); - } - bool affect_caster = !IsNPC(); //NPC AE spells do not affect the NPC caster - entity_list.AEBardPulse(this, ae_center, spell_id, affect_caster); - break; - } - - case GroupSpell: - { - if(spell_target->IsGrouped()) { - LogSpells("Bard Song Pulse: spell [{}], Group targeting group of [{}]", spell_id, spell_target->GetName()); - Group *target_group = entity_list.GetGroupByMob(spell_target); - if(target_group) - target_group->GroupBardPulse(this, spell_id); - } - else if(spell_target->IsRaidGrouped() && spell_target->IsClient()) { - LogSpells("Bard Song Pulse: spell [{}], Raid group targeting raid group of [{}]", spell_id, spell_target->GetName()); - Raid *r = entity_list.GetRaidByClient(spell_target->CastToClient()); - if(r){ - uint32 gid = r->GetGroup(spell_target->GetName()); - if(gid < 12){ - r->GroupBardPulse(this, spell_id, gid); - } - else{ - BardPulse(spell_id, this); -#ifdef GROUP_BUFF_PETS - if (GetPet() && HasPetAffinity() && !GetPet()->IsCharmed()) - GetPet()->BardPulse(spell_id, this); -#endif - } - } - } - else { - LogSpells("Bard Song Pulse: spell [{}], Group target without group. Affecting caster", spell_id); - BardPulse(spell_id, this); -#ifdef GROUP_BUFF_PETS - if (GetPet() && HasPetAffinity() && !GetPet()->IsCharmed()) - GetPet()->BardPulse(spell_id, this); -#endif - } - break; - } - } - - if(IsClient()) - CastToClient()->CheckSongSkillIncrease(spell_id); - - return(true); -} - -void Mob::BardPulse(uint16 spell_id, Mob *caster) { - // so for Solon's Song of the Sirens (725) if we're repulsing, we need to skip - // other charms have mana and don't repulse - // This is probably not the ideal place for this, but it will work - if (IsCharmed() && GetOwner() == caster && IsEffectInSpell(spell_id, SE_Charm)) { - return; - } - int buffs_i; - int buff_count = GetMaxTotalSlots(); - for (buffs_i = 0; buffs_i < buff_count; buffs_i++) { - if(buffs[buffs_i].spellid != spell_id) - continue; - if(buffs[buffs_i].casterid != caster->GetID()) { - LogSpells("Bard Pulse for [{}]: found buff from caster [{}] and we are pulsing for [{}] are there two bards playing the same song???", spell_id, buffs[buffs_i].casterid, caster->GetID()); - return; - } - //extend the spell if it will expire before the next pulse - if(buffs[buffs_i].ticsremaining <= 3) { - buffs[buffs_i].ticsremaining += 3; - LogSpells("Bard Song Pulse [{}]: extending duration in slot [{}] to [{}] tics", spell_id, buffs_i, buffs[buffs_i].ticsremaining); - } - - //should we send this buff update to the client... seems like it would - //be a lot of traffic for no reason... -//this may be the wrong packet... - if(IsClient()) { - auto packet = new EQApplicationPacket(OP_Action, sizeof(Action_Struct)); - - Action_Struct* action = (Action_Struct*) packet->pBuffer; - action->source = caster->GetID(); - action->target = GetID(); - action->spell = spell_id; - action->force = spells[spell_id].push_back; - action->hit_heading = GetHeading(); - action->hit_pitch = spells[spell_id].push_up; - action->instrument_mod = caster->GetInstrumentMod(spell_id); - action->effect_flag = 0; - action->spell_level = action->level = buffs[buffs_i].casterlevel; - action->type = DamageTypeSpell; - entity_list.QueueCloseClients(this, packet, false, RuleI(Range, SongMessages), 0, true, IsClient() ? FilterPCSpells : FilterNPCSpells); - - action->effect_flag = 4; - - if (spells[spell_id].push_back != 0.0f || spells[spell_id].push_up != 0.0f) - { - if (IsClient()) - { - if (!IsBuffSpell(spell_id)) - { - CastToClient()->cheat_manager.SetExemptStatus(KnockBack, true); - } - } - } - - if (IsClient() && IsEffectInSpell(spell_id, SE_ShadowStep)) - { - CastToClient()->cheat_manager.SetExemptStatus(ShadowStep, true); - } - - if(!IsEffectInSpell(spell_id, SE_BindAffinity)) - { - CastToClient()->QueuePacket(packet); - } - - auto message_packet = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct)); - CombatDamage_Struct *cd = (CombatDamage_Struct *)message_packet->pBuffer; - cd->target = action->target; - cd->source = action->source; - cd->type = DamageTypeSpell; - cd->spellid = action->spell; - cd->force = action->force; - cd->hit_heading = action->hit_heading; - cd->hit_pitch = action->hit_pitch; - cd->damage = 0; - if(!IsEffectInSpell(spell_id, SE_BindAffinity)) - { - entity_list.QueueCloseClients(this, message_packet, false, RuleI(Range, SongMessages), 0, true, IsClient() ? FilterPCSpells : FilterNPCSpells); - } - safe_delete(message_packet); - safe_delete(packet); - - } - //we are done... - return; - } - LogSpells("Bard Song Pulse [{}]: Buff not found, reapplying spell", spell_id); - //this spell is not affecting this mob, apply it. - caster->SpellOnTarget(spell_id, this); } /////////////////////////////////////////////////////////////////////////////// @@ -3501,6 +3375,8 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid return -1; } } + //do not fade buff if from bard pulse, live does not give a fades message. + bool from_bard_song_pulse = caster ? caster->IsActiveBardSong(spell_id) : false; // at this point we know that this buff will stick, but we have // to remove some other buffs already worn if will_overwrite is true @@ -3510,7 +3386,9 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid end = overwrite_slots.end(); for (; cur != end; ++cur) { // strip spell - BuffFadeBySlot(*cur, false); + if (!from_bard_song_pulse) { + BuffFadeBySlot(*cur, false); + } // if we hadn't found a free slot before, or if this is earlier // we use it @@ -3653,8 +3531,6 @@ int Mob::CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite) bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectiveness, bool use_resist_adjust, int16 resist_adjust, bool isproc, int level_override, int32 duration_override) { - bool is_damage_or_lifetap_spell = IsDamageSpell(spell_id) || IsLifetapSpell(spell_id); - // well we can't cast a spell on target without a target if(!spelltar) { @@ -3667,6 +3543,11 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes return false; } + if (!IsValidSpell(spell_id)) + return false; + + bool is_damage_or_lifetap_spell = IsDamageSpell(spell_id) || IsLifetapSpell(spell_id); + if(IsDetrimentalSpell(spell_id) && !IsAttackAllowed(spelltar, true) && !IsResurrectionEffects(spell_id) && !IsEffectInSpell(spell_id, SE_BindSight)) { if(!IsClient() || !CastToClient()->GetGM()) { MessageString(Chat::SpellFailure, SPELL_NO_HOLD); @@ -3674,12 +3555,13 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes } } + if (!DoCastingChecksOnTarget(false, spell_id, spelltar)) { + return false; + } + EQApplicationPacket *action_packet = nullptr, *message_packet = nullptr; float spell_effectiveness; - if(!IsValidSpell(spell_id)) - return false; - // these target types skip pcnpc only check (according to dev quotes) // other AE spells this is redundant, oh well // 1 = PCs, 2 = NPCs @@ -6025,17 +5907,6 @@ int Mob::GetCasterLevel(uint16 spell_id) { return std::max(1, level); } -//this method does NOT tell the client to stop singing the song. -//this is NOT the right way to stop a mob from singing, use InterruptSpell -//you should really know what your doing before you call this -void Mob::_StopSong() -{ - bardsong = 0; - bardsong_target_id = 0; - bardsong_slot = CastingSlot::Gem1; - bardsong_timer.Disable(); -} - //This member function sets the buff duration on the client //however it does not work if sent quickly after an action packets, which is what one might perfer to do //Thus I use this in the buff process to update the correct duration once after casting @@ -6280,8 +6151,12 @@ void Client::SendSpellAnim(uint16 targetid, uint16 spell_id) entity_list.QueueCloseClients(this, &app, false, RuleI(Range, SpellParticles)); } -void Client::SendItemRecastTimer(uint32 recast_type, uint32 recast_delay) +void Client::SendItemRecastTimer(int32 recast_type, uint32 recast_delay) { + if (recast_type == -1) { + return; + } + if (!recast_delay) { recast_delay = GetPTimers().GetRemainingTime(pTimerItemStart + recast_type); } @@ -6290,12 +6165,125 @@ void Client::SendItemRecastTimer(uint32 recast_type, uint32 recast_delay) auto outapp = new EQApplicationPacket(OP_ItemRecastDelay, sizeof(ItemRecastDelay_Struct)); ItemRecastDelay_Struct *ird = (ItemRecastDelay_Struct *)outapp->pBuffer; ird->recast_delay = recast_delay; - ird->recast_type = recast_type; + ird->recast_type = static_cast(recast_type); QueuePacket(outapp); safe_delete(outapp); } } +void Client::SetItemRecastTimer(int32 spell_id, uint32 inventory_slot) +{ + EQ::ItemInstance *item = CastToClient()->GetInv().GetItem(inventory_slot); + + int recast_delay = 0; + int recast_type = 0; + bool from_augment = false; + + if (!item) { + return; + } + + //Check primary item. + if (item->GetItem()->RecastDelay > 0) { + recast_type = item->GetItem()->RecastType; + recast_delay = item->GetItem()->RecastDelay; + } + //Check augmenent + else{ + for (int r = EQ::invaug::SOCKET_BEGIN; r <= EQ::invaug::SOCKET_END; r++) { + const EQ::ItemInstance* aug_i = item->GetAugment(r); + + if (!aug_i) { + continue; + } + const EQ::ItemData* aug = aug_i->GetItem(); + if (!aug) { + continue; + } + + if (aug->Click.Effect == spell_id) { + recast_delay = aug_i->GetItem()->RecastDelay; + recast_type = aug_i->GetItem()->RecastType; + from_augment = true; + break; + } + } + } + //must use SPA 415 with focus (SPA 310) to reduce item recast + int reduction = GetFocusEffect(focusReduceRecastTime, spell_id); + if (reduction) { + recast_delay -= reduction; + } + + recast_delay = std::max(recast_delay, 0); + + if (recast_delay > 0) { + + GetPTimers().Start((pTimerItemStart + recast_type), static_cast(recast_delay)); + if (recast_type != -1) { + database.UpdateItemRecastTimestamps( + CharacterID(), + recast_type, + GetPTimers().Get(pTimerItemStart + recast_type)->GetReadyTimestamp() + ); + } + + if (!from_augment) { + SendItemRecastTimer(recast_type, static_cast(recast_delay)); + } + } +} + +bool Client::HasItemRecastTimer(int32 spell_id, uint32 inventory_slot) +{ + EQ::ItemInstance *item = CastToClient()->GetInv().GetItem(inventory_slot); + + int recast_delay = 0; + int recast_type = 0; + bool from_augment = false; + + if (!item) { + return false; + } + + if (!item->GetItem()) { + return false; + } + + //Check primary item. + if (item->GetItem()->RecastDelay > 0) { + recast_type = item->GetItem()->RecastType; + recast_delay = item->GetItem()->RecastDelay; + } + //Check augmenent + else { + for (int r = EQ::invaug::SOCKET_BEGIN; r <= EQ::invaug::SOCKET_END; r++) { + const EQ::ItemInstance* aug_i = item->GetAugment(r); + + if (!aug_i) { + continue; + } + const EQ::ItemData* aug = aug_i->GetItem(); + if (!aug) { + continue; + } + + if (aug->Click.Effect == spell_id) { + recast_delay = aug_i->GetItem()->RecastDelay; + recast_type = aug_i->GetItem()->RecastType; + break; + } + } + } + + if (!CastToClient()->GetPTimers().Expired(&database, (pTimerItemStart + recast_type), false)) { + return true; + } + + return false; +} + + void Mob::CalcDestFromHeading(float heading, float distance, float MaxZDiff, float StartX, float StartY, float &dX, float &dY, float &dZ) { if (!distance) { return; } @@ -6563,3 +6551,43 @@ void Client::ResetCastbarCooldownBySpellID(uint32 spell_id) { } } } + +bool Mob::IsActiveBardSong(int32 spell_id) { + + if (spell_id == bardsong) { + return true; + } + return false; +} + +void Mob::DoBardCastingFromItemClick(bool is_casting_bard_song, uint32 cast_time, int32 spell_id, uint16 target_id, EQ::spells::CastingSlot slot, uint32 item_slot, uint32 recast_type, uint32 recast_delay) +{ + /* + Known bug: When a bard uses an augment with a clicky that has a cast time, the cast won't display. This issue only affects bards. + */ + if (is_casting_bard_song) { + //For spells with cast times. Cancel song cast, stop pusling and start item cast. + if (cast_time != 0) { + EQApplicationPacket *outapp = nullptr; + outapp = new EQApplicationPacket(OP_InterruptCast, sizeof(InterruptCast_Struct)); + InterruptCast_Struct* ic = (InterruptCast_Struct*)outapp->pBuffer; + ic->messageid = SONG_ENDS; + ic->spawnid = GetID(); + outapp->priority = 5; + CastToClient()->QueuePacket(outapp); + safe_delete(outapp); + + SendSpellBarDisable(); + ZeroCastingVars(); + ZeroBardPulseVars(); + } + } + + if (cast_time != 0) { + CastSpell(spell_id, target_id, CastingSlot::Item, cast_time, 0, 0, item_slot); + } + //Instant cast items do not stop bard songs or interrupt casting. + else if (DoCastingChecksOnCaster(spell_id)) { + SpellFinished(spell_id, entity_list.GetMob(target_id), CastingSlot::Item, 0, item_slot); + } +} From 8a48473dbc3c8a0dd108fe637347b5d047ede0f0 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 8 Feb 2022 07:35:47 -0500 Subject: [PATCH 597/624] [Spells] Fix for AA and Discipline recast timers being set on spell casting failure. (#1971) * recast timer updates * reworked * removed unneeded param * fix expendible AA * fixed * Update spells.cpp * [Spells] Fix for AA and Discipline recast timers being set on spell casting failure. don't check recasts from triggered spells. --- zone/aa.cpp | 78 ++++++++++++++++++++++---------------- zone/client.h | 2 + zone/effects.cpp | 48 +++-------------------- zone/mob.h | 2 +- zone/spells.cpp | 99 ++++++++++++++++++++++++++++++++++++------------ 5 files changed, 130 insertions(+), 99 deletions(-) diff --git a/zone/aa.cpp b/zone/aa.cpp index f455bfd53..a05f9dc47 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -1227,65 +1227,65 @@ void Client::IncrementAlternateAdvancementRank(int rank_id) { void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { AA::Rank *rank = zone->GetAlternateAdvancementRank(rank_id); - if(!rank) { + if (!rank) { return; } AA::Ability *ability = rank->base_ability; - if(!ability) { + if (!ability) { return; } - if(!IsValidSpell(rank->spell)) { + if (!IsValidSpell(rank->spell)) { + return; + } + //do not allow AA to cast if your actively casting another AA. + if (rank->spell == casting_spell_id && rank->id == casting_spell_aa_id) { return; } - if(!CanUseAlternateAdvancementRank(rank)) { + if (!CanUseAlternateAdvancementRank(rank)) { return; } - + bool use_toggle_passive_hotkey = UseTogglePassiveHotkey(*rank); //make sure it is not a passive - if(!rank->effects.empty() && !use_toggle_passive_hotkey) { + if (!rank->effects.empty() && !use_toggle_passive_hotkey) { return; } uint32 charges = 0; // We don't have the AA - if (!GetAA(rank_id, &charges)) + if (!GetAA(rank_id, &charges)) { return; + } //if expendable make sure we have charges - if(ability->charges > 0 && charges < 1) + if (ability->charges > 0 && charges < 1) { return; + } //check cooldown - if(!p_timers.Expired(&database, rank->spell_type + pTimerAAStart, false)) { + if (!p_timers.Expired(&database, rank->spell_type + pTimerAAStart, false)) { uint32 aaremain = p_timers.GetRemainingTime(rank->spell_type + pTimerAAStart); uint32 aaremain_hr = aaremain / (60 * 60); uint32 aaremain_min = (aaremain / 60) % 60; uint32 aaremain_sec = aaremain % 60; - if(aaremain_hr >= 1) { + if (aaremain_hr >= 1) { Message(Chat::Red, "You can use this ability again in %u hour(s) %u minute(s) %u seconds", - aaremain_hr, aaremain_min, aaremain_sec); + aaremain_hr, aaremain_min, aaremain_sec); } else { Message(Chat::Red, "You can use this ability again in %u minute(s) %u seconds", - aaremain_min, aaremain_sec); + aaremain_min, aaremain_sec); } - return; } - //calculate cooldown - int cooldown = rank->recast_time - GetAlternateAdvancementCooldownReduction(rank); - if(cooldown < 0) { - cooldown = 0; - } - - if (!IsCastWhileInvis(rank->spell)) + if (!IsCastWhileInvis(rank->spell)) { CommonBreakInvisible(); + } if (spells[rank->spell].sneak && (!hidden || (hidden && (Timer::GetCurrentTime() - tmHidden) < 4000))) { MessageString(Chat::SpellFailure, SNEAK_RESTRICT); @@ -1293,13 +1293,15 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { } // // Modern clients don't require pet targeted for AA casts that are ST_Pet - if (spells[rank->spell].target_type == ST_Pet || spells[rank->spell].target_type == ST_SummonedPet) + if (spells[rank->spell].target_type == ST_Pet || spells[rank->spell].target_type == ST_SummonedPet) { target_id = GetPetID(); + } // extra handling for cast_not_standing spells if (!IgnoreCastingRestriction(rank->spell)) { - if (GetAppearance() == eaSitting) // we need to stand! + if (GetAppearance() == eaSitting) { // we need to stand! SetAppearance(eaStanding, false); + } if (GetAppearance() != eaStanding) { MessageString(Chat::SpellFailure, STAND_TO_CAST); @@ -1312,24 +1314,35 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { } else { // Bards can cast instant cast AAs while they are casting or channeling item cast. - if (GetClass() == BARD && IsCasting() && spells[rank->spell].cast_time == 0) { + if (GetClass() == BARD && IsCasting() && spells[rank->spell].cast_time == 0) { if (!DoCastingChecksOnCaster(rank->spell)) { return; } - if (!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQ::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].resist_difficulty, false)) { - return; - } - ExpendAlternateAdvancementCharge(ability->id); + SpellFinished(rank->spell, entity_list.GetMob(target_id), EQ::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].resist_difficulty, false, -1, false, rank->id); } + //Known issue: If you attempt to give a Bard an AA with a cast time, the cast timer will not display on the client (no live bard AA have cast time). else { - if (!CastSpell(rank->spell, target_id, EQ::spells::CastingSlot::AltAbility, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, cooldown, nullptr, rank->id)) { - return; - } + CastSpell(rank->spell, target_id, EQ::spells::CastingSlot::AltAbility, -1, -1, 0, -1, 0xFFFFFFFF, 0, nullptr, rank->id); } } +} - CastToClient()->GetPTimers().Start(rank->spell_type + pTimerAAStart, cooldown); - SendAlternateAdvancementTimer(rank->spell_type, 0, 0); +void Client::SetAARecastTimer(AA::Rank *rank_in, int32 spell_id) { + + if (!rank_in) { + return; + } + + //calculate AA cooldown + int timer_duration = rank_in->recast_time - GetAlternateAdvancementCooldownReduction(rank_in); + + if (timer_duration <= 0) { + return; + } + + CastToClient()->GetPTimers().Start(rank_in->spell_type + pTimerAAStart, timer_duration); + CastToClient()->SendAlternateAdvancementTimer(rank_in->spell_type, 0, 0); + LogSpells("Spell [{}]: Setting AA reuse timer [{}] to [{}]", spell_id, rank_in->spell_type + pTimerAAStart, timer_duration); } int Mob::GetAlternateAdvancementCooldownReduction(AA::Rank *rank_in) { @@ -1363,6 +1376,7 @@ int Mob::GetAlternateAdvancementCooldownReduction(AA::Rank *rank_in) { } void Mob::ExpendAlternateAdvancementCharge(uint32 aa_id) { + for (auto &iter : aa_ranks) { AA::Ability *ability = zone->GetAlternateAdvancementAbility(iter.first); if (ability && aa_id == ability->id) { diff --git a/zone/client.h b/zone/client.h index f30f9add7..3b3b0ac84 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1495,6 +1495,8 @@ public: void SendItemRecastTimer(int32 recast_type, uint32 recast_delay = 0); void SetItemRecastTimer(int32 spell_id, uint32 inventory_slot); bool HasItemRecastTimer(int32 spell_id, uint32 inventory_slot); + void SetDisciplineRecastTimer(int32 spell_id); + void SetAARecastTimer(AA::Rank *rank_in, int32 spell_id); inline bool AggroMeterAvailable() const { return ((m_ClientVersionBit & EQ::versions::maskRoF2AndLater)) && RuleB(Character, EnableAggroMeter); } // RoF untested inline void SetAggroMeterLock(int in) { m_aggrometer.set_lock_id(in); } diff --git a/zone/effects.cpp b/zone/effects.cpp index 24f9afd52..98ebb32dd 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -800,51 +800,15 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) { return false; } - bool instant_recast = true; - - if(spell.recast_time > 0) { - uint32 reduced_recast = spell.recast_time / 1000; - auto focus = GetFocusEffect(focusReduceRecastTime, spell_id); - // do stupid stuff because custom servers. - // we really should be able to just do the -= focus but since custom servers could have shorter reuse timers - // we have to make sure we don't underflow the uint32 ... - // and yes, the focus effect can be used to increase the durations (spell 38944) - if (focus > reduced_recast) { - reduced_recast = 0; - if (GetPTimers().Enabled((uint32)DiscTimer)) - GetPTimers().Clear(&database, (uint32)DiscTimer); - } else { - reduced_recast -= focus; - } - - if (reduced_recast > 0){ - instant_recast = false; - - if (GetClass() == BARD && IsCasting() && spells[spell_id].cast_time == 0) { - if (DoCastingChecksOnCaster(spell_id)) { - if (SpellFinished(spell_id, entity_list.GetMob(target), EQ::spells::CastingSlot::Discipline, 0, -1, spells[spell_id].resist_difficulty, false, -1, (uint32)DiscTimer, reduced_recast, false)) { - SendDisciplineTimer(spells[spell_id].timer_id, reduced_recast); - } - } - } - else { - if (CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline, -1, -1, 0, -1, (uint32)DiscTimer, reduced_recast)) { - SendDisciplineTimer(spells[spell_id].timer_id, reduced_recast); - } - } + if (GetClass() == BARD && IsCasting() && spells[spell_id].cast_time == 0) { + if (DoCastingChecksOnCaster(spell_id)) { + SpellFinished(spell_id, entity_list.GetMob(target), EQ::spells::CastingSlot::Discipline); } } - - if (instant_recast) { - if (GetClass() == BARD && IsCasting() && spells[spell_id].cast_time == 0) { - if (DoCastingChecksOnCaster(spell_id)) { - SpellFinished(spell_id, entity_list.GetMob(target), EQ::spells::CastingSlot::Discipline, 0, -1, spells[spell_id].resist_difficulty, false, -1, 0xFFFFFFFF, 0, false); - } - } - else { - CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline); - } + else { + CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline); } + return(true); } diff --git a/zone/mob.h b/zone/mob.h index f0af8f8b9..702031616 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -326,7 +326,7 @@ public: void CastedSpellFinished(uint16 spell_id, uint32 target_id, EQ::spells::CastingSlot slot, uint16 mana_used, uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0); bool SpellFinished(uint16 spell_id, Mob *target, EQ::spells::CastingSlot slot = EQ::spells::CastingSlot::Item, uint16 mana_used = 0, - uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false, int level_override = -1, uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, bool from_casted_spell = false); + uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false, int level_override = -1, bool from_casted_spell = false, uint32 aa_id = 0); void SendBeginCast(uint16 spell_id, uint32 casttime); virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar, int reflect_effectiveness = 0, bool use_resist_adjust = false, int16 resist_adjust = 0, bool isproc = false, int level_override = -1, int32 duration_override = 0); diff --git a/zone/spells.cpp b/zone/spells.cpp index e4decc0f6..6d4acc291 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1681,7 +1681,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo } // we're done casting, now try to apply the spell - if(!SpellFinished(spell_id, spell_target, slot, mana_used, inventory_slot, resist_adjust, false,-1, 0xFFFFFFFF, 0, true)) + if(!SpellFinished(spell_id, spell_target, slot, mana_used, inventory_slot, resist_adjust, false,-1, true)) { LogSpells("Casting of [{}] canceled: SpellFinished returned false", spell_id); // most of the cases we return false have a message already or are logic errors that shouldn't happen @@ -2300,7 +2300,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce // if you need to abort the casting, return false bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, uint16 mana_used, uint32 inventory_slot, int16 resist_adjust, bool isproc, int level_override, - uint32 timer, uint32 timer_duration, bool from_casted_spell) + bool from_casted_spell, uint32 aa_id) { Mob *ae_center = nullptr; @@ -2674,32 +2674,54 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui if (mgb) { SetMGB(false); } - /* - Set Recast Timer on spells. - */ - if(IsClient() && !isproc) + + //all spell triggers use Item slot, but don't have an item associated. We don't need to check recast timers on these. + bool is_triggered_spell = false; + if (slot == CastingSlot::Item && inventory_slot == 0xFFFFFFFF) { + is_triggered_spell = true; + } + + if (IsClient() && !isproc && !is_triggered_spell) { - //Support for bards to get disc recast timers while singing - if (GetClass() == BARD && spell_id != casting_spell_id && timer != 0xFFFFFFFF) { - CastToClient()->GetPTimers().Start(timer, timer_duration); - LogSpells("Spell [{}]: Setting bard disciple reuse timer from spell finished [{}] to [{}]", spell_id, timer, timer_duration); + //Set Item or Augment Click Recast Timer + if (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt) { + CastToClient()->SetItemRecastTimer(spell_id, inventory_slot); } + //Set Discipline Recast Timer + else if (slot == CastingSlot::Discipline) { + if (spell_id == casting_spell_id || (GetClass() == BARD && spells[spell_id].cast_time == 0 && spell_id != casting_spell_id)) { + CastToClient()->SetDisciplineRecastTimer(spell_id); + } + } + //Set AA Recast Timer. + else if (slot == CastingSlot::AltAbility){ + uint32 active_aa_id = 0; + //aa_id is only passed directly into spellfinished when a bard is using AA while casting, this supports casting an AA while clicking an instant AA. + if (GetClass() == BARD && spells[spell_id].cast_time == 0 && aa_id) { + active_aa_id = aa_id; + } + else { + active_aa_id = casting_spell_aa_id; + } + + AA::Rank *rank = zone->GetAlternateAdvancementRank(active_aa_id); - if(casting_spell_aa_id) { - AA::Rank *rank = zone->GetAlternateAdvancementRank(casting_spell_aa_id); + CastToClient()->SetAARecastTimer(rank, spell_id); - if(rank && rank->base_ability) { + if (rank && rank->base_ability) { ExpendAlternateAdvancementCharge(rank->base_ability->id); } } - else if(spell_id == casting_spell_id && casting_spell_timer != 0xFFFFFFFF) + //Set Custom Recast Timer + else if (spell_id == casting_spell_id && casting_spell_timer != 0xFFFFFFFF) { //aa new todo: aa expendable charges here CastToClient()->GetPTimers().Start(casting_spell_timer, casting_spell_timer_duration); LogSpells("Spell [{}]: Setting custom reuse timer [{}] to [{}]", spell_id, casting_spell_timer, casting_spell_timer_duration); } - else if(spells[spell_id].recast_time > 1000 && !spells[spell_id].is_discipline) { - int recast = spells[spell_id].recast_time/1000; + //Set Spell Recast Timer + else if (spells[spell_id].recast_time > 1000 && !spells[spell_id].is_discipline) { + int recast = spells[spell_id].recast_time / 1000; if (spell_id == SPELL_LAY_ON_HANDS) //lay on hands { recast -= GetAA(aaFervrentBlessing) * 420; @@ -2719,20 +2741,14 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui } recast = std::max(recast, 0); } - + LogSpells("Spell [{}]: Setting long reuse timer to [{}] s (orig [{}])", spell_id, recast, spells[spell_id].recast_time); - + if (recast > 0) { CastToClient()->GetPTimers().Start(pTimerSpellStart + spell_id, recast); } } } - /* - Set Recast Timer on item clicks, including augmenets. - */ - if(IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt)){ - CastToClient()->SetItemRecastTimer(spell_id, inventory_slot); - } if (IsNPC()) { CastToNPC()->AI_Event_SpellCastFinished(true, static_cast(slot)); @@ -2793,6 +2809,8 @@ bool Mob::ApplyBardPulse(int32 spell_id, Mob *spell_target, CastingSlot slot) { if (!SpellFinished(spell_id, spell_target, slot, spells[spell_id].mana, 0xFFFFFFFF, spells[spell_id].resist_difficulty)) { return false; } + + return true; } /////////////////////////////////////////////////////////////////////////////// @@ -6283,6 +6301,39 @@ bool Client::HasItemRecastTimer(int32 spell_id, uint32 inventory_slot) return false; } +void Client::SetDisciplineRecastTimer(int32 spell_id) { + + if (!IsValidSpell(spell_id)) { + return; + } + + if (spells[spell_id].recast_time == 0) { + return; + } + + pTimerType DiscTimer = pTimerDisciplineReuseStart + spells[spell_id].timer_id; + uint32 timer_duration = spells[spell_id].recast_time / 1000; + auto focus = GetFocusEffect(focusReduceRecastTime, spell_id); + + if (focus > timer_duration) { + timer_duration = 0; + if (GetPTimers().Enabled((uint32)DiscTimer)) { + GetPTimers().Clear(&database, (uint32)DiscTimer); + } + else { + timer_duration -= focus; + } + } + + if (timer_duration <= 0) { + return; + } + + CastToClient()->GetPTimers().Start((uint32)DiscTimer, timer_duration); + CastToClient()->SendDisciplineTimer(spells[spell_id].timer_id, timer_duration); + LogSpells("Spell [{}]: Setting disciple reuse timer [{}] to [{}]", spell_id, spells[spell_id].timer_id, timer_duration); +} + void Mob::CalcDestFromHeading(float heading, float distance, float MaxZDiff, float StartX, float StartY, float &dX, float &dY, float &dZ) { From 872d494bb6111043741022c7b1c677ff8a288e71 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 8 Feb 2022 07:36:04 -0500 Subject: [PATCH 598/624] [Bug Fix] Summon Companion causing pets to warps away. (#1972) * Update spell_effects.cpp * Update spell_effects.cpp --- zone/spell_effects.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index ff09b955d..bebf3b09f 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2163,9 +2163,12 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove snprintf(effect_desc, _EDLEN, "Call Pet"); #endif // this is cast on self, not on the pet - if(GetPet() && GetPet()->IsNPC()) - { - GetPet()->CastToNPC()->GMMove(GetX(), GetY(), GetZ(), GetHeading()); + Mob *casters_pet = GetPet(); + if(casters_pet && casters_pet->IsNPC()){ + casters_pet->CastToNPC()->GMMove(GetX(), GetY(), GetZ(), GetHeading()); + if (!casters_pet->GetTarget()) { + casters_pet->StopNavigation(); + } } break; } From e962ad3a351158bfa98f2b50a50355119453797c Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 8 Feb 2022 07:36:20 -0500 Subject: [PATCH 599/624] procs silence (#1973) --- zone/mob.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/zone/mob.cpp b/zone/mob.cpp index d6f56a8db..6f180d768 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3984,6 +3984,16 @@ void Mob::ExecWeaponProc(const EQ::ItemInstance *inst, uint16 spell_id, Mob *on, return; } + if (IsSilenced() && !IsDiscipline(spell_id)) { + MessageString(Chat::Red, SILENCED_STRING); + return; + } + + if (IsAmnesiad() && IsDiscipline(spell_id)) { + MessageString(Chat::Red, MELEE_SILENCE); + return; + } + if(inst && IsClient()) { //const cast is dirty but it would require redoing a ton of interfaces at this point //It should be safe as we don't have any truly const EQ::ItemInstance floating around anywhere. From 752e6c89f31854bab8064ee4235bec15fd255f90 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 8 Feb 2022 09:03:31 -0500 Subject: [PATCH 600/624] [Spells] Allow damage spells to heal if quest based spell mitigation is over 100 pct. (#1978) * heal from nuke * Update mob.cpp --- zone/spell_effects.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index bebf3b09f..71f98f40f 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -260,8 +260,14 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove caster->ResourceTap(-dmg, spell_id); } - dmg = -dmg; - Damage(caster, dmg, spell_id, spell.skill, false, buffslot, false); + if (dmg <= 0) { + dmg = -dmg; + Damage(caster, dmg, spell_id, spell.skill, false, buffslot, false); + } + //handles custom situation where quest function mitigation put high enough to allow damage to heal. + else { + HealDamage(dmg, caster); + } } else if(dmg > 0) { //healing spell... From 79f250da2d58ca341de5dc4165b6bd242f96f5fd Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 8 Feb 2022 18:32:13 -0500 Subject: [PATCH 601/624] [API] Perl functions added to apply spell effects directly to NPCs without requiring buffs. (#1975) * script functions working * Update perl_npc.cpp * [API] Perl functions added to apply spell effects directly to NPCs without requiring buffs. --- zone/mob_ai.cpp | 26 ++++++++++++++++++++++++-- zone/npc.h | 3 ++- zone/perl_npc.cpp | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 3 deletions(-) diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 491115d3c..0d73e7938 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -2761,9 +2761,8 @@ void NPC::ApplyAISpellEffects(StatBonuses* newbon) } // adds a spell to the list, taking into account priority and resorting list as needed. -void NPC::AddSpellEffectToNPCList(uint16 iSpellEffectID, int32 base_value, int32 limit, int32 max_value) +void NPC::AddSpellEffectToNPCList(uint16 iSpellEffectID, int32 base_value, int32 limit, int32 max_value, bool apply_bonus) { - if(!iSpellEffectID) return; @@ -2775,6 +2774,29 @@ void NPC::AddSpellEffectToNPCList(uint16 iSpellEffectID, int32 base_value, int32 t.limit = limit; t.max_value = max_value; AIspellsEffects.push_back(t); + + //we recalculate if applied from quest script. + if (apply_bonus) { + CalcBonuses(); + } +} + +void NPC::RemoveSpellEffectFromNPCList(uint16 iSpellEffectID, bool apply_bonus) +{ + auto iter = AIspellsEffects.begin(); + while (iter != AIspellsEffects.end()) + { + if ((*iter).spelleffectid == iSpellEffectID) + { + iter = AIspellsEffects.erase(iter); + continue; + } + ++iter; + } + + if (apply_bonus) { + CalcBonuses(); + } } bool IsSpellEffectInList(DBnpcspellseffects_Struct* spelleffect_list, uint16 iSpellEffectID, int32 base_value, int32 limit, int32 max_value) { diff --git a/zone/npc.h b/zone/npc.h index d3578f1d2..15d3c8ebc 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -449,8 +449,9 @@ public: uint32 GetAdventureTemplate() const { return adventure_template_id; } void AddSpellToNPCList(int16 iPriority, uint16 iSpellID, uint32 iType, int16 iManaCost, int32 iRecastDelay, int16 iResistAdjust, int8 min_hp, int8 max_hp); - void AddSpellEffectToNPCList(uint16 iSpellEffectID, int32 base_value, int32 limit, int32 max_value); + void AddSpellEffectToNPCList(uint16 iSpellEffectID, int32 base_value, int32 limit, int32 max_value, bool apply_bonus = false); void RemoveSpellFromNPCList(uint16 spell_id); + void RemoveSpellEffectFromNPCList(uint16 iSpellEffectID, bool apply_bonus = false); Timer *GetRefaceTimer() const { return reface_timer; } const uint32 GetAltCurrencyType() const { return NPCTypedata->alt_currency_type; } diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index 4bd19e4e4..a10acbbe8 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -1845,6 +1845,40 @@ XS(XS_NPC_GetLootList) { } } +XS(XS_NPC_AddAISpellEffect); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_AddAISpellEffect) { + dXSARGS; + if (items != 5) + Perl_croak(aTHX_ "Usage: NPC::AddAISpellEffect(THIS, spell_effect id, base_value, limit_value, max_value)"); // @categories Spells and Disciplines + { + NPC *THIS; + + int spell_effect_id = (int)SvIV(ST(1)); + int base_value = (int)SvIV(ST(2)); + int limit_value = (int)SvIV(ST(3)); + int max_value = (int)SvIV(ST(4)); + + VALIDATE_THIS_IS_NPC; + THIS->AddSpellEffectToNPCList(spell_effect_id, base_value, limit_value, max_value, true); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_RemoveAISpellEffect); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_RemoveAISpellEffect) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::RemoveAISpellEffect(THIS, int spelleffect_id)"); // @categories Spells and Disciplines + { + NPC *THIS; + int spell_effect_id = (int)SvIV(ST(1)); + VALIDATE_THIS_IS_NPC; + THIS->RemoveSpellEffectFromNPCList(spell_effect_id, true); + } + XSRETURN_EMPTY; +} + + #ifdef __cplusplus extern "C" #endif @@ -1864,6 +1898,7 @@ XS(boot_NPC) { XS_VERSION_BOOTCHECK; newXSproto(strcpy(buf, "AI_SetRoambox"), XS_NPC_AI_SetRoambox, file, "$$$$$$;$$"); newXSproto(strcpy(buf, "AddAISpell"), XS_NPC_AddSpellToNPCList, file, "$$$$$$$"); + newXSproto(strcpy(buf, "AddAISpellEffect"), XS_NPC_AddAISpellEffect, file, "$$$$$"); newXSproto(strcpy(buf, "AddCash"), XS_NPC_AddCash, file, "$$$$$"); newXSproto(strcpy(buf, "AddDefensiveProc"), XS_NPC_AddDefensiveProc, file, "$$$"); newXSproto(strcpy(buf, "AddItem"), XS_NPC_AddItem, file, "$$;$$$$$$$$"); @@ -1939,6 +1974,7 @@ XS(boot_NPC) { newXSproto(strcpy(buf, "PickPocket"), XS_NPC_PickPocket, file, "$$"); newXSproto(strcpy(buf, "RecalculateSkills"), XS_NPC_RecalculateSkills, file, "$"); newXSproto(strcpy(buf, "RemoveAISpell"), XS_NPC_RemoveSpellFromNPCList, file, "$$"); + newXSproto(strcpy(buf, "RemoveAISpellEffect"), XS_NPC_RemoveAISpellEffect, file, "$$"); newXSproto(strcpy(buf, "RemoveCash"), XS_NPC_RemoveCash, file, "$"); newXSproto(strcpy(buf, "RemoveDefensiveProc"), XS_NPC_RemoveDefensiveProc, file, "$$"); newXSproto(strcpy(buf, "RemoveFromHateList"), XS_NPC_RemoveFromHateList, file, "$$"); From f65a6d27616ba9dc48460057e3ddbacbd578b77c Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 8 Feb 2022 20:46:59 -0500 Subject: [PATCH 602/624] [Quest API] Add AddAISpellEffect(spell_effect_id, base_value, limit_value, max_value) and RemoveAISpellEffect(spell_effect_id) to Lua. (#1981) - Add npc:AddAISpellEffect(spell_effect_id, base_value, limit_value, max_value) to Lua. - Add npc:RemoveAISpellEffect(spell_effect_id) to Lua. --- zone/lua_npc.cpp | 21 +++++++++++++++++++++ zone/lua_npc.h | 3 +++ zone/mob_ai.cpp | 11 +++++++++++ zone/npc.h | 1 + zone/perl_npc.cpp | 31 ++++++++++++++++++++++++------- 5 files changed, 60 insertions(+), 7 deletions(-) diff --git a/zone/lua_npc.cpp b/zone/lua_npc.cpp index cb569c566..d82f936e0 100644 --- a/zone/lua_npc.cpp +++ b/zone/lua_npc.cpp @@ -635,6 +635,24 @@ Lua_NPC_Loot_List Lua_NPC::GetLootList(lua_State* L) { return ret; } +void Lua_NPC::AddAISpellEffect(int spell_effect_id, int base_value, int limit_value, int max_value) +{ + Lua_Safe_Call_Void(); + self->AddSpellEffectToNPCList(spell_effect_id, base_value, limit_value, max_value, true); +} + +void Lua_NPC::RemoveAISpellEffect(int spell_effect_id) +{ + Lua_Safe_Call_Void(); + self->RemoveSpellEffectFromNPCList(spell_effect_id, true); +} + +bool Lua_NPC::HasAISpellEffect(int spell_effect_id) +{ + Lua_Safe_Call_Bool(); + return self->HasAISpellEffect(spell_effect_id); +} + luabind::scope lua_register_npc() { return luabind::class_("NPC") .def(luabind::constructor<>()) @@ -642,6 +660,7 @@ luabind::scope lua_register_npc() { .def("AI_SetRoambox", (void(Lua_NPC::*)(float,float,float,float,float,uint32,uint32))&Lua_NPC::AI_SetRoambox) .def("AddAISpell", (void(Lua_NPC::*)(int,int,int,int,int,int))&Lua_NPC::AddAISpell) .def("AddAISpell", (void(Lua_NPC::*)(int,int,int,int,int,int,int,int))&Lua_NPC::AddAISpell) + .def("AddAISpellEffect", (void(Lua_NPC::*)(int,int,int,int))&Lua_NPC::AddAISpellEffect) .def("AddCash", (void(Lua_NPC::*)(int,int,int,int))&Lua_NPC::AddCash) .def("AddItem", (void(Lua_NPC::*)(int,int))&Lua_NPC::AddItem) .def("AddItem", (void(Lua_NPC::*)(int,int,bool))&Lua_NPC::AddItem) @@ -711,6 +730,7 @@ luabind::scope lua_register_npc() { .def("GetSwarmOwner", (int(Lua_NPC::*)(void))&Lua_NPC::GetSwarmOwner) .def("GetSwarmTarget", (int(Lua_NPC::*)(void))&Lua_NPC::GetSwarmTarget) .def("GetWaypointMax", (int(Lua_NPC::*)(void))&Lua_NPC::GetWaypointMax) + .def("HasAISpellEffect", (bool(Lua_NPC::*)(int))&Lua_NPC::HasAISpellEffect) .def("HasItem", (bool(Lua_NPC::*)(uint32))&Lua_NPC::HasItem) .def("IsAnimal", (bool(Lua_NPC::*)(void))&Lua_NPC::IsAnimal) .def("IsGuarding", (bool(Lua_NPC::*)(void))&Lua_NPC::IsGuarding) @@ -726,6 +746,7 @@ luabind::scope lua_register_npc() { .def("PickPocket", (void(Lua_NPC::*)(Lua_Client))&Lua_NPC::PickPocket) .def("RecalculateSkills", (void(Lua_NPC::*)(void))&Lua_NPC::RecalculateSkills) .def("RemoveAISpell", (void(Lua_NPC::*)(int))&Lua_NPC::RemoveAISpell) + .def("RemoveAISpellEffect", (void(Lua_NPC::*)(int))&Lua_NPC::RemoveAISpellEffect) .def("RemoveCash", (void(Lua_NPC::*)(void))&Lua_NPC::RemoveCash) .def("RemoveItem", (void(Lua_NPC::*)(int))&Lua_NPC::RemoveItem) .def("RemoveItem", (void(Lua_NPC::*)(int,int))&Lua_NPC::RemoveItem) diff --git a/zone/lua_npc.h b/zone/lua_npc.h index 0e6ada5a7..58cae136c 100644 --- a/zone/lua_npc.h +++ b/zone/lua_npc.h @@ -149,6 +149,9 @@ public: float GetHealScale(); float GetSpellScale(); Lua_NPC_Loot_List GetLootList(lua_State* L); + void AddAISpellEffect(int spell_effect_id, int base_value, int limit_value, int max_value); + void RemoveAISpellEffect(int spell_effect_id); + bool HasAISpellEffect(int spell_effect_id); }; #endif diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 0d73e7938..906b3b5c9 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -2799,6 +2799,17 @@ void NPC::RemoveSpellEffectFromNPCList(uint16 iSpellEffectID, bool apply_bonus) } } +bool NPC::HasAISpellEffect(uint16 spell_effect_id) +{ + for (const auto& spell_effect : AIspellsEffects) { + if (spell_effect.spelleffectid == spell_effect_id) { + return true; + } + } + + return false; +} + bool IsSpellEffectInList(DBnpcspellseffects_Struct* spelleffect_list, uint16 iSpellEffectID, int32 base_value, int32 limit, int32 max_value) { for (uint32 i=0; i < spelleffect_list->numentries; i++) { if (spelleffect_list->entries[i].spelleffectid == iSpellEffectID && spelleffect_list->entries[i].base_value == base_value diff --git a/zone/npc.h b/zone/npc.h index 15d3c8ebc..aa9089e4f 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -452,6 +452,7 @@ public: void AddSpellEffectToNPCList(uint16 iSpellEffectID, int32 base_value, int32 limit, int32 max_value, bool apply_bonus = false); void RemoveSpellFromNPCList(uint16 spell_id); void RemoveSpellEffectFromNPCList(uint16 iSpellEffectID, bool apply_bonus = false); + bool HasAISpellEffect(uint16 spell_effect_id); Timer *GetRefaceTimer() const { return reface_timer; } const uint32 GetAltCurrencyType() const { return NPCTypedata->alt_currency_type; } diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index a10acbbe8..1b6794d72 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -1849,14 +1849,14 @@ XS(XS_NPC_AddAISpellEffect); /* prototype to pass -Wmissing-prototypes */ XS(XS_NPC_AddAISpellEffect) { dXSARGS; if (items != 5) - Perl_croak(aTHX_ "Usage: NPC::AddAISpellEffect(THIS, spell_effect id, base_value, limit_value, max_value)"); // @categories Spells and Disciplines + Perl_croak(aTHX_ "Usage: NPC::AddAISpellEffect(THIS, int spell_effect_id, int base_value, int limit_value, int max_value)"); // @categories Spells and Disciplines { NPC *THIS; - int spell_effect_id = (int)SvIV(ST(1)); - int base_value = (int)SvIV(ST(2)); - int limit_value = (int)SvIV(ST(3)); - int max_value = (int)SvIV(ST(4)); + int spell_effect_id = (int) SvIV(ST(1)); + int base_value = (int) SvIV(ST(2)); + int limit_value = (int) SvIV(ST(3)); + int max_value = (int) SvIV(ST(4)); VALIDATE_THIS_IS_NPC; THIS->AddSpellEffectToNPCList(spell_effect_id, base_value, limit_value, max_value, true); @@ -1868,16 +1868,32 @@ XS(XS_NPC_RemoveAISpellEffect); /* prototype to pass -Wmissing-prototypes */ XS(XS_NPC_RemoveAISpellEffect) { dXSARGS; if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::RemoveAISpellEffect(THIS, int spelleffect_id)"); // @categories Spells and Disciplines + Perl_croak(aTHX_ "Usage: NPC::RemoveAISpellEffect(THIS, int spell_effect_id)"); // @categories Spells and Disciplines { NPC *THIS; - int spell_effect_id = (int)SvIV(ST(1)); + int spell_effect_id = (int) SvIV(ST(1)); VALIDATE_THIS_IS_NPC; THIS->RemoveSpellEffectFromNPCList(spell_effect_id, true); } XSRETURN_EMPTY; } +XS(XS_NPC_HasAISpellEffect); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_HasAISpellEffect) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::HasAISpellEffect(THIS, int spell_effect_id)"); // @categories Spells and Disciplines + { + NPC *THIS; + bool has_spell_effect = false; + int spell_effect_id = (int) SvIV(ST(1)); + VALIDATE_THIS_IS_NPC; + has_spell_effect = THIS->HasAISpellEffect(spell_effect_id); + ST(0) = boolSV(has_spell_effect); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} #ifdef __cplusplus extern "C" @@ -1959,6 +1975,7 @@ XS(boot_NPC) { newXSproto(strcpy(buf, "GetSwarmOwner"), XS_NPC_GetSwarmOwner, file, "$"); newXSproto(strcpy(buf, "GetSwarmTarget"), XS_NPC_GetSwarmTarget, file, "$"); newXSproto(strcpy(buf, "GetWaypointMax"), XS_NPC_GetWaypointMax, file, "$"); + newXSproto(strcpy(buf, "HasAISpellEffect"), XS_NPC_HasAISpellEffect, file, "$$"); newXSproto(strcpy(buf, "HasItem"), XS_NPC_HasItem, file, "$$"); newXSproto(strcpy(buf, "IsAnimal"), XS_NPC_IsAnimal, file, "$"); newXSproto(strcpy(buf, "IsGuarding"), XS_NPC_IsGuarding, file, "$"); From 1f560529da5900b057abf780aefb35ded16ebfaf Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 9 Feb 2022 15:07:38 -0500 Subject: [PATCH 603/624] [Bug Fix] Bard update fixes 1 (#1982) * fix for bard item charge consumables * [Bug Fix] Bards not consuming item click charges on instant cast items. * [Bug Fix] Bard update fixes 1 bards not respecting deity/race/class restrictions on instant cast items --- zone/client_packet.cpp | 62 +++++------ zone/mob.h | 2 + zone/spells.cpp | 231 +++++++++++++++++++++++------------------ 3 files changed, 163 insertions(+), 132 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index bab86a465..71af0465c 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -8792,39 +8792,41 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) spell_id = item->Click.Effect; bool is_casting_bard_song = false; - if - ( - spell_id > 0 && - ( - !IsValidSpell(spell_id) || - casting_spell_id || - delaytimer || - spellend_timer.Enabled() || - IsStunned() || - IsFeared() || - IsMezzed() || - DivineAura() || - (spells[spell_id].target_type == ST_Ring) || - (IsSilenced() && !IsDiscipline(spell_id)) || - (IsAmnesiad() && IsDiscipline(spell_id)) || - (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) || - (inst->IsScaling() && inst->GetExp() <= 0) // charms don't have spells when less than 0 - ) - ) - { - /* - Bards on live can click items while casting spell gems, it stops that song cast and replaces it with item click cast. - Can not click while casting other items. - */ - if (GetClass() == BARD && IsCasting() && casting_spell_slot < CastingSlot::MaxGems) - { - is_casting_bard_song = true; - } - else - { + if (spell_id > 0) { + + if (!IsValidSpell(spell_id) || + IsStunned() || + IsFeared() || + IsMezzed() || + DivineAura() || + (spells[spell_id].target_type == ST_Ring) || + (IsSilenced() && !IsDiscipline(spell_id)) || + (IsAmnesiad() && IsDiscipline(spell_id)) || + (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) || + (inst->IsScaling() && inst->GetExp() <= 0)) { // charms don't have spells when less than 0 + SendSpellBarEnable(spell_id); return; } + + if (casting_spell_id || + delaytimer || + spellend_timer.Enabled()) { + + /* + Bards on live can click items while casting spell gems, it stops that song cast and replaces it with item click cast. + Can not click while casting other items. + */ + if (GetClass() == BARD && IsCasting() && casting_spell_slot < CastingSlot::MaxGems) + { + is_casting_bard_song = true; + } + else + { + SendSpellBarEnable(spell_id); + return; + } + } } // Modern clients don't require pet targeted for item clicks that are ST_Pet if (spell_id > 0 && (spells[spell_id].target_type == ST_Pet || spells[spell_id].target_type == ST_SummonedPet)) diff --git a/zone/mob.h b/zone/mob.h index 702031616..c2c28296f 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -357,6 +357,8 @@ public: void ApplySpellEffectIllusion(int32 spell_id, Mob* caster, int buffslot, int base, int limit, int max); void ApplyIllusionToCorpse(int32 spell_id, Corpse* new_corpse); void SendIllusionWearChange(Client* c); + int16 GetItemSlotToConsumeCharge(int32 spell_id, uint32 inventory_slot); + bool CheckItemRaceClassDietyRestrictionsOnCast(uint32 inventory_slot); //Bard bool ApplyBardPulse(int32 spell_id, Mob *spell_target, EQ::spells::CastingSlot slot); diff --git a/zone/spells.cpp b/zone/spells.cpp index 6d4acc291..b9b8a66ef 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -206,55 +206,10 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, } //Added to prevent MQ2 exploitation of equipping normally-unequippable/clickable items with effects and clicking them for benefits. - if(item_slot && IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt)) + if (item_slot && IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt)) { - EQ::ItemInstance *itm = CastToClient()->GetInv().GetItem(item_slot); - int bitmask = 1; - bitmask = bitmask << (CastToClient()->GetClass() - 1); - if( itm && itm->GetItem()->Classes != 65535 ) { - if ((itm->GetItem()->Click.Type == EQ::item::ItemEffectEquipClick) && !(itm->GetItem()->Classes & bitmask)) { - if (CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoF) { - // They are casting a spell from an item that requires equipping but shouldn't let them equip it - LogError("HACKER: [{}] (account: [{}]) attempted to click an equip-only effect on item [{}] (id: [{}]) which they shouldn't be able to equip!", - CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID); - database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking equip-only item with an invalid class"); - } - else { - MessageString(Chat::Red, MUST_EQUIP_ITEM); - } - return(false); - } - if ((itm->GetItem()->Click.Type == EQ::item::ItemEffectClick2) && !(itm->GetItem()->Classes & bitmask)) { - if (CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoF) { - // They are casting a spell from an item that they don't meet the race/class requirements to cast - LogError("HACKER: [{}] (account: [{}]) attempted to click a race/class restricted effect on item [{}] (id: [{}]) which they shouldn't be able to click!", - CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID); - database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking race/class restricted item with an invalid class"); - } - else { - if (CastToClient()->ClientVersion() >= EQ::versions::ClientVersion::RoF) - { - // Line 181 in eqstr_us.txt was changed in RoF+ - Message(Chat::Yellow, "Your race, class, or deity cannot use this item."); - } - else - { - MessageString(Chat::Red, CANNOT_USE_ITEM); - } - } - return(false); - } - } - if (itm && (itm->GetItem()->Click.Type == EQ::item::ItemEffectEquipClick) && item_slot > EQ::invslot::EQUIPMENT_END){ - if (CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoF) { - // They are attempting to cast a must equip clicky without having it equipped - LogError("HACKER: [{}] (account: [{}]) attempted to click an equip-only effect on item [{}] (id: [{}]) without equiping it!", CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID); - database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking equip-only item without equiping it"); - } - else { - MessageString(Chat::Red, MUST_EQUIP_ITEM); - } - return(false); + if (!CheckItemRaceClassDietyRestrictionsOnCast(item_slot)) { + return false; } } @@ -1627,57 +1582,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo if(IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt) && inventory_slot != 0xFFFFFFFF) // 10 is an item { - bool fromaug = false; - EQ::ItemData* augitem = nullptr; - uint32 recastdelay = 0; - int recasttype = 0; - - while (true) { - if (item == nullptr) - break; - - for (int r = EQ::invaug::SOCKET_BEGIN; r <= EQ::invaug::SOCKET_END; r++) { - const EQ::ItemInstance* aug_i = item->GetAugment(r); - - if (!aug_i) - continue; - const EQ::ItemData* aug = aug_i->GetItem(); - if (!aug) - continue; - - if (aug->Click.Effect == spell_id) - { - recastdelay = aug_i->GetItem()->RecastDelay; - recasttype = aug_i->GetItem()->RecastType; - fromaug = true; - break; - } - } - - break; - } - - if (item && item->IsClassCommon() && (item->GetItem()->Click.Effect == spell_id) && item->GetCharges() || fromaug) - { - //const ItemData* item = item->GetItem(); - int16 charges = item->GetItem()->MaxCharges; - - if(fromaug) { charges = -1; } //Don't destroy the parent item - - if(charges > -1) { // charged item, expend a charge - LogSpells("Spell [{}]: Consuming a charge from item [{}] ([{}]) which had [{}]/[{}] charges", spell_id, item->GetItem()->Name, item->GetItem()->ID, item->GetCharges(), item->GetItem()->MaxCharges); - DeleteChargeFromSlot = inventory_slot; - } else { - LogSpells("Spell [{}]: Cast from unlimited charge item [{}] ([{}]) ([{}] charges)", spell_id, item->GetItem()->Name, item->GetItem()->ID, item->GetItem()->MaxCharges); - } - } - else - { - LogSpells("Item used to cast spell [{}] was missing from inventory slot [{}] after casting!", spell_id, inventory_slot); - Message(Chat::Red, "Casting Error: Active casting item not found in inventory slot %i", inventory_slot); - InterruptSpell(); - return; - } + DeleteChargeFromSlot = GetItemSlotToConsumeCharge(spell_id, inventory_slot); } // we're done casting, now try to apply the spell @@ -1699,8 +1604,9 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo TryTriggerOnCastFocusEffect(focusTriggerOnCast, spell_id); - if(DeleteChargeFromSlot >= 0) + if (DeleteChargeFromSlot >= 0) { CastToClient()->DeleteItemInInventory(DeleteChargeFromSlot, 1, true); + } // // at this point the spell has successfully been cast @@ -6638,7 +6544,128 @@ void Mob::DoBardCastingFromItemClick(bool is_casting_bard_song, uint32 cast_time CastSpell(spell_id, target_id, CastingSlot::Item, cast_time, 0, 0, item_slot); } //Instant cast items do not stop bard songs or interrupt casting. - else if (DoCastingChecksOnCaster(spell_id)) { - SpellFinished(spell_id, entity_list.GetMob(target_id), CastingSlot::Item, 0, item_slot); + else if (CheckItemRaceClassDietyRestrictionsOnCast(item_slot) && DoCastingChecksOnCaster(spell_id)) { + int16 DeleteChargeFromSlot = GetItemSlotToConsumeCharge(spell_id, item_slot); + if (SpellFinished(spell_id, entity_list.GetMob(target_id), CastingSlot::Item, 0, item_slot)) { + if (IsClient() && DeleteChargeFromSlot >= 0) { + CastToClient()->DeleteItemInInventory(DeleteChargeFromSlot, 1, true); + } + } + } +} + +int16 Mob::GetItemSlotToConsumeCharge(int32 spell_id, uint32 inventory_slot) +{ + int16 DeleteChargeFromSlot = -1; + + if (!IsClient() || inventory_slot == 0xFFFFFFFF) { + return DeleteChargeFromSlot; + } + + EQ::ItemInstance *item = nullptr; + item = CastToClient()->GetInv().GetItem(inventory_slot); + + bool fromaug = false; + EQ::ItemData* augitem = nullptr; + + while (true) { + if (item == nullptr) + break; + + for (int r = EQ::invaug::SOCKET_BEGIN; r <= EQ::invaug::SOCKET_END; r++) { + const EQ::ItemInstance* aug_i = item->GetAugment(r); + + if (!aug_i) { + continue; + } + const EQ::ItemData* aug = aug_i->GetItem(); + if (!aug) { + continue; + } + if (aug->Click.Effect == spell_id){ + fromaug = true; + break; + } + } + + break; + } + + if (item && item->IsClassCommon() && (item->GetItem()->Click.Effect == spell_id) && item->GetCharges() || fromaug){ + int16 charges = item->GetItem()->MaxCharges; + + if (fromaug) { charges = -1; } //Don't destroy the parent item + + if (charges > -1) { // charged item, expend a charge + LogSpells("Spell [{}]: Consuming a charge from item [{}] ([{}]) which had [{}]/[{}] charges", spell_id, item->GetItem()->Name, item->GetItem()->ID, item->GetCharges(), item->GetItem()->MaxCharges); + DeleteChargeFromSlot = inventory_slot; + } + else { + LogSpells("Spell [{}]: Cast from unlimited charge item [{}] ([{}]) ([{}] charges)", spell_id, item->GetItem()->Name, item->GetItem()->ID, item->GetItem()->MaxCharges); + } + } + else{ + LogSpells("Item used to cast spell [{}] was missing from inventory slot [{}] after casting!", spell_id, inventory_slot); + Message(Chat::Red, "Casting Error: Active casting item not found in inventory slot %i", inventory_slot); + InterruptSpell(); + return DeleteChargeFromSlot; + } + return DeleteChargeFromSlot; +} + +bool Mob::CheckItemRaceClassDietyRestrictionsOnCast(uint32 inventory_slot) { + + if (inventory_slot == 0xFFFFFFFF) { + return false; + } + + //Added to prevent MQ2 exploitation of equipping normally-unequippable/clickable items with effects and clicking them for benefits. + EQ::ItemInstance *itm = CastToClient()->GetInv().GetItem(inventory_slot); + int bitmask = 1; + bitmask = bitmask << (CastToClient()->GetClass() - 1); + if (itm && itm->GetItem()->Classes != 65535) { + if ((itm->GetItem()->Click.Type == EQ::item::ItemEffectEquipClick) && !(itm->GetItem()->Classes & bitmask)) { + if (CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoF) { + // They are casting a spell from an item that requires equipping but shouldn't let them equip it + LogError("HACKER: [{}] (account: [{}]) attempted to click an equip-only effect on item [{}] (id: [{}]) which they shouldn't be able to equip!", + CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID); + database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking equip-only item with an invalid class"); + } + else { + MessageString(Chat::Red, MUST_EQUIP_ITEM); + } + return(false); + } + if ((itm->GetItem()->Click.Type == EQ::item::ItemEffectClick2) && !(itm->GetItem()->Classes & bitmask)) { + if (CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoF) { + // They are casting a spell from an item that they don't meet the race/class requirements to cast + LogError("HACKER: [{}] (account: [{}]) attempted to click a race/class restricted effect on item [{}] (id: [{}]) which they shouldn't be able to click!", + CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID); + database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking race/class restricted item with an invalid class"); + } + else { + if (CastToClient()->ClientVersion() >= EQ::versions::ClientVersion::RoF) + { + // Line 181 in eqstr_us.txt was changed in RoF+ + Message(Chat::Yellow, "Your race, class, or deity cannot use this item."); + } + else + { + MessageString(Chat::Red, CANNOT_USE_ITEM); + } + } + return(false); + } + } + if (itm && (itm->GetItem()->Click.Type == EQ::item::ItemEffectEquipClick) && inventory_slot > EQ::invslot::EQUIPMENT_END) { + if (CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoF) { + // They are attempting to cast a must equip clicky without having it equipped + LogError("HACKER: [{}] (account: [{}]) attempted to click an equip-only effect on item [{}] (id: [{}]) without equiping it!", CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID); + database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking equip-only item without equiping it"); + } + else { + MessageString(Chat::Red, MUST_EQUIP_ITEM); + } + return(false); } } From f0bf285836f6c53c47626e9fe2b650c87878bc3c Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 9 Feb 2022 15:12:39 -0500 Subject: [PATCH 604/624] [Spells] Support for SPA 194 SE_FadingMemories to use max level checks on aggroed mobs (#1979) * escape fix for different target types * implemented max level for fade * test * update * update * support modern limits * Update ruletypes.h * update * [Spells] Support for SPA 194 SE_FadingMemories to use max level checks on aggroed mobs not sure why this code got removed, maybe merge error. --- common/ruletypes.h | 1 + common/spdat.h | 4 ++-- zone/client.h | 2 +- zone/common.h | 3 ++- zone/entity.cpp | 28 +++++++++++++++++++++++++++- zone/entity.h | 1 + zone/spell_effects.cpp | 34 +++++++++++++++++++++++++++++++++- 7 files changed, 67 insertions(+), 6 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 4acd79dbd..6dec50fa3 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -410,6 +410,7 @@ RULE_INT(Spells, ClericInnateHealFocus, 5, "Clerics on live get a 5 pct innate h RULE_BOOL(Spells, DOTsScaleWithSpellDmg, false, "Allow SpellDmg stat to affect DoT spells") RULE_BOOL(Spells, HOTsScaleWithHealAmt, false, "Allow HealAmt stat to affect HoT spells") RULE_BOOL(Spells, CompoundLifetapHeals, true, "True: Lifetap heals calculate damage bonuses and then heal bonuses. False: Lifetaps heal using the amount damaged to mob.") +RULE_BOOL(Spells, UseFadingMemoriesMaxLevel, false, "Enables to limit field in spell data to set the max level that over which an NPC will ignore fading memories effect and not lose aggro.") RULE_CATEGORY_END() RULE_CATEGORY(Combat) diff --git a/common/spdat.h b/common/spdat.h index 7e0582f91..caa80ce54 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -916,8 +916,8 @@ typedef enum { #define SE_EndurancePool 190 // implemented #define SE_Amnesia 191 // implemented - Silence vs Melee Effect #define SE_Hate 192 // implemented - Instant and hate over time. -#define SE_SkillAttack 193 // implemented -#define SE_FadingMemories 194 // implemented +#define SE_SkillAttack 193 // implemented, +#define SE_FadingMemories 194 // implemented, @Aggro, Remove from hate lists and make invisible. Can set max level of NPCs that can be affected. base: success chance, limit: max level (ROF2), max: max level (modern client), Note: Support for max level requires Rule (Spells, UseFadingMemoriesMaxLevel) to be true. If used from limit field, then it set as the level, ie. max level of 75 would use limit value of 75. If set from max field, max level 75 would use max value of 1075, if you want to set it so it checks a level range above the spell target then for it to only work on mobs 5 levels or below you set max value to 5. #define SE_StunResist 195 // implemented #define SE_StrikeThrough 196 // implemented #define SE_SkillDamageTaken 197 // implemented diff --git a/zone/client.h b/zone/client.h index 3b3b0ac84..5cbc203ac 100644 --- a/zone/client.h +++ b/zone/client.h @@ -980,7 +980,7 @@ public: //bool DecreaseByType(uint32 type, uint8 amt); bool DecreaseByID(uint32 type, int16 quantity); uint8 SlotConvert2(uint8 slot); //Maybe not needed. - void Escape(); //AA Escape + void Escape(); //keep or quest function void DisenchantSummonedBags(bool client_update = true); void RemoveNoRent(bool client_update = true); void RemoveDuplicateLore(bool client_update = true); diff --git a/zone/common.h b/zone/common.h index 795391ad3..31299d3b4 100644 --- a/zone/common.h +++ b/zone/common.h @@ -209,7 +209,8 @@ enum { IMMUNE_AGGRO_CLIENT = 49, IMMUNE_AGGRO_NPC = 50, MODIFY_AVOID_DAMAGE = 51, //Modify by percent the NPCs chance to riposte, block, parry or dodge individually, or for all skills - MAX_SPECIAL_ATTACK = 52 + IMMUNE_FADING_MEMORIES = 52, + MAX_SPECIAL_ATTACK = 53 }; typedef enum { //fear states diff --git a/zone/entity.cpp b/zone/entity.cpp index ad9da930d..0aa2aba78 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1514,7 +1514,7 @@ void EntityList::RemoveFromTargets(Mob *mob, bool RemoveFromXTargets) if (!m) continue; - if (RemoveFromXTargets) { + if (RemoveFromXTargets && mob) { if (m->IsClient() && (mob->CheckAggro(m) || mob->IsOnFeignMemory(m))) m->CastToClient()->RemoveXTarget(mob, false); // FadingMemories calls this function passing the client. @@ -1526,6 +1526,32 @@ void EntityList::RemoveFromTargets(Mob *mob, bool RemoveFromXTargets) } } +void EntityList::RemoveFromTargetsFadingMemories(Mob *spell_target, bool RemoveFromXTargets, uint32 max_level) +{ + for (auto &e : mob_list) { + auto &mob = e.second; + + if (!mob) { + continue; + } + + if (max_level && mob->GetLevel() > max_level) + continue; + + if (mob->GetSpecialAbility(IMMUNE_FADING_MEMORIES)) + continue; + + if (RemoveFromXTargets && spell_target) { + if (mob->IsClient() && (spell_target->CheckAggro(mob) || spell_target->IsOnFeignMemory(mob))) + mob->CastToClient()->RemoveXTarget(spell_target, false); + else if (spell_target->IsClient() && (mob->CheckAggro(spell_target) || mob->IsOnFeignMemory(spell_target))) + spell_target->CastToClient()->RemoveXTarget(mob, false); + } + + mob->RemoveFromHateList(spell_target); + } +} + void EntityList::RemoveFromXTargets(Mob *mob) { auto it = client_list.begin(); diff --git a/zone/entity.h b/zone/entity.h index a743a18b0..bc44cb2c7 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -389,6 +389,7 @@ public: void ExpeditionWarning(uint32 minutes_left); void RemoveFromTargets(Mob* mob, bool RemoveFromXTargets = false); + void RemoveFromTargetsFadingMemories(Mob* spell_target, bool RemoveFromXTargets = false, uint32 max_level = 0); void RemoveFromXTargets(Mob* mob); void RemoveFromAutoXTargets(Mob* mob); void ReplaceWithTarget(Mob* pOldMob, Mob*pNewTarget); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 71f98f40f..f3701f316 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2208,9 +2208,41 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Fading Memories"); #endif + int max_level = 0; + + if (RuleB(Spells, UseFadingMemoriesMaxLevel)) { + //handle ROF2 era where limit value determines max level + if (spells[spell_id].limit_value[i]) { + max_level = spells[spell_id].limit_value[i]; + } + //handle modern client era where max value determines max level or range above client. + else if (spells[spell_id].max_value[i]) { + if (spells[spell_id].max_value[i] >= 1000) { + max_level = 1000 - spells[spell_id].max_value[i]; + } + else { + max_level = GetLevel() + spells[spell_id].max_value[i]; + } + } + } + if(zone->random.Roll(spells[spell_id].base_value[i])) { if (IsClient()) { - CastToClient()->Escape(); + int pre_aggro_count = CastToClient()->GetAggroCount(); + entity_list.RemoveFromTargetsFadingMemories(this, true, max_level); + SetInvisible(Invisibility::Invisible); + int post_aggro_count = CastToClient()->GetAggroCount(); + if (RuleB(Spells, UseFadingMemoriesMaxLevel)) { + if (pre_aggro_count == post_aggro_count) { + Message(Chat::SpellFailure, "You failed to escape from all your opponents."); + break; + } + else if (post_aggro_count) { + Message(Chat::SpellFailure, "You failed to escape from combat but you evade some of your opponents."); + break; + } + } + MessageString(Chat::Skills, ESCAPE); } else{ entity_list.RemoveFromTargets(caster); From a6d1652f449cfec89e899be5c89f6e362133d0b6 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 9 Feb 2022 18:02:45 -0500 Subject: [PATCH 605/624] fixed (#1983) --- zone/aa.cpp | 9 ++++++--- zone/spells.cpp | 6 +++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/zone/aa.cpp b/zone/aa.cpp index a05f9dc47..b5a85f5e6 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -1333,9 +1333,12 @@ void Client::SetAARecastTimer(AA::Rank *rank_in, int32 spell_id) { return; } - //calculate AA cooldown - int timer_duration = rank_in->recast_time - GetAlternateAdvancementCooldownReduction(rank_in); - + int timer_duration = rank_in->recast_time; + + if (timer_duration) { + timer_duration = rank_in->recast_time - GetAlternateAdvancementCooldownReduction(rank_in); + } + if (timer_duration <= 0) { return; } diff --git a/zone/spells.cpp b/zone/spells.cpp index b9b8a66ef..93e649a7d 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -6226,9 +6226,9 @@ void Client::SetDisciplineRecastTimer(int32 spell_id) { if (GetPTimers().Enabled((uint32)DiscTimer)) { GetPTimers().Clear(&database, (uint32)DiscTimer); } - else { - timer_duration -= focus; - } + } + else { + timer_duration -= focus; } if (timer_duration <= 0) { From fbbbd3b09d02db4c160a008e2d4f57fc542cd18a Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 9 Feb 2022 23:24:18 -0500 Subject: [PATCH 606/624] [Bug Fix] PR 1982 (#1985) * Update spells.cpp * [Bug Fix] PR 1982 --- zone/effects.cpp | 4 +++- zone/spells.cpp | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index 98ebb32dd..149dc0742 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -806,7 +806,9 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) { } } else { - CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline); + if (!CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline)) { + return false; + } } return(true); diff --git a/zone/spells.cpp b/zone/spells.cpp index 93e649a7d..f54d3fbcb 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -209,6 +209,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, if (item_slot && IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt)) { if (!CheckItemRaceClassDietyRestrictionsOnCast(item_slot)) { + StopCastSpell(spell_id, send_spellbar_enable); return false; } } @@ -6668,4 +6669,6 @@ bool Mob::CheckItemRaceClassDietyRestrictionsOnCast(uint32 inventory_slot) { } return(false); } + + return true; } From d656be6be41100f56b738d038544f90f24c211b5 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 10 Feb 2022 14:58:28 -0500 Subject: [PATCH 607/624] [Bug Fix] Fix for PR1954 target restriction with npcpc_only_flag from groupbuffs (#1986) * Update spells.cpp * Update spells.cpp * [Bug Fix] Fix for PR1954 target restriction with npcpc_only_flag from groupbuffs disc failure log * [Bug Fix] Fix for PR1954 target restriction with npcpc_only_flag from groupbuffs improved HasItemRecastTimer check --- zone/effects.cpp | 1 + zone/spells.cpp | 71 +++++++++++++++++++++++++++++++----------------- 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index 149dc0742..cfb9dbdf9 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -807,6 +807,7 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) { } else { if (!CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline)) { + LogSpells("Discipline [{}] failed at cast spell.", spell_id); return false; } } diff --git a/zone/spells.cpp b/zone/spells.cpp index f54d3fbcb..1bfde6a53 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -697,23 +697,30 @@ bool Mob::DoCastingChecksOnTarget(bool check_on_casting, int32 spell_id, Mob *sp Always check again on SpellOnTarget to account for AE checks. */ - + bool ignore_on_casting = false; bool ignore_if_npc_or_gm = false; if (!IsClient() || (IsClient() && CastToClient()->GetGM())) { ignore_if_npc_or_gm = true; } - if (check_on_casting && !spell_target){ + if (check_on_casting){ - if (IsGroupSpell(spell_id) || - spells[spell_id].target_type == ST_AEClientV1 || - spells[spell_id].target_type == ST_AECaster || - spells[spell_id].target_type == ST_Ring || - spells[spell_id].target_type == ST_Beam){ - return true; + if (!spell_target) { + if (IsGroupSpell(spell_id) || + spells[spell_id].target_type == ST_AEClientV1 || + spells[spell_id].target_type == ST_AECaster || + spells[spell_id].target_type == ST_Ring || + spells[spell_id].target_type == ST_Beam) { + return true; + } + else if (spells[spell_id].target_type == ST_Self) { + spell_target = this; + } } - else if (spells[spell_id].target_type == ST_Self) { - spell_target = this; + else { + if (IsGroupSpell(spell_id) && spell_target != this) { + ignore_on_casting = true; + } } } @@ -773,13 +780,20 @@ bool Mob::DoCastingChecksOnTarget(bool check_on_casting, int32 spell_id, Mob *sp Prevents buff from being cast based on tareget ing PC OR NPC (1 = PCs, 2 = NPCs) These target types skip pcnpc only check (according to dev quotes) */ - if (spells[spell_id].pcnpc_only_flag && spells[spell_id].target_type != ST_AETargetHateList && - spells[spell_id].target_type != ST_HateList) { - if (spells[spell_id].pcnpc_only_flag == 1 && !spell_target->IsClient() && !spell_target->IsMerc() && !spell_target->IsBot()) { - return false; - } - else if (spells[spell_id].pcnpc_only_flag == 2 && (spell_target->IsClient() || spell_target->IsMerc() || spell_target->IsBot())) { - return false; + if (!ignore_on_casting) { + if (spells[spell_id].pcnpc_only_flag && spells[spell_id].target_type != ST_AETargetHateList && spells[spell_id].target_type != ST_HateList) { + if (spells[spell_id].pcnpc_only_flag == 1 && !spell_target->IsClient() && !spell_target->IsMerc() && !spell_target->IsBot()) { + if (check_on_casting) { + Message(Chat::SpellFailure, "This spell only works on other PCs"); + } + return false; + } + else if (spells[spell_id].pcnpc_only_flag == 2 && (spell_target->IsClient() || spell_target->IsMerc() || spell_target->IsBot())) { + if (check_on_casting) { + Message(Chat::SpellFailure, "This spell only works on NPCs."); + } + return false; + } } } /* @@ -1272,7 +1286,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo item = CastToClient()->GetInv().GetItem(inventory_slot); //checked for in reagents and charges. if (CastToClient()->HasItemRecastTimer(spell_id, inventory_slot)) { MessageString(Chat::Red, SPELL_RECAST); - LogSpells("Casting of [{}] canceled: item spell reuse timer not expired", spell_id); + LogSpells("Casting of [{}] canceled: item or augment spell reuse timer not expired", spell_id); StopCasting(); return; } @@ -3480,10 +3494,6 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes } } - if (!DoCastingChecksOnTarget(false, spell_id, spelltar)) { - return false; - } - EQApplicationPacket *action_packet = nullptr, *message_packet = nullptr; float spell_effectiveness; @@ -3577,6 +3587,11 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes mod_spell_cast(spell_id, spelltar, reflect_effectiveness, use_resist_adjust, resist_adjust, isproc); + if (!DoCastingChecksOnTarget(false, spell_id, spelltar)) { + safe_delete(action_packet); + return false; + } + // now check if the spell is allowed to land if (RuleB(Spells, EnableBlockedBuffs)) { // We return true here since the caster's client should act like normal @@ -6194,13 +6209,19 @@ bool Client::HasItemRecastTimer(int32 spell_id, uint32 inventory_slot) } if (aug->Click.Effect == spell_id) { - recast_delay = aug_i->GetItem()->RecastDelay; - recast_type = aug_i->GetItem()->RecastType; + if (aug_i->GetItem() && aug_i->GetItem()->RecastDelay > 0) { + recast_delay = aug_i->GetItem()->RecastDelay; + recast_type = aug_i->GetItem()->RecastType; + } break; } } } - + //do not check if item has no recast delay. + if (!recast_delay) { + return false; + } + //if time is not expired, then it exists and therefore we have a recast on this item. if (!CastToClient()->GetPTimers().Expired(&database, (pTimerItemStart + recast_type), false)) { return true; } From f9eb4603a3eb542105fba54836b43fc1172b37dd Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 10 Feb 2022 16:09:56 -0500 Subject: [PATCH 608/624] [Quest API] Add GetEnvironmentalDamageName() to Perl/Lua. (#1964) - Add EQ::constants::GetEnvironmentalDamageMap() and EQ::constants::GetEnvironmentalDamageName(). - Add quest::getenvironmentaldamagename(damage_type) to Perl. - Add eq.get_environmental_damage_name(damage_type) to Lua. - Cleanup GM messages for avoiding environmental damage. --- common/emu_constants.cpp | 32 ++++++++++++++++++----- common/emu_constants.h | 10 +++++++ common/eq_packet_structs.h | 2 +- common/patches/rof2_structs.h | 2 +- common/patches/rof_structs.h | 2 +- common/patches/sod_structs.h | 2 +- common/patches/sof_structs.h | 2 +- zone/client_packet.cpp | 49 ++++++++++++++++++++--------------- zone/embparser_api.cpp | 17 ++++++++++++ zone/lua_general.cpp | 5 ++++ zone/questmgr.cpp | 5 ++++ zone/questmgr.h | 3 ++- 12 files changed, 98 insertions(+), 33 deletions(-) diff --git a/common/emu_constants.cpp b/common/emu_constants.cpp index 7c084bc9d..99710712e 100644 --- a/common/emu_constants.cpp +++ b/common/emu_constants.cpp @@ -226,12 +226,12 @@ std::string EQ::constants::GetLDoNThemeName(uint32 theme_id) const std::map& EQ::constants::GetFlyModeMap() { static const std::map flymode_map = { - { EQ::constants::GravityBehavior::Ground, "Ground" }, - { EQ::constants::GravityBehavior::Flying, "Flying" }, - { EQ::constants::GravityBehavior::Levitating, "Levitating" }, - { EQ::constants::GravityBehavior::Water, "Water" }, - { EQ::constants::GravityBehavior::Floating, "Floating" }, - { EQ::constants::GravityBehavior::LevitateWhileRunning, "Levitating While Running" }, + { GravityBehavior::Ground, "Ground" }, + { GravityBehavior::Flying, "Flying" }, + { GravityBehavior::Levitating, "Levitating" }, + { GravityBehavior::Water, "Water" }, + { GravityBehavior::Floating, "Floating" }, + { GravityBehavior::LevitateWhileRunning, "Levitating While Running" }, }; return flymode_map; } @@ -337,3 +337,23 @@ std::string EQ::constants::GetAccountStatusName(uint8 account_status) return status_name; } + +const std::map& EQ::constants::GetEnvironmentalDamageMap() +{ + static const std::map damage_type_map = { + { EnvironmentalDamage::Lava, "Lava" }, + { EnvironmentalDamage::Drowning, "Drowning" }, + { EnvironmentalDamage::Falling, "Falling" }, + { EnvironmentalDamage::Trap, "Trap" } + }; + return damage_type_map; +} + +std::string EQ::constants::GetEnvironmentalDamageName(uint8 damage_type) +{ + if (EQ::ValueWithin(damage_type, EnvironmentalDamage::Lava, EnvironmentalDamage::Trap)) { + auto damage_types = EQ::constants::GetEnvironmentalDamageMap(); + return damage_types[damage_type]; + } + return std::string(); +} diff --git a/common/emu_constants.h b/common/emu_constants.h index 20985378f..527df11a1 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -230,6 +230,13 @@ namespace EQ LevitateWhileRunning }; + enum EnvironmentalDamage : uint8 { + Lava = 250, + Drowning, + Falling, + Trap + }; + const char *GetStanceName(StanceType stance_type); int ConvertStanceTypeToIndex(StanceType stance_type); @@ -248,6 +255,9 @@ namespace EQ extern const std::map& GetAccountStatusMap(); std::string GetAccountStatusName(uint8 account_status); + extern const std::map& GetEnvironmentalDamageMap(); + std::string GetEnvironmentalDamageName(uint8 damage_type); + const int STANCE_TYPE_FIRST = stancePassive; const int STANCE_TYPE_LAST = stanceBurnAE; const int STANCE_TYPE_COUNT = stanceBurnAE; diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 872f89859..49d6a7454 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -2762,7 +2762,7 @@ struct EnvDamage2_Struct { /*0004*/ uint16 unknown4; /*0006*/ uint32 damage; /*0010*/ uint8 unknown10[12]; -/*0022*/ uint8 dmgtype; //FA = Lava; FC = Falling +/*0022*/ uint8 dmgtype; // FA = Lava, FB = Drowning, FC = Falling, FD = Trap /*0023*/ uint8 unknown2[4]; /*0027*/ uint16 constant; //Always FFFF /*0029*/ uint16 unknown29; diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index 229a3b3e7..5fa9a8c5f 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -3061,7 +3061,7 @@ struct EnvDamage2_Struct { /*0006*/ uint32 damage; /*0010*/ float unknown10; // New to Underfoot - Seen 1 /*0014*/ uint8 unknown14[12]; -/*0026*/ uint8 dmgtype; // FA = Lava; FC = Falling +/*0026*/ uint8 dmgtype; // FA = Lava, FB = Drowning, FC = Falling, FD = Trap /*0027*/ uint8 unknown27[4]; /*0031*/ uint16 unknown31; // New to Underfoot - Seen 66 /*0033*/ uint16 constant; // Always FFFF diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index 27365a36d..e068e94a9 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -3032,7 +3032,7 @@ struct EnvDamage2_Struct { /*0006*/ uint32 damage; /*0010*/ float unknown10; // New to Underfoot - Seen 1 /*0014*/ uint8 unknown14[12]; -/*0026*/ uint8 dmgtype; // FA = Lava; FC = Falling +/*0026*/ uint8 dmgtype; // FA = Lava, FB = Drowning, FC = Falling, FD = Trap /*0027*/ uint8 unknown27[4]; /*0031*/ uint16 unknown31; // New to Underfoot - Seen 66 /*0033*/ uint16 constant; // Always FFFF diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 37392fc18..c39b5b65f 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -2539,7 +2539,7 @@ struct EnvDamage2_Struct { /*0004*/ uint16 unknown4; /*0006*/ uint32 damage; /*0010*/ uint8 unknown10[12]; -/*0022*/ uint8 dmgtype; //FA = Lava; FC = Falling +/*0022*/ uint8 dmgtype; // FA = Lava, FB = Drowning, FC = Falling, FD = Trap /*0023*/ uint8 unknown2[4]; /*0027*/ uint16 constant; //Always FFFF /*0029*/ uint16 unknown29; diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index 3129e3b0b..d750d24ad 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -2509,7 +2509,7 @@ struct EnvDamage2_Struct { /*0004*/ uint16 unknown4; /*0006*/ uint32 damage; /*0010*/ uint8 unknown10[12]; -/*0022*/ uint8 dmgtype; //FA = Lava; FC = Falling +/*0022*/ uint8 dmgtype; // FA = Lava, FB = Drowning, FC = Falling, FD = Trap /*0023*/ uint8 unknown2[4]; /*0027*/ uint16 constant; //Always FFFF /*0029*/ uint16 unknown29; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 71af0465c..578de0fe6 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -5784,8 +5784,7 @@ void Client::Handle_OP_EndLootRequest(const EQApplicationPacket *app) void Client::Handle_OP_EnvDamage(const EQApplicationPacket *app) { - if (!ClientFinishedLoading()) - { + if (!ClientFinishedLoading()) { SetHP(GetHP() - 1); return; } @@ -5795,38 +5794,46 @@ void Client::Handle_OP_EnvDamage(const EQApplicationPacket *app) DumpPacket(app); return; } + EnvDamage2_Struct* ed = (EnvDamage2_Struct*)app->pBuffer; - - int damage = ed->damage; - - if (ed->dmgtype == 252) { - - int mod = spellbonuses.ReduceFallDamage + itembonuses.ReduceFallDamage + aabonuses.ReduceFallDamage; - + auto damage = ed->damage; + if (ed->dmgtype == EQ::constants::EnvironmentalDamage::Falling) { + uint32 mod = spellbonuses.ReduceFallDamage + itembonuses.ReduceFallDamage + aabonuses.ReduceFallDamage; damage -= damage * mod / 100; } - if (damage < 0) + if (damage < 0) { damage = 31337; + } if (admin >= minStatusToAvoidFalling && GetGM()) { - Message(Chat::Red, "Your GM status protects you from %i points of type %i environmental damage.", ed->damage, ed->dmgtype); + Message( + Chat::Red, + fmt::format( + "Your GM status protects you from {} points of {} (Type {}) damage.", + ed->damage, + EQ::constants::GetEnvironmentalDamageName(ed->dmgtype), + ed->dmgtype + ).c_str() + ); SetHP(GetHP() - 1);//needed or else the client wont acknowledge return; - } - else if (GetInvul()) { - Message(Chat::Red, "Your invuln status protects you from %i points of type %i environmental damage.", ed->damage, ed->dmgtype); + } else if (GetInvul()) { + Message( + Chat::Red, + fmt::format( + "Your invulnerability protects you from {} points of {} (Type {}) damage.", + ed->damage, + EQ::constants::GetEnvironmentalDamageName(ed->dmgtype), + ed->dmgtype + ).c_str() + ); SetHP(GetHP() - 1);//needed or else the client wont acknowledge return; - } - else if (zone->GetZoneID() == 183 || zone->GetZoneID() == 184) { - // Hard coded tutorial and load zones for no fall damage + } else if (zone->GetZoneID() == Zones::TUTORIAL || zone->GetZoneID() == Zones::LOAD) { // Hard coded tutorial and load zones for no fall damage return; - } - else { + } else { SetHP(GetHP() - (damage * RuleR(Character, EnvironmentDamageMulipliter))); - - /* EVENT_ENVIRONMENTAL_DAMAGE */ int final_damage = (damage * RuleR(Character, EnvironmentDamageMulipliter)); std::string export_string = fmt::format( "{} {} {}", diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 67034c4ab..47c85161b 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -8092,6 +8092,22 @@ XS(XS__getbodytypename) { } } +XS(XS__getenvironmentaldamagename); +XS(XS__getenvironmentaldamagename) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::getenvironmentaldamagename(uint8 damage_type)"); + + dXSTARG; + uint8 damage_type = (uint8) SvIV(ST(0)); + std::string environmental_damage_name = quest_manager.getenvironmentaldamagename(damage_type); + + sv_setpv(TARG, environmental_damage_name.c_str()); + XSprePUSH; + PUSHTARG; + XSRETURN(1); +} + /* This is the callback perl will look for to setup the quest package's XSUBs @@ -8398,6 +8414,7 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "getcurrencyitemid"), XS__getcurrencyitemid, file); newXS(strcpy(buf, "getgendername"), XS__getgendername, file); newXS(strcpy(buf, "getdeityname"), XS__getdeityname, file); + newXS(strcpy(buf, "getenvironmentaldamagename"), XS__getenvironmentaldamagename, file); newXS(strcpy(buf, "getguildnamebyid"), XS__getguildnamebyid, file); newXS(strcpy(buf, "getguildidbycharid"), XS__getguildidbycharid, file); newXS(strcpy(buf, "getgroupidbycharid"), XS__getgroupidbycharid, file); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 444f1ad03..2d6ae07cd 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -3367,6 +3367,10 @@ std::string lua_get_body_type_name(uint32 bodytype_id) { return quest_manager.getbodytypename(bodytype_id); } +std::string lua_get_environmental_damage_name(uint8 damage_type) { + return quest_manager.getenvironmentaldamagename(damage_type); +} + #define LuaCreateNPCParse(name, c_type, default_value) do { \ cur = table[#name]; \ if(luabind::type(cur) != LUA_TNIL) { \ @@ -3814,6 +3818,7 @@ luabind::scope lua_register_general() { luabind::def("get_faction_name", &lua_get_faction_name), luabind::def("get_language_name", &lua_get_language_name), luabind::def("get_body_type_name", &lua_get_body_type_name), + luabind::def("get_environmental_damage_name", &lua_get_environmental_damage_name), /* Cross Zone diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 5bcbbfb14..189a399eb 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -3698,3 +3698,8 @@ const SPDat_Spell_Struct* QuestManager::getspell(uint32 spell_id) { } return nullptr; } + +std::string QuestManager::getenvironmentaldamagename(uint8 damage_type) { + std::string environmental_damage_name = EQ::constants::GetEnvironmentalDamageName(damage_type); + return environmental_damage_name; +} diff --git a/zone/questmgr.h b/zone/questmgr.h index 3bd3ddfe3..b8a296ed7 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -330,7 +330,8 @@ public: std::string getinventoryslotname(int16 slot_id); int getitemstat(uint32 item_id, std::string stat_identifier); int getspellstat(uint32 spell_id, std::string stat_identifier, uint8 slot = 0); - const SPDat_Spell_Struct *getspell(uint32 spell_id); + const SPDat_Spell_Struct *getspell(uint32 spell_id); + std::string getenvironmentaldamagename(uint8 damage_type); Client *GetInitiator() const; NPC *GetNPC() const; From 1ea88886073339132f7e12370900a9d3b5b279fc Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 10 Feb 2022 16:10:06 -0500 Subject: [PATCH 609/624] [Commands] Cleanup #timers Command. (#1965) - Cleanup message and logic. - Utilize popup instead of chat messages. - Utilize ConvertSecondsToTime() helper method. --- zone/gm_commands/timers.cpp | 46 ++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/zone/gm_commands/timers.cpp b/zone/gm_commands/timers.cpp index d8e74a8b3..4e36661e4 100755 --- a/zone/gm_commands/timers.cpp +++ b/zone/gm_commands/timers.cpp @@ -2,21 +2,45 @@ void command_timers(Client *c, const Seperator *sep) { - if (!c->GetTarget() || !c->GetTarget()->IsClient()) { - c->Message(Chat::White, "Need a player target for timers."); - return; + auto target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); } - Client *them = c->GetTarget()->CastToClient(); - std::vector > res; - them->GetPTimers().ToVector(res); + std::vector> timers; + target->GetPTimers().ToVector(timers); - c->Message(Chat::White, "Timers for target:"); + std::string popup_title = fmt::format( + "Recast Timers for {}", + c == target ? + "Yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ); - int r; - int l = res.size(); - for (r = 0; r < l; r++) { - c->Message(Chat::White, "Timer %d: %d seconds remain.", res[r].first, res[r].second->GetRemainingTime()); + std::string popup_text = ""; + + popup_text += ""; + + for (const auto& timer : timers) { + auto remaining_time = timer.second->GetRemainingTime(); + if (remaining_time) { + popup_text += fmt::format( + "", + timer.first, + ConvertSecondsToTime(remaining_time) + ); + } } + + popup_text += "
Timer IDRemaining
{}{}
"; + + c->SendPopupToClient( + popup_title.c_str(), + popup_text.c_str() + ); } From d83ced6f76106a1e7edaafc1b73dc9a8feccdedb Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 10 Feb 2022 16:10:16 -0500 Subject: [PATCH 610/624] [Commands] Cleanup #undye and #undyeme Commands. (#1966) - Fix #undye command as its method was not being used in command.cpp. - Cleanup messages and logic for both #undye and #undyeme. --- zone/command.cpp | 1 + zone/gm_commands/undye.cpp | 22 +++++++++++++++++----- zone/gm_commands/undyeme.cpp | 7 ++----- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 2eee4e1f1..e4baf134e 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -378,6 +378,7 @@ int command_init(void) command_add("trapinfo", "- Gets infomation about the traps currently spawned in the zone.", AccountStatus::QuestTroupe, command_trapinfo) || command_add("tune", "Calculate statistical values related to combat.", AccountStatus::GMAdmin, command_tune) || command_add("ucs", "- Attempts to reconnect to the UCS server", AccountStatus::Player, command_ucs) || + command_add("undye", "- Remove dye from all of your or your target's armor slots", AccountStatus::GMAdmin, command_undye) || command_add("undyeme", "- Remove dye from all of your armor slots", AccountStatus::Player, command_undyeme) || command_add("unfreeze", "- Unfreeze your target", AccountStatus::QuestTroupe, command_unfreeze) || command_add("unlock", "- Unlock the worldserver", AccountStatus::GMLeadAdmin, command_unlock) || diff --git a/zone/gm_commands/undye.cpp b/zone/gm_commands/undye.cpp index af07be7a7..e5430a02b 100755 --- a/zone/gm_commands/undye.cpp +++ b/zone/gm_commands/undye.cpp @@ -2,11 +2,23 @@ void command_undye(Client *c, const Seperator *sep) { + auto target = c; if (c->GetTarget() && c->GetTarget()->IsClient()) { - c->GetTarget()->CastToClient()->Undye(); + target = c->GetTarget()->CastToClient(); } - else { - c->Message(Chat::White, "ERROR: Client target required"); - } -} + target->Undye(); + c->Message( + Chat::White, + fmt::format( + "Undyed armor for {}.", + c == target ? + "yourself" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ) + ).c_str() + ); +} diff --git a/zone/gm_commands/undyeme.cpp b/zone/gm_commands/undyeme.cpp index b2e451202..8bd6924b1 100755 --- a/zone/gm_commands/undyeme.cpp +++ b/zone/gm_commands/undyeme.cpp @@ -2,9 +2,6 @@ void command_undyeme(Client *c, const Seperator *sep) { - if (c) { - c->Undye(); - c->Message(Chat::Red, "Dye removed from all slots. Please zone for the process to complete."); - } + c->Undye(); + c->Message(Chat::White, "Undyed armor for yourself."); } - From 49d7eb1402432f879a2b49d18b2a65b22a152c1e Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 10 Feb 2022 16:10:23 -0500 Subject: [PATCH 611/624] [Commands] Cleanup #version Command. (#1967) - Utilize popup over chat message. --- zone/gm_commands/version.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/zone/gm_commands/version.cpp b/zone/gm_commands/version.cpp index ecb9b74d4..cc5d0a531 100755 --- a/zone/gm_commands/version.cpp +++ b/zone/gm_commands/version.cpp @@ -2,9 +2,17 @@ void command_version(Client *c, const Seperator *sep) { - c->Message(Chat::White, "Current version information."); - c->Message(Chat::White, " %s", CURRENT_VERSION); - c->Message(Chat::White, " Compiled on: %s at %s", COMPILE_DATE, COMPILE_TIME); - c->Message(Chat::White, " Last modified on: %s", LAST_MODIFIED); + std::string popup_text = ""; + + popup_text += fmt::format("", CURRENT_VERSION); + popup_text += fmt::format("", COMPILE_DATE, COMPILE_TIME); + popup_text += fmt::format("", LAST_MODIFIED); + + popup_text += "
Version{}
Compiled{} {}
Last Modified{}
"; + + c->SendPopupToClient( + "Server Version Information", + popup_text.c_str() + ); } From 7bf466cf3f6ad9a9dfd87f67479f26dea4639f3a Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 10 Feb 2022 16:10:31 -0500 Subject: [PATCH 612/624] [Commands] Cleanup #netstats Command. (#1970) - Utilize popup over chat messages. --- zone/gm_commands/netstats.cpp | 294 +++++++++++++++++++--------------- 1 file changed, 161 insertions(+), 133 deletions(-) diff --git a/zone/gm_commands/netstats.cpp b/zone/gm_commands/netstats.cpp index 6bd56f4fa..87fd11a4c 100755 --- a/zone/gm_commands/netstats.cpp +++ b/zone/gm_commands/netstats.cpp @@ -2,140 +2,168 @@ void command_netstats(Client *c, const Seperator *sep) { - if (c) { - auto client = c; - if (c->GetTarget() && c->GetTarget()->IsClient()) { - client = c->GetTarget()->CastToClient(); - } + bool is_full = !strcasecmp(sep->arg[1], "full"); + bool is_reset = !strcasecmp(sep->arg[1], "reset"); - if (strcasecmp(sep->arg[1], "reset") == 0) { - auto connection = c->Connection(); - c->Message(Chat::White, "Resetting client stats (packet loss will not read correctly after reset)."); - connection->ResetStats(); - return; - } - - auto connection = c->Connection(); - auto opts = connection->GetManager()->GetOptions(); - auto eqs_stats = connection->GetStats(); - auto &stats = eqs_stats.DaybreakStats; - auto now = EQ::Net::Clock::now(); - auto sec_since_stats_reset = std::chrono::duration_cast>( - now - stats.created - ).count(); - - c->Message(Chat::White, "Netstats:"); - c->Message(Chat::White, "--------------------------------------------------------------------"); - c->Message( - Chat::White, - "Sent Bytes: %u (%.2f/sec)", - stats.sent_bytes, - stats.sent_bytes / sec_since_stats_reset - ); - c->Message( - Chat::White, - "Recv Bytes: %u (%.2f/sec)", - stats.recv_bytes, - stats.recv_bytes / sec_since_stats_reset - ); - c->Message( - Chat::White, "Bytes Before Encode (Sent): %u, Compression Rate: %.2f%%", stats.bytes_before_encode, - static_cast(stats.bytes_before_encode - stats.sent_bytes) / - static_cast(stats.bytes_before_encode) * 100.0 - ); - c->Message( - Chat::White, "Bytes After Decode (Recv): %u, Compression Rate: %.2f%%", stats.bytes_after_decode, - static_cast(stats.bytes_after_decode - stats.recv_bytes) / - static_cast(stats.bytes_after_decode) * 100.0 - ); - c->Message(Chat::White, "Min Ping: %u", stats.min_ping); - c->Message(Chat::White, "Max Ping: %u", stats.max_ping); - c->Message(Chat::White, "Last Ping: %u", stats.last_ping); - c->Message(Chat::White, "Average Ping: %u", stats.avg_ping); - c->Message(Chat::White, "--------------------------------------------------------------------"); - c->Message( - Chat::White, - "(Realtime) Recv Packets: %u (%.2f/sec)", - stats.recv_packets, - stats.recv_packets / sec_since_stats_reset - ); - c->Message( - Chat::White, - "(Realtime) Sent Packets: %u (%.2f/sec)", - stats.sent_packets, - stats.sent_packets / sec_since_stats_reset - ); - c->Message(Chat::White, "(Sync) Recv Packets: %u", stats.sync_recv_packets); - c->Message(Chat::White, "(Sync) Sent Packets: %u", stats.sync_sent_packets); - c->Message(Chat::White, "(Sync) Remote Recv Packets: %u", stats.sync_remote_recv_packets); - c->Message(Chat::White, "(Sync) Remote Sent Packets: %u", stats.sync_remote_sent_packets); - c->Message( - Chat::White, - "Packet Loss In: %.2f%%", - 100.0 * (1.0 - static_cast(stats.sync_recv_packets) / - static_cast(stats.sync_remote_sent_packets))); - c->Message( - Chat::White, - "Packet Loss Out: %.2f%%", - 100.0 * (1.0 - static_cast(stats.sync_remote_recv_packets) / - static_cast(stats.sync_sent_packets))); - c->Message(Chat::White, "--------------------------------------------------------------------"); - c->Message( - Chat::White, - "Resent Packets: %u (%.2f/sec)", - stats.resent_packets, - stats.resent_packets / sec_since_stats_reset - ); - c->Message( - Chat::White, - "Resent Fragments: %u (%.2f/sec)", - stats.resent_fragments, - stats.resent_fragments / sec_since_stats_reset - ); - c->Message( - Chat::White, - "Resent Non-Fragments: %u (%.2f/sec)", - stats.resent_full, - stats.resent_full / sec_since_stats_reset - ); - c->Message( - Chat::White, - "Dropped Datarate Packets: %u (%.2f/sec)", - stats.dropped_datarate_packets, - stats.dropped_datarate_packets / sec_since_stats_reset - ); - - if (opts.daybreak_options.outgoing_data_rate > 0.0) { - c->Message( - Chat::White, - "Outgoing Link Saturation %.2f%% (%.2fkb/sec)", - 100.0 * (1.0 - ((opts.daybreak_options.outgoing_data_rate - stats.datarate_remaining) / - opts.daybreak_options.outgoing_data_rate)), - opts.daybreak_options.outgoing_data_rate - ); - } - - if (strcasecmp(sep->arg[1], "full") == 0) { - c->Message(Chat::White, "--------------------------------------------------------------------"); - c->Message(Chat::White, "Sent Packet Types"); - for (auto i = 0; i < _maxEmuOpcode; ++i) { - auto cnt = eqs_stats.SentCount[i]; - if (cnt > 0) { - c->Message(Chat::White, "%s: %u (%.2f / sec)", OpcodeNames[i], cnt, cnt / sec_since_stats_reset); - } - } - - c->Message(Chat::White, "--------------------------------------------------------------------"); - c->Message(Chat::White, "Recv Packet Types"); - for (auto i = 0; i < _maxEmuOpcode; ++i) { - auto cnt = eqs_stats.RecvCount[i]; - if (cnt > 0) { - c->Message(Chat::White, "%s: %u (%.2f / sec)", OpcodeNames[i], cnt, cnt / sec_since_stats_reset); - } - } - } - - c->Message(Chat::White, "--------------------------------------------------------------------"); + if (is_reset) { + auto connection = c->Connection(); + c->Message(Chat::White, "Resetting client stats (packet loss will not read correctly after reset)."); + connection->ResetStats(); + return; } + + auto connection = c->Connection(); + auto opts = connection->GetManager()->GetOptions(); + auto eqs_stats = connection->GetStats(); + auto &stats = eqs_stats.DaybreakStats; + auto now = EQ::Net::Clock::now(); + auto sec_since_stats_reset = std::chrono::duration_cast>( + now - stats.created + ).count(); + + std::string popup_text = ""; + + popup_text += fmt::format( + "", + stats.sent_bytes, + stats.sent_bytes / sec_since_stats_reset + ); + + popup_text += fmt::format( + "", + stats.recv_bytes, + stats.recv_bytes / sec_since_stats_reset + ); + + popup_text += "

"; + + popup_text += fmt::format( + "", + stats.bytes_before_encode, + static_cast(stats.bytes_before_encode - stats.sent_bytes) / + static_cast(stats.bytes_before_encode) * 100.0 + ); + + popup_text += fmt::format( + "", + stats.bytes_after_decode, + static_cast(stats.bytes_after_decode - stats.recv_bytes) / + static_cast(stats.bytes_after_decode) * 100.0 + ); + + popup_text += "

"; + + popup_text += fmt::format("
", stats.min_ping); + popup_text += fmt::format("", stats.max_ping); + popup_text += fmt::format("", stats.last_ping); + popup_text += fmt::format("", stats.avg_ping); + + popup_text += "

"; + + popup_text += fmt::format( + "", + stats.recv_packets, + stats.recv_packets / sec_since_stats_reset + ); + + popup_text += fmt::format( + "", + stats.sent_packets, + stats.sent_packets / sec_since_stats_reset + ); + + popup_text += "

"; + + popup_text += fmt::format("", stats.sync_recv_packets); + popup_text += fmt::format("", stats.sync_sent_packets); + popup_text += fmt::format("", stats.sync_remote_recv_packets); + popup_text += fmt::format("", stats.sync_remote_sent_packets); + + popup_text += "

"; + + popup_text += fmt::format( + "", + (100.0 * (1.0 - static_cast(stats.sync_recv_packets) / static_cast(stats.sync_remote_sent_packets))) + ); + + popup_text += fmt::format( + "", + (100.0 * (1.0 - static_cast(stats.sync_remote_recv_packets) / static_cast(stats.sync_sent_packets))) + ); + + popup_text += "

"; + + popup_text += fmt::format( + "
", + stats.resent_packets, + stats.resent_packets / sec_since_stats_reset + ); + + popup_text += fmt::format( + "", + stats.resent_fragments, + stats.resent_fragments / sec_since_stats_reset + ); + + popup_text += fmt::format( + "", + stats.resent_full, + stats.resent_full / sec_since_stats_reset + ); + + popup_text += "

"; + + popup_text += fmt::format( + "", + stats.dropped_datarate_packets, + stats.dropped_datarate_packets / sec_since_stats_reset + ); + + if (opts.daybreak_options.outgoing_data_rate > 0.0) { + popup_text += fmt::format( + "", + (100.0 * (1.0 - ((opts.daybreak_options.outgoing_data_rate - stats.datarate_remaining) / opts.daybreak_options.outgoing_data_rate))), + opts.daybreak_options.outgoing_data_rate + ); + } + + if (is_full) { + popup_text += "

"; + + popup_text += ""; + for (auto i = 0; i < _maxEmuOpcode; ++i) { + auto cnt = eqs_stats.SentCount[i]; + if (cnt > 0) { + popup_text += fmt::format( + "", + OpcodeNames[i], + cnt, + cnt / sec_since_stats_reset + ); + } + } + + popup_text += "

"; + + popup_text += ""; + for (auto i = 0; i < _maxEmuOpcode; ++i) { + auto cnt = eqs_stats.RecvCount[i]; + if (cnt > 0) { + popup_text += fmt::format( + "", + OpcodeNames[i], + cnt, + cnt / sec_since_stats_reset + ); + } + } + } + + popup_text += "
Sent Bytes{} ({:.2f} Per Second)
Received Bytes{} ({:.2f} Per Second)
Bytes Before Encode (Sent){}Compression Rate{:.2f}%%
Bytes After Decode (Received){}Compression Rate{:.2f}%%
Min Ping{}
Max Ping{}
Last Ping{}
Average Ping{}
(Realtime) Received Packets{} ({:.2f} Per Second)
(Realtime) Sent Packets{} ({:.2f} Per Second)
(Sync) Received Packets{}
(Sync) Sent Packets{}
(Sync) Remote Received Packets{}
(Sync) Remote Sent Packets{}
Packet Loss In{:.2f}%%
Packet Loss Out{:.2f}%%
Resent Packets{} ({:.2f} Per Second)
Resent Fragments{} ({:.2f} Per Second)
Resent Non-Fragments{} ({:.2f} Per Second)
Dropped Datarate Packets{} ({:.2f} Per Second)
Outgoing Link Saturation{:.2f}%% ({:.2f}kb Per Second)
Sent Packet Types
{}{} ({:.2f} Per Second)
Received Packet Types
{}{} ({:.2f} Per Second)
"; + + c->SendPopupToClient( + "Network Statistics", + popup_text.c_str() + ); } From 968ae26c992316915cf53fae22d0a3d60a800d64 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 10 Feb 2022 16:14:17 -0500 Subject: [PATCH 613/624] [Commands] Cleanup #hatelist Command. (#1976) - Cleanup messages and logic. --- zone/command.cpp | 2 +- zone/gm_commands/hatelist.cpp | 10 +++---- zone/hate_list.cpp | 54 +++++++++++++++++++++++++++++------ 3 files changed, 51 insertions(+), 15 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index e4baf134e..a83fc46fa 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -209,7 +209,7 @@ int command_init(void) command_add("hair", "- Change the hair style of your target", AccountStatus::QuestTroupe, command_hair) || command_add("haircolor", "- Change the hair color of your target", AccountStatus::QuestTroupe, command_haircolor) || command_add("haste", "[percentage] - Set your haste percentage", AccountStatus::GMAdmin, command_haste) || - command_add("hatelist", " - Display hate list for target.", AccountStatus::QuestTroupe, command_hatelist) || + command_add("hatelist", "- Display hate list for NPC.", AccountStatus::QuestTroupe, command_hatelist) || command_add("heal", "- Completely heal your target", AccountStatus::Steward, command_heal) || command_add("helm", "- Change the helm of your target", AccountStatus::QuestTroupe, command_helm) || command_add("help", "[search term] - List available commands and their description, specify partial command as argument to search", AccountStatus::Player, command_help) || diff --git a/zone/gm_commands/hatelist.cpp b/zone/gm_commands/hatelist.cpp index 13006d47b..83fe75e20 100755 --- a/zone/gm_commands/hatelist.cpp +++ b/zone/gm_commands/hatelist.cpp @@ -2,14 +2,12 @@ void command_hatelist(Client *c, const Seperator *sep) { - Mob *target = c->GetTarget(); - if (target == nullptr) { - c->Message(Chat::White, "Error: you must have a target."); + if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); return; } - c->Message(Chat::White, "Display hate list for %s..", target->GetName()); + auto target = c->GetTarget(); + target->PrintHateListToClient(c); } - - diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index 83ec481fa..75923c99e 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -645,15 +645,53 @@ bool HateList::IsHateListEmpty() { void HateList::PrintHateListToClient(Client *c) { - auto iterator = list.begin(); - while (iterator != list.end()) - { - struct_HateList *e = (*iterator); - c->Message(Chat::White, "- name: %s, damage: %d, hate: %d", - (e->entity_on_hatelist && e->entity_on_hatelist->GetName()) ? e->entity_on_hatelist->GetName() : "(null)", - e->hatelist_damage, e->stored_hate_amount); + if (list.size()) { + c->Message( + Chat::White, + fmt::format( + "Displaying hate list for {} ({}).", + hate_owner->GetCleanName(), + hate_owner->GetID() + ).c_str() + ); - ++iterator; + auto entity_number = 1; + for (const auto& hate_entity : list) { + if (hate_entity->entity_on_hatelist) { + c->Message( + Chat::White, + fmt::format( + "Hate Entity {} | Name: {} ({}) Damage: {} Hate: {}", + entity_number, + hate_entity->entity_on_hatelist->GetName(), + hate_entity->entity_on_hatelist->GetID(), + hate_entity->hatelist_damage, + hate_entity->stored_hate_amount + ).c_str() + ); + } else { + c->Message( + Chat::White, + fmt::format( + "Hate Entity {} | Damage: {} Hate: {}", + entity_number, + hate_entity->hatelist_damage, + hate_entity->stored_hate_amount + ).c_str() + ); + } + + entity_number++; + } + } else { + c->Message( + Chat::White, + fmt::format( + "{} ({}) has nothing on its hatelist.", + hate_owner->GetCleanName(), + hate_owner->GetID() + ).c_str() + ); } } From 5396c0c88b6481780171ac76646808e0a3b64d02 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 10 Feb 2022 16:14:40 -0500 Subject: [PATCH 614/624] [Commands] Cleanup #name Command. (#1977) - Cleanup messages and logic. --- zone/command.cpp | 2 +- zone/gm_commands/name.cpp | 50 ++++++++++++++++++++++++--------------- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index a83fc46fa..abfe1f462 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -252,7 +252,7 @@ int command_init(void) command_add("myskills", "- Show details about your current skill levels", AccountStatus::Player, command_myskills) || command_add("mysql", "[Help|Query] [SQL Query] - Mysql CLI, see 'Help' for options.", AccountStatus::GMImpossible, command_mysql) || command_add("mystats", "- Show details about you or your pet", AccountStatus::Guide, command_mystats) || - command_add("name", "[newname] - Rename your player target", AccountStatus::GMLeadAdmin, command_name) || + command_add("name", "[New Name] - Rename your player target", AccountStatus::GMLeadAdmin, command_name) || command_add("netstats", "- Gets the network stats for a stream.", AccountStatus::GMMgmt, command_netstats) || command_add("network", "- Admin commands for the udp network interface.", AccountStatus::GMImpossible, command_network) || command_add("npccast", "[targetname/entityid] [spellid] - Causes NPC target to cast spellid on targetname/entityid", AccountStatus::QuestTroupe, command_npccast) || diff --git a/zone/gm_commands/name.cpp b/zone/gm_commands/name.cpp index fbe60f752..58dda8e75 100755 --- a/zone/gm_commands/name.cpp +++ b/zone/gm_commands/name.cpp @@ -2,29 +2,41 @@ void command_name(Client *c, const Seperator *sep) { - Client *target; - - if ((strlen(sep->arg[1]) == 0) || (!(c->GetTarget() && c->GetTarget()->IsClient()))) { - c->Message(Chat::White, "Usage: #name newname (requires player target)"); + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #name [New Name] - Rename your player target"); + return; } - else { - target = c->GetTarget()->CastToClient(); - char *oldname = strdup(target->GetName()); - if (target->ChangeFirstName(sep->arg[1], c->GetName())) { - c->Message(Chat::White, "Successfully renamed %s to %s", oldname, sep->arg[1]); - // until we get the name packet working right this will work - c->Message(Chat::White, "Sending player to char select."); - target->Kick("Name was changed"); - } - else { + + if (c->GetTarget() && c->GetTarget()->IsClient()) { + auto target = c->GetTarget()->CastToClient(); + + std::string new_name = sep->arg[1]; + std::string old_name = target->GetCleanName(); + + if (target->ChangeFirstName(new_name.c_str(), c->GetCleanName())) { c->Message( - Chat::Red, - "ERROR: Unable to rename %s. Check that the new name '%s' isn't already taken.", - oldname, - sep->arg[2] + Chat::White, + fmt::format( + "Successfully renamed {} to {}", + old_name, + new_name + ).c_str() + ); + + c->Message(Chat::White, "Sending player to char select."); + + target->Kick("Name was changed"); + } else { + c->Message( + Chat::White, + fmt::format( + "Unable to rename {}. Check that the new name '{}' isn't already taken.", + old_name, + new_name + ).c_str() ); } - free(oldname); } } From 4a4158380551c134302a7517f1a59a14d8735d69 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 10 Feb 2022 16:17:52 -0500 Subject: [PATCH 615/624] [Commands] Cleanup #flagedit Command. (#1968) - Cleanup and fix messages and logic. - Utilized popup for listing zones that have required flags. --- zone/command.cpp | 2 +- zone/gm_commands/flagedit.cpp | 381 +++++++++++++++++++++++----------- 2 files changed, 258 insertions(+), 125 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index abfe1f462..66c23769d 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -183,7 +183,7 @@ int command_init(void) command_add("findzone", "[search criteria] - Search database zones", AccountStatus::GMAdmin, command_findzone) || command_add("fixmob", "[race|gender|texture|helm|face|hair|haircolor|beard|beardcolor|heritage|tattoo|detail] [next|prev] - Manipulate appearance of your target", AccountStatus::QuestTroupe, command_fixmob) || command_add("flag", "[status] [acctname] - Refresh your admin status, or set an account's admin status if arguments provided", AccountStatus::Player, command_flag) || - command_add("flagedit", "- Edit zone flags on your target", AccountStatus::GMAdmin, command_flagedit) || + command_add("flagedit", "- Edit zone flags on your target. Use #flagedit help for more info.", AccountStatus::GMAdmin, command_flagedit) || command_add("flags", "- displays the flags of you or your target", AccountStatus::Player, command_flags) || command_add("flymode", "[0/1/2/3/4/5] - Set your or your player target's flymode to ground/flying/levitate/water/floating/levitate_running", AccountStatus::Guide, command_flymode) || command_add("fov", "- Check wether you're behind or in your target's field of view", AccountStatus::QuestTroupe, command_fov) || diff --git a/zone/gm_commands/flagedit.cpp b/zone/gm_commands/flagedit.cpp index 7e0bff517..2b3cf746b 100755 --- a/zone/gm_commands/flagedit.cpp +++ b/zone/gm_commands/flagedit.cpp @@ -2,156 +2,289 @@ void command_flagedit(Client *c, const Seperator *sep) { - //super-command for editing zone flags - if (sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { - c->Message(Chat::White, "Syntax: #flagedit [lockzone|unlockzone|listzones|give|take]."); + int arguments = sep->argnum; + if (!arguments) { + auto flags_link = EQ::SayLinkEngine::GenerateQuestSaylink("#flags", false, "#flags"); c->Message( Chat::White, - "...lockzone [zone id/short] [flag name] - Set the specified flag name on the zone, locking the zone" + "Usage: #flagedit lock [Zone ID|Zone Short Name] [Flag Name] - Set the specified flag name on the zone, locking the zone" ); - c->Message(Chat::White, "...unlockzone [zone id/short] - Removes the flag requirement from the specified zone"); - c->Message(Chat::White, "...listzones - List all zones which require a flag, and their flag's name"); - c->Message(Chat::White, "...give [zone id/short] - Give your target the zone flag for the specified zone."); c->Message( Chat::White, - "...take [zone id/short] - Take the zone flag for the specified zone away from your target" + "Usage: #flagedit unlock [Zone ID|Zone Short Name] - Removes the flag requirement from the specified zone" + ); + c->Message( + Chat::White, + "Usage: #flagedit list - List all zones which require a flag, and their flag's name" + ); + c->Message( + Chat::White, + "Usage: #flagedit give [Zone ID|Zone Short Name] - Give your target the zone flag for the specified zone." + ); + c->Message( + Chat::White, + "Usage: #flagedit take [Zone ID|Zone Short Name] - Take the zone flag for the specified zone away from your target" + ); + c->Message( + Chat::White, + fmt::format( + "Note: Use {} to view the flags a player has.", + flags_link + ).c_str() ); - c->Message(Chat::White, "...Note: use #flags to view flags on a person"); return; } - if (!strcasecmp(sep->arg[1], "lockzone")) { - uint32 zoneid = 0; - if (sep->arg[2][0] != '\0') { - zoneid = atoi(sep->arg[2]); - if (zoneid < 1) { - zoneid = ZoneID(sep->arg[2]); + bool is_give = !strcasecmp(sep->arg[1], "give"); + bool is_list = !strcasecmp(sep->arg[1], "list"); + bool is_lock = !strcasecmp(sep->arg[1], "lock"); + bool is_take = !strcasecmp(sep->arg[1], "take"); + bool is_unlock = !strcasecmp(sep->arg[1], "unlock"); + + if ( + !is_give && + !is_list && + !is_lock && + !is_take && + !is_unlock + ) { + auto flags_link = EQ::SayLinkEngine::GenerateQuestSaylink("#flags", false, "#flags"); + c->Message( + Chat::White, + "Usage: #flagedit lock [Zone ID|Zone Short Name] [Flag Name] - Set the specified flag name on the zone, locking the zone" + ); + c->Message( + Chat::White, + "Usage: #flagedit unlock [Zone ID|Zone Short Name] - Removes the flag requirement from the specified zone" + ); + c->Message( + Chat::White, + "Usage: #flagedit list - List all zones which require a flag, and their flag's name" + ); + c->Message( + Chat::White, + "Usage: #flagedit give [Zone ID|Zone Short Name] - Give your target the zone flag for the specified zone." + ); + c->Message( + Chat::White, + "Usage: #flagedit take [Zone ID|Zone Short Name] - Take the zone flag for the specified zone away from your target" + ); + c->Message( + Chat::White, + fmt::format( + "Note: Use {} to view the flags a player has.", + flags_link + ).c_str() + ); + return; + } + + if (is_give) { + uint32 zone_id = ( + sep->IsNumber(2) ? + std::stoul(sep->arg[2]) : + ZoneID(sep->arg[2]) + ); + std::string zone_short_name = str_tolower(ZoneName(zone_id, true)); + bool is_unknown_zone = zone_short_name.find("unknown") != std::string::npos; + if (zone_id && !is_unknown_zone) { + std::string zone_long_name = ZoneLongName(zone_id); + auto target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); } - } - if (zoneid < 1) { - c->Message(Chat::Red, "zone required. see help."); - return; - } - char flag_name[128]; - if (sep->argplus[3][0] == '\0') { - c->Message(Chat::Red, "flag name required. see help."); - return; - } - database.DoEscapeString(flag_name, sep->argplus[3], 64); - flag_name[127] = '\0'; - - std::string query = StringFormat( - "UPDATE zone SET flag_needed = '%s' " - "WHERE zoneidnumber = %d AND version = %d", - flag_name, zoneid, zone->GetInstanceVersion()); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::Red, "Error updating zone: %s", results.ErrorMessage().c_str()); - return; - } - - c->Message(Chat::Yellow, "Success! Zone %s now requires a flag, named %s", ZoneName(zoneid), flag_name); - return; - } - - if (!strcasecmp(sep->arg[1], "unlockzone")) { - uint32 zoneid = 0; - if (sep->arg[2][0] != '\0') { - zoneid = atoi(sep->arg[2]); - if (zoneid < 1) { - zoneid = ZoneID(sep->arg[2]); - } - } - - if (zoneid < 1) { - c->Message(Chat::Red, "zone required. see help."); - return; - } - - std::string query = StringFormat( - "UPDATE zone SET flag_needed = '' " - "WHERE zoneidnumber = %d AND version = %d", - zoneid, zone->GetInstanceVersion()); - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - c->Message(Chat::Yellow, "Error updating zone: %s", results.ErrorMessage().c_str()); - return; - } - - c->Message(Chat::Yellow, "Success! Zone %s no longer requires a flag.", ZoneName(zoneid)); - return; - } - - if (!strcasecmp(sep->arg[1], "listzones")) { - std::string query = "SELECT zoneidnumber, short_name, long_name, version, flag_needed " - "FROM zone WHERE flag_needed != ''"; - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - return; - } - - c->Message(Chat::White, "Zones which require flags:"); - for (auto row = results.begin(); row != results.end(); ++row) + target->SetZoneFlag(zone_id); c->Message( Chat::White, - "Zone %s (%s,%s) version %s requires key %s", - row[2], + fmt::format( + "{} now {} the flag for {} ({}).", + c == target ? + "You" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ), + c == target ? "have" : "has", + zone_long_name, + zone_id + ).c_str() + ); + return; + } + } else if (is_list) { + std::string query = SQL( + SELECT long_name, zoneidnumber, version, flag_needed + FROM zone + WHERE flag_needed != '' + ORDER BY long_name ASC + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + return; + } + + std::string popup_text = ""; + + popup_text += ""; + + for (auto row : results) { + popup_text += fmt::format( + "", row[0], row[1], - row[3], - row[4] + ( + std::stoi(row[2]) != 0 ? + fmt::format( + "[Version {}]", + row[2] + ) : + "" + ), + row[3] + ); + } + + popup_text += "
ZoneFlag Required
{} ({}){}{}
"; + + c->SendPopupToClient( + "Zone Flags", + popup_text.c_str() + ); + + return; + } else if (is_lock) { + uint32 zone_id = ( + sep->IsNumber(2) ? + std::stoul(sep->arg[2]) : + ZoneID(sep->arg[2]) + ); + std::string zone_short_name = str_tolower(ZoneName(zone_id, true)); + bool is_unknown_zone = zone_short_name.find("unknown") != std::string::npos; + if (zone_id && !is_unknown_zone) { + if (arguments < 3) { + c->Message( + Chat::White, + "Usage: #flagedit lock [Zone ID|Zone Short Name] [Flag Name] - Set the specified flag name on the zone, locking the zone" + ); + return; + } + + std::string flag_name = EscapeString(sep->argplus[3]); + std::string zone_long_name = ZoneLongName(zone_id); + + auto query = fmt::format( + SQL( + UPDATE zone + SET flag_needed = '{}' + WHERE zoneidnumber = {} AND version = {} + ), + flag_name, + zone_id, + zone->GetInstanceVersion() ); - return; - } - - if (!strcasecmp(sep->arg[1], "give")) { - uint32 zoneid = 0; - if (sep->arg[2][0] != '\0') { - zoneid = atoi(sep->arg[2]); - if (zoneid < 1) { - zoneid = ZoneID(sep->arg[2]); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message( + Chat::White, + fmt::format( + "Error updating zone flag for {} ({}).", + zone_long_name, + zone_id + ).c_str() + ); + return; } - } - if (zoneid < 1) { - c->Message(Chat::Red, "zone required. see help."); + + c->Message( + Chat::White, + fmt::format( + "{} ({}) now requires a flag, named {}.", + zone_long_name, + zone_id, + flag_name + ).c_str() + ); return; } - - Mob *t = c->GetTarget(); - if (t == nullptr || !t->IsClient()) { - c->Message(Chat::Red, "client target required"); - return; - } - - t->CastToClient()->SetZoneFlag(zoneid); - return; - } - - if (!strcasecmp(sep->arg[1], "give")) { - uint32 zoneid = 0; - if (sep->arg[2][0] != '\0') { - zoneid = atoi(sep->arg[2]); - if (zoneid < 1) { - zoneid = ZoneID(sep->arg[2]); + } else if (is_take) { + uint32 zone_id = ( + sep->IsNumber(2) ? + std::stoul(sep->arg[2]) : + ZoneID(sep->arg[2]) + ); + std::string zone_short_name = str_tolower(ZoneName(zone_id, true)); + bool is_unknown_zone = zone_short_name.find("unknown") != std::string::npos; + if (zone_id && !is_unknown_zone) { + std::string zone_long_name = ZoneLongName(zone_id); + auto target = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); } - } - if (zoneid < 1) { - c->Message(Chat::Red, "zone required. see help."); + + target->ClearZoneFlag(zone_id); + c->Message( + Chat::White, + fmt::format( + "{} no longer {} the flag for {} ({}).", + c == target ? + "You" : + fmt::format( + "{} ({})", + target->GetCleanName(), + target->GetID() + ), + c == target ? "have" : "has", + zone_long_name, + zone_id + ).c_str() + ); return; } + } else if (is_unlock) { + uint32 zone_id = ( + sep->IsNumber(2) ? + std::stoul(sep->arg[2]) : + ZoneID(sep->arg[2]) + ); + std::string zone_short_name = str_tolower(ZoneName(zone_id, true)); + bool is_unknown_zone = zone_short_name.find("unknown") != std::string::npos; + if (zone_id && !is_unknown_zone) { + std::string zone_long_name = ZoneLongName(zone_id); + auto query = fmt::format( + SQL( + UPDATE zone + SET flag_needed = '' + WHERE zoneidnumber = {} AND version = {} + ), + zone_id, + zone->GetInstanceVersion() + ); + auto results = content_db.QueryDatabase(query); + if (!results.Success()) { + c->Message( + Chat::White, + fmt::format( + "Error updating zone flag for {} ({}).", + zone_long_name, + zone_id + ).c_str() + ); + return; + } - Mob *t = c->GetTarget(); - if (t == nullptr || !t->IsClient()) { - c->Message(Chat::Red, "client target required"); + c->Message( + Chat::White, + fmt::format( + "{} ({}) no longer requires a flag.", + zone_long_name, + zone_id + ).c_str() + ); return; } - - t->CastToClient()->ClearZoneFlag(zoneid); - return; } - - c->Message(Chat::Yellow, "Invalid action specified. use '#flagedit help' for help"); } From 9110bc863ef893b6f2b9643e5a1b4f30cb9d7883 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 10 Feb 2022 16:52:15 -0500 Subject: [PATCH 616/624] [Commands] Cleanup #logs Command. (#1969) - Cleanup messages and logic. - Utilize popup for list and applying settings. --- zone/gm_commands/logs.cpp | 263 ++++++++++++++++++++++++++------------ 1 file changed, 182 insertions(+), 81 deletions(-) diff --git a/zone/gm_commands/logs.cpp b/zone/gm_commands/logs.cpp index 16ba3439a..bda01a63a 100755 --- a/zone/gm_commands/logs.cpp +++ b/zone/gm_commands/logs.cpp @@ -5,97 +5,198 @@ extern WorldServer worldserver; void command_logs(Client *c, const Seperator *sep) { - int logs_set = 0; - if (sep->argnum > 0) { - /* #logs reload_all */ - if (strcasecmp(sep->arg[1], "reload_all") == 0) { - auto pack = new ServerPacket(ServerOP_ReloadLogs, 0); - worldserver.SendPacket(pack); - c->Message( - Chat::Red, - "Successfully sent the packet to world to reload log settings from the database for all zones" - ); - safe_delete(pack); - } - /* #logs list_settings */ - if (strcasecmp(sep->arg[1], "list_settings") == 0 || - (strcasecmp(sep->arg[1], "set") == 0 && strcasecmp(sep->arg[3], "") == 0)) { - c->Message(Chat::White, "[Category ID | console | file | gmsay | Category Description]"); - int redisplay_columns = 0; - for (int i = 0; i < Logs::LogCategory::MaxCategoryID; i++) { - if (redisplay_columns == 10) { - c->Message(Chat::White, "[Category ID | console | file | gmsay | Category Description]"); - redisplay_columns = 0; - } - c->Message( - 0, - StringFormat( - "--- %i | %u | %u | %u | %s", - i, - LogSys.log_settings[i].log_to_console, - LogSys.log_settings[i].log_to_file, - LogSys.log_settings[i].log_to_gmsay, - Logs::LogCategoryName[i] - ).c_str()); - redisplay_columns++; - } - } - /* #logs set */ - if (strcasecmp(sep->arg[1], "set") == 0) { - if (strcasecmp(sep->arg[2], "console") == 0) { - LogSys.log_settings[atoi(sep->arg[3])].log_to_console = atoi(sep->arg[4]); - logs_set = 1; - } - else if (strcasecmp(sep->arg[2], "file") == 0) { - LogSys.log_settings[atoi(sep->arg[3])].log_to_file = atoi(sep->arg[4]); - logs_set = 1; - } - else if (strcasecmp(sep->arg[2], "gmsay") == 0) { - LogSys.log_settings[atoi(sep->arg[3])].log_to_gmsay = atoi(sep->arg[4]); - logs_set = 1; - } - else { - c->Message( - Chat::White, - "--- #logs set [console|file|gmsay] - Sets log settings during the lifetime of the zone" - ); - c->Message(Chat::White, "--- #logs set gmsay 20 1 - Would output Quest errors to gmsay"); - } - if (logs_set == 1) { - c->Message(Chat::Yellow, "Your Log Settings have been applied"); - c->Message( - Chat::Yellow, - "Output Method: %s :: Debug Level: %i - Category: %s", - sep->arg[2], - atoi(sep->arg[4]), - Logs::LogCategoryName[atoi(sep->arg[3])] - ); - } - /* We use a general 'is_category_enabled' now, let's update when we update any output settings - This is used in hot places of code to check if its enabled in any way before triggering logs - */ - if (atoi(sep->arg[4]) > 0) { - LogSys.log_settings[atoi(sep->arg[3])].is_category_enabled = 1; - } - else { - LogSys.log_settings[atoi(sep->arg[3])].is_category_enabled = 0; - } - } + int arguments = sep->argnum; + if (!arguments) { + c->Message( + Chat::White, + "#logs list - Shows current log settings and categories loaded into the current process' memory for the first 50 log categories" + ); + c->Message( + Chat::White, + "#logs list [Start Category ID] - Shows current log settings and categories loaded into the current process' memory, only shows 50 at a time starting at specified Category ID" + ); + c->Message( + Chat::White, + "#logs reload - Reload all settings in world and all zone processes with what is defined in the database" + ); + c->Message( + Chat::White, + "#logs set [console|file|gmsay] [Category ID] [Debug Level (1-3)] - Sets log settings during the lifetime of the zone" + ); + return; } - else { - c->Message(Chat::White, "#logs usage:"); + + bool is_list = !strcasecmp(sep->arg[1], "list"); + bool is_reload = !strcasecmp(sep->arg[1], "reload"); + bool is_set = !strcasecmp(sep->arg[1], "set"); + + if (!is_list && !is_reload && !is_set) { c->Message( Chat::White, - "--- #logs reload_all - Reload all settings in world and all zone processes with what is defined in the database" + "#logs list - Shows current log settings and categories loaded into the current process' memory for the first 50 log categories" ); c->Message( Chat::White, - "--- #logs list_settings - Shows current log settings and categories loaded into the current process' memory" + "#logs list [Start Category ID] - Shows current log settings and categories loaded into the current process' memory, only shows 50 at a time starting at specified Category ID" ); c->Message( Chat::White, - "--- #logs set [console|file|gmsay] - Sets log settings during the lifetime of the zone" + "#logs reload - Reload all settings in world and all zone processes with what is defined in the database" ); + c->Message( + Chat::White, + "#logs set [console|file|gmsay] [Category ID] [Debug Level (1-3)] - Sets log settings during the lifetime of the zone" + ); + return; + } + + if (is_list) { + uint32 start_category_id = 1; + if (sep->IsNumber(2)) { + start_category_id = std::stoul(sep->arg[2]); + } + + uint32 max_category_id = (start_category_id + 49); + + std::string popup_text = ""; + + popup_text += ""; + popup_text += ""; + popup_text += ""; + popup_text += ""; + popup_text += ""; + popup_text += ""; + popup_text += ""; + + for (int index = start_category_id; index <= max_category_id; index++) { + if (index >= Logs::LogCategory::MaxCategoryID) { + max_category_id = (Logs::LogCategory::MaxCategoryID - 1); + break; + } + + popup_text += fmt::format( + "", + index, + Logs::LogCategoryName[index], + LogSys.log_settings[index].log_to_console, + LogSys.log_settings[index].log_to_file, + LogSys.log_settings[index].log_to_gmsay + ); + } + + popup_text += "
IDNameConsoleFileGM Say
{}{}{}{}{}
"; + + std::string popup_title = fmt::format( + "Log Settings [{} to {}]", + start_category_id, + max_category_id + ); + + c->SendPopupToClient( + popup_title.c_str(), + popup_text.c_str() + ); + + c->Message( + Chat::White, + fmt::format( + "Viewing log category settings from {} ({}) to {} ({}).", + Logs::LogCategoryName[start_category_id], + start_category_id, + Logs::LogCategoryName[max_category_id], + max_category_id + ).c_str() + ); + + int next_category_id = (max_category_id + 1); + if (next_category_id < Logs::LogCategory::MaxCategoryID) { + auto next_list_string = fmt::format( + "#logs list {}", + next_category_id + ); + + auto next_list_link = EQ::SayLinkEngine::GenerateQuestSaylink( + next_list_string, + false, + next_list_string + ); + + c->Message( + Chat::White, + fmt::format( + "To view the next 50 log settings, you can use {}.", + next_list_link + ).c_str() + ); + } + } else if (is_reload) { + auto pack = new ServerPacket(ServerOP_ReloadLogs, 0); + worldserver.SendPacket(pack); + c->Message( + Chat::White, + "Reloaded log settings worldwide." + ); + safe_delete(pack); + } else if (is_set) { + auto logs_set = false; + bool is_console = !strcasecmp(sep->arg[2], "console"); + bool is_file = !strcasecmp(sep->arg[2], "file"); + bool is_gmsay = !strcasecmp(sep->arg[2], "gmsay"); + + if (!is_console && !is_file && !is_gmsay) { + c->Message( + Chat::White, + "#logs set [console|file|gmsay] [Category ID] [Debug Level (1-3)] - Sets log settings during the lifetime of the zone" + ); + c->Message(Chat::White, "Example: #logs set gmsay 20 1 - Would output Quest errors to gmsay"); + return; + } + + logs_set = true; + + auto category_id = std::stoul(sep->arg[3]); + auto setting = std::stoul(sep->arg[4]); + + if (is_console) { + LogSys.log_settings[category_id].log_to_console = setting; + } else if (is_file) { + LogSys.log_settings[category_id].log_to_file = setting; + } else if (is_gmsay) { + LogSys.log_settings[category_id].log_to_gmsay = setting; + } + + if (logs_set) { + std::string popup_text = ""; + + popup_text += fmt::format( + "", + category_id + ); + + popup_text += fmt::format( + "", + Logs::LogCategoryName[category_id] + ); + + popup_text += fmt::format( + "", + sep->arg[2] + ); + + popup_text += fmt::format( + "", + setting + ); + + popup_text += "
ID{}
Category{}
Method{}
Setting{}
"; + + c->SendPopupToClient( + "Log Settings Applied", + popup_text.c_str() + ); + } + + LogSys.log_settings[category_id].is_category_enabled = setting ? 1 : 0; } } From fdd260d5fab02c8c306bbabe66f5c6a44e1ae774 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 10 Feb 2022 20:05:36 -0500 Subject: [PATCH 617/624] [Commands] Cleanup #npcloot Command. (#1974) - Cleanup messages and logic. - Utilize item links where helpful. - Implement #npcloot remove all to remove all loot from an NPC using GetLootList(). - #npcloot money now supports 0 to 65,535 for the money types, clamped using EQ::Clamp. --- zone/command.cpp | 2 +- zone/gm_commands/npcloot.cpp | 309 +++++++++++++++++++++++++---------- 2 files changed, 228 insertions(+), 83 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 66c23769d..36da9fc95 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -259,7 +259,7 @@ int command_init(void) command_add("npcedit", "[column] [value] - Mega NPC editing command", AccountStatus::GMAdmin, command_npcedit) || command_add("npceditmass", "[name-search] [column] [value] - Mass (Zone wide) NPC data editing command", AccountStatus::GMAdmin, command_npceditmass) || command_add("npcemote", "[message] - Make your NPC target emote a message.", AccountStatus::GMLeadAdmin, command_npcemote) || - command_add("npcloot", "[show/money/add/remove] [itemid/all/money: pp gp sp cp] - Manipulate the loot an NPC is carrying", AccountStatus::QuestTroupe, command_npcloot) || + command_add("npcloot", "- Manipulate the loot an NPC is carrying. Use #npcloot help for more information.", AccountStatus::QuestTroupe, command_npcloot) || command_add("npcsay", "[message] - Make your NPC target say a message.", AccountStatus::GMLeadAdmin, command_npcsay) || command_add("npcshout", "[message] - Make your NPC target shout a message.", AccountStatus::GMLeadAdmin, command_npcshout) || command_add("npcspawn", "[create/add/update/remove/delete] - Manipulate spawn DB", AccountStatus::GMAreas, command_npcspawn) || diff --git a/zone/gm_commands/npcloot.cpp b/zone/gm_commands/npcloot.cpp index 6ebe557ac..9f95f56ff 100755 --- a/zone/gm_commands/npcloot.cpp +++ b/zone/gm_commands/npcloot.cpp @@ -1,104 +1,249 @@ #include "../client.h" #include "../corpse.h" +#include "../../common/data_verification.h" void command_npcloot(Client *c, const Seperator *sep) { - if (c->GetTarget() == 0) { - c->Message(Chat::White, "Error: No target"); - // #npcloot show + if (!c->GetTarget() || (!c->GetTarget()->IsNPC() && !c->GetTarget()->IsCorpse())) { + c->Message(Chat::White, "You must target an NPC or a Corpse to use this command."); + return; } - else if (strcasecmp(sep->arg[1], "show") == 0) { - if (c->GetTarget()->IsNPC()) { - c->GetTarget()->CastToNPC()->QueryLoot(c); - } - else if (c->GetTarget()->IsCorpse()) { - c->GetTarget()->CastToCorpse()->QueryLoot(c); - } - else { - c->Message(Chat::White, "Error: Target's type doesnt have loot"); - } + + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #npcloot add [Item ID] [Charges] [Equip] [Augment 1 ID] [Augment 2 ID] [Augment 3 ID] [Augment 4 ID] [Augment 5 ID] [Augment 6 ID] - Adds the specified item to an NPC's loot"); + c->Message(Chat::White, "Usage: #npcloot money [Platinum] [Gold] [Silver] [Copper] - Set an NPC's current money"); + c->Message(Chat::White, "Usage: #npcloot remove [All|Item ID] - Remove loot from an NPC by ID or remove all loot"); + c->Message(Chat::White, "Usage: #npcloot show - Shows target NPC's or Corpse's current loot"); + return; } - // These 2 types are *BAD* for the next few commands - else if (c->GetTarget()->IsClient() || c->GetTarget()->IsCorpse()) { - c->Message(Chat::White, "Error: Invalid target type, try a NPC =)."); - // #npcloot add + + bool is_add = !strcasecmp(sep->arg[1], "add"); + bool is_money = !strcasecmp(sep->arg[1], "money"); + bool is_remove = !strcasecmp(sep->arg[1], "remove"); + bool is_show = !strcasecmp(sep->arg[1], "show"); + + if ( + !is_add && + !is_money && + !is_remove && + !is_show + ) { + c->Message(Chat::White, "Usage: #npcloot add [Item ID] [Charges] [Equip] [Augment 1 ID] [Augment 2 ID] [Augment 3 ID] [Augment 4 ID] [Augment 5 ID] [Augment 6 ID] - Adds the specified item to an NPC's loot"); + c->Message(Chat::White, "Usage: #npcloot money [Platinum] [Gold] [Silver] [Copper] - Set an NPC's current money"); + c->Message(Chat::White, "Usage: #npcloot remove [All|Item ID] - Remove loot from an NPC by ID or remove all loot"); + c->Message(Chat::White, "Usage: #npcloot show - Shows target NPC's or Corpse's current loot"); + return; } - else if (strcasecmp(sep->arg[1], "add") == 0) { - // #npcloot add item - if (c->GetTarget()->IsNPC() && sep->IsNumber(2)) { - uint32 item = atoi(sep->arg[2]); - if (database.GetItem(item)) { - if (sep->arg[3][0] != 0 && sep->IsNumber(3)) { - c->GetTarget()->CastToNPC()->AddItem(item, atoi(sep->arg[3]), 0); - } - else { - c->GetTarget()->CastToNPC()->AddItem(item, 1, 0); - } - c->Message(Chat::White, "Added item(%i) to the %s's loot.", item, c->GetTarget()->GetName()); - } - else { - c->Message(Chat::White, "Error: #npcloot add: Item(%i) does not exist!", item); - } + + if (is_add) { + if (!c->GetTarget()->IsNPC() || !sep->IsNumber(2)) { + c->Message(Chat::White, "Usage: #npcloot add [Item ID] [Charges] [Equip] [Augment 1 ID] [Augment 2 ID] [Augment 3 ID] [Augment 4 ID] [Augment 5 ID] [Augment 6 ID] - Adds the specified item to an NPC's loot"); + return; } - else if (!sep->IsNumber(2)) { - c->Message(Chat::White, "Error: #npcloot add: Itemid must be a number."); + + auto item_id = std::stoul(sep->arg[2]); + auto item_charges = sep->IsNumber(3) ? static_cast(std::stoul(sep->arg[3])) : 1; + bool equip_item = arguments >= 4 ? atobool(sep->arg[4]) : false; + auto augment_one_id = sep->IsNumber(5) ? std::stoul(sep->arg[5]) : 0; + auto augment_two_id = sep->IsNumber(6) ? std::stoul(sep->arg[6]) : 0; + auto augment_three_id = sep->IsNumber(7) ? std::stoul(sep->arg[7]) : 0; + auto augment_four_id = sep->IsNumber(8) ? std::stoul(sep->arg[8]) : 0; + auto augment_five_id = sep->IsNumber(9) ? std::stoul(sep->arg[9]) : 0; + auto augment_six_id = sep->IsNumber(10) ? std::stoul(sep->arg[10]) : 0; + + auto item_data = database.GetItem(item_id); + + if (!item_data) { + c->Message( + Chat::White, + fmt::format( + "Item ID {} could not be found", + item_id + ).c_str() + ); + return; } - else { - c->Message(Chat::White, "Error: #npcloot add: This is not a valid target."); + + c->GetTarget()->CastToNPC()->AddItem( + item_id, + item_charges, + equip_item, + augment_one_id, + augment_two_id, + augment_three_id, + augment_four_id, + augment_five_id, + augment_six_id + ); + + auto item = database.CreateItem( + item_id, + item_charges, + augment_one_id, + augment_two_id, + augment_three_id, + augment_four_id, + augment_five_id, + augment_six_id + ); + + EQ::SayLinkEngine linker; + linker.SetLinkType(EQ::saylink::SayLinkItemInst); + linker.SetItemInst(item); + + auto item_link = linker.GenerateLink(); + + c->Message( + Chat::White, + fmt::format( + "Added {} ({}) to {} ({}).", + item_link, + item_id, + c->GetTarget()->GetCleanName(), + c->GetTarget()->GetID() + ).c_str() + ); + } else if (is_money) { + if (!c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; } - } - // #npcloot remove - else if (strcasecmp(sep->arg[1], "remove") == 0) { - //#npcloot remove all - if (strcasecmp(sep->arg[2], "all") == 0) { - c->Message(Chat::White, "Error: #npcloot remove all: Not yet implemented."); - //#npcloot remove itemid + + auto target = c->GetTarget()->CastToNPC(); + + if (sep->IsNumber(2)) { + uint16 platinum = EQ::Clamp(std::stoi(sep->arg[2]), 0, 65535); + uint16 gold = sep->IsNumber(3) ? EQ::Clamp(std::stoi(sep->arg[3]), 0, 65535) : 0; + uint16 silver = sep->IsNumber(4) ? EQ::Clamp(std::stoi(sep->arg[4]), 0, 65535) : 0; + uint16 copper = sep->IsNumber(5) ? EQ::Clamp(std::stoi(sep->arg[5]), 0, 65535) : 0; + target->AddCash( + copper, + silver, + gold, + platinum + ); + + auto money_string = ( + ( + copper || + silver || + gold || + platinum + ) ? + ConvertMoneyToString( + platinum, + gold, + silver, + copper + ) : + "no money" + ); + + c->Message( + Chat::White, + fmt::format( + "{} ({}) now has {}.", + target->GetCleanName(), + target->GetID(), + money_string + ).c_str() + ); + } else { + c->Message(Chat::White, "Usage: #npcloot money [Platinum] [Gold] [Silver] [Copper] - Set an NPC's current money"); } - else { - if (c->GetTarget()->IsNPC() && sep->IsNumber(2)) { - uint32 item = atoi(sep->arg[2]); - c->GetTarget()->CastToNPC()->RemoveItem(item); - c->Message(Chat::White, "Removed item(%i) from the %s's loot.", item, c->GetTarget()->GetName()); - } - else if (!sep->IsNumber(2)) { - c->Message(Chat::White, "Error: #npcloot remove: Item must be a number."); - } - else { - c->Message(Chat::White, "Error: #npcloot remove: This is not a valid target."); - } + } else if (is_remove) { + if (!c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; } - } - // #npcloot money - else if (strcasecmp(sep->arg[1], "money") == 0) { - if (c->GetTarget()->IsNPC() && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4) && sep->IsNumber(5)) { - if ((atoi(sep->arg[2]) < 34465 && atoi(sep->arg[2]) >= 0) && - (atoi(sep->arg[3]) < 34465 && atoi(sep->arg[3]) >= 0) && - (atoi(sep->arg[4]) < 34465 && atoi(sep->arg[4]) >= 0) && - (atoi(sep->arg[5]) < 34465 && atoi(sep->arg[5]) >= 0)) { - c->GetTarget()->CastToNPC()->AddCash( - atoi(sep->arg[5]), - atoi(sep->arg[4]), - atoi(sep->arg[3]), - atoi(sep->arg[2])); + + auto target = c->GetTarget()->CastToNPC(); + + bool is_remove_all = !strcasecmp(sep->arg[2], "all"); + if (is_remove_all) { + auto loot_list = target->GetLootList(); + auto total_item_count = 0; + for (const auto& item_id : loot_list) { + auto item_count = target->CountItem(item_id); + + target->RemoveItem(item_id); + c->Message( Chat::White, - "Set %i Platinum, %i Gold, %i Silver, and %i Copper as %s's money.", - atoi(sep->arg[2]), - atoi(sep->arg[3]), - atoi(sep->arg[4]), - atoi(sep->arg[5]), - c->GetTarget()->GetName()); + fmt::format( + "Removed {} {} ({}) from {} ({}).", + item_count, + database.CreateItemLink(item_id), + item_id, + target->GetCleanName(), + target->GetID() + ).c_str() + ); + + total_item_count += item_count; } - else { - c->Message(Chat::White, "Error: #npcloot money: Values must be between 0-34465."); + + if (!total_item_count) { + c->Message( + Chat::White, + fmt::format( + "{} ({}) has no items to remove.", + target->GetCleanName(), + target->GetID() + ).c_str() + ); + } else { + c->Message( + Chat::White, + fmt::format( + "{} Item{} removed from {} ({}).", + total_item_count, + total_item_count != 1 ? "s" : "", + target->GetCleanName(), + target->GetID() + ).c_str() + ); + } + } else { + if (sep->IsNumber(2)) { + auto item_id = std::stoul(sep->arg[2]); + auto item_count = target->CountItem(item_id); + if (item_count) { + target->RemoveItem(item_id); + c->Message( + Chat::White, + fmt::format( + "Removed {} {} ({}) from {} ({}).", + item_count, + database.CreateItemLink(item_id), + item_id, + target->GetCleanName(), + target->GetID() + ).c_str() + ); + } else { + c->Message( + Chat::White, + fmt::format( + "{} ({}) does not have any {} ({}).", + target->GetCleanName(), + target->GetID(), + database.CreateItemLink(item_id), + item_id + ).c_str() + ); + } + } else { + c->Message(Chat::White, "Usage: #npcloot remove [All|Item ID] - Remove loot from an NPC by ID or remove all loot"); } } - else { - c->Message(Chat::White, "Usage: #npcloot money platinum gold silver copper"); + } else if (is_show) { + if (c->GetTarget()->IsNPC()) { + c->GetTarget()->CastToNPC()->QueryLoot(c); + } else if (c->GetTarget()->IsCorpse()) { + c->GetTarget()->CastToCorpse()->QueryLoot(c); } } - else { - c->Message(Chat::White, "Usage: #npcloot [show/money/add/remove] [itemid/all/money: pp gp sp cp]"); - } } From db988e4261f4fda1439643d23b0998b04ad30c7d Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Thu, 10 Feb 2022 23:55:59 -0500 Subject: [PATCH 618/624] group message (#1987) --- common/ruletypes.h | 1 + zone/client_packet.cpp | 7 +++++++ zone/string_ids.h | 1 + 3 files changed, 9 insertions(+) diff --git a/common/ruletypes.h b/common/ruletypes.h index 6dec50fa3..37be4a438 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -192,6 +192,7 @@ RULE_INT(Character, MagicianTrackingDistanceMultiplier, 0, "If you want magician RULE_INT(Character, EnchanterTrackingDistanceMultiplier, 0, "If you want enchanters to be able to track, increase this above 0. 0 disables tracking packets.") RULE_INT(Character, BeastlordTrackingDistanceMultiplier, 0, "If you want beastlords to be able to track, increase this above 0. 0 disables tracking packets.") RULE_INT(Character, BerserkerTrackingDistanceMultiplier, 0, "If you want berserkers to be able to track, increase this above 0. 0 disables tracking packets.") +RULE_BOOL(Character, OnInviteReceiveAlreadyinGroupMessage, true, "If you want clients to receive a message when trying to invite a player into a group that is currently in another group.") RULE_CATEGORY_END() RULE_CATEGORY(Mercs) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 578de0fe6..fcc2c76e2 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -6960,6 +6960,13 @@ void Client::Handle_OP_GroupInvite2(const EQApplicationPacket *app) Invitee->CastToClient()->QueuePacket(app); } } + else { + if (RuleB(Character, OnInviteReceiveAlreadyinGroupMessage)) { + if (!Invitee->CastToClient()->MercOnlyOrNoGroup()) { + Message(Chat::LightGray, "%s is already in another group.", Invitee->GetCleanName()); + } + } + } } #ifdef BOTS else if (Invitee->IsBot()) { diff --git a/zone/string_ids.h b/zone/string_ids.h index 55cec653c..d5b6c52f3 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -469,6 +469,7 @@ #define LEADER_OF_X_GUILD 12258 #define NOT_IN_A_GUILD 12259 #define TARGET_PLAYER_FOR_GUILD_STATUS 12260 +#define TARGET_ALREADY_IN_GROUP 12265 //% 1 is already in another group. #define GROUP_INVITEE_NOT_FOUND 12268 //You must target a player or use /invite to invite someone to your group. #define GROUP_INVITEE_SELF 12270 //12270 You cannot invite yourself. #define ALREADY_IN_PARTY 12272 //That person is already in your party. From 66935fe21bbee189f5454c94e76f4880f2aaf738 Mon Sep 17 00:00:00 2001 From: KimLS Date: Fri, 11 Feb 2022 01:15:02 -0800 Subject: [PATCH 619/624] Fix for passing std::string to vsprintf --- zone/spell_effects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index f3701f316..01520dc3e 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -8248,7 +8248,7 @@ void Mob::SendCastRestrictionMessage(int requirement_id, bool target_requirement */ - std::string msg = ""; + const char *msg = ""; if (target_requirement) { msg = "Your target does not meet the spell requirements. "; From e2484997dda857177c392ec617cee6dc1a8e85fd Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 11 Feb 2022 10:58:43 -0500 Subject: [PATCH 620/624] revert completed (#1988) Too many issues popping up that are difficult to track down. This was probably not best way to solve the problem. --- zone/aa.cpp | 59 ++++++++++------------------ zone/client.h | 2 - zone/effects.cpp | 48 +++++++++++++++++++---- zone/mob.h | 2 +- zone/spells.cpp | 100 ++++++++++++----------------------------------- 5 files changed, 86 insertions(+), 125 deletions(-) diff --git a/zone/aa.cpp b/zone/aa.cpp index b5a85f5e6..df46bbb8f 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -1239,10 +1239,6 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { if (!IsValidSpell(rank->spell)) { return; } - //do not allow AA to cast if your actively casting another AA. - if (rank->spell == casting_spell_id && rank->id == casting_spell_aa_id) { - return; - } if (!CanUseAlternateAdvancementRank(rank)) { return; @@ -1257,13 +1253,11 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { uint32 charges = 0; // We don't have the AA - if (!GetAA(rank_id, &charges)) { + if (!GetAA(rank_id, &charges)) return; - } //if expendable make sure we have charges - if (ability->charges > 0 && charges < 1) { + if (ability->charges > 0 && charges < 1) return; - } //check cooldown if (!p_timers.Expired(&database, rank->spell_type + pTimerAAStart, false)) { @@ -1280,28 +1274,32 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { Message(Chat::Red, "You can use this ability again in %u minute(s) %u seconds", aaremain_min, aaremain_sec); } + return; } - if (!IsCastWhileInvis(rank->spell)) { - CommonBreakInvisible(); + //calculate cooldown + int cooldown = rank->recast_time - GetAlternateAdvancementCooldownReduction(rank); + if (cooldown < 0) { + cooldown = 0; } + if (!IsCastWhileInvis(rank->spell)) + CommonBreakInvisible(); + if (spells[rank->spell].sneak && (!hidden || (hidden && (Timer::GetCurrentTime() - tmHidden) < 4000))) { MessageString(Chat::SpellFailure, SNEAK_RESTRICT); return; } // // Modern clients don't require pet targeted for AA casts that are ST_Pet - if (spells[rank->spell].target_type == ST_Pet || spells[rank->spell].target_type == ST_SummonedPet) { + if (spells[rank->spell].target_type == ST_Pet || spells[rank->spell].target_type == ST_SummonedPet) target_id = GetPetID(); - } // extra handling for cast_not_standing spells if (!IgnoreCastingRestriction(rank->spell)) { - if (GetAppearance() == eaSitting) { // we need to stand! + if (GetAppearance() == eaSitting) // we need to stand! SetAppearance(eaStanding, false); - } if (GetAppearance() != eaStanding) { MessageString(Chat::SpellFailure, STAND_TO_CAST); @@ -1318,34 +1316,20 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { if (!DoCastingChecksOnCaster(rank->spell)) { return; } - SpellFinished(rank->spell, entity_list.GetMob(target_id), EQ::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].resist_difficulty, false, -1, false, rank->id); + if (!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQ::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].resist_difficulty, false)) { + return; + } + ExpendAlternateAdvancementCharge(ability->id); } - //Known issue: If you attempt to give a Bard an AA with a cast time, the cast timer will not display on the client (no live bard AA have cast time). else { - CastSpell(rank->spell, target_id, EQ::spells::CastingSlot::AltAbility, -1, -1, 0, -1, 0xFFFFFFFF, 0, nullptr, rank->id); + if (!CastSpell(rank->spell, target_id, EQ::spells::CastingSlot::AltAbility, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, cooldown, nullptr, rank->id)) { + return; + } } } -} -void Client::SetAARecastTimer(AA::Rank *rank_in, int32 spell_id) { - - if (!rank_in) { - return; - } - - int timer_duration = rank_in->recast_time; - - if (timer_duration) { - timer_duration = rank_in->recast_time - GetAlternateAdvancementCooldownReduction(rank_in); - } - - if (timer_duration <= 0) { - return; - } - - CastToClient()->GetPTimers().Start(rank_in->spell_type + pTimerAAStart, timer_duration); - CastToClient()->SendAlternateAdvancementTimer(rank_in->spell_type, 0, 0); - LogSpells("Spell [{}]: Setting AA reuse timer [{}] to [{}]", spell_id, rank_in->spell_type + pTimerAAStart, timer_duration); + CastToClient()->GetPTimers().Start(rank->spell_type + pTimerAAStart, cooldown); + SendAlternateAdvancementTimer(rank->spell_type, 0, 0); } int Mob::GetAlternateAdvancementCooldownReduction(AA::Rank *rank_in) { @@ -1379,7 +1363,6 @@ int Mob::GetAlternateAdvancementCooldownReduction(AA::Rank *rank_in) { } void Mob::ExpendAlternateAdvancementCharge(uint32 aa_id) { - for (auto &iter : aa_ranks) { AA::Ability *ability = zone->GetAlternateAdvancementAbility(iter.first); if (ability && aa_id == ability->id) { diff --git a/zone/client.h b/zone/client.h index 5cbc203ac..6b8c1e1a1 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1495,8 +1495,6 @@ public: void SendItemRecastTimer(int32 recast_type, uint32 recast_delay = 0); void SetItemRecastTimer(int32 spell_id, uint32 inventory_slot); bool HasItemRecastTimer(int32 spell_id, uint32 inventory_slot); - void SetDisciplineRecastTimer(int32 spell_id); - void SetAARecastTimer(AA::Rank *rank_in, int32 spell_id); inline bool AggroMeterAvailable() const { return ((m_ClientVersionBit & EQ::versions::maskRoF2AndLater)) && RuleB(Character, EnableAggroMeter); } // RoF untested inline void SetAggroMeterLock(int in) { m_aggrometer.set_lock_id(in); } diff --git a/zone/effects.cpp b/zone/effects.cpp index cfb9dbdf9..aa41f35a0 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -800,18 +800,50 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) { return false; } - if (GetClass() == BARD && IsCasting() && spells[spell_id].cast_time == 0) { - if (DoCastingChecksOnCaster(spell_id)) { - SpellFinished(spell_id, entity_list.GetMob(target), EQ::spells::CastingSlot::Discipline); + bool instant_recast = true; + + if (spell.recast_time > 0) { + uint32 reduced_recast = spell.recast_time / 1000; + auto focus = GetFocusEffect(focusReduceRecastTime, spell_id); + // do stupid stuff because custom servers. + // we really should be able to just do the -= focus but since custom servers could have shorter reuse timers + // we have to make sure we don't underflow the uint32 ... + // and yes, the focus effect can be used to increase the durations (spell 38944) + if (focus > reduced_recast) { + reduced_recast = 0; + if (GetPTimers().Enabled((uint32)DiscTimer)) + GetPTimers().Clear(&database, (uint32)DiscTimer); } - } - else { - if (!CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline)) { - LogSpells("Discipline [{}] failed at cast spell.", spell_id); - return false; + else { + reduced_recast -= focus; + } + + if (reduced_recast > 0) { + instant_recast = false; + + if (GetClass() == BARD && IsCasting() && spells[spell_id].cast_time == 0) { + if (DoCastingChecksOnCaster(spell_id)) { + SpellFinished(spell_id, entity_list.GetMob(target), EQ::spells::CastingSlot::Discipline, 0, -1, spells[spell_id].resist_difficulty, false, -1, (uint32)DiscTimer, reduced_recast, false); + } + } + else { + CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline, -1, -1, 0, -1, (uint32)DiscTimer, reduced_recast); + } + + SendDisciplineTimer(spells[spell_id].timer_id, reduced_recast); } } + if (instant_recast) { + if (GetClass() == BARD && IsCasting() && spells[spell_id].cast_time == 0) { + if (DoCastingChecksOnCaster(spell_id)) { + SpellFinished(spell_id, entity_list.GetMob(target), EQ::spells::CastingSlot::Discipline, 0, -1, spells[spell_id].resist_difficulty, false, -1, 0xFFFFFFFF, 0, false); + } + } + else { + CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline); + } + } return(true); } diff --git a/zone/mob.h b/zone/mob.h index c2c28296f..d0b3988ea 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -326,7 +326,7 @@ public: void CastedSpellFinished(uint16 spell_id, uint32 target_id, EQ::spells::CastingSlot slot, uint16 mana_used, uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0); bool SpellFinished(uint16 spell_id, Mob *target, EQ::spells::CastingSlot slot = EQ::spells::CastingSlot::Item, uint16 mana_used = 0, - uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false, int level_override = -1, bool from_casted_spell = false, uint32 aa_id = 0); + uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false, int level_override = -1, uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, bool from_casted_spell = false); void SendBeginCast(uint16 spell_id, uint32 casttime); virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar, int reflect_effectiveness = 0, bool use_resist_adjust = false, int16 resist_adjust = 0, bool isproc = false, int level_override = -1, int32 duration_override = 0); diff --git a/zone/spells.cpp b/zone/spells.cpp index 1bfde6a53..226670cf4 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1601,7 +1601,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo } // we're done casting, now try to apply the spell - if(!SpellFinished(spell_id, spell_target, slot, mana_used, inventory_slot, resist_adjust, false,-1, true)) + if(!SpellFinished(spell_id, spell_target, slot, mana_used, inventory_slot, resist_adjust, false,-1, 0xFFFFFFFF, 0, true)) { LogSpells("Casting of [{}] canceled: SpellFinished returned false", spell_id); // most of the cases we return false have a message already or are logic errors that shouldn't happen @@ -2221,7 +2221,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce // if you need to abort the casting, return false bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, uint16 mana_used, uint32 inventory_slot, int16 resist_adjust, bool isproc, int level_override, - bool from_casted_spell, uint32 aa_id) + uint32 timer, uint32 timer_duration, bool from_casted_spell) { Mob *ae_center = nullptr; @@ -2595,54 +2595,32 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui if (mgb) { SetMGB(false); } - - //all spell triggers use Item slot, but don't have an item associated. We don't need to check recast timers on these. - bool is_triggered_spell = false; - if (slot == CastingSlot::Item && inventory_slot == 0xFFFFFFFF) { - is_triggered_spell = true; - } - - if (IsClient() && !isproc && !is_triggered_spell) + /* + Set Recast Timer on spells. + */ + if(IsClient() && !isproc) { - //Set Item or Augment Click Recast Timer - if (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt) { - CastToClient()->SetItemRecastTimer(spell_id, inventory_slot); + //Support for bards to get disc recast timers while singing + if (GetClass() == BARD && spell_id != casting_spell_id && timer != 0xFFFFFFFF) { + CastToClient()->GetPTimers().Start(timer, timer_duration); + LogSpells("Spell [{}]: Setting bard disciple reuse timer from spell finished [{}] to [{}]", spell_id, timer, timer_duration); } - //Set Discipline Recast Timer - else if (slot == CastingSlot::Discipline) { - if (spell_id == casting_spell_id || (GetClass() == BARD && spells[spell_id].cast_time == 0 && spell_id != casting_spell_id)) { - CastToClient()->SetDisciplineRecastTimer(spell_id); - } - } - //Set AA Recast Timer. - else if (slot == CastingSlot::AltAbility){ - uint32 active_aa_id = 0; - //aa_id is only passed directly into spellfinished when a bard is using AA while casting, this supports casting an AA while clicking an instant AA. - if (GetClass() == BARD && spells[spell_id].cast_time == 0 && aa_id) { - active_aa_id = aa_id; - } - else { - active_aa_id = casting_spell_aa_id; - } - - AA::Rank *rank = zone->GetAlternateAdvancementRank(active_aa_id); - CastToClient()->SetAARecastTimer(rank, spell_id); + if(casting_spell_aa_id) { + AA::Rank *rank = zone->GetAlternateAdvancementRank(casting_spell_aa_id); - if (rank && rank->base_ability) { + if(rank && rank->base_ability) { ExpendAlternateAdvancementCharge(rank->base_ability->id); } } - //Set Custom Recast Timer - else if (spell_id == casting_spell_id && casting_spell_timer != 0xFFFFFFFF) + else if(spell_id == casting_spell_id && casting_spell_timer != 0xFFFFFFFF) { //aa new todo: aa expendable charges here CastToClient()->GetPTimers().Start(casting_spell_timer, casting_spell_timer_duration); LogSpells("Spell [{}]: Setting custom reuse timer [{}] to [{}]", spell_id, casting_spell_timer, casting_spell_timer_duration); } - //Set Spell Recast Timer - else if (spells[spell_id].recast_time > 1000 && !spells[spell_id].is_discipline) { - int recast = spells[spell_id].recast_time / 1000; + else if(spells[spell_id].recast_time > 1000 && !spells[spell_id].is_discipline) { + int recast = spells[spell_id].recast_time/1000; if (spell_id == SPELL_LAY_ON_HANDS) //lay on hands { recast -= GetAA(aaFervrentBlessing) * 420; @@ -2662,14 +2640,20 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui } recast = std::max(recast, 0); } - + LogSpells("Spell [{}]: Setting long reuse timer to [{}] s (orig [{}])", spell_id, recast, spells[spell_id].recast_time); - + if (recast > 0) { CastToClient()->GetPTimers().Start(pTimerSpellStart + spell_id, recast); } } } + /* + Set Recast Timer on item clicks, including augmenets. + */ + if(IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt)){ + CastToClient()->SetItemRecastTimer(spell_id, inventory_slot); + } if (IsNPC()) { CastToNPC()->AI_Event_SpellCastFinished(true, static_cast(slot)); @@ -2730,8 +2714,6 @@ bool Mob::ApplyBardPulse(int32 spell_id, Mob *spell_target, CastingSlot slot) { if (!SpellFinished(spell_id, spell_target, slot, spells[spell_id].mana, 0xFFFFFFFF, spells[spell_id].resist_difficulty)) { return false; } - - return true; } /////////////////////////////////////////////////////////////////////////////// @@ -6229,40 +6211,6 @@ bool Client::HasItemRecastTimer(int32 spell_id, uint32 inventory_slot) return false; } -void Client::SetDisciplineRecastTimer(int32 spell_id) { - - if (!IsValidSpell(spell_id)) { - return; - } - - if (spells[spell_id].recast_time == 0) { - return; - } - - pTimerType DiscTimer = pTimerDisciplineReuseStart + spells[spell_id].timer_id; - uint32 timer_duration = spells[spell_id].recast_time / 1000; - auto focus = GetFocusEffect(focusReduceRecastTime, spell_id); - - if (focus > timer_duration) { - timer_duration = 0; - if (GetPTimers().Enabled((uint32)DiscTimer)) { - GetPTimers().Clear(&database, (uint32)DiscTimer); - } - } - else { - timer_duration -= focus; - } - - if (timer_duration <= 0) { - return; - } - - CastToClient()->GetPTimers().Start((uint32)DiscTimer, timer_duration); - CastToClient()->SendDisciplineTimer(spells[spell_id].timer_id, timer_duration); - LogSpells("Spell [{}]: Setting disciple reuse timer [{}] to [{}]", spell_id, spells[spell_id].timer_id, timer_duration); -} - - void Mob::CalcDestFromHeading(float heading, float distance, float MaxZDiff, float StartX, float StartY, float &dX, float &dY, float &dZ) { if (!distance) { return; } From 99793cab8b5550cc711e3137fc494669d789da11 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 11 Feb 2022 16:25:59 -0500 Subject: [PATCH 621/624] [Spells] Fix for AA recast timers not resetting properly (#1989) * revert completed Too many issues popping up that are difficult to track down. This was probably not best way to solve the problem. * fixed for real * Update aa.cpp * Update aa.cpp * Update aa.cpp * should work * remove spaces * [Spells] Fix for AA recast timers not resetting properly --- zone/aa.cpp | 17 +++++++---------- zone/mob.h | 2 +- zone/spells.cpp | 33 ++++++++++++++++++++++----------- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/zone/aa.cpp b/zone/aa.cpp index df46bbb8f..3607385d2 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -1278,10 +1278,9 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { return; } - //calculate cooldown - int cooldown = rank->recast_time - GetAlternateAdvancementCooldownReduction(rank); - if (cooldown < 0) { - cooldown = 0; + int timer_duration = rank->recast_time - GetAlternateAdvancementCooldownReduction(rank); + if (timer_duration < 0) { + timer_duration = 0; } if (!IsCastWhileInvis(rank->spell)) @@ -1316,20 +1315,18 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { if (!DoCastingChecksOnCaster(rank->spell)) { return; } - if (!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQ::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].resist_difficulty, false)) { + + if (!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQ::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].resist_difficulty, false, -1, + rank->spell_type + pTimerAAStart, timer_duration, false, rank->id)) { return; } - ExpendAlternateAdvancementCharge(ability->id); } else { - if (!CastSpell(rank->spell, target_id, EQ::spells::CastingSlot::AltAbility, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, cooldown, nullptr, rank->id)) { + if (!CastSpell(rank->spell, target_id, EQ::spells::CastingSlot::AltAbility, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, timer_duration, nullptr, rank->id)) { return; } } } - - CastToClient()->GetPTimers().Start(rank->spell_type + pTimerAAStart, cooldown); - SendAlternateAdvancementTimer(rank->spell_type, 0, 0); } int Mob::GetAlternateAdvancementCooldownReduction(AA::Rank *rank_in) { diff --git a/zone/mob.h b/zone/mob.h index d0b3988ea..ca38653fa 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -326,7 +326,7 @@ public: void CastedSpellFinished(uint16 spell_id, uint32 target_id, EQ::spells::CastingSlot slot, uint16 mana_used, uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0); bool SpellFinished(uint16 spell_id, Mob *target, EQ::spells::CastingSlot slot = EQ::spells::CastingSlot::Item, uint16 mana_used = 0, - uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false, int level_override = -1, uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, bool from_casted_spell = false); + uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false, int level_override = -1, uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, bool from_casted_spell = false, uint32 aa_id = 0); void SendBeginCast(uint16 spell_id, uint32 casttime); virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar, int reflect_effectiveness = 0, bool use_resist_adjust = false, int16 resist_adjust = 0, bool isproc = false, int level_override = -1, int32 duration_override = 0); diff --git a/zone/spells.cpp b/zone/spells.cpp index 226670cf4..ee73add8c 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2221,7 +2221,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce // if you need to abort the casting, return false bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, uint16 mana_used, uint32 inventory_slot, int16 resist_adjust, bool isproc, int level_override, - uint32 timer, uint32 timer_duration, bool from_casted_spell) + uint32 timer, uint32 timer_duration, bool from_casted_spell, uint32 aa_id) { Mob *ae_center = nullptr; @@ -2600,22 +2600,33 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui */ if(IsClient() && !isproc) { - //Support for bards to get disc recast timers while singing + if (slot == CastingSlot::AltAbility) { + if (!aa_id) { + aa_id = casting_spell_aa_id; + } + if (aa_id) { + AA::Rank *rank = zone->GetAlternateAdvancementRank(aa_id); + //handle expendable AA's + if (rank && rank->base_ability) { + ExpendAlternateAdvancementCharge(rank->base_ability->id); + } + //set AA recast timer + CastToClient()->SendAlternateAdvancementTimer(rank->spell_type, 0, 0); + } + } + //handle bard AA and Discipline recast timers when singing if (GetClass() == BARD && spell_id != casting_spell_id && timer != 0xFFFFFFFF) { CastToClient()->GetPTimers().Start(timer, timer_duration); - LogSpells("Spell [{}]: Setting bard disciple reuse timer from spell finished [{}] to [{}]", spell_id, timer, timer_duration); + LogSpells("Spell [{}]: Setting BARD custom reuse timer [{}] to [{}]", spell_id, casting_spell_timer, casting_spell_timer_duration); } - - if(casting_spell_aa_id) { - AA::Rank *rank = zone->GetAlternateAdvancementRank(casting_spell_aa_id); - - if(rank && rank->base_ability) { - ExpendAlternateAdvancementCharge(rank->base_ability->id); - } + //handles AA and Discipline recast timers + else if (spell_id == casting_spell_id && casting_spell_timer != 0xFFFFFFFF) + { + CastToClient()->GetPTimers().Start(casting_spell_timer, casting_spell_timer_duration); + LogSpells("Spell [{}]: Setting custom reuse timer [{}] to [{}]", spell_id, casting_spell_timer, casting_spell_timer_duration); } else if(spell_id == casting_spell_id && casting_spell_timer != 0xFFFFFFFF) { - //aa new todo: aa expendable charges here CastToClient()->GetPTimers().Start(casting_spell_timer, casting_spell_timer_duration); LogSpells("Spell [{}]: Setting custom reuse timer [{}] to [{}]", spell_id, casting_spell_timer, casting_spell_timer_duration); } From d2d7b8108d41586a1d7a3e3530a48f22ada9b9f0 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 11 Feb 2022 16:26:08 -0500 Subject: [PATCH 622/624] [Commands] Cleanup #ai Command. (#1980) - Cleanup messages and logic. - Remove #ai start/#ai stop as they can crash zones and are mostly useless. - Add EQ::constants::GetConsiderLevelMap() and EQ::constants::GetConsiderLevelName(). - Add quest::getconsiderlevelname(consider_level) to Perl. - Add eq.get_consider_level_name(consider_level) to Lua. --- common/emu_constants.cpp | 25 +++ common/emu_constants.h | 15 ++ zone/embparser_api.cpp | 18 ++ zone/gm_commands/ai.cpp | 360 ++++++++++++++++++++++++++------------- zone/lua_general.cpp | 5 + zone/questmgr.cpp | 4 + zone/questmgr.h | 1 + 7 files changed, 310 insertions(+), 118 deletions(-) diff --git a/common/emu_constants.cpp b/common/emu_constants.cpp index 99710712e..4d5c40b64 100644 --- a/common/emu_constants.cpp +++ b/common/emu_constants.cpp @@ -338,6 +338,31 @@ std::string EQ::constants::GetAccountStatusName(uint8 account_status) return status_name; } +const std::map& EQ::constants::GetConsiderLevelMap() +{ + static const std::map consider_level_map = { + { ConsiderLevel::Ally, "Ally" }, + { ConsiderLevel::Warmly, "Warmly" }, + { ConsiderLevel::Kindly, "Kindly" }, + { ConsiderLevel::Amiably, "Amiably" }, + { ConsiderLevel::Indifferently, "Indifferently" }, + { ConsiderLevel::Apprehensively, "Apprehensively" }, + { ConsiderLevel::Dubiously, "Dubiously" }, + { ConsiderLevel::Threateningly, "Threateningly" }, + { ConsiderLevel::Scowls, "Scowls" } + }; + return consider_level_map; +} + +std::string EQ::constants::GetConsiderLevelName(uint8 faction_consider_level) +{ + auto consider_levels = EQ::constants::GetConsiderLevelMap(); + if (!consider_levels[faction_consider_level].empty()) { + return consider_levels[faction_consider_level]; + } + return std::string(); +} + const std::map& EQ::constants::GetEnvironmentalDamageMap() { static const std::map damage_type_map = { diff --git a/common/emu_constants.h b/common/emu_constants.h index 527df11a1..f6272274e 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -255,6 +255,9 @@ namespace EQ extern const std::map& GetAccountStatusMap(); std::string GetAccountStatusName(uint8 account_status); + extern const std::map& GetConsiderLevelMap(); + std::string GetConsiderLevelName(uint8 consider_level); + extern const std::map& GetEnvironmentalDamageMap(); std::string GetEnvironmentalDamageName(uint8 damage_type); @@ -401,4 +404,16 @@ enum AugmentActions : int { Destroy }; +enum ConsiderLevel : uint8 { + Ally = 1, + Warmly, + Kindly, + Amiably, + Indifferently, + Apprehensively, + Dubiously, + Threateningly, + Scowls +}; + #endif /*COMMON_EMU_CONSTANTS_H*/ diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 47c85161b..04a8654f1 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -8092,6 +8092,23 @@ XS(XS__getbodytypename) { } } +XS(XS__getconsiderlevelname); +XS(XS__getconsiderlevelname) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::getconsiderlevelname(uint8 consider_level)"); + { + dXSTARG; + uint8 consider_level = (uint8) SvUV(ST(0)); + std::string consider_level_name = quest_manager.getconsiderlevelname(consider_level); + + sv_setpv(TARG, consider_level_name.c_str()); + XSprePUSH; + PUSHTARG; + XSRETURN(1); + } +} + XS(XS__getenvironmentaldamagename); XS(XS__getenvironmentaldamagename) { dXSARGS; @@ -8392,6 +8409,7 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "getcharidbyname"), XS__getcharidbyname, file); newXS(strcpy(buf, "getclassname"), XS__getclassname, file); newXS(strcpy(buf, "getcleannpcnamebyid"), XS__getcleannpcnamebyid, file); + newXS(strcpy(buf, "getconsiderlevelname"), XS__getconsiderlevelname, file); newXS(strcpy(buf, "gethexcolorcode"), XS__gethexcolorcode, file); newXS(strcpy(buf, "getcurrencyid"), XS__getcurrencyid, file); newXS(strcpy(buf, "getexpmodifierbycharid"), XS__getexpmodifierbycharid, file); diff --git a/zone/gm_commands/ai.cpp b/zone/gm_commands/ai.cpp index 93f9a057f..e7f686307 100755 --- a/zone/gm_commands/ai.cpp +++ b/zone/gm_commands/ai.cpp @@ -2,138 +2,262 @@ void command_ai(Client *c, const Seperator *sep) { - Mob *target = c->GetTarget(); + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #ai consider [Mob Name] - Show how an NPC considers to a mob"); + c->Message(Chat::White, "Usage: #ai faction [Faction ID] - Set an NPC's Faction ID"); + c->Message(Chat::White, "Usage: #ai guard - Save an NPC's guard spot to their current location"); + c->Message(Chat::White, "Usage: #ai roambox [Distance] [Min X] [Max X] [Min Y] [Max Y] [Delay] [Minimum Delay] - Set an NPC's roambox using X and Y coordinates"); + c->Message(Chat::White, "Usage: #ai roambox [Distance] [Roam Distance] [Delay] [Minimum Delay] - Set an NPC's roambox using roam distance"); + c->Message(Chat::White, "Usage: #ai spells [Spell List ID] - Set an NPC's Spell List ID"); + return; + } - if (strcasecmp(sep->arg[1], "factionid") == 0) { - if (target && sep->IsNumber(2)) { - if (target->IsNPC()) { - target->CastToNPC()->SetNPCFactionID(atoi(sep->arg[2])); - } - else { - c->Message(Chat::White, "%s is not an NPC.", target->GetName()); - } - } - else { - c->Message(Chat::White, "Usage: (targeted) #ai factionid [factionid]"); - } + if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; } - else if (strcasecmp(sep->arg[1], "spellslist") == 0) { - if (target && sep->IsNumber(2) && atoi(sep->arg[2]) >= 0) { - if (target->IsNPC()) { - target->CastToNPC()->AI_AddNPCSpells(atoi(sep->arg[2])); - } - else { - c->Message(Chat::White, "%s is not an NPC.", target->GetName()); - } - } - else { - c->Message(Chat::White, "Usage: (targeted) #ai spellslist [npc_spells_id]"); - } + + auto target = c->GetTarget()->CastToNPC(); + + bool is_consider = !strcasecmp(sep->arg[1], "consider"); + bool is_faction = !strcasecmp(sep->arg[1], "faction"); + bool is_guard = !strcasecmp(sep->arg[1], "guard"); + bool is_roambox = !strcasecmp(sep->arg[1], "roambox"); + bool is_spells = !strcasecmp(sep->arg[1], "spells"); + + if ( + !is_consider && + !is_faction && + !is_guard && + !is_roambox && + !is_spells + ) { + c->Message(Chat::White, "Usage: #ai consider [Mob Name] - Show how an NPC considers to a mob"); + c->Message(Chat::White, "Usage: #ai faction [Faction ID] - Set an NPC's Faction ID"); + c->Message(Chat::White, "Usage: #ai guard - Save an NPC's guard spot to their current location"); + c->Message(Chat::White, "Usage: #ai roambox [Distance] [Min X] [Max X] [Min Y] [Max Y] [Delay] [Minimum Delay] - Set an NPC's roambox using X and Y coordinates"); + c->Message(Chat::White, "Usage: #ai roambox [Distance] [Roam Distance] [Delay] [Minimum Delay] - Set an NPC's roambox using roam distance"); + c->Message(Chat::White, "Usage: #ai spells [Spell List ID] - Set an NPC's Spell List ID"); + return; } - else if (strcasecmp(sep->arg[1], "con") == 0) { - if (target && sep->arg[2][0] != 0) { - Mob *tar2 = entity_list.GetMob(sep->arg[2]); - if (tar2) { + + if (is_consider) { + if (arguments == 2) { + auto mob_name = sep->arg[2]; + auto mob_to_consider = entity_list.GetMob(mob_name); + if (mob_to_consider) { + auto consider_level = static_cast(mob_to_consider->GetReverseFactionCon(target)); c->Message( Chat::White, - "%s considering %s: %i", - target->GetName(), - tar2->GetName(), - tar2->GetReverseFactionCon(target)); - } - else { - c->Message(Chat::White, "Error: %s not found.", sep->arg[2]); - } - } - else { - c->Message(Chat::White, "Usage: (targeted) #ai con [mob name]"); - } - } - else if (strcasecmp(sep->arg[1], "guard") == 0) { - if (target && target->IsNPC()) { - target->CastToNPC()->SaveGuardSpot(target->GetPosition()); - } - else { - c->Message( - Chat::White, - "Usage: (targeted) #ai guard - sets npc to guard the current location (use #summon to move)" - ); - } - } - else if (strcasecmp(sep->arg[1], "roambox") == 0) { - if (target && target->IsAIControlled() && target->IsNPC()) { - if ((sep->argnum == 6 || sep->argnum == 7 || sep->argnum == 8) && sep->IsNumber(2) && sep->IsNumber(3) && - sep->IsNumber(4) && sep->IsNumber(5) && sep->IsNumber(6)) { - uint32 tmp = 2500; - uint32 tmp2 = 2500; - if (sep->IsNumber(7)) { - tmp = atoi(sep->arg[7]); - } - if (sep->IsNumber(8)) { - tmp2 = atoi(sep->arg[8]); - } - target->CastToNPC()->AI_SetRoambox( - atof(sep->arg[2]), - atof(sep->arg[3]), - atof(sep->arg[4]), - atof(sep->arg[5]), - atof(sep->arg[6]), - tmp, - tmp2 + fmt::format( + "{} ({}) considers {} ({}) as {} ({}).", + target->GetCleanName(), + target->GetID(), + mob_to_consider->GetCleanName(), + mob_to_consider->GetID(), + EQ::constants::GetConsiderLevelName(consider_level), + consider_level + ).c_str() ); } - else if ((sep->argnum == 3 || sep->argnum == 4) && sep->IsNumber(2) && sep->IsNumber(3)) { - uint32 tmp = 2500; - uint32 tmp2 = 2500; + } else { + c->Message(Chat::White, "Usage: #ai consider [Mob Name] - Show how an NPC considers a mob"); + } + } else if (is_faction) { + if (sep->IsNumber(2)) { + auto faction_id = std::stoi(sep->arg[2]); + auto faction_name = content_db.GetFactionName(faction_id); + target->SetNPCFactionID(faction_id); + c->Message( + Chat::White, + fmt::format( + "{} ({}) is now on Faction {}.", + target->GetCleanName(), + target->GetID(), + ( + faction_name.empty() ? + std::to_string(faction_id) : + fmt::format( + "{} ({})", + faction_name, + faction_id + ) + ) + ).c_str() + ); + } else { + c->Message(Chat::White, "Usage: #ai faction [Faction ID] - Set an NPC's Faction ID"); + } + } else if (is_guard) { + auto target_position = target->GetPosition(); + + target->SaveGuardSpot(target_position); + + c->Message( + Chat::White, + fmt::format( + "{} ({}) now has a guard spot of {:.2f}, {:.2f}, {:.2f} with a heading of {:.2f}.", + target->GetCleanName(), + target->GetID(), + target_position.x, + target_position.y, + target_position.z, + target_position.w + ).c_str() + ); + } else if (is_roambox) { + if (target->IsAIControlled()) { + if ( + arguments >= 6 && + arguments <= 8 && + sep->IsNumber(2) && + sep->IsNumber(3) && + sep->IsNumber(4) && + sep->IsNumber(5) && + sep->IsNumber(6) + ) { + auto distance = std::stof(sep->arg[2]); + auto min_x = std::stof(sep->arg[3]); + auto max_x = std::stof(sep->arg[4]); + auto min_y = std::stof(sep->arg[5]); + auto max_y = std::stof(sep->arg[6]); + + uint32 delay = 2500; + uint32 minimum_delay = 2500; + + if (sep->IsNumber(7)) { + delay = std::stoul(sep->arg[7]); + } + + if (sep->IsNumber(8)) { + minimum_delay = std::stoul(sep->arg[8]); + } + + target->CastToNPC()->AI_SetRoambox( + distance, + max_x, + min_x, + max_y, + min_y, + delay, + minimum_delay + ); + + c->Message( + Chat::White, + fmt::format( + "{} ({}) now has a roambox from {}, {} to {}, {} with {} and {} and a distance of {}.", + target->GetCleanName(), + target->GetID(), + min_x, + min_y, + max_x, + max_y, + ( + delay ? + fmt::format( + "a delay of {} ({})", + ConvertMillisecondsToTime(delay), + delay + ): + "no delay" + ), + ( + minimum_delay ? + fmt::format( + "a minimum delay of {} ({})", + ConvertMillisecondsToTime(minimum_delay), + minimum_delay + ): + "no minimum delay" + ), + distance + ).c_str() + ); + } else if ( + arguments >= 3 && + arguments <= 4 && + sep->IsNumber(2) && + sep->IsNumber(3) + ) { + auto max_distance = std::stof(sep->arg[2]); + auto roam_distance_variance = std::stof(sep->arg[3]); + + uint32 delay = 2500; + uint32 minimum_delay = 2500; + if (sep->IsNumber(4)) { - tmp = atoi(sep->arg[4]); + delay = std::stoul(sep->arg[4]); } + if (sep->IsNumber(5)) { - tmp2 = atoi(sep->arg[5]); + minimum_delay = std::stoul(sep->arg[5]); } - target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), tmp, tmp2); + + target->CastToNPC()->AI_SetRoambox( + max_distance, + roam_distance_variance, + delay, + minimum_delay + ); + + c->Message( + Chat::White, + fmt::format( + "{} ({}) now has a roambox with a max distance of {} and a roam distance variance of {} with {} and {}.", + target->GetCleanName(), + target->GetID(), + max_distance, + roam_distance_variance, + ( + delay ? + fmt::format( + "a delay of {} ({})", + delay, + ConvertMillisecondsToTime(delay) + ): + "no delay" + ), + ( + minimum_delay ? + fmt::format( + "a minimum delay of {} ({})", + minimum_delay, + ConvertMillisecondsToTime(delay) + ): + "no minimum delay" + ) + ).c_str() + ); + } else { + c->Message(Chat::White, "Usage: #ai roambox [Distance] [Min X] [Max X] [Min Y] [Max Y] [Delay] [Minimum Delay] - Set an NPC's roambox using X and Y coordinates"); + c->Message(Chat::White, "Usage: #ai roambox [Distance] [Roam Distance] [Delay] [Minimum Delay] - Set an NPC's roambox using roam distance"); } - else { - c->Message(Chat::White, "Usage: #ai roambox dist max_x min_x max_y min_y [delay] [mindelay]"); - c->Message(Chat::White, "Usage: #ai roambox dist roamdist [delay] [mindelay]"); + } else { + c->Message(Chat::White, "You must target an NPC with AI."); + } + } else if (is_spells) { + if (sep->IsNumber(2)) { + auto spell_list_id = std::stoul(sep->arg[2]); + if (spell_list_id >= 0) { + target->CastToNPC()->AI_AddNPCSpells(spell_list_id); + + c->Message( + Chat::White, + fmt::format( + "{} ({}) is now using Spell List {}.", + target->GetCleanName(), + target->GetID(), + spell_list_id + ).c_str() + ); + } else { + c->Message(Chat::White, "Spell List ID must be greater than or equal to 0."); } + } else { + c->Message(Chat::White, "Usage: #ai spells [Spell List ID] - Set an NPC's Spell List ID"); } - else { - c->Message(Chat::White, "You need a AI NPC targeted"); - } - } - else if (strcasecmp(sep->arg[1], "stop") == 0 && c->Admin() >= commandToggleAI) { - if (target) { - if (target->IsAIControlled()) { - target->AI_Stop(); - } - else { - c->Message(Chat::White, "Error: Target is not AI controlled"); - } - } - else { - c->Message(Chat::White, "Usage: Target a Mob with AI enabled and use this to turn off their AI."); - } - } - else if (strcasecmp(sep->arg[1], "start") == 0 && c->Admin() >= commandToggleAI) { - if (target) { - if (!target->IsAIControlled()) { - target->AI_Start(); - } - else { - c->Message(Chat::White, "Error: Target is already AI controlled"); - } - } - else { - c->Message(Chat::White, "Usage: Target a Mob with AI disabled and use this to turn on their AI."); - } - } - else { - c->Message(Chat::White, "#AI Sub-commands"); - c->Message(Chat::White, " factionid"); - c->Message(Chat::White, " spellslist"); - c->Message(Chat::White, " con"); - c->Message(Chat::White, " guard"); } } diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 2d6ae07cd..db4394b0f 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -3367,6 +3367,10 @@ std::string lua_get_body_type_name(uint32 bodytype_id) { return quest_manager.getbodytypename(bodytype_id); } +std::string lua_get_consider_level_name(uint8 consider_level) { + return quest_manager.getconsiderlevelname(consider_level); +} + std::string lua_get_environmental_damage_name(uint8 damage_type) { return quest_manager.getenvironmentaldamagename(damage_type); } @@ -3818,6 +3822,7 @@ luabind::scope lua_register_general() { luabind::def("get_faction_name", &lua_get_faction_name), luabind::def("get_language_name", &lua_get_language_name), luabind::def("get_body_type_name", &lua_get_body_type_name), + luabind::def("get_consider_level_name", &lua_get_consider_level_name), luabind::def("get_environmental_damage_name", &lua_get_environmental_damage_name), /* diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 189a399eb..02ffb60df 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1052,6 +1052,10 @@ std::string QuestManager::getbodytypename(uint32 bodytype_id) { return EQ::constants::GetBodyTypeName(static_cast(bodytype_id)); } +std::string QuestManager::getconsiderlevelname(uint8 consider_level) { + return EQ::constants::GetConsiderLevelName(consider_level); +} + void QuestManager::safemove() { QuestManagerCurrentQuestVars(); if (initiator && initiator->IsClient()) diff --git a/zone/questmgr.h b/zone/questmgr.h index b8a296ed7..f859a012d 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -119,6 +119,7 @@ public: std::string getfactionname(int faction_id); std::string getlanguagename(int language_id); std::string getbodytypename(uint32 bodytype_id); + std::string getconsiderlevelname(uint8 consider_level); void safemove(); void rain(int weather); void snow(int weather); From fa9314811ecc89ad0ed44230bf55b2bdc46e5836 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 12 Feb 2022 15:12:35 -0500 Subject: [PATCH 623/624] [Bug Fix] Fix for Bot command casting (#1990) * missing return true in ApplyBardPulse * Update bot.cpp * [Bug Fix] Fix for Bot command casting compile error * [Bug Fix] Fix for Bot command casting * Update bot.cpp --- zone/bot.cpp | 3 ++- zone/spells.cpp | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 32599248c..dcb8af574 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -7104,7 +7104,8 @@ bool Bot::CastSpell(uint16 spell_id, uint16 target_id, EQ::spells::CastingSlot s bardsong_timer.Disable(); } - Result = DoCastSpell(spell_id, target_id, slot, cast_time, mana_cost, oSpellWillFinish, item_slot, aa_id); + Result = Mob::CastSpell(spell_id, target_id, slot, cast_time, mana_cost, oSpellWillFinish, item_slot, 0xFFFFFFFF, 0, resist_adjust, aa_id); + } return Result; } diff --git a/zone/spells.cpp b/zone/spells.cpp index ee73add8c..a03f05408 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2725,6 +2725,8 @@ bool Mob::ApplyBardPulse(int32 spell_id, Mob *spell_target, CastingSlot slot) { if (!SpellFinished(spell_id, spell_target, slot, spells[spell_id].mana, 0xFFFFFFFF, spells[spell_id].resist_difficulty)) { return false; } + + return true; } /////////////////////////////////////////////////////////////////////////////// From 14f536505ecfd18818eeeb373d4db518d23b571b Mon Sep 17 00:00:00 2001 From: neckkola <65987027+neckkola@users.noreply.github.com> Date: Sat, 12 Feb 2022 21:42:28 -0400 Subject: [PATCH 624/624] Fixed Bard AE Target Spells Removed assert for buffs --- zone/spells.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index a03f05408..0c2a762a6 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2418,15 +2418,15 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui case AECaster: case AETarget: { -#ifdef BOTS - if(IsBot()) { - bool StopLogic = false; - if(!this->CastToBot()->DoFinishedSpellAETarget(spell_id, spell_target, slot, StopLogic)) - return false; - if(StopLogic) - break; - } -#endif //BOTS +//#ifdef BOTS +// if(IsBot()) { +// bool StopLogic = false; +// if(!this->CastToBot()->DoFinishedSpellAETarget(spell_id, spell_target, slot, StopLogic)) +// return false; +// if(StopLogic) +// break; +// } +//#endif //BOTS // we can't cast an AE spell without something to center it on assert(ae_center != nullptr); @@ -3332,7 +3332,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid } // now add buff at emptyslot - assert(buffs[emptyslot].spellid == SPELL_UNKNOWN); // sanity check + //assert(buffs[emptyslot].spellid == SPELL_UNKNOWN); // sanity check buffs[emptyslot].spellid = spell_id; buffs[emptyslot].casterlevel = caster_level;